diff --git a/webapps/src/main/java/io/meeds/task/portlet/TasksPortlet.java b/webapps/src/main/java/io/meeds/task/portlet/TasksPortlet.java new file mode 100644 index 000000000..1c2be82d3 --- /dev/null +++ b/webapps/src/main/java/io/meeds/task/portlet/TasksPortlet.java @@ -0,0 +1,77 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2026 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.task.portlet; + +import java.io.IOException; + +import javax.portlet.ActionRequest; +import javax.portlet.ActionResponse; +import javax.portlet.PortletException; +import javax.portlet.PortletMode; +import javax.portlet.PortletPreferences; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; + +import org.exoplatform.commons.api.portlet.GenericDispatchedViewPortlet; +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.portal.application.PortalRequestContext; +import org.exoplatform.portal.config.UserACL; +import org.exoplatform.services.security.ConversationState; + +public class TasksPortlet extends GenericDispatchedViewPortlet { + + private UserACL userAcl; + + @Override + public void processAction(ActionRequest request, ActionResponse response) throws IOException, PortletException { + if (!canModifySettings()) { + throw new PortletException("User is not allowed to save settings"); + } + PortletPreferences preferences = request.getPreferences(); + String settings = request.getParameter("settings"); + preferences.setValue("settings", settings); + preferences.store(); + response.setPortletMode(PortletMode.VIEW); + } + + @Override + protected void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { + request.setAttribute("canEdit", canModifySettings()); + super.doView(request, response); + } + + private boolean canModifySettings() { + PortalRequestContext requestContext = PortalRequestContext.getCurrentInstance(); + if (requestContext == null || requestContext.getPage() == null) { + return false; + } + return getUserAcl().hasEditPermission(requestContext.getPage(), + ConversationState.getCurrent().getIdentity()); + } + + private UserACL getUserAcl() { + if (userAcl == null) { + userAcl = ExoContainerContext.getService(UserACL.class); + } + return userAcl; + } + +} diff --git a/webapps/src/main/resources/locale/portlet/taskManagement_en.properties b/webapps/src/main/resources/locale/portlet/taskManagement_en.properties index 024b6df28..890bd5936 100644 --- a/webapps/src/main/resources/locale/portlet/taskManagement_en.properties +++ b/webapps/src/main/resources/locale/portlet/taskManagement_en.properties @@ -48,6 +48,8 @@ label.and=and label.projectManagers=Project Managers label.projectParticipants=Project Participant(s) tasks.label.allSet=All set! +label.seeAll=See all +label.editSettings=Edit settings task.placeholder=Share your thoughts here label.userOrGroup=User or Group @@ -446,3 +448,9 @@ alert.success.label.updated=Label successfully updated alert.error=An error occured alert.error.title.mandatory=Task title is mandatory alert.error.title.length=Task title length should be between 3 and 1024. + +tasks.settings.edit.drawer.title=Edit tasks list +tasks.settings.edit.drawer.updateSeeMore.label=Update see more +tasks.settings.edit.drawer.opensInSameTab=Opens in same tab +tasks.settings.save.success.message=Settings saved successfully +tasks.settings.save.error.message=Error while saving settings \ No newline at end of file diff --git a/webapps/src/main/webapp/WEB-INF/portlet.xml b/webapps/src/main/webapp/WEB-INF/portlet.xml index 1105f807e..020dbebaa 100644 --- a/webapps/src/main/webapp/WEB-INF/portlet.xml +++ b/webapps/src/main/webapp/WEB-INF/portlet.xml @@ -25,7 +25,7 @@ tasks - org.exoplatform.commons.api.portlet.GenericDispatchedViewPortlet + io.meeds.task.portlet.TasksPortlet portlet-view-dispatched-file-path /tasks.jsp @@ -41,6 +41,17 @@ Tasks + + + settings + + { + "seeAllUrl":null, + "sameTab":true + } + + + diff --git a/webapps/src/main/webapp/WEB-INF/tld/portlet_2_0.tld b/webapps/src/main/webapp/WEB-INF/tld/portlet_2_0.tld new file mode 100644 index 000000000..06237ee84 --- /dev/null +++ b/webapps/src/main/webapp/WEB-INF/tld/portlet_2_0.tld @@ -0,0 +1,188 @@ + + + + + Portlet 2.0 Tag Library + Portlet 2.0 Tags + 2.0 + portlet + http://java.sun.com/portlet_2_0 + + + + param + org.gatein.pc.portlet.impl.jsr286.taglib.URLParameter286Tag + empty + + + name + true + true + + + + value + true + true + + + + + + + property + org.gatein.pc.portlet.impl.jsr286.taglib.URLProperty286Tag + empty + + + name + true + true + + + + value + true + true + + + + + + + renderURL + org.gatein.pc.portlet.impl.jsr286.taglib.RenderURL286Tag + org.gatein.pc.portlet.impl.jsr286.taglib.GenerateURL286TagTEI + JSP + + + portletMode + true + + + + secure + true + + + + var + true + + + + windowState + true + + + + escapeXml + true + + + + copyCurrentRenderParameters + true + + + + + + + defineObjects + org.gatein.pc.portlet.impl.jsr286.taglib.DefineObjects286Tag + org.gatein.pc.portlet.impl.jsr286.taglib.DefineObjects286TagTEI + empty + + + + + actionURL + org.gatein.pc.portlet.impl.jsr286.taglib.ActionURL286Tag + org.gatein.pc.portlet.impl.jsr286.taglib.GenerateURL286TagTEI + JSP + + + portletMode + true + + + + secure + true + + + + var + true + + + + windowState + true + + + + escapeXml + true + + + + copyCurrentRenderParameters + true + + + + name + true + + + + + + + + resourceURL + org.gatein.pc.portlet.impl.jsr286.taglib.ResourceURL286Tag + org.gatein.pc.portlet.impl.jsr286.taglib.ResourceURL286TagTEI + JSP + + + secure + true + + + + var + true + + + + escapeXml + true + + + + cacheability + true + + + + id + true + + + + + + + namespace + org.gatein.pc.portlet.impl.jsr286.taglib.Namespace286Tag + empty + + + + \ No newline at end of file diff --git a/webapps/src/main/webapp/WEB-INF/web.xml b/webapps/src/main/webapp/WEB-INF/web.xml index 45bab5edb..f86327cbd 100644 --- a/webapps/src/main/webapp/WEB-INF/web.xml +++ b/webapps/src/main/webapp/WEB-INF/web.xml @@ -44,4 +44,10 @@ /images/* + + + http://java.sun.com/portlet_2_0 + /WEB-INF/tld/portlet_2_0.tld + + diff --git a/webapps/src/main/webapp/js/tasksService.js b/webapps/src/main/webapp/js/tasksService.js index 04776c74b..96209ebbc 100644 --- a/webapps/src/main/webapp/js/tasksService.js +++ b/webapps/src/main/webapp/js/tasksService.js @@ -122,3 +122,21 @@ export function updateCompleted(task) { body: JSON.stringify(task) }).then(resp => resp.json()).finally(() => document.dispatchEvent(new CustomEvent('hideTopBarLoading'))); } + +export function saveSettings(saveSettingsURL, settings) { + const formData = new FormData(); + formData.append('settings', JSON.stringify(settings)); + const urlParams = new URLSearchParams(formData).toString(); + return fetch(saveSettingsURL.replaceAll('&', '&'), { + method: 'POST', + credentials: 'include', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: urlParams, + }).then(resp => { + if (!resp.ok) { + throw new Error('Error while saving tasks settings'); + } + }); +} diff --git a/webapps/src/main/webapp/tasks.jsp b/webapps/src/main/webapp/tasks.jsp index 2c02bc571..700ef57b7 100644 --- a/webapps/src/main/webapp/tasks.jsp +++ b/webapps/src/main/webapp/tasks.jsp @@ -18,21 +18,35 @@ %> <%@ page import="java.util.ResourceBundle" %> <%@ page import="org.exoplatform.services.resources.ResourceBundleService" %> -<%@ page import="org.exoplatform.container.PortalContainer" %><% - - String itemsLimit = System.getProperty("exo.dw.page.snapshot.itemsLimit", "10"); +<%@ page import="org.exoplatform.container.PortalContainer" %> +<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %> + + +<% + String itemsLimit = System.getProperty("exo.dw.page.snapshot.itemsLimit", "10"); - PortalContainer portalContainer = PortalContainer.getCurrentInstance(session.getServletContext()); - ResourceBundleService resourceBundleService = portalContainer.getComponentInstanceOfType(ResourceBundleService.class); - ResourceBundle resourceBundle = resourceBundleService.getResourceBundle("locale.portlet.taskManagement", request.getLocale()); + PortalContainer portalContainer = PortalContainer.getCurrentInstance(session.getServletContext()); + ResourceBundleService resourceBundleService = portalContainer.getComponentInstanceOfType(ResourceBundleService.class); + ResourceBundle resourceBundle = resourceBundleService.getResourceBundle("locale.portlet.taskManagement", request.getLocale()); + String portletId = (String) request.getAttribute("portletStorageId"); + String domId = "tasksApplication" + portletId; + String valueDomId = "tasksSettingsValue" + portletId; + + boolean canEdit = (boolean) request.getAttribute("canEdit"); + Object settings = (String[]) request.getAttribute("settings"); + if (settings != null) { + settings = ((String[]) settings)[0]; + } + %>
-
- -
+
+ + +
\ No newline at end of file diff --git a/webapps/src/main/webapp/vue-app/tasks/components/TasksApp.vue b/webapps/src/main/webapp/vue-app/tasks/components/TasksApp.vue index 6fef9351e..9d60bb8b9 100644 --- a/webapps/src/main/webapp/vue-app/tasks/components/TasksApp.vue +++ b/webapps/src/main/webapp/vue-app/tasks/components/TasksApp.vue @@ -16,94 +16,141 @@ --> @@ -233,6 +280,18 @@ export default { }); }, methods: { + openSettingsDrawer() { + this.$refs.settingsDrawer.open(); + }, + settingsUpdated(settings) { + this.$root.settings.seeAllUrl = settings.seeAllUrl; + this.$root.settings.sameTab = settings.sameTab; + }, + openSeeAll() { + const target = this.$root?.settings?.sameTab ? '_self' : '_blank'; + const url = this.$root?.settings?.seeAllUrl; + window.open(url, target); + }, getMyOverDueTasks() { const task = { dueCategory: 'overDue', diff --git a/webapps/src/main/webapp/vue-app/tasks/components/settings/TasksSettingsDrawer.vue b/webapps/src/main/webapp/vue-app/tasks/components/settings/TasksSettingsDrawer.vue new file mode 100644 index 000000000..f39f47078 --- /dev/null +++ b/webapps/src/main/webapp/vue-app/tasks/components/settings/TasksSettingsDrawer.vue @@ -0,0 +1,131 @@ + + + \ No newline at end of file diff --git a/webapps/src/main/webapp/vue-app/tasks/initComponents.js b/webapps/src/main/webapp/vue-app/tasks/initComponents.js index 11db36e37..0e303647f 100644 --- a/webapps/src/main/webapp/vue-app/tasks/initComponents.js +++ b/webapps/src/main/webapp/vue-app/tasks/initComponents.js @@ -17,11 +17,13 @@ import TasksApp from './components/TasksApp.vue'; import TaskDetails from './components/TaskDetails.vue'; import TaskEmptyRow from './components/TaskEmptyRow.vue'; +import TasksSettingsDrawer from './components/settings/TasksSettingsDrawer.vue'; const components = { 'my-tasks-app': TasksApp, 'task-details': TaskDetails, 'task-empty-row': TaskEmptyRow, + 'tasks-settings-drawer': TasksSettingsDrawer }; for (const key in components) { diff --git a/webapps/src/main/webapp/vue-app/tasks/main.js b/webapps/src/main/webapp/vue-app/tasks/main.js index 6bb6819ef..676a7d983 100644 --- a/webapps/src/main/webapp/vue-app/tasks/main.js +++ b/webapps/src/main/webapp/vue-app/tasks/main.js @@ -21,16 +21,20 @@ const lang = window.eXo && eXo?.env?.portal?.language || 'en'; const url = `/task-management/i18n/locale.portlet.taskManagement?lang=${lang}`; -const appId = 'tasks'; - -export function init(itemsLimit) { -//getting locale ressources +export function init(appId, itemsLimit, settings, settingsSaveUrl, canEdit) { + //getting locale ressources exoi18n.loadLanguageAsync(lang, url) .then(i18n => { + if (!settings.seeAllUrl) { + settings.seeAllUrl = `/${eXo.env.portal.containerName}/${eXo.env.portal.metaPortalName}/tasks`; + } // init Vue app when locale ressources are ready Vue.createApp({ data: { - itemsLimit: itemsLimit + itemsLimit, + settings, + settingsSaveUrl, + canEdit }, template: ``, i18n,