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 ) )