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.CopyContentMetadataComponent; 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 * 044 */ 045public class CopyContentClientInteraction extends AbstractLogEnabled implements Component, Serviceable 046{ 047 /** The ametys component that handle content copy */ 048 protected CopyContentMetadataComponent _copyContentComponent; 049 050 /** JSON helper */ 051 protected JSONUtils _jsonUtils; 052 053 @Override 054 public void service(ServiceManager sManager) throws ServiceException 055 { 056 _copyContentComponent = (CopyContentMetadataComponent) sManager.lookup(CopyContentMetadataComponent.ROLE); 057 _jsonUtils = (JSONUtils) sManager.lookup(JSONUtils.ROLE); 058 } 059 060 /** 061 * Creates a content by copy of another one.<br> 062 * Also handle the possible inner duplication depending on the duplication mode for each metadata of type "content". 063 * @param baseContentId The id of content to copy 064 * @param newContentTitle The title of content to create 065 * @param jsonMap The metadata to copy as a JSON string 066 * @param metadataSetNameToCopy The metadata set name to copy. Can be null 067 * @param fallbackMetadataSetNameToCopy The fallback metadata set name to use if 'metadataSetNameToCopy' does not exist. Can be null 068 * @param metadataSetTypeToCopy The metadata set type to copy. Can be null 069 * @param initActionId The init workflow action id for copy 070 * @param editActionId The workflow action for editing content 071 * @return the copy result 072 * @throws Exception if an error occurred during copy 073 */ 074 @Callable 075 public Map<String, Object> createContentByCopy(String baseContentId, String newContentTitle, String jsonMap, String metadataSetNameToCopy, String fallbackMetadataSetNameToCopy, String metadataSetTypeToCopy, int initActionId, int editActionId) throws Exception 076 { 077 return createContentByCopy(baseContentId, newContentTitle, null, jsonMap, metadataSetNameToCopy, fallbackMetadataSetNameToCopy, metadataSetTypeToCopy, 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 metadata 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 metadata to copy as a JSON string 086 * @param metadataSetNameToCopy The metadata set name to copy. Can be null 087 * @param fallbackMetadataSetNameToCopy The fallback metadata set name to use if 'metadataSetNameToCopy' does not exist. Can be null 088 * @param metadataSetTypeToCopy The metadata set type 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 @Callable 096 public Map<String, Object> createContentByCopy(String baseContentId, String newContentTitle, String targetContentType, String jsonMap, String metadataSetNameToCopy, String fallbackMetadataSetNameToCopy, String metadataSetTypeToCopy, int initActionId, int editActionId) throws Exception 097 { 098 Map<String, Object> result = new HashMap<> (); 099 100 if (StringUtils.isEmpty(baseContentId)) 101 { 102 throw new IllegalArgumentException("The id of content to copy parameter is mandatory."); 103 } 104 105 String metadataSetName = StringUtils.defaultIfEmpty(metadataSetNameToCopy, "default-edition"); 106 String fallbackMetadataSetName = StringUtils.defaultIfEmpty(fallbackMetadataSetNameToCopy, "main"); 107 String metadataSetType = StringUtils.defaultIfEmpty(metadataSetTypeToCopy, "edition"); 108 109 Map<String, Object> copyMap = jsonMap != null ? _jsonUtils.convertJsonToMap(jsonMap) : null; 110 111 CopyReport copyReport = _copyContentComponent.copyContent(baseContentId, newContentTitle, copyMap, metadataSetName, fallbackMetadataSetName, metadataSetType, targetContentType, initActionId, editActionId); 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 metadata to copy as a JSON string 130 * @param metadataSetNameToCopy The metadata set name to copy. Can be null. 131 * @param fallbackMetadataSetNameToCopy The fallback metadata set name if 'metadataSetName' is not found. Can be null. 132 * @param metadataSetTypeToCopy The metadata set type to copy. Can be null 133 * @return the copy result 134 * @throws Exception if an error occurred during copy 135 */ 136 @Callable 137 public Map<String, Object> editContentByCopy(String baseContentId, List<String> targetContentIds, String jsonMap, String metadataSetNameToCopy, String fallbackMetadataSetNameToCopy, String metadataSetTypeToCopy) throws Exception 138 { 139 Map<String, Object> result = new HashMap<> (); 140 141 if (StringUtils.isEmpty(baseContentId)) 142 { 143 throw new IllegalArgumentException("The id of content to copy parameter is mandatory."); 144 } 145 146 String metadataSetName = StringUtils.defaultIfEmpty(metadataSetNameToCopy, "default-edition"); 147 String fallbackMetadataSetName = StringUtils.defaultIfEmpty(fallbackMetadataSetNameToCopy, "main"); 148 String metadataSetType = StringUtils.defaultIfEmpty(metadataSetTypeToCopy, "edition"); 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, metadataSetName, fallbackMetadataSetName, metadataSetType); 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>> metadataErrors = 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("metadataErrors", metadataErrors); 187 188 for (CopyReport copyReport : copyReports) 189 { 190 _buildReport(copyReport, mainContent, createdContents, editedContents, failedContentCopies, metadataErrors, 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 metadataErrors The list of metadata 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>> metadataErrors) 206 { 207 _buildReport(copyReport, mainContent, createdContents, editedContents, failedContentCopies, metadataErrors, 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 metadataErrors The list of metadata 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>> metadataErrors, 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 } 258 259 // Metadata errors 260 for (I18nizableText metadataLabel : copyReport.getMetadataErrors()) 261 { 262 Map<String, Object> errorEntry = new HashMap<>(4); 263 metadataErrors.add(errorEntry); 264 265 errorEntry.put("label", metadataLabel); 266 errorEntry.put("contentId", copyReport.getTargetContentId()); 267 errorEntry.put("contentTitle", copyReport.getTargetContentTitle()); 268 errorEntry.put("contentIsReferenceTable", copyReport.getTargetContentIsReferenceTable()); 269 } 270 271 // Merge child reports. 272 for (CopyReport childReport : copyReport.getChildReports()) 273 { 274 _buildReport(childReport, mainContent, createdContents, editedContents, failedContentCopies, metadataErrors); 275 } 276 } 277 278 /** 279 * Retrieve the identifiers of the created contents through the copy report 280 * @param copyReport The report of the copy 281 * @return list of identifiers 282 */ 283 protected List<String> _getCreatedContentIds(CopyReport copyReport) 284 { 285 List<String> contentIds = new ArrayList<>(); 286 287 if (CopyState.SUCCESS.equals(copyReport.getStatus()) && CopyMode.CREATION.equals(copyReport.getMode())) 288 { 289 contentIds.add(copyReport.getTargetContentId()); 290 } 291 292 for (CopyReport childReport : copyReport.getChildReports()) 293 { 294 contentIds.addAll(_getCreatedContentIds(childReport)); 295 } 296 297 return contentIds; 298 } 299 300 /** 301 * Retrieve the identifiers of the created contents through the copy reports 302 * @param copyReports list of copy reports 303 * @return list of identifiers 304 */ 305 protected List<String> _getCreatedContentIds(List<CopyReport> copyReports) 306 { 307 List<String> contentIds = new ArrayList<>(); 308 309 for (CopyReport copyReport : copyReports) 310 { 311 contentIds.addAll(_getCreatedContentIds(copyReport)); 312 } 313 314 return contentIds; 315 } 316 317 /** 318 * Retrieve the identifiers of the edited contents through the copy reports 319 * @param copyReports list of copy reports 320 * @return list of identifiers 321 */ 322 protected List<String> _getEditedContentIds(List<CopyReport> copyReports) 323 { 324 List<String> contentIds = new ArrayList<>(); 325 326 // Edited content are only at the first level. 327 for (CopyReport copyReport : copyReports) 328 { 329 if (CopyState.SUCCESS.equals(copyReport.getStatus()) && CopyMode.EDITION.equals(copyReport.getMode())) 330 { 331 contentIds.add(copyReport.getTargetContentId()); 332 } 333 } 334 335 return contentIds; 336 } 337 338}