001/* Copyright 2018 Anyware Services 002 * 003 * Licensed under the Apache License, Version 2.0 (the "License"); 004 * you may not use this file except in compliance with the License. 005 * You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software 010 * distributed under the License is distributed on an "AS IS" BASIS, 011 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 * See the License for the specific language governing permissions and 013 * limitations under the License. 014 */ 015package org.ametys.cms.content.referencetable; 016 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Map; 024import java.util.Map.Entry; 025import java.util.Set; 026 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.cocoon.components.ContextHelper; 030import org.apache.cocoon.environment.Request; 031 032import org.ametys.cms.repository.Content; 033import org.ametys.cms.repository.ContentDAO; 034import org.ametys.core.observation.Event; 035import org.ametys.core.ui.Callable; 036import org.ametys.plugins.repository.AmetysObjectIterable; 037import org.ametys.plugins.repository.trash.TrashElement; 038 039/** 040 * DAO deleting a content from a hierarchical reference table reference table 041 * 042 */ 043public class HierarchicalReferenceTablesDeleteContentDAO extends ContentDAO 044{ 045 /** The request attribute name to list all children contents to delete */ 046 protected static final String _REQUEST_ATTRIBUTE_CONTENT_IDS = "ContentIdsToDelete"; 047 048 /** The helper component for hierarchical reference tables */ 049 protected HierarchicalReferenceTablesHelper _hierarchicalReferenceTablesHelper; 050 051 @Override 052 public void service(ServiceManager manager) throws ServiceException 053 { 054 super.service(manager); 055 _hierarchicalReferenceTablesHelper = (HierarchicalReferenceTablesHelper) manager.lookup(HierarchicalReferenceTablesHelper.ROLE); 056 } 057 058 @Override 059 protected String getRightToDelete() 060 { 061 return "CMS_Rights_ReferenceTables_Delete"; 062 } 063 064 @Override 065 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 066 public Map<String, Object> forceDeleteContents(List<String> contentsId) 067 { 068 Request request = ContextHelper.getRequest(_context); 069 070 List<String> contentsIdToDelete = new ArrayList<>(contentsId); 071 for (String contentId : contentsId) 072 { 073 Content content = _resolver.resolveById(contentId); 074 contentsIdToDelete.addAll(_hierarchicalReferenceTablesHelper.getAllChildren(content)); 075 } 076 077 request.setAttribute(_REQUEST_ATTRIBUTE_CONTENT_IDS, contentsIdToDelete); 078 Map<String, Object> returnMap = super.forceDeleteContents(contentsIdToDelete); 079 request.removeAttribute(_REQUEST_ATTRIBUTE_CONTENT_IDS); 080 081 return returnMap; 082 } 083 084 @Override 085 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 086 public Map<String, Object> deleteContents(List<String> contentsId) 087 { 088 Request request = ContextHelper.getRequest(_context); 089 090 List<String> contentsIdToDelete = new ArrayList<>(contentsId); 091 for (String contentId : contentsId) 092 { 093 Content content = _resolver.resolveById(contentId); 094 contentsIdToDelete.addAll(_hierarchicalReferenceTablesHelper.getAllChildren(content)); 095 } 096 097 request.setAttribute(_REQUEST_ATTRIBUTE_CONTENT_IDS, contentsIdToDelete); 098 Map<String, Object> returnMap = super.deleteContents(contentsIdToDelete); 099 request.removeAttribute(_REQUEST_ATTRIBUTE_CONTENT_IDS); 100 101 return returnMap; 102 } 103 104 @Override 105 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 106 public Map<String, Object> forceTrashContents(List<String> contentsId) 107 { 108 Request request = ContextHelper.getRequest(_context); 109 110 List<String> contentsIdToDelete = new ArrayList<>(contentsId); 111 for (String contentId : contentsId) 112 { 113 Content content = _resolver.resolveById(contentId); 114 contentsIdToDelete.addAll(_hierarchicalReferenceTablesHelper.getAllChildren(content)); 115 } 116 117 request.setAttribute(_REQUEST_ATTRIBUTE_CONTENT_IDS, contentsIdToDelete); 118 Map<String, Object> returnMap = super.forceTrashContents(contentsIdToDelete); 119 request.removeAttribute(_REQUEST_ATTRIBUTE_CONTENT_IDS); 120 121 return returnMap; 122 } 123 124 @Override 125 @Callable(rights = Callable.CHECKED_BY_IMPLEMENTATION) 126 public Map<String, Object> trashContents(List<String> contentsId) 127 { 128 Request request = ContextHelper.getRequest(_context); 129 130 // Compute the hierarchy to be able to edit the trash element accordingly 131 Map<String, Collection<String>> hierarchy = new HashMap<>(); 132 for (String id : contentsId) 133 { 134 Content content = _resolver.resolveById(id); 135 hierarchy.putAll(_getHierarchy(content)); 136 } 137 138 // flatten the hierarchy 139 List<String> contentsIdToDelete = _flattenHierarchy(hierarchy); 140 141 request.setAttribute(_REQUEST_ATTRIBUTE_CONTENT_IDS, contentsIdToDelete); 142 Map<String, Object> returnMap = super.trashContents(contentsIdToDelete); 143 request.removeAttribute(_REQUEST_ATTRIBUTE_CONTENT_IDS); 144 145 for (String deletedId : contentsIdToDelete) 146 { 147 TrashElement trashElement = _trashElementDAO.find(deletedId); 148 if (trashElement != null) 149 { 150 // add linked objects to trash element based on the previously computed hierarchy 151 Collection<String> linkedObjectsIds = hierarchy.get(deletedId); 152 if (!linkedObjectsIds.isEmpty()) 153 { 154 trashElement.addLinkedObjects(linkedObjectsIds.toArray(String[]::new)); 155 } 156 157 // hide the element if it's deletion was not requested by the user 158 if (!contentsId.contains(deletedId)) 159 { 160 trashElement.setHidden(true); 161 } 162 163 if (trashElement.needsSave()) 164 { 165 trashElement.saveChanges(); 166 167 // Notify observers 168 Map<String, Object> eventParams = new HashMap<>(); 169 eventParams.put(org.ametys.cms.ObservationConstants.ARGS_TRASH_ELEMENT_ID, trashElement.getId()); 170 eventParams.put(org.ametys.cms.ObservationConstants.ARGS_AMETYS_OBJECT_ID, deletedId); 171 _observationManager.notify(new Event(org.ametys.cms.ObservationConstants.EVENT_TRASH_UPDATED, _currentUserProvider.getUser(), eventParams)); 172 } 173 } 174 } 175 176 return returnMap; 177 } 178 179 private List<String> _flattenHierarchy(Map<String, Collection<String>> hierarchy) 180 { 181 Set<String> ids = new HashSet<>(); 182 for (Entry<String, Collection<String>> entry : hierarchy.entrySet()) 183 { 184 ids.add(entry.getKey()); 185 ids.addAll(entry.getValue()); 186 } 187 return new ArrayList<>(ids); 188 } 189 190 private Map<String, Collection<String>> _getHierarchy(Content content) 191 { 192 Map<String, Collection<String>> result = new HashMap<>(); 193 List<String> childrenIds = new ArrayList<>(); 194 try (AmetysObjectIterable<Content> children = _hierarchicalReferenceTablesHelper.getDirectChildren(content)) 195 { 196 for (Content child : children) 197 { 198 result.putAll(_getHierarchy(child)); 199 childrenIds.add(child.getId()); 200 } 201 } 202 203 result.put(content.getId(), childrenIds); 204 return result; 205 } 206 207 /** 208 * Test if content is still referenced by a content other than a child or its parent before removing it 209 * @param content The content to remove 210 * @return true if content is still referenced 211 */ 212 @SuppressWarnings("unchecked") 213 @Override 214 protected boolean _isContentReferenced(Content content) 215 { 216 Request request = ContextHelper.getRequest(_context); 217 return _hierarchicalReferenceTablesHelper._isContentReferenced(content, (List<String>) request.getAttribute(_REQUEST_ATTRIBUTE_CONTENT_IDS)); 218 } 219}