Skip to content

Commit

Permalink
feat: Allow to Impersonate Containers by cloning Background Image - M…
Browse files Browse the repository at this point in the history
…EED-7736 - Meeds-io/MIPs#160 (#254)

This change will allow to clone Containers Background images when
creating a site from a site template or a site template from an existing
site.
  • Loading branch information
boubaker authored and exo-swf committed Nov 18, 2024
1 parent 6dffaa9 commit 8b7b861
Show file tree
Hide file tree
Showing 4 changed files with 439 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/**
* This file is part of the Meeds project (https://meeds.io/).
*
* Copyright (C) 2020 - 2024 Meeds Association [email protected]
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package io.meeds.layout.service;

import static io.meeds.layout.plugin.attachment.LayoutBackgroundAttachmentPlugin.OBJECT_TYPE;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.UUID;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import org.exoplatform.commons.file.model.FileInfo;
import org.exoplatform.commons.file.model.FileItem;
import org.exoplatform.commons.file.services.FileService;
import org.exoplatform.portal.config.UserACL;
import org.exoplatform.portal.config.model.ApplicationBackgroundStyle;
import org.exoplatform.portal.config.model.Container;
import org.exoplatform.portal.config.model.ModelStyle;
import org.exoplatform.portal.config.model.Page;
import org.exoplatform.social.attachment.AttachmentService;
import org.exoplatform.social.attachment.model.ObjectAttachmentDetail;
import org.exoplatform.social.attachment.model.ObjectAttachmentList;
import org.exoplatform.social.attachment.model.UploadedAttachmentDetail;
import org.exoplatform.social.core.manager.IdentityManager;
import org.exoplatform.upload.UploadResource;
import org.exoplatform.upload.UploadService;

import lombok.SneakyThrows;

@Service
public class ContainerLayoutSrvice {

private static final String CONTAINER_BACKGROUND_IMAGE_URI = String.format("/%s/", OBJECT_TYPE);

@Autowired
private AttachmentService attachmentService;

@Autowired
private UploadService uploadService;

@Autowired
private FileService fileService;

@Autowired
private UserACL userAcl;

@Autowired
private IdentityManager identityManager;

private long superUserIdentityId;

public void impersonateContainer(Container container, Page page) throws Exception {
if (page == null || container == null) { // PortalConfig isn't managed yet
// for now (Site Layout)
return;
}
ModelStyle cssStyle = container.getCssStyle();
if (cssStyle != null && StringUtils.isNotBlank(cssStyle.getBackgroundImage())) {
String clonedBackgroundImageUrl = null;
try {
clonedBackgroundImageUrl = cloneBackgroundUrl(container, page, cssStyle.getBackgroundImage());
} finally {
// Even in case of exception thrown, set null
cssStyle.setBackgroundImage(clonedBackgroundImageUrl);
}
}
ApplicationBackgroundStyle appBackgroundStyle = container.getAppBackgroundStyle();
if (appBackgroundStyle != null
&& StringUtils.isNotBlank(appBackgroundStyle.getBackgroundImage())) {
String clonedBackgroundImageUrl = null;
try {
clonedBackgroundImageUrl = cloneBackgroundUrl(container, page, appBackgroundStyle.getBackgroundImage());
} finally {
// Even in case of exception thrown, set null
appBackgroundStyle.setBackgroundImage(clonedBackgroundImageUrl);
}
}
}

private String cloneBackgroundUrl(Container container, Page page, String backgroundImageUrl) throws Exception { // NOSONAR
String clonedBackgroundImageUrl = null;
if (StringUtils.contains(backgroundImageUrl, CONTAINER_BACKGROUND_IMAGE_URI)) {
String[] backgroundImageUrlParts = backgroundImageUrl.split("/");
String fileIdString = backgroundImageUrlParts[backgroundImageUrlParts.length - 1];
if (StringUtils.isNotBlank(fileIdString) && NumberUtils.isCreatable(fileIdString)) {
FileItem file = fileService.getFile(Long.parseLong(fileIdString));
if (file != null) {
String objectId = getObjectId(container, page);
UploadResource uploadResource = createUploadResource(file);
ObjectAttachmentDetail attachment = saveAttachment(objectId, uploadResource);
if (attachment != null) {
clonedBackgroundImageUrl = buildBackgroundUrl(objectId, attachment);
}
}
}
}
return clonedBackgroundImageUrl;
}

private String getObjectId(Container container, Page page) {
return String.format("%s_%s",
page.getStorageId().replace("page_", ""),
container.getStorageId());
}

private String buildBackgroundUrl(String objectId, ObjectAttachmentDetail attachment) {
return String.format("/portal/rest/v1/social/attachments/%s/%s/%s",
OBJECT_TYPE,
objectId,
attachment.getId());
}

@SneakyThrows
private ObjectAttachmentDetail saveAttachment(String objectId, UploadResource uploadResource) {
UploadedAttachmentDetail attachmentDetail = new UploadedAttachmentDetail(uploadResource);
attachmentService.saveAttachment(attachmentDetail,
OBJECT_TYPE,
objectId,
null,
getSuperUserIdentityId());
ObjectAttachmentList attachmentList = attachmentService.getAttachments(OBJECT_TYPE, objectId);
if (attachmentList != null && CollectionUtils.isNotEmpty(attachmentList.getAttachments())) {
return attachmentList.getAttachments().get(0);
} else {
return null;
}
}

private UploadResource createUploadResource(FileItem fileItem) throws IOException {
String uploadId = UUID.randomUUID().toString();
FileInfo fileInfo = fileItem.getFileInfo();
Path path = Files.createTempFile(fileInfo.getName(), ".png");
try (InputStream inputStream = fileItem.getAsStream()) {
File file = path.toFile();
FileUtils.copyInputStreamToFile(inputStream, file);
UploadResource uploadResource = new UploadResource(uploadId,
fileInfo.getName(),
fileInfo.getMimetype(),
file.getAbsolutePath(),
fileInfo.getSize(),
fileInfo.getSize(),
UploadResource.UPLOADED_STATUS);
uploadService.createUploadResource(uploadResource);
return uploadResource;
}
}

private long getSuperUserIdentityId() {
if (superUserIdentityId == 0) {
superUserIdentityId = Long.parseLong(identityManager.getOrCreateUserIdentity(userAcl.getSuperUser()).getId());
}
return superUserIdentityId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,32 +69,35 @@
@Service
public class PageLayoutService {

public static final String EMPTY_PAGE_TEMPLATE = "empty";
public static final String EMPTY_PAGE_TEMPLATE = "empty";

private static final Log LOG = ExoLogger.getLogger(PageLayoutService.class);
private static final Log LOG = ExoLogger.getLogger(PageLayoutService.class);

private static final Pattern GENERIC_STYLE_MATCHER_VALIDATOR = Pattern.compile("[#0-9a-zA-Z\\(\\),\\./\"'\\-%_ ]+");
private static final Pattern GENERIC_STYLE_MATCHER_VALIDATOR = Pattern.compile("[#0-9a-zA-Z\\(\\),\\./\"'\\-%_ ]+");

private static final String PAGE_NOT_EXISTS_MESSAGE = "Page with key %s doesn't exist";
private static final String PAGE_NOT_EXISTS_MESSAGE = "Page with key %s doesn't exist";

private static final String PAGE_NOT_ACCESSIBLE_MESSAGE = "Page with ref %s isn't accessible for user %s";
private static final String PAGE_NOT_ACCESSIBLE_MESSAGE = "Page with ref %s isn't accessible for user %s";

private static final String PAGE_NOT_EDITABLE_MESSAGE = "Page with ref %s isn't editable for user %s";
private static final String PAGE_NOT_EDITABLE_MESSAGE = "Page with ref %s isn't editable for user %s";

@Autowired
private LayoutService layoutService;
private LayoutService layoutService;

@Autowired
private LayoutAclService aclService;
private LayoutAclService aclService;

@Autowired
private PageTemplateService pageTemplateService;
private ContainerLayoutSrvice containerLayoutSrvice;

@Autowired
private PortletInstanceService portletInstanceService;
private PageTemplateService pageTemplateService;

@Autowired
private AddOnService addOnService;
private PortletInstanceService portletInstanceService;

@Autowired
private AddOnService addOnService;

public List<PageContext> getPages(String siteTypeName,
String siteName,
Expand Down Expand Up @@ -141,7 +144,7 @@ public Application getPageApplicationLayout(PageKey pageKey,

public void impersonateSite(SiteKey siteKey) {
PortalConfig portalConfig = layoutService.getPortalConfig(siteKey);
impersonateModel(portalConfig.getPortalLayout());
impersonateModel(portalConfig.getPortalLayout(), null);
List<PageContext> pages = layoutService.findPages(siteKey);
if (CollectionUtils.isNotEmpty(pages)) {
pages.forEach(page -> impersonatePage(page.getKey()));
Expand All @@ -150,7 +153,7 @@ public void impersonateSite(SiteKey siteKey) {

public void impersonatePage(PageKey pageKey) {
Page page = getPageLayout(pageKey);
impersonateModel(page);
impersonateModel(page, page);
}

public Page getPageLayout(PageKey pageKey, String username) throws ObjectNotFoundException, IllegalAccessException {
Expand Down Expand Up @@ -476,12 +479,19 @@ private void computeApplicationPreferences(Application application, String usern
application.setState(applicationState);
}

@SneakyThrows
private void impersonateModel(ModelObject object) {
private void impersonateModel(ModelObject object, Page page) {
if (object instanceof Container container) {
ArrayList<ModelObject> children = container.getChildren();
try {
containerLayoutSrvice.impersonateContainer(container, page);
} catch (Exception e) {
LOG.warn("Error while impersonating container '{}' in page '{}'. Ignore cloning container background image.",
container.getStorageId(),
page.getStorageId(),
e);
}
if (CollectionUtils.isNotEmpty(children)) {
children.forEach(this::impersonateModel);
children.forEach(c -> this.impersonateModel(c, page));
}
} else if (object instanceof Application application) {
Portlet preferences = portletInstanceService.exportApplicationPreferences(application);
Expand Down
Loading

0 comments on commit 8b7b861

Please sign in to comment.