CommunityWebController.java

/*
 * Copyright (C) 2000 - 2024 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 javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.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"));
  }
}