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