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 type of metadata set to use for copy. */
067    public static final String COPY_METADATASET_TYPE = CreateContentByCopyFunction.class.getName() + "$metadataSetType";
068
069    /** The metadata copy component */
070    protected CopyContentMetadataComponent _copyContentMetadataHelper;
071    /** The content type helper */
072    protected ContentTypesHelper _cTypesHelper;
073
074    /** I18n Utils */
075    protected I18nUtils _i18nUtils;
076
077    @Override
078    public void service(ServiceManager manager) throws ServiceException
079    {
080        super.service(manager);
081        _copyContentMetadataHelper = (CopyContentMetadataComponent) manager.lookup(CopyContentMetadataComponent.ROLE);
082        _cTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
083        _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE);
084    }
085    
086    @SuppressWarnings("cast")
087    @Override
088    public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException
089    {
090        // Preparation of the transientVars to work with the super method (CreateContentFunction).
091        Content baseContent = getBaseContentForCopy(transientVars);
092        
093        if (baseContent == null)
094        {
095            throw new WorkflowException("Unable to retrieve the base content for the duplication.");
096        }
097        
098        if (transientVars.get(CreateContentFunction.CONTENT_TITLE_KEY) == null)
099        {
100            if (_contentHelper.isMultilingual(baseContent))
101            {
102                Map<String, String> titleVariants = new HashMap<>();
103                Map<String, String> baseTitleVariants = _contentHelper.getTitleVariants(baseContent);
104                
105                for (Entry<String, String> entry : baseTitleVariants.entrySet())
106                {
107                    titleVariants.put(entry.getKey(), entry.getValue() + _i18nUtils.translate(new I18nizableText("plugin.cms", "CONTENT_COPY_CREATE_CONTENT_TITLE_SUFFIX")));
108                }
109                
110                String defaultLang = (String) transientVars.get(CreateContentFunction.CONTENT_LANGUAGE_KEY);
111                Locale defaultLocale = StringUtils.isNotEmpty(defaultLang) ? new Locale(defaultLang) : new Locale(titleVariants.keySet().iterator().next());
112                transientVars.put(CreateContentFunction.CONTENT_NAME_KEY, baseContent.getTitle(defaultLocale));
113                transientVars.put(CreateContentFunction.CONTENT_TITLE_VARIANTS_KEY, titleVariants);
114            }
115            else
116            {
117                transientVars.put(CreateContentFunction.CONTENT_NAME_KEY, baseContent.getTitle(null));
118                // If title is not set, the base title is set with a copy suffix. 
119                transientVars.put(CreateContentFunction.CONTENT_TITLE_KEY, baseContent.getTitle(null) + _i18nUtils.translate(new I18nizableText("plugin.cms", "CONTENT_COPY_CREATE_CONTENT_TITLE_SUFFIX")));
120            }
121        }
122        else
123        {
124            transientVars.put(CreateContentFunction.CONTENT_NAME_KEY, (String) transientVars.get(CreateContentFunction.CONTENT_TITLE_KEY));
125        }
126        
127        if (transientVars.get(CreateContentFunction.CONTENT_TYPES_KEY) == null)
128        {
129            transientVars.put(CreateContentFunction.CONTENT_TYPES_KEY, baseContent.getTypes());
130        }
131        
132        transientVars.put(CreateContentFunction.CONTENT_MIXINS_KEY, baseContent.getMixinTypes());
133        transientVars.put(CreateContentFunction.CONTENT_LANGUAGE_KEY, baseContent.getLanguage());
134        
135        // Super method call.
136        super.execute(transientVars, args, ps);
137    }
138    
139    /**
140     * Get the content
141     * @param transientVars The workflow transiant vars with BASE_CONTENT_KEY (the content itself) or BASE_CONTENT_ID (the id of the content)
142     * @return the content or null if the object cannot be found
143     */
144    protected Content getBaseContentForCopy (Map transientVars)
145    {
146        Content baseContent = (Content) transientVars.get(BASE_CONTENT_KEY);
147        
148        if (baseContent == null)
149        {
150            String baseContentId = (String) transientVars.get(BASE_CONTENT_ID);
151            
152            try
153            {
154                baseContent = _resolver.resolveById(baseContentId);
155            }
156            catch (UnknownAmetysObjectException e)
157            {
158                return null;
159            }
160        }
161        
162        return baseContent;
163    }
164
165    @Override
166    protected void _populateAdditionalData(Map transientVars, ModifiableContent content) throws WorkflowException
167    {
168        super._populateAdditionalData(transientVars, content);
169        
170        // This is where the duplication process is made. After the new content has been created.
171        Content baseContent = getBaseContentForCopy(transientVars);
172        
173        String metadataSetName = (String) transientVars.get(COPY_METADATASET_NAME);
174        String metadataSetType = (String) transientVars.get(COPY_METADATASET_TYPE);
175        
176        if (!transientVars.containsKey(COPY_REPORT_KEY))
177        {
178            transientVars.put(COPY_REPORT_KEY, new CopyReport(baseContent.getId(), _contentHelper.getTitle(baseContent), true, metadataSetName != null ? metadataSetName : "main", metadataSetType != null ? metadataSetType : "edition", CopyMode.CREATION));
179        }
180        CopyReport copyReport = (CopyReport) transientVars.get(COPY_REPORT_KEY);
181        
182        Map<String, Object> copyMap = _buildCopyMap(transientVars, baseContent, metadataSetName, metadataSetType);
183        
184        _copyContentMetadataHelper.copyMetadataMap(baseContent, content, copyMap, copyReport);
185    }
186    
187    
188    /**
189     * Get the builded copy map
190     * @param transientVars the vars
191     * @param baseContent the base content to be copied
192     * @param metadataSetName the metadata set name
193     * @param metadataSetType the metadata set type
194     * @return the builded copy map
195     */
196    @SuppressWarnings("unchecked")
197    protected Map<String, Object> _buildCopyMap(Map transientVars, Content baseContent, String metadataSetName, String metadataSetType)
198    {
199        Map<String, Object> copyMap = (Map<String, Object>) transientVars.get(COPY_MAP_KEY);
200        if (copyMap == null)
201        {
202            copyMap = _copyContentMetadataHelper.buildCopyMap(baseContent, metadataSetName != null ? metadataSetName : "main", metadataSetType != null ? metadataSetType : "edition");
203        }
204        
205        return copyMap;
206    }
207}