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}