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.core.api.util.RiceConstants;
32 import org.kuali.rice.kew.api.KewApiConstants;
33 import org.kuali.rice.kew.api.WorkflowRuntimeException;
34 import org.kuali.rice.kew.api.document.attribute.DocumentAttribute;
35 import org.kuali.rice.kew.api.document.attribute.DocumentAttributeFactory;
36 import org.kuali.rice.kew.api.document.search.DocumentSearchCriteria;
37 import org.kuali.rice.kew.api.document.search.DocumentSearchResult;
38 import org.kuali.rice.kew.api.document.search.DocumentSearchResults;
39 import org.kuali.rice.kew.docsearch.DocumentSearchCustomizationMediator;
40 import org.kuali.rice.kew.docsearch.DocumentSearchInternalUtils;
41 import org.kuali.rice.kew.docsearch.dao.DocumentSearchDAO;
42 import org.kuali.rice.kew.docsearch.service.DocumentSearchService;
43 import org.kuali.rice.kew.doctype.SecuritySession;
44 import org.kuali.rice.kew.doctype.bo.DocumentType;
45 import org.kuali.rice.kew.exception.WorkflowServiceError;
46 import org.kuali.rice.kew.exception.WorkflowServiceErrorException;
47 import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl;
48 import org.kuali.rice.kew.framework.document.search.AttributeFields;
49 import org.kuali.rice.kew.framework.document.search.DocumentSearchCriteriaConfiguration;
50 import org.kuali.rice.kew.framework.document.search.DocumentSearchResultValue;
51 import org.kuali.rice.kew.framework.document.search.DocumentSearchResultValues;
52 import org.kuali.rice.kew.impl.document.search.DocumentSearchGenerator;
53 import org.kuali.rice.kew.impl.document.search.DocumentSearchGeneratorImpl;
54 import org.kuali.rice.kew.service.KEWServiceLocator;
55 import org.kuali.rice.kew.useroptions.UserOptions;
56 import org.kuali.rice.kew.useroptions.UserOptionsService;
57 import org.kuali.rice.kew.util.Utilities;
58 import org.kuali.rice.kim.api.group.Group;
59 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
60 import org.kuali.rice.kns.service.DictionaryValidationService;
61 import org.kuali.rice.kns.service.DataDictionaryService;
62 import org.kuali.rice.kns.service.KNSServiceLocator;
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 customizedAttributeMap.remove(name);
260 }
261 } else {
262 if (!documentAttributeNamesCustomized.contains(name)) {
263 newDocumentAttributes.add(documentAttribute);
264 }
265 }
266 }
267
268 for (List<DocumentAttribute.AbstractBuilder<?>> cusotmizedDocumentAttribute : customizedAttributeMap.values()) {
269 newDocumentAttributes.addAll(cusotmizedDocumentAttribute);
270 }
271 result.setDocumentAttributes(newDocumentAttributes);
272 }
273
274
275
276
277
278
279 protected DocumentSearchCriteria applyCriteriaCustomizations(DocumentType documentType, DocumentSearchCriteria criteria) {
280 if (documentType == null) {
281 return criteria;
282 }
283 DocumentSearchCriteria customizedCriteria = getDocumentSearchCustomizationMediator().customizeCriteria(documentType, criteria);
284 if (customizedCriteria != null) {
285 return customizedCriteria;
286 }
287 return criteria;
288 }
289
290 protected DocumentSearchCriteria applyCriteriaDefaults(DocumentSearchCriteria criteria) {
291 DocumentSearchCriteria.Builder comparisonCriteria = createEmptyComparisonCriteria(criteria);
292 boolean isCriteriaEmpty = criteria.equals(comparisonCriteria.build());
293 boolean isTitleOnly = false;
294 boolean isDocTypeOnly = false;
295 if (!isCriteriaEmpty) {
296 comparisonCriteria.setTitle(criteria.getTitle());
297 isTitleOnly = criteria.equals(comparisonCriteria.build());
298 }
299
300 if (!isCriteriaEmpty && !isTitleOnly) {
301 comparisonCriteria = createEmptyComparisonCriteria(criteria);
302 comparisonCriteria.setDocumentTypeName(criteria.getDocumentTypeName());
303 isDocTypeOnly = criteria.equals(comparisonCriteria.build());
304 }
305
306 if (isCriteriaEmpty || isTitleOnly || isDocTypeOnly) {
307 DocumentSearchCriteria.Builder criteriaBuilder = DocumentSearchCriteria.Builder.create(criteria);
308 Integer defaultCreateDateDaysAgoValue = null;
309 if (isCriteriaEmpty || isDocTypeOnly) {
310
311 defaultCreateDateDaysAgoValue = KewApiConstants.DOCUMENT_SEARCH_NO_CRITERIA_CREATE_DATE_DAYS_AGO;
312 } else if (isTitleOnly) {
313
314
315 defaultCreateDateDaysAgoValue = KewApiConstants.DOCUMENT_SEARCH_DOC_TITLE_CREATE_DATE_DAYS_AGO;
316 }
317
318 if (defaultCreateDateDaysAgoValue != null) {
319
320 MutableDateTime mutableDateTime = new MutableDateTime();
321 mutableDateTime.addDays(defaultCreateDateDaysAgoValue.intValue());
322 criteriaBuilder.setDateCreatedFrom(mutableDateTime.toDateTime());
323 }
324 criteria = criteriaBuilder.build();
325 }
326 return criteria;
327 }
328
329 protected DocumentSearchCriteria.Builder createEmptyComparisonCriteria(DocumentSearchCriteria criteria) {
330 DocumentSearchCriteria.Builder builder = DocumentSearchCriteria.Builder.create();
331
332 builder.setSaveName(criteria.getSaveName());
333 builder.setStartAtIndex(criteria.getStartAtIndex());
334 builder.setMaxResults(criteria.getMaxResults());
335 builder.setIsAdvancedSearch(criteria.getIsAdvancedSearch());
336 builder.setSearchOptions(criteria.getSearchOptions());
337 return builder;
338 }
339
340 protected List<RemotableAttributeField> determineSearchFields(DocumentType documentType) {
341 List<RemotableAttributeField> searchFields = new ArrayList<RemotableAttributeField>();
342 if (documentType != null) {
343 DocumentSearchCriteriaConfiguration searchConfiguration =
344 getDocumentSearchCustomizationMediator().getDocumentSearchCriteriaConfiguration(documentType);
345 if (searchConfiguration != null) {
346 List<AttributeFields> attributeFields = searchConfiguration.getSearchAttributeFields();
347 if (attributeFields != null) {
348 for (AttributeFields fields : attributeFields) {
349 searchFields.addAll(fields.getRemotableAttributeFields());
350 }
351 }
352 }
353 }
354 return searchFields;
355 }
356
357 public DocumentSearchGenerator getStandardDocumentSearchGenerator() {
358 String searchGeneratorClass = ConfigContext.getCurrentContextConfig().getProperty(KewApiConstants.STANDARD_DOC_SEARCH_GENERATOR_CLASS_CONFIG_PARM);
359 if (searchGeneratorClass == null){
360 return new DocumentSearchGeneratorImpl();
361 }
362 return (DocumentSearchGenerator)GlobalResourceLoader.getObject(new ObjectDefinition(searchGeneratorClass));
363 }
364
365 @Override
366 public void validateDocumentSearchCriteria(DocumentSearchGenerator docSearchGenerator, DocumentSearchCriteria.Builder criteria) {
367 List<WorkflowServiceError> errors = this.validateWorkflowDocumentSearchCriteria(criteria);
368 List<RemotableAttributeError> searchAttributeErrors = docSearchGenerator.validateSearchableAttributes(criteria);
369 if (!CollectionUtils.isEmpty(searchAttributeErrors)) {
370
371 for (RemotableAttributeError searchAttributeError : searchAttributeErrors) {
372 for (String errorMessage : searchAttributeError.getErrors()) {
373 WorkflowServiceError error = new WorkflowServiceErrorImpl(errorMessage, "error.custom", errorMessage);
374 errors.add(error);
375 }
376 }
377 }
378 if (!errors.isEmpty() || !GlobalVariables.getMessageMap().hasNoErrors()) {
379 throw new WorkflowServiceErrorException("Document Search Validation Errors", errors);
380 }
381 }
382
383 protected List<WorkflowServiceError> validateWorkflowDocumentSearchCriteria(DocumentSearchCriteria.Builder criteria) {
384 List<WorkflowServiceError> errors = new ArrayList<WorkflowServiceError>();
385
386
387
388 criteria.setApproverPrincipalName(trimCriteriaValue(criteria.getApproverPrincipalName()));
389 criteria.setViewerPrincipalName(trimCriteriaValue(criteria.getViewerPrincipalName()));
390 criteria.setInitiatorPrincipalName(trimCriteriaValue(criteria.getInitiatorPrincipalName()));
391 validateGroupCriteria(criteria, errors);
392 criteria.setDocumentId(criteria.getDocumentId());
393
394
395 boolean compareDatePairs = true;
396 if (criteria.getDateCreatedFrom() == null) {
397 compareDatePairs = false;
398 }
399 else {
400 if (!validateDate("dateCreatedFrom", criteria.getDateCreatedFrom().toString(), "dateCreatedFrom")) {
401 compareDatePairs = false;
402 } else {
403 criteria.setDateCreatedFrom(criteria.getDateCreatedFrom());
404 }
405 }
406 if (criteria.getDateCreatedTo() == null) {
407 compareDatePairs = false;
408 }
409 else {
410 if (!validateDate("dateCreatedTo", criteria.getDateCreatedTo().toString(), "dateCreatedTo")) {
411 compareDatePairs = false;
412 } else {
413 criteria.setDateCreatedTo(criteria.getDateCreatedTo());
414 }
415 }
416 if (compareDatePairs) {
417 if (!checkDateRanges(new SimpleDateFormat(RiceConstants.SIMPLE_DATE_FORMAT_FOR_DATE).format(criteria.getDateCreatedFrom().toDate()), new SimpleDateFormat(RiceConstants.SIMPLE_DATE_FORMAT_FOR_DATE).format(criteria.getDateCreatedTo().toDate()))) {
418 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"));
419 }
420 }
421
422 compareDatePairs = true;
423 if (criteria.getDateApprovedFrom() == null) {
424 compareDatePairs = false;
425 }
426 else {
427 if (!validateDate("dateApprovedFrom", criteria.getDateApprovedFrom().toString(), "dateApprovedFrom")) {
428 compareDatePairs = false;
429 } else {
430 criteria.setDateApprovedFrom(criteria.getDateApprovedFrom());
431 }
432 }
433 if (criteria.getDateApprovedTo() == null) {
434 compareDatePairs = false;
435 }
436 else {
437 if (!validateDate("dateApprovedTo", criteria.getDateApprovedTo().toString(), "dateApprovedTo")) {
438 compareDatePairs = false;
439 } else {
440 criteria.setDateApprovedTo(criteria.getDateApprovedTo());
441 }
442 }
443 if (compareDatePairs) {
444 if (!checkDateRanges(new SimpleDateFormat(RiceConstants.SIMPLE_DATE_FORMAT_FOR_DATE).format(criteria.getDateApprovedFrom().toDate()), new SimpleDateFormat(RiceConstants.SIMPLE_DATE_FORMAT_FOR_DATE).format(criteria.getDateApprovedTo().toDate()))) {
445 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"));
446 }
447 }
448
449 compareDatePairs = true;
450 if (criteria.getDateFinalizedFrom() == null) {
451 compareDatePairs = false;
452 }
453 else {
454 if (!validateDate("dateFinalizedFrom", criteria.getDateFinalizedFrom().toString(), "dateFinalizedFrom")) {
455 compareDatePairs = false;
456 } else {
457 criteria.setDateFinalizedFrom(criteria.getDateFinalizedFrom());
458 }
459 }
460 if (criteria.getDateFinalizedTo() == null) {
461 compareDatePairs = false;
462 }
463 else {
464 if (!validateDate("dateFinalizedTo", criteria.getDateFinalizedTo().toString(), "dateFinalizedTo")) {
465 compareDatePairs = false;
466 } else {
467 criteria.setDateFinalizedTo(criteria.getDateFinalizedTo());
468 }
469 }
470 if (compareDatePairs) {
471 if (!checkDateRanges(new SimpleDateFormat(RiceConstants.SIMPLE_DATE_FORMAT_FOR_DATE).format(criteria.getDateFinalizedFrom().toDate()), new SimpleDateFormat(RiceConstants.SIMPLE_DATE_FORMAT_FOR_DATE).format(criteria.getDateFinalizedTo().toDate()))) {
472 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"));
473 }
474 }
475
476 compareDatePairs = true;
477 if (criteria.getDateLastModifiedFrom() == null) {
478 compareDatePairs = false;
479 }
480 else {
481 if (!validateDate("dateLastModifiedFrom", criteria.getDateLastModifiedFrom().toString(), "dateLastModifiedFrom")) {
482 compareDatePairs = false;
483 } else {
484 criteria.setDateLastModifiedFrom(criteria.getDateLastModifiedFrom());
485 }
486 }
487 if (criteria.getDateLastModifiedTo() == null) {
488 compareDatePairs = false;
489 }
490 else {
491 if (!validateDate("dateLastModifiedTo", criteria.getDateLastModifiedTo().toString(), "dateLastModifiedTo")) {
492 compareDatePairs = false;
493 } else {
494 criteria.setDateLastModifiedTo(criteria.getDateLastModifiedTo());
495 }
496 }
497 if (compareDatePairs) {
498 if (!checkDateRanges(new SimpleDateFormat(RiceConstants.SIMPLE_DATE_FORMAT_FOR_DATE).format(criteria.getDateLastModifiedFrom().toDate()), new SimpleDateFormat(RiceConstants.SIMPLE_DATE_FORMAT_FOR_DATE).format(criteria.getDateLastModifiedTo().toDate()))) {
499 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"));
500 }
501 }
502 return errors;
503 }
504
505 private boolean validateDate(String dateFieldName, String dateFieldValue, String dateFieldErrorKey) {
506
507 int oldErrorCount = GlobalVariables.getMessageMap().getErrorCount();
508 getDictionaryValidationService().validateAttributeFormat(DOC_SEARCH_CRITERIA_CLASS, dateFieldName, dateFieldValue, DATA_TYPE_DATE, dateFieldErrorKey);
509 return (GlobalVariables.getMessageMap().getErrorCount() <= oldErrorCount);
510 }
511
512 public static DictionaryValidationService getDictionaryValidationService() {
513 if (dictionaryValidationService == null) {
514 dictionaryValidationService = KNSServiceLocator.getKNSDictionaryValidationService();
515 }
516 return dictionaryValidationService;
517 }
518
519 public static DataDictionaryService getDataDictionaryService() {
520 if (dataDictionaryService == null) {
521 dataDictionaryService = KNSServiceLocator.getDataDictionaryService();
522 }
523 return dataDictionaryService;
524 }
525
526 private boolean checkDateRanges(String fromDate, String toDate) {
527 return Utilities.checkDateRanges(fromDate, toDate);
528 }
529 private String trimCriteriaValue(String criteriaValue) {
530 if (StringUtils.isNotBlank(criteriaValue)) {
531 criteriaValue = criteriaValue.trim();
532 }
533 if (StringUtils.isBlank(criteriaValue)) {
534 return null;
535 }
536 return criteriaValue;
537 }
538
539 private void validateGroupCriteria(DocumentSearchCriteria.Builder criteria, List<WorkflowServiceError> errors) {
540 if (StringUtils.isNotBlank(criteria.getGroupViewerId())) {
541 Group group = KimApiServiceLocator.getGroupService().getGroup(criteria.getGroupViewerId());
542 if (group == null) {
543 errors.add(new WorkflowServiceErrorImpl("Workgroup Viewer Name is not a workgroup", "docsearch.DocumentSearchService.workgroup.viewer"));
544 }
545 } else {
546 criteria.setGroupViewerId(null);
547 }
548 }
549
550 @Override
551 public List<KeyValue> getNamedSearches(String principalId) {
552 List<UserOptions> namedSearches = userOptionsService.findByUserQualified(principalId, NAMED_SEARCH_ORDER_BASE + "%");
553 List<KeyValue> sortedNamedSearches = new ArrayList<KeyValue>(0);
554 if (!namedSearches.isEmpty()) {
555 Collections.sort(namedSearches);
556 for (UserOptions namedSearch : namedSearches) {
557 KeyValue keyValue = new ConcreteKeyValue(namedSearch.getOptionId(), namedSearch.getOptionId().substring(NAMED_SEARCH_ORDER_BASE.length(), namedSearch.getOptionId().length()));
558 sortedNamedSearches.add(keyValue);
559 }
560 }
561 return sortedNamedSearches;
562 }
563
564 @Override
565 public List<KeyValue> getMostRecentSearches(String principalId) {
566 UserOptions order = userOptionsService.findByOptionId(LAST_SEARCH_ORDER_OPTION, principalId);
567 List<KeyValue> sortedMostRecentSearches = new ArrayList<KeyValue>();
568 if (order != null && order.getOptionVal() != null && !"".equals(order.getOptionVal())) {
569 List<UserOptions> mostRecentSearches = userOptionsService.findByUserQualified(principalId, LAST_SEARCH_BASE_NAME + "%");
570 String[] ordered = order.getOptionVal().split(",");
571 for (String anOrdered : ordered) {
572 UserOptions matchingOption = null;
573 for (UserOptions option : mostRecentSearches) {
574 if (anOrdered.equals(option.getOptionId())) {
575 matchingOption = option;
576 break;
577 }
578 }
579 if (matchingOption != null) {
580 DocumentSearchCriteria matchingCriteria = getCriteriaFromSavedSearch(matchingOption);
581 sortedMostRecentSearches.add(new ConcreteKeyValue(anOrdered, getSavedSearchAbbreviatedString(matchingCriteria)));
582 }
583 }
584 }
585 return sortedMostRecentSearches;
586 }
587
588 public DocumentSearchCriteria clearCriteria(DocumentType documentType, DocumentSearchCriteria criteria) {
589 DocumentSearchCriteria clearedCriteria = getDocumentSearchCustomizationMediator().customizeClearCriteria(
590 documentType, criteria);
591 if (clearedCriteria == null) {
592 clearedCriteria = getStandardDocumentSearchGenerator().clearSearch(criteria);
593 }
594 return clearedCriteria;
595 }
596
597 protected String getSavedSearchAbbreviatedString(DocumentSearchCriteria criteria) {
598 Map<String, String> abbreviatedStringMap = new LinkedHashMap<String, String>();
599 addAbbreviatedString(abbreviatedStringMap, "Doc Type", criteria.getDocumentTypeName());
600 addAbbreviatedString(abbreviatedStringMap, "Initiator", criteria.getInitiatorPrincipalName());
601 addAbbreviatedString(abbreviatedStringMap, "Doc Id", criteria.getDocumentId());
602 addAbbreviatedRangeString(abbreviatedStringMap, "Created", criteria.getDateCreatedFrom(),
603 criteria.getDateCreatedTo());
604 addAbbreviatedString(abbreviatedStringMap, "Title", criteria.getTitle());
605 addAbbreviatedString(abbreviatedStringMap, "App Doc Id", criteria.getApplicationDocumentId());
606 addAbbreviatedRangeString(abbreviatedStringMap, "Approved", criteria.getDateApprovedFrom(),
607 criteria.getDateApprovedTo());
608 addAbbreviatedRangeString(abbreviatedStringMap, "Modified", criteria.getDateLastModifiedFrom(), criteria.getDateLastModifiedTo());
609 addAbbreviatedRangeString(abbreviatedStringMap, "Finalized", criteria.getDateFinalizedFrom(), criteria.getDateFinalizedTo());
610 addAbbreviatedRangeString(abbreviatedStringMap, "App Doc Status Changed", criteria.getDateApplicationDocumentStatusChangedFrom(), criteria.getDateApplicationDocumentStatusChangedTo());
611 addAbbreviatedString(abbreviatedStringMap, "Approver", criteria.getApproverPrincipalName());
612 addAbbreviatedString(abbreviatedStringMap, "Viewer", criteria.getViewerPrincipalName());
613 addAbbreviatedString(abbreviatedStringMap, "Group Viewer", criteria.getGroupViewerId());
614 addAbbreviatedString(abbreviatedStringMap, "Node", criteria.getRouteNodeName());
615 addAbbreviatedMultiValuedString(abbreviatedStringMap, "Status", criteria.getDocumentStatuses());
616 addAbbreviatedMultiValuedString(abbreviatedStringMap, "Category", criteria.getDocumentStatusCategories());
617 for (String documentAttributeName : criteria.getDocumentAttributeValues().keySet()) {
618 addAbbreviatedMultiValuedString(abbreviatedStringMap, documentAttributeName, criteria.getDocumentAttributeValues().get(documentAttributeName));
619 }
620 StringBuilder stringBuilder = new StringBuilder();
621 int iteration = 0;
622 for (String label : abbreviatedStringMap.keySet()) {
623 stringBuilder.append(label).append("=").append(abbreviatedStringMap.get(label));
624 if (iteration < abbreviatedStringMap.keySet().size()) {
625 stringBuilder.append("; ");
626 }
627 }
628 return stringBuilder.toString();
629 }
630
631 protected void addAbbreviatedString(Map<String, String> abbreviatedStringMap, String label, String value) {
632 if (StringUtils.isNotBlank(value)) {
633 abbreviatedStringMap.put(label, value);
634 }
635 }
636
637 protected void addAbbreviatedMultiValuedString(Map<String, String> abbreviatedStringMap, String label, Collection<? extends Object> values) {
638 if (CollectionUtils.isNotEmpty(values)) {
639 List<String> stringValues = new ArrayList<String>();
640 for (Object value : values) {
641 stringValues.add(value.toString());
642 }
643 abbreviatedStringMap.put(label, StringUtils.join(stringValues, ","));
644 }
645 }
646
647 protected void addAbbreviatedRangeString(Map<String, String> abbreviatedStringMap, String label, DateTime dateFrom, DateTime dateTo) {
648 if (dateFrom != null || dateTo != null) {
649 StringBuilder abbreviatedString = new StringBuilder();
650 if (dateFrom != null) {
651 abbreviatedString.append(CoreApiServiceLocator.getDateTimeService().toDateString(dateFrom.toDate()));
652 }
653 abbreviatedString.append("..");
654 if (dateTo != null) {
655 abbreviatedString.append(CoreApiServiceLocator.getDateTimeService().toDateString(dateTo.toDate()));
656 }
657 abbreviatedStringMap.put(label, abbreviatedString.toString());
658 }
659 }
660
661
662
663
664
665
666
667
668
669 private void saveSearch(String principalId, DocumentSearchCriteria criteria) {
670 if (StringUtils.isBlank(principalId)) {
671 return;
672 }
673
674 try {
675 String savedSearchString = DocumentSearchInternalUtils.marshalDocumentSearchCriteria(criteria);
676
677 if (StringUtils.isNotBlank(criteria.getSaveName())) {
678 userOptionsService.save(principalId, NAMED_SEARCH_ORDER_BASE + criteria.getSaveName(), savedSearchString);
679 } else {
680
681 UserOptions searchOrder = userOptionsService.findByOptionId(LAST_SEARCH_ORDER_OPTION, principalId);
682
683 if (searchOrder == null) {
684 userOptionsService.save(principalId, LAST_SEARCH_BASE_NAME + "0", savedSearchString);
685 userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, LAST_SEARCH_BASE_NAME + "0");
686 } else {
687 String[] currentOrder = searchOrder.getOptionVal().split(",");
688
689 if (currentOrder.length == MAX_SEARCH_ITEMS) {
690
691
692
693 String searchName = currentOrder[currentOrder.length - 1];
694 String[] newOrder = new String[MAX_SEARCH_ITEMS];
695 newOrder[0] = searchName;
696 for (int i = 0; i < currentOrder.length - 1; i++) {
697 newOrder[i + 1] = currentOrder[i];
698 }
699
700 String newSearchOrder = rejoinWithCommas(newOrder);
701
702 userOptionsService.save(principalId, searchName, savedSearchString);
703 userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, newSearchOrder);
704 } else {
705
706
707
708
709 int absMax = 0;
710 for (String aCurrentOrder : currentOrder) {
711 int current = new Integer(aCurrentOrder.substring(LAST_SEARCH_BASE_NAME.length(),
712 aCurrentOrder.length()));
713 if (current > absMax) {
714 absMax = current;
715 }
716 }
717 String searchName = LAST_SEARCH_BASE_NAME + ++absMax;
718 String[] newOrder = new String[currentOrder.length + 1];
719 newOrder[0] = searchName;
720 for (int i = 0; i < currentOrder.length; i++) {
721 newOrder[i + 1] = currentOrder[i];
722 }
723
724 String newSearchOrder = rejoinWithCommas(newOrder);
725
726 userOptionsService.save(principalId, searchName, savedSearchString);
727 userOptionsService.save(principalId, LAST_SEARCH_ORDER_OPTION, newSearchOrder);
728 }
729 }
730 }
731 } catch (Exception e) {
732
733
734 LOG.error("Unable to save search due to exception", e);
735 }
736 }
737
738
739
740
741
742
743 private String rejoinWithCommas(String[] newOrder) {
744 StringBuilder newSearchOrder = new StringBuilder("");
745 for (String aNewOrder : newOrder) {
746 if (newSearchOrder.length() != 0) {
747 newSearchOrder.append(",");
748 }
749 newSearchOrder.append(aNewOrder);
750 }
751 return newSearchOrder.toString();
752 }
753
754 public ConfigurationService getKualiConfigurationService() {
755 if (kualiConfigurationService == null) {
756 kualiConfigurationService = CoreApiServiceLocator.getKualiConfigurationService();
757 }
758 return kualiConfigurationService;
759 }
760
761 @Override
762 public int getMaxResultCap(DocumentSearchCriteria criteria){
763 return docSearchDao.getMaxResultCap(criteria);
764 }
765
766 @Override
767 public int getFetchMoreIterationLimit(){
768 return docSearchDao.getFetchMoreIterationLimit();
769 }
770
771 }