KmeliaPublication.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.org/docs/core/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.model;
import org.silverpeas.components.kmelia.service.KmeliaHelper;
import org.silverpeas.components.kmelia.service.KmeliaService;
import org.silverpeas.core.ResourceReference;
import org.silverpeas.core.SilverpeasExceptionMessages;
import org.silverpeas.core.admin.service.OrganizationController;
import org.silverpeas.core.admin.service.OrganizationControllerProvider;
import org.silverpeas.core.admin.user.model.User;
import org.silverpeas.core.comment.model.Comment;
import org.silverpeas.core.comment.service.CommentService;
import org.silverpeas.core.comment.service.CommentServiceProvider;
import org.silverpeas.core.contribution.model.ContributionIdentifier;
import org.silverpeas.core.contribution.model.ContributionModel;
import org.silverpeas.core.contribution.model.I18nContribution;
import org.silverpeas.core.contribution.model.SilverpeasContent;
import org.silverpeas.core.contribution.model.Thumbnail;
import org.silverpeas.core.contribution.model.WithPermanentLink;
import org.silverpeas.core.contribution.model.WithThumbnail;
import org.silverpeas.core.contribution.publication.model.CompletePublication;
import org.silverpeas.core.contribution.publication.model.Location;
import org.silverpeas.core.contribution.publication.model.PublicationDetail;
import org.silverpeas.core.contribution.publication.model.PublicationPK;
import org.silverpeas.core.contribution.publication.model.PublicationPath;
import org.silverpeas.core.contribution.publication.service.PublicationService;
import org.silverpeas.core.i18n.ResourceTranslation;
import org.silverpeas.core.node.model.NodePK;
import org.silverpeas.core.pdc.pdc.model.ClassifyPosition;
import org.silverpeas.core.pdc.pdc.model.PdcException;
import org.silverpeas.core.pdc.pdc.service.PdcManager;
import org.silverpeas.core.security.authorization.NodeAccessControl;
import org.silverpeas.core.silverstatistics.access.service.StatisticService;
import org.silverpeas.kernel.bundle.ResourceLocator;
import org.silverpeas.core.util.URLUtil;
import org.silverpeas.kernel.logging.SilverLogger;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
/**
* A publication as defined in a Kmelia component. A publication in Kmelia is a publication that is
* always in a given topic (the father of the publication), and that can be positioned in the PDC,
* can have attachments, and can be commented.
* <p>
* In Kmelia, a publication can be in one or more topics. In such a case, a Kmelia publication that
* is in a topic other that the original one is said to be an alias of the publication in the
* original topic: any changes are done in the true publication.
* </p>
*/
public class KmeliaPublication
implements I18nContribution, SilverpeasContent, WithPermanentLink, WithThumbnail {
private static final long serialVersionUID = 4861635754389280165L;
private PublicationDetail detail;
private CompletePublication completeDetail;
private final PublicationPK pk;
private boolean read = false;
private Location location;
private KmeliaPublication(PublicationPK id) {
this.pk = id;
}
/**
* Gets the Kmelia publication with the specified primary key identifying it uniquely. If no such
* publication exists with the specified key, then the runtime exception {@link
* KmeliaRuntimeException} is thrown. The publication is by default the original one and hence not
* an alias.
* @param pk the primary key of the publication to get.
* @return the Kmelia publication matching the primary key.
*/
public static KmeliaPublication withPK(final PublicationPK pk) {
KmeliaPublication publication = new KmeliaPublication(pk);
publication.loadPublicationDetail();
return publication;
}
/**
* Gets the Kmelia publication with the specified primary key and that is in the topic specified
* by its primary key. If no such publication exists with the specified key or if the publication
* has no such father, then the runtime exception {@link KmeliaRuntimeException} is thrown.
* @param pk the primary key of the publication to get.
* @param fatherPk the primary key of the topic that is the father of the publication.
* @return the Kmelia publication matching the primary key.
*/
public static KmeliaPublication withPK(final PublicationPK pk, final NodePK fatherPk) {
KmeliaPublication publication = withPK(pk);
publication.setFather(fatherPk, null);
return publication;
}
private void setFather(final NodePK fatherPk, final Map<String, List<Location>> locationCache) {
location = findLocation(fatherPk, locationCache);
if (location != null) {
getDetail().setAlias(location.isAlias());
}
}
private Location findLocation(final NodePK pk, final Map<String, List<Location>> locationCache) {
final Predicate<Location> predicate;
final String nodeIMsg;
if (pk != null) {
predicate = pk::equals;
nodeIMsg = " in node " + pk.getId() + "(Kmelia " + pk.getInstanceId() + ")";
} else {
predicate = l -> !l.isAlias();
nodeIMsg = "";
}
final PublicationPK mainPubPk =
getDetail().isClone() ? getDetail().getClonePK() : getDetail().getPK();
return getAllLocations(mainPubPk, locationCache).stream()
.filter(predicate)
.findFirst()
.orElseGet(() -> {
if (pk == null || !KmeliaHelper.isKmax(pk.getInstanceId())) {
throw new KmeliaRuntimeException(
"Unable to find the location of the publication " + getId() + nodeIMsg);
}
return null;
});
}
private Collection<Location> getAllLocations(final PublicationPK mainPubPk,
Map<String, List<Location>> locationCache) {
if (locationCache != null) {
return locationCache.getOrDefault(mainPubPk.getId(), Collections.emptyList());
}
return PublicationService.get()
.getAllLocations(mainPubPk);
}
public Location getLocation() {
if (location == null) {
location = findLocation(null, null);
}
return location;
}
/**
* Gets the Kmelia publication from the specified publication detail. The publication is by
* default the original one and not an alias.
* @param detail the detail about the publication to get.
* @return the Kmelia publication matching the specified publication detail.
*/
public static KmeliaPublication fromDetail(final PublicationDetail detail) {
KmeliaPublication publication = new KmeliaPublication(detail.getPK());
publication.setPublicationDetail(detail);
return publication;
}
/**
* Gets the Kmelia publication in the given topic from the specified publication detail.
* @param detail the detail about the publication to get.
* @param fatherPK the primary key of the topic that is father of the publication to get.
* @return the Kmelia publication matching the specified publication detail.
*/
public static KmeliaPublication fromDetail(final PublicationDetail detail,
final NodePK fatherPK) {
return fromDetail(detail, fatherPK, null);
}
/**
* Gets the Kmelia publication in the given topic from the specified publication detail.
* @param detail the detail about the publication to get.
* @param fatherPK the primary key of the topic that is father of the publication to get.
* @param locationCache cache of locations already loaded.
* @return the Kmelia publication matching the specified publication detail.
*/
public static KmeliaPublication fromDetail(final PublicationDetail detail, final NodePK fatherPK,
final Map<String, List<Location>> locationCache) {
final KmeliaPublication publication = new KmeliaPublication(detail.getPK());
publication.setPublicationDetail(detail);
publication.setFather(fatherPK, locationCache);
return publication;
}
/**
* Gets the Kmelia publication from the specified complete publication detail. The publication is
* by default the original one and not an alias.
* @param detail the complete detail about the publication to get.
* @return the Kmelia publication matching the specified complete publication detail.
*/
public static KmeliaPublication aKmeliaPublicationFromCompleteDetail(
final CompletePublication detail) {
KmeliaPublication publication = new KmeliaPublication(detail.getPublicationDetail()
.getPK());
publication.setPublicationCompleteDetail(detail);
return publication;
}
public boolean isRead() {
return this.read;
}
public void setAsRead() {
this.read = true;
}
/**
* Is this publication an alias of an existing Kmelia publication?
* @return true if this publication is an alias, false otherwise.
*/
public boolean isAlias() {
return getDetail().isAlias();
}
/**
* Is this publication visible?
* @return true if this publication is visible, false otherwise.
*/
public boolean isVisible() {
return getDetail().isVisible();
}
/**
* Gets the primary key of this publication.
* @return the publication primary key.
*/
public PublicationPK getPk() {
return pk;
}
/**
* Gets the unique identifier of this publication.
* @return the unique identifier of this publication.
*/
@Override
public String getId() {
return pk.getId();
}
/**
* Gets the complete URL at which this publication is located.
* @return the publication URL.
*/
public String getURL() {
String defaultURL = getOrganizationController().getDomain(getCreator().getDomainId())
.getSilverpeasServerURL();
String serverURL = ResourceLocator.getGeneralSettingBundle()
.getString("httpServerBase", defaultURL);
return serverURL + URLUtil.getSimpleURL(URLUtil.URL_PUBLI, getPk().getId());
}
/**
* Gets the details about this publication.
* @return the publication details.
*/
public PublicationDetail getDetail() {
if (detail == null) {
loadPublicationDetail();
}
return detail;
}
/**
* Gets the complete detail about this publication.
* @return the publication complete details.
*/
public CompletePublication getCompleteDetail() {
if (completeDetail == null) {
setPublicationCompleteDetail(getKmeliaService().getCompletePublication(pk));
}
return completeDetail;
}
/**
* Gets the creator of this publication (the initial author).
* @return the detail about the creator of this publication.
*/
@Override
public User getCreator() {
String creatorId = getDetail().getCreatorId();
return User.getById(creatorId);
}
/**
* Gets the user that has lastly modified this publication. He's the last one that has worked on
* this publication. If this publication was not modified since its creation, the creator is
* returned as he's the last user that has worked on this publication.
* @return the detail about the last modifier of this publication.
*/
@Override
public User getLastUpdater() {
User lastModifier;
String modifierId = getDetail().getUpdaterId();
if (modifierId == null) {
lastModifier = getCreator();
} else {
lastModifier = User.getById(modifierId);
}
return lastModifier;
}
/**
* Gets the comments on this publication.
* @return an unmodifiable list with the comments on this publication.
*/
public List<Comment> getComments() {
return Collections.unmodifiableList(
getCommentService().getAllCommentsOnResource(PublicationDetail.getResourceType(),
new ResourceReference(pk)));
}
/**
* Gets the positions in the PDC of this publication.
* @return an unmodifiable list with the PDC positions of this publication.
*/
public List<ClassifyPosition> getPDCPositions() {
int silverObjectId = getKmeliaService().getSilverObjectId(pk);
try {
return PdcManager.get()
.getPositions(silverObjectId, pk.getInstanceId());
} catch (PdcException e) {
throw new KmeliaRuntimeException(e);
}
}
public int getNbAccess() {
try {
return getStatisticService().getCount(new ResourceReference(detail.getPK()), 1,
"Publication");
} catch (Exception e) {
SilverLogger.getLogger(this)
.error(e);
}
return -1;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final KmeliaPublication other = (KmeliaPublication) obj;
return Objects.equals(this.pk, other.pk);
}
@Override
public int hashCode() {
int hash = 7;
hash = 67 * hash + (this.pk != null ? this.pk.hashCode() : 0);
return hash;
}
private void setPublicationDetail(final PublicationDetail detail) {
if (detail == null) {
throw new KmeliaRuntimeException(
SilverpeasExceptionMessages.failureOnGetting("publication detail", getId()));
}
this.detail = detail;
}
private void setPublicationCompleteDetail(final CompletePublication detail) {
if (this.detail == null) {
setPublicationDetail(detail.getPublicationDetail());
}
this.completeDetail = detail;
}
private KmeliaService getKmeliaService() {
try {
return KmeliaService.get();
} catch (Exception e) {
throw new KmeliaRuntimeException(e);
}
}
private StatisticService getStatisticService() {
return StatisticService.get();
}
private CommentService getCommentService() {
return CommentServiceProvider.getCommentService();
}
private OrganizationController getOrganizationController() {
return OrganizationControllerProvider.getOrganisationController();
}
private void loadPublicationDetail() {
setPublicationDetail(getKmeliaService().getPublicationDetail(pk));
}
@Override
public String getComponentInstanceId() {
return getDetail().getInstanceId();
}
@Override
public String getSilverpeasContentId() {
return getDetail().getSilverpeasContentId();
}
@Override
public Date getCreationDate() {
return getDetail().getCreationDate();
}
@Override
public Date getLastUpdateDate() {
return getDetail().getLastUpdateDate();
}
@Override
public String getTitle() {
return getDetail().getTitle();
}
@Override
public String getDescription() {
return getDetail().getDescription();
}
@Override
public String getContributionType() {
return getDetail().getContributionType();
}
@SuppressWarnings({"unchecked"})
@Override
public Optional<PublicationPath> getResourcePath() {
return getDetail().getResourcePath();
}
/**
* Is the specified user can access this publication?
* <p/>
* A user can access a publication if he has enough rights to access both the Kmelia instance in
* which is managed this publication and the topics to which this publication belongs to.
* @param user a user in Silverpeas.
* @return true if the user can access this publication, false otherwise.
*/
@Override
public boolean canBeAccessedBy(final User user) {
return getDetail().canBeAccessedBy(user);
}
/**
* Get the number of comments on this publication
* @return the number.
*/
public int getNumberOfComments() {
return getCommentService().getCommentsCountOnResource(PublicationDetail.getResourceType(),
new ResourceReference(getPk()));
}
/**
* Gets the original location of this alias according to the access right the of the specified
* user. If this location isn't an alias, then returns itself. If the user has no access right to
* the original location, then returns nothing. If no original location can be found, whatever the
* access right of the user, a {@link KmeliaRuntimeException} is thrown.
* @param userId the unique identifier of the user for which the access right on the original
* location has to be checked.
* @return either the original location (itself if this location is already the original one) or
* nothing whether the given user has no access right on it.
*/
public Optional<Location> getOriginalLocation(String userId) {
final Location originalLocation = findLocation(null, null);
if (originalLocation != null && NodeAccessControl.get()
.isUserAuthorized(userId, originalLocation)) {
return Optional.of(originalLocation);
} else {
return Optional.empty();
}
}
public ValidatorsList getValidators() {
return getKmeliaService().getAllValidators(getPk());
}
@Override
public String getPermalink() {
return detail.getPermalink();
}
@Override
public Thumbnail getThumbnail() {
return detail.getThumbnail();
}
@Override
public ContributionIdentifier getIdentifier() {
return detail.getIdentifier();
}
@Override
public ResourceTranslation getTranslation(final String language) {
return detail.getTranslation(language);
}
@Override
public ContributionModel getModel() {
return detail.getModel();
}
}