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

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

Restructure atom editor controllers - pulling out parent superclass
to allow re-use of shared code + use new routes names + add new method
to handle drop down setting up.

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 ndgUtils import ndgObject
8from ndgUtils.models.Atom import Atom
9from ndgUtils.lib.utilities import escapeSpecialCharacters
10import ndgUtils.lib.existdbclient as edc
11from milk_server.lib.base import *
12from editorconstants import *
13from milk_server.lib.ndgInterface import ndgInterface
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        # save the current atom - to avoid this needing be recreated by the
73        # asynch viewAssociatedData call
74        session['currentAtom'] = c.atom
75        session.save()
76        logging.info("Data model set up")
77
78
79    def __getTidyInputs(self):
80        '''
81        The inputs can be used generically to specify atom attributes - however
82        some inputs should not be set against the Atom object since they use slots
83        so cannot be pickled - which means saving the atom in the session with fail.
84        This method clears out any unwanted inputs
85        '''
86        logging.debug("Getting pickleable input data")
87        inputs = request.params
88        tidyInputs = {}
89        for key, val in inputs.items():
90            if not isinstance(val, cgi.FieldStorage):
91                tidyInputs[key] = val
92        logging.debug("Pickleable data extracted")
93        return tidyInputs
94
95
96
97    def _unpackErrors(self, e):
98        '''
99        Add exception errors to the common error structures - for use
100        in templates
101        @param e: Exception to add
102        '''
103        if not c.errors:
104            c.errors = {}
105
106        errorMessage = e.message
107        if g.debugAtomEditor:
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        if hasattr(e, 'unpack_errors'):
121            c.errors.update(e.unpack_errors())
122           
123        else:
124            c.errors['Unexpected error'] = [c.xml]
125
126        # tidy up errors - escaping any xml tags, if necessary
127        for key, errors in c.errors.items():
128            newErrors = []
129            for error in errors:
130                for err in error.split('<br/>'):
131                    newErrors.append(escapeSpecialCharacters(err))
132            c.errors[key] = newErrors
133
134
135    def _handleError(self, e, template='atom_editor/error'):
136        '''
137        Handle exceptions thrown; if debug mode on, display full stack trace
138        in output, otherwise just show basic error message in error template
139        @param e: Exception to process
140        @keyword template: template to render - 'error' is the default - NB, if
141        an alternative is specified it should have a div with class set to 'error'
142        containing the variable, c.xml to display properly
143        '''
144        self._unpackErrors(e)
145        logging.error(c.xml)
146        response.status_code = 400
147        return render("genshi", template)
148
149
150    def _getExistClient(self, providerID):
151        '''
152        Use the config data to set up and return an eXist client
153        '''
154        logging.info("Setting up eXist client to provider, '%s'" %providerID)
155        # firstly check if there is a current connection available
156        eXistClient = g.eXistDBCons.get(providerID)
157        if eXistClient:
158            logging.info("- found existing client to provider - returning this")
159            return eXistClient
160       
161        # lookup the eXist DB to use according to the provider ID for the
162        # data - NB, this is specified in the milk.config file
163        if not hasattr(self, 'cf'):
164            self._setup()
165
166        existHost = self.cf.get('NDG_EXIST', providerID)
167        configFile = self.cf.get('NDG_EXIST','passwordFile')
168        eXistClient = edc.eXistDBClient(eXistDBHostname = existHost, \
169                                       configFile = configFile)
170        # add the client to the global variables for re-use
171        g.eXistDBCons[providerID] = eXistClient
172        logging.info("Returning eXist client")
173        return eXistClient
174
175
176    def savePageAndRender(self, template, **inputs):
177        '''
178        Save the current path info - to provide a memory function when changing
179        tabs + render the given template with the specified inputs filled in
180        automatically
181        @param template: name of template to render
182        @param inputs: dict of inputs with keynames matching input names in template
183        form 
184        '''
185        logging.debug("Saving current page url (%s) - to keep track of atom editor state" \
186                      %self.pathInfo)
187        session['currentEditor'] = self.pathInfo
188        session.save()
189        logging.debug("Now rendering current template (%s)" %template)
190        html = render("genshi", template)
191        # NB, need html in unicode for for htmlfill.render
192        html = unicode(html, 'utf-8', 'xmlcharrefreplace')
193        for key, val in inputs.items():
194            inputs[key] = str(val)
195   
196        return htmlfill.render(html, inputs)
Note: See TracBrowser for help on using the repository browser.