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