source: trunk/grails-app/controllers/dbnp/studycapturing/TemplateEditorController.groovy @ 567

Last change on this file since 567 was 567, checked in by duh, 11 years ago
  • developmental commit for bug # 107

-- turning off all bootstrapping to ensure that all possible bugs do really surface and are not obscured by bootstrap hacks / dummy data
-- SelectAddMore?.js now support the passing of multiple values to a dialog
-- in the subject page of the wizard the template select now opens a dialog passing on the entity AND the ontologies
-- templateEditor has been extended to pass ontologies as well

  • Property svn:keywords set to Date Author Rev
File size: 23.4 KB
Line 
1/**
2 * TemplateEditorController Controler
3 *
4 * Webflow driven template editor
5 *
6 * @author  Jeroen Wesbeek
7 * @since       20100415
8 * @package     studycapturing
9 *
10 * Revision information:
11 * $Rev: 567 $
12 * $Author: duh $
13 * $Date: 2010-06-16 11:55:55 +0000 (wo, 16 jun 2010) $
14 */
15package dbnp.studycapturing
16import dbnp.data.*
17import dbnp.studycapturing.*
18import cr.co.arquetipos.crypto.Blowfish
19import grails.converters.*
20import java.lang.reflect.*;
21
22class TemplateEditorController {
23    def entityName;
24    def entity;
25
26    /**
27     * index closure
28     */
29    def index = {
30        // Check whether a right entity is given
31        if( !_checkEntity() ) {
32                        return
33                }
34
35        // fetch all templates for this entity
36        def templates = Template.findAllByEntity(entity)
37
38                // Generate a human readable entity name
39                def parts = entityName.tokenize( '.' );
40                def humanReadableEntity = parts[ parts.size() - 1 ];
41
42        return [
43            entity: entity,
44            templates: templates,
45            encryptedEntity: params.entity,
46            humanReadableEntity: humanReadableEntity,
47                        ontologies: params.ontologies
48        ];
49    }
50
51        /**
52         * Shows the editing of a template
53         */
54        def template = {
55        // Check whether a right entity is given
56        if( !_checkEntity() ) {
57                        return
58                }
59
60        // Check whether a template is selected. If not, redirect the user to the index
61        def selectedTemplate = params.template;
62        def template = null;
63                def domainFields = null;
64
65        if( selectedTemplate ) {
66            template = Template.get( selectedTemplate );
67
68                        // Find the domain classes for this template/entity
69                        // See http://www.javaworld.com/javaworld/javaqa/1999-07/06-qa-invoke.html
70                        Method m = template.entity.getDeclaredMethod("giveDomainFields", null);
71                        domainFields = m.invoke( null, null );
72        } else {
73                        redirect(action:"index",params:[entity:params.entity])
74                        return;
75                }
76
77        // fetch all templates for this entity
78        def templates = Template.findAllByEntity(entity)
79
80                // Generate a human readable entity name
81                def parts = entityName.tokenize( '.' );
82                def humanReadableEntity = parts[ parts.size() - 1 ];
83
84
85
86                // Find all available fields
87                def allFields = TemplateField.findAllByEntity( entity ).sort { a, b -> a.name <=> b.name }
88
89        return [
90            entity: entity,
91            templates: templates,
92            encryptedEntity: params.entity,
93            fieldTypes: TemplateFieldType.list(),
94                        ontologies: Ontology.list(),
95            humanReadableEntity: humanReadableEntity,
96
97            template: template,
98                        allFields: allFields,
99                        domainFields: domainFields
100        ];
101
102        }
103
104
105    /**
106     * Shows an error page
107     *
108     * TODO: improve the error page
109     */
110    def error = {
111        render( 'view': 'error' );
112    }
113
114    /**
115     * Creates a new template using a AJAX call
116         *
117         * @return                      JSON object with two entries:
118         *                                              id: [id of this object]
119         *                                              html: HTML to replace the contents of the LI-item that was updated.
120         *                                      On error the method gives a HTTP response status 500 and the error
121     */
122    def createTemplate = {
123                // Decode the entity
124        if( !_checkEntity() ) {
125                        response.status = 500;
126                        render "Incorrect entity given";
127                        return;
128                }
129
130                // set entity
131                params.entity = entity;
132
133                // got ontologies?
134                if (params.ontologies) {
135                        // fetch the ontologies
136                        def ontologies = []
137                        params.ontologies.split(/\,/).each() { ncboId ->
138                                // trim ncboId
139                                ncboId = ncboId.trim()
140
141                                // find ontology
142                                def ontology = Ontology.findAllByNcboId( ncboId )
143
144                                // got the ontology?
145                                if (ontology) {
146                                        ontology.each() {
147                                                ontologies[ ontologies.size() ] = it
148                                        }
149                                } else {
150                                        // no, fetch it from the bioportal
151                                        ontology = Ontology.getBioPortalOntology( ncboId )
152
153                                        // does it validate?
154                                        if (ontology.validate()) {
155                                                // yeah, add it!
156                                                ontology.save(flush:true)
157                                                ontology.refresh()
158                                                ontologies[ ontologies.size() ] = ontology
159                                        }
160                                }
161                        }
162
163                        // and set it as parameter again
164                        params.ontologies = ontologies
165                } else {
166                        params.remove('ontologies')
167                }
168println params
169
170                // Create the template field and add it to the template
171                def template = new Template( params );
172//TemplateEntity.getField(entity.domainFields, 'species').ontologies = [Ontology.findByNcboId(1132)]
173TemplateEntity.getField(entity.domainFields, 'species').ontologies = params.ontologies
174
175        if (template.validate() && template.save(flush: true)) {
176println template
177println template.fields
178println template.getRequiredFields()
179println template.findAllByEntity( entity )
180
181
182
183                        def html = g.render( template: 'elements/liTemplate', model: [template: template] );
184                        def output = [ id: template.id, html: html ];
185                        render output as JSON;
186
187            //render '';
188        } else {
189            response.status = 500;
190            render 'Template could not be created because errors occurred.';
191            return
192        }
193    }
194
195    /**
196     * Updates a selected template using a AJAX call
197         *
198         * @param id    ID of the template to update
199         * @return              JSON object with two entries:
200         *                                      id: [id of this object]
201         *                                      html: HTML to replace the contents of the LI-item that was updated.
202         *                              On error the method gives a HTTP response status 500 and the error
203     */
204    def updateTemplate = {
205        // Search for the template field
206        def template = Template.get( params.id );
207        if( !template ) {
208            response.status = 404;
209            render 'Template not found';
210            return;
211        }
212
213        // Update the field if it is not updated in between
214        if (params.version) {
215            def version = params.version.toLong()
216            if (template.version > version) {
217                response.status = 500;
218                render 'Template was updated while you were working on it. Please reload and try again.';
219                return
220            }
221        }
222
223        template.properties = params
224        if (!template.hasErrors() && template.save(flush: true)) {
225                        def html = g.render( template: 'elements/liTemplate', model: [template: template] );
226                        def output = [ id: template.id, html: html ];
227                        render output as JSON;
228        } else {
229            response.status = 500;
230            render 'Template was not updated because errors occurred.';
231            return
232        }
233    }
234
235    /**
236     * Deletes a template using a AJAX call
237     *
238         * @param template              ID of the template to move
239         * @return                              JSON object with one entry:
240         *                                                      id: [id of this object]
241         *                                              On error the method gives a HTTP response status 500 and the error
242     */
243    def deleteTemplate = {
244        // Search for the template field
245        def  template = Template.get( params.template );
246        if( !template ) {
247            response.status = 404;
248            render 'Template not found';
249            return;
250        }
251
252        // Delete the template field
253                try {
254                        template.delete(flush: true)
255
256                        def output = [ id: template.id ];
257                        render output as JSON;
258                }
259                catch (org.springframework.dao.DataIntegrityViolationException e) {
260            response.status = 500;
261            render 'Template could not be deleted: ' + e.getMessage();
262                }
263    }
264
265    /**
266     * Creates a new template field using a AJAX call
267         *
268         * @param template      ID of the template to add a field to
269         * @return                      JSON object with two entries:
270         *                                              id: [id of this object]
271         *                                              html: HTML to replace the contents of the LI-item that was updated.
272         *                                      On error the method gives a HTTP response status 500 and the error
273     */
274    def createField = {
275        // Search for the template
276        def template = Template.get( params.template );
277
278        if( !template ) {
279            response.status = 404;
280            render 'Template not found';
281            return;
282        }
283
284                // Decode the entity, in order to set a good property
285        if( !_checkEntity() ) {
286                        response.status = 500;
287                        render "Incorrect entity given";
288                        return;
289                }
290
291                params.entity = entity;
292
293                // See whether this field already exists. It is checked by name, type and unit and entity
294                // The search is done using search by example (see http://grails.org/DomainClass+Dynamic+Methods, method find)
295                def uniqueParams = [ name: params.name, type: params.type, unit: params.unit, entity: params.entity ];
296                if( TemplateField.find( new TemplateField( uniqueParams ) ) ) {
297                        response.status = 500;
298                        render "A field with this name, type and unit already exists.";
299                        return;
300                }
301
302                // See whether this exists as domain field. If it does, raise an error
303                Method m = template.entity.getDeclaredMethod("giveDomainFields", null);
304                def domainFields = m.invoke( null, null );
305                if( domainFields.find { it.name.toLowerCase() == params.name.toLowerCase() } ) {
306                        response.status = 500;
307                        render "All templates for entity " + template.entity + " contain a domain field with name " + params.name + ". You can not create a field with this name.";;
308                        return;
309                }
310
311                // If this field is type stringlist, we have to prepare the parameters
312                if( params.type.toString() == 'STRINGLIST' ) {
313                        def listEntries = [];
314                        params.listEntries.eachLine {
315                                // Search whether a listitem with this name already exists.
316                                // if it does, use that one to prevent pollution of the database
317                                def name = it.trim();
318                                def listitem = TemplateFieldListItem.findByName( name );7
319                                if( !listitem ) {
320                                        listitem = new TemplateFieldListItem( name: name )
321                                }
322                                listEntries.add( listitem );
323                        }
324
325                        params.listEntries = listEntries;
326                } else {
327                        params.remove( 'listEntries' );
328                }
329
330                // If this field isnot a ontologyterm, we should remove the ontologies
331                if( params.type.toString() != 'ONTOLOGYTERM' ) {
332                        params.remove( 'ontologies' );
333                }
334
335                // Create the template field and add it to the template
336                def templateField = new TemplateField( params );
337        if (templateField.save(flush: true)) {
338
339                        def html = g.render( template: 'elements/available', model: [templateField: templateField, ontologies: Ontology.list(), fieldTypes: TemplateFieldType.list()] );
340                        def output = [ id: templateField.id, html: html ];
341                        render output as JSON;
342
343            //render '';
344        } else {
345            response.status = 500;
346            render 'TemplateField could not be created because errors occurred.';
347            return
348        }
349    }
350
351    /**
352     * Updates a selected template field using a AJAX call
353         *
354         * @param id    ID of the field to update
355         * @return              JSON object with two entries:
356         *                                      id: [id of this object]
357         *                                      html: HTML to replace the contents of the LI-item that was updated.
358         *                              On error the method gives a HTTP response status 500 and the error
359     */
360    def updateField = {
361        // Search for the template field
362        def templateField = TemplateField.get( params.id );
363        if( !templateField ) {
364            response.status = 404;
365            render 'TemplateField not found';
366            return;
367        }
368
369        // Update the field if it is not updated in between
370        if (params.version) {
371            def version = params.version.toLong()
372            if (templateField.version > version) {
373                response.status = 500;
374                render 'TemplateField was updated while you were working on it. Please reload and try again.';
375                return
376            }
377        }
378
379                // If this field is type stringlist or ontology, we have to prepare the parameters
380                if( params.type.toString() == 'STRINGLIST' ) {
381                        def listEntries = [];
382                        params.listEntries.eachLine {
383                                // Search whether a listitem with this name already exists.
384                                // if it does, use that one to prevent pollution of the database
385                                def name = it.trim();
386                                def listitem = TemplateFieldListItem.findByName( name );7
387                                if( !listitem ) {
388                                        listitem = new TemplateFieldListItem( name: name )
389                                }
390                                listEntries.add( listitem );
391                        }
392
393                        params.listEntries = listEntries;
394                } else {
395                        params.remove( 'listEntries' );
396                }
397
398                // If this field is a ontologyterm, we add ontology objects
399                if( params.type.toString() == 'ONTOLOGYTERM' && params.ontologies ) {
400                        params.ontologies = Ontology.getAll( params.ontologies.collect { Integer.parseInt( it ) } );
401                } else {
402                        params.remove( 'ontologies' );
403                }
404
405                // Set all parameters
406                templateField.properties = params
407        if (!templateField.hasErrors() && templateField.save(flush: true)) {
408                        def html = g.render( template: 'elements/available', model: [templateField: templateField, ontologies: Ontology.list(), fieldTypes: TemplateFieldType.list()] );
409                        def output = [ id: templateField.id, html: html ];
410                        render output as JSON;
411        } else {
412            response.status = 500;
413            render 'TemplateField was not updated because errors occurred.';
414            return
415        }
416    }
417
418    /**
419     * Deletes a template field using a AJAX call
420     *
421         * @param templateField ID of the templatefield to move
422         * @return                              JSON object with one entry:
423         *                                                      id: [id of this object]
424         *                                              On error the method gives a HTTP response status 500 and the error
425     */
426    def deleteField = {
427        // Search for the template field
428        def  templateField = TemplateField.get( params.templateField );
429        if( !templateField ) {
430            response.status = 404;
431            render 'TemplateField not found';
432            return;
433        }
434
435        // Delete the template field
436                try {
437                        templateField.delete(flush: true)
438
439                        def output = [ id: templateField.id ];
440                        render output as JSON;
441                }
442                catch (org.springframework.dao.DataIntegrityViolationException e) {
443            response.status = 500;
444            render 'TemplateField could not be deleted: ' + e.getMessage();
445                }
446    }
447
448    /**
449     * Adds a new template field to a template using a AJAX call
450         *
451         * @param template      ID of the template to add a field to
452         * @return                      JSON object with two entries:
453         *                                              id: [id of this object]
454         *                                              html: HTML to replace the contents of the LI-item that was updated.
455         *                                      On error the method gives a HTTP response status 404 or 500 and the error
456     */
457    def addField = {
458        // Search for the template
459        def template = Template.get( params.template );
460
461        if( !template ) {
462            response.status = 404;
463            render 'Template not found';
464            return;
465        }
466
467        // Search for the template field
468        def templateField = TemplateField.get( params.templateField );
469        if( !templateField ) {
470            response.status = 404;
471            render 'TemplateField does not exist';
472            return;
473        }
474
475        // The template field should exist within the template
476        if( template.fields.contains( templateField ) ) {
477            response.status = 500;
478            render 'TemplateField is already found within template';
479            return;
480        }
481
482                // If the template is in use, only non-required fields can be added
483                if( template.inUse() && templateField.required ) {
484                        response.status = 500;
485                        render 'Only non-required fields can be added to templates that are in use.'
486                        return;
487                }
488
489                // All field names within a template should be unique
490                if( template.fields.find { it.name.toLowerCase() == templateField.name.toLowerCase() } ) {
491                        response.status = 500;
492                        render 'This template already contains a field with name ' + templateField.name + '. Field names should be unique within a template.'
493                        return;
494                }
495
496                if( !params.position || Integer.parseInt( params.position ) == -1) {
497                        template.fields.add( templateField )
498                } else {
499                        template.fields.add( Integer.parseInt( params.position ), templateField )
500                }
501                template.save(flush:true);
502
503                def html = g.render( template: 'elements/selected', model: [templateField: templateField, template: template, ontologies: Ontology.list(), fieldTypes: TemplateFieldType.list()] );
504                def output = [ id: templateField.id, html: html ];
505                render output as JSON;
506    }
507
508    /**
509     * Removes a selected template field from the template using a AJAX call
510         *
511         * @param templateField ID of the field to update
512         * @param template              ID of the template for which the field should be removed
513         * @return                              JSON object with two entries:
514         *                                                      id: [id of this object]
515         *                                                      html: HTML to replace the contents of the LI-item that was updated.
516         *                                              On error the method gives a HTTP response status 404 or 500 and the error
517     */
518    def removeField = {
519        // Search for the template
520        def template = Template.get( params.template );
521
522        if( !template ) {
523            response.status = 404;
524            render 'Template not found';
525            return;
526        }
527
528        // Search for the template field
529        def templateField = TemplateField.get( params.templateField );
530        if( !templateField ) {
531            response.status = 404;
532            render 'TemplateField not found';
533            return;
534        }
535
536        // The template field should exist within the template
537        if( !template.fields.contains( templateField ) ) {
538            response.status = 404;
539            render 'TemplateField not found within template';
540            return;
541        }
542
543                // If the template is in use, field can not be removed
544                if( template.inUse() ) {
545                        response.status = 500;
546                        render 'No fields can be removed from templates that are in use.'
547                        return;
548                }
549
550                // Delete the field from this template
551        def currentIndex = template.fields.indexOf( templateField );
552        template.fields.remove( currentIndex );
553                template.save(flush:true);
554
555
556                def html = g.render( template: 'elements/available', model: [templateField: templateField, ontologies: Ontology.list(), fieldTypes: TemplateFieldType.list()] );
557                def output = [ id: templateField.id, html: html ];
558                render output as JSON;
559    }
560
561    /**
562     * Moves a template field using a AJAX call
563     *
564         * @param template              ID of the template that contains this field
565         * @param templateField ID of the templatefield to move
566         * @param position              New index of the templatefield in the array. The index is 0-based.
567         * @return                              JSON object with two entries:
568         *                                                      id: [id of this object]
569         *                                                      html: HTML to replace the contents of the LI-item that was updated.
570         *                                              On error the method gives a HTTP response status 500 and the error
571     */
572    def moveField = {
573        // Search for the template
574        def template = Template.get( params.template );
575
576        if( !template ) {
577            response.status = 404;
578            render 'Template not found';
579            return;
580        }
581
582        // Search for the template field
583        def  templateField = TemplateField.get( params.templateField );
584        if( !templateField ) {
585            response.status = 404;
586            render 'TemplateField not found';
587            return;
588        }
589
590        // The template field should exist within the template
591        if( !template.fields.contains( templateField ) ) {
592            response.status = 404;
593            render 'TemplateField not found within template';
594            return;
595        }
596
597        // Move the item
598        def currentIndex = template.fields.indexOf( templateField );
599        def moveField = template.fields.remove( currentIndex );
600        template.fields.add( Integer.parseInt( params.position ), moveField );
601                template.save(flush:true);
602
603                def html = g.render( template: 'elements/selected', model: [templateField: templateField, template: template, fieldTypes: TemplateFieldType.list()] );
604                def output = [ id: templateField.id, html: html ];
605                render output as JSON;
606    }
607
608        /**
609         * Checks how many template use a specific template field
610         *
611         * @param       id      ID of the template field
612         * @return      int     Number of uses
613         */
614        def numFieldUses = {
615        // Search for the template field
616        def  templateField = TemplateField.get( params.id );
617        if( !templateField ) {
618            response.status = 404;
619            render 'TemplateField not found';
620            return;
621        }
622
623                render templateField.numUses();
624        }
625
626        /**
627         * Adds a ontolgy based on the ID given
628         *
629         * @param       ncboID
630         * @return      JSON    Ontology object
631         */
632        def addOntologyById = {
633                def id = params.ncboID;
634
635                if( !id ) {
636                        response.status = 500;
637                        render 'No ID given'
638                        return;
639                }
640
641                if( Ontology.findByNcboId( Integer.parseInt(  id ) ) ) {
642                        response.status = 500;
643                        render 'This ontology was already added to the system';
644                        return;
645                }
646
647                def ontology = null;
648
649                try {
650                        ontology = dbnp.data.Ontology.getBioPortalOntology( id );
651                } catch( Exception e ) {
652                        response.status = 500;
653                        render e.getMessage();
654                        return;
655                }
656
657                if( !ontology ) {
658                        response.status = 404;
659                        render 'Ontology with ID ' + id + ' not found';
660                        return;
661                }
662
663                // Validate ontology
664                if (!ontology.validate()) {
665                        response.status = 500;
666                        render ontology.errors.join( '; ' );
667                        return;
668                }
669
670                // Save ontology and render the object as JSON
671                ontology.save(flush: true)
672                render ontology as JSON
673        }
674
675        /**
676         * Adds a ontolgy based on the ID given
677         *
678         * @param       ncboID
679         * @return      JSON    Ontology object
680         */
681        def addOntologyByTerm = {
682                def id = params.termID;
683
684                if( !id ) {
685                        response.status = 500;
686                        render 'No ID given'
687                        return;
688                }
689
690                def ontology = null;
691
692                try {
693                        ontology = dbnp.data.Ontology.getBioPortalOntologyByTerm( id );
694                } catch( Exception e ) {
695                        response.status = 500;
696                        render e.getMessage();
697                        return;
698                }
699
700                if( !ontology ) {
701                        response.status = 404;
702                        render 'Ontology form term ' + id + ' not found';
703                        return;
704                }
705
706                // Validate ontology
707                if (!ontology.validate()) {
708                        response.status = 500;
709                        render ontology.errors.join( '; ' );
710                        return;
711                }
712
713                // Save ontology and render the object as JSON
714                ontology.save(flush: true)
715                render ontology as JSON
716        }
717
718
719    /**
720     * Checks whether a correct entity is given
721         *
722         * @return      boolean True if a correct entity is given. Returns false and raises an error otherwise
723         * @see         error()
724     */
725    def _checkEntity = {
726        // got a entity get parameter?
727        entityName = _parseEntityType();
728
729        if( !entityName ) {
730            error();
731            return false;
732        }
733
734        // Create an object of this type
735        entity = _getEntity( entityName );
736
737        if( !entity ) {
738            error();
739            return false
740        }
741
742        return true;
743    }
744
745
746    /**
747     * Checks whether the entity type is given and can be parsed
748         *
749         * @return      Name of the entity if parsing is succesful, false otherwise
750     */
751    def _parseEntityType() {
752        def entityName;
753        if (params.entity) {
754            // decode entity get parameter
755            if (grailsApplication.config.crypto) {
756                    // generate a Blowfish encrypted and Base64 encoded string.
757                    entityName = Blowfish.decryptBase64(
758                            params.entity,
759                            grailsApplication.config.crypto.shared.secret
760                    )
761            } else {
762                    // base64 only; this is INSECURE! Even though it is not
763                    // very likely, it is possible to exploit this and have
764                    // Grails dynamically instantiate whatever class you like.
765                    // If that constructor does something harmfull this could
766                    // be dangerous. Hence, use encryption (above) instead...
767                    entityName = new String(params.entity.toString().decodeBase64())
768            }
769
770            return entityName;
771        } else {
772            return false;
773        }
774    }
775
776    /**
777     * Creates an object of the given entity.
778         *
779         * @return False if the entity is not a subclass of TemplateEntity
780     */
781    def _getEntity( entityName ) {
782        // Find the templates
783        def entity = Class.forName(entityName, true, this.getClass().getClassLoader())
784
785        // succes, is entity an instance of TemplateEntity?
786        if (entity.superclass =~ /TemplateEntity$/) {
787            return entity;
788        } else {
789            return false;
790        }
791
792    }
793}
Note: See TracBrowser for help on using the repository browser.