source: TI07-MOLES/trunk/PythonCode/browse/portal/cgi/browse/browseCGI.py @ 1587

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI07-MOLES/trunk/PythonCode/browse/portal/cgi/browse/browseCGI.py
Revision 1587, 13.9 KB checked in by lawrence, 13 years ago (diff)

Better handling of request URLs for both discovery and browse

  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2# CGI Script to support prototype NDG MOLES_Portal (Browse) functionality
3# Bryan Lawrence, April, 2006
4
5import cgi
6#import cgitb;ctitb.enable()
7
8import os
9import ElementTree as ET
10
11from insecure import *
12from secure import *
13from stubB import *
14
15from renderEntity import renderEntity
16from renderPage import renderPage
17from renderDiscoverySet import renderDiscoverySet
18from htmlUtilities import selector,hyperlink
19from Utilities import *
20from ETxmlView import *
21#from DiscoveryWS import *
22from ndgSearch import ndgSearch
23from DIF import DIF
24import base64
25#from eXistInterface import *
26from DiscoveryState import DiscoveryState
27
28import Cookie
29
30
31       
32class BrowseSession:
33       
34        ''' Holds the browse and select history '''
35       
36        def __init__(self,cookie,config):
37               
38                '''Instantiate with an unsecured browse session (security only
39                required if a secure resource is the target) '''
40                self.config=config
41                self.rawCookie=cookie
42                self.cookie=Cookie.SimpleCookie(cookie)
43                self.history=RingBuffer(10)
44                self.selected=RingBuffer(5)
45                self.historyNames=RingBuffer(10)
46                self.__load()
47                       
48        def __toXML(self):
49                ''' Used to serialise the session into a cookie string '''
50                xml='<ndgCookie><bh>'
51                for uri,name in self.getHistory():
52                        xml+='<i><u>%s</u><n>%s</n></i>'%(base64.urlsafe_b64encode(uri),name)
53                xml+='</bh><sh>'
54                for uri,name in self.getSelected():
55                    xml+='<i><u>%s</u><n>%s</n></i>'%(base64.urlsafe_b64encode(uri),name)
56                xml+='</sh></ndgCookie>'
57                return xml
58               
59        def __load(self):
60            ''' get lists of URI values out of a cookie '''
61            try:
62                e=ET.fromstring(self.cookie['history'].value)
63                for se in e:
64                    for item in se:
65                        uri,name=item.find('u'),item.find('n')
66                        self.__addTo(base64.urlsafe_b64decode(uri.text),name.text,se.tag)
67            except KeyError:
68                pass
69                       
70        def __addTo(self,uri,name,tag,ignore=False):
71            d={'bh':self.history,'sh':self.selected}
72            current=d[tag].tolist()
73            # key the uri's by name ... so we can deal with
74            # duplicate abbreviations.
75            names={}
76            for i in current: names[i[1]]=i[0]
77            if ignore: 
78                d[tag].append((uri,name))
79            else:
80                if name in names.keys(): # we might already have it
81                    if names[name]!=uri:
82                        #it's a duplicate name, but not a duplicate uri
83                        d[tag].append((uri,EnumerateString(name)))
84                else: #it's new ...
85                    d[tag].append((uri,name))
86                       
87        def addToHistory(self,uri,name,ignore=False):
88           ''' Add a URI to the session history'''
89           self.__addTo(uri,name,'bh',ignore)
90                       
91        def getHistory(self):
92           ''' Return a list of the items in the history '''
93           return self.history.tolist()
94       
95        def addToSelected(self,uri,name,ignore=False):
96            ''' Add a URI to the selected items '''
97            self.__addTo(uri,name,'sh',ignore)
98            self.__addTo(uri,name,'bh',ignore)
99                   
100        def getSelected(self):
101            ''' Return a list of selected items '''     
102            return self.selected.tolist()       
103       
104        def makeCookie(self,ndgSec=None):
105                ''' Create a local cookie '''
106                import md5,time,base64
107                # start by creating a unique session id
108                m=md5.new()
109                m.update('this is a seed string')
110                m.update(str(time.time()))
111                cookie=Cookie.SimpleCookie()
112                cookie['session']=base64.encodestring(m.digest())[:-3].replace("/", "$")
113                cookie['history']=self.__toXML()
114                if ndgSec is not None:
115                    cookie['NDG-ID1']=ndgSec[0]
116                    cookie['NDG-ID2']=ndgSec[1]
117                return cookie
118
119class CGIcontroller:
120        ''' Currently holds the cgi environment and controls '''
121       
122        def __init__(self,config):
123                ''' Instantiate the CGI environment'''
124                #
125                # Need to refactor all of this to use the request class and then move
126                # the request class out into the utilities ...
127                # (underway)
128               
129                self.env=os.environ
130                #self.path=self.env.get('PATH_INFO','/') #commented out because I don't think
131                #we use it ...
132                self.config=config
133                self.response=Response()
134                self.request=Request(self.env,cgi)
135                self.FieldStorage=self.request.variables
136                self.requestURL=self.request.URL
137                if 'SEARCHSTRING' in self.FieldStorage:
138                        url=self.request.baseURL+'?%s'%urllib.urlencode(
139                        {'SEARCHSTRING':self.FieldStorage['SEARCHSTRING']})
140                else: url=self.requestURL
141                self.selector=selector(url,'select',config.get('layout','selectI'))
142                self.serviceFile=self.config.get('services','serviceFile')
143               
144
145        def makeGateway(self,cookie=None):
146                ''' Make connection to NDG security and load what is necessary for
147                an NDG cookie to be written '''
148                aa=self.config.get('security','localAA',None)
149                if 'NDG-ID1' in self.FieldStorage and 'NDG-ID2' in self.FieldStorage:
150                    #this is a redirect following login ...
151                    cmdLine=(self.FieldStorage['NDG-ID1'],self.FieldStorage['NDG-ID2'])
152                else: cmdLine=None
153                self.ndgGate=gateway2NDGsession(self.requestURL,self.config,aa,cookie=self.cookie,cmdLine=cmdLine)
154                self.ndgSec=cmdLine
155
156        def goforit(self):
157                ''' This method actually responds to the user'''
158               
159                self.ViewTextOnly,self.ViewXML=0,0
160               
161                #Instantiate the Session Environment
162                cookie=self.env.get('HTTP_COOKIE',None)
163                self.session=BrowseSession(cookie,self.config)
164                self.cookie=self.session.cookie # now a cookie class object ...
165                if self.config.logfile is not None: self.config.log(self.cookie)
166               
167                #this is where we invoke NDG security setup, even if we don't
168                #need it ...
169                self.makeGateway()
170               
171                #this will do for the moment, although I'd rather the whole
172                #URI was self consistent using the request object.
173               
174                if 'Error' in self.FieldStorage:
175                    content,name=self.FieldStorage['Error'],'URL Error'
176                    self.response.cookie=self.session.makeCookie(ndgSec=self.ndgSec)
177                   
178                if 'select' in self.FieldStorage:
179                    n=self.FieldStorage['name']
180                    s=ServiceBinding(n=n,
181                                     entryID=self.FieldStorage['select'],
182                                     serviceFile=self.serviceFile)
183                    self.session.addToSelected(s.url,n)
184
185                    #we also need to trim the selector off the current requestURL
186                    self.requestURL=self.requestURL[0:self.requestURL.find('&select')]
187                    self.selector.baseURL=self.requestURL
188               
189                #use name as an error return as well in the following calls ...
190                if 'uri' in self.FieldStorage:
191                    self.uri=self.FieldStorage['uri']
192                    content,name=self.__browse()
193                elif 'SEARCHSTRING' in self.FieldStorage:
194                    # probably ought to watch out for search=text types ...
195                    self.searchType='text'#self.FieldStorage['search']
196                    content,name=self.__search()
197                elif 'Error' in self.FieldStorage:
198                    pass
199                else: 
200                    content,name=self.error('No URI or search string. Begin browsing via search box or pass a specific URI argument.'),'No URI'
201
202                if not self.ViewTextOnly:
203                    historyHTML='<p>'
204                    for item in self.session.getHistory():
205                        historyHTML+=hyperlink(item[1],item[0])+'<br/>'
206                    historyHTML+='</p>'
207                    selectHTML='<p>'
208                    for item in self.session.getSelected():
209                        selectHTML+=hyperlink(item[1],item[0])+'<br/>'
210                    selectHTML+='</p>'
211                    self.response.content=renderPage(
212                        content,historyHTML,selectHTML,name,self.config)
213                else:
214                    self.response.content=content
215                self.response.cookie=self.session.makeCookie(ndgSec=self.ndgSec)
216                return self.response
217               
218        def __browse(self):
219                ''' Handle orthodox browsing '''
220               
221                if self.FieldStorage.has_key('text'):
222                    self.ViewTextOnly=1
223                elif self.FieldStorage.has_key('xml'):
224                    self.ViewXML=1
225               
226                format='NDG-B0'
227                if self.FieldStorage.has_key('D'):format='DIF'
228               
229                #get the xml document
230                db=self.config.get('db','exist',None)
231                passwordFile=self.config.get('security','passwords','passwords.txt')
232                               
233                jar=self.config.get('services','jarLoc')
234                javabin=self.config.get('services','javaBinary')
235
236                xml=insecureGetDoc(self.uri,db=db,format=format,jar=jar,javaBin=javabin,
237                                   passwordFile=passwordFile)
238               
239                # this isn't ideal but it'll do for now ...
240                if xml is None: 
241                    #then it's not an NDG DIF, and we need to go get it, but
242                    #unfortunately that requires a search on the entry id, then
243                    #a document retrieval
244                    ws=ndgSearch()
245                    ws.search(self.uri)
246                    if ws.documents is not None:
247                        xml=ws.getDoc(ws.documents[0])
248                    else:
249                        xml='Error'
250                    if xml[0:5]=='Error':
251                        return self.error('Unable to obtain record [%s] from database'%self.uri),self.uri
252       
253                #create document instance
254                if format=='NDG-B0':
255                    self.b=stubB(xml,serviceFile=self.serviceFile)
256                elif format=='DIF':
257                    self.b=DIF(xml,serviceFile=self.serviceFile)
258               
259                if self.b.xml is None:
260                    content=self.error('Unable to parse record [%s] from database'%self.uri)
261                    return content,0
262                else:
263                    self.session.addToHistory(self.b.binding.url,self.b.abbreviation)
264                    if  self.b.constraints.exist:
265                        # we need to evaluate them
266                        #result=self.ndgGate.check(self.b.constraints.SimpleCondition)
267                        try:
268                             result=self.ndgGate.check(self.b.constraints.SimpleCondition)
269                             if result=='AccessGranted': 
270                                access=1
271                             else:
272                                access=0
273                        except Exception,e:
274                             #unable to make ndggate check ... deny
275                             access=0
276                             result=self.error('Unable to connect to security gateway, access to secure resource denied')
277                             result+='<p>%s</p>'%str(self.b.constraints.SimpleCondition)
278                             result+='<p>%s</p>'%str(e)
279                    else:
280                        access=1
281                    if access:
282                        name=self.b.name
283                        if self.ViewTextOnly:
284                            self.response.contentType='text/plain'
285                            content=et2text(self.b.tree)
286                        elif self.ViewXML==1:
287                            content=et2html(self.b.tree)
288                        else:
289                            content=self.b.toHTML(self.config)
290                    else:
291                        name='NDG Browse'
292                        content=result
293
294                    return content,name
295               
296        def __search(self):
297            ''' Handle simple searching within the context of the browse '''
298            stride=20
299            if 'SEARCHSTRING' in self.FieldStorage:
300                searchString=self.FieldStorage['SEARCHSTRING']
301                searchSession,start,howmany=None,1,stride
302                if 'searchSession' in self.FieldStorage:
303                      searchSession=self.FieldStorage['searchSession']
304                if 'start' in self.FieldStorage:
305                    try:
306                        start=int(self.FieldStorage['start'])
307                    except: pass
308                if 'howmany' in self.FieldStorage:
309                    try:
310                        stride=int(self.FieldStorage['howmany'])
311                    except: pass
312
313                title='Search for '+searchString
314                try:
315                    ws=ndgSearch() # instantiate
316                except Exception, e:
317                    return self.error('<p>%s<br/>%s'%(e,'Unable to connect to Search BackEnd')),'Error'
318
319                hits=0
320                if searchSession is not None:
321                   pass
322                   #return self.error('<p>search sessions not yet implemented</p>'),'Error'
323                else:
324                   pass
325               
326                documents=ws.search(searchString,start=start,howmany=stride)
327                hits=ws.hits
328                id=ws.serverSessionID
329               
330                if hits==0: return self.error('No records found'),title
331                state=DiscoveryState(id,self.request,hits,stride=stride,offset=start)
332                results=ws.getAllDocsAsElements()
333                difs=[]
334                for result in results: difs.append(DIF(result,serviceFile=self.serviceFile,et=1))
335       
336                html=renderDiscoverySet(difs,state,
337                               summary=1,spatial=1,temporal=1,services=1)
338               
339                return html,title
340            else:
341                return self.error('No valid search option'),'Error'
342             
343        def error(self,message):
344                ''' Construct a nice formal response, but don't throw a 404 ... '''
345                return '<p>Error: %s</p>'%message
346               
Note: See TracBrowser for help on using the repository browser.