[Checkins] SVN: z3ext.product/ initial import

Nikolay Kim fafhrd at datacom.kz
Tue Mar 25 14:41:11 EDT 2008


Log message for revision 84940:
  initial import

Changed:
  A   z3ext.product/
  A   z3ext.product/branches/
  A   z3ext.product/tags/
  A   z3ext.product/trunk/
  A   z3ext.product/trunk/AUTHOR.txt
  A   z3ext.product/trunk/CHANGES.txt
  A   z3ext.product/trunk/LICENSE.txt
  A   z3ext.product/trunk/bootstrap.py
  A   z3ext.product/trunk/buildout.cfg
  A   z3ext.product/trunk/setup.py
  A   z3ext.product/trunk/src/
  A   z3ext.product/trunk/src/z3ext/
  A   z3ext.product/trunk/src/z3ext/__init__.py
  A   z3ext.product/trunk/src/z3ext/product/
  A   z3ext.product/trunk/src/z3ext/product/README.txt
  A   z3ext.product/trunk/src/z3ext/product/__init__.py
  A   z3ext.product/trunk/src/z3ext/product/browser.zcml
  A   z3ext.product/trunk/src/z3ext/product/configure.zcml
  A   z3ext.product/trunk/src/z3ext/product/i18n.py
  A   z3ext.product/trunk/src/z3ext/product/installer.py
  A   z3ext.product/trunk/src/z3ext/product/interfaces.py
  A   z3ext.product/trunk/src/z3ext/product/meta.zcml
  A   z3ext.product/trunk/src/z3ext/product/product.py
  A   z3ext.product/trunk/src/z3ext/product/product_icon.gif
  A   z3ext.product/trunk/src/z3ext/product/registry.py
  A   z3ext.product/trunk/src/z3ext/product/tests.py
  A   z3ext.product/trunk/src/z3ext/product/utils.py
  A   z3ext.product/trunk/src/z3ext/product/view.pt
  A   z3ext.product/trunk/src/z3ext/product/view.py
  A   z3ext.product/trunk/src/z3ext/product/zcml.py

-=-

Property changes on: z3ext.product/trunk
___________________________________________________________________
Name: svn:ignore
   + bin
coverage
develop-eggs
eggs
parts
.installed.cfg


Added: z3ext.product/trunk/AUTHOR.txt
===================================================================
--- z3ext.product/trunk/AUTHOR.txt	                        (rev 0)
+++ z3ext.product/trunk/AUTHOR.txt	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1 @@
+Nikolay Kim (fafhrd91 <at> gmail <dot> com)

Added: z3ext.product/trunk/CHANGES.txt
===================================================================
--- z3ext.product/trunk/CHANGES.txt	                        (rev 0)
+++ z3ext.product/trunk/CHANGES.txt	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,47 @@
+=======
+CHANGES
+=======
+
+1.0.0 (2008-03-26)
+------------------
+
+- Tests added
+
+- Code moved to svn.zope.org
+
+
+0.11.1 (2008-03-04)
+-------------------
+
+- Added 'broken' base registry in case product not available.
+
+
+0.11 (2008-02-28)
+-----------------
+
+- Use z3c.baseregistry
+
+- Use z3c.autoinclude
+
+- Create components registry for each product.
+  now it's possible to use <registerIn registry="z3ext.product.productName'/>
+  for registering product adapters and utulities.
+
+
+0.10.2 (2008-02-18)
+------------------
+
+- Don't show 'Products Management' configlet
+  if there are no installable products
+
+
+0.10.1 (2008-02-01)
+------------------
+
+- Removed `grant` directives
+
+
+0.10 (2008-02-01)
+------------------
+
+- Initial release

Added: z3ext.product/trunk/LICENSE.txt
===================================================================
--- z3ext.product/trunk/LICENSE.txt	                        (rev 0)
+++ z3ext.product/trunk/LICENSE.txt	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,54 @@
+Zope Public License (ZPL) Version 2.1
+-------------------------------------
+
+A copyright notice accompanies this license document that
+identifies the copyright holders.
+
+This license has been certified as open source. It has also
+been designated as GPL compatible by the Free Software
+Foundation (FSF).
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+1. Redistributions in source code must retain the
+   accompanying copyright notice, this list of conditions,
+   and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the accompanying
+   copyright notice, this list of conditions, and the
+   following disclaimer in the documentation and/or other
+   materials provided with the distribution.
+
+3. Names of the copyright holders must not be used to
+   endorse or promote products derived from this software
+   without prior written permission from the copyright
+   holders.
+
+4. The right to distribute this software or to use it for
+   any purpose does not give you the right to use
+   Servicemarks (sm) or Trademarks (tm) of the copyright
+   holders. Use of them is covered by separate agreement
+   with the copyright holders.
+
+5. If any files are modified, you must cause the modified
+   files to carry prominent notices stating that you changed
+   the files and the date of any change.
+
+Disclaimer
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS''
+  AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+  NO EVENT SHALL THE COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+  OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+  DAMAGE.

Added: z3ext.product/trunk/bootstrap.py
===================================================================
--- z3ext.product/trunk/bootstrap.py	                        (rev 0)
+++ z3ext.product/trunk/bootstrap.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 1839 2008-03-25 13:28:26Z fafhrd91 $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)

Added: z3ext.product/trunk/buildout.cfg
===================================================================
--- z3ext.product/trunk/buildout.cfg	                        (rev 0)
+++ z3ext.product/trunk/buildout.cfg	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,18 @@
+[buildout]
+develop = .
+parts = test coverage-test coverage-report
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3ext.product [test]
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = z3ext.product [test]
+defaults = ['--coverage', '../../coverage']
+
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')

Added: z3ext.product/trunk/setup.py
===================================================================
--- z3ext.product/trunk/setup.py	                        (rev 0)
+++ z3ext.product/trunk/setup.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,77 @@
+##############################################################################
+#
+# Copyright (c) 2008 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.
+#
+##############################################################################
+"""Setup for z3ext.product package
+
+$Id: setup.py 1842 2008-03-25 16:41:22Z fafhrd91 $
+"""
+import sys, os
+from setuptools import setup, find_packages
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+version = '1.0.0dev'
+
+
+setup(name='z3ext.product',
+      version=version,
+      description="Implementation of product (add-on) concept.",
+      long_description=(
+          'Detailed Documentation\n' +
+          '======================\n'
+          + '\n\n' +
+          read('src', 'z3ext', 'product', 'README.txt')
+          + '\n\n' +
+          read('CHANGES.txt')
+          ),
+      classifiers=[
+        'Development Status :: 5 - Production/Stable',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Zope Public License',
+        'Programming Language :: Python',
+        'Natural Language :: English',
+        'Operating System :: OS Independent',
+        'Topic :: Internet :: WWW/HTTP',
+        'Framework :: Zope3'],
+      author='Nikolay Kim',
+      author_email='fafhrd91 at gmail.com',
+      url='http://z3ext.net/',
+      license='ZPL 2.1',
+      packages=find_packages('src'),
+      package_dir = {'':'src'},
+      namespace_packages=['z3ext'],
+      install_requires = ['setuptools',
+                          'ZODB3',
+                          'zope.schema',
+                          'zope.component',
+                          'zope.interface',
+                          'zope.security',
+                          'zope.i18nmessageid',
+                          'zope.lifecycleevent',
+                          'zope.configuration',
+                          'zope.app.component',
+			  'z3c.baseregistry',
+			  'z3c.configurator',
+			  'z3c.autoinclude',
+			  'z3ext.layout',
+			  'z3ext.controlpanel',
+                          'z3ext.statusmessage',
+                          ],
+      extras_require = dict(test=['zope.app.testing',
+                                  'zope.testing',
+                                  ]),
+      include_package_data = True,
+      zip_safe = False
+      )


Property changes on: z3ext.product/trunk/setup.py
___________________________________________________________________
Name: svn:executable
   + *

Added: z3ext.product/trunk/src/z3ext/__init__.py
===================================================================
--- z3ext.product/trunk/src/z3ext/__init__.py	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/__init__.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,6 @@
+# namespace package boilerplate
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError, e:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)

Added: z3ext.product/trunk/src/z3ext/product/README.txt
===================================================================
--- z3ext.product/trunk/src/z3ext/product/README.txt	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/README.txt	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,274 @@
+=================
+Products (Add-on)
+=================
+
+Products system based on z3c.baseregistry package. This package
+simplify creating and managing registries.
+
+Loading zcml configuration
+
+  >>> import z3ext.product
+  >>> from zope.configuration import xmlconfig
+  >>> context = xmlconfig.string("""
+  ... <configure xmlns:z3ext="http://namespaces.zope.org/z3ext"
+  ...    xmlns="http://namespaces.zope.org/zope" i18n_domain="z3ext">
+  ...    <include package="z3ext.controlpanel" file="meta.zcml" />
+  ...    <include package="z3ext.product" file="meta.zcml" />
+  ...    <include package="zope.security" file="meta.zcml" />
+  ...    <include package="z3c.baseregistry" file="meta.zcml" />
+  ...    
+  ...    <permission
+  ...      id="z3ext.ManageProducts"
+  ...      title="Manage products" />
+  ...      
+  ...    <z3ext:configlet
+  ...      name="product"
+  ...      schema="z3ext.product.interfaces.IProductInstaller"
+  ...      title="Products management"
+  ...      description="This is the Add-on Products install section."
+  ...      class="z3ext.product.installer.ProductsInstaller"
+  ...      permission="z3ext.ManageProducts" />
+  ...
+  ... </configure>""")
+
+  >>> from zope import component
+  >>> from z3ext.controlpanel.interfaces import IConfiglet
+
+  >>> installer = component.getUtility(IConfiglet, 'product')
+  >>> installer.keys()
+  ()
+
+  >>> installer.isAvailable()
+  False
+
+  >>> from zope import schema, interface
+
+  >>> class IMyProduct(interface.Interface):
+  ...     """ Basic product """
+  ...
+  ...     email = schema.TextLine(
+  ...         title=u"E-mail Address",
+  ...         description=u"E-mail Address used to send notifications")
+
+  >>> context = xmlconfig.string('''
+  ... <configure
+  ...    xmlns:z3ext="http://namespaces.zope.org/z3ext" i18n_domain="test">
+  ... 
+  ...   <z3ext:product
+  ...     name="my-product"
+  ...     title="My product"
+  ...     configurable="true"
+  ...     schema="z3ext.product.README.IMyProduct" />
+  ...
+  ... </configure>''', context)
+
+After registration we can get product declaration by it's schema.
+
+  >>> from z3ext.product.interfaces import IProduct
+
+  >>> product = component.getUtility(IMyProduct)
+  >>> product
+  <z3ext.controlpanel.configlettype.Configlet<product.my-product> ...>
+
+  >>> product.__title__
+  u'My product'
+
+  >>> IMyProduct.providedBy(product)
+  True
+
+Or we can get product by it's name
+
+  >>> component.getUtility(IProduct, 'my-product') is product
+  True
+
+But product is also is configlet, configlet name is 'product.' prefix
+and product name
+
+  >>> configlet = component.getUtility(IConfiglet, 'product.my-product')
+  >>> configlet
+  <z3ext.controlpanel.configlettype.Configlet<product.my-product> ...>
+
+  >>> configlet is product
+  True
+
+
+Product manipulation
+--------------------
+
+instalation status
+
+  >>> product.isInstalled()
+  False
+
+or
+
+  >>> product.__installed__
+  False
+
+instalation
+
+  >>> product.install()
+  >>> product.__installed__
+  True
+
+  >>> product.install()
+  Traceback (most recent call last):
+  ...
+  ProductAlreadyInstalledError: Product already installed.
+  
+
+updateing product
+
+  >>> product.update()
+
+uninstall
+
+  >>> product.uninstall()
+  >>> product.__installed__
+  False
+
+we can't uninstall or update not installed product
+
+  >>> product.uninstall()
+  Traceback (most recent call last):
+  ...
+  ProductNotInstalledError: Product not installed.
+
+  >>> product.update()
+  Traceback (most recent call last):
+  ...
+  ProductNotInstalledError: Product not installed.
+
+
+Product extension
+-----------------
+
+We can create product extension
+
+  >>> class IMyProductExtension(interface.Interface):
+  ...     """Basic User Preferences"""
+
+  >>> context = xmlconfig.string('''
+  ... <configure
+  ...    xmlns:z3ext="http://namespaces.zope.org/z3ext" i18n_domain="test">
+  ... 
+  ...   <z3ext:product
+  ...     name="my-product.ext"
+  ...     title="Product extension"
+  ...     schema="z3ext.product.README.IMyProductExtension" />
+  ...
+  ... </configure>''', context)
+
+  >>> ext = component.getUtility(IMyProductExtension)
+  >>> ext
+  <z3ext.controlpanel.configlettype.Configlet<product.my-product.ext> ...>
+
+  >>> ext.__installed__
+  False
+
+  >>> ext.isInstalled()
+  False
+
+  >>> component.getUtility(IProduct, 'my-product.ext') is ext
+  True
+  
+  >>> component.getUtility(IConfiglet, 'product.my-product.ext') is ext
+  True
+
+We have to install product first, then we can install product extension
+
+  >>> ext.install()
+  Traceback (most recent call last):
+  ...
+  ProductNotInstalledError: my-product
+
+  >>> ext.update()
+  Traceback (most recent call last):
+  ...
+  ProductNotInstalledError: my-product
+
+  >>> product.install()
+  >>> ext.install()
+
+  >>> ext = component.getUtility(IMyProductExtension)
+  >>> ext.__installed__
+  True
+
+  >>> ext.isInstalled()
+  True
+
+  >>> ext.update()
+
+  >>> ext.uninstall()
+  >>> ext.__installed__
+  False
+
+If we uninstall product, extension will uninstall also
+
+  >>> ext.install()
+  >>> product.uninstall()
+  >>> ext.__installed__
+  False
+
+  >>> product.listExtensions()
+  [u'ext']
+
+
+Component registry
+------------------
+
+When we register product, system automaticly creates component registry
+
+  >>> registry = z3ext.product.registries['my-product']
+
+  >>> print registry
+  Product: My product
+
+  >>> repr(registry)
+  '<Product: My product>'
+
+  >>> component.interfaces.IComponents.providedBy(registry)
+  True
+
+When we install product, product's component registry automaticly
+added to current site manager bases.
+
+  >>> component.getSiteManager().__bases__
+  (<BaseGlobalComponents base>,)
+
+  >>> product.install()
+
+  >>> component.getSiteManager().__bases__
+  (<Product: My product>, <BaseGlobalComponents base>)
+
+  >>> product.uninstall()
+  >>> component.getSiteManager().__bases__
+  (<BaseGlobalComponents base>,)
+
+With z3c.baseregistry it's possible to use <registerIn />
+directive. For example:
+
+<registerIn registry="z3ext.product.my-product>
+  ... various declarations
+</registerIn>
+
+
+Product configlet
+-----------------
+
+  >>> installer = component.getUtility(IConfiglet, 'product')
+  >>> installer.keys()
+  (u'my-product',)
+
+  >>> installer.isAvailable()
+  True
+
+  >>> installer['my-product'] is product
+  True
+
+  >>> product.isAvailable()
+  False
+
+  >>> product.install()
+  >>> product.isAvailable()
+  True

Added: z3ext.product/trunk/src/z3ext/product/__init__.py
===================================================================
--- z3ext.product/trunk/src/z3ext/product/__init__.py	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/__init__.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,5 @@
+# This file is necessary to make this directory a package.
+
+registries = {}
+
+from z3ext.product.product import Product, ProductExtension

Added: z3ext.product/trunk/src/z3ext/product/browser.zcml
===================================================================
--- z3ext.product/trunk/src/z3ext/product/browser.zcml	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/browser.zcml	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,40 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:z3ext="http://namespaces.zope.org/z3ext"
+   xmlns:browser="http://namespaces.zope.org/browser"
+   i18n_domain="z3ext.product">
+
+  <browser:icon
+     name="zmi_icon"
+     title="Product"
+     for=".interfaces.IProduct"
+     file="product_icon.gif" />
+
+  <browser:icon
+     name="zmi_icon"
+     title="Products management"
+     for=".interfaces.IProductInstaller"
+     file="product_icon.gif" />
+
+  <browser:icon
+     name="zmi_icon"
+     title="Product extension"
+     for=".interfaces.IProductExtension"
+     file="product_icon.gif" />
+
+  <z3ext:pagelet
+     name="index.html"
+     for=".interfaces.IProductInstaller"
+     template="view.pt"
+     class=".view.InstallerView"
+     permission="z3ext.ManageProducts"  />
+
+  <browser:menuItem
+     for=".interfaces.IProductInstaller"
+     menu="zmi_views"
+     title="Products management"
+     action="@@index.html"
+     permission="z3ext.ManageProducts"
+     order="1" />
+
+</configure>

Added: z3ext.product/trunk/src/z3ext/product/configure.zcml
===================================================================
--- z3ext.product/trunk/src/z3ext/product/configure.zcml	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/configure.zcml	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,23 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:zcml="http://namespaces.zope.org/zcml"
+   xmlns:z3ext="http://namespaces.zope.org/z3ext"
+   i18n_domain="z3ext.product">
+
+  <autoinclude package="z3ext.product" />
+
+  <permission
+     id="z3ext.ManageProducts"
+     title="Manage products" />
+
+  <z3ext:configlet
+     name="product"
+     schema=".interfaces.IProductInstaller"
+     title="Products management"
+     description="This is the Add-on Products install section."
+     class=".installer.ProductsInstaller"
+     permission="z3ext.ManageProducts" />
+
+  <include file="browser.zcml" />
+
+</configure>

Added: z3ext.product/trunk/src/z3ext/product/i18n.py
===================================================================
--- z3ext.product/trunk/src/z3ext/product/i18n.py	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/i18n.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,19 @@
+##############################################################################
+#
+# Copyright (c) 2008 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.
+#
+##############################################################################
+"""
+
+$Id: interfaces.py 860 2008-01-09 08:48:08Z fafhrd91 $
+"""
+from zope.i18nmessageid import MessageFactory
+_ = MessageFactory('z3ext')

Added: z3ext.product/trunk/src/z3ext/product/installer.py
===================================================================
--- z3ext.product/trunk/src/z3ext/product/installer.py	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/installer.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# Copyright (c) 2008 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.
+#
+##############################################################################
+""" 
+
+$Id: installer.py 1472 2008-02-18 11:35:13Z fafhrd91 $
+"""
+from zope import interface
+from z3ext.controlpanel.interfaces import IConfiglet
+
+
+class ProductsInstaller(object):
+
+    def isAvailable(self):
+        if not len(self):
+            return False
+
+        return super(ProductsInstaller, self).isAvailable()

Added: z3ext.product/trunk/src/z3ext/product/interfaces.py
===================================================================
--- z3ext.product/trunk/src/z3ext/product/interfaces.py	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/interfaces.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,116 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" z3ext.product interfaces
+
+$Id: interfaces.py 1843 2008-03-25 18:39:00Z fafhrd91 $
+"""
+from zope import schema, interface
+
+
+class ProductError(Exception):
+    """ base error class for product management """
+
+
+class ProductNotInstalledError(ProductError):
+    """ """
+
+
+class ProductAlreadyInstalledError(ProductError):
+    """ """
+
+
+class InvalidProduct(ProductError):
+    """ """
+
+
+class ProductWarningError(ProductError):
+    """ dependencies error """
+
+
+class IProduct(interface.Interface):
+    """ product information """
+
+    __product_name__ = schema.TextLine(
+        title = u'Product name',
+        required = True)
+
+    __installed__ = schema.Bool(
+        title = u'Installed',
+        description = u'Is product installed.',
+        default = False,
+        required = False)
+
+    def install():
+        """ install and configure product """
+
+    def uninstall():
+        """ uninstall product """
+
+    def update():
+        """ update product """
+
+    def isInstalled():
+        """ is product installed """
+
+    def listExtensions():
+        """ list IProductExtension for this product """
+
+
+class IProductExtension(interface.Interface):
+    """ product extension """
+
+
+class IProductInstaller(interface.Interface):
+    """ installer for external products """
+
+
+class IAbstractProductEvent(interface.Interface):
+    """ base event interface """
+
+    id = schema.TextLine(
+        title = u'Product id',
+        required = True)
+
+    product = interface.Attribute('IProduct object')
+
+
+class IProductInstalledEvent(IAbstractProductEvent):
+    """ new product installed """
+
+
+class IProductUninstalledEvent(IAbstractProductEvent):
+    """ product uninstalled """
+
+
+class IProductUpdatedEvent(IAbstractProductEvent):
+    """ product updated """
+
+
+class AbstractProductEvent(object):
+
+    def __init__(self, id, product):
+        self.id = id
+        self.product = product
+
+
+class ProductInstalledEvent(AbstractProductEvent):
+    interface.implements(IProductInstalledEvent)
+
+
+class ProductUninstalledEvent(AbstractProductEvent):
+    interface.implements(IProductUninstalledEvent)
+
+
+class ProductUpdatedEvent(AbstractProductEvent):
+    interface.implements(IProductUpdatedEvent)

Added: z3ext.product/trunk/src/z3ext/product/meta.zcml
===================================================================
--- z3ext.product/trunk/src/z3ext/product/meta.zcml	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/meta.zcml	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,21 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:meta="http://namespaces.zope.org/meta">
+
+  <meta:directives namespace="http://namespaces.zope.org/z3ext">
+    <meta:complexDirective
+       name="product"
+       schema=".zcml.IProductDirective"
+       handler=".zcml.ProductDirective">
+
+      <meta:subdirective
+         name="allow"
+         schema="zope.app.component.metadirectives.IAllowSubdirective" />
+
+      <meta:subdirective
+         name="require"
+         schema="zope.app.component.metadirectives.IRequireSubdirective" />
+    </meta:complexDirective>
+  </meta:directives>
+
+</configure>

Added: z3ext.product/trunk/src/z3ext/product/product.py
===================================================================
--- z3ext.product/trunk/src/z3ext/product/product.py	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/product.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,124 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+"""
+
+$Id: product.py 1843 2008-03-25 18:39:00Z fafhrd91 $
+"""
+from BTrees.OOBTree import OOBTree
+
+from zope import interface, event
+from zope.component import getSiteManager
+from zope.component import getUtility, queryUtility, getUtilitiesFor
+
+from z3c.configurator import configure
+
+import z3ext.product
+from z3ext.controlpanel.configlettype import ConfigletProperty
+
+from z3ext.product.i18n import _
+from z3ext.product import interfaces
+from z3ext.product.interfaces import IProduct, IProductExtension
+
+
+class Product(object):
+    """ base product class """
+    interface.implements(IProduct)
+
+    __installed__ = ConfigletProperty(IProduct['__installed__'])
+
+    def install(self):
+        if self.__installed__:
+            raise interfaces.ProductAlreadyInstalledError(
+                _('Product already installed.'))
+
+        self.__installed__ = True
+        self.update()
+
+        sm = getSiteManager()
+
+        registry = getattr(z3ext.product, self.__product_name__)
+        if registry not in sm.__bases__:
+            sm.__bases__ = (registry,) + sm.__bases__
+
+        event.notify(interfaces.ProductInstalledEvent(self.__product_name__, self))
+
+    def update(self):
+        if not self.__installed__:
+            raise interfaces.ProductNotInstalledError(
+                _('Product not installed.'))
+
+        configure(self, {})
+
+        site = getSiteManager()
+        registry = getattr(z3ext.product, self.__product_name__)
+        if registry not in site.__bases__:
+            site.__bases__ = (registry,) + site.__bases__
+
+        event.notify(interfaces.ProductUpdatedEvent(
+            self.__product_name__, self))
+
+    def uninstall(self):
+        for name, ext in self.items():
+            if IProductExtension.providedBy(ext) and ext.__installed__:
+                ext.uninstall()
+
+        if not self.__installed__:
+            raise interfaces.ProductNotInstalledError(
+                _('Product not installed.'))
+
+        self.__installed__ = False
+
+        site = getSiteManager()
+        registry = getattr(z3ext.product, self.__product_name__)
+
+        if registry in site.__bases__:
+            bases = list(site.__bases__)
+            bases.remove(registry)
+            site.__bases__ = tuple(bases)
+
+        event.notify(interfaces.ProductUninstalledEvent(
+            self.__product_name__, self))
+
+    def isInstalled(self):
+        return self.__installed__
+
+    def listExtensions(self):
+        exts = []
+        for name, ext in self.items():
+            if IProductExtension.providedBy(ext):
+                exts.append(name)
+
+        return exts
+
+
+class ProductExtension(Product):
+    interface.implements(IProductExtension)
+
+    def install(self):
+        if not self.__parent__.__installed__:
+            raise interfaces.ProductNotInstalledError(
+                self.__parent__.__product_name__)
+        super(ProductExtension, self).install()
+
+    def update(self):
+        if not self.__parent__.__installed__:
+            raise interfaces.ProductNotInstalledError(
+                self.__parent__.__product_name__)
+        super(ProductExtension, self).update()
+
+    def isInstalled(self):
+        if self.__parent__.__installed__:
+            return self.__installed__
+        else:
+            return False

Added: z3ext.product/trunk/src/z3ext/product/product_icon.gif
===================================================================
(Binary files differ)


Property changes on: z3ext.product/trunk/src/z3ext/product/product_icon.gif
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: z3ext.product/trunk/src/z3ext/product/registry.py
===================================================================
--- z3ext.product/trunk/src/z3ext/product/registry.py	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/registry.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,63 @@
+##############################################################################
+#
+# Copyright (c) 2008 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.
+#
+##############################################################################
+""" 
+
+$Id:  2007-12-12 12:27:02Z fafhrd $
+"""
+from zope.component import queryUtility
+from zope.component.interfaces import IComponents
+from zope.component import globalregistry, globalSiteManager
+
+import z3ext.product
+from z3ext.product.i18n import _
+
+
+def BC(components, name):
+    bc = getattr(z3ext.product, name, None)
+    if bc is None:
+        return broken
+    else:
+        return bc
+
+
+class ProductRegistry(globalregistry.BaseGlobalComponents):
+
+    def __init__(self, name, title):
+        self.title = title
+        self.__name__ = name
+        self.__parent__ =  globalSiteManager
+        super(ProductRegistry, self).__init__(name)
+
+    def __str__(self):
+        return "Product: %s"%self.title
+
+    def __repr__(self):
+        return "<Product: %s>"%self.title
+
+    def __reduce__(self):
+        # Global site managers are pickled as global objects 
+        return BC, (self.__parent__, self.__name__)
+
+
+class BrokenProductRegistry(globalregistry.BaseGlobalComponents):
+
+    def __init__(self):
+        self.__parent__ =  globalSiteManager
+        super(BrokenProductRegistry, self).__init__('broken')
+
+    def __repr__(self):
+        return _("Product: Broken product!!!")
+
+
+broken = BrokenProductRegistry()

Added: z3ext.product/trunk/src/z3ext/product/tests.py
===================================================================
--- z3ext.product/trunk/src/z3ext/product/tests.py	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/tests.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" 
+
+$Id: tests.py 84833 2008-03-21 09:09:54Z fafhrd $
+"""
+import unittest, doctest
+from zope.component import testing
+from zope.app.testing import setup
+from z3ext.controlpanel.testing import setUpControlPanel
+
+def setUp(test):
+    setup.placefulSetUp(True)
+    setup.setUpTestAsModule(test, 'z3ext.product.README')
+    setUpControlPanel()
+
+def tearDown(test):
+    setup.placefulTearDown()
+    setup.tearDownTestAsModule(test)
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite(
+            'README.txt',
+            setUp=setUp, tearDown=tearDown,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
+        ))

Added: z3ext.product/trunk/src/z3ext/product/utils.py
===================================================================
--- z3ext.product/trunk/src/z3ext/product/utils.py	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/utils.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,98 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+"""
+
+$Id: utils.py 1839 2008-03-25 13:28:26Z fafhrd91 $
+"""
+from zope import interface, event, component
+from zope.component import queryUtility
+from zope.lifecycleevent import ObjectCreatedEvent
+from zope.security.proxy import removeSecurityProxy
+
+from zope.app.component.hooks import getSite
+from zope.app.component.site import SiteManagementFolder
+from zope.app.component.interfaces import ISite
+
+from z3ext.product.i18n import _
+from z3ext.product.interfaces import IProduct, IProductExtension
+
+
+def registerUtility(id, factory, ifaces, container='system'):
+    site = getSite()
+    if not ISite.providedBy(site):
+        raise RuntimeError(_("Can't create utility."))
+
+    sm = component.getSiteManager()
+
+    if isinstance(container, basestring):
+        if container:
+            if container not in sm:
+                folder = SiteManagementFolder()
+                event.notify(ObjectCreatedEvent(folder))
+                sm[container] = folder
+
+            container = sm[container]
+        else:
+            container = sm
+    elif container is None:
+        container = sm
+
+    if id not in container:
+        if callable(factory):
+            utility = factory()
+        else:
+            utility = component.createObject(factory)
+
+        event.notify(ObjectCreatedEvent(utility))
+        removeSecurityProxy(container)[id] = utility
+
+        for iface, name in ifaces:
+            sm.registerUtility(utility, iface, name)
+
+    return container[id]
+
+
+def unregisterUtility(id, ifaces, container='system'):
+    site = getSite()
+    sm = component.getSiteManager()
+
+    if container not in sm:
+        return
+
+    container = sm[container]
+
+    if id in container:
+        utility = container[id]
+
+        for iface, name in ifaces:
+            sm.unregisterUtility(utility, iface, name)
+
+        del container[id]
+
+
+class ProductTest(object):
+
+    def __init__(self, product):
+        self.product = product
+
+    def __call__(self, *args, **kw):
+        product = queryUtility(IProduct, self.product)
+        if product is not None:
+            return product.__installed__
+
+        product = queryUtility(IProductExtension, self.product)
+        if product is not None:
+            return product.__installed__
+
+        return False

Added: z3ext.product/trunk/src/z3ext/product/view.pt
===================================================================
--- z3ext.product/trunk/src/z3ext/product/view.pt	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/view.pt	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,86 @@
+<div i18n:domain="z3ext.product" tal:define="data view/getProducts">
+  <div class="topframe" tal:condition="data/installed">
+    <h1>Installed Products</h1>
+
+    <form tal:attributes="action request/URL" method="post">
+      <ul class="listing">
+	<li tal:repeat="product data/installed">
+	  <div class="icon">
+	    <input class="noborder" type="checkbox"
+                   name="products:list" tal:attributes="value product/name" />
+	    <img tal:replace="structure product/product/@@zmi_icon" />
+	  </div>
+	  <div class="details">
+	    <a tal:omit-tag="not:product/configlet"
+	       tal:attributes="href string:${product/name}/">
+	      <span tal:content="product/title"></span>
+	    </a>
+	    <div tal:content="product/description"></div>
+	  </div>
+	  <div class="small"
+	       tal:define="name product/name;
+			   extensions python:view.getExtensions(product['product'])"
+	       tal:condition="extensions">
+	    <ul class="listing">
+	      <li tal:repeat="product extensions">
+		<div class="icon">
+		  <img tal:replace="structure product/@@zmi_icon" />
+		</div>
+		<div class="details">
+		  <span>
+		    <a tal:omit-tag="not:product/isAvailable"
+		       tal:attributes="href string:${product/__parent__/__name__}/${product/__name__}/">
+		      <span tal:content="product/__title__"></span>
+		    </a>
+		    <a tal:attributes="href 
+		         string:${context/@@absolute_url}/?extension=${product/__id__}"
+		       tal:condition="product/__installed__">
+		      (Installed)
+		    </a>
+		    <a tal:attributes="href
+		         string:${context/@@absolute_url}/?extension=${product/__id__}"
+		       tal:condition="not:product/__installed__">
+		      (Not installed)
+		    </a>
+		  </span>
+		  <div tal:content="product/__description__"></div>
+		</div>
+	      </li>
+	    </ul>
+	  </div>
+	</li>
+      </ul>
+
+      <div class="formControls">
+	<input type="submit" class="z-form-button" name="update" value="Update" />
+	<input type="submit" class="z-form-removebutton" name="uninstall" value="Uninstall" />
+      </div>
+    </form>
+  </div>
+  
+  <div class="frame" tal:condition="data/notinstalled"
+       tal:attributes="class python:not data['installed'] and 'topframe' or 'frame'">
+    <h1>Products available for install</h1>
+    <p>This is the Add-on Products install section, you can add products in the lists below.</p>
+
+    <form tal:attributes="action request/URL" method="post">
+      <ul class="listing">
+	<li tal:repeat="product data/notinstalled">
+	  <div class="icon">
+	    <input class="noborder" type="checkbox"
+                   name="products:list" tal:attributes="value product/name" />
+	    <img tal:replace="structure product/product/@@zmi_icon" />
+	  </div>
+	  <div class="details">
+	    <span tal:content="product/title"></span>
+	    <div tal:content="product/description"></div>
+	  </div>
+	</li>
+      </ul>
+
+      <div class="formControls">
+	<input type="submit" class="context" name="install" value="Install" />
+      </div>
+    </form>
+  </div>
+</div>

Added: z3ext.product/trunk/src/z3ext/product/view.py
===================================================================
--- z3ext.product/trunk/src/z3ext/product/view.py	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/view.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,150 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" z3ext.product interfaces
+
+$Id: view.py 1839 2008-03-25 13:28:26Z fafhrd91 $
+"""
+import logging, sys
+from transaction import abort
+from zope.component import getUtility, queryUtility, getUtilitiesFor
+
+from z3ext.layout.pagelet import BrowserPagelet
+from z3ext.statusmessage.interfaces import IStatusMessage
+
+from z3ext.product.i18n import _
+from z3ext.product.interfaces import IProductExtension
+from z3ext.product.interfaces import ProductWarningError
+
+def log_exc(msg=''):
+    log = logging.getLogger(u'z3ext.product')
+    log.log(logging.ERROR, msg, exc_info=sys.exc_info())
+
+
+class InstallerView(BrowserPagelet):
+
+    def getProducts(self):
+        context = self.context
+
+        installed = []
+        notinstalled = []
+
+        for name, product in context.items():
+            info = {'name': name,
+                    'product': product,
+                    'title': product.__title__,
+                    'description': product.__description__}
+
+            if product.__installed__:
+                info['configlet'] = product.isAvailable()
+                installed.append((product.__title__, info))
+            else:
+                notinstalled.append((product.__title__, info))
+
+        installed.sort()
+        installed = [info for t, info in installed]
+
+        notinstalled.sort()
+        notinstalled = [info for t, info in notinstalled]
+
+        return {'installed': installed, 'notinstalled': notinstalled}
+
+    def getExtensions(self, product):
+        extensions = []
+
+        for name in product.listExtensions():
+            ext = product.get(name)
+            extensions.append((ext.__title__, ext))
+        extensions.sort()
+        return [ext for t, ext in extensions]
+
+    def update(self, *args, **kw):
+        request = self.request
+        context = self.context
+
+        service = IStatusMessage(request)
+        products = request.get('products', ())
+
+        if request.has_key('install'):
+            if not products:
+                service.add(_('Select one or more products to install.'), 'warning')
+            else:
+                try:
+                    for product_id in products:
+                        product = context.get(product_id)
+                        product.install()
+
+                    service.add(_('Selected products has been installed.'))
+                except ProductWarningError, e:
+                    abort()
+                    service.add(unicode(e), 'warning')
+                except Exception, e:
+                    abort()
+                    log_exc(str(e))
+                    service.add(e, 'error')
+
+        elif request.has_key('update'):
+            if not products:
+                service.add(_('Select one or more products to update.'), 'warning')
+            else:
+                try:
+                    for product_id in products:
+                        product = context.get(product_id)
+                        product.update()
+
+                    service.add('Selected products has been updated.')
+                except Exception, e:
+                    abort()
+                    log_exc(str(e))
+                    service.add(e, 'error')
+
+        elif request.has_key('uninstall'):
+            if not products:
+                service.add(_('Select one or more products to uninstall.'), 'warning')
+            else:
+                try:
+                    for product_id in products:
+                        product = context.get(product_id)
+                        product.uninstall()
+
+                    service.add(_('Selected products has been uninstalled.'))
+                except Exception, e:
+                    abort()
+                    log_exc(str(e))
+                    service.add(e, 'error')
+
+        elif request.has_key('extension'):
+            try:
+                product = ''
+                extension = request.get('extension', '')
+                if '.' in extension:
+                    c, product, extension = extension.split('.', 2)
+
+                product = context.get(product)
+                if product is not None:
+                    extension = product.get(extension)
+                
+                if not IProductExtension.providedBy(extension):
+                    extension = None
+
+                if extension is None:
+                    service.add("Can't fine extension.", 'error')
+                else:
+                    if extension.__installed__:
+                        extension.uninstall()
+                    else:
+                        extension.install()
+            except Exception, e:
+                abort()
+                log_exc(str(e))
+                service.add(e, 'error')

Added: z3ext.product/trunk/src/z3ext/product/zcml.py
===================================================================
--- z3ext.product/trunk/src/z3ext/product/zcml.py	                        (rev 0)
+++ z3ext.product/trunk/src/z3ext/product/zcml.py	2008-03-25 18:41:11 UTC (rev 84940)
@@ -0,0 +1,95 @@
+##############################################################################
+#
+# Copyright (c) 2007 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.
+#
+##############################################################################
+""" z3ext:product directive
+
+$Id: interfaces.py 802 2007-12-28 11:25:55Z fafhrd91 $
+"""
+from zope import schema
+from zope.configuration import fields
+from zope.component.zcml import utility
+from zope.component import globalSiteManager
+from zope.component.interfaces import IComponents
+from zope.security.checker import CheckerPublic
+
+import z3ext.product
+from z3ext.controlpanel.zcml import IConfigletDirective, ConfigletDirective
+
+from interfaces import IProduct
+from registry import ProductRegistry
+from product import Product, ProductExtension
+
+
+class IProductDirective(IConfigletDirective):
+    
+    configurable = schema.Bool(
+        title = u'Configurable',
+        default = False,
+        required = False)
+
+
+class ProductDirective(ConfigletDirective):
+
+    def __init__(self, _context, name, schema, title,
+                 description='', class_=None, provides=(),
+                 permission='z3ext.ManageProducts', tests=(), configurable=False):
+
+        if '.' not in name:
+            product_class = Product
+        else:
+            product_class = ProductExtension
+
+        if class_ is None:
+            class_ = product_class
+        else:
+            class_ = (class_, product_class)
+
+        if configurable:
+            test = ProductTest()
+            tests = (test,) + tuple(tests)
+        else:
+            tests = (False,)
+
+        # create component registry
+        registry = ProductRegistry(name, title)
+        z3ext.product.registries[name] = registry
+        setattr(z3ext.product, name, registry)
+        utility(_context, IComponents, registry, name=name)
+
+        # register configlet
+        productName = name
+        name = 'product.' + name
+
+        super(ProductDirective, self).__init__(
+            _context, name, schema, title,
+            description, class_, provides, permission, tests)
+
+        self._class.__product_name__ = productName
+
+        if configurable:
+            test.product = self._configlet
+
+        utility(_context, IProduct, self._configlet, name=productName)
+
+        self.require(_context, permission, interface=(IProduct,))
+        self.require(_context, CheckerPublic,
+                     attributes=('isInstalled', '__installed__'))
+
+
+class ProductTest(object):
+
+    def __init__(self, product=None):
+        self.product = product
+
+    def __call__(self, *args, **kw):
+        return self.product.isInstalled()



More information about the Checkins mailing list