1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-04-11 11:12:03 +02:00

Desktop, CLI: Allow importing Evernote task lists (#8440)

This commit is contained in:
Rob Moffat 2023-07-11 13:39:49 +01:00 committed by GitHub
parent 7e53a41a30
commit 0071a05a6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 155 additions and 6 deletions

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE en-export SYSTEM "http://xml.evernote.com/pub/evernote-export4.dtd">
<en-export export-date="20230809T080314Z" application="Evernote" version="10.58.8">
<note>
<title>Here is a simple test</title>
<created>20230709T080219Z</created>
<updated>20230709T080302Z</updated>
<note-attributes>
</note-attributes>
<content>
<![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd"><en-note><div>a</div><div><br/></div><div>b</div><div><br/></div><div>List 1</div><div style="--en-task-group:true; --en-id:9876cc26-ebd0-482d-bb36-603e2c0512d0;--en-content-hash:7e7703c4ce2d1805937a6024e5f7b426;-webkit-user-modify:read-only;-moz-user-modify:read-only;user-modify:read-only;border-radius:3px;border:1px solid rgba(182,182,182,0.09);background:rgba(174,174,174,0.09);overflow:hidden;color:#868686"><div style="background:rgba(182,182,182,0.09) no-repeat 6px 6px url(&quot;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzg2ODY4NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE5LjcyNTEgMjBDMjAuMTgxIDIwIDIwLjYwMTEgMTkuNzU2IDIwLjgyOSAxOS4zNkMyMS4wNTcgMTguOTY0IDIxLjA1NyAxOC40NzYgMjAuODI5IDE4LjA4TDEzLjEwMzEgNC42Mzk5OUMxMi44NzYgNC4yNDM5OSAxMi40NTUxIDQgMTIgNEMxMS41NDQ5IDQgMTEuMTI0IDQuMjQzOTkgMTAuODk2MSA0LjYzOTk5TDMuMTcwOTggMTguMDhDMi45NDMwMSAxOC40NzYgMi45NDMwMSAxOC45NjQgMy4xNzA5OCAxOS4zNkMzLjM5ODEgMTkuNzU2IDMuODE4OTMgMjAgNC4yNzQwOCAyMEgxOS43MjUxWk00Ljg2NDg5IDE4LjRMMTIgNS45ODc5OEwxOS4xMzUxIDE4LjRINC44NjQ4OVoiLz4KPHBhdGggZD0iTTEyLjc1IDEwLjU3NTJDMTIuNzUgMTAuMTYxIDEyLjQxNDIgOS44MjUyMyAxMiA5LjgyNTIzQzExLjU4NTggOS44MjUyMyAxMS4yNSAxMC4xNjEgMTEuMjUgMTAuNTc1MlYxMy4yOTc5QzExLjI1IDEzLjcxMjEgMTEuNTg1OCAxNC4wNDc5IDEyIDE0LjA0NzlDMTIuNDE0MiAxNC4wNDc5IDEyLjc1IDEzLjcxMjEgMTIuNzUgMTMuMjk3OVYxMC41NzUyWiIvPgo8cGF0aCBkPSJNMTIuODc1IDE1Ljk1MTFDMTIuODc1IDE2LjQzNDQgMTIuNDgzMiAxNi44MjYxIDEyIDE2LjgyNjFDMTEuNTE2OCAxNi44MjYxIDExLjEyNSAxNi40MzQ0IDExLjEyNSAxNS45NTExQzExLjEyNSAxNS40Njc5IDExLjUxNjggMTUuMDc2MSAxMiAxNS4wNzYxQzEyLjQ4MzIgMTUuMDc2MSAxMi44NzUgMTUuNDY3OSAxMi44NzUgMTUuOTUxMVoiLz4KPC9zdmc+Cg==&quot;);padding:8px 38px;font-weight:600">Content not supported</div><div style="padding:2px 6px;margin: 1em">This block is a placeholder for Tasks, which has been officially released on the newest version of Evernote and is no longer supported on this version. Deleting or moving this block may cause unexpected behavior in newer versions of Evernote.</div></div><div>List 2</div><div style="--en-task-group:true; --en-id:b89e9766-5afc-47b8-9c54-e42962d69421;--en-content-hash:85f6897ebdd0d3d12ba8fcc3ce6b8e56;-webkit-user-modify:read-only;-moz-user-modify:read-only;user-modify:read-only;border-radius:3px;border:1px solid rgba(182,182,182,0.09);background:rgba(174,174,174,0.09);overflow:hidden;color:#868686"><div style="background:rgba(182,182,182,0.09) no-repeat 6px 6px url(&quot;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzg2ODY4NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE5LjcyNTEgMjBDMjAuMTgxIDIwIDIwLjYwMTEgMTkuNzU2IDIwLjgyOSAxOS4zNkMyMS4wNTcgMTguOTY0IDIxLjA1NyAxOC40NzYgMjAuODI5IDE4LjA4TDEzLjEwMzEgNC42Mzk5OUMxMi44NzYgNC4yNDM5OSAxMi40NTUxIDQgMTIgNEMxMS41NDQ5IDQgMTEuMTI0IDQuMjQzOTkgMTAuODk2MSA0LjYzOTk5TDMuMTcwOTggMTguMDhDMi45NDMwMSAxOC40NzYgMi45NDMwMSAxOC45NjQgMy4xNzA5OCAxOS4zNkMzLjM5ODEgMTkuNzU2IDMuODE4OTMgMjAgNC4yNzQwOCAyMEgxOS43MjUxWk00Ljg2NDg5IDE4LjRMMTIgNS45ODc5OEwxOS4xMzUxIDE4LjRINC44NjQ4OVoiLz4KPHBhdGggZD0iTTEyLjc1IDEwLjU3NTJDMTIuNzUgMTAuMTYxIDEyLjQxNDIgOS44MjUyMyAxMiA5LjgyNTIzQzExLjU4NTggOS44MjUyMyAxMS4yNSAxMC4xNjEgMTEuMjUgMTAuNTc1MlYxMy4yOTc5QzExLjI1IDEzLjcxMjEgMTEuNTg1OCAxNC4wNDc5IDEyIDE0LjA0NzlDMTIuNDE0MiAxNC4wNDc5IDEyLjc1IDEzLjcxMjEgMTIuNzUgMTMuMjk3OVYxMC41NzUyWiIvPgo8cGF0aCBkPSJNMTIuODc1IDE1Ljk1MTFDMTIuODc1IDE2LjQzNDQgMTIuNDgzMiAxNi44MjYxIDEyIDE2LjgyNjFDMTEuNTE2OCAxNi44MjYxIDExLjEyNSAxNi40MzQ0IDExLjEyNSAxNS45NTExQzExLjEyNSAxNS40Njc5IDExLjUxNjggMTUuMDc2MSAxMiAxNS4wNzYxQzEyLjQ4MzIgMTUuMDc2MSAxMi44NzUgMTUuNDY3OSAxMi44NzUgMTUuOTUxMVoiLz4KPC9zdmc+Cg==&quot;);padding:8px 38px;font-weight:600">Content not supported</div><div style="padding:2px 6px;margin: 1em">This block is a placeholder for Tasks, which has been officially released on the newest version of Evernote and is no longer supported on this version. Deleting or moving this block may cause unexpected behavior in newer versions of Evernote.</div></div></en-note> ]]>
</content>
<task>
<title>Clara</title>
<created>20230709T080237Z</created>
<updated>20230709T080300Z</updated>
<taskStatus>completed</taskStatus>
<taskFlag>false</taskFlag>
<sortWeight>J</sortWeight>
<noteLevelID>7ab2b90f-4aed-405f-8541-cb56132a2e1a</noteLevelID>
<taskGroupNoteLevelID>9876cc26-ebd0-482d-bb36-603e2c0512d0</taskGroupNoteLevelID>
<dueDate>2757600913T000000Z</dueDate>
<statusUpdated>20230709T080237Z</statusUpdated>
<creator>73671305</creator>
<lastEditor>73671305</lastEditor>
</task>
<task>
<title>Bob</title>
<created>20230709T080237Z</created>
<updated>20230709T080250Z</updated>
<taskStatus>open</taskStatus>
<taskFlag>false</taskFlag>
<sortWeight>B</sortWeight>
<noteLevelID>98185f99-1779-4af3-988b-4af054a2c8c0</noteLevelID>
<taskGroupNoteLevelID>9876cc26-ebd0-482d-bb36-603e2c0512d0</taskGroupNoteLevelID>
<dueDate>2757600913T000000Z</dueDate>
<statusUpdated>20230709T080237Z</statusUpdated>
<creator>73671305</creator>
<lastEditor>73671305</lastEditor>
</task>
<task>
<title>Jeff</title>
<created>20230709T080300Z</created>
<updated>20230709T080302Z</updated>
<taskStatus>open</taskStatus>
<taskFlag>false</taskFlag>
<sortWeight>B</sortWeight>
<noteLevelID>8458b06b-0007-4c3d-b61b-87abe15f55b8</noteLevelID>
<taskGroupNoteLevelID>b89e9766-5afc-47b8-9c54-e42962d69421</taskGroupNoteLevelID>
<dueDate>2757600913T000000Z</dueDate>
<statusUpdated>20230709T080300Z</statusUpdated>
<creator>73671305</creator>
<lastEditor>73671305</lastEditor>
</task>
</note>
</en-export>

View File

@ -0,0 +1,12 @@
a
b
List 1
- [x] Clara
- [ ] Bob
List 2
- [ ] Jeff

View File

@ -0,0 +1,17 @@
<en-note>
<div>a</div>
<div><br/></div>
<div>b</div>
<div><br/></div>
<div>List 1</div>
<div style="--en-task-group:true; --en-id:9876cc26-ebd0-482d-bb36-603e2c0512d0;--en-content-hash:7e7703c4ce2d1805937a6024e5f7b426;-webkit-user-modify:read-only;-moz-user-modify:read-only;user-modify:read-only;border-radius:3px;border:1px solid rgba(182,182,182,0.09);background:rgba(174,174,174,0.09);overflow:hidden;color:#868686">
<div style="background:rgba(182,182,182,0.09) no-repeat 6px 6px url(&quot;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzg2ODY4NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE5LjcyNTEgMjBDMjAuMTgxIDIwIDIwLjYwMTEgMTkuNzU2IDIwLjgyOSAxOS4zNkMyMS4wNTcgMTguOTY0IDIxLjA1NyAxOC40NzYgMjAuODI5IDE4LjA4TDEzLjEwMzEgNC42Mzk5OUMxMi44NzYgNC4yNDM5OSAxMi40NTUxIDQgMTIgNEMxMS41NDQ5IDQgMTEuMTI0IDQuMjQzOTkgMTAuODk2MSA0LjYzOTk5TDMuMTcwOTggMTguMDhDMi45NDMwMSAxOC40NzYgMi45NDMwMSAxOC45NjQgMy4xNzA5OCAxOS4zNkMzLjM5ODEgMTkuNzU2IDMuODE4OTMgMjAgNC4yNzQwOCAyMEgxOS43MjUxWk00Ljg2NDg5IDE4LjRMMTIgNS45ODc5OEwxOS4xMzUxIDE4LjRINC44NjQ4OVoiLz4KPHBhdGggZD0iTTEyLjc1IDEwLjU3NTJDMTIuNzUgMTAuMTYxIDEyLjQxNDIgOS44MjUyMyAxMiA5LjgyNTIzQzExLjU4NTggOS44MjUyMyAxMS4yNSAxMC4xNjEgMTEuMjUgMTAuNTc1MlYxMy4yOTc5QzExLjI1IDEzLjcxMjEgMTEuNTg1OCAxNC4wNDc5IDEyIDE0LjA0NzlDMTIuNDE0MiAxNC4wNDc5IDEyLjc1IDEzLjcxMjEgMTIuNzUgMTMuMjk3OVYxMC41NzUyWiIvPgo8cGF0aCBkPSJNMTIuODc1IDE1Ljk1MTFDMTIuODc1IDE2LjQzNDQgMTIuNDgzMiAxNi44MjYxIDEyIDE2LjgyNjFDMTEuNTE2OCAxNi44MjYxIDExLjEyNSAxNi40MzQ0IDExLjEyNSAxNS45NTExQzExLjEyNSAxNS40Njc5IDExLjUxNjggMTUuMDc2MSAxMiAxNS4wNzYxQzEyLjQ4MzIgMTUuMDc2MSAxMi44NzUgMTUuNDY3OSAxMi44NzUgMTUuOTUxMVoiLz4KPC9zdmc+Cg==&quot;);padding:8px 38px;font-weight:600">Content not supported</div>
<div style="padding:2px 6px;margin: 1em">This block is a placeholder for Tasks, which has been officially released on the newest version of Evernote and is no longer supported on this version. Deleting or moving this block may cause unexpected behavior in newer versions of Evernote.</div>
</div>
<div>List 2</div>
<div style="--en-task-group:true; --en-id:b89e9766-5afc-47b8-9c54-e42962d69421;--en-content-hash:85f6897ebdd0d3d12ba8fcc3ce6b8e56;-webkit-user-modify:read-only;-moz-user-modify:read-only;user-modify:read-only;border-radius:3px;border:1px solid rgba(182,182,182,0.09);background:rgba(174,174,174,0.09);overflow:hidden;color:#868686">
<div style="background:rgba(182,182,182,0.09) no-repeat 6px 6px url(&quot;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iIzg2ODY4NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTE5LjcyNTEgMjBDMjAuMTgxIDIwIDIwLjYwMTEgMTkuNzU2IDIwLjgyOSAxOS4zNkMyMS4wNTcgMTguOTY0IDIxLjA1NyAxOC40NzYgMjAuODI5IDE4LjA4TDEzLjEwMzEgNC42Mzk5OUMxMi44NzYgNC4yNDM5OSAxMi40NTUxIDQgMTIgNEMxMS41NDQ5IDQgMTEuMTI0IDQuMjQzOTkgMTAuODk2MSA0LjYzOTk5TDMuMTcwOTggMTguMDhDMi45NDMwMSAxOC40NzYgMi45NDMwMSAxOC45NjQgMy4xNzA5OCAxOS4zNkMzLjM5ODEgMTkuNzU2IDMuODE4OTMgMjAgNC4yNzQwOCAyMEgxOS43MjUxWk00Ljg2NDg5IDE4LjRMMTIgNS45ODc5OEwxOS4xMzUxIDE4LjRINC44NjQ4OVoiLz4KPHBhdGggZD0iTTEyLjc1IDEwLjU3NTJDMTIuNzUgMTAuMTYxIDEyLjQxNDIgOS44MjUyMyAxMiA5LjgyNTIzQzExLjU4NTggOS44MjUyMyAxMS4yNSAxMC4xNjEgMTEuMjUgMTAuNTc1MlYxMy4yOTc5QzExLjI1IDEzLjcxMjEgMTEuNTg1OCAxNC4wNDc5IDEyIDE0LjA0NzlDMTIuNDE0MiAxNC4wNDc5IDEyLjc1IDEzLjcxMjEgMTIuNzUgMTMuMjk3OVYxMC41NzUyWiIvPgo8cGF0aCBkPSJNMTIuODc1IDE1Ljk1MTFDMTIuODc1IDE2LjQzNDQgMTIuNDgzMiAxNi44MjYxIDEyIDE2LjgyNjFDMTEuNTE2OCAxNi44MjYxIDExLjEyNSAxNi40MzQ0IDExLjEyNSAxNS45NTExQzExLjEyNSAxNS40Njc5IDExLjUxNjggMTUuMDc2MSAxMiAxNS4wNzYxQzEyLjQ4MzIgMTUuMDc2MSAxMi44NzUgMTUuNDY3OSAxMi44NzUgMTUuOTUxMVoiLz4KPC9zdmc+Cg==&quot;);padding:8px 38px;font-weight:600">Content not supported</div>
<div style="padding:2px 6px;margin: 1em">This block is a placeholder for Tasks, which has been officially released on the newest version of Evernote and is no longer supported on this version. Deleting or moving this block may cause unexpected behavior in newer versions of Evernote.</div>
</div>
</en-note>

View File

@ -22,7 +22,6 @@ describe('import-enex-md-gen', () => {
it('should convert ENEX content to Markdown', async () => { it('should convert ENEX content to Markdown', async () => {
const files = await shim.fsDriver().readDirStats(enexSampleBaseDir); const files = await shim.fsDriver().readDirStats(enexSampleBaseDir);
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
const htmlFilename = files[i].path; const htmlFilename = files[i].path;
if (htmlFilename.indexOf('.html') < 0) continue; if (htmlFilename.indexOf('.html') < 0) continue;
@ -108,6 +107,14 @@ describe('import-enex-md-gen', () => {
expect(all[0].size).toBe(0); expect(all[0].size).toBe(0);
}); });
it('should handle tasks', async () => {
const filePath = `${enexSampleBaseDir}/tasks.enex`;
await importEnex('', filePath);
const expectedMd = await shim.fsDriver().readFile(`${enexSampleBaseDir}/tasks.md`);
const note: NoteEntity = (await Note.all())[0];
expect(note.body).toEqual(expectedMd);
});
it('should handle empty note content', async () => { it('should handle empty note content', async () => {
const filePath = `${enexSampleBaseDir}/empty_content.enex`; const filePath = `${enexSampleBaseDir}/empty_content.enex`;
await expectNotThrow(() => importEnex('', filePath)); await expectNotThrow(() => importEnex('', filePath));

View File

@ -39,6 +39,7 @@ enum ListTag {
Ul = 'ul', Ul = 'ul',
Ol = 'ol', Ol = 'ol',
CheckboxList = 'checkboxList', CheckboxList = 'checkboxList',
TaskList = 'taskList',
} }
interface ParserStateList { interface ParserStateList {
@ -58,6 +59,13 @@ interface ParserState {
currentCode?: string; currentCode?: string;
} }
interface ExtractedTask {
title: string;
completed: boolean;
groupId: string;
}
interface EnexXmlToMdArrayResult { interface EnexXmlToMdArrayResult {
content: Section; content: Section;
resources: ResourceEntity[]; resources: ResourceEntity[];
@ -554,7 +562,7 @@ function isHighlight(context: any, _nodeName: string, attributes: any) {
return false; return false;
} }
function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<EnexXmlToMdArrayResult> { function enexXmlToMdArray(stream: any, resources: ResourceEntity[], tasks: ExtractedTask[]): Promise<EnexXmlToMdArrayResult> {
const remainingResources = resources.slice(); const remainingResources = resources.slice();
const removeRemainingResource = (id: string) => { const removeRemainingResource = (id: string) => {
@ -618,6 +626,12 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
saxStream.on('text', (text: string) => { saxStream.on('text', (text: string) => {
if (['table', 'tr', 'tbody'].indexOf(section.type) >= 0) return; if (['table', 'tr', 'tbody'].indexOf(section.type) >= 0) return;
const currentList = state.lists && state.lists.length ? state.lists[state.lists.length - 1] : null;
if ((currentList) && (currentList.tag === ListTag.TaskList)) {
// skip text on task lists
return;
}
text = !state.inPre ? unwrapInnerText(text) : text; text = !state.inPre ? unwrapInnerText(text) : text;
section.lines = collapseWhiteSpaceAndAppend(section.lines, state, text); section.lines = collapseWhiteSpaceAndAppend(section.lines, state, text);
}); });
@ -741,7 +755,20 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
section.lines.push(newSection); section.lines.push(newSection);
section = newSection; section = newSection;
} else if (isBlockTag(n)) { } else if (isBlockTag(n)) {
const isTodosList = cssValue(this, nodeAttributes.style, '--en-task-group') === 'true';
if (isTodosList) {
const todoGroup = cssValue(this, nodeAttributes.style, '--en-id');
section.lines.push(BLOCK_OPEN); section.lines.push(BLOCK_OPEN);
for (const t of tasks) {
if (t.groupId === todoGroup) {
section.lines.push(`- [${t.completed ? 'x' : ' '}] ${t.title}\n`);
}
}
tagInfo.name = ListTag.TaskList;
state.lists.push({ tag: ListTag.TaskList, counter: 1, startedText: false });
} else {
section.lines.push(BLOCK_OPEN);
}
} else if (isListTag(n)) { } else if (isListTag(n)) {
section.lines.push(BLOCK_OPEN); section.lines.push(BLOCK_OPEN);
const isCheckboxList = cssValue(this, nodeAttributes.style, '--en-todo') === 'true'; const isCheckboxList = cssValue(this, nodeAttributes.style, '--en-todo') === 'true';
@ -957,6 +984,9 @@ function enexXmlToMdArray(stream: any, resources: ResourceEntity[]): Promise<Ene
if (section && section.parent) section = section.parent; if (section && section.parent) section = section.parent;
} }
} else if (isNewLineOnlyEndTag(n)) { } else if (isNewLineOnlyEndTag(n)) {
if (poppedTag.name === ListTag.TaskList) {
state.lists.pop();
}
section.lines.push(BLOCK_CLOSE); section.lines.push(BLOCK_CLOSE);
} else if (n === 'td' || n === 'th') { } else if (n === 'td' || n === 'th') {
if (section && section.parent) section = section.parent; if (section && section.parent) section = section.parent;
@ -1370,9 +1400,9 @@ function renderLines(lines: any[]) {
return mdLines; return mdLines;
} }
async function enexXmlToMd(xmlString: string, resources: ResourceEntity[]) { async function enexXmlToMd(xmlString: string, resources: ResourceEntity[], tasks: ExtractedTask[]) {
const stream = stringToStream(xmlString); const stream = stringToStream(xmlString);
const result = await enexXmlToMdArray(stream, resources); const result = await enexXmlToMdArray(stream, resources, tasks);
let mdLines = renderLines(result.content.lines); let mdLines = renderLines(result.content.lines);

View File

@ -138,8 +138,15 @@ interface ExtractedResource {
title?: string; title?: string;
} }
interface ExtractedTask {
title: string;
completed: boolean;
groupId: string;
}
interface ExtractedNote extends NoteEntity { interface ExtractedNote extends NoteEntity {
resources?: ExtractedResource[]; resources?: ExtractedResource[];
tasks: ExtractedTask[];
tags?: string[]; tags?: string[];
title?: string; title?: string;
bodyXml?: string; bodyXml?: string;
@ -369,6 +376,7 @@ export default async function importEnex(parentFolderId: string, filePath: strin
let note: ExtractedNote = null; let note: ExtractedNote = null;
let noteAttributes: Record<string, any> = null; let noteAttributes: Record<string, any> = null;
let noteResource: ExtractedResource = null; let noteResource: ExtractedResource = null;
let noteTask: ExtractedTask = null;
let noteResourceAttributes: Record<string, any> = null; let noteResourceAttributes: Record<string, any> = null;
let noteResourceRecognition: NoteResourceRecognition = null; let noteResourceRecognition: NoteResourceRecognition = null;
const notes: ExtractedNote[] = []; const notes: ExtractedNote[] = [];
@ -432,7 +440,7 @@ export default async function importEnex(parentFolderId: string, filePath: strin
const body = importOptions.outputFormat === 'html' ? const body = importOptions.outputFormat === 'html' ?
await enexXmlToHtml(note.bodyXml, note.resources) : await enexXmlToHtml(note.bodyXml, note.resources) :
await enexXmlToMd(note.bodyXml, note.resources); await enexXmlToMd(note.bodyXml, note.resources, note.tasks);
delete note.bodyXml; delete note.bodyXml;
note.markup_language = importOptions.outputFormat === 'html' ? note.markup_language = importOptions.outputFormat === 'html' ?
@ -510,6 +518,14 @@ export default async function importEnex(parentFolderId: string, filePath: strin
if (!(n in noteResource)) (noteResource as any)[n] = ''; if (!(n in noteResource)) (noteResource as any)[n] = '';
(noteResource as any)[n] += text; (noteResource as any)[n] += text;
} }
} else if (noteTask) {
if (n === 'title') {
noteTask.title = text;
} else if (n === 'taskStatus') {
noteTask.completed = text === 'completed';
} else if (n === 'taskGroupNoteLevelID') {
noteTask.groupId = text;
}
} else if (note) { } else if (note) {
if (n === 'title') { if (n === 'title') {
note.title = text; note.title = text;
@ -536,6 +552,7 @@ export default async function importEnex(parentFolderId: string, filePath: strin
if (n === 'note') { if (n === 'note') {
note = { note = {
resources: [], resources: [],
tasks: [],
tags: [], tags: [],
bodyXml: '', bodyXml: '',
}; };
@ -549,6 +566,12 @@ export default async function importEnex(parentFolderId: string, filePath: strin
noteResource = { noteResource = {
hasData: false, hasData: false,
}; };
} else if (n === 'task') {
noteTask = {
title: '',
completed: false,
groupId: '',
};
} }
})); }));
@ -602,6 +625,9 @@ export default async function importEnex(parentFolderId: string, filePath: strin
note.source_url = noteAttributes['source-url'] ? noteAttributes['source-url'].trim() : ''; note.source_url = noteAttributes['source-url'] ? noteAttributes['source-url'].trim() : '';
noteAttributes = null; noteAttributes = null;
} else if (n === 'task') {
note.tasks.push(noteTask);
noteTask = null;
} else if (n === 'resource') { } else if (n === 'resource') {
let mimeType = noteResource.mime ? noteResource.mime.trim() : ''; let mimeType = noteResource.mime ? noteResource.mime.trim() : '';