From b316b2f4a324b63d4c9f0d76f28595875697bd48 Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Tue, 22 Apr 2008 23:24:30 +0000 Subject: [PATCH] * Increasing after_login max string size from 100 to 1000 to accomodate larger URLs. * controller.Notebooks now takes (and stores) an https_url constructor parameter. * New controller.Notebooks.updates() method to produce an updates RSS feed for a particular notebook. * New controller.Notebooks.get_update_link() method to make a brief page with just a link for an updated note, referred to by the feed. * Implemented views for the new RSS feed. * Fixed bug in Rss_item's guid that caused newlines to be inserted before and after long URLs. * Still need to unit test new controller code. --- controller/Notebooks.py | 71 +++++++++++++++++++++++++++++++++++++++- controller/Root.py | 4 +-- controller/Users.py | 2 +- view/Rss_item.py | 4 ++- view/Update_link_page.py | 28 ++++++++++++++++ view/Updates_rss.py | 49 +++++++++++++++++++++++++++ 6 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 view/Update_link_page.py create mode 100644 view/Updates_rss.py diff --git a/controller/Notebooks.py b/controller/Notebooks.py index 4834ad3..5d69e61 100644 --- a/controller/Notebooks.py +++ b/controller/Notebooks.py @@ -18,6 +18,8 @@ from view.Json import Json from view.Html_file import Html_file from view.Note_tree_area import Note_tree_area from view.Notebook_rss import Notebook_rss +from view.Updates_rss import Updates_rss +from view.Update_link_page import Update_link_page class Notebooks( object ): @@ -28,7 +30,7 @@ class Notebooks( object ): """ Controller for dealing with notebooks and their notes, corresponding to the "/notebooks" URL. """ - def __init__( self, database, users, files ): + def __init__( self, database, users, files, https_url ): """ Create a new Notebooks object. @@ -39,11 +41,14 @@ class Notebooks( object ): @type files: controller.Files @param files: controller for all uploaded files, used here for deleting files that are no longer referenced within saved notes + @type https_url: unicode + @param https_url: base URL to use for SSL http requests, or an empty string @return: newly constructed Notebooks """ self.__database = database self.__users = users self.__files = files + self.__https_url = https_url @expose( view = Main_page, rss = Notebook_rss ) @strongly_expire @@ -204,6 +209,70 @@ class Notebooks( object ): invites = invites or [], ) + @expose( view = None, rss = Updates_rss ) + @strongly_expire + @end_transaction + @validate( + notebook_id = Valid_id(), + notebook_name = Valid_string(), + ) + def updates( self, notebook_id, notebook_name ): + """ + Provide the information necessary to display an updated notes RSS feed for the given notebook. + This method does not require any sort of login. + + @type notebook_id: unicode + @param notebook_id: id of the notebook to provide updates for + @type notebook_name: unicode + @param notebook_name: name of the notebook to include in the RSS feed + @rtype: unicode + @return: rendered RSS feed + """ + notebook = self.__database.load( Notebook, notebook_id ) + if not notebook: + raise Access_error() + + recent_notes = self.__database.select_many( Note, notebook.sql_load_notes( start = 0, count = 10 ) ) + + return dict( + recent_notes = [ ( note.object_id, note.revision ) for note in recent_notes ], + notebook_id = notebook_id, + notebook_name = notebook_name, + https_url = self.__https_url, + ) + + @expose( view = Update_link_page ) + @strongly_expire + @end_transaction + @validate( + notebook_id = Valid_id(), + notebook_name = Valid_string(), + note_id = Valid_id(), + revision = Valid_revision(), + ) + def get_update_link( self, notebook_id, notebook_name, note_id, revision ): + """ + Provide the information necessary to display a link to an updated note. This method does not + require any sort of login. + + @type notebook_id: unicode + @param notebook_id: id of the notebook the note is in + @type notebook_name: unicode + @param notebook_name: name of the notebook + @type note_id: unicode + @param note_id: id of the note to link to + @type revision: unicode + @param revision: ignored; present so RSS feed readers distinguish between different revisions + @rtype: unicode + @return: rendered HTML page + """ + return dict( + notebook_id = notebook_id, + notebook_name = notebook_name, + note_id = note_id, + https_url = self.__https_url, + ) + @expose( view = Json ) @strongly_expire @end_transaction diff --git a/controller/Root.py b/controller/Root.py index a723a14..6867af7 100644 --- a/controller/Root.py +++ b/controller/Root.py @@ -46,7 +46,7 @@ class Root( object ): settings[ u"global" ].get( u"luminotes.rate_plans", [] ), ) self.__files = Files( database, self.__users ) - self.__notebooks = Notebooks( database, self.__users, self.__files ) + self.__notebooks = Notebooks( database, self.__users, self.__files, settings[ u"global" ].get( u"luminotes.https_url", u"" ) ) self.__suppress_exceptions = suppress_exceptions # used for unit tests @expose( Main_page ) @@ -55,7 +55,7 @@ class Root( object ): @validate( note_title = unicode, invite_id = Valid_id( none_okay = True ), - after_login = Valid_string( min = 0, max = 100 ), + after_login = Valid_string( min = 0, max = 1000 ), plan = Valid_int( none_okay = True ), user_id = Valid_id( none_okay = True ), ) diff --git a/controller/Users.py b/controller/Users.py index 2dfb1cb..948a095 100644 --- a/controller/Users.py +++ b/controller/Users.py @@ -407,7 +407,7 @@ class Users( object ): password = Valid_string( min = 1, max = 30 ), login_button = unicode, invite_id = Valid_id( none_okay = True ), - after_login = Valid_string( min = 0, max = 100 ), + after_login = Valid_string( min = 0, max = 1000 ), ) def login( self, username, password, login_button, invite_id = None, after_login = None ): """ diff --git a/view/Rss_item.py b/view/Rss_item.py index f2c7acd..f365816 100644 --- a/view/Rss_item.py +++ b/view/Rss_item.py @@ -9,5 +9,7 @@ class Rss_item( Item ): Link( link ), Description( description ), Dc_date( date ), - Guid( guid ), + # if we don't set the separator to empty, Node inserts newlines when the guid gets too long. + # newlines in guids make Thunderbird angry + Guid( guid, separator = u"" ), ) diff --git a/view/Update_link_page.py b/view/Update_link_page.py new file mode 100644 index 0000000..cf14dd4 --- /dev/null +++ b/view/Update_link_page.py @@ -0,0 +1,28 @@ +from Tags import Html, Head, Title, Body, A + + +class Update_link_page( Html ): + def __init__( self, notebook_id, notebook_name, note_id, https_url ): + if notebook_name == u"Luminotes": + notebook_path = u"/" + elif notebook_name == u"Luminotes user guide": + notebook_path = u"/guide" + elif notebook_name == u"Luminotes blog": + notebook_path = u"/blog" + else: + notebook_path = u"/notebooks/%s" % notebook_id + + notebook_path = https_url + notebook_path + + Html.__init__( + self, + Head( + Title( "Note updated" ), + ), + Body( + u"A note in ", + A( u"this notebook", href = notebook_path ), + u"has been updated.", + A( u"View the note.", href = u"%s?note_id=%s" % ( notebook_path, note_id ) ), + ), + ) diff --git a/view/Updates_rss.py b/view/Updates_rss.py new file mode 100644 index 0000000..a2e62f4 --- /dev/null +++ b/view/Updates_rss.py @@ -0,0 +1,49 @@ +import cgi +from urllib import urlencode +from Rss_channel import Rss_channel +from Rss_item import Rss_item + + +class Updates_rss( Rss_channel ): + def __init__( + self, + recent_notes, + notebook_id, + notebook_name, + https_url, + ): + if notebook_name == u"Luminotes": + notebook_path = u"/" + elif notebook_name == u"Luminotes user guide": + notebook_path = u"/guide" + elif notebook_name == u"Luminotes blog": + notebook_path = u"/blog" + else: + notebook_path = u"/notebooks/%s" % notebook_id + + notebook_path = https_url + notebook_path + + Rss_channel.__init__( + self, + cgi.escape( notebook_name ), + notebook_path, + u"Luminotes notebook", + [ Rss_item( + title = u"Note updated", + link = self.note_link( notebook_id, notebook_name, note_id, revision, https_url ), + description = cgi.escape( u'A note in this notebook has been updated. View the note.' % ( notebook_path, notebook_path, note_id ) ), + date = revision.strftime( "%Y-%m-%dT%H:%M:%SZ" ), + guid = self.note_link( notebook_id, notebook_name, note_id, revision, https_url ), + ) for ( note_id, revision ) in recent_notes ], + ) + + @staticmethod + def note_link( notebook_id, notebook_name, note_id, revision, https_url ): + query = urlencode( [ + ( u"notebook_id", notebook_id ), + ( u"notebook_name", notebook_name ), + ( u"note_id", note_id ), + ( u"revision", unicode( revision ) ), + ] ) + + return cgi.escape( u"%s/notebooks/get_update_link?%s" % ( https_url, query ) )