001/*
002 *  Copyright 2010 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.explorer.resources.jcr;
017
018import java.io.InputStream;
019import java.util.Calendar;
020import java.util.Date;
021import java.util.GregorianCalendar;
022import java.util.List;
023import java.util.Set;
024
025import javax.jcr.Binary;
026import javax.jcr.Node;
027import javax.jcr.NodeIterator;
028import javax.jcr.Property;
029import javax.jcr.RepositoryException;
030import javax.jcr.Value;
031import javax.jcr.version.VersionHistory;
032
033import org.apache.commons.lang3.StringUtils;
034import org.apache.jackrabbit.JcrConstants;
035
036import org.ametys.core.user.UserIdentity;
037import org.ametys.plugins.explorer.ExplorerNode;
038import org.ametys.plugins.explorer.resources.ModifiableResource;
039import org.ametys.plugins.explorer.resources.Resource;
040import org.ametys.plugins.explorer.resources.ResourceHelper;
041import org.ametys.plugins.repository.AmetysObject;
042import org.ametys.plugins.repository.AmetysRepositoryException;
043import org.ametys.plugins.repository.CopiableAmetysObject;
044import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
045import org.ametys.plugins.repository.RepositoryConstants;
046import org.ametys.plugins.repository.dublincore.DCMITypes;
047import org.ametys.plugins.repository.jcr.DefaultLockableAmetysObject;
048import org.ametys.plugins.repository.jcr.DublinCoreHelper;
049import org.ametys.plugins.repository.jcr.NodeHelper;
050import org.ametys.plugins.repository.jcr.NodeTypeHelper;
051import org.ametys.plugins.repository.tag.TaggableAmetysObjectHelper;
052import org.ametys.plugins.repository.trash.ConflictingNameException;
053import org.ametys.plugins.repository.trash.TrashElement;
054import org.ametys.plugins.repository.trash.TrashableAmetysObject;
055import org.ametys.plugins.repository.trash.UnknownParentException;
056
057/**
058 * Default implementation of an {@link Resource}, backed by a JCR node.<br>
059 * @param <F> the actual type of factory.
060 */
061public class JCRResource<F extends JCRResourceFactory> extends DefaultLockableAmetysObject<F> implements ModifiableResource, CopiableAmetysObject, TrashableAmetysObject
062{
063    /** The name of node holding the creator */
064    public static final String CREATOR_NODE_NAME = "creator";
065    /** Constants for lastModified Metadata */
066    public static final String CREATION_DATE = "creationDate";
067    /** The name of node holding the last contributor */
068    public static final String CONTRIBUTOR_NODE_NAME = "contributor";
069    
070    /**
071     * Creates an {@link JCRResource}.
072     * @param node the node backing this {@link AmetysObject}
073     * @param parentPath the parentPath in the Ametys hierarchy
074     * @param factory the DefaultAmetysObjectFactory which created the AmetysObject
075     */
076    public JCRResource(Node node, String parentPath, F factory)
077    {
078        super(node, parentPath, factory);
079    }
080    
081    @Override
082    public void setData(InputStream stream, String mimeType, Date lastModified, UserIdentity author)
083    {
084        Node fileNode = getNode();
085        
086        try
087        {
088            setLastContributor(author);
089            
090            Node resourceNode = null;
091            
092            if (fileNode.hasNode(JcrConstants.JCR_CONTENT))
093            {
094                // Already exists
095                resourceNode = fileNode.getNode(JcrConstants.JCR_CONTENT);
096            }
097            else
098            {
099                resourceNode = fileNode.addNode(JcrConstants.JCR_CONTENT, JcrConstants.NT_RESOURCE);
100                setCreator(author);
101                setCreationDate(new Date());
102            }
103            
104            GregorianCalendar gc = new GregorianCalendar();
105            gc.setTime(lastModified);
106            resourceNode.setProperty(JcrConstants.JCR_LASTMODIFIED, gc);
107            
108            resourceNode.setProperty(JcrConstants.JCR_MIMETYPE, mimeType);
109            
110            Binary binary = resourceNode.getSession().getValueFactory().createBinary(stream);
111            resourceNode.setProperty(JcrConstants.JCR_DATA, binary);
112        }
113        catch (RepositoryException e)
114        {
115            throw new AmetysRepositoryException("Cannot set data for resource " + this.getName() + " (" + this.getId() + ")", e);
116        }
117    }
118    
119    @Override
120    public void setLastModified(Date lastModified)
121    {
122        Node fileNode = getNode();
123        
124        try
125        {
126            Node resourceNode = fileNode.getNode(JcrConstants.JCR_CONTENT);
127            
128            GregorianCalendar gc = new GregorianCalendar();
129            gc.setTime(lastModified);
130            resourceNode.setProperty(JcrConstants.JCR_LASTMODIFIED, gc);
131        }
132        catch (RepositoryException e)
133        {
134            throw new AmetysRepositoryException("Cannot set lastmodified for resource " + this.getName() + " (" + this.getId() + ")", e);
135        }
136    }
137    
138    @Override
139    public void setKeywords(String keywords)
140    {
141        String[] words = StringUtils.stripAll(StringUtils.split(keywords, ','));
142        
143        String[] trimWords = new String[words.length];
144        for (int i = 0; i < words.length; i++)
145        {
146            trimWords[i] = words[i].trim();
147        }
148        
149        Node fileNode = getNode();
150        try
151        {
152            fileNode.setProperty("ametys:keywords", trimWords);
153        }
154        catch (RepositoryException e)
155        {
156            throw new AmetysRepositoryException("Cannot set keywords for resource " + this.getName() + " (" + this.getId() + ")", e);
157        }
158    }
159    
160    @Override
161    public void setKeywords(String[] keywords)
162    {
163        Node fileNode = getNode();
164        try
165        {
166            fileNode.setProperty("ametys:keywords", keywords);
167        }
168        catch (RepositoryException e)
169        {
170            throw new AmetysRepositoryException("Cannot set keywords for resource " + this.getName() + " (" + this.getId() + ")", e);
171        }
172    }
173    
174    @Override
175    public void setMimeType(String mimeType)
176    {
177        Node fileNode = getNode();
178        
179        try
180        {
181            Node resourceNode = fileNode.getNode(JcrConstants.JCR_CONTENT);
182            
183            resourceNode.setProperty(JcrConstants.JCR_MIMETYPE, mimeType);
184        }
185        catch (RepositoryException e)
186        {
187            throw new AmetysRepositoryException("Cannot set mimetype for resource " + this.getName() + " (" + this.getId() + ")", e);
188        }
189    }
190    
191    @Override
192    public void setCreator(UserIdentity author)
193    {
194        try
195        {
196            Node authorNode = null;
197            if (getNode().hasNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + CREATOR_NODE_NAME))
198            {
199                authorNode = getNode().getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + CREATOR_NODE_NAME);
200            }
201            else
202            {
203                authorNode = getNode().addNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + CREATOR_NODE_NAME, RepositoryConstants.USER_NODETYPE);
204            }
205            authorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":login", author.getLogin());
206            authorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":population", author.getPopulationId());
207        }
208        catch (RepositoryException e)
209        {
210            throw new AmetysRepositoryException("Cannot set creator for resource " + this.getName() + " (" + this.getId() + ")", e);
211        }
212    }
213    
214    @Override
215    public InputStream getInputStream () throws AmetysRepositoryException
216    {
217        Node fileNode = getNode();
218        try
219        {
220            Node resourceNode = null;
221            
222            if (fileNode.hasNode(JcrConstants.JCR_CONTENT))
223            {
224                // Already exists
225                resourceNode = fileNode.getNode(JcrConstants.JCR_CONTENT);
226                return resourceNode.getProperty(JcrConstants.JCR_DATA).getBinary().getStream();
227            }
228            return null;
229        }
230        catch (RepositoryException e)
231        {
232            throw new AmetysRepositoryException("Cannot get inputstream for resource " + this.getName() + " (" + this.getId() + ")", e);
233        }
234    }
235    
236    @Override
237    public String getMimeType ()  throws AmetysRepositoryException
238    {
239        Node fileNode = getNode();
240        try
241        {
242            Node resourceNode = null;
243            
244            if (fileNode.hasNode(JcrConstants.JCR_CONTENT))
245            {
246                // Already exists
247                resourceNode = fileNode.getNode(JcrConstants.JCR_CONTENT);
248                return resourceNode.getProperty(JcrConstants.JCR_MIMETYPE).getString();
249            }
250            
251            return null;
252        }
253        catch (RepositoryException e)
254        {
255            throw new AmetysRepositoryException("Cannot get mimetype for resource " + this.getName() + " (" + this.getId() + ")", e);
256        }
257    }
258    
259    @Override
260    public Date getLastModified () throws AmetysRepositoryException
261    {
262        Node fileNode = getNode();
263        try
264        {
265            Node resourceNode = null;
266            
267            if (fileNode.hasNode(JcrConstants.JCR_CONTENT))
268            {
269                resourceNode = fileNode.getNode(JcrConstants.JCR_CONTENT);
270                return resourceNode.getProperty(JcrConstants.JCR_LASTMODIFIED).getDate().getTime();
271            }
272            
273            return null;
274        }
275        catch (RepositoryException e)
276        {
277            throw new AmetysRepositoryException("Cannot get lastmodified for resource " + this.getName() + " (" + this.getId() + ")", e);
278        }
279    }
280    
281    @Override
282    public long getLength() throws AmetysRepositoryException
283    {
284        Node fileNode = getNode();
285        try
286        {
287            Node resourceNode = null;
288            
289            if (fileNode.hasNode(JcrConstants.JCR_CONTENT))
290            {
291                resourceNode = fileNode.getNode(JcrConstants.JCR_CONTENT);
292                return resourceNode.getProperty(JcrConstants.JCR_DATA).getLength();
293            }
294            
295            return 0;
296        }
297        catch (RepositoryException e)
298        {
299            throw new AmetysRepositoryException("Cannot get length for resource " + this.getName() + " (" + this.getId() + ")", e);
300        }
301    }
302    
303    @Override
304    public UserIdentity getCreator() throws AmetysRepositoryException
305    {
306        try
307        {
308            Node authorNode = getNode().getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + CREATOR_NODE_NAME);
309            return new UserIdentity(authorNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":login").getString(), authorNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":population").getString());
310        }
311        catch (RepositoryException e)
312        {
313            throw new AmetysRepositoryException("Cannot get creator for resource " + this.getName() + " (" + this.getId() + ")", e);
314        }
315    }
316    
317    /**
318     * Get the author from old revision
319     * @param revision The revision
320     * @return The user identity of the author or <code>null</code> if not found
321     * @throws RepositoryException If an error occurred
322     */
323    public UserIdentity getAuthorFromRevision (String revision) throws RepositoryException
324    {
325        try
326        {
327            switchToRevision(revision);
328            
329            VersionHistory history = getVersionHistory();
330            Node versionNode = history.getVersion(revision);
331            Node frozenNode = versionNode.getNode(JcrConstants.JCR_FROZENNODE);
332            
333            if (frozenNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + CREATOR_NODE_NAME))
334            {
335                Node authorNode = frozenNode.getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + CREATOR_NODE_NAME);
336                return new UserIdentity(authorNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":login").getString(), authorNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":population").getString());
337            }
338            return null;
339        }
340        catch (RepositoryException e)
341        {
342            throw new AmetysRepositoryException("Unable to get author from revision: " + revision + " of resource " + this.getName() + " (" + this.getId() + ")", e);
343        }
344    }
345    
346    @Override
347    public String[] getKeywords() throws AmetysRepositoryException
348    {
349        Node fileNode = getNode();
350        try
351        {
352            if (!fileNode.hasProperty("ametys:keywords"))
353            {
354                return new String[0];
355            }
356            
357            Value[] values = fileNode.getProperty("ametys:keywords").getValues();
358            String[] result = new String[values.length];
359            
360            for (int i = 0; i < values.length; i++)
361            {
362                result[i] = values[i].getString();
363            }
364            
365            return result;
366        }
367        catch (RepositoryException e)
368        {
369            throw new AmetysRepositoryException("Cannot get keywords for resource " + this.getName() + " (" + this.getId() + ")", e);
370        }
371    }
372    
373    @Override
374    public String getKeywordsAsString() throws AmetysRepositoryException
375    {
376        Node fileNode = getNode();
377        try
378        {
379            if (!fileNode.hasProperty("ametys:keywords"))
380            {
381                return StringUtils.EMPTY;
382            }
383            
384            StringBuilder sb = new StringBuilder();
385            Value[] values = fileNode.getProperty("ametys:keywords").getValues();
386            
387            for (Value value : values)
388            {
389                if (sb.length() > 0)
390                {
391                    sb.append(", ");
392                }
393                
394                sb.append(value.getString());
395            }
396            
397            return sb.toString();
398        }
399        catch (RepositoryException e)
400        {
401            throw new AmetysRepositoryException("Cannot get keywords for resource " + this.getName() + " (" + this.getId() + ")", e);
402        }
403    }
404    
405    @Override
406    protected void restoreFromNode(Node node) throws RepositoryException
407    {
408        super.restoreFromNode(node);
409        
410        // First remove node
411        NodeIterator nit = getBaseNode().getNodes(JcrConstants.JCR_CONTENT);
412        while (nit.hasNext())
413        {
414            nit.nextNode().remove();
415        }
416        
417        NodeIterator new_nit = node.getNodes(JcrConstants.JCR_CONTENT);
418        while (new_nit.hasNext())
419        {
420            copyNode(getBaseNode(), new_nit.nextNode());
421        }
422    }
423    
424    @Override
425    public String getResourcePath() throws AmetysRepositoryException
426    {
427        return ((ExplorerNode) getParent()).getExplorerPath() + "/" + getName();
428    }
429    
430    @Override
431    public AmetysObject copyTo(ModifiableTraversableAmetysObject parent, String name) throws AmetysRepositoryException
432    {
433        try
434        {
435            String nodeTypeName = NodeTypeHelper.getNodeTypeName(getNode());
436            
437            JCRResource copiedResource = parent.createChild(name, nodeTypeName);
438            
439            copiedResource.setKeywords(getKeywords());
440            copiedResource.setData(getInputStream(), getMimeType(), getLastModified(), getCreator());
441            
442            parent.saveChanges();
443            
444            return copiedResource;
445        }
446        catch (RepositoryException e)
447        {
448            throw new AmetysRepositoryException("Error copying the collection " + getId() + " into " + parent.getId(), e);
449        }
450    }
451    
452    @Override
453    public AmetysObject copyTo(ModifiableTraversableAmetysObject parent, String name, List<String> restrictTo) throws AmetysRepositoryException
454    {
455        return copyTo(parent, name);
456    }
457    
458    // Dublin Core metadata. //
459    
460    @Override
461    public String getDCTitle() throws AmetysRepositoryException
462    {
463        return DublinCoreHelper.getDCTitle(this, getName());
464    }
465    
466    @Override
467    public void setDCTitle(String title) throws AmetysRepositoryException
468    {
469        DublinCoreHelper.setDCTitle(this, title);
470    }
471    
472    @Override
473    public String getDCCreator() throws AmetysRepositoryException
474    {
475        return DublinCoreHelper.getDCCreator(this);
476    }
477    
478    @Override
479    public void setDCCreator(String creator) throws AmetysRepositoryException
480    {
481        DublinCoreHelper.setDCCreator(this, creator);
482    }
483    
484    @Override
485    public String[] getDCSubject() throws AmetysRepositoryException
486    {
487        return DublinCoreHelper.getDCSubject(this, getKeywords());
488    }
489    
490    @Override
491    public void setDCSubject(String[] subject) throws AmetysRepositoryException
492    {
493        DublinCoreHelper.setDCSubject(this, subject);
494    }
495    
496    @Override
497    public String getDCDescription() throws AmetysRepositoryException
498    {
499        return DublinCoreHelper.getDCDescription(this);
500    }
501    
502    @Override
503    public void setDCDescription(String description) throws AmetysRepositoryException
504    {
505        DublinCoreHelper.setDCDescription(this, description);
506    }
507    
508    @Override
509    public String getDCPublisher() throws AmetysRepositoryException
510    {
511        return DublinCoreHelper.getDCPublisher(this);
512    }
513    
514    @Override
515    public void setDCPublisher(String publisher) throws AmetysRepositoryException
516    {
517        DublinCoreHelper.setDCPublisher(this, publisher);
518    }
519    
520    @Override
521    public String getDCContributor() throws AmetysRepositoryException
522    {
523        return DublinCoreHelper.getDCContributor(this, UserIdentity.userIdentityToString(getCreator()));
524    }
525    
526    @Override
527    public void setDCContributor(String contributor) throws AmetysRepositoryException
528    {
529        DublinCoreHelper.setDCContributor(this, contributor);
530    }
531    
532    @Override
533    public Date getDCDate() throws AmetysRepositoryException
534    {
535        return DublinCoreHelper.getDCDate(this, getLastModified());
536    }
537    
538    @Override
539    public void setDCDate(Date date) throws AmetysRepositoryException
540    {
541        DublinCoreHelper.setDCDate(this, date);
542    }
543    
544    @Override
545    public String getDCType() throws AmetysRepositoryException
546    {
547        return DublinCoreHelper.getDCType(this, _getDefaultDCType());
548    }
549    
550    private String _getDefaultDCType ()
551    {
552        String mimetype = getMimeType();
553        
554        if (mimetype == null)
555        {
556            return DCMITypes.TEXT;
557        }
558        else if (mimetype.startsWith("image"))
559        {
560            return DCMITypes.IMAGE;
561        }
562        else if (mimetype.startsWith("video") || "application/x-shockwave-flash".equals(mimetype))
563        {
564            return DCMITypes.INTERACTIVERESOURCE;
565        }
566        else if (mimetype.startsWith("audio"))
567        {
568            return DCMITypes.SOUND;
569        }
570        
571        return DCMITypes.TEXT;
572    }
573    
574    
575    @Override
576    public void setDCType(String type) throws AmetysRepositoryException
577    {
578        DublinCoreHelper.setDCType(this, type);
579    }
580    
581    @Override
582    public String getDCFormat() throws AmetysRepositoryException
583    {
584        return DublinCoreHelper.getDCFormat(this, getMimeType());
585    }
586    
587    @Override
588    public void setDCFormat(String format) throws AmetysRepositoryException
589    {
590        DublinCoreHelper.setDCFormat(this, format);
591    }
592    
593    @Override
594    public String getDCIdentifier() throws AmetysRepositoryException
595    {
596        return DublinCoreHelper.getDCIdentifier(this, getId());
597    }
598    
599    @Override
600    public void setDCIdentifier(String identifier) throws AmetysRepositoryException
601    {
602        DublinCoreHelper.setDCIdentifier(this, identifier);
603    }
604    
605    @Override
606    public String getDCSource() throws AmetysRepositoryException
607    {
608        return DublinCoreHelper.getDCSource(this);
609    }
610    
611    @Override
612    public void setDCSource(String source) throws AmetysRepositoryException
613    {
614        DublinCoreHelper.setDCSource(this, source);
615    }
616    
617    @Override
618    public String getDCLanguage() throws AmetysRepositoryException
619    {
620        return DublinCoreHelper.getDCLanguage(this);
621    }
622    
623    @Override
624    public void setDCLanguage(String language) throws AmetysRepositoryException
625    {
626        DublinCoreHelper.setDCLanguage(this, language);
627    }
628    
629    @Override
630    public String getDCRelation() throws AmetysRepositoryException
631    {
632        return DublinCoreHelper.getDCRelation(this);
633    }
634    
635    @Override
636    public void setDCRelation(String relation) throws AmetysRepositoryException
637    {
638        DublinCoreHelper.setDCRelation(this, relation);
639    }
640    
641    @Override
642    public String getDCCoverage() throws AmetysRepositoryException
643    {
644        return DublinCoreHelper.getDCCoverage(this, getDCLanguage());
645    }
646    
647    @Override
648    public void setDCCoverage(String coverage) throws AmetysRepositoryException
649    {
650        DublinCoreHelper.setDCCoverage(this, coverage);
651    }
652    
653    @Override
654    public String getDCRights() throws AmetysRepositoryException
655    {
656        return DublinCoreHelper.getDCRights(this);
657    }
658    
659    @Override
660    public void setDCRights(String rights) throws AmetysRepositoryException
661    {
662        DublinCoreHelper.setDCRights(this, rights);
663    }
664
665    public Date getCreationDate() throws AmetysRepositoryException
666    {
667        Node fileNode = getNode();
668        try
669        {
670            if (!fileNode.hasProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + CREATION_DATE))
671            {
672                return null;
673            }
674            
675            return fileNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + CREATION_DATE).getDate().getTime();
676        }
677        catch (RepositoryException e)
678        {
679            throw new AmetysRepositoryException("Cannot get creation date for resource " + this.getName() + " (" + this.getId() + ")", e);
680        }
681    }
682
683    public void setCreationDate(Date creationDate)
684    {
685        Node fileNode = getNode();
686        try
687        {
688            Calendar calendar = new GregorianCalendar();
689            calendar.setTime(creationDate);
690            fileNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":" + CREATION_DATE, calendar);
691        }
692        catch (RepositoryException e)
693        {
694            throw new AmetysRepositoryException("Cannot set create date for resource " + this.getName() + " (" + this.getId() + ")", e);
695        }
696    }
697
698    public UserIdentity getLastContributor() throws AmetysRepositoryException
699    {
700        try
701        {
702            Node authorNode = getNode().getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + CONTRIBUTOR_NODE_NAME);
703            return new UserIdentity(authorNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":login").getString(), authorNode.getProperty(RepositoryConstants.NAMESPACE_PREFIX + ":population").getString());
704        }
705        catch (RepositoryException e)
706        {
707            throw new AmetysRepositoryException("Cannot get last contributor for resource " + this.getName() + " (" + this.getId() + ")", e);
708        }
709    }
710
711    public void setLastContributor(UserIdentity lastContributor)
712    {
713        try
714        {
715            Node lastContributorNode = null;
716            if (getNode().hasNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + CONTRIBUTOR_NODE_NAME))
717            {
718                lastContributorNode = getNode().getNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + CONTRIBUTOR_NODE_NAME);
719            }
720            else
721            {
722                lastContributorNode = getNode().addNode(RepositoryConstants.NAMESPACE_PREFIX + ":" + CONTRIBUTOR_NODE_NAME, RepositoryConstants.USER_NODETYPE);
723            }
724            lastContributorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":login", lastContributor.getLogin());
725            lastContributorNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ":population", lastContributor.getPopulationId());
726        }
727        catch (RepositoryException e)
728        {
729            throw new AmetysRepositoryException("Cannot set last contributor for resource " + this.getName() + " (" + this.getId() + ")", e);
730        }
731    }
732    
733    public void tag(String tag) throws AmetysRepositoryException
734    {
735        TaggableAmetysObjectHelper.tag(this, tag);
736    }
737
738    public void untag(String tag) throws AmetysRepositoryException
739    {
740        TaggableAmetysObjectHelper.untag(this, tag);
741    }
742
743    public Set<String> getTags() throws AmetysRepositoryException
744    {
745        return TaggableAmetysObjectHelper.getTags(this);
746    }
747    
748    public TrashElement moveToTrash()
749    {
750        TrashElement trashElement = _getFactory()._getTrashElementDAO().createTrashElement(this, getName());
751        
752        // Clone the ametys object from the original session to trash session
753        Node storedNode = NodeHelper.cloneNode(getNode(), trashElement.getNode());
754        
755        try
756        {
757            storedNode.setProperty("ametys-internal:path", this.getParentPath());
758        }
759        catch (RepositoryException e)
760        {
761            throw new AmetysRepositoryException("failed to store the content path", e);
762        }
763
764        // Remove the node from the original session
765        remove();
766        
767        return trashElement;
768    }
769    
770    public JCRResource restoreFromTrash(MergeComputationMode mergeComputationMode) throws UnknownParentException, ConflictingNameException
771    {
772        try
773        {
774            Property property = getNode().getProperty("ametys-internal:path");
775            String parentPath = property.getValue().getString();
776            property.remove();
777            JCRResourcesCollection parent = ResourceHelper.getOrCreateResourcesCollection(_getFactory()._getResolver(), parentPath);
778            if (parent == null)
779            {
780                throw new UnknownParentException("No parent available at path " + parentPath);
781            }
782
783            JCRResource restoredResource = ResourceHelper.restoreResource(this, parent, mergeComputationMode, _getFactory()._getResourcesDAO());
784            
785            // Remove the node from the trash session
786            remove();
787            
788            restoredResource.saveChanges();
789            return restoredResource;
790        }
791        catch (RepositoryException e)
792        {
793            throw new AmetysRepositoryException("Failed to restore JCRResource with id '" + getId() + "'", e);
794        }
795    }
796}