001/* 002 * Copyright 2010 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.alerts; 017 018import java.util.ArrayList; 019import java.util.Calendar; 020import java.util.GregorianCalendar; 021import java.util.HashSet; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Set; 025 026import org.ametys.cms.repository.Content; 027import org.ametys.core.user.UserIdentity; 028import org.ametys.core.user.population.PopulationContextHelper; 029import org.ametys.core.util.URLEncoder; 030import org.ametys.plugins.repository.AmetysObjectIterable; 031import org.ametys.plugins.repository.AmetysRepositoryException; 032import org.ametys.plugins.repository.query.expression.DateExpression; 033import org.ametys.plugins.repository.query.expression.Expression; 034import org.ametys.plugins.repository.query.expression.Expression.Operator; 035import org.ametys.runtime.config.Config; 036import org.ametys.runtime.i18n.I18nizableText; 037import org.ametys.web.repository.content.WebContent; 038import org.ametys.web.repository.page.Page; 039import org.ametys.web.repository.page.PageQueryHelper; 040import org.ametys.web.repository.page.jcr.DefaultPage; 041import org.ametys.web.repository.site.Site; 042import org.apache.avalon.framework.configuration.Configuration; 043import org.apache.avalon.framework.configuration.ConfigurationException; 044import org.apache.cocoon.components.ContextHelper; 045import org.apache.cocoon.environment.Request; 046 047/** 048 * Alerts engine: sends alerts mail. 049 * This is the web version of the engine: it sets the currently processed content's 050 * site name in the request object, and sends additional site and page information 051 * in the alerts e-mails. 052 */ 053public class AlertEngine extends org.ametys.cms.alerts.AlertEngine 054{ 055 /** True if the alert of page publication ending is enabled */ 056 protected boolean _pagePublicationEnabled; 057 058 /** The "page nearing end of publication" e-mail will be sent to users that have this right. */ 059 protected Set<String> _pagePublicationEndRights; 060 061 /** The "page nearing end of publication" e-mail subject i18n key. */ 062 protected String _pagePublicationEndSubject; 063 064 /** The "page nearing end of publication" e-mail body i18n key. */ 065 protected String _pagePublicationEndBody; 066 067 /** 068 * Default constructor 069 */ 070 public AlertEngine() 071 { 072 super(); 073 } 074 075 /** 076 * Constructor used to send instant alerts 077 * @param contentIds The content's id 078 * @param message the message 079 */ 080 public AlertEngine (List<String> contentIds, String message) 081 { 082 super(contentIds, message); 083 } 084 085 @Override 086 public void configure(Configuration configuration) throws ConfigurationException 087 { 088 super.configure(configuration); 089 090 Configuration pagePublicationEndConf = configuration.getChild("pagePublicationEnd", false); 091 092 if (pagePublicationEndConf != null) 093 { 094 _pagePublicationEnabled = true; 095 _pagePublicationEndRights = _getRightsFromConf(pagePublicationEndConf); 096 _pagePublicationEndSubject = pagePublicationEndConf.getChild("subjectKey").getValue(); 097 _pagePublicationEndBody = pagePublicationEndConf.getChild("bodyKey").getValue(); 098 } 099 } 100 101 @Override 102 protected void _sendAlerts() throws AmetysRepositoryException 103 { 104 // Send the content alerts. 105 super._sendAlerts(); 106 107 if (!_instantMode && _pagePublicationEnabled) 108 { 109 // Send the page alerts. 110 _sendPagePublicationEndAlerts(); 111 } 112 } 113 114 @Override 115 protected void _setRequestAttributes (Content content) 116 { 117 Request request = ContextHelper.getRequest(_context); 118 119 Site site = _getSite(content); 120 if (site != null) 121 { 122 String siteName = site.getName(); 123 // Set the site name into the request for the other components to be able to retrieve it. 124 request.setAttribute("siteName", siteName); 125 126 List<String> populationContexts = new ArrayList<>(); 127 128 // Set the population contexts to be able to get allowed users 129 populationContexts.add("/sites/" + siteName); 130 populationContexts.add("/sites-fo/" + siteName); 131 132 request.setAttribute(PopulationContextHelper.POPULATION_CONTEXTS_REQUEST_ATTR, populationContexts); 133 } 134 } 135 136 /** 137 * Set the necessary request attributes 138 * @param page The concerned page 139 */ 140 protected void _setRequestAttributes (Page page) 141 { 142 Request request = ContextHelper.getRequest(_context); 143 144 String siteName = page.getSiteName(); 145 146 // Set the site name into the request for the other components to be able to retrieve it. 147 request.setAttribute("siteName", siteName); 148 149 List<String> populationContexts = new ArrayList<>(); 150 151 // Set the population contexts to be able to get allowed users 152 populationContexts.add("/sites/" + siteName); 153 populationContexts.add("/sites-fo/" + siteName); 154 155 request.setAttribute(PopulationContextHelper.POPULATION_CONTEXTS_REQUEST_ATTR, populationContexts); 156 } 157 158 /** 159 * Send the "page nearing end of publication" alerts. 160 * @throws AmetysRepositoryException if an error occurs. 161 */ 162 protected void _sendPagePublicationEndAlerts() throws AmetysRepositoryException 163 { 164 Long delay = Config.getInstance().getValueAsLong("remind.before.publication.end"); 165 if (delay != null && delay > 0) 166 { 167 Calendar calendar = new GregorianCalendar(); 168 _removeTimeParts(calendar); 169 calendar.add(Calendar.DAY_OF_MONTH, delay.intValue()); 170 171 // Get all the pages which publication end is in X days. 172 Expression nearPublicationEndExpr = new DateExpression(DefaultPage.METADATA_PUBLICATION_END_DATE, Operator.EQ, calendar.getTime()); 173 174 String query = PageQueryHelper.getPageXPathQuery(null, null, null, nearPublicationEndExpr, null); 175 176 try (AmetysObjectIterable<Page> pages = _ametysResolver.query(query)) 177 { 178 if (_LOGGER.isInfoEnabled()) 179 { 180 _LOGGER.info("Pages within " + Long.toString(delay) + " days of publication end: " + pages.getSize()); 181 } 182 183 for (Page page : pages) 184 { 185 // Send the alert e-mails. 186 _sendPagePublicationEndEmail(page); 187 } 188 } 189 } 190 } 191 192 /** 193 * Send the "page publication end" alert e-mail. 194 * @param page the page nearing publication end. 195 * @throws AmetysRepositoryException if an error occurs. 196 */ 197 protected void _sendPagePublicationEndEmail(Page page) throws AmetysRepositoryException 198 { 199 _setRequestAttributes(page); 200 201 Set<UserIdentity> users = new HashSet<>(); 202 for (String right : _pagePublicationEndRights) 203 { 204 users.addAll(_rightManager.getAllowedUsers(right, page).resolveAllowedUsers(Config.getInstance().getValueAsBoolean("runtime.mail.massive.sending"))); 205 } 206 207 List<String> params = _getPagePublicationEndParams(page); 208 209 I18nizableText i18nSubject = new I18nizableText(null, _pagePublicationEndSubject, params); 210 I18nizableText i18nBody = new I18nizableText(null, _pagePublicationEndBody, params); 211 212 String subject = _i18nUtils.translate(i18nSubject); 213 String body = _i18nUtils.translate(i18nBody); 214 215 _sendMails(subject, body, users, _mailFrom); 216 } 217 218 @Override 219 protected String getI18nKeyBody(String bodyI18nKey, Content content) 220 { 221 String i18nKey = bodyI18nKey; 222 223 Site site = _getSite(content); 224 if (site == null) 225 { 226 i18nKey += "_NOSITE"; 227 } 228 else if (_getPage(content, site) == null) 229 { 230 // Orphan content 231 i18nKey += "_ORPHAN"; 232 } 233 234 return i18nKey; 235 } 236 237 @Override 238 protected List<String> _getAdditionalParams(Content content) 239 { 240 List<String> params = new ArrayList<>(); 241 242 Site site = _getSite(content); 243 if (site != null) 244 { 245 params.add(site.getTitle()); // {4} 246 } 247 248 Page page = _getPage(content, site); 249 if (page != null) 250 { 251 params.add(page.getTitle()); // {5} 252 } 253 254 return params; 255 } 256 257 /** 258 * Get the "page nearing end of publication" mail parameters. 259 * @param page the page. 260 * @return the mail parameters. 261 */ 262 protected List<String> _getPagePublicationEndParams(Page page) 263 { 264 List<String> params = new ArrayList<>(); 265 266 String siteTitle = page.getSite().getTitle(); 267 String delay = Config.getInstance().getValueAsString("remind.before.publication.end"); 268 269 params.add(page.getTitle()); 270 params.add(_getPageUrl(page)); 271 params.add(delay); 272 params.add(siteTitle); 273 274 return params; 275 } 276 277 @Override 278 protected String _getContentUrl(Content content) 279 { 280 Site site = _getSite(content); 281 if (site != null) 282 { 283 StringBuilder url = new StringBuilder(_baseUrl); 284 if (!_baseUrl.endsWith("/")) 285 { 286 url.append('/'); 287 } 288 289 Page page = _getPage(content, site); 290 if (page != null) 291 { 292 url.append(site.getName()).append("/index.html?uitool=uitool-page,id:%27").append(URLEncoder.encodeParameter(page.getId())).append("%27"); 293 } 294 else 295 { 296 url.append(site.getName()).append("/index.html?uitool=uitool-content,id:%27").append(URLEncoder.encodeParameter(content.getId())).append("%27"); 297 } 298 299 return url.toString(); 300 } 301 else 302 { 303 return super._getContentUrl(content); 304 } 305 } 306 307 /** 308 * Get the URL of the back-office, opening on the page tool. 309 * @param page the page to link to. 310 * @return the page URL. 311 */ 312 protected String _getPageUrl(Page page) 313 { 314 StringBuilder url = new StringBuilder(_baseUrl); 315 if (!_baseUrl.endsWith("/")) 316 { 317 url.append('/'); 318 } 319 url.append(page.getSite().getName()).append("/index.html?uitool=uitool-page,id:%27").append(page.getId()).append("%27"); 320 return url.toString(); 321 } 322 323 /** 324 * Get the site name 325 * @param content The content 326 * @return the site name 327 */ 328 protected Site _getSite(Content content) 329 { 330 if (content instanceof WebContent) 331 { 332 return ((WebContent) content).getSite(); 333 } 334 335 return null; 336 } 337 338 /** 339 * Get the page referenced by this content 340 * @param content The content 341 * @param site the site 342 * @return the page or null. 343 */ 344 protected Page _getPage(Content content, Site site) 345 { 346 if (content instanceof WebContent) 347 { 348 Iterator<Page> pages = ((WebContent) content).getReferencingPages().iterator(); 349 if (pages.hasNext()) 350 { 351 return pages.next(); 352 } 353 } 354 return null; 355 } 356 357}