Foundational work for both tags and discussion forums. Should have checked this in in smaller pieces.
This commit is contained in:
parent
86e5e38d69
commit
388f2fcb02
8
NEWS
8
NEWS
|
@ -1,3 +1,11 @@
|
|||
1.5.5:
|
||||
* Improved speed of Luminotes Desktop by adding some database indices. This
|
||||
will help in particular for larger notebooks with many notes.
|
||||
* Added some code to automatically upgrade your database when upgrading to a
|
||||
new Luminotes release. This applies to all Luminotes products.
|
||||
* Added code to support Luminotes discussion forums.
|
||||
* Laid some of the foundational groundwork for future tags support.
|
||||
|
||||
1.5.4: October 9, 2008
|
||||
* Fixed a visual bug in which clicking up or down to reorder your notebooks
|
||||
didn't display correctly.
|
||||
|
|
38
UPGRADE
38
UPGRADE
|
@ -1,18 +1,44 @@
|
|||
When upgrading Luminotes, if you are using memcached, it is recommended
|
||||
that you restart memcached to clear your cache.
|
||||
|
||||
|
||||
Upgrading from Luminotes 1.5.0 or higher
|
||||
----------------------------------------
|
||||
|
||||
If you're using Luminotes 1.5.0 or higher and you'd like to upgrade to a
|
||||
newer version, Luminotes will automatically upgrade your database when
|
||||
you start Luminotes after an upgrade. This means that all of your notes
|
||||
and notebooks created in an older versions of Luminotes will be included
|
||||
in the upgrade. You don't have to do a thing other than install the
|
||||
software for the new release, and then execute the following command:
|
||||
|
||||
export PYTHONPATH=.
|
||||
python2.4 tools/updatedb.py
|
||||
|
||||
|
||||
Upgrading from Luminotes 1.0, 1.2, 1.3, or 1.4
|
||||
----------------------------------------------
|
||||
|
||||
If you're using an older version of Luminotes (prior to 1.5.0) and you'd
|
||||
like to upgrade to a newer version, you'll have to perform database
|
||||
upgrades manually. Below are the intructions for doing so.
|
||||
|
||||
To upgrade the Luminotes database from an earlier version, manually apply each
|
||||
relevant schema delta file within model/delta/
|
||||
|
||||
For instance, if you were upgrading from version 5.0.1 to 5.0.4, you would
|
||||
For instance, if you are upgrading from version 1.3.12 to 1.5.0, you would
|
||||
apply the following deltas in order:
|
||||
|
||||
psql -U luminotes luminotes -f model/delta/5.0.2.sql
|
||||
psql -U luminotes luminotes -f model/delta/5.0.3.sql
|
||||
psql -U luminotes luminotes -f model/delta/5.0.4.sql
|
||||
psql -U luminotes luminotes -f model/delta/1.3.14.sql
|
||||
psql -U luminotes luminotes -f model/delta/1.4.0.sql
|
||||
psql -U luminotes luminotes -f model/delta/1.5.0.sql
|
||||
|
||||
Any version which does not introduce a schema change does not have a
|
||||
corresponding schema delta file.
|
||||
|
||||
Sometimes I include comments within a schema delta file with additional
|
||||
manual steps you need to take.
|
||||
IMPORTANT: Even if you are upgrading past version 1.5.0 to a newer version,
|
||||
you should stop applying schema delta files after 1.5.0. This is because the
|
||||
Luminotes automatic schema upgrade process will pick up after that point.
|
||||
|
||||
After you've updated the schema, run the updatedb.py script:
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
VERSION = u"1.5.4"
|
||||
VERSION = u"1.5.5"
|
||||
|
|
|
@ -290,7 +290,7 @@ class Files( object ):
|
|||
"""
|
||||
db_file = self.__database.load( File, file_id )
|
||||
|
||||
if not db_file or not self.__users.check_access( user_id, db_file.notebook_id ):
|
||||
if not db_file or not self.__users.load_notebook( user_id, db_file.notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
# if the file is openable as an image, then allow the user to view it instead of downloading it
|
||||
|
@ -396,7 +396,7 @@ class Files( object ):
|
|||
"""
|
||||
db_file = self.__database.load( File, file_id )
|
||||
|
||||
if not db_file or not self.__users.check_access( user_id, db_file.notebook_id ):
|
||||
if not db_file or not self.__users.load_notebook( user_id, db_file.notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
filename = db_file.filename.replace( '"', r"\"" )
|
||||
|
@ -432,7 +432,7 @@ class Files( object ):
|
|||
"""
|
||||
db_file = self.__database.load( File, file_id )
|
||||
|
||||
if not db_file or not self.__users.check_access( user_id, db_file.notebook_id ):
|
||||
if not db_file or not self.__users.load_notebook( user_id, db_file.notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
cherrypy.response.headerMap[ u"Content-Type" ] = u"image/png"
|
||||
|
@ -491,7 +491,7 @@ class Files( object ):
|
|||
"""
|
||||
db_file = self.__database.load( File, file_id )
|
||||
|
||||
if not db_file or not self.__users.check_access( user_id, db_file.notebook_id ):
|
||||
if not db_file or not self.__users.load_notebook( user_id, db_file.notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
cherrypy.response.headerMap[ u"Content-Type" ] = db_file.content_type
|
||||
|
@ -531,7 +531,7 @@ class Files( object ):
|
|||
@return: rendered HTML page
|
||||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id, read_write = True ):
|
||||
if not self.__users.load_notebook( user_id, notebook_id, read_write = True, note_id = note_id ):
|
||||
raise Access_error()
|
||||
|
||||
file_id = self.__database.next_id( File )
|
||||
|
@ -565,7 +565,7 @@ class Files( object ):
|
|||
@return: rendered HTML page
|
||||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id, read_write = True ):
|
||||
if not self.__users.load_notebook( user_id, notebook_id, read_write = True ):
|
||||
raise Access_error()
|
||||
|
||||
file_id = self.__database.next_id( File )
|
||||
|
@ -622,7 +622,7 @@ class Files( object ):
|
|||
current_uploads_lock.release()
|
||||
|
||||
user = self.__database.load( User, user_id )
|
||||
if not user or not self.__users.check_access( user_id, notebook_id, read_write = True ):
|
||||
if not user or not self.__users.load_notebook( user_id, notebook_id, read_write = True ):
|
||||
uploaded_file.delete()
|
||||
return dict( script = general_error_script % u"Sorry, you don't have access to do that. Please make sure you're logged in as the correct user." )
|
||||
|
||||
|
@ -739,7 +739,7 @@ class Files( object ):
|
|||
"""
|
||||
db_file = self.__database.load( File, file_id )
|
||||
|
||||
if not db_file or not self.__users.check_access( user_id, db_file.notebook_id ):
|
||||
if not db_file or not self.__users.load_notebook( user_id, db_file.notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
user = self.__database.load( User, user_id )
|
||||
|
@ -778,7 +778,7 @@ class Files( object ):
|
|||
"""
|
||||
db_file = self.__database.load( File, file_id )
|
||||
|
||||
if not db_file or not self.__users.check_access( user_id, db_file.notebook_id, read_write = True ):
|
||||
if not db_file or not self.__users.load_notebook( user_id, db_file.notebook_id, read_write = True ):
|
||||
raise Access_error()
|
||||
|
||||
self.__database.execute( db_file.sql_delete(), commit = False )
|
||||
|
@ -817,7 +817,7 @@ class Files( object ):
|
|||
"""
|
||||
db_file = self.__database.load( File, file_id )
|
||||
|
||||
if not db_file or not self.__users.check_access( user_id, db_file.notebook_id, read_write = True ):
|
||||
if not db_file or not self.__users.load_notebook( user_id, db_file.notebook_id, read_write = True ):
|
||||
raise Access_error()
|
||||
|
||||
db_file.filename = filename
|
||||
|
@ -919,7 +919,7 @@ class Files( object ):
|
|||
|
||||
db_file = self.__database.load( File, file_id )
|
||||
|
||||
if not db_file or not self.__users.check_access( user_id, db_file.notebook_id ):
|
||||
if not db_file or not self.__users.load_notebook( user_id, db_file.notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
parser = self.parse_csv( file_id )
|
||||
|
|
|
@ -1,26 +1,35 @@
|
|||
import cherrypy
|
||||
from model.User import User
|
||||
from model.Notebook import Notebook
|
||||
from Expose import expose
|
||||
from Validate import validate
|
||||
from Validate import validate, Valid_string
|
||||
from Database import Valid_id, end_transaction
|
||||
from Users import grab_user_id
|
||||
from Notebooks import Notebooks
|
||||
from view.Forums_page import Forums_page
|
||||
from view.Forum_page import Forum_page
|
||||
from view.Main_page import Main_page
|
||||
|
||||
|
||||
class Forums( object ):
|
||||
"""
|
||||
Controller for dealing with discussion forums, corresponding to the "/forums" URL.
|
||||
"""
|
||||
def __init__( self, database, users ):
|
||||
def __init__( self, database, notebooks, users ):
|
||||
"""
|
||||
Create a new Forums object.
|
||||
|
||||
@type database: controller.Database
|
||||
@param database: database that forums are stored in
|
||||
@type notebooks: controller.Users
|
||||
@param notebooks: controller for all notebooks
|
||||
@type users: controller.Users
|
||||
@param users: controller for all users
|
||||
@rtype: Forums
|
||||
@return: newly constructed Forums
|
||||
"""
|
||||
self.__database = database
|
||||
self.__notebooks = notebooks
|
||||
self.__users = users
|
||||
|
||||
@expose( view = Forums_page )
|
||||
|
@ -32,6 +41,9 @@ class Forums( object ):
|
|||
def index( self, user_id ):
|
||||
"""
|
||||
Provide the information necessary to display the listing of available forums (currently hard-coded).
|
||||
|
||||
@type user_id: unicode or NoneType
|
||||
@param user_id: id of the current user
|
||||
"""
|
||||
result = self.__users.current( user_id )
|
||||
parents = [ notebook for notebook in result[ u"notebooks" ] if notebook.trash_id and not notebook.deleted ]
|
||||
|
@ -41,3 +53,49 @@ class Forums( object ):
|
|||
result[ "first_notebook" ] = None
|
||||
|
||||
return result
|
||||
|
||||
@expose( view = Forum_page )
|
||||
@end_transaction
|
||||
@grab_user_id
|
||||
@validate(
|
||||
forum_name = Valid_string( max = 100 ),
|
||||
user_id = Valid_id( none_okay = True ),
|
||||
)
|
||||
def default( self, forum_name, user_id ):
|
||||
"""
|
||||
Provide the information necessary to display the current threads within a forum.
|
||||
|
||||
@type forum_name: unicode
|
||||
@param forum_name: name of the forum to display
|
||||
@type user_id: unicode or NoneType
|
||||
@param user_id: id of the current user
|
||||
"""
|
||||
result = self.__users.current( user_id )
|
||||
parents = [ notebook for notebook in result[ u"notebooks" ] if notebook.trash_id and not notebook.deleted ]
|
||||
if len( parents ) > 0:
|
||||
result[ "first_notebook" ] = parents[ 0 ]
|
||||
else:
|
||||
result[ "first_notebook" ] = None
|
||||
|
||||
anonymous = self.__database.select_one( User, User.sql_load_by_username( u"anonymous" ), use_cache = True )
|
||||
if anonymous is None:
|
||||
raise Access_error()
|
||||
|
||||
# TODO: this needs to sort by either thread/note creation or modification date
|
||||
threads = self.__database.select_many(
|
||||
Notebook,
|
||||
anonymous.sql_load_notebooks(
|
||||
parents_only = False, undeleted_only = True, tag_name = u"forum", tag_value = forum_name
|
||||
)
|
||||
)
|
||||
|
||||
# if there are no matching threads, then this forum doesn't exist
|
||||
if len( threads ) == 0:
|
||||
raise cherrypy.NotFound
|
||||
|
||||
result[ "forum_name" ] = forum_name
|
||||
result[ "threads" ] = threads
|
||||
return result
|
||||
|
||||
# threads() is just an alias for Notebooks.default()
|
||||
threads = Notebooks.default
|
||||
|
|
|
@ -135,7 +135,7 @@ class Notebooks( object ):
|
|||
]
|
||||
if len( result[ u"notebooks" ] ) == 0:
|
||||
raise Access_error()
|
||||
result[ u"notebooks" ][ 0 ].read_write = False
|
||||
result[ u"notebooks" ][ 0 ].read_write = Notebook.READ_ONLY
|
||||
result[ u"notebooks" ][ 0 ].owner = False
|
||||
elif preview in ( u"owner", u"default", None ):
|
||||
read_write = True
|
||||
|
@ -148,9 +148,8 @@ class Notebooks( object ):
|
|||
if revision:
|
||||
result[ "note_read_write" ] = False
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
notebook = result[ u"notebook" ]
|
||||
|
||||
if notebook.name != u"Luminotes":
|
||||
result[ "recent_notes" ] = self.__database.select_many( Note, notebook.sql_load_notes( start = 0, count = 10 ) )
|
||||
|
||||
|
@ -181,9 +180,11 @@ class Notebooks( object ):
|
|||
@type previous_revision: unicode or NoneType
|
||||
@param previous_revision: older revision timestamp to diff with the given revision (optional)
|
||||
@type read_write: bool or NoneType
|
||||
@param read_write: whether the notebook should be returned as read-write (optional, defaults to True)
|
||||
@param read_write: whether the notebook should be returned as read-write (optional, defaults to True).
|
||||
this can only lower access, not elevate it
|
||||
@type owner: bool or NoneType
|
||||
@param owner: whether the notebook should be returned as owner-level access (optional, defaults to True)
|
||||
@param owner: whether the notebook should be returned as owner-level access (optional, defaults to True).
|
||||
this can only lower access, not elevate it
|
||||
@type user_id: unicode or NoneType
|
||||
@param user_id: id of current logged-in user (if any)
|
||||
@rtype: dict
|
||||
|
@ -197,23 +198,16 @@ class Notebooks( object ):
|
|||
@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
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
if notebook is None:
|
||||
raise Access_error()
|
||||
|
||||
if read_write is False:
|
||||
notebook.read_write = False
|
||||
elif not self.__users.check_access( user_id, notebook_id, read_write = True ):
|
||||
notebook.read_write = False
|
||||
notebook.read_write = Notebook.READ_ONLY
|
||||
|
||||
if owner is False:
|
||||
notebook.owner = False
|
||||
elif not self.__users.check_access( user_id, notebook_id, owner = True ):
|
||||
notebook.owner = False
|
||||
|
||||
if note_id:
|
||||
note = self.__database.load( Note, note_id, revision )
|
||||
|
@ -234,7 +228,7 @@ class Notebooks( object ):
|
|||
startup_notes = self.__database.select_many( Note, notebook.sql_load_startup_notes() )
|
||||
total_notes_count = self.__database.select_one( int, notebook.sql_count_notes(), use_cache = True )
|
||||
|
||||
if self.__users.check_access( user_id, notebook_id, owner = True ):
|
||||
if self.__users.load_notebook( user_id, notebook_id, owner = True ):
|
||||
invites = self.__database.select_many( Invite, Invite.sql_load_notebook_invites( notebook_id ) )
|
||||
else:
|
||||
invites = []
|
||||
|
@ -350,7 +344,9 @@ class Notebooks( object ):
|
|||
@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
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
||||
note = self.__database.load( Note, note_id, revision )
|
||||
|
@ -362,8 +358,7 @@ class Notebooks( object ):
|
|||
)
|
||||
|
||||
if note and note.notebook_id != notebook_id:
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
if notebook and note.notebook_id == notebook.trash_id:
|
||||
if note.notebook_id == notebook.trash_id:
|
||||
if revision:
|
||||
return dict(
|
||||
note = summarize and self.summarize_note( note ) or note,
|
||||
|
@ -414,14 +409,11 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
|
||||
if notebook is None:
|
||||
note = None
|
||||
else:
|
||||
note = self.__database.select_one( Note, notebook.sql_load_note_by_title( note_title ) )
|
||||
|
||||
return dict(
|
||||
|
@ -520,14 +512,11 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
|
||||
if notebook is None:
|
||||
note = None
|
||||
else:
|
||||
note = self.__database.select_one( Note, notebook.sql_load_note_by_title( note_title ) )
|
||||
|
||||
return dict(
|
||||
|
@ -558,7 +547,9 @@ class Notebooks( object ):
|
|||
@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
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
||||
note = self.__database.load( Note, note_id )
|
||||
|
@ -570,8 +561,7 @@ class Notebooks( object ):
|
|||
)
|
||||
|
||||
if note.notebook_id != notebook_id:
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
if notebook and note.notebook_id == notebook.trash_id:
|
||||
if note.notebook_id == notebook.trash_id:
|
||||
return dict(
|
||||
revisions = None,
|
||||
)
|
||||
|
@ -610,10 +600,8 @@ class Notebooks( object ):
|
|||
@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
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
raise Access_error()
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
||||
|
@ -696,17 +684,19 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id, read_write = True ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id, read_write = True, note_id = note_id )
|
||||
user = self.__database.load( User, user_id )
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
|
||||
if not user or not notebook:
|
||||
raise Access_error()
|
||||
raise Access_error();
|
||||
|
||||
note = self.__database.load( Note, note_id )
|
||||
|
||||
# if the user has read-write access only to their own notes in this notebook, force the startup
|
||||
# flag to be True for this note
|
||||
if notebook.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES:
|
||||
startup = True
|
||||
|
||||
# check whether the provided note contents have been changed since the previous revision
|
||||
def update_note( current_notebook, old_note, startup, user ):
|
||||
# the note hasn't been changed, so bail without updating it
|
||||
|
@ -805,11 +795,8 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id, read_write = True ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id, read_write = True )
|
||||
user = self.__database.load( User, user_id )
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
|
||||
if not user or not notebook:
|
||||
raise Access_error()
|
||||
|
@ -819,6 +806,9 @@ class Notebooks( object ):
|
|||
if not note:
|
||||
raise Access_error()
|
||||
|
||||
if not self.__users.load_notebook( user_id, note.notebook_id, read_write = True, note_id = note.object_id ):
|
||||
raise Access_error()
|
||||
|
||||
# check whether the provided note contents have been changed since the previous revision
|
||||
def update_note( current_notebook, old_note, user ):
|
||||
# if the revision to revert to is already the newest revision, bail without updating the note
|
||||
|
@ -895,10 +885,7 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id, read_write = True ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id, read_write = True, note_id = note_id )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
@ -949,10 +936,7 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id, read_write = True ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id, read_write = True, note_id = note_id )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
@ -1005,12 +989,9 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id, read_write = True ):
|
||||
raise Access_error()
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id, read_write = True )
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
if not notebook or notebook.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES:
|
||||
raise Access_error()
|
||||
|
||||
notes = self.__database.select_many( Note, notebook.sql_load_notes() )
|
||||
|
@ -1063,7 +1044,9 @@ class Notebooks( object ):
|
|||
@raise Validation_error: one of the arguments is invalid
|
||||
@raise Search_error: the provided search_text is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
||||
MAX_SEARCH_TEXT_LENGTH = 256
|
||||
|
@ -1112,14 +1095,19 @@ class Notebooks( object ):
|
|||
@raise Validation_error: one of the arguments is invalid
|
||||
@raise Search_error: the provided search_text is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
# if the anonymous user has access to the given notebook, then run the search as the anonymous
|
||||
# user instead of the given user id
|
||||
if self.__users.check_access( user_id = None, notebook_id = notebook_id ) is True:
|
||||
anonymous = self.__database.select_one( User, User.sql_load_by_username( u"anonymous" ), use_cache = True )
|
||||
if not anonymous:
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__users.load_notebook( anonymous.object_id, notebook_id )
|
||||
if notebook:
|
||||
user_id = anonymous.object_id
|
||||
else:
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
||||
MAX_SEARCH_TEXT_LENGTH = 256
|
||||
if len( search_text ) > MAX_SEARCH_TEXT_LENGTH:
|
||||
|
@ -1162,10 +1150,7 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
@ -1197,10 +1182,7 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
@ -1234,10 +1216,7 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
@ -1346,13 +1325,10 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id, read_write = True, owner = True )
|
||||
user = self.__database.load( User, user_id )
|
||||
if not self.__users.check_access( user_id, notebook_id, read_write = True, owner = True ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
if not user or not notebook:
|
||||
raise Access_error()
|
||||
|
||||
# prevent renaming of the trash notebook to anything
|
||||
|
@ -1399,14 +1375,10 @@ class Notebooks( object ):
|
|||
if user_id is None:
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id, read_write = True, owner = True )
|
||||
user = self.__database.load( User, user_id )
|
||||
|
||||
if not self.__users.check_access( user_id, notebook_id, read_write = True, owner = True ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
if not user or not notebook:
|
||||
raise Access_error()
|
||||
|
||||
# prevent deletion of a trash notebook directly
|
||||
|
@ -1454,14 +1426,10 @@ class Notebooks( object ):
|
|||
if user_id is None:
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id, read_write = True, owner = True )
|
||||
user = self.__database.load( User, user_id )
|
||||
|
||||
if not self.__users.check_access( user_id, notebook_id, read_write = True, owner = True ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
|
||||
if not notebook:
|
||||
if not user or not notebook:
|
||||
raise Access_error()
|
||||
|
||||
# prevent deletion of a trash notebook directly
|
||||
|
@ -1498,10 +1466,7 @@ class Notebooks( object ):
|
|||
if user_id is None:
|
||||
raise Access_error()
|
||||
|
||||
if not self.__users.check_access( user_id, notebook_id, read_write = True, owner = True ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id, read_write = True, owner = True )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
@ -1537,11 +1502,10 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
user = self.__database.load( User, user_id )
|
||||
if not user:
|
||||
|
||||
if not user or not notebook:
|
||||
raise Access_error()
|
||||
|
||||
# load the notebooks to which this user has access
|
||||
|
@ -1609,11 +1573,10 @@ class Notebooks( object ):
|
|||
@raise Access_error: the current user doesn't have access to the given notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if not self.__users.check_access( user_id, notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
user = self.__database.load( User, user_id )
|
||||
if not user:
|
||||
|
||||
if not user or not notebook:
|
||||
raise Access_error()
|
||||
|
||||
# load the notebooks to which this user has access
|
||||
|
@ -1676,7 +1639,6 @@ class Notebooks( object ):
|
|||
Provide the information necessary to display a notebook's recent updated/created notes, in
|
||||
reverse chronological order by update time.
|
||||
|
||||
|
||||
@type notebook_id: unicode
|
||||
@param notebook_id: id of the notebook containing the notes
|
||||
@type start: unicode or NoneType
|
||||
|
@ -1689,10 +1651,7 @@ class Notebooks( object ):
|
|||
@return: { 'notes': recent_notes_list }
|
||||
@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 )
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
if notebook is None:
|
||||
raise Access_error()
|
||||
|
@ -1720,10 +1679,7 @@ class Notebooks( object ):
|
|||
@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 )
|
||||
notebook = self.__users.load_notebook( user_id, notebook_id )
|
||||
|
||||
if notebook is None:
|
||||
raise Access_error()
|
||||
|
@ -1798,7 +1754,7 @@ class Notebooks( object ):
|
|||
raise Access_error()
|
||||
|
||||
db_file = self.__database.load( File, file_id )
|
||||
if db_file is None or not self.__users.check_access( user_id, db_file.notebook_id ):
|
||||
if db_file is None or not self.__users.load_notebook( user_id, db_file.notebook_id ):
|
||||
raise Access_error()
|
||||
|
||||
# if the file has a "note_id" header column, record its index
|
||||
|
|
|
@ -58,7 +58,7 @@ class Root( object ):
|
|||
settings[ u"global" ].get( u"luminotes.download_products", [] ),
|
||||
)
|
||||
self.__notebooks = Notebooks( database, self.__users, self.__files, settings[ u"global" ].get( u"luminotes.https_url", u"" ) )
|
||||
self.__forums = Forums( database, self.__users )
|
||||
self.__forums = Forums( database, self.__notebooks, self.__users )
|
||||
self.__suppress_exceptions = suppress_exceptions # used for unit tests
|
||||
|
||||
@expose( Main_page )
|
||||
|
@ -486,4 +486,4 @@ class Root( object ):
|
|||
users = property( lambda self: self.__users )
|
||||
groups = property( lambda self: self.__groups )
|
||||
files = property( lambda self: self.__files )
|
||||
# forums = property( lambda self: self.__forums )
|
||||
forums = property( lambda self: self.__forums )
|
||||
|
|
|
@ -702,34 +702,62 @@ class Users( object ):
|
|||
|
||||
return user
|
||||
|
||||
def check_access( self, user_id, notebook_id, read_write = False, owner = False ):
|
||||
def load_notebook( self, user_id, notebook_id, read_write = False, owner = False, note_id = None ):
|
||||
"""
|
||||
Determine whether the given user has access to the given notebook.
|
||||
Determine whether the given user has access to the given notebook, and if so, return that
|
||||
notebook.
|
||||
|
||||
If the notebook.read_write member is READ_WRITE_FOR_OWN_NOTES, and a particular note_id is
|
||||
given, then make sure that the given note_id is one of the user's own notes.
|
||||
|
||||
@type user_id: unicode
|
||||
@param user_id: id of user whose access to check
|
||||
@type notebook_id: unicode
|
||||
@param notebook_id: id of notebook to check access for
|
||||
@type read_write: bool
|
||||
@param read_write: True if read-write access is being checked, False if read-only access (defaults to False)
|
||||
@type read_write: boolean
|
||||
@param read_write: True if the notebook must be READ_WRITE or READ_WRITE_FOR_OWN_NOTES,
|
||||
False if read-write access is not to be checked (defaults to False)
|
||||
@type owner: bool
|
||||
@param owner: True if owner-level access is being checked (defaults to False)
|
||||
@rtype: bool
|
||||
@return: True if the user has access
|
||||
@type note_id: unicode
|
||||
@param note_id: id of the note in the given notebook that the user is trying to access.
|
||||
if the notebook is READ_WRITE_FOR_OWN_NOTES, then the given note is checked
|
||||
to make sure its user_id is the same as the given user_id. for READ_WRITE
|
||||
and READ_ONLY notebooks, this note_id parameter is ignored
|
||||
@rtype: Notebook or NoneType
|
||||
@return: the loaded notebook if the user has access to it, None otherwise
|
||||
"""
|
||||
anonymous = self.__database.select_one( User, User.sql_load_by_username( u"anonymous" ), use_cache = True )
|
||||
notebook = self.__database.select_one( Notebook, anonymous.sql_load_notebooks( notebook_id = notebook_id ) )
|
||||
|
||||
if self.__database.select_one( bool, anonymous.sql_has_access( notebook_id, read_write, owner ) ):
|
||||
return True
|
||||
|
||||
if user_id:
|
||||
# check if the given user has access to this notebook
|
||||
if not notebook and user_id:
|
||||
user = self.__database.load( User, user_id )
|
||||
if not user:
|
||||
return None
|
||||
|
||||
if user and self.__database.select_one( bool, user.sql_has_access( notebook_id, read_write, owner ) ):
|
||||
return True
|
||||
notebook = self.__database.select_one( Notebook, user.sql_load_notebooks( notebook_id = notebook_id ) )
|
||||
|
||||
return False
|
||||
# if the user has no access to this notebook, bail
|
||||
if notebook is None:
|
||||
return None
|
||||
|
||||
if read_write and notebook.read_write == Notebook.READ_ONLY:
|
||||
return None
|
||||
|
||||
if owner and not notebook.owner:
|
||||
return None
|
||||
|
||||
# if a particular note_id is given, and the notebook is READ_WRITE_FOR_OWN_NOTES, then check
|
||||
# that the user is associated with that note
|
||||
if note_id and notebook.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES:
|
||||
note = self.__database.load( Note, note_id )
|
||||
if not note:
|
||||
return None
|
||||
|
||||
if user_id != note.user_id or notebook_id != note.notebook_id:
|
||||
return None
|
||||
|
||||
return notebook
|
||||
|
||||
def check_group( self, user_id, group_id, admin = False ):
|
||||
"""
|
||||
|
@ -1006,7 +1034,9 @@ class Users( object ):
|
|||
if len( email_addresses ) > 5000:
|
||||
raise Invite_error( u"Please enter fewer email addresses." )
|
||||
|
||||
if not self.check_access( user_id, notebook_id, read_write = True, owner = True ):
|
||||
notebook = self.load_notebook( user_id, notebook_id, read_write = True, owner = True )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
||||
# except for viewer-only invites, this feature requires a rate plan above basic
|
||||
|
@ -1027,10 +1057,6 @@ class Users( object ):
|
|||
else:
|
||||
raise Access_error()
|
||||
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
if notebook is None:
|
||||
raise Access_error()
|
||||
|
||||
# parse email_addresses string into individual email addresses
|
||||
email_addresses_list = set()
|
||||
for piece in WHITESPACE_OR_COMMA_PATTERN.split( email_addresses ):
|
||||
|
@ -1136,12 +1162,13 @@ class Users( object ):
|
|||
@raise Validation_error: one of the arguments is invalid
|
||||
@raise Access_error: user_id doesn't have owner-level notebook access to revoke an invite
|
||||
"""
|
||||
if not self.check_access( user_id, notebook_id, read_write = True, owner = True ):
|
||||
notebook = self.load_notebook( user_id, notebook_id, read_write = True, owner = True )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
||||
invite = self.__database.load( Invite, invite_id )
|
||||
notebook = self.__database.load( Notebook, notebook_id )
|
||||
if not notebook or not invite or not invite.email_address or invite.notebook_id != notebook_id:
|
||||
if not invite or not invite.email_address or invite.notebook_id != notebook_id:
|
||||
raise Access_error()
|
||||
|
||||
self.__database.execute(
|
||||
|
|
|
@ -10,7 +10,7 @@ from controller.Database import Database, Connection_wrapper
|
|||
|
||||
class Test_database( object ):
|
||||
def setUp( self ):
|
||||
# make an in-memory sqlite database to use in place of PostgreSQL during testing
|
||||
# make an in-memory sqlite database to use during testing
|
||||
self.connection = Connection_wrapper( sqlite.connect( ":memory:", detect_types = sqlite.PARSE_DECLTYPES, check_same_thread = False ) )
|
||||
self.cache = Stub_cache()
|
||||
cursor = self.connection.cursor()
|
||||
|
|
|
@ -175,13 +175,13 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"user" ).object_id == self.user.object_id
|
||||
assert len( result.get( u"notebooks" ) ) == 3
|
||||
assert result.get( u"notebooks" )[ 2 ].object_id == self.notebook.object_id
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == True
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebooks" )[ 2 ].owner == True
|
||||
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 result.get( u"notebook" ).read_write == True
|
||||
assert result.get( u"notebook" ).read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebook" ).owner == True
|
||||
assert len( result.get( u"startup_notes" ) ) == 1
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
|
@ -211,13 +211,13 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"user" ).object_id == self.user.object_id
|
||||
assert len( result.get( u"notebooks" ) ) == 1
|
||||
assert result.get( u"notebooks" )[ 0 ].object_id == self.notebook.object_id
|
||||
assert result.get( u"notebooks" )[ 0 ].read_write == False
|
||||
assert result.get( u"notebooks" )[ 0 ].read_write == Notebook.READ_ONLY
|
||||
assert result.get( u"notebooks" )[ 0 ].owner == False
|
||||
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 result.get( u"notebook" ).read_write == False
|
||||
assert result.get( u"notebook" ).read_write == Notebook.READ_ONLY
|
||||
assert result.get( u"notebook" ).owner == False
|
||||
assert len( result.get( u"startup_notes" ) ) == 1
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
|
@ -247,13 +247,13 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"user" ).object_id == self.user.object_id
|
||||
assert len( result.get( u"notebooks" ) ) == 1
|
||||
assert result.get( u"notebooks" )[ 0 ].object_id == self.notebook.object_id
|
||||
assert result.get( u"notebooks" )[ 0 ].read_write == True
|
||||
assert result.get( u"notebooks" )[ 0 ].read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebooks" )[ 0 ].owner == False
|
||||
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 result.get( u"notebook" ).read_write == True
|
||||
assert result.get( u"notebook" ).read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebook" ).owner == False
|
||||
assert len( result.get( u"startup_notes" ) ) == 1
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
|
@ -283,13 +283,13 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"user" ).object_id == self.user.object_id
|
||||
assert len( result.get( u"notebooks" ) ) == 3
|
||||
assert result.get( u"notebooks" )[ 2 ].object_id == self.notebook.object_id
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == True
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebooks" )[ 2 ].owner == True
|
||||
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 result.get( u"notebook" ).read_write == True
|
||||
assert result.get( u"notebook" ).read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebook" ).owner == True
|
||||
assert len( result.get( u"startup_notes" ) ) == 1
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
|
@ -319,13 +319,13 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"user" ).object_id == self.user.object_id
|
||||
assert len( result.get( u"notebooks" ) ) == 1
|
||||
assert result.get( u"notebooks" )[ 0 ].object_id == self.anon_notebook.object_id
|
||||
assert result.get( u"notebooks" )[ 0 ].read_write == False
|
||||
assert result.get( u"notebooks" )[ 0 ].read_write == Notebook.READ_ONLY
|
||||
assert result.get( u"notebooks" )[ 0 ].owner == False
|
||||
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.anon_notebook.object_id
|
||||
assert result.get( u"notebook" ).read_write == False
|
||||
assert result.get( u"notebook" ).read_write == Notebook.READ_ONLY
|
||||
assert result.get( u"notebook" ).owner == False
|
||||
assert len( result.get( u"startup_notes" ) ) == 0
|
||||
assert result[ "total_notes_count" ] == 0
|
||||
|
@ -351,13 +351,13 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"user" ).object_id == self.user.object_id
|
||||
assert len( result.get( u"notebooks" ) ) == 1
|
||||
assert result.get( u"notebooks" )[ 0 ].object_id == self.anon_notebook.object_id
|
||||
assert result.get( u"notebooks" )[ 0 ].read_write == False
|
||||
assert result.get( u"notebooks" )[ 0 ].read_write == Notebook.READ_ONLY
|
||||
assert result.get( u"notebooks" )[ 0 ].owner == False
|
||||
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.anon_notebook.object_id
|
||||
assert result.get( u"notebook" ).read_write == False
|
||||
assert result.get( u"notebook" ).read_write == Notebook.READ_ONLY
|
||||
assert result.get( u"notebook" ).owner == False
|
||||
assert len( result.get( u"startup_notes" ) ) == 0
|
||||
assert result[ "total_notes_count" ] == 0
|
||||
|
@ -380,14 +380,18 @@ class Test_notebooks( Test_controller ):
|
|||
|
||||
assert result.get( u"user" ).object_id == self.user.object_id
|
||||
assert len( result.get( u"notebooks" ) ) == 3
|
||||
assert result.get( u"notebooks" )[ 1 ].object_id == self.anon_notebook.object_id
|
||||
assert result.get( u"notebooks" )[ 1 ].read_write == False
|
||||
assert result.get( u"notebooks" )[ 1 ].owner == False
|
||||
notebook = result[ u"notebooks" ][ 0 ]
|
||||
if notebook.name == u"trash":
|
||||
notebook = result[ u"notebooks" ][ 1 ]
|
||||
|
||||
assert notebook.object_id == self.anon_notebook.object_id
|
||||
assert notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == False
|
||||
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.anon_notebook.object_id
|
||||
assert result.get( u"notebook" ).read_write == False
|
||||
assert result.get( u"notebook" ).read_write == Notebook.READ_ONLY
|
||||
assert result.get( u"notebook" ).owner == False
|
||||
assert len( result.get( u"startup_notes" ) ) == 0
|
||||
assert result[ "total_notes_count" ] == 0
|
||||
|
@ -468,13 +472,13 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"user" ).object_id == self.user.object_id
|
||||
assert len( result.get( u"notebooks" ) ) == 3
|
||||
assert result.get( u"notebooks" )[ 2 ].object_id == self.notebook.object_id
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == True
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebooks" )[ 2 ].owner == True
|
||||
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 result.get( u"notebook" ).read_write == True
|
||||
assert result.get( u"notebook" ).read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebook" ).owner == True
|
||||
assert len( result.get( u"startup_notes" ) ) == 1
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
|
@ -511,13 +515,13 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"user" ).object_id == self.user.object_id
|
||||
assert len( result.get( u"notebooks" ) ) == 3
|
||||
assert result.get( u"notebooks" )[ 2 ].object_id == self.notebook.object_id
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == True
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebooks" )[ 2 ].owner == True
|
||||
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 result.get( u"notebook" ).read_write == True
|
||||
assert result.get( u"notebook" ).read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebook" ).owner == True
|
||||
assert len( result.get( u"startup_notes" ) ) == 1
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
|
@ -560,13 +564,13 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"user" ).object_id == self.user.object_id
|
||||
assert len( result.get( u"notebooks" ) ) == 3
|
||||
assert result.get( u"notebooks" )[ 2 ].object_id == self.notebook.object_id
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == True
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebooks" )[ 2 ].owner == True
|
||||
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 result.get( u"notebook" ).read_write == True
|
||||
assert result.get( u"notebook" ).read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebook" ).owner == True
|
||||
assert len( result.get( u"startup_notes" ) ) == 1
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
|
@ -602,13 +606,13 @@ class Test_notebooks( Test_controller ):
|
|||
assert result.get( u"user" ).object_id == self.user.object_id
|
||||
assert len( result.get( u"notebooks" ) ) == 3
|
||||
assert result.get( u"notebooks" )[ 2 ].object_id == self.notebook.object_id
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == True
|
||||
assert result.get( u"notebooks" )[ 2 ].read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebooks" )[ 2 ].owner == True
|
||||
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 result.get( u"notebook" ).read_write == True
|
||||
assert result.get( u"notebook" ).read_write == Notebook.READ_WRITE
|
||||
assert result.get( u"notebook" ).owner == True
|
||||
assert len( result.get( u"startup_notes" ) ) == 1
|
||||
assert result[ "total_notes_count" ] == 2
|
||||
|
@ -644,7 +648,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert len( startup_notes ) == 1
|
||||
assert startup_notes[ 0 ].object_id == self.note.object_id
|
||||
|
@ -669,7 +673,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == False
|
||||
assert notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == True
|
||||
assert len( startup_notes ) == 1
|
||||
assert startup_notes[ 0 ].object_id == self.note.object_id
|
||||
|
@ -694,7 +698,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == False
|
||||
assert len( startup_notes ) == 1
|
||||
assert startup_notes[ 0 ].object_id == self.note.object_id
|
||||
|
@ -718,7 +722,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert len( startup_notes ) == 1
|
||||
assert startup_notes[ 0 ].object_id == self.note.object_id
|
||||
|
@ -751,7 +755,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert len( startup_notes ) == 1
|
||||
assert startup_notes[ 0 ].object_id == self.note.object_id
|
||||
|
@ -790,7 +794,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert invite.object_id == self.invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert len( startup_notes ) == 1
|
||||
assert startup_notes[ 0 ].object_id == self.note.object_id
|
||||
|
@ -830,7 +834,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert invites[ 1 ].object_id == invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert len( startup_notes ) == 1
|
||||
assert startup_notes[ 0 ].object_id == self.note.object_id
|
||||
|
@ -861,7 +865,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert invites[ 1 ].object_id == invite.object_id
|
||||
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert len( startup_notes ) == 1
|
||||
assert startup_notes[ 0 ].object_id == self.note.object_id
|
||||
|
@ -901,7 +905,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert result[ "invites" ] == []
|
||||
|
||||
assert notebook.object_id == self.anon_notebook.object_id
|
||||
assert notebook.read_write == False
|
||||
assert notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == False
|
||||
assert len( startup_notes ) == 0
|
||||
user = self.database.load( User, self.user.object_id )
|
||||
|
@ -1727,6 +1731,72 @@ class Test_notebooks( Test_controller ):
|
|||
def test_save_startup_note( self ):
|
||||
self.test_save_note( startup = True )
|
||||
|
||||
def test_save_note_in_notebook_with_read_write_for_own_notes( self ):
|
||||
self.login()
|
||||
|
||||
self.database.execute( self.user.sql_update_access(
|
||||
self.notebook.object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES, owner = True,
|
||||
) )
|
||||
|
||||
previous_revision = self.note.revision
|
||||
new_note_contents = u"<h3>new title</h3>new blah"
|
||||
result = self.http_post( "/notebooks/save_note/", dict(
|
||||
notebook_id = self.notebook.object_id,
|
||||
note_id = self.note.object_id,
|
||||
contents = new_note_contents,
|
||||
startup = False,
|
||||
previous_revision = previous_revision,
|
||||
), session_id = self.session_id )
|
||||
|
||||
assert result[ "new_revision" ]
|
||||
assert result[ "new_revision" ].revision != previous_revision
|
||||
assert result[ "new_revision" ].user_id == self.user.object_id
|
||||
assert result[ "new_revision" ].username == self.username
|
||||
current_revision = result[ "new_revision" ].revision
|
||||
assert result[ "previous_revision" ].revision == previous_revision
|
||||
assert result[ "previous_revision" ].user_id == self.user.object_id
|
||||
assert result[ "previous_revision" ].username == self.username
|
||||
|
||||
# make sure the old title can no longer be loaded
|
||||
result = self.http_post( "/notebooks/load_note_by_title/", dict(
|
||||
notebook_id = self.notebook.object_id,
|
||||
note_title = "my title",
|
||||
), session_id = self.session_id )
|
||||
|
||||
note = result[ "note" ]
|
||||
assert note == None
|
||||
|
||||
# make sure the new title is now loadable
|
||||
result = self.http_post( "/notebooks/load_note_by_title/", dict(
|
||||
notebook_id = self.notebook.object_id,
|
||||
note_title = "new title",
|
||||
), session_id = self.session_id )
|
||||
|
||||
note = result[ "note" ]
|
||||
|
||||
assert note.object_id == self.note.object_id
|
||||
assert note.title == "new title"
|
||||
assert note.contents == new_note_contents
|
||||
assert note.startup == True # startup is forced to True in READ_WRITE_FOR_OWN_NOTES notebook
|
||||
assert note.user_id == self.user.object_id
|
||||
assert note.rank == 0
|
||||
|
||||
# make sure that the correct revisions are returned and are in chronological order
|
||||
result = self.http_post( "/notebooks/load_note_revisions/", dict(
|
||||
notebook_id = self.notebook.object_id,
|
||||
note_id = self.note.object_id,
|
||||
), session_id = self.session_id )
|
||||
|
||||
revisions = result[ "revisions" ]
|
||||
assert revisions != None
|
||||
assert len( revisions ) == 3
|
||||
assert revisions[ 1 ].revision == previous_revision
|
||||
assert revisions[ 1 ].user_id == self.user.object_id
|
||||
assert revisions[ 1 ].username == self.username
|
||||
assert revisions[ 2 ].revision == current_revision
|
||||
assert revisions[ 2 ].user_id == self.user.object_id
|
||||
assert revisions[ 2 ].username == self.username
|
||||
|
||||
def test_save_note_by_different_user( self, startup = False ):
|
||||
self.login2()
|
||||
|
||||
|
@ -1796,6 +1866,27 @@ class Test_notebooks( Test_controller ):
|
|||
assert revisions[ 2 ].user_id == self.user2.object_id
|
||||
assert revisions[ 2 ].username == self.username2
|
||||
|
||||
def test_save_note_by_different_user_with_notebook_read_write_for_own_notes( self ):
|
||||
self.login2()
|
||||
|
||||
self.database.execute( self.user2.sql_update_access(
|
||||
self.notebook.object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES, owner = True,
|
||||
) )
|
||||
|
||||
previous_revision = self.note.revision
|
||||
new_note_contents = u"<h3>new title</h3>new blah"
|
||||
result = self.http_post( "/notebooks/save_note/", dict(
|
||||
notebook_id = self.notebook.object_id,
|
||||
note_id = self.note.object_id,
|
||||
contents = new_note_contents,
|
||||
startup = False,
|
||||
previous_revision = previous_revision,
|
||||
), session_id = self.session_id )
|
||||
|
||||
assert result.get( "error" )
|
||||
user = self.database.load( User, self.user.object_id )
|
||||
assert user.storage_bytes == 0
|
||||
|
||||
def test_save_note_without_login( self, startup = False ):
|
||||
# save over an existing note supplying new contents and a new title
|
||||
previous_revision = self.note.revision
|
||||
|
@ -3688,7 +3779,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert isinstance( notebook, Notebook )
|
||||
assert notebook.object_id == new_notebook_id
|
||||
assert notebook.name == u"new notebook"
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.trash_id
|
||||
|
||||
|
@ -3715,7 +3806,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert result[ "invites" ] == []
|
||||
|
||||
assert notebook.object_id == new_notebook_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
|
||||
def test_create_without_login( self ):
|
||||
|
@ -3810,7 +3901,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert isinstance( notebook, Notebook )
|
||||
assert notebook.object_id == remaining_notebook_id
|
||||
assert notebook.name == u"my notebook"
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.trash_id
|
||||
assert notebook.user_id == self.user.object_id
|
||||
|
@ -3864,7 +3955,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert isinstance( notebook, Notebook )
|
||||
assert notebook.object_id == remaining_notebook_id
|
||||
assert notebook.name == u"my notebook"
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.trash_id
|
||||
assert notebook.user_id == self.user.object_id
|
||||
|
@ -4009,7 +4100,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert isinstance( notebook, Notebook )
|
||||
assert notebook.object_id == notebook_id
|
||||
assert notebook.name == self.notebook.name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.trash_id
|
||||
assert notebook.user_id == self.user.object_id
|
||||
|
@ -4069,7 +4160,7 @@ class Test_notebooks( Test_controller ):
|
|||
assert isinstance( notebook, Notebook )
|
||||
assert notebook.object_id == notebook_id
|
||||
assert notebook.name == self.notebook.name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.trash_id
|
||||
assert notebook.user_id == self.user.object_id
|
||||
|
@ -4511,7 +4602,7 @@ class Test_notebooks( Test_controller ):
|
|||
|
||||
assert notebook.name == u"imported notebook"
|
||||
assert notebook.trash_id
|
||||
assert notebook.read_write is True
|
||||
assert notebook.read_write is Notebook.READ_WRITE
|
||||
assert notebook.owner is True
|
||||
assert notebook.deleted is False
|
||||
assert notebook.user_id == self.user.object_id
|
||||
|
|
|
@ -62,10 +62,10 @@ 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, read_write = False, owner = False ) )
|
||||
self.database.execute( self.anonymous.sql_save_notebook( self.blog_notebook.object_id, read_write = False, owner = False ) )
|
||||
self.database.execute( self.anonymous.sql_save_notebook( self.guide_notebook.object_id, read_write = False, owner = False ) )
|
||||
self.database.execute( self.anonymous.sql_save_notebook( self.privacy_notebook.object_id, read_write = False, owner = False ) )
|
||||
self.database.execute( self.anonymous.sql_save_notebook( self.anon_notebook.object_id, read_write = False, owner = False, rank = 0 ) )
|
||||
self.database.execute( self.anonymous.sql_save_notebook( self.blog_notebook.object_id, read_write = False, owner = False, rank = 1 ) )
|
||||
self.database.execute( self.anonymous.sql_save_notebook( self.guide_notebook.object_id, read_write = False, owner = False, rank = 2 ) )
|
||||
self.database.execute( self.anonymous.sql_save_notebook( self.privacy_notebook.object_id, read_write = False, owner = False, rank = 3 ) )
|
||||
|
||||
def test_index( self ):
|
||||
result = self.http_get( "/" )
|
||||
|
@ -429,7 +429,7 @@ class Test_root( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.anon_notebook.object_id ][ 0 ]
|
||||
assert notebook.object_id == self.anon_notebook.object_id
|
||||
assert notebook.name == self.anon_notebook.name
|
||||
assert notebook.read_write == False
|
||||
assert notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == False
|
||||
|
||||
rate_plan = result[ u"rate_plan" ]
|
||||
|
@ -451,7 +451,7 @@ class Test_root( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebook.object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebook.object_id
|
||||
assert notebook.name == self.notebook.name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
|
||||
rate_plan = result[ u"rate_plan" ]
|
||||
|
|
|
@ -62,21 +62,23 @@ class Test_users( Test_controller ):
|
|||
self.database.save( self.notebooks[ 0 ] )
|
||||
self.database.save( self.notebooks[ 1 ] )
|
||||
|
||||
self.user = User.create( self.database.next_id( User ), self.username, self.password, self.email_address )
|
||||
self.database.save( self.user, commit = False )
|
||||
|
||||
self.anon_notebook = Notebook.create( self.database.next_id( Notebook ), u"anon notebook" )
|
||||
self.database.save( self.anon_notebook )
|
||||
self.startup_note = Note.create(
|
||||
self.database.next_id( Note ), u"<h3>login</h3>",
|
||||
notebook_id = self.anon_notebook.object_id, startup = True,
|
||||
user_id = self.user.object_id,
|
||||
)
|
||||
self.database.save( self.startup_note )
|
||||
self.database.save( self.startup_note, commit = False )
|
||||
|
||||
self.group = Group.create( self.database.next_id( Group ), u"my group" )
|
||||
self.database.save( self.group, commit = False )
|
||||
self.group2 = Group.create( self.database.next_id( Group ), u"other group" )
|
||||
self.database.save( self.group2, commit = False )
|
||||
|
||||
self.user = User.create( self.database.next_id( User ), self.username, self.password, self.email_address )
|
||||
self.database.save( self.user, commit = False )
|
||||
self.database.execute( self.user.sql_save_notebook( notebook_id1, read_write = True, owner = True, rank = 0 ), commit = False )
|
||||
self.database.execute( self.user.sql_save_notebook( trash_id1, read_write = True, owner = True ), commit = False )
|
||||
self.database.execute( self.user.sql_save_notebook( notebook_id2, read_write = True, owner = True, rank = 1 ), commit = False )
|
||||
|
@ -180,7 +182,7 @@ class Test_users( Test_controller ):
|
|||
assert notebook.revision
|
||||
assert notebook.name == u"trash"
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == None
|
||||
|
||||
|
@ -189,7 +191,7 @@ class Test_users( Test_controller ):
|
|||
assert notebook.revision
|
||||
assert notebook.name == u"my notebook"
|
||||
assert notebook.trash_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -198,7 +200,7 @@ class Test_users( Test_controller ):
|
|||
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 notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == False
|
||||
assert notebook.rank == None
|
||||
|
||||
|
@ -250,8 +252,8 @@ class Test_users( Test_controller ):
|
|||
assert result[ u"user" ].username == self.new_username
|
||||
assert result[ u"user" ].email_address == self.new_email_address
|
||||
|
||||
assert cherrypy.root.users.check_access( user.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.check_access( user.object_id, self.notebooks[ 0 ].trash_id )
|
||||
assert cherrypy.root.users.load_notebook( user.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.load_notebook( user.object_id, self.notebooks[ 0 ].trash_id )
|
||||
|
||||
# the notebook that the user was invited to should be in the list of returned notebooks
|
||||
notebooks = dict( [ ( notebook.object_id, notebook ) for notebook in result[ u"notebooks" ] ] )
|
||||
|
@ -261,7 +263,7 @@ class Test_users( Test_controller ):
|
|||
assert notebook.revision
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.trash_id
|
||||
assert notebook.read_write == False
|
||||
assert notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == False
|
||||
assert notebook.rank == 1
|
||||
|
||||
|
@ -269,7 +271,7 @@ class Test_users( Test_controller ):
|
|||
assert notebook.revision
|
||||
assert notebook.name == u"trash"
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.read_write == False
|
||||
assert notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == False
|
||||
assert notebook.rank == None
|
||||
|
||||
|
@ -277,7 +279,7 @@ class Test_users( Test_controller ):
|
|||
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 notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == False
|
||||
assert notebook.rank == None
|
||||
|
||||
|
@ -317,7 +319,7 @@ class Test_users( Test_controller ):
|
|||
assert notebook.revision
|
||||
assert notebook.name == u"trash"
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == None
|
||||
|
||||
|
@ -326,7 +328,7 @@ class Test_users( Test_controller ):
|
|||
assert notebook.revision
|
||||
assert notebook.name == u"my notebook"
|
||||
assert notebook.trash_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -335,7 +337,7 @@ class Test_users( Test_controller ):
|
|||
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 notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == False
|
||||
assert notebook.rank == None
|
||||
|
||||
|
@ -639,7 +641,7 @@ class Test_users( Test_controller ):
|
|||
assert notebook.revision
|
||||
assert notebook.name == u"trash"
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == None
|
||||
|
||||
|
@ -648,7 +650,7 @@ class Test_users( Test_controller ):
|
|||
assert notebook.revision
|
||||
assert notebook.name == u"my notebook"
|
||||
assert notebook.trash_id
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -657,7 +659,7 @@ class Test_users( Test_controller ):
|
|||
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 notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == False
|
||||
assert notebook.rank == None
|
||||
|
||||
|
@ -740,27 +742,27 @@ class Test_users( Test_controller ):
|
|||
assert len( result[ u"notebooks" ] ) == 5
|
||||
assert result[ u"notebooks" ][ 0 ].object_id
|
||||
assert result[ u"notebooks" ][ 0 ].name == u"trash"
|
||||
assert result[ u"notebooks" ][ 0 ].read_write == True
|
||||
assert result[ u"notebooks" ][ 0 ].read_write == Notebook.READ_WRITE
|
||||
assert result[ u"notebooks" ][ 0 ].owner == True
|
||||
assert result[ u"notebooks" ][ 0 ].rank == None
|
||||
assert result[ u"notebooks" ][ 1 ].object_id
|
||||
assert result[ u"notebooks" ][ 1 ].name == u"trash"
|
||||
assert result[ u"notebooks" ][ 1 ].read_write == True
|
||||
assert result[ u"notebooks" ][ 1 ].read_write == Notebook.READ_WRITE
|
||||
assert result[ u"notebooks" ][ 1 ].owner == True
|
||||
assert result[ u"notebooks" ][ 1 ].rank == None
|
||||
assert result[ u"notebooks" ][ 2 ].object_id == self.notebooks[ 0 ].object_id
|
||||
assert result[ u"notebooks" ][ 2 ].name == self.notebooks[ 0 ].name
|
||||
assert result[ u"notebooks" ][ 2 ].read_write == True
|
||||
assert result[ u"notebooks" ][ 2 ].read_write == Notebook.READ_WRITE
|
||||
assert result[ u"notebooks" ][ 2 ].owner == True
|
||||
assert result[ u"notebooks" ][ 2 ].rank == 0
|
||||
assert result[ u"notebooks" ][ 3 ].object_id == self.notebooks[ 1 ].object_id
|
||||
assert result[ u"notebooks" ][ 3 ].name == self.notebooks[ 1 ].name
|
||||
assert result[ u"notebooks" ][ 3 ].read_write == True
|
||||
assert result[ u"notebooks" ][ 3 ].read_write == Notebook.READ_WRITE
|
||||
assert result[ u"notebooks" ][ 3 ].owner == True
|
||||
assert result[ u"notebooks" ][ 3 ].rank == 1
|
||||
assert result[ u"notebooks" ][ 4 ].object_id == self.anon_notebook.object_id
|
||||
assert result[ u"notebooks" ][ 4 ].name == self.anon_notebook.name
|
||||
assert result[ u"notebooks" ][ 4 ].read_write == False
|
||||
assert result[ u"notebooks" ][ 4 ].read_write == Notebook.READ_ONLY
|
||||
assert result[ u"notebooks" ][ 4 ].owner == False
|
||||
assert result[ u"notebooks" ][ 4 ].rank == None
|
||||
assert result[ u"login_url" ] is None
|
||||
|
@ -783,7 +785,7 @@ class Test_users( Test_controller ):
|
|||
assert len( result[ u"notebooks" ] ) == 1
|
||||
assert result[ u"notebooks" ][ 0 ].object_id == self.anon_notebook.object_id
|
||||
assert result[ u"notebooks" ][ 0 ].name == self.anon_notebook.name
|
||||
assert result[ u"notebooks" ][ 0 ].read_write == False
|
||||
assert result[ u"notebooks" ][ 0 ].read_write == Notebook.READ_ONLY
|
||||
assert result[ u"notebooks" ][ 0 ].owner == False
|
||||
assert result[ u"notebooks" ][ 0 ].rank == None
|
||||
|
||||
|
@ -831,8 +833,8 @@ class Test_users( Test_controller ):
|
|||
invite_notebook_id = result[ u"redirect" ].split( u"/notebooks/" )[ -1 ]
|
||||
assert invite_notebook_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
|
||||
def test_login_with_after_login( self ):
|
||||
after_login = u"/foo/bar"
|
||||
|
@ -895,45 +897,215 @@ class Test_users( Test_controller ):
|
|||
assert user.group_storage_bytes == 0
|
||||
assert user.revision > previous_revision
|
||||
|
||||
def test_check_access( self ):
|
||||
access = cherrypy.root.users.check_access( self.user.object_id, self.notebooks[ 0 ].object_id )
|
||||
def test_load_notebook( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id )
|
||||
|
||||
assert access is True
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_check_access_read_write( self ):
|
||||
access = cherrypy.root.users.check_access( self.user.object_id, self.notebooks[ 0 ].object_id, read_write = True )
|
||||
def test_load_notebook_unknown_notebook( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, u"unknownid" )
|
||||
|
||||
assert access is True
|
||||
assert notebook is None
|
||||
|
||||
def test_check_access_owner( self ):
|
||||
access = cherrypy.root.users.check_access( self.user.object_id, self.notebooks[ 0 ].object_id, owner = True )
|
||||
def test_load_notebook_unknown_user( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( u"unknownuser", self.notebooks[ 0 ].object_id )
|
||||
|
||||
assert access is True
|
||||
assert notebook is None
|
||||
|
||||
def test_check_access_full( self ):
|
||||
access = cherrypy.root.users.check_access( self.user.object_id, self.notebooks[ 0 ].object_id, read_write = True, owner = True )
|
||||
def test_load_notebook_read_write( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id, read_write = True )
|
||||
|
||||
assert access is True
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_check_access_anon( self ):
|
||||
access = cherrypy.root.users.check_access( self.user.object_id, self.anon_notebook.object_id )
|
||||
def test_load_notebook_owner( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id, owner = True )
|
||||
|
||||
assert access is True
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_check_access_anon_read_write( self ):
|
||||
access = cherrypy.root.users.check_access( self.user.object_id, self.anon_notebook.object_id, read_write = True )
|
||||
def test_load_notebook_full( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id, read_write = True, owner = True )
|
||||
|
||||
assert access is False
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_check_access_anon_owner( self ):
|
||||
access = cherrypy.root.users.check_access( self.user.object_id, self.anon_notebook.object_id, owner = True )
|
||||
def test_load_notebook_with_note_id( self ):
|
||||
note = Note.create(
|
||||
self.database.next_id( Note ), u"<h3>hi</h3>",
|
||||
notebook_id = self.notebooks[ 0 ].object_id,
|
||||
user_id = self.user.object_id,
|
||||
)
|
||||
self.database.save( note )
|
||||
|
||||
assert access is False
|
||||
self.database.execute( self.user.sql_update_access(
|
||||
self.notebooks[ 0 ].object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES, owner = False,
|
||||
) )
|
||||
|
||||
def test_check_access_anon_full( self ):
|
||||
access = cherrypy.root.users.check_access( self.user.object_id, self.anon_notebook.object_id, read_write = True, owner = True )
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = note.object_id )
|
||||
|
||||
assert access is False
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_load_notebook_with_note_id_by_another_user( self ):
|
||||
note = Note.create(
|
||||
self.database.next_id( Note ), u"<h3>hi from another user</h3>",
|
||||
notebook_id = self.notebooks[ 0 ].object_id,
|
||||
user_id = self.user2.object_id,
|
||||
)
|
||||
self.database.save( note )
|
||||
|
||||
self.database.execute( self.user.sql_update_access(
|
||||
self.notebooks[ 0 ].object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES, owner = False,
|
||||
) )
|
||||
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = note.object_id )
|
||||
|
||||
assert notebook is None
|
||||
|
||||
def test_load_notebook_with_unknown_note_id( self ):
|
||||
self.database.execute( self.user.sql_update_access(
|
||||
self.notebooks[ 0 ].object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES, owner = False,
|
||||
) )
|
||||
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = u"unknownid" )
|
||||
|
||||
assert notebook is None
|
||||
|
||||
def test_load_notebook_with_note_id_in_another_notebook( self ):
|
||||
self.database.execute( self.user.sql_update_access(
|
||||
self.notebooks[ 0 ].object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES, owner = False,
|
||||
) )
|
||||
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = self.startup_note.object_id )
|
||||
|
||||
assert notebook is None
|
||||
|
||||
def test_load_notebook_read_write_with_note_id( self ):
|
||||
note = Note.create(
|
||||
self.database.next_id( Note ), u"<h3>hi</h3>",
|
||||
notebook_id = self.notebooks[ 0 ].object_id,
|
||||
user_id = self.user.object_id,
|
||||
)
|
||||
self.database.save( note )
|
||||
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = note.object_id )
|
||||
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_load_notebook_read_write_with_note_id_by_another_user( self ):
|
||||
note = Note.create(
|
||||
self.database.next_id( Note ), u"<h3>hi from another user</h3>",
|
||||
notebook_id = self.notebooks[ 0 ].object_id,
|
||||
user_id = self.user2.object_id,
|
||||
)
|
||||
self.database.save( note )
|
||||
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = note.object_id )
|
||||
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_load_notebook_read_write_with_unknown_note_id( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = u"unknownid" )
|
||||
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_load_notebook_read_write_with_note_id_in_another_notebook( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = self.startup_note.object_id )
|
||||
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_load_notebook_read_only_with_note_id( self ):
|
||||
note = Note.create(
|
||||
self.database.next_id( Note ), u"<h3>hi</h3>",
|
||||
notebook_id = self.notebooks[ 0 ].object_id,
|
||||
user_id = self.user.object_id,
|
||||
)
|
||||
self.database.save( note )
|
||||
|
||||
self.database.execute( self.user.sql_update_access(
|
||||
self.notebooks[ 0 ].object_id, read_write = Notebook.READ_ONLY, owner = False,
|
||||
) )
|
||||
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = note.object_id )
|
||||
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_load_notebook_read_only_with_note_id_by_another_user( self ):
|
||||
note = Note.create(
|
||||
self.database.next_id( Note ), u"<h3>hi from another user</h3>",
|
||||
notebook_id = self.notebooks[ 0 ].object_id,
|
||||
user_id = self.user2.object_id,
|
||||
)
|
||||
self.database.save( note )
|
||||
|
||||
self.database.execute( self.user.sql_update_access(
|
||||
self.notebooks[ 0 ].object_id, read_write = Notebook.READ_ONLY, owner = False,
|
||||
) )
|
||||
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = note.object_id )
|
||||
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_load_notebook_read_only_with_unknown_note_id( self ):
|
||||
self.database.execute( self.user.sql_update_access(
|
||||
self.notebooks[ 0 ].object_id, read_write = Notebook.READ_ONLY, owner = False,
|
||||
) )
|
||||
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = u"unknownid" )
|
||||
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_load_notebook_read_only_with_note_id_in_another_notebook( self ):
|
||||
self.database.execute( self.user.sql_update_access(
|
||||
self.notebooks[ 0 ].object_id, read_write = Notebook.READ_ONLY, owner = False,
|
||||
) )
|
||||
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.notebooks[ 0 ].object_id,
|
||||
note_id = self.startup_note.object_id )
|
||||
|
||||
assert notebook
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
|
||||
def test_load_notebook_anon( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.anon_notebook.object_id )
|
||||
|
||||
assert notebook
|
||||
assert notebook.object_id == self.anon_notebook.object_id
|
||||
|
||||
def test_load_notebook_anon_read_write( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.anon_notebook.object_id, read_write = True )
|
||||
|
||||
assert notebook is None
|
||||
|
||||
def test_load_notebook_anon_owner( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.anon_notebook.object_id, owner = True )
|
||||
|
||||
assert notebook is None
|
||||
|
||||
def test_load_notebook_anon_full( self ):
|
||||
notebook = cherrypy.root.users.load_notebook( self.user.object_id, self.anon_notebook.object_id, read_write = True, owner = True )
|
||||
|
||||
assert notebook is None
|
||||
|
||||
def test_check_group( self ):
|
||||
membership = cherrypy.root.users.check_group( self.user.object_id, self.group.object_id )
|
||||
|
@ -1097,7 +1269,7 @@ class Test_users( Test_controller ):
|
|||
assert len( result[ u"notebooks" ] ) == 1
|
||||
assert result[ u"notebooks" ][ 0 ].object_id == self.anon_notebook.object_id
|
||||
assert result[ u"notebooks" ][ 0 ].name == self.anon_notebook.name
|
||||
assert result[ u"notebooks" ][ 0 ].read_write == False
|
||||
assert result[ u"notebooks" ][ 0 ].read_write == Notebook.READ_ONLY
|
||||
assert result[ u"notebooks" ][ 0 ].owner == False
|
||||
assert result[ u"notebooks" ][ 0 ].rank == None
|
||||
|
||||
|
@ -2151,8 +2323,8 @@ class Test_users( Test_controller ):
|
|||
invite_id = invite_id,
|
||||
), session_id = self.session_id )
|
||||
|
||||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
|
||||
self.login()
|
||||
result = self.http_post( "/users/revoke_invite", dict(
|
||||
|
@ -2163,8 +2335,8 @@ class Test_users( Test_controller ):
|
|||
assert result[ u"message" ]
|
||||
assert len( result[ u"invites" ] ) == 0
|
||||
|
||||
assert not cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert not cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
assert not cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert not cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
|
||||
def test_revoke_invite_redeemed_self( self ):
|
||||
self.login()
|
||||
|
@ -2191,8 +2363,8 @@ class Test_users( Test_controller ):
|
|||
invite_id = invite_id,
|
||||
), session_id = self.session_id )
|
||||
|
||||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
|
||||
# as user2, revoke that user's own invite
|
||||
result = self.http_post( "/users/revoke_invite", dict(
|
||||
|
@ -2204,8 +2376,8 @@ class Test_users( Test_controller ):
|
|||
assert len( result[ u"invites" ] ) == 0
|
||||
|
||||
# the user should no longer have any access
|
||||
assert not cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert not cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
assert not cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert not cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
|
||||
def test_revoke_invite_without_login( self ):
|
||||
# login to send the invites, but don't send the logged-in session id for revoke_invite() below
|
||||
|
@ -2347,8 +2519,8 @@ class Test_users( Test_controller ):
|
|||
), session_id = self.session_id )
|
||||
|
||||
# assert that access has been granted
|
||||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
|
||||
# assert that the user is redirected to the notebook that the invite is for
|
||||
assert result[ u"redirect"].startswith( u"/notebooks/" )
|
||||
|
@ -2385,8 +2557,8 @@ class Test_users( Test_controller ):
|
|||
), session_id = self.session_id )
|
||||
|
||||
# assert that access is still granted
|
||||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.check_access( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].object_id )
|
||||
assert cherrypy.root.users.load_notebook( self.user2.object_id, self.notebooks[ 0 ].trash_id )
|
||||
|
||||
# assert that the user is redirected to the notebook that the invite is for
|
||||
assert result[ u"redirect"].startswith( u"/notebooks/" )
|
||||
|
@ -4104,7 +4276,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -4145,7 +4317,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -4184,7 +4356,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -4224,7 +4396,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -4262,7 +4434,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -4307,7 +4479,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -4436,7 +4608,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -4484,7 +4656,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -4532,7 +4704,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -4578,7 +4750,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -4627,7 +4799,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
@ -4675,7 +4847,7 @@ class Test_users( Test_controller ):
|
|||
notebook = [ notebook for notebook in result[ u"notebooks" ] if notebook.object_id == self.notebooks[ 0 ].object_id ][ 0 ]
|
||||
assert notebook.object_id == self.notebooks[ 0 ].object_id
|
||||
assert notebook.name == self.notebooks[ 0 ].name
|
||||
assert notebook.read_write == True
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == True
|
||||
assert notebook.rank == 0
|
||||
|
||||
|
|
|
@ -9,8 +9,10 @@ import urllib2 as urllib
|
|||
import cherrypy
|
||||
import webbrowser
|
||||
from controller.Database import Database
|
||||
from controller.Schema_upgrader import Schema_upgrader
|
||||
from controller.Root import Root
|
||||
from config import Common
|
||||
from config.Version import VERSION
|
||||
|
||||
|
||||
INITIAL_SOCKET_TIMEOUT_SECONDS = 1
|
||||
|
@ -103,6 +105,10 @@ def main( args ):
|
|||
ssl_mode = cherrypy.config.configMap[ u"global" ].get( u"luminotes.db_ssl_mode" ),
|
||||
)
|
||||
|
||||
# if necessary, upgrade the database schema to match this current version of the code
|
||||
schema_upgrader = Schema_upgrader( database )
|
||||
schema_upgrader.upgrade_schema( to_version = VERSION )
|
||||
|
||||
cherrypy.lowercase_api = True
|
||||
root = Root( database, cherrypy.config.configMap )
|
||||
cherrypy.root = root
|
||||
|
@ -114,6 +120,7 @@ def callback( log_access_file, log_file, server_url, port_filename, socket_port,
|
|||
# record our listening socket port
|
||||
if port_filename:
|
||||
port_file = file( port_filename, "w" )
|
||||
os.chmod( port_filename, stat.S_IRUSR | stat.S_IWUSR )
|
||||
port_file.write( "%s" % socket_port )
|
||||
port_file.close()
|
||||
|
||||
|
|
|
@ -12,8 +12,12 @@ class Notebook( Persistent ):
|
|||
WHITESPACE_PATTERN = re.compile( r"\s+" )
|
||||
SEARCH_OPERATORS = re.compile( r"[&|!()'\\:]" )
|
||||
|
||||
READ_ONLY = 0 # user can only view the notes within this notebook
|
||||
READ_WRITE = 1 # user can view and edit the notes within this notebook
|
||||
READ_WRITE_FOR_OWN_NOTES = 2 # user can only edit their own notes, not notes created by others
|
||||
|
||||
def __init__( self, object_id, revision = None, name = None, trash_id = None, deleted = False,
|
||||
user_id = None, read_write = True, owner = True, rank = None ):
|
||||
user_id = None, read_write = None, owner = True, rank = None, own_notes_only = False ):
|
||||
"""
|
||||
Create a new notebook with the given id and name.
|
||||
|
||||
|
@ -30,11 +34,14 @@ class Notebook( Persistent ):
|
|||
@type user_id: unicode or NoneType
|
||||
@param user_id: id of the user who most recently updated this notebook object (optional)
|
||||
@type read_write: bool or NoneType
|
||||
@param read_write: whether this view of the notebook is currently read-write (optional, defaults to True)
|
||||
@param read_write: whether this view of the notebook is currently read-write. one of:
|
||||
READ_ONLY, READ_WRITE, READ_WRITE_FOR_OWN_NOTES (optional, defaults to READ_WRITE)
|
||||
@type owner: bool or NoneType
|
||||
@param owner: whether this view of the notebook currently has owner-level access (optional, defaults to True)
|
||||
@type rank: float or NoneType
|
||||
@param rank: indicates numeric ordering of this note in relation to other notebooks
|
||||
@type own_notes_only: bool or NoneType
|
||||
@param own_notes_only: True makes read_write be READ_WRITE_FOR_OWN_NOTES (optional, defaults to False)
|
||||
@rtype: Notebook
|
||||
@return: newly constructed notebook
|
||||
"""
|
||||
|
@ -43,12 +50,22 @@ class Notebook( Persistent ):
|
|||
self.__trash_id = trash_id
|
||||
self.__deleted = deleted
|
||||
self.__user_id = user_id
|
||||
|
||||
read_write = {
|
||||
None: Notebook.READ_WRITE,
|
||||
True: Notebook.READ_WRITE,
|
||||
False: Notebook.READ_ONLY,
|
||||
}.get( read_write, read_write )
|
||||
|
||||
if own_notes_only is True and read_write != Notebook.READ_ONLY:
|
||||
read_write = Notebook.READ_WRITE_FOR_OWN_NOTES
|
||||
|
||||
self.__read_write = read_write
|
||||
self.__owner = owner
|
||||
self.__rank = rank
|
||||
|
||||
@staticmethod
|
||||
def create( object_id, name = None, trash_id = None, deleted = False, user_id = None, read_write = True, owner = True, rank = None ):
|
||||
def create( object_id, name = None, trash_id = None, deleted = False, user_id = None, read_write = None, owner = True, rank = None, own_notes_only = False ):
|
||||
"""
|
||||
Convenience constructor for creating a new notebook.
|
||||
|
||||
|
@ -63,15 +80,18 @@ class Notebook( Persistent ):
|
|||
@type user_id: unicode or NoneType
|
||||
@param user_id: id of the user who most recently updated this notebook object (optional)
|
||||
@type read_write: bool or NoneType
|
||||
@param read_write: whether this view of the notebook is currently read-write (optional, defaults to True)
|
||||
@param read_write: whether this view of the notebook is currently read-write. one of:
|
||||
READ_ONLY, READ_WRITE, READ_WRITE_FOR_OWN_NOTES (optional, defaults to READ_WRITE)
|
||||
@type owner: bool or NoneType
|
||||
@param owner: whether this view of the notebook currently has owner-level access (optional, defaults to True)
|
||||
@type rank: float or NoneType
|
||||
@param rank: indicates numeric ordering of this note in relation to other notebooks
|
||||
@type own_notes_only: bool or NoneType
|
||||
@param own_notes_only: True makes read_write be READ_WRITE_FOR_OWN_NOTES (optional, defaults to False)
|
||||
@rtype: Notebook
|
||||
@return: newly constructed notebook
|
||||
"""
|
||||
return Notebook( object_id, name = name, trash_id = trash_id, user_id = user_id, read_write = read_write, owner = owner, rank = rank )
|
||||
return Notebook( object_id, name = name, trash_id = trash_id, user_id = user_id, read_write = read_write, owner = owner, rank = rank, own_notes_only = own_notes_only )
|
||||
|
||||
@staticmethod
|
||||
def sql_load( object_id, revision = None ):
|
||||
|
@ -264,6 +284,42 @@ class Notebook( Persistent ):
|
|||
"select count( id ) from note_current where notebook_id = %s;" % \
|
||||
( quote( self.object_id ) )
|
||||
|
||||
def sql_load_tag_by_name( self, user_id, tag_name ):
|
||||
"""
|
||||
Return a SQL string to load a tag associated with this notebook by the given user.
|
||||
"""
|
||||
return \
|
||||
"""
|
||||
select
|
||||
tag.id, tag.revision, tag.notebook_id, tag.user_id, tag.name, tag.description, tag_notebook.value
|
||||
from
|
||||
tag_notebook, tag
|
||||
where
|
||||
tag_notebook.notebook_id = %s and
|
||||
tag_notebook.user_id = %s and
|
||||
tag_notebook.tag_id = tag.id and
|
||||
tag.name = %s
|
||||
order by tag.name;
|
||||
""" % ( quote( self.object_id ), quote( user_id ), quote( tag_name ) )
|
||||
|
||||
def sql_load_tags( self, user_id ):
|
||||
"""
|
||||
Return a SQL string to load a list of all the tags associated with this notebook by the given
|
||||
user.
|
||||
"""
|
||||
return \
|
||||
"""
|
||||
select
|
||||
tag.id, tag.revision, tag.notebook_id, tag.user_id, tag.name, tag.description, tag_notebook.value
|
||||
from
|
||||
tag_notebook, tag
|
||||
where
|
||||
tag_notebook.notebook_id = %s and
|
||||
tag_notebook.user_id = %s and
|
||||
tag_notebook.tag_id = tag.id
|
||||
order by tag.name;
|
||||
""" % ( quote( self.object_id ), quote( user_id ) )
|
||||
|
||||
def to_dict( self ):
|
||||
d = Persistent.to_dict( self )
|
||||
|
||||
|
@ -285,6 +341,12 @@ class Notebook( Persistent ):
|
|||
def __set_read_write( self, read_write ):
|
||||
# The read_write member isn't actually saved to the database, so setting it doesn't need to
|
||||
# call update_revision().
|
||||
read_write = {
|
||||
None: Notebook.READ_WRITE,
|
||||
True: Notebook.READ_WRITE,
|
||||
False: Notebook.READ_ONLY,
|
||||
}.get( read_write, read_write )
|
||||
|
||||
self.__read_write = read_write
|
||||
|
||||
def __set_owner( self, owner ):
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
from Persistent import Persistent, quote
|
||||
|
||||
|
||||
class Tag( Persistent ):
|
||||
"""
|
||||
A tag for a note or a notebook.
|
||||
"""
|
||||
def __init__( self, object_id, revision = None, notebook_id = None, user_id = None, name = None, description = None, value = None ):
|
||||
"""
|
||||
Create a Tag with the given id.
|
||||
|
||||
@type object_id: unicode
|
||||
@param object_id: id of the Tag
|
||||
@type revision: datetime or NoneType
|
||||
@param revision: revision timestamp of the object (optional, defaults to now)
|
||||
@type notebook_id: unicode or NoneType
|
||||
@param notebook_id: id of the notebook whose namespace this tag is in, if any
|
||||
@type user_id: unicode or NoneType
|
||||
@param user_id: id of the user who most recently updated this tag, if any
|
||||
@type name: unicode or NoneType
|
||||
@param name: name of the tag (optional)
|
||||
@type description: unicode or NoneType
|
||||
@param description: brief description of the tag (optional)
|
||||
@type value: unicode or NoneType
|
||||
@param value: per-note or per-notebook value of the tag (optional)
|
||||
@rtype: Tag
|
||||
@return: newly constructed Tag
|
||||
"""
|
||||
Persistent.__init__( self, object_id, revision )
|
||||
self.__notebook_id = notebook_id
|
||||
self.__user_id = user_id
|
||||
self.__name = name
|
||||
self.__description = description
|
||||
self.__value = value
|
||||
|
||||
@staticmethod
|
||||
def create( object_id, notebook_id = None, user_id = None, name = None, description = None, value = None ):
|
||||
"""
|
||||
Convenience constructor for creating a new Tag.
|
||||
|
||||
@type object_id: unicode
|
||||
@param object_id: id of the Tag
|
||||
@type notebook_id: unicode or NoneType
|
||||
@param notebook_id: id of the notebook whose namespace this tag is in, if any
|
||||
@type user_id: unicode or NoneType
|
||||
@param user_id: id of the user who most recently updated this tag, if any
|
||||
@type name: unicode or NoneType
|
||||
@param name: name of the tag (optional)
|
||||
@type description: unicode or NoneType
|
||||
@param description: brief description of the tag (optional)
|
||||
@type value: unicode or NoneType
|
||||
@param value: per-note or per-notebook value of the tag (optional)
|
||||
@rtype: Tag
|
||||
@return: newly constructed Tag
|
||||
"""
|
||||
return Tag( object_id, notebook_id = notebook_id, user_id = user_id, name = name, description = description, value = value )
|
||||
|
||||
@staticmethod
|
||||
def sql_load( object_id, revision = None ):
|
||||
# Tags don't store old revisions
|
||||
if revision:
|
||||
raise NotImplementedError()
|
||||
|
||||
return \
|
||||
"""
|
||||
select
|
||||
tag.id, tag.revision, tag.notebook_id, tag.user_id, tag.name, tag.description
|
||||
from
|
||||
tag
|
||||
where
|
||||
tag.id = %s;
|
||||
""" % quote( object_id )
|
||||
|
||||
@staticmethod
|
||||
def sql_load_by_name( name, notebook_id = None, user_id = None ):
|
||||
if notebook_id:
|
||||
notebook_id_clause = " and tag.notebook_id = %s" % quote( notebook_id )
|
||||
else:
|
||||
notebook_id_clause = ""
|
||||
|
||||
if user_id:
|
||||
user_id_clause = " and tag.user_id = %s" % quote( user_id )
|
||||
else:
|
||||
user_id_clause = ""
|
||||
|
||||
return \
|
||||
"""
|
||||
select
|
||||
tag.id, tag.revision, tag.notebook_id, tag.user_id, tag.name, tag.description
|
||||
from
|
||||
tag
|
||||
where
|
||||
tag.name = %s%s%s;
|
||||
""" % ( quote( name ), notebook_id_clause, user_id_clause )
|
||||
|
||||
@staticmethod
|
||||
def sql_id_exists( object_id, revision = None ):
|
||||
if revision:
|
||||
raise NotImplementedError()
|
||||
|
||||
return "select id from tag where id = %s;" % quote( object_id )
|
||||
|
||||
def sql_exists( self ):
|
||||
return Tag.sql_id_exists( self.object_id )
|
||||
|
||||
def sql_create( self ):
|
||||
return "insert into tag ( id, revision, notebook_id, user_id, name, description ) values ( %s, %s, %s, %s, %s, %s );" % \
|
||||
( quote( self.object_id ), quote( self.revision ), quote( self.__notebook_id ),
|
||||
quote( self.__user_id ), quote( self.__name ), quote( self.__description ) )
|
||||
|
||||
def sql_update( self ):
|
||||
return "update tag set revision = %s, notebook_id = %s, user_id = %s, name = %s, description = %s where id = %s;" % \
|
||||
( quote( self.revision ), quote( self.__notebook_id ), quote( self.__user_id ),
|
||||
quote( self.__name ), quote( self.__description ), quote( self.object_id ) )
|
||||
|
||||
def sql_delete( self ):
|
||||
return "delete from tag where id = %s;" % quote( self.object_id )
|
||||
|
||||
def to_dict( self ):
|
||||
d = Persistent.to_dict( self )
|
||||
d.update( dict(
|
||||
notebook_id = self.__notebook_id,
|
||||
user_id = self.__user_id,
|
||||
name = self.__name,
|
||||
description = self.__description,
|
||||
value = self.__value,
|
||||
) )
|
||||
|
||||
return d
|
||||
|
||||
notebook_id = property( lambda self: self.__notebook_id )
|
||||
user_id = property( lambda self: self.__user_id )
|
||||
name = property( lambda self: self.__name )
|
||||
description = property( lambda self: self.__description )
|
||||
value = property( lambda self: self.__value )
|
|
@ -2,6 +2,7 @@ import sha
|
|||
import random
|
||||
from copy import copy
|
||||
from Persistent import Persistent, quote
|
||||
from Notebook import Notebook
|
||||
|
||||
|
||||
class User( Persistent ):
|
||||
|
@ -132,7 +133,8 @@ class User( Persistent ):
|
|||
def sql_load_by_email_address( email_address ):
|
||||
return "select * from luminotes_user_current where email_address = %s;" % quote( email_address )
|
||||
|
||||
def sql_load_notebooks( self, parents_only = False, undeleted_only = False, read_write = False ):
|
||||
def sql_load_notebooks( self, parents_only = False, undeleted_only = False, read_write = False,
|
||||
tag_name = None, tag_value = None, notebook_id = None ):
|
||||
"""
|
||||
Return a SQL string to load a list of the notebooks to which this user has access.
|
||||
"""
|
||||
|
@ -151,28 +153,55 @@ class User( Persistent ):
|
|||
else:
|
||||
read_write_clause = ""
|
||||
|
||||
if tag_name:
|
||||
tag_tables = ", tag_notebook, tag"
|
||||
tag_clause = \
|
||||
"""
|
||||
and tag_notebook.tag_id = tag.id and tag_notebook.user_id = %s and
|
||||
tag_notebook.notebook_id = notebook_current.id and tag.name = %s
|
||||
""" % ( quote( self.object_id ), quote( tag_name ) )
|
||||
|
||||
if tag_value:
|
||||
tag_clause += " and tag_notebook.value = %s" % quote( tag_value )
|
||||
else:
|
||||
tag_tables = ""
|
||||
tag_clause = ""
|
||||
|
||||
# useful for loading just a single notebook that the user has access to
|
||||
if notebook_id:
|
||||
notebook_id_clause = " and notebook_current.id = %s" % quote( notebook_id )
|
||||
else:
|
||||
notebook_id_clause = ""
|
||||
|
||||
return \
|
||||
"""
|
||||
select
|
||||
notebook_current.*, user_notebook.read_write, user_notebook.owner, user_notebook.rank
|
||||
notebook_current.*, user_notebook.read_write, user_notebook.owner, user_notebook.rank, user_notebook.own_notes_only
|
||||
from
|
||||
user_notebook, notebook_current
|
||||
user_notebook, notebook_current%s
|
||||
where
|
||||
user_notebook.user_id = %s%s%s%s and
|
||||
user_notebook.user_id = %s%s%s%s%s%s and
|
||||
user_notebook.notebook_id = notebook_current.id
|
||||
order by user_notebook.rank;
|
||||
""" % ( quote( self.object_id ), parents_only_clause, undeleted_only_clause, read_write_clause )
|
||||
""" % ( tag_tables, quote( self.object_id ), parents_only_clause, undeleted_only_clause,
|
||||
read_write_clause, tag_clause, notebook_id_clause )
|
||||
|
||||
def sql_save_notebook( self, notebook_id, read_write = True, owner = True, rank = None ):
|
||||
def sql_save_notebook( self, notebook_id, read_write = True, owner = True, rank = None, own_notes_only = False ):
|
||||
"""
|
||||
Return a SQL string to save the id of a notebook to which this user has access.
|
||||
"""
|
||||
if rank is None: rank = quote( None )
|
||||
|
||||
return \
|
||||
"insert into user_notebook ( user_id, notebook_id, read_write, owner, rank ) values " + \
|
||||
"( %s, %s, %s, %s, %s );" % ( quote( self.object_id ), quote( notebook_id ), quote( read_write and 't' or 'f' ),
|
||||
quote( owner and 't' or 'f' ), rank )
|
||||
"insert into user_notebook ( user_id, notebook_id, read_write, owner, rank, own_notes_only ) values " + \
|
||||
"( %s, %s, %s, %s, %s, %s );" % (
|
||||
quote( self.object_id ),
|
||||
quote( notebook_id ),
|
||||
quote( read_write and 't' or 'f' ),
|
||||
quote( owner and 't' or 'f' ),
|
||||
rank,
|
||||
quote( own_notes_only and 't' or 'f' ),
|
||||
)
|
||||
|
||||
def sql_remove_notebook( self, notebook_id ):
|
||||
"""
|
||||
|
@ -202,14 +231,26 @@ class User( Persistent ):
|
|||
"select user_id from user_notebook where user_id = %s and notebook_id = %s;" % \
|
||||
( quote( self.object_id ), quote( notebook_id ) )
|
||||
|
||||
def sql_update_access( self, notebook_id, read_write = False, owner = False ):
|
||||
def sql_update_access( self, notebook_id, read_write = Notebook.READ_ONLY, owner = False ):
|
||||
"""
|
||||
Return a SQL string to update the user's notebook access to the given read_write and owner level.
|
||||
"""
|
||||
return \
|
||||
"update user_notebook set read_write = %s, owner = %s where user_id = %s and notebook_id = %s;" % \
|
||||
( quote( read_write and 't' or 'f' ), quote( owner and 't' or 'f' ), quote( self.object_id ),
|
||||
quote( notebook_id ) )
|
||||
"update user_notebook set read_write = %s, owner = %s, own_notes_only = %s where user_id = %s and notebook_id = %s;" % (
|
||||
quote( ( read_write != Notebook.READ_ONLY ) and 't' or 'f' ),
|
||||
quote( owner and 't' or 'f' ),
|
||||
quote( ( read_write == Notebook.READ_WRITE_FOR_OWN_NOTES ) and 't' or 'f' ),
|
||||
quote( self.object_id ),
|
||||
quote( notebook_id ),
|
||||
)
|
||||
|
||||
def sql_save_notebook_tag( self, notebook_id, tag_id, value = None ):
|
||||
"""
|
||||
Return a SQL string to associate a tag with a notebook of this user.
|
||||
"""
|
||||
return \
|
||||
"insert into tag_notebook ( notebook_id, tag_id, value, user_id ) values " + \
|
||||
"( %s, %s, %s, %s );" % ( quote( notebook_id ), quote( tag_id ), quote( value ), quote( self.object_id ) )
|
||||
|
||||
def sql_update_notebook_rank( self, notebook_id, rank ):
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
create table tag (
|
||||
id text,
|
||||
revision timestamp with time zone,
|
||||
notebook_id text,
|
||||
user_id text,
|
||||
name text,
|
||||
description text
|
||||
);
|
||||
ALTER TABLE ONLY tag ADD CONSTRAINT tag_pkey PRIMARY KEY (id);
|
||||
CREATE INDEX tag_notebook_id_index ON tag USING btree (notebook_id);
|
||||
CREATE INDEX tag_user_id_index ON tag USING btree (user_id);
|
||||
|
||||
create table tag_notebook (
|
||||
notebook_id text,
|
||||
tag_id text,
|
||||
value text,
|
||||
user_id text
|
||||
);
|
||||
ALTER TABLE ONLY tag_notebook ADD CONSTRAINT tag_notebook_pkey PRIMARY KEY (user_id, notebook_id, tag_id);
|
||||
|
||||
create table tag_note (
|
||||
note_id text,
|
||||
tag_id text,
|
||||
value text
|
||||
);
|
||||
ALTER TABLE ONLY tag_note ADD CONSTRAINT tag_note_pkey PRIMARY KEY (note_id, tag_id);
|
||||
|
||||
ALTER TABLE user_notebook ADD COLUMN own_notes_only boolean DEFAULT false;
|
||||
|
||||
update user_notebook set rank = 0 from luminotes_user_current, notebook_current where user_notebook.user_id = luminotes_user_current.id and username = 'anonymous' and user_notebook.notebook_id = notebook_current.id and notebook_current.name = 'Luminotes';
|
|
@ -0,0 +1,37 @@
|
|||
create table tag (
|
||||
id text,
|
||||
revision timestamp with time zone,
|
||||
notebook_id text,
|
||||
user_id text,
|
||||
name text,
|
||||
description text
|
||||
);
|
||||
CREATE INDEX tag_pkey ON tag (id);
|
||||
CREATE INDEX tag_notebook_id_index ON tag (notebook_id);
|
||||
CREATE INDEX tag_user_id_index ON tag (user_id);
|
||||
|
||||
create table tag_notebook (
|
||||
notebook_id text,
|
||||
tag_id text,
|
||||
value text,
|
||||
user_id text
|
||||
);
|
||||
CREATE INDEX tag_notebook_pkey ON tag_notebook (user_id, notebook_id, tag_id);
|
||||
|
||||
create table tag_note (
|
||||
note_id text,
|
||||
tag_id text,
|
||||
value text
|
||||
);
|
||||
CREATE INDEX tag_note_pkey ON tag_note (note_id, tag_id);
|
||||
|
||||
CREATE INDEX file_pkey ON file (id);
|
||||
CREATE INDEX invite_pkey ON invite (id);
|
||||
CREATE INDEX luminotes_user_pkey ON luminotes_user (id, revision);
|
||||
CREATE INDEX note_pkey ON note (id, revision);
|
||||
CREATE INDEX notebook_pkey ON notebook (id, revision);
|
||||
CREATE INDEX password_reset_pkey ON password_reset (id);
|
||||
CREATE INDEX download_access_pkey ON download_access (id);
|
||||
CREATE INDEX user_notebook_pkey ON user_notebook (user_id, notebook_id);
|
||||
|
||||
ALTER TABLE user_notebook ADD COLUMN own_notes_only boolean DEFAULT false;
|
|
@ -12,4 +12,7 @@ DROP TABLE user_notebook;
|
|||
DROP TABLE user_group;
|
||||
DROP TABLE invite;
|
||||
DROP TABLE file;
|
||||
DROP TABLE tag;
|
||||
DROP TABLE tag_notebook;
|
||||
DROP TABLE tag_note;
|
||||
DROP FUNCTION drop_html_tags( text );
|
||||
|
|
246
model/schema.sql
246
model/schema.sql
|
@ -1,5 +1,5 @@
|
|||
--
|
||||
-- PostgreSQL database dump
|
||||
-- PostgreSQL database schema
|
||||
--
|
||||
|
||||
SET client_encoding = 'UTF8';
|
||||
|
@ -12,22 +12,10 @@ SET default_tablespace = '';
|
|||
|
||||
SET default_with_oids = false;
|
||||
|
||||
|
||||
--
|
||||
-- Name: drop_html_tags(text); Type: FUNCTION; Schema: public; Owner: luminotes
|
||||
--
|
||||
|
||||
CREATE FUNCTION drop_html_tags(text) RETURNS text
|
||||
AS $_$select regexp_replace( regexp_replace( $1, '</?(div|p|br|ul|ol|li|h3)( [^>]*?)?/?>', ' ', 'gi' ), '<[^>]+?>', '', 'g' );$_$
|
||||
LANGUAGE sql;
|
||||
|
||||
|
||||
ALTER FUNCTION public.drop_html_tags(text) OWNER TO luminotes;
|
||||
|
||||
--
|
||||
-- Name: file; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE file (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone,
|
||||
|
@ -37,14 +25,30 @@ CREATE TABLE file (
|
|||
size_bytes integer,
|
||||
content_type text
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.file OWNER TO luminotes;
|
||||
CREATE TABLE tag (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone,
|
||||
notebook_id text,
|
||||
user_id text,
|
||||
name text,
|
||||
description text
|
||||
);
|
||||
ALTER TABLE public.tag OWNER TO luminotes;
|
||||
CREATE TABLE tag_notebook (
|
||||
notebook_id text,
|
||||
tag_id text,
|
||||
value text,
|
||||
user_id text
|
||||
);
|
||||
|
||||
--
|
||||
-- Name: invite; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE public.tag_notebook OWNER TO luminotes;
|
||||
CREATE TABLE tag_note (
|
||||
note_id text,
|
||||
tag_id text,
|
||||
value text
|
||||
);
|
||||
ALTER TABLE public.tag_note OWNER TO luminotes;
|
||||
CREATE TABLE invite (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
|
@ -55,37 +59,16 @@ CREATE TABLE invite (
|
|||
"owner" boolean,
|
||||
redeemed_user_id text
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.invite OWNER TO luminotes;
|
||||
|
||||
--
|
||||
-- Name: luminotes_group; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE luminotes_group (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
name text
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.luminotes_group OWNER TO luminotes;
|
||||
|
||||
--
|
||||
-- Name: luminotes_group_current; Type: VIEW; Schema: public; Owner: luminotes
|
||||
--
|
||||
|
||||
CREATE VIEW luminotes_group_current AS
|
||||
SELECT luminotes_group.id, luminotes_group.revision, luminotes_group.name FROM luminotes_group WHERE (luminotes_group.revision IN (SELECT max(sub_group.revision) AS max FROM luminotes_group sub_group WHERE (sub_group.id = luminotes_group.id)));
|
||||
|
||||
|
||||
ALTER TABLE public.luminotes_group_current OWNER TO luminotes;
|
||||
|
||||
--
|
||||
-- Name: luminotes_user; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE luminotes_user (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
|
@ -96,24 +79,10 @@ CREATE TABLE luminotes_user (
|
|||
storage_bytes integer,
|
||||
rate_plan integer
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.luminotes_user OWNER TO luminotes;
|
||||
|
||||
--
|
||||
-- Name: luminotes_user_current; Type: VIEW; Schema: public; Owner: luminotes
|
||||
--
|
||||
|
||||
CREATE VIEW luminotes_user_current AS
|
||||
SELECT luminotes_user.id, luminotes_user.revision, luminotes_user.username, luminotes_user.salt, luminotes_user.password_hash, luminotes_user.email_address, luminotes_user.storage_bytes, luminotes_user.rate_plan FROM luminotes_user WHERE (luminotes_user.revision IN (SELECT max(sub_user.revision) AS max FROM luminotes_user sub_user WHERE (sub_user.id = luminotes_user.id)));
|
||||
|
||||
|
||||
ALTER TABLE public.luminotes_user_current OWNER TO luminotes;
|
||||
|
||||
--
|
||||
-- Name: note; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE note (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
|
@ -126,24 +95,10 @@ CREATE TABLE note (
|
|||
search tsvector,
|
||||
user_id text
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.note OWNER TO luminotes;
|
||||
|
||||
--
|
||||
-- Name: note_current; Type: VIEW; Schema: public; Owner: luminotes
|
||||
--
|
||||
|
||||
CREATE VIEW note_current AS
|
||||
SELECT note.id, note.revision, note.title, note.contents, note.notebook_id, note.startup, note.deleted_from_id, note.rank, note.search, note.user_id FROM note WHERE (note.revision IN (SELECT max(sub_note.revision) AS max FROM note sub_note WHERE (sub_note.id = note.id)));
|
||||
|
||||
|
||||
ALTER TABLE public.note_current OWNER TO luminotes;
|
||||
|
||||
--
|
||||
-- Name: notebook; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE notebook (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
|
@ -152,239 +107,106 @@ CREATE TABLE notebook (
|
|||
deleted boolean DEFAULT false,
|
||||
user_id text
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.notebook OWNER TO luminotes;
|
||||
|
||||
--
|
||||
-- Name: notebook_current; Type: VIEW; Schema: public; Owner: luminotes
|
||||
--
|
||||
|
||||
CREATE VIEW notebook_current AS
|
||||
SELECT notebook.id, notebook.revision, notebook.name, notebook.trash_id, notebook.deleted, notebook.user_id FROM notebook WHERE (notebook.revision IN (SELECT max(sub_notebook.revision) AS max FROM notebook sub_notebook WHERE (sub_notebook.id = notebook.id)));
|
||||
|
||||
|
||||
ALTER TABLE public.notebook_current OWNER TO luminotes;
|
||||
|
||||
--
|
||||
-- Name: password_reset; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE password_reset (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
email_address text,
|
||||
redeemed boolean
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.password_reset OWNER TO luminotes;
|
||||
|
||||
|
||||
-- Name: download_access; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE download_access (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
item_number text,
|
||||
transaction_id text
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.download_access OWNER TO luminotes;
|
||||
|
||||
--
|
||||
--
|
||||
-- Name: user_group; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE user_group (
|
||||
user_id text NOT NULL,
|
||||
group_id text NOT NULL,
|
||||
"admin" boolean DEFAULT false
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.user_group OWNER TO luminotes;
|
||||
|
||||
--
|
||||
-- Name: user_notebook; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE user_notebook (
|
||||
user_id text NOT NULL,
|
||||
notebook_id text NOT NULL,
|
||||
read_write boolean DEFAULT false,
|
||||
"owner" boolean DEFAULT false,
|
||||
rank numeric
|
||||
rank numeric,
|
||||
own_notes_only boolean DEFAULT false
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.user_notebook OWNER TO luminotes;
|
||||
|
||||
|
||||
-- Name: file_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY file
|
||||
ADD CONSTRAINT file_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY tag
|
||||
ADD CONSTRAINT tag_pkey PRIMARY KEY (id);
|
||||
|
||||
--
|
||||
-- Name: invite_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
ALTER TABLE ONLY tag_notebook
|
||||
ADD CONSTRAINT tag_notebook_pkey PRIMARY KEY (user_id, notebook_id, tag_id);
|
||||
|
||||
ALTER TABLE ONLY tag_note
|
||||
ADD CONSTRAINT tag_note_pkey PRIMARY KEY (note_id, tag_id);
|
||||
|
||||
ALTER TABLE ONLY invite
|
||||
ADD CONSTRAINT invite_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: luminotes_user_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY luminotes_user
|
||||
ADD CONSTRAINT luminotes_user_pkey PRIMARY KEY (id, revision);
|
||||
|
||||
|
||||
--
|
||||
-- Name: note_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY note
|
||||
ADD CONSTRAINT note_pkey PRIMARY KEY (id, revision);
|
||||
|
||||
|
||||
--
|
||||
-- Name: notebook_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY notebook
|
||||
ADD CONSTRAINT notebook_pkey PRIMARY KEY (id, revision);
|
||||
|
||||
|
||||
--
|
||||
-- Name: password_reset_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY password_reset
|
||||
ADD CONSTRAINT password_reset_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: download_access_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY download_access
|
||||
ADD CONSTRAINT download_access_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_notebook_pkey; Type: CONSTRAINT; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY user_notebook
|
||||
ADD CONSTRAINT user_notebook_pkey PRIMARY KEY (user_id, notebook_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: file_note_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX file_note_id_index ON file USING btree (note_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: file_notebook_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX file_notebook_id_index ON file USING btree (notebook_id);
|
||||
|
||||
CREATE INDEX tag_notebook_id_index ON tag USING btree (notebook_id);
|
||||
|
||||
--
|
||||
-- Name: luminotes_group_pkey; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
CREATE INDEX tag_user_id_index ON tag USING btree (user_id);
|
||||
|
||||
CREATE INDEX luminotes_group_pkey ON luminotes_group USING btree (id, revision);
|
||||
|
||||
|
||||
--
|
||||
-- Name: luminotes_user_email_address_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX luminotes_user_email_address_index ON luminotes_user USING btree (email_address);
|
||||
|
||||
|
||||
--
|
||||
-- Name: luminotes_user_username_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX luminotes_user_username_index ON luminotes_user USING btree (username);
|
||||
|
||||
|
||||
--
|
||||
-- Name: note_notebook_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX note_notebook_id_index ON note USING btree (notebook_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: note_notebook_id_startup_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX note_notebook_id_startup_index ON note USING btree (notebook_id, startup);
|
||||
|
||||
|
||||
--
|
||||
-- Name: note_notebook_id_title_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX note_notebook_id_title_index ON note USING btree (notebook_id, md5(title));
|
||||
|
||||
|
||||
--
|
||||
-- Name: password_reset_email_address_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX password_reset_email_address_index ON password_reset USING btree (email_address);
|
||||
|
||||
|
||||
--
|
||||
-- Name: download_access_transaction_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX download_access_transaction_id_index ON download_access USING btree (transaction_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: search_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX search_index ON note USING gist (search);
|
||||
|
||||
|
||||
--
|
||||
-- Name: search_update; Type: TRIGGER; Schema: public; Owner: luminotes
|
||||
--
|
||||
|
||||
CREATE TRIGGER search_update
|
||||
BEFORE INSERT OR UPDATE ON note
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE tsearch2('search', 'drop_html_tags', 'title', 'contents');
|
||||
|
||||
|
||||
--
|
||||
-- Name: public; Type: ACL; Schema: -; Owner: postgres
|
||||
--
|
||||
|
||||
REVOKE ALL ON SCHEMA public FROM PUBLIC;
|
||||
REVOKE ALL ON SCHEMA public FROM postgres;
|
||||
GRANT ALL ON SCHEMA public TO postgres;
|
||||
GRANT ALL ON SCHEMA public TO PUBLIC;
|
||||
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--
|
||||
-- Name: file; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
-- SQLite database schema
|
||||
--
|
||||
|
||||
CREATE TABLE file (
|
||||
|
@ -12,10 +12,27 @@ CREATE TABLE file (
|
|||
content_type text
|
||||
);
|
||||
|
||||
CREATE TABLE tag (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone,
|
||||
notebook_id text,
|
||||
user_id text,
|
||||
name text,
|
||||
description text
|
||||
);
|
||||
|
||||
--
|
||||
-- Name: invite; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
CREATE TABLE tag_notebook (
|
||||
notebook_id text,
|
||||
tag_id text,
|
||||
value text,
|
||||
user_id text
|
||||
);
|
||||
|
||||
CREATE TABLE tag_note (
|
||||
note_id text,
|
||||
tag_id text,
|
||||
value text
|
||||
);
|
||||
|
||||
CREATE TABLE invite (
|
||||
id text NOT NULL,
|
||||
|
@ -28,30 +45,15 @@ CREATE TABLE invite (
|
|||
redeemed_user_id text
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: luminotes_group; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE luminotes_group (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
name text
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: luminotes_group_current; Type: VIEW; Schema: public; Owner: luminotes
|
||||
--
|
||||
|
||||
CREATE VIEW luminotes_group_current AS
|
||||
SELECT id, revision, name FROM luminotes_group WHERE (luminotes_group.revision IN (SELECT max(sub_group.revision) AS max FROM luminotes_group sub_group WHERE (sub_group.id = luminotes_group.id)));
|
||||
|
||||
|
||||
--
|
||||
-- Name: luminotes_user; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE luminotes_user (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
|
@ -63,19 +65,9 @@ CREATE TABLE luminotes_user (
|
|||
rate_plan integer
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: luminotes_user_current; Type: VIEW; Schema: public; Owner: luminotes
|
||||
--
|
||||
|
||||
CREATE VIEW luminotes_user_current AS
|
||||
SELECT id, revision, username, salt, password_hash, email_address, storage_bytes, rate_plan FROM luminotes_user WHERE (luminotes_user.revision IN (SELECT max(sub_user.revision) AS max FROM luminotes_user sub_user WHERE (sub_user.id = luminotes_user.id)));
|
||||
|
||||
|
||||
--
|
||||
-- Name: note; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE note (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
|
@ -89,19 +81,9 @@ CREATE TABLE note (
|
|||
user_id text
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: note_current; Type: VIEW; Schema: public; Owner: luminotes
|
||||
--
|
||||
|
||||
CREATE VIEW note_current AS
|
||||
SELECT id, revision, title, contents, notebook_id, startup, deleted_from_id, rank, search, user_id FROM note WHERE (note.revision IN (SELECT max(sub_note.revision) AS max FROM note sub_note WHERE (sub_note.id = note.id)));
|
||||
|
||||
|
||||
--
|
||||
-- Name: notebook; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE notebook (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
|
@ -111,19 +93,9 @@ CREATE TABLE notebook (
|
|||
user_id text
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: notebook_current; Type: VIEW; Schema: public; Owner: luminotes
|
||||
--
|
||||
|
||||
CREATE VIEW notebook_current AS
|
||||
SELECT id, revision, name, trash_id, deleted, user_id FROM notebook WHERE (notebook.revision IN (SELECT max(sub_notebook.revision) AS max FROM notebook sub_notebook WHERE (sub_notebook.id = notebook.id)));
|
||||
|
||||
|
||||
--
|
||||
-- Name: password_reset; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE password_reset (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
|
@ -131,10 +103,6 @@ CREATE TABLE password_reset (
|
|||
redeemed boolean
|
||||
);
|
||||
|
||||
|
||||
-- Name: download_access; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE download_access (
|
||||
id text NOT NULL,
|
||||
revision timestamp with time zone NOT NULL,
|
||||
|
@ -142,118 +110,69 @@ CREATE TABLE download_access (
|
|||
transaction_id text
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_group; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE user_group (
|
||||
user_id text NOT NULL,
|
||||
group_id text NOT NULL,
|
||||
"admin" boolean DEFAULT false
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: user_notebook; Type: TABLE; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE user_notebook (
|
||||
user_id text NOT NULL,
|
||||
notebook_id text NOT NULL,
|
||||
read_write boolean DEFAULT false,
|
||||
"owner" boolean DEFAULT false,
|
||||
rank numeric
|
||||
rank numeric,
|
||||
own_notes_only boolean DEFAULT false
|
||||
);
|
||||
|
||||
CREATE INDEX file_pkey ON file (id);
|
||||
|
||||
CREATE INDEX tag_pkey ON tag (id);
|
||||
|
||||
--
|
||||
-- Name: file_note_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
CREATE INDEX tag_notebook_pkey ON tag_notebook (user_id, notebook_id, tag_id);
|
||||
|
||||
CREATE INDEX tag_note_pkey ON tag_note (note_id, tag_id);
|
||||
|
||||
CREATE INDEX invite_pkey ON invite (id);
|
||||
|
||||
CREATE INDEX luminotes_user_pkey ON luminotes_user (id, revision);
|
||||
|
||||
CREATE INDEX note_pkey ON note (id, revision);
|
||||
|
||||
CREATE INDEX notebook_pkey ON notebook (id, revision);
|
||||
|
||||
CREATE INDEX password_reset_pkey ON password_reset (id);
|
||||
|
||||
CREATE INDEX download_access_pkey ON download_access (id);
|
||||
|
||||
CREATE INDEX user_notebook_pkey ON user_notebook (user_id, notebook_id);
|
||||
|
||||
CREATE INDEX file_note_id_index ON file (note_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: file_notebook_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX file_notebook_id_index ON file (notebook_id);
|
||||
|
||||
CREATE INDEX tag_notebook_id_index ON tag (notebook_id);
|
||||
|
||||
--
|
||||
-- Name: luminotes_group_pkey; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
CREATE INDEX tag_user_id_index ON tag (user_id);
|
||||
|
||||
CREATE INDEX luminotes_group_pkey ON luminotes_group (id, revision);
|
||||
|
||||
|
||||
--
|
||||
-- Name: luminotes_user_email_address_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX luminotes_user_email_address_index ON luminotes_user (email_address);
|
||||
|
||||
|
||||
--
|
||||
-- Name: luminotes_user_username_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX luminotes_user_username_index ON luminotes_user (username);
|
||||
|
||||
|
||||
--
|
||||
-- Name: note_notebook_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX note_notebook_id_index ON note (notebook_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: note_notebook_id_startup_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX note_notebook_id_startup_index ON note (notebook_id, startup);
|
||||
|
||||
|
||||
--
|
||||
-- Name: note_notebook_id_title_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX note_notebook_id_title_index ON note (notebook_id, title);
|
||||
|
||||
|
||||
--
|
||||
-- Name: password_reset_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX password_reset_id_index ON password_reset (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: password_reset_email_address_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX password_reset_email_address_index ON password_reset (email_address);
|
||||
|
||||
|
||||
-- Name: download_access_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX download_access_id_index ON password_reset (id);
|
||||
|
||||
|
||||
-- Name: download_access_transaction_id_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX download_access_transaction_id_index ON download_access (transaction_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: search_index; Type: INDEX; Schema: public; Owner: luminotes; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX search_index ON note (search);
|
||||
|
||||
-- vim: ft=sql
|
||||
|
|
|
@ -12,11 +12,11 @@ class Test_notebook( object ):
|
|||
self.trash_name = u"trash"
|
||||
self.user_id = u"me"
|
||||
self.delta = timedelta( seconds = 1 )
|
||||
self.read_write = True
|
||||
self.read_write = Notebook.READ_WRITE
|
||||
self.owner = False
|
||||
self.rank = 17.5
|
||||
|
||||
self.trash = Notebook.create( self.trash_id, self.trash_name, read_write = False, deleted = False, user_id = self.user_id )
|
||||
self.trash = Notebook.create( self.trash_id, self.trash_name, read_write = Notebook.READ_ONLY, deleted = False, user_id = self.user_id )
|
||||
self.notebook = Notebook.create( self.object_id, self.name, trash_id = self.trash.object_id, deleted = False, user_id = self.user_id, read_write = self.read_write, owner = self.owner, rank = self.rank )
|
||||
self.note = Note.create( "19", u"<h3>title</h3>blah" )
|
||||
|
||||
|
@ -24,7 +24,6 @@ class Test_notebook( object ):
|
|||
assert self.notebook.object_id == self.object_id
|
||||
assert datetime.now( tz = utc ) - self.notebook.revision < self.delta
|
||||
assert self.notebook.name == self.name
|
||||
assert self.notebook.read_write == True
|
||||
assert self.notebook.trash_id == self.trash_id
|
||||
assert self.notebook.deleted == False
|
||||
assert self.notebook.user_id == self.user_id
|
||||
|
@ -35,14 +34,104 @@ class Test_notebook( object ):
|
|||
assert self.trash.object_id == self.trash_id
|
||||
assert datetime.now( tz = utc ) - self.trash.revision < self.delta
|
||||
assert self.trash.name == self.trash_name
|
||||
assert self.trash.read_write == False
|
||||
assert self.trash.trash_id == None
|
||||
assert self.trash.deleted == False
|
||||
assert self.trash.user_id == self.user_id
|
||||
assert self.trash.read_write == False
|
||||
assert self.trash.read_write == Notebook.READ_ONLY
|
||||
assert self.trash.owner == True
|
||||
assert self.trash.rank == None
|
||||
|
||||
def test_create_read_write_true( self ):
|
||||
notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = True, owner = self.owner, rank = self.rank )
|
||||
|
||||
assert notebook.object_id == self.object_id
|
||||
assert datetime.now( tz = utc ) - notebook.revision < self.delta
|
||||
assert notebook.name == self.name
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.deleted == False
|
||||
assert notebook.user_id == self.user_id
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == self.owner
|
||||
assert notebook.rank == self.rank
|
||||
|
||||
def test_create_read_write_false( self ):
|
||||
notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = False, owner = self.owner, rank = self.rank )
|
||||
|
||||
assert notebook.object_id == self.object_id
|
||||
assert datetime.now( tz = utc ) - notebook.revision < self.delta
|
||||
assert notebook.name == self.name
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.deleted == False
|
||||
assert notebook.user_id == self.user_id
|
||||
assert notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == self.owner
|
||||
assert notebook.rank == self.rank
|
||||
|
||||
def test_create_read_write_none( self ):
|
||||
notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = None, owner = self.owner, rank = self.rank )
|
||||
|
||||
assert notebook.object_id == self.object_id
|
||||
assert datetime.now( tz = utc ) - notebook.revision < self.delta
|
||||
assert notebook.name == self.name
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.deleted == False
|
||||
assert notebook.user_id == self.user_id
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == self.owner
|
||||
assert notebook.rank == self.rank
|
||||
|
||||
def test_create_read_write_true_and_own_notes_only_true( self ):
|
||||
notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = True, owner = self.owner, rank = self.rank, own_notes_only = True )
|
||||
|
||||
assert notebook.object_id == self.object_id
|
||||
assert datetime.now( tz = utc ) - notebook.revision < self.delta
|
||||
assert notebook.name == self.name
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.deleted == False
|
||||
assert notebook.user_id == self.user_id
|
||||
assert notebook.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES
|
||||
assert notebook.owner == self.owner
|
||||
assert notebook.rank == self.rank
|
||||
|
||||
def test_create_read_write_false_and_own_notes_only_true( self ):
|
||||
notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = False, owner = self.owner, rank = self.rank, own_notes_only = True )
|
||||
|
||||
assert notebook.object_id == self.object_id
|
||||
assert datetime.now( tz = utc ) - notebook.revision < self.delta
|
||||
assert notebook.name == self.name
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.deleted == False
|
||||
assert notebook.user_id == self.user_id
|
||||
assert notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == self.owner
|
||||
assert notebook.rank == self.rank
|
||||
|
||||
def test_create_read_write_false_and_own_notes_only_false( self ):
|
||||
notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = False, owner = self.owner, rank = self.rank, own_notes_only = False )
|
||||
|
||||
assert notebook.object_id == self.object_id
|
||||
assert datetime.now( tz = utc ) - notebook.revision < self.delta
|
||||
assert notebook.name == self.name
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.deleted == False
|
||||
assert notebook.user_id == self.user_id
|
||||
assert notebook.read_write == Notebook.READ_ONLY
|
||||
assert notebook.owner == self.owner
|
||||
assert notebook.rank == self.rank
|
||||
|
||||
def test_create_read_write_true_and_own_notes_only_false( self ):
|
||||
notebook = Notebook.create( self.object_id, self.name, trash_id = None, deleted = False, user_id = self.user_id, read_write = True, owner = self.owner, rank = self.rank, own_notes_only = False )
|
||||
|
||||
assert notebook.object_id == self.object_id
|
||||
assert datetime.now( tz = utc ) - notebook.revision < self.delta
|
||||
assert notebook.name == self.name
|
||||
assert notebook.trash_id == None
|
||||
assert notebook.deleted == False
|
||||
assert notebook.user_id == self.user_id
|
||||
assert notebook.read_write == Notebook.READ_WRITE
|
||||
assert notebook.owner == self.owner
|
||||
assert notebook.rank == self.rank
|
||||
|
||||
def test_set_name( self ):
|
||||
new_name = u"my new notebook"
|
||||
previous_revision = self.notebook.revision
|
||||
|
@ -52,10 +141,31 @@ class Test_notebook( object ):
|
|||
assert self.notebook.revision > previous_revision
|
||||
|
||||
def test_set_read_write( self ):
|
||||
original_revision = self.notebook.revision
|
||||
self.notebook.read_write = Notebook.READ_WRITE_FOR_OWN_NOTES
|
||||
|
||||
assert self.notebook.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES
|
||||
assert self.notebook.revision == original_revision
|
||||
|
||||
def test_set_read_write_true( self ):
|
||||
original_revision = self.notebook.revision
|
||||
self.notebook.read_write = True
|
||||
|
||||
assert self.notebook.read_write == True
|
||||
assert self.notebook.read_write == Notebook.READ_WRITE
|
||||
assert self.notebook.revision == original_revision
|
||||
|
||||
def test_set_read_write_false( self ):
|
||||
original_revision = self.notebook.revision
|
||||
self.notebook.read_write = False
|
||||
|
||||
assert self.notebook.read_write == Notebook.READ_ONLY
|
||||
assert self.notebook.revision == original_revision
|
||||
|
||||
def test_set_read_write_none( self ):
|
||||
original_revision = self.notebook.revision
|
||||
self.notebook.read_write = None
|
||||
|
||||
assert self.notebook.read_write == Notebook.READ_WRITE
|
||||
assert self.notebook.revision == original_revision
|
||||
|
||||
def test_set_deleted( self ):
|
||||
|
@ -84,7 +194,7 @@ class Test_notebook( object ):
|
|||
|
||||
assert d.get( "name" ) == self.name
|
||||
assert d.get( "trash_id" ) == self.trash.object_id
|
||||
assert d.get( "read_write" ) == True
|
||||
assert d.get( "read_write" ) == self.read_write
|
||||
assert d.get( "deleted" ) == self.notebook.deleted
|
||||
assert d.get( "user_id" ) == self.notebook.user_id
|
||||
assert d.get( "object_id" ) == self.notebook.object_id
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
from pytz import utc
|
||||
from datetime import datetime, timedelta
|
||||
from model.Tag import Tag
|
||||
|
||||
|
||||
class Test_tag( object ):
|
||||
def setUp( self ):
|
||||
self.object_id = u"17"
|
||||
self.notebook_id = u"19"
|
||||
self.user_id = u"20"
|
||||
self.name = u"mytag"
|
||||
self.description = u"this is my tag"
|
||||
self.value = u"a value"
|
||||
self.delta = timedelta( seconds = 1 )
|
||||
|
||||
self.tag = Tag.create( self.object_id, self.notebook_id, self.user_id, self.name,
|
||||
self.description, self.value )
|
||||
|
||||
def test_create( self ):
|
||||
assert self.tag.object_id == self.object_id
|
||||
assert self.tag.notebook_id == self.notebook_id
|
||||
assert self.tag.user_id == self.user_id
|
||||
assert self.tag.name == self.name
|
||||
assert self.tag.description == self.description
|
||||
assert self.tag.value == self.value
|
||||
|
||||
def test_to_dict( self ):
|
||||
d = self.tag.to_dict()
|
||||
|
||||
assert d.get( "object_id" ) == self.object_id
|
||||
assert datetime.now( tz = utc ) - d.get( "revision" ) < self.delta
|
||||
assert d.get( "notebook_id" ) == self.notebook_id
|
||||
assert d.get( "user_id" ) == self.user_id
|
||||
assert d.get( "name" ) == self.name
|
||||
assert d.get( "description" ) == self.description
|
||||
assert d.get( "value" ) == self.value
|
1
setup.py
1
setup.py
|
@ -253,6 +253,7 @@ data_files = [
|
|||
( "static/images/toolbar/small", files( "static/images/toolbar/small/*.*", excludes = [ "static/images/toolbar/small/*.xcf" ] ) ),
|
||||
( "static/js", files( "static/js/*.*" ) ),
|
||||
( "static/js", files( "static/js/*_LICENSE" ) ),
|
||||
( "model/delta", files( "model/delta/*.sqlite" ) ),
|
||||
]
|
||||
|
||||
package_data = { ".": sum( [ pair[ 1 ] for pair in data_files ], [] ) }
|
||||
|
|
|
@ -20,14 +20,12 @@ The Luminotes user guide explains every feature of Luminotes in full detail,
|
|||
and it even includes tips for organizing your wiki.
|
||||
</p>
|
||||
|
||||
<!--
|
||||
<h4><!a href="/forums/" target="_top">discussion forums</a></h4>
|
||||
<h4><a href="/forums/" target="_top">discussion forums</a></h4>
|
||||
|
||||
<p>
|
||||
In the Luminotes discussion forums, you can ask about Luminotes features and
|
||||
chat with your fellow Luminoters.
|
||||
</p>
|
||||
-->
|
||||
|
||||
<h4><a href="/blog" target="_top">blog</a></h4>
|
||||
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
IMAGE_DIR = "/static/images/";
|
||||
NOTEBOOK_READ_ONLY = 0;
|
||||
NOTEBOOK_READ_WRITE = 1;
|
||||
NOTEBOOK_READ_WRITE_FOR_OWN_NOTES = 2;
|
||||
|
||||
|
||||
function Wiki( invoker ) {
|
||||
this.next_id = null;
|
||||
|
@ -11,6 +15,7 @@ function Wiki( invoker ) {
|
|||
this.open_editors = new Array(); // map of open notes: lowercase note title to editor
|
||||
this.search_results_editor = null; // editor for display of search results
|
||||
this.invoker = invoker;
|
||||
this.user = evalJSON( getElement( "user" ).value );
|
||||
this.rate_plan = evalJSON( getElement( "rate_plan" ).value );
|
||||
this.yearly = evalJSON( getElement( "yearly" ).value );
|
||||
this.storage_usage_high = false;
|
||||
|
@ -18,7 +23,6 @@ function Wiki( invoker ) {
|
|||
this.invite_id = getElement( "invite_id" ).value;
|
||||
this.after_login = getElement( "after_login" ).value;
|
||||
this.signup_plan = getElement( "signup_plan" ).value;
|
||||
this.email_address = getElement( "email_address" ).value || "";
|
||||
this.groups = evalJSON( getElement( "groups" ).value );
|
||||
this.font_size = null;
|
||||
this.small_toolbar = false;
|
||||
|
@ -48,7 +52,7 @@ function Wiki( invoker ) {
|
|||
this.notebook = notebook;
|
||||
}
|
||||
|
||||
if ( this.notebook && this.notebook.read_write ) {
|
||||
if ( this.notebook && this.notebook.read_write != NOTEBOOK_READ_ONLY ) {
|
||||
var unsupported_agent = null;
|
||||
var beta_agent = null;
|
||||
|
||||
|
@ -76,7 +80,7 @@ function Wiki( invoker ) {
|
|||
skip_empty_message
|
||||
);
|
||||
|
||||
this.display_storage_usage( evalJSON( getElement( "storage_bytes" ).value || "0" ) );
|
||||
this.display_storage_usage( this.user.storage_bytes || "0" );
|
||||
|
||||
connect( this.invoker, "error_message", this, "display_error" );
|
||||
connect( this.invoker, "message", this, "display_message" );
|
||||
|
@ -116,11 +120,11 @@ function Wiki( invoker ) {
|
|||
}
|
||||
|
||||
var rename = evalJSON( getElement( "rename" ).value );
|
||||
if ( rename && this.notebook.read_write )
|
||||
if ( rename && this.notebook.read_write == NOTEBOOK_READ_WRITE )
|
||||
this.start_notebook_rename();
|
||||
|
||||
// if a notebook was just deleted, show a message with an undo button
|
||||
if ( deleted_id && this.notebook.read_write ) {
|
||||
if ( deleted_id && this.notebook.read_write == NOTEBOOK_READ_WRITE ) {
|
||||
var undo_button = createDOM( "input", {
|
||||
"type": "button",
|
||||
"class": "message_button",
|
||||
|
@ -276,7 +280,8 @@ Wiki.prototype.populate = function ( startup_notes, current_notes, note_read_wri
|
|||
startup_note.deleted_from_id,
|
||||
startup_note.revision,
|
||||
startup_note.creation,
|
||||
this.notebook.read_write, false, focus
|
||||
this.notebook.read_write, false, focus, null,
|
||||
startup_note.user_id
|
||||
);
|
||||
|
||||
if ( startup_note.title )
|
||||
|
@ -296,7 +301,8 @@ Wiki.prototype.populate = function ( startup_notes, current_notes, note_read_wri
|
|||
note.deleted_from_id,
|
||||
note.revision,
|
||||
note.creation,
|
||||
this.notebook.read_write && note_read_write, false, focus
|
||||
this.notebook.read_write != NOTEBOOK_READ_ONLY && note_read_write, false, focus, null,
|
||||
note.user_id
|
||||
);
|
||||
focus = false;
|
||||
}
|
||||
|
@ -308,7 +314,7 @@ Wiki.prototype.populate = function ( startup_notes, current_notes, note_read_wri
|
|||
if ( empty_trash_link )
|
||||
connect( empty_trash_link, "onclick", function ( event ) { self.delete_all_editors( event ); } );
|
||||
|
||||
if ( this.notebook.read_write ) {
|
||||
if ( this.notebook.read_write != NOTEBOOK_READ_ONLY ) {
|
||||
connect( window, "onunload", function ( event ) { self.editor_focused( null, true ); } );
|
||||
connect( "newNote", "onclick", this, "create_blank_editor" );
|
||||
connect( "createLink", "onclick", this, "toggle_link_button" );
|
||||
|
@ -453,7 +459,7 @@ Wiki.prototype.create_blank_editor = function ( event ) {
|
|||
}
|
||||
}
|
||||
|
||||
var editor = this.create_editor( undefined, 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, null, this.user.object_id );
|
||||
this.increment_total_notes_count();
|
||||
this.blank_editor_id = editor.id;
|
||||
signal( this, "note_added", editor );
|
||||
|
@ -693,6 +699,7 @@ Wiki.prototype.parse_loaded_editor = function ( result, note_title, requested_re
|
|||
var actual_creation = result.note.creation;
|
||||
var note_text = result.note.contents;
|
||||
var deleted_from_id = result.note.deleted;
|
||||
var user_id = result.note.user_id;
|
||||
} else {
|
||||
// if the title looks like a URL, then make it a link to an external site
|
||||
if ( /^\w+:\/\//.test( note_title ) ) {
|
||||
|
@ -707,6 +714,7 @@ Wiki.prototype.parse_loaded_editor = function ( result, note_title, requested_re
|
|||
var deleted_from_id = null;
|
||||
var actual_revision = null;
|
||||
var actual_creation = null;
|
||||
var user_id = null;
|
||||
this.increment_total_notes_count();
|
||||
}
|
||||
|
||||
|
@ -716,7 +724,7 @@ Wiki.prototype.parse_loaded_editor = function ( result, note_title, requested_re
|
|||
var read_write = this.notebook.read_write;
|
||||
|
||||
var self = this;
|
||||
var editor = this.create_editor( id, note_text, deleted_from_id, actual_revision, actual_creation, read_write, true, false, position_after );
|
||||
var editor = this.create_editor( id, note_text, deleted_from_id, actual_revision, actual_creation, read_write, true, false, position_after, user_id );
|
||||
if ( !requested_revision )
|
||||
connect( editor, "init_complete", function () { signal( self, "note_added", editor ); } );
|
||||
id = editor.id;
|
||||
|
@ -726,10 +734,21 @@ 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, creation, read_write, highlight, focus, position_after ) {
|
||||
Wiki.prototype.create_editor = function ( id, note_text, deleted_from_id, revision, creation, read_write, highlight, focus, position_after, user_id ) {
|
||||
var self = this;
|
||||
var dirty = false;
|
||||
|
||||
if ( read_write == NOTEBOOK_READ_ONLY )
|
||||
read_write = false;
|
||||
else if ( read_write == NOTEBOOK_READ_WRITE )
|
||||
read_write = true;
|
||||
else if ( read_write == NOTEBOOK_READ_WRITE_FOR_OWN_NOTES ) {
|
||||
if ( user_id == this.user.object_id )
|
||||
read_write = true;
|
||||
else
|
||||
read_write = false;
|
||||
}
|
||||
|
||||
if ( isUndefinedOrNull( id ) ) {
|
||||
if ( this.notebook.read_write ) {
|
||||
id = this.next_id;
|
||||
|
@ -743,7 +762,7 @@ Wiki.prototype.create_editor = function ( id, note_text, deleted_from_id, revisi
|
|||
}
|
||||
|
||||
// for read-only notes within read-write notebooks, tack the revision timestamp onto the start of the note text
|
||||
if ( !read_write && this.notebook.read_write && revision ) {
|
||||
if ( !read_write && this.notebook.read_write == NOTEBOOK_READ_WRITE && revision ) {
|
||||
var short_revision = this.brief_revision( revision );
|
||||
var note_id = id.split( ' ' )[ 0 ];
|
||||
note_text = '<p>Previous revision from ' + short_revision + '</p>' +
|
||||
|
@ -984,7 +1003,7 @@ Wiki.prototype.editor_mouse_hovered = function ( editor, target ) {
|
|||
}
|
||||
|
||||
Wiki.prototype.key_pressed = function ( event ) {
|
||||
if ( !this.notebook.read_write )
|
||||
if ( this.notebook.read_write == NOTEBOOK_READ_ONLY )
|
||||
return;
|
||||
|
||||
var code = event.key().code;
|
||||
|
@ -1221,7 +1240,7 @@ Wiki.prototype.update_toolbar = function() {
|
|||
var link = null;
|
||||
|
||||
// a read-only notebook doesn't have a visible toolbar
|
||||
if ( !this.notebook.read_write )
|
||||
if ( this.notebook.read_write == NOTEBOOK_READ_ONLY )
|
||||
return;
|
||||
|
||||
if ( this.focused_editor ) {
|
||||
|
@ -1332,7 +1351,7 @@ Wiki.prototype.hide_editor = function ( event, editor ) {
|
|||
this.display_empty_message();
|
||||
} else {
|
||||
// before hiding an editor, save it
|
||||
if ( this.notebook.read_write && editor.read_write ) {
|
||||
if ( this.notebook.read_write != NOTEBOOK_READ_ONLY && editor.read_write ) {
|
||||
var self = this;
|
||||
this.save_editor( editor, false, function () {
|
||||
editor.shutdown();
|
||||
|
@ -1367,7 +1386,7 @@ Wiki.prototype.delete_editor = function ( event, editor ) {
|
|||
|
||||
var self = this;
|
||||
this.save_editor( editor, false, function () {
|
||||
if ( self.notebook.read_write && editor.read_write ) {
|
||||
if ( self.notebook.read_write != NOTEBOOK_READ_ONLY && editor.read_write ) {
|
||||
self.invoker.invoke( "/notebooks/delete_note", "POST", {
|
||||
"notebook_id": self.notebook_id,
|
||||
"note_id": editor.id
|
||||
|
@ -1414,7 +1433,7 @@ Wiki.prototype.undelete_editor_via_trash = function ( event, editor ) {
|
|||
if ( this.startup_notes[ editor.id ] )
|
||||
delete this.startup_notes[ editor.id ];
|
||||
|
||||
if ( this.notebook.read_write && editor.read_write ) {
|
||||
if ( this.notebook.read_write != NOTEBOOK_READ_ONLY && editor.read_write ) {
|
||||
var self = this;
|
||||
this.invoker.invoke( "/notebooks/undelete_note", "POST", {
|
||||
"notebook_id": editor.deleted_from_id,
|
||||
|
@ -1437,7 +1456,7 @@ Wiki.prototype.undelete_editor_via_trash = function ( event, editor ) {
|
|||
|
||||
Wiki.prototype.undelete_editor_via_undo = function( event, editor, position_after ) {
|
||||
if ( editor ) {
|
||||
if ( this.notebook.read_write && editor.read_write ) {
|
||||
if ( this.notebook.read_write != NOTEBOOK_READ_ONLY && editor.read_write ) {
|
||||
var self = this;
|
||||
this.invoker.invoke( "/notebooks/undelete_note", "POST", {
|
||||
"notebook_id": this.notebook_id,
|
||||
|
@ -1458,7 +1477,7 @@ Wiki.prototype.undelete_editor_via_undo = function( event, editor, position_afte
|
|||
}
|
||||
|
||||
Wiki.prototype.undelete_editor_via_undelete = function( event, note_id, position_after ) {
|
||||
if ( this.notebook.read_write ) {
|
||||
if ( this.notebook.read_write != NOTEBOOK_READ_ONLY ) {
|
||||
var self = this;
|
||||
this.invoker.invoke( "/notebooks/undelete_note", "POST", {
|
||||
"notebook_id": this.notebook_id,
|
||||
|
@ -1600,7 +1619,7 @@ Wiki.prototype.submit_form = function ( form ) {
|
|||
}
|
||||
} else if ( url == "/users/update_settings" ) {
|
||||
callback = function ( result ) {
|
||||
self.email_address = result.email_address || "";
|
||||
self.user.email_address = result.email_address || "";
|
||||
self.display_message( "Your account settings have been updated." );
|
||||
}
|
||||
} else if ( url == "/users/signup_group_member" ) {
|
||||
|
@ -1996,7 +2015,7 @@ Wiki.prototype.display_settings = function () {
|
|||
createDOM( "br", {} ),
|
||||
createDOM( "input",
|
||||
{ "type": "text", "name": "email_address", "id": "email_address", "class": "text_field",
|
||||
"size": "30", "maxlength": "60", "value": this.email_address || "" }
|
||||
"size": "30", "maxlength": "60", "value": this.user.email_address || "" }
|
||||
)
|
||||
),
|
||||
createDOM( "p", {},
|
||||
|
@ -2510,7 +2529,7 @@ Wiki.prototype.delete_all_editors = function ( event ) {
|
|||
|
||||
this.startup_notes = new Array();
|
||||
|
||||
if ( this.notebook.read_write ) {
|
||||
if ( this.notebook.read_write == NOTEBOOK_READ_WRITE ) {
|
||||
var self = this;
|
||||
this.invoker.invoke( "/notebooks/delete_all_notes", "POST", {
|
||||
"notebook_id": this.notebook_id
|
||||
|
|
|
@ -50,7 +50,7 @@ class Initializer( object ):
|
|||
def create_main_notebook( self ):
|
||||
# create the main notebook
|
||||
main_notebook_id = self.database.next_id( Notebook )
|
||||
self.main_notebook = Notebook.create( main_notebook_id, u"Luminotes" )
|
||||
self.main_notebook = Notebook.create( main_notebook_id, u"Luminotes", rank = 0 )
|
||||
self.database.save( self.main_notebook, commit = False )
|
||||
|
||||
# no need to create default notes for the desktop version
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/python2.4
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import cherrypy
|
||||
from datetime import datetime
|
||||
from controller.Database import Database
|
||||
from model.Notebook import Notebook
|
||||
from model.Note import Note
|
||||
from model.User import User
|
||||
from model.Tag import Tag
|
||||
|
||||
|
||||
class Thread_maker( object ):
|
||||
"""
|
||||
Create a thread for a new forum.
|
||||
"""
|
||||
def __init__( self, database, forum_name ):
|
||||
self.database = database
|
||||
self.forum_name = forum_name
|
||||
|
||||
self.make_thread()
|
||||
self.database.commit()
|
||||
|
||||
def make_thread( self ):
|
||||
title = u"Welcome to the Luminotes %s forum!" % self.forum_name
|
||||
|
||||
# create a notebook thread to go in the forum
|
||||
notebook_id = self.database.next_id( Notebook, commit = False )
|
||||
thread_notebook = Notebook.create(
|
||||
notebook_id,
|
||||
title,
|
||||
)
|
||||
self.database.save( thread_notebook, commit = False )
|
||||
|
||||
anonymous = self.database.select_one( User, User.sql_load_by_username( u"anonymous" ) )
|
||||
|
||||
# add a single welcome note to the new thread
|
||||
note_id = self.database.next_id( Note, commit = False )
|
||||
note = Note.create(
|
||||
note_id,
|
||||
u"""
|
||||
<h3>%s</h3> You can discuss any Luminotes %s topics here. This is a public discussion
|
||||
forum, so please keep that in mind when posting. And have fun.
|
||||
""" % ( title, self.forum_name ),
|
||||
notebook_id,
|
||||
startup = True,
|
||||
rank = 0,
|
||||
user_id = anonymous.object_id,
|
||||
creation = datetime.now(),
|
||||
)
|
||||
self.database.save( note, commit = False )
|
||||
|
||||
# load the forum tag, or create one if it doesn't exist
|
||||
tag = self.database.select_one( Tag, Tag.sql_load_by_name( u"forum", user_id = anonymous.object_id ) )
|
||||
if not tag:
|
||||
tag_id = self.database.next_id( Tag, commit = False )
|
||||
tag = Tag.create(
|
||||
tag_id,
|
||||
notebook_id = None, # this tag is not in the namespace of a single notebook
|
||||
user_id = anonymous.object_id,
|
||||
name = u"forum",
|
||||
description = u"discussion forum threads"
|
||||
)
|
||||
self.database.save( tag, commit = False )
|
||||
|
||||
# associate the forum tag with the previously created notebook thread, and set that
|
||||
# association's value to forum_name
|
||||
self.database.execute(
|
||||
anonymous.sql_save_notebook_tag( notebook_id, tag.object_id, value = self.forum_name ),
|
||||
commit = False,
|
||||
)
|
||||
|
||||
# give the anonymous user access to the new notebook thread
|
||||
self.database.execute(
|
||||
anonymous.sql_save_notebook( notebook_id, read_write = True, owner = False, own_notes_only = True ),
|
||||
commit = False,
|
||||
)
|
||||
|
||||
|
||||
def main( args ):
|
||||
import cherrypy
|
||||
from config import Common
|
||||
|
||||
cherrypy.config.update( Common.settings )
|
||||
desktop = False
|
||||
|
||||
if args and "-d" in args:
|
||||
from config import Development
|
||||
settings = Development.settings
|
||||
args.remove( "-d" )
|
||||
elif args and "-l" in args:
|
||||
from config import Desktop
|
||||
settings = Desktop.settings
|
||||
desktop = True
|
||||
args.remove( "-l" )
|
||||
else:
|
||||
from config import Production
|
||||
settings = Production.settings
|
||||
|
||||
cherrypy.config.update( settings )
|
||||
|
||||
database = Database(
|
||||
host = cherrypy.config.configMap[ u"global" ].get( u"luminotes.db_host" ),
|
||||
ssl_mode = cherrypy.config.configMap[ u"global" ].get( u"luminotes.db_ssl_mode" ),
|
||||
data_dir = ".",
|
||||
)
|
||||
ranker = Thread_maker( database, *args )
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main( sys.argv[ 1: ] )
|
|
@ -3,7 +3,7 @@ from Tags import Div, Img, A, P, Span, I, Br
|
|||
|
||||
|
||||
class Forums_page( Product_page ):
|
||||
def __init__( self, user, notebooks, first_notebook, login_url, logout_url, rate_plan ):
|
||||
def __init__( self, user, notebooks, first_notebook, login_url, logout_url, rate_plan, groups ):
|
||||
Product_page.__init__(
|
||||
self,
|
||||
user,
|
||||
|
@ -21,14 +21,15 @@ class Forums_page( Product_page ):
|
|||
),
|
||||
),
|
||||
Div(
|
||||
Span( A( u"general discussion", href = u"/forums/general" ), class_ = u"forum_title" ),
|
||||
P(
|
||||
u"""
|
||||
Swap tips about making the most out of your personal wiki, and discuss your ideas for
|
||||
new Luminotes features and enhancements.
|
||||
"""
|
||||
),
|
||||
Span( A( u"technical support", href = u"/forums/support" ), class_ = u"forum_title" ),
|
||||
P( u"Having a problem with your wiki? Something not working as expected? Ask about it here." ),
|
||||
|
||||
Span( A( u"feature requests", href = u"/forums/features" ), class_ = u"forum_title" ),
|
||||
P( u"Discuss your ideas for new Luminotes features and enhancements." ),
|
||||
|
||||
Span( A( u"general discussion", href = u"/forums/general" ), class_ = u"forum_title" ),
|
||||
P( u"Swap tips about making the most out of your personal wiki." ),
|
||||
class_ = u"forums_text",
|
||||
),
|
||||
class_ = u"forums_area",
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
from Tags import Div, P, Span, H4, A, Strong, Img, Input, Br
|
||||
from Rounded_div import Rounded_div
|
||||
from Search_form import Search_form
|
||||
from model.Notebook import Notebook
|
||||
|
||||
|
||||
class Link_area( Div ):
|
||||
def __init__( self, notebooks, notebook, parent_id, notebook_path, updates_path, user, rate_plan ):
|
||||
linked_notebooks = [ nb for nb in notebooks if
|
||||
( nb.read_write or not nb.name.startswith( u"Luminotes" ) ) and
|
||||
(
|
||||
nb.read_write == Notebook.READ_WRITE or
|
||||
( nb.read_write == Notebook.READ_ONLY and not nb.name.startswith( u"Luminotes" ) )
|
||||
) and
|
||||
nb.name not in ( u"trash" ) and
|
||||
nb.deleted is False
|
||||
]
|
||||
|
@ -17,7 +21,7 @@ class Link_area( Div ):
|
|||
Div(
|
||||
H4(
|
||||
u"this notebook",
|
||||
notebook.read_write and Input(
|
||||
notebook.read_write != Notebook.READ_ONLY and Input(
|
||||
type = u"button",
|
||||
class_ = u"note_button small_text",
|
||||
id = u"save_button",
|
||||
|
@ -60,7 +64,7 @@ class Link_area( Div ):
|
|||
class_ = u"link_area_item",
|
||||
) or None ),
|
||||
|
||||
notebook.read_write and Div(
|
||||
( notebook.read_write != Notebook.READ_ONLY ) and Div(
|
||||
A(
|
||||
u"nothing but notes",
|
||||
href = u"#",
|
||||
|
@ -70,7 +74,7 @@ class Link_area( Div ):
|
|||
class_ = u"link_area_item",
|
||||
) or None,
|
||||
|
||||
( not notebook.read_write and notebook.name != u"Luminotes" ) and Div(
|
||||
( notebook.read_write != Notebook.READ_WRITE and notebook.name != u"Luminotes" ) and Div(
|
||||
A(
|
||||
u"export",
|
||||
href = u"#",
|
||||
|
@ -80,7 +84,7 @@ class Link_area( Div ):
|
|||
class_ = u"link_area_item",
|
||||
) or None,
|
||||
|
||||
notebook.read_write and Span(
|
||||
( notebook.read_write == Notebook.READ_WRITE ) and Span(
|
||||
Div(
|
||||
A(
|
||||
u"import",
|
||||
|
|
|
@ -9,6 +9,7 @@ from Toolbar import Toolbar
|
|||
from Json import Json
|
||||
from Rounded_div import Rounded_div
|
||||
from config.Version import VERSION
|
||||
from model.Notebook import Notebook
|
||||
|
||||
|
||||
class Main_page( Page ):
|
||||
|
@ -105,7 +106,7 @@ class Main_page( Page ):
|
|||
except IOError:
|
||||
pass
|
||||
|
||||
if notebook.read_write is True:
|
||||
if notebook.read_write == Notebook.READ_WRITE:
|
||||
header_note_title = u"wiki"
|
||||
else:
|
||||
all_notes = startup_notes + notes
|
||||
|
@ -118,7 +119,7 @@ class Main_page( Page ):
|
|||
"Luminotes privacy policy": "privacy",
|
||||
}.get( header_note_title, header_note_title )
|
||||
|
||||
own_notebooks = [ nb for nb in notebooks if nb.read_write is True ]
|
||||
own_notebooks = [ nb for nb in notebooks if nb.read_write == Notebook.READ_WRITE ]
|
||||
header_notebook = own_notebooks and own_notebooks[ 0 ] or notebook
|
||||
|
||||
Page.__init__(
|
||||
|
@ -133,7 +134,7 @@ class Main_page( Page ):
|
|||
Script( type = u"text/javascript", src = u"/static/js/Invoker.js" ) or None,
|
||||
Script( type = u"text/javascript", src = u"/static/js/Editor.js" ) or None,
|
||||
Script( type = u"text/javascript", src = u"/static/js/Wiki.js" ) or None,
|
||||
Input( type = u"hidden", name = u"storage_bytes", id = u"storage_bytes", value = user.storage_bytes ),
|
||||
Input( type = u"hidden", name = u"user", id = u"user", value = json( user ) ),
|
||||
Input( type = u"hidden", name = u"rate_plan", id = u"rate_plan", value = json( rate_plan ) ),
|
||||
Input( type = u"hidden", name = u"yearly", id = u"yearly", value = json( signup_yearly ) ),
|
||||
Input( type = u"hidden", name = u"notebooks", id = u"notebooks", value = json( notebooks ) ),
|
||||
|
@ -148,7 +149,6 @@ class Main_page( Page ):
|
|||
Input( type = u"hidden", name = u"invite_id", id = u"invite_id", value = invite_id ),
|
||||
Input( type = u"hidden", name = u"after_login", id = u"after_login", value = after_login ),
|
||||
Input( type = u"hidden", name = u"signup_plan", id = u"signup_plan", value = signup_plan ),
|
||||
Input( type = u"hidden", name = u"email_address", id = u"email_address", value = user.email_address ),
|
||||
Input( type = u"hidden", name = u"groups", id = u"groups", value = json( groups ) ),
|
||||
Div(
|
||||
id = u"status_area",
|
||||
|
@ -158,7 +158,7 @@ class Main_page( Page ):
|
|||
Div(
|
||||
Note_tree_area(
|
||||
Toolbar(
|
||||
hide_toolbar = parent_id or not notebook.read_write
|
||||
hide_toolbar = parent_id or notebook.read_write == Notebook.READ_ONLY
|
||||
),
|
||||
notebook,
|
||||
root_notes,
|
||||
|
@ -168,7 +168,7 @@ class Main_page( Page ):
|
|||
id = u"left_area",
|
||||
),
|
||||
Div(
|
||||
notebook.read_write and Noscript(
|
||||
( notebook.read_write != Notebook.READ_ONLY ) and Noscript(
|
||||
P( Strong(
|
||||
u"""
|
||||
Luminotes requires JavaScript to be enabled in your web browser in order to edit
|
||||
|
@ -181,7 +181,7 @@ class Main_page( Page ):
|
|||
( notebook.name == u"Luminotes" and title == u"source code" ) and \
|
||||
Strong( "%s %s" % ( notebook.name, VERSION ) ) or \
|
||||
Span(
|
||||
( notebook.name == u"trash" or not notebook.read_write ) \
|
||||
( notebook.name == u"trash" or notebook.read_write != Notebook.READ_WRITE ) \
|
||||
and Strong( notebook.name ) \
|
||||
or Span( Strong( notebook.name ), id = u"notebook_header_name", title = "Rename this notebook." ),
|
||||
),
|
||||
|
@ -204,7 +204,7 @@ class Main_page( Page ):
|
|||
Span( id = u"notes_top" ),
|
||||
id = u"notes",
|
||||
),
|
||||
notebook.read_write and Div(
|
||||
( notebook.read_write != Notebook.READ_ONLY ) and Div(
|
||||
id = u"blank_note_stub",
|
||||
class_ = u"blank_note_stub_hidden_border",
|
||||
) or None,
|
||||
|
|
Reference in New Issue