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