Can now create and rename notebooks. Still need to implement deletion and some other niceties.
This commit is contained in:
parent
0869ae996b
commit
b22c784d39
4
NEWS
4
NEWS
|
@ -1,2 +1,6 @@
|
|||
1.0.1: November ??, 2007
|
||||
* Ability to create, rename, delete, and switch between multiple wiki
|
||||
notebooks in a single account.
|
||||
|
||||
1.0.0: November 12, 2007
|
||||
* Initial release.
|
||||
|
|
|
@ -8,6 +8,7 @@ from Expire import strongly_expire
|
|||
from Html_nuker import Html_nuker
|
||||
from model.Notebook import Notebook
|
||||
from model.Note import Note
|
||||
from model.User import User
|
||||
from view.Main_page import Main_page
|
||||
from view.Json import Json
|
||||
from view.Html_file import Html_file
|
||||
|
@ -702,6 +703,85 @@ class Notebooks( object ):
|
|||
notes = startup_notes + other_notes,
|
||||
)
|
||||
|
||||
@expose( view = Json )
|
||||
@grab_user_id
|
||||
@validate(
|
||||
user_id = Valid_id( none_okay = True ),
|
||||
)
|
||||
def create( self, user_id ):
|
||||
"""
|
||||
Create a new notebook and give it a default name.
|
||||
|
||||
@type user_id: unicode or NoneType
|
||||
@param user_id: id of current logged-in user (if any)
|
||||
@rtype dict
|
||||
@return { "redirect": notebookurl }
|
||||
@raise Access_error: the current user doesn't have access to create a notebook
|
||||
@raise Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
if user_id is None:
|
||||
raise Access_error()
|
||||
|
||||
user = self.__database.load( User, user_id )
|
||||
|
||||
# create the notebook along with a trash
|
||||
trash_id = self.__database.next_id( Notebook, commit = False )
|
||||
trash = Notebook.create( trash_id, u"trash" )
|
||||
self.__database.save( trash, commit = False )
|
||||
|
||||
notebook_id = self.__database.next_id( Notebook, commit = False )
|
||||
notebook = Notebook.create( notebook_id, u"new notebook", trash_id )
|
||||
self.__database.save( notebook, commit = False )
|
||||
|
||||
# record the fact that the user has access to their new notebook
|
||||
self.__database.execute( user.sql_save_notebook( notebook_id, read_write = True ), commit = False )
|
||||
self.__database.execute( user.sql_save_notebook( trash_id, read_write = True ), commit = False )
|
||||
self.__database.commit()
|
||||
|
||||
return dict(
|
||||
redirect = u"/notebooks/%s" % notebook_id,
|
||||
)
|
||||
|
||||
@expose( view = Json )
|
||||
@grab_user_id
|
||||
@validate(
|
||||
notebook_id = Valid_id(),
|
||||
name = Valid_string( min = 1, max = 100 ),
|
||||
user_id = Valid_id( none_okay = True ),
|
||||
)
|
||||
def rename( self, notebook_id, name, user_id ):
|
||||
"""
|
||||
Change the name of the given notebook.
|
||||
|
||||
@type notebook_id: unicode
|
||||
@param notebook_id: id of notebook to rename
|
||||
@type name: unicode
|
||||
@param name: new name of the notebook
|
||||
@type user_id: unicode or NoneType
|
||||
@param user_id: id of current logged-in user (if any)
|
||||
@rtype dict
|
||||
@return {}
|
||||
@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 )
|
||||
|
||||
if not notebook:
|
||||
raise Access_error()
|
||||
|
||||
# prevent just anyone from making official Luminotes notebooks
|
||||
if name.startswith( u"Luminotes" ) and not notebook.name.startswith( u"Luminotes" ):
|
||||
raise Access_error( "That notebook name is not available. Please try a different one." )
|
||||
|
||||
notebook.name = name
|
||||
self.__database.save( notebook, commit = False )
|
||||
self.__database.commit()
|
||||
|
||||
return dict()
|
||||
|
||||
def load_recent_notes( self, notebook_id, start = 0, count = 10, user_id = None ):
|
||||
"""
|
||||
Provide the information necessary to display the page for a particular notebook's most recent
|
||||
|
|
|
@ -108,6 +108,7 @@ class Root( object ):
|
|||
|
||||
result = self.__users.current( user_id )
|
||||
main_notebooks = [ nb for nb in result[ "notebooks" ] if nb.name == u"Luminotes" ]
|
||||
|
||||
result.update( self.__notebooks.contents( main_notebooks[ 0 ].object_id, user_id = user_id ) )
|
||||
|
||||
return result
|
||||
|
|
|
@ -1650,6 +1650,94 @@ class Test_notebooks( Test_controller ):
|
|||
|
||||
assert result.get( "error" )
|
||||
|
||||
def test_create( self ):
|
||||
self.login()
|
||||
|
||||
result = self.http_post( "/notebooks/create", dict(), session_id = self.session_id )
|
||||
|
||||
assert result[ u"redirect" ].startswith( u"/notebooks/" )
|
||||
|
||||
new_notebook_id = result[ u"redirect" ].split( u"/notebooks/" )[ -1 ]
|
||||
notebook = self.database.last_saved_obj
|
||||
|
||||
assert isinstance( notebook, Notebook )
|
||||
assert notebook.object_id == new_notebook_id
|
||||
assert notebook.name == u"new notebook"
|
||||
assert notebook.read_write == True
|
||||
assert notebook.trash_id
|
||||
|
||||
def test_contents_after_create( self ):
|
||||
self.login()
|
||||
|
||||
result = self.http_post( "/notebooks/create", dict(), session_id = self.session_id )
|
||||
new_notebook_id = result[ u"redirect" ].split( u"/notebooks/" )[ -1 ]
|
||||
|
||||
result = cherrypy.root.notebooks.contents(
|
||||
notebook_id = new_notebook_id,
|
||||
user_id = self.user.object_id,
|
||||
)
|
||||
|
||||
notebook = result[ "notebook" ]
|
||||
assert result[ "total_notes_count" ] == 0
|
||||
assert result[ "startup_notes" ] == []
|
||||
assert result[ "notes" ] == []
|
||||
|
||||
assert notebook.object_id == new_notebook_id
|
||||
assert notebook.read_write == True
|
||||
|
||||
def test_create_without_login( self ):
|
||||
result = self.http_post( "/notebooks/create", dict() )
|
||||
|
||||
assert result[ u"error" ]
|
||||
|
||||
def test_rename( self ):
|
||||
self.login()
|
||||
|
||||
new_name = u"renamed notebook"
|
||||
result = self.http_post( "/notebooks/rename", dict(
|
||||
notebook_id = self.notebook.object_id,
|
||||
name = new_name,
|
||||
), session_id = self.session_id )
|
||||
|
||||
assert u"error" not in result
|
||||
|
||||
def test_contents_after_rename( self ):
|
||||
self.login()
|
||||
|
||||
new_name = u"renamed notebook"
|
||||
self.http_post( "/notebooks/rename", dict(
|
||||
notebook_id = self.notebook.object_id,
|
||||
name = new_name,
|
||||
), session_id = self.session_id )
|
||||
|
||||
result = cherrypy.root.notebooks.contents(
|
||||
notebook_id = self.notebook.object_id,
|
||||
user_id = self.user.object_id,
|
||||
)
|
||||
|
||||
notebook = result[ "notebook" ]
|
||||
assert notebook.name == new_name
|
||||
|
||||
def test_rename_without_login( self ):
|
||||
new_name = u"renamed notebook"
|
||||
result = self.http_post( "/notebooks/rename", dict(
|
||||
notebook_id = self.notebook.object_id,
|
||||
name = new_name,
|
||||
) )
|
||||
|
||||
assert result[ u"error" ]
|
||||
|
||||
def test_rename_with_reserved_name( self ):
|
||||
self.login()
|
||||
|
||||
new_name = u"Luminotes blog"
|
||||
result = self.http_post( "/notebooks/rename", dict(
|
||||
notebook_id = self.notebook.object_id,
|
||||
name = new_name,
|
||||
), session_id = self.session_id )
|
||||
|
||||
assert result[ u"error" ]
|
||||
|
||||
def test_recent_notes( self ):
|
||||
result = cherrypy.root.notebooks.load_recent_notes(
|
||||
self.notebook.object_id,
|
||||
|
|
|
@ -64,7 +64,9 @@ class Test_root( Test_controller ):
|
|||
|
||||
def test_index( self ):
|
||||
result = self.http_get( "/" )
|
||||
|
||||
assert result
|
||||
assert result[ u"notebook" ].object_id == self.anon_notebook.object_id
|
||||
|
||||
def test_index_after_login( self ):
|
||||
self.login()
|
||||
|
@ -88,6 +90,7 @@ class Test_root( Test_controller ):
|
|||
|
||||
assert result
|
||||
assert result.get( u"redirect" ) is None
|
||||
assert result[ u"notebook" ].object_id == self.anon_notebook.object_id
|
||||
|
||||
def test_default( self ):
|
||||
result = self.http_get(
|
||||
|
@ -98,6 +101,7 @@ class Test_root( Test_controller ):
|
|||
assert result[ u"notes" ]
|
||||
assert len( result[ u"notes" ] ) == 1
|
||||
assert result[ u"notes" ][ 0 ].object_id == self.anon_note.object_id
|
||||
assert result[ u"notebook" ].object_id == self.anon_notebook.object_id
|
||||
|
||||
def test_default_with_unknown_note( self ):
|
||||
result = self.http_get(
|
||||
|
@ -134,6 +138,7 @@ class Test_root( Test_controller ):
|
|||
|
||||
assert result
|
||||
assert u"error" not in result
|
||||
assert result[ u"notebook" ].object_id == self.blog_notebook.object_id
|
||||
|
||||
def test_blog_with_note_id( self ):
|
||||
result = self.http_get(
|
||||
|
@ -142,6 +147,7 @@ class Test_root( Test_controller ):
|
|||
|
||||
assert result
|
||||
assert u"error" not in result
|
||||
assert result[ u"notebook" ].object_id == self.blog_notebook.object_id
|
||||
|
||||
def test_blog_rss( self ):
|
||||
result = self.http_get(
|
||||
|
@ -150,6 +156,7 @@ class Test_root( Test_controller ):
|
|||
|
||||
assert result
|
||||
assert u"error" not in result
|
||||
assert result[ u"notebook" ].object_id == self.blog_notebook.object_id
|
||||
|
||||
def test_guide( self ):
|
||||
result = self.http_get(
|
||||
|
@ -158,6 +165,7 @@ class Test_root( Test_controller ):
|
|||
|
||||
assert result
|
||||
assert u"error" not in result
|
||||
assert result[ u"notebook" ].object_id == self.guide_notebook.object_id
|
||||
|
||||
def test_privacy( self ):
|
||||
result = self.http_get(
|
||||
|
@ -166,6 +174,7 @@ class Test_root( Test_controller ):
|
|||
|
||||
assert result
|
||||
assert u"error" not in result
|
||||
assert result[ u"notebook" ].object_id == self.privacy_notebook.object_id
|
||||
|
||||
def test_next_id( self ):
|
||||
result = self.http_get( "/next_id" )
|
||||
|
|
|
@ -266,6 +266,14 @@ ol li {
|
|||
padding: 0.2em;
|
||||
}
|
||||
|
||||
#notebook_header_name:hover {
|
||||
color: #ff6600;
|
||||
}
|
||||
|
||||
#rename_form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#notebook_border {
|
||||
padding: 0 0 0 0.4em;
|
||||
}
|
||||
|
|
|
@ -201,6 +201,30 @@ Wiki.prototype.populate = function ( startup_notes, current_notes, note_read_wri
|
|||
self.save_editor( null, true );
|
||||
} );
|
||||
}
|
||||
|
||||
var add_notebook_link = getElement( "add_notebook_link" );
|
||||
if ( add_notebook_link ) {
|
||||
connect( add_notebook_link, "onclick", function ( event ) {
|
||||
self.invoker.invoke( "/notebooks/create", "POST" );
|
||||
event.stop();
|
||||
} );
|
||||
}
|
||||
|
||||
var rename_notebook_link = getElement( "rename_notebook_link" );
|
||||
if ( rename_notebook_link ) {
|
||||
connect( rename_notebook_link, "onclick", function ( event ) {
|
||||
self.start_notebook_rename();
|
||||
event.stop();
|
||||
} );
|
||||
}
|
||||
|
||||
var notebook_header_name = getElement( "notebook_header_name" );
|
||||
if ( notebook_header_name ) {
|
||||
connect( notebook_header_name, "onclick", function ( event ) {
|
||||
self.start_notebook_rename();
|
||||
event.stop();
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
Wiki.prototype.background_clicked = function ( event ) {
|
||||
|
@ -1295,6 +1319,101 @@ Wiki.prototype.create_all_notes_link = function ( note_id, note_title ) {
|
|||
);
|
||||
}
|
||||
|
||||
Wiki.prototype.start_notebook_rename = function () {
|
||||
this.clear_messages();
|
||||
this.clear_pulldowns();
|
||||
|
||||
// if a renaming is already in progress, end the renaming instead of starting one
|
||||
var notebook_name_field = getElement( "notebook_name_field" );
|
||||
if ( notebook_name_field ) {
|
||||
this.end_notebook_rename();
|
||||
return;
|
||||
}
|
||||
|
||||
notebook_name_field = createDOM(
|
||||
"input", {
|
||||
"type": "text",
|
||||
"value": this.notebook.name,
|
||||
"id": "notebook_name_field",
|
||||
"name": "notebook_name_field",
|
||||
"size": "30",
|
||||
"maxlength": "100",
|
||||
"class": "text_field"
|
||||
}
|
||||
);
|
||||
|
||||
var ok_button = createDOM(
|
||||
"input", {
|
||||
"type": "button",
|
||||
"class": "message_button",
|
||||
"value": "ok",
|
||||
"title": "dismiss this message"
|
||||
}
|
||||
);
|
||||
|
||||
var rename_form = createDOM(
|
||||
"form", { "id": "rename_form" }, notebook_name_field, ok_button
|
||||
);
|
||||
|
||||
replaceChildNodes( "notebook_header_area", rename_form );
|
||||
|
||||
var self = this;
|
||||
connect( rename_form, "onsubmit", function ( event ) {
|
||||
self.end_notebook_rename();
|
||||
event.stop();
|
||||
} );
|
||||
connect( ok_button, "onclick", function ( event ) {
|
||||
self.end_notebook_rename();
|
||||
event.stop();
|
||||
} );
|
||||
|
||||
notebook_name_field.focus();
|
||||
notebook_name_field.select();
|
||||
}
|
||||
|
||||
Wiki.prototype.end_notebook_rename = function () {
|
||||
var new_notebook_name = getElement( "notebook_name_field" ).value;
|
||||
|
||||
// if the new name is blank or reserved, don't actually rename the notebook
|
||||
if ( /^\s*$/.test( new_notebook_name ) )
|
||||
new_notebook_name = this.notebook.name;
|
||||
|
||||
if ( /^\s*Luminotes/.test( new_notebook_name ) ) {
|
||||
new_notebook_name = this.notebook.name;
|
||||
this.display_error( "That notebook name is not available. Please try a different one." );
|
||||
}
|
||||
|
||||
// rename the notebook in the header
|
||||
var notebook_header_name = createDOM(
|
||||
"span",
|
||||
{ "id": "notebook_header_name" },
|
||||
createDOM( "strong", {}, new_notebook_name )
|
||||
);
|
||||
replaceChildNodes( "notebook_header_area", notebook_header_name );
|
||||
|
||||
var self = this;
|
||||
connect( notebook_header_name, "onclick", function ( event ) {
|
||||
self.start_notebook_rename();
|
||||
event.stop();
|
||||
} );
|
||||
|
||||
// rename the notebook link on the right side of the page
|
||||
replaceChildNodes(
|
||||
"notebook_" + this.notebook.object_id,
|
||||
document.createTextNode( new_notebook_name )
|
||||
);
|
||||
|
||||
// if the name has changed, then send the new name to the server
|
||||
if ( new_notebook_name == this.notebook.name )
|
||||
return;
|
||||
|
||||
this.notebook.name = new_notebook_name;
|
||||
this.invoker.invoke( "/notebooks/rename", "POST", {
|
||||
"notebook_id": this.notebook_id,
|
||||
"name": new_notebook_name
|
||||
} );
|
||||
}
|
||||
|
||||
Wiki.prototype.toggle_editor_changes = function ( event, editor ) {
|
||||
// if the pulldown is already open, then just close it
|
||||
var pulldown_id = "changes_" + editor.id;
|
||||
|
|
|
@ -13,7 +13,7 @@ class Link_area( Div ):
|
|||
( parent_id is None ) and Div(
|
||||
A(
|
||||
u"all notes",
|
||||
href = u"/notebooks/%s" % notebook.object_id,
|
||||
href = u"#",
|
||||
id = u"all_notes_link",
|
||||
title = u"View a list of all notes in this notebook.",
|
||||
),
|
||||
|
@ -45,6 +45,16 @@ class Link_area( Div ):
|
|||
) or None,
|
||||
|
||||
notebook.read_write and Span(
|
||||
( notebook.name != u"trash" ) and Div(
|
||||
A(
|
||||
u"rename notebook",
|
||||
href = u"#",
|
||||
id = u"rename_notebook_link",
|
||||
title = u"Change the name of this notebook.",
|
||||
),
|
||||
class_ = u"link_area_item",
|
||||
) or None,
|
||||
|
||||
notebook.trash_id and Div(
|
||||
A(
|
||||
u"trash",
|
||||
|
@ -89,6 +99,15 @@ class Link_area( Div ):
|
|||
),
|
||||
class_ = u"link_area_item",
|
||||
) for nb in linked_notebooks ],
|
||||
Div(
|
||||
A(
|
||||
u"add new notebook",
|
||||
href = u"#",
|
||||
id = u"add_notebook_link",
|
||||
title = u"Create a new wiki notebook.",
|
||||
),
|
||||
class_ = u"link_area_item",
|
||||
),
|
||||
id = u"notebooks_area"
|
||||
),
|
||||
|
||||
|
|
|
@ -123,9 +123,7 @@ class Main_page( Page ):
|
|||
),
|
||||
Rounded_div(
|
||||
( notebook.name == u"trash" ) and u"trash_notebook" or u"current_notebook",
|
||||
( len( notes ) > 0 ) and \
|
||||
A( Strong( notebook.name ), href = notebook_path ) \
|
||||
or Strong( notebook.name ),
|
||||
( notebook.name == u"trash" ) and Strong( u"trash" ) or Span( Strong( notebook.name ), id = u"notebook_header_name" ),
|
||||
parent_id and Span(
|
||||
u" | ",
|
||||
A( u"empty trash", href = u"/notebooks/%s" % notebook.object_id, id = u"empty_trash_link" ),
|
||||
|
|
Reference in New Issue