View Javadoc

1   /**
2    * Copyright 2005-2015 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.maintenance;
17  
18  import org.apache.commons.collections.CollectionUtils;
19  import org.apache.commons.lang.StringUtils;
20  import org.apache.ojb.broker.core.proxy.ProxyHelper;
21  import org.kuali.rice.core.api.config.property.ConfigContext;
22  import org.kuali.rice.core.api.util.RiceKeyConstants;
23  import org.kuali.rice.kew.api.KewApiServiceLocator;
24  import org.kuali.rice.kew.api.WorkflowDocument;
25  import org.kuali.rice.kew.api.doctype.DocumentType;
26  import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
27  import org.kuali.rice.kim.api.identity.Person;
28  import org.kuali.rice.krad.bo.DocumentAttachment;
29  import org.kuali.rice.krad.bo.DocumentHeader;
30  import org.kuali.rice.krad.bo.GlobalBusinessObject;
31  import org.kuali.rice.krad.bo.MultiDocumentAttachment;
32  import org.kuali.rice.krad.bo.Note;
33  import org.kuali.rice.krad.bo.PersistableAttachment;
34  import org.kuali.rice.krad.bo.PersistableAttachmentList;
35  import org.kuali.rice.krad.bo.PersistableBusinessObject;
36  import org.kuali.rice.krad.datadictionary.DocumentEntry;
37  import org.kuali.rice.krad.datadictionary.WorkflowAttributes;
38  import org.kuali.rice.krad.datadictionary.WorkflowProperties;
39  import org.kuali.rice.krad.document.DocumentBase;
40  import org.kuali.rice.krad.document.SessionDocument;
41  import org.kuali.rice.krad.exception.PessimisticLockingException;
42  import org.kuali.rice.krad.exception.ValidationException;
43  import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
44  import org.kuali.rice.krad.rules.rule.event.SaveDocumentEvent;
45  import org.kuali.rice.krad.service.DocumentDictionaryService;
46  import org.kuali.rice.krad.service.DocumentHeaderService;
47  import org.kuali.rice.krad.service.DocumentService;
48  import org.kuali.rice.krad.service.KRADServiceLocator;
49  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
50  import org.kuali.rice.krad.service.MaintenanceDocumentService;
51  import org.kuali.rice.krad.util.GlobalVariables;
52  import org.kuali.rice.krad.util.KRADConstants;
53  import org.kuali.rice.krad.util.NoteType;
54  import org.kuali.rice.krad.util.ObjectUtils;
55  import org.kuali.rice.krad.util.documentserializer.PropertySerializabilityEvaluator;
56  import org.w3c.dom.Document;
57  import org.w3c.dom.Node;
58  import org.w3c.dom.NodeList;
59  import org.xml.sax.InputSource;
60  import org.xml.sax.SAXException;
61  
62  import javax.persistence.CascadeType;
63  import javax.persistence.Column;
64  import javax.persistence.Entity;
65  import javax.persistence.FetchType;
66  import javax.persistence.JoinColumn;
67  import javax.persistence.ManyToMany;
68  import javax.persistence.ManyToOne;
69  import javax.persistence.Table;
70  import javax.persistence.Transient;
71  import javax.xml.parsers.DocumentBuilder;
72  import javax.xml.parsers.DocumentBuilderFactory;
73  import javax.xml.parsers.ParserConfigurationException;
74  import java.io.IOException;
75  import java.io.StringReader;
76  import java.util.ArrayList;
77  import java.util.Arrays;
78  import java.util.Collections;
79  import java.util.List;
80  
81  /**
82   * Document class for all maintenance documents which wraps the maintenance object in
83   * a <code>Maintainable</code> that is also used for various callbacks
84   *
85   * <p>
86   * The maintenance xml structure will be: <maintainableDocumentContents maintainableImplClass="className">
87   * <oldMaintainableObject>... </oldMaintainableObject> <newMaintainableObject>... </newMaintainableObject>
88   * </maintainableDocumentContents> Maintenance Document
89   * </p>
90   */
91  @Entity
92  @Table(name = "KRNS_MAINT_DOC_T")
93  public class MaintenanceDocumentBase extends DocumentBase implements MaintenanceDocument, SessionDocument {
94      private static final long serialVersionUID = -505085142412593305L;
95      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(MaintenanceDocumentBase.class);
96  
97      public static final String MAINTAINABLE_IMPL_CLASS = "maintainableImplClass";
98      public static final String OLD_MAINTAINABLE_TAG_NAME = "oldMaintainableObject";
99      public static final String NEW_MAINTAINABLE_TAG_NAME = "newMaintainableObject";
100     public static final String MAINTENANCE_ACTION_TAG_NAME = "maintenanceAction";
101     public static final String NOTES_TAG_NAME = "notes";
102 
103     @Transient
104     private static transient DocumentDictionaryService documentDictionaryService;
105     @Transient
106     private static transient MaintenanceDocumentService maintenanceDocumentService;
107     @Transient
108     private static transient DocumentHeaderService documentHeaderService;
109     @Transient
110     private static transient DocumentService documentService;
111 
112     @Transient
113     protected Maintainable oldMaintainableObject;
114     @Transient
115     protected Maintainable newMaintainableObject;
116 
117     @Column(name = "DOC_CNTNT", length = 4096)
118     protected String xmlDocumentContents;
119     @Transient
120     protected boolean fieldsClearedOnCopy;
121     @Transient
122     protected boolean displayTopicFieldInNotes = false;
123     @Transient
124     protected String attachmentPropertyName;
125     @Transient
126     protected String attachmentListPropertyName;
127     @Transient
128     protected String attachmentCollectionName;
129 
130     @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
131     @JoinColumn(name = "DOC_HDR_ID", insertable = false, updatable = false)
132     protected DocumentAttachment attachment;
133 
134     @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
135     @JoinColumn(name = "DOC_HDR_ID", insertable = false, updatable = false)
136     protected List<MultiDocumentAttachment> attachments;
137 
138     public String getAttachmentPropertyName() {
139         return this.attachmentPropertyName;
140     }
141 
142     public void setAttachmentPropertyName(String attachmentPropertyName) {
143         this.attachmentPropertyName = attachmentPropertyName;
144     }
145 
146     public String getAttachmentListPropertyName() {
147         return this.attachmentListPropertyName;
148     }
149 
150     public void setAttachmentListPropertyName(String attachmentListPropertyName) {
151         this.attachmentListPropertyName = attachmentListPropertyName;
152     }
153 
154     public String getAttachmentCollectionName() {
155         return this.attachmentCollectionName;
156     }
157 
158     public void setAttachmentCollectionName(String attachmentCollectionName) {
159         this.attachmentCollectionName = attachmentCollectionName;
160     }
161 
162     public MaintenanceDocumentBase() {
163         super();
164         fieldsClearedOnCopy = false;
165     }
166 
167     /**
168      * Initializies the maintainables.
169      */
170     public MaintenanceDocumentBase(String documentTypeName) {
171         this();
172         Class clazz = getDocumentDictionaryService().getMaintainableClass(documentTypeName);
173         try {
174             oldMaintainableObject = (Maintainable) clazz.newInstance();
175             newMaintainableObject = (Maintainable) clazz.newInstance();
176 
177             // initialize maintainable with a data object
178             Class<?> dataObjectClazz = getDocumentDictionaryService().getMaintenanceDataObjectClass(documentTypeName);
179             oldMaintainableObject.setDataObject(dataObjectClazz.newInstance());
180             oldMaintainableObject.setDataObjectClass(dataObjectClazz);
181             newMaintainableObject.setDataObject(dataObjectClazz.newInstance());
182             newMaintainableObject.setDataObjectClass(dataObjectClazz);
183         } catch (InstantiationException e) {
184             LOG.error("Unable to initialize maintainables of type " + clazz.getName());
185             throw new RuntimeException("Unable to initialize maintainables of type " + clazz.getName());
186         } catch (IllegalAccessException e) {
187             LOG.error("Unable to initialize maintainables of type " + clazz.getName());
188             throw new RuntimeException("Unable to initialize maintainables of type " + clazz.getName());
189         }
190     }
191 
192     /**
193      * Builds out the document title for maintenance documents - this will get loaded into the flex doc and passed into
194      * workflow. It will be searchable.
195      */
196     @Override
197     public String getDocumentTitle() {
198         String documentTitle = "";
199 
200         documentTitle = newMaintainableObject.getDocumentTitle(this);
201         if (StringUtils.isNotBlank(documentTitle)) {
202             // if doc title has been overridden by maintainable, use it
203             return documentTitle;
204         }
205 
206         // TODO - build out with bo label once we get the data dictionary stuff in place
207         // build out the right classname
208         String className = newMaintainableObject.getDataObject().getClass().getName();
209         String truncatedClassName = className.substring(className.lastIndexOf('.') + 1);
210         if (isOldDataObjectInDocument()) {
211             documentTitle = "Edit ";
212         } else {
213             documentTitle = "New ";
214         }
215         documentTitle += truncatedClassName + " - ";
216         documentTitle += this.getDocumentHeader().getDocumentDescription() + " ";
217         return documentTitle;
218     }
219 
220     /**
221      * @param xmlDocument
222      * @return
223      */
224     protected boolean isOldMaintainableInDocument(Document xmlDocument) {
225         boolean isOldMaintainableInExistence = false;
226         if (xmlDocument.getElementsByTagName(OLD_MAINTAINABLE_TAG_NAME).getLength() > 0) {
227             isOldMaintainableInExistence = true;
228         }
229         return isOldMaintainableInExistence;
230     }
231 
232     /**
233      * Checks old maintainable bo has key values
234      */
235     @Override
236     public boolean isOldDataObjectInDocument() {
237         boolean isOldBusinessObjectInExistence = false;
238         if (oldMaintainableObject == null || oldMaintainableObject.getDataObject() == null) {
239             isOldBusinessObjectInExistence = false;
240         } else {
241             isOldBusinessObjectInExistence = oldMaintainableObject.isOldDataObjectInDocument();
242         }
243         return isOldBusinessObjectInExistence;
244     }
245 
246     /**
247      * This method is a simplified-naming wrapper around isOldDataObjectInDocument(), so that the method name
248      * matches the functionality.
249      */
250     @Override
251     public boolean isNew() {
252         return MaintenanceUtils.isMaintenanceDocumentCreatingNewRecord(newMaintainableObject.getMaintenanceAction());
253     }
254 
255     /**
256      * This method is a simplified-naming wrapper around isOldDataObjectInDocument(), so that the method name
257      * matches the functionality.
258      */
259     @Override
260     public boolean isEdit() {
261         if (KRADConstants.MAINTENANCE_EDIT_ACTION.equalsIgnoreCase(newMaintainableObject.getMaintenanceAction())) {
262             return true;
263         } else {
264             return false;
265         }
266         // return isOldDataObjectInDocument();
267     }
268 
269     @Override
270     public boolean isNewWithExisting() {
271         if (KRADConstants.MAINTENANCE_NEWWITHEXISTING_ACTION
272                 .equalsIgnoreCase(newMaintainableObject.getMaintenanceAction())) {
273             return true;
274         } else {
275             return false;
276         }
277     }
278 
279     @Override
280     public void populateMaintainablesFromXmlDocumentContents() {
281         // get a hold of the parsed xml document, then read the classname,
282         // then instantiate one to two instances depending on content
283         // then populate those instances
284         if (!StringUtils.isEmpty(xmlDocumentContents)) {
285             DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
286             try {
287                 DocumentBuilder builder = factory.newDocumentBuilder();
288                 Document xmlDocument = builder.parse(new InputSource(new StringReader(xmlDocumentContents)));
289                 String clazz = xmlDocument.getDocumentElement().getAttribute(MAINTAINABLE_IMPL_CLASS);
290                 if (isOldMaintainableInDocument(xmlDocument)) {
291                     oldMaintainableObject = (Maintainable) Class.forName(clazz).newInstance();
292                     Object dataObject = getDataObjectFromXML(OLD_MAINTAINABLE_TAG_NAME);
293 
294                     String oldMaintenanceAction = getMaintenanceAction(xmlDocument, OLD_MAINTAINABLE_TAG_NAME);
295                     oldMaintainableObject.setMaintenanceAction(oldMaintenanceAction);
296 
297                     oldMaintainableObject.setDataObject(dataObject);
298                     oldMaintainableObject.setDataObjectClass(dataObject.getClass());
299                 }
300                 newMaintainableObject = (Maintainable) Class.forName(clazz).newInstance();
301                 Object bo = getDataObjectFromXML(NEW_MAINTAINABLE_TAG_NAME);
302                 newMaintainableObject.setDataObject(bo);
303                 newMaintainableObject.setDataObjectClass(bo.getClass());
304 
305                 String newMaintenanceAction = getMaintenanceAction(xmlDocument, NEW_MAINTAINABLE_TAG_NAME);
306                 newMaintainableObject.setMaintenanceAction(newMaintenanceAction);
307 
308                 if (newMaintainableObject.isNotesEnabled()) {
309                     List<Note> notes = getNotesFromXml(NOTES_TAG_NAME);
310                     setNotes(notes);
311                 }
312             } catch (ParserConfigurationException e) {
313                 LOG.error("Error while parsing document contents", e);
314                 throw new RuntimeException("Could not load document contents from xml", e);
315             } catch (SAXException e) {
316                 LOG.error("Error while parsing document contents", e);
317                 throw new RuntimeException("Could not load document contents from xml", e);
318             } catch (IOException e) {
319                 LOG.error("Error while parsing document contents", e);
320                 throw new RuntimeException("Could not load document contents from xml", e);
321             } catch (InstantiationException e) {
322                 LOG.error("Error while parsing document contents", e);
323                 throw new RuntimeException("Could not load document contents from xml", e);
324             } catch (IllegalAccessException e) {
325                 LOG.error("Error while parsing document contents", e);
326                 throw new RuntimeException("Could not load document contents from xml", e);
327             } catch (ClassNotFoundException e) {
328                 LOG.error("Error while parsing document contents", e);
329                 throw new RuntimeException("Could not load document contents from xml", e);
330             }
331         }
332     }
333 
334     /**
335      * This method is a lame containment of ugly DOM walking code. This is ONLY necessary because of the version
336      * conflicts between Xalan.jar in 2.6.x and 2.7. As soon as we can upgrade to 2.7, this will be switched to using
337      * XPath, which is faster and much easier on the eyes.
338      *
339      * @param xmlDocument
340      * @param oldOrNewElementName - String oldMaintainableObject or newMaintainableObject
341      * @return the value of the element, or null if none was there
342      */
343     protected String getMaintenanceAction(Document xmlDocument, String oldOrNewElementName) {
344 
345         if (StringUtils.isBlank(oldOrNewElementName)) {
346             throw new IllegalArgumentException("oldOrNewElementName may not be blank, null, or empty-string.");
347         }
348 
349         String maintenanceAction = null;
350         NodeList rootChildren = xmlDocument.getDocumentElement().getChildNodes();
351         for (int i = 0; i < rootChildren.getLength(); i++) {
352             Node rootChild = rootChildren.item(i);
353             if (oldOrNewElementName.equalsIgnoreCase(rootChild.getNodeName())) {
354                 NodeList maintChildren = rootChild.getChildNodes();
355                 for (int j = 0; j < maintChildren.getLength(); j++) {
356                     Node maintChild = maintChildren.item(j);
357                     if (MAINTENANCE_ACTION_TAG_NAME.equalsIgnoreCase(maintChild.getNodeName())) {
358                         maintenanceAction = maintChild.getChildNodes().item(0).getNodeValue();
359                     }
360                 }
361             }
362         }
363         return maintenanceAction;
364     }
365 
366     private List<Note> getNotesFromXml(String notesTagName) {
367         String notesXml =
368                 StringUtils.substringBetween(xmlDocumentContents, "<" + notesTagName + ">", "</" + notesTagName + ">");
369         if (StringUtils.isBlank(notesXml)) {
370             return Collections.emptyList();
371         }
372         List<Note> notes = (List<Note>) KRADServiceLocator.getXmlObjectSerializerService().fromXml(notesXml);
373         if (notes == null) {
374             return Collections.emptyList();
375         }
376         return notes;
377     }
378 
379     /**
380      * Retrieves substring of document contents from maintainable tag name. Then use xml service to translate xml into
381      * a
382      * business object.
383      */
384     protected Object getDataObjectFromXML(String maintainableTagName) {
385         String maintXml = StringUtils.substringBetween(xmlDocumentContents, "<" + maintainableTagName + ">",
386                 "</" + maintainableTagName + ">");
387 
388         boolean ignoreMissingFields = false;
389         String classAndDocTypeNames = ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.Config.IGNORE_MISSIONG_FIELDS_ON_DESERIALIZE);
390         if (!StringUtils.isEmpty(classAndDocTypeNames)) {
391             String classNameOnXML = StringUtils.substringBetween(xmlDocumentContents, "<" + maintainableTagName + "><", ">");
392             String classNamesNoSpaces = removeSpacesAround(classAndDocTypeNames);
393             List<String> classAndDocTypeNamesList = Arrays.asList(org.apache.commons.lang.StringUtils.split(classNamesNoSpaces, ","));
394             String originalDocTypeId = getDocumentHeader().getWorkflowDocument().getDocumentTypeId();
395             DocumentType docType = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeById(originalDocTypeId);
396 
397             while (docType != null && !ignoreMissingFields) {
398                 for(String classNameOrDocTypeName : classAndDocTypeNamesList){
399                     if (docType.getName().equalsIgnoreCase(classNameOrDocTypeName) ||
400                         classNameOnXML.equalsIgnoreCase(classNameOrDocTypeName)) {
401                             ignoreMissingFields = true;
402                             break;
403                     }
404                 }
405                 if (!StringUtils.isEmpty(docType.getParentId())) {
406                     docType = KewApiServiceLocator.getDocumentTypeService().getDocumentTypeById(docType.getParentId());
407                 } else {
408                     docType = null;
409                 }
410             }
411         }
412         if (!ignoreMissingFields) {
413             return KRADServiceLocator.getXmlObjectSerializerService().fromXml(maintXml);
414         } else {
415             return KRADServiceLocator.getXmlObjectSerializerIgnoreMissingFieldsService().fromXml(maintXml);
416         }
417     }
418 
419     /**
420      * Removes the spaces around the elements on a csv list of elements.
421      * <p>
422      * A null input will return a null output.
423      * </p>
424      *
425      * @param csv a list of elements in csv format e.g. foo, bar, baz
426      * @return a list of elements in csv format without spaces e.g. foo,bar,baz
427      */
428     private String removeSpacesAround(String csv) {
429         if (csv == null) {
430             return null;
431         }
432 
433         final StringBuilder result = new StringBuilder();
434         for (final String value : csv.split(",")) {
435             if (!"".equals(value.trim())) {
436                 result.append(value.trim());
437                 result.append(",");
438             }
439         }
440 
441         //remove trailing comma
442         int i = result.lastIndexOf(",");
443         if (i != -1) {
444             result.deleteCharAt(i);
445         }
446 
447         return result.toString();
448     }
449 
450     /**
451      * Populates the xml document contents from the maintainables.
452      *
453      * @see MaintenanceDocument#populateXmlDocumentContentsFromMaintainables()
454      */
455     @Override
456     public void populateXmlDocumentContentsFromMaintainables() {
457         StringBuilder docContentBuffer = new StringBuilder();
458         docContentBuffer.append("<maintainableDocumentContents maintainableImplClass=\"")
459                 .append(newMaintainableObject.getClass().getName()).append("\">");
460 
461         // if business objects notes are enabled then we need to persist notes to the XML
462         if (getNewMaintainableObject().isNotesEnabled()) {
463             docContentBuffer.append("<" + NOTES_TAG_NAME + ">");
464             // copy notes to a non-ojb Proxied ArrayList to get rid of the usage of those proxies
465             // note: XmlObjectSerializerServiceImpl should be doing this for us but it does not
466             // appear to be working (at least in this case) and the xml comes through
467             // with the fully qualified ListProxyDefault class name from OJB embedded inside it.
468             List<Note> noteList = new ArrayList<Note>();
469             for (Note note : getNotes()) {
470                 noteList.add(note);
471             }
472             docContentBuffer.append(KRADServiceLocator.getXmlObjectSerializerService().toXml(noteList));
473             docContentBuffer.append("</" + NOTES_TAG_NAME + ">");
474         }
475         if (oldMaintainableObject != null && oldMaintainableObject.getDataObject() != null) {
476             // TODO: refactor this out into a method
477             docContentBuffer.append("<" + OLD_MAINTAINABLE_TAG_NAME + ">");
478 
479             Object oldBo = oldMaintainableObject.getDataObject();
480 
481             // hack to resolve XStream not dealing well with Proxies
482             if (oldBo instanceof PersistableBusinessObject) {
483                 ObjectUtils.materializeAllSubObjects((PersistableBusinessObject) oldBo);
484             }
485 
486             docContentBuffer.append(KRADServiceLocator.getBusinessObjectSerializerService()
487                     .serializeBusinessObjectToXml(oldBo));
488 
489             // add the maintainable's maintenanceAction
490             docContentBuffer.append("<" + MAINTENANCE_ACTION_TAG_NAME + ">");
491             docContentBuffer.append(oldMaintainableObject.getMaintenanceAction());
492             docContentBuffer.append("</" + MAINTENANCE_ACTION_TAG_NAME + ">\n");
493 
494             docContentBuffer.append("</" + OLD_MAINTAINABLE_TAG_NAME + ">");
495         }
496         docContentBuffer.append("<" + NEW_MAINTAINABLE_TAG_NAME + ">");
497 
498         Object newBo = newMaintainableObject.getDataObject();
499 
500         if (newBo instanceof PersistableBusinessObject) {
501             // hack to resolve XStream not dealing well with Proxies
502             ObjectUtils.materializeAllSubObjects((PersistableBusinessObject) newBo);
503         }
504 
505         docContentBuffer
506                 .append(KRADServiceLocator.getBusinessObjectSerializerService().serializeBusinessObjectToXml(newBo));
507 
508         // add the maintainable's maintenanceAction
509         docContentBuffer.append("<" + MAINTENANCE_ACTION_TAG_NAME + ">");
510         docContentBuffer.append(newMaintainableObject.getMaintenanceAction());
511         docContentBuffer.append("</" + MAINTENANCE_ACTION_TAG_NAME + ">\n");
512 
513         docContentBuffer.append("</" + NEW_MAINTAINABLE_TAG_NAME + ">");
514         docContentBuffer.append("</maintainableDocumentContents>");
515         xmlDocumentContents = docContentBuffer.toString();
516     }
517 
518     /**
519      * @see org.kuali.rice.krad.document.DocumentBase#doRouteStatusChange(org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange)
520      */
521     @Override
522     public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
523         super.doRouteStatusChange(statusChangeEvent);
524 
525         WorkflowDocument workflowDocument = getDocumentHeader().getWorkflowDocument();
526         getNewMaintainableObject().doRouteStatusChange(getDocumentHeader());
527         // commit the changes to the Maintainable BusinessObject when it goes to Processed (ie, fully approved),
528         // and also unlock it
529         if (workflowDocument.isProcessed()) {
530             String documentNumber = getDocumentHeader().getDocumentNumber();
531             newMaintainableObject.setDocumentNumber(documentNumber);
532 
533             //Populate Attachment Property
534             if (newMaintainableObject.getDataObject() instanceof PersistableAttachment) {
535                 populateAttachmentBeforeSave();
536             }
537 
538             //Populate Attachment Property
539             if (newMaintainableObject.getDataObject() instanceof PersistableAttachmentList) {
540                 populateBoAttachmentListBeforeSave();
541             }
542 
543 
544             newMaintainableObject.saveDataObject();
545 
546             if (!getDocumentService().saveDocumentNotes(this)) {
547                 throw new IllegalStateException(
548                         "Failed to save document notes, this means that the note target was not ready for notes to be attached when it should have been.");
549             }
550 
551             //Attachment should be deleted from Maintenance Document attachment table
552             deleteDocumentAttachment();
553             deleteDocumentAttachmentList();
554 
555             getMaintenanceDocumentService().deleteLocks(documentNumber);
556 
557             //for issue 3070, check if delete record
558             if (this.checkAllowsRecordDeletion() && this.checkMaintenanceAction() &&
559                     this.checkDeletePermission(newMaintainableObject.getDataObject())) {
560                 newMaintainableObject.deleteDataObject();
561             }
562         }
563 
564         // unlock the document when its canceled or disapproved or placed inException status
565         if (workflowDocument.isCanceled() || workflowDocument.isDisapproved() || workflowDocument.isRecalled() || workflowDocument.isException()) {
566             //Attachment should be deleted from Maintenance Document attachment table
567             deleteDocumentAttachment();
568             deleteDocumentAttachmentList();
569 
570             String documentNumber = getDocumentHeader().getDocumentNumber();
571             getMaintenanceDocumentService().deleteLocks(documentNumber);
572         }
573     }
574 
575     @Override
576     /**
577      * @see org.kuali.rice.krad.document.DocumentBase#getWorkflowEngineDocumentIdsToLock()
578      */
579     public List<String> getWorkflowEngineDocumentIdsToLock() {
580         if (newMaintainableObject != null) {
581             return newMaintainableObject.getWorkflowEngineDocumentIdsToLock();
582         }
583         return Collections.emptyList();
584     }
585 
586     /**
587      * Pre-Save hook.
588      *
589      * @see org.kuali.rice.krad.document.Document#prepareForSave()
590      */
591     @Override
592     public void prepareForSave() {
593         if (newMaintainableObject != null) {
594             newMaintainableObject.prepareForSave();
595         }
596     }
597 
598     /**
599      * @see org.kuali.rice.krad.document.DocumentBase#processAfterRetrieve()
600      */
601     @Override
602     public void processAfterRetrieve() {
603 
604         super.processAfterRetrieve();
605 
606         populateMaintainablesFromXmlDocumentContents();
607         if (oldMaintainableObject != null) {
608             oldMaintainableObject.setDocumentNumber(documentNumber);
609         }
610         if (newMaintainableObject != null) {
611             newMaintainableObject.setDocumentNumber(documentNumber);
612             newMaintainableObject.processAfterRetrieve();
613             if(newMaintainableObject.getDataObject() instanceof PersistableAttachment) {
614                 populateAttachmentForBO();
615             }
616             if(newMaintainableObject.getDataObject() instanceof PersistableAttachmentList) {
617                 populateAttachmentListForBO();
618             }
619             // If a maintenance lock exists, warn the user.
620             checkForLockingDocument(false);
621         }
622     }
623 
624     /**
625      * @return Returns the newMaintainableObject.
626      */
627     @Override
628     public Maintainable getNewMaintainableObject() {
629         return newMaintainableObject;
630     }
631 
632     /**
633      * @param newMaintainableObject The newMaintainableObject to set.
634      */
635     @Override
636     public void setNewMaintainableObject(Maintainable newMaintainableObject) {
637         this.newMaintainableObject = newMaintainableObject;
638     }
639 
640     /**
641      * @return Returns the oldMaintainableObject.
642      */
643     public Maintainable getOldMaintainableObject() {
644         return oldMaintainableObject;
645     }
646 
647     /**
648      * @param oldMaintainableObject The oldMaintainableObject to set.
649      */
650     @Override
651     public void setOldMaintainableObject(Maintainable oldMaintainableObject) {
652         this.oldMaintainableObject = oldMaintainableObject;
653     }
654 
655     @Override
656     public void setDocumentNumber(String documentNumber) {
657         super.setDocumentNumber(documentNumber);
658 
659         // set the finDocNumber on the Maintainable
660         oldMaintainableObject.setDocumentNumber(documentNumber);
661         newMaintainableObject.setDocumentNumber(documentNumber);
662     }
663 
664     /**
665      * Gets the fieldsClearedOnCopy attribute.
666      *
667      * @return Returns the fieldsClearedOnCopy.
668      */
669     @Override
670     public final boolean isFieldsClearedOnCopy() {
671         return fieldsClearedOnCopy;
672     }
673 
674     /**
675      * Sets the fieldsClearedOnCopy attribute value.
676      *
677      * @param fieldsClearedOnCopy The fieldsClearedOnCopy to set.
678      */
679     @Override
680     public final void setFieldsClearedOnCopy(boolean fieldsClearedOnCopy) {
681         this.fieldsClearedOnCopy = fieldsClearedOnCopy;
682     }
683 
684     /**
685      * Gets the xmlDocumentContents attribute.
686      *
687      * @return Returns the xmlDocumentContents.
688      */
689     @Override
690     public String getXmlDocumentContents() {
691         return xmlDocumentContents;
692     }
693 
694     /**
695      * Sets the xmlDocumentContents attribute value.
696      *
697      * @param xmlDocumentContents The xmlDocumentContents to set.
698      */
699     @Override
700     public void setXmlDocumentContents(String xmlDocumentContents) {
701         this.xmlDocumentContents = xmlDocumentContents;
702     }
703 
704     /**
705      * @see org.kuali.rice.krad.document.Document#getAllowsCopy()
706      */
707     @Override
708     public boolean getAllowsCopy() {
709         return getDocumentDictionaryService().getAllowsCopy(this);
710     }
711 
712     /**
713      * @see MaintenanceDocument#getDisplayTopicFieldInNotes()
714      */
715     @Override
716     public boolean getDisplayTopicFieldInNotes() {
717         return displayTopicFieldInNotes;
718     }
719 
720     /**
721      * @see MaintenanceDocument#setDisplayTopicFieldInNotes(boolean)
722      */
723     @Override
724     public void setDisplayTopicFieldInNotes(boolean displayTopicFieldInNotes) {
725         this.displayTopicFieldInNotes = displayTopicFieldInNotes;
726     }
727 
728     @Override
729     /**
730      * Overridden to avoid serializing the xml twice, because of the xmlDocumentContents property of this object
731      */
732     public String serializeDocumentToXml() {
733         String tempXmlDocumentContents = xmlDocumentContents;
734         xmlDocumentContents = null;
735         String xmlForWorkflow = super.serializeDocumentToXml();
736         xmlDocumentContents = tempXmlDocumentContents;
737         return xmlForWorkflow;
738     }
739 
740     @Override
741     public void prepareForSave(KualiDocumentEvent event) {
742         super.prepareForSave(event);
743         if(newMaintainableObject.getDataObject() instanceof PersistableAttachment) {
744             populateDocumentAttachment();
745             populateAttachmentForBO();
746             //clear out attachment file for old data object so it isn't serialized in doc content
747             if (oldMaintainableObject.getDataObject() instanceof PersistableAttachment) {
748                 ((PersistableAttachment)oldMaintainableObject.getDataObject()).setAttachmentContent(null);
749             }
750         }
751         if(newMaintainableObject.getDataObject() instanceof PersistableAttachmentList) {
752             populateDocumentAttachmentList();
753             populateAttachmentListForBO();
754             if (oldMaintainableObject.getDataObject() instanceof PersistableAttachmentList) {
755                 for (PersistableAttachment pa : ((PersistableAttachmentList<PersistableAttachment>)oldMaintainableObject.getDataObject()).getAttachments()) {
756                     pa.setAttachmentContent(null);
757                 }
758             }
759         }
760         populateXmlDocumentContentsFromMaintainables();
761     }
762 
763     /**
764      * The attachment BO is proxied in OJB.  For some reason when an attachment does not yet exist,
765      * refreshReferenceObject is not returning null and the proxy cannot be materialized. So, this method exists to
766      * properly handle the proxied attachment BO.  This is a hack and should be removed post JPA migration.
767      */
768     protected void refreshAttachment() {
769         if (ObjectUtils.isNull(attachment)) {
770             this.refreshReferenceObject("attachment");
771             final boolean isProxy = attachment != null && ProxyHelper.isProxy(attachment);
772             if (isProxy && ProxyHelper.getRealObject(attachment) == null) {
773                 attachment = null;
774             }
775         }
776     }
777 
778     protected void refreshAttachmentList() {
779         if (ObjectUtils.isNull(attachments)) {
780             this.refreshReferenceObject("attachments");
781             final boolean isProxy = attachments != null && ProxyHelper.isProxy(attachments);
782             if (isProxy && ProxyHelper.getRealObject(attachments) == null) {
783                 attachments = null;
784             }
785         }
786     }
787 
788     public void populateAttachmentForBO() {
789     // TODO: need to convert this from using struts form file
790 
791     }
792 
793 
794 
795     public void populateDocumentAttachment() {
796         // TODO: need to convert this from using struts form file
797 //        refreshAttachment();
798 //
799 //        if (fileAttachment != null && StringUtils.isNotEmpty(fileAttachment.getFileName())) {
800 //            //Populate DocumentAttachment BO
801 //            if (attachment == null) {
802 //                attachment = new DocumentAttachment();
803 //            }
804 //
805 //            byte[] fileContents;
806 //            try {
807 //                fileContents = fileAttachment.getFileData();
808 //                if (fileContents.length > 0) {
809 //                    attachment.setFileName(fileAttachment.getFileName());
810 //                    attachment.setContentType(fileAttachment.getContentType());
811 //                    attachment.setAttachmentContent(fileAttachment.getFileData());
812 //                    attachment.setDocumentNumber(getDocumentNumber());
813 //                }
814 //            } catch (FileNotFoundException e) {
815 //                LOG.error("Error while populating the Document Attachment", e);
816 //                throw new RuntimeException("Could not populate DocumentAttachment object", e);
817 //            } catch (IOException e) {
818 //                LOG.error("Error while populating the Document Attachment", e);
819 //                throw new RuntimeException("Could not populate DocumentAttachment object", e);
820 //            }
821 //        }
822 ////        else if(attachment != null) {
823 ////            //Attachment has been deleted - Need to delete the Attachment Reference Object
824 ////            deleteAttachment();
825 ////        }
826     }
827 
828     public void populateAttachmentListForBO() { }
829 
830     public void populateAttachmentBeforeSave() { }
831 
832     public void populateDocumentAttachmentList() { }
833 
834     public void populateBoAttachmentListBeforeSave() { }
835 
836 
837     public void deleteDocumentAttachment() {
838         KRADServiceLocator.getBusinessObjectService().delete(attachment);
839         attachment = null;
840     }
841 
842     public void deleteDocumentAttachmentList() {
843         if (CollectionUtils.isNotEmpty(attachments)) {
844             KRADServiceLocator.getBusinessObjectService().delete(attachments);
845             attachments = null;
846         }
847     }
848 
849     /**
850      * Explicitly NOT calling super here.  This is a complete override of the validation rules behavior.
851      *
852      * @see org.kuali.rice.krad.document.DocumentBase#validateBusinessRules(org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent)
853      */
854     @Override
855     public void validateBusinessRules(KualiDocumentEvent event) {
856         if (GlobalVariables.getMessageMap().hasErrors()) {
857             logErrors();
858             throw new ValidationException("errors occured before business rule");
859         }
860 
861         // check for locking documents for MaintenanceDocuments
862         // TODO: Why is this here.  This "if" will always be true
863         if (this instanceof MaintenanceDocument) {
864             checkForLockingDocument(true);
865         }
866 
867         // Make sure the business object's version number matches that of the database's copy.
868         if (newMaintainableObject != null) {
869             if (newMaintainableObject.isLockable()) {
870                 PersistableBusinessObject pbObject = KRADServiceLocator.getBusinessObjectService()
871                         .retrieve(newMaintainableObject.getPersistableBusinessObject());
872                 Long pbObjectVerNbr = ObjectUtils.isNull(pbObject) ? null : pbObject.getVersionNumber();
873                 Long newObjectVerNbr = newMaintainableObject.getPersistableBusinessObject().getVersionNumber();
874                 if (pbObjectVerNbr != null && !(pbObjectVerNbr.equals(newObjectVerNbr))) {
875                     GlobalVariables.getMessageMap()
876                             .putError(KRADConstants.GLOBAL_ERRORS, RiceKeyConstants.ERROR_VERSION_MISMATCH);
877                     throw new ValidationException(
878                             "Version mismatch between the local business object and the database business object");
879                 }
880             }
881         }
882 
883         // perform validation against rules engine
884         if (LOG.isInfoEnabled()) {
885             LOG.info("invoking rules engine on document " + getDocumentNumber());
886         }
887         boolean isValid = true;
888         isValid = KRADServiceLocatorWeb.getKualiRuleService().applyRules(event);
889 
890         // check to see if the br eval passed or failed
891         if (!isValid) {
892             logErrors();
893             // TODO: better error handling at the lower level and a better error message are
894             // needed here
895             throw new ValidationException("business rule evaluation failed");
896         } else if (GlobalVariables.getMessageMap().hasErrors()) {
897             logErrors();
898             if (event instanceof SaveDocumentEvent) {
899                 // for maintenance documents, we want to always actually do a save if the
900                 // user requests a save, even if there are validation or business rules
901                 // failures. this empty if does this, and allows the document to be saved,
902                 // even if there are failures.
903                 // BR or validation failures on a ROUTE even should always stop the route,
904                 // that has not changed
905             } else {
906                 throw new ValidationException(
907                         "Unreported errors occured during business rule evaluation (rule developer needs to put meaningful error messages into global ErrorMap)");
908             }
909         }
910         LOG.debug("validation completed");
911     }
912 
913     protected void checkForLockingDocument(boolean throwExceptionIfLocked) {
914         MaintenanceUtils.checkForLockingDocument(this, throwExceptionIfLocked);
915     }
916 
917     /**
918      * this needs to happen after the document itself is saved, to preserve consistency of the ver_nbr and in the case
919      * of initial save, because this can't be saved until the document is saved initially
920      *
921      * @see org.kuali.rice.krad.document.DocumentBase#postProcessSave(org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent)
922      */
923     @Override
924     public void postProcessSave(KualiDocumentEvent event) {
925         if (getNewMaintainableObject().getDataObject() instanceof PersistableBusinessObject) {
926             PersistableBusinessObject bo = (PersistableBusinessObject) getNewMaintainableObject().getDataObject();
927             if (bo instanceof GlobalBusinessObject) {
928                 KRADServiceLocator.getBusinessObjectService().save(bo);
929             }
930         }
931 
932         //currently only global documents could change the list of what they're affecting during routing,
933         //so could restrict this to only happening with them, but who knows if that will change, so safest
934         //to always do the delete and re-add...seems a bit inefficient though if nothing has changed, which is
935         //most of the time...could also try to only add/update/delete what's changed, but this is easier
936         if (!(event instanceof SaveDocumentEvent)) { //don't lock until they route
937             getMaintenanceDocumentService().deleteLocks(this.getDocumentNumber());
938             getMaintenanceDocumentService().storeLocks(this.getNewMaintainableObject().generateMaintenanceLocks());
939         }
940     }
941 
942     /**
943      * @see org.kuali.rice.krad.document.DocumentBase#getDocumentBusinessObject()
944      */
945     @Override
946     public Object getDocumentDataObject() {
947         return getNewMaintainableObject().getDataObject();
948     }
949 
950     /**
951      * <p>The Note target for maintenance documents is determined by whether or not the underlying {@link Maintainable}
952      * supports business object notes or not.  This is determined via a call to {@link
953      * Maintainable#isBoNotesEnabled()}.
954      * The note target is then derived as follows: <p/> <ul> <li>If the {@link Maintainable} supports business object
955      * notes, delegate to {@link #getDocumentDataObject()}. <li>Otherwise, delegate to the default implementation of
956      * getNoteTarget on the superclass which will effectively return a reference to the {@link DocumentHeader}. </ul>
957      *
958      * @see org.kuali.rice.krad.document.Document#getNoteTarget()
959      */
960     @Override
961     public PersistableBusinessObject getNoteTarget() {
962         if (getNewMaintainableObject() == null) {
963             throw new IllegalStateException(
964                     "Failed to acquire the note target.  The new maintainable object on this document is null.");
965         }
966         if (getNewMaintainableObject().isNotesEnabled()) {
967             return (PersistableBusinessObject) getDocumentDataObject();
968         }
969         return super.getNoteTarget();
970     }
971 
972     /**
973      * The {@link NoteType} for maintenance documents is determined by whether or not the underlying {@link
974      * Maintainable} supports business object notes or not.  This is determined via a call to {@link
975      * Maintainable#   isBoNotesEnabled()}.  The {@link NoteType} is then derived as follows: <p/> <ul> <li>If the {@link
976      * Maintainable} supports business object notes, return {@link NoteType#BUSINESS_OBJECT}. <li>Otherwise, delegate
977      * to
978      * {@link DocumentBase#getNoteType()} </ul>
979      *
980      * @see org.kuali.rice.krad.document.Document#getNoteType()
981      * @see org.kuali.rice.krad.document.Document#getNoteTarget()
982      */
983     @Override
984     public NoteType getNoteType() {
985         if (getNewMaintainableObject().isNotesEnabled()) {
986             return NoteType.BUSINESS_OBJECT;
987         }
988         return super.getNoteType();
989     }
990 
991     @Override
992     public PropertySerializabilityEvaluator getDocumentPropertySerizabilityEvaluator() {
993         String docTypeName = "";
994         if (newMaintainableObject != null) {
995             docTypeName = getDocumentDictionaryService()
996                     .getMaintenanceDocumentTypeName(this.newMaintainableObject.getDataObjectClass());
997         } else { // I don't know why we aren't just using the header in the first place
998             // but, in the case where we can't get it in the way above, attempt to get
999             // it off the workflow document header
1000             if (getDocumentHeader() != null && getDocumentHeader().getWorkflowDocument() != null) {
1001                 docTypeName = getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
1002             }
1003         }
1004         if (!StringUtils.isBlank(docTypeName)) {
1005             DocumentEntry documentEntry =
1006                     getDocumentDictionaryService().getMaintenanceDocumentEntry(docTypeName);
1007             if (documentEntry != null) {
1008                 WorkflowProperties workflowProperties = documentEntry.getWorkflowProperties();
1009                 WorkflowAttributes workflowAttributes = documentEntry.getWorkflowAttributes();
1010                 return createPropertySerializabilityEvaluator(workflowProperties, workflowAttributes);
1011             } else {
1012                 LOG.error("Unable to obtain DD DocumentEntry for document type: '" + docTypeName + "'");
1013             }
1014         } else {
1015             LOG.error("Unable to obtain document type name for this document: " + this);
1016         }
1017         LOG.error("Returning null for the PropertySerializabilityEvaluator");
1018         return null;
1019     }
1020 
1021     public DocumentAttachment getAttachment() {
1022         return this.attachment;
1023     }
1024 
1025     public void setAttachment(DocumentAttachment attachment) {
1026         this.attachment = attachment;
1027     }
1028 
1029     public List<MultiDocumentAttachment> getAttachments() {
1030         return this.attachments;
1031     }
1032 
1033     public void setAttachments(List<MultiDocumentAttachment> attachments) {
1034         this.attachments = attachments;
1035     }
1036 
1037     /**
1038      * This overridden method is used to delete the {@link DocumentHeader} object due to the system not being able to
1039      * manage the {@link DocumentHeader} object via mapping files
1040      *
1041      * @see org.kuali.rice.krad.bo.PersistableBusinessObjectBase#postRemove()
1042      */
1043     @Override
1044     protected void postRemove() {
1045         super.postRemove();
1046         getDocumentHeaderService().deleteDocumentHeader(getDocumentHeader());
1047     }
1048 
1049     /**
1050      * This overridden method is used to retrieve the {@link DocumentHeader} object due to the system not being able to
1051      * manage the {@link DocumentHeader} object via mapping files
1052      *
1053      * @see org.kuali.rice.krad.bo.PersistableBusinessObjectBase#postLoad()
1054      */
1055     @Override
1056     protected void postLoad() {
1057         super.postLoad();
1058         setDocumentHeader(getDocumentHeaderService().getDocumentHeaderById(getDocumentNumber()));
1059     }
1060 
1061     /**
1062      * This overridden method is used to insert the {@link DocumentHeader} object due to the system not being able to
1063      * manage the {@link DocumentHeader} object via mapping files
1064      *
1065      * @see org.kuali.rice.krad.bo.PersistableBusinessObjectBase#prePersist()
1066      */
1067     @Override
1068     protected void prePersist() {
1069         super.prePersist();
1070         getDocumentHeaderService().saveDocumentHeader(getDocumentHeader());
1071     }
1072 
1073     /**
1074      * This overridden method is used to save the {@link DocumentHeader} object due to the system not being able to
1075      * manage the {@link DocumentHeader} object via mapping files
1076      *
1077      * @see org.kuali.rice.krad.bo.PersistableBusinessObjectBase#preUpdate()
1078      */
1079     @Override
1080     protected void preUpdate() {
1081         super.preUpdate();
1082         getDocumentHeaderService().saveDocumentHeader(getDocumentHeader());
1083     }
1084 
1085     /**
1086      * This method to check whether the document class implements SessionDocument
1087      *
1088      * @return
1089      */
1090     public boolean isSessionDocument() {
1091         return SessionDocument.class.isAssignableFrom(this.getClass());
1092     }
1093 
1094     /**
1095      * Returns whether or not the new maintainable object supports custom lock descriptors. Will always return false if
1096      * the new maintainable is null.
1097      *
1098      * @see org.kuali.rice.krad.document.Document#useCustomLockDescriptors()
1099      * @see org.kuali.rice.krad.maintenance.Maintainable#useCustomLockDescriptors()
1100      */
1101     @Override
1102     public boolean useCustomLockDescriptors() {
1103         return (newMaintainableObject != null && newMaintainableObject.useCustomLockDescriptors());
1104     }
1105 
1106     /**
1107      * Returns the custom lock descriptor generated by the new maintainable object, if defined. Will throw a
1108      * PessimisticLockingException if the new maintainable is null.
1109      *
1110      * @see org.kuali.rice.krad.document.Document#getCustomLockDescriptor(org.kuali.rice.kim.api.identity.Person)
1111      * @see org.kuali.rice.krad.maintenance.Maintainable#getCustomLockDescriptor(org.kuali.rice.kim.api.identity.Person)
1112      */
1113     @Override
1114     public String getCustomLockDescriptor(Person user) {
1115         if (newMaintainableObject == null) {
1116             throw new PessimisticLockingException("Maintenance Document " + getDocumentNumber() +
1117                     " is using pessimistic locking with custom lock descriptors, but no new maintainable object has been defined");
1118         }
1119         return newMaintainableObject.getCustomLockDescriptor(user);
1120     }
1121 
1122     protected DocumentDictionaryService getDocumentDictionaryService() {
1123         if (documentDictionaryService == null) {
1124             documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService();
1125         }
1126         return documentDictionaryService;
1127     }
1128 
1129     protected MaintenanceDocumentService getMaintenanceDocumentService() {
1130         if (maintenanceDocumentService == null) {
1131             maintenanceDocumentService = KRADServiceLocatorWeb.getMaintenanceDocumentService();
1132         }
1133         return maintenanceDocumentService;
1134     }
1135 
1136     protected DocumentHeaderService getDocumentHeaderService() {
1137         if (documentHeaderService == null) {
1138             documentHeaderService = KRADServiceLocatorWeb.getDocumentHeaderService();
1139         }
1140         return documentHeaderService;
1141     }
1142 
1143     protected DocumentService getDocumentService() {
1144         if (documentService == null) {
1145             documentService = KRADServiceLocatorWeb.getDocumentService();
1146         }
1147         return documentService;
1148     }
1149 
1150     //for issue KULRice3070
1151     protected boolean checkAllowsRecordDeletion() {
1152         Boolean allowsRecordDeletion = KRADServiceLocatorWeb.getDocumentDictionaryService()
1153                 .getAllowsRecordDeletion(this.getNewMaintainableObject().getDataObjectClass());
1154         if (allowsRecordDeletion != null) {
1155             return allowsRecordDeletion.booleanValue();
1156         } else {
1157             return false;
1158         }
1159     }
1160 
1161     //for KULRice3070
1162     protected boolean checkMaintenanceAction() {
1163         return this.getNewMaintainableObject().getMaintenanceAction().equals(KRADConstants.MAINTENANCE_DELETE_ACTION);
1164     }
1165 
1166     //for KULRice3070
1167     protected boolean checkDeletePermission(Object dataObject) {
1168         boolean allowsMaintain = false;
1169 
1170         String maintDocTypeName = KRADServiceLocatorWeb.getDocumentDictionaryService()
1171                 .getMaintenanceDocumentTypeName(dataObject.getClass());
1172 
1173         if (StringUtils.isNotBlank(maintDocTypeName)) {
1174             allowsMaintain = KRADServiceLocatorWeb.getDataObjectAuthorizationService()
1175                     .canMaintain(dataObject, GlobalVariables.getUserSession().getPerson(), maintDocTypeName);
1176         }
1177         return allowsMaintain;
1178     }
1179 }