diff --git a/controller/Expose.py b/controller/Expose.py
index 7b83051..214b2a0 100644
--- a/controller/Expose.py
+++ b/controller/Expose.py
@@ -74,6 +74,7 @@ def expose( view = None, rss = None ):
return unicode( view_override( **result ) )
except:
if redirect is None:
+ print result
raise
# if that doesn't work, and there's a redirect, then redirect
diff --git a/controller/Notebooks.py b/controller/Notebooks.py
index 7ee4ed3..233962c 100644
--- a/controller/Notebooks.py
+++ b/controller/Notebooks.py
@@ -68,6 +68,8 @@ class Notebooks( object ):
@param parent_id: id of parent notebook to this notebook (optional)
@type revision: unicode or NoneType
@param revision: revision timestamp of the provided note (optional)
+ @type user_id: unicode or NoneType
+ @param user_id: id of current logged-in user (if any)
@rtype: unicode
@return: rendered HTML page
"""
@@ -97,7 +99,7 @@ class Notebooks( object ):
'notebook': notebook,
'startup_notes': notelist,
'total_notes_count': notecount,
- 'note': note or None,
+ 'notes': notelist,
}
@raise Access_error: the current user doesn't have access to the given notebook or note
@raise Validation_error: one of the arguments is invalid
@@ -130,7 +132,7 @@ class Notebooks( object ):
notebook = notebook,
startup_notes = startup_notes,
total_notes_count = total_notes_count,
- note = note,
+ notes = note and [ note ] or [],
)
@expose( view = Json )
@@ -693,3 +695,36 @@ class Notebooks( object ):
notebook_name = notebook.name,
notes = startup_notes + other_notes,
)
+
+ def load_recent_notes( self, notebook_id, start = 0, count = 10, user_id = None ):
+ """
+ Provide the information necessary to display the page for a particular notebook's most recent
+ notes.
+
+ @type notebook_id: unicode
+ @param notebook_id: id of the notebook to display
+ @type start: unicode or NoneType
+ @param start: index of recent note to start with (defaults to 0, the most recent note)
+ @type count: int or NoneType
+ @param count: number of recent notes to display (defaults to 10 notes)
+ @type user_id: unicode or NoneType
+ @param user_id: id of current logged-in user (if any)
+ @rtype: dict
+ @return: data for Main_page() constructor
+ @raise Access_error: the current user doesn't have access to the given notebook or note
+ """
+ if not self.__users.check_access( user_id, notebook_id ):
+ raise Access_error()
+
+ notebook = self.__database.load( Notebook, notebook_id )
+
+ if notebook is None:
+ raise Access_error()
+
+ recent_notes = self.__database.select_many( Note, notebook.sql_load_recent_notes( start, count ) )
+
+ result = self.__users.current( user_id )
+ result.update( self.contents( notebook_id, user_id = user_id ) )
+ result[ u"notes" ] = recent_notes
+
+ return result
diff --git a/controller/Root.py b/controller/Root.py
index 97cd0d3..bb67ed4 100644
--- a/controller/Root.py
+++ b/controller/Root.py
@@ -2,7 +2,7 @@ import cherrypy
from Expose import expose
from Expire import strongly_expire
-from Validate import validate
+from Validate import validate, Valid_int
from Notebooks import Notebooks
from Users import Users, grab_user_id
from Database import Valid_id
@@ -106,11 +106,36 @@ class Root( object ):
return dict( redirect = https_url )
result = self.__users.current( user_id )
- first_notebook_id = result[ u"notebooks" ][ 0 ].object_id
- result.update( self.__notebooks.contents( first_notebook_id, user_id = user_id ) )
+ main_notebooks = [ nb for nb in result[ "notebooks" ] if nb.name == u"Luminotes" ]
+ result.update( self.__notebooks.contents( main_notebooks[ 0 ].object_id, user_id = user_id ) )
return result
+ @expose( view = Main_page )
+ @grab_user_id
+ @validate(
+ start = Valid_int( min = 0 ),
+ count = Valid_int( min = 1, max = 50 ),
+ user_id = Valid_id( none_okay = True ),
+ )
+ def blog( self, start = 0, count = 10, user_id = None ):
+ """
+ Provide the information necessary to display the blog notebook with notes in reverse
+ chronological order.
+
+ @type start: unicode or NoneType
+ @param start: index of recent note to start with (defaults to 0, the most recent note)
+ @type count: int or NoneType
+ @param count: number of recent notes to display (defaults to 10 notes)
+ @rtype: unicode
+ @return: rendered HTML page
+ @raise Validation_error: one of the arguments is invalid
+ """
+ result = self.__users.current( user_id = None )
+ blog_notebooks = [ nb for nb in result[ "notebooks" ] if nb.name == u"Luminotes blog" ]
+
+ return self.__notebooks.load_recent_notes( blog_notebooks[ 0 ].object_id, start, count, user_id )
+
# TODO: move this method to controller.Notebooks, and maybe give it a more sensible name
@expose( view = Json )
def next_id( self ):
diff --git a/controller/Users.py b/controller/Users.py
index 42c117d..6cd7b31 100644
--- a/controller/Users.py
+++ b/controller/Users.py
@@ -370,21 +370,22 @@ class Users( object ):
# in addition to this user's own notebooks, add to that list the anonymous user's notebooks
login_url = None
- notebooks = self.__database.select_many( Notebook, anonymous.sql_load_notebooks() )
+ anon_notebooks = self.__database.select_many( Notebook, anonymous.sql_load_notebooks() )
if user_id and user_id != anonymous.object_id:
- notebooks += self.__database.select_many( Notebook, user.sql_load_notebooks() )
+ notebooks = self.__database.select_many( Notebook, user.sql_load_notebooks() )
# if the user is not logged in, return a login URL
else:
- if len( notebooks ) > 0 and notebooks[ 0 ]:
- main_notebook = notebooks[ 0 ]
+ notebooks = []
+ if len( anon_notebooks ) > 0 and anon_notebooks[ 0 ]:
+ main_notebook = anon_notebooks[ 0 ]
login_note = self.__database.select_one( Note, main_notebook.sql_load_note_by_title( u"login" ) )
if login_note:
login_url = "%s/notebooks/%s?note_id=%s" % ( self.__https_url, main_notebook.object_id, login_note.object_id )
return dict(
user = user,
- notebooks = notebooks,
+ notebooks = notebooks + anon_notebooks,
login_url = login_url,
logout_url = self.__https_url + u"/",
rate_plan = ( user.rate_plan < len( self.__rate_plans ) ) and self.__rate_plans[ user.rate_plan ] or {},
@@ -548,11 +549,11 @@ class Users( object ):
result[ "startup_notes" ] = self.__database.select_many( Note, main_notebook.sql_load_startup_notes() )
result[ "total_notes_count" ] = self.__database.select_one( Note, main_notebook.sql_count_notes() )
result[ "note_read_write" ] = False
- result[ "note" ] = Note.create(
+ result[ "notes" ] = [ Note.create(
object_id = u"password_reset",
contents = unicode( Redeem_reset_note( password_reset_id, matching_users ) ),
notebook_id = main_notebook.object_id,
- )
+ ) ]
return result
diff --git a/controller/Validate.py b/controller/Validate.py
index 5f0963b..e136b6c 100644
--- a/controller/Validate.py
+++ b/controller/Validate.py
@@ -134,6 +134,26 @@ class Valid_bool( object ):
raise ValueError()
+class Valid_int( object ):
+ """
+ Validator for an integer value.
+ """
+ def __init__( self, min = None, max = None ):
+ self.min = min
+ self.max = max
+ self.message = None
+
+ def __call__( self, value ):
+ value = int( value )
+
+ if self.min is not None and value < min:
+ self.message = "is too small"
+ if self.max is not None and value > max:
+ self.message = "is too large"
+
+ return value
+
+
def validate( **expected ):
"""
validate() can be used to require that the arguments of the decorated method successfully pass
diff --git a/controller/test/Test_controller.py b/controller/test/Test_controller.py
index 7bdf860..ed5d57a 100644
--- a/controller/test/Test_controller.py
+++ b/controller/test/Test_controller.py
@@ -4,6 +4,7 @@ from Stub_view import Stub_view
from config import Common
from datetime import datetime
from StringIO import StringIO
+from copy import copy
class Test_controller( object ):
@@ -128,6 +129,23 @@ class Test_controller( object ):
Notebook.sql_load_startup_notes = lambda self: \
lambda database: sql_load_startup_notes( self, database )
+ def sql_load_recent_notes( self, database, start, count ):
+ notes = []
+
+ for ( object_id, obj_list ) in database.objects.items():
+ obj = obj_list[ -1 ]
+ if isinstance( obj, Note ) and obj.notebook_id == self.object_id:
+ obj = copy( obj )
+ obj._Note__creation = database.objects[ object_id ][ 0 ].revision
+ notes.append( obj )
+
+ notes.sort( lambda a, b: -cmp( a.creation, b.creation ) )
+ notes = notes[ start : start + count ]
+ return notes
+
+ Notebook.sql_load_recent_notes = lambda self, start = 0, count = 10: \
+ lambda database: sql_load_recent_notes( self, database, start, count )
+
def sql_load_note_by_title( self, title, database ):
notes = []
diff --git a/controller/test/Test_notebooks.py b/controller/test/Test_notebooks.py
index 193759c..3453610 100644
--- a/controller/test/Test_notebooks.py
+++ b/controller/test/Test_notebooks.py
@@ -80,7 +80,7 @@ class Test_notebooks( Test_controller ):
assert result.get( u"notebook" ).object_id == self.notebook.object_id
assert len( result.get( u"startup_notes" ) ) == 1
assert result[ "total_notes_count" ] == 2
- assert result.get( u"note" ) is None
+ assert result.get( u"notes" ) == []
assert result.get( u"parent_id" ) == None
assert result.get( u"note_read_write" ) in ( None, True )
@@ -103,7 +103,10 @@ class Test_notebooks( Test_controller ):
assert result.get( u"notebook" ).object_id == self.notebook.object_id
assert len( result.get( u"startup_notes" ) ) == 1
assert result[ "total_notes_count" ] == 2
- assert result.get( u"note" ).object_id == self.note.object_id
+
+ assert result.get( "notes" )
+ assert len( result.get( "notes" ) ) == 1
+ assert result.get( u"notes" )[ 0 ].object_id == self.note.object_id
assert result.get( u"parent_id" ) == None
assert result.get( u"note_read_write" ) in ( None, True )
@@ -130,8 +133,11 @@ class Test_notebooks( Test_controller ):
assert result.get( u"notebook" ).object_id == self.notebook.object_id
assert len( result.get( u"startup_notes" ) ) == 1
assert result[ "total_notes_count" ] == 2
- assert result.get( u"note" ).object_id == self.note.object_id
- assert result.get( u"note" ).revision == self.note.revision
+
+ assert result.get( "notes" )
+ assert len( result.get( "notes" ) ) == 1
+ assert result.get( u"notes" )[ 0 ].object_id == self.note.object_id
+ assert result.get( u"notes" )[ 0 ].revision == self.note.revision
assert result.get( u"parent_id" ) == None
assert result.get( u"note_read_write" ) == False
@@ -155,7 +161,7 @@ class Test_notebooks( Test_controller ):
assert result.get( u"notebook" ).object_id == self.notebook.object_id
assert len( result.get( u"startup_notes" ) ) == 1
assert result[ "total_notes_count" ] == 2
- assert result.get( u"note" ) is None
+ assert result.get( u"notes" ) == []
assert result.get( u"parent_id" ) == parent_id
assert result.get( u"note_read_write" ) in ( None, True )
@@ -171,7 +177,7 @@ class Test_notebooks( Test_controller ):
notebook = result[ "notebook" ]
startup_notes = result[ "startup_notes" ]
assert result[ "total_notes_count" ] == 2
- assert result[ "note" ] == None
+ assert result[ "notes" ] == []
assert notebook.object_id == self.notebook.object_id
assert notebook.read_write == True
@@ -196,8 +202,11 @@ class Test_notebooks( Test_controller ):
assert len( startup_notes ) == 1
assert startup_notes[ 0 ].object_id == self.note.object_id
- note = result[ "note" ]
+ notes = result[ "notes" ]
+ assert notes
+ assert len( notes ) == 1
+ note = notes[ 0 ]
assert note.object_id == self.note.object_id
user = self.database.load( User, self.user.object_id )
assert user.storage_bytes == 0
@@ -220,8 +229,11 @@ class Test_notebooks( Test_controller ):
assert len( startup_notes ) == 1
assert startup_notes[ 0 ].object_id == self.note.object_id
- note = result[ "note" ]
+ notes = result[ "notes" ]
+ assert notes
+ assert len( notes ) == 1
+ note = notes[ 0 ]
assert note.object_id == self.note.object_id
assert note.revision == self.note.revision
user = self.database.load( User, self.user.object_id )
@@ -255,7 +267,7 @@ class Test_notebooks( Test_controller ):
notebook = result[ "notebook" ]
startup_notes = result[ "startup_notes" ]
- assert result[ "note" ] == None
+ assert result[ "notes" ] == []
assert result[ "total_notes_count" ] == 0
assert notebook.object_id == self.anon_notebook.object_id
@@ -1604,6 +1616,101 @@ class Test_notebooks( Test_controller ):
assert result.get( "error" )
+ def test_recent_notes( self ):
+ result = cherrypy.root.notebooks.load_recent_notes(
+ self.notebook.object_id,
+ user_id = self.user.object_id,
+ )
+
+ assert result.get( u"user" ).object_id == self.user.object_id
+ assert len( result.get( u"notebooks" ) ) == 3
+ assert result.get( u"login_url" ) is None
+ assert result.get( u"logout_url" )
+ assert result.get( u"rate_plan" )
+ assert result.get( u"notebook" ).object_id == self.notebook.object_id
+ assert len( result.get( u"startup_notes" ) ) == 1
+ assert result[ "total_notes_count" ] == 2
+
+ notes = result.get( u"notes" )
+ assert notes
+ assert len( notes ) == 2
+ assert notes[ 0 ].object_id == self.note2.object_id
+ assert notes[ 1 ].object_id == self.note.object_id
+
+ assert result.get( u"parent_id" ) == None
+ assert result.get( u"note_read_write" ) in ( None, True )
+
+ user = self.database.load( User, self.user.object_id )
+ assert user.storage_bytes == 0
+
+ def test_recent_notes_with_start( self ):
+ result = cherrypy.root.notebooks.load_recent_notes(
+ self.notebook.object_id,
+ start = 1,
+ user_id = self.user.object_id,
+ )
+
+ assert result.get( u"user" ).object_id == self.user.object_id
+ assert len( result.get( u"notebooks" ) ) == 3
+ assert result.get( u"login_url" ) is None
+ assert result.get( u"logout_url" )
+ assert result.get( u"rate_plan" )
+ assert result.get( u"notebook" ).object_id == self.notebook.object_id
+ assert len( result.get( u"startup_notes" ) ) == 1
+ assert result[ "total_notes_count" ] == 2
+
+ notes = result.get( u"notes" )
+ assert notes
+ assert len( notes ) == 1
+ assert notes[ 0 ].object_id == self.note.object_id
+
+ assert result.get( u"parent_id" ) == None
+ assert result.get( u"note_read_write" ) in ( None, True )
+
+ user = self.database.load( User, self.user.object_id )
+ assert user.storage_bytes == 0
+
+ def test_recent_notes_with_count( self ):
+ result = cherrypy.root.notebooks.load_recent_notes(
+ self.notebook.object_id,
+ count = 1,
+ user_id = self.user.object_id,
+ )
+
+ assert result.get( u"user" ).object_id == self.user.object_id
+ assert len( result.get( u"notebooks" ) ) == 3
+ assert result.get( u"login_url" ) is None
+ assert result.get( u"logout_url" )
+ assert result.get( u"rate_plan" )
+ assert result.get( u"notebook" ).object_id == self.notebook.object_id
+ assert len( result.get( u"startup_notes" ) ) == 1
+ assert result[ "total_notes_count" ] == 2
+
+ notes = result.get( u"notes" )
+ assert notes
+ assert len( notes ) == 1
+ assert notes[ 0 ].object_id == self.note2.object_id
+
+ assert result.get( u"parent_id" ) == None
+ assert result.get( u"note_read_write" ) in ( None, True )
+
+ user = self.database.load( User, self.user.object_id )
+ assert user.storage_bytes == 0
+
+ @raises( Access_error )
+ def test_recent_notes_with_unknown_notebok( self ):
+ result = cherrypy.root.notebooks.load_recent_notes(
+ self.unknown_notebook_id,
+ user_id = self.user.object_id,
+ )
+
+ @raises( Access_error )
+ def test_recent_notes_with_incorrect_user( self ):
+ result = cherrypy.root.notebooks.load_recent_notes(
+ self.notebook.object_id,
+ user_id = self.anonymous.object_id,
+ )
+
def login( self ):
result = self.http_post( "/users/login", dict(
username = self.username,
diff --git a/controller/test/Test_root.py b/controller/test/Test_root.py
index 9d16b88..be1df63 100644
--- a/controller/test/Test_root.py
+++ b/controller/test/Test_root.py
@@ -13,7 +13,7 @@ class Test_root( Test_controller ):
self.notebook = Notebook.create( self.database.next_id( Notebook ), u"my notebook" )
self.database.save( self.notebook )
- self.anon_notebook = Notebook.create( self.database.next_id( Notebook ), u"anon notebook" )
+ self.anon_notebook = Notebook.create( self.database.next_id( Notebook ), u"Luminotes" )
self.database.save( self.anon_notebook )
self.anon_note = Note.create(
self.database.next_id( Note ), u"
my note
",
@@ -21,6 +21,14 @@ class Test_root( Test_controller ):
)
self.database.save( self.anon_note )
+ self.blog_notebook = Notebook.create( self.database.next_id( Notebook ), u"Luminotes blog" )
+ self.database.save( self.blog_notebook )
+ self.blog_note = Note.create(
+ self.database.next_id( Note ), u"my blog entry
",
+ notebook_id = self.blog_notebook.object_id,
+ )
+ self.database.save( self.blog_note )
+
self.username = u"mulder"
self.password = u"trustno1"
self.email_address = u"outthere@example.com"
@@ -34,6 +42,7 @@ class Test_root( Test_controller ):
self.anonymous = User.create( self.database.next_id( User ), u"anonymous" )
self.database.save( self.anonymous )
self.database.execute( self.anonymous.sql_save_notebook( self.anon_notebook.object_id ) )
+ self.database.execute( self.anonymous.sql_save_notebook( self.blog_notebook.object_id ) )
def test_index( self ):
result = self.http_get( "/" )
@@ -68,8 +77,9 @@ class Test_root( Test_controller ):
)
assert result
- assert result[ u"note" ]
- assert result[ u"note" ].object_id == self.anon_note.object_id
+ assert result[ u"notes" ]
+ assert len( result[ u"notes" ] ) == 1
+ assert result[ u"notes" ][ 0 ].object_id == self.anon_note.object_id
def test_default_with_unknown_note( self ):
result = self.http_get(
@@ -99,6 +109,13 @@ class Test_root( Test_controller ):
assert result.get( "redirect" )
assert result.get( "redirect" ).startswith( "https://" )
+ def test_blog( self ):
+ result = self.http_get(
+ "/blog",
+ )
+
+ assert result
+
def test_next_id( self ):
result = self.http_get( "/next_id" )
diff --git a/controller/test/Test_users.py b/controller/test/Test_users.py
index acb2714..5e5c8a7 100644
--- a/controller/test/Test_users.py
+++ b/controller/test/Test_users.py
@@ -104,26 +104,26 @@ class Test_users( Test_controller ):
notebooks = result[ u"notebooks" ]
notebook = notebooks[ 0 ]
- assert notebook.object_id == self.anon_notebook.object_id
- assert notebook.revision == self.anon_notebook.revision
- assert notebook.name == self.anon_notebook.name
- assert notebook.trash_id == None
- assert notebook.read_write == False
-
- notebook = notebooks[ 1 ]
assert notebook.object_id == new_notebook_id
assert notebook.revision
assert notebook.name == u"my notebook"
assert notebook.trash_id
assert notebook.read_write == True
- notebook = notebooks[ 2 ]
- assert notebook.object_id == notebooks[ 1 ].trash_id
+ notebook = notebooks[ 1 ]
+ assert notebook.object_id == notebooks[ 0 ].trash_id
assert notebook.revision
assert notebook.name == u"trash"
assert notebook.trash_id == None
assert notebook.read_write == True
+ notebook = notebooks[ 2 ]
+ assert notebook.object_id == self.anon_notebook.object_id
+ assert notebook.revision == self.anon_notebook.revision
+ assert notebook.name == self.anon_notebook.name
+ assert notebook.trash_id == None
+ assert notebook.read_write == False
+
assert result.get( u"login_url" ) is None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
@@ -164,26 +164,26 @@ class Test_users( Test_controller ):
notebooks = result[ u"notebooks" ]
assert len( notebooks ) == 3
notebook = notebooks[ 0 ]
- assert notebook.object_id == self.anon_notebook.object_id
- assert notebook.revision == self.anon_notebook.revision
- assert notebook.name == self.anon_notebook.name
- assert notebook.trash_id == None
- assert notebook.read_write == False
-
- notebook = notebooks[ 1 ]
assert notebook.object_id == new_notebook_id
assert notebook.revision
assert notebook.name == u"my notebook"
assert notebook.trash_id
assert notebook.read_write == True
- notebook = notebooks[ 2 ]
- assert notebook.object_id == notebooks[ 1 ].trash_id
+ notebook = notebooks[ 1 ]
+ assert notebook.object_id == notebooks[ 0 ].trash_id
assert notebook.revision
assert notebook.name == u"trash"
assert notebook.trash_id == None
assert notebook.read_write == True
+ notebook = notebooks[ 2 ]
+ assert notebook.object_id == self.anon_notebook.object_id
+ assert notebook.revision == self.anon_notebook.revision
+ assert notebook.name == self.anon_notebook.name
+ assert notebook.trash_id == None
+ assert notebook.read_write == False
+
assert result.get( u"login_url" ) is None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
@@ -259,12 +259,12 @@ class Test_users( Test_controller ):
assert result[ u"user" ].object_id == self.user.object_id
assert result[ u"user" ].username == self.user.username
assert len( result[ u"notebooks" ] ) == 3
- assert result[ u"notebooks" ][ 0 ].object_id == self.anon_notebook.object_id
- assert result[ u"notebooks" ][ 0 ].read_write == False
- assert result[ u"notebooks" ][ 1 ].object_id == self.notebooks[ 0 ].object_id
+ assert result[ u"notebooks" ][ 0 ].object_id == self.notebooks[ 0 ].object_id
+ assert result[ u"notebooks" ][ 0 ].read_write == True
+ assert result[ u"notebooks" ][ 1 ].object_id == self.notebooks[ 1 ].object_id
assert result[ u"notebooks" ][ 1 ].read_write == True
- assert result[ u"notebooks" ][ 2 ].object_id == self.notebooks[ 1 ].object_id
- assert result[ u"notebooks" ][ 2 ].read_write == True
+ assert result[ u"notebooks" ][ 2 ].object_id == self.anon_notebook.object_id
+ assert result[ u"notebooks" ][ 2 ].read_write == False
assert result[ u"login_url" ] is None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
@@ -390,11 +390,14 @@ class Test_users( Test_controller ):
assert result[ u"startup_notes" ][ 0 ].title == self.startup_note.title
assert result[ u"startup_notes" ][ 0 ].contents == self.startup_note.contents
assert result[ u"note_read_write" ] is False
- assert result[ u"note" ].title == u"complete your password reset"
- assert result[ u"note" ].notebook_id == self.anon_notebook.object_id
- assert u"password reset" in result[ u"note" ].contents
- assert self.user.username in result[ u"note" ].contents
- assert self.user2.username in result[ u"note" ].contents
+
+ assert result[ u"notes" ]
+ assert len( result[ u"notes" ] ) == 1
+ assert result[ u"notes" ][ 0 ].title == u"complete your password reset"
+ assert result[ u"notes" ][ 0 ].notebook_id == self.anon_notebook.object_id
+ assert u"password reset" in result[ u"notes" ][ 0 ].contents
+ assert self.user.username in result[ u"notes" ][ 0 ].contents
+ assert self.user2.username in result[ u"notes" ][ 0 ].contents
def test_redeem_reset_unknown( self ):
password_reset_id = u"unknownresetid"
diff --git a/model/Note.py b/model/Note.py
index bf35210..0d7d495 100644
--- a/model/Note.py
+++ b/model/Note.py
@@ -10,7 +10,7 @@ class Note( Persistent ):
TITLE_PATTERN = re.compile( u"(.*?)
", flags = re.IGNORECASE )
def __init__( self, object_id, revision = None, title = None, contents = None, notebook_id = None,
- startup = None, deleted_from_id = None, rank = None ):
+ startup = None, deleted_from_id = None, rank = None, creation = None ):
"""
Create a new note with the given id and contents.
@@ -30,6 +30,8 @@ class Note( Persistent ):
@param deleted_from_id: id of the notebook that this note was deleted from (optional)
@type rank: float or NoneType
@param rank: indicates numeric ordering of this note in relation to other startup notes
+ @type creation: datetime or NoneType
+ @param creation: creation timestamp of the object (optional, defaults to None)
@rtype: Note
@return: newly constructed note
"""
@@ -40,9 +42,10 @@ class Note( Persistent ):
self.__startup = startup or False
self.__deleted_from_id = deleted_from_id
self.__rank = rank
+ self.__creation = creation
@staticmethod
- def create( object_id, contents = None, notebook_id = None, startup = None, rank = None ):
+ def create( object_id, contents = None, notebook_id = None, startup = None, rank = None, creation = None ):
"""
Convenience constructor for creating a new note.
@@ -56,10 +59,12 @@ class Note( Persistent ):
@param startup: whether this note should be displayed upon startup (optional, defaults to False)
@type rank: float or NoneType
@param rank: indicates numeric ordering of this note in relation to other startup notes
+ @type creation: datetime or NoneType
+ @param creation: creation timestamp of the object (optional, defaults to None)
@rtype: Note
@return: newly constructed note
"""
- note = Note( object_id, notebook_id = notebook_id, startup = startup, rank = rank )
+ note = Note( object_id, notebook_id = notebook_id, startup = startup, rank = rank, creation = creation )
note.contents = contents
return note
@@ -138,6 +143,7 @@ class Note( Persistent ):
contents = self.__contents,
title = self.__title,
deleted_from_id = self.__deleted_from_id,
+ creation = self.__creation,
) )
return d
@@ -148,3 +154,4 @@ class Note( Persistent ):
startup = property( lambda self: self.__startup, __set_startup )
deleted_from_id = property( lambda self: self.__deleted_from_id, __set_deleted_from_id )
rank = property( lambda self: self.__rank, __set_rank )
+ creation = property( lambda self: self.__creation )
diff --git a/model/Notebook.py b/model/Notebook.py
index b2f49ce..287800e 100644
--- a/model/Notebook.py
+++ b/model/Notebook.py
@@ -92,6 +92,27 @@ class Notebook( Persistent ):
"""
return "select * from note_current where notebook_id = %s and startup = 't' order by rank;" % quote( self.object_id )
+ def sql_load_recent_notes( self, start = 0, count = 10 ):
+ """
+ Return a SQL string to load a list of the most recently created notes within this notebook.
+
+ @type start: int or NoneType
+ @param start: index of recent note to start with (defaults to 0, the most recent note)
+ @type count: int or NoneType
+ @param count: number of recent notes to return (defaults to 10 notes)
+ """
+ return \
+ """
+ select
+ note_current.*, note_creation.revision as creation
+ from
+ note_current,
+ ( select id, min( revision ) as revision from note where notebook_id = %s group by id ) as note_creation
+ where
+ notebook_id = %s and note_current.id = note_creation.id
+ offset %d limit %d;
+ """ % ( quote( self.object_id ), quote( self.object_id ), start, count )
+
def sql_load_note_by_id( self, note_id ):
"""
Return a SQL string to load a particular note within this notebook by the note's id.
diff --git a/model/test/Test_note.py b/model/test/Test_note.py
index 98ec339..2226084 100644
--- a/model/test/Test_note.py
+++ b/model/test/Test_note.py
@@ -11,9 +11,10 @@ class Test_note( object ):
self.notebook_id = u"18"
self.startup = False
self.rank = 17.5
+ self.creation = datetime.now()
self.delta = timedelta( seconds = 1 )
- self.note = Note.create( self.object_id, self.contents, self.notebook_id, self.startup, self.rank )
+ self.note = Note.create( self.object_id, self.contents, self.notebook_id, self.startup, self.rank, self.creation )
def test_create( self ):
assert self.note.object_id == self.object_id
@@ -24,6 +25,7 @@ class Test_note( object ):
assert self.note.startup == self.startup
assert self.note.deleted_from_id == None
assert self.note.rank == self.rank
+ assert self.note.creation == self.creation
def test_set_contents( self ):
new_title = u"new title"
@@ -39,6 +41,7 @@ class Test_note( object ):
assert self.note.startup == self.startup
assert self.note.deleted_from_id == None
assert self.note.rank == self.rank
+ assert self.note.creation == self.creation
def test_set_contents_with_html_title( self ):
new_title = u"new title"
@@ -55,6 +58,7 @@ class Test_note( object ):
assert self.note.startup == self.startup
assert self.note.deleted_from_id == None
assert self.note.rank == self.rank
+ assert self.note.creation == self.creation
def test_set_contents_with_multiple_titles( self ):
new_title = u"new title"
@@ -71,6 +75,7 @@ class Test_note( object ):
assert self.note.startup == self.startup
assert self.note.deleted_from_id == None
assert self.note.rank == self.rank
+ assert self.note.creation == self.creation
def test_set_notebook_id( self ):
previous_revision = self.note.revision
@@ -108,6 +113,7 @@ class Test_note( object ):
assert d.get( "contents" ) == self.contents
assert d.get( "title" ) == self.title
assert d.get( "deleted_from_id" ) == None
+ assert d.get( "creation" ) == self.note.creation
class Test_note_blank( Test_note ):
@@ -118,6 +124,7 @@ class Test_note_blank( Test_note ):
self.notebook_id = None
self.startup = False
self.rank = None
+ self.creation = None
self.delta = timedelta( seconds = 1 )
self.note = Note.create( self.object_id )
@@ -131,3 +138,4 @@ class Test_note_blank( Test_note ):
assert self.note.startup == False
assert self.note.deleted_from_id == None
assert self.note.rank == None
+ assert self.note.creation == None
diff --git a/static/js/Wiki.js b/static/js/Wiki.js
index 96085c4..7b9b576 100644
--- a/static/js/Wiki.js
+++ b/static/js/Wiki.js
@@ -36,7 +36,7 @@ function Wiki( invoker ) {
// populate the wiki with startup notes
this.populate(
evalJSON( getElement( "startup_notes" ).value || "null" ),
- evalJSON( getElement( "note" ).value || "null" ),
+ evalJSON( getElement( "current_notes" ).value || "null" ),
evalJSON( getElement( "note_read_write" ).value || "true" )
);
@@ -111,21 +111,22 @@ Wiki.prototype.display_storage_usage = function( storage_bytes ) {
);
}
-Wiki.prototype.populate = function ( startup_notes, note, note_read_write ) {
+Wiki.prototype.populate = function ( startup_notes, current_notes, note_read_write ) {
// create an editor for each startup note in the received notebook, focusing the first one
var focus = true;
for ( var i in startup_notes ) {
var startup_note = startup_notes[ i ];
this.startup_notes[ startup_note.object_id ] = true;
- // don't actually create an editor if a particular note was provided in the result
- if ( !note ) {
+ // don't actually create an editor if a particular list of notes was provided in the result
+ if ( current_notes.length == 0 ) {
var editor = this.create_editor(
startup_note.object_id,
- // grab this note's contents from the static