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", ),