source: trunk/grails-app/taglib/dbnp/studycapturing/WizardTagLib.groovy @ 568

Last change on this file since 568 was 568, checked in by duh, 10 years ago
  • bugfix for issue #98 (person selects are empty and add/modify do not work)
  • Property svn:keywords set to Date Author Rev
File size: 33.4 KB
Line 
1package dbnp.studycapturing
2
3import org.codehaus.groovy.grails.plugins.web.taglib.JavascriptTagLib
4import dbnp.studycapturing.*
5import dbnp.data.*
6import cr.co.arquetipos.crypto.Blowfish
7
8/**
9 * Wizard tag library
10 *
11 * @author Jeroen Wesbeek
12 * @since 20100113
13 * @package wizard
14 *
15 * Revision information:
16 * $Rev: 568 $
17 * $Author: duh $
18 * $Date: 2010-06-16 12:27:55 +0000 (wo, 16 jun 2010) $
19 */
20class WizardTagLib extends JavascriptTagLib {
21        // define the tag namespace (e.g.: <wizard:action ... />
22        static namespace = "wizard"
23
24        // define the AJAX provider to use
25        static ajaxProvider = "jquery"
26
27        // define default text field width
28        static defaultTextFieldSize = 25;
29
30        /**
31         * ajaxButton tag, this is a modified version of the default
32         * grails submitToRemote tag to work with grails webflows.
33         * Usage is identical to submitToRemote with the only exception
34         * that a 'name' form element attribute is required. E.g.
35         * <wizard:ajaxButton name="myAction" value="myButton ... />
36         *
37         * you can also provide a javascript function to execute after
38         * success. This behaviour differs from the default 'after'
39         * action which always fires after a button press...
40         *
41         * @see http://blog.osx.eu/2010/01/18/ajaxifying-a-grails-webflow/
42         * @see http://www.grails.org/WebFlow
43         * @see http://www.grails.org/Tag+-+submitToRemote
44         * @todo perhaps some methods should be moved to a more generic
45         *        'webflow' taglib or plugin
46         * @param Map attributes
47         * @param Closure body
48         */
49        def ajaxButton = { attrs, body ->
50                // get the jQuery version
51                def jQueryVersion = grailsApplication.getMetadata()['plugins.jquery']
52
53                // fetch the element name from the attributes
54                def elementName = attrs['name'].replaceAll(/ /, "_")
55
56                // javascript function to call after success
57                def afterSuccess = attrs['afterSuccess']
58
59                // src parameter?
60                def src = attrs['src']
61                def alt = attrs['alt']
62
63                // generate a normal submitToRemote button
64                def button = submitToRemote(attrs, body)
65
66                /**
67                 * as of now (grails 1.2.0 and jQuery 1.3.2.4) the grails webflow does
68                 * not properly work with AJAX as the submitToRemote button does not
69                 * handle and submit the form properly. In order to support webflows
70                 * this method modifies two parts of a 'normal' submitToRemote button:
71                 *
72                 * 1) replace 'this' with 'this.form' as the 'this' selector in a button
73                 *    action refers to the button and / or the action upon that button.
74                 *    However, it should point to the form the button is part of as the
75                 *    the button should submit the form data.
76                 * 2) prepend the button name to the serialized data. The default behaviour
77                 *    of submitToRemote is to remove the element name altogether, while
78                 *    the grails webflow expects a parameter _eventId_BUTTONNAME to execute
79                 *    the appropriate webflow action. Hence, we are going to prepend the
80                 *    serialized formdata with an _eventId_BUTTONNAME parameter.
81                 */
82                if (jQueryVersion =~ /^1.([1|2|3]).(.*)/) {
83                        // fix for older jQuery plugin versions
84                        button = button.replaceFirst(/data\:jQuery\(this\)\.serialize\(\)/, "data:\'_eventId_${elementName}=1&\'+jQuery(this.form).serialize()")
85                } else {
86                        // as of jQuery plugin version 1.4.0.1 submitToRemote has been modified and the
87                        // this.form part has been fixed. Consequently, our wrapper has changed as well...
88                        button = button.replaceFirst(/data\:jQuery/, "data:\'_eventId_${elementName}=1&\'+jQuery")
89                }
90
91                // add an after success function call?
92                // usefull for performing actions on success data (hence on refreshed
93                // wizard pages, such as attaching tooltips)
94                if (afterSuccess) {
95                        button = button.replaceFirst(/\.html\(data\)\;/, '.html(data);' + afterSuccess + ';')
96                }
97
98                // got an src parameter?
99                if (src) {
100                        def replace = 'type="image" src="' + src + '"'
101
102                        if (alt) replace = replace + ' alt="' + alt + '"'
103
104                        button = button.replaceFirst(/type="button"/, replace)
105                }
106
107                // replace double semi colons
108                button = button.replaceAll(/;{2,}/, ';')
109
110                // render button
111                out << button
112        }
113
114        /**
115         * generate a ajax submit JavaScript
116         * @see WizardTagLib::ajaxFlowRedirect
117         * @see WizardTagLib::baseElement (ajaxSubmitOnChange)
118         */
119        def ajaxSubmitJs = { attrs, body ->
120                // define AJAX provider
121                setProvider([library: ajaxProvider])
122
123                // got a function name?
124                def functionName = attrs.remove('functionName')
125                if (functionName && !attrs.get('name')) {
126                        attrs.name = functionName
127                }
128
129                // generate an ajax button
130                def button = this.ajaxButton(attrs, body)
131
132                // strip the button part to only leave the Ajax call
133                button = button.replaceFirst(/<[^\"]*onclick=\"/, '')
134                button = button.replaceFirst(/return false.*/, '')
135
136                // change form if a form attribute is present
137                if (attrs.get('form')) {
138            button = button.replace(
139                                "jQuery(this).parents('form:first')",
140                                "\$('" + attrs.get('form') + "')"
141                        )
142                }
143
144                out << button
145        }
146
147        /**
148         * generate ajax webflow redirect javascript
149         *
150         * As we have an Ajaxified webflow, the initial wizard page
151         * cannot contain a wizard form, as upon a failing submit
152         * (e.g. the form data does not validate) the form should be
153         * shown again. However, the Grails webflow then renders the
154         * complete initial wizard page into the success div. As this
155         * ruins the page layout (a page within a page) we want the
156         * initial page to redirect to the first wizard form to enter
157         * the webflow correctly. We do this by emulating an ajax post
158         * call which updates the wizard content with the first wizard
159         * form.
160         *
161         * Usage: <wizard:ajaxFlowRedirect form="form#wizardForm" name="next" url="[controller:'wizard',action:'pages']" update="[success:'wizardPage',failure:'wizardError']" />
162         * form = the form identifier
163         * name = the action to execute in the webflow
164         * update = the divs to update upon success or error
165         *
166         * OR: to generate a JavaScript function you can call yourself, use 'functionName' instead of 'name'
167         *
168         * Example initial webflow action to work with this javascript:
169         * ...
170         * mainPage {
171         *      render(view: "/wizard/index")
172         *      onRender {
173         *              flow.page = 1
174         *  }
175         *      on("next").to "pageOne"
176         * }
177         * ...
178         *
179         * @param Map attributes
180         * @param Closure body
181         */
182        def ajaxFlowRedirect = { attrs, body ->
183                // generate javascript
184                out << '<script type="text/javascript">'
185                out << '$(document).ready(function() {'
186                out << ajaxSubmitJs(attrs, body)
187                out << '});'
188                out << '</script>'
189        }
190
191        /**
192         * render the content of a particular wizard page
193         * @param Map attrs
194         * @param Closure body  (help text)
195         */
196        def pageContent = { attrs, body ->
197                // define AJAX provider
198                setProvider([library: ajaxProvider])
199
200                // render new body content
201                //      - this JavaScript variable is used by baseElement to workaround an IE
202                //        specific issue (double submit on onchange events). The hell with IE!
203                //        @see baseElement
204                out << '<script type="text/javascript">var lastRequestTime = 0;</script>'
205                out << render(template: "/wizard/common/tabs")
206                out << '<div class="content">'
207                out << body()
208                out << '</div>'
209                out << render(template: "/wizard/common/navigation")
210                out << render(template: "/wizard/common/error")
211        }
212
213        /**
214         * generate a base form element
215         * @param String inputElement name
216         * @param Map attributes
217         * @param Closure help content
218         */
219        def baseElement = { inputElement, attrs, help ->
220println ".rendering [" + inputElement + "] with name [" + attrs.get('name') + "] and value [" + ((attrs.value) ? attrs.get('value').toString() : "-") + "]"
221                // work variables
222                def internetExplorer = (request.getHeader("User-Agent") =~ /MSIE/)
223                def description = attrs.remove('description')
224                def addExampleElement = attrs.remove('addExampleElement')
225                def addExample2Element = attrs.remove('addExample2Element')
226                def helpText = help().trim()
227
228                // got an ajax onchange action?
229                def ajaxOnChange = attrs.remove('ajaxOnChange')
230                if (ajaxOnChange) {
231                        if (!attrs.onChange) attrs.onChange = ''
232
233                        // add onChange AjaxSubmit javascript
234                        if (internetExplorer) {
235                                //              - somehow IE submits these onchanges twice which messes up some parts of the wizard
236                                //                (especially the events page). In order to bypass this issue I have introduced an
237                                //                if statement utilizing the 'before' and 'after' functionality of the submitToRemote
238                                //                function. This check expects lastRequestTime to be in the global Javascript scope,
239                                //                (@see pageContent) and calculates the time difference in miliseconds between two
240                                //                onChange executions. If this is more than 100 miliseconds the request is executed,
241                                //                otherwise it will be ignored... --> 20100527 - Jeroen Wesbeek
242                                attrs.onChange += ajaxSubmitJs(
243                                        [
244                                                before: "var execute=true;try { var currentTime=new Date().getTime();execute = ((currentTime-lastRequestTime) > 100);lastRequestTime=currentTime;  } catch (e) {};if (execute) { 1",
245                                                after: "}",
246                                                functionName: ajaxOnChange,
247                                                url: attrs.get('url'),
248                                                update: attrs.get('update'),
249                                                afterSuccess: attrs.get('afterSuccess')
250                                        ],
251                                        ''
252                                )
253                        } else {
254                                // this another W3C browser that actually behaves as expected... damn you IE, DAMN YOU!
255                                attrs.onChange += ajaxSubmitJs(
256                                        [
257                                                functionName: ajaxOnChange,
258                                                url: attrs.get('url'),
259                                                update: attrs.get('update'),
260                                                afterSuccess: attrs.get('afterSuccess')
261                                        ],
262                                        ''
263                                )
264                        }
265                }
266
267                // execute inputElement call
268                def renderedElement = "$inputElement"(attrs)
269
270                // if false, then we skip this element
271                if (!renderedElement) return false
272
273                // render a form element
274                if (attrs.get('elementId')) {
275                out << '<div class="element" id="'+ attrs.remove('elementId') +'">'
276                } else {
277                        out << '<div class="element">'
278                }
279                out << ' <div class="description">'
280                out << description
281                out << ' </div>'
282                out << ' <div class="input">'
283                out << renderedElement
284                if (helpText.size() > 0) {
285                        out << '        <div class="helpIcon"></div>'
286                }
287
288                // add an disabled input box for feedback purposes
289                // @see dateElement(...)
290                if (addExampleElement) {
291                        def exampleAttrs = new LinkedHashMap()
292                        exampleAttrs.name = attrs.get('name') + 'Example'
293                        exampleAttrs.class = 'isExample'
294                        exampleAttrs.disabled = 'disabled'
295                        exampleAttrs.size = 30
296                        out << textField(exampleAttrs)
297                }
298
299                // add an disabled input box for feedback purposes
300                // @see dateElement(...)
301                if (addExample2Element) {
302                        def exampleAttrs = new LinkedHashMap()
303                        exampleAttrs.name = attrs.get('name') + 'Example2'
304                        exampleAttrs.class = 'isExample'
305                        exampleAttrs.disabled = 'disabled'
306                        exampleAttrs.size = 30
307                        out << textField(exampleAttrs)
308                }
309
310                out << ' </div>'
311
312                // add help content if it is available
313                if (helpText.size() > 0) {
314                        out << '  <div class="helpContent">'
315                        out << '    ' + helpText
316                        out << '  </div>'
317                }
318
319                out << '</div>'
320        }
321
322        /**
323         * render an ajaxButtonElement
324         * @param Map attrs
325         * @param Closure body  (help text)
326         */
327        def ajaxButtonElement = { attrs, body ->
328                baseElement.call(
329                        'ajaxButton',
330                        attrs,
331                        body
332                )
333        }
334
335        /**
336         * render a textFieldElement
337         * @param Map attrs
338         * @param Closure body  (help text)
339         */
340        def textFieldElement = { attrs, body ->
341                // set default size, or scale to max length if it is less than the default size
342                if (!attrs.get("size")) {
343                        if (attrs.get("maxlength")) {
344                                attrs.size = ((attrs.get("maxlength") as int) > defaultTextFieldSize) ? defaultTextFieldSize : attrs.get("maxlength")
345                        } else {
346                                attrs.size = defaultTextFieldSize
347                        }
348                }
349
350                // render template element
351                baseElement.call(
352                        'textField',
353                        attrs,
354                        body
355                )
356        }
357
358        /**
359         * render a textAreaElement
360         * @param Map attrs
361         * @param Closure body  (help text)
362         */
363        def textAreaElement = { attrs, body ->
364                // set default size, or scale to max length if it is less than the default size
365
366                // render template element
367                baseElement.call(
368                        'textArea',
369                        attrs,
370                        body
371                )
372        }
373
374
375        /**
376         * render a select form element
377         * @param Map attrs
378         * @param Closure body  (help text)
379         */
380        def selectElement = { attrs, body ->
381                baseElement.call(
382                        'select',
383                        attrs,
384                        body
385                )
386        }
387
388        /**
389         * render a checkBox form element
390         * @param Map attrs
391         * @param Closure body  (help text)
392         */
393        def checkBoxElement = { attrs, body ->
394                baseElement.call(
395                        'checkBox',
396                        attrs,
397                        body
398                )
399        }
400
401        /**
402         * render a set of radio form elements
403         * @param Map attrs
404         * @param Closure body  (help text)
405         */
406        def radioElement = { attrs, body ->
407                baseElement.call(
408                        'radioList',
409                        attrs,
410                        body
411                )
412        }
413
414        /**
415         * render a set of radio elements
416         * @param Map attrs
417         * @param Closure body  (help text)
418         */
419        def radioList = { attrs ->
420                def checked = true
421
422                attrs.elements.each {
423                        out << radio(
424                                name: attrs.name,
425                                value: it,
426                                checked: (attrs.value == it || (!attrs.value && checked))
427                        )
428                        out << it
429                        checked = false
430                }
431        }
432
433        /**
434         * render a dateElement
435         * NOTE: datepicker is attached through wizard.js!
436         * @param Map attrs
437         * @param Closure body  (help text)
438         */
439        def dateElement = { attrs, body ->
440                // transform value?
441                if (attrs.value instanceof Date) {
442                        // transform date instance to formatted string (dd/mm/yyyy)
443                        attrs.value = String.format('%td/%<tm/%<tY', attrs.value)
444                }
445
446                // add 'rel' field to identity the datefield using javascript
447                attrs.rel = 'date'
448
449                // set some textfield values
450                attrs.maxlength = (attrs.maxlength) ? attrs.maxlength : 10
451                attrs.addExampleElement = true
452
453                // render a normal text field
454                //out << textFieldElement(attrs,body)
455                textFieldElement.call(
456                        attrs,
457                        body
458                )
459        }
460
461        /**
462         * render a dateElement
463         * NOTE: datepicker is attached through wizard.js!
464         * @param Map attrs
465         * @param Closure body  (help text)
466         */
467        def timeElement = { attrs, body ->
468                // transform value?
469                if (attrs.value instanceof Date) {
470                        // transform date instance to formatted string (dd/mm/yyyy)
471                        attrs.value = String.format('%td/%<tm/%<tY %<tH:%<tM', attrs.value)
472                }
473
474                // add 'rel' field to identity the field using javascript
475                attrs.rel = 'datetime'
476
477                attrs.addExampleElement = true
478                attrs.addExample2Element = true
479                attrs.maxlength = 16
480
481                // render a normal text field
482                //out << textFieldElement(attrs,body)
483                textFieldElement.call(
484                        attrs,
485                        body
486                )
487        }
488
489        /**
490         * Button form element
491         * @param Map attributes
492         * @param Closure help content
493         */
494        def buttonElement = { attrs, body ->
495                // render template element
496                baseElement.call(
497                        'ajaxButton',
498                        attrs,
499                        body
500                )
501        }
502
503
504        /**
505         * Term form element
506         * @param Map attributes
507         * @param Closure help content
508         */
509        def termElement = { attrs, body ->
510                // render term element
511                baseElement.call(
512                        'termSelect',
513                        attrs,
514                        body
515                )
516        }
517
518        /**
519         * Term select element
520         * @param Map attributes
521         */
522        def termSelect = { attrs ->
523                def from = []
524println "termSelect --> " + attrs
525
526                // got ontologies?
527                if (attrs.ontologies) {
528                        // are the ontologies a string?
529                        if (attrs.ontologies instanceof String) {
530                                attrs.ontologies.split(/\,/).each() { ncboId ->
531                                        // trim the id
532                                        ncboId.trim()
533
534                                        // fetch all terms for this ontology
535                                        def ontology = Ontology.findAllByNcboId(ncboId)
536
537                                        // does this ontology exist?
538                                        if (ontology) {
539                                                ontology.each() {
540                                                        Term.findAllByOntology(it).each() {
541                                                                // key = ncboId:concept-id
542                                                                from[ from.size() ] = it.name
543                                                        }
544                                                }
545                                        }
546                                }
547                        } else if (attrs.ontologies instanceof Set) {
548                                // are they a set instead?
549                                def ontologyList = ""
550
551                                // iterate through set
552                                attrs.ontologies.each() { ontology ->
553                                        if (ontology) {
554                                                ontologyList += ontology.ncboId + ","
555
556                                                Term.findAllByOntology(ontology).each() {
557                                                        from[ from.size() ] = it.name
558                                                }
559
560                                                // strip trailing comma
561                                                attrs.ontologies = ontologyList[0..-2]
562                                        }
563                                }
564                        }
565
566                        // sort alphabetically
567                        from.sort()
568
569                        // add a dummy field?
570                        if (attrs.remove('addDummy')) {
571                                from.add(0,'')
572                        }
573
574                        // define 'from'
575                        attrs.from = from
576
577                        // add 'rel' attribute
578                        attrs.rel = 'term'
579
580                        out << select(attrs)
581                } else {
582                        out << "<b>ontologies missing!</b>"
583                }
584        }
585
586        /**
587         * Ontology form element
588         * @param Map attributes
589         * @param Closure help content
590         */
591        def ontologyElement = { attrs, body ->
592                // @see http://www.bioontology.org/wiki/index.php/NCBO_Widgets#Term-selection_field_on_a_form
593                // @see ontology-chooser.js, table-editor.js
594                baseElement.call(
595                        'textField',
596                        [
597                            name: attrs.name,
598                                value: attrs.value,
599                                description: attrs.description,
600                                rel: 'ontology-' + ((attrs.ontology) ? attrs.ontology : 'all'),
601                                size: 25
602                        ],
603                        body
604                )
605                out << hiddenField(
606                        name: attrs.name + '-concept_id'
607                )
608                out << hiddenField(
609                        name: attrs.name + '-ontology_id'
610                )
611                out << hiddenField(
612                        name: attrs.name + '-full_id'
613                )
614        }
615
616        /**
617         * Study form element
618         * @param Map attributes
619         * @param Closure help content
620         */
621        def studyElement = { attrs, body ->
622                // render study element
623                baseElement.call(
624                        'studySelect',
625                        attrs,
626                        body
627                )
628        }
629
630        /**
631         * render a study select element
632         * @param Map attrs
633         */
634        def studySelect = { attrs ->
635                // for now, just fetch all studies
636                attrs.from = Study.findAll()
637
638                // got a name?
639                if (!attrs.name) {
640                        attrs.name = "study"
641                }
642
643                // got result?
644                if (attrs.from.size() > 0) {
645                        out << select(attrs)
646                } else {
647                        // no, return false to make sure this element
648                        // is not rendered in the template
649                        return false
650                }
651        }
652
653        /**
654         * Template form element
655         * @param Map attributes
656         * @param Closure help content
657         */
658        def templateElement = { attrs, body ->
659                // add a rel element if it does not exist
660                if (!attrs.rel) {
661                        attrs.rel = 'template'
662                }
663               
664                // render template element
665                baseElement.call(
666                        'templateSelect',
667                        attrs,
668                        body
669                )
670        }
671
672        /**
673         * render a template select element
674         * @param Map attrs
675         */
676        def templateSelect = { attrs ->
677                def entity = attrs.remove('entity')
678
679                // add the entity class name to the element
680                // do we have crypto information available?
681                if (grailsApplication.config.crypto) {
682                        // generate a Blowfish encrypted and Base64 encoded string.
683                        attrs['entity'] = Blowfish.encryptBase64(
684                                entity.toString().replaceAll(/^class /, ''),
685                                grailsApplication.config.crypto.shared.secret
686                        )
687                } else {
688                        // base64 only; this is INSECURE! As this class
689                        // is instantiated elsewehere. Possibly exploitable!
690                        attrs['entity'] = entity.toString().replaceAll(/^class /, '').bytes.encodeBase64()
691                }
692
693                // fetch templates
694                attrs.from = (entity) ? Template.findAllByEntity(entity) : Template.findAll()
695
696                // got a name?
697                if (!attrs.name) {
698                        attrs.name = 'template'
699                }
700
701                // got result?
702                if (attrs.from.size() > 0 || attrs.get('addDummy')) {
703                        // transform all values into strings
704                        def from = []
705                        attrs.from.each { from[ from.size() ] = it.toString() }
706
707                        // sort alphabetically
708                        from.sort()
709
710                        // add a dummy field?
711                        if (attrs.remove('addDummy')) {
712                                from.add(0,'')
713                        }
714
715                        // set attributes
716                        attrs.from = from
717                        attrs.value = (attrs.value) ? attrs.value.toString() : ''
718
719                        // output select element
720                        out << select(attrs)
721                } else {
722                        // no, return false to make sure this element
723                        // is not rendered in the template
724                        return false
725                }
726        }
727
728
729        /**
730         * File form element
731         * @param Map attributes
732         * @param Closure help content
733         */
734        def fileFieldElement = { attrs, body ->
735                // render term element
736                baseElement.call(
737                        'fileField',
738                        attrs,
739                        body
740                )
741        }
742
743        /**
744         * file field.
745         * @param attributes
746         */
747        def fileField = { attrs ->
748                /*
749                out << '<input type="file" name="' + attrs.name + '"/>'
750                if( attrs.value ) {
751                        out << '<a href="' + resource(dir: '') + '/file/get/' + attrs.value + '" class="isExample">Now contains: ' + attrs.value + '</a>'
752                }
753                */
754
755                out << '<div id="upload_button_' + attrs.name + '" class="upload_button">Upload</div>';
756                out << '<input type="hidden" name="' + attrs.name + '" id="' + attrs.name + '" value="' + attrs.value + '">';
757                out << '<div id="' + attrs.name + 'Example" class="upload_info"></div>';
758                out << '<script type="text/javascript">';
759                out << '  $(document).ready( function() { ';
760                out << '    var filename = "' + attrs.value + '";';
761                out << '    fileUploadField( "' + attrs.name + '" );';
762                out << '    if( filename != "" ) {';
763                out << '      $("#' + attrs.name + 'Example").html("Current file: " + createFileHTML( filename ) )';
764                out << '    }';
765                out << '  } );';
766                out << "</script>\n";
767        }
768
769        /**
770         * Protocol form element
771         * @param Map attributes
772         * @param Closure help content
773         */
774        def protocolElement = { attrs, body ->
775                // render protocol element
776                baseElement.call(
777                        'protocolSelect',
778                        attrs,
779                        body
780                )
781        }
782
783        /**
784         * render a protocol select element
785         * @param Map attrs
786         */
787        def protocolSelect = { attrs ->
788                // fetch all protocold
789                attrs.from = Protocol.findAll() // for now, all protocols
790
791                // got a name?
792                if (!attrs.name) {
793                        attrs.name = 'protocol'
794                }
795
796                out << select(attrs)
797        }
798
799        def show = { attrs ->
800                // is object parameter set?
801                def o = attrs.object
802
803                println o.getProperties();
804                o.getProperties().each {
805                        println it
806                }
807
808                out << "!! test version of 'show' tag !!"
809        }
810
811        /**
812         * render table headers for all subjectFields in a template
813         * @param Map attributes
814         */
815        def templateColumnHeaders = { attrs ->
816                def entity              = (attrs.get('entity'))
817                def template    = (entity && entity instanceof TemplateEntity) ? entity.template : null
818
819                // got a template?
820                if (template) {
821                        // render template fields
822                        entity.giveFields().each() {
823                                def ucName              = it.name[0].toUpperCase() + it.name.substring(1)
824
825                                out << '<div class="' + attrs.get('class') + '">' + ucName + (it.unit ? " (${it.unit})" : '')
826                                if (it.comment) {
827                                        out << '<div class="helpIcon"></div>'
828                                        out << '<div class="helpContent">' + it.comment + '</div>'
829                                }
830                                out << '</div>'
831                        }
832                }
833        }
834
835        def templateColumns = { attrs ->
836                // render template fields as columns
837                attrs.renderType = 'column'
838                out << renderTemplateFields(attrs)
839        }
840
841        def templateElements = { attrs ->
842                // render template fields as form elements
843                attrs.renderType = 'element'
844                out << renderTemplateFields(attrs)
845        }
846
847        /**
848         * render form elements based on an entity's template
849         * @param Map attributes
850         * @param String body
851         */
852        def renderTemplateFields = { attrs ->
853                def renderType  = attrs.remove('renderType')
854                def entity              = (attrs.get('entity'))
855                def prependName = (attrs.get('name')) ? attrs.remove('name')+'_' : ''
856                def template    = (entity && entity instanceof TemplateEntity) ? entity.template : null
857                def inputElement= null
858                def addDummy    = (attrs.get('addDummy')) ? true : false
859
860                // got a template?
861                if (template) {
862                        // render template fields
863                        entity.giveFields().each() {
864                                def fieldValue  = entity.getFieldValue(it.name)
865                                def helpText    = (it.comment && renderType == 'element') ? it.comment : ''
866                                def ucName              = it.name[0].toUpperCase() + it.name.substring(1)
867
868                                // output column opening element?
869                                if (renderType == 'column') {
870                                        out << '<div class="' + attrs.get('class') + '">'
871                                }
872
873println ".SHOWING "+it.type.toString()
874
875                                switch (it.type.toString()) {
876                                        case ['STRING', 'INTEGER', 'FLOAT', 'DOUBLE']:
877                                                inputElement = (renderType == 'element') ? 'textFieldElement' : 'textField'
878                                                out << "$inputElement"(
879                                                        description: ucName,
880                                                        name: prependName + it.escapedName(),
881                                                        value: fieldValue
882                                                ){helpText}
883                                                break
884                                        case 'TEXT':
885                                                inputElement = (renderType == 'element') ? 'textAreaElement' : 'textField'
886                                                out << "$inputElement"(
887                                                        description: ucName,
888                                                        name: prependName + it.escapedName(),
889                                                        value: fieldValue
890                                                ){helpText}
891                                                break
892                                        case 'STRINGLIST':
893                                                inputElement = (renderType == 'element') ? 'selectElement' : 'select'
894                                                if (!it.listEntries.isEmpty()) {
895                                                        out << "$inputElement"(
896                                                                description: ucName,
897                                                                name: prependName + it.escapedName(),
898                                                                from: it.listEntries,
899                                                                value: fieldValue
900                                                        ){helpText}
901                                                } else {
902                                                        out << '<span class="warning">no values!!</span>'
903                                                }
904                                                break
905                                        case 'ONTOLOGYTERM':
906                                                // @see http://www.bioontology.org/wiki/index.php/NCBO_Widgets#Term-selection_field_on_a_form
907                                                // @see ontology-chooser.js
908                                                inputElement = (renderType == 'element') ? 'termElement' : 'termSelect'
909
910                                                if (it.ontologies) {
911                                                        out << "$inputElement"(
912                                                                description     : ucName,
913                                                                name            : prependName + it.escapedName(),
914                                                                value           : fieldValue.toString(),
915                                                                ontologies      : it.ontologies,
916                                                                addDummy        : addDummy
917                                                        ){helpText}
918                                                } else {
919                                                        out << "$inputElement"(
920                                                                description     : ucName,
921                                                                name            : prependName + it.escapedName(),
922                                                                value           : fieldValue.toString(),
923                                                                addDummy        : addDummy
924                                                        ){helpText}
925                                                }
926                                                break
927                                        case 'ONTOLOGYTERM-old':
928                                                // @see http://www.bioontology.org/wiki/index.php/NCBO_Widgets#Term-selection_field_on_a_form
929                                                // @see ontology-chooser.js
930                                                inputElement = (renderType == 'element') ? 'textFieldElement' : 'textField'
931                                                out << "$inputElement"(
932                                                        name: prependName + it.escapedName(),
933                                                        value: fieldValue,
934                                                        rel: 'ontology-all',
935                                                        size: 100
936                                                )
937                                                out << hiddenField(
938                                                        name: prependName + it.name + '-concept_id',
939                                                        value: fieldValue
940                                                )
941                                                out << hiddenField(
942                                                        name: prependName + it.escapedName() + '-ontology_id',
943                                                        value: fieldValue
944                                                )
945                                                out << hiddenField(
946                                                        name: prependName + it.escapedName() + '-full_id',
947                                                        value: fieldValue
948                                                )
949                                                break
950                                        case 'DATE':
951                                                inputElement = (renderType == 'element') ? 'dateElement' : 'textField'
952
953                                                // transform value?
954                                                if (fieldValue instanceof Date) {
955                                                        if (fieldValue.getHours() == 0 && fieldValue.getMinutes() == 0) {
956                                                                // transform date instance to formatted string (dd/mm/yyyy)
957                                                                fieldValue = String.format('%td/%<tm/%<tY', fieldValue)
958                                                        } else {
959                                                                // transform to date + time
960                                                                fieldValue = String.format('%td/%<tm/%<tY %<tH:%<tM', fieldValue)
961                                                        }
962                                                }
963
964                                                // render element
965                                                out << "$inputElement"(
966                                                        description: ucName,
967                                                        name: prependName + it.escapedName(),
968                                                        value: fieldValue,
969                                                        rel: 'date'
970                                                ){helpText}
971                                                break
972                                        case ['RELTIME']:
973                                                inputElement = (renderType == 'element') ? 'textFieldElement' : 'textField'
974                                                out << "$inputElement"(
975                                                        description: ucName,
976                                                        name: prependName + it.escapedName(),
977                                                        value: new RelTime( fieldValue ).toString(),
978                            addExampleElement: true,
979                            onBlur: 'showExampleReltime(this)'
980                                                ){helpText}
981                                                break
982                                        case ['FILE']:
983                                                inputElement = (renderType == 'element') ? 'fileFieldElement' : 'fileField'
984                                                out << "$inputElement"(
985                                                        description: ucName,
986                                                        name: prependName + it.escapedName(),
987                                                        value: fieldValue ? fieldValue : "",
988                            addExampleElement: true
989                                                ){helpText}
990                                                break
991                                        case ['BOOLEAN']:
992                                                inputElement = (renderType == 'element') ? 'checkBoxElement' : 'checkBox'
993                                                out << "$inputElement"(
994                                                        description: ucName,
995                                                        name: prependName + it.escapedName(),
996                                                        value: fieldValue
997                                                ){helpText}
998                                                break
999                                        default:
1000                                                // unsupported field type
1001                                                out << '<span class="warning">!' + it.type + '</span>'
1002                                                break
1003                                }
1004
1005                                // output column closing element?
1006                                if (renderType == 'column') {
1007                                        out << '</div>'
1008                                }
1009                        }
1010                }
1011        }
1012
1013        def PublicationSelectElement = { attrs, body ->
1014                attrs.description = 'Publications';
1015                // render list with publications currently available
1016                baseElement.call(
1017                        '_publicationList',
1018                        attrs,
1019                        body
1020                )
1021
1022                attrs.description = '';
1023
1024                // render 'Add publication button'
1025                baseElement.call(
1026                        '_publicationAddButton',
1027                        attrs,
1028                        body
1029                )
1030        }
1031
1032        /**
1033         * Renders a input box for publications
1034         */
1035        def publicationSelect = { attrs, body ->
1036                if (attrs.get('value') == null) {
1037                        attrs.value = [];
1038                }
1039                if (attrs.get('description') == null) {
1040                        attrs.description = '';
1041                }
1042                out << '<form id="' + attrs.name + '_form" onSubmit="return false;">';
1043                out << textField(
1044                        name: attrs.get("name"),
1045                        value: '',
1046                        rel: 'publication-pubmed',
1047                        style: 'width: 400px;'
1048                );
1049                out << '</form>';
1050                out << '<script type="text/javascript">';
1051                out << '  var onSelect = function( chooserObject, inputElement, event, ui ) { selectPubMedAdd( chooserObject, inputElement, event, ui ); enableButton( ".' + attrs.name + '_publication_dialog", "Add", true ); };'
1052                out << '  iField = $( "#' + attrs.get('name') + '" );';
1053                out << '  new PublicationChooser().initAutocomplete( iField, { "select" : onSelect } );';
1054                out << '</script>';
1055        }
1056
1057        def _publicationList = { attrs, body ->
1058                def display_none = 'none';
1059                if (!attrs.get('value') || attrs.get('value').size() == 0) {
1060                        display_none = 'inline';
1061                }
1062
1063                // Add a unordered list
1064                out << '<ul class="publication_list" id="' + attrs.name + '_list">';
1065
1066                out << '<li>';
1067                out << '<span class="publication_none" id="' + attrs.name + '_none" style="display: ' + display_none + ';">';
1068                out << 'No publications selected';
1069                out << '</span>';
1070                out << '</li>';
1071
1072                out << '</ul>';
1073
1074                // Add the publications using javascript
1075                out << '<script type="text/javascript">'
1076                if (attrs.get('value') && attrs.get('value').size() > 0) {
1077                        def i = 0;
1078                        attrs.get('value').each {
1079                                out << 'showPublication( ';
1080                                out << '  "' + attrs.name + '",';
1081                                out << '  ' + it.id + ',';
1082                                out << '  "' + it.title + '",';
1083                                out << '  "' + it.authorsList + '",';
1084                                out << '  ' + i++;
1085                                out << ');';
1086                        }
1087                }
1088                out << '</script>';
1089
1090                def ids;
1091                if (attrs.get('value') && attrs.get('value').size() > 0) {
1092                        ids = attrs.get('value').id.join(',')
1093                } else {
1094                        ids = '';
1095                }
1096                out << '<input type="hidden" name="' + attrs.name + '_ids" value="' + ids + '" id="' + attrs.name + '_ids">';
1097        }
1098
1099        def _publicationAddButton = { attrs, body ->
1100
1101                // Output the dialog for the publications
1102                out << '<div id="' + attrs.name + '_dialog">';
1103                out << '<p>Search for a publication on pubmed. You can search on a part of the title or authors. </p>';
1104                out << publicationSelect(attrs, body);
1105                out << '</div>';
1106                out << '<script type="text/javascript">';
1107                out << '  createPublicationDialog( "' + attrs.name + '" );'
1108                out << '</script>';
1109
1110                out << '<input type="button" onClick="openPublicationDialog(\'' + attrs.name + '\' );" value="Add Publication">';
1111        }
1112
1113        def ContactSelectElement = { attrs, body ->
1114
1115                attrs.description = 'Contacts';
1116                // render list with publications currently available
1117                baseElement.call(
1118                        '_contactList',
1119                        attrs,
1120                        body
1121                )
1122
1123                attrs.description = '';
1124
1125                // render 'publications list'
1126                out << '<div id="' + attrs.name + '_dialog" class="contacts_dialog" style="display: none;">'
1127                baseElement.call(
1128                        '_personSelect',
1129                        attrs,
1130                        body
1131                )
1132                baseElement.call(
1133                        '_roleSelect',
1134                        attrs,
1135                        body
1136                )
1137                baseElement.call(
1138                        '_contactAddButtonAddition',
1139                        attrs,
1140                        body
1141                )
1142                out << '</div>';
1143
1144                // render 'Add contact button'
1145                baseElement.call(
1146                        '_contactAddDialogButton',
1147                        attrs,
1148                        body
1149                )
1150        }
1151
1152        def _contactList = { attrs, body ->
1153                def display_none = 'none';
1154                if (!attrs.get('value') || attrs.get('value').size() == 0) {
1155                        display_none = 'inline';
1156                }
1157
1158                // Add a unordered list
1159                out << '<ul class="contact_list" id="' + attrs.name + '_list">';
1160
1161                out << '<li>';
1162                out << '<span class="contacts_none" id="' + attrs.name + '_none" style="display: ' + display_none + ';">';
1163                out << 'No contacts selected';
1164                out << '</span>';
1165                out << '</li>';
1166
1167                out << '</ul>';
1168
1169                // Add the contacts using javascript
1170                out << '<script type="text/javascript">'
1171                if (attrs.get('value') && attrs.get('value').size() > 0) {
1172                        def i = 0;
1173                        attrs.get('value').each {
1174                                out << 'showContact( ';
1175                                out << '  "' + attrs.name + '",';
1176                                out << '  "' + it.person.id + '-' + it.role.id + '",';
1177                                out << '  "' + it.person.lastName + ', ' + it.person.firstName + (it.person.prefix ? ' ' + it.person.prefix : '') + '",';
1178                                out << '  "' + it.role.name + '",';
1179                                out << '  ' + i++;
1180                                out << ');';
1181                        }
1182                }
1183                out << '</script>';
1184
1185                def ids = '';
1186                if (attrs.get('value') && attrs.get('value').size() > 0) {
1187                        ids = attrs.get('value').collect { it.person.id + '-' + it.role.id }
1188                        ids = ids.join(',');
1189                }
1190                out << '<input type="hidden" name="' + attrs.name + '_ids" value="' + ids + '" id="' + attrs.name + '_ids">';
1191        }
1192
1193        def _contactAddSelect = { attrs, body ->
1194                out << _personSelect(attrs) + _roleSelect(attrs);
1195        }
1196
1197        def _contactAddButtonAddition = { attrs, body ->
1198                out << '<input type="button" onClick="addContact ( \'' + attrs.name + '\' ); $(\'#' + attrs.name + '_dialog\').hide(); $( \'#' + attrs.name + '_dialogButton\' ).show();" value="Add">';
1199                out << '<input type="button" onClick="$(\'#' + attrs.name + '_dialog\').hide(); $( \'#' + attrs.name + '_dialogButton\' ).show();" value="Close">';
1200        }
1201
1202        def _contactAddDialogButton = { attrs, body ->
1203                out << '<input type="button" onClick="$( \'#' + attrs.name + '_dialog\' ).show(); $(this).hide();" id="' + attrs.name + '_dialogButton" value="Add Contact">';
1204        }
1205        /**
1206         * Person select element
1207         * @param Map attributes
1208         */
1209        def _personSelect = { attrs ->
1210                def selectAttrs = new LinkedHashMap();
1211
1212                // define 'from'
1213                def persons = Person.findAll().sort({ a, b -> a.lastName == b.lastName ? (a.firstName <=> b.firstName) : (a.lastName <=> b.lastName) } as Comparator);
1214                selectAttrs.from = persons.collect { it.lastName + ', ' + it.firstName + (it.prefix ? ' ' + it.prefix : '') }
1215                selectAttrs.keys = persons.id;
1216
1217                // add 'rel' attribute
1218                selectAttrs.rel = 'person'
1219                selectAttrs.name = attrs.name + '_person';
1220
1221                // add a dummy field
1222                selectAttrs.from.add(0,'')
1223
1224                out << "Person: " + select(selectAttrs)
1225        }
1226
1227        /**
1228         * Role select element
1229         * @param Map attributes
1230         */
1231        def _roleSelect = { attrs ->
1232                println(attrs);
1233                def selectAttrs = new LinkedHashMap();
1234
1235                // define 'from'
1236                def roles = PersonRole.findAll();
1237                selectAttrs.from = roles.collect { it.name };
1238                selectAttrs.keys = roles.id;
1239
1240                // add 'rel' attribute
1241                selectAttrs.rel = 'role'
1242                selectAttrs.name = attrs.name + '_role';
1243
1244                // add a dummy field
1245                selectAttrs.from.add(0,'')
1246
1247                out << "Role: " + select(selectAttrs)
1248        }
1249}
Note: See TracBrowser for help on using the repository browser.