focalboard/webapp/src/archiver.ts

122 lines
4.2 KiB
TypeScript
Raw Normal View History

2020-10-20 21:50:53 +02:00
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
2021-03-02 22:21:55 +01:00
import {ArchiveUtils, IArchiveHeader, IArchiveLine, IBlockArchiveLine} from './blocks/archive'
2021-03-03 00:35:44 +01:00
import {IBlock} from './blocks/block'
2021-03-03 00:14:37 +01:00
import {LineReader} from './lineReader'
2020-10-20 21:52:56 +02:00
import mutator from './mutator'
import {Utils} from './utils'
2021-02-16 19:40:35 +01:00
import {BoardTree} from './viewModel/boardTree'
2020-10-08 18:21:27 +02:00
class Archiver {
2020-10-20 22:36:54 +02:00
static async exportBoardTree(boardTree: BoardTree): Promise<void> {
2020-10-20 21:50:53 +02:00
const blocks = boardTree.allBlocks
2021-03-02 22:21:55 +01:00
this.exportArchive(blocks)
2020-10-20 21:50:53 +02:00
}
2020-10-20 22:36:54 +02:00
static async exportFullArchive(): Promise<void> {
2020-10-20 21:50:53 +02:00
const blocks = await mutator.exportFullArchive()
2021-03-02 22:21:55 +01:00
this.exportArchive(blocks)
2020-10-20 21:50:53 +02:00
}
2021-03-02 22:21:55 +01:00
private static exportArchive(blocks: readonly IBlock[]): void {
const content = ArchiveUtils.buildBlockArchive(blocks)
2020-10-20 21:50:53 +02:00
const date = new Date()
2021-01-26 20:23:20 +01:00
const filename = `archive-${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}.focalboard`
2020-10-20 21:50:53 +02:00
const link = document.createElement('a')
2020-10-20 21:52:56 +02:00
link.style.display = 'none'
2020-10-20 21:50:53 +02:00
// const file = new Blob([content], { type: "text/json" })
// link.href = URL.createObjectURL(file)
link.href = 'data:text/json,' + encodeURIComponent(content)
link.download = filename
document.body.appendChild(link) // FireFox support
link.click()
// TODO: Remove or reuse link
}
2021-03-03 01:13:26 +01:00
private static async importBlocksFromFile(file: File): Promise<void> {
let blockCount = 0
2021-03-03 03:09:13 +01:00
const maxBlocksPerImport = 1000
let blocks: IBlock[] = []
2021-03-02 22:21:55 +01:00
2021-03-03 00:14:37 +01:00
let isFirstLine = true
2021-03-03 01:13:26 +01:00
return new Promise<void>((resolve) => {
LineReader.readFile(file, async (line, completed) => {
2021-03-03 00:14:37 +01:00
if (completed) {
2021-03-03 01:13:26 +01:00
if (blocks.length > 0) {
await mutator.importFullArchive(blocks)
blockCount += blocks.length
}
Utils.log(`Imported ${blockCount} blocks.`)
resolve()
2021-03-03 00:14:37 +01:00
return
}
2021-03-02 22:21:55 +01:00
2021-03-03 00:14:37 +01:00
if (isFirstLine) {
isFirstLine = false
const header = JSON.parse(line) as IArchiveHeader
if (header.date && header.version >= 1) {
const date = new Date(header.date)
Utils.log(`Import archive, version: ${header.version}, date/time: ${date.toLocaleString()}.`)
}
} else {
const row = JSON.parse(line) as IArchiveLine
if (!row || !row.type || !row.data) {
2021-03-02 22:21:55 +01:00
Utils.logError('importFullArchive ERROR parsing line')
2021-03-03 00:14:37 +01:00
return
2021-03-02 22:21:55 +01:00
}
2021-03-03 00:14:37 +01:00
switch (row.type) {
2021-03-02 22:21:55 +01:00
case 'block': {
2021-03-03 00:14:37 +01:00
const blockLine = row as IBlockArchiveLine
2021-03-02 22:21:55 +01:00
const block = blockLine.data
2021-03-03 00:35:44 +01:00
if (Archiver.isValidBlock(block)) {
blocks.push(block)
2021-03-03 01:13:26 +01:00
if (blocks.length >= maxBlocksPerImport) {
2021-03-03 03:09:13 +01:00
const blocksToSend = blocks
blocks = []
await mutator.importFullArchive(blocksToSend)
blockCount += blocksToSend.length
2021-03-03 01:13:26 +01:00
}
2021-03-03 00:35:44 +01:00
}
2021-03-02 22:21:55 +01:00
break
}
}
}
2021-03-03 00:14:37 +01:00
})
})
2021-03-02 22:21:55 +01:00
}
2021-03-03 00:35:44 +01:00
static isValidBlock(block: IBlock): boolean {
if (!block.id || !block.rootId) {
return false
}
return true
}
2020-10-20 21:50:53 +02:00
static importFullArchive(onComplete?: () => void): void {
const input = document.createElement('input')
2020-10-20 21:52:56 +02:00
input.type = 'file'
2021-01-26 20:23:20 +01:00
input.accept = '.focalboard'
2020-10-20 21:50:53 +02:00
input.onchange = async () => {
2020-11-13 02:24:24 +01:00
const file = input.files && input.files[0]
2021-03-02 22:21:55 +01:00
if (file) {
2021-03-03 01:13:26 +01:00
await Archiver.importBlocksFromFile(file)
2021-03-02 22:21:55 +01:00
}
2020-10-20 21:50:53 +02:00
onComplete?.()
2020-10-20 21:52:56 +02:00
}
2020-10-20 21:50:53 +02:00
2020-10-20 21:52:56 +02:00
input.style.display = 'none'
2020-10-20 21:50:53 +02:00
document.body.appendChild(input)
input.click()
// TODO: Remove or reuse input
}
2020-10-08 18:21:27 +02:00
}
2020-10-20 21:50:53 +02:00
export {Archiver}