InfoLetterRequestRouter.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:
 * "https://www.silverpeas.org/legal/floss_exception.html"
 *
 * 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 <https://www.gnu.org/licenses/>.
 */
package org.silverpeas.components.infoletter.servlets;

import org.silverpeas.components.infoletter.control.InfoLetterSessionController;
import org.silverpeas.components.infoletter.model.InfoLetter;
import org.silverpeas.components.infoletter.model.InfoLetterPublication;
import org.silverpeas.components.infoletter.model.InfoLetterPublicationPdC;
import org.silverpeas.core.contribution.content.ddwe.DragAndDropWbeFile;
import org.silverpeas.core.contribution.content.renderer.ContributionContentRenderer;
import org.silverpeas.core.contribution.model.ContributionContent;
import org.silverpeas.core.util.DateUtil;
import org.silverpeas.core.util.file.FileItem;
import org.silverpeas.kernel.util.StringUtil;
import org.silverpeas.core.web.http.HttpRequest;
import org.silverpeas.core.web.mvc.controller.ComponentContext;
import org.silverpeas.core.web.mvc.controller.MainSessionController;
import org.silverpeas.core.web.mvc.route.ComponentRequestRouter;
import org.silverpeas.core.web.util.viewgenerator.html.browsebars.BrowseBarElement;
import org.silverpeas.core.webapi.wbe.WbeFileEdition;

import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Set;

import static org.silverpeas.kernel.util.StringUtil.*;
import static org.silverpeas.core.web.ddwe.DragAndDropEditorConfig.withConnectors;

public class InfoLetterRequestRouter extends ComponentRequestRouter<InfoLetterSessionController> {

  private static final long serialVersionUID = 5722456216811272025L;
  private static final String ADMIN = "admin";
  private static final String PUBLISHER = "publisher";
  private static final String POST_METHOD = "POST";
  private static final String PUBLICATION = "parution";
  private static final String PUBLICATION_TITLE = "parutionTitle";
  private static final String TITLE = "title";
  private static final String DESCRIPTION = "description";
  private static final String BROWSE_BAR_PATH = "browseBarPath";
  private static final String LIST_EMAILS = "listEmails";
  private static final String INLINED_CSS_HTML = "inlinedCssHtml";
  private static final String HEADER_LETTER_JSP = "headerLetter.jsp";
  private static final String EMAILS_MANAGER_JSP = "emailsManager.jsp";
  private static final String PREVIEW = "Preview";
  private static final String RETURN_URL_ATTR = "ReturnUrl";
  private static final String INFO_LETTER_SENT_JSP = "infoLetterSended.jsp";
  private static final String EMAIL_ERRORS_ATTR = "EmailErrors";
  private static final String SHOW_HEADER_ATTR = "showHeader";

  @Override
  public InfoLetterSessionController createComponentSessionController(
      MainSessionController mainSessionCtrl, ComponentContext componentContext) {
    return new InfoLetterSessionController(mainSessionCtrl, componentContext);
  }

  /**
   * This method has to be implemented in the component request rooter class. returns the session
   * control bean name to be put in the request object ex : for almanach, returns "almanach"
   */
  @Override
  public String getSessionControlBeanName() {
    return "infoLetter";
  }

  private void setGlobalInfo(InfoLetterSessionController infoLetterSC, HttpServletRequest request) {
    String language = infoLetterSC.getLanguage();
    // the flag is the best user's profile
    String flag = getFlag(infoLetterSC.getUserRoles());
    request.setAttribute("userIsAdmin", ADMIN.equals(flag));
    request.setAttribute("isAnonymous", infoLetterSC.getUserDetail().isAnonymous());
    request.setAttribute("isAccessGuest", infoLetterSC.getUserDetail().isAccessGuest());
    request.setAttribute("isPdcUsed", infoLetterSC.isPdcUsed());
    request.setAttribute("language", language);
    request.setAttribute("browseContext",
        new String[]{infoLetterSC.getSpaceLabel(), infoLetterSC.getComponentLabel(),
            infoLetterSC.getSpaceId(), infoLetterSC.getComponentId(),
            infoLetterSC.getComponentUrl()});
  }

  private String setMainContext(InfoLetterSessionController infoLetterSC,
      HttpServletRequest request) {
    // the flag is the best user's profile
    loadCommonHomepageDataAndSetRequestAttributes(infoLetterSC, request);
    final boolean showHeader = infoLetterSC.getSettings().getBoolean(SHOW_HEADER_ATTR, false);
    request.setAttribute(SHOW_HEADER_ATTR, showHeader);
    final boolean isTemplateExist = infoLetterSC.getInfoLetter().existsTemplateContent();
    request.setAttribute("IsTemplateExist", isTemplateExist);
    final String flag = getFlag(infoLetterSC.getUserRoles());
    return (PUBLISHER.equals(flag) || ADMIN.equals(flag)) ?
        "listLetterAdmin.jsp" :
        "listLetterUser.jsp";
  }

  private void loadCommonHomepageDataAndSetRequestAttributes(
      final InfoLetterSessionController infoLetterSC, final HttpServletRequest request) {
    final InfoLetter defaultLetter = infoLetterSC.getInfoLetter();
    final String letterName = defaultStringIfNotDefined(defaultLetter.getName());
    final String letterDescription = defaultStringIfNotDefined(defaultLetter.getDescription());
    final String letterFrequency = defaultStringIfNotDefined(defaultLetter.getPeriode());
    final List<InfoLetterPublication> publications = infoLetterSC.getInfoLetterPublications();
    request.setAttribute("letterName", letterName);
    request.setAttribute("letterDescription", letterDescription);
    request.setAttribute("letterFrequence", letterFrequency);
    request.setAttribute("listParutions", publications);
    request.setAttribute("userIsSuscriber", infoLetterSC.isSubscriber());
  }

  /**
   * This method has to be implemented by the component request rooter it has to compute a
   * destination page
   * @param function The entering request function (ex : "Main.jsp")
   * @param infoLetterSC The component Session Control, build and initialised.
   * @param request the incoming HTTP request.
   * @return The complete destination URL for a forward (ex :
   * "/almanach/jsp/almanach.jsp?flag=user")
   */
  @Override
  public String getDestination(String function, InfoLetterSessionController infoLetterSC,
      HttpRequest request) {
    String destination;
    // the flag is the best user's profile
    String flag = getFlag(infoLetterSC.getUserRoles());
    try {
      setGlobalInfo(infoLetterSC, request);
      if ((function.startsWith("Accueil")) || (function.startsWith("Main"))) {
        destination = setMainContext(infoLetterSC, request);
      } else if ("View".equals(function)) {
        String publication = defaultStringIfNotDefined(param(request, PUBLICATION),
            param(request, "Id"));
        if (StringUtil.isDefined(publication)) {
          InfoLetterPublicationPdC ilp = infoLetterSC.getInfoLetterPublication(publication);
          request.setAttribute(PUBLICATION, publication);
          request.setAttribute(PUBLICATION_TITLE, ilp.getTitle());
          destination = "viewLetter.jsp";
        } else {
          destination = setMainContext(infoLetterSC, request);
        }
      } else if (function.startsWith("portlet")) {
        final boolean showHeader = infoLetterSC.getSettings().getBoolean(SHOW_HEADER_ATTR, false);
        request.setAttribute(SHOW_HEADER_ATTR, showHeader);
        loadCommonHomepageDataAndSetRequestAttributes(infoLetterSC, request);
        if (PUBLISHER.equals(flag) || ADMIN.equals(flag)) {
          destination = "portletListLetterAdmin.jsp?Profile=" + flag;
        } else {
          destination = "portletListLetterUser.jsp?Profile=" + flag;

        }
      } else if (function.startsWith(PREVIEW)) {
        // the preview shows an issue being written, which the readers get through View once it is
        // published only
        checkIsPublisherOrManager(flag, "preview an issue being written");
        String publication = param(request, PUBLICATION);
        if (StringUtil.isDefined(publication)) {
          InfoLetterPublicationPdC ilp = infoLetterSC.getInfoLetterPublication(publication);
          request.setAttribute("entity", ilp);
          request.setAttribute(PUBLICATION, publication);
          request.setAttribute(PUBLICATION_TITLE, ilp.getTitle());
          destination = "previewLetter.jsp";
        } else {
          destination = setMainContext(infoLetterSC, request);
        }
      } else if (function.startsWith("EditTemplateContent")) {
        checkIsManager(flag, "modify the template");
        final DragAndDropWbeFile file = infoLetterSC.getTemplateFileForEdition();
        return WbeFileEdition.get()
            .initializeWith(request, file,
                withConnectors("SaveTemplateContent", "Main")
                    .setMailMode()
                    .addBrowseBarElement(new BrowseBarElement(infoLetterSC.getString("infoLetter.template"), ""))
                    .build()::applyTo)
            .orElse(null);
      } else if (function.startsWith("SaveTemplateContent")) {
        checkIsManager(flag, "modify the template");
        final String manualContent = param(request, "editor");
        final InfoLetter infoLetter = infoLetterSC.getInfoLetter();
        infoLetter.saveTemplateContent(manualContent);
        destination = setMainContext(infoLetterSC, request);
      } else if (function.startsWith("EditContent")) {
        checkIsPublisherOrManager(flag, "modify the content of an issue");
        String publication = param(request, PUBLICATION);
        if (StringUtil.isDefined(publication)) {
          destination = null;
          final InfoLetterPublicationPdC ilp = infoLetterSC.getInfoLetterPublication(publication);
          if (!request.getParameterAsBoolean("old")) {
            final boolean resetWithTemplate = request.getParameterAsBoolean("resetWithTemplate");
            final String publicationParam = "?parution=" + publication;
            if (resetWithTemplate) {
              checkIsPublisherOrManager(flag, "reset the content of an issue");
              // unlike the edition itself, which is a mere navigation and hence remains a GET,
              // resetting the content overwrites it
              checkIsPostRequest(request, "Resetting the content of an issue");
              infoLetterSC.resetWithTemplateFor(ilp);
              return getDestination(PREVIEW, infoLetterSC, request);
            }
            final DragAndDropWbeFile file = infoLetterSC.getFileForEditionOf(ilp);
            final String validateUrl = "SaveContent" + publicationParam;
            final String cancelUrl = PREVIEW + publicationParam;
            destination = WbeFileEdition.get()
                .initializeWith(request, file,
                    withConnectors(validateUrl, cancelUrl).setMailMode().build()::applyTo)
                .orElse(null);
          }
          if (isNotDefined(destination)) {
            request.setAttribute(PUBLICATION, publication);
            request.setAttribute(PUBLICATION_TITLE, ilp.getTitle());
            request.setAttribute("parutionContent", ilp.getWysiwygContent()
                .map(ContributionContent::getRenderer)
                .map(ContributionContentRenderer::renderEdition)
                .orElse(EMPTY));
            destination = "editLetter.jsp";
          } else {
            return destination;
          }
        } else {
          destination = setMainContext(infoLetterSC, request);
        }
      } else if (function.startsWith("ParutionHeaders")) {
        checkIsPublisherOrManager(flag, "create or modify an issue");
        String publication = param(request, PUBLICATION);
        String title = "";
        String description = "";
        String browsBarPath = infoLetterSC.getString("infoLetter.newLetterHeader");
        if (!"".equals(publication)) {
          InfoLetterPublicationPdC ilp = infoLetterSC.getInfoLetterPublication(publication);
          title = ilp.getTitle();
          description = ilp.getDescription();
          browsBarPath = title;
        }
        request.setAttribute(PUBLICATION, publication);
        request.setAttribute(TITLE, title);
        request.setAttribute(DESCRIPTION, description);
        request.setAttribute(BROWSE_BAR_PATH, browsBarPath);
        destination = HEADER_LETTER_JSP;
      } else if (function.startsWith("ValidateParution")) {
        checkIsPublisherOrManager(flag, "publish an issue");
        String publication = param(request, PUBLICATION);
        String[] emailErrors = new String[0];
        if (StringUtil.isDefined(publication)) {
          InfoLetterPublicationPdC ilp = infoLetterSC.getInfoLetterPublication(publication);
          ilp.setPublicationState(InfoLetterPublication.PUBLICATION_VALIDEE);
          ilp.setParutionDate(DateUtil.date2SQLDate(new java.util.Date()));
          infoLetterSC.updateInfoLetterPublication(ilp);
          infoLetterSC.notifyInternalSubscribers(ilp);
          emailErrors = infoLetterSC.sendByMailToExternalSubscribers(ilp);
        }
        request.setAttribute(EMAIL_ERRORS_ATTR, emailErrors);
        destination = INFO_LETTER_SENT_JSP;
      } else if (function.startsWith("ChangeParutionHeaders")) {
        // the form is already submitted by POST, but the parameters would be read as well from the
        // query string of a GET, which no keyword of its URL makes the token required on
        checkIsPublisherOrManager(flag, "create or modify an issue");
        checkIsPostRequest(request, "Creating or modifying an issue");
        String publication = param(request, PUBLICATION);
        String title = param(request, TITLE);
        String description = param(request, DESCRIPTION);
        List<InfoLetter> listLettres = infoLetterSC.getInfoLetters();
        InfoLetter defaultLetter = listLettres.get(0);

        if (publication.isEmpty()) {
          InfoLetterPublicationPdC ilp = new InfoLetterPublicationPdC();
          ilp.setInstanceId(infoLetterSC.getComponentId());
          ilp.setTitle(title);
          ilp.setDescription(description);
          ilp.setPublicationState(InfoLetterPublication.PUBLICATION_EN_REDACTION);
          ilp.setLetterId(defaultLetter.getPK().getId());
          // Classify content
          String positions = request.getParameter("Positions");
          ilp.setPositions(positions);
          infoLetterSC.createInfoLetterPublication(ilp);
          publication = ilp.getPK().getId();
        } else {
          InfoLetterPublicationPdC ilp = infoLetterSC.getInfoLetterPublication(publication);
          ilp.setTitle(title);
          ilp.setDescription(description);
          infoLetterSC.updateInfoLetterPublication(ilp);
        }
        request.setAttribute(PUBLICATION, publication);
        request.setAttribute(TITLE, title);
        request.setAttribute(DESCRIPTION, description);
        request.setAttribute(BROWSE_BAR_PATH, title);
        destination = HEADER_LETTER_JSP;
      } else if (function.equals("DeletePublication")) {
        checkIsPublisherOrManager(flag, "delete an issue");
        final String id = request.getParameter("id");
        if (isDefined(id)) {
          infoLetterSC.deleteInfoLetterPublication(id);
        }
        destination = setMainContext(infoLetterSC, request);
      } else if (function.equals("DeletePublications")) {
        // deleting several issues at once is offered to the managers only
        checkIsManager(flag, "delete several issues at once");
        request.getParameterAsList("selectedIds")
            .forEach(infoLetterSC::deleteInfoLetterPublication);
        destination = setMainContext(infoLetterSC, request);
      } else if (function.startsWith("LetterHeaders")) {
        checkIsPublisherOrManager(flag, "modify the headers of the newsletter");
        InfoLetter defaultLetter = infoLetterSC.getInfoLetter();
        String letterName = defaultStringIfNotDefined(defaultLetter.getName());
        String letterDescription = defaultStringIfNotDefined(defaultLetter.getDescription());
        String letterFrequency = defaultStringIfNotDefined(defaultLetter.getPeriode());
        request.setAttribute("letterName", letterName);
        request.setAttribute("letterDescription", letterDescription);
        request.setAttribute("letterFrequence", letterFrequency);
        destination = "modifHeaders.jsp";
      } else if (function.startsWith("ChangeLetterHeaders")) {
        checkIsPublisherOrManager(flag, "modify the headers of the newsletter");
        checkIsPostRequest(request, "Modifying the headers of the newsletter");
        InfoLetter defaultLetter = infoLetterSC.getInfoLetter();
        String letterName = param(request, "name");
        String letterDescription = param(request, DESCRIPTION);
        String letterFrequency = param(request, "frequence");
        defaultLetter.setName(letterName);
        defaultLetter.setDescription(letterDescription);
        defaultLetter.setPeriode(letterFrequency);
        infoLetterSC.updateInfoLetter(defaultLetter);
        destination = setMainContext(infoLetterSC, request);
      } else if (function.startsWith("Emails")) {
        checkIsPublisherOrManager(flag, "list the external subscribers");
        Set<String> listEmails = infoLetterSC.getEmailsExternalsSubscribers();
        request.setAttribute(LIST_EMAILS, listEmails);
        destination = EMAILS_MANAGER_JSP;
      } else if (function.startsWith("SuscribeMe")) {
        // any user is entitled to subscribe themselves, but not to be lured into doing so
        checkIsPostRequest(request, "Subscribing");
        infoLetterSC.subscribeUser();
        destination = setMainContext(infoLetterSC, request);
      } else if (function.startsWith("UnsuscribeMe")) {
        checkIsPostRequest(request, "Unsubscribing");
        infoLetterSC.unsubscribeUser();
        destination = setMainContext(infoLetterSC, request);
      } else if (function.startsWith("DeleteEmails")) {
        checkIsPublisherOrManager(flag, "delete external subscribers");
        String[] emails = request.getParameterValues("mails");
        if (emails != null) {
          infoLetterSC.deleteExternalsSubscribers(emails);
        }
        Set<String> listEmails = infoLetterSC.getEmailsExternalsSubscribers();
        request.setAttribute(LIST_EMAILS, listEmails);
        destination = EMAILS_MANAGER_JSP;
      } else if (function.startsWith("DeleteAllEmails")) {
        checkIsPublisherOrManager(flag, "delete external subscribers");
        infoLetterSC.deleteAllExternalsSubscribers();
        Set<String> listEmails = infoLetterSC.getEmailsExternalsSubscribers();
        request.setAttribute(LIST_EMAILS, listEmails);
        destination = EMAILS_MANAGER_JSP;
      } else if (function.startsWith("AddMail")) {
        checkIsPublisherOrManager(flag, "add external subscribers");
        destination = "addEmail.jsp";
      } else if (function.startsWith("NewMail")) {
        checkIsPublisherOrManager(flag, "add external subscribers");
        checkIsPostRequest(request, "Adding external subscribers");
        String newMails = param(request, "newmails");
        infoLetterSC.addExternalsSubscribers(newMails);
        Set<String> listEmails = infoLetterSC.getEmailsExternalsSubscribers();
        request.setAttribute(LIST_EMAILS, listEmails);
        destination = EMAILS_MANAGER_JSP;
      } else if (function.startsWith("Suscribers")) {
        checkIsPublisherOrManager(flag, "manage the internal subscribers");
        destination = infoLetterSC.initUserPanel();
      } else if (function.startsWith("RetourPanel")) {
        // the subscribers applied here are the ones selected in the user panel, held by the
        // session and not carried by the request, so a GET is harmless enough to be kept: it is
        // the URL the user panel of Silverpeas goes back to
        checkIsPublisherOrManager(flag, "manage the internal subscribers");
        infoLetterSC.retourUserPanel();
        destination = setMainContext(infoLetterSC, request);
      } else if (function.equals("ViewTemplateInlinedCssHtml")) {
        final InfoLetter infoLetter = infoLetterSC.getInfoLetter();
        request.setAttribute(INLINED_CSS_HTML, infoLetter.getTemplateWysiwygContent()
            .map(ContributionContent::getRenderer)
            .map(r -> r.renderView(true))
            .orElse(EMPTY));
        destination = "inlinedCssHtml.jsp";
      }  else if (function.equals("ViewInlinedCssHtml")) {
        final String id = request.getParameter("id");
        final InfoLetterPublicationPdC pub = infoLetterSC.getInfoLetterPublication(id);
        // this is the way the readers get an issue from the list, so the role isn't the criterion
        // here: it is the issue itself, which is for everyone once published only
        if (!pub.isValid()) {
          checkIsPublisherOrManager(flag, "read an issue being written");
        }
        request.setAttribute(INLINED_CSS_HTML, pub.getWysiwygContent()
            .map(ContributionContent::getRenderer)
            .map(r -> r.renderView(true))
            .orElse(EMPTY));
        destination = "inlinedCssHtml.jsp";
      } else if (function.startsWith("searchResult")) {
        String id = defaultStringIfNotDefined(request.getParameter("Id"));
        String type = request.getParameter("Type");
        if (InfoLetter.TYPE.equals(type)) {
          destination = setMainContext(infoLetterSC, request);
        } else {
          if (!id.isEmpty()) {
            return getDestination("View", infoLetterSC, request);
          } else {
            destination = setMainContext(infoLetterSC, request);
          }
        }
      } else if (function.startsWith("ImportEmailsCsv")) {
        checkIsPublisherOrManager(flag, "import external subscribers");
        FileItem fileItem = request.getSingleFile();
        infoLetterSC.importCsvEmails(fileItem);
        destination = "importEmailsCsv.jsp?Result=OK";
      } else if (function.equals("ExportEmailsCsv")) {
        checkIsPublisherOrManager(flag, "export the external subscribers");
        boolean exportOk = infoLetterSC.exportCsvEmails();
        request.setAttribute("ExportOk", Boolean.toString(exportOk));
        if (exportOk) {
          request.setAttribute("EmailCsvName",
              infoLetterSC.getComponentId() + InfoLetterSessionController.EXPORT_CSV_NAME);
        }
        destination = "exportEmailsCsv.jsp";
      } else if (function.startsWith("SendLetterTo")) {
        // the issue being mailed isn't published yet, so its content isn't for every reader
        checkIsPublisherOrManager(flag, "mail an issue being written");
        String publication = param(request, PUBLICATION);
        String[] emailErrors = new String[0];
        if (StringUtil.isDefined(publication)) {
          InfoLetterPublicationPdC ilp = infoLetterSC.getInfoLetterPublication(publication);
          emailErrors = function.endsWith("Manager") ?
              infoLetterSC.notifyManagers(ilp) :
              infoLetterSC.notifyMe(ilp);
        }
        request.setAttribute(EMAIL_ERRORS_ATTR, emailErrors);
        final String returnUrl = defaultStringIfNotDefined(request.getParameter(RETURN_URL_ATTR),
            PREVIEW);
        request.setAttribute(RETURN_URL_ATTR, returnUrl + "?parution=" + publication);
        destination = INFO_LETTER_SENT_JSP;
      } else if (function.startsWith("SaveContent")) {
        checkIsPublisherOrManager(flag, "modify the content of an issue");
        String publication = param(request, PUBLICATION);
        String manualContent = param(request, "editor");
        if (StringUtil.isDefined(publication)) {
          InfoLetterPublicationPdC ilp = infoLetterSC.getInfoLetterPublication(publication);
          ilp.saveContent(manualContent);
          return getDestination(PREVIEW, infoLetterSC, request);
        } else {
          destination = setMainContext(infoLetterSC, request);
        }
      } else {
        destination = function;
      }
      if (!function.startsWith("Suscribers")) {
        destination = "/infoLetter/jsp/" + destination;
      }
    } catch (Exception e) {
      request.setAttribute("jakarta.servlet.jsp.jspException", e);
      destination = "/admin/jsp/errorpageMain.jsp";
    }

    return destination;
  }

  /**
   * Checks the user is entitled to the operation, whose functional label is given, otherwise a
   * forbidden error is thrown.
   */
  private void checkIsPublisherOrManager(final String flag, final String operation) {
    if (!PUBLISHER.equals(flag) && !ADMIN.equals(flag)) {
      throwHttpForbiddenError("Only publishers and managers can " + operation);
    }
  }

  /**
   * Checks the user manages the newsletter, otherwise a forbidden error is thrown. This is meant
   * for the operations the publishers aren't entitled to, such as the ones on the template or the
   * ones applying to several issues at once.
   */
  private void checkIsManager(final String flag, final String operation) {
    if (!ADMIN.equals(flag)) {
      throwHttpForbiddenError("Only managers can " + operation);
    }
  }

  /**
   * Checks the operation, whose functional label is given, is requested by POST, otherwise a
   * forbidden error is thrown. The synchronizer token is required on a POST whatever its URL,
   * whereas it is required on a GET only when its URL holds one of a few keywords. An operation
   * writing something has therefore to refuse the GET, otherwise it could be requested from
   * another site on behalf of the user being lured.
   */
  private void checkIsPostRequest(final HttpRequest request, final String operation) {
    if (!POST_METHOD.equals(request.getMethod())) {
      throwHttpForbiddenError(operation + " has to be requested by POST");
    }
  }

  private String getFlag(String[] profiles) {
    String flag = "user";
    for (final String profile : profiles) {
      // if publisher, return it, we won't find a better profile
      if (profile.equals(ADMIN)) {
        return profile;
      } else if (!profile.equals("user")) {
        flag = profile;
      }
    }
    return flag;
  }

  private String param(HttpServletRequest request, String name) {
    return defaultStringIfNotDefined(request.getParameter(name));
  }
}