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 @Override
114 public void clearNamedSearches(String principalId) {
115 String[] clearListNames = { NAMED_SEARCH_ORDER_BASE + "%", LAST_SEARCH_BASE_NAME + "%", LAST_SEARCH_ORDER_OPTION + "%" };
116 for (String clearListName : clearListNames)
117 {
118 List<UserOptions> records = userOptionsService.findByUserQualified(principalId, clearListName);
119 for (UserOptions userOptions : records) {
120 userOptionsService.deleteUserOptions(userOptions);
121 }
122 }
123 }
124
125 @Override
126 public DocumentSearchCriteria getNamedSearchCriteria(String principalId, String searchName) {
127
128 searchName = searchName.startsWith(NAMED_SEARCH_ORDER_BASE) ? searchName : (NAMED_SEARCH_ORDER_BASE + searchName);
129 return getSavedSearchCriteria(principalId, searchName);
130 }
131
132 @Override
133 public DocumentSearchCriteria getSavedSearchCriteria(String principalId, String searchName) {
134 UserOptions savedSearch = userOptionsService.findByOptionId(searchName, principalId);
135 if (savedSearch == null) {
136 return null;
137 }
138 return getCriteriaFromSavedSearch(savedSearch);
139 }
140
141 protected DocumentSearchCriteria getCriteriaFromSavedSearch(UserOptions savedSearch) {
142 String optionValue = savedSearch.getOptionVal();
143 try {
144 return DocumentSearchInternalUtils.unmarshalDocumentSearchCriteria(optionValue);
145 } catch (IOException e) {
146
147 LOG.warn("Failed to load saved search for name '" + savedSearch.getOptionId() + "' removing saved search from database.");
148 userOptionsService.deleteUserOptions(savedSearch);
149 return DocumentSearchCriteria.Builder.create().build();
150
151 }
152 }
153
154 private String getOptionCriteriaField(UserOptions userOption, String fieldName) {
155 String value = userOption.getOptionVal();
156 if (value != null) {
157 String[] fields = value.split(",,");
158 for (String field : fields)
159 {
160 if (field.startsWith(fieldName + "="))
161 {
162 return field.substring(field.indexOf(fieldName) + fieldName.length() + 1, field.length());
163 }
164 }
165 }
166 return null;
167 }
168
169 @Override
170 public DocumentSearchResults lookupDocuments(String principalId, DocumentSearchCriteria criteria) {
171 DocumentSearchGenerator docSearchGenerator = getStandardDocumentSearchGenerator();
172 DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByNameCaseInsensitive(criteria.getDocumentTypeName());
173 DocumentSearchCriteria.Builder criteriaBuilder = DocumentSearchCriteria.Builder.create(criteria);
174 validateDocumentSearchCriteria(docSearchGenerator, criteriaBuilder);
175 DocumentSearchCriteria builtCriteria = applyCriteriaCustomizations(documentType, criteriaBuilder.build());
176
177
178
179
180 if (!CollectionUtils.isEmpty(criteria.getApplicationDocumentStatuses())
181 && CollectionUtils.isEmpty(builtCriteria.getApplicationDocumentStatuses())) {
182 DocumentSearchCriteria.Builder patchedCriteria = DocumentSearchCriteria.Builder.create(builtCriteria);
183 patchedCriteria.setApplicationDocumentStatuses(criteriaBuilder.getApplicationDocumentStatuses());
184 builtCriteria = patchedCriteria.build();
185 }
186
187 builtCriteria = applyCriteriaDefaults(builtCriteria);
188 boolean criteriaModified = !criteria.equals(builtCriteria);
189 List<RemotableAttributeField> searchFields = determineSearchFields(documentType);
190 DocumentSearchResults.Builder searchResults = docSearchDao.findDocuments(docSearchGenerator, builtCriteria, criteriaModified, searchFields);
191 if (documentType != null) {
192
193
194
195 DocumentSearchCriteria.Builder docSearchUserIdCriteriaBuilder = DocumentSearchCriteria.Builder.create(builtCriteria);
196 docSearchUserIdCriteriaBuilder.setDocSearchUserId(principalId);
197 DocumentSearchCriteria docSearchUserIdCriteria = docSearchUserIdCriteriaBuilder.build();
198
199 DocumentSearchResultValues resultValues = getDocumentSearchCustomizationMediator().customizeResults(documentType, docSearchUserIdCriteria, searchResults.build());
200 if (resultValues != null && CollectionUtils.isNotEmpty(resultValues.getResultValues())) {
201 Map<String, DocumentSearchResultValue> resultValueMap = new HashMap<String, DocumentSearchResultValue>();
202 for (DocumentSearchResultValue resultValue : resultValues.getResultValues()) {
203 resultValueMap.put(resultValue.getDocumentId(), resultValue);
204 }
205 for (DocumentSearchResult.Builder result : searchResults.getSearchResults()) {
206 DocumentSearchResultValue value = resultValueMap.get(result.getDocument().getDocumentId());
207 if (value != null) {
208 applyResultCustomization(result, value);
209 }
210 }
211 }
212 }
213
214 if (StringUtils.isNotBlank(principalId) && !searchResults.getSearchResults().isEmpty()) {
215 DocumentSearchResults builtResults = searchResults.build();
216 Set<String> authorizedDocumentIds = KEWServiceLocator.getDocumentSecurityService().documentSearchResultAuthorized(
217 principalId, builtResults, new SecuritySession(principalId));
218 if (CollectionUtils.isNotEmpty(authorizedDocumentIds)) {
219 int numFiltered = 0;
220 List<DocumentSearchResult.Builder> finalResults = new ArrayList<DocumentSearchResult.Builder>();
221 for (DocumentSearchResult.Builder result : searchResults.getSearchResults()) {
222 if (authorizedDocumentIds.contains(result.getDocument().getDocumentId())) {
223 finalResults.add(result);
224 } else {
225 numFiltered++;
226 }
227 }
228 searchResults.setSearchResults(finalResults);
229 searchResults.setNumberOfSecurityFilteredResults(numFiltered);
230 } else {
231 searchResults.setNumberOfSecurityFilteredResults(searchResults.getSearchResults().size());
232 searchResults.setSearchResults(Collections.<DocumentSearchResult.Builder>emptyList());
233 }
234 }
235 saveSearch(principalId, builtCriteria);
236 return searchResults.build();
237 }
238
239 protected void applyResultCustomization(DocumentSearchResult.Builder result, DocumentSearchResultValue value) {
240 Map<String, List<DocumentAttribute.AbstractBuilder<?>>> customizedAttributeMap =
241 new LinkedHashMap<String, List<DocumentAttribute.AbstractBuilder<?>>>();
242 for (DocumentAttribute customizedAttribute : value.getDocumentAttributes()) {
243 List<DocumentAttribute.AbstractBuilder<?>> attributesForName = customizedAttributeMap.get(customizedAttribute.getName());
244 if (attributesForName == null) {
245 attributesForName = new ArrayList<DocumentAttribute.AbstractBuilder<?>>();
246 customizedAttributeMap.put(customizedAttribute.getName(), attributesForName);
247 }
248 attributesForName.add(DocumentAttributeFactory.loadContractIntoBuilder(customizedAttribute));
249 }
250
251 Set<String> documentAttributeNamesCustomized = new HashSet<String>();
252 List<DocumentAttribute.AbstractBuilder<?>> newDocumentAttributes = new ArrayList<DocumentAttribute.AbstractBuilder<?>>();
253 for (DocumentAttribute.AbstractBuilder<?> documentAttribute : result.getDocumentAttributes()) {
254 String name = documentAttribute.getName();
255 if (customizedAttributeMap.containsKey(name)) {
256 if (!documentAttributeNamesCustomized.contains(name)) {
257 documentAttributeNamesCustomized.add(name);
258 newDocumentAttributes.addAll(customizedAttributeMap.get(name));
259 }
260 } else {
261 newDocumentAttributes.add(documentAttribute);
262 }
263 }
264 result.setDocumentAttributes(newDocumentAttributes);
265 }
266
267
268
269
270
271
272
273 protected DocumentSearchCriteria applyCriteriaCustomizations(DocumentType documentType, DocumentSearchCriteria criteria) {
274 if (documentType == null) {
275 return criteria;
276 }
277 DocumentSearchCriteria customizedCriteria = getDocumentSearchCustomizationMediator().customizeCriteria(documentType, criteria);
278 if (customizedCriteria != null) {
279 return customizedCriteria;
280 }
281 return criteria;
282 }
283
284 protected DocumentSearchCriteria applyCriteriaDefaults(DocumentSearchCriteria criteria) {
285 DocumentSearchCriteria.Builder comparisonCriteria = createEmptyComparisonCriteria(criteria);
286 boolean isCriteriaEmpty = criteria.equals(comparisonCriteria.build());
287 boolean isTitleOnly = false;
288 boolean isDocTypeOnly = false;
289 if (!isCriteriaEmpty) {
290 comparisonCriteria.setTitle(criteria.getTitle());
291 isTitleOnly = criteria.equals(comparisonCriteria.build());
292 }
293
294 if (!isCriteriaEmpty && !isTitleOnly) {
295 comparisonCriteria = createEmptyComparisonCriteria(criteria);
296 comparisonCriteria.setDocumentTypeName(criteria.getDocumentTypeName());
297 isDocTypeOnly = criteria.equals(comparisonCriteria.build());
298 }
299
300 if (isCriteriaEmpty || isTitleOnly || isDocTypeOnly) {
301 DocumentSearchCriteria.Builder criteriaBuilder = DocumentSearchCriteria.Builder.create(criteria);
302 Integer defaultCreateDateDaysAgoValue = null;
303 if (isCriteriaEmpty || isDocTypeOnly) {
304
305 defaultCreateDateDaysAgoValue = KewApiConstants.DOCUMENT_SEARCH_NO_CRITERIA_CREATE_DATE_DAYS_AGO;
306 } else if (isTitleOnly) {
307
308
309 defaultCreateDateDaysAgoValue = KewApiConstants.DOCUMENT_SEARCH_DOC_TITLE_CREATE_DATE_DAYS_AGO;
310 }
311
312 if (defaultCreateDateDaysAgoValue != null) {
313
314 MutableDateTime mutableDateTime = new MutableDateTime();
315 mutableDateTime.addDays(defaultCreateDateDaysAgoValue.intValue());
316 criteriaBuilder.setDateCreatedFrom(mutableDateTime.toDateTime());
317 }
318 criteria = criteriaBuilder.build();
319 }
320 return criteria;
321 }
322
323 protected DocumentSearchCriteria.Builder createEmptyComparisonCriteria(DocumentSearchCriteria criteria) {
324 DocumentSearchCriteria.Builder builder = DocumentSearchCriteria.Builder.create();
325
326 builder.setSaveName(criteria.getSaveName());
327 builder.setStartAtIndex(criteria.getStartAtIndex());
328 builder.setMaxResults(criteria.getMaxResults());
329 builder.setIsAdvancedSearch(criteria.getIsAdvancedSearch());
330 builder.setSearchOptions(criteria.getSearchOptions());
331 return builder;
332 }
333
334 protected List<RemotableAttributeField> determineSearchFields(DocumentType documentType) {
335 List<RemotableAttributeField> searchFields = new ArrayList<RemotableAttributeField>();
336 if (documentType != null) {
337 DocumentSearchCriteriaConfiguration searchConfiguration =
338 getDocumentSearchCustomizationMediator().getDocumentSearchCriteriaConfiguration(documentType);
339 if (searchConfiguration != null) {
340 List<AttributeFields> attributeFields = searchConfiguration.getSearchAttributeFields();
341 if (attributeFields != null) {
342 for (AttributeFields fields : attributeFields) {
343 searchFields.addAll(fields.getRemotableAttributeFields());
344 }
345 }
346 }
347 }
348 return searchFields;
349 }
350
351 public DocumentSearchGenerator getStandardDocumentSearchGenerator() {
352 String searchGeneratorClass = ConfigContext.getCurrentContextConfig().getProperty(KewApiConstants.STANDARD_DOC_SEARCH_GENERATOR_CLASS_CONFIG_PARM);
353 if (searchGeneratorClass == null){
354 return new DocumentSearchGeneratorImpl();
355 }
356 return (DocumentSearchGenerator)GlobalResourceLoader.getObject(new ObjectDefinition(searchGeneratorClass));
357 }
358
359 @Override
360 public void validateDocumentSearchCriteria(DocumentSearchGenerator docSearchGenerator, DocumentSearchCriteria.Builder criteria) {
361 List<WorkflowServiceError> errors = this.validateWorkflowDocumentSearchCriteria(criteria);
362 List<RemotableAttributeError> searchAttributeErrors = docSearchGenerator.validateSearchableAttributes(criteria);
363 if (!CollectionUtils.isEmpty(searchAttributeErrors)) {
364
365 for (RemotableAttributeError searchAttributeError : searchAttributeErrors) {
366 for (String errorMessage : searchAttributeError.getErrors()) {
367 WorkflowServiceError error = new WorkflowServiceErrorImpl(errorMessage, "error.custom", errorMessage);
368 errors.add(error);
369 }
370 }
371 }
372 if (!errors.isEmpty() || !GlobalVariables.getMessageMap().hasNoErrors()) {
373 throw new WorkflowServiceErrorException("Document Search Validation Errors", errors);
374 }
375 }
376
377 protected List<WorkflowServiceError> validateWorkflowDocumentSearchCriteria(DocumentSearchCriteria.Builder criteria) {
378 List<WorkflowServiceError> errors = new ArrayList<WorkflowServiceError>();
379
380
381
382 criteria.setApproverPrincipalName(trimCriteriaValue(criteria.getApproverPrincipalName()));
383 criteria.setViewerPrincipalName(trimCriteriaValue(criteria.getViewerPrincipalName()));
384 criteria.setInitiatorPrincipalName(trimCriteriaValue(criteria.getInitiatorPrincipalName()));
385 validateGroupCriteria(criteria, errors);
386 criteria.setDocumentId(criteria.getDocumentId());
387
388
389 boolean compareDatePairs = true;
390 if (criteria.getDateCreatedFrom() == null) {
391 compareDatePairs = false;
392 }
393 else {
394 if (!validateDate("dateCreatedFrom", criteria.getDateCreatedFrom().toString(), "dateCreatedFrom")) {
395 compareDatePairs = false;
396 } else {
397 criteria.setDateCreatedFrom(criteria.getDateCreatedFrom());
398 }
399 }
400 if (criteria.getDateCreatedTo() == null) {
401 compareDatePairs = false;
402 }
403 else {
404 if (!validateDate("dateCreatedTo", criteria.getDateCreatedTo().toString(), "dateCreatedTo")) {
405 compareDatePairs = false;
406 } else {
407 criteria.setDateCreatedTo(criteria.getDateCreatedTo());
408 }
409 }
410 if (compareDatePairs) {
411 if (!checkDateRanges(new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateCreatedFrom().toDate()), new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateCreatedTo().toDate()))) {
412 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"));
413 }
414 }
415
416 compareDatePairs = true;
417 if (criteria.getDateApprovedFrom() == null) {
418 compareDatePairs = false;
419 }
420 else {
421 if (!validateDate("dateApprovedFrom", criteria.getDateApprovedFrom().toString(), "dateApprovedFrom")) {
422 compareDatePairs = false;
423 } else {
424 criteria.setDateApprovedFrom(criteria.getDateApprovedFrom());
425 }
426 }
427 if (criteria.getDateApprovedTo() == null) {
428 compareDatePairs = false;
429 }
430 else {
431 if (!validateDate("dateApprovedTo", criteria.getDateApprovedTo().toString(), "dateApprovedTo")) {
432 compareDatePairs = false;
433 } else {
434 criteria.setDateApprovedTo(criteria.getDateApprovedTo());
435 }
436 }
437 if (compareDatePairs) {
438 if (!checkDateRanges(new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateApprovedFrom().toDate()), new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateApprovedTo().toDate()))) {
439 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"));
440 }
441 }
442
443 compareDatePairs = true;
444 if (criteria.getDateFinalizedFrom() == null) {
445 compareDatePairs = false;
446 }
447 else {
448 if (!validateDate("dateFinalizedFrom", criteria.getDateFinalizedFrom().toString(), "dateFinalizedFrom")) {
449 compareDatePairs = false;
450 } else {
451 criteria.setDateFinalizedFrom(criteria.getDateFinalizedFrom());
452 }
453 }
454 if (criteria.getDateFinalizedTo() == null) {
455 compareDatePairs = false;
456 }
457 else {
458 if (!validateDate("dateFinalizedTo", criteria.getDateFinalizedTo().toString(), "dateFinalizedTo")) {
459 compareDatePairs = false;
460 } else {
461 criteria.setDateFinalizedTo(criteria.getDateFinalizedTo());
462 }
463 }
464 if (compareDatePairs) {
465 if (!checkDateRanges(new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateFinalizedFrom().toDate()), new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateFinalizedTo().toDate()))) {
466 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"));
467 }
468 }
469
470 compareDatePairs = true;
471 if (criteria.getDateLastModifiedFrom() == null) {
472 compareDatePairs = false;
473 }
474 else {
475 if (!validateDate("dateLastModifiedFrom", criteria.getDateLastModifiedFrom().toString(), "dateLastModifiedFrom")) {
476 compareDatePairs = false;
477 } else {
478 criteria.setDateLastModifiedFrom(criteria.getDateLastModifiedFrom());
479 }
480 }
481 if (criteria.getDateLastModifiedTo() == null) {
482 compareDatePairs = false;
483 }
484 else {
485 if (!validateDate("dateLastModifiedTo", criteria.getDateLastModifiedTo().toString(), "dateLastModifiedTo")) {
486 compareDatePairs = false;
487 } else {
488 criteria.setDateLastModifiedTo(criteria.getDateLastModifiedTo());
489 }
490 }
491 if (compareDatePairs) {
492 if (!checkDateRanges(new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateLastModifiedFrom().toDate()), new SimpleDateFormat("MM/dd/yyyy").format(criteria.getDateLastModifiedTo().toDate()))) {
493 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"));
494 }
495 }
496 return errors;
497 }
498
499 private boolean validateDate(String dateFieldName, String dateFieldValue, String dateFieldErrorKey) {
500
501 int oldErrorCount = GlobalVariables.getMessageMap().getErrorCount();
502 getDictionaryValidationService().validateAttributeFormat(DOC_SEARCH_CRITERIA_CLASS, dateFieldName, dateFieldValue, DATA_TYPE_DATE, dateFieldErrorKey);
503 return (GlobalVariables.getMessageMap().getErrorCount() <= oldErrorCount);
504 }
505
506 public static DictionaryValidationService getDictionaryValidationService() {
507 if (dictionaryValidationService == null) {
508 dictionaryValidationService = KNSServiceLocator.getKNSDictionaryValidationService();
509 }
510 return dictionaryValidationService;
511 }
512
513 public static DataDictionaryService getDataDictionaryService() {
514 if (dataDictionaryService == null) {
515 dataDictionaryService = KNSServiceLocator.getDataDictionaryService();
516 }
517 return dataDictionaryService;
518 }
519
520 private boolean checkDateRanges(String fromDate, String toDate) {
521 return Utilities.checkDateRanges(fromDate, toDate);
522 }
523 private String trimCriteriaValue(String criteriaValue) {
524 if (StringUtils.isNotBlank(criteriaValue)) {
525 criteriaValue = criteriaValue.trim();
526 }
527 if (StringUtils.isBlank(criteriaValue)) {
528 return null;
529 }
530 return criteriaValue;
531 }
532
533 private void validateGroupCriteria(DocumentSearchCriteria.Builder criteria, List<WorkflowServiceError> errors) {
534 if (StringUtils.isNotBlank(criteria.getGroupViewerId())) {
535 Group group = KimApiServiceLocator.getGroupService().getGroup(criteria.getGroupViewerId());
536 if (group == null) {
537 errors.add(new WorkflowServiceErrorImpl("Workgroup Viewer Name is not a workgroup", "docsearch.DocumentSearchService.workgroup.viewer"));
538 }
539 } else {
540 criteria.setGroupViewerId(null);
541 }
542 }
543
544 @Override
545 public List<KeyValue> getNamedSearches(String principalId) {
546 List<UserOptions> namedSearches = userOptionsService.findByUserQualified(principalId, NAMED_SEARCH_ORDER_BASE + "%");
547 List<KeyValue> sortedNamedSearches = new ArrayList<KeyValue>(0);
548 if (!namedSearches.isEmpty()) {
549 Collections.sort(namedSearches);
550 for (UserOptions namedSearch : namedSearches) {
551 KeyValue keyValue = new ConcreteKeyValue(namedSearch.getOptionId(), namedSearch.getOptionId().substring(NAMED_SEARCH_ORDER_BASE.length(), namedSearch.getOptionId().length()));
552 sortedNamedSearches.add(keyValue);
553 }
554 }
555 return sortedNamedSearches;
556 }
557
558 @Override
559 public List<KeyValue> getMostRecentSearches(String principalId) {
560 UserOptions order = userOptionsService.findByOptionId(LAST_SEARCH_ORDER_OPTION, principalId);
561 List<KeyValue> sortedMostRecentSearches = new ArrayList<KeyValue>();
562 if (order != null && order.getOptionVal() != null && !"".equals(order.getOptionVal())) {
563 List<UserOptions> mostRecentSearches = userOptionsService.findByUserQualified(principalId, LAST_SEARCH_BASE_NAME + "%");
564 String[] ordered = order.getOptionVal().split(",");
565 for (String anOrdered : ordered) {
566 UserOptions matchingOption = null;
567 for (UserOptions option : mostRecentSearches) {
568 if (anOrdered.equals(option.getOptionId())) {
569 matchingOption = option;
570 break;
571 }
572 }
573 if (matchingOption != null) {
574 DocumentSearchCriteria matchingCriteria = getCriteriaFromSavedSearch(matchingOption);
575 sortedMostRecentSearches.add(new ConcreteKeyValue(anOrdered, getSavedSearchAbbreviatedString(matchingCriteria)));
576 }
577 }
578 }
579 return sortedMostRecentSearches;
580 }
581
582 public DocumentSearchCriteria clearCriteria(DocumentType documentType, DocumentSearchCriteria criteria) {
583 DocumentSearchCriteria clearedCriteria = getDocumentSearchCustomizationMediator().customizeClearCriteria(
584 documentType, criteria);
585 if (clearedCriteria == null) {
586 clearedCriteria = getStandardDocumentSearchGenerator().clearSearch(criteria);
587 }
588 return clearedCriteria;
589 }
590
591 protected String getSavedSearchAbbreviatedString(DocumentSearchCriteria criteria) {
592 Map<String, String> abbreviatedStringMap = new LinkedHashMap<String, String>();
593 addAbbreviatedString(abbreviatedStringMap, "Doc Type", criteria.getDocumentTypeName());
594 addAbbreviatedString(abbreviatedStringMap, "Initiator", criteria.getInitiatorPrincipalName());
595 addAbbreviatedString(abbreviatedStringMap, "Doc Id", criteria.getDocumentId());
596 addAbbreviatedRangeString(abbreviatedStringMap, "Created", criteria.getDateCreatedFrom(),
597 criteria.getDateCreatedTo());
598 addAbbreviatedString(abbreviatedStringMap, "Title", criteria.getTitle());
599 addAbbreviatedString(abbreviatedStringMap, "App Doc Id", criteria.getApplicationDocumentId());
600 addAbbreviatedRangeString(abbreviatedStringMap, "Approved", criteria.getDateApprovedFrom(),
601 criteria.getDateApprovedTo());
602 addAbbreviatedRangeString(abbreviatedStringMap, "Modified", criteria.getDateLastModifiedFrom(), criteria.getDateLastModifiedTo());
603 addAbbreviatedRangeString(abbreviatedStringMap, "Finalized", criteria.getDateFinalizedFrom(), criteria.getDateFinalizedTo());
604 addAbbreviatedRangeString(abbreviatedStringMap, "App Doc Status Changed", criteria.getDateApplicationDocumentStatusChangedFrom(), criteria.getDateApplicationDocumentStatusChangedTo());
605 addAbbreviatedString(abbreviatedStringMap, "Approver", criteria.getApproverPrincipalName());
606 addAbbreviatedString(abbreviatedStringMap, "Viewer", criteria.getViewerPrincipalName());
607 addAbbreviatedString(abbreviatedStringMap, "Group Viewer", criteria.getGroupViewerId());
608 addAbbreviatedString(abbreviatedStringMap, "Node", criteria.getRouteNodeName());
609 addAbbreviatedMultiValuedString(abbreviatedStringMap, "Status", criteria.getDocumentStatuses());
610 addAbbreviatedMultiValuedString(abbreviatedStringMap, "Category", criteria.getDocumentStatusCategories());
611 for (String documentAttributeName : criteria.getDocumentAttributeValues().keySet()) {
612 addAbbreviatedMultiValuedString(abbreviatedStringMap, documentAttributeName, criteria.getDocumentAttributeValues().get(documentAttributeName));
613 }
614 StringBuilder stringBuilder = new StringBuilder();
615 int iteration = 0;
616 for (String label : abbreviatedStringMap.keySet()) {
617 stringBuilder.append(label).append("=").append(abbreviatedStringMap.get(label));
618 if (iteration < abbreviatedStringMap.keySet().size()) {
619 stringBuilder.append("; ");
620 }
621 }
622 return stringBuilder.toString();
623 }
624
625 protected void addAbbreviatedString(Map<String, String> abbreviatedStringMap, String label, String value) {
626 if (StringUtils.isNotBlank(value)) {
627 abbreviatedStringMap.put(label, value);
628 }
629 }
630
631 protected void addAbbreviatedMultiValuedString(Map<String, String> abbreviatedStringMap, String label, Collection<? extends Object> values) {
632 if (CollectionUtils.isNotEmpty(values)) {
633 List<String> stringValues = new ArrayList<String>();
634 for (Object value : values) {
635 stringValues.add(value.toString());
636 }
637 abbreviatedStringMap.put(label, StringUtils.join(stringValues, ","));
638 }
639 }
640
641 protected void addAbbreviatedRangeString(Map<String, String> abbreviatedStringMap, String label, DateTime dateFrom, DateTime dateTo) {
642 if (dateFrom != null || dateTo != null) {
643 StringBuilder abbreviatedString = new StringBuilder();
644 if (dateFrom != null) {
645 abbreviatedString.append(CoreApiServiceLocator.getDateTimeService().toDateString(dateFrom.toDate()));
646 }
647 abbreviatedString.append("..");
648 if (dateTo != null) {
649 abbreviatedString.append(CoreApiServiceLocator.getDateTimeService().toDateString(dateTo.toDate()));
650 }
651 abbreviatedStringMap.put(label, abbreviatedString.toString());
652 }
653 }
654
655
656
657
658
659
660
661
662
663 private void saveSearch(String principalId, DocumentSearchCriteria criteria) {
664 if (StringUtils.isBlank(principalId)) {
665 return;
666 }
667
668 try {
669 String savedSearchString = DocumentSearchInternalUtils.marshalDocumentSearchCriteria(criteria);
670
671 if (StringUtils.isNotBlank(criteria.getSaveName())) {
672 userOptionsService.save(principalId, NAMED_SEARCH_ORDER_BASE + criteria.getSaveName(), savedSearchString);
673 } else {
674
675 UserOptions searchOrder = userOptionsService.findByOptionId(LAST_SEARCH_ORDER_OPTION, principalId);
676
677 if (searchOrder == null) {
678 userOptionsService.save(principalId, LAST_SEARCH_BASE_NAME + "0", savedSearchString);
679 userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, LAST_SEARCH_BASE_NAME + "0");
680 } else {
681 String[] currentOrder = searchOrder.getOptionVal().split(",");
682
683 if (currentOrder.length == MAX_SEARCH_ITEMS) {
684
685
686
687 String searchName = currentOrder[currentOrder.length - 1];
688 String[] newOrder = new String[MAX_SEARCH_ITEMS];
689 newOrder[0] = searchName;
690 for (int i = 0; i < currentOrder.length - 1; i++) {
691 newOrder[i + 1] = currentOrder[i];
692 }
693
694 String newSearchOrder = rejoinWithCommas(newOrder);
695
696 userOptionsService.save(principalId, searchName, savedSearchString);
697 userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, newSearchOrder);
698 } else {
699
700
701
702
703 int absMax = 0;
704 for (String aCurrentOrder : currentOrder) {
705 int current = new Integer(aCurrentOrder.substring(LAST_SEARCH_BASE_NAME.length(),
706 aCurrentOrder.length()));
707 if (current > absMax) {
708 absMax = current;
709 }
710 }
711 String searchName = LAST_SEARCH_BASE_NAME + ++absMax;
712 String[] newOrder = new String[currentOrder.length + 1];
713 newOrder[0] = searchName;
714 for (int i = 0; i < currentOrder.length; i++) {
715 newOrder[i + 1] = currentOrder[i];
716 }
717
718 String newSearchOrder = rejoinWithCommas(newOrder);
719
720 userOptionsService.save(principalId, searchName, savedSearchString);
721 userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, newSearchOrder);
722 }
723 }
724 }
725 } catch (Exception e) {
726
727
728 LOG.error("Unable to save search due to exception", e);
729 }
730 }
731
732
733
734
735
736
737 private String rejoinWithCommas(String[] newOrder) {
738 StringBuilder newSearchOrder = new StringBuilder("");
739 for (String aNewOrder : newOrder) {
740 if (newSearchOrder.length() != 0) {
741 newSearchOrder.append(",");
742 }
743 newSearchOrder.append(aNewOrder);
744 }
745 return newSearchOrder.toString();
746 }
747
748 public ConfigurationService getKualiConfigurationService() {
749 if (kualiConfigurationService == null) {
750 kualiConfigurationService = KRADServiceLocator.getKualiConfigurationService();
751 }
752 return kualiConfigurationService;
753 }
754
755 }