View Javadoc
1   /*
2    * Copyright 2008 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.ole.sys.document.authorization;
17  
18  import java.text.MessageFormat;
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.HashSet;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Set;
25  
26  import org.apache.commons.lang.StringUtils;
27  import org.kuali.ole.sys.OLEConstants;
28  import org.kuali.ole.sys.OLEKeyConstants;
29  import org.kuali.ole.sys.businessobject.AccountingLine;
30  import org.kuali.ole.sys.businessobject.FinancialSystemDocumentHeader;
31  import org.kuali.ole.sys.context.SpringContext;
32  import org.kuali.ole.sys.document.AccountingDocument;
33  import org.kuali.ole.sys.document.Correctable;
34  import org.kuali.ole.sys.document.web.AccountingLineRenderingContext;
35  import org.kuali.ole.sys.document.web.AccountingLineViewAction;
36  import org.kuali.ole.sys.document.web.AccountingLineViewField;
37  import org.kuali.ole.sys.identity.OleKimAttributes;
38  import org.kuali.rice.core.api.config.property.ConfigurationService;
39  import org.kuali.rice.kew.api.WorkflowDocument;
40  import org.kuali.rice.kim.api.KimConstants;
41  import org.kuali.rice.kim.api.identity.Person;
42  import org.kuali.rice.kns.document.authorization.DocumentAuthorizer;
43  import org.kuali.rice.kns.service.DocumentHelperService;
44  import org.kuali.rice.krad.util.GlobalVariables;
45  import org.kuali.rice.krad.util.KRADConstants;
46  import org.kuali.rice.krad.util.ObjectUtils;
47  
48  /**
49   * The default implementation of AccountingLineAuthorizer
50   */
51  public class AccountingLineAuthorizerBase implements AccountingLineAuthorizer {
52      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AccountingLineAuthorizerBase.class);
53  
54      private static ConfigurationService kualiConfigurationService;
55      protected static String riceImagePath;
56      protected static String kfsImagePath;
57  
58      /**
59       * Returns the basic actions - add for new lines, delete and balance inquiry for existing lines
60       * 
61       * @see org.kuali.ole.sys.document.authorization.AccountingLineAuthorizer#getActions(org.kuali.ole.sys.document.AccountingDocument,
62       *      org.kuali.ole.sys.businessobject.AccountingLine, java.lang.String, java.lang.Integer, org.kuali.rice.kim.api.identity.Person,
63       *      java.lang.String)
64       */
65      public List<AccountingLineViewAction> getActions(AccountingDocument accountingDocument, AccountingLineRenderingContext accountingLineRenderingContext, String accountingLinePropertyName, Integer accountingLineIndex, Person currentUser, String groupTitle) {
66          List<AccountingLineViewAction> actions = new ArrayList<AccountingLineViewAction>();
67  
68          if (accountingLineRenderingContext.isEditableLine() || isMessageMapContainingErrorsOnLine(accountingLinePropertyName)) {
69              Map<String, AccountingLineViewAction> actionMap = this.getActionMap(accountingLineRenderingContext, accountingLinePropertyName, accountingLineIndex, groupTitle);
70              actions.addAll(actionMap.values());
71          }
72  
73          return actions;
74      }
75      
76      /**
77       * Determines if the error map contains any errors which exist on the currently rendered accounting line
78       * @param accountingLinePropertyName the property name of the accounting line
79       * @return true if there are errors on the line, false otherwise
80       */
81      protected boolean isMessageMapContainingErrorsOnLine(String accountingLinePropertyName) {
82          for (Object errorKeyAsObject : GlobalVariables.getMessageMap().getPropertiesWithErrors()) {
83              if (((String)errorKeyAsObject).startsWith(accountingLinePropertyName)) return true;
84          }
85          return false;
86      }
87  
88      /**
89       * Returns a new empty HashSet
90       * 
91       * @see org.kuali.ole.sys.document.authorization.AccountingLineAuthorizer#getUnviewableBlocks(org.kuali.ole.sys.document.AccountingDocument,
92       *      org.kuali.ole.sys.businessobject.AccountingLine, java.lang.String, boolean)
93       */
94      public Set<String> getUnviewableBlocks(AccountingDocument accountingDocument, AccountingLine accountingLine, boolean newLine, Person currentUser) {
95          return new HashSet<String>();
96      }
97  
98      /**
99       * @see org.kuali.ole.sys.document.authorization.AccountingLineAuthorizer#renderNewLine(org.kuali.ole.sys.document.AccountingDocument,
100      *      java.lang.String)
101      */
102     public boolean renderNewLine(AccountingDocument accountingDocument, String accountingGroupProperty) {
103         return (accountingDocument.getDocumentHeader().getWorkflowDocument().isInitiated() || accountingDocument.getDocumentHeader().getWorkflowDocument().isSaved());
104     }
105 
106     /**
107      * @see org.kuali.ole.sys.document.authorization.AccountingLineAuthorizer#isGroupEditable(org.kuali.ole.sys.document.AccountingDocument,
108      *      java.lang.String, org.kuali.rice.kim.api.identity.Person)
109      */
110     public boolean isGroupEditable(AccountingDocument accountingDocument, List<? extends AccountingLineRenderingContext> accountingLineRenderingContexts, Person currentUser) {
111         WorkflowDocument workflowDocument = accountingDocument.getDocumentHeader().getWorkflowDocument();
112         if (workflowDocument.isInitiated() || workflowDocument.isSaved()) {
113             return StringUtils.equalsIgnoreCase( workflowDocument.getInitiatorPrincipalId(), currentUser.getPrincipalId() );
114         }
115         
116         for (AccountingLineRenderingContext renderingContext : accountingLineRenderingContexts) {
117             if (renderingContext.isEditableLine()) return true;
118         }
119 
120         return false;
121     }
122 
123     /**
124      * collection the actions that are allowed for the given accounting line
125      * 
126      * @param accountingLine the given accounting line
127      * @param accountingLinePropertyName the property name of the given account line, typically, the form name
128      * @param accountingLineIndex the index of the given accounting line in its accounting line group
129      * @param groupTitle the title of the accounting line group
130      * @return the actions that are allowed for the given accounting line
131      */
132     protected Map<String, AccountingLineViewAction> getActionMap(AccountingLineRenderingContext accountingLineRenderingContext, String accountingLinePropertyName, Integer accountingLineIndex, String groupTitle) {
133         Map<String, AccountingLineViewAction> actionMap = new HashMap<String, AccountingLineViewAction>();
134 
135         if (accountingLineIndex == null || accountingLineIndex < 0) {
136             AccountingLineViewAction addAction = this.getAddAction(accountingLineRenderingContext.getAccountingLine(), accountingLinePropertyName, groupTitle);
137             actionMap.put(OLEConstants.INSERT_METHOD, addAction);
138         }
139         else {
140             if (accountingLineRenderingContext.allowDelete()) {
141                 AccountingLineViewAction deleteAction = this.getDeleteAction(accountingLineRenderingContext.getAccountingLine(), accountingLinePropertyName, accountingLineIndex, groupTitle);
142                 actionMap.put(KRADConstants.DELETE_METHOD, deleteAction);
143             }
144 
145             AccountingLineViewAction balanceInquiryAction = this.getBalanceInquiryAction(accountingLineRenderingContext.getAccountingLine(), accountingLinePropertyName, accountingLineIndex, groupTitle);
146             actionMap.put(OLEConstants.PERFORMANCE_BALANCE_INQUIRY_FOR_METHOD, balanceInquiryAction);
147         }
148 
149         return actionMap;
150     }
151 
152     /**
153      * determine whether the current user has permission to edit the given field in the given accounting line
154      * 
155      * @param accountingDocument the given accounting document
156      * @param accountingLine the given accounting line in the document
157      * @param fieldName the name of a field in the given accounting line
158      * @param editableLine whether the parent line of this field is editable
159      * @param editablePage whether the parent page of this field is editable
160      * @param currentUser the current user
161      * @return true if the the current user has permission to edit the given field in the given accounting line; otherwsie, false
162      */
163     public final boolean hasEditPermissionOnField(AccountingDocument accountingDocument, AccountingLine accountingLine, String accountingLineCollectionProperty, String fieldName, boolean editableLine, boolean editablePage, Person currentUser) {
164         if (!determineEditPermissionOnField(accountingDocument, accountingLine, accountingLineCollectionProperty, fieldName, editablePage)) {
165             return false;
166         }
167         
168         // the fields in a new line should be always editable
169         if (accountingLine.getSequenceNumber() == null) {
170             return true;
171         }
172 
173         // examine whether the given field can be editable
174         boolean hasEditPermissionOnField = editableLine || this.determineEditPermissionByFieldName(accountingDocument, accountingLine, getKimHappyPropertyNameForField(accountingLineCollectionProperty+"."+fieldName), currentUser);
175         if (hasEditPermissionOnField == false) {
176             // kim check shows field should not be editable based on contents of field - check if line error message occurred on this line
177             // if error message shows up, then the value must have changed recently so - we make it editable to allow user to correct it
178             WorkflowDocument workflowDocument = accountingDocument.getDocumentHeader().getWorkflowDocument();
179             if (workflowDocument.isEnroute() && isMessageMapContainingErrorsOnLine(accountingLineCollectionProperty)) return true;
180          }
181         return hasEditPermissionOnField;
182     }
183  
184     /**
185      * Allows the overriding of whether a field on an accounting line is editable or not
186      * @param accountingDocument the accounting document the line to test is on
187      * @param accountingLine the accounting line to test
188      * @param accountingLineCollectionProperty the property that the accounting line lives in
189      * @param fieldName the name of the field we are testing
190      * @param editableLine whether the parent line of this field is editable
191      * @param editablePage whether the parent page of this field is editable
192      * @return true if the field can be edited (subject to subsequence KIM check); false otherwise
193      */
194     public boolean determineEditPermissionOnField(AccountingDocument accountingDocument, AccountingLine accountingLine, String accountingLineCollectionProperty, String fieldName, boolean editablePage) {
195         if (!editablePage) return false; // no edits by default on non editable pages
196         
197         final FinancialSystemDocumentHeader documentHeader = (FinancialSystemDocumentHeader) accountingDocument.getDocumentHeader();
198         final WorkflowDocument workflowDocument = documentHeader.getWorkflowDocument();
199 
200         // if a document is cancelled or in error, all of its fields cannot be editable
201         if (workflowDocument.isCanceled() || ObjectUtils.isNotNull(documentHeader.getFinancialDocumentInErrorNumber())) {
202             return false;
203         }
204 
205         return true;
206     }
207 
208     /**
209      * Determine whether the current user has permission to edit the given accounting line as a whole
210      * 
211      * @param accountingDocument the given accounting document
212      * @param accountingLine the given accounting line in the document
213      * @param currentUser the current user
214      * @return true if the the current user has permission to edit the given accounting line; otherwsie, false
215      */
216     public final boolean hasEditPermissionOnAccountingLine(AccountingDocument accountingDocument, AccountingLine accountingLine, String accountingLineCollectionProperty, Person currentUser, boolean pageIsEditable) {        
217         if (determineEditPermissionOnLine(accountingDocument, accountingLine, accountingLineCollectionProperty, StringUtils.equalsIgnoreCase( accountingDocument.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId(), currentUser.getPrincipalId() ), pageIsEditable)) {
218             
219             if (approvedForUnqualifiedEditing(accountingDocument, accountingLine, accountingLineCollectionProperty, StringUtils.equalsIgnoreCase( accountingDocument.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId(), currentUser.getPrincipalId() ))) {
220                 return true;  // don't do the KIM check, we're good
221             }
222             
223             // examine whether the whole line can be editable via KIM check
224             final String lineFieldName = getKimHappyPropertyNameForField(accountingLineCollectionProperty);
225             return this.determineEditPermissionByFieldName(accountingDocument, accountingLine, lineFieldName, currentUser);
226         }
227         return false;
228     }
229  
230     /**
231      * A hook to decide, pre-KIM check, if there's an edit permission on the given accounting line
232      * @param accountingDocument the accounting document the line is or wants to be associated with
233      * @param accountingLine the accounting line itself
234      * @param accountingLineCollectionProperty the collection the accounting line is or would be part of
235      * @param currentUserIsDocumentInitiator is the current user the initiator of the document?
236      * @return true if the line as a whole can be edited, false otherwise
237      */
238     public boolean determineEditPermissionOnLine(AccountingDocument accountingDocument, AccountingLine accountingLine, String accountingLineCollectionProperty, boolean currentUserIsDocumentInitiator, boolean pageIsEditable) {
239         if (accountingDocument instanceof Correctable) {
240             String errorDocumentNumber = ((FinancialSystemDocumentHeader)accountingDocument.getDocumentHeader()).getFinancialDocumentInErrorNumber();
241             if (StringUtils.isNotBlank(errorDocumentNumber))
242                 return false;
243         }
244         
245         return true;
246     }
247     
248     /**
249      * Determines if the given line is editable, no matter what a KIM check would say about line editability.  In the default case,
250      * any accounting line is editable - minus KIM check - when the document is PreRoute, or if the line is a new line
251      * @param accountingDocument the accounting document the line is or wants to be associated with
252      * @param accountingLine the accounting line itself
253      * @param accountingLineCollectionProperty the collection the accounting line is or would be part of
254      * @param currentUserIsDocumentInitiator is the current user the initiator of the document?
255      * @return true if the line as a whole can be edited without the KIM check, false otherwise
256      */
257     protected boolean approvedForUnqualifiedEditing(AccountingDocument accountingDocument, AccountingLine accountingLine, String accountingLineCollectionProperty, boolean currentUserIsDocumentInitiator) {
258         // the fields in a new line should be always editable
259         if (accountingLine.getSequenceNumber() == null) {
260             return true;
261         }
262         
263         // check the initiation permission on the document if it is in the state of preroute
264         WorkflowDocument workflowDocument = accountingDocument.getDocumentHeader().getWorkflowDocument();
265         if (workflowDocument.isInitiated() || workflowDocument.isSaved()) {
266             return currentUserIsDocumentInitiator;
267         }
268         return false;
269     }
270 
271     /**
272      * determine whether the current user has permission to edit the given field in the given accounting line
273      * 
274      * @param accountingDocument the given accounting document
275      * @param accountingLine the given accounting line in the document
276      * @param fieldName the name of a field in the given accounting line
277      * @param currentUser the current user
278      * @return true if the the current user has permission to edit the given field in the given accounting line; otherwsie, false
279      */    
280     protected boolean determineEditPermissionByFieldName(AccountingDocument accountingDocument, AccountingLine accountingLine, String fieldName, Person currentUser) {        
281         final Map<String,String> roleQualifiers = this.getRoleQualifiers(accountingDocument, accountingLine);
282         final String documentTypeName = accountingDocument.getDocumentHeader().getWorkflowDocument().getDocumentTypeName();
283         final Map<String,String> permissionDetail = this.getPermissionDetails(documentTypeName, fieldName);
284 
285         return this.hasEditPermission(accountingDocument, currentUser, permissionDetail, roleQualifiers);       
286     }
287 
288     /**
289      * determine whether the current user has modification permission on an accounting line with the given qualifications. The
290      * permission template and namespace have been setup in the method.
291      * 
292      * @param currentUser the current user
293      * @param permissionDetails the given permission details
294      * @param roleQualifiers the given role qualifications
295      * @return true if the user has edit permission on an accounting line with the given qualifications; otherwise, false
296      */
297     protected boolean hasEditPermission(AccountingDocument accountingDocument, Person currentUser, Map<String,String> permissionDetails, Map<String,String> roleQualifiers) {
298         String pricipalId = currentUser.getPrincipalId();
299         DocumentAuthorizer accountingDocumentAuthorizer = this.getDocumentAuthorizer(accountingDocument);
300         
301         return accountingDocumentAuthorizer.isAuthorizedByTemplate(accountingDocument, OLEConstants.ParameterNamespaces.OLE, OLEConstants.PermissionTemplate.MODIFY_ACCOUNTING_LINES.name, pricipalId, permissionDetails, roleQualifiers);
302     }
303 
304     /**
305      * Gathers together all the information for a permission detail attribute set
306      * 
307      * @param documentTypeName the document
308      * @param fieldName the given field name
309      * @return all the information for a permission detail attribute set
310      */
311     protected Map<String,String> getPermissionDetails(String documentTypeName, String fieldName) {
312         Map<String,String> permissionDetails = new HashMap<String,String>();
313 
314         if (StringUtils.isNotBlank(documentTypeName)) {
315             permissionDetails.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, documentTypeName);
316         }
317 
318         if (StringUtils.isNotBlank(fieldName)) {
319             permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, fieldName);
320         }
321 
322         return permissionDetails;
323     }
324 
325     /**
326      * Gathers together the role qualifiers for the KIM perm call
327      * 
328      * @param accountingLine the accounting line to get role qualifiers from
329      * @return the gathered Map<String,String> of role qualifiers
330      */
331     protected final Map<String,String> getRoleQualifiers(AccountingDocument accountingDocument, AccountingLine accountingLine) {
332         Map<String,String> roleQualifiers = new HashMap<String,String>();
333 
334         if (accountingLine != null) {
335             roleQualifiers.put(OleKimAttributes.CHART_OF_ACCOUNTS_CODE, accountingLine.getChartOfAccountsCode());
336             roleQualifiers.put(OleKimAttributes.ACCOUNT_NUMBER, accountingLine.getAccountNumber());
337         }
338 
339         return roleQualifiers;
340     }
341 
342     /**
343      * @param field AccountingLineViewField to find KIM-happy property name for
344      * @return a property name that KIM will like
345      */
346     protected String getKimHappyPropertyNameForField(String convertedName) {
347         convertedName = stripDocumentPrefixFromName(convertedName);
348 
349         return replaceCollectionElementsWithPlurals(convertedName);
350     }
351 
352     /**
353      * get the full property name of the given field
354      * 
355      * @param field the field to get the name from
356      * @return the full property name of the given field, typically, a combination of property prefix and simple property name
357      */
358     protected String getFieldName(AccountingLineViewField field) {
359         String propertyPrefix = field.getField().getPropertyPrefix();
360         String propertyName = field.getField().getPropertyName();
361 
362         return StringUtils.isNotBlank(propertyPrefix) ? (propertyPrefix + "." + propertyName) : propertyName;
363     }
364 
365     /**
366      * Strips "document." and everything before from the property name
367      * 
368      * @param name the property name to strip the document portion off of
369      * @return the stripped name
370      */
371     protected String stripDocumentPrefixFromName(String name) {
372         return name.replaceFirst("(.)*document\\.", StringUtils.EMPTY);
373     }
374 
375     /**
376      * Replaces references to collection elements to their respective plural names WARNING: this method is totally lame and I for
377      * one wished it didn't have to exist
378      * 
379      * @param name the property name with perhaps collection elements in
380      * @return the corrected name
381      */
382     protected String replaceCollectionElementsWithPlurals(String name) {
383         String temp = name.replaceAll("\\[\\d+\\]", "s");
384         // now - need to check if the property name ends with a double "s", which is incorrect
385         if ( temp.endsWith( "ss" ) ) {
386             temp = StringUtils.chop(temp);
387         }
388         return temp;
389     }
390 
391     /**
392      * construct the balance inquiry action for the given accounting line
393      * 
394      * @param accountingLine the given accounting line
395      * @param accountingLinePropertyName the property name of the given account line, typically, the form name
396      * @param accountingLineIndex the index of the given accounting line in its accounting line group
397      * @param groupTitle the title of the accounting line group
398      * @return the balance inquiry action for the given accounting line
399      */
400     protected AccountingLineViewAction getBalanceInquiryAction(AccountingLine accountingLine, String accountingLinePropertyName, Integer accountingLineIndex, String groupTitle) {
401         String actionMethod = this.getBalanceInquiryMethod(accountingLine, accountingLinePropertyName, accountingLineIndex);
402         String actionLabel = this.getActionLabel(OLEKeyConstants.AccountingLineViewRendering.ACCOUNTING_LINE_BALANCE_INQUIRY_ACTION_LABEL, groupTitle, accountingLineIndex + 1);
403 
404         String actionImageName = getKFSImagePath() + "tinybutton-balinquiry.gif";
405 
406         return new AccountingLineViewAction(actionMethod, actionLabel, actionImageName);
407     }
408 
409     /**
410      * construct the delete action for the given accounting line
411      * 
412      * @param accountingLine the given accounting line
413      * @param accountingLinePropertyName the property name of the given account line, typically, the form name
414      * @param accountingLineIndex the index of the given accounting line in its accounting line group
415      * @param groupTitle the title of the accounting line group
416      * @return the delete action for the given accounting line
417      */
418     protected AccountingLineViewAction getDeleteAction(AccountingLine accountingLine, String accountingLinePropertyName, Integer accountingLineIndex, String groupTitle) {
419         String actionMethod = this.getDeleteLineMethod(accountingLine, accountingLinePropertyName, accountingLineIndex);
420         String actionLabel = this.getActionLabel(OLEKeyConstants.AccountingLineViewRendering.ACCOUNTING_LINE_DELETE_ACTION_LABEL, groupTitle, accountingLineIndex + 1);
421 
422         String actionImageName = getRiceImagePath() + "tinybutton-delete1.gif";
423 
424         return new AccountingLineViewAction(actionMethod, actionLabel, actionImageName);
425     }
426 
427     /**
428      * construct the add action for the given accounting line, typically, a new accounting line
429      * 
430      * @param accountingLine the given accounting line
431      * @param accountingLinePropertyName the property name of the given account line, typically, the form name
432      * @param accountingLineIndex the index of the given accounting line in its accounting line group
433      * @param groupTitle the title of the accounting line group
434      * @return the add action for the given accounting line
435      */
436     protected AccountingLineViewAction getAddAction(AccountingLine accountingLine, String accountingLinePropertyName, String groupTitle) {
437         String actionMethod = this.getAddMethod(accountingLine, accountingLinePropertyName);
438         String actionLabel = this.getActionLabel(OLEKeyConstants.AccountingLineViewRendering.ACCOUNTING_LINE_ADD_ACTION_LABEL, groupTitle);
439 
440         String actionImageName = getRiceImagePath() + "tinybutton-add1.gif";
441 
442         return new AccountingLineViewAction(actionMethod, actionLabel, actionImageName);
443     }
444 
445     /**
446      * get a label for an action with the specified message key and values
447      * 
448      * @param messageKey the given message key that points to the label
449      * @param values the given values that would be displayed in label
450      * @return a label for an action with the specified message key and values
451      */
452     protected String getActionLabel(String messageKey, Object... values) {
453         String messageBody = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(messageKey);
454 
455         return MessageFormat.format(messageBody, values);
456     }
457 
458     /**
459      * Builds the action method name of the method that adds accounting lines for this group
460      * 
461      * @param accountingLine the accounting line an action is being checked for
462      * @param accountingLinePropertyName the property name of the accounting line
463      * @return the action method name of the method that adds accounting lines for this group
464      */
465     protected String getAddMethod(AccountingLine accountingLine, String accountingLineProperty) {
466         final String infix = getActionInfixForNewAccountingLine(accountingLine, accountingLineProperty);
467         return OLEConstants.INSERT_METHOD + infix + "Line.anchoraccounting" + infix + "Anchor";
468     }
469 
470     /**
471      * Builds the action method name of the method that deletes accounting lines for this group
472      * 
473      * @param accountingLine the accounting line an action is being checked for
474      * @param accountingLinePropertyName the property name of the accounting line
475      * @param accountingLineIndex the index of the given accounting line within the the group being rendered
476      * @return the action method name of the method that deletes accounting lines for this group
477      */
478     protected String getDeleteLineMethod(AccountingLine accountingLine, String accountingLineProperty, Integer accountingLineIndex) {
479         final String infix = getActionInfixForExtantAccountingLine(accountingLine, accountingLineProperty);
480         return KRADConstants.DELETE_METHOD + infix + "Line.line" + accountingLineIndex + ".anchoraccounting" + infix + "Anchor";
481     }
482 
483     /**
484      * Builds the action method name of the method that performs a balance inquiry on accounting lines for this group
485      * 
486      * @param accountingLine the accounting line an action is being checked for
487      * @param accountingLinePropertyName the property name of the accounting line
488      * @param accountingLineIndex the index of the given accounting line within the the group being rendered
489      * @return the action method name of the method that performs a balance inquiry on accounting lines for this group
490      */
491     protected String getBalanceInquiryMethod(AccountingLine accountingLine, String accountingLineProperty, Integer accountingLineIndex) {
492         final String infix = getActionInfixForExtantAccountingLine(accountingLine, accountingLineProperty);
493         return OLEConstants.PERFORMANCE_BALANCE_INQUIRY_FOR_METHOD + infix + "Line.line" + accountingLineIndex + ".anchoraccounting" + infix + "existingLineLineAnchor" + accountingLineIndex;
494     }
495 
496     /**
497      * Gets the "action infix" for the given accounting line, so that the action knows it is supposed to add to source vs. target
498      * 
499      * @param accountingLine the accounting line an action is being checked for
500      * @param accountingLinePropertyName the property name of the accounting line
501      * @return the name of the action infix
502      */
503     protected String getActionInfixForNewAccountingLine(AccountingLine accountingLine, String accountingLinePropertyName) {
504         if (accountingLine.isSourceAccountingLine()) {
505             return OLEConstants.SOURCE;
506         }
507 
508         if (accountingLine.isTargetAccountingLine()) {
509             return OLEConstants.TARGET;
510         }
511 
512         return OLEConstants.EMPTY_STRING;
513     }
514 
515     /**
516      * Gets the "action infix" for the given accounting line which already exists on the document, so that the action knows it is
517      * supposed to add to source vs. target
518      * 
519      * @param accountingLine the accounting line an action is being checked for
520      * @param accountingLinePropertyName the property name of the accounting line
521      * @return the name of the action infix
522      */
523     protected String getActionInfixForExtantAccountingLine(AccountingLine accountingLine, String accountingLinePropertyName) {
524         if (accountingLine.isSourceAccountingLine()) {
525             return OLEConstants.SOURCE;
526         }
527 
528         if (accountingLine.isTargetAccountingLine()) {
529             return OLEConstants.TARGET;
530         }
531 
532         return OLEConstants.EMPTY_STRING;
533     }
534 
535     /**
536      * get the document authorizer of the given accounting document
537      * 
538      * @param accountingDocument the given accounting document
539      * @return the document authorizer of the given accounting document
540      */
541     protected DocumentAuthorizer getDocumentAuthorizer(AccountingDocument accountingDocument) {
542         return SpringContext.getBean(DocumentHelperService.class).getDocumentAuthorizer(accountingDocument);
543     }
544     
545     /**
546      * @return the path to rice images
547      */
548     protected String getRiceImagePath() {
549         if (riceImagePath == null) {
550             riceImagePath = getConfigurationService().getPropertyValueAsString(KRADConstants.EXTERNALIZABLE_IMAGES_URL_KEY);
551         }
552         return riceImagePath;
553     }
554     
555     /**
556      * @return the path to OLE images
557      */
558     protected String getKFSImagePath() {
559         if (kfsImagePath == null) {
560             kfsImagePath = getConfigurationService().getPropertyValueAsString(KRADConstants.APPLICATION_EXTERNALIZABLE_IMAGES_URL_KEY);
561         }
562         return kfsImagePath;
563     }
564     
565     protected ConfigurationService getConfigurationService() {
566         if ( kualiConfigurationService == null ) {
567             kualiConfigurationService = SpringContext.getBean(ConfigurationService.class);
568         }
569         return kualiConfigurationService;
570     }
571 }