PublicationsRenderer.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.kmelia.servlets.renderers;
import org.apache.commons.io.FilenameUtils;
import org.apache.ecs.html.A;
import org.apache.ecs.html.Option;
import org.apache.ecs.wml.Alignment;
import org.apache.ecs.wml.Img;
import org.owasp.encoder.Encode;
import org.silverpeas.components.kmelia.KmeliaPublicationHelper;
import org.silverpeas.components.kmelia.control.KmeliaSessionController;
import org.silverpeas.components.kmelia.model.KmeliaPublication;
import org.silverpeas.components.kmelia.model.ValidatorsList;
import org.silverpeas.components.kmelia.service.KmeliaHelper;
import org.silverpeas.core.ResourceReference;
import org.silverpeas.core.admin.component.model.ComponentInstLight;
import org.silverpeas.core.admin.service.OrganizationController;
import org.silverpeas.core.admin.user.model.SilverpeasRole;
import org.silverpeas.core.admin.user.model.User;
import org.silverpeas.core.contribution.attachment.AttachmentServiceProvider;
import org.silverpeas.core.contribution.attachment.model.SimpleDocument;
import org.silverpeas.core.contribution.model.Thumbnail;
import org.silverpeas.core.contribution.publication.model.PublicationDetail;
import org.silverpeas.core.contribution.publication.service.PublicationService;
import org.silverpeas.core.io.media.image.thumbnail.ThumbnailSettings;
import org.silverpeas.core.template.SilverpeasTemplate;
import org.silverpeas.core.template.SilverpeasTemplateFactory;
import org.silverpeas.core.util.MultiSilverpeasBundle;
import org.silverpeas.core.util.URLUtil;
import org.silverpeas.core.util.WebEncodeHelper;
import org.silverpeas.core.util.file.FileRepositoryManager;
import org.silverpeas.core.util.file.FileServerUtils;
import org.silverpeas.core.viewer.service.ViewerProvider;
import org.silverpeas.core.web.util.viewgenerator.html.ImageTag;
import org.silverpeas.core.web.util.viewgenerator.html.UserNameGenerator;
import org.silverpeas.core.web.util.viewgenerator.html.board.Board;
import org.silverpeas.core.web.util.viewgenerator.html.pagination.Pagination;
import org.silverpeas.core.webapi.rating.RaterRatingEntity;
import org.silverpeas.kernel.bundle.ResourceLocator;
import org.silverpeas.kernel.bundle.SettingBundle;
import org.silverpeas.kernel.util.Pair;
import org.silverpeas.kernel.util.StringUtil;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import static org.silverpeas.components.kmelia.model.KmeliaPublicationSort.*;
import static org.silverpeas.core.admin.user.model.SilverpeasRole.*;
import static org.silverpeas.core.contribution.publication.model.PublicationDetail.*;
import static org.silverpeas.core.web.selection.BasketSelectionUI.getPutIntoBasketSelectionHtmlSnippet;
import static org.silverpeas.core.web.util.viewgenerator.html.JavascriptPluginInclusion.scriptContent;
import static org.silverpeas.kernel.util.StringUtil.defaultStringIfNotDefined;
/**
* A renderer of a paginated list of publications.
*
* @author mmoquillon
*/
public class PublicationsRenderer implements Renderer {
private static final String END_DIV = "</div>";
private static final String END_SPAN = "</span>";
private static final String NEW_LINE = "<br/>";
private static final String SCRIPT_END = "</script>";
private static final String SCRIPT_START = "<script>";
private static final String KMELIA_PUBLICATION_ICON = "kmelia.publication";
private static final String ALT = "\" alt=\"";
private static final String TITLE_ATTR = "\" title=\"";
private static final String ANCHOR_END = "\"/></a>";
private static final String KMELIA_LINK_ICON = "kmelia.link";
private final SettingBundle settings =
ResourceLocator.getSettingBundle("org.silverpeas.publication.publicationSettings");
@Override
public void render(Writer writer, RenderingContext ctx) throws IOException {
var resources = ctx.getResources();
var kmeliaScc = ctx.getSessionController();
var gef = ctx.getGraphicElementFactory();
String publicationSrc = resources.getIcon(KMELIA_PUBLICATION_ICON);
boolean showNoPublisMessage = resources.getSetting("showNoPublisMessage", true);
PublicationFragmentSettings fragmentSettings = initFragmentSettings(ctx);
int nbPubsPerPage = kmeliaScc.getNbPublicationsPerPage();
int firstDisplayedItemIndex = kmeliaScc.getIndexOfFirstPubToDisplay();
var allPubs = ctx.getPublications();
int nbPubs = allPubs.size();
Pagination pagination = gef.getPagination(nbPubs, nbPubsPerPage, firstDisplayedItemIndex);
List<KmeliaPublication> pubsToRender = allPubs.subList(pagination.getFirstItemIndex(),
pagination.getLastItemIndex());
writer.write("<form name=\"publicationsForm\" onsubmit=\"return false;\">");
if (!pubsToRender.isEmpty()) {
renderPublicationsPaginatedList(pubsToRender, writer, ctx, pagination, fragmentSettings);
} else if (showNoPublisMessage) {
String noPublications = kmeliaScc.getString("PubAucune");
if (ctx.isSearchInProgress()) {
noPublications = kmeliaScc.getString("NoPubFound");
}
writer.write("<div class=\"tableBoard\" id=\"noPublicationMessage\">");
writer.write("<div id=\"pubsHeader\"><img src=\"" + publicationSrc + "\" border=\"0\" /> ");
writer.write(
"<span>" + resources.getString("GML.publications") + "</span></div>");
writer.write("<p>" + noPublications + "</p>");
writer.write(END_DIV);
}
writer.write("</form>");
writer.write(scriptContent("sp.selection.newCheckboxMonitor('form[name=publicationsForm] " +
"input[name=C1]')" +
".addEventListener('change', function(){" +
"if(typeof showPublicationCheckedBoxes === 'function') {showPublicationCheckedBoxes();}" +
"}, 'displayPublication');").toString());
}
private void renderPublicationsPaginatedList(List<KmeliaPublication> pubs, Writer writer,
RenderingContext ctx, Pagination pagination, PublicationFragmentSettings fragmentSettings) throws IOException {
Board board = ctx.getGraphicElementFactory().getBoard();
writer.write(board.printBefore());
renderPublicationsListHeader(writer, ctx, pagination);
writer.write("<ul>");
for (KmeliaPublication aPub : pubs) {
renderPublication(aPub, writer, ctx, fragmentSettings);
}
writer.write("</ul>");
writer.write("<div id=\"pagination\">");
writer.write(pagination.printIndex("doPagination", true));
writer.write(END_DIV);
renderFilePreviewJavascript(writer, ctx);
renderFileViewJavascript(writer, ctx);
writer.write(board.printAfter());
}
private void renderPublication(KmeliaPublication aPub, Writer writer, RenderingContext ctx,
PublicationFragmentSettings fragmentSettings) throws IOException {
PublicationDetail pub = aPub.getDetail();
String highlightClass = getHighlightClass(ctx, pub);
var resources = ctx.getResources();
var kmeliaScc = ctx.getSessionController();
Pair<String, String> state = getPublicationState(aPub, ctx);
String cssClasses = "important" + pub.getImportance();
if (pub.isNew()) {
cssClasses += " new-contribution";
}
if (ctx.isSearchInProgress()) {
if (aPub.isRead()) {
cssClasses += " read";
} else {
cssClasses += " unread";
}
}
writer.write("<li class=\"");
writer.write(cssClasses);
writer.write("\" onmouseover=\"showPublicationOperations(this);\"");
writer.write(" onmouseout=\"hidePublicationOperations(this);\">");
writer.write("<div class=\"firstColumn\">");
if (!kmeliaScc.getUserDetail().isAnonymous() && !kmeliaScc.isKmaxMode()) {
String checked = "";
if (ctx.getSelectedPublications() != null &&
ctx.getSelectedPublications().contains(pub.getPK())) {
checked = "checked=\"checked\"";
}
writer.write("<span class=\"selection\">");
writer.write("<input type=\"checkbox\" name=\"C1\" value=\"" + pub.getPK().getId() + "-" +
pub.getPK().getInstanceId() + "\" " + checked +
" onclick=\"sendPubId(this.value, this.checked);\"/>");
writer.write(END_SPAN);
}
if (!ctx.isSeeAlso()) {
Thumbnail thumbnail = pub.getThumbnail();
if (thumbnail != null && Boolean.parseBoolean(resources.getSetting("isVignetteVisible"))) {
writer.write("<span class=\"thumbnail\">");
renderPublicationThumbnail(pub, writer, ctx);
writer.write(END_SPAN);
}
}
writer.write(END_DIV);
fragmentSettings.setPubColor(state.getSecond());
fragmentSettings.setHighlightClass(highlightClass);
fragmentSettings.setPubState(state.getFirst());
fragmentSettings.setLinksAllowed(ctx.isLinksAllowed());
fragmentSettings.setSeeAlso(ctx.isSeeAlso());
fragmentSettings.setLinkAttachment(ctx.isAttachmentToLink());
writer.write("<div class=\"publication\"><a name=\"" + pub.getPK().getId() + "\"></a>");
renderPublicationFragment(aPub, writer, ctx, fragmentSettings);
writer.write(END_DIV);
writer.write(
getPutIntoBasketSelectionHtmlSnippet(String.format("putPublicationInBasket('%s')",
pub.getIdentifier().asString()), kmeliaScc.getCurrentLanguage()));
writer.write("</li>");
}
private Pair<String, String> getPublicationState(KmeliaPublication aPub,
RenderingContext ctx) {
PublicationDetail pub = aPub.getDetail();
var resources = ctx.getResources();
Pair<String, String> state;
if (pub.getStatus() != null && pub.isValid()) {
state = computeValidPublicationState(aPub, ctx);
} else {
state = computePublicationState(aPub, ctx);
}
String pubState = state.getFirst();
String pubColor = state.getSecond();
if (pub.isAlias()) {
pubState = resources.getString("kmelia.Shortcut");
}
return Pair.of(pubState, pubColor);
}
private Pair<String, String> computeValidPublicationState(KmeliaPublication aPub,
RenderingContext ctx) {
PublicationDetail pub = aPub.getDetail();
String profile = ctx.getRole();
var resources = ctx.getResources();
var kmeliaScc = ctx.getSessionController();
boolean targetValidationEnabled =
kmeliaScc.isTargetValidationEnable() || kmeliaScc.isTargetMultiValidationEnable();
User creator = aPub.getCreator();
String pubColor = "";
String pubState = null;
var currentUserId = kmeliaScc.getUserDetail().getId();
if (pub.haveGotClone() && CLONE_STATUS.equals(pub.getCloneStatus()) &&
!USER.isInRole(profile)) {
pubColor = "blue";
pubState = resources.getString("kmelia.UpdateInProgress");
} else if (DRAFT_STATUS.equals(pub.getCloneStatus())) {
if (currentUserId.equals(creator.getId())) {
pubColor = "gray";
pubState = resources.getString("PubStateDraft");
}
} else if (TO_VALIDATE_STATUS.equals(pub.getCloneStatus())) {
if (ADMIN.isInRole(profile) || PUBLISHER.isInRole(profile) ||
currentUserId.equals(creator.getId())) {
pubColor = "red";
pubState = getPublicationStateByValidationStatus(aPub, resources, targetValidationEnabled);
}
} else {
pubState = getPublicationStateByVisibility(pub, resources);
if (!pub.isVisible()) {
pubColor = "gray";
}
}
return Pair.of(pubState, pubColor);
}
private String getPublicationStateByValidationStatus(KmeliaPublication aPub,
MultiSilverpeasBundle resources, boolean targetValidationEnabled) {
String pubState;
pubState = resources.getString("kmelia.PubStateToValidate");
if (targetValidationEnabled) {
ValidatorsList validatorsList = aPub.getValidators();
pubState = getTargetedValidationInfo(validatorsList, resources);
}
return pubState;
}
private static String getPublicationStateByVisibility(PublicationDetail pub,
MultiSilverpeasBundle resources) {
String pubState;
if (pub.isNotYetVisible()) {
pubState = resources.getString("kmelia.VisibleFrom") + " " +
resources.getOutputDateAndHour(pub.getBeginDateAndHour());
} else if (pub.isNoMoreVisible()) {
pubState = resources.getString("kmelia.VisibleTo") + " " +
resources.getOutputDateAndHour(pub.getEndDateAndHour());
} else {
pubState = null;
}
return pubState;
}
private Pair<String, String> computePublicationState(KmeliaPublication aPub,
RenderingContext ctx) {
PublicationDetail pub = aPub.getDetail();
String profile = ctx.getRole();
var resources = ctx.getResources();
var kmeliaScc = ctx.getSessionController();
boolean targetValidationEnabled =
kmeliaScc.isTargetValidationEnable() || kmeliaScc.isTargetMultiValidationEnable();
String currentUserId = kmeliaScc.getUserDetail().getId();
boolean hasModificationAccess = ADMIN.isInRole(profile) || PUBLISHER.isInRole(profile) ||
pub.isPublicationEditor(currentUserId) ||
(!USER.isInRole(profile) && kmeliaScc.isCoWritingEnable());
Pair<String, String> state = null;
if (pub.getStatus() != null && pub.isDraft()) {
state = getDraftPublicationState(aPub, ctx);
} else if (pub.getStatus() != null && pub.isRefused()) {
if (ADMIN.isInRole(profile) || PUBLISHER.isInRole(profile) ||
(WRITER.isInRole(profile) &&
(pub.isPublicationEditor(currentUserId) || kmeliaScc.isCoWritingEnable()))) {
state = Pair.of(resources.getString("PublicationRefused"), "red");
}
} else if (hasModificationAccess) {
// in co-redaction, all the publications to be validated are rendered (except for readers)
String pubColor = "red";
String pubState = getDefaultRWPublicationState(aPub, ctx, targetValidationEnabled);
state = Pair.of(pubState, pubColor);
}
return state == null ? Pair.of(null, "") : state;
}
private String getDefaultRWPublicationState(KmeliaPublication aPub,
RenderingContext ctx, boolean targetValidationEnabled) {
PublicationDetail pub = aPub.getDetail();
String pubState;
var resources = ctx.getResources();
if (pub.isRefused()) {
pubState = resources.getString("kmelia.PubStateUnvalidate");
} else {
pubState = resources.getString("kmelia.PubStateToValidate");
if (targetValidationEnabled) {
ValidatorsList validatorsList = aPub.getValidators();
pubState = getTargetedValidationInfo(validatorsList, resources);
}
}
return pubState;
}
private Pair<String, String> getDraftPublicationState(KmeliaPublication aPub,
RenderingContext ctx) {
// in draft mode, in the case of a co-redaction or a draft mode visible to everyone, the
// draft publications are visible for everyone except the readers. Otherwise, the
// draft publications are only visible to their owner (creator)
PublicationDetail pub = aPub.getDetail();
String profile = ctx.getRole();
var kmeliaScc = ctx.getSessionController();
String currentUserId = kmeliaScc.getUserDetail().getId();
if (pub.isPublicationEditor(currentUserId) ||
((kmeliaScc.isCoWritingEnable() && kmeliaScc.isDraftVisibleWithCoWriting()) &&
!USER.isInRole(profile))) {
return Pair.of(ctx.getResources().getString("PubStateDraft"), "gray");
}
return Pair.of(null, "");
}
private static String getHighlightClass(RenderingContext ctx, PublicationDetail pub) {
String pubIdToHighlight = ctx.getPublicationToHighlight();
String highlightClass = "";
if (StringUtil.isDefined(pubIdToHighlight) &&
pubIdToHighlight.equals(pub.getPK().getId())) {
highlightClass = "highlight";
}
return highlightClass;
}
private static PublicationFragmentSettings initFragmentSettings(RenderingContext ctx) {
var kmeliaScc = ctx.getSessionController();
var resources = ctx.getResources();
PublicationFragmentSettings fragmentSettings = new PublicationFragmentSettings();
fragmentSettings.setDisplayLinks(URLUtil.displayUniversalLinks());
fragmentSettings.setShowImportance(kmeliaScc.isFieldImportanceVisible());
fragmentSettings.setFileStorageShowExtraInfoPub(resources.getSetting(
"fileStorageShowExtraInfoPub", false));
fragmentSettings.setShowTopicPathNameinSearchResult(resources.getSetting(
"showTopicPathNameinSearchResult", true));
fragmentSettings.setToSearch(ctx.isSearchInProgress());
fragmentSettings.setRateable(kmeliaScc.isPublicationRatingAllowed());
return fragmentSettings;
}
void renderFilePreviewJavascript(Writer out, RenderingContext ctx)
throws IOException {
String sb = SCRIPT_START +
"function previewFile(target, attachmentId) {" +
" $(target).preview(\"document\", {" +
" documentType: 'attachment'," +
" documentId: attachmentId," +
" lang: '" + ctx.getSessionController().getCurrentLanguage() + "'" +
" });" +
" return false;" +
"}" +
SCRIPT_END;
out.write(sb);
}
void renderFileViewJavascript(Writer out, RenderingContext ctx)
throws IOException {
String sb = SCRIPT_START +
"function viewFile(target, attachmentId) {" +
" $(target).view(\"document\", {" +
" documentType: 'attachment'," +
" documentId: attachmentId," +
" lang: '" + ctx.getSessionController().getCurrentLanguage() + "'" +
" });" +
" return false;" +
"}" +
SCRIPT_END;
out.write(sb);
}
void renderPublicationFragment(KmeliaPublication aPub,
Writer writer, RenderingContext ctx, PublicationFragmentSettings fragmentSettings)
throws IOException {
var kmeliaScc = ctx.getSessionController();
var userId = kmeliaScc.getUserDetail().getId();
String topicId = kmeliaScc.getCurrentFolderId();
// check if this instance use a custom template
boolean specificTemplateUsed = kmeliaScc.isCustomPublicationTemplateUsed();
// check if publication is draggable
boolean canBeCut = KmeliaPublicationHelper
.isCanBeCut(kmeliaScc.getComponentId(), userId, kmeliaScc.getUserTopicProfile(),
aPub.getCreator());
boolean alias = aPub.isAlias();
fragmentSettings.setDraggable(canBeCut && !alias && !KmeliaHelper.isToValidateFolder(topicId));
if (specificTemplateUsed) {
renderPublicationTemplatedFragment(aPub, writer, ctx, fragmentSettings);
} else {
renderPublicationDefaultFragment(aPub, writer, ctx, fragmentSettings);
}
}
void renderPublicationTemplatedFragment(KmeliaPublication aPub,
Writer out, RenderingContext ctx, PublicationFragmentSettings fragmentSettings)
throws IOException {
var kmeliaScc = ctx.getSessionController();
var resources = ctx.getResources();
String language = kmeliaScc.getCurrentLanguage();
SilverpeasTemplate template = SilverpeasTemplateFactory.createSilverpeasTemplateOnComponents();
PublicationDetail pub = aPub.getDetail();
String name = pub.getName(language);
String description = pub.getDescription(language);
template.setAttribute("publication", pub);
template.setAttribute("link", "javascript:onClick=publicationGoTo('" + pub.getId() + "')");
template.setAttribute("name", Encode.forHtml(name));
template.setAttribute("description",
WebEncodeHelper.convertBlanksForHtml(Encode.forHtml(description)));
template.setAttribute("showDescription",
StringUtil.isDefined(description) && !description.equals(name));
template.setAttribute("importance", pub.getImportance());
template.setAttribute("showImportance",
fragmentSettings.isShowImportance() && !fragmentSettings.isLinkAttachment());
template.setAttribute("date", displayDate(pub, kmeliaScc, resources));
template.setAttribute("creationDate", resources.getOutputDate(pub.getCreationDate()));
template.setAttribute("updateDate", resources.getOutputDate(pub.getLastUpdateDate()));
// check if the pub date must be display
boolean showPubDate = true;
if (fragmentSettings.isLinkAttachment()) {
showPubDate = fragmentSettings.isFileStorageShowExtraInfoPub();
}
template.setAttribute("showDate", showPubDate);
// check if user name in the list must be display
boolean showUserName = kmeliaScc.showUserNameInList();
if (fragmentSettings.isLinkAttachment()) {
showUserName = showUserName && fragmentSettings.isFileStorageShowExtraInfoPub();
}
template.setAttribute("username", getUserName(aPub, kmeliaScc));
template.setAttribute("showUsername", showUserName);
template.setAttribute("permalink", getHTMLPublicationPermalink(pub, ctx));
template.setAttribute("showPermalink",
fragmentSettings.isDisplayLinks() && !fragmentSettings.isSeeAlso() &&
!fragmentSettings.isLinkAttachment());
template.setAttribute("status", fragmentSettings.getPubState());
template.setAttribute("statusColor", fragmentSettings.getPubColor());
template.setAttribute("highlightClass", fragmentSettings.getHighlightClass());
template.setAttribute("showRef",
fragmentSettings.isSeeAlso() && resources.getSetting("linkManagerShowPubId", false));
// Show topic name only in search in topic case
if (fragmentSettings.isToSearch() && fragmentSettings.isShowTopicPathNameinSearchResult()) {
template.setAttribute("path", getHTMLPublicationFullPath(pub, ctx));
}
boolean showAuthor = kmeliaScc.isAuthorUsed() && StringUtil.isDefined(pub.getAuthor());
if (fragmentSettings.isLinkAttachment()) {
showAuthor = showAuthor && fragmentSettings.isFileStorageShowExtraInfoPub();
}
template.setAttribute("showAuthor", showAuthor);
template.setAttribute("author", pub.getAuthor());
template.setAttribute("files",
getHTMLBlockOfFiles(pub, ctx, fragmentSettings));
if (!"0".equals(pub.getInfoId())) {
template.setAttribute("formName", pub.getInfoId());
template.setAttribute("form", pub.getFormValues(language));
}
String fragment = template
.applyFileTemplateOnComponent("kmelia", kmeliaScc.getCustomPublicationTemplateName());
out.write(fragment);
}
void renderPublicationDefaultFragment(KmeliaPublication aPub,
Writer writer, RenderingContext ctx, PublicationFragmentSettings fragmentSettings)
throws IOException {
var kmeliaScc = ctx.getSessionController();
var resources = ctx.getResources();
PublicationDetail pub = aPub.getDetail();
String language = kmeliaScc.getCurrentLanguage();
String name = Encode.forHtml(pub.getName(language));
writer.write("<div class=\"publication-name line1\">");
if (fragmentSettings.isLinksAllowed()) {
renderPublicationLink(pub, name, writer, fragmentSettings);
} else {
renderPublicationRef(pub, name, writer, fragmentSettings, resources);
}
writer.write(" ");
if (StringUtil.isDefined(fragmentSettings.getPubState())) {
renderPublicationState(writer, fragmentSettings);
}
if (fragmentSettings.isRateable()) {
renderPublicationRating(writer, pub);
}
writer.write(END_DIV);
renderPublicationAdditionalInfo(aPub, writer, ctx, fragmentSettings);
String description = pub.getDescription(language);
// displays publication description
if (StringUtil.isDefined(description) && !description.equals(name)) {
writer.write("<p class=\"description line3\">");
writer.write(WebEncodeHelper.convertBlanksForHtml(Encode.forHtml(description)));
writer.write("</p>");
}
writer.write(
getHTMLBlockOfFiles(pub, ctx, fragmentSettings));
writer.write(SCRIPT_START);
writer.write("whenSilverpeasReady(function() {" +
" document.querySelectorAll('.publication-name a').forEach(function(a) {\n" +
" a.addEventListener('click', function(e) {\n" +
" e.preventDefault();\n" +
" e.stopPropagation();\n" +
" const pubId = this.querySelector('span.pub-link').getAttribute('id').trim().slice" +
"(4);\n" +
" publicationGoTo(pubId);\n" +
" });" +
" });" +
"});");
writer.write(SCRIPT_END);
}
private void renderPublicationAdditionalInfo(KmeliaPublication aPub, Writer writer,
RenderingContext ctx, PublicationFragmentSettings fragmentSettings) throws IOException {
var kmeliaScc = ctx.getSessionController();
var resources = ctx.getResources();
PublicationDetail pub = aPub.getDetail();
writer.write("<div class=\"line2 ");
writer.write(fragmentSettings.getPubColor());
writer.write("\">");
// Show topic name only in search in topic case
if (fragmentSettings.isToSearch() && fragmentSettings.isShowTopicPathNameinSearchResult()) {
writer.write(getHTMLPublicationFullPath(pub, ctx));
}
// check if user name in the list must be display
boolean showUserNameInList = kmeliaScc.showUserNameInList();
if (fragmentSettings.isLinkAttachment()) {
showUserNameInList = showUserNameInList && fragmentSettings.isFileStorageShowExtraInfoPub();
}
if (showUserNameInList) {
writer.write("<span class=\"user\">");
writer.write(getUserName(aPub, kmeliaScc));
writer.write(" - </span>");
}
// check if the pub date must be display
boolean showPubDate;
if (fragmentSettings.isLinkAttachment()) {
showPubDate = fragmentSettings.isFileStorageShowExtraInfoPub();
} else {
showPubDate = true;
}
if (showPubDate) {
writer.write("<span class=\"date\">");
writer.write(displayDate(pub, kmeliaScc, resources));
writer.write(END_SPAN);
}
// check if he author name must be display
boolean showAuthor = kmeliaScc.isAuthorUsed() && StringUtil.isDefined(pub.getAuthor());
if (fragmentSettings.isLinkAttachment()) {
showAuthor = showAuthor && fragmentSettings.isFileStorageShowExtraInfoPub();
}
if (showAuthor) {
writer.write("<span class=\"author\">");
writer.write(" - (");
writer.write(resources.getString("GML.author"));
writer.write(": ");
writer.write(pub.getAuthor());
writer.write(")</span>");
}
// displays permalink
if (fragmentSettings.isDisplayLinks() && !fragmentSettings.isSeeAlso() &&
!fragmentSettings.isLinkAttachment()) {
writer.write("<span class=\"permalink\">");
writer.write(getHTMLPublicationPermalink(pub, ctx));
writer.write(END_SPAN);
}
writer.write(END_DIV);
}
private static void renderPublicationRating(Writer writer, PublicationDetail pub) throws IOException {
RaterRatingEntity raterRatingEntity = RaterRatingEntity.fromRateable(pub);
writer.write(raterRatingEntity
.toJSonScript("raterRatingEntity_" + raterRatingEntity.getContributionId()));
writer.write(
"<div silverpeas-rating readonly=\"true\" shownbraterratings=\"false\" " +
"starsize=\"small\" " +
"raterrating=\"raterRatingEntity_" + raterRatingEntity.getContributionId() +
"\"></div>");
}
private static void renderPublicationState(Writer writer,
PublicationFragmentSettings fragmentSettings) throws IOException {
writer.write("<span class=\"state ");
writer.write(fragmentSettings.getPubColor());
writer.write("\">(");
writer.write(fragmentSettings.getPubState());
writer.write(")</span>");
}
private static void renderPublicationRef(PublicationDetail pub, String name, Writer writer,
PublicationFragmentSettings fragmentSettings, MultiSilverpeasBundle resources) throws IOException {
String ref = "";
if (fragmentSettings.isSeeAlso() && resources.getSetting("linkManagerShowPubId", false)) {
ref = " [ " + pub.getPK().getId() + " ] ";
}
writer.write("<div class=\"");
writer.write(fragmentSettings.getPubColor());
writer.write("\"><span class=\"" + fragmentSettings.getHighlightClass() + "\">");
writer.write(ref);
writer.write(name);
writer.write("</span></div>");
}
private static void renderPublicationLink(PublicationDetail pub, String name, Writer writer,
PublicationFragmentSettings fragmentSettings) throws IOException {
if (StringUtil.isDefined(fragmentSettings.getPubColor())) {
writer.write("<div class=\"");
writer.write(fragmentSettings.getPubColor());
writer.write("\"><a href=\"#\">");
} else {
writer.write("<div><a href=\"#\">");
}
writer.write("<span id=\"pub-" + pub.getPK().getId() + "\" class=\"pub-link");
if (fragmentSettings.isDraggable()) {
writer.write(" jstree-draggable\">");
writer.write(name);
} else {
writer.write("\">");
writer.write(name);
}
writer.write("</span></a></div>");
}
String displayDate(PublicationDetail pub, KmeliaSessionController kmeliaScc,
MultiSilverpeasBundle resources) {
if (5 == kmeliaScc.getSortValue() || 6 == kmeliaScc.getSortValue()) {
return resources.getOutputDate(pub.getCreationDate());
} else {
return resources.getOutputDate(pub.getLastUpdateDate());
}
}
String getHTMLBlockOfFiles(PublicationDetail pub, RenderingContext ctx,
PublicationFragmentSettings fragmentSettings) {
var kmeliaScc = ctx.getSessionController();
StringBuilder sb = new StringBuilder(1024);
boolean displayFiles =
(KmeliaHelper.isToolbox(kmeliaScc.getComponentId()) || kmeliaScc.attachmentsInPubList()) &&
!fragmentSettings.isSeeAlso() || fragmentSettings.isLinkAttachment();
if (displayFiles) {
sb.append("<span class=\"files\">");
sb.append(getHTMLBlockOfAttachments(pub, ctx));
sb.append(END_SPAN);
}
return sb.toString();
}
private void renderPublicationThumbnail(PublicationDetail pub, Writer writer,
RenderingContext ctx) throws IOException {
ThumbnailSettings thumbnailSettings = ctx.getSessionController().getThumbnailSettings();
String vignetteUrl;
String width = String.valueOf(thumbnailSettings.getWidth());
String height = String.valueOf(thumbnailSettings.getHeight());
if (pub.getImage().startsWith("/")) {
vignetteUrl = pub.getImage();
} else {
vignetteUrl = FileServerUtils.getUrl(pub.getPK().
getComponentName(), "vignette", pub.getImage(), pub.getImageMimeType(),
settings.getString("imagesSubDirectory"));
if (pub.getThumbnail().isCropped()) {
// thumbnail is cropped, no resize
width = null;
height = null;
}
}
ImageTag imageTag = new ImageTag();
imageTag.setSrc(vignetteUrl);
imageTag.setType("vignette");
String size = defaultStringIfNotDefined(width) + "x" + defaultStringIfNotDefined(height);
if (!"x".equals(size)) {
imageTag.setSize(size);
}
writer.write(imageTag.toString());
}
String getHTMLPublicationPermalink(PublicationDetail pub, RenderingContext ctx) {
String link;
var kmeliaScc = ctx.getSessionController();
var resources = ctx.getResources();
if (!pub.getPK().getInstanceId().equals(kmeliaScc.getComponentId())) {
link = URLUtil
.getSimpleURL(URLUtil.URL_PUBLI, pub.getPK().getId(), kmeliaScc.getComponentId());
} else {
link = URLUtil.getSimpleURL(URLUtil.URL_PUBLI, pub.getPK().getId());
}
return " - <a class=\"sp-permalink\" href=\"" + link + "\"><img src=\"" +
resources.getIcon(KMELIA_LINK_ICON) +
"\" border=\"0\" align=\"absmiddle\" alt=\"" +
resources.getString("kmelia.CopyPublicationLink") + TITLE_ATTR +
resources.getString("kmelia.CopyPublicationLink") + ANCHOR_END;
}
void renderSortingListBox(Writer writer, RenderingContext ctx)
throws IOException {
var ksc = ctx.getSessionController();
var resources = ctx.getResources();
boolean manualSort = ksc.getSort().getCurrentSort() == SORT_MANUAL;
writer.write(
"<select name=\"sortBy\" id=\"sortingList\" onChange=\"javascript:sortGoTo(this" +
".selectedIndex);\">");
writer.write("<option>" + resources.getString("SortBy") + "</option>");
writer.write("<option>-------------------------------</option>");
writer.write(getSortingListBoxEntry(SORT_UPDATE_ASC, resources.getString("DateAsc"), ksc));
writer.write(getSortingListBoxEntry(SORT_UPDATE_DESC, resources.getString("DateDesc"), ksc));
writer.write(getSortingListBoxEntry(SORT_CREATION_ASC, resources.getString("CreateDateAsc"),
ksc));
writer.write(getSortingListBoxEntry(SORT_CREATION_DESC, resources.getString("CreateDateDesc"),
ksc));
writer.write(getSortingListBoxEntry(SORT_CREATOR_ASC, resources.getString("PubAuteur"), ksc));
if (ksc.isFieldImportanceVisible()) {
writer.write(getSortingListBoxEntry(SORT_IMPORTANCE_ASC, resources.getString("PubImportance"),
ksc));
}
writer.write(getSortingListBoxEntry(SORT_TITLE_ASC, resources.getString("PubTitre"), ksc));
writer.write(getSortingListBoxEntry(SORT_DESCRIPTION_ASC, resources.getString("PubDescription"),
ksc));
if (manualSort) {
writer.write(
getSortingListBoxEntry(SORT_MANUAL, resources.getString("kmelia.sort.manual"), ksc));
}
writer.write("</select>");
if (manualSort && SilverpeasRole.ADMIN == ksc.getHighestSilverpeasUserRole()) {
// Display link to reset manual sort
Img img = new Img();
img.setSrc(resources.getIcon("kmelia.delete"));
A resetSort = new A();
resetSort.setHref("#");
resetSort.setOnClick("resetSort()");
resetSort.setID("resetSort");
resetSort.addElement(img);
resetSort.setTitle(resources.getString("kmelia.sort.manual.reset"));
writer.write(resetSort.toString());
}
}
private String getSortingListBoxEntry(int value, String label, KmeliaSessionController ksc) {
Option option = new Option(label, value);
option.setID("sort" + value);
option.setSelected(isSelectedSort(ksc, value));
return option.toString();
}
private boolean isSelectedSort(KmeliaSessionController ksc, int sort) {
final int currentSort = ksc.getSearchContext() != null ?
ksc.getSearchContext().getSortValue() :
ksc.getSortValue();
return sort == currentSort;
}
private void renderPublicationsListHeader(Writer writer, RenderingContext ctx,
Pagination pagination) throws IOException {
var resources = ctx.getResources();
String publicationSrc = resources.getIcon(KMELIA_PUBLICATION_ICON);
writer.write("<div id=\"pubsHeader\">");
Img img = new Img(publicationSrc).setAlt("");
writer.write(img.toString());
writer.write("<span id=\"pubsCounter\">");
writer.write("<span>" + pagination.printCounter() + "</span> ");
if (ctx.getPublications().size() > 1) {
writer.write(resources.getString("GML.publications"));
} else {
writer.write(resources.getString("GML.publication"));
}
writer.write(END_SPAN);
writer.write("<span id=\"pubsSort\">");
if (ctx.isSortAllowed()) {
renderSortingListBox(writer, ctx);
}
writer.write(END_SPAN);
writer.write(END_DIV);
}
private String getUserName(KmeliaPublication userPub, KmeliaSessionController kmeliaScc) {
User currentUser = userPub.getCreator();
PublicationDetail pub = userPub.getDetail();
String updaterId = pub.getUpdaterId();
User updater = null;
if (updaterId != null && !updaterId.isEmpty()) {
updater = kmeliaScc.getUserDetail(updaterId);
}
if (updater == null) {
updater = currentUser;
}
if (updater != null && (StringUtil.isDefined(updater.getFirstName()) ||
StringUtil.isDefined(updater.getLastName()))) {
return UserNameGenerator.toString(updater, kmeliaScc.getUserId());
}
return kmeliaScc.getString("kmelia.UnknownUser");
}
private String getHTMLBlockOfAttachments(PublicationDetail pubDetail, RenderingContext ctx) {
var kmeliaScc = ctx.getSessionController();
ResourceReference resourceReference = new ResourceReference(pubDetail.getPK());
List<SimpleDocument> documents = AttachmentServiceProvider.getAttachmentService().
listDocumentsByForeignKey(resourceReference, kmeliaScc.getCurrentLanguage());
StringBuilder result = new StringBuilder(documents.size() * 256);
boolean hasDisplayableAttachments = false;
for (SimpleDocument document : documents) {
SimpleDocument attachment = document.getLastPublicVersion();
if (attachment != null) {
if (!hasDisplayableAttachments) {
result.append("<table border=\"0\">");
hasDisplayableAttachments = true;
}
result.append(getHTMLOfFile(document.getId(), attachment, ctx));
}
}
if (hasDisplayableAttachments) {
result.append("</table>");
}
return result.toString();
}
private String getHTMLOfFile(String docId, SimpleDocument attachment, RenderingContext ctx) {
if (!ctx.isAttachmentToLink()) {
return buildNonLinkedAttachment(docId, attachment, ctx);
} else {
return buildLinkedAttachment(docId, attachment, ctx);
}
}
private static String buildLinkedAttachment(String docId, SimpleDocument attachment,
RenderingContext ctx) {
String icon = FileRepositoryManager.getFileIcon(FilenameUtils.getExtension(attachment.
getFilename()));
String size = FileRepositoryManager.formatFileSize(attachment.getSize());
String info = Encode.forHtml(attachment.getDescription());
String downloadTime = FileRepositoryManager.getFileDownloadTime(attachment.getSize());
String permalink = URLUtil.getSimpleURL(URLUtil.URL_FILE, docId);
String url = FileServerUtils.getApplicationContext() + attachment.getAttachmentURL();
// determines the label to display
Pair<String, String> titleAndLogicalName = getTitleAndLogicalName(attachment);
StringBuilder result = new StringBuilder(1024);
var resources = ctx.getResources();
String displayedTitle;
if (StringUtil.isNotDefined(titleAndLogicalName.getFirst())) {
displayedTitle = titleAndLogicalName.getSecond();
} else {
displayedTitle = titleAndLogicalName.getFirst();
}
// create the javascript which allows the attachment link selecting
String javascriptFunction =
"selectAttachment('" + url + "','" + icon + "','" + displayedTitle + "')";
String link = "<a href=\"javascript:" + javascriptFunction + "\" >";
result.append("<tr><td valign=\"top\">");
// Add doc type icon
result.append(link).append("<img src=\"").append(icon)
.append("\" border=\"0\" align=\"absmiddle\"/></a> </td>");
result.append("<td valign=\"top\">").append(link);
// inserts label (attachment title or logical file name)
result.append(displayedTitle);
result.append("</a>");
// inserts permalink
if (StringUtil.isDefined(permalink)) {
result.append(" <a href=\"").append(permalink)
.append("\" target=\"_blank\"><img src=\"").append(resources.getIcon(KMELIA_LINK_ICON))
.append("\" border=\"0\" valign=\"absmiddle\" alt=\"")
.append(resources.getString("kmelia.CopyFileLink")).append(TITLE_ATTR)
.append(resources.getString("kmelia.CopyFileLink")).append(ANCHOR_END);
}
result.append(NEW_LINE);
// displays extra information if parameter is true
if (resources.getSetting("fileStorageShowExtraInfoAttachment", false)) {
result.append("<i>");
if (StringUtil.isDefined(titleAndLogicalName.getFirst())) {
result.append(titleAndLogicalName.getSecond()).append(" / ");
}
// Add file size
result.append(size);
// and download estimation
result.append(" / ").append(downloadTime).append(" / ")
.append(resources.getOutputDate(attachment.getCreationDate()));
result.append("</i>");
// Add info
if (StringUtil.isDefined(info)) {
result.append(NEW_LINE).append(WebEncodeHelper.javaStringToHtmlParagraphe(info));
}
}
return result.toString();
}
private static String buildNonLinkedAttachment(String docId, SimpleDocument attachment,
RenderingContext ctx) {
String id = attachment.getPk().getId();
Pair<String, String> titleAndLogicalName = getTitleAndLogicalName(attachment);
String info = Encode.forHtml(attachment.getDescription());
String size = FileRepositoryManager.formatFileSize(attachment.getSize());
String downloadTime = FileRepositoryManager.getFileDownloadTime(attachment.getSize());
boolean previewable = ViewerProvider.isPreviewable(attachment.getAttachmentPath());
boolean viewable = ViewerProvider.isViewable(attachment.getAttachmentPath());
StringBuilder result = new StringBuilder(1024);
var resources = ctx.getResources();
boolean isUserAllowedToDownloadFile =
attachment.isDownloadAllowedForRolesFrom(ctx.getSessionController().getUserDetail());
String fileTitle = StringUtil.isDefined(titleAndLogicalName.getFirst()) ?
titleAndLogicalName.getFirst() : titleAndLogicalName.getSecond();
buildAttachmentInfo(result, docId, attachment, ctx, fileTitle);
result.append(NEW_LINE);
result.append("<i>");
boolean showTitle = resources.getSetting("showTitle", true);
if (StringUtil.isDefined(titleAndLogicalName.getSecond()) &&
(!titleAndLogicalName.getSecond().equals(fileTitle) || !showTitle)) {
result.append(titleAndLogicalName.getSecond()).append(" / ");
}
// Add file size
if (resources.getSetting("showFileSize", true)) {
result.append(size);
}
// and download estimation
if (resources.getSetting("showDownloadEstimation", false)) {
result.append(" / ").append(downloadTime).append(" / ")
.append(resources.getOutputDate(attachment.getCreationDate()));
}
if (previewable) {
buildPreviewableBlock(result, id, resources);
}
if (viewable) {
buildViewableBlock(result, id, resources);
}
if (!attachment.isDownloadAllowedForReaders()) {
String forbiddenDownloadHelp =
isUserAllowedToDownloadFile ? resources.getString("GML.download.forbidden.readers") :
resources.getString("GML.download.forbidden");
result.append(" <img class=\"forbidden-download-file\" src=\"")
.append(resources.getIcon("kmelia.file.forbidden-download")).append(ALT)
.append(forbiddenDownloadHelp).append(TITLE_ATTR).append(forbiddenDownloadHelp)
.append("\"/>");
}
result.append("</i>");
// Add info
if (StringUtil.isDefined(info) && resources.getSetting("showInfo", true)) {
result.append(NEW_LINE).append(WebEncodeHelper.convertBlanksForHtml(info));
}
result.append("</td></tr>");
return result.toString();
}
private static void buildAttachmentInfo(StringBuilder result, String docId,
SimpleDocument attachment, RenderingContext ctx, String fileTitle) {
String icon = FileRepositoryManager.getFileIcon(FilenameUtils.getExtension(attachment.
getFilename()));
String permalink = URLUtil.getSimpleURL(URLUtil.URL_FILE, docId);
String url = FileServerUtils.getApplicationContext() + attachment.getAttachmentURL();
var resources = ctx.getResources();
boolean isUserAllowedToDownloadFile =
attachment.isDownloadAllowedForRolesFrom(ctx.getSessionController().getUserDetail());
String link = isUserAllowedToDownloadFile ? "<a href=\"" + url + "\" target=\"_blank\">" :
"<span class=\"forbidden-download\">";
result.append("<tr><td valign=\"top\">");
// Add doc type icon
Img iconImg = new Img(icon).setAlignment(Alignment.MIDDLE);
result.append(link).append(iconImg.toString()).append("</a> </td>");
result.append("<td valign=\"top\">").append(link);
boolean showTitle = resources.getSetting("showTitle", true);
if (StringUtil.isDefined(fileTitle) && showTitle) {
result.append(Encode.forHtml(fileTitle));
}
result.append(isUserAllowedToDownloadFile ? "</a>" : END_SPAN);
if (StringUtil.isDefined(permalink) && isUserAllowedToDownloadFile) {
buildPermalinkBlock(result, permalink, resources);
}
}
private static void buildPermalinkBlock(StringBuilder result, String permalink, MultiSilverpeasBundle resources) {
result.append(" <a href=\"").append(permalink)
.append("\" target=\"_blank\"><img src=\"").append(resources.getIcon(KMELIA_LINK_ICON))
.append("\" border=\"0\" valign=\"absmiddle\" alt=\"").append(TITLE_ATTR)
.append(ANCHOR_END);
}
private static void buildViewableBlock(StringBuilder result, String id, MultiSilverpeasBundle resources) {
result.append(" <img onclick=\"javascript:viewFile(this, '").append(id)
.append("');\" class=\"view-file\" src=\"")
.append(resources.getIcon("kmelia.file.view")).append(ALT)
.append(resources.getString("GML.view.file")).append(TITLE_ATTR)
.append(resources.getString("GML.view.file")).append("\"/>");
}
private static void buildPreviewableBlock(StringBuilder result, String id, MultiSilverpeasBundle resources) {
result.append(" <img onclick=\"javascript:previewFile(this, '").append(id)
.append("');\" class=\"preview-file\" src=\"")
.append(resources.getIcon("kmelia.file.preview")).append(ALT)
.append(resources.getString("GML.preview.file")).append(TITLE_ATTR)
.append(resources.getString("GML.preview.file")).append("\"/>");
}
private static Pair<String, String> getTitleAndLogicalName(SimpleDocument attachment) {
String logicalName = Encode.forHtml(attachment.getFilename());
String title = Encode.forHtml(attachment.getTitle());
if (!StringUtil.isDefined(attachment.getTitle())) {
title = logicalName;
// do not display filename twice
logicalName = null;
}
if (attachment.isVersioned()) {
title += " v" + attachment.getMajorVersion();
}
return Pair.of(title, logicalName);
}
private String getHTMLPublicationFullPath(PublicationDetail pub, RenderingContext ctx) {
// Get space and componentLabel of the publication (can be different from context)
var kmelia = ctx.getSessionController();
OrganizationController controller = kmelia.getOrganisationController();
ComponentInstLight compoInstLight = controller.getComponentInstLight(pub.getInstanceId());
String componentLabel = compoInstLight.getLabel(kmelia.getCurrentLanguage());
String spaceLabel = Encode.forHtml(
controller.getSpaceInstLightById(compoInstLight.getDomainFatherId())
.getName(kmelia.getCurrentLanguage()));
return PublicationService.get().getMainLocation(pub.getPK())
.map(m -> {
String topicPathName = spaceLabel + " > " + componentLabel + " > " + kmelia.displayPath(
kmelia.getKmeliaService().getPath(m.getId(), m.getInstanceId()), false, 3);
return "<div class=\"publiPath\">" + topicPathName + END_DIV;
})
.orElse(StringUtil.EMPTY);
}
private String getTargetedValidationInfo(ValidatorsList validatorsList,
MultiSilverpeasBundle resources) {
if (validatorsList.isAtLeastOnceValidatorActive()) {
return resources.getStringWithParams("kmelia.PubStateToValidateBy",
validatorsList.getValidatorNames());
} else {
return resources.getString("kmelia.publication.validators.nomore");
}
}
}