witten
/
luminotes
Archived
1
0
Fork 0

Some early work in support of a reverse chronological order view of a notebook, useful for a blog.

This commit is contained in:
Dan Helfman 2007-10-30 23:05:46 +00:00
parent 99c08bc3d1
commit ad58956f34
15 changed files with 359 additions and 80 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 = []

View File

@ -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,

View File

@ -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"<h3>my note</h3>",
@ -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"<h3>my blog entry</h3>",
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" )

View File

@ -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"

View File

@ -10,7 +10,7 @@ class Note( Persistent ):
TITLE_PATTERN = re.compile( u"<h3>(.*?)</h3>", 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 )

View File

@ -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.

View File

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

View File

@ -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 <noscript> area
// grab this note's contents from the static notes area
getElement( "static_note_" + startup_note.object_id ).innerHTML,
startup_note.deleted_from_id,
startup_note.revision,
startup_note.creation,
this.notebook.read_write, false, focus
);
@ -134,17 +135,23 @@ Wiki.prototype.populate = function ( startup_notes, note, note_read_write ) {
}
}
// if one particular note was provided, then just display an editor for that note
if ( note )
// if particular notes were provided, then display editors for them
var focus = true;
for ( var i in current_notes ) {
var note = current_notes[ i ];
this.create_editor(
note.object_id,
getElement( "static_note_" + note.object_id ).innerHTML,
note.deleted_from_id,
note.revision,
this.notebook.read_write && note_read_write, false, true
note.creation,
this.notebook.read_write && note_read_write, false, focus
);
focus = false;
}
if ( startup_notes.length == 0 && !note )
if ( startup_notes.length == 0 && current_notes.length == 0 )
this.display_empty_message();
var self = this;
@ -222,7 +229,7 @@ Wiki.prototype.create_blank_editor = function ( event ) {
}
}
var editor = this.create_editor( undefined, undefined, undefined, undefined, this.notebook.read_write, true, true );
var editor = this.create_editor( undefined, undefined, undefined, undefined, undefined, this.notebook.read_write, true, true );
this.increment_total_notes_count();
this.blank_editor_id = editor.id;
@ -416,6 +423,7 @@ Wiki.prototype.parse_loaded_editor = function ( result, note_title, requested_re
if ( requested_revision )
id += " " + requested_revision;
var actual_revision = result.note.revision;
var actual_creation = result.note.creation;
var note_text = result.note.contents;
var deleted_from_id = result.note.deleted;
} else {
@ -423,6 +431,7 @@ Wiki.prototype.parse_loaded_editor = function ( result, note_title, requested_re
var note_text = "<h3>" + note_title;
var deleted_from_id = null;
var actual_revision = null;
var actual_creation = null;
this.increment_total_notes_count();
}
@ -431,7 +440,7 @@ Wiki.prototype.parse_loaded_editor = function ( result, note_title, requested_re
else
var read_write = this.notebook.read_write;
var editor = this.create_editor( id, note_text, deleted_from_id, actual_revision, read_write, true, false );
var editor = this.create_editor( id, note_text, deleted_from_id, actual_revision, actual_creation, read_write, true, false );
id = editor.id;
// if a link that launched this editor was provided, update it with the created note's id
@ -439,7 +448,7 @@ Wiki.prototype.parse_loaded_editor = function ( result, note_title, requested_re
link.href = "/notebooks/" + this.notebook_id + "?note_id=" + id;
}
Wiki.prototype.create_editor = function ( id, note_text, deleted_from_id, revision, read_write, highlight, focus ) {
Wiki.prototype.create_editor = function ( id, note_text, deleted_from_id, revision, creation, read_write, highlight, focus ) {
var self = this;
if ( isUndefinedOrNull( id ) ) {
if ( this.notebook.read_write ) {
@ -458,6 +467,11 @@ Wiki.prototype.create_editor = function ( id, note_text, deleted_from_id, revisi
note_text = "<p>Previous revision from " + short_revision + "</p>" + note_text;
}
if ( !read_write && creation ) {
var short_creation = this.brief_revision( creation );
note_text = "<p>" + short_creation + "</p>" + note_text;
}
var startup = this.startup_notes[ id ];
var editor = new Editor( id, this.notebook_id, note_text, deleted_from_id, revision, read_write, startup, highlight, focus );

View File

@ -3,6 +3,8 @@ from Tags import Div, Span, H4, A
class Link_area( Div ):
def __init__( self, notebooks, notebook, total_notes_count, parent_id ):
linked_notebooks = [ nb for nb in notebooks if nb.read_write and nb.name not in ( u"trash" ) ]
Div.__init__(
self,
Div(
@ -57,7 +59,7 @@ class Link_area( Div ):
),
Div(
( len( notebooks ) > 1 ) and H4( u"notebooks" ) or None,
( len( linked_notebooks ) > 0 ) and H4( u"notebooks" ) or None,
[ Span(
Div(
A(
@ -66,7 +68,7 @@ class Link_area( Div ):
id = u"notebook_%s" % nb.object_id,
),
class_ = ( nb.object_id == notebook.object_id ) and u"link_area_item current_notebook_name" or u"link_area_item",
) ) for nb in notebooks if nb.name not in ( u"Luminotes", u"trash" ) ],
) ) for nb in linked_notebooks ],
id = u"notebooks_area"
),

View File

@ -20,16 +20,16 @@ class Main_page( Page ):
logout_url = None,
startup_notes = None,
total_notes_count = None,
note = None,
notes = None,
note_read_write = True,
):
startup_note_ids = [ startup_note.object_id for startup_note in startup_notes ]
static_notes = Div(
note and Div(
notes and [ Div(
note.contents,
id = "static_note_%s" % note.object_id,
) or
) for note in notes ] or
[ Div(
startup_note.contents,
id = "static_note_%s" % startup_note.object_id
@ -46,12 +46,12 @@ class Main_page( Page ):
u"deleted_from_id" : startup_note.deleted_from_id,
} for startup_note in startup_notes ]
if note:
note_dict = {
u"object_id" : note.object_id,
u"revision" : note.revision,
u"deleted_from_id" : note.deleted_from_id,
}
note_dicts = [ {
u"object_id" : note.object_id,
u"revision" : note.revision,
u"deleted_from_id" : note.deleted_from_id,
u"creation" : note.creation,
} for note in notes ]
def json( string ):
return escape( unicode( Json( string ) ), quote = True )
@ -66,7 +66,7 @@ class Main_page( Page ):
Input( type = u"hidden", name = u"notebook_id", id = u"notebook_id", value = notebook.object_id ),
Input( type = u"hidden", name = u"parent_id", id = u"parent_id", value = parent_id or "" ),
Input( type = u"hidden", name = u"startup_notes", id = u"startup_notes", value = json( startup_note_dicts ) ),
Input( type = u"hidden", name = u"note", id = u"note", value = note and json( note_dict ) or "" ),
Input( type = u"hidden", name = u"current_notes", id = u"current_notes", value = json( note_dicts ) ),
Input( type = u"hidden", name = u"note_read_write", id = u"note_read_write", value = json( note_read_write ) ),
Div(
id = u"status_area",