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