mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 11:39:01 +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
	 zadam
						zadam