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