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