Using a members-only forum
==========================

Test starting conversations, replying and modifying comments in a default
members-only forum. This is very similar to a member-posting forum, except
anonymous users cannot see the forum at all.

First, some set-up:

    >>> from Zope2.App import zcml
    >>> import Products
    >>> zcml.load_config('configure.zcml', package=Products.Ploneboard)

    >>> from Products.Ploneboard.tests import utils
    >>> utils.setUpDefaultMembersBoardAndForum(self)

    >>> from Testing.testbrowser import Browser
    >>> browser = Browser()
    >>> browser.handleErrors = False

Let us log all exceptions, which is useful for debugging. Also, clear portlet
slots, to make the test browser less confused by things like the recent portlet
and the navtree.

    >>> self.portal.error_log._ignored_exceptions = ()
    >>> self.portal.left_slots = self.portal.right_slots = []
    >>> workflow = self.portal.portal_workflow

Make the forum member-only

    >>> workflow.doActionFor(self.forum, 'make_private')
    >>> workflow.getInfoFor(self.forum, 'review_state')
    'private'

    >>> utils.logoutThenLoginAs(self, browser, 'member1')

View forum
----------

The forum created behind the scenes should now be shown here.

    >>> browser.open(self.board.absolute_url())
    >>> browser.contents
    '...Forum 1...'

If we go to the forum, there are no conversations shown.

    >>> browser.getLink('Forum 1').click()
    >>> browser.contents
    '...No conversations in this forum yet...'

Add a new conversation
----------------------

Now we can add a new conversation. We set a title and some body text. The body
text can contain HTML as well.

    >>> browser.getControl('Start a new Conversation').click()
    >>> browser.url
    '.../add_conversation_form...'
    >>> browser.getControl('Title').value = 'New title'
    >>> browser.getControl('Body text').value = 'Some <b>body</b> text'

We have attachment buttons, although we won't upload anything now.
INFO: This test fails (LookupError: name 'files:list') if SimpleAttachment is not installed.

    >>> browser.getControl(name='files:list', index=0)
    <Control name='files:list' type='file'>

Submit the form, and we should be returned to the forum view. The conversation
should exist, and we should be able to view it.

    >>> browser.getControl(name='form.button.Post').click()
    >>> browser.url.startswith(self.forum.absolute_url())
    True
    >>> conversation = self.forum.getConversations()[0]

    >>> import re
    >>> browser.getLink(url=re.compile('\/%s$' % conversation.getId())).click()

Add comment to own comment
--------------------------

Add a comment to our own comment. Use the quick-reply field first.

    >>> browser.getControl(name='text').value = 'A quick reply'
    >>> browser.getControl(name='form.button.Post').click()
    >>> browser.url.startswith(conversation.absolute_url())
    True
    >>> browser.contents
    '...A quick reply...'

Then add a full reply.

    >>> browser.getControl('Reply', index=0).click()
    >>> browser.url
    '.../add_comment_form...'
    >>> browser.getControl(name='text').value
    '...Previously Member one wrote:...<blockquote>Some <b>body</b> text</blockquote>...'
    >>> browser.getControl(name='text').value = 'A full reply'

Although we won't add attachments in this test, at least make sure the button
is there:

    >>> browser.getControl(name='files:list', index=0)
    <Control name='files:list' type='file'>

Submit and make sure we go back to the conversation

    >>> browser.getControl(name='form.button.Post').click()
    >>> browser.url.startswith(conversation.absolute_url())
    True
    >>> browser.contents
    '...A full reply...'

Edit own comment
----------------

A member cannot edit his own comment - no changing of history! Verify that there
is no 'Edit' button present.

    >>> browser.getControl('Edit', index=0)
    Traceback (most recent call last):
    ...
    LookupError: label 'Edit'

Delete a comment
----------------

A member cannot delete his own comment. Verify that there is no 'Delete' button
present.

    >>> browser.getControl('Delete')
    Traceback (most recent call last):
    ...
    LookupError: label 'Delete'


Delete a conversation
---------------------

If a user *could* delete his own posts, deleting the first post should also
delete the conversation. Since that is not enabled in this type of forum, it
is not verified here.

Workflow actions
-----------------

Workflow actions appear for operations like publish or retract. Members should
see no such actions in this type of forum, however, since their posts are
automatically published.

    >>> browser.getControl('Submit')
    Traceback (most recent call last):
    ...
    LookupError: label 'Submit'
    >>> browser.getControl('Publish')
    Traceback (most recent call last):
    ...
    LookupError: label 'Publish'
    >>> browser.getControl('Retract')
    Traceback (most recent call last):
    ...
    LookupError: label 'Retract'
    >>> browser.getControl('Reject')
    Traceback (most recent call last):
    ...
    LookupError: label 'Reject'


View other member's comment
---------------------------

Log in as another member

    >>> utils.logoutThenLoginAs(self, browser, 'member2')

Find the forum, and go to the new post.

    >>> browser.open(self.forum.absolute_url())
    >>> browser.getLink(url='/%s' % conversation.getId()).click()

Add reply to other member's comment
-----------------------------------

Add a new comment. Use the quick-reply field first.

    >>> browser.getControl(name='text').value = 'Another quick reply'
    >>> browser.getControl(name='form.button.Post').click()
    >>> browser.url.startswith(conversation.absolute_url())
    True
    >>> browser.contents
    '...Another quick reply...'

Then add a full reply.

    >>> browser.getControl('Reply', index=0).click()
    >>> browser.url
    '.../add_comment_form...'
    >>> browser.getControl(name='text').value = 'Another full reply'

Although we won't add attachments in this test, at least make sure the button
is there:

    >>> browser.getControl(name='files:list', index=0)
    <Control name='files:list' type='file'>

Submit and make sure we go back to the conversation

    >>> browser.getControl(name='form.button.Post').click()
    >>> browser.url.startswith(conversation.absolute_url())
    True
    >>> browser.contents
    '...Another full reply...'

View as reviewer
----------------

Log in as a reviewer.

    >>> utils.logoutThenLoginAs(self, browser, 'reviewer1')

Find the forum, and go to the new post.

    >>> browser.open(self.forum.absolute_url())
    >>> browser.getLink(url='/%s' % conversation.getId()).click()

The moderation queue form is not shown since this is not a moderated forum.

    >>> browser.open(self.forum.absolute_url())
    >>> 'moderation_form' in browser.contents
    False

The moderation queue form is not shown since this is not a moderated forum.

    >>> browser.open(self.forum.absolute_url())
    >>> 'moderation_form' in browser.contents
    False

It is, however, shown at the root board, since that aggregates all forums. Make
sure it is empty.

    >>> browser.open(self.board.absolute_url())
    >>> browser.getLink('Moderate').click()
    >>> browser.contents
    '...There are no comments in the moderation queue...'
    >>> conversation.getId() in browser.contents
    False

The reviewer cannot edit or delete any comments.

    >>> browser.open(conversation.absolute_url())

    >>> browser.getControl('Delete')
    Traceback (most recent call last):
    ...
    LookupError: label 'Delete'
    >>> browser.getControl('Edit')
    Traceback (most recent call last):
    ...
    LookupError: label 'Edit'

However, a reviewer can retract a comment:

    >>> browser.getControl('Retract', index=0).click()
    >>> browser.contents
    '...retracted...'
    >>> workflow.getInfoFor(conversation.getComments()[0], 'review_state')
    'retracted'

Logging in as another member, the retracted comment cannot be viewed:

    >>> utils.logoutThenLoginAs(self, browser, 'member2')

    >>> browser.open(self.forum.absolute_url())
    >>> browser.getLink(url='/%s' % conversation.getId()).click()
    >>> 'retracted' in browser.contents
    False

However, the owner can see it, but cannot publish.

    >>> utils.logoutThenLoginAs(self, browser, 'member1')

    >>> browser.open(self.forum.absolute_url())
    >>> browser.getLink(url='/%s' % conversation.getId()).click()
    >>> 'retracted' in browser.contents
    True
    >>> browser.getControl('Publish')
    Traceback (most recent call last):
    ...
    LookupError: label 'Publish'

The reviewer can re-publish the comment again:

    >>> utils.logoutThenLoginAs(self, browser, 'reviewer1')

    >>> browser.open(self.forum.absolute_url())
    >>> browser.getLink(url='/%s' % conversation.getId()).click()
    >>> 'retracted' in browser.contents
    True
    >>> browser.getControl('Publish').click()
    >>> workflow.getInfoFor(conversation.getComments()[0], 'review_state')
    'published'

View as manager
---------------

Log in as a manager.

    >>> utils.logoutThenLoginAs(self, browser, 'manager1')

Find the forum, and go to the new post.

    >>> browser.open(self.forum.absolute_url())
    >>> browser.getLink(url='/%s' % conversation.getId()).click()

A manager can edit and delete comments, as well as apply workflow transitions.
Deleting will be tested later.

    >>> browser.getControl('Retract', index=0)
    <SubmitControl name=None type='submit'>

    >>> browser.getControl('Edit', index=0).click()
    >>> browser.getControl('Title').value = 'New Topic - New Title'
    >>> browser.getControl(name='text').value = 'A new topic with more text'

    >>> # XXX: zope.testbrowser converts the # in the url into %23, which
    >>> # makes the URL invalid. Until that bug if fixed, we can't reasonably
    >>> # test this!
    >>> # browser.getControl('Save').click()
    >>> # browser.contents
    >>> # '...New Topic - New Title...A new topic with more text...'

Locking a conversation
----------------------

Lock a conversation, and ensure that reply buttons do not appear.

    >>> browser.open(conversation.absolute_url())
    >>> browser.getLink('Lock').click()
    >>> browser.getControl(name='form.button.Post')
    Traceback (most recent call last):
    ...
    LookupError: name 'form.button.Post'
    >>> browser.getControl('Reply', index=0)
    Traceback (most recent call last):
    ...
    LookupError: label 'Reply'

Verify that they do not appear as a regular member either.

    >>> utils.logoutThenLoginAs(self, browser, 'member1')
    >>> browser.open(self.forum.absolute_url())
    >>> browser.getLink(url='/%s' % conversation.getId()).click()

    >>> browser.getControl(name='form.button.Post')
    Traceback (most recent call last):
    ...
    LookupError: name 'form.button.Post'
    >>> browser.getControl('Reply', index=0)
    Traceback (most recent call last):
    ...
    LookupError: label 'Reply'

Unlock again

    >>> utils.logoutThenLoginAs(self, browser, 'manager1')
    >>> browser.open(conversation.absolute_url())
    >>> browser.getLink('Activate').click()

View as anonymous
-----------------

In a members-only forum, anonymous cannot even see the forum:

    >>> browser.open('%s/logout' % self.portal.absolute_url())
    >>> browser.open(self.board.absolute_url())
    >>> browser.contents
    '...No forums exist in this board...'

Deleting comments and conversations
-----------------------------------

A manager can delete comments:

    >>> utils.logoutThenLoginAs(self, browser, 'manager1')
    >>> browser.open(self.forum.absolute_url())
    >>> browser.getLink(url='/%s' % conversation.getId()).click()

    >>> commentsBefore = len(conversation.getComments())
    >>> browser.getControl('Delete', index=-1).click()
    >>> browser.contents
    '...Comment deleted...'
    >>> commentsBefore == len(conversation.getComments()) + 1
    True

Deleting the last comment also deletes the entire thread

    >>> for i in range(conversation.getNumberOfComments()):
    ...     browser.getControl('Delete', index=0).click()
    >>> browser.url.startswith(self.forum.absolute_url())
    True
    >>> browser.contents
    '...Conversation deleted...No conversations in this forum yet...'
    >>> 'New topic' in browser.contents
    False
