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