mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	added rename note bulk action
This commit is contained in:
		
							
								
								
									
										11
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										11
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -22,6 +22,7 @@ | |||||||
|         "cookie-parser": "1.4.6", |         "cookie-parser": "1.4.6", | ||||||
|         "csurf": "1.11.0", |         "csurf": "1.11.0", | ||||||
|         "dayjs": "1.11.3", |         "dayjs": "1.11.3", | ||||||
|  |         "dayjs-plugin-utc": "^0.1.2", | ||||||
|         "ejs": "3.1.8", |         "ejs": "3.1.8", | ||||||
|         "electron-debug": "3.2.0", |         "electron-debug": "3.2.0", | ||||||
|         "electron-dl": "3.3.1", |         "electron-dl": "3.3.1", | ||||||
| @@ -3097,6 +3098,11 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz", |       "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz", | ||||||
|       "integrity": "sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==" |       "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": { |     "node_modules/debug": { | ||||||
|       "version": "4.3.4", |       "version": "4.3.4", | ||||||
|       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", |       "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", |       "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.3.tgz", | ||||||
|       "integrity": "sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==" |       "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": { |     "debug": { | ||||||
|       "version": "4.3.4", |       "version": "4.3.4", | ||||||
|       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", |       "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", | ||||||
|   | |||||||
| @@ -37,6 +37,7 @@ | |||||||
|     "cookie-parser": "1.4.6", |     "cookie-parser": "1.4.6", | ||||||
|     "csurf": "1.11.0", |     "csurf": "1.11.0", | ||||||
|     "dayjs": "1.11.3", |     "dayjs": "1.11.3", | ||||||
|  |     "dayjs-plugin-utc": "^0.1.2", | ||||||
|     "ejs": "3.1.8", |     "ejs": "3.1.8", | ||||||
|     "electron-debug": "3.2.0", |     "electron-debug": "3.2.0", | ||||||
|     "electron-dl": "3.3.1", |     "electron-dl": "3.3.1", | ||||||
|   | |||||||
| @@ -14,16 +14,19 @@ let becca = null; | |||||||
|  * Base class for all backend entities. |  * Base class for all backend entities. | ||||||
|  */ |  */ | ||||||
| class AbstractEntity { | class AbstractEntity { | ||||||
|  |     /** @protected */ | ||||||
|     beforeSaving() { |     beforeSaving() { | ||||||
|         this.generateIdIfNecessary(); |         this.generateIdIfNecessary(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @protected */ | ||||||
|     generateIdIfNecessary() { |     generateIdIfNecessary() { | ||||||
|         if (!this[this.constructor.primaryKeyName]) { |         if (!this[this.constructor.primaryKeyName]) { | ||||||
|             this[this.constructor.primaryKeyName] = utils.newEntityId(); |             this[this.constructor.primaryKeyName] = utils.newEntityId(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @protected */ | ||||||
|     generateHash(isDeleted = false) { |     generateHash(isDeleted = false) { | ||||||
|         let contentToHash = ""; |         let contentToHash = ""; | ||||||
|  |  | ||||||
| @@ -38,10 +41,12 @@ class AbstractEntity { | |||||||
|         return utils.hash(contentToHash).substr(0, 10); |         return utils.hash(contentToHash).substr(0, 10); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @protected */ | ||||||
|     getUtcDateChanged() { |     getUtcDateChanged() { | ||||||
|         return this.utcDateModified || this.utcDateCreated; |         return this.utcDateModified || this.utcDateCreated; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @protected */ | ||||||
|     get becca() { |     get becca() { | ||||||
|         if (!becca) { |         if (!becca) { | ||||||
|             becca = require('../becca'); |             becca = require('../becca'); | ||||||
| @@ -50,6 +55,7 @@ class AbstractEntity { | |||||||
|         return becca; |         return becca; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @protected */ | ||||||
|     addEntityChange(isDeleted = false) { |     addEntityChange(isDeleted = false) { | ||||||
|         entityChangesService.addEntityChange({ |         entityChangesService.addEntityChange({ | ||||||
|             entityName: this.constructor.entityName, |             entityName: this.constructor.entityName, | ||||||
| @@ -61,6 +67,7 @@ class AbstractEntity { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @protected */ | ||||||
|     getPojoToSave() { |     getPojoToSave() { | ||||||
|         return this.getPojo(); |         return this.getPojo(); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -62,6 +62,7 @@ class Attribute extends AbstractEntity { | |||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     init() { |     init() { | ||||||
|         if (this.attributeId) { |         if (this.attributeId) { | ||||||
|             this.becca.attributes[this.attributeId] = this; |             this.becca.attributes[this.attributeId] = this; | ||||||
|   | |||||||
| @@ -9,6 +9,9 @@ const entityChangesService = require('../../services/entity_changes'); | |||||||
| const AbstractEntity = require("./abstract_entity"); | const AbstractEntity = require("./abstract_entity"); | ||||||
| const NoteRevision = require("./note_revision"); | const NoteRevision = require("./note_revision"); | ||||||
| const TaskContext = require("../../services/task_context"); | const TaskContext = require("../../services/task_context"); | ||||||
|  | const dayjs = require("dayjs"); | ||||||
|  | const utc = require('dayjs/plugin/utc') | ||||||
|  | dayjs.extend(utc) | ||||||
|  |  | ||||||
| const LABEL = 'label'; | const LABEL = 'label'; | ||||||
| const RELATION = 'relation'; | const RELATION = 'relation'; | ||||||
| @@ -84,13 +87,17 @@ class Note extends AbstractEntity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     init() { |     init() { | ||||||
|         /** @type {Branch[]} */ |         /** @type {Branch[]} | ||||||
|  |          * @private */ | ||||||
|         this.parentBranches = []; |         this.parentBranches = []; | ||||||
|         /** @type {Note[]} */ |         /** @type {Note[]} | ||||||
|  |          * @private */ | ||||||
|         this.parents = []; |         this.parents = []; | ||||||
|         /** @type {Note[]} */ |         /** @type {Note[]} | ||||||
|  |          * @private*/ | ||||||
|         this.children = []; |         this.children = []; | ||||||
|         /** @type {Attribute[]} */ |         /** @type {Attribute[]} | ||||||
|  |          * @private */ | ||||||
|         this.ownedAttributes = []; |         this.ownedAttributes = []; | ||||||
|  |  | ||||||
|         /** @type {Attribute[]|null} |         /** @type {Attribute[]|null} | ||||||
| @@ -100,7 +107,8 @@ class Note extends AbstractEntity { | |||||||
|          * @private*/ |          * @private*/ | ||||||
|         this.inheritableAttributeCache = null; |         this.inheritableAttributeCache = null; | ||||||
|  |  | ||||||
|         /** @type {Attribute[]} */ |         /** @type {Attribute[]} | ||||||
|  |          * @private*/ | ||||||
|         this.targetRelations = []; |         this.targetRelations = []; | ||||||
|  |  | ||||||
|         this.becca.addNote(this.noteId, this); |         this.becca.addNote(this.noteId, this); | ||||||
| @@ -114,16 +122,19 @@ class Note extends AbstractEntity { | |||||||
|         /** |         /** | ||||||
|          * size of the content in bytes |          * size of the content in bytes | ||||||
|          * @type {int|null} |          * @type {int|null} | ||||||
|  |          * @private | ||||||
|          */ |          */ | ||||||
|         this.contentSize = null; |         this.contentSize = null; | ||||||
|         /** |         /** | ||||||
|          * size of the content and note revision contents in bytes |          * size of the content and note revision contents in bytes | ||||||
|          * @type {int|null} |          * @type {int|null} | ||||||
|  |          * @private | ||||||
|          */ |          */ | ||||||
|         this.noteSize = null; |         this.noteSize = null; | ||||||
|         /** |         /** | ||||||
|          * number of note revisions for this note |          * number of note revisions for this note | ||||||
|          * @type {int|null} |          * @type {int|null} | ||||||
|  |          * @private | ||||||
|          */ |          */ | ||||||
|         this.revisionCount = null; |         this.revisionCount = null; | ||||||
|     } |     } | ||||||
| @@ -225,6 +236,22 @@ class Note extends AbstractEntity { | |||||||
|             WHERE noteId = ?`, [this.noteId]); |             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 {*} */ |     /** @returns {*} */ | ||||||
|     getJsonContent() { |     getJsonContent() { | ||||||
|         const content = this.getContent(); |         const content = this.getContent(); | ||||||
| @@ -350,6 +377,7 @@ class Note extends AbstractEntity { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @private */ | ||||||
|     __getAttributes(path) { |     __getAttributes(path) { | ||||||
|         if (path.includes(this.noteId)) { |         if (path.includes(this.noteId)) { | ||||||
|             return []; |             return []; | ||||||
| @@ -401,7 +429,10 @@ class Note extends AbstractEntity { | |||||||
|         return this.__attributeCache; |         return this.__attributeCache; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {Attribute[]} */ |     /** | ||||||
|  |      * @private | ||||||
|  |      * @returns {Attribute[]} | ||||||
|  |      */ | ||||||
|     __getInheritableAttributes(path) { |     __getInheritableAttributes(path) { | ||||||
|         if (path.includes(this.noteId)) { |         if (path.includes(this.noteId)) { | ||||||
|             return []; |             return []; | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ import UpdateRelationTargetBulkAction from "../widgets/bulk_actions/relation/upd | |||||||
| import ExecuteScriptBulkAction from "../widgets/bulk_actions/execute_script.js"; | import ExecuteScriptBulkAction from "../widgets/bulk_actions/execute_script.js"; | ||||||
| import AddLabelBulkAction from "../widgets/bulk_actions/label/add_label.js"; | import AddLabelBulkAction from "../widgets/bulk_actions/label/add_label.js"; | ||||||
| import AddRelationBulkAction from "../widgets/bulk_actions/relation/add_relation.js"; | import AddRelationBulkAction from "../widgets/bulk_actions/relation/add_relation.js"; | ||||||
|  | import RenameNoteBulkAction from "../widgets/bulk_actions/note/rename_note.js"; | ||||||
|  |  | ||||||
| const ACTION_GROUPS = [ | const ACTION_GROUPS = [ | ||||||
|     { |     { | ||||||
| @@ -24,7 +25,7 @@ const ACTION_GROUPS = [ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         title: 'Notes', |         title: 'Notes', | ||||||
|         actions: [DeleteNoteBulkAction, DeleteNoteRevisionsBulkAction, MoveNoteBulkAction], |         actions: [RenameNoteBulkAction, MoveNoteBulkAction, DeleteNoteBulkAction, DeleteNoteRevisionsBulkAction], | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|         title: 'Other', |         title: 'Other', | ||||||
| @@ -33,6 +34,7 @@ const ACTION_GROUPS = [ | |||||||
| ]; | ]; | ||||||
|  |  | ||||||
| const ACTION_CLASSES = [ | const ACTION_CLASSES = [ | ||||||
|  |     RenameNoteBulkAction, | ||||||
|     MoveNoteBulkAction, |     MoveNoteBulkAction, | ||||||
|     DeleteNoteBulkAction, |     DeleteNoteBulkAction, | ||||||
|     DeleteNoteRevisionsBulkAction, |     DeleteNoteRevisionsBulkAction, | ||||||
|   | |||||||
| @@ -9,6 +9,17 @@ const TPL = ` | |||||||
|     </td> |     </td> | ||||||
|     <td class="button-column"> |     <td class="button-column"> | ||||||
|         <span class="bx bx-x icon-action action-conf-del"></span> |         <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> |     </td> | ||||||
| </tr>`; | </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; |     box-shadow: 10px 10px 93px -25px black; | ||||||
|     padding: 10px 15px 10px 15px !important; |     padding: 10px 15px 10px 15px !important; | ||||||
|     width: 600px; |     width: 600px; | ||||||
|  |     white-space: normal; | ||||||
| } | } | ||||||
|  |  | ||||||
| .help-dropdown .dropdown-menu pre { | .help-dropdown .dropdown-menu pre { | ||||||
|   | |||||||
| @@ -4,6 +4,8 @@ const becca = require("../becca/becca"); | |||||||
| const cloningService = require("./cloning"); | const cloningService = require("./cloning"); | ||||||
| const branchService = require("./branches"); | const branchService = require("./branches"); | ||||||
| const utils = require("./utils"); | const utils = require("./utils"); | ||||||
|  | const dayjs = require("dayjs"); | ||||||
|  | const cls = require("./cls.js"); | ||||||
|  |  | ||||||
| const ACTION_HANDLERS = { | const ACTION_HANDLERS = { | ||||||
|     addLabel: (action, note) => { |     addLabel: (action, note) => { | ||||||
| @@ -30,6 +32,17 @@ const ACTION_HANDLERS = { | |||||||
|             relation.markAsDeleted(); |             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) => { |     renameLabel: (action, note) => { | ||||||
|         for (const label of note.getOwnedLabels(action.oldLabelName)) { |         for (const label of note.getOwnedLabels(action.oldLabelName)) { | ||||||
|             // attribute name is immutable, renaming means delete old + create new |             // attribute name is immutable, renaming means delete old + create new | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user