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