001/*
002 *  Copyright 2015 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.web.clientsideelement;
017
018import java.time.ZonedDateTime;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.avalon.framework.activity.Initializable;
024import org.apache.avalon.framework.service.ServiceException;
025import org.apache.avalon.framework.service.ServiceManager;
026import org.quartz.JobKey;
027import org.quartz.SchedulerException;
028
029import org.ametys.core.observation.Event;
030import org.ametys.core.observation.ObservationManager;
031import org.ametys.core.schedule.Runnable;
032import org.ametys.core.ui.Callable;
033import org.ametys.core.ui.ClientSideElement;
034import org.ametys.core.user.UserIdentity;
035import org.ametys.plugins.core.schedule.Scheduler;
036import org.ametys.runtime.model.type.ElementType;
037import org.ametys.runtime.model.type.ModelItemTypeConstants;
038import org.ametys.web.ObservationConstants;
039import org.ametys.web.publication.PublishPageRunnable;
040import org.ametys.web.publication.UnpublishPageRunnable;
041import org.ametys.web.repository.page.ModifiablePage;
042import org.ametys.web.repository.page.Page;
043import org.ametys.web.repository.page.PageDataTypeExtensionPoint;
044import org.ametys.web.repository.page.jcr.DefaultPage;
045
046/**
047 * This {@link ClientSideElement} creates a button representing the schedule publication status of a page.
048 *
049 */
050public class ScheduledPageClientSideElement extends AbstractPageClientSideElement implements Initializable
051{
052    private ObservationManager _observationManager;
053    private Scheduler _scheduler;
054    private PageDataTypeExtensionPoint _pageDataTypeExtensionPoint;
055    private ElementType<ZonedDateTime> _dateElementType;
056
057    @Override
058    public void service(ServiceManager smanager) throws ServiceException
059    {
060        super.service(smanager);
061        _observationManager = (ObservationManager) smanager.lookup(ObservationManager.ROLE);
062        _scheduler = (Scheduler) smanager.lookup(Scheduler.ROLE);
063        _pageDataTypeExtensionPoint = (PageDataTypeExtensionPoint) smanager.lookup(PageDataTypeExtensionPoint.ROLE);
064    }
065
066    @SuppressWarnings("unchecked")
067    public void initialize() throws Exception
068    {
069        _dateElementType = (ElementType<ZonedDateTime>) _pageDataTypeExtensionPoint.getExtension(ModelItemTypeConstants.DATETIME_TYPE_ID);
070    }
071    
072    /**
073     * Get the publication dates of a page
074     * @param pageId the id of the page
075     * @return a map
076     */
077    @Callable
078    public Map<String, Object> getPublicationDates(String pageId)
079    {
080        Map<String, Object> dates = new HashMap<> ();
081        
082        Page page = _resolver.resolveById(pageId);
083        
084        ZonedDateTime startDate = page.getValue(DefaultPage.METADATA_PUBLICATION_START_DATE);
085        if (startDate != null)
086        {
087            dates.put("startDate", _dateElementType.toString(startDate));
088        }
089        
090        ZonedDateTime endDate = page.getValue(DefaultPage.METADATA_PUBLICATION_END_DATE);
091        if (endDate != null)
092        {
093            dates.put("endDate", _dateElementType.toString(endDate));
094        }
095        
096        return dates;
097    }
098    
099    /**
100     * Set date of publication of pages
101     * @param pageIds The ids of pages to update
102     * @param startDateAsStr The start date. Can be null.
103     * @param endDateAsStr The end date. Can be null.
104     * @return true if operation has succeeded.
105     */
106    @Callable
107    public boolean setPublicationDate (List<String> pageIds, String startDateAsStr, String endDateAsStr)
108    {
109        ZonedDateTime startDate = startDateAsStr != null ? _dateElementType.castValue(startDateAsStr) : null;
110        ZonedDateTime endDate = endDateAsStr != null ? _dateElementType.castValue(endDateAsStr) : null;
111        
112        for (String id : pageIds)
113        {
114            ModifiablePage page = _resolver.resolveById(id);
115            UserIdentity userIdentity = _currentUserProvider.getUser();
116            
117            try
118            {
119                _scheduleStartDate(page, userIdentity, startDate);
120                _scheduleEndDate(page, userIdentity, endDate);
121                
122                page.saveChanges();
123                
124                // Notify listeners in case the page has to be publish or unpublish right now
125                Map<String, Object> eventParams = new HashMap<>();
126                eventParams.put(ObservationConstants.ARGS_PAGE, page);
127                _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_CHANGED, _currentUserProvider.getUser(), eventParams));
128                
129                _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_UPDATED, _currentUserProvider.getUser(), eventParams));
130            }
131            catch (SchedulerException e)
132            {
133                if (getLogger().isErrorEnabled())
134                {
135                    getLogger().error("An error occured when trying to schedule the publishing of the page " + id, e);
136                }
137            }
138        }
139        
140        return true;
141    }
142    
143    private void _scheduleStartDate (ModifiablePage page, UserIdentity userIdentity, ZonedDateTime startDate) throws SchedulerException
144    {
145        // Publish job
146        Runnable publishRunnable = new PublishPageRunnable(page.getId(), page.getTitle(), userIdentity, startDate);
147        JobKey jobKey = new JobKey(publishRunnable.getId(), Scheduler.JOB_GROUP);
148        
149        if (_scheduler.getScheduler().checkExists(jobKey))
150        {
151            // Remove any existing corresponding job
152            getLogger().debug("Removing the existing job for publishing {} ({})", page.getTitle(), page.getId());
153            _scheduler.getScheduler().deleteJob(jobKey);
154        }
155        
156        if (startDate != null)
157        {
158            page.setValue(DefaultPage.METADATA_PUBLICATION_START_DATE, startDate);
159            
160            if (startDate.isAfter(ZonedDateTime.now()))
161            {
162                // No necessary to run job if date is in the past
163                getLogger().debug("Creating a job for publishing {} ({}) on {}", page.getTitle(), page.getId(), startDate);
164                _scheduler.scheduleJob(publishRunnable);
165            }
166            else
167            {
168                getLogger().debug("No job needed for publishing {} ({}) on {}", page.getTitle(), page.getId(), startDate);
169            }
170        }
171        else
172        {
173            page.removeValue(DefaultPage.METADATA_PUBLICATION_START_DATE);
174            getLogger().debug("No schedule for publishing was required for {} ({})", page.getTitle(), page.getId());
175        }
176    }
177    
178    private void _scheduleEndDate (ModifiablePage page, UserIdentity userIdentity, ZonedDateTime endDate) throws SchedulerException
179    {
180        // Unpublish job
181        Runnable unpublishRunnable = new UnpublishPageRunnable(page.getId(), page.getTitle(), userIdentity, endDate);
182        JobKey jobKey = new JobKey(unpublishRunnable.getId(), Scheduler.JOB_GROUP);
183        
184        if (_scheduler.getScheduler().checkExists(jobKey))
185        {
186            // Remove any existing corresponding job
187            getLogger().debug("Removing the existing job for unpublishing {} ({})", page.getTitle(), page.getId());
188            _scheduler.getScheduler().deleteJob(jobKey);
189        }
190        
191        if (endDate != null)
192        {
193            page.setValue(DefaultPage.METADATA_PUBLICATION_END_DATE, endDate);
194            
195            if (endDate.isAfter(ZonedDateTime.now()))
196            {
197                // No necessary to run job if date is in the past
198                getLogger().debug("Creating a job for unpublishing {} ({}) on {}", page.getTitle(), page.getId(), endDate);
199                _scheduler.scheduleJob(unpublishRunnable);
200            }
201            else
202            {
203                getLogger().debug("No job needed for unpublishing {} ({}) on {}", page.getTitle(), page.getId(), endDate);
204            }
205        }
206        else
207        {
208            page.removeValue(DefaultPage.METADATA_PUBLICATION_END_DATE);
209            getLogger().debug("No schedule for unpublishing was required for {} ({})", page.getTitle(), page.getId());
210        }
211    }
212}