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

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