001/*
002 *  Copyright 2012 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.cms.repository;
017
018import java.util.Date;
019import java.util.GregorianCalendar;
020import java.util.List;
021import java.util.Locale;
022import java.util.Map;
023
024import javax.jcr.Node;
025import javax.jcr.RepositoryException;
026
027import org.apache.avalon.framework.component.Component;
028
029import org.ametys.cms.content.references.OutgoingReferences;
030import org.ametys.cms.data.type.ModelItemTypeConstants;
031import org.ametys.core.user.UserIdentity;
032import org.ametys.plugins.repository.AmetysRepositoryException;
033import org.ametys.plugins.repository.RepositoryConstants;
034import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData;
035import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData;
036import org.ametys.runtime.model.ElementDefinition;
037import org.ametys.runtime.model.ModelItem;
038
039
040/**
041 * Provides helper methods to use the {@link ModifiableContent} API on {@link DefaultContent}s.
042 */
043public class ModifiableContentHelper implements Component
044{
045    /** The Avalon role */
046    public static final String ROLE = ModifiableContentHelper.class.getName();
047
048    /**
049     * Set a {@link DefaultContent} type.
050     * @param content the {@link DefaultContent} to set.
051     * @param type the type.
052     * @throws AmetysRepositoryException if an error occurs.
053     */
054    public void setType(DefaultContent content, String type) throws AmetysRepositoryException
055    {
056        setTypes(content, new String[] {type});
057    }
058    
059    /**
060     * Set {@link DefaultContent} types.
061     * @param content the {@link DefaultContent} to set.
062     * @param types the types.
063     * @throws AmetysRepositoryException if an error occurs.
064     */
065    public void setTypes(DefaultContent content, String[] types) throws AmetysRepositoryException
066    {
067        Node node = content.getNode();
068        
069        try
070        {
071            content._checkLock();
072            node.setProperty(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + DefaultContent.METADATA_CONTENTTYPE, types);
073        }
074        catch (RepositoryException e)
075        {
076            throw new AmetysRepositoryException("Unable to set contentType property", e);
077        }
078    }
079    
080    /**
081     * Set {@link DefaultContent} mixins.
082     * @param content the {@link DefaultContent} to set.
083     * @param mixins the mixins.
084     * @throws AmetysRepositoryException if an error occurs.
085     */
086    public void setMixinTypes(DefaultContent content, String[] mixins) throws AmetysRepositoryException
087    {
088        Node node = content.getNode();
089        
090        try
091        {
092            content._checkLock();
093            node.setProperty(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + DefaultContent.METADATA_MIXINCONTENTTYPES, mixins);
094        }
095        catch (RepositoryException e)
096        {
097            throw new AmetysRepositoryException("Unable to set mixins property", e);
098        }
099    }
100    
101    /**
102     * Set a {@link DefaultContent} language.
103     * @param content the {@link DefaultContent} to modify.
104     * @param language the language to set.
105     * @throws AmetysRepositoryException if an error occurs.
106     */
107    public void setLanguage(DefaultContent content, String language) throws AmetysRepositoryException
108    {
109        Node node = content.getNode();
110        
111        try
112        {
113            content._checkLock();
114            node.setProperty(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + DefaultContent.METADATA_LANGUAGE, language);
115        }
116        catch (RepositoryException e)
117        {
118            throw new AmetysRepositoryException("Unable to set language property", e);
119        }
120    }
121    
122    /**
123     * Set a {@link DefaultContent} title for the given locale. 
124     * @param content the {@link DefaultContent} to set.
125     * @param title the title to set.
126     * @param locale The locale. Can be null if the content is not a multilingual content.
127     * @throws AmetysRepositoryException if an error occurs.
128     */
129    public void setTitle(DefaultContent content, String title, Locale locale) throws AmetysRepositoryException
130    {
131        ModelItem titleDefinition = content.getDefinition(Content.ATTRIBUTE_TITLE);
132        ModifiableRepositoryData repositoryData = new JCRRepositoryData(content.getNode());
133        if (titleDefinition instanceof ElementDefinition && ModelItemTypeConstants.MULTILINGUAL_STRING_ELEMENT_TYPE_ID.equals(((ElementDefinition) titleDefinition).getType().getId()))
134        {
135            if (locale == null)
136            {
137                throw new IllegalArgumentException("Cannot set a title with null locale on a multilingual title");
138            }
139            
140            ModifiableRepositoryData titleRepositoryData;
141            if (repositoryData.hasValue(Content.ATTRIBUTE_TITLE))
142            {
143                titleRepositoryData = repositoryData.getRepositoryData(Content.ATTRIBUTE_TITLE);
144            }
145            else
146            {
147                titleRepositoryData = repositoryData.addRepositoryData(Content.ATTRIBUTE_TITLE, RepositoryConstants.MULTILINGUAL_STRING_METADATA_NODETYPE);
148            }
149            titleRepositoryData.setValue(locale.toString(), title);
150        }
151        else
152        {
153            repositoryData.setValue(Content.ATTRIBUTE_TITLE, title);
154        }
155    }
156    
157    /**
158     * Set the title of non-multilingual {@link DefaultContent}.
159     * Be careful! Use only if content's title is not a multilingual string. If not sure use {@link #setTitle(DefaultContent, String, Locale)} instead.
160     * @param content the {@link DefaultContent} to set.
161     * @param title the title to set.
162     * @throws AmetysRepositoryException if an error occurs.
163     */
164    public void setTitle(DefaultContent content, String title) throws AmetysRepositoryException
165    {
166        setTitle(content, title, null);
167    }
168    
169    /**
170     * Copy the title of the source content to the target content
171     * @param srcContent The source content
172     * @param targetContent The target content
173     * @throws AmetysRepositoryException if an error occurs.
174     */
175    public void copyTitle(Content srcContent, ModifiableContent targetContent) throws AmetysRepositoryException
176    {
177        targetContent.setValue(Content.ATTRIBUTE_TITLE, srcContent.getValue(Content.ATTRIBUTE_TITLE));
178    }
179    
180    /**
181     * Set a {@link DefaultContent} user.
182     * @param content the {@link DefaultContent} to set.
183     * @param user the user to set.
184     * @throws AmetysRepositoryException if an error occurs.
185     */
186    public void setCreator(DefaultContent content, UserIdentity user) throws AmetysRepositoryException
187    {
188        try
189        {
190            // TODO CMS-9336 All the metatadata here should be store using types
191            ModifiableRepositoryData repositoryData = new JCRRepositoryData(content.getNode());
192            ModifiableRepositoryData creatorRepositoryData;
193            if (repositoryData.hasValue(DefaultContent.METADATA_CREATOR))
194            {
195                creatorRepositoryData = repositoryData.getRepositoryData(DefaultContent.METADATA_CREATOR);
196            }
197            else
198            {
199                creatorRepositoryData = repositoryData.addRepositoryData(DefaultContent.METADATA_CREATOR, RepositoryConstants.USER_NODETYPE);
200            }
201            creatorRepositoryData.setValue("login", user.getLogin());
202            creatorRepositoryData.setValue("population", user.getPopulationId());
203        }
204        catch (AmetysRepositoryException e)
205        {
206            throw new AmetysRepositoryException("Error setting the creator property.", e);
207        }
208    }
209    
210    /**
211     * Set a {@link DefaultContent} creation date.
212     * @param content the {@link DefaultContent} to set.
213     * @param creationDate the creation date to set.
214     * @throws AmetysRepositoryException if an error occurs.
215     */
216    public void setCreationDate(DefaultContent content, Date creationDate) throws AmetysRepositoryException
217    {
218        ModifiableRepositoryData repositoryData = new JCRRepositoryData(content.getNode());
219        GregorianCalendar calendar = new GregorianCalendar();
220        calendar.setTime(creationDate);
221        repositoryData.setValue(DefaultContent.METADATA_CREATION, calendar);
222    }
223    
224    /**
225     * Set a {@link DefaultContent} contributor.
226     * @param content the {@link DefaultContent} to set.
227     * @param user the contributor to set.
228     * @throws AmetysRepositoryException if an error occurs.
229     */
230    public void setLastContributor(DefaultContent content, UserIdentity user) throws AmetysRepositoryException
231    {
232        try
233        {
234            ModifiableRepositoryData repositoryData = new JCRRepositoryData(content.getNode());
235            ModifiableRepositoryData contributorRepositoryData;
236            if (repositoryData.hasValue(DefaultContent.METADATA_CONTRIBUTOR))
237            {
238                contributorRepositoryData = repositoryData.getRepositoryData(DefaultContent.METADATA_CONTRIBUTOR);
239            }
240            else
241            {
242                contributorRepositoryData = repositoryData.addRepositoryData(DefaultContent.METADATA_CONTRIBUTOR, RepositoryConstants.USER_NODETYPE);
243            }
244            contributorRepositoryData.setValue("login", user.getLogin());
245            contributorRepositoryData.setValue("population", user.getPopulationId());
246        }
247        catch (AmetysRepositoryException e)
248        {
249            throw new AmetysRepositoryException("Error setting the last contributor property.", e);
250        }
251    }
252    
253    /**
254     * Set a {@link DefaultContent} last modification date.
255     * @param content the {@link DefaultContent} to set.
256     * @param lastModified the last modification date to set.
257     * @throws AmetysRepositoryException if an error occurs.
258     */
259    public void setLastModified(DefaultContent content, Date lastModified) throws AmetysRepositoryException
260    {
261        ModifiableRepositoryData repositoryData = new JCRRepositoryData(content.getNode());
262        GregorianCalendar calendar = new GregorianCalendar();
263        calendar.setTime(lastModified);
264        repositoryData.setValue(DefaultContent.METADATA_MODIFIED, calendar);
265    }
266    
267    /**
268     * Set a {@link DefaultContent} first validation date.
269     * @param content the {@link DefaultContent} to set.
270     * @param validationDate the first validation date.
271     * @throws AmetysRepositoryException if an error occurs.
272     */
273    public void setFirstValidationDate(DefaultContent content, Date validationDate) throws AmetysRepositoryException
274    {
275        ModifiableRepositoryData repositoryData = new JCRRepositoryData(content.getNode());
276        GregorianCalendar calendar = new GregorianCalendar();
277        calendar.setTime(validationDate);
278        repositoryData.setValue(DefaultContent.METADATA_FIRST_VALIDATION, calendar);
279    }
280    
281    /**
282     * Set a {@link DefaultContent} last validation date.
283     * @param content the {@link DefaultContent} to set.
284     * @param validationDate the last validation date.
285     * @throws AmetysRepositoryException if an error occurs.
286     */
287    public void setLastValidationDate(DefaultContent content, Date validationDate) throws AmetysRepositoryException
288    {
289        ModifiableRepositoryData repositoryData = new JCRRepositoryData(content.getNode());
290        GregorianCalendar calendar = new GregorianCalendar();
291        calendar.setTime(validationDate);
292        repositoryData.setValue(DefaultContent.METADATA_LAST_VALIDATION, calendar);
293    }
294    
295    /**
296     * Set a {@link DefaultContent} last major validation date.
297     * @param content the {@link DefaultContent} to set.
298     * @param validationDate the last major validation date.
299     * @throws AmetysRepositoryException if an error occurs.
300     */
301    public void setLastMajorValidationDate(DefaultContent content, Date validationDate) throws AmetysRepositoryException
302    {
303        ModifiableRepositoryData repositoryData = new JCRRepositoryData(content.getNode());
304        GregorianCalendar calendar = new GregorianCalendar();
305        calendar.setTime(validationDate);
306        repositoryData.setValue(DefaultContent.METADATA_LAST_MAJORVALIDATION, calendar);
307    }
308    
309    /**
310     * Store the outgoing references on the content.
311     * @param content The content concerned by these outgoing references.
312     * @param references A non null map of outgoing references grouped by metadata (key are metadata path)
313     * @throws AmetysRepositoryException if an error occurs.
314     */
315    public void setOutgoingReferences(DefaultContent content, Map<String, OutgoingReferences> references) throws AmetysRepositoryException
316    {
317        try
318        {
319            Node contentNode = content.getNode();
320            if (contentNode.hasNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + DefaultContent.METADATA_ROOT_OUTGOING_REFERENCES))
321            {
322                contentNode.getNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + DefaultContent.METADATA_ROOT_OUTGOING_REFERENCES).remove();
323            }
324            
325            if (references.size() != 0)
326            {
327                Node rootOutgoingRefsNode = contentNode.addNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + DefaultContent.METADATA_ROOT_OUTGOING_REFERENCES);
328                for (String path : references.keySet())
329                {
330                    // Outgoing references node creation (for the given path)
331                    Node outgoingRefsNode = rootOutgoingRefsNode.addNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + DefaultContent.METADATA_OUTGOING_REFERENCES);
332                    outgoingRefsNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ':' + DefaultContent.METADATA_OUTGOING_REFERENCES_PATH_PROPERTY, path);
333                    
334                    // Reference nodes per type
335                    OutgoingReferences outgoingReferences = references.get(path);
336                    
337                    for (String type : outgoingReferences.keySet())
338                    {
339                        List<String> referenceValues = outgoingReferences.get(type);
340                        if (referenceValues != null && !referenceValues.isEmpty())
341                        {
342                            Node outgoingReferenceNode = outgoingRefsNode.addNode(type, RepositoryConstants.NAMESPACE_PREFIX + ':' + DefaultContent.METADATA_OUTGOING_REFERENCE_NODETYPE);
343                            outgoingReferenceNode.setProperty(RepositoryConstants.NAMESPACE_PREFIX + ':' + DefaultContent.METADATA_OUTGOING_REFERENCE_PROPERTY, referenceValues.toArray(new String[referenceValues.size()]));
344                        }
345                    }
346                }
347            }
348        }
349        catch (RepositoryException e)
350        {
351            throw new AmetysRepositoryException(e);
352        }
353    }
354}