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.ArrayList; 019import java.util.Arrays; 020import java.util.HashMap; 021import java.util.LinkedList; 022import java.util.List; 023import java.util.Map; 024 025import org.apache.avalon.framework.component.Component; 026import org.apache.avalon.framework.logger.AbstractLogEnabled; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.avalon.framework.service.Serviceable; 030import org.apache.commons.lang.StringUtils; 031import org.apache.commons.lang3.BooleanUtils; 032 033import org.ametys.cms.content.CopyContentComponent; 034import org.ametys.cms.content.CopyReport; 035import org.ametys.cms.content.CopyReport.CopyMode; 036import org.ametys.cms.content.CopyReport.CopyState; 037import org.ametys.cms.rights.ContentRightAssignmentContext; 038import org.ametys.core.ui.Callable; 039import org.ametys.core.util.JSONUtils; 040import org.ametys.runtime.i18n.I18nizableText; 041 042/** 043 * Component for content copy 044 */ 045public class CopyContentClientInteraction extends AbstractLogEnabled implements Component, Serviceable 046{ 047 /** Helper for actually edit and copy contents */ 048 protected CopyContentComponent _copyContentComponent; 049 050 private JSONUtils _jsonUtils; 051 052 @Override 053 public void service(ServiceManager sManager) throws ServiceException 054 { 055 _copyContentComponent = (CopyContentComponent) sManager.lookup(CopyContentComponent.ROLE); 056 _jsonUtils = (JSONUtils) sManager.lookup(JSONUtils.ROLE); 057 } 058 059 /** 060 * Creates a content by copy of another one.<br> 061 * Also handle the possible inner duplication depending on the duplication mode for each attribute of type "content". 062 * @param baseContentId The id of content to copy 063 * @param newContentTitle The title of content to create 064 * @param jsonMap The attributes to copy as a JSON string 065 * @param viewNameToCopy The view name to copy. Can be null 066 * @param fallbackViewNameToCopy The fallback view name to use if 'viewNameToCopy' does not exist. Can be null 067 * @param viewModeToCopy The view mode to copy. Can be null 068 * @param initActionId The init workflow action id for copy 069 * @param editActionId The workflow action for editing content 070 * @return the copy result 071 * @throws Exception if an error occurred during copy 072 */ 073 // Only do read check on the source. The right check for creation will be done by the workflow 074 @Callable(rights = Callable.READ_ACCESS, rightContext = ContentRightAssignmentContext.ID, paramIndex = 0) 075 public Map<String, Object> createContentByCopy(String baseContentId, String newContentTitle, String jsonMap, String viewNameToCopy, String fallbackViewNameToCopy, String viewModeToCopy, int initActionId, int editActionId) throws Exception 076 { 077 return createContentByCopy(baseContentId, newContentTitle, null, jsonMap, viewNameToCopy, fallbackViewNameToCopy, viewModeToCopy, initActionId, editActionId); 078 } 079 080 /** 081 * Creates a content by copy of another one.<br> 082 * Also handle the possible inner duplication depending on the duplication mode for each attribute of type "content". 083 * @param baseContentId The id of content to copy 084 * @param newContentTitle The title of content to create 085 * @param jsonMap The attributes to copy as a JSON string 086 * @param viewNameToCopy The view name to copy. Can be null 087 * @param fallbackViewNameToCopy The fallback view name to use if 'viewNameToCopy' does not exist. Can be null 088 * @param viewModeToCopy The view mode to copy. Can be null 089 * @param targetContentType The type of content to create. If null the type(s) of created content will be those of base content. 090 * @param initActionId The workflow action id for copy to init the new content 091 * @param editActionId The workflow action id for copy to edit the newly created content 092 * @return the copy result 093 * @throws Exception if an error occurred during copy 094 */ 095 // Only do read check on the source. The right check for creation will be done by the workflow 096 @Callable(rights = Callable.READ_ACCESS, rightContext = ContentRightAssignmentContext.ID, paramIndex = 0) 097 public Map<String, Object> createContentByCopy(String baseContentId, String newContentTitle, String targetContentType, String jsonMap, String viewNameToCopy, String fallbackViewNameToCopy, String viewModeToCopy, int initActionId, int editActionId) throws Exception 098 { 099 Map<String, Object> result = new HashMap<> (); 100 101 if (StringUtils.isEmpty(baseContentId)) 102 { 103 throw new IllegalArgumentException("The id of content to copy parameter is mandatory."); 104 } 105 106 String viewName = StringUtils.defaultIfEmpty(viewNameToCopy, "default-edition"); 107 String fallbackViewName = StringUtils.defaultIfEmpty(fallbackViewNameToCopy, "main"); 108 109 Map<String, Object> copyMap = jsonMap != null ? _jsonUtils.convertJsonToMap(jsonMap) : null; 110 111 CopyReport copyReport = _copyContentComponent.copyContent(baseContentId, newContentTitle, copyMap, viewName, fallbackViewName, targetContentType, initActionId); 112 113 // CMS-5137 Open directly the main created content after copied 114 if (CopyState.SUCCESS.equals(copyReport.getStatus())) 115 { 116 result.put("mainContentId", copyReport.getTargetContentId()); 117 } 118 119 result.put("contentIds", _getCreatedContentIds(copyReport)); 120 result.put("report", _buildReport(Arrays.asList(copyReport))); 121 122 return result; 123 } 124 125 /** 126 * Edits contents by copied of another one.<br> 127 * @param baseContentId The id of content to copy 128 * @param targetContentIds The ids of content to edit 129 * @param jsonMap The attributes to copy as a JSON string 130 * @param viewNameToCopy The view name to copy. Can be null. 131 * @param fallbackViewNameToCopy The fallback view name if 'viewName' is not found. Can be null. 132 * @param viewModeToCopy The view mode to copy. Can be null 133 * @return the copy result 134 * @throws Exception if an error occurred during copy 135 */ 136 // Only do read check on the source. The right check for target edition will be done by the workflow 137 @Callable(rights = Callable.READ_ACCESS, rightContext = ContentRightAssignmentContext.ID, paramIndex = 0) 138 public Map<String, Object> editContentByCopy(String baseContentId, List<String> targetContentIds, String jsonMap, String viewNameToCopy, String fallbackViewNameToCopy, String viewModeToCopy) throws Exception 139 { 140 Map<String, Object> result = new HashMap<> (); 141 142 if (StringUtils.isEmpty(baseContentId)) 143 { 144 throw new IllegalArgumentException("The id of content to copy parameter is mandatory."); 145 } 146 147 String viewName = StringUtils.defaultIfEmpty(viewNameToCopy, "default-edition"); 148 String fallbackViewName = StringUtils.defaultIfEmpty(fallbackViewNameToCopy, "main"); 149 150 Map<String, Object> copyMap = jsonMap != null ? _jsonUtils.convertJsonToMap(jsonMap) : null; 151 152 List<CopyReport> copyReports = new LinkedList<>(); 153 for (String targetContentId : targetContentIds) 154 { 155 CopyReport copyReport = _copyContentComponent.editContent(baseContentId, targetContentId, copyMap, viewName, fallbackViewName); 156 copyReports.add(copyReport); 157 } 158 159 result.put("report", _buildReport(copyReports)); 160 result.put("createdContentIds", _getCreatedContentIds(copyReports)); 161 result.put("editedContentIds", _getEditedContentIds(copyReports)); 162 163 return result; 164 } 165 166 167 /** 168 * Build the copy report 169 * @param copyReports List of copy report 170 * @return The builded report 171 */ 172 protected Map<String, Object> _buildReport(List<CopyReport> copyReports) 173 { 174 Map<String, Object > reportMap = new HashMap<>(); 175 176 Map<String, Object> mainContent = new HashMap<>(); 177 List<Map<String, Object>> createdContents = new LinkedList<>(); 178 List<Map<String, Object>> editedContents = new LinkedList<>(); 179 List<Map<String, Object>> failedContentCopies = new LinkedList<>(); 180 List<Map<String, Object>> attributesErrors = new LinkedList<>(); 181 182 reportMap.put("mainContent", mainContent); 183 reportMap.put("createdContents", createdContents); 184 reportMap.put("editedContents", editedContents); 185 reportMap.put("failedContentCopies", failedContentCopies); 186 reportMap.put("attributesErrors", attributesErrors); 187 188 for (CopyReport copyReport : copyReports) 189 { 190 _buildReport(copyReport, mainContent, createdContents, editedContents, failedContentCopies, attributesErrors, true); 191 } 192 193 return reportMap; 194 } 195 196 /** 197 * Helper method used to build the report (rootLevel is false) 198 * @param copyReport The report of the copy 199 * @param mainContent The map that will receive the copy report informations during a creation at rootLevel. 200 * @param createdContents The list that will receive the copy report informations during a creation at another level. 201 * @param editedContents The list that will receive the copy report informations during something else than a creation 202 * @param failedContentCopies The list that will receive th copy report informations if an error occurred during the copy 203 * @param attributesErrors The list of attributes errors reported 204 */ 205 protected void _buildReport(CopyReport copyReport, Map<String, Object> mainContent, List<Map<String, Object>> createdContents, List<Map<String, Object>> editedContents, List<Map<String, Object>> failedContentCopies, List<Map<String, Object>> attributesErrors) 206 { 207 _buildReport(copyReport, mainContent, createdContents, editedContents, failedContentCopies, attributesErrors, false); 208 } 209 210 /** 211 * Helper method used to build the report 212 * @param copyReport The report of the copy 213 * @param mainContent The map that will receive the copy report informations during a creation at rootLevel. 214 * @param createdContents The list that will receive the copy report informations during a creation at another level. 215 * @param editedContents The list that will receive the copy report informations during something else than a creation 216 * @param failedContentCopies The list that will receive th copy report informations if an error occurred during the copy 217 * @param attributesErrors The list of attributes errors reported 218 * @param rootLevel True indicates the root level of the report. 219 */ 220 protected void _buildReport(CopyReport copyReport, Map<String, Object> mainContent, List<Map<String, Object>> createdContents, List<Map<String, Object>> editedContents, List<Map<String, Object>> failedContentCopies, List<Map<String, Object>> attributesErrors, boolean rootLevel) 221 { 222 // success entry 223 if (CopyState.SUCCESS.equals(copyReport.getStatus())) 224 { 225 Map<String, Object> contentEntry = new HashMap<>(3); 226 contentEntry.put("id", copyReport.getTargetContentId()); 227 contentEntry.put("title", copyReport.getTargetContentTitle()); 228 contentEntry.put("isReferenceTable", copyReport.getTargetContentIsReferenceTable()); 229 contentEntry.put("copiedAttachments", !copyReport.getCopiedAttachments().isEmpty()); 230 231 if (CopyMode.CREATION.equals(copyReport.getMode())) 232 { 233 if (rootLevel) 234 { 235 // CMS-5137 do not include main created content in the report. 236 mainContent.putAll(contentEntry); 237 } 238 else 239 { 240 createdContents.add(contentEntry); 241 } 242 } 243 else 244 { 245 editedContents.add(contentEntry); 246 } 247 } 248 else 249 { 250 // error entry 251 Map<String, Object> failedCopyEntry = new HashMap<>(3); 252 failedContentCopies.add(failedCopyEntry); 253 254 failedCopyEntry.put("id", copyReport.getBaseContentId()); 255 failedCopyEntry.put("title", copyReport.getBaseContentTitle()); 256 failedCopyEntry.put("isReferenceTable", BooleanUtils.isNotFalse(copyReport.getBaseContentIsReferenceTable())); 257 I18nizableText errorMessage = copyReport.getErrorMessage(); 258 if (errorMessage != null) 259 { 260 failedCopyEntry.put("error", errorMessage); 261 } 262 } 263 264 // Merge child reports. 265 for (CopyReport childReport : copyReport.getChildReports()) 266 { 267 _buildReport(childReport, mainContent, createdContents, editedContents, failedContentCopies, attributesErrors); 268 } 269 } 270 271 /** 272 * Retrieve the map of identifiers of base copied contents with the linked created contents through the copy report 273 * @param copyReport The report of the copy 274 * @return map of created content ids with their based content ids 275 */ 276 protected Map<String, String> _getCreatedContentIds(CopyReport copyReport) 277 { 278 Map<String, String> contentIds = new HashMap<>(); 279 280 if (CopyState.SUCCESS.equals(copyReport.getStatus()) && CopyMode.CREATION.equals(copyReport.getMode())) 281 { 282 contentIds.put(copyReport.getBaseContentId(), copyReport.getTargetContentId()); 283 } 284 285 for (CopyReport childReport : copyReport.getChildReports()) 286 { 287 contentIds.putAll(_getCreatedContentIds(childReport)); 288 } 289 290 return contentIds; 291 } 292 293 /** 294 * Retrieve the identifiers of the created contents through the copy reports 295 * @param copyReports list of copy reports 296 * @return list of identifiers 297 */ 298 protected List<String> _getCreatedContentIds(List<CopyReport> copyReports) 299 { 300 List<String> contentIds = new ArrayList<>(); 301 302 for (CopyReport copyReport : copyReports) 303 { 304 contentIds.addAll(_getCreatedContentIds(copyReport).values()); 305 } 306 307 return contentIds; 308 } 309 310 /** 311 * Retrieve the identifiers of the edited contents through the copy reports 312 * @param copyReports list of copy reports 313 * @return list of identifiers 314 */ 315 protected List<String> _getEditedContentIds(List<CopyReport> copyReports) 316 { 317 List<String> contentIds = new ArrayList<>(); 318 319 // Edited content are only at the first level. 320 for (CopyReport copyReport : copyReports) 321 { 322 if (CopyState.SUCCESS.equals(copyReport.getStatus()) && CopyMode.EDITION.equals(copyReport.getMode())) 323 { 324 contentIds.add(copyReport.getTargetContentId()); 325 } 326 } 327 328 return contentIds; 329 } 330}