source: documentViewer/documentViewer.py @ 602:ef1d0a1fc9fa

Last change on this file since 602:ef1d0a1fc9fa was 602:ef1d0a1fc9fa, checked in by casties, 11 years ago

fix bug with no dc-metadata.

File size: 43.0 KB
Line 
1from OFS.Folder import Folder
2from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
3from Products.PageTemplates.PageTemplateFile import PageTemplateFile
4from App.ImageFile import ImageFile
5from AccessControl import ClassSecurityInfo
6from AccessControl import getSecurityManager
7from Globals import package_home
8
9import xml.etree.ElementTree as ET
10
11import os
12import sys
13import urllib
14import logging
15import math
16import urlparse 
17import re
18import string
19import json
20
21from Products.MetaDataProvider import MetaDataFolder
22
23from SrvTxtUtils import getInt, utf8ify, getText, getHttpData, refreshingImageFileIndexHtml
24   
25def serializeNode(node, encoding="utf-8"):
26    """returns a string containing node as XML"""
27    s = ET.tostring(node)
28   
29    # 4Suite:
30    #    stream = cStringIO.StringIO()
31    #    Ft.Xml.Domlette.Print(node, stream=stream, encoding=encoding)
32    #    s = stream.getvalue()
33    #    stream.close()
34    return s
35
36def getMDText(node):
37    """returns the @text content from the MetaDataProvider metadata node"""
38    if isinstance(node, dict):
39        return node.get('@text', None)
40   
41    return node
42
43def getParentPath(path, cnt=1):
44    """returns pathname shortened by cnt"""
45    # make sure path doesn't end with /
46    path = path.rstrip('/')
47    # split by /, shorten, and reassemble
48    return '/'.join(path.split('/')[0:-cnt])
49
50def getPnForPf(docinfo, pf, default=0):
51    """returns image number for image file name or default"""
52    if 'imgFileNames' in docinfo:
53        pn = docinfo['imgFileNames'].get(pf, None)
54        if pn is None:
55            # try to cut extension
56            xi = pf.rfind('.')
57            if xi > 0:
58                pf = pf[:xi]
59                # try again, else return 0
60                pn = docinfo['imgFileNames'].get(pf, default)
61            else:
62                # no extension
63                pn = default
64               
65        return pn
66   
67    return default
68
69def getPfForPn(docinfo, pn, default=None):
70    """returns image file name for image number or default"""
71    if 'imgFileIndexes' in docinfo:
72        pn = docinfo['imgFileIndexes'].get(pn, default)
73        return pn
74   
75    return default
76
77
78##
79## documentViewer class
80##
81class documentViewer(Folder):
82    """document viewer"""
83    meta_type="Document viewer"
84   
85    security=ClassSecurityInfo()
86    manage_options=Folder.manage_options+(
87        {'label':'Configuration','action':'changeDocumentViewerForm'},
88        )
89   
90    metadataService = None
91    """MetaDataFolder instance"""
92   
93
94    #
95    # templates and forms
96    #
97    # viewMode templates
98    viewer_text = PageTemplateFile('zpt/viewer/viewer_text', globals())
99    viewer_xml = PageTemplateFile('zpt/viewer/viewer_xml', globals())
100    viewer_image = PageTemplateFile('zpt/viewer/viewer_image', globals())
101    viewer_index = PageTemplateFile('zpt/viewer/viewer_index', globals())
102    viewer_thumbs = PageTemplateFile('zpt/viewer/viewer_thumbs', globals())
103    viewer_indexonly = PageTemplateFile('zpt/viewer/viewer_indexonly', globals())
104    # available layer types (annotator not default)
105    builtinLayers = {'text': ['dict','search','gis'],
106                     'xml': None, 'image': None, 'index': ['extended']}
107    availableLayers = builtinLayers;
108    # layer templates
109    layer_text_dict = PageTemplateFile('zpt/viewer/layer_text_dict', globals())
110    layer_text_search = PageTemplateFile('zpt/viewer/layer_text_search', globals())
111    layer_text_annotator = PageTemplateFile('zpt/viewer/layer_text_annotator', globals())
112    layer_text_gis = PageTemplateFile('zpt/viewer/layer_text_gis', globals())
113    layer_text_pundit = PageTemplateFile('zpt/viewer/layer_text_pundit', globals())
114    layer_image_annotator = PageTemplateFile('zpt/viewer/layer_image_annotator', globals())
115    layer_image_search = PageTemplateFile('zpt/viewer/layer_image_search', globals())
116    layer_index_extended = PageTemplateFile('zpt/viewer/layer_index_extended', globals())
117    # toc templates
118    toc_thumbs = PageTemplateFile('zpt/viewer/toc_thumbs', globals())
119    toc_text = PageTemplateFile('zpt/viewer/toc_text', globals())
120    toc_figures = PageTemplateFile('zpt/viewer/toc_figures', globals())
121    toc_concordance = PageTemplateFile('zpt/viewer/toc_concordance', globals())
122    toc_handwritten = PageTemplateFile('zpt/viewer/toc_handwritten', globals())
123    toc_none = PageTemplateFile('zpt/viewer/toc_none', globals())
124    # other templates
125    common_template = PageTemplateFile('zpt/viewer/common_template', globals())
126    info_xml = PageTemplateFile('zpt/viewer/info_xml', globals())
127    docuviewer_css = ImageFile('css/docuviewer.css',globals())
128    # make docuviewer_css refreshable for development
129    docuviewer_css.index_html = refreshingImageFileIndexHtml
130    docuviewer_ie_css = ImageFile('css/docuviewer_ie.css',globals())
131    # make docuviewer_ie_css refreshable for development
132    #docuviewer_ie_css.index_html = refreshingImageFileIndexHtml
133    jquery_js = ImageFile('js/jquery.js',globals())
134   
135   
136    def __init__(self,id,imageScalerUrl=None,textServerName=None,title="",digilibBaseUrl=None,thumbcols=2,thumbrows=5,authgroups="mpiwg"):
137        """init document viewer"""
138        self.id=id
139        self.title=title
140        self.thumbcols = thumbcols
141        self.thumbrows = thumbrows
142        # authgroups is list of authorized groups (delimited by ,)
143        self.authgroups = [s.strip().lower() for s in authgroups.split(',')]
144        # create template folder so we can always use template.something
145       
146        templateFolder = Folder('template')
147        self['template'] = templateFolder # Zope-2.12 style
148        #self._setObject('template',templateFolder) # old style
149        try:
150            import MpdlXmlTextServer
151            textServer = MpdlXmlTextServer.MpdlXmlTextServer(id='fulltextclient',serverName=textServerName)
152            templateFolder['fulltextclient'] = textServer
153            #templateFolder._setObject('fulltextclient',textServer)
154        except Exception, e:
155            logging.error("Unable to create MpdlXmlTextServer for fulltextclient: "+str(e))
156           
157        try:
158            from Products.zogiLib.zogiLib import zogiLib
159            zogilib = zogiLib(id="zogilib", title="zogilib for docuviewer", dlServerURL=imageScalerUrl, layout="book")
160            templateFolder['zogilib'] = zogilib
161            #templateFolder._setObject('zogilib',zogilib)
162        except Exception, e:
163            logging.error("Unable to create zogiLib for zogilib: "+str(e))
164           
165        try:
166            # assume MetaDataFolder instance is called metadata
167            self.metadataService = getattr(self, 'metadata')
168        except Exception, e:
169            logging.error("Unable to find MetaDataFolder 'metadata': "+str(e))
170           
171        if digilibBaseUrl is not None:
172            self.digilibBaseUrl = digilibBaseUrl
173            self.digilibScalerUrl = digilibBaseUrl + '/servlet/Scaler'
174            self.digilibViewerUrl = digilibBaseUrl + '/jquery/digilib.html'
175           
176       
177    # proxy text server methods to fulltextclient
178    def getTextPage(self, **args):
179        """returns full text content of page"""
180        return self.template.fulltextclient.getTextPage(**args)
181
182    def getSearchResults(self, **args):
183        """loads list of search results and stores XML in docinfo"""
184        return self.template.fulltextclient.getSearchResults(**args)
185
186    def getResultsPage(self, **args):
187        """returns one page of the search results"""
188        return self.template.fulltextclient.getResultsPage(**args)
189
190    def getTextInfo(self, **args):
191        """returns document info from the text server"""
192        return self.template.fulltextclient.getTextInfo(**args)
193
194    def getToc(self, **args):
195        """loads table of contents and stores XML in docinfo"""
196        return self.template.fulltextclient.getToc(**args)
197
198    def getTocPage(self, **args):
199        """returns one page of the table of contents"""
200        return self.template.fulltextclient.getTocPage(**args)
201
202    def getRepositoryType(self, **args):
203        """get repository type"""
204        return self.template.fulltextclient.getRepositoryType(**args)
205
206    def getTextDownloadUrl(self, **args):
207        """get list of gis places on one page"""
208        return self.template.fulltextclient.getTextDownloadUrl(**args)
209 
210    def getPlacesOnPage(self, **args):
211        """get list of gis places on one page"""
212        return self.template.fulltextclient.getPlacesOnPage(**args)
213 
214    # Thumb list for CoolIris Plugin
215    thumbs_main_rss = PageTemplateFile('zpt/thumbs_main_rss', globals())
216    security.declareProtected('View','thumbs_rss')
217    def thumbs_rss(self,mode,url,viewMode="auto",start=None,pn=1):
218        '''
219        view it
220        @param mode: defines how to access the document behind url
221        @param url: url which contains display information
222        @param viewMode: image: display images, text: display text, default is auto (try text, else image)
223       
224        '''
225       
226        if not hasattr(self, 'template'):
227            # this won't work
228            logging.error("template folder missing!")
229            return "ERROR: template folder missing!"
230                       
231        if not self.digilibBaseUrl:
232            self.digilibBaseUrl = self.findDigilibUrl() or "http://nausikaa.mpiwg-berlin.mpg.de/digitallibrary"
233           
234        docinfo = self.getDocinfo(mode=mode,url=url)
235        #pageinfo = self.getPageinfo(start=start,current=pn,docinfo=docinfo)
236        pageinfo = self.getPageinfo(start=start,pn=pn, docinfo=docinfo)
237        ''' ZDES '''
238        pt = getattr(self.template, 'thumbs_main_rss')
239       
240        if viewMode=="auto": # automodus gewaehlt
241            if docinfo.has_key("textURL") or docinfo.get('textURLPath',None): #texturl gesetzt und textViewer konfiguriert
242                viewMode="text"
243            else:
244                viewMode="image"
245               
246        return pt(docinfo=docinfo,pageinfo=pageinfo,viewMode=viewMode)
247
248 
249    security.declareProtected('View','index_html')
250    def index_html(self, url, mode="texttool", viewMode="auto", viewLayer=None, tocMode=None, start=None, pn=None, pf=None):
251        """
252        show page
253        @param url: url which contains display information
254        @param mode: defines how to access the document behind url
255        @param viewMode: 'image': display images, 'text': display text, 'xml': display xml, default is 'auto'
256        @param viewLayer: sub-type of viewMode, e.g. layer 'dict' for viewMode='text'
257        @param tocMode: type of 'table of contents' for navigation (thumbs, text, figures, none)
258        """
259       
260        logging.debug("documentViewer(index_html) mode=%s url=%s viewMode=%s viewLayer=%s start=%s pn=%s pf=%s"%(mode,url,viewMode,viewLayer,start,pn,pf))
261       
262        if not hasattr(self, 'template'):
263            # this won't work
264            logging.error("template folder missing!")
265            return "ERROR: template folder missing!"
266           
267        if not getattr(self, 'digilibBaseUrl', None):
268            self.digilibBaseUrl = self.findDigilibUrl() or "http://digilib.mpiwg-berlin.mpg.de/digitallibrary"
269           
270        # mode=filepath should not have toc-thumbs
271        if tocMode is None:
272            if mode == "filepath":
273                tocMode = "none"
274            else:
275                tocMode = "thumbs"
276           
277        # docinfo: information about document (cached)
278        docinfo = self.getDocinfo(mode=mode,url=url,tocMode=tocMode)
279       
280        # userinfo: user settings (cached)
281        userinfo = self.getUserinfo()
282       
283        # auto viewMode: text if there is a text else images
284        if viewMode=="auto": 
285            if docinfo.get('textURLPath', None):
286                # docinfo.get('textURL', None) not implemented yet
287                viewMode = "text"
288                if viewLayer is None and 'viewLayer' not in userinfo:
289                    # use layer dict as default
290                    viewLayer = "dict"
291            else:
292                viewMode = "image"
293               
294        elif viewMode == "text_dict":
295            # legacy fix
296            viewMode = "text"
297            viewLayer = "dict"
298           
299        elif viewMode == 'images':
300            # legacy fix
301            viewMode = 'image'
302            self.REQUEST['viewMode'] = 'image'
303
304        # safe viewLayer in userinfo
305        userinfo['viewLayer'] = viewLayer
306               
307        # pageinfo: information about page (not cached)
308        pageinfo = self.getPageinfo(start=start, pn=pn, pf=pf, docinfo=docinfo, userinfo=userinfo, viewMode=viewMode, viewLayer=viewLayer, tocMode=tocMode)
309                   
310        # get template /template/viewer_$viewMode
311        pt = getattr(self.template, 'viewer_%s'%viewMode, None)
312        if pt is None:
313            logging.error("No template for viewMode=%s!"%viewMode)
314            # TODO: error page?
315            return "No template for viewMode=%s!"%viewMode
316       
317        # and execute with parameters
318        return pt(docinfo=docinfo, pageinfo=pageinfo)
319 
320    def getAvailableLayers(self):
321        """returns dict with list of available layers per viewMode"""
322        return self.availableLayers
323   
324    def findDigilibUrl(self):
325        """try to get the digilib URL from zogilib"""
326        url = self.template.zogilib.getDLBaseUrl()
327        return url
328   
329    def getScalerUrl(self, fn=None, pn=None, dw=100, dh=100, docinfo=None):
330        """returns URL to digilib Scaler with params"""
331        url = None
332        if docinfo is not None:
333            url = docinfo.get('imageURL', None)
334           
335        if url is None:
336            url = self.digilibScalerUrl
337            if fn is None and docinfo is not None:
338                fn = docinfo.get('imagePath','')
339           
340            url += "fn=%s"%fn
341           
342        if pn:
343            url += "&pn=%s"%pn
344           
345        url += "&dw=%s&dh=%s"%(dw,dh)
346        return url
347
348    def getDocumentViewerURL(self):
349        """returns the URL of this instance"""
350        return self.absolute_url()
351   
352    def getStyle(self, idx, selected, style=""):
353        """returns a string with the given style and append 'sel' if idx == selected."""
354        #logger("documentViewer (getstyle)", logging.INFO, "idx: %s selected: %s style: %s"%(idx,selected,style))
355        if idx == selected:
356            return style + 'sel'
357        else:
358            return style
359   
360    def getParams(self, param=None, val=None, params=None, duplicates=None):
361        """returns dict with URL parameters.
362       
363        Takes URL parameters and additionally param=val or dict params.
364        Deletes key if value is None."""
365        # copy existing request params
366        newParams=self.REQUEST.form.copy()
367        # change single param
368        if param is not None:
369            if val is None:
370                if newParams.has_key(param):
371                    del newParams[param]
372            else:
373                newParams[param] = str(val)
374               
375        # change more params
376        if params is not None:
377            for (k, v) in params.items():
378                if v is None:
379                    # val=None removes param
380                    if newParams.has_key(k):
381                        del newParams[k]
382                       
383                else:
384                    newParams[k] = v
385
386        if duplicates:
387            # eliminate lists (coming from duplicate keys)
388            for (k,v) in newParams.items():
389                if isinstance(v, list):
390                    if duplicates == 'comma':
391                        # make comma-separated list of non-empty entries
392                        newParams[k] = ','.join([t for t in v if t])
393                    elif duplicates == 'first':
394                        # take first non-empty entry
395                        newParams[k] = [t for t in v if t][0]
396       
397        return newParams
398   
399    def getLink(self, param=None, val=None, params=None, baseUrl=None, paramSep='&', duplicates='comma'):
400        """returns URL to documentviewer with parameter param set to val or from dict params"""
401        urlParams = self.getParams(param=param, val=val, params=params, duplicates=duplicates)
402        # quote values and assemble into query string (not escaping '/')
403        ps = paramSep.join(["%s=%s"%(k, urllib.quote_plus(utf8ify(v), '/')) for (k, v) in urlParams.items()])
404        if baseUrl is None:
405            baseUrl = self.getDocumentViewerURL()
406           
407        url = "%s?%s"%(baseUrl, ps)
408        return url
409
410    def getLinkAmp(self, param=None, val=None, params=None, baseUrl=None, duplicates='comma'):
411        """link to documentviewer with parameter param set to val"""
412        return self.getLink(param=param, val=val, params=params, baseUrl=baseUrl, paramSep='&', duplicates=duplicates)
413   
414
415    def setAvailableLayers(self, newLayerString=None):
416        """sets availableLayers to newLayerString or tries to autodetect available layers.
417        assumes layer templates have the form layer_{m}_{l} for layer l in mode m.
418        newLayerString is parsed as JSON."""
419        if newLayerString is not None:
420            try:
421                layers = json.loads(newLayerString)
422                if 'text' in layers and 'image' in layers:
423                    self.availableLayers = layers
424                    return
425            except:
426                pass
427
428            logging.error("invalid layers=%s! autodetecting..."%repr(newLayerString))
429           
430        # start with builtin layers
431        self.availableLayers = self.builtinLayers.copy()
432        # add layers from templates
433        for t in self.template:
434            if t.startswith('layer_'):
435                try:
436                    (x, m, l) = t.split('_', 3)
437                    if m not in self.availableLayers:
438                        # mode m doesn't exist -> new list
439                        self.availableLayers[m] = [l]
440                       
441                    else:
442                        # m exists -> append
443                        if l not in self.availableLayers[m]:
444                            self.availableLayers[m].append()
445                           
446                except:
447                    pass
448
449    def getAvailableLayersJson(self):
450        """returns available layers as JSON string."""
451        return json.dumps(self.availableLayers)
452   
453   
454    def getInfo_xml(self,url,mode):
455        """returns info about the document as XML"""
456        if not self.digilibBaseUrl:
457            self.digilibBaseUrl = self.findDigilibUrl() or "http://digilib.mpiwg-berlin.mpg.de/digitallibrary"
458       
459        docinfo = self.getDocinfo(mode=mode,url=url)
460        pt = getattr(self.template, 'info_xml')
461        return pt(docinfo=docinfo)
462
463    def getAuthenticatedUser(self, anon=None):
464        """returns the authenticated user object or None. (ignores Zopes anonymous user)"""
465        user = getSecurityManager().getUser()
466        if user is not None and user.getUserName() != "Anonymous User":
467            return user
468        else:
469            return anon
470
471    def isAccessible(self, docinfo):
472        """returns if access to the resource is granted"""
473        access = docinfo.get('accessType', None)
474        logging.debug("documentViewer (accessOK) access type %s"%access)
475        if access == 'free':
476            logging.debug("documentViewer (accessOK) access is free")
477            return True
478       
479        elif access is None or access in self.authgroups:
480            # only local access -- only logged in users
481            user = self.getAuthenticatedUser()
482            logging.debug("documentViewer (accessOK) user=%s ip=%s"%(user,self.REQUEST.getClientAddr()))
483            return (user is not None)
484       
485        logging.error("documentViewer (accessOK) unknown access type %s"%access)
486        return False
487
488    def getUserinfo(self):
489        """returns userinfo object"""
490        logging.debug("getUserinfo")
491        userinfo = {}
492        # look for cached userinfo in session
493        if self.REQUEST.SESSION.has_key('userinfo'):
494            userinfo = self.REQUEST.SESSION['userinfo']
495            # check if its still current?
496        else:
497            # store in session
498            self.REQUEST.SESSION['userinfo'] = userinfo
499           
500        return userinfo
501
502    def getDocinfo(self, mode, url, tocMode=None):
503        """returns docinfo depending on mode"""
504        logging.debug("getDocinfo: mode=%s, url=%s"%(mode,url))
505        # look for cached docinfo in session
506        if self.REQUEST.SESSION.has_key('docinfo'):
507            docinfo = self.REQUEST.SESSION['docinfo']
508            # check if its still current
509            if docinfo is not None and docinfo.get('mode', None) == mode and docinfo.get('url', None) == url:
510                logging.debug("getDocinfo: docinfo in session. keys=%s"%docinfo.keys())
511                return docinfo
512           
513        # new docinfo
514        docinfo = {'mode': mode, 'url': url}
515        # add self url
516        docinfo['viewerUrl'] = self.getDocumentViewerURL()
517        docinfo['digilibBaseUrl'] = self.digilibBaseUrl
518        docinfo['digilibScalerUrl'] = self.digilibScalerUrl
519        docinfo['digilibViewerUrl'] = self.digilibViewerUrl
520        # get index.meta DOM
521        docUrl = None
522        metaDom = None
523        if mode=="texttool": 
524            # url points to document dir or index.meta
525            metaDom = self.metadataService.getDomFromPathOrUrl(url)
526            if metaDom is None:
527                raise IOError("Unable to find index.meta for mode=texttool!")
528           
529            docUrl = url.replace('/index.meta', '')
530            if url.startswith('/mpiwg/online/'):
531                docUrl = url.replace('/mpiwg/online/', '', 1)
532
533        elif mode=="imagepath":
534            # url points to folder with images, index.meta optional
535            # asssume index.meta in parent dir
536            docUrl = getParentPath(url)
537            metaDom = self.metadataService.getDomFromPathOrUrl(docUrl)
538            docinfo['imagePath'] = url.replace('/mpiwg/online', '', 1)
539
540        elif mode=="filepath":
541            # url points to image file, index.meta optional
542            docinfo['imageURL'] = "%s?fn=%s"%(self.digilibScalerUrl, url)
543            docinfo['numPages'] = 1
544            # asssume index.meta is two path segments up
545            docUrl = getParentPath(url, 2)
546            metaDom = self.metadataService.getDomFromPathOrUrl(docUrl)
547
548        else:
549            logging.error("documentViewer (getdocinfo) unknown mode: %s!"%mode)
550            raise ValueError("Unknown mode %s! Has to be one of 'texttool','imagepath','filepath'."%(mode))
551       
552        docinfo['documentUrl'] = docUrl
553        # process index.meta contents
554        if metaDom is not None and metaDom.tag == 'resource':
555            # document directory name and path
556            resource = self.metadataService.getResourceData(dom=metaDom)
557            if resource:
558                docinfo = self.getDocinfoFromResource(docinfo, resource)
559
560            # texttool info
561            texttool = self.metadataService.getTexttoolData(dom=metaDom, recursive=1, all=True)
562            if texttool:
563                docinfo = self.getDocinfoFromTexttool(docinfo, texttool)
564                # document info from full text server
565                if docinfo.get('textURLPath', None):
566                    docinfo = self.getTextInfo(mode=None, docinfo=docinfo)
567                    # include list of pages TODO: do we need this always?
568                    docinfo = self.getTextInfo(mode='pages', docinfo=docinfo)
569           
570            # bib info
571            bib = self.metadataService.getBibData(dom=metaDom)
572            if bib:
573                # save extended version as 'bibx' TODO: ugly
574                bibx = self.metadataService.getBibData(dom=metaDom, all=True, recursive=1)
575                if len(bibx) == 1:
576                    # unwrap list if possible
577                    bibx = bibx[0]
578                   
579                docinfo['bibx'] = bibx
580                docinfo = self.getDocinfoFromBib(docinfo, bib, bibx)
581            else:
582                # no bib - try info.xml
583                docinfo = self.getDocinfoFromPresentationInfoXml(docinfo)
584               
585            # auth info
586            access = self.metadataService.getAccessData(dom=metaDom)
587            if access:
588                docinfo = self.getDocinfoFromAccess(docinfo, access)
589
590            # attribution info
591            attribution = self.metadataService.getAttributionData(dom=metaDom)
592            if attribution:
593                logging.debug("getDocinfo: attribution=%s"%repr(attribution))
594                docinfo['attribution'] = attribution
595
596            # copyright info
597            copyright = self.metadataService.getCopyrightData(dom=metaDom)
598            if copyright:
599                logging.debug("getDocinfo: copyright=%s"%repr(copyright))
600                docinfo['copyright'] = copyright
601
602            # DRI (permanent ID)
603            dri = self.metadataService.getDRI(dom=metaDom, type='mpiwg')
604            if dri:
605                docinfo['DRI'] = dri
606
607            # (presentation) context
608            ctx = self.metadataService.getContextData(dom=metaDom, all=True)
609            if ctx:
610                logging.debug("getcontext: ctx=%s"%repr(ctx))
611                docinfo['presentationContext'] = ctx
612
613        # image path
614        if mode != 'texttool':
615            # override image path from texttool with url parameter TODO: how about mode=auto?
616            docinfo['imagePath'] = url.replace('/mpiwg/online', '', 1)
617
618        # check numPages
619        if docinfo.get('numPages', 0) == 0:
620            # number of images from digilib
621            if docinfo.get('imagePath', None):
622                imgpath = docinfo['imagePath'].replace('/mpiwg/online', '', 1)
623                logging.debug("imgpath=%s"%imgpath)
624                docinfo['imageURL'] = "%s?fn=%s"%(self.digilibScalerUrl, imgpath)
625                docinfo = self.getDocinfoFromDigilib(docinfo, imgpath)
626            else:
627                # imagePath still missing? try "./pageimg"
628                imgPath = os.path.join(docUrl, 'pageimg')
629                docinfo = self.getDocinfoFromDigilib(docinfo, imgPath)
630                if docinfo.get('numPages', 0) > 0:
631                    # there are pages
632                    docinfo['imagePath'] = imgPath
633                    docinfo['imageURL'] = "%s?fn=%s"%(self.digilibScalerUrl, docinfo['imagePath'])
634
635        # check numPages
636        if docinfo.get('numPages', 0) == 0:
637            if docinfo.get('numTextPages', 0) > 0:
638                # replace with numTextPages (text-only?)
639                docinfo['numPages'] = docinfo['numTextPages']
640
641        # normalize path
642        if 'imagePath' in docinfo and not docinfo['imagePath'].startswith('/'):
643            docinfo['imagePath'] = '/' + docinfo['imagePath']
644
645        logging.debug("documentViewer (getdocinfo) docinfo: keys=%s"%docinfo.keys())
646        # store in session
647        self.REQUEST.SESSION['docinfo'] = docinfo
648        return docinfo
649
650
651    def getDocinfoFromResource(self, docinfo, resource):
652        """reads contents of resource element into docinfo"""
653        docName = resource.get('name', None)
654        docinfo['documentName'] = docName
655        docPath = resource.get('archive-path', None)
656        if docPath:
657            # clean up document path
658            if docPath[0] != '/':
659                docPath = '/' + docPath
660               
661            if docName and (not docPath.endswith(docName)):
662                docPath += "/" + docName
663           
664        else:
665            # use docUrl as docPath
666            docUrl = docinfo['documentURL']
667            if not docUrl.startswith('http:'):
668                docPath = docUrl
669        if docPath:
670            # fix URLs starting with /mpiwg/online
671            docPath = docPath.replace('/mpiwg/online', '', 1)
672
673        docinfo['documentPath'] = docPath
674        return docinfo
675
676    def getDocinfoFromTexttool(self, docinfo, texttool):
677        """reads contents of texttool element into docinfo"""
678        logging.debug("texttool=%s"%repr(texttool))
679        # unpack list if necessary
680        if isinstance(texttool, list):
681            texttool = texttool[0]
682                                   
683        # image dir
684        imageDir = getMDText(texttool.get('image', None))
685        docPath = getMDText(docinfo.get('documentPath', None))
686        if imageDir and docPath:
687            imageDir = os.path.join(docPath, imageDir)
688            imageDir = imageDir.replace('/mpiwg/online', '', 1)
689            docinfo['imagePath'] = imageDir
690       
691        # old style text URL
692        textUrl = getMDText(texttool.get('text', None))
693        if textUrl and docPath:
694            if urlparse.urlparse(textUrl)[0] == "": #keine url
695                textUrl = os.path.join(docPath, textUrl) 
696           
697            docinfo['textURL'] = textUrl
698   
699        # new style text-url-path (can be more than one with "repository" attribute)
700        textUrlNode = texttool.get('text-url-path', None)
701        if not isinstance(textUrlNode, list):
702            textUrlNode = [textUrlNode]
703
704        for tun in textUrlNode:
705            textUrl = getMDText(tun)
706            if textUrl:
707                textUrlAtts = tun.get('@attr')
708                if (textUrlAtts and 'repository' in textUrlAtts):
709                    textRepo = textUrlAtts['repository']
710                    # use matching repository
711                    if self.getRepositoryType() == textRepo:
712                        docinfo['textURLPath'] = textUrl
713                        docinfo['textURLRepository'] = textRepo
714                        break
715               
716                else:
717                    # no repo attribute - use always
718                    docinfo['textURLPath'] = textUrl
719           
720        # page flow
721        docinfo['pageFlow'] = getMDText(texttool.get('page-flow', 'ltr'))
722           
723        # odd pages are left
724        docinfo['oddPage'] = getMDText(texttool.get('odd-scan-position', 'left'))
725           
726        # number of title page (default 1)
727        docinfo['titlePage'] = getMDText(texttool.get('title-scan-no', 1))
728           
729        # old presentation stuff
730        presentation = getMDText(texttool.get('presentation', None))
731        if presentation and docPath:
732            if presentation.startswith('http:'):
733                docinfo['presentationUrl'] = presentation
734            else:
735                docinfo['presentationUrl'] = os.path.join(docPath, presentation)
736               
737        # make sure we have at least fake DC data
738        if 'creator' not in docinfo:
739            docinfo['creator'] = '[no author found]'
740           
741        if 'title' not in docinfo:
742            docinfo['title'] = '[no title found]'
743           
744        if 'date' not in docinfo:
745            docinfo['date'] = '[no date found]'
746       
747        return docinfo
748
749    def getDocinfoFromBib(self, docinfo, bib, bibx=None):
750        """reads contents of bib element into docinfo"""
751        logging.debug("getDocinfoFromBib bib=%s"%repr(bib))
752        # put all raw bib fields in dict "bib"
753        docinfo['bib'] = bib
754        bibtype = bib.get('@type', None)
755        docinfo['bibType'] = bibtype
756        # also store DC metadata for convenience
757        dc = self.metadataService.getDCMappedData(bib)
758        docinfo['creator'] = dc.get('creator','')
759        docinfo['title'] = dc.get('title','')
760        docinfo['date'] = dc.get('date','')
761        return docinfo
762           
763    def getDocinfoFromAccess(self, docinfo, acc):
764        """reads contents of access element into docinfo"""
765        #TODO: also read resource type
766        logging.debug("getDocinfoFromAccess acc=%s"%repr(acc))
767        try:
768            acctype = acc['@attr']['type']
769            if acctype:
770                access=acctype
771                if access in ['group', 'institution']:
772                    access = acc['name'].lower()
773               
774                docinfo['accessType'] = access
775
776        except:
777            pass
778       
779        return docinfo
780
781    def getDocinfoFromDigilib(self, docinfo, path):
782        infoUrl=self.digilibBaseUrl+"/dirInfo-xml.jsp?fn="+path
783        # fetch data
784        txt = getHttpData(infoUrl)
785        if not txt:
786            logging.error("Unable to get dir-info from %s"%(infoUrl))
787            return docinfo
788
789        dom = ET.fromstring(txt)
790        dir = dom
791        # save size
792        size = dir.findtext('size')
793        logging.debug("getDocinfoFromDigilib: size=%s"%size)
794        if size:
795            docinfo['numPages'] = int(size)
796        else:
797            docinfo['numPages'] = 0
798            return docinfo
799           
800        # save list of image names and numbers
801        imgNames = {}
802        imgIndexes = {}
803        for f in dir:
804            fn = f.findtext('name')
805            pn = getInt(f.findtext('index'))
806            imgNames[fn] = pn
807            imgIndexes[pn] = fn
808           
809        docinfo['imgFileNames'] = imgNames
810        docinfo['imgFileIndexes'] = imgIndexes
811        return docinfo
812           
813           
814    def getDocinfoFromPresentationInfoXml(self,docinfo):
815        """gets DC-like bibliographical information from the presentation entry in texttools"""
816        url = docinfo.get('presentationUrl', None)
817        if not url:
818            logging.error("getDocinfoFromPresentation: no URL!")
819            return docinfo
820       
821        dom = None
822        metaUrl = None
823        if url.startswith("http://"):
824            # real URL
825            metaUrl = url
826        else:
827            # online path
828            server=self.digilibBaseUrl+"/servlet/Texter?fn="
829            metaUrl=server+url
830       
831        txt=getHttpData(metaUrl)
832        if txt is None:
833            logging.error("Unable to read info.xml from %s"%(url))
834            return docinfo
835           
836        dom = ET.fromstring(txt)
837        docinfo['creator']=getText(dom.find(".//author"))
838        docinfo['title']=getText(dom.find(".//title"))
839        docinfo['date']=getText(dom.find(".//date"))
840        return docinfo
841   
842
843    def getPageinfo(self, pn=None, pf=None, start=None, rows=None, cols=None, docinfo=None, userinfo=None, viewMode=None, viewLayer=None, tocMode=None):
844        """returns pageinfo with the given parameters"""
845        logging.debug("getPageInfo(pn=%s, pf=%s, start=%s, rows=%s, cols=%s, viewMode=%s, viewLayer=%s, tocMode=%s)"%(pn,pf,start,rows,cols,viewMode,viewLayer,tocMode))
846        pageinfo = {}
847        pageinfo['viewMode'] = viewMode
848        # split viewLayer if necessary
849        if isinstance(viewLayer,basestring):
850            viewLayer = viewLayer.split(',')
851           
852        if isinstance(viewLayer, list):
853            logging.debug("getPageinfo: viewLayer is list:%s"%viewLayer)
854            # save (unique) list in viewLayers
855            seen = set()
856            viewLayers = [l for l in viewLayer if l and l not in seen and not seen.add(l)]
857            pageinfo['viewLayers'] = viewLayers
858            # stringify viewLayer
859            viewLayer = ','.join(viewLayers)
860        else:
861            #create list
862            pageinfo['viewLayers'] = [viewLayer]
863                       
864        pageinfo['viewLayer'] = viewLayer
865        pageinfo['tocMode'] = tocMode
866
867        # pf takes precedence over pn
868        if pf:
869            pageinfo['pf'] = pf
870            pn = getPnForPf(docinfo, pf)
871            # replace pf in request params (used for creating new URLs)
872            self.REQUEST.form.pop('pf', None)
873            self.REQUEST.form['pn'] = pn
874        else:
875            pn = getInt(pn, 1)
876            pf = getPfForPn(docinfo, pn)
877            pageinfo['pf'] = pf
878           
879        pageinfo['pn'] = pn
880        rows = int(rows or self.thumbrows)
881        pageinfo['rows'] = rows
882        cols = int(cols or self.thumbcols)
883        pageinfo['cols'] = cols
884        grpsize = cols * rows
885        pageinfo['groupsize'] = grpsize
886        # is start is empty use one around pn
887        start = getInt(start, default=(math.ceil(float(pn)/float(grpsize))*grpsize-(grpsize-1)))
888        # int(current / grpsize) * grpsize +1))
889        pageinfo['start'] = start
890        # get number of pages
891        np = int(docinfo.get('numPages', 0))
892        if np == 0:
893            # try numTextPages
894            np = docinfo.get('numTextPages', 0)
895            if np != 0:
896                docinfo['numPages'] = np
897
898        # cache table of contents
899        pageinfo['tocPageSize'] = getInt(self.REQUEST.get('tocPageSize', 30))
900        pageinfo['numgroups'] = int(np / grpsize)
901        if np % grpsize > 0:
902            pageinfo['numgroups'] += 1
903
904        pageFlowLtr = docinfo.get('pageFlow', 'ltr') != 'rtl'
905        oddScanLeft = docinfo.get('oddPage', 'left') != 'right'
906        # add zeroth page for two columns
907        pageZero = (cols == 2 and (pageFlowLtr != oddScanLeft))
908        pageinfo['pageZero'] = pageZero
909        pageinfo['pageBatch'] = self.getPageBatch(start=start, rows=rows, cols=cols, pageFlowLtr=pageFlowLtr, pageZero=pageZero, minIdx=1, maxIdx=np)
910        # more page parameters
911        pageinfo['characterNormalization'] = self.REQUEST.get('characterNormalization','reg')
912        if docinfo.get('pageNumbers'):
913            # get original page numbers
914            pageNumber = docinfo['pageNumbers'].get(pn, None)
915            if pageNumber is not None:
916                pageinfo['pageNumberOrig'] = pageNumber['no']
917                pageinfo['pageNumberOrigNorm'] = pageNumber['non']
918       
919        # cache search results
920        query = self.REQUEST.get('query',None)
921        pageinfo['query'] = query
922        if query and viewMode == 'text':
923            pageinfo['resultPageSize'] = getInt(self.REQUEST.get('resultPageSize', 10))
924            queryType = self.REQUEST.get('queryType', 'fulltextMorph')
925            pageinfo['queryType'] = queryType
926            pageinfo['resultStart'] = getInt(self.REQUEST.get('resultStart', '1'))
927            self.getSearchResults(mode=queryType, query=query, pageinfo=pageinfo, docinfo=docinfo)
928           
929            # highlighting
930            highlightQuery = self.REQUEST.get('highlightQuery', None)
931            if highlightQuery:
932                pageinfo['highlightQuery'] = highlightQuery
933                pageinfo['highlightElement'] = self.REQUEST.get('highlightElement', '')
934                pageinfo['highlightElementPos'] = self.REQUEST.get('highlightElementPos', '')
935           
936        return pageinfo
937
938
939    def getPageBatch(self, start=1, rows=10, cols=2, pageFlowLtr=True, pageZero=False, minIdx=1, maxIdx=0):
940        """returns dict with array of page information for one screenfull of thumbnails"""
941        batch = {}
942        grpsize = rows * cols
943        if maxIdx == 0:
944            maxIdx = start + grpsize
945
946        nb = int(math.ceil(maxIdx / float(grpsize)))
947        # list of all batch start and end points
948        batches = []
949        if pageZero:
950            ofs = 0
951        else:
952            ofs = 1
953           
954        for i in range(nb):
955            s = i * grpsize + ofs
956            e = min((i + 1) * grpsize + ofs - 1, maxIdx)
957            batches.append({'start':s, 'end':e})
958           
959        batch['batches'] = batches
960
961        pages = []
962        if pageZero and start == 1:
963            # correct beginning
964            idx = 0
965        else:
966            idx = start
967           
968        for r in range(rows):
969            row = []
970            for c in range(cols):
971                if idx < minIdx or idx > maxIdx:
972                    page = {'idx':None}
973                else:
974                    page = {'idx':idx}
975                   
976                idx += 1
977                if pageFlowLtr:
978                    row.append(page)
979                else:
980                    row.insert(0, page) 
981               
982            pages.append(row)
983           
984        if start > 1:
985            batch['prevStart'] = max(start - grpsize, 1)
986        else:
987            batch['prevStart'] = None
988           
989        if start + grpsize <= maxIdx:
990            batch['nextStart'] = start + grpsize
991        else:
992            batch['nextStart'] = None
993
994        batch['pages'] = pages
995        batch['first'] = minIdx
996        batch['last'] = maxIdx
997        return batch
998       
999    def getBatch(self, start=1, size=10, end=0, data=None, fullData=True):
1000        """returns dict with information for one screenfull of data."""
1001        batch = {}
1002        if end == 0:
1003            end = start + size                   
1004           
1005        nb = int(math.ceil(end / float(size)))
1006        # list of all batch start and end points
1007        batches = []
1008        for i in range(nb):
1009            s = i * size + 1
1010            e = min((i + 1) * size, end)
1011            batches.append({'start':s, 'end':e})
1012           
1013        batch['batches'] = batches
1014        # list of elements in this batch
1015        this = []
1016        j = 0
1017        for i in range(start, min(start+size, end+1)):
1018            if data:
1019                if fullData:
1020                    d = data.get(i, None)
1021                else:
1022                    d = data.get(j, None)
1023                    j += 1
1024           
1025            else:
1026                d = i+1
1027               
1028            this.append(d)
1029           
1030        batch['this'] = this
1031        if start > 1:
1032            batch['prevStart'] = max(start - size, 1)
1033        else:
1034            batch['prevStart'] = None
1035           
1036        if start + size < end:
1037            batch['nextStart'] = start + size
1038        else:
1039            batch['nextStart'] = None
1040       
1041        batch['first'] = start
1042        batch['last'] = end
1043        return batch
1044       
1045
1046    def getAnnotatorGroupsForUser(self, user, annotationServerUrl="http://tuxserve03.mpiwg-berlin.mpg.de/AnnotationManager"):
1047        """returns list of groups {name:*, id:*} on the annotation server for the user"""
1048        groups = []
1049        groupsUrl = "%s/annotator/groups?user=%s"%(annotationServerUrl,user)
1050        data = getHttpData(url=groupsUrl, noExceptions=True)
1051        if data:
1052            res = json.loads(data)
1053            rows = res.get('rows', None)
1054            if rows is None:
1055                return groups
1056            for r in rows:
1057                groups.append({'id': r.get('id', None), 'name': r.get('name', None), 'uri': r.get('uri', None)})
1058               
1059        return groups
1060   
1061
1062    security.declareProtected('View management screens','changeDocumentViewerForm')   
1063    changeDocumentViewerForm = PageTemplateFile('zpt/changeDocumentViewer', globals())
1064   
1065    def changeDocumentViewer(self,title="",digilibBaseUrl=None,thumbrows=2,thumbcols=5,authgroups='mpiwg',availableLayers=None,RESPONSE=None):
1066        """init document viewer"""
1067        self.title=title
1068        self.digilibBaseUrl = digilibBaseUrl
1069        self.digilibScalerUrl = digilibBaseUrl + '/servlet/Scaler'
1070        self.digilibViewerUrl = digilibBaseUrl + '/jquery/digilib.html'
1071        self.thumbrows = thumbrows
1072        self.thumbcols = thumbcols
1073        self.authgroups = [s.strip().lower() for s in authgroups.split(',')]
1074        try:
1075            # assume MetaDataFolder instance is called metadata
1076            self.metadataService = getattr(self, 'metadata')
1077        except Exception, e:
1078            logging.error("Unable to find MetaDataFolder 'metadata': "+str(e))
1079           
1080        self.setAvailableLayers(availableLayers)
1081
1082        if RESPONSE is not None:
1083            RESPONSE.redirect('manage_main')
1084       
1085def manage_AddDocumentViewerForm(self):
1086    """add the viewer form"""
1087    pt=PageTemplateFile('zpt/addDocumentViewer', globals()).__of__(self)
1088    return pt()
1089 
1090def manage_AddDocumentViewer(self,id,imageScalerUrl="",textServerName="",title="",RESPONSE=None):
1091    """add the viewer"""
1092    newObj=documentViewer(id,imageScalerUrl=imageScalerUrl,title=title,textServerName=textServerName)
1093    self._setObject(id,newObj)
1094   
1095    if RESPONSE is not None:
1096        RESPONSE.redirect('manage_main')
Note: See TracBrowser for help on using the repository browser.