1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package org.xwiki.watchlist.internal.api

File WatchListEvent.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart9.png
41% of files have more coverage

Code metrics

44
110
29
1
514
277
57
0.52
3.79
29
1.97

Classes

Class Line # Actions
WatchListEvent 49 110 0% 57 31
0.830601183.1%
 

Contributing tests

This file is covered by 4 tests. .

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.watchlist.internal.api;
21   
22    import java.util.ArrayList;
23    import java.util.Date;
24    import java.util.List;
25   
26    import javax.inject.Provider;
27   
28    import org.apache.commons.lang3.StringUtils;
29    import org.xwiki.model.EntityType;
30    import org.xwiki.model.reference.DocumentReference;
31    import org.xwiki.model.reference.EntityReferenceProvider;
32    import org.xwiki.model.reference.EntityReferenceSerializer;
33    import org.xwiki.model.reference.SpaceReference;
34    import org.xwiki.watchlist.internal.WatchListEventHTMLDiffExtractor;
35   
36    import com.xpn.xwiki.XWikiContext;
37    import com.xpn.xwiki.XWikiException;
38    import com.xpn.xwiki.doc.XWikiDocument;
39    import com.xpn.xwiki.plugin.activitystream.api.ActivityEventType;
40    import com.xpn.xwiki.user.api.XWikiRightService;
41    import com.xpn.xwiki.web.Utils;
42   
43    /**
44    * The class representing an event in the WatchList. The current implementation is a wrapper for one or more
45    * ActivityEvent.
46    *
47    * @version $Id: ee864321191cba17421246dfc5b00b51e38513e3 $
48    */
 
49    public class WatchListEvent implements Comparable<WatchListEvent>
50    {
51    /**
52    * Default document version on creation.
53    */
54    private static final String INITIAL_DOCUMENT_VERSION = "1.1";
55   
56    /**
57    * The version before the initial version used for document, used to get empty versions of documents.
58    */
59    private static final String PREINITIAL_DOCUMENT_VERSION = "1.0";
60   
61    /**
62    * Event hashcode.
63    */
64    private final int hashCode;
65   
66    /**
67    * Type of the event (example: "update").
68    */
69    private String type;
70   
71    /**
72    * Version of the document before the event happened.
73    */
74    private String previousVersion;
75   
76    /**
77    * List of versions affected by this event. It will contain only one entry if the event is not a composite event.
78    */
79    private List<String> versions;
80   
81    /**
82    * List of authors for this event. It will contain only one entry if the event is not a composite event.
83    */
84    private List<String> authors;
85   
86    /**
87    * List of dates for this event. It will contain only one entry if the event is not a composite event.
88    */
89    private List<Date> dates;
90   
91    /**
92    * Difference generated by update events in a document, formatted in HTML.
93    */
94    private String htmlDiff;
95   
96    private DocumentReference documentReference;
97   
98    private DocumentReference authorReference;
99   
100    private List<DocumentReference> authorReferences;
101   
102    private String version;
103   
104    private Date date;
105   
106    private List<WatchListEvent> events;
107   
108    private EntityReferenceSerializer<String> serializer;
109   
110    private EntityReferenceSerializer<String> localSerializer;
111   
112    private EntityReferenceProvider defaultEntityReferenceProvider;
113   
114    /**
115    * Constructor.
116    *
117    * @param documentReference the document on which the event happened
118    * @param type the type of event
119    * @param userReference the user that triggered the event
120    * @param version the version of the document after the event happened on it
121    * @param date the date of the event
122    */
 
123  87 toggle public WatchListEvent(DocumentReference documentReference, String type, DocumentReference userReference,
124    String version, Date date)
125    {
126  87 this.documentReference = documentReference;
127  87 this.type = type;
128  87 this.authorReference = userReference;
129  87 this.version = version;
130  87 this.date = date;
131   
132  87 this.events = new ArrayList<>();
133  87 this.events.add(this);
134   
135  87 int hash = 3;
136  87 if (ActivityEventType.UPDATE.equals(type)) {
137  65 hashCode = 42 * hash + documentReference.hashCode() + type.hashCode();
138    } else {
139  22 hashCode = 42 * hash + documentReference.hashCode() + type.hashCode() + date.hashCode();
140    }
141    }
142   
143    /**
144    * Add another event associated to this event.
145    *
146    * @param event The event to add.
147    */
 
148  19 toggle public void addEvent(WatchListEvent event)
149    {
150  19 if (ActivityEventType.DELETE.equals(event.getType())) {
151    // If the document has been deleted, reset this event
152  0 type = event.getType();
153  0 versions = null;
154  0 authors = null;
155  0 previousVersion = null;
156  0 htmlDiff = null;
157  19 } else if (ActivityEventType.UPDATE.equals(event.getType()) && ActivityEventType.DELETE.equals(getType())) {
158    // If an update event had been fired before a delete, discard it
159  2 return;
160    }
161   
162  17 events.add(event);
163    }
164   
165    /**
166    * @return The wiki in which the event happened.
167    */
 
168  45 toggle public String getWiki()
169    {
170  45 return getDocumentReference().getWikiReference().getName();
171    }
172   
173    /**
174    * @return The space in which the event happened.
175    */
 
176  15 toggle public String getSpace()
177    {
178  15 SpaceReference spaceReference = getDocumentReference().getLastSpaceReference();
179  15 return getLocalSerializer().serialize(spaceReference);
180    }
181   
182    /**
183    * @return the serialized space for display, taking into account Nested Pages, i.e. not displaying WebHome for a
184    * nicer user experience
185    * @since 8.3M2
186    */
 
187  6 toggle public String getDisplaySpace()
188    {
189  6 if (getDefaultEntityReferenceProvider().getDefaultReference(EntityType.DOCUMENT).getName().equals(
190    getDocumentReference().getName()) && getDocumentReference().getLastSpaceReference().getParent() != null)
191    {
192  0 return getLocalSerializer().serialize(getDocumentReference().getLastSpaceReference().getParent());
193    } else {
194  6 return getSpace();
195    }
196    }
197   
198    /**
199    * @return The space, prefixed with the wiki name, in which the event happened (example: "xwiki:Main").
200    */
 
201  35 toggle public String getPrefixedSpace()
202    {
203  35 SpaceReference spaceReference = getDocumentReference().getLastSpaceReference();
204  35 return getSerializer().serialize(spaceReference);
205    }
206   
207    /**
208    * @return The fullName of the document which has generated this event (example: "Main.WebHome").
209    */
 
210  93 toggle public String getFullName()
211    {
212  93 return getLocalSerializer().serialize(getDocumentReference());
213    }
214   
215    /**
216    * @return The fullName of the document which has generated this event, prefixed with the wiki name. Example:
217    * "xwiki:Main.WebHome".
218    */
 
219  43 toggle public String getPrefixedFullName()
220    {
221  43 return getSerializer().serialize(getDocumentReference());
222    }
223   
224    /**
225    * @return The external URL of the document which has fired the event
226    * @deprecated use the XWiki API to get the internal/external URL of the document, using
227    * {@link #getDocumentReference()}.
228    */
 
229  0 toggle @Deprecated
230    public String getUrl()
231    {
232  0 String url = "";
233   
234  0 try {
235  0 XWikiContext context = getXWikiContext();
236  0 url = context.getWiki().getDocument(getDocumentReference(), context).getExternalURL("view", context);
237    } catch (Exception e) {
238    // Do nothing, we don't want to throw exceptions in notification emails.
239    }
240   
241  0 return url;
242    }
243   
244    /**
245    * @return The date when the event occurred.
246    */
 
247  13 toggle public Date getDate()
248    {
249  13 return this.date;
250    }
251   
252    /**
253    * @return Get all the dates of a composite event, if this event is not a composite this list will contain single
254    * entry.
255    */
 
256  4 toggle public List<Date> getDates()
257    {
258  4 if (dates == null) {
259  4 dates = new ArrayList<Date>();
260   
261  4 for (WatchListEvent event : events) {
262  8 dates.add(event.getDate());
263    }
264    }
265   
266  4 return dates;
267    }
268   
269    /**
270    * @return The type of this event (example: "update", "delete").
271    */
 
272  115 toggle public String getType()
273    {
274  115 return type;
275    }
276   
277    /**
278    * @return The user who generated the event.
279    */
 
280  59 toggle public String getAuthor()
281    {
282  59 if (authorReference == null) {
283  0 return XWikiRightService.GUEST_USER_FULLNAME;
284    }
285   
286  59 return getSerializer().serialize(authorReference);
287    }
288   
289    /**
290    * @return Get all the authors of a composite event, if this event is not a composite this list will contain single
291    * entry.
292    */
 
293  34 toggle public List<String> getAuthors()
294    {
295  34 if (authors == null) {
296  34 authors = new ArrayList<String>();
297   
298  34 for (WatchListEvent event : events) {
299  51 String author = event.getAuthor();
300  51 if (!authors.contains(author)) {
301  35 authors.add(author);
302    }
303    }
304    }
305   
306  34 return authors;
307    }
308   
309    /**
310    * @return the user who generated the event
311    */
 
312  18 toggle public DocumentReference getAuthorReference()
313    {
314  18 return authorReference;
315    }
316   
317    /**
318    * @return all the references of the authors of a composite event, if this event is not a composite this list will
319    * contain single entry.
320    * @since 7.1RC1
321    */
 
322  2 toggle public List<DocumentReference> getAuthorReferences()
323    {
324  2 if (authorReferences == null) {
325  2 authorReferences = new ArrayList<DocumentReference>();
326   
327  2 for (WatchListEvent event : events) {
328  2 DocumentReference eventAuthorReference = event.getAuthorReference();
329  2 if (!authorReferences.contains(eventAuthorReference)) {
330  2 authorReferences.add(eventAuthorReference);
331    }
332    }
333    }
334   
335  2 return authorReferences;
336    }
337   
338    /**
339    * @return The version of the document at the time it has generated the event.
340    */
 
341  41 toggle public String getVersion()
342    {
343  41 return version;
344    }
345   
346    /**
347    * @return All the versions from a composite event, if the event is not a composite the list will contain a single
348    * entry
349    */
 
350  7 toggle public List<String> getVersions()
351    {
352  7 if (versions == null) {
353  7 versions = new ArrayList<String>();
354   
355  7 for (WatchListEvent event : events) {
356  11 if (!StringUtils.isBlank(event.getVersion()) && !versions.contains(event.getVersion())) {
357  11 versions.add(event.getVersion());
358    }
359    }
360    }
361   
362  7 return versions;
363    }
364   
365    /**
366    * @return The version of the document which has generated the event, before the actual event.
367    */
 
368  5 toggle public String getPreviousVersion()
369    {
370  5 if (previousVersion == null) {
371  3 String initialVersion = "";
372  3 previousVersion = "";
373   
374  3 try {
375  3 List<String> allVersions = getVersions();
376  3 initialVersion = allVersions.get(allVersions.size() - 1);
377   
378  3 if (initialVersion.equals(INITIAL_DOCUMENT_VERSION)) {
379  2 previousVersion = PREINITIAL_DOCUMENT_VERSION;
380  1 } else if (!StringUtils.isBlank(initialVersion)) {
381    // Retrieve the previous version from the document archive.
382  1 XWikiContext context = getXWikiContext();
383   
384  1 XWikiDocument doc = context.getWiki().getDocument(getDocumentReference(), context);
385  1 XWikiDocument initialDoc = context.getWiki().getDocument(doc, initialVersion, context);
386  1 String docPreviousVersion = initialDoc.getPreviousVersion();
387  1 if (docPreviousVersion != null) {
388  1 this.previousVersion = docPreviousVersion;
389    }
390    }
391    } catch (XWikiException e) {
392    // Catch the exception to be sure we won't send emails containing stacktraces to users.
393  0 e.printStackTrace();
394    }
395    }
396   
397  5 return previousVersion;
398    }
399   
400    /**
401    * @return True if the event is made of multiple events.
402    */
 
403  4 toggle public boolean isComposite()
404    {
405  4 return events.size() > 1;
406    }
407   
408    /**
409    * @return The diff, formatted in HTML, to display to the user when a document has been updated, or null if an error
410    * occurred while computing the diff
411    */
 
412  2 toggle public String getHTMLDiff()
413    {
414    // TODO: Deprecate this method and offer an alternative to compute it from a script service that accesses the
415    // WatchListEventHTMLDiffExtractor component.
416  2 if (htmlDiff == null) {
417  2 try {
418  2 htmlDiff = Utils.getComponent(WatchListEventHTMLDiffExtractor.class).getHTMLDiff(this);
419    } catch (Exception e) {
420    // Catch the exception to be sure we won't send emails containing stacktraces to users.
421  0 e.printStackTrace();
422    }
423    }
424   
425  2 return htmlDiff;
426    }
427   
428    /**
429    * Perform a string comparison on the prefixed fullName of the source document.
430    *
431    * @param event event to compare with
432    * @return the result of the string comparison
433    */
 
434  1 toggle @Override
435    public int compareTo(WatchListEvent event)
436    {
437  1 return getDocumentReference().compareTo(event.getDocumentReference());
438    }
439   
440    /**
441    * @return the document on which the event happened
442    */
 
443  347 toggle public DocumentReference getDocumentReference()
444    {
445  347 return this.documentReference;
446    }
447   
 
448  1 toggle @Override
449    public int hashCode()
450    {
451  1 return hashCode;
452    }
453   
454    /**
455    * Overriding of the default equals method.
456    *
457    * @param obj the ActivityEvent to be compared with
458    * @return True if the two events have been generated by the same document and are equals or conflicting
459    */
 
460  63 toggle @Override
461    public boolean equals(Object obj)
462    {
463  63 if (this == obj) {
464  0 return true;
465    }
466   
467  63 if (!(obj instanceof WatchListEvent)) {
468  0 return false;
469    }
470   
471    // At first this method was returning true when the documents were the same and the events were the same type.
472    // Since we don't want to keep update events for documents that have been deleted this method has been modified
473    // to a point were it performs something different from a equals(), it returns true when obj is a delete event
474    // and 'this' is an update event. See WatchListEventManager#WatchListEventManager(Date, XWikiContext).
475    // TODO: refactoring.
476  63 WatchListEvent event = ((WatchListEvent) obj);
477  63 return this.documentReference.equals(event.getDocumentReference())
478    && WatchListEventType.UPDATE.equals(getType())
479    && (WatchListEventType.UPDATE.equals(event.getType()) || WatchListEventType.DELETE.equals(event.getType()));
480    }
481   
 
482  137 toggle private EntityReferenceSerializer<String> getSerializer()
483    {
484  137 if (this.serializer == null) {
485  51 this.serializer = Utils.getComponent(EntityReferenceSerializer.TYPE_STRING);
486    }
487   
488  137 return this.serializer;
489    }
490   
 
491  108 toggle private EntityReferenceSerializer<String> getLocalSerializer()
492    {
493  108 if (this.localSerializer == null) {
494  64 this.localSerializer = Utils.getComponent(EntityReferenceSerializer.TYPE_STRING, "local");
495    }
496   
497  108 return this.localSerializer;
498    }
499   
 
500  6 toggle private EntityReferenceProvider getDefaultEntityReferenceProvider()
501    {
502  6 if (this.defaultEntityReferenceProvider == null) {
503  2 this.defaultEntityReferenceProvider = Utils.getComponent(EntityReferenceProvider.class);
504    }
505   
506  6 return this.defaultEntityReferenceProvider;
507    }
508   
 
509  1 toggle private XWikiContext getXWikiContext()
510    {
511  1 Provider<XWikiContext> provider = Utils.getComponent(XWikiContext.TYPE_PROVIDER);
512  1 return provider.get();
513    }
514    }