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.text.DateFormat; 019import java.util.ArrayList; 020import java.util.Calendar; 021import java.util.Date; 022import java.util.GregorianCalendar; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Locale; 026import java.util.Map; 027 028import org.apache.avalon.framework.context.Context; 029import org.apache.avalon.framework.context.ContextException; 030import org.apache.avalon.framework.context.Contextualizable; 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.cocoon.components.ContextHelper; 034import org.apache.cocoon.i18n.I18nUtils; 035import org.quartz.JobKey; 036import org.quartz.SchedulerException; 037 038import org.ametys.core.observation.Event; 039import org.ametys.core.observation.ObservationManager; 040import org.ametys.core.schedule.Runnable; 041import org.ametys.core.ui.Callable; 042import org.ametys.core.ui.ClientSideElement; 043import org.ametys.plugins.core.schedule.Scheduler; 044import org.ametys.runtime.i18n.I18nizableText; 045import org.ametys.runtime.parameter.ParameterHelper; 046import org.ametys.runtime.parameter.ParameterHelper.ParameterType; 047import org.ametys.web.ObservationConstants; 048import org.ametys.web.publication.PublishPageRunnable; 049import org.ametys.web.publication.UnpublishPageRunnable; 050import org.ametys.web.repository.page.ModifiablePage; 051import org.ametys.web.repository.page.Page; 052import org.ametys.web.repository.page.jcr.DefaultPage; 053 054/** 055 * This {@link ClientSideElement} creates a button representing the schedule publication status of a page. 056 * 057 */ 058public class ScheduledPageClientSideElement extends AbstractPageClientSideElement implements Contextualizable 059{ 060 private Context _context; 061 private ObservationManager _observationManager; 062 private Scheduler _scheduler; 063 064 @Override 065 public void contextualize(Context context) throws ContextException 066 { 067 _context = context; 068 } 069 070 @Override 071 public void service(ServiceManager smanager) throws ServiceException 072 { 073 super.service(smanager); 074 _observationManager = (ObservationManager) smanager.lookup(ObservationManager.ROLE); 075 _scheduler = (Scheduler) smanager.lookup(Scheduler.ROLE); 076 } 077 078 /** 079 * Get the publication dates of a page 080 * @param pageId the id of the page 081 * @return a map 082 */ 083 @Callable 084 public Map<String, Object> getPublicationDates(String pageId) 085 { 086 Map<String, Object> dates = new HashMap<> (); 087 088 Page page = _resolver.resolveById(pageId); 089 090 Date startDate = page.getMetadataHolder().getDate(DefaultPage.METADATA_PUBLICATION_START_DATE, null); 091 if (startDate != null) 092 { 093 dates.put("startDate", ParameterHelper.valueToString(startDate)); 094 } 095 096 Date endDate = page.getMetadataHolder().getDate(DefaultPage.METADATA_PUBLICATION_END_DATE, null); 097 if (endDate != null) 098 { 099 dates.put("endDate", ParameterHelper.valueToString(endDate)); 100 } 101 102 return dates; 103 } 104 105 /** 106 * Set date of publication of pages 107 * @param pageIds The ids of pages to update 108 * @param startDateAsStr The start date. Can be null. 109 * @param endDateAsStr The end date. Can be null. 110 * @return true if operation has succeeded. 111 */ 112 @Callable 113 public boolean setPublicationDate (List<String> pageIds, String startDateAsStr, String endDateAsStr) 114 { 115 Date startDate = startDateAsStr != null ? (Date) ParameterHelper.castValue(startDateAsStr, ParameterType.DATE) : null; 116 Date endDate = endDateAsStr != null ? (Date) ParameterHelper.castValue(endDateAsStr, ParameterType.DATE) : null; 117 118 for (String id : pageIds) 119 { 120 ModifiablePage page = _resolver.resolveById(id); 121 122 try 123 { 124 _scheduleStartDate(page, startDate); 125 _scheduleEndDate(page, endDate); 126 127 page.saveChanges(); 128 129 // Notify listeners in case the page has to be publish or unpublish right now 130 Map<String, Object> eventParams = new HashMap<>(); 131 eventParams.put(ObservationConstants.ARGS_PAGE, page); 132 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_CHANGED, _currentUserProvider.getUser(), eventParams)); 133 134 _observationManager.notify(new Event(ObservationConstants.EVENT_PAGE_UPDATED, _currentUserProvider.getUser(), eventParams)); 135 } 136 catch (SchedulerException e) 137 { 138 if (getLogger().isErrorEnabled()) 139 { 140 getLogger().error("An error occured when trying to schedule the publishing of the page " + id, e); 141 } 142 } 143 } 144 145 return true; 146 } 147 148 private void _scheduleStartDate (ModifiablePage page, Date startDate) throws SchedulerException 149 { 150 // Publish job 151 Runnable publishRunnable = new PublishPageRunnable(page.getId(), page.getTitle(), startDate); 152 JobKey jobKey = new JobKey(publishRunnable.getId(), Scheduler.JOB_GROUP); 153 154 if (_scheduler.getScheduler().checkExists(jobKey)) 155 { 156 // If date is in past, unecessary to run the job 157 _scheduler.getScheduler().deleteJob(jobKey); 158 } 159 160 if (startDate != null) 161 { 162 page.getMetadataHolder().setMetadata(DefaultPage.METADATA_PUBLICATION_START_DATE, startDate); 163 164 if (startDate.after(new Date())) 165 { 166 // No necessary to run job if date is in the past 167 _scheduler.scheduleJob(publishRunnable); 168 } 169 } 170 else if (page.getMetadataHolder().hasMetadata(DefaultPage.METADATA_PUBLICATION_START_DATE)) 171 { 172 page.getMetadataHolder().removeMetadata(DefaultPage.METADATA_PUBLICATION_START_DATE); 173 } 174 } 175 176 private void _scheduleEndDate (ModifiablePage page, Date endDate) throws SchedulerException 177 { 178 // Unpublish job 179 Runnable unpublishRunnable = new UnpublishPageRunnable(page.getId(), page.getTitle(), endDate); 180 JobKey jobKey = new JobKey(unpublishRunnable.getId(), Scheduler.JOB_GROUP); 181 182 if (_scheduler.getScheduler().checkExists(jobKey)) 183 { 184 // A end publication was already scheduled, delete the job 185 _scheduler.getScheduler().deleteJob(jobKey); 186 } 187 188 if (endDate != null) 189 { 190 page.getMetadataHolder().setMetadata(DefaultPage.METADATA_PUBLICATION_END_DATE, endDate); 191 192 if (endDate.after(new Date())) 193 { 194 // No necessary to run job if date is in the past 195 _scheduler.scheduleJob(unpublishRunnable); 196 } 197 } 198 else if (page.getMetadataHolder().hasMetadata(DefaultPage.METADATA_PUBLICATION_END_DATE)) 199 { 200 page.getMetadataHolder().removeMetadata(DefaultPage.METADATA_PUBLICATION_END_DATE); 201 } 202 } 203 204 /** 205 * Get the page status 206 * @param pageIds The page ids 207 * @return the page status 208 */ 209 @SuppressWarnings("unchecked") 210 @Callable 211 public Map<String, Object> getStatus (List<String> pageIds) 212 { 213 Map<String, Object> results = new HashMap<>(); 214 215 results.put("allright-pages", new ArrayList<Map<String, Object>>()); 216 results.put("noright-pages", new ArrayList<Map<String, Object>>()); 217 results.put("nomodifiable-pages", new ArrayList<Map<String, Object>>()); 218 results.put("scheduled-pages", new ArrayList<Map<String, Object>>()); 219 results.put("scheduled-valid-pages", new ArrayList<Map<String, Object>>()); 220 results.put("scheduled-forthcoming-pages", new ArrayList<Map<String, Object>>()); 221 results.put("scheduled-outofdate-pages", new ArrayList<Map<String, Object>>()); 222 223 Calendar now = new GregorianCalendar(); 224 now.set(Calendar.HOUR_OF_DAY, 0); 225 now.set(Calendar.MINUTE, 0); 226 now.set(Calendar.SECOND, 0); 227 now.set(Calendar.MILLISECOND, 0); 228 229 for (String pageId : pageIds) 230 { 231 Page page = _resolver.resolveById(pageId); 232 Map<String, Object> pageParams = getPageDefaultParameters(page); 233 234 if (!(page instanceof ModifiablePage)) 235 { 236 pageParams.put("description", getNoModifiablePageDescription(page)); 237 List<Map<String, Object>> nomodifiablePages = (List<Map<String, Object>>) results.get("nomodifiable-pages"); 238 nomodifiablePages.add(pageParams); 239 } 240 else if (!hasRight(page)) 241 { 242 pageParams.put("description", getNoRightPageDescription(page)); 243 244 List<Map<String, Object>> norightPages = (List<Map<String, Object>>) results.get("noright-pages"); 245 norightPages.add(pageParams); 246 } 247 else 248 { 249 Date startDate = page.getMetadataHolder().getDate(DefaultPage.METADATA_PUBLICATION_START_DATE, null); 250 Date endDate = page.getMetadataHolder().getDate(DefaultPage.METADATA_PUBLICATION_END_DATE, null); 251 252 boolean isScheduled = startDate != null || endDate != null; 253 if (isScheduled) 254 { 255 Map objectModel = ContextHelper.getObjectModel(_context); 256 Locale locale = I18nUtils.findLocale(objectModel, "locale", null, Locale.getDefault(), true); 257 258 if (_synchronizeComponent.isDateValid(page)) 259 { 260 pageParams.put("description", _getDateValidDescription(page)); 261 262 List<Map<String, Object>> validPages = (List<Map<String, Object>>) results.get("scheduled-valid-pages"); 263 validPages.add(pageParams); 264 } 265 else if (endDate != null && endDate.before(now.getTime())) 266 { 267 pageParams.put("description", _getOutOfDateDescription(page, endDate, locale)); 268 269 List<Map<String, Object>> oodPages = (List<Map<String, Object>>) results.get("scheduled-outofdate-pages"); 270 oodPages.add(pageParams); 271 } 272 else if (startDate != null && startDate.after(now.getTime())) 273 { 274 pageParams.put("description", _getForthComingDescription(page, startDate, endDate, locale)); 275 276 List<Map<String, Object>> forthcomingPages = (List<Map<String, Object>>) results.get("scheduled-forthcoming-pages"); 277 forthcomingPages.add(pageParams); 278 } 279 280 List<Map<String, Object>> scheduledPages = (List<Map<String, Object>>) results.get("scheduled-pages"); 281 scheduledPages.add(pageParams); 282 } 283 284 List<Map<String, Object>> allrightPages = (List<Map<String, Object>>) results.get("allright-pages"); 285 allrightPages.add(pageParams); 286 } 287 } 288 289 return results; 290 } 291 292 private I18nizableText _getDateValidDescription (Page page) 293 { 294 List<String> i18nParameters = new ArrayList<>(); 295 i18nParameters.add(page.getTitle()); 296 297 I18nizableText ed = (I18nizableText) this._script.getParameters().get("scheduled-page-valid-description"); 298 return new I18nizableText(ed.getCatalogue(), ed.getKey(), i18nParameters); 299 } 300 301 private I18nizableText _getOutOfDateDescription (Page page, Date endDate, Locale locale) 302 { 303 List<String> i18nParameters = new ArrayList<>(); 304 i18nParameters.add(page.getTitle()); 305 306 DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, locale); 307 i18nParameters.add(endDate != null ? df.format(endDate) : "-"); 308 309 I18nizableText ed = (I18nizableText) this._script.getParameters().get("scheduled-page-outofdate-description"); 310 return new I18nizableText(ed.getCatalogue(), ed.getKey(), i18nParameters); 311 } 312 313 private I18nizableText _getForthComingDescription (Page page, Date startDate, Date endDate, Locale locale) 314 { 315 List<String> i18nParameters = new ArrayList<>(); 316 i18nParameters.add(page.getTitle()); 317 318 DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, locale); 319 i18nParameters.add(startDate != null ? df.format(startDate) : "-"); 320 i18nParameters.add(endDate != null ? df.format(endDate) : "-"); 321 322 I18nizableText ed = (I18nizableText) this._script.getParameters().get("scheduled-page-forthcoming-description"); 323 return new I18nizableText(ed.getCatalogue(), ed.getKey(), i18nParameters); 324 } 325 326}