diff --git a/notes-service/src/main/java/io/meeds/notes/rest/model/PageEntity.java b/notes-service/src/main/java/io/meeds/notes/rest/model/PageEntity.java index 1085dc6ca7..aaaa3a1360 100644 --- a/notes-service/src/main/java/io/meeds/notes/rest/model/PageEntity.java +++ b/notes-service/src/main/java/io/meeds/notes/rest/model/PageEntity.java @@ -44,4 +44,6 @@ public class PageEntity implements Serializable { private String lang; private PagePropertiesEntity properties; + + private boolean extensionDataUpdated; } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/model/Page.java b/notes-service/src/main/java/org/exoplatform/wiki/model/Page.java index 415bf763a0..e96fddb8a8 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/model/Page.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/model/Page.java @@ -95,6 +95,8 @@ public class Page { private boolean hasChild; + private String latestVersionId; + private boolean isDeleted; private Page parent; diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java b/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java index 1cb46c63af..1f0c864e66 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/impl/NoteServiceImpl.java @@ -255,6 +255,7 @@ public Page createNote(Wiki noteBook, String parentNoteName, Page note, Identity note.setContent(note.getContent()); Page createdPage = createNote(noteBook, parentPage, note); NotePageProperties properties = note.getProperties(); + String draftPageId = String.valueOf(properties != null && properties.isDraft() ? properties.getNoteId() : null); try { if (properties != null) { properties.setNoteId(Long.parseLong(createdPage.getId())); @@ -273,6 +274,15 @@ public Page createNote(Wiki noteBook, String parentNoteName, Page note, Identity createdPage.setCanImport(canImportNotes(note.getAuthor(), space, note)); createdPage.setCanView(canViewNotes(note.getAuthor(), space, note)); } + dataStorage.addPageVersion(createdPage, userIdentity.getUserId()); + PageVersion pageVersion = dataStorage.getPublishedVersionByPageIdAndLang(Long.valueOf(createdPage.getId()), createdPage.getLang()); + createdPage.setLatestVersionId(pageVersion != null ? pageVersion.getId() : null); + if (pageVersion != null && draftPageId != null) { + Map eventData = new HashMap<>(); + eventData.put("draftPageId", draftPageId); + eventData.put("pageVersionId", pageVersion.getId()); + Utils.broadcast(listenerService, "note.page.version.created", this, eventData); + } return createdPage; } else { throw new EntityNotFoundException("Parent note not found"); @@ -1006,6 +1016,8 @@ public List getVersionsHistoryOfNote(Page note, String userName) th @Override public void createVersionOfNote(Page note, String userName) throws WikiException { PageVersion pageVersion = dataStorage.addPageVersion(note, userName); + String pageVersionId = pageVersion.getId(); + note.setLatestVersionId(pageVersionId); if (note.getLang() != null) { try { NotePageProperties properties = note.getProperties(); @@ -1027,8 +1039,14 @@ public void createVersionOfNote(Page note, String userName) throws WikiException null, NOTE_METADATA_PAGE_OBJECT_TYPE, NOTE_METADATA_VERSION_PAGE_OBJECT_TYPE, - userName - ); + userName); + } + DraftPage draftPage = dataStorage.getLatestDraftPageByTargetPageAndLang(Long.valueOf(note.getId()), note.getLang()); + if (draftPage != null) { + Map eventData = new HashMap<>(); + eventData.put("draftPageId", draftPage.getId()); + eventData.put("pageVersionId", pageVersionId); + Utils.broadcast(listenerService, "note.page.version.created", this, eventData); } } @@ -1211,6 +1229,14 @@ public DraftPage createDraftForExistPage(DraftPage draftPage, log.error("Failed to save draft note metadata", e); } newDraftPage.setProperties(properties); + // + PageVersion pageVersion = getPublishedVersionByPageIdAndLang(Long.valueOf(newDraftPage.getTargetPageId()), newDraftPage.getLang()); + if (pageVersion != null) { + Map eventData = new HashMap<>(); + eventData.put("pageVersionId", pageVersion.getId()); + eventData.put("draftForExistingPageId", newDraftPage.getId()); + Utils.broadcast(listenerService, "note.draft.for.exist.page.created", this, eventData); + } return newDraftPage; } @@ -1405,6 +1431,7 @@ public Page getNoteByIdAndLang(Long pageId, Identity userIdentity, String source page.setContent(publishedVersion.getContent()); page.setLang(publishedVersion.getLang()); page.setProperties(publishedVersion.getProperties()); + page.setLatestVersionId(publishedVersion.getId()); if (lang != null) { page.setMetadatas(retrieveMetadataItems(pageId + "-" + lang, userIdentity.getUserId())); } @@ -1421,6 +1448,7 @@ public Page getNoteByIdAndLang(Long pageId, String lang) { page.setTitle(publishedVersion.getTitle()); page.setContent(publishedVersion.getContent()); page.setLang(publishedVersion.getLang()); + page.setLatestVersionId(publishedVersion.getId()); } return page; } diff --git a/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java b/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java index c4e81abaab..3c78e38359 100644 --- a/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java +++ b/notes-service/src/main/java/org/exoplatform/wiki/service/rest/NotesRestService.java @@ -824,8 +824,15 @@ public Response updateNoteById(@Parameter(description = "Note id", required = tr WikiPageParams noteParams = new WikiPageParams(note_.getWikiType(), note_.getWikiOwner(), newNoteName); noteService.removeDraftOfNote(noteParams, note.getLang()); } - } else if (note_.isToBePublished()){ - note_ = noteService.updateNote(note_, PageUpdateType.PUBLISH, identity); + } else if (note_.isToBePublished()) { + note_ = noteService.updateNote(note_, PageUpdateType.PUBLISH, identity); + } else if (note.isExtensionDataUpdated()) { + note_ = noteService.updateNote(note_, PageUpdateType.EDIT_PAGE_CONTENT_AND_TITLE, identity); + noteService.createVersionOfNote(note_, identity.getUserId()); + if (!Utils.ANONYM_IDENTITY.equals(identity.getUserId())) { + WikiPageParams noteParams = new WikiPageParams(note_.getWikiType(), note_.getWikiOwner(), newNoteName); + noteService.removeDraftOfNote(noteParams, note.getLang()); + } } else { // in this case, the note didnt change on title nor content. As we need the page // url in front side, we compute it here diff --git a/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java b/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java index 9978ae29fe..a4f9130dd4 100644 --- a/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java +++ b/notes-service/src/test/java/org/exoplatform/wiki/service/TestNoteService.java @@ -866,7 +866,7 @@ public void testCreateDraftForNewPageWithProperties() throws Exception { } public void testCreateDraftForExistPageWithProperties() throws Exception { - Identity user = new Identity("user"); + Identity user = new Identity("root"); this.bindMockedUploadService(); NotePageProperties notePageProperties = createNotePageProperties(0L, "alt text", "summary Test"); DraftPage draftPage = new DraftPage(); @@ -972,10 +972,10 @@ public void testFeaturedImageWhenRemoveDraftById() throws Exception { } public void testGetDraftsOfWiki() throws Exception { - Identity user = new Identity("user"); + Identity root = new Identity("root"); Wiki portalWiki = getOrCreateWiki(wService, PortalConfig.PORTAL_TYPE, "testPortal"); - Page note = noteService.createNote(portalWiki, "Home", new Page("testGetDraftsOfWiki", "testGetDraftsOfWiki"), user); - Page note2 = noteService.createNote(portalWiki, "Home", new Page("testGetDraftsOfWiki", "testGetDraftsOfWiki"), user); + Page note = noteService.createNote(portalWiki, "Home", new Page("testGetDraftsOfWiki", "testGetDraftsOfWiki"), root); + Page note2 = noteService.createNote(portalWiki, "Home", new Page("testGetDraftsOfWiki", "testGetDraftsOfWiki"), root); DraftPage draftPage = new DraftPage(); draftPage.setTitle("test"); draftPage.setContent("test"); diff --git a/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/ckeditor/config.js b/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/ckeditor/config.js index ba6289729c..6231ce7741 100644 --- a/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/ckeditor/config.js +++ b/notes-webapp/src/main/webapp/WEB-INF/conf/wiki/ckeditor/config.js @@ -29,6 +29,7 @@ CKEDITOR.editorConfig = function (config) { 'Table', 'EmbedSemantic', 'CodeSnippet', + 'attachFile', 'InsertOptions' ]; if (webPageNote) { @@ -68,7 +69,7 @@ CKEDITOR.editorConfig = function (config) { }, { name: 'blocks', - items: ['Blockquote'] + items: ['Blockquote', 'attachFile'] }, ]; let extraPlugins = `a11ychecker,balloonpanel,indent,indentblock,indentlist,codesnippet,sharedspace,copyformatting,table,tabletools,embedsemantic,autolink,colordialog${!webPageNote && ',tagSuggester' || ''},emoji,link,font,justify,widget,${!webPageNote && ',insertOptions' || ''},contextmenu,tabletools,tableresize,toc,linkBalloon,suggester`; @@ -87,7 +88,7 @@ CKEDITOR.editorConfig = function (config) { } } const notesEditorExtensions = extensionRegistry.loadExtensions('NotesEditor', 'ckeditor-extensions'); - if (notesEditorExtensions?.length && this.useExtraPlugins) { + if (notesEditorExtensions?.length) { notesEditorExtensions.forEach(notesEditorExtension => { if (notesEditorExtension.extraPlugin) { extraPlugins = `${extraPlugins},${notesEditorExtension.extraPlugin}`; @@ -96,7 +97,7 @@ CKEDITOR.editorConfig = function (config) { removePlugins = `${extraPlugins},${notesEditorExtension.removePlugin}`; } if (notesEditorExtension.extraToolbarItem) { - toolbar[0].push(notesEditorExtension.extraToolbarItem); + toolbar[toolbar.length - 1].items.push(notesEditorExtension.extraToolbarItem); } }); } diff --git a/notes-webapp/src/main/webapp/vue-app/notes-editor/components/NotesEditorDashboard.vue b/notes-webapp/src/main/webapp/vue-app/notes-editor/components/NotesEditorDashboard.vue index 72c0417d18..6f1a079c7d 100644 --- a/notes-webapp/src/main/webapp/vue-app/notes-editor/components/NotesEditorDashboard.vue +++ b/notes-webapp/src/main/webapp/vue-app/notes-editor/components/NotesEditorDashboard.vue @@ -129,6 +129,8 @@ export default { saveButtonIcon: 'fas fa-save', translationSwitch: false, newTranslation: false, + autosaveProcessedFromEditorExtension: false, + extensionDataUpdated: false }; }, computed: { @@ -136,7 +138,7 @@ export default { return (!this.note?.title || this.note?.title?.length < 3 || this.note?.title?.length > this.titleMaxLength) || (this.noteNotModified - && !this.propertiesModified && !this.draftNote) || this.savingDraft; + && !this.propertiesModified && !this.draftNote && !this.note.draftPage) || this.savingDraft; }, noteNotModified() { return this.note?.title === this.originalNote?.title && this.$noteUtils.isSameContent(this.note?.content, this.originalNote?.content); @@ -195,6 +197,7 @@ export default { document.addEventListener('automatic-translation-extensions-updated', () => { this.refreshTranslationExtensions(); }); + document.addEventListener('note-editor-extensions-data-updated', (evt) => this.processAutoSaveFromEditorExtension(evt)); this.getAvailableLanguages(); window.addEventListener('beforeunload', () => { if (!this.postingNote && this.note.draftPage && this.note.id) { @@ -248,6 +251,65 @@ export default { }); }, methods: { + processAutoSaveFromEditorExtension(event) { + if (event.detail.processAutoSave) { + this.extensionDataUpdated = true; + this.autosaveProcessedFromEditorExtension = true; + this.draftSavingStatus = this.$t('notes.draft.savingDraftStatus'); + clearTimeout(this.saveDraft); + const draftNote = this.fillDraftNote(); + if (!draftNote.title) { + draftNote.title = this.$t('notes.untitled.title'); + } + draftNote.lang = this.selectedLanguage; + if (this.newDraft){ + draftNote.id = null; + } + if (draftNote.properties) { + draftNote.properties.draft = true; + if (this.newTranslation && !this.featuredImageUpdated) { + draftNote.properties.featuredImage = null; + } + } + this.$notesService.saveDraftNote(draftNote, this.parentPageId).then(savedDraftNote => { + this.actualNote = { + id: savedDraftNote.id, + name: savedDraftNote.name, + title: savedDraftNote.title, + content: savedDraftNote.content, + author: savedDraftNote.author, + owner: savedDraftNote.owner, + properties: savedDraftNote.properties + }; + this.newDraft=false; + savedDraftNote.parentPageId = this.parentPageId; + this.note = savedDraftNote; + localStorage.setItem(`draftNoteId-${this.note.id}-${this.selectedLanguage}`, JSON.stringify(savedDraftNote)); + this.newTranslation = false; + }).then(() => { + this.savingDraft = false; + setTimeout(() => { + this.draftSavingStatus = this.$t('notes.draft.savedDraftStatus'); + if (this.autosaveProcessedFromEditorExtension) { + document.dispatchEvent(new CustomEvent('note-draft-auto-save-done', { + detail: { + draftId: this.note.id + } + })); + } + this.autosaveProcessedFromEditorExtension = false; + }, this.autoSaveDelay); + }).catch(e => { + console.error('Error when creating draft note: ', e); + this.$root.$emit('show-alert', { + type: 'error', + message: this.$t(`notes.message.${e.message}`) + }); + }); + } else { + this.draftSavingStatus = this.$t('notes.draft.savedDraftStatus'); + } + }, editorClosed() { window.close(); }, @@ -299,7 +361,8 @@ export default { parentPageId: this.note?.draftPage && this.note?.targetPageId === this.parentPageId ? null : this.parentPageId, toBePublished: false, appName: this.appName, - properties: properties + properties: properties, + extensionDataUpdated: this.extensionDataUpdated }; if (note.id) { this.updateNote(note); @@ -332,9 +395,14 @@ export default { }).finally(() => { this.enableClickOnce(); this.removeLocalStorageCurrentDraft(currentDraftId); + this.extensionDataUpdated = false; }); }, createNote(note) { + note.properties = { + draft: this.note?.draftPage, + noteId: this.note?.id + }; return this.$notesService.createNote(note).then(data => { const draftNote = JSON.parse(localStorage.getItem(`draftNoteId-${this.note.id}-${this.selectedLanguage}`)); this.note = data; diff --git a/notes-webapp/src/main/webapp/vue-app/notes-rich-editor/components/NoteFullRichEditor.vue b/notes-webapp/src/main/webapp/vue-app/notes-rich-editor/components/NoteFullRichEditor.vue index 53415268ea..6293029094 100644 --- a/notes-webapp/src/main/webapp/vue-app/notes-rich-editor/components/NoteFullRichEditor.vue +++ b/notes-webapp/src/main/webapp/vue-app/notes-rich-editor/components/NoteFullRichEditor.vue @@ -54,7 +54,8 @@ :placeholder="titlePlaceholder" type="text" :maxlength="noteTitleMaxLength + 1" - class="py-0 px-1 mt-5 mb-0"> + class="py-0 px-1 mt-5 mb-0" + @input="waitUserTyping()">