1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.list.common.provider;
23
24 import org.opencastproject.elasticsearch.api.SearchIndexException;
25 import org.opencastproject.elasticsearch.api.SearchResult;
26 import org.opencastproject.elasticsearch.api.SearchResultItem;
27 import org.opencastproject.elasticsearch.index.ElasticsearchIndex;
28 import org.opencastproject.elasticsearch.index.objects.series.Series;
29 import org.opencastproject.elasticsearch.index.objects.series.SeriesIndexSchema;
30 import org.opencastproject.elasticsearch.index.objects.series.SeriesSearchQuery;
31 import org.opencastproject.list.api.ListProviderException;
32 import org.opencastproject.list.api.ResourceListFilter;
33 import org.opencastproject.list.api.ResourceListProvider;
34 import org.opencastproject.list.api.ResourceListQuery;
35 import org.opencastproject.list.common.query.SeriesListQuery;
36 import org.opencastproject.security.api.Permissions;
37 import org.opencastproject.security.api.SecurityService;
38 import org.opencastproject.util.data.Tuple;
39 import org.opencastproject.util.requests.SortCriterion;
40
41 import org.apache.commons.lang3.StringUtils;
42 import org.osgi.framework.BundleContext;
43 import org.osgi.service.component.annotations.Activate;
44 import org.osgi.service.component.annotations.Component;
45 import org.osgi.service.component.annotations.Reference;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Calendar;
52 import java.util.Date;
53 import java.util.HashMap;
54 import java.util.List;
55 import java.util.Map;
56 import java.util.function.Function;
57 import java.util.stream.Collectors;
58
59 @Component(
60 service = ResourceListProvider.class,
61 property = {
62 "service.description=Series list provider",
63 "opencast.service.type=org.opencastproject.list.provider.SeriesListProvider"
64 }
65 )
66 public class SeriesListProvider implements ResourceListProvider {
67 private static final Logger logger = LoggerFactory.getLogger(SeriesListProvider.class);
68
69 public static final String PROVIDER_PREFIX = "SERIES";
70
71 public static final String NAME = PROVIDER_PREFIX + ".NAME";
72 public static final String CONTRIBUTORS = PROVIDER_PREFIX + ".CONTRIBUTORS";
73 public static final String SUBJECT = PROVIDER_PREFIX + ".SUBJECT";
74 public static final String TITLE = PROVIDER_PREFIX + ".TITLE";
75 public static final String TITLE_EXTENDED = PROVIDER_PREFIX + ".TITLE_EXTENDED";
76 public static final String LANGUAGE = PROVIDER_PREFIX + ".LANGUAGE";
77 public static final String ORGANIZERS = PROVIDER_PREFIX + ".ORGANIZERS";
78 public static final String LICENSE = PROVIDER_PREFIX + ".LICENSE";
79 public static final String SERIES_WRITE_ONLY = PROVIDER_PREFIX + ".WRITE_ONLY";
80
81 private static final String[] NAMES = { PROVIDER_PREFIX, CONTRIBUTORS, ORGANIZERS, TITLE_EXTENDED,
82 SERIES_WRITE_ONLY };
83
84
85 private ElasticsearchIndex searchIndex;
86
87
88 private SecurityService securityService;
89
90 @Activate
91 protected void activate(BundleContext bundleContext) {
92 logger.info("Series list provider activated!");
93 }
94
95
96 @Reference
97 public void setSearchIndex(ElasticsearchIndex searchIndex) {
98 this.searchIndex = searchIndex;
99 }
100
101
102 @Reference
103 public void setSecurityService(SecurityService securityService) {
104 this.securityService = securityService;
105 }
106
107 @Override
108 public String[] getListNames() {
109 return NAMES;
110 }
111
112 @Override
113 public Map<String, String> getList(String listName, ResourceListQuery query)
114 throws ListProviderException {
115 SeriesSearchQuery seriesQuery = toSearchQuery(query);
116 Map<String, String> result = new HashMap<>();
117 if (TITLE.equals(listName)) {
118 seriesQuery.sortByTitle(SortCriterion.Order.Ascending);
119 for (String title : searchIndex.getTermsForField(SeriesIndexSchema.TITLE, Series.DOCUMENT_TYPE)) {
120 result.put(title, title);
121 }
122 } else if (CONTRIBUTORS.equals(listName)) {
123 seriesQuery.sortByContributors(SortCriterion.Order.Ascending);
124 for (String contributor : searchIndex.getTermsForField(SeriesIndexSchema.CONTRIBUTORS, Series.DOCUMENT_TYPE)) {
125 result.put(contributor, contributor);
126 }
127 } else if (ORGANIZERS.equals(listName)) {
128 seriesQuery.sortByOrganizers(SortCriterion.Order.Ascending);
129 for (String organizer : searchIndex.getTermsForField(SeriesIndexSchema.ORGANIZERS, Series.DOCUMENT_TYPE)) {
130 result.put(organizer, organizer);
131 }
132 } else {
133 try {
134 seriesQuery.sortByTitle(SortCriterion.Order.Ascending);
135 seriesQuery.sortByCreatedDateTime(SortCriterion.Order.Descending);
136 seriesQuery.sortByOrganizers(SortCriterion.Order.Ascending);
137 if (SERIES_WRITE_ONLY.equals(listName)) {
138 seriesQuery.withAction(Permissions.Action.WRITE);
139 }
140 SearchResult<Series> searchResult = searchIndex.getByQuery(seriesQuery);
141 Calendar calendar = Calendar.getInstance();
142
143 Map<String, Long> duplicates = Arrays.stream(searchResult.getItems())
144 .map(series -> series.getSource().getTitle())
145 .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
146 for (SearchResultItem<Series> item : searchResult.getItems()) {
147 Series s = item.getSource();
148 if (TITLE_EXTENDED.equals(listName)) {
149 Date created = s.getCreatedDateTime();
150 List<String> organizers = s.getOrganizers();
151 StringBuilder sb = new StringBuilder(s.getTitle());
152 if (created != null || (organizers != null && !organizers.isEmpty())) {
153 List<String> extendedTitleData = new ArrayList<>();
154 if (created != null) {
155 calendar.setTime(created);
156 extendedTitleData.add(Integer.toString(calendar.get(Calendar.YEAR)));
157 }
158 if (organizers != null && !organizers.isEmpty()) {
159 extendedTitleData.addAll(organizers);
160 }
161 sb.append(" (").append(StringUtils.join(extendedTitleData, ", ")).append(")");
162 }
163 result.put(s.getIdentifier(), sb.toString());
164 } else if (PROVIDER_PREFIX.equals(listName)) {
165 String newSeriesName = s.getTitle();
166 if (duplicates.get(newSeriesName) > 1L) {
167
168
169 if (s.getIdentifier().length() > 8) {
170 newSeriesName += " " + "(ID: " + s.getIdentifier().substring(0, 8) + "...)";
171 } else {
172 newSeriesName += " " + "(ID: " + s.getIdentifier() + ")";
173 }
174 logger.trace(String.format("Repeated series title \"%s\" found, changing to \"%s\" for admin-ui display",
175 s.getTitle(), newSeriesName));
176 }
177 result.put(s.getIdentifier(), newSeriesName);
178 } else {
179 result.put(s.getIdentifier(), s.getTitle());
180 }
181 }
182 } catch (SearchIndexException e) {
183 logger.warn("Unable to query series.", e);
184 }
185 }
186 return result;
187 }
188
189 @Override
190 public boolean isTranslatable(String listName) {
191 return false;
192 }
193
194 @Override
195 public String getDefault() {
196 return null;
197 }
198
199
200
201
202
203
204
205 protected SeriesSearchQuery toSearchQuery(ResourceListQuery query) {
206 SeriesSearchQuery seriesQuery = new SeriesSearchQuery(securityService.getOrganization().getId(),
207 securityService.getUser());
208 if (query.getLimit().isPresent()) {
209 seriesQuery.withLimit(query.getLimit().get());
210 }
211 if (query.getOffset().isPresent()) {
212 seriesQuery.withOffset(query.getOffset().get());
213 }
214
215 for (ResourceListFilter filter : query.getFilters()) {
216 if (filter.getValue().isEmpty()) {
217 continue;
218 } else if (SeriesListQuery.FILTER_CREATIONDATE_NAME.equals(filter.getName())) {
219 Tuple<Date, Date> creationDate = (Tuple<Date, Date>) filter.getValue().get();
220 if (creationDate.getA() != null) {
221 seriesQuery.withCreatedFrom(creationDate.getA());
222 }
223 if (creationDate.getB() != null) {
224 seriesQuery.withCreatedTo(creationDate.getB());
225 }
226 } else if (SeriesListQuery.FILTER_CREATOR_NAME.equals(filter.getName())) {
227 seriesQuery.withCreator((String)filter.getValue().get());
228 } else if (SeriesListQuery.FILTER_CONTRIBUTORS_NAME.equals(filter.getName())) {
229 seriesQuery.withContributor((String)filter.getValue().get());
230 } else if (SeriesListQuery.FILTER_LANGUAGE_NAME.equals(filter.getName())) {
231 seriesQuery.withLanguage((String)filter.getValue().get());
232 } else if (SeriesListQuery.FILTER_LICENSE_NAME.equals(filter.getName())) {
233 seriesQuery.withLicense((String)filter.getValue().get());
234 } else if (SeriesListQuery.FILTER_ORGANIZERS_NAME.equals(filter.getName())) {
235 seriesQuery.withOrganizer((String)filter.getValue().get());
236 } else if (SeriesListQuery.FILTER_SUBJECT_NAME.equals(filter.getName())) {
237 seriesQuery.withSubject((String)filter.getValue().get());
238 } else if (SeriesListQuery.FILTER_TEXT_NAME.equals(filter.getName())) {
239 seriesQuery.withText((String)filter.getValue().get());
240 } else if (SeriesListQuery.FILTER_TITLE_NAME.equals(filter.getName())) {
241 seriesQuery.withTitle((String)filter.getValue().get());
242 }
243 }
244
245 if (query instanceof SeriesListQuery) {
246 if (((SeriesListQuery) query).getReadPermission().isPresent()
247 || ((SeriesListQuery) query).getWritePermission().isPresent()) {
248 seriesQuery.withoutActions();
249 if (((SeriesListQuery) query).getReadPermission().orElse(true)) {
250 seriesQuery.withAction(Permissions.Action.READ);
251 }
252 if (((SeriesListQuery) query).getWritePermission().orElse(false)) {
253 seriesQuery.withAction(Permissions.Action.WRITE);
254 }
255 }
256 }
257 return seriesQuery;
258 }
259 }