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

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

Disable (old) security code from MILK stack +
make handling of edit mode on/off more robust - redirecting users
back to 'discovery' home page when not in edit mode.

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