001    /*
002     * Copyright 2011 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.kfs.sys.document.web;
017    
018    import java.util.ArrayList;
019    import java.util.List;
020    import java.util.Map;
021    
022    import javax.servlet.jsp.JspException;
023    import javax.servlet.jsp.PageContext;
024    import javax.servlet.jsp.tagext.Tag;
025    
026    import org.apache.commons.beanutils.PropertyUtils;
027    import org.apache.commons.lang.StringUtils;
028    import org.kuali.kfs.sys.businessobject.AccountingLine;
029    import org.kuali.kfs.sys.context.SpringContext;
030    import org.kuali.kfs.sys.document.AccountingDocument;
031    import org.kuali.kfs.sys.document.authorization.AccountingLineAuthorizer;
032    import org.kuali.kfs.sys.web.struts.KualiAccountingDocumentFormBase;
033    import org.kuali.rice.kim.bo.Person;
034    import org.kuali.rice.kns.datadictionary.BusinessObjectEntry;
035    import org.kuali.rice.kns.service.DataDictionaryService;
036    import org.kuali.rice.kns.util.FieldUtils;
037    import org.kuali.rice.kns.util.GlobalVariables;
038    import org.kuali.rice.kns.util.KNSConstants;
039    import org.kuali.rice.kns.web.ui.Field;
040    
041    /**
042     * A container which holds a single accounting line and the elements which will render it
043     */
044    public class RenderableAccountingLineContainer implements RenderableElement, AccountingLineRenderingContext {
045        private List<AccountingLineTableRow> rows;
046        private List<AccountingLineViewAction> actions;
047        private AccountingLine accountingLine;
048        private String accountingLineProperty;
049        private List<Field> fields;
050        private List<String> fieldNames;
051        private KualiAccountingDocumentFormBase form;
052        private String groupLabel;
053        private Integer lineCount;
054        private List errors;
055        private AccountingLineAuthorizer accountingLineAuthorizer;
056        private boolean editableLine;
057        private boolean deletable = false;
058        
059        /**
060         * Constructs a RenderableAccountingLineContainer
061         * @param form the form being rendered
062         * @param accountingLine the accounting line this container will render
063         * @param accountingLineProperty the property to that accounting line
064         * @param rows the rows to render
065         * @param newLine whether this is a new accounting line or not
066         * @param groupLabel the label for the group this accounting line is being rendered part of
067         * @param errors the set of errors currently on the document
068         * @param accountingLineAuthorizer the accounting line authorizer for the document
069         * @param editableLine whether this line, as a whole _line_ is editable
070         */
071        public RenderableAccountingLineContainer(KualiAccountingDocumentFormBase form, AccountingLine accountingLine, String accountingLineProperty, List<AccountingLineTableRow> rows, Integer lineCount, String groupLabel, List errors, AccountingLineAuthorizer accountingLineAuthorizer, boolean editableLine) {
072            this.form = form;
073            this.accountingLine = accountingLine;
074            this.accountingLineProperty = accountingLineProperty;
075            this.rows = rows;
076            this.lineCount = lineCount;
077            this.groupLabel = groupLabel;
078            this.errors = errors;
079            this.accountingLineAuthorizer = accountingLineAuthorizer;
080            this.editableLine = editableLine;
081        }
082        
083        /**
084         * Gets the accountingLine attribute. 
085         * @return Returns the accountingLine.
086         */
087        public AccountingLine getAccountingLine() {
088            return accountingLine;
089        }
090    
091        /**
092         * Gets the accountingLineProperty attribute. 
093         * @return Returns the accountingLineProperty.
094         */
095        public String getAccountingLineProperty() {
096            return accountingLineProperty;
097        }
098    
099        /**
100         * Gets the actions attribute. 
101         * @return Returns the actions.
102         */
103        public List<AccountingLineViewAction> getActionsForLine() {
104            if (actions == null) {
105                actions = accountingLineAuthorizer.getActions(form.getFinancialDocument(), this, accountingLineProperty, lineCount, GlobalVariables.getUserSession().getPerson(), groupLabel);
106            }
107            return actions;
108        }
109    
110        /**
111         * Gets the newLine attribute. 
112         * @return Returns the newLine.
113         */
114        public boolean isNewLine() {
115            return lineCount == null;
116        }
117    
118        /**
119         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#getCurrentLineCount()
120         */
121        public Integer getCurrentLineCount() {
122            return lineCount;
123        }
124    
125        /**
126         * Gets the rows attribute. 
127         * @return Returns the rows.
128         */
129        public List<AccountingLineTableRow> getRows() {
130            return rows;
131        }
132        
133        /**
134         * @return the number of cells this accounting line container will render
135         */
136        public int getCellCount() {
137            int maxCells = 0;
138            for (AccountingLineTableRow row : rows) {
139                final int maxRowCellCount = row.getChildCellCount();
140                if (maxCells < maxRowCellCount) {
141                    maxCells = maxRowCellCount;
142                }
143            }
144            return maxCells;
145        }
146        
147        /**
148         * Adds empty cells to a table row
149         * @param cellCount the number of cells we should be rendering
150         * @param row the row to pad out
151         */
152        protected void padOutRow(int cellCount, AccountingLineTableRow row) {
153            while ((cellCount - row.getChildCellCount()) > 0) {
154                row.addCell(new AccountingLineTableCell());
155            }
156        }
157        
158        /**
159         * While holding an action block, this is not an action block
160         * @see org.kuali.kfs.sys.document.web.RenderableElement#isActionBlock()
161         */
162        public boolean isActionBlock() {
163            return false;
164        }
165        
166        /**
167         * This is never empty
168         * @see org.kuali.kfs.sys.document.web.RenderableElement#isEmpty()
169         */
170        public boolean isEmpty() {
171            return false;
172        }
173        
174        /**
175         * This is not hidden 
176         * @see org.kuali.kfs.sys.document.web.RenderableElement#isHidden()
177         */
178        public boolean isHidden() {
179            return false;
180        }
181        
182        /**
183         * Renders all the rows
184         * @see org.kuali.kfs.sys.document.web.RenderableElement#renderElement(javax.servlet.jsp.PageContext, javax.servlet.jsp.tagext.Tag, org.kuali.kfs.sys.document.web.AccountingLineRenderingContext)
185         */
186        public void renderElement(PageContext pageContext, Tag parentTag, AccountingLineRenderingContext renderingContext) throws JspException {
187            for (AccountingLineTableRow row : rows) {
188                row.renderElement(pageContext, parentTag, renderingContext);
189            }
190        }
191        
192        /**
193         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#getAccountingLinePropertyPath()
194         */
195        public String getAccountingLinePropertyPath() {
196            return accountingLineProperty;
197        }
198        
199        /**
200         * Appends all fields from rows that this contains
201         * @see org.kuali.kfs.sys.document.web.RenderableElement#appendFieldNames(java.util.List)
202         */
203        public void appendFields(List<Field> fields) {
204            for (AccountingLineTableRow row : rows) {
205                row.appendFields(fields);
206            }
207        }
208        
209        /**
210         * Returns all of the field names within the accounting line to render
211         * @return a List of field names with the accounting line property prefixed
212         */
213        public List<Field> getFieldsForAccountingLine() {
214            if (fields == null) {
215                fields = new ArrayList<Field>();
216                appendFields(fields);
217            }
218            return fields;
219        }
220        
221        /**
222         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#getFieldNamesForAccountingLine()
223         */
224        public List<String> getFieldNamesForAccountingLine() {
225            if (fieldNames == null) {
226                fieldNames = new ArrayList<String>();
227                for (Field field : getFieldsForAccountingLine()) {
228                    fieldNames.add(accountingLineProperty+"."+field.getPropertyName());
229                }
230            }
231            return fieldNames;
232        }
233        
234        /**
235         * @see org.kuali.kfs.sys.document.web.RenderableElement#populateWithTabIndexIfRequested(int[], int)
236         */
237        public void populateWithTabIndexIfRequested( int reallyHighIndex) {
238            for (AccountingLineTableRow row : rows) {
239                row.populateWithTabIndexIfRequested(reallyHighIndex);
240            }
241        }
242        
243        /**
244         * Returns the unconvertedValues for the current form
245         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#getUnconvertedValues()
246         */
247        public Map getUnconvertedValues() {
248            return form.getUnconvertedValues();
249        }
250        
251        /**
252         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#populateValuesForFields()
253         */
254        public void populateValuesForFields() {
255            FieldUtils.populateFieldsFromBusinessObject(getFieldsForAccountingLine(), accountingLine);
256            
257            BusinessObjectEntry boDDEntry = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(getAccountingLine().getClass().getName());
258            
259            for (Field field : getFieldsForAccountingLine()) {
260                setUnconvertedValueIfNecessary(field);
261                setShouldShowSecure(field, boDDEntry);
262            }
263        }
264        
265        /**
266         * Sees if the given field has an unconverted value living in the unconverted value map and if so,
267         * changes the value to that
268         * @param field the field to possibly set an unconverted value on
269         */
270        protected void setUnconvertedValueIfNecessary(Field field) {
271            String propertyName = accountingLineProperty+"."+field.getPropertyName();
272            if (getUnconvertedValues().get(propertyName) != null) {
273                field.setPropertyValue((String)getUnconvertedValues().get(propertyName));
274            }
275        }
276        
277        /**
278         * Sets the masked value equal to the value if the current user can see the unmasked value for a secure field
279         * @param field the field to possible change the value for
280         * @param boDDEntry the data dictionary entry for the accounting line
281         */
282        protected void setShouldShowSecure(Field field, BusinessObjectEntry boDDEntry) {
283            // TODO: FIX
284            
285            // from Warren:  k... org.kuali.rice.kns.service.BusinessObjectAuthorizationService.getMaintenanceDocumentRestrictions(MaintenanceDocument, Person) has the determination of what restrictions there should be
286            // org.kuali.rice.kns.util.FieldUtils.applyAuthorization(Field, String, MaintenanceDocumentRestrictions) applies those restrictions
287        }
288        /**
289         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#getAccountingDocument()
290         */
291        public AccountingDocument getAccountingDocument() {
292            return form.getFinancialDocument();
293        }
294        
295        /**
296         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#fieldsCanRenderDynamicLabels()
297         */
298        public boolean fieldsCanRenderDynamicLabels() {
299            return !form.isHideDetails();
300        }
301        /**
302         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#fieldsShouldRenderHelp()
303         */
304        public boolean fieldsShouldRenderHelp() {
305            return form.isFieldLevelHelpEnabled();
306        }
307        /**
308         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#getTabState(java.lang.String)
309         */
310        public String getTabState(String tabKey) {
311            return form.getTabState(tabKey);
312        }
313        /**
314         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#incrementTabIndex()
315         */
316        public void incrementTabIndex() {
317            form.incrementTabIndex();
318        }
319        
320        /**
321         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#getGroupLabel()
322         */
323        public String getGroupLabel() {
324           return this.groupLabel; 
325        }
326    
327        /**
328         * Gets the errors attribute. 
329         * @return Returns the errors.
330         */
331        public List getErrors() {
332            return errors;
333        }
334    
335        /**
336         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#getForm()
337         */
338        public KualiAccountingDocumentFormBase getForm() {
339            return form;
340        }
341    
342        /**
343         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#getAccountingLineContainingObjectPropertyName()
344         */
345        public String getAccountingLineContainingObjectPropertyName() {
346            return StringUtils.substringBeforeLast(this.getAccountingLinePropertyPath(), String.valueOf(PropertyUtils.NESTED_DELIM));
347        }
348    
349        /**
350         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#isFieldModifyable(org.kuali.kfs.sys.document.web.AccountingLineViewField)
351         */
352        public boolean isFieldModifyable(String fieldName) {
353            Person currentUser = GlobalVariables.getUserSession().getPerson();
354            final boolean pageIsEditable = getForm().getDocumentActions().containsKey(KNSConstants.KUALI_ACTION_CAN_EDIT);
355            return accountingLineAuthorizer.hasEditPermissionOnField(getAccountingDocument(), accountingLine, this.accountingLineProperty, fieldName, editableLine, pageIsEditable, currentUser);
356        }
357    
358        /**
359         * Gets the editableLine attribute. 
360         * @return Returns the editableLine.
361         */
362        public boolean isEditableLine() {
363            return editableLine;
364        }
365        
366        /**
367         * Sets the editableLine attribute value.
368         * 
369         * @param editableLine The editableLine to set.
370         */
371        public void setEditableLine(boolean editableLine) {
372            this.editableLine = editableLine;
373        }
374    
375        /**
376         * Determines whether the line within this rendering context can be deleted.
377         * @see org.kuali.kfs.sys.document.web.AccountingLineRenderingContext#allowDelete()
378         */
379        public boolean allowDelete() {
380            return deletable;
381        }
382        
383        /**
384         * Makes the line within this accounting line context deletable
385         */
386        public void makeDeletable() {
387            deletable = true;
388        }
389    }
390