001/*
002 *  Copyright 2015 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.cart;
017
018import java.time.ZonedDateTime;
019import java.util.ArrayList;
020import java.util.HashMap;
021import java.util.HashSet;
022import java.util.LinkedList;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031import org.slf4j.LoggerFactory;
032
033import org.ametys.core.right.RightManager;
034import org.ametys.core.right.RightManager.RightResult;
035import org.ametys.core.ui.Callable;
036import org.ametys.core.user.CurrentUserProvider;
037import org.ametys.core.user.UserIdentity;
038import org.ametys.core.user.UserManager;
039import org.ametys.core.util.DateUtils;
040import org.ametys.plugins.cart.Cart.CartElementType;
041import org.ametys.plugins.core.user.UserHelper;
042import org.ametys.plugins.repository.AmetysObjectResolver;
043import org.ametys.plugins.repository.AmetysRepositoryException;
044import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
045import org.ametys.plugins.repository.UnknownAmetysObjectException;
046import org.ametys.plugins.repository.jcr.NameHelper;
047
048/**
049 * DAO for manipulating carts
050 */
051public class CartsDAO implements Serviceable, Component
052{
053    /** The Avalon role */
054    public static final String ROLE = CartsDAO.class.getName();
055    
056    /** The user manager */
057    protected UserManager _userManager;
058    
059    /** The current user provider */
060    private CurrentUserProvider _userProvider;
061    
062    /** The Ametys object resolver */
063    private AmetysObjectResolver _resolver;
064
065    private UserHelper _userHelper;
066
067    private RightManager _rightManager;
068    
069    @Override
070    public void service(ServiceManager serviceManager) throws ServiceException
071    {
072        _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
073        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
074        _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE);
075        _userHelper = (UserHelper) serviceManager.lookup(UserHelper.ROLE);
076        _rightManager = (RightManager) serviceManager.lookup(RightManager.ROLE);
077    }
078    
079    /**
080     * Gets carts information.
081     * @param cartIds The ids of the carts to retrieve.
082     * @return The carts information
083     */
084    @Callable(rights = Callable.SKIP_BUILTIN_CHECK)
085    public Map<String, Object> getCartsInformation (List<String> cartIds)
086    {
087        Map<String, Object> result = new HashMap<>();
088        
089        List<Map<String, Object>> carts = new LinkedList<>();
090        List<Map<String, Object>> notAllowedCarts = new LinkedList<>();
091        Set<String> unknownCarts = new HashSet<>();
092        
093        for (String id : cartIds)
094        {
095            Cart cart = getCart(id);
096            
097            if (cart != null)
098            {
099                if (_hasRight(cart))
100                {
101                    carts.add(getCartProperties(cart));
102                }
103                else
104                {
105                    notAllowedCarts.add(getCartProperties(cart));
106                }
107                
108            }
109            else
110            {
111                unknownCarts.add(id);
112            }
113        }
114        
115        result.put("carts", carts);
116        result.put("unknownCarts", unknownCarts);
117        result.put("notAllowedCarts", notAllowedCarts);
118        
119        return result;
120    }
121    
122    /**
123     * Creates a cart.
124     * @param title The title
125     * @param description The description
126     * @param documentation The documentation of the cart
127     * @return The id of the created cart
128     */
129    @Callable(rights = "Plugin_Cart_Rights_Create")
130    public Map<String, String> createCart (String title, String description, String documentation)
131    {
132        Map<String, String> result = new HashMap<>();
133        
134        ModifiableTraversableAmetysObject cartsNode = CartHelper.getCartsNode(_resolver);
135        
136        String name = NameHelper.filterName(title);
137        
138        // Find unique name
139        int index = 1;
140        String originalName = name;
141        while (cartsNode.hasChild(name))
142        {
143            name = originalName + "_" + (++index);
144        }
145        
146        // title might be modified to handle project with same title.
147        String realTitle = title + (index > 1 ? " (" + index + ")" : "");
148        
149        Cart cart = cartsNode.createChild(name, CartFactory.CART_NODETYPE);
150        cart.setTitle(realTitle);
151        cart.setDescription(description);
152        cart.setDocumentation(documentation);
153        cart.setAuthor(_userProvider.getUser());
154        cart.setContributor(_userProvider.getUser());
155        cart.setCreationDate(ZonedDateTime.now());
156        cart.setLastModificationDate(ZonedDateTime.now());
157        
158        cartsNode.saveChanges();
159        
160        result.put("id", cart.getId());
161        
162        return result;
163    }
164    
165    /**
166     * Updates a cart.
167     * @param id The id of the cart to update
168     * @param title The title
169     * @param description The description
170     * @param documentation The documentation of the cart
171     * @return The id of the updated cart
172     */
173    @Callable(rights = Callable.SKIP_BUILTIN_CHECK)
174    public Map<String, String> updateCart (String id, String title, String description, String documentation)
175    {
176        Map<String, String> result = new HashMap<>();
177        
178        Cart cart = _resolver.resolveById(id);
179        
180        if (canWrite(_userProvider.getUser(), cart))
181        {
182            cart.setTitle(title);
183            cart.setDescription(description);
184            cart.setDocumentation(documentation);
185            cart.setContributor(_userProvider.getUser());
186            cart.setLastModificationDate(ZonedDateTime.now());
187            cart.saveChanges();
188        }
189        else
190        {
191            result.put("message", "not-allowed");
192        }
193        
194        result.put("id", cart.getId());
195        
196        return result;
197    }
198    
199    /**
200     * Deletes some carts.
201     * @param ids The ids of the carts to delete
202     * @return The ids of the deleted carts
203     */
204    @Callable(rights = Callable.SKIP_BUILTIN_CHECK)
205    public Map<String, Object> deleteCarts (List<String> ids)
206    {
207        Map<String, Object> result = new HashMap<>();
208        
209        List<String> deletedCarts = new ArrayList<>();
210        List<String> unknownCarts = new ArrayList<>();
211        List<String> notallowedCarts = new ArrayList<>();
212        
213        for (String id : ids)
214        {
215            try
216            {
217                Cart cart = _resolver.resolveById(id);
218                if (canWrite(_userProvider.getUser(), cart))
219                {
220                    cart.remove();
221                    cart.saveChanges();
222                    deletedCarts.add(id);
223                }
224                else
225                {
226                    notallowedCarts.add(cart.getTitle());
227                }
228            }
229            catch (UnknownAmetysObjectException e)
230            {
231                unknownCarts.add(id);
232                LoggerFactory.getLogger(getClass()).error("Unable to delete cart. The cart of id '" + id + " doesn't exist", e);
233            }
234        }
235        
236        result.put("deletedCarts", deletedCarts);
237        result.put("notallowedCarts", notallowedCarts);
238        result.put("unknownCarts", unknownCarts);
239        
240        return result;
241    }
242    
243    /**
244     * Add elements to a cart.
245     * @param cartId The cart id.
246     * @param type The type of element.
247     * @param elementParams The parameters of the element.
248     * @return The id of the cart or an error
249     */
250    @Callable(rights = Callable.SKIP_BUILTIN_CHECK)
251    public Map<String, Object> addElements (String cartId, String type, Map<String, Object> elementParams)
252    {
253        Map<String, Object> result = new HashMap<>();
254        
255        UserIdentity user = _userProvider.getUser();
256        
257        Cart cart = _resolver.resolveById(cartId);
258        
259        if (!canWrite(user, cart))
260        {
261            LoggerFactory.getLogger(getClass()).error("User '{}' try to add elements to a cart without convenient privileges", user);
262            result.put("msg", "not-allowed");
263            return result;
264        }
265        
266        switch (CartElementType.valueOf(type.toUpperCase()))
267        {
268            case CONTENT:
269                @SuppressWarnings("unchecked")
270                List<String> contentIds = (List<String>) elementParams.get("ids");
271                for (String contentId : contentIds)
272                {
273                    cart.addContent(contentId);
274                }
275                break;
276
277            case RESOURCE:
278                @SuppressWarnings("unchecked")
279                List<String> resourceIds = (List<String>) elementParams.get("ids");
280                for (String resourceId : resourceIds)
281                {
282                    cart.addResource(resourceId);
283                }
284                break;
285                
286            case CARTQUERY:
287                String title = (String) elementParams.get("title");
288                String description = (String) elementParams.get("description");
289                
290                cart.addQuery(user, title, description);
291                break;
292            
293            case CARTQUERYFROMDIRECTORY:
294                @SuppressWarnings("unchecked")
295                List<String> queryIds = (List<String>) elementParams.get("queryIds");
296                for (String queryId : queryIds)
297                {
298                    cart.addQueryFormDirectory(queryId);
299                }
300                break;
301                
302            default:
303                throw new IllegalArgumentException("Unknown cart element type");
304        }
305
306        cart.setContributor(_userProvider.getUser());
307        cart.setLastModificationDate(ZonedDateTime.now());
308        cart.saveChanges();
309        
310        result.put("id", cart.getId());
311        
312        return result;
313    }
314    
315    /**
316     * Deletes elements of a cart.
317     * @param cartId The cart id.
318     * @param cartElements The elements to delete.
319     * @return The id of the cart or an error
320     */
321    @Callable(rights = Callable.SKIP_BUILTIN_CHECK)
322    public Map<String, Object> deleteElements (String cartId, List<Map<String, String>> cartElements)
323    {
324        Map<String, Object> result = new HashMap<>();
325        
326        Cart cart = _resolver.resolveById(cartId);
327        
328        UserIdentity user = _userProvider.getUser();
329        
330        if (!canWrite(user, cart))
331        {
332            LoggerFactory.getLogger(getClass()).error("User '{}' try to add elements to a cart without convenient privileges", user);
333            result.put("msg", "not-allowed");
334            return result;
335        }
336        
337        for (Map<String, String> cartElement : cartElements)
338        {
339            CartElementType type = CartElementType.valueOf(cartElement.get("type").toUpperCase());
340            cart.removeElement(cartElement.get("id"), type);
341        }
342
343        cart.setContributor(_userProvider.getUser());
344        cart.setLastModificationDate(ZonedDateTime.now());
345        cart.saveChanges();
346       
347        result.put("id", cartId);
348        
349        return result;
350    }
351    
352    /**
353     * Get the cart with given id
354     * @param cartId The cart id
355     * @return The retrieved cart or null.
356     */
357    public Cart getCart(String cartId)
358    {
359        try
360        {
361            return _resolver.resolveById(cartId);
362        }
363        catch (AmetysRepositoryException e)
364        {
365            if (LoggerFactory.getLogger(getClass()).isWarnEnabled())
366            {
367                LoggerFactory.getLogger(getClass()).warn("Failed to retrieves the cart with id : " +  cartId, e);
368            }
369            
370            return null;
371        }
372    }
373    
374    /**
375     * Get the cart type properties
376     * @param cart The cart
377     * @return The cart type properties
378     */
379    public Map<String, Object> getCartProperties (Cart cart)
380    {
381        Map<String, Object> infos = new HashMap<>();
382        
383        infos.put("id", cart.getId());
384        infos.put("title", cart.getTitle());
385        infos.put("description", cart.getDescription());
386        infos.put("documentation", cart.getDocumentation());
387        infos.put("author", _userHelper.user2json(cart.getAuthor()));
388        infos.put("contributor", _userHelper.user2json(cart.getContributor())); 
389        infos.put("lastModificationDate", DateUtils.zonedDateTimeToString(cart.getLastModificationDate()));
390        infos.put("creationDate", DateUtils.zonedDateTimeToString(cart.getCreationDate()));
391        
392        UserIdentity currentUser = _userProvider.getUser();
393        infos.put("canRead", canRead(currentUser, cart));
394        infos.put("canWrite", canWrite(currentUser, cart));
395        infos.put("canAssignRights", canAssignRights(currentUser, cart));
396        
397        return infos;
398    }
399    
400    /**
401     * Test if the current user has the right needed by the content type to view this cart.
402     * @param cart The cart
403     * @return true if the user has the right needed, false otherwise.
404     */
405    protected boolean _hasRight(Cart cart)
406    {
407        UserIdentity user = _userProvider.getUser();
408        return canRead(user, cart) || canWrite(user, cart);
409    }
410    
411    /**
412     * Check if a user have read rights on a cart
413     * @param userIdentity the user
414     * @param cart the cart
415     * @return true if the user have read rights on a cart
416     */
417    public boolean canRead(UserIdentity userIdentity, Cart cart)
418    {
419        return _rightManager.hasReadAccess(userIdentity, cart);
420    }
421
422    /**
423     * Check if a user have write rights on a cart
424     * @param userIdentity the user
425     * @param cart the cart
426     * @return true if the user have write rights on a cart
427     */
428    public boolean canWrite(UserIdentity userIdentity, Cart cart)
429    {
430        return _rightManager.hasRight(userIdentity, "Cart_Rights_Admin", cart) == RightResult.RIGHT_ALLOW;
431    }
432
433    /**
434     * Check if a user have rights to edit rights on a cart
435     * @param userIdentity the user
436     * @param cart the cart
437     * @return true if the user have write rights to edit rights on a cart
438     */
439    public boolean canAssignRights(UserIdentity userIdentity, Cart cart)
440    {
441        return canWrite(userIdentity, cart) || _rightManager.hasRight(userIdentity, "Runtime_Rights_Rights_Handle", "/cms") == RightResult.RIGHT_ALLOW;
442    }
443
444}