changeset 15:36698624435b

blog page mostly works
author k0s <k0scist@gmail.com>
date Sun, 25 Oct 2009 17:25:58 -0400
parents 645aa0f3279f
children 2bd6b2b543dc
files bitsyblog/bitsyblog.py bitsyblog/templates/blog.html bitsyblog/templates/navigation.html
diffstat 3 files changed, 166 insertions(+), 111 deletions(-) [+]
line wrap: on
line diff
--- a/bitsyblog/bitsyblog.py	Wed Oct 07 17:26:13 2009 -0400
+++ b/bitsyblog/bitsyblog.py	Sun Oct 25 17:25:58 2009 -0400
@@ -31,11 +31,13 @@
 from blog import FileBlog
 from cStringIO import StringIO
 from docutils.utils import SystemMessage
+from genshi.builder import Markup
 from genshi.template import TemplateLoader
 from lxml import etree
 from markup.form import Form
 from paste.fileapp import FileApp
 from pkg_resources import resource_filename
+from urlparse import urlparse
 from user import FilespaceUsers
 from webob import Request, Response, exc
 
@@ -55,10 +57,13 @@
                  'subject': '[ %(date)s ]:',
                  'n_links': 5, # number of links for navigation
                  'site_name': 'bitsyblog',
-                 'auto_reload': 'False',
+                 'auto_reload': 'True',
                  'help_file': None,
                  }
 
+
+    cooked_bodies = {}
+
     def __init__(self, **kw):
         for key in self.defaults:
             setattr(self, key, kw.get(key, self.defaults[key]))
@@ -386,41 +391,37 @@
             name = None
         return name, path
 
-    def user_url(self, request, user, *args, **kw):
-        permalink = kw.get('permalink')
-        if permalink:
-            _args = [ request.host_url, user ]
-        else:
-            _args = [ user ]
-        _args.extend(args)
-        return '/'.join([str(arg) for arg in _args])
+    ### methods 
 
-    ### 
-
-    def isentry(self, string): # TODO -> blog.py
-        return (len(string) == len(''.join(utils.timeformat))) and string.isdigit()
-
-    def permalink(self, request, blogentry):
-        return self.user_url(request, blogentry.user, blogentry.datestamp(), permalink=True)
-
-    def link(self, request, path, permanant=False):
+    def link(self, request, path=(), permanant=False):
         if isinstance(path, basestring):
             path = [ path ]
         path = [ i.strip('/') for i in path ]
         if permanant:
-            path = [ request.application_url ] + list(path)
-            return '/'.join(path)
+            application_url = request.application_url
         else:
-            if request.path:
-                path = [ request.path ] + list(path)
-            return '/%s' % ('/'.join(path))
+            
+            application_url = urlparse(request.application_url)[2]
+        path = [ application_url ] + list(path)
+        return '/'.join(path)
 
-    ### mangled URLs
+    def user_url(self, request, user, *args, **kw):
+        """link to a user resource"""
+        permalink = kw.get('permalink', False)
+        path = [ user ]
+        path.extend(args)
+        return self.link(request, path, permalink)
+
+    def permalink(self, request, blogentry):
+        """permalink for a blog entry"""
+        return self.user_url(request, blogentry.user, blogentry.datestamp(), permalink=True)
 
     def mangledurl(self, request, blogentry):
+        """return a mangled url for obfuscated sharing"""
         return self.user_url(request, blogentry.user, 'x%x' % (int(blogentry.datestamp()) * self.users.secret(blogentry.user)), permalink=True)
 
     def unmangleurl(self, url, user):
+        """unmangle a url for obfuscated sharing"""
         url = url.strip('x')
         try:
             value = int(url, 16)
@@ -436,6 +437,10 @@
         
     ### blog retrival methods
 
+    def isentry(self, string): # TODO -> blog.py
+        """returns whether the string is a blog entry"""
+        return (len(string) == len(''.join(utils.timeformat))) and string.isdigit()
+
     def number_of_posts(self, request, user=None):
         """return the number of blog posts to display"""
         # determine number of posts to display (None -> all)
@@ -451,7 +456,6 @@
 
     def number_of_links(self, request, user=None):
         """return the number of links to display in the navigation"""
-        # determine number of navigation links to display
         n_links = request.GET.get('n')
         if n_links == 'all':
             return -1
@@ -525,25 +529,28 @@
         if feedtitle:
             head_markup = ( '<link rel="alternate" type="application/atom+xml" title="%s" href="atom" />' % feedtitle, 
                             '<link rel="alternate" type="application/rss+xml" title="%s" href="rss" />' % feedtitle,)
+        
         return markup.wrap(self.site_nav(request)+body, title, stylesheets, head_markup=head_markup)
 
     def site_nav(self, request):
         """returns HTML for site navigation"""
-        links = [('/',), ]
+
+        links = [(self.link(request), '/'), ]
         user = self.authenticated(request)
         if user:
-            links.extend([('/%s' % user, user),
-                          ('/%s/post' % user, 'post'),
-                          ('/%s/preferences' % user, 'preferences'),
-                          ('/logout', 'logout')])
+            links.extend([(self.user_url(request, user), user),
+                          (self.user_url(request, user, 'post'), user),
+                          (self.user_url(request, user, 'preferences'), 'preferences'),
+                          (self.link(request, 'logout'), 'logout')])
         else:
-            links.extend([('/login', 'login'), ('/join', 'join')])
+            links.extend([(self.link(request, 'login'), 'login'), 
+                          (self.link(request, 'join'), 'join')])
 
         if hasattr(self, 'help'):
-            links.append(('/help', 'help'))
+            links.append((self.link(request, 'help'), 'help'))
 
-        links = [ markup.link(*i) for i in links ]
-        return markup.listify(links, ordered=False, **{ 'class': 'site-nav'})
+        request.environ['data']['links'] = links
+
 
     def index(self, n_links):
         retval = StringIO()
@@ -597,60 +604,90 @@
         print >> retval, '</div>'
         return retval.getvalue()
 
-    def blog_entry(self, request, user, entry):
-        """given the content string, return a marked-up blog entry"""
-        # XXX no need to pass user
+#     def blog_entry(self, request, user, entry):
+#         """given the content string, return a marked-up blog entry"""
+#         # XXX no need to pass user
 
-        # user preferences
-        prefs = request.user.settings
-        format = prefs.get('Date format', self.date_format)
-        subject = prefs.get('Subject', self.subject)
+#         # user preferences
+#         prefs = request.user.settings
+#         format = prefs.get('Date format', self.date_format)
+#         subject = prefs.get('Subject', self.subject)
 
-        role = self.role(user, request)
+#         role = self.role(user, request)
         
-        subject = subject % { 'date' : entry.date.strftime(format) }
-        subject = cgi.escape(subject)
-        html = StringIO()
-        blog_id = entry.datestamp()
-        print >> html, '<div id="%s" class="blog-entry">' % blog_id
-        print >> html, '<a name="%s" />' % blog_id
-        print >> html, '<div class="subject">'
-        print >> html, '<a href="/%s">%s</a>' % (self.user_url(request, user, blog_id), subject)
-        if (entry.privacy == 'secret') and (role == 'friend'):
-            print >> html, '<em>secret</em>'
-        print >> html, '</div>'
-        print >> html, self.cooker(entry.body)
+#         subject = subject % { 'date' : entry.date.strftime(format) }
+#         subject = cgi.escape(subject)
+#         html = StringIO()
+#         blog_id = entry.datestamp()
+#         print >> html, '<div id="%s" class="blog-entry">' % blog_id
+#         print >> html, '<a name="%s" />' % blog_id
+#         print >> html, '<div class="subject">'
+#         print >> html, '<a href="/%s">%s</a>' % (self.user_url(request, user, blog_id), subject)
+#         if (entry.privacy == 'secret') and (role == 'friend'):
+#             print >> html, '<em>secret</em>'
+#         print >> html, '</div>'
+#         print >> html, self.cooker(entry.body)
 
-        if role == 'author':
-            print >> html, '<div><form action="/%s" method="post">' % self.user_url(request, entry.user, blog_id)
-            print >> html, self.privacy_settings(entry.privacy)
-            print >> html, '<input type="submit" name="submit" value="Change Privacy" />'
-            print >> html, '</form></div>'
-            if entry.privacy != 'public':
-                title = "You can give this URL so people may see this %s post without logging in" % entry.privacy
-                print >> html, '<div>'
-                print >> html, '<span title="%s">Mangled URL:</span>' % title
-                print >> html, markup.link(self.mangledurl(request, entry))
-                print >> html, '</div>'
+#         if role == 'author':
+#             print >> html, '<div><form action="/%s" method="post">' % self.user_url(request, entry.user, blog_id)
+#             print >> html, self.privacy_settings(entry.privacy)
+#             print >> html, '<input type="submit" name="submit" value="Change Privacy" />'
+#             print >> html, '</form></div>'
+#             if entry.privacy != 'public':
+#                 title = "You can give this URL so people may see this %s post without logging in" % entry.privacy
+#                 print >> html, '<div>'
+#                 print >> html, '<span title="%s">Mangled URL:</span>' % title
+#                 print >> html, markup.link(self.mangledurl(request, entry))
+#                 print >> html, '</div>'
         
-        print >> html, '</div>'
-        return html.getvalue()
+#         print >> html, '</div>'
+#         return html.getvalue()
         
     def write_blog(self, user, blog, path, n_links, request):
         """return the user's blog in HTML"""
-        # XXX no need to pass path or user!
-        retval = StringIO()
-        print >> retval,  self.navigation(user, blog, path, n_links, 0)
+
+        # XXX probably should go elsewhere
         for entry in blog:
-            print >> retval, self.blog_entry(request, user, entry)
-        feedtitle=None
-        if request.path_info.strip('/') == user:
-            feedtitle = "%s's blog" % user
-        title = None
-        if len(blog) == 1:
-            format = request.user.settings.get('Date format', self.date_format)
-            title = blog[0].date.strftime(format)
-        return self.render(request, retval.getvalue(), title=title, feedtitle=feedtitle)
+            if (user, entry.datestamp()) not in self.cooked_bodies:
+                self.cooked_bodies[(user, entry.datestamp())] = self.cooker(entry.body)
+            entry.cooked_body = Markup(self.cooked_bodies[(user, entry.datestamp())])
+
+        # site nav 
+        # XXX def site_nav() puts directly in request.environ['data']
+        # should return instead
+        self.site_nav(request)
+
+        # user data -> should be moved up the chain
+        data = request.environ['data']
+        data['user'] = user
+        data['role'] = self.role(user, request)
+        data['stylesheets'] = () # TODO
+        data['subject'] = request.user.settings.get('Subject', self.subject)
+        data['date_format'] = request.user.settings.get('Date format', self.date_format)
+        data['user_url'] = self.user_url
+        data['mangledurl'] = self.mangledurl
+
+        # blog data
+        data['blog'] = blog
+        data['n_links'] = n_links
+
+        # render the template
+        template = self.loader.load('blog.html')
+        return template.generate(**data).render()
+
+#         # XXX no need to pass path or user!
+#         retval = StringIO()
+#         print >> retval,  self.navigation(user, blog, path, n_links, 0)
+#         for entry in blog:
+#             print >> retval, self.blog_entry(request, user, entry)
+#         feedtitle=None
+#         if request.path_info.strip('/') == user:
+#             feedtitle = "%s's blog" % user
+#         title = None
+#         if len(blog) == 1:
+#             format = request.user.settings.get('Date format', self.date_format)
+#             title = blog[0].date.strftime(format)
+#         return self.render(request, retval.getvalue(), title=title, feedtitle=feedtitle)
                            
     def restructuredText(self, string):
         origstring = string
@@ -781,7 +818,7 @@
         return self.render(request, retval.getvalue())
 
     def preferences_form(self, request, user):
-        prefs = self.request.user.settings
+        prefs = request.user.settings
         form = Form()
 
         # date format
@@ -863,31 +900,26 @@
         return self.user, path
 
     def user_url(self, request, user, *args, **kw):
-        permalink = kw.get('permalink')
-        if permalink:
-            _args = [ request.host_url ]
-        else:
-            _args = [ ]
-        _args.extend(args)
-        return '/'.join([str(arg) for arg in _args])
+        permalink = kw.get('permalink', False)
+        return self.link(request, args, permalink)
 
     def passwords(self):
         return { self.user: self.users.password(self.user) }
 
     def site_nav(self, request):
         """returns HTML for site navigation"""
-        links = [('/', self.user), ]
+        links = [(self.user_url(request, self.user), self.user), ]
         user = self.authenticated(request)
         if user:
-            links.extend([
-                          ('/post', 'post'),
-                          ('/preferences', 'preferences'),
-                          ('/logout', 'logout')])
+            links.extend([(self.user_url(request, user, 'post'), 'post'),
+                          (self.user_url(request, user, 'preferences'), 'preferences'),
+                          (self.link(request, 'logout'), 'logout')])
         else:
-            links.append(('/login', 'login'))
+            links.append((self.link(request, 'login'), 'login'))
 
         if hasattr(self, 'help'):
-            links.append(('/help', 'help'))
+            links.append((self.link(request, 'help'), 'about'))
+
 
-        links = [ markup.link(*i) for i in links ]
-        return markup.listify(links, ordered=False, **{ 'class': 'site-nav'})
+        request.environ['data']['links'] = links
+
--- a/bitsyblog/templates/blog.html	Wed Oct 07 17:26:13 2009 -0400
+++ b/bitsyblog/templates/blog.html	Sun Oct 25 17:25:58 2009 -0400
@@ -8,8 +8,8 @@
   <head>
     <title>${user} - ${site_name}</title>
 
-    <py:for each="index, sheet in stylesheets">
-      <link href="${link(request, ('css', sheet)"
+    <py:for each="index, sheet in enumerate(stylesheets)">
+      <link href="${link(request, ('css', sheet))}"
             type="text/css" 
             rel="${index and 'alternate stylesheet' or 'stylesheet'}"
             title="sheet.rsplit('.', 1)[0]"/>
@@ -19,13 +19,16 @@
 
   <body>
     
-    <!-- nav links -->
-    <xi:include href="navigation.html">
+    <xi:include href="navigation.html" />
       
-      <div py:if="n_links == 0 or not len(blog)" class="navigation">
+      <div class="navigation"
+           py:if="n_links == 0 or not len(blog)"
+           py:with="_n_links = (n_links != -1) and n_links or len(blog)">
         <ul>
-          
+          <li py:for="index in range(_n_links)">
+          </li>
         </ul>
+        <a py:if="_n_links != len(blog)" href="TODO?n=all">more</a>
       </div>
 
       <!-- blog entries -->
@@ -34,28 +37,41 @@
         <a name="${entry.datestamp()}" />
         
         <div class="subject">
-          <a href="${'foo'}">${subject}</a>
-          <em py:if="entry.privacy == secret and role == 'friend'">
+          <a href="${user_url(request, request.user.name, entry.datestamp())}">${subject % dict(date=entry.date.strftime(date_format))}</a>
+          <em py:if="entry.privacy == 'secret' and role == 'friend'">
             secret
           </em>
         </div>
         
-        ${body}
+        ${entry.cooked_body}
         
         <div py:if="role == 'author'">
 
           <!-- privacy settings -->
           <form action="TODO" method="post">
-            <span title="viewable to everyone">public<input checked type="radio" name="privacy" value="public"/></span> 
-            <span title="viewable only to your friends">secret<input type="radio" name="privacy" value="secret"/></span> 
-            <span title="viewable only to you">private<input type="radio" name="privacy" value="private"/></span>
+            <span title="viewable to everyone">
+              public
+              <input type="radio" name="privacy" value="public" 
+                     py:attrs="{'checked': entry.privacy=='public' and True or None}"/>
+            </span> 
+            <span title="viewable only to your friends">
+              secret
+              <input type="radio" name="privacy" value="secret" 
+                     py:attrs="{'checked': entry.privacy=='secret' and True or None}"/>
+            </span> 
+            <span title="viewable only to you">
+              private
+              <input type="radio" name="privacy" value="private"
+                     py:attrs="{'checked': entry.privacy=='private' and True or None}"/>
+            </span>
             <input type="submit" name="submit" value="Change Privacy" />
           </form>
 
           <!-- mangled URL -->
-          <div>
+          <div py:if="entry.privacy != 'public'"
+               py:with="mangled_url = mangledurl(request, entry)">
             <span title="${'You can give this URL so people may see this %s post without logging in' % entry.privacy}">Mangled URL:</span>
-            <a href="TODO">${'TODO'}</a>
+            <a href="${mangled_url}">${mangled_url}</a>
           </div>
         </div>
 
--- a/bitsyblog/templates/navigation.html	Wed Oct 07 17:26:13 2009 -0400
+++ b/bitsyblog/templates/navigation.html	Sun Oct 25 17:25:58 2009 -0400
@@ -5,7 +5,14 @@
       xmlns:py="http://genshi.edgewall.org/"
       xmlns:xi="http://www.w3.org/2001/XInclude"
       py:strip="True">
+
   <!-- nav bar -->
-
+  <div class="site-nav">
+    <ul>
+      <li py:for="link, name in links">
+        <a href="${link}">${name}</a>
+      </li>
+    </ul>
+  </div>
     
 </html>