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

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