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