001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.tag; 020 021import java.io.IOException; 022import java.util.Iterator; 023import java.util.LinkedHashMap; 024import java.util.Map; 025import java.util.Map.Entry; 026 027import lucee.commons.lang.IDGenerator; 028import lucee.commons.lang.StringUtil; 029import lucee.runtime.exp.ApplicationException; 030import lucee.runtime.exp.PageException; 031import lucee.runtime.ext.tag.BodyTagImpl; 032import lucee.runtime.functions.dynamicEvaluation.DE; 033import lucee.runtime.functions.string.JSStringFormat; 034import lucee.runtime.net.http.ReqRspUtil; 035import lucee.runtime.op.Caster; 036import lucee.runtime.op.Decision; 037import lucee.runtime.tag.util.DeprecatedUtil; 038import lucee.runtime.type.Collection.Key; 039import lucee.runtime.type.Struct; 040import lucee.runtime.type.StructImpl; 041import lucee.runtime.type.util.KeyConstants; 042 043/** 044 * implementation of the form tag 045 */ 046public final class Form extends BodyTagImpl { 047 048 public static final int FORMAT_HTML = 0; 049 public static final int FORMAT_FLASH = 1; 050 public static final int FORMAT_XML = 2; 051 private static final String DEFAULT_ARCHIVE = "/lucee/lucee-applet.cfm"; 052 053 private static final int WMODE_WINDOW = 0; 054 private static final int WMODE_TRANSPARENT = 1; 055 private static final int WMODE_OPAQUE = 2; 056 057 058 //private static int _count=0; 059 private int count=0; 060 private String name; 061 private String action; 062 private boolean preserveData; 063 private String onsubmit; 064 private String onreset; 065 private String onload; 066 private String passthrough; 067 private String method="post"; 068 private String scriptSrc; 069 070 private int format=FORMAT_HTML; 071 072 073 074 private Struct attributes=new StructImpl(); 075 private Map inputs=new LinkedHashMap(); 076 private String strSkin; 077 private String archive=null; 078 private String codebase=null; // TODO muss einen wert haben -> /lucee - context/classes/cf-j2re-win.cab.cfm 079 private String height="100%"; 080 private String width="100%"; 081 private boolean preloader=true; 082 private int timeout=0; 083 private int wMode=WMODE_WINDOW; 084 private boolean accessible=false; 085 private String onError; 086 087 088 @Override 089 public void release() { 090 super.release(); 091 name=null; 092 action=null; 093 preserveData=false; 094 attributes.clear(); 095 onsubmit=null; 096 onreset=null; 097 onload=null; 098 passthrough=null; 099 method="post"; 100 scriptSrc=null; 101 strSkin=null; 102 archive=null; 103 codebase=null; 104 height="100%"; 105 width="100%"; 106 preloader=true; 107 timeout=0; 108 wMode=WMODE_WINDOW; 109 accessible=false; 110 onError=null; 111 inputs.clear(); 112 } 113 114 115 /** 116 * @param enablecab The enablecab to set. 117 * @throws ApplicationException 118 */ 119 public void setEnablecab(boolean enablecab) { 120 DeprecatedUtil.tagAttribute(pageContext,"Form", "enablecab"); 121 122 } 123 /** 124 * @param method The method to set. 125 * @throws ApplicationException 126 */ 127 public void setMethod(String method) throws ApplicationException { 128 method=method.trim().toLowerCase(); 129 if(method.equals("get") || method.equals("post")) 130 this.method = method; 131 else 132 throw new ApplicationException("invalid value for attribute method from tag form, attribute can have value [get,post] but now is ["+method+"]"); 133 } 134 135 136 /** 137 * @param format the format to set 138 * @throws ApplicationException 139 */ 140 public void setFormat(String strFormat) throws ApplicationException { 141 strFormat=strFormat.trim().toLowerCase(); 142 143 if("html".equals(strFormat)) format=FORMAT_HTML; 144 else if("xml".equals(strFormat)) format=FORMAT_XML; 145 else if("flash".equals(strFormat)) format=FORMAT_FLASH; 146 147 else throw new ApplicationException("invalid value ["+strFormat+"] for attribute format, for this attribute only the following values are supported " + 148 "[xml, html, flash]"); 149 150 if(format!=FORMAT_HTML) 151 throw new ApplicationException("format ["+strFormat+"] is not supported, only the following formats are supported [html]"); 152 // TODO support other formats 153 } 154 155 /** 156 * @param skin The skin to set. 157 */ 158 public void setSkin(String strSkin) { 159 this.strSkin=strSkin; 160 } 161 162 /** 163 * @param action The action to set. 164 */ 165 public void setAction(String action) { 166 this.action = action; 167 } 168 169 /** 170 * @param scriptSrc The scriptSrc to set. 171 */ 172 public void setScriptsrc(String scriptSrc) { 173 this.scriptSrc = scriptSrc; 174 } 175 176 /** 177 * @param archive The archive to set. 178 * @throws ApplicationException 179 */ 180 public void setArchive(String archive) { 181 archive=archive.trim().toLowerCase().replace('\\', '/'); 182 if(!StringUtil.startsWith(archive, '/')) archive="/"+archive; 183 this.archive = archive; 184 } 185 /** 186 * @param codebase The codebase to set. 187 * @throws ApplicationException 188 */ 189 public void setCodebase(String codebase) { 190 this.codebase = codebase; 191 } 192 /** 193 * @param cssclass The cssclass to set. 194 */ 195 public void setClass(String cssclass) { 196 attributes.setEL("class",cssclass); 197 } 198 /** 199 * @param cssstyle The cssstyle to set. 200 */ 201 public void setStyle(String cssstyle) { 202 attributes.setEL("style",cssstyle); 203 } 204 /** 205 * @param enctype The enctype to set. 206 */ 207 public void setEnctype(String enctype) { 208 attributes.setEL("enctype",enctype); 209 } 210 /** 211 * @param id The id to set. 212 */ 213 public void setId(String id) { 214 attributes.setEL("id",id); 215 } 216 217 public void setAccept(String accept) { 218 attributes.setEL("accept",accept); 219 } 220 221 public void setAcceptcharset(String accept_charset) { 222 attributes.setEL("accept-charset",accept_charset); 223 } 224 225 public void setAccept_charset(String accept_charset) { 226 attributes.setEL("accept-charset",accept_charset); 227 } 228 /** 229 * @param name The name to set. 230 * @throws ApplicationException 231 */ 232 public void setName(String name) throws ApplicationException { 233 this.name=name; 234 checkName(name); 235 } 236 237 private static void checkName(String name) throws ApplicationException { 238 if(name.length()==0)return; 239 int len=name.length(); 240 241 for(int pos=0;pos<len;pos++) { 242 char c=name.charAt(pos); 243 if((c>='a' && c<='z')||(c>='A' && c<='Z')||(c>='0' && c<='9')||(c=='_')||(c=='-')||(c==':')||(c=='.')) 244 continue; 245 throw new ApplicationException("value of attribute name ["+name+"] is invalid, only the following characters are allowed [a-z,A-Z,0-9,-,_,:,.]"); 246 } 247 } 248 249 /** 250 * @param onreset The onreset to set. 251 */ 252 public void setOnreset(String onreset) { 253 this.onreset=onreset; 254 } 255 256 /** 257 * @param onreset The onreset to set. 258 */ 259 public void setOnload(String onload) { 260 this.onload=onload; 261 } 262 263 /** 264 * @param onsubmit The onsubmit to set. 265 */ 266 public void setOnsubmit(String onsubmit) { 267 this.onsubmit = onsubmit; 268 } 269 270 public void setOnerror(String onError) { 271 this.onError=onError; 272 } 273 274 public void setOnclick(String onclick) { 275 attributes.setEL("onclick",onclick); 276 } 277 278 public void setOndblclick(String ondblclick) { 279 attributes.setEL("ondblclick",ondblclick); 280 } 281 282 public void setOnmousedown(String onmousedown) { 283 attributes.setEL("onmousedown",onmousedown); 284 } 285 286 public void setOnmouseup(String onmouseup) { 287 attributes.setEL("onmouseup",onmouseup); 288 } 289 290 public void setOnmouseover(String onmouseover) { 291 attributes.setEL("onmouseover",onmouseover); 292 } 293 294 public void setOnmousemove(String onmousemove) { 295 attributes.setEL("onmousemove",onmousemove); 296 } 297 298 public void setOnmouseout(String onmouseout) { 299 attributes.setEL("onmouseout",onmouseout); 300 } 301 302 public void setOnkeypress(String onkeypress) { 303 attributes.setEL("onkeypress",onkeypress); 304 } 305 306 public void setOnkeydown(String onkeydown) { 307 attributes.setEL("onkeydown",onkeydown); 308 } 309 310 public void setOnkeyup(String onkeyup) { 311 attributes.setEL("onkeyup",onkeyup); 312 } 313 314 /** 315 * @param passthrough The passthrough to set. 316 * @throws PageException 317 */ 318 public void setPassthrough(Object passthrough) throws PageException { 319 if(passthrough instanceof Struct) { 320 Struct sct = (Struct) passthrough; 321 //lucee.runtime.type.Collection.Key[] keys=sct.keys(); 322 //lucee.runtime.type.Collection.Key key; 323 Iterator<Entry<Key, Object>> it = sct.entryIterator(); 324 Entry<Key, Object> e; 325 while(it.hasNext()) { 326 e = it.next(); 327 attributes.setEL(e.getKey(),e.getValue()); 328 } 329 } 330 else this.passthrough = Caster.toString(passthrough); 331 } 332 /** 333 * @param preserveData The preserveData to set. 334 * @throws ApplicationException 335 */ 336 public void setPreservedata(boolean preserveData) throws ApplicationException { 337 //this.preserveData = preserveData; 338 if(preserveData)throw new ApplicationException("attribute preserveData for tag form is not supported at the moment"); 339 } 340 /** 341 * @param target The target to set. 342 */ 343 public void setTarget(String target) { 344 attributes.setEL("target",target); 345 } 346 347 public void setTitle(String title) { 348 attributes.setEL("title",title); 349 } 350 351 public void setDir(String dir) { 352 attributes.setEL("dir",dir); 353 } 354 355 public void setLang(String lang) { 356 attributes.setEL("lang",lang); 357 } 358 359 /** 360 * @param height the height to set 361 */ 362 public void setHeight(String height) { 363 this.height=height; 364 } 365 366 /** 367 * @param width the width to set 368 */ 369 public void setWidth(String width) { 370 this.width=width; 371 } 372 373 /** 374 * @param preloader the preloader to set 375 */ 376 public void setPreloader(boolean preloader) { 377 this.preloader=preloader; 378 } 379 380 /** 381 * @param timeout the timeout to set 382 */ 383 public void setTimeout(double timeout) { 384 this.timeout=(int)timeout; 385 } 386 387 /** 388 * @param strWMode the wmode to set 389 * @throws ApplicationException 390 */ 391 public void setWmode(String strWMode) throws ApplicationException { 392 strWMode=strWMode.toLowerCase().trim(); 393 if("window".equals(strWMode)) wMode=WMODE_WINDOW; 394 else if("transparent".equals(strWMode)) wMode=WMODE_TRANSPARENT; 395 else if("opaque".equals(strWMode)) wMode=WMODE_OPAQUE; 396 397 else throw new ApplicationException("invalid value ["+strWMode+"] for attribute wmode, for this attribute only the following values are supported " + 398 "[window, transparent, opaque]"); 399 } 400 401 /** 402 * @param strWMode the wmode to set 403 */ 404 public void setAccessible(boolean accessible) { 405 this.accessible=accessible; 406 } 407 408 @Override 409 public int doStartTag() throws PageException { 410 411 try { 412 return _doStartTag(); 413 } 414 catch (IOException e) { 415 throw Caster.toPageException(e); 416 } 417 } 418 private int _doStartTag() throws PageException, IOException { 419 String contextPath = pageContext. getHttpServletRequest().getContextPath(); 420 if(contextPath==null) contextPath=""; 421 if(archive==null) { 422 archive=contextPath+DEFAULT_ARCHIVE; 423 } 424 count=IDGenerator.intId(); 425 426 if(name==null) { 427 name="CFForm_"+count; 428 } 429 attributes.setEL(KeyConstants._name,name); 430 431 if(action==null) action=ReqRspUtil.self(pageContext. getHttpServletRequest()); 432 attributes.setEL(KeyConstants._action,action); 433 434 String suffix=StringUtil.isEmpty(name)?""+count:StringUtil.toVariableName(name); 435 String funcName="lucee_form_"+count; 436 437 String checkName="_CF_check"+suffix; 438 String resetName="_CF_reset"+suffix; 439 String loadName="_CF_load"+suffix; 440 441 442 443 //boolean hasListener=false; 444 if(onsubmit==null) attributes.setEL("onsubmit","return "+funcName+".check();"); 445 else { 446 attributes.setEL("onsubmit","return "+checkName+"();"); 447 //hasListener=true; 448 } 449 if(onreset!=null) { 450 attributes.setEL("onreset",resetName+"();"); 451 //hasListener=true; 452 } 453 if(onload!=null) { 454 attributes.setEL("onload",loadName+"();"); 455 //hasListener=true; 456 } 457 458 if(scriptSrc==null)scriptSrc=contextPath+"/lucee/form.cfm"; 459 attributes.setEL("method",method); 460 461 pageContext.forceWrite("<script language = \"JavaScript\" type=\"text/javascript\" src=\""+scriptSrc+"\"></script>"); 462 //if(hasListener) { 463 pageContext.forceWrite("<script language = \"JavaScript\" type=\"text/javascript\">\n"); 464 if(onsubmit!=null)pageContext.forceWrite("function "+checkName+"() { if("+funcName+".check()){"+onsubmit+"\nreturn true;}else {return false;}}\n"); 465 else pageContext.forceWrite("function "+checkName+"() { return "+funcName+".check();}\n"); 466 467 468 if(onreset!=null)pageContext.forceWrite("function "+resetName+"() {"+onreset+"\n}\n"); 469 if(onload!=null)pageContext.forceWrite("function "+loadName+"() {"+onload+"\n}\n"); 470 pageContext.forceWrite("\n</script>"); 471 472 //} 473 pageContext.forceWrite("<form"); 474 475 Iterator<Entry<Key, Object>> it = attributes.entryIterator(); 476 Entry<Key, Object> e; 477 while(it.hasNext()) { 478 e = it.next(); 479 pageContext.forceWrite(" "); 480 pageContext.forceWrite(e.getKey().getString()); 481 pageContext.forceWrite("="); 482 pageContext.forceWrite(de(Caster.toString(e.getValue()))); 483 484 } 485 486 if(passthrough!=null) { 487 pageContext.forceWrite(" "); 488 pageContext.forceWrite(passthrough); 489 } 490 pageContext.forceWrite(">"); 491 492 return EVAL_BODY_INCLUDE; 493 } 494 495 @Override 496 public int doEndTag() throws PageException { 497 String funcName="lucee_form_"+count; 498 try { 499 pageContext.forceWrite("</form><!-- name:"+name+" --><script>\n"); 500 pageContext.forceWrite(funcName+"=new LuceeForms("+js(name)+","+js(onError)+");\n"); 501 Iterator it = inputs.keySet().iterator(); 502 while(it.hasNext()) { 503 InputBean input=(InputBean) inputs.get(it.next()); 504 505 pageContext.forceWrite(funcName+".addInput("+js(input.getName())+","+input.isRequired()+ 506 ","+input.getType()+","+input.getValidate()+ 507 ","+(input.getPattern())+","+js(input.getMessage())+ 508 ","+js(input.getOnError())+","+js(input.getOnValidate())+ 509 ","+range(input.getRangeMin())+","+range(input.getRangeMax())+ 510 ","+(input.getMaxLength())+ 511 ");\n"); 512 } 513 pageContext.forceWrite("</script>"); 514 } 515 catch (IOException e) { 516 throw Caster.toPageException(e); 517 } 518 return EVAL_PAGE; 519 } 520 521 private String range(double range) { 522 if(!Decision.isValid(range)) return "null"; 523 return Caster.toString(range); 524 } 525 526 527 private String de(String str) { 528 return DE.call(pageContext,str); 529 } 530 private String js(String str) { 531 if(str==null) return "null"; 532 return "'"+JSStringFormat.call(pageContext,str)+"'"; 533 } 534 535 536 /** 537 * @param input 538 * @throws ApplicationException 539 */ 540 public void setInput(InputBean input) throws ApplicationException { 541 if(input.getType()==Input.TYPE_TEXT || input.getType()==Input.TYPE_PASSWORD) { 542 InputBean i=(InputBean)inputs.get(input.getName().toLowerCase()); 543 if(i!=null && (i.getType()==Input.TYPE_TEXT || i.getType()==Input.TYPE_PASSWORD)) { 544 throw new ApplicationException("duplicate input field ["+i.getName()+"] for form","a text or password field must be unique"); 545 } 546 } 547 548 //if(StringUtil.isEmpty(input.getOnError(),true) && !StringUtil.isEmpty(onError,true)) 549 // input.setOnError(onError); 550 551 inputs.put(input.getName().toLowerCase(),input); 552 } 553 554 /** 555 * @return Returns the name. 556 */ 557 public String getName() { 558 return name; 559 } 560 /** 561 * @return Returns the onsubmit. 562 */ 563 public String getOnsubmit() { 564 return onsubmit; 565 } 566 567 public int getFormat() { 568 return format; 569 } 570 571 572 public String getArchive() { 573 return archive; 574 } 575 576 577 public String getCodebase() { 578 return codebase; 579 } 580}