1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """Advene http server.
20
21 This module defines a number of classes that describe the structure of
22 the Advene webserver. The L{AdveneWebServer} class is the main server
23 class.
24
25 URL syntax
26 ==========
27
28 The URL syntax is described in each decription class.
29
30 The server can run standalone or embedded in another application
31 (typically the C{advene} GUI). In all cases, Webserver depends on
32 AdveneController.
33 """
34
35 import advene.core.config as config
36 import advene.core.version
37
38 import sys
39 import os
40 import re
41 import urllib
42 import cgi
43 import socket
44 import imghdr
45
46 from gettext import gettext as _
47
48 import cherrypy
49
50 if int(cherrypy.__version__.split('.')[0]) < 3:
51 raise _("The webserver requires version 3.0 of CherryPy at least.")
52
53 import advene.util.helper as helper
54 from advene.model.cam.package import Package
55 from advene.model.tales import AdveneContext
56 from advene.model.exceptions import NoSuchElementError, UnreachableImportError
57 import advene.util.session
58
59 from simpletal.simpleTALES import PathNotFoundException
60 from simpletal.simpleTAL import TemplateParseException
61
63 if name.startswith("_tales_"):
64 return name[7:]
65 else:
66 return name
67
68 DEBUG=True
70 """Common functionalities for all cherrypy nodes.
71 """
73 self.controller=controller
74
76 """Error message handling.
77 """
78 err = sys.exc_info()
79 if DEBUG:
80 print "Error handling"
81 import traceback, StringIO
82 bodyFile = StringIO.StringIO()
83 traceback.print_exc(file = bodyFile)
84 cherrypy.response.status=500
85 cherrypy.response.headers['Content-type']='text/html'
86 cherrypy.response.body = ["""<html><head><title>Advene - Error</title></head><body>Sorry, an error occured. <pre>%s</pre></body></html>""" % bodyFile.getvalue() ]
87 bodyFile.close()
88
89 else:
90
91 cherrypy.response.body = ['Error: ' + str(err[0])]
92
94 """Returns a string representing the active location bar.
95
96 This method will use the current URL path, and return an HTML string
97 where the different components of the path are linked to their URL.
98
99 @return: a HTML string
100 @rtype: string
101 """
102 s = urllib.splittag(
103 urllib.splitquery(cherrypy.request.path_info)[0]
104 )[0]
105 path = s.split('/')[1:]
106 return """<a href="/">/</a>""" + "/".join(
107 ["""<a href="%s">%s</a>"""
108 % (uri, name)
109 for (name, uri) in zip(path,
110 ["/"+"/".join(path[:i+1])
111 for i in range(len(path))])]
112 )
113
115 """Write the cache-control headers in the response.
116
117 This method sends cache-control headers to ensure that the
118 browser does not cache the response, as it may vary from one
119 call to another.
120
121 @return: nothing
122 """
123 cherrypy.response.headers['Pragma']='no-cache'
124 cherrypy.response.headers['Cache-Control']='max-age=0'
125
126 - def start_html (self, title="", headers=None, head_section=None, body_attributes="",
127 mode=None, mimetype=None, duplicate_title=False, cache=False):
128 """Starts writing a HTML response (header + common body start).
129
130 @param title: Title of the HTML document (default : "")
131 @type title: string
132 @param headers: additional headers to add
133 @type headers: a list of tuples (keyword, value)
134 @param head_section: additional HTML code to add in the <head></head> section
135 @type head_section: string
136 @param body_attributes: attributes added to the body tag
137 @type body_attributes: string
138
139 @param mode: presentation mode (navigation, raw). Default: navigation
140 @type mode: string
141 @param duplicate_title: duplicate title as HTML header (h1)
142 @type duplicate_title: boolean
143 @param cache: make the document cacheable. Default: False
144 @type cache: boolean
145 @return: nothing
146 """
147 if mode is None:
148 mode = config.data.webserver['displaymode']
149 cherrypy.response.status=200
150 if mode == 'navigation' or mimetype is None or mimetype == 'text/html':
151 mimetype='text/html; charset=utf-8'
152
153 if mimetype.startswith('text/') and 'charset' not in mimetype:
154 mimetype += '; charset=utf-8'
155 cherrypy.response.headers['Content-type']=mimetype
156 if headers is not None:
157 for h in headers:
158 cherrypy.request.headers[h[0]]=h[1]
159 if not cache:
160 self.no_cache ()
161
162 res=[]
163 if mode == "navigation":
164 res.append("""<html><head><title>%s</title><link rel="stylesheet" type="text/css" href="/data/advene.css" />""" % title)
165 if head_section is not None:
166 res.append(head_section)
167
168 res.append("</head><body %s>" % body_attributes)
169
170 res.append(_("""
171 <p>
172 <a href="/admin">Server administration</a> |
173 <a href="/media">Media control</a> |
174 <a href="%(path)s?mode=raw">Raw view</a>
175 </p>
176 Location: %(locationbar)s
177 <hr>
178 """) % { 'locationbar': self.location_bar (),
179 'path': cherrypy.request.path_info } )
180
181 if duplicate_title and mode == 'navigation':
182 res.append("<h1>%s</h1>\n" % title)
183
184 return "".join(res)
185
186 - def send_no_content(self):
187 """Sends a No Content (204) response.
188
189 Mainly used in /media handling.
190 """
191 cherrypy.response.status=204
192 self.no_cache ()
193 return ""
194
196 """Sends a redirect response.
197
198 As this method generates headers, it must be called before other
199 headers or content.
200
201 @param location: the URL to redirect to.
202 @type location: string (URL)
203 """
204 raise cherrypy.HTTPRedirect(location)
205
207 """Sends an error response.
208
209 @param message: the error message
210 @type message: string
211 """
212 if message is None:
213 message=_("Unspecified Error")
214 raise cherrypy.HTTPError(status, _("""
215 <h1>Error</h1>
216 <p>An error occurred:</p>
217 %s
218 """) % message)
219
221 """Return the image type (mime) of the object.
222
223 This method examines the content of the given object, and
224 returns either a mime-type if it is an image, I{None} if it is
225 not.
226
227 @param o: an object
228 @type o: any
229 @return: the content-type if o is an image, else None
230 @rtype: string
231 """
232 res=imghdr.what (None, str(o))
233 if res is not None:
234 return "image/%s" % res
235 else:
236 return None
237
239 """Handles the X{/admin} requests.
240
241 The C{/admin} folder contains the following elements for the
242 administration of the server:
243
244 - C{/admin/list} : display the list of currently loaded packages
245 - C{/admin/load} : load a new package
246 - C{/admin/save/alias} : save a package
247 - C{/admin/delete/alias} : remove a loaded package
248 - C{/admin/status} : display current status
249 - C{/admin/display} : display or set the default webserver display mode
250 - C{/admin/halt} : halt the webserver
251
252 Accessing the C{/admin} folder itself displays the summary
253 (equivalent to the root document).
254
255 Loading, saving or deleting packages is done by specifying the
256 C{alias} parameter. In the case of the C{/admin/load} action,
257 the C{uri} parameter must provide the appropriate URI.
258
259 Setting the X{display mode}
260 ===========================
261
262 Accessing the C{/admin/display} element updates the server
263 display mode. The data should be available as a parameter
264 named C{mode}, which is either C{default} or C{raw}, or as
265 last element in the URI, for instance
266 C{/admin/display/navigation}
267
268 @param l: the access path as a list of elements,
269 with the initial one (C{access}) omitted
270 @type l: list
271 @param query: the options given in the URL
272 @type query: dict
273 """
275 """Display the main administration page.
276
277 This method displays the administration page of the server, which
278 should link to all functionalities.
279 """
280 res=[ self.start_html (_("Server Administration"), duplicate_title=True, mode='navigation') ]
281 if self.controller.server.displaymode == 'raw':
282 switch='navigation'
283 else:
284 switch='raw'
285 mode_sw="""%(mode)s (<a href="/admin/display/%(switch)s">switch to %(switch)s</a>)""" % {
286 'mode': self.controller.server.displaymode, 'switch': switch }
287
288 res.append(_("""
289 <p><a href="/admin/reset">Reset the server</a></p>
290 <p><a href="/admin/halt">Halt the server</a></p>
291 <p><a href="/admin/list">List available files</a></p>
292 <p><a href="/packages">List loaded packages</a> (%(packagelist)s)</p>
293 <p>Display mode : %(displaymode)s</p>
294 <hr>
295 <p>Load a package :
296 <form action="/admin/load" method="GET">
297 Alias: <input type="text" name="alias" /><br>
298 URI: <input type="text" name="uri" /><br>
299 <input type="submit" value="Load" />
300 </form>
301 </body></html>
302 """) % { 'packagelist': " | ".join( ['<a href="/packages/%s">%s</a>' % (alias, alias)
303 for alias in self.controller.packages.keys() ] ),
304 'displaymode': mode_sw })
305 return "".join(res)
306 index.exposed=True
307
309 """Display available Advene files.
310
311 This method displays the data files in the data directory.
312 Maybe it should be removed when the server runs embedded.
313 """
314 res=[ self.start_html (_("Available files"), duplicate_title=True, mode='navigation') ]
315 res.append ("<ul>")
316
317 l=[ os.path.join(self.controller.server.packages_directory, n)
318 for n in os.listdir(self.controller.server.packages_directory)
319 if n.lower().endswith('.czp') ]
320 l.sort(lambda a, b: cmp(a.lower(), b.lower()))
321 for uri in l:
322 name, ext = os.path.splitext(uri)
323 alias = re.sub('[^a-zA-Z0-9_]', '_', os.path.basename(name))
324 res.append ("""
325 <li><a href="/admin/load?alias=%(alias)s&uri=%(uri)s">%(uri)s</a></li>
326 """ % {'alias':alias, 'uri':uri})
327 res.append ("""
328 </ul>
329 """)
330 return "".join(res)
331 list.exposed=True
332
333 - def load(self, *args, **query):
334 """Load the specified URI with the given alias.
335 """
336 try:
337 alias = query['alias']
338 except KeyError:
339 return self.send_error (501,
340 _("""You should specify an alias"""))
341 try:
342 uri = query['uri']
343 except KeyError:
344 return self.send_error (501,
345 _("""You should specify an uri"""))
346 try:
347
348
349 self.controller.load_package (uri=uri, alias=alias)
350 return "".join( (
351 self.start_html (_("Package %s loaded") % alias, duplicate_title=True, mode='navigation'),
352 _("""<p>Go to the <a href="/packages/%(alias)s">%(alias)s</a> package, or to the <a href="/packages">package list</a>.""") % { 'alias': alias }
353 ))
354 except Exception, e:
355 return self.send_error(501, _("""<p>Cannot load package %(alias)s : %(error)s</p>""" % {
356 'alias': alias,
357 'error': str(e) }))
358 load.exposed=True
359
361 """Unload a package.
362 """
363 try:
364 self.controller.unregister_package (alias)
365 return "".join((
366 self.start_html (_("Package %s deleted") % alias, duplicate_title=True, mode='navigation'),
367 _("""<p>Go to the <a href="/packages">package list</a>.""")
368 ))
369 except Exception, e:
370 return self.send_error(501, _("""<p>Cannot delete package %(alias)s : %(error)s</p>""" % {
371 'alias': alias,
372 'error': str(e) }
373 ))
374 delete.exposed = True
375
376 - def save(self, alias=None):
377 """Save a package.
378 """
379 try:
380 if alias is not None:
381
382 self.controller.save_package(alias=alias)
383 else:
384 self.controller.save_package()
385 alias='default'
386 return "".join((
387 self.start_html (_("Package %s saved") % alias, duplicate_title=True, mode='navigation'),
388 _("""<p>Go to the <a href="/packages/%(alias)s">%(alias)s</a> package, or to the <a href="/packages">package list</a>.""") % { 'alias': alias }
389 ))
390 except Exception, e:
391 return self.send_error(501, _("""<p>Cannot save package %(alias)s : %(error)s</p>""" % {
392 'alias': alias,
393 'error': str(e) }
394 ))
395 save.exposed=True
396
398 """Reset packages list.
399 """
400 self.controller.reset()
401 return self.start_html (_('Server reset'), duplicate_title=True, mode='navigation')
402 reset.exposed=True
403
405 """Halt server.
406 """
407 self.controller.server.stop()
408 raise SystemExit(0)
409 halt.exposed=True
410
412 """Set display mode.
413 """
414 if mode:
415
416 if mode in ('raw', 'navigation'):
417 self.controller.server.displaymode=mode
418 ref=cherrypy.request.headers.get('Referer', "/")
419 return self.send_redirect(ref)
420 else:
421
422 cherrypy.response.status=200
423 cherrypy.response.headers['Content-type']='text/plain'
424 self.no_cache ()
425 return self.controller.server.displaymode
426 display.exposed=True
427
429 """Node for packages access.
430 """
432 """Display currently available (loaded) packages.
433
434 This method displays the list of currently loaded
435 packages. The generated list provides links for accessing,
436 reloading, saving or removing the package.
437 """
438 res=[ self.start_html (_("Loaded package(s)"), mode='navigation') ]
439
440 res.append (_("""
441 <h1>Loaded package(s)</h1>
442 <table border="1" width="50%">
443 <tr>
444 <th>Alias</th>
445 <th>Action</th>
446 <th>URI</th>
447 <th>Annotations</th>
448 </tr>
449 """))
450 for alias in self.controller.packages.keys():
451 p = self.controller.packages[alias]
452 res.append (_("""<tr>
453 <td><a href="/packages/%(alias)s">%(alias)s</a></td>
454 <td align="center"><a href="/admin/load?alias=%(alias)s&uri=%(uri)s">Reload</a>|<a href="/admin/delete?alias=%(alias)s">Drop</a>|<a href="/admin/save?alias=%(alias)s">Save</a></td>
455 <td>%(uri)s</td>
456 <td>%(size)d</td>
457 </tr>
458 """) % { 'alias':alias, 'uri':p.uri, 'size':len(list(p.own.annotations)) })
459 res.append ("""
460 </ul>
461 """)
462 return "".join(res)
463 index.exposed=True
464
466 """Display a view for a TALES expression.
467
468 This method displays the view for the element defined by the
469 expression C{tales} wrt to package C{p}, with facultative
470 parameters in C{query}. It handles the X{/packages} folder.
471
472 The query can hold an optional parameter, named C{mode}, which
473 indicates a specific display mode for the resulting
474 object. Valid modes are:
475
476 - C{image} : the object is an image, and we generate the
477 correct headers.
478 - C{raw} : do not display the navigation interface, but only the
479 object's view
480 - C{content} : return the content data of an object, with
481 its specific mime-type
482 - C{default} : default mode, with navigation interface
483
484 The C{display mode} has a default value at the server level,
485 wich can be set through the C{/admin} folder.
486
487 An autodetect feature will force the display mode to C{image}
488 if the object is an image (depending on the return value of
489 L{image_type}).
490
491 All other other parameters given on the URL path are kepts in
492 the C{query} dictionary, which is available in TALES
493 expressions through the C{request/} root element.
494
495 @param p: the package in which the expression should be evaluated
496 @type p: advene.model.Package
497 @param tales: a TALES expression
498 @type tales: string
499 @param query: options used in TAL/TALES processing
500 @type query: dict
501 """
502 res=[]
503 alias = self.controller.aliases[p]
504
505 if query is None:
506 query={}
507
508 if tales == "":
509 expr = "here"
510 elif tales.startswith ('options'):
511 expr = tales
512 else:
513 expr = "here/%s" % tales
514
515
516 advene.util.session.session.package=self.controller.package
517
518 context = self.controller.build_context (here=p, alias=alias)
519 context.pushLocals()
520 context.setLocal('request', query)
521
522
523 context.setLocal (u'view', p)
524
525 try:
526 objet = context.evaluate(expr)
527 except PathNotFoundException, e:
528 self.start_html (_("Error"), duplicate_title=True, mode='navigation')
529 res.append (_("""The TALES expression %s is not valid.""") % tales)
530 res.append (unicode(e.args).encode('utf-8'))
531 return "".join(res)
532 except NoSuchElementError, e:
533 self.start_html (_("Error"), duplicate_title=True)
534 res.append (_("""The element %s cannot be found.""") % unicode(e))
535 return "".join(res)
536 except UnreachableImportError, e:
537 self.start_html (_("Error"), duplicate_title=True)
538 res.append (_("""The element %s is in a package which could not be imported.""") % unicode(e))
539 return "".join(res)
540
541
542
543
544
545
546
547
548 displaymode = self.controller.server.displaymode
549
550 if query.has_key('mode'):
551 displaymode = query['mode']
552 del (query['mode'])
553
554 if displaymode != "raw":
555 displaymode = "navigation"
556
557
558 if hasattr (objet, 'view') and callable (objet.view):
559
560 context = self.controller.build_context(here=objet, alias=alias)
561 context.pushLocals()
562 context.setLocal('request', query)
563
564 context.setLocal(u'view', objet)
565 try:
566 v=objet.view (context=context)
567 if v.contenttype.startswith('text'):
568 res.append( self.start_html(mimetype=v.contenttype) )
569 res.append (unicode(v).encode('utf-8'))
570 else:
571 res.append( self.start_html(mimetype=v.contenttype, mode='raw') )
572 res.append(v)
573 except PathNotFoundException, e:
574 res.append( self.start_html(_("Error")) )
575 res.append(_("<h1>Error</h1>"))
576 res.append(_("""<p>There was an error in the TALES expression.</p>
577 <pre>%s</pre>""") % cgi.escape(unicode(e.args[0]).encode('utf-8')))
578 return "".join(res)
579 else:
580 mimetype=None
581 try:
582 mimetype = objet.mimetype
583 except AttributeError:
584 try:
585 mimetype = objet.contenttype
586 except AttributeError:
587 pass
588 try:
589 if mimetype and mimetype.startswith('text'):
590 res.append( self.start_html(mimetype=mimetype) )
591 res.append (unicode(objet).encode('utf-8'))
592 else:
593 res.append( self.start_html(mimetype=mimetype, mode='raw' ) )
594 res.append(str(objet))
595 return res
596 except PathNotFoundException, e:
597 res.append(_("<h1>Error</h1>"))
598 res.append(_("""<p>There was an error.</p>
599 <pre>%s</pre>""") % cgi.escape(unicode(e.args[0]).encode('utf-8')))
600 except TemplateParseException, e:
601 res.append(_("<h1>Error</h1>"))
602 res.append(_("""<p>There was an error in the template code.</p>
603 <p>Tag name: <strong>%(tagname)s</strong></p>
604 <p>Error message: <em>%(message)s</em></p>""") % {
605 'tagname': cgi.escape(e.location),
606 'message': e.errorDescription})
607
608
609 if displaymode == 'navigation' and 'html' in cherrypy.response.headers['Content-type']:
610 uri=cherrypy.request.path_info
611 levelup = uri[:uri.rindex("/")]
612 auto_components = [ c
613 for c in helper.get_valid_members (objet)
614 if not c.startswith('----') ]
615 auto_components.sort()
616 try:
617 auto_views = objet.validViews
618 auto_views.sort()
619 except AttributeError:
620 auto_views = []
621
622 res.append (_("""
623 <hr>
624 <p>
625 Location: %(location)s<br>
626 <form name="navigation" method="GET">
627 <a href="%(levelup)s">Up one level</a> |
628 Next level :
629 <select name="path" onchange="submit()">
630 <option selected></option>"
631 """) % {
632 'location': self.location_bar (),
633 'levelup': levelup})
634
635 if hasattr (objet, 'view'):
636 res.append ("<option>view</option>")
637
638 res.append ("\n".join(
639 ["""<option>%s</option>""" % c for c in auto_components]))
640
641 res.append (_("""
642 </select> View: <select name="view" onchange="submit()">
643 <option selected></option>
644 """))
645
646 res.append ("\n".join(
647 ["""<option value="%s">%s</option>""" %
648 ("/".join((cherrypy.url(), "view", c)), c)
649 for c in auto_views]))
650
651 res.append ("""
652 </select>
653 <input type="submit" value="go">
654 </form>
655 <form name="entry" method="GET">
656 <input size="50" type="text" name="path" accesskey="p">
657 <input type="submit" value="go"></form>
658 """)
659 res.append (_("""<hr>
660 <p>Evaluating expression "<strong>%(expression)s</strong>" on package %(uri)s returns %(value)s</p>
661 """) % {
662 'expression': tales ,
663 'uri': p.uri,
664 'value': cgi.escape(str(type(objet)))})
665 return "".join(res)
666
667 - def default(self, *args, **query):
668 """Access a specific package.
669
670 URL handling
671 ============
672
673 The URL is first split in components, and some parameters may
674 modify the interpretation of the path.
675
676 The C{path} parameter is used to interactively modifying the
677 path (through a form).
678
679 Else, the C{path} value will be appended to the current path
680 of the URL, and the browser is redirected to this new location.
681
682 The C{view} parameter is a shortcut for rapidly accessing an
683 element view. Its value is in fact the URL displaying the
684 correct view, and the browser is redirected.
685
686 """
687 if not args:
688 return self.index()
689
690 pkgid = args[0]
691
692 try:
693 p = self.controller.packages[pkgid]
694 except KeyError:
695 return self.send_error (501, _("<p>Package <strong>%s</strong> not loaded</p>")
696 % pkgid)
697
698
699 if query.has_key('view'):
700 if query['view'] != '':
701
702
703 return self.send_redirect (query['view'])
704 else:
705
706
707 del(query['view'])
708
709 if query.has_key('path'):
710
711 p="/".join( (cherrypy.url(), query['path']) )
712 if query['path'].find ('..') != -1:
713 p = os.path.normpath (p)
714 del(query['path'])
715 if len(query) == 0:
716 location = p
717 else:
718 location = "%s?%s" % (p,
719 "&".join (["%s=%s" % (k,urllib.quote(query[k]))
720 for k in query.keys()]))
721 return self.send_redirect (location)
722
723 tales = "/".join (args[1:])
724
725 if cherrypy.request.method == 'PUT':
726 return self.handle_put_request(*args, **query)
727 elif cherrypy.request.method == 'POST':
728 return self.handle_post_request(*args, **query)
729 elif cherrypy.request.method != 'GET':
730 return self.send_error(400, 'Unknown method: %s' % cherrypy.request.method)
731
732 try:
733 return self.display_package_element (p , tales, query)
734 except TemplateParseException, e:
735 res=[ self.start_html(_("Error")) ]
736 res.append(_("<h1>Error</h1>"))
737 res.append(_("""<p>There was an error in the template code.</p>
738 <p>Tag name: <strong>%(tagname)s</strong></p>
739 <p>Error message: <em>%(message)s</em></p>""") % {
740 'tagname': cgi.escape(e.location),
741 'message': e.errorDescription })
742 except PathNotFoundException, e:
743 res=[ self.start_html(_("Error")) ]
744 res.append(_("<h1>Error</h1>"))
745 res.append(_("""<p>There was an error in the expression.</p>
746 <pre>%s</pre>""") % cgi.escape(unicode(e.args[0]).encode('utf-8')))
747 except:
748
749 t, v, tr = sys.exc_info()
750 import code
751 res=[ self.start_html(_("Error")) ]
752 res.append(_("<h1>Error</h1>"))
753 res.append(_("""<p>Cannot resolve TALES expression %(expr)s on package %(package)s<p><pre>
754 %(type)s
755 %(value)s
756 %(traceback)s</pre>""") % {
757 'expr': tales,
758 'package': pkgid,
759 'type': unicode(tales),
760 'value': unicode(v),
761 'traceback': "\n".join(code.traceback.format_tb (tr)) })
762
763 return "".join(res)
764 default.exposed=True
765
766
768 """Common methods for all web resources.
769
770 URL syntax
771 ==========
772
773 The virtual tree served by this server has the following entry points :
774
775 - C{/admin} : administrate the webserver
776 - C{/packages} : access to packages
777 - C{/media} : control the player
778 - C{/action} : list and invoke Advene actions
779 - C{/application} : control the application
780 """
782 self.controller=controller
783 self.admin=Admin(controller)
784 self.packages=Packages(controller)
785
787 """Display the server root document.
788 """
789 res=[ self.start_html (_("Advene webserver"), duplicate_title=True, mode='navigation') ]
790 res.append(_("""<p>Welcome on the <a href="http://liris.cnrs.fr/advene/">Advene</a> webserver run by %(userid)s on %(serveraddress)s.</p>""") %
791 {
792 'userid': config.data.userid,
793 'serveraddress': cherrypy.request.base,
794 })
795
796 if len(self.controller.packages) == 0:
797 res.append(_(""" <p>No package is loaded. You can access the <a href="/admin">server administration page</a>.<p>"""))
798 else:
799
800
801
802 if len(self.controller.packages) <= 2:
803 alias='advene'
804 p=self.controller.packages['advene']
805 mes=_("""the <a href="/packages/%s">loaded package's data</a>""") % alias
806 res.append(_(""" <p>You can either access %s or the <a href="/admin">server administration page</a>.<p>""") % mes)
807
808
809 return "".join(res)
810 index.exposed=True
811
813 """Embedded HTTP server for the Advene framework.
814
815 This is an embedded HTTP Server dedicated to serving Advene
816 packages content, and interacting with a media player.
817
818 @ivar controller: the controller
819 @type controller: advene.core.controller.Controller
820
821 @ivar urlbase: the base URL for this server
822 @type urlbase: string
823 @ivar displaymode: the default display-mode
824 @type displaymode: string
825 @ivar authorized_hosts: the list of authorized hosts
826 @type authorized_hosts: dict
827 """
828 - def __init__(self, controller=None, port=1234):
829 """HTTP Server initialization.
830
831 When running embedded, the server should be provided a
832 C{controller} parameter, responsible for handling package
833 loading and player interaction.
834
835 @param controller: the controller
836 @type controller: advene.core.controller.Controller
837 @param port: the port number the server should bind to
838 @type port: int
839 """
840 self.controller=controller
841 self.urlbase = u"http://localhost:%d/" % port
842 self.displaymode = config.data.webserver['displaymode']
843
844 settings = {
845 'global': {
846 'server.socket_port' : port,
847
848
849 'log.screen': False,
850 'log.access_file': config.data.advenefile('webserver.log', 'settings'),
851 'log.error_file': config.data.advenefile('webserver-error.log', 'settings'),
852 'server.reverse_dns': False,
853 'server.thread_pool': 10,
854 'engine.autoreload_on': False,
855
856 'server.environment': "production",
857 },
858
859
860
861 }
862 cherrypy.config.update(settings)
863
864
865
866 self.authorized_hosts = {'127.0.0.1': 'localhost'}
867
868 app_config={
869 '/favicon.ico': {
870 'tools.staticfile.on': True,
871 'tools.staticfile.filename': config.data.advenefile( ( 'pixmaps', 'advene.ico' ) ),
872 },
873 '/data': {
874 'tools.staticdir.on': True,
875 'tools.staticdir.dir': config.data.path['web'],
876 },
877 }
878 cherrypy.tree.mount(Root(controller), config=app_config)
879
880 try:
881
882 cherrypy.server.quickstart()
883 except Exception, e:
884 self.controller.log(_("Cannot start HTTP server: %s") % unicode(e))
885
887 """Start the webserver.
888 """
889 self.controller.queue_action(cherrypy.engine.start, False)
890 return True
891
893 """Stop the webserver.
894 """
895 cherrypy.engine.stop()
896 cherrypy.server.stop()
897
900 self.packages={}
901 self.aliases={}
902 self.server=None
903
904 - def load_main_package(self, fname):
905 p=Package(fname)
906 self.packages['advene']=p
907 self.aliases[p]='advene'
908
911
914
917
920
922 self.packages={}
923 self.aliases={}
924
925 - def build_context(self, here=None, alias=None):
926 c=AdveneContext(here)
927 c.addGlobal("package", here)
928 return c
929
932
933 if __name__ == '__main__':
934 controller=BasicController()
935 controller.load_main_package('file:' + sys.argv[1])
936 controller.server=AdveneWebServer(controller)
937
938 controller.server.start()
939 cherrypy.engine.block()
940