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 metadataSetTypeToCopy The metadata set type 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 @Callable 074 public Map<String, Object> createContentByCopy(String baseContentId, String newContentTitle, String jsonMap, String metadataSetNameToCopy, String metadataSetTypeToCopy, int initActionId, int editActionId) throws Exception 075 { 076 return createContentByCopy(baseContentId, newContentTitle, null, jsonMap, metadataSetNameToCopy, metadataSetTypeToCopy, initActionId, editActionId); 077 } 078 079 /** 080 * Creates a content by copy of another one.<br> 081 * Also handle the possible inner duplication depending on the duplication mode for each metadata of type "content". 082 * @param baseContentId The id of content to copy 083 * @param newContentTitle The title of content to create 084 * @param jsonMap The metadata to copy as a JSON string 085 * @param metadataSetNameToCopy The metadata set name to copy. Can be null 086 * @param metadataSetTypeToCopy The metadata set type 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 metadataSetNameToCopy, String metadataSetTypeToCopy, 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 metadataSetName = StringUtils.defaultIfEmpty(metadataSetNameToCopy, "main"); 104 String metadataSetType = StringUtils.defaultIfEmpty(metadataSetTypeToCopy, "edition"); 105 106 Map<String, Object> copyMap = jsonMap != null ? _jsonUtils.convertJsonToMap(jsonMap) : null; 107 108 CopyReport copyReport = _copyContentComponent.copyContent(baseContentId, newContentTitle, copyMap, metadataSetName, metadataSetType, targetContentType, initActionId, editActionId); 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 metadata to copy as a JSON string 127 * @param metadataSetNameToCopy The metadata set name to copy. Can be null 128 * @param metadataSetTypeToCopy The metadata set type 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 metadataSetNameToCopy, String metadataSetTypeToCopy) 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 metadataSetName = StringUtils.defaultIfEmpty(metadataSetNameToCopy, "main"); 143 String metadataSetType = StringUtils.defaultIfEmpty(metadataSetTypeToCopy, "edition"); 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, metadataSetName, metadataSetType); 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>> metadataErrors = 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("metadataErrors", metadataErrors); 182 183 for (CopyReport copyReport : copyReports) 184 { 185 _buildReport(copyReport, mainContent, createdContents, editedContents, failedContentCopies, metadataErrors, 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 metadataErrors The list of metadata 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>> metadataErrors) 201 { 202 _buildReport(copyReport, mainContent, createdContents, editedContents, failedContentCopies, metadataErrors, 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 metadataErrors The list of metadata 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>> metadataErrors, 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 // Metadata errors 255 for (I18nizableText metadataLabel : copyReport.getMetadataErrors()) 256 { 257 Map<String, Object> errorEntry = new HashMap<>(4); 258 metadataErrors.add(errorEntry); 259 260 errorEntry.put("label", metadataLabel); 261 errorEntry.put("contentId", copyReport.getTargetContentId()); 262 errorEntry.put("contentTitle", copyReport.getTargetContentTitle()); 263 errorEntry.put("contentIsReferenceTable", copyReport.getTargetContentIsReferenceTable()); 264 } 265 266 // Merge child reports. 267 for (CopyReport childReport : copyReport.getChildReports()) 268 { 269 _buildReport(childReport, mainContent, createdContents, editedContents, failedContentCopies, metadataErrors); 270 } 271 } 272 273 /** 274 * Retrieve the identifiers of the created contents through the copy report 275 * @param copyReport The report of the copy 276 * @return list of identifiers 277 */ 278 protected List<String> _getCreatedContentIds(CopyReport copyReport) 279 { 280 List<String> contentIds = new ArrayList<>(); 281 282 if (CopyState.SUCCESS.equals(copyReport.getStatus()) && CopyMode.CREATION.equals(copyReport.getMode())) 283 { 284 contentIds.add(copyReport.getTargetContentId()); 285 } 286 287 for (CopyReport childReport : copyReport.getChildReports()) 288 { 289 contentIds.addAll(_getCreatedContentIds(childReport)); 290 } 291 292 return contentIds; 293 } 294 295 /** 296 * Retrieve the identifiers of the created contents through the copy reports 297 * @param copyReports list of copy reports 298 * @return list of identifiers 299 */ 300 protected List<String> _getCreatedContentIds(List<CopyReport> copyReports) 301 { 302 List<String> contentIds = new ArrayList<>(); 303 304 for (CopyReport copyReport : copyReports) 305 { 306 contentIds.addAll(_getCreatedContentIds(copyReport)); 307 } 308 309 return contentIds; 310 } 311 312 /** 313 * Retrieve the identifiers of the edited contents through the copy reports 314 * @param copyReports list of copy reports 315 * @return list of identifiers 316 */ 317 protected List<String> _getEditedContentIds(List<CopyReport> copyReports) 318 { 319 List<String> contentIds = new ArrayList<>(); 320 321 // Edited content are only at the first level. 322 for (CopyReport copyReport : copyReports) 323 { 324 if (CopyState.SUCCESS.equals(copyReport.getStatus()) && CopyMode.EDITION.equals(copyReport.getMode())) 325 { 326 contentIds.add(copyReport.getTargetContentId()); 327 } 328 } 329 330 return contentIds; 331 } 332 333}