| 1 | import os | 1 | import os |
|---|
| 2 | | 2 | |
|---|
| 3 | def get_media_header(media_url_base, annotation_store_fqdn, page_uri): | 3 | def 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 | |
|---|
| 51 | def get_buttons(page_uri): | 51 | def 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 | |
|---|
| | | 65 | def 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">></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 | |
|---|
| 65 | class MarginaliaMedia(object): | 106 | class 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 | |
|---|