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.util.ArrayList;
019import java.util.HashMap;
020import java.util.HashSet;
021import java.util.LinkedList;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025import java.util.stream.Collectors;
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.apache.commons.lang3.StringUtils;
032import org.slf4j.LoggerFactory;
033
034import org.ametys.cms.FilterNameHelper;
035import org.ametys.core.group.GroupIdentity;
036import org.ametys.core.ui.Callable;
037import org.ametys.core.user.CurrentUserProvider;
038import org.ametys.core.user.UserIdentity;
039import org.ametys.core.user.UserManager;
040import org.ametys.plugins.cart.Cart.CartElementType;
041import org.ametys.plugins.cart.Cart.CartProfile;
042import org.ametys.plugins.cart.Cart.Visibility;
043import org.ametys.plugins.core.user.UserHelper;
044import org.ametys.plugins.repository.AmetysObjectResolver;
045import org.ametys.plugins.repository.AmetysRepositoryException;
046import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
047import org.ametys.plugins.repository.UnknownAmetysObjectException;
048
049/**
050 * DAO for manipulating carts
051 */
052public class CartsDAO implements Serviceable, Component
053{
054    /** The Avalon role */
055    public static final String ROLE = CartsDAO.class.getName();
056    
057    /** The user manager */
058    protected UserManager _userManager;
059    
060    /** The current user provider */
061    private CurrentUserProvider _userProvider;
062    
063    /** The Ametys object resolver */
064    private AmetysObjectResolver _resolver;
065
066    private UserHelper _userHelper;
067    
068    @Override
069    public void service(ServiceManager serviceManager) throws ServiceException
070    {
071        _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
072        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
073        _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE);
074        _userHelper = (UserHelper) serviceManager.lookup(UserHelper.ROLE);
075    }
076    
077    /**
078     * Gets carts information.
079     * @param cartIds The ids of the carts to retrieve.
080     * @return The carts information
081     */
082    @Callable
083    public Map<String, Object> getCartsInformation (List<String> cartIds)
084    {
085        Map<String, Object> result = new HashMap<>();
086        
087        List<Map<String, Object>> carts = new LinkedList<>();
088        List<Map<String, Object>> notAllowedCarts = new LinkedList<>();
089        Set<String> unknownCarts = new HashSet<>();
090        
091        for (String id : cartIds)
092        {
093            Cart cart = getCart(id);
094            
095            if (cart != null && _hasRight(cart))
096            {
097                if (_hasRight(cart))
098                {
099                    carts.add(getCartProperties(cart));
100                }
101                else
102                {
103                    notAllowedCarts.add(getCartProperties(cart));
104                }
105                
106            }
107            else
108            {
109                unknownCarts.add(id);
110            }
111        }
112        
113        result.put("carts", carts);
114        result.put("unknownCarts", unknownCarts);
115        result.put("notAllowedCarts", unknownCarts);
116        
117        return result;
118    }
119    
120    /**
121     * Creates a cart.
122     * @param title The title
123     * @param description The description
124     * @return The id of the created cart
125     */
126    @Callable
127    public Map<String, String> createCart (String title, String description)
128    {
129        Map<String, String> result = new HashMap<>();
130        
131        ModifiableTraversableAmetysObject cartsNode = CartHelper.getCartsNode(_resolver);
132        
133        String name = FilterNameHelper.filterName(title);
134        
135        // Find unique name
136        int index = 1;
137        String originalName = name;
138        while (cartsNode.hasChild(name))
139        {
140            name = originalName + "_" + (++index);
141        }
142        
143        // title might be modified to handle project with same title.
144        String realTitle = title + (index > 1 ? " (" + index + ")" : "");
145        
146        Cart cart = cartsNode.createChild(name, CartFactory.CART_NODETYPE);
147        cart.setTitle(realTitle);
148        cart.setDescription(description);
149        cart.setAuthor(_userProvider.getUser());
150        
151        cart.setVisibility(Visibility.PRIVATE);
152        
153        cartsNode.saveChanges();
154        
155        result.put("id", cart.getId());
156        
157        return result;
158    }
159    
160    /**
161     * Updates a cart.
162     * @param id The id of the cart to update
163     * @param title The title
164     * @param description The description
165     * @return The id of the updated cart
166     */
167    @Callable
168    public Map<String, String> updateCart (String id, String title, String description)
169    {
170        Map<String, String> result = new HashMap<>();
171        
172        Cart cart = _resolver.resolveById(id);
173        UserIdentity author = cart.getAuthor();
174        
175        if (author != null && author.equals(_userProvider.getUser()))
176        {
177            cart.setTitle(title);
178            cart.setDescription(description);
179            cart.saveChanges();
180        }
181        else
182        {
183            result.put("message", "not-allowed");
184        }
185        
186        result.put("id", cart.getId());
187        
188        return result;
189    }
190    
191    /**
192     * Deletes some carts.
193     * @param ids The ids of the carts to delete
194     * @return The ids of the deleted carts
195     */
196    @Callable
197    public Map<String, Object> deleteCarts (List<String> ids)
198    {
199        Map<String, Object> result = new HashMap<>();
200        
201        List<String> deletedCarts = new ArrayList<>();
202        List<String> unknownCarts = new ArrayList<>();
203        List<String> notallowedCarts = new ArrayList<>();
204        
205        for (String id : ids)
206        {
207            try
208            {
209                Cart cart = _resolver.resolveById(id);
210                if (cart.getAuthor().equals(_userProvider.getUser()))
211                {
212                    cart.remove();
213                    cart.saveChanges();
214                    deletedCarts.add(id);
215                }
216                else
217                {
218                    notallowedCarts.add(cart.getTitle());
219                }
220            }
221            catch (UnknownAmetysObjectException e)
222            {
223                unknownCarts.add(id);
224                LoggerFactory.getLogger(getClass()).error("Unable to delete cart. The cart of id '" + id + " doesn't exist", e);
225            }
226        }
227        
228        result.put("deletedCarts", deletedCarts);
229        result.put("notallowedCarts", notallowedCarts);
230        result.put("unknownCarts", unknownCarts);
231        
232        return result;
233    }
234    
235    /**
236     * Add elements to a cart.
237     * @param cartId The cart id.
238     * @param type The type of element.
239     * @param elementParams The parameters of the element.
240     * @return The id of the cart or an error
241     */
242    @Callable
243    public Map<String, Object> addElements (String cartId, String type, Map<String, Object> elementParams)
244    {
245        Map<String, Object> result = new HashMap<>();
246        
247        UserIdentity user = _userProvider.getUser();
248        
249        Cart cart = _resolver.resolveById(cartId);
250        
251        if (!cart.canWrite(user))
252        {
253            LoggerFactory.getLogger(getClass()).error("User '{}' try to add elements to a cart without convenient privileges", user);
254            result.put("msg", "not-allowed");
255            return result;
256        }
257        
258        switch (CartElementType.valueOf(type.toUpperCase()))
259        {
260            case CONTENT:
261                @SuppressWarnings("unchecked")
262                List<String> contentIds = (List<String>) elementParams.get("ids");
263                for (String contentId : contentIds)
264                {
265                    cart.addContent(contentId);
266                }
267                break;
268
269            case RESOURCE:
270                @SuppressWarnings("unchecked")
271                List<String> resourceIds = (List<String>) elementParams.get("ids");
272                for (String resourceId : resourceIds)
273                {
274                    cart.addResource(resourceId);
275                }
276                break;
277                
278            case CARTQUERY:
279                String title = (String) elementParams.get("title");
280                String description = (String) elementParams.get("description");
281                
282                cart.addQuery(user, title, description);
283                break;
284                
285            default:
286                throw new IllegalArgumentException("Unknown cart element type");
287        }
288        
289        cart.saveChanges();
290        
291        result.put("id", cart.getId());
292        
293        return result;
294    }
295    
296    /**
297     * Deletes elements of a cart.
298     * @param cartId The cart id.
299     * @param cartElements The elements to delete.
300     * @return The id of the cart or an error
301     */
302    @Callable
303    public Map<String, Object> deleteElements (String cartId, List<Map<String, String>> cartElements)
304    {
305        Map<String, Object> result = new HashMap<>();
306        
307        Cart cart = _resolver.resolveById(cartId);
308        
309        UserIdentity user = _userProvider.getUser();
310        
311        if (!cart.canWrite(user))
312        {
313            LoggerFactory.getLogger(getClass()).error("User '{}' try to add elements to a cart without convenient privileges", user);
314            result.put("msg", "not-allowed");
315            return result;
316        }
317        
318        for (Map<String, String> cartElement : cartElements)
319        {
320            CartElementType type = CartElementType.valueOf(cartElement.get("type").toUpperCase());
321            cart.removeElement(cartElement.get("id"), type);
322        }
323        
324        cart.saveChanges();
325       
326        result.put("id", cartId);
327        
328        return result;
329    }
330    
331    /**
332     * Changes the visibility of a cart.
333     * @param cartId The cart id.
334     * @param visibilityStr The visibility to set.
335     * @return An empty map, or an error message.
336     */
337    @Callable
338    public Map<String, Object> changeVisibility (String cartId, String visibilityStr)
339    {
340        Map<String, Object> result = new HashMap<>();
341        
342        // Parameter checks.
343        Cart cart = null;
344        if (StringUtils.isNotEmpty(cartId))
345        {
346            cart = _resolver.resolveById(cartId);
347        }
348        else
349        {
350            throw new IllegalArgumentException("Mandatory cart id parameter is missing.");
351        }
352        
353        Visibility visibility = Visibility.valueOf(visibilityStr.toUpperCase());
354        
355        UserIdentity author = cart.getAuthor();
356        if (author != null && author.equals(_userProvider.getUser()))
357        {
358            cart.setVisibility(visibility);
359            cart.saveChanges();
360        }
361        else
362        {
363            result.put("message", "not-allowed");
364        }
365        
366        return result;
367    }
368    
369    /**
370     * Adds users or groups to a cart profile.
371     * @param cartId The cart id.
372     * @param profileId The profile id.
373     * @param entries The users (or groups) entries.
374     * @param type The type of the entries to add: 'users' or 'groups'
375     * @return An empty map, or an error.
376     */
377    @Callable
378    public Map<String, Object> assignRights (String cartId, String profileId, List<Map<String, String>> entries, String type)
379    {
380        Map<String, Object> result = new HashMap<>();
381        
382        // Parameter checks.
383        Cart cart = null;
384        if (StringUtils.isNotEmpty(cartId))
385        {
386            cart = _resolver.resolveById(cartId);
387        }
388        else
389        {
390            throw new IllegalArgumentException("Mandatory cart id parameter is missing.");
391        }
392        
393        if (!"users".equals(type) && !"groups".equals(type))
394        {
395            throw new IllegalArgumentException("Unexpected type parameter : " + type);
396        }
397        
398        if (!"read_access".equals(profileId) && !"write_access".equals(profileId))
399        {
400            throw new IllegalArgumentException("Unexpected profile identifier : " + profileId);
401        }
402        
403        UserIdentity author = cart.getAuthor();
404        if (author != null && author.equals(_userProvider.getUser()))
405        {
406            CartProfile profile = CartProfile.valueOf(profileId.toUpperCase());
407            
408            // Add the entries to the profile
409            
410            
411            // Set new values
412            if ("users".equals(type))
413            {
414                Set<UserIdentity> allEntries = cart.getGrantedUsers(profile);
415                
416                for (Map<String, String> user : entries)
417                {
418                    UserIdentity identity = new UserIdentity(user.get("login"), user.get("populationId"));
419                    if (!allEntries.contains(identity))
420                    {
421                        allEntries.add(identity);
422                    }
423                }
424                cart.setGrantedUsers(profile, allEntries);
425            }
426            else
427            {
428                Set<GroupIdentity> allEntries = cart.getGrantedGroups(profile);
429                
430                for (Map<String, String> group : entries)
431                {
432                    GroupIdentity identity = new GroupIdentity(group.get("id"), group.get("groupDirectory"));
433                    if (!allEntries.contains(identity))
434                    {
435                        allEntries.add(identity);
436                    }
437                }
438                cart.setGrantedGroups(profile, allEntries);
439            }
440            
441            // Save
442            cart.saveChanges();
443        }
444        else
445        {
446            result.put("message", "not-allowed");
447        }
448        
449        return result;
450    }
451    
452    /**
453     * Deletes users or groups to a cart profile.
454     * @param cartId The cart id.
455     * @param profileId The profile id.
456     * @param users The users to delete.
457     * @param groups The groups to delete;
458     * @return An empty map, or an error.
459     */
460    @Callable
461    public Map<String, Object> removeAssignment (String cartId, String profileId, List<Map<String, String>> users, List<Map<String, String>> groups)
462    {
463        Map<String, Object> result = new HashMap<>();
464        
465        // Parameter checks.
466        Cart cart = null;
467        if (StringUtils.isNotEmpty(cartId))
468        {
469            cart = _resolver.resolveById(cartId);
470        }
471        else
472        {
473            throw new IllegalArgumentException("Mandatory cart id parameter is missing.");
474        }
475        
476        if (!"read_access".equals(profileId) && !"write_access".equals(profileId))
477        {
478            throw new IllegalArgumentException("Unexpected profile identifier : " + profileId);
479        }
480        
481        UserIdentity author = cart.getAuthor();
482        if (author != null && author.equals(_userProvider.getUser()))
483        {
484            CartProfile profile = CartProfile.valueOf(profileId.toUpperCase());
485            
486            if (!users.isEmpty())
487            {
488                Set<UserIdentity> grantedUsers = cart.getGrantedUsers(profile);
489                grantedUsers.removeAll(users.stream().map(userAsMap -> new UserIdentity(userAsMap.get("login"), userAsMap.get("populationId"))).collect(Collectors.toList()));
490                
491                cart.setGrantedUsers(profile, grantedUsers);
492            }
493            
494            if (!groups.isEmpty())
495            {
496                Set<GroupIdentity> grantedGroups = cart.getGrantedGroups(profile);
497                grantedGroups.removeAll(groups.stream().map(groupAsMap -> new GroupIdentity(groupAsMap.get("id"), groupAsMap.get("groupDirectory"))).collect(Collectors.toList()));
498                
499                cart.setGrantedGroups(profile, grantedGroups);
500            }
501            
502            // Save
503            cart.saveChanges();
504        }
505        else
506        {
507            result.put("message", "not-allowed");
508        }
509        
510        return result;
511    }
512    
513    /**
514     * Get the cart with given id
515     * @param cartId The cart id
516     * @return The retrieved cart or null.
517     */
518    public Cart getCart(String cartId)
519    {
520        try
521        {
522            return _resolver.resolveById(cartId);
523        }
524        catch (AmetysRepositoryException e)
525        {
526            if (LoggerFactory.getLogger(getClass()).isWarnEnabled())
527            {
528                LoggerFactory.getLogger(getClass()).warn("Failed to retrieves the cart with id : " +  cartId, e);
529            }
530            
531            return null;
532        }
533    }
534    
535    /**
536     * Get the cart type properties
537     * @param cart The cart
538     * @return The cart type properties
539     */
540    public Map<String, Object> getCartProperties (Cart cart)
541    {
542        Map<String, Object> infos = new HashMap<>();
543        
544        infos.put("id", cart.getId());
545        infos.put("title", cart.getTitle());
546        infos.put("description", cart.getDescription());
547        infos.put("author", _userHelper.user2json(cart.getAuthor()));
548        infos.put("visibility", cart.getVisibility().toString());
549        
550        UserIdentity currentUser = _userProvider.getUser();
551        infos.put("canRead", cart.canRead(currentUser));
552        infos.put("canWrite", cart.canWrite(currentUser));
553        
554        return infos;
555    }
556    
557    /**
558     * Test if the current user has the right needed by the content type to view this cart.
559     * @param cart The cart
560     * @return true if the user has the right needed, false otherwise.
561     */
562    protected boolean _hasRight(Cart cart)
563    {
564        UserIdentity user = _userProvider.getUser();
565        return cart.canRead(user) || cart.canWrite(user);
566    }
567
568}