source: MILK/trunk/milk_server/milk_server/controllers/atom_editor/atomeditorcontroller.py @ 4992

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

Adjust code to fit with changes to ndgCommon codebase + tidy up
structure of ndgInterface to make easier to follow + add documentation
+ add universal debug mode + various other small code tidy ups.

Line 
1'''
2 Class representing base pylons controller for the atom editor shared functions
3 
4 @author: C Byrom, Tessella Dec 2008
5'''
6import logging, xmlrpclib, cgi, traceback
7from ndg.common.src.models.ndgObject import ndgObject
8from ndg.common.src.models.Atom import Atom
9from ndg.common.src.lib.utilities import escapeSpecialCharacters
10from milk_server.lib.base import *
11from milk_server.lib.ndgInterface import ndgInterface
12from milk_server.lib import Utilities
13from editorconstants import *
14from formencode import htmlfill
15   
16class AtomEditorController(BaseController):
17    '''
18    Provides the pylons controller for shared functions
19    '''
20    STANDARD_SAVE = 1
21    ADD_ASSOCIATIONS = 3
22    REMOVE_ASSOCIATIONS = 4
23
24    def _setup(self, uri=None, loadAtom=True):
25        '''
26        Common setup stuff for all the actions on this controller
27        @keyword uri: uri of the atom to set up
28        @keyword loadAtom: if True, load the atom into c.atom, otherwise don't.
29        Default is True.
30        '''
31        logging.info("Setting up AtomEditorController")
32        self.cf=request.environ['ndgConfig']
33        self.inputs = self.__getTidyInputs()
34
35        if uri:
36            self.ndgObject = ndgObject(uri, config=self.cf)
37            if loadAtom:
38                self.__prepareDataModel(uri)
39
40        logging.info("AtomEditorController set up")
41
42
43    def __prepareDataModel(self, uri):
44        '''
45        Set up the underlying atom data model - loading the bulk from eXist
46        then updating any input fields appropriately
47        '''
48        logging.info("Preparing underlying data model")
49        logging.info("Retrieving document to edit")
50        # NB, don't use the cache as docs are likely to change
51        # quite a lot during editing; ensure you've always got
52        # the latest updates loaded
53        interface = ndgInterface()
54        status,x = interface.GetXML(uri, useCache=False)
55
56        if not status:
57            code=400
58            if x.startswith('<p> Access Denied'):
59                code=401
60
61            c.xml='%s'%x
62            response.status_code = code
63            raise SystemError('Problem experienced retrieving atom doc from eXist')
64
65        # NB, passing in the inputs will overwrite any original values with the
66        # user input ones
67        if not self.inputs:
68            self.inputs = self.__getTidyInputs()
69
70        c.atom = Atom(xmlString=str(x), ndgObject = self.ndgObject, **dict(self.inputs))
71
72        # lookup the atom publication state
73        edc = Utilities.getExistClient(c.atom.ME.providerID)
74        c.atom.state = edc.getAtomPublicationState(c.atom.datasetID, 
75                                                   c.atom.ME.providerID)
76        # save the current atom - to avoid this needing be recreated by the
77        # asynch viewAssociatedData call
78        session['currentAtom'] = c.atom
79        session.save()
80        logging.info("Data model set up")
81
82
83    def __getTidyInputs(self):
84        '''
85        The inputs can be used generically to specify atom attributes - however
86        some inputs should not be set against the Atom object since they use slots
87        so cannot be pickled - which means saving the atom in the session with fail.
88        This method clears out any unwanted inputs
89        '''
90        logging.debug("Getting pickleable input data")
91        inputs = request.params
92        tidyInputs = {}
93        for key, val in inputs.items():
94            if not isinstance(val, cgi.FieldStorage):
95                tidyInputs[key] = val
96
97        logging.debug("Pickleable data extracted")
98        return tidyInputs
99
100
101    def _unpackErrors(self, e):
102        '''
103        Add exception errors to the common error structures - for use
104        in templates
105        @param e: Exception to add
106        @return errors: dict of errors
107        '''
108        if not c.errors:
109            c.errors = {}
110
111        errorMessage = e.message
112        if g.debugModeOn == 'True':
113            errorMessage = traceback.format_exc()
114
115        c.xml = escapeSpecialCharacters('Unexpected error loading page [%s]' \
116                                        %str(errorMessage))
117        c.doc = ''
118       
119        # unpack errors, if possible - NB, the c.errors value is used by the template
120        # function, displayErrors, which is used by most of the editor templates
121        if isinstance(e, xmlrpclib.Fault):
122            # strip out the exception type - NB, this is usually native library code
123            # and is of no real interest - and will just confuse viewers
124            c.errors['Unexpected error'] = e.faultString.split(':')[-1] 
125
126        if hasattr(e, 'unpack_errors'):
127            c.errors.update(e.unpack_errors())
128           
129        else:
130            c.errors['Unexpected error'] = [c.xml]
131
132        # tidy up errors - escaping any xml tags, if necessary
133        for key, errors in c.errors.items():
134            newErrors = []
135            for error in errors:
136                for err in error.split('<br/>'):
137                    newErrors.append(escapeSpecialCharacters(err))
138            c.errors[key] = newErrors
139       
140        return c.errors
141
142
143    def _handleError(self, e, template='atom_editor/error'):
144        '''
145        Handle exceptions thrown; if debug mode on, display full stack trace
146        in output, otherwise just show basic error message in error template
147        @param e: Exception to process
148        @keyword template: template to render - 'error' is the default - NB, if
149        an alternative is specified it should have a div with class set to 'error'
150        containing the variable, c.xml to display properly
151        '''
152        self._unpackErrors(e)
153        logging.error(c.xml)
154        response.status_code = 400
155        return render("genshi", template)
156
157
158    def savePageAndRender(self, template, **inputs):
159        '''
160        Save the current path info - to provide a memory function when changing
161        tabs + render the given template with the specified inputs filled in
162        automatically
163        @param template: name of template to render
164        @param inputs: dict of inputs with keynames matching input names in template
165        form 
166        '''
167        logging.debug("Saving current page url (%s) - to keep track of atom editor state" \
168                      %self.pathInfo)
169        session['currentEditor'] = self.pathInfo
170        session.save()
171        logging.debug("Now rendering current template (%s)" %template)
172        html = render("genshi", template)
173        # NB, need html in unicode for for htmlfill.render
174        html = unicode(html, 'utf-8', 'xmlcharrefreplace')
175        for key, val in inputs.items():
176            inputs[key] = str(val)
177
178        return htmlfill.render(html, inputs)
Note: See TracBrowser for help on using the repository browser.