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