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

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

First cut of standalone discovery code ... integrated into one python
module with the browe stuff ... (next steps include split into two
parents and one child egg as well as making it actually work).

  • 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.requestURL,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,selector=self.selector,
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.