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