diff --git a/controller/Validate.py b/controller/Validate.py
index 95e8507..a51d6f1 100644
--- a/controller/Validate.py
+++ b/controller/Validate.py
@@ -134,7 +134,7 @@ class Valid_bool( object ):
raise ValueError()
-def validate( **kwarg_types ):
+def validate( **expected ):
"""
validate() can be used to require that the arguments of the decorated method successfully pass
through particular validators. The validate() method itself is evaluated where it is used as a
@@ -177,64 +177,62 @@ def validate( **kwarg_types ):
args = list( args )
args_index = 1 # skip the self argument
- for ( arg_name, arg_type ) in kwarg_types.items():
- if arg_name == u"kwargs":
- key_type = kwarg_types[ u"kwargs" ].keys()[ 0 ]
- value_type = kwarg_types[ u"kwargs" ].values()[ 0 ]
+ # determine the expected argument names from the decorated function itself
+ code = function.func_code
+ expected_names = code.co_varnames[ : code.co_argcount ]
- for ( key, value ) in kwargs.items():
- if key not in kwarg_types:
- del( kwargs[ key ] )
- try:
- kwargs[ str( key_type( key ) ) ] = value_type( value )
- except ( ValueError, TypeError ):
- raise Validation_error( key, value, value_type )
+ # validate each of the expected arguments
+ for expected_name in expected_names:
+ if expected_name == u"self": continue
+ expected_type = expected.get( expected_name )
- continue
-
- # look for arg_name in kwargs and store the validated value there
- if arg_name in kwargs:
- value = kwargs.get( arg_name )
- # if there's a tuple of multiple validators for this arg_name, use all of them
- if isinstance( arg_type, tuple ):
- for validator in arg_type:
+ # look for expected_name in kwargs and store the validated value there
+ if expected_name in kwargs:
+ value = kwargs.get( expected_name )
+ # if there's a tuple of multiple validators for this expected_name, use all of them
+ if isinstance( expected_type, tuple ):
+ for validator in expected_type:
try:
value = validator( value )
except ( ValueError, TypeError ):
- raise Validation_error( arg_name, value, validator )
- kwargs[ str( arg_name ) ] = value
+ raise Validation_error( expected_name, value, validator )
+ kwargs[ str( expected_name ) ] = value
# otherwise, there's just a single validator
else:
try:
- kwargs[ str( arg_name ) ] = arg_type( value )
+ kwargs[ str( expected_name ) ] = expected_type( value )
except ( ValueError, TypeError ):
- raise Validation_error( arg_name, value, arg_type )
+ raise Validation_error( expected_name, value, expected_type )
continue
- # arg_name wasn't found in kwargs, so use args instead
+ # expected_name wasn't found in kwargs, so look for it in args. if it's not there either,
+ # raise unless there's a default value for the argument in the decorated function
if args_index >= len( args ):
- raise Validation_error( arg_name, None, arg_type, message = u"is required" )
+ if function.func_defaults and args_index >= len( args ) - len( function.func_defaults ):
+ continue
+ raise Validation_error( expected_name, None, expected_type, message = u"is required" )
value = args[ args_index ]
- # if there's a tuple of multiple validators for this arg_name, use all of them
- if isinstance( arg_type, tuple ):
- for validator in arg_type:
+ # if there's a tuple of multiple validators for this expected_name, use all of them
+ if isinstance( expected_type, tuple ):
+ for validator in expected_type:
try:
value = validator( value )
except ( ValueError, TypeError ):
- raise Validation_error( arg_name, value, validator )
+ raise Validation_error( expected_name, value, validator )
args[ args_index ] = value
# otherwise, there's just a single validator
else:
try:
- args[ args_index ] = arg_type( value )
+ args[ args_index ] = expected_type( value )
except ( ValueError, TypeError ):
- raise Validation_error( arg_name, value, arg_type )
+ raise Validation_error( expected_name, value, expected_type )
args_index += 1
+ # if there are any unexpected arguments, raise
for ( arg_name, arg_value ) in kwargs.items():
- if not arg_name in kwarg_types:
- print arg_name, kwarg_types
+ if not arg_name in expected_names:
+ print arg_name, expected
raise Validation_error( arg_name, arg_value, None, message = u"is an unknown argument" )
return function( *args, **kwargs )
diff --git a/controller/test/Test_notebooks.py b/controller/test/Test_notebooks.py
index 184113f..2897900 100644
--- a/controller/test/Test_notebooks.py
+++ b/controller/test/Test_notebooks.py
@@ -62,6 +62,12 @@ class Test_notebooks( Test_controller ):
assert result.get( u"notebook_id" ) == self.notebook.object_id
+ def test_default_with_note( self ):
+ result = self.http_get( "/notebooks/%s?note_id=%s" % ( self.notebook.object_id, self.note.object_id ) )
+
+ assert result.get( u"notebook_id" ) == self.notebook.object_id
+ assert result.get( u"note_id" ) == self.note.object_id
+
def test_contents( self ):
self.login()
@@ -76,6 +82,24 @@ class Test_notebooks( Test_controller ):
assert len( notebook.startup_notes ) == 1
assert notebook.startup_notes[ 0 ] == self.note
+ def test_contents_with_note( self ):
+ self.login()
+
+ result = self.http_get(
+ "/notebooks/contents?notebook_id=%s¬e_id=%s" % ( self.notebook.object_id, self.note.object_id ),
+ session_id = self.session_id,
+ )
+
+ notebook = result[ "notebook" ]
+
+ assert notebook.object_id == self.notebook.object_id
+ assert len( notebook.startup_notes ) == 1
+ assert notebook.startup_notes[ 0 ] == self.note
+
+ note = result[ "note" ]
+
+ assert note.object_id == self.note.object_id
+
def test_contents_without_login( self ):
result = self.http_get(
"/notebooks/contents?notebook_id=%s" % self.notebook.object_id,
diff --git a/static/js/Editor.js b/static/js/Editor.js
index a26d90b..539ae0e 100644
--- a/static/js/Editor.js
+++ b/static/js/Editor.js
@@ -1,7 +1,8 @@
note_titles = {} // map from note title to the open editor for that note
-function Editor( id, note_text, revisions_list, insert_after_iframe_id, read_write, startup, highlight, focus ) {
+function Editor( id, notebook_id, note_text, revisions_list, insert_after_iframe_id, read_write, startup, highlight, focus ) {
this.id = id;
+ this.notebook_id;
this.initial_text = note_text;
this.revisions_list = revisions_list;
this.read_write = read_write;
@@ -273,11 +274,11 @@ Editor.prototype.mouse_clicked = function ( event ) {
var id;
var link_title = scrapeText( link );
var editor = note_titles[ link_title ];
- var href_leaf = link.href.split( "/" ).pop();
+ var href_leaf = link.href.split( "?note_id=" ).pop();
// if the link's title corresponds to an open note id, set that as the link's destination
if ( editor ) {
id = editor.id;
- link.href = "/notes/" + id;
+ link.href = "/notebooks/" + this.notebook_id + "?note_id=" + id;
// if this is a new link, get a new note id and set it for the link's destination
} else if ( href_leaf == "new" ) {
signal( this, "load_editor_by_title", link_title, this.iframe.id );
@@ -363,7 +364,7 @@ Editor.prototype.start_link = function () {
range.setStart( container, range.startOffset - 1 );
}
- this.exec_command( "createLink", "/notes/new" );
+ this.exec_command( "createLink", "/notebooks/" + this.notebook_id + "?note_id=new" );
var links = getElementsByTagAndClassName( "a", null, parent = this.document );
for ( var i in links ) {
@@ -382,7 +383,7 @@ Editor.prototype.start_link = function () {
}
// otherwise, just create a link with the selected text as the link title
} else {
- this.exec_command( "createLink", "/notes/new" );
+ this.exec_command( "createLink", "/notebooks/" + this.notebook_id + "?note_id=new" );
}
} else if ( this.document.selection ) { // browsers such as IE
var range = this.document.selection.createRange();
@@ -395,7 +396,7 @@ Editor.prototype.start_link = function () {
range.select();
}
- this.exec_command( "createLink", "/notes/new" );
+ this.exec_command( "createLink", "/notebooks/" + this.notebook_id + "?note_id=new" );
}
}
diff --git a/static/js/Wiki.js b/static/js/Wiki.js
index 1a997ee..e7607aa 100644
--- a/static/js/Wiki.js
+++ b/static/js/Wiki.js
@@ -16,7 +16,8 @@ function Wiki() {
if ( this.notebook_id ) {
this.invoker.invoke(
"/notebooks/contents", "GET", {
- "notebook_id": this.notebook_id
+ "notebook_id": this.notebook_id,
+ "note_id": getElement( "note_id" ).value
},
function( result ) { self.populate( result ); }
);
@@ -121,9 +122,17 @@ Wiki.prototype.populate = function ( result ) {
var note = this.notebook.startup_notes[ i ];
if ( !note ) continue;
this.startup_notes[ note.object_id ] = true;
- var focus = ( i == 0 );
- this.create_editor( note.object_id, note.contents, note.revisions_list, undefined, undefined, false, focus );
+
+ // don't actually create an editor if a particular note was provided in the result
+ if ( !result.note ) {
+ var focus = ( i == 0 );
+ this.create_editor( note.object_id, note.contents, note.revisions_list, undefined, undefined, false, focus );
+ }
}
+
+ // if one particular note was provided, then just display an editor for that note
+ if ( result.note )
+ this.create_editor( result.note.object_id, result.note.contents, result.note.revisions_list, undefined, undefined, false, true );
}
Wiki.prototype.background_clicked = function ( event ) {
@@ -214,7 +223,7 @@ Wiki.prototype.create_editor = function ( id, note_text, revisions_list, insert_
for ( var i in links ) {
// a link matches if its contained text is the same as this note's title
if ( scrapeText( links[ i ] ) == note_title )
- links[ i ].href = "/notes/" + id;
+ links[ i ].href = "/notebooks/" + this.notebook_id + "?note_id=" + id;
}
}
@@ -232,7 +241,7 @@ Wiki.prototype.create_editor = function ( id, note_text, revisions_list, insert_
}
var startup = this.startup_notes[ id ];
- var editor = new Editor( id, note_text, revisions_list, undefined, this.read_write, startup, highlight, focus );
+ var editor = new Editor( id, this.notebook_id, note_text, revisions_list, undefined, this.read_write, startup, highlight, focus );
if ( this.read_write ) {
connect( editor, "state_changed", this, "editor_state_changed" );
@@ -575,7 +584,7 @@ Pulldown.prototype.shutdown = function () {
}
-Options_pulldown = function ( notebook_id, invoker, editor ) {
+function Options_pulldown( notebook_id, invoker, editor ) {
Pulldown.call( this, notebook_id, "options_" + editor.id, editor.options_button );
this.invoker = invoker;
@@ -620,7 +629,7 @@ Options_pulldown.prototype.shutdown = function () {
}
-Changes_pulldown = function ( notebook_id, invoker, editor ) {
+function Changes_pulldown( notebook_id, invoker, editor ) {
Pulldown.call( this, notebook_id, "changes_" + editor.id, editor.changes_button );
this.invoker = invoker;
diff --git a/view/Html_file.py b/view/Html_file.py
index 6df759f..0e5667e 100644
--- a/view/Html_file.py
+++ b/view/Html_file.py
@@ -4,7 +4,7 @@ from Tags import Html, Head, Title, Style, Meta, Body, H1, Div, Span, Hr, A
class Html_file( Html ):
- NOTE_LINK_PATTERN = re.compile( u'', re.IGNORECASE )
+ NOTE_LINK_PATTERN = re.compile( u'', re.IGNORECASE )
def __init__( self, notebook_name, notes ):
relinked_notes = {} # map from note id to relinked note contents
diff --git a/view/Main_page.py b/view/Main_page.py
index 0aa2b60..89495bb 100644
--- a/view/Main_page.py
+++ b/view/Main_page.py
@@ -6,13 +6,14 @@ from Toolbar import Toolbar
class Main_page( Page ):
- def __init__( self, notebook_id = None ):
+ def __init__( self, notebook_id = None, note_id = None ):
title = None
Page.__init__(
self,
title,
Input( type = u"hidden", name = u"notebook_id", id = u"notebook_id", value = notebook_id or "" ),
+ Input( type = u"hidden", name = u"note_id", id = u"note_id", value = note_id or "" ),
Div(
id = u"status_area",
),