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