# CommunityWebController.java

    /*
     * Copyright (C) 2000 - 2026 Silverpeas
     *
     * This program is free software: you can redistribute it and/or modify
     * it under the terms of the GNU Affero General Public License as
     * published by the Free Software Foundation, either version 3 of the
     * License, or (at your option) any later version.
     *
     * As a special exception to the terms and conditions of version 3.0 of
     * the GPL, you may redistribute this Program in connection with Free/Libre
     * Open Source Software ("FLOSS") applications as described in Silverpeas's
     * FLOSS exception.  You should have received a copy of the text describing
     * the FLOSS exception, and it is also available here:
     * "http://www.silverpeas.com/legal/licensing"
     *
     * 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 Affero General Public License for more details.
     *
     * You should have received a copy of the GNU Affero General Public License
     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     */
    package org.silverpeas.components.community.control;

    import org.apache.ecs.ElementContainer;
    import org.apache.ecs.xhtml.div;
    import org.silverpeas.components.community.CommunityComponentSettings;
    import org.silverpeas.components.community.CommunityWebManager;
    import org.silverpeas.components.community.model.CommunityOfUsers;
    import org.silverpeas.core.admin.service.OrganizationController;
    import org.silverpeas.core.admin.space.SpaceHomePageType;
    import org.silverpeas.core.admin.space.SpaceInst;
    import org.silverpeas.core.admin.user.model.SilverpeasRole;
    import org.silverpeas.core.admin.user.model.User;
    import org.silverpeas.core.contribution.model.WysiwygContent;
    import org.silverpeas.core.html.WebPlugin;
    import org.silverpeas.kernel.bundle.LocalizationBundle;
    import org.silverpeas.kernel.util.Pair;
    import org.silverpeas.core.web.http.HttpRequest;
    import org.silverpeas.core.web.look.proxy.SpaceHomepageProxy;
    import org.silverpeas.core.web.look.proxy.SpaceHomepageProxyManager;
    import org.silverpeas.core.web.mvc.controller.ComponentContext;
    import org.silverpeas.core.web.mvc.controller.MainSessionController;
    import org.silverpeas.core.web.mvc.webcomponent.AbstractNavigationContextListener;
    import org.silverpeas.core.web.mvc.webcomponent.Navigation;
    import org.silverpeas.core.web.mvc.webcomponent.NavigationContext;
    import org.silverpeas.core.web.mvc.webcomponent.annotation.Homepage;
    import org.silverpeas.core.web.mvc.webcomponent.annotation.LowestRoleAccess;
    import org.silverpeas.core.web.mvc.webcomponent.annotation.NavigationStep;
    import org.silverpeas.core.web.mvc.webcomponent.annotation.RedirectTo;
    import org.silverpeas.core.web.mvc.webcomponent.annotation.RedirectToInternal;
    import org.silverpeas.core.web.mvc.webcomponent.annotation.RedirectToInternalJsp;
    import org.silverpeas.core.web.mvc.webcomponent.annotation.WebComponentController;

    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.POST;
    import jakarta.ws.rs.Path;
    import jakarta.ws.rs.Produces;
    import jakarta.ws.rs.core.MediaType;

    import static org.silverpeas.components.community.CommunityWebManager.NO_PAGINATION;
    import static org.silverpeas.components.community.web.html.JavascriptPluginInclusion.COMMUNITY_SUBSCRIPTION;
    import static org.silverpeas.core.util.URLUtil.getApplicationURL;
    import static org.silverpeas.core.util.WebEncodeHelper.javaStringToJsString;
    import static org.silverpeas.core.web.look.LookHelper.getLookHelper;
    import static org.silverpeas.core.web.mvc.util.WysiwygRouting.WysiwygRoutingContext.fromComponentSessionController;
    import static org.silverpeas.core.web.util.viewgenerator.html.JavascriptPluginInclusion.scriptContent;

    /**
     * <p>
     * The Web Component Controller of the application.
     * </p>
     * <p>
     * It takes in charge, per user, the web navigation of the user in the application. It is a session
     * scoped bean; it is instantiated for each user session.
     * </p>
     */
    @SuppressWarnings("CdiManagedBeanInconsistencyInspection")
    @WebComponentController(CommunityComponentSettings.COMPONENT_NAME)
    public class CommunityWebController extends
        org.silverpeas.core.web.mvc.webcomponent.WebComponentController<CommunityWebRequestContext> {
      private static final long serialVersionUID = -8606482122697353961L;

      // Some navigation step identifier definitions
      private static final String MEMBER_LIST_NS_ID = "memberListNavStepIdentifier";
      private static final String HISTORY_VIEW_NS_ID = "historyViewNavStepIdentifier";

      // Some identifier definitions
      public static final String MEMBER_LIST_ARRAYPANE_IDENTIFIER = "communityMemberListIdentifier";
      public static final String HISTORY_ARRAYPANE_IDENTIFIER = "communityHistoryViewIdentifier";

      /**
       * Standard Web Controller Constructor.
       * @param mainSessionCtrl the main user session controller.
       * @param componentContext The component's context.
       */
      public CommunityWebController(MainSessionController mainSessionCtrl,
          ComponentContext componentContext) {
        super(mainSessionCtrl, componentContext, CommunityComponentSettings.MESSAGES_PATH,
            CommunityComponentSettings.SETTINGS_PATH, CommunityComponentSettings.SETTINGS_PATH);
      }

      /**
       * This method is called one times once this web component controller is instantiated for a given
       * user.
       * You can perform here some specific treatments here. For example, you can register Web
       * navigation listeners that will be invoked at each navigation step change. For simple web
       * navigation, this method is usually empty.
       * @param context the web request context.
       */
      @Override
      protected void onInstantiation(final CommunityWebRequestContext context) {
        context.getNavigationContext()
            .addListener(new AbstractNavigationContextListener<CommunityWebRequestContext>() {
              @Override
              public void navigationContextCleared(
                  final NavigationContext<CommunityWebRequestContext> navigationContext) {
                super.navigationContextCleared(navigationContext);
                clearArrayStates(navigationContext.getWebComponentRequestContext());
              }

              @Override
              public void navigationStepCreated(
                  final NavigationContext<CommunityWebRequestContext> navigationContext) {
                super.navigationStepCreated(navigationContext);
                final var navigationStep = navigationContext.getCurrentNavigationStep();
                final LocalizationBundle multilang = context.getMultilang();
                if (MEMBER_LIST_NS_ID.equals(navigationStep.getIdentifier())) {
                  navigationStep.withLabel(multilang.getString("community.browsebar.item.members"));
                } else if (HISTORY_VIEW_NS_ID.equals(navigationStep.getIdentifier())) {
                  navigationStep.withLabel(multilang.getString("community.browsebar.item.history"));
                }
              }

              /**
               * Clears the state of arrays.
               */
              private void clearArrayStates(CommunityWebRequestContext context) {
                setSessionAttribute(context, MEMBER_LIST_ARRAYPANE_IDENTIFIER, null);
                setSessionAttribute(context, HISTORY_ARRAYPANE_IDENTIFIER, null);
              }
            });
      }

      @Override
      protected void beforeRequestProcessing(final CommunityWebRequestContext context) {
        super.beforeRequestProcessing(context);
        final HttpRequest request = context.getRequest();
        request.setAttribute("communityOfUsers", context.getCommunity());
        request.setAttribute("adminMustValidateNewMember", context.adminMustValidateNewMember());
        request.setAttribute("isSpaceHomepage", context.isSpaceHomePage());
        request.setAttribute("isMember", context.isMember());
        request.setAttribute("isMembershipPending", context.isMembershipPending());
      }

      /**
       * Prepares the rendering of the home page.
       * @param context the context of the incoming request.
       */
      @GET
      @Path("Main")
      @Homepage
      @RedirectToInternal("{view}?ComponentMainPage={isComponentMainPage}")
      public void home(CommunityWebRequestContext context) {
        if (context.isMember() && context.isSpaceHomePage()) {
          context.addRedirectVariable("view", "spaceHomepageProxy");
          context.addRedirectVariable("isComponentMainPage", Boolean.FALSE.toString());
        } else {
          context.addRedirectVariable("view", "appHomepage");
          context.addRedirectVariable("isComponentMainPage", Boolean.TRUE.toString());
        }
      }

      /**
       * Prepares the rendering of the home page.
       * @param context the context of the incoming request.
       */
      @GET
      @Path("appHomepage")
      @RedirectToInternalJsp("main.jsp")
      public void appHomepage(CommunityWebRequestContext context) {
        final HttpRequest request = context.getRequest();
        final CommunityWebManager manager = CommunityWebManager.get();
        if (context.canValidateNewMember()) {
          final var membersToValidate =
              manager.getMembersToValidate(context.getCommunity(), NO_PAGINATION);
          request.setAttribute("membersToValidate", membersToValidate);
        }
        final WysiwygContent content = context.getCommunity().getSpacePresentationContent();
        request.setAttribute("spacePresentationContent", content.getRenderer().renderView());
        request.setAttribute("displayNbMembersForNonMembers", context.displayNbMembersForNonMembers());
        request.setAttribute("displayCharterOnSpaceHomepage", context.displayCharterOnSpaceHomepage());
      }

      @POST
      @Path("parameters/displayCharterOnSpaceHomepage")
      @Produces(MediaType.APPLICATION_JSON)
      public void setDisplayCharterOnSpaceHomepage(CommunityWebRequestContext context) {
        CommunityWebManager.get()
            .setDisplayCharterOnSpaceHomepage(context.getCommunity(),
                context.getRequest().getParameterAsBoolean("value"));
      }

      /**
       * Prepares the rendering of the home page.
       * @param context the context of the incoming request.
       */
      @GET
      @Path("spaceHomepageProxy")
      @RedirectTo("{path}?SpaceId={spaceId}")
      public void spaceHomepageProxy(CommunityWebRequestContext context) {
        final CommunityOfUsers community = context.getCommunity();
        final SpaceInst space = OrganizationController.get().getSpaceInstById(community.getSpaceId());
        final SpaceHomepageProxy proxy = SpaceHomepageProxyManager.get().getProxyOf(space);
        final Pair<String, SpaceHomePageType> homePage = community.getHomePage();
        proxy.setFirstPageType(homePage.getSecond().ordinal());
        proxy.setFirstPageExtraParam(homePage.getFirst());
        final ElementContainer xhtml = new ElementContainer();
        xhtml.addElement(WebPlugin.get().getHtml(COMMUNITY_SUBSCRIPTION, getLanguage()));
        final div communityHtml = new div();
        communityHtml.setID("community-membership");
        communityHtml.addElement(String.format("<silverpeas-community-membership \n" +
                "        v-bind:display-nb-members-for-non-members='%s' \n" +
                "        v-bind:display-charter-on-space-homepage='%s' \n" +
                "        v-on:membership-join='reloadPage' \n" +
                "        v-on:membership-leave='reloadPage'></silverpeas-community-membership>",
            context.displayNbMembersForNonMembers(),
            context.displayCharterOnSpaceHomepage()));
        xhtml.addElement(communityHtml);
        final String vueJsStarter = String.format("SpVue.createApp({\n" +
                "        provide : function() {\n" +
                "          return {\n" +
                "            context: this.context,\n" +
                "            communityService: new CommunityService(this.context),\n" +
                "            membershipService: new CommunityMembershipService(this.context)}},\n" +
                "        data : function() {\n" +
                "          return {\n" +
                "            context : {\n" +
                "              currentUser : currentUser,\n" +
                "              componentInstanceId : '%s',\n" +
                "              spaceLabel : '%s'}}},\n" +
                "        methods : {\n" +
                "          reloadPage : function() {\n" +
                "            spWindow.loadSpace('%s');}}\n" +
                "      }).mount('#community-membership');\n",
            community.getComponentInstanceId(),
            javaStringToJsString(context.getSpaceLabel()),
            community.getSpaceId());
        xhtml.addElement(scriptContent(vueJsStarter));
        final var topWidget = new SpaceHomepageProxy.Widget();
        topWidget.setTitle(context.getComponentInstanceLabel());
        topWidget.setContent(xhtml.toString());
        proxy.setThinWidget(topWidget);
        final String defaultHomepage = getLookHelper(
            context.getRequest().getSession(false)).getSettings("defaultHomepage", "/dt");
        context.addRedirectVariable("path", defaultHomepage);
        context.addRedirectVariable("spaceId", community.getSpaceId());
      }

      @GET
      @Path("members")
      @RedirectToInternalJsp("members.jsp")
      @NavigationStep(identifier = MEMBER_LIST_NS_ID)
      @LowestRoleAccess(value = SilverpeasRole.READER)
      public void listMembers(CommunityWebRequestContext context) {
        final HttpRequest request = context.getRequest();
        final var members = CommunityWebManager.get().getMembers(context.getCommunity(), NO_PAGINATION);
        request.setAttribute("members", members);
      }

      @POST
      @Path("members/join")
      @Produces(MediaType.APPLICATION_JSON)
      public void join(CommunityWebRequestContext context) {
        CommunityWebManager.get().join(context.getCommunity());
      }

      @POST
      @Path("members/join/validate/{userId}")
      @Produces(MediaType.APPLICATION_JSON)
      @LowestRoleAccess(value = SilverpeasRole.ADMIN)
      public void validate(CommunityWebRequestContext context) {
        final HttpRequest request = context.getRequest();
        final User requester = User.getById(context.getPathVariables().get("userId"));
        final boolean accept = request.getParameterAsBoolean("accept");
        final String message = request.getParameter("message");
        CommunityWebManager.get().validateRequestOf(requester, context.getCommunity(), accept, message);
      }

      @POST
      @Path("members/leave/{userId}")
      @LowestRoleAccess(value = SilverpeasRole.ADMIN)
      @Produces(MediaType.APPLICATION_JSON)
      public void endMembershipOf(CommunityWebRequestContext context) {
        final User member = User.getById(context.getPathVariables().get("userId"));
        CommunityWebManager.get().endMembershipOf(context.getCommunity(), member);
      }

      @POST
      @Path("members/leave")
      @LowestRoleAccess(value = SilverpeasRole.READER)
      @Produces(MediaType.APPLICATION_JSON)
      public void leave(CommunityWebRequestContext context) {
        final HttpRequest request = context.getRequest();
        final int reason = request.getParameterAsInteger("reason");
        final String message = request.getParameter("message");
        final boolean contactInFuture = request.getParameterAsBoolean("contactInFuture");
        CommunityWebManager.get().leave(context.getCommunity(), reason, message, contactInFuture);
      }

      @GET
      @Path("members/history")
      @RedirectToInternalJsp("history.jsp")
      @NavigationStep(identifier = HISTORY_VIEW_NS_ID)
      @LowestRoleAccess(value = SilverpeasRole.ADMIN)
      public void getHistory(CommunityWebRequestContext context) {
        final HttpRequest request = context.getRequest();
        final var history = CommunityWebManager.get().getHistory(context.getCommunity(), NO_PAGINATION);
        request.setAttribute("history", history);
      }

      @GET
      @Path("spaceHomepage/edit")
      public Navigation editSpaceHomePage(CommunityWebRequestContext context) {
        final WysiwygContent content = context.getCommunity().getSpacePresentationContent();
        return context.redirectToHtmlEditor(fromComponentSessionController(this)
            .withBrowseInfo(getString("community.browsebar.item.spaceHomePage.edition"))
            .withContributionId(content.getContribution().getIdentifier())
            .withIndexation(false)
            .withComeBackUrl(getApplicationURL() + context.getComponentUriBase() + "Main"));
      }
    }