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.renderers;
017
018 import java.io.IOException;
019 import java.util.ArrayList;
020 import java.util.List;
021 import java.util.Map;
022 import java.util.Set;
023
024 import javax.servlet.jsp.JspException;
025 import javax.servlet.jsp.JspWriter;
026 import javax.servlet.jsp.PageContext;
027 import javax.servlet.jsp.tagext.Tag;
028
029 import org.apache.commons.lang.StringUtils;
030 import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
031 import org.kuali.kfs.sys.context.SpringContext;
032 import org.kuali.kfs.sys.document.AccountingDocument;
033 import org.kuali.kfs.sys.document.datadictionary.AccountingLineGroupDefinition;
034 import org.kuali.kfs.sys.document.datadictionary.AccountingLineViewActionDefinition;
035 import org.kuali.kfs.sys.document.web.AccountingLineViewAction;
036 import org.kuali.rice.kns.authorization.AuthorizationConstants;
037 import org.kuali.rice.kns.service.KNSServiceLocator;
038 import org.kuali.rice.kns.service.KualiConfigurationService;
039 import org.kuali.rice.kns.web.taglib.html.KNSFileTag;
040 import org.kuali.rice.kns.web.taglib.html.KNSImageTag;
041
042 /**
043 * Renders the standard group header/import line
044 */
045 public class GroupTitleLineRenderer implements Renderer, CellCountCurious {
046 private int titleCellSpan = 4;
047 private int cellCount = 1;
048 private AccountingLineGroupDefinition accountingLineGroupDefinition;
049 private AccountingDocument accountingDocument;
050 private String lineCollectionProperty;
051 private KNSFileTag scriptFileTag = new KNSFileTag();
052 private KNSFileTag noscriptFileTag = new KNSFileTag();
053 private KNSImageTag uploadButtonTag = new KNSImageTag();
054 private KNSImageTag cancelButtonTag = new KNSImageTag();
055 private boolean shouldUpload = true;
056 private boolean canEdit = false;
057
058 private boolean groupActionsRendered = false;
059
060 /**
061 * Constructs a ImportLineRenderer, setting defaults on the tags that will always exist
062 */
063 public GroupTitleLineRenderer() {
064 scriptFileTag.setSize("30");
065 noscriptFileTag.setSize("30");
066 noscriptFileTag.setStyle("font:10px;height:16px;");
067 uploadButtonTag.setSrc(KNSServiceLocator.getKualiConfigurationService().getPropertyString("externalizable.images.url") + "tinybutton-add1.gif");
068 uploadButtonTag.setStyleClass("tinybutton");
069 cancelButtonTag.setProperty("methodToCall.cancel");
070 cancelButtonTag.setSrc(KNSServiceLocator.getKualiConfigurationService().getPropertyString("externalizable.images.url") + "tinybutton-cancelimport.gif");
071 cancelButtonTag.setStyleClass("tinybutton");
072 }
073
074 /**
075 * @see org.kuali.kfs.sys.document.web.renderers.Renderer#clear()
076 */
077 public void clear() {
078 cellCount = 1;
079 accountingLineGroupDefinition = null;
080 titleCellSpan = 4;
081 lineCollectionProperty = null;
082 accountingDocument = null;
083 shouldUpload = true;
084 canEdit = false;
085
086 // clean script file tag
087 scriptFileTag.setPageContext(null);
088 scriptFileTag.setParent(null);
089 scriptFileTag.setProperty(null);
090
091 // clean noscript file tag
092 noscriptFileTag.setPageContext(null);
093 noscriptFileTag.setParent(null);
094 noscriptFileTag.setProperty(null);
095
096 // clean upload button tag
097 uploadButtonTag.setPageContext(null);
098 uploadButtonTag.setParent(null);
099 uploadButtonTag.setProperty(null);
100 uploadButtonTag.setAlt(null);
101 uploadButtonTag.setTitle(null);
102
103 // clean cancel import tag
104 cancelButtonTag.setPageContext(null);
105 cancelButtonTag.setParent(null);
106 cancelButtonTag.setAlt(null);
107 cancelButtonTag.setTitle(null);
108 cancelButtonTag.setOnclick(null);
109 }
110
111 /**
112 * @see org.kuali.kfs.sys.document.web.renderers.Renderer#render(javax.servlet.jsp.PageContext, javax.servlet.jsp.tagext.Tag,
113 * org.kuali.core.bo.BusinessObject)
114 */
115 public void render(PageContext pageContext, Tag parentTag) throws JspException {
116 try {
117 pageContext.getOut().write(buildRowBeginning());
118
119 pageContext.getOut().write(buildTitleCell());
120 this.renderGroupLevelActions(pageContext, parentTag);
121
122 pageContext.getOut().write(buildRowEnding());
123 }
124 catch (IOException ioe) {
125 throw new JspException("Difficulty in rendering import/group header line", ioe);
126 }
127 }
128
129 /**
130 * Builds a tag for the row beginning
131 *
132 * @returns the String with the HTML for the row opening
133 */
134 protected String buildRowBeginning() {
135 return "<tr>";
136 }
137
138 /**
139 * Builds the tag for the row beginning
140 *
141 * @returns the String with the HTML for the row beginning
142 */
143 protected String buildRowEnding() {
144 return "</tr>";
145 }
146
147 protected void renderGroupLevelActions(PageContext pageContext, Tag parentTag) throws JspException {
148 JspWriter out = pageContext.getOut();
149
150 try {
151 out.write(this.buildGroupActionsBeginning());
152
153 this.renderGroupActions(pageContext, parentTag);
154
155 this.renderUploadCell(pageContext, parentTag);
156
157 out.write(this.buildGroupActionsColumnEnding());
158 }
159 catch (IOException ioe) {
160 throw new JspException("Difficulty rendering group level actions", ioe);
161 }
162 }
163
164 /**
165 * Builds a tag for the row beginning
166 *
167 * @returns the String with the HTML for the row opening
168 */
169 protected String buildGroupActionsBeginning() {
170 if (this.canUpload() || this.isGroupActionsRendered()) {
171 StringBuilder groupActionsBeginning = new StringBuilder();
172 final int width = cellCount - titleCellSpan;
173
174 groupActionsBeginning.append("<td ");
175 groupActionsBeginning.append("colspan=\"");
176 groupActionsBeginning.append(Integer.toString(width));
177 groupActionsBeginning.append("\" ");
178
179 groupActionsBeginning.append("class=\"tab-subhead-import\" ");
180 groupActionsBeginning.append("align=\"right\" ");
181 groupActionsBeginning.append("nowrap=\"nowrap\" ");
182 groupActionsBeginning.append("style=\"border-right: none;\"");
183 groupActionsBeginning.append(">");
184
185 return groupActionsBeginning.toString();
186 }
187
188 return StringUtils.EMPTY;
189 }
190
191 /**
192 * Builds the tag for the row beginning
193 *
194 * @returns the String with the HTML for the row beginning
195 */
196 protected String buildGroupActionsColumnEnding() {
197 return this.canUpload() || this.isGroupActionsRendered() ? "</td>" : StringUtils.EMPTY;
198 }
199
200 /**
201 * Builds the tags for the title cell of the import line
202 *
203 * @return the String with the HTML for the title cell
204 */
205 protected String buildTitleCell() {
206 StringBuilder titleCell = new StringBuilder();
207 int colSpan = (this.canUpload() || this.isGroupActionsRendered()) ? titleCellSpan : cellCount;
208
209 titleCell.append("<td ");
210
211 titleCell.append("colspan=\"");
212 titleCell.append(colSpan);
213 titleCell.append("\" ");
214
215 titleCell.append("class=\"tab-subhead\" ");
216
217 titleCell.append("style=\"border-right: none;\"");
218
219 titleCell.append(">");
220
221 titleCell.append(buildGroupAnchor());
222
223 titleCell.append(accountingLineGroupDefinition.getGroupLabel());
224
225 titleCell.append("</td>");
226
227 return titleCell.toString();
228 }
229
230 /**
231 * Builds the unique anchor for this group
232 *
233 * @return the unique anchor for this group
234 */
235 protected String buildGroupAnchor() {
236 return "<a name=\"accounting" + getGroupInfix() + "Anchor\"></a>";
237 }
238
239 protected void renderGroupActions(PageContext pageContext, Tag parentTag) throws JspException {
240 List<? extends AccountingLineViewActionDefinition> accountingLineGroupActions = accountingLineGroupDefinition.getAccountingLineGroupActions();
241 if (!this.isGroupActionsRendered() || accountingLineGroupActions == null || accountingLineGroupActions.isEmpty()) {
242 return;
243 }
244
245 List<AccountingLineViewAction> viewActions = new ArrayList<AccountingLineViewAction>();
246 for (AccountingLineViewActionDefinition action : accountingLineGroupActions) {
247 String actionMethod = action.getActionMethod();
248 String actionLabel = action.getActionLabel();
249 String imageName = SpringContext.getBean(KualiConfigurationService.class).getPropertyString("externalizable.images.url") + action.getImageName();
250
251 AccountingLineViewAction viewAction = new AccountingLineViewAction(actionMethod, actionLabel, imageName);
252 viewActions.add(viewAction);
253 }
254
255 if (!viewActions.isEmpty()) {
256 ActionsRenderer actionsRenderer = new ActionsRenderer();
257 actionsRenderer.setTagBeginning(" ");
258 actionsRenderer.setTagEnding(" ");
259 actionsRenderer.setPostButtonSpacing(" ");
260 actionsRenderer.setActions(viewActions);
261 actionsRenderer.render(pageContext, parentTag);
262 actionsRenderer.clear();
263 }
264 }
265
266 /**
267 * A dumb way to get the group infix that tries to figure out if it's dealing with a source or target line
268 *
269 * @return the String "source" or "target" to populate the buildGroupAnchor
270 */
271 protected String getGroupInfix() {
272 Class accountingLineClass = accountingLineGroupDefinition.getAccountingLineClass();
273 return (accountingLineClass.isAssignableFrom(SourceAccountingLine.class) ? "source" : "target");
274 }
275
276 /**
277 * Oy, the big one...this one actually renders instead of returning the HTML in a String. This is because it's kind of complex
278 * (and a likely target for future refactoring)
279 *
280 * @param pageContext the page contex to render to
281 * @param parentTag the tag that is requesting all the rendering
282 * @throws JspException thrown if something goes wrong
283 */
284 protected void renderUploadCell(PageContext pageContext, Tag parentTag) throws JspException {
285 JspWriter out = pageContext.getOut();
286
287 if (canUpload()) {
288 try {
289 String hideImport = getHideImportName();
290 String showImport = getShowImportName();
291 String showLink = getShowLinkName();
292 String uploadDiv = getUploadDivName();
293
294 out.write("\n<SCRIPT type=\"text/javascript\">\n");
295 out.write("<!--\n");
296 out.write("\tfunction " + hideImport + "() {\n");
297 out.write("\t\tdocument.getElementById(\"" + showLink + "\").style.display=\"inline\";\n");
298 out.write("\t\tdocument.getElementById(\"" + uploadDiv + "\").style.display=\"none\";\n");
299 out.write("\t}\n");
300 out.write("\tfunction " + showImport + "() {\n");
301 out.write("\t\tdocument.getElementById(\"" + showLink + "\").style.display=\"none\";\n");
302 out.write("\t\tdocument.getElementById(\"" + uploadDiv + "\").style.display=\"inline\";\n");
303 out.write("\t}\n");
304 out.write("\tdocument.write(\n");
305 out.write("\t\t'<a id=\"" + showLink + "\" href=\"#\" onclick=\"" + showImport + "();return false;\">' +\n");
306 out.write("\t\t'<img src=\"" + SpringContext.getBean(KualiConfigurationService.class).getPropertyString("externalizable.images.url") + "tinybutton-importlines.gif\" title=\"import file\" alt=\"import file\"' +\n");
307 out.write("\t\t'width=\"72\" border=\"0\">' +\n");
308 out.write("\t\t'</a>' +\n");
309 out.write("\t\t'<div id=\"" + uploadDiv + "\" style=\"display:none;\" >' +\n");
310
311 out.write("\t\t'");
312
313 scriptFileTag.setPageContext(pageContext);
314 scriptFileTag.setParent(parentTag);
315 scriptFileTag.setProperty(accountingLineGroupDefinition.getImportedLinePropertyPrefix() + "File");
316 scriptFileTag.doStartTag();
317 scriptFileTag.doEndTag();
318
319 out.write("' +\n");
320 out.write("\t\t'");
321
322 uploadButtonTag.setPageContext(pageContext);
323 uploadButtonTag.setParent(parentTag);
324 uploadButtonTag.setProperty("methodToCall.upload" + StringUtils.capitalize(accountingLineGroupDefinition.getImportedLinePropertyPrefix()) + "Lines");
325 uploadButtonTag.setAlt("insert " + accountingLineGroupDefinition.getGroupLabel() + " accounting lines");
326 uploadButtonTag.setTitle("insert " + accountingLineGroupDefinition.getGroupLabel() + " accounting lines");
327 uploadButtonTag.doStartTag();
328 uploadButtonTag.doEndTag();
329
330 out.write("' +\n");
331
332 out.write("\t\t'");
333
334 cancelButtonTag.setPageContext(pageContext);
335 cancelButtonTag.setParent(parentTag);
336 cancelButtonTag.setAlt("Cancel import of " + accountingLineGroupDefinition.getGroupLabel() + " accounting lines");
337 cancelButtonTag.setTitle("Cancel import of " + accountingLineGroupDefinition.getGroupLabel() + " accounting lines");
338 cancelButtonTag.setOnclick(getHideImportName() + "();return false;");
339 cancelButtonTag.doStartTag();
340 cancelButtonTag.doEndTag();
341
342 out.write("' +\n");
343
344 out.write("\t'</div>');\n");
345 out.write("\t//-->\n");
346 out.write("</SCRIPT>\n");
347 out.write("<NOSCRIPT>\n");
348 out.write("\tImport " + accountingLineGroupDefinition.getGroupLabel() + " lines\n");
349
350 noscriptFileTag.setPageContext(pageContext);
351 noscriptFileTag.setParent(parentTag);
352 noscriptFileTag.setProperty(accountingLineGroupDefinition.getImportedLinePropertyPrefix() + "File");
353 noscriptFileTag.doStartTag();
354 noscriptFileTag.doEndTag();
355
356 uploadButtonTag.doStartTag();
357 uploadButtonTag.doEndTag();
358
359 out.write("</NOSCRIPT>\n");
360 }
361 catch (IOException ioe) {
362 throw new JspException("Difficulty rendering accounting lines import upload", ioe);
363 }
364 }
365 }
366
367 /**
368 * @return the name of the line collection property, but in a form that is okay for javascript variable/function naming
369 */
370 protected String getVariableFriendlyLineCollectionProperty() {
371 return lineCollectionProperty.replaceAll("[^A-Za-z]", "_");
372 }
373
374 /**
375 * @return the name of the hide import function
376 */
377 protected String getHideImportName() {
378 return "hide" + getVariableFriendlyLineCollectionProperty() + "Import";
379 }
380
381 /**
382 * @return the name of the show import function
383 */
384 protected String getShowImportName() {
385 return "show" + getVariableFriendlyLineCollectionProperty() + "Import";
386 }
387
388 /**
389 * @return the name of the show link element
390 */
391 protected String getShowLinkName() {
392 return lineCollectionProperty + "ShowLink";
393 }
394
395 /**
396 * @return the name of the upload div
397 */
398 protected String getUploadDivName() {
399 return "upload" + lineCollectionProperty + "Div";
400 }
401
402 /**
403 * Determines if an upload can proceed for the accounting line group
404 *
405 * @return true if upload is possible, false otherwise
406 */
407 protected boolean canUpload() {
408 return (canEdit && accountingDocument.getAccountingLineParser() != null && shouldUpload);
409 }
410
411 /**
412 * Allows overriding of whether something can be uploaded - though this serves only to turn uploading more off, never more on
413 *
414 * @param allowUpload should we be allowed to upload?
415 */
416 public void overrideCanUpload(boolean allowUpload) {
417 this.shouldUpload = allowUpload;
418 }
419
420 /**
421 * Gets the cellCount attribute.
422 *
423 * @return Returns the cellCount.
424 */
425 public int getCellCount() {
426 return cellCount;
427 }
428
429 /**
430 * Sets the cellCount attribute value.
431 *
432 * @param cellCount The cellCount to set.
433 */
434 public void setCellCount(int cellCount) {
435 this.cellCount = cellCount;
436 }
437
438 /**
439 * Gets the accountingDocument attribute.
440 *
441 * @return Returns the accountingDocument.
442 */
443 public AccountingDocument getAccountingDocument() {
444 return accountingDocument;
445 }
446
447 /**
448 * Sets the accountingDocument attribute value.
449 *
450 * @param accountingDocument The accountingDocument to set.
451 */
452 public void setAccountingDocument(AccountingDocument accountingDocument) {
453 this.accountingDocument = accountingDocument;
454 }
455
456 /**
457 * Gets the accountingLineGroupDefinition attribute.
458 *
459 * @return Returns the accountingLineGroupDefinition.
460 */
461 public AccountingLineGroupDefinition getAccountingLineGroupDefinition() {
462 return accountingLineGroupDefinition;
463 }
464
465 /**
466 * Sets the accountingLineGroupDefinition attribute value.
467 *
468 * @param accountingLineGroupDefinition The accountingLineGroupDefinition to set.
469 */
470 public void setAccountingLineGroupDefinition(AccountingLineGroupDefinition accountingLineGroupDefinition) {
471 this.accountingLineGroupDefinition = accountingLineGroupDefinition;
472 }
473
474 /**
475 * Gets the titleCellSpan attribute.
476 *
477 * @return Returns the titleCellSpan.
478 */
479 public int getTitleCellSpan() {
480 return titleCellSpan;
481 }
482
483 /**
484 * Sets the titleCellSpan attribute value.
485 *
486 * @param titleCellSpan The titleCellSpan to set.
487 */
488 public void setTitleCellSpan(int titleCellSpan) {
489 this.titleCellSpan = titleCellSpan;
490 }
491
492 /**
493 * Gets the lineCollectionProperty attribute.
494 *
495 * @return Returns the lineCollectionProperty.
496 */
497 public String getLineCollectionProperty() {
498 return lineCollectionProperty;
499 }
500
501 /**
502 * Sets the lineCollectionProperty attribute value.
503 *
504 * @param lineCollectionProperty The lineCollectionProperty to set.
505 */
506 public void setLineCollectionProperty(String lineCollectionProperty) {
507 this.lineCollectionProperty = lineCollectionProperty;
508 }
509
510 /**
511 * Gets the groupActionsRendered attribute.
512 *
513 * @return Returns the groupActionsRendered.
514 */
515 public boolean isGroupActionsRendered() {
516 return groupActionsRendered;
517 }
518
519 /**
520 * Sets the groupActionsRendered attribute value.
521 *
522 * @param groupActionsRendered The groupActionsRendered to set.
523 */
524 public void setGroupActionsRendered(boolean groupActionsRenderred) {
525 this.groupActionsRendered = groupActionsRenderred;
526 }
527
528 /**
529 * Sets the canEdit attribute value.
530 * @param canEdit The canEdit to set.
531 */
532 public void setCanEdit(boolean canEdit) {
533 this.canEdit = canEdit;
534 }
535
536 }