001/*
002 *  Copyright 2014 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.cms.lock;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.avalon.framework.component.Component;
024import org.apache.avalon.framework.logger.AbstractLogEnabled;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.avalon.framework.service.Serviceable;
028import org.apache.commons.lang.StringUtils;
029
030import org.ametys.cms.content.ContentHelper;
031import org.ametys.cms.repository.Content;
032import org.ametys.core.right.RightManager;
033import org.ametys.core.right.RightManager.RightResult;
034import org.ametys.core.ui.Callable;
035import org.ametys.core.user.CurrentUserProvider;
036import org.ametys.core.user.UserIdentity;
037import org.ametys.plugins.repository.AmetysObjectResolver;
038import org.ametys.plugins.repository.AmetysRepositoryException;
039import org.ametys.plugins.repository.lock.LockAwareAmetysObject;
040import org.ametys.plugins.repository.lock.LockHelper;
041import org.ametys.plugins.repository.lock.LockableAmetysObject;
042
043
044/**
045 * Unlock or lock one or several content(s) 
046 */
047public class LockContentManager extends AbstractLogEnabled implements Serviceable, Component
048{
049    
050    /** The component role. */
051    public static final String ROLE = LockContentManager.class.getName();
052    
053    /** The "unlock all" right ID. */
054    public static final String UNLOCK_ALL_RIGHT = "CMS_Rights_UnlockAll";
055    
056    /** The rights manager */
057    protected RightManager _rightManager;
058    /** The Ametys object resolver */
059    protected AmetysObjectResolver _resolver;
060    /**  The current user provider.*/
061    protected CurrentUserProvider _currentUserProvider;
062    /** The content helper */
063    protected ContentHelper _contentHelper;
064    
065    @Override
066    public void service(ServiceManager smanager) throws ServiceException
067    {
068        _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
069        _rightManager = (RightManager) smanager.lookup(RightManager.ROLE);
070        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
071        _contentHelper = (ContentHelper) smanager.lookup(ContentHelper.ROLE);
072    }
073    
074    /**
075     * Test if the current user can unlock the given locked object.<br>
076     * A user can unlock a locked object if he is the lock owner, or if he has the right to unlock all objects.
077     * @param object the locked object to test.
078     * @return true if the current user can unlock the given locked object, false otherwise.
079     */
080    public boolean canUnlock(LockAwareAmetysObject object)
081    {
082        return canUnlock(object, _currentUserProvider.getUser());
083    }
084    
085    /**
086     * Test if a specific user can unlock the given locked object.<br>
087     * A user can unlock a locked object if he is the lock owner, or if he has the right to unlock all objects.
088     * @param object the locked object to test.
089     * @param user the user.
090     * @return true if the current user can unlock the given locked object, false otherwise.
091     */
092    public boolean canUnlock(LockAwareAmetysObject object, UserIdentity user)
093    {
094        return LockHelper.isLockOwner(object, user) || canUnlockAll(user);
095    }
096    
097    /**
098     * Test if the current user has the right to unlock all contents.
099     * @return true if the current user has the right to unlock all contents, false otherwise.
100     */
101    public boolean canUnlockAll()
102    {
103        return canUnlockAll(_currentUserProvider.getUser());
104    }
105    
106    /**
107     * Test if a specific user has the right to unlock all contents.
108     * @param user the user.
109     * @return true if the user has the right to unlock all contents, false otherwise.
110     */
111    public boolean canUnlockAll(UserIdentity user)
112    {
113        return _rightManager.hasRight(_currentUserProvider.getUser(), UNLOCK_ALL_RIGHT, "/cms") == RightResult.RIGHT_ALLOW;
114    }
115    
116    /**
117     * Unlock or lock contents
118     * @param contents the contents to unlock or lock
119     * @param mode the mode ('lock' or 'unlock')
120     * @return the result JSON map
121     */
122    @Callable
123    public Map<String, Object> unlockOrLock(List<String> contents, String mode) 
124    {
125        Map<String, Object> result = new HashMap<>();
126        
127        if ("unlock".equals(mode))
128        {
129            result = unlock(contents);
130        }
131        else
132        {
133            result = lock(contents);
134        }
135        
136        result.put("mode", mode);
137        return result;
138    }
139    
140    /**
141     * Unlock contents
142     * @param contents The ids of contents to unlock
143     * @return the result JSON map
144     */
145    protected Map<String, Object> unlock(List<String> contents)
146    {
147        UserIdentity currentUser = _currentUserProvider.getUser();
148        boolean canDelockAll = _rightManager.hasRight(currentUser, "CMS_Rights_UnlockAll", "/cms") == RightResult.RIGHT_ALLOW;
149        
150        List<String> unlockContentsId = new ArrayList<>();
151        String unlockedContents = "";
152        String stillLockedContents = "";
153        String failLockedContents = "";
154        
155        for (String contentId : contents)
156        {
157            LockableAmetysObject lockableContent = (LockableAmetysObject) _resolver.resolveById(contentId);
158            if (!lockableContent.isLocked())
159            {
160                getLogger().warn("The content '" + contentId + "' is not locked anymore.");
161            }
162            else if (!LockHelper.isLockOwner(lockableContent, currentUser) && !canDelockAll)
163            {
164                getLogger().warn("The user '" + currentUser + "' try to unlock content '" + contentId + "' but he is not the lock owner");
165                
166                if (stillLockedContents.length() > 0)
167                {
168                    stillLockedContents += ", ";
169                }
170                stillLockedContents += _contentHelper.getTitle((Content) lockableContent);
171            }
172            else
173            {
174                try
175                {
176                    lockableContent.unlock();
177                    
178                    unlockContentsId.add(contentId);
179                    if (unlockedContents.length() > 0)
180                    {
181                        unlockedContents += ", ";
182                    }
183                    unlockedContents += _contentHelper.getTitle((Content) lockableContent);
184                    if (getLogger().isDebugEnabled())
185                    {
186                        getLogger().debug("The user was unlocked content '" + contentId + "'");
187                    }
188                    
189                }
190                catch (AmetysRepositoryException e)
191                {
192                    getLogger().error("Unable to unlock content '" + contentId + "'", e);
193                    
194                    if (failLockedContents.length() > 0)
195                    {
196                        failLockedContents += ", ";
197                    }
198                    failLockedContents += _contentHelper.getTitle((Content) lockableContent);
199                }
200            }
201        }
202        
203        Map<String, Object> result = new HashMap<>();
204        
205        result.put("unlocked-contents-id", StringUtils.join(unlockContentsId, ","));
206        result.put("unlocked-contents", unlockedContents);
207        result.put("still-locked-contents", stillLockedContents);
208        result.put("fail-unlocked-contents", failLockedContents);
209        
210        return result;
211    }
212    
213    /**
214     * Lock contents
215     * @param contents The ids of contents to lock
216     * @return the result JSON map
217     */
218    protected Map<String, Object> lock (List<String> contents)
219    {
220        List<String> lockContentsId = new ArrayList<>();
221        String lockedContents = "";
222        String alreadyLockedContents = "";
223        String failUnlockedContents = "";
224        
225        for (String contentId : contents)
226        {
227            LockableAmetysObject lockableContent = (LockableAmetysObject) _resolver.resolveById(contentId);
228            if (lockableContent.isLocked() && !LockHelper.isLockOwner(lockableContent, _currentUserProvider.getUser()))
229            {
230                getLogger().warn("The content '" + contentId + "' is already locked.");
231                if (alreadyLockedContents.length() > 0)
232                {
233                    alreadyLockedContents += ", ";
234                }
235                alreadyLockedContents += _contentHelper.getTitle((Content) lockableContent);
236            }
237            else if (!lockableContent.isLocked())
238            {
239                try
240                {
241                    lockableContent.lock();
242                    lockContentsId.add(contentId);
243                    if (lockedContents.length() > 0)
244                    {
245                        lockedContents += ", ";
246                    }
247                    lockedContents += _contentHelper.getTitle((Content) lockableContent);
248                }
249                catch (AmetysRepositoryException e)
250                {
251                    getLogger().error("Unable to lock content '" + contentId + "'", e);
252                    if (failUnlockedContents.length() > 0)
253                    {
254                        failUnlockedContents += ", ";
255                    }
256                    failUnlockedContents += _contentHelper.getTitle((Content) lockableContent);
257                }
258            }
259        }
260        
261        Map<String, Object> result = new HashMap<>();
262        
263        result.put("locked-contents-id", StringUtils.join(lockContentsId, ","));
264        result.put("locked-contents", lockedContents);
265        result.put("already-locked-contents", alreadyLockedContents);
266        result.put("fail-locked-contents", failUnlockedContents);
267        
268        return result;
269    }
270}