001/*
002 *  Copyright 2013 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.Collections;
021import java.util.HashSet;
022import java.util.List;
023import java.util.Set;
024import java.util.stream.Collectors;
025
026import javax.jcr.Node;
027import javax.jcr.RepositoryException;
028
029import org.apache.commons.lang3.ArrayUtils;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033import org.ametys.cms.data.ametysobject.ModifiableModelAwareDataAwareAmetysObject;
034import org.ametys.cms.data.holder.ModifiableIndexableDataHolder;
035import org.ametys.cms.data.holder.impl.DefaultModifiableModelAwareDataHolder;
036import org.ametys.cms.indexing.solr.SolrAclCacheUninfluentialObject;
037import org.ametys.cms.repository.Content;
038import org.ametys.core.user.UserIdentity;
039import org.ametys.plugins.explorer.resources.Resource;
040import org.ametys.plugins.queriesdirectory.Query;
041import org.ametys.plugins.repository.AmetysObject;
042import org.ametys.plugins.repository.AmetysRepositoryException;
043import org.ametys.plugins.repository.UnknownAmetysObjectException;
044import org.ametys.plugins.repository.data.holder.group.ModifiableModelAwareRepeater;
045import org.ametys.plugins.repository.data.holder.group.ModifiableModelAwareRepeaterEntry;
046import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData;
047import org.ametys.plugins.repository.jcr.DefaultAmetysObject;
048
049/**
050 * Class representing a cart, backed by a JCR node.<br>
051 */
052@SolrAclCacheUninfluentialObject
053public class Cart extends DefaultAmetysObject<CartFactory> implements ModifiableModelAwareDataAwareAmetysObject
054{
055    /** Property name for cart title */
056    public static final String TITLE = "label";
057    /** Property name for cart description */
058    public static final String DESCRIPTION = "description";
059    /** Property name for cart documentation */
060    public static final String DOCUMENTATION = "documentation";
061    /** Property name for cart author */
062    public static final String AUTHOR = "author";
063    /** Property name for cart last contributor */
064    public static final String CONTRIBUTOR = "contributor";
065    /** Property name for cart creation date */
066    public static final String CREATIONDATE = "creationDate";
067    /** Property name for cart last modification date */
068    public static final String LASTMODIFICATIONDATE = "lastModificationDate";
069
070    /** Property name for cart content elements */
071    public static final String CONTENT_CART_ELEMENTS = "contents";
072    /** Property name for cart resource elements */
073    public static final String RESOURCE_CART_ELEMENTS = "resources";
074    /** Property name for cart queries elements */
075    public static final String QUERIES_CART_ELEMENTS = "queries";
076    /** Property name for cart queries from directory elements */
077    public static final String QUERIES_FROM_DIRECTORY_CART_ELEMENTS = "queries-from-directory";
078    
079    /** Property name for cart query id */
080    public static final String QUERY_ID_PROPERTY = "id";
081    /** Property name for cart query description */
082    public static final String QUERY_DESCRIPTION_PROPERTY = "description";
083    /** Property name for cart query author */
084    public static final String QUERY_AUTHOR_PROPERTY = "author";
085    /** Property name for cart query title */
086    public static final String QUERY_TITLE_PROPERTY = "title";
087    /** Property name for cart query creation date */
088    public static final String QUERY_DATE_PROPERTY = "date";
089    
090    private static Logger _logger = LoggerFactory.getLogger(Cart.class.getName());
091    
092    /**
093     * Rights profiles
094     */
095    public enum CartProfile
096    {
097        /** Read access */
098        READ_ACCESS,
099        /** Write access */
100        WRITE_ACCESS,
101        /** Right access */
102        RIGHT_ACCESS;
103        
104        @Override
105        public String toString()
106        {
107            return name().toLowerCase();
108        }
109    }
110    
111    /**
112     * Types of CartElement
113     */
114    public enum CartElementType
115    {
116        /** Type: content. */
117        CONTENT,
118        /** Type: resource. */
119        RESOURCE,
120        /** Type: cartQuery. */
121        CARTQUERY,
122        /** Type: query from directory. */
123        CARTQUERYFROMDIRECTORY
124    }
125    
126    /**
127     * Creates an {@link Cart}.
128     * @param node the node backing this {@link AmetysObject}
129     * @param parentPath the parentPath in the Ametys hierarchy
130     * @param factory the DefaultAmetysObjectFactory which created the AmetysObject
131     */
132    public Cart(Node node, String parentPath, CartFactory factory)
133    {
134        super(node, parentPath, factory);
135    }
136    
137    /**
138     * Set the title of this cart.
139     * @param title the title
140     */
141    public void setTitle(String title)
142    {
143        this.setValue(TITLE, title);
144    }
145    
146    /**
147     * Set the description of this cart.
148     * @param description the description
149     */
150    public void setDescription(String description)
151    {
152        this.setValue(DESCRIPTION, description);
153    }
154    
155    /**
156     * Set the documentation of this cart.
157     * @param documentation the documentation
158     */
159    public void setDocumentation(String documentation)
160    {
161        this.setValue(DOCUMENTATION, documentation);
162    }
163    
164    /**
165     * Set the author of this cart.
166     * @param author the author
167     */
168    public void setAuthor(UserIdentity author)
169    {
170        this.setValue(AUTHOR, author);
171    }
172
173    /**
174     * Set the last contributor of this cart.
175     * @param contributor the last contributor
176     */
177    public void setContributor(UserIdentity contributor)
178    {
179        this.setValue(CONTRIBUTOR, contributor);
180    }
181    
182    /**
183     * Set the date of the creation.
184     * @param creationDate the last modification date to set.
185     */
186    public void setCreationDate(ZonedDateTime creationDate)
187    {
188        this.setValue(CREATIONDATE, creationDate);
189    }
190    
191    /**
192     * Set the date of the last modification.
193     * @param lastModificationDate the last modification date to set.
194     */
195    public void setLastModificationDate(ZonedDateTime lastModificationDate)
196    {
197        this.setValue(LASTMODIFICATIONDATE, lastModificationDate);
198    }
199    
200    /**
201     * Get the title of the cart
202     * @return The title
203     */
204    public String getTitle()
205    {
206        return this.getValue(TITLE);
207    }
208        
209    /**
210     * Get the description of the cart
211     * @return The description
212     */
213    public String getDescription()
214    {
215        return this.getValue(DESCRIPTION);
216    }
217
218    /**
219     * Get the documentation of the cart
220     * @return The documentation
221     */
222    public String getDocumentation()
223    {
224        return this.getValue(DOCUMENTATION);
225    }
226    
227    /**
228     * Get the author of the cart
229     * @return The author
230     */
231    public UserIdentity getAuthor()
232    {
233        return this.getValue(AUTHOR);
234    }
235    
236    /**
237     * Get the last contributor of the cart
238     * @return The contributor
239     */
240    public UserIdentity getContributor()
241    {
242        return this.getValue(CONTRIBUTOR);
243    }
244    
245    /**
246     * Get the date of the last modification of the cart
247     * @return The date
248     */
249    public ZonedDateTime getCreationDate()
250    {
251        return this.getValue(CREATIONDATE);
252    }
253    
254    /**
255     * Get the date of the last modification of the cart
256     * @return The date
257     */
258    public ZonedDateTime getLastModificationDate()
259    {
260        return this.getValue(LASTMODIFICATIONDATE);
261    }
262        
263    /**
264     * Add a content to the cart
265     * @param contentId The content id
266     */
267    public void addContent (String contentId)
268    {
269        _addElementToCart(contentId, CONTENT_CART_ELEMENTS);
270    }
271    
272    /**
273     * Add a resource to the cart
274     * @param resourceId The resource id
275     */
276    public void addResource (String resourceId)
277    {
278        _addElementToCart(resourceId, RESOURCE_CART_ELEMENTS);
279    }
280    
281    /**
282     * Add a query from directory to the cart
283     * @param queryId The query id
284     */
285    public void addQueryFormDirectory (String queryId)
286    {
287        _addElementToCart(queryId, QUERIES_FROM_DIRECTORY_CART_ELEMENTS);
288    }
289
290    private void _addElementToCart(String elementId, String attributePath)
291    {
292        String[] elmtArray = getValue(attributePath, false, new String[0]);
293        Set<String> elmts = new HashSet<>();
294        Collections.addAll(elmts, elmtArray);
295        elmts.add(elementId);
296        setValue(attributePath, elmts.toArray(new String[elmts.size()]));
297    }
298    
299    /**
300     * Add a query to the cart
301     * @param author The author of the query
302     * @param title The title of the query
303     * @param description The query as string
304     */
305    public void addQuery (UserIdentity author, String title, String description)
306    {
307        addQuery(org.ametys.core.util.StringUtils.generateKey(), author, title, description, ZonedDateTime.now());
308    }
309    
310    /**
311     * Add a query to the cart
312     * @param id The id of the query
313     * @param author The author of the query
314     * @param title The title of the query
315     * @param description The query as string
316     * @param date The creation date of the query
317     */
318    public void addQuery (String id, UserIdentity author, String title, String description, ZonedDateTime date)
319    {
320        ModifiableModelAwareRepeater queries = getRepeater(QUERIES_CART_ELEMENTS, true);
321        ModifiableModelAwareRepeaterEntry query = queries.addEntry();
322        query.setValue(QUERY_ID_PROPERTY, id);
323        query.setValue(QUERY_TITLE_PROPERTY, title);
324        query.setValue(QUERY_DESCRIPTION_PROPERTY, description);
325        query.setValue(QUERY_AUTHOR_PROPERTY, author);
326        query.setValue(QUERY_DATE_PROPERTY, date);
327    }
328    
329    /**
330     * Delete an element
331     * @param elmtId The id of element to remove
332     * @param elmtType The type of element to remove
333     */
334    public void removeElement (String elmtId, CartElementType elmtType)
335    {
336        switch (elmtType)
337        {
338            case CONTENT:
339                _removeContent(elmtId);
340                break;
341                
342            case RESOURCE:
343                _removeResource(elmtId);
344                break;
345                
346            case CARTQUERY:
347                _removeQuery(elmtId);
348                break;
349                
350            case CARTQUERYFROMDIRECTORY:
351                _removeQueryFromDirectory(elmtId);
352                break;
353            
354            default:
355                break;
356        }
357    }
358    
359    /**
360     * Remove a resource from the cart
361     * @param resourceIdToRemove The id of the resource to remove
362     */
363    protected void _removeResource(String resourceIdToRemove)
364    {
365        _removeElementFromCart(resourceIdToRemove, RESOURCE_CART_ELEMENTS);
366    }
367    
368    /**
369     * Remove a content from the cart
370     * @param contentIdToRemove The id of the content to remove
371     */
372    protected void _removeContent(String contentIdToRemove)
373    {
374        _removeElementFromCart(contentIdToRemove, CONTENT_CART_ELEMENTS);
375    }
376    
377    /**
378     * Remove a query from directory from the cart
379     * @param queryIdToRemove The id of the query from directory to remove
380     */
381    protected void _removeQueryFromDirectory(String queryIdToRemove)
382    {
383        _removeElementFromCart(queryIdToRemove, QUERIES_FROM_DIRECTORY_CART_ELEMENTS);
384    }
385    
386    private void _removeElementFromCart(String elementId, String attributePath)
387    {
388        String[] elements = getValue(attributePath, false, new String[0]);
389        elements = ArrayUtils.removeElement(elements, elementId);
390        setValue(attributePath, elements);
391    }
392    
393    /**
394     * Remove a query from the cart
395     * @param queryIdToRemove The id of the query to remove
396     */
397    protected void _removeQuery(String queryIdToRemove)
398    {
399        ModifiableModelAwareRepeater queries = getRepeater(QUERIES_CART_ELEMENTS, true);
400        ModifiableModelAwareRepeaterEntry entryToRemove = queries.getEntries()
401                .stream()
402              .filter(entry -> entry.getValue(QUERY_ID_PROPERTY).equals(queryIdToRemove))
403              .findFirst()
404              .orElse(null);
405        
406        if (entryToRemove != null)
407        {
408            queries.removeEntry(entryToRemove.getPosition());
409        }
410    }
411    
412    /**
413     * Get the elements of the cart
414     * @return The elements of the cart
415     */
416    public List<CartElement> getElements ()
417    {
418        List<CartElement> elmts = new ArrayList<>();
419        
420        try
421        {
422            elmts.addAll(getContentCartElements());
423            elmts.addAll(getResourceCartElements());
424            elmts.addAll(getQueryCartElements());
425            elmts.addAll(getQueryFromDirectoryCartElements());
426        }
427        catch (RepositoryException e)
428        {
429            throw new AmetysRepositoryException("Error while getting cart elements", e);
430        }
431        
432        return elmts;
433    }
434    
435    /**
436     * Get the contents of the cart
437     * @return The elements of the cart
438     * @throws RepositoryException if an exception occurs while exploring the repository
439     */
440    public List<ContentElement> getContentCartElements () throws RepositoryException
441    {
442        List<String> unexistingElmts = new ArrayList<>();
443        
444        List<ContentElement> elmts = new ArrayList<>();
445        
446        String[] contents = getValue(CONTENT_CART_ELEMENTS, false, new String[0]);
447        
448        for (String contentId : contents)
449        {
450            try
451            {
452                Content content = _getFactory().getResolver().resolveById(contentId);
453                elmts.add(new ContentElement(content, _getFactory()._getContentTypesHelper(), _getFactory()._getContentTypeEP()));
454            }
455            catch (UnknownAmetysObjectException e)
456            {
457                _logger.error("The content of id '{}' does not exist anymore. It will be deleting from cart '{}'.", contentId, getId());
458                unexistingElmts.add(contentId);
459            }
460        }
461        // Delete unexisting contents
462        for (String contentId : unexistingElmts)
463        {
464            _removeContent(contentId);
465        }
466        
467        return elmts;
468    }
469    
470    /**
471     * Get the resources of the cart
472     * @return The elements of the cart
473     * @throws RepositoryException if an exception occurs while exploring the repository
474     */
475    public List<ResourceElement> getResourceCartElements () throws RepositoryException
476    {
477        List<String> unexistingElmts = new ArrayList<>();
478        List<ResourceElement> elmts = new ArrayList<>();
479
480        
481        String[] resources = getValue(RESOURCE_CART_ELEMENTS, false, new String[0]);
482        
483        for (String resourceId : resources)
484        {
485            try
486            {
487                Resource resource = _getFactory().getResolver().resolveById(resourceId);
488                elmts.add(new ResourceElement(resource));
489            }
490            catch (UnknownAmetysObjectException e)
491            {
492                _logger.error("The resource of id '{}' does not exist anymore. It will be deleting from cart '{}'.", resourceId, getId());
493                unexistingElmts.add(resourceId);
494            }
495        }
496        
497        // Delete unexisting resources
498        for (String resourceId : unexistingElmts)
499        {
500            _removeResource(resourceId);
501        }
502        
503        return elmts;
504    }
505    
506    /**
507     * Get the resources of the cart
508     * @return The elements of the cart
509     * @throws RepositoryException if an exception occurs while exploring the repository
510     */
511    public List<QueryFromDirectoryElement> getQueryFromDirectoryCartElements () throws RepositoryException
512    {
513        List<String> unexistingElmts = new ArrayList<>();
514        List<QueryFromDirectoryElement> elmts = new ArrayList<>();
515
516        
517        String[] queries = getValue(QUERIES_FROM_DIRECTORY_CART_ELEMENTS, false, new String[0]);
518        for (String queryId : queries)
519        {
520            try
521            {
522                Query query = _getFactory().getResolver().resolveById(queryId);
523                elmts.add(new QueryFromDirectoryElement(query));
524            }
525            catch (UnknownAmetysObjectException e)
526            {
527                _logger.error("The query of id '{}' does not exist anymore. It will be deleting from cart '{}'.", queryId, getId());
528                unexistingElmts.add(queryId);
529            }
530        }
531        
532        // Delete unexisting queries
533        for (String queryId : unexistingElmts)
534        {
535            _removeQueryFromDirectory(queryId);
536        }
537        
538        return elmts;
539    }
540    
541    /**
542     * <code>true</code> if the query cart element exist
543     * @param queryId the query id
544     * @return <code>true</code> if the query cart element exist
545     */
546    public boolean hasQueryFromDirectoryCartElement(String queryId)
547    {
548        String[] queries = getValue(QUERIES_FROM_DIRECTORY_CART_ELEMENTS, false, new String[0]);
549        return ArrayUtils.contains(queries, queryId);
550    }
551    
552    /**
553     * Get the queries of the cart
554     * @return The elements of the cart
555     * @throws RepositoryException if an exception occurs while exploring the repository
556     */
557    public List<QueryElement> getQueryCartElements () throws RepositoryException
558    {
559        ModifiableModelAwareRepeater queries = getRepeater(QUERIES_CART_ELEMENTS, true);
560        
561        List<QueryElement> elmts = queries.getEntries()
562                .stream()
563                .map(entry -> {
564                    String id = entry.getValue(QUERY_ID_PROPERTY);
565                    UserIdentity author = entry.getValue(QUERY_AUTHOR_PROPERTY);
566                    String query = entry.getValue(QUERY_DESCRIPTION_PROPERTY);
567                    String title = entry.getValue(QUERY_TITLE_PROPERTY);
568                    ZonedDateTime creationDate = entry.getValue(QUERY_DATE_PROPERTY);
569                           
570                    return new QueryElement(id, query, author, creationDate, author, creationDate, title);
571                })
572                .collect(Collectors.toList());
573        
574        return elmts;
575    }
576
577    public ModifiableIndexableDataHolder getDataHolder()
578    {
579        JCRRepositoryData repositoryData = new JCRRepositoryData(getNode());
580        return new DefaultModifiableModelAwareDataHolder(repositoryData, this._getFactory().getModel());
581    }
582}