001/* 002 * Copyright 2018 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.plugins.userdirectory; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.commons.lang3.StringUtils; 028import org.apache.commons.lang3.tuple.Pair; 029import org.slf4j.Logger; 030 031import org.ametys.cms.content.ContentHelper; 032import org.ametys.cms.repository.Content; 033import org.ametys.cms.repository.WorkflowAwareContent; 034import org.ametys.cms.workflow.ContentWorkflowHelper; 035import org.ametys.cms.workflow.EditContentFunction; 036import org.ametys.core.util.I18nUtils; 037import org.ametys.plugins.repository.data.holder.values.SynchronizableValue; 038import org.ametys.plugins.repository.data.holder.values.SynchronizableValue.Mode; 039import org.ametys.plugins.workflow.AbstractWorkflowComponent; 040import org.ametys.runtime.i18n.I18nizableText; 041 042import com.opensymphony.workflow.InvalidActionException; 043import com.opensymphony.workflow.WorkflowException; 044 045/** 046 * Delete orgunit component 047 */ 048public class DeleteOrgUnitComponent extends AbstractDeleteUDContentComponent 049{ 050 /** The avalon role. */ 051 public static final String ROLE = DeleteOrgUnitComponent.class.getName(); 052 053 /** The organisation chart page handler */ 054 protected OrganisationChartPageHandler _oCPageHandler; 055 056 /** The content helper */ 057 protected ContentHelper _contentHelper; 058 059 /** The i18n utils */ 060 protected I18nUtils _i18nUtils; 061 062 /** The content workflow helper */ 063 protected ContentWorkflowHelper _contentWorkflowHelper; 064 065 @Override 066 public void service(ServiceManager smanager) throws ServiceException 067 { 068 super.service(smanager); 069 _oCPageHandler = (OrganisationChartPageHandler) smanager.lookup(OrganisationChartPageHandler.ROLE); 070 _contentHelper = (ContentHelper) smanager.lookup(ContentHelper.ROLE); 071 _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE); 072 _contentWorkflowHelper = (ContentWorkflowHelper) smanager.lookup(ContentWorkflowHelper.ROLE); 073 } 074 075 @Override 076 public boolean isContentReferenced(Content content, Logger logger) 077 { 078 List<String> ignoreContentTypes = new ArrayList<>(); 079 ignoreContentTypes.add(OrganisationChartPageHandler.ORGUNIT_CONTENT_TYPE); 080 ignoreContentTypes.add(UserDirectoryPageHandler.ABSTRACT_USER_CONTENT_TYPE); 081 082 return _contentHelper.hasReferencingContents(content, ignoreContentTypes, true); 083 } 084 085 @Override 086 protected boolean _checkBeforeDeletion(Content content, Map<String, String> rights, Map<String, Object> results, Logger logger) 087 { 088 // Check right and lock on content it self 089 boolean allRight = _canDeleteContent(content, rights, results); 090 091 // Check lock on parent contents 092 allRight = _checkParentBeforeDeletion(content, results) && allRight; 093 094 // Check lock on users contents 095 allRight = _checkUsersBeforeDeletion(content, results) && allRight; 096 097 // Check right and lock on children to be deleted or modified 098 allRight = _checkChildrenBeforeDeletion(content, rights, results, logger) && allRight; 099 100 return allRight; 101 } 102 103 /** 104 * True if the parent content is not locked 105 * @param content the content 106 * @param results the results map 107 * @return true if the parent content is not locked 108 */ 109 protected boolean _checkParentBeforeDeletion(Content content, Map<String, Object> results) 110 { 111 boolean allRight = true; 112 113 // Check if parents are not locked 114 Content parentContent = _oCPageHandler.getParentContent(content); 115 if (_isLocked(parentContent)) 116 { 117 @SuppressWarnings("unchecked") 118 List<Content> lockedContents = (List<Content>) results.get("locked-contents"); 119 lockedContents.add(parentContent); 120 121 allRight = false; 122 } 123 124 return allRight; 125 } 126 127 /** 128 * True if the users content is not locked 129 * @param content the content 130 * @param results the results map 131 * @return true if the parent content is not locked 132 */ 133 protected boolean _checkUsersBeforeDeletion(Content content, Map<String, Object> results) 134 { 135 boolean allRight = true; 136 137 // Check if users are not locked 138 for (Content user : _oCPageHandler.getUserContents(content)) 139 { 140 if (_isLocked(user)) 141 { 142 @SuppressWarnings("unchecked") 143 List<Content> lockedContents = (List<Content>) results.get("locked-contents"); 144 lockedContents.add(user); 145 146 allRight = false; 147 } 148 } 149 150 return allRight; 151 } 152 153 /** 154 * Browse children to check if deletion could succeed 155 * @param contentToCheck The current content to check 156 * @param results The result 157 * @param rights the map of rights id with its prefix 158 * @param logger The logger 159 * @return true if the deletion can be processed 160 */ 161 protected boolean _checkChildrenBeforeDeletion(Content contentToCheck, Map<String, String> rights, Map<String, Object> results, Logger logger) 162 { 163 boolean allRight = true; 164 165 List<Content> childOrgUnits = _oCPageHandler.getChildContents(contentToCheck); 166 for (Content childOrgUnit : childOrgUnits) 167 { 168 if (!_canDeleteContent(childOrgUnit, rights, results)) 169 { 170 allRight = false; 171 } 172 else if (isContentReferenced(childOrgUnit, logger)) 173 { 174 @SuppressWarnings("unchecked") 175 List<Content> referencedContents = (List<Content>) results.get("referenced-contents"); 176 referencedContents.add(childOrgUnit); 177 178 allRight = false; 179 } 180 else 181 { 182 // Browse children recursively 183 allRight = _checkChildrenBeforeDeletion(childOrgUnit, rights, results, logger) && allRight; 184 } 185 } 186 187 return allRight; 188 } 189 190 @Override 191 protected boolean _removeRelations(Content orgUnit, Logger logger) 192 { 193 boolean success = true; 194 195 // Delete relation to parent and users 196 List<Pair<String, Content>> incomingReferences = _contentHelper.getReferencingContents(orgUnit); 197 for (Pair<String, Content> reference : incomingReferences) 198 { 199 Content refContent = reference.getRight(); 200 String valuePath = reference.getLeft(); 201 202 try 203 { 204 // Ignore child orgunits because they are going to be deleted 205 if (valuePath.equals(OrganisationChartPageHandler.CHILD_ORGUNIT_ATTRIBUTE_NAME)) 206 { 207 I18nizableText commentText = new I18nizableText("plugin.user-directory", "PLUGINS_USER_DIRECTORY_WORKFLOW_ACTION_REMOVE_ORGUNIT_REFERENCE_MSG"); 208 String comment = _i18nUtils.translate(commentText, refContent.getLanguage()); 209 210 _removeRelation((WorkflowAwareContent) refContent, valuePath, orgUnit, comment); 211 } 212 else if (valuePath.equals(UserDirectoryHelper.ORGUNITS_ATTRIBUTE)) 213 { 214 I18nizableText commentText = new I18nizableText("plugin.user-directory", "PLUGINS_USER_DIRECTORY_WORKFLOW_ACTION_REMOVE_USER_REFERENCE_MSG"); 215 String comment = _i18nUtils.translate(commentText, refContent.getLanguage()); 216 217 _removeRelation((WorkflowAwareContent) refContent, valuePath, orgUnit, comment); 218 } 219 } 220 catch (WorkflowException | InvalidActionException e) 221 { 222 logger.error("Unable to remove relation to content \"{}\" ({}) for referencing content \"{}\" ({}) ", orgUnit.getTitle(), orgUnit.getId(), refContent.getTitle(), refContent.getId(), e); 223 success = false; 224 } 225 } 226 227 return success; 228 } 229 230 private void _removeRelation(WorkflowAwareContent content, String attribute, Content orgUnit, String comment) throws WorkflowException 231 { 232 SynchronizableValue value = new SynchronizableValue(List.of(orgUnit.getId())); 233 value.setMode(Mode.REMOVE); 234 Map<String, Object> values = Map.of(attribute, value); 235 236 Map<String, Object> inputs = new HashMap<>(); 237 if (StringUtils.isNotEmpty(comment)) 238 { 239 inputs.put("comment", comment); 240 } 241 242 Map<String, Object> parameters = new HashMap<>(); 243 244 parameters.put(EditContentFunction.VALUES_KEY, values); 245 parameters.put(EditContentFunction.QUIT, true); 246 inputs.put(AbstractWorkflowComponent.CONTEXT_PARAMETERS_KEY, parameters); 247 248 _contentWorkflowHelper.doAction(content, _removeReferenceActionId, inputs); 249 } 250 251 252 @Override 253 protected Set<String> _getContentIdsToDelete(Content content, Map<String, String> rights, Map<String, Object> results, Logger logger) 254 { 255 Set<String> toDelete = new HashSet<>(); 256 257 if (_canDeleteContent(content, rights, results)) 258 { 259 toDelete.add(content.getId()); 260 261 for (Content childOrgUnit : _oCPageHandler.getChildContents(content)) 262 { 263 if (!isContentReferenced(childOrgUnit, logger) && _removeUsersRelation(childOrgUnit, logger)) 264 { 265 toDelete.addAll(_getContentIdsToDelete(childOrgUnit, rights, results, logger)); 266 } 267 else 268 { 269 // The child program item can not be deleted, remove the relation to the parent and stop iteration 270 @SuppressWarnings("unchecked") 271 List<Content> referencedContents = (List<Content>) results.get("referenced-contents"); 272 referencedContents.add(childOrgUnit); 273 } 274 } 275 } 276 277 return toDelete; 278 } 279 280 /** 281 * Remove users relation 282 * @param orgUnit the orgunit content 283 * @param logger The logger 284 * @return true if relations have been removed 285 */ 286 protected boolean _removeUsersRelation(Content orgUnit, Logger logger) 287 { 288 boolean success = true; 289 290 // Delete relation users 291 List<Pair<String, Content>> incomingReferences = _contentHelper.getReferencingContents(orgUnit); 292 for (Pair<String, Content> refPair : incomingReferences) 293 { 294 Content refContent = refPair.getValue(); 295 String valuePath = refPair.getKey(); 296 297 try 298 { 299 // Just remove when path is from users 300 if (valuePath.equals(UserDirectoryHelper.ORGUNITS_ATTRIBUTE)) 301 { 302 I18nizableText commentText = new I18nizableText("plugin.user-directory", "PLUGINS_USER_DIRECTORY_WORKFLOW_ACTION_REMOVE_REFERENCE_MSG"); 303 String comment = _i18nUtils.translate(commentText, refContent.getLanguage()); 304 305 _removeRelation((WorkflowAwareContent) refContent, valuePath, orgUnit, comment); 306 } 307 } 308 catch (WorkflowException | InvalidActionException e) 309 { 310 logger.error("Unable to remove relations to content \"" + orgUnit.getTitle() + " (" + orgUnit.getId() + ")", e); 311 success = false; 312 } 313 } 314 315 return success; 316 } 317}