View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.document;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.ArrayUtils;
20  import org.apache.commons.lang.StringUtils;
21  import org.kuali.rice.core.api.CoreApiServiceLocator;
22  import org.kuali.rice.core.api.config.property.ConfigContext;
23  import org.kuali.rice.core.api.config.property.ConfigurationService;
24  import org.kuali.rice.core.api.exception.RiceRuntimeException;
25  import org.kuali.rice.core.api.util.RiceKeyConstants;
26  import org.kuali.rice.coreservice.framework.parameter.ParameterConstants;
27  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
28  import org.kuali.rice.kew.api.KewApiConstants;
29  import org.kuali.rice.kew.api.KewApiServiceLocator;
30  import org.kuali.rice.kew.api.WorkflowDocument;
31  import org.kuali.rice.kew.api.action.ActionRequest;
32  import org.kuali.rice.kew.api.action.ActionRequestType;
33  import org.kuali.rice.kew.api.action.DocumentActionParameters;
34  import org.kuali.rice.kew.api.action.WorkflowDocumentActionsService;
35  import org.kuali.rice.kew.api.doctype.DocumentType;
36  import org.kuali.rice.kew.api.exception.WorkflowException;
37  import org.kuali.rice.kim.api.identity.Person;
38  import org.kuali.rice.krad.UserSessionUtils;
39  import org.kuali.rice.krad.bo.AdHocRouteRecipient;
40  import org.kuali.rice.krad.bo.Attachment;
41  import org.kuali.rice.krad.bo.DocumentHeader;
42  import org.kuali.rice.krad.bo.Note;
43  import org.kuali.rice.krad.exception.DocumentAuthorizationException;
44  import org.kuali.rice.krad.exception.UnknownDocumentIdException;
45  import org.kuali.rice.krad.exception.ValidationException;
46  import org.kuali.rice.krad.maintenance.MaintenanceDocument;
47  import org.kuali.rice.krad.rules.rule.event.AddNoteEvent;
48  import org.kuali.rice.krad.rules.rule.event.DocumentEvent;
49  import org.kuali.rice.krad.rules.rule.event.RouteDocumentEvent;
50  import org.kuali.rice.krad.rules.rule.event.SaveDocumentEvent;
51  import org.kuali.rice.krad.service.AttachmentService;
52  import org.kuali.rice.krad.service.DataDictionaryService;
53  import org.kuali.rice.krad.service.DocumentDictionaryService;
54  import org.kuali.rice.krad.service.DocumentService;
55  import org.kuali.rice.krad.service.KRADServiceLocator;
56  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
57  import org.kuali.rice.krad.service.LegacyDataAdapter;
58  import org.kuali.rice.krad.service.NoteService;
59  import org.kuali.rice.krad.uif.UifConstants;
60  import org.kuali.rice.krad.uif.UifParameters;
61  import org.kuali.rice.krad.uif.UifPropertyPaths;
62  import org.kuali.rice.krad.uif.component.BindingInfo;
63  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
64  import org.kuali.rice.krad.uif.view.DocumentView;
65  import org.kuali.rice.krad.util.GlobalVariables;
66  import org.kuali.rice.krad.util.KRADConstants;
67  import org.kuali.rice.krad.util.KRADUtils;
68  import org.kuali.rice.krad.util.NoteType;
69  import org.kuali.rice.krad.web.form.DialogResponse;
70  import org.kuali.rice.krad.web.form.DocumentFormBase;
71  import org.kuali.rice.krad.web.form.UifFormBase;
72  import org.kuali.rice.krad.web.service.CollectionControllerService;
73  import org.kuali.rice.krad.web.service.ModelAndViewService;
74  import org.kuali.rice.krad.web.service.NavigationControllerService;
75  import org.kuali.rice.krad.web.service.impl.ControllerServiceImpl;
76  import org.kuali.rice.ksb.api.KsbApiServiceLocator;
77  import org.springframework.web.multipart.MultipartFile;
78  import org.springframework.web.servlet.ModelAndView;
79  
80  import javax.servlet.http.HttpServletResponse;
81  import javax.xml.namespace.QName;
82  import java.io.IOException;
83  import java.util.ArrayList;
84  import java.util.List;
85  import java.util.Properties;
86  import java.util.Set;
87  
88  /**
89   * Default implementation of the document controller service.
90   *
91   * @author Kuali Rice Team (rice.collab@kuali.org)
92   */
93  public class DocumentControllerServiceImpl extends ControllerServiceImpl implements DocumentControllerService {
94      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(
95              DocumentControllerServiceImpl.class);
96  
97      // COMMAND constants which cause docHandler to load an existing document instead of creating a new one
98      protected static final String[] DOCUMENT_LOAD_COMMANDS =
99              {KewApiConstants.ACTIONLIST_COMMAND, KewApiConstants.DOCSEARCH_COMMAND, KewApiConstants.SUPERUSER_COMMAND,
100                     KewApiConstants.HELPDESK_ACTIONLIST_COMMAND};
101     protected static final String SENSITIVE_DATA_DIALOG = "DialogGroup-SensitiveData";
102     protected static final String EXPLANATION_DIALOG = "DisapproveExplanationDialog";
103 
104     private LegacyDataAdapter legacyDataAdapter;
105     private DataDictionaryService dataDictionaryService;
106     private DocumentService documentService;
107     private DocumentDictionaryService documentDictionaryService;
108     private AttachmentService attachmentService;
109     private NoteService noteService;
110     private ModelAndViewService modelAndViewService;
111     private NavigationControllerService navigationControllerService;
112     private ConfigurationService configurationService;
113     private CollectionControllerService collectionControllerService;
114     private ParameterService parameterService;
115 
116     /**
117      * Determines whether a new document instance needs created or we need to load an existing document by
118      * checking the {@link org.kuali.rice.krad.web.form.DocumentFormBase#getCommand()} value, then delegates to
119      * a helper method to carry out the action.
120      *
121      * {@inheritDoc}
122      */
123     @Override
124     public ModelAndView docHandler(DocumentFormBase form) throws WorkflowException {
125         String command = form.getCommand();
126         DocumentView view = (DocumentView) form.getView();
127 
128         if (ArrayUtils.contains(DOCUMENT_LOAD_COMMANDS, command) && (form.getDocId() != null)) {
129             checkReturnLocationForDocSearch(command, form);
130 
131             loadDocument(form);
132 
133             if (KewApiConstants.SUPERUSER_COMMAND.equals(command)) {
134                 view.setSuperUserView(true);
135             }
136         } else if (KewApiConstants.INITIATE_COMMAND.equals(command)) {
137             if (view != null) {
138                 form.setApplyDefaultValues(true);
139             }
140 
141             createDocument(form);
142         } else {
143             LOG.error("docHandler called with invalid parameters");
144             throw new IllegalArgumentException("docHandler called with invalid parameters");
145         }
146 
147         return getModelAndViewService().getModelAndView(form);
148     }
149 
150     /**
151      * Determines if the DOCSEARCH_COMMAND is the present command value and updates the return location to the base
152      * application url
153      *
154      * @param command the current command value
155      * @param form the form with the updated return location
156      */
157     private void checkReturnLocationForDocSearch(String command, DocumentFormBase form) {
158         if (KewApiConstants.DOCSEARCH_COMMAND.equals(command)) {
159             form.setReturnLocation(ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.APPLICATION_URL_KEY));
160         }
161     }
162 
163     /**
164      * Loads the document by its provided document header id on the given form.
165      *
166      * <p>This has been abstracted out so that it can be overridden in children if the need arises</p>
167      *
168      * @param form form instance that contains the doc id parameter and where
169      * the retrieved document instance should be set
170      */
171     protected void loadDocument(DocumentFormBase form) throws WorkflowException {
172         String docId = form.getDocId();
173 
174         if (LOG.isDebugEnabled()) {
175             LOG.debug("Loading document" + docId);
176         }
177 
178         Document document = getDocumentService().getByDocumentHeaderId(docId);
179         if (document == null) {
180             throw new UnknownDocumentIdException(
181                     "Document no longer exists.  It may have been cancelled before being saved.");
182         }
183 
184         WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
185         if (!getDocumentDictionaryService().getDocumentAuthorizer(document).canOpen(document,
186                 GlobalVariables.getUserSession().getPerson())) {
187             throw buildAuthorizationException("open", document);
188         }
189 
190         // re-retrieve the document using the current user's session - remove
191         // the system user from the WorkflowDcument object
192         if (workflowDocument != document.getDocumentHeader().getWorkflowDocument()) {
193             LOG.warn("Workflow document changed via canOpen check");
194             document.getDocumentHeader().setWorkflowDocument(workflowDocument);
195         }
196 
197         form.setDocument(document);
198         form.setDocTypeName(workflowDocument.getDocumentTypeName());
199 
200         UserSessionUtils.addWorkflowDocument(GlobalVariables.getUserSession(), workflowDocument);
201     }
202 
203     /**
204      * Creates a new document of the type specified by the docTypeName property of the given form.
205      *
206      * <p>This has been abstracted out so that it can be overridden in children if the need arises</p>
207      *
208      * @param form form instance that contains the doc type parameter and where
209      * the new document instance should be set
210      */
211     protected void createDocument(DocumentFormBase form) throws WorkflowException {
212         if (LOG.isDebugEnabled()) {
213             LOG.debug("Creating new document instance for doc type: " + form.getDocTypeName());
214         }
215 
216         Document doc = getDocumentService().getNewDocument(form.getDocTypeName());
217 
218         form.setDocument(doc);
219         form.setDocTypeName(doc.getDocumentHeader().getWorkflowDocument().getDocumentTypeName());
220     }
221 
222     /**
223      * {@inheritDoc}
224      */
225     @Override
226     public ModelAndView cancel(UifFormBase form) {
227         performWorkflowAction((DocumentFormBase) form, UifConstants.WorkflowAction.CANCEL);
228 
229         return getNavigationControllerService().returnToPrevious(form);
230     }
231 
232     /**
233      * {@inheritDoc}
234      */
235     @Override
236     public ModelAndView reload(DocumentFormBase form) throws WorkflowException {
237         Document document = form.getDocument();
238 
239         // prepare the reload action by calling dochandler (set doc id and command)
240         form.setDocId(document.getDocumentNumber());
241         form.setCommand(DOCUMENT_LOAD_COMMANDS[1]);
242 
243         form.setEvaluateFlagsAndModes(true);
244         form.setCanEditView(null);
245 
246         GlobalVariables.getMessageMap().putInfo(KRADConstants.GLOBAL_MESSAGES, RiceKeyConstants.MESSAGE_RELOADED);
247 
248         return docHandler(form);
249     }
250 
251     /**
252      * {@inheritDoc}
253      */
254     @Override
255     public ModelAndView recall(DocumentFormBase form) {
256         performWorkflowAction(form, UifConstants.WorkflowAction.RECALL);
257 
258         return getModelAndViewService().getModelAndView(form);
259     }
260 
261     /**
262      * {@inheritDoc}
263      */
264     @Override
265     public ModelAndView save(DocumentFormBase form) {
266         return save(form, null);
267     }
268 
269     /**
270      * {@inheritDoc}
271      */
272     @Override
273     public ModelAndView save(DocumentFormBase form, SaveDocumentEvent saveDocumentEvent) {
274         Document document = form.getDocument();
275 
276         // get the explanation from the document and check it for sensitive data
277         String explanation = document.getDocumentHeader().getExplanation();
278         ModelAndView sensitiveDataDialogModelAndView = checkSensitiveDataAndWarningDialog(explanation, form);
279 
280         // if a sensitive data warning dialog is returned then display it
281         if (sensitiveDataDialogModelAndView != null) {
282             return sensitiveDataDialogModelAndView;
283         }
284 
285         performWorkflowAction(form, UifConstants.WorkflowAction.SAVE, saveDocumentEvent);
286 
287         return getModelAndViewService().getModelAndView(form);
288     }
289 
290     /**
291      * {@inheritDoc}
292      */
293     @Override
294     public ModelAndView complete(DocumentFormBase form) {
295         performWorkflowAction(form, UifConstants.WorkflowAction.COMPLETE);
296 
297         return getModelAndViewService().getModelAndView(form);
298     }
299 
300     /**
301      * {@inheritDoc}
302      */
303     @Override
304     public ModelAndView route(DocumentFormBase form) {
305         performWorkflowAction(form, UifConstants.WorkflowAction.ROUTE);
306 
307         return getModelAndViewService().getModelAndView(form);
308     }
309 
310     /**
311      * {@inheritDoc}
312      */
313     @Override
314     public ModelAndView blanketApprove(DocumentFormBase form) {
315         Document document = form.getDocument();
316         WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument();
317 
318         //disable blanket approve if adhoc route completion exists
319         List<AdHocRouteRecipient> adHocRecipients = new ArrayList<AdHocRouteRecipient>();
320         adHocRecipients.addAll(document.getAdHocRoutePersons());
321         adHocRecipients.addAll(document.getAdHocRouteWorkgroups());
322 
323         //check for ad hoc completion request
324         for (AdHocRouteRecipient adHocRouteRecipient : adHocRecipients) {
325             String actionRequestedCode = adHocRouteRecipient.getActionRequested();
326 
327             //add error and send back
328             if (KewApiConstants.ACTION_REQUEST_COMPLETE_REQ.equals(actionRequestedCode)) {
329                 GlobalVariables.getMessageMap().putError(KRADConstants.NEW_AD_HOC_ROUTE_WORKGROUP_PROPERTY_NAME,
330                         RiceKeyConstants.ERROR_ADHOC_COMPLETE_BLANKET_APPROVE_NOT_ALLOWED);
331 
332                 return getModelAndViewService().getModelAndView(form);
333             }
334         }
335 
336         performWorkflowAction(form, UifConstants.WorkflowAction.BLANKETAPPROVE);
337 
338         if (GlobalVariables.getMessageMap().hasErrors()) {
339             return getModelAndViewService().getModelAndView(form);
340         }
341 
342         return getNavigationControllerService().returnToPrevious(form);
343     }
344 
345     /**
346      * {@inheritDoc}
347      */
348     @Override
349     public ModelAndView approve(DocumentFormBase form) {
350         performWorkflowAction(form, UifConstants.WorkflowAction.APPROVE);
351 
352         return getNavigationControllerService().returnToPrevious(form);
353     }
354 
355     /**
356      * {@inheritDoc}
357      */
358     @Override
359     public ModelAndView disapprove(DocumentFormBase form) {
360         // get the explanation for disapproval from the disapprove dialog and check it for sensitive data
361         String explanationData = generateDisapprovalNote(form);
362         ModelAndView sensitiveDataDialogModelAndView = checkSensitiveDataAndWarningDialog(explanationData, form);
363 
364         // if a sensitive data warning dialog is returned then display it
365         if (sensitiveDataDialogModelAndView != null) {
366             return sensitiveDataDialogModelAndView;
367         }
368 
369         performWorkflowAction(form, UifConstants.WorkflowAction.DISAPPROVE);
370 
371         return getNavigationControllerService().returnToPrevious(form);
372     }
373 
374     /**
375      * Convenience method for generating disapproval note with text from the explanation dialog.
376      *
377      * @param form document form instance containing the explanation dialog
378      * @return
379      */
380     protected String generateDisapprovalNote(DocumentFormBase form) {
381         String explanationData = form.getDialogExplanations().get(EXPLANATION_DIALOG);
382         if(explanationData == null) {
383             return "";
384         }
385 
386         // build out full message to return
387         String introNoteMessage = getConfigurationService().getPropertyValueAsString(
388                 RiceKeyConstants.MESSAGE_DISAPPROVAL_NOTE_TEXT_INTRO) + KRADConstants.BLANK_SPACE;
389 
390         return introNoteMessage + explanationData;
391     }
392 
393     /**
394      * {@inheritDoc}
395      */
396     @Override
397     public ModelAndView fyi(DocumentFormBase form) {
398         performWorkflowAction(form, UifConstants.WorkflowAction.FYI);
399 
400         return getNavigationControllerService().returnToPrevious(form);
401     }
402 
403     /**
404      * {@inheritDoc}
405      */
406     @Override
407     public ModelAndView acknowledge(DocumentFormBase form) {
408         performWorkflowAction(form, UifConstants.WorkflowAction.ACKNOWLEDGE);
409 
410         return getNavigationControllerService().returnToPrevious(form);
411     }
412 
413     /**
414      * {@inheritDoc}
415      */
416     @Override
417     public ModelAndView sendAdHocRequests(DocumentFormBase form) {
418         performWorkflowAction(form, UifConstants.WorkflowAction.SENDADHOCREQUESTS);
419 
420         return getModelAndViewService().getModelAndView(form);
421     }
422 
423     /**
424      * {@inheritDoc}
425      */
426     @Override
427     public ModelAndView supervisorFunctions(DocumentFormBase form) {
428         String workflowSuperUserUrl = getConfigurationService().getPropertyValueAsString(KRADConstants.WORKFLOW_URL_KEY)
429                 + "/"
430                 + KRADConstants.SUPERUSER_ACTION;
431 
432         Properties props = new Properties();
433         props.setProperty(UifParameters.METHOD_TO_CALL, UifConstants.MethodToCallNames.DISPLAY_SUPER_USER_DOCUMENT);
434         props.setProperty(UifPropertyPaths.DOCUMENT_ID, form.getDocument().getDocumentNumber());
435 
436         return getModelAndViewService().performRedirect(form, workflowSuperUserUrl, props);
437     }
438 
439     /**
440      * {@inheritDoc}
441      */
442     @Override
443     public ModelAndView close(DocumentFormBase form) {
444         return getNavigationControllerService().returnToPrevious(form);
445     }
446 
447     /**
448      * Validates the note, saves attachment, adds the time stamp and author, and calls the
449      * generic addLine method.
450      *
451      * {@inheritDoc}
452      */
453     @Override
454     public ModelAndView insertNote(DocumentFormBase form) {
455         Document document = form.getDocument();
456 
457         Note newNote = getAddLineNoteInstance(form);
458         setNewNoteProperties(form, document, newNote);
459 
460         Attachment attachment = getNewNoteAttachment(form, document, newNote);
461 
462         // validate the note
463         boolean rulesPassed = KRADServiceLocatorWeb.getKualiRuleService().applyRules(new AddNoteEvent(document,
464                 newNote));
465         if (!rulesPassed) {
466             return getModelAndViewService().getModelAndView(form);
467         }
468 
469         // adding the attachment after refresh gets called, since the attachment record doesn't get persisted
470         // until the note does (and therefore refresh doesn't have any attachment to autoload based on the id, nor
471         // does it autopopulate the id since the note hasn't been persisted yet)
472         if (attachment != null) {
473             newNote.addAttachment(attachment);
474         }
475 
476         // check for sensitive data within the note and display warning dialog if necessary
477         ModelAndView sensitiveDataDialogModelAndView = checkSensitiveDataAndWarningDialog(newNote.getNoteText(), form);
478         if (sensitiveDataDialogModelAndView != null) {
479             return sensitiveDataDialogModelAndView;
480         }
481 
482         saveNewNote(form, document, newNote);
483 
484         return getCollectionControllerService().addLine(form);
485     }
486 
487     /**
488      * Retrieves the note instance on the form that should be added to the document notes.
489      *
490      * @param form form instance containing the add note instance
491      * @return
492      */
493     protected Note getAddLineNoteInstance(DocumentFormBase form) {
494         String selectedCollectionId = form.getActionParamaterValue(UifParameters.SELECTED_COLLECTION_ID);
495 
496         BindingInfo addLineBindingInfo = (BindingInfo) form.getViewPostMetadata().getComponentPostData(
497                 selectedCollectionId, UifConstants.PostMetadata.ADD_LINE_BINDING_INFO);
498 
499         String addLinePath = addLineBindingInfo.getBindingPath();
500         Object addLine = ObjectPropertyUtils.getPropertyValue(form, addLinePath);
501 
502         return (Note) addLine;
503     }
504 
505     /**
506      * Defaults properties (posted timestamp, object id, author) on the note instance that will be added.
507      *
508      * @param form form instance containing the add note instance
509      * @param document document instance the note will be added to
510      * @param newNote note instance to set properties on
511      */
512     protected void setNewNoteProperties(DocumentFormBase form, Document document, Note newNote) {
513         newNote.setNotePostedTimestampToCurrent();
514         newNote.setRemoteObjectIdentifier(document.getNoteTarget().getObjectId());
515 
516         Person kualiUser = GlobalVariables.getUserSession().getPerson();
517         if (kualiUser == null) {
518             throw new IllegalStateException("Current UserSession has a null Person.");
519         }
520 
521         newNote.setAuthorUniversalIdentifier(kualiUser.getPrincipalId());
522     }
523 
524     /**
525      * Builds an attachment for the file (if any) associated with the add note instance.
526      *
527      * @param form form instance containing the attachment file
528      * @param document document instance the attachment should be associated with
529      * @param newNote note instance the attachment should be associated with
530      * @return Attachment instance for the note, or null if no attachment file was present
531      */
532     protected Attachment getNewNoteAttachment(DocumentFormBase form, Document document, Note newNote) {
533         MultipartFile attachmentFile = form.getAttachmentFile();
534 
535         if ((attachmentFile == null) || StringUtils.isBlank(attachmentFile.getOriginalFilename())) {
536             return null;
537         }
538 
539         if (attachmentFile.getSize() == 0) {
540             GlobalVariables.getMessageMap().putError(String.format("%s.%s",
541                     KRADConstants.NEW_DOCUMENT_NOTE_PROPERTY_NAME, KRADConstants.NOTE_ATTACHMENT_FILE_PROPERTY_NAME),
542                     RiceKeyConstants.ERROR_UPLOADFILE_EMPTY, attachmentFile.getOriginalFilename());
543 
544             return null;
545         }
546 
547         String attachmentTypeCode = null;
548         if (newNote.getAttachment() != null) {
549             attachmentTypeCode = newNote.getAttachment().getAttachmentTypeCode();
550         }
551 
552         DocumentAuthorizer documentAuthorizer = getDocumentDictionaryService().getDocumentAuthorizer(document);
553         if (!documentAuthorizer.canAddNoteAttachment(document, attachmentTypeCode,
554                 GlobalVariables.getUserSession().getPerson())) {
555             throw buildAuthorizationException("annotate", document);
556         }
557 
558         Attachment attachment;
559         try {
560             attachment = getAttachmentService().createAttachment(document.getNoteTarget(),
561                     attachmentFile.getOriginalFilename(), attachmentFile.getContentType(),
562                     (int) attachmentFile.getSize(), attachmentFile.getInputStream(), attachmentTypeCode);
563         } catch (IOException e) {
564             throw new RiceRuntimeException("Unable to store attachment", e);
565         }
566 
567         return attachment;
568     }
569 
570     /**
571      * Saves a new note instance to the data store if the document state allows it.
572      *
573      * @param form form instance containing the add note instance
574      * @param document document instance the note is associated with
575      * @param newNote note instance to save
576      */
577     protected void saveNewNote(DocumentFormBase form, Document document, Note newNote) {
578         DocumentHeader documentHeader = document.getDocumentHeader();
579 
580         if (!documentHeader.getWorkflowDocument().isInitiated() && StringUtils.isNotEmpty(
581                 document.getNoteTarget().getObjectId()) && !(document instanceof MaintenanceDocument && NoteType
582                 .BUSINESS_OBJECT.getCode().equals(newNote.getNoteTypeCode()))) {
583 
584             getNoteService().save(newNote);
585         }
586     }
587 
588     /**
589      * {@inheritDoc}
590      */
591     @Override
592     public ModelAndView deleteNote(DocumentFormBase form) {
593         String selectedLineIndex = form.getActionParamaterValue(UifParameters.SELECTED_LINE_INDEX);
594 
595         Document document = form.getDocument();
596 
597         Note note = document.getNote(Integer.parseInt(selectedLineIndex));
598         Attachment attachment = note.getAttachment();
599 
600         String attachmentTypeCode = null;
601         if (attachment != null) {
602             attachmentTypeCode = attachment.getAttachmentTypeCode();
603         }
604 
605         // verify the user has permissions to delete the note
606         Person user = GlobalVariables.getUserSession().getPerson();
607         if (!getDocumentDictionaryService().getDocumentAuthorizer(document).canDeleteNoteAttachment(document,
608                 attachmentTypeCode, note.getAuthorUniversalIdentifier(), user)) {
609             throw buildAuthorizationException("annotate", document);
610         }
611 
612         if (attachment != null && attachment.isComplete()) {
613             getAttachmentService().deleteAttachmentContents(attachment);
614         }
615 
616         // if document is not saved there is no need to delete the note (it is not persisted)
617         if (!document.getDocumentHeader().getWorkflowDocument().isInitiated()) {
618             getNoteService().deleteNote(note);
619         }
620 
621         return getCollectionControllerService().deleteLine(form);
622     }
623 
624     /**
625      * Retrieves a note attachment by either the line index of the note within the documents note collection, or
626      * by the note identifier.
627      *
628      * {@inheritDoc}
629      */
630     @Override
631     public ModelAndView downloadAttachment(DocumentFormBase form, HttpServletResponse response) {
632         Attachment attachment = null;
633 
634         String selectedLineIndex = form.getActionParamaterValue(UifParameters.SELECTED_LINE_INDEX);
635         if (StringUtils.isNotBlank(selectedLineIndex)) {
636             Note note = form.getDocument().getNote(Integer.parseInt(selectedLineIndex));
637             attachment = note.getAttachment();
638         } else {
639             Long noteIdentifier = Long.valueOf(form.getActionParamaterValue(KRADConstants.NOTE_IDENTIFIER));
640             Note note = getNoteService().getNoteByNoteId(noteIdentifier);
641             if ((note != null) && (note.getAttachment() != null)) {
642                 attachment = note.getAttachment();
643 
644                 // make sure the reference back to note is set for the note service dependencies
645                 attachment.setNote(note);
646             }
647         }
648 
649         if (attachment == null) {
650             throw new RuntimeException("Unable to find attachment for action parameters passed.");
651         }
652 
653         try {
654             KRADUtils.addAttachmentToResponse(response, getAttachmentService().retrieveAttachmentContents(attachment),
655                     attachment.getAttachmentMimeTypeCode(), attachment.getAttachmentFileName(),
656                     attachment.getAttachmentFileSize().longValue());
657         } catch (IOException e) {
658             throw new RuntimeException("Unable to download note attachment", e);
659         }
660 
661         return null;
662     }
663 
664     /**
665      * {@inheritDoc}
666      */
667     @Override
668     public ModelAndView cancelAttachment(DocumentFormBase form) {
669         form.setAttachmentFile(null);
670 
671         return getModelAndViewService().getModelAndView(form);
672     }
673 
674     /**
675      * {@inheritDoc}
676      */
677     @Override
678     public ModelAndView superUserTakeActions(DocumentFormBase form) {
679         Document document = form.getDocument();
680 
681         if (StringUtils.isBlank(document.getSuperUserAnnotation())) {
682             GlobalVariables.getMessageMap().putErrorForSectionId(
683                     "Uif-SuperUserAnnotation", RiceKeyConstants.ERROR_SUPER_USER_TAKE_ACTIONS_MISSING);
684         }
685 
686         Set<String> selectedActionRequests = form.getSelectedCollectionLines().get(UifPropertyPaths.ACTION_REQUESTS);
687 
688         if (CollectionUtils.isEmpty(selectedActionRequests)) {
689             GlobalVariables.getMessageMap().putErrorForSectionId(
690                     "Uif-SuperUserActionRequests", RiceKeyConstants.ERROR_SUPER_USER_TAKE_ACTIONS_NONE_SELECTED);
691         }
692 
693         if (GlobalVariables.getMessageMap().hasErrors()) {
694             return getModelAndViewService().getModelAndView(form);
695         }
696 
697         List<ActionRequest> actionRequests = new ArrayList<ActionRequest>();
698         for (String selectedActionRequest : selectedActionRequests) {
699             ActionRequest actionRequest = ObjectPropertyUtils.getPropertyValue(document, selectedActionRequest);
700             actionRequests.add(actionRequest);
701         }
702 
703         for (ActionRequest actionRequest : actionRequests) {
704             if (StringUtils.equals(actionRequest.getActionRequested().getCode(), ActionRequestType.COMPLETE.getCode()) ||
705                     StringUtils.equals(actionRequest.getActionRequested().getCode(), ActionRequestType.APPROVE.getCode())) {
706                 document = getDocumentService().validateAndPersistDocument(document, new RouteDocumentEvent(document));
707                 form.setDocument(document);
708             }
709 
710             performSuperUserWorkflowAction(form, UifConstants.SuperUserWorkflowAction.TAKEACTION, actionRequest);
711         }
712 
713         document.setSuperUserAnnotation("");
714         form.getSelectedCollectionLines().remove(UifPropertyPaths.ACTION_REQUESTS);
715 
716         return getModelAndViewService().getModelAndView(form);
717     }
718 
719     /**
720      * {@inheritDoc}
721      */
722     @Override
723     public ModelAndView superUserApprove(DocumentFormBase form) {
724         Document document = form.getDocument();
725 
726         if (StringUtils.isBlank(document.getSuperUserAnnotation())) {
727             GlobalVariables.getMessageMap().putErrorForSectionId(
728                     "Uif-SuperUserAnnotation", RiceKeyConstants.ERROR_SUPER_USER_APPROVE_MISSING);
729         }
730 
731         Set<String> selectedCollectionLines = form.getSelectedCollectionLines().get(UifPropertyPaths.ACTION_REQUESTS);
732 
733         if (!CollectionUtils.isEmpty(selectedCollectionLines)) {
734             GlobalVariables.getMessageMap().putErrorForSectionId(
735                     "Uif-SuperUserActionRequests", RiceKeyConstants.ERROR_SUPER_USER_APPROVE_ACTIONS_CHECKED);
736         }
737 
738         if (GlobalVariables.getMessageMap().hasErrors()) {
739             return getModelAndViewService().getModelAndView(form);
740         }
741 
742         performSuperUserWorkflowAction(form, UifConstants.SuperUserWorkflowAction.APPROVE);
743 
744         return getModelAndViewService().getModelAndView(form);
745     }
746 
747     /**
748      * {@inheritDoc}
749      */
750     @Override
751     public ModelAndView superUserDisapprove(DocumentFormBase form) {
752         Document document = form.getDocument();
753 
754         if (StringUtils.isBlank(document.getSuperUserAnnotation())) {
755             GlobalVariables.getMessageMap().putErrorForSectionId(
756                     "Uif-SuperUserAnnotation", RiceKeyConstants.ERROR_SUPER_USER_DISAPPROVE_MISSING);
757         }
758 
759         Set<String> selectedCollectionLines = form.getSelectedCollectionLines().get(UifPropertyPaths.ACTION_REQUESTS);
760 
761         if (!CollectionUtils.isEmpty(selectedCollectionLines)) {
762             GlobalVariables.getMessageMap().putErrorForSectionId(
763                     "Uif-SuperUserActionRequests", RiceKeyConstants.ERROR_SUPER_USER_DISAPPROVE_ACTIONS_CHECKED);
764         }
765 
766         if (GlobalVariables.getMessageMap().hasErrors()) {
767             return getModelAndViewService().getModelAndView(form);
768         }
769 
770         performSuperUserWorkflowAction(form, UifConstants.SuperUserWorkflowAction.DISAPPROVE);
771 
772         return getModelAndViewService().getModelAndView(form);
773     }
774 
775     /**
776      * {@inheritDoc}
777      */
778     @Override
779     public void performWorkflowAction(DocumentFormBase form, UifConstants.WorkflowAction action) {
780         performWorkflowAction(form, action, null);
781     }
782 
783     /**
784      * {@inheritDoc}
785      */
786     @Override
787     public void performWorkflowAction(DocumentFormBase form, UifConstants.WorkflowAction action,
788             DocumentEvent documentEvent) {
789         Document document = form.getDocument();
790 
791         if (LOG.isDebugEnabled()) {
792             LOG.debug("Performing workflow action " + action.name() + "for document: " + document.getDocumentNumber());
793         }
794 
795         // evaluate flags on save only if we are saving for the first time (transitioning to saved status)
796         if (!UifConstants.WorkflowAction.SAVE.equals(action) || document.getDocumentHeader().getWorkflowDocument()
797                 .isInitiated()) {
798             form.setEvaluateFlagsAndModes(true);
799             form.setCanEditView(null);
800         }
801 
802         try {
803             String successMessageKey = null;
804             switch (action) {
805                 case SAVE:
806                     if (documentEvent == null) {
807                         document = getDocumentService().saveDocument(document);
808                     } else {
809                         document = getDocumentService().saveDocument(document, documentEvent);
810                     }
811 
812                     successMessageKey = RiceKeyConstants.MESSAGE_SAVED;
813                     break;
814                 case ROUTE:
815                     document = getDocumentService().routeDocument(document, form.getAnnotation(),
816                             combineAdHocRecipients(form));
817                     successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_SUCCESSFUL;
818                     break;
819                 case BLANKETAPPROVE:
820                     document = getDocumentService().blanketApproveDocument(document, form.getAnnotation(),
821                             combineAdHocRecipients(form));
822                     successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_APPROVED;
823                     break;
824                 case APPROVE:
825                     document = getDocumentService().approveDocument(document, form.getAnnotation(),
826                             combineAdHocRecipients(form));
827                     successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_APPROVED;
828                     break;
829                 case DISAPPROVE:
830                     String disapprovalNoteText = generateDisapprovalNote(form);
831                     document = getDocumentService().disapproveDocument(document, disapprovalNoteText);
832                     successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_DISAPPROVED;
833                     break;
834                 case FYI:
835                     document = getDocumentService().clearDocumentFyi(document, combineAdHocRecipients(form));
836                     successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_FYIED;
837                     break;
838                 case ACKNOWLEDGE:
839                     document = getDocumentService().acknowledgeDocument(document, form.getAnnotation(),
840                             combineAdHocRecipients(form));
841                     successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_ACKNOWLEDGED;
842                     break;
843                 case CANCEL:
844                     if (getDocumentService().documentExists(document.getDocumentNumber())) {
845                         document = getDocumentService().cancelDocument(document, form.getAnnotation());
846                         successMessageKey = RiceKeyConstants.MESSAGE_CANCELLED;
847                     }
848                     break;
849                 case COMPLETE:
850                     if (getDocumentService().documentExists(document.getDocumentNumber())) {
851                         document = getDocumentService().completeDocument(document, form.getAnnotation(),
852                                 combineAdHocRecipients(form));
853                         successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_SUCCESSFUL;
854                     }
855                     break;
856                 case SENDADHOCREQUESTS:
857                     getDocumentService().sendAdHocRequests(document, form.getAnnotation(), combineAdHocRecipients(
858                             form));
859                     successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_SUCCESSFUL;
860                     break;
861                 case RECALL:
862                     if (getDocumentService().documentExists(document.getDocumentNumber())) {
863                         String recallExplanation = form.getDialogExplanations().get(
864                                 KRADConstants.QUESTION_ACTION_RECALL_REASON);
865                         document = getDocumentService().recallDocument(document, recallExplanation, true);
866                         successMessageKey = RiceKeyConstants.MESSAGE_ROUTE_RECALLED;
867                     }
868                     break;
869             }
870 
871             // push potentially updated document back into the form
872             form.setDocument(document);
873 
874             if (successMessageKey != null) {
875                 GlobalVariables.getMessageMap().putInfo(KRADConstants.GLOBAL_MESSAGES, successMessageKey);
876             }
877         } catch (ValidationException e) {
878             // log the error and swallow exception so screen will draw with errors.
879             // we don't want the exception to bubble up and the user to see an incident page, but instead just return to
880             // the page and display the actual errors. This would need a fix to the API at some point.
881             KRADUtils.logErrors();
882             LOG.error("Validation Exception occured for document :" + document.getDocumentNumber(), e);
883 
884             // if no errors in map then throw runtime because something bad happened
885             if (GlobalVariables.getMessageMap().hasNoErrors()) {
886                 throw new RiceRuntimeException("Validation Exception with no error message.", e);
887             }
888         } catch (Exception e) {
889             throw new RiceRuntimeException(
890                     "Exception trying to invoke action " + action.name() + " for document: " + document
891                             .getDocumentNumber(), e);
892         }
893 
894         form.setAnnotation("");
895     }
896 
897     /**
898      * Convenience method to combine the two lists of ad hoc recipients into one which should be done before
899      * calling any of the document service methods that expect a list of ad hoc recipients.
900      *
901      * @param form document form instance containing the ad hod lists
902      * @return List<AdHocRouteRecipient> combined ad hoc recipients
903      */
904     protected List<AdHocRouteRecipient> combineAdHocRecipients(DocumentFormBase form) {
905         Document document = form.getDocument();
906 
907         List<AdHocRouteRecipient> adHocRecipients = new ArrayList<AdHocRouteRecipient>();
908         adHocRecipients.addAll(document.getAdHocRoutePersons());
909         adHocRecipients.addAll(document.getAdHocRouteWorkgroups());
910 
911         return adHocRecipients;
912     }
913 
914     /**
915      * {@inheritDoc}
916      */
917     @Override
918     public void performSuperUserWorkflowAction(DocumentFormBase form, UifConstants.SuperUserWorkflowAction action) {
919         performSuperUserWorkflowAction(form, action, null);
920     }
921 
922     /**
923      * {@inheritDoc}
924      */
925     @Override
926     public void performSuperUserWorkflowAction(DocumentFormBase form, UifConstants.SuperUserWorkflowAction action,
927             ActionRequest actionRequest) {
928         Document document = form.getDocument();
929 
930         if (LOG.isDebugEnabled()) {
931             LOG.debug("Performing super user workflow action " + action.name() + "for document: " + document.getDocumentNumber());
932         }
933 
934         try {
935             String documentTypeId = document.getDocumentHeader().getWorkflowDocument().getDocumentTypeId();
936             String documentNumber = document.getDocumentNumber();
937             String principalId = GlobalVariables.getUserSession().getPrincipalId();
938             String superUserAnnotation = document.getSuperUserAnnotation();
939 
940             WorkflowDocumentActionsService documentActions = getWorkflowDocumentActionsService(documentTypeId);
941             DocumentActionParameters parameters = DocumentActionParameters.create(documentNumber, principalId, superUserAnnotation);
942 
943             String successMessageKey = null;
944             switch (action) {
945                 case TAKEACTION:
946                     if (actionRequest != null) {
947                         documentActions.superUserTakeRequestedAction(parameters, true, actionRequest.getId());
948 
949                         String actionRequestedCode = actionRequest.getActionRequested().getCode();
950                         if (StringUtils.equals(actionRequestedCode, ActionRequestType.ACKNOWLEDGE.getCode())) {
951                             successMessageKey = RiceKeyConstants.MESSAGE_SUPER_USER_ACTION_REQUEST_ACKNOWLEDGED;
952                         } else if (StringUtils.equals(actionRequestedCode, ActionRequestType.FYI.getCode())) {
953                             successMessageKey = RiceKeyConstants.MESSAGE_SUPER_USER_ACTION_REQUEST_FYIED;
954                         } else if (StringUtils.equals(actionRequestedCode, ActionRequestType.COMPLETE.getCode())) {
955                             successMessageKey = RiceKeyConstants.MESSAGE_SUPER_USER_ACTION_REQUEST_COMPLETED;
956                         } else if (StringUtils.equals(actionRequestedCode, ActionRequestType.APPROVE.getCode())) {
957                             successMessageKey = RiceKeyConstants.MESSAGE_SUPER_USER_ACTION_REQUEST_APPROVED;
958                         } else {
959                             successMessageKey = RiceKeyConstants.MESSAGE_SUPER_USER_ACTION_REQUEST_APPROVED;
960                         }
961                     }
962                     break;
963                 case APPROVE:
964                     documentActions.superUserBlanketApprove(parameters, true);
965 
966                     successMessageKey = RiceKeyConstants.MESSAGE_SUPER_USER_APPROVED;
967                     break;
968                 case DISAPPROVE:
969                     documentActions.superUserDisapprove(parameters, true);
970 
971                     successMessageKey = RiceKeyConstants.MESSAGE_SUPER_USER_DISAPPROVED;
972                     break;
973             }
974 
975             form.setEvaluateFlagsAndModes(true);
976             form.setCanEditView(null);
977 
978             if (successMessageKey != null) {
979                 if (actionRequest != null) {
980                     GlobalVariables.getMessageMap().putInfo(KRADConstants.GLOBAL_MESSAGES, successMessageKey,
981                             document.getDocumentNumber(), actionRequest.getId());
982                 } else {
983                     GlobalVariables.getMessageMap().putInfo(KRADConstants.GLOBAL_MESSAGES, successMessageKey,
984                             document.getDocumentNumber());
985                 }
986             }
987         } catch (ValidationException e) {
988             // log the error and swallow exception so screen will draw with errors.
989             // we don't want the exception to bubble up and the user to see an incident page, but instead just return to
990             // the page and display the actual errors. This would need a fix to the API at some point.
991             KRADUtils.logErrors();
992             LOG.error("Validation Exception occured for document :" + document.getDocumentNumber(), e);
993 
994             // if no errors in map then throw runtime because something bad happened
995             if (GlobalVariables.getMessageMap().hasNoErrors()) {
996                 throw new RiceRuntimeException("Validation Exception with no error message.", e);
997             }
998         } catch (Exception e) {
999             throw new RiceRuntimeException(
1000                     "Exception trying to invoke action " + action.name() + " for document: " + document
1001                             .getDocumentNumber(), e);
1002         }
1003 
1004         document.setSuperUserAnnotation("");
1005     }
1006 
1007     /**
1008      * Helper method to get the correct {@link WorkflowDocumentActionsService} from the {@code applicationId} of the
1009      * document type.
1010      *
1011      * @param documentTypeId the document type to get the application id from
1012      *
1013      * @return the correct {@link WorkflowDocumentActionsService} from the {@code applicationId} of the document type
1014      */
1015     protected WorkflowDocumentActionsService getWorkflowDocumentActionsService(String documentTypeId) {
1016         DocumentType documentType = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeById(documentTypeId);
1017         String applicationId = documentType.getApplicationId();
1018         QName serviceName = new QName(KewApiConstants.Namespaces.KEW_NAMESPACE_2_0,
1019                 KewApiConstants.ServiceNames.WORKFLOW_DOCUMENT_ACTIONS_SERVICE_SOAP);
1020 
1021         WorkflowDocumentActionsService service = (WorkflowDocumentActionsService) KsbApiServiceLocator.getServiceBus()
1022                 .getService(serviceName, applicationId);
1023 
1024         if (service == null) {
1025             service = KewApiServiceLocator.getWorkflowDocumentActionsService();
1026         }
1027 
1028         return service;
1029     }
1030 
1031     /**
1032      * Helper method to check if sensitive data is present in a given string and dialog display.
1033      *
1034      * <p>If the string is sensitive we want to return a dialog box to make sure user wants to continue,
1035      * else we just return null</p>
1036      *
1037      * @param field the string to check for sensitive data
1038      * @param form the form to add the dialog to
1039      * @return the model and view for the dialog or null if there isn't one
1040      */
1041     protected ModelAndView checkSensitiveDataAndWarningDialog(String field, UifFormBase form) {
1042         boolean hasSensitiveData = KRADUtils.containsSensitiveDataPatternMatch(field);
1043         Boolean warnForSensitiveData = getParameterService().getParameterValueAsBoolean(KRADConstants.KNS_NAMESPACE,
1044                 ParameterConstants.ALL_COMPONENT,
1045                 KRADConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS_WARNING_IND);
1046 
1047         // if there is sensitive data and the flag to warn for sensitive data is set,
1048         // then we want a dialog returned if there is not already one
1049         if (hasSensitiveData && warnForSensitiveData.booleanValue()) {
1050             DialogResponse sensitiveDataDialogResponse = form.getDialogResponse(SENSITIVE_DATA_DIALOG);
1051 
1052             if (sensitiveDataDialogResponse == null) {
1053                 // no sensitive data dialog found, so create one on the form and return it
1054                 return getModelAndViewService().showDialog(SENSITIVE_DATA_DIALOG, true, form);
1055             }
1056         }
1057 
1058         return null;
1059     }
1060 
1061     /**
1062      * Convenience method for building document authorization exceptions.
1063      *
1064      * @param action the action that was requested
1065      * @param document document instance the action was requested for
1066      */
1067     protected DocumentAuthorizationException buildAuthorizationException(String action, Document document) {
1068         return new DocumentAuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(),
1069                 action, document.getDocumentNumber());
1070     }
1071 
1072     protected LegacyDataAdapter getLegacyDataAdapter() {
1073         if (this.legacyDataAdapter == null) {
1074             this.legacyDataAdapter = KRADServiceLocatorWeb.getLegacyDataAdapter();
1075         }
1076         return this.legacyDataAdapter;
1077     }
1078 
1079     public void setLegacyDataAdapter(LegacyDataAdapter legacyDataAdapter) {
1080         this.legacyDataAdapter = legacyDataAdapter;
1081     }
1082 
1083     protected DataDictionaryService getDataDictionaryService() {
1084         if (this.dataDictionaryService == null) {
1085             this.dataDictionaryService = KRADServiceLocatorWeb.getDataDictionaryService();
1086         }
1087         return this.dataDictionaryService;
1088     }
1089 
1090     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
1091         this.dataDictionaryService = dataDictionaryService;
1092     }
1093 
1094     protected DocumentService getDocumentService() {
1095         if (this.documentService == null) {
1096             this.documentService = KRADServiceLocatorWeb.getDocumentService();
1097         }
1098         return this.documentService;
1099     }
1100 
1101     public void setDocumentService(DocumentService documentService) {
1102         this.documentService = documentService;
1103     }
1104 
1105     protected DocumentDictionaryService getDocumentDictionaryService() {
1106         if (this.documentDictionaryService == null) {
1107             this.documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService();
1108         }
1109         return this.documentDictionaryService;
1110     }
1111 
1112     public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) {
1113         this.documentDictionaryService = documentDictionaryService;
1114     }
1115 
1116     protected AttachmentService getAttachmentService() {
1117         if (attachmentService == null) {
1118             attachmentService = KRADServiceLocator.getAttachmentService();
1119         }
1120         return this.attachmentService;
1121     }
1122 
1123     public void setAttachmentService(AttachmentService attachmentService) {
1124         this.attachmentService = attachmentService;
1125     }
1126 
1127     protected NoteService getNoteService() {
1128         if (noteService == null) {
1129             noteService = KRADServiceLocator.getNoteService();
1130         }
1131 
1132         return this.noteService;
1133     }
1134 
1135     public void setNoteService(NoteService noteService) {
1136         this.noteService = noteService;
1137     }
1138 
1139     protected ModelAndViewService getModelAndViewService() {
1140         return modelAndViewService;
1141     }
1142 
1143     public void setModelAndViewService(ModelAndViewService modelAndViewService) {
1144         this.modelAndViewService = modelAndViewService;
1145     }
1146 
1147     protected NavigationControllerService getNavigationControllerService() {
1148         return navigationControllerService;
1149     }
1150 
1151     public void setNavigationControllerService(NavigationControllerService navigationControllerService) {
1152         this.navigationControllerService = navigationControllerService;
1153     }
1154 
1155     protected ConfigurationService getConfigurationService() {
1156         return configurationService;
1157     }
1158 
1159     public void setConfigurationService(ConfigurationService configurationService) {
1160         this.configurationService = configurationService;
1161     }
1162 
1163     protected CollectionControllerService getCollectionControllerService() {
1164         return collectionControllerService;
1165     }
1166 
1167     public void setCollectionControllerService(CollectionControllerService collectionControllerService) {
1168         this.collectionControllerService = collectionControllerService;
1169     }
1170 
1171     protected ParameterService getParameterService() {
1172         return parameterService;
1173     }
1174 
1175     public void setParameterService(ParameterService parameterService) {
1176         this.parameterService = parameterService;
1177     }
1178 }