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

Last change on this file since 575 was 575, checked in by keesvb, 10 years ago

additional fix for #114, unneccesary complex invoke of giveDomainFields() in template editor

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