/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
*
* This library 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 2.1 of the License, or (at your option) any later version.
*
* This library 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.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.ade.containerpage;
import org.opencms.ade.configuration.CmsADEConfigData;
import org.opencms.ade.containerpage.shared.CmsContainer;
import org.opencms.ade.containerpage.shared.CmsContainerElementData;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.types.CmsResourceTypeXmlContent;
import org.opencms.i18n.CmsEncoder;
import org.opencms.jsp.CmsJspTagHeadIncludes;
import org.opencms.jsp.util.CmsJspStandardContextBean;
import org.opencms.loader.CmsTemplateLoaderFacade;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.OpenCms;
import org.opencms.search.galleries.CmsGallerySearch;
import org.opencms.search.galleries.CmsGallerySearchResult;
import org.opencms.security.CmsPermissionSet;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import org.opencms.workplace.editors.directedit.CmsAdvancedDirectEditProvider;
import org.opencms.workplace.editors.directedit.CmsDirectEditMode;
import org.opencms.workplace.editors.directedit.I_CmsDirectEditProvider;
import org.opencms.workplace.explorer.CmsResourceUtil;
import org.opencms.xml.containerpage.CmsContainerBean;
import org.opencms.xml.containerpage.CmsContainerElementBean;
import org.opencms.xml.containerpage.CmsContainerPageBean;
import org.opencms.xml.containerpage.CmsFormatterBean;
import org.opencms.xml.containerpage.CmsFormatterConfiguration;
import org.opencms.xml.containerpage.CmsGroupContainerBean;
import org.opencms.xml.containerpage.CmsXmlContainerPage;
import org.opencms.xml.containerpage.CmsXmlContainerPageFactory;
import org.opencms.xml.containerpage.CmsXmlGroupContainer;
import org.opencms.xml.containerpage.CmsXmlGroupContainerFactory;
import org.opencms.xml.content.CmsXmlContentProperty;
import org.opencms.xml.content.CmsXmlContentPropertyHelper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
/**
* Utility class to generate the element data objects used within the container-page editor.<p>
*
* @since 8.0.0
*/
public class CmsElementUtil {
/** Static reference to the log. */
private static final Log LOG = CmsLog.getLog(org.opencms.ade.containerpage.CmsElementUtil.class);
/** The cms context. */
private CmsObject m_cms;
/** The current page uri. */
private String m_currentPageUri;
/** The content locale. */
private Locale m_locale;
/** The request parameters to use while rendering the elements. */
@SuppressWarnings("unused")
private Map<String, Object> m_parameterMap;
/** The http request. */
private HttpServletRequest m_req;
/** The http response. */
private HttpServletResponse m_res;
/** The standard context bean. */
private CmsJspStandardContextBean m_standardContext;
/**
* Creates a new instance.<p>
*
* @param cms the cms context
* @param currentPageUri the current page uri
* @param req the http request
* @param res the http response
* @param locale the content locale
*
* @throws CmsException if something goes wrong
*/
public CmsElementUtil(
CmsObject cms,
String currentPageUri,
HttpServletRequest req,
HttpServletResponse res,
Locale locale)
throws CmsException {
m_cms = OpenCms.initCmsObject(cms);
m_req = req;
m_res = res;
m_currentPageUri = currentPageUri;
m_locale = locale;
// initializing request for standard context bean
req.setAttribute(CmsJspStandardContextBean.ATTRIBUTE_CMS_OBJECT, m_cms);
m_standardContext = CmsJspStandardContextBean.getInstance(req);
CmsXmlContainerPage xmlContainerPage = CmsXmlContainerPageFactory.unmarshal(
cms,
m_cms.readResource(currentPageUri),
req);
CmsContainerPageBean containerPage = xmlContainerPage.getContainerPage(cms, m_locale);
m_standardContext.setPage(containerPage);
}
/**
* Creates a new instance.<p>
*
* @param cms the cms context
* @param currentPageUri the current page uri
* @param requestParameters the request parameters to use while rendering the elements
* @param req the http request
* @param res the http response
* @param locale the content locale
*
* @throws CmsException if something goes wrong
*/
public CmsElementUtil(
CmsObject cms,
String currentPageUri,
String requestParameters,
HttpServletRequest req,
HttpServletResponse res,
Locale locale)
throws CmsException {
this(cms, currentPageUri, req, res, locale);
m_parameterMap = parseRequestParameters(requestParameters);
}
/**
* Returns the rendered element content for all the given containers.
*
* @param element the element to render
* @param containers the containers the element appears in
*
* @return a map from container names to rendered page contents
*/
public Map<String, String> getContentsByContainerName(
CmsContainerElementBean element,
Collection<CmsContainer> containers) {
CmsADEConfigData adeConfig = OpenCms.getADEManager().lookupConfiguration(
m_cms,
m_cms.getRequestContext().addSiteRoot(m_currentPageUri));
CmsFormatterConfiguration configs = adeConfig.getFormatters(m_cms, element.getResource());
Map<String, String> result = new HashMap<String, String>();
for (CmsContainer container : containers) {
String name = container.getName();
CmsFormatterBean formatter = configs.getFormatter(container.getType(), container.getWidth());
if (formatter != null) {
String content = null;
try {
content = getElementContent(element, m_cms.readResource(formatter.getJspStructureId()), container);
} catch (Exception e) {
LOG.error(e.getLocalizedMessage(), e);
}
if (content != null) {
content = removeScriptTags(content);
result.put(name, content);
}
}
}
return result;
}
/**
* Returns the data for an element.<p>
*
* @param element the resource
* @param containers the containers on the current container page
*
* @return the data for an element
*
* @throws CmsException if something goes wrong
*/
public CmsContainerElementData getElementData(CmsContainerElementBean element, Collection<CmsContainer> containers)
throws CmsException {
Locale requestLocale = m_cms.getRequestContext().getLocale();
m_cms.getRequestContext().setLocale(m_locale);
element.initResource(m_cms);
CmsResourceUtil resUtil = new CmsResourceUtil(m_cms, element.getResource());
CmsUUID structureId = resUtil.getResource().getStructureId();
String title = resUtil.getTitle();
if (!structureId.isNullUUID()) {
CmsGallerySearchResult searchResult = CmsGallerySearch.searchById(m_cms, structureId, requestLocale);
title = searchResult.getTitle();
}
CmsContainerElementData elementBean = new CmsContainerElementData();
elementBean.setReleasedAndNotExpired(element.isReleasedAndNotExpired());
elementBean.setClientId(element.editorHash());
elementBean.setSitePath(resUtil.getFullPath());
elementBean.setLastModifiedDate(element.getResource().getDateLastModified());
elementBean.setLastModifiedByUser(m_cms.readUser(element.getResource().getUserLastModified()).getName());
elementBean.setNavText(resUtil.getNavText());
elementBean.setTitle(title);
elementBean.setResourceType(OpenCms.getResourceManager().getResourceType(element.getResource().getTypeId()).getTypeName());
Set<String> cssResources = new LinkedHashSet<String>();
for (String cssSitePath : CmsJspTagHeadIncludes.getCSSHeadIncludes(m_cms, element.getResource())) {
cssResources.add(OpenCms.getLinkManager().substituteLinkForUnknownTarget(m_cms, cssSitePath));
}
elementBean.setCssResources(cssResources);
Map<String, CmsXmlContentProperty> settingConfig = CmsXmlContentPropertyHelper.getPropertyInfo(
m_cms,
element.getResource());
elementBean.setSettings(CmsXmlContentPropertyHelper.convertPropertiesToClientFormat(
m_cms,
element.getIndividualSettings(),
settingConfig));
elementBean.setSettingConfig(new LinkedHashMap<String, CmsXmlContentProperty>(settingConfig));
// no need to check permissions for in memory resources
elementBean.setViewPermission(element.isInMemoryOnly()
|| m_cms.hasPermissions(
element.getResource(),
CmsPermissionSet.ACCESS_VIEW,
false,
CmsResourceFilter.IGNORE_EXPIRATION));
String noEditReason = "";
if (CmsResourceTypeXmlContent.isXmlContent(element.getResource())) {
if (!element.isInMemoryOnly()) {
noEditReason = CmsEncoder.escapeHtml(resUtil.getNoEditReason(
OpenCms.getWorkplaceManager().getWorkplaceLocale(m_cms),
true));
}
} else {
noEditReason = org.opencms.jsp.Messages.get().getBundle().key(
org.opencms.jsp.Messages.GUI_ELEMENT_RESOURCE_CAN_NOT_BE_EDITED_0);
}
elementBean.setNoEditReason(noEditReason);
elementBean.setStatus(resUtil.getStateAbbreviation());
Map<String, String> contents = new HashMap<String, String>();
if (element.isGroupContainer(m_cms)) {
Set<String> types = new HashSet<String>();
Map<String, CmsContainer> containersByName = new HashMap<String, CmsContainer>();
for (CmsContainer container : containers) {
types.add(container.getType());
containersByName.put(container.getName(), container);
}
CmsXmlGroupContainer xmlGroupContainer = CmsXmlGroupContainerFactory.unmarshal(
m_cms,
element.getResource(),
m_req);
CmsGroupContainerBean groupContainer = xmlGroupContainer.getGroupContainer(m_cms, m_locale);
elementBean.setGroupContainer(true);
// make sure to use the content title and not the property title
elementBean.setTitle(groupContainer.getTitle());
elementBean.setTypes(groupContainer.getTypes());
elementBean.setDescription(groupContainer.getDescription());
if (groupContainer.getTypes().isEmpty()) {
if (groupContainer.getElements().isEmpty()) {
String emptySub = "<div>NEW AND EMPTY</div>";
for (String name : containersByName.keySet()) {
contents.put(name, emptySub);
}
} else {
// TODO: throw appropriate exception
return null;
}
} else {
// add formatter and content entries for the supported types
for (CmsContainer cnt : containersByName.values()) {
String type = cnt.getType();
if (groupContainer.getTypes().contains(type)) {
contents.put(cnt.getName(), "<div>should not be used</div>");
}
}
}
// add subitems
List<String> subItems = new ArrayList<String>();
for (CmsContainerElementBean subElement : groupContainer.getElements()) {
// collect ids
subItems.add(subElement.editorHash());
}
elementBean.setSubItems(subItems);
} else {
// get the formatter configuration
Map<String, String> contentsByName = getContentsByContainerName(element, containers);
contents = contentsByName;
}
elementBean.setContents(contents);
m_cms.getRequestContext().setLocale(requestLocale);
return elementBean;
}
/**
* Returns the content of an element when rendered with the given formatter.<p>
*
* @param element the element bean
* @param formatter the formatter uri
* @param container the container for which the element content should be retrieved
*
* @return generated html code
*
* @throws CmsException if an cms related error occurs
* @throws ServletException if a jsp related error occurs
*
* @throws IOException if a jsp related error occurs
*/
private String getElementContent(CmsContainerElementBean element, CmsResource formatter, CmsContainer container)
throws CmsException, ServletException, IOException {
element.initResource(m_cms);
CmsTemplateLoaderFacade loaderFacade = new CmsTemplateLoaderFacade(OpenCms.getResourceManager().getLoader(
formatter), element.getResource(), formatter);
CmsResource loaderRes = loaderFacade.getLoaderStartResource();
String oldUri = m_cms.getRequestContext().getUri();
try {
m_cms.getRequestContext().setUri(m_currentPageUri);
CmsContainerBean containerBean = null;
if ((m_standardContext.getPage() != null)
&& m_standardContext.getPage().getContainers().containsKey(container.getName())) {
containerBean = m_standardContext.getPage().getContainers().get(container.getName());
} else {
containerBean = new CmsContainerBean(
container.getName(),
container.getType(),
container.getMaxElements(),
Collections.<CmsContainerElementBean> emptyList());
}
if (containerBean.getWidth() == null) {
containerBean.setWidth(String.valueOf(container.getWidth()));
}
m_standardContext.setContainer(containerBean);
m_standardContext.setElement(element);
m_standardContext.setEdited(true);
// to enable 'old' direct edit features for content-collector-elements,
// set the direct-edit-provider-attribute in the request
I_CmsDirectEditProvider eb = new CmsAdvancedDirectEditProvider();
eb.init(m_cms, CmsDirectEditMode.TRUE, element.getSitePath());
m_req.setAttribute(I_CmsDirectEditProvider.ATTRIBUTE_DIRECT_EDIT_PROVIDER, eb);
String encoding = m_res.getCharacterEncoding();
return (new String(loaderFacade.getLoader().dump(m_cms, loaderRes, null, m_locale, m_req, m_res), encoding)).trim();
} finally {
m_cms.getRequestContext().setUri(oldUri);
}
}
/**
* Parses the given request parameters string into a parameter map.<p>
*
* @param requestParameters the request parameters to parse
*
* @return the parameter map
*/
private Map<String, Object> parseRequestParameters(String requestParameters) {
Map<String, Object> parameterMap;
if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(requestParameters)) {
parameterMap = new HashMap<String, Object>();
String[] params = requestParameters.split("&");
for (int i = 0; i < params.length; i++) {
int position = params[i].indexOf("=");
if (position >= 0) {
String key = params[i].substring(0, position);
String value = params[i].substring(position + 1);
if (value.contains(",")) {
parameterMap.put(key, value.split(","));
} else {
parameterMap.put(key, value);
}
}
}
} else {
parameterMap = Collections.<String, Object> emptyMap();
}
return parameterMap;
}
/**
* Removes all script tags from given input.<p>
*
* @param input the input to remove script tags from
*
* @return the cleaned input
*/
private String removeScriptTags(String input) {
Pattern removePattern = Pattern.compile("<script[^>]*?>[\\s\\S]*?<\\/script>");
Matcher match = removePattern.matcher(input);
return match.replaceAll("");
}
}