1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package org.xwiki.notifications.notifiers.internal.email

File AbstractMimeMessageIterator.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

6
75
13
1
314
211
25
0.33
5.77
13
1.92

Classes

Class Line # Actions
AbstractMimeMessageIterator 57 75 0% 25 6
0.936170293.6%
 

Contributing tests

This file is covered by 1 test. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.notifications.notifiers.internal.email;
21   
22    import java.util.ArrayList;
23    import java.util.Collection;
24    import java.util.Collections;
25    import java.util.HashMap;
26    import java.util.HashSet;
27    import java.util.Iterator;
28    import java.util.List;
29    import java.util.Map;
30    import java.util.Set;
31   
32    import javax.inject.Inject;
33    import javax.inject.Named;
34    import javax.mail.internet.AddressException;
35    import javax.mail.internet.InternetAddress;
36    import javax.mail.internet.MimeMessage;
37   
38    import org.slf4j.Logger;
39    import org.xwiki.bridge.DocumentAccessBridge;
40    import org.xwiki.mail.MailSenderConfiguration;
41    import org.xwiki.mail.MimeMessageFactory;
42    import org.xwiki.model.reference.DocumentReference;
43    import org.xwiki.model.reference.EntityReferenceSerializer;
44    import org.xwiki.notifications.CompositeEvent;
45    import org.xwiki.notifications.NotificationException;
46    import org.xwiki.notifications.notifiers.email.NotificationEmailRenderer;
47    import org.xwiki.wiki.descriptor.WikiDescriptorManager;
48   
49    import com.xpn.xwiki.api.Attachment;
50   
51    /**
52    * Abstract iterator for sending MIME notification messages (usually emails).
53    *
54    * @since 9.6RC1
55    * @version $Id: 13bfefca077000266517ea636a7d28733bd10389 $
56    */
 
57    public abstract class AbstractMimeMessageIterator implements Iterator<MimeMessage>, Iterable<MimeMessage>
58    {
59    private static final String EVENTS = "events";
60   
61    private static final String HTML_EVENTS = "htmlEvents";
62   
63    private static final String PLAIN_TEXT_EVENTS = "plainTextEvents";
64   
65    private static final String SORTED_EVENTS = "sortedEvents";
66   
67    private static final String EMAIL_PROPERTY = "email";
68   
69    private static final String FROM = "from";
70   
71    private static final String TO = "to";
72   
73    private static final String VELOCITY_VARIABLES = "velocityVariables";
74   
75    private static final String ERROR_MESSAGE = "Failed to generate an email for the user [{}].";
76   
77    private static final String ATTACHMENTS = "attachments";
78   
79    @Inject
80    protected Logger logger;
81   
82    @Inject
83    protected EntityReferenceSerializer<String> serializer;
84   
85    @Inject
86    @Named("template")
87    private MimeMessageFactory<MimeMessage> factory;
88   
89    @Inject
90    private DocumentAccessBridge documentAccessBridge;
91   
92    @Inject
93    private NotificationEmailRenderer defaultNotificationEmailRenderer;
94   
95    @Inject
96    private WikiDescriptorManager wikiDescriptorManager;
97   
98    @Inject
99    private MailSenderConfiguration mailSenderConfiguration;
100   
101    @Inject
102    private UserAvatarAttachmentExtractor userAvatarAttachmentExtractor;
103   
104    @Inject
105    private LogoAttachmentExtractor logoAttachmentExtractor;
106   
107    @Inject
108    private MailTemplateImageAttachmentsExtractor mailTemplateImageAttachmentsExtractor;
109   
110    private NotificationUserIterator userIterator;
111   
112    private Map<String, Object> factoryParameters = new HashMap<>();
113   
114    private DocumentReference templateReference;
115   
116    private List<CompositeEvent> currentEvents = Collections.emptyList();
117   
118    private DocumentReference currentUser;
119   
120    private InternetAddress currentUserEmail;
121   
122    private boolean hasNext;
123   
124    /**
125    * Initialize the iterator.
126    * A class extending {@link AbstractMimeMessageIterator} should implement a same initialize method that calls
127    * this one at the end of its execution.
128    *
129    * @param userIterator iterator that returns all users
130    * @param factoryParameters parameters for the email factory
131    * @param templateReference reference to the mail template
132    */
 
133  372 toggle protected void initialize(NotificationUserIterator userIterator, Map<String, Object> factoryParameters,
134    DocumentReference templateReference)
135    {
136  372 this.userIterator = userIterator;
137  372 this.factoryParameters = factoryParameters;
138  372 this.templateReference = templateReference;
139  372 this.computeNext();
140    }
141   
142    protected abstract List<CompositeEvent> retrieveCompositeEventList(DocumentReference user)
143    throws NotificationException;
144   
145    /**
146    * Compute the message that will be sent to the next user in the iterator.
147    */
 
148  375 toggle protected void computeNext()
149    {
150  375 this.currentEvents = Collections.emptyList();
151  375 this.currentUserEmail = null;
152  379 while ((this.currentEvents.isEmpty() || currentUserEmail == null) && this.userIterator.hasNext()) {
153  4 this.currentUser = this.userIterator.next();
154  4 try {
155  4 this.currentUserEmail = new InternetAddress(getUserEmail(this.currentUser));
156    } catch (AddressException e) {
157    // The user has not written a valid email
158  1 continue;
159    }
160   
161  3 try {
162    // TODO: in a next version, it will be important to paginate these results and to send several emails
163    // if there is too much content
164  3 this.currentEvents = retrieveCompositeEventList(currentUser);
165    } catch (NotificationException e) {
166  0 logger.error(ERROR_MESSAGE, this.currentUser, e);
167    }
168    }
169   
170  375 this.hasNext = currentUserEmail != null && !this.currentEvents.isEmpty();
171    }
172   
 
173  3 toggle private void updateFactoryParameters() throws NotificationException, AddressException
174    {
175    // We need to clear all the attachments that have been put in the previous iteration, otherwise, we end up
176    // duplicating the wiki logo, the user avatars, and every attachments that are common to several emails...
177  3 getAttachments().clear();
178   
179  3 handleEvents();
180  3 handleWikiLogo();
181  3 handleImageAttachmentsFromTemplate();
182   
183  3 try {
184  3 factoryParameters.put(FROM, new InternetAddress(mailSenderConfiguration.getFromAddress()));
185    } catch (AddressException | NullPointerException e) {
186  0 logger.warn("No default email address is configured in the administration.");
187    }
188   
189  3 factoryParameters.put(TO, this.currentUserEmail);
190    }
191   
 
192  3 toggle private void handleImageAttachmentsFromTemplate() throws NotificationException
193    {
194  3 Collection<Attachment> attachments = getAttachments();
195   
196  3 try {
197  3 attachments.addAll(mailTemplateImageAttachmentsExtractor.getImages(templateReference));
198    } catch (Exception e) {
199  0 throw new NotificationException(
200    String.format("Failed to get the attachments of the template [%s].", templateReference), e);
201    }
202    }
203   
 
204  3 toggle private void handleEvents() throws NotificationException
205    {
206  3 String usedId = serializer.serialize(this.currentUser);
207    // Render all the events both in HTML and Plain Text
208  3 List<String> htmlEvents = new ArrayList<>();
209  3 List<String> plainTextEvents = new ArrayList<>();
210  3 EventsSorter eventsSorter = new EventsSorter();
211  3 for (CompositeEvent event : currentEvents) {
212  4 String html = defaultNotificationEmailRenderer.renderHTML(event, usedId);
213  4 String plainText = defaultNotificationEmailRenderer.renderPlainText(event, usedId);
214  4 htmlEvents.add(html);
215  4 plainTextEvents.add(plainText);
216  4 eventsSorter.add(event, html, plainText);
217    }
218   
219    // Put in the velocity parameters all the events and their rendered version
220  3 Map<String, Object> velocityVariables = getVelocityVariables();
221  3 velocityVariables.put(EVENTS, currentEvents);
222  3 velocityVariables.put(HTML_EVENTS, htmlEvents);
223  3 velocityVariables.put(PLAIN_TEXT_EVENTS, plainTextEvents);
224  3 velocityVariables.put(SORTED_EVENTS, eventsSorter.sort());
225   
226  3 handleAvatars();
227    }
228   
 
229  3 toggle private void handleWikiLogo()
230    {
231  3 try {
232  3 getAttachments().add(logoAttachmentExtractor.getLogo());
233    } catch (Exception e) {
234  0 logger.warn("Failed to get the logo.", e);
235    }
236    }
237   
 
238  12 toggle private Collection<Attachment> getAttachments()
239    {
240  12 Object attachments = factoryParameters.get(ATTACHMENTS);
241  12 if (attachments != null) {
242  10 return (Collection<Attachment>) attachments;
243    }
244   
245  2 Collection<Attachment> newList = new ArrayList<>();
246  2 factoryParameters.put(ATTACHMENTS, newList);
247  2 return newList;
248    }
249   
 
250  3 toggle private void handleAvatars()
251    {
252  3 Set<DocumentReference> userAvatars = new HashSet<>();
253  3 for (CompositeEvent event : currentEvents) {
254  4 userAvatars.addAll(event.getUsers());
255    }
256  3 Collection<Attachment> attachments = getAttachments();
257  3 for (DocumentReference userAvatar : userAvatars) {
258  1 try {
259  1 attachments.add(userAvatarAttachmentExtractor.getUserAvatar(userAvatar, 32));
260    } catch (Exception e) {
261  0 logger.warn("Failed to add the avatar of [{}] in the email.", userAvatar, e);
262    }
263    }
264    }
265   
 
266  3 toggle private Map<String, Object> getVelocityVariables()
267    {
268  3 Object velocityVariables = factoryParameters.get(VELOCITY_VARIABLES);
269  3 if (velocityVariables == null) {
270  2 velocityVariables = new HashMap<String, Object>();
271  2 factoryParameters.put(VELOCITY_VARIABLES, velocityVariables);
272    }
273   
274  3 return (Map<String, Object>) velocityVariables;
275    }
276   
 
277  4 toggle private String getUserEmail(DocumentReference user)
278    {
279  4 return (String) documentAccessBridge.getProperty(user,
280    new DocumentReference(wikiDescriptorManager.getCurrentWikiId(), "XWiki", "XWikiUsers"),
281    0,
282    EMAIL_PROPERTY);
283    }
284   
 
285  375 toggle @Override
286    public boolean hasNext()
287    {
288  375 return hasNext;
289    }
290   
 
291  3 toggle @Override
292    public MimeMessage next()
293    {
294  3 MimeMessage message = null;
295  3 try {
296  3 updateFactoryParameters();
297  3 message = this.factory.createMessage(templateReference, factoryParameters);
298    } catch (Exception e) {
299  0 logger.error(ERROR_MESSAGE, this.currentUser, e);
300    }
301   
302    // Look for the next email to send
303  3 this.computeNext();
304   
305    // But return the current email
306  3 return message;
307    }
308   
 
309  372 toggle @Override
310    public Iterator<MimeMessage> iterator()
311    {
312  372 return this;
313    }
314    }