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