1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.watchlist.internal

File DefaultWatchListStore.java

 

Coverage histogram

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

Code metrics

24
88
12
1
335
196
36
0.41
7.33
12
3

Classes

Class Line # Actions
DefaultWatchListStore 51 88 0% 36 14
0.8870967688.7%
 

Contributing tests

No tests hitting this source file were found.

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;
21   
22    import java.util.ArrayList;
23    import java.util.Collection;
24    import java.util.List;
25   
26    import javax.inject.Inject;
27    import javax.inject.Provider;
28    import javax.inject.Singleton;
29   
30    import org.apache.commons.lang3.StringUtils;
31    import org.slf4j.Logger;
32    import org.xwiki.component.annotation.Component;
33    import org.xwiki.localization.ContextualLocalizationManager;
34    import org.xwiki.watchlist.internal.api.AutomaticWatchMode;
35    import org.xwiki.watchlist.internal.api.WatchListStore;
36    import org.xwiki.watchlist.internal.api.WatchedElementType;
37    import org.xwiki.watchlist.internal.documents.WatchListClassDocumentInitializer;
38   
39    import com.xpn.xwiki.XWikiContext;
40    import com.xpn.xwiki.XWikiException;
41    import com.xpn.xwiki.doc.XWikiDocument;
42    import com.xpn.xwiki.objects.BaseObject;
43   
44    /**
45    * WatchList store class. Handles user subscription storage.
46    *
47    * @version $Id: 01e7f81e4f408002aa63cde25e44b3a0b08a2ac3 $
48    */
49    @Component
50    @Singleton
 
51    public class DefaultWatchListStore implements WatchListStore
52    {
53    /**
54    * Character used to separated wiki and space in XWiki model.
55    */
56    public static final String WIKI_SPACE_SEP = ":";
57   
58    /**
59    * Character used to separated space and page in XWiki model.
60    */
61    public static final String SPACE_PAGE_SEP = ".";
62   
63    /**
64    * Character used to separated values in XProperties lists.
65    */
66    public static final String PIPE_SEP = "|";
67   
68    /**
69    * XWiki Class used to store user.
70    */
71    public static final String USERS_CLASS = "XWiki.XWikiUsers";
72   
73    /**
74    * Logging helper object.
75    */
76    @Inject
77    private Logger logger;
78   
79    /**
80    * Context provider.
81    */
82    @Inject
83    private Provider<XWikiContext> contextProvider;
84   
85    /**
86    * Used to read cached notification related information.
87    */
88    @Inject
89    private Provider<WatchListNotificationCache> notificationCache;
90   
91    /**
92    * Used to resolve translations.
93    */
94    @Inject
95    private ContextualLocalizationManager localization;
96   
 
97  498 toggle @Override
98    public Collection<String> getWatchedElements(String user, WatchedElementType type) throws XWikiException
99    {
100  498 BaseObject watchListObject = getWatchListObject(user);
101  492 List<String> watchedItems = watchListObject.getListValue(getWatchListClassPropertyForType(type));
102   
103  492 return watchedItems;
104    }
105   
106    /**
107    * Gets the WatchList XWiki Object from a user's profile's page.
108    *
109    * @param user the user to check
110    * @return the WatchList XWiki BaseObject
111    * @throws XWikiException if BaseObject creation fails or if user does not exists
112    */
 
113  562 toggle public BaseObject getWatchListObject(String user) throws XWikiException
114    {
115  562 XWikiContext context = contextProvider.get();
116   
117  562 XWikiDocument userDocument = context.getWiki().getDocument(user, context);
118  562 if (userDocument.isNew() || userDocument.getObject(USERS_CLASS) == null) {
119  6 throw new XWikiException(XWikiException.MODULE_XWIKI_PLUGINS, XWikiException.ERROR_XWIKI_UNKNOWN, "User ["
120    + user + "] does not exists");
121    }
122   
123  556 BaseObject obj = userDocument.getObject(WatchListClassDocumentInitializer.DOCUMENT_FULL_NAME);
124  556 if (obj == null) {
125  6 obj = createWatchListObject(user, context);
126    }
127   
128  556 return obj;
129    }
130   
131    /**
132    * Creates a WatchList XWiki Object in the user's profile's page.
133    *
134    * @param user XWiki User
135    * @param context Context of the request
136    * @return the watchlist object that has been created
137    * @throws XWikiException if the document cannot be saved
138    */
 
139  6 toggle public BaseObject createWatchListObject(String user, XWikiContext context) throws XWikiException
140    {
141  6 XWikiDocument userDocument = context.getWiki().getDocument(user, context);
142   
143  6 BaseObject object = userDocument.newObject(WatchListClassDocumentInitializer.DOCUMENT_FULL_NAME, context);
144  6 context.getWiki().saveDocument(userDocument, this.localization.getTranslationPlain("watchlist.create.object"),
145    true, context);
146   
147  6 return object;
148    }
149   
150    /**
151    * Get the name of the XClass property the given type is stored in.
152    *
153    * @param type type to retrieve
154    * @return the name of the XClass property
155    */
 
156  512 toggle private String getWatchListClassPropertyForType(WatchedElementType type)
157    {
158  512 String result = StringUtils.EMPTY;
159   
160  512 switch (type) {
161  139 case WIKI:
162  139 result = WatchListClassDocumentInitializer.WIKIS_PROPERTY;
163  139 break;
164  142 case SPACE:
165  142 result = WatchListClassDocumentInitializer.SPACES_PROPERTY;
166  142 break;
167  173 case DOCUMENT:
168  173 result = WatchListClassDocumentInitializer.DOCUMENTS_PROPERTY;
169  173 break;
170  58 case USER:
171  58 result = WatchListClassDocumentInitializer.USERS_PROPERTY;
172  58 break;
173  0 default:
174  0 break;
175    }
176   
177  512 return result;
178    }
179   
 
180  371 toggle @Override
181    public boolean isWatched(String element, String user, WatchedElementType type) throws XWikiException
182    {
183    // TODO: Can this be optimized by a direct "exists" query on the list item? Would it e better than what we
184    // currently have with the document cache? If we try a query, it would also need to be performed on the user's
185    // wiki/database, not the current one.
186  371 Collection<String> watchedElements = getWatchedElements(user, type);
187  365 if (WatchedElementType.SPACE.equals(type)) {
188    // Special handling for Nested Spaces
189  117 for (String watchedSpace : watchedElements) {
190    // Check if there is an exact match on the watched space or if the current space is nested inside a
191    // watched space.
192  55 String watchedSpacePrefix = String.format("%s.", watchedSpace);
193  55 if (element.equals(watchedSpace) || element.startsWith(watchedSpacePrefix)) {
194  6 return true;
195    }
196    }
197   
198  111 return false;
199    } else {
200  248 return watchedElements.contains(element);
201    }
202    }
203   
 
204  16 toggle @Override
205    public boolean addWatchedElement(String user, String newWatchedElement, WatchedElementType type)
206    throws XWikiException
207    {
208  16 XWikiContext context = contextProvider.get();
209  16 String elementToWatch = newWatchedElement;
210   
211  16 if (!WatchedElementType.WIKI.equals(type) && !newWatchedElement.contains(WIKI_SPACE_SEP)) {
212  0 elementToWatch = context.getWikiId() + WIKI_SPACE_SEP + newWatchedElement;
213    }
214   
215  16 if (isWatched(elementToWatch, user, type)) {
216  1 return false;
217    }
218   
219    // Copy the list of watched elements because it could be unmodifiable.
220  15 List<String> watchedElements = new ArrayList<String>(getWatchedElements(user, type));
221   
222  15 watchedElements.add(elementToWatch);
223   
224  15 setWatchListElementsProperty(user, type, watchedElements);
225  15 return true;
226    }
227   
228    /**
229    * Sets a DBList property in the user's WatchList Object, then saves the user's profile.
230    *
231    * @param user the user whose watchlist to set
232    * @param type the element type as defined by {@link WatchedElementType}
233    * @param elements the elements to store
234    * @throws XWikiException if the user's profile cannot be saved
235    */
 
236  20 toggle private void setWatchListElementsProperty(String user, WatchedElementType type, Collection<String> elements)
237    throws XWikiException
238    {
239  20 XWikiContext context = contextProvider.get();
240  20 XWikiDocument userDocument = context.getWiki().getDocument(user, context);
241   
242  20 List<String> elementsList = new ArrayList<String>(elements);
243  20 userDocument.setDBStringListValue(WatchListClassDocumentInitializer.DOCUMENT_FULL_NAME,
244    getWatchListClassPropertyForType(type), elementsList);
245   
246  20 context.getWiki().saveDocument(userDocument, localization.getTranslationPlain("watchlist.save.object"), true,
247    context);
248    }
249   
 
250  5 toggle @Override
251    public boolean removeWatchedElement(String user, String watchedElement, WatchedElementType type)
252    throws XWikiException
253    {
254  5 XWikiContext context = contextProvider.get();
255  5 String elementToRemove = watchedElement;
256   
257  5 if (!WatchedElementType.WIKI.equals(type) && !watchedElement.contains(WIKI_SPACE_SEP)) {
258  0 elementToRemove = context.getWikiId() + WIKI_SPACE_SEP + watchedElement;
259    }
260   
261  5 if (!this.isWatched(elementToRemove, user, type)) {
262  0 return false;
263    }
264   
265  5 Collection<String> watchedElements = getWatchedElements(user, type);
266  5 watchedElements.remove(elementToRemove);
267   
268  5 this.setWatchListElementsProperty(user, type, watchedElements);
269  5 return true;
270    }
271   
 
272  48 toggle @Override
273    public AutomaticWatchMode getAutomaticWatchMode(String user)
274    {
275  48 XWikiContext context = contextProvider.get();
276  48 AutomaticWatchMode mode = null;
277   
278  48 try {
279  48 BaseObject watchObject = getWatchListObject(user);
280   
281  48 String value = watchObject.getStringValue(WatchListClassDocumentInitializer.AUTOMATICWATCH_PROPERTY);
282   
283  48 if (StringUtils.isNotBlank(value)
284    && !WatchListClassDocumentInitializer.AUTOMATICWATCH_DEFAULT_VALUE.equals(value)) {
285  12 mode = AutomaticWatchMode.valueOf(value);
286    }
287    } catch (Exception e) {
288    // Failed for some reason, now try getting it from xwiki.cfg
289  0 logger.error("Failed to get automatic watch mode for user [{}], using fallbacks", user, e);
290    }
291   
292  48 if (mode == null) {
293  36 String value = context.getWiki().Param("xwiki.plugin.watchlist.automaticwatch");
294   
295  36 if (value != null) {
296  0 try {
297  0 mode = AutomaticWatchMode.valueOf(value.toUpperCase());
298    } catch (Exception e) {
299  0 logger.warn("Invalid configuration in xwiki.plugin.watchlist.automaticwatch", e);
300    }
301    }
302    }
303   
304  48 return mode != null ? mode : AutomaticWatchMode.MAJOR;
305    }
306   
 
307  111 toggle @Override
308    public List<String> getIntervals()
309    {
310  111 return notificationCache.get().getIntervals();
311    }
312   
 
313  16 toggle @Override
314    public String getInterval(String user)
315    {
316  16 String result = "";
317   
318  16 try {
319  16 BaseObject watchObject = getWatchListObject(user);
320   
321  16 result = watchObject.getStringValue(WatchListClassDocumentInitializer.INTERVAL_PROPERTY);
322    } catch (Exception e) {
323    // Failed for some reason, now try getting it from xwiki.cfg
324  0 logger.error("Failed to get notification interval for user [{}], using fallbacks", user, e);
325    }
326   
327  16 return result;
328    }
329   
 
330  59 toggle @Override
331    public Collection<String> getSubscribers(String intervalId)
332    {
333  59 return notificationCache.get().getSubscribers(intervalId);
334    }
335    }