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            // If date is in past, unecessary to run the job
152            _scheduler.getScheduler().deleteJob(jobKey);
153        }
154        
155        if (startDate != null)
156        {
157            page.setValue(DefaultPage.METADATA_PUBLICATION_START_DATE, startDate);
158            
159            if (startDate.isAfter(ZonedDateTime.now()))
160            {
161                // No necessary to run job if date is in the past
162                _scheduler.scheduleJob(publishRunnable);
163            }
164        }
165        else if (page.hasValue(DefaultPage.METADATA_PUBLICATION_START_DATE))
166        {
167            page.removeValue(DefaultPage.METADATA_PUBLICATION_START_DATE);
168        }
169    }
170    
171    private void _scheduleEndDate (ModifiablePage page, UserIdentity userIdentity, ZonedDateTime endDate) throws SchedulerException
172    {
173        // Unpublish job
174        Runnable unpublishRunnable = new UnpublishPageRunnable(page.getId(), page.getTitle(), userIdentity, endDate);
175        JobKey jobKey = new JobKey(unpublishRunnable.getId(), Scheduler.JOB_GROUP);
176        
177        if (_scheduler.getScheduler().checkExists(jobKey))
178        {
179            // A end publication was already scheduled, delete the job
180            _scheduler.getScheduler().deleteJob(jobKey);
181        }
182        
183        if (endDate != null)
184        {
185            page.setValue(DefaultPage.METADATA_PUBLICATION_END_DATE, endDate);
186            
187            if (endDate.isAfter(ZonedDateTime.now()))
188            {
189                // No necessary to run job if date is in the past
190                _scheduler.scheduleJob(unpublishRunnable);
191            }
192        }
193        else if (page.hasValue(DefaultPage.METADATA_PUBLICATION_END_DATE))
194        {
195            page.removeValue(DefaultPage.METADATA_PUBLICATION_END_DATE);
196        }
197    }
198}