added rename note bulk action

This commit is contained in:
zadam 2022-06-12 23:29:11 +02:00
parent 7ba619c71d
commit f8fd8e47a9
10 changed files with 141 additions and 7 deletions

11
package-lock.json generated
View File

@ -22,6 +22,7 @@
"cookie-parser": "1.4.6",
"csurf": "1.11.0",
"dayjs": "1.11.3",
"dayjs-plugin-utc": "^0.1.2",
"ejs": "3.1.8",
"electron-debug": "3.2.0",
"electron-dl": "3.3.1",
@ -3097,6 +3098,11 @@
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz",
"integrity": "sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A=="
},
"node_modules/dayjs-plugin-utc": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/dayjs-plugin-utc/-/dayjs-plugin-utc-0.1.2.tgz",
"integrity": "sha512-ExERH5o3oo6jFOdkvMP3gytTCQ9Ksi5PtylclJWghr7k7m3o2U5QrwtdiJkOxLOH4ghr0EKhpqGefzGz1VvVJg=="
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -13198,6 +13204,11 @@
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz",
"integrity": "sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A=="
},
"dayjs-plugin-utc": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/dayjs-plugin-utc/-/dayjs-plugin-utc-0.1.2.tgz",
"integrity": "sha512-ExERH5o3oo6jFOdkvMP3gytTCQ9Ksi5PtylclJWghr7k7m3o2U5QrwtdiJkOxLOH4ghr0EKhpqGefzGz1VvVJg=="
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",

View File

@ -37,6 +37,7 @@
"cookie-parser": "1.4.6",
"csurf": "1.11.0",
"dayjs": "1.11.3",
"dayjs-plugin-utc": "^0.1.2",
"ejs": "3.1.8",
"electron-debug": "3.2.0",
"electron-dl": "3.3.1",

View File

@ -14,16 +14,19 @@ let becca = null;
* Base class for all backend entities.
*/
class AbstractEntity {
/** @protected */
beforeSaving() {
this.generateIdIfNecessary();
}
/** @protected */
generateIdIfNecessary() {
if (!this[this.constructor.primaryKeyName]) {
this[this.constructor.primaryKeyName] = utils.newEntityId();
}
}
/** @protected */
generateHash(isDeleted = false) {
let contentToHash = "";
@ -38,10 +41,12 @@ class AbstractEntity {
return utils.hash(contentToHash).substr(0, 10);
}
/** @protected */
getUtcDateChanged() {
return this.utcDateModified || this.utcDateCreated;
}
/** @protected */
get becca() {
if (!becca) {
becca = require('../becca');
@ -50,6 +55,7 @@ class AbstractEntity {
return becca;
}
/** @protected */
addEntityChange(isDeleted = false) {
entityChangesService.addEntityChange({
entityName: this.constructor.entityName,
@ -61,6 +67,7 @@ class AbstractEntity {
});
}
/** @protected */
getPojoToSave() {
return this.getPojo();
}

View File

@ -62,6 +62,7 @@ class Attribute extends AbstractEntity {
return this;
}
init() {
if (this.attributeId) {
this.becca.attributes[this.attributeId] = this;

View File

@ -9,6 +9,9 @@ const entityChangesService = require('../../services/entity_changes');
const AbstractEntity = require("./abstract_entity");
const NoteRevision = require("./note_revision");
const TaskContext = require("../../services/task_context");
const dayjs = require("dayjs");
const utc = require('dayjs/plugin/utc')
dayjs.extend(utc)
const LABEL = 'label';
const RELATION = 'relation';
@ -84,13 +87,17 @@ class Note extends AbstractEntity {
}
init() {
/** @type {Branch[]} */
/** @type {Branch[]}
* @private */
this.parentBranches = [];
/** @type {Note[]} */
/** @type {Note[]}
* @private */
this.parents = [];
/** @type {Note[]} */
/** @type {Note[]}
* @private*/
this.children = [];
/** @type {Attribute[]} */
/** @type {Attribute[]}
* @private */
this.ownedAttributes = [];
/** @type {Attribute[]|null}
@ -100,7 +107,8 @@ class Note extends AbstractEntity {
* @private*/
this.inheritableAttributeCache = null;
/** @type {Attribute[]} */
/** @type {Attribute[]}
* @private*/
this.targetRelations = [];
this.becca.addNote(this.noteId, this);
@ -114,16 +122,19 @@ class Note extends AbstractEntity {
/**
* size of the content in bytes
* @type {int|null}
* @private
*/
this.contentSize = null;
/**
* size of the content and note revision contents in bytes
* @type {int|null}
* @private
*/
this.noteSize = null;
/**
* number of note revisions for this note
* @type {int|null}
* @private
*/
this.revisionCount = null;
}
@ -225,6 +236,22 @@ class Note extends AbstractEntity {
WHERE noteId = ?`, [this.noteId]);
}
get dateCreatedObj() {
return this.dateCreated === null ? null : dayjs(this.dateCreated);
}
get utcDateCreatedObj() {
return this.utcDateCreated === null ? null : dayjs.utc(this.utcDateCreated);
}
get dateModifiedObj() {
return this.dateModified === null ? null : dayjs(this.dateModified);
}
get utcDateModifiedObj() {
return this.utcDateModified === null ? null : dayjs.utc(this.utcDateModified);
}
/** @returns {*} */
getJsonContent() {
const content = this.getContent();
@ -350,6 +377,7 @@ class Note extends AbstractEntity {
}
}
/** @private */
__getAttributes(path) {
if (path.includes(this.noteId)) {
return [];
@ -401,7 +429,10 @@ class Note extends AbstractEntity {
return this.__attributeCache;
}
/** @returns {Attribute[]} */
/**
* @private
* @returns {Attribute[]}
*/
__getInheritableAttributes(path) {
if (path.includes(this.noteId)) {
return [];

View File

@ -12,6 +12,7 @@ import UpdateRelationTargetBulkAction from "../widgets/bulk_actions/relation/upd
import ExecuteScriptBulkAction from "../widgets/bulk_actions/execute_script.js";
import AddLabelBulkAction from "../widgets/bulk_actions/label/add_label.js";
import AddRelationBulkAction from "../widgets/bulk_actions/relation/add_relation.js";
import RenameNoteBulkAction from "../widgets/bulk_actions/note/rename_note.js";
const ACTION_GROUPS = [
{
@ -24,7 +25,7 @@ const ACTION_GROUPS = [
},
{
title: 'Notes',
actions: [DeleteNoteBulkAction, DeleteNoteRevisionsBulkAction, MoveNoteBulkAction],
actions: [RenameNoteBulkAction, MoveNoteBulkAction, DeleteNoteBulkAction, DeleteNoteRevisionsBulkAction],
},
{
title: 'Other',
@ -33,6 +34,7 @@ const ACTION_GROUPS = [
];
const ACTION_CLASSES = [
RenameNoteBulkAction,
MoveNoteBulkAction,
DeleteNoteBulkAction,
DeleteNoteRevisionsBulkAction,

View File

@ -9,6 +9,17 @@ const TPL = `
</td>
<td class="button-column">
<span class="bx bx-x icon-action action-conf-del"></span>
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
<p>This will delete matched notes.</p>
<p>After the deletion, it's possible to undelete them from <span class="bx bx-history"></span> Recent Notes dialog.</p>
<p>To erase notes permanently, you can go after the deletion to the Option -> Other and click the "Erase deleted notes now" button.</p>
</div>
</div>
</td>
</tr>`;

View File

@ -0,0 +1,56 @@
import SpacedUpdate from "../../../services/spaced_update.js";
import AbstractBulkAction from "../abstract_bulk_action.js";
const TPL = `
<tr>
<td colspan="2">
<div style="display: flex; align-items: center">
<div style="margin-right: 10px; flex-shrink: 0;">Rename note title to:</div>
<input type="text"
class="form-control new-title"
placeholder="new note title"
title="Click help icon on the right to see all the options"/>
</div>
</td>
<td class="button-column">
<div class="dropdown help-dropdown">
<span class="bx bx-help-circle icon-action" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></span>
<div class="dropdown-menu dropdown-menu-right p-4">
<p>The given value is evaluated as JavaScript string and thus can be enriched with dynamic content via the injected <code>note</code> variable (note being renamed). Examples:</p>
<ul>
<li><code>Note</code> - all matched notes are renamed to "Note"</li>
<li><code>NEW: \${note.title}</code> - matched notes titles are prefixed with "NEW: "</li>
<li><code>\${note.dateCreatedObj.format('MM-DD:')}: \${note.title}</code> - matched notes are prefixed with note's creation month-date</li>
</ul>
See API docs for <a href="https://zadam.github.io/trilium/backend_api/Note.html">note</a> and its <a href="https://day.js.org/docs/en/display/format">dateCreatedObj / utcDateCreatedObj properties</a> for details.
</div>
</div>
<span class="bx bx-x icon-action action-conf-del"></span>
</td>
</tr>`;
export default class RenameNoteBulkAction extends AbstractBulkAction {
static get actionName() { return "renameNote"; }
static get actionTitle() { return "Rename note"; }
doRender() {
const $action = $(TPL);
const $newTitle = $action.find('.new-title');
$newTitle.val(this.actionDef.newTitle || "");
const spacedUpdate = new SpacedUpdate(async () => {
await this.saveAction({
newTitle: $newTitle.val(),
});
}, 1000);
$newTitle.on('input', () => spacedUpdate.scheduleUpdate());
return $action;
}
}

View File

@ -868,6 +868,7 @@ body {
box-shadow: 10px 10px 93px -25px black;
padding: 10px 15px 10px 15px !important;
width: 600px;
white-space: normal;
}
.help-dropdown .dropdown-menu pre {

View File

@ -4,6 +4,8 @@ const becca = require("../becca/becca");
const cloningService = require("./cloning");
const branchService = require("./branches");
const utils = require("./utils");
const dayjs = require("dayjs");
const cls = require("./cls.js");
const ACTION_HANDLERS = {
addLabel: (action, note) => {
@ -30,6 +32,17 @@ const ACTION_HANDLERS = {
relation.markAsDeleted();
}
},
renameNote: (action, note) => {
// "officially" injected value:
// - note
const newTitle = eval('`' + action.newTitle + '`');
if (note.title !== newTitle) {
note.title = newTitle;
note.save();
}
},
renameLabel: (action, note) => {
for (const label of note.getOwnedLabels(action.oldLabelName)) {
// attribute name is immutable, renaming means delete old + create new