1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.kew.docsearch.service.impl;
17
18 import org.apache.commons.collections.CollectionUtils;
19 import org.apache.commons.lang.StringUtils;
20 import org.joda.time.DateTime;
21 import org.joda.time.MutableDateTime;
22 import org.kuali.rice.core.api.CoreApiServiceLocator;
23 import org.kuali.rice.core.api.config.property.ConfigContext;
24 import org.kuali.rice.core.api.config.property.ConfigurationService;
25 import org.kuali.rice.core.api.reflect.ObjectDefinition;
26 import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
27 import org.kuali.rice.core.api.uif.RemotableAttributeError;
28 import org.kuali.rice.core.api.uif.RemotableAttributeField;
29 import org.kuali.rice.core.api.util.ConcreteKeyValue;
30 import org.kuali.rice.core.api.util.KeyValue;
31 import org.kuali.rice.kew.api.KewApiConstants;
32 import org.kuali.rice.kew.api.WorkflowRuntimeException;
33 import org.kuali.rice.kew.api.document.attribute.DocumentAttribute;
34 import org.kuali.rice.kew.api.document.attribute.DocumentAttributeFactory;
35 import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
36 import org.kuali.rice.kew.api.document.search.DocumentSearchResult;
37 import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
38 import org.kuali.rice.kew.docsearch.DocumentSearchCustomizationMediator;
39 import org.kuali.rice.kew.docsearch.DocumentSearchInternalUtils;
40 import org.kuali.rice.kew.docsearch.dao.DocumentSearchDAO;
41 import org.kuali.rice.kew.docsearch.service.DocumentSearchService;
42 import org.kuali.rice.kew.doctype.SecuritySession;
43 import org.kuali.rice.kew.doctype.bo.DocumentType;
44 import org.kuali.rice.kew.exception.WorkflowServiceError;
45 import org.kuali.rice.kew.exception.WorkflowServiceErrorException;
46 import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
47 import org.kuali.rice.kew.framework.document.search.AttributeFields;
48 import org.kuali.rice.kew.framework.document.search.DocumentSearchCriteriaConfiguration;
49 import org.kuali.rice.kew.framework.document.search.DocumentSearchResultValue;
50 import org.kuali.rice.kew.framework.document.search.DocumentSearchResultValues;
51 import org.kuali.rice.kew.impl.document.search.DocumentSearchGenerator;
52 import org.kuali.rice.kew.impl.document.search.DocumentSearchGeneratorImpl;
53 import org.kuali.rice.kew.service.KEWServiceLocator;
54 import org.kuali.rice.kew.useroptions.UserOptions;
55 import org.kuali.rice.kew.useroptions.UserOptionsService;
56 import org.kuali.rice.kew.util.Utilities;
57 import org.kuali.rice.kim.api.group.Group;
58 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
59 import org.kuali.rice.kns.service.DictionaryValidationService;
60 import org.kuali.rice.kns.service.DataDictionaryService;
61 import org.kuali.rice.kns.service.KNSServiceLocator;
62 import org.kuali.rice.krad.service.KRADServiceLocator;
63 import org.kuali.rice.krad.util.GlobalVariables;
64
65 import java.io.IOException;
66 import java.text.SimpleDateFormat;
67 import java.util.ArrayList;
68 import java.util.Collection;
69 import java.util.Collections;
70 import java.util.HashMap;
71 import java.util.HashSet;
72 import java.util.LinkedHashMap;
73 import java.util.List;
74 import java.util.Map;
75 import java.util.Set;
76
77 public class DocumentSearchServiceImpl implements DocumentSearchService {
78
79 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentSearchServiceImpl.class);
80
81 private static final int MAX_SEARCH_ITEMS = 5;
82 private static final String LAST_SEARCH_ORDER_OPTION = "DocSearch.LastSearch.Order";
83 private static final String NAMED_SEARCH_ORDER_BASE = "DocSearch.NamedSearch.";
84 private static final String LAST_SEARCH_BASE_NAME = "DocSearch.LastSearch.Holding";
85 private static final String DOC_SEARCH_CRITERIA_CLASS = "org.kuali.rice.kew.api.document.search.DocumentSearchCriteria";
86 private static final String DATA_TYPE_DATE = "datetime";
87
88 private volatile ConfigurationService kualiConfigurationService;
89 private DocumentSearchCustomizationMediator documentSearchCustomizationMediator;
90
91 private DocumentSearchDAO docSearchDao;
92 private UserOptionsService userOptionsService;
93
94 private static DictionaryValidationService dictionaryValidationService;
95 private static DataDictionaryService dataDictionaryService;
96
97 public void setDocumentSearchDAO(DocumentSearchDAO docSearchDao) {
98 this.docSearchDao = docSearchDao;
99 }
100
101 public void setUserOptionsService(UserOptionsService userOptionsService) {
102 this.userOptionsService = userOptionsService;
103 }
104
105 public void setDocumentSearchCustomizationMediator(DocumentSearchCustomizationMediator documentSearchCustomizationMediator) {
106 this.documentSearchCustomizationMediator = documentSearchCustomizationMediator;
107 }
108
109 protected DocumentSearchCustomizationMediator getDocumentSearchCustomizationMediator() {
110 return this.documentSearchCustomizationMediator;
111 }
112
113 public void clearNamedSearches(String principalId) {
114 String[] clearListNames = { NAMED_SEARCH_ORDER_BASE + "%", LAST_SEARCH_BASE_NAME + "%", LAST_SEARCH_ORDER_OPTION + "%" };
115 for (String clearListName : clearListNames)
116 {
117 List<UserOptions> records = userOptionsService.findByUserQualified(principalId, clearListName);
118 for (UserOptions userOptions : records) {
119 userOptionsService.deleteUserOptions((UserOptions) userOptions);
120 }
121 }
122 }
123
124 public DocumentSearchCriteria getNamedSearchCriteria(String principalId, String searchName) {
125 return getSavedSearchCriteria(principalId, NAMED_SEARCH_ORDER_BASE + searchName);
126 }
127
128 public DocumentSearchCriteria getSavedSearchCriteria(String principalId, String searchName) {
129 UserOptions savedSearch = userOptionsService.findByOptionId(searchName, principalId);
130 if (savedSearch == null) {
131 return null;
132 }
133 return getCriteriaFromSavedSearch(savedSearch);
134 }
135
136 protected DocumentSearchCriteria getCriteriaFromSavedSearch(UserOptions savedSearch) {
137 String optionValue = savedSearch.getOptionVal();
138 try {
139 return DocumentSearchInternalUtils.unmarshalDocumentSearchCriteria(optionValue);
140 } catch (IOException e) {
141 throw new WorkflowRuntimeException("Failed to load saved search for name '" + savedSearch.getOptionId() + "'", e);
142 }
143 }
144
145 private String getOptionCriteriaField(UserOptions userOption, String fieldName) {
146 String value = userOption.getOptionVal();
147 if (value != null) {
148 String[] fields = value.split(",,");
149 for (String field : fields)
150 {
151 if (field.startsWith(fieldName + "="))
152 {
153 return field.substring(field.indexOf(fieldName) + fieldName.length() + 1, field.length());
154 }
155 }
156 }
157 return null;
158 }
159
160 @Override
161 public DocumentSearchResults lookupDocuments(String principalId, DocumentSearchCriteria criteria) {
162 DocumentSearchGenerator docSearchGenerator = getStandardDocumentSearchGenerator();
163 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(criteria.getDocumentTypeName());
164 DocumentSearchCriteria.Builder criteriaBuilder = DocumentSearchCriteria.Builder.create(criteria);
165 validateDocumentSearchCriteria(docSearchGenerator, criteriaBuilder);
166 DocumentSearchCriteria builtCriteria = applyCriteriaCustomizations(documentType, criteriaBuilder.build());
167 builtCriteria = applyCriteriaDefaults(builtCriteria);
168 boolean criteriaModified = !criteria.equals(builtCriteria);
169 List<RemotableAttributeField> searchFields = determineSearchFields(documentType);
170 DocumentSearchResults.Builder searchResults = docSearchDao.findDocuments(docSearchGenerator, builtCriteria, criteriaModified, searchFields);
171 if (documentType != null) {
172 DocumentSearchResultValues resultValues = getDocumentSearchCustomizationMediator().customizeResults(documentType, builtCriteria, searchResults.build());
173 if (resultValues != null && CollectionUtils.isNotEmpty(resultValues.getResultValues())) {
174 Map<String, DocumentSearchResultValue> resultValueMap = new HashMap<String, DocumentSearchResultValue>();
175 for (DocumentSearchResultValue resultValue : resultValues.getResultValues()) {
176 resultValueMap.put(resultValue.getDocumentId(), resultValue);
177 }
178 for (DocumentSearchResult.Builder result : searchResults.getSearchResults()) {
179 DocumentSearchResultValue value = resultValueMap.get(result.getDocument().getDocumentId());
180 if (value != null) {
181 applyResultCustomization(result, value);
182 }
183 }
184 }
185 }
186
187 if (StringUtils.isNotBlank(principalId) && !searchResults.getSearchResults().isEmpty()) {
188 DocumentSearchResults builtResults = searchResults.build();
189 Set<String> authorizedDocumentIds = KEWServiceLocator.getDocumentSecurityService().documentSearchResultAuthorized(
190 principalId, builtResults, new SecuritySession(principalId));
191 if (CollectionUtils.isNotEmpty(authorizedDocumentIds)) {
192 int numFiltered = 0;
193 List<DocumentSearchResult.Builder> finalResults = new ArrayList<DocumentSearchResult.Builder>();
194 for (DocumentSearchResult.Builder result : searchResults.getSearchResults()) {
195 if (authorizedDocumentIds.contains(result.getDocument().getDocumentId())) {
196 finalResults.add(result);
197 } else {
198 numFiltered++;
199 }
200 }
201 searchResults.setSearchResults(finalResults);
202 searchResults.setNumberOfSecurityFilteredResults(numFiltered);
203 } else {
204 searchResults.setNumberOfSecurityFilteredResults(searchResults.getSearchResults().size());
205 searchResults.setSearchResults(Collections.<DocumentSearchResult.Builder>emptyList());
206 }
207 }
208 saveSearch(principalId, builtCriteria);
209 return searchResults.build();
210 }
211
212 protected void applyResultCustomization(DocumentSearchResult.Builder result, DocumentSearchResultValue value) {
213 Map<String, List<DocumentAttribute.AbstractBuilder<?>>> customizedAttributeMap =
214 new LinkedHashMap<String, List<DocumentAttribute.AbstractBuilder<?>>>();
215 for (DocumentAttribute customizedAttribute : value.getDocumentAttributes()) {
216 List<DocumentAttribute.AbstractBuilder<?>> attributesForName = customizedAttributeMap.get(value.getDocumentId());
217 if (attributesForName == null) {
218 attributesForName = new ArrayList<DocumentAttribute.AbstractBuilder<?>>();
219 customizedAttributeMap.put(value.getDocumentId(), attributesForName);
220 }
221 attributesForName.add(DocumentAttributeFactory.loadContractIntoBuilder(customizedAttribute));
222 }
223
224 Set<String> documentAttributeNamesCustomized = new HashSet<String>();
225 List<DocumentAttribute.AbstractBuilder<?>> newDocumentAttributes = new ArrayList<DocumentAttribute.AbstractBuilder<?>>();
226 for (DocumentAttribute.AbstractBuilder<?> documentAttribute : result.getDocumentAttributes()) {
227 String name = documentAttribute.getName();
228 if (!documentAttributeNamesCustomized.contains(name) && customizedAttributeMap.containsKey(name)) {
229 documentAttributeNamesCustomized.add(name);
230 newDocumentAttributes.addAll(customizedAttributeMap.get(name));
231 }
232 }
233 }
234
235
236
237
238
239
240
241 protected DocumentSearchCriteria applyCriteriaCustomizations(DocumentType documentType, DocumentSearchCriteria criteria) {
242 if (documentType == null) {
243 return criteria;
244 }
245 DocumentSearchCriteria customizedCriteria = getDocumentSearchCustomizationMediator().customizeCriteria(documentType, criteria);
246 if (customizedCriteria != null) {
247 return customizedCriteria;
248 }
249 return criteria;
250 }
251
252 protected DocumentSearchCriteria applyCriteriaDefaults(DocumentSearchCriteria criteria) {
253 DocumentSearchCriteria.Builder comparisonCriteria = createEmptyComparisonCriteria(criteria);
254 boolean isCriteriaEmpty = criteria.equals(comparisonCriteria.build());
255 boolean isTitleOnly = false;
256 if (!isCriteriaEmpty) {
257 comparisonCriteria.setTitle(criteria.getTitle());
258 isTitleOnly = criteria.equals(comparisonCriteria.build());
259 }
260
261 if (isCriteriaEmpty || isTitleOnly) {
262 DocumentSearchCriteria.Builder criteriaBuilder = DocumentSearchCriteria.Builder.create(criteria);
263 Integer defaultCreateDateDaysAgoValue = null;
264 if (isCriteriaEmpty) {
265
266 defaultCreateDateDaysAgoValue = KewApiConstants.DOCUMENT_SEARCH_NO_CRITERIA_CREATE_DATE_DAYS_AGO;
267 } else if (isTitleOnly) {
268
269
270 defaultCreateDateDaysAgoValue = KewApiConstants.DOCUMENT_SEARCH_DOC_TITLE_CREATE_DATE_DAYS_AGO;
271 }
272 if (defaultCreateDateDaysAgoValue != null) {
273
274 MutableDateTime mutableDateTime = new MutableDateTime();
275 mutableDateTime.addDays(defaultCreateDateDaysAgoValue.intValue());
276 criteriaBuilder.setDateCreatedFrom(mutableDateTime.toDateTime());
277 }
278 criteria = criteriaBuilder.build();
279 }
280 return criteria;
281 }
282
283 protected DocumentSearchCriteria.Builder createEmptyComparisonCriteria(DocumentSearchCriteria criteria) {
284 DocumentSearchCriteria.Builder builder = DocumentSearchCriteria.Builder.create();
285
286 builder.setSaveName(criteria.getSaveName());
287 builder.setStartAtIndex(criteria.getStartAtIndex());
288 builder.setMaxResults(criteria.getMaxResults());
289 builder.setIsAdvancedSearch(criteria.getIsAdvancedSearch());
290 return builder;
291 }
292
293 protected List<RemotableAttributeField> determineSearchFields(DocumentType documentType) {
294 List<RemotableAttributeField> searchFields = new ArrayList<RemotableAttributeField>();
295 if (documentType != null) {
296 DocumentSearchCriteriaConfiguration searchConfiguration =
297 getDocumentSearchCustomizationMediator().getDocumentSearchCriteriaConfiguration(documentType);
298 if (searchConfiguration != null) {
299 List<AttributeFields> attributeFields = searchConfiguration.getSearchAttributeFields();
300 if (attributeFields != null) {
301 for (AttributeFields fields : attributeFields) {
302 searchFields.addAll(fields.getRemotableAttributeFields());
303 }
304 }
305 }
306 }
307 return searchFields;
308 }
309
310 public DocumentSearchGenerator getStandardDocumentSearchGenerator() {
311 String searchGeneratorClass = ConfigContext.getCurrentContextConfig().getProperty(KewApiConstants.STANDARD_DOC_SEARCH_GENERATOR_CLASS_CONFIG_PARM);
312 if (searchGeneratorClass == null){
313 return new DocumentSearchGeneratorImpl();
314 }
315 return (DocumentSearchGenerator)GlobalResourceLoader.getObject(new ObjectDefinition(searchGeneratorClass));
316 }
317
318 @Override
319 public void validateDocumentSearchCriteria(DocumentSearchGenerator docSearchGenerator, DocumentSearchCriteria.Builder criteria) {
320 List<WorkflowServiceError> errors = this.validateWorkflowDocumentSearchCriteria(criteria);
321 List<RemotableAttributeError> searchAttributeErrors = docSearchGenerator.validateSearchableAttributes(criteria);
322 if (!CollectionUtils.isEmpty(searchAttributeErrors)) {
323
324 for (RemotableAttributeError searchAttributeError : searchAttributeErrors) {
325 for (String errorMessage : searchAttributeError.getErrors()) {
326 WorkflowServiceError error = new WorkflowServiceErrorImpl(errorMessage, "error.custom", errorMessage);
327 errors.add(error);
328 }
329 }
330 }
331 if (!errors.isEmpty() || !GlobalVariables.getMessageMap().hasNoErrors()) {
332 throw new WorkflowServiceErrorException("Document Search Validation Errors", errors);
333 }
334 }
335
336 protected List<WorkflowServiceError> validateWorkflowDocumentSearchCriteria(DocumentSearchCriteria.Builder criteria) {
337 List<WorkflowServiceError> errors = new ArrayList<WorkflowServiceError>();
338
339
340
341 criteria.setApproverPrincipalName(trimCriteriaValue(criteria.getApproverPrincipalName()));
342 criteria.setViewerPrincipalName(trimCriteriaValue(criteria.getViewerPrincipalName()));
343 criteria.setInitiatorPrincipalName(trimCriteriaValue(criteria.getInitiatorPrincipalName()));
344 validateGroupCriteria(criteria, errors);
345 criteria.setDocumentId(criteria.getDocumentId());
346
347
348 boolean compareDatePairs = true;
349 if (criteria.getDateCreatedFrom() == null) {
350 compareDatePairs = false;
351 }
352 else {
353 if (!validateDate("dateCreatedFrom", criteria.getDateCreatedFrom().toString(), "dateCreatedFrom")) {
354 compareDatePairs = false;
355 } else {
356 criteria.setDateCreatedFrom(criteria.getDateCreatedFrom());
357 }
358 }
359 if (criteria.getDateCreatedTo() == null) {
360 compareDatePairs = false;
361 }
362 else {
363 if (!validateDate("dateCreatedTo", criteria.getDateCreatedTo().toString(), "dateCreatedTo")) {
364 compareDatePairs = false;
365 } else {
366 criteria.setDateCreatedTo(criteria.getDateCreatedTo());
367 }
368 }
369 if (compareDatePairs) {
370 if (!checkDateRanges(new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateCreatedFrom().toDate()), new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateCreatedTo().toDate()))) {
371 errors.add(new WorkflowServiceErrorImpl("The Date Created From (Date Created) must not have a \"From\" date that occurs after the \"To\" date.", "docsearch.DocumentSearchService.dateCreatedRange"));
372 }
373 }
374
375 compareDatePairs = true;
376 if (criteria.getDateApprovedFrom() == null) {
377 compareDatePairs = false;
378 }
379 else {
380 if (!validateDate("dateApprovedFrom", criteria.getDateApprovedFrom().toString(), "dateApprovedFrom")) {
381 compareDatePairs = false;
382 } else {
383 criteria.setDateApprovedFrom(criteria.getDateApprovedFrom());
384 }
385 }
386 if (criteria.getDateApprovedTo() == null) {
387 compareDatePairs = false;
388 }
389 else {
390 if (!validateDate("dateApprovedTo", criteria.getDateApprovedTo().toString(), "dateApprovedTo")) {
391 compareDatePairs = false;
392 } else {
393 criteria.setDateApprovedTo(criteria.getDateApprovedTo());
394 }
395 }
396 if (compareDatePairs) {
397 if (!checkDateRanges(new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateApprovedFrom().toDate()), new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateApprovedTo().toDate()))) {
398 errors.add(new WorkflowServiceErrorImpl("The Date Approved From (Date Approved) must not have a \"From\" date that occurs after the \"To\" date.", "docsearch.DocumentSearchService.dateApprovedRange"));
399 }
400 }
401
402 compareDatePairs = true;
403 if (criteria.getDateFinalizedFrom() == null) {
404 compareDatePairs = false;
405 }
406 else {
407 if (!validateDate("dateFinalizedFrom", criteria.getDateFinalizedFrom().toString(), "dateFinalizedFrom")) {
408 compareDatePairs = false;
409 } else {
410 criteria.setDateFinalizedFrom(criteria.getDateFinalizedFrom());
411 }
412 }
413 if (criteria.getDateFinalizedTo() == null) {
414 compareDatePairs = false;
415 }
416 else {
417 if (!validateDate("dateFinalizedTo", criteria.getDateFinalizedTo().toString(), "dateFinalizedTo")) {
418 compareDatePairs = false;
419 } else {
420 criteria.setDateFinalizedTo(criteria.getDateFinalizedTo());
421 }
422 }
423 if (compareDatePairs) {
424 if (!checkDateRanges(new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateFinalizedFrom().toDate()), new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateFinalizedTo().toDate()))) {
425 errors.add(new WorkflowServiceErrorImpl("The Date Finalized From (Date Finalized) must not have a \"From\" date that occurs after the \"To\" date.", "docsearch.DocumentSearchService.dateFinalizedRange"));
426 }
427 }
428
429 compareDatePairs = true;
430 if (criteria.getDateLastModifiedFrom() == null) {
431 compareDatePairs = false;
432 }
433 else {
434 if (!validateDate("dateLastModifiedFrom", criteria.getDateLastModifiedFrom().toString(), "dateLastModifiedFrom")) {
435 compareDatePairs = false;
436 } else {
437 criteria.setDateLastModifiedFrom(criteria.getDateLastModifiedFrom());
438 }
439 }
440 if (criteria.getDateLastModifiedTo() == null) {
441 compareDatePairs = false;
442 }
443 else {
444 if (!validateDate("dateLastModifiedTo", criteria.getDateLastModifiedTo().toString(), "dateLastModifiedTo")) {
445 compareDatePairs = false;
446 } else {
447 criteria.setDateLastModifiedTo(criteria.getDateLastModifiedTo());
448 }
449 }
450 if (compareDatePairs) {
451 if (!checkDateRanges(new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateLastModifiedFrom().toDate()), new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateLastModifiedTo().toDate()))) {
452 errors.add(new WorkflowServiceErrorImpl("The Date Last Modified From (Date Last Modified) must not have a \"From\" date that occurs after the \"To\" date.", "docsearch.DocumentSearchService.dateLastModifiedRange"));
453 }
454 }
455 return errors;
456 }
457
458 private boolean validateDate(String dateFieldName, String dateFieldValue, String dateFieldErrorKey) {
459
460 int oldErrorCount = GlobalVariables.getMessageMap().getErrorCount();
461 getDictionaryValidationService().validateAttributeFormat(DOC_SEARCH_CRITERIA_CLASS, dateFieldName, dateFieldValue, DATA_TYPE_DATE, dateFieldErrorKey);
462 return (GlobalVariables.getMessageMap().getErrorCount() <= oldErrorCount);
463 }
464
465 public static DictionaryValidationService getDictionaryValidationService() {
466 if (dictionaryValidationService == null) {
467 dictionaryValidationService = KNSServiceLocator.getDictionaryValidationService();
468 }
469 return dictionaryValidationService;
470 }
471
472 public static DataDictionaryService getDataDictionaryService() {
473 if (dataDictionaryService == null) {
474 dataDictionaryService = KNSServiceLocator.getDataDictionaryService();
475 }
476 return dataDictionaryService;
477 }
478
479 private boolean checkDateRanges(String fromDate, String toDate) {
480 return Utilities.checkDateRanges(fromDate, toDate);
481 }
482 private String trimCriteriaValue(String criteriaValue) {
483 if (StringUtils.isNotBlank(criteriaValue)) {
484 criteriaValue = criteriaValue.trim();
485 }
486 if (StringUtils.isBlank(criteriaValue)) {
487 return null;
488 }
489 return criteriaValue;
490 }
491
492 private void validateGroupCriteria(DocumentSearchCriteria.Builder criteria, List<WorkflowServiceError> errors) {
493 if (StringUtils.isNotBlank(criteria.getGroupViewerId())) {
494 Group group = KimApiServiceLocator.getGroupService().getGroup(criteria.getGroupViewerId());
495 if (group == null) {
496 errors.add(new WorkflowServiceErrorImpl("Workgroup Viewer Name is not a workgroup", "docsearch.DocumentSearchService.workgroup.viewer"));
497 }
498 } else {
499 criteria.setGroupViewerId(null);
500 }
501 }
502
503 @Override
504 public List<KeyValue> getNamedSearches(String principalId) {
505 List<UserOptions> namedSearches = userOptionsService.findByUserQualified(principalId, NAMED_SEARCH_ORDER_BASE + "%");
506 List<KeyValue> sortedNamedSearches = new ArrayList<KeyValue>(0);
507 if (!namedSearches.isEmpty()) {
508 Collections.sort(namedSearches);
509 for (UserOptions namedSearch : namedSearches) {
510 KeyValue keyValue = new ConcreteKeyValue(namedSearch.getOptionId(), namedSearch.getOptionId().substring(NAMED_SEARCH_ORDER_BASE.length(), namedSearch.getOptionId().length()));
511 sortedNamedSearches.add(keyValue);
512 }
513 }
514 return sortedNamedSearches;
515 }
516
517 @Override
518 public List<KeyValue> getMostRecentSearches(String principalId) {
519 UserOptions order = userOptionsService.findByOptionId(LAST_SEARCH_ORDER_OPTION, principalId);
520 List<KeyValue> sortedMostRecentSearches = new ArrayList<KeyValue>();
521 if (order != null && order.getOptionVal() != null && !"".equals(order.getOptionVal())) {
522 List<UserOptions> mostRecentSearches = userOptionsService.findByUserQualified(principalId, LAST_SEARCH_BASE_NAME + "%");
523 String[] ordered = order.getOptionVal().split(",");
524 for (String anOrdered : ordered) {
525 UserOptions matchingOption = null;
526 for (UserOptions option : mostRecentSearches) {
527 if (anOrdered.equals(option.getOptionId())) {
528 matchingOption = option;
529 break;
530 }
531 }
532 if (matchingOption != null) {
533 DocumentSearchCriteria matchingCriteria = getCriteriaFromSavedSearch(matchingOption);
534 sortedMostRecentSearches.add(new ConcreteKeyValue(anOrdered, getSavedSearchAbbreviatedString(matchingCriteria)));
535 }
536 }
537 }
538 return sortedMostRecentSearches;
539 }
540
541 public DocumentSearchCriteria clearCriteria(DocumentType documentType, DocumentSearchCriteria criteria) {
542 DocumentSearchCriteria clearedCriteria = getDocumentSearchCustomizationMediator().customizeClearCriteria(
543 documentType, criteria);
544 if (clearedCriteria == null) {
545 clearedCriteria = getStandardDocumentSearchGenerator().clearSearch(criteria);
546 }
547 return clearedCriteria;
548 }
549
550 protected String getSavedSearchAbbreviatedString(DocumentSearchCriteria criteria) {
551 Map<String, String> abbreviatedStringMap = new LinkedHashMap<String, String>();
552 addAbbreviatedString(abbreviatedStringMap, "Doc Type", criteria.getDocumentTypeName());
553 addAbbreviatedString(abbreviatedStringMap, "Initiator", criteria.getInitiatorPrincipalName());
554 addAbbreviatedString(abbreviatedStringMap, "Doc Id", criteria.getDocumentId());
555 addAbbreviatedRangeString(abbreviatedStringMap, "Created", criteria.getDateCreatedFrom(),
556 criteria.getDateCreatedTo());
557 addAbbreviatedString(abbreviatedStringMap, "Title", criteria.getTitle());
558 addAbbreviatedString(abbreviatedStringMap, "App Doc Id", criteria.getApplicationDocumentId());
559 addAbbreviatedRangeString(abbreviatedStringMap, "Approved", criteria.getDateApprovedFrom(),
560 criteria.getDateApprovedTo());
561 addAbbreviatedRangeString(abbreviatedStringMap, "Modified", criteria.getDateLastModifiedFrom(), criteria.getDateLastModifiedTo());
562 addAbbreviatedRangeString(abbreviatedStringMap, "Finalized", criteria.getDateFinalizedFrom(), criteria.getDateFinalizedTo());
563 addAbbreviatedRangeString(abbreviatedStringMap, "App Doc Status Changed", criteria.getDateApplicationDocumentStatusChangedFrom(), criteria.getDateApplicationDocumentStatusChangedTo());
564 addAbbreviatedString(abbreviatedStringMap, "Approver", criteria.getApproverPrincipalName());
565 addAbbreviatedString(abbreviatedStringMap, "Viewer", criteria.getViewerPrincipalName());
566 addAbbreviatedString(abbreviatedStringMap, "Group Viewer", criteria.getGroupViewerId());
567 addAbbreviatedString(abbreviatedStringMap, "Node", criteria.getRouteNodeName());
568 addAbbreviatedMultiValuedString(abbreviatedStringMap, "Status", criteria.getDocumentStatuses());
569 addAbbreviatedMultiValuedString(abbreviatedStringMap, "Category", criteria.getDocumentStatusCategories());
570 for (String documentAttributeName : criteria.getDocumentAttributeValues().keySet()) {
571 addAbbreviatedMultiValuedString(abbreviatedStringMap, documentAttributeName, criteria.getDocumentAttributeValues().get(documentAttributeName));
572 }
573 StringBuilder stringBuilder = new StringBuilder();
574 int iteration = 0;
575 for (String label : abbreviatedStringMap.keySet()) {
576 stringBuilder.append(label).append("=").append(abbreviatedStringMap.get(label));
577 if (iteration < abbreviatedStringMap.keySet().size()) {
578 stringBuilder.append("; ");
579 }
580 }
581 return stringBuilder.toString();
582 }
583
584 protected void addAbbreviatedString(Map<String, String> abbreviatedStringMap, String label, String value) {
585 if (StringUtils.isNotBlank(value)) {
586 abbreviatedStringMap.put(label, value);
587 }
588 }
589
590 protected void addAbbreviatedMultiValuedString(Map<String, String> abbreviatedStringMap, String label, Collection<? extends Object> values) {
591 if (CollectionUtils.isNotEmpty(values)) {
592 List<String> stringValues = new ArrayList<String>();
593 for (Object value : values) {
594 stringValues.add(value.toString());
595 }
596 abbreviatedStringMap.put(label, StringUtils.join(stringValues, ","));
597 }
598 }
599
600 protected void addAbbreviatedRangeString(Map<String, String> abbreviatedStringMap, String label, DateTime dateFrom, DateTime dateTo) {
601 if (dateFrom != null || dateTo != null) {
602 StringBuilder abbreviatedString = new StringBuilder();
603 if (dateFrom != null) {
604 abbreviatedString.append(CoreApiServiceLocator.getDateTimeService().toDateString(dateFrom.toDate()));
605 }
606 abbreviatedString.append("..");
607 if (dateTo != null) {
608 abbreviatedString.append(CoreApiServiceLocator.getDateTimeService().toDateString(dateTo.toDate()));
609 }
610 abbreviatedStringMap.put(label, abbreviatedString.toString());
611 }
612 }
613
614
615
616
617
618
619
620
621
622 private void saveSearch(String principalId, DocumentSearchCriteria criteria) {
623 if (StringUtils.isBlank(principalId)) {
624 return;
625 }
626
627 try {
628 String savedSearchString = DocumentSearchInternalUtils.marshalDocumentSearchCriteria(criteria);
629
630 if (StringUtils.isNotBlank(criteria.getSaveName())) {
631 userOptionsService.save(principalId, NAMED_SEARCH_ORDER_BASE + criteria.getSaveName(), savedSearchString);
632 } else {
633
634 UserOptions searchOrder = userOptionsService.findByOptionId(LAST_SEARCH_ORDER_OPTION, principalId);
635
636 if (searchOrder == null) {
637 userOptionsService.save(principalId, LAST_SEARCH_BASE_NAME + "0", savedSearchString);
638 userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, LAST_SEARCH_BASE_NAME + "0");
639 } else {
640 String[] currentOrder = searchOrder.getOptionVal().split(",");
641
642 if (currentOrder.length == MAX_SEARCH_ITEMS) {
643
644
645
646 String searchName = currentOrder[currentOrder.length - 1];
647 String[] newOrder = new String[MAX_SEARCH_ITEMS];
648 newOrder[0] = searchName;
649 for (int i = 0; i < currentOrder.length - 1; i++) {
650 newOrder[i + 1] = currentOrder[i];
651 }
652
653 String newSearchOrder = "";
654 for (String aNewOrder : newOrder) {
655 if (!"".equals(newSearchOrder)) {
656 newSearchOrder += ",";
657 }
658 newSearchOrder += aNewOrder;
659 }
660
661 userOptionsService.save(principalId, searchName, savedSearchString);
662 userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, newSearchOrder);
663 } else {
664
665
666
667
668 int absMax = 0;
669 for (String aCurrentOrder : currentOrder) {
670 int current = new Integer(aCurrentOrder.substring(LAST_SEARCH_BASE_NAME.length(),
671 aCurrentOrder.length()));
672 if (current > absMax) {
673 absMax = current;
674 }
675 }
676 String searchName = LAST_SEARCH_BASE_NAME + ++absMax;
677 String[] newOrder = new String[currentOrder.length + 1];
678 newOrder[0] = searchName;
679 for (int i = 0; i < currentOrder.length; i++) {
680 newOrder[i + 1] = currentOrder[i];
681 }
682 String newSearchOrder = "";
683 for (String aNewOrder : newOrder) {
684 if (!"".equals(newSearchOrder)) {
685 newSearchOrder += ",";
686 }
687 newSearchOrder += aNewOrder;
688 }
689 userOptionsService.save(principalId, searchName, savedSearchString);
690 userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, newSearchOrder);
691 }
692 }
693 }
694 } catch (Exception e) {
695
696
697 LOG.error("Unable to save search due to exception", e);
698 }
699 }
700
701 public ConfigurationService getKualiConfigurationService() {
702 if (kualiConfigurationService == null) {
703 kualiConfigurationService = KRADServiceLocator.getKualiConfigurationService();
704 }
705 return kualiConfigurationService;
706 }
707
708 }