Changeset 118

Show
Ignore:
Timestamp:
04/09/07 14:59:53 (2 years ago)
Author:
rgrp
Message:

Add a format_entry method in annotater.marginalia which produces the correct html sections need by marginalia for js annotation (essentially just a port of existing work from shakespeare.format.TextFormatterAnnotate? -- this is the natural home).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • annotater/trunk/annotater/marginalia/__init__.py

    Revision 117 Revision 118
    1import os 1import os 
    2 2 
    3def get_media_header(media_url_base, annotation_store_fqdn, page_uri): 3def get_media_header(media_url_base, annotation_store_fqdn, page_uri): 
    4    """Get html head section including all media links needed for marginalia. 4    """Get html head section including all media links needed for marginalia. 
    5 5 
    6    @param media_url_base: base url for all the links to media files. 6    @param media_url_base: base url for all the links to media files. 
    7    @param annotation_store_fqdn: fully qualified domain name (e.g. 7    @param annotation_store_fqdn: fully qualified domain name (e.g. 
    8        http://sub.domain.com/) for the annotation store app. Because of 8        http://sub.domain.com/) for the annotation store app. Because of 
    9        hard-coding in the marginalia js libraries offset url from the FQDN 9        hard-coding in the marginalia js libraries offset url from the FQDN 
    10        for annotation store then must be '/annotation', i.e. store must reside 10        for annotation store then must be '/annotation', i.e. store must reside 
    11        at http://fqdn/annotation/. 11        at http://fqdn/annotation/. 
    12    @param page_uri: uri for page you are annotating (used as page identifier) 12    @param page_uri: uri for page you are annotating (used as page identifier) 
    13    """ 13    """ 
    14    if annotation_store_fqdn.endswith('/'): 14    if annotation_store_fqdn.endswith('/'): 
    15        annotation_store_fqdn = annotation_store_fqdn[:-1] 15        annotation_store_fqdn = annotation_store_fqdn[:-1] 
    16    # we re-add a trailing slash so strip if already there 16    # we re-add a trailing slash so strip if already there 
    17    if media_url_base.endswith('/'): 17    if media_url_base.endswith('/'): 
    18        media_url_base = media_url_base[:-1] 18        media_url_base = media_url_base[:-1] 
    19    values = { 19    values = { 
    20            'media_url' : media_url_base, 20            'media_url' : media_url_base, 
    21            'app_url'   : annotation_store_fqdn, 21            'app_url'   : annotation_store_fqdn, 
    22            'page_uri'  : page_uri, 22            'page_uri'  : page_uri, 
    23            } 23            } 
    24    html_header = \ 24    html_header = \ 
    25''' 25''' 
    26        <link rel="stylesheet" type="text/css" href="%(media_url)s/example.css"/> 26        <link rel="stylesheet" type="text/css" href="%(media_url)s/example.css"/> 
    27    <script type="text/javascript" src="%(media_url)s/log.js"></script> 27    <script type="text/javascript" src="%(media_url)s/log.js"></script> 
    28    <script type="text/javascript" src="%(media_url)s/config.js"></script> 28    <script type="text/javascript" src="%(media_url)s/config.js"></script> 
    29    <script type="text/javascript" src="%(media_url)s/html-model.js"></script> 29    <script type="text/javascript" src="%(media_url)s/html-model.js"></script> 
    30    <script type="text/javascript" src="%(media_url)s/domutil.js"></script> 30    <script type="text/javascript" src="%(media_url)s/domutil.js"></script> 
    31    <script type="text/javascript" src="%(media_url)s/ranges.js"></script> 31    <script type="text/javascript" src="%(media_url)s/ranges.js"></script> 
    32    <script type="text/javascript" src="%(media_url)s/post-micro.js"></script> 32    <script type="text/javascript" src="%(media_url)s/post-micro.js"></script> 
    33    <script type="text/javascript" src="%(media_url)s/rest-annotate.js"></script> 33    <script type="text/javascript" src="%(media_url)s/rest-annotate.js"></script> 
    34    <script type="text/javascript" src="%(media_url)s/lang/en.js"></script> 34    <script type="text/javascript" src="%(media_url)s/lang/en.js"></script> 
    35    <script type="text/javascript" src="%(media_url)s/annotation.js"></script> 35    <script type="text/javascript" src="%(media_url)s/annotation.js"></script> 
    36    <script type="text/javascript" src="%(media_url)s/smartcopy.js"></script> 36    <script type="text/javascript" src="%(media_url)s/smartcopy.js"></script> 
    37    <script type="text/javascript"> 37    <script type="text/javascript"> 
    38      annotationInit( '%(app_url)s', 'anonymous', 'anonymous', null ); 38      annotationInit( '%(app_url)s', 'anonymous', 'anonymous', null ); 
    39      smartcopyInit( ); 39      smartcopyInit( ); 
    40      function generic_onload() 40      function generic_onload() 
    41      { 41      { 
    42        showAllAnnotations( "%(page_uri)s#*" ); 42        showAllAnnotations( "%(page_uri)s#*" ); 
    43      }; 43      }; 
    44    </script> 44    </script> 
    45    <!-- must be called after generic_onload is defined --> 45    <!-- must be called after generic_onload is defined --> 
    46    <script type="text/javascript" src="%(media_url)s/onload.js"></script> 46    <script type="text/javascript" src="%(media_url)s/onload.js"></script> 
    47''' % values 47''' % values 
    48    return html_header 48    return html_header 
    49 49 
    50 50 
    51def get_buttons(page_uri): 51def get_buttons(page_uri): 
    52    "Get Show/Hide Annotation buttons." 52    "Get Show/Hide Annotation buttons." 
    53    html = \ 53    html = \ 
    54''' 54''' 
    55  <form> 55  <form> 
    56    <!-- add #* so that all annotations are picked up --> 56    <!-- add #* so that all annotations are picked up --> 
    57    <input type="button" onclick='showAllAnnotations( "%(page_uri)s#*")' value="Show Annotations" /><br /> 57    <input type="button" onclick='showAllAnnotations( "%(page_uri)s#*")' value="Show Annotations" /><br /> 
    58    <input type="button" onclick='hideAllAnnotations( "%(page_uri)s#*")' value="Hide Annotations" /><br /> 58    <input type="button" onclick='hideAllAnnotations( "%(page_uri)s#*")' value="Hide Annotations" /><br /> 
    59  </form> 59  </form> 
    60''' 60''' 
    61    out = html % { 'page_uri' : page_uri } 61    out = html % { 'page_uri' : page_uri } 
    62    return out 62    return out 
    63 63 
    64 64 
      65def format_entry(**kwargs): 
      66    """Create a marginalia 'entry', that is the basic annotatable unit. 
      67    @param kwargs: 
      68        content: the html content which is to become the annotatable item. 
      69        title: title for the annotation (if any). 
      70        page_uri: uri for this page (used with the id to create the id for the 
      71            annotation). 
      72        id: id for this annotation with the page 
      73        author: author of the annotation. 
      74    """ 
      75    entry_template = u''' 
      76    <div id="%(id)s" class="hentry"> 
      77        <h3 class="entry-title">%(title)s</h3> 
      78        <div class="entry-content"> 
      79            %(content)s 
      80        </div><!-- /entry-content --> 
      81        <p class="metadata"> 
      82            <a rel="bookmark" href="%(page_uri)s#%(id)s">#</a> 
      83            <span class="author">%(author)s</span> 
      84        </p> 
      85        <div class="notes"> 
      86            <button class="createAnnotation" onclick="createAnnotation('%(id)s',true)" title="Click here to create an annotation">&gt;</button> 
      87            <ol> 
      88                <li></li> 
      89            </ol> 
      90        </div><!-- /notes --> 
      91    </div><!-- /hentry --> 
      92''' 
      93    values = { 
      94            'content' : '', 
      95            'title' : '', 
      96            'id' : 'm0', 
      97            'page_uri' : 'http://localhost:8080/', 
      98            'author' : '', 
      99            } 
      100    for key in kwargs: 
      101        values[key] = kwargs[key] 
      102    result = entry_template % values 
      103    return result 
      104 
      105 
    65class MarginaliaMedia(object): 106class MarginaliaMedia(object): 
    66    """WSGI App to make available the marginalia media files needed for 107    """WSGI App to make available the marginalia media files needed for 
    67    marginalia javascript annotation to work. 108    marginalia javascript annotation to work. 
    68    """ 109    """ 
    69 110 
    70    def __init__(self, mount_path): 111    def __init__(self, mount_path): 
    71        """ 112        """ 
    72        @param mount_path: url path at which this app is mounted e.g. /marginalia 113        @param mount_path: url path at which this app is mounted e.g. /marginalia 
    73        """ 114        """ 
    74        self.mount_path = mount_path 115        self.mount_path = mount_path 
    75        # remove the trailing slash 116        # remove the trailing slash 
    76        if self.mount_path.endswith('/'): 117        if self.mount_path.endswith('/'): 
    77            self.mount_path = self.mount_path[:-1] 118            self.mount_path = self.mount_path[:-1] 
    78        import paste.urlparser 119        import paste.urlparser 
    79        self.fileserver_app = paste.urlparser.make_pkg_resources( 120        self.fileserver_app = paste.urlparser.make_pkg_resources( 
    80                {}, 121                {}, 
    81                'annotater', 122                'annotater', 
    82                'annotater/marginalia' 123                'annotater/marginalia' 
    83                ) 124                ) 
    84 125 
    85    def __call__(self, environ, start_response): 126    def __call__(self, environ, start_response): 
    86        path_info = environ['PATH_INFO'] 127        path_info = environ['PATH_INFO'] 
    87        filename = path_info[len(self.mount_path):] 128        filename = path_info[len(self.mount_path):] 
    88        environ['PATH_INFO'] = filename 129        environ['PATH_INFO'] = filename 
    89        return self.fileserver_app(environ, start_response) 130        return self.fileserver_app(environ, start_response) 
    90        if filename.endswith('.js') or filename.endswith('.css'): 131        if filename.endswith('.js') or filename.endswith('.css'): 
    91            status = '200 OK' 132            status = '200 OK' 
    92            if filename.endswith('.js'): filetype = 'text/javascript' 133            if filename.endswith('.js'): filetype = 'text/javascript' 
    93            else: filetype = 'text/css' 134            else: filetype = 'text/css' 
    94            response_headers = [('Content-type', filetype)] 135            response_headers = [('Content-type', filetype)] 
    95            start_response(status, response_headers) 136            start_response(status, response_headers) 
    96            fp = os.path.join(self.media_path, filename) 137            fp = os.path.join(self.media_path, filename) 
    97            fo = file(fp) 138            fo = file(fp) 
    98            content = fo.read() 139            content = fo.read() 
    99            fo.close() 140            fo.close() 
    100            return [content] 141            return [content] 
    101        else: 142        else: 
    102            status = '404 Not Found' 143            status = '404 Not Found' 
    103            response_headers = [('Content-type','text/html')] 144            response_headers = [('Content-type','text/html')] 
    104            start_response(status, response_headers) 145            start_response(status, response_headers) 
    105            out = 'File %s not found' % filename 146            out = 'File %s not found' % filename 
    106            return [out] 147            return [out] 
    107 148 
  • annotater/trunk/annotater/marginalia/marginalia_test.py

    Revision 117 Revision 118
    1import os 1import os 
    2from StringIO import StringIO 2from StringIO import StringIO 
    3 3 
    4import twill 4import twill 
    5from twill import commands as web 5from twill import commands as web 
    6 6 
    7import annotater.marginalia 7import annotater.marginalia 
    8 8 
    9 9 
    10class TestGetMediaHeader: 10class TestGetMediaHeader: 
    11    base_url = '/marginalia' 11    base_url = '/marginalia' 
    12    app_url = 'http://localhost:5000' 12    app_url = 'http://localhost:5000' 
    13    page_uri = 'http://demo.openshakespeare.org/demo.html'  13    page_uri = 'http://demo.openshakespeare.org/demo.html'  
    14 14 
    15    def test_1(self): 15    def test_1(self): 
    16        out = annotater.marginalia.get_media_header(self.base_url, 16        out = annotater.marginalia.get_media_header(self.base_url, 
    17                self.app_url, 17                self.app_url, 
    18                self.page_uri) 18                self.page_uri) 
    19        assert self.base_url in out 19        assert self.base_url in out 
    20        assert self.app_url in out 20        assert self.app_url in out 
    21        assert '<script type="text/javascript" src="' in out 21        assert '<script type="text/javascript" src="' in out 
    22        assert self.page_uri in out 22        assert self.page_uri in out 
    23 23 
    24    def test_no_trailing_slash_on_app_url(self): 24    def test_no_trailing_slash_on_app_url(self): 
    25        app_url = self.app_url + '/' 25        app_url = self.app_url + '/' 
    26        out = annotater.marginalia.get_media_header(self.base_url, 26        out = annotater.marginalia.get_media_header(self.base_url, 
    27                app_url, 27                app_url, 
    28                self.page_uri) 28                self.page_uri) 
    29        assert app_url not in out 29        assert app_url not in out 
    30        assert app_url[:-1] in out 30        assert app_url[:-1] in out 
    31 31 
    32 32 
    33class TestGetButtons: 33class TestGetButtons: 
    34 34 
    35    def test_1(self): 35    def test_1(self): 
    36        uri = 'http://demo.openshakespeare.org/view?name=blahblah' 36        uri = 'http://demo.openshakespeare.org/view?name=blahblah' 
    37        out = annotater.marginalia.get_buttons(uri) 37        out = annotater.marginalia.get_buttons(uri) 
    38        assert uri in out 38        assert uri in out 
      39 
      40 
      41class TestFormatEntry: 
      42 
      43    starttext = unicode('''Blah \xc3\xa6 
      44blah &amp; blah''', 'utf-8') 
      45    exp = u''' 
      46    <div id="m2" class="hentry"> 
      47        <h3 class="entry-title">Test Stuff</h3> 
      48        <div class="entry-content"> 
      49            Blah \xe6 
      50blah &amp; blah 
      51        </div><!-- /entry-content --> 
      52        <p class="metadata"> 
      53            <a rel="bookmark" href="http://demo.openshakespeare.org/#m2">#</a> 
      54            <span class="author">Nemo</span> 
      55        </p> 
      56        <div class="notes"> 
      57            <button class="createAnnotation" onclick="createAnnotation('m2',true)" title="Click here to create an annotation">&gt;</button> 
      58            <ol> 
      59                <li></li> 
      60            </ol> 
      61        </div><!-- /notes --> 
      62    </div><!-- /hentry --> 
      63''' 
      64     
      65    def test_format(self): 
      66        newtitle = 'Test Stuff' 
      67        page_url = 'http://demo.openshakespeare.org/' 
      68        out = annotater.marginalia.format_entry( 
      69                content=self.starttext, 
      70                page_uri=page_url, 
      71                title=newtitle, 
      72                id='m2', 
      73                author='Nemo', 
      74                ) 
      75        exp = self.exp.encode('utf-8') 
      76        print '"%s"' % out.encode('utf-8') 
      77        print '"%s"' % exp 
      78        for count in range(len(out)): 
      79            end = max(count+5, len(out))  
      80            if out[count] != self.exp[count]: 
      81                print 'Error:', count, exp[count:count+5] 
      82        assert out == self.exp 
    39 83 
    40 84 
    41class TestMarginaliaFiles: 85class TestMarginaliaFiles: 
    42 86 
    43    def setup_method(self, name=''): 87    def setup_method(self, name=''): 
    44        # set without trailing slash 88        # set without trailing slash 
    45        self.base_url = '/marginalia' 89        self.base_url = '/marginalia' 
    46        wsgi_app = annotater.marginalia.MarginaliaMedia(self.base_url) 90        wsgi_app = annotater.marginalia.MarginaliaMedia(self.base_url) 
    47        self.base_url = '/marginalia' + '/' 91        self.base_url = '/marginalia' + '/' 
    48        twill.add_wsgi_intercept('localhost', 8080, lambda : wsgi_app) 92        twill.add_wsgi_intercept('localhost', 8080, lambda : wsgi_app) 
    49        twill.set_output(StringIO()) 93        twill.set_output(StringIO()) 
    50        self.siteurl = 'http://localhost:8080' 94        self.siteurl = 'http://localhost:8080' 
    51 95 
    52    def teardown_method(self, name=''): 96    def teardown_method(self, name=''): 
    53        twill.remove_wsgi_intercept('localhost', 8080) 97        twill.remove_wsgi_intercept('localhost', 8080) 
    54 98 
    55    def test_js(self): 99    def test_js(self): 
    56        filename = 'domutil.js' 100        filename = 'domutil.js' 
    57        url = self.siteurl + self.base_url + filename 101        url = self.siteurl + self.base_url + filename 
    58        print url 102        print url 
    59        web.go(url) 103        web.go(url) 
    60        web.code(200) 104        web.code(200) 
    61        web.find('ELEMENT_NODE = 1;') 105        web.find('ELEMENT_NODE = 1;') 
    62 106 
    63    def test_js_2(self): 107    def test_js_2(self): 
    64        filename = 'lang/en.js' 108        filename = 'lang/en.js' 
    65        url = self.siteurl + self.base_url + filename 109        url = self.siteurl + self.base_url + filename 
    66        web.go(url) 110        web.go(url) 
    67        web.code(200) 111        web.code(200) 
    68 112 
    69 113 
    70class TestMarginaliaFiles2(TestMarginaliaFiles): 114class TestMarginaliaFiles2(TestMarginaliaFiles): 
    71    # a different base name 115    # a different base name 
    72 116 
    73    def setup_method(self, name=''): 117    def setup_method(self, name=''): 
    74        self.base_url = '/' 118        self.base_url = '/' 
    75        wsgi_app = annotater.marginalia.MarginaliaMedia(self.base_url) 119        wsgi_app = annotater.marginalia.MarginaliaMedia(self.base_url) 
    76        twill.add_wsgi_intercept('localhost', 8080, lambda : wsgi_app) 120        twill.add_wsgi_intercept('localhost', 8080, lambda : wsgi_app) 
    77        twill.set_output(StringIO()) 121        twill.set_output(StringIO()) 
    78        self.siteurl = 'http://localhost:8080' 122        self.siteurl = 'http://localhost:8080' 
    79 123