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