001/* 002 * Copyright 2013 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.cms.workflow.copy; 017 018import java.util.HashMap; 019import java.util.Locale; 020import java.util.Map; 021import java.util.Map.Entry; 022 023import org.apache.avalon.framework.service.ServiceException; 024import org.apache.avalon.framework.service.ServiceManager; 025import org.apache.commons.lang3.LocaleUtils; 026import org.apache.commons.lang3.StringUtils; 027 028import org.ametys.cms.content.CopyContentComponent; 029import org.ametys.cms.content.CopyReport; 030import org.ametys.cms.content.CopyReport.CopyMode; 031import org.ametys.cms.contenttype.ContentTypesHelper; 032import org.ametys.cms.repository.Content; 033import org.ametys.cms.repository.ModifiableContent; 034import org.ametys.cms.workflow.CreateContentFunction; 035import org.ametys.cms.workflow.EditContentFunction; 036import org.ametys.core.util.I18nUtils; 037import org.ametys.plugins.repository.UnknownAmetysObjectException; 038import org.ametys.plugins.workflow.AbstractWorkflowComponent; 039import org.ametys.runtime.i18n.I18nizableText; 040 041import com.opensymphony.module.propertyset.PropertySet; 042import com.opensymphony.workflow.WorkflowException; 043 044/** 045 * OSWorkflow function to create a content by copy of another 046 * 047 * The required transient variables: 048 * - CreateContentByCopyFunction.BASE_CONTENT_KEY - Content The content that will be used for duplication. 049 * - or CreateContentByCopyFunction.BASE_CONTENT_ID - String The id of content The content that will be used for duplication. 050 * 051 * - CreateContentByCopyFunction.COPY_MAP_KEY - Map<String, Object> The map of properties to copy. Can be null if CreateContentByCopyFunction.COPY_VIEW_NAME is used 052 */ 053public class CreateContentByCopyFunction extends CreateContentFunction 054{ 055 /** Constant for storing the base content used for the duplication into the transient variables map. */ 056 public static final String BASE_CONTENT_KEY = CreateContentByCopyFunction.class.getName() + "$baseContent"; 057 058 /** Constant for storing the id of base content used for the duplication into the transient variables map. */ 059 public static final String BASE_CONTENT_ID = CreateContentByCopyFunction.class.getName() + "$baseContentId"; 060 061 /** Constant for storing the map containing the duplication info into the transient variables map. Can be null.*/ 062 public static final String COPY_MAP_KEY = CreateContentByCopyFunction.class.getName() + "$copyProperties"; 063 064 /** Constant for storing the copy report object into the transient variables map. Can be null. */ 065 public static final String COPY_REPORT_KEY = CreateContentByCopyFunction.class.getName() + "$copyReport"; 066 067 /** Constant for storing the name of view use for copy. */ 068 public static final String COPY_VIEW_NAME = CreateContentByCopyFunction.class.getName() + "$viewName"; 069 /** Constant for storing the name of fallback view to use for copy. */ 070 public static final String COPY_FALLBACK_VIEW_NAME = CreateContentByCopyFunction.class.getName() + "$fallbackViewName"; 071 072 /** The content copy component */ 073 protected CopyContentComponent _copyContentComponent; 074 /** The content type helper */ 075 protected ContentTypesHelper _cTypesHelper; 076 077 /** I18n Utils */ 078 protected I18nUtils _i18nUtils; 079 080 @Override 081 public void service(ServiceManager manager) throws ServiceException 082 { 083 super.service(manager); 084 _copyContentComponent = (CopyContentComponent) manager.lookup(CopyContentComponent.ROLE); 085 _cTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE); 086 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 087 } 088 089 @SuppressWarnings("unchecked") 090 @Override 091 public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException 092 { 093 // Preparation of the transientVars to work with the super method (CreateContentFunction). 094 Content baseContent = getBaseContentForCopy(transientVars); 095 096 if (baseContent == null) 097 { 098 throw new WorkflowException("Unable to retrieve the base content for the duplication."); 099 } 100 101 if (transientVars.get(CreateContentFunction.CONTENT_TITLE_KEY) == null) 102 { 103 if (_contentHelper.isMultilingual(baseContent)) 104 { 105 Map<String, String> titleVariants = new HashMap<>(); 106 Map<String, String> baseTitleVariants = _contentHelper.getTitleVariants(baseContent); 107 108 for (Entry<String, String> entry : baseTitleVariants.entrySet()) 109 { 110 titleVariants.put(entry.getKey(), entry.getValue() + _i18nUtils.translate(new I18nizableText("plugin.cms", "CONTENT_COPY_CREATE_CONTENT_TITLE_SUFFIX"))); 111 } 112 113 String defaultLang = (String) transientVars.get(CreateContentFunction.CONTENT_LANGUAGE_KEY); 114 Locale defaultLocale = StringUtils.isNotEmpty(defaultLang) ? LocaleUtils.toLocale(defaultLang) : LocaleUtils.toLocale(titleVariants.keySet().iterator().next()); 115 transientVars.put(CreateContentFunction.CONTENT_NAME_KEY, baseContent.getTitle(defaultLocale)); 116 transientVars.put(CreateContentFunction.CONTENT_TITLE_VARIANTS_KEY, titleVariants); 117 } 118 else 119 { 120 transientVars.put(CreateContentFunction.CONTENT_NAME_KEY, baseContent.getTitle(null)); 121 // If title is not set, the base title is set with a copy suffix. 122 transientVars.put(CreateContentFunction.CONTENT_TITLE_KEY, baseContent.getTitle(null) + _i18nUtils.translate(new I18nizableText("plugin.cms", "CONTENT_COPY_CREATE_CONTENT_TITLE_SUFFIX"))); 123 } 124 } 125 else 126 { 127 transientVars.put(CreateContentFunction.CONTENT_NAME_KEY, transientVars.get(CreateContentFunction.CONTENT_TITLE_KEY)); 128 } 129 130 if (transientVars.get(CreateContentFunction.CONTENT_TYPES_KEY) == null) 131 { 132 transientVars.put(CreateContentFunction.CONTENT_TYPES_KEY, baseContent.getTypes()); 133 } 134 135 transientVars.put(CreateContentFunction.CONTENT_MIXINS_KEY, baseContent.getMixinTypes()); 136 transientVars.put(CreateContentFunction.CONTENT_LANGUAGE_KEY, baseContent.getLanguage()); 137 138 // Super method call. 139 super.execute(transientVars, args, ps); 140 141 ModifiableContent targetContent = (ModifiableContent) getResultsMap(transientVars).get(CONTENT_KEY); 142 143 // process copy map 144 String viewName = (String) transientVars.getOrDefault(COPY_VIEW_NAME, "default-edition"); 145 String fallbackViewName = (String) transientVars.getOrDefault(COPY_FALLBACK_VIEW_NAME, "main"); 146 147 if (!transientVars.containsKey(COPY_REPORT_KEY)) 148 { 149 transientVars.put(COPY_REPORT_KEY, new CopyReport(baseContent.getId(), _contentHelper.getTitle(baseContent), true, viewName, fallbackViewName, CopyMode.CREATION)); 150 } 151 152 CopyReport copyReport = (CopyReport) transientVars.get(COPY_REPORT_KEY); 153 154 Map<String, Object> copyMap = (Map<String, Object>) transientVars.get(COPY_MAP_KEY); 155 Map<String, Object> additionalCopyMap = getAdditionalCopyMap(transientVars, baseContent, viewName, fallbackViewName); 156 157 // get values from copy map 158 Map<String, Object> values = _copyContentComponent.computeValues(baseContent, targetContent, copyMap, additionalCopyMap, viewName, fallbackViewName, copyReport); 159 160 // Title attribute must never be copied, it has been set during the content creation. 161 values.remove("title"); 162 163 // process input values for EditContentFunction 164 processValues(transientVars, targetContent, values); 165 166 Map<String, Object> parameters = new HashMap<>(); 167 parameters.put(EditContentFunction.VALUES_KEY, values); 168 parameters.put(EditContentFunction.QUIT, true); 169 transientVars.put(AbstractWorkflowComponent.CONTEXT_PARAMETERS_KEY, parameters); 170 } 171 172 /** 173 * Called after creation and before execution of the {@link EditContentFunction} to allow subclasses to add/remove/modify the values. 174 * @param transientVars The workflow transient vars 175 * @param targetContent The newly created content 176 * @param values the values computed from the copyMap and to be given to the EditContentFunction. 177 * @throws WorkflowException if an error occurs 178 */ 179 protected void processValues(Map transientVars, ModifiableContent targetContent, Map<String, Object> values) throws WorkflowException 180 { 181 // do nothing 182 } 183 184 /** 185 * Get the content 186 * @param transientVars The workflow transient vars with BASE_CONTENT_KEY (the content itself) or BASE_CONTENT_ID (the id of the content) 187 * @return the content or null if the object cannot be found 188 */ 189 protected Content getBaseContentForCopy (Map transientVars) 190 { 191 Content baseContent = (Content) transientVars.get(BASE_CONTENT_KEY); 192 193 if (baseContent == null) 194 { 195 String baseContentId = (String) transientVars.get(BASE_CONTENT_ID); 196 197 try 198 { 199 baseContent = _resolver.resolveById(baseContentId); 200 } 201 catch (UnknownAmetysObjectException e) 202 { 203 return null; 204 } 205 } 206 207 return baseContent; 208 } 209 210 /** 211 * Get an additional copy map, if any. It allows subclasses to provide additional data to copy. 212 * This map is concatenated to the initial copy map. 213 * @param transientVars the workflow parameters 214 * @param content the source content to be copied 215 * @param viewName the view name 216 * @param fallbackViewName the fallback view name, if required view does not exist 217 * @return the additional copy map, or null if none. 218 * @throws WorkflowException if an error occurs 219 */ 220 protected Map<String, Object> getAdditionalCopyMap(Map transientVars, Content content, String viewName, String fallbackViewName) throws WorkflowException 221 { 222 // the default implementation does not provide any additional copy map 223 return null; 224 } 225 226 @Override 227 public I18nizableText getLabel() 228 { 229 return new I18nizableText("plugin.cms", "PLUGINS_CMS_CREATE_CONTENT_BY_COPY_FUNCTION_LABEL"); 230 } 231}