summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Vagelpohl <jens@dataflake.org>2010-02-08 21:15:39 +0000
committerJens Vagelpohl <jens@dataflake.org>2010-02-08 21:15:39 +0000
commitcede7afc59cb669aff7014e3c6c24c608ca820b1 (patch)
treebf376161371d9adc9ad827842f5efdc41fe803d0
parent97dc538402adab7a467c12c08bbfb0738f811c13 (diff)
downloadProducts.LDAPMultiPlugins-cede7afc59cb669aff7014e3c6c24c608ca820b1.zip
Products.LDAPMultiPlugins-cede7afc59cb669aff7014e3c6c24c608ca820b1.tar.gz
- checkpoint
git-svn-id: file:///svn-public/Products.LDAPMultiPlugins/trunk@1907 835909ba-7c00-0410-bfa4-884f43845301
-rw-r--r--Products/LDAPMultiPlugins/CHANGES.txt69
-rw-r--r--Products/LDAPMultiPlugins/COPYRIGHT.txt2
-rw-r--r--Products/LDAPMultiPlugins/DEPENDENCIES.txt3
-rw-r--r--Products/LDAPMultiPlugins/HISTORY.txt271
-rw-r--r--Products/LDAPMultiPlugins/INSTALL.txt31
-rw-r--r--Products/LDAPMultiPlugins/README.txt24
-rw-r--r--Products/LDAPMultiPlugins/VERSION.txt2
-rw-r--r--bootstrap.py122
-rw-r--r--buildout.cfg28
-rw-r--r--docs/.static/dataflake.css63
-rw-r--r--docs/.static/dataflake.icobin0 -> 894 bytes
-rw-r--r--docs/.templates/layout.html41
-rw-r--r--docs/Makefile70
-rw-r--r--docs/api.rst31
-rw-r--r--docs/changes.rst1
-rw-r--r--docs/conf.py188
-rw-r--r--docs/development.rst69
-rw-r--r--docs/glossary.rst43
-rw-r--r--docs/index.rst51
-rw-r--r--docs/install.rst29
-rw-r--r--docs/usage_python.rst5
-rw-r--r--docs/usage_zmi.rst4
22 files changed, 915 insertions, 232 deletions
diff --git a/Products/LDAPMultiPlugins/CHANGES.txt b/Products/LDAPMultiPlugins/CHANGES.txt
index b22da3e..a429ecb 100644
--- a/Products/LDAPMultiPlugins/CHANGES.txt
+++ b/Products/LDAPMultiPlugins/CHANGES.txt
@@ -3,75 +3,8 @@ Changelog for Products.LDAPMultiplugins
To see earlier changes please see HISTORY.txt.
-1.10 (unreleased)
------------------
-
-
-1.9 (2010-01-27)
-----------------
-
-- Feature: The enumerateUsers method has a parameter "exact_match" that
- was only applied if the search was by logn or user id, since that is
- the way it is used in the standard PluggableAuthService plugins.
- However, the user folder searchUsers method can accept this parameter
- as well and thus limit searches by other criteria to exact matches.
- This is useful enough to implement, even though it breaks the standard.
- (http://www.dataflake.org/tracker/issue_00656)
-
-
-1.8 (2009-02-17)
-----------------
-
-- Bug: Move plugin registration from initialize method to module level to
- avoid multiple registrations.
- (http://www.dataflake.org/tracker/issue_00631 by Ramon Navarro Bosch)
-
-- Feature: Implemented GenericSetup import/export handlers and registered
- import/export steps.
-
-- Bug: Fixed the Zope dependency, which was listed as 2.8+. It's 2.9+.
-
-
-1.7 (2008-07-19)
+2.0 (unreleased)
----------------
-- Bug: LDAPMultiPlugin.enumerateUsers: The variable used as key for the
- caching mechanism was mutated after being computed, leading to cache
- keys that can never be found again. Found by Wichert Akkerman.
- (http://www.dataflake.org/tracker/issue_00613)
-
-
-1.6 (2008-06-05)
-----------------
-
-- Bug: ActiveDirectoryMultiPlugin.enumerateGroups: In order to support
- group searches on the binary objectGUID attribute, utilize a new
- flag exposed by the LDAPUserFolder LDAPDelegate search method
- that prevents the customary UTF8-encoding of the search filter
- expression. **NOTE**: With this change the LDAPUserFolder version
- dependency changes to version 2.9 or higher!
- (http://www.dataflake.org/tracker/issue_00576 by Wichert Akkerman)
-
-- Bug: ActiveDirectoryMultiPlugin.enumerateGroups: If the requested group
- id is a binary string, like a objectGUID attribute, it was mangled
- by a lowercasing operation. Removed the lowercasing.
- (http://www.dataflake.org/tracker/issue_00575 by Wichert Akkerman)
-
-- Feature: Added caching to the getGroupsForPrincipal method. Thanks to
- Wichert Akkerman for the patch.
- (http://www.dataflake.org/tracker/issue_00571)
-
-
-1.5 (2007-06-13)
-----------------
-
-- Bug: The product will no longer silently fail to install if the
- LDAPUserFolder package is not installed. Silent failure does
- not look like a good strategy here.
-
-- Bug: fixes and import cleanups after running Pyflakes
- (http://divmod.org:81/svn/Divmod/trunk/Pyflakes/)
-- Documentation: added some additional configuration hints to the README,
- thanks go to Brett Lentz (http://www.dataflake.org/tracker/issue_00559)
diff --git a/Products/LDAPMultiPlugins/COPYRIGHT.txt b/Products/LDAPMultiPlugins/COPYRIGHT.txt
index ad46690..7297827 100644
--- a/Products/LDAPMultiPlugins/COPYRIGHT.txt
+++ b/Products/LDAPMultiPlugins/COPYRIGHT.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2009 Jens Vagelpohl and contributors.
+Copyright (c) 2004-2010 Jens Vagelpohl and contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
diff --git a/Products/LDAPMultiPlugins/DEPENDENCIES.txt b/Products/LDAPMultiPlugins/DEPENDENCIES.txt
deleted file mode 100644
index 5b3a50e..0000000
--- a/Products/LDAPMultiPlugins/DEPENDENCIES.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Zope >= 2.8.5
-PluggableAuthService >= 1.4 (http://www.zope.org/Products/PluggableAuthService)
-LDAPUserFolder >= 2.9 (http://www.dataflake.org/software/ldapuserfolder/)
diff --git a/Products/LDAPMultiPlugins/HISTORY.txt b/Products/LDAPMultiPlugins/HISTORY.txt
index e31abd3..f025096 100644
--- a/Products/LDAPMultiPlugins/HISTORY.txt
+++ b/Products/LDAPMultiPlugins/HISTORY.txt
@@ -2,186 +2,231 @@
HISTORICAL CHANGES
==================
-1.5-beta (2007-03-03)
----------------------
+1.9 (2010-01-27)
+----------------
- Bugs fixed
+- Feature: The enumerateUsers method has a parameter "exact_match" that
+ was only applied if the search was by logn or user id, since that is
+ the way it is used in the standard PluggableAuthService plugins.
+ However, the user folder searchUsers method can accept this parameter
+ as well and thus limit searches by other criteria to exact matches.
+ This is useful enough to implement, even though it breaks the standard.
+ (http://www.dataflake.org/tracker/issue_00656)
- - LDAPMultiPlugin.enumerateUsers: If no useful search criteria are
- given (meaning no user ID or login is specified), fabricate a
- criteria set that will return all users, this is the expected
- behavior when calling enumerateUsers.
- - LDAPMultiPlugin.enumerateUsers: When iterating over search results
- from the user folder we now look for the special "fake result"
- emitted by the user folder if there is an error. Not elegant, but
- needed until error handling is changed in the LDAPUserFolder.
+1.8 (2009-02-17)
+----------------
- - Instead of throwing exceptions, the ActiveDirectoryMultiPlugin
- will now log error conditions and continue, with a patch from
- Mark Hammond.
- (http://www.dataflake.org/tracker/issue_00554)
+- Bug: Move plugin registration from initialize method to module level to
+ avoid multiple registrations.
+ (http://www.dataflake.org/tracker/issue_00631 by Ramon Navarro Bosch)
- - Adjusted an import that has been removed from the
- PluggableAuthService utils module.
- (http://www.dataflake.org/tracker/issue_00542)
+- Feature: Implemented GenericSetup import/export handlers and registered
+ import/export steps.
- - Remove the ICredentialsUpdatePlugin implementation - it was
- implemented wrongly and should not have been part of the contract
- at all due to an interface misunderstanding.
- (http://www.dataflake.org/tracker/issue_00539)
+- Bug: Fixed the Zope dependency, which was listed as 2.8+. It's 2.9+.
- Other
- - Moved the PluggableAuthService dependency up to version 1.4
+1.7 (2008-07-19)
+----------------
+- Bug: LDAPMultiPlugin.enumerateUsers: The variable used as key for the
+ caching mechanism was mutated after being computed, leading to cache
+ keys that can never be found again. Found by Wichert Akkerman.
+ (http://www.dataflake.org/tracker/issue_00613)
-1.4 (2006-10-16)
+
+1.6 (2008-06-05)
----------------
- Bugs fixed
+- Bug: ActiveDirectoryMultiPlugin.enumerateGroups: In order to support
+ group searches on the binary objectGUID attribute, utilize a new
+ flag exposed by the LDAPUserFolder LDAPDelegate search method
+ that prevents the customary UTF8-encoding of the search filter
+ expression. **NOTE**: With this change the LDAPUserFolder version
+ dependency changes to version 2.9 or higher!
+ (http://www.dataflake.org/tracker/issue_00576 by Wichert Akkerman)
- - The ActiveDirectoryMultiPlugin did not ensure to correctly
- escape search filters it constructed internally.
- (http://www.dataflake.org/tracker/issue_00507)
+- Bug: ActiveDirectoryMultiPlugin.enumerateGroups: If the requested group
+ id is a binary string, like a objectGUID attribute, it was mangled
+ by a lowercasing operation. Removed the lowercasing.
+ (http://www.dataflake.org/tracker/issue_00575 by Wichert Akkerman)
- - The add form selection whether or not to use SSL for the LDAP
- server connection was not handed through correctly, identified
- by Olivier Nicole (http://www.dataflake.org/tracker/issue_00526)
+- Feature: Added caching to the getGroupsForPrincipal method. Thanks to
+ Wichert Akkerman for the patch.
+ (http://www.dataflake.org/tracker/issue_00571)
- Other
- - Revamped the way recursive group memberships are found and applied,
- not sure if the previous implementation was a bug or not. Many thanks
- to John Hannon for a patch. This change includes the ability to
- specify a nesting depth to which the recursive search will go.
- (http://www.dataflake.org/tracker/issue_00513)
+1.5 (2007-06-13)
+----------------
- - Added some notes on how to enable caching using the ZCacheable
- mechanism
+- Bug: The product will no longer silently fail to install if the
+ LDAPUserFolder package is not installed. Silent failure does
+ not look like a good strategy here.
+- Bug: fixes and import cleanups after running Pyflakes
+ (http://divmod.org:81/svn/Divmod/trunk/Pyflakes/)
-1.3 (2006-07-29)
+- Documentation: added some additional configuration hints to the README,
+ thanks go to Brett Lentz (http://www.dataflake.org/tracker/issue_00559)
+
+
+1.5-beta (2007-03-03)
+---------------------
+
+- LDAPMultiPlugin.enumerateUsers: If no useful search criteria are
+ given (meaning no user ID or login is specified), fabricate a
+ criteria set that will return all users, this is the expected
+ behavior when calling enumerateUsers.
+
+- LDAPMultiPlugin.enumerateUsers: When iterating over search results
+ from the user folder we now look for the special "fake result"
+ emitted by the user folder if there is an error. Not elegant, but
+ needed until error handling is changed in the LDAPUserFolder.
+
+- Instead of throwing exceptions, the ActiveDirectoryMultiPlugin
+ will now log error conditions and continue, with a patch from
+ Mark Hammond.
+ (http://www.dataflake.org/tracker/issue_00554)
+
+- Adjusted an import that has been removed from the
+ PluggableAuthService utils module.
+ (http://www.dataflake.org/tracker/issue_00542)
+
+- Remove the ICredentialsUpdatePlugin implementation - it was
+ implemented wrongly and should not have been part of the contract
+ at all due to an interface misunderstanding.
+ (http://www.dataflake.org/tracker/issue_00539)
+
+- Moved the PluggableAuthService dependency up to version 1.4
+
+
+1.4 (2006-10-16)
----------------
- Bugs fixed
+- The ActiveDirectoryMultiPlugin did not ensure to correctly
+ escape search filters it constructed internally.
+ (http://www.dataflake.org/tracker/issue_00507)
- * Update the enumerateGroups method to use the new LDAPUserFolder
- method "searchGroups". This changes the LDAPUserFolder dependency
- to version 2.7. Patch provided by Leonardo Rochael Almeida.
+- The add form selection whether or not to use SSL for the LDAP
+ server connection was not handed through correctly, identified
+ by Olivier Nicole (http://www.dataflake.org/tracker/issue_00526)
- * The ActiveDirectoryMultiPlugin enumerateUsers method would only
- search correctly if login or id were explicitly specified
- (thanks to Sidnei da Silva for the patch).
+- Revamped the way recursive group memberships are found and applied,
+ not sure if the previous implementation was a bug or not. Many thanks
+ to John Hannon for a patch. This change includes the ability to
+ specify a nesting depth to which the recursive search will go.
+ (http://www.dataflake.org/tracker/issue_00513)
- * Make sure to apply the same checks for user existence in
- getRolesForPrincipal that are used by getPropertiesForUser
- (http://www.dataflake.org/tracker/issue_00503 by Riccardo Lemmi)
+- Added some notes on how to enable caching using the ZCacheable
+ mechanism
+
+
+1.3 (2006-07-29)
+----------------
- * Fixed the enumerateUsers implementation to be more efficient and
- use the new searchUsers method on the LDAPUserFolder (thanks to
- Wichert Akkerman for the problem description and solution)
+- Update the enumerateGroups method to use the new LDAPUserFolder
+ method "searchGroups". This changes the LDAPUserFolder dependency
+ to version 2.7. Patch provided by Leonardo Rochael Almeida.
- Other
+- The ActiveDirectoryMultiPlugin enumerateUsers method would only
+ search correctly if login or id were explicitly specified
+ (thanks to Sidnei da Silva for the patch).
- * Added simple caching of groups information, provided by
- Leonardo Rochael Almeida.
+- Make sure to apply the same checks for user existence in
+ getRolesForPrincipal that are used by getPropertiesForUser
+ (http://www.dataflake.org/tracker/issue_00503 by Riccardo Lemmi)
- * Software dependencies are now documented in a separate
- DEPENDENCIES.txt file. Please note that the packages mentioned
- in DEPENDENCIES.txt may have their own dependencies that must be
- satisfied as well.
+- Fixed the enumerateUsers implementation to be more efficient and
+ use the new searchUsers method on the LDAPUserFolder (thanks to
+ Wichert Akkerman for the problem description and solution)
- * Replaced all zLOG usage with equivalent calls into the Python
- logging module, and reducing the chattiness coded into the
- ActiveDirectoryMultiPlugin (INFO -> DEBUG)
+- Added simple caching of groups information, provided by
+ Leonardo Rochael Almeida.
- * Started on a test suite
+- Software dependencies are now documented in a separate
+ DEPENDENCIES.txt file. Please note that the packages mentioned
+ in DEPENDENCIES.txt may have their own dependencies that must be
+ satisfied as well.
+
+- Replaced all zLOG usage with equivalent calls into the Python
+ logging module, and reducing the chattiness coded into the
+ ActiveDirectoryMultiPlugin (INFO -> DEBUG)
+
+- Started on a test suite
1.2 (2006-03-02)
----------------
- Bugs fixed
+- In order to avoid duplicate search results, the enumerateUsers
+ method used a simple dictionary to store DNs for records that
+ were already processed. However, the keys put into this dictionary
+ were munged and really could not be compared to raw search
+ result DNs anymore. Thanks go to Wichert Akkerman for spotting this
+ obvious error (http://www.dataflake.org/tracker/issue_00485).
- * In order to avoid duplicate search results, the enumerateUsers
- method used a simple dictionary to store DNs for records that
- were already processed. However, the keys put into this dictionary
- were munged and really could not be compared to raw search
- result DNs anymore. Thanks go to Wichert Akkerman for spotting this
- obvious error (http://www.dataflake.org/tracker/issue_00485).
+- Speed up enumerateGroups by letting the LDAP server do more of
+ the filtering (thanks to Wichert Akkerman,
+ http://www.dataflake.org/tracker/issue_00483)
- * Speed up enumerateGroups by letting the LDAP server do more of
- the filtering (thanks to Wichert Akkerman,
- http://www.dataflake.org/tracker/issue_00483)
-
- * Applied a performance fix to the ActiveDirectoryPlugin's
- _recurseGroups method (thanks got to Mark Hammond for the patch,
- http://www.dataflake.org/tracker/issue_00476)
+- Applied a performance fix to the ActiveDirectoryPlugin's
+ _recurseGroups method (thanks got to Mark Hammond for the patch,
+ http://www.dataflake.org/tracker/issue_00476)
1.1 (2005-10-29)
----------------
- Bugs fixed
-
- * The LDAPMultiPlugins ignored default roles configured on the
- LDAPUserFolder and would not add it to the set of roles
- computed (seen by Sidnei da Silva).
+- The LDAPMultiPlugins ignored default roles configured on the
+ LDAPUserFolder and would not add it to the set of roles
+ computed (seen by Sidnei da Silva).
- * enumerateUsers now allows you to do exact-match searches on
- attributes other than just the user ID and login (patch
- by Sidnei da Silva). **Note**: This code now requires
- LDAPUserFolder versions 2.6 or higher, which support exact
- match searches using LDAPUserFolder.findUsers.
+- enumerateUsers now allows you to do exact-match searches on
+ attributes other than just the user ID and login (patch
+ by Sidnei da Silva). **Note**: This code now requires
+ LDAPUserFolder versions 2.6 or higher, which support exact
+ match searches using LDAPUserFolder.findUsers.
1.0 (2005-08-18)
----------------
- Other
+- The interface machinery expected by the PluggableAuthService has
+ been changed to use Zope 3-style interfaces. Thanks go to Leonardo
+ Rochael Almeida who provided a patch to fix the resulting breakage.
- * The interface machinery expected by the PluggableAuthService has
- been changed to use Zope 3-style interfaces. Thanks go to Leonardo
- Rochael Almeida who provided a patch to fix the resulting breakage.
-
- * Changed the initialization code for the plugins to conform to the
- changed initialization code in the LDAPUserFolder product versions
- 2.6beta3 and up.
+- Changed the initialization code for the plugins to conform to the
+ changed initialization code in the LDAPUserFolder product versions
+ 2.6beta3 and up.
1.0beta3
--------
- Other
-
- * Changes to the way the user IDs are mangled/unmangled to be in line
- with the changes in the latest PluggableAuthService code
- (Patch provided by Mark Hammond)
+- Changes to the way the user IDs are mangled/unmangled to be in line
+ with the changes in the latest PluggableAuthService code
+ (Patch provided by Mark Hammond)
1.0beta2
--------
- Bugs fixed
-
- * When retrieving properties for a user, None values have to be
- converted to an empty string to prevent the user propertysheet
- machinery from blowing up trying to guess what kind of
- property a None value could represent.
+- When retrieving properties for a user, None values have to be
+ converted to an empty string to prevent the user propertysheet
+ machinery from blowing up trying to guess what kind of
+ property a None value could represent.
1.0beta1
--------
- Bugs fixed
-
- * Role retrieval was broken, small fix involves changing a call to the
- LDAPUserFolder
+- Role retrieval was broken, small fix involves changing a call to the
+ LDAPUserFolder
LDAPMultiPlugins 0.9
--------------------
- First public release
+- First public release
diff --git a/Products/LDAPMultiPlugins/INSTALL.txt b/Products/LDAPMultiPlugins/INSTALL.txt
index 53c2d80..f04cc52 100644
--- a/Products/LDAPMultiPlugins/INSTALL.txt
+++ b/Products/LDAPMultiPlugins/INSTALL.txt
@@ -1,25 +1,10 @@
-=======================================
-Installing the LDAPMultiPlugins Product
-=======================================
+Installation
+============
+Products.LDAPMultiPlugins is installed as an egg using
+setuptools[1], or by including it as a dependency in
+zc.buildout[2] buildout configurations.
-This product does not require any special handling after unzipping
-and untarring it in the Zope Products directory. You should do
-something like:
+After installation you need to restart Zope to load the package.
- $ cp LDAPMultiPlugins-xyz.tgz <zope_root>/lib/python/Products
- $ cd <zope_root>/lib/python/Products
- $ tar zxvf LDAPMultiPlugins-xyz.tgz
- <watch files being decompressed>
-
-Windows users can use WinZip or similar, it can handle tarred
-gzip files. Make sure to move the extracted LDAPMultiPlugins
-folder to your Zope installation's lib/python/Products-folder.
-
-That's all. Do not forget to restart Zope afterwards. You will then
-be able to select the "LDAP Multi Plugin" and the "Active Directory
-Multi Plugin" from the list of plugins to add when you navigate to
-your PluggableAuthService-based user folder and select the "Contents"
-tab in the Zope Management Interface (ZMI).
-
-See README.txt and DEPENDENCIES.txt for any other dependencies and
-requirements.
+[1] http://peak.telecommunity.com/DevCenter/setuptools
+[2] http://www.buildout.org/
diff --git a/Products/LDAPMultiPlugins/README.txt b/Products/LDAPMultiPlugins/README.txt
index d2b758f..eab6137 100644
--- a/Products/LDAPMultiPlugins/README.txt
+++ b/Products/LDAPMultiPlugins/README.txt
@@ -9,43 +9,21 @@ the backend for the services they provide. The PluggableAuthService is a
Zope user folder product that can be extended in modular fashion using
various plugins.
-Please make sure to read the documentation included in the LDAPUserFolder
-package (http://pypi.python.org/pypi/Products.LDAPUserFolder) as well.
-
Bug tracker
===========
-
Please post questions, bug reports or feature requests to the bug tracker
at http://www.dataflake.org/tracker/
+
SVN version
===========
-
You can retrieve the latest code from Subversion using setuptools or
zc.buildout via this URL:
http://svn.dataflake.org/svn/Products.LDAPMultiPlugins/trunk#egg=Products.LDAPMultiPlugins
-Caching
-=======
-
-The results of some calls into the plugins provided by these package can be
-cached using the Zope ZCacheable mechanism:
-
-- In the Zope Management Interface (ZMI) of your PluggableAuthService
- instance, select 'RAM Cache Manager' from the dropdown, give it an ID
- and configure it according to your needs.
-
-- Click on your LDAP/ActiveDirectoryMultiPlugin and use the 'Cache'
- ZMI tab on the far right to associate the newly created RAM Cache
- Manager object with the plugin.
-
-Now your plugin will use the RAM Cache Manager object to cache results from
-some of the possibly expensive API calls.
-
-
Special features - Active Directory Multi Plugin
================================================
diff --git a/Products/LDAPMultiPlugins/VERSION.txt b/Products/LDAPMultiPlugins/VERSION.txt
index 1b3d67b..678e3b2 100644
--- a/Products/LDAPMultiPlugins/VERSION.txt
+++ b/Products/LDAPMultiPlugins/VERSION.txt
@@ -1 +1 @@
-1.10dev
+2.0dev
diff --git a/bootstrap.py b/bootstrap.py
new file mode 100644
index 0000000..3d832b8
--- /dev/null
+++ b/bootstrap.py
@@ -0,0 +1,122 @@
+##############################################################################
+#
+# 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$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+from optparse import OptionParser
+
+tmpeggs = tempfile.mkdtemp()
+
+is_jython = sys.platform.startswith('java')
+
+# parsing arguments
+parser = OptionParser()
+parser.add_option("-v", "--version", dest="version",
+ help="use a specific zc.buildout version")
+parser.add_option("-d", "--distribute",
+ action="store_true", dest="distribute", default=False,
+ help="Use Disribute rather than Setuptools.")
+
+parser.add_option("-c", None, action="store", dest="config_file",
+ help=("Specify the path to the buildout configuration "
+ "file to be used."))
+
+options, args = parser.parse_args()
+
+# if -c was provided, we push it back into args for buildout' main function
+if options.config_file is not None:
+ args += ['-c', options.config_file]
+
+if options.version is not None:
+ VERSION = '==%s' % options.version
+else:
+ VERSION = ''
+
+USE_DISTRIBUTE = options.distribute
+args = args + ['bootstrap']
+
+to_reload = False
+try:
+ import pkg_resources
+ if not hasattr(pkg_resources, '_distribute'):
+ to_reload = True
+ raise ImportError
+except ImportError:
+ ez = {}
+ if USE_DISTRIBUTE:
+ exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py'
+ ).read() in ez
+ ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True)
+ else:
+ exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+ if to_reload:
+ reload(pkg_resources)
+ else:
+ import pkg_resources
+
+if sys.platform == 'win32':
+ def quote(c):
+ if ' ' in c:
+ return '"%s"' % c # work around spawn lamosity on windows
+ else:
+ return c
+else:
+ def quote (c):
+ return c
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+ws = pkg_resources.working_set
+
+if USE_DISTRIBUTE:
+ requirement = 'distribute'
+else:
+ requirement = 'setuptools'
+
+if is_jython:
+ import subprocess
+
+ assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
+ quote(tmpeggs), 'zc.buildout' + VERSION],
+ env=dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse(requirement)).location
+ ),
+ ).wait() == 0
+
+else:
+ assert os.spawnle(
+ os.P_WAIT, sys.executable, quote (sys.executable),
+ '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION,
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse(requirement)).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout' + VERSION)
+import zc.buildout.buildout
+zc.buildout.buildout.main(args)
+shutil.rmtree(tmpeggs)
+
diff --git a/buildout.cfg b/buildout.cfg
new file mode 100644
index 0000000..5865119
--- /dev/null
+++ b/buildout.cfg
@@ -0,0 +1,28 @@
+# This buildout is used for development of Products.LDAPMultiPlugins
+# It gets the necessary eggs and creates a test runner and a python
+# interpreter.
+
+[buildout]
+develop = .
+parts = test py docs
+
+[py]
+recipe = zc.recipe.egg
+eggs = Products.LDAPMultiPlugins
+interpreter = py
+
+[test]
+recipe = zc.recipe.testrunner
+defaults = ['-cv']
+eggs = Products.LDAPMultiPlugins
+
+[docs]
+recipe = zc.recipe.egg
+eggs =
+ Products.LDAPMultiPlugins
+ sphinx
+ sphinx-pypi-upload
+ zc.rst2
+ pkginfo
+ repoze.sphinx.autointerface
+interpreter = docpy
diff --git a/docs/.static/dataflake.css b/docs/.static/dataflake.css
new file mode 100644
index 0000000..02fbe04
--- /dev/null
+++ b/docs/.static/dataflake.css
@@ -0,0 +1,63 @@
+@import url('default.css');
+body {
+ background-color: #FDFDFD;
+ color: #333333;
+ font-family: sans-serif;
+}
+
+div.body h1, div.body h2, div.body h3, div.body h4, div.body h5, div.body h6 {
+ color: #333333;
+ font-family: sans-serif;
+}
+
+div.body h1 {
+ padding-top: 7px;
+}
+
+div.document {
+ background-color: #FDFDFD;
+}
+
+div.sphinxsidebarwrapper {
+ padding: 0 5px 0 10px;
+}
+
+div.sphinxsidebar {
+ background-color: #DCDDDE;
+}
+
+div.sphinxsidebar h3,h4,h5,li,a,p {
+ color: #333333 !important;
+ font-family: sans-serif;
+}
+
+div.sphinxsidebar h3 {
+ padding-top: 1em;
+}
+
+div.sphinxsidebar h4 {
+ font-size: 1.1em;
+ font-family: sans-serif;
+}
+
+
+div.documentwrapper {
+ background-color: #DCDDDE;
+}
+
+div.related {
+ color: #FDFDFD !important;
+ background-color: #333333;
+}
+
+div.related a, div.related li {
+ color: #FDFDFD !important;
+}
+
+span.pre {
+ font-weight: bold;
+}
+
+div.footer {
+ color: #333333;
+}
diff --git a/docs/.static/dataflake.ico b/docs/.static/dataflake.ico
new file mode 100644
index 0000000..e6c5140
--- /dev/null
+++ b/docs/.static/dataflake.ico
Binary files differ
diff --git a/docs/.templates/layout.html b/docs/.templates/layout.html
new file mode 100644
index 0000000..b7791a5
--- /dev/null
+++ b/docs/.templates/layout.html
@@ -0,0 +1,41 @@
+{% extends "!layout.html" %}
+
+{% block rootrellink %}
+ <li><a href="http://docs.dataflake.org/">docs.dataflake.org</a>{{ reldelim1 }}</li>
+ <li><a href="{{ pathto(master_doc) }}">{{ project }} v{{ release }}</a>{{ reldelim1 }}</li>
+{% endblock %}
+
+{%- block sidebartoc %}
+ <h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
+ {{ toc }}
+{%- endblock %}
+
+{% block sidebarrel %}
+ {%- if prev %}
+ <h3>Previous topic</h3>
+ <ul>
+ <li><a href="{{ prev.link|e }}" title="{{ _('previous chapter') }}">{{ prev.title }}</a></li>
+ </ul>
+ {%- endif %}
+ {%- if next %}
+ <h3>Next topic</h3>
+ <ul>
+ <li><a href="{{ next.link|e }}" title="{{ _('next chapter') }}">{{ next.title }}</a></li>
+ </ul>
+ {%- endif %}
+
+ <h3>Related links</h3>
+ <ul>
+ <li><a class="reference external" href="http://pypi.python.org/pypi/{{ project|e }}/">Project home</a></li>
+ <li><a class="reference external" href="http://svn.dataflake.org/viewvc/{{ project|e }}/">Browse sources</a></li>
+ <li><a class="reference external" href="http://www.dataflake.org/tracker/">Bug tracker</a></li>
+ <li><a class="reference external" href="http://www.zetwork.com/">Commercial support</a></li>
+ </ul>
+{% endblock %}
+
+{%- block sidebarsearch %}
+ {{ super() }}
+
+ <br/>
+ <h3><a href="{{ project }}.pdf">Download as PDF</a></h3>
+{% endblock %}
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..4e212a9
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,70 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS = -q
+SPHINXBUILD = ../bin/sphinx-build
+PAPER = a4
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " pickle to make pickle files (usable by e.g. sphinx-web)"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " changes to make an overview over all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+
+clean:
+ -rm -rf _build/*
+
+html:
+ mkdir -p _build/html _build/doctrees
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) _build/html
+ @echo
+ @echo "Build finished. The HTML pages are in _build/html."
+
+pickle:
+ mkdir -p _build/pickle _build/doctrees
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) _build/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files or run"
+ @echo " sphinx-web _build/pickle"
+ @echo "to start the sphinx-web server."
+
+web: pickle
+
+htmlhelp:
+ mkdir -p _build/htmlhelp _build/doctrees
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) _build/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in _build/htmlhelp."
+
+latex:
+ mkdir -p _build/latex _build/doctrees
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in _build/latex."
+ @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+ "run these through (pdf)latex."
+
+changes:
+ mkdir -p _build/changes _build/doctrees
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) _build/changes
+ @echo
+ @echo "The overview file is in _build/changes."
+
+linkcheck:
+ mkdir -p _build/linkcheck _build/doctrees
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) _build/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in _build/linkcheck/output.txt."
diff --git a/docs/api.rst b/docs/api.rst
new file mode 100644
index 0000000..86293f2
--- /dev/null
+++ b/docs/api.rst
@@ -0,0 +1,31 @@
+.. _api_interfaces_section:
+
+Interfaces
+----------
+
+The :term:`PluggableAuthService` plugins in this package
+implement interfaces defined in the :term:`PluggableAuthService`
+package.
+
+.. autointerface:: Products.PluggableAuthService.interfaces.plugins.IAuthenticationPlugin
+
+.. autointerface:: Products.PluggableAuthService.interfaces.plugins.ICredentialsResetPlugin
+
+.. autointerface:: Products.PluggableAuthService.interfaces.plugins.IGroupEnumerationPlugin
+
+.. autointerface:: Products.PluggableAuthService.interfaces.plugins.IGroupsPlugin
+
+.. autointerface:: Products.PluggableAuthService.interfaces.plugins.IPropertiesPlugin
+
+.. autointerface:: Products.PluggableAuthService.interfaces.plugins.IRoleAssignerPlugin
+
+.. autointerface:: Products.PluggableAuthService.interfaces.plugins.IRoleEnumerationPlugin
+
+.. autointerface:: Products.PluggableAuthService.interfaces.plugins.IRolesPlugin
+
+.. autointerface:: Products.PluggableAuthService.interfaces.plugins.IUpdatePlugin
+
+.. autointerface:: Products.PluggableAuthService.interfaces.plugins.IUserAdderPlugin
+
+.. autointerface:: Products.PluggableAuthService.interfaces.plugins.IUserEnumerationPlugin
+
diff --git a/docs/changes.rst b/docs/changes.rst
new file mode 100644
index 0000000..919f84a
--- /dev/null
+++ b/docs/changes.rst
@@ -0,0 +1 @@
+.. include:: ../Products/LDAPMultiPlugins/CHANGES.txt
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..a578603
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,188 @@
+# -*- coding: utf-8 -*-
+#
+# Products.LDAPMultiPlugins documentation build configuration file
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# The contents of this file are pickled, so don't put values in the namespace
+# that aren't pickleable (module imports are okay, they're removed automatically).
+#
+# All configuration values have a default value; values that are commented out
+# serve to show the default value.
+
+import datetime
+import os
+import pkginfo
+import sys
+
+# If your extensions are in another directory, add it here. If the directory
+# is relative to the documentation root, use os.path.abspath to make it
+# absolute, like shown here.
+parent = os.path.dirname(os.path.dirname(__file__))
+parent_dir = os.path.abspath(parent)
+sys.path.append(parent_dir)
+pkg_info = pkginfo.Develop(parent_dir)
+
+# General configuration
+# ---------------------
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'repoze.sphinx.autointerface']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['.templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General substitutions.
+project = pkg_info.name
+copyright = '2009-%i, Jens Vagelpohl' % datetime.datetime.now().year
+
+# The default replacements for |version| and |release|, also used in various
+# other places throughout the built documents.
+#
+# The short X.Y version.
+version = pkg_info.version.replace('dev', '')
+# The full version, including alpha/beta/rc tags.
+release = pkg_info.version
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+today_fmt = '%B %d, %Y'
+
+# List of documents that shouldn't be included in the build.
+#unused_docs = []
+
+# List of directories, relative to source directories, that shouldn't be searched
+# for source files.
+#exclude_dirs = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# Options for HTML output
+# -----------------------
+
+# The style sheet to use for HTML and HTML Help pages. A file of that name
+# must exist either in Sphinx' static/ path, or in one of the custom paths
+# given in html_static_path.
+html_style = 'dataflake.css'
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (within the static path) to place at the top of
+# the sidebar.
+#html_logo = '.static/logo_hi.gif'
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+html_favicon = 'dataflake.ico'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['.static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_use_modindex = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, the reST sources are included in the HTML build as _sources/<name>.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = ''
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'productsldapmultiplugins'
+
+
+# Options for LaTeX output
+# ------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, document class [howto/manual]).
+latex_documents = [
+ ( 'index'
+ , '%s.tex' % project
+ , '%s Documentation' % project
+ , 'Jens Vagelpohl'
+ , 'manual'
+ ),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = '.static/logo_hi.gif'
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_use_modindex = True
diff --git a/docs/development.rst b/docs/development.rst
new file mode 100644
index 0000000..16cf9fe
--- /dev/null
+++ b/docs/development.rst
@@ -0,0 +1,69 @@
+Development
+===========
+
+.. highlight:: bash
+
+Getting the source code
+-----------------------
+The source code is maintained in the Dataflake Subversion
+repository at `http://svn.dataflake.org <http://svn.dataflake.org/>`_.
+To check out the trunk::
+
+ svn co http://svn.dataflake.org/svn/Products.LDAPMultiPlugins/trunk/
+
+You can also browse the code online at
+`http://svn.dataflake.org/viewvc/Products.LDAPMultiPlugins
+<http://svn.dataflake.org/viewvc/Products.LDAPMultiPlugins/>`_.
+
+When using setuptools or zc.buildout you can use the following
+URL to retrieve the latest development code as Python egg::
+
+ http://svn.dataflake.org/svn/Products.LDAPMultiPlugins/trunk#egg=Products.LDAPMultiPlugins
+
+Bug tracker
+-----------
+For bug reports, suggestions or questions please use the
+dataflake bug tracker at
+`http://www.dataflake.org/tracker <http://www.dataflake.org/tracker/>`_.
+
+Setting up a development sandbox and testing
+--------------------------------------------
+Once you've obtained a source checkout, you can follow these
+instructions to perform various development tasks.
+All development requires that you run the buildout from the
+package root directory::
+
+ $ python bootstrap.py
+ $ bin/buildout
+
+Once you have a buildout, the tests can be run as follows::
+
+ $ bin/test
+
+Building the documentation
+--------------------------
+The Sphinx documentation is built by doing the following from the
+directory containing setup.py::
+
+ $ cd docs
+ $ make html
+
+Making a release
+----------------
+The first thing to do when making a release is to check that the ReST
+to be uploaded to PyPI is valid::
+
+ $ bin/docpy setup.py --long-description | bin/rst2 html \
+ --link-stylesheet \
+ --stylesheet=http://www.python.org/styles/styles.css > build/desc.html
+
+Once you're certain everything is as it should be, the following will
+build the distribution, upload it to PyPI, register the metadata with
+PyPI and upload the Sphinx documentation to PyPI::
+
+ $ bin/buildout -o
+ $ bin/docpy setup.py sdist register upload upload_sphinx --upload-dir=docs/_build/html
+
+The ``bin/buildout`` will make sure the correct package information is
+used.
+
diff --git a/docs/glossary.rst b/docs/glossary.rst
new file mode 100644
index 0000000..7c0d6b4
--- /dev/null
+++ b/docs/glossary.rst
@@ -0,0 +1,43 @@
+.. _glossary:
+
+============================
+Glossary
+============================
+
+.. glossary::
+
+ Active Directory
+ A non-standards-conformant LDAP server implementation sold by
+ Microsoft.
+ Interface
+ An attribute of a model object that determines its type. It is an
+ instance of a ``zope.interface`` Interface class.
+ PluggableAuthService
+ A pluggable Zope 2 authentication and authorization framework
+ that defines a modular user folder for Zope 2 and Plone.
+ python-ldap
+ The `python-ldap <http://python-ldap.sourceforge.net>`_ library is
+ used to communicate with LDAP servers.
+ Setuptools
+ `Setuptools <http://peak.telecommunity.com/DevCenter/setuptools>`_
+ builds on Python's ``distutils`` to provide easier building,
+ distribution, and installation of packages.
+ Virtualenv
+ An isolated Python environment. Allows you to control which
+ packages are used on a particular project by cloning your main
+ Python. `virtualenv <http://pypi.python.org/pypi/virtualenv>`_
+ was created by Ian Bicking.
+ ZCML
+ XML-based configuration language used by Zope.
+ ZMI
+ Zope offers the ZMI (Zope Managment Interface) for managing your
+ Zope site through a browser. You can get to it by going to the
+ path ``/manage`` in your Zope site and logging in with credentials
+ for a user with role ``Manager``.
+ Zope
+ `The Z Object Publishing Framework <http://zope.org>`_. The granddaddy
+ of Python web frameworks.
+ ZODB
+ The `ZODB <http://www.zodb.org>`_ is the object database included in
+ Zope.
+
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..349da62
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,51 @@
+.. _index:
+
+=============
+Documentation
+=============
+
+:mod:`Products.LDAPMultiPlugins` provides a set of plugins for
+the :term:`Zope` :term:`PluggableAuthService` product. The
+PluggableAuthService defines a modular user folder with very
+fine-grained control over ever aspect of user authentication
+and authorization. The plugins in this package store and
+retrieve their data using LDAP.
+
+Narrative documentation
+-----------------------
+
+Narrative documentation explaining how to use :mod:`Products.LDAPMultiPlugins`.
+
+.. toctree::
+ :maxdepth: 2
+
+ install
+ usage_zmi
+ usage_python
+ development
+ changes
+
+API documentation
+-----------------
+
+API documentation for :mod:`Products.LDAPMultiPlugins`.
+
+.. toctree::
+ :maxdepth: 2
+
+ api
+
+Support
+-------
+
+If you need commercial support for this software package, please
+contact zetwork GmbH at `http://www.zetwork.com
+<http://www.zetwork.com/>`_.
+
+Indices and tables
+------------------
+
+* :ref:`genindex`
+* :ref:`search`
+* :ref:`glossary`
+
diff --git a/docs/install.rst b/docs/install.rst
new file mode 100644
index 0000000..cea4854
--- /dev/null
+++ b/docs/install.rst
@@ -0,0 +1,29 @@
+Installation
+============
+
+You will need `Python <http://python.org>`_ version 2.4 or better to
+run :mod:`Products.LDAPMultiPlugins`. Development of
+:mod:`Products.LDAPMultiPlugins` is done primarily under Python 2.6, so
+that version is recommended.
+
+.. warning:: To successfully install :mod:`Products.LDAPMultiPlugins`,
+ you will need :term:`setuptools` installed on your Python system
+ in order to run the ``easy_install`` command.
+
+It is advisable to install :mod:`Products.LDAPMultiPlugins` into a
+:term:`virtualenv` in order to obtain isolation from any "system"
+packages you've got installed in your Python version (and likewise,
+to prevent :mod:`Products.LDAPMultiPlugins` from globally installing
+versions of packages that are not compatible with your system Python).
+
+After you've got the requisite dependencies installed, you may install
+:mod:`Products.LDAPMultiPlugins` into your Python environment using the
+following command::
+
+ $ easy_install Products.LDAPMultiPlugins
+
+If you use :mod:`zc.buildout` you can add :mod:`Products.LDAPMultiPlugins`
+to the necessary ``eggs`` section to have it pulled in automatically.
+
+When you ``easy_install`` :mod:`Products.LDAPMultiPlugins`, the
+:term:`python-ldap` libraries are installed if they are not present.
diff --git a/docs/usage_python.rst b/docs/usage_python.rst
new file mode 100644
index 0000000..260eccd
--- /dev/null
+++ b/docs/usage_python.rst
@@ -0,0 +1,5 @@
+Usage from Python
+=================
+
+The :ref:`api_interfaces_section` page contains more
+information about the various plugin APIs.
diff --git a/docs/usage_zmi.rst b/docs/usage_zmi.rst
new file mode 100644
index 0000000..73d51c2
--- /dev/null
+++ b/docs/usage_zmi.rst
@@ -0,0 +1,4 @@
+Usage from the Zope ZMI
+=======================
+
+