mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
e11e57f1d8
The implementation uses / symbol as a nesting separator. I.e. tag/subtag is a nested tag, where tag is the parent tag and subtag is its child. Creating a tag named tag/subtag/subsubtag creates three tags, one for each level. The tags are associated using parent_id field. In the app, viewing notes with a tag will also show all notes that are associated with any of the tag's descendant tags (same for the note count). Deleting a tag will also delete all its descendant tags. In the desktop app the tags are shown nested just like the notebooks.
96 lines
2.8 KiB
JavaScript
96 lines
2.8 KiB
JavaScript
const { BaseCommand } = require('./base-command.js');
|
|
const { app } = require('./app.js');
|
|
const { _ } = require('lib/locale.js');
|
|
const Tag = require('lib/models/Tag.js');
|
|
const BaseModel = require('lib/BaseModel.js');
|
|
const { time } = require('lib/time-utils.js');
|
|
|
|
class Command extends BaseCommand {
|
|
usage() {
|
|
return 'tag <tag-command> [tag] [note]';
|
|
}
|
|
|
|
description() {
|
|
return _('<tag-command> can be "add", "remove", "list", or "notetags" to assign or remove [tag] from [note], to list notes associated with [tag], or to list tags associated with [note]. The command `tag list` can be used to list all the tags (use -l for long option).');
|
|
}
|
|
|
|
options() {
|
|
return [['-l, --long', _('Use long list format. Format is ID, NOTE_COUNT (for notebook), DATE, TODO_CHECKED (for to-dos), TITLE')]];
|
|
}
|
|
|
|
async action(args) {
|
|
let tag = null;
|
|
const options = args.options;
|
|
|
|
if (args.tag) tag = await app().loadItem(BaseModel.TYPE_TAG, args.tag);
|
|
let notes = [];
|
|
if (args.note) {
|
|
notes = await app().loadItems(BaseModel.TYPE_NOTE, args.note);
|
|
}
|
|
|
|
const command = args['tag-command'];
|
|
|
|
if (command == 'remove' && !tag) throw new Error(_('Cannot find "%s".', args.tag));
|
|
|
|
if (command == 'add') {
|
|
if (!notes.length) throw new Error(_('Cannot find "%s".', args.note));
|
|
if (!tag) tag = await Tag.saveNested({}, args.tag, { userSideValidation: true });
|
|
for (let i = 0; i < notes.length; i++) {
|
|
await Tag.addNote(tag.id, notes[i].id);
|
|
}
|
|
} else if (command == 'remove') {
|
|
if (!tag) throw new Error(_('Cannot find "%s".', args.tag));
|
|
if (!notes.length) throw new Error(_('Cannot find "%s".', args.note));
|
|
for (let i = 0; i < notes.length; i++) {
|
|
await Tag.removeNote(tag.id, notes[i].id);
|
|
}
|
|
} else if (command == 'list') {
|
|
if (tag) {
|
|
const notes = await Tag.notes(tag.id);
|
|
notes.map(note => {
|
|
let line = '';
|
|
if (options.long) {
|
|
line += BaseModel.shortId(note.id);
|
|
line += ' ';
|
|
line += time.formatMsToLocal(note.user_updated_time);
|
|
line += ' ';
|
|
}
|
|
if (note.is_todo) {
|
|
line += '[';
|
|
if (note.todo_completed) {
|
|
line += 'X';
|
|
} else {
|
|
line += ' ';
|
|
}
|
|
line += '] ';
|
|
} else {
|
|
line += ' ';
|
|
}
|
|
line += note.title;
|
|
this.stdout(line);
|
|
});
|
|
} else {
|
|
const tags = await Tag.all();
|
|
tags.map(tag => {
|
|
this.stdout(Tag.getCachedFullTitle(tag.id));
|
|
});
|
|
}
|
|
} else if (command == 'notetags') {
|
|
if (args.tag) {
|
|
const note = await app().loadItem(BaseModel.TYPE_NOTE, args.tag);
|
|
if (!note) throw new Error(_('Cannot find "%s".', args.tag));
|
|
const tags = await Tag.tagsByNoteId(note.id);
|
|
tags.map(tag => {
|
|
this.stdout(tag.title);
|
|
});
|
|
} else {
|
|
throw new Error(_('Cannot find "%s".', ''));
|
|
}
|
|
} else {
|
|
throw new Error(_('Invalid command: "%s"', command));
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = Command;
|