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 *
RevLine 
[764]1#!/usr/bin/env python
2# CGI Script to support prototype NDG MOLES_Portal (Browse) functionality
3# Bryan Lawrence, April, 2006
4
5import cgi
[765]6#import cgitb;ctitb.enable()
[845]7
[764]8import os
[800]9import ElementTree as ET
[845]10
[765]11from insecure import *
[783]12from secure import *
[800]13from stubB import *
[845]14
[814]15from renderEntity import renderEntity
16from renderPage import renderPage
[996]17from renderDiscoverySet import renderDiscoverySet
[1020]18from htmlUtilities import selector,hyperlink
[845]19from Utilities import *
[976]20from ETxmlView import *
[1577]21#from DiscoveryWS import *
22from ndgSearch import ndgSearch
[1152]23from DIF import DIF
[1175]24import base64
[1577]25#from eXistInterface import *
26from DiscoveryState import DiscoveryState
[845]27
[828]28import Cookie
[764]29
[1577]30
31       
[764]32class BrowseSession:
[842]33       
[1023]34        ''' Holds the browse and select history '''
[842]35       
[996]36        def __init__(self,cookie,config):
[842]37               
38                '''Instantiate with an unsecured browse session (security only
39                required if a secure resource is the target) '''
[828]40                self.config=config
[842]41                self.rawCookie=cookie
[828]42                self.cookie=Cookie.SimpleCookie(cookie)
[783]43                self.history=RingBuffer(10)
[1020]44                self.selected=RingBuffer(5)
[1286]45                self.historyNames=RingBuffer(10)
[1020]46                self.__load()
47                       
[828]48        def __toXML(self):
[1020]49                ''' Used to serialise the session into a cookie string '''
50                xml='<ndgCookie><bh>'
[828]51                for uri,name in self.getHistory():
[1175]52                        xml+='<i><u>%s</u><n>%s</n></i>'%(base64.urlsafe_b64encode(uri),name)
[1020]53                xml+='</bh><sh>'
54                for uri,name in self.getSelected():
[1175]55                    xml+='<i><u>%s</u><n>%s</n></i>'%(base64.urlsafe_b64encode(uri),name)
[1020]56                xml+='</sh></ndgCookie>'
[828]57                return xml
[1020]58               
59        def __load(self):
60            ''' get lists of URI values out of a cookie '''
61            try:
[1049]62                e=ET.fromstring(self.cookie['history'].value)
[1020]63                for se in e:
64                    for item in se:
65                        uri,name=item.find('u'),item.find('n')
[1175]66                        self.__addTo(base64.urlsafe_b64decode(uri.text),name.text,se.tag)
[1049]67            except KeyError:
[1020]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()
[1286]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: 
[1020]78                d[tag].append((uri,name))
[1286]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))
[1020]86                       
[842]87        def addToHistory(self,uri,name,ignore=False):
[1020]88           ''' Add a URI to the session history'''
89           self.__addTo(uri,name,'bh',ignore)
90                       
[783]91        def getHistory(self):
[1020]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       
[991]104        def makeCookie(self,ndgSec=None):
[828]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("/", "$")
[1020]113                cookie['history']=self.__toXML()
[1050]114                if ndgSec is not None:
115                    cookie['NDG-ID1']=ndgSec[0]
116                    cookie['NDG-ID2']=ndgSec[1]
[828]117                return cookie
[1098]118
[764]119class CGIcontroller:
120        ''' Currently holds the cgi environment and controls '''
121       
[980]122        def __init__(self,config):
[764]123                ''' Instantiate the CGI environment'''
[1098]124                #
125                # Need to refactor all of this to use the request class and then move
126                # the request class out into the utilities ...
[1317]127                # (underway)
128               
[764]129                self.env=os.environ
[1317]130                #self.path=self.env.get('PATH_INFO','/') #commented out because I don't think
131                #we use it ...
[1185]132                self.config=config
[828]133                self.response=Response()
[1317]134                self.request=Request(self.env,cgi)
135                self.FieldStorage=self.request.variables
[1098]136                self.requestURL=self.request.URL
[1525]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'))
[1152]142                self.serviceFile=self.config.get('services','serviceFile')
[991]143               
[1047]144
145        def makeGateway(self,cookie=None):
[1050]146                ''' Make connection to NDG security and load what is necessary for
147                an NDG cookie to be written '''
[991]148                aa=self.config.get('security','localAA',None)
[1047]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'])
[1048]152                else: cmdLine=None
[1140]153                self.ndgGate=gateway2NDGsession(self.requestURL,self.config,aa,cookie=self.cookie,cmdLine=cmdLine)
[1050]154                self.ndgSec=cmdLine
[764]155
156        def goforit(self):
157                ''' This method actually responds to the user'''
158               
[996]159                self.ViewTextOnly,self.ViewXML=0,0
160               
161                #Instantiate the Session Environment
[1140]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 ...
[996]165                if self.config.logfile is not None: self.config.log(self.cookie)
166               
[1050]167                #this is where we invoke NDG security setup, even if we don't
168                #need it ...
169                self.makeGateway()
170               
[765]171                #this will do for the moment, although I'd rather the whole
[1137]172                #URI was self consistent using the request object.
[1020]173               
[1185]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                   
[1020]178                if 'select' in self.FieldStorage:
[1173]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
[1020]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               
[996]189                #use name as an error return as well in the following calls ...
[1020]190                if 'uri' in self.FieldStorage:
191                    self.uri=self.FieldStorage['uri']
192                    content,name=self.__browse()
[1525]193                elif 'SEARCHSTRING' in self.FieldStorage:
194                    # probably ought to watch out for search=text types ...
195                    self.searchType='text'#self.FieldStorage['search']
[1020]196                    content,name=self.__search()
[1185]197                elif 'Error' in self.FieldStorage:
198                    pass
[996]199                else: 
[1137]200                    content,name=self.error('No URI or search string. Begin browsing via search box or pass a specific URI argument.'),'No URI'
[1023]201
[1006]202                if not self.ViewTextOnly:
[996]203                    historyHTML='<p>'
204                    for item in self.session.getHistory():
[1049]205                        historyHTML+=hyperlink(item[1],item[0])+'<br/>'
[996]206                    historyHTML+='</p>'
[1020]207                    selectHTML='<p>'
208                    for item in self.session.getSelected():
[1049]209                        selectHTML+=hyperlink(item[1],item[0])+'<br/>'
[1020]210                    selectHTML+='</p>'
[996]211                    self.response.content=renderPage(
[1020]212                        content,historyHTML,selectHTML,name,self.config)
[996]213                else:
214                    self.response.content=content
[1050]215                self.response.cookie=self.session.makeCookie(ndgSec=self.ndgSec)
[996]216                return self.response
[935]217               
[996]218        def __browse(self):
219                ''' Handle orthodox browsing '''
220               
[963]221                if self.FieldStorage.has_key('text'):
222                    self.ViewTextOnly=1
223                elif self.FieldStorage.has_key('xml'):
224                    self.ViewXML=1
225               
[1171]226                format='NDG-B0'
227                if self.FieldStorage.has_key('D'):format='DIF'
228               
[842]229                #get the xml document
[857]230                db=self.config.get('db','exist',None)
[1525]231                passwordFile=self.config.get('security','passwords','passwords.txt')
232                               
233                jar=self.config.get('services','jarLoc')
[1171]234                javabin=self.config.get('services','javaBinary')
[1525]235
[1286]236                xml=insecureGetDoc(self.uri,db=db,format=format,jar=jar,javaBin=javabin,
237                                   passwordFile=passwordFile)
[1577]238               
[1525]239                # this isn't ideal but it'll do for now ...
[1577]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)
[1586]246                    if ws.documents is not None:
247                        xml=ws.getDoc(ws.documents[0])
248                    else:
249                        xml='Error'
[1577]250                    if xml[0:5]=='Error':
251                        return self.error('Unable to obtain record [%s] from database'%self.uri),self.uri
252       
[1525]253                #create document instance
[1171]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)
[935]258               
259                if self.b.xml is None:
[1577]260                    content=self.error('Unable to parse record [%s] from database'%self.uri)
[996]261                    return content,0
[935]262                else:
[1171]263                    self.session.addToHistory(self.b.binding.url,self.b.abbreviation)
[991]264                    if  self.b.constraints.exist:
[935]265                        # we need to evaluate them
[1182]266                        #result=self.ndgGate.check(self.b.constraints.SimpleCondition)
[1158]267                        try:
268                             result=self.ndgGate.check(self.b.constraints.SimpleCondition)
269                             if result=='AccessGranted': 
270                                access=1
271                             else:
272                                access=0
[1182]273                        except Exception,e:
[1158]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')
[1182]277                             result+='<p>%s</p>'%str(self.b.constraints.SimpleCondition)
278                             result+='<p>%s</p>'%str(e)
[935]279                    else:
280                        access=1
281                    if access:
[996]282                        name=self.b.name
[963]283                        if self.ViewTextOnly:
[976]284                            self.response.contentType='text/plain'
[963]285                            content=et2text(self.b.tree)
[976]286                        elif self.ViewXML==1:
287                            content=et2html(self.b.tree)
[963]288                        else:
[1158]289                            content=self.b.toHTML(self.config)
[935]290                    else:
[996]291                        name='NDG Browse'
[991]292                        content=result
[996]293
294                    return content,name
[842]295               
[996]296        def __search(self):
297            ''' Handle simple searching within the context of the browse '''
[1525]298            stride=20
[1020]299            if 'SEARCHSTRING' in self.FieldStorage:
300                searchString=self.FieldStorage['SEARCHSTRING']
[1577]301                searchSession,start,howmany=None,1,stride
[1416]302                if 'searchSession' in self.FieldStorage:
[1577]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
[1137]313                title='Search for '+searchString
[1006]314                try:
[1577]315                    ws=ndgSearch() # instantiate
[1006]316                except Exception, e:
317                    return self.error('<p>%s<br/>%s'%(e,'Unable to connect to Search BackEnd')),'Error'
[1577]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()
[996]333                difs=[]
[1577]334                for result in results: difs.append(DIF(result,serviceFile=self.serviceFile,et=1))
[1525]335       
[1020]336                html=renderDiscoverySet(difs,state,selector=self.selector,
337                               summary=1,spatial=1,temporal=1,services=1)
[1137]338               
[996]339                return html,title
340            else:
[1006]341                return self.error('No valid search option'),'Error'
[996]342             
[764]343        def error(self,message):
[825]344                ''' Construct a nice formal response, but don't throw a 404 ... '''
345                return '<p>Error: %s</p>'%message
[764]346               
Note: See TracBrowser for help on using the repository browser.