// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import * as fs from 'fs' import minimist from 'minimist' import {exit} from 'process' import {ArchiveUtils} from '../util/archive' import {Block} from '../../webapp/src/blocks/block' import {Board} from '../../webapp/src/blocks/board' import {IPropertyOption, IPropertyTemplate, createBoard} from '../../webapp/src/blocks/board' import {createBoardView} from '../../webapp/src/blocks/boardView' import {createCard} from '../../webapp/src/blocks/card' import {createTextBlock} from '../../webapp/src/blocks/textBlock' import {Item, Project, Section, Todoist} from './todoist' import {Utils} from './utils' // HACKHACK: To allow Utils.CreateGuid to work (global.window as any) = {} const optionColors = [ // 'propColorDefault', 'propColorGray', 'propColorBrown', 'propColorOrange', 'propColorYellow', 'propColorGreen', 'propColorBlue', 'propColorPurple', 'propColorPink', 'propColorRed', ] const defaultSections = ['No Status', 'Next Up', 'In Progress', 'Completed', 'Archived'].map(title => { return { id: Utils.createGuid(), name: title, } as Section }) let noStatusSectionID: any let optionColorIndex = 0 function main() { const args: minimist.ParsedArgs = minimist(process.argv.slice(2)) const inputFile = args['i'] const outputFile = args['o'] || 'archive.focalboard' if (!inputFile) { showHelp() } if (!fs.existsSync(inputFile)) { console.error(`File not found: ${inputFile}`) exit(2) } // Read input const inputData = fs.readFileSync(inputFile, 'utf-8') const input = JSON.parse(inputData) as Todoist const boards = [] as Board[] const blocks = [] as Block[] input.projects.forEach(project => { const [brds, blks] = convert(input, project) boards.push(...brds) blocks.push(...blks) }) // Save output // TODO: Stream output const outputData = ArchiveUtils.buildBlockArchive(boards, blocks) fs.writeFileSync(outputFile, outputData) console.log(`Exported to ${outputFile}`) } function convert(input: Todoist, project: Project): [Board[], Block[]] { const boards: Board[] = [] const blocks: Block[] = [] if (project.name === 'Inbox') { return [boards, blocks] } // Board const board = createBoard() console.log(`Board: ${project.name}`) board.title = project.name board.description = project.name // Convert lists (columns) to a Select property const optionIdMap = new Map() const options: IPropertyOption[] = [] const columns = getProjectColumns(input, project) console.log("columns: " + JSON.stringify(columns)) columns.forEach(list => { const optionId = Utils.createGuid() if (list.name === 'No Status') { noStatusSectionID = list.id } optionIdMap.set(String(list.id), optionId) const color = optionColors[optionColorIndex % optionColors.length] optionColorIndex += 1 const option: IPropertyOption = { id: optionId, value: list.name, color, } options.push(option) }) const cardProperty: IPropertyTemplate = { id: Utils.createGuid(), name: 'List', type: 'select', options } board.cardProperties = [cardProperty] boards.push(board) // Board view const view = createBoardView() view.title = 'Board View' view.fields.viewType = 'board' view.boardId = board.id view.parentId = board.id blocks.push(view) // Cards const cards = getProjectCards(input, project) cards.forEach(card => { const outCard = createCard() outCard.title = card.content outCard.boardId = board.id outCard.parentId = board.id // Map lists to Select property options const cardSectionId = card.section_id ? card.section_id : noStatusSectionID const optionId = optionIdMap.get(String(cardSectionId)) if (optionId) { outCard.fields.properties[cardProperty.id] = optionId } else { console.warn(`Invalid idList: ${cardSectionId} for card: ${card.content}`) } blocks.push(outCard) // console.log(`\t${card.desc}`) const text = createTextBlock() text.title = getCardDescription(input, card).join('\n\n') text.boardId = board.id text.parentId = outCard.id blocks.push(text) outCard.fields.contentOrder = [text.id] }) return [boards, blocks] } function getProjectColumns(input: Todoist, project: Project): Array
{ const sections = [{ id: noStatusSectionID, name: 'No Section', } as Section] sections.push(...input.sections.filter(section => section.project_id === project.id)) return sections.length > 1 ? sections : defaultSections } function getProjectCards(input: Todoist, project: Project): Array { return input.items.filter(item => item.project_id === project.id) } function getCardDescription(input: Todoist, item: Item): Array { return input.notes.filter(note => note.item_id === item.id).map(item => { let description = "" if (item.content) { description = item.content } if (item.file_attachment) { description += `\n\nAttachment: [${item.file_attachment.title}](${item.file_attachment.url})` } return description }) } function showHelp() { exit(1) } main()