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.plugins.workspaces.project.notification; 017 018import java.util.ArrayList; 019import java.util.List; 020import java.util.Locale; 021import java.util.Map; 022import java.util.Set; 023 024import javax.mail.MessagingException; 025 026import org.apache.avalon.framework.context.Context; 027import org.apache.avalon.framework.context.ContextException; 028import org.apache.avalon.framework.context.Contextualizable; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.avalon.framework.service.Serviceable; 032import org.apache.cocoon.components.ContextHelper; 033import org.apache.commons.collections.CollectionUtils; 034import org.apache.commons.lang.StringUtils; 035 036import org.ametys.cms.transformation.xslt.ResolveURIComponent; 037import org.ametys.core.observation.Event; 038import org.ametys.core.observation.Observer; 039import org.ametys.core.right.RightManager; 040import org.ametys.core.user.User; 041import org.ametys.core.user.UserIdentity; 042import org.ametys.core.user.UserManager; 043import org.ametys.core.util.I18nUtils; 044import org.ametys.core.util.mail.SendMailHelper; 045import org.ametys.plugins.explorer.ObservationConstants; 046import org.ametys.plugins.repository.AmetysObject; 047import org.ametys.plugins.repository.AmetysObjectIterator; 048import org.ametys.plugins.repository.AmetysObjectResolver; 049import org.ametys.plugins.workspaces.project.ProjectManager; 050import org.ametys.plugins.workspaces.project.modules.WorkspaceModule; 051import org.ametys.plugins.workspaces.project.modules.WorkspaceModuleExtensionPoint; 052import org.ametys.plugins.workspaces.project.objects.Project; 053import org.ametys.runtime.config.Config; 054import org.ametys.runtime.i18n.I18nizableText; 055import org.ametys.runtime.plugin.component.AbstractLogEnabled; 056import org.ametys.runtime.plugin.component.PluginAware; 057import org.ametys.web.renderingcontext.RenderingContext; 058import org.ametys.web.renderingcontext.RenderingContextHandler; 059import org.ametys.web.repository.page.Page; 060 061/** 062 * {@link Observer} for observing events on resources project 063 */ 064public abstract class AbstractSendNotificationObserver extends AbstractLogEnabled implements Observer, Serviceable, Contextualizable, PluginAware 065{ 066 /** The Ametys Object Resolver*/ 067 protected AmetysObjectResolver _resolver; 068 /** The avalon context */ 069 protected Context _context; 070 /** The users manager */ 071 protected UserManager _userManager; 072 /** The rights manager */ 073 protected RightManager _rightManager; 074 /** The i18n utils */ 075 protected I18nUtils _i18nUtils; 076 /** The project manager */ 077 protected ProjectManager _projectManager; 078 /** The rendering context handler */ 079 protected RenderingContextHandler _renderingContextHandler; 080 /** The name of current plugin */ 081 protected String _pluginName; 082 /** The workspace module managers EP */ 083 protected WorkspaceModuleExtensionPoint _moduleManagerEP; 084 085 @Override 086 public void service(ServiceManager manager) throws ServiceException 087 { 088 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 089 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 090 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 091 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 092 _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE); 093 _renderingContextHandler = (RenderingContextHandler) manager.lookup(RenderingContextHandler.ROLE); 094 _moduleManagerEP = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE); 095 } 096 097 @Override 098 public void contextualize(Context context) throws ContextException 099 { 100 _context = context; 101 } 102 103 public void setPluginInfo(String pluginName, String featureName, String id) 104 { 105 _pluginName = pluginName; 106 } 107 108 @Override 109 public int getPriority(Event event) 110 { 111 return Observer.MAX_PRIORITY; 112 } 113 114 @Override 115 public void observe(Event event, Map<String, Object> transientVars) 116 { 117 UserIdentity userIdentity = event.getIssuer(); 118 User user = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin()); 119 120 Project project = getProject(event); 121 122 if (project != null) 123 { 124 notifyEvent(project, event.getId(), event.getArguments(), user); 125 } 126 } 127 128 /** 129 * Notify email by mail 130 * @param project The project 131 * @param eventId The id of event 132 * @param eventParams The event's arguments 133 * @param issuer The issuer 134 */ 135 protected abstract void notifyEvent (Project project, String eventId, Map<String, Object> eventParams, User issuer); 136 137 /** 138 * Get the project from event 139 * @param event The event 140 * @return the project or null if not found 141 */ 142 protected Project getProject (Event event) 143 { 144 Map<String, Object> args = event.getArguments(); 145 146 String targetId = (String) args.get(ObservationConstants.ARGS_ID); 147 String parentID = (String) args.get(ObservationConstants.ARGS_PARENT_ID); 148 149 AmetysObject object = null; 150 if (parentID != null) 151 { 152 object = _resolver.resolveById(parentID); 153 } 154 else 155 { 156 object = _resolver.resolveById(targetId); 157 } 158 159 AmetysObject parent = object.getParent(); 160 161 while (parent != null) 162 { 163 if (parent instanceof Project) 164 { 165 return (Project) parent; 166 } 167 168 parent = parent.getParent(); 169 } 170 171 return null; 172 } 173 174 /** 175 * Get the common mail parameters 176 * @param project The parent project 177 * @param issuer The user responsible of the action 178 * @param explorerNodeId the id of the explorer node. Can be null. 179 * @return an array of strings used in every mail 180 */ 181 protected List<String> getMailCommonParams(Project project, User issuer, String explorerNodeId) 182 { 183 List<String> mailBodyParams = new ArrayList<>(); 184 // {0} project title 185 mailBodyParams.add(project.getTitle()); 186 // {1} module url 187 mailBodyParams.add(getUrl(project, explorerNodeId)); 188 // {2} user full name 189 String login = issuer.getIdentity().getLogin(); 190 String populationId = issuer.getIdentity().getPopulationId(); 191 mailBodyParams.add(_userManager.getUser(populationId, login).getFullName()); 192 // {3} user email 193 mailBodyParams.add(issuer.getEmail()); 194 195 return mailBodyParams; 196 } 197 198 /** 199 * Get the default language to resolve module's page 200 * @return The default language 201 */ 202 protected String getDefaultLanguage() 203 { 204 Map objectModel = ContextHelper.getObjectModel(_context); 205 Locale locale = org.apache.cocoon.i18n.I18nUtils.findLocale(objectModel, "locale", null, Locale.getDefault(), true); 206 return locale.getLanguage(); 207 } 208 209 /** 210 * Get the module's page 211 * @param project The project 212 * @param moduleId The module 213 * @return The page or <code>null</code> if not found 214 */ 215 protected Page getModulePage(Project project, String moduleId) 216 { 217 AmetysObjectIterator<Page> pages = null; 218 219 WorkspaceModule module = _moduleManagerEP.getModule(moduleId); 220 if (module != null && _projectManager.isModuleActivated(project, moduleId)) 221 { 222 pages = module.getModulePages(project, null).iterator(); 223 } 224 225 String defaultLanguage = getDefaultLanguage(); 226 Page firstPage = null; 227 228 if (pages != null && pages.getSize() > 0) 229 { 230 while (pages.hasNext()) 231 { 232 Page page = pages.next(); 233 firstPage = page; 234 if (page.getSitemapName().equals(defaultLanguage)) 235 { 236 return page; 237 } 238 } 239 } 240 241 return firstPage; 242 } 243 244 /** 245 * Get the absolute full url of module'page 246 * @param project The project 247 * @param moduleId The module. Can not be null 248 * @param objectId The id of concerned object. Can be null. 249 * @return The 250 */ 251 protected String getModuleUrl(Project project, String moduleId, String objectId) 252 { 253 Page modulePage = getModulePage(project, moduleId); 254 if (modulePage != null) 255 { 256 RenderingContext currentContext = _renderingContextHandler.getRenderingContext(); 257 258 try 259 { 260 StringBuilder sb = new StringBuilder(); 261 262 _renderingContextHandler.setRenderingContext(RenderingContext.FRONT); 263 264 sb.append(ResolveURIComponent.resolve("page", modulePage.getId(), false, true)); 265 266 if (objectId != null) 267 { 268 sb.append("#").append(objectId); 269 } 270 271 return sb.toString(); 272 } 273 finally 274 { 275 _renderingContextHandler.setRenderingContext(currentContext); 276 } 277 } 278 else 279 { 280 return getProjectUrl(project); 281 } 282 } 283 284 /** 285 * Get the absolute url of project 286 * @param project The project 287 * @return the project's url 288 */ 289 protected String getProjectUrl(Project project) 290 { 291 return project.getSites().iterator().next().getUrl(); 292 } 293 294 295 /** 296 * Get the URL of project to insert in email body 297 * @param project The project 298 * @param objectId The id of concerned object 299 * @return The full URL 300 */ 301 protected abstract String getUrl(Project project, String objectId); 302 303 /** 304 * Sent an email 305 * @param eventId The id of event 306 * @param recipients The recipients of the mail 307 * @param mailBodyi18nKey i18n key for the body 308 * @param mailSubjecti18nKey i18n key for the subject 309 * @param mailBodyParams parameters for the body 310 * @param mailSubjectParams parameters for the subject 311 */ 312 protected void sendMail(String eventId, List<UserIdentity> recipients, String mailBodyi18nKey, String mailSubjecti18nKey, List<String> mailBodyParams, List<String> mailSubjectParams) 313 { 314 I18nizableText i18nSubject = new I18nizableText("plugin." + _pluginName, mailSubjecti18nKey, mailSubjectParams); 315 String subject = _i18nUtils.translate(i18nSubject); 316 317 I18nizableText i18nBody = new I18nizableText("plugin." + _pluginName, mailBodyi18nKey, mailBodyParams); 318 String body = _i18nUtils.translate(i18nBody); 319 320 String sender = Config.getInstance().getValueAsString("smtp.mail.from"); 321 322 for (UserIdentity userIdentity : recipients) 323 { 324 User recipient = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin()); 325 if (recipient != null) 326 { 327 String email = recipient.getEmail(); 328 if (StringUtils.isNotEmpty(email)) 329 { 330 try 331 { 332 SendMailHelper.sendMail(subject, null, body, email, sender, true); 333 } 334 catch (MessagingException e) 335 { 336 getLogger().warn("Could not send a notification e-mail to " + email, e); 337 } 338 } 339 } 340 } 341 } 342 343 /** 344 * Get the users allowed to be notified 345 * @param eventId The id of event 346 * @param object The object on which to test rights 347 * @return The allowed users 348 */ 349 protected List<UserIdentity> getUsersToNotify(String eventId, AmetysObject object) 350 { 351 boolean returnAll = Config.getInstance().getValueAsBoolean("runtime.mail.massive.sending"); 352 Set<UserIdentity> readAccessUsers = _rightManager.getReadAccessAllowedUsers(object).resolveAllowedUsers(returnAll); 353 Set<UserIdentity> mailAllowedUsers = _rightManager.getAllowedUsers(getRightIdForNotify(), object).resolveAllowedUsers(returnAll); 354 355 return (List<UserIdentity>) CollectionUtils.retainAll(readAccessUsers, mailAllowedUsers); 356 } 357 358 /** 359 * Get the right to check allowed users to notify by mail 360 * @return the right id to check 361 */ 362 protected abstract String getRightIdForNotify(); 363 364 /** 365 * format the path without the root path 366 * @param rootPath The root path to remove 367 * @param path The absolute path 368 * @return the local path 369 */ 370 protected String _getPath (String rootPath, String path) 371 { 372 int index = path.indexOf(rootPath); 373 return path.substring(index + rootPath.length()); 374 } 375}