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.CopyContentMetadataComponent;
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.core.util.I18nUtils;
035import org.ametys.plugins.repository.UnknownAmetysObjectException;
036import org.ametys.runtime.i18n.I18nizableText;
037
038import com.opensymphony.module.propertyset.PropertySet;
039import com.opensymphony.workflow.WorkflowException;
040
041/**
042 * OSWorkflow function to create a content by copy of another
043 * 
044 * The required transient variables:
045 * - CreateContentByCopyFunction.BASE_CONTENT_KEY - Content The content that will be used for duplication.
046 * - or CreateContentByCopyFunction.BASE_CONTENT_ID - String The id of content The content that will be used for duplication.
047 * 
048 * - CreateContentByCopyFunction.COPY_MAP_KEY - Map<String, Object> The map of properties to copy. Can be null if CreateContentByCopyFunction.COPY_METADATASET_NAME is used
049 */
050public class CreateContentByCopyFunction extends CreateContentFunction
051{
052    /** Constant for storing the base content used for the duplication into the transient variables map. */
053    public static final String BASE_CONTENT_KEY = CreateContentByCopyFunction.class.getName() + "$baseContent";
054    
055    /** Constant for storing the id of base content used for the duplication into the transient variables map. */
056    public static final String BASE_CONTENT_ID = CreateContentByCopyFunction.class.getName() + "$baseContentId";
057    
058    /** Constant for storing the map containing the duplication info into the transient variables map. Can be null.*/
059    public static final String COPY_MAP_KEY = CreateContentByCopyFunction.class.getName() + "$copyProperties";
060    
061    /** Constant for storing the copy report object into the transient variables map. Can be null. */
062    public static final String COPY_REPORT_KEY = CreateContentByCopyFunction.class.getName() + "$copyReport";
063    
064    /** Constant for storing the name of metadata set to use for copy. */
065    public static final String COPY_METADATASET_NAME = CreateContentByCopyFunction.class.getName() + "$metadataSetName";
066    /** Constant for storing the name of fallback metadata set to use for copy. */
067    public static final String COPY_FALLBACK_METADATASET_NAME = CreateContentByCopyFunction.class.getName() + "$fallbackMetadataSetName";
068    /** Constant for storing the type of metadata set to use for copy. */
069    public static final String COPY_METADATASET_TYPE = CreateContentByCopyFunction.class.getName() + "$metadataSetType";
070
071    /** The metadata copy component */
072    protected CopyContentMetadataComponent _copyContentMetadataHelper;
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        _copyContentMetadataHelper = (CopyContentMetadataComponent) manager.lookup(CopyContentMetadataComponent.ROLE);
084        _cTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
085        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
086    }
087    
088    @SuppressWarnings("cast")
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    
141    /**
142     * Get the content
143     * @param transientVars The workflow transiant vars with BASE_CONTENT_KEY (the content itself) or BASE_CONTENT_ID (the id of the content)
144     * @return the content or null if the object cannot be found
145     */
146    protected Content getBaseContentForCopy (Map transientVars)
147    {
148        Content baseContent = (Content) transientVars.get(BASE_CONTENT_KEY);
149        
150        if (baseContent == null)
151        {
152            String baseContentId = (String) transientVars.get(BASE_CONTENT_ID);
153            
154            try
155            {
156                baseContent = _resolver.resolveById(baseContentId);
157            }
158            catch (UnknownAmetysObjectException e)
159            {
160                return null;
161            }
162        }
163        
164        return baseContent;
165    }
166
167    @Override
168    protected void _populateAdditionalData(Map transientVars, ModifiableContent content) throws WorkflowException
169    {
170        super._populateAdditionalData(transientVars, content);
171        
172        // This is where the duplication process is made. After the new content has been created.
173        Content baseContent = getBaseContentForCopy(transientVars);
174        
175        String metadataSetName = StringUtils.defaultIfBlank((String) transientVars.get(COPY_METADATASET_NAME), "default-edition");
176        String fallbackMetadataSetName = StringUtils.defaultIfBlank((String) transientVars.get(COPY_FALLBACK_METADATASET_NAME), "main");
177        String metadataSetType = StringUtils.defaultIfBlank((String) transientVars.get(COPY_METADATASET_TYPE), "edition");
178        
179        if (!transientVars.containsKey(COPY_REPORT_KEY))
180        {
181            transientVars.put(COPY_REPORT_KEY, new CopyReport(baseContent.getId(), _contentHelper.getTitle(baseContent), true, metadataSetName, fallbackMetadataSetName, metadataSetType, CopyMode.CREATION));
182        }
183        CopyReport copyReport = (CopyReport) transientVars.get(COPY_REPORT_KEY);
184        
185        Map<String, Object> copyMap = _buildCopyMap(transientVars, baseContent, metadataSetName, fallbackMetadataSetName, metadataSetType);
186        
187        _copyContentMetadataHelper.copyMetadataMap(baseContent, content, copyMap, copyReport);
188    }
189    
190    
191    /**
192     * Get the builded copy map
193     * @param transientVars the vars
194     * @param baseContent the base content to be copied
195     * @param metadataSetName the metadata set name
196     * @param fallbackMetadataSetName the fallbacl metadata set name, if required metadata set does not exist
197     * @param metadataSetType the metadata set type
198     * @return the builded copy map
199     */
200    @SuppressWarnings("unchecked")
201    protected Map<String, Object> _buildCopyMap(Map transientVars, Content baseContent, String metadataSetName, String fallbackMetadataSetName, String metadataSetType)
202    {
203        Map<String, Object> copyMap = (Map<String, Object>) transientVars.get(COPY_MAP_KEY);
204        if (copyMap == null)
205        {
206            copyMap = _copyContentMetadataHelper.buildCopyMap(baseContent, metadataSetName, fallbackMetadataSetName, metadataSetType);
207        }
208        
209        return copyMap;
210    }
211}