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

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

Display more detail on deployments summary - providing clickable link
to deployment doc + properly enable debug mode on/off with boolean
string test + hide help text for atom state when in view mode + hide
link to help page when on help page - but display link to home page +
fix problem with text displayed on 'add selections' button - needed
to set explicit name for this to avoid htmlfill overwriting value
erroneously + remove unecessary package reference from setup.

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