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