From b09877f07f126d0bfe1333e29857cfa147d6f7c3 Mon Sep 17 00:00:00 2001 From: Sofien Haj Chedhli Date: Thu, 12 Sep 2024 18:04:04 +0100 Subject: [PATCH 01/14] feat: Implement the administration management of automatic translation for news - EXO-72353 - Meeds-io/MIPs#129 (#225) --- .../java/io/meeds/news/rest/NewsRest.java | 91 ++++++++++--------- .../components/ContentRichEditor.vue | 2 +- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/content-service/src/main/java/io/meeds/news/rest/NewsRest.java b/content-service/src/main/java/io/meeds/news/rest/NewsRest.java index cf7c4df60..ec20db0c3 100644 --- a/content-service/src/main/java/io/meeds/news/rest/NewsRest.java +++ b/content-service/src/main/java/io/meeds/news/rest/NewsRest.java @@ -750,51 +750,6 @@ public ResponseEntity canPublishNews(@RequestParam(name = "spaceId", re } } - private NewsFilter buildFilter(List spaces, String filter, String text, String author, int limit, int offset) { - NewsFilter newsFilter = new NewsFilter(); - - newsFilter.setSpaces(spaces); - if (StringUtils.isNotEmpty(filter)) { - FilterType filterType = FilterType.valueOf(filter.toUpperCase()); - switch (filterType) { - case PINNED: { - newsFilter.setPublishedNews(true); - break; - } - - case MYPOSTED: { - if (StringUtils.isNotEmpty(author)) { - newsFilter.setAuthor(author); - } - break; - } - case DRAFTS: { - if (StringUtils.isNotEmpty(author)) { - newsFilter.setAuthor(author); - } - newsFilter.setDraftNews(true); - break; - } - case SCHEDULED: { - if (StringUtils.isNotEmpty(author)) { - newsFilter.setAuthor(author); - } - newsFilter.setScheduledNews(true); - break; - } - } - newsFilter.setOrder("UPDATED_DATE"); - } - // Set text to search news with - if (StringUtils.isNotEmpty(text) && text.indexOf("#") != 0) { - newsFilter.setSearchText(text); - } - newsFilter.setLimit(limit); - newsFilter.setOffset(offset); - - return newsFilter; - } - @DeleteMapping(path = "translation/{id}", produces = MediaType.APPLICATION_JSON_VALUE) @Secured("users") @Operation(summary = "Delete article version with language", method = "DELETE", description = "This deletes the article version ") @@ -859,4 +814,50 @@ public ResponseEntity> getAvailableTranslationLanguages(@PathVariab return ResponseEntity.internalServerError().build(); } } + + private NewsFilter buildFilter(List spaces, String filter, String text, String author, int limit, int offset) { + NewsFilter newsFilter = new NewsFilter(); + + newsFilter.setSpaces(spaces); + if (StringUtils.isNotEmpty(filter)) { + FilterType filterType = FilterType.valueOf(filter.toUpperCase()); + switch (filterType) { + case PINNED: { + newsFilter.setPublishedNews(true); + break; + } + + case MYPOSTED: { + if (StringUtils.isNotEmpty(author)) { + newsFilter.setAuthor(author); + } + break; + } + case DRAFTS: { + if (StringUtils.isNotEmpty(author)) { + newsFilter.setAuthor(author); + } + newsFilter.setDraftNews(true); + break; + } + case SCHEDULED: { + if (StringUtils.isNotEmpty(author)) { + newsFilter.setAuthor(author); + } + newsFilter.setScheduledNews(true); + break; + } + } + newsFilter.setOrder("UPDATED_DATE"); + } + // Set text to search news with + if (StringUtils.isNotEmpty(text) && text.indexOf("#") != 0) { + newsFilter.setSearchText(text); + } + newsFilter.setLimit(limit); + newsFilter.setOffset(offset); + + return newsFilter; + } + } diff --git a/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue b/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue index 1a4434afd..9b3c6f25f 100644 --- a/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue +++ b/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue @@ -696,7 +696,7 @@ export default { return this.$noteUtils.isSameContent(this.article.content, this.originalArticle.content); }, refreshTranslationExtensions() { - this.editorExtensions = extensionRegistry.loadExtensions('notesEditor', 'translation-extension'); + this.editorExtensions = extensionRegistry.loadExtensions('contentEditor', 'translation-extension'); }, isEmptyDraft() { const isTitleEmpty = !this.article?.title; From 434dbdb50a8871166eca42707c8d90df122ef69e Mon Sep 17 00:00:00 2001 From: Sofien Haj Chedhli Date: Tue, 17 Sep 2024 13:13:26 +0100 Subject: [PATCH 02/14] feat: Add the newsServices as a prototype in the favorite drawer extension - EXO-74122 - Meeds-io/MIPs#129 (#229) --- .../news-extensions/favorite-drawer-extensions/main.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/content-webapp/src/main/webapp/vue-app/news-extensions/favorite-drawer-extensions/main.js b/content-webapp/src/main/webapp/vue-app/news-extensions/favorite-drawer-extensions/main.js index 8a41dc1bc..12c53b02c 100644 --- a/content-webapp/src/main/webapp/vue-app/news-extensions/favorite-drawer-extensions/main.js +++ b/content-webapp/src/main/webapp/vue-app/news-extensions/favorite-drawer-extensions/main.js @@ -19,6 +19,13 @@ */ import './initComponents.js'; import {initExtensions} from './extensions.js'; +import * as newsServices from '../../services/newsServices.js'; + +if (!Vue.prototype.$newsServices) { + window.Object.defineProperty(Vue.prototype, '$newsServices', { + value: newsServices, + }); +} export function init() { initExtensions(); From 07012f7bedd322ba2aa4e016e12afb406ea87fa0 Mon Sep 17 00:00:00 2001 From: Sofien Haj Chedhli Date: Tue, 17 Sep 2024 16:54:12 +0100 Subject: [PATCH 03/14] feat: Implement consult news action menu revamping - EXO-73149 - Meeds-io/MIPs#129 (#228) --- .../src/main/webapp/skin/less/newsApp.less | 6 +- .../main/webapp/skin/less/newsDetails.less | 5 + .../components/ExoNewsActivityComposer.vue | 1000 ----------------- .../initComponents.js | 4 +- .../components/ExoNewsDetails.vue | 6 +- .../components/ExoNewsDetailsActionMenu.vue | 131 --- .../components/ExoNewsDetailsToolBar.vue | 9 +- .../vue-app/news-details/initComponents.js | 4 +- .../ExoNewsDetailsActionMenuApp.vue | 69 +- .../vue-app/news/components/NewsAppItem.vue | 6 +- 10 files changed, 81 insertions(+), 1159 deletions(-) delete mode 100644 content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ExoNewsActivityComposer.vue delete mode 100644 content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsActionMenu.vue diff --git a/content-webapp/src/main/webapp/skin/less/newsApp.less b/content-webapp/src/main/webapp/skin/less/newsApp.less index 812a65273..dd4259e16 100644 --- a/content-webapp/src/main/webapp/skin/less/newsApp.less +++ b/content-webapp/src/main/webapp/skin/less/newsApp.less @@ -1567,7 +1567,11 @@ input.ignore-vuetify-classes.datePickerText.flex-grow-0 { opacity: 0.2; z-index: 0!important; } - +.deleteArticleOption { + .deleteArticleIcon, .deleteArticleText { + color: @errorColor !important; + } +} @media (max-width: 488px) { .article { width: 100% !important; diff --git a/content-webapp/src/main/webapp/skin/less/newsDetails.less b/content-webapp/src/main/webapp/skin/less/newsDetails.less index 4c1f4f3ab..ab7084537 100644 --- a/content-webapp/src/main/webapp/skin/less/newsDetails.less +++ b/content-webapp/src/main/webapp/skin/less/newsDetails.less @@ -418,3 +418,8 @@ .news-details-toolbar { position: inherit!important; } +.deleteArticleOption { + .deleteArticleIcon, .deleteArticleText { + color: @errorColor !important; + } +} diff --git a/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ExoNewsActivityComposer.vue b/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ExoNewsActivityComposer.vue deleted file mode 100644 index 7eb6e5922..000000000 --- a/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ExoNewsActivityComposer.vue +++ /dev/null @@ -1,1000 +0,0 @@ - - - - diff --git a/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/initComponents.js b/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/initComponents.js index 4d5ee61bd..dbf744bc0 100644 --- a/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/initComponents.js +++ b/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/initComponents.js @@ -17,8 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -import ExoNewsActivityComposer from './components/ExoNewsActivityComposer.vue'; + import ExoNewsFileDrop from './components/ExoNewsFileDrop.vue'; import ContentRichEditor from './components/ContentRichEditor.vue'; @@ -26,7 +25,6 @@ import * as newsServices from '../services/newsServices.js'; import * as newsConstants from '../services/newsConstants.js'; const components = { - 'exo-news-activity-composer': ExoNewsActivityComposer, 'exo-news-file-drop': ExoNewsFileDrop, 'content-rich-editor': ContentRichEditor }; diff --git a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue index 8269f9107..6add4a419 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue @@ -39,7 +39,9 @@ :activity-id="activityId" :show-edit-button="showEditButton" :show-delete-button="showDeleteButton" - :show-publish-button="showPublishButton" /> + :show-publish-button="showPublishButton" + @delete-article="deleteConfirmDialog" + @edit-article="editLink" /> - - - - diff --git a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue index 1a90fc732..00abad080 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue @@ -27,14 +27,16 @@ @click="$root.$emit('open-schedule-drawer','editScheduledNews')"> {{ $t("news.composer.btn.scheduleArticle") }} - + :show-publish-button="showPublishButton" + @delete-article="$emit('delete-article')" + @edit-article="$emit('edit-article')" /> + class="px-0 mx-2 pa-0 py-0 overflow-hidden"> - - - + + + + fas fa-edit + + {{ $t('news.details.header.menu.edit') }} - + - - + + + fa fa-share + + {{ $t('news.details.header.menu.share') }} - + - - + + + fas fa-edit + + {{ $t('news.details.header.menu.resume') }} - + - - + + + fa-solid fa-paper-plane + + + {{ $t('news.details.header.menu.publish') }} + + + + + fas fa-trash + + {{ $t('news.details.header.menu.delete') }} - + @@ -92,6 +118,16 @@ export default { required: false, default: false }, + showPublishButton: { + type: Boolean, + required: false, + default: false + }, + currentApp: { + type: String, + required: false, + default: null + }, }, data: () => ({ actionMenu: null, @@ -105,5 +141,10 @@ export default { } }); }, + mounted() { + $('#UIPortalApplication').parent().click(() => { + this.actionMenu = false; + }); + }, }; \ No newline at end of file diff --git a/content-webapp/src/main/webapp/vue-app/news/components/NewsAppItem.vue b/content-webapp/src/main/webapp/vue-app/news/components/NewsAppItem.vue index cb57cf28b..b246882bd 100644 --- a/content-webapp/src/main/webapp/vue-app/news/components/NewsAppItem.vue +++ b/content-webapp/src/main/webapp/vue-app/news/components/NewsAppItem.vue @@ -42,8 +42,9 @@ :show-delete-button="news.canDelete" :show-share-button="showShareButton && !isDraftsFilter" :show-resume-button="news.draft && isDraftsFilter" - @delete="deleteConfirmDialog" - @edit="editLink(news)" /> + :current-app="currentApplication" + @delete-article="deleteConfirmDialog" + @edit-article="editLink(news)" /> ({ showShareButton: true, + currentApplication: 'newsApp', dateTimeFormat: { hour: '2-digit', minute: '2-digit', From d428ab9ab002ca696c82061e2d890d2ece48e236 Mon Sep 17 00:00:00 2001 From: Sofien Haj Chedhli Date: Thu, 19 Sep 2024 19:27:02 +0100 Subject: [PATCH 04/14] feat: Implement article copy link action - EXO-73602 - Meeds-io/MIPs#129 (#230) --- .../locale/portlet/news/News_en.properties | 2 + .../locale/portlet/news/News_fr.properties | 2 + .../main/webapp/WEB-INF/gatein-resources.xml | 1 + .../src/main/webapp/skin/less/newsApp.less | 5 - .../main/webapp/skin/less/newsDetails.less | 12 ++ .../components/ExoNewsDetailsApp.vue | 1 + .../components/ExoNewsDetails.vue | 11 +- .../components/ExoNewsDetailsActivity.vue | 1 + .../components/ExoNewsDetailsToolBar.vue | 8 +- .../mobile/ExoNewsDetailsToolBarMobile.vue | 16 ++- .../vue-app/news-details/initComponents.js | 4 + .../main/webapp/vue-app/news-details/main.js | 1 + .../ExoNewsDetailsActionMenuApp.vue | 116 ++++++++------- .../news/components/NewsActionMenuItems.vue | 134 ++++++++++++++++++ .../news/components/NewsMobileActionMenu.vue | 100 +++++++++++++ .../webapp/vue-app/news/initComponents.js | 4 + 16 files changed, 355 insertions(+), 63 deletions(-) create mode 100644 content-webapp/src/main/webapp/vue-app/news/components/NewsActionMenuItems.vue create mode 100644 content-webapp/src/main/webapp/vue-app/news/components/NewsMobileActionMenu.vue diff --git a/content-service/src/main/resources/locale/portlet/news/News_en.properties b/content-service/src/main/resources/locale/portlet/news/News_en.properties index 1fad6f513..f2b7f5f4a 100644 --- a/content-service/src/main/resources/locale/portlet/news/News_en.properties +++ b/content-service/src/main/resources/locale/portlet/news/News_en.properties @@ -158,6 +158,8 @@ news.avatar.author.alt={0}'s avatar news.details.header.menu.share=Share news.details.header.menu.edit=Edit +news.details.header.menu.copy.link=Copy link +news.alert.success.label.linkCopied=Link successfully copied in the clipboard news.details.header.menu.resume=Resume news.details.header.menu.delete=Delete news.details.header.menu.publish=Publish diff --git a/content-service/src/main/resources/locale/portlet/news/News_fr.properties b/content-service/src/main/resources/locale/portlet/news/News_fr.properties index f0f861ee8..3857f1e5b 100644 --- a/content-service/src/main/resources/locale/portlet/news/News_fr.properties +++ b/content-service/src/main/resources/locale/portlet/news/News_fr.properties @@ -158,6 +158,8 @@ news.avatar.author.alt=Avatar de {0} news.details.header.menu.share=Partager news.details.header.menu.edit=Modifier +news.details.header.menu.copy.link=Copier le lien +news.alert.success.label.linkCopied=Lien copié dans le presse-papier news.details.header.menu.resume=Continuer news.details.header.menu.delete=Supprimer news.details.header.menu.publish=Diffuser diff --git a/content-webapp/src/main/webapp/WEB-INF/gatein-resources.xml b/content-webapp/src/main/webapp/WEB-INF/gatein-resources.xml index a6103bc05..64bba5cb3 100644 --- a/content-webapp/src/main/webapp/WEB-INF/gatein-resources.xml +++ b/content-webapp/src/main/webapp/WEB-INF/gatein-resources.xml @@ -42,6 +42,7 @@ News Enterprise /skin/css/newsApp.css + newsDetails diff --git a/content-webapp/src/main/webapp/skin/less/newsApp.less b/content-webapp/src/main/webapp/skin/less/newsApp.less index dd4259e16..5102e2e27 100644 --- a/content-webapp/src/main/webapp/skin/less/newsApp.less +++ b/content-webapp/src/main/webapp/skin/less/newsApp.less @@ -1567,11 +1567,6 @@ input.ignore-vuetify-classes.datePickerText.flex-grow-0 { opacity: 0.2; z-index: 0!important; } -.deleteArticleOption { - .deleteArticleIcon, .deleteArticleText { - color: @errorColor !important; - } -} @media (max-width: 488px) { .article { width: 100% !important; diff --git a/content-webapp/src/main/webapp/skin/less/newsDetails.less b/content-webapp/src/main/webapp/skin/less/newsDetails.less index ab7084537..f1d10b07f 100644 --- a/content-webapp/src/main/webapp/skin/less/newsDetails.less +++ b/content-webapp/src/main/webapp/skin/less/newsDetails.less @@ -423,3 +423,15 @@ color: @errorColor !important; } } +.newsMenu .v-menu__content { + overflow: visible !important; +} +.newsActionMenuItems .action-menu-item { + min-height: 36px !important; + height: 36px !important; + .icon-menu { + width: 28px; + height: 36px !important; + color: @greyColorLighten1Default; + } +} diff --git a/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue b/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue index 4d9f14196..d389be6d7 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue @@ -17,6 +17,7 @@ :show-delete-button="showDeleteButton" :translations="translations" :selected-translation="selectedTranslation" + :show-copy-link-button="true" /> diff --git a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue index 6add4a419..5c6bb92b5 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue @@ -25,7 +25,10 @@ :news="news" :show-edit-button="showEditButton" :show-delete-button="showDeleteButton" - :show-publish-button="showPublishButton" /> + :show-copy-link-button="showCopyLinkButton" + :show-publish-button="showPublishButton" + @delete-article="deleteConfirmDialog" + @edit-article="editLink" /> { diff --git a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsActivity.vue b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsActivity.vue index 238fb4c0a..d05e91d28 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsActivity.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsActivity.vue @@ -28,6 +28,7 @@ :show-edit-button="showEditButton" :show-publish-button="showPublishButton" :show-delete-button="showDeleteButton" + :show-copy-link-button="true" :translations="translations" :selected-translation="selectedTranslation" /> diff --git a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue index 00abad080..dbecafec6 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue @@ -28,13 +28,14 @@ {{ $t("news.composer.btn.scheduleArticle") }} - + :show-copy-link-button="showCopyLinkButton" + :news-published="newsPublished" + @edit-article="$emit('edit-article', news)" + @delete-article="$emit('delete-article')" /> + class="px-0 mx-2 pa-0 py-0 overflow-hidden newsMenu"> - - - - - fas fa-edit - - - {{ $t('news.details.header.menu.edit') }} - - - - - fa fa-share - - - {{ $t('news.details.header.menu.share') }} - - - - - fas fa-edit - - - {{ $t('news.details.header.menu.resume') }} - - - - - fa-solid fa-paper-plane - - - {{ $t('news.details.header.menu.publish') }} - - - - - fas fa-trash - - - {{ $t('news.details.header.menu.delete') }} - - - + + @@ -128,6 +106,11 @@ export default { required: false, default: null }, + showCopyLinkButton: { + type: Boolean, + required: false, + default: false + } }, data: () => ({ actionMenu: null, @@ -146,5 +129,34 @@ export default { this.actionMenu = false; }); }, + computed: { + isMobile() { + return this.$vuetify.breakpoint.name === 'xs' || this.$vuetify.breakpoint.name === 'sm'; + } + }, + methods: { + copyLink() { + let newsLink = window.location.href.split(eXo.env.portal.metaPortalName)[0]; + if (this.news?.published && this.news.audience === 'all') { + newsLink = newsLink.concat(eXo.env.portal.metaPortalName).concat(`/news-detail?newsId=${this.news.id}&type=article`); + } else { + newsLink = newsLink.concat(eXo.env.portal.metaPortalName).concat(`/activity?id=${this.news.activityId}`); + } + if (this.news?.lang) { + newsLink = newsLink.concat(`&lang=${this.news.lang}`); + } + navigator.clipboard.writeText(newsLink); + document.dispatchEvent(new CustomEvent('alert-message', {detail: { + alertType: 'success', + alertMessage: this.$t('news.alert.success.label.linkCopied') , + }})); + if (this.isMobile) { + this.$root.$emit('close-news-mobile-action-menu'); + } + }, + openBottomMenu() { + return this.isMobile && this.$root.$emit('open-news-mobile-action-menu'); + } + } }; \ No newline at end of file diff --git a/content-webapp/src/main/webapp/vue-app/news/components/NewsActionMenuItems.vue b/content-webapp/src/main/webapp/vue-app/news/components/NewsActionMenuItems.vue new file mode 100644 index 000000000..fe4d977fe --- /dev/null +++ b/content-webapp/src/main/webapp/vue-app/news/components/NewsActionMenuItems.vue @@ -0,0 +1,134 @@ + + + + \ No newline at end of file diff --git a/content-webapp/src/main/webapp/vue-app/news/components/NewsMobileActionMenu.vue b/content-webapp/src/main/webapp/vue-app/news/components/NewsMobileActionMenu.vue new file mode 100644 index 000000000..b2e15f31f --- /dev/null +++ b/content-webapp/src/main/webapp/vue-app/news/components/NewsMobileActionMenu.vue @@ -0,0 +1,100 @@ + + + + \ No newline at end of file diff --git a/content-webapp/src/main/webapp/vue-app/news/initComponents.js b/content-webapp/src/main/webapp/vue-app/news/initComponents.js index 2586b66e3..05e56dfec 100644 --- a/content-webapp/src/main/webapp/vue-app/news/initComponents.js +++ b/content-webapp/src/main/webapp/vue-app/news/initComponents.js @@ -27,6 +27,8 @@ import NewsFilterSpaceList from './components/NewsFilterSpaceList.vue'; import NewsFilterSpaceSearch from './components/NewsFilterSpaceSearch.vue'; import ExoNewsDetailsActionMenuApp from './components/ExoNewsDetailsActionMenuApp.vue'; import NewsAppItem from './components/NewsAppItem.vue'; +import NewsActionMenuItems from './components/NewsActionMenuItems.vue'; +import NewsMobileActionMenu from './components/NewsMobileActionMenu.vue'; const components = { 'news-app': NewsApp, @@ -38,6 +40,8 @@ const components = { 'news-filter-space-search': NewsFilterSpaceSearch, 'exo-news-details-action-menu-app': ExoNewsDetailsActionMenuApp, 'news-app-item': NewsAppItem, + 'news-action-menu-items': NewsActionMenuItems, + 'news-mobile-action-menu': NewsMobileActionMenu }; for (const key in components) { From fe9d4f74812c3fefd7593d691b951fd86d396623 Mon Sep 17 00:00:00 2001 From: Sofien Haj Chedhli Date: Thu, 19 Sep 2024 19:30:34 +0100 Subject: [PATCH 05/14] feat: Disable the save/publish button when draft status is saving - EXO-74280 - Meeds-io/MIPs#129 (#235) --- .../components/ContentRichEditor.vue | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue b/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue index 9b3c6f25f..afb13bebc 100644 --- a/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue +++ b/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue @@ -163,7 +163,7 @@ export default { }, disableSaveButton() { return this.postingNews || !this.article.title - || this.isEmptyArticleContent + || this.savingDraft || (!this.propertiesModified && this.articleNotChanged && this.article.publicationState !== 'draft'); @@ -172,9 +172,6 @@ export default { return this.originalArticle?.title === this.article.title && this.isSameArticleContent() && !this.propertiesModified; }, - isEmptyArticleContent() { - return this.article.content === '' || Array.from(new DOMParser().parseFromString(this.article.content, 'text/html').body.childNodes).every(node => (node.nodeName === 'P' && !node.textContent.trim() && node.children.length === 0) || (node.nodeType === Node.TEXT_NODE && !node.textContent.trim())); - }, propertiesModified() { return JSON.stringify(this.article?.properties) !== JSON.stringify(this.originalArticle?.properties); }, @@ -303,6 +300,7 @@ export default { }).then(() => this.$emit('draftUpdated')) .then(() => this.draftSavingStatus = this.$t('news.composer.draft.savedDraftStatus')) .finally(() => { + this.savingDraft = false; this.enableClickOnce(); if (this.articleType === 'latest_draft' && this.selectedLanguage) { this.updateUrl(); @@ -378,14 +376,19 @@ export default { this.article.draftPage = true; }) .then(() => this.$emit('draftUpdated')) - .then(() => this.draftSavingStatus = this.$t('news.composer.draft.savedDraftStatus')); + .then(() => { + this.draftSavingStatus = this.$t('news.composer.draft.savedDraftStatus'); + this.savingDraft = false; + }); } else { this.$newsServices.deleteDraft(this.article.id) .then(() => this.$emit('draftDeleted')) - .then(() => this.draftSavingStatus = this.$t('news.composer.draft.savedDraftStatus')); + .then(() => { + this.draftSavingStatus = this.$t('news.composer.draft.savedDraftStatus'); + this.savingDraft = false; + }); this.article.id = null; } - this.savingDraft = false; } else if (this.article.title || this.article.body) { article.publicationState = 'draft'; this.$newsServices.saveNews(article).then((createdArticle) => { @@ -396,8 +399,8 @@ export default { if (!this.articleId) { this.articleId = createdArticle.id; } - this.savingDraft = false; this.$emit('draftCreated'); + this.savingDraft = false; }); } else { this.draftSavingStatus = ''; From 501ad675b346677a382110baf85ae1b24981b13f Mon Sep 17 00:00:00 2001 From: Ayoub Zayati Date: Wed, 25 Sep 2024 17:35:14 +0200 Subject: [PATCH 06/14] feat: Fix content body inserted images permissions - EXO-74263_EXO-74265_EXO-74266 - Meeds-io/MIPs#129 (#240) --- .../java/io/meeds/news/rest/NewsRest.java | 2 - .../io/meeds/news/service/NewsService.java | 3 +- .../news/service/impl/NewsServiceImpl.java | 287 +++++++++++------- .../java/io/meeds/news/utils/NewsUtils.java | 115 ++++--- .../service/impl/NewsServiceImplTest.java | 1 + 5 files changed, 240 insertions(+), 168 deletions(-) diff --git a/content-service/src/main/java/io/meeds/news/rest/NewsRest.java b/content-service/src/main/java/io/meeds/news/rest/NewsRest.java index ec20db0c3..853467015 100644 --- a/content-service/src/main/java/io/meeds/news/rest/NewsRest.java +++ b/content-service/src/main/java/io/meeds/news/rest/NewsRest.java @@ -119,8 +119,6 @@ public class NewsRest { private ScheduledExecutorService scheduledExecutor; - private static final int CACHE_DURATION_SECONDS = 31536000; - private enum FilterType { PINNED, MYPOSTED, DRAFTS, SCHEDULED, ALL } diff --git a/content-service/src/main/java/io/meeds/news/service/NewsService.java b/content-service/src/main/java/io/meeds/news/service/NewsService.java index 9262f8985..fabbcd17e 100644 --- a/content-service/src/main/java/io/meeds/news/service/NewsService.java +++ b/content-service/src/main/java/io/meeds/news/service/NewsService.java @@ -337,10 +337,11 @@ News createDraftArticleForNewPage(News draftArticle, * @param updater * @param page * @param creationDate + * @param space * @return the created news draft for an existing news article * @throws Exception when error occurs */ - News createDraftForExistingPage(News news, String updater, Page page, long creationDate) throws Exception; + News createDraftForExistingPage(News news, String updater, Page page, long creationDate, Space space) throws Exception; /** * @param news {@link News} news article to be deleted diff --git a/content-service/src/main/java/io/meeds/news/service/impl/NewsServiceImpl.java b/content-service/src/main/java/io/meeds/news/service/impl/NewsServiceImpl.java index 22e0d4b60..5cff10032 100644 --- a/content-service/src/main/java/io/meeds/news/service/impl/NewsServiceImpl.java +++ b/content-service/src/main/java/io/meeds/news/service/impl/NewsServiceImpl.java @@ -28,14 +28,25 @@ import java.time.OffsetTime; import java.time.ZoneId; import java.time.ZoneOffset; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TimeZone; import java.util.regex.Matcher; import java.util.stream.Stream; -import io.meeds.notes.model.NotePageProperties; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; import org.exoplatform.commons.api.notification.NotificationContext; import org.exoplatform.commons.api.notification.model.PluginKey; @@ -92,110 +103,109 @@ import io.meeds.news.service.NewsTargetingService; import io.meeds.news.utils.NewsUtils; import io.meeds.news.utils.NewsUtils.NewsObjectType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Service; +import io.meeds.notes.model.NotePageProperties; @Primary @Service public class NewsServiceImpl implements NewsService { - public static final String NEWS_ARTICLES_ROOT_NOTE_PAGE_NAME = "Articles"; + public static final String NEWS_ARTICLES_ROOT_NOTE_PAGE_NAME = "Articles"; - private static final String HTML_AT_SYMBOL_PATTERN = "@"; + private static final String HTML_AT_SYMBOL_PATTERN = "@"; - private static final String HTML_AT_SYMBOL_ESCAPED_PATTERN = "@"; + private static final String HTML_AT_SYMBOL_ESCAPED_PATTERN = "@"; - public static final MetadataType NEWS_METADATA_TYPE = new MetadataType(1000, "news"); + public static final MetadataType NEWS_METADATA_TYPE = new MetadataType(1000, "news"); - public static final String NEWS_METADATA_NAME = "news"; + public static final String NEWS_METADATA_NAME = "news"; - public static final String NEWS_METADATA_DRAFT_OBJECT_TYPE = "newsDraftPage"; + public static final String NEWS_METADATA_DRAFT_OBJECT_TYPE = "newsDraftPage"; /** The Constant PUBLISHED. */ - public final static String PUBLISHED = "published"; + public final static String PUBLISHED = "published"; /** The Constant POSTED. */ - public final static String POSTED = "posted"; + public final static String POSTED = "posted"; /** The Constant DRAFT. */ - public final static String DRAFT = "draft"; + public final static String DRAFT = "draft"; /** The Constant STAGED. */ - public final static String STAGED = "staged"; + public final static String STAGED = "staged"; /** The Constant AUDIENCE. */ - public static final String NEWS_AUDIENCE = "audience"; + public static final String NEWS_AUDIENCE = "audience"; /** The Constant DELETED. */ - public static final String NEWS_DELETED = "deleted"; + public static final String NEWS_DELETED = "deleted"; /** The Constant NEWS_ID. */ - public static final String NEWS_ID = "newsId"; + public static final String NEWS_ID = "newsId"; /** The Constant SCHEDULE_POST_DATE. */ - public static final String SCHEDULE_POST_DATE = "schedulePostDate"; + public static final String SCHEDULE_POST_DATE = "schedulePostDate"; /** The Constant NEWS_ACTIVITIES. */ - public static final String NEWS_ACTIVITIES = "activities"; + public static final String NEWS_ACTIVITIES = "activities"; /** The Constant NEWS_PUBLICATION_STATE. */ - public static final String NEWS_PUBLICATION_STATE = "publicationState"; + public static final String NEWS_PUBLICATION_STATE = "publicationState"; /** The Constant NEWS_ACTIVITY_POSTED. */ - public static final String NEWS_ACTIVITY_POSTED = "activityPosted"; + public static final String NEWS_ACTIVITY_POSTED = "activityPosted"; /** The Constant NEWS_METADATA_PAGE_OBJECT_TYPE. */ - public static final String NEWS_METADATA_PAGE_OBJECT_TYPE = "newsPage"; + public static final String NEWS_METADATA_PAGE_OBJECT_TYPE = "newsPage"; /** The Constant NEWS_METADATA_PAGE_VERSION_OBJECT_TYPE. */ - public static final String NEWS_METADATA_PAGE_VERSION_OBJECT_TYPE = "newsPageVersion"; + public static final String NEWS_METADATA_PAGE_VERSION_OBJECT_TYPE = "newsPageVersion"; /** The Constant NEWS_VIEWERS. */ - public static final String NEWS_VIEWERS = "viewers"; + public static final String NEWS_VIEWERS = "viewers"; /** The Constant NEWS_VIEWS. */ - public static final String NEWS_VIEWS = "viewsCount"; + public static final String NEWS_VIEWS = "viewsCount"; /** The Constant NEWS_METADATA_LATEST_DRAFT_OBJECT_TYPE. */ - public static final String NEWS_METADATA_LATEST_DRAFT_OBJECT_TYPE = "newsLatestDraftPage"; + public static final String NEWS_METADATA_LATEST_DRAFT_OBJECT_TYPE = "newsLatestDraftPage"; - public static final String NEWS_ATTACHMENTS_IDS = "attachmentsIds"; + public static final String NEWS_ATTACHMENTS_IDS = "attachmentsIds"; - public static final String ARTICLE_CONTENT = "content"; + public static final String ARTICLE_CONTENT = "content"; - public static final MetadataKey NEWS_METADATA_KEY = - new MetadataKey(NEWS_METADATA_TYPE.getName(), NEWS_METADATA_NAME, 0); + public static final String SPACES = "spaces"; - private static final Log LOG = ExoLogger.getLogger(NewsServiceImpl.class); + public static final MetadataKey NEWS_METADATA_KEY = + new MetadataKey(NEWS_METADATA_TYPE.getName(), NEWS_METADATA_NAME, 0); - @Autowired - private SpaceService spaceService; + private static final Log LOG = ExoLogger.getLogger(NewsServiceImpl.class); @Autowired - private NoteService noteService; + private SpaceService spaceService; @Autowired - private MetadataService metadataService; + private NoteService noteService; + @Autowired + private MetadataService metadataService; @Autowired - private NewsTargetingService newsTargetingService; + private NewsTargetingService newsTargetingService; @Autowired - private IndexingService indexingService; + private IndexingService indexingService; @Autowired - private IdentityManager identityManager; + private IdentityManager identityManager; @Autowired - private ActivityManager activityManager; + private ActivityManager activityManager; @Autowired - private WikiService wikiService; + private WikiService wikiService; @Autowired - private NewsSearchConnector newsSearchConnector; + private NewsSearchConnector newsSearchConnector; /** * {@inheritDoc} @@ -270,15 +280,15 @@ public News updateNews(News news, News originalNews = getNewsById(newsId, updaterIdentity, false, newsObjectType); List oldTargets = newsTargetingService.getTargetsByNews(news); boolean canPublish = NewsUtils.canPublishNews(news.getSpaceId(), updaterIdentity); - Set previousMentions = NewsUtils.processMentions(originalNews.getOriginalBody(), - spaceService.getSpaceById(news.getSpaceId())); + Space space = spaceService.getSpaceById(news.getSpaceId()); + Set previousMentions = NewsUtils.processMentions(originalNews.getOriginalBody(), space); if (NewsObjectType.DRAFT.name().toLowerCase().equals(newsObjectType)) { - return updateDraftArticleForNewPage(news, updater); + return updateDraftArticleForNewPage(news, updater, space); } else if (LATEST_DRAFT.name().toLowerCase().equals(newsObjectType)) { - return createOrUpdateDraftForExistingPage(news, updater); - } - else if (ARTICLE.name().equalsIgnoreCase(newsObjectType) && CONTENT_AND_TITLE.name().equalsIgnoreCase(newsUpdateType) && StringUtils.isNotEmpty(news.getLang())) { - return addNewArticleVersionWithLang(news, updaterIdentity); + return createOrUpdateDraftArticleForExistingPage(news, updater, space); + } else if (ARTICLE.name().equalsIgnoreCase(newsObjectType) && CONTENT_AND_TITLE.name().equalsIgnoreCase(newsUpdateType) + && StringUtils.isNotEmpty(news.getLang())) { + return addNewArticleVersionWithLang(news, updaterIdentity, space); } if (publish != news.isPublished() && news.isCanPublish()) { news.setPublished(publish); @@ -304,7 +314,7 @@ else if (ARTICLE.name().equalsIgnoreCase(newsObjectType) && CONTENT_AND_TITLE.na // They need the original news to treat the news audience and exclude space // members from notification. if (ARTICLE.name().toLowerCase().equals(newsObjectType)) { - news = updateNewsArticle(news, updaterIdentity, newsUpdateType); + news = updateArticle(news, updaterIdentity, newsUpdateType); } if (POSTED.equals(news.getPublicationState())) { @@ -350,8 +360,7 @@ public void deleteNews(String newsId, Identity currentIdentity, String newsObjec if (draft != null) { // check if the latest draft has the same illustration // with the news article to do not remove it. - deleteDraftArticle(draft.getId(), - currentIdentity.getUserId()); + deleteDraftArticle(draft.getId(), currentIdentity.getUserId()); } } } else { @@ -365,7 +374,8 @@ public void deleteNews(String newsId, Identity currentIdentity, String newsObjec indexingService.unindex(NewsIndexingServiceConnector.TYPE, String.valueOf(news.getId())); List articleLanguages = getArticleLanguages(newsId, false); if (CollectionUtils.isNotEmpty(articleLanguages)) { - articleLanguages.forEach(lang -> indexingService.unindex(NewsIndexingServiceConnector.TYPE, news.getId().concat("-").concat(lang))); + articleLanguages.forEach(lang -> indexingService.unindex(NewsIndexingServiceConnector.TYPE, + news.getId().concat("-").concat(lang))); } NewsUtils.broadcastEvent(NewsUtils.DELETE_NEWS, currentIdentity.getUserId(), news); } @@ -407,10 +417,12 @@ public void publishNews(News newsToPublish, String publisher) throws Exception { } news.setAudience(newsToPublish.getAudience()); NewsUtils.broadcastEvent(NewsUtils.PUBLISH_NEWS, news.getId(), news); + Space space = spaceService.getSpaceById(news.getSpaceId()); - Map shareArticleEventListenerData = new HashMap<>(); - shareArticleEventListenerData.putAll(Map.of(NEWS_ATTACHMENTS_IDS, getArticleAttachmentIdsToShare(Long.parseLong(news.getSpaceId()), Long.parseLong(newsPageObject.getId())),"space", space, NEWS_AUDIENCE, news.getAudience(),ARTICLE_CONTENT, news.getBody())); - NewsUtils.broadcastEvent(NewsUtils.SHARE_CONTENT_ATTACHMENTS,this, shareArticleEventListenerData); + // Update content permissions + updateArticlePermissions(List.of(space), + news, + getArticleAttachmentIds(Long.parseLong(news.getSpaceId()), Long.parseLong(newsPageObject.getId()))); try { sendNotification(publisher, news, NotificationConstants.NOTIFICATION_CONTEXT.PUBLISH_NEWS); } catch (Error | Exception e) { @@ -424,6 +436,7 @@ public void publishNews(News newsToPublish, String publisher) throws Exception { @Override public void unpublishNews(String newsId, String publisher) throws Exception { News news = getNewsArticleById(newsId); + Space space = spaceService.getSpaceById(news.getSpaceId()); newsTargetingService.deleteNewsTargets(news, publisher); NewsPageObject newsPageObject = new NewsPageObject(NEWS_METADATA_PAGE_OBJECT_TYPE, @@ -446,6 +459,11 @@ public void unpublishNews(String newsId, String publisher) throws Exception { newsMetadataItem.setUpdatedDate(updatedDate.getTime()); String publisherId = identityManager.getOrCreateUserIdentity(publisher).getId(); metadataService.updateMetadataItem(newsMetadataItem, Long.parseLong(publisherId), false); + // Update content permissions + updateArticlePermissions(List.of(space), + news, + getArticleAttachmentIds(Long.parseLong(news.getSpaceId()), + Long.parseLong(newsPageObject.getId()))); } } @@ -465,10 +483,10 @@ public News getNewsById(String newsId, */ @Override public News getNewsByIdAndLang(String newsId, - Identity currentIdentity, - boolean editMode, - String newsObjectType, - String lang) throws IllegalAccessException { + Identity currentIdentity, + boolean editMode, + String newsObjectType, + String lang) throws IllegalAccessException { News news = null; try { if (newsObjectType == null) { @@ -678,7 +696,7 @@ public News getNewsByActivityId(String activityId, Identity currentIdentity) thr @Override public News getNewsByActivityIdAndLang(String activityId, Identity currentIdentity, String lang) throws IllegalAccessException, - ObjectNotFoundException { + ObjectNotFoundException { ExoSocialActivity activity = activityManager.getActivity(activityId); if (activity == null) { throw new ObjectNotFoundException("Activity with id " + activityId + " wasn't found"); @@ -702,12 +720,15 @@ public News getNewsByActivityIdAndLang(String activityId, Identity currentIdenti throw new IllegalAccessException("Shared Activity '" + activityId + "' Poster " + activity.getPosterId() + " isn't found"); } - return getNewsByActivityIdAndLang(originalActivityId, NewsUtils.getUserIdentity(sharedActivityPosterIdentity.getRemoteId()), lang); + return getNewsByActivityIdAndLang(originalActivityId, + NewsUtils.getUserIdentity(sharedActivityPosterIdentity.getRemoteId()), + lang); } throw new ObjectNotFoundException("Activity with id " + activityId + " isn't of type news nor a shared news"); } return getNewsByIdAndLang(newsId, currentIdentity, false, ARTICLE.name().toLowerCase(), lang); } + /** * {@inheritDoc} */ @@ -724,7 +745,7 @@ public News scheduleNews(News news, Identity currentIdentity, String newsObjectT // scheduling. news = createNewsArticlePage(news, currentIdentity.getUserId()); } else if (newsObjectType.equalsIgnoreCase(ARTICLE.name())) { - updateNewsArticle(news, currentIdentity, NewsUtils.NewsUpdateType.SCHEDULE.name().toLowerCase()); + updateArticle(news, currentIdentity, NewsUtils.NewsUpdateType.SCHEDULE.name().toLowerCase()); } if (news != null) { if (NewsUtils.canPublishNews(space.getId(), currentIdentity)) { @@ -847,13 +868,11 @@ public void shareNews(News news, metadataItem.setProperties(properties); metadataService.updateMetadataItem(metadataItem, Long.parseLong(userIdentity.getId()), false); - - Map shareArticleEventListenerData = new HashMap<>(); - shareArticleEventListenerData.put(NEWS_ATTACHMENTS_IDS, getArticleAttachmentIdsToShare( - Long.parseLong(space.getId()), - Long.parseLong(newsPageObject.getId()))); - shareArticleEventListenerData.putAll(Map.of("space", space, NEWS_AUDIENCE, news.getAudience(),ARTICLE_CONTENT, news.getBody())); - NewsUtils.broadcastEvent(NewsUtils.SHARE_CONTENT_ATTACHMENTS,this, shareArticleEventListenerData); + // Update content permissions + updateArticlePermissions(List.of(space), + news, + getArticleAttachmentIds(Long.parseLong(news.getSpaceId()), + Long.parseLong(newsPageObject.getId()))); NewsUtils.broadcastEvent(NewsUtils.SHARE_NEWS, userIdentity.getRemoteId(), news); } @@ -908,7 +927,6 @@ public News createDraftArticleForNewPage(News draftArticle, Long.parseLong(identityManager.getOrCreateUserIdentity(draftArticleCreator) .getId())); - draftArticle.setProperties(draftArticlePage.getProperties()); draftArticle.setIllustrationURL(NewsUtils.buildIllustrationUrl(draftArticle.getProperties(), draftArticlePage.getLang())); draftArticle.setId(draftArticlePage.getId()); @@ -927,7 +945,8 @@ public News createDraftArticleForNewPage(News draftArticle, draftArticleMetadataItemProperties, Long.parseLong(draftArticleMetadataItemCreatorIdentityId), false); - + // Update content permissions + updateArticlePermissions(List.of(draftArticleSpace), draftArticle, null); return draftArticle; } return null; @@ -1024,6 +1043,9 @@ public News createNewsArticlePage(News newsArticle, String newsArticleCreator) t false); // delete the draft deleteDraftArticle(draftNewsId, poster.getUserId()); + + // Update content permissions + updateArticlePermissions(List.of(space), newsArticle, null); return newsArticle; } } @@ -1037,7 +1059,8 @@ public News createNewsArticlePage(News newsArticle, String newsArticleCreator) t public News createDraftForExistingPage(News draftArticle, String updater, Page targetArticlePage, - long creationDate) throws Exception { + long creationDate, + Space space) throws Exception { DraftPage draftArticlePage = new DraftPage(); draftArticlePage.setNewPage(false); draftArticlePage.setTargetPageId(targetArticlePage.getId()); @@ -1069,6 +1092,9 @@ public News createDraftForExistingPage(News draftArticle, draftArticleMetadataItemProperties, Long.parseLong(draftArticleMetadataItemCreatorIdentityId), false); + + // Update content permissions + updateArticlePermissions(List.of(space), draftArticle, null); return draftArticle; } @@ -1149,19 +1175,24 @@ public List getArticleLanguages(String articleId, boolean withDrafts) th return noteService.getPageAvailableTranslationLanguages(Long.parseLong(articleId), withDrafts); } - private News updateDraftArticleForNewPage(News draftArticle, String draftArticleUpdater) throws WikiException, - IllegalAccessException { + private News updateDraftArticleForNewPage(News draftArticle, String draftArticleUpdater, Space space) throws WikiException, + IllegalAccessException { DraftPage draftArticlePage = noteService.getDraftNoteById(draftArticle.getId(), draftArticleUpdater); if (draftArticlePage != null) { draftArticlePage.setTitle(draftArticle.getTitle()); draftArticlePage.setContent(draftArticle.getBody()); draftArticlePage.setProperties(draftArticle.getProperties()); // created and updated date set by default during the draft creation - DraftPage draftPage = noteService.updateDraftForNewPage(draftArticlePage, - System.currentTimeMillis(), - Long.parseLong(identityManager.getOrCreateUserIdentity(draftArticleUpdater).getId())); + DraftPage draftPage = + noteService.updateDraftForNewPage(draftArticlePage, + System.currentTimeMillis(), + Long.parseLong(identityManager.getOrCreateUserIdentity(draftArticleUpdater) + .getId())); draftArticle.setProperties(draftPage.getProperties()); draftArticle.setIllustrationURL(NewsUtils.buildIllustrationUrl(draftPage.getProperties(), draftArticle.getLang())); + + // Update content permissions + updateArticlePermissions(List.of(space), draftArticle, null); return draftArticle; } return null; @@ -1247,9 +1278,7 @@ private void buildArticleVersionProperties(News article, List news } } - private void buildArticleProperties(News article, - String currentUsername, - MetadataItem metadataItem) throws Exception { + private void buildArticleProperties(News article, String currentUsername, MetadataItem metadataItem) throws Exception { if (metadataItem != null && !MapUtils.isEmpty(metadataItem.getProperties())) { Map properties = metadataItem.getProperties(); if (properties.containsKey(NEWS_ACTIVITIES) && properties.get(NEWS_ACTIVITIES) != null) { @@ -1743,7 +1772,7 @@ private void postNewsActivity(News news) throws Exception { updateNewsActivities(activity.getId(), news); } - private News updateNewsArticle(News news, Identity updater, String newsUpdateType) throws Exception { + private News updateArticle(News news, Identity updater, String newsUpdateType) throws Exception { String newsId = news.getTargetPageId() != null ? news.getTargetPageId() : news.getId(); Page existingPage = noteService.getNoteById(newsId); if (existingPage != null) { @@ -1804,6 +1833,19 @@ private News updateNewsArticle(News news, Identity updater, String newsUpdateTyp null); deleteDraftArticle(draftPage.getId(), updater.getUserId()); } + Map metadataItemProperties = existingPageMetadataItem.getProperties(); + if (metadataItemProperties.containsKey(NEWS_ACTIVITIES) && metadataItemProperties.get(NEWS_ACTIVITIES) != null) { + String[] articleActivities = metadataItemProperties.get(NEWS_ACTIVITIES).split(";"); + List articleSpaces = new ArrayList<>(); + for (int i = 0; i < articleActivities.length; i++) { + String sharedInSpaceId = articleActivities[i].split(":")[0]; + Space space = spaceService.getSpaceById(sharedInSpaceId); + if (space != null) { + articleSpaces.add(space); + } + } + updateArticlePermissions(articleSpaces, news, null); + } return news; } return null; @@ -1871,7 +1913,6 @@ private News buildArticle(String newsId, String lang, boolean fetchOriginal) thr news.setIllustrationURL(NewsUtils.buildIllustrationUrl(news.getProperties(), pageVersion.getLang())); } - NewsPageVersionObject newsPageVersionObject = new NewsPageVersionObject(NEWS_METADATA_PAGE_VERSION_OBJECT_TYPE, pageVersion.getId(), @@ -1887,22 +1928,23 @@ private News buildArticle(String newsId, String lang, boolean fetchOriginal) thr return null; } - private News createOrUpdateDraftForExistingPage(News news, String updater) throws Exception { + private News createOrUpdateDraftArticleForExistingPage(News news, String updater, Space space) throws Exception { String pageId = news.getTargetPageId() != null ? news.getTargetPageId() : news.getId(); Page existingPage = noteService.getNoteById(pageId); if (existingPage == null) { return null; } - DraftPage draftPage = noteService.getLatestDraftPageByUserAndTargetPageAndLang(Long.parseLong(pageId), updater, news.getLang()); + DraftPage draftPage = + noteService.getLatestDraftPageByUserAndTargetPageAndLang(Long.parseLong(pageId), updater, news.getLang()); if (draftPage == null) { - news = createDraftForExistingPage(news, updater, existingPage, System.currentTimeMillis()); + news = createDraftForExistingPage(news, updater, existingPage, System.currentTimeMillis(), space); } else { - news = updateDraftForExistingPage(news, updater, existingPage, draftPage); + news = updateDraftArticleForExistingPage(news, updater, existingPage, draftPage, space); } return news; } - private News updateDraftForExistingPage(News news, String updater, Page page, DraftPage draftPage) { + private News updateDraftArticleForExistingPage(News news, String updater, Page page, DraftPage draftPage, Space space) { try { draftPage.setTitle(news.getTitle()); draftPage.setContent(news.getBody()); @@ -1924,7 +1966,12 @@ private News updateDraftForExistingPage(News news, String updater, Page page, Dr page.getId(), Long.parseLong(news.getSpaceId())); - MetadataItem latestDraftArticleMetadataItem = metadataService.getMetadataItemsByMetadataAndObject(NEWS_METADATA_KEY, latestDraftObject).stream().findFirst().orElse(null); + MetadataItem latestDraftArticleMetadataItem = metadataService + .getMetadataItemsByMetadataAndObject(NEWS_METADATA_KEY, + latestDraftObject) + .stream() + .findFirst() + .orElse(null); if (latestDraftArticleMetadataItem != null) { Map latestDraftArticleMetadataItemProperties = latestDraftArticleMetadataItem.getProperties(); if (latestDraftArticleMetadataItemProperties == null) { @@ -1934,13 +1981,16 @@ private News updateDraftForExistingPage(News news, String updater, Page page, Dr latestDraftArticleMetadataItem.setProperties(latestDraftArticleMetadataItemProperties); String draftArticleMetadataItemUpdaterIdentityId = identityManager.getOrCreateUserIdentity(updater).getId(); metadataService.updateMetadataItem(latestDraftArticleMetadataItem, - Long.parseLong(draftArticleMetadataItemUpdaterIdentityId), false); + Long.parseLong(draftArticleMetadataItemUpdaterIdentityId), + false); } else { Map latestDraftArticleMetadataItemProperties = new HashMap<>(); setLatestDraftProperties(latestDraftArticleMetadataItemProperties, news); metadataService.createMetadataItem(latestDraftObject, NEWS_METADATA_KEY, latestDraftArticleMetadataItemProperties, false); } + // Update content permissions + updateArticlePermissions(List.of(space), news, null); } catch (Exception exception) { return null; } @@ -2030,7 +2080,7 @@ private News postScheduledArticle(News news) throws ObjectNotFoundException { return null; } - private News addNewArticleVersionWithLang(News news, Identity versionCreator) throws Exception { + private News addNewArticleVersionWithLang(News news, Identity versionCreator, Space space) throws Exception { News existingNews = getNewsArticleById(news.getId()); String newsId = news.getTargetPageId() != null ? news.getTargetPageId() : news.getId(); Page existingPage = noteService.getNoteById(newsId); @@ -2039,11 +2089,22 @@ private News addNewArticleVersionWithLang(News news, Identity versionCreator) th existingPage = noteService.updateNote(existingPage, PageUpdateType.EDIT_PAGE_CONTENT_AND_TITLE, versionCreator); news.setPublicationState(POSTED); // update the metadata item - MetadataItem metadataItem = metadataService.getMetadataItemsByMetadataAndObject(NEWS_METADATA_KEY, new NewsPageObject(NEWS_METADATA_PAGE_OBJECT_TYPE, newsId, null, Long.parseLong(existingNews.getSpaceId()))).stream().findFirst().orElse(null); + MetadataItem metadataItem = + metadataService.getMetadataItemsByMetadataAndObject(NEWS_METADATA_KEY, + new NewsPageObject(NEWS_METADATA_PAGE_OBJECT_TYPE, + newsId, + null, + Long.parseLong(existingNews.getSpaceId()))) + .stream() + .findFirst() + .orElse(null); if (metadataItem != null) { Calendar calendar = Calendar.getInstance(); metadataItem.setUpdatedDate(calendar.getTime().getTime()); - metadataService.updateMetadataItem(metadataItem, Long.parseLong(identityManager.getOrCreateUserIdentity(versionCreator.getUserId()).getId()), false); + metadataService.updateMetadataItem(metadataItem, + Long.parseLong(identityManager.getOrCreateUserIdentity(versionCreator.getUserId()) + .getId()), + false); } existingPage.setTitle(news.getTitle()); existingPage.setContent(news.getBody()); @@ -2061,28 +2122,46 @@ private News addNewArticleVersionWithLang(News news, Identity versionCreator) th NewsUtils.broadcastEvent(NewsUtils.ADD_ARTICLE_TRANSLATION, versionCreator, news); String newsTranslationId = news.getId().concat("-").concat(news.getLang()); indexingService.index(NewsIndexingServiceConnector.TYPE, newsTranslationId); + updateArticlePermissions(List.of(space), news, null); return news; } return null; } - private List getArticleAttachmentIdsToShare(long spaceId, long articlePageId) { + private List getArticleAttachmentIds(long spaceId, long articlePageId) { List attachmentIds = new ArrayList<>(); PageVersion pageVersion = noteService.getPublishedVersionByPageIdAndLang(articlePageId, null); NewsPageVersionObject newsPageVersionObject = new NewsPageVersionObject(NEWS_METADATA_PAGE_VERSION_OBJECT_TYPE, - pageVersion.getId(), - null, - spaceId); + pageVersion.getId(), + null, + spaceId); List newsPageVersionMetadataItems = metadataService.getMetadataItemsByMetadataAndObject(NEWS_METADATA_KEY, - newsPageVersionObject); - Map newsPageVersionMetadataItemProperties = newsPageVersionMetadataItems.get(0).getProperties(); - if (!MapUtils.isEmpty(newsPageVersionMetadataItems.get(0).getProperties())) { - - if (newsPageVersionMetadataItemProperties.containsKey(NEWS_ATTACHMENTS_IDS) - && newsPageVersionMetadataItemProperties.get(NEWS_ATTACHMENTS_IDS) != null) { - attachmentIds.addAll(List.of(newsPageVersionMetadataItemProperties.get(NEWS_ATTACHMENTS_IDS).split(";"))); + newsPageVersionObject); + if (!CollectionUtils.isEmpty(newsPageVersionMetadataItems)) { + MetadataItem newsPageVersionMetadataItem = newsPageVersionMetadataItems.get(0); + if (newsPageVersionMetadataItem != null) { + Map newsPageVersionMetadataItemProperties = newsPageVersionMetadataItem.getProperties(); + if (!MapUtils.isEmpty(newsPageVersionMetadataItemProperties) + && newsPageVersionMetadataItemProperties.containsKey(NEWS_ATTACHMENTS_IDS) + && newsPageVersionMetadataItemProperties.get(NEWS_ATTACHMENTS_IDS) != null) { + attachmentIds.addAll(List.of(newsPageVersionMetadataItemProperties.get(NEWS_ATTACHMENTS_IDS).split(";"))); + } } } return attachmentIds; } + + private void updateArticlePermissions(List spaces, News article, List articleAttachmentIds) { + Map updateContentPermissionEventListenerData = new HashMap<>(); + updateContentPermissionEventListenerData.putAll(Map.of("spaces", spaces, ARTICLE_CONTENT, article.getBody())); + if (articleAttachmentIds != null) { + updateContentPermissionEventListenerData.put(NEWS_ATTACHMENTS_IDS, articleAttachmentIds); + } + + if (article.getAudience() != null) { + updateContentPermissionEventListenerData.put(NEWS_AUDIENCE, article.getAudience()); + } + NewsUtils.broadcastEvent(NewsUtils.UPDATE_CONTENT_PERMISSIONS, this, updateContentPermissionEventListenerData); + } } + \ No newline at end of file diff --git a/content-service/src/main/java/io/meeds/news/utils/NewsUtils.java b/content-service/src/main/java/io/meeds/news/utils/NewsUtils.java index cc3f1fa3a..017576858 100644 --- a/content-service/src/main/java/io/meeds/news/utils/NewsUtils.java +++ b/content-service/src/main/java/io/meeds/news/utils/NewsUtils.java @@ -53,39 +53,39 @@ public class NewsUtils { - private static final Log LOG = ExoLogger.getLogger(NewsUtils.class); + private static final Log LOG = ExoLogger.getLogger(NewsUtils.class); - public static final String POST_NEWS = "exo.news.postArticle"; + public static final String POST_NEWS = "exo.news.postArticle"; - public static final String POST_NEWS_ARTICLE = "exo.news.gamification.postArticle"; + public static final String POST_NEWS_ARTICLE = "exo.news.gamification.postArticle"; - public static final String PUBLISH_NEWS = "exo.news.gamification.PublishArticle"; + public static final String PUBLISH_NEWS = "exo.news.gamification.PublishArticle"; - public static final String VIEW_NEWS = "exo.news.viewArticle"; + public static final String VIEW_NEWS = "exo.news.viewArticle"; - public static final String SHARE_NEWS = "exo.news.shareArticle"; + public static final String SHARE_NEWS = "exo.news.shareArticle"; - public static final String COMMENT_NEWS = "exo.news.commentArticle"; + public static final String COMMENT_NEWS = "exo.news.commentArticle"; - public static final String LIKE_NEWS = "exo.news.likeArticle"; + public static final String LIKE_NEWS = "exo.news.likeArticle"; - public static final String DELETE_NEWS = "exo.news.deleteArticle"; + public static final String DELETE_NEWS = "exo.news.deleteArticle"; - public static final String UPDATE_NEWS = "exo.news.updateArticle"; + public static final String UPDATE_NEWS = "exo.news.updateArticle"; - public static final String SCHEDULE_NEWS = "exo.news.scheduleArticle"; + public static final String SCHEDULE_NEWS = "exo.news.scheduleArticle"; - public static final String UNSCHEDULE_NEWS = "exo.news.unscheduleArticle"; + public static final String UNSCHEDULE_NEWS = "exo.news.unscheduleArticle"; - public static final String NEWS_METADATA_OBJECT_TYPE = "news"; + public static final String NEWS_METADATA_OBJECT_TYPE = "news"; - public static final String DISPLAYED_STATUS = "displayed"; + public static final String DISPLAYED_STATUS = "displayed"; - public static final String TARGET_PERMISSIONS = "permissions"; + public static final String TARGET_PERMISSIONS = "permissions"; - public static final String SPACE_NEWS_AUDIENCE = "space"; + public static final String SPACE_NEWS_AUDIENCE = "space"; - public static final String ALL_NEWS_AUDIENCE = "all"; + public static final String ALL_NEWS_AUDIENCE = "all"; public static final String PUBLISHER_MEMBERSHIP_NAME = "publisher"; @@ -93,11 +93,11 @@ public class NewsUtils { public static final String PLATFORM_WEB_CONTRIBUTORS_GROUP = "/platform/web-contributors"; - public static final String SHARE_CONTENT_ATTACHMENTS = "content.share.attachments"; - - public static final String ADD_ARTICLE_TRANSLATION = "content.add.article.translation"; + public static final String ADD_ARTICLE_TRANSLATION = "content.add.article.translation"; - public static final String REMOVE_ARTICLE_TRANSLATION = "content.remove.article.translation"; + public static final String REMOVE_ARTICLE_TRANSLATION = "content.remove.article.translation"; + + public static final String UPDATE_CONTENT_PERMISSIONS = "content.update.permissions"; public enum NewsObjectType { DRAFT, LATEST_DRAFT, ARTICLE; @@ -160,43 +160,36 @@ public static List getMyFilteredSpacesIds(org.exoplatform.services.securit return getMySpaces(userIdentity).stream().map(space -> Long.valueOf(space.getId())).toList(); } - public static List getAllowedDraftArticleSpaceIds(org.exoplatform.services.security.Identity userIdentity, List filteredSpacesIds) throws Exception { + public static List getAllowedDraftArticleSpaceIds(org.exoplatform.services.security.Identity userIdentity, + List filteredSpacesIds) throws Exception { SpaceService spaceService = CommonsUtils.getService(SpaceService.class); - return getMySpaces(userIdentity).stream() - .filter(space -> { - boolean allowed = spaceService.canRedactOnSpace(space, userIdentity) - || canPublishNews(space.getId(), userIdentity); - if (!CollectionUtils.isEmpty(filteredSpacesIds)) { - return allowed && filteredSpacesIds.contains(space.getId()); - } - return allowed; - }) - .map(space -> Long.valueOf(space.getId())) - .toList(); + return getMySpaces(userIdentity).stream().filter(space -> { + boolean allowed = spaceService.canRedactOnSpace(space, userIdentity) || canPublishNews(space.getId(), userIdentity); + if (!CollectionUtils.isEmpty(filteredSpacesIds)) { + return allowed && filteredSpacesIds.contains(space.getId()); + } + return allowed; + }).map(space -> Long.valueOf(space.getId())).toList(); } - public static List getAllowedScheduledNewsSpacesIds(org.exoplatform.services.security.Identity currentIdentity, List filteredSpacesIds) throws Exception { + public static List getAllowedScheduledNewsSpacesIds(org.exoplatform.services.security.Identity currentIdentity, + List filteredSpacesIds) throws Exception { SpaceService spaceService = CommonsUtils.getService(SpaceService.class); - return getMySpaces(currentIdentity).stream() - .filter(space -> { - boolean allowed = (spaceService.isManager(space, currentIdentity.getUserId()) - || spaceService.isRedactor(space, currentIdentity.getUserId()) - || canPublishNews(space.getId(), currentIdentity)); - if (!CollectionUtils.isEmpty(filteredSpacesIds)) { - return allowed && filteredSpacesIds.contains(space.getId()); - } - return allowed; - }) - .map(space -> Long.valueOf(space.getId())) - .toList(); + return getMySpaces(currentIdentity).stream().filter(space -> { + boolean allowed = (spaceService.isManager(space, currentIdentity.getUserId()) + || spaceService.isRedactor(space, currentIdentity.getUserId()) || canPublishNews(space.getId(), currentIdentity)); + if (!CollectionUtils.isEmpty(filteredSpacesIds)) { + return allowed && filteredSpacesIds.contains(space.getId()); + } + return allowed; + }).map(space -> Long.valueOf(space.getId())).toList(); } public static boolean canPublishNews(String spaceId, org.exoplatform.services.security.Identity currentIdentity) { if (!StringUtils.isBlank(spaceId)) { SpaceService spaceService = CommonsUtils.getService(SpaceService.class); Space space = spaceService.getSpaceById(spaceId); - return currentIdentity != null - && space != null + return currentIdentity != null && space != null && (currentIdentity.isMemberOf(PLATFORM_WEB_CONTRIBUTORS_GROUP, PUBLISHER_MEMBERSHIP_NAME) || spaceService.isPublisher(space, currentIdentity.getUserId()) || spaceService.isManager(space, currentIdentity.getUserId()) @@ -227,7 +220,7 @@ public static org.exoplatform.services.security.Identity getUserIdentity(String } return identity; } - + public static String buildDraftUrl(DraftPage draftPage) { StringBuilder draftArticleUrl = new StringBuilder(); draftArticleUrl.append("/") @@ -245,19 +238,19 @@ public static String buildNewsArticleUrl(News news, String currentUsername) thro SpaceService spaceService = CommonsUtils.getService(SpaceService.class); if (currentUsername != null && spaceService.isMember(news.getSpaceId(), currentUsername) && news.getActivityId() != null) { newsArticleUrl.append("/") - .append(PortalContainer.getCurrentPortalContainerName()) - .append("/") - .append(CommonsUtils.getCurrentPortalOwner()) - .append("/activity?id=") - .append(news.getActivityId()); + .append(PortalContainer.getCurrentPortalContainerName()) + .append("/") + .append(CommonsUtils.getCurrentPortalOwner()) + .append("/activity?id=") + .append(news.getActivityId()); } else { newsArticleUrl.append("/") - .append(PortalContainer.getCurrentPortalContainerName()) - .append("/") - .append(CommonsUtils.getCurrentPortalOwner()) - .append("/news-detail?newsId=") - .append(news.getId()) - .append("&type=article"); + .append(PortalContainer.getCurrentPortalContainerName()) + .append("/") + .append(CommonsUtils.getCurrentPortalOwner()) + .append("/news-detail?newsId=") + .append(news.getId()) + .append("&type=article"); } if (news.getLang() != null) { newsArticleUrl.append("&lang="); @@ -265,6 +258,7 @@ public static String buildNewsArticleUrl(News news, String currentUsername) thro } return newsArticleUrl.toString(); } + public static String buildSpaceUrl(String spaceId) { StringBuilder spaceUrl = new StringBuilder(); SpaceService spaceService = CommonsUtils.getService(SpaceService.class); @@ -306,5 +300,4 @@ private static List getMySpaces(org.exoplatform.services.security.Identit return Arrays.asList(memberSpacesListAccess.load(0, memberSpacesListAccess.getSize())); } - } diff --git a/content-service/src/test/java/io/meeds/news/service/impl/NewsServiceImplTest.java b/content-service/src/test/java/io/meeds/news/service/impl/NewsServiceImplTest.java index dddf3da2b..fe07e8278 100644 --- a/content-service/src/test/java/io/meeds/news/service/impl/NewsServiceImplTest.java +++ b/content-service/src/test/java/io/meeds/news/service/impl/NewsServiceImplTest.java @@ -933,6 +933,7 @@ public void testUnScheduleNews() throws Exception { News newsArticle = mock(News.class); when(newsArticle.getId()).thenReturn("1"); + when(newsArticle.getBody()).thenReturn("body"); DraftPage draftPage = mock(DraftPage.class); when(draftPage.getUpdatedDate()).thenReturn(new Date()); From 54e2ae5fd87a10d95e422b7fef8d717ad629e197 Mon Sep 17 00:00:00 2001 From: Ayoub Zayati Date: Tue, 1 Oct 2024 11:06:58 +0200 Subject: [PATCH 07/14] fix: Fix displaying content detail for public sites - EXO-74419 - Meeds-io/MIPs#129 (#243) --- .../components/ContentRichEditor.vue | 2 +- .../components/ExoNewsDetailsApp.vue | 2 +- .../components/ExoNewsDetailsActivity.vue | 2 +- .../main/webapp/vue-app/services/newsServices.js | 14 ++++++++++++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue b/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue index afb13bebc..fd5531c97 100644 --- a/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue +++ b/content-webapp/src/main/webapp/vue-app/news-activity-composer-app/components/ContentRichEditor.vue @@ -659,7 +659,7 @@ export default { this.$refs.editor.closePluginsDrawer(); }, getAvailableLanguages() { - return this.$notesService.getAvailableLanguages().then(data => { + return this.$newsServices.getAvailableLanguages().then(data => { this.languages = data || []; this.languages.sort((a, b) => a.text.localeCompare(b.text)); this.allLanguages = this.languages; diff --git a/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue b/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue index d389be6d7..2a1f03f66 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue @@ -68,7 +68,7 @@ export default { }, methods: { getAvailableLanguages() { - return this.$notesService.getAvailableLanguages().then(data => { + return this.$newsServices.getAvailableLanguages().then(data => { this.languages = data || []; }); }, diff --git a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsActivity.vue b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsActivity.vue index d05e91d28..bfefe235c 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsActivity.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsActivity.vue @@ -101,7 +101,7 @@ export default { }, methods: { getAvailableLanguages() { - return this.$notesService.getAvailableLanguages().then(data => { + return this.$newsServices.getAvailableLanguages().then(data => { this.languages = data || []; }); }, diff --git a/content-webapp/src/main/webapp/vue-app/services/newsServices.js b/content-webapp/src/main/webapp/vue-app/services/newsServices.js index ca5639052..4dea4d5f4 100644 --- a/content-webapp/src/main/webapp/vue-app/services/newsServices.js +++ b/content-webapp/src/main/webapp/vue-app/services/newsServices.js @@ -277,6 +277,20 @@ export function deleteArticleTranslation(newsId, lang) { }); } +export function getAvailableLanguages() { + const lang = eXo?.env.portal.language || 'en'; + return fetch(`${newsConstants.PORTAL}/${newsConstants.PORTAL_REST}/notes/languages?lang=${lang}`, { + method: 'GET', + credentials: 'include', + }).then(resp => { + if (!resp || !resp.ok) { + throw new Error('Response code indicates a server error', resp); + } else { + return resp.json(); + } + }); +} + export function getArticleLanguages(articleId, withDrafts) { return fetch(`${newsConstants.CONTENT_API}/contents/translation/${articleId}?withDrafts=${withDrafts}`, { credentials: 'include', From e46e7c40b7d793e370e1202438cf78114a4abe52 Mon Sep 17 00:00:00 2001 From: Ayoub Zayati Date: Tue, 1 Oct 2024 14:56:00 +0200 Subject: [PATCH 08/14] feat: Implement Space news target auto creation listener in order to create space news target when a space is created - EXO-73039 - Meeds-io/MIPs#129 (#245) --- .../SpaceNewsTargetAutoCreationListener.java | 64 +++++++++++++++++++ .../news/service/NewsTargetingService.java | 20 +++++- .../impl/NewsTargetingServiceImpl.java | 43 +++++++++---- .../webapp/WEB-INF/conf/configuration.xml | 1 + .../conf/news/feature-flags-configuration.xml | 35 ++++++++++ .../WEB-INF/conf/news/news-configuration.xml | 10 +++ 6 files changed, 160 insertions(+), 13 deletions(-) create mode 100644 content-service/src/main/java/io/meeds/news/listener/SpaceNewsTargetAutoCreationListener.java create mode 100644 content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml diff --git a/content-service/src/main/java/io/meeds/news/listener/SpaceNewsTargetAutoCreationListener.java b/content-service/src/main/java/io/meeds/news/listener/SpaceNewsTargetAutoCreationListener.java new file mode 100644 index 000000000..e869f3832 --- /dev/null +++ b/content-service/src/main/java/io/meeds/news/listener/SpaceNewsTargetAutoCreationListener.java @@ -0,0 +1,64 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2024 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.news.listener; + +import java.util.Map; + +import org.exoplatform.commons.utils.CommonsUtils; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.services.security.ConversationState; +import org.exoplatform.services.security.Identity; +import org.exoplatform.social.core.space.SpaceListenerPlugin; +import org.exoplatform.social.core.space.model.Space; +import org.exoplatform.social.core.space.spi.SpaceLifeCycleEvent; + +import io.meeds.news.rest.NewsTargetingEntity; +import io.meeds.news.service.NewsTargetingService; + +public class SpaceNewsTargetAutoCreationListener extends SpaceListenerPlugin { + + private static final Log LOG = + ExoLogger.getLogger(SpaceNewsTargetAutoCreationListener.class); + + private NewsTargetingService newsTargetingService; + + private static final String SPACE_NEWS_TARGET_AUTO_CREATION_FEATURE = "spaceNewsTargetAutoCreation"; + + public SpaceNewsTargetAutoCreationListener(NewsTargetingService newsTargetingService) { + this.newsTargetingService = newsTargetingService; + } + + @Override + public void spaceCreated(SpaceLifeCycleEvent event) { + if (CommonsUtils.isFeatureActive(SPACE_NEWS_TARGET_AUTO_CREATION_FEATURE)) { + Identity currentIdentity = ConversationState.getCurrent().getIdentity(); + Space space = event.getSpace(); + NewsTargetingEntity spaceNewsTargetEntity = new NewsTargetingEntity(); + spaceNewsTargetEntity.setName(space.getDisplayName()); + spaceNewsTargetEntity.setProperties(Map.of("label", space.getDisplayName(), "permissions", "space:" + space.getId())); + try { + newsTargetingService.createNewsTarget(spaceNewsTargetEntity, currentIdentity, false); + } catch (Exception e) { + LOG.warn("Can't create space {} news target", space.getPrettyName()); + } + } + } +} diff --git a/content-service/src/main/java/io/meeds/news/service/NewsTargetingService.java b/content-service/src/main/java/io/meeds/news/service/NewsTargetingService.java index 4406aed3d..9619bcdb2 100644 --- a/content-service/src/main/java/io/meeds/news/service/NewsTargetingService.java +++ b/content-service/src/main/java/io/meeds/news/service/NewsTargetingService.java @@ -63,8 +63,7 @@ void deleteTargetByName(String targetName, * Gets the {@link List} of {@link News} targets linked to a given * {@link News} * - * @param news {@link News} for which targets to be - * retrieved + * @param news {@link News} for which targets to be retrieved * @return {@link List} of {@link News} targets by {@link News} news object */ List getTargetsByNews(News news); @@ -127,6 +126,23 @@ Metadata createNewsTarget(NewsTargetingEntity newsTargetingEntity, org.exoplatform.services.security.Identity currentIdentity) throws IllegalArgumentException, IllegalAccessException; + /** + * Create news target + * + * @param newsTargetingEntity {@link News} TargetingEntity + * @param currentIdentity current {@link Identity} attempting to create + * {@link News} target + * @param checkPermissions true if permissions are checked + * @return created {@link News} target {@link Metadata} + * @throws IllegalArgumentException when user creates a {@link News} target + * that already exists + * @throws IllegalAccessException when user doesn't have access to create + * {@link News} target + */ + Metadata createNewsTarget(NewsTargetingEntity newsTargetingEntity, + org.exoplatform.services.security.Identity currentIdentity, + boolean checkPermissions) throws IllegalArgumentException, IllegalAccessException; + /** * Update news target * diff --git a/content-service/src/main/java/io/meeds/news/service/impl/NewsTargetingServiceImpl.java b/content-service/src/main/java/io/meeds/news/service/impl/NewsTargetingServiceImpl.java index afbdc33a7..ecd0a4eec 100644 --- a/content-service/src/main/java/io/meeds/news/service/impl/NewsTargetingServiceImpl.java +++ b/content-service/src/main/java/io/meeds/news/service/impl/NewsTargetingServiceImpl.java @@ -25,6 +25,9 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; @@ -48,9 +51,6 @@ import io.meeds.news.rest.NewsTargetingPermissionsEntity; import io.meeds.news.service.NewsTargetingService; import io.meeds.news.utils.NewsUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Primary; -import org.springframework.stereotype.Service; /** * Service managing News Targeting @@ -118,7 +118,7 @@ public List getAllowedTargets(org.exoplatform.services.secu @Override public void deleteTargetByName(String targetName, org.exoplatform.services.security.Identity currentIdentity) throws IllegalAccessException { - if (currentIdentity != null && !NewsUtils.canManageNewsPublishTargets(currentIdentity)) { + if (!NewsUtils.canManageNewsPublishTargets(currentIdentity)) { throw new IllegalArgumentException("User " + currentIdentity.getUserId() + " not authorized to delete news target with name " + targetName); } @@ -129,7 +129,10 @@ public void deleteTargetByName(String targetName, @Override public List getTargetsByNews(News news) { - NewsTargetObject newsTargetObject = new NewsTargetObject(NewsUtils.NEWS_METADATA_OBJECT_TYPE, news.getId(), null, Long.parseLong(news.getSpaceId())); + NewsTargetObject newsTargetObject = new NewsTargetObject(NewsUtils.NEWS_METADATA_OBJECT_TYPE, + news.getId(), + null, + Long.parseLong(news.getSpaceId())); List newsTargets = metadataService.getMetadataItemsByMetadataTypeAndObject(METADATA_TYPE.getName(), newsTargetObject); return newsTargets.stream().map(MetadataItem::getMetadata).map(Metadata::getName).toList(); @@ -144,14 +147,21 @@ public void saveNewsTarget(News news, if (!NewsUtils.canPublishNews(news.getSpaceId(), currentIdentity)) { throw new IllegalAccessException("User " + currentUserId + " not authorized to save news targets"); } - NewsTargetObject newsTargetObject = new NewsTargetObject(NewsUtils.NEWS_METADATA_OBJECT_TYPE, news.getId(), null, Long.parseLong(news.getSpaceId())); + NewsTargetObject newsTargetObject = new NewsTargetObject(NewsUtils.NEWS_METADATA_OBJECT_TYPE, + news.getId(), + null, + Long.parseLong(news.getSpaceId())); Identity currentSocIdentity = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, currentUserId); Map properties = new LinkedHashMap<>(); properties.put(NewsUtils.DISPLAYED_STATUS, String.valueOf(displayed)); targets.stream().forEach(targetName -> { try { MetadataKey metadataKey = new MetadataKey(NewsTargetingService.METADATA_TYPE.getName(), targetName, 0); - metadataService.createMetadataItem(newsTargetObject, metadataKey, properties, Long.parseLong(currentSocIdentity.getId()), false); + metadataService.createMetadataItem(newsTargetObject, + metadataKey, + properties, + Long.parseLong(currentSocIdentity.getId()), + false); } catch (ObjectAlreadyExistsException e) { LOG.warn("Targets with name {} is already associated to object {}. Ignore error since it will not affect result.", targetName, @@ -174,7 +184,10 @@ public List getNewsTargetItemsByTargetName(String targetName, long @Override public void deleteNewsTargets(News news) { - NewsTargetObject newsTargetObject = new NewsTargetObject(NewsUtils.NEWS_METADATA_OBJECT_TYPE, news.getId(), null, Long.parseLong(news.getSpaceId())); + NewsTargetObject newsTargetObject = new NewsTargetObject(NewsUtils.NEWS_METADATA_OBJECT_TYPE, + news.getId(), + null, + Long.parseLong(news.getSpaceId())); metadataService.deleteMetadataItemsByMetadataTypeAndObject(METADATA_TYPE.getName(), newsTargetObject); } @@ -191,13 +204,21 @@ public void deleteNewsTargets(News news, String currentUserId) throws IllegalAcc public Metadata createNewsTarget(NewsTargetingEntity newsTargetingEntity, org.exoplatform.services.security.Identity currentIdentity) throws IllegalArgumentException, IllegalAccessException { + return createNewsTarget(newsTargetingEntity, currentIdentity, true); + } + + @Override + public Metadata createNewsTarget(NewsTargetingEntity newsTargetingEntity, + org.exoplatform.services.security.Identity currentIdentity, + boolean withPermissions) throws IllegalArgumentException, IllegalAccessException { + if (withPermissions && !NewsUtils.canManageNewsPublishTargets(currentIdentity)) { + throw new IllegalAccessException("User " + currentIdentity.getUserId() + " not authorized to create news targets"); + } Identity identity = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, currentIdentity.getUserId()); long userIdentityId = identity == null ? 0 : Long.parseLong(identity.getId()); Metadata metadata = fromEntity(newsTargetingEntity); metadata.setCreatorId(userIdentityId); - if (!NewsUtils.canManageNewsPublishTargets(currentIdentity)) { - throw new IllegalAccessException("User " + currentIdentity.getUserId() + " not authorized to create news targets"); - } + MetadataKey targetMetadataKey = new MetadataKey(METADATA_TYPE.getName(), metadata.getName(), 0); Metadata storedMetadata = metadataService.getMetadataByKey(targetMetadataKey); if (storedMetadata != null) { diff --git a/content-webapp/src/main/webapp/WEB-INF/conf/configuration.xml b/content-webapp/src/main/webapp/WEB-INF/conf/configuration.xml index fb4a4bcd6..b19066d98 100644 --- a/content-webapp/src/main/webapp/WEB-INF/conf/configuration.xml +++ b/content-webapp/src/main/webapp/WEB-INF/conf/configuration.xml @@ -32,5 +32,6 @@ war:/conf/news/metadata-plugins-configuration.xml war:/conf/news/gamification-configuration.xml war:/conf/news/ckeditor-configuration.xml + war:/conf/news/feature-flags-configuration.xml diff --git a/content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml b/content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml new file mode 100644 index 000000000..902800a6e --- /dev/null +++ b/content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml @@ -0,0 +1,35 @@ + + + + + ContentFeatureProperties + org.exoplatform.container.ExtendedPropertyConfigurator + + + ContentFeatureProperties + Content Feature enablement flag + + + + + \ No newline at end of file diff --git a/content-webapp/src/main/webapp/WEB-INF/conf/news/news-configuration.xml b/content-webapp/src/main/webapp/WEB-INF/conf/news/news-configuration.xml index e16eb920f..e65a72572 100644 --- a/content-webapp/src/main/webapp/WEB-INF/conf/news/news-configuration.xml +++ b/content-webapp/src/main/webapp/WEB-INF/conf/news/news-configuration.xml @@ -59,4 +59,14 @@ + + + org.exoplatform.social.core.space.spi.SpaceService + + content.listeners.social.space + addSpaceListener + io.meeds.news.listener.SpaceNewsTargetAutoCreationListener + Create a space news target when a new space is created + + From 095617abdee83df39d23641207198bb63b878e49 Mon Sep 17 00:00:00 2001 From: Ayoub Zayati Date: Mon, 7 Oct 2024 14:51:13 +0200 Subject: [PATCH 09/14] feat: Remove unread activities notifications channel for PublishNewsNotificationPlugin - EXO-73048 - Meeds-io/MIPs#129 (#252) --- .../main/webapp/WEB-INF/conf/news/notification/configuration.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/content-webapp/src/main/webapp/WEB-INF/conf/news/notification/configuration.xml b/content-webapp/src/main/webapp/WEB-INF/conf/news/notification/configuration.xml index 4ff15fc67..fc49e6c45 100644 --- a/content-webapp/src/main/webapp/WEB-INF/conf/news/notification/configuration.xml +++ b/content-webapp/src/main/webapp/WEB-INF/conf/news/notification/configuration.xml @@ -189,7 +189,6 @@ notification.plugin.ids - PublishNewsNotificationPlugin MentionInNewsNotificationPlugin PostNewsNotificationPlugin From 85e64b6be5e2f24c829b92c95ebf1130d1070caa Mon Sep 17 00:00:00 2001 From: Ayoub Zayati Date: Mon, 7 Oct 2024 14:55:57 +0200 Subject: [PATCH 10/14] feat: Remove unread activities notifications channel for MentionInNewsNotificationPlugin - EXO-73152 - Meeds-io/MIPs#129 (#253) --- .../main/webapp/WEB-INF/conf/news/notification/configuration.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/content-webapp/src/main/webapp/WEB-INF/conf/news/notification/configuration.xml b/content-webapp/src/main/webapp/WEB-INF/conf/news/notification/configuration.xml index fc49e6c45..ee2888306 100644 --- a/content-webapp/src/main/webapp/WEB-INF/conf/news/notification/configuration.xml +++ b/content-webapp/src/main/webapp/WEB-INF/conf/news/notification/configuration.xml @@ -189,7 +189,6 @@ notification.plugin.ids - MentionInNewsNotificationPlugin PostNewsNotificationPlugin From bd1de534130a238a535cb0ac0f46957316caaf70 Mon Sep 17 00:00:00 2001 From: Ayoub Zayati Date: Mon, 7 Oct 2024 18:02:25 +0200 Subject: [PATCH 11/14] feat: Send published article public link in publish news mail notification message - EXO-73050 - Meeds-io/MIPs#129 (#255) --- .../meeds/news/notification/provider/MailTemplateProvider.java | 3 +-- .../news/notification/provider/MailTemplateProviderTest.java | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/content-service/src/main/java/io/meeds/news/notification/provider/MailTemplateProvider.java b/content-service/src/main/java/io/meeds/news/notification/provider/MailTemplateProvider.java index 1f80c1ec1..ba3c331a6 100644 --- a/content-service/src/main/java/io/meeds/news/notification/provider/MailTemplateProvider.java +++ b/content-service/src/main/java/io/meeds/news/notification/provider/MailTemplateProvider.java @@ -111,8 +111,7 @@ protected MessageInfo makeMessage(NotificationContext ctx) { templateContext.put("AUTHOR_AVATAR_URL", encoder.encode(authorAvatarUrl)); templateContext.put("CONTEXT", encoder.encode(context)); StringBuilder activityUrl = new StringBuilder(); - Space space = spaceService.getSpaceByDisplayName(contentSpaceName); - if (pluginId.equals(PublishNewsNotificationPlugin.ID) && !spaceService.isMember(space, notification.getTo())) { + if (pluginId.equals(PublishNewsNotificationPlugin.ID)) { String portalName = PortalContainer.getCurrentPortalContainerName(); String portalOwner = CommonsUtils.getCurrentPortalOwner(); String currentDomain = CommonsUtils.getCurrentDomain(); diff --git a/content-service/src/test/java/io/meeds/news/notification/provider/MailTemplateProviderTest.java b/content-service/src/test/java/io/meeds/news/notification/provider/MailTemplateProviderTest.java index 62edf4edf..4dbcff7b3 100644 --- a/content-service/src/test/java/io/meeds/news/notification/provider/MailTemplateProviderTest.java +++ b/content-service/src/test/java/io/meeds/news/notification/provider/MailTemplateProviderTest.java @@ -153,7 +153,6 @@ public void shouldInstantiateMailTemplate() { when(encoder.encode("jean")).thenReturn("jean"); when(encoder.encode("http://localhost:8080/content/images/newsImageDefault.png")).thenReturn("http://localhost:8080/content/images/newsImageDefault.png"); when(encoder.encode("COMMENT MY NEWS")).thenReturn("COMMENT MY NEWS"); - when(encoder.encode("http://localhost:8080/portal/intranet/activity?id=39")).thenReturn("http://localhost:8080/portal/intranet/activity?id=39"); when(notification.getValueOwnerParameter("read")).thenReturn("true"); when(notification.getId()).thenReturn("NotifId123"); Date date = new Date(); From ffb7f4cdd6980af2cc1812090c190b40ec53c05a Mon Sep 17 00:00:00 2001 From: Helmi Akermi <70575401+hakermi@users.noreply.github.com> Date: Wed, 9 Oct 2024 09:41:39 +0100 Subject: [PATCH 12/14] feat: Implement new article view layout - EXO-73043 - Meeds-io/MIPs#129 (#256) Implement new article view layout --- .../conf/news/feature-flags-configuration.xml | 45 ++++-- .../workspace/UIContentHeadTemplate.gtmpl | 10 ++ .../main/webapp/skin/less/newsDetails.less | 20 +++ .../components/ExoNewsDetailsApp.vue | 12 +- .../components/ExoNewsDetails.vue | 109 ++++++++------- .../components/ExoNewsDetailsActivity.vue | 33 ++++- .../components/ExoNewsDetailsBody.vue | 130 ++++++++++++++++-- .../components/ExoNewsDetailsToolBar.vue | 34 ++++- .../main/webapp/vue-app/news-details/main.js | 22 +-- .../ExoNewsDetailsActionMenuApp.vue | 40 +++--- .../news/components/NewsActionMenuItems.vue | 27 +++- .../vue-app/news/components/NewsApp.vue | 51 ++++++- .../vue-app/news/components/NewsAppItem.vue | 35 +---- .../news/components/NewsMobileActionMenu.vue | 89 ++++++------ 14 files changed, 454 insertions(+), 203 deletions(-) create mode 100644 content-webapp/src/main/webapp/groovy/webui/workspace/UIContentHeadTemplate.gtmpl diff --git a/content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml b/content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml index 902800a6e..224454f61 100644 --- a/content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml +++ b/content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml @@ -21,15 +21,36 @@ - - ContentFeatureProperties - org.exoplatform.container.ExtendedPropertyConfigurator - - - ContentFeatureProperties - Content Feature enablement flag - - - - - \ No newline at end of file + + + ContentFeatureProperties + org.exoplatform.container.ExtendedPropertyConfigurator + + + ContentFeatureProperties + Content Feature enablement flag + + + + + + + + org.exoplatform.groovyscript.text.TemplateService + + UIPortalApplication-head + addTemplateExtension + org.exoplatform.groovyscript.text.TemplateExtensionPlugin + + + templates + The list of templates to include in HTML Page Header with UIPortalApplication.gtmpl + + war:/groovy/webui/workspace/UIContentHeadTemplate.gtmpl + + + + + diff --git a/content-webapp/src/main/webapp/groovy/webui/workspace/UIContentHeadTemplate.gtmpl b/content-webapp/src/main/webapp/groovy/webui/workspace/UIContentHeadTemplate.gtmpl new file mode 100644 index 000000000..7f055fe04 --- /dev/null +++ b/content-webapp/src/main/webapp/groovy/webui/workspace/UIContentHeadTemplate.gtmpl @@ -0,0 +1,10 @@ +<% + import org.exoplatform.commons.api.settings.ExoFeatureService; + def rcontext = _ctx.getRequestContext(); + ExoFeatureService featureService = uicomponent.getApplicationComponent(ExoFeatureService.class); + def userName = rcontext.getRemoteUser(); +%> + + diff --git a/content-webapp/src/main/webapp/skin/less/newsDetails.less b/content-webapp/src/main/webapp/skin/less/newsDetails.less index f1d10b07f..c70f036ea 100644 --- a/content-webapp/src/main/webapp/skin/less/newsDetails.less +++ b/content-webapp/src/main/webapp/skin/less/newsDetails.less @@ -104,6 +104,10 @@ opacity: 0.4; } + .go-back-button { + background: rgba(0, 0, 0, 0.5); + color: @whiteColorDefault + } } } @@ -177,11 +181,27 @@ .newsDetails { .news-top-information { + .articleTitle { + font-size: 34px !important; + line-height: 1.1; + } + .newsTitle { max-width: 80%; margin: 20px auto 0; } } + + .article-summary { + font-size: 24px; + line-height: 1.2; + } + + .no-updater-info { + margin-left: 54px ~'!important; /** orientation=lt */ '; + margin-right: 54px ~'!important; /** orientation=rt */ '; + } + .news-header-content { display: inline-flex; .newsInformation { diff --git a/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue b/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue index 2a1f03f66..96c15b37c 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue @@ -1,5 +1,7 @@ @@ -47,6 +48,11 @@ export default { originalVersion: null, previousSelectedTranslation: null }), + computed: { + articleNewLayoutEnabled() { + return eXo?.env?.portal?.articleNewLayoutEnabled; + } + }, created() { this.getAvailableLanguages(); const url = new URL(window.location.href); diff --git a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue index 5c6bb92b5..5eed486eb 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetails.vue @@ -19,57 +19,69 @@ --> \ No newline at end of file + diff --git a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsBody.vue b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsBody.vue index c00e6c9cf..bb1a75a30 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsBody.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsBody.vue @@ -20,7 +20,105 @@ --> - @@ -132,6 +120,9 @@ export default { computed: { isMobile() { return this.$vuetify.breakpoint.name === 'xs' || this.$vuetify.breakpoint.name === 'sm'; + }, + articleNewLayoutEnabled() { + return eXo?.env?.portal?.articleNewLayoutEnabled; } }, methods: { @@ -155,8 +146,19 @@ export default { } }, openBottomMenu() { - return this.isMobile && this.$root.$emit('open-news-mobile-action-menu'); + if (this.isMobile) { + this.$root.$emit('open-news-mobile-action-menu', { + news: this.news, + showShareButton: this.showShareButton, + showEditButton: this.showEditButton, + showResumeButton: this.showResumeButton, + showDeleteButton: this.showDeleteButton, + showPublishButton: this.showCopyLinkButton, + showCopyLinkButton: this.showCopyLinkButton, + currentApp: this.currentApp + }); + } } } }; - \ No newline at end of file + diff --git a/content-webapp/src/main/webapp/vue-app/news/components/NewsActionMenuItems.vue b/content-webapp/src/main/webapp/vue-app/news/components/NewsActionMenuItems.vue index fe4d977fe..254722544 100644 --- a/content-webapp/src/main/webapp/vue-app/news/components/NewsActionMenuItems.vue +++ b/content-webapp/src/main/webapp/vue-app/news/components/NewsActionMenuItems.vue @@ -33,7 +33,10 @@ {{ $t('news.details.header.menu.copy.link') }} - + @@ -43,7 +46,10 @@ {{ $t('news.details.header.menu.edit') }} - + @@ -53,7 +59,10 @@ {{ $t('news.details.header.menu.share') }} - + @@ -63,7 +72,10 @@ {{ $t('news.details.header.menu.resume') }} - + @@ -73,7 +85,10 @@ {{ $t('news.details.header.menu.publish') }} - + @@ -131,4 +146,4 @@ export default { } }, }; - \ No newline at end of file + diff --git a/content-webapp/src/main/webapp/vue-app/news/components/NewsApp.vue b/content-webapp/src/main/webapp/vue-app/news/components/NewsApp.vue index 649cea538..cb0d9bbf2 100644 --- a/content-webapp/src/main/webapp/vue-app/news/components/NewsApp.vue +++ b/content-webapp/src/main/webapp/vue-app/news/components/NewsApp.vue @@ -92,9 +92,10 @@ :key="news.newsId" :news="news" :news-filter="newsFilter" + class="newsItem" @update-news-list="updateNewsList" @delete-news="deleteNews" - class="newsItem" /> + @open-delete-confirm-dialog="deleteConfirmDialog" />
@@ -107,8 +108,19 @@ {{ $t('news.app.loadMore') }}
+ + @@ -144,6 +156,7 @@ export default { day: 'numeric', }, newsScheduleAndFilterDisplaying: false, + newsToDelete: null }; }, computed: { @@ -166,6 +179,12 @@ export default { isDraftsFilter() { return this.newsFilter === 'drafts'; }, + confirmDeleteNewsDialogMessage() { + return this.isDraftsFilter ? this.$t('news.message.confirmDeleteDraftNews') : this.$t('news.message.confirmDeleteNews'); + }, + confirmDeleteNewsDialogTitle() { + return this.isDraftsFilter ? this.$t('news.title.confirmDeleteDraftNews') : this.$t('news.title.confirmDeleteNews'); + }, }, watch: { searchText() { @@ -250,6 +269,36 @@ export default { }); }, methods: { + editLink(news) { + const editUrl = this.getEditUrl(news); + window.open(editUrl, '_blank'); + this.$refs?.mobileActionMenu?.close(); + }, + deleteByConfirm() { + this.deleteNews(this.newsToDelete); + }, + deleteConfirmDialog(news) { + this.newsToDelete = news; + this.$refs.deleteConfirmDialog.open(); + this.$refs.mobileActionMenu.close(); + }, + getEditUrl(news) { + let editUrl = `${eXo.env.portal.context}/${eXo.env.portal.metaPortalName}/news/editor?newsId=${news.targetPageId || news.newsId}`; + if (news.spaceId) { + editUrl += `&spaceId=${news.spaceId}`; + } + if (news.activityId) { + editUrl += `&activityId=${news.activityId}`; + } + if (news.spaceUrl) { + editUrl += `&spaceName=${news.spaceUrl.substring(news.spaceUrl.lastIndexOf('/') + 1)}`; + } + editUrl += `&type=${news.activityId && 'latest_draft' || 'draft'}`; + if (news.lang) { + editUrl += `&lang=${news.lang}`; + } + return editUrl; + }, getDraftUrl(item) { let draftUrl = `${eXo.env.portal.context}/${eXo.env.portal.metaPortalName}/news/editor`; draftUrl += `?newsId=${item.activityId && item.targetPageId || item.id}`; diff --git a/content-webapp/src/main/webapp/vue-app/news/components/NewsAppItem.vue b/content-webapp/src/main/webapp/vue-app/news/components/NewsAppItem.vue index b246882bd..089f41423 100644 --- a/content-webapp/src/main/webapp/vue-app/news/components/NewsAppItem.vue +++ b/content-webapp/src/main/webapp/vue-app/news/components/NewsAppItem.vue @@ -45,13 +45,6 @@ :current-app="currentApplication" @delete-article="deleteConfirmDialog" @edit-article="editLink(news)" /> -
@@ -146,18 +139,9 @@ export default { isDraftsFilter() { return this.newsFilter === 'drafts'; }, - confirmDeleteNewsDialogMessage() { - return this.isDraftsFilter ? this.$t('news.message.confirmDeleteDraftNews') : this.$t('news.message.confirmDeleteNews'); - }, - confirmDeleteNewsDialogTitle() { - return this.isDraftsFilter ? this.$t('news.title.confirmDeleteDraftNews') : this.$t('news.title.confirmDeleteNews'); - }, displayClock() { return this.news && (this.news.schedulePostDate || this.news.updatedDate); }, - draftNews() { - return this.news && this.news.draft; - }, spaceId() { return this.news && this.news.spaceId; }, @@ -170,28 +154,15 @@ export default { }, methods: { getEditUrl(news) { - let editUrl = `${eXo.env.portal.context}/${eXo.env.portal.metaPortalName}/news/editor?newsId=${news.targetPageId || news.newsId}`; - if (news.spaceId) { - editUrl += `&spaceId=${news.spaceId}`; - } - if (news.activityId) { - editUrl += `&activityId=${news.activityId}`; - } - if (news.spaceUrl) { - editUrl += `&spaceName=${news.spaceUrl.substring(news.spaceUrl.lastIndexOf('/') + 1)}`; - } - editUrl += `&type=${news.activityId && 'latest_draft' || 'draft'}`; - if (news.lang) { - editUrl += `&lang=${news.lang}`; - } - return editUrl; + return this.$parent.$parent.getEditUrl(news); }, editLink(news) { const editUrl = this.getEditUrl(news); window.open(editUrl, '_blank'); + this.$refs?.mobileActionMenu?.close(); }, deleteConfirmDialog() { - this.$refs.deleteConfirmDialog.open(); + this.$emit('open-delete-confirm-dialog', this.news); }, } }; diff --git a/content-webapp/src/main/webapp/vue-app/news/components/NewsMobileActionMenu.vue b/content-webapp/src/main/webapp/vue-app/news/components/NewsMobileActionMenu.vue index b2e15f31f..ff0a5582a 100644 --- a/content-webapp/src/main/webapp/vue-app/news/components/NewsMobileActionMenu.vue +++ b/content-webapp/src/main/webapp/vue-app/news/components/NewsMobileActionMenu.vue @@ -33,68 +33,67 @@ :show-publish-button="showPublishButton" :show-resume-button="showResumeButton" :show-share-button="showShareButton" - @copy-link="$emit('copy-link')" + @copy-link="copyLink" @edit-article="$emit('edit-article', news)" - @delete-article="$emit('delete-article')" /> + @delete-article="$emit('delete-article', news)" /> \ No newline at end of file + From 2abca1f119aa742cf7826cf6d86a50c08c70381e Mon Sep 17 00:00:00 2001 From: Ayoub Zayati Date: Fri, 11 Oct 2024 11:00:21 +0200 Subject: [PATCH 13/14] feat: Remove all the feature flag MIP129 - EXO-74680 - Meeds-io/MIPs#129 (#259) --- .../SpaceNewsTargetAutoCreationListener.java | 26 ++-- .../webapp/WEB-INF/conf/configuration.xml | 1 - .../conf/news/feature-flags-configuration.xml | 56 ------- .../workspace/UIContentHeadTemplate.gtmpl | 10 -- .../components/ExoNewsDetailsApp.vue | 7 +- .../components/ExoNewsDetails.vue | 22 +-- .../components/ExoNewsDetailsBody.vue | 145 +----------------- .../components/ExoNewsDetailsToolBar.vue | 14 +- .../mobile/ExoNewsDetailsBodyMobile.vue | 128 ---------------- .../mobile/ExoNewsDetailsToolBarMobile.vue | 114 -------------- .../vue-app/news-details/initComponents.js | 6 +- .../ExoNewsDetailsActionMenuApp.vue | 5 +- 12 files changed, 16 insertions(+), 518 deletions(-) delete mode 100644 content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml delete mode 100644 content-webapp/src/main/webapp/groovy/webui/workspace/UIContentHeadTemplate.gtmpl delete mode 100644 content-webapp/src/main/webapp/vue-app/news-details/components/mobile/ExoNewsDetailsBodyMobile.vue delete mode 100644 content-webapp/src/main/webapp/vue-app/news-details/components/mobile/ExoNewsDetailsToolBarMobile.vue diff --git a/content-service/src/main/java/io/meeds/news/listener/SpaceNewsTargetAutoCreationListener.java b/content-service/src/main/java/io/meeds/news/listener/SpaceNewsTargetAutoCreationListener.java index e869f3832..b2aa8dff5 100644 --- a/content-service/src/main/java/io/meeds/news/listener/SpaceNewsTargetAutoCreationListener.java +++ b/content-service/src/main/java/io/meeds/news/listener/SpaceNewsTargetAutoCreationListener.java @@ -21,7 +21,6 @@ import java.util.Map; -import org.exoplatform.commons.utils.CommonsUtils; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.security.ConversationState; @@ -35,30 +34,25 @@ public class SpaceNewsTargetAutoCreationListener extends SpaceListenerPlugin { - private static final Log LOG = - ExoLogger.getLogger(SpaceNewsTargetAutoCreationListener.class); + private static final Log LOG = ExoLogger.getLogger(SpaceNewsTargetAutoCreationListener.class); private NewsTargetingService newsTargetingService; - private static final String SPACE_NEWS_TARGET_AUTO_CREATION_FEATURE = "spaceNewsTargetAutoCreation"; - public SpaceNewsTargetAutoCreationListener(NewsTargetingService newsTargetingService) { this.newsTargetingService = newsTargetingService; } @Override public void spaceCreated(SpaceLifeCycleEvent event) { - if (CommonsUtils.isFeatureActive(SPACE_NEWS_TARGET_AUTO_CREATION_FEATURE)) { - Identity currentIdentity = ConversationState.getCurrent().getIdentity(); - Space space = event.getSpace(); - NewsTargetingEntity spaceNewsTargetEntity = new NewsTargetingEntity(); - spaceNewsTargetEntity.setName(space.getDisplayName()); - spaceNewsTargetEntity.setProperties(Map.of("label", space.getDisplayName(), "permissions", "space:" + space.getId())); - try { - newsTargetingService.createNewsTarget(spaceNewsTargetEntity, currentIdentity, false); - } catch (Exception e) { - LOG.warn("Can't create space {} news target", space.getPrettyName()); - } + Identity currentIdentity = ConversationState.getCurrent().getIdentity(); + Space space = event.getSpace(); + NewsTargetingEntity spaceNewsTargetEntity = new NewsTargetingEntity(); + spaceNewsTargetEntity.setName(space.getDisplayName()); + spaceNewsTargetEntity.setProperties(Map.of("label", space.getDisplayName(), "permissions", "space:" + space.getId())); + try { + newsTargetingService.createNewsTarget(spaceNewsTargetEntity, currentIdentity, false); + } catch (Exception e) { + LOG.warn("Can't create space {} news target", space.getPrettyName()); } } } diff --git a/content-webapp/src/main/webapp/WEB-INF/conf/configuration.xml b/content-webapp/src/main/webapp/WEB-INF/conf/configuration.xml index b19066d98..fb4a4bcd6 100644 --- a/content-webapp/src/main/webapp/WEB-INF/conf/configuration.xml +++ b/content-webapp/src/main/webapp/WEB-INF/conf/configuration.xml @@ -32,6 +32,5 @@ war:/conf/news/metadata-plugins-configuration.xml war:/conf/news/gamification-configuration.xml war:/conf/news/ckeditor-configuration.xml - war:/conf/news/feature-flags-configuration.xml diff --git a/content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml b/content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml deleted file mode 100644 index 224454f61..000000000 --- a/content-webapp/src/main/webapp/WEB-INF/conf/news/feature-flags-configuration.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - ContentFeatureProperties - org.exoplatform.container.ExtendedPropertyConfigurator - - - ContentFeatureProperties - Content Feature enablement flag - - - - - - - - org.exoplatform.groovyscript.text.TemplateService - - UIPortalApplication-head - addTemplateExtension - org.exoplatform.groovyscript.text.TemplateExtensionPlugin - - - templates - The list of templates to include in HTML Page Header with UIPortalApplication.gtmpl - - war:/groovy/webui/workspace/UIContentHeadTemplate.gtmpl - - - - - diff --git a/content-webapp/src/main/webapp/groovy/webui/workspace/UIContentHeadTemplate.gtmpl b/content-webapp/src/main/webapp/groovy/webui/workspace/UIContentHeadTemplate.gtmpl deleted file mode 100644 index 7f055fe04..000000000 --- a/content-webapp/src/main/webapp/groovy/webui/workspace/UIContentHeadTemplate.gtmpl +++ /dev/null @@ -1,10 +0,0 @@ -<% - import org.exoplatform.commons.api.settings.ExoFeatureService; - def rcontext = _ctx.getRequestContext(); - ExoFeatureService featureService = uicomponent.getApplicationComponent(ExoFeatureService.class); - def userName = rcontext.getRemoteUser(); -%> - - diff --git a/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue b/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue index 96c15b37c..27d306466 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details-app/components/ExoNewsDetailsApp.vue @@ -1,7 +1,7 @@ @@ -289,9 +167,6 @@ export default { this.$root.$on('update-news-body', this.setNewsContent); }, computed: { - articleNewLayoutEnabled() { - return eXo?.env?.portal?.articleNewLayoutEnabled; - }, showUpdaterInfo() { return !this.isPublicAccess; }, @@ -312,12 +187,6 @@ export default { newsTitle() { return this.news && this.newsTitleContent; }, - showUpdateInfo() { - return this.news && this.news.updateDate && this.news.updater !== '__system' && this.news.updateDate !== 'null' && this.news.publicationDate && this.news.publicationDate !== 'null' && new Date(this.news.updateDate).getTime() > new Date(this.news.publicationDate).getTime(); - }, - authorProfile() { - return this.news && this.news.author; - }, articleUpdater() { return this.news?.updater || this.news?.author; }, @@ -330,12 +199,6 @@ export default { updaterFullName() { return this.news && this.news.updaterFullName; }, - updaterProfileURL() { - return this.news && `${eXo.env.portal.context}/${eXo.env.portal.metaPortalName}/profile/${this.news.updater}`; - }, - newsUpdater() { - return this.news && this.news.updater; - }, publicationDate() { return this.news?.publicationDate && new Date(this.news.publicationDate); }, @@ -359,13 +222,7 @@ export default { }, publicationState() { return this.news && this.news.publicationState; - }, - notSameUpdater() { - return this.news && this.news.updater !== this.news.author; - }, - scheduleDate() { - return this.news && this.news.schedulePostDate; - }, + } }, methods: { setNewsTitle(title) { diff --git a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue index 4ef5596fe..aebad6c56 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue +++ b/content-webapp/src/main/webapp/vue-app/news-details/components/ExoNewsDetailsToolBar.vue @@ -22,14 +22,7 @@
- - - @@ -41,7 +34,6 @@ {{ $t("news.composer.btn.scheduleArticle") }} @@ -60,8 +52,7 @@ v-if="displayFavoriteButton" :news="news" :activity-id="activityId" - :class="[{'pull-right mt-1 me-2': articleNewLayoutEnabled}, - {'mt-6 pull-right': !articleNewLayoutEnabled}]" /> + class="pull-right mt-1 me-2" />
@@ -127,9 +118,6 @@ export default { }; }, computed: { - articleNewLayoutEnabled() { - return eXo?.env?.portal?.articleNewLayoutEnabled; - }, historyClearedBackUrl() { return this.news && this.news.spaceMember ? this.news.spaceUrl : `${eXo.env.portal.context}/${eXo.env.portal.metaPortalName}`; }, diff --git a/content-webapp/src/main/webapp/vue-app/news-details/components/mobile/ExoNewsDetailsBodyMobile.vue b/content-webapp/src/main/webapp/vue-app/news-details/components/mobile/ExoNewsDetailsBodyMobile.vue deleted file mode 100644 index 00d6342a6..000000000 --- a/content-webapp/src/main/webapp/vue-app/news-details/components/mobile/ExoNewsDetailsBodyMobile.vue +++ /dev/null @@ -1,128 +0,0 @@ - - - - diff --git a/content-webapp/src/main/webapp/vue-app/news-details/components/mobile/ExoNewsDetailsToolBarMobile.vue b/content-webapp/src/main/webapp/vue-app/news-details/components/mobile/ExoNewsDetailsToolBarMobile.vue deleted file mode 100644 index 4d2eb1327..000000000 --- a/content-webapp/src/main/webapp/vue-app/news-details/components/mobile/ExoNewsDetailsToolBarMobile.vue +++ /dev/null @@ -1,114 +0,0 @@ - - - diff --git a/content-webapp/src/main/webapp/vue-app/news-details/initComponents.js b/content-webapp/src/main/webapp/vue-app/news-details/initComponents.js index 3dafba3a5..b79a1768b 100644 --- a/content-webapp/src/main/webapp/vue-app/news-details/initComponents.js +++ b/content-webapp/src/main/webapp/vue-app/news-details/initComponents.js @@ -22,8 +22,6 @@ import ExoNewsDetails from './components/ExoNewsDetails.vue'; import ExoNewsDetailsActionMenuApp from '../news/components/ExoNewsDetailsActionMenuApp.vue'; import ExoNewsDetailsActivity from './components/ExoNewsDetailsActivity.vue'; import ExoNewsDetailsToolBar from './components/ExoNewsDetailsToolBar.vue'; -import ExoNewsDetailsToolBarMobile from './components/mobile/ExoNewsDetailsToolBarMobile.vue'; -import ExoNewsDetailsBodyMobile from './components/mobile/ExoNewsDetailsBodyMobile.vue'; import ExoNewsDetailsBody from './components/ExoNewsDetailsBody.vue'; import ExoNewsDetailsTime from './components/ExoNewsDetailsTime.vue'; import ExoNewsEditPublishingDrawer from './components/ExoNewsEditPublishingDrawer.vue'; @@ -36,8 +34,6 @@ const components = { 'exo-news-details-activity': ExoNewsDetailsActivity, 'exo-news-details-action-menu-app': ExoNewsDetailsActionMenuApp, 'exo-news-details-toolbar': ExoNewsDetailsToolBar, - 'exo-news-details-toolbar-mobile': ExoNewsDetailsToolBarMobile, - 'exo-news-details-body-mobile': ExoNewsDetailsBodyMobile, 'exo-news-details-body': ExoNewsDetailsBody, 'exo-news-details-time': ExoNewsDetailsTime, 'exo-news-edit-publishing-drawer': ExoNewsEditPublishingDrawer, @@ -62,4 +58,4 @@ if (!Vue.prototype.$newsConstants) { window.Object.defineProperty(Vue.prototype, '$newsConstants', { value: newsConstants, }); -} \ No newline at end of file +} diff --git a/content-webapp/src/main/webapp/vue-app/news/components/ExoNewsDetailsActionMenuApp.vue b/content-webapp/src/main/webapp/vue-app/news/components/ExoNewsDetailsActionMenuApp.vue index ea7dd4b27..f87c0d9f2 100644 --- a/content-webapp/src/main/webapp/vue-app/news/components/ExoNewsDetailsActionMenuApp.vue +++ b/content-webapp/src/main/webapp/vue-app/news/components/ExoNewsDetailsActionMenuApp.vue @@ -31,7 +31,7 @@ @@ -120,9 +120,6 @@ export default { computed: { isMobile() { return this.$vuetify.breakpoint.name === 'xs' || this.$vuetify.breakpoint.name === 'sm'; - }, - articleNewLayoutEnabled() { - return eXo?.env?.portal?.articleNewLayoutEnabled; } }, methods: { From 248369a2292d5bfe323c75062d8f6a1690156b7a Mon Sep 17 00:00:00 2001 From: Helmi Akermi <70575401+hakermi@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:57:04 +0100 Subject: [PATCH 14/14] fix: Fix image cropper styles in content editor - EXO-74717 - Meeds-io/MIPs#129 (#260) Fix image cropper styles in content editor --- content-webapp/src/main/webapp/WEB-INF/gatein-resources.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/content-webapp/src/main/webapp/WEB-INF/gatein-resources.xml b/content-webapp/src/main/webapp/WEB-INF/gatein-resources.xml index 64bba5cb3..cf57786b2 100644 --- a/content-webapp/src/main/webapp/WEB-INF/gatein-resources.xml +++ b/content-webapp/src/main/webapp/WEB-INF/gatein-resources.xml @@ -51,6 +51,7 @@ Enterprise /skin/css/newsComposer.css Notes + ImageCropper