* 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.
This commit is contained in:
parent
d3dce6f775
commit
b316b2f4a3
|
@ -18,6 +18,8 @@ from view.Json import Json
|
||||||
from view.Html_file import Html_file
|
from view.Html_file import Html_file
|
||||||
from view.Note_tree_area import Note_tree_area
|
from view.Note_tree_area import Note_tree_area
|
||||||
from view.Notebook_rss import Notebook_rss
|
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 ):
|
class Notebooks( object ):
|
||||||
|
@ -28,7 +30,7 @@ class Notebooks( object ):
|
||||||
"""
|
"""
|
||||||
Controller for dealing with notebooks and their notes, corresponding to the "/notebooks" URL.
|
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.
|
Create a new Notebooks object.
|
||||||
|
|
||||||
|
@ -39,11 +41,14 @@ class Notebooks( object ):
|
||||||
@type files: controller.Files
|
@type files: controller.Files
|
||||||
@param files: controller for all uploaded files, used here for deleting files that are no longer
|
@param files: controller for all uploaded files, used here for deleting files that are no longer
|
||||||
referenced within saved notes
|
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
|
@return: newly constructed Notebooks
|
||||||
"""
|
"""
|
||||||
self.__database = database
|
self.__database = database
|
||||||
self.__users = users
|
self.__users = users
|
||||||
self.__files = files
|
self.__files = files
|
||||||
|
self.__https_url = https_url
|
||||||
|
|
||||||
@expose( view = Main_page, rss = Notebook_rss )
|
@expose( view = Main_page, rss = Notebook_rss )
|
||||||
@strongly_expire
|
@strongly_expire
|
||||||
|
@ -204,6 +209,70 @@ class Notebooks( object ):
|
||||||
invites = invites or [],
|
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 )
|
@expose( view = Json )
|
||||||
@strongly_expire
|
@strongly_expire
|
||||||
@end_transaction
|
@end_transaction
|
||||||
|
|
|
@ -46,7 +46,7 @@ class Root( object ):
|
||||||
settings[ u"global" ].get( u"luminotes.rate_plans", [] ),
|
settings[ u"global" ].get( u"luminotes.rate_plans", [] ),
|
||||||
)
|
)
|
||||||
self.__files = Files( database, self.__users )
|
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
|
self.__suppress_exceptions = suppress_exceptions # used for unit tests
|
||||||
|
|
||||||
@expose( Main_page )
|
@expose( Main_page )
|
||||||
|
@ -55,7 +55,7 @@ class Root( object ):
|
||||||
@validate(
|
@validate(
|
||||||
note_title = unicode,
|
note_title = unicode,
|
||||||
invite_id = Valid_id( none_okay = True ),
|
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 ),
|
plan = Valid_int( none_okay = True ),
|
||||||
user_id = Valid_id( none_okay = True ),
|
user_id = Valid_id( none_okay = True ),
|
||||||
)
|
)
|
||||||
|
|
|
@ -407,7 +407,7 @@ class Users( object ):
|
||||||
password = Valid_string( min = 1, max = 30 ),
|
password = Valid_string( min = 1, max = 30 ),
|
||||||
login_button = unicode,
|
login_button = unicode,
|
||||||
invite_id = Valid_id( none_okay = True ),
|
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 ):
|
def login( self, username, password, login_button, invite_id = None, after_login = None ):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -9,5 +9,7 @@ class Rss_item( Item ):
|
||||||
Link( link ),
|
Link( link ),
|
||||||
Description( description ),
|
Description( description ),
|
||||||
Dc_date( date ),
|
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"" ),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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 ) ),
|
||||||
|
),
|
||||||
|
)
|
|
@ -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 <a href="%s">this notebook</a> has been updated. <a href="%s?note_id=%s">View the note.</a>' % ( 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 ) )
|
Reference in New Issue