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