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