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

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

Add new home page for the browse service + implement usage of the
before methods on controllers to check if MILK is set up to
allow the various services before the controller methods are invoked.
Also add a top level default controller to redirect user to the correct
home page depending on the mode of operation + improve styles,
remove unused controller and move error template to more general top
level - for re-use across the MILK stack.

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