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