Skip to content

Commit

Permalink
Add a ZCML directive to create new viewlet manager interfaces. Update…
Browse files Browse the repository at this point in the history
… docstrings.
  • Loading branch information
jamadden committed Oct 12, 2017
1 parent 7887bd6 commit 993514c
Show file tree
Hide file tree
Showing 14 changed files with 270 additions and 9 deletions.
31 changes: 31 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,37 @@
now preferred to explicit calls to ``options/messages`` when
possible.

- Add support for viewlets. Several default viewlet managers are
supplied, and a ZCML directive ``<browser:newViewletManager>`` is
provided so themes can create new viewlet managers::

<browser:newViewletManager id="ILeftColumn" />
<browser:viewletManager
name="left_column"
provides=".viewlets.ILeftColumn" />

- Add a path adapter to easily get formatted dates from a post, either
a static format (``post/formatted_date:webiso``) or dynamically from
a variable (``post/formatted_date:?date_format``).

- Add a view to get the text of a post, respecting teaser settings:
``post/@@post_text/content``.

- Move feed support to a ``@@feeds`` view for headers, and a viewlet
for body::

<browser:viewlet
name="feed_content_header"
manager=".interfaces.IHtmlBodyContentHeaderViewletManager"
class=".feeds.HTMLFeedLinkViewlet"
layer=".interfaces.IAuthorPageKind"
permission="zope.Public"
weight="1"
classification_name="author"
/>

- Add a view interface (``ICommentKind``) for comment systems. Only Disqus is
currently supported. Note that this may move in the future to be a layer.

0.0.1a1 (2017-10-09)
====================
Expand Down
30 changes: 30 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,33 @@ Templating
==========

.. automodule:: nti.nikola_chameleon.template

Adapters
========

.. automodule:: nti.nikola_chameleon.adapters

Feeds
=====

.. automodule:: nti.nikola_chameleon.feeds

Macros
======

.. automodule:: nti.nikola_chameleon.macro

Request
=======

.. automodule:: nti.nikola_chameleon.request


View
====
.. automodule:: nti.nikola_chameleon.view

ZCML
====

.. automodule:: nti.nikola_chameleon.zcml
8 changes: 5 additions & 3 deletions src/nti/nikola_chameleon/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class MetaPathAdapter(object):
Lets us access the meta object in path
expressions.
post/meta:link post/meta:keywords
``post/meta:link`` ``post/meta:keywords``
"""

def __init__(self, context):
Expand All @@ -36,9 +36,11 @@ def traverse(self, name, furtherPath):
class FormattedDatePathAdapter(object):
"""
Lets us access the formatted date function
in tales expressions
in tales expressions.
post/formatted_date:webiso post/formatted_date:?date_format
``post/formatted_date:webiso`` ``post/formatted_date:?date_format``
.. versionadded:: 0.0.1a2
"""

def __init__(self, context):
Expand Down
1 change: 1 addition & 0 deletions src/nti/nikola_chameleon/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

<include package="zope.component" file="meta.zcml" />
<include package="zope.component" />
<include package="nti.nikola_chameleon" file="meta.zcml" />
<!-- required by zope.browserpage -->
<include package="zope.security" file="meta.zcml" />

Expand Down
15 changes: 13 additions & 2 deletions src/nti/nikola_chameleon/feeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,19 @@ class HTMLFeedLinkViewlet(ViewletBase):
"""
A viewlet to render one or more feed links into html.
You must subclass this to set the name of the classification attribute to
find in the request options dictionary in the ``classification_name`` attribute.
You must subclass this to set the name of the classification
attribute to find in the request options dictionary in the
``classification_name`` attribute. This is usually done in ZCML::
<browser:viewlet
name="feed_content_header"
manager=".interfaces.IHtmlBodyContentHeaderViewletManager"
class=".feeds.HTMLFeedLinkViewlet"
layer=".interfaces.IAuthorPageKind"
permission="zope.Public"
weight="1"
classification_name="author"
/>
"""
available = True
classification_name = None
Expand Down
2 changes: 1 addition & 1 deletion src/nti/nikola_chameleon/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
Similarly, the :class:`ICommentKind` (with its subclasses
:class:`ICommentKindNone` and :class:`ICommentKindAllowed` and *its*
various system types markers will be applied to the ``view`` variable).
various system types markers) will be applied to the ``view`` variable.
"""
from __future__ import absolute_import
from __future__ import division
Expand Down
11 changes: 10 additions & 1 deletion src/nti/nikola_chameleon/macro.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
The Chameleon ZPT plugin for nikola.
Supporting code for z3c.macro.
"""
from __future__ import absolute_import
Expand Down Expand Up @@ -28,6 +28,15 @@ def include(self, stream, econtext, *args, **kwargs):

@interface.implementer(ITraversable)
class NamedMacroView(object):
"""
Provides a generic ``@@macros`` view for finding a z3c.macro registration
on a context *different* than the current context.
The object that it was traversed from will become the ``context`` variable
during executing of the macro. E.g., in ``metal:use-macro="post/@@macros/a_macro"``, the
registered macro ``a_macro`` will be looked up with ``post`` as its context and will
execute with ``post`` as its ``context`` variable.
"""

def __init__(self, context, request):
self.context = context
Expand Down
10 changes: 10 additions & 0 deletions src/nti/nikola_chameleon/meta.zcml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<configure xmlns:meta="http://namespaces.zope.org/meta">

<meta:directives namespace="http://namespaces.zope.org/browser">

<meta:directive
name="newViewletManager"
schema=".zcml.IViewletManagerDirective"
handler=".zcml.viewletManagerDirective" />
</meta:directives>
</configure>
3 changes: 3 additions & 0 deletions src/nti/nikola_chameleon/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@ def _apply_request_layer(self, request, options, template):
interface.alsoProvides(request, interfaces.IBookPageKind)

def new_view_for_context(self, context, request):
# XXX: These are really layers that should be on the
# request, not the view. The view should have more of a relationship
# to the template being requested?
view = View(context, request, self)
options = request.options
if not interfaces.IPost.providedBy(context):
Expand Down
2 changes: 1 addition & 1 deletion src/nti/nikola_chameleon/request.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
The Chameleon ZPT plugin for nikola.
Request objects.
"""
from __future__ import absolute_import
Expand Down
77 changes: 77 additions & 0 deletions src/nti/nikola_chameleon/tests/test_zcml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
"""
Tests for zcml.py
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function


# disable: accessing protected members, too many methods
# pylint: disable=W0212,R0904

import unittest

from hamcrest import assert_that
from hamcrest import has_property
from hamcrest import is_not as does_not

from zope.configuration import xmlconfig
from zope.testing.cleanup import CleanUp

from .. import viewlets

class TestZCML(CleanUp,
unittest.TestCase):

def test_registers_interface(self):
zcml = """
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<include package="nti.nikola_chameleon" file="meta.zcml" />
<browser:newViewletManager
id="ILeftColumn" />
</configure>
"""

xmlconfig.string(zcml)

assert_that(viewlets, has_property('ILeftColumn'))

def test_viewlet_manager_in_same_zcml(self):
assert_that(viewlets, does_not(has_property('ILeftColumn')))
zcml = """
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<include package="nti.nikola_chameleon" file="meta.zcml" />
<include package="zope.viewlet" file="meta.zcml" />
<browser:newViewletManager
id="ILeftColumn" />
<browser:viewletManager
name="left_column"
provides="nti.nikola_chameleon.viewlets.ILeftColumn"
permission="zope.Public" />
</configure>
"""

xmlconfig.string(zcml)
assert_that(viewlets, has_property('ILeftColumn'))

context = object()
from zope.publisher.browser import TestRequest
request = TestRequest()
from zope.publisher.browser import BrowserView
view = BrowserView(context, request)

import zope.component
from zope.viewlet import interfaces
zope.component.getMultiAdapter(
(context, request, view),
interfaces.IViewletManager,
name='left_column')

zope.component.getMultiAdapter(
(context, request, view),
viewlets.ILeftColumn,
name='left_column')
7 changes: 6 additions & 1 deletion src/nti/nikola_chameleon/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ def __init__(self, context, request, templates):
class PostTextView(BaseView):
"""
For getting the text of a post, while respecting teasers.
.. versionadded:: 0.0.1a2
"""

@property
Expand Down Expand Up @@ -85,4 +87,7 @@ def preview(self):
return 'post-preview' if self.teaser else ''

class ConditionalViewletManager(ZConditionalViewletManager):
pass
"""
A viewlet manager that respects the ``weight`` and ``available``
attributes.
"""
13 changes: 13 additions & 0 deletions src/nti/nikola_chameleon/viewlets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
"""
Holds viewlet manager interfaces defined by
ZCML.
There should be no actual contents in this file.
.. versionadded:: 0.0.1a2
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
69 changes: 69 additions & 0 deletions src/nti/nikola_chameleon/zcml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
"""
ZCML directives.
.. versionadded:: 0.0.1a2
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from zope import interface
from zope.interface.interface import InterfaceClass
#from zope.configuration.fields import GlobalInterface
from zope.configuration.fields import PythonIdentifier
from zope.viewlet.interfaces import IViewletManager


from . import viewlets

class IViewletManagerDirective(interface.Interface):
"""
Creates a new viewlet manager interface in the
``nti.nikola_chameleon.viewlets`` package.
"""

id = PythonIdentifier(
title=u"The class name of the interface",
required=True
)

# TODO: Extend a viewlet manager interface?

def viewletManagerDirective(_context, id):
id = str(id) # ensure native string
if id in dir(viewlets):
return

# We must do our work right now so that it can
# be used by other directives.

manager = InterfaceClass(
id, (IViewletManager,),
__doc__='Viewlet Manager',
__module__='nti.nikola_chameleon.viewlets'
)
manager.setTaggedValue("zcml_created", True)

setattr(viewlets, id, manager)


def _cleanUp():
to_remove = []
for name, value in vars(viewlets).items():
try:
if value.getTaggedValue('zcml_created'):
to_remove.append(name)
except (AttributeError, KeyError):
pass

for name in to_remove:
delattr(viewlets, name)


try:
from zope.testing import cleanup
except ImportError:
pass
else:
cleanup.addCleanUp(_cleanUp)

0 comments on commit 993514c

Please sign in to comment.