001/*
002 *  Copyright 2019 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.Collections;
019import java.util.HashMap;
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.StringUtils;
027import org.apache.commons.lang3.tuple.Pair;
028import org.slf4j.Logger;
029
030import org.ametys.cms.content.ContentHelper;
031import org.ametys.cms.repository.Content;
032import org.ametys.cms.repository.WorkflowAwareContent;
033import org.ametys.cms.workflow.ContentWorkflowHelper;
034import org.ametys.cms.workflow.EditContentFunction;
035import org.ametys.core.util.I18nUtils;
036import org.ametys.plugins.repository.data.holder.impl.DataHolderHelper;
037import org.ametys.plugins.repository.data.holder.values.SynchronizableRepeater;
038import org.ametys.plugins.workflow.AbstractWorkflowComponent;
039import org.ametys.runtime.i18n.I18nizableText;
040import org.ametys.runtime.model.ModelItem;
041
042import com.opensymphony.workflow.InvalidActionException;
043import com.opensymphony.workflow.WorkflowException;
044
045/**
046 * Delete user component
047 */
048public class DeleteUserComponent extends AbstractDeleteUDContentComponent
049{
050    /** The avalon role. */
051    public static final String ROLE = DeleteUserComponent.class.getName();
052   
053    /** The user directory helper */
054    protected UserDirectoryHelper _udHelper;
055    
056    /** The content helper */
057    protected ContentHelper _contentHelper;
058    
059    /** The content workflow helper */
060    protected ContentWorkflowHelper _contentWorkflowHelper;
061    
062    /** The i18n utils */
063    protected I18nUtils _i18nUtils;
064    
065    @Override
066    public void service(ServiceManager smanager) throws ServiceException
067    {
068        super.service(smanager);
069        _udHelper = (UserDirectoryHelper) smanager.lookup(UserDirectoryHelper.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        return _contentHelper.hasReferencingContents(content, Collections.singletonList(UserDirectoryHelper.ORGUNIT_CONTENT_TYPE), true);
079    }
080    
081    @Override
082    protected boolean _checkBeforeDeletion(Content content, Map<String, String> rights, Map<String, Object> results, Logger logger)
083    {
084        // Check right and lock on content it self
085        boolean allRight = _canDeleteContent(content, rights, results);
086        
087        // Check lock on orgunits contents
088        allRight = _checkOrgUnitsBeforeDeletion(content, results) && allRight;
089        
090        return allRight;
091    }
092    
093    /**
094     * True if the parent content is not locked
095     * @param content the parent content
096     * @param results the results map
097     * @return true if the parent content is not locked
098     */
099    protected boolean _checkOrgUnitsBeforeDeletion(Content content,  Map<String, Object> results)
100    {
101        boolean allRight = true;
102        
103        // Check if parents are not locked
104        List<Content> orgUnits = _udHelper.getOrgUnits(content);
105        for (Content orgUnit : orgUnits)
106        {
107            if (_isLocked(orgUnit))
108            {
109                @SuppressWarnings("unchecked")
110                List<Content> lockedContents = (List<Content>) results.get("locked-contents");
111                lockedContents.add(orgUnit);
112                
113                allRight = false;
114            }
115        }
116        
117        return allRight;
118    }
119
120    @Override
121    protected boolean _removeRelations(Content content, Map<String, Object> parameters, Logger logger)
122    {
123        boolean success = true;
124        
125        // Delete relation to orgunits
126        List<Pair<String, Content>> incomingReferences = _contentHelper.getReferencingContents(content);
127        for (Pair<String, Content> reference : incomingReferences)
128        {
129            String valuePath = reference.getLeft();
130            Content orgUnit = reference.getRight();
131            
132            try
133            {
134                I18nizableText commentText = new I18nizableText("plugin.user-directory", "PLUGINS_USER_DIRECTORY_WORKFLOW_ACTION_REMOVE_USER_REFERENCE_MSG");
135                String comment = _i18nUtils.translate(commentText, orgUnit.getLanguage());
136                
137                _removeRelation((WorkflowAwareContent) orgUnit, valuePath, comment);
138            }
139            catch (WorkflowException | InvalidActionException e)
140            {
141                logger.error("Unable to remove relation to content \"{}\" ({}) for referencing content \"{}\" ({}) ", content.getTitle(), content.getId(), orgUnit.getTitle(), orgUnit.getId(), e);
142                success = false;
143            }
144        }
145        
146        return success;
147    }
148    
149    private void _removeRelation(WorkflowAwareContent orgUnit, String valuePath, String comment) throws WorkflowException
150    {
151        // The valuePath is like users[1]/user
152        String entryPath = StringUtils.substringBeforeLast(valuePath, ModelItem.ITEM_PATH_SEPARATOR);
153        
154        int position = DataHolderHelper.getRepeaterNameAndEntryPosition(entryPath).getRight();
155        
156        SynchronizableRepeater repeater = SynchronizableRepeater.appendOrRemove(List.of(), Set.of(position));
157        Map<String, Object> values = Map.of("users", repeater);
158        
159        Map<String, Object> inputs = new HashMap<>();
160        if (StringUtils.isNotEmpty(comment))
161        {
162            inputs.put("comment", comment);
163        }
164        
165        Map<String, Object> parameters = new HashMap<>();
166        
167        parameters.put(EditContentFunction.VALUES_KEY, values);
168        parameters.put(EditContentFunction.QUIT, true);
169        inputs.put(AbstractWorkflowComponent.CONTEXT_PARAMETERS_KEY, parameters);
170    
171        _contentWorkflowHelper.doAction(orgUnit, _removeReferenceActionId, inputs);
172    }
173
174    @Override
175    protected Set<String> _getContentIdsToDelete(Content content, Map<String, Object> parameters, Map<String, String> rights, Map<String, Object> results, Logger logger)
176    {
177        return Collections.singleton(content.getId());
178    }
179}