Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 46 additions & 1 deletion src/formats/evernote-enex.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,55 @@
import { FileSystemAdapter, Notice } from 'obsidian';
import { FileSystemAdapter, Notice, Setting } from 'obsidian';
import { path } from '../filesystem';
import { FormatImporter } from '../format-importer';
import { ImportContext } from '../main';
import { defaultYarleOptions, dropTheRope } from './yarle/yarle';

export class EvernoteEnexImporter extends FormatImporter {
private includeTitleInFrontmatter: boolean;
private includeCreationTimeInFrontmatter: boolean;
private includeUpdateTimeInFrontmatter: boolean;

init() {
this.addFileChooserSetting('Evernote', ['enex'], true);
this.addOutputLocationSetting('Evernote');

this.includeTitleInFrontmatter = false;
let titleDescFragment = new DocumentFragment();
titleDescFragment.createSpan({ text: 'This preserves titles with special characters like slashes, although you\'ll need a plugin like ' });
titleDescFragment.createEl('a', {
text: 'Front Matter Title',
href: 'https://github.com/snezhig/obsidian-front-matter-title',
});
titleDescFragment.createSpan({ text: ' to display them.' });
new Setting(this.modal.contentEl)
.setName('Include original title in frontmatter')
.setDesc(titleDescFragment)
.addToggle(toggle => {
toggle.setValue(this.includeTitleInFrontmatter);
toggle.onChange(async (value) => {
this.includeTitleInFrontmatter = value;
});
});

this.includeCreationTimeInFrontmatter = false;
new Setting(this.modal.contentEl)
.setName('Include created date in frontmatter')
.addToggle(toggle => {
toggle.setValue(this.includeCreationTimeInFrontmatter);
toggle.onChange(async (value) => {
this.includeCreationTimeInFrontmatter = value;
});
});

this.includeUpdateTimeInFrontmatter = false;
new Setting(this.modal.contentEl)
.setName('Include updated date in frontmatter')
.addToggle(toggle => {
toggle.setValue(this.includeUpdateTimeInFrontmatter);
toggle.onChange(async (value) => {
this.includeUpdateTimeInFrontmatter = value;
});
});
}

async import(ctx: ImportContext) {
Expand All @@ -32,6 +74,9 @@ export class EvernoteEnexImporter extends FormatImporter {
...{
enexSources: files,
outputDir: path.join(adapter.getBasePath(), folder.path),
includeTitleInFrontmatter: this.includeTitleInFrontmatter,
includeCreationTimeInFrontmatter: this.includeCreationTimeInFrontmatter,
includeUpdateTimeInFrontmatter: this.includeUpdateTimeInFrontmatter,
},
};

Expand Down
3 changes: 3 additions & 0 deletions src/formats/yarle/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ export interface YarleOptions {
skipReminderOrder?: boolean;
skipReminderDoneTime?: boolean;
skipTags?: boolean;
includeTitleInFrontmatter?: boolean;
includeCreationTimeInFrontmatter?: boolean;
includeUpdateTimeInFrontmatter?: boolean;
useHashTags?: boolean;
replaceWhitespacesInTagsByUnderscore?: boolean;
skipEnexFileNameFromOutputPath?: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { escapeYamlValue } from '../../yaml-utils';

export const applyConditionalTemplate = (text: string, P: any, newValue?: string): string => {
const escapedValue = escapeYamlValue(newValue);
return text
.replace(new RegExp(`${P.CONTENT_PLACEHOLDER}`, 'g'), newValue || '')
.replace(new RegExp(`${P.CONTENT_PLACEHOLDER}`, 'g'), escapedValue)
.replace(new RegExp(`${P.START_BLOCK}`, 'g'), '')
.replace(new RegExp(`${P.END_BLOCK}`, 'g'), '');
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { applyTemplateOnBlock } from './apply-template-on-block';
import { getTemplateBlockSettings } from './get-templateblock-settings';

export const applyContentTemplate = (noteData: NoteData, inputText: string, check: Function): string => {
const contentTemplateSettings = getTemplateBlockSettings(inputText, check, P, noteData.content);
const contentTemplateSettings = getTemplateBlockSettings(inputText, check, P, noteData.content, true);

return applyTemplateOnBlock(contentTemplateSettings);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const applyTagsYamlListTemplate = (noteData: NoteData, inputText: string,
if (noteData.tags) {
tags = '\n'+noteData.tags.split(' ').map(tag => ` - ${tag.replace(/^#/, '')}`).join('\n');
}
const tagsTemplateSettings = getTemplateBlockSettings(inputText, check, P, tags);
const tagsTemplateSettings = getTemplateBlockSettings(inputText, check, P, tags, true);

return applyTemplateOnBlock(tagsTemplateSettings);
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TemplateBlockSettings } from '../template-settings';
import { escapeYamlValue } from '../../yaml-utils';

export const applyTemplateOnBlock = ({
template,
Expand All @@ -7,12 +8,14 @@ export const applyTemplateOnBlock = ({
endBlockPlaceholder,
valuePlaceholder,
value,
skipYamlEscaping,
}: TemplateBlockSettings): string => {
if (value && check()) {
const finalValue = skipYamlEscaping ? value : escapeYamlValue(value);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of adding this new flag, I think it would be simpler and easier to follow if value passed into this function is already escaped if it needs to be. So for the two templates where escaping is enabled now, it needs to instead escape noteData.content or noteData.title before calling getTemplateBlockSettings.

return template
.replace(new RegExp(`${startBlockPlaceholder}`, 'g'), '')
.replace(new RegExp(`${endBlockPlaceholder}`, 'g'), '')
.replace(new RegExp(`${valuePlaceholder}`, 'g'), value);
.replace(new RegExp(`${valuePlaceholder}`, 'g'), finalValue);

}
const reg = `${startBlockPlaceholder}([\\d\\D])(?:.|(\r\n|\r|\n))*?(?=${endBlockPlaceholder})${endBlockPlaceholder}`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { TemplateBlockSettings } from '../template-settings';

export const getTemplateBlockSettings = (text: string, check: Function, T: any, value?: string): TemplateBlockSettings => {
export const getTemplateBlockSettings = (text: string, check: Function, T: any, value?: string, skipYamlEscaping?: boolean): TemplateBlockSettings => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove skipYamlEscaping. See related comment.

return {
template: text,
check,
startBlockPlaceholder: T.START_BLOCK,
endBlockPlaceholder: T.END_BLOCK,
valuePlaceholder: T.CONTENT_PLACEHOLDER,
value,
skipYamlEscaping,
};
};
5 changes: 4 additions & 1 deletion src/formats/yarle/utils/templates/default-template.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const frontmatterDelimiter = '---\n';
const titleBlock = '{title-block}title: {title}{end-title-block}\n';
const createdAtBlock = '{created-at-block}created: {created-at}{end-created-at-block}\n';
const updatedAtBlock = '{updated-at-block}updated: {updated-at}{end-updated-at-block}\n';
const sourceBlock = '{source-url-block}source: {source-url}{end-source-url-block}\n';
const tagBlock = '{tags-yaml-list-block}\ntags: {tags-yaml-list}\n\n{end-tags-yaml-list-block}';
const contentBlock = '{content-block}{content}{end-content-block}\n';

export const defaultTemplate = frontmatterDelimiter + tagBlock + sourceBlock +frontmatterDelimiter + contentBlock;
export const defaultTemplate = frontmatterDelimiter + titleBlock + tagBlock + createdAtBlock + updatedAtBlock + sourceBlock + frontmatterDelimiter + contentBlock;
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './remove-link-to-original-placeholder';
export * from './remove-remindertime-placeholder';
export * from './remove-reminderdonetime-placeholder';
export * from './remove-reminderorder-placeholder';
export * from './remove-title-placeholder';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as T from '../placeholders/title-placeholders';

import { removePlaceholder } from './remove-placeholder';

export const removeTitlePlaceholder = (text: string): string => {
return removePlaceholder(text, T);
};
1 change: 1 addition & 0 deletions src/formats/yarle/utils/templates/template-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export interface TemplateBlockSettings {
endBlockPlaceholder: string;
valuePlaceholder: string;
value?: string;
skipYamlEscaping?: boolean;
}
11 changes: 7 additions & 4 deletions src/formats/yarle/utils/templates/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,27 @@ import { YarleOptions } from '../../options';
import { applyContentTemplate, applyCreatedAtTemplate, applyLocationTemplate, applyNotebookTemplate, applyReminderDoneTimeTemplate, applyReminderOrderTemplate, applyReminderTimeTemplate, applySourceUrlTemplate,applyTagsYamlListTemplate, applyTagsTemplate, applyTitleTemplate, applyUpdatedAtTemplate } from './apply-functions';

import * as T from './placeholders/metadata-placeholders';
import { removeCreatedAtPlaceholder, removeLinkToOriginalTemplate, removeLocationPlaceholder, removeNotebookPlaceholder, removeReminderDoneTimePlaceholder, removeReminderOrderPlaceholder, removeReminderTimePlaceholder, removeSourceUrlPlaceholder, removeUpdatedAtPlaceholder } from './remove-functions';
import { removeCreatedAtPlaceholder, removeLinkToOriginalTemplate, removeLocationPlaceholder, removeNotebookPlaceholder, removeReminderDoneTimePlaceholder, removeReminderOrderPlaceholder, removeReminderTimePlaceholder, removeSourceUrlPlaceholder, removeTitlePlaceholder, removeUpdatedAtPlaceholder } from './remove-functions';

export const applyTemplate = (noteData: NoteData, yarleOptions: YarleOptions) => {

let result = yarleOptions.currentTemplate;

result = applyTitleTemplate(noteData, result, () => noteData.title);
result = applyTagsTemplate(noteData, result, () => !yarleOptions.skipTags);
result = applyTagsYamlListTemplate(noteData, result, () => !yarleOptions.skipTags);
result = applyContentTemplate(noteData, result, () => noteData.content);

result = removeLinkToOriginalTemplate(result);

result = (!yarleOptions.skipCreationTime && noteData.createdAt)
result = (yarleOptions.includeTitleInFrontmatter && noteData.title)
? applyTitleTemplate(noteData, result, () => noteData.title)
: removeTitlePlaceholder(result);

result = (yarleOptions.includeCreationTimeInFrontmatter && noteData.createdAt)
? applyCreatedAtTemplate(noteData, result)
: removeCreatedAtPlaceholder(result);

result = (!yarleOptions.skipUpdateTime && noteData.updatedAt)
result = (yarleOptions.includeUpdateTimeInFrontmatter && noteData.updatedAt)
? applyUpdatedAtTemplate(noteData, result)
: removeUpdatedAtPlaceholder(result);

Expand Down
38 changes: 38 additions & 0 deletions src/formats/yarle/utils/yaml-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Escapes a string value for use in YAML frontmatter.
* Handles special characters like colons, quotes, newlines, etc.
*
* @param value - The string value to escape
* @returns YAML-safe string representation
*/
export function escapeYamlValue(value: string | undefined): string {
if (!value) {
return '';
}

const trimmed = value.trim();

// YAML doesn't allow newlines in simple quoted strings. Replace with a
// space, as there's no way to tell whether the user intended the linebreaks
// to be maintained (via a literal "|" block) or ignored (via a folded ">"
// block).
if (/[\r\n]/.test(trimmed)) {
const singleLine = trimmed.replace(/\s*[\r\n]+\s*/g, ' ');
return escapeYamlValue(singleLine);
}

// Quote the string if it starts with a YAML special character, or contains
// a colon followed by a space.
const needsQuoting =
/^[-?:,\[\]{}#&*!|>'"%@`]/.test(trimmed) ||
/:\s/.test(trimmed);

if (needsQuoting) {
// Backslashes and double-quotes must be escaped inside of YAML
// double-quoted strings. So we replace \ -> \\ and " -> \", before
// wrapping in double quotes.
return '"' + trimmed.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
}

return trimmed;
}