diff --git a/NEWS b/NEWS index 044411f..324b61b 100644 --- a/NEWS +++ b/NEWS @@ -6,10 +6,17 @@ * Improved site navigation by adding more useful links to the page footer. * Converted the Luminotes blog to work like a forum, so now you can post comments on Luminotes blog posts. + * You can now subscribe to an RSS feed in order to follow a whole Luminotes + discussion forum. + * The listing of threads in a discussion forum now shows how many posts + there are in each thread. * Fixed a bug in which search result note summaries were not showing the portion of the note that matched the search term. (Luminotes Server) * Fixed a visual bug in which undoing the deletion of a note didn't always make the note reappear, even though the undeletion always worked. + * NOTE: After upgrading to this release, you must restart memcached to clear + the cache. Failing to do so will cause errors with the Notebook object. + This does not apply to Luminotes Desktop. 1.5.6: November 12, 2008 * Greatly improved the speed of wiki searching for Luminotes Server and diff --git a/controller/Forums.py b/controller/Forums.py index 6c50bb9..b1a78ab 100644 --- a/controller/Forums.py +++ b/controller/Forums.py @@ -13,6 +13,7 @@ from Notebooks import Notebooks from Users import Access_error from view.Forums_page import Forums_page from view.Forum_page import Forum_page +from view.Forum_rss import Forum_rss from view.Main_page import Main_page @@ -90,7 +91,7 @@ class Forum( object ): self.__users = users self.__name = name - @expose( view = Forum_page ) + @expose( view = Forum_page, rss = Forum_rss ) @strongly_expire @end_transaction @grab_user_id @@ -102,8 +103,7 @@ class Forum( object ): ) def index( self, start = 0, count = 50, note_id = None, user_id = None ): """ - Provide the information necessary to display the current threads within a forum (in reverse - chronological order). + Provide the information necessary to display the current threads within a forum. @type start: integer or NoneType @param start: index of first forum thread to display (optional, defaults to 0) @@ -130,19 +130,12 @@ class Forum( object ): if anonymous is None: raise Access_error() - # whether this is a blog determines whether the posts are diplayed in forward or reverse - # chronological order - if self.__name == u"blog": - reverse = False - else: - reverse = True - # load a slice of the list of the threads in this forum, excluding those with a default name threads = self.__database.select_many( Notebook, anonymous.sql_load_notebooks( parents_only = False, undeleted_only = True, tag_name = u"forum", tag_value = self.__name, - exclude_notebook_name = self.DEFAULT_THREAD_NAME, reverse = reverse, + exclude_notebook_name = self.DEFAULT_THREAD_NAME, reverse = True, start = start, count = count, ) ) @@ -263,6 +256,11 @@ class Forum( object ): self.__database.commit() + if self.__name == "blog": + return dict( + redirect = u"/blog/%s" % thread_id, + ) + return dict( redirect = u"/forums/%s/%s" % ( self.__name, thread_id ), ) diff --git a/controller/Notebooks.py b/controller/Notebooks.py index 853c5f5..c6e0b32 100644 --- a/controller/Notebooks.py +++ b/controller/Notebooks.py @@ -125,19 +125,17 @@ class Notebooks( object ): result[ u"notebooks" ] = [ notebook for notebook in result[ "notebooks" ] if notebook.object_id == notebook_id ] - if len( result[ u"notebooks" ] ) == 0: - raise Access_error() - result[ u"notebooks" ][ 0 ].owner = False + if len( result[ u"notebooks" ] ) == 1: + result[ u"notebooks" ][ 0 ].owner = False elif preview == u"viewer": read_write = False owner = False result[ u"notebooks" ] = [ notebook for notebook in result[ "notebooks" ] if notebook.object_id == notebook_id ] - if len( result[ u"notebooks" ] ) == 0: - raise Access_error() - result[ u"notebooks" ][ 0 ].read_write = Notebook.READ_ONLY - result[ u"notebooks" ][ 0 ].owner = False + if len( result[ u"notebooks" ] ) == 1: + 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 owner = True @@ -155,7 +153,10 @@ class Notebooks( object ): forum_tags = [ tag for tag in notebook.tags if tag.name == u"forum" ] if forum_tags: forum_name = forum_tags[ 0 ].value - redirect = u"/forums/%s/%s" % ( forum_name, notebook_id ) + if forum_name == "blog": + redirect = u"/blog/%s" % notebook_id + else: + redirect = u"/forums/%s/%s" % ( forum_name, notebook_id ) if note_id: redirect += u"?note_id=%s" % note_id diff --git a/controller/test/Test_forums.py b/controller/test/Test_forums.py index d96aff9..757f51c 100644 --- a/controller/test/Test_forums.py +++ b/controller/test/Test_forums.py @@ -15,7 +15,7 @@ class Test_forums( Test_controller ): self.notebook = Notebook.create( self.database.next_id( Notebook ), u"my notebook", trash_id = u"foo" ) self.database.save( self.notebook ) - self.anon_notebook = Notebook.create( self.database.next_id( Notebook ), u"Luminotes" ) + self.anon_notebook = Notebook.create( self.database.next_id( Notebook ), u"Luminotes", trash_id = u"bar" ) self.database.save( self.anon_notebook ) self.anon_note = Note.create( self.database.next_id( Note ), u"

my note

", @@ -43,6 +43,9 @@ class Test_forums( Test_controller ): self.database.save( self.anonymous ) self.database.execute( self.anonymous.sql_save_notebook( self.anon_notebook.object_id ) ) + self.blog_user = User.create( self.database.next_id( User ), u"witten", self.password, self.email_address ) + self.database.save( self.blog_user ) + tag_id = self.database.next_id( Tag ) self.forum_tag = Tag.create( tag_id, @@ -71,14 +74,26 @@ class Test_forums( Test_controller ): self.anonymous.sql_save_notebook_tag( self.support_thread.object_id, self.forum_tag.object_id, value = u"support" ), ) + self.blog_thread = Notebook.create( self.database.next_id( Notebook ), u"Welcome to the blog!" ) + self.database.save( self.blog_thread ) + self.database.execute( + self.anonymous.sql_save_notebook( self.blog_thread.object_id, read_write = Notebook.READ_WRITE_FOR_OWN_NOTES ), + ) + self.database.execute( + self.blog_user.sql_save_notebook( self.blog_thread.object_id, read_write = Notebook.READ_WRITE ), + ) + self.database.execute( + self.anonymous.sql_save_notebook_tag( self.blog_thread.object_id, self.forum_tag.object_id, value = u"forum" ), + ) + def test_index( self ): result = self.http_get( "/forums/" ) assert result assert result.get( u"redirect" ) is None assert result[ u"user" ].username == u"anonymous" - assert len( result[ u"notebooks" ] ) == 3 - assert result[ u"first_notebook" ] == None + assert len( result[ u"notebooks" ] ) == 4 + assert result[ u"first_notebook" ].object_id == self.anon_notebook.object_id assert result[ u"login_url" ] == u"https://luminotes.com/notebooks/%s?note_id=%s" % ( self.anon_notebook.object_id, self.login_note.object_id, ) @@ -93,7 +108,7 @@ class Test_forums( Test_controller ): assert result assert result.get( u"redirect" ) is None assert result[ u"user" ].username == self.user.username - assert len( result[ u"notebooks" ] ) == 4 + assert len( result[ u"notebooks" ] ) == 5 assert result[ u"first_notebook" ].object_id == self.notebook.object_id assert result[ u"login_url" ] == None assert result[ u"logout_url" ] == u"https://luminotes.com/users/logout" @@ -123,6 +138,18 @@ class Test_forums( Test_controller ): assert result[ u"count" ] == 50 assert result[ u"total_thread_count" ] == 1 + def test_general_with_note_id( self ): + result = self.http_get( "/forums/general?note_id=blah" ) + + headers = result.get( "headers" ) + assert headers + assert headers.get( "Location" ) == u"http:///forums/general/?note_id=blah" + + def test_general_with_trailing_slash_and_note_id( self ): + result = self.http_get( "/forums/general/?note_id=blah" ) + + assert result.get( "redirect" ) == u"/forums/general/blah" + def test_support( self ): result = self.http_get( "/forums/support/" ) @@ -223,7 +250,7 @@ class Test_forums( Test_controller ): result = self.http_get( "/forums/general/%s" % self.general_thread.object_id ) assert result.get( u"user" ).object_id == self.anonymous.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 4 assert result.get( u"notebooks" )[ 0 ].object_id == self.anon_notebook.object_id assert result.get( u"login_url" ) assert result.get( u"logout_url" ) @@ -250,7 +277,7 @@ class Test_forums( Test_controller ): ) assert result.get( u"user" ).object_id == self.user.object_id - assert len( result.get( u"notebooks" ) ) == 4 + assert len( result.get( u"notebooks" ) ) == 5 assert result.get( u"notebooks" )[ 0 ].object_id == self.notebook.object_id assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) @@ -322,7 +349,7 @@ class Test_forums( Test_controller ): result = self.http_get( "/forums/general/%s" % self.general_thread.object_id ) assert result.get( u"user" ).object_id == self.anonymous.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 4 assert result.get( u"notebooks" )[ 0 ].object_id == self.anon_notebook.object_id assert result.get( u"login_url" ) assert result.get( u"logout_url" ) @@ -352,7 +379,7 @@ class Test_forums( Test_controller ): result = self.http_get( "/forums/general/%s?start=1" % self.general_thread.object_id ) assert result.get( u"user" ).object_id == self.anonymous.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 4 assert result.get( u"notebooks" )[ 0 ].object_id == self.anon_notebook.object_id assert result.get( u"login_url" ) assert result.get( u"logout_url" ) @@ -381,7 +408,7 @@ class Test_forums( Test_controller ): result = self.http_get( "/forums/general/%s?count=2" % self.general_thread.object_id ) assert result.get( u"user" ).object_id == self.anonymous.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 4 assert result.get( u"notebooks" )[ 0 ].object_id == self.anon_notebook.object_id assert result.get( u"login_url" ) assert result.get( u"logout_url" ) @@ -410,7 +437,7 @@ class Test_forums( Test_controller ): result = self.http_get( "/forums/general/%s?start=1&count=1" % self.general_thread.object_id ) assert result.get( u"user" ).object_id == self.anonymous.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 4 assert result.get( u"notebooks" )[ 0 ].object_id == self.anon_notebook.object_id assert result.get( u"login_url" ) assert result.get( u"logout_url" ) @@ -438,7 +465,7 @@ class Test_forums( Test_controller ): result = self.http_get( "/forums/general/%s?note_id=%s" % ( self.general_thread.object_id, self.note.object_id ) ) assert result.get( u"user" ).object_id == self.anonymous.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 4 assert result.get( u"notebooks" )[ 0 ].object_id == self.anon_notebook.object_id assert result.get( u"login_url" ) assert result.get( u"logout_url" ) @@ -461,7 +488,7 @@ class Test_forums( Test_controller ): user = self.database.load( User, self.user.object_id ) assert user.storage_bytes == 0 - def __assert_new_forum_thread( self, thread, expected_id ): + def __assert_new_forum_thread( self, thread, expected_id, expected_user_id = None ): assert thread assert thread.object_id == expected_id assert thread.name == u"new discussion" @@ -469,7 +496,7 @@ class Test_forums( Test_controller ): assert thread.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES assert thread.owner == False assert thread.deleted == False - assert thread.user_id == self.user.object_id + assert thread.user_id == ( expected_user_id or self.user.object_id ) def test_general_create_thread( self ): self.login() @@ -517,6 +544,62 @@ class Test_forums( Test_controller ): assert headers assert headers.get( "Location" ) == u"http:///login?after_login=%s" % urllib.quote( path ) + def test_blog_create_thread( self ): + self.login_blog() + + result = self.http_get( + "/blog/create_thread", + session_id = self.session_id, + ) + + redirect = result.get( u"redirect" ) + assert redirect + assert redirect.startswith( u"/blog/" ) + new_thread_id = redirect.split( "/blog/" )[ -1 ].split( u"?" )[ 0 ] + + thread = cherrypy.root.users.load_notebook( self.blog_user.object_id, new_thread_id, read_write = True ) + self.__assert_new_forum_thread( thread, new_thread_id, self.blog_user.object_id ) + tags = self.database.select_many( Tag, thread.sql_load_tags( self.blog_user.object_id ) ) + assert tags == [] + + thread = cherrypy.root.users.load_notebook( self.anonymous.object_id, new_thread_id, read_write = False ) + self.__assert_new_forum_thread( thread, new_thread_id, self.blog_user.object_id ) + tags = self.database.select_many( Tag, thread.sql_load_tags( self.anonymous.object_id ) ) + + assert tags + assert len( tags ) == 1 + assert tags[ 0 ].name == u"forum" + assert tags[ 0 ].value == u"blog" + + notes = self.database.select_many( Note, thread.sql_load_notes() ) + assert notes + assert len( notes ) == 1 + assert notes[ 0 ].title == None + assert notes[ 0 ].contents == u"

" + assert notes[ 0 ].notebook_id == thread.object_id + assert notes[ 0 ].startup is True + assert notes[ 0 ].deleted_from_id is None + assert notes[ 0 ].rank == 0 + assert notes[ 0 ].user_id == self.blog_user.object_id + + def test_blog_create_thread_without_login( self ): + path = "/blog/create_thread" + result = self.http_get( path ) + + headers = result.get( "headers" ) + assert headers + assert headers.get( "Location" ) == u"http:///login?after_login=%s" % urllib.quote( path ) + + def test_blog_create_thread_without_access( self ): + self.login() + + result = self.http_get( + "/blog/create_thread", + session_id = self.session_id, + ) + + assert u"access" in result[ u"body" ][ 0 ] + def login( self ): result = self.http_post( "/users/login", dict( username = self.username, @@ -532,3 +615,12 @@ class Test_forums( Test_controller ): login_button = u"login", ) ) self.session_id = result[ u"session_id" ] + + def login_blog( self ): + result = self.http_post( "/users/login", dict( + username = self.blog_user.username, + password = self.password, + login_button = u"login", + ) ) + self.session_id = result[ u"session_id" ] + diff --git a/controller/test/Test_notebooks.py b/controller/test/Test_notebooks.py index 8329bba..15f7d8d 100644 --- a/controller/test/Test_notebooks.py +++ b/controller/test/Test_notebooks.py @@ -192,6 +192,33 @@ class Test_notebooks( Test_controller ): redirect = result.get( "redirect" ) assert redirect == u"/forums/chickens/%s" % self.notebook.object_id + def test_default_blog( self ): + self.login() + + tag_id = self.database.next_id( Tag, commit = False ) + new_tag = Tag.create( + tag_id, + notebook_id = None, # this tag is not in the namespace of a single notebook + user_id = self.user.object_id, + name = u"forum", + description = u"a discussion forum" + ) + self.database.save( new_tag, commit = False ) + + self.database.execute( + self.user.sql_save_notebook_tag( self.notebook.object_id, new_tag.object_id, value = u"blog" ), + commit = False, + ) + self.database.commit() + + result = self.http_get( + "/notebooks/%s" % self.notebook.object_id, + session_id = self.session_id, + ) + + redirect = result.get( "redirect" ) + assert redirect == u"/blog/%s" % self.notebook.object_id + def test_default( self ): self.login() @@ -201,10 +228,10 @@ 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 == Notebook.READ_WRITE - assert result.get( u"notebooks" )[ 2 ].owner == True + 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 == Notebook.READ_WRITE + assert result.get( u"notebooks" )[ 0 ].owner == True assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -309,10 +336,10 @@ 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 == Notebook.READ_WRITE - assert result.get( u"notebooks" )[ 2 ].owner == True + 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 == Notebook.READ_WRITE + assert result.get( u"notebooks" )[ 0 ].owner == True assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -345,10 +372,7 @@ 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 == Notebook.READ_ONLY - assert result.get( u"notebooks" )[ 0 ].owner == False + assert len( result.get( u"notebooks" ) ) == 0 assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -377,10 +401,7 @@ class Test_notebooks( Test_controller ): # even though a collaborator preview is being requested, this user only has preview-level # access. so read_write should be False on the returned notebook 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 == Notebook.READ_ONLY - assert result.get( u"notebooks" )[ 0 ].owner == False + assert len( result.get( u"notebooks" ) ) == 0 assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -407,14 +428,12 @@ class Test_notebooks( Test_controller ): ) assert result.get( u"user" ).object_id == self.user.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 1 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 notebook.object_id == self.notebook.object_id + assert notebook.read_write == Notebook.READ_WRITE + assert notebook.owner == True assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -498,10 +517,10 @@ 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 == Notebook.READ_WRITE - assert result.get( u"notebooks" )[ 2 ].owner == True + 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 == Notebook.READ_WRITE + assert result.get( u"notebooks" )[ 0 ].owner == True assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -541,10 +560,10 @@ 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 == Notebook.READ_WRITE - assert result.get( u"notebooks" )[ 2 ].owner == True + 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 == Notebook.READ_WRITE + assert result.get( u"notebooks" )[ 0 ].owner == True assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -590,10 +609,10 @@ 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 == Notebook.READ_WRITE - assert result.get( u"notebooks" )[ 2 ].owner == True + 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 == Notebook.READ_WRITE + assert result.get( u"notebooks" )[ 0 ].owner == True assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -632,10 +651,10 @@ 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 == Notebook.READ_WRITE - assert result.get( u"notebooks" )[ 2 ].owner == True + 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 == Notebook.READ_WRITE + assert result.get( u"notebooks" )[ 0 ].owner == True assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -4749,7 +4768,7 @@ class Test_notebooks( Test_controller ): ) assert result.get( u"user" ).object_id == self.user.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 1 assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -4779,7 +4798,7 @@ class Test_notebooks( Test_controller ): ) assert result.get( u"user" ).object_id == self.user.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 1 assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -4808,7 +4827,7 @@ class Test_notebooks( Test_controller ): ) assert result.get( u"user" ).object_id == self.user.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 1 assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -4850,7 +4869,7 @@ class Test_notebooks( Test_controller ): ) assert result.get( u"user" ).object_id == self.user.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 1 assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -4880,7 +4899,7 @@ class Test_notebooks( Test_controller ): ) assert result.get( u"user" ).object_id == self.user.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 1 assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) @@ -4909,7 +4928,7 @@ class Test_notebooks( Test_controller ): ) assert result.get( u"user" ).object_id == self.user.object_id - assert len( result.get( u"notebooks" ) ) == 3 + assert len( result.get( u"notebooks" ) ) == 1 assert result.get( u"login_url" ) is None assert result.get( u"logout_url" ) assert result.get( u"rate_plan" ) diff --git a/view/Forum_page.py b/view/Forum_page.py index 954cd67..ed80123 100644 --- a/view/Forum_page.py +++ b/view/Forum_page.py @@ -1,8 +1,9 @@ import os.path import cherrypy +from datetime import datetime from Product_page import Product_page from Page_navigation import Page_navigation -from Tags import Div, H1, A, P +from Tags import Div, H1, A, P, Span, Link, Img class Forum_page( Product_page ): @@ -11,11 +12,12 @@ class Forum_page( Product_page ): threads, total_thread_count, start = 0, count = None, ): base_path = cherrypy.request.path + updates_path = "%s?rss" % base_path - if base_path.startswith( u"/forums/" ): - full_forum_name = u"%s forum" % forum_name - else: + if forum_name == u"blog": full_forum_name = u"Luminotes %s" % forum_name + else: + full_forum_name = u"%s forum" % forum_name Product_page.__init__( self, @@ -24,15 +26,24 @@ class Forum_page( Product_page ): login_url, logout_url, full_forum_name, # note title + Link( rel = u"alternate", type = u"application/rss+xml", title = full_forum_name, href = updates_path ) or None, P( H1( full_forum_name ), ), Div( - base_path.startswith( u"/forums/" ) and P( - A( u"start a new discussion", href = os.path.join( base_path, u"create_thread" ) ), - u" | ", - A( u"all forums", href = u"/forums/" ), + P( + base_path.startswith( u"/forums/" ) and Span( + A( u"start a new discussion", href = os.path.join( base_path, u"create_thread" ) ), + u" | ", + A( u"all forums", href = u"/forums/" ), + u" | ", + ) or None, + A( u"subscribe to rss", href = updates_path ), + A( + Img( src = u"/static/images/rss.png", width = u"14", height = u"14", class_ = u"middle_image padding_left" ), + href = updates_path, + ), class_ = u"small_text", ) or None, [ Div( @@ -40,8 +51,26 @@ class Forum_page( Product_page ): thread.name, href = os.path.join( base_path, thread.object_id ), ), + Span( + self.post_count( thread, forum_name ), + class_ = u"small_text", + ) ) for thread in threads ], class_ = u"forum_threads", ), Page_navigation( base_path, len( threads ), total_thread_count, start, count ), ) + + @staticmethod + def post_count( thread, forum_name ): + if forum_name != u"blog": + if thread.note_count > 1: + return u"(%s posts)" % thread.note_count + return None + + if thread.note_count == 2: + return u"(1 comment)" + elif thread.note_count > 2: + return u"(%s comments)" % ( thread.note_count - 1 ) + + return None diff --git a/view/Forum_rss.py b/view/Forum_rss.py new file mode 100644 index 0000000..4d7ea87 --- /dev/null +++ b/view/Forum_rss.py @@ -0,0 +1,42 @@ +import cgi +import os.path +import cherrypy +from Rss_channel import Rss_channel +from Rss_item import Rss_item + + +class Forum_rss( Rss_channel ): + def __init__( + self, + user, + notebooks, + first_notebook, + login_url, + logout_url, + rate_plan, + groups, + forum_name, + threads, + total_thread_count, + start = 0, + count = None, + ): + forum_path = cherrypy.request.base + cherrypy.request.path + if forum_name == u"blog": + full_forum_name = u"Luminotes %s" % forum_name + else: + full_forum_name = u"%s forum" % forum_name + + Rss_channel.__init__( + self, + full_forum_name, + forum_path, + full_forum_name, + [ Rss_item( + title = cgi.escape( thread.name ), + link = os.path.join( forum_path, thread.object_id ), + description = cgi.escape( thread.name ), + date = thread.revision.strftime( "%Y-%m-%dT%H:%M:%SZ" ), + guid = os.path.join( forum_path, thread.object_id ), + ) for thread in threads ], + ) diff --git a/view/Link_area.py b/view/Link_area.py index 38fc36d..c53387c 100644 --- a/view/Link_area.py +++ b/view/Link_area.py @@ -28,16 +28,14 @@ class Link_area( Div ): Div.__init__( self, toolbar, + ( user.username == u"anonymous" ) and self.forum_link( forum_tag, forum_name ) or None, ( user.username != u"anonymous" ) and Div( ( notebook_path != u"/" ) and Div( H4( u"this %s" % notebook_word, id = u"this_notebook_area_title", ), - forum_tag and Div( - A( u"%s forum" % forum_name, href = "/forums/%s" % forum_name ), - class_ = u"link_area_item", - ) or None, + self.forum_link( forum_tag, forum_name ), ( rate_plan.get( u"notebook_sharing" ) and notebook.name == u"Luminotes blog" ) and Div( A( @@ -208,3 +206,19 @@ class Link_area( Div ): ) or None, id = u"link_area", ) + + @staticmethod + def forum_link( forum_tag, forum_name ): + if not forum_tag: + return None + + if forum_name == u"blog": + return Div( + A( u"Luminotes %s" % forum_name, href = "/blog/" ), + class_ = u"link_area_item", + ) + + return Div( + A( u"%s forum" % forum_name, href = "/forums/%s" % forum_name ), + class_ = u"link_area_item", + ) diff --git a/view/Main_page.py b/view/Main_page.py index 451481b..77e474b 100644 --- a/view/Main_page.py +++ b/view/Main_page.py @@ -94,14 +94,13 @@ class Main_page( Page ): updates_path = None forum_tags = [ tag for tag in notebook.tags if tag.name == u"forum" ] + forum_tag = None if notebook.name == u"Luminotes": notebook_path = u"/" updates_path = None # no RSS feed for the main notebook elif notebook.name == u"Luminotes user guide": notebook_path = u"/guide" - elif notebook.name == u"Luminotes blog": - notebook_path = u"/blog" elif forum_tags: forum_tag = forum_tags[ 0 ] notebook_path = u"/forums/%s/%s" % ( forum_tag.value, notebook.object_id ) @@ -125,7 +124,6 @@ class Main_page( Page ): "contact info": "contact", "meet the team": "team", "Luminotes user guide": "guide", - "Luminotes blog": "blog", "Luminotes privacy policy": "privacy", }.get( header_note_title, header_note_title ) @@ -136,10 +134,8 @@ class Main_page( Page ): self, title, Link( rel = u"stylesheet", type = u"text/css", href = u"/static/css/header.css" ), - ( notebook.name == u"Luminotes blog" ) \ - and Link( rel = u"alternate", type = u"application/rss+xml", title = u"Luminotes blog", href = "/blog?rss" ) \ - or ( updates_path and \ - Link( rel = u"alternate", type = u"application/rss+xml", title = notebook.name, href = updates_path ) or None ), + updates_path and \ + Link( rel = u"alternate", type = u"application/rss+xml", title = notebook.name, href = updates_path ) or None, Script( type = u"text/javascript", src = u"/static/js/MochiKit.js" ) or None, 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, @@ -170,7 +166,7 @@ class Main_page( Page ): Toolbar( notebook, hide_toolbar = parent_id or notebook.read_write == Notebook.READ_ONLY, - note_word = ( notebook.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES ) and u"post" or u"note", + note_word = forum_tag and u"post" or u"note", ), notebooks, notebook, parent_id, notebook_path, updates_path, user, rate_plan, ), @@ -212,8 +208,6 @@ class Main_page( Page ): Page_navigation( notebook_path, len( notes ), total_notes_count, start, count, ), - ( notebook.read_write == Notebook.READ_WRITE_FOR_OWN_NOTES and user.username and user.username != u"anonymous" ) and \ - P( u"If you write a comment, click the save button to publish it.", class_ = u"small_text" ) or None, Div( Span( id = u"notes_top" ), id = u"notes", @@ -231,6 +225,8 @@ class Main_page( Page ): u"document.getElementById( 'static_notes' ).style.display = 'none';", type = u"text/javascript", ), + ( forum_tag and user.username and user.username != u"anonymous" ) and \ + P( u"If you write a comment, click the save button to publish it.", class_ = u"small_text" ) or None, Page_navigation( notebook_path, len( notes ), total_notes_count, start, count, return_text = u"return to the discussion",