[Checkins] SVN: zmi.core/trunk/src/zmi/core/component/ Copy browser code from zope.app.component.

Yusei Tahara yusei at domen.cx
Sun Jun 7 11:24:17 EDT 2009


Log message for revision 100690:
  Copy browser code from zope.app.component.
  

Changed:
  A   zmi.core/trunk/src/zmi/core/component/__init__.py
  A   zmi.core/trunk/src/zmi/core/component/configure.zcml
  A   zmi.core/trunk/src/zmi/core/component/ftesting.zcml
  A   zmi.core/trunk/src/zmi/core/component/registration.pt
  A   zmi.core/trunk/src/zmi/core/component/registration.py
  A   zmi.core/trunk/src/zmi/core/component/site.txt
  A   zmi.core/trunk/src/zmi/core/component/siteregistration.pt
  A   zmi.core/trunk/src/zmi/core/component/tests.py

-=-
Added: zmi.core/trunk/src/zmi/core/component/__init__.py
===================================================================
--- zmi.core/trunk/src/zmi/core/component/__init__.py	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/component/__init__.py	2009-06-07 15:24:17 UTC (rev 100690)
@@ -0,0 +1,165 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""View support for adding and configuring utilities and adapters.
+
+$Id: __init__.py 94095 2008-12-16 07:43:39Z nadako $
+"""
+__docformat__ = 'restructuredtext'
+
+import zope.component
+from zope.exceptions.interfaces import UserError
+from zope.security.proxy import removeSecurityProxy
+from zope.publisher.browser import BrowserView
+from zope.component.interfaces import IFactory
+from zope.component.interface import searchInterface
+from zope.location.interfaces import ISite
+
+from zope.app.component.i18n import ZopeMessageFactory as _
+from zope.app.component.site import LocalSiteManager
+from zmi.core.container.adding import Adding
+
+class ComponentAdding(Adding):
+    """Adding subclass used for registerable components."""
+
+    menu_id = "add_component"
+
+    def add(self, content):
+        # Override so as to save a reference to the added object
+        self.added_object = super(ComponentAdding, self).add(content)
+        return self.added_object
+
+    def nextURL(self):
+        v = zope.component.queryMultiAdapter(
+            (self.added_object, self.request), name="registration.html")
+        if v is not None:
+            url = str(zope.component.getMultiAdapter(
+                (self.added_object, self.request), name='absolute_url'))
+            return url + "/@@registration.html"
+
+        return super(ComponentAdding, self).nextURL()
+
+    def action(self, type_name, id=''):
+        # For special case of that we want to redirect to another adding view
+        # (usually another menu such as AddUtility)
+        if type_name.startswith("../"):
+            # Special case
+            url = type_name
+            if id:
+                url += "?id=" + id
+            self.request.response.redirect(url)
+            return
+
+        # Call the superclass action() method.
+        # As a side effect, self.added_object is set by add() above.
+        super(ComponentAdding, self).action(type_name, id)
+
+    _addFilterInterface = None
+
+    def addingInfo(self):
+        # A site management folder can have many things. We only want
+        # things that implement a particular interface
+        info = super(ComponentAdding, self).addingInfo()
+        if self._addFilterInterface is None:
+            return info
+        out = []
+        for item in info:
+            extra = item.get('extra')
+            if extra:
+                factoryname = extra.get('factory')
+                if factoryname:
+                    factory = zope.component.getUtility(IFactory, factoryname)
+                    intf = factory.getInterfaces()
+                    if not intf.extends(self._addFilterInterface):
+                        # We only skip new addMenuItem style objects
+                        # that don't implement our wanted interface.
+                        continue
+
+            out.append(item)
+
+        return out
+
+
+class UtilityAdding(ComponentAdding):
+    """Adding subclass used for adding utilities."""
+
+    menu_id = None
+    title = _("Add Utility")
+
+    def nextURL(self):
+        v = zope.component.queryMultiAdapter(
+            (self.added_object, self.request), name="addRegistration.html")
+        if v is not None:
+            url = zope.component.absoluteURL(self.added_object, self.request)
+            return url + "/@@addRegistration.html"
+
+        return super(UtilityAdding, self).nextURL()
+
+
+class MakeSite(BrowserView):
+    """View for converting a possible site to a site."""
+
+    def addSiteManager(self):
+        """Convert a possible site to a site
+
+        >>> from zope.traversing.interfaces import IContainmentRoot
+        >>> from zope.interface import implements
+
+        >>> class PossibleSite(object):
+        ...     implements(IContainmentRoot)
+        ...     def setSiteManager(self, sm):
+        ...         from zope.interface import directlyProvides
+        ...         directlyProvides(self, ISite)
+
+
+        >>> folder = PossibleSite()
+
+        >>> from zope.publisher.browser import TestRequest
+        >>> request = TestRequest()
+
+        Now we'll make our folder a site:
+
+        >>> MakeSite(folder, request).addSiteManager()
+
+        Now verify that we have a site:
+
+        >>> ISite.providedBy(folder)
+        1
+
+        Note that we've also redirected the request:
+
+        >>> request.response.getStatus()
+        302
+
+        >>> request.response.getHeader('location')
+        '++etc++site/@@SelectedManagementView.html'
+
+        If we try to do it again, we'll fail:
+
+        >>> MakeSite(folder, request).addSiteManager()
+        Traceback (most recent call last):
+        ...
+        UserError: This is already a site
+
+        """
+        if ISite.providedBy(self.context):
+            raise UserError(_(u'This is already a site'))
+
+        # We don't want to store security proxies (we can't,
+        # actually), so we have to remove proxies here before passing
+        # the context to the SiteManager.
+        bare = removeSecurityProxy(self.context)
+        sm = LocalSiteManager(bare)
+        self.context.setSiteManager(sm)
+        self.request.response.redirect(
+            "++etc++site/@@SelectedManagementView.html")

Added: zmi.core/trunk/src/zmi/core/component/configure.zcml
===================================================================
--- zmi.core/trunk/src/zmi/core/component/configure.zcml	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/component/configure.zcml	2009-06-07 15:24:17 UTC (rev 100690)
@@ -0,0 +1,140 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:browser="http://namespaces.zope.org/browser"
+   >
+
+<!-- Registration Managemenet -->
+
+  <browser:page
+      for="*"
+      name="registration.html"
+      permission="zope.ManageSite"
+      class=".registration.RegistrationView"
+      />
+
+  <browser:menuItem
+      menu="zmi_views"
+      title="Registration"
+      for="*"
+      action="@@registration.html"
+      order="999"
+  />
+
+  <browser:page
+      for="*"
+      name="addRegistration.html"
+      permission="zope.ManageSite"
+      class=".registration.AddUtilityRegistration" 
+      />
+
+  <adapter factory=".registration.UtilityRegistrationDisplay" /> 
+  <adapter factory=".registration.UtilitySiteRegistrationDisplay" /> 
+
+<!-- Site Management Folder -->
+
+  <browser:addMenuItem
+      class="zope.app.component.site.SiteManagementFolder"
+      permission="zope.ManageSite"
+      title="Site-Management Folder"
+      />
+
+  <browser:page
+      for="zope.app.component.interfaces.ISiteManagementFolder"
+      permission="zope.ManageSite"
+      class="zope.app.container.browser.contents.JustContents"
+      name="index.html" attribute="index"
+      />
+
+  <browser:page
+      name="contents.html"
+      for="zope.app.component.interfaces.ISiteManagementFolder"
+      menu="zmi_views" title="Contents"
+      permission="zope.ManageSite"
+      class="zope.app.container.browser.contents.Contents"
+      attribute="contents"
+      />
+
+  <browser:view
+      name="+"
+      menu="zmi_actions" title="Add"
+      for="zope.app.component.interfaces.ISiteManagementFolder"
+      permission="zope.ManageSite"
+      class="zope.app.component.browser.ComponentAdding"
+      >
+    <browser:page name="index.html"  attribute="index"  />
+    <browser:page name="action.html" attribute="action" />
+  </browser:view>
+
+<!-- Site Manager navigation action -->
+
+  <browser:page
+      for="zope.location.interfaces.IPossibleSite"
+      name="addSiteManager.html"
+      permission="zope.ManageSite"
+      class=".MakeSite"
+      attribute="addSiteManager"
+      />
+
+  <browser:menuItem
+      menu="zmi_actions" title="Make a site"
+      for="zope.location.interfaces.IPossibleSite"
+      action="@@addSiteManager.html"
+      filter="python:
+        not modules['zope.location.interfaces'].ISite.providedBy(context)"
+      permission="zope.ManageSite"
+      />
+
+  <browser:menuItem
+      menu="zmi_actions"
+      title="Manage Site"
+      for="zope.location.interfaces.ISite"
+      action="++etc++site/@@SelectedManagementView.html"
+      permission="zope.ManageSite"
+      />
+
+<!-- SiteManager -->
+
+  <browser:page
+      name="contents.html"
+      for="zope.app.component.interfaces.ILocalSiteManager"
+      menu="zmi_views" title="Contents"
+      permission="zope.ManageSite"
+      class="zope.app.container.browser.contents.Contents"
+      attribute="contents" />
+
+  <browser:view
+      name="+"
+      menu="zmi_actions" title="Add Site Management Folder"
+      for="zope.app.component.interfaces.ILocalSiteManager"
+      permission="zope.ManageSite"
+      class="zope.app.container.browser.adding.Adding"
+      >
+    <browser:page name="index.html" attribute="index"/>
+    <browser:page name="action.html" attribute="action"/>
+  </browser:view>
+
+  <browser:pages
+      for="zope.app.component.interfaces.ILocalSiteManager"
+      permission="zope.ManageSite"
+      class="zope.app.container.browser.contents.JustContents"
+      >
+    <browser:page name="index.html" attribute="index" />
+  </browser:pages>
+
+  <browser:page
+      for="zope.app.component.interfaces.ILocalSiteManager"
+      name="registrations.html"
+      menu="zmi_views" title="Registrations"
+      permission="zope.ManageSite"
+      class=".registration.SiteRegistrationView"
+      />
+
+  <browser:menuItem
+      menu="zmi_views" title="Registration"
+      for="zope.app.component.interfaces.ILocalSiteManager"
+      action="@@registration.html"
+      filter="python:False"
+      permission="zope.ManageSite"
+      />
+
+</configure>

Added: zmi.core/trunk/src/zmi/core/component/ftesting.zcml
===================================================================
--- zmi.core/trunk/src/zmi/core/component/ftesting.zcml	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/component/ftesting.zcml	2009-06-07 15:24:17 UTC (rev 100690)
@@ -0,0 +1,15 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="zope"
+    >
+    
+  <include package="zope.app.component" file="ftesting.zcml"/>
+
+  <browser:addMenuItem
+      class="zmi.core.component.tests.Sample"
+      permission="zope.ManageSite"
+      title="Sample"
+      />
+
+</configure>

Added: zmi.core/trunk/src/zmi/core/component/registration.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/component/registration.pt	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/component/registration.pt	2009-06-07 15:24:17 UTC (rev 100690)
@@ -0,0 +1,57 @@
+<html metal:use-macro="context/@@standard_macros/view"
+      i18n:domain="zope">
+<body>
+<div metal:fill-slot="body">
+
+  <form action="@@addRegistration.html" method="get"
+        tal:condition="not:view/registrations">
+    <p i18n:translate="">This object isn't yet registered.</p>
+    <input type="submit" value="Register this object"
+           i18n:attributes="value register-button" />
+  </form>
+
+  <tal:block tal:condition="view/registrations">
+  <form action="" method="post" tal:attributes="action request/URL">
+    <p i18n:translate="">
+      This object is registered:
+    </p>
+    <table>
+      <tr tal:repeat="registration view/registrations">
+         <td>
+           <input type="checkbox"
+                  class="noborder" name="ids:list"
+                  tal:attributes="value registration/id;
+                                  id registration/id;"
+                  />
+         </td>
+         <td tal:define="info registration/render">
+           <tal:block content="info/info">
+             zope.app.fooIFoo utility named bob
+           </tal:block>
+           <tal:block condition="info/comment">
+             <br />
+             <tal:block content="info/comment">
+               comment: needed a bob
+             </tal:block>
+           </tal:block>
+         </td>
+      </tr>
+      <tr>
+        <td></td>
+        <td>
+          <input type="submit" value="Unregister" name="deactivate"
+                 i18n:attributes="value unregister-button" />
+        </td>
+      </tr>
+    </table>
+  </form>
+
+  <form action="@@addRegistration.html" method="get">
+    <input type="submit" value="Register this object again"
+           i18n:attributes="value register-again-button" />
+  </form>
+  </tal:block>
+
+</div>
+</body>
+</html>

Added: zmi.core/trunk/src/zmi/core/component/registration.py
===================================================================
--- zmi.core/trunk/src/zmi/core/component/registration.py	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/component/registration.py	2009-06-07 15:24:17 UTC (rev 100690)
@@ -0,0 +1,267 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""General registry-related views
+
+$Id: registration.py 73635 2007-03-26 15:47:22Z dobe $
+"""
+import warnings
+
+from zope import interface, component, schema
+from zope.formlib import form
+from zope.publisher.browser import BrowserPage
+from zope.security.proxy import removeSecurityProxy
+import zope.component.interfaces
+import zope.publisher.interfaces.browser
+
+import zope.app.pagetemplate
+from zope.app.component.i18n import ZopeMessageFactory as _
+
+
+def _registrations(context, comp):
+    sm = component.getSiteManager(context)
+    for r in sm.registeredUtilities():
+        if r.component == comp or comp is None:
+            yield r
+    for r in sm.registeredAdapters():
+        if r.factory == comp or comp is None:
+            yield r
+    for r in sm.registeredSubscriptionAdapters():
+        if r.factory == comp or comp is None:
+            yield r
+    for r in sm.registeredHandlers():
+        if r.factory == comp or comp is None:
+            yield r
+
+class IRegistrationDisplay(interface.Interface):
+    """Display registration information
+    """
+
+    def id():
+        """Return an identifier suitable for use in mapping
+        """
+
+    def render():
+        "Return an HTML view of a registration object"
+
+    def unregister():
+        "Remove the registration by unregistering the component"
+
+class ISiteRegistrationDisplay(IRegistrationDisplay):
+    """Display registration information, including the component registered
+    """
+
+class RegistrationView(BrowserPage):
+
+    component.adapts(None, zope.publisher.interfaces.browser.IBrowserRequest)
+
+    render = zope.app.pagetemplate.ViewPageTemplateFile('registration.pt')
+
+    def registrations(self):
+        registrations = [
+            component.getMultiAdapter((r, self.request), IRegistrationDisplay)
+            for r in sorted(_registrations(self.context, self.context))
+            ]
+        return registrations
+
+    def update(self):
+        registrations = dict([(r.id(), r) for r in self.registrations()])
+        for id in self.request.form.get('ids', ()):
+            r = registrations.get(id)
+            if r is not None:
+                r.unregister()
+
+    def __call__(self):
+        self.update()
+        return self.render()
+
+class UtilityRegistrationDisplay(object):
+    """Utility Registration Details"""
+
+    component.adapts(zope.component.interfaces.IUtilityRegistration,
+                     zope.publisher.interfaces.browser.IBrowserRequest)
+    interface.implements(IRegistrationDisplay)
+
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
+    def provided(self):
+        provided = self.context.provided
+        return provided.__module__ + '.' + provided.__name__
+
+    def id(self):
+        return 'R' + (("%s %s" % (self.provided(), self.context.name))
+                      .encode('utf8')
+                      .encode('base64')
+                      .replace('+', '_')
+                      .replace('=', '')
+                      .replace('\n', '')
+                      )
+
+    def _comment(self):
+        comment = self.context.info or ''
+        if comment:
+            comment = _("comment: ${comment}", mapping={"comment": comment})
+        return comment
+
+    def _provided(self):
+        name = self.context.name
+        provided = self.provided()
+        if name:
+            info = _("${provided} utility named '${name}'",
+                     mapping={"provided": provided, "name": name})
+        else:
+            info = _("${provided} utility",
+                     mapping={"provided": provided})
+        return info
+
+    def render(self):
+        return {
+            "info": self._provided(),
+            "comment": self._comment()
+            }
+
+    def unregister(self):
+        self.context.registry.unregisterUtility(
+            self.context.component,
+            self.context.provided,
+            self.context.name,
+            )
+
+class SiteRegistrationView(RegistrationView):
+
+    render = zope.app.pagetemplate.ViewPageTemplateFile('siteregistration.pt')
+
+    def registrations(self):
+        registrations = [
+            component.getMultiAdapter((r, self.request),
+                                      ISiteRegistrationDisplay)
+            for r in sorted(_registrations(self.context, None))
+            ]
+        return registrations
+
+class UtilitySiteRegistrationDisplay(UtilityRegistrationDisplay):
+    """Utility Registration Details"""
+
+    interface.implementsOnly(ISiteRegistrationDisplay)
+
+    def render(self):
+        url = component.getMultiAdapter(
+            (self.context.component, self.request), name='absolute_url')
+        try:
+            url = url()
+        except TypeError:
+            url = ""
+
+        cname = getattr(self.context.component, '__name__', '')
+        if not cname:
+            cname = _("(unknown name)")
+        if url:
+            url += "/@@SelectedManagementView.html"
+
+        return {
+            "cname": cname,
+            "url": url,
+            "info": self._provided(),
+            "comment": self._comment()
+            }
+
+class AddUtilityRegistration(form.Form):
+    """View for registering utilities
+
+    Normally, the provided interface and name are input.
+
+    A subclass can provide an empty 'name' attribute if the component should
+    always be registered without a name.
+
+    A subclass can provide a 'provided' attribute if a component
+    should always be registered with the same interface.
+
+    """
+    component.adapts(None, zope.publisher.interfaces.browser.IBrowserRequest)
+
+    form_fields = form.Fields(
+        schema.Choice(
+           __name__ = 'provided',
+           title=_("Provided interface"),
+           description=_("The interface provided by the utility"),
+           vocabulary="Utility Component Interfaces",
+           required=True,
+           ),
+        schema.TextLine(
+           __name__ = 'name',
+           title=_("Register As"),
+           description=_("The name under which the utility will be known."),
+           required=False,
+           default=u'',
+           missing_value=u''
+           ),
+        schema.Text(
+           __name__ = 'comment',
+           title=_("Comment"),
+           required=False,
+           default=u'',
+           missing_value=u''
+           ),
+        )
+
+    name = provided = None
+
+    prefix = 'field' # in hopes of making old tests pass. :)
+
+    def __init__(self, context, request):
+        if self.name is not None:
+            self.form_fields = self.form_fields.omit('name')
+        if self.provided is not None:
+            self.form_fields = self.form_fields.omit('provided')
+        super(AddUtilityRegistration, self).__init__(context, request)
+
+    def update(self):
+        # hack to make work with old tests
+        if 'UPDATE_SUBMIT' in self.request.form:
+            warnings.warn(
+                "Old test needs to be updated.",
+                DeprecationWarning)
+            self.request.form['field.actions.register'] = 'Register'
+            self.request.form['field.comment'] = u''
+        super(AddUtilityRegistration, self).update()
+
+    @property
+    def label(self):
+        return _("Register a $classname",
+                 mapping=dict(classname=self.context.__class__.__name__)
+                 )
+
+    @form.action(_("Register"))
+    def register(self, action, data):
+        sm = component.getSiteManager(self.context)
+        name = self.name
+        if name is None:
+            name = data['name']
+        provided = self.provided
+        if provided is None:
+            provided = data['provided']
+
+        # We have to remove the security proxy to save the registration
+        sm.registerUtility(
+            removeSecurityProxy(self.context),
+            provided, name,
+            data['comment'] or '')
+
+        if 'UPDATE_SUBMIT' in self.request.form:
+            # Backward compat until 3.5
+            self.request.response.redirect('@@SelectedManagementView.html')
+            return
+
+        self.request.response.redirect('@@registration.html')

Added: zmi.core/trunk/src/zmi/core/component/site.txt
===================================================================
--- zmi.core/trunk/src/zmi/core/component/site.txt	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/component/site.txt	2009-06-07 15:24:17 UTC (rev 100690)
@@ -0,0 +1,44 @@
+Managing a Site
+---------------
+
+Create the browser object we'll be using.
+
+    >>> from zope.testbrowser.testing import Browser
+    >>> browser = Browser()
+    >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+    >>> browser.open('http://localhost/manage')
+
+When we originally enter a Zope instance, there is only a root folder that is
+already a site:
+
+    >>> 'Manage Site' in browser.contents
+    True
+
+Let's now add a new folder called ``samplesite`` and make it a site:
+
+    >>> browser.getLink(url='folder.Folder').click()
+    >>> browser.getControl(name='new_value').value = 'samplesite'
+    >>> browser.getControl('Apply').click()
+
+    >>> browser.getLink('samplesite').click()
+    >>> browser.getLink('Make a site').click()
+
+We are automatically forwarded to the site manager of the site. The default
+site management folder is always available:
+
+    >>> 'default' in browser.contents
+    True
+
+Let's now delete the site again:
+
+    >>> browser.getLink('[top]').click()
+    >>> browser.getControl(name='ids:list').getControl(
+    ...     value='samplesite').selected = True
+
+    >>> browser.handleErrors = False
+    >>> browser.getControl('Delete').click()
+
+The site should be gone now.
+
+    >>> 'samplesite' in browser.contents
+    False


Property changes on: zmi.core/trunk/src/zmi/core/component/site.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: zmi.core/trunk/src/zmi/core/component/siteregistration.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/component/siteregistration.pt	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/component/siteregistration.pt	2009-06-07 15:24:17 UTC (rev 100690)
@@ -0,0 +1,63 @@
+<html metal:use-macro="context/@@standard_macros/view"
+    i18n:domain="zope">
+<body>
+<div metal:fill-slot="body">
+<form tal:attributes="action request/URL"
+      method="POST"
+      >
+  <div tal:condition="not:view/registrations">
+    <p i18n:translate="">Nothing is registered for this site.</p>
+  </div>
+  <div tal:condition="view/registrations">
+    <p i18n:translate="">
+      Registrations for this site:
+    </p>
+    <table>
+      <tr tal:repeat="registration view/registrations">
+         <td>
+           <input type="checkbox"
+                  class="noborder" name="ids:list"
+                  tal:attributes="value registration/id;
+                                  id registration/id;
+                                  "
+                  />
+         </td>
+         <td tal:define="info registration/render">
+           <tal:block condition="info/url">
+             <a href="foo/bar" tal:attributes="href info/url"
+               tal:content="info/cname">foo/bar</a>
+           </tal:block>
+           <tal:block condition="not: info/url"
+             i18n:translate="">
+             <tal:block i18n:name="name" content="info/cname">
+               foo/bar
+             </tal:block>
+             (moved or deleted)
+           </tal:block>
+           <br />
+           <tal:block content="info/info">
+             zope.app.fooIFoo utility named bob
+           </tal:block>
+           <tal:block condition="info/comment">
+             <br />
+             <tal:block content="info/comment">
+               comment: needed a bob
+             </tal:block>
+           </tal:block>
+         </td>
+      </tr>
+      <tr>
+        <td></td>
+        <td>
+          <input type="submit" value="Unregister" name="deactivate"
+                 i18n:attributes="value unregister-button" />
+        </td>
+      </tr>
+    </table>
+  </div>
+
+</form>
+
+</div>
+</body>
+</html>

Added: zmi.core/trunk/src/zmi/core/component/tests.py
===================================================================
--- zmi.core/trunk/src/zmi/core/component/tests.py	                        (rev 0)
+++ zmi.core/trunk/src/zmi/core/component/tests.py	2009-06-07 15:24:17 UTC (rev 100690)
@@ -0,0 +1,49 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Registration functional tests
+
+$Id: tests.py 92115 2008-10-13 13:18:24Z sidnei $
+"""
+import os.path
+import unittest
+import zope.app.testing.functional
+from zope import interface
+from zope.app.component.testing import AppComponentLayer
+from zope.app.testing import functional
+from zope.testing import doctest
+
+AppComponentBrowserLayer = functional.ZCMLLayer(
+    os.path.join(os.path.dirname(__file__), 'ftesting.zcml'),
+    __name__, 'AppComponentBrowserLayer', allow_teardown=True)
+
+class ISampleBase(interface.Interface):
+    pass
+
+class ISample(ISampleBase):
+    pass
+
+class Sample:
+    interface.implements(ISample)
+
+
+def test_suite():
+    site = zope.app.testing.functional.FunctionalDocFileSuite(
+        "site.txt",
+        optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)
+    site.layer = AppComponentBrowserLayer
+    return unittest.TestSuite((site,))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+



More information about the Checkins mailing list