diff --git a/controller/Root.py b/controller/Root.py index cfee3e8..97cd0d3 100644 --- a/controller/Root.py +++ b/controller/Root.py @@ -7,6 +7,8 @@ from Notebooks import Notebooks from Users import Users, grab_user_id from Database import Valid_id from model.Note import Note +from model.Notebook import Notebook +from model.User import User from view.Main_page import Main_page from view.Json import Json from view.Error_page import Error_page @@ -39,9 +41,42 @@ class Root( object ): ) self.__notebooks = Notebooks( database, self.__users ) + @expose( Main_page ) + @validate( + note_title = unicode, + ) + def default( self, note_title ): + """ + Convenience method for accessing a note in the main notebook by name rather than by note id. + """ + # if the user is logged in and not using https, and they request the sign up or login note, then + # redirect to the https version of the page (if available) + https_url = self.__settings[ u"global" ].get( u"luminotes.https_url" ) + https_proxy_ip = self.__settings[ u"global" ].get( u"luminotes.https_proxy_ip" ) + + if note_title in ( u"sign_up", u"login" ) and https_url and cherrypy.request.remote_addr != https_proxy_ip: + return dict( redirect = u"%s/%s" % ( https_url, note_title ) ) + + result = self.__users.current( user_id = None ) + first_notebook = result[ u"notebooks" ][ 0 ] + user_id = result[ u"user" ].object_id + + note_title = note_title.replace( u"_", " " ) + note = self.__database.select_one( Note, first_notebook.sql_load_note_by_title( note_title ) ) + if not note: + raise cherrypy.NotFound + + result.update( self.__notebooks.contents( first_notebook.object_id, user_id = user_id, note_id = note.object_id ) ) + + return result + @expose() - def default( self, password_reset_id ): - # if the value looks like an id, assume it's a password reset id, and redirect + def r( self, password_reset_id ): + """ + Redirect to the password reset URL, based on the given password_reset id. The sole purpose of + this method is to shorten password reset URLs sent by email so email clients don't wrap them. + """ + # if the value looks like an id, it's a password reset id, so redirect try: validator = Valid_id() password_reset_id = validator( password_reset_id ) diff --git a/controller/Users.py b/controller/Users.py index e4aedaa..b712d59 100644 --- a/controller/Users.py +++ b/controller/Users.py @@ -356,6 +356,7 @@ class Users( object ): 'rate_plan': rateplandict, } @raise Validation_error: one of the arguments is invalid + @raise Access_error: user_id or anonymous user unknown """ # if there's no logged-in user, default to the anonymous user anonymous = self.__database.select_one( User, User.sql_load_by_username( u"anonymous" ) ) @@ -490,7 +491,7 @@ class Users( object ): u"Someone has requested a password reset for a Luminotes user with your email\n" + u"address. If this someone is you, please visit the following link for a\n" + u"username reminder or a password reset:\n\n" + - u"%s/%s\n\n" % ( self.__https_url or self.__http_url, password_reset.object_id ) + + u"%s/r/%s\n\n" % ( self.__https_url or self.__http_url, password_reset.object_id ) + u"This link will expire in 24 hours.\n\n" + u"Thanks!" ) diff --git a/controller/test/Test_root.py b/controller/test/Test_root.py index 1875463..9d16b88 100644 --- a/controller/test/Test_root.py +++ b/controller/test/Test_root.py @@ -1,4 +1,6 @@ import cherrypy +from model.Note import Note +from model.Notebook import Notebook from model.User import User from controller.Scheduler import Scheduler from Test_controller import Test_controller @@ -8,6 +10,17 @@ class Test_root( Test_controller ): def setUp( self ): Test_controller.setUp( self ) + self.notebook = Notebook.create( self.database.next_id( Notebook ), u"my notebook" ) + self.database.save( self.notebook ) + + self.anon_notebook = Notebook.create( self.database.next_id( Notebook ), u"anon notebook" ) + self.database.save( self.anon_notebook ) + self.anon_note = Note.create( + self.database.next_id( Note ), u"

my note

", + notebook_id = self.anon_notebook.object_id, + ) + self.database.save( self.anon_note ) + self.username = u"mulder" self.password = u"trustno1" self.email_address = u"outthere@example.com" @@ -16,6 +29,11 @@ class Test_root( Test_controller ): self.user = User.create( self.database.next_id( User ), self.username, self.password, self.email_address ) self.database.save( self.user ) + self.database.execute( self.user.sql_save_notebook( self.notebook.object_id ) ) + + 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 ) ) def test_index( self ): result = self.http_get( "/" ) @@ -44,6 +62,43 @@ class Test_root( Test_controller ): assert result assert result.get( u"redirect" ) is None + def test_default( self ): + result = self.http_get( + "/my_note", + ) + + assert result + assert result[ u"note" ] + assert result[ u"note" ].object_id == self.anon_note.object_id + + def test_default_with_unknown_note( self ): + result = self.http_get( + "/unknown_note", + ) + + body = result.get( u"body" ) + assert body + assert len( body ) > 0 + assert u"404" in body[ 0 ] + + def test_default_with_login_note( self ): + result = self.http_get( + "/login", + ) + + assert result + assert result.get( "redirect" ) + assert result.get( "redirect" ).startswith( "https://" ) + + def test_default_with_sign_up_note( self ): + result = self.http_get( + "/sign_up", + ) + + assert result + assert result.get( "redirect" ) + assert result.get( "redirect" ).startswith( "https://" ) + def test_next_id( self ): result = self.http_get( "/next_id" ) @@ -57,6 +112,7 @@ class Test_root( Test_controller ): result = self.http_get( "/four_oh_four" ) body = result.get( u"body" ) + assert body assert len( body ) > 0 assert u"404" in body[ 0 ] @@ -77,6 +133,6 @@ class Test_root( Test_controller ): def test_redeem_reset( self ): redeem_reset_id = u"foobarbaz" - result = self.http_get( "/%s" % redeem_reset_id ) + result = self.http_get( "/r/%s" % redeem_reset_id ) assert result[ u"redirect" ] == u"/users/redeem_reset/%s" % redeem_reset_id diff --git a/controller/test/Test_users.py b/controller/test/Test_users.py index 590aa28..acb2714 100644 --- a/controller/test/Test_users.py +++ b/controller/test/Test_users.py @@ -15,7 +15,7 @@ from controller.Users import Access_error class Test_users( Test_controller ): - RESET_LINK_PATTERN = re.compile( "(https?://\S+)?/(\S+)" ) + RESET_LINK_PATTERN = re.compile( "(https?://\S+)?/r/(\S+)" ) def setUp( self ): Test_controller.setUp( self )