mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
added rename note bulk action
This commit is contained in:
parent
7ba619c71d
commit
f8fd8e47a9
11
package-lock.json
generated
11
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ class Attribute extends AbstractEntity {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
init() {
|
||||
if (this.attributeId) {
|
||||
this.becca.attributes[this.attributeId] = this;
|
||||
|
@ -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 [];
|
||||
|
@ -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,
|
||||
|
@ -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>`;
|
||||
|
||||
|
56
src/public/app/widgets/bulk_actions/note/rename_note.js
Normal file
56
src/public/app/widgets/bulk_actions/note/rename_note.js
Normal 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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user