diff --git a/webapp/src/main/webapp/vue-apps/spaces-list/services.js b/component/api/src/main/java/io/meeds/social/navigation/constant/SidebarItemType.java similarity index 63% rename from webapp/src/main/webapp/vue-apps/spaces-list/services.js rename to component/api/src/main/java/io/meeds/social/navigation/constant/SidebarItemType.java index 11b9b979fd7..64ab41c1d67 100644 --- a/webapp/src/main/webapp/vue-apps/spaces-list/services.js +++ b/component/api/src/main/java/io/meeds/social/navigation/constant/SidebarItemType.java @@ -1,26 +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 + * 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. + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +package io.meeds.social.navigation.constant; -import * as spaceTemplateService from '../space-templates-management/js/SpaceTemplateService.js'; - -if (!Vue.prototype.$spaceTemplateService) { - window.Object.defineProperty(Vue.prototype, '$spaceTemplateService', { - value: spaceTemplateService, - }); +public enum SidebarItemType { + PAGE, SITE, SEPARATOR, SPACES, SPACE_TEMPLATE, SPACE, LINK; } 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..e38d546e6df --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/model/NavigationConfiguration.java @@ -0,0 +1,57 @@ +/** + * 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.Data; + +@Data +public class NavigationConfiguration implements Cloneable { + + private TopbarConfiguration topbar; + + private SidebarConfiguration sidebar; + + private final long lastModified; + + public NavigationConfiguration() { + this.lastModified = System.currentTimeMillis(); + } + + public NavigationConfiguration(TopbarConfiguration topbar, SidebarConfiguration sidebar) { + this.topbar = topbar; + this.sidebar = sidebar; + this.lastModified = System.currentTimeMillis(); + } + + public NavigationConfiguration(TopbarConfiguration topbar, + SidebarConfiguration sidebar, + long lastModified) { + this.topbar = topbar; + this.sidebar = sidebar; + this.lastModified = lastModified; + } + + @Override + public NavigationConfiguration clone() { // NOSONAR + return new NavigationConfiguration(topbar == null ? null : topbar.clone(), + sidebar == null ? null : sidebar.clone(), + lastModified); + } + +} 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..0fbb426825f --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/model/SidebarConfiguration.java @@ -0,0 +1,54 @@ +/** + * 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.ArrayList; +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 implements Cloneable { + + private boolean allowUserCustomHome; + + private SidebarMode defaultMode; + + private SidebarMode userMode; + + private List allowedModes; + + private List items; + + @Override + public SidebarConfiguration clone() { // NOSONAR + return new SidebarConfiguration(allowUserCustomHome, + defaultMode, + userMode, + allowedModes == null ? null : new ArrayList<>(allowedModes), + items == null ? null : items.stream().map(SidebarItem::clone).toList()); + } + +} 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..097fad79bed --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/model/SidebarItem.java @@ -0,0 +1,68 @@ +/** + * 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.HashMap; +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 implements Cloneable { + + 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; + } + + @Override + public SidebarItem clone() { // NOSONAR + return new SidebarItem(name, + url, + target, + avatar, + icon, + type, + items == null ? null : items.stream().map(SidebarItem::clone).toList(), + properties == null ? null : new HashMap<>(properties)); + } + +} 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..2f03723fda1 --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/model/TopbarApplication.java @@ -0,0 +1,75 @@ +/** + * 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 java.util.Objects; + +import org.apache.commons.lang3.StringUtils; + +import io.meeds.social.navigation.constant.TopbarItemType; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TopbarApplication { + + public static final String CONTENT_ID_PROP_NAME = "contentId"; + + private String id; + + private String name; + + private String description; + + private String icon; + + private TopbarItemType type; + + private boolean enabled; + + private boolean mobile; + + private Map properties; + + @Override + public boolean equals(Object o) { + if (!(o instanceof TopbarApplication app)) { + return false; + } else if (StringUtils.equals(id, app.getId())) { + return true; + } else if (properties == null || app.getProperties() == null) { + return false; + } else { + return StringUtils.equals(properties.get(CONTENT_ID_PROP_NAME), + app.getProperties().get(CONTENT_ID_PROP_NAME)); + } + } + + @Override + public int hashCode() { + return Objects.hash(properties == null ? id : + StringUtils.firstNonBlank(properties.get(CONTENT_ID_PROP_NAME), id)); + } + +} 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..a66fe9bb3a7 --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/model/TopbarConfiguration.java @@ -0,0 +1,49 @@ +/** + * 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.ArrayList; +import java.util.List; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TopbarConfiguration implements Cloneable { + + private boolean displayCompanyName; + + private boolean displaySiteName; + + private boolean displayMobileCompanyLogo; + + private List applications; + + @Override + public TopbarConfiguration clone() { // NOSONAR + return new TopbarConfiguration(displayCompanyName, + displaySiteName, + displayMobileCompanyLogo, + new ArrayList<>(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..f9a2a7e0e57 --- /dev/null +++ b/component/api/src/main/java/io/meeds/social/navigation/plugin/SidebarPlugin.java @@ -0,0 +1,69 @@ +/** + * 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; + +/** + * A class to allow managing Sidebar item types + */ +public interface SidebarPlugin { + + static final SidebarItem SIDEBAR_SEPARATOR = new SidebarItem(SidebarItemType.SEPARATOR); + + /** + * @return {@link SidebarItemType} managed by the implementing plugin + */ + default SidebarItemType getType() { + return null; + } + + /** + * Resolves Item Name and Icon when storage is maintained + * + * @param item + * @param username + * @param locale + */ + default SidebarItem resolveProperties(SidebarItem item, String username, Locale locale) { + return item; + } + + /** + * @return {@link List} of {@link SidebarItem} to inject on startup + */ + default List getDefaultItems() { + return Collections.emptyList(); + } + + /** + * @param item {@link SidebarItem} + * @param username User name + * @return true if the item exists and the user has access to it, else false + */ + default boolean itemExists(SidebarItem item, String username) { + return true; + } + +} diff --git a/component/api/src/main/java/org/exoplatform/social/core/search/Sorting.java b/component/api/src/main/java/org/exoplatform/social/core/search/Sorting.java index 8b005a4fca7..b0e13daea67 100644 --- a/component/api/src/main/java/org/exoplatform/social/core/search/Sorting.java +++ b/component/api/src/main/java/org/exoplatform/social/core/search/Sorting.java @@ -32,7 +32,7 @@ public static enum OrderBy { } public static enum SortBy { - RELEVANCY("relevancy"), DATE("date"), TITLE("title"), LASTNAME("lastName"), FIRSTNAME("firstName"), FULLNAME("fullName"); + RELEVANCY("relevancy"), DATE("date"), TITLE("title"), LASTNAME("lastName"), FIRSTNAME("firstName"), FULLNAME("fullName"), LASTVISITED("lastVisited"); private String fieldName; diff --git a/component/api/src/main/java/org/exoplatform/social/core/space/spi/SpaceService.java b/component/api/src/main/java/org/exoplatform/social/core/space/spi/SpaceService.java index c028ed8ac7e..62c3b6d9942 100644 --- a/component/api/src/main/java/org/exoplatform/social/core/space/spi/SpaceService.java +++ b/component/api/src/main/java/org/exoplatform/social/core/space/spi/SpaceService.java @@ -915,6 +915,15 @@ default ListAccess getLastAccessedSpace(String remoteId) { throw new UnsupportedOperationException(); } + /** + * @param username + * @param spaceFilter + * @return + */ + default ListAccess getLastAccessedSpaceByFilter(String username, SpaceFilter spaceFilter) { + throw new UnsupportedOperationException(); + } + /** * Registers a space lifecycle listener. * diff --git a/component/core/pom.xml b/component/core/pom.xml index 4159bcda106..9a641537852 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.72 diff --git a/component/core/src/main/java/io/meeds/social/navigation/listener/NavigationConfigurationSiteDisplayListener.java b/component/core/src/main/java/io/meeds/social/navigation/listener/NavigationConfigurationSiteDisplayListener.java new file mode 100644 index 00000000000..b418eebebb4 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/listener/NavigationConfigurationSiteDisplayListener.java @@ -0,0 +1,98 @@ +/** + * 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.listener; + +import static io.meeds.social.navigation.plugin.AbstractLayoutSidebarPlugin.SITE_NAME_PROP_NAME; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import org.exoplatform.portal.config.model.PortalConfig; +import org.exoplatform.portal.mop.SiteKey; +import org.exoplatform.portal.mop.service.LayoutService; +import org.exoplatform.services.listener.Event; +import org.exoplatform.services.listener.ListenerBase; +import org.exoplatform.services.listener.ListenerService; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.NavigationConfiguration; +import io.meeds.social.navigation.service.NavigationConfigurationService; + +import jakarta.annotation.PostConstruct; + +@Component +public class NavigationConfigurationSiteDisplayListener implements ListenerBase { + + @Autowired + private ListenerService listenerService; + + @Autowired + private LayoutService layoutService; + + @PostConstruct + public void init() { + listenerService.addListener(NavigationConfigurationService.NAVIGATION_CONFIGURATION_UPDATED_EVENT, this); + } + + @Override + public void onEvent(Event event) throws Exception { + NavigationConfiguration oldConfiguration = event.getSource(); + NavigationConfiguration newConfiguration = event.getData(); + + List oldSiteNames = oldConfiguration == null ? + Collections.emptyList() : + getSiteNames(oldConfiguration); + List newSiteNames = getSiteNames(newConfiguration); + + List sitesWithoutSharedLayout = new ArrayList<>(oldSiteNames); + sitesWithoutSharedLayout.removeAll(newSiteNames); + sitesWithoutSharedLayout.forEach(siteName -> { + PortalConfig site = layoutService.getPortalConfig(SiteKey.portal(siteName)); + if (site != null) { + site.setDisplayed(false); + layoutService.save(site); + } + }); + + newSiteNames.forEach(siteName -> { + PortalConfig site = layoutService.getPortalConfig(SiteKey.portal(siteName)); + int displayOrder = newSiteNames.indexOf(siteName) + 1; + if (site != null + && (!site.isDisplayed() || site.getDisplayOrder() != displayOrder)) { + site.setDisplayed(true); + site.setDisplayOrder(displayOrder); + layoutService.save(site); + } + }); + } + + private List getSiteNames(NavigationConfiguration configuration) { + return configuration.getSidebar() + .getItems() + .stream() + .filter(item -> item.getType() == SidebarItemType.SITE) + .map(item -> item.getProperties().get(SITE_NAME_PROP_NAME)) + .toList(); + } + +} diff --git a/component/core/src/main/java/io/meeds/social/navigation/plugin/AbstractLayoutSidebarPlugin.java b/component/core/src/main/java/io/meeds/social/navigation/plugin/AbstractLayoutSidebarPlugin.java new file mode 100644 index 00000000000..cfd9c75f323 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/AbstractLayoutSidebarPlugin.java @@ -0,0 +1,293 @@ +/** + * 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.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.ResourceBundle; +import java.util.stream.Stream; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; + +import org.exoplatform.commons.utils.ExpressionUtil; +import org.exoplatform.portal.application.PortalRequestHandler; +import org.exoplatform.portal.config.UserACL; +import org.exoplatform.portal.config.model.Page; +import org.exoplatform.portal.config.model.PortalConfig; +import org.exoplatform.portal.mop.PageType; +import org.exoplatform.portal.mop.SiteKey; +import org.exoplatform.portal.mop.SiteType; +import org.exoplatform.portal.mop.State; +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.DescriptionService; +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.LocaleConfigService; +import org.exoplatform.services.resources.LocaleContextInfo; +import org.exoplatform.services.resources.ResourceBundleManager; +import org.exoplatform.services.resources.ResourceBundleService; +import org.exoplatform.web.WebAppController; +import org.exoplatform.web.controller.QualifiedName; + +import io.meeds.social.navigation.model.SidebarItem; +import io.meeds.social.translation.service.TranslationService; + +import lombok.SneakyThrows; + +public abstract class AbstractLayoutSidebarPlugin implements SidebarPlugin { + + public static final String SITE_NAME_PROP_NAME = "siteName"; + + public static final String SITE_ID_PROP_NAME = "siteId"; + + public static final String SITE_TYPE_PROP_NAME = "siteType"; + + public static final String SITE_EXPAND_PAGES_PROP_NAME = "expandPages"; + + public static final String NODE_ID_PROP_NAME = "navigationNodeId"; + + public static final String SITE_DISPLAY_NAME_PROP_NAME = "siteDisplayName"; + + public static final String SITE_ICON_PROP_NAME = "siteIcon"; + + public static final String SITE_TRANSLATION_OBJECT_TYPE = "site"; + + public static final String SITE_TRANSLATION_LABEL_FIELD_NAME = "label"; + + @Autowired + protected NavigationService navigationService; + + @Autowired + protected LayoutService layoutService; + + @Autowired + protected TranslationService translationService; + + @Autowired + protected DescriptionService descriptionService; + + @Autowired + protected ResourceBundleManager resourceBundleManager; + + @Autowired + protected LocaleConfigService localeConfigService; + + @Autowired + protected OrganizationService organizationService; + + @Autowired + protected UserACL userAcl; + + @Autowired + private WebAppController webController; + + protected SidebarItem resolvePageItemProperties(SidebarItem item, Locale locale) { + String nodeId = item.getProperties().get(NODE_ID_PROP_NAME); + NodeData node = navigationService.getNodeById(Long.parseLong(nodeId)); + + SiteKey siteKey = node.getSiteKey(); + long siteId = getSiteId(siteKey); + + item.setName(getNodeLabel(Long.parseLong(nodeId), locale)); + if (item.getIcon() == null) { + item.setIcon(node.getState().getIcon()); + } + + item.setProperties(new HashMap<>(item.getProperties())); + item.getProperties().put(SITE_ID_PROP_NAME, String.valueOf(siteId)); + item.getProperties().put(SITE_TYPE_PROP_NAME, siteKey.getTypeName()); + item.getProperties().put(SITE_NAME_PROP_NAME, siteKey.getName()); + item.getProperties().put(SITE_ICON_PROP_NAME, getSiteIcon(siteKey)); + item.getProperties().put(SITE_DISPLAY_NAME_PROP_NAME, getSiteLabel(siteKey, locale)); + + if (node.getState().getPageRef() != null) { + Page page = layoutService.getPage(node.getState().getPageRef()); + if (PageType.LINK.name().equals(page.getType())) { + item.setUrl(page.getLink()); + item.setTarget(node.getState().getTarget()); + } else { + item.setUrl(getNodeUri(node)); + } + } + return item; + } + + protected String getNodeLabel(long nodeId, Locale locale) { + NodeData nodeData = navigationService.getNodeById(nodeId); + Map nodeLabels = descriptionService.getDescriptions(String.valueOf(nodeId)); + if (MapUtils.isEmpty(nodeLabels)) { + return getLabelOrDefault(nodeData.getSiteKey(), + nodeData.getState().getLabel(), + locale, + StringUtils.firstNonBlank(nodeData.getState().getLabel(), nodeData.getName())); + } else if (nodeLabels.containsKey(locale)) { + return nodeLabels.get(locale).getName(); + } else if (nodeLabels.containsKey(localeConfigService.getDefaultLocaleConfig().getLocale())) { + return nodeLabels.get(localeConfigService.getDefaultLocaleConfig().getLocale()).getName(); + } else if (nodeLabels.containsKey(ResourceBundleService.DEFAULT_CROWDIN_LOCALE)) { + return nodeLabels.get(ResourceBundleService.DEFAULT_CROWDIN_LOCALE).getName(); + } else { + return nodeLabels.values().iterator().next().getName(); + } + } + + protected 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 (isVisibilityEligible(state) + && state.getPageRef() != null + && StringUtils.isNotBlank(state.getIcon())) { + return state.getIcon(); + } else { + return null; + } + }).filter(Objects::nonNull).findFirst().orElse(null); + } + return null; + } + + @SneakyThrows + protected String getSiteLabel(SiteKey siteKey, Locale locale) { + long siteId = getSiteId(siteKey); + String label = translationService.getTranslationLabelOrDefault(SITE_TRANSLATION_OBJECT_TYPE, + siteId, + SITE_TRANSLATION_LABEL_FIELD_NAME, + locale); + if (StringUtils.isNotBlank(label)) { + return label; + } + + PortalConfig site = layoutService.getPortalConfig(siteId); + label = StringUtils.firstNonBlank(site.getLabel(), + site.getName(), + siteKey.getName()); + if (siteKey.getType() == SiteType.PORTAL) { + return getLabelOrDefault(siteKey, label, locale, siteKey.getName()); + } else if (siteKey.getType() == SiteType.GROUP) { + Group siteGroup = organizationService.getGroupHandler() + .findGroupById(siteKey.getName()); + if (siteGroup != null) { + return siteGroup.getLabel(); + } + } + return label; + } + + protected boolean isEligiblePage(long nodeId, String username) { + NodeData node = navigationService.getNodeById(nodeId); + if (node == null || node.getSiteKey() == null || node.getState() == null) { + return false; + } else { + PortalConfig site = layoutService.getPortalConfig(node.getSiteKey()); + if (!userAcl.hasAccessPermission(site, userAcl.getUserIdentity(username))) { + return false; + } else if (node.getState() == null || !isVisibilityEligible(node.getState())) { + return false; + } else if (node.getState().getPageRef() == null) { + return node.iterator(false).hasNext(); + } else { + Page page = layoutService.getPage(node.getState().getPageRef()); + if (page == null) { + return node.iterator(false).hasNext(); + } else { + return userAcl.hasAccessPermission(page, userAcl.getUserIdentity(username)); + } + } + } + } + + protected boolean isVisibilityEligible(NodeState state) { + if (state.getVisibility() == Visibility.DISPLAYED) { + return true; + } else if (state.getVisibility() == Visibility.TEMPORAL) { + return (state.getEndPublicationTime() == 0 || state.getEndPublicationTime() < System.currentTimeMillis()) + && (state.getStartPublicationTime() == 0 || state.getStartPublicationTime() > System.currentTimeMillis()); + } + return false; + } + + private String getNodeUri(NodeData node) { + SiteKey siteKey = node.getSiteKey(); + StringBuilder uriBuilder = new StringBuilder(); + buildUri(node, uriBuilder); + + Map params = new HashMap<>(); + params.put(WebAppController.HANDLER_PARAM, PortalRequestHandler.HANDLER_NAME); + params.put(PortalRequestHandler.REQUEST_SITE_NAME, siteKey.getName()); + params.put(PortalRequestHandler.REQUEST_SITE_TYPE, siteKey.getTypeName()); + params.put(PortalRequestHandler.REQUEST_PATH, uriBuilder.toString().replaceFirst("/", "")); + params.put(PortalRequestHandler.LANG, Locale.ENGLISH.toLanguageTag()); + return "/portal" + + webController.getRouter().render(params).replace("/en", "").replace("?lang=en", "").replace("&lang=en", ""); + } + + private void buildUri(NodeData node, StringBuilder uriBuilder) { + if (StringUtils.isNotBlank(node.getName()) + && !StringUtils.equals(node.getName(), "default")) { + uriBuilder.insert(0, node.getName()); + if (!uriBuilder.isEmpty()) { + uriBuilder.insert(0, "/"); + } + } + if (StringUtils.isNotBlank(node.getParentId())) { + NodeData parentNode = navigationService.getNodeById(Long.parseLong(node.getParentId())); + buildUri(parentNode, uriBuilder); + } + } + + private String getLabelOrDefault(SiteKey siteKey, String label, Locale locale, String defaultLabel) { + 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(defaultLabel); + } else { + return StringUtils.firstNonBlank(label, defaultLabel); + } + } + + private long getSiteId(SiteKey siteKey) { + PortalConfig site = layoutService.getPortalConfig(siteKey); + return Long.parseLong((site.getStorageId().split("_"))[1]); + } + + 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/AbstractSpaceSidebarPlugin.java b/component/core/src/main/java/io/meeds/social/navigation/plugin/AbstractSpaceSidebarPlugin.java new file mode 100644 index 00000000000..09b04d18b6c --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/AbstractSpaceSidebarPlugin.java @@ -0,0 +1,150 @@ +/** + * 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.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; + +import org.exoplatform.social.core.search.Sorting; +import org.exoplatform.social.core.search.Sorting.OrderBy; +import org.exoplatform.social.core.search.Sorting.SortBy; +import org.exoplatform.social.core.space.SpaceFilter; +import org.exoplatform.social.core.space.model.Space; +import org.exoplatform.social.core.space.spi.SpaceService; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; + +import lombok.SneakyThrows; + +public abstract class AbstractSpaceSidebarPlugin implements SidebarPlugin { + + public static final int SPACES_LIMIT_DEFAULT = 4; + + public static final String SPACES_LIMIT = "limit"; + + public static final String SPACES_SORT_BY = "sortBy"; + + public static final String DISPLAY_ONLY_WHEN_MEMBER_PROP_NAME = "displayOnlyWhenMember"; + + private static final String NOT_MEMBER_PROP_NAME = "notMember"; + + @Autowired + private SpaceService spaceService; + + protected abstract void buildSpaceFilter(SidebarItem item, SpaceFilter spaceFilter); + + protected List getSpaces(SidebarItem item, String username) { + SpaceFilter spaceFilter = new SpaceFilter(); + SidebarSpaceSortBy sortBy = getSortBy(item); + spaceFilter.setSorting(getSorting(item, sortBy)); + spaceFilter.setRemoteId(username); + buildSpaceFilter(item, spaceFilter); + int limit = getLimit(item); + Space[] spaces = getSpaces(item, spaceFilter, username, sortBy, limit); + if (spaces.length == 0 && isDisplayOnlyWhenMember(item)) { + item.getProperties().put(NOT_MEMBER_PROP_NAME, String.valueOf(!isMember(spaceFilter, username))); + } + return Arrays.stream(spaces) + .filter(Objects::nonNull) + .map(this::toSidebarItem) + .toList(); + } + + private SidebarItem toSidebarItem(Space space) { + Map properties = new HashMap<>(); + properties.put("id", space.getId()); + properties.put("groupId", space.getGroupId()); + return new SidebarItem(space.getDisplayName(), + "/portal/s/" + space.getId(), + null, + space.getAvatarUrl(), + null, + SidebarItemType.SPACE, + null, + properties); + } + + private SortBy getSortField(SidebarItem item, SidebarSpaceSortBy sortBy) { // NOSONAR + return Sorting.SortBy.TITLE; + } + + private OrderBy getSortDirection(SidebarItem item, SidebarSpaceSortBy sortBy) { // NOSONAR + return sortBy == SidebarSpaceSortBy.LAST_ACCESS ? OrderBy.DESC : OrderBy.ASC; + } + + private SidebarSpaceSortBy getSortByField(String sortByProperty) { + return StringUtils.isBlank(sortByProperty) ? SidebarSpaceSortBy.TITLE : + SidebarSpaceSortBy.valueOf(sortByProperty); + } + + private Sorting getSorting(SidebarItem item, SidebarSpaceSortBy sortBy) { + return new Sorting(getSortField(item, sortBy), getSortDirection(item, sortBy)); + } + + @SneakyThrows + private Space[] getSpaces(SidebarItem item, SpaceFilter spaceFilter, String username, SidebarSpaceSortBy sortBy, int limit) { + if (limit <= 0) { + return new Space[0]; + } + return switch (sortBy) { + case TITLE: { + yield spaceService.getMemberSpacesByFilter(username, spaceFilter).load(0, limit); + } + case FAVORITE: { + yield spaceService.getFavoriteSpacesByFilter(username, spaceFilter).load(0, limit); + } + case LAST_ACCESS: { + yield spaceService.getLastAccessedSpaceByFilter(username, spaceFilter).load(0, limit); + } + default: + yield new Space[0]; + }; + } + + @SneakyThrows + private boolean isMember(SpaceFilter spaceFilter, String username) { + return spaceService.getMemberSpacesByFilter(username, spaceFilter).getSize() > 0; + } + + private SidebarSpaceSortBy getSortBy(SidebarItem item) { + String sortByProperty = item.getProperties().get(SPACES_SORT_BY); + return getSortByField(sortByProperty); + } + + private int getLimit(SidebarItem item) { + String limitProperty = item.getProperties().get(SPACES_LIMIT); + return StringUtils.isBlank(limitProperty) ? SPACES_LIMIT_DEFAULT : Integer.parseInt(limitProperty); + } + + private boolean isDisplayOnlyWhenMember(SidebarItem item) { + return StringUtils.equals(item.getProperties().get(DISPLAY_ONLY_WHEN_MEMBER_PROP_NAME), "true"); + } + + protected enum SidebarSpaceSortBy { + TITLE, FAVORITE, LAST_ACCESS; + } + +} 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..248b7afe094 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/DefaultSidebarPlugin.java @@ -0,0 +1,22 @@ +/** + * 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; + +public class DefaultSidebarPlugin implements SidebarPlugin { +} diff --git a/component/core/src/main/java/io/meeds/social/navigation/plugin/LinkSidebarPlugin.java b/component/core/src/main/java/io/meeds/social/navigation/plugin/LinkSidebarPlugin.java new file mode 100644 index 00000000000..b10ab3b439d --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/LinkSidebarPlugin.java @@ -0,0 +1,66 @@ +/** + * 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 static io.meeds.social.navigation.plugin.SidebarPluginUtils.getNameFromProperties; + +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.services.resources.LocaleConfigService; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; + +@Component +@Order(50) +public class LinkSidebarPlugin implements SidebarPlugin { + + public static final String LINK_NAMES = "names"; + + @Autowired + private LocaleConfigService localeConfigService; + + @Override + public SidebarItemType getType() { + return SidebarItemType.LINK; + } + + @Override + public SidebarItem resolveProperties(SidebarItem item, String username, Locale locale) { + item.setName(getNameFromProperties(localeConfigService, item, LINK_NAMES, locale)); + return item; + } + + @Override + public List getDefaultItems() { + return Collections.emptyList(); + } + + @Override + public boolean itemExists(SidebarItem item, String username) { + 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..efa45de27a6 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/PageSidebarPlugin.java @@ -0,0 +1,52 @@ +/** + * 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.Locale; + +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; + +@Component +@Order(20) +public class PageSidebarPlugin extends AbstractLayoutSidebarPlugin { + + @Override + public SidebarItemType getType() { + return SidebarItemType.PAGE; + } + + @Override + public SidebarItem resolveProperties(SidebarItem item, String username, Locale locale) { + return resolvePageItemProperties(item, locale); + } + + @Override + public boolean itemExists(SidebarItem item, String username) { + if (item == null || item.getProperties() == null) { + return false; + } + String nodeIdProperty = item.getProperties().get(NODE_ID_PROP_NAME); + return isEligiblePage(Long.parseLong(nodeIdProperty), username); + } + +} diff --git a/component/core/src/main/java/io/meeds/social/navigation/plugin/SidebarPluginUtils.java b/component/core/src/main/java/io/meeds/social/navigation/plugin/SidebarPluginUtils.java new file mode 100644 index 00000000000..25710575d49 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/SidebarPluginUtils.java @@ -0,0 +1,68 @@ +/** + * 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.Locale; + +import org.json.JSONException; +import org.json.JSONObject; + +import org.exoplatform.services.resources.LocaleConfigService; + +import io.meeds.social.navigation.model.SidebarItem; + +public class SidebarPluginUtils { + + private SidebarPluginUtils() { + // Static methods only + } + + public static String getNameFromProperties(LocaleConfigService localeConfigService, + SidebarItem item, + String propName, + Locale locale) { + String names = item.getProperties().get(propName); + JSONObject jsonObject = new JSONObject(names); + String lang = getLocaleName(locale); + Object name = getName(jsonObject, lang); + if (name == null) { + lang = getLocaleName(localeConfigService.getDefaultLocaleConfig().getLocale()); + name = getName(jsonObject, lang); + if (name == null) { + lang = jsonObject.keys().next(); + name = getName(jsonObject, lang); + } + } + return String.valueOf(name); + } + + public static String getLocaleName(Locale locale) { + return locale.toLanguageTag().replace("-", "_"); // Use same name as + // localeConfigService + } + + public static Object getName(JSONObject jsonObject, String lang) { + try { + return jsonObject.get(lang); + } catch (JSONException e) { + return 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..54e4c0070fe --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/SiteSidebarPlugin.java @@ -0,0 +1,136 @@ +/** + * 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.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +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.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.navigation.NodeContext; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; + +@Component +@Order(10) +public class SiteSidebarPlugin extends AbstractLayoutSidebarPlugin { + + @Autowired + private UserPortalConfigService userPortalConfigService; + + @Override + public SidebarItemType getType() { + return SidebarItemType.SITE; + } + + @Override + public boolean itemExists(SidebarItem item, String username) { + if (item == null || item.getProperties() == null) { + return false; + } + PortalConfig site = layoutService.getPortalConfig(getSiteKey(item)); + return site != null && userAcl.hasAccessPermission(site, userAcl.getUserIdentity(username)); + } + + @Override + public SidebarItem resolveProperties(SidebarItem item, String username, Locale locale) { + SiteKey siteKey = getSiteKey(item); + item.setName(getSiteLabel(siteKey, locale)); + if (StringUtils.isBlank(item.getIcon())) { + item.setIcon(getSiteIcon(siteKey)); + } + if (StringUtils.isBlank(item.getUrl())) { + item.setUrl("/portal/" + siteKey.getName()); + } + if (StringUtils.equals(item.getProperties().get(SITE_EXPAND_PAGES_PROP_NAME), "true")) { + NodeContext> rootNode = navigationService.loadNode(siteKey); + if (rootNode != null && rootNode.getSize() > 0) { + item.setItems(rootNode.getNodes() + .stream() + .filter(n -> isEligiblePage(Long.parseLong(n.getId()), username)) + .map(node -> { + SidebarItem pageItem = new SidebarItem(SidebarItemType.PAGE); + pageItem.setProperties(Collections.singletonMap(NODE_ID_PROP_NAME, node.getData().getId())); + return resolvePageItemProperties(pageItem, locale); + }) + .toList()); + } + } + 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(); + } + + protected SidebarItem toSidebarItem(SiteKey siteKey) { + return new SidebarItem(siteKey.getName(), + "/portal/" + siteKey.getName(), + null, + null, + getSiteIcon(siteKey), + SidebarItemType.SITE, + null, + buildSiteProperties(siteKey)); + } + + 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_ID_PROP_NAME, String.valueOf(siteId)); + if (isMetaSite) { + properties.put(SITE_EXPAND_PAGES_PROP_NAME, "true"); + } + return properties; + } + + private SiteKey getSiteKey(SidebarItem item) { + return new SiteKey(item.getProperties().get(SITE_TYPE_PROP_NAME), + item.getProperties().get(SITE_NAME_PROP_NAME)); + } + +} diff --git a/component/core/src/main/java/io/meeds/social/navigation/plugin/SpaceListSidebarPlugin.java b/component/core/src/main/java/io/meeds/social/navigation/plugin/SpaceListSidebarPlugin.java new file mode 100644 index 00000000000..ac907c5e59c --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/SpaceListSidebarPlugin.java @@ -0,0 +1,120 @@ +/** + * 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 static io.meeds.social.navigation.plugin.SidebarPluginUtils.getNameFromProperties; + +import java.util.Collections; +import java.util.HashMap; +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.stereotype.Component; + +import org.exoplatform.portal.config.UserACL; +import org.exoplatform.portal.config.UserPortalConfigService; +import org.exoplatform.portal.config.model.Page; +import org.exoplatform.portal.mop.SiteKey; +import org.exoplatform.portal.mop.page.PageKey; +import org.exoplatform.portal.mop.service.LayoutService; +import org.exoplatform.services.resources.LocaleConfigService; +import org.exoplatform.social.core.space.SpaceFilter; + +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; + +@Component +@Order(40) +public class SpaceListSidebarPlugin extends AbstractSpaceSidebarPlugin { + + public static final String SPACES_NAMES = "names"; + + public static final PageKey SPACES_PAGE_KEY = new PageKey(SiteKey.portal("global"), "all-spaces"); + + @Autowired + private LocaleConfigService localeConfigService; + + @Autowired + private UserPortalConfigService portalConfigService; + + @Autowired + private LayoutService layoutService; + + @Autowired + private UserACL userAcl; + + @Override + public SidebarItemType getType() { + return SidebarItemType.SPACES; + } + + @Override + public boolean itemExists(SidebarItem item, String username) { + if (item == null || item.getProperties() == null) { + return false; + } + return item.getProperties().containsKey(SPACES_NAMES); + } + + @Override + public SidebarItem resolveProperties(SidebarItem item, String username, Locale locale) { + item.setName(getNameFromProperties(localeConfigService, + item, + SPACES_NAMES, + locale)); + item.setItems(getSpaces(item, username)); + + if (hasAccessToSpacesPage(username)) { + item.setUrl(String.format("/portal/%s/spaces", portalConfigService.getMetaPortal())); + } else { + item.setUrl(null); + } + return item; + } + + @Override + public List getDefaultItems() { + Map properties = new HashMap<>(); + properties.put(SPACES_NAMES, "{\"en\": \"sidebar.viewAllSpaces\"}"); + properties.put(SPACES_LIMIT, "0"); + properties.put(SPACES_SORT_BY, "TITLE"); + return Collections.singletonList(new SidebarItem(null, + null, + null, + null, + "fa-layer-group", + SidebarItemType.SPACES, + null, + properties)); + } + + @Override + protected void buildSpaceFilter(SidebarItem item, SpaceFilter spaceFilter) { + // No specific space filter + } + + private boolean hasAccessToSpacesPage(String username) { + Page spacesPage = layoutService.getPage(SPACES_PAGE_KEY); + return spacesPage != null && userAcl.hasAccessPermission(spacesPage, userAcl.getUserIdentity(username)); + } + +} 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..b3f9455271b --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/plugin/SpaceTemplateSidebarPlugin.java @@ -0,0 +1,127 @@ +/** + * 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.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.annotation.Order; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; + +import org.exoplatform.social.core.space.SpaceFilter; + +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 extends AbstractSpaceSidebarPlugin { + + public static final String SPACE_TEMPLATE_ID_PROP_NAME = "spaceTemplateId"; + + @Autowired + private SpaceTemplateService spaceTemplateService; + + @Value("#{'${social.spaceTemplates.sidebar.defaultSort:announcement,community,project,circle}'.split(',')}") + private List defaultSpaceTemplatesSort; + + @Override + public SidebarItemType getType() { + return SidebarItemType.SPACE_TEMPLATE; + } + + @Override + public boolean itemExists(SidebarItem item, String username) { + if (item == null || item.getProperties() == null) { + return false; + } + String spaceTemplateIdProperty = item.getProperties().get(SPACE_TEMPLATE_ID_PROP_NAME); + long spaceTemplateId = Long.parseLong(spaceTemplateIdProperty); + SpaceTemplate spaceTemplate = spaceTemplateService.getSpaceTemplate(spaceTemplateId); + return spaceTemplate != null && spaceTemplate.isEnabled() && !spaceTemplate.isDeleted(); + } + + @Override + public SidebarItem resolveProperties(SidebarItem item, String username, 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()); + item.setItems(getSpaces(item, username)); + } + return item; + } + + @Override + public List getDefaultItems() { + List spaceTemplateItems = spaceTemplateService.getSpaceTemplates(null, Pageable.unpaged(), true) + .stream() + .filter(t -> t.isEnabled() && !t.isDeleted()) + .sorted(this::sortDefaultSpaceTemplates) + .map(this::toSidebarItem) + .toList(); + return Arrays.asList(ArrayUtils.addFirst(spaceTemplateItems.toArray(new SidebarItem[0]), + SIDEBAR_SEPARATOR)); + } + + @Override + protected void buildSpaceFilter(SidebarItem item, SpaceFilter spaceFilter) { + String spaceTemplateId = item.getProperties().get(SPACE_TEMPLATE_ID_PROP_NAME); + spaceFilter.setTemplateId(Long.parseLong(spaceTemplateId)); + } + + 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) { + Map properties = new HashMap<>(); + properties.put(SPACE_TEMPLATE_ID_PROP_NAME, String.valueOf(spaceTemplate.getId())); + properties.put(SPACES_LIMIT, String.valueOf(SPACES_LIMIT_DEFAULT)); + properties.put(SPACES_SORT_BY, SidebarSpaceSortBy.LAST_ACCESS.name()); + properties.put(DISPLAY_ONLY_WHEN_MEMBER_PROP_NAME, "true"); + return properties; + } + + private int sortDefaultSpaceTemplates(SpaceTemplate t1, SpaceTemplate t2) { + return getDefaultTemplateOrder(t1) - getDefaultTemplateOrder(t2); + } + + private int getDefaultTemplateOrder(SpaceTemplate spaceTemplate) { + return spaceTemplate.isSystem() ? defaultSpaceTemplatesSort.indexOf(spaceTemplate.getLayout()) : 100; + } + +} 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..788209bf7a4 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/service/NavigationConfigurationInitService.java @@ -0,0 +1,112 @@ +/** + * 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 io.meeds.common.ContainerTransactional; +import io.meeds.social.navigation.constant.SidebarMode; +import io.meeds.social.navigation.model.NavigationConfiguration; +import io.meeds.social.navigation.model.SidebarConfiguration; +import io.meeds.social.navigation.model.SidebarItem; +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 { + + @Autowired + private NavigationConfigurationService navigationConfigurationService; + + @Autowired + private List menuPlugins; + + @Value("${navigation.configuration.forceUpdate:false}") + private boolean forceUpdate; + + @Value("${navigation.configuration.displayCompanyName:true}") + private boolean displayCompanyName; + + @Value("${navigation.configuration.displayMobileCompanyLogo:true}") + private boolean displayMobileCompanyLogo; + + @Value("${navigation.configuration.displaySiteName:true}") + private boolean displaySiteName; + + @Value("${navigation.configuration.allowUserCustomHome:true}") + private boolean allowUserCustomHome; + + @Value("${navigation.configuration.defaultMode:ICON}") + private SidebarMode defaultMode; + + @PostConstruct + @ContainerTransactional + public void init() { + if (forceUpdate || navigationConfigurationService.getConfiguration() == null) { + navigationConfigurationService.updateConfiguration(buildDefaultNavigationConfiguration()); + } + } + + private NavigationConfiguration buildDefaultNavigationConfiguration() { + TopbarConfiguration topbarConfiguration = new TopbarConfiguration(displayCompanyName, + displaySiteName, + displayMobileCompanyLogo, + navigationConfigurationService.getDefaultTopbarApplications()); + SidebarConfiguration sidebarConfiguration = new SidebarConfiguration(allowUserCustomHome, + defaultMode, + null, + Arrays.asList(SidebarMode.values()), + getDefaultNavigations()); + return new NavigationConfiguration(topbarConfiguration, sidebarConfiguration); + } + + /** + * @return Default Sites Navigations + */ + private List getDefaultNavigations() { + if (menuPlugins != null) { + return menuPlugins.stream() + .map(SidebarPlugin::getDefaultItems) + .flatMap(items -> { + if (CollectionUtils.isEmpty(items)) { + return Stream.empty(); + } else { + return items.stream(); + } + }) + .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..3d471bac51c --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/service/NavigationConfigurationService.java @@ -0,0 +1,252 @@ +/** + * 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.Collections; +import java.util.List; +import java.util.Locale; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Service; + +import org.exoplatform.commons.addons.AddOnService; +import org.exoplatform.portal.config.UserPortalConfigService; +import org.exoplatform.portal.config.model.Application; +import org.exoplatform.portal.config.model.TransientApplicationState; +import org.exoplatform.services.listener.ListenerService; + +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.NavigationConfiguration; +import io.meeds.social.navigation.model.SidebarConfiguration; +import io.meeds.social.navigation.model.SidebarItem; +import io.meeds.social.navigation.model.TopbarApplication; +import io.meeds.social.navigation.model.TopbarConfiguration; +import io.meeds.social.navigation.plugin.DefaultSidebarPlugin; +import io.meeds.social.navigation.plugin.SidebarPlugin; +import io.meeds.social.navigation.storage.NavigationConfigurationStorage; + +import jakarta.annotation.PostConstruct; +import lombok.Setter; +import lombok.SneakyThrows; + +/** + * A Service to manage Topbar and Sidebar configurations + */ +@Service +public class NavigationConfigurationService { + + public static final String NAVIGATION_CONFIGURATION_UPDATED_EVENT = "social.navigation.configuration.updated"; + + private static final String TOPBAR_APPLICATION_ENABLED_PATTERN = "social.topbar.application.%s.enabled"; + + private static final String TOPBAR_APPLICATION_MOBILE_PATTERN = "social.topbar.application.%s.mobile"; + + private static final String TOP_NAVIGATION_ADDON_CONTAINER = "middle-topNavigation-container"; + + private static final SidebarPlugin DEFAULT_MENU_PLUGIN = new DefaultSidebarPlugin(); + + @Autowired + private NavigationConfigurationStorage navigationConfigurationStorage; + + @Autowired + private UserPortalConfigService userPortalConfigService; + + @Autowired + private AddOnService addonContainerService; + + @Autowired + private Environment environment; + + @Autowired + private ListenerService listenerService; + + @Autowired + private List menuPlugins; + + @Setter + private List defaultTopbarApplications; + + @PostConstruct + @ContainerTransactional + public void init() { + this.defaultTopbarApplications = getDefaultTopbarApplications(); + NavigationConfiguration configuration = getConfiguration(); + if (configuration != null) { + userPortalConfigService.setAllowUserHome(configuration.getSidebar().isAllowUserCustomHome()); + } + } + + /** + * @return {@link NavigationConfiguration} with the complete configuration of + * Navigation + */ + public NavigationConfiguration getConfiguration() { + return getConfiguration(null, 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(String username, Locale locale, boolean resolve) { + NavigationConfiguration configuration = navigationConfigurationStorage.getConfiguration(defaultTopbarApplications); + if (configuration == null) { + return null; + } else { + configuration.getSidebar() + .setItems(configuration.getSidebar() + .getItems() + .stream() + .filter(item -> !resolve || getPlugin(item.getType()).itemExists(item, username)) + .map(item -> resolve ? expandSidebarItem(item, username, locale) : item) + .toList()); + return configuration; + } + } + + /** + * @param username User name + * @param locale {@link Locale} to compute Menu item names + * @return {@link TopbarConfiguration} switch user role and customized + * settings + */ + public TopbarConfiguration getTopbarConfiguration(String username, Locale locale) { + return getConfiguration(username, locale, true).getTopbar(); + } + + /** + * @param username User name + * @param locale {@link Locale} to compute Menu item names + * @return {@link SidebarConfiguration} switch user role and customized + * settings + */ + public SidebarConfiguration getSidebarConfiguration(String username, Locale locale) { + SidebarConfiguration sidebarConfiguration = getConfiguration(username, locale, true).getSidebar(); + if (StringUtils.isNotBlank(username)) { + SidebarMode mode = getSidebarUserMode(username, sidebarConfiguration); + sidebarConfiguration.setUserMode(mode); + } + return sidebarConfiguration; + } + + /** + * Retrieves the preferred mode of sidebar by a user + * + * @param username User name as identifier + * @return preferred {@link SidebarMode} or default if not set by user yet + */ + public SidebarMode getSidebarUserMode(String username) { + NavigationConfiguration configuration = navigationConfigurationStorage.getConfiguration(defaultTopbarApplications); + return getSidebarUserMode(username, configuration.getSidebar()); + } + + /** + * Updates the preferred mode of sidebar by the user + * + * @param username User name as identifier + * @param mode Preferred {@link SidebarMode} by the user + */ + public void updateSidebarUserMode(String username, SidebarMode mode) { + navigationConfigurationStorage.updateSidebarUserMode(username, mode); + } + + /** + * Updates the Navigation configuration + * + * @param navigationConfiguration + */ + public void updateConfiguration(NavigationConfiguration navigationConfiguration) { + NavigationConfiguration existingConfiguration = getConfiguration(); + try { + navigationConfigurationStorage.updateConfiguration(navigationConfiguration); + listenerService.broadcast(NAVIGATION_CONFIGURATION_UPDATED_EVENT, existingConfiguration, navigationConfiguration); + } finally { + userPortalConfigService.setAllowUserHome(navigationConfiguration.getSidebar().isAllowUserCustomHome()); + } + } + + /** + * @return Default Topbar Applications as configured in {@link AddOnService} + */ + public List getDefaultTopbarApplications() { + if (defaultTopbarApplications == null) { + defaultTopbarApplications = addonContainerService.getApplications(TOP_NAVIGATION_ADDON_CONTAINER) + .stream() + .map(this::toTopbarApplication) + .toList(); + } + return defaultTopbarApplications; + } + + private TopbarApplication toTopbarApplication(Application app) { + String portletName = app.getState() instanceof TransientApplicationState applicationState ? + applicationState.getContentId() + .split("/")[1] : + "-"; + String applicationId = StringUtils.firstNonBlank(app.getId(), portletName); + String applicationTitle = StringUtils.firstNonBlank(app.getTitle(), portletName); + String applicationDescription = StringUtils.firstNonBlank(app.getDescription(), "-"); + String applicationIcon = StringUtils.firstNonBlank(app.getIcon(), "far fa-question-circle"); + return new TopbarApplication(applicationId, + applicationTitle, + applicationDescription, + applicationIcon, + TopbarItemType.APP, + !StringUtils.equals(environment.getProperty(String.format(TOPBAR_APPLICATION_ENABLED_PATTERN, + app.getId()), + "true"), + "false"), + !StringUtils.equals(environment.getProperty(String.format(TOPBAR_APPLICATION_MOBILE_PATTERN, + app.getId()), + "true"), + "false"), + Collections.singletonMap(TopbarApplication.CONTENT_ID_PROP_NAME, + ((TransientApplicationState) app.getState()).getContentId())); + } + + private SidebarMode getSidebarUserMode(String username, SidebarConfiguration sidebarConfiguration) { + SidebarMode mode = navigationConfigurationStorage.getSidebarUserMode(username); + if (sidebarConfiguration != null + && (mode == null + || !sidebarConfiguration.getAllowedModes().contains(mode))) { + mode = sidebarConfiguration.getDefaultMode(); + } + return mode; + } + + @SneakyThrows + private SidebarItem expandSidebarItem(SidebarItem item, String username, Locale locale) { + return getPlugin(item.getType()).resolveProperties(item, username, 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..ee7f0459673 --- /dev/null +++ b/component/core/src/main/java/io/meeds/social/navigation/storage/NavigationConfigurationStorage.java @@ -0,0 +1,176 @@ +/** + * 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.Collections; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.collections4.CollectionUtils; +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.SidebarMode; +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 String SETTING_USER_MODE_KEY = "sidebarMode"; + + private static final Context SETTING_GLOBAL_CONTEXT = Context.GLOBAL.id("NavigationConfiguration"); + + private static final Scope SETTING_SCOPE = Scope.APPLICATION.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.clone(); + } + + public void updateConfiguration(NavigationConfiguration navigationConfiguration) { + try { + settingService.set(SETTING_GLOBAL_CONTEXT, + SETTING_SCOPE, + SETTING_KEY, + SettingValue.create(JsonUtils.toJsonString(navigationConfiguration))); + } finally { + this.navigationConfiguration = null; + } + } + + public SidebarMode getSidebarUserMode(String username) { + SettingValue settingValue = settingService.get(Context.USER.id(username), + SETTING_SCOPE, + SETTING_USER_MODE_KEY); + return settingValue == null || settingValue.getValue() == null ? null : + SidebarMode.valueOf(settingValue.getValue().toString()); + } + + public void updateSidebarUserMode(String username, SidebarMode mode) { + settingService.set(Context.USER.id(username), + SETTING_SCOPE, + SETTING_USER_MODE_KEY, + SettingValue.create(mode.name())); + } + + private NavigationConfiguration retrieveNavigationConfiguration(List defaultApplications) { + SettingValue settingValue = settingService.get(SETTING_GLOBAL_CONTEXT, SETTING_SCOPE, SETTING_KEY); + if (settingValue == null || settingValue.getValue() == null) { + return NULL_VALUE; + } else { + NavigationConfiguration configuration = JsonUtils.fromJsonString(settingValue.getValue().toString(), + NavigationConfiguration.class); + if (defaultApplications == null) { + defaultApplications = Collections.emptyList(); + } + addMissingTopbarApplication(configuration, defaultApplications); + removeDroppedApplications(configuration, defaultApplications); + updateApplicationIds(configuration, defaultApplications); + return configuration; + } + } + + /** + * Remove applications which aren't available in addon container anymore + * + * @param configuration + * @param topbarApplications + * @param addonContainerApplications + */ + private void removeDroppedApplications(NavigationConfiguration configuration, + List addonContainerApplications) { + List topbarApplications = configuration.getTopbar().getApplications(); + List topbarApplicationsToRemove = topbarApplications.stream() + .filter(topbarApp -> topbarApp.getType() + == TopbarItemType.APP + && addonContainerApplications.stream() + .noneMatch(containerApp -> Objects.equals(containerApp, + topbarApp))) + .toList(); + if (CollectionUtils.isNotEmpty(topbarApplicationsToRemove)) { + List mergedApplications = new ArrayList<>(topbarApplications); + mergedApplications.removeAll(topbarApplicationsToRemove); + configuration.getTopbar().setApplications(mergedApplications); + } + } + + /** + * Add applications which are newly made available in addon container + * + * @param configuration + * @param topbarApplications + * @param addonContainerApplications + */ + private void addMissingTopbarApplication(NavigationConfiguration configuration, + List addonContainerApplications) { + List topbarApplications = configuration.getTopbar().getApplications(); + List topbarApplicationsToAdd = addonContainerApplications.stream() + .filter(containerApp -> topbarApplications.stream() + .noneMatch(topbarApp -> Objects.equals(containerApp, + topbarApp))) + .toList(); + if (CollectionUtils.isNotEmpty(topbarApplicationsToAdd)) { + List mergedApplications = new ArrayList<>(topbarApplications); + mergedApplications.addAll(topbarApplicationsToAdd); + configuration.getTopbar().setApplications(mergedApplications); + } + } + + /** + * This will update the topbar applications by the dynamic container + * regenerated id after each startup + * + * @param configuration + * @param defaultApplications + */ + private void updateApplicationIds(NavigationConfiguration configuration, List defaultApplications) { + defaultApplications.forEach(defaultApp -> { + List topbarApplications = configuration.getTopbar().getApplications(); + TopbarApplication application = topbarApplications.stream() + .filter(topbarApplication -> Objects.equals(topbarApplication, + defaultApp)) + .findFirst() + .orElse(null); + if (application != null) { + application.setId(defaultApp.getId()); + } + }); + } + +} 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..9fda8e67ed7 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 @@ -331,15 +331,15 @@ public ListAccess getLastAccessedSpace(String username) { } @Override - public ListAccess getVisitedSpaces(String username) { + public ListAccess getLastAccessedSpaceByFilter(String username, SpaceFilter spaceFilter) { if (StringUtils.isBlank(username) || IdentityConstants.ANONIM.equals(username)) { return new ListAccessImpl<>(Space.class, Collections.emptyList()); } return new SpaceListAccess(spaceStorage, spaceSearchConnector, - getSpaceTemplateService(), username, - SpaceListAccessType.VISITED); + spaceFilter, + SpaceListAccessType.LASTEST_ACCESSED); } @Override @@ -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/core/src/main/java/org/exoplatform/social/core/jpa/storage/SpaceStorage.java b/component/core/src/main/java/org/exoplatform/social/core/jpa/storage/SpaceStorage.java index b715921d32e..81de26f2b61 100644 --- a/component/core/src/main/java/org/exoplatform/social/core/jpa/storage/SpaceStorage.java +++ b/component/core/src/main/java/org/exoplatform/social/core/jpa/storage/SpaceStorage.java @@ -389,13 +389,6 @@ public int getVisibleSpacesCount(String userId, SpaceFilter spaceFilter) throws return getSpacesCount(userId, null, xFilter); } - public List getVisitedSpaces(SpaceFilter spaceFilter, int offset, int limit) throws SpaceStorageException { - XSpaceFilter xFilter = new XSpaceFilter(); - xFilter.setSpaceFilter(spaceFilter); - xFilter.setVisited(true); - return getSpaces(spaceFilter.getRemoteId(), SpaceMembershipStatus.MEMBER, xFilter, offset, limit); - } - public Instant getSpaceMembershipDate(long spaceId, String username) { return spaceMemberDAO.getSpaceMembershipDate(spaceId, username); } diff --git a/component/core/src/main/java/org/exoplatform/social/core/jpa/storage/dao/jpa/SpaceDAO.java b/component/core/src/main/java/org/exoplatform/social/core/jpa/storage/dao/jpa/SpaceDAO.java index 475e513a588..e5308d3f45b 100644 --- a/component/core/src/main/java/org/exoplatform/social/core/jpa/storage/dao/jpa/SpaceDAO.java +++ b/component/core/src/main/java/org/exoplatform/social/core/jpa/storage/dao/jpa/SpaceDAO.java @@ -188,7 +188,7 @@ public List findSpaceIdsByFilter(XSpaceFilter filter, long offset, long li if (CollectionUtils.isEmpty(result)) { return Collections.emptyList(); } else { - return result.stream().map(t -> Long.parseLong(((Object[]) t.get(0))[0].toString())).toList(); + return result.stream().map(t -> t.get(0, Long.class)).toList(); } } @@ -283,9 +283,15 @@ private String getQueryFilterContent(XSpaceFilter spaceFilter, List parameterNames, boolean count) { String querySelect = count ? "SELECT COUNT(DISTINCT s.id) FROM SocSpaceEntity s " : - "SELECT DISTINCT(s.id, " + getSortField(spaceFilter) + ") FROM SocSpaceEntity s "; - if (parameterNames.contains(PARAM_USER_ID) || spaceFilter.isLastAccess()) { - querySelect += " INNER JOIN s.members sm "; + "SELECT DISTINCT(s.id), " + getSortField(spaceFilter) + " FROM SocSpaceEntity s "; + boolean lastAccess = isLastAccess(spaceFilter); + if (parameterNames.contains(PARAM_USER_ID) || lastAccess) { + if (StringUtils.isNotBlank(spaceFilter.getRemoteId()) && lastAccess) { + querySelect += " INNER JOIN s.members sm ON sm.userId = :userId AND sm.status = io.meeds.social.space.constant.SpaceMembershipStatus.MEMBER "; + parameterNames.add(PARAM_USER_ID); + } else { + querySelect += " INNER JOIN s.members sm "; + } } String queryContent; @@ -296,7 +302,7 @@ private String getQueryFilterContent(XSpaceFilter spaceFilter, } if (!count) { queryContent += " ORDER BY " + getSortField(spaceFilter) + - (spaceFilter.isLastAccess() || spaceFilter.getSorting().orderBy.equals(Sorting.OrderBy.DESC) ? " DESC " : " ASC "); + (isSortDescending(spaceFilter) ? " DESC " : " ASC "); } return queryContent; } @@ -412,11 +418,10 @@ private void buildPermissionPredicates(XSpaceFilter spaceFilter, // NOSONAR } private void buildSortSuffixes(XSpaceFilter spaceFilter, List suffixes) { - Sorting sorting = spaceFilter.getSorting(); String sortField = getSortField(spaceFilter); suffixes.add("OrderBy"); suffixes.add(StringUtils.capitalize(sortField.replace("s.", "").replace("sm.", ""))); - if (sorting.orderBy.equals(Sorting.OrderBy.DESC)) { + if (isSortDescending(spaceFilter)) { suffixes.add("DESC"); } else { suffixes.add("ASC"); @@ -425,7 +430,7 @@ private void buildSortSuffixes(XSpaceFilter spaceFilter, List suffixes) private String getSortField(XSpaceFilter spaceFilter) { Sorting sorting = spaceFilter.getSorting(); - if (spaceFilter.isLastAccess()) { + if (isLastAccess(spaceFilter)) { return "sm.lastAccess"; } else if (sorting.sortBy.equals(SortBy.DATE)) { return "s.createdDate"; @@ -434,4 +439,15 @@ private String getSortField(XSpaceFilter spaceFilter) { } } + private boolean isSortDescending(XSpaceFilter spaceFilter) { + return isLastAccess(spaceFilter) + || spaceFilter.getSorting() == null + || spaceFilter.getSorting().orderBy.equals(Sorting.OrderBy.DESC); + } + + private boolean isLastAccess(XSpaceFilter spaceFilter) { + return spaceFilter.isLastAccess() + || (spaceFilter.getSorting() != null && spaceFilter.getSorting().sortBy.equals(SortBy.LASTVISITED)); + } + } diff --git a/component/core/src/main/java/org/exoplatform/social/core/space/SpaceListAccess.java b/component/core/src/main/java/org/exoplatform/social/core/space/SpaceListAccess.java index 4ba8735f116..7ee20a38618 100644 --- a/component/core/src/main/java/org/exoplatform/social/core/space/SpaceListAccess.java +++ b/component/core/src/main/java/org/exoplatform/social/core/space/SpaceListAccess.java @@ -259,9 +259,6 @@ public Space[] load(int offset, int limit) { // NOSONAR case LASTEST_ACCESSED: listSpaces = spaceStorage.getLastAccessedSpace(this.getSpaceFilter(), offset, limit); break; - case VISITED: - listSpaces = spaceStorage.getVisitedSpaces(this.getSpaceFilter(), offset, limit); - break; case COMMON: listSpaces = spaceStorage.getCommonSpaces(this.userId, this.otherUserId, offset, limit); break; diff --git a/component/core/src/main/java/org/exoplatform/social/core/space/SpaceListAccessType.java b/component/core/src/main/java/org/exoplatform/social/core/space/SpaceListAccessType.java index 87bf781a32f..8ad911e48ff 100644 --- a/component/core/src/main/java/org/exoplatform/social/core/space/SpaceListAccessType.java +++ b/component/core/src/main/java/org/exoplatform/social/core/space/SpaceListAccessType.java @@ -52,8 +52,6 @@ public enum SpaceListAccessType { LASTEST_ACCESSED, /** Provides Relationship of Users requesting to join a Space */ PENDING_REQUESTS, - /** Gets the spaces which are visited at least once */ - VISITED, /** Gets the common spaces between two users */ COMMON } diff --git a/component/core/src/main/java/org/exoplatform/social/core/storage/cache/CachedSpaceStorage.java b/component/core/src/main/java/org/exoplatform/social/core/storage/cache/CachedSpaceStorage.java index bcf219b8f0e..a96cdfc8386 100644 --- a/component/core/src/main/java/org/exoplatform/social/core/storage/cache/CachedSpaceStorage.java +++ b/component/core/src/main/java/org/exoplatform/social/core/storage/cache/CachedSpaceStorage.java @@ -601,7 +601,7 @@ public void updateSpaceAccessed(String remoteId, Space space) throws SpaceStorag spacesCache.select(selector); } catch (Exception e) { LOG.error("Error while removing cache entries for remoteId=" + remoteId + ", space=" + space.getDisplayName() + - " and type=" + SpaceType.LATEST_ACCESSED.name() + " or type=" + SpaceType.VISITED, e); + " and type=" + SpaceType.LATEST_ACCESSED.name(), e); } } @@ -648,25 +648,6 @@ public List getLastSpaces(final int limit) { return buildSpaces(keys); } - @Override - public List getVisitedSpaces(final SpaceFilter filter, final int offset, final int limit) throws SpaceStorageException { - // - SpaceFilterKey key = new SpaceFilterKey(filter.getRemoteId(), filter, SpaceType.VISITED); - ListSpacesKey listKey = new ListSpacesKey(key, offset, limit); - - // - ListSpacesData keys = spacesFutureCache.get(() -> { - if (limit == 0) { - return buildIds(Collections.emptyList()); - } - List got = CachedSpaceStorage.super.getVisitedSpaces(filter, offset, limit); - return buildIds(got); - }, listKey); - - // - return buildSpaces(keys); - } - @Override public List getMemberRoleSpaceIdentityIds(String identityId, int offset, int limit) throws SpaceStorageException { // diff --git a/component/core/src/main/java/org/exoplatform/social/core/storage/cache/model/key/SpaceFilterKey.java b/component/core/src/main/java/org/exoplatform/social/core/storage/cache/model/key/SpaceFilterKey.java index 4283fb30a46..fbc70151fb7 100644 --- a/component/core/src/main/java/org/exoplatform/social/core/storage/cache/model/key/SpaceFilterKey.java +++ b/component/core/src/main/java/org/exoplatform/social/core/storage/cache/model/key/SpaceFilterKey.java @@ -31,10 +31,13 @@ public class SpaceFilterKey implements CacheKey { private final String userId; + private long templateId; + private final int hash; public SpaceFilterKey(String userId, SpaceFilter filter, SpaceType type) { this.hash = Objects.hash(filter); + this.templateId = filter == null ? 0 : filter.getTemplateId(); this.type = type; this.userId = userId; } diff --git a/component/core/src/main/java/org/exoplatform/social/core/storage/cache/model/key/SpaceType.java b/component/core/src/main/java/org/exoplatform/social/core/storage/cache/model/key/SpaceType.java index b08f098b57e..d282c67d5d8 100644 --- a/component/core/src/main/java/org/exoplatform/social/core/storage/cache/model/key/SpaceType.java +++ b/component/core/src/main/java/org/exoplatform/social/core/storage/cache/model/key/SpaceType.java @@ -31,7 +31,6 @@ public enum SpaceType { VISIBLE, ALL, LATEST_ACCESSED, - VISITED, MEMBER_IDENTITY_IDS, MEMBER_IDS } diff --git a/component/core/src/main/java/org/exoplatform/social/core/storage/cache/selector/LastAccessedSpacesCacheSelector.java b/component/core/src/main/java/org/exoplatform/social/core/storage/cache/selector/LastAccessedSpacesCacheSelector.java index 45879c8a03d..6fbc4d15ae5 100644 --- a/component/core/src/main/java/org/exoplatform/social/core/storage/cache/selector/LastAccessedSpacesCacheSelector.java +++ b/component/core/src/main/java/org/exoplatform/social/core/storage/cache/selector/LastAccessedSpacesCacheSelector.java @@ -14,13 +14,15 @@ import org.exoplatform.social.core.storage.cache.model.key.SpaceType; /** - * Cache selector for last accessed spaces. - * It select all cache entries for the given userId and for space type LATEST_ACCESSED or VISITED. + * Cache selector for last accessed spaces. It select all cache entries for the + * given userId and for space type LATEST_ACCESSED or VISITED. */ public class LastAccessedSpacesCacheSelector extends CacheSelector { private String remoteId; + private long templateId; + private Space space; private SocialStorageCacheService cacheService; @@ -35,18 +37,13 @@ public LastAccessedSpacesCacheSelector(String remoteId, Space space, SocialStora @Override public boolean select(ListSpacesKey listSpacesKey, ObjectCacheInfo objectCacheInfo) { - if(listSpacesKey == null) { + if (listSpacesKey == null || listSpacesKey.getKey() == null) { return false; + } else { + SpaceFilterKey spaceFilterKey = listSpacesKey.getKey(); + return remoteId.equals(spaceFilterKey.getUserId()) + && SpaceType.LATEST_ACCESSED.equals(spaceFilterKey.getType()); } - - SpaceFilterKey spaceFilterKey = listSpacesKey.getKey(); - if(spaceFilterKey == null) { - return false; - } - - return remoteId.equals(spaceFilterKey.getUserId()) - && (SpaceType.LATEST_ACCESSED.equals(spaceFilterKey.getType()) - || SpaceType.VISITED.equals(spaceFilterKey.getType())); } @SuppressWarnings("unchecked") @@ -62,7 +59,9 @@ public void onSelect(ExoCache updateStore = false; return; } else if (listSpacesKey.getOffset() == 0 - && SpaceType.LATEST_ACCESSED.equals(listSpacesKey.getKey().getType())) { + && SpaceType.LATEST_ACCESSED.equals(listSpacesKey.getKey().getType()) + && (listSpacesKey.getKey().getTemplateId() == 0 + || templateId == listSpacesKey.getKey().getTemplateId())) { SpaceKey spaceKey = new SpaceKey(space.getSpaceId()); ids = new ArrayList<>(ids); if (ids.contains(spaceKey)) { diff --git a/component/core/src/test/java/io/meeds/social/navigation/AbstractNavigationConfigurationTest.java b/component/core/src/test/java/io/meeds/social/navigation/AbstractNavigationConfigurationTest.java new file mode 100644 index 00000000000..05caf478921 --- /dev/null +++ b/component/core/src/test/java/io/meeds/social/navigation/AbstractNavigationConfigurationTest.java @@ -0,0 +1,145 @@ +/** + * 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; + +import org.junit.After; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl; +import org.exoplatform.component.test.ConfigurationUnit; +import org.exoplatform.component.test.ConfiguredBy; +import org.exoplatform.component.test.ContainerScope; +import org.exoplatform.portal.config.UserACL; +import org.exoplatform.portal.mop.service.LayoutService; +import org.exoplatform.portal.mop.service.NavigationService; +import org.exoplatform.social.core.jpa.test.AbstractCoreTest; + +import io.meeds.kernel.test.AbstractSpringTest; +import io.meeds.social.navigation.plugin.LinkSidebarPlugin; +import io.meeds.social.navigation.plugin.PageSidebarPlugin; +import io.meeds.social.navigation.plugin.SiteSidebarPlugin; +import io.meeds.social.navigation.plugin.SpaceListSidebarPlugin; +import io.meeds.social.navigation.plugin.SpaceTemplateSidebarPlugin; +import io.meeds.social.navigation.service.NavigationConfigurationService; +import io.meeds.social.space.template.entity.SpaceTemplateEntity; +import io.meeds.social.space.template.model.SpaceTemplate; +import io.meeds.social.space.template.service.SpaceTemplateService; +import io.meeds.social.space.template.storage.SpaceTemplateStorage; +import io.meeds.social.space.template.utils.EntityMapper; +import io.meeds.spring.AvailableIntegration; + +import lombok.SneakyThrows; + +@SpringBootApplication(scanBasePackages = { + AbstractNavigationConfigurationTest.MODULE_NAME, + AvailableIntegration.KERNEL_TEST_MODULE, + AvailableIntegration.JPA_MODULE, +}, exclude = { + LiquibaseAutoConfiguration.class, +}) +@ConfiguredBy({ + @ConfigurationUnit(scope = ContainerScope.ROOT, path = "conf/configuration.xml"), + @ConfigurationUnit(scope = ContainerScope.ROOT, path = "conf/exo.social.component.core-local-root-configuration.xml"), + @ConfigurationUnit(scope = ContainerScope.PORTAL, path = "conf/portal/configuration.xml"), + @ConfigurationUnit(scope = ContainerScope.PORTAL, path = "conf/exo.social.component.core-local-configuration.xml"), + @ConfigurationUnit(scope = ContainerScope.PORTAL, path = "conf/social.component.core-local-navigation-portal-configuration.xml"), +}) +@RunWith(SpringRunner.class) +public abstract class AbstractNavigationConfigurationTest extends AbstractCoreTest { + + public static final String MODULE_NAME = "io.meeds.social.navigation"; + + protected static final String SITE_NAME = "contribute"; + + private SpaceTemplateDAO spaceTemplateDao = new SpaceTemplateDAO(); + + @Autowired + protected LayoutService layoutService; + + @Autowired + protected NavigationService navigationService; + + @Autowired + protected SpaceTemplateService spaceTemplateService; + + @Autowired + protected SpaceTemplateStorage spaceTemplateStorage; + + @Autowired + protected UserACL userAcl; + + @Autowired + protected LinkSidebarPlugin linkSidebarPlugin; + + @Autowired + protected PageSidebarPlugin pageSidebarPlugin; + + @Autowired + protected SiteSidebarPlugin siteSidebarPlugin; + + @Autowired + protected SpaceListSidebarPlugin spaceListSidebarPlugin; + + @Autowired + protected SpaceTemplateSidebarPlugin spaceTemplateSidebarPlugin; + + @Autowired + protected NavigationConfigurationService navigationConfigurationService; + + protected SpaceTemplate spaceTemplate; + + public AbstractNavigationConfigurationTest() { + AbstractSpringTest.setTestClass(this.getClass()); + } + + @Before + public void beforeEach() throws Exception { + setUp(); + begin(); + } + + @After + public void afterEach() throws Exception { + end(); + tearDown(); + } + + @SneakyThrows + public SpaceTemplate mockSpaceTemplate() { + spaceTemplate = spaceTemplateService.getSpaceTemplates().getFirst(); + if (spaceTemplateDao.find(spaceTemplate.getId()) == null) { + SpaceTemplateEntity spaceTemplateEntity = EntityMapper.toEntity(spaceTemplate); + spaceTemplateEntity.setId(null); + spaceTemplateEntity = spaceTemplateDao.create(spaceTemplateEntity); + if (spaceTemplate.getId() != spaceTemplateEntity.getId()) { + spaceTemplate.setId(spaceTemplateEntity.getId()); + spaceTemplateStorage.updateSpaceTemplate(spaceTemplate); + } + } + return spaceTemplate; + } + + public static class SpaceTemplateDAO extends GenericDAOJPAImpl { + } +} diff --git a/component/core/src/test/java/io/meeds/social/navigation/InitContainerTestSuite.java b/component/core/src/test/java/io/meeds/social/navigation/InitContainerTestSuite.java new file mode 100644 index 00000000000..cdc99d1ef05 --- /dev/null +++ b/component/core/src/test/java/io/meeds/social/navigation/InitContainerTestSuite.java @@ -0,0 +1,57 @@ +/** + * 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; + +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.container.PortalContainer; + +import io.meeds.social.navigation.listener.NavigationConfigurationSiteDisplayListenerTest; +import io.meeds.social.navigation.plugin.LinkSidebarPluginTest; +import io.meeds.social.navigation.plugin.PageSidebarPluginTest; +import io.meeds.social.navigation.plugin.SiteSidebarPluginTest; +import io.meeds.social.navigation.plugin.SpaceListSidebarPluginTest; +import io.meeds.social.navigation.plugin.SpaceTemplateSidebarPluginTest; +import io.meeds.social.navigation.service.NavigationConfigurationServiceTest; + +@RunWith(Suite.class) +@SuiteClasses({ + LinkSidebarPluginTest.class, + PageSidebarPluginTest.class, + SiteSidebarPluginTest.class, + SpaceListSidebarPluginTest.class, + SpaceTemplateSidebarPluginTest.class, + NavigationConfigurationServiceTest.class, + NavigationConfigurationSiteDisplayListenerTest.class, +}) +public class InitContainerTestSuite { + + @BeforeClass + public static void setUp() { + if (PortalContainer.getInstanceIfPresent() != null) { + PortalContainer.getInstance().stop(); + ExoContainerContext.setCurrentContainer(null); + } + } + +} diff --git a/component/core/src/test/java/io/meeds/social/navigation/listener/NavigationConfigurationSiteDisplayListenerTest.java b/component/core/src/test/java/io/meeds/social/navigation/listener/NavigationConfigurationSiteDisplayListenerTest.java new file mode 100644 index 00000000000..1c0bd30020f --- /dev/null +++ b/component/core/src/test/java/io/meeds/social/navigation/listener/NavigationConfigurationSiteDisplayListenerTest.java @@ -0,0 +1,83 @@ +/** + * 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.listener; + +import static io.meeds.social.navigation.plugin.AbstractLayoutSidebarPlugin.SITE_NAME_PROP_NAME; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +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 io.meeds.social.navigation.AbstractNavigationConfigurationTest; +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.NavigationConfiguration; +import io.meeds.social.navigation.model.SidebarItem; + +public class NavigationConfigurationSiteDisplayListenerTest extends AbstractNavigationConfigurationTest { + + @Test + public void testUpdateSiteDisplayedStatusByConfiguration() { + NavigationConfiguration configuration = navigationConfigurationService.getConfiguration(); + assertNotNull(configuration); + assertNotNull(configuration.getTopbar()); + assertNotNull(configuration.getSidebar()); + + SiteFilter siteFilter = new SiteFilter(); + siteFilter.setSiteType(SiteType.PORTAL); + List sites = layoutService.getSites(siteFilter); + assertNotNull(sites); + + List siteNames = getSiteNames(configuration); + String siteName = siteNames.get(0); + + List originalSidebarItems = configuration.getSidebar().getItems(); + List sidebarItems = new ArrayList<>(originalSidebarItems); + sidebarItems.removeIf(item -> item.getType() == SidebarItemType.SITE && siteName.equals(item.getProperties().get(SITE_NAME_PROP_NAME))); + + configuration.getSidebar().setItems(sidebarItems); + navigationConfigurationService.updateConfiguration(configuration); + + SiteKey standaloneSiteKey = SiteKey.portal(siteName); + + PortalConfig updatedSite = layoutService.getPortalConfig(standaloneSiteKey); + assertFalse(updatedSite.isDisplayed()); + + configuration.getSidebar().setItems(originalSidebarItems); + navigationConfigurationService.updateConfiguration(configuration); + + updatedSite = layoutService.getPortalConfig(standaloneSiteKey); + assertTrue(updatedSite.isDisplayed()); + } + + private List getSiteNames(NavigationConfiguration configuration) { + return configuration.getSidebar() + .getItems() + .stream() + .filter(item -> item.getType() == SidebarItemType.SITE) + .map(item -> item.getProperties().get(SITE_NAME_PROP_NAME)) + .toList(); + } + +} diff --git a/component/core/src/test/java/io/meeds/social/navigation/plugin/LinkSidebarPluginTest.java b/component/core/src/test/java/io/meeds/social/navigation/plugin/LinkSidebarPluginTest.java new file mode 100644 index 00000000000..900225551a4 --- /dev/null +++ b/component/core/src/test/java/io/meeds/social/navigation/plugin/LinkSidebarPluginTest.java @@ -0,0 +1,81 @@ +/** + * 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.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.junit.Test; + +import io.meeds.social.navigation.AbstractNavigationConfigurationTest; +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; +import io.meeds.social.util.JsonUtils; + +public class LinkSidebarPluginTest extends AbstractNavigationConfigurationTest { + + @Test + public void testGetType() { + assertEquals(SidebarItemType.LINK, linkSidebarPlugin.getType()); + } + + @Test + public void testGetDefaultItems() { + List defaultItems = linkSidebarPlugin.getDefaultItems(); + assertNotNull(defaultItems); + assertTrue(defaultItems.isEmpty()); + } + + @Test + public void testItemExists() { + assertTrue(linkSidebarPlugin.itemExists(null, null)); + } + + @Test + public void testResolveProperties() { + String enName = "Test EN"; + String frName = "Test FR"; + + Map names = new HashMap<>(); + names.put(Locale.ENGLISH.toLanguageTag(), enName); + names.put(Locale.FRENCH.toLanguageTag(), frName); + String namesJsonString = JsonUtils.toJsonString(names); + + SidebarItem item = new SidebarItem("fakeName", + "url", + "target", + "avatar", + "icon", + SidebarItemType.LINK, + null, + Collections.singletonMap(LinkSidebarPlugin.LINK_NAMES, namesJsonString)); + + SidebarItem result = linkSidebarPlugin.resolveProperties(item, null, Locale.ENGLISH); + assertNotNull(result); + assertEquals(enName, result.getName()); + + result = linkSidebarPlugin.resolveProperties(item, null, Locale.FRENCH); + assertNotNull(result); + assertEquals(frName, result.getName()); + } + +} diff --git a/component/core/src/test/java/io/meeds/social/navigation/plugin/PageSidebarPluginTest.java b/component/core/src/test/java/io/meeds/social/navigation/plugin/PageSidebarPluginTest.java new file mode 100644 index 00000000000..15387890afd --- /dev/null +++ b/component/core/src/test/java/io/meeds/social/navigation/plugin/PageSidebarPluginTest.java @@ -0,0 +1,115 @@ +/** + * 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 static io.meeds.social.navigation.plugin.AbstractLayoutSidebarPlugin.NODE_ID_PROP_NAME; +import static io.meeds.social.navigation.plugin.AbstractLayoutSidebarPlugin.SITE_DISPLAY_NAME_PROP_NAME; +import static io.meeds.social.navigation.plugin.AbstractLayoutSidebarPlugin.SITE_ICON_PROP_NAME; +import static io.meeds.social.navigation.plugin.AbstractLayoutSidebarPlugin.SITE_ID_PROP_NAME; +import static io.meeds.social.navigation.plugin.AbstractLayoutSidebarPlugin.SITE_NAME_PROP_NAME; +import static io.meeds.social.navigation.plugin.AbstractLayoutSidebarPlugin.SITE_TYPE_PROP_NAME; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import org.junit.Test; + +import org.exoplatform.portal.config.model.PortalConfig; +import org.exoplatform.portal.mop.SiteKey; +import org.exoplatform.portal.mop.navigation.NodeContext; + +import io.meeds.social.navigation.AbstractNavigationConfigurationTest; +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; + +public class PageSidebarPluginTest extends AbstractNavigationConfigurationTest { + + @Test + public void testGetType() { + assertEquals(SidebarItemType.PAGE, pageSidebarPlugin.getType()); + } + + @Test + public void testGetDefaultItems() { + List defaultItems = pageSidebarPlugin.getDefaultItems(); + assertNotNull(defaultItems); + assertTrue(defaultItems.isEmpty()); + } + + @Test + public void testItemExists() { + assertFalse(pageSidebarPlugin.itemExists(null, null)); + assertFalse(pageSidebarPlugin.itemExists(new SidebarItem(), null)); + + SidebarItem item = new SidebarItem(SidebarItemType.PAGE); + item.setProperties(Collections.singletonMap(NODE_ID_PROP_NAME, "3555")); + assertFalse(pageSidebarPlugin.itemExists(item, null)); + + NodeContext> node = navigationService.loadNode(SiteKey.portal(SITE_NAME)); + assertNotNull(node); + + item.setProperties(Collections.singletonMap(NODE_ID_PROP_NAME, node.get("a").getId())); + assertTrue(pageSidebarPlugin.itemExists(item, userAcl.getSuperUser())); + assertTrue(pageSidebarPlugin.itemExists(item, "demo")); + assertTrue(pageSidebarPlugin.itemExists(item, null)); + + item.setProperties(Collections.singletonMap(NODE_ID_PROP_NAME, node.get("b").getId())); + assertFalse(pageSidebarPlugin.itemExists(item, userAcl.getSuperUser())); + assertFalse(pageSidebarPlugin.itemExists(item, "demo")); + assertFalse(pageSidebarPlugin.itemExists(item, null)); + + item.setProperties(Collections.singletonMap(NODE_ID_PROP_NAME, node.get("c").getId())); + assertTrue(pageSidebarPlugin.itemExists(item, userAcl.getSuperUser())); + assertTrue(pageSidebarPlugin.itemExists(item, "demo")); + assertFalse(pageSidebarPlugin.itemExists(item, null)); + + item.setProperties(Collections.singletonMap(NODE_ID_PROP_NAME, node.get("d").getId())); + assertTrue(pageSidebarPlugin.itemExists(item, userAcl.getSuperUser())); + assertFalse(pageSidebarPlugin.itemExists(item, "demo")); + assertFalse(pageSidebarPlugin.itemExists(item, null)); + } + + @Test + public void testResolveProperties() { + SiteKey siteKey = SiteKey.portal(SITE_NAME); + + SidebarItem item = new SidebarItem(SidebarItemType.PAGE); + NodeContext> node = navigationService.loadNode(siteKey); + item.setProperties(Collections.singletonMap(NODE_ID_PROP_NAME, node.get("d").getId())); + pageSidebarPlugin.resolveProperties(item, userAcl.getSuperUser(), Locale.ENGLISH); + + assertEquals("d", item.getName()); + assertEquals("/portal/contribute/d", item.getUrl()); + assertNull(item.getTarget()); + assertNull(item.getAvatar()); + assertNull(item.getItems()); + assertEquals("d-icon", item.getIcon()); + + PortalConfig site = layoutService.getPortalConfig(siteKey); + String siteId = (site.getStorageId().split("_"))[1]; + + assertEquals(siteId, item.getProperties().get(SITE_ID_PROP_NAME)); + assertEquals(siteKey.getTypeName(), item.getProperties().get(SITE_TYPE_PROP_NAME)); + assertEquals(siteKey.getName(), item.getProperties().get(SITE_NAME_PROP_NAME)); + assertEquals("a-icon", item.getProperties().get(SITE_ICON_PROP_NAME)); + assertEquals(SITE_NAME, item.getProperties().get(SITE_DISPLAY_NAME_PROP_NAME)); + } + +} diff --git a/component/core/src/test/java/io/meeds/social/navigation/plugin/SiteSidebarPluginTest.java b/component/core/src/test/java/io/meeds/social/navigation/plugin/SiteSidebarPluginTest.java new file mode 100644 index 00000000000..a88499cc5ba --- /dev/null +++ b/component/core/src/test/java/io/meeds/social/navigation/plugin/SiteSidebarPluginTest.java @@ -0,0 +1,86 @@ +/** + * 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 static io.meeds.social.navigation.plugin.AbstractLayoutSidebarPlugin.SITE_NAME_PROP_NAME; +import static io.meeds.social.navigation.plugin.AbstractLayoutSidebarPlugin.SITE_TYPE_PROP_NAME; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; + +import io.meeds.social.navigation.AbstractNavigationConfigurationTest; +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; + +public class SiteSidebarPluginTest extends AbstractNavigationConfigurationTest { + + @Test + public void testGetType() { + assertEquals(SidebarItemType.SITE, siteSidebarPlugin.getType()); + } + + @Test + public void testGetDefaultItems() { + List defaultItems = siteSidebarPlugin.getDefaultItems(); + assertNotNull(defaultItems); + assertFalse(defaultItems.isEmpty()); + assertTrue(defaultItems.stream().anyMatch(item -> StringUtils.equals(item.getName(), "classic"))); + } + + @Test + public void testItemExists() { + assertFalse(siteSidebarPlugin.itemExists(null, null)); + assertFalse(siteSidebarPlugin.itemExists(new SidebarItem(), null)); + + SidebarItem item = new SidebarItem(SidebarItemType.SITE); + Map properties = new HashMap<>(); + item.setProperties(properties); + properties.put(SITE_TYPE_PROP_NAME, "portal"); + + properties.put(SITE_NAME_PROP_NAME, "notfound"); + assertFalse(siteSidebarPlugin.itemExists(item, null)); + + properties.put(SITE_NAME_PROP_NAME, SITE_NAME); + assertTrue(siteSidebarPlugin.itemExists(item, null)); + } + + @Test + public void testResolveProperties() { + SidebarItem item = new SidebarItem(SidebarItemType.SITE); + Map properties = new HashMap<>(); + item.setProperties(properties); + properties.put(SITE_TYPE_PROP_NAME, "portal"); + properties.put(SITE_NAME_PROP_NAME, SITE_NAME); + + siteSidebarPlugin.resolveProperties(item, userAcl.getSuperUser(), Locale.ENGLISH); + + assertEquals(SITE_NAME, item.getName()); + assertEquals("/portal/" + SITE_NAME, item.getUrl()); + assertNull(item.getTarget()); + assertNull(item.getAvatar()); + assertNull(item.getItems()); + assertEquals("a-icon", item.getIcon()); + } + +} diff --git a/component/core/src/test/java/io/meeds/social/navigation/plugin/SpaceListSidebarPluginTest.java b/component/core/src/test/java/io/meeds/social/navigation/plugin/SpaceListSidebarPluginTest.java new file mode 100644 index 00000000000..f05b06dad6c --- /dev/null +++ b/component/core/src/test/java/io/meeds/social/navigation/plugin/SpaceListSidebarPluginTest.java @@ -0,0 +1,114 @@ +/** + * 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 static io.meeds.social.navigation.plugin.SpaceListSidebarPlugin.SPACES_NAMES; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Before; +import org.junit.Test; + +import org.exoplatform.social.core.space.model.Space; + +import io.meeds.social.navigation.AbstractNavigationConfigurationTest; +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; +import io.meeds.social.util.JsonUtils; + +public class SpaceListSidebarPluginTest extends AbstractNavigationConfigurationTest { + + @Before + @Override + public void beforeEach() throws Exception { + super.beforeEach(); + for (int i = 0; i < 5; i++) { + Space space = new Space(); + space.setRegistration(Space.OPEN); + space.setVisibility(Space.PUBLIC); + spaceService.createSpace(space, userAcl.getSuperUser()); + } + } + + @Test + public void testGetType() { + assertEquals(SidebarItemType.SPACES, spaceListSidebarPlugin.getType()); + } + + @Test + public void testGetDefaultItems() { + List defaultItems = spaceListSidebarPlugin.getDefaultItems(); + assertNotNull(defaultItems); + assertTrue(defaultItems.stream() + .anyMatch(item -> StringUtils.contains(item.getProperties().get(SPACES_NAMES), + "sidebar.viewAllSpaces"))); + } + + @Test + public void testItemExists() { + assertFalse(spaceListSidebarPlugin.itemExists(null, null)); + assertFalse(spaceListSidebarPlugin.itemExists(new SidebarItem(), null)); + SidebarItem item = new SidebarItem(SidebarItemType.SPACES); + assertFalse(spaceListSidebarPlugin.itemExists(item, null)); + + item.setProperties(Collections.singletonMap(SPACES_NAMES, "{}")); + assertTrue(spaceListSidebarPlugin.itemExists(item, null)); + } + + @Test + public void testResolveProperties() { + String enName = "Test EN"; + String frName = "Test FR"; + + Map names = new HashMap<>(); + names.put(Locale.ENGLISH.toLanguageTag(), enName); + names.put(Locale.FRENCH.toLanguageTag(), frName); + String namesJsonString = JsonUtils.toJsonString(names); + + SidebarItem item = new SidebarItem("fakeName", + "url", + "target", + "avatar", + "icon", + SidebarItemType.SPACES, + null, + Collections.singletonMap(SPACES_NAMES, namesJsonString)); + + SidebarItem result = spaceListSidebarPlugin.resolveProperties(item, null, Locale.ENGLISH); + assertNotNull(result); + assertEquals(enName, result.getName()); + + result = spaceListSidebarPlugin.resolveProperties(item, null, Locale.FRENCH); + assertNotNull(result); + assertEquals(frName, result.getName()); + assertNotNull(result.getItems()); + assertTrue(result.getItems().isEmpty()); + + result = spaceListSidebarPlugin.resolveProperties(item, userAcl.getSuperUser(), Locale.FRENCH); + assertNotNull(result.getItems()); + assertEquals(AbstractSpaceSidebarPlugin.SPACES_LIMIT_DEFAULT, + item.getItems().size()); + } + +} diff --git a/component/core/src/test/java/io/meeds/social/navigation/plugin/SpaceTemplateSidebarPluginTest.java b/component/core/src/test/java/io/meeds/social/navigation/plugin/SpaceTemplateSidebarPluginTest.java new file mode 100644 index 00000000000..af7d5bc71a4 --- /dev/null +++ b/component/core/src/test/java/io/meeds/social/navigation/plugin/SpaceTemplateSidebarPluginTest.java @@ -0,0 +1,97 @@ +/** + * 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 static io.meeds.social.navigation.plugin.SpaceTemplateSidebarPlugin.SPACE_TEMPLATE_ID_PROP_NAME; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Before; +import org.junit.Test; + +import org.exoplatform.social.core.space.model.Space; + +import io.meeds.social.navigation.AbstractNavigationConfigurationTest; +import io.meeds.social.navigation.constant.SidebarItemType; +import io.meeds.social.navigation.model.SidebarItem; + +public class SpaceTemplateSidebarPluginTest extends AbstractNavigationConfigurationTest { + + @Before + @Override + public void beforeEach() throws Exception { + super.beforeEach(); + if (spaceTemplate == null) { + mockSpaceTemplate(); + for (int i = 0; i < 5; i++) { + Space space = new Space(); + space.setRegistration(Space.OPEN); + space.setVisibility(Space.PUBLIC); + space.setTemplateId(spaceTemplate.getId()); + spaceService.createSpace(space, userAcl.getSuperUser()); + } + } + } + + @Test + public void testGetType() { + assertEquals(SidebarItemType.SPACE_TEMPLATE, spaceTemplateSidebarPlugin.getType()); + } + + @Test + public void testGetDefaultItems() { + List defaultItems = spaceTemplateSidebarPlugin.getDefaultItems(); + assertNotNull(defaultItems); + assertTrue(defaultItems.stream() + .anyMatch(item -> item.getProperties() != null + && StringUtils.equals(item.getProperties().get(SPACE_TEMPLATE_ID_PROP_NAME), + String.valueOf(spaceTemplate.getId())))); + } + + @Test + public void testItemExists() { + assertFalse(spaceTemplateSidebarPlugin.itemExists(null, null)); + assertFalse(spaceTemplateSidebarPlugin.itemExists(new SidebarItem(), null)); + + SidebarItem item = new SidebarItem(SidebarItemType.SPACE_TEMPLATE); + item.setProperties(Collections.singletonMap(SPACE_TEMPLATE_ID_PROP_NAME, String.valueOf(spaceTemplate.getId()))); + assertTrue(spaceTemplateSidebarPlugin.itemExists(item, null)); + } + + @Test + public void testResolveProperties() { + SidebarItem item = new SidebarItem(SidebarItemType.SPACE_TEMPLATE); + item.setProperties(Collections.singletonMap(SPACE_TEMPLATE_ID_PROP_NAME, String.valueOf(spaceTemplate.getId()))); + spaceTemplateSidebarPlugin.resolveProperties(item, userAcl.getSuperUser(), Locale.ENGLISH); + + assertEquals(spaceTemplate.getName(), item.getName()); + assertEquals(spaceTemplate.getIcon(), item.getIcon()); + assertNull(item.getUrl()); + assertNull(item.getTarget()); + assertNull(item.getAvatar()); + + assertNotNull(item.getItems()); + assertEquals(AbstractSpaceSidebarPlugin.SPACES_LIMIT_DEFAULT, + item.getItems().size()); + } + +} diff --git a/component/core/src/test/java/io/meeds/social/navigation/service/NavigationConfigurationServiceTest.java b/component/core/src/test/java/io/meeds/social/navigation/service/NavigationConfigurationServiceTest.java new file mode 100644 index 00000000000..e5c8aea1a4b --- /dev/null +++ b/component/core/src/test/java/io/meeds/social/navigation/service/NavigationConfigurationServiceTest.java @@ -0,0 +1,102 @@ +/** + * 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.Locale; + +import org.junit.Before; +import org.junit.Test; + +import org.exoplatform.social.core.space.model.Space; + +import io.meeds.social.navigation.AbstractNavigationConfigurationTest; +import io.meeds.social.navigation.constant.SidebarMode; +import io.meeds.social.navigation.model.NavigationConfiguration; +import io.meeds.social.navigation.model.SidebarConfiguration; +import io.meeds.social.navigation.model.SidebarItem; +import io.meeds.social.navigation.model.TopbarConfiguration; + +public class NavigationConfigurationServiceTest extends AbstractNavigationConfigurationTest { + + @Before + @Override + public void beforeEach() throws Exception { + super.beforeEach(); + if (spaceTemplate == null) { + mockSpaceTemplate(); + for (int i = 0; i < 5; i++) { + Space space = new Space(); + space.setRegistration(Space.OPEN); + space.setVisibility(Space.PUBLIC); + space.setTemplateId(spaceTemplate.getId()); + spaceService.createSpace(space, userAcl.getSuperUser()); + } + } + } + + @Test + public void testGetConfiguration() { + NavigationConfiguration configuration = navigationConfigurationService.getConfiguration(); + assertNotNull(configuration); + assertNotNull(configuration.getTopbar()); + assertNotNull(configuration.getSidebar()); + } + + @Test + public void testGetConfigurationByUser() { + NavigationConfiguration configuration = navigationConfigurationService.getConfiguration(userAcl.getSuperUser(), + Locale.FRENCH, + true); + assertNotNull(configuration); + + TopbarConfiguration topbar = configuration.getTopbar(); + assertNotNull(topbar); + assertTrue(topbar.isDisplayCompanyName()); + assertTrue(topbar.isDisplaySiteName()); + + SidebarConfiguration sidebar = configuration.getSidebar(); + assertNotNull(sidebar); + assertTrue(sidebar.isAllowUserCustomHome()); + assertEquals(3, sidebar.getAllowedModes().size()); + assertEquals(SidebarMode.ICON, sidebar.getDefaultMode()); + assertNotNull(sidebar.getItems()); + assertFalse(sidebar.getItems().isEmpty()); + SidebarItem item = sidebar.getItems().getFirst(); + assertEquals("contribute", item.getName()); + } + + @Test + public void testSidebarUserMode() { + NavigationConfiguration configuration = navigationConfigurationService.getConfiguration(userAcl.getSuperUser(), + Locale.FRENCH, + true); + + SidebarMode sidebarUserMode = navigationConfigurationService.getSidebarUserMode(userAcl.getSuperUser()); + assertEquals(configuration.getSidebar().getDefaultMode(), sidebarUserMode); + + navigationConfigurationService.updateSidebarUserMode(userAcl.getSuperUser(), SidebarMode.STICKY); + sidebarUserMode = navigationConfigurationService.getSidebarUserMode(userAcl.getSuperUser()); + assertEquals(SidebarMode.STICKY, sidebarUserMode); + + navigationConfigurationService.updateSidebarUserMode(userAcl.getSuperUser(), SidebarMode.ICON); + sidebarUserMode = navigationConfigurationService.getSidebarUserMode(userAcl.getSuperUser()); + assertEquals(SidebarMode.ICON, sidebarUserMode); + } + +} diff --git a/component/core/src/test/java/io/meeds/social/upgrade/InitContainerTestSuite.java b/component/core/src/test/java/io/meeds/social/upgrade/InitContainerTestSuite.java index c942474e3bd..7b4b3cd0f85 100644 --- a/component/core/src/test/java/io/meeds/social/upgrade/InitContainerTestSuite.java +++ b/component/core/src/test/java/io/meeds/social/upgrade/InitContainerTestSuite.java @@ -24,6 +24,8 @@ import org.exoplatform.commons.testing.BaseExoContainerTestSuite; import org.exoplatform.commons.testing.ConfigTestCase; +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.container.PortalContainer; import io.meeds.social.space.service.SpaceLayoutServiceTest; @@ -37,6 +39,10 @@ public class InitContainerTestSuite extends BaseExoContainerTestSuite { @BeforeClass public static void setUp() throws Exception { + if (PortalContainer.getInstanceIfPresent() != null) { + PortalContainer.getInstance().stop(); + ExoContainerContext.setCurrentContainer(null); + } initConfiguration(InitContainerTestSuite.class); beforeSetup(); } diff --git a/component/core/src/test/java/org/exoplatform/social/core/jpa/storage/SpaceStorageTest.java b/component/core/src/test/java/org/exoplatform/social/core/jpa/storage/SpaceStorageTest.java index 86689c769f3..5eecdd813a6 100644 --- a/component/core/src/test/java/org/exoplatform/social/core/jpa/storage/SpaceStorageTest.java +++ b/component/core/src/test/java/org/exoplatform/social/core/jpa/storage/SpaceStorageTest.java @@ -1545,29 +1545,6 @@ public void testGetVisibleSpacesInvitedMember() throws Exception { } } - public void testVisited() throws Exception { - Space space0 = getSpaceInstance(5); - spaceStorage.saveSpace(space0, true); - Space space1 = getSpaceInstance(6); - spaceStorage.saveSpace(space1, true); - - SpaceFilter filter = new SpaceFilter(); - filter.setRemoteId("ghost"); - - List result = spaceStorage.getVisitedSpaces(filter, 0, -1); - assertEquals(2, result.size()); - assertEquals(space0.getId(), result.get(0).getId()); - - restartTransaction(); - spaceStorage.updateSpaceAccessed("ghost", space1); - - // getVisitedSpaces return a list of spaces that - // order by visited space then others - result = spaceStorage.getVisitedSpaces(filter, 0, -1); - assertEquals(2, result.size()); - assertEquals(space1.getId(), result.get(0).getId()); - } - public void testLastAccess() throws Exception { Space space2 = getSpaceInstance(7); spaceStorage.saveSpace(space2, true); diff --git a/component/core/src/test/java/org/exoplatform/social/core/jpa/test/InitContainerTestSuite.java b/component/core/src/test/java/org/exoplatform/social/core/jpa/test/InitContainerTestSuite.java index e1199cd0e68..a1d315ddaba 100644 --- a/component/core/src/test/java/org/exoplatform/social/core/jpa/test/InitContainerTestSuite.java +++ b/component/core/src/test/java/org/exoplatform/social/core/jpa/test/InitContainerTestSuite.java @@ -22,6 +22,8 @@ import org.exoplatform.commons.testing.BaseExoContainerTestSuite; import org.exoplatform.commons.testing.ConfigTestCase; +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.container.PortalContainer; import org.exoplatform.social.attachment.AttachmentServiceTest; import org.exoplatform.social.core.binding.spi.GroupSpaceBindingServiceTest; import org.exoplatform.social.core.binding.spi.RDBMSGroupSpaceBindingStorageTest; @@ -133,6 +135,10 @@ public class InitContainerTestSuite extends BaseExoContainerTestSuite { @BeforeClass public static void setUp() throws Exception { + if (PortalContainer.getInstanceIfPresent() != null) { + PortalContainer.getInstance().stop(); + ExoContainerContext.setCurrentContainer(null); + } initConfiguration(InitContainerTestSuite.class); beforeSetup(); } diff --git a/component/core/src/test/java/org/exoplatform/social/core/space/spi/SpaceServiceTest.java b/component/core/src/test/java/org/exoplatform/social/core/space/spi/SpaceServiceTest.java index 69aff7d96c2..b90c104b83c 100644 --- a/component/core/src/test/java/org/exoplatform/social/core/space/spi/SpaceServiceTest.java +++ b/component/core/src/test/java/org/exoplatform/social/core/space/spi/SpaceServiceTest.java @@ -2309,7 +2309,6 @@ private Space getSpaceInstance(int number) { Space space = new Space(); space.setDisplayName(MY_SPACE_DISPLAY_NAME_PREFIX + number); space.setPrettyName(space.getDisplayName()); - space.setRegistration(Space.OPEN); space.setDescription(SPACE_DESCRIPTION + number); space.setVisibility(Space.PUBLIC); space.setRegistration(Space.VALIDATION); diff --git a/component/core/src/test/resources/conf/exo.social.component.core-local-configuration.xml b/component/core/src/test/resources/conf/exo.social.component.core-local-configuration.xml index 4a5c3384e38..370ca22378e 100644 --- a/component/core/src/test/resources/conf/exo.social.component.core-local-configuration.xml +++ b/component/core/src/test/resources/conf/exo.social.component.core-local-configuration.xml @@ -47,6 +47,67 @@ io.meeds.social.richeditor.RichEditorConfigurationServiceImpl + + org.exoplatform.portal.config.UserACL + io.meeds.social.authorization.AuthorizationManager + + + super.user + administrator + root + + + + portal.administrator.groups + administrator + /platform/administrators + + + + portal.administrator.mstype + administrator + manager + + + + portal.creator.groups + groups with membership type have permission to manage portal + *:/platform/administrators + + + + navigation.creator.membership.type + specific membership type have full permission with group navigation + manager + + + + guests.group + guests group + /platform/guests + + + + mandatory.groups + Groups that can not be deleted. + /platform/administrators + /platform/users + /platform/guests + /spaces + + + + mandatory.mstypes + Membership type that can not be deleted. + member + publisher + redactor + manager + * + + + + io.meeds.social.translation.service.TranslationService @@ -1068,5 +1129,4 @@ org.exoplatform.portal.resource.SkinResourceRequestHandler - org.exoplatform.web.WebAppController diff --git a/component/core/src/test/resources/conf/social.component.core-local-navigation-portal-configuration.xml b/component/core/src/test/resources/conf/social.component.core-local-navigation-portal-configuration.xml new file mode 100644 index 00000000000..3f902cbc60a --- /dev/null +++ b/component/core/src/test/resources/conf/social.component.core-local-navigation-portal-configuration.xml @@ -0,0 +1,62 @@ + + + + + + org.exoplatform.portal.config.UserPortalConfigService + + new.portal.config.user.listener + initListener + org.exoplatform.portal.config.NewPortalConfigListener + + + portal.configuration + + + true + + + + + contribute + + + + + portal + + + overwrite + + + classpath:/portal/config + + + + + + + + diff --git a/component/core/src/test/resources/portal/config/portal/contribute/navigation.xml b/component/core/src/test/resources/portal/config/portal/contribute/navigation.xml new file mode 100644 index 00000000000..060fe04a676 --- /dev/null +++ b/component/core/src/test/resources/portal/config/portal/contribute/navigation.xml @@ -0,0 +1,52 @@ + + + + + + a + a-icon + DISPLAYED + portal::contribute::a + + + b + b-icon + HIDDEN + portal::contribute::b + + + c + c-icon + DISPLAYED + portal::contribute::c + + + d + d-icon + DISPLAYED + portal::contribute::d + + + diff --git a/component/core/src/test/resources/portal/config/portal/contribute/pages.xml b/component/core/src/test/resources/portal/config/portal/contribute/pages.xml new file mode 100644 index 00000000000..d5c4a82b7e5 --- /dev/null +++ b/component/core/src/test/resources/portal/config/portal/contribute/pages.xml @@ -0,0 +1,46 @@ + + + + + + a + + + + b + test_access_permissions + + + + c + *:/platform/users + + + + d + test_access_permissions + + + diff --git a/component/core/src/test/resources/portal/config/portal/contribute/portal.xml b/component/core/src/test/resources/portal/config/portal/contribute/portal.xml new file mode 100644 index 00000000000..55033b45c6f --- /dev/null +++ b/component/core/src/test/resources/portal/config/portal/contribute/portal.xml @@ -0,0 +1,33 @@ + + + + contribute + en + Everyone + *:/platform/administrators + + + + 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..2b5ed28aeab --- /dev/null +++ b/component/service/src/main/java/io/meeds/social/navigation/rest/NavigationConfigurationRest.java @@ -0,0 +1,112 @@ +/** + * 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.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import io.meeds.social.navigation.constant.SidebarMode; +import io.meeds.social.navigation.model.NavigationConfiguration; +import io.meeds.social.navigation.model.SidebarConfiguration; +import io.meeds.social.navigation.model.TopbarConfiguration; +import io.meeds.social.navigation.service.NavigationConfigurationService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +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("users") + @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.getRemoteUser(), request.getLocale(), true); + } + + @GetMapping("/topbar") + @Secured("users") + @Operation(summary = "Retrieve Topbar settings", + method = "GET", + description = "This retrieves the configuration of Topbar switch user roles") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Request fulfilled"), + }) + public TopbarConfiguration getTopbarConfiguration(HttpServletRequest request) { + return navigationConfigurationService.getTopbarConfiguration(request.getRemoteUser(), request.getLocale()); + } + + @GetMapping("/sidebar") + @Operation(summary = "Retrieve Sidebar settings", method = "GET", description = "This retrieves the configuration of Sidebar switch user roles") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Request fulfilled"), + }) + public SidebarConfiguration getSidebarConfiguration(HttpServletRequest request) { + return navigationConfigurationService.getSidebarConfiguration(request.getRemoteUser(), request.getLocale()); + } + + @PutMapping(path = "/sidebar/mode", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + @Secured("users") + @Operation(summary = "Retrieve Sidebar settings", method = "GET", description = "This retrieves the configuration of Sidebar switch user roles") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Request fulfilled"), + }) + public void updateSidebarUserMode(HttpServletRequest request, + @Parameter(description = "User preferred Sidebar Mode") + @RequestParam("mode") + SidebarMode mode) { + navigationConfigurationService.updateSidebarUserMode(request.getRemoteUser(), mode); + } + + @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..074b12085db 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 @@ -54,6 +54,7 @@ import io.meeds.social.translation.service.TranslationService; import jakarta.servlet.http.HttpServletRequest; +import lombok.SneakyThrows; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; @@ -790,21 +791,23 @@ public static CollectionEntity buildEntityFromSpaces(List spaces, int limit, String expand, UriInfo uriInfo) { - List spaceInfos = new ArrayList<>(); - for (Space space : spaces) { - SpaceEntity spaceInfo = buildEntityFromSpace(space, username, uriInfo.getPath(), expand); - spaceInfos.add(spaceInfo.getDataEntity()); - } + Map unreadItemsPerSpace = buildSpacesUnread(username, expand); + List spaceInfos = spaces.stream() + .map(space -> { + SpaceEntity spaceInfo = buildEntityFromSpace(space, username, uriInfo.getPath(), expand); + DataEntity dataEntity = spaceInfo.getDataEntity(); + if (unreadItemsPerSpace.containsKey(space.getSpaceId())) { + dataEntity.put(RestProperties.UNREAD, unreadItemsPerSpace.get(space.getSpaceId())); + } + return dataEntity; + }) + .toList(); CollectionEntity collectionSpace = new CollectionEntity(spaceInfos, SPACES_TYPE, offset, limit); - if (StringUtils.isNotBlank(expand) && Arrays.asList(StringUtils.split(expand, ",")).contains(RestProperties.UNREAD)) { - SpaceWebNotificationService spaceWebNotificationService = ExoContainerContext.getService(SpaceWebNotificationService.class); - Map unreadItemsPerSpace = spaceWebNotificationService.countUnreadItemsBySpace(username); - if (MapUtils.isNotEmpty(unreadItemsPerSpace)) { - collectionSpace.setUnreadPerSpace(unreadItemsPerSpace.entrySet() - .stream() - .collect(Collectors.toMap(e -> e.getKey().toString(), - Entry::getValue))); - } + if (MapUtils.isNotEmpty(unreadItemsPerSpace)) { + collectionSpace.setUnreadPerSpace(unreadItemsPerSpace.entrySet() + .stream() + .collect(Collectors.toMap(e -> e.getKey().toString(), + Entry::getValue))); } return collectionSpace; } @@ -979,6 +982,17 @@ public static SpaceEntity buildEntityFromSpace(Space space, String userId, Strin return spaceEntity; } + @SneakyThrows + public static void buildSpaceUnread(SpaceEntity spaceEntity, String username, String expand) { + if (StringUtils.isNotBlank(expand) && Arrays.asList(StringUtils.split(expand, ",")).contains(RestProperties.UNREAD)) { + SpaceWebNotificationService spaceWebNotificationService = ExoContainerContext.getService(SpaceWebNotificationService.class); + long unreadBadge = spaceWebNotificationService.countUnreadActivitiesBySpace(username, Long.parseLong(spaceEntity.getId())); + if (unreadBadge > 0) { + spaceEntity.getDataEntity().put(RestProperties.UNREAD, unreadBadge); + } + } + } + public static List buildSpaceMemberships(SpaceService spaceService, List spaces, String userId, @@ -2269,12 +2283,21 @@ 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()); } + private static Map buildSpacesUnread(String username, String expand) { + if (StringUtils.isNotBlank(expand) && Arrays.asList(StringUtils.split(expand, ",")).contains(RestProperties.UNREAD)) { + SpaceWebNotificationService spaceWebNotificationService = ExoContainerContext.getService(SpaceWebNotificationService.class); + return spaceWebNotificationService.countUnreadItemsBySpace(username); + } else { + return Collections.emptyMap(); + } + } + private static List getSiteNavigations(UserPortal userPortal, UserNode rootNode, boolean expandNavigations, diff --git a/component/service/src/main/java/org/exoplatform/social/rest/impl/space/SpaceRest.java b/component/service/src/main/java/org/exoplatform/social/rest/impl/space/SpaceRest.java index 07c1fdf7f6c..dd843a1dbce 100644 --- a/component/service/src/main/java/org/exoplatform/social/rest/impl/space/SpaceRest.java +++ b/component/service/src/main/java/org/exoplatform/social/rest/impl/space/SpaceRest.java @@ -299,7 +299,7 @@ public Response getSpaces( // NOSONAR } else if (StringUtils.equalsIgnoreCase(SPACE_FILTER_TYPE_FAVORITE, filterType)) { listAccess = spaceService.getFavoriteSpacesByFilter(authenticatedUser, spaceFilter); } else if (StringUtils.equalsIgnoreCase(LAST_VISITED_SPACES, filterType)) { - listAccess = spaceService.getLastAccessedSpace(authenticatedUser); + listAccess = spaceService.getLastAccessedSpaceByFilter(authenticatedUser, spaceFilter); } else { return Response.status(400).entity("Unrecognized space filter type").build(); } @@ -367,6 +367,15 @@ public Response createSpace( .entity(e.getCode().name()) .build()); } + saveSpaceAvatar(space, model); + saveSpaceBanner(model, space); + try { + inviteExternalUsers(uriInfo, model, space, authenticatedUser); + } catch (Exception e) { + LOG.warn("Error while sending external users invitations to space {}", + space.getGroupId(), + e); + } return EntityBuilder.getResponse(EntityBuilder.buildEntityFromSpace(space, authenticatedUser, uriInfo.getPath(), expand), uriInfo, RestUtils.getJsonMediaType(), @@ -771,36 +780,7 @@ public Response updateSpaceById( spaceService.renameSpace(space, model.getDisplayName(), authenticatedUser); } - if (model.getExternalInvitedUsers() != null - && (securitySettingService.getRegistrationType() == UserRegistrationType.OPEN - || securitySettingService.isRegistrationExternalUser())) { - String uri = uriInfo.getBaseUri() - .toString() - .substring(0, - uriInfo.getBaseUri() - .toString() - .lastIndexOf("/")); - StringBuilder url = new StringBuilder(uri); - - PasswordRecoveryService passwordRecoveryService = CommonsUtils.getService(PasswordRecoveryService.class); - LocaleConfigService localeConfigService = CommonsUtils.getService(LocaleConfigService.class); - Locale locale = null; - try { - String defaultLanguage = localeConfigService.getDefaultLocaleConfig().getLocale().toLanguageTag(); - locale = LocaleUtils.toLocale(defaultLanguage); - } catch (Exception e) { - LOG.error("Failure to retrieve portal config", e); - } - for (String externalInvitedUser : model.getExternalInvitedUsers()) { - String tokenId = passwordRecoveryService.sendExternalRegisterEmail(authenticatedUser, - externalInvitedUser, - locale, - space.getDisplayName(), - url); - spaceService.saveSpaceExternalInvitation(space.getId(), externalInvitedUser, tokenId); - } - } - + inviteExternalUsers(uriInfo, model, space, authenticatedUser); fillSpaceFromModel(space, model); space.setEditor(authenticatedUser); space = spaceService.updateSpace(space, model.getInvitedMembers()); @@ -1007,11 +987,11 @@ private void fillSpaceFromModel(Space space, SpaceEntity model) { space.setTemplateId(model.getTemplateId()); } - if (StringUtils.isNotBlank(model.getBannerId())) { + if (StringUtils.isNotBlank(model.getId()) && StringUtils.isNotBlank(model.getBannerId())) { updateProfileField(space, Profile.BANNER, model.getBannerId()); } - if (StringUtils.isNotBlank(model.getAvatarId())) { + if (StringUtils.isNotBlank(model.getId()) && StringUtils.isNotBlank(model.getAvatarId())) { updateProfileField(space, Profile.AVATAR, model.getAvatarId()); } @@ -1164,6 +1144,7 @@ private Response buildSpaceResponse(Space space, String expand, UriInfo uriInfo, } SpaceEntity spaceEntity = EntityBuilder.buildEntityFromSpace(space, authenticatedUser, uriInfo.getPath(), expand); + EntityBuilder.buildSpaceUnread(spaceEntity, authenticatedUser, expand); EntityTag eTag = new EntityTag(String.valueOf(spaceEntity.hashCode())); Response.ResponseBuilder builder = request.evaluatePreconditions(eTag); if (builder == null) { @@ -1193,4 +1174,63 @@ private Response.ResponseBuilder getDefaultBannerBuilder() throws IOException { return builder; } + private void saveSpaceAvatar(Space space, SpaceEntity model) { + if (StringUtils.isNotBlank(model.getAvatarId())) { + try { + updateProfileField(space, Profile.AVATAR, model.getAvatarId()); + } catch (IOException e) { + LOG.warn("Error adding Space Avatar. Avoid stopping space creation process and continue", e); + } + } + } + + private void saveSpaceBanner(SpaceEntity model, Space space) { + if (StringUtils.isNotBlank(model.getBannerId())) { + try { + updateProfileField(space, Profile.BANNER, model.getBannerId()); + } catch (IOException e) { + LOG.warn("Error adding Space Banner. Avoid stopping space creation process and continue", e); + } + } + } + + private boolean inviteExternalUsers(UriInfo uriInfo, SpaceEntity model, Space space, String authenticatedUser) throws Exception { + int errorsCount = 0; + if (model.getExternalInvitedUsers() != null + && (securitySettingService.getRegistrationType() == UserRegistrationType.OPEN + || securitySettingService.isRegistrationExternalUser())) { + String uri = uriInfo.getBaseUri() + .toString() + .substring(0, + uriInfo.getBaseUri() + .toString() + .lastIndexOf("/")); + StringBuilder url = new StringBuilder(uri); + + PasswordRecoveryService passwordRecoveryService = CommonsUtils.getService(PasswordRecoveryService.class); + LocaleConfigService localeConfigService = CommonsUtils.getService(LocaleConfigService.class); + Locale locale = null; + try { + String defaultLanguage = localeConfigService.getDefaultLocaleConfig().getLocale().toLanguageTag(); + locale = LocaleUtils.toLocale(defaultLanguage); + } catch (Exception e) { + LOG.error("Failure to retrieve portal config", e); + } + for (String externalInvitedUser : model.getExternalInvitedUsers()) { + try { + String tokenId = passwordRecoveryService.sendExternalRegisterEmail(authenticatedUser, + externalInvitedUser, + locale, + space.getDisplayName(), + url); + spaceService.saveSpaceExternalInvitation(space.getId(), externalInvitedUser, tokenId); + } catch (Exception e) { + LOG.warn("Error while sending external invitation to user {}", externalInvitedUser, e); + errorsCount++; + } + } + } + return errorsCount == 0; + } + } diff --git a/webapp/.eslintrc.json b/webapp/.eslintrc.json index 662dd78eac9..5be9841ab8d 100644 --- a/webapp/.eslintrc.json +++ b/webapp/.eslintrc.json @@ -4,6 +4,7 @@ ], "rules": { "vue/multi-word-component-names": "off", + "vuejs-accessibility/no-autofocus": "off", "no-prototype-builtins": ["warn"], "vue/no-mutating-props": ["warn"] }, diff --git a/webapp/pom.xml b/webapp/pom.xml index b3cc874be95..73b1abc2971 100644 --- a/webapp/pom.xml +++ b/webapp/pom.xml @@ -438,7 +438,7 @@ portlet/ActivitySearch/Style.less portlet/ActivityStream/Style.less portlet/GettingStarted/Style.less - portlet/HamburgerMenu/Style.less + portlet/Sidebar/Style.less portlet/IDMGroupsManagement/Style.less portlet/IDMMembershipTypesManagement/Style.less portlet/IDMUsersManagement/Style.less diff --git a/webapp/src/main/resources/locale/portal/HamburgerMenu_en.properties b/webapp/src/main/resources/locale/portal/HamburgerMenu_en.properties index 0f9eb35c4b5..5d31189a443 100644 --- a/webapp/src/main/resources/locale/portal/HamburgerMenu_en.properties +++ b/webapp/src/main/resources/locale/portal/HamburgerMenu_en.properties @@ -15,6 +15,7 @@ menu.role.navigation.first.level=First Level Navigation menu.role.navigation.second.level=Second Level Navigation menu.expand=Lock Sidebar menu.collapse=Hide Sidebar +menu.reduce=Icons View menu.createNewSpace=Create a space menu.seeMySpaces=See my spaces menu.spaces.joinOrCreateSpace=Join or create a space @@ -25,3 +26,23 @@ menu.productName.seeProduct=See Product Updates menu.settings=Access User Settings menu.logout=Log out menu.spaces.openSpaceAdvancedActions=See other options +menu.spacesExpand=Expand spaces of {0} +menu.spacesCollapse=Collapse spaces of {0} +menu.accessToSpacesList=Access to spaces list +menu.accessToPagesList=Access to pages list +menu.userHomeLink=User Home Link +menu.spaces.recent=Recent +menu.spaces.favorite=Favorite +menu.spaces.unread=Unread +menu.spaces.yourSpaces=Your Spaces +menu.spaces.noUnreadSpaces=No unread activities here +menu.spaces.noFavoriteSpaces=You haven't marked any space as a favorite +menu.spaces.openSidebarTooltip=Open Sites page links +menu.spaces.addNewSpaceTooltip=Filter by space +menu.spaces.filterBySpaceTooltip=Filter by space +menu.companyNameTooltip=Platform Name: {0} +menu.companyLogoTooltip=Platform Logo +menu.siteNameTooltip=Access Site: {0} +menu.pageNameTooltip=Access Page: {0} +menu.spaceTooltip=Access  space: {0} +menu.spacesTooltip=Access All Spaces diff --git a/webapp/src/main/resources/locale/portlet/GeneralSettings_en.properties b/webapp/src/main/resources/locale/portlet/GeneralSettings_en.properties index f5e8bb3bfab..e6007c04504 100644 --- a/webapp/src/main/resources/locale/portlet/GeneralSettings_en.properties +++ b/webapp/src/main/resources/locale/portlet/GeneralSettings_en.properties @@ -172,3 +172,110 @@ 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={0}Topbar{1} +generalSettings.brandingInfos=Platform Infos +generalSettings.displayCompanyName.desktop=When the sidebar is hidden +generalSettings.displayCompanyName.desktop.choice1=Display Logo & Name +generalSettings.displayCompanyName.desktop.choice2=Only the Logo +generalSettings.displayCompanyName.mobile=When using Mobile device +generalSettings.displayCompanyName.mobile.choice=Display Logo +generalSettings.displaySiteName=Remind the site or space name +generalSettings.displaySiteName.label=Display 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 {0}personal home{1} +generalSettings.whatIsUserCustomHome=Why allowing a personal home? +generalSettings.whatIsUserCustomHomeDescription=Let users personalize their experience by setting any sidebar item as their custom homepage. +generalSettings.sidebarAllowedModes={0}Display options{1} +generalSettings.whatIsSidebarAllowedModes=What are the display options? +generalSettings.whatIsSidebarAllowedModesDescription1=The Sidebar can be displayed in three ways: +generalSettings.whatIsSidebarAllowedModesDescription2=- Hide: The sidebar is closed. Users can open it by clicking the burger menu. +generalSettings.whatIsSidebarAllowedModesDescription3=- Icon View: The sidebar is minimized to icons only. +generalSettings.whatIsSidebarAllowedModesDescription4=- Keep Menu Open: The sidebar stays open unless the screen width is less than 1264px. +generalSettings.sidebarDefaultMode={0}Default Display{1} +generalSettings.whatIsSidebarDefaultMode=Why setting a default display? +generalSettings.whatIsSidebarDefaultModeDescription1=Help new users explore your platform by setting a default sidebar display. +generalSettings.whatIsSidebarDefaultModeDescription2=Only permitted options will be available (See Display options) +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 +generalSettings.editSideBarItem=Edit item +generalSettings.removeSideBarItem=Remove item +generalSettings.addSideBarItem=Add item +generalSettings.addSideBarItemSite=Site +generalSettings.addSideBarItemSpaces=Spaces +generalSettings.addSideBarItemLink=Link +generalSettings.addSideBarItemSeparator=Separator +generalSettings.add=Add +generalSettings.update=Update +generalSettings.addSideBarItemSite.drawerTitle=Add Site or Page +generalSettings.updateSideBarItemSite.drawerTitle=Update Site or Page +generalSettings.addSideBarItemSpaces.drawerTitle=Add Spaces +generalSettings.updateSideBarItemSpaces.drawerTitle=Edit Spaces +generalSettings.addSideBarItemLink.drawerTitle=Add a Link +generalSettings.updateSideBarItemLink.drawerTitle=Edit a Link +generalSettings.siteOption=Site +generalSettings.pageOption=Page +generalSettings.selectASite=Select a site +generalSettings.selectAPage=Select a page +generalSettings.selectASitePlaceholder=Start typing to search a site +generalSettings.anyPage=Any page +generalSettings.sidebar.spaceTemplateOption=Template +generalSettings.sidebar.spacesOption=Spaces +generalSettings.sidebar.spacesUpdateNameLabel=Update name +generalSettings.sidebar.searchTemplatePlaceholder=Search for a template +generalSettings.sidebar.spacesSortItemsBy=Sort items by: +generalSettings.sidebar.spacesSortItemsByTitle=Title +generalSettings.sidebar.spacesSortItemsByFavorite=User favorite +generalSettings.sidebar.spacesSortItemsByRecentlyVisited=User last visited +generalSettings.sidebar.spacesNamesPlaceHolder=Menu item name +generalSettings.sidebar.spacesNamesDrawerTitle=Add Menu item translations +generalSettings.sidebar.spacesSelectLimit=Select number of items to list +generalSettings.sidebar.displaySpacesInMobile=List them when using mobile +generalSettings.sidebar.linkName=Name +generalSettings.sidebar.linkDescription=Tooltip +generalSettings.sidebar.linkLabel=Link +generalSettings.sidebar.linkIcon=Update the icon +generalSettings.sidebar.linkTarget=Opens in same tab +generalSettings.sidebar.linkPlaceholder=Add link URL +generalSettings.sidebar.linkNamePlaceHolder=Give a name to your link +generalSettings.sidebar.linkNameDrawerTitle=Add name translations +generalSettings.sidebar.linkDescriptionPlaceHolder=Add a description to display in a tooltip +generalSettings.sidebar.linkDescriptionDrawerTitle=Add the description translations +generalSettings.sidebar.invalidLink=Invalid link +generalSettings.sidebar.exceedsMaxLength=Input exceeds max length +generalSettings.topbar.helpTooltip=What can I customize? +generalSettings.topbar.helpDescription1=Below, find options you can customize for the topbar. +generalSettings.topbar.helpDescription2=FYI, public site topbar will not get those customization. +generalSettings.topbar.helpDescription3=- Decide to display platform logo and/or name when the menu is hidden +generalSettings.topbar.helpDescription4=- Hide or not the site name or the space name. Icon or logo will be displayed no matter you decided to hide the name +generalSettings.topbar.helpDescription5=- Identify which options can be listed for users in the topbar +generalSettings.topbar.helpDescription6=- Precise which one can be accessed using a mobile device +generalSettings.moveUp=Move up +generalSettings.moveDown=Move down +generalSettings.topbar.switchDevicePreview=Switch device preview +generalSettings.defaultUserHouse=Default user home: Authenticated users will be redirected by default to this location when visiting the website +generalSettings.sidebar.spaces.sidebarDisplay=Sidebar Display +generalSettings.sidebar.spaces.displayOnlyWhenMember=Display only when member of a space diff --git a/webapp/src/main/resources/locale/portlet/Portlets_en.properties b/webapp/src/main/resources/locale/portlet/Portlets_en.properties index f96ae0d30c4..3b74dcece9d 100644 --- a/webapp/src/main/resources/locale/portlet/Portlets_en.properties +++ b/webapp/src/main/resources/locale/portlet/Portlets_en.properties @@ -1060,4 +1060,6 @@ nodeIconPickerDrawer.showMore=Show more nodeIconPickerDrawer.searchPlaceholder=Filter by name nodeIconPickerDrawer.edit=Update -UIChangeAvatarContainer.title.deleteAvatar=Delete Picture \ No newline at end of file +UIChangeAvatarContainer.title.deleteAvatar=Delete Picture + +sidebar.viewAllSpaces=View All Spaces diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ar.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ar.properties index c111593409d..233b938dc90 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ar.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ar.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=ولكن يمكنك البدء ب spacesList.label.noSpacesLink=إضافة الفضاء الأول الخاص بك. spacesList.label.addNewSpace=إضافة فضاء spacesList.label.editSpace=تعديل الفضاء {0} -spacesList.label.spaceDetails=تفاصيل الفضاء -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=دعوة المستخدمين -spacesList.label.displayName=الاسم الظاهر -spacesList.label.description=الوصف -spacesList.label.name=الاسم -spacesList.label.spaceTemplate=نموذج spacesList.label.yes=نعم spacesList.label.no=لا spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=إزالة من الإشارات المرجعي spacesList.button.requestJoin=طلب الوصول spacesList.button.cancelRequest=إلغاء الطلب spacesList.button.close=اغلاق -spacesList.button.continue=متابعة spacesList.button.back=الرجوع spacesList.button.cancel=إلغاء spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_aro.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_aro.properties index ee0e3769c71..2f05f86f1fa 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_aro.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_aro.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=ولكن يمكنك البدء ب spacesList.label.noSpacesLink=إضافة الفضاء الأول الخاص بك. spacesList.label.addNewSpace=مساحة spacesList.label.editSpace=تعديل الفضاء {0} -spacesList.label.spaceDetails=تفاصيل الفضاء -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=دعوة المستخدمين -spacesList.label.displayName=الاسم المعروض -spacesList.label.description=الوصف -spacesList.label.name=الاسم -spacesList.label.spaceTemplate=القالب spacesList.label.yes=نعم spacesList.label.no=لا spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=إزالة من الإشارات المرجعي spacesList.button.requestJoin=طلب الوصول spacesList.button.cancelRequest=إلغاء الطلب spacesList.button.close=اغلاق -spacesList.button.continue=واصل spacesList.button.back=رجوع spacesList.button.cancel=الغاء spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_az.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_az.properties index caea1acf1ce..d47c774c91e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_az.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_az.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Açıqlama -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Ləğv Et spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ca.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ca.properties index ecf4c691e1c..8fcc493dd9d 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ca.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ca.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Afegeix un Espai spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Descripció -spacesList.label.name=Nom -spacesList.label.spaceTemplate=Plantilla spacesList.label.yes=Sí spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Tanca -spacesList.button.continue=Continua spacesList.button.back=Enrere spacesList.button.cancel=Cancel·la spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ceb.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ceb.properties index c04350424c2..2977c002e86 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ceb.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ceb.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Ang Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_co.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_co.properties index 7be91786b7a..a406a728e0e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_co.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_co.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_cs.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_cs.properties index 73a3f022218..49fd28614cc 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_cs.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_cs.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Přidat skupinu spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Popis -spacesList.label.name=Název -spacesList.label.spaceTemplate=Šablona spacesList.label.yes=Ano spacesList.label.no=Ne spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Zavřít -spacesList.button.continue=Pokračovat spacesList.button.back=Zpět spacesList.button.cancel=Zrušit spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_de.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_de.properties index d7b94c23f16..fae383a7976 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_de.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_de.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=aber Sie können jetzt spacesList.label.noSpacesLink=Ihren ersten Raum hinzufügen. spacesList.label.addNewSpace=Raum hinzufügen spacesList.label.editSpace=Raum {0} bearbeiten -spacesList.label.spaceDetails=Raumdetails: -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Benutzer einladen -spacesList.label.displayName=Anzeigename -spacesList.label.description=Beschreibung -spacesList.label.name=Name -spacesList.label.spaceTemplate=Vorlage spacesList.label.yes=Ja spacesList.label.no=Nein spacesList.label.apply=Anwenden @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Aus Lesezeichen entfernen spacesList.button.requestJoin=Zugang anfordern spacesList.button.cancelRequest=Anfrage abbrechen spacesList.button.close=Schließen -spacesList.button.continue=Fortsetzen spacesList.button.back=Zurück spacesList.button.cancel=Abbrechen spacesList.button.add=Hinzufügen diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_el.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_el.properties index d2c48563fea..755337c2321 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_el.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_el.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Προσθήκη Χώρου spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Περιγραφή -spacesList.label.name=Όνομα -spacesList.label.spaceTemplate=Πρότυπο spacesList.label.yes=Ναι spacesList.label.no=Όχι spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Κλείσιμο -spacesList.button.continue=Συνέχεια spacesList.button.back=Επιστροφή spacesList.button.cancel=Ακύρωση spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_en.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_en.properties index 7be91786b7a..2f57219b1a3 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_en.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_en.properties @@ -20,14 +20,25 @@ spacesList.label.noSpacesYetDescription1=You don't have any space yet, spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space +spacesList.label.addNewSpaceWithTemplate=Add {0} spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template +spacesList.label.nameTitle=Name +spacesList.label.nameLabel=Give a name to your space +spacesList.label.namePlaceholder=Name your space +spacesList.label.propertiesTitle=Properties +spacesList.label.descriptionLabel=Describe your space +spacesList.label.descriptionPlaceholder=Detail purpose of your space +spacesList.label.spaceAccessTitle=Access Control +spacesList.label.avatarLabel=Avatar +spacesList.label.bannerLabel=Banner +spacesList.label.changeBanner=Change Banner +spacesList.label.deleteBanner=Delete Banner +spacesList.label.changeAvatar=Change Avatar +spacesList.label.deleteAvatar=Delete Avatar +spacesList.label.invitationTitle=Invitation +spacesList.label.accessTitle=Access +spacesList.label.visibilityTitle=Visibility spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -35,12 +46,16 @@ spacesList.label.hidden=Hidden spacesList.label.hiddenSpace=Hidden space spacesList.label.registration=Registration spacesList.label.open=Open +spacesList.description.open=Any user can join the space. No validation required. spacesList.label.validation=Request Approval +spacesList.description.validation=User can request to join. Membership must be approved by a host. spacesList.label.closed=Invite Only +spacesList.description.closed=Users can't request to join. They should be invited. spacesList.description.open=Anyone can join. No approval needed. spacesList.description.validation=User can request to join. Membership must be approved by an admin. spacesList.description.closed=Users can't request to join. Admins must send invitations. spacesList.description.hidden=The space is not listed in the space directory +spacesList.label.private=Listed spacesList.description.private=The space is visible in the directory. spacesList.warning=Warning spacesList.warning.descriptionExceededLength=The length of the text in field "Description" must be less than {0} characters. @@ -64,7 +79,7 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue +spacesList.button.next=Next spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.button.add=Add @@ -106,3 +121,6 @@ spacesList.button.copyLink.success=Link copied into the clipboard spacesList.button.copyLink.error=Error while copying site URL spacesList.button.visitPublicSite=Visit public site spacesList.button.openSpace=Open Space +spacesList.title.usersToInvite=Invite users +spacesList.label.pending=Pending +spacesList.label.viewAllSpaces=View all spaces diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_es_ES.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_es_ES.properties index 5b2fac12119..69daa425f82 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_es_ES.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_es_ES.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=pero puedes empezar por spacesList.label.noSpacesLink=añadir tu primer espacio. spacesList.label.addNewSpace=Añadir un Espacio spacesList.label.editSpace=Editar espacio {0} -spacesList.label.spaceDetails=Detalles del espacio -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invitar usuarios -spacesList.label.displayName=Mostrar nombre -spacesList.label.description=Descripción -spacesList.label.name=Nombre -spacesList.label.spaceTemplate=Plantilla spacesList.label.yes=Sí spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Eliminar de marcadores spacesList.button.requestJoin=Solicitar acceso spacesList.button.cancelRequest=Cancelar solicitud spacesList.button.close=Cerrar -spacesList.button.continue=Continuar spacesList.button.back=Volver spacesList.button.cancel=Cancelar spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_eu.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_eu.properties index 7be91786b7a..a406a728e0e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_eu.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_eu.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fa.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fa.properties index 0cadea3db8d..e800880a128 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fa.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fa.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=اضافه کردن فضا spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=شرح -spacesList.label.name=نام -spacesList.label.spaceTemplate=قالب spacesList.label.yes=بله spacesList.label.no=خیر spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=بستن -spacesList.button.continue=ادامه spacesList.button.back=بازگشت spacesList.button.cancel=انصراف spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fi.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fi.properties index 7be91786b7a..a406a728e0e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fi.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fi.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fil.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fil.properties index b7970c6b5ec..38bd29ab8d2 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fil.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fil.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Magdagdag ng Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Paglalarawan -spacesList.label.name=Pangalan -spacesList.label.spaceTemplate=Ang template spacesList.label.yes=Oo spacesList.label.no=Hindi spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Isara -spacesList.button.continue=Ipagpatuloy spacesList.button.back=Likuran spacesList.button.cancel=Kanselahin spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fr.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fr.properties index 92019adaf79..9eb4d1ae5b6 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fr.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_fr.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=mais vous pouvez commencer par spacesList.label.noSpacesLink=créer votre premier espace. spacesList.label.addNewSpace=Créer un Espace spacesList.label.editSpace=Modifier l'espace {0} -spacesList.label.spaceDetails=Détails de l'espace -spacesList.label.spaceAccess=Gestion des accès spacesList.label.inviteUsers=Inviter des utilisateurs -spacesList.label.displayName=Nom affiché -spacesList.label.description=Description -spacesList.label.name=Nom -spacesList.label.spaceTemplate=Modèle spacesList.label.yes=Oui spacesList.label.no=Non spacesList.label.apply=Appliquer @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Retirer des favoris spacesList.button.requestJoin=Demander l’accès spacesList.button.cancelRequest=Annuler la demande spacesList.button.close=Fermer -spacesList.button.continue=Poursuivre spacesList.button.back=Retour spacesList.button.cancel=Annuler spacesList.button.add=Ajouter diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_hi.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_hi.properties index 7be91786b7a..a406a728e0e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_hi.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_hi.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_hu.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_hu.properties index 7be91786b7a..a406a728e0e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_hu.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_hu.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_id.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_id.properties index 63aec7618cd..ac7afdaa4dc 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_id.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_id.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=Tapi Anda bisa memulainya dari spacesList.label.noSpacesLink=Menambahkan forum pertama Anda spacesList.label.addNewSpace=Tambah Forum spacesList.label.editSpace=Edit Forum {0} -spacesList.label.spaceDetails=Rincian Forum -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Undang pengguna -spacesList.label.displayName=Nama Tampilan -spacesList.label.description=Deskripsi -spacesList.label.name=Nama -spacesList.label.spaceTemplate=Templete spacesList.label.yes=Ya spacesList.label.no=Tidak spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Hapus dari yang ditandai spacesList.button.requestJoin=Minta akses spacesList.button.cancelRequest=Batalkan permintaan spacesList.button.close=Tutup -spacesList.button.continue=Lanjutan spacesList.button.back=Kembali spacesList.button.cancel=Batal spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_in.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_in.properties index d9e936f9d94..9d420a767eb 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_in.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_in.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Space access spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.hidden=Hidden @@ -63,7 +57,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.filter.all=All spaces diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_it.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_it.properties index 4cf92514e79..bc732e4de0d 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_it.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_it.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=ma puoi cominciare spacesList.label.noSpacesLink=aggiungendo il tuo primo spazio. spacesList.label.addNewSpace=Aggiungi spazio spacesList.label.editSpace=Modifica Spazio {0} -spacesList.label.spaceDetails=Dettagli dello spazio -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invita utenti -spacesList.label.displayName=Visualizza nome -spacesList.label.description=Descrizione -spacesList.label.name=Nome -spacesList.label.spaceTemplate=Modello spacesList.label.yes=Si spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Rimuover dai preferiti spacesList.button.requestJoin=Richiedi accesso spacesList.button.cancelRequest=Annulla richiesta spacesList.button.close=Chiudere -spacesList.button.continue=Continua spacesList.button.back=Indietro spacesList.button.cancel=Annulla spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ja.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ja.properties index 1a69d28500f..b794e31dddf 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ja.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ja.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=スペースを追加 spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=説明 -spacesList.label.name=名前 -spacesList.label.spaceTemplate=テンプレート spacesList.label.yes=はい spacesList.label.no=いいえ spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=閉じる -spacesList.button.continue=続ける spacesList.button.back=戻る spacesList.button.cancel=キャンセル spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_kab.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_kab.properties index d31e5458a1e..0d0e5508b1e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_kab.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_kab.properties @@ -20,13 +20,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Space access spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.hidden=Hidden @@ -59,7 +53,6 @@ spacesList.button.join=Join spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.filter.all=All spaces diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ko.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ko.properties index 7be91786b7a..a406a728e0e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ko.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ko.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_lt.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_lt.properties index 447fc245fb9..98d65256701 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_lt.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_lt.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Pridėti erdvę spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Aprašymas -spacesList.label.name=Pavadinimas -spacesList.label.spaceTemplate=Šablonas spacesList.label.yes=Taip spacesList.label.no=Ne spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Uždaryti -spacesList.button.continue=Toliau spacesList.button.back=Atgal spacesList.button.cancel=Atšaukti spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ms.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ms.properties index e1cefecd83a..578aaf4e1e1 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ms.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ms.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Batal spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_nl.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_nl.properties index df3ebc24467..0909597212c 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_nl.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_nl.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Ruimte toevoegen spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Beschrijving -spacesList.label.name=Naam -spacesList.label.spaceTemplate=Sjablonen spacesList.label.yes=Ja spacesList.label.no=Nee spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Sluiten -spacesList.button.continue=ga verder spacesList.button.back=Terug spacesList.button.cancel=Annuleren spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_no.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_no.properties index a21987d0089..27b3f627e6e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_no.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_no.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Legg til fellesskap spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Beskrivelse -spacesList.label.name=Navn -spacesList.label.spaceTemplate=Mal spacesList.label.yes=Ja spacesList.label.no=Nei spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Lukk -spacesList.button.continue=Gå videre spacesList.button.back=Tilbake spacesList.button.cancel=Avbryt spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pcm.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pcm.properties index 7be91786b7a..a406a728e0e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pcm.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pcm.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pl.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pl.properties index 73d6f66fa28..26636d98855 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pl.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pl.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Dodaj przestrzeń spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Opis -spacesList.label.name=Nazwa -spacesList.label.spaceTemplate=Szablon spacesList.label.yes=Tak spacesList.label.no=Nie spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Zamknij -spacesList.button.continue=Kontynuuj spacesList.button.back=Wstecz spacesList.button.cancel=Anuluj spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pt_BR.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pt_BR.properties index a048d0fa2db..f0f8c0cce4a 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pt_BR.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pt_BR.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=mas você pode começar por spacesList.label.noSpacesLink=adicionando o seu primeiro espaço. spacesList.label.addNewSpace=Adicionar espaço spacesList.label.editSpace=Editar Espaço {0} -spacesList.label.spaceDetails=Detalhes do Espaço -spacesList.label.spaceAccess=Acesso ao Space spacesList.label.inviteUsers=Convidar usuários -spacesList.label.displayName=Mostrar nome -spacesList.label.description=Descrição -spacesList.label.name=Nome -spacesList.label.spaceTemplate=Modelo spacesList.label.yes=Sim spacesList.label.no=Não spacesList.label.apply=Aplicar @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remover dos favoritos spacesList.button.requestJoin=Solicitar acesso spacesList.button.cancelRequest=Cancelar pedido spacesList.button.close=Fechar -spacesList.button.continue=Continar spacesList.button.back=Voltar spacesList.button.cancel=Cancelar spacesList.button.add=Adicionar diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pt_PT.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pt_PT.properties index 377034cef4e..ad5da9d9106 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pt_PT.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_pt_PT.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Adicionar espaço spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Descrição -spacesList.label.name=Nome -spacesList.label.spaceTemplate=Modelo spacesList.label.yes=Sim spacesList.label.no=Não spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Fechar -spacesList.button.continue=Continar spacesList.button.back=Voltar spacesList.button.cancel=Cancelar spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ro.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ro.properties index 2097c412056..51541c2ca6c 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ro.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ro.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Adaugă spaţiu spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Descriere -spacesList.label.name=Nume -spacesList.label.spaceTemplate=Şablon spacesList.label.yes=Da spacesList.label.no=Nu spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Închide -spacesList.button.continue=Continuați spacesList.button.back=Înapoi spacesList.button.cancel=Renunță spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ru.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ru.properties index 2c0c65432f4..a540b1a2b9c 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ru.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ru.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Добавить область spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Описание -spacesList.label.name=Название -spacesList.label.spaceTemplate=Шаблон spacesList.label.yes=Да spacesList.label.no=Нет spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Закрыть -spacesList.button.continue=Продолжить spacesList.button.back=Назад spacesList.button.cancel=Отмена spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sk.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sk.properties index 7be91786b7a..a406a728e0e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sk.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sk.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sl.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sl.properties index 89ae3d2130a..28caa0b56b2 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sl.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sl.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Dodaj prostor spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Prikazno ime -spacesList.label.description=Opis -spacesList.label.name=Ime -spacesList.label.spaceTemplate=Predloga spacesList.label.yes=Da spacesList.label.no=Ne spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Prekliči prošnjo spacesList.button.close=Zapri -spacesList.button.continue=Nadaljuj spacesList.button.back=Nazaj spacesList.button.cancel=Prekliči spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sq.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sq.properties index 5690663a40c..6bf312e791e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sq.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sq.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=crwdns42012:0crwdne42012:0 spacesList.label.noSpacesLink=crwdns42014:0crwdne42014:0 spacesList.label.addNewSpace=crwdns42016:0crwdne42016:0 spacesList.label.editSpace=crwdns42018:0{0}crwdne42018:0 -spacesList.label.spaceDetails=crwdns42020:0crwdne42020:0 -spacesList.label.spaceAccess=crwdns42022:0crwdne42022:0 spacesList.label.inviteUsers=crwdns42024:0crwdne42024:0 -spacesList.label.displayName=crwdns42026:0crwdne42026:0 -spacesList.label.description=crwdns42028:0crwdne42028:0 -spacesList.label.name=crwdns42030:0crwdne42030:0 -spacesList.label.spaceTemplate=crwdns42032:0crwdne42032:0 spacesList.label.yes=crwdns42034:0crwdne42034:0 spacesList.label.no=crwdns42036:0crwdne42036:0 spacesList.label.apply=crwdns91216:0crwdne91216:0 @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=crwdns42096:0crwdne42096:0 spacesList.button.requestJoin=crwdns42098:0crwdne42098:0 spacesList.button.cancelRequest=crwdns42100:0crwdne42100:0 spacesList.button.close=crwdns42102:0crwdne42102:0 -spacesList.button.continue=crwdns42104:0crwdne42104:0 spacesList.button.back=crwdns42106:0crwdne42106:0 spacesList.button.cancel=crwdns42108:0crwdne42108:0 spacesList.button.add=crwdns91220:0crwdne91220:0 diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sv_SE.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sv_SE.properties index 82aaef8f484..0d67e5203fa 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sv_SE.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_sv_SE.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Lägg till webb-yta spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Beskrivning -spacesList.label.name=Namn -spacesList.label.spaceTemplate=Mall spacesList.label.yes=Ja spacesList.label.no=Nej spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Stäng -spacesList.button.continue=Fortsätt spacesList.button.back=Tillbaka spacesList.button.cancel=Avbryt spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_th.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_th.properties index 7be91786b7a..a406a728e0e 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_th.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_th.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Close -spacesList.button.continue=Continue spacesList.button.back=Back spacesList.button.cancel=Cancel spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_tl.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_tl.properties index 82cc81c4f00..f38afb6129b 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_tl.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_tl.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Magdagdag ng Espasyo spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Description -spacesList.label.name=Name -spacesList.label.spaceTemplate=Ang Template spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Sarado -spacesList.button.continue=Magpatuloy spacesList.button.back=Bumalik spacesList.button.cancel=Kanselahin spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_tr.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_tr.properties index 3ba70575905..48e3ea60f02 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_tr.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_tr.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=ancak ilk alanınızı spacesList.label.noSpacesLink=ekleyerek başlayabilirsiniz. spacesList.label.addNewSpace=Alan Ekle spacesList.label.editSpace={0} Alanını Düzenleyin -spacesList.label.spaceDetails=Alan ayrıntıları -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Kullanıcıları davet et -spacesList.label.displayName=Ekran adı -spacesList.label.description=Açıklama -spacesList.label.name=Ad -spacesList.label.spaceTemplate=Şablon spacesList.label.yes=Evet spacesList.label.no=Hayır spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Erişim isteği gönderin spacesList.button.cancelRequest=İsteği iptal et spacesList.button.close=Kapat -spacesList.button.continue=Devam spacesList.button.back=Geri spacesList.button.cancel=İptal spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_uk.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_uk.properties index 4c41101a496..114410a7d3d 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_uk.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_uk.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=але ви можете почати spacesList.label.noSpacesLink=додавання вашої першої області. spacesList.label.addNewSpace=Додати область spacesList.label.editSpace=Редагувати область {0} -spacesList.label.spaceDetails=Деталі області -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Запросити користувачів -spacesList.label.displayName=Відображуване ім'я -spacesList.label.description=Опис -spacesList.label.name=Ім’я -spacesList.label.spaceTemplate=Шаблон spacesList.label.yes=Так spacesList.label.no=Ні spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Запит доступу spacesList.button.cancelRequest=Скасувати запит spacesList.button.close=Закрити -spacesList.button.continue=Продовжити spacesList.button.back=Назад spacesList.button.cancel=Скасувати spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ur_IN.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ur_IN.properties index e08c0d7d673..08eb7f529b7 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ur_IN.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_ur_IN.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Add Space spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=تفصیل -spacesList.label.name=نام -spacesList.label.spaceTemplate=سانچا spacesList.label.yes=Yes spacesList.label.no=No spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=بند -spacesList.button.continue=Continue spacesList.button.back=پىچھے spacesList.button.cancel=منسوخ spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_vi.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_vi.properties index ff60ac23daa..ec9220d7f42 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_vi.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_vi.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=Thêm Nhóm spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=Mô tả -spacesList.label.name=Tên -spacesList.label.spaceTemplate=Mẫu thiết kế spacesList.label.yes=Có spacesList.label.no=Không spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=Đóng -spacesList.button.continue=Tiếp tục spacesList.button.back=Trở lại spacesList.button.cancel=Hủy bỏ spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_zh_CN.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_zh_CN.properties index 9a8f17e3ce2..7b67a66f907 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_zh_CN.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_zh_CN.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=添加空间 spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=说明 -spacesList.label.name=名称 -spacesList.label.spaceTemplate=模板 spacesList.label.yes=是 spacesList.label.no=否 spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=关闭 -spacesList.button.continue=继续 spacesList.button.back=返回 spacesList.button.cancel=取消 spacesList.button.add=Add diff --git a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_zh_TW.properties b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_zh_TW.properties index f468963e6ab..75273044b08 100644 --- a/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_zh_TW.properties +++ b/webapp/src/main/resources/locale/portlet/social/SpacesListApplication_zh_TW.properties @@ -21,13 +21,7 @@ spacesList.label.noSpacesYetDescription2=but you can start by spacesList.label.noSpacesLink=adding your first space. spacesList.label.addNewSpace=新增空間 spacesList.label.editSpace=Edit Space {0} -spacesList.label.spaceDetails=Space details -spacesList.label.spaceAccess=Access Rules spacesList.label.inviteUsers=Invite users -spacesList.label.displayName=Display name -spacesList.label.description=描述 -spacesList.label.name=名稱 -spacesList.label.spaceTemplate=模板 spacesList.label.yes=是 spacesList.label.no=否 spacesList.label.apply=Apply @@ -64,7 +58,6 @@ spacesList.button.removeBookmark=Remove from bookmarked spacesList.button.requestJoin=Request access spacesList.button.cancelRequest=Cancel request spacesList.button.close=關閉 -spacesList.button.continue=繼續 spacesList.button.back=返回 spacesList.button.cancel=取消 spacesList.button.add=Add diff --git a/webapp/src/main/resources/social.properties b/webapp/src/main/resources/social.properties index bdf205b419e..e3c871e568a 100644 --- a/webapp/src/main/resources/social.properties +++ b/webapp/src/main/resources/social.properties @@ -16,3 +16,4 @@ # 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. +social.topbar.application.topbarAdministrationLink.mobile=false 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..54989440821 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 @@ -14,14 +14,14 @@ containerName - hamburger-menu-items-container + sidebar-items-container - HamburgerMenu + Sidebar - social/HamburgerMenu + social/Sidebar @@ -34,7 +34,7 @@ - Hamburger Menu + Sidebar false @@ -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/WEB-INF/gatein-resources.xml b/webapp/src/main/webapp/WEB-INF/gatein-resources.xml index 3087765b275..e95b576a637 100644 --- a/webapp/src/main/webapp/WEB-INF/gatein-resources.xml +++ b/webapp/src/main/webapp/WEB-INF/gatein-resources.xml @@ -44,10 +44,10 @@ social - HamburgerMenu + Sidebar Enterprise - /skin/css/portlet/HamburgerMenu/Style.css - 1 + /skin/css/portlet/Sidebar/Style.css + ImageCropper @@ -103,6 +103,20 @@ ImageCropper + + social + SpacesAdministration + Enterprise + ImageCropper + + + + social + SpacesList + Enterprise + ImageCropper + + social MembersPortlet @@ -1105,6 +1119,9 @@ extensionRegistry + + spaceForm + @@ -1435,9 +1452,6 @@ commonVueComponents - - notificationExtensions - eXoVueI18n @@ -1454,7 +1468,6 @@ TopBarFavorites - FavoriteDrawerGRP vue @@ -1499,6 +1512,9 @@ extensionRegistry + + autoLinker + siteDetailsComponent @@ -1576,6 +1592,9 @@ extensionRegistry + + spaceForm + @@ -1669,8 +1688,24 @@ TopBarLogo + + + vue + + + eXoVueI18n + - spaceBannerLogoPopover + commonVueComponents + + + extensionRegistry + + + vuetify @@ -1996,6 +2031,9 @@ extensionRegistry + + spaceForm + @@ -2179,6 +2217,32 @@ + + spaceForm + + + commonVueComponents + + + vue + + + vuetify + + + eXoVueI18n + + + extensionRegistry + + + imageCropper + + + peopleListComponents vue @@ -2660,15 +2725,15 @@ vuetify + + jquery + $ + - notificationExtensions - notificationGRP - + favoriteDrawerExtensions + FavoriteDrawerGRP vue @@ -2684,10 +2749,6 @@ vuetify - - jquery - $ - diff --git a/webapp/src/main/webapp/WEB-INF/jsp/portlet/hamburgerMenu.jsp b/webapp/src/main/webapp/WEB-INF/jsp/portlet/hamburgerMenu.jsp deleted file mode 100644 index d07291b2806..00000000000 --- a/webapp/src/main/webapp/WEB-INF/jsp/portlet/hamburgerMenu.jsp +++ /dev/null @@ -1,65 +0,0 @@ -<%@page import="org.apache.commons.lang3.StringUtils"%> -<%@page import="org.exoplatform.web.PortalHttpServletResponseWrapper"%> -<%@page import="org.exoplatform.portal.application.PortalRequestContext"%> -<%@page import="org.exoplatform.commons.api.settings.SettingValue"%> -<%@page import="org.exoplatform.commons.api.settings.data.Scope"%> -<%@page import="org.exoplatform.commons.api.settings.data.Context"%> -<%@page import="org.exoplatform.commons.api.settings.SettingService"%> -<%@page import="org.exoplatform.container.ExoContainerContext"%> -<%@page import="io.meeds.social.space.template.service.SpaceTemplateService"%> -<% - boolean canCreateSpace = ExoContainerContext.getService(SpaceTemplateService.class).canCreateSpace(request.getRemoteUser()); - SettingValue stickySettingValue = ExoContainerContext.getService(SettingService.class).get(Context.USER.id(request.getRemoteUser()), Scope.APPLICATION.id("HamburgerMenu"), "Sticky"); - boolean sticky = stickySettingValue == null ? Boolean.parseBoolean(System.getProperty("io.meeds.userPrefs.HamburgerMenu.sticky", "false")) : Boolean.parseBoolean(stickySettingValue.getValue().toString()); - - PortalRequestContext rcontext = (PortalRequestContext) PortalRequestContext.getCurrentInstance(); - PortalHttpServletResponseWrapper responseWrapper = (PortalHttpServletResponseWrapper) rcontext.getResponse(); - if (rcontext.getRequest().getParameter("sticky") != null) { - sticky = StringUtils.equals("true", rcontext.getRequest().getParameter("sticky")); - } - - responseWrapper.addHeader("Link", "; rel=preload; as=fetch; crossorigin=use-credentials", false); - responseWrapper.addHeader("Link", "; rel=preload; as=fetch; crossorigin=use-credentials", false); -%> -
-
-
- <% if (sticky) { %> - - <% } else { %> - -
- -
-
- <% } %> -
- -
-
\ No newline at end of file diff --git a/webapp/src/main/webapp/WEB-INF/jsp/portlet/platformSettings.jsp b/webapp/src/main/webapp/WEB-INF/jsp/portlet/platformSettings.jsp index 9f1b30112ba..807c5cd7fc5 100644 --- a/webapp/src/main/webapp/WEB-INF/jsp/portlet/platformSettings.jsp +++ b/webapp/src/main/webapp/WEB-INF/jsp/portlet/platformSettings.jsp @@ -1,15 +1,12 @@ +<%@ page import="org.exoplatform.container.ExoContainerContext"%> <%@ page import="org.exoplatform.portal.config.UserPortalConfigService" %> <%@ page import="org.exoplatform.portal.application.PortalRequestContext" %> -<%@ page import="org.exoplatform.portal.mop.service.LayoutService" %> -<%@ page import="org.exoplatform.portal.config.model.PortalConfig" %> <%@ page import="org.exoplatform.web.application.RequestContext" %> -<%@ page import="org.exoplatform.commons.utils.CommonsUtils" %> <% - PortalRequestContext requestContext = ((PortalRequestContext) RequestContext.getCurrentInstance()); - PortalConfig administrationSite = CommonsUtils.getService(LayoutService.class).getPortalConfig("PORTAL", "administration"); - String path = CommonsUtils.getService(UserPortalConfigService.class).computePortalSitePath("administration", requestContext.getRequest()); +String path = ExoContainerContext.getService(UserPortalConfigService.class) + .getDefaultSitePath("administration", request.getRemoteUser()); + if (path != null) { %> -<% if (administrationSite != null && !administrationSite.isDisplayed() && path != null) { %>
diff --git a/webapp/src/main/webapp/WEB-INF/jsp/portlet/sidebar.jsp b/webapp/src/main/webapp/WEB-INF/jsp/portlet/sidebar.jsp new file mode 100644 index 00000000000..27507471b39 --- /dev/null +++ b/webapp/src/main/webapp/WEB-INF/jsp/portlet/sidebar.jsp @@ -0,0 +1,92 @@ +<%@page import="io.meeds.social.navigation.constant.SidebarMode"%> +<%@page import="io.meeds.social.navigation.service.NavigationConfigurationService"%> +<%@page import="io.meeds.portal.security.service.SecuritySettingService"%> +<%@page import="io.meeds.portal.security.constant.UserRegistrationType"%> +<%@page import="org.exoplatform.social.core.identity.model.Identity"%> +<%@page import="io.meeds.social.util.JsonUtils"%> +<%@page import="org.exoplatform.social.notification.service.SpaceWebNotificationService"%> +<%@page import="java.util.Map"%> +<%@page import="org.exoplatform.portal.config.UserPortalConfigService"%> +<%@page import="org.apache.commons.lang3.StringUtils"%> +<%@page import="org.exoplatform.web.PortalHttpServletResponseWrapper"%> +<%@page import="org.exoplatform.portal.application.PortalRequestContext"%> +<%@page import="org.exoplatform.commons.api.settings.SettingValue"%> +<%@page import="org.exoplatform.commons.api.settings.data.Scope"%> +<%@page import="org.exoplatform.commons.api.settings.data.Context"%> +<%@page import="org.exoplatform.commons.api.settings.SettingService"%> +<%@page import="org.exoplatform.container.ExoContainerContext"%> +<%@page import="io.meeds.social.space.template.service.SpaceTemplateService"%> +<%@page import="org.exoplatform.social.webui.Utils"%> +<% +PortalRequestContext rcontext = (PortalRequestContext) PortalRequestContext.getCurrentInstance(); + UserPortalConfigService portalConfigService = ExoContainerContext.getService(UserPortalConfigService.class); + + NavigationConfigurationService navigationConfigurationService = ExoContainerContext.getService(NavigationConfigurationService.class); + SidebarMode mode = navigationConfigurationService.getSidebarUserMode(request.getRemoteUser()); + boolean allowUserHome = navigationConfigurationService.getConfiguration().getSidebar().isAllowUserCustomHome(); + + Identity viewerIdentity = Utils.getViewerIdentity(); + String avatarUrl = viewerIdentity == null ? "" : viewerIdentity.getProfile().getAvatarUrl(); + + Map unreadPerSpace = ExoContainerContext.getService(SpaceWebNotificationService.class) + .countUnreadItemsBySpace(request.getRemoteUser()); + + String defaultUserPath; + if (StringUtils.equals(rcontext.getPortalOwner(), "public")) { + defaultUserPath = "/portal/public"; + } else { + defaultUserPath = portalConfigService.getUserHomePage(request.getRemoteUser()); + if (defaultUserPath == null) { + defaultUserPath = portalConfigService.getDefaultPath(request.getRemoteUser()); + } + } + if (defaultUserPath == null) { + defaultUserPath = "/portal/" + rcontext.getPortalOwner(); + } + + ((PortalHttpServletResponseWrapper) rcontext.getResponse()).addHeader("Link", "; rel=preload; as=fetch; crossorigin=use-credentials", false); + + SecuritySettingService securitySettingService = ExoContainerContext.getService(SecuritySettingService.class); + boolean isExternalFeatureEnabled = securitySettingService.getRegistrationType() == UserRegistrationType.OPEN || securitySettingService.isRegistrationExternalUser(); +%> +
+
+
+ <% if (mode == SidebarMode.STICKY) { %> + + <% } else { %> + +
+ <% if (mode == SidebarMode.HIDDEN) { %> + + <% } %> +
+
+ <% } %> +
+ +
+
\ No newline at end of file diff --git a/webapp/src/main/webapp/WEB-INF/jsp/portlet/spaceTemplateManagement.jsp b/webapp/src/main/webapp/WEB-INF/jsp/portlet/spaceTemplateManagement.jsp new file mode 100644 index 00000000000..0efd5754e31 --- /dev/null +++ b/webapp/src/main/webapp/WEB-INF/jsp/portlet/spaceTemplateManagement.jsp @@ -0,0 +1,16 @@ +<%@page import="org.exoplatform.container.ExoContainerContext"%> +<%@page import="io.meeds.portal.security.service.SecuritySettingService"%> +<%@page import="io.meeds.portal.security.constant.UserRegistrationType"%> +<% + SecuritySettingService securitySettingService = ExoContainerContext.getService(SecuritySettingService.class); + boolean isExternalFeatureEnabled = securitySettingService.getRegistrationType() == UserRegistrationType.OPEN || securitySettingService.isRegistrationExternalUser(); +%> +
+
+ +
+
diff --git a/webapp/src/main/webapp/WEB-INF/jsp/portlet/spacesAdministration.jsp b/webapp/src/main/webapp/WEB-INF/jsp/portlet/spacesAdministration.jsp new file mode 100644 index 00000000000..6780fdbc527 --- /dev/null +++ b/webapp/src/main/webapp/WEB-INF/jsp/portlet/spacesAdministration.jsp @@ -0,0 +1,16 @@ +<%@page import="org.exoplatform.container.ExoContainerContext"%> +<%@page import="io.meeds.portal.security.service.SecuritySettingService"%> +<%@page import="io.meeds.portal.security.constant.UserRegistrationType"%> +<% + SecuritySettingService securitySettingService = ExoContainerContext.getService(SecuritySettingService.class); + boolean isExternalFeatureEnabled = securitySettingService.getRegistrationType() == UserRegistrationType.OPEN || securitySettingService.isRegistrationExternalUser(); +%> +
+
+ +
+
\ No newline at end of file diff --git a/webapp/src/main/webapp/WEB-INF/jsp/portlet/spacesList.jsp b/webapp/src/main/webapp/WEB-INF/jsp/portlet/spacesList.jsp index a45d6a81053..977a8d6a71c 100644 --- a/webapp/src/main/webapp/WEB-INF/jsp/portlet/spacesList.jsp +++ b/webapp/src/main/webapp/WEB-INF/jsp/portlet/spacesList.jsp @@ -1,5 +1,7 @@ <%@page import="io.meeds.social.space.template.service.SpaceTemplateService"%> <%@page import="org.exoplatform.container.ExoContainerContext"%> +<%@page import="io.meeds.portal.security.service.SecuritySettingService"%> +<%@page import="io.meeds.portal.security.constant.UserRegistrationType"%> <% Object filter = request.getAttribute("filter"); if (filter == null) { @@ -9,6 +11,8 @@ } boolean canCreateSpace = ExoContainerContext.getService(SpaceTemplateService.class) .canCreateSpace(request.getRemoteUser()); + SecuritySettingService securitySettingService = ExoContainerContext.getService(SecuritySettingService.class); + boolean isExternalFeatureEnabled = securitySettingService.getRegistrationType() == UserRegistrationType.OPEN || securitySettingService.isRegistrationExternalUser(); %>
diff --git a/webapp/src/main/webapp/WEB-INF/jsp/portlet/topbarLogo.jsp b/webapp/src/main/webapp/WEB-INF/jsp/portlet/topbarLogo.jsp index bf2097a74c3..e212219af98 100644 --- a/webapp/src/main/webapp/WEB-INF/jsp/portlet/topbarLogo.jsp +++ b/webapp/src/main/webapp/WEB-INF/jsp/portlet/topbarLogo.jsp @@ -1,3 +1,12 @@ +<%@page import="org.apache.commons.collections4.CollectionUtils"%> +<%@page import="io.meeds.social.navigation.plugin.AbstractLayoutSidebarPlugin"%> +<%@page import="io.meeds.social.navigation.constant.SidebarItemType"%> +<%@page import="io.meeds.social.navigation.model.SidebarItem"%> +<%@page import="io.meeds.social.navigation.constant.SidebarMode"%> +<%@page import="io.meeds.social.navigation.model.SidebarConfiguration"%> +<%@page import="io.meeds.social.navigation.model.NavigationConfiguration"%> +<%@page import="io.meeds.social.navigation.model.TopbarConfiguration"%> +<%@page import="io.meeds.social.navigation.service.NavigationConfigurationService"%> <%@ page import="org.exoplatform.services.security.IdentityConstants"%> <%@ page import="java.net.URLEncoder"%> <%@ page import="org.apache.commons.lang3.StringUtils"%> @@ -23,14 +32,11 @@ <%@ page import="org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider" %> <%@ page import="java.util.Optional" %> <% - String spaceId = null; - String logoPath = null; - String logoTitle = null; +String spaceId = null; String portalPath = null; - String defaultHomePath = ""; String titleClass = ""; String imageClass = ""; - String homePath= ""; + int membersNumber= 0; boolean isFavorite= false; boolean muted= false; @@ -45,26 +51,38 @@ UserPortalConfigService portalConfigService = CommonsUtils.getService(UserPortalConfigService.class); UserSettingService userSettingService = CommonsUtils.getService(UserSettingService.class); UserSetting userSetting = authenticatedUser == null ? null : userSettingService.get(authenticatedUser); + NavigationConfigurationService navigationConfigurationService = ExoContainerContext.getService(NavigationConfigurationService.class); + TopbarConfiguration topbarConfiguration = navigationConfigurationService.getTopbarConfiguration(request.getRemoteUser(), request.getLocale()); + SidebarConfiguration sidebarConfiguration = navigationConfigurationService.getSidebarConfiguration(request.getRemoteUser(), request.getLocale()); - defaultHomePath = "/portal/" + requestContext.getPortalOwner(); - if (space == null) { - BrandingService brandingService = CommonsUtils.getService(BrandingService.class); - logoPath = brandingService.getLogoPath(); - logoTitle = brandingService.getCompanyName(); + String defaultHomePath = "/portal/" + requestContext.getPortalOwner(); - if (StringUtils.equals(requestContext.getPortalOwner(), "public")) { - portalPath = "/portal/public"; - } else { - portalPath = portalConfigService.getUserHomePage(request.getRemoteUser()); - if (portalPath == null) { - portalPath = portalConfigService.computePortalPath(requestContext.getRequest()); - if (portalPath == null) { - portalPath = defaultHomePath; - } - } - } - titleClass = "company"; + BrandingService brandingService = CommonsUtils.getService(BrandingService.class); + String logoPath = brandingService.getLogoPath(); + String logoTitle = brandingService.getCompanyName(); + if (StringUtils.equals(requestContext.getPortalOwner(), "public")) { + portalPath = "/portal/public"; } else { + if (sidebarConfiguration.isAllowUserCustomHome()) { + portalPath = portalConfigService.getUserHomePage(request.getRemoteUser()); + } + if (portalPath == null) { + portalPath = portalConfigService.getDefaultPath(request.getRemoteUser()); + if (portalPath == null) { + portalPath = defaultHomePath; + } + } + } + titleClass = "company"; + + String spaceLogoPath = null; + String spaceLogoTitle = null; + String spacePortalPath = null; + if (space != null) { + spaceLogoPath = space.getAvatarUrl(); + spaceLogoTitle = space.getDisplayName(); + spacePortalPath = "/portal/s/" + space.getId(); + FavoriteService favoriteService = ExoContainerContext.getService(FavoriteService.class); Identity userIdentity = identityManager.getOrCreateUserIdentity(authenticatedUser); spaceId = space.getId(); @@ -73,73 +91,82 @@ canRedactOnSpace = authenticatedUser == null ? false : spaceService.canRedactOnSpace(space, authenticatedUser); isFavorite = authenticatedUser == null ? false : favoriteService.isFavorite(new Favorite(space.DEFAULT_SPACE_METADATA_OBJECT_TYPE, space.getId(), null, Long.parseLong(userIdentity.getId()))); muted = authenticatedUser == null ? false : userSetting.isSpaceMuted(Long.parseLong(spaceId)); - logoPath = space.getAvatarUrl(); - logoTitle = space.getDisplayName(); String permanentSpaceName = space.getGroupId().split("/")[2]; - portalPath = "/portal/s/" + space.getId(); membersNumber = space.getMembers().length; spaceDescription = Optional.ofNullable(space.getDescription()).orElse(""); if (authenticatedUser != null) { - for(String username : space.getManagers()) { - Profile profile = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, username).getProfile(); - managers.add(profile); - } + for(String username : space.getManagers()) { + Profile profile = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, username).getProfile(); + managers.add(profile); + } } - homePath = authenticatedUser == null ? "/" : Optional.ofNullable(portalConfigService.getUserHomePage(request.getRemoteUser())).orElse(defaultHomePath); } String directionVuetifyClass = requestContext.getOrientation().isRT() ? "v-application--is-rtl" : "v-application--is-ltr"; + boolean displayCompanyName = topbarConfiguration.isDisplayCompanyName(); + boolean displayMobileCompanyLogo = topbarConfiguration.isDisplayMobileCompanyLogo(); + boolean displaySiteName = topbarConfiguration.isDisplaySiteName(); + SidebarMode sidebarMode = sidebarConfiguration.getUserMode(); + SidebarItem sidebarItem = space == null ? sidebarConfiguration.getItems().stream().filter(item -> item.getUrl() != null + && item.getType() == SidebarItemType.SITE + && requestContext.getRequest().getRequestURI().toString().startsWith(item.getUrl())) + .findFirst() + .orElse(null) + : null; + boolean isSitePage = false; + if (sidebarItem != null + && StringUtils.equals(sidebarItem.getProperties().get(AbstractLayoutSidebarPlugin.SITE_EXPAND_PAGES_PROP_NAME), "true") + && CollectionUtils.isNotEmpty(sidebarItem.getItems())) { + sidebarItem = sidebarItem.getItems().stream().filter(item -> item.getUrl() != null + && item.getType() == SidebarItemType.PAGE + && requestContext.getRequest().getRequestURI().toString().startsWith(item.getUrl())) + .findFirst() + .orElse(null); + isSitePage = true; + } %>
-
+
-
-
- <% if (space == null) { %> - <% if (logoPath != null) { %> - - <%=logoTitle%> - - <% } %> - - - - <% } else { %> - - <% } %> -
-
+
\ No newline at end of file diff --git a/webapp/src/main/webapp/WEB-INF/portlet.xml b/webapp/src/main/webapp/WEB-INF/portlet.xml index a84b33f6758..48b77c10d76 100644 --- a/webapp/src/main/webapp/WEB-INF/portlet.xml +++ b/webapp/src/main/webapp/WEB-INF/portlet.xml @@ -388,11 +388,11 @@ - HamburgerMenu + Sidebar org.exoplatform.commons.api.portlet.GenericDispatchedViewPortlet portlet-view-dispatched-file-path - /WEB-INF/jsp/portlet/hamburgerMenu.jsp + /WEB-INF/jsp/portlet/sidebar.jsp layout-css-class @@ -402,7 +402,7 @@ text/html - Hamburger Menu + Sidebar Menu @@ -432,10 +432,8 @@ org.exoplatform.commons.api.portlet.GenericDispatchedViewPortlet portlet-view-dispatched-file-path - /html/spacesAdministration.html + /WEB-INF/jsp/portlet/spacesAdministration.jsp - -1 - PUBLIC text/html @@ -1057,10 +1055,8 @@ org.exoplatform.commons.api.portlet.GenericDispatchedViewPortlet portlet-view-dispatched-file-path - /html/spaceTemplateManagement.html + /WEB-INF/jsp/portlet/spaceTemplateManagement.jsp - -1 - PUBLIC text/html diff --git a/webapp/src/main/webapp/groovy/social/webui/UISocialPortalApplicationHead.gtmpl b/webapp/src/main/webapp/groovy/social/webui/UISocialPortalApplicationHead.gtmpl index 33b3cbfc873..be1dd8fcf95 100644 --- a/webapp/src/main/webapp/groovy/social/webui/UISocialPortalApplicationHead.gtmpl +++ b/webapp/src/main/webapp/groovy/social/webui/UISocialPortalApplicationHead.gtmpl @@ -40,6 +40,7 @@ eXo.env.portal.profileOwner = "<%=ownerIdentityId == null ? "" : Utils.getOwnerRemoteId()%>" ; eXo.env.portal.profileOwnerIdentityId = "<%=ownerIdentityId == null ? "" : Utils.getOwnerIdentityId()%>" ; eXo.env.portal.userIdentityId = "<%=viewerIdentityId == null ? "" : viewerIdentityId%>" ; + eXo.env.portal.companyLogo = "<%=brandingService.getLogoPath()%>" ; eXo.env.portal.companyName = "<%=brandingService.getCompanyName()%>" ; eXo.env.portal.isExternal = <%=Utils.isExternal(Utils.getViewerIdentity())%>; eXo.env.portal.cometdToken = "<%=cometdToken%>"; diff --git a/webapp/src/main/webapp/groovy/social/webui/UITopbarApplicationsContainer.gtmpl b/webapp/src/main/webapp/groovy/social/webui/UITopbarApplicationsContainer.gtmpl new file mode 100644 index 00000000000..50359f0d8e6 --- /dev/null +++ b/webapp/src/main/webapp/groovy/social/webui/UITopbarApplicationsContainer.gtmpl @@ -0,0 +1,59 @@ +<% + import java.util.Locale; + + import io.meeds.social.navigation.model.TopbarConfiguration; + import io.meeds.social.navigation.service.NavigationConfigurationService; + + import org.exoplatform.portal.webui.workspace.UIPortalApplication; + import org.exoplatform.commons.utils.HTMLEntityEncoder; + + def rcontext = _ctx.getRequestContext(); + UIPortalApplication uiPortalApp = rcontext.getUIApplication(); + + if (!uicomponent.hasPermission()) { + return; + } + + String cssStyle = ""; + String uiComponentWidth = uicomponent.getWidth(); + String uiComponentHeight = uicomponent.getHeight(); + if(uiComponentWidth != null || uiComponentHeight != null) cssStyle = "style=\""; + if(uiComponentWidth != null) cssStyle += "width: "+uiComponentWidth+";" + if(uiComponentHeight != null) cssStyle += "height: "+uiComponentHeight+";" + if(cssStyle.length() > 0) cssStyle += "\""; + + String uiComponentClass = uicomponent.getCssClass() == null ? "" : uicomponent.getCssClass(); + + /** Trim the prefix UIContainer- if any, this hardcoded part is needed to update nested container via Ajax */ + String componentId = uicomponent.getId(); + if(componentId.startsWith("UIContainer-")){ + uicomponent.setId(componentId.substring("UIContainer-".length())); + } + + NavigationConfigurationService navigationConfigurationService = uicomponent.getApplicationComponent(NavigationConfigurationService.class); + TopbarConfiguration topbarConfiguration = navigationConfigurationService.getConfiguration().getTopbar(); +%> +
+
+
+
+
+ <% + for (application in topbarConfiguration.getApplications()) { + if (application.isEnabled()) { + def child = uicomponent.getChildById(application.getId()); + if (child != null && child.isRendered()) { + %> +
"> + <% child.processRender(rcontext);%> +
+ <% + } + } + } + %> +
+
+
+
+
\ No newline at end of file diff --git a/webapp/src/main/webapp/html/spaceTemplateManagement.html b/webapp/src/main/webapp/html/spaceTemplateManagement.html deleted file mode 100644 index 2cbaacc41a2..00000000000 --- a/webapp/src/main/webapp/html/spaceTemplateManagement.html +++ /dev/null @@ -1,9 +0,0 @@ -
-
- -
-
diff --git a/webapp/src/main/webapp/html/spacesAdministration.html b/webapp/src/main/webapp/html/spacesAdministration.html deleted file mode 100644 index ddcbfcee67c..00000000000 --- a/webapp/src/main/webapp/html/spacesAdministration.html +++ /dev/null @@ -1,9 +0,0 @@ -
-
- -
-
\ No newline at end of file diff --git a/webapp/src/main/webapp/images/sidebar.svg b/webapp/src/main/webapp/images/sidebar.svg new file mode 100644 index 00000000000..349a7f52605 --- /dev/null +++ b/webapp/src/main/webapp/images/sidebar.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/src/main/webapp/skin/less/portlet/HamburgerMenu/Style.less b/webapp/src/main/webapp/skin/less/portlet/HamburgerMenu/Style.less deleted file mode 100644 index e5356d0a4d7..00000000000 --- a/webapp/src/main/webapp/skin/less/portlet/HamburgerMenu/Style.less +++ /dev/null @@ -1,152 +0,0 @@ -/** - - This file is part of the Meeds project (https://meeds.io/). - - Copyright (C) 2020 - 2023 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. - -*/ - -@import "../../variables.less"; -@import "../../mixins.less"; -@import "../../common/siteDetails/Style.less"; - -@media (min-width: @minTabletWidth) { - #StickyHamburgerMenu, #HamburgerNavigationMenu .v-navigation-drawer__content { - .specific-scrollbar() - } -} - -.HamburgerNavigationMenu { - .spacesIcon { - .fa(); - .fa-history(); - } - - .UserPageLink .UserPageHome, .UserPageLinkHome .UserPageHome { - .fa(); - .fa-house-user(); - } - - .UserPageLink .homePage { - display: none; - } - - .UserPageLink:hover .homePage, .UserPageLink:focus .homePage, .UserPageLink button:focus .homePage { - display: flex; - } - - .HamburgerNavigationMenuLink { - border-right: 1px solid @greyColorLighten1Opacity1 ~'!important; /** orientation=lt */ '; - border-left: 1px solid @greyColorLighten1Opacity1 ~'!important; /** orientation=rt */ '; - - i { - color: @toolbarLightIconColor !important; - } - .hamburger-unread-badge { - border-radius: 100%; - border: 2px solid white; - top: 6px; - right: 10px; - width: 16px; - height: 16px; - } - } - .uiIcon.uiIconToolbarNavItem { - font-size: 20px; - max-width: 28px; - color: @toolbarLightIconColor !important; - margin: 0 auto; - } - .accountTitleWrapper { - border-bottom: 1px solid #f9f9f9; - .accountTitleItem { - max-width: 100%; - &:before { - display: none!important; - } - &:hover { - &:before { - opacity: 0; - } - } - &:focus { - &:before { - opacity: 0; - } - } - .accountTitleLabel { - color: @toolbarLightLinkHoverColor; - font-size: 0.9rem!important; - } - .externalFlagClass { - color: @baseColorLight; - font-weight: normal; - } - } - } - .v-list-item { - position: relative; - - .v-list-item__icon { - min-width: 28px; - margin-right: 20px ~'!important; /** orientation=lt */ '; - margin-left: 20px ~'!important; /** orientation=rt */ '; - - i { - color: @toolbarLightIconColor; - font-size: 20px; - text-align: center; - margin: 0 auto; - } - } - } - .v-list-item--active { - color: @textLightColor!important; - background: @greyColorLighten1Opacity1; - &:before { - content: ''; - width: 8px; - background:@secondaryColor; - position: absolute; - left: 0; - opacity: 1!important; - } - .v-list-item__icon { - i { - color: @toolbarLightIconColor !important; - } - .spaceRightArrow { - color: @toolbarDarkLinkHoverColor !important; - } - } - } -} - -#HamburgerMenuSpaceLeftNavigationActions i.v-icon { - font-size: 20px !important; -} - -@media (min-width: @minDesktop) { - .HamburgerMenuSticky { - #UITopBarContainerParent { - position: relative; - } - /* added for page loading phase */ - #ParentSiteLeftContainer { - min-width: 310px; - background-color: @lightWhite; - } - } -} diff --git a/webapp/src/main/webapp/skin/less/portlet/Sidebar/Style.less b/webapp/src/main/webapp/skin/less/portlet/Sidebar/Style.less new file mode 100644 index 00000000000..f245256e4b7 --- /dev/null +++ b/webapp/src/main/webapp/skin/less/portlet/Sidebar/Style.less @@ -0,0 +1,67 @@ +/** + + This file is part of the Meeds project (https://meeds.io/). + + Copyright (C) 2020 - 2023 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. + +*/ + +@import "../../variables.less"; +@import "../../mixins.less"; +@import "../../common/siteDetails/Style.less"; + +.HamburgerNavigationMenu { + + .UserPageLink .UserPageHome, .UserPageLinkHome .UserPageHome { + .fa(); + .fa-house-user(); + } + + .UserPageLink .homePage { + display: none; + } + + .UserPageLink:hover .homePage, .UserPageLink:focus .homePage, .UserPageLink button:focus .homePage { + display: flex; + } + + .HamburgerNavigationMenuLink { + border-right: 1px solid @greyColorLighten1Opacity1 ~'!important; /** orientation=lt */ '; + border-left: 1px solid @greyColorLighten1Opacity1 ~'!important; /** orientation=rt */ '; + + i { + color: @toolbarLightIconColor !important; + } + } + .uiIcon.uiIconToolbarNavItem { + font-size: 20px; + max-width: 28px; + color: @toolbarLightIconColor !important; + margin: 0 auto; + } +} +.hamburger-unread-badge { + border-radius: 100%; + border: 2px solid white; + top: 6px; + right: 10px ~'; /** orientation=lt */ '; + left: 10px ~'; /** orientation=rt */ '; + width: 16px; + height: 16px; +} + +#HamburgerMenuSpaceLeftNavigationActions i.v-icon { + font-size: 20px !important; +} diff --git a/webapp/src/main/webapp/vue-apps/common/components/ExoDrawer.vue b/webapp/src/main/webapp/vue-apps/common/components/ExoDrawer.vue index 2d743133cc1..8278cc8288b 100644 --- a/webapp/src/main/webapp/vue-apps/common/components/ExoDrawer.vue +++ b/webapp/src/main/webapp/vue-apps/common/components/ExoDrawer.vue @@ -26,7 +26,9 @@ :class="goBackButton && 'ps-1'" class="pe-0"> - + {{ $vuetify.rtl && 'fa fa-arrow-right' || 'fa fa-arrow-left' }} @@ -172,6 +174,10 @@ export default { type: Boolean, default: false, }, + permanent: { + type: Boolean, + default: false, + }, }, data: () => ({ initialized: false, @@ -226,29 +232,34 @@ export default { this.$emit('expand-updated', this.expand); }, drawer() { - if (this.drawer) { - document.dispatchEvent(new CustomEvent('drawerOpened')); - if (!this.initialized) { - this.initialized = true; - } - eXo.openedDrawers.push(this); - this.$emit('opened'); - if (this.disablePullToRefresh) { - document.body.style.overscrollBehaviorY = 'contain'; - } - } else { - document.dispatchEvent(new CustomEvent('drawerClosed')); - if (eXo.openedDrawers) { - const currentOpenedDrawerIndex = eXo.openedDrawers.indexOf(this); - if (currentOpenedDrawerIndex >= 0) { - eXo.openedDrawers.splice(currentOpenedDrawerIndex, 1); + if (!this.permanent) { + if (this.drawer) { + document.dispatchEvent(new CustomEvent('drawerOpened')); + if (!this.initialized) { + this.initialized = true; + } + eXo.openedDrawers.push(this); + this.$emit('opened'); + if (this.disablePullToRefresh) { + document.body.style.overscrollBehaviorY = 'contain'; + } + } else { + document.dispatchEvent(new CustomEvent('drawerClosed')); + if (eXo.openedDrawers) { + const currentOpenedDrawerIndex = eXo.openedDrawers.indexOf(this); + if (currentOpenedDrawerIndex >= 0) { + eXo.openedDrawers.splice(currentOpenedDrawerIndex, 1); + } + } + this.$emit('closed'); + if (this.disablePullToRefresh) { + document.body.style.overscrollBehaviorY = ''; } } - this.$emit('closed'); - if (this.disablePullToRefresh) { - document.body.style.overscrollBehaviorY = ''; - } + } else if (!this.initialized) { + this.initialized = true; } + this.$emit('input', this.drawer); this.expand = this.expanded; }, @@ -313,6 +324,13 @@ export default { this.close(); } }, + goBack(event) { + if (this.$listeners?.['go-back']) { + this.$emit('go-back'); + } else { + this.close(event); + } + }, close(event) { if (this.confirmClose) { if (this.$refs.closeConfirmDialog) { diff --git a/webapp/src/main/webapp/vue-apps/common/components/FavoriteButton.vue b/webapp/src/main/webapp/vue-apps/common/components/FavoriteButton.vue index a2cce890583..c8a6a863b96 100644 --- a/webapp/src/main/webapp/vue-apps/common/components/FavoriteButton.vue +++ b/webapp/src/main/webapp/vue-apps/common/components/FavoriteButton.vue @@ -129,7 +129,7 @@ export default { return this.isFavorite && this.$t('Favorite.tooltip.DeleteFavorite') || this.$t('Favorite.tooltip.AddAsFavorite'); }, favoriteIconColor() { - return this.isFavorite && 'yellow--text text--darken-2 ml-n2px' || 'icon-default-color'; + return this.isFavorite && 'yellow--text text--darken-2 ma-n2px' || 'icon-default-color'; }, favoriteIcon() { return this.isFavorite && 'fas fa-star' || 'far fa-star'; diff --git a/webapp/src/main/webapp/vue-apps/common/js/NavigationUtils.js b/webapp/src/main/webapp/vue-apps/common/js/NavigationUtils.js index 8b8dbe40cdf..63f19cbeb31 100644 --- a/webapp/src/main/webapp/vue-apps/common/js/NavigationUtils.js +++ b/webapp/src/main/webapp/vue-apps/common/js/NavigationUtils.js @@ -31,3 +31,8 @@ export function getNavigationNodeTarget(navigation) { navigation.nodeTarget = navigation?.target === 'SAME_TAB' && '_self' || '_blank'; return navigation.nodeTarget; } + +export function getNavigationNodeRel(navigation) { + navigation.nodeRel = navigation?.target === 'NEW_TAB' && 'nofollow noreferrer noopener' || null; + return navigation.nodeRel; +} diff --git a/webapp/src/main/webapp/vue-apps/space-templates-management/js/SpaceTemplateService.js b/webapp/src/main/webapp/vue-apps/common/js/SpaceTemplateService.js similarity index 100% rename from webapp/src/main/webapp/vue-apps/space-templates-management/js/SpaceTemplateService.js rename to webapp/src/main/webapp/vue-apps/common/js/SpaceTemplateService.js diff --git a/webapp/src/main/webapp/vue-apps/common/js/SuggesterService.js b/webapp/src/main/webapp/vue-apps/common/js/SuggesterService.js index e4b9b1c0fa7..6586e378bdd 100644 --- a/webapp/src/main/webapp/vue-apps/common/js/SuggesterService.js +++ b/webapp/src/main/webapp/vue-apps/common/js/SuggesterService.js @@ -51,6 +51,7 @@ function searchSpaces(filter, items, onlyRedactor, excludeRedactionalSpace, only fullName: item.displayName, originalName: item.shortName, avatarUrl: item.avatarUrl ? item.avatarUrl : `/portal/rest/v1/social/spaces/${item.prettyName}/avatar`, + membersCount: item.membersCount, }, }); } diff --git a/webapp/src/main/webapp/vue-apps/common/js/Utils.js b/webapp/src/main/webapp/vue-apps/common/js/Utils.js index e3b705b76fa..e12b69d2dab 100644 --- a/webapp/src/main/webapp/vue-apps/common/js/Utils.js +++ b/webapp/src/main/webapp/vue-apps/common/js/Utils.js @@ -26,11 +26,25 @@ export function trim(text) { } export function includeExtensions(suffix) { - Object.keys(window.requirejs.s.contexts._.registry) - .filter(definedMofule => definedMofule.includes(suffix)) - .forEach(module => { - window.require([module], app => app.init && app.init()); - }); + if (!window.requirejs.loadedExtension) { + window.requirejs.loadedExtension = {}; + } + const modules = Object.keys(window.requirejs.s.contexts._.config.paths).filter(m => m?.includes?.(suffix)); + if (modules?.length) { + return Promise.all(modules.map(module => new Promise(resolve => + window.require([module], app => { + if (!window.requirejs.loadedExtension[module]) { + window.requirejs.loadedExtension[module] = true; + return Promise.resolve(app?.init?.()) + .then(resolve); + } else { + return resolve(); + } + }) + ))); + } else { + return Promise.resolve(); + } } export function blobToBase64(blob) { @@ -50,4 +64,26 @@ export function convertImageDataAsSrc(imageData) { } else { return imageData; } +} + +export function toLinkUrl(url) { + if (url?.indexOf?.('./') === 0) { + url = `${window.location.pathname.replace(/\/$/g, '')}${url.replace(/\.\//g, '/')}`; + } + if (url?.indexOf?.('/') === 0) { + url = `${window.location.origin}${url}`; + } + const useNonSSL = url?.indexOf('http://') === 0; + url = Autolinker.parse(url || '', { + urls: true, + email: false, + phone: false, + mention: false, + hashtag: false, + })?.[0]?.getUrl?.()?.replace?.('javascript:', ''); + if (useNonSSL) { + return url; + } else { + return url?.replace?.('http://', 'https://'); + } } \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/common/main.js b/webapp/src/main/webapp/vue-apps/common/main.js index 87424afab52..230477583f9 100644 --- a/webapp/src/main/webapp/vue-apps/common/main.js +++ b/webapp/src/main/webapp/vue-apps/common/main.js @@ -21,6 +21,7 @@ import * as profileSettingsService from '../common/js/ProfileSettingsService.js' import * as profileLabelService from '../common/js/ProfileLabelService.js'; import * as siteService from './js/SiteService.js'; import * as navigationUtils from './js/NavigationUtils.js'; +import * as spaceTemplateService from './js/SpaceTemplateService.js'; // get overrided components if exists if (extensionRegistry) { @@ -101,6 +102,9 @@ window.Object.defineProperty(Vue.prototype, '$navigationService', { window.Object.defineProperty(Vue.prototype, '$navigationUtils', { value: navigationUtils, }); +window.Object.defineProperty(Vue.prototype, '$spaceTemplateService', { + value: spaceTemplateService, +}); if (eXo.env.portal.userIdentityId) { window.Object.defineProperty(Vue.prototype, '$currentUserIdentity', { @@ -158,7 +162,7 @@ export function init(i18n) { }).$mount(drawersOverlayElement); } } - let parentNotificationsElement = document.querySelector('#bottom-all-container'); + let parentNotificationsElement = document.querySelector('#vuetify-apps') || document.querySelector('#bottom-all-container'); let alertNotificationsElement = parentNotificationsElement?.querySelector('#alert-notifications'); if (!alertNotificationsElement) { if (!parentNotificationsElement) { diff --git a/webapp/src/main/webapp/vue-apps/favorites-list-top-bar/components/TopBarFavoritesDrawer.vue b/webapp/src/main/webapp/vue-apps/favorites-list-top-bar/components/TopBarFavoritesDrawer.vue index f9861a0146d..7fdf8b73cde 100644 --- a/webapp/src/main/webapp/vue-apps/favorites-list-top-bar/components/TopBarFavoritesDrawer.vue +++ b/webapp/src/main/webapp/vue-apps/favorites-list-top-bar/components/TopBarFavoritesDrawer.vue @@ -99,7 +99,10 @@ export default { methods: { openDrawer() { this.retrieveFavoritesList(); - this.$refs.favoritesDrawer.open(); + window.require(['SHARED/favoriteDrawerExtensions'], () => { + Promise.resolve(this.$utils.includeExtensions('FavoriteDrawerExtension')) + .then(() => this.$refs.favoritesDrawer.open()); + }); }, retrieveFavoritesList() { this.loading = true; 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..c762f91114b 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 @@ -22,14 +22,14 @@ +
@@ -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/common/StickyPositionElement.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/common/StickyPositionElement.vue new file mode 100644 index 00000000000..d1997f220f6 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/common/StickyPositionElement.vue @@ -0,0 +1,107 @@ + + + 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..4ac37637f9e --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/NavigationSettings.vue @@ -0,0 +1,118 @@ + + + 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..34934a4129c --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebar.vue @@ -0,0 +1,312 @@ + + + \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebarAddButton.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebarAddButton.vue new file mode 100644 index 00000000000..51951d08c25 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebarAddButton.vue @@ -0,0 +1,123 @@ + + + \ 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..d9172f8a875 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebarPreview.vue @@ -0,0 +1,120 @@ + + + \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebarPreviewItem.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebarPreviewItem.vue new file mode 100644 index 00000000000..924c03c82d8 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/NavigationSettingsSidebarPreviewItem.vue @@ -0,0 +1,152 @@ + + + \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/drawer/NavigationSettingsAddSidebarLinkDrawer.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/drawer/NavigationSettingsAddSidebarLinkDrawer.vue new file mode 100644 index 00000000000..0833af29f48 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/drawer/NavigationSettingsAddSidebarLinkDrawer.vue @@ -0,0 +1,223 @@ + + + \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/drawer/NavigationSettingsAddSidebarSiteDrawer.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/drawer/NavigationSettingsAddSidebarSiteDrawer.vue new file mode 100644 index 00000000000..2ff4ce25ac0 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/drawer/NavigationSettingsAddSidebarSiteDrawer.vue @@ -0,0 +1,382 @@ + + + \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/drawer/NavigationSettingsAddSidebarSpacesDrawer.vue b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/drawer/NavigationSettingsAddSidebarSpacesDrawer.vue new file mode 100644 index 00000000000..3094ae424af --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/sidebar/drawer/NavigationSettingsAddSidebarSpacesDrawer.vue @@ -0,0 +1,336 @@ + + + \ 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..4d7db261420 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/topbar/NavigationSettingsTopbar.vue @@ -0,0 +1,266 @@ + + + \ 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..3aab24f67e7 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/components/navigation/topbar/NavigationSettingsTopbarPreview.vue @@ -0,0 +1,115 @@ + + + \ 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..ab1cc415e02 100644 --- a/webapp/src/main/webapp/vue-apps/general-settings/initComponents.js +++ b/webapp/src/main/webapp/vue-apps/general-settings/initComponents.js @@ -28,6 +28,7 @@ import BorderRadiusSelector from './components/branding/form/BorderRadiusSelecto import BackgroundImageAttachment from './components/branding/form/BackgroundImageAttachment.vue'; import BackgroundInput from './components/branding/form/BackgroundInput.vue'; import CustomStyleInput from './components/branding/form/CustomStyleInput.vue'; +import StickyPositionElement from './components/common/StickyPositionElement.vue'; import SiteBranding from './components/branding/SiteBranding.vue'; import SiteBrandingWindow from './components/branding/SiteBrandingWindow.vue'; @@ -39,6 +40,20 @@ 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'; +import NavigationSettingsSidebarPreviewItem from './components/navigation/sidebar/NavigationSettingsSidebarPreviewItem.vue'; +import NavigationSettingsSidebarAddButton from './components/navigation/sidebar/NavigationSettingsSidebarAddButton.vue'; + +import NavigationSettingsAddSidebarLinkDrawer from './components/navigation/sidebar/drawer/NavigationSettingsAddSidebarLinkDrawer.vue'; +import NavigationSettingsAddSidebarSiteDrawer from './components/navigation/sidebar/drawer/NavigationSettingsAddSidebarSiteDrawer.vue'; +import NavigationSettingsAddSidebarSpacesDrawer from './components/navigation/sidebar/drawer/NavigationSettingsAddSidebarSpacesDrawer.vue'; + const components = { 'portal-general-settings': GeneralSettings, 'portal-general-settings-branding-site-window': SiteBrandingWindow, @@ -57,6 +72,17 @@ 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, + 'portal-general-settings-navigation-settings-sidebar-preview-item': NavigationSettingsSidebarPreviewItem, + 'portal-general-settings-navigation-settings-sidebar-add-button': NavigationSettingsSidebarAddButton, + 'portal-general-settings-navigation-settings-sidebar-add-link-drawer': NavigationSettingsAddSidebarLinkDrawer, + 'portal-general-settings-navigation-settings-sidebar-add-site-drawer': NavigationSettingsAddSidebarSiteDrawer, + 'portal-general-settings-navigation-settings-sidebar-add-spaces-drawer': NavigationSettingsAddSidebarSpacesDrawer, + 'sticky-position-element': StickyPositionElement, }; 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..e3fa5a7bec3 --- /dev/null +++ b/webapp/src/main/webapp/vue-apps/general-settings/js/NavigationConfigurationService.js @@ -0,0 +1,49 @@ +/** + * 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) { + configuration = JSON.parse(JSON.stringify(configuration)); + configuration.sidebar.items.forEach(item => delete item.items); + 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..3076f3dbc70 100644 --- a/webapp/src/main/webapp/vue-apps/general-settings/services.js +++ b/webapp/src/main/webapp/vue-apps/general-settings/services.js @@ -19,10 +19,20 @@ 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, -}); -window.Object.defineProperty(Vue.prototype, '$languageSettingService', { - value: languageSettingService, -}); +if (!Vue.prototype.$registrationService) { + window.Object.defineProperty(Vue.prototype, '$registrationService', { + value: registrationService, + }); +} +if (!Vue.prototype.$languageSettingService) { + window.Object.defineProperty(Vue.prototype, '$languageSettingService', { + value: languageSettingService, + }); +} +if (!Vue.prototype.$navigationConfigurationService) { + window.Object.defineProperty(Vue.prototype, '$navigationConfigurationService', { + value: navigationConfigurationService, + }); +} diff --git a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/profile/ProfileHamburgerNavigation.vue b/webapp/src/main/webapp/vue-apps/hamburger-menu/components/profile/ProfileHamburgerNavigation.vue deleted file mode 100644 index 2326b2da9c5..00000000000 --- a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/profile/ProfileHamburgerNavigation.vue +++ /dev/null @@ -1,122 +0,0 @@ - - - - diff --git a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/recent-spaces/RecentSpacesHamburgerNavigation.vue b/webapp/src/main/webapp/vue-apps/hamburger-menu/components/recent-spaces/RecentSpacesHamburgerNavigation.vue deleted file mode 100644 index 847b1f6cb06..00000000000 --- a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/recent-spaces/RecentSpacesHamburgerNavigation.vue +++ /dev/null @@ -1,150 +0,0 @@ - - - diff --git a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/recent-spaces/SpaceNavigationItem.vue b/webapp/src/main/webapp/vue-apps/hamburger-menu/components/recent-spaces/SpaceNavigationItem.vue deleted file mode 100644 index 5856df996cd..00000000000 --- a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/recent-spaces/SpaceNavigationItem.vue +++ /dev/null @@ -1,261 +0,0 @@ - - - diff --git a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/recent-spaces/SpacesHamburgerNavigation.vue b/webapp/src/main/webapp/vue-apps/hamburger-menu/components/recent-spaces/SpacesHamburgerNavigation.vue deleted file mode 100644 index c91c956923c..00000000000 --- a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/recent-spaces/SpacesHamburgerNavigation.vue +++ /dev/null @@ -1,193 +0,0 @@ - - - diff --git a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/site/SiteHamburgerItem.vue b/webapp/src/main/webapp/vue-apps/hamburger-menu/components/site/SiteHamburgerItem.vue deleted file mode 100644 index 69f46e69c1c..00000000000 --- a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/site/SiteHamburgerItem.vue +++ /dev/null @@ -1,161 +0,0 @@ - - - - diff --git a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/site/SiteHamburgerNavigation.vue b/webapp/src/main/webapp/vue-apps/hamburger-menu/components/site/SiteHamburgerNavigation.vue deleted file mode 100644 index c8aff75c4ad..00000000000 --- a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/site/SiteHamburgerNavigation.vue +++ /dev/null @@ -1,178 +0,0 @@ - - - - \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/user/UserHamburgerNavigation.vue b/webapp/src/main/webapp/vue-apps/hamburger-menu/components/user/UserHamburgerNavigation.vue deleted file mode 100644 index ca05a2d5885..00000000000 --- a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/user/UserHamburgerNavigation.vue +++ /dev/null @@ -1,99 +0,0 @@ - - - - diff --git a/webapp/src/main/webapp/vue-apps/hamburger-menu/initComponents.js b/webapp/src/main/webapp/vue-apps/hamburger-menu/initComponents.js deleted file mode 100644 index 80c08a81902..00000000000 --- a/webapp/src/main/webapp/vue-apps/hamburger-menu/initComponents.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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. - */ -import HamburgerMenuNavigation from './components/HamburgerMenuNavigation.vue'; -import HamburgerMenuNavigationButton from './components/HamburgerMenuNavigationButton.vue'; -import HamburgerMenuParentDrawer from './components/HamburgerMenuParentDrawer.vue'; -import HamburgerMenuParentSticky from './components/HamburgerMenuParentSticky.vue'; -import HamburgerMenuNavigationFirstLevel from './components/HamburgerMenuNavigationFirstLevel.vue'; -import HamburgerMenuNavigationSecondLevel from './components/HamburgerMenuNavigationSecondLevel.vue'; -import HamburgerMenuNavigationThirdLevel from './components/HamburgerMenuNavigationThirdLevel.vue'; -import ProfileHamburgerNavigation from './components/profile/ProfileHamburgerNavigation.vue'; -import RecentSpacesHamburgerNavigation from './components/recent-spaces/RecentSpacesHamburgerNavigation.vue'; -import SpaceNavigationItem from './components/recent-spaces/SpaceNavigationItem.vue'; -import SpacePanelHamburgerNavigation from './components/recent-spaces/SpacePanelHamburgerNavigation.vue'; -import SpacesHamburgerNavigation from './components/recent-spaces/SpacesHamburgerNavigation.vue'; -import SpacesNavigationContent from './components/recent-spaces/SpacesNavigationContent.vue'; -import SpacesNavigationEmpty from './components/recent-spaces/SpacesNavigationEmpty.vue'; -import SpaceHamburgerActionMenu from './components/recent-spaces/SpaceHamburgerActionMenu.vue'; -import SiteHamburgerNavigation from './components/site/SiteHamburgerNavigation.vue'; -import UserHamburgerNavigation from './components/user/UserHamburgerNavigation.vue'; -import SitesHamburger from './components/site/SitesHamburger.vue'; -import SiteHamburgerItem from './components/site/SiteHamburgerItem.vue'; - -const components = { - 'hamburger-menu-navigation': HamburgerMenuNavigation, - 'hamburger-menu-navigation-button': HamburgerMenuNavigationButton, - 'hamburger-menu-parent-drawer': HamburgerMenuParentDrawer, - 'hamburger-menu-parent-menu': HamburgerMenuParentSticky, - 'hamburger-menu-navigation-first-level': HamburgerMenuNavigationFirstLevel, - 'hamburger-menu-navigation-second-level': HamburgerMenuNavigationSecondLevel, - 'hamburger-menu-navigation-third-level': HamburgerMenuNavigationThirdLevel, - 'profile-hamburger-navigation': ProfileHamburgerNavigation, - 'spaces-hamburger-navigation': SpacesHamburgerNavigation, - 'recent-spaces-hamburger-navigation': RecentSpacesHamburgerNavigation, - 'space-navigation-item': SpaceNavigationItem, - 'space-panel-hamburger-navigation': SpacePanelHamburgerNavigation, - 'spaces-navigation-content': SpacesNavigationContent, - 'spaces-navigation-empty': SpacesNavigationEmpty, - 'space-hamburger-action-menu': SpaceHamburgerActionMenu, - 'site-hamburger-navigation': SiteHamburgerNavigation, - 'user-hamburger-navigation': UserHamburgerNavigation, - 'sites-hamburger': SitesHamburger, - 'site-hamburger-item': SiteHamburgerItem, -}; - -for (const key in components) { - Vue.component(key, components[key]); -} diff --git a/webapp/src/main/webapp/vue-apps/hamburger-menu/main.js b/webapp/src/main/webapp/vue-apps/hamburger-menu/main.js deleted file mode 100644 index c55a6f91938..00000000000 --- a/webapp/src/main/webapp/vue-apps/hamburger-menu/main.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of the Meeds project (https://meeds.io/). - * - * Copyright (C) 2020 - 2023 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. - */ -import './initComponents.js'; -import './extensions.js'; - -// get overrided components if exists -if (extensionRegistry) { - const components = extensionRegistry.loadComponents('HamburgerMenu'); - if (components && components.length > 0) { - components.forEach(cmp => { - Vue.component(cmp.componentName, cmp.componentOptions); - }); - } -} - -Vuetify.prototype.preset = eXo.env.portal.vuetifyPreset; - -const lang = eXo && eXo.env && eXo.env.portal && eXo.env.portal.language || 'en'; -const url = `/social/i18n/locale.portal.HamburgerMenu?lang=${lang}`; - -document.dispatchEvent(new CustomEvent('displayTopBarLoading')); - -const appId = 'HamburgerNavigationMenu'; - -export function init(canAddSpaces) { - exoi18n.loadLanguageAsync(lang, url) - .then(i18n => { - // init Vue app when locale ressources are ready - Vue.createApp({ - data: { - canAddSpaces, - rtl: eXo.env.portal.orientation === 'rtl', - ltr: eXo.env.portal.orientation === 'ltr', - }, - mounted() { - document.dispatchEvent(new CustomEvent('hideTopBarLoading')); - }, - template: ``, - i18n, - vuetify: Vue.prototype.vuetifyOptions, - }, `#${appId}`, 'Hamburger Menu'); - }); -} \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/links/components/settings/LinkFormDrawer.vue b/webapp/src/main/webapp/vue-apps/links/components/settings/LinkFormDrawer.vue index 4a03ba2f085..31e68a34159 100644 --- a/webapp/src/main/webapp/vue-apps/links/components/settings/LinkFormDrawer.vue +++ b/webapp/src/main/webapp/vue-apps/links/components/settings/LinkFormDrawer.vue @@ -174,7 +174,7 @@ export default { v => !!v?.length || ' ', v => { try { - return !!this.$linkService.toLinkUrl(v)?.length || this.$t('links.input.invalidLink'); + return !!this.$utils.toLinkUrl(v)?.length || this.$t('links.input.invalidLink'); } catch (e) { return this.$t('links.input.invalidLink'); } diff --git a/webapp/src/main/webapp/vue-apps/links/components/settings/LinkInput.vue b/webapp/src/main/webapp/vue-apps/links/components/settings/LinkInput.vue index 1a732ce1f1b..017f7f767b6 100644 --- a/webapp/src/main/webapp/vue-apps/links/components/settings/LinkInput.vue +++ b/webapp/src/main/webapp/vue-apps/links/components/settings/LinkInput.vue @@ -88,7 +88,7 @@ export default { && (this.link?.name[this.$root.language] || this.link?.name[this.$root.defaultLanguage]); }, url() { - return this.$linkService.toLinkUrl(this.link?.url); + return this.$utils.toLinkUrl(this.link?.url); }, iconUrl() { if (this.link?.iconSrc) { diff --git a/webapp/src/main/webapp/vue-apps/links/components/settings/LinkSettingsDrawer.vue b/webapp/src/main/webapp/vue-apps/links/components/settings/LinkSettingsDrawer.vue index 43aa97d9ade..cc02329891c 100644 --- a/webapp/src/main/webapp/vue-apps/links/components/settings/LinkSettingsDrawer.vue +++ b/webapp/src/main/webapp/vue-apps/links/components/settings/LinkSettingsDrawer.vue @@ -340,7 +340,7 @@ export default { v => !!v?.length || ' ', v => { try { - return !!this.$linkService.toLinkUrl(v)?.length || this.$t('links.input.invalidLink'); + return !!this.$utils.toLinkUrl(v)?.length || this.$t('links.input.invalidLink'); } catch (e) { return this.$t('links.input.invalidLink'); } diff --git a/webapp/src/main/webapp/vue-apps/links/components/view/LinksCard.vue b/webapp/src/main/webapp/vue-apps/links/components/view/LinksCard.vue index 760d9a87adb..55e427dab93 100644 --- a/webapp/src/main/webapp/vue-apps/links/components/view/LinksCard.vue +++ b/webapp/src/main/webapp/vue-apps/links/components/view/LinksCard.vue @@ -85,7 +85,7 @@ export default { return this.link?.description?.[this.$root.language] || this.link?.description?.[this.$root.defaultLanguage]; }, url() { - return this.$linkService.toLinkUrl(this.link?.url); + return this.$utils.toLinkUrl(this.link?.url); }, target() { return this.link?.sameTab && '_self' || '_blank'; diff --git a/webapp/src/main/webapp/vue-apps/links/components/view/LinksColumn.vue b/webapp/src/main/webapp/vue-apps/links/components/view/LinksColumn.vue index 3ea91968858..4967aefe0b7 100644 --- a/webapp/src/main/webapp/vue-apps/links/components/view/LinksColumn.vue +++ b/webapp/src/main/webapp/vue-apps/links/components/view/LinksColumn.vue @@ -74,7 +74,7 @@ export default { return this.link?.description?.[this.$root.language] || this.link?.description?.[this.$root.defaultLanguage]; }, url() { - return this.$linkService.toLinkUrl(this.link?.url); + return this.$utils.toLinkUrl(this.link?.url); }, target() { return this.link?.sameTab && '_self' || '_blank'; diff --git a/webapp/src/main/webapp/vue-apps/links/components/view/LinksHeader.vue b/webapp/src/main/webapp/vue-apps/links/components/view/LinksHeader.vue index 6a72489e2ea..0619d4afa50 100644 --- a/webapp/src/main/webapp/vue-apps/links/components/view/LinksHeader.vue +++ b/webapp/src/main/webapp/vue-apps/links/components/view/LinksHeader.vue @@ -121,7 +121,7 @@ export default { return this.settings?.header?.[this.$root.language] || this.settings?.header?.[this.$root.defaultLanguage]; }, seeMoreUrl() { - return this.$linkService.toLinkUrl(this.settings?.seeMore); + return this.$utils.toLinkUrl(this.settings?.seeMore); }, }, }; diff --git a/webapp/src/main/webapp/vue-apps/links/components/view/LinksList.vue b/webapp/src/main/webapp/vue-apps/links/components/view/LinksList.vue index bde2946618b..d6de2ff15a6 100644 --- a/webapp/src/main/webapp/vue-apps/links/components/view/LinksList.vue +++ b/webapp/src/main/webapp/vue-apps/links/components/view/LinksList.vue @@ -69,7 +69,7 @@ export default { return this.settings?.header?.[this.$root.language] || this.settings?.header?.[this.$root.defaultLanguage]; }, seeMoreUrl() { - return this.$linkService.toLinkUrl(this.settings?.seeMore); + return this.$utils.toLinkUrl(this.settings?.seeMore); }, isColumn() { return this.type === 'COLUMN'; diff --git a/webapp/src/main/webapp/vue-apps/links/js/LinkService.js b/webapp/src/main/webapp/vue-apps/links/js/LinkService.js index 25bf48f17f2..e753c1094a0 100644 --- a/webapp/src/main/webapp/vue-apps/links/js/LinkService.js +++ b/webapp/src/main/webapp/vue-apps/links/js/LinkService.js @@ -69,25 +69,3 @@ export function saveSettingName(url, name) { } }); } - -export function toLinkUrl(url) { - if (url?.indexOf?.('./') === 0) { - url = `${window.location.pathname.replace(/\/$/g, '')}${url.replace(/\.\//g, '/')}`; - } - if (url?.indexOf?.('/') === 0) { - url = `${window.location.origin}${url}`; - } - const useNonSSL = url?.indexOf('http://') === 0; - url = Autolinker.parse(url || '', { - urls: true, - email: false, - phone: false, - mention: false, - hashtag: false, - })?.[0]?.getUrl?.()?.replace?.('javascript:', ''); - if (useNonSSL) { - return url; - } else { - return url?.replace?.('http://', 'https://'); - } -} \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/notification-extensions/components/UserNotification.vue b/webapp/src/main/webapp/vue-apps/notification-extensions/components/UserNotification.vue index 52a18556d6f..043e741ee22 100644 --- a/webapp/src/main/webapp/vue-apps/notification-extensions/components/UserNotification.vue +++ b/webapp/src/main/webapp/vue-apps/notification-extensions/components/UserNotification.vue @@ -37,15 +37,19 @@ export default { default: null, }, }, + data: () => ({ + refresh: 1, + }), computed: { extension() { - return Object.values(this.$root.notificationExtensions) - .sort((ext1, ext2) => (ext1.rank || 0) - (ext2.rank || 0)) - .find(extension => extension.match && extension.match(this.notification) || extension.type === this.notification.plugin) + return this.refresh > 0 + && Object.values(this.$root.notificationExtensions) + .sort((ext1, ext2) => (ext1.rank || 0) - (ext2.rank || 0)) + .find(extension => extension.match && extension.match(this.notification) || extension.type === this.notification.plugin) || null; }, extensionComponent() { - return this.extension && { + return this.$root.notificationExtensions && this.extension?.vueComponent && { componentName: 'notification-extension', componentOptions: { vueComponent: this.extension.vueComponent, @@ -58,5 +62,16 @@ export default { }; }, }, + created() { + document.addEventListener('notification-extensions-refresh', this.forceRefreshExtension); + }, + beforeDestroy() { + document.removeEventListener('notification-extensions-refresh', this.forceRefreshExtension); + }, + methods: { + refreshExtension() { + this.refresh++; + }, + }, }; \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/notification-top-bar/components/TopBarNotification.vue b/webapp/src/main/webapp/vue-apps/notification-top-bar/components/TopBarNotification.vue index 8e7f7b71d3c..3eb12b03248 100644 --- a/webapp/src/main/webapp/vue-apps/notification-top-bar/components/TopBarNotification.vue +++ b/webapp/src/main/webapp/vue-apps/notification-top-bar/components/TopBarNotification.vue @@ -23,9 +23,10 @@ ({ badge: 0, open: false, + loading: false, }), watch: { open() { @@ -73,8 +75,15 @@ export default { methods: { openDrawer() { this.$root.initialized = false; + this.loading = true; this.$root.lastLoadedNotificationIndex = 0; - this.open = true; + window.require(['SHARED/notificationExtensions'], () => { + Promise.resolve(this.$utils.includeExtensions('NotificationExtension')) + .then(() => { + this.open = true; + this.loading = false; + }); + }); }, updateBadgeByEvent(event) { this.updateBadge(event?.detail?.data?.numberOnBadge || 0); diff --git a/webapp/src/main/webapp/vue-apps/notification-top-bar/main.js b/webapp/src/main/webapp/vue-apps/notification-top-bar/main.js index dbc611a4a00..02a59b1b7c2 100644 --- a/webapp/src/main/webapp/vue-apps/notification-top-bar/main.js +++ b/webapp/src/main/webapp/vue-apps/notification-top-bar/main.js @@ -60,18 +60,23 @@ export function init(badge) { }, methods: { refreshNotificationExtensions() { + const notificationExtensions = {}; const extensions = extensionRegistry.loadExtensions('WebNotification', 'notification-content-extension'); extensions.forEach(extension => { - if (extension.type) { - this.$set(this.notificationExtensions, extension.type, extension); + if (extension.type && !this.notificationExtensions[extension.type]) { + notificationExtensions[extension.type] = extension; } }); + this.notificationExtensions = { + ...this.notificationExtensions, + ...notificationExtensions + }; + document.dispatchEvent(new CustomEvent('notification-extensions-refresh')); }, }, }, `#${appId}`, 'Topbar Notifications'); }) .finally(() => { Vue.prototype.$utils.includeExtensions('NotificationPopoverExtension'); - Vue.prototype.$utils.includeExtensions('NotificationExtension'); }); } \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/platform-settings/components/PlatformSettings.vue b/webapp/src/main/webapp/vue-apps/platform-settings/components/PlatformSettings.vue index 2de494e8717..f716637d99c 100644 --- a/webapp/src/main/webapp/vue-apps/platform-settings/components/PlatformSettings.vue +++ b/webapp/src/main/webapp/vue-apps/platform-settings/components/PlatformSettings.vue @@ -3,10 +3,10 @@ \ No newline at end of file + + \ No newline at end of file diff --git a/webapp/src/main/webapp/vue-apps/platform-settings/main.js b/webapp/src/main/webapp/vue-apps/platform-settings/main.js index 7cb6da3eced..ba8685d31f7 100644 --- a/webapp/src/main/webapp/vue-apps/platform-settings/main.js +++ b/webapp/src/main/webapp/vue-apps/platform-settings/main.js @@ -33,12 +33,9 @@ const appId = 'platformSettings'; const lang = window?.eXo?.env?.portal?.language || 'en'; const i18NUrl = `/social/i18n/locale.portlet.PlatformSettings?lang=${lang}`; -export function init(url) { +export function init() { exoi18n.loadLanguageAsync(lang, i18NUrl).then(i18n => { Vue.createApp({ - data: { - url, - }, mounted() { this.$root.$applicationLoaded(); }, diff --git a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/HamburgerMenuNavigation.vue b/webapp/src/main/webapp/vue-apps/sidebar/components/Sidebar.vue similarity index 62% rename from webapp/src/main/webapp/vue-apps/hamburger-menu/components/HamburgerMenuNavigation.vue rename to webapp/src/main/webapp/vue-apps/sidebar/components/Sidebar.vue index b7935bef7bd..4900416af5e 100644 --- a/webapp/src/main/webapp/vue-apps/hamburger-menu/components/HamburgerMenuNavigation.vue +++ b/webapp/src/main/webapp/vue-apps/sidebar/components/Sidebar.vue @@ -8,6 +8,7 @@ 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 @@ -23,75 +24,62 @@ color="transaprent" class="HamburgerNavigationMenu" flat> - -
-