001/*
002 *  Copyright 2014 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.clientsideelement;
017
018import java.util.ArrayList;
019import java.util.Date;
020import java.util.HashMap;
021import java.util.HashSet;
022import java.util.List;
023import java.util.Map;
024import java.util.Set;
025
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.commons.lang3.StringUtils;
029
030import org.ametys.cms.ObservationConstants;
031import org.ametys.cms.alerts.AlertsConstants;
032import org.ametys.cms.content.archive.ArchiveConstants;
033import org.ametys.cms.repository.Content;
034import org.ametys.core.observation.Event;
035import org.ametys.core.observation.ObservationManager;
036import org.ametys.core.ui.Callable;
037import org.ametys.core.ui.StaticClientSideElement;
038import org.ametys.plugins.repository.AmetysObjectResolver;
039import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
040import org.ametys.plugins.repository.version.ModifiableMetadataAwareVersionableAmetysObject;
041import org.ametys.runtime.config.Config;
042import org.ametys.runtime.i18n.I18nizableText;
043import org.ametys.runtime.parameter.ParameterHelper;
044import org.ametys.runtime.parameter.ParameterHelper.ParameterType;
045
046/**
047 * This element creates a ribbon button to schedule the archiving of a content
048 */
049public class ScheduleArchivingClientSideElement extends StaticClientSideElement
050{
051    /** I18n error key : content type error */
052    private static final String __I18N_KEY_CONTENT_TYPE_ERROR = "PLUGINS_CMS_ARCHIVE_SCHEDULE_CONTENT_TYPE_ERROR";
053
054    /** I18n error key : unexpected error */
055    private static final String __I18N_KEY_UNEXPECTED_ERROR = "PLUGINS_CMS_ARCHIVE_SCHEDULE_UNEXPECTED_ERROR";
056    
057    private ObservationManager _observationManager;
058    private AmetysObjectResolver _resolver;
059
060    @Override
061    public void service(ServiceManager manager) throws ServiceException
062    {
063        super.service(manager);
064        _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE);
065        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
066    }
067    
068    /**
069     * Set or remove the <code>ArchiveConstants.META_ARCHIVE_SCHEDULED_DATE</code> on a content.
070     * @param contentIds The ids of contents
071     * @param scheduledDateAsStr The schedule date as string
072     * @return the successful and failed contents
073     */
074    @Callable
075    public Map<String, Object> setScheduledArchivingDate (List<String> contentIds, String scheduledDateAsStr)
076    {
077        Map<String, Object> results = new HashMap<>();
078        
079        // Error list
080        List<I18nizableText> failedContents = new ArrayList<>();
081
082        // Success list, containing the contentId that have been successfully scheduled
083        List<String> successContents = new ArrayList<>();
084
085        Date scheduledDate = (Date) ParameterHelper.castValue(scheduledDateAsStr, ParameterType.DATE);
086        
087        if (StringUtils.isNotEmpty(scheduledDateAsStr))
088        {
089            if (scheduledDate == null)
090            {
091                getLogger().error("Cannot cast value '" + scheduledDate + "' into type 'date'");
092                throw new IllegalArgumentException("Invalid format for date value '" + scheduledDate + "'");
093            }
094
095            // Date must be set at least after tomorrow
096            /*if (scheduledDate.compareTo(new DateMidnight().plusDays(1).toDate()) < 0)
097            {
098                getLogger().error("The scheduled date '" + scheduledDate + "' cannot be set before the current date");
099                throw new IllegalArgumentException("The scheduled date '" + scheduledDate + "' cannot be set before the current date");
100            }*/
101        }
102        
103        // For each content
104        for (String contentId : contentIds)
105        {
106            try
107            {
108                Content content = _resolver.resolveById(contentId);
109
110                if (content instanceof ModifiableMetadataAwareVersionableAmetysObject)
111                {
112                    ModifiableMetadataAwareVersionableAmetysObject uContent = (ModifiableMetadataAwareVersionableAmetysObject) content;
113                    ModifiableCompositeMetadata metadata = uContent.getUnversionedMetadataHolder();
114
115                    // Remove META_ARCHIVE_SCHEDULED_DATE and
116                    // SCHEDULED_ARCHIVING_REMINDER_LAST_DATE if date is empty.
117                    if (StringUtils.isEmpty(scheduledDateAsStr))
118                    {
119                        if (metadata.hasMetadata(ArchiveConstants.META_ARCHIVE_SCHEDULED_DATE))
120                        {
121                            metadata.removeMetadata(ArchiveConstants.META_ARCHIVE_SCHEDULED_DATE);
122
123                            if (getLogger().isInfoEnabled())
124                            {
125                                getLogger().info("Content with id : '" + uContent.getId() + "' does not have a scheduled archiving date anymore.");
126                            }
127                        }
128
129                        if (metadata.hasMetadata(AlertsConstants.SCHEDULED_ARCHIVING_REMINDER_LAST_DATE))
130                        {
131                            metadata.removeMetadata(AlertsConstants.SCHEDULED_ARCHIVING_REMINDER_LAST_DATE);
132                        }
133                    }
134                    else
135                    {
136                        metadata.setMetadata(ArchiveConstants.META_ARCHIVE_SCHEDULED_DATE, scheduledDate);
137                    }
138
139                    if (uContent.needsSave())
140                    {
141                        uContent.saveChanges();
142
143                        // report success
144                        successContents.add(uContent.getId());
145
146                        // Notify observer
147                        Map<String, Object> eventParams = new HashMap<>();
148                        eventParams.put(ObservationConstants.ARGS_CONTENT, content);
149                        eventParams.put(ObservationConstants.ARGS_CONTENT_ID, contentId);
150
151                        _observationManager.notify(new Event(ObservationConstants.EVENT_CONTENT_MODIFIED, _currentUserProvider.getUser(), eventParams));
152                    }
153                }
154                else
155                {
156                    getLogger().error("Unable to set a scheduled archiving for the content : '" + content.getId() + "'. It is not a ModifiableMetadataAwareVersionableAmetysObject.");
157
158                    // report error
159                    List<String> i18nParams = new ArrayList<>();
160                    i18nParams.add(content.getTitle());
161                    failedContents.add(new I18nizableText("plugin.cms", __I18N_KEY_CONTENT_TYPE_ERROR, i18nParams));
162
163                }
164            }
165            catch (Exception e)
166            {
167                getLogger().error("Unexpected exception while trying to schedule an archiving for the content : '" + contentId + "'.");
168
169                // report error
170                List<String> i18nParams = new ArrayList<>();
171                i18nParams.add(contentId);
172                failedContents.add(new I18nizableText("plugin.cms", __I18N_KEY_UNEXPECTED_ERROR, i18nParams));
173            }
174        }
175
176        results.put("error", failedContents);
177        results.put("success", successContents);
178        
179        return results;
180    }
181
182    /**
183     * Gets the scheduled archiving dates of one or several contents
184     * @param contentIds the ids of the contents
185     * @return result the server's response in JSON
186     */
187    @Callable
188    public Map<String, Object> getScheduledArchivingDate(List<String> contentIds)
189    {
190        Map<String, Object> results = new HashMap<> ();
191        Set<Date> dates = new HashSet<>();
192    
193        // Retrieves the date for each content
194        for (String contentId : contentIds)
195        {
196            Content content = _resolver.resolveById(contentId);
197    
198            if (content instanceof ModifiableMetadataAwareVersionableAmetysObject)
199            {
200                Date date = ((ModifiableMetadataAwareVersionableAmetysObject) content).getUnversionedMetadataHolder().getDate(ArchiveConstants.META_ARCHIVE_SCHEDULED_DATE, null);
201                dates.add(date);
202            }
203        }
204    
205        // Retrieves the closest date from current time.
206        dates.remove(null);
207        Date scheduledDate = getDateNearestToCurrent(dates);
208    
209        // Generate the JSON
210        Map<String, Object> content = new HashMap<> ();
211        if (scheduledDate != null)
212        {
213            content.put("date", ParameterHelper.valueToString(scheduledDate));
214        }
215        results.put("content", content);
216        
217        return results;
218    }
219    
220    private Date getDateNearestToCurrent(Set<Date> dates)
221    {
222        Date returnDate = null;
223        Date currentDate = new Date();
224
225        for (Date date : dates)
226        {
227            if (date.compareTo(currentDate) >= 0 && (returnDate == null || date.compareTo(returnDate) < 0))
228            {
229                returnDate = date;
230            }
231        }
232        return returnDate;
233    }
234    
235    @Override
236    public List<Script> getScripts(boolean ignoreRights, Map<String, Object> contextParameters)
237    {
238        if (Config.getInstance().getValueAsBoolean("archive.scheduler.enabled"))
239        {
240            return super.getScripts(ignoreRights, contextParameters);
241        }
242        
243        return new ArrayList<>();
244    }
245}