View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.list.common.provider;
23  
24  import org.opencastproject.elasticsearch.index.ElasticsearchIndex;
25  import org.opencastproject.elasticsearch.index.objects.event.Event;
26  import org.opencastproject.elasticsearch.index.objects.event.EventIndexSchema;
27  import org.opencastproject.elasticsearch.index.objects.series.Series;
28  import org.opencastproject.elasticsearch.index.objects.series.SeriesIndexSchema;
29  import org.opencastproject.list.api.ResourceListProvider;
30  import org.opencastproject.list.api.ResourceListQuery;
31  import org.opencastproject.list.util.ListProviderUtil;
32  import org.opencastproject.security.api.User;
33  import org.opencastproject.security.api.UserDirectoryService;
34  
35  import org.apache.commons.lang3.StringUtils;
36  import org.osgi.service.component.annotations.Activate;
37  import org.osgi.service.component.annotations.Component;
38  import org.osgi.service.component.annotations.Modified;
39  import org.osgi.service.component.annotations.Reference;
40  import org.slf4j.Logger;
41  import org.slf4j.LoggerFactory;
42  
43  import java.util.ArrayList;
44  import java.util.Collection;
45  import java.util.Collections;
46  import java.util.Comparator;
47  import java.util.HashMap;
48  import java.util.HashSet;
49  import java.util.Iterator;
50  import java.util.LinkedHashMap;
51  import java.util.List;
52  import java.util.Map;
53  import java.util.Set;
54  import java.util.SortedSet;
55  import java.util.TreeSet;
56  
57  @Component(
58      service = ResourceListProvider.class,
59      property = {
60          "service.description=Contributors list provider",
61          "opencast.service.type=org.opencastproject.list.provider.ContributorsListProvider"
62      }
63  )
64  public class ContributorsListProvider implements ResourceListProvider {
65  
66    private static final String CONFIGURATION_KEY_EXCLUDE_USER_PROVIDER = "exclude.user.provider";
67    private static final String ALL_USER_PROVIDERS_VALUE = "*";
68  
69    private static final String PROVIDER_PREFIX = "CONTRIBUTORS";
70  
71    public static final String DEFAULT = PROVIDER_PREFIX;
72    public static final String NAMES_TO_USERNAMES = PROVIDER_PREFIX + ".NAMES.TO.USERNAMES";
73    public static final String USERNAMES = PROVIDER_PREFIX + ".USERNAMES";
74  
75    protected static final String[] NAMES = { PROVIDER_PREFIX, USERNAMES, NAMES_TO_USERNAMES };
76  
77    private static final Logger logger = LoggerFactory.getLogger(ContributorsListProvider.class);
78  
79    private final Set<String> excludeUserProvider = new HashSet<>();
80    private UserDirectoryService userDirectoryService;
81    private ElasticsearchIndex searchIndex;
82  
83    @Activate
84    protected void activate(Map<String, Object> properties) {
85      modified(properties);
86      logger.info("Contributors list provider activated!");
87    }
88  
89    @Modified
90    public void modified(Map<String, Object> properties) {
91      Object excludeUserProviderValue = properties.get(CONFIGURATION_KEY_EXCLUDE_USER_PROVIDER);
92      excludeUserProvider.clear();
93      if (excludeUserProviderValue != null) {
94        for (String userProvider : StringUtils.split(excludeUserProviderValue.toString(), ',')) {
95          if (StringUtils.trimToNull(userProvider) != null) {
96            excludeUserProvider.add(StringUtils.trimToNull(userProvider));
97          }
98        }
99      }
100     logger.debug("Excluded user providers: {}", excludeUserProvider);
101   }
102 
103   /** OSGi callback for users services. */
104   @Reference
105   public void setUserDirectoryService(UserDirectoryService userDirectoryService) {
106     this.userDirectoryService = userDirectoryService;
107   }
108 
109   /** OSGi callback for the search index. */
110   @Reference
111   public void setIndex(ElasticsearchIndex index) {
112     this.searchIndex = index;
113   }
114 
115   @Override
116   public String[] getListNames() {
117     return NAMES;
118   }
119 
120   @Override
121   public Map<String, String> getList(String listName, ResourceListQuery query) {
122     if (listName.equalsIgnoreCase(USERNAMES)) {
123       return getListWithUserNames(query);
124     } else if (listName.equalsIgnoreCase(NAMES_TO_USERNAMES)) {
125       return getListWithTechnicalPresenters(query);
126     } else {
127       return getList(query);
128     }
129   }
130 
131   /**
132    * Get all of the contributors with friendly printable names.
133    *
134    * @param query
135    *          The query for the list including limit and offset.
136    * @return The {@link Map} including all of the contributors.
137    */
138   protected Map<String, String> getList(ResourceListQuery query) {
139     Map<String, String> usersList = new HashMap<String, String>();
140     int offset = 0;
141     int limit = 0;
142     SortedSet<String> contributorsList = new TreeSet<String>(new Comparator<String>() {
143       @Override
144       public int compare(String name1, String name2) {
145         return name1.compareTo(name2);
146       }
147     });
148 
149     if (!excludeUserProvider.contains(ALL_USER_PROVIDERS_VALUE)) {
150       Iterator<User> users = userDirectoryService.findUsers("%", offset, limit);
151       while (users.hasNext()) {
152         User u = users.next();
153         if (!excludeUserProvider.contains(u.getProvider()) && StringUtils.isNotBlank(u.getName())) {
154           contributorsList.add(u.getName());
155         }
156       }
157     }
158 
159     contributorsList.addAll(searchIndex.getTermsForField(EventIndexSchema.CONTRIBUTOR,
160             Event.DOCUMENT_TYPE));
161     contributorsList.addAll(searchIndex.getTermsForField(EventIndexSchema.PRESENTER,
162             Event.DOCUMENT_TYPE));
163     contributorsList.addAll(searchIndex.getTermsForField(EventIndexSchema.PUBLISHER,
164             Event.DOCUMENT_TYPE));
165     contributorsList.addAll(searchIndex.getTermsForField(SeriesIndexSchema.CONTRIBUTORS,
166             Series.DOCUMENT_TYPE));
167     contributorsList.addAll(searchIndex.getTermsForField(SeriesIndexSchema.ORGANIZERS,
168             Series.DOCUMENT_TYPE));
169     contributorsList.addAll(searchIndex.getTermsForField(SeriesIndexSchema.PUBLISHERS,
170             Series.DOCUMENT_TYPE));
171 
172     // TODO: TThis is not a good idea.
173     // TODO: The search index can handle limit and offset.
174     // TODO: We should not request all data.
175     if (query != null) {
176       if (query.getLimit().isPresent()) {
177         limit = query.getLimit().get();
178       }
179 
180       if (query.getOffset().isPresent()) {
181         offset = query.getOffset().get();
182       }
183     }
184 
185     int i = 0;
186 
187     for (String contributor : contributorsList) {
188       if (i >= offset && (limit == 0 || i < limit)) {
189         usersList.put(contributor, contributor);
190       }
191       i++;
192     }
193 
194     return usersList;
195   }
196 
197   /**
198    * Get the contributors list including usernames and organizations for the users available.
199    *
200    * @param query
201    *          The query for the list including limit and offset.
202    * @return The {@link Map} including all of the contributors.
203    */
204   protected Map<String, String> getListWithTechnicalPresenters(ResourceListQuery query) {
205     int offset = 0;
206     int limit = 0;
207 
208     List<Contributor> contributorsList = new ArrayList<Contributor>();
209 
210     HashSet<String> labels = new HashSet<String>();
211 
212     if (!excludeUserProvider.contains(ALL_USER_PROVIDERS_VALUE)) {
213       Iterator<User> users = userDirectoryService.findUsers("%", offset, limit);
214       while (users.hasNext()) {
215         User u = users.next();
216         if (!excludeUserProvider.contains(u.getProvider())) {
217           if (StringUtils.isNotBlank(u.getName())) {
218             contributorsList.add(new Contributor(u.getUsername(), u.getName()));
219             labels.add(u.getName());
220           } else {
221             contributorsList.add(new Contributor(u.getUsername(), u.getUsername()));
222             labels.add(u.getUsername());
223           }
224         }
225       }
226     }
227 
228     addIndexNamesToMap(labels, contributorsList, searchIndex
229             .getTermsForField(EventIndexSchema.PRESENTER, Event.DOCUMENT_TYPE));
230     addIndexNamesToMap(labels, contributorsList, searchIndex
231             .getTermsForField(EventIndexSchema.CONTRIBUTOR, Event.DOCUMENT_TYPE));
232     addIndexNamesToMap(labels, contributorsList, searchIndex
233             .getTermsForField(SeriesIndexSchema.CONTRIBUTORS, Event.DOCUMENT_TYPE));
234     addIndexNamesToMap(labels, contributorsList, searchIndex
235             .getTermsForField(SeriesIndexSchema.ORGANIZERS, Event.DOCUMENT_TYPE));
236     addIndexNamesToMap(labels, contributorsList, searchIndex
237             .getTermsForField(SeriesIndexSchema.PUBLISHERS, Event.DOCUMENT_TYPE));
238 
239     Collections.sort(contributorsList, new Comparator<Contributor>() {
240       @Override
241       public int compare(Contributor contributor1, Contributor contributor2) {
242         return contributor1.getLabel().compareTo(contributor2.getLabel());
243       }
244     });
245 
246     Map<String, String> contributorMap = new LinkedHashMap<>();
247     for (Contributor contributor : contributorsList) {
248       contributorMap.put(contributor.getKey(), contributor.getLabel());
249     }
250 
251     return ListProviderUtil.filterMap(contributorMap, query);
252   }
253 
254   /**
255    * Get the contributors list including usernames and organizations for the users available.
256    *
257    * @param query
258    *          The query for the list including limit and offset.
259    * @return The {@link Map} including all of the contributors.
260    */
261   protected Map<String, String> getListWithUserNames(ResourceListQuery query) {
262 
263     int offset = 0;
264     int limit = 0;
265 
266     List<Contributor> contributorsList = new ArrayList<Contributor>();
267 
268     HashSet<String> labels = new HashSet<String>();
269 
270     if (!excludeUserProvider.contains(ALL_USER_PROVIDERS_VALUE)) {
271       Iterator<User> users = userDirectoryService.findUsers("%", offset, limit);
272       while (users.hasNext()) {
273         User u = users.next();
274         if (!excludeUserProvider.contains(u.getProvider())) {
275           if (StringUtils.isNotBlank(u.getName())) {
276             contributorsList.add(new Contributor(u.getUsername(), u.getName()));
277             labels.add(u.getName());
278           } else {
279             contributorsList.add(new Contributor(u.getUsername(), u.getUsername()));
280             labels.add(u.getUsername());
281           }
282         }
283       }
284     }
285 
286     Collections.sort(contributorsList, new Comparator<Contributor>() {
287       @Override
288       public int compare(Contributor contributor1, Contributor contributor2) {
289         return contributor1.getLabel().compareTo(contributor2.getLabel());
290       }
291     });
292 
293     Map<String, String> contributorMap = new LinkedHashMap<>();
294     for (Contributor contributor : contributorsList) {
295       contributorMap.put(contributor.getKey(), contributor.getLabel());
296     }
297 
298     return ListProviderUtil.filterMap(contributorMap, query);
299   }
300 
301   /**
302    * Add all names in the index to the map if they aren't already present as a user.
303    *
304    * @param userLabels
305    *          The collection of user labels, the full names of the users.
306    * @param contributors
307    *          The collection of all contributors including the index names that will be added.
308    * @param indexNames
309    *          The list of new names from the index.
310    */
311   protected void addIndexNamesToMap(Set<String> userLabels, Collection<Contributor> contributors,
312           List<String> indexNames) {
313     for (String indexName : indexNames) {
314       if (!userLabels.contains(indexName)) {
315         contributors.add(new Contributor(indexName, indexName));
316       }
317     }
318   }
319 
320   @Override
321   public boolean isTranslatable(String listName) {
322     return false;
323   }
324 
325   @Override
326   public String getDefault() {
327     return null;
328   }
329 
330   private class Contributor {
331     public String getKey() {
332       return key;
333     }
334 
335     public String getLabel() {
336       return label;
337     }
338 
339     private String key;
340     private String label;
341 
342     Contributor(String key, String label) {
343       this.key = key;
344       this.label = label;
345     }
346 
347     @Override
348     public String toString() {
349       return key + ":" + label;
350     }
351   }
352 }