Mercurial > hg > bitsyblog
comparison bitsyblog/bitsyblog.py @ 15:36698624435b
blog page mostly works
author | k0s <k0scist@gmail.com> |
---|---|
date | Sun, 25 Oct 2009 17:25:58 -0400 |
parents | 645aa0f3279f |
children | 2bd6b2b543dc |
comparison
equal
deleted
inserted
replaced
14:645aa0f3279f | 15:36698624435b |
---|---|
29 import utils # local import | 29 import utils # local import |
30 | 30 |
31 from blog import FileBlog | 31 from blog import FileBlog |
32 from cStringIO import StringIO | 32 from cStringIO import StringIO |
33 from docutils.utils import SystemMessage | 33 from docutils.utils import SystemMessage |
34 from genshi.builder import Markup | |
34 from genshi.template import TemplateLoader | 35 from genshi.template import TemplateLoader |
35 from lxml import etree | 36 from lxml import etree |
36 from markup.form import Form | 37 from markup.form import Form |
37 from paste.fileapp import FileApp | 38 from paste.fileapp import FileApp |
38 from pkg_resources import resource_filename | 39 from pkg_resources import resource_filename |
40 from urlparse import urlparse | |
39 from user import FilespaceUsers | 41 from user import FilespaceUsers |
40 from webob import Request, Response, exc | 42 from webob import Request, Response, exc |
41 | 43 |
42 ### exceptions | 44 ### exceptions |
43 | 45 |
53 defaults = { 'date_format': '%H:%M %F', | 55 defaults = { 'date_format': '%H:%M %F', |
54 'file_dir': os.path.dirname(__file__), | 56 'file_dir': os.path.dirname(__file__), |
55 'subject': '[ %(date)s ]:', | 57 'subject': '[ %(date)s ]:', |
56 'n_links': 5, # number of links for navigation | 58 'n_links': 5, # number of links for navigation |
57 'site_name': 'bitsyblog', | 59 'site_name': 'bitsyblog', |
58 'auto_reload': 'False', | 60 'auto_reload': 'True', |
59 'help_file': None, | 61 'help_file': None, |
60 } | 62 } |
63 | |
64 | |
65 cooked_bodies = {} | |
61 | 66 |
62 def __init__(self, **kw): | 67 def __init__(self, **kw): |
63 for key in self.defaults: | 68 for key in self.defaults: |
64 setattr(self, key, kw.get(key, self.defaults[key])) | 69 setattr(self, key, kw.get(key, self.defaults[key])) |
65 self.n_links = int(self.n_links) # could be a string from the .ini | 70 self.n_links = int(self.n_links) # could be a string from the .ini |
384 request.user = self.users[name] | 389 request.user = self.users[name] |
385 else: | 390 else: |
386 name = None | 391 name = None |
387 return name, path | 392 return name, path |
388 | 393 |
389 def user_url(self, request, user, *args, **kw): | 394 ### methods |
390 permalink = kw.get('permalink') | 395 |
391 if permalink: | 396 def link(self, request, path=(), permanant=False): |
392 _args = [ request.host_url, user ] | |
393 else: | |
394 _args = [ user ] | |
395 _args.extend(args) | |
396 return '/'.join([str(arg) for arg in _args]) | |
397 | |
398 ### | |
399 | |
400 def isentry(self, string): # TODO -> blog.py | |
401 return (len(string) == len(''.join(utils.timeformat))) and string.isdigit() | |
402 | |
403 def permalink(self, request, blogentry): | |
404 return self.user_url(request, blogentry.user, blogentry.datestamp(), permalink=True) | |
405 | |
406 def link(self, request, path, permanant=False): | |
407 if isinstance(path, basestring): | 397 if isinstance(path, basestring): |
408 path = [ path ] | 398 path = [ path ] |
409 path = [ i.strip('/') for i in path ] | 399 path = [ i.strip('/') for i in path ] |
410 if permanant: | 400 if permanant: |
411 path = [ request.application_url ] + list(path) | 401 application_url = request.application_url |
412 return '/'.join(path) | |
413 else: | 402 else: |
414 if request.path: | 403 |
415 path = [ request.path ] + list(path) | 404 application_url = urlparse(request.application_url)[2] |
416 return '/%s' % ('/'.join(path)) | 405 path = [ application_url ] + list(path) |
417 | 406 return '/'.join(path) |
418 ### mangled URLs | 407 |
408 def user_url(self, request, user, *args, **kw): | |
409 """link to a user resource""" | |
410 permalink = kw.get('permalink', False) | |
411 path = [ user ] | |
412 path.extend(args) | |
413 return self.link(request, path, permalink) | |
414 | |
415 def permalink(self, request, blogentry): | |
416 """permalink for a blog entry""" | |
417 return self.user_url(request, blogentry.user, blogentry.datestamp(), permalink=True) | |
419 | 418 |
420 def mangledurl(self, request, blogentry): | 419 def mangledurl(self, request, blogentry): |
420 """return a mangled url for obfuscated sharing""" | |
421 return self.user_url(request, blogentry.user, 'x%x' % (int(blogentry.datestamp()) * self.users.secret(blogentry.user)), permalink=True) | 421 return self.user_url(request, blogentry.user, 'x%x' % (int(blogentry.datestamp()) * self.users.secret(blogentry.user)), permalink=True) |
422 | 422 |
423 def unmangleurl(self, url, user): | 423 def unmangleurl(self, url, user): |
424 """unmangle a url for obfuscated sharing""" | |
424 url = url.strip('x') | 425 url = url.strip('x') |
425 try: | 426 try: |
426 value = int(url, 16) | 427 value = int(url, 16) |
427 except ValueError: | 428 except ValueError: |
428 return None | 429 return None |
433 entry = str(value) | 434 entry = str(value) |
434 if self.isentry(entry): | 435 if self.isentry(entry): |
435 return self.blog.entry(user, entry, ['public', 'secret', 'private']) | 436 return self.blog.entry(user, entry, ['public', 'secret', 'private']) |
436 | 437 |
437 ### blog retrival methods | 438 ### blog retrival methods |
439 | |
440 def isentry(self, string): # TODO -> blog.py | |
441 """returns whether the string is a blog entry""" | |
442 return (len(string) == len(''.join(utils.timeformat))) and string.isdigit() | |
438 | 443 |
439 def number_of_posts(self, request, user=None): | 444 def number_of_posts(self, request, user=None): |
440 """return the number of blog posts to display""" | 445 """return the number of blog posts to display""" |
441 # determine number of posts to display (None -> all) | 446 # determine number of posts to display (None -> all) |
442 n_posts = request.GET.get('posts', None) | 447 n_posts = request.GET.get('posts', None) |
449 n_posts = None | 454 n_posts = None |
450 return n_posts | 455 return n_posts |
451 | 456 |
452 def number_of_links(self, request, user=None): | 457 def number_of_links(self, request, user=None): |
453 """return the number of links to display in the navigation""" | 458 """return the number of links to display in the navigation""" |
454 # determine number of navigation links to display | |
455 n_links = request.GET.get('n') | 459 n_links = request.GET.get('n') |
456 if n_links == 'all': | 460 if n_links == 'all': |
457 return -1 | 461 return -1 |
458 try: | 462 try: |
459 n_links = int(n_links) | 463 n_links = int(n_links) |
523 title = ' - '.join(_title) | 527 title = ' - '.join(_title) |
524 head_markup = () | 528 head_markup = () |
525 if feedtitle: | 529 if feedtitle: |
526 head_markup = ( '<link rel="alternate" type="application/atom+xml" title="%s" href="atom" />' % feedtitle, | 530 head_markup = ( '<link rel="alternate" type="application/atom+xml" title="%s" href="atom" />' % feedtitle, |
527 '<link rel="alternate" type="application/rss+xml" title="%s" href="rss" />' % feedtitle,) | 531 '<link rel="alternate" type="application/rss+xml" title="%s" href="rss" />' % feedtitle,) |
532 | |
528 return markup.wrap(self.site_nav(request)+body, title, stylesheets, head_markup=head_markup) | 533 return markup.wrap(self.site_nav(request)+body, title, stylesheets, head_markup=head_markup) |
529 | 534 |
530 def site_nav(self, request): | 535 def site_nav(self, request): |
531 """returns HTML for site navigation""" | 536 """returns HTML for site navigation""" |
532 links = [('/',), ] | 537 |
538 links = [(self.link(request), '/'), ] | |
533 user = self.authenticated(request) | 539 user = self.authenticated(request) |
534 if user: | 540 if user: |
535 links.extend([('/%s' % user, user), | 541 links.extend([(self.user_url(request, user), user), |
536 ('/%s/post' % user, 'post'), | 542 (self.user_url(request, user, 'post'), user), |
537 ('/%s/preferences' % user, 'preferences'), | 543 (self.user_url(request, user, 'preferences'), 'preferences'), |
538 ('/logout', 'logout')]) | 544 (self.link(request, 'logout'), 'logout')]) |
539 else: | 545 else: |
540 links.extend([('/login', 'login'), ('/join', 'join')]) | 546 links.extend([(self.link(request, 'login'), 'login'), |
547 (self.link(request, 'join'), 'join')]) | |
541 | 548 |
542 if hasattr(self, 'help'): | 549 if hasattr(self, 'help'): |
543 links.append(('/help', 'help')) | 550 links.append((self.link(request, 'help'), 'help')) |
544 | 551 |
545 links = [ markup.link(*i) for i in links ] | 552 request.environ['data']['links'] = links |
546 return markup.listify(links, ordered=False, **{ 'class': 'site-nav'}) | 553 |
547 | 554 |
548 def index(self, n_links): | 555 def index(self, n_links): |
549 retval = StringIO() | 556 retval = StringIO() |
550 print >> retval, '<h1><img src="bitsyblog.png" alt="bitsyblog"/></h1>' | 557 print >> retval, '<h1><img src="bitsyblog.png" alt="bitsyblog"/></h1>' |
551 | 558 |
595 print >> retval, markup.listify(entries) | 602 print >> retval, markup.listify(entries) |
596 print >> retval, more | 603 print >> retval, more |
597 print >> retval, '</div>' | 604 print >> retval, '</div>' |
598 return retval.getvalue() | 605 return retval.getvalue() |
599 | 606 |
600 def blog_entry(self, request, user, entry): | 607 # def blog_entry(self, request, user, entry): |
601 """given the content string, return a marked-up blog entry""" | 608 # """given the content string, return a marked-up blog entry""" |
602 # XXX no need to pass user | 609 # # XXX no need to pass user |
603 | 610 |
604 # user preferences | 611 # # user preferences |
605 prefs = request.user.settings | 612 # prefs = request.user.settings |
606 format = prefs.get('Date format', self.date_format) | 613 # format = prefs.get('Date format', self.date_format) |
607 subject = prefs.get('Subject', self.subject) | 614 # subject = prefs.get('Subject', self.subject) |
608 | 615 |
609 role = self.role(user, request) | 616 # role = self.role(user, request) |
610 | 617 |
611 subject = subject % { 'date' : entry.date.strftime(format) } | 618 # subject = subject % { 'date' : entry.date.strftime(format) } |
612 subject = cgi.escape(subject) | 619 # subject = cgi.escape(subject) |
613 html = StringIO() | 620 # html = StringIO() |
614 blog_id = entry.datestamp() | 621 # blog_id = entry.datestamp() |
615 print >> html, '<div id="%s" class="blog-entry">' % blog_id | 622 # print >> html, '<div id="%s" class="blog-entry">' % blog_id |
616 print >> html, '<a name="%s" />' % blog_id | 623 # print >> html, '<a name="%s" />' % blog_id |
617 print >> html, '<div class="subject">' | 624 # print >> html, '<div class="subject">' |
618 print >> html, '<a href="/%s">%s</a>' % (self.user_url(request, user, blog_id), subject) | 625 # print >> html, '<a href="/%s">%s</a>' % (self.user_url(request, user, blog_id), subject) |
619 if (entry.privacy == 'secret') and (role == 'friend'): | 626 # if (entry.privacy == 'secret') and (role == 'friend'): |
620 print >> html, '<em>secret</em>' | 627 # print >> html, '<em>secret</em>' |
621 print >> html, '</div>' | 628 # print >> html, '</div>' |
622 print >> html, self.cooker(entry.body) | 629 # print >> html, self.cooker(entry.body) |
623 | 630 |
624 if role == 'author': | 631 # if role == 'author': |
625 print >> html, '<div><form action="/%s" method="post">' % self.user_url(request, entry.user, blog_id) | 632 # print >> html, '<div><form action="/%s" method="post">' % self.user_url(request, entry.user, blog_id) |
626 print >> html, self.privacy_settings(entry.privacy) | 633 # print >> html, self.privacy_settings(entry.privacy) |
627 print >> html, '<input type="submit" name="submit" value="Change Privacy" />' | 634 # print >> html, '<input type="submit" name="submit" value="Change Privacy" />' |
628 print >> html, '</form></div>' | 635 # print >> html, '</form></div>' |
629 if entry.privacy != 'public': | 636 # if entry.privacy != 'public': |
630 title = "You can give this URL so people may see this %s post without logging in" % entry.privacy | 637 # title = "You can give this URL so people may see this %s post without logging in" % entry.privacy |
631 print >> html, '<div>' | 638 # print >> html, '<div>' |
632 print >> html, '<span title="%s">Mangled URL:</span>' % title | 639 # print >> html, '<span title="%s">Mangled URL:</span>' % title |
633 print >> html, markup.link(self.mangledurl(request, entry)) | 640 # print >> html, markup.link(self.mangledurl(request, entry)) |
634 print >> html, '</div>' | 641 # print >> html, '</div>' |
635 | 642 |
636 print >> html, '</div>' | 643 # print >> html, '</div>' |
637 return html.getvalue() | 644 # return html.getvalue() |
638 | 645 |
639 def write_blog(self, user, blog, path, n_links, request): | 646 def write_blog(self, user, blog, path, n_links, request): |
640 """return the user's blog in HTML""" | 647 """return the user's blog in HTML""" |
641 # XXX no need to pass path or user! | 648 |
642 retval = StringIO() | 649 # XXX probably should go elsewhere |
643 print >> retval, self.navigation(user, blog, path, n_links, 0) | |
644 for entry in blog: | 650 for entry in blog: |
645 print >> retval, self.blog_entry(request, user, entry) | 651 if (user, entry.datestamp()) not in self.cooked_bodies: |
646 feedtitle=None | 652 self.cooked_bodies[(user, entry.datestamp())] = self.cooker(entry.body) |
647 if request.path_info.strip('/') == user: | 653 entry.cooked_body = Markup(self.cooked_bodies[(user, entry.datestamp())]) |
648 feedtitle = "%s's blog" % user | 654 |
649 title = None | 655 # site nav |
650 if len(blog) == 1: | 656 # XXX def site_nav() puts directly in request.environ['data'] |
651 format = request.user.settings.get('Date format', self.date_format) | 657 # should return instead |
652 title = blog[0].date.strftime(format) | 658 self.site_nav(request) |
653 return self.render(request, retval.getvalue(), title=title, feedtitle=feedtitle) | 659 |
660 # user data -> should be moved up the chain | |
661 data = request.environ['data'] | |
662 data['user'] = user | |
663 data['role'] = self.role(user, request) | |
664 data['stylesheets'] = () # TODO | |
665 data['subject'] = request.user.settings.get('Subject', self.subject) | |
666 data['date_format'] = request.user.settings.get('Date format', self.date_format) | |
667 data['user_url'] = self.user_url | |
668 data['mangledurl'] = self.mangledurl | |
669 | |
670 # blog data | |
671 data['blog'] = blog | |
672 data['n_links'] = n_links | |
673 | |
674 # render the template | |
675 template = self.loader.load('blog.html') | |
676 return template.generate(**data).render() | |
677 | |
678 # # XXX no need to pass path or user! | |
679 # retval = StringIO() | |
680 # print >> retval, self.navigation(user, blog, path, n_links, 0) | |
681 # for entry in blog: | |
682 # print >> retval, self.blog_entry(request, user, entry) | |
683 # feedtitle=None | |
684 # if request.path_info.strip('/') == user: | |
685 # feedtitle = "%s's blog" % user | |
686 # title = None | |
687 # if len(blog) == 1: | |
688 # format = request.user.settings.get('Date format', self.date_format) | |
689 # title = blog[0].date.strftime(format) | |
690 # return self.render(request, retval.getvalue(), title=title, feedtitle=feedtitle) | |
654 | 691 |
655 def restructuredText(self, string): | 692 def restructuredText(self, string): |
656 origstring = string | 693 origstring = string |
657 settings = { 'report_level': 5 } | 694 settings = { 'report_level': 5 } |
658 string = string.strip() | 695 string = string.strip() |
779 print >> retval, '<input type="submit" name="submit" value="Post" />' | 816 print >> retval, '<input type="submit" name="submit" value="Post" />' |
780 print >> retval, '</form>' | 817 print >> retval, '</form>' |
781 return self.render(request, retval.getvalue()) | 818 return self.render(request, retval.getvalue()) |
782 | 819 |
783 def preferences_form(self, request, user): | 820 def preferences_form(self, request, user): |
784 prefs = self.request.user.settings | 821 prefs = request.user.settings |
785 form = Form() | 822 form = Form() |
786 | 823 |
787 # date format | 824 # date format |
788 format = prefs.get('Date format', self.date_format) | 825 format = prefs.get('Date format', self.date_format) |
789 value = datetime.datetime.now().strftime(format) | 826 value = datetime.datetime.now().strftime(format) |
861 path = [] | 898 path = [] |
862 request.user = self.users[self.user] | 899 request.user = self.users[self.user] |
863 return self.user, path | 900 return self.user, path |
864 | 901 |
865 def user_url(self, request, user, *args, **kw): | 902 def user_url(self, request, user, *args, **kw): |
866 permalink = kw.get('permalink') | 903 permalink = kw.get('permalink', False) |
867 if permalink: | 904 return self.link(request, args, permalink) |
868 _args = [ request.host_url ] | |
869 else: | |
870 _args = [ ] | |
871 _args.extend(args) | |
872 return '/'.join([str(arg) for arg in _args]) | |
873 | 905 |
874 def passwords(self): | 906 def passwords(self): |
875 return { self.user: self.users.password(self.user) } | 907 return { self.user: self.users.password(self.user) } |
876 | 908 |
877 def site_nav(self, request): | 909 def site_nav(self, request): |
878 """returns HTML for site navigation""" | 910 """returns HTML for site navigation""" |
879 links = [('/', self.user), ] | 911 links = [(self.user_url(request, self.user), self.user), ] |
880 user = self.authenticated(request) | 912 user = self.authenticated(request) |
881 if user: | 913 if user: |
882 links.extend([ | 914 links.extend([(self.user_url(request, user, 'post'), 'post'), |
883 ('/post', 'post'), | 915 (self.user_url(request, user, 'preferences'), 'preferences'), |
884 ('/preferences', 'preferences'), | 916 (self.link(request, 'logout'), 'logout')]) |
885 ('/logout', 'logout')]) | |
886 else: | 917 else: |
887 links.append(('/login', 'login')) | 918 links.append((self.link(request, 'login'), 'login')) |
888 | 919 |
889 if hasattr(self, 'help'): | 920 if hasattr(self, 'help'): |
890 links.append(('/help', 'help')) | 921 links.append((self.link(request, 'help'), 'about')) |
891 | 922 |
892 links = [ markup.link(*i) for i in links ] | 923 |
893 return markup.listify(links, ordered=False, **{ 'class': 'site-nav'}) | 924 request.environ['data']['links'] = links |
925 |