[open-ils-commits] r168 - in servres/trunk/conifer: . static syrup templates templates/components templates/feeds (gfawcett)

svn at svn.open-ils.org svn at svn.open-ils.org
Thu Mar 12 21:57:32 EDT 2009


Author: gfawcett
Date: 2009-03-12 21:57:31 -0400 (Thu, 12 Mar 2009)
New Revision: 168

Added:
   servres/trunk/conifer/templates/feeds/
   servres/trunk/conifer/templates/feeds/course_atom.xml
   servres/trunk/conifer/templates/feeds/course_feed_index.xhtml
Modified:
   servres/trunk/conifer/genshi_support.py
   servres/trunk/conifer/static/main.css
   servres/trunk/conifer/syrup/models.py
   servres/trunk/conifer/syrup/urls.py
   servres/trunk/conifer/syrup/views.py
   servres/trunk/conifer/templates/components/course.xhtml
Log:
Feeds! Atom feeds for course-site items.

Some notes:

* all feeds are Atom; comments on my Atom details are welcome.

* several different feeds per course site. E.g., just top-level items;
  recently-changed items; a walk of all items in the site directory
  tree; many others possible.

* by design, the feeds themselves are anonymous-access. I don't see a
  real security risk here, but if exposing titles and modification
  dates violates some policy, we can change it.

* all links in the feeds refer back to the Reserves system, so they
  can be authenticated if necessary. This is also true for "URL items"
  -- the Atom link is back to the canonical item-URL in Reserves,
  which redirects to the target URL (if you're allowed to know it).

* Django has its own feed system. I tried it, and then chose not to
  use it. Genshi does a fine job, and IMHO Django makes it harder to
  offer multiple feed-variants on individual items like Courses. It
  looks good for simpler feed-needs though, and has the benefit of
  supporting both Atom and RSS. (Not that we couldn't do that with
  Genshi too.)

There's room for more feed types: "My Courses", "Things in My
Courses", "canned search", etc. Ideas are most welcome.


Modified: servres/trunk/conifer/genshi_support.py
===================================================================
--- servres/trunk/conifer/genshi_support.py	2009-03-12 22:44:40 UTC (rev 167)
+++ servres/trunk/conifer/genshi_support.py	2009-03-13 01:57:31 UTC (rev 168)
@@ -43,8 +43,8 @@
 #------------------------------------------------------------
 # main API
 
-def render(tname, _django_type=HttpResponse, **kwargs):
+def render(tname, _django_type=HttpResponse, _serialization='xhtml', **kwargs):
     request = get_request()
     _inject_django_things_into_namespace(request, kwargs)
-    return _django_type(template(tname).generate(**kwargs).render('xhtml'))
+    return _django_type(template(tname).generate(**kwargs).render(_serialization))
 

Modified: servres/trunk/conifer/static/main.css
===================================================================
--- servres/trunk/conifer/static/main.css	2009-03-12 22:44:40 UTC (rev 167)
+++ servres/trunk/conifer/static/main.css	2009-03-13 01:57:31 UTC (rev 168)
@@ -199,6 +199,8 @@
 
 #edit_course_link { margin: 8 0 8 0; font-size: 95%; }
 
+#feeds_panel { float: right; font-size: 95%; margin: 8 0; }
+
 .breadcrumbs { margin: 8 8 8 0; }
 
 .errorlist { float: right; }

Modified: servres/trunk/conifer/syrup/models.py
===================================================================
--- servres/trunk/conifer/syrup/models.py	2009-03-12 22:44:40 UTC (rev 167)
+++ servres/trunk/conifer/syrup/models.py	2009-03-13 01:57:31 UTC (rev 168)
@@ -422,7 +422,7 @@
 
 
     date_created = m.DateTimeField(auto_now_add=True)
-    last_modified = m.DateTimeField()
+    last_modified = m.DateTimeField(auto_now=True)
 
     def title_hl(self, terms):
         hl_title = self.title
@@ -458,12 +458,12 @@
 
         return self.item_type in ('ELEC', 'URL')
 
-    def item_url(self, suffix=''):
+    def item_url(self, suffix='', force_local_url=False):
         if self.item_type == 'ELEC' and suffix == '':
             return '/syrup/course/%d/item/%d/dl/%s' % (
                 self.course_id, self.id, 
                 self.fileobj.name.split('/')[-1])
-        if self.item_type == 'URL' and suffix == '':
+        if self.item_type == 'URL' and suffix == '' and not force_local_url:
             return self.url
         else:
             return '/syrup/course/%d/item/%d/%s' % (

Modified: servres/trunk/conifer/syrup/urls.py
===================================================================
--- servres/trunk/conifer/syrup/urls.py	2009-03-12 22:44:40 UTC (rev 167)
+++ servres/trunk/conifer/syrup/urls.py	2009-03-13 01:57:31 UTC (rev 168)
@@ -26,6 +26,7 @@
     (r'^course/(?P<course_id>\d+)/edit/$', 'edit_course'),
     (r'^course/(?P<course_id>\d+)/edit/delete/$', 'delete_course'),
     (r'^course/(?P<course_id>\d+)/edit/permission/$', 'edit_course_permissions'),
+    (r'^course/(?P<course_id>\d+)/feeds/(?P<feed_type>.*)$', 'course_feeds'),
     (ITEM_PREFIX + r'$', 'item_detail'),
     (ITEM_PREFIX + r'dl/(?P<filename>.*)$', 'item_download'),
     (ITEM_PREFIX + r'meta$', 'item_metadata'),
@@ -35,7 +36,6 @@
     (r'^admin/terms/' + GENERIC_REGEX, 'admin_terms'),
     (r'^admin/depts/' + GENERIC_REGEX, 'admin_depts'),
     (r'^admin/news/' + GENERIC_REGEX, 'admin_news'),
-
 #     (r'^admin/terms/(?P<term_id>\d+)/$', 'admin_term_edit'),
 #     (r'^admin/terms/(?P<term_id>\d+)/delete$', 'admin_term_delete'),
 #     (r'^admin/terms/$', 'admin_term'),

Modified: servres/trunk/conifer/syrup/views.py
===================================================================
--- servres/trunk/conifer/syrup/views.py	2009-03-12 22:44:40 UTC (rev 167)
+++ servres/trunk/conifer/syrup/views.py	2009-03-13 01:57:31 UTC (rev 168)
@@ -156,6 +156,11 @@
             return _access_denied(_('Only administrators are allowed here.'))
     return hdlr
 
+#decorator
+def public(handler):
+    # A no-op! Just here to be used to explicitly decorate methods
+    # that are supposed to be public.
+    return handler
 #-----------------------------------------------------------------------------
 
 def welcome(request):
@@ -838,3 +843,46 @@
     clean_body = strip_and_nonblank('body')
 
 admin_news = generic_handler(NewsForm, decorator=admin_only)
+
+
+
+#-----------------------------------------------------------------------------
+# Course feeds
+
+ at public                         # and proud of it!
+def course_feeds(request, course_id, feed_type):
+    course = get_object_or_404(models.Course, pk=course_id)
+    if feed_type == '':
+        return g.render('feeds/course_feed_index.xhtml', 
+                        course=course)
+    else:
+        items = course.items()
+        def render_title(item):
+            return item.title
+        if feed_type == 'top-level':
+            items = items.filter(parent_heading=None).order_by('-sort_order')
+        elif feed_type == 'recent-changes':
+            items = items.order_by('-last_modified')
+        elif feed_type == 'tree':
+            def flatten(nodes, acc):
+                for node in nodes:
+                    item, kids = node
+                    acc.append(item)
+                    flatten(kids, acc)
+                return acc
+            items = flatten(course.item_tree(), [])
+            def render_title(item):
+                if item.parent_heading:
+                    return '%s :: %s' % (item.parent_heading.title, item.title)
+                else:
+                    return item.title
+
+        lastmod = max(i.last_modified for i in items)
+        return g.render('feeds/course_atom.xml',
+                        course=course,
+                        feed_type=feed_type,
+                        lastmod=lastmod,
+                        render_title=render_title,
+                        items=items,
+                        root='http://localhost:8000',
+                        _serialization='xml')

Modified: servres/trunk/conifer/templates/components/course.xhtml
===================================================================
--- servres/trunk/conifer/templates/components/course.xhtml	2009-03-12 22:44:40 UTC (rev 167)
+++ servres/trunk/conifer/templates/components/course.xhtml	2009-03-13 01:57:31 UTC (rev 168)
@@ -22,6 +22,7 @@
       ${course_search(course)}
       <h1><a href="${course.course_url()}"><span py:if="course.code">${course.code}: </span>${course.title}</a></h1>
     </div>
+      <div id="feeds_panel"><a href="${course.course_url('feeds/')}">Feeds</a></div>
   </div>
   
   <!-- !show_tree: display a tree of items in a hierarchical style. -->

Added: servres/trunk/conifer/templates/feeds/course_atom.xml
===================================================================
--- servres/trunk/conifer/templates/feeds/course_atom.xml	                        (rev 0)
+++ servres/trunk/conifer/templates/feeds/course_atom.xml	2009-03-13 01:57:31 UTC (rev 168)
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      xmlns:py="http://genshi.edgewall.org/">
+  <title>Reserves (${feed_type}) for ${course.list_display()}</title>
+  <link href="${root}${course.course_url()}"
+  rel="alternate"/>
+  <link href="${root}${course.course_url('feeds/' + feed_type)}"
+  rel="self"/>
+  <id>${root}${course.course_url('feeds/') + feed_type}</id>
+  <updated>${lastmod.strftime("%Y-%m-%dT%H:%M:%SZ")}</updated>
+  <entry py:for="item in items">
+    <title>${render_title(item)}</title>
+    <link href="${root}${item.item_url(force_local_url=True)}"
+    rel="alternate"></link>
+    <updated>${item.last_modified.strftime("%Y-%m-%dT%H:%M:%SZ")}</updated>
+    <id>${root}${item.item_url('')}</id>
+    <summary type="html">${item.get_item_type_display()}.
+    <div py:if="item.item_type=='HEADING'" py:strip="True">
+      Contains ${len(models.Item.objects.filter(parent_heading=item))} items.
+    </div>
+  </summary>
+  </entry>
+</feed>

Added: servres/trunk/conifer/templates/feeds/course_feed_index.xhtml
===================================================================
--- servres/trunk/conifer/templates/feeds/course_feed_index.xhtml	                        (rev 0)
+++ servres/trunk/conifer/templates/feeds/course_feed_index.xhtml	2009-03-13 01:57:31 UTC (rev 168)
@@ -0,0 +1,22 @@
+<?python
+title = _('Available Feeds')
+?>
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:xi="http://www.w3.org/2001/XInclude"
+      xmlns:py="http://genshi.edgewall.org/">
+  <xi:include href="../master.xhtml"/>
+  <xi:include href="../components/course.xhtml"/>
+  <head>
+    <title>${title}</title>
+    <script type="text/javascript" src="/static/menublocks.js"/>
+  </head>
+  <body>
+    ${course_banner(course)}
+    <h2>Available Feeds</h2>
+    <ul>
+      <li><a href="recent-changes">Recent changes</a></li>
+      <li><a href="top-level">Top level items in this site</a></li>
+      <li><a href="tree">Tree-walk of all items in this site</a></li>
+    </ul>
+  </body>
+</html>



More information about the open-ils-commits mailing list