From ad13ba1354a941ec5ecf965434cc10e48efdd124 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Wed, 20 Nov 2024 19:02:28 +0100 Subject: [PATCH 01/64] feat: Prepare Tobar and Sidebar Navigation Service - MEED-7776 - Meeds-io/MIPs#159 (#4196) This change will add Backend and Basic Frontend for Topbar and Sidebar configuration. --- .../navigation/constant/SidebarItemType.java | 23 ++ .../navigation/constant/SidebarMode.java | 23 ++ .../navigation/constant/TopbarItemType.java | 23 ++ .../model/NavigationConfiguration.java | 34 +++ .../model/SidebarConfiguration.java | 42 ++++ .../social/navigation/model/SidebarItem.java | 55 ++++ .../navigation/model/TopbarApplication.java | 50 ++++ .../navigation/model/TopbarConfiguration.java | 38 +++ .../navigation/plugin/SidebarPlugin.java | 46 ++++ component/core/pom.xml | 2 +- .../plugin/DefaultSidebarPlugin.java | 50 ++++ .../navigation/plugin/PageSidebarPlugin.java | 73 ++++++ .../navigation/plugin/SiteSidebarPlugin.java | 234 ++++++++++++++++++ .../plugin/SpaceTemplateSidebarPlugin.java | 93 +++++++ .../NavigationConfigurationInitService.java | 146 +++++++++++ .../NavigationConfigurationService.java | 106 ++++++++ .../NavigationConfigurationStorage.java | 132 ++++++++++ .../space/service/SpaceLayoutService.java | 3 + .../space/service/SpaceServiceImpl.java | 3 + .../service/SpaceTemplateService.java | 11 +- .../rest/NavigationConfigurationRest.java | 73 ++++++ .../social/rest/api/EntityBuilder.java | 2 +- .../portlet/GeneralSettings_en.properties | 34 +++ .../dynamic-container-configuration.xml | 112 ++++----- .../components/GeneralSettings.vue | 33 ++- .../navigation/NavigationSettings.vue | 84 +++++++ .../sidebar/NavigationSettingsSidebar.vue | 208 ++++++++++++++++ .../NavigationSettingsSidebarPreview.vue | 35 +++ .../topbar/NavigationSettingsTopbar.vue | 181 ++++++++++++++ .../NavigationSettingsTopbarPreview.vue | 92 +++++++ .../general-settings/initComponents.js | 13 + .../js/NavigationConfigurationService.js | 47 ++++ .../webapp/vue-apps/general-settings/main.js | 5 + .../vue-apps/general-settings/services.js | 4 + 34 files changed, 2048 insertions(+), 62 deletions(-) create mode 100644 component/api/src/main/java/io/meeds/social/navigation/constant/SidebarItemType.java create mode 100644 component/api/src/main/java/io/meeds/social/navigation/constant/SidebarMode.java create mode 100644 component/api/src/main/java/io/meeds/social/navigation/constant/TopbarItemType.java create mode 100644 component/api/src/main/java/io/meeds/social/navigation/model/NavigationConfiguration.java create mode 100644 component/api/src/main/java/io/meeds/social/navigation/model/SidebarConfiguration.java create mode 100644 component/api/src/main/java/io/meeds/social/navigation/model/SidebarItem.java create mode 100644 component/api/src/main/java/io/meeds/social/navigation/model/TopbarApplication.java create mode 100644 component/api/src/main/java/io/meeds/social/navigation/model/TopbarConfiguration.java create mode 100644 component/api/src/main/java/io/meeds/social/navigation/plugin/SidebarPlugin.java create mode 100644 component/core/src/main/java/io/meeds/social/navigation/plugin/DefaultSidebarPlugin.java create mode 100644 component/core/src/main/java/io/meeds/social/navigation/plugin/PageSidebarPlugin.java create mode 100644 component/core/src/main/java/io/meeds/social/navigation/plugin/SiteSidebarPlugin.java create mode 100644 component/core/src/main/java/io/meeds/social/navigation/plugin/SpaceTemplateSidebarPlugin.java create mode 100644 component/core/src/main/java/io/meeds/social/navigation/service/NavigationConfigurationInitService.java create mode 100644 component/core/src/main/java/io/meeds/social/navigation/service/NavigationConfigurationService.java create mode 100644 component/core/src/main/java/io/meeds/social/navigation/storage/NavigationConfigurationStorage.java create mode 100644 component/service/src/main/java/io/meeds/social/navigation/rest/NavigationConfigurationRest.java create mode 100644 webapp/src/main/webapp/vue-apps/general-settings/components/navigation/NavigationSettings.vue create mode 100644 webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebar.vue create mode 100644 webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebarPreview.vue create mode 100644 webapp/src/main/webapp/vue-apps/general-settings/components/navigation/topbar/NavigationSettingsTopbar.vue create mode 100644 webapp/src/main/webapp/vue-apps/general-settings/components/navigation/topbar/NavigationSettingsTopbarPreview.vue create mode 100644 webapp/src/main/webapp/vue-apps/general-settings/js/NavigationConfigurationService.js diff --git a/component/api/src/main/java/io/meeds/social/navigation/constant/SidebarItemType.java b/component/api/src/main/java/io/meeds/social/navigation/constant/SidebarItemType.java new file mode 100644 index 00000000000..8dd978bae42 --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/constant/SidebarItemType.java @@ -0,0 +1,23 @@ +/** + * 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.social.navigation.constant; + +public enum SidebarItemType { + PAGE, SITE, SEPARATOR, SPACES, SPACE_TEMPLATE; +} diff --git a/component/api/src/main/java/io/meeds/social/navigation/constant/SidebarMode.java b/component/api/src/main/java/io/meeds/social/navigation/constant/SidebarMode.java new file mode 100644 index 00000000000..48a871f61de --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/constant/SidebarMode.java @@ -0,0 +1,23 @@ +/** + * 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.social.navigation.constant; + +public enum SidebarMode { + HIDDEN, ICON, STICKY; +} diff --git a/component/api/src/main/java/io/meeds/social/navigation/constant/TopbarItemType.java b/component/api/src/main/java/io/meeds/social/navigation/constant/TopbarItemType.java new file mode 100644 index 00000000000..1cc58416ac0 --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/constant/TopbarItemType.java @@ -0,0 +1,23 @@ +/** + * 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.social.navigation.constant; + +public enum TopbarItemType { + APP, LINK; +} diff --git a/component/api/src/main/java/io/meeds/social/navigation/model/NavigationConfiguration.java b/component/api/src/main/java/io/meeds/social/navigation/model/NavigationConfiguration.java new file mode 100644 index 00000000000..afd3c66f8e7 --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/model/NavigationConfiguration.java @@ -0,0 +1,34 @@ +/** + * 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.social.navigation.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class NavigationConfiguration { + + private TopbarConfiguration topbar; + + private SidebarConfiguration sidebar; + +} diff --git a/component/api/src/main/java/io/meeds/social/navigation/model/SidebarConfiguration.java b/component/api/src/main/java/io/meeds/social/navigation/model/SidebarConfiguration.java new file mode 100644 index 00000000000..995e30c0bbb --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/model/SidebarConfiguration.java @@ -0,0 +1,42 @@ +/** + * 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.social.navigation.model; + +import java.util.List; + +import io.meeds.social.navigation.constant.SidebarMode; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SidebarConfiguration { + + private boolean allowUserCustomHome; + + private SidebarMode defaultMode; + + private List allowedModes; + + private List items; + +} diff --git a/component/api/src/main/java/io/meeds/social/navigation/model/SidebarItem.java b/component/api/src/main/java/io/meeds/social/navigation/model/SidebarItem.java new file mode 100644 index 00000000000..db0c5e9c3ce --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/model/SidebarItem.java @@ -0,0 +1,55 @@ +/** + * 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.social.navigation.model; + +import java.util.List; +import java.util.Map; + +import io.meeds.social.navigation.constant.SidebarItemType; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SidebarItem { + + private String name; + + private String url; + + private String target; + + private String avatar; + + private String icon; + + private SidebarItemType type; + + private List items; + + private Map properties; + + public SidebarItem(SidebarItemType type) { + this.type = type; + } + +} diff --git a/component/api/src/main/java/io/meeds/social/navigation/model/TopbarApplication.java b/component/api/src/main/java/io/meeds/social/navigation/model/TopbarApplication.java new file mode 100644 index 00000000000..caadaf38c5b --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/model/TopbarApplication.java @@ -0,0 +1,50 @@ +/** + * 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.social.navigation.model; + +import java.util.Map; + +import io.meeds.social.navigation.constant.TopbarItemType; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TopbarApplication { + + private String id; + + private String name; + + private String description; + + private String icon; + + private TopbarItemType type; + + private boolean enabled; + + private boolean mobile; + + private Map properties; + +} diff --git a/component/api/src/main/java/io/meeds/social/navigation/model/TopbarConfiguration.java b/component/api/src/main/java/io/meeds/social/navigation/model/TopbarConfiguration.java new file mode 100644 index 00000000000..f3e1a661c21 --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/model/TopbarConfiguration.java @@ -0,0 +1,38 @@ +/** + * 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.social.navigation.model; + +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TopbarConfiguration { + + private boolean displayCompanyName; + + private boolean displaySiteName; + + private List applications; + +} diff --git a/component/api/src/main/java/io/meeds/social/navigation/plugin/SidebarPlugin.java b/component/api/src/main/java/io/meeds/social/navigation/plugin/SidebarPlugin.java new file mode 100644 index 00000000000..3ffd4c0e27c --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/plugin/SidebarPlugin.java @@ -0,0 +1,46 @@ +/** + * 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.social.navigation.plugin; + +import java.util.List; +import java.util.Locale; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; + +/** + * A class to allow managing Sidebar item types + */ +public interface SidebarPlugin { + + SidebarItemType getType(); + + /** + * Resolves Item Name and Icon when storage is maintained + * + * @param item + * @param locale + */ + SidebarItem resolveProperties(SidebarItem item, Locale locale); + + List getDefaultItems(); + + boolean itemExists(SidebarItem item); + +} diff --git a/component/core/pom.xml b/component/core/pom.xml index 4159bcda106..d5fe5a2d07f 100644 --- a/component/core/pom.xml +++ b/component/core/pom.xml @@ -29,7 +29,7 @@ Meeds:: PLF:: Social Core Component Meeds Social Core Component: People and Space - 0.71 + 0.70 diff --git a/component/core/src/main/java/io/meeds/social/navigation/plugin/DefaultSidebarPlugin.java b/component/core/src/main/java/io/meeds/social/navigation/plugin/DefaultSidebarPlugin.java new file mode 100644 index 00000000000..42f90624ec2 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/DefaultSidebarPlugin.java @@ -0,0 +1,50 @@ +/** + * 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.social.navigation.plugin; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; + +public class DefaultSidebarPlugin implements SidebarPlugin { + + @Override + public SidebarItemType getType() { + return null; + } + + @Override + public List getDefaultItems() { + return Collections.emptyList(); + } + + @Override + public SidebarItem resolveProperties(SidebarItem item, Locale locale) { + return item; + } + + @Override + public boolean itemExists(SidebarItem item) { + return true; + } + +} diff --git a/component/core/src/main/java/io/meeds/social/navigation/plugin/PageSidebarPlugin.java b/component/core/src/main/java/io/meeds/social/navigation/plugin/PageSidebarPlugin.java new file mode 100644 index 00000000000..7df41581126 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/PageSidebarPlugin.java @@ -0,0 +1,73 @@ +/** + * 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.social.navigation.plugin; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import org.exoplatform.portal.mop.navigation.NodeData; +import org.exoplatform.portal.mop.service.NavigationService; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; + +@Component +@Order(20) +public class PageSidebarPlugin implements SidebarPlugin { + + private static final String NODE_ID_PROP_NAME = "navigationNodeId"; + + @Autowired + private NavigationService navigationService; + + @Override + public SidebarItemType getType() { + return SidebarItemType.PAGE; + } + + @Override + public SidebarItem resolveProperties(SidebarItem item, Locale locale) { + String nodeId = item.getProperties().get(NODE_ID_PROP_NAME); + NodeData node = navigationService.getNodeById(Long.parseLong(nodeId)); + if (node != null) { + item.setName(node.getName()); + if (node.getState() != null && node.getState().getIcon() != null) { + item.setIcon(node.getState().getIcon()); + } + } + return item; + } + + @Override + public List getDefaultItems() { + return Collections.emptyList(); + } + + @Override + public boolean itemExists(SidebarItem item) { + String nodeId = item.getProperties().get(NODE_ID_PROP_NAME); + return navigationService.getNodeById(Long.parseLong(nodeId)) != null; + } + +} diff --git a/component/core/src/main/java/io/meeds/social/navigation/plugin/SiteSidebarPlugin.java b/component/core/src/main/java/io/meeds/social/navigation/plugin/SiteSidebarPlugin.java new file mode 100644 index 00000000000..1fcf5aedf83 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/SiteSidebarPlugin.java @@ -0,0 +1,234 @@ +/** + * 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.social.navigation.plugin; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import org.exoplatform.commons.utils.ExpressionUtil; +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.portal.config.UserPortalConfigService; +import org.exoplatform.portal.config.model.PortalConfig; +import org.exoplatform.portal.mop.SiteFilter; +import org.exoplatform.portal.mop.SiteKey; +import org.exoplatform.portal.mop.SiteType; +import org.exoplatform.portal.mop.Visibility; +import org.exoplatform.portal.mop.navigation.NodeContext; +import org.exoplatform.portal.mop.navigation.NodeData; +import org.exoplatform.portal.mop.navigation.NodeState; +import org.exoplatform.portal.mop.service.LayoutService; +import org.exoplatform.portal.mop.service.NavigationService; +import org.exoplatform.services.organization.Group; +import org.exoplatform.services.organization.OrganizationService; +import org.exoplatform.services.resources.LocaleContextInfo; +import org.exoplatform.services.resources.ResourceBundleManager; +import org.exoplatform.services.resources.ResourceBundleService; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; +import io.meeds.social.translation.service.TranslationService; + +import lombok.SneakyThrows; + +@Component +@Order(10) +public class SiteSidebarPlugin implements SidebarPlugin { + + private static final String SITE_EXPAND_PAGES_PROP_NAME = "expandPages"; + + private static final String SITE_NAME_PROP_NAME = "siteName"; + + private static final String SITE_ID_PROP_NAME = "siteId"; + + private static final String SITE_TYPE_PROP_NAME = "siteType"; + + private static final String SITE_OBJECT_TYPE = "site"; + + private static final String SITE_LABEL_FIELD_NAME = "label"; + + @Autowired + private TranslationService translationService; + + @Autowired + private UserPortalConfigService userPortalConfigService; + + @Autowired + private LayoutService layoutService; + + @Autowired + private NavigationService navigationService; + + @Autowired + private ResourceBundleManager resourceBundleManager; + + @Override + public SidebarItemType getType() { + return SidebarItemType.SITE; + } + + @Override + public SidebarItem resolveProperties(SidebarItem item, Locale locale) { + String siteId = item.getProperties().get(SITE_ID_PROP_NAME); + String label = translationService.getTranslationLabelOrDefault(SITE_OBJECT_TYPE, + Long.parseLong(siteId), + SITE_LABEL_FIELD_NAME, + locale); + if (StringUtils.isBlank(label)) { + String siteType = item.getProperties().get(SITE_TYPE_PROP_NAME); + String siteName = item.getProperties().get(SITE_NAME_PROP_NAME); + item.setName(getSiteLabel(new SiteKey(siteType, siteName), locale)); + } else { + item.setName(label); + } + + NodeContext> rootNode = navigationService.loadNode(new SiteKey(item.getProperties() + .get(SITE_TYPE_PROP_NAME), + item.getProperties() + .get(SITE_NAME_PROP_NAME))); + if (rootNode != null && rootNode.getSize() > 0) { + NodeContext> firstNode = rootNode.get(0); + if (firstNode.getData() != null + && firstNode.getData().getState() != null + && firstNode.getData().getState().getIcon() != null) { + item.setIcon(firstNode.getData().getState().getIcon()); + } + } + return item; + } + + @Override + public List getDefaultItems() { + SiteFilter siteFilter = new SiteFilter(); + siteFilter.setDisplayed(true); + siteFilter.setSiteType(SiteType.PORTAL); + siteFilter.setExcludedSiteName(UserPortalConfigService.DEFAULT_GLOBAL_PORTAL); + siteFilter.setExcludeSpaceSites(true); + siteFilter.setSortByDisplayOrder(true); + siteFilter.setFilterByDisplayed(true); + List sites = layoutService.getSites(siteFilter); + return sites.stream() + .map(site -> toSidebarItem(SiteKey.portal(site.getName()))) + .toList(); + } + + @Override + public boolean itemExists(SidebarItem item) { + String siteType = item.getProperties().get(SITE_TYPE_PROP_NAME); + String siteName = item.getProperties().get(SITE_NAME_PROP_NAME); + return layoutService.getPortalConfig(siteType, siteName) != null; + } + + private SidebarItem toSidebarItem(SiteKey siteKey) { + return new SidebarItem(siteKey.getName(), + "/portal/" + siteKey.getName(), + null, + null, + getSiteIcon(siteKey), + SidebarItemType.SITE, + null, + buildSiteProperties(siteKey)); + } + + private String getSiteIcon(SiteKey siteKey) { + NodeContext> rootNode = navigationService.loadNode(siteKey); + if (rootNode != null && rootNode.getSize() > 0) { + Collection> nodes = rootNode.getNodes(); + return nodes.stream().map(node -> { + NodeData data = node.getData(); + NodeState state = data.getState(); + if ((state.getVisibility() == Visibility.DISPLAYED + || state.getVisibility() == Visibility.TEMPORAL) + && state.getPageRef() != null + && StringUtils.isNotBlank(state.getIcon())) { + return state.getIcon(); + } else { + return null; + } + }).filter(Objects::nonNull).findFirst().orElse(null); + } + return null; + } + + private Map buildSiteProperties(SiteKey siteKey) { + PortalConfig site = layoutService.getPortalConfig(siteKey); + long siteId = Long.parseLong((site.getStorageId().split("_"))[1]); + boolean isMetaSite = StringUtils.equals(userPortalConfigService.getMetaPortal(), siteKey.getName()); + + Map properties = new HashMap<>(); + properties.put(SITE_TYPE_PROP_NAME, siteKey.getTypeName()); + properties.put(SITE_NAME_PROP_NAME, siteKey.getName()); + properties.put(SITE_EXPAND_PAGES_PROP_NAME, String.valueOf(isMetaSite)); + properties.put(SITE_ID_PROP_NAME, String.valueOf(siteId)); + return properties; + } + + @SneakyThrows + private String getSiteLabel(SiteKey siteKey, Locale locale) { + PortalConfig site = layoutService.getPortalConfig(siteKey); + String label = site == null ? + siteKey.getName() : + StringUtils.firstNonBlank(site.getLabel(), + site.getName(), + siteKey.getName()); + if (siteKey.getType() == SiteType.PORTAL) { + return StringUtils.firstNonBlank(getLabel(siteKey, label, locale), + siteKey.getName()); + } else if (siteKey.getType() == SiteType.GROUP) { + Group siteGroup = ExoContainerContext.getService(OrganizationService.class) + .getGroupHandler() + .findGroupById(siteKey.getName()); + if (siteGroup != null) { + return siteGroup.getLabel(); + } + } + return label; + } + + private String getLabel(SiteKey siteKey, String label, Locale locale) { + if (ExpressionUtil.isResourceBindingExpression(label)) { + return Stream.of(locale, ResourceBundleService.DEFAULT_CROWDIN_LOCALE) + .map(l -> getBundle(siteKey.getTypeName(), siteKey.getName(), locale)) + .filter(Objects::nonNull) + .map(b -> ExpressionUtil.getExpressionValue(b, label)) + .filter(StringUtils::isNotBlank) + .findFirst() + .orElse(label); + } else { + return label; + } + } + + private ResourceBundle getBundle(String siteType, String siteName, Locale locale) { + return resourceBundleManager.getNavigationResourceBundle(LocaleContextInfo.getLocaleAsString(locale), + siteType, + siteName); + } + +} diff --git a/component/core/src/main/java/io/meeds/social/navigation/plugin/SpaceTemplateSidebarPlugin.java b/component/core/src/main/java/io/meeds/social/navigation/plugin/SpaceTemplateSidebarPlugin.java new file mode 100644 index 00000000000..ec83d0c66ac --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/SpaceTemplateSidebarPlugin.java @@ -0,0 +1,93 @@ +/** + * 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.social.navigation.plugin; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; +import io.meeds.social.space.template.model.SpaceTemplate; +import io.meeds.social.space.template.service.SpaceTemplateService; + +@Component +@Order(30) +public class SpaceTemplateSidebarPlugin implements SidebarPlugin { + + private static final String SPACE_TEMPLATE_ID_PROP_NAME = "spaceTemplateId"; + + @Autowired + private SpaceTemplateService spaceTemplateService; + + @Override + public SidebarItemType getType() { + return SidebarItemType.SPACE_TEMPLATE; + } + + @Override + public SidebarItem resolveProperties(SidebarItem item, Locale locale) { + String spaceTemplateId = item.getProperties().get(SPACE_TEMPLATE_ID_PROP_NAME); + SpaceTemplate spaceTemplate = spaceTemplateService.getSpaceTemplate(Long.parseLong(spaceTemplateId), locale, true); + if (spaceTemplate != null) { + item.setName(spaceTemplate.getName()); + item.setIcon(spaceTemplate.getIcon()); + } + return item; + } + + @Override + public List getDefaultItems() { + return spaceTemplateService.getSpaceTemplates(null, Pageable.unpaged(), true) + .stream() + .filter(t -> t.isEnabled() && !t.isDeleted()) + .map(this::toSidebarItem) + .toList(); + } + + @Override + public boolean itemExists(SidebarItem item) { + String spaceTemplateId = item.getProperties().get(SPACE_TEMPLATE_ID_PROP_NAME); + SpaceTemplate spaceTemplate = spaceTemplateService.getSpaceTemplate(Long.parseLong(spaceTemplateId)); + return spaceTemplate != null && spaceTemplate.isEnabled() && !spaceTemplate.isDeleted(); + } + + private SidebarItem toSidebarItem(SpaceTemplate spaceTemplate) { + return new SidebarItem(spaceTemplate.getName(), + null, + null, + null, + spaceTemplate.getIcon(), + SidebarItemType.SPACE_TEMPLATE, + null, + buildSpaceTemplateProperties(spaceTemplate)); + } + + private Map buildSpaceTemplateProperties(SpaceTemplate spaceTemplate) { + return Collections.singletonMap(SPACE_TEMPLATE_ID_PROP_NAME, + String.valueOf(spaceTemplate.getId())); + } + +} diff --git a/component/core/src/main/java/io/meeds/social/navigation/service/NavigationConfigurationInitService.java b/component/core/src/main/java/io/meeds/social/navigation/service/NavigationConfigurationInitService.java new file mode 100644 index 00000000000..7237cf5e54b --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/service/NavigationConfigurationInitService.java @@ -0,0 +1,146 @@ +/** + * 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.social.navigation.service; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import org.exoplatform.commons.addons.AddOnService; +import org.exoplatform.portal.config.model.TransientApplicationState; + +import io.meeds.common.ContainerTransactional; +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.constant.SidebarMode; +import io.meeds.social.navigation.constant.TopbarItemType; +import io.meeds.social.navigation.model.SidebarConfiguration; +import io.meeds.social.navigation.model.SidebarItem; +import io.meeds.social.navigation.model.NavigationConfiguration; +import io.meeds.social.navigation.model.TopbarApplication; +import io.meeds.social.navigation.model.TopbarConfiguration; +import io.meeds.social.navigation.plugin.SidebarPlugin; + +import jakarta.annotation.PostConstruct; + +/** + * A Service to inject Default Left Menu configuration on server startup + */ +@Component +public class NavigationConfigurationInitService { + + private static final String TOP_NAVIGATION_ADDON_CONTAINER = "middle-topNavigation-container"; + + private static final SidebarItem SIDEBAR_SEPARATOR = new SidebarItem(SidebarItemType.SEPARATOR); + + @Autowired + private NavigationConfigurationService navigationConfigurationService; + + @Autowired + private AddOnService addonContainerService; + + @Autowired + private List menuPlugins; + + @Value("${navigation.configuration.forceUpdate:false}") + private boolean forceUpdate; + + @Value("${navigation.configuration.displayCompanyName:true}") + private boolean displayCompanyName; + + @Value("${navigation.configuration.displaySiteName:true}") + private boolean displaySiteName; + + @Value("${navigation.configuration.allowUserCustomHome:true}") + private boolean allowUserCustomHome; + + @Value("${navigation.configuration.defaultMode:HIDDEN}") + private SidebarMode defaultMode; + + private List defaultTopbarApplications; + + @PostConstruct + @ContainerTransactional + public void init() { + navigationConfigurationService.setDefaultTopbarApplications(getDefaultTopbarApplications()); + if (forceUpdate || navigationConfigurationService.getConfiguration() == null) { + navigationConfigurationService.updateConfiguration(buildDefaultNavigationConfiguration()); + } + } + + private NavigationConfiguration buildDefaultNavigationConfiguration() { + TopbarConfiguration topbarConfiguration = new TopbarConfiguration(displayCompanyName, + displaySiteName, + getDefaultTopbarApplications()); + SidebarConfiguration sidebarConfiguration = new SidebarConfiguration(allowUserCustomHome, + defaultMode, + Arrays.asList(SidebarMode.values()), + getDefaultNavigations()); + return new NavigationConfiguration(topbarConfiguration, sidebarConfiguration); + } + + /** + * @return Default Topbar Applications as configured in {@link AddOnService} + */ + private List getDefaultTopbarApplications() { + if (defaultTopbarApplications == null) { + defaultTopbarApplications = addonContainerService.getApplications(TOP_NAVIGATION_ADDON_CONTAINER) + .stream() + .map(app -> new TopbarApplication(app.getId(), + app.getTitle(), + app.getDescription(), + app.getIcon(), + TopbarItemType.APP, + true, + true, + Collections.singletonMap("contentId", + ((TransientApplicationState) app.getState()).getContentId()))) + .toList(); + } + return defaultTopbarApplications; + } + + /** + * @return Default Sites Navigations + */ + private List getDefaultNavigations() { + if (menuPlugins != null) { + List list = menuPlugins.stream() + .map(SidebarPlugin::getDefaultItems) + .flatMap(items -> { + if (CollectionUtils.isEmpty(items)) { + return Stream.empty(); + } else { + return Stream.concat(items.stream(), Stream.of(SIDEBAR_SEPARATOR)); + } + }) + .toList(); + // Delete last separator + return list.isEmpty() ? list : list.stream().limit(list.size() - 1l).toList(); + } else { + return Collections.emptyList(); + } + } + +} diff --git a/component/core/src/main/java/io/meeds/social/navigation/service/NavigationConfigurationService.java b/component/core/src/main/java/io/meeds/social/navigation/service/NavigationConfigurationService.java new file mode 100644 index 00000000000..bf00f60bea9 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/service/NavigationConfigurationService.java @@ -0,0 +1,106 @@ +/** + * 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.social.navigation.service; + +import java.util.List; +import java.util.Locale; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; +import io.meeds.social.navigation.model.NavigationConfiguration; +import io.meeds.social.navigation.model.TopbarApplication; +import io.meeds.social.navigation.plugin.DefaultSidebarPlugin; +import io.meeds.social.navigation.plugin.SidebarPlugin; +import io.meeds.social.navigation.storage.NavigationConfigurationStorage; + +import lombok.Setter; +import lombok.SneakyThrows; + +/** + * A Service to manage Topbar and Sidebar configurations + */ +@Service +public class NavigationConfigurationService { + + private static final SidebarPlugin DEFAULT_MENU_PLUGIN = new DefaultSidebarPlugin(); + + @Autowired + private NavigationConfigurationStorage navigationConfigurationStorage; + + @Autowired + private List menuPlugins; + + @Setter + private List defaultTopbarApplications; + + /** + * @return {@link NavigationConfiguration} with the complete configuration of + * Navigation + */ + public NavigationConfiguration getConfiguration() { + return getConfiguration(null, false); + } + + /** + * @param resolve either resolve name and icon of elements or not + * @return {@link NavigationConfiguration} with the complete configuration of + * Navigation + */ + public NavigationConfiguration getConfiguration(Locale locale, boolean resolve) { + NavigationConfiguration configuration = navigationConfigurationStorage.getConfiguration(defaultTopbarApplications); + if (configuration == null) { + return null; + } else { + configuration.getSidebar() + .setItems(configuration.getSidebar() + .getItems() + .stream() + .filter(item -> getPlugin(item.getType()).itemExists(item)) + .map(item -> resolve ? expandSidebarItem(item, locale) : item) + .toList()); + return configuration; + } + } + + /** + * Updates the Navigation configuration + * + * @param navigationConfiguration + */ + public void updateConfiguration(NavigationConfiguration navigationConfiguration) { + navigationConfigurationStorage.updateConfiguration(navigationConfiguration); + } + + @SneakyThrows + private SidebarItem expandSidebarItem(SidebarItem item, Locale locale) { + return getPlugin(item.getType()).resolveProperties(item, locale); + } + + private SidebarPlugin getPlugin(SidebarItemType type) { + return menuPlugins == null ? DEFAULT_MENU_PLUGIN : + menuPlugins.stream() + .filter(p -> p.getType() == type) + .findFirst() + .orElse(DEFAULT_MENU_PLUGIN); + } + +} diff --git a/component/core/src/main/java/io/meeds/social/navigation/storage/NavigationConfigurationStorage.java b/component/core/src/main/java/io/meeds/social/navigation/storage/NavigationConfigurationStorage.java new file mode 100644 index 00000000000..3b5c82b6389 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/storage/NavigationConfigurationStorage.java @@ -0,0 +1,132 @@ +/** + * 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.social.navigation.storage; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import org.exoplatform.commons.api.settings.SettingService; +import org.exoplatform.commons.api.settings.SettingValue; +import org.exoplatform.commons.api.settings.data.Context; +import org.exoplatform.commons.api.settings.data.Scope; + +import io.meeds.social.navigation.constant.TopbarItemType; +import io.meeds.social.navigation.model.NavigationConfiguration; +import io.meeds.social.navigation.model.TopbarApplication; +import io.meeds.social.util.JsonUtils; + +@Component +public class NavigationConfigurationStorage { + + private static final String SETTING_KEY = "configuration"; + + private static final Scope SETTING_GLOBAL_SCOPE = Scope.GLOBAL.id("NavigationConfiguration"); + + private static final Context SETTING_GLOBAL_CONTEXT = Context.GLOBAL.id("NavigationConfiguration"); + + private static final NavigationConfiguration NULL_VALUE = new NavigationConfiguration(); + + @Autowired + private SettingService settingService; + + private NavigationConfiguration navigationConfiguration; + + public NavigationConfiguration getConfiguration(List defaultApplications) { + if (navigationConfiguration == null) { + navigationConfiguration = retrieveNavigationConfiguration(defaultApplications); + } + return navigationConfiguration == NULL_VALUE ? null : navigationConfiguration; + } + + public void updateConfiguration(NavigationConfiguration navigationConfiguration) { + try { + settingService.set(SETTING_GLOBAL_CONTEXT, + SETTING_GLOBAL_SCOPE, + SETTING_KEY, + SettingValue.create(JsonUtils.toJsonString(navigationConfiguration))); + } finally { + this.navigationConfiguration = null; + } + } + + private NavigationConfiguration retrieveNavigationConfiguration(List defaultApplications) { + SettingValue settingValue = settingService.get(SETTING_GLOBAL_CONTEXT, SETTING_GLOBAL_SCOPE, SETTING_KEY); + if (settingValue == null || settingValue.getValue() == null) { + return NULL_VALUE; + } else { + NavigationConfiguration configuration = JsonUtils.fromJsonString(settingValue.getValue().toString(), + NavigationConfiguration.class); + List applications = configuration.getTopbar().getApplications(); + addMissingTopbarApplication(configuration, applications, defaultApplications); + removeDroppedApplications(configuration, applications, defaultApplications); + return configuration; + } + } + + /** + * Remove applications which aren't available in addon container anymore + * + * @param configuration + * @param applications + * @param addonContainerApplications + */ + private void removeDroppedApplications(NavigationConfiguration configuration, + List applications, + List addonContainerApplications) { + List applicationsToRemove = applications.stream() + .filter(app -> app.getType() == TopbarItemType.APP + && addonContainerApplications.stream() + .noneMatch(containerApp -> StringUtils.equals(containerApp.getId(), + app.getId()))) + .toList(); + if (CollectionUtils.isNotEmpty(applicationsToRemove)) { + List mergedApplications = new ArrayList<>(applications); + mergedApplications.removeAll(applicationsToRemove); + configuration.getTopbar().setApplications(mergedApplications); + } + } + + /** + * Add applications which are newly made available in addon container + * + * @param configuration + * @param applications + * @param addonContainerApplications + */ + private void addMissingTopbarApplication(NavigationConfiguration configuration, + List applications, + List addonContainerApplications) { + List applicationsToAdd = addonContainerApplications.stream() + .filter(app -> applications.stream() + .noneMatch(containerApp -> StringUtils.equals(containerApp.getId(), + app.getId()))) + .toList(); + if (CollectionUtils.isNotEmpty(applicationsToAdd)) { + List mergedApplications = new ArrayList<>(applications); + mergedApplications.addAll(applicationsToAdd); + configuration.getTopbar().setApplications(mergedApplications); + } + } + +} diff --git a/component/core/src/main/java/io/meeds/social/space/service/SpaceLayoutService.java b/component/core/src/main/java/io/meeds/social/space/service/SpaceLayoutService.java index 5f87113bb74..003a40b0134 100644 --- a/component/core/src/main/java/io/meeds/social/space/service/SpaceLayoutService.java +++ b/component/core/src/main/java/io/meeds/social/space/service/SpaceLayoutService.java @@ -78,6 +78,9 @@ public SpaceLayoutService(SpaceService spaceService, */ public void createSpaceSite(Space space) throws ObjectNotFoundException { SpaceTemplate spaceTemplate = spaceTemplateService.getSpaceTemplate(space.getTemplateId()); + if (spaceTemplate == null || !spaceTemplate.isEnabled() || spaceTemplate.isDeleted()) { + throw new ObjectNotFoundException(String.format("Enabled Space Template with id %s not found", space.getTemplateId())); + } portalConfigService.createSiteFromTemplate(SiteKey.groupTemplate(StringUtils.firstNonBlank(spaceTemplate.getLayout(), DEFAULT_SITE_TEMPLATE)), SiteKey.group(space.getGroupId()), diff --git a/component/core/src/main/java/io/meeds/social/space/service/SpaceServiceImpl.java b/component/core/src/main/java/io/meeds/social/space/service/SpaceServiceImpl.java index 633d4b30f2d..617f5645fe8 100644 --- a/component/core/src/main/java/io/meeds/social/space/service/SpaceServiceImpl.java +++ b/component/core/src/main/java/io/meeds/social/space/service/SpaceServiceImpl.java @@ -395,6 +395,9 @@ public Space createSpace(Space space, String username, List identities spaceToCreate.setManagers(new String[] { username }); SpaceTemplate spaceTemplate = getSpaceTemplateService().getSpaceTemplate(spaceToCreate.getTemplateId()); + if (spaceTemplate == null || !spaceTemplate.isEnabled() || spaceTemplate.isDeleted()) { + throw new SpaceException(Code.UNKNOWN_SPACE_TEMPLATE); + } copySpaceTemplateProperties(spaceToCreate, spaceTemplate, username, getUsersToInvite(identitiesToInvite)); spaceLifeCycle.setCurrentEvent(Type.SPACE_CREATED); diff --git a/component/core/src/main/java/io/meeds/social/space/template/service/SpaceTemplateService.java b/component/core/src/main/java/io/meeds/social/space/template/service/SpaceTemplateService.java index 8a79f79ab64..26d49dce619 100644 --- a/component/core/src/main/java/io/meeds/social/space/template/service/SpaceTemplateService.java +++ b/component/core/src/main/java/io/meeds/social/space/template/service/SpaceTemplateService.java @@ -135,7 +135,7 @@ public List getManagingSpaceTemplates(String username) { } public SpaceTemplate getSpaceTemplate(long templateId) { - return spaceTemplateStorage.getSpaceTemplate(templateId); + return getSpaceTemplate(templateId, null, false); } public SpaceTemplate getSpaceTemplate(long templateId, @@ -149,7 +149,12 @@ public SpaceTemplate getSpaceTemplate(long templateId, if (!canViewTemplate(spaceTemplate, username)) { throw new IllegalAccessException(); } - if (expand) { + return getSpaceTemplate(templateId, locale, expand); + } + + public SpaceTemplate getSpaceTemplate(long templateId, Locale locale, boolean expand) { + SpaceTemplate spaceTemplate = spaceTemplateStorage.getSpaceTemplate(templateId); + if (expand && spaceTemplate != null && locale != null) { computeSpaceTemplateAttributes(spaceTemplate, locale); } return spaceTemplate; @@ -192,7 +197,7 @@ public boolean canViewTemplate(long templateId, String username) { public boolean canCreateSpace(long templateId, String username) { SpaceTemplate spaceTemplate = getSpaceTemplate(templateId); - return canViewTemplate(spaceTemplate, username) && spaceTemplate.isEnabled(); + return spaceTemplate != null && !spaceTemplate.isDeleted() && spaceTemplate.isEnabled() && canViewTemplate(spaceTemplate, username); } public boolean canCreateSpace(String username) { diff --git a/component/service/src/main/java/io/meeds/social/navigation/rest/NavigationConfigurationRest.java b/component/service/src/main/java/io/meeds/social/navigation/rest/NavigationConfigurationRest.java new file mode 100644 index 00000000000..d982a668d1b --- /dev/null +++ b/component/service/src/main/java/io/meeds/social/navigation/rest/NavigationConfigurationRest.java @@ -0,0 +1,73 @@ +/** + * 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.social.navigation.rest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import io.meeds.social.navigation.model.NavigationConfiguration; +import io.meeds.social.navigation.service.NavigationConfigurationService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; + +@RestController +@RequestMapping("/navigation/settings") +@Tag(name = "/social/rest/navigation/settings", description = "Managing Topbar and Sidebar Navigation Configuration") +public class NavigationConfigurationRest { + + @Autowired + private NavigationConfigurationService navigationConfigurationService; + + @GetMapping + @Secured("administrators") + @Operation(summary = "Retrieve Topbar and Sidebar settings", + method = "GET", + description = "This retrieves the complete configuration of Topbar and Sidebar") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Request fulfilled"), + }) + public NavigationConfiguration getNavigationConfiguration(HttpServletRequest request) { + return navigationConfigurationService.getConfiguration(request.getLocale(), true); + } + + @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + @Secured("administrators") + @Operation(summary = "Updates Topbar and Sidebar settings", + method = "PUT", + description = "This updates the Topbar and Sidebar Menu settings") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Request fulfilled"), + }) + public void updateNavigationConfiguration(HttpServletRequest request, + @RequestBody + NavigationConfiguration navigationConfiguration) { + navigationConfigurationService.updateConfiguration(navigationConfiguration); + } + +} diff --git a/component/service/src/main/java/org/exoplatform/social/rest/api/EntityBuilder.java b/component/service/src/main/java/org/exoplatform/social/rest/api/EntityBuilder.java index 59969c6cee3..8e17be2e222 100644 --- a/component/service/src/main/java/org/exoplatform/social/rest/api/EntityBuilder.java +++ b/component/service/src/main/java/org/exoplatform/social/rest/api/EntityBuilder.java @@ -2269,7 +2269,7 @@ private static String getSiteLabel(SiteKey siteKey, UserPortal userPortal) { private static UserPortalConfig getUserPortalConfig(HttpServletRequest request, PortalConfig portalConfig, - SiteType siteType) throws Exception { + SiteType siteType) { String portalName = siteType == SiteType.PORTAL ? portalConfig.getName() : getUserPortalConfigService().getMetaPortal(); return getUserPortalConfigService().getUserPortalConfig(portalName, request.getRemoteUser()); diff --git a/webapp/src/main/resources/locale/portlet/GeneralSettings_en.properties b/webapp/src/main/resources/locale/portlet/GeneralSettings_en.properties index f5e8bb3bfab..fe20c8bd87b 100644 --- a/webapp/src/main/resources/locale/portlet/GeneralSettings_en.properties +++ b/webapp/src/main/resources/locale/portlet/GeneralSettings_en.properties @@ -172,3 +172,37 @@ generalSettings.subtitle.manageDefaultLanguage=Select the default platform langu generalSettings.manageDefaultLanguage.drawer.title=Default Language generalSettings.defaultLanguageSettingSaved=Default language saved successfully. generalSettings.defaultLanguageSettingError=Error while saving default language setting. Please contact the support services or try again later. + +generalSettings.navigationCharacteristics=Topbar & Sidebar +generalSettings.subtitle.navigationCharacteristics=Customize the navigation experience +generalSettings.topbar=Topbar +generalSettings.brandingInfos=Branding Infos +generalSettings.displayCompanyName=Company Name +generalSettings.displaySiteName=Site Name +generalSettings.topbarOptions=Topbar Options +generalSettings.header.topbarApplicationName=Name +generalSettings.header.topbarApplicationDescription=Description +generalSettings.header.topbarApplicationMove=Move +generalSettings.header.topbarApplicationStatus=Status +generalSettings.header.topbarApplicationMobile=Mobile +generalSettings.search.name=Search +generalSettings.search.description=Find any content from your communities +generalSettings.notifications.name=Notification +generalSettings.notifications.description=Retrieve notifications +generalSettings.favorite.name=Favorite +generalSettings.favorite.description=List your favorite contents +generalSettings.settings.name=Settings +generalSettings.settings.description=Administration Site Link +generalSettings.sidebar=Sidebar +generalSettings.allowUserCustomHome=Users can set their personal home +generalSettings.sidebarAllowedModes=Display options +generalSettings.sidebarDefaultMode=Default Display +generalSettings.sidebar.mode.HIDDEN=Hide +generalSettings.sidebar.mode.ICON=Icon view +generalSettings.sidebar.mode.STICKY=Keep Menu Open +generalSettings.sidebarItemsOrganization=Items Organization +generalSettings.header.sidebarItem=Item +generalSettings.header.sidebarMove=Move +generalSettings.header.sidebarActions=Action +generalSettings.sidebarSeparator=Separator +generalSettings.navigationSettingsUpdatedSuccessfully=Navigation Setttings updated successfully diff --git a/webapp/src/main/webapp/WEB-INF/conf/social-extension/portal/dynamic-container-configuration.xml b/webapp/src/main/webapp/WEB-INF/conf/social-extension/portal/dynamic-container-configuration.xml index 61bdd3c1db9..9d0d32ca462 100644 --- a/webapp/src/main/webapp/WEB-INF/conf/social-extension/portal/dynamic-container-configuration.xml +++ b/webapp/src/main/webapp/WEB-INF/conf/social-extension/portal/dynamic-container-configuration.xml @@ -61,14 +61,14 @@ containerName - SearchPortlet + top-bar-logo-container - SearchPortlet + TopBarLogo - social/Search + social/TopBarLogo @@ -81,7 +81,7 @@ - Search + Top bar logo false @@ -104,18 +104,30 @@ priority - 20 + 1 containerName - top-bar-logo-container + middle-topNavigation-container - TopBarLogo + NotificationTopBar + + topbarSearch + + + generalSettings.search.name + + + generalSettings.search.description + + + fa-search + - social/TopBarLogo + social/Search @@ -127,18 +139,6 @@ - - Top bar logo - - - false - - - false - - - false - @@ -161,6 +161,18 @@ NotificationTopBar + + topbarNotifications + + + generalSettings.notifications.name + + + generalSettings.notifications.description + + + fa-bell + social/TopBarNotification @@ -174,18 +186,6 @@ - - Notification Top Bar - - - false - - - false - - - false - @@ -208,6 +208,18 @@ FavoritesTopBar + + topbarFavorite + + + generalSettings.favorite.name + + + generalSettings.favorite.description + + + fa-star + social/TopBarFavorites @@ -221,18 +233,6 @@ - - Favorites Top Bar - - - false - - - false - - - false - @@ -390,6 +390,18 @@ PlatformSettings + + topbarAdministrationLink + + + generalSettings.settings.name + + + generalSettings.settings.description + + + fa-cog + social/PlatformSettings @@ -400,18 +412,6 @@ - - Platform Settings - - - false - - - false - - - false - diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/GeneralSettings.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/GeneralSettings.vue index 22ab544d390..fe96a19cef9 100644 --- a/webapp/src/main/webapp/vue-apps/general-settings/components/GeneralSettings.vue +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/GeneralSettings.vue @@ -58,6 +58,9 @@ + @@ -81,11 +84,17 @@ @close="close" /> +
@@ -104,6 +113,23 @@ + + + + {{ $t('generalSettings.navigationCharacteristics') }} + + + {{ $t('generalSettings.subtitle.navigationCharacteristics') }} + + + + + fa-caret-right + + + @@ -201,6 +227,9 @@ export default { changed: false, }), watch: { + branding() { + this.$root.branding = this.branding; + }, errorMessage() { if (this.errorMessage) { this.$root.$emit('alert-message', this.$t(this.errorMessage), 'error'); @@ -214,6 +243,8 @@ export default { this.$root.selectedTab = 'access'; } else if (window.location.hash === '#display') { this.$root.selectedTab = 'branding'; + } else if (window.location.hash === '#navigation') { + this.$root.selectedTab = 'navigation'; } else if (window.location.hash === '#logincustomization') { this.$root.selectedTab = 'login'; } diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/NavigationSettings.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/NavigationSettings.vue new file mode 100644 index 00000000000..f4b809cbc26 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/NavigationSettings.vue @@ -0,0 +1,84 @@ + + + diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebar.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebar.vue new file mode 100644 index 00000000000..6c9e60c8a51 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebar.vue @@ -0,0 +1,208 @@ + + + \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebarPreview.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebarPreview.vue new file mode 100644 index 00000000000..c2fd5b03971 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebarPreview.vue @@ -0,0 +1,35 @@ + + + \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/topbar/NavigationSettingsTopbar.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/topbar/NavigationSettingsTopbar.vue new file mode 100644 index 00000000000..b05ecd10a07 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/topbar/NavigationSettingsTopbar.vue @@ -0,0 +1,181 @@ + + + \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/topbar/NavigationSettingsTopbarPreview.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/topbar/NavigationSettingsTopbarPreview.vue new file mode 100644 index 00000000000..78a1c2e013b --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/topbar/NavigationSettingsTopbarPreview.vue @@ -0,0 +1,92 @@ + + + \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/general-settings/initComponents.js b/webapp/src/main/webapp/vue-apps/general-settings/initComponents.js index e8c335f1664..59b89f4dec6 100644 --- a/webapp/src/main/webapp/vue-apps/general-settings/initComponents.js +++ b/webapp/src/main/webapp/vue-apps/general-settings/initComponents.js @@ -39,6 +39,14 @@ import DefaultSpacesDrawer from './components/registration/DefaultSpacesDrawer.v import PublicSiteEditDrawer from './components/public-site/PublicSiteEditDrawer.vue'; import DefaultLanguageDrawer from './components/language/DefaultLanguageDrawer.vue'; +import NavigationSettings from './components/navigation/NavigationSettings.vue'; + +import NavigationSettingsTopbar from './components/navigation/topbar/NavigationSettingsTopbar.vue'; +import NavigationSettingsTopbarPreview from './components/navigation/topbar/NavigationSettingsTopbarPreview.vue'; + +import NavigationSettingsSidebar from './components/navigation/sidebar/NavigationSettingsSidebar.vue'; +import NavigationSettingsSidebarPreview from './components/navigation/sidebar/NavigationSettingsSidebarPreview.vue'; + const components = { 'portal-general-settings': GeneralSettings, 'portal-general-settings-branding-site-window': SiteBrandingWindow, @@ -57,6 +65,11 @@ const components = { 'portal-general-settings-default-spaces-drawer': DefaultSpacesDrawer, 'portal-general-settings-public-site-drawer': PublicSiteEditDrawer, 'portal-general-settings-default-language-drawer': DefaultLanguageDrawer, + 'portal-general-settings-navigation-settings': NavigationSettings, + 'portal-general-settings-navigation-settings-topbar': NavigationSettingsTopbar, + 'portal-general-settings-navigation-settings-topbar-preview': NavigationSettingsTopbarPreview, + 'portal-general-settings-navigation-settings-sidebar': NavigationSettingsSidebar, + 'portal-general-settings-navigation-settings-sidebar-preview': NavigationSettingsSidebarPreview, }; for (const key in components) { diff --git a/webapp/src/main/webapp/vue-apps/general-settings/js/NavigationConfigurationService.js b/webapp/src/main/webapp/vue-apps/general-settings/js/NavigationConfigurationService.js new file mode 100644 index 00000000000..041dfbfb8b9 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/js/NavigationConfigurationService.js @@ -0,0 +1,47 @@ +/** + * 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. + */ + +export function getConfiguration() { + return fetch('/social/rest/navigation/settings', { + method: 'GET', + credentials: 'include', + }).then((resp) => { + if (resp?.ok) { + return resp.json(); + } else { + throw new Error('Error when retrieving configuration'); + } + }); +} + +export function saveConfiguration(configuration) { + return fetch('/social/rest/navigation/settings', { + headers: { + 'Content-Type': 'application/json' + }, + method: 'PUT', + credentials: 'include', + body: JSON.stringify(configuration), + }).then((resp) => { + if (!resp?.ok) { + throw new Error('Error when saving configuration'); + } + }); +} diff --git a/webapp/src/main/webapp/vue-apps/general-settings/main.js b/webapp/src/main/webapp/vue-apps/general-settings/main.js index 175f5c654ec..2879f9b04f3 100644 --- a/webapp/src/main/webapp/vue-apps/general-settings/main.js +++ b/webapp/src/main/webapp/vue-apps/general-settings/main.js @@ -43,6 +43,7 @@ export function init(publicSiteVisible, publicSiteId) { .then(i18n => Vue.createApp({ data: { + branding: null, selectedTab: null, loading: false, publicSiteVisible, @@ -70,6 +71,10 @@ export function init(publicSiteVisible, publicSiteId) { if (window.location.hash !== '#display') { window.location.hash = '#display'; } + } else if (this.selectedTab === 'navigation') { + if (window.location.hash !== '#navigation') { + window.location.hash = '#navigation'; + } } else if (this.selectedTab === 'login') { if (window.location.hash !== '#logincustomization') { window.location.hash = '#logincustomization'; diff --git a/webapp/src/main/webapp/vue-apps/general-settings/services.js b/webapp/src/main/webapp/vue-apps/general-settings/services.js index 55541448468..bb67b2ed68e 100644 --- a/webapp/src/main/webapp/vue-apps/general-settings/services.js +++ b/webapp/src/main/webapp/vue-apps/general-settings/services.js @@ -19,6 +19,7 @@ import * as registrationService from './js/RegistrationService.js'; import * as languageSettingService from './js/LanguageSettingService.js'; +import * as navigationConfigurationService from './js/NavigationConfigurationService.js'; window.Object.defineProperty(Vue.prototype, '$registrationService', { value: registrationService, @@ -26,3 +27,6 @@ window.Object.defineProperty(Vue.prototype, '$registrationService', { window.Object.defineProperty(Vue.prototype, '$languageSettingService', { value: languageSettingService, }); +window.Object.defineProperty(Vue.prototype, '$navigationConfigurationService', { + value: navigationConfigurationService, +}); From 823ec972a54a4d242d9dc0bf7f994510d107d419 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Thu, 21 Nov 2024 05:03:40 +0100 Subject: [PATCH 02/64] feat: Allow to configure Sidebar and Topbar in Mobile - MEED-7777 - Meeds-io/MIPs#159 --- .../model/NavigationConfiguration.java | 8 +++++- .../model/SidebarConfiguration.java | 13 ++++++++-- .../navigation/model/TopbarConfiguration.java | 10 +++++++- .../NavigationConfigurationStorage.java | 2 +- .../navigation/NavigationSettings.vue | 22 +++++++++++++--- .../sidebar/NavigationSettingsSidebar.vue | 3 ++- .../topbar/NavigationSettingsTopbar.vue | 25 ++++++++++++++++--- .../NavigationSettingsTopbarPreview.vue | 14 ++++++++--- 8 files changed, 80 insertions(+), 17 deletions(-) diff --git a/component/api/src/main/java/io/meeds/social/navigation/model/NavigationConfiguration.java b/component/api/src/main/java/io/meeds/social/navigation/model/NavigationConfiguration.java index afd3c66f8e7..282ed253eb0 100644 --- a/component/api/src/main/java/io/meeds/social/navigation/model/NavigationConfiguration.java +++ b/component/api/src/main/java/io/meeds/social/navigation/model/NavigationConfiguration.java @@ -25,10 +25,16 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class NavigationConfiguration { +public class NavigationConfiguration implements Cloneable { private TopbarConfiguration topbar; private SidebarConfiguration sidebar; + @Override + public NavigationConfiguration clone() { // NOSONAR + return new NavigationConfiguration(topbar == null ? null : topbar.clone(), + sidebar == null ? null : sidebar.clone()); + } + } diff --git a/component/api/src/main/java/io/meeds/social/navigation/model/SidebarConfiguration.java b/component/api/src/main/java/io/meeds/social/navigation/model/SidebarConfiguration.java index 995e30c0bbb..3e7b6a29127 100644 --- a/component/api/src/main/java/io/meeds/social/navigation/model/SidebarConfiguration.java +++ b/component/api/src/main/java/io/meeds/social/navigation/model/SidebarConfiguration.java @@ -18,6 +18,7 @@ */ package io.meeds.social.navigation.model; +import java.util.ArrayList; import java.util.List; import io.meeds.social.navigation.constant.SidebarMode; @@ -29,9 +30,9 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class SidebarConfiguration { +public class SidebarConfiguration implements Cloneable { - private boolean allowUserCustomHome; + private boolean allowUserCustomHome; private SidebarMode defaultMode; @@ -39,4 +40,12 @@ public class SidebarConfiguration { private List items; + @Override + public SidebarConfiguration clone() { // NOSONAR + return new SidebarConfiguration(allowUserCustomHome, + defaultMode, + new ArrayList<>(allowedModes), + new ArrayList<>(items)); + } + } diff --git a/component/api/src/main/java/io/meeds/social/navigation/model/TopbarConfiguration.java b/component/api/src/main/java/io/meeds/social/navigation/model/TopbarConfiguration.java index f3e1a661c21..e7a97ceab99 100644 --- a/component/api/src/main/java/io/meeds/social/navigation/model/TopbarConfiguration.java +++ b/component/api/src/main/java/io/meeds/social/navigation/model/TopbarConfiguration.java @@ -18,6 +18,7 @@ */ package io.meeds.social.navigation.model; +import java.util.ArrayList; import java.util.List; import lombok.AllArgsConstructor; @@ -27,7 +28,7 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class TopbarConfiguration { +public class TopbarConfiguration implements Cloneable { private boolean displayCompanyName; @@ -35,4 +36,11 @@ public class TopbarConfiguration { private List applications; + @Override + public TopbarConfiguration clone() { // NOSONAR + return new TopbarConfiguration(displayCompanyName, + displaySiteName, + new ArrayList<>(applications)); + } + } diff --git a/component/core/src/main/java/io/meeds/social/navigation/storage/NavigationConfigurationStorage.java b/component/core/src/main/java/io/meeds/social/navigation/storage/NavigationConfigurationStorage.java index 3b5c82b6389..9ddbd8ea05e 100644 --- a/component/core/src/main/java/io/meeds/social/navigation/storage/NavigationConfigurationStorage.java +++ b/component/core/src/main/java/io/meeds/social/navigation/storage/NavigationConfigurationStorage.java @@ -56,7 +56,7 @@ public NavigationConfiguration getConfiguration(List defaultA if (navigationConfiguration == null) { navigationConfiguration = retrieveNavigationConfiguration(defaultApplications); } - return navigationConfiguration == NULL_VALUE ? null : navigationConfiguration; + return navigationConfiguration == NULL_VALUE ? null : navigationConfiguration.clone(); } public void updateConfiguration(NavigationConfiguration navigationConfiguration) { diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/NavigationSettings.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/NavigationSettings.vue index f4b809cbc26..4eceff36067 100644 --- a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/NavigationSettings.vue +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/NavigationSettings.vue @@ -20,7 +20,7 @@ -->