Quills browser tests
====================

Here we check for fixed bugs usins tests, that don't fit into the 'narrative' in
the main browser test. First some boilerplate to get our browser up and running:

    >>> self.setRoles(("Contributor",))
    >>> browser = self.getBrowser(logged_in=True)
    >>> browser.handleErrors = False
    >>> entry = self.weblog.addEntry("Blog entry",
    ...                              "Just for testing",
    ...                              "Nothing to see.",
    ...                              ['fishslapping'],
    ...                              id="entry")
    >>> from quills.core.interfaces import IWeblogEntry
    >>> IWeblogEntry.providedBy(entry)
    True

Make it discussable and publish it

    >>> entry = self.weblog.getEntry('entry')
    >>> entry_content = entry.getWeblogEntryContentObject()
    >>> entry_content.allowDiscussion(allowDiscussion=True)
    >>> entry.publish()

    >>> date = entry.getPublicationDate()
    >>> year = str(date.year())
    >>> month = str(date.month()).zfill(2)
    >>> day = str(date.day()).zfill(2)

    >>> self.setRoles(("Contributor", "Reviewer", "Manager"))
    >>> browser.open('http://nohost/plone/weblog/%s/%s/%s/entry' % (year, month, day))
    >>> browser.getControl('Add Comment').click()
    >>> browser.getControl('Subject').value = "Parrot"
    >>> browser.getControl('Comment').value = "Is dead. Is deceased."

Issue #111 shows that the URLs generated by the archive portlet are not correct.
Even when the weblog is not supposed to be using an extra 'archive' URL segment,
the URLs always have that segment in them.

To test this, we'll first make sure that the weblog config is setup to use the
'archive' segment for URLs.

    >>> from quills.core.interfaces import IWeblogConfiguration
    >>> config = IWeblogConfiguration(self.weblog.getWeblogContentObject())
    >>> config.archive_format = 'archive'

Now we'll get a page and check its body for the appropriate link.

    >>> browser.open('http://nohost/plone/weblog/')
    >>> url = '<a href="http://nohost/plone/weblog/archive/%s/%s">'
    >>> url = url % (year, month)
    >>> url in browser.contents
    True

Now, if we change the archive_format, we should get different URLs.

    >>> config.archive_format = ''
    >>> browser.open('http://nohost/plone/weblog/')
    >>> url = '<a href="http://nohost/plone/weblog/%s/%s">'
    >>> url = url % (year, month)
    >>> url in browser.contents
    True


There was an issue whereby the correct comment count didn't get shown for each
weblog entry displayed in the weblog_view.  We verify that this is no longer
the case here.

First, let's add a comment so that we know one is there.

    >>> from Products.CMFCore.utils import getToolByName
    >>> dtool = getToolByName(self.portal, 'portal_discussion')
    >>> entry_discussion = dtool.getDiscussionFor(entry_content)
    >>> comment_id = entry_discussion.createReply(title='Comment Title',
    ...                                           text='a little test body')

Now, when we look at the weblog view, we should find that there is a link to the
comments for `entry', together with a count of how many comments there are on
it.

    >>> browser.open('http://nohost/plone/weblog/')
    >>> url = '<a href="http://nohost/plone/weblog/%s/%s/%s/entry#comments"'
    >>> url = url % (year, month, day)
    >>> url in browser.contents
    True
    >>> '<span>1</span>' in browser.contents
    True

>> repr(browser.contents)

This last line of test is fairly lame as it could potentially match anywhere in
the source.  An example of what we are really trying to match is the following:

"""
          <a href="http://nohost/plone/weblog/2007/09/24/entry#comments"
           style="text-decoration: none;">
          Comments:
          </a>

          <span>3</span>
"""


Issue #112 found that the recent comments portlet was generating incorrect links
to comments as it wasn't utilising the archive URL of the weblog entry objects.

    >>> txt = '<a href="http://nohost/plone/weblog/%s/%s/%s/entry#%s"'
    >>> txt = txt % (year, month, day, comment_id)
    >>> txt in browser.contents
    True


Issue #117 found that the weblog admin portlet got displayed to anonymous users,
rather than being restricted to admin-ish users.  Let's verify that this is no
longer the case.

    >>> self.setRoles([])
    >>> browser = self.getBrowser(logged_in=False)
    >>> browser.handleErrors = False
    >>> browser.open('http://nohost/plone/weblog/')
    >>> 'portletWeblogAdmin' in browser.contents
    False


Issue #143: Portlets do not show up in empty blogs
--------------------------------------------------

This issue is caused by the way BasePortletRenderer implements ``available``.

We do not use one of Quills' portlet here but make our own, as the problem
is located in BasePortletRenderer.

    >>> from plone.app.portlets.portlets import base
    >>> from quills.app.portlets.base import BasePortletRenderer
    >>> class TestRenderer(BasePortletRenderer, base.Renderer):
    ...     """A simple Renderer"""
    ...     pass

Now create a blog. And see if we can get our portlet renderer. We first try
with an empty blog. This a bit overly complicated because this test must work
with both Quills and QuillsEnabled.

    >>> blog = self.createBlog('issue-143')
    >>> blogFolder = self.portal['issue-143']
    >>> from zope.component import getMultiAdapter
    >>> request = blogFolder.REQUEST
    >>> view = getMultiAdapter((blogFolder, request), name='view')
    >>> renderer = TestRenderer(blogFolder, request, view, None, None)
    >>> renderer.available
    True

Now with one private entry in it.

    >>> entry = blog.addEntry('Tesing issue #143', 'Nothing', 'Nothing',
    ...	                      id="issue-143")
    >>> renderer.available
    True

And now with that one published. In all three cases the portlet should show up.
We cannot do this directly on entry as it might be only an adapter.

    >>> from Products.CMFCore.utils import getToolByName
    >>> wft = getToolByName(self.getPortal(), 'portal_workflow')
    >>> wft.getInfoFor(blogFolder['issue-143'], 'review_state')
    'private'

    >>> entry.publish()
    >>> renderer.available
    True


Issue #115: Blog posts published in the future should not appear
----------------------------------------------------------------

This was not a bug, really. Quills behave correctly, hiding entries scheduled
for future publication as it should. This test-case confirms this.

We will test here access by Quills API and through the web.

    >>> from quills.core.interfaces.weblog import IWeblog
    >>> blog = self.weblog
    >>> IWeblog.providedBy(blog)
    True

We create a entry and publish, though not yet in the future.
    

    >>> id = 'issue-115'
    >>> entry = blog.addEntry("Issue #115", "Tesing for issue 115",
    ...                       "Nothing.", id=id)
    >>> entry.publish()

This entry should have an effective date before now, or none at best. We cannot
get effective directly from the entry because it might be only an adapter.

    >>> from DateTime import DateTime # We cannot use python datetime here, alas
    >>> effective = self.portal.weblog[id].effective()
    >>> now = DateTime()
    >>> effective is None or effective <= now
    True
    
It is visible.

    >>> id in map(lambda x: x.id, blog.getEntries())
    True

Now make it become effective in the future. It should still be visible since
we are managers and possess the appropriate rights.

    >>> from Products.CMFCore.permissions import AccessInactivePortalContent
    >>> from Products.CMFCore.utils import _checkPermission
    >>> _checkPermission(AccessInactivePortalContent,
    ...		 self.portal.weblog) and True
    True

    >>> futureDate = now + 7
    >>> self.portal.weblog[id].setEffectiveDate(futureDate)
    >>> self.portal.weblog[id].indexObject()
    >>> id in map(lambda x: x.id, blog.getEntries())
    True
        
Now we drop that right. The entry should no longer be visible.

    >>> from AccessControl import getSecurityManager
    >>> self.logout()
    >>> _checkPermission(AccessInactivePortalContent,
    ...                  self.portal.weblog) and True
        
    >>> id in map(lambda x: x.id, blog.getEntries())
    False

If published in the past it should be visible again.
    
    >>> self.portal.weblog[id].setEffectiveDate(effective)
    >>> self.portal.weblog[id].indexObject()
    >>> id in map(lambda x: x.id, blog.getEntries())
    True
    
Login again and set for future publication.

    >>> self.loginAsPortalOwner()
    >>> _checkPermission(AccessInactivePortalContent,
    ...	                 self.portal.weblog) and True
    True
    >>> self.portal.weblog[id].setEffectiveDate(futureDate)
    >>> self.portal.weblog[id].indexObject()
    >>> id in map(lambda x: x.id, blog.getEntries())
    True

Now same procedure through the web. Our entry should be invisible.

    >>> browser = self.getBrowser(logged_in=False)
    >>> browser.handleErrors = False
    >>> browser.open('http://nohost/plone/weblog/')
    >>> browser.getLink(url="http://nohost/plone/weblog/%s" % (id,))
    Traceback (most recent call last):
        ...
    LinkNotFoundError

After resetting the date it should be visible again.

    >>> self.portal.weblog[id].setEffectiveDate(effective)
    >>> self.portal.weblog[id].indexObject()
    >>> browser.open('http://nohost/plone/weblog/')
    >>> browser.getLink(url="http://nohost/plone/weblog/%s" % (id,))
    <Link ...>

We do not test for draft stated entries, because those are hidden from public
viewing anyway. We have to check the archive, though.

First some preparations, like getting the archive URL prefix.

    >>> from quills.app.interfaces import IWeblogEnhancedConfiguration
    >>> weblog_config = IWeblogEnhancedConfiguration(self.portal.weblog)
    >>> archivePrefix = weblog_config.archive_format

We check through the web only. First with effective in the past.

    >>> path = "/".join([archivePrefix, "%s" % (effective.year(),)])
    >>> browser.open('http://nohost/plone/weblog/%s' % (path,))
    >>> browser.getLink(url="http://nohost/plone/weblog/%s" % (id,))
    <Link ...>

Then with effective in the future.

    >>> path = "/".join([archivePrefix, "%s" % (futureDate.year(),)])
    >>> self.portal.weblog[id].setEffectiveDate(futureDate)
    >>> self.portal.weblog[id].indexObject()
    >>> browser.open('http://nohost/plone/weblog/%s' % (path,))
    >>> browser.getLink(url="http://nohost/plone/weblog/%s" % (id,))
    Traceback (most recent call last):
        ...
    LinkNotFoundError

Finally we should test syndication, but this would require some package
implementing that feature, which we do not want do depend on here.


Issue #158 — "Add Entry" of the Weblog Admin portlet fails
-----------------------------------------------------------

An exception is raised that, because the specified portal type does not exist.
In fact the type specified is "None". This is happens because no default
type is configured for Products.Quills weblogs.

XXX: Test-case does not work for QuillsEnabled!

Create a fresh blog, in the case someone might accidentally have set a default
portal type before. Populate it a little.

    >>> self.setRoles(("Manager",))
    >>> blog = self.createBlog('issue-158')
    >>> blogFolder = self.portal['issue-158']
    >>> entry = blog.addEntry('Tesing issue #158', 'Nothing',
    ...                       'Nothing', id="issue-158")
    >>> entry.publish()

Now click the "Add Entry" link. The edit form should be present.

    >>> browser = self.getBrowser(logged_in=True)
    >>> browser.handleErrors = True
    >>> browser.open('http://nohost/plone/issue-158/')
    >>> browser.getLink(text='Add Entry').click()
    >>> browser.getForm(id='weblogentry-base-edit')
    <zope.testbrowser.browser.Form object ...>


Issues #149 & #162: Memory leak and folder listing breakage
-----------------------------------------------------------

Both issues are cause by the way Quills wraps up Catalog Brains into an
IWeblogEntry adapter. It sets this wrapper class with "useBrains" of
Products.ZCatalog.Catalog. Doing so on each query causes the memory leak, as
the Catalog creates a class on the fly around the class passed to useBrains.
Never resetting the class causes the folder listing to break, because now
all catalog queries, even those from non Quills code, use Quills custom Brain.
This brain however defines methods which are simple member variable in the
default Brain, causing those clients to break.

To test for those bug, first publish a post, then render the Weblog View once.
This will cause some of the incriminating code to be called. Testing all 
occurances would not be sensible. A fix must make sure to break all those
calls by renaming the custom catalog class!

An exception is raised that, because the specified portal type does not exist.
In fact the type specified is "None". This is happens because no default
type is configured for Products.Quills weblogs.
Create a fresh blog, in the case someone might accidentally have set a default
portal type before. Populate it a little.

    >>> entry = self.weblog.addEntry('Tesing issue # 149 & #162', 'Nothing',
    ...                       'Nothing', id="issue-158")
    >>> entry.publish()
    >>> browser = self.getBrowser(logged_in=True)
    >>> browser.handleErrors = True
    >>> browser.open('http://nohost/plone/weblog/')

Now query a non Quills object from the catalog (in fact no query should ever
return a custom Quills brain). At least the Welcome message should exist.
Then check if the brain is a Quills adapter.

    >>> from Products.CMFCore.utils import getToolByName
    >>> catalog = getToolByName(self.portal, 'portal_catalog')
    >>> results = catalog(path="/", portal_type="Document")
    >>> len(results) > 0
    True

    >>> from quills.core.interfaces import IWeblogEntry
    >>> IWeblogEntry.providedBy(results[0])
    False

    
Issue #172 — Can't log comments from default view on weblog entries
-------------------------------------------------------------------

Quills default view for Weblog Entries is named 'weblogentry_view'. Plone
however links to individual items via the 'view' alias. This happens for
instance in collections or the recent items portlet. The Weblog Entries
still get rendered, important actions are missing though, e.g. the user
actions for copy/paste/delete or workflow actions. The commenting button 
is also missing.

We will need write access to the blog.

    >>> self.logout()
    >>> self.login()
    >>> self.setRoles(("Manager",))

Create a discussable weblog entry first.

    >>> from quills.app.browser.weblogview import WeblogEntryView
    >>> traverseTo = self.portal.restrictedTraverse # for brevity
    >>> entry = self.weblog.addEntry("Test for issue #172", "Nothing",
    ...                              "Nothing", id="issue-172")
    >>> entry_content = entry.getWeblogEntryContentObject()
    >>> entry_content.allowDiscussion(allowDiscussion=True)
    >>> entry.publish()

There should be a fully functionaly WeblogEntryView at 'weblogentry_view'.

    >>> browser = self.getBrowser(logged_in=True)
    >>> browser.handleErrors = False
    >>> browser.open('http://nohost/plone/weblog/issue-172/weblogentry_view')

That inculdes actions like cut and paste,

    >>> browser.getLink(text='Actions') # of issue-172/weblogentry_view
    <Link ...>

and also workflow control,

    >>> browser.getLink(text='State:') # of issue-172/weblogentry_view
    <Link ...>

    >>> browser.getForm(name='reply') # of issue-172/weblogentry_view
    <zope.testbrowser.browser.Form object at ...>

and finally commenting, which must be enabled, of course.

The same should be available when we navigate to issue-172/view.

    >>> browser = self.getBrowser(logged_in=True)
    >>> browser.handleErrors = False
    >>> browser.open('http://nohost/plone/weblog/issue-172/view')

That inculdes actions like cut and paste,

    >>> browser.getLink(text='Actions') # of issue-172/view
    <Link ...>

and also workflow control,

    >>> browser.getLink(text='State:') # of issue-172/view
    <Link ...>

and finally commenting, which must be enabled, of course.

    >>> browser.getForm(name='reply') # of issue-172/view
    <zope.testbrowser.browser.Form object at ...>
