001/*
002 *  Copyright 2015 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.odf.program;
017
018import java.util.HashMap;
019import java.util.Map;
020import java.util.Map.Entry;
021
022import org.apache.avalon.framework.component.Component;
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.avalon.framework.service.Serviceable;
026
027import org.ametys.cms.ObservationConstants;
028import org.ametys.cms.repository.ModifiableContent;
029import org.ametys.cms.repository.WorkflowAwareContent;
030import org.ametys.cms.workflow.ContentWorkflowHelper;
031import org.ametys.core.observation.Event;
032import org.ametys.core.observation.ObservationManager;
033import org.ametys.core.ui.Callable;
034import org.ametys.core.user.CurrentUserProvider;
035import org.ametys.core.user.UserIdentity;
036import org.ametys.odf.ODFHelper;
037import org.ametys.odf.observation.OdfObservationConstants;
038import org.ametys.odf.translation.TranslationHelper;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.plugins.repository.UnknownAmetysObjectException;
041
042/**
043 * DAO for manipulating ODF programs.
044 *
045 */
046public class ProgramDAO implements Serviceable, Component
047{
048    /** The Avalon role */
049    public static final String ROLE = ProgramDAO.class.getName();
050    
051    /** The ametys object resolver */
052    protected AmetysObjectResolver _resolver;
053    /** Observer manager. */
054    protected ObservationManager _observationManager;
055    /** The current user provider. */
056    protected CurrentUserProvider _currentUserProvider;
057    /** ODF helper */
058    protected ODFHelper _odfHelper;
059    /** The content workflow helper */
060    protected ContentWorkflowHelper _contentWorkflowHelper;
061    
062    @Override
063    public void service(ServiceManager manager) throws ServiceException
064    {
065        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
066        _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE);
067        _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE);
068        _odfHelper = (ODFHelper) manager.lookup(ODFHelper.ROLE);
069        _contentWorkflowHelper = (ContentWorkflowHelper) manager.lookup(ContentWorkflowHelper.ROLE);
070    }
071    
072    /**
073     * Translates a given program in a language
074     * @param contentId The program id
075     * @param language The language in which to translate
076     * @param fullCopy True if full copy
077     * @return A Map with id of translated program or an error message
078     * @throws Exception if an error occurs
079     */
080    @Callable
081    public Map<String, String> translateProgram (String contentId, String language, boolean fullCopy) throws Exception
082    {
083        Map<String, String> result = new HashMap<>();
084        
085        Program program = _resolver.resolveById(contentId);
086        
087        if (program.getLanguage().equals(language))
088        {
089            result.put("error", "same-language");
090            return result;
091        }
092        
093        Map<String, String> translatedContents = new HashMap<>();
094        
095        String translatedProgramId = null;
096        
097        // Get existing translations
098        Map<String, String> translations = TranslationHelper.getTranslations(program);
099        
100        if (!translations.containsKey(language))
101        {
102            ModifiableContent translatedProgram = _odfHelper.copyProgramItem(program, null, language, null, fullCopy, translatedContents, translatedContents, translatedContents, translatedContents, translatedContents, translatedContents);
103            translatedProgramId = translatedProgram.getId();
104        }
105        else
106        {
107            translatedProgramId = translations.get(language);
108            // Check if content exists
109            try
110            {
111                _resolver.resolveById(translatedProgramId);
112                result.put("error", "already-exists");
113            }
114            catch (UnknownAmetysObjectException e) 
115            {
116                ModifiableContent translatedProgram = _odfHelper.copyProgramItem(program, null, language, null, fullCopy, translatedContents, translatedContents, translatedContents, translatedContents, translatedContents, translatedContents);
117                translatedProgramId = translatedProgram.getId();
118            }
119        }
120        
121        linkTranslations(translatedContents);
122        
123        // Add the workflow step, send notifications and extract outgoing references
124        for (String newContentId : translatedContents.values())
125        {
126            WorkflowAwareContent newContent = _resolver.resolveById(newContentId);
127            _contentWorkflowHelper.doAction(newContent, getCopyActionId());
128        }
129        
130        result.put("translated-program-id", translatedProgramId);
131        
132        return result;
133    }
134    
135    /**
136     * Store links to the other translations for all copied contents
137     * @param translatedContents The translated contents
138     */
139    protected void linkTranslations(Map<String, String> translatedContents)
140    {
141        for (Entry<String, String> entry : translatedContents.entrySet())
142        {
143            ModifiableContent originalContent = _resolver.resolveById(entry.getKey());
144            ModifiableContent translatedContent = _resolver.resolveById(entry.getValue());
145            
146            linkTranslations(originalContent, translatedContent);
147            
148            originalContent.saveChanges();
149            translatedContent.saveChanges();
150            
151            Map<String, Object> eventParams = new HashMap<>();
152            eventParams.put(ObservationConstants.ARGS_CONTENT, originalContent);
153            
154            _observationManager.notify(new Event(OdfObservationConstants.ODF_CONTENT_TRANSLATED, _getCurrentUser(), eventParams));
155        }
156    }
157    
158    /**
159     * Store links to the other translations in all the translated objects.
160     * @param originalContent The original content
161     * @param translatedContent The translated content
162     */
163    protected void linkTranslations(ModifiableContent originalContent, ModifiableContent translatedContent)
164    {
165        Map<String, String> translations = TranslationHelper.getTranslations(originalContent);
166        
167        // Add the original and translated references.
168        translations.put(originalContent.getLanguage(), originalContent.getId());
169        translations.put(translatedContent.getLanguage(), translatedContent.getId());
170        
171        for (String contentId : translations.values())
172        {
173            ModifiableContent content = _resolver.resolveById(contentId);
174            
175            Map<String, String> otherTranslations = new HashMap<>(translations);
176            otherTranslations.remove(content.getLanguage());
177            
178            TranslationHelper.setTranslations(content, otherTranslations);
179        }
180    }
181    
182    /**
183     * Provides the current user.
184     * @return the identity which cannot be <code>null</code>.
185     */
186    protected UserIdentity _getCurrentUser()
187    {
188        return _currentUserProvider.getUser();
189    }
190    
191    /**
192     * Get the workflow action id for copy.
193     * @return The workflow action id
194     */
195    protected int getCopyActionId()
196    {
197        return 210;
198    }
199}