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