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 }