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