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