source: MILK/trunk/milk_server/milk_server/controllers/atom_editor/editatom.py @ 4964

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/MILK/trunk/milk_server/milk_server/controllers/atom_editor/editatom.py@4964
Revision 4964, 27.6 KB checked in by cbyrom, 11 years ago (diff)

Adjust granulite to ensure that granule atoms are validated before
they are created - and add code to properly deal with validation errors.

Line 
1'''
2 Class representing pylons controller for the creation and editing of atom
3 data
4 
5 @author: C Byrom, Tessella Sep 2008
6'''
7import logging, traceback, sys, cgi
8from xml.parsers.expat import ExpatError
9from formencode import Invalid
10from genshi.filters import HTMLFormFiller
11from genshi import HTML
12from milk_server.lib.base import *
13from milk_server.models.form import *
14from milk_server.lib import mailer
15import ndg.common.src.lib.htmlUtilities as utils
16from ndg.common.src.models.Atom import Atom, Person, Link, Category
17from ndg.common.src.models import AtomState
18from ndg.common.src.lib.atomvalidator import ValidationError
19import ndg.common.src.clients.xmldb.abstractxmldbatomclient as ac
20from ndg.common.src.models.MolesEntity import MolesEntity as ME
21from ndg.common.src.lib.utilities import escapeSpecialCharacters
22from ndg.common.src.models.vocabtermdata import VocabTermData as VTD
23from ndg.common.src.lib.granulite import granulite
24from editorconstants import *
25from atomeditorcontroller import AtomEditorController
26from milk_server.lib import Utilities
27
28class EditatomController(AtomEditorController):
29    '''
30    Provides the pylons controller for editing NDG Atom documents.
31    '''
32    def upload(self, uri):
33        '''
34        Upload a CSML, CDML or granulite file and store it in the session variable
35        NB, if the uri is specified, we're already dealing with an atom
36        (which this refers to) - so the file is not a granulite - since
37        this is used to create an atom from scratch
38        '''
39        logging.info("Uploading file...")
40        self._setup(uri=uri)
41       
42        granFile = request.POST.get('upload_granulite')
43        csmlOrCdmlFile = request.POST.get('CSMLOrCDML')
44       
45        # check whether we can replace existing atoms
46        replaceAtom = self.inputs.get('replaceAtom')
47       
48        # NB, need to turn from string to boolean - there doesn't seem a reliable
49        # way of doing this using built in methods - so just do simple check
50        if replaceAtom == 'True':
51            replaceAtom = True
52        else:
53            replaceAtom = False
54       
55        # if this is true, then re-extract the inputs from the session data
56        if replaceAtom:
57            if session.get(GRAN_FILE_VALUE):
58                granFile = cgi.FieldStorage()
59                granFile.value = session.get(GRAN_FILE_VALUE)
60                granFile.filename = session.get(GRAN_FILE_NAME)
61                del session[GRAN_FILE_VALUE]
62                del session[GRAN_FILE_NAME]
63               
64            if session.get(CSML_OR_CDML_FILE_VALUE):
65                csmlOrCdmlFile = cgi.FieldStorage()
66                csmlOrCdmlFile.value = session.get(CSML_OR_CDML_FILE_VALUE)
67                csmlOrCdmlFile.filename = session.get(CSML_OR_CDML_FILE_NAME)
68                del session[CSML_OR_CDML_FILE_VALUE]
69                del session[CSML_OR_CDML_FILE_NAME]
70         
71        c.errors = {}
72        try:
73            logging.info("Validating inputs")
74            validator = LoadGranuliteFormSchema()
75            validator.to_python(self.inputs)
76            logging.info("- inputs valid")
77           
78            useCSMLID = True
79            if uri:
80                useCSMLID = False
81
82            if (granFile == '' or granFile == None) and \
83                (csmlOrCdmlFile == '' or csmlOrCdmlFile == None):
84                errorMessage = "Error: could not load file - please try again"
85                logging.error(errorMessage)
86                raise IOError(errorMessage)
87            else:
88                # Prepare the basic data model
89                # NB, if loading a granulite, this will create the displayed atom
90                # with the ID taken from the CSML file, if specified
91                fileContents = None
92                if (granFile is not None and granFile != ''):
93                    fileContents = granFile.value
94                   
95                # use the granulite helper class to add either the full granulite
96                # data or just the CSML/CDML data
97                # NB, we'll be creating the atom in the default local eXist
98                eXistClient = Utilities.getExistClient('local')
99                gran = granulite(fileContents, granuleAtom = c.atom, \
100                                 eXistClient = eXistClient, \
101                                 csmlOrCdmlFile = csmlOrCdmlFile, \
102                                 timeAxis = self.inputs.get('timeAxis'), \
103                                 datasetID = self.inputs.get('granuleDatasetID'), \
104                                 useCSMLID = useCSMLID, \
105                                 replaceAtom = replaceAtom)
106
107                # now process the input file and add any extra required data
108                if uri:
109                    c.atom = gran.processCSMLOrCDMLFile()
110
111                    # save new data - NB, for granulites, this is done as part of the
112                    # processing steps
113                    self.saveAtomToExist(c.atom)
114                else:
115                    try:
116                        c.atom = gran.processGranulite()
117
118                        # Now set up the ndgObject with the created atom's vals
119                        self._setup(uri=c.atom.ndgURI, loadAtom = False)
120                        c.atom.ndgObject = self.ndgObject
121                    except ac.DuplicateError, e:
122                        # we've found an existing atom with the same ID
123                        # - give the users the choice of replacing the contents of this atom
124                        # or just exiting
125                        # - NB, do this via a session variable to act as a flag
126                        # for a javascript command
127                        session[OVERWRITE_GRANULE_FLAG] = e.message
128                       
129                        # store the inputs data for easy retrieval
130                        # - NB, file fields don't behave as text fields - for security
131                        # purposes - so need to store their data as session variables
132                        # for easy retrieval
133                        # - Also, cannot pickle the cgi.FieldStorage object so extract
134                        # picklable data and recreate on the return run
135                        if granFile != '':
136                            session[GRAN_FILE_VALUE] = granFile.value
137                            session[GRAN_FILE_NAME] = granFile.filename
138                        if csmlOrCdmlFile != '':
139                            session[CSML_OR_CDML_FILE_VALUE] = csmlOrCdmlFile.value
140                            session[CSML_OR_CDML_FILE_NAME] = csmlOrCdmlFile.filename
141                       
142                        # need to return to original screen - so clear out variables
143                        c.atom = None
144                        uri = None
145                           
146                # now do redirection - NB, this ensures that current atom contents are
147                # reloaded and displayed
148                logging.info("File data loaded and extracted to atom")
149        except Invalid, e:
150            logging.info(" - inputs invalid")
151            c.errors = e.unpack_errors()
152        except ValidationError, e:
153            logging.info(e)
154            self._unpackErrors(e)
155           
156        except Exception, e:
157            c.errors['WARNING'] = ['Error loading data: the displayed data will not be saved - please fix problem and retry']
158            self._unpackErrors(e)
159        except SystemExit, ee:
160            # NB, some of the CSML libraries just sys.exit on problems - catch errors here
161            c.errors['ERROR'] = ['Problem encountered whilst transforming the CDML data into CSML']
162            self._unpackErrors(ee)
163
164        if c.atom and hasattr(c.atom, 'ndgURI'):
165            self.pathInfo = self.pathInfo.replace('upload', 'editAtom')
166
167            # NB, if there are errors, don't redirect otherwise these will get lost
168            if not c.errors:
169                h.redirect_to(h.url_for('edit', uri = c.atom.ndgURI))
170            else:
171                c.atom.contentFile = None
172                return self.edit(c.atom.ndgURI)
173                           
174        elif uri:
175            # something has gone wrong here...
176            return render("genshi", 'atom_editor/error')
177        else:
178            return self.createGranule(**self.inputs)
179
180   
181    def saveAtom(self, uri, saveLevel=0):
182        '''
183        Save the atom contents - NB, validation is done by method decoration
184        - if this fails, the action is reran as a GET with htmlfill auto-populating
185        the fields to keep them as they were before the submission
186        '''
187        logging.info("Saving input atom data")
188        c.errors = {}
189        try:
190            self._setup(uri)
191        except Exception, e:
192            return self._handleError(e)
193       
194        # variable to hold publication state changes
195        newState = None
196       
197        # save atom association changes
198        if int(saveLevel) == self.ADD_ASSOCIATIONS:
199            atomLinks = self.extractAtomAssociations(self.inputs)
200            c.atom.addUniqueRelatedLinks(atomLinks)
201        elif int(saveLevel) == self.REMOVE_ASSOCIATIONS:
202            atomLinks = self.extractAtomAssociations(self.inputs)
203            c.atom.removeRelatedLinks(atomLinks)
204        else:
205            authors = self.extractAuthorDetails(self.inputs)
206            c.atom.addAuthors(authors)
207   
208            onlineRefs = self.extractOnlineReferenceDetails(self.inputs)
209            c.atom.addOnlineReferences(onlineRefs)
210
211            params = self.extractParameterDetails(self.inputs)
212            # NB, the atom type and subtype are added to the categories when the
213            # atom is exported to XML - so don't need to worry about overwriting
214            # them now
215            c.atom.parameters = params
216           
217            if self.inputs.get('subtype'):
218                c.atom.subtype = self.getLatestTermURLFromDropDownInput( \
219                        self.inputs.get('subtype'))
220                c.atom.subtypeID = c.atom.subtype.split('/')[-1]
221           
222            if self.inputs.get('publication_state'):
223                newState = AtomState.getAtomState(self.inputs['publication_state'])
224
225        logging.info("Validating input")
226        try:
227            g.validator.setAtom(c.atom)
228            g.validator.validateAtom()
229            logging.info("- input valid")
230           
231            # if a change of state has been specified,
232
233            self.saveAtomToExist(c.atom, newState = newState)
234        except Exception, e:
235            self._unpackErrors(e)
236            logging.info("- input invalid")
237            return self.edit(uri)
238                   
239        # now do redirection - NB, this ensures that current atom contents are
240        # reloaded and displayed
241        h.redirect_to(h.url_for(controller = 'atom_editor/editatom', action='edit', \
242                        uri = c.atom.ndgURI))
243
244
245    def prepareEditForm(self, uri):
246        '''
247        Get everything set up for displaying the edit form
248        @param uri: ndg url for the atom to load in edit mode
249        '''
250        if not c.errors:
251            c.errors = {}
252
253        # NB, can get here directly from saveAtom - if there have been errors
254        # - in this case keep original data
255        if not c.atom:
256            self._setup(uri)
257           
258        c.title= EDIT_TITLE %c.atom.ndgURI
259        c.uri = c.atom.ndgURI
260       
261        c.saveLink = h.url_for('save', saveLevel = self.STANDARD_SAVE, uri = c.atom.ndgURI)
262        c.saveAssoc = h.url_for('save', saveLevel = self.REMOVE_ASSOCIATIONS, uri = c.atom.ndgURI)
263        c.deploymentsURL = h.url_for('viewAssociatedData', type = VTD.DEPLOYMENT_TERM, \
264                                     uri = c.atom.ndgURI)
265        c.dataEntitiesURL = h.url_for('viewAssociatedData', type = VTD.DE_TERM, \
266                                     uri = c.atom.ndgURI)
267       
268        # adjust atom type to cope with activity deployment exception
269        atomType = c.atom.atomTypeID
270        if atomType == g.vtd.ACTIVITY_TERM and \
271            c.atom.subtypeID == g.vtd.DEPLOYMENT_TERM:
272            atomType = g.vtd.DEPLOYMENT_TERM
273       
274        c.addEntityLink = h.url_for('list', searchData = '0', \
275                               associatedAtomID = c.atom.ndgURI, \
276                               associatedAtomType = atomType, 
277                               associationType = utils.ENTITY_ASSOCIATION)
278           
279        c.addGranuleLink = h.url_for('list', searchData = '0', \
280                               associatedAtomID = c.atom.ndgURI, \
281                               associatedAtomType = atomType, 
282                               associationType = utils.GRANULE_ASSOCIATION)
283           
284        c.addDeploymentLink = h.url_for('list', searchData = '0', \
285                               associatedAtomID = c.atom.ndgURI, \
286                               associatedAtomType = atomType, 
287                               associationType = utils.DEPLOYMENT_ASSOCIATION)
288
289        # account for special case where we're dealing with deployments
290        listVals = g.vtd.getValidSubTypes(c.atom.atomTypeID)
291        if c.atom.isDeployment():
292            listVals = [g.vtd.TERM_DATA[g.vtd.DEPLOYMENT_TERM]]
293
294        c.subTypes = utils.getVocabTermDataDropdown(listVals, \
295                                        selected=c.atom.subtype)
296       
297        c.states = h.options_for_select(AtomState.selectList, c.atom.state.stateFlag)
298        self.inputs['publication_state'] = c.atom.state.stateFlag
299       
300        self.__setDropDownSelectVal('subtype', c.atom.subtype, listVals)
301        self.addRelatedLinksDropDowns()
302
303
304    def __setDropDownSelectVal(self, name, val, vtds):
305        '''
306        Given a list of vocab terms, with the name of a 'select' tag and the selected
307        value, set the proper value in the inputs dict to allow htmlfill to correctly
308        display the list
309        @param name: name of select element to set the select value of
310        @param val: value of the selected item - NB, this need not be the current vocab
311        term url - but should start with the main stem and end with the termID
312        @param vtds: list of vocab term definition objects
313        '''
314        if not val:
315            return
316        for vtd in vtds:
317            if val.endswith(vtd.termID) and \
318                val.startswith(vtd.vocabURL):
319                self.inputs[name] = utils.getVocabTermDataSelectValue(vtd)
320                return
321           
322
323    def delete(self, uri):
324        '''
325        Delete the atom associated with the specified uri - and return
326        user to the atom home page.  NB, only granule atoms can be deleted
327        at the moment.
328        '''
329        if uri:
330            try:
331                logging.info("Deleting atom, '%s'" %uri)
332                self._setup(uri)
333                eXistClient = Utilities.getExistClient(c.atom.ME.providerID)
334                gran = granulite(None, granuleAtom = c.atom, \
335                                 eXistClient = eXistClient, \
336                                 deleteMode = True)
337   
338                gran.deleteGranuleAndDEReferences()
339                c.deleteResult = "Atom deleted successfully."
340                logging.info("- atom deleted")
341            except Exception, e:
342                logging.error("Problem occured whilst deleting atom: '%s'" %e.message)
343                c.deleteResult = "Warning: a problem occured whilst deleting the atom - this " + \
344                    "may have left the system in an unstable state - please check if the atom, or " + \
345                    "references to the atom still exist"
346
347        return render("genshi", "atom_editor/atom_home")
348       
349   
350    def edit(self, uri):
351        '''
352        Edit the atom with the specified ndg uri
353        '''
354        logging.info("Setting up atom edit template")
355        try:
356            self.prepareEditForm(uri)
357           
358            # NB, there appears to be a bug in htmlfill which automagically
359            # clears out content from textarea - so need to set the content
360            # explicitly for htmlfill to use
361            self.inputs['Content'] = c.atom.Content
362            self.inputs['Summary'] = c.atom.Summary
363            return self.savePageAndRender("atom_editor/atom_editor", **self.inputs)
364       
365        except ExpatError, e:
366            c.xml='XML content is not well formed'
367            c.doc=str(e)
368            logging.error("Error retrieving [%s] - XML content: %s" % (uri, e))
369        except SystemError, e:
370            return self._handleError(e)
371        except Exception, e:
372            errorMessage = traceback.format_exc()
373            c.xml='Unexpected error [%s] viewing [%s]'%(str(e), uri)
374            c.doc=''
375            logging.error(c.xml)
376       
377        response.status_code = 400
378        return render("genshi", 'atom_editor/error')
379
380
381    def addRelatedLinksDropDowns(self):
382        '''
383        Set up the drop down lists required for the selection of online ref links
384        '''
385        # at the very least, we need a simple drop down list with no preselected
386        # values
387        logging.debug("Setting up drop down lists for related links")
388        vtds = g.vtd.getValidTypes(g.vtd.ONLINE_REF_CATEGORY)
389        c.relatedLinkTerms = utils.getVocabTermDataDropdown(vtds)
390
391        # ensure we have set up the correct inputs to allow htmlfill to show
392        # the correct selected value
393        for i, link in enumerate(c.atom.relatedLinks):
394            logging.debug("Adding dropdown for related link, '%s'" %(str(link)))
395            refLabel = Atom.ONLINE_REF_LABEL + "." + str(i) + '.rel'
396           
397            # get the value of the selected list
398            self.__setDropDownSelectVal(refLabel, link.rel, vtds)
399
400        logging.debug("Finished setting up drop down lists")
401
402
403    def extractAuthorDetails(self, inputs):
404        '''
405        Retrieve author data from inputs and set appropriately on Atom, if any
406        found
407        @return: list of Person objects with the author data
408        '''
409        logging.info("Extracting author data from inputs")
410        processedAuthors = []
411        authors = []
412        for key in inputs:
413            keyBits = key.split('.')
414            if len(keyBits) == 3 and keyBits[1] not in processedAuthors:
415               
416                authorType = -1
417                if key.lower().startswith('author'):
418                    authorType = Person.AUTHOR_TYPE
419                elif key.lower().startswith('contributor'):
420                    authorType = Person.CONTRIBUTOR_TYPE
421                elif key.lower().startswith('responsible'):
422                    authorType = Person.RESPONSIBLE_PARTY_TYPE
423                else:
424                    continue
425
426                # NB, adding an empty object here is valid as it will clear out
427                # existing entries, potentially
428                author = Person(personType = authorType)
429                # check if the remove checkbox has been set
430                keyStem = ".".join(keyBits[0:2])
431                if inputs.get(keyStem + ".remove"):
432                    logging.info("Removing author data")
433                else:
434                    author.name = inputs.get(keyStem + '.name') or ""
435                    author.uri = inputs.get(keyStem + '.uri') or ""
436                    author.role = inputs.get(keyStem + '.role') or ""
437                   
438                    logging.info("Adding new author info")
439                    logging.debug("Extracted author (type:'%s', name:'%s', uri:'%s', role:'%s')" \
440                                  %(author.type, author.name, author.uri, author.role))
441                authors.append(author)
442                processedAuthors.append(keyBits[1])
443
444        logging.info("Finished extracting author data")
445        return authors
446
447
448    def extractOnlineReferenceDetails(self, inputs):
449        '''
450        Retrieve online reference data from inputs and set appropriately on Atom, if any
451        found
452        @return: list of Link objects containing the extracted data
453        '''
454        logging.info("Extracting related links data from inputs")
455        processedLinks = []
456        links = []
457
458        for key in inputs:
459            keyBits = key.split('.')
460            if len(keyBits) == 3 and keyBits[1] not in processedLinks:
461               
462                if key.lower().startswith(Atom.ONLINE_REF_LABEL):
463                    link = Link()
464                    keyStem = ".".join(keyBits[0:2])
465                   
466                    if inputs.get(keyStem + ".remove"):
467                        logging.info("Removing online reference data")
468                    else:
469                        # NB, this is in the format vocabURL--termID, so requires further
470                        # processing
471                        link.rel = self.getLatestTermURLFromDropDownInput(inputs.get(keyStem + '.rel'))
472                        link.href = inputs.get(keyStem + '.href') or ""
473                        link.title = inputs.get(keyStem + '.title') or ""
474                       
475                        if not link.hasValue():
476                            continue
477                           
478                        logging.info("Adding new online reference info")
479                        logging.debug("Extracted online reference (href:'%s', title:'%s', rel:'%s')" \
480                                      %(link.href, link.title, link.rel))
481                        links.append(link)
482
483                    processedLinks.append(keyBits[1])
484                else:
485                    continue
486
487        logging.info("Finished extracting links data")
488        return links
489
490
491    def extractParameterDetails(self, inputs):
492        '''
493        Retrieve parameters data from inputs and set appropriately on Atom, if any
494        found
495        @return: list of Category objects containing the extracted data
496        '''
497        logging.info("Extracting parameters data from inputs")
498        processedParameters = []
499        parameters = []
500
501        for key in inputs:
502            keyBits = key.split('.')
503            if len(keyBits) == 3 and keyBits[1] not in processedParameters:
504               
505                if key.lower().startswith(Atom.PARAMETER_LABEL):
506                    parameter = Category()
507                    keyStem = ".".join(keyBits[0:2])
508                   
509                    if inputs.get(keyStem + ".remove"):
510                        logging.info("Removing parameters data")
511                    else:
512                        parameter.term = inputs.get(keyStem + '.term') or ""
513                        parameter.scheme = inputs.get(keyStem + '.scheme') or ""
514                        parameter.label = inputs.get(keyStem + '.label') or ""
515                           
516                        logging.info("Adding new parameter info")
517                        logging.debug("Extracted parameter (vocabURL:'%s', label:'%s', term:'%s')" \
518                                      %(parameter.scheme, parameter.label, parameter.term))
519                        parameters.append(parameter)
520
521                    processedParameters.append(keyBits[1])
522                else:
523                    continue
524
525        logging.info("Finished extracting parameters data")
526        return parameters
527
528
529    def extractAtomAssociations(self, inputs):
530        '''
531        Retrieve atom data from inputs and create related links pointing to
532        this data
533        @return: list of Links representing the related atoms
534        '''
535        logging.info("Extracting related atom ID data from inputs")
536        atoms = []
537        processedAtoms = []
538
539        for key in inputs:
540            if key.lower().startswith(Atom.ATOM_REF_LABEL):
541                (x, href, title, rel) = key.split(Atom.DELIMITER)
542                # NB, we handle removes by effectively ignoring them later on
543                if href not in processedAtoms:
544                    processedAtoms.append(href)
545
546                    link = Link()
547                    link.href = href or ""
548                    link.title = title or ""
549                    link.rel = rel or ""
550                   
551                    # adjust href to point to the view, not the edit version
552                    link.href = link.href.replace('editAtom', 'view')
553                   
554                    logging.debug("Extracted atom info (href:'%s', title:'%s', rel:'%s')" \
555                                  %(link.href, link.title, link.rel))
556                    atoms.append(link)
557            else:
558                continue
559
560        logging.info("Finished extracting atoms data")
561        return atoms
562               
563
564    def getLatestTermURLFromDropDownInput(self, inputVal):
565        '''
566        Term ID and vocabURL are specified in the drop down menus
567        - using the input from this, return the lastest full href to the
568        term ID
569        '''
570        termData = inputVal.split('--')
571        return g.vtd.getCurrentVocabURI(termData[0]) + \
572                        "/" + termData[1]
573
574
575    def saveAtomToExist(self, atom, newState = None):
576        '''
577        Save the specified atom in eXist
578        @param atom: atom object to save to eXist
579        @keyword newState:  AtomState publication state to move the atom to, default: None
580        - use the current atom state
581        @return atom: atom object saved in eXist
582        '''
583        logging.info("Saving changes to eXist")
584        eXist = Utilities.getExistClient(atom.ME.providerID)
585       
586        if newState and newState != atom.state:
587            createdAtom = eXist.changeAtomPublicationState(atom, newState)
588        else:
589            createdAtom = eXist.createAtom(atom)
590        logging.info("Changes successfully saved to eXist")
591        return createdAtom
592
593   
594    def create(self, saveData = None, **inputs):
595        '''
596        Create a new atom
597        '''
598        self._setup()
599        if saveData:
600            logging.info("Validating input")
601            try:
602                validator = CreateAtomFormSchema()
603                validator.to_python(self.inputs)
604                logging.info("- input valid")
605               
606                logging.info("Creating basic atom")
607                atomTypeID = self.inputs.get('atomTypeID').split('--')[1]
608                self.inputs['atomTypeID'] = atomTypeID
609   
610                # activity deployments should have subtype deployment specified automatically
611                if atomTypeID == g.vtd.ACTIVITY_DEPLOYMENT_TERM:
612                    self.inputs['subtypeID'] = g.vtd.DEPLOYMENT_TERM
613                    self.inputs['atomTypeID'] = g.vtd.ACTIVITY_TERM
614                   
615                self.inputs['providerID'] = self.inputs.get('providerID').split('--')[1]
616                atom = self.saveAtomToExist(Atom(**dict(self.inputs)))
617                url = h.url_for('edit', uri = atom.ndgURI, saveData=None)
618               
619                # NB, the redirect throws an exception, so be careful not to catch it
620                h.redirect_to(url)
621            except Invalid, e:
622                c.errors = e.unpack_errors()
623               
624           
625        logging.info("Setting up atom create template")
626        c.title = CREATE_ATOM_TITLE
627       
628        # set up the drop down content - NB, add special case, 'deployment activity'
629        # - this is just a specialised activity - i.e. with subtype preset
630        c.atomTypes = utils.getVocabTermDataDropdown(g.vtd.getValidTypes(g.vtd.ATOM_CATEGORY))
631        c.providerIDs = utils.getVocabTermDataDropdown(g.vtd.getValidTypes(g.vtd.PROVIDER_CATEGORY))
632
633        try:
634            return self.savePageAndRender('atom_editor/atom_creator', **self.inputs)
635
636        except Exception, e:
637            return self._handleError(e)
638
639   
640    def createGranule(self, **inputs):
641        '''
642        Create a new atom from a granulite file
643        '''
644        logging.info("Setting up new atom from granulite template")
645        c.title='Create new data granule atom - from a granulite file'
646
647        if not hasattr(c, 'errors'):
648            c.errors = {}
649        try:
650            return self.savePageAndRender('atom_editor/atom_granulator', **inputs)
651
652        except Exception, e:
653            return self._handleError(e)
Note: See TracBrowser for help on using the repository browser.