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