From 8a8ab8d28efdd32e4316ea8d9242a36a594379cc Mon Sep 17 00:00:00 2001
From: =?utf8?q?Beno=C3=AEt=20Pin?= <benoit.pin@gmail.com>
Date: Mon, 25 Oct 2010 12:58:48 +0200
Subject: [PATCH] =?utf8?q?Ajout=20de=20GroupUserFolder=20extrait=20de=20l'?=
 =?utf8?q?=C3=A9tat=20suivant=20:=20URL:=20http://svn.cri.ensmp.fr/svn/Gro?=
 =?utf8?q?upUserFolder/branches/3.55.1=20Repository=20Root:=20http://svn.c?=
 =?utf8?q?ri.ensmp.fr/svn/GroupUserFolder=20Repository=20UUID:=201bf790b2-?=
 =?utf8?q?e4d4-0310-9a6c-c8a412c25dae=20Revision:=20591=20Node=20Kind:=20d?=
 =?utf8?q?irectory=20Schedule:=20normal=20Last=20Changed=20Author:=20pin?=
 =?utf8?q?=20Last=20Changed=20Rev:=20591=20Last=20Changed=20Date:=202009-0?=
 =?utf8?q?2-11=2018:49:59=20+0100=20(Mer,=2011=20f=C3=A9v=202009)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=utf8
Content-Transfer-Encoding: 8bit

---
 ABOUT                                         |    1 +
 CHANGES                                       |  375 +++
 CONTRIBUTORS                                  |   20 +
 DynaList.py                                   |   96 +
 Extensions/Install.py                         |  126 +
 Extensions/__init__.py                        |   24 +
 GRUFFolder.py                                 |  299 ++
 GRUFUser.py                                   |  935 ++++++
 GroupDataTool.py                              |  443 +++
 GroupUserFolder.py                            | 2806 +++++++++++++++++
 GroupsTool.py                                 |  495 +++
 GroupsToolPermissions.py                      |   49 +
 INSTALL.txt                                   |   16 +
 Installation.py                               |  247 ++
 LDAPGroupFolder.py                            |  393 +++
 LDAPUserFolderAdapter.py                      |  215 ++
 LICENSE                                       |   57 +
 LICENSE.txt                                   |   57 +
 Log.py                                        |  195 ++
 PRODUCT_NAME                                  |    1 +
 PatchCatalogTool.py                           |   23 +
 PloneFeaturePreview.py                        |  271 ++
 README.txt                                    |  118 +
 TESTED_WITH                                   |   21 +
 TODO                                          |   82 +
 __init__.py                                   |  128 +
 class_utility.py                              |  197 ++
 cvs2cl.pl                                     | 1995 ++++++++++++
 debian/changelog                              |    6 +
 debian/config                                 |   22 +
 debian/control                                |   22 +
 debian/copyright                              |   18 +
 debian/postinst                               |   50 +
 debian/prerm                                  |   40 +
 debian/rules                                  |   89 +
 debian/templates                              |    7 +
 debian/watch                                  |    5 +
 design.txt                                    |   34 +
 doc/FAQ                                       |   43 +
 doc/GRUF3.0.stx                               |   80 +
 doc/GRUFLogo.png                              |  Bin 0 -> 31213 bytes
 doc/SCREENSHOTS                               |   32 +
 doc/folder_contents.png                       |  Bin 0 -> 22877 bytes
 doc/icon.png                                  |  Bin 0 -> 31213 bytes
 doc/interview.txt                             |  111 +
 doc/menu.png                                  |  Bin 0 -> 62208 bytes
 doc/tab_audit.png                             |  Bin 0 -> 25238 bytes
 doc/tab_groups.png                            |  Bin 0 -> 31957 bytes
 doc/tab_overview.png                          |  Bin 0 -> 30494 bytes
 doc/tab_sources.png                           |  Bin 0 -> 23197 bytes
 doc/tab_users.png                             |  Bin 0 -> 38818 bytes
 doc/user_edit.png                             |  Bin 0 -> 69485 bytes
 dtml/GRUFFolder_main.dtml                     |  275 ++
 dtml/GRUF_audit.zpt                           |  236 ++
 dtml/GRUF_contents.zpt                        |  216 ++
 dtml/GRUF_groups.zpt                          |  267 ++
 dtml/GRUF_newusers.zpt                        |   32 +
 dtml/GRUF_overview.zpt                        |  208 ++
 dtml/GRUF_user.zpt                            |  247 ++
 dtml/GRUF_users.zpt                           |  340 ++
 dtml/GRUF_wizard.zpt                          |  127 +
 dtml/addLDAPGroupFolder.dtml                  |   55 +
 dtml/configureGroupsTool.dtml                 |   52 +
 dtml/explainGroupDataTool.dtml                |   10 +
 dtml/explainGroupsTool.dtml                   |   11 +
 dtml/groups.dtml                              |  224 ++
 dtml/roles.png                                |  Bin 0 -> 26916 bytes
 global_symbols.py                             |   91 +
 interfaces/.cvsignore                         |    2 +
 interfaces/IUserFolder.py                     |  614 ++++
 interfaces/__init__.py                        |   26 +
 interfaces/portal_groupdata.py                |   93 +
 interfaces/portal_groups.py                   |  144 +
 product.txt                                   |    1 +
 .../gruf/GroupSpaceFolderishType_view.pt.old  |   16 +
 skins/gruf/change_password.py                 |   31 +
 skins/gruf/defaultGroup.gif                   |  Bin 0 -> 1225 bytes
 skins/gruf/folder_localrole_form_plone1.pt    |  358 +++
 skins/gruf/getUsersInGroup.py                 |   21 +
 skins/gruf/gruf_ldap_required_fields.py       |   14 +
 skins/gruf/prefs_group_manage.cpy             |   26 +
 skins/gruf/prefs_group_manage.cpy.metadata    |    6 +
 skins/gruf_plone_2_0/README.txt               |    4 +
 skins/gruf_plone_2_0/folder_localrole_form.pt |  445 +++
 svn-commit.tmp                                |    4 +
 tool.gif                                      |  Bin 0 -> 166 bytes
 version.txt                                   |    1 +
 www/GRUFGroups.gif                            |  Bin 0 -> 607 bytes
 www/GRUFUsers.gif                             |  Bin 0 -> 539 bytes
 www/GroupUserFolder.gif                       |  Bin 0 -> 600 bytes
 www/LDAPGroupFolder.gif                       |  Bin 0 -> 977 bytes
 www/down_arrow.gif                            |  Bin 0 -> 56 bytes
 www/down_arrow_grey.gif                       |  Bin 0 -> 56 bytes
 www/up_arrow.gif                              |  Bin 0 -> 54 bytes
 www/up_arrow_grey.gif                         |  Bin 0 -> 54 bytes
 95 files changed, 14441 insertions(+)
 create mode 100644 ABOUT
 create mode 100644 CHANGES
 create mode 100644 CONTRIBUTORS
 create mode 100644 DynaList.py
 create mode 100644 Extensions/Install.py
 create mode 100644 Extensions/__init__.py
 create mode 100644 GRUFFolder.py
 create mode 100644 GRUFUser.py
 create mode 100644 GroupDataTool.py
 create mode 100644 GroupUserFolder.py
 create mode 100644 GroupsTool.py
 create mode 100644 GroupsToolPermissions.py
 create mode 100644 INSTALL.txt
 create mode 100644 Installation.py
 create mode 100755 LDAPGroupFolder.py
 create mode 100755 LDAPUserFolderAdapter.py
 create mode 100644 LICENSE
 create mode 100644 LICENSE.txt
 create mode 100644 Log.py
 create mode 100644 PRODUCT_NAME
 create mode 100644 PatchCatalogTool.py
 create mode 100755 PloneFeaturePreview.py
 create mode 100644 README.txt
 create mode 100644 TESTED_WITH
 create mode 100644 TODO
 create mode 100644 __init__.py
 create mode 100644 class_utility.py
 create mode 100755 cvs2cl.pl
 create mode 100644 debian/changelog
 create mode 100755 debian/config
 create mode 100644 debian/control
 create mode 100644 debian/copyright
 create mode 100755 debian/postinst
 create mode 100755 debian/prerm
 create mode 100755 debian/rules
 create mode 100644 debian/templates
 create mode 100644 debian/watch
 create mode 100644 design.txt
 create mode 100644 doc/FAQ
 create mode 100644 doc/GRUF3.0.stx
 create mode 100644 doc/GRUFLogo.png
 create mode 100644 doc/SCREENSHOTS
 create mode 100644 doc/folder_contents.png
 create mode 100644 doc/icon.png
 create mode 100644 doc/interview.txt
 create mode 100644 doc/menu.png
 create mode 100644 doc/tab_audit.png
 create mode 100644 doc/tab_groups.png
 create mode 100644 doc/tab_overview.png
 create mode 100644 doc/tab_sources.png
 create mode 100644 doc/tab_users.png
 create mode 100644 doc/user_edit.png
 create mode 100644 dtml/GRUFFolder_main.dtml
 create mode 100644 dtml/GRUF_audit.zpt
 create mode 100644 dtml/GRUF_contents.zpt
 create mode 100644 dtml/GRUF_groups.zpt
 create mode 100644 dtml/GRUF_newusers.zpt
 create mode 100644 dtml/GRUF_overview.zpt
 create mode 100644 dtml/GRUF_user.zpt
 create mode 100644 dtml/GRUF_users.zpt
 create mode 100644 dtml/GRUF_wizard.zpt
 create mode 100755 dtml/addLDAPGroupFolder.dtml
 create mode 100644 dtml/configureGroupsTool.dtml
 create mode 100644 dtml/explainGroupDataTool.dtml
 create mode 100644 dtml/explainGroupsTool.dtml
 create mode 100755 dtml/groups.dtml
 create mode 100644 dtml/roles.png
 create mode 100644 global_symbols.py
 create mode 100644 interfaces/.cvsignore
 create mode 100644 interfaces/IUserFolder.py
 create mode 100644 interfaces/__init__.py
 create mode 100644 interfaces/portal_groupdata.py
 create mode 100644 interfaces/portal_groups.py
 create mode 100644 product.txt
 create mode 100644 skins/gruf/GroupSpaceFolderishType_view.pt.old
 create mode 100644 skins/gruf/change_password.py
 create mode 100644 skins/gruf/defaultGroup.gif
 create mode 100644 skins/gruf/folder_localrole_form_plone1.pt
 create mode 100644 skins/gruf/getUsersInGroup.py
 create mode 100755 skins/gruf/gruf_ldap_required_fields.py
 create mode 100755 skins/gruf/prefs_group_manage.cpy
 create mode 100755 skins/gruf/prefs_group_manage.cpy.metadata
 create mode 100755 skins/gruf_plone_2_0/README.txt
 create mode 100644 skins/gruf_plone_2_0/folder_localrole_form.pt
 create mode 100644 svn-commit.tmp
 create mode 100644 tool.gif
 create mode 100644 version.txt
 create mode 100644 www/GRUFGroups.gif
 create mode 100644 www/GRUFUsers.gif
 create mode 100644 www/GroupUserFolder.gif
 create mode 100644 www/LDAPGroupFolder.gif
 create mode 100644 www/down_arrow.gif
 create mode 100644 www/down_arrow_grey.gif
 create mode 100644 www/up_arrow.gif
 create mode 100644 www/up_arrow_grey.gif

diff --git a/ABOUT b/ABOUT
new file mode 100644
index 0000000..bee4873
--- /dev/null
+++ b/ABOUT
@@ -0,0 +1 @@
+A Zope Product that manages Groups of Users
diff --git a/CHANGES b/CHANGES
new file mode 100644
index 0000000..b8506de
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,375 @@
+v3.55.1 - 2007-11-08
+
+  * Fix #6984: Now GroupData verifies if it is related to GRUF group or PlonePAS
+    group.
+    [encolpe]
+
+v3.54.4 - 2007-04-29
+
+  * Death to tabindexes!
+    [limi]
+
+v3.54.3 - 2007-04-16
+
+  * Update methods to provide protection against XSS attacks via GET requests
+    [bloodbare, alecm]
+
+v3.54.2 - 2007-02-06
+
+  * Fix a bug in group removing in another group.
+    [encolpe]
+
+v3.54.1 - 2006-12-15
+
+  * Fix _getMemberIds for LDAPUserFolder 2.7 when groups are stored in LDAPUF
+    [encolpe]
+
+  * Got rid of zLOG in favor of logging.
+    [stefan]
+
+v3.54 - 2006-09-19
+  * Fix a bug with LDAPUserFolder where another UserFolder was returned when LUF
+    was requested [jvloothuis]
+
+v3.53 - 2006-09-08
+  * Removed refresh.txt. You should add this locally if you want to use it.
+    [hannosch]
+
+  * getUsers: efficiency improvement: anti-double user inclusion is done by
+    checking key presence in a dict instead of looking up name in a list
+    [b_mathieu]
+
+  * Fix searchUsersByAttribute returning twice the same user id when a second
+    source is present
+    [b_mathieu]
+
+v3.52 - 2006-05-30
+
+  * Plone 2.1 service release
+
+v3.51 - 2006-05-15
+
+  * Changed getLocalRolesForDisplay to check for 'Manage properties' instead of
+    'Access contents information'. This is still not perfect but at least
+    Anonymous can no longer get at local roles information this way.
+    Fixes http://dev.plone.org/plone/ticket/5492
+    [stefan]
+
+  * Remove some noise log message and the product name parameter from ToolInit.
+    [hannosch]
+
+  * Forces exact match with LDAP on user search
+
+v3.5 - 2005-12-20
+
+  * By default, uses title instead of meta_type in the source management
+    pages. [pjgrizel]
+
+  * It's now possible to search very quickly users from a group
+    in LDAP; long-awaited improvement! [pjgrizel]
+
+  * Correct some wrong security settings.
+    [hannosch]
+
+  * Fix some stupid failing tests so finally all tests pass again.
+    [hannosch]
+
+  * Fix encoding warning in GroupUserFolder.py
+    [encolpe]
+
+  * Made the GroupDataTool call notifyModified() on members who are
+    added or removed from a group
+    [bmh]
+
+v3.4 - 20050904
+
+  * Dynamically fixed the remaining bug in folder_localrole_form.
+
+  * Now the users tab in ZMI allow you to search a user (useful w/ LDAP)
+
+  * Fixed a bug in Plone 2.0 UI when searching a large set of users
+
+  * Added a 'wizard' tab to help in managing LDAP sources.
+
+  * Fixed getProperty in GroupDataTool not to acquire properties.
+    [panjunyong]
+
+[v3.3 - 20050725]
+
+  * Added an 'enable/disable' feature on the sources. Now, you can entierly
+    disable a users source without actually removing it. Very useful for
+    testing purposes!
+
+  * Removed an optimization when user is None in authenticate(), so
+    than you can use GRUF with CASUserFolder (thanks to Alexandre
+    Sauv?mr.lex@free.fr>)
+
+  * Fixed 1235351 (possible infinite recursion in an audit method)
+
+  * Fixed [ 1243323 ] GRUF: bug in createGrouparea() in GroupsTool.py
+
+[v3.23 - 20050724]
+
+  * Fixed unit tests. Now the unit tests are working with the latest ZTC
+    version.
+    [tiran]
+
+[v3.22 - 20050706]
+
+  * Fixed a missing import in GroupsTool.py (http://plone.org/collector/4209)
+    [hannosch]
+
+  * Fixed a nested groups issue with LDAPUserFolder. This is not a widely
+    used schema with ldap anyway.
+    [pjgrizel]
+
+  * Fixed LDAPUserFolderAdapter's search_by_dn bug: search by _login_attr
+    but not _rdnattr
+    [panjunyong]
+
+  * _getLocalRolesForDisplay was marking users as groups for groups that had
+    the same as users (http://plone.org/collector/3711).  Made unit tests run
+    even if LDAPUserFolder is not installed.
+    [alecm]
+
+[v3.2 - 20050307]
+
+  Service release.
+
+[v3.2RC2 - 20050305]
+
+  * Now your user sources (especially LUF) can have a 'portait' property which
+    will be used as your user's portrait. This works only in 'preview.txt'-mode.
+
+  * You can put a 'notifyGroupAreaCreated' in your 'groups' folder as you would
+    be able to put a 'notifyMemberAreaCreated' in your 'members' folder.
+    So you can execute some code at group area creation. Thanks to F. Carlier !
+
+  * Added a helper table on the sources tab to help managing LUF group mappings
+
+  * Fixed a bug in Zope 2.7 preventing the zope quickstart page to show up.
+    A hasUsers() method was missing from GRUF's API.
+
+  * Fixed a bug in ZMI which prevented LUF cached users to be individually
+    managed by GRUF.
+
+
+[v3.2RC1 - 20041215]
+
+  * _doChangeUser and _doChangeGroup lost existing groups if the groups argument
+    was omitted. Fixed these and the Zope 2.5-style APIs accordingly.
+    [stefan]
+
+  * Updated API to have a better conformance to the original Zope API.
+    Thanks to Stefan H Holek for this clever advice.
+
+  * Uncommented cache clearing code in _doChangeUser as it appears to be required.
+    [stefan]
+
+  * Added a Plone 2.0 optional patch to improve LDAP and groups management.
+    It's basically a preview of what will be done in Plone 2.1 for users managment.
+    For example, now, you can assign local roles to users in your LDAP directory,
+    EVEN if they're not in the cache in folder_localrole_form.
+    Other "preview" features will come later. Please read README and PloneFeaturePreview.py
+    files for more explanations on these.
+
+  * Made manage_GRUFUsers page a little faster with LDAP by preventing users count.
+
+  * Fixed [ 1051387 ] addGroup fails if type 'Folder' is not implicitly addable.
+
+  * Fixed other minor or cosmetic bugs
+
+  * Group mapping is automatically created by LDAPGroupFolder when you create a group
+    with its interface.
+
+v3_1_1 - 20040906
+
+  * Fixed a bug in getProperty() - it always returned None !
+
+  * Fixed a bug which caused AUTHENTICATED_USER source id to be invalid
+
+v3_1 - 20040831
+
+  * Group-to-role mapping now works for LDAPGroupFolder
+
+  * Debug mode now allows broken source not to be checken against
+
+  * Fixed getUser() bug with remote_user_mode (getUser(None) now returns None).
+    Thanks to Marco Bizzari.
+
+v3_0 - 20040623
+
+  * Minor interface changes
+
+  * Documentation update
+
+v3_0Beta2
+
+  * Various bug fixes
+
+  * Better support for Plone UI. PLEASE USE PLONE2's pjgrizel-gruf3-branch IN SVN!
+    See README-Plone for further explanation
+
+v3_0Beta1
+
+  * API REFACTORING
+
+  * FAR BETTER LDAP SUPPORT (see README-LDAP.stx)
+
+v2_0 - 20040302
+
+  * Reindexing new GroupSpace objects
+    2004/03/10 Maik Rder
+
+  * Speedup improvements by Heldge Tesdal
+
+  * Fixed ZMI overview refreshing bug
+
+  * GroupsTool method createGrouparea now calls the GroupSpace
+    method setInitialGroup with the group that it is created for.
+    In case this method does not exists, the default behaviour
+    is employed. This is done so that the GroupSpace can decide on its
+    own what the policy should be regarding the group that it is
+    initially created for.
+    See the implementation of GrufSpaces for an example of how this
+    can be used in order to give the initial group the role GroupMember.
+    2004/02/25 Maik Rder
+
+  * Removed GroupSpace code, which can now be found in
+    http://ingeniweb.sourceforge.net/Products/GrufSpaces
+    2004/02/25 Maik Rder
+
+v2_0Beta3 - 20040224
+
+  * Improved performance on LDAP Directories
+
+  * Fixed various Plone UI bugs (password & roles changing)
+
+  * Fixed "AttributeError: URL1" bug in ZMI
+
+v2_0Beta2 - 20031222
+
+  * Added GroupSpace object for Plone websites (see website/GroupSpaceDesign_xx.stx)
+
+  * Fixed __getattr__-related bug
+
+  * Fixed inituser-related bug
+
+  * Cosmetic fixes and minor bugs
+
+v2_0Beta1 - 20031026
+
+  * Include support for multi-sources
+
+v1_32 - 20030923
+
+  * Pass __getitem__ access onto user objects (XUF compatibility)
+
+  * Allow ZMI configuration of group workspaces (CMF Tool)
+
+  * Added security declarations to CMF tools
+
+  * new getPureUserNames() and getPurseUsers() methods to get user
+    objects without group objects
+
+v1_31 - 20030731
+
+  * Many performance improvements (tree and audit views)
+
+  * Fixed a recursion pb. on the left pane tree (!)
+
+  * Added a batch view for "overview" page when there's more than
+    100 users registered in the system
+
+v1_3 - 20030723
+
+  * GRUF NOW SUPPORTS NESTED GROUPS - Transparently, of course.
+
+  * Updated website information & screenshots
+
+  * Major ZMI improving, including everywhere-to-everywhere links,
+    edition of a single user or group, and minor cosmetic fixes
+
+  * The tree view in ZMI now show groups and user (if there's no more
+    than 50, to avoid having server outage)
+
+  * Improved performance
+
+  * Improved test plan
+
+  * Fixed a bug in password generation algorythm with non-iso Python installs
+
+  * Fixed a minor bug in group acquisition stack (it apparently had no side-effect)
+
+v1_21 - 20030710
+
+  * ZMI cosmetic fixes
+
+  * Fixed the bug that prevented LDAP-defined attributes to be acquired by GRUFUser.
+    This bug showed-up with LDAPUserFolder.
+
+v1_2 - 20030709
+
+  * HTML documentation
+
+  * Add a management tab on GRUF to allow users and groups to be created
+    at this top-level management interface.
+
+v1_1 - 20030702
+
+  * Security improvements
+
+  * Added an 'audit' tab to check what's going on
+
+  * GroupsTool and GroupDataTool added for Plone
+
+  * Improved Plone skins
+
+  * Improved Plone installation
+
+  * GRUF Users now 'inherit' from their underlying user object
+
+v1_0RC1 - 20030514
+
+  * Code cleaning
+
+  * Documentation improving
+
+  * API improving (added a few utility methods)
+
+  * UI improving (see skins changes)
+
+  * getId() bug fixing (see ChangeLog)
+
+v0_2 - 20030331
+
+  * Users are now acquired correctly, which prevents you from hotfixing anything !!! :-)
+
+  * This fixed Zope 2.5 w/ Plone bug
+
+  * Better log reporting
+
+  * Validated with LDAPUserFolder and SimpleUserFolder
+
+v0_1 - 20021126
+
+  * User creation is now supported
+
+  * Fixed a bug (with an axe) that prevented Zope module Owner.py code to work.
+    The Owner.py calls aq_inner and aq_parent methods on a User object to get its
+    security context. So it found the underlying User object instead of the GRUF
+    itself. So we fixed this by setting dummy UserFolder-context methods on the
+    GRUFUser objects. This is ugly and should be fixed later by acquiring the
+    underlying User object from a better context.
+
+  * Fixed getUserName in GRUFUser that returned group names without the "group"
+    prefix.
+
+  * Fixed various "cosmetic" bugs
+
+  * Documented the whole stuff
+
+v0_0 - 20021126
+
+  Started to work on this wonderful product.
+
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644
index 0000000..97b6f84
--- /dev/null
+++ b/CONTRIBUTORS
@@ -0,0 +1,20 @@
+
+CONTRIBUTORS
+
+  P.-J. Grizel <grizel@ingeniweb.com> : Lead programming, Design, Coding, Testing, ZMI interfaces
+  
+  O. Deckmyn <deckmyn@ingeniweb.com>: Design, Main user interface
+
+  J Cameron Cooper <jccooper@jcameroncooper.com>: GroupDataTool and GroupsTool design & coding
+
+  Brent Hendricks <brentmh@ece.rice.edu>: GroupDataTool and GroupsTool design & coding
+
+  Helge Tesdal <info@plonesolutions.com> merged PJ's multi-groups branch to the 1.32 GRUF version.
+
+  Maik Röder <maik.roeder@ingeniweb.com>: moved GroupSpace out of GRUF
+
+  Volker: LDAPGroupFolder initial coding
+
+  Kai Bielenberg <kai@bielenberg.info>: LDAP tips
+
+  Jens Vagelpohl <jens@dataflake.org>: Help on LDAPUserFolder support
diff --git a/DynaList.py b/DynaList.py
new file mode 100644
index 0000000..22b1be6
--- /dev/null
+++ b/DynaList.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+DynaList.py => a list that has dynamic data (ie. calculated by a 'data' method).
+Please override this class and define a data(self,) method that will return the actual list.
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: DynaList.py 30098 2006-09-08 12:35:01Z encolpe $
+__docformat__ = 'restructuredtext'
+
+class DynaList:
+    def __init__(self, initlist=None):
+        pass
+
+    def __repr__(self): return repr(self.data())
+    def __lt__(self, other): return self.data() <  self.__cast(other)
+    def __le__(self, other): return self.data() <= self.__cast(other)
+    def __eq__(self, other): return self.data() == self.__cast(other)
+    def __ne__(self, other): return self.data() != self.__cast(other)
+    def __gt__(self, other): return self.data() >  self.__cast(other)
+    def __ge__(self, other): return self.data() >= self.__cast(other)
+    def __cast(self, other):
+        if isinstance(other, UserList): return other.data()
+        else: return other
+    def __cmp__(self, other):
+        raise RuntimeError, "UserList.__cmp__() is obsolete"
+    def __contains__(self, item): return item in self.data()
+    def __len__(self): return len(self.data())
+    def __getitem__(self, i): return self.data()[i]
+    def __setitem__(self, i, item): self.data()[i] = item
+    def __delitem__(self, i): del self.data()[i]
+    def __getslice__(self, i, j):
+        i = max(i, 0); j = max(j, 0)
+        return self.__class__(self.data()[i:j])
+    def __setslice__(self, i, j, other):
+        i = max(i, 0); j = max(j, 0)
+        if isinstance(other, UserList):
+            self.data()[i:j] = other.data()
+        elif isinstance(other, type(self.data())):
+            self.data()[i:j] = other
+        else:
+            self.data()[i:j] = list(other)
+    def __delslice__(self, i, j):
+        i = max(i, 0); j = max(j, 0)
+        del self.data()[i:j]
+    def __add__(self, other):
+        if isinstance(other, UserList):
+            return self.__class__(self.data() + other.data())
+        elif isinstance(other, type(self.data())):
+            return self.__class__(self.data() + other)
+        else:
+            return self.__class__(self.data() + list(other))
+    def __radd__(self, other):
+        if isinstance(other, UserList):
+            return self.__class__(other.data() + self.data())
+        elif isinstance(other, type(self.data())):
+            return self.__class__(other + self.data())
+        else:
+            return self.__class__(list(other) + self.data())
+    def __iadd__(self, other):
+        raise NotImplementedError, "Not implemented"
+
+    def __mul__(self, n):
+        return self.__class__(self.data()*n)
+    __rmul__ = __mul__
+    def __imul__(self, n):
+        raise NotImplementedError, "Not implemented"
+    def append(self, item): self.data().append(item)
+    def insert(self, i, item): self.data().insert(i, item)
+    def pop(self, i=-1): return self.data().pop(i)
+    def remove(self, item): self.data().remove(item)
+    def count(self, item): return self.data().count(item)
+    def index(self, item): return self.data().index(item)
+    def reverse(self): self.data().reverse()
+    def sort(self, *args): apply(self.data().sort, args)
+    def extend(self, other):
+        if isinstance(other, UserList):
+            self.data().extend(other.data())
+        else:
+            self.data().extend(other)
diff --git a/Extensions/Install.py b/Extensions/Install.py
new file mode 100644
index 0000000..87de596
--- /dev/null
+++ b/Extensions/Install.py
@@ -0,0 +1,126 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: Install.py 30098 2006-09-08 12:35:01Z encolpe $
+__docformat__ = 'restructuredtext'
+
+from Products.GroupUserFolder import groupuserfolder_globals
+from Products.GroupUserFolder.GroupUserFolder import GroupUserFolder
+from StringIO import StringIO
+from Products.CMFCore.utils import getToolByName
+from Products.CMFCore.DirectoryView import addDirectoryViews
+from Acquisition import aq_base
+from OFS.Folder import manage_addFolder
+
+
+SKIN_NAME = "gruf"
+_globals = globals()
+
+def install_plone(self, out):
+    pass
+
+def install_subskin(self, out, skin_name=SKIN_NAME, globals=groupuserfolder_globals):
+    print >>out, "  Installing subskin."
+    skinstool=getToolByName(self, 'portal_skins')
+    if skin_name not in skinstool.objectIds():
+        print >>out, "    Adding directory view for GRUF"
+        addDirectoryViews(skinstool, 'skins', globals)
+
+    for skinName in skinstool.getSkinSelections():
+        path = skinstool.getSkinPath(skinName)
+        path = [i.strip() for i in  path.split(',')]
+        try:
+            if skin_name not in path:
+                path.insert(path.index('custom') +1, skin_name)
+        except ValueError:
+            if skin_name not in path:
+                path.append(skin_name)
+
+        path = ','.join(path)
+        skinstool.addSkinSelection( skinName, path)
+    print >>out, "  Done installing subskin."
+
+def walk(out, obj, operation):
+    if obj.isPrincipiaFolderish:
+        for content in obj.objectValues():
+            walk(out, content, operation)
+    operation(out, obj)
+
+
+def migrate_user_folder(obj, out, ):
+    """
+    Move a user folder into a temporary folder, create a GroupUserFolder,
+    and then move the old user folder into the Users portion of the GRUF.
+    NOTE: You cant copy/paste between CMF and Zope folder.  *sigh*
+    """
+    id = obj.getId()
+    if id == 'acl_users':
+        if obj.__class__.__name__ == "GroupUserFolder":
+            # Avoid already-created GRUFs
+            print >>out, "    Do NOT migrate acl_users at %s, as it is already a GroupUserFolder" % ('/'.join( obj.getPhysicalPath() ), )
+            return out.getvalue()
+
+        print >>out, "    Migrating acl_users folder at %s to a GroupUserFolder" % ('/'.join( obj.getPhysicalPath() ), )
+
+        container = obj.aq_parent
+
+        # Instead of using Copy/Paste we hold a reference to the acl_users
+        # and use that reference instead of physically moving objects in ZODB
+        tmp_users=container._getOb('acl_users')
+        tmp_allow=container.__allow_groups__
+
+        del container.__allow_groups__
+        if 'acl_users' in container.objectIds():
+            container.manage_delObjects('acl_users')
+
+        container.manage_addProduct['GroupUserFolder'].manage_addGroupUserFolder()
+        container.acl_users.Users.manage_delObjects( 'acl_users' )
+        container.acl_users.Users._setObject('acl_users', aq_base(tmp_users))
+        container.__allow_groups__ = aq_base(getattr(container,'acl_users'))
+
+    return out.getvalue()
+
+
+def migrate_plone_site_to_gruf(self, out = None):
+    if out is None:
+        out = StringIO()
+    print >>out, "  Attempting to migrate UserFolders to GroupUserFolders..."
+    urltool=getToolByName(self, 'portal_url')
+    plonesite = urltool.getPortalObject()
+    ## We disable the 'walk' operation because if the acl_users object is deep inside
+    ## the Plone site, that is a real problem. Furthermore, that may be because
+    ## we're already digging an GRUF and have the risk to update a GRUF/User/acl_users
+    ## object !
+##    walk(out, plonesite, migrate_user_folder)
+    for obj in plonesite.objectValues():
+        migrate_user_folder(obj, out, )
+    print >>out, "  Done Migrating UserFolders to GroupUserFolders."
+    return out.getvalue()
+
+def install(self):
+    out = StringIO()
+    print >>out, "Installing GroupUserFolder"
+    install_subskin(self, out)
+    install_plone(self, out)
+    migrate_plone_site_to_gruf(self, out)
+    print >>out, "Done."
+    return out.getvalue()
diff --git a/Extensions/__init__.py b/Extensions/__init__.py
new file mode 100644
index 0000000..9889842
--- /dev/null
+++ b/Extensions/__init__.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: __init__.py 30098 2006-09-08 12:35:01Z encolpe $
+__docformat__ = 'restructuredtext'
diff --git a/GRUFFolder.py b/GRUFFolder.py
new file mode 100644
index 0000000..6e3fef6
--- /dev/null
+++ b/GRUFFolder.py
@@ -0,0 +1,299 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: GRUFFolder.py 30098 2006-09-08 12:35:01Z encolpe $
+__docformat__ = 'restructuredtext'
+
+
+# fakes a method from a DTML file
+from Globals import MessageDialog, DTMLFile
+
+from AccessControl import ClassSecurityInfo
+from Globals import InitializeClass
+from Acquisition import Implicit
+from Globals import Persistent
+from AccessControl.Role import RoleManager
+from OFS.SimpleItem import Item
+from OFS.PropertyManager import PropertyManager
+from OFS import ObjectManager, SimpleItem
+from DateTime import DateTime
+from App import ImageFile
+
+#XXX PJ DynaList is very hairy - why vs. PerstList?
+# (see C__ac_roles__ class below for an explanation)
+import DynaList
+import AccessControl.Role, webdav.Collection
+import Products
+import os
+import string
+import shutil
+import random
+
+
+
+def manage_addGRUFUsers(self, id="Users", dtself=None,REQUEST=None,**ignored):
+    """ """
+    f=GRUFUsers(id)
+    self=self.this()
+    try:    self._setObject(id, f)
+    except: return MessageDialog(
+                   title  ='Item Exists',
+                   message='This object already contains a GRUFUsers Folder',
+                   action ='%s/manage_main' % REQUEST['URL1'])
+    if REQUEST is not None:
+        REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
+
+def manage_addGRUFGroups(self, id="Groups", dtself=None,REQUEST=None,**ignored):
+    """ """
+    f=GRUFGroups(id)
+    self=self.this()
+    try:    self._setObject(id, f)
+    except: return MessageDialog(
+                   title  ='Item Exists',
+                   message='This object already contains a GRUFGroups Folder',
+                   action ='%s/manage_main' % REQUEST['URL1'])
+    if REQUEST is not None:
+        REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
+
+class GRUFFolder(ObjectManager.ObjectManager, SimpleItem.Item):
+    isAnObjectManager=1
+    isPrincipiaFolderish=1
+    manage_main=DTMLFile('dtml/GRUFFolder_main', globals())
+    manage_options=( {'label':'Contents', 'action':'manage_main'}, ) + \
+                     SimpleItem.Item.manage_options
+
+    security = ClassSecurityInfo()
+    def __creatable_by_emergency_user__(self): return 1
+
+    def __init__(self, id = None):
+        if id:
+            self.id = id
+        else:
+            self.id = self.default_id
+
+    def getId(self,):
+        if self.id:
+            return self.id
+        else:
+            return self.default_id      # Used for b/w compatibility
+
+    def getUserSourceId(self,):
+        return self.getId()
+
+    def isValid(self,):
+        """
+        isValid(self,) => Return true if an acl_users is inside
+        """
+        if "acl_users" in self.objectIds():
+            return 1
+        return None
+
+    security.declarePublic('header_text')
+    def header_text(self,):
+        """
+        header_text(self,) => Text that appears in the content's
+                              view heading zone
+        """
+        return ""
+
+    def getUserFolder(self,):
+        """
+        getUserFolder(self,) => get the underlying user folder, UNRESTRICTED !
+        """
+        if not "acl_users" in self.objectIds():
+            raise "ValueError", "Please put an acl_users in %s " \
+                                "before using GRUF" % (self.getId(),)
+        return self.restrictedTraverse('acl_users')
+
+    def getUserNames(self,):
+        """
+        getUserNames(self,) => None
+
+        We override this to prevent SimpleUserFolder to use GRUF's getUserNames() method.
+        It's, of course, still possible to override a getUserNames method with SimpleUserFolder:
+        just call it 'new_getUserNames'.
+        """
+        # Call the "new_getUserNames" method if available
+        if "new_getUserNames" in self.objectIds():
+            return self.unrestrictedTraverse('new_getUserNames')()
+
+        # Return () if nothing is there
+        return ()
+
+
+
+
+class GRUFUsers(GRUFFolder):
+    """
+    GRUFUsers : GRUFFolder that holds users
+    """
+    meta_type="GRUFUsers"
+    default_id = "Users"
+
+    manage_options = GRUFFolder.manage_options
+
+    class C__ac_roles__(Persistent, Implicit, DynaList.DynaList):
+        """
+        __ac_roles__ dynastring.
+        Do not forget to set _target to class instance.
+
+        XXX DynaList is surely not efficient but it's the only way
+        I found to do what I wanted easily. Someone should take
+        a look to PerstList instead to see if it's possible
+        to do the same ? (ie. having a list which elements are
+        the results of a method call).
+
+        However, even if DynaList is not performant, it's not
+        a critical point because this list is meant to be
+        looked at only when a User object is looked at INSIDE
+        GRUF (especially to set groups a user belongs to).
+        So in practice only used within ZMI.
+        """
+        def data(self,):
+            return self.userdefined_roles()
+
+
+    # Property setting
+    ac_roles = C__ac_roles__()
+    __ac_roles__ = ac_roles
+
+    enabled = 1                         # True if it's enabled, false if not
+
+    def enableSource(self,):
+        """enableSource(self,) => Set enable status to 1
+        """
+        self.enabled = 1
+
+    def disableSource(self,):
+        """disableSource(self,) => explicit ;)
+        """
+        self.enabled = None
+
+    def isEnabled(self,):
+        """
+        Return true if enabled (surprisingly)
+        """
+        return not not self.enabled
+
+    def header_text(self,):
+        """
+        header_text(self,) => Text that appears in the content's view
+                              heading zone
+        """
+        if not "acl_users" in self.objectIds():
+            return "Please put an acl_users here before ever " \
+                   "starting to use this object."
+
+        ret = """In this folder, groups are seen as ROLES from user's
+                 view. To put a user into a group, affect him a role
+                 that matches his group.<br />"""
+
+        return ret
+
+
+    def listGroups(self,):
+        """
+        listGroups(self,) => return a list of groups defined as roles
+        """
+        return self.Groups.restrictedTraverse('listGroups')()
+
+
+    def userdefined_roles(self):
+        "Return list of user-defined roles"
+        return self.listGroups()
+
+
+class GRUFGroups(GRUFFolder):
+    """
+    GRUFGroups : GRUFFolder that holds groups
+    """
+    meta_type="GRUFGroups"
+    default_id = "Groups"
+
+    _group_prefix = "group_"
+
+
+    class C__ac_roles__(Persistent, Implicit, DynaList.DynaList):
+        """
+        __ac_roles__ dynastring.
+        Do not forget to set _target to class instance.
+
+        XXX DynaList is surely not efficient but it's the only way
+        I found to do what I wanted easily. Someone should take
+        a look to PerstList instead to see if it's possible
+        to do the same ? (ie. having a list which elements are
+        the results of a method call).
+
+        However, even if DynaList is not performant, it's not
+        a critical point because this list is meant to be
+        looked at only when a User object is looked at INSIDE
+        GRUF (especially to set groups a user belongs to).
+        So in practice only used within ZMI.
+        """
+        def data(self,):
+            return self.userdefined_roles()
+
+
+    ac_roles = C__ac_roles__()
+    __ac_roles__ = ac_roles
+
+
+    def header_text(self,):
+        """
+        header_text(self,) => Text that appears in the content's
+                              view heading zone
+        """
+        ret = ""
+        if not "acl_users" in self.objectIds():
+            return "Please put an acl_users here before ever " \
+                   "starting to use this object."
+        return ret
+
+    def _getGroup(self, id):
+        """
+        _getGroup(self, id) => same as getUser() but... with a group :-)
+        This method will return an UNWRAPPED object
+        """
+        return self.acl_users.getUser(id)
+
+
+    def listGroups(self, prefixed = 1):
+        """
+        Return a list of available groups.
+        Group names are prefixed !
+        """
+        if not prefixed:
+            return self.acl_users.getUserNames()
+        else:
+            ret = []
+            for grp in self.acl_users.getUserNames():
+                ret.append("%s%s" % (self._group_prefix, grp))
+            return ret
+
+
+    def userdefined_roles(self):
+        "Return list of user-defined roles"
+        return self.listGroups()
+
+
+InitializeClass(GRUFUsers)
+InitializeClass(GRUFGroups)
diff --git a/GRUFUser.py b/GRUFUser.py
new file mode 100644
index 0000000..4142c42
--- /dev/null
+++ b/GRUFUser.py
@@ -0,0 +1,935 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: GRUFUser.py 40118 2007-04-01 15:13:44Z alecm $
+__docformat__ = 'restructuredtext'
+
+from copy import copy
+
+# fakes a method from a DTML File
+from Globals import MessageDialog, DTMLFile
+
+from AccessControl import ClassSecurityInfo
+from AccessControl import Permissions
+from AccessControl import getSecurityManager
+from Globals import InitializeClass
+from Acquisition import Implicit, aq_inner, aq_parent, aq_base
+from Globals import Persistent
+from AccessControl.Role import RoleManager
+from OFS.SimpleItem import Item
+from OFS.PropertyManager import PropertyManager
+from OFS import ObjectManager, SimpleItem
+from DateTime import DateTime
+from App import ImageFile
+import AccessControl.Role, webdav.Collection
+import Products
+import os
+import string
+import shutil
+import random
+from global_symbols import *
+import AccessControl
+from Products.GroupUserFolder import postonly
+import GRUFFolder
+import GroupUserFolder
+from AccessControl.PermissionRole \
+  import _what_not_even_god_should_do, rolesForPermissionOn
+from ComputedAttribute import ComputedAttribute
+
+
+import os
+import traceback
+
+from interfaces.IUserFolder import IUser, IGroup
+
+_marker = ['INVALID_VALUE']
+
+# NOTE : _what_not_even_god_should_do is a specific permission defined by ZOPE
+# that indicates that something has not to be done within Zope.
+# This value is given to the ACCESS_NONE directive of a SecurityPolicy.
+# It's rarely used within Zope BUT as it is documented (in AccessControl)
+# and may be used by third-party products, we have to process it.
+
+
+#GROUP_PREFIX is a constant
+
+class GRUFUserAtom(AccessControl.User.BasicUser, Implicit): 
+    """
+    Base class for all GRUF-catched User objects.
+    There's, alas, many copy/paste from AccessControl.BasicUser...
+    """
+    security = ClassSecurityInfo()
+
+    security.declarePrivate('_setUnderlying')
+    def _setUnderlying(self, user):
+        """
+        _setUnderlying(self, user) => Set the GRUFUser properties to
+        the underlying user's one.
+        Be careful that any change to the underlying user won't be
+        reported here. $$$ We don't know yet if User object are
+        transaction-persistant or not...
+        """
+        self._original_name   = user.getUserName()
+        self._original_password = user._getPassword()
+        self._original_roles = user.getRoles()
+        self._original_domains = user.getDomains()
+        self._original_id = user.getId()
+        self.__underlying__ = user # Used for authenticate() and __getattr__
+
+
+    # ----------------------------
+    # Public User object interface
+    # ----------------------------
+
+    # Maybe allow access to unprotected attributes. Note that this is
+    # temporary to avoid exposing information but without breaking
+    # everyone's current code. In the future the security will be
+    # clamped down and permission-protected here. Because there are a
+    # fair number of user object types out there, this method denies
+    # access to names that are private parts of the standard User
+    # interface or implementation only. The other approach (only
+    # allowing access to public names in the User interface) would
+    # probably break a lot of other User implementations with extended
+    # functionality that we cant anticipate from the base scaffolding.
+
+    security.declarePrivate('__init__')
+    def __init__(self, underlying_user, GRUF, isGroup, source_id, ):
+        # When calling, set isGroup it to TRUE if this user represents a group
+        self._setUnderlying(underlying_user)
+        self._isGroup = isGroup
+        self._GRUF = GRUF
+        self._source_id = source_id
+        self.id = self._original_id
+        # Store the results of getRoles and getGroups. Initially set to None,
+        # set to a list after the methods are first called.
+        # If you are caching users you want to clear these.
+        self.clearCachedGroupsAndRoles()
+
+    security.declarePrivate('clearCachedGroupsAndRoles')
+    def clearCachedGroupsAndRoles(self, underlying_user = None):
+        self._groups = None
+        self._user_roles = None
+        self._group_roles = None
+        self._all_roles = None
+        if underlying_user:
+            self._setUnderlying(underlying_user)
+        self._original_user_roles = None
+
+    security.declarePublic('isGroup')
+    def isGroup(self,):
+        """Return 1 if this user is a group abstraction"""
+        return self._isGroup
+
+    security.declarePublic('getUserSourceId')
+    def getUserSourceId(self,):
+        """
+        getUserSourceId(self,) => string
+        Return the GRUF's GRUFUsers folder used to fetch this user.
+        """
+        return self._source_id
+
+    security.declarePrivate('getGroupNames')
+    def getGroupNames(self,):
+        """..."""
+        ret = self._getGroups(no_recurse = 1)
+        return map(lambda x: x[GROUP_PREFIX_LEN:], ret)
+
+    security.declarePrivate('getGroupIds')
+    def getGroupIds(self,):
+        """..."""
+        return list(self._getGroups(no_recurse = 1))
+
+    security.declarePrivate("getAllGroups")
+    def getAllGroups(self,):
+        """Same as getAllGroupNames()"""
+        return self.getAllGroupIds()
+
+    security.declarePrivate('getAllGroupNames')
+    def getAllGroupNames(self,):
+        """..."""
+        ret = self._getGroups()
+        return map(lambda x: x[GROUP_PREFIX_LEN:], ret)
+
+    security.declarePrivate('getAllGroupIds')
+    def getAllGroupIds(self,):
+        """..."""
+        return list(self._getGroups())
+
+    security.declarePrivate('getGroups')
+    def getGroups(self, *args, **kw):
+        """..."""
+        ret = self._getGroups(*args, **kw)
+        return list(ret)
+
+    security.declarePrivate("getImmediateGroups")
+    def getImmediateGroups(self,):
+        """
+        Return NON-TRANSITIVE groups
+        """
+        ret = self._getGroups(no_recurse = 1)
+        return list(ret)
+    
+    def _getGroups(self, no_recurse = 0, already_done = None, prefix = GROUP_PREFIX):
+        """
+        getGroups(self, no_recurse = 0, already_done = None, prefix = GROUP_PREFIX) => list of strings
+        
+        If this user is a user (uh, uh), get its groups.
+        THIS METHODS NOW SUPPORTS NESTED GROUPS ! :-)
+        The already_done parameter prevents infite recursions.
+        Keep it as it is, never give it a value.
+
+        If no_recurse is true, return only first level groups
+
+        This method is private and should remain so.
+        """
+        if already_done is None:
+            already_done = []
+
+        # List this user's roles. We consider that roles starting 
+        # with GROUP_PREFIX are in fact groups, and thus are 
+        # returned (prefixed).
+        if self._groups is not None:
+            return self._groups
+
+        # Populate cache if necessary
+        if self._original_user_roles is None:
+            self._original_user_roles = self.__underlying__.getRoles()
+
+        # Scan roles to find groups
+        ret = []
+        for role in self._original_user_roles:
+            # Inspect group-like roles
+            if role.startswith(prefix):
+
+                # Prevent infinite recursion
+                if self._isGroup and role in already_done:
+                    continue
+
+                # Get the underlying group
+                grp = self.aq_parent.getUser(role)
+                if not grp:
+                    continue    # Invalid group
+
+                # Do not add twice the current group
+                if role in ret:
+                    continue
+
+                # Append its nested groups (if recurse is asked)
+                ret.append(role)
+                if no_recurse:
+                    continue
+                for extend in grp.getGroups(already_done = ret):
+                    if not extend in ret:
+                        ret.append(extend)
+
+        # Return the groups
+        self._groups = tuple(ret)
+        return self._groups
+
+
+    security.declarePrivate('getGroupsWithoutPrefix')
+    def getGroupsWithoutPrefix(self, **kw):
+        """
+        Same as getGroups but return them without a prefix.
+        """
+        ret = []
+        for group in self.getGroups(**kw):
+          if group.startswith(GROUP_PREFIX):
+            ret.append(group[len(GROUP_PREFIX):])
+        return ret
+
+    security.declarePublic('getUserNameWithoutGroupPrefix')
+    def getUserNameWithoutGroupPrefix(self):
+        """Return the username of a user without a group prefix"""
+        if self.isGroup() and \
+          self._original_name[:len(GROUP_PREFIX)] == GROUP_PREFIX:
+            return self._original_name[len(GROUP_PREFIX):]
+        return self._original_name
+
+    security.declarePublic('getUserId')
+    def getUserId(self):
+        """Return the user id of a user"""
+        if self.isGroup() and \
+          not self._original_name[:len(GROUP_PREFIX)] == GROUP_PREFIX:
+            return "%s%s" % (GROUP_PREFIX, self._original_name )
+        return self._original_name
+
+    security.declarePublic("getName")
+    def getName(self,):
+        """Get user's or group's name.
+        For a user, the name can be set by the underlying user folder but usually id == name.
+        For a group, the ID is prefixed, but the NAME is NOT prefixed by 'group_'.
+        """
+        return self.getUserNameWithoutGroupPrefix()
+
+    security.declarePublic("getUserName")
+    def getUserName(self,):
+        """Alias for getName()"""
+        return self.getUserNameWithoutGroupPrefix()
+    
+    security.declarePublic('getId')
+    def getId(self, unprefixed = 0):
+        """Get the ID of the user. The ID can be used, at least from
+        Python, to get the user from the user's UserDatabase
+        """
+        # Return the right id
+        if self.isGroup() and not self._original_name.startswith(GROUP_PREFIX) and not unprefixed:
+            return "%s%s" % (GROUP_PREFIX, self._original_name)
+        return self._original_name
+
+    security.declarePublic('getRoles')
+    def getRoles(self):
+        """
+        Return the list (tuple) of roles assigned to a user.
+        THIS IS WHERE THE ATHENIANS REACHED !
+        """
+        if self._all_roles is not None:
+            return self._all_roles
+
+        # Return user and groups roles
+        self._all_roles = GroupUserFolder.unique(self.getUserRoles() + self.getGroupRoles())
+        return self._all_roles
+
+    security.declarePublic('getUserRoles')
+    def getUserRoles(self):
+        """
+        returns the roles defined for the user without the group roles
+        """
+        if self._user_roles is not None:
+            return self._user_roles
+        prefix = GROUP_PREFIX
+        if self._original_user_roles is None:
+            self._original_user_roles = self.__underlying__.getRoles()
+        self._user_roles = tuple([r for r in self._original_user_roles if not r.startswith(prefix)])
+        return self._user_roles
+
+    security.declarePublic("getGroupRoles")
+    def getGroupRoles(self,):
+        """
+        Return the tuple of roles belonging to this user's group(s)
+        """
+        if self._group_roles is not None:
+            return self._group_roles
+        ret = []
+        acl_users = self._GRUF.acl_users 
+        groups = acl_users.getGroupIds()      # XXX We can have a cache here
+        
+        for group in self.getGroups():
+            if not group in groups:
+                Log("Group", group, "is invalid. Ignoring.")
+                # This may occur when groups are deleted
+                # Ignored silently
+                continue
+            ret.extend(acl_users.getGroup(group).getUserRoles())
+
+        self._group_roles = GroupUserFolder.unique(ret)
+        return self._group_roles
+
+    security.declarePublic('getRolesInContext')
+    def getRolesInContext(self, object, userid = None):
+        """
+        Return the list of roles assigned to the user,
+        including local roles assigned in context of
+        the passed in object.
+        """
+        if not userid:
+            userid=self.getId()
+
+        roles = {}
+        for role in self.getRoles():
+            roles[role] = 1
+
+        user_groups = self.getGroups()
+
+        inner_obj = getattr(object, 'aq_inner', object)
+        while 1:
+            # Usual local roles retreiving
+            local_roles = getattr(inner_obj, '__ac_local_roles__', None)
+            if local_roles:
+                if callable(local_roles):
+                    local_roles = local_roles()
+                dict = local_roles or {}
+
+                for role in dict.get(userid, []):
+                    roles[role] = 1
+
+                # Get roles & local roles for groups
+                # This handles nested groups as well
+                for groupid in user_groups:
+                    for role in dict.get(groupid, []):
+                        roles[role] = 1
+                        
+            # LocalRole blocking
+            obj = getattr(inner_obj, 'aq_base', inner_obj)
+            if getattr(obj, '__ac_local_roles_block__', None):
+                break
+
+            # Loop management
+            inner = getattr(inner_obj, 'aq_inner', inner_obj)
+            parent = getattr(inner, 'aq_parent', None)
+            if parent is not None:
+                inner_obj = parent
+                continue
+            if hasattr(inner_obj, 'im_self'):
+                inner_obj=inner_obj.im_self
+                inner_obj=getattr(inner_obj, 'aq_inner', inner_obj)
+                continue
+            break
+
+        return tuple(roles.keys())
+
+    security.declarePublic('getDomains')
+    def getDomains(self):
+        """Return the list of domain restrictions for a user"""
+        return self._original_domains
+
+
+    security.declarePrivate("getProperty")
+    def getProperty(self, name, default=_marker):
+        """getProperty(self, name) => return property value or raise AttributeError
+        """
+        # Try to do an attribute lookup on the underlying user object
+        v = getattr(self.__underlying__, name, default)
+        if v is _marker:
+            raise AttributeError, name
+        return v
+
+    security.declarePrivate("hasProperty")
+    def hasProperty(self, name):
+        """hasProperty"""
+        return hasattr(self.__underlying__, name)
+
+    security.declarePrivate("setProperty")
+    def setProperty(self, name, value):
+        """setProperty => Try to set the property...
+        By now, it's available only for LDAPUserFolder
+        """
+        # Get actual source
+        src = self._GRUF.getUserSource(self.getUserSourceId())
+        if not src:
+            raise RuntimeError, "Invalid or missing user source for '%s'." % (self.getId(),)
+
+        # LDAPUserFolder => specific API.
+        if hasattr(src, "manage_setUserProperty"):
+            # Unmap pty name if necessary, get it in the schema
+            ldapname = None
+            for schema in src.getSchemaConfig().values():
+                if schema["ldap_name"] == name:
+                    ldapname = schema["ldap_name"]
+                if schema["public_name"] == name:
+                    ldapname = schema["ldap_name"]
+                    break
+
+            # If we didn't find it, we skip it
+            if ldapname is None:
+                raise KeyError, "Invalid LDAP attribute: '%s'." % (name, )
+            
+            # Edit user
+            user_dn = src._find_user_dn(self.getUserName())
+            src.manage_setUserProperty(user_dn, ldapname, value)
+
+            # Expire the underlying user object
+            self.__underlying__ = src.getUser(self.getId())
+            if not self.__underlying__:
+                raise RuntimeError, "Error while setting property of '%s'." % (self.getId(),)
+
+        # Now we check if the property has been changed
+        if not self.hasProperty(name):
+            raise NotImplementedError, "Property setting is not supported for '%s'." % (name,)
+        v = self._GRUF.getUserById(self.getId()).getProperty(name)
+        if not v == value:
+            Log(LOG_DEBUG, "Property '%s' for user '%s' should be '%s' and not '%s'" % (
+                name, self.getId(), value, v,
+                ))
+            raise NotImplementedError, "Property setting is not supported for '%s'." % (name,)
+
+    # ------------------------------
+    # Internal User object interface
+    # ------------------------------
+
+    security.declarePrivate('authenticate')
+    def authenticate(self, password, request):
+        # We prevent groups from authenticating
+        if self._isGroup:
+            return None
+        return self.__underlying__.authenticate(password, request)
+
+
+    security.declarePublic('allowed')
+    def allowed(self, object, object_roles=None):
+        """Check whether the user has access to object. The user must
+           have one of the roles in object_roles to allow access."""
+
+        if object_roles is _what_not_even_god_should_do:
+            return 0
+
+        # Short-circuit the common case of anonymous access.
+        if object_roles is None or 'Anonymous' in object_roles:
+            return 1
+
+        # Provide short-cut access if object is protected by 'Authenticated'
+        # role and user is not nobody
+        if 'Authenticated' in object_roles and \
+            (self.getUserName() != 'Anonymous User'):
+            return 1
+
+        # Check for ancient role data up front, convert if found.
+        # This should almost never happen, and should probably be
+        # deprecated at some point.
+        if 'Shared' in object_roles:
+            object_roles = self._shared_roles(object)
+            if object_roles is None or 'Anonymous' in object_roles:
+                return 1
+
+
+        # Trying to make some speed improvements, changes starts here.
+        # Helge Tesdal, Plone Solutions AS, http://www.plonesolutions.com
+        # We avoid using the getRoles() and getRolesInContext() methods to be able
+        # to short circuit.
+
+        # Dict for faster lookup and avoiding duplicates
+        object_roles_dict = {}
+        for role in object_roles:
+            object_roles_dict[role] = 1
+
+        if [role for role in self.getUserRoles() if object_roles_dict.has_key(role)]:
+            if self._check_context(object):
+                return 1
+            return None
+
+        # Try the top level group roles.
+        if [role for role in self.getGroupRoles() if object_roles_dict.has_key(role)]:
+            if self._check_context(object):
+                return 1
+            return None
+
+        user_groups = self.getGroups()
+        # No luck on the top level, try local roles
+        inner_obj = getattr(object, 'aq_inner', object)
+        userid = self.getId()
+        while 1:
+            local_roles = getattr(inner_obj, '__ac_local_roles__', None)
+            if local_roles:
+                if callable(local_roles):
+                    local_roles = local_roles()
+                dict = local_roles or {}
+
+                if [role for role in dict.get(userid, []) if object_roles_dict.has_key(role)]:
+                    if self._check_context(object):
+                        return 1
+                    return None
+
+                # Get roles & local roles for groups
+                # This handles nested groups as well
+                for groupid in user_groups:
+                    if [role for role in dict.get(groupid, []) if object_roles_dict.has_key(role)]:
+                        if self._check_context(object):
+                            return 1
+                        return None
+
+            # LocalRole blocking
+            obj = getattr(inner_obj, 'aq_base', inner_obj)
+            if getattr(obj, '__ac_local_roles_block__', None):
+                break
+
+            # Loop control
+            inner = getattr(inner_obj, 'aq_inner', inner_obj)
+            parent = getattr(inner, 'aq_parent', None)
+            if parent is not None:
+                inner_obj = parent
+                continue
+            if hasattr(inner_obj, 'im_self'):
+                inner_obj=inner_obj.im_self
+                inner_obj=getattr(inner_obj, 'aq_inner', inner_obj)
+                continue
+            break
+        return None
+
+
+    security.declarePublic('hasRole')
+    def hasRole(self, *args, **kw):
+        """hasRole is an alias for 'allowed' and has been deprecated.
+
+        Code still using this method should convert to either 'has_role' or
+        'allowed', depending on the intended behaviour.
+
+        """
+        import warnings
+        warnings.warn('BasicUser.hasRole is deprecated, please use '
+            'BasicUser.allowed instead; hasRole was an alias for allowed, but '
+            'you may have ment to use has_role.', DeprecationWarning)
+        return self.allowed(*args, **kw)
+
+    #                                                           #
+    #               Underlying user object support              #
+    #                                                           #
+    
+    def __getattr__(self, name):
+        # This will call the underlying object's methods
+        # if they are not found in this user object.
+        # We will have to check Chris' http://www.plope.com/Members/chrism/plone_on_zope_head
+        # to make it work with Zope HEAD.
+        ret = getattr(self.__dict__['__underlying__'], name)
+        return ret
+
+    security.declarePublic('getUnwrappedUser')
+    def getUnwrappedUser(self,):
+        """
+        same as GRUF.getUnwrappedUser, but implicitly with this particular user
+        """
+        return self.__dict__['__underlying__']
+
+    def __getitem__(self, name):
+        # This will call the underlying object's methods
+        # if they are not found in this user object.
+        return self.__underlying__[name]
+
+    #                                                           #
+    #                      HTML link support                    #
+    #                                                           #
+
+    def asHTML(self, implicit=0):
+        """
+        asHTML(self, implicit=0) => HTML string
+        Used to generate homogeneous links for management screens
+        """
+        acl_users = self.acl_users
+        if self.isGroup():
+            color = acl_users.group_color
+            kind = "Group"
+        else:
+            color = acl_users.user_color
+            kind = "User"
+
+        ret = '''<a href="%(href)s" alt="%(alt)s"><font color="%(color)s">%(name)s</font></a>''' % {
+            "color": color,
+            "href": "%s/%s/manage_workspace?FORCE_USER=1" % (acl_users.absolute_url(), self.getId(), ),
+            "name": self.getUserNameWithoutGroupPrefix(),
+            "alt": "%s (%s)" % (self.getUserNameWithoutGroupPrefix(), kind, ),
+            }
+        if implicit:
+            return "<i>%s</i>" % ret
+        return ret
+
+    
+    security.declarePrivate("isInGroup")
+    def isInGroup(self, groupid):
+        """Return true if the user is member of the specified group id
+        (including transitive groups)"""
+        return groupid in self.getAllGroupIds()
+
+    security.declarePublic("getRealId")
+    def getRealId(self,):
+        """Return id WITHOUT group prefix
+        """
+        raise NotImplementedError, "Must be derived in subclasses"
+    
+
+class GRUFUser(GRUFUserAtom):
+    """
+    This is the class for actual user objects
+    """
+    __implements__ = (IUser, )
+
+    security = ClassSecurityInfo()
+
+    #                                                           #
+    #                     User Mutation                         #
+    #                                                           #
+
+    security.declarePublic('changePassword')
+    def changePassword(self, password, REQUEST=None):
+        """Set the user's password. This method performs its own security checks"""
+        # Check security
+        user = getSecurityManager().getUser()
+        if not user.has_permission(Permissions.manage_users, self._GRUF):       # Is manager ?
+            if user.__class__.__name__ != "GRUFUser":
+                raise "Unauthorized", "You cannot change someone else's password."
+            if not user.getId() == self.getId():    # Is myself ?
+                raise "Unauthorized", "You cannot change someone else's password."
+        
+        # Just do it
+        self.clearCachedGroupsAndRoles()
+        return self._GRUF.userSetPassword(self.getId(), password)
+    changePassword = postonly(changePassword)
+
+    security.declarePrivate("setRoles")
+    def setRoles(self, roles):
+        """Change the roles of a user atom.
+        """
+        self.clearCachedGroupsAndRoles()
+        return self._GRUF.userSetRoles(self.getId(), roles)
+
+    security.declarePrivate("addRole")
+    def addRole(self, role):
+        """Append a role for a user atom
+        """
+        self.clearCachedGroupsAndRoles()
+        return self._GRUF.userAddRole(self.getId(), role)
+
+    security.declarePrivate("removeRole")
+    def removeRole(self, role):
+        """Remove the role of a user atom
+        """
+        self.clearCachedGroupsAndRoles()
+        return self._GRUF.userRemoveRole(self.getId(), role)
+
+    security.declarePrivate("setPassword")
+    def setPassword(self, newPassword):
+        """Set the password of a user
+        """
+        self.clearCachedGroupsAndRoles()
+        return self._GRUF.userSetPassword(self.getId(), newPassword)
+
+    security.declarePrivate("setDomains")
+    def setDomains(self, domains):
+        """Set domains for a user
+        """
+        self.clearCachedGroupsAndRoles()
+        self._GRUF.userSetDomains(self.getId(), domains)
+        self._original_domains = self._GRUF.userGetDomains(self.getId())
+
+    security.declarePrivate("addDomain")
+    def addDomain(self, domain):
+        """Append a domain to a user
+        """
+        self.clearCachedGroupsAndRoles()
+        self._GRUF.userAddDomain(self.getId(), domain)
+        self._original_domains = self._GRUF.userGetDomains(self.getId())
+
+    security.declarePrivate("removeDomain")
+    def removeDomain(self, domain):
+        """Remove a domain from a user
+        """
+        self.clearCachedGroupsAndRoles()
+        self._GRUF.userRemoveDomain(self.getId(), domain)
+        self._original_domains = self._GRUF.userGetDomains(self.getId())
+
+    security.declarePrivate("setGroups")
+    def setGroups(self, groupnames):
+        """Set the groups of a user
+        """
+        self.clearCachedGroupsAndRoles()
+        return self._GRUF.userSetGroups(self.getId(), groupnames)
+
+    security.declarePrivate("addGroup")
+    def addGroup(self, groupname):
+        """add a group to a user atom
+        """
+        self.clearCachedGroupsAndRoles()
+        return self._GRUF.userAddGroup(self.getId(), groupname)
+
+    security.declarePrivate("removeGroup")
+    def removeGroup(self, groupname):
+        """remove a group from a user atom.
+        """
+        self.clearCachedGroupsAndRoles()
+        return self._GRUF.userRemoveGroup(self.getId(), groupname)
+
+    security.declarePrivate('_getPassword')
+    def _getPassword(self):
+        """Return the password of the user."""
+        return self._original_password
+
+    security.declarePublic("getRealId")
+    def getRealId(self,):
+        """Return id WITHOUT group prefix
+        """
+        return self.getId()
+
+
+class GRUFGroup(GRUFUserAtom):
+    """
+    This is the class for actual group objects
+    """
+    __implements__ = (IGroup, )
+    
+    security = ClassSecurityInfo()
+    
+    security.declarePublic("getRealId")
+    def getRealId(self,):
+        """Return group id WITHOUT group prefix
+        """
+        return self.getId()[len(GROUP_PREFIX):]
+    
+    def _getLDAPMemberIds(self,):
+        """
+        _getLDAPMemberIds(self,) => Uses LDAPUserFolder to find
+        users in a group.
+        """
+        # Find the right source
+        gruf = self.aq_parent
+        src = None
+        for src in gruf.listUserSources():
+            if not src.meta_type == "LDAPUserFolder":
+                continue
+        if src is None:
+            Log(LOG_DEBUG, "No LDAPUserFolder source found")
+            return []
+
+        # Find the group in LDAP
+        groups = src.getGroups()
+        groupid = self.getId()
+        grp = [ group for group in groups if group[0] == self.getId() ]
+        if not grp:
+            Log(LOG_DEBUG, "No such group ('%s') found." % (groupid,))
+            return []
+
+        # Return the grup member ids
+        userids = src.getGroupedUsers(grp)
+        Log(LOG_DEBUG, "We've found %d users belonging to the group '%s'" % (len(userids), grp), )
+        return userids
+    
+    def _getMemberIds(self, users = 1, groups = 1, transitive = 1, ):
+        """
+        Return the member ids (users and groups) of the atoms of this group.
+        Transitiveness attribute is ignored with LDAP (no nested groups with
+        LDAP anyway).
+        This method now uses a shortcut to fetch members of an LDAP group
+        (stored either within Zope or within your LDAP server)
+        """
+        # Initial parameters.
+        # We fetch the users/groups list depending on what we search,
+        # and carefuly avoiding to use LDAP sources.
+        gruf = self.aq_parent
+        ldap_sources = []
+        lst = []
+        if transitive:
+            method = "getAllGroupIds"
+        else:
+            method = "getGroupIds"
+        if users:
+            for src in gruf.listUserSources():
+                if src.meta_type == 'LDAPUserFolder':
+                    ldap_sources.append(src)
+                    continue # We'll fetch 'em later
+                lst.extend(src.getUserNames())
+        if groups:
+            lst.extend(gruf.getGroupIds())
+
+        # First extraction for regular user sources.
+        # This part is very very long, and the more users you have,
+        # the longer this method will be.
+        groupid = self.getId()
+        groups_mapping = {}
+        for u in lst:
+            usr = gruf.getUser(u)
+            if not usr:
+                groups_mapping[u] = []
+                Log(LOG_WARNING, "Invalid user retreiving:", u)
+            else:
+                groups_mapping[u] = getattr(usr, method)()
+        members = [u for u in lst if groupid in groups_mapping[u]]
+
+        # If we have LDAP sources, we fetch user-group mapping inside directly
+        groupid = self.getId()
+        for src in ldap_sources:
+            groups = src.getGroups()
+            # With LDAPUserFolder >= 2.7 we need to add GROUP_PREFIX to group_name
+            # We keep backward compatibility
+            grp = [ group for group in groups if group[0] == self.getId() or \
+                                                 GROUP_PREFIX + group[0] == self.getId()]
+            if not grp:
+                Log(LOG_DEBUG, "No such group ('%s') found." % (groupid,))
+                continue
+
+            # Return the grup member ids
+            userids = [ str(u) for u in src.getGroupedUsers(grp) ]
+            Log(LOG_DEBUG, "We've found %d users belonging to the group '%s'" % (len(userids), grp), )
+            members.extend(userids)
+
+        # Return the members we've found
+        return members
+
+    security.declarePrivate("getMemberIds")
+    def getMemberIds(self, transitive = 1, ):
+        "Return member ids of this group, including or not transitive groups."
+        return self._getMemberIds(transitive = transitive)
+
+    security.declarePrivate("getUserMemberIds")
+    def getUserMemberIds(self, transitive = 1, ):
+        """Return the member ids (users only) of the users of this group"""
+        return self._getMemberIds(groups = 0, transitive = transitive)
+    
+    security.declarePrivate("getGroupMemberIds")
+    def getGroupMemberIds(self, transitive = 1, ):
+        """Return the members ids (groups only) of the groups of this group"""
+        return self._getMemberIds(users = 0, transitive = transitive)
+    
+    security.declarePrivate("hasMember")
+    def hasMember(self, id):
+        """Return true if the specified atom id is in the group.
+        This is the contrary of IUserAtom.isInGroup(groupid)"""
+        gruf = self.aq_parent
+        return id in gruf.getMemberIds(self.getId())
+    
+    security.declarePrivate("addMember")
+    def addMember(self, userid):
+        """Add a user the the current group"""
+        gruf = self.aq_parent
+        groupid = self.getId()
+        usr = gruf.getUser(userid)
+        if not usr:
+            raise ValueError, "Invalid user: '%s'" % (userid, )
+        if not groupid in gruf.getGroupNames() + gruf.getGroupIds():
+            raise ValueError, "Invalid group: '%s'" % (groupid, )
+        groups = list(usr.getGroups())
+        groups.append(groupid)
+        groups = GroupUserFolder.unique(groups)
+        return gruf._updateUser(userid, groups = groups)
+
+    security.declarePrivate("removeMember")
+    def removeMember(self, userid):
+        """Remove a user from the current group"""
+        gruf = self.aq_parent
+        groupid = self.getId()
+
+        # Check the user
+        usr = gruf.getUser(userid)
+        if not usr:
+            raise ValueError, "Invalid user: '%s'" % (userid, )
+
+        # Now, remove the group
+        groups = list(usr.getImmediateGroups())
+        if groupid in groups:
+            groups.remove(groupid)
+            gruf._updateUser(userid, groups = groups)
+        else:
+            raise ValueError, "User '%s' doesn't belong to group '%s'" % (userid, groupid, )
+    
+    security.declarePrivate("setMembers")
+    def setMembers(self, userids):
+        """Set the members of the group
+        """
+        member_ids = self.getMemberIds()
+        all_ids = copy(member_ids)
+        all_ids.extend(userids)
+        groupid = self.getId()
+        for id in all_ids:
+            if id in member_ids and id not in userids:
+                self.removeMember(id)
+            elif id not in member_ids and id in userids:
+                self.addMember(id)
+
+
+InitializeClass(GRUFUser)
+InitializeClass(GRUFGroup)
diff --git a/GroupDataTool.py b/GroupDataTool.py
new file mode 100644
index 0000000..9b05326
--- /dev/null
+++ b/GroupDataTool.py
@@ -0,0 +1,443 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+## Copyright (c) 2003 The Connexions Project, All Rights Reserved
+## initially written by J Cameron Cooper, 11 June 2003
+## concept with Brent Hendricks, George Runyan
+"""
+Basic group data tool.
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: GroupDataTool.py 52136 2007-10-21 20:38:00Z encolpe $
+__docformat__ = 'restructuredtext'
+
+from Products.CMFCore.utils import UniqueObject, getToolByName
+from OFS.SimpleItem import SimpleItem
+from OFS.PropertyManager import PropertyManager
+from Globals import DTMLFile
+from Globals import InitializeClass
+from AccessControl.Role import RoleManager
+from BTrees.OOBTree import OOBTree
+from ZPublisher.Converters import type_converters
+from Acquisition import aq_inner, aq_parent, aq_base
+from AccessControl import ClassSecurityInfo, Permissions, Unauthorized, getSecurityManager
+
+from Products.CMFCore.ActionProviderBase import ActionProviderBase
+# BBB CMF < 1.5
+try:
+    from Products.CMFCore.permissions import ManagePortal
+except ImportError:
+    from Products.CMFCore.CMFCorePermissions import ManagePortal
+
+from Products.CMFCore.MemberDataTool import CleanupTemp
+
+from interfaces.portal_groupdata import portal_groupdata as IGroupDataTool
+from interfaces.portal_groupdata import GroupData as IGroupData
+from Products.GroupUserFolder import postonly
+from Products.GroupUserFolder.GRUFUser import GRUFGroup
+
+_marker = []  # Create a new marker object.
+
+from global_symbols import *
+
+
+class GroupDataTool (UniqueObject, SimpleItem, PropertyManager, ActionProviderBase):
+    """ This tool wraps group objects, allowing transparent access to properties.
+    """
+    # The latter will work only with Plone 1.1 => hence, the if
+    __implements__ = (IGroupDataTool, ActionProviderBase.__implements__)
+
+    id = 'portal_groupdata'
+    meta_type = 'CMF Group Data Tool'
+    _actions = ()
+
+    _v_temps = None
+    _properties=({'id':'title', 'type': 'string', 'mode': 'wd'},)
+
+    security = ClassSecurityInfo()
+
+    manage_options=( ActionProviderBase.manage_options +
+                     ({ 'label' : 'Overview'
+                       , 'action' : 'manage_overview'
+                       },
+                     )
+                   + PropertyManager.manage_options
+                   + SimpleItem.manage_options
+                   )
+
+    #
+    #   ZMI methods
+    #
+    security.declareProtected(ManagePortal, 'manage_overview')
+    manage_overview = DTMLFile('dtml/explainGroupDataTool', globals())
+
+    def __init__(self):
+        self._members = OOBTree()
+        # Create the default properties.
+        self._setProperty('description', '', 'text')
+        self._setProperty('email', '', 'string')
+
+    #
+    #   'portal_groupdata' interface methods
+    #
+    security.declarePrivate('wrapGroup')
+    def wrapGroup(self, g):
+        """Returns an object implementing the GroupData interface"""
+        id = g.getId()
+        members = self._members
+        if not members.has_key(id):
+            # Get a temporary member that might be
+            # registered later via registerMemberData().
+            temps = self._v_temps
+            if temps is not None and temps.has_key(id):
+                portal_group = temps[id]
+            else:
+                base = aq_base(self)
+                portal_group = GroupData(base, id)
+                if temps is None:
+                    self._v_temps = {id:portal_group}
+                    if hasattr(self, 'REQUEST'):
+                        # No REQUEST during tests.
+                        self.REQUEST._hold(CleanupTemp(self))
+                else:
+                    temps[id] = portal_group
+        else:
+            portal_group = members[id]
+        # Return a wrapper with self as containment and
+        # the user as context.
+        return portal_group.__of__(self).__of__(g)
+
+    security.declarePrivate('registerGroupData')
+    def registerGroupData(self, g, id):
+        '''
+        Adds the given member data to the _members dict.
+        This is done as late as possible to avoid side effect
+        transactions and to reduce the necessary number of
+        entries.
+        '''
+        self._members[id] = aq_base(g)
+
+InitializeClass(GroupDataTool)
+
+
+class GroupData (SimpleItem):
+
+    __implements__ = IGroupData
+
+    security = ClassSecurityInfo()
+
+    id = None
+    _tool = None
+
+    def __init__(self, tool, id):
+        self.id = id
+        # Make a temporary reference to the tool.
+        # The reference will be removed by notifyModified().
+        self._tool = tool
+
+    def _getGRUF(self,):
+        return self.acl_users
+
+    security.declarePrivate('notifyModified')
+    def notifyModified(self):
+        # Links self to parent for full persistence.
+        tool = getattr(self, '_tool', None)
+        if tool is not None:
+            del self._tool
+            tool.registerGroupData(self, self.getId())
+
+    security.declarePublic('getGroup')
+    def getGroup(self):
+        """ Returns the actual group implementation. Varies by group
+        implementation (GRUF/Nux/et al). In GRUF this is a user object."""
+        # The user object is our context, but it's possible for
+        # restricted code to strip context while retaining
+        # containment.  Therefore we need a simple security check.
+        parent = aq_parent(self)
+        bcontext = aq_base(parent)
+        bcontainer = aq_base(aq_parent(aq_inner(self)))
+        if bcontext is bcontainer or not hasattr(bcontext, 'getUserName'):
+            raise 'GroupDataError', "Can't find group data"
+        # Return the user object, which is our context.
+        return parent
+
+    def getTool(self):
+        return aq_parent(aq_inner(self))
+
+    security.declarePublic("getGroupMemberIds")
+    def getGroupMemberIds(self,):
+        """
+        Return a list of group member ids
+        """
+        return map(lambda x: x.getMemberId(), self.getGroupMembers())
+
+    security.declarePublic("getAllGroupMemberIds")
+    def getAllGroupMemberIds(self,):
+        """
+        Return a list of group member ids
+        """
+        return map(lambda x: x.getMemberId(), self.getAllGroupMembers())
+
+    security.declarePublic('getGroupMembers')
+    def getGroupMembers(self, ):
+        """
+        Returns a list of the portal_memberdata-ish members of the group.
+        This doesn't include TRANSITIVE groups/users.
+        """
+        md = self.portal_memberdata
+        gd = self.portal_groupdata
+        ret = []
+        for u_name in self.getGroup().getMemberIds(transitive = 0, ):
+            usr = self._getGRUF().getUserById(u_name)
+            if not usr:
+                raise AssertionError, "Cannot retreive a user by its id !"
+            if usr.isGroup():
+                ret.append(gd.wrapGroup(usr))
+            else:
+                ret.append(md.wrapUser(usr))
+        return ret
+
+    security.declarePublic('getAllGroupMembers')
+    def getAllGroupMembers(self, ):
+        """
+        Returns a list of the portal_memberdata-ish members of the group.
+        This will include transitive groups / users
+        """
+        md = self.portal_memberdata
+        gd = self.portal_groupdata
+        ret = []
+        for u_name in self.getGroup().getMemberIds():
+            usr = self._getGRUF().getUserById(u_name)
+            if not usr:
+                raise AssertionError, "Cannot retreive a user by its id !"
+            if usr.isGroup():
+                ret.append(gd.wrapGroup(usr))
+            else:
+                ret.append(md.wrapUser(usr))
+        return ret
+
+    def _getGroup(self,):
+        """
+        _getGroup(self,) => Get the underlying group object
+        """
+        return self._getGRUF().getGroupByName(self.getGroupName())
+
+
+    security.declarePrivate("canAdministrateGroup")
+    def canAdministrateGroup(self,):
+        """
+        Return true if the #current# user can administrate this group
+        """
+        user = getSecurityManager().getUser()
+        tool = self.getTool()
+        portal = getToolByName(tool, 'portal_url').getPortalObject()
+        
+        # Has manager users pemission? 
+        if user.has_permission(Permissions.manage_users, portal):
+            return True
+
+        # Is explicitly mentioned as a group administrator?
+        managers = self.getProperty('delegated_group_member_managers', ())
+        if user.getId() in managers:
+            return True
+
+        # Belongs to a group which is explicitly mentionned as a group administrator
+        meth = getattr(user, "getAllGroupNames", None)
+        if meth:
+            groups = meth()
+        else:
+            groups = ()
+        for v in groups:
+            if v in managers:
+                return True
+
+        # No right to edit this: we complain.
+        return False
+
+    security.declarePublic('addMember')
+    def addMember(self, id, REQUEST=None):
+        """ Add the existing member with the given id to the group"""
+        # We check if the current user can directly or indirectly administrate this group
+        if not self.canAdministrateGroup():
+            raise Unauthorized, "You cannot add a member to the group."
+        self._getGroup().addMember(id)
+
+        # Notify member that they've been changed
+        mtool = getToolByName(self, 'portal_membership')
+        member = mtool.getMemberById(id)
+        if member:
+            member.notifyModified()
+    addMember = postonly(addMember)
+
+    security.declarePublic('removeMember')
+    def removeMember(self, id, REQUEST=None):
+        """Remove the member with the provided id from the group.
+        """
+        # We check if the current user can directly or indirectly administrate this group
+        if not self.canAdministrateGroup():
+            raise Unauthorized, "You cannot remove a member from the group."
+        self._getGroup().removeMember(id)
+
+        # Notify member that they've been changed
+        mtool = getToolByName(self, 'portal_membership')
+        member = mtool.getMemberById(id)
+        if member:
+            member.notifyModified()
+    removeMember = postonly(removeMember)
+
+    security.declareProtected(Permissions.manage_users, 'setProperties')
+    def setProperties(self, properties=None, **kw):
+        '''Allows the manager group to set his/her own properties.
+        Accepts either keyword arguments or a mapping for the "properties"
+        argument.
+        '''
+        if properties is None:
+            properties = kw
+        return self.setGroupProperties(properties)
+
+    security.declareProtected(Permissions.manage_users, 'setGroupProperties')
+    def setGroupProperties(self, mapping):
+        '''Sets the properties of the member.
+        '''
+        # Sets the properties given in the MemberDataTool.
+        tool = self.getTool()
+        for id in tool.propertyIds():
+            if mapping.has_key(id):
+                if not self.__class__.__dict__.has_key(id):
+                    value = mapping[id]
+                    if type(value)==type(''):
+                        proptype = tool.getPropertyType(id) or 'string'
+                        if type_converters.has_key(proptype):
+                            value = type_converters[proptype](value)
+                    setattr(self, id, value)
+                    
+        # Hopefully we can later make notifyModified() implicit.
+        self.notifyModified()
+
+    security.declarePublic('getProperties')
+    def getProperties(self, ):
+        """ Return the properties of this group. Properties are as usual in Zope."""
+        tool = self.getTool()
+        ret = {}
+        for pty in tool.propertyIds():
+            try:
+                ret[pty] = self.getProperty(pty)
+            except ValueError:
+                # We ignore missing ptys
+                continue
+        return ret
+
+    security.declarePublic('getProperty')
+    def getProperty(self, id, default=_marker):
+        """ Returns the value of the property specified by 'id' """
+        tool = self.getTool()
+        base = aq_base( self )
+
+        # First, check the wrapper (w/o acquisition).
+        value = getattr( base, id, _marker )
+        if value is not _marker:
+            return value
+
+        # Then, check the tool and the user object for a value.
+        tool_value = tool.getProperty( id, _marker )
+        user_value = getattr( aq_base(self.getGroup()), id, _marker )
+
+        # If the tool doesn't have the property, use user_value or default
+        if tool_value is _marker:
+            if user_value is not _marker:
+                return user_value
+            elif default is not _marker:
+                return default
+            else:
+                raise ValueError, 'The property %s does not exist' % id
+
+        # If the tool has an empty property and we have a user_value, use it
+        if not tool_value and user_value is not _marker:
+            return user_value
+
+        # Otherwise return the tool value
+        return tool_value
+
+    def __str__(self):
+        return self.getGroupId()
+
+    security.declarePublic("isGroup")
+    def isGroup(self,):
+        """
+        isGroup(self,) => Return true if this is a group.
+        Will always return true for groups.
+        As MemberData objects do not support this method, it is quite useless by now.
+        So one can use groupstool.isGroup(g) instead to get this information.
+        """
+        return 1
+
+    ### Group object interface ###
+
+    security.declarePublic('getGroupName')
+    def getGroupName(self):
+        """Return the name of the group, without any special decorations (like GRUF prefixes.)"""
+        return self.getGroup().getName()
+
+    security.declarePublic('getGroupId')
+    def getGroupId(self):
+        """Get the ID of the group. The ID can be used, at least from
+        Python, to get the user from the user's UserDatabase.
+        Within Plone, all group ids are UNPREFIXED."""
+        if isinstance(self, GRUFGroup):
+            return self.getGroup().getId(unprefixed = 1)
+        else:
+            return self.getGroup().getId()
+
+    def getGroupTitleOrName(self):
+        """Get the Title property of the group. If there is none
+        then return the name """
+        title = self.getProperty('title', None)
+        return title or self.getGroupName()
+
+    security.declarePublic("getMemberId")
+    def getMemberId(self,):
+        """This exists only for a basic user/group API compatibility
+        """
+        return self.getGroupId()
+
+    security.declarePublic('getRoles')
+    def getRoles(self):
+        """Return the list of roles assigned to a user."""
+        return self.getGroup().getRoles()
+
+    security.declarePublic('getRolesInContext')
+    def getRolesInContext(self, object):
+        """Return the list of roles assigned to the user,  including local
+        roles assigned in context of the passed in object."""
+        return self.getGroup().getRolesInContext(object)
+
+    security.declarePublic('getDomains')
+    def getDomains(self):
+        """Return the list of domain restrictions for a user"""
+        return self.getGroup().getDomains()
+
+    security.declarePublic('has_role')
+    def has_role(self, roles, object=None):
+        """Check to see if a user has a given role or roles."""
+        return self.getGroup().has_role(roles, object)
+
+    # There are other parts of the interface but they are
+    # deprecated for use with CMF applications.
+
+InitializeClass(GroupData)
diff --git a/GroupUserFolder.py b/GroupUserFolder.py
new file mode 100644
index 0000000..8d6d85a
--- /dev/null
+++ b/GroupUserFolder.py
@@ -0,0 +1,2806 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+GroupUserFolder product
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: GroupUserFolder.py 40118 2007-04-01 15:13:44Z alecm $
+__docformat__ = 'restructuredtext'
+
+
+# fakes a method from a DTML file
+from Globals import MessageDialog, DTMLFile
+
+from AccessControl import ClassSecurityInfo
+from AccessControl import Permissions
+from AccessControl import getSecurityManager
+from AccessControl import Unauthorized
+from Globals import InitializeClass
+from Acquisition import aq_base, aq_inner, aq_parent
+from Acquisition import Implicit
+from Globals import Persistent
+from AccessControl.Role import RoleManager
+from OFS.SimpleItem import Item
+from OFS.PropertyManager import PropertyManager
+import OFS
+from OFS import ObjectManager, SimpleItem
+from DateTime import DateTime
+from App import ImageFile
+from Products.PageTemplates import PageTemplateFile
+import AccessControl.Role, webdav.Collection
+import Products
+import os
+import string
+import sys
+import time
+import math
+import random
+from global_symbols import *
+import AccessControl.User
+import GRUFFolder
+import GRUFUser
+from Products.PageTemplates import PageTemplateFile
+import class_utility
+from Products.GroupUserFolder import postonly
+
+from interfaces.IUserFolder import IUserFolder
+
+## Developers notes
+##
+## The REQUEST.GRUF_PROBLEM variable is defined whenever GRUF encounters
+## a problem than can be showed in the management screens. It's always
+## logged as LOG_WARNING level anyway.
+
+_marker = []
+
+def unique(sequence, _list = 0):
+    """Make a sequence a list of unique items"""
+    uniquedict = {}
+    for v in sequence:
+        uniquedict[v] = 1
+    if _list:
+        return list(uniquedict.keys())
+    return tuple(uniquedict.keys())
+
+
+def manage_addGroupUserFolder(self, dtself=None, REQUEST=None, **ignored):
+    """ Factory method that creates a UserFolder"""
+    f=GroupUserFolder()
+    self=self.this()
+    try:    self._setObject('acl_users', f)
+    except: return MessageDialog(
+                   title  ='Item Exists',
+                   message='This object already contains a User Folder',
+                   action ='%s/manage_main' % REQUEST['URL1'])
+    self.__allow_groups__=f
+    self.acl_users._post_init()
+
+    self.acl_users.Users.manage_addUserFolder()
+    self.acl_users.Groups.manage_addUserFolder()
+
+    if REQUEST is not None:
+        REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
+
+
+
+
+class GroupUserFolder(OFS.ObjectManager.ObjectManager,
+                      AccessControl.User.BasicUserFolder,
+                      ):
+    """
+    GroupUserFolder => User folder with groups management
+    """
+
+    #                                                                           #
+    #                              ZOPE  INFORMATION                            #
+    #                                                                           #
+
+    meta_type='Group User Folder'
+    id       ='acl_users'
+    title    ='Group-aware User Folder'
+
+    __implements__ = (IUserFolder, )
+    def __creatable_by_emergency_user__(self): return 1
+
+    isAnObjectManager = 1
+    isPrincipiaFolderish = 1
+    isAUserFolder = 1
+
+##    _haveLDAPUF = 0
+
+    security = ClassSecurityInfo()
+
+    manage_options=(
+        (
+        {'label':'Overview', 'action':'manage_overview'},
+        {'label':'Sources', 'action':'manage_GRUFSources'},
+        {'label':'LDAP Wizard', 'action':'manage_wizard'},
+        {'label':'Groups', 'action':'manage_groups'},
+        {'label':'Users', 'action':'manage_users'},
+        {'label':'Audit', 'action':'manage_audit'},
+        ) + \
+        OFS.ObjectManager.ObjectManager.manage_options + \
+        RoleManager.manage_options + \
+        Item.manage_options )
+
+    manage_main = OFS.ObjectManager.ObjectManager.manage_main
+##    manage_overview = DTMLFile('dtml/GRUF_overview', globals())
+    manage_overview = PageTemplateFile.PageTemplateFile('dtml/GRUF_overview', globals())
+    manage_audit = PageTemplateFile.PageTemplateFile('dtml/GRUF_audit', globals())
+    manage_wizard = PageTemplateFile.PageTemplateFile('dtml/GRUF_wizard', globals())
+    manage_groups = PageTemplateFile.PageTemplateFile('dtml/GRUF_groups', globals())
+    manage_users = PageTemplateFile.PageTemplateFile('dtml/GRUF_users', globals())
+    manage_newusers = PageTemplateFile.PageTemplateFile('dtml/GRUF_newusers', globals())
+    manage_GRUFSources = PageTemplateFile.PageTemplateFile('dtml/GRUF_contents', globals())
+    manage_user = PageTemplateFile.PageTemplateFile('dtml/GRUF_user', globals())
+
+    __ac_permissions__=(
+        ('Manage users',
+         ('manage_users',
+          'user_names', 'setDomainAuthenticationMode',
+          )
+         ),
+        )
+
+
+    # Color constants, only useful within GRUF management screens
+    user_color = "#006600"
+    group_color = "#000099"
+    role_color = "#660000"
+
+    # User and group images
+    img_user = ImageFile.ImageFile('www/GRUFUsers.gif', globals())
+    img_group = ImageFile.ImageFile('www/GRUFGroups.gif', globals())
+
+
+
+    #                                                                           #
+    #                             OFFICIAL INTERFACE                            #
+    #                                                                           #
+
+    security.declarePublic("hasUsers")
+    def hasUsers(self, ):
+        """
+        From Zope 2.7's User.py:
+        This is not a formal API method: it is used only to provide
+        a way for the quickstart page to determine if the default user
+        folder contains any users to provide instructions on how to
+        add a user for newbies.  Using getUserNames or getUsers would have
+        posed a denial of service risk.
+        In GRUF, this method always return 1."""
+        return 1
+
+    security.declareProtected(Permissions.manage_users, "user_names")
+    def user_names(self,):
+        """
+        user_names() => return user IDS and not user NAMES !!!
+        Due to a Zope inconsistency, the Role.get_valid_userids return user names
+        and not user ids - which is bad. As GRUF distinguishes names and ids, this
+        will cause it to break, especially in the listLocalRoles form. So we change
+        user_names() behaviour so that it will return ids and not names.
+        """
+        return self.getUserIds()
+
+
+    security.declareProtected(Permissions.manage_users, "getUserNames")
+    def getUserNames(self, __include_groups__ = 1, __include_users__ = 1, __groups_prefixed__ = 0):
+        """
+        Return a list of all possible user atom names in the system.
+        Groups will be returned WITHOUT their prefix by this method.
+        So, there might be a collision between a user name and a group name.
+        [NOTA: This method is time-expensive !]
+        """
+        if __include_users__:
+            LogCallStack(LOG_DEBUG, "This call can be VERY expensive!")
+        names = []
+        ldap_sources = []
+
+        # Fetch users in user sources
+        if __include_users__:
+            for src in self.listUserSources():
+                names.extend(src.getUserNames())
+
+        # Append groups if possible
+        if __include_groups__:
+            # Regular groups
+            if "acl_users" in self._getOb('Groups').objectIds():
+                names.extend(self.Groups.listGroups(prefixed = __groups_prefixed__))
+
+            # LDAP groups
+            for ldapuf in ldap_sources:
+                if ldapuf._local_groups:
+                    continue
+                for g in ldapuf.getGroups(attr = LDAP_GROUP_RDN):
+                    if __groups_prefixed__:
+                        names.append("%s%s" % (GROUP_PREFIX, g))
+                    else:
+                        names.append(g)
+        # Return a list of unique names
+        return unique(names, _list = 1)
+
+    security.declareProtected(Permissions.manage_users, "getUserIds")
+    def getUserIds(self,):
+        """
+        Return a list of all possible user atom ids in the system.
+        WARNING: Please see the id Vs. name consideration at the
+        top of this document. So, groups will be returned
+        WITH their prefix by this method
+        [NOTA: This method is time-expensive !]
+        """
+        return self.getUserNames(__groups_prefixed__ = 1)
+
+    security.declareProtected(Permissions.manage_users, "getUsers")
+    def getUsers(self, __include_groups__ = 1, __include_users__ = 1):
+        """Return a list of user and group objects.
+        In case of some UF implementations, the returned object may only be a subset
+        of all possible users.
+        In other words, you CANNOT assert that len(getUsers()) equals len(getUserNames()).
+        With cache-support UserFolders, such as LDAPUserFolder, the getUser() method will
+        return only cached user objects instead of fetching all possible users.
+        """
+        Log(LOG_DEBUG, "getUsers")
+        ret = []
+        names_set = {}
+
+        # avoid too many lookups for 'has_key' in loops
+        isUserProcessed = names_set.has_key
+
+        # Fetch groups first (then the user must be
+        # prefixed by 'group_' prefix)
+        if __include_groups__:
+            # Fetch regular groups
+            for u in self._getOb('Groups').acl_users.getUsers():
+                if not u:
+                    continue        # Ignore empty users
+
+                name = u.getId()
+                if isUserProcessed(name):
+                    continue        # Prevent double users inclusion
+
+                # Append group
+                names_set[name] = True
+                ret.append(
+                    GRUFUser.GRUFGroup(u, self, isGroup = 1, source_id = "Groups").__of__(self)
+                    )
+
+        # Fetch users then
+        if __include_users__:
+            for src in self.listUserSources():
+                for u in src.getUsers():
+                    if not u:
+                        continue        # Ignore empty users
+
+                    name = u.getId()
+                    if isUserProcessed(name):
+                        continue        # Prevent double users inclusion
+
+                    # Append user
+                    names_set[name] = True
+                    ret.append(
+                        GRUFUser.GRUFUser(u, self, source_id = src.getUserSourceId(), isGroup = 0).__of__(self)
+                        )
+
+        return tuple(ret)
+
+    security.declareProtected(Permissions.manage_users, "getUser")
+    def getUser(self, name, __include_users__ = 1, __include_groups__ = 1, __force_group_id__ = 0):
+        """
+        Return the named user object or None.
+        User have precedence over group.
+        If name is None, getUser() will return None.
+        """
+        # Basic check
+        if name is None:
+            return None
+
+        # Prevent infinite recursion when instanciating a GRUF
+        # without having sub-acl_users set
+        if not "acl_users" in self._getOb('Groups').objectIds():
+            return None
+
+        # Fetch groups first (then the user must be prefixed by 'group_' prefix)
+        if __include_groups__ and name.startswith(GROUP_PREFIX):
+            id = name[GROUP_PREFIX_LEN:]
+
+            # Fetch regular groups
+            u = self._getOb('Groups')._getGroup(id)
+            if u:
+                ret = GRUFUser.GRUFGroup(
+                    u, self, isGroup = 1, source_id = "Groups"
+                    ).__of__(self)
+                return ret              # XXX This violates precedence
+
+        # Fetch users then
+        if __include_users__:
+            for src in self.listUserSources():
+                u = src.getUser(name)
+                if u:
+                    ret = GRUFUser.GRUFUser(u, self, source_id = src.getUserSourceId(), isGroup = 0).__of__(self)
+                    return ret
+
+        # Then desperatly try to fetch groups (without beeing prefixed by 'group_' prefix)
+        if __include_groups__ and (not __force_group_id__):
+            u = self._getOb('Groups')._getGroup(name)
+            if u:
+                ret = GRUFUser.GRUFGroup(u, self, isGroup = 1, source_id = "Groups").__of__(self)
+                return ret
+
+        return None
+
+
+    security.declareProtected(Permissions.manage_users, "getUserById")
+    def getUserById(self, id, default=_marker):
+        """Return the user atom corresponding to the given id. Can return groups.
+        """
+        ret = self.getUser(id, __force_group_id__ = 1)
+        if not ret:
+            if default is _marker:
+                return None
+            ret = default
+        return ret
+
+
+    security.declareProtected(Permissions.manage_users, "getUserByName")
+    def getUserByName(self, name, default=_marker):
+        """Same as getUser() but works with a name instead of an id.
+        [NOTA: Theorically, the id is a handle, while the name is the actual login name.
+        But difference between a user id and a user name is unsignificant in
+        all current User Folder implementations... except for GROUPS.]
+        """
+        # Try to fetch a user first
+        usr = self.getUser(name)
+
+        # If not found, try to fetch a group by appending the prefix
+        if not usr:
+            name = "%s%s" % (GROUP_PREFIX, name)
+            usr = self.getUserById(name, default)
+
+        return usr
+
+    security.declareProtected(Permissions.manage_users, "getPureUserNames")
+    def getPureUserNames(self, ):
+        """Fetch the list of actual users from GRUFUsers.
+        """
+        return self.getUserNames(__include_groups__ = 0)
+
+
+    security.declareProtected(Permissions.manage_users, "getPureUserIds")
+    def getPureUserIds(self,):
+        """Same as getUserIds() but without groups
+        """
+        return self.getUserNames(__include_groups__ = 0)
+
+    security.declareProtected(Permissions.manage_users, "getPureUsers")
+    def getPureUsers(self):
+        """Return a list of pure user objects.
+        """
+        return self.getUsers(__include_groups__ = 0)
+
+    security.declareProtected(Permissions.manage_users, "getPureUser")
+    def getPureUser(self, id, ):
+        """Return the named user object or None"""
+        # Performance tricks
+        if not id:
+            return None
+
+        # Fetch it
+        return self.getUser(id, __include_groups__ = 0)
+
+
+    security.declareProtected(Permissions.manage_users, "getGroupNames")
+    def getGroupNames(self, ):
+        """Same as getUserNames() but without pure users.
+        """
+        return self.getUserNames(__include_users__ = 0, __groups_prefixed__ = 0)
+
+    security.declareProtected(Permissions.manage_users, "getGroupIds")
+    def getGroupIds(self, ):
+        """Same as getUserNames() but without pure users.
+        """
+        return self.getUserNames(__include_users__ = 0, __groups_prefixed__ = 1)
+
+    security.declareProtected(Permissions.manage_users, "getGroups")
+    def getGroups(self):
+        """Same as getUsers() but without pure users.
+        """
+        return self.getUsers(__include_users__ = 0)
+
+    security.declareProtected(Permissions.manage_users, "getGroup")
+    def getGroup(self, name, prefixed = 1):
+        """Return the named user object or None"""
+        # Performance tricks
+        if not name:
+            return None
+
+        # Unprefix group name
+        if not name.startswith(GROUP_PREFIX):
+            name = "%s%s" % (GROUP_PREFIX, name, )
+
+        # Fetch it
+        return self.getUser(name, __include_users__ = 0)
+
+    security.declareProtected(Permissions.manage_users, "getGroupById")
+    def getGroupById(self, id, default = _marker):
+        """Same as getUserById(id) but forces returning a group.
+        """
+        ret = self.getUser(id, __include_users__ = 0, __force_group_id__ = 1)
+        if not ret:
+            if default is _marker:
+                return None
+            ret = default
+        return ret
+
+    security.declareProtected(Permissions.manage_users, "getGroupByName")
+    def getGroupByName(self, name, default = _marker):
+        """Same as getUserByName(name) but forces returning a group.
+        """
+        ret = self.getUser(name, __include_users__ = 0, __force_group_id__ = 0)
+        if not ret:
+            if default is _marker:
+                return None
+            ret = default
+        return ret
+
+
+
+    #                                                                           #
+    #                              REGULAR MUTATORS                             #
+    #                                                                           #
+
+    security.declareProtected(Permissions.manage_users, "userFolderAddUser")
+    def userFolderAddUser(self, name, password, roles, domains, groups = (),
+                          REQUEST=None, **kw):
+        """API method for creating a new user object. Note that not all
+        user folder implementations support dynamic creation of user
+        objects.
+        """
+        return self._doAddUser(name, password, roles, domains, groups, **kw)
+    userFolderAddUser = postonly(userFolderAddUser)
+
+    security.declareProtected(Permissions.manage_users, "userFolderEditUser")
+    def userFolderEditUser(self, name, password, roles, domains, groups = None,
+                           REQUEST=None, **kw):
+        """API method for changing user object attributes. Note that not
+        all user folder implementations support changing of user object
+        attributes.
+        Arguments ARE required.
+        """
+        return self._doChangeUser(name, password, roles, domains, groups, **kw)
+    userFolderEditUser = postonly(userFolderEditUser)
+
+    security.declareProtected(Permissions.manage_users, "userFolderUpdateUser")
+    def userFolderUpdateUser(self, name, password = None, roles = None,
+                             domains = None, groups = None, REQUEST=None, **kw):
+        """API method for changing user object attributes. Note that not
+        all user folder implementations support changing of user object
+        attributes.
+        Arguments are optional"""
+        return self._updateUser(name, password, roles, domains, groups, **kw)
+    userFolderUpdateUser = postonly(userFolderUpdateUser)
+
+    security.declareProtected(Permissions.manage_users, "userFolderDelUsers")
+    def userFolderDelUsers(self, names, REQUEST=None):
+        """API method for deleting one or more user atom objects. Note that not
+        all user folder implementations support deletion of user objects."""
+        return self._doDelUsers(names)
+    userFolderDelUsers = postonly(userFolderDelUsers)
+
+    security.declareProtected(Permissions.manage_users, "userFolderAddGroup")
+    def userFolderAddGroup(self, name, roles, groups = (), REQUEST=None, **kw):
+        """API method for creating a new group.
+        """
+        while name.startswith(GROUP_PREFIX):
+            name = name[GROUP_PREFIX_LEN:]
+        return self._doAddGroup(name, roles, groups, **kw)
+    userFolderAddGroup = postonly(userFolderAddGroup)
+
+    security.declareProtected(Permissions.manage_users, "userFolderEditGroup")
+    def userFolderEditGroup(self, name, roles, groups = None, REQUEST=None,
+                            **kw):
+        """API method for changing group object attributes.
+        """
+        return self._doChangeGroup(name, roles = roles, groups = groups, **kw)
+    userFolderEditGroup = postonly(userFolderEditGroup)
+
+    security.declareProtected(Permissions.manage_users, "userFolderUpdateGroup")
+    def userFolderUpdateGroup(self, name, roles = None, groups = None,
+                              REQUEST=None, **kw):
+        """API method for changing group object attributes.
+        """
+        return self._updateGroup(name, roles = roles, groups = groups, **kw)
+    userFolderUpdateGroup = postonly(userFolderUpdateGroup)
+
+    security.declareProtected(Permissions.manage_users, "userFolderDelGroups")
+    def userFolderDelGroups(self, names, REQUEST=None):
+        """API method for deleting one or more group objects.
+        Implem. note : All ids must be prefixed with 'group_',
+        so this method ends up beeing only a filter of non-prefixed ids
+        before calling userFolderDelUsers().
+        """
+        return self._doDelGroups(names)
+    userFolderDelUsers = postonly(userFolderDelUsers)
+
+
+
+    #                                                                           #
+    #                               SEARCH METHODS                              #
+    #                                                                           #
+
+
+    security.declareProtected(Permissions.manage_users, "searchUsersByAttribute")
+    def searchUsersByAttribute(self, attribute, search_term):
+        """Return user ids whose 'attribute' match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying user folder:
+        it may return all users, return only cached users (for LDAPUF) or return no users.
+        This will return all users whose name contains search_term (whaterver its case).
+        THIS METHOD MAY BE VERY EXPENSIVE ON USER FOLDER KINDS WHICH DO NOT PROVIDE A
+        SEARCHING METHOD (ie. every UF kind except LDAPUF).
+        'attribute' can be 'id' or 'name' for all UF kinds, or anything else for LDAPUF.
+        """
+        ret = []
+        for src in self.listUserSources():
+            # Use source-specific search methods if available
+            if hasattr(src.aq_base, "findUser"):
+                # LDAPUF
+                Log(LOG_DEBUG, "We use LDAPUF to find users")
+                id_attr = src._uid_attr
+                if attribute == 'name':
+                    attr = src._login_attr
+                elif attribute == 'id':
+                    attr = src._uid_attr
+                else:
+                    attr = attribute
+                Log(LOG_DEBUG, "we use findUser", attr, search_term, )
+                users = src.findUser(attr, search_term, exact_match = True)
+                ret.extend(
+                    [ u[id_attr] for u in users ],
+                    )
+            else:
+                # Other types of user folder
+                search_term = search_term.lower()
+
+                # Find the proper method according to the attribute type
+                if attribute == "name":
+                    method = "getName"
+                elif attribute == "id":
+                    method = "getId"
+                else:
+                    raise NotImplementedError, "Attribute searching is only supported for LDAPUserFolder by now."
+
+                # Actually search
+                src_id = src.getUserSourceId()
+                for u in src.getUsers():
+                    if not u:
+                        continue
+                    u = GRUFUser.GRUFUser(u, self, source_id=src_id,
+                                          isGroup=0).__of__(self)
+                    s = getattr(u, method)().lower()
+                    if string.find(s, search_term) != -1:
+                        ret.append(u.getId())
+        Log(LOG_DEBUG, "We've found them:", ret)
+        return ret
+
+    security.declareProtected(Permissions.manage_users, "searchUsersByName")
+    def searchUsersByName(self, search_term):
+        """Return user ids whose name match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying user folder:
+        it may return all users, return only cached users (for LDAPUF) or return no users.
+        This will return all users whose name contains search_term (whaterver its case).
+        THIS METHOD MAY BE VERY EXPENSIVE ON USER FOLDER KINDS WHICH DO NOT PROVIDE A
+        SEARCHING METHOD (ie. every UF kind except LDAPUF)
+        """
+        return self.searchUsersByAttribute("name", search_term)
+
+    security.declareProtected(Permissions.manage_users, "searchUsersById")
+    def searchUsersById(self, search_term):
+        """Return user ids whose id match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying user folder:
+        it may return all users, return only cached users (for LDAPUF) or return no users.
+        This will return all users whose name contains search_term (whaterver its case).
+        THIS METHOD MAY BE VERY EXPENSIVE ON USER FOLDER KINDS WHICH DO NOT PROVIDE A
+        SEARCHING METHOD (ie. every UF kind except LDAPUF)
+        """
+        return self.searchUsersByAttribute("id", search_term)
+
+
+    security.declareProtected(Permissions.manage_users, "searchGroupsByAttribute")
+    def searchGroupsByAttribute(self, attribute, search_term):
+        """Return group ids whose 'attribute' match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying group folder:
+        it may return all groups, return only cached groups (for LDAPUF) or return no groups.
+        This will return all groups whose name contains search_term (whaterver its case).
+        THIS METHOD MAY BE VERY EXPENSIVE ON GROUP FOLDER KINDS WHICH DO NOT PROVIDE A
+        SEARCHING METHOD (ie. every UF kind except LDAPUF).
+        'attribute' can be 'id' or 'name' for all UF kinds, or anything else for LDAPUF.
+        """
+        ret = []
+        src = self.Groups
+
+        # Use source-specific search methods if available
+        if hasattr(src.aq_base, "findGroup"):
+            # LDAPUF
+            id_attr = src._uid_attr
+            if attribute == 'name':
+                attr = src._login_attr
+            elif attribute == 'id':
+                attr = src._uid_attr
+            else:
+                attr = attribute
+            groups = src.findGroup(attr, search_term)
+            ret.extend(
+                [ u[id_attr] for u in groups ],
+                )
+        else:
+            # Other types of group folder
+            search_term = search_term.lower()
+
+            # Find the proper method according to the attribute type
+            if attribute == "name":
+                method = "getName"
+            elif attribute == "id":
+                method = "getId"
+            else:
+                raise NotImplementedError, "Attribute searching is only supported for LDAPGroupFolder by now."
+
+            # Actually search
+            for u in self.getGroups():
+                s = getattr(u, method)().lower()
+                if string.find(s, search_term) != -1:
+                    ret.append(u.getId())
+        return ret
+
+    security.declareProtected(Permissions.manage_users, "searchGroupsByName")
+    def searchGroupsByName(self, search_term):
+        """Return group ids whose name match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying group folder:
+        it may return all groups, return only cached groups (for LDAPUF) or return no groups.
+        This will return all groups whose name contains search_term (whaterver its case).
+        THIS METHOD MAY BE VERY EXPENSIVE ON GROUP FOLDER KINDS WHICH DO NOT PROVIDE A
+        SEARCHING METHOD (ie. every UF kind except LDAPUF)
+        """
+        return self.searchGroupsByAttribute("name", search_term)
+
+    security.declareProtected(Permissions.manage_users, "searchGroupsById")
+    def searchGroupsById(self, search_term):
+        """Return group ids whose id match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying group folder:
+        it may return all groups, return only cached groups (for LDAPUF) or return no groups.
+        This will return all groups whose name contains search_term (whaterver its case).
+        THIS METHOD MAY BE VERY EXPENSIVE ON GROUP FOLDER KINDS WHICH DO NOT PROVIDE A
+        SEARCHING METHOD (ie. every UF kind except LDAPUF)
+        """
+        return self.searchGroupsByAttribute("id", search_term)
+
+    #                                                                           #
+    #                         SECURITY MANAGEMENT METHODS                       #
+    #                                                                           #
+
+    security.declareProtected(Permissions.manage_users, "setRolesOnUsers")
+    def setRolesOnUsers(self, roles, userids, REQUEST = None):
+        """Set a common set of roles for a bunch of user atoms.
+        """
+        for usr in userids:
+            self.userSetRoles(usr, roles)
+    setRolesOnUsers = postonly(setRolesOnUsers)
+
+##    def setUsersOfRole(self, usernames, role):
+##        """Sets the users of a role.
+##        XXX THIS METHOD SEEMS TO BE SEAMLESS.
+##        """
+##        raise NotImplementedError, "Not implemented."
+
+    security.declareProtected(Permissions.manage_users, "getUsersOfRole")
+    def getUsersOfRole(self, role, object = None):
+        """Gets the user (and group) ids having the specified role...
+        ...on the specified Zope object if it's not None
+        ...on their own information if the object is None.
+        NOTA: THIS METHOD IS VERY EXPENSIVE.
+        XXX PERFORMANCES HAVE TO BE IMPROVED
+        """
+        ret = []
+        for id in self.getUserIds():
+            if role in self.getRolesOfUser(id):
+                ret.append(id)
+        return tuple(ret)
+
+    security.declarePublic("getRolesOfUser")
+    def getRolesOfUser(self, userid):
+        """Alias for user.getRoles()
+        """
+        return self.getUserById(userid).getRoles()
+
+    security.declareProtected(Permissions.manage_users, "userFolderAddRole")
+    def userFolderAddRole(self, role, REQUEST=None):
+        """Add a new role. The role will be appended, in fact, in GRUF's surrounding folder.
+        """
+        if role in self.aq_parent.valid_roles():
+            raise ValueError, "Role '%s' already exist" % (role, )
+
+        return self.aq_parent._addRole(role)
+    userFolderAddRole = postonly(userFolderAddRole)
+
+    security.declareProtected(Permissions.manage_users, "userFolderDelRoles")
+    def userFolderDelRoles(self, roles, REQUEST=None):
+        """Delete roles.
+        The removed roles will be removed from the UserFolder's users and groups as well,
+        so this method can be very time consuming with a large number of users.
+        """
+        # Check that roles exist
+        ud_roles = self.aq_parent.userdefined_roles()
+        for r in roles:
+            if not r in ud_roles:
+                raise ValueError, "Role '%s' is not defined on acl_users' parent folder" % (r, )
+
+        # Remove role on all users
+        for r in roles:
+            for u in self.getUsersOfRole(r, ):
+                self.userRemoveRole(u, r, )
+
+        # Actually remove role
+        return self.aq_parent._delRoles(roles, None)
+    userFolderDelRoles = postonly(userFolderDelRoles)
+
+    security.declarePublic("userFolderGetRoles")
+    def userFolderGetRoles(self, ):
+        """
+        userFolderGetRoles(self,) => tuple of strings
+        List the roles defined at the top of GRUF's folder.
+        This includes both user-defined roles and default roles.
+        """
+        return tuple(self.aq_parent.valid_roles())
+
+
+    # Groups support
+
+    security.declareProtected(Permissions.manage_users, "setMembers")
+    def setMembers(self, groupid, userids, REQUEST=None):
+        """Set the members of the group
+        """
+        self.getGroup(groupid).setMembers(userids)
+    setMembers = postonly(setMembers)
+
+    security.declareProtected(Permissions.manage_users, "addMember")
+    def addMember(self, groupid, userid, REQUEST=None):
+        """Add a member to a group
+        """
+        return self.getGroup(groupid).addMember(userid)
+    addMember = postonly(addMember)
+
+    security.declareProtected(Permissions.manage_users, "removeMember")
+    def removeMember(self, groupid, userid, REQUEST=None):
+        """Remove a member from a group.
+        """
+        return self.getGroup(groupid).removeMember(userid)
+    removeMember = postonly(removeMember)
+
+    security.declareProtected(Permissions.manage_users, "getMemberIds")
+    def getMemberIds(self, groupid):
+        """Return the list of member ids (groups and users) in this group
+        """
+        m = self.getGroup(groupid)
+        if not m:
+            raise ValueError, "Invalid group: '%s'" % groupid
+        return self.getGroup(groupid).getMemberIds()
+
+    security.declareProtected(Permissions.manage_users, "getUserMemberIds")
+    def getUserMemberIds(self, groupid):
+        """Return the list of member ids (groups and users) in this group
+        """
+        return self.getGroup(groupid).getUserMemberIds()
+
+    security.declareProtected(Permissions.manage_users, "getGroupMemberIds")
+    def getGroupMemberIds(self, groupid):
+        """Return the list of member ids (groups and users) in this group
+        XXX THIS MAY BE VERY EXPENSIVE !
+        """
+        return self.getGroup(groupid).getGroupMemberIds()
+
+    security.declareProtected(Permissions.manage_users, "hasMember")
+    def hasMember(self, groupid, id):
+        """Return true if the specified atom id is in the group.
+        This is the contrary of IUserAtom.isInGroup(groupid).
+        THIS CAN BE VERY EXPENSIVE
+        """
+        return self.getGroup(groupid).hasMember(id)
+
+
+    # User mutation
+
+##    def setUserId(id, newId):
+##        """Change id of a user atom.
+##        """
+
+##    def setUserName(id, newName):
+##        """Change the name of a user atom.
+##        """
+
+    security.declareProtected(Permissions.manage_users, "userSetRoles")
+    def userSetRoles(self, id, roles, REQUEST=None):
+        """Change the roles of a user atom.
+        """
+        self._updateUser(id, roles = roles)
+    userSetRoles = postonly(userSetRoles)
+
+    security.declareProtected(Permissions.manage_users, "userAddRole")
+    def userAddRole(self, id, role, REQUEST=None):
+        """Append a role for a user atom
+        """
+        roles = list(self.getUser(id).getRoles())
+        if not role in roles:
+            roles.append(role)
+            self._updateUser(id, roles = roles)
+    userAddRole = postonly(userAddRole)
+
+    security.declareProtected(Permissions.manage_users, "userRemoveRole")
+    def userRemoveRole(self, id, role, REQUEST=None):
+        """Remove the role of a user atom. Will NOT complain if role doesn't exist
+        """
+        roles = list(self.getRolesOfUser(id))
+        if role in roles:
+            roles.remove(role)
+            self._updateUser(id, roles = roles)
+    userRemoveRole = postonly(userRemoveRole)
+
+    security.declareProtected(Permissions.manage_users, "userSetPassword")
+    def userSetPassword(self, id, newPassword, REQUEST=None):
+        """Set the password of a user
+        """
+        u = self.getPureUser(id)
+        if not u:
+            raise ValueError, "Invalid pure user id: '%s'" % (id,)
+        self._updateUser(u.getId(), password = newPassword, )
+    userSetPassword = postonly(userSetPassword)
+
+    security.declareProtected(Permissions.manage_users, "userGetDomains")
+    def userGetDomains(self, id):
+        """get domains for a user
+        """
+        usr = self.getPureUser(id)
+        return tuple(usr.getDomains())
+
+    security.declareProtected(Permissions.manage_users, "userSetDomains")
+    def userSetDomains(self, id, domains, REQUEST=None):
+        """Set domains for a user
+        """
+        usr = self.getPureUser(id)
+        self._updateUser(usr.getId(), domains = domains, )
+    userSetDomains = postonly(userSetDomains)
+
+    security.declareProtected(Permissions.manage_users, "userAddDomain")
+    def userAddDomain(self, id, domain, REQUEST=None):
+        """Append a domain to a user
+        """
+        usr = self.getPureUser(id)
+        domains = list(usr.getDomains())
+        if not domain in domains:
+            roles.append(domain)
+            self._updateUser(usr.getId(), domains = domains, )
+    userAddDomain = postonly(userAddDomain)
+
+    security.declareProtected(Permissions.manage_users, "userRemoveDomain")
+    def userRemoveDomain(self, id, domain, REQUEST=None):
+        """Remove a domain from a user
+        """
+        usr = self.getPureUser(id)
+        domains = list(usr.getDomains())
+        if not domain in domains:
+            raise ValueError, "User '%s' doesn't have domain '%s'" % (id, domain, )
+        while domain in domains:
+            roles.remove(domain)
+        self._updateUser(usr.getId(), domains = domains)
+    userRemoveDomain = postonly(userRemoveDomain)
+
+    security.declareProtected(Permissions.manage_users, "userSetGroups")
+    def userSetGroups(self, id, groupnames, REQUEST=None):
+        """Set the groups of a user
+        """
+        self._updateUser(id, groups = groupnames)
+    userSetGroups = postonly(userSetGroups)
+
+    security.declareProtected(Permissions.manage_users, "userAddGroup")
+    def userAddGroup(self, id, groupname, REQUEST=None):
+        """add a group to a user atom
+        """
+        groups = list(self.getUserById(id).getGroups())
+        if not groupname in groups:
+            groups.append(groupname)
+            self._updateUser(id, groups = groups)
+    userAddGroup = postonly(userAddGroup)
+
+
+    security.declareProtected(Permissions.manage_users, "userRemoveGroup")
+    def userRemoveGroup(self, id, groupname, REQUEST=None):
+        """remove a group from a user atom.
+        """
+        groups = list(self.getUserById(id).getGroupNames())
+        if groupname.startswith(GROUP_PREFIX):
+            groupname = groupname[GROUP_PREFIX_LEN:]
+        if groupname in groups:
+            groups.remove(groupname)
+            self._updateUser(id, groups = groups)
+    userRemoveGroup = postonly(userRemoveGroup)
+
+
+    #                                                                           #
+    #                             VARIOUS OPERATIONS                            #
+    #                                                                           #
+
+    def __init__(self):
+        """
+        __init__(self) -> initialization method
+        We define it to prevend calling ancestor's __init__ methods.
+        """
+        pass
+
+
+    security.declarePrivate('_post_init')
+    def _post_init(self):
+        """
+        _post_init(self) => meant to be called when the
+                            object is in the Zope tree
+        """
+        uf = GRUFFolder.GRUFUsers()
+        gf = GRUFFolder.GRUFGroups()
+        self._setObject('Users', uf)
+        self._setObject('Groups', gf)
+        self.id = "acl_users"
+
+    def manage_beforeDelete(self, item, container):
+        """
+        Special overloading for __allow_groups__ attribute
+        """
+        if item is self:
+            try:
+                del container.__allow_groups__
+            except:
+                pass
+
+    def manage_afterAdd(self, item, container):
+        """Same
+        """
+        if item is self:
+            container.__allow_groups__ = aq_base(self)
+
+    #                                                                   #
+    #                           VARIOUS UTILITIES                       #
+    #                                                                   #
+    # These methods shouldn't be used directly for most applications,   #
+    # but they might be useful for some special processing.             #
+    #                                                                   #
+
+    security.declarePublic('getGroupPrefix')
+    def getGroupPrefix(self):
+        """ group prefix """
+        return GROUP_PREFIX
+
+    security.declarePrivate('getGRUFPhysicalRoot')
+    def getGRUFPhysicalRoot(self,):
+        # $$$ trick meant to be used within
+        # fake_getPhysicalRoot (see __init__)
+        return self.getPhysicalRoot()
+
+    security.declareProtected(Permissions.view, 'getGRUFId')
+    def getGRUFId(self,):
+        """
+        Alias to self.getId()
+        """
+        return self.getId()
+
+    security.declareProtected(Permissions.manage_users, "getUnwrappedUser")
+    def getUnwrappedUser(self, name):
+        """
+        getUnwrappedUser(self, name) => user object or None
+
+        This method is used to get a User object directly from the User's
+        folder acl_users, without wrapping it with group information.
+
+        This is useful for UserFolders that define additional User classes,
+        when you want to call specific methods on these user objects.
+
+        For example, LDAPUserFolder defines a 'getProperty' method that's
+        not inherited from the standard User object. You can, then, use
+        the getUnwrappedUser() to get the matching user and call this
+        method.
+        """
+        src_id = self.getUser(name).getUserSourceId()
+        return self.getUserSource(src_id).getUser(name)
+
+    security.declareProtected(Permissions.manage_users, "getUnwrappedGroup")
+    def getUnwrappedGroup(self, name):
+        """
+        getUnwrappedGroup(self, name) => user object or None
+
+        Same as getUnwrappedUser but for groups.
+        """
+        return self.Groups.acl_users.getUser(name)
+
+    #                                                                           #
+    #                        AUTHENTICATION INTERFACE                           #
+    #                                                                           #
+
+    security.declarePrivate("authenticate")
+    def authenticate(self, name, password, request):
+        """
+        Pass the request along to the underlying user-related UserFolder
+        object
+        THIS METHOD RETURNS A USER OBJECT OR NONE, as specified in the code
+        in AccessControl/User.py.
+        We also check for inituser in there.
+        """
+        # Emergency user checking stuff
+        emergency = self._emergency_user
+        if emergency and name == emergency.getUserName():
+            if emergency.authenticate(password, request):
+                return emergency
+            else:
+                return None
+
+        # Usual GRUF authentication
+        for src in self.listUserSources():
+            # XXX We can imagine putting a try/except here to "ignore"
+            # UF errors such as SQL or LDAP shutdown
+            u = src.authenticate(name, password, request)
+            if u:
+                return GRUFUser.GRUFUser(u, self, isGroup = 0, source_id = src.getUserSourceId()).__of__(self)
+
+        # No acl_users in the Users folder or no user authenticated
+        # => we refuse authentication
+        return None
+
+
+
+
+    #                                                                           #
+    #                               GRUF'S GUTS :-)                             #
+    #                                                                           #
+
+    security.declarePrivate("_doAddUser")
+    def _doAddUser(self, name, password, roles, domains, groups = (), **kw):
+        """
+        Create a new user. This should be implemented by subclasses to
+        do the actual adding of a user. The 'password' will be the
+        original input password, unencrypted. The implementation of this
+        method is responsible for performing any needed encryption.
+        """
+        prefix = GROUP_PREFIX
+
+        # Prepare groups
+        roles = list(roles)
+        gruf_groups = self.getGroupIds()
+        for group in groups:
+            if not group.startswith(prefix):
+                group = "%s%s" % (prefix, group, )
+            if not group in gruf_groups:
+                raise ValueError, "Invalid group: '%s'" % (group, )
+            roles.append(group)
+
+        # Reset the users overview batch
+        self._v_batch_users = []
+
+        # Really add users
+        return self.getDefaultUserSource()._doAddUser(
+            name,
+            password,
+            roles,
+            domains,
+            **kw)
+
+    security.declarePrivate("_doChangeUser")
+    def _doChangeUser(self, name, password, roles, domains, groups = None, **kw):
+        """
+        Modify an existing user. This should be implemented by subclasses
+        to make the actual changes to a user. The 'password' will be the
+        original input password, unencrypted. The implementation of this
+        method is responsible for performing any needed encryption.
+
+        A None password should not change it (well, we hope so)
+        """
+        # Get actual user name and id
+        usr = self.getUser(name)
+        if usr is None:
+            raise ValueError, "Invalid user: '%s'" % (name,)
+        id = usr.getRealId()
+
+        # Don't lose existing groups
+        if groups is None:
+            groups = usr.getGroups()
+
+        roles = list(roles)
+        groups = list(groups)
+
+        # Change groups affectation
+        cur_groups = self.getGroups()
+        given_roles = tuple(usr.getRoles()) + tuple(roles)
+        for group in groups:
+            if not group.startswith(GROUP_PREFIX, ):
+                group = "%s%s" % (GROUP_PREFIX, group, )
+            if not group in cur_groups and not group in given_roles:
+                roles.append(group)
+
+        # Reset the users overview batch
+        self._v_batch_users = []
+
+        # Change the user itself
+        src = usr.getUserSourceId()
+        Log(LOG_NOTICE, name, "Source:", src)
+        ret = self.getUserSource(src)._doChangeUser(
+            id, password, roles, domains, **kw)
+
+        # Invalidate user cache if necessary
+        usr.clearCachedGroupsAndRoles()
+        authenticated = getSecurityManager().getUser()
+        if id == authenticated.getId() and hasattr(authenticated, 'clearCachedGroupsAndRoles'):
+            authenticated.clearCachedGroupsAndRoles(self.getUserSource(src).getUser(id))
+
+        return ret
+
+    security.declarePrivate("_updateUser")
+    def _updateUser(self, id, password = None, roles = None, domains = None, groups = None):
+        """
+        _updateUser(self, id, password = None, roles = None, domains = None, groups = None)
+
+        This one should work for users AND groups.
+
+        Front-end to _doChangeUser, but with a better default value support.
+        We guarantee that None values will let the underlying UF keep the original ones.
+        This is not true for the password: some buggy UF implementation may not
+        handle None password correctly :-(
+        """
+        # Get the former values if necessary. Username must be valid !
+        usr = self.getUser(id)
+        if roles is None:
+            # Remove invalid roles and group names
+            roles = usr._original_roles
+            roles = filter(lambda x: not x.startswith(GROUP_PREFIX), roles)
+            roles = filter(lambda x: x not in ('Anonymous', 'Authenticated', 'Shared', ''), roles)
+        else:
+            # Check if roles are valid
+            roles = filter(lambda x: x not in ('Anonymous', 'Authenticated', 'Shared', ''), roles)
+            vr = self.userFolderGetRoles()
+            for r in roles:
+                if not r in vr:
+                    raise ValueError, "Invalid or inexistant role: '%s'." % (r, )
+        if domains is None:
+            domains = usr._original_domains
+        if groups is None:
+            groups = usr.getGroups(no_recurse = 1)
+        else:
+            # Check if given groups are valid
+            glist = self.getGroupNames()
+            glist.extend(map(lambda x: "%s%s" % (GROUP_PREFIX, x), glist))
+            for g in groups:
+                if not g in glist:
+                    raise ValueError, "Invalid group: '%s'" % (g, )
+
+        # Reset the users overview batch
+        self._v_batch_users = []
+
+        # Change the user
+        return self._doChangeUser(id, password, roles, domains, groups)
+
+    security.declarePrivate("_doDelUsers")
+    def _doDelUsers(self, names):
+        """
+        Delete one or more users. This should be implemented by subclasses
+        to do the actual deleting of users.
+        This won't delete groups !
+        """
+        # Collect information about user sources
+        sources = {}
+        for name in names:
+            usr = self.getUser(name, __include_groups__ = 0)
+            if not usr:
+                continue        # Ignore invalid user names
+            src = usr.getUserSourceId()
+            if not sources.has_key(src):
+                sources[src] = []
+            sources[src].append(name)
+        for src, names in sources.items():
+            self.getUserSource(src)._doDelUsers(names)
+
+        # Reset the users overview batch
+        self._v_batch_users = []
+
+
+    #                                   #
+    #           Groups interface        #
+    #                                   #
+
+    security.declarePrivate("_doAddGroup")
+    def _doAddGroup(self, name, roles, groups = (), **kw):
+        """
+        Create a new group. Password will be randomly created, and domain will be None.
+        Supports nested groups.
+        """
+        # Prepare initial data
+        domains = ()
+        password = ""
+        if roles is None:
+            roles = []
+        if groups is None:
+            groups = []
+
+        for x in range(0, 10):  # Password will be 10 chars long
+            password = "%s%s" % (password, random.choice(string.lowercase), )
+
+        # Compute roles
+        roles = list(roles)
+        prefix = GROUP_PREFIX
+        gruf_groups = self.getGroupIds()
+        for group in groups:
+            if not group.startswith(prefix):
+                group = "%s%s" % (prefix, group, )
+            if group == "%s%s" % (prefix, name, ):
+                raise ValueError, "Infinite recursion for group '%s'." % (group, )
+            if not group in gruf_groups:
+                raise ValueError, "Invalid group: '%s' (defined groups are %s)" % (group, gruf_groups)
+            roles.append(group)
+
+        # Reset the users overview batch
+        self._v_batch_users = []
+
+        # Actual creation
+        return self.Groups.acl_users._doAddUser(
+            name, password, roles, domains, **kw
+            )
+
+    security.declarePrivate("_doChangeGroup")
+    def _doChangeGroup(self, name, roles, groups = None, **kw):
+        """Modify an existing group."""
+        # Remove prefix if given
+        if name.startswith(self.getGroupPrefix()):
+            name = name[GROUP_PREFIX_LEN:]
+
+        # Check if group exists
+        grp = self.getGroup(name, prefixed = 0)
+        if grp is None:
+            raise ValueError, "Invalid group: '%s'" % (name,)
+
+        # Don't lose existing groups
+        if groups is None:
+            groups = grp.getGroups()
+
+        roles = list(roles or [])
+        groups = list(groups or [])
+
+        # Change groups affectation
+        cur_groups = self.getGroups()
+        given_roles = tuple(grp.getRoles()) + tuple(roles)
+        for group in groups:
+            if not group.startswith(GROUP_PREFIX, ):
+                group = "%s%s" % (GROUP_PREFIX, group, )
+            if group == "%s%s" % (GROUP_PREFIX, grp.id):
+                raise ValueError, "Cannot affect group '%s' to itself!" % (name, )        # Prevent direct inclusion of self
+            new_grp = self.getGroup(group)
+            if not new_grp:
+                raise ValueError, "Invalid or inexistant group: '%s'" % (group, )
+            if "%s%s" % (GROUP_PREFIX, grp.id) in new_grp.getGroups():
+                raise ValueError, "Cannot affect %s to group '%s' as it would lead to circular references." % (group, name, )        # Prevent indirect inclusion of self
+            if not group in cur_groups and not group in given_roles:
+                roles.append(group)
+
+        # Reset the users overview batch
+        self._v_batch_users = []
+
+        # Perform the change
+        domains = ""
+        password = ""
+        for x in range(0, 10):  # Password will be 10 chars long
+            password = "%s%s" % (password, random.choice(string.lowercase), )
+        return self.Groups.acl_users._doChangeUser(name, password,
+                                                  roles, domains, **kw)
+
+    security.declarePrivate("_updateGroup")
+    def _updateGroup(self, name, roles = None, groups = None):
+        """
+        _updateGroup(self, name, roles = None, groups = None)
+
+        Front-end to _doChangeUser, but with a better default value support.
+        We guarantee that None values will let the underlying UF keep the original ones.
+        This is not true for the password: some buggy UF implementation may not
+        handle None password correctly but we do not care for Groups.
+
+        group name can be prefixed or not
+        """
+        # Remove prefix if given
+        if name.startswith(self.getGroupPrefix()):
+            name = name[GROUP_PREFIX_LEN:]
+
+        # Get the former values if necessary. Username must be valid !
+        usr = self.getGroup(name, prefixed = 0)
+        if roles is None:
+            # Remove invalid roles and group names
+            roles = usr._original_roles
+            roles = filter(lambda x: not x.startswith(GROUP_PREFIX), roles)
+            roles = filter(lambda x: x not in ('Anonymous', 'Authenticated', 'Shared'), roles)
+        if groups is None:
+            groups = usr.getGroups(no_recurse = 1)
+
+        # Reset the users overview batch
+        self._v_batch_users = []
+
+        # Change the user
+        return self._doChangeGroup(name, roles, groups)
+
+
+    security.declarePrivate("_doDelGroup")
+    def _doDelGroup(self, name):
+        """Delete one user."""
+        # Remove prefix if given
+        if name.startswith(self.getGroupPrefix()):
+            name = name[GROUP_PREFIX_LEN:]
+
+        # Reset the users overview batch
+        self._v_batch_users = []
+
+        # Delete it
+        return self.Groups.acl_users._doDelUsers([name])
+
+    security.declarePrivate("_doDelGroups")
+    def _doDelGroups(self, names):
+        """Delete one or more users."""
+        for group in names:
+            if not self.getGroupByName(group, None):
+                continue        # Ignore invalid groups
+            self._doDelGroup(group)
+
+
+
+
+    #                                           #
+    #      Pretty Management form methods       #
+    #                                           #
+
+
+    security.declarePublic('getGRUFVersion')
+    def getGRUFVersion(self,):
+        """
+        getGRUFVersion(self,) => Return human-readable GRUF version as a string.
+        """
+        rev_date = "$Date: 2007-04-01 17:13:44 +0200 (dim, 01 avr 2007) $"[7:-2]
+        return "%s / Revised %s" % (version__, rev_date)
+
+
+    reset_entry = "__None__"            # Special entry used for reset
+
+    security.declareProtected(Permissions.manage_users, "changeUser")
+    def changeUser(self, user, groups = [], roles = [], REQUEST = {}, ):
+        """
+        changeUser(self, user, groups = [], roles = [], REQUEST = {}, ) => used in ZMI
+        """
+        obj = self.getUser(user)
+        if obj.isGroup():
+            self._updateGroup(name = user, groups = groups, roles = roles, )
+        else:
+            self._updateUser(id = user, groups = groups, roles = roles, )
+
+
+        if REQUEST.has_key('RESPONSE'):
+            return REQUEST.RESPONSE.redirect(self.absolute_url() + "/" + obj.getId() + "/manage_workspace?FORCE_USER=1")
+    changeUser = postonly(changeUser)
+
+    security.declareProtected(Permissions.manage_users, "deleteUser")
+    def deleteUser(self, user, REQUEST = {}, ):
+        """
+        deleteUser(self, user, REQUEST = {}, ) => used in ZMI
+        """
+        pass
+    deleteUser = postonly(deleteUser)
+
+    security.declareProtected(Permissions.manage_users, "changeOrCreateUsers")
+    def changeOrCreateUsers(self, users = [], groups = [], roles = [], new_users = [], default_password = '', REQUEST = {}, ):
+        """
+        changeOrCreateUsers => affect roles & groups to users and/or create new users
+
+        All parameters are strings or lists (NOT tuples !).
+        NO CHECKING IS DONE. This is an utility method, it's not part of the official API.
+        """
+        # Manage roles / groups deletion
+        del_roles = 0
+        del_groups = 0
+        if self.reset_entry in roles:
+            roles.remove(self.reset_entry)
+            del_roles = 1
+        if self.reset_entry in groups:
+            groups.remove(self.reset_entry)
+            del_groups = 1
+        if not roles and not del_roles:
+            roles = None                # None instead of [] to avoid deletion
+            add_roles = []
+        else:
+            add_roles = roles
+        if not groups and not del_groups:
+            groups = None
+            add_groups = []
+        else:
+            add_groups = groups
+
+        # Passwords management
+        passwords_list = []
+
+        # Create brand new users
+        for new in new_users:
+            # Strip name
+            name = string.strip(new)
+            if not name:
+                continue
+
+            # Avoid erasing former users
+            if name in map(lambda x: x.getId(), self.getUsers()):
+                continue
+
+            # Use default password or generate a random one
+            if default_password:
+                password = default_password
+            else:
+                password = ""
+                for x in range(0, 8):  # Password will be 8 chars long
+                    password = "%s%s" % (password, random.choice("ABCDEFGHJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789"), )
+            self._doAddUser(name, password, add_roles, (), add_groups, )
+
+            # Store the newly created password
+            passwords_list.append({'name':name, 'password':password})
+
+        # Update existing users
+        for user in users:
+            self._updateUser(id = user, groups = groups, roles = roles, )
+
+        # Web request
+        if REQUEST.has_key('RESPONSE'):
+            # Redirect if no users have been created
+            if not passwords_list:
+                return REQUEST.RESPONSE.redirect(self.absolute_url() + "/manage_users")
+
+            # Show passwords form
+            else:
+                REQUEST.set('USER_PASSWORDS', passwords_list)
+                return self.manage_newusers(None, self)
+
+        # Simply return the list of created passwords
+        return passwords_list
+    changeOrCreateUsers = postonly(changeOrCreateUsers)
+
+    security.declareProtected(Permissions.manage_users, "deleteUsers")
+    def deleteUsers(self, users = [], REQUEST = {}):
+        """
+        deleteUsers => explicit
+
+        All parameters are strings. NO CHECKING IS DONE. This is an utility method !
+        """
+        # Delete them
+        self._doDelUsers(users, )
+
+        # Redirect
+        if REQUEST.has_key('RESPONSE'):
+            return REQUEST.RESPONSE.redirect(self.absolute_url() + "/manage_users")
+    deleteUsers = postonly(deleteUsers)
+
+    security.declareProtected(Permissions.manage_users, "changeOrCreateGroups")
+    def changeOrCreateGroups(self, groups = [], roles = [], nested_groups = [], new_groups = [], REQUEST = {}, ):
+        """
+        changeOrCreateGroups => affect roles to groups and/or create new groups
+
+        All parameters are strings. NO CHECKING IS DONE. This is an utility method !
+        """
+        # Manage roles / groups deletion
+        del_roles = 0
+        del_groups = 0
+        if self.reset_entry in roles:
+            roles.remove(self.reset_entry)
+            del_roles = 1
+        if self.reset_entry in nested_groups:
+            nested_groups.remove(self.reset_entry)
+            del_groups = 1
+        if not roles and not del_roles:
+            roles = None                # None instead of [] to avoid deletion
+            add_roles = []
+        else:
+            add_roles = roles
+        if not nested_groups and not del_groups:
+            nested_groups = None
+            add_groups = []
+        else:
+            add_groups = nested_groups
+
+        # Create brand new groups
+        for new in new_groups:
+            name = string.strip(new)
+            if not name:
+                continue
+            self._doAddGroup(name, roles, groups = add_groups)
+
+        # Update existing groups
+        for group in groups:
+            self._updateGroup(group, roles = roles, groups = nested_groups)
+
+        # Redirect
+        if REQUEST.has_key('RESPONSE'):
+            return REQUEST.RESPONSE.redirect(self.absolute_url() + "/manage_groups")
+    changeOrCreateGroups = postonly(changeOrCreateGroups)
+
+    security.declareProtected(Permissions.manage_users, "deleteGroups")
+    def deleteGroups(self, groups = [], REQUEST = {}):
+        """
+        deleteGroups => explicit
+
+        All parameters are strings. NO CHECKING IS DONE. This is an utility method !
+        """
+        # Delete groups
+        for group in groups:
+            self._doDelGroup(group, )
+
+        # Redirect
+        if REQUEST.has_key('RESPONSE'):
+            return REQUEST.RESPONSE.redirect(self.absolute_url() + "/manage_groups")
+    deleteGroups = postonly(deleteGroups)
+
+    #                                                                   #
+    #                   Local Roles Acquisition Blocking                #
+    #   Those two methods perform their own security check.             #
+    #                                                                   #
+
+    security.declarePublic("acquireLocalRoles")
+    def acquireLocalRoles(self, folder, status, REQUEST=None):
+        """
+        Enable or disable local role acquisition on the specified folder.
+        If status is true, it will enable, else it will disable.
+        Note that the user _must_ have the change_permissions permission on the
+        folder to allow changes on it.
+        If you want to use this code from a product, please use _acquireLocalRoles()
+        instead: this private method won't check security on the destination folder.
+        It's usually a bad idea to use _acquireLocalRoles() directly in your product,
+        but, well, after all, you do what you want ! :^)
+        """
+        # Perform security check on destination folder
+        if not getSecurityManager().checkPermission(Permissions.change_permissions, folder):
+            raise Unauthorized(name = "acquireLocalRoles")
+
+        return self._acquireLocalRoles(folder, status)
+    acquireLocalRoles = postonly(acquireLocalRoles)
+
+    def _acquireLocalRoles(self, folder, status):
+        """Same as _acquireLocalRoles() but won't perform security check on the folder.
+        """
+        # Set the variable (or unset it if it's defined)
+        if not status:
+            folder.__ac_local_roles_block__ = 1
+        else:
+            if getattr(folder, '__ac_local_roles_block__', None):
+                folder.__ac_local_roles_block__ = None
+
+
+    security.declarePublic("isLocalRoleAcquired")
+    def isLocalRoleAcquired(self, folder):
+        """Return true if the specified folder allows local role acquisition.
+        """
+        if getattr(folder, '__ac_local_roles_block__', None):
+            return 0
+        return 1
+
+
+    #                                                                                   #
+    #                           Security audit and info methods                         #
+    #                                                                                   #
+
+
+    # This method normally has NOT to be public ! It is because of a CMF inconsistancy.
+    # folder_localrole_form is accessible to users who have the manage_properties permissions
+    # (according to portal_types/Folder/Actions information). This is silly !
+    # folder_localrole_form should be, in CMF, accessible only to those who have the
+    # manage_users permissions instead of manage_properties permissions.
+    # This is yet another one CMF bug we have to care about.
+    # To deal with that in Plone2.1, we check for a particular permission on the destination
+    # object _inside_ the method.
+    security.declarePublic("getLocalRolesForDisplay")
+    def getLocalRolesForDisplay(self, object):
+        """This is used for plone's local roles display
+        This method returns a tuple (massagedUsername, roles, userType, actualUserName).
+        This method is protected by the 'Manage properties' permission. We may
+        change that if it's too permissive..."""
+        # Perform security check on destination object
+        if not getSecurityManager().checkPermission(Permissions.manage_properties, object):
+            raise Unauthorized(name = "getLocalRolesForDisplay")
+
+        return self._getLocalRolesForDisplay(object)
+
+    def _getLocalRolesForDisplay(self, object):
+        """This is used for plone's local roles display
+        This method returns a tuple (massagedUsername, roles, userType, actualUserName)"""
+        result = []
+        local_roles = object.get_local_roles()
+        prefix = self.getGroupPrefix()
+        for one_user in local_roles:
+            massagedUsername = username = one_user[0]
+            roles = one_user[1]
+            userType = 'user'
+            if prefix:
+                if self.getGroupById(username) is not None:
+                    massagedUsername = username[len(prefix):]
+                    userType = 'group'
+            else:
+                userType = 'unknown'
+            result.append((massagedUsername, roles, userType, username))
+        return tuple(result)
+
+
+    security.declarePublic("getAllLocalRoles")
+    def getAllLocalRoles(self, object):
+        """getAllLocalRoles(self, object): return a dictionnary {useratom_id: roles} of local
+        roles defined AND herited at a certain point. This will handle lr-blocking
+        as well.
+        """
+        # Perform security check on destination object
+        if not getSecurityManager().checkPermission(Permissions.change_permissions, object):
+            raise Unauthorized(name = "getAllLocalRoles")
+
+        return self._getAllLocalRoles(object)
+
+
+    def _getAllLocalRoles(self, object):
+        """getAllLocalRoles(self, object): return a dictionnary {useratom_id: roles} of local
+        roles defined AND herited at a certain point. This will handle lr-blocking
+        as well.
+        """
+        # Modified from AccessControl.User.getRolesInContext().
+        merged = {}
+        object = getattr(object, 'aq_inner', object)
+        while 1:
+            if hasattr(object, '__ac_local_roles__'):
+                dict = object.__ac_local_roles__ or {}
+                if callable(dict): dict = dict()
+                for k, v in dict.items():
+                    if not merged.has_key(k):
+                        merged[k] = {}
+                    for role in v:
+                        merged[k][role] = 1
+            if not self.isLocalRoleAcquired(object):
+                break
+            if hasattr(object, 'aq_parent'):
+                object=object.aq_parent
+                object=getattr(object, 'aq_inner', object)
+                continue
+            if hasattr(object, 'im_self'):
+                object=object.im_self
+                object=getattr(object, 'aq_inner', object)
+                continue
+            break
+        for key, value in merged.items():
+            merged[key] = value.keys()
+        return merged
+
+
+
+    # Plone-specific security matrix computing method.
+    security.declarePublic("getPloneSecurityMatrix")
+    def getPloneSecurityMatrix(self, object):
+        """getPloneSecurityMatrix(self, object): return a list of dicts of the current object
+        and all its parents. The list is sorted with portal object first.
+        Each dict has the following structure:
+        {
+          depth: (0 for portal root, 1 for 1st-level folders and so on),
+          id:
+          title:
+          icon:
+          absolute_url:
+          security_permission: true if current user can change security on this object
+          state: (workflow state)
+          acquired_local_roles: 0 if local role blocking is enabled for this folder
+          roles: {
+            'role1': {
+              'all_local_roles': [r1, r2, r3, ] (all defined local roles, including parent ones)
+              'defined_local_roles': [r3, ] (local-defined only local roles)
+              'permissions': ['Access contents information', 'Modify portal content', ] (only a subset)
+              'same_permissions': true if same permissions as the parent
+              'same_all_local_roles': true if all_local_roles is the same as the parent
+              'same_defined_local_roles': true if defined_local_roles is the same as the parent
+              },
+            'role2': {...},
+            },
+        }
+        """
+        # Perform security check on destination object
+        if not getSecurityManager().checkPermission(Permissions.access_contents_information, object):
+            raise Unauthorized(name = "getPloneSecurityMatrix")
+
+        # Basic inits
+        mt = self.portal_membership
+
+        # Fetch all possible roles in the portal
+        all_roles = ['Anonymous'] + mt.getPortalRoles()
+
+        # Fetch parent folders list until the portal
+        all_objects = []
+        cur_object = object
+        while 1:
+            if not getSecurityManager().checkPermission(Permissions.access_contents_information, cur_object):
+                raise Unauthorized(name = "getPloneSecurityMatrix")
+            all_objects.append(cur_object)
+            if cur_object.meta_type == "Plone Site":
+                break
+            cur_object = object.aq_parent
+        all_objects.reverse()
+
+        # Scan those folders to get all the required information about them
+        ret = []
+        previous = None
+        count = 0
+        for obj in all_objects:
+            # Basic information
+            current = {
+                "depth": count,
+                "id": obj.getId(),
+                "title": obj.Title(),
+                "icon": obj.getIcon(),
+                "absolute_url": obj.absolute_url(),
+                "security_permission": getSecurityManager().checkPermission(Permissions.change_permissions, obj),
+                "acquired_local_roles": self.isLocalRoleAcquired(obj),
+                "roles": {},
+                "state": "XXX TODO XXX",         # XXX TODO
+                }
+            count += 1
+
+            # Workflow state
+            # XXX TODO
+
+            # Roles
+            all_local_roles = {}
+            local_roles = self._getAllLocalRoles(obj)
+            for user, roles in self._getAllLocalRoles(obj).items():
+                for role in roles:
+                    if not all_local_roles.has_key(role):
+                        all_local_roles[role] = {}
+                    all_local_roles[role][user] = 1
+            defined_local_roles = {}
+            if hasattr(obj.aq_base, 'get_local_roles'):
+                for user, roles in obj.get_local_roles():
+                    for role in roles:
+                        if not defined_local_roles.has_key(role):
+                            defined_local_roles[role] = {}
+                        defined_local_roles[role][user] = 1
+
+            for role in all_roles:
+                all = all_local_roles.get(role, {}).keys()
+                defined = defined_local_roles.get(role, {}).keys()
+                all.sort()
+                defined.sort()
+                same_all_local_roles = 0
+                same_defined_local_roles = 0
+                if previous:
+                    if previous['roles'][role]['all_local_roles'] == all:
+                        same_all_local_roles = 1
+                    if previous['roles'][role]['defined_local_roles'] == defined:
+                        same_defined_local_roles = 1
+
+                current['roles'][role] = {
+                    "all_local_roles": all,
+                    "defined_local_roles": defined,
+                    "same_all_local_roles": same_all_local_roles,
+                    "same_defined_local_roles": same_defined_local_roles,
+                    "permissions": [],  # XXX TODO
+                    }
+
+            ret.append(current)
+            previous = current
+
+        return ret
+
+
+    security.declareProtected(Permissions.manage_users, "computeSecuritySettings")
+    def computeSecuritySettings(self, folders, actors, permissions, cache = {}):
+        """
+        computeSecuritySettings(self, folders, actors, permissions, cache = {}) => return a structure that is suitable for security audit Page Template.
+
+        - folders is the structure returned by getSiteTree()
+        - actors is the structure returned by listUsersAndRoles()
+        - permissions is ((id: permission), (id: permission), ...)
+        - cache is passed along requests to make computing faster
+        """
+        # Scan folders and actors to get the relevant information
+        usr_cache = {}
+        for id, depth, path in folders:
+            folder = self.unrestrictedTraverse(path)
+            for kind, actor, display, handle, html in actors:
+                if kind in ("user", "group"):
+                    # Init structure
+                    if not cache.has_key(path):
+                        cache[path] = {(kind, actor): {}}
+                    elif not cache[path].has_key((kind, actor)):
+                        cache[path][(kind, actor)] = {}
+                    else:
+                        cache[path][(kind, actor)] = {}
+
+                    # Split kind into groups and get individual role information
+                    perm_keys = []
+                    usr = usr_cache.get(actor)
+                    if not usr:
+                        usr = self.getUser(actor)
+                        usr_cache[actor] = usr
+                    roles = usr.getRolesInContext(folder,)
+                    for role in roles:
+                        for perm_key in self.computeSetting(path, folder, role, permissions, cache).keys():
+                            cache[path][(kind, actor)][perm_key] = 1
+
+                else:
+                    # Get role information
+                    self.computeSetting(path, folder, actor, permissions, cache)
+
+        # Return the computed cache
+        return cache
+
+
+    security.declareProtected(Permissions.manage_users, "computeSetting")
+    def computeSetting(self, path, folder, actor, permissions, cache):
+        """
+        computeSetting(......) => used by computeSecuritySettings to populate the cache for ROLES
+        """
+        # Avoid doing things twice
+        kind = "role"
+        if cache.get(path, {}).get((kind, actor), None) is not None:
+            return cache[path][(kind, actor)]
+
+        # Initilize cache structure
+        if not cache.has_key(path):
+            cache[path] = {(kind, actor): {}}
+        elif not cache[path].has_key((kind, actor)):
+            cache[path][(kind, actor)] = {}
+
+        # Analyze permission settings
+        ps = folder.permission_settings()
+        for perm_key, permission in permissions:
+            # Check acquisition of permission setting.
+            can = 0
+            acquired = 0
+            for p in ps:
+                if p['name'] == permission:
+                    acquired = not not p['acquire']
+
+            # If acquired, call the parent recursively
+            if acquired:
+                parent = folder.aq_parent.getPhysicalPath()
+                perms = self.computeSetting(parent, self.unrestrictedTraverse(parent), actor, permissions, cache)
+                can = perms.get(perm_key, None)
+
+            # Else, check permission here
+            else:
+                for p in folder.rolesOfPermission(permission):
+                    if p['name'] == "Anonymous":
+                        # If anonymous is allowed, then everyone is allowed
+                        if p['selected']:
+                            can = 1
+                            break
+                    if p['name'] == actor:
+                        if p['selected']:
+                            can = 1
+                            break
+
+            # Extend the data structure according to 'can' setting
+            if can:
+                cache[path][(kind, actor)][perm_key] = 1
+
+        return cache[path][(kind, actor)]
+
+
+    security.declarePrivate('_getNextHandle')
+    def _getNextHandle(self, index):
+        """
+        _getNextHandle(self, index) => utility function to
+        get an unique handle for each legend item.
+        """
+        return "%02d" % index
+
+
+    security.declareProtected(Permissions.manage_users, "listUsersAndRoles")
+    def listUsersAndRoles(self,):
+        """
+        listUsersAndRoles(self,) => list of tuples
+
+        This method is used by the Security Audit page.
+        XXX HAS TO BE OPTIMIZED
+        """
+        request = self.REQUEST
+        display_roles = request.get('display_roles', 0)
+        display_groups = request.get('display_groups', 0)
+        display_users = request.get('display_users', 0)
+
+        role_index = 0
+        user_index = 0
+        group_index = 0
+        ret = []
+
+        # Collect roles
+        if display_roles:
+            for r in self.aq_parent.valid_roles():
+                handle = "R%02d" % role_index
+                role_index += 1
+                ret.append(('role', r, r, handle, r))
+
+        # Collect users
+        if display_users:
+            for u in map(lambda x: x.getId(), self.getPureUsers()):
+                obj = self.getUser(u)
+                html = obj.asHTML()
+                handle = "U%02d" % user_index
+                user_index += 1
+                ret.append(('user', u, u, handle, html))
+
+        if display_groups:
+            for u in self.getGroupNames():
+                obj = self.getUser(u)
+                handle = "G%02d" % group_index
+                html = obj.asHTML()
+                group_index += 1
+                ret.append(('group', u, obj.getUserNameWithoutGroupPrefix(), handle, html))
+
+        # Return list
+        return ret
+
+    security.declareProtected(Permissions.manage_users, "getSiteTree")
+    def getSiteTree(self, obj=None, depth=0):
+        """
+        getSiteTree(self, obj=None, depth=0) => special structure
+
+        This is used by the security audit page
+        """
+        ret = []
+        if not obj:
+            if depth==0:
+                obj = self.aq_parent
+            else:
+                return ret
+
+        ret.append([obj.getId(), depth, string.join(obj.getPhysicalPath(), '/')])
+        for sub in obj.objectValues():
+            try:
+                # Ignore user folders
+                if sub.getId() in ('acl_users', ):
+                    continue
+
+                # Ignore portal_* stuff
+                if sub.getId()[:len('portal_')] == 'portal_':
+                    continue
+
+                if sub.isPrincipiaFolderish:
+                    ret.extend(self.getSiteTree(sub, depth + 1))
+
+            except:
+                # We ignore exceptions
+                pass
+
+        return ret
+
+    security.declareProtected(Permissions.manage_users, "listAuditPermissions")
+    def listAuditPermissions(self,):
+        """
+        listAuditPermissions(self,) => return a list of eligible permissions
+        """
+        ps = self.permission_settings()
+        return map(lambda p: p['name'], ps)
+
+    security.declareProtected(Permissions.manage_users, "getDefaultPermissions")
+    def getDefaultPermissions(self,):
+        """
+        getDefaultPermissions(self,) => return default R & W permissions for security audit.
+        """
+        # If there's a Plone site in the above folder, use plonish permissions
+        hasPlone = 0
+        p = self.aq_parent
+        if p.meta_type == "CMF Site":
+            hasPlone = 1
+        else:
+            for obj in p.objectValues():
+                if obj.meta_type == "CMF Site":
+                    hasPlone = 1
+                    break
+
+        if hasPlone:
+            return {'R': 'View',
+                    'W': 'Modify portal content',
+                    }
+        else:
+            return {'R': 'View',
+                    'W': 'Change Images and Files',
+                    }
+
+
+    #                                                                           #
+    #                           Users/Groups tree view                          #
+    #                                (ZMI only)                                 #
+    #                                                                           #
+
+
+    security.declarePrivate('getTreeInfo')
+    def getTreeInfo(self, usr, dict = {}):
+        "utility method"
+        # Prevend infinite recursions
+        name = usr.getUserName()
+        if dict.has_key(name):
+            return
+        dict[name] = {}
+
+        # Properties
+        noprefix = usr.getUserNameWithoutGroupPrefix()
+        is_group = usr.isGroup()
+        if usr.isGroup():
+            icon = string.join(self.getPhysicalPath(), '/') + '/img_group'
+##            icon = self.absolute_url() + '/img_group'
+        else:
+            icon = ' img_user'
+##            icon = self.absolute_url() + '/img_user'
+
+        # Subobjects
+        belongs_to = []
+        for grp in usr.getGroups(no_recurse = 1):
+            belongs_to.append(grp)
+            self.getTreeInfo(self.getGroup(grp))
+
+        # Append (and return) structure
+        dict[name] = {
+            "name": noprefix,
+            "is_group": is_group,
+            "icon": icon,
+            "belongs_to": belongs_to,
+            }
+        return dict
+
+
+    security.declarePrivate("tpValues")
+    def tpValues(self):
+        # Avoid returning HUUUUUUGE lists
+        # Use the cache at first
+        if self._v_no_tree and self._v_cache_no_tree > time.time():
+            return []        # Do not use the tree
+
+        # XXX - I DISABLE THE TREE BY NOW (Pb. with icon URL)
+        return []
+
+        # Then, use a simple computation to determine opportunity to use the tree or not
+        ngroups = len(self.getGroupNames())
+        if ngroups > MAX_TREE_USERS_AND_GROUPS:
+            self._v_no_tree = 1
+            self._v_cache_no_tree = time.time() + TREE_CACHE_TIME
+            return []
+        nusers = len(self.getUsers())
+        if ngroups + nusers > MAX_TREE_USERS_AND_GROUPS:
+            meth_list = self.getGroups
+        else:
+            meth_list = self.getUsers
+        self._v_no_tree = 0
+
+        # Get top-level user and groups list
+        tree_dict = {}
+        top_level_names = []
+        top_level = []
+        for usr in meth_list():
+            self.getTreeInfo(usr, tree_dict)
+            if not usr.getGroups(no_recurse = 1):
+                top_level_names.append(usr.getUserName())
+        for id in top_level_names:
+            top_level.append(treeWrapper(id, tree_dict))
+
+        # Return this top-level list
+        top_level.sort(lambda x, y: cmp(x.sortId(), y.sortId()))
+        return top_level
+
+
+    def tpId(self,):
+        return self.getId()
+
+
+    #                                                                           #
+    #                      Direct traversal to user or group info               #
+    #                                                                           #
+
+    def manage_workspace(self, REQUEST):
+        """
+        manage_workspace(self, REQUEST) => Overrided to allow direct user or group traversal
+        via the left tree view.
+        """
+        path = string.split(REQUEST.PATH_INFO, '/')[:-1]
+        userid = path[-1]
+
+        # Use individual usr/grp management screen (only if name is passed along the mgt URL)
+        if userid != "acl_users":
+            usr = self.getUserById(userid)
+            if usr:
+                REQUEST.set('username', userid)
+                REQUEST.set('MANAGE_TABS_NO_BANNER', '1')   # Prevent use of the manage banner
+                return self.restrictedTraverse('manage_user')()
+
+        # Default management screen
+        return self.restrictedTraverse('manage_overview')()
+
+
+    # Tree caching information
+    _v_no_tree =  0
+    _v_cache_no_tree = 0
+    _v_cache_tree = (0, [])
+
+
+    def __bobo_traverse__(self, request, name):
+        """
+        Looks for the name of a user or a group.
+        This applies only if users list is not huge.
+        """
+        # Check if it's an attribute
+        if hasattr(self.aq_base, name, ):
+            return getattr(self, name)
+
+        # It's not an attribute, maybe it's a user/group
+        # (this feature is used for the tree)
+        if name.startswith('_'):
+            pass        # Do not fetch users
+        elif name.startswith('manage_'):
+            pass        # Do not fetch users
+        elif name in INVALID_USER_NAMES:
+            pass        # Do not fetch users
+        else:
+            # Only try to get users is fetch_user is true.
+            # This is only for performance reasons.
+            # The following code block represent what we want to minimize
+            if self._v_cache_tree[0] < time.time():
+                un = map(lambda x: x.getId(), self.getUsers())            # This is the cost we want to avoid
+                self._v_cache_tree = (time.time() + TREE_CACHE_TIME, un, )
+            else:
+                un = self._v_cache_tree[1]
+
+            # Get the user if we can
+            if name in un:
+                self._v_no_tree = 0
+                return self
+
+            # Force getting the user if we must
+            if request.get("FORCE_USER"):
+                self._v_no_tree = 0
+                return self
+
+        # This will raise if it's not possible to acquire 'name'
+        return getattr(self, name, )
+
+
+
+    #                                                                                   #
+    #                           USERS / GROUPS BATCHING (ZMI SCREENS)                   #
+    #                                                                                   #
+
+    _v_batch_users = []
+
+    security.declareProtected(Permissions.view_management_screens, "listUsersBatches")
+    def listUsersBatches(self,):
+        """
+        listUsersBatches(self,) => return a list of (start, end) tuples.
+        Return None if batching is not necessary
+        """
+        # Time-consuming stuff !
+        un = map(lambda x: x.getId(), self.getPureUsers())
+        if len(un) <= MAX_USERS_PER_PAGE:
+            return None
+        un.sort()
+
+        # Split this list into small groups if necessary
+        ret = []
+        idx = 0
+        l_un = len(un)
+        nbatches = int(math.ceil(l_un / float(MAX_USERS_PER_PAGE)))
+        for idx in range(0, nbatches):
+            first = idx * MAX_USERS_PER_PAGE
+            last = first + MAX_USERS_PER_PAGE - 1
+            if last >= l_un:
+                last = l_un - 1
+            # Append a tuple (not dict) to avoid too much memory consumption
+            ret.append((first, last, un[first], un[last]))
+
+        # Cache & return it
+        self._v_batch_users = un
+        return ret
+
+    security.declareProtected(Permissions.view_management_screens, "listUsersBatchTable")
+    def listUsersBatchTable(self,):
+        """
+        listUsersBatchTable(self,) => Same a mgt screens but divided into sublists to
+        present them into 5 columns.
+        XXX have to merge this w/getUsersBatch to make it in one single pass
+        """
+        # Iterate
+        ret = []
+        idx = 0
+        current = []
+        for rec in (self.listUsersBatches() or []):
+            if not idx % 5:
+                if current:
+                    ret.append(current)
+                current = []
+            current.append(rec)
+            idx += 1
+
+        if current:
+            ret.append(current)
+
+        return ret
+
+    security.declareProtected(Permissions.view_management_screens, "getUsersBatch")
+    def getUsersBatch(self, start):
+        """
+        getUsersBatch(self, start) => user list
+        """
+        # Rebuild the list if necessary
+        if not self._v_batch_users:
+            un = map(lambda x: x.getId(), self.getPureUsers())
+            self._v_batch_users = un
+
+        # Return the batch
+        end = start + MAX_USERS_PER_PAGE
+        ids = self._v_batch_users[start:end]
+        ret = []
+        for id in ids:
+            usr = self.getUser(id)
+            if usr:                     # Prevent adding invalid users
+                ret.append(usr)
+        return ret
+
+
+    #                                                                            #
+    #                         Multiple sources management                        #
+    #                                                                            #
+
+    # Arrows
+    img_up_arrow = ImageFile.ImageFile('www/up_arrow.gif', globals())
+    img_down_arrow = ImageFile.ImageFile('www/down_arrow.gif', globals())
+    img_up_arrow_grey = ImageFile.ImageFile('www/up_arrow_grey.gif', globals())
+    img_down_arrow_grey = ImageFile.ImageFile('www/down_arrow_grey.gif', globals())
+
+    security.declareProtected(Permissions.manage_users, "toggleSource")
+    def toggleSource(self, src_id, REQUEST = {}):
+        """
+        toggleSource(self, src_id, REQUEST = {}) => toggle enabled/disabled source
+        """
+        # Find the source
+        ids = self.objectIds('GRUFUsers')
+        if not src_id in ids:
+            raise ValueError, "Invalid source: '%s' (%s)" % (src_id, ids)
+        src = getattr(self, src_id)
+        if src.enabled:
+            src.disableSource()
+        else:
+            src.enableSource()
+
+        # Redirect where we want to
+        if REQUEST.has_key('RESPONSE'):
+            return REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_GRUFSources')
+
+
+    security.declareProtected(Permissions.manage_users, "listUserSources")
+    def listUserSources(self, ):
+        """
+        listUserSources(self, ) => Return a list of userfolder objects
+        Only return VALID (ie containing an acl_users) user sources if all is None
+        XXX HAS TO BE OPTIMIZED VERY MUCH!
+        We add a check in debug mode to ensure that invalid sources won't be added
+        to the list.
+        This method return only _enabled_ user sources.
+        """
+        ret = []
+        dret = {}
+        if DEBUG_MODE:
+            for src in self.objectValues(['GRUFUsers']):
+                if not src.enabled:
+                    continue
+                if 'acl_users' in src.objectIds():
+                    if getattr(aq_base(src.acl_users), 'authenticate', None):   # Additional check in debug mode
+                        dret[src.id] = src.acl_users                            # we cannot use restrictedTraverse here because
+                                                                                # of infinite recursion issues.
+        else:
+            for src in self.objectValues(['GRUFUsers']):
+                if not src.enabled:
+                    continue
+                if not 'acl_users' in src.objectIds():
+                    continue
+                dret[src.id] = src.acl_users
+        ret = dret.items()
+        ret.sort()
+        return [ src[1] for src in ret ]
+
+    security.declareProtected(Permissions.manage_users, "listUserSourceFolders")
+    def listUserSourceFolders(self, ):
+        """
+        listUserSources(self, ) => Return a list of GRUFUsers objects
+        """
+        ret = []
+        for src in self.objectValues(['GRUFUsers']):
+            ret.append(src)
+        ret.sort(lambda x,y: cmp(x.id, y.id))
+        return ret
+
+    security.declarePrivate("getUserSource")
+    def getUserSource(self, id):
+        """
+        getUserSource(self, id) => GRUFUsers.acl_users object.
+        Raises if no acl_users available
+        """
+        return getattr(self, id).acl_users
+
+    security.declarePrivate("getUserSourceFolder")
+    def getUserSourceFolder(self, id):
+        """
+        getUserSourceFolder(self, id) => GRUFUsers object
+        """
+        return getattr(self, id)
+
+    security.declareProtected(Permissions.manage_users, "addUserSource")
+    def addUserSource(self, factory_uri, REQUEST = {}, *args, **kw):
+        """
+        addUserSource(self, factory_uri, REQUEST = {}, *args, **kw) => redirect
+        Adds the specified user folder
+        """
+        # Get the initial Users id
+        ids = self.objectIds('GRUFUsers')
+        if ids:
+            ids.sort()
+            if ids == ['Users',]:
+                last = 0
+            else:
+                last = int(ids[-1][-2:])
+            next_id = "Users%02d" % (last + 1, )
+        else:
+            next_id = "Users"
+
+        # Add the GRUFFolder object
+        uf = GRUFFolder.GRUFUsers(id = next_id)
+        self._setObject(next_id, uf)
+
+##        # If we use ldap, tag it
+##        if string.find(factory_uri.lower(), "ldap") > -1:
+##            self._haveLDAPUF += 1
+
+        # Add its underlying UserFolder
+        # If we're called TTW, uses a redirect else tries to call the UF factory directly
+        if REQUEST.has_key('RESPONSE'):
+            return REQUEST.RESPONSE.redirect("%s/%s/%s" % (self.absolute_url(), next_id, factory_uri))
+        return getattr(self, next_id).unrestrictedTraverse(factory_uri)(*args, **kw)
+    addUserSource = postonly(addUserSource)
+
+    security.declareProtected(Permissions.manage_users, "deleteUserSource")
+    def deleteUserSource(self, id = None, REQUEST = {}):
+        """
+        deleteUserSource(self, id = None, REQUEST = {}) => Delete the specified user source
+        """
+        # Check the source id
+        if type(id) != type('s'):
+            raise ValueError, "You must choose a valid source to delete and confirm it."
+
+        # Delete it
+        self.manage_delObjects([id,])
+        if REQUEST.has_key('RESPONSE'):
+            return REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_GRUFSources')
+    deleteUserSource = postonly(deleteUserSource)
+
+    security.declareProtected(Permissions.manage_users, "getDefaultUserSource")
+    def getDefaultUserSource(self,):
+        """
+        getDefaultUserSource(self,) => acl_users object
+        Return default user source for user writing.
+        XXX By now, the FIRST source is the default one. This may change in the future.
+        """
+        lst = self.listUserSources()
+        if not lst:
+            raise RuntimeError, "No valid User Source to add users in."
+        return lst[0]
+
+
+    security.declareProtected(Permissions.manage_users, "listAvailableUserSources")
+    def listAvailableUserSources(self, filter_permissions = 1, filter_classes = 1):
+        """
+        listAvailableUserSources(self, filter_permissions = 1, filter_classes = 1) => tuples (name, factory_uri)
+        List UserFolder replacement candidates.
+
+        - if filter_classes is true, return only ones which have a base UserFolder class
+        - if filter_permissions, return only types the user has rights to add
+        """
+        ret = []
+
+        # Fetch candidate types
+        user = getSecurityManager().getUser()
+        meta_types = []
+        if callable(self.all_meta_types):
+            all=self.all_meta_types()
+        else:
+            all=self.all_meta_types
+        for meta_type in all:
+            if filter_permissions and meta_type.has_key('permission'):
+                if user.has_permission(meta_type['permission'],self):
+                    meta_types.append(meta_type)
+            else:
+                meta_types.append(meta_type)
+
+        # Keep only, if needed, BasicUserFolder-derived classes
+        for t in meta_types:
+            if t['name'] == self.meta_type:
+                continue        # Do not keep GRUF ! ;-)
+
+            if filter_classes:
+                try:
+                    if t.get('instance', None) and t['instance'].isAUserFolder:
+                        ret.append((t['name'], t['action']))
+                        continue
+                    if t.get('instance', None) and class_utility.isBaseClass(AccessControl.User.BasicUserFolder, t['instance']):
+                        ret.append((t['name'], t['action']))
+                        continue
+                except AttributeError:
+                    pass        # We ignore 'invalid' instances (ie. that wouldn't define a __base__ attribute)
+            else:
+                ret.append((t['name'], t['action']))
+
+        return tuple(ret)
+
+    security.declareProtected(Permissions.manage_users, "moveUserSourceUp")
+    def moveUserSourceUp(self, id, REQUEST = {}):
+        """
+        moveUserSourceUp(self, id, REQUEST = {}) => used in management screens
+        try to get ids as consistant as possible
+        """
+        # List and sort sources and preliminary checks
+        ids = self.objectIds('GRUFUsers')
+        ids.sort()
+        if not ids or not id in ids:
+            raise ValueError, "Invalid User Source: '%s'" % (id,)
+
+        # Find indexes to swap
+        src_index = ids.index(id)
+        if src_index == 0:
+            raise ValueError, "Cannot move '%s'  User Source up." % (id, )
+        dest_index = src_index - 1
+
+        # Find numbers to swap, fix them if they have more than 1 as offset
+        if ids[dest_index] == 'Users':
+            dest_num = 0
+        else:
+            dest_num = int(ids[dest_index][-2:])
+        src_num = dest_num + 1
+
+        # Get ids
+        src_id = id
+        if dest_num == 0:
+            dest_id = "Users"
+        else:
+            dest_id = "Users%02d" % (dest_num,)
+        tmp_id = "%s_" % (dest_id, )
+
+        # Perform the swap
+        self._renameUserSource(src_id, tmp_id)
+        self._renameUserSource(dest_id, src_id)
+        self._renameUserSource(tmp_id, dest_id)
+
+        # Return back to the forms
+        if REQUEST.has_key('RESPONSE'):
+            return REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_GRUFSources')
+    moveUserSourceUp = postonly(moveUserSourceUp)
+
+    security.declareProtected(Permissions.manage_users, "moveUserSourceDown")
+    def moveUserSourceDown(self, id, REQUEST = {}):
+        """
+        moveUserSourceDown(self, id, REQUEST = {}) => used in management screens
+        try to get ids as consistant as possible
+        """
+        # List and sort sources and preliminary checks
+        ids = self.objectIds('GRUFUsers')
+        ids.sort()
+        if not ids or not id in ids:
+            raise ValueError, "Invalid User Source: '%s'" % (id,)
+
+        # Find indexes to swap
+        src_index = ids.index(id)
+        if src_index == len(ids) - 1:
+            raise ValueError, "Cannot move '%s'  User Source up." % (id, )
+        dest_index = src_index + 1
+
+        # Find numbers to swap, fix them if they have more than 1 as offset
+        if id == 'Users':
+            dest_num = 1
+        else:
+            dest_num = int(ids[dest_index][-2:])
+        src_num = dest_num - 1
+
+        # Get ids
+        src_id = id
+        if dest_num == 0:
+            dest_id = "Users"
+        else:
+            dest_id = "Users%02d" % (dest_num,)
+        tmp_id = "%s_" % (dest_id, )
+
+        # Perform the swap
+        self._renameUserSource(src_id, tmp_id)
+        self._renameUserSource(dest_id, src_id)
+        self._renameUserSource(tmp_id, dest_id)
+
+        # Return back to the forms
+        if REQUEST.has_key('RESPONSE'):
+            return REQUEST.RESPONSE.redirect(self.absolute_url() + '/manage_GRUFSources')
+    moveUserSourceDown = postonly(moveUserSourceDown)
+
+
+    security.declarePrivate('_renameUserSource')
+    def _renameUserSource(self, id, new_id, ):
+        """
+        Rename a particular sub-object.
+        Taken fro CopySupport.manage_renameObject() code, modified to disable verifications.
+        """
+        try: self._checkId(new_id)
+        except: raise CopyError, MessageDialog(
+                      title='Invalid Id',
+                      message=sys.exc_info()[1],
+                      action ='manage_main')
+        ob=self._getOb(id)
+##        if not ob.cb_isMoveable():
+##            raise "Copy Error", eNotSupported % id
+##        self._verifyObjectPaste(ob)           # This is what we disable
+        try:    ob._notifyOfCopyTo(self, op=1)
+        except: raise CopyError, MessageDialog(
+                      title='Rename Error',
+                      message=sys.exc_info()[1],
+                      action ='manage_main')
+        self._delObject(id)
+        ob = aq_base(ob)
+        ob._setId(new_id)
+
+        # Note - because a rename always keeps the same context, we
+        # can just leave the ownership info unchanged.
+        self._setObject(new_id, ob, set_owner=0)
+
+
+    security.declareProtected(Permissions.manage_users, "replaceUserSource")
+    def replaceUserSource(self, id = None, new_factory = None, REQUEST = {}, *args, **kw):
+        """
+        replaceUserSource(self, id = None, new_factory = None, REQUEST = {}, *args, **kw) => perform user source replacement
+
+        If new_factory is None, find it inside REQUEST (useful for ZMI screens)
+        """
+        # Check the source id
+        if type(id) != type('s'):
+            raise ValueError, "You must choose a valid source to replace and confirm it."
+
+        # Retreive factory if not explicitly passed
+        if not new_factory:
+            for record in REQUEST.get("source_rec", []):
+                if record['id'] == id:
+                    new_factory = record['new_factory']
+                    break
+            if not new_factory:
+                raise ValueError, "You must select a new User Folder type."
+
+        # Delete the former one
+        us = getattr(self, id)
+        if "acl_users" in us.objectIds():
+            us.manage_delObjects(['acl_users'])
+
+        ## If we use ldap, tag it
+        #if string.find(new_factory.lower(), "ldap") > -1:
+        #    self._haveLDAPUF += 1
+
+        # Re-create the underlying UserFolder
+        # If we're called TTW, uses a redirect else tries to call the UF factory directly
+        if REQUEST.has_key('RESPONSE'):
+            return REQUEST.RESPONSE.redirect("%s/%s/%s" % (self.absolute_url(), id, new_factory))
+        return us.unrestrictedTraverse(new_factory)(*args, **kw) # XXX minor security pb ?
+    replaceUserSource = postonly(replaceUserSource)
+
+
+    security.declareProtected(Permissions.manage_users, "hasLDAPUserFolderSource")
+    def hasLDAPUserFolderSource(self, ):
+        """
+        hasLDAPUserFolderSource(self,) => boolean
+        Return true if a LUF source is instanciated.
+        """
+        for src in self.listUserSources():
+            if src.meta_type == "LDAPUserFolder":
+                return 1
+        return None
+
+
+    security.declareProtected(Permissions.manage_users, "updateLDAPUserFolderMapping")
+    def updateLDAPUserFolderMapping(self, REQUEST = None):
+        """
+        updateLDAPUserFolderMapping(self, REQUEST = None) => None
+
+        Update the first LUF source in the process so that LDAP-group-to-Zope-role mapping
+        is done.
+        This is done by calling the appropriate method in LUF and affecting all 'group_' roles
+        to the matching LDAP groups.
+        """
+        # Fetch all groups
+        groups = self.getGroupIds()
+
+        # Scan sources
+        for src in self.listUserSources():
+            if not src.meta_type == "LDAPUserFolder":
+                continue
+
+            # Delete all former group mappings
+            deletes = []
+            for (grp, role) in src.getGroupMappings():
+                if role.startswith('group_'):
+                    deletes.append(grp)
+            src.manage_deleteGroupMappings(deletes)
+
+            # Append all group mappings if it can be done
+            ldap_groups = src.getGroups(attr = "cn")
+            for grp in groups:
+                if src._local_groups:
+                    grp_name = grp
+                else:
+                    grp_name = grp[len('group_'):]
+                Log(LOG_DEBUG, "cheching", grp_name, "in", ldap_groups, )
+                if not grp_name in ldap_groups:
+                    continue
+                Log(LOG_DEBUG, "Map", grp, "to", grp_name)
+                src.manage_addGroupMapping(
+                    grp_name,
+                    grp,
+                    )
+
+        # Return
+        if REQUEST:
+            return REQUEST.RESPONSE.redirect(
+                self.absolute_url() + "/manage_wizard",
+                )
+        updateLDAPUserFolderMapping = postonly(updateLDAPUserFolderMapping)
+
+
+    #                                                                               #
+    #                               The Wizard Section                              #
+    #                                                                               #
+
+    def listLDAPUserFolderMapping(self,):
+        """
+        listLDAPUserFolderMapping(self,) => utility method
+        """
+        ret = []
+        gruf_done = []
+        ldap_done = []
+
+        # Scan sources
+        for src in self.listUserSources():
+            if not src.meta_type == "LDAPUserFolder":
+                continue
+
+            # Get all GRUF & LDAP groups
+            if src._local_groups:
+                gruf_ids = self.getGroupIds()
+            else:
+                gruf_ids = self.getGroupIds()
+            ldap_mapping = src.getGroupMappings()
+            ldap_groups = src.getGroups(attr = "cn")
+            for grp,role in ldap_mapping:
+                if role in gruf_ids:
+                    ret.append((role, grp))
+                    gruf_done.append(role)
+                    ldap_done.append(grp)
+                    if not src._local_groups:
+                        ldap_done.append(role)
+            for grp in ldap_groups:
+                if not grp in ldap_done:
+                    ret.append((None, grp))
+            for grp in gruf_ids:
+                if not grp in gruf_done:
+                    ret.append((grp, None))
+            Log(LOG_DEBUG, "return", ret)
+            return ret
+
+
+    security.declareProtected(Permissions.manage_users, "getInvalidMappings")
+    def getInvalidMappings(self,):
+        """
+        return true if LUF mapping looks good
+        """
+        wrong = []
+        grufs = []
+        for gruf, ldap in self.listLDAPUserFolderMapping():
+            if gruf and ldap:
+                continue
+            if not gruf:
+                continue
+            if gruf.startswith('group_'):
+                gruf = gruf[len('group_'):]
+            grufs.append(gruf)
+        for gruf, ldap in self.listLDAPUserFolderMapping():
+            if gruf and ldap:
+                continue
+            if not ldap:
+                continue
+            if ldap.startswith('group_'):
+                ldap = ldap[len('group_'):]
+            if ldap in grufs:
+                wrong.append(ldap)
+
+        return wrong
+
+    security.declareProtected(Permissions.manage_users, "getLUFSource")
+    def getLUFSource(self,):
+        """
+        getLUFSource(self,) => Helper to get a pointer to the LUF src.
+        Return None if not available
+        """
+        for src in self.listUserSources():
+            if src.meta_type == "LDAPUserFolder":
+                return src
+
+    security.declareProtected(Permissions.manage_users, "areLUFGroupsLocal")
+    def areLUFGroupsLocal(self,):
+        """return true if luf groups are stored locally"""
+        return hasattr(self.getLUFSource(), '_local_groups')
+
+
+    security.declareProtected(Permissions.manage_users, "haveLDAPGroupFolder")
+    def haveLDAPGroupFolder(self,):
+        """return true if LDAPGroupFolder is the groups source
+        """
+        return not not self.Groups.acl_users.meta_type == 'LDAPGroupFolder'
+    
+    security.declarePrivate('searchGroups')
+    def searchGroups(self, **kw):
+        names = self.getUserNames(__include_users__ = 0, __groups_prefixed__ = 1)
+        return [{'id' : gn} for gn in names]
+        
+
+
+class treeWrapper:
+    """
+    treeWrapper: Wrapper around user/group objects for the tree
+    """
+    def __init__(self, id, tree, parents = []):
+        """
+        __init__(self, id, tree, parents = []) => wraps the user object for dtml-tree
+        """
+        # Prepare self-contained information
+        self._id = id
+        self.name = tree[id]['name']
+        self.icon = tree[id]['icon']
+        self.is_group = tree[id]['is_group']
+        parents.append(id)
+        self.path = parents
+
+        # Prepare subobjects information
+        subobjects = []
+        for grp_id in tree.keys():
+            if id in tree[grp_id]['belongs_to']:
+                subobjects.append(treeWrapper(grp_id, tree, parents))
+        subobjects.sort(lambda x, y: cmp(x.sortId(), y.sortId()))
+        self.subobjects = subobjects
+
+    def id(self,):
+        return self.name
+
+    def sortId(self,):
+        if self.is_group:
+            return "__%s" % (self._id,)
+        else:
+            return self._id
+
+    def tpValues(self,):
+        """
+        Return 'subobjects'
+        """
+        return self.subobjects
+
+    def tpId(self,):
+        return self._id
+
+    def tpURL(self,):
+        return self.tpId()
+
+InitializeClass(GroupUserFolder)
diff --git a/GroupsTool.py b/GroupsTool.py
new file mode 100644
index 0000000..e76caa1
--- /dev/null
+++ b/GroupsTool.py
@@ -0,0 +1,495 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+## Copyright (c) 2003 The Connexions Project, All Rights Reserved
+## initially written by J Cameron Cooper, 11 June 2003
+## concept with Brent Hendricks, George Runyan
+"""
+Basic usergroup tool.
+"""
+__version__ = "$Revision$"
+# $Source:  $
+# $Id: GroupsTool.py 50142 2007-09-25 13:13:12Z wichert $
+__docformat__ = 'restructuredtext'
+
+from Products.CMFCore.utils import UniqueObject
+from Products.CMFCore.utils import getToolByName
+from Products.CMFCore.utils import _checkPermission
+from OFS.SimpleItem import SimpleItem
+from Globals import InitializeClass, DTMLFile, MessageDialog
+from Acquisition import aq_base
+from AccessControl.User import nobody
+from AccessControl import ClassSecurityInfo
+from ZODB.POSException import ConflictError
+# BBB CMF < 1.5
+try:
+    from Products.CMFCore.permissions import ManagePortal
+    from Products.CMFCore.permissions import View
+    from Products.CMFCore.permissions import ViewManagementScreens
+except ImportError:
+    from Products.CMFCore.CMFCorePermissions import ManagePortal
+    from Products.CMFCore.CMFCorePermissions import View
+    from Products.CMFCore.CMFCorePermissions import ViewManagementScreens
+
+from Products.GroupUserFolder import postonly
+from GroupsToolPermissions import AddGroups
+from GroupsToolPermissions import ManageGroups
+from GroupsToolPermissions import DeleteGroups
+from GroupsToolPermissions import ViewGroups
+from GroupsToolPermissions import SetGroupOwnership
+from Products.CMFCore.ActionProviderBase import ActionProviderBase
+from interfaces.portal_groups import portal_groups as IGroupsTool
+from global_symbols import *
+
+# Optional feature-preview support
+import PloneFeaturePreview
+
+class GroupsTool (UniqueObject, SimpleItem, ActionProviderBase, ):
+    """ This tool accesses group data through a GRUF acl_users object.
+
+    It can be replaced with something that groups member data in a
+    different way.
+    """
+    # Show implementation only if  IGroupsTool is defined
+    # The latter will work only with Plone 1.1 => hence, the if
+    if hasattr(ActionProviderBase, '__implements__'):
+        __implements__ = (IGroupsTool, ActionProviderBase.__implements__)
+
+    id = 'portal_groups'
+    meta_type = 'CMF Groups Tool'
+    _actions = ()
+
+    security = ClassSecurityInfo()
+
+    groupworkspaces_id = "groups"
+    groupworkspaces_title = "Groups"
+    groupWorkspacesCreationFlag = 1
+    groupWorkspaceType = "Folder"
+    groupWorkspaceContainerType = "Folder"
+
+    manage_options=(
+            ( { 'label' : 'Configure'
+                     , 'action' : 'manage_config'
+                    },
+                ) + ActionProviderBase.manage_options +
+                ( { 'label' : 'Overview'
+                     , 'action' : 'manage_overview'
+                     },
+                ) + SimpleItem.manage_options)
+
+    #                                                   #
+    #                   ZMI methods                     #
+    #                                                   #
+    security.declareProtected(ViewManagementScreens, 'manage_overview')
+    manage_overview = DTMLFile('dtml/explainGroupsTool', globals())     # unlike MembershipTool
+    security.declareProtected(ViewManagementScreens, 'manage_config')
+    manage_config = DTMLFile('dtml/configureGroupsTool', globals())
+
+    security.declareProtected(ManagePortal, 'manage_setGroupWorkspacesFolder')
+    def manage_setGroupWorkspacesFolder(self, id='groups', title='Groups', REQUEST=None):
+        """ZMI method for workspace container name set."""
+        self.setGroupWorkspacesFolder(id, title)
+        return self.manage_config(manage_tabs_message="Workspaces folder name set to %s" % id)
+
+    security.declareProtected(ManagePortal, 'manage_setGroupWorkspaceType')
+    def manage_setGroupWorkspaceType(self, type='Folder', REQUEST=None):
+        """ZMI method for workspace type set."""
+        self.setGroupWorkspaceType(type)
+        return self.manage_config(manage_tabs_message="Group Workspaces type set to %s" % type)
+
+    security.declareProtected(ManagePortal, 'manage_setGroupWorkspaceContainerType')
+    def manage_setGroupWorkspaceContainerType(self, type='Folder', REQUEST=None):
+        """ZMI method for workspace type set."""
+        self.setGroupWorkspaceContainerType(type)
+        return self.manage_config(manage_tabs_message="Group Workspaces container type set to %s" % type)
+
+    security.declareProtected(ViewGroups, 'getGroupById')
+    def getGroupById(self, id):
+        """
+        Returns the portal_groupdata-ish object for a group corresponding to this id.
+        """
+        if id==None:
+            return None
+        g = self.acl_users.getGroupByName(id, None)
+        if g is not None:
+            g = self.wrapGroup(g)
+        return g
+
+    security.declareProtected(ViewGroups, 'getGroupsByUserId')
+    def getGroupsByUserId(self, userid):
+        """Return a list of the groups the user corresponding to 'userid' belongs to."""
+        #log("getGroupsByUserId(%s)" % userid)
+        user = self.acl_users.getUser(userid)
+        #log("user '%s' is in groups %s" % (userid, user.getGroups()))
+        if user:
+            groups = user.getGroups() or []
+        else:
+            groups = []
+        return [self.getGroupById(elt) for elt in groups]
+
+    security.declareProtected(ViewGroups, 'listGroups')
+    def listGroups(self):
+        """Return a list of the available portal_groupdata-ish objects."""
+        return [ self.wrapGroup(elt) for elt in self.acl_users.getGroups() ]
+
+    security.declareProtected(ViewGroups, 'listGroupIds')
+    def listGroupIds(self):
+        """Return a list of the available groups' ids as entered (without group prefixes)."""
+        return self.acl_users.getGroupNames()
+
+    security.declareProtected(ViewGroups, 'listGroupNames')
+    def listGroupNames(self):
+        """Return a list of the available groups' ids as entered (without group prefixes)."""
+        return self.acl_users.getGroupNames()
+
+    security.declarePublic("isGroup")
+    def isGroup(self, u):
+        """Test if a user/group object is a group or not.
+        You must pass an object you get earlier with wrapUser() or wrapGroup()
+        """
+        base = aq_base(u)
+        if hasattr(base, "isGroup") and base.isGroup():
+            return 1
+        return 0
+
+    security.declareProtected(View, 'searchForGroups')
+    def searchForGroups(self, REQUEST = {}, **kw):
+        """Return a list of groups meeting certain conditions. """
+        # arguments need to be better refined?
+        if REQUEST:
+            dict = REQUEST
+        else:
+            dict = kw
+
+        name = dict.get('name', None)
+        email = dict.get('email', None)
+        roles = dict.get('roles', None)
+        title = dict.get('title', None)
+        title_or_name = dict.get('title_or_name', None)
+        
+        last_login_time = dict.get('last_login_time', None)
+        #is_manager = self.checkPermission('Manage portal', self)
+
+        if name:
+            name = name.strip().lower()
+        if not name:
+            name = None
+        if email:
+            email = email.strip().lower()
+        if not email:
+            email = None
+        if title:
+            title = title.strip().lower()
+        if title_or_name:
+            title_or_name = title_or_name.strip().lower()
+        if not title:
+            title = None
+
+        res = []
+        portal = self.portal_url.getPortalObject()
+        for g in portal.portal_groups.listGroups():
+            #if not (g.listed or is_manager):
+            #    continue
+            if name:
+                if (g.getGroupName().lower().find(name) == -1) and (g.getGroupId().lower().find(name) == -1):
+                    continue
+            if email:
+                if g.email.lower().find(email) == -1:
+                    continue
+            if roles:
+                group_roles = g.getRoles()
+                found = 0
+                for r in roles:
+                    if r in group_roles:
+                        found = 1
+                        break
+                if not found:
+                    continue
+            if title:
+                if g.title.lower().find(title) == -1:
+                    continue
+            if title_or_name:
+                # first search for title
+                if g.title.lower().find(title_or_name) == -1:
+                    # not found, now search for name
+                    if (g.getGroupName().lower().find(title_or_name) == -1) and (g.getGroupId().lower().find(title_or_name) == -1):
+                        continue
+                
+            if last_login_time:
+                if g.last_login_time < last_login_time:
+                    continue
+            res.append(g)
+
+        return res
+
+    security.declareProtected(AddGroups, 'addGroup')
+    def addGroup(self, id, roles = [], groups = [], REQUEST=None, *args, **kw):
+        """Create a group, and a group workspace if the toggle is on, with the supplied id, roles, and domains.
+
+        Underlying user folder must support adding users via the usual Zope API.
+        Passwords for groups ARE irrelevant in GRUF."""
+        if id in self.listGroupIds():
+            raise ValueError, "Group '%s' already exists." % (id, )
+        self.acl_users.userFolderAddGroup(id, roles = roles, groups = groups )
+        self.createGrouparea(id)
+        self.getGroupById(id).setProperties(**kw)
+    addGroup = postonly(addGroup)
+
+    security.declareProtected(ManageGroups, 'editGroup')
+    def editGroup(self, id, roles = None, groups = None, REQUEST=None, *args, **kw):
+        """Edit the given group with the supplied password, roles, and domains.
+
+        Underlying user folder must support editing users via the usual Zope API.
+        Passwords for groups seem to be currently irrelevant in GRUF."""
+        self.acl_users.userFolderEditGroup(id, roles = roles, groups = groups, )
+        self.getGroupById(id).setProperties(**kw)
+    editGroup = postonly(editGroup)
+
+    security.declareProtected(DeleteGroups, 'removeGroups')
+    def removeGroups(self, ids, keep_workspaces=0, REQUEST=None):
+        """Remove the group in the provided list (if possible).
+
+        Will by default remove this group's GroupWorkspace if it exists. You may
+        turn this off by specifying keep_workspaces=true.
+        Underlying user folder must support removing users via the usual Zope API."""
+        for gid in ids:
+            gdata = self.getGroupById(gid)
+            gusers = gdata.getGroupMembers()
+            for guser in gusers:
+                gdata.removeMember(guser.id)
+
+        self.acl_users.userFolderDelGroups(ids)
+        gwf = self.getGroupWorkspacesFolder()
+        if not gwf: # _robert_
+            return
+        if not keep_workspaces:
+            for id in ids:
+                if hasattr(aq_base(gwf), id):
+                    gwf._delObject(id)
+    removeGroups = postonly(removeGroups)
+
+    security.declareProtected(SetGroupOwnership, 'setGroupOwnership')
+    def setGroupOwnership(self, group, object, REQUEST=None):
+        """Make the object 'object' owned by group 'group' (a portal_groupdata-ish object).
+
+        For GRUF this is easy. Others may have to re-implement."""
+        user = group.getGroup()
+        if user is None:
+            raise ValueError, "Invalid group: '%s'." % (group, )
+        object.changeOwnership(user)
+        object.manage_setLocalRoles(user.getId(), ['Owner'])
+    setGroupOwnership = postonly(setGroupOwnership)
+
+    security.declareProtected(ManagePortal, 'setGroupWorkspacesFolder')
+    def setGroupWorkspacesFolder(self, id="", title=""):
+        """ Set the location of the Group Workspaces folder by id.
+
+        The Group Workspaces Folder contains all the group workspaces, just like the
+        Members folder contains all the member folders.
+
+         If anyone really cares, we can probably make the id work as a path as well,
+         but for the moment it's only an id for a folder in the portal root, just like the
+         corresponding MembershipTool functionality. """
+        self.groupworkspaces_id = id.strip()
+        self.groupworkspaces_title = title
+
+    security.declareProtected(ManagePortal, 'getGroupWorkspacesFolderId')
+    def getGroupWorkspacesFolderId(self):
+        """ Get the Group Workspaces folder object's id.
+
+        The Group Workspaces Folder contains all the group workspaces, just like the
+        Members folder contains all the member folders. """
+        return self.groupworkspaces_id
+
+    security.declareProtected(ManagePortal, 'getGroupWorkspacesFolderTitle')
+    def getGroupWorkspacesFolderTitle(self):
+        """ Get the Group Workspaces folder object's title.
+        """
+        return self.groupworkspaces_title
+
+    security.declarePublic('getGroupWorkspacesFolder')
+    def getGroupWorkspacesFolder(self):
+        """ Get the Group Workspaces folder object.
+
+        The Group Workspaces Folder contains all the group workspaces, just like the
+        Members folder contains all the member folders. """
+        parent = self.aq_inner.aq_parent
+        folder = getattr(parent, self.getGroupWorkspacesFolderId(), None)
+        return folder
+
+    security.declareProtected(ManagePortal, 'toggleGroupWorkspacesCreation')
+    def toggleGroupWorkspacesCreation(self, REQUEST=None):
+        """ Toggles the flag for creation of a GroupWorkspaces folder upon creation of the group. """
+        if not hasattr(self, 'groupWorkspacesCreationFlag'):
+            self.groupWorkspacesCreationFlag = 0
+
+        self.groupWorkspacesCreationFlag = not self.groupWorkspacesCreationFlag
+
+        m = self.groupWorkspacesCreationFlag and 'turned on' or 'turned off'
+
+        return self.manage_config(manage_tabs_message="Workspaces creation %s" % m)
+
+    security.declareProtected(ManagePortal, 'getGroupWorkspacesCreationFlag')
+    def getGroupWorkspacesCreationFlag(self):
+        """Return the (boolean) flag indicating whether the Groups Tool will create a group workspace
+        upon the creation of the group (if one doesn't exist already). """
+        return self.groupWorkspacesCreationFlag
+
+    security.declareProtected(AddGroups, 'createGrouparea')
+    def createGrouparea(self, id):
+        """Create a space in the portal for the given group, much like member home
+        folders."""
+        parent = self.aq_inner.aq_parent
+        workspaces = self.getGroupWorkspacesFolder()
+        pt = getToolByName( self, 'portal_types' )
+
+        if id and self.getGroupWorkspacesCreationFlag():
+            if workspaces is None:
+                # add GroupWorkspaces folder
+                pt.constructContent(
+                    type_name = self.getGroupWorkspaceContainerType(),
+                    container = parent,
+                    id = self.getGroupWorkspacesFolderId(),
+                    )
+                workspaces = self.getGroupWorkspacesFolder()
+                workspaces.setTitle(self.getGroupWorkspacesFolderTitle())
+                workspaces.setDescription("Container for " + self.getGroupWorkspacesFolderId())
+                # how about ownership?
+
+                # this stuff like MembershipTool...
+                portal_catalog = getToolByName( self, 'portal_catalog' )
+                portal_catalog.unindexObject(workspaces)     # unindex GroupWorkspaces folder
+                workspaces._setProperty('right_slots', (), 'lines')
+                
+            if workspaces is not None and not hasattr(workspaces.aq_base, id):
+                # add workspace to GroupWorkspaces folder
+                pt.constructContent(
+                    type_name = self.getGroupWorkspaceType(),
+                    container = workspaces,
+                    id = id,
+                    )
+                space = self.getGroupareaFolder(id)
+                space.setTitle("%s workspace" % id)
+                space.setDescription("Container for objects shared by this group")
+
+                if hasattr(space, 'setInitialGroup'):
+                    # GroupSpaces can have their own policies regarding the group
+                    # that they are created for.
+                    user = self.getGroupById(id).getGroup()
+                    if user is not None:
+                        space.setInitialGroup(user)
+                else:
+                    space.manage_delLocalRoles(space.users_with_local_role('Owner'))
+                    self.setGroupOwnership(self.getGroupById(id), space)
+
+                # Hook to allow doing other things after grouparea creation.
+                notify_script = getattr(workspaces, 'notifyGroupAreaCreated', None)
+                if notify_script is not None:
+                    notify_script()
+
+                # Re-indexation
+                portal_catalog = getToolByName( self, 'portal_catalog' )
+                portal_catalog.reindexObject(space)
+ 
+    security.declareProtected(ManagePortal, 'getGroupWorkspaceType')
+    def getGroupWorkspaceType(self):
+        """Return the Type (as in TypesTool) to make the GroupWorkspace."""
+        return self.groupWorkspaceType
+
+    security.declareProtected(ManagePortal, 'setGroupWorkspaceType')
+    def setGroupWorkspaceType(self, type):
+        """Set the Type (as in TypesTool) to make the GroupWorkspace."""
+        self.groupWorkspaceType = type
+
+    security.declareProtected(ManagePortal, 'getGroupWorkspaceContainerType')
+    def getGroupWorkspaceContainerType(self):
+        """Return the Type (as in TypesTool) to make the GroupWorkspace."""
+        return self.groupWorkspaceContainerType
+
+    security.declareProtected(ManagePortal, 'setGroupWorkspaceContainerType')
+    def setGroupWorkspaceContainerType(self, type):
+        """Set the Type (as in TypesTool) to make the GroupWorkspace."""
+        self.groupWorkspaceContainerType = type
+
+    security.declarePublic('getGroupareaFolder')
+    def getGroupareaFolder(self, id=None, verifyPermission=0):
+        """Returns the object of the group's work area."""
+        if id is None:
+            group = self.getAuthenticatedMember()
+            if not hasattr(member, 'getGroupId'):
+                return None
+            id = group.getGroupId()
+        workspaces = self.getGroupWorkspacesFolder()
+        if workspaces:
+            try:
+                folder = workspaces[id]
+                if verifyPermission and not _checkPermission('View', folder):
+                    # Don't return the folder if the user can't get to it.
+                    return None
+                return folder
+            except KeyError: pass
+        return None
+
+    security.declarePublic('getGroupareaURL')
+    def getGroupareaURL(self, id=None, verifyPermission=0):
+        """Returns the full URL to the group's work area."""
+        ga = self.getGroupareaFolder(id, verifyPermission)
+        if ga is not None:
+            return ga.absolute_url()
+        else:
+            return None
+
+    security.declarePrivate('wrapGroup')
+    def wrapGroup(self, g, wrap_anon=0):
+        ''' Sets up the correct acquisition wrappers for a group
+        object and provides an opportunity for a portal_memberdata
+        tool to retrieve and store member data independently of
+        the user object.
+        '''
+        b = getattr(g, 'aq_base', None)
+        if b is None:
+            # u isn't wrapped at all.  Wrap it in self.acl_users.
+            b = g
+            g = g.__of__(self.acl_users)
+        if (b is nobody and not wrap_anon) or hasattr(b, 'getMemberId'):
+            # This user is either not recognized by acl_users or it is
+            # already registered with something that implements the
+            # member data tool at least partially.
+            return g
+
+        parent = self.aq_inner.aq_parent
+        base = getattr(parent, 'aq_base', None)
+        if hasattr(base, 'portal_groupdata'):
+            # Get portal_groupdata to do the wrapping.
+            Log(LOG_DEBUG, "parent", parent)
+            gd = getToolByName(parent, 'portal_groupdata')
+            Log(LOG_DEBUG, "group data", gd)
+            try:
+                #log("wrapping group %s" % g)
+                portal_group = gd.wrapGroup(g)
+                return portal_group
+            except ConflictError:
+                raise
+            except:
+                import logging
+                logger = logging.getLogger('GroupUserFolder.GroupsTool')
+                logger.exception('Error during wrapGroup')
+        # Failed.
+        return g
+
+InitializeClass(GroupsTool)
diff --git a/GroupsToolPermissions.py b/GroupsToolPermissions.py
new file mode 100644
index 0000000..bc6b1b6
--- /dev/null
+++ b/GroupsToolPermissions.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+## Copyright (c) 2003 The Connexions Project, All Rights Reserved
+## initially written by J Cameron Cooper, 11 June 2003
+## concept with Brent Hendricks, George Runyan
+"""
+Basic usergroup tool.
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: GroupsToolPermissions.py 30098 2006-09-08 12:35:01Z encolpe $
+__docformat__ = 'restructuredtext'
+
+# BBB CMF < 1.5
+try:
+    from Products.CMFCore.permissions import *
+except ImportError:
+    from Products.CMFCore.CMFCorePermissions import *
+
+AddGroups = 'Add Groups'
+setDefaultRoles(AddGroups, ('Manager',))
+
+ManageGroups = 'Manage Groups'
+setDefaultRoles(ManageGroups, ('Manager',))
+
+ViewGroups = 'View Groups'
+setDefaultRoles(ViewGroups, ('Manager', 'Owner', 'Member'))
+
+DeleteGroups = 'Delete Groups'
+setDefaultRoles(DeleteGroups, ('Manager', ))
+
+SetGroupOwnership = 'Set Group Ownership'
+setDefaultRoles(SetGroupOwnership, ('Manager', 'Owner'))
diff --git a/INSTALL.txt b/INSTALL.txt
new file mode 100644
index 0000000..7373ab1
--- /dev/null
+++ b/INSTALL.txt
@@ -0,0 +1,16 @@
+HOW TO INSTALL GRUF?
+
+  GRUF installs just like any other Zope product. Just untar it in your Products directory,
+  restart Zope, and you're done.
+
+HOW TO USE GRUF?
+
+  To enjoy groups within Zope, you just have to instansiate a GroupUserFolder instead of your
+  UserFolder. GRUF creates two default acl_users for you inside itself (one for Users and one
+  for Groups. see README.txt for technical explanation) but you can remove them and replace
+  them by other kind of User Folders: LDAPUserFolder, SQLUserFolder, SimpleUserFolder,
+  or whatever suits your needs.
+
+PLONE INSTALLATION
+
+  See README-Plone file for explanation on Plone installation.
diff --git a/Installation.py b/Installation.py
new file mode 100644
index 0000000..1be3578
--- /dev/null
+++ b/Installation.py
@@ -0,0 +1,247 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: Installation.py 30098 2006-09-08 12:35:01Z encolpe $
+__docformat__ = 'restructuredtext'
+
+
+from cStringIO import StringIO
+import string
+from Products.CMFCore.utils import getToolByName
+from Products.CMFCore.TypesTool import ContentFactoryMetadata
+from Products.CMFCore.DirectoryView import addDirectoryViews
+from Products.CMFPlone.migrations.migration_util import safeEditProperty
+
+class Installation:
+    def __init__(self, root):
+        self.root=root
+        self.out=StringIO()
+        self.typesTool = getToolByName(self.root, 'portal_types')
+        self.skinsTool = getToolByName(self.root, 'portal_skins')
+        self.portal_properties = getToolByName(self.root, 'portal_properties')
+        self.navigation_properties = self.portal_properties.navigation_properties
+        self.form_properties = self.portal_properties.form_properties
+
+    def report(self):
+        self.out.write('Installation completed.\n')
+        return self.out.getvalue()
+
+    def setupTools(self, product_name, tools):
+        addTool = self.root.manage_addProduct[product_name].manage_addTool
+        for tool, title in tools:
+            found = 0
+            for obj in self.root.objectValues():
+                if obj.meta_type == tool:
+                    found = 1
+            if not found:
+                addTool(tool, None)
+
+            found = 0
+            root=self.root
+            for obj in root.objectValues():
+                if obj.meta_type == tool:
+                    obj.title=title
+                    self.out.write("Added '%s' tool.\n" % (tool,))
+                    found = 1
+            if not found:
+                self.out.write("Couldn't add '%s' tool.\n" % (tool,))
+
+    def installSubSkin(self, skinFolder):
+        """ Install a subskin, i.e. a folder/directoryview.
+        """
+        for skin in self.skinsTool.getSkinSelections():
+            path = self.skinsTool.getSkinPath(skin)
+            path = map( string.strip, string.split( path,',' ) )
+            if not skinFolder in path:
+                try:
+                    path.insert( path.index( 'custom')+1, skinFolder )
+                except ValueError:
+                    path.append(skinFolder)
+                path = string.join( path, ', ' )
+                self.skinsTool.addSkinSelection( skin, path )
+                self.out.write('Subskin successfully installed into %s.\n' % skin)
+            else:
+                self.out.write('*** Subskin was already installed into %s.\n' % skin)
+
+    def setupCustomModelsSkin(self, skin_name):
+        """ Install custom skin folder
+        """
+        try:
+            self.skinsTool.manage_addProduct['OFSP'].manage_addFolder(skin_name + 'CustomModels')
+        except:
+            self.out.write('*** Skin %sCustomModels already existed in portal_skins.\n' % skin_name)
+        self.installSubSkin('%sCustomModels' % skin_name)
+
+    def setupTypesandSkins(self, fti_list, skin_name, install_globals):
+        """
+        setup of types and skins
+        """
+
+        # Former types deletion (added by PJG)
+        for f in fti_list:
+            if f['id'] in self.typesTool.objectIds():
+                self.out.write('*** Object "%s" already existed in the types tool => deleting\n' % (f['id']))
+                self.typesTool._delObject(f['id'])
+
+        # Type re-creation
+        for f in fti_list:
+            # Plone1 : if cmfformcontroller is not available and plone1_action key is defined,
+            # use this key instead of the regular 'action' key.
+            if (not self.hasFormController()) and f.has_key('plone1_action'):
+                f['action'] = f['plone1_action']
+
+            # Regular FTI processing
+            cfm = apply(ContentFactoryMetadata, (), f)
+            self.typesTool._setObject(f['id'], cfm)
+            self.out.write('Type "%s" registered with the types tool\n' % (f['id']))
+
+        # Install de chaque nouvelle subskin/layer
+        try:
+            addDirectoryViews(self.skinsTool, 'skins', install_globals)
+            self.out.write( "Added directory views to portal_skins.\n" )
+        except:
+            self.out.write( '*** Unable to add directory views to portal_skins.\n')
+
+        # Param de chaque nouvelle subskin/layer
+        self.installSubSkin(skin_name)
+
+    def isPlone2(self,):
+        """
+        isPlone2(self,) => return true if we're using Plone2 ! :-)
+        """
+        return self.hasFormController()
+
+    def hasFormController(self,):
+        """
+        hasFormController(self,) => Return 1 if CMFFC is available
+        """
+        if 'portal_form_controller' in self.root.objectIds():
+            return 1
+        else:
+            return None
+
+    def addFormValidators(self, mapping):
+        """
+        Adds the form validators.
+        DON'T ADD ANYTHING IF CMFFORMCONTROLLER IS INSTALLED
+        """
+        # Plone2 management
+        if self.hasFormController():
+            return
+        for (key, value) in mapping:
+            safeEditProperty(self.form_properties, key, value)
+
+    def addNavigationTransitions(self, transitions):
+        """
+        Adds Navigation Transitions in portal properties
+        """
+        # Plone2 management
+        if self.hasFormController():
+            return
+        for (key, value) in transitions:
+            safeEditProperty(self.navigation_properties, key, value)
+
+    def setPermissions(self, perms_list):
+        """
+        setPermissions(self) => Set standard permissions / roles
+        """
+        # As a default behavior, newly-created permissions are granted to owner and manager.
+        # To change this, just comment this code and grab back the code commented below to
+        # make it suit your needs.
+        for perm in perms_list:
+            self.root.manage_permission(
+                perm,
+                ('Manager', 'Owner'),
+                acquire = 1
+                )
+        self.out.write("Reseted default permissions\n")
+
+    def installMessageCatalog(self, plone, prodglobals, domain, poPrefix):
+        """Sets up the a message catalog for this product
+        according to the available languages in both:
+        - .pot files in the "i18n" folder of this product
+        - MessageCatalog available for this domain
+        Typical use, create below this function:
+        def installCatalog(self):
+            installMessageCatalog(self, Products.MyProduct, 'mydomain', 'potfile_')
+            return
+        This assumes that you set the domain 'mydomain' in 'translation_service'
+        and the .../Products/YourProduct/i18n/potfile_en.po (...) contain your messages.
+
+        @param plone: the plone site
+        @type plone: a 'Plone site' object
+        @param prodglobals: see PloneSkinRegistrar.__init__
+        @param domain: the domain nick in Plone 'translation_service'
+        @type domain: string or None for the default domain
+            (you shouldn't use the default domain)
+        @param poPrefix: .po files to use start with that prefix.
+            i.e. use 'foo_' to install words from 'foo_fr.po', 'foo_en.po' (...)
+        @type poPrefix: string
+        """
+
+        installInfo = (
+            "!! I18N INSTALLATION CANCELED !!\n"
+            "It seems that your Plone instance does not have the i18n features installed correctly.\n"
+            "You should have a 'translation_service' object in your Plone root.\n"
+            "This object should have the '%(domain)s' domain registered and associated\n"
+            "with an **existing** MessageCatalog object.\n"
+            "Fix all this first and come back here." % locals())
+        #
+        # Find Plone i18n resources
+        #
+        try:
+            ts = getattr(plone, 'translation_service')
+        except AttributeError, e:
+            return installInfo
+        found = 0
+        for nick, path in ts.getDomainInfo():
+            if nick == domain:
+                found = 1
+                break
+        if not found:
+            return installInfo
+        try:
+            mc = ts.restrictedTraverse(path)
+        except (AttributeError, KeyError), e:
+            return installInfo
+        self.out.write("Installing I18N messages into '%s'\n" % '/'.join(mc.getPhysicalPath()))
+        enabledLangs = [nick for nick, lang in mc.get_languages_tuple()]
+        self.out.write("This MessageCatalog has %s languages enabled.\n" %  ", ".join(enabledLangs))
+        #
+        # Find .po files
+        #
+        i18nPath = os.path.join(prodglobals['__path__'][0], 'i18n')
+        poPtn = os.path.join(i18nPath, poPrefix + '*.po')
+        poFiles = glob.glob(poPtn)
+        rxFindLanguage = re.compile(poPrefix +r'(.*)\.po')
+        poRsrc = {}
+        for file in poFiles:
+            k = rxFindLanguage.findall(file)[0]
+            poRsrc[k] = file
+        self.out.write("This Product provides messages for %s languages.\n" % ", ".join(poRsrc.keys()))
+        for lang in enabledLangs:
+            if poRsrc.has_key(lang):
+                self.out.write("Adding support for language %s.\n" % lang)
+                fh = open(poRsrc[lang])
+                mc.manage_import(lang, fh.read())
+                fh.close()
+        self.out.write("Done !")
diff --git a/LDAPGroupFolder.py b/LDAPGroupFolder.py
new file mode 100755
index 0000000..a269384
--- /dev/null
+++ b/LDAPGroupFolder.py
@@ -0,0 +1,393 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: LDAPGroupFolder.py 587 2008-07-31 09:20:06Z pin $
+__docformat__ = 'restructuredtext'
+
+import time, traceback
+
+# Zope imports
+from Globals import DTMLFile, InitializeClass
+from Acquisition import aq_base
+from AccessControl import ClassSecurityInfo
+from AccessControl.User import SimpleUser
+from AccessControl.Permissions import view_management_screens, manage_users
+from OFS.SimpleItem import SimpleItem
+from DateTime import DateTime
+
+from Products.GroupUserFolder import postonly
+import GroupUserFolder
+
+from global_symbols import *
+
+# LDAPUserFolder package imports
+from Products.LDAPUserFolder.SimpleCache import SimpleCache
+
+addLDAPGroupFolderForm = DTMLFile('dtml/addLDAPGroupFolder', globals())
+
+
+class LDAPGroupFolder(SimpleItem):
+    """ """
+    security = ClassSecurityInfo()
+
+    meta_type = 'LDAPGroupFolder'
+    id = 'acl_users'
+
+    isPrincipiaFolderish=1
+    isAUserFolder=1
+
+    manage_options=(
+        ({'label' : 'Groups', 'action' : 'manage_main',},)
+        + SimpleItem.manage_options
+        )
+
+    security.declareProtected(view_management_screens, 'manage_main')
+    manage_main = DTMLFile('dtml/groups', globals())
+    
+    
+    def __setstate__(self, v):
+        """ """
+        LDAPGroupFolder.inheritedAttribute('__setstate__')(self, v)
+        self._cache = SimpleCache()
+        self._cache.setTimeout(600)
+        self._cache.clear()
+
+    def __init__( self, title, luf=''):
+        """ """
+        self._luf = luf
+        self._cache = SimpleCache()
+        self._cache.setTimeout(600)
+        self._cache.clear()
+
+    security.declarePrivate(manage_users, 'getGRUF')
+    def getGRUF(self):
+        """ """
+        return self.aq_parent.aq_parent
+
+
+    security.declareProtected(manage_users, 'getLUF')
+    def getLUF(self):
+        """ """
+        s = self.getGRUF().getUserSource(self._luf)
+        if getattr(s, 'meta_type', None) != "LDAPUserFolder":
+            # whoops, we moved LDAPUF... let's try to find it back
+            Log(LOG_WARNING, "LDAPUserFolder moved. Trying to find it back.")
+            s = None
+            for src in self.getGRUF().listUserSources():
+                if src.meta_type == "LDAPUserFolder":
+                    self._luf = src.getPhysicalPath()[-2]
+                    s = src
+                    break
+            if not s:
+                raise RuntimeError, "You must change your groups source in GRUF if you do not have a LDAPUserFolder as a users source."
+        return s
+
+
+    security.declareProtected(manage_users, 'getGroups')
+    def getGroups(self, dn='*', attr=None, pwd=''):
+        """ """
+        return self.getLUF().getGroups(dn, attr, pwd)
+
+
+    security.declareProtected(manage_users, 'getGroupType')
+    def getGroupType(self, group_dn):
+        """ """
+        return self.getLUF().getGroupType(group_dn)
+
+    security.declareProtected(manage_users, 'getGroupMappings')
+    def getGroupMappings(self):
+        """ """
+        return self.getLUF().getGroupMappings()
+
+    security.declareProtected(manage_users, 'manage_addGroupMapping')
+    def manage_addGroupMapping(self, group_name, role_name, REQUEST=None):
+        """ """
+        self._cache.remove(group_name)
+        self.getLUF().manage_addGroupMapping(group_name, role_name, None)
+
+        if REQUEST:
+            msg = 'Added LDAP group to Zope role mapping: %s -> %s' % (
+                group_name, role_name)
+            return self.manage_main(manage_tabs_message=msg)
+    manage_addGroupMapping = postonly(manage_addGroupMapping)
+
+    security.declareProtected(manage_users, 'manage_deleteGroupMappings')
+    def manage_deleteGroupMappings(self, group_names, REQUEST=None):
+        """ Delete mappings from LDAP group to Zope role """
+        self._cache.clear()
+        self.getLUF().manage_deleteGroupMappings(group_names, None)
+        if REQUEST:
+            msg = 'Deleted LDAP group to Zope role mapping for: %s' % (
+                ', '.join(group_names))
+            return self.manage_main(manage_tabs_message=msg)
+    manage_deleteGroupMappings = postonly(manage_deleteGroupMappings)
+
+    security.declareProtected(manage_users, 'manage_addGroup')
+    def manage_addGroup( self
+                       , newgroup_name
+                       , newgroup_type='groupOfUniqueNames'
+                       , REQUEST=None
+                       ):
+        """Add a new group in groups_base.
+        """
+        self.getLUF().manage_addGroup(newgroup_name, newgroup_type, None)
+        
+        if REQUEST:
+            msg = 'Added new group %s' % (newgroup_name)
+            return self.manage_main(manage_tabs_message=msg)
+    manage_addGroup = postonly(manage_addGroup)
+
+    security.declareProtected(manage_users, 'manage_deleteGroups')
+    def manage_deleteGroups(self, dns=[], REQUEST=None):
+        """ Delete groups from groups_base """
+        self.getLUF().manage_deleteGroups(dns, None)
+        self._cache.clear()
+ 
+        if REQUEST:
+            msg = 'Deleted group(s):<br> %s' % '<br>'.join(dns)
+            return self.manage_main(manage_tabs_message=msg)
+    manage_deleteGroups = postonly(manage_deleteGroups)
+
+    security.declareProtected(manage_users, 'getUser')
+    def getUser(self, name):
+        """ """
+        # Prevent locally stored groups
+        luf = self.getLUF()
+        if luf._local_groups:
+            return []
+
+        # Get the group from the cache
+        user = self._cache.get(name, '')
+        if user:
+            return user
+
+        # Scan groups to find the proper user.
+        # THIS MAY BE EXPENSIVE AND HAS TO BE OPTIMIZED...
+        grps = self.getLUF().getGroups()
+        valid_roles = self.userFolderGetRoles()
+        dn = None
+        for n, g_dn in grps:
+            if n == name:
+                dn = g_dn
+                break
+        if not dn:
+            return None
+
+        # Current mapping
+        roles = self.getLUF()._mapRoles([name])
+
+        # Nested groups
+        groups = list(self.getLUF().getGroups(dn=dn, attr='cn', ))
+        roles.extend(self.getLUF()._mapRoles(groups))
+
+        # !!! start test
+        Log(LOG_DEBUG, name, "roles", groups, roles)
+        Log(LOG_DEBUG, name, "mapping", getattr(self.getLUF(), '_groups_mappings', {}))
+        # !!! end test
+
+        actual_roles = []
+        for r in roles:
+            if r in valid_roles:
+                actual_roles.append(r)
+            elif "%s%s" % (GROUP_PREFIX, r) in valid_roles:
+                actual_roles.append("%s%s" % (GROUP_PREFIX, r))
+        Log(LOG_DEBUG, name, "actual roles", actual_roles)
+        user = GroupUser(n, '', actual_roles, [])
+        self._cache.set(name, user)
+        return user
+        
+    security.declareProtected(manage_users, 'getUserNames')
+    def getUserNames(self):
+        """ """
+        Log(LOG_DEBUG, "getUserNames", )
+        LogCallStack(LOG_DEBUG)
+        # Prevent locally stored groups
+        luf = self.getLUF()
+        if luf._local_groups:
+            return []
+        return [g[0] for g in luf.getGroups()]
+
+    security.declareProtected(manage_users, 'getUsers')
+    def getUsers(self, authenticated=1):
+        """ """
+        # Prevent locally stored groups
+        luf = self.getLUF()
+        if luf._local_groups:
+            return []
+
+        data = []
+        
+        grps = self.getLUF().getGroups()
+        valid_roles = self.userFolderGetRoles()
+        for n, dn in grps:
+            # Group mapping
+            roles = self.getLUF()._mapRoles([n])
+            
+            # computation
+            actual_roles = []
+            for r in roles:
+                if r in valid_roles:
+                    actual_roles.append(r)
+                elif "%s%s" % (GROUP_PREFIX, r) in valid_roles:
+                    actual_roles.append("%s%s" % (GROUP_PREFIX, r))
+            user = GroupUser(n, '', actual_roles, [])
+            data.append(user)
+
+        return data
+
+    security.declarePrivate('_doAddUser')
+    def _doAddUser(self, name, password, roles, domains, **kw):
+        """WARNING: If a role with exists with the same name as the group, we do not add
+        the group mapping for it, but we create it as if it were a Zope ROLE.
+        Ie. it's not possible to have a GRUF Group name = a Zope role name, BUT,
+        with this system, it's possible to differenciate between LDAP groups and LDAP roles.
+        """
+        self.getLUF().manage_addGroup(name)
+        self.manage_addGroupMapping(name, "group_" + name, None, )
+        self._doChangeUser(name, password, roles, domains, **kw)
+
+    security.declarePrivate('_doDelUsers')
+    def _doDelUsers(self, names):
+        dns = []
+        luf = self.getLUF()
+        for g_name, dn in luf.getGroups():
+            if g_name in names:
+                dns.append(dn)
+        self._cache.clear()
+        return luf.manage_deleteGroups(dns)
+
+    security.declarePrivate('_doChangeUser')
+    def _doChangeUser(self, name, password, roles, domains, **kw):
+        """
+        This is used to change the groups (especially their roles).
+
+        [ THIS TEXT IS OUTDATED :
+          WARNING: If a ZOPE role with the same name as the GRUF group exists,
+          we do not add the group mapping for it, but we create it as if it were a Zope ROLE.
+          Ie. it's not possible to have a GRUF Group name = a Zope role name, BUT,
+          with this system, it's possible to differenciate between LDAP groups and LDAP roles.
+        ]
+        """
+        luf = self.getLUF()
+        self._cache.remove(name)
+
+        # Get group DN
+        dn = None
+        for g_name, g_dn in luf.getGroups():
+            if g_name == name:
+                dn = g_dn
+                break
+        if not dn:
+            raise ValueError, "Invalid LDAP group: '%s'" % (name, )
+                
+        # Edit group mappings
+##        if name in self.aq_parent.valid_roles():
+##            # This is, in fact, a role
+##            self.getLUF().manage_addGroupMapping(name, name)
+##        else:
+##            # This is a group -> we set it as a group
+##            self.getLUF().manage_addGroupMapping(name, self.getGroupPrefix() + name)
+
+        # Change roles
+        if luf._local_groups:
+            luf.manage_editUserRoles(dn, roles)
+        else:
+            # We have to transform roles into group dns: transform them as a dict
+            role_dns = []
+            all_groups = luf.getGroups()
+            all_roles = luf.valid_roles()
+            groups = {}
+            for g in all_groups:
+                groups[g[0]] = g[1]
+
+            # LDAPUF < 2.4Beta3 adds possibly invalid roles to the user roles
+            # (for example, adding the cn of a group additionnaly to the mapped zope role).
+            # So we must remove from our 'roles' list all roles which are prefixed by group prefix
+            # but are not actually groups.
+            # If a group has the same name as a role, we assume that it should be a _role_.
+            # We should check against group/role mapping here, but... well... XXX TODO !
+            # See "HERE IT IS" comment below.
+
+            # Scan roles we are asking for to manage groups correctly
+            for role in roles:
+                if not role in all_roles:
+                    continue                        # Do not allow propagation of invalid roles
+                if role.startswith(GROUP_PREFIX):
+                    role = role[GROUP_PREFIX_LEN:]            # Remove group prefix : groups are stored WITHOUT prefix in LDAP
+                    if role in all_roles:
+                        continue                            # HERE IT IS
+                r = groups.get(role, None)
+                if not r:
+                    Log(LOG_WARNING, "LDAP Server doesn't provide a '%s' group (asked for user '%s')." % (role, name, ))
+                    continue
+                role_dns.append(r)
+
+            # Perform the change
+            luf.manage_editGroupRoles(dn, role_dns)
+
+
+
+def manage_addLDAPGroupFolder( self, title = '', luf='', REQUEST=None):
+    """ """
+    this_folder = self.this()
+
+    if hasattr(aq_base(this_folder), 'acl_users') and REQUEST is not None:
+        msg = 'This+object+already+contains+a+User+Folder'
+
+    else:
+        # Try to guess where is LUF
+        if not luf:
+            for src in this_folder.listUserSources():
+                if src.meta_type == "LDAPUserFolder":
+                    luf = src.aq_parent.getId()
+
+        # No LUF found : error
+        if not luf:
+            raise KeyError, "You must be within GRUF with a LDAPUserFolder as one of your user sources."
+            
+        n = LDAPGroupFolder( title, luf )
+
+        this_folder._setObject('acl_users', n)
+        this_folder.__allow_groups__ = self.acl_users
+        
+        msg = 'Added+LDAPGroupFolder'
+ 
+    # return to the parent object's manage_main
+    if REQUEST:
+        url = REQUEST['URL1']
+        qs = 'manage_tabs_message=%s' % msg
+        REQUEST.RESPONSE.redirect('%s/manage_main?%s' % (url, qs))
+
+
+InitializeClass(LDAPGroupFolder)
+
+
+class GroupUser(SimpleUser):
+    """ """
+
+    def __init__(self, name, password, roles, domains):
+        SimpleUser.__init__(self, name, password, roles, domains)
+        self._created = time.time()
+
+    def getCreationTime(self):
+        """ """
+        return DateTime(self._created)
diff --git a/LDAPUserFolderAdapter.py b/LDAPUserFolderAdapter.py
new file mode 100755
index 0000000..642cdb8
--- /dev/null
+++ b/LDAPUserFolderAdapter.py
@@ -0,0 +1,215 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: LDAPUserFolderAdapter.py 587 2008-07-31 09:20:06Z pin $
+__docformat__ = 'restructuredtext'
+
+
+from global_symbols import *
+from Products.GroupUserFolder import postonly
+
+
+# These mandatory attributes are required by LDAP schema.
+# They will be filled with user name as a default value.
+# You have to provide a gruf_ldap_required_fields python script
+# in your Plone's skins if you want to override this.
+MANDATORY_ATTRIBUTES = ("sn", "cn", )
+
+
+def _doAddUser(self, name, password, roles, domains, **kw):
+    """
+    Special user adding method for use with LDAPUserFolder.
+    This will ensure parameters are correct for LDAP management
+    """
+    kwargs = {}               # We will pass this dict
+    attrs = {}
+
+    # Get gruf_ldap_required_fields result and fill in mandatory stuff
+    if hasattr(self, "gruf_ldap_required_fields"):
+        attrs = self.gruf_ldap_required_fields(login = name)
+    else:
+        for attr in MANDATORY_ATTRIBUTES:
+            attrs[attr] = name
+    kwargs.update(attrs)
+
+    # We assume that name is rdn attribute
+    rdn_attr = self._rdnattr
+    kwargs[rdn_attr] = name
+
+    # Manage password(s)
+    kwargs['user_pw'] = password
+    kwargs['confirm_pw'] = password
+
+    # Mangle roles
+    kwargs['user_roles'] = self._mangleRoles(name, roles)
+
+    # Delegate to LDAPUF default method
+    msg = self.manage_addUser(kwargs = kwargs)
+    if msg:
+        raise RuntimeError, msg
+
+
+def _doDelUsers(self, names):
+    """
+    Remove a bunch of users from LDAP.
+    We have to call manage_deleteUsers but, before, we need to find their dn.
+    """
+    dns = []
+    for name in names:
+        dns.append(self._find_user_dn(name))
+
+    self.manage_deleteUsers(dns)
+
+
+def _find_user_dn(self, name):
+    """
+    Convert a name to an LDAP dn
+    """
+    # Search records matching name
+    login_attr = self._login_attr
+    v = self.findUser(search_param = login_attr, search_term = name)
+
+    # Filter to keep exact matches only
+    v = filter(lambda x: x[login_attr] == name, v)
+
+    # Now, decide what to do
+    l = len(v)
+    if not l:
+        # Invalid name
+        raise "Invalid user name: '%s'" % (name, )
+    elif l > 1:
+        # Several records... don't know how to handle
+        raise "Duplicate user name for '%s'" % (name, )
+    return v[0]['dn']
+
+
+def _mangleRoles(self, name, roles):
+    """
+    Return role_dns for this user
+    """
+    # Local groups => the easiest part
+    if self._local_groups:
+        return roles
+
+    # We have to transform roles into group dns: transform them as a dict
+    role_dns = []
+    all_groups = self.getGroups()
+    all_roles = self.valid_roles()
+    groups = {}
+    for g in all_groups:
+        groups[g[0]] = g[1]
+
+    # LDAPUF does the mistake of adding possibly invalid roles to the user roles
+    # (for example, adding the cn of a group additionnaly to the mapped zope role).
+    # So we must remove from our 'roles' list all roles which are prefixed by group prefix
+    # but are not actually groups.
+    # See http://www.dataflake.org/tracker/issue_00376 for more information on that
+    # particular issue.
+    # If a group has the same name as a role, we assume that it should be a _role_.
+    # We should check against group/role mapping here, but... well... XXX TODO !
+    # See "HERE IT IS" comment below.
+
+    # Scan roles we are asking for to manage groups correctly
+    for role in roles:
+        if not role in all_roles:
+            continue                        # Do not allow propagation of invalid roles
+        if role.startswith(GROUP_PREFIX):
+            role = role[GROUP_PREFIX_LEN:]          # Remove group prefix : groups are stored WITHOUT prefix in LDAP
+            if role in all_roles:
+                continue                            # HERE IT IS
+        r = groups.get(role, None)
+        if not r:
+            Log(LOG_WARNING, "LDAP Server doesn't provide a '%s' group (required for user '%s')." % (role, name, ))
+        else:
+            role_dns.append(r)
+
+    return role_dns
+
+
+def _doChangeUser(self, name, password, roles, domains, **kw):
+    """
+    Update a user
+    """
+    # Find the dn at first
+    dn = self._find_user_dn(name)
+    
+    # Change password
+    if password is not None:
+        if password == '':
+            raise ValueError, "Password must not be empty for LDAP users."
+        self.manage_editUserPassword(dn, password)
+        
+    # Perform role change
+    self.manage_editUserRoles(dn, self._mangleRoles(name, roles))
+
+    # (No domain management with LDAP.)
+
+    
+def manage_editGroupRoles(self, user_dn, role_dns=[], REQUEST=None):
+    """ Edit the roles (groups) of a group """
+    from Products.LDAPUserFolder.utils import GROUP_MEMBER_MAP
+    try:
+        from Products.LDAPUserFolder.LDAPDelegate import ADD, DELETE
+    except ImportError:
+        # Support for LDAPUserFolder >= 2.6
+        ADD = self._delegate.ADD
+        DELETE = self._delegate.DELETE
+
+    msg = ""
+
+##    Log(LOG_DEBUG, "assigning", role_dns, "to", user_dn)
+    all_groups = self.getGroups(attr='dn')
+    cur_groups = self.getGroups(dn=user_dn, attr='dn')
+    group_dns = []
+    for group in role_dns:
+        if group.find('=') == -1:
+            group_dns.append('cn=%s,%s' % (group, self.groups_base))
+        else:
+            group_dns.append(group)
+
+    if self._local_groups:
+        if len(role_dns) == 0:
+            del self._groups_store[user_dn]
+        else:
+            self._groups_store[user_dn] = role_dns
+
+    else:
+        for group in all_groups:
+            member_attr = GROUP_MEMBER_MAP.get(self.getGroupType(group))
+
+            if group in cur_groups and group not in group_dns:
+                action = DELETE
+            elif group in group_dns and group not in cur_groups:
+                action = ADD
+            else:
+                action = None
+            if action is not None:
+                msg = self._delegate.modify(
+                    group
+                    , action
+                    , {member_attr : [user_dn]}
+                    )
+##                Log(LOG_DEBUG, "group", group, "subgroup", user_dn, "result", msg)
+
+    if msg:
+        raise RuntimeError, msg
+manage_editGroupRoles = postonly(manage_editGroupRoles)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..5fe2d42
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,57 @@
+Zope Public License (ZPL) Version 2.0
+-----------------------------------------------
+
+This software is Copyright (c) Ingeniweb (tm) and
+Contributors. All rights reserved.
+
+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 above
+   copyright notice, this list of conditions, and the following
+   disclaimer.
+
+2. Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions, and the following
+   disclaimer in the documentation and/or other materials
+   provided with the distribution.
+
+3. The name Ingeniweb (tm) must not be used to
+   endorse or promote products derived from this software
+   without prior written permission from Ingeniweb.
+
+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 Ingeniweb.
+
+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 INGENIWEB ``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 INGENIWEB OR ITS CONTRIBUTORS 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.
+
+
+This software consists of contributions made by Ingeniweb
+and many individuals on behalf of Ingeniweb.  
+Specific attributions are listed in the
+accompanying credits file.
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..5fe2d42
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,57 @@
+Zope Public License (ZPL) Version 2.0
+-----------------------------------------------
+
+This software is Copyright (c) Ingeniweb (tm) and
+Contributors. All rights reserved.
+
+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 above
+   copyright notice, this list of conditions, and the following
+   disclaimer.
+
+2. Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions, and the following
+   disclaimer in the documentation and/or other materials
+   provided with the distribution.
+
+3. The name Ingeniweb (tm) must not be used to
+   endorse or promote products derived from this software
+   without prior written permission from Ingeniweb.
+
+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 Ingeniweb.
+
+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 INGENIWEB ``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 INGENIWEB OR ITS CONTRIBUTORS 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.
+
+
+This software consists of contributions made by Ingeniweb
+and many individuals on behalf of Ingeniweb.  
+Specific attributions are listed in the
+accompanying credits file.
\ No newline at end of file
diff --git a/Log.py b/Log.py
new file mode 100644
index 0000000..9da05b5
--- /dev/null
+++ b/Log.py
@@ -0,0 +1,195 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+One can override the following variables :
+
+LOG_LEVEL : The log level, from 0 to 5.
+A Log level n implies all logs from 0 to n.
+LOG_LEVEL MUST BE OVERRIDEN !!!!!
+
+
+LOG_NONE = 0            => No log output
+LOG_CRITICAL = 1        => Critical problems (data consistency, module integrity, ...)
+LOG_ERROR = 2           => Error (runtime exceptions, ...)
+LOG_WARNING = 3         => Warning (non-blocking exceptions, ...)
+LOG_NOTICE = 4          => Notices (Special conditions, ...)
+LOG_DEBUG = 5           => Debug (Debugging information)
+
+
+LOG_PROCESSOR : A dictionnary holding, for each key, the data processor.
+A data processor is a function that takes only one parameter : the data to print.
+Default : LogFile for all keys.
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: Log.py 33389 2006-11-11 11:24:41Z shh42 $
+__docformat__ = 'restructuredtext'
+
+
+
+LOG_LEVEL = -1
+
+LOG_NONE = 0
+LOG_CRITICAL = 1
+LOG_ERROR = 2
+LOG_WARNING = 3
+LOG_NOTICE = 4
+LOG_DEBUG = 5
+
+from sys import stdout, stderr, exc_info
+import time
+import thread
+import threading
+import traceback
+import os
+import pprint
+import string
+
+LOG_STACK_DEPTH = [-2]
+
+def Log(level, *args):
+    """
+    Log(level, *args) => Pretty-prints data on the console with additional information.
+    """
+    if LOG_LEVEL and level <= LOG_LEVEL:
+        if not level in LOG_PROCESSOR.keys():
+            raise ValueError, "Invalid log level :", level
+
+        stack = ""
+        stackItems = traceback.extract_stack()
+        for depth in LOG_STACK_DEPTH:
+            stackItem = stackItems[depth]
+            stack = "%s%s:%s:" % (stack, os.path.basename(stackItem[0]), stackItem[1],)
+        pr = "%8s %s%s: " % (
+            LOG_LABEL[level],
+            stack,
+            time.ctime(time.time()),
+            )
+        for data in args:
+            try:
+                if "\n" in data:
+                    data = data
+                else:
+                    data = pprint.pformat(data)
+            except:
+                data = pprint.pformat(data)
+            pr = pr + data + " "
+
+        LOG_PROCESSOR[level](level, LOG_LABEL[level], pr, )
+
+def LogCallStack(level, *args):
+    """
+    LogCallStack(level, *args) => View the whole call stack for the specified call
+    """
+    if LOG_LEVEL and level <= LOG_LEVEL:
+        if not level in LOG_PROCESSOR.keys():
+            raise ValueError, "Invalid log level :", level
+
+        stack = string.join(traceback.format_list(traceback.extract_stack()[:-1]))
+        pr = "%8s %s:\n%s\n" % (
+            LOG_LABEL[level],
+            time.ctime(time.time()),
+            stack
+            )
+        for data in args:
+            try:
+                if "\n" in data:
+                    data = data
+                else:
+                    data = pprint.pformat(data)
+            except:
+                data = pprint.pformat(data)
+            pr = pr + data + " "
+
+        LOG_PROCESSOR[level](level, LOG_LABEL[level], pr, )
+
+
+
+def FormatStack(stack):
+    """
+    FormatStack(stack) => string
+
+    Return a 'loggable' version of the stack trace
+    """
+    ret = ""
+    for s in stack:
+        ret = ret + "%s:%s:%s: %s\n" % (os.path.basename(s[0]), s[1], s[2], s[3])
+    return ret
+
+
+def LogException():
+    """
+    LogException () => None
+
+    Print an exception information on the console
+    """
+    Log(LOG_NOTICE, "EXCEPTION >>>")
+    traceback.print_exc(file = LOG_OUTPUT)
+    Log(LOG_NOTICE, "<<< EXCEPTION")
+
+
+LOG_OUTPUT = stderr
+def LogFile(level, label, data, ):
+    """
+    LogFile : writes data to the LOG_OUTPUT file.
+    """
+    LOG_OUTPUT.write(data+'\n')
+    LOG_OUTPUT.flush()
+
+
+import logging
+
+CUSTOM_TRACE = 5
+logging.addLevelName('TRACE', CUSTOM_TRACE)
+
+zLogLevelConverter = {
+    LOG_NONE: CUSTOM_TRACE,
+    LOG_CRITICAL: logging.CRITICAL,
+    LOG_ERROR: logging.ERROR,
+    LOG_WARNING: logging.WARNING,
+    LOG_NOTICE: logging.INFO,
+    LOG_DEBUG: logging.DEBUG,
+    }
+
+def LogzLog(level, label, data, ):
+    """
+    LogzLog : writes data though Zope's logging facility
+    """
+    logger = logging.getLogger('GroupUserFolder')
+    logger.log(zLogLevelConverter[level], data + "\n", )
+
+
+
+LOG_PROCESSOR = {
+    LOG_NONE: LogzLog,
+    LOG_CRITICAL: LogzLog,
+    LOG_ERROR: LogzLog,
+    LOG_WARNING: LogzLog,
+    LOG_NOTICE: LogzLog,
+    LOG_DEBUG: LogFile,
+    }
+
+
+LOG_LABEL = {
+    LOG_NONE: "",
+    LOG_CRITICAL: "CRITICAL",
+    LOG_ERROR:    "ERROR   ",
+    LOG_WARNING:  "WARNING ",
+    LOG_NOTICE:   "NOTICE  ",
+    LOG_DEBUG:    "DEBUG   ",
+    }
diff --git a/PRODUCT_NAME b/PRODUCT_NAME
new file mode 100644
index 0000000..aaad7b8
--- /dev/null
+++ b/PRODUCT_NAME
@@ -0,0 +1 @@
+GroupUserFolder
diff --git a/PatchCatalogTool.py b/PatchCatalogTool.py
new file mode 100644
index 0000000..a9d54e9
--- /dev/null
+++ b/PatchCatalogTool.py
@@ -0,0 +1,23 @@
+"""
+$Id: PatchCatalogTool.py,v 1.3 2003/07/10 15:27:22 pjgrizel dead $
+"""
+
+try:
+    from Products.CMFCore.CatalogTool import CatalogTool
+except ImportError:
+    pass
+else:
+    if not hasattr(CatalogTool, '_old_listAllowedRolesAndUsers'):
+        def _listAllowedRolesAndUsers(self, user):
+            result = self._old_listAllowedRolesAndUsers(user)
+            getGroups = getattr(user, 'getGroups', None)
+            if getGroups is not None:
+                for group in getGroups():
+                    result.append('user:'+group)
+            return result
+
+        from zLOG import LOG, INFO
+        LOG('GroupUserFolder', INFO, 'Patching CatalogTool')
+
+        CatalogTool._old_listAllowedRolesAndUsers = CatalogTool._listAllowedRolesAndUsers
+        CatalogTool._listAllowedRolesAndUsers = _listAllowedRolesAndUsers
diff --git a/PloneFeaturePreview.py b/PloneFeaturePreview.py
new file mode 100755
index 0000000..0cf9854
--- /dev/null
+++ b/PloneFeaturePreview.py
@@ -0,0 +1,271 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+                                                                           
+                      GRUF3 Feature-preview stuff.                         
+                                                                           
+ This code shouldn't be here but allow people to preview advanced GRUF3    
+ features (eg. flexible LDAP searching in 'sharing' tab, ...) in Plone 2,  
+ without having to upgrade to Plone 2.1.
+                                                                           
+ Methods here are monkey-patched by now but will be provided directly by
+ Plone 2.1.
+ Please forgive this 'uglyness' but some users really want to have full    
+ LDAP support without switching to the latest Plone version ! ;)
+
+
+ BY DEFAULT, this thing IS enabled with Plone 2.0.x
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: PloneFeaturePreview.py 587 2008-07-31 09:20:06Z pin $
+__docformat__ = 'restructuredtext'
+
+from Products.CMFCore.utils import UniqueObject
+from Products.CMFCore.utils import getToolByName
+from OFS.SimpleItem import SimpleItem
+from OFS.Image import Image
+from Globals import InitializeClass, DTMLFile, MessageDialog
+from Acquisition import aq_base
+from AccessControl.User import nobody
+from AccessControl import ClassSecurityInfo
+from Products.CMFCore.ActionProviderBase import ActionProviderBase
+from interfaces.portal_groups import portal_groups as IGroupsTool
+from global_symbols import *
+
+
+# This is "stollen" from MembershipTool.py
+# this should probably be in MemberDataTool.py
+def searchForMembers( self, REQUEST=None, **kw ):
+    """
+    searchForMembers(self, REQUEST=None, **kw) => normal or fast search method.
+
+    The following properties can be provided:
+    - name
+    - email
+    - last_login_time
+    - roles
+
+    This is an 'AND' request.
+
+    If name is provided, then a _fast_ search is performed with GRUF's
+    searchUsersByName() method. This will improve performance.
+
+    In any other case, a regular (possibly _slow_) search is performed.
+    As it uses the listMembers() method, which is itself based on gruf.getUsers(),
+    this can return partial results. This may change in the future.
+    """
+    md = self.portal_memberdata
+    mt = self.portal_membership
+    if REQUEST:
+        dict = REQUEST
+    else:
+        dict = kw
+
+    # Attributes retreiving & mangling
+    name = dict.get('name', None)
+    email = dict.get('email', None)
+    roles = dict.get('roles', None)
+    last_login_time = dict.get('last_login_time', None)
+    is_manager = mt.checkPermission('Manage portal', self)
+    if name:
+        name = name.strip().lower()
+    if email:
+        email = email.strip().lower()
+
+
+    # We want 'name' request to be handled properly with large user folders.
+    # So we have to check both the fullname and loginname, without scanning all
+    # possible users.
+    md_users = None
+    uf_users = None
+    if name:
+        # We first find in MemberDataTool users whose _full_ name match what we want.
+        lst = md.searchMemberDataContents('fullname', name)
+        md_users = [ x['username'] for x in lst ]
+
+        # Fast search management if the underlying acl_users support it.
+        # This will allow us to retreive users by their _id_ (not name).
+        acl_users = self.acl_users
+        meth = getattr(acl_users, "searchUsersByName", None)
+        if meth:
+            uf_users = meth(name)           # gruf search
+
+    # Now we have to merge both lists to get a nice users set.
+    # This is possible only if both lists are filled (or we may miss users else).
+    Log(LOG_DEBUG, md_users, uf_users, )
+    members = []
+    if md_users is not None and uf_users is not None:
+        names_checked = 1
+        wrap = mt.wrapUser
+        getUser = acl_users.getUser
+        for userid in md_users:
+            members.append(wrap(getUser(userid)))
+        for userid in uf_users:
+            if userid in md_users:
+                continue             # Kill dupes
+            usr = getUser(userid)
+            if usr is not None:
+                members.append(wrap(usr))
+
+        # Optimization trick
+        if not email and \
+               not roles and \
+               not last_login_time:
+            return members          
+    else:
+        # If the lists are not available, we just stupidly get the members list
+        members = self.listMembers()
+        names_checked = 0
+
+    # Now perform individual checks on each user
+    res = []
+    portal = self.portal_url.getPortalObject()
+
+    for member in members:
+        #user = md.wrapUser(u)
+        u = member.getUser()
+        if not (member.listed or is_manager):
+            continue
+        if name and not names_checked:
+            if (u.getUserName().lower().find(name) == -1 and
+                member.getProperty('fullname').lower().find(name) == -1):
+                continue
+        if email:
+            if member.getProperty('email').lower().find(email) == -1:
+                continue
+        if roles:
+            user_roles = member.getRoles()
+            found = 0
+            for r in roles:
+                if r in user_roles:
+                    found = 1
+                    break
+            if not found:
+                continue
+        if last_login_time:
+            if member.last_login_time < last_login_time:
+                continue
+        res.append(member)
+    Log(LOG_DEBUG, res)
+    return res
+
+
+def listAllowedMembers(self,):
+    """listAllowedMembers => list only members which belong
+    to the same groups/roles as the calling user.
+    """
+    user = self.REQUEST['AUTHENTICATED_USER']
+    caller_roles = user.getRoles()              # Have to provide a hook for admins
+    current_members = self.listMembers()
+    allowed_members =[]
+    for member in current_members:
+        for role in caller_roles:
+            if role in member.getRoles():
+                allowed_members.append(member)
+                break
+    return allowed_members
+
+
+def _getPortrait(self, member_id):
+    """
+    return member_id's portrait if you can.
+    If it's not possible, just try to fetch a 'portait' property from the underlying user source,
+    then create a portrait from it.
+    """
+    # fetch the 'portrait' property
+    Log(LOG_DEBUG, "trying to fetch the portrait for the given member id")
+    portrait = self._former_getPortrait(member_id)
+    if portrait:
+        Log(LOG_DEBUG, "Returning the old-style portrait:", portrait, "for", member_id)
+        return portrait
+
+    # Try to find a portrait in the user source
+    member = self.portal_membership.getMemberById(member_id)
+    portrait = member.getUser().getProperty('portrait', None)
+    if not portrait:
+        Log(LOG_DEBUG, "No portrait available in the user source for", member_id)
+        return None
+
+    # Convert the user-source portrait into a plone-complyant one
+    Log(LOG_DEBUG, "Converting the portrait", type(portrait))
+    portrait = Image(id=member_id, file=portrait, title='')
+    membertool = self.portal_memberdata
+    membertool._setPortrait(portrait, member_id)
+
+    # Re-call ourself to retreive the real portrait
+    Log(LOG_DEBUG, "Returning the real portrait")
+    return self._former_getPortrait(member_id)
+
+
+def setLocalRoles( self, obj, member_ids, member_role, reindex=1 ):
+    """ Set local roles on an item """
+    member = self.getAuthenticatedMember()
+    gruf = self.acl_users
+    my_roles = member.getRolesInContext( obj )
+
+    if 'Manager' in my_roles or member_role in my_roles:
+        for member_id in member_ids:
+            u = gruf.getUserById(member_id) or gruf.getGroupByName(member_id)
+            if not u:
+                continue
+            member_id = u.getUserId()
+            roles = list(obj.get_local_roles_for_userid( userid=member_id ))
+
+            if member_role not in roles:
+                roles.append( member_role )
+                obj.manage_setLocalRoles( member_id, roles )
+
+    if reindex:
+        # It is assumed that all objects have the method
+        # reindexObjectSecurity, which is in CMFCatalogAware and
+        # thus PortalContent and PortalFolder.
+        obj.reindexObjectSecurity()
+
+def deleteLocalRoles( self, obj, member_ids, reindex=1 ):
+    """ Delete local roles for members member_ids """
+    member = self.getAuthenticatedMember()
+    my_roles = member.getRolesInContext( obj )
+    gruf = self.acl_users
+    member_ids = [
+        u.getUserId() for u in [
+            gruf.getUserById(u) or gruf.getGroupByName(u) for u in member_ids
+            ] if u
+        ]
+
+    if 'Manager' in my_roles or 'Owner' in my_roles:
+        obj.manage_delLocalRoles( userids=member_ids )
+
+    if reindex:
+        obj.reindexObjectSecurity()
+
+# Monkeypatch it !
+if PREVIEW_PLONE21_IN_PLONE20_:
+    from Products.CMFCore import MembershipTool as CMFCoreMembershipTool
+    CMFCoreMembershipTool.MembershipTool.setLocalRoles = setLocalRoles
+    CMFCoreMembershipTool.MembershipTool.deleteLocalRoles = deleteLocalRoles
+    from Products.CMFPlone import MemberDataTool
+    from Products.CMFPlone import MembershipTool
+    MembershipTool.MembershipTool.searchForMembers = searchForMembers
+    MembershipTool.MembershipTool.listAllowedMembers = listAllowedMembers
+    MemberDataTool.MemberDataTool._former_getPortrait = MemberDataTool.MemberDataTool._getPortrait
+    MemberDataTool.MemberDataTool._getPortrait = _getPortrait
+    Log(LOG_NOTICE, "Applied GRUF's monkeypatch over Plone 2.0.x. Enjoy!")
+
+
+
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..bf9168a
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,118 @@
+GroupUserFolder
+
+
+(c)2002-03-04 Ingeniweb
+
+
+
+(This is a structured-text formated file)
+
+
+
+ABSTRACT
+
+  GroupUserFolder is a kind of user folder that provides a special kind of user management.
+  Some users are "flagged" as GROUP and then normal users will be able to belong to one or
+  serveral groups.
+
+  See http://ingeniweb.sourceforge.net/Products/GroupUserFolder for detailed information.
+
+DOWNLOAD
+
+  See http://sourceforge.net/project/showfiles.php?group_id=55262&package_id=81576
+
+
+STRUCTURE
+
+  Group and "normal" User management is distinct. Here's a typical GroupUserFolder hierarchy::
+
+     - acl_users (GroupUserFolder)
+     |
+     |-- Users (GroupUserFolder-related class)
+     | |
+     | |-- acl_users (UserFolder or derived class)
+     |
+     |-- Groups (GroupUserFolder-related class)
+     | |
+     | |-- acl_users (UserFolder or derived class)
+
+
+  So, INSIDE the GroupUserFolder (or GRUF), there are 2 acl_users :
+
+    - The one in the 'Users' object manages real users
+
+    - The one in the 'Groups' object manages groups
+
+  The two acl_users are completely independants. They can even be of different kinds.
+  For example, a Zope UserFolder for Groups management and an LDAPUserFolder for Users management.
+
+  Inside the "Users" acl_users, groups are seen as ROLES (that's what we call "groles") so that 
+  roles can be assigned to users using the same storage as regular users. Groups are prefixed
+  by "group " so that they could be easily recognized within roles.
+
+  Then, on the top GroupUserFolder, groups and roles both are seen as users, and users have their
+  normal behaviour (ie. "groles" are not shown), except that users affected to one or several groups
+  have their roles extended with the roles affected to the groups they belong to.
+
+
+  Just for information : one user can belong to zero, one or more groups.
+  One group can have zero, one or more users affected.
+
+  [2003-05-10] There's currently no way to get a list of all users belonging to a particular group.
+
+
+GROUPS BEHAVIOUR
+
+  
+  ...will be documented soon...
+
+
+GRUF AND PLONE
+
+  See the dedicated README-Plone file.
+
+
+GRUF AND SimpleUserFolder
+
+  You might think there is a bug using GRUF with SimpleUserFolder (but there's not): if you create
+  a SimpleUserFolder within a GRUF a try to see it from the ZMI, you will get an InfiniteRecursionError.
+
+  That's because SimpleUserFolder tries to fetch a getUserNames() method and finds GRUF's one, which 
+  tries to call SimpleUserFolder's one which tries to fetch a getUserNames() method and finds GRUF's one, 
+  which tries to call SimpleUserFolder's one which tries to fetch a getUserNames() method and finds GRUF's one, 
+  which  tries to call SimpleUserFolder's one which tries to fetch a getUserNames() method and finds GRUF's 
+  one, which  tries to call SimpleUserFolder's one which tries to fetch a getUserNames() method and finds 
+  GRUF's one, which  tries to call SimpleUserFolder's one which tries to fetch a getUserNames() method and 
+  finds GRUF's one, which  tries to call SimpleUserFolder's one which tries (see what I mean ?)
+
+  To avoid this, just create a new_getUserNames() object (according to SimpleUserFolder specification) in the folder
+  where you put your SimpleUserFolder in (ie. one of 'Users' or 'Groups' folders).
+
+  GRUF also implies that the SimpleUserFolder methods you create are defined in the 'Users' or 'Groups' folder.
+  If you define them above in the ZODB hierarchy, they will never be acquired and GRUF ones will be catched
+  instead, causing infinite recursions.
+
+
+GRUF AND LDAPUserFolder
+
+  [NEW IN 3.0 VERSION: PLEASE READ README-LDAP.stx INSTEAD]
+
+BUGS
+
+  There is a bug using GRUF with Zope 2.5 and Plone 1.0Beta3 : when trying to join the plone site
+  as a new user, there is a Zope error "Unable to unpickle object"... I don't know how to fix that now.
+  With Zope 2.6 there is no such bug.
+
+DEBUG
+
+  If you put a file named 'debug.txt' in your GRUF's product directory, it will switch the product in
+  debug mode next time you restart Zope. This is the common behaviour for all Ingeniweb products.
+  Debug mode is normally just a way of printing more things on the console. But, with GRUF, debug
+  mode (since 3.1 version) enables a basic user source integrity check. If you've got a broken user 
+  folder product on your hard drive that you use as a source with GRUF, it will allow you to unlock
+  the situation.
+
+LICENCE
+
+  GRUF > 2 is released under the terms of the Zope Public Licence (ZPL). Specific arrangements can be found for closed-source projects : please contact us.
+
diff --git a/TESTED_WITH b/TESTED_WITH
new file mode 100644
index 0000000..5f4a809
--- /dev/null
+++ b/TESTED_WITH
@@ -0,0 +1,21 @@
+3.52 - 2006-05-30
+=================
+
+This version has been tested successfuly with the following products.
+It may not depend on all those products but if you experience problems you may track this down.
+
+  * GroupUserFolder 3.52
+
+
+
+3.51 - 2006-05-15
+=================
+
+This version has been tested successfuly with the following products.
+It may not depend on all those products but if you experience problems you may track this down.
+
+  * GroupUserFolder 3.51
+
+
+
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..924a8e9
--- /dev/null
+++ b/TODO
@@ -0,0 +1,82 @@
+TODO-LIST
+
+  * Virer lien cliquable onglet "Users" sur utilisateurs qui ne sont PAS dans
+    getUserNames()
+
+  * check caches ?
+
+  * Corriger le bug des arguments par défaut:
+
+    - grep -R "def.*= \[\]" *
+
+    - grep -R "def.*= {}" *
+
+    Cf.  http://www.ferg.org/projects/python_gotchas.html#bct_sec_5
+
+[v3.2]
+
+  * Reactivated cache expiration code (thanks to J.P. LADAGE)
+
+  * GRUF3 preview mode with Plone2.0.x
+
+
+[v3.1]
+
+  * Allow LocalRole blacklisting
+
+  * [Plone] Allow user property mutation: needs MembershipTool update !
+
+  * [ZMI] Add an "Add group/roles" and "Remove group/roles" along with the
+    current "Change" button on users/group view (thanks to Danny Bloemendaal)
+
+  * [ZMI] Improve ZMI for large users lists (batching, 'select all' buttons,
+    'expand all' for tree, ...)
+
+  * [CMF] Test within CMF (not only Plone)
+
+  * [ZMI] Improve users/groups admin screens:
+
+    - use thin borders for audit table and fix cell width
+
+    - add a 'toggle getUserNames()' button on 'Users' tab and use getUsers() by default
+
+[v3.0 => Planned 2003-06]
+
+  DONE * [LDAP] Improve group mapping for already existing groups
+
+  DONE * [Core] Implement join()/leave() methods (and logic!) on groups
+
+  DONE * [Core] Implement some feature to make LDAPUF roles/groups binding easier
+
+  DONE * Plone tools refactoring (user interface must support nested groups, cleaning necessary,
+    API refactoring necessary)
+
+  DONE * FIX DOCUMENTATION (especially README & INSTALLs)
+
+  DONE * Apply security on API methods
+
+  DONE * Check users overview : users disapear sometimes
+
+  DONE * Pass to ZPL licence
+
+  DONE * [CMF/Plone] Test & Document change_password
+
+  DONE * [CMF/Plone] Test & Document searchResults
+
+  DONE * [Doc] Document the whole GRUF API
+
+[v1.4 => Planned 2003-08-31]
+
+  DONE * [Core] Implement multi-UserFolder-sources
+
+  * [Core/ZMI] Implement something to list all members of a particular group
+    and put this view in individual group management screen.
+
+[v1.31 => Planned 2003-08-31]
+
+  DONE * [Core] Fix impossible group removing in users view
+
+  DONE * [ZMI] Optimize screens
+
+  DONE * [CMF/Plone] Fix groups loss when changing pw
+
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..e7603ac
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,128 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: __init__.py 40111 2007-04-01 09:12:57Z alecm $
+__docformat__ = 'restructuredtext'
+
+# postonly protections
+try:
+    # Zope 2.8.9, 2.9.7 and 2.10.3 (and up)
+    from AccessControl.requestmethod import postonly
+except ImportError:
+    try:
+        # Try the hotfix too
+        from Products.Hotfix_20070320 import postonly
+    except:
+        def postonly(callable): return callable
+
+
+import GroupUserFolder
+import GRUFFolder
+import PatchCatalogTool
+try:
+    import Products.LDAPUserFolder
+    hasLDAP = 1
+except ImportError:
+    hasLDAP = 0
+from global_symbols import *
+
+# Plone import try/except
+try:
+    from Products.CMFCore.DirectoryView import registerDirectory
+    import GroupsToolPermissions
+except:
+    # No registerdir available -> we ignore
+    pass
+
+# Used in Extension/install.py
+global groupuserfolder_globals
+groupuserfolder_globals=globals()
+
+# LDAPUserFolder patching
+if hasLDAP:
+    import LDAPGroupFolder
+    
+    def patch_LDAPUF():
+        # Now we can patch LDAPUF
+        from Products.LDAPUserFolder import LDAPUserFolder
+        import LDAPUserFolderAdapter
+        LDAPUserFolder._doAddUser = LDAPUserFolderAdapter._doAddUser
+        LDAPUserFolder._doDelUsers = LDAPUserFolderAdapter._doDelUsers
+        LDAPUserFolder._doChangeUser = LDAPUserFolderAdapter._doChangeUser
+        LDAPUserFolder._find_user_dn = LDAPUserFolderAdapter._find_user_dn
+        LDAPUserFolder.manage_editGroupRoles = LDAPUserFolderAdapter.manage_editGroupRoles
+        LDAPUserFolder._mangleRoles = LDAPUserFolderAdapter._mangleRoles
+
+    # Patch LDAPUF  : XXX FIXME: have to find something cleaner here?
+    patch_LDAPUF()
+
+def initialize(context):
+
+    try:
+        registerDirectory('skins', groupuserfolder_globals)
+    except:
+        # No registerdir available => we ignore
+        pass
+
+    context.registerClass(
+        GroupUserFolder.GroupUserFolder,
+        permission='Add GroupUserFolders',
+        constructors=(GroupUserFolder.manage_addGroupUserFolder,),
+        icon='www/GroupUserFolder.gif',
+        )
+
+    if hasLDAP:
+        context.registerClass(
+            LDAPGroupFolder.LDAPGroupFolder,
+            permission='Add GroupUserFolders',
+            constructors=(LDAPGroupFolder.addLDAPGroupFolderForm, LDAPGroupFolder.manage_addLDAPGroupFolder,),
+            icon='www/LDAPGroupFolder.gif',
+            )
+
+    context.registerClass(
+        GRUFFolder.GRUFUsers,
+        permission='Add GroupUserFolder',
+        constructors=(GRUFFolder.manage_addGRUFUsers,),
+        visibility=None,
+        icon='www/GRUFUsers.gif',
+        )
+
+    context.registerClass(
+        GRUFFolder.GRUFGroups,
+        permission='Add GroupUserFolder',
+        constructors=(GRUFFolder.manage_addGRUFGroups,),
+        visibility=None,
+        icon='www/GRUFGroups.gif',
+        )
+
+    try:
+        from Products.CMFCore.utils import ToolInit, ContentInit
+        from GroupsTool import GroupsTool
+        from GroupDataTool import GroupDataTool
+        ToolInit( meta_type='CMF Groups Tool'
+                  , tools=( GroupsTool, GroupDataTool, )
+                  , icon="tool.gif"
+                  ).initialize( context )
+
+    except ImportError:
+        Log(LOG_NOTICE, "Unable to import GroupsTool and/or GroupDataTool. \
+        This won't disable GRUF but if you use CMF/Plone you won't get benefit of its special features.")
diff --git a/class_utility.py b/class_utility.py
new file mode 100644
index 0000000..79357bd
--- /dev/null
+++ b/class_utility.py
@@ -0,0 +1,197 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: class_utility.py 30098 2006-09-08 12:35:01Z encolpe $
+__docformat__ = 'restructuredtext'
+
+import string
+import re
+import threading
+import string
+
+# Base classes global vars management
+_BASECLASSESLOCK = threading.RLock()
+_BASECLASSES = {}
+_BASEMETALOCK = threading.RLock()
+_BASEMETA = {}
+
+def showaq(self, indent=''):
+    "showaq"
+    rval = ""
+    obj = self
+    base = getattr(obj, 'aq_base', obj)
+    try: id = base.id
+    except: id = str(base)
+    try: id = id()
+    except: pass
+
+    if hasattr(obj, 'aq_self'):
+        if hasattr(obj.aq_self, 'aq_self'):
+            rval = rval + indent + "(" + id + ")\n"
+            rval = rval + indent + "|  \\\n"
+            rval = rval + showaq(obj.aq_self, '|   ' + indent)
+            rval = rval + indent + "|\n"
+        if hasattr(obj, 'aq_parent'):
+            rval = rval + indent + id + "\n"
+            rval = rval + indent + "|\n"
+            rval = rval + showaq(obj.aq_parent, indent)
+    else:
+        rval = rval + indent + id + "\n"
+    return rval
+
+
+def listBaseMetaTypes(cl, reverse = 0):
+    """
+    listBaseMetaTypes(cl, reverse = 0) => list of strings
+
+    List all base meta types for this class.
+    """
+    # Look for the class in _BASEMETA cache
+    try:
+        return _BASEMETA[cl][reverse]
+
+    except KeyError:
+        _populateBaseMetaTypes(cl)
+        return listBaseMetaTypes(cl, reverse)
+
+def isBaseMetaType(meta, cl):
+    try:
+        return _BASEMETA[cl][2].has_key(meta)
+
+    except KeyError:
+        _populateBaseMetaTypes(cl)
+        return isBaseMetaType(meta, cl)
+
+def _populateBaseMetaTypes(cl):
+    """Fill the base classes structure"""
+    # Fill the base classes list
+    try:
+        ret = [cl.meta_type]
+    except AttributeError:
+        ret = []
+
+    for b in cl.__bases__:
+        ret = list(listBaseMetaTypes(b, 1)) + ret
+
+    # Fill the base classes dict
+    bases = {}
+    for b in ret:
+        bases[b] = 1
+
+    _BASEMETALOCK.acquire()
+    try:
+        rev = ret[:]
+        rev.reverse()
+        _BASEMETA[cl] = (tuple(rev), tuple(ret), bases)
+    finally:
+        _BASEMETALOCK.release()
+
+def objectIds(container, meta_types = []):
+    """
+    """
+    return map(lambda x: x[0], objectItems(container, meta_types))
+
+def objectValues(container, meta_types = []):
+    """
+    """
+    return map(lambda x: x[1], objectItems(container, meta_types))
+
+def objectItems(container, meta_types = []):
+    """
+    objectItems(container, meta_types = [])
+    Same as a container's objectItem method, meta_types are scanned in the base classes too.
+    Ie. all objects derivated from Folder will be returned by objectItem(x, ['Folder'])
+    """
+    # Convert input type
+    if type(meta_types) not in (type(()), type([])):
+        meta_types = [meta_types]
+
+    # Special case where meta_types is empty
+    if not meta_types:
+        return container.objectItems()
+
+    # Otherwise : check parent for each meta_type
+    ret = []
+    for (id, obj) in container.objectItems():
+        for mt in meta_types:
+            if isBaseMetaType(mt, obj.__class__):
+                ret.append((id, obj))
+                break
+
+    return ret
+
+
+
+def listBaseClasses(cl, reverse = 0):
+    """
+    listBaseClasses(cl, reverse = 0) => list of classes
+
+    List all the base classes of an object.
+    When reverse is 0, return the self class first.
+    When reverse is 1, return the self class last.
+
+    WARNING : reverse is 0 or 1, it is an integer, NOT A BOOLEAN ! (optim issue)
+
+    CACHE RESULTS
+
+    WARNING : for optimization issues, the ORIGINAL tuple is returned : please do not change it !
+    """
+    # Look for the class in _BASECLASSES cache
+    try:
+        return _BASECLASSES[cl][reverse]
+
+    except:
+        _populateBaseClasses(cl)
+        return listBaseClasses(cl, reverse)
+
+
+def isBaseClass(base, cl):
+    """
+    isBaseClass(base, cl) => Boolean
+    Return true if base is a base class of cl
+    """
+    try:
+        return _BASECLASSES[cl][2].has_key(base)
+    except:
+        _populateBaseClasses(cl)
+        return isBaseClass(base, cl)
+
+
+def _populateBaseClasses(cl):
+    """Fill the base classes structure"""
+    # Fill the base classes list
+    ret = [cl]
+    for b in cl.__bases__:
+        ret = list(listBaseClasses(b, 1)) + ret
+
+    # Fill the base classes dict
+    bases = {}
+    for b in ret:
+        bases[b] = 1
+
+    _BASECLASSESLOCK.acquire()
+    try:
+        rev = ret[:]
+        rev.reverse()
+        _BASECLASSES[cl] = (tuple(rev), tuple(ret), bases)
+    finally:
+        _BASECLASSESLOCK.release()
diff --git a/cvs2cl.pl b/cvs2cl.pl
new file mode 100755
index 0000000..51371b9
--- /dev/null
+++ b/cvs2cl.pl
@@ -0,0 +1,1995 @@
+#!/bin/sh
+exec perl -w -x $0 ${1+"$@"} # -*- mode: perl; perl-indent-level: 2; -*-
+#!perl -w
+
+
+##############################################################
+###                                                        ###
+### cvs2cl.pl: produce ChangeLog(s) from `cvs log` output. ###
+###                                                        ###
+##############################################################
+
+## $Revision: 1.2 $
+## $Date: 2005-08-19 23:51:07 +0200 (ven, 19 aoû 2005) $
+## $Author: dreamcatcher $
+##
+##   (C) 2001,2002,2003 Martyn J. Pearce <fluffy@cpan.org>, under the GNU GPL.
+##   (C) 1999 Karl Fogel <kfogel@red-bean.com>, under the GNU GPL.
+##
+##   (Extensively hacked on by Melissa O'Neill <oneill@cs.sfu.ca>.)
+##
+## cvs2cl.pl is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2, or (at your option)
+## any later version.
+##
+## cvs2cl.pl is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You may have received a copy of the GNU General Public License
+## along with cvs2cl.pl; see the file COPYING.  If not, write to the
+## Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+## Boston, MA 02111-1307, USA.
+
+
+use strict;
+use Text::Wrap;
+use Time::Local;
+use File::Basename;
+
+
+# The Plan:
+#
+# Read in the logs for multiple files, spit out a nice ChangeLog that
+# mirrors the information entered during `cvs commit'.
+#
+# The problem presents some challenges. In an ideal world, we could
+# detect files with the same author, log message, and checkin time --
+# each <filelist, author, time, logmessage> would be a changelog entry.
+# We'd sort them; and spit them out.  Unfortunately, CVS is *not atomic*
+# so checkins can span a range of times.  Also, the directory structure
+# could be hierarchical.
+#
+# Another question is whether we really want to have the ChangeLog
+# exactly reflect commits. An author could issue two related commits,
+# with different log entries, reflecting a single logical change to the
+# source. GNU style ChangeLogs group these under a single author/date.
+# We try to do the same.
+#
+# So, we parse the output of `cvs log', storing log messages in a
+# multilevel hash that stores the mapping:
+#   directory => author => time => message => filelist
+# As we go, we notice "nearby" commit times and store them together
+# (i.e., under the same timestamp), so they appear in the same log
+# entry.
+#
+# When we've read all the logs, we twist this mapping into
+# a time => author => message => filelist mapping for each directory.
+#
+# If we're not using the `--distributed' flag, the directory is always
+# considered to be `./', even as descend into subdirectories.
+
+
+############### Globals ################
+
+# What we run to generate it:
+my $Log_Source_Command = "cvs log";
+
+# In case we have to print it out:
+my $VERSION = '$Revision: 1.2 $';
+$VERSION =~ s/\S+\s+(\S+)\s+\S+/$1/;
+
+## Vars set by options:
+
+# Print debugging messages?
+my $Debug = 0;
+
+# Just show version and exit?
+my $Print_Version = 0;
+
+# Just print usage message and exit?
+my $Print_Usage = 0;
+
+# Single top-level ChangeLog, or one per subdirectory?
+my $Distributed = 0;
+
+# What file should we generate (defaults to "ChangeLog")?
+my $Log_File_Name = "ChangeLog";
+
+# Grab most recent entry date from existing ChangeLog file, just add
+# to that ChangeLog.
+my $Cumulative = 0;
+
+# Expand usernames to email addresses based on a map file?
+my $User_Map_File = "";
+
+# Output to a file or to stdout?
+my $Output_To_Stdout = 0;
+
+# Eliminate empty log messages?
+my $Prune_Empty_Msgs = 0;
+
+# Tags of which not to output
+my @ignore_tags;
+
+# Don't call Text::Wrap on the body of the message
+my $No_Wrap = 0;
+
+# Separates header from log message.  Code assumes it is either " " or
+# "\n\n", so if there's ever an option to set it to something else,
+# make sure to go through all conditionals that use this var.
+my $After_Header = " ";
+
+# XML Encoding
+my $XML_Encoding = '';
+
+# Format more for programs than for humans.
+my $XML_Output = 0;
+
+# Do some special tweaks for log data that was written in FSF
+# ChangeLog style.
+my $FSF_Style = 0;
+
+# Show times in UTC instead of local time
+my $UTC_Times = 0;
+
+# Show times in output?
+my $Show_Times = 1;
+
+# Show day of week in output?
+my $Show_Day_Of_Week = 0;
+
+# Show revision numbers in output?
+my $Show_Revisions = 0;
+
+# Show tags (symbolic names) in output?
+my $Show_Tags = 0;
+
+# Show tags separately in output?
+my $Show_Tag_Dates = 0;
+
+# Show branches by symbolic name in output?
+my $Show_Branches = 0;
+
+# Show only revisions on these branches or their ancestors.
+my @Follow_Branches;
+
+# Don't bother with files matching this regexp.
+my @Ignore_Files;
+
+# How exactly we match entries.  We definitely want "o",
+# and user might add "i" by using --case-insensitive option.
+my $Case_Insensitive = 0;
+
+# Maybe only show log messages matching a certain regular expression.
+my $Regexp_Gate = "";
+
+# Pass this global option string along to cvs, to the left of `log':
+my $Global_Opts = "";
+
+# Pass this option string along to the cvs log subcommand:
+my $Command_Opts = "";
+
+# Read log output from stdin instead of invoking cvs log?
+my $Input_From_Stdin = 0;
+
+# Don't show filenames in output.
+my $Hide_Filenames = 0;
+
+# Max checkin duration. CVS checkin is not atomic, so we may have checkin
+# times that span a range of time. We assume that checkins will last no
+# longer than $Max_Checkin_Duration seconds, and that similarly, no
+# checkins will happen from the same users with the same message less
+# than $Max_Checkin_Duration seconds apart.
+my $Max_Checkin_Duration = 180;
+
+# What to put at the front of [each] ChangeLog.
+my $ChangeLog_Header = "";
+
+# Whether to enable 'delta' mode, and for what start/end tags.
+my $Delta_Mode = 0;
+my $Delta_From = "";
+my $Delta_To = "";
+
+## end vars set by options.
+
+# latest observed times for the start/end tags in delta mode
+my $Delta_StartTime = 0;
+my $Delta_EndTime = 0;
+
+# In 'cvs log' output, one long unbroken line of equal signs separates
+# files:
+my $file_separator = "======================================="
+                   . "======================================";
+
+# In 'cvs log' output, a shorter line of dashes separates log messages
+# within a file:
+my $logmsg_separator = "----------------------------";
+
+############### End globals ############
+
+
+&parse_options ();
+&derive_change_log ();
+
+
+### Everything below is subroutine definitions. ###
+
+# If accumulating, grab the boundary date from pre-existing ChangeLog.
+sub maybe_grab_accumulation_date ()
+{
+  if (! $Cumulative) {
+    return "";
+  }
+
+  # else
+
+  open (LOG, "$Log_File_Name")
+      or die ("trouble opening $Log_File_Name for reading ($!)");
+
+  my $boundary_date;
+  while (<LOG>)
+  {
+    if (/^(\d\d\d\d-\d\d-\d\d\s+\d\d:\d\d)/)
+    {
+      $boundary_date = "$1";
+      last;
+    }
+  }
+
+  close (LOG);
+  return $boundary_date;
+}
+
+# Fills up a ChangeLog structure in the current directory.
+sub derive_change_log ()
+{
+  # See "The Plan" above for a full explanation.
+
+  my %grand_poobah;
+
+  my $file_full_path;
+  my $time;
+  my $revision;
+  my $author;
+  my $msg_txt;
+  my $detected_file_separator;
+
+  my %tag_date_printed;
+
+  # Might be adding to an existing ChangeLog
+  my $accumulation_date = &maybe_grab_accumulation_date ();
+  if ($accumulation_date) {
+    # Insert -d immediately after 'cvs log'
+    my $Log_Date_Command = "-d\'>${accumulation_date}\'";
+    $Log_Source_Command =~ s/(^.*log\S*)/$1 $Log_Date_Command/;
+    &debug ("(adding log msg starting from $accumulation_date)\n");
+  }
+
+  # We might be expanding usernames
+  my %usermap;
+
+  # In general, it's probably not very maintainable to use state
+  # variables like this to tell the loop what it's doing at any given
+  # moment, but this is only the first one, and if we never have more
+  # than a few of these, it's okay.
+  my $collecting_symbolic_names = 0;
+  my %symbolic_names;    # Where tag names get stored.
+  my %branch_names;      # We'll grab branch names while we're at it.
+  my %branch_numbers;    # Save some revisions for @Follow_Branches
+  my @branch_roots;      # For showing which files are branch ancestors.
+
+  # Bleargh.  Compensate for a deficiency of custom wrapping.
+  if (($After_Header ne " ") and $FSF_Style)
+  {
+    $After_Header .= "\t";
+  }
+
+  if (! $Input_From_Stdin) {
+    &debug ("(run \"${Log_Source_Command}\")\n");
+    open (LOG_SOURCE, "$Log_Source_Command |")
+        or die "unable to run \"${Log_Source_Command}\"";
+  }
+  else {
+    open (LOG_SOURCE, "-") or die "unable to open stdin for reading";
+  }
+
+  binmode LOG_SOURCE;
+
+  %usermap = &maybe_read_user_map_file ();
+
+  while (<LOG_SOURCE>)
+  {
+    # Canonicalize line endings
+    s/\r$//;
+    # If on a new file and don't see filename, skip until we find it, and
+    # when we find it, grab it.
+    if ((! (defined $file_full_path)) and /^Working file: (.*)/)
+    {
+      $file_full_path = $1;
+      if (@Ignore_Files)
+      {
+        my $base;
+        ($base, undef, undef) = fileparse ($file_full_path);
+        # Ouch, I wish trailing operators in regexps could be
+        # evaluated on the fly!
+        if ($Case_Insensitive) {
+          if (grep ($file_full_path =~ m|$_|i, @Ignore_Files)) {
+            undef $file_full_path;
+          }
+        }
+        elsif (grep ($file_full_path =~ m|$_|, @Ignore_Files)) {
+          undef $file_full_path;
+        }
+      }
+      next;
+    }
+
+    # Just spin wheels if no file defined yet.
+    next if (! $file_full_path);
+
+    # Collect tag names in case we're asked to print them in the output.
+    if (/^symbolic names:$/) {
+      $collecting_symbolic_names = 1;
+      next;  # There's no more info on this line, so skip to next
+    }
+    if ($collecting_symbolic_names)
+    {
+      # All tag names are listed with whitespace in front in cvs log
+      # output; so if see non-whitespace, then we're done collecting.
+      if (/^\S/) {
+        $collecting_symbolic_names = 0;
+      }
+      else    # we're looking at a tag name, so parse & store it
+      {
+        # According to the Cederqvist manual, in node "Tags", tag
+        # names must start with an uppercase or lowercase letter and
+        # can contain uppercase and lowercase letters, digits, `-',
+        # and `_'.  However, it's not our place to enforce that, so
+        # we'll allow anything CVS hands us to be a tag:
+        /^\s+([^:]+): ([\d.]+)$/;
+        my $tag_name = $1;
+        my $tag_rev  = $2;
+
+        # A branch number either has an odd number of digit sections
+        # (and hence an even number of dots), or has ".0." as the
+        # second-to-last digit section.  Test for these conditions.
+        my $real_branch_rev = "";
+        if (($tag_rev =~ /^(\d+\.\d+\.)+\d+$/)   # Even number of dots...
+            and (! ($tag_rev =~ /^(1\.)+1$/)))   # ...but not "1.[1.]1"
+        {
+          $real_branch_rev = $tag_rev;
+        }
+        elsif ($tag_rev =~ /(\d+\.(\d+\.)+)0.(\d+)/)  # Has ".0."
+        {
+          $real_branch_rev = $1 . $3;
+        }
+        # If we got a branch, record its number.
+        if ($real_branch_rev)
+        {
+          $branch_names{$real_branch_rev} = $tag_name;
+          if (@Follow_Branches) {
+            if (grep ($_ eq $tag_name, @Follow_Branches)) {
+              $branch_numbers{$tag_name} = $real_branch_rev;
+            }
+          }
+        }
+        else {
+          # Else it's just a regular (non-branch) tag.
+          push (@{$symbolic_names{$tag_rev}}, $tag_name);
+        }
+      }
+    }
+    # End of code for collecting tag names.
+
+    # If have file name, but not revision, and see revision, then grab
+    # it.  (We collect unconditionally, even though we may or may not
+    # ever use it.)
+    if ((! (defined $revision)) and (/^revision (\d+\.[\d.]+)/))
+    {
+      $revision = $1;
+
+      if (@Follow_Branches)
+      {
+        foreach my $branch (@Follow_Branches)
+        {
+          # Special case for following trunk revisions
+          if (($branch =~ /^trunk$/i) and ($revision =~ /^[0-9]+\.[0-9]+$/))
+          {
+            goto dengo;
+          }
+
+          my $branch_number = $branch_numbers{$branch};
+          if ($branch_number)
+          {
+            # Are we on one of the follow branches or an ancestor of
+            # same?
+            #
+            # If this revision is a prefix of the branch number, or
+            # possibly is less in the minormost number, OR if this
+            # branch number is a prefix of the revision, then yes.
+            # Otherwise, no.
+            #
+            # So below, we determine if any of those conditions are
+            # met.
+
+            # Trivial case: is this revision on the branch?
+            # (Compare this way to avoid regexps that screw up Emacs
+            # indentation, argh.)
+            if ((substr ($revision, 0, ((length ($branch_number)) + 1)))
+                eq ($branch_number . "."))
+            {
+              goto dengo;
+            }
+            # Non-trivial case: check if rev is ancestral to branch
+            elsif ((length ($branch_number)) > (length ($revision)))
+            {
+              $revision =~ /^((?:\d+\.)+)(\d+)$/;
+              my $r_left = $1;          # still has the trailing "."
+              my $r_end = $2;
+
+              $branch_number =~ /^((?:\d+\.)+)(\d+)\.\d+$/;
+              my $b_left = $1;  # still has trailing "."
+              my $b_mid  = $2;   # has no trailing "."
+
+              if (($r_left eq $b_left)
+                  && ($r_end <= $b_mid))
+              {
+                goto dengo;
+              }
+            }
+          }
+        }
+      }
+      else    # (! @Follow_Branches)
+      {
+        next;
+      }
+
+      # Else we are following branches, but this revision isn't on the
+      # path.  So skip it.
+      undef $revision;
+    dengo:
+      next;
+    }
+
+    # If we don't have a revision right now, we couldn't possibly
+    # be looking at anything useful.
+    if (! (defined ($revision))) {
+      $detected_file_separator = /^$file_separator$/o;
+      if ($detected_file_separator) {
+        # No revisions for this file; can happen, e.g. "cvs log -d DATE"
+        goto CLEAR;
+      }
+      else {
+        next;
+      }
+    }
+
+    # If have file name but not date and author, and see date or
+    # author, then grab them:
+    unless (defined $time)
+    {
+      if (/^date: .*/)
+      {
+        ($time, $author) = &parse_date_and_author ($_);
+        if (defined ($usermap{$author}) and $usermap{$author}) {
+          $author = $usermap{$author};
+        }
+      }
+      else {
+        $detected_file_separator = /^$file_separator$/o;
+        if ($detected_file_separator) {
+          # No revisions for this file; can happen, e.g. "cvs log -d DATE"
+          goto CLEAR;
+        }
+      }
+      # If the date/time/author hasn't been found yet, we couldn't
+      # possibly care about anything we see.  So skip:
+      next;
+    }
+
+    # A "branches: ..." line here indicates that one or more branches
+    # are rooted at this revision.  If we're showing branches, then we
+    # want to show that fact as well, so we collect all the branches
+    # that this is the latest ancestor of and store them in
+    # @branch_roots.  Just for reference, the format of the line we're
+    # seeing at this point is:
+    #
+    #    branches:  1.5.2;  1.5.4;  ...;
+    #
+    # Okay, here goes:
+
+    if (/^branches:\s+(.*);$/)
+    {
+      if ($Show_Branches)
+      {
+        my $lst = $1;
+        $lst =~ s/(1\.)+1;|(1\.)+1$//;  # ignore the trivial branch 1.1.1
+        if ($lst) {
+          @branch_roots = split (/;\s+/, $lst);
+        }
+        else {
+          undef @branch_roots;
+        }
+        next;
+      }
+      else
+      {
+        # Ugh.  This really bothers me.  Suppose we see a log entry
+        # like this:
+        #
+        #    ----------------------------
+        #    revision 1.1
+        #    date: 1999/10/17 03:07:38;  author: jrandom;  state: Exp;
+        #    branches:  1.1.2;
+        #    Intended first line of log message begins here.
+        #    ----------------------------
+        #
+        # The question is, how we can tell the difference between that
+        # log message and a *two*-line log message whose first line is
+        #
+        #    "branches:  1.1.2;"
+        #
+        # See the problem?  The output of "cvs log" is inherently
+        # ambiguous.
+        #
+        # For now, we punt: we liberally assume that people don't
+        # write log messages like that, and just toss a "branches:"
+        # line if we see it but are not showing branches.  I hope no
+        # one ever loses real log data because of this.
+        next;
+      }
+    }
+
+    # If have file name, time, and author, then we're just grabbing
+    # log message texts:
+    $detected_file_separator = /^$file_separator$/o;
+    if ($detected_file_separator && ! (defined $revision)) {
+      # No revisions for this file; can happen, e.g. "cvs log -d DATE"
+      goto CLEAR;
+    }
+    unless ($detected_file_separator || /^$logmsg_separator$/o)
+    {
+      $msg_txt .= $_;   # Normally, just accumulate the message...
+      next;
+    }
+    # ... until a msg separator is encountered:
+    # Ensure the message contains something:
+    if ((! $msg_txt)
+        || ($msg_txt =~ /^\s*\.\s*$|^\s*$/)
+        || ($msg_txt =~ /\*\*\* empty log message \*\*\*/))
+    {
+      if ($Prune_Empty_Msgs) {
+        goto CLEAR;
+      }
+      # else
+      $msg_txt = "[no log message]\n";
+    }
+
+    ### Store it all in the Grand Poobah:
+    {
+      my $dir_key;        # key into %grand_poobah
+      my %qunk;           # complicated little jobbie, see below
+
+      # Each revision of a file has a little data structure (a `qunk')
+      # associated with it.  That data structure holds not only the
+      # file's name, but any additional information about the file
+      # that might be needed in the output, such as the revision
+      # number, tags, branches, etc.  The reason to have these things
+      # arranged in a data structure, instead of just appending them
+      # textually to the file's name, is that we may want to do a
+      # little rearranging later as we write the output.  For example,
+      # all the files on a given tag/branch will go together, followed
+      # by the tag in parentheses (so trunk or otherwise non-tagged
+      # files would go at the end of the file list for a given log
+      # message).  This rearrangement is a lot easier to do if we
+      # don't have to reparse the text.
+      #
+      # A qunk looks like this:
+      #
+      #   {
+      #     filename    =>    "hello.c",
+      #     revision    =>    "1.4.3.2",
+      #     time        =>    a timegm() return value (moment of commit)
+      #     tags        =>    [ "tag1", "tag2", ... ],
+      #     branch      =>    "branchname" # There should be only one, right?
+      #     branchroots =>    [ "branchtag1", "branchtag2", ... ]
+      #   }
+
+      if ($Distributed) {
+        # Just the basename, don't include the path.
+        ($qunk{'filename'}, $dir_key, undef) = fileparse ($file_full_path);
+      }
+      else {
+        $dir_key = "./";
+        $qunk{'filename'} = $file_full_path;
+      }
+
+      # This may someday be used in a more sophisticated calculation
+      # of what other files are involved in this commit.  For now, we
+      # don't use it much except for delta mode, because the
+      # common-commit-detection algorithm is hypothesized to be
+      # "good enough" as it stands.
+      $qunk{'time'} = $time;
+
+      # We might be including revision numbers and/or tags and/or
+      # branch names in the output.  Most of the code from here to
+      # loop-end deals with organizing these in qunk.
+
+      $qunk{'revision'} = $revision;
+
+      # Grab the branch, even though we may or may not need it:
+      $qunk{'revision'} =~ /((?:\d+\.)+)\d+/;
+      my $branch_prefix = $1;
+      $branch_prefix =~ s/\.$//;  # strip off final dot
+      if ($branch_names{$branch_prefix}) {
+        $qunk{'branch'} = $branch_names{$branch_prefix};
+      }
+
+      # If there's anything in the @branch_roots array, then this
+      # revision is the root of at least one branch.  We'll display
+      # them as branch names instead of revision numbers, the
+      # substitution for which is done directly in the array:
+      if (@branch_roots) {
+        my @roots = map { $branch_names{$_} } @branch_roots;
+        $qunk{'branchroots'} = \@roots;
+      }
+
+      # Save tags too.
+      if (defined ($symbolic_names{$revision})) {
+        $qunk{'tags'} = $symbolic_names{$revision};
+        delete $symbolic_names{$revision};
+
+	# If we're in 'delta' mode, update the latest observed
+	# times for the beginning and ending tags, and
+	# when we get around to printing output, we will simply restrict
+	# ourselves to that timeframe...
+	
+	if ($Delta_Mode) {
+	  if (($time > $Delta_StartTime) &&
+	      (grep { $_ eq $Delta_From } @{$qunk{'tags'}}))
+	  {
+	    $Delta_StartTime = $time;
+	  }
+	  
+	  if (($time > $Delta_EndTime) &&
+	      (grep { $_ eq $Delta_To } @{$qunk{'tags'}}))
+	  {
+	    $Delta_EndTime = $time;
+	  }
+	}
+      }
+
+      # Add this file to the list
+      # (We use many spoonfuls of autovivication magic. Hashes and arrays
+      # will spring into existence if they aren't there already.)
+
+      &debug ("(pushing log msg for ${dir_key}$qunk{'filename'})\n");
+
+      # Store with the files in this commit.  Later we'll loop through
+      # again, making sure that revisions with the same log message
+      # and nearby commit times are grouped together as one commit.
+      push (@{$grand_poobah{$dir_key}{$author}{$time}{$msg_txt}}, \%qunk);
+    }
+
+  CLEAR:
+    # Make way for the next message
+    undef $msg_txt;
+    undef $time;
+    undef $revision;
+    undef $author;
+    undef @branch_roots;
+
+    # Maybe even make way for the next file:
+    if ($detected_file_separator) {
+      undef $file_full_path;
+      undef %branch_names;
+      undef %branch_numbers;
+      undef %symbolic_names;
+    }
+  }
+
+  close (LOG_SOURCE);
+
+  ### Process each ChangeLog
+
+  while (my ($dir,$authorhash) = each %grand_poobah)
+  {
+    &debug ("DOING DIR: $dir\n");
+
+    # Here we twist our hash around, from being
+    #   author => time => message => filelist
+    # in %$authorhash to
+    #   time => author => message => filelist
+    # in %changelog.
+    #
+    # This is also where we merge entries.  The algorithm proceeds
+    # through the timeline of the changelog with a sliding window of
+    # $Max_Checkin_Duration seconds; within that window, entries that
+    # have the same log message are merged.
+    #
+    # (To save space, we zap %$authorhash after we've copied
+    # everything out of it.)
+
+    my %changelog;
+    while (my ($author,$timehash) = each %$authorhash)
+    {
+      my $lasttime;
+      my %stamptime;
+      foreach my $time (sort {$main::a <=> $main::b} (keys %$timehash))
+      {
+        my $msghash = $timehash->{$time};
+        while (my ($msg,$qunklist) = each %$msghash)
+        {
+ 	  my $stamptime = $stamptime{$msg};
+          if ((defined $stamptime)
+              and (($time - $stamptime) < $Max_Checkin_Duration)
+              and (defined $changelog{$stamptime}{$author}{$msg}))
+          {
+ 	    push(@{$changelog{$stamptime}{$author}{$msg}}, @$qunklist);
+          }
+          else {
+            $changelog{$time}{$author}{$msg} = $qunklist;
+            $stamptime{$msg} = $time;
+          }
+        }
+      }
+    }
+    undef (%$authorhash);
+
+    ### Now we can write out the ChangeLog!
+
+    my ($logfile_here, $logfile_bak, $tmpfile);
+
+    if (! $Output_To_Stdout) {
+      $logfile_here =  $dir . $Log_File_Name;
+      $logfile_here =~ s/^\.\/\//\//;   # fix any leading ".//" problem
+      $tmpfile      = "${logfile_here}.cvs2cl$$.tmp";
+      $logfile_bak  = "${logfile_here}.bak";
+
+      open (LOG_OUT, ">$tmpfile") or die "Unable to open \"$tmpfile\"";
+    }
+    else {
+      open (LOG_OUT, ">-") or die "Unable to open stdout for writing";
+    }
+
+    print LOG_OUT $ChangeLog_Header;
+
+    if ($XML_Output) {
+      my $encoding    = 
+        length $XML_Encoding ? qq'encoding="$XML_Encoding"' : '';
+      my $version     = 'version="1.0"';
+      my $declaration = 
+        sprintf '<?xml %s?>', join ' ', grep length, $version, $encoding;
+      my $root        =
+        '<changelog xmlns="http://www.red-bean.com/xmlns/cvs2cl/">';
+      print LOG_OUT "$declaration\n\n$root\n\n";
+    }
+
+    foreach my $time (sort {$main::b <=> $main::a} (keys %changelog))
+    {
+      next if ($Delta_Mode &&
+	       (($time <= $Delta_StartTime) ||
+		($time > $Delta_EndTime && $Delta_EndTime)));
+
+      # Set up the date/author line.
+      # kff todo: do some more XML munging here, on the header
+      # part of the entry:
+      my ($ignore,$min,$hour,$mday,$mon,$year,$wday)
+          = $UTC_Times ? gmtime($time) : localtime($time);
+
+      # XML output includes everything else, we might as well make
+      # it always include Day Of Week too, for consistency.
+      if ($Show_Day_Of_Week or $XML_Output) {
+        $wday = ("Sunday", "Monday", "Tuesday", "Wednesday",
+                 "Thursday", "Friday", "Saturday")[$wday];
+        $wday = ($XML_Output) ? "<weekday>${wday}</weekday>\n" : " $wday";
+      }
+      else {
+        $wday = "";
+      }
+
+      my $authorhash = $changelog{$time};
+      if ($Show_Tag_Dates) {
+        my %tags;
+        while (my ($author,$mesghash) = each %$authorhash) {
+          while (my ($msg,$qunk) = each %$mesghash) {
+            foreach my $qunkref2 (@$qunk) {
+	      if (defined ($$qunkref2{'tags'})) {
+                foreach my $tag (@{$$qunkref2{'tags'}}) {
+                  $tags{$tag} = 1;
+                }
+              }
+	    }
+          }
+        }
+        foreach my $tag (keys %tags) {
+          if (!defined $tag_date_printed{$tag}) {
+            $tag_date_printed{$tag} = $time;
+            if ($XML_Output) {
+              # NOT YET DONE
+            }
+            else {
+             if ($Show_Times) {
+              printf LOG_OUT ("%4u-%02u-%02u${wday} %02u:%02u  tag %s\n\n",
+                              $year+1900, $mon+1, $mday, $hour, $min, $tag);
+             } else {
+               printf LOG_OUT ("%4u-%02u-%02u${wday}  tag %s\n\n",
+                               $year+1900, $mon+1, $mday, $tag);
+             }
+            }
+          }
+        }
+      }
+      while (my ($author,$mesghash) = each %$authorhash)
+      {
+        # If XML, escape in outer loop to avoid compound quoting:
+        if ($XML_Output) {
+          $author = &xml_escape ($author);
+        }
+
+      FOOBIE:
+        while (my ($msg,$qunklist) = each %$mesghash)
+        {
+          ## MJP: 19.xii.01 : Exclude @ignore_tags
+          for my $ignore_tag (@ignore_tags) {
+            next FOOBIE
+              if grep $_ eq $ignore_tag, map(@{$_->{tags}},
+                                             grep(defined $_->{tags},
+                                                  @$qunklist));
+          }
+          ## MJP: 19.xii.01 : End exclude @ignore_tags
+
+          my $files               = &pretty_file_list ($qunklist);
+          my $header_line;          # date and author
+          my $body;                 # see below
+          my $wholething;           # $header_line + $body
+
+          if ($XML_Output) {
+            $header_line =
+                sprintf ("<date>%4u-%02u-%02u</date>\n"
+                         . "${wday}"
+                         . "<time>%02u:%02u</time>\n"
+                         . "<author>%s</author>\n",
+                         $year+1900, $mon+1, $mday, $hour, $min, $author);
+          }
+          else {
+           if ($Show_Times) {
+            $header_line =
+                sprintf ("%4u-%02u-%02u${wday} %02u:%02u  %s\n\n",
+                         $year+1900, $mon+1, $mday, $hour, $min, $author);
+           } else {
+             $header_line =
+                sprintf ("%4u-%02u-%02u${wday}  %s\n\n",
+                         $year+1900, $mon+1, $mday, $author);
+           }
+          }
+
+          $Text::Wrap::huge = 'overflow'
+            if $Text::Wrap::VERSION >= 2001.0130;
+          # Reshape the body according to user preferences.
+          if ($XML_Output)
+          {
+            $msg = &preprocess_msg_text ($msg);
+            $body = $files . $msg;
+          }
+          elsif ($No_Wrap)
+          {
+            $msg = &preprocess_msg_text ($msg);
+            $files = wrap ("\t", "	", "$files");
+            $msg =~ s/\n(.*)/\n\t$1/g;
+            unless ($After_Header eq " ") {
+              $msg =~ s/^(.*)/\t$1/g;
+            }
+            $body = $files . $After_Header . $msg;
+          }
+          else  # do wrapping, either FSF-style or regular
+          {
+            if ($FSF_Style)
+            {
+              $files = wrap ("\t", "        ", "$files");
+
+              my $files_last_line_len = 0;
+              if ($After_Header eq " ")
+              {
+                $files_last_line_len = &last_line_len ($files);
+                $files_last_line_len += 1;  # for $After_Header
+              }
+
+              $msg = &wrap_log_entry
+                  ($msg, "\t", 69 - $files_last_line_len, 69);
+              $body = $files . $After_Header . $msg;
+            }
+            else  # not FSF-style
+            {
+              $msg = &preprocess_msg_text ($msg);
+              $body = $files . $After_Header . $msg;
+              $body = wrap ("\t", "        ", "$body");
+            }
+          }
+
+          $wholething = $header_line . $body;
+
+          if ($XML_Output) {
+            $wholething = "<entry>\n${wholething}</entry>\n";
+          }
+
+          # One last check: make sure it passes the regexp test, if the
+          # user asked for that.  We have to do it here, so that the
+          # test can match against information in the header as well
+          # as in the text of the log message.
+
+          # How annoying to duplicate so much code just because I
+          # can't figure out a way to evaluate scalars on the trailing
+          # operator portion of a regular expression.  Grrr.
+          if ($Case_Insensitive) {
+            unless ($Regexp_Gate && ($wholething !~ /$Regexp_Gate/oi)) {
+              print LOG_OUT "${wholething}\n";
+            }
+          }
+          else {
+            unless ($Regexp_Gate && ($wholething !~ /$Regexp_Gate/o)) {
+              print LOG_OUT "${wholething}\n";
+            }
+          }
+        }
+      }
+    }
+
+    if ($XML_Output) {
+      print LOG_OUT "</changelog>\n";
+    }
+
+    close (LOG_OUT);
+
+    if (! $Output_To_Stdout)
+    {
+      # If accumulating, append old data to new before renaming.  But
+      # don't append the most recent entry, since it's already in the
+      # new log due to CVS's idiosyncratic interpretation of "log -d".
+      if ($Cumulative && -f $logfile_here)
+      {
+        open (NEW_LOG, ">>$tmpfile")
+            or die "trouble appending to $tmpfile ($!)";
+
+        open (OLD_LOG, "<$logfile_here")
+            or die "trouble reading from $logfile_here ($!)";
+
+        my $started_first_entry = 0;
+        my $passed_first_entry = 0;
+        while (<OLD_LOG>)
+        {
+          if (! $passed_first_entry)
+          {
+            if ((! $started_first_entry)
+                && /^(\d\d\d\d-\d\d-\d\d\s+\d\d:\d\d)/) {
+              $started_first_entry = 1;
+            }
+            elsif (/^(\d\d\d\d-\d\d-\d\d\s+\d\d:\d\d)/) {
+              $passed_first_entry = 1;
+              print NEW_LOG $_;
+            }
+          }
+          else {
+            print NEW_LOG $_;
+          }
+        }
+
+        close (NEW_LOG);
+        close (OLD_LOG);
+      }
+
+      if (-f $logfile_here) {
+        rename ($logfile_here, $logfile_bak);
+      }
+      rename ($tmpfile, $logfile_here);
+    }
+  }
+}
+
+sub parse_date_and_author ()
+{
+  # Parses the date/time and author out of a line like:
+  #
+  # date: 1999/02/19 23:29:05;  author: apharris;  state: Exp;
+
+  my $line = shift;
+
+  my ($year, $mon, $mday, $hours, $min, $secs, $author) = $line =~
+      m#(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+);\s+author:\s+([^;]+);#
+          or  die "Couldn't parse date ``$line''";
+  die "Bad date or Y2K issues" unless ($year > 1969 and $year < 2258);
+  # Kinda arbitrary, but useful as a sanity check
+  my $time = timegm($secs,$min,$hours,$mday,$mon-1,$year-1900);
+
+  return ($time, $author);
+}
+
+# Here we take a bunch of qunks and convert them into printed
+# summary that will include all the information the user asked for.
+sub pretty_file_list ()
+{
+  if ($Hide_Filenames and (! $XML_Output)) {
+    return "";
+  }
+
+  my $qunksref = shift;
+  my @qunkrefs = @$qunksref;
+  my @filenames;
+  my $beauty = "";          # The accumulating header string for this entry.
+  my %non_unanimous_tags;   # Tags found in a proper subset of qunks
+  my %unanimous_tags;       # Tags found in all qunks
+  my %all_branches;         # Branches found in any qunk
+  my $common_dir = undef;   # Dir prefix common to all files ("" if none)
+  my $fbegun = 0;           # Did we begin printing filenames yet?
+
+  # First, loop over the qunks gathering all the tag/branch names.
+  # We'll put them all in non_unanimous_tags, and take out the
+  # unanimous ones later.
+ QUNKREF:
+  foreach my $qunkref (@qunkrefs)
+  {
+    ## MJP: 19.xii.01 : Exclude @ignore_tags
+    for my $ignore_tag (@ignore_tags) {
+      next QUNKREF
+        if grep $_ eq $ignore_tag, @{$$qunkref{'tags'}};
+    }
+    ## MJP: 19.xii.01 : End exclude @ignore_tags
+
+    # Keep track of whether all the files in this commit were in the
+    # same directory, and memorize it if so.  We can make the output a
+    # little more compact by mentioning the directory only once.
+    if ((scalar (@qunkrefs)) > 1)
+    {
+      if (! (defined ($common_dir)))
+      {
+        my ($base, $dir);
+        ($base, $dir, undef) = fileparse ($$qunkref{'filename'});
+
+        if ((! (defined ($dir)))  # this first case is sheer paranoia
+            or ($dir eq "")
+            or ($dir eq "./")
+            or ($dir eq ".\\"))
+        {
+          $common_dir = "";
+        }
+        else
+        {
+          $common_dir = $dir;
+        }
+      }
+      elsif ($common_dir ne "")
+      {
+        # Already have a common dir prefix, so how much of it can we preserve?
+        $common_dir = &common_path_prefix ($$qunkref{'filename'}, $common_dir);
+      }
+    }
+    else  # only one file in this entry anyway, so common dir not an issue
+    {
+      $common_dir = "";
+    }
+
+    if (defined ($$qunkref{'branch'})) {
+      $all_branches{$$qunkref{'branch'}} = 1;
+    }
+    if (defined ($$qunkref{'tags'})) {
+      foreach my $tag (@{$$qunkref{'tags'}}) {
+        $non_unanimous_tags{$tag} = 1;
+      }
+    }
+  }
+
+  # Any tag held by all qunks will be printed specially... but only if
+  # there are multiple qunks in the first place!
+  if ((scalar (@qunkrefs)) > 1) {
+    foreach my $tag (keys (%non_unanimous_tags)) {
+      my $everyone_has_this_tag = 1;
+      foreach my $qunkref (@qunkrefs) {
+        if ((! (defined ($$qunkref{'tags'})))
+            or (! (grep ($_ eq $tag, @{$$qunkref{'tags'}})))) {
+          $everyone_has_this_tag = 0;
+        }
+      }
+      if ($everyone_has_this_tag) {
+        $unanimous_tags{$tag} = 1;
+        delete $non_unanimous_tags{$tag};
+      }
+    }
+  }
+
+  if ($XML_Output)
+  {
+    # If outputting XML, then our task is pretty simple, because we
+    # don't have to detect common dir, common tags, branch prefixing,
+    # etc.  We just output exactly what we have, and don't worry about
+    # redundancy or readability.
+
+    foreach my $qunkref (@qunkrefs)
+    {
+      my $filename    = $$qunkref{'filename'};
+      my $revision    = $$qunkref{'revision'};
+      my $tags        = $$qunkref{'tags'};
+      my $branch      = $$qunkref{'branch'};
+      my $branchroots = $$qunkref{'branchroots'};
+
+      $filename = &xml_escape ($filename);   # probably paranoia
+      $revision = &xml_escape ($revision);   # definitely paranoia
+
+      $beauty .= "<file>\n";
+      $beauty .= "<name>${filename}</name>\n";
+      $beauty .= "<revision>${revision}</revision>\n";
+      if ($branch) {
+        $branch   = &xml_escape ($branch);     # more paranoia
+        $beauty .= "<branch>${branch}</branch>\n";
+      }
+      foreach my $tag (@$tags) {
+        $tag = &xml_escape ($tag);  # by now you're used to the paranoia
+        $beauty .= "<tag>${tag}</tag>\n";
+      }
+      foreach my $root (@$branchroots) {
+        $root = &xml_escape ($root);  # which is good, because it will continue
+        $beauty .= "<branchroot>${root}</branchroot>\n";
+      }
+      $beauty .= "</file>\n";
+    }
+
+    # Theoretically, we could go home now.  But as long as we're here,
+    # let's print out the common_dir and utags, as a convenience to
+    # the receiver (after all, earlier code calculated that stuff
+    # anyway, so we might as well take advantage of it).
+
+    if ((scalar (keys (%unanimous_tags))) > 1) {
+      foreach my $utag ((keys (%unanimous_tags))) {
+        $utag = &xml_escape ($utag);   # the usual paranoia
+        $beauty .= "<utag>${utag}</utag>\n";
+      }
+    }
+    if ($common_dir) {
+      $common_dir = &xml_escape ($common_dir);
+      $beauty .= "<commondir>${common_dir}</commondir>\n";
+    }
+
+    # That's enough for XML, time to go home:
+    return $beauty;
+  }
+
+  # Else not XML output, so complexly compactify for chordate
+  # consumption.  At this point we have enough global information
+  # about all the qunks to organize them non-redundantly for output.
+
+  if ($common_dir) {
+    # Note that $common_dir still has its trailing slash
+    $beauty .= "$common_dir: ";
+  }
+
+  if ($Show_Branches)
+  {
+    # For trailing revision numbers.
+    my @brevisions;
+
+    foreach my $branch (keys (%all_branches))
+    {
+      foreach my $qunkref (@qunkrefs)
+      {
+        if ((defined ($$qunkref{'branch'}))
+            and ($$qunkref{'branch'} eq $branch))
+        {
+          if ($fbegun) {
+            # kff todo: comma-delimited in XML too?  Sure.
+            $beauty .= ", ";
+          }
+          else {
+            $fbegun = 1;
+          }
+          my $fname = substr ($$qunkref{'filename'}, length ($common_dir));
+          $beauty .= $fname;
+          $$qunkref{'printed'} = 1;  # Just setting a mark bit, basically
+
+          if ($Show_Tags && (defined @{$$qunkref{'tags'}})) {
+            my @tags = grep ($non_unanimous_tags{$_}, @{$$qunkref{'tags'}});
+
+            if (@tags) {
+              $beauty .= " (tags: ";
+              $beauty .= join (', ', @tags);
+              $beauty .= ")";
+            }
+          }
+
+          if ($Show_Revisions) {
+            # Collect the revision numbers' last components, but don't
+            # print them -- they'll get printed with the branch name
+            # later.
+            $$qunkref{'revision'} =~ /.+\.([\d]+)$/;
+            push (@brevisions, $1);
+
+            # todo: we're still collecting branch roots, but we're not
+            # showing them anywhere.  If we do show them, it would be
+            # nifty to just call them revision "0" on a the branch.
+            # Yeah, that's the ticket.
+          }
+        }
+      }
+      $beauty .= " ($branch";
+      if (@brevisions) {
+        if ((scalar (@brevisions)) > 1) {
+          $beauty .= ".[";
+          $beauty .= (join (',', @brevisions));
+          $beauty .= "]";
+        }
+        else {
+          # Square brackets are spurious here, since there's no range to
+          # encapsulate
+          $beauty .= ".$brevisions[0]";
+        }
+      }
+      $beauty .= ")";
+    }
+  }
+
+  # Okay; any qunks that were done according to branch are taken care
+  # of, and marked as printed.  Now print everyone else.
+
+  foreach my $qunkref (@qunkrefs)
+  {
+    next if (defined ($$qunkref{'printed'}));   # skip if already printed
+
+    if ($fbegun) {
+      $beauty .= ", ";
+    }
+    else {
+      $fbegun = 1;
+    }
+    $beauty .= substr ($$qunkref{'filename'}, length ($common_dir));
+    # todo: Shlomo's change was this:
+    # $beauty .= substr ($$qunkref{'filename'},
+    #              (($common_dir eq "./") ? "" : length ($common_dir)));
+    $$qunkref{'printed'} = 1;  # Set a mark bit.
+
+    if ($Show_Revisions || $Show_Tags)
+    {
+      my $started_addendum = 0;
+
+      if ($Show_Revisions) {
+        $started_addendum = 1;
+        $beauty .= " (";
+        $beauty .= "$$qunkref{'revision'}";
+      }
+      if ($Show_Tags && (defined $$qunkref{'tags'})) {
+        my @tags = grep ($non_unanimous_tags{$_}, @{$$qunkref{'tags'}});
+        if ((scalar (@tags)) > 0) {
+          if ($started_addendum) {
+            $beauty .= ", ";
+          }
+          else {
+            $beauty .= " (tags: ";
+          }
+          $beauty .= join (', ', @tags);
+          $started_addendum = 1;
+        }
+      }
+      if ($started_addendum) {
+        $beauty .= ")";
+      }
+    }
+  }
+
+  # Unanimous tags always come last.
+  if ($Show_Tags && %unanimous_tags)
+  {
+    $beauty .= " (utags: ";
+    $beauty .= join (', ', sort keys (%unanimous_tags));
+    $beauty .= ")";
+  }
+
+  # todo: still have to take care of branch_roots?
+
+  $beauty = "* $beauty:";
+
+  return $beauty;
+}
+
+sub common_path_prefix ()
+{
+  my $path1 = shift;
+  my $path2 = shift;
+
+  my ($dir1, $dir2);
+  (undef, $dir1, undef) = fileparse ($path1);
+  (undef, $dir2, undef) = fileparse ($path2);
+
+  # Transmogrify Windows filenames to look like Unix.
+  # (It is far more likely that someone is running cvs2cl.pl under
+  # Windows than that they would genuinely have backslashes in their
+  # filenames.)
+  $dir1 =~ tr#\\#/#;
+  $dir2 =~ tr#\\#/#;
+
+  my $accum1 = "";
+  my $accum2 = "";
+  my $last_common_prefix = "";
+
+  while ($accum1 eq $accum2)
+  {
+    $last_common_prefix = $accum1;
+    last if ($accum1 eq $dir1);
+    my ($tmp1) = split (/\//, (substr ($dir1, length ($accum1))));
+    my ($tmp2) = split (/\//, (substr ($dir2, length ($accum2))));
+    $accum1 .= "$tmp1/" if (defined $tmp1 and $tmp1 ne '');
+    $accum2 .= "$tmp2/" if (defined $tmp2 and $tmp2 ne '');
+  }
+
+  return $last_common_prefix;
+}
+
+sub preprocess_msg_text ()
+{
+  my $text = shift;
+
+  # Strip out carriage returns (as they probably result from DOSsy editors).
+  $text =~ s/\r\n/\n/g;
+
+  # If it *looks* like two newlines, make it *be* two newlines:
+  $text =~ s/\n\s*\n/\n\n/g;
+
+  if ($XML_Output)
+  {
+    $text = &xml_escape ($text);
+    $text = "<msg>${text}</msg>\n";
+  }
+  elsif (! $No_Wrap)
+  {
+    # Strip off lone newlines, but only for lines that don't begin with
+    # whitespace or a mail-quoting character, since we want to preserve
+    # that kind of formatting.  Also don't strip newlines that follow a
+    # period; we handle those specially next.  And don't strip
+    # newlines that precede an open paren.
+    1 while ($text =~ s/(^|\n)([^>\s].*[^.\n])\n([^>\n])/$1$2 $3/g);
+
+    # If a newline follows a period, make sure that when we bring up the
+    # bottom sentence, it begins with two spaces.
+    1 while ($text =~ s/(^|\n)([^>\s].*)\n([^>\n])/$1$2  $3/g);
+  }
+
+  return $text;
+}
+
+sub last_line_len ()
+{
+  my $files_list = shift;
+  my @lines = split (/\n/, $files_list);
+  my $last_line = pop (@lines);
+  return length ($last_line);
+}
+
+# A custom wrap function, sensitive to some common constructs used in
+# log entries.
+sub wrap_log_entry ()
+{
+  my $text = shift;                  # The text to wrap.
+  my $left_pad_str = shift;          # String to pad with on the left.
+
+  # These do NOT take left_pad_str into account:
+  my $length_remaining = shift;      # Amount left on current line.
+  my $max_line_length  = shift;      # Amount left for a blank line.
+
+  my $wrapped_text = "";             # The accumulating wrapped entry.
+  my $user_indent = "";              # Inherited user_indent from prev line.
+
+  my $first_time = 1;                # First iteration of the loop?
+  my $suppress_line_start_match = 0; # Set to disable line start checks.
+
+  my @lines = split (/\n/, $text);
+  while (@lines)   # Don't use `foreach' here, it won't work.
+  {
+    my $this_line = shift (@lines);
+    chomp $this_line;
+
+    if ($this_line =~ /^(\s+)/) {
+      $user_indent = $1;
+    }
+    else {
+      $user_indent = "";
+    }
+
+    # If it matches any of the line-start regexps, print a newline now...
+    if ($suppress_line_start_match)
+    {
+      $suppress_line_start_match = 0;
+    }
+    elsif (($this_line =~ /^(\s*)\*\s+[a-zA-Z0-9]/)
+           || ($this_line =~ /^(\s*)\* [a-zA-Z0-9_\.\/\+-]+/)
+           || ($this_line =~ /^(\s*)\([a-zA-Z0-9_\.\/\+-]+(\)|,\s*)/)
+           || ($this_line =~ /^(\s+)(\S+)/)
+           || ($this_line =~ /^(\s*)- +/)
+           || ($this_line =~ /^()\s*$/)
+           || ($this_line =~ /^(\s*)\*\) +/)
+           || ($this_line =~ /^(\s*)[a-zA-Z0-9](\)|\.|\:) +/))
+    {
+      # Make a line break immediately, unless header separator is set
+      # and this line is the first line in the entry, in which case
+      # we're getting the blank line for free already and shouldn't
+      # add an extra one.
+      unless (($After_Header ne " ") and ($first_time))
+      {
+        if ($this_line =~ /^()\s*$/) {
+          $suppress_line_start_match = 1;
+          $wrapped_text .= "\n${left_pad_str}";
+        }
+
+        $wrapped_text .= "\n${left_pad_str}";
+      }
+
+      $length_remaining = $max_line_length - (length ($user_indent));
+    }
+
+    # Now that any user_indent has been preserved, strip off leading
+    # whitespace, so up-folding has no ugly side-effects.
+    $this_line =~ s/^\s*//;
+
+    # Accumulate the line, and adjust parameters for next line.
+    my $this_len = length ($this_line);
+    if ($this_len == 0)
+    {
+      # Blank lines should cancel any user_indent level.
+      $user_indent = "";
+      $length_remaining = $max_line_length;
+    }
+    elsif ($this_len >= $length_remaining) # Line too long, try breaking it.
+    {
+      # Walk backwards from the end.  At first acceptable spot, break
+      # a new line.
+      my $idx = $length_remaining - 1;
+      if ($idx < 0) { $idx = 0 };
+      while ($idx > 0)
+      {
+        if (substr ($this_line, $idx, 1) =~ /\s/)
+        {
+          my $line_now = substr ($this_line, 0, $idx);
+          my $next_line = substr ($this_line, $idx);
+          $this_line = $line_now;
+
+          # Clean whitespace off the end.
+          chomp $this_line;
+
+          # The current line is ready to be printed.
+          $this_line .= "\n${left_pad_str}";
+
+          # Make sure the next line is allowed full room.
+          $length_remaining = $max_line_length - (length ($user_indent));
+
+          # Strip next_line, but then preserve any user_indent.
+          $next_line =~ s/^\s*//;
+
+          # Sneak a peek at the user_indent of the upcoming line, so
+          # $next_line (which will now precede it) can inherit that
+          # indent level.  Otherwise, use whatever user_indent level
+          # we currently have, which might be none.
+          my $next_next_line = shift (@lines);
+          if ((defined ($next_next_line)) && ($next_next_line =~ /^(\s+)/)) {
+            $next_line = $1 . $next_line if (defined ($1));
+            # $length_remaining = $max_line_length - (length ($1));
+            $next_next_line =~ s/^\s*//;
+          }
+          else {
+            $next_line = $user_indent . $next_line;
+          }
+          if (defined ($next_next_line)) {
+            unshift (@lines, $next_next_line);
+          }
+          unshift (@lines, $next_line);
+
+          # Our new next line might, coincidentally, begin with one of
+          # the line-start regexps, so we temporarily turn off
+          # sensitivity to that until we're past the line.
+          $suppress_line_start_match = 1;
+
+          last;
+        }
+        else
+        {
+          $idx--;
+        }
+      }
+
+      if ($idx == 0)
+      {
+        # We bottomed out because the line is longer than the
+        # available space.  But that could be because the space is
+        # small, or because the line is longer than even the maximum
+        # possible space.  Handle both cases below.
+
+        if ($length_remaining == ($max_line_length - (length ($user_indent))))
+        {
+          # The line is simply too long -- there is no hope of ever
+          # breaking it nicely, so just insert it verbatim, with
+          # appropriate padding.
+          $this_line = "\n${left_pad_str}${this_line}";
+        }
+        else
+        {
+          # Can't break it here, but may be able to on the next round...
+          unshift (@lines, $this_line);
+          $length_remaining = $max_line_length - (length ($user_indent));
+          $this_line = "\n${left_pad_str}";
+        }
+      }
+    }
+    else  # $this_len < $length_remaining, so tack on what we can.
+    {
+      # Leave a note for the next iteration.
+      $length_remaining = $length_remaining - $this_len;
+
+      if ($this_line =~ /\.$/)
+      {
+        $this_line .= "  ";
+        $length_remaining -= 2;
+      }
+      else  # not a sentence end
+      {
+        $this_line .= " ";
+        $length_remaining -= 1;
+      }
+    }
+
+    # Unconditionally indicate that loop has run at least once.
+    $first_time = 0;
+
+    $wrapped_text .= "${user_indent}${this_line}";
+  }
+
+  # One last bit of padding.
+  $wrapped_text .= "\n";
+
+  return $wrapped_text;
+}
+
+sub xml_escape ()
+{
+  my $txt = shift;
+  $txt =~ s/&/&amp;/g;
+  $txt =~ s/</&lt;/g;
+  $txt =~ s/>/&gt;/g;
+  return $txt;
+}
+
+sub maybe_read_user_map_file ()
+{
+  my %expansions;
+
+  if ($User_Map_File)
+  {
+    open (MAPFILE, "<$User_Map_File")
+        or die ("Unable to open $User_Map_File ($!)");
+
+    while (<MAPFILE>)
+    {
+      next if /^\s*#/;  # Skip comment lines.
+      next if not /:/;  # Skip lines without colons.
+
+      # It is now safe to split on ':'.
+      my ($username, $expansion) = split ':';
+      chomp $expansion;
+      $expansion =~ s/^'(.*)'$/$1/;
+      $expansion =~ s/^"(.*)"$/$1/;
+
+      # If it looks like the expansion has a real name already, then
+      # we toss the username we got from CVS log.  Otherwise, keep
+      # it to use in combination with the email address.
+
+      if ($expansion =~ /^\s*<{0,1}\S+@.*/) {
+        # Also, add angle brackets if none present
+        if (! ($expansion =~ /<\S+@\S+>/)) {
+          $expansions{$username} = "$username <$expansion>";
+        }
+        else {
+          $expansions{$username} = "$username $expansion";
+        }
+      }
+      else {
+        $expansions{$username} = $expansion;
+      }
+    }
+
+    close (MAPFILE);
+  }
+
+  return %expansions;
+}
+
+sub parse_options ()
+{
+  # Check this internally before setting the global variable.
+  my $output_file;
+
+  # If this gets set, we encountered unknown options and will exit at
+  # the end of this subroutine.
+  my $exit_with_admonishment = 0;
+
+  while (my $arg = shift (@ARGV))
+  {
+    if ($arg =~ /^-h$|^-help$|^--help$|^--usage$|^-?$/) {
+      $Print_Usage = 1;
+    }
+    elsif ($arg =~ /^--delta$/) {
+      my $narg = shift(@ARGV) || die "$arg needs argument.\n";
+      if ($narg =~ /^([A-Za-z][A-Za-z0-9_\-]*):([A-Za-z][A-Za-z0-9_\-]*)$/) {
+	$Delta_From = $1;
+	$Delta_To = $2;
+	$Delta_Mode = 1;
+      } else {
+	die "--delta FROM_TAG:TO_TAG is what you meant to say.\n";
+      }
+    }
+    elsif ($arg =~ /^--debug$/) {        # unadvertised option, heh
+      $Debug = 1;
+    }
+    elsif ($arg =~ /^--version$/) {
+      $Print_Version = 1;
+    }
+    elsif ($arg =~ /^-g$|^--global-opts$/) {
+      my $narg = shift (@ARGV) || die "$arg needs argument.\n";
+      # Don't assume CVS is called "cvs" on the user's system:
+      $Log_Source_Command =~ s/(^\S*)/$1 $narg/;
+    }
+    elsif ($arg =~ /^-l$|^--log-opts$/) {
+      my $narg = shift (@ARGV) || die "$arg needs argument.\n";
+      $Log_Source_Command .= " $narg";
+    }
+    elsif ($arg =~ /^-f$|^--file$/) {
+      my $narg = shift (@ARGV) || die "$arg needs argument.\n";
+      $output_file = $narg;
+    }
+    elsif ($arg =~ /^--accum$/) {
+      $Cumulative = 1;
+    }
+    elsif ($arg =~ /^--fsf$/) {
+      $FSF_Style = 1;
+    }
+    elsif ($arg =~ /^-U$|^--usermap$/) {
+      my $narg = shift (@ARGV) || die "$arg needs argument.\n";
+      $User_Map_File = $narg;
+    }
+    elsif ($arg =~ /^-W$|^--window$/) {
+      defined(my $narg = shift (@ARGV)) || die "$arg needs argument.\n";
+      $Max_Checkin_Duration = $narg;
+    }
+    elsif ($arg =~ /^-I$|^--ignore$/) {
+      my $narg = shift (@ARGV) || die "$arg needs argument.\n";
+      push (@Ignore_Files, $narg);
+    }
+    elsif ($arg =~ /^-C$|^--case-insensitive$/) {
+      $Case_Insensitive = 1;
+    }
+    elsif ($arg =~ /^-R$|^--regexp$/) {
+      my $narg = shift (@ARGV) || die "$arg needs argument.\n";
+      $Regexp_Gate = $narg;
+    }
+    elsif ($arg =~ /^--stdout$/) {
+      $Output_To_Stdout = 1;
+    }
+    elsif ($arg =~ /^--version$/) {
+      $Print_Version = 1;
+    }
+    elsif ($arg =~ /^-d$|^--distributed$/) {
+      $Distributed = 1;
+    }
+    elsif ($arg =~ /^-P$|^--prune$/) {
+      $Prune_Empty_Msgs = 1;
+    }
+    elsif ($arg =~ /^-S$|^--separate-header$/) {
+      $After_Header = "\n\n";
+    }
+    elsif ($arg =~ /^--no-wrap$/) {
+      $No_Wrap = 1;
+    }
+    elsif ($arg =~ /^--gmt$|^--utc$/) {
+      $UTC_Times = 1;
+    }
+    elsif ($arg =~ /^-w$|^--day-of-week$/) {
+      $Show_Day_Of_Week = 1;
+    }
+    elsif ($arg =~ /^--no-times$/) {
+      $Show_Times = 0;
+    }
+    elsif ($arg =~ /^-r$|^--revisions$/) {
+      $Show_Revisions = 1;
+    }
+    elsif ($arg =~ /^-t$|^--tags$/) {
+      $Show_Tags = 1;
+    }
+    elsif ($arg =~ /^-T$|^--tagdates$/) {
+      $Show_Tag_Dates = 1;
+    }
+    elsif ($arg =~ /^-b$|^--branches$/) {
+      $Show_Branches = 1;
+    }
+    elsif ($arg =~ /^-F$|^--follow$/) {
+      my $narg = shift (@ARGV) || die "$arg needs argument.\n";
+      push (@Follow_Branches, $narg);
+    }
+    elsif ($arg =~ /^--stdin$/) {
+      $Input_From_Stdin = 1;
+    }
+    elsif ($arg =~ /^--header$/) {
+      my $narg = shift (@ARGV) || die "$arg needs argument.\n";
+      $ChangeLog_Header = &slurp_file ($narg);
+      if (! defined ($ChangeLog_Header)) {
+        $ChangeLog_Header = "";
+      }
+    }
+    elsif ($arg =~ /^--xml-encoding$/) {
+      my $narg = shift (@ARGV) || die "$arg needs argument.\n";
+      $XML_Encoding = $narg ;
+    }
+    elsif ($arg =~ /^--xml$/) {
+      $XML_Output = 1;
+    }
+    elsif ($arg =~ /^--hide-filenames$/) {
+      $Hide_Filenames = 1;
+      $After_Header = "";
+    }
+    elsif ($arg =~ /^--ignore-tag$/ ) {
+      die "$arg needs argument.\n"
+        unless @ARGV;
+      push @ignore_tags, shift @ARGV;
+    }
+    else {
+      # Just add a filename as argument to the log command
+      $Log_Source_Command .= " '$arg'";
+    }
+  }
+
+  ## Check for contradictions...
+
+  if ($Output_To_Stdout && $Distributed) {
+    print STDERR "cannot pass both --stdout and --distributed\n";
+    $exit_with_admonishment = 1;
+  }
+
+  if ($Output_To_Stdout && $output_file) {
+    print STDERR "cannot pass both --stdout and --file\n";
+    $exit_with_admonishment = 1;
+  }
+
+  if ($XML_Output && $Cumulative) {
+    print STDERR "cannot pass both --xml and --accum\n";
+    $exit_with_admonishment = 1;
+  }
+
+  # Or if any other error message has already been printed out, we
+  # just leave now:
+  if ($exit_with_admonishment) {
+    &usage ();
+    exit (1);
+  }
+  elsif ($Print_Usage) {
+    &usage ();
+    exit (0);
+  }
+  elsif ($Print_Version) {
+    &version ();
+    exit (0);
+  }
+
+  ## Else no problems, so proceed.
+
+  if ($output_file) {
+    $Log_File_Name = $output_file;
+  }
+}
+
+sub slurp_file ()
+{
+  my $filename = shift || die ("no filename passed to slurp_file()");
+  my $retstr;
+
+  open (SLURPEE, "<${filename}") or die ("unable to open $filename ($!)");
+  my $saved_sep = $/;
+  undef $/;
+  $retstr = <SLURPEE>;
+  $/ = $saved_sep;
+  close (SLURPEE);
+  return $retstr;
+}
+
+sub debug ()
+{
+  if ($Debug) {
+    my $msg = shift;
+    print STDERR $msg;
+  }
+}
+
+sub version ()
+{
+  print "cvs2cl.pl version ${VERSION}; distributed under the GNU GPL.\n";
+}
+
+sub usage ()
+{
+  &version ();
+  print <<'END_OF_INFO';
+Generate GNU-style ChangeLogs in CVS working copies.
+
+Notes about the output format(s):
+
+   The default output of cvs2cl.pl is designed to be compact, formally
+   unambiguous, but still easy for humans to read.  It is largely
+   self-explanatory, I hope; the one abbreviation that might not be
+   obvious is "utags".  That stands for "universal tags" -- a
+   universal tag is one held by all the files in a given change entry.
+
+   If you need output that's easy for a program to parse, use the
+   --xml option.  Note that with XML output, just about all available
+   information is included with each change entry, whether you asked
+   for it or not, on the theory that your parser can ignore anything
+   it's not looking for.
+
+Notes about the options and arguments (the actual options are listed
+last in this usage message):
+
+  * The -I and -F options may appear multiple times.
+
+  * To follow trunk revisions, use "-F trunk" ("-F TRUNK" also works).
+    This is okay because no would ever, ever be crazy enough to name a
+    branch "trunk", right?  Right.
+
+  * For the -U option, the UFILE should be formatted like
+    CVSROOT/users. That is, each line of UFILE looks like this
+       jrandom:jrandom@red-bean.com
+    or maybe even like this
+       jrandom:'Jesse Q. Random <jrandom@red-bean.com>'
+    Don't forget to quote the portion after the colon if necessary.
+
+  * Many people want to filter by date.  To do so, invoke cvs2cl.pl
+    like this:
+       cvs2cl.pl -l "-d'DATESPEC'"
+    where DATESPEC is any date specification valid for "cvs log -d".
+    (Note that CVS 1.10.7 and below requires there be no space between
+    -d and its argument).
+
+Options/Arguments:
+
+  -h, -help, --help, or -?     Show this usage and exit
+  --version                    Show version and exit
+  -r, --revisions              Show revision numbers in output
+  -b, --branches               Show branch names in revisions when possible
+  -t, --tags                   Show tags (symbolic names) in output
+  -T, --tagdates               Show tags in output on their first occurance
+  --stdin                      Read from stdin, don't run cvs log
+  --stdout                     Output to stdout not to ChangeLog
+  -d, --distributed            Put ChangeLogs in subdirs
+  -f FILE, --file FILE         Write to FILE instead of "ChangeLog"
+  --fsf                        Use this if log data is in FSF ChangeLog style
+  -W SECS, --window SECS       Window of time within which log entries unify
+  -U UFILE, --usermap UFILE    Expand usernames to email addresses from UFILE
+  -R REGEXP, --regexp REGEXP   Include only entries that match REGEXP
+  -I REGEXP, --ignore REGEXP   Ignore files whose names match REGEXP
+  -C, --case-insensitive       Any regexp matching is done case-insensitively
+  -F BRANCH, --follow BRANCH   Show only revisions on or ancestral to BRANCH
+  -S, --separate-header        Blank line between each header and log message
+  --no-wrap                    Don't auto-wrap log message (recommend -S also)
+  --gmt, --utc                 Show times in GMT/UTC instead of local time
+  --accum                      Add to an existing ChangeLog (incompat w/ --xml)
+  -w, --day-of-week            Show day of week
+  --no-times                   Don't show times in output
+  --header FILE                Get ChangeLog header from FILE ("-" means stdin)
+  --xml                        Output XML instead of ChangeLog format
+  --xml-encoding ENCODING      Insert encoding clause in XML header
+  --hide-filenames             Don't show filenames (ignored for XML output)
+  -P, --prune                  Don't show empty log messages
+  -g OPTS, --global-opts OPTS  Invoke like this "cvs OPTS log ..."
+  -l OPTS, --log-opts OPTS     Invoke like this "cvs ... log OPTS"
+  FILE1 [FILE2 ...]            Show only log information for the named FILE(s)
+
+See http://www.red-bean.com/cvs2cl for maintenance and bug info.
+END_OF_INFO
+}
+
+__END__
+
+=head1 NAME
+
+cvs2cl.pl - produces GNU-style ChangeLogs in CVS working copies, by
+    running "cvs log" and parsing the output.  Shared log entries are
+    unified in an intuitive way.
+
+=head1 DESCRIPTION
+
+This script generates GNU-style ChangeLog files from CVS log
+information.  Basic usage: just run it inside a working copy and a
+ChangeLog will appear.  It requires repository access (i.e., 'cvs log'
+must work).  Run "cvs2cl.pl --help" to see more advanced options.
+
+See http://www.red-bean.com/cvs2cl for updates, and for instructions
+on getting anonymous CVS access to this script.
+
+Maintainer: Karl Fogel <kfogel@red-bean.com>
+Please report bugs to <bug-cvs2cl@red-bean.com>.
+
+=head1 README
+
+This script generates GNU-style ChangeLog files from CVS log
+information.  Basic usage: just run it inside a working copy and a
+ChangeLog will appear.  It requires repository access (i.e., 'cvs log'
+must work).  Run "cvs2cl.pl --help" to see more advanced options.
+
+See http://www.red-bean.com/cvs2cl for updates, and for instructions
+on getting anonymous CVS access to this script.
+
+Maintainer: Karl Fogel <kfogel@red-bean.com>
+Please report bugs to <bug-cvs2cl@red-bean.com>.
+
+=head1 PREREQUISITES
+
+This script requires C<Text::Wrap>, C<Time::Local>, and
+C<File::Basename>.
+It also seems to require C<Perl 5.004_04> or higher.
+
+=pod OSNAMES
+
+any
+
+=pod SCRIPT CATEGORIES
+
+Version_Control/CVS
+
+=cut
+
+-*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*- -*-
+
+Note about a bug-slash-opportunity:
+-----------------------------------
+
+There's a bug in Text::Wrap, which affects cvs2cl.  This script
+reveals it:
+
+  #!/usr/bin/perl -w
+
+  use Text::Wrap;
+
+  my $test_text =
+  "This script demonstrates a bug in Text::Wrap.  The very long line
+  following this paragraph will be relocated relative to the surrounding
+  text:
+
+  ====================================================================
+
+  See?  When the bug happens, we'll get the line of equal signs below
+  this paragraph, even though it should be above.";
+
+  # Print out the test text with no wrapping:
+  print "$test_text";
+  print "\n";
+  print "\n";
+
+  # Now print it out wrapped, and see the bug:
+  print wrap ("\t", "        ", "$test_text");
+  print "\n";
+  print "\n";
+
+If the line of equal signs were one shorter, then the bug doesn't
+happen.  Interesting.
+
+Anyway, rather than fix this in Text::Wrap, we might as well write a
+new wrap() which has the following much-needed features:
+
+* initial indentation, like current Text::Wrap()
+* subsequent line indentation, like current Text::Wrap()
+* user chooses among: force-break long words, leave them alone, or die()?
+* preserve existing indentation: chopped chunks from an indented line
+  are indented by same (like this line, not counting the asterisk!)
+* optional list of things to preserve on line starts, default ">"
+
+Note that the last two are essentially the same concept, so unify in
+implementation and give a good interface to controlling them.
+
+And how about:
+
+Optionally, when encounter a line pre-indented by same as previous
+line, then strip the newline and refill, but indent by the same.
+Yeah...
+
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..5d45ed1
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,6 @@
+zope-groupuserfolder (0.3-1) unstable; urgency=low
+
+  * Initial Release.
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Wed, 16 Apr 2003 10:04:50 +0200
+
diff --git a/debian/config b/debian/config
new file mode 100755
index 0000000..575a51b
--- /dev/null
+++ b/debian/config
@@ -0,0 +1,22 @@
+#!/bin/sh -e
+#----------------------------------------------------------------
+# Simple `.config' script for zope-* packages.
+# First coded by Luca - De Whiskey's - De Vitis <luca@debian.org>
+#----------------------------------------------------------------
+
+# Load the confmodule.
+. /usr/share/debconf/confmodule
+
+# Setup.
+db_version 2.0
+db_capb backup
+
+# Prompt the question to the user.
+db_input low "$(basename $0 .config)/postinst" || true
+db_go
+
+# Stop the communication with the db.
+db_stop
+
+# That's all folks!
+exit 0
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..4e94e29
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,22 @@
+Source: zope-groupuserfolder
+Section: web
+Priority: optional
+Maintainer: Sylvain Thenault <sylvain.thenault@logilab.fr> 
+Build-Depends: debhelper (>= 3.0.0) 
+Standards-Version: 3.5.8
+
+Package: zope-groupuserfolder
+Architecture: all
+Depends: zope
+Description: Group management for Zope [dummy package]
+ GroupUserFolder is a kind of user folder that provides a special kind of user
+ management.
+ Some users are "flagged" as GROUP and then normal users will be able to belong
+ to one or
+ serveral groups.
+ .
+ .
+ This package is an empty dummy package that always depends on
+ a package built for Debian's default Python version.
+
+
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..5a2656f
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,18 @@
+This package was debianized by Sylvain Thenault <sylvain.thenault@logilab.fr>  Sat, 13 Apr 2002 19:05:23 +0200.
+
+It was downloaded from ftp://ftp.sourceforge.net/pub/sourceforge/collective
+
+Upstream Author: 
+
+  P.-J. Grizel <grizel@ingeniweb.com>
+
+Copyright:
+
+Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
+Copyright (c) 2002 Ingeniweb SARL
+
+
+This software is distributed under the term of the Zope Public License version 2.0.
+Please, refer to /usr/share/doc/zope/ZPL-2.0
+
+    
diff --git a/debian/postinst b/debian/postinst
new file mode 100755
index 0000000..e254c5a
--- /dev/null
+++ b/debian/postinst
@@ -0,0 +1,50 @@
+#! /bin/sh
+#----------------------------------------------------------------
+# Simple `.postinst' script for zope-* packages.
+# First coded by Luca - De Whiskey's - De Vitis <luca@debian.org>
+#----------------------------------------------------------------
+
+set -e
+
+# summary of how this script can be called:
+#        * <postinst> `configure' <most-recently-configured-version>
+#        * <old-postinst> `abort-upgrade' <new version>
+#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+#          <new-version>
+#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+#          <failed-install-package> <version> `removing'
+#          <conflicting-package> <version>
+# for details, see /usr/doc/packaging-manual/
+#
+# quoting from the policy:
+#     Any necessary prompting should almost always be confined to the
+#     post-installation script, and should be protected with a conditional
+#     so that unnecessary prompting doesn't happen if a package's
+#     installation fails and the `postinst' is called with `abort-upgrade',
+#     `abort-remove' or `abort-deconfigure'.
+
+# Load confmodule.
+. /usr/share/debconf/confmodule
+db_version 2.0
+
+case "$1" in
+    configure)
+		# Get the answer.
+		db_get "$(basename $0 .postinst)/postinst" || true
+		test "$RET" = "true" && /etc/init.d/zope restart
+    ;;
+    abort-upgrade|abort-remove|abort-deconfigure)
+    ;;
+    *)
+        echo "postinst called with unknown argument \`$1'" >&2
+        exit 0
+    ;;
+esac
+
+# Stop the communication with the db.
+db_stop
+
+#DEBHELPER#
+
+# That's all folks!
+exit 0
diff --git a/debian/prerm b/debian/prerm
new file mode 100755
index 0000000..8cd992c
--- /dev/null
+++ b/debian/prerm
@@ -0,0 +1,40 @@
+#! /bin/sh
+#----------------------------------------------------------------
+# Simple `.prerm' script for zope-* packages.
+# First coded by Luca - De Whiskey's - De Vitis <luca@debian.org>
+#----------------------------------------------------------------
+
+set -e
+
+# summary of how this script can be called:
+#        * <prerm> `remove'
+#        * <old-prerm> `upgrade' <new-version>
+#        * <new-prerm> `failed-upgrade' <old-version>
+#        * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
+#        * <deconfigured's-prerm> `deconfigure' `in-favour'
+#          <package-being-installed> <version> `removing'
+#          <conflicting-package> <version>
+# for details, see /usr/share/doc/packaging-manual/
+
+# I simply replaced the PACKAGE variable with the subscript
+dpkg --listfiles $(basename $0 .prerm) |
+	awk '$0~/\.py$/ {print $0"c\n" $0"o"}' |
+	xargs rm -f >&2
+
+case "$1" in
+	remove|upgrade|deconfigure)
+	;;
+	failed-upgrade)
+	;;
+	*)
+		echo "prerm called with unknown argument \`$1'" >&2
+		exit 0
+	;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..866f9f1
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,89 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper.
+# GNU copyright 1997 to 1999 by Joey Hess.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# This is the debhelper compatability version to use.
+export DH_COMPAT=4
+
+
+
+build: DH_OPTIONS=
+build: build-stamp
+build-stamp: 
+	dh_testdir
+	
+	touch build-stamp
+
+clean: 
+	dh_testdir
+	dh_testroot
+	rm -f build-stamp configure-stamp
+	rm -rf build
+	rm -rf debian/python?.?-tmp*
+	dh_clean
+
+install: DH_OPTIONS=
+install: build
+	dh_testdir
+	dh_testroot
+	dh_clean -k
+	dh_installdirs
+	
+	find . -type f -not \( 			-path '*/debian/*' -or 			-name 'build-stamp' -or 			-name 'LICENSE.txt' -or 			-name '.cvsignore' 		\) -exec install -D --mode=644 {} debian/zope-groupuserfolder/usr/lib/zope/lib/python/Products/GroupUserFolder/{} \;
+	
+	
+	
+
+
+# Build architecture-independent files here.
+binary-indep: DH_OPTIONS=-i
+binary-indep: build install
+	dh_testdir
+	dh_testroot
+	dh_install
+	
+	
+	
+	
+	gzip -9 -c ChangeLog > changelog.gz
+	dh_installdocs -A TODO changelog.gz 
+	dh_installchangelogs
+	
+	dh_link
+	dh_compress
+	dh_fixperms
+	dh_installdeb
+	dh_gencontrol 
+	dh_md5sums
+	dh_builddeb
+
+# Build architecture-dependent files here.
+binary-arch: DH_OPTIONS=-a
+binary-arch: build install
+	dh_testdir 
+	dh_testroot 
+	dh_install
+	
+	
+	
+	
+	gzip -9 -c ChangeLog > changelog.gz
+	dh_installdocs -A TODO changelog.gz 
+	dh_installchangelogs
+	
+	dh_strip
+	dh_link
+	dh_compress 
+	dh_fixperms
+	dh_installdeb
+	dh_shlibdeps
+	dh_gencontrol
+	dh_md5sums
+	dh_builddeb
+
+binary: binary-indep 
+.PHONY: build clean binary-arch binary-indep binary
+
diff --git a/debian/templates b/debian/templates
new file mode 100644
index 0000000..1ec4847
--- /dev/null
+++ b/debian/templates
@@ -0,0 +1,7 @@
+Template: zope-cmfforum/postinst
+Type: boolean
+Default: true
+Description: Do you want me to restart Zope?
+ To let this product/feature work properly, you need to restart Zope. If
+ you want, I may restart Zope automatically, else you should do it your
+ self.
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..3ed49e9
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,5 @@
+# Example watch control file for uscan
+# Rename this file to "watch" and then you can run the "uscan" command
+# to check for upstream updates and more.
+# Site		                Directory	Pattern			Version	Script
+ftp.sourceforge.net	/pub/sourceforge/collective	GroupUserFolder-(.*)\.tar\.gz	debian	uupdate
diff --git a/design.txt b/design.txt
new file mode 100644
index 0000000..871edc0
--- /dev/null
+++ b/design.txt
@@ -0,0 +1,34 @@
+Here are the main initial ideas behind GRUF :
+
+   Before we started writing this component, we spent a lot of time on 
+the design (yes, using paper and pen ;)), thinking a lot on how to be 
+as generic as possible. As a conclusion of our design sessions, we came
+up with the following requirements :
+  
+  - a group has to be seen by zope like an user. This way, we can
+guarantee that the _whole_ standard security machinery of Zope will
+continue to work like a charm, without even a hotfix.
+  
+   - a first consequence of this is that GRUF will work out of the box
+   with any Zope application, including Plone ;)
+   
+   - a second consequence is : groups just have to be stored in
+   a separate acl_users
+  
+  - GRUF must be able to handle _any_ existing acl_users component ; including LDAP
+  or sql one
+  
+  - GRUF has to be as transparent as possible to applications (read
+  "should act as a normal user folder")
+  
+  - Group nesting should be supported
+  
+  - Multiple sources for users should be supported (ex : source 1 is
+  SQL, source 2 is LDAP, source 3 is another LDAP).
+  
+  The API was designed, test cases were written, code was done,
+documentation was written, first version went out and the first customers
+were (very) happy. Yes, exactly in this order ;)
+
+
+
diff --git a/doc/FAQ b/doc/FAQ
new file mode 100644
index 0000000..301cefb
--- /dev/null
+++ b/doc/FAQ
@@ -0,0 +1,43 @@
+Can I nest some GRUFs?
+    Maybe... but what for ?
+
+Does GRUF support nested groups ?
+    Nested groups in group-whithin-a-group feature.
+    And, yes, GRUF supports it since 1.3 version.
+
+Does GRUF support multiple user sources ?
+    Multiple user sources is a feature that would allow you to store users in several userfolders.
+    For example, you could have your regular admin users in a standard User Folder, your intranet
+    users in an LDAPUserFolder and your extranet users in an SQL-based user folder
+    GRUF supports this from version 2.0Beta1.
+
+Can I use GRUF outside Plone ?
+    Yes, yes, yes, yes and yes. This is a major design consideration for us.
+
+Is GRUF stable ?
+    It's used in a production environment for several major websites. Furthermore, it's qualified to be included
+    in Plone 1.1. It's considered reliable enough - except for "Beta" versions, of course.
+
+Is GRUF maintained ?
+    Yes, it is, actively. Features (especially regarding useablility) are often 
+    added to GRUF. Official releases are considered very stable.
+
+Can I help ?
+    Yes, for sure !
+    GRUF is an Open-Source project and we, at Ingeniweb, are always happy to help people getting involved
+    with our products. Just contact us to submit your ideas, patches or insults ! :-)
+    In any case, if you want to work on GRUF's CVS, please work in a branch, never on the HEAD!
+    I want this to ensure the latest CVS HEAD is always very stable.
+
+Why cannot I assign local roles to groups using Plone 2.0.x ?
+    There's a bug in Plone's folder_localroles_form in Plone 2.0.x, preventing it to work with
+    GRUF 3. That's because group name is passed to GRUF's methods instead of group id.
+    To solve this, you either have to fix the form by yourself (replace group_name by group_id),
+    or wait for Plone 2.1 ;)
+    A sample fixed form is provided in the gruf_plone_2_0 skin folder (which is NOT installed
+    by default).
+
+Does GRUF work with CASUserFolder
+    There are two CASUserFolder implementation. One made by a clown and one made by a megalomaniac ;)
+    I prefer the first one. He prefers me anyway ;) See this page for more information:
+    http://www.zope.org/Members/mrlex/CASUserFolder/CASUserFolder/CAS_and_Zope
diff --git a/doc/GRUF3.0.stx b/doc/GRUF3.0.stx
new file mode 100644
index 0000000..50e77fb
--- /dev/null
+++ b/doc/GRUF3.0.stx
@@ -0,0 +1,80 @@
+GRUF 3.0 is out !
+
+  Abstract
+
+    GRUF 3.0 is out ! This new version brings a lot of API enhancement, along with far better
+    test cases. So, this version is simpler to use for the programmer, and safer to use
+    for end users. And, the cherry on the cake, this version brings a far better LDAP support,
+    especially for large LDAP directories for user searching and listing.
+
+  Link
+
+    Here is the link to <a href="https://sourceforge.net/project/showfiles.php?group_id=55262&package_id=81576&release_id=248008">
+    GRUF 3.0 on Sourceforge</a>.
+
+  What's new ?
+
+    * **New API**, easier to understand and to use (and well-documented in an interface).
+
+    * Complete **LDAPUserFolder** integration, including user creation and user modification.
+
+    * Complete **LDAPUserFolder** integration for **groups**, including group creation and modification!
+   
+    * Far better **test case**, with more than... 220 tests, including LDAP tests !
+
+    * Better **Plone** interfacing - this will require Plone 2.1 to work with Plone's
+      management panels.
+
+  What's the future ?
+
+    This version is not fully compatible with Plone2.0 anymore because of the API changes.
+
+    So the next step is to integrate GRUF3 into Plone's next version (namely 2.1). A working
+    branch is already available on <a href="http://svn.plone.org/">SVN</a>: 'pjgrizel-gruf3-branch'.
+    You can patch your Plone2 against this branch if necessary, but this won't be supported!
+
+    Then, GRUF 3.1, which we plan to release this summer, will include **local roles blacklisting**!
+
+
+GRUF 3.0 est sorti !
+
+  Résumé
+
+    GRUF 3.0 est sorti ! Cette version apporte un certain nombre de modifications pour les 
+    programmeurs (nouvelle API, plein de nouveaux tests) pour l'environnement Plone, mais aussi 
+    et surtout simplifie la configuration et l'interfaçage avec des annuaires LDAP.
+
+  Lien
+
+    Voici le lien vers <a href="https://sourceforge.net/project/showfiles.php?group_id=55262&package_id=81576&release_id=248008">
+    GRUF 3.0 sur Sourceforge</a>.
+
+  Quoi d'neuf ?
+
+    * **Nouvelle API**, plus facile à utiliser et à comprendre (et bien documentée dans des interfaces)
+
+    * Support complet de **LDAPUserFolder**, y compris création et modification d'utilisateurs.
+
+    * Support complet de **LDAPUserFolder** pour les groupes ! Y compris création et modification de
+      groupes.
+
+    * Super **test case** avec plus de 220 tests, y compris des tests avec LDAP.
+
+    * Amélioration de l'interfaçage avec **Plone** pour la gestion des membres et des groupes.
+    Ceci nécessite la version 2.1 de Plone pour fonctionner.
+
+  Et maintenant, qu'est-ce qu'on fait ?
+
+    Cette version de GRUF n'est plus pleinement compatible avec Plone2 (notamment au niveau des
+    pages d'administration des utilisateurs et des groupes) du fait du changement de l'API.
+
+    La prochaine étape est donc d'intégrer GRUF3 à Plone 2.1. Une branche déjà opérationnelle
+    est disponible sur le <a href="http://svn.plone.org/">SVN de Plone</a> : 'pjgrizel-gruf3-branch'.
+    Les grufeurs les plus acharnés prendront un malin plaisir à patcher leur Plone2 avec cette branche,
+    il n'y a pas d'obstacle technique à cette manipulation.
+
+    L'étape suivante est l'intégration du **blacklisting de local rôles** (c'est plus élégant à dire que
+    "noir-listage des rôles locaux") dans GRUF 3.1. Tout ceci sera disponible cet été si la canicule
+    le permet !
+
+
diff --git a/doc/GRUFLogo.png b/doc/GRUFLogo.png
new file mode 100644
index 0000000000000000000000000000000000000000..c6aa14de91bf714ed0430db225cd17aa4e14f76c
GIT binary patch
literal 31213
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
R+p%MY_f7AW={<Y*e*mzWs<Z$A

literal 0
HcmV?d00001

diff --git a/doc/SCREENSHOTS b/doc/SCREENSHOTS
new file mode 100644
index 0000000..778a4b8
--- /dev/null
+++ b/doc/SCREENSHOTS
@@ -0,0 +1,32 @@
+.. figure:: doc/small_menu.png
+   :scale: 30
+   :target: doc/menu.png
+
+   The way GRUF shows-up in the Zope Management Interface
+
+
+.. figure:: doc/small_tab_overview.png
+   :scale: 30
+   :target: doc/tab_overview.png
+
+   Detail of GRUF in ZMI
+
+.. figure:: doc/small_tab_groups.png
+   :scale: 30
+   :target: doc/tab_groups.png
+
+   The overview page with many users
+
+.. figure:: doc/small_tab_groups.png
+   :scale: 30
+   :target: doc/tab_groups.png
+
+   The groups management interface
+
+.. figure:: doc/small_tab_sources.png
+   :scale: 30
+   :target: doc/tab_sources.png
+
+   A sample security audit page
+ 
+
diff --git a/doc/folder_contents.png b/doc/folder_contents.png
new file mode 100644
index 0000000000000000000000000000000000000000..390ccb7b20824902e1ef68af289ed3b1c587ff18
GIT binary patch
literal 22877
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

literal 0
HcmV?d00001

diff --git a/doc/icon.png b/doc/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..c6aa14de91bf714ed0430db225cd17aa4e14f76c
GIT binary patch
literal 31213
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
R+p%MY_f7AW={<Y*e*mzWs<Z$A

literal 0
HcmV?d00001

diff --git a/doc/interview.txt b/doc/interview.txt
new file mode 100644
index 0000000..965da26
--- /dev/null
+++ b/doc/interview.txt
@@ -0,0 +1,111 @@
+(Voici le texte d'une interview réalisé par Tarek pour le site zopeur.com)
+
+(Désolé pour le français ;-) )
+
+
+
+
+1) qu'est ce que GRUF ?
+
+ GRUF  signifie  "GRoup  User  Folder". Il s'agit d'un User Folder pour
+ Zope  capable  d'offrir un support pour les groupes. Contrairement aux
+ autres  types  d'UserFolder  se basent sur divers supports (ZODB, SQL,
+ LDAP,  ...) pour identifier les utilisateurs, GRUF délègue cette tâche
+ à  un UserFolder classique. Par exemple, pour utiliser GRUF avec LDAP,
+ il  suffit  de coupler GRUF à un LDAPUserFolder tout à fait classique.
+ Cette architecture permet de se dispenser de l'écriture de plugins.
+
+
+2) Quels sont ses particularités / avantages comparé à d'autres produits
+ du genre ?
+
+ Avec  GRUF,  aucun  patch n'est fait dans le code de Zope. GRUF est un
+ UserFolder classique et n'utilise aucune "magie" pour fonctionner.
+
+ Aucun patch dans Zope n'a été nécessaire ; pas même de MonkeyPatch.
+
+ Dans  l'interface d'administration de GRUF, on crée deux UserFolders :
+ un pour les groupes et un pour les utilisateurs. Dans l'UserFolder des
+ utilisateurs,  le  groupes  sont affectés aux utilisateurs en tant que
+ rôles.
+
+ Dès que l'on sort de GRUF, en revanche, les groupes sont vus comme des 
+ utilisateurs  "normaux"  sous  Zope. On peut leur affecter des droits, 
+ des rôles locaux, etc.
+
+ C'est cette "astuce" qui fait que GRUF fonctionne directment avec
+ toutes les applications Zope, sans rien changer au code source !
+
+ L'architecture  de  GRUF  permet  d'utiliser  des  types  d'UserFolder
+ classiques  comme  base  d'utilisateurs  ou  de groupes (le UserFolder
+ standard  de  Zope  mais aussi LDAPUserFolder, ExUserFolder, etc). Pas
+ besoin de développer et de maintenir des PlugIns !
+
+ Autrement dit, GRUF reste simple dans son principe, totalement intégré
+ à  Zope (pas de "hotfixing" de Zope), et compatible avec virtuellement
+ tous les types d'UserFolder qui respectent l'API standard de Zope.
+
+ Enfin,  un des points forts de GRUF est son plan de tests... Plusieurs
+ centaines de tests pour garantir un maximum de qualité !
+
+
+3) Dans quelle mesure l'outil peut il s'intégrer à un portail Plone ?
+
+ Depuis  Plone2,  GRUF  est  partie  intégrante  de  Plone.  Des écrans
+ spécifiques  ont  été  développés  pour administrer les groupes depuis
+ l'interface  de  Plone  mais  en dehors de cet aspect "visuel", aucune
+ adaptation  au  niveau  de  la  programmation  n'a été nécessaire pour
+ rendre Plone compatible avec GRUF.
+
+ Ni pour rendre GRUF compatible Plone, d'ailleurs ;)
+
+ Depuis  Plone2,  un  "tool"  est  proposé  pour  rendre la gestion des
+ groupes  sous  Plone  similaire  à  celle  des  utilisateurs  sous CMF
+ (l'équivalent du MembershipTool, mais pour... les groupes !).
+
+
+4) Et à un autre portail (CMS,Zwook, etc.. ) ? Est-ce que l'outil est
+dédié Plone ?
+
+ Depuis  le  départ,  GRUF est un outil _indépendant_ de Plone. Et nous
+ nous  efforçons,  à chaque version, de vérifier son bon fonctionnement
+ en  dehors  de  Plone.  Puisque  GRUF  ne modifie rien à la logique de
+ gestion  des utilisateurs de Zope, il est donc tout à fait possible de
+ remplacer  n'importe quel UserFolder pour bénéficier de la gestion des
+ groupes.
+
+ Il  est  donc  possible, en théorie, de l'utiliser avec ces outils, si
+ ceux-ci  n'utilisent  pas  eux-même du code spécifique à un UserFolder
+ particulier.
+
+
+5) Le futur de GRUF ?
+
+ GRUF3,  qui est encore en phase de qualification, propose une nouvelle
+ API  beaucoup  plus  intuitive.  Nous  avons  aussi optimisé certaines
+ routines,  notamment  pour  LDAP  (LDAPUserFolder  dispose en effet de
+ beaucoup d'optimisations spécifiques).
+
+ GRUF 3 est en phase finale de qualification auprès d'un annuaire de
+ 90.000 utilisateurs ! ;)
+
+ La  prochaîne  étape  dans GRUF sera la possibilité de restreindre des
+ rôles  locaux  : actuellement, Zope ne permet que d'en ajouter, jamais
+ d'en  soustraire  - alors que cela pourrait s'avérer bien pratique. Si
+ tout va bien, cela sera implémenté dans les prochaînes semaines.
+ C'est la notion de "BlackList".
+
+ Nous   avons  également  plein  d'idées  pour  rendre  les  interfaces
+ d'administration  des  utilisateurs/groupes,  que  ce soit côté ZMI ou
+ côté  Plone,  plus intuitives et agréables. Bref, le travail ne manque
+ pas !
+
+ D'ailleurs, n'oublions pas que GRUF est un composant OpenSource, et
+ que, à ce titre, tout le monde peut apporter son grain de sel : code,
+ idées, écrans, doc, traductions, etc...
+
+ Et  quoi qu'il en soit, nous devons une fière chandèle à la communauté
+ Plone  qui  a  testé  intensivement  GRUF,  nous a aidé pour certaines
+ parties,  nous  a envoyé des patches et des idées... C'est là toute la
+ force d'une communauté soudée !
+
diff --git a/doc/menu.png b/doc/menu.png
new file mode 100644
index 0000000000000000000000000000000000000000..cb254846263c79fdfcbb3b5045d22b39b720c9f6
GIT binary patch
literal 62208
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

literal 0
HcmV?d00001

diff --git a/doc/tab_audit.png b/doc/tab_audit.png
new file mode 100644
index 0000000000000000000000000000000000000000..58d78f1940193acdafadd0172b31191c1a80e5fa
GIT binary patch
literal 25238
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
AmH+?%

literal 0
HcmV?d00001

diff --git a/doc/tab_groups.png b/doc/tab_groups.png
new file mode 100644
index 0000000000000000000000000000000000000000..c515febca610482b60e5af4326c2945005799cf4
GIT binary patch
literal 31957
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
Klg*RH`2R2HhAwFU

literal 0
HcmV?d00001

diff --git a/doc/tab_overview.png b/doc/tab_overview.png
new file mode 100644
index 0000000000000000000000000000000000000000..57793602a82bcaf6af962bb3a42688beab14d093
GIT binary patch
literal 30494
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
X{YG8plCJ<r1}-xr%cErm*lYg=_X0ly

literal 0
HcmV?d00001

diff --git a/doc/tab_sources.png b/doc/tab_sources.png
new file mode 100644
index 0000000000000000000000000000000000000000..4666f446dac6c759d5bfaf6c8f3ce6c95e735be5
GIT binary patch
literal 23197
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
Qz$So>roo9yb?eao3x&U&<p2Nx

literal 0
HcmV?d00001

diff --git a/doc/tab_users.png b/doc/tab_users.png
new file mode 100644
index 0000000000000000000000000000000000000000..2bc6e9cee70cd238c9ddeb5a7900b193e870ec92
GIT binary patch
literal 38818
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
O?Ef=YKbLh*2~7YAXRMC^

literal 0
HcmV?d00001

diff --git a/doc/user_edit.png b/doc/user_edit.png
new file mode 100644
index 0000000000000000000000000000000000000000..9b8bea511c94bc9838e1c714a757cb3f253bb92a
GIT binary patch
literal 69485
zcmbq(V{m0%&~A*0ZF6GVb~3ST+sTRTOl)V8nIscTY}>YNpWxiQ_1*jT{`tCUZS?Nl
z)xCDrvwHQjqg9lok-p)70|Ns?l9iEA1p|ZV1Oo$afP?wckPp>zex0D)#AMaszQiBS
zJnHKn-dRS+4Gatc{l5V|w;A~Ur6hHi)OJ^MvUK+{akT(bG%<5^a<DMBF(H+*akcPr
za<y|KRn%@zrlb6_#s1G$+||Ow-Nwn0RNcnG0*r;5nT?&9g`Ez@eGLo@+RfQjQ_DN|
zQpeA?gbNi1!frwAl62{J!>p&?GhC~9V&8UW0`olN!#-1|=zLbP_e-$M($MJg8LXJL
zfYkj0;^%pRFbEqEwQU5)=*aA_ey213#{tD*y)hE!3?)+hO-wMaldy%KFdNd)sm>`|
zeEsIS+=k#P`>53?Fz|}co}7GdzZdhl1p42<3Nuc)_U5+$FXJ0tkB;j<L`uNm8c><=
zCQ9g1=h=Df3a}bN!9!}>`f)u75w}kpY=5d3AK*C2B0{nK-opQA<^Q947uw>PbdLFg
z>J#?PFSnTWrI4l18_xIo1*&$Jz~I`kX#IZpvt0U~qr+IMcC(eX{(&6ShktGYqtOb}
z_!?^WQ4UqA(>xn=ot9L6aI%G2FLA5v_5<YAFZp?G15$fG#~l2n;_i+a6k=|(mHmkc
zy17TGV(9*PNFyK+wB^{Lvx%GWQkxV598Gm4Y$tK{>wl>Oc4G*UgRr97WsO4s93BZ3
zv1W20OcSSThvSzhAgFsC)P7%kEe(hO;F>|EqGp<mFrWqOfbp!+u$F+x^7=9D-=cEL
zMnrsm`~vry$((RFurj0*1^`t(QB+@8c>j^N*mgyO{6m=sD9S>Qw86eTb9#LC5+2L3
z|H*g$w|oN#Sqyu}Xwz#;Ai*R(HSE1~c-Nx%{4v6?H#VAg5FJTV0OvQUB4u!N-oX$_
zgQOC&O7%zUrnc4N7k3qrg;DFH3BxBkCm4$0cNX1r9uj*WABqoHOcsHiHU_G{)sF2E
zfTK0nd)3NT($ikYq}u4Wj%j6f;k1F~>q7w2V2$P7kbS(*fabM=Mw_h0NfN*6Oro6%
z?Zsm<>B#pLR!1uky511;WyjQaNpH`?DE43sUocewwX$qSHi&|BdhLZm+)L}5Odiq0
zZQ?);)SF0pS2Bp~(+hd%;V%AOBvy6%K%j*wXwe9>TtsBLa<1?lDx`Wf=MuyQrAK&G
z{n@Eo2QpDnsaE^O{+{?SU4$KwKcIw|k-fqRgSY*V!g}JUJ!cxQgb?IGrgaezoy)Xt
z`Le7Zy4=2|`re@4t`~QLRVx~?OlIe$7e5E98cYn|KJSa}3|e04-eo2|r|`*u*CRqI
z2IHbDQcO5gE4)U=`$qa>c>olgY(^lo`+YQQAi~d!V1-<KkzXOTU>dXmk@Bic61{KH
zSg(>%9BPF=84%_E-B>6k<u+6m9BJYZkRPM4k~WeuN6`T5>QwKi99WrsnvtVS`#E*X
zto1&Y=FU7oY^Co&&(UjZ=glw~Vd2^Mi8Uf5y<PY*`6_(<9lrTmF?>seKU;gRMVNw_
zg?ul2;@pV^qEilpD`ImjLRVBJ3(+)4JQ#eS!;N`i%mBu!I`RNzSOh4RTNwS9jDO-E
znm@csqa*Nx#v}H}6Me4ul<KfuOH2MOz~XZ{TZ9a_L7_hh<}XOpR{y!X2=;ir2TE3*
zN)9qC{}g}DP@<>f0SChQhY&0Prc6iS`WKQxrf8qhKFIh?C|O}0g`oKt5RuHm7{D)I
z2arA|e9+!!F{<7f=BO_Lg5Y{gm=C8R_${0<2h#E73YG_UA3rA!KA`B<INzGnc%yve
z_-kPDUW$4$oq=Gs0X=D-`q^PCIw~OXp%>y$qu;O7EVkFEAXd;HHl{I!<ZkJAnh&$`
z7!zD{EEy4;7!Z_J+zeUWCqp@43;>S41fYd9oB)xHazY2)6nu|8E#R&pR<8upnF4)q
z2WP#T$$of|Sdcz6U#bF`MQnkzJ#P;I@`C`~w@U3i7vZ8gx7Y0u!06|-U4PGWfrg7A
zNaFt^?^-v^K*RceJ9Mb$FMyVDuoII4ZjF6@T|<41|CM|%WiJ8)!%J|mQ&;<fbOSxN
zb*cIx%l(y<YgszKs;Ss2rM$HYb8uA3Ug;m0-v@sVB>iMK9+XT9nv7@gq0)&)i52z+
z4W3?Q^;$TSf&D1WC2CO6hhfkBY&*`$&&NC4(YUu<y1G`d8tMRz@48zi(AStHp5M=3
zC2-FlJKY$o+ukp~F*<23$G>iU&gAH%opp5c2k{?u2=nCkrA{LM+`9$b8#eo~+db{r
zS^0T$&J)`ta4l`wDms)J*r+)OYzqqny(#Z}@@|k=<78JD*y-uws7`ZwZ8;I~YF|8&
zrF98rPSMo}I{ka9V7J>6;K`iQ=jpke?ciO3h8JiNv@W5Y>?O9a^<3S>BJxD`L2Gwt
zyFxi#^-LuKy5A>l5<cv#-gfZX&u#89@gr*Qfzu}vJv9@x@G_HL5N~uZP&cD#GBZ{M
z%Kv!OFBVEXZ9=gs*x8bBYZ|?5vfJRl6iT#tPA5MvgEp*5C~yrn3N>CP9i!4Wu(rAZ
zrNn+l#R@HKQIM`VW%XryrZkqbPmwt-#*I{4(Y9E!`p{EjtW>sTk)!x_i<PhWv+N9S
zp)FMLg%G@#;?kl}6y{oq?XgNACW?k=NGo*wK$gkXb(9f_c;`~~&P*5vBO^skj$t*4
z+L7gDUkzPI_ACjQ8i0DlX2WQ(#W4wih?uKnRY!E2aQ>y>s{O5BR3vP~cn}Vc@05g<
z{;dBEab$jbUlZDci2sIE_^I7pIPL%=;ecyplIlhVqe~ckxg*0F5tkNY*ab@3MDJ%8
zs?_>e&|g!j;9N`-T!>`RCwlRa-w3vnxP9^DNdiv<In^KFN6+wcILS5U#w7wRxJU_z
zvl@=(L;rS)AyzWu{Z|C`S1y8hb9=3-Iw4P`*Q81FrwI$<lSci6?h!7@F$hARWu&OQ
zGR#jjT`PW$%J4b)tyD(l-eD)J4szkKu|Xe-lDFS3&>+J3O*lJe9QdE=5H;w`VT@8-
zaqDj^&k)vNNdA$h%XqJ0sK9TKD&@qn>qy$>JmKWk<C4$nRC!_+fm8FR3klM&fWu9I
z0>6%s=Ib}k9fY&4i~W-y#nH=4<~F^ICH;GgCahj!CQ>~zYDb^}95?>(U(g|NkDzOS
z_Sh<=T!94dl01uEcM5WPKRKIgneUN076^V6B1MRFhL_L+0^?*CJd*DDi_7;)=oyXY
z+M8OdL%A>BdC27DfCDsHp#SC#EZ&c6sVZ~vJ!wu)ep(N4;Uih(z?E6p5hp!uZX$~{
z8OU9bpb)zzHetKhNfvHII|&-ApE(spaYeT3DIv`3c5z3%l$Oa0**}Chw+~MBL}psS
zymG`qFhS5ih{OAF)OiWVGe4U+7nfgPxrb^`Em|ihiXj*S5pu>09WV;LiDt&PKzqXX
zZmw<5G33B%FOTFBm%g4T<7(BK;yc7s(19ES#SzL%U@$yvdm0r@COdo`TEWBAm7~4Q
zW5-dPK@#h{9jae9qaqPfO%J3DM29(Hg{@RP2ukiD1H1hzkyPrO#BglPsJuRUr>qlK
zikFCH5zDj}Zg4lhr3M5aL<aPq$d=a%DG|12<3)&lNX<T(WA&5U<4(vTm6)wKg1hIt
zxa$%oyW6V*cgd%|g~Q+s^E*N-sY&qfQDphcT)6P_-d{>ei8<G>a`_mQNc*D;hhaa;
zqT_|b9C)E=Dxmwr4&dDe?T*l>^dk8q7e&SLO<~a^u0g4q7}6}J6^F+|E77%;mY?^@
zvJ07yOBwfHyEA_`2bzeEsEY+xWwJzNs8nu;;7PT?W$E(jKc2w-`+gF_9lzJ|Fw)dv
zc;P17;;o6Sbn_d1h%Dlf5c|yT9wz0(C04l0%q!b8i9+egTjd~(<Sj%m#dQ3;ek!7U
z7K^RxMwE*pUCW4yq{Eb#ewC?IWVSJOhsjB)$s{*nR0&ju1K8CXo}$@+tR^<#r;LCP
zp>M8*3<_?%badZaV=5l)VWW8c@JCZuX35E8DDOA?52QWBw6J-3;TBifqfp4;G~)<?
zY)JOh#I)gZs5J>(MAn|YD1;)c{EryOQKhgVvf>3>G}X3LoO#9Vry84{p!p#!A3`Fu
zVjK+qp^?tZ0c0mHjqeAjm)n20#o^*9$M3DWI;;=EuTWe$hbEJf6{C-)vE%ZkBw^to
z;sn$xvEUKWO3^Wux4amDrYt;Bam_ju=h4x<f@e|~vh60UnQ?<G0T~Lb{s{~u&W|ZQ
zhxl5zJ!#x2mcA``I!aL7^lVoT$35E0D^?L4CvK%LoaC|**wPA!({CM;2G%5|mzPFC
zt5GWBj!uj9q4yAOvxs>~K8z1c&7nV9e;hlC=b2EkklSu!4E$t~QkFh-p}lZJHf)i9
z7gFpSt<*)V&~;~pgr!^7jnE}m^(9xue3}qk?`WQUeUZpT6<mCo=nzQFOo+lw>mbV+
zP0(H$+*Ilm5s7SO7?y(&v(@q)(np67;R-7^39}E={dpi#PC-$Qzdj-=+rpz6RuLYI
zd7H&%LRP^3T{C~f#l@UXhpSId3q*U<C|TXNzm-2irUaD^U-N95W82$#4$Yu0Yf5Gl
z!Il;A@J=l$GnDN1I}Q&f?wX}$C^g^O08&&W;L1HFFfs0Te#_WC?hFr&(H+LCSaGdi
zk4u&HA9V2)-+X9hpCO906JuraLSI$DkPS?zi6qo#FoM4r(QD=shT$wCf>bceUWa6;
zvA$$B5vgn(ZatW&p$uIt8J_fHgeKQO00q4%w?A;|DAmhcprch1TMbP-TmX#!46Kv^
zS5EvXdi!r@Mb9^>t&J7?mN?Ttzawo963bu5XX%<c8V3AKjmeds21cWK$Q&!M$CP7&
zsQk+)S^$dW&DOK^$uerMOS$lV)kj`*Wz;J&ZiDh1y%k-60MUzg;-OZ7v!C%IduR2k
z<CzV*xp^(#a2YERs4Nte1Hkth9=w6h0?AAP1d}^hM$(mHi-PbUMJu|&8eT$dOQx`Y
zdnAwx5IKe)!=SM-TPq0m4odNe01w^;!by2Sf<mSIXyWZ*+CAjIC4UcAyoUTuKloLw
z&|+rCjh@v#S64EN&HPtbff(of)@P7(sxPnlh9$8Q!Q_t?Ufg$iOPrz=m<{Le+Ots_
z@R$~<oamyt+GwcKqhUvuVY;{+fg9fs46p{Q*>7sV@{kqpL4n5%1#t2|4~L_tD#57E
zh9Y$Wx32y<T3+5PAtj>X&XB{@K>&n$DMnFjO!{H_4tO}jDdsFSY}tz)8>$+;qJ!2x
zum$I?8#wMpk1ubugBHV_!214{s%JQlx!B6Jwu$Ht*N(|oj;t6}u<QmNdRUhYr)^^;
z_(&k<OlrR&$r$~%wf0g1JL@OXd=bj^@@)zJJ^3&E^udW(j!go6n>E75iAAwHfLCgi
zTmt>mF7jAGK*)-%3@=rgvio<PkTkQS!0u1iZ`HKnSU*<SYwCnZ*<oy4IzE(C*V&DD
zWv2;`y~}gyq~+2i)mu-S1rbGxtMRVtI#irhONp#X)6}xc)HNk6M49=EEed;!%@U<I
z20!+Hz8x;8YuM72loPjN?9W`WC8F>Q{9m=!e`+o=w%c3OGq@{~iVfxpYQ0~-qO$W{
zIWO<>xJjbN`Z^cYYBEb{5^#imC`BWc4Uo(EkEE@Xwsz73JcsU^(G7r?3SD|iqGn}<
z<lokAi2Uq{NNh|(1->Du9{kWwQdQEyDErMOxNNs}aXsW)v-9aP_Tg&lnwC8lSLZ8A
zyWq9T9Z<lW>zSLZsbpbTDy8h-@4_i<myEon7Nt3{2``>JpNLbMQJn`qoQ@V!x5-AK
zl{S)w)rvnyT<sGnmTYn18oGWM<jJy~L>Mf%loD6?!{#JD3hhKV>z_=M!n=gppDG!T
zDy2cz;$NJ}+9iP5fwlsVy*--QCwn5{Tg#h64FLR~Yt4s)R8<l6*b`Z2Y0W=(GiqI~
zOVBgy$f)H!jgX(&*rI!3%9*_wib~7$p6ZQtqo-}FxQp`&O3QiKxy|WmP5H@-Np?T0
z2RF{oYR5?pBlz4|z%P*)?+SJPKs8iKm?P5do*Y?YLic2B=XfU(D%y|mT0n0d#x6lW
zBEX*sF^VFC=fgA_1;ebQWKnl!9Ad|(zMR(>)-wfhiycISW>#VtQ#}=XR9nixXxvJ=
zQa`oeD>i;CtgX1m>lTyNaJvkp^6Z@d>TBcQ2Zj9=`yRx_D^f({oAj>`!xC9nmpOts
zb3+-rQ~M9~8+DN(BKuO=fG;Pa7USvyOODp`D*X0W-0P^?iKy3}YJ`&yKxgFXMX9^A
z1}5u#>B7?)WK4h0V5bx&{Yvh6kM{!IOUm@t>WtqATf?6%uM`Dc!v%=%b(vv%=sCLs
z+7!ri_6cl+HhA<Wmwk#nY2&G|)<)W!%a@r~bLdXGmr{11_oaU$gbJN(38H5RPCW5#
zyQRr(ySn)rir`ILuETAZm2fhGhIM4iv>B5B&1jCdi`F3YpSSL%gqQMluUJ2thN8r(
z_(o*zAKcBfR(P{po1W*E|JIQeHQS&Th5kSddd`Mj699>zS+|^deF9O>w~pI8dnbQ4
zHN+a#!VMWrmIwyR)V%E+|8<~y=?~!<om=A#uIM@%IG;K$>L5_L!;81jcc=^)q5ym{
zXQTPt`J9YG>iT&oV({8SPFUD7{nH{N*E9Ymuq=KF=ur_+v?`E+pSI>+y6zD6v>8s%
z1wh`N7b#2LlUoRskvOy(s)XPGN2mnK(Wkrzn16j1ZKzIxJ2+$|VaoD~b7s`7A9kbp
zK^Jf#IqzMkTV%+umOk&dCl>w&u+i`Okcc=WN7qtTdn3vO6pupbH*ywYc`cKTM|JI%
zX_rhCbc9`FZ{G?OQ_<P*a=+)++A#9ydflZaQg8)|bbHp-HoeS)swW%y%9c1Fh1x7_
zh5LB-!mYfW#JZ8J`aAhbSxT!O%!#^?WO}Px)w_Ev50}VF2nD{6-(fAu)DK(h@f>$^
zIv?5{(N}6{j5Q#Xb(MGm^Dk4VJH>>vRVykUAcL+)js~wm1!`UN!qQYJ`|uwv^H_)_
z^Z6OusPERA^+O=JwWPn_Twgg61&xv^LHvy&+dZD^3lIh|okn-*-cYa8BJB3tAFwks
z&}2UF&wbc#h|?tXNS5!4%p$OAK+yrT6(IKZ{H+t+YFS=`#0qo^knH$ODDBbFLcl;n
z;E%WNZR4KJ;C#6MnUQRpQPIYti-)93)1K|%VoWub*74riB#)Ne(b&qGs{Nl8-M>wy
zc7sc;D?mL)>_11-^R7Bo*X9-%4s=hN7k^gt{x+Ll>t8VLkq3;#J4yZ099-o5A3yXp
zn)-RM)r0yJp9%Pl8ELnT6ZkO|SjzPIngSmMlk@QY<xAJ!zl;JBTh~<dKPvWYpR~Lj
z`xk9L^8X9(Kih4me9iM`Os)TX{b>Pyx;5!`SNy+_j3D%?AN^uFvHa-2LiwPoZ~l7N
zqWeFYJQq|i0tNra3w?eUioj+FO%k;DAKg8hgiTc+z8pz&<Vsngr@bY12tPY(Y=3GM
z0>AHntk_kLCC`oer|XU8k${+bXJ~!X)ayk5c-bL)TQ)UFhv_b^;147<hp{^b?~68D
z!!Iv+o11rFC%2{YbqhY?O;-S`PgYp}y1T6(nb&~%F6Rh-r1eI?sCAMd|77Vjt15wc
zhE*il{xSAWg6T%`qTH*jv3qKx8Y8;#+e2U(Gg;o^GbHO8_TgI#WzChi&cP$yf4LXK
z*&W-exjYucR43#7gVPzZ&L3EurnvNurL+L7?=f)HuOnU_A<w0cuDs%L#-m-8cj`i~
zSIYvp=3wL_Z-uOh*302ZHl1p{Q3^yS!YR3$oD-#TR&0;M^)wahYX>!*{`3%cmwbM(
zFTUp9)eBdUc~;C7yv4twyxQN%4p3jq=N9SC7*^#r3GUgMKl1rAcz1PlR`Q6ex^og*
zMP0@xtnOe`o<qIIC{ylh%G~dvh&x~y8HngEJLPGyuIt%@SBfHJW%;^<GV}yx>;5I)
zq9zul7nc_$$m#(+AP}TLZy-f%_doum9E5u$SzyD1AsI={wgnXnZ+x|ICb0y9aT9-0
z$JuQ6J@QY=K8}_6Y~6QpW}nvviE$43VeGY@=Q{b~`wD^dxJZeTMOzQ=;&bQPe+en2
zKZw@6&sB7PR|H-(kIY4mXAH~A9(K<UY){GohWAJK4+w9cDN8P_FKTzF<U!<E$JSk?
zKtV+jzBh+y^uOBg<&$vecl^aX@7yd382NXVn+>0jjEy()rnCoqzmJ=yzpcQ278tET
zn&vXTPYuY95G==BB$w^x3`68ok><V(iFJHH?zo-$oo&#}eUiOttSP^I?>)lWu{bb`
z^tD-v<rd+im@6>jlZ&*!LeJs0R$v9kyjbzRbX<9NnsEvUvAaNE9zm~@j-%LhWhl<}
zKv(gXKP(xV4w&YcaxnY#F3_58y&V4Ag8d<K`ybDzmHZ<!NvV`U$`B8V=r+lB(~vb_
zE-UdSoMbrarYs%v;jPLH6XV_95Jw8$5C0;v*GlW-5*fr0awU$?qnd$oBasAoPuK^}
zqHEUGrL1(0$SxTWfivvuc?5$$Z|)8-SEbb}x95V0%2Nzo+K=MSv+~ivSr}-$bqZdZ
zw{>IbxwhFW%=i*Pbkr-@pljiED?wbFgz|TRtpF-uP<1?j!FQ)Dy^Sf*@7HBA11RVR
zzVA)Jh|-PV$n5yv=V+(vqt1rs>+g|KlIBIIP0P<45ZKT9Cy;~V6RG#mk{2=0b;_i}
zc!K;-Z#9)ol+we?7NOf=&(>-9!Z{24cpD*sr7~j@)l_i>l0Pj4_m8E?WL*OK4m*1Q
z6olQhn@1%-5a$z3)vpaO&D3H{2iBWov@4^+|2quYl4GL;jXT%YpR?Z~2*KdIr8j%$
z`Vh!{M9n^+zsCibL3hoz<D&^D?pn`|4Y3Go*;tZo3*2qq+&8B@sNwrH_)LHWYdqw-
zOxmaXw&#+Y(9ei|_2V$AO;r*Q+X}t6wO*2@aoC}?-(&>|m7#t6bcx+^hL(<$%a&mU
zwa<+#e<EVdOFJ7#!NQdKEX*+iwj|@GuTzKbI4>I^40REP2=?&vg$ntGmu;~m*E@!-
zyP?wY)C8w(Hn3OT(vSZ9Kw)k~_W7-)-WT<vVr|K5*nZ`AndVoKhEt3bv~VrP(-KYb
za7{5CXFYJ~{CjJTfW{e=+Ks=bmvln2xBsB*K&1;9IUZ-W`Iu_$PNnf}ZYduw6}~WY
z4*z!UvXcGRJ5kN3x}1CO(kXsKIC`rS3wAFCDfLs!r?gn~@2&6ersyW(^SNHHXhNP~
zE5}OC-Ks<{`WO<b^F!VRV6G-Y{ZB?fr<x8^+>^?Z;8oFQ?D&|bUZLTqlABm?SAB7Y
z7yBMC!89dUXlw4|I_4>eG1RGHEOA((yKh#G5`=>HTHCOcFJ*tLclRcYamJ6Ym+^!*
z#VBOj*Cf)uA=Y30R99hno;Uuyz!g8PUoSMY-STZB`o=4M$O-Gt>Ndw<|F7uBJKb+~
z`74bZNi-q{75O&v`$oI@$*`+G<xcVAe%tPMxh&Z4O@mhMxAEumC{xV8#e`$cjlyfX
zN@&M_`td_1xsFXcYfVJ${RsX?4DFJ{zobmm7$R6NX$2FoJQDgx70{WDN2s&5=q+L|
ztl|FA@G|&Yyh`xNa@-)R_(=tts!~v0G;g^(vg!o?P2;+#p7LkLhdjfbVWL2LCF1wt
zVgq%fUD~FZgp?O%eg9GZUtAyV7uU#5fD$Ix+xnNqG(lTooS0NB8o=LN`{Sd7;<tlO
z|6GRSR+L<Z=QMzvR6Yq}P|9)V<?iI~*7>|C0p$w7Jy*La=;$(kdLxl`YB&%NLC19O
zZNYG&LP$yRv^gOcuft*`ch96*F|xPU-Lh}dnUDSGGBB3-^jT+(nE0rF{%A6;^A^1E
zJJ%3DEU)3_gyFMN1-o9s_3gg%4Lv4QAL4Tnu>S*Ff01Z;_B$7+fCKZd($_1SYs)1e
zTvH^kHuY`8jIa|2jIK>_&sycO$B1`y7M7#+&aY1ONnK|@G9w>iAtp|~y-9>p#@3D7
zb;jxMm|^gC0n<q0zD1ks{oP}5A@-1crglP87h#laD4S;KL8LbCMIyreqCw<M@S?z%
z2-|rgM3tD%U2w}40_5mI-38j&p1=wUM()glf4}%DVAYpRKs_eBx6W&8L;btK9_*pD
zr@y%>LWhxI_)q4*6CC`B<4X7{AUWQ4Ju~Kzk;yK1>YzCxn9jSvbKBRQdg6>cRD$+}
zFcK0Qy`nxO7nViZhYv!D`nsBw=dRpm$GrZQ=4oYitP6r+@Sbc;C?&2nvSktVo&SuN
zb5J@m<-@RKj!-j!F^jw`W6O>c*v!9=Z9CQsMQFYXW2U7rSc$NJ!qI3r64t!8A6NdR
zl}Bn}sv!;?HxW&QxugF6v&zgbFou_Pf>fD8f7X|3M6EVQG(YCgxoC{TdQoxS4(mD>
zgzT{6W>e^}ts$*4V_2^Q$=`BL;Z<0{0v71td<!fB{lT1+7^D8?{sns$&hu5<gw8L-
zZg#ZHD^XMTI6gsA^RNP2cK7d3h@(Lw5>BdsCbp}PtUYfuWEC3(IXA&FpEfGpXh=iu
zb(ZvUt=@qkmHiDIp5;YlzNnqfFSh54nK4irM-E{@-6$#8rNO-E)flTT(tyP6B_LOn
z>A*E(&iU}tlQi2HzqpvNm%>W!Z9JgxU683s{_z%362%D|$2u%tI{6bomnv7%f5xIK
z{&V$beuOza(wL$h3tfFf8cLZvY4ceE3nVg;{w@p(t}!z4uQMt)8#4hH9m5uJ@-)#R
zw$MqYfJL8vqt*a30(PlZy|(<bGY->{O6(^M#yzx9ChhR^B<j6zaS)YgH(cP)*rtFZ
z_+?hT*;t?6I503eg0Do>fm36j;C|=-QC8spE8gns<uAG_fWC*4KE-kDr;S><LKb!w
zdh`H!^7p?o=5h?^(GsS~GL+cSaRllG?EL%<4O&YvG504|zV8Mi0$t3+KYtH<)>VP6
zxF+hIKN36l%I-q1c<sia1WiM18tV{1YX?DE2j%o5<y8sX=roUbc15WBt2$yC1r8Ae
zEj#lXhqU95C~o`hmO<v8gA*wgP1X79{0qn!_Y{Fa)nmRc<gZqF<q|RaD*3O1&LNx9
zUG36tP>Ls{p#&KPdY<2|S_n*erG9M!$;gt~wq3hO8;r2wQA1h_2%SkZ+DQDb+gOvd
znl9GQx0`$}H$(n{d6r6$@q|a>N<>uL3Hq?QCTZrhxDOjYi>!uMw>KW3bisQ*yGNY$
zhj5Gf-TOK{{!xTR$>-H(qTOUH-@ks{NGCE<Of-82HomT(7r-8#(4zeCRx-pQFzhMw
zrZ#qNQFk*`SNBU&0T*uN?eq%|HNGkG>)H%8Ylif5ecQ7%<o-MFe)9s3ewNnU(pv8|
z4#R-#WxG!-v8MdIv26s9E;~qs@N>xUToj`21~Tf*3K8pr`7EqP_^h}??3y7=_Im8q
zGE@G_$H^}}oDGURGCKXX85M!=>AB|_9u_$nexcD-DMy$la{0TLBacA$4G1)m%!uH^
zlkwK%lNuQ0diWtEKd<KV%=AQ!v+j2rIxY|(YJR{6Cy%A{o+iw8gMkwYLUd%$t=oCJ
z;ATKqCRCJEqCh2(kunx8$7cMNXIqTf)%iI=R{U&>gc<6G1MNzGlG{&2B|4AW)$a1!
z8Zj|;pHl5tZE%9f#@p69RUDN+ukld$OaNE$@+&NpaQ2-omPXb&CMtX(h+AGv9HM^T
z$dB=8{d~EG=V9s8SPdftt18PPSMLtN-g-;w7#AAIcnD3$&?X$z_cDAH>FADdAEu=a
z!0oZ<m<0oi*YA4i&wjrcS5&TbJAbk4GxXmX%nOhlaCmvT%lMX6$S)l(1^L{&aG&5f
zYbXzHnpe=R_h2r%nLAhJlR(^KWL441Hz#P#DM&%*56KSW+gBvjJ4`_dAf71EOET1X
zhd-56{(iea^0^w6M}pNA_}QBC+_KvtJCdAp5yx}-xdWd~M4X1$GSInSON}hvAHYV;
zQG%d=PzWJpilGfVxuz`zuxqm3klNIeZ3h2a!ql^Z@Xlz>m>%kLq0wCfX1oQ{RCFwb
zbmbrrV6BRG2<@ppXPH=y9FXsC{%|lzSgbCh+@+$wDhs3`4g2V23BkQypweIrGkssX
zT6h^`t6NG<{_W#kFb#xWygfKE&tBR#>>esvnl}p;9Y}o>C`;(d|KTh4mng3Xfq9)7
z@<_~J*#yjV-`BC-t#nfYOM)|S8^x8@e<?w!vJo=gc2}f^CDb^8qq6t<5bgQ9vFPP+
z1<aNA#}kluSdvjM@sHyhbObJ6^NxbeAMrp!u(n95!eqaHji=MNFwM_1djhO-9p=f8
zK`pHofJ#q?4gTNiiAeiG)m~jHqiEl?M<+{g5I^N3bbn-MlhFrsbq0##Yl|?F&I+C5
z36OQ<MC;))qSFSx#N-71q+YT_0783@vGypyMg-M~Y~YlNF-J6kTZ7Qe%D5I5J<imR
zlpIJF4ibfX!3@I*%s3&Hn#TctA3N}-BuT({bcS{&0xsr&*pr5^gxWCuaw&Z>mj(a$
zCFXEHjEEsp8}s&@A~XPm9!!V<!6M|F-FC%|Mz42tj>2hfZCh5Jv5)O?c$`f}eTUI8
zU}hT9#LrUZVTQSTO$#765C!(`OHOqxD2u318HEwY@y9UdJoO%ex0KX+{9u1E*CTf=
z=FQW+DDN*i8H*z>tvM9GZ`bc69hhGJ(twmmgt=#MFt(TBm|GyDdswZ{PM_lV!7rG7
zoP~~>`XksqkEQG!zsk|iVxkDd1VV1JAM9}uj>e=|ZzunHWSJ2|M!7rpOgT>lZ%_hG
zfcDLt=$5L+ZNkjmouAQa@mXX(tj_hMSuxflIQ96V!pCycEPoAFredaXLlR-=hFh3*
zrtXPikf`@ZS|}>9PuQdlhF$zfSVGjb$Jv`KbV>*IrHHa~@$jVAe2GK@{62z9Z?|p=
zQ;kx)m`5N(A0uKh&64g}aRg{uL0}nmvdu4Mp<bzR;DeENqBv3;r7=$UkXiK%KYI&t
zFQMCd2#7|NsP~K9j(1@Ck37*26iV=KFCSx_lN>K+ylM$nyqpNw)Za@_ns54Pnnh}C
zPFVs-lRWyo;_Tic$O6!FgWqm4j~%*Va(8`7K7;yPo~|rLolzUEF`)GG>)`~It}4h_
zYT0P-E*}e)^aHERcd`Gzw_C_E_*R4w1nKR}(vgq8tV?vZi7IWOG-o<8^__3AM`gx#
z`2Ag&Pn~(u_rEh5TyqZboaS<T-P)B4I)h&8HC2T9#H184dIsGk%(ko$SS`Cz+RSb?
z;8mhqzc)7j;&S2IUlKpu3nFFAHkV4=^A^5dww@oj%o)?Dv8z$3S$|oqhI}N*Qa*;t
zXsr$EMOGmp4J9pvD5aGGwSh{SArq{$))PL@LVV$H>;j4b?&;V?_|k#S1Ph*O>uSMI
z9jn0{!xbB<!MVlB6?nn>?Iv{wyL+I4gphT!ML|(eCOk!{tQ?Pb_QH|3yT8({$Gcw_
z@}|3l>+(9rr|9g<cNKus0HeA}^;8Z{#I)w3L8$8+M7}a?;Xx|8YPq0@VNE>6??q=O
zk_+e5R9JH#75=Q(S;&n(Xvna@L@k;*=%zp0hQbrM7hT-rp5TF&%`>yNO4tvVo%*p4
z=DZ88`H@mIQLxb<0*bKQvB2>sLq>MWQ36%44UTs_=g(blfZ6cIPqPtfAAS<;1S!e~
zpcLx3LekxJEg*2kz>dotrt{?+vYBvT2ZDtEYlsOVc#mD?%rg6ZcZ+Dhuqg*)7E?9i
zCu`wUXNbeGM`%0jdniBVD)7mc7|f?2kQ3bJz@(}{B|-6nqz53x8z<%EO-~s@AR<)9
z5Yk0)h&=&uSb`0wz1nn*yEM7M>iPVGlM2;66AfiIIZW=kBxfHNG(a?rO~E;l&fcpg
zsEUM-YO$|3(YyZqs=Q*Wx>Jj7Wl4OexE-zQEXhmto_W&M=stgFR=1Z}hN4=?nt#t!
zQ<Xvr8{^EQ;}ul&Vm*2U<o6OWym9aLz_MaG`vt8H*E}avdwZ>ZQS>#9cSLOWe?zul
zF4D;w=&OkG9^Kb>qu}B{G>9pTX88Qp>C(tz9DN$BaAzD-_&_3UNRYZ#UCuzStCQc(
zB$cnL6)J;|PoGDH#hZd_$P1DFou3-#VD9!O>)e3{SwBSxl33vBGpb?3>a9^@srLHy
zX@VpBcdGXLCt}c0$Tk!_jFX_;otZOVSGHUGn_1$5!O7C^fqZE8GK}zXR-!eJ@KM`_
z0BjuHXV$zYRC(pj;!q>};{t)p6uQ2&NvL>C=>UIzM?4w}?11nay3tb#T8?;Y>=?0W
zUUh50aY9k<OO4XV;J+ihy$F$TD$A>qfkNkWr`m#xlTD0VoNV4w=tT?=!?owWt2tzK
zI>f^7V+I#9iOk1q0!D6vZ9&BL3GRJOD3bTatY}hnq4x?;V@uXSG5U~u0%T!h%$kti
z$$hkyw2Y7SbK%AIH~KywF!>}+awn8KASPE&BDi|5GjL!qTVv5K{h=xVkG%DuCu$SS
zlrU`gHfuB<`3Rc(vx<l;ScZ<BR=wE6-&VTRXMT3DYJKflvhu+qi<Re=QLODrXHmzz
zFw|0SMBz0-=x2>!cpgnC2P%2NQ9z#l!@T$KL2EG^2D0Sw6S(qmui0C*r?+O2|1p8$
zR8PTLQG!^d8`Ve!@j~v)7YJbrYiy!WS}ppqaC0WaWi7T%>(x~VMp_#`$a>(Tb64j*
z2>c5TZFh`t463oF!~DzAO~Qe@Gkn`))U+QvsFJTd3!8R&y~dg*Nr(lB)Gq?OeB6%)
z#AGyl!cI^a^GX@)@cD@&_Ftnnue;Mcg$n-qv(DkkJCBPwy;k6i)b-na1DBXbA3d$N
zf!p5~mgTAw&i%4r0lsTj`8vp*voITxGhNYt6I2q<jD+O2igq$^x+Iv#v%QU1R0|+R
zRA=W7Q<}Z0Ts6@9fP|H{bFMML2BqgI3@5!!Kz_&CFW5@9Z2JquV9Q`Q750?|V##X_
zS7&^jzt(>910@An0OY4qUzkru8v?MDqi(c6tHEYSt2H5yr`<>uAaLALb{1m&j%KP{
zAZ28x#!OBhPl<f{#4Mw|sJmHy3cc2D6|uIspU}=)9xOkSlmJ%t^_v^0MsWgEMLbm2
z$3<x|&|sz@L=s_32(nnf6-oXP`3gBkA~L5Z@*VhPa5vd=c^CvfB{&?@uqPaOFps?d
zQzkl9zSiz$d=Qp<`1&|@Fr}sJ&d0z}MJ3H?M~{R?H3A`Mu(MtlN_SMDcqO%cD~R{i
zB!*6G;y9lX%NpOTAv-inF36kI+f=`lBxsH%vbCmfI>8eS#Od4G!dEw6g%Q<YiS+Y_
z|A_5XKwn_n<HWp7&F6Qp#`k|KH0V;|kuiimTur^z-w9AD^O^KeVgFQkTV-#@WA=2*
z3BW8X;)o+Z(Eqp_g}t>RR?$Y!K;rKHs)|(V)QiiOa%bNH)8k7yCl>`d+JRc&g(fmM
zJGU(cEX*#!#0_%zWNWkKM8eQ6!iQ1E^mfj*6}>?>V%#5Fh2*6WLdiFG!MJ^^(p3S2
zUj+*&b_{u1zvlLsdUX`FdfHNk#Uf+(6B7)xXc?j7$Zn|qZLYV{K#Ao>x+8wkisR}6
z{RNVf?kO}=Ib`D+$y=(RSz-qv1gQx%P$jtCbJyt8<1?q1_hgF-)Zs7eSvlNgVvkZs
zxBJNiw(>FRe6qx#1}*%?Y1=W4cbPnSouJZ==6({mUfjh2u?gExDWy2)urSQtrq3IO
zkr%9wYN;7+UJ1V1y$uS<mQCxL@Q}Cry-g42b~$HY-Fx#S3=PJI@n};&&=!@&n7K$C
zVdy-ggL-U_W7~~(Q)*{1i;FKh`R&(I34b_cAN%4;V9YV!gF<$~lnm0fY*!+{`GX=^
z>ym+L;LXDHr4P!~#N2OR8Qd5+Vj<Zaz#Bp4Aa;b*+`|COx=I;*8;bH-eiLm9sw~nd
zvb#FDFoi7bG=Nk3)!#yj7&RUj*_<MQZL|y;54I)Z%(F$I^7z$jxZHApXnds%PTCws
zYQ<|Ni6kNjmm$`eC<CrI4BKxXW_Kh4LpVYSzf$xZyq=zVOH4#zBethJrcBsXC`xp5
zr+;{X^XB{6ezBXnq%BUowbQG*OjU+E9`Vu+)|1CoiGy&9Y&2>A=ydi|NfRSqA>op!
zNf>{ITi+%<0Vg@>P3*3dA}|G&@Xjd1o)dG!_sNCA3tP3s(%<jHwomOrzVHxj+<pVc
zY$15SJ@O}A7IaXa;G2TwwZh(y`?qX4BYAL(u}hLMJ%#2_$(&T$0ZEKyjX(9#7ya)n
z3FLM4s?eo99M&_YstEo>Z(Se!r8fH_7?16iZ~%1%n4*BcQQF(W&_U95=VAaA?p4uC
z910mcJGKh*e_l<Cl^Vr;#T6YZ2><&0uu|AFVl3ib4x@MEy`cvyQOMlm>f)w5X7j%G
zlu-P-4Xcqkit?lMY-)UcU1bh2(+698W3lSf3<WSluuH2ejqd(1GTlP7g#~T=d~Y&M
ztCryn>NVpR)%5SJBSr~MmC}Nf;~1A`;Ov$#51&s!ypG0Gh5r7cczY-ehW*>;q->U1
zd69}K+178aE4wf<oeqtq_Y$EIp^Q<n1a0!eqfO0VX~ngbSg&E^7!TylFBLu94I)QF
zJf=CSAp|eJpzch;_-6?RGtzN!sb!<Z!65aHX~x`>^<#;|mRUu%<|(M&2I9R|qZI<d
z5-t1Ab`dGagU0er+;#GKxSv#}&;$M;DE}GymJsWkZ>JhR9e)EhLBWZ~?bu25ArHRB
zs1hii|FUKSjl}JbOO(%0a9=*kHYkO3NWO_(m_hqk!TX0UxfVMcOgizc@z`0fXZGMI
z9u@>M--c`Lo!uHPMMx5D8-kAV=IlFf+~5nn3M{X5MgnQgG+NXq6;HYL`G^ma8PNr^
zZCQ5Xp?yk9Pwp6pM6G6ml%SN@imfWIDTU(S>2H0){nwGCF+~l`R=cJ^zqxj19H0>$
zL-0903;p7JIDGg(jSu!+uL%O!z@jZq6ra?f1@4wCW_<C3RWa<B2N#u+&>OO@H;{sV
zwZH1+dL9H!G=c1L^D;=*%U@f+;|X0C?JT)-qanw`BLJJV@t;|KS9Vqh)%G*(29C_x
z;nQDhU_me0JXvY*T{pE*m7kIi**7t;74gr&55Evpc#R$3545wAmrO=v(_45OFrn8d
z>s!eF!)tY=Fec_WYk^~Rtp@hg#Sc8f5L`GCP{9DsM#pG+vQ)Z@phiyZbN4i7OigVw
zxn=5;xh@6Cl(AJtGs?H*t}{&WE{f}PBjlR5<kzg40Me-pxI1WQ&#iycPtzdI+gB^=
zdRC)iSXl+UZDEiYw?1?Hull7N8LIC-<Bzu?cpPqDZC3(M#prua;t#?HB9Pyp1amob
zg--R`4KnM%i=XwKFjd#_OsRS$#b7vvDb#Bq2=r)u$cSDK;TX3ZS#$FDPJhkEHn9;J
zqLX*w4X(jv6_rvNnPM*%%D7K($_S0Lyg3H<6F>VMB|K4BJXO42M@T22`fS1I8sIjf
z^58sK*oFhDe}ktvEO=#-fV}Epog>LHMISxeS?E5Ge{h<Tr*RN>ykn8UFW<s7hyfZQ
z7y%-v?dpc%%<H_9_s>6S+5M!GmdPLk<K*%BH_h!2i7i!jmo27jQ+NlUtI&$f9XNIO
zUGyB~M)_ZkC?5D?Jfa9w{6XTo_ax$}j2k|rJ00UhHMIrZ@ybQjC;OdFd1OV|O2_dX
z-2Co3S}g+t5{4`=cL6!<-Axi!Kjv8(k_JNr?9w_CmerLn0Sx{iPL9^gKkRYwBb=SH
z@8|DUMoL0>dOxdv%{)}tnVG{Oq@?PJsJtNnlN&xlll`V#esX&OrV`-H>uH4iqUdJs
zfAkI#YXY|kNLIR$6|+T^VEB-9&z{dg_6AS0sRy;Kt7dvxD*;JxDCcK<$E41_VVFrh
zl&lDl8u6=8H?%j%#IEk8GD+Y01iwGw4t#9m8%+U7vL|+84d&SV!n?OXVHMtW_E{c$
z2P`x?ES{Z#@y?{TRBywv2#DY*MIj{=PKGqvi@7K->V375jQ!>`>6_;2eqo+hL{7T}
zLlyhmk<`2)aIJe=2~(HL`yl%VW#5&7nch(U0VsR#jdK^W-O%%N=sWx9Vv8&lq0OyS
z#BtR1i%<@d2YWQwOtEX!t^C-p=5gBU%TUq#+YBYIq*W5L5IUC8HRwi1zu%TlO?<Eb
z13&ocqK0={<;dKLIT88gW!GxYNUpA#`?ahtx)pe{3fZvgC4FezWw~7Z!67t7w*q3}
z@X;EGhDR!)Ch1N9I7u{D9Knq8=CC`gPPZ$x1#k|;Z6$b8{<%^<CXtMKuMk~h+1mTG
z=3@PDgD|R%KpaJPA+T<0QZRmxERv1;$jI<oGeo^OzfZiT+8<O5J7gPKvckU4nulkZ
z7rcYEG=lOx!9C7{hQIxy4lVB#S68@I^ON)VWpi5J<NHzP_a(#Rek6Vcr2}+J4*C|R
zG!N(m>2b(h?ElqL#VF;i&Ko<<;QiNsSvKot#P1}c{Cd#GjfjK}5=q|8bDJS}U<+o&
zhutHTdJa#hp8OFF^07`Cymv4@+ehaMb*eS`wx5YO9fZ<F!VTo}-98tckt8yVXLxO$
z`r}OoJ-iL>Cp7U!#_dY+?*(Pvj~i|7frR^J{F$HJ4_FXM&D<LUp2Lzp1cH3*)`}(G
z$?Y$Axo#*F)$^I04nN0tunX8m-K>8c5NA-*ML<S*E89{8o<Z`8TwtXci@QtokP8fv
zIKKf&BB)m}zQGW*8sf+sO#|ctN~Bef0+<mWFMiQ8+T~fpDl^T@WKHUlYayJ#@?^iU
z^LJ7kkyc1z%CE*Y4iU=uM|RKdkPS$SYqq6*<yVg{e}8L>%s5y33`kaaiVM`8$bR~5
z?+jHj;-kdf(0t8LV3I8=u9O`SY~9?15YT^-KDy{k_bVh|VO2_ZU6Ekpah@mFNpW3z
z7i3|b0za6Rr$zk;adiGU&Hp~K!?KZv7>9IlM^7o?i!kIiD*;c%a_!CBj0br1yD3K>
z#)vPjgxuV=s=ME8?j{f;r1)v(EYa}#Tr^5NFy2IAK`#G%dB_~|os_H?ZCPh8d5K~&
zRASBEswm|0Cx@w!q#_i1iF(-cH`+Yt8QvS}npITi`&ATBAnGh5O<Z}g^d`nzM$u?8
z{xrd;{n9uZCZO{Z7I-kJqjw8gaxQ!(dCp|K6||s;l-wYh@Z#B1=*SV})}0>)dnw>I
zFyRRK^^-^oG6zYpZy4A!J1%TS<lfL1Jd{V#I$xJ@c+b4lUcZ5em!ch#V)8%NW}z<-
z{HeVcsdW29{?LZ`K|(zTwpU|5E3r1ZT$S8!_(bLO@<=@otJoqZNH}q+VS%(OK7FiR
zi%B0dck*lafO286R_|?~=b7QIkn96XC2CW+WA23*zaI0NRe6E6Tra$FinBeEe7E|w
zwz!s2bR~$S0B?=Hz`1or%L^k?Hk69e5fBH5kB;?Tvb!hYY}7>6TUD13=!Y5JRVEKq
z5pdW~r#pevX*<WC*ke2FqV~M_wwWvL8bT|RmN9p`Z6v;Ui=qPo+i@v7YgF+oOsMV<
zgJS@aVxKd`@ZJR+;^ffes!srb2y|Js+pCQ%kLZRD_pTPAaVON~l6JNkOk3zv2;&ic
zabpE_wsz0y2Klo(i<p4jrH-_xm6*(re>;`Dh9d$rKVvREg4s3&Z^R+QeJWeioe%2j
zylMfLf#jfN!g`?)yCp<g$<5s`%UktD$feDs0HxvwtWgaXIKnAbLww|;i|4g4ru3%H
zA!UUO1_Xidv;Fm^Td24qR4kybgf2NQ&p!aFw?CY8frtjK{DvYLPLubRgjjObcnd~h
z?T%ZHChH341?*b@Fv#3I)nfZ06+B@Sh^|AQ<;u)|mhWcPs_E&jl&Vu<QQ_pk9oGqY
z>23x_vlh9AlGD_D{ilY;^POKIaxUdzJ$~l_glO<N1Zh6>UK?dua0hpa$T}v`FkhR(
z8<e69Z<(*Pn{6dTrumHQ-)*+G^wJ(9q<AsV7Zgc<6fv-ftrTq4->gWXLlKsNJ8bUQ
zg6n#_VS6cAPxfoIHVTSqQoY4`m7G7q^Aw_m$g>cJ^l>>+cAp~`z(!N|RE(+{BPVtJ
zBvv@vK*`kke*Ox$%K5j*R<ZE7fdh2ppKl#>_Y(A;bH|Rnu86cmCF<TmK6t_f6Vl>y
z_(k8C@#s5YPb7c}^J56JsQf2_c=3)~%#+xd0(69kH0#}D-^oS6J+MU;^!b5Pym2Ac
zhqKE&MTCP3bC*8rD<;i+<PqMm%)9+pKk=CC)tUT3(KaCI+n@P};kOCb=kIuqJ@wZa
zYU=US?gPg4snj~Kk(SQo@;#SR$^{P1r<_|)PPKp}*}!YANPe!6<PO-HwulI88ZzW6
z6KTUjb2wK?s(<hgxPvpEq-bygk;$;CZQ%F$JFLf4%#R8xxo!eK?6wHRX$ow_-7c&G
z(FlFQ@KJY8Z+{qdgVg=lk=Y}PQYI*g4<e^MBllL3C^l9#a%O)?3ryzNJV3Gy@xspJ
ztCb*hxF8t25Qzs(_a7CzM=knLa-;MYl0dpBk*|bjUJSm+q1ztC$EqBbeU(Y%U(}n>
zef+dt;<LQImn`yBoIV^H5Gy+|em=;%xloo{u5Wzx0CaCkn_LpVHF9E8@JuTci#%{u
zHh0l1K(k$t(}sPr$hc7Y-Dk{9DQqx1pYLz&G)NOsPU!e%B4=&I7Dbe#5k}qW0iN0X
z*OsA<G8D<hZKJsBwlts3wHg2FiTK)BP-C0cD+di?>*Cpi^&K3_?5!O4oRQn}58Sj+
zg|Jz6vf@$2_1ZT8S8ZouL%4jT4{WC2UPnEl3u$dcA-DH<$EL9!DyX-K7Wjbg7H!wC
z&(6VX+UI?QHqY(v(gCj?8Fc!-f#iGnuvjVcGAEo7&Dh{@kQK(BJ(Yz2#g60`#>&5o
z*q>W{$N7V?*_MWv{<t>t0UK5$f}{$S<(_F%plIII{D`9OD?fJ6M5{kGvgJyo+gdY{
zk5arB@8bM^5gM3sahIoPY|e$mT<4|SJq0p1W|A4b@{yeGXgERgeX29Rs$D0fE<5QL
z7>hRFgl$JR|F_Ev`Xaatm%>X0wP0`WBbK4b*@;>kCE!ksNCcA^$ew?p+cz>K)xLF~
z4<@<@pX?lmK}vQDU$+CqW+Ds?Bjetu`pqCR4M^k9dw}<HbY-8*MY1mR`lhyn@x-VI
zEG>m60bIfzjWF(+je}g$qciOjA$$XGZ|A~4n<v2mRbXz5_*5a90|U3MRm@5I68|a*
zEd`<Dky-*1IU<b)AkVsy6t4t@Ymj1ho;*-Qr-gH(h!H=tU=L;#@+*y1Z^oGP@DWjt
z&L6kWN5x=q_*4mSbsr5A8brUxNtDt~chsn=jXcZE_N;o49T3<*bqv3BofSk-mq0O3
z<>aFxBNCfEDF(ElhBxZ9B$~l!<C{oiS<qgzj65G3eJc2puQ?IV;*YG($UuR+beO59
zdVB2=yBoaln8cE*Wv8P2BoiYZ4g11Kg#c~2jBxUpluEq6{YVXu$xS+XI|MusqdmXt
zz*AA+`Z8+w0VhKIu3cy`@ZRMTo~I});St;;KW^)o>!(dK5fPZ?1hIPm8ii(PtWZu0
zM^Nh^5F3PR3t9Xh^4<fksbyOnMp02wQBjeuqH=5?BE3XKK?OuqdQ(wp0@4W(wupj?
zfC`96iHZo)A_CHhfYKrK&_fRpNPrO1_k8i(_s%Wv-FweH@4o-{{eR#7Z8kHrXC|}P
zS~F|btTk)yr?v=o=vLp~7x`q^<lOAGqmFc^HB0DsGK>HY?o^t}`2hQzSKH<NPb83^
zwpOXQiXPwhKBa2DTk1~Ov&vEH<87hZGB;Lyc#P)edL=KuUHf-md(r57Q(<K^O7-({
z`OCc3sZV7BLw4D-#!vF}R7c*nMz`mD-26PjPIhMD#JSsTtP1xM0-6cq>Ta(dJ~nhh
zV!znKaHj+Lf;-?%hMtoteV1kohqf(^u8f>cy|i9Vx=2E8|HQVlH#E;J?fF!_AX=FM
zJaU@b(r@YPIquFaJ1afSdGTX9%5hTtd3O1fmk0A`pP72Nh3_Mh%6ZhCr;g2+uVb?E
z%|033%&2NST5!=nZzr;_)5N-SC6J(vtZ}9VTW=r3Q=z*(8mz+HZNoN*GuU8T{-sL~
z941S~s&J#hO7e^|_wVOzb-f?ulW-ukYQS*^-NjbujML<*l}rlrVq@WvX$H|x@RV-n
z-XueT$D_LrBOUBik5&g&@I7u)?+GKjn$^6Wvfrdg)YY5x3F+$(PB<aEv&KhPla2H%
zw47`XiP>YeulS0{Gm_Cv=kQuIa-u5)^%agLdA}t(a@ft%+Ua3hd&ML7*?X(@;TPpC
z^Ad~tyNV%CpZ@2mdfw*YIu1Z|mab$eopE#bE`7_6o1nDprj@NJf9I~y@5GILA7WQB
z51m`qI<i;4HTJ&sCmp5Y<E5>8XZ=ls52i04jSa2<q<v!V3l+3sT;!UZZUNi(@{&a@
zF8sH2D6UM>nWUPV&rj~zR4w9ZEWFQulYpk)HI$9i^4x^nszIMqr@nn)u)o(EqC~um
z(?@93T$71MH+lx9nKT&hqd~np);xD;Ua>`c$JwWQ6LZYA%MO-pA3zzmMk6b-&>~6u
z&a4*k4-F#6zD#6`^eN``F%{0swS4V50qym;+25RvpgdT_B&|;~?A?oi!uZJ;4Ax`m
zf!f56Wnt|?&0}p4?ygxj&W2)Gg^HujyGAKJzqBRV!H9BlYZb{d(DiZH(4z(SZ*!5G
z`DJu!!aEGN*}T}a`a$c=qje8XPzL6{I6^1J?J&@3y{zn0ZXahjTRyuTo)f#UDdh1*
z&4UE=ydb%A`r`U#m#FJhSpDJaxOblva2aLOXo5`O*&}DRY9{O*(_^OF7Pr9RVTkvm
zxS4s600cYq@*;^OUtF!H&1g80jFh%^+BJ<)u(dm<(1yFaa{6X3eCD*<Io#%G-G_tb
znc+3ZqqIZ%wPVgJ6!*#Io!_rdwF;ivbQkkOSZQJ|({0UYHR`bQt~I7j_(e*HfSUZN
z*P+^14CMABT(ud{xl5@c3-QwPOZ(5t#s~#R%LzSPJfNC~)Rnfs{)~zW+^Mr(<H)Jw
zin`vIPt6fq1sv!PV&m6+S*5YDtb2c((V-O**C?V%T>TDZuzsmipmg)YpzeL^EBEa~
zo^E>~5`H&s=<HLMNty2xPayfyb%BKvX}GYji!7Bb`+q#w5FGtc5wMNQC<~b!7F)H{
z*d%z=*7IRR&AolWD6W#utDt)gP5R=e%euuMFp)J4<==?=R02E>Y!AG8u_#dPJ706!
z;G>p~-n!n{OBc}Oc^w_`ioI^8LIG{>8f#u2xvfxp=a=xzgsLCg^`0eds1A8BOy$|+
z92=4^EE!PzL|$<WIaGf^i`tR+6)l1@k-YhGMe14Z<wp(vFJawK#LET4g`sW@i<Yk;
z*PLzR0?gJ_Qjigz6}z<$hObu|+!pZSVda&FZXVpnBD}@P_aBW0w$&5~8tzCz65~+u
z;=Y&VQ+#!|M_3M=Tx%&atLJg<DyyWXsz+U(YC~AKt{HeoW)P{E`~7a@o%*d?uA$X*
zsd&2Ho&i$4QT=+Pfbug|C5Z8fB39S5ojFhe2eqD>@*&>|ej?nWh-*sdJr#rM9WZaW
zn&judzhHc2-^)kYDO6)rY1`HE*1fy0<!>OLjya!xxpQwdQ7-S?r`x_y?5tZ$KLZw5
z<%p}(y6JD>$d@NQe2-T@aek%=XE<((`t-H8v&850ge?26Gup5C-sjfQSFR{!&1AIv
zrK-2jb%Ljx*W)l3Tl$jG8rRQ%?q4GkqN1nWFjmo27ly$hQYJo8kGR{4A6Xh^&L3;D
zi1{cqqdlHzdLDt@;WM7z=}5iu4B^N4jx)Dv`=~!}xnH*BU4hx4?w5_{Hi+0)ys#=R
zV$NR=i;=u+mDCo!!9(<cHBY&2v;K-8cFTi^oQJq00hUjUwUe-l)Xw($ZFM7klg?5p
z#!OIJ^W@_%6V3KBFShG8XTM3E?mx56f^dd2_w?A#@5lvRUr*zbPEm`AJo~pLg<VV%
z5z)`xG(x+qGviI+sJ`!_q&^I?;WTUT*_CYa@`cPzx6Xr-8o}l623GTt)tCEnTHL1(
z<ttc)Y+D*UFQ6UYICe)u+}-AU_(HwG<8JmTd$l?c!t|S#*yQbP7M3J){BXlDNz1eT
zsC90wD-s$<ZQGG`fmbMOx};ma&78yr(UHSt>CU#VxMwxqF73QYDc*UUMVr~EA{eTy
zxcceA&nmG$4x^0yc`K_^YvR*5pV97BiOCd3!`CZO!?w4)Z!0$P-5Blvr7N;A5gy-y
zcujZ{yPZ5*+eSA7m17B?XRPaG0s^*9?QYUOux``inQKQwMC(6Z;hCN=LZJ$-i$ym5
zp0(-z%O)e4&!>G(kC@v#Py>aw3MQ_V^C^Fu($);qO_b4UW)IJP?)o}56Mr;F3t{I^
zqTfU%UE;68oNCnAnOqt4>M}tj?&$&4p^4L@Bqk!@%HE_?Um{L1wqJf(1b5aVC=1@F
zwu$6WFEWq{mjhiTL2=F1=GeNq+ZJ0zEO*+b6a8#)3WtL>r1v~k<1(@hWCw3;4Bgv$
zSyseR-%;iGO-3a*UY)pe;O-q!*_%UR7L4K?WVnm}*|dDT>#HJZ1y_I0_+fGKS`gGg
zOZR!%Et{Z<J+pA?m&a2nlkdJquD_W&eeq(JeejRt$!R#G%9MZ*je-HuN#j4F0|aFX
zlGP#tb6wUso}R*E<17wZh^kxyohHg0GV|*0Q?R}6WL3Fn=y!vqD*-6CQ-^mSyk%>I
zz}}2Ui|bY-oMe`}oPv2vvqr&7F=)<&EP~nae?idV6%a7=%M^RPMni#*PeA!U%|!Wg
zFzf%PCZa#f^UOq(kK84l-w`G#$ZyEMIrVMZo~GlDx33+#a^dY}(~X7+RSzEI+;aA*
zI=1Oq_4~DE6%p^xDVSg0ZSL=@8Qju)Y3$^sjuj5tJC>o8qCrSX@bHOgvxDH%0H&(^
zz<6+i>Ec6!gIh;daj5HHBtIw*frgW25!xb)KKw9JfJ5EFAWAOaG)90yJEfD$)#6=B
zZcQ$Hfan4GDjdEc?C1&$1h?<@u-R&OAQBVH{uz~M>gdE2J(!+8acLEdnTBQ>T*_~k
zO0J!#0`XDyk5}v4UTtCW{vE<^JfHGzC4Lzd<Z2srJB|1%*GS{Fa?gj`)qutkl$nUj
zjW#);eeC7XnP(eJd-S$2jo2@ID|+ymybMa*EStTHqD1GH=~BnKwYTddMi7a5%cO2p
z+E$Toyz+I!OC@%ToOTYbQT;X8xB%IJep;WV5zWhID9D(I{Sus!2;7gub_2kH4<42N
z(+xnp^a4(tLEQWoF!_dXA3>}9-Q+~v6qLWwv0}kV3*Al{#9C@q%7J#O4Vs18=7+oK
zf{0&1NNj2Fu~|Z)@8l<Br#Gs5J1U~ve7XBr_w`5b`hP9WI9egQNZB=dPNamOAaS(_
zq)=9h?b>w^^Wsj{{n&BUC5`RX$D6qWUz5A!cDLiwwJX;7F3l$HFi>~MifE)S*fCJ%
zaY9-Xt=;zV%jgoRH|r`cl`bnQ&2aPdjx1_V9oxaZuCRS{KZd%^_4}gE*{bLds3r3W
zG^%Of(pi}Kdh)%hS;41(OHw`ZPv6_!YM7VaakP`Z7~Fs5dbf4zpbESxDxOf}kLez-
zXSw)fxjb(6I6#jG_Mm4=mlQyqCl7g-c9d;;F^(yL6nb#3Gd7i#y`GzT4M(dser?Dd
zbZrki$KDuiJh~4X<~w0d2KNSt^!n(m)|k?3I44u^k%<k+@UxzPCF6u_I<Pl(caeP0
z2FNny?FV<hiBW(}o`D@(3SM7YX$H>0y39)lC{M2*L0)ri;=<51Z50cvk5f4xrEV8(
zCJ~yUJo13n3gR-e?Eu$LUGju_(T@JGIsOy6>zEZcqi}U=w++;xN{m>lw!$pctf)T0
z`TX`T8L6BjCCSNl4Fl_7@gA%euz*FVRcUx=areEzK>g@yq(pA}UWbzht(v*xKVDDC
zN1vp-Zj`P&`C9TdTWBKv;sG=LafUJMQadfgbT<bvT*@184qMEi5>)r65@QDDJFY#{
z+W3i>UB3+%kl*rbc=$ps<Mv^8%{S?}Lxv4bIj&>ebBVjC&>&el0q6%iuaM}^1Glh$
z)<pyp*L}Fq4KUvKruORlC-N!$+|6rA6Ws}JP9x>*za(`8<-hM9cr#e2Pe)LkRHa{z
ziU&63<e~*t410jnp?<N7FL3k<q4SIO7`Vsv>&eO8>zfOx!nrFPLZRJlRk;T3<4JcN
zl?gAkcEUq`Gth`pgH3ZiQdFOw+Wk0yDkU1t=7v7g!U2uqSjfKk<+i}Db(9H%^W_JT
z2=A}A`YWdeZkGEAba@5dx@365CWb{v<q@1Jv<Y!s2xNI40y2JbGGu<6Z69|F8fXM>
zc2un<FbBrH09QIl?jQB7f3YW;a?-KJXwEw4C2?xgo(%A8Y1@9R8QG*Vi@%_5296<o
zz;IT|yT4;qRm{nQVSwuO1)QWG;|iYOX`<NCt0UZE<!27Lhf=));B;n66fhkU>C2$%
z%&IiZAJtsCIB<GU#O*03`irYV?wS<aU7zZ@Gs?_TJ)Q{7XM>k&r(D_ZrT5ppoWGm<
z_!rBMs59z^-Gz5@4~v6{!0&jMx>qTlxDJjC+xE-B7nD)E)Oi~8!o?{u-R&?^G%hH<
z$d4Hhi!sc4Hd`bgFTCbt57RU4qW0S~uJjD@y2%ejTpg6fLOEM7>!HZ}Z192Fd|KHp
zJ@*UTkqP_*>%1PQPi1+ndR`C*0;xvH{7amxJ&`kFr-phOh4DwJuEr~03R1dY_;RL>
zH3FBFjW}IRXRy)mrYp7}bSOv=EHbY69$V?wj#wS(K8l@U3YQR`QAVYMcKl1@;lF@0
zCBQGNUAc;TB43S<kH6?Q4w?>||C=~yZ<ELD@g4VX1bA*3zPv7Lu3TTx?VHA(-mCWo
z*B=oXS@-2F-^I4V(<cHXCdER(y#|TxgKIeILJg(HKb==f-!8ON*u808Xw~)O+Kz)a
z%Z4&B82Q4+FASwJ3d^2lZ<OAt7wOlG+riC`Q1C^!9LP1JSCvgTWbUaItyRvFdhHZU
z_OK;+2XS$jsM~Jil1pCEUeP4RRPO*rit-#Cn80wW^s`w*sVjHxtj(e%aI||~$D+Sp
zDTz;fZ+n?*7<5i6sK*rPmuzh^?=^d3fR#8n5$BvU_#q2Kv%Ut-vt^N7`NEX|%eo<4
zOR2d<#pSH(*|;XZfN6;Y(j+n%&VHJVA!17~-Z@PUk22W~o>6{i851seBM_W4|H;He
zs&+)PsA6iZi`%<kKN6Wx*w58=ha(g$Y4Ir~(LdC1>cRC97I+zAbjd4JbDQ)T%7;vB
zCzQt`sn&hp+VV)d(uJ?^YrsvEy^Mh+NVD$8xw8UZH^m33Qhj`K42x6hi4ldg+}l$~
z+wLKy(m~T7OwMeOP>I~9DeRM`c?*bp0=L70c$0>&v*TtCm1^*|0~UFo%A{i@%X%r*
z<)7IX)Di07hnk~j7FGSmJo*Y4;Yw>#m0I<hK@-b;tb(ic{4PMug$#)?sXewJjrLs)
z@RV|G4x=gdjZ$+j*wCR%PC{N^0(+uba7aH+#X&sJd0zICOu64D1T7twz5?CQ%!I>h
zLiRo@AqxBz;YkrQRuT<vTc{|r$toYZh$VMzxjQOdgzm0*pSoz+M?|LHiJuy`b!92-
zInq7X?{y=Jy>tcaCE6fPvWPb}f9;txutbZS+!;=Cy@<;GG)jB@<!G|YBbPQ>Fx3jZ
z%Q*vUH-1k`o2+6hG-wK%clf9tv<?jmY~#b-z(HlZZboL83>IUf-vGlo^=ed~z`^k@
zr7Pfulse@mc9@Pw^pYgGep{tn4Y&!QNhuRf9R@w{asQ$^l04?vuwOZd@$i(+0n8>a
z2ja@1XY=4YWs+?Sr!nkv=!*cX6|;h1xX8eM7DoVoXk(KQT`0wMy5pUx+isqhSKB{#
zVPf%LLGlkqc$46sfft#lw(?!}?B81d1MzYcr+WU|?(^Tb-kBVgi@rfwyJH9L)9hi9
z7YXE#7H9bQMpE}**!OVjne}_Mf62v@vQ^QzQE&A}6C@W_X%QvUE7OZ;3q`1&*%C-J
zwvjI-&-Wg|&pE2iCDb_%sbmcpd-f_uvzVQ^7O3H}E$BBjw#Y8JP1%%2YfG-$4vUIA
zrsaN8`Gf=ENJR0L#EIkex7*<|WD&Zvyc}f~rkRtQ@aSfi<}zeF^fa4V8Mm{EO<=o~
z!76z9Gp~-~#`Cz*lr%HqNukMk^Mv%4<_}JRcac&}EJAMloxS2KBy>Jke8{U59nRF2
ziFP(Jl<a&SFW%BB&5fQ<^UH45>F-%e2R=6ASd=r+Kvhr<1Q<oPy3lv4r8Bv{R6E>x
zt4_8%g*KQ9@+y|^U=;^a-(c$PgF&6wU}E<ZSf!)@g$jY`E?v1A<H-CB-(=BTI0`pU
zv3>faM2ZHTlhXH^hNGPVG_(c)0^niP(sx^L5Us2Lm?cwvYC(P}{Y?v^TLY(Z8|;0D
zZqkRf(pf3H%Og00l=1OAm_AEwW;0gh)xGibPuAqwAj(X<1?c3Omzr|%O|!)q=3|%n
zoqF{*E=c|B^ziwe`@u_khC|Ao_B=`p{|idH|F2i}UrX>Pt+%i6{ZC5s3fTKR0@`1L
z_IQ3@;rZje$KSUMYZm<Q&?Rf7YuBdDXU;xS@!8DxEmvZTz}}yvmX}&=EoZkgz4Mc{
zR5Wdr=%7X+1-3Z6!376YqI0X#iL6$!vs7&5N)E~_G>+r<vPf*%LqkA^Ob8nObXwDm
zgqWyI_W|CU8-u*{Zk>x;urjf^)k3G18l+Hm**}w^Srj!3iFZwetS`hyV^MIHQpQtt
zmtkBJnZO)oO9#A+M@4oxFEKfI=gT|$XcgIVnQ0{t>z|g2bscQ_vS9m~G_lDEf#}z}
zU?1zF9h75}?mh=94lZg*w>Vg4-HEDEI@vou)N4p?q06AfqwEHQG4n=@<jpSka1NEj
zx}<$c+lm4<;j-fLU8oAmw(KSa3$J$wedbwWaS!lyjck2W4@Y-pYQzs0gF4c_uJL#u
z$wc<@7MlU_cFq9%(4tf@Z&|5d3z<y6qTQ}l&;fG8UAXRcF1QEe=Dv(|f^^V7oJ@BM
zesz>QBwJg2c6Px4{zQbZb*w^ctWhHzO-H0a?Bp(DTesLzO#poT>H<{6WPJhRbJ$1w
z*wDeN<PbA5U}|BP{Z3E7(Sv)Vml38l@Fs&mxgh2yPV~5jr}QAUET<}%jnGi^H4oL|
zErluZ#s)iSS28-K4r|W;UrFMhr7$93K9bMw(y=`vjy#e?{zj4+JGTC_vcKm&k0kL!
zFL=9W`K||+ZlC_4@@BL}-SL~4<HPN$r{1rsvb)?xN$e91k9j_H;E9<DA0H>S<&Mm+
zV_)zuzGXHhuO^+}C*XX`a+D#clbQ>9f*f4E%!-gy(U!Ezl?_z0qP3pPZxUVeC64q0
zx#%fs8IgKuaiw(9i9L4`a!XpVv7K>`^Xs|2Sh|^rq?9`}D^4srZt>V}k=F|Q;fYCv
z8XtBTEMgINlAgv~w0v=Zrg|Z6=aD(EI?o)`l)c$Mq}XxTopL1e=nST)h8s${V7gO~
z=rK)u+d8Dto}H%|IW^Rzgy~oqHkh*TrPHY!aCfl;TT<;t52xlAtdz0kK@%FbFY7(7
zg0>Td_0yWITH;D)(-?j2oT!RtEHAH=_Sbc=UrA)^jhoGR#II=S415*~k=@?)X4WPe
zkAI1LHlJT%DK+)t7P3O6TdT3dH_Bole?|$}=(W{H+ad6hYUmh2%4`^u&(gk(v0x$m
zDIa@QEb-tN5qyd^#HLoL4|?gRI0Ux#_6;XPEeZGB0ih61<**%?-ZqV+NRc5$Ut75p
zo*Ogp&h8!IMiicU6t{BU9|+N<>)gke`b+#_f(53Jg?-6*rAbD)qq)+fR<kR|kki1O
zm{(gmPiJ@Q)sGKR83n~@Zoh{v{yNXX8K8PtNh`qBM2AO&A%7;qD?Fn7pI<+Tu)?+a
z)b9H?DBsuH?z??hoh~Zlxl{Q(W$iClb#uQw(qmNHt$F?S^_npaYSQvX=Kf#EYG}i=
z2ghlXYTCmDWi*1e%-DL)I4Q_M#V$Me(!du{yI`Gh#4(^WkhX}90?)m`ak!IeXSJn)
zi5LVb#95`XE&^K`f;mho5r+l@PFZdlGUCU+DJpVDz9=YQiGUhZ8-brgsRLzN-#{e?
z@0zlfbJcMNXc7H}c(fvC*d3^HB-PEmNPr>R{sij2i%FdQ8h9Vto=l_kt2cK0SQKTE
zlxLSl?*2k@R3VUB%r=8^bqYbrc-~;3bP&B(fIa3o%thOWlEHlb+ovCk<fDj8hu*hD
zEF1c=IT#Sg0yyx*Cm;B6@*e|K9jJ_$^i;dZiTNe53XS;BrvI}*9fOo|v2W9VeCR*R
z%>xFa){cIy@y-GdEWac7&pZlnY5EoA(BGMx2l41dyjI*7(Lcfd_Y{MkVacCjTSj=8
z>SnLWt~Btgz5XnOC(-?RI{1-s>VHm(zpjZ6YM54^;yY!$5*zm+zaV9&xx-ptj}5yl
z)}*DSWnC9c-PZgtX#Z$T9kIe|DnsJ!g8GH~9%JkQ7Y>W_n1&B1*F^m2cHSwS*K&t?
zPY3L9eTNtzP;OQ5w%)ZN(y%a#WRy6K`|x`M;QXKJNUVdnG(1|l9ev?X0gF4=^6C}2
zRx=o0Ra9k`@ZScSHgm7CNW8ZNPhf*(%cFQGuYDIQAIZVnL0l(cZo)DixN)-fO<C;9
zKN@{bX(1o3kejsI$;45#NYA$Fb1|7st>8phGRpQ^6Arv!&!8=bA-3q?%GWQ<N~w2)
zkU^;{SlL=V?2%RyO@(|vK;u`!hSl~ZBj8ri9Bjm8H*^+q@G<>9B+B6wNos(s)^gXD
z$z0b!SV&U1aRXe2rWp-I0E(C75<x2+{uH?A2TS9RvFW+nwxj?X(Cx~^gLHOuB~Q-`
z=G=%}?7MN`p#x%hK*aFONCw*w=P*A!xgz2`ApaKt{RXLj7<ghEG0Bow?mq)5M}wc$
zy1qxmz^qhQ7&rtjjmr)ifolWc4Sg<g+>WwXP-GMfr_zYPhuo*)Fpa2if;lOu(7KQP
zMP8t`Qfq0SDN9HhkM7UMsGvzi(FH#k=K^yaR_4UC5Yy0=4<Jd4t&7p2Ar29-us%z=
z%mGaVO(*AR2&@C4iv-Ao&v*d*`skpw$N{ew4j<xS-45zM3mptN_KJk<brj_G{h#@}
z9>o8pH)iAe(DzsQ_}1P0ZP-t@`gaZc$yzUyRCkB5RdYQ@u3tJHqA2@NT-E24+S01I
z!<k9$wNf`iwbzV>iIWdaZM;kORyp>;XiRwhcbu@M$yNUK@64dLMTkOoclTp_5-}g0
ze(nC-FBeTV>9cPd_moiA=3k9}KkS3#+O?Y!cTQ}(GuB6rn4ObAm`xf2ukKy)bk=18
zkjq@*1P_<wY|NreM<L1W-6daga=aS9Tw6I^tK@?P0k$#|Ier9K?VJg{6`MA$&o<UQ
zw`V@CbLMzks|MFKvgL~gy(u+|--CsXEj;1=QLe~)FtDtc_}XV87*p!K95l<R&u7n(
zy>cX6gIiXhSGg*{{cDpy7d@}fLT5vMgH+9;yk@+>dUY|{kYU+9_E}0kMnVq}D78Dl
z^poNF03*DeSfV#8oE7(kh39%WCwe&;iDu1~UW4}AocIcTLYz;-Vhp~9odjlxtUDZ)
zS~GWX@4{I2tMlAPL0PpPk#L8X&x~u%rFsf@=NUVDUlX`bFjFz;UD459SPvrc+>48l
z8mc=;EUfS$e+XQ+j=L(Xejc&<e$WIN;=y$!BYW9qh*evjS$^P5kTSJ|zcL&}k`JmO
z>>Xk~$Q$mmf>g6%m*&oAEI}8um+%euese-%tzUXE)OOWT*wTJS&z6drv)NvEXlfUk
zM+d2t89$ddXKcAhQBt56%I{sF2ZdXaat6C17X7YPUJRYS78TXepWhBgWR|9Rl(?G=
zcjV5MXOuhH<12RBO15wd;e_+iFU+7rl8@|e8Au&uCN(QN7KZEfYazzdq||(jCk+$z
z0KMQO-E3Bxq_8|A|1q@-vOuR2^5x<vN|JhzNuS&heC$gh@aKB5BwQ(;*j#21Ma<n&
zovV|gPT_XL4a?)uGrP+?Q5hAbmGhy}^&af5J>boqhZ|a<1=tw2;0|gqV3i3S>rh;~
zqA!_Qp)6A4Dz8+>xRkd!$bJWlvUA4odQ-5xF0|Xo4Q%6^knsaQ#egGc*vEY26$B+r
zS+*6ivc_$N?4BFJ182b62GWJIVDJMjrZ~m78{c8_%+JRrYc`n(;2|5tblpWsc_E{l
zj)ooWv!$lLOk>=@VLP4alB;!1H8HzeW6}vyTChOlSz}7)uPI9({PCaCn?V4*v)N+m
zS$w#xLN+wJ;vQ#1Q<YlMX%_uin>qMCj=d@r-Vg7HT-aaw9l5=b3Te#C*$opFagor9
z_u|sRes!eE(vDW#4p0Agr*0yRZVGc+wjI3?<?A@;^@1Q2dLh_o-`z)punb?l9vS|*
z#C2VGu7_H%-@f9^5k79aG*_g{E}ux=sa8Am7Q0tj4(-_zcm?*w6|7LAjnb^$-)xVU
zucaRJCz}i9FVk<#NDq3cY#o=&29JxaR|>_Xuiu4zjSSe)173dhm~9<)lQq<Ul-qZu
z-yep`WY0j|DP@sKVea!@D=X4>j{j$pGQaJoLmtf4<Je%ruo7J9n8s3hfbJUhQc<oQ
ziY*SBWrBs>9tQKgb*VkFC0sqe=h1^)AztH;;`}L9>#%n6vjdBqb)L5aH>Lv3dbVti
zyU~qhG{0S6Hm13@Zm#otokoVc(KElPCj=EIXvE1w*qx=)*sBxF<p!=Rm8dGBJF2)z
z%g4@q;D*%IDi<tE@60#a=1@=8`=I<)gD{+bbcnUR18Rofc8(p?q7@Z(HeWr-l5Q`G
z*M)2}zddB>)y-X<hRzd)bq63}@qG8e;Uc|-`ABh)J};DX1{A+Wl`ewkm0Fa+L%Prp
z>i#xDAmZkV;dK;+d6*G-<-|!!HR-`?RIIE-<WJ=M?RDQAX49pMpIe0|u>cp34wo(3
zG93;7c4zefy7WA6)j>;(!5LD8E4ZGlwqSO$-PL9EX&{XeX<@Tq&fT6>%Y^M7EZ~eq
zX%^ZA_W~6qJ#cJW8VJOKet1)gA0CigRJ4Owtfq@KyEQJ|L5UTw*n(0QL~!wk+dWDo
ze|PJRONcEa4JM}--!<}d-9x`|o1Am^|F+eCB+e5ulC)k9sBV3POjlcN^tO3aSNyX-
zUwY4mSKD&L=dK;Pw=-sgY@SPviCUlMl;HG6^F?g?^ug0lcVvtT+Q#jFxp|YecdDLa
zl{LnU|8$!+4TdYlA4!pp9X=_%AOQX64IC!yKFA86Q%OUFQj_|N+6=x)b99!lw=gt@
zcYxwr>9WL*_sEa_BuYi(p^EgYoEmF6gn@Mhq-4=vE{DLv$`(g8b^0r8p*ICCMUX4a
z{wBz&3;~8(;i~MG-WB`aXiDUGnnSx*D$~5qPC79BhF|gt40~*N91k(D)o2#++>*xL
zFx@5dE!6ZCfxDK24}<T0P2mbaj}VdL59U<$?Uf@ty%@p-H;NypClu=ds-UMJdgAg$
z3G{cn$L;xyz!hoEPPi*QC+`q~QdeLHnmo|?u%dP~(N{c$qs112#USmKYcvLP0$ehP
zUjj|{5<N7-fj{W33_+}cw=yWU?-K4K)||e)`%zTGV=y8F|90c=M89QjF$>%bOUGkz
z)_l-{-Kjb+f4S2P75jvbT-c%{U7y}LErlt=>8ci3e|aIcImhijg=F{`WNv0)1)w6|
z)?iGugO;@Uv9Nq#WD&<+%9e{BwCi&rhRcG};v<wv5GCY7`B7}Z)c28Uq>dGOqHRB`
z91_{DtQqHizsYMMe)g*XL(oN6#Og!;_0o_2RO!#74d9-p0Pzb7_Sw9-ML?I!^GP_9
zZ%cQ^q+{sT7o<{}rkH|3Lo`AT)teYm>1)NTYNnxyyUhXb`?F%2>ShKWgZxsB^p8O!
zerxloP(`aS2fYkH+|D-}7@>%tx6alzTBh&pPmKK<`%@a?6~vW125fzeHnvP8aIl=*
zq5jSd*#}s{ZyKlz7&<5tKJsb?r+|1<(-eVJlV~XsOV3x6a--NiE_F%sj5&$+E96IZ
z3?`nW?dNJ;cxnE1jynZc+J<*KZ%@Wz2RrkfbB7Hs(mNmK_at%4-Hf8pqzN+MokC?9
zxGXip41o<M+!e`vMR##^{E7UX4D5cjvh>kFx2a@BSt%mumeSS;&63u6CEiw2O?+-N
zpY{cvUFgs)Vlacd2GT2QMLq0V9T-q<Z-elGO3kOxyv{spCO2);U#Q!1y_e(VT&+T_
zw3Nr%yg9QJoQ(04I5147V$i}pqUrNW%&St>md@Q~PA)k-`aY7b9GxR;8Mu^i>13^Z
ze8d_mRC~~frS{p>@V$8S4ASZhD&D6u{u)6RlIy+^-It25vwzxy)?fzafbZ_fg*HU_
zx?P#0%)FCO#EZ|bEEg@ZDA$~iC-)9dc<8tk(G23m*V87hpnBhPMD1Kr2TKwq%F@bH
z@^HeVtkROVwe>U=xMC}=h?HjrxmQtF7~Nw^!^8EI$XK{}6x(W!p)~o`LF+kt7YHS1
z6jwp(AG|%jQ8H#5sFLRV#|>(z*jrQjI}+UmQ%2XqiVQP}eN>0oH<Z@m9aGnAtU`nt
z?$4lGmA6Dl{g|V<+5#Z@zzdyMUv}S9AdJ-~orv$GpLCvD8zCN<b5+a_-=xO#wR@s2
z+jp&1iYpDLzbMG5VA?qkOQOe=b3N_^D<E?GKRl9@X=I;2ns--~B7VtraPQZ{13AZV
zU#EQ-K#Ow-T}v1AwRzTeJL*)zOOg)dfEB%9Elj7nFI8N9a{`fS;HVij$GPqS3-_-E
zYZ-Jk!l$;^o3isG7Sds@cgGg&9zr|$>&2QeRHc$u;rPdZ_GWy7ztS}XoZ-}Qn_C+;
zq&9GNYT!kADVn^luT#bA`8;Z0bGmJOr}{w|_)m2H^9fM<ur?yCu}BU>nf)|TO>r)j
zT14bS=zZt16D#6hP{t5ns;QQzhnPE7EM!vQzPXmWeRlY9gy$VSm5<@Fs*0Z}LZ}`l
zJGo*dK---5HVB&=A@sqtKJUWp(<H6iYCW-RRDq4H@YJ%;H&a6R*C>l<m*JofigwCD
zcdp~ZdtQAbWM+-e^^>YOjtO3Of-YUdsf=qkm^QuYUh@@Scejuhu3h+UOjT$Jp;?y@
zGG8&F4Hpgg?8@Gv)92u)x}a`&2UCi#*#)5vpKn3yaS4~PwTuV{FXR$oE+NFBQr81^
zBkQ3OdfdD$g70(-dZM${n7y;FeUzJE8g8HxaVw%*(P#<Ga8v^CjJyW=N@|(%F3#L5
z^=s>c7V=C8F+R<csw4JUXun}jn#al{wq2254-X!2$*C5K^Geq;I=yF?-Vjj751P7-
z+GpA1BPI9X^y=<Lc1J(u#Bo|9utqTD2E;2>M}-=q{WpIWcbW7tV{=;<0;=zBupZKM
zAAA+l$y&lM5qILoX|&0PB^XG*th@XeZ&U*{(xJ;5$|vKmcxARw3H@|1fFEasnN8<~
z?OFfe@7$JG0GDl){>#$`bb)|u7-pHJ;h><^Kyo98er$nI^bI1c1}MS5h}e)I{TQv|
z>Dh_AGZojp{Eg9Pyzl)ZjQwxR@ECovhUdWftzka0k|DC!zrM5IA@o_n=<~DdgzGjx
zo^8-Mm%G_)^7|+GZ%4ib-T!8Q%My6oWw=Z*f#S~JyKf3n?|+a^&Alp?-noIb#%c3R
zsrieZ=eZF(|1ub9p34(E+d|Wnc<lYR?(fAJm?{iJ9k6sSL!FZh^E#QMD!f2kBKQr^
zA_oG>q=yOacs%{hE{;eGgq`ofFIJMj26|?@YQl7Fh0Gbt#uO!}z(xl_O2M^!n#W4D
zDy`rdKDAy#TqXy_U`C;x-qdQu6A1RmFrN-FAC6uL^^|ASdgn$-uu1zxlH0Qy%`k1X
z<q&mPC%48Spz_j|G_}hv_weAJwL|T=_Jlu%cO+Sk*DHjmOWxPhQgP#0!=!YdsYlhQ
z=RFx%AVlnO**tKt9Wl#{M!6=b6mwEt`G=`YLjSp*gpq#15u+Z)TWM?aDe^Xn?nn>-
zqt)T71gl%9E-`C(RsLJ}1Hzws>y~jDEaWq_<d6eiSc`JTz4Z4|4lF?WgJpWNTd_uu
zqkqhmik4yR7o#!8RX!Jq@?6d+ErDTKv>)y`99^Mz1cAq2bkP(5UHz+JFP$TxH$WI}
zUBw}WnZo)pJ<nIpS^QUg{1+^ZOaXd4HglN-z;~D23zn&2ps7hv9|UuG(hyg#D1bRU
zKa40|LHkI`aOAD^SN}SLbMWYs#1XkYq+ZE#9GC2y<vzaH9ZlBi3%(oJtLmD&#AMfT
zT`DP+=>xT_m+axu^SQMAfh1gBKlfE_(k2U-$Z`v2S+A?r3Jl=D>K)pediBj3b;(6+
z<a;Y<ntlEWLX9wKDYCK~G$Z>^?qm<QMAGgw>K0cV5uy}j#St(CVQz42(<7572q)pB
znB2|Ys68%GWx574UL$r5XbV!qI6|=}nHE515bj3p1}2=D0GtJwu^z@%Y{3q&l5K&z
zYe!H84NH+utZBeK$pv^SyToy`1$-;z0Npd24MvEi;;Dg&-AIWJ+**WmF<wie!oFyw
zHR*JeyRW@eu$?HjE^QFl6?;A+09J?CAV+OH<zng7n-5AKNH@<}>Xgcv{;^<8+IM_I
z%Q=_F_${1sI;FlB<lSoZ#c7T<<*POr9dGI^lY#Jv<5Yvm)v)G@sJvUFexigI?~Tdy
zOeFHuW3(nww3dMpX^If1cT6!Admff(>%5U=7){tMjoop!`T>Qd$H)APqp4$2gBkH(
zqkw^tysXgz`a`w|J$9v|)VhLw@(wx!^)Wrx;iSj{ksk6`M1`0}Q`04b8P#TFKmzI@
zNi>Sr03IhB5emT~|FYNr7b^RUVPRw{nhRBdLv&!WFUPnE++c&Qi&vq9=Mg=;Ij+d4
zG`07DRxyr9UkfZ@q!2luiK>FC893y<lxB(`{E{=ttsIg6VHZvi;Yc@w22SM&aD;J_
zbenz99MG__3{6C=fWj8()xXX_!oU4>;N9(|a3IZ*o(1^qF2Y9#|L29e#~=kz^tF-?
z4k6c75u^Zn(2fN9?&?an{KzM=oL>fVcWQAf@ZpU68Ah@*i<sA-6Sv`LJYrse+;3M$
zxG|W&WS35|SY8&$4id8K8OKf71zoyc#hJmop<gd^Wa}v61sLk`WVD(Tv<2#cC!40E
z?STj9h!*#SlKpF|i&Bs>R0~fEPN2njnkwi)I>N8wWgWT&TL3XVnRR4-ZJyPo5n#f5
z>EO&4De!~@F_ON0VIrCV4rxQ0p%6F0ij`ZM8t7og!MIR&GsFPt`Y!jV;eeq0--2Q9
zLino&X&72i83zJjKS*B!NBR|QU3gQHhy{na8iczv;AnF#2*INzs}`UgpgIMdEo3v&
zYY_u%#Ij5!PVpVW@$~7t#o&Dh&k=9*pShFO@xSFXe<aT1PW`91@g3h(bDU>5m9ay8
zwf*%Ox%Hfuc`{cw6n145%@9691I^RYAECtmjQ*nlF#AVue@_kn_vGp4&-T76Z@c4W
z0}nN|zo90rnDIwY@wa7osA<pJyjo%Fnp5!?&WpyLKEEOM3}f%m(e>IgjQb<x4;D_h
z8(vDz?bq?d+E53j(A120>LHgLv<jOW0HqV6HvC?d9F>S>4V8HtFn+2Ye~<bL0lFx|
zY4+8>FZ0i4qzBXGB!&M4r5~-#Z|3#Cp!54gsL-!__`4eaD_D%UWE=H^f35KYm`c9W
z&R-N-Ty+(B7;X3sqwm}I{4I9=Nc<;86^z#y^PR1VyYP6^E$hc?K5X6o{^qfu*?kg&
zYcO`*UF)Z*G^Q#RK_D`5sB&yFsq|7FyV7eM5c(g0uZ3%YtBW8!(6ftElyi*XZe_JV
z`1*AO>*`ts4g8Y~c9<wg1D<CTjT8R=6_|nfM31$vvRW(0v(H;K_#09)(EqMYE4^B7
z)yMhPwMhs(+3Rrq`Z(Y1Q?uW{fA89G(0BhUsdsuay?N;)8|4n{SmM8Mf1jYg5i6`-
z^?Xp?=7+QZ$rJ^G9eQ*e)hE|!d;X>E>+VXo?dvXVe+lm|v)!aqijIlg{4t$uq2ecc
zhdbDNFw59Mu}HW!qQ+nSjYypr2BSYGC6xtqWV=aM4&&-RmDG_tkJ|<WFGIIMjyCQi
zOuR8Tls&#5y^AZ2qAd9{D=*xD+CUtaQhH>Lz$T7$sUO9(cW|t7f#UYEaP~ByZ2Bd*
z^@kU;d`rG!xgqGLLV-4l!{qV~&__xu$M34NSDFI@m$rCy24f~!m=wtw$KHEi3rMyg
zem<eytCp_8B?N-L{#H;qK&B7fFrNV%SfGUrLIhX*xQH{Lh}Yz048$cNbb&iY-U!{R
z_5C6ZbPx^Zq|Tn*f9a2OEnXS8K}aa;<?HFfMQ>hg;x{~}d;PmOWqaO+`<J(#t+M<0
z?zVZ@y94G!?xPc{t5)v_O%92dznffVJM>jfsWw9`{lOCC3*%Zr9}}Sg9Ef0!gH8s#
zRk%MT(kMJFY!(7qp-WwOxs3XQ-z#6jcd7xt{k{G0CZ61y{pauX{CBaR<cg>P)g6zx
zmx9-*I1Ra$bngmlJk$||zHWltJh5u~w{v_dowwhtv8p|ve_nWEEoY;3dymk$D?8NQ
zPo=b12&mRSyM3jZD+gU43W;0YO$&(R9a#b{XLVoX(zyg<h(n|dV5_v{30-$+7vkby
z;FFY%W+eu+SyG5=+h$cljUTmuqQfe6vy~sc7K+$i<YB)<2wJXdBcU2}Gf8SrKePup
z81`e2k2F4$&m1j0g?b_cO4j*N0#Uq-TS#N!i*IZlW`l7?DM`ZceiHQKebE*XBPS|w
z&Y4VfpS?MCzXUE~-ouGf-y|cfnJ&3`Qj@smjHi-Nb9s@Wq}Tq`r|NRn&koNP-&4rU
z7_a`3@jgmW<o6{$mBa?aF*lhU2+5&Q3A<yoK;kD~eFkoUgHA9$z#)hMyV;sDzu6j$
zX2pv|_nz5~?8O=UWS`ILBX+KK1C`DiR}i;)K)DTUsV=Z=tDz<2$fT1N+{*c%4YCkf
zQS#;p$1X+{)t%=$Vc~#_)N>ptnFUp?52%@~p|)<{H#`M7D<|x@K*EP(PPHRN1ei~7
zaF+q%je7>i{sZrzDF2M$fRcL*yjMjZF1-ifuGJaguomJUJ>1VWK^g0?D<4<@Im?(~
z5=}OOY#O*uR2-G|U133^LVLfwUJtD|u6q<=d`?nCPIsq|b@6VKE|1Zh*Zy}C<J_}x
zINVsXZrp+hTy4h?ghsLcaC$|5F5bCfIFoyU;W9Y}_0AMq4wJiQ4xr|?RD-ya$>73#
zk)PWD_Y;a+Kp3_ikbDT<GsABN^wJnAYVFAMEz0aXT1`3O9bAI4i;o!AURNt7h943r
z%&&X(S|_8i4x3-HWmE1p6SqxMrySuxvY*4;aO!0L#C@J8Y3o|<m7WvIoC>Og)>a*_
zR&TG&CoW=;3tTw3n*Mp9_F2}v3Rkn{5!OA0-m_XUOo}){n0#B}>vz<B{PTcrWK({+
zKYjg2PL9@a1AohjrNKuEphg*Yh^$=~gRMFG8Mo3mi!bXn8bf#hp~x(EKf=uxfE<}4
zTS#+>Yd}z>I?xF6Oa^uI$6fh*GR;x2p5wI*(5O7;5tav~G3Fnm;6KxPX73`20`wQB
z=~xBD%qxsQY;^B<1_iox=UUnlj?TrT1!S|>E+!e6R$9LLaIR|?@lg<BPv$fe(`ApG
zv0+=dxnTqW^IQ{l%xYJU*ga#k$`PNVB$9}Rtf`yZGHZ6&>trQGt#`QCeOk*-kord9
z4iP552|Mfw#|I7$D2-VExOX)Z9j^@y_@($HB^G{A^`oc_Aa^<vJ}V7a(TgyIev-$Q
zAv=cNl8QyQ7nrLh5QZn*D^wf;-R)S<wmZsm#+i6xg}t%4SL3r!?x;(nh@N4~BNDK$
zl|8f-7H0afy~=p@HK~4=4Bi0Y0rry20<;aD;kZA0QzD%Qm$IRL)(`>YD4q{Q`2%dx
z3mQTiydV+bJ~*%c$jcRQnSBr>k={|UFN6#jyrCqRiUyH6%qO}ga}4{b3Rlmt4Lzg^
zCNiXKm^y{^)<6p-IBXl0-v!_NZysZ7sqwsihJDNej~xoDA>K9}3=n~}CMO8i;kmzt
z#t7M5uWsNH-A#~=AdBX<9-=0=CE5>TcizLlJyZzm6Wxg+C3Kt#kiH5LNHt6p!+otA
zN*u>d?_qh3xP!Y>XIEIDi^XoEU@ntzkFe3)qcre|Mvq|{Wzp=^!8;_viVHb&xe*Kj
zc-MY!`HvhF@qXVdz&xSRFK|d$!7n&|VP|g10@Td{E@!yhUqDE3u6(hG44gd+6va*Q
z#_a;QJQ@^)@_u`G90Wq0d%sA4#MLc-CDEqd|AUP1uZ#UYiT3}nkgAvpz5ac2Eo}E6
zGWY-C7XC@l@8@qmc2_<j_z3S9pjBOeo>Dyix8UYiiFU*4(_6#Zk9D@4I9k)T{pgRj
zHaA_fmQBm2UMEa)7d-CW6KRfc{6d}CT|}B>a(ZrHBNS@uR4DZEMU4t)#eXD0l9)!k
z%|+N(;MRZ`@ZqG7_xzPWi&&!ArYVpAP6?MMB+39X@0jO*^oxHjKK_6I2cVfP`+dkQ
zYm4SL@bD}98-CNu{#{c>-ik%&<sEByQ)Hd^)1nV!N3N{jFf(OQP}O=d^~lJS-<+2n
zQIp+UiMDWw6k}qO!NC7ZkIrL%J#74)*!_4Wf4m#HeOx$@2Qt2ozkw_x#Qg6R`InM;
zU7{v$Gauik)o)=?z^!UOb!PS7tKii5HIv_S)oC9;nt#aZqj2zFwK$tN{r77ASJMCY
zS{!@+Kqt?H+W9var+r=cS6cS>^72|dVCXh*d~4W^p_cgR6It2b_uafg9r1&2_4(Cx
z?#q#EMs=n)W(FHP*?#DSfpE*UAOZjTwO`FcrD-v~y<ab8If+S}a=C!hmn(R+Tt0E;
zFM~gV0mX>$lEdWc2em4-TOMMHoZB`@j5b?BIKimgy%(5-%#}28I?~e#qrxn&2lqK7
z&w5VImU7?Kmils_6h*2H{N&aeNT2P<3HgFjA&p)sr2k;~mlwgv;#c1I=*)s#S+Zq-
z{5&aA>6KSGO^OM*!FO^|c-D_Vu6hE(;frFz7MHZ_objhj2V8(?RMD_>lnSZIPm>85
z!X#?1m+NlD%0heu(+dRn89;L~soJ<1&PvW9wi4w{;4@Lnm<f4r*ow%AEo-=obbZ`}
zH6Ik;g-e=%tl>O56m!EbveJ^sx;Jb#PU20IsyKe+nF;7RY*<YN#ioPA%I*sFSZI70
z)UgHe&<jqwnh<~~Q+E(D--(FG+aNiDsJBFD<oU$j1z^8ij(sazX+{ly_AkNs-vjp_
zq?*pr(*c?TvS1+3!44)-!5l2@F8%5O55%uI^YG?$3h8XYtB)MsS=H2j^JQ0~THDcg
z>Bnzmf6!X@E>C%7tK%_4rjVEO<mZ7biWQ==VvjfqBAdSm+;qm?b#8B)We0PB4$jHn
z%P@mv;+>wwRsv|qersMhj}nOPbT5QP#h^w7>)o@fEI6@CYu!xX`hr)lILTbTcedPa
zHEiXs{WNYOPLHB+o}lx#aCriOwu*46HpNsL;{vtlDF<ro&yf|@N1lVFalA<#-sKxU
z3;UW~&9&K+Mu8N~fZw1b={vI%01wL}E9_2y_Jj98&k^;~6GBS@%uvp0xYxw{DsSxg
zW7oZd(5goH*jGKP4;Ap3f#6^Cy5c`p<6oEh$?R8c=G(LB+i^}r#Up#Zwb}Za*EJN`
z?{(oKDsSYU=eQFg4ITp^v;yD%{UpQ?&#LlwT#V+mDr3HiTNgM#!E*LDu%x%W{T-$M
zX%-$>Ix9Bc-?Ky4xURr^Q{89pHF|jk-iflcXt5*bTH}-2jPL23-D?%k%yW_;d*S_D
zPdd%0Z~0;HbCU&cpTzHdjC1|K)0-cE?&|otALDmE7U44JoI>Z{{0=q>Hmk7?yu$_E
z2s#0sOanJS5*CB_ryu(>!>K}Lb3O^~`ERyqe0*E*zri!r(f@aq|0kJv@Jx<;A#rHa
zBgx0Y9(%TYf1<ioVC<|!z3t4bHonZ^!YLdMHpe(SJCr$X-M+ppG$lTs=iA^vTpaxR
zZv??%(MXd#-3JsjrcTc!j(MeJ^WA39tAOzg^C_$t#Pe)B*_m$i>+2Ii%%w>JPOcKg
zUeTa@R=K@qnXRYW#V$yROEIEhS1Y%}xhr-wW0r5+#~A!PfthS`Z7F+Vjg44^ZCX>V
z!+d$5_C04}Rsd;EdeVj9Q&Qj_DD_4Nxi%BVAaB#6*gusj^uXz39bCrZIK9F@tTaPj
z@T_`;)QsX;IOX8d<MI-b+gY~fMesD_>{}-pc_Tf^<p543E)&!dFm_ltn7mP)?PYko
zeM!bJ7N=-|meHc0V3%z&P8nFASVw3}`1pkK%*xtJ>zr6JRkZ+hPWywNNd2doYh=b-
z!Tv9elK5-VnGVO$izqzIYTlX6flTl8fEg&cJfs<(b<enJ>Nh75V2Wy&8w-_h)6NJE
z(KPk+Ej^{(xEDd+X_CxkKF2i?L;*!Xr1%Fst;v$1ktfpNI4(+Z?AzB~Em+rT5APF$
z!?Kr+`y<X!-(5<OTPTt{GXJ8)_BM2@i&()ia6e*Y{o~k5x}-}PWn%NK6*DND(!%-$
zHN=gcRVI53arals<Rb#wlwZx9>HO5<BvMaVsY5Ioa3)1tRC4!@E6y3&8Kc_Jt9)pb
zS$9!_#2cxE87r1nN}7<jOIm#dn_>73yr`w@l|{+((`v(Sm9}}FU^dv*>9E29<x2)f
zJ^s`3OsV9bJYnqKq^?SjAA(wz+^e*TA0AOXMm>r8F_2PiKlwe)9CynjB=DvQJlnSu
z-Q)2(y82UV?sV~(D`jt1?bj@a!>yT1(k9jcMI0+;f!^uK9xY+p^dp`=i=4nhl7Z=D
zHwWH8G)Y4o0;bwI9Wm6f)<Z0*wM75tkAjl;EoQPiLTY!GIY>D{%i~GbKafs?w)Rsd
ztj;C((aytK+foI;<yTYpQXAp#^VfF8<#$@7@fY$e)Rr7z&?xmrm~EC6@72>-VJgw*
z#_XaUMw|9Ylg*hjlRLCPvu^z$7s}+vWxa&rk667E%No&L4*r2$dp^tBAU^ol2Dch@
zgr`1p6k1kF87jm^iKeVv6G&gd?woZW5sUT11YNx&lFJ^f>O{vvN6<Yphw_VBHAF=U
z=9+a^P43Ds&`|f^%qI0V)X~Qd$v-x_+A4_rLNZ~+{A~S+;nv&yjfYHcE6V!i#u8sR
z=s6@rX;Ci1xwb#ukNny<H-4{t%uIUVv&ZGN$<a)GVXpy6m?+izLg~Jnl*Lr;QTjUK
zO>@7%P0-pg>1?cD&%q%UErBhnm}`uW4Pji$UNFAxi<jOIZ6lC@sVyp9!8q8T$FQ1G
zh@xbtie&oJKyv(Y^XZR@DKpC#AgXFuon!uz-%{hP<_;gcUsL^Zaj1#J1^R>;(IYl=
zK{bcmjVE46)fPxT5}&$wYH}`C=w8CIgz*pbD79YC9=43n<RmXy*Jd`)vZBhri<{I6
zXU`Fmi`LjQcQEA{>hoZ_5vO>IsL_z6?r=t}{z`J>|3ln+05$b>Z=)hADhgPT4hjk)
zO+Y|u6a=J;h=6nvkls5aHb7dC-ib(4DFH&SQbUi_00BY|J(NHQq<rE1{oi-KnR~za
zzB~8+=bi~O;hddw_St){wVr3~wb$;(VT09;fJrIaNI$kBiJ}epJ-a}Kr|{S~bfo>}
zrsWz4<9v;9T-e7F@RtNe43o1v7hrpe&@9*&!?dvjt^i5J?AsiaBvYKU<<?2v6h+e9
zkYw4ny@}<-{iZn0np2=F)J$N=ihu4s>qz+k7EubDhj<6C9SNm4NRf-CT`3>3=Zgj*
z_VsTDOo9~-#B~EYi-T2V$WF)qya~RH>USTf1d2IjOe2++6w(nC#hAgk5M9W?as%tf
z!B9dr4I5Eu*=N}(pyXX~Z*eJf_+239zlG<)>a-ca=<y%*-1t5u4-XAu;jBr+33vaP
z+$Yj7;u*7j(V<PVy+Ray|Dl9XMeT>}$uSZ|V8f`2xm>y_+rxDN5|mBnx$1-A^{Z7F
ze&vK46iwsCnSJ-nOKNsq=Fpov!Hy{zZpNnG0NRA}y#nO!3YBhT2OMI98ubZn!m+{@
zSsrR!V#b{(zK`og$A8)1m`6&6Y<1TzoYi|l0By!z0lMB>d~ZlMtNw_g5OZ|j@+8#D
zHP|TRl|8?|h0O*oL0D~z6j}`@FqgFtmDDoq72&Fc79;5uT6-ycLLp*{hJKHg2VcIo
zMD${phJqU;CGRl0Bzv047|KSwtfTJ-bq-~@XQeg!+D<Q@_4~VKdQyDs58Yr;|B0>Y
zKW+h|%_-<|fW7>IFvb-<+(bL&=J&YXyc9N!Od>-O=(iv|rF(l3a17Ubz1FBmsC_eO
zxj)!N;X29XYw@)u0_InJ>jrf#jk+jVFfjA)gBH(O6YUQhQ*QELCPEoxP#k7oA?yCy
zB1ydpsFY@ENiAQit5;)y=VF5}Oa9vyHiG_OR;RG~wedC0ri<LzX5_@5OTM-sJ+qrm
z7LU)2C&vDy^;MwRc=I-}9$JB-Got~|-(=U@bk7Gv&%eFZ?0Oe?YbT_*nE~#$8ki)>
zx;vD5)kSNOezj=P*RX4lY;MuuP^nwbIJ;bH#P#&Bfv*zp58g;DT00xJ;)w3`?l{=w
zr1zkYfyvS3wjfj$|6UEeHy(=iAPB11?2ehxRH=3jZRL5SRhIS!yi_NN<Oum(xzDI&
z#(46+_{GRIf&A$Q785xYEf&yC=&r|<qRVx~fMBity(8bwB3ohv%8Zoi*&Uhnh^7hx
z=TQV_>;@UF*B;M^51*Wz%aT`|saf9h=bYNO;eJBh@I&c1Be6Sm?ZPXwQ7}n}TcteV
zg5m>gT2E1MYK!`gCYN5VO7AkQkvSom*&i$}3QNOQeP@)lRE7L4|Fv+niS(M}0VmTO
zfO;1NeTVO3elC9RjSE3+wwS6fpW7vH`Tfj<Y{To%a-ndGK_UyE-Ed8u(*|7ckV%$(
z+1Nqk=U(!W#PTSYyu&Mgh~ESBpx<$LO<IE7h0zQ3g>bk%vZOk}fUK9ZNaxkRIqMiZ
zvtEzA_a?>^XxRsSw>Dl{leb$Tl$ANYi>vGRGoPf${8uL*HGJP-HAhT4aZhe(MEiVe
z7P2kWHZQu49UT9JrEz;|5sN9?mRD4NnF9l4o>}wVgqGhLmsh~w!UcS)x$Sl<_O9->
zI0vTktD+n_|Hby}MP{08A4sl3CH%Gme-W`|T~ezk;D*YtT2?U@!lUF|PEWX?gJKo=
zfmrY$*y5XcpMIV0-z;$tuzKCan0!B__PU_uq<MM;wciK(`!R#9ugMV43W+Ay$ysMN
z%0$dWAw0O;rP?i{`mDf$$J@=4tsf&%ecPT{R<@wcbz8e~3UlnL^yP<iKb-^PAzn{s
zEh`#}Sj)B<hy7*B$2V(oA8Z?j#B7(mEWNkSCe&2hqEdRJnAn1{#9XSGf&ewlNcx9J
zV7U{Bcp=~yg3q#s;^;;Pd=_B1%=o9&;`ed3l@uj6+daCTZu!=UQ{8sBTNOYqk8`ZA
zl+>dFXQ-;%_M7aX1C<gXeH%)E5iMWDvfR^Z@_vY_Tut|&WsxhVP|y(BhtjUN3Egj5
zpZ5F?%fNdKBsBNK%$1ZVeeZ@sScRI7h=^tOVC}}xgN3<+hli8~aU%KD{%waklWHPu
zKh)q#31EA<&{AamZgpf<wv`bQFD%)ow+JX|ilZ@4X+Cczd-CGr5po+iU|N>zq(`}U
zVnlG*RJUOUb_p(C<mwPn$Y$^>XcQ&IN_zB%Brz^#g9Lnk`|SALeO(@3?8=@n<U7B2
zzrwSkzKv2_Dh0+${A&7YAa7I~)k|@TwnS(RArkjX9xIE{&9r8{C#<R>#=i5U2t>)Q
z`vE{&^27ded;T0d_2(6mH-n#4Goox@9f+n<=I}zo>*KkAg)U~g1{<mpdxA;^8*uv#
zJXWUTOxp7(ZQrJtpX9PA#;!@w;hbZYM{K2Ehu9wC>%L}}6z#nv-LIKgYww$%z4V%l
zw(UK9^NKTP<IAI&6CP?%`MVz_wi0x+@gyPFU6Vs<M#>}QgOvK%vyfLFUrG&#AS8vh
zfy{tTTvNzs#~y)7O!*OtM35Z?Y#JO!!80D<y~+Z&Utk2;)K`~RCtH7q1ie9q4oVim
z(p+Ua9my>2Cr0}%gMN0)-iO#))fPVl0c{FzLdt)}hn7>UoB{vVn*TO(rzz8R@blM#
zh9*yNajn1`l;XoC7<?G*aLvk1`!L{(l8@W3cC>?;N5rPqesBooBKFKM<B3dmkAbg`
z?0`CcYxmbCw8f^aneT9NO{qp0;pnzgvga3cFcx;q%@jV)F;fw)eR7VVSma`~y1v&f
z;bWP17*kAnJONE!UV}KgWdj<fI4Yb#vwhEwkI?AyoI{XHV;G4<O;f0ZYiHQxEBzkw
z3)OcEc~GM&SG=i*%F&ZopcGTQvWNjG8QVFdAsIAN8O=@+A;9D4?eekUDxLT+tiWS7
zl~*j}(wt&9p`WHk*~g#y52Kro!VJ@veqpitX$K@vfkS-LwiyOgB7lka&U|H3i}1rm
z_6LeS+s{}0Ic*d?(gXBRw9IKf6Y#rPr(t>*GdW2!0LLdom*BBZWE$^6>{|mxiItP!
z{NZv$K&9@KWr!Yh+ZIVkT#0K!#U9R)cD%LcG7Y359#$%pt$s&NJ5r@&GGE`?bQzTd
zjEU1z*0!E+LCNO$^aM-<>A;M4%c!q^)_i%txR*@SlW&#~c+$MN_yp*!&M`=ot1lw{
z#sn?T_<ZaouglQ5h(|H6rs<obUl?WayvA@yoEVgXmjlfa`GJsOFP<z?Tn`E<`M1;D
z7jr9a`}g#Wzu#~+Zboh@MQJJ^lG^JzyNqA&I{uLTXzO|A%;l>8P~04F_y2~y{vTcb
zhvK3hUr{r7&aQDs@NP`rx0~^5FQ2=9%kbOZrhndJ0_8&aOJ<L)hv!|q{^QKIV}ciJ
znDu`?{u<373K@lk$MR6P8Uzf%K`HZ-gq`_zx0X}B_!~dfY)>+h{e7kV<YOaZ;czFJ
zT-Q(y9&-}_m8u(E4N?LUC&tG#$=eiX&@pZeiFQLblZboEeW2qXhb&dlJ;4WJgUhU#
zI9-xi=@d2k+>Mann`P?o#&WFO8lgFuMHZ7IoJQRtr4RHdB6W#ZzR60w1z=v0yKkl;
z$<8b|r@?!G8tRIlWsbwmB}<9DBk1Z9LVQUXRCOiJtBM(KvILi!Q)-qAQ##XYqbR;P
z*+VHN>k<60S5^a-EI>sfw~<rT!lZuV9(3TD)sv)bpZ33AIs`ssyrKDq(sN6E>Ysok
zlX$pq{T)=qLG-evONYZ1%1fUGvR5UK`G9ez3s0e?{fVH7$*dsbvCE1%GC(+`qS$5X
zc3_xz&wBcQwWYr*D;%AmUWP2B?7N9!P4;Gvxy{y{>ZK1#{lE0BZaLB+R_vc=?Ia&I
zHzectvxta^-Pqu;fNZ=^bBAbC5|I){-oIr=#=)!+?FC>Rev;oBs-0!YX^k%n@$L97
zV9=aEtAlaNi#CfTbvFy6#J12=`6+ufJPZ1$C%tH(G>;cRFeGA7xb8NGbMq!96GuN`
zZ+x3?)Au|^A5+PgaA;q{HFX8AoSk-SZ+mkx&7v3WgZsJov6MpV^S)`W*#!U&2{MoV
zD+=rm`Rhx~j;<-c;g50ax7R$}z?qoG&%!o9Ppa2wMK_BC)8^egKq|?^mIOcuTImV=
z#2E5t5S_XwAVNH|G}1%4-((aAKpY-tRFpSH;W|+)nB&>C3%Cf>rTWf#R$p>gVhdtp
zNiVZE4fm3qi6pd>eK$?D0H#kv@&O_wzr(HNs!^Aru{cr)by<SVveuWR7V(g;jfFnT
zyZ$TMfwKkN+=k@#)6nEA?JsOhH#3L|N-g$giiaQAY+87#Lv~?0%~AAC%%FB+yS=^r
z$U1Ru{L(4|{0nCK4^BkcVq@RQ(jL&i_Z_MR$jpTm-KHeXAblSO2Ty?ia!h~};f2pz
zUUY4v>@i-OHH%{K_dFAEyBb&F-lS<D3o<BfkGguoqA=uRHTKQQ5o3M8v+RwOeZ*$X
zzSQd1H44VK2b51S^4-DZ7wU0w2yXG7l-oQU<m~e`nM;`Hq`yMJyxGGB1Lx{2?};JK
z3lHKz?q(nQdg=lO%cxhx{`RcNi}{DKjL#2x9S|+oF4V{ZC|pG^`lq2%GYw{2rcH|h
z!LyTRS=%uO9WjryMiDbd5c@MrCoO%w<=R0@fPb~i!z&n8%Q+{>=0BJ)rONolqq7hW
z)N~iiQQ#7LQP0s?%g<RvILi^y%z(T|AFNO+NO>FlF}AZ8Bh&on--Fs!v?|GaBaG7;
zbRFQ_fwxduOhRAtbt);IXyPlKqc(=^$+vAmN~NTNqy>@=#Zg_?=Co7%&Y55XYUQtS
z)Dp_bTy_KT+U^}Ih%WYKTV=GaTilln?B7EiuKHYtv=%s{grpme(Y}@28477(NTzmZ
z_|;}78FPx5^N0<HsTE0Y>X7i)HKYlN*-QUYgr$#D??rEb%P*uOI|9AGU8}Y5MK4JR
zf&lVHE?u4nQdvYUw<ViH9vFnI5x5CBZcdGj)7!=e`s1U6>i8$%cTPfW>jFi|zlaT_
zEaC{uXeaLwlu8{yxT|$D;>4Td`%A66>h?&=R*=weENx9Z2+8ey;-^4EQVda_Y1*7p
z>-zc4;#iR9AnRlGW}pU_`;vM~5|JLdsI_^fcsz43Xdq~^2W{Jpr9VkmPXfh>ORT|B
zB~XM1v@p%V$Hk5%B|)i0v}KGu6H;6{u(6j}3e2|N{CAI)rJMRxwqywH$Q&8?dK9-~
zH&@0>4g&^5F_*cT0JERsK(QgL)j7+V?8XF)-&SXJ+>~!W0KHs>{<VXDwnN>_mGB0Y
zup{CjmJC3{b`@LJ6|VL51FNGm!ud$($(0hy<Vu55^~j`}6D1nO>p?yWETi!(Y0+PG
zyCmDy;aMPj^mV(W&~?DXRGxFI&}~JIMiBIbu~x(iStv3g6GglqG{{kohGrNHZxzlQ
zH#~v1RV||rAX-aUS)SEx;`sNLp4tSE5~Cc%wN-MB8FSgvlw$b+wBh1iMx8ooS5aE>
z$fFpLD`T`1j+o#V6)FIj>k6(vVuzrvcPMR2ig?V7!d{~^93)Rzgcsqwi1=0V^nc7%
z(42o?bedTwXf|@@WZj+1A5#r@h>U7v;iE`pqvwtSSKvdjX`gLc%nM)@IsOb5$Lr4F
zdyU_d82`fW-$2?C$|-U{9!Npo$Yibsjq`U0#!gp^6uT@G_KPLk2#x^On91Kn0es1>
z;FPU~LrlE1ZNFzedWh7rZ}`>#=u0Rh=UUX^4X^0Q=-ponmKP!zAlB4z#7Dt^3A-(x
zO3Gh+^#cIh6P$uK0wN?3Zk@X1<Vi>`RT~(PWhqGDJFs;TCKa={Jsv*YgLb+3JWBd-
z)8&sb#gsCmNe(4_0}gtLE?%Fa4agj#d~^ByXtG*wdJD@OtjtX*c}L9N!yk!Zjwb0+
z$*pgt$f%x{?f~-45!UcOBL81H@gL3juu-(*#jO+|<2fGkgYiM}C+cPkkd~!st=^qk
zCbXxPF(tLiO9He|ejlB`hp7i^Zfw@vs(+jajuuc_h&O(+kle^Hs9$`dPhx+YJ2&Nl
z1842|`A5)izQqeBv}|nGTJ~xMC&kA34zk>yofgM{$8;K{nVc@#LTp7O;2GFsDFQ?`
zWuFV6WdT;L&jUut?jg$cvE`*ErAW29$H^O@YmB;pWuUyAqNYL$7KJ9~h-`YF*{$!0
z572!&%K=}rZy>9X!I#YDPjpEO#=TnWDPDpCYZh7*do$nyMj|9bdMjiU)>1Q%6|Cc)
zb~V>)IPsk2vf${)!yLnbVZ1flK|Jm&bqfRuS-%g`iJW6%e?>VVfg-+JawkG0I3;(#
zLMLd}D21C8d!lJl2YrJt>0Ph-VRf%2`s&tjp0jLhXybKE(pGkL8s``k!Ka5X3=~>z
zHKYLKw@Eh$USQ1hyL2G8@9vy#O-A^$ER4(pQ4>rNhmfz_NPw+8?&xt}NC9w-G7}tH
zQz6tIHyf%;mIdX`&HM>T3R3RM=JUP)T}ws!Z+}}HEAfgo?rCv?MjI6Kyxz(nij83!
zd^8o^)~~2j1EmzA#u~H*D7h8;+QoZvj7!hVE2O6#i|w+~_UyG&H_ioSi9v-XnoNVh
zX=tu`&z%_+oKC3;(xU)4ziQgR85A23)tFnlHxR&n$>w}_;M11ESzH4yZdQI|+YkGt
zsQd$5Hl_GRV2@XzQA^^<^U}=y&HdNm*8Y-|Gv7ZQ-|p?7vpsX>oZWu_Y;MQn|09I@
zzbQlouv0NE)U4ED))%bf7>1u3e|gH97v;@Vs_TPZANj=vJo_UQAIGZ<<b=b0KMm~O
z*NDGMjf_TPFqqxNMVtCja?5S(VzvXCkEH(lWYSrBWZC^OCwg|ac^tIg<i(SOH2po{
zR)fOs)%qOAChwxRd;!TXAwFm*D`=f8)B&<<L4(F<vd@pk99x|HWC@0b9Q5o~L0n*u
zHY8)y^2ZP*K6~GG?=-#X<{_}=%f5sgrjL<LKr2Vcre$T}Knh|P7uCUg(i03ERRW6z
zW2Rpse`60!F#Xx8X*{Ls=){e*8f`#|oZV}>3bXZkC=;dmg)i)=rKJVb!uL`sa|C5m
z5Lf<Px2$FOZPqb-SFz-{cw)ywEl}|=K(|06WW7QZBe^q4T}v4Ss?H*3_UALTKY;(1
znJkPCpUPe<obIZU0Okf}QuUS5aYxy>J>m?QFSe1gRBd0*PmYjS4A7EC_|f)eOZ59@
zxg=DlU@zo0^P<&ZBzvCt^4`XGKzn>cr9zZxC&c(V%GpUVi)``tBw^DDwl$EgPTd{~
ziVWXGNa`GCTJVSTD3DgrPgCj*iCRhe&9tv45AlcGPU{+7AMuxlf9QG)%I+c-R}*S)
zIUEv20>&{d)V{J9X<7-8oN19rA)=NZcPsXm0XYVMNF3TA<xNmC7;VEKWhcP_v*24q
z!=?B9$g47xVdIO7{2`=k5|CWq4v>N!DrJ}>1Y+)#JpR+%Yty;kAsM43Zkg-_GD{JU
zH7jg(*9F7uxq9l1#@X6!O%E^i&lFFh`{ef179*-+cI~>-sasHPVe!T&i4x(4<Nnlg
z_Yxu)sBPUCzm?Z#hJTq29a@Y(a2X}DIpr3o#bVW{+moj3*H-;iW5t-lD5D|zz)8#j
zMS4l$Tx#D>1ZPoyMen1NDz#-4<wmP`?pE$m>|SsTnFtUd2M{+&clsiHR_u=MW)mRM
zQYVNeD7n8+mgcQuePQ%dF$htq;q^AhfMmHMN)o+4m8)R6acdX5f1mlrUgAb*Gnkyv
z9DUsAOOeHQDji=Xt3P2gX!!%HhPU8lWz_>7kcl8(sQ&RGH^n3y>hpk1a4zHwl<x@=
zX#(|5Fo}VeWqpfCg}sV+{p3bVj902sri~y**l9&B!hTRcyO)kqaxZK&OW0tgUu~CX
znUL=HbOp7SE#X_a)o9dfGiv1FT!4fk7bj7z>7-55dgwGfWJh>ycfg*oD#MDw@2er#
zPkz#dh)VROF78jk6!6u)j9QE51A$$Zr;_>q`ZIi<Od~R5H@8laE2Pe{KmCu!$%*;@
zYF2oBDB|-Art>f8%dgRwGjWtNoqqAC`}i?p@*csvU3TY%eHXMy4{_svQeGcG^N+z^
zXnabb<62V&#R+_3iag+i?`9UPd1!-8nv&DfOWurE2*+*|TeAW~!fI=ljN{!Tv`do$
zAis!O2x1^3K4mj7`(71U6zQ|K#U?=U-0}cTVuHvA<`4|w9tF2V35j>vI`L_CL5;|x
z@gM>SavZxs7?b>FK|&QK8y?SkoTh*Je{=-T(Um_875&P#MqLSV`;5l3r?31oH)lZZ
zukaz1lS0>%@cpc)n0G^BH=$=_-c?41YVX|U2zYe1Q@Z0?@7>}s3nLeai|EJKEF!Jg
zhuy~1Nc^6(o+c^+=c0b0T$*#b58st^y}o@mra5^)!nf>O`+C^92y-u0=u(pN_Vh!1
zY|#l^wjt=S@b$2!P>Dq>+vKJ!WM3A$Z)4;Zau_n$Bk#30vD6-`)JvBlO0(0NuhqEI
z%f>GtaP9QyNd{5M>7BCEHSdQjdhM}Bc%lHA8&;~%sE6Hpk_}v9I5ytmw4}*5Uyv85
z>-goc5HIzhA5Kdc;aJv5x+}ECOuZa;eq&ypN2h4!l`RuYH*kM%&khy(m3j!@8ELXs
zmSd^VSq7!~FrNOFmp=2Kd-eoq2A)3ZE=KU5n`9O?$n+=aWcd4z@JqW)b-)lx8C&lq
z7Dj&=G*ZYXnSm5)_&2E|Mi^yoWF*r<V>dYov-bj0mfECO+N7UsJFD|MK!mqL%2I6*
z1)+`T;Yb(m<$*)-j`|>#vl-I6i}sL#w0AP&zYGeG(0W;sfR6)P7VA9E>=D@m1X2P$
zDwQ5}rVhrHmKRb{i*J1kR?N7y2h_T0S|4D?&!a-5YiMpHVP7lloT&jEU;KdL3F$!b
z1b3rJGw6Kgeta{&sd7q$O*VVytn*Z51mFVM=G{3pHRT&Ha>1MXN)!TOe@+p<gd_Ka
zy7Y&-e3+gDYY!`#OrKbZ!?Shh16uAyip(hNBe6l{E5Cky@tiQXoYnm?{aUJt?a-Ma
zNiuJ56NcE^EYOKV9{(29G&eUN#6rv_y38gnbZ2dK-`z^+Y+}6SwK-Y&4R7k~Jo!CO
zrd`^W@L^D3qgph7OBeZa0#H{LV}c5+$>>2K3JF{N-YN8O-jPbZib_TaZ&%FpZr@Ij
zQW~}CD!5|fXRl;z-*-dgDD3U=0_1fW5js|0f9gv{$Xdg}`=%h@I$AdH!(BN{up<A4
zQrbBAQtc!&%x&dZ#k0V!GNzn|xQ~mvoyNu^@Kr9>o*6V+$M&a52tp*<6^z&xRNJgz
z<I*~_2ui$mFuhsV>_SB070#1axAub`C17yDz(YOLFp!K+P)p}lk$L}EY471rSp2N>
zx@^aIuKUiy-Gqb$ZHB7-7u7cDj5IP0j^OrY-7YwsjfV%bt~KTjprm*ms?pBv-lCoi
zsG5}y7fQ?jkYUb|UD$AZuk_7fjIg#FM4p?B-}QoZU#tjl?P}(MHb3hjs+X5F&P11M
zbzy)I;V?vgC%<Wnjh*AiOxheO-m#QHxTQ99$Ydnq?1px*O6TU<(N{TcjBn3(XIK4s
zSm<Z0_Q5aHj`cf+DIg$#b3K9>1Yffb)5AU_H`#<wO4|`s6j`GLRui}xmOE>*v_5h(
z&irW>;9=B*8IHZwk;%^fAlr65u)Rsg3#dd`VNw}g?Gu%uxji>)OH68XVj(n4*CvSY
zNWO{tDQ1%{+k1UXNe)~$BQw^Bi#_<FM0P%a4)6IBeJ`}-Di5KUm}`8_-Pwi`{=Sc|
zbIE}eGYAQQ?`17=r{cH#GrZs**F!doE`T9X(S!HoFO1u~_L?3}sv`9%ps%`TvTt}~
z@y&a=LJ7div5}W<QDCW7LX{J;banr}#&iUD26voF*_V9ief5Kr%E#1@_;*2X@rIk2
zm}Snd@@|EX4~HQ3D}faRZXMsv_$*Y<V6JT9EH+h836tQ8*#yLvaz!a(7ZFKEh&96g
z(QHcF$pgZ!miwUCS<OJ$`>pJjh1-9}%0x50xz|6=k06HrR%IRDvBCKT8POv04GzmT
z)U}(d=!*OFbR`1vBIifsCjuWeBj!gafAoK#24fw^p&AO9T><>l)vNeJS+Gs8|M}^L
zpRyJMT1GK(DeK!9g4PD)M-`>}sOVr+z-0D;iDg%Ptk?Ed_x)LqMb$Q-#Ma7z>-R4{
zzsPekH8=2<d%=E@q_~b|U6>+Vc7=h-ooDf}9bj=Wx~6|ix*-03vUjZfeiPS<tiE|e
zN>kO?m%)rzXUA>E>%=^A;rO>#wz0Wh!I`^8e4sM}q3<weR=@X)4tWM#9uDR{^GFyh
z&!~tc001rrCzEvEfR;PIgYQk&u~tEcV}=yma(#n7ZN-umB;yzA$efy5{pDJ|u6hC7
z18Kj2@H~PnPfZrvkwT;*_QqFIX0}naPvV&wq#M5?P*M)Va^_x>iD74)hU8?UKhuX?
z0M^>SjNbn%z=Xb-bc6eB{e|$t&8-~jzf4VyJ*nfW?ez+K<5GWZ59_J}Sx_n?m|q1G
z)urcFSB?knUhQWjyXrA+QwL9o0>OVZ#fvHt-2$NYeQ%paK^0BqmZ}6{x1>ra9P*CG
z-DTA!tkiISSnFi&NOoEUdHpcK!D-xx7Rh?~nHs}er!{31TT-#HQr_@e2Of5s0HDg@
z#_aXZ_-zjedh6nxk{z*P69}Z`h@O`P^JpnXCXMq*?e_-`n&|idlk&>1IkeatbPo~;
zk409H3pwYukLqd?ToKw}Cpnbsm+SihuSwT~Se^i{aeyj99u^^fv85mTX&*!OBWk>S
zA?gl1CjnEI9%og|Xn!N28iB-H3-%^*|B~!At}jKGerK<ICm^aiQ6CuzGY=mU<(1A=
zvp{tk&_9=R3ry6!RdVZ{@H^v6RAr=6=@wpU)WD`Y;dG(IBzD)u{cZw%Naq_<TUzH=
zW3N^4`wAe=-<DH-i(i1UrsN9kW2EiEa<0nrapa;x9b-KNI#1ZXK0XHMQ3CHbiZ@<I
z>J@|n?o)=Z9zgkduHNN0DGz?0-#In=8c{?Yaj}+HDV|v)m2C>-94I&j8b6?)cCP#z
zwb>9`7jbiI{J-tWym<a@<p^|YCfrLMaxTt*|Crj5#a9r3S85%6>FnHro$xGjkaPgg
zJ`nhp*vdv$8PWcBn{b%tXEqYIIsp&P9(z5o)(!Y}teEZ{*KuO?8e$+RwTw+o0}U32
z=-MwdW<g2Ji(4rWMr_eLxR@7)NF28L3fo*AR{)1uIJ?=eK4*6NwXk5tZHinTVoGne
z7uk74C(GWXebxz2{Ca4F9AbJyo>S$IZDgJiC~FG&%CpP4!CtRy@SbO?eX8Hh?vbe?
z)Yn3wr64R=Yt86D%jnYBZaDCox>8>B)cD{AVbr-B6VN9?Nqx_JDLIzmj)x_5JbZR3
zhp`22&BF;$(z6ttS`RpQusOA(z_AB`{BY=(e$~=37RX^0{DBABgYo~g+%__BNP{ok
zd7i&(tyE)Ks1BxFP|m1@dSwQ3T(Sw6eoc)lqc-aESCU6Z30)C;#6C9zbo>$P#5x{^
zC9r4LIBW6dG&}Ca#5EOpnJj#pBN>y1$CfZgk=MaTwF!-g@qDzpeMxvaK>?tn4=<$}
zpK1kg3_Ky&mB=R+ApH!yK{Z?nnY0EP{+sO(`;!R(4~u-he{B4_zv7d1Swt^Kr3F2*
zyj=lge~rvd-I3E9CcC9`D&>sS`I+>T(q_laqPuW}+7B-vP4Ic~^NL}Q+lOb|>Uh10
za5x;s+0&K?fiMbxBklW25~SdhNLgKeK*p_6>A@GG&a_K|zVBYTZYI<?3_|CRuH1;~
zY&-=KlJPl+H7XELF1KA2?|9(GM8$xAqUjY_y3afFMy2+0JWGy*o3iq__4wZ8mC&g^
zHe!P!d~ZsK;bavH6LFeqI6U57{>yn6!Oj0$jFLyX<ua~;eOY9k^j?nqOC<DviNE^n
zoPYltALbdILQ~J||0RI@?=Mg@7eAZhr?=SfHw-&~^;#aNb`FPK4CzR7Q)uIhrT(EG
zx2sPw*iYRGB2vL_1=B##u&KHEK<j5Z2Pdb+6EsCBR$=e?D6v6>r53ZzR5U(W7k-Tl
z#}{Glm1e6HcK6Av&gRSqn4rD21*&5QiC5L>J`8^*ZbJO_pNvlX5nB1|lpP3!)&{Q|
z_<n^y+aNL<oz};Xxx@js{3hxXC4%;|aiwN;@$!D#WsYONcPa}ZK=u!QdY~8zHFYo`
zm6dJ5kFrlUebCf{RR>C;?})y|?m^Y*&K#}7#4lkR0CREbhr4n+V~g0`*`O}OF0^gq
zq`9@NSA@q3kLKy<4x%KTPfCdT`nCJVBd7-U^sPHO&b5#rZdM>nOF0G)nBQ_Ef`~Of
zN@vDHRzx$D8p2{Wk~t_fs7X6Z8QYfjLQ6?C<#qtesyHT8U1ML@<G#YKa^6;sbs$65
z+YNoMS!yl=92d_+1tN|7o^D(Up1m-rAA~*nV9o!yY4KIRTrQHno%1qOW)J)G@o+T2
z+Bw3vqk$!W>pPAb-fBF01z3jli8cW&HY5kchTs)}zV1!C3%QcPi*f4yOL1=QE9U#?
z`RlY=ldXIay^OrM-RO(9f3f60&L^~z9x;`KRz~2rz)HeLc);(Qb3UREHmEEo&LkZl
zH=C1l=g^sW#rMQB8Cp-bJ-eK}Gu_}Wb8Nq`J#({(tp`&d7CSj>0bg2zX<HD5cTB6(
zBW%Xv?AaV!cs}HsS}x*t9)?$wZ~ZY|>3;JYtkBo_*)D|+^5{5V!+Janxi5azRBVJ9
z*p#X0rsz~D35xb1CYM~gKAdanwu8&N2L@fN(hwt$ma8@f4QV?=0u}5Mk=kK`%fH|}
zUro0^&Y*4mMwe*7+w#3njt<l_30+2y<*e=wV4LvV0D0rfwCaT3D;uts_C?}T!Nj7j
zjoP6VI{)eJzyNa-D3!4F4>$5R)ws?qWH;~?FX4}ZZ>%89!@{~~SP3N0RMzl_Yl{>=
zc!0}$!##%Z@PI1%IiRnhm`s78;@3Dnaxw(ZnlQdiNKbnV)NZf$;ynNO=&swANw(b*
zenbi9Y}V$$rD}oPDHksnVokwuH`c~Hu-bwwsn=*ol5xQu0~=i}<Io5_=K<NfS<Lx~
zp8=9ux5)uAjEyHywXCvx4nQkBESTA9?mk8Cb0)6HbYOl3Ud>K=_$TIxcLv>fD&JqE
z+xjPIDkV)z3eX9}&NlURADO%A{W&_qwgXi7x`;7B7K__QGdqZmqjMC}Ge7EUd|p}I
zfIljxzxUnlB}eZ1bFM9v3RJ1Dlh4V%{GM7}+j#CXHA^v|?}eE>9*~_GW<{L|^we}c
zeKuBk(mOtna7^7<E6;;8!JYSrPw`YNUgE>|>ne74Jzkf*>CC3T!CA$jdR2w*Va2kO
z0tt|9H`Z$y>@!Bq=$;S}^xEU?f>C3_@Aj(m&UE#)ki#5kxxLF?fnxaJFjJ}Ck%+0^
z&4-D3+E$v1GW{Li$vyjz79dIR>ODgXjL(ImP83gh5Z)#j--JKfri$)0*tiU{O^45i
z7sEtj?FFZ3VY=(WvWgyjqaQhvuhXA$^|TCp`fPk5%Gi~fZ$xKFQX($atq(BYV9(f>
zf-<Rb<+3~)t|7G5fCfVig#-gO>75-=@r|%5r^za=IU9De!-Ry*rxAIJ#Y$O+Bb?Jp
zrxj#*VJSh4@C0d_Ew3mJi+Pw}$cpjH@gcefUm)eNfC9O1zr4yMzjw@vmU6PE0|RZ3
zYwibaB4SLxrfEq2QMcFaG<#&f_|(hT_ja}Jolti`a8gNZXMfbF)3?`#Vbo#y{Q7=6
zPC6J3uTP?ISOyhD#JTywwlOls&CBAO8=F^1OSfAB@pz)+l6e?^uy74}nQ{AsR>_8P
zrD!UC?0}sm@oE*Crw;aXR*=eObL$nuc@L`9T(;hTQ}ZvHU$t+?H<!r0<!?8bxkF>M
z)lU3L0v6j|LNv)GC`$McM<TY$OYGjBMAa-hS@E2RVw?Ojp=Y63$v*bB+(v`3W3}xp
zd3T(U!d^FDaA)oB0Z*~HZkI0q5KC>!uCjRW^6Hr#LvRb;{D;?Utp5qi$`<l1%=)x1
z9MWs{!ssB)XaCsvT5(dx=~m7q{w{06Owfmh=;%M8IrP!mS9Yn{=xMviXz?yKxMxuK
z7pj1<Cj`a!E9?m;sJ|^Rbg}XDG<6o^Obbq)C~(tJQXu*8Z-~tY+D{nq=17E5O~Xb*
zL$0fio%F+A_IMG>A6GJ=X{N`n;sk=`o1FfY5_?G1_4mFa7IJIEJ!g-uS)EpGExjfW
zk6$T)hpD+HP|}u*8|}HUjY_RYSU|h^wuJcZ4QcT7N>-fH^s=ZQ$cJUS4OF*pIZ-XF
zq@VbVdlA8yymvfAV*&691Av4NBb=i;soIERG1(!Xj>5UzcxtIk_mgq1lkZ0xo4wAv
z(&DFWqPN?ml)Mn<kigkTmN`q6biNctI?ygNn1ORy2z|RX_bW?%x8g@Z#cnj#!ffbN
z*4iGqX<(hyTN2-ayLKgnVkEv4r~PO=Ydo=gdiAXo*6$R%Q1yY^|Ha$%-wzfF|N9<c
z%fH5@Gyra-dS~{!`%tZG)~m81>GLMOD6jjmjg2a}ekJA8_cm#h_=Y=le{W99XrB4b
z(=)!Oho{vmz&S)J%Qjp#q+X!Y>9knfWJbua{3*jxBF>b_T8jkDvi~6EJi4Vw;=KB~
z$I^%^?Vab|MMD_F%ToxJTrnxo<Wxk}2=(maaVaTq@JRcgH;0^)?Ncu}oCxhX+<)$y
z8zcXoSFedr@%ySG4%LIqS|@9<E?;fWLCWiT9l!9lG{%^Mgf}khp~O!rPBqgt6Eley
zvt`MOZstqh2@m85p)UxuTPoVR7E5zOC7kPQc3UG`&UBCc<9d4B_P2f_)hA#W^-8yz
zVq1hI+s3M+)y~=-o7o`<$3e4VIQ8kBhlh9g1qK38C^os5=i3Vj;khUWp7eK~-Dctc
z@=gk}QC)+~>ijY3vr%@3sIeW+!tjsT5U<1+5{*f@e}*88Fy9g%LX|kwU}AQ#rfB#*
zQtEy5YUxb7CD-N;Yjf`KFe?8uO}_U2U)8@Z@nR$>U1eV19LC`HGi%FRoHflE+?LML
z!1XKrz%lm~m-Ww(hBRYnZjO`umP*{4xfj|zu7&}hsg>z|nHU#0%g8Zq(#!T?{%QS$
z?(UoK>kbTO&9744CGvLM?MV~Y#uqVqh$p_%Ywpah$}uUNEMI)oe#$^!$fFu8^5sX0
zSWdh0ya=BLR%J_t_!Yy;66Herc9`>G!K5=Titb~s^FSXTR`%#HyK0_(?s5HC?VRw+
z!Oj}y{c4-BE8HPamqM#fQ*V*qP?%w)vd?hv$6W^Mn?{09i?Pn-gkJ~{6^m{3x$4bD
zEPAdiOqF3?tu~&)`tYit#}#yQBK-Kf4hBA7dV2WE=_@cL8pTo1T1q3=-1J5_6!j&M
z?0(-?E2^<S<{^O-Uhf#y!5?;c{?%}io>Y>dNO{(-!onxjPW9|rD0Ck#R8bwIr-SX*
zhw0~mMcFIVK`?}0B@sp%^W`p_{_f6=<*W3cJo+9nKRoTu=e%{||M;ilKmGs65dIs5
z{y&cL|CL@LSy$5<Kc~7pVO*cSb~jSpya-=v7691ehFj|acO*>N|IMW0X3v#cG^KAb
zJ-gd3?L6^~`sLZ(s7L=apRAt7DHi;?q>v;-9hsjBD`0gw6+9-I|GCcBB%vOh>Ea0I
zkgRIViIEF>efE0VjgSGZ(abaKHB8PXSzGQugqJ3sJzL3$x9Cwoo1{<0cq7x7jt8ZO
zX1CuiTMHzGnndfJsuIq7P4U)BgXx#t^ta}J>YE3dbA%gZFSGwK7At+t2#oycGSQvn
z%Sit5hCyCP@LjZpkq~!FK59q7i)-b+r~a08W>RSKvxdyDYPrp1>E-2R|E4%`%u?<^
zvMoFNcSVVpTqgWaoz>{Qnuc5=9XTfa^A)FOR|NuZDoAISTd<Vq3zZ#;q}_Cf6!yzn
zr&WXv&e=UEi;h1Vlg04n!H4F)o1@c@1YC0x{MuX|?=qYh_^|%+*zyalEv+|<)2J{;
z#__^xsc3$Sjg%r-G;^jr?%6nf&3jS*@EcETFt>h+lew7Tj}~JiOLAFJ{eEdw(v+WW
z(Ibrhkk-#!UVaz&xTdT^lnUZw3ujdBcEb3h!%umlyUVpB#mf3Xwv4CGEi|cTa!Yci
z{b#m+_(g&nw`dc?#;^J#LdVUY=feh85k&DRe+NA#2-=GwyS%g^qM#tP8HHG&cMG~v
z!VU6A#Vj{g%7jimx0`O=TK(QH`?ShQzV-WOD-8P_zts7ZHQEPnIB^ZhwC+Dv6t@3}
zqz`<lnX1aN>T{%?_m)mKmv$9Dj?jD{;68qZsmCKlA3gtYDh<#j=lXJy^)Azm<%o1s
z1?I{Q58+Y%rXvG*GDom|#(T#|LfVSGYHchmFePG1A_mvXY0?>4smU7ovnpBsXfQqZ
zM_gWA*`7wP;+8*&TlDBbq4^Cn3rA5av3yQ33*t2Q`Ij9=vsU2ZQseVStsssK-e#Ci
zCH$5!yprbr?~tzP>ZF@&ik<j@i@7$`S2229t#}=lLSxD0dlhe|Hh0U-n^xG~eoVsV
zO{XJ!UC$-gy{P7aSFb*lkT1zW{a$#zJ!?NHuh!5d;yo&+<)zx^aR7K4qFulyb(v$B
zBnbSfsmUQfFodSV((?1VP+4Y;D&^zmBfzt0H%+@klRLb1IvUXaYm#w7(M{h6v^^7q
zrKIKFl7&2m72duJhs6$FbiJVUrn<24W3^n+tE>CP(X-O(jU@j!89V*W>Ka{?U$Vz?
zA7RC`_K^k5A}9}n^1Vw=tC?fxt>cD6cRFpy<0HIv_{f=3$#b#KzLyPapdw+JRnj{8
z5;qU%ifgj?b6tC1>e?n9ot<$9(HA%i>1T@5Ug6dHMin^WzW3`%PByPzPyM0}9npb~
zvWLDn$rakbu}Ebr?{Vu=O*d9Ki`wAa%rI?6zLJYE7dBExGKK0>93MKm-iYpab0B5v
z<nH!zvDR<Jr#z&l|9IMjA2IS3fLA3yzakKID7XIMOK$6my?)RQHC_{>8m-*|$FYiZ
z&MOUvPpMNSlP<xWIoH-+vuIr39F49hsdv9J`&YH?%XPOI5%s&tj)C{uH%HSBihz1T
zBcgFIxAyx!Qt@^l%4faQewe)Klg<_WkfbInOh&B<{Fw*dFZ3>Gx!B+FC^7M~k9T15
zWvz+#t__$^KJb{Kjr4j3lY-Xj^sKQnHy&Fy+opNY)hsHw!fK`Gl-!c<6z53RhN^wo
zreUbX_B%J(Ra}O+^HqX5ApIsp*^;t&`~LbPrk297;teUMxf?MTP+H<bn{26rn30=E
zp=Uxr4$DmN*y6mues?@3ldnBuv7>H|VqSgzOK)`>n$-lV%5?4Tb7=qhATAe|{H~wR
zgADqOob4u+hxHbeA7q8Mjzz*pG!0he$v>eq^uK}&l7&jRi8)X{gRfxgWIyHm#Y<^Z
zLY0BSxmv2K2BOJ!m{)SB`)fW!>k%`hypEEUw?6(oZ)>={pC~&Z&+$bBuDUtH5j`~(
zIVhF+vreN17WHYvv_ERH`mg^2mu|;&)15n};$rn@6J_~qFO7}$VeSHNdpOH_i<?^K
zCsb>GZ7GUO@oGYqLA%X*{=UjLf|*F?+L|Vzev0g|pR{3fq=<KZH~jf}e=SM&ZL-we
z40GcMn23ojG>&*LL;hfIXqXZy_O$;{Zc(V?r(rm7i2tgn68YIK|Ehcw`JRFEfgV$_
zo&>D(Y1uuyH=TB$S_i~$7>!)_W<pi+8UIhTq;w~1S=eVKNFJ9Rb+?6#%6$=E7oYQd
zZOl123sNDnfziven<T0{@iD0Q@~iPF6$0FI3+i|25?;Pih1yILVM})~Irpy0xz7S=
z)ukBa#P-bdiyCz@`Q2_ga_Gf&zG7)d&#@FR@Se(H{e|A9Eq{aArdHjV`8R5B6fgBw
z7f8g~nreuaj;OluY6jl3*U`7Q!yA>|R2p-RS=pmNludJgpSM6;aQpRp`W$B=jmbo6
zi|D*>54$?aa9Wv4@lx|kik6M9SiVLGQ)jEFv)IGmRyCr>3tJ<)+aTRpvj2_+D9Drf
zSY!mXlRtN@=V8caK3I~gY$vDM_X?3XlNYDPmOh*Oo(jH)Mfy&W&i^zOzIVaoOmG)p
zl!+BSo^zvfZ2c18v%}L){yL$DUvq{343Y(Y3iPyiEz7p?^6o#^z{WF^ijRMO8BE{2
z*Qjyw@}U}5FFNFV5zN+NGLkR$A;UYFo7dvZe(YVK`QsE&x-wBEBpKWmEXuZAU3UYw
zs!N59oqBvtZ+95xieNT1v+jx?YXz88755}10?l5b{&Wd#iSzZuNrZJ*PDtgsCU}zO
zudVYsa!7mT&wK`~J@>&qm$70S1A|*pvtMK!07EW|3z}>Ozgd5rFvLoS^N6!gxoSB)
z50JHhF1P0CpNQ&bs*ZB|R@|j4AGm%th%fW8IL#BPRTA@zkg#9!Q71{{)|~|V*IUb$
zzDRrele5)_M;D`Gbv3JX;1$li;QNZ*yEv*HmXEw-$d8kvDs$dPBW7LKGxUeQy%|=b
zwj?;-LOg(NXUrP&DX#Js^E}=ZChnxnk@cZU##$rzDi9$n&Fz}&GWy;|L7<t69i*$%
zB8qBA!ujHT0Vuln@11f7S>6nG_O|Ojpf7|00o29UHe<UXY(16XuUR;KU%y0-pE02B
zT%)OJ)7kaN=gP={YEvS6EKkgg-?LuBS1Ha?AKgg4pIUAI`lWqJ7iFj|hKAtAI5hI8
z*5_05l}B?*dM1;$+*btBZajvjFkNw#=D4!(lW@+crM<YN=`(7+opFNdN2zAIQVPal
zSfe!~3*#e{ZZ+_5AB|T|fkG%i^3PQ-ubVMng}pTcLw`)Yc^#nOk2t9COcIZ*^;o<(
zB#V`brkUfFPEK7*ZA$cP^jEnAo0-B$u3TKujQpcrOZ6soKJe;2HJ&x^{N|<Sn@LoY
z``m^k)aIn8_-U=d3S6po{Gd2E-cwKBq9vTZxfgd`1^q$R(=kHuS88goz2uG0ssX?1
zo=i74aETi2W^dSLgb0qm7pTb0GVFRTs)~Qfg_rvibLQ3E?%OVg(!U9hev2Vz#{|c#
zqL0DPm4i1sJk3aPht<c|*|TceG<FjuhSb6S^GT8pRi|+)qcLaqz02o<OVpqJxWC5y
z{mZZ?qXiWHyrWl3(E40#RnPnT9(#)pE|J+Xa5Xjf*SoGTOUlIRR|3@V?gJzj8`tMY
z*9U(cr^T`sNJz+C_(#jZ$FVt8f|dB*MYYv?Y4WvP=8qdnM#jqkCSLz&&Lp3?M*=2&
z_pHtq+GbnJZqRDyD%?@!(GrehC(aEgx+v8?^5MAF<DwaO{!EaAZ0f=H<$PaOMV1*W
z)%P8~<c%jrCHJ-6NH%K+MyYS}WE+*#3WpFl+XrF4zKFIJc~|Q@=<a8^wEg_E<5}v*
zad^*URG#Y;FVaD%n2bTv;p&xz`zMc<U+Ukx_VFpFZHl{5=(@K7HIaV^{KD8y<X7$R
z?b+KGf&`b(BpRf@Uxk)^iBUZRRA32CX0yT7o8b!5+zXtF@tG!aR$U1_2dEl-r$e7*
zY8sy7NXN<K?Z5W^5UO104Ed^w7hBoJ4NZgON#`EBqB~3LOIxE&{R*CI%`qrBt<f^A
zUizE$_S&)NqB~kc0r9$@+?6r<2wkDw_!;Gq*6eN5`-~^v3}JiuvRpr8A|#NmCYMhF
zEzU}izeiP3a|*8b<yza5bdkQqs~uSY{e7EpHg`x(|Hk+4a|+u}`sGSM!X||W=3rNZ
zNIJb-JOsikt4-BF6$Z$0wJgE|rEuZA;Yl?OXw+*xc^!SEBeQ-+&i!cmS><BO7JE-p
z=qowK&qd6!g-JU#q>*NU@~ryys_4qVw+n->u7ti)GU!X9vIn<KX}DHBHr~vvFluV}
zolrsUzQN_<{L3*;KXt3LI~_Nr%x@YyGP28a%^wd$-lEGI;JSI!IN4!Z*`3|m_6MWJ
z-Qyn}%_|I!ocMH>i|Mwaha%_XUx#B-q{R3pD&*$(=9Pl;adZPwR|e8&e-Z>mH8YQu
zdv89F9>L1#O@4g(TP|`}h6DZPnWoFLWN@RTK&*}uaW!{7u#i#Id?D0m@S&`1+VbBO
z^wH7dGe%I00nfV?#;UHrq%99B=y49d^nb&ya#V1&eM*2pAX`k=FNfUHTMnmd?xcri
zYBUt!Ps6T4@L|tbQtt&RQ};D5Y`8)lOtqZ`8d_G-d+8*qy3`&69c|w+Z*29fFV_hO
zj|~m9=pM#l0lwA)1_qaw%j|Wz`Se|j;w<8#%cmD>|7F#d@=pB!ri)J{=kHp?n(z<4
zV!5ZguiX2%?<c4{QKt87?QzPW$kwyBR6?2-+O2AREzr6Ad8v59_?bTfLp2rj)2(#H
zdUZqr4L6Yeb07VMe$l*WUpo9pn#~v|_jFqZ&0t$WYAl%q{b9ZI)OvxdO!&2DV{fCL
z^RxB5^;^6pcgDGB(nRsY{<7(4hE!vSSjK+4hQ}4JG#To;QJ%myNWh&X-G^eDo8Jop
zs`5oA{8M{Ab7lN`9(ZRtr0Y4<h0T}0*0t`b7rKv1(xz8=F4l^%`E-lmD`tl}Y4DC?
zxZe1@JQ<oXa>Q3*L-G6zF{Z*js}k$-)t_^PJZY?b1W8;5imC7Dk@UPQ((EkC!0WDY
zdp|k^#q!K_v@q<K9px@6#?=oQYF4Fv<cR&-9ILy}E1TP&Lr+(qs*}upk2()m@jgp7
zK-h~hyNr3{nsD}yA?ocpM+MT8ya{8K=eR131*)Fa{Cw}h6rFY}txk5`mC2(|Al=ff
z>zWrP99OM>S4MD6=Lyw$*`s)TK>!~d0y<9djG*EW=4)}Pg3DX*XV2cQMmV3kVB+@^
zagnF~k3oX~l~VRou+XY^ttve0)%E?r->B{`QAlw}{no9l^T&Cx9kR6W&xu=Hi{wIY
zx%X*%CZ*K6P6)1L(Z#8cycs~+I7$i-Z-?z&m`Zf8ZDnl&h+nr*Ji~V*T4;S_3_0Ce
zen5|r)}t5Uz7`j%tASOxU+g{nnvG4iU$gBc6)cu`Zcp_our5~<YDRuDYx-l5T&_od
z$fL>{4_V$QQ1K2VezurB7fHp%jH*>vog0UB-#?i+nv#pZuU_zU)Cl`8`i`07C)LYq
zuqn6cuTpDL9ivud>Cw7ZYwjkmqjoqrZ-3oQZ}Yvoe`4*=<n}?$9|qNF+u`p}R$QtO
zbF>{!(z3;;MaFqavw;rO7QzIhD7U%f+U!)z%<D|)d82A;Uv?!v2wQD#2Q?A~hu;OC
zw{rEX$JqaPj49VHz)GBlZ|Aq<vStvBje-_+biZuxSeiTbFEY5Qvkba^N9~>Ypi3vB
z`uTD1+09L=n|)HgtFcSuoay(vxh!{6YCJ(WYn~ZTvNWwXTm-_)o#gQK>)m|%$y*m&
zFq{e_YNjf(R(GXzbZ8T+t2}C}yEA=nS@wILa(tL5=7*nflMmHL!q)R9;PX&;KE#+R
zAZ;+^t;Or5$`2rqI06tR3p?UgcGAmw{F=kO1vNZft3GU~7q5aQuD7Hd79530Erv|H
zRz{%VBPK08dO>-&WU{4XWsc>{OwR!sOG+)e%BTwV$r97uDGF0YVu4)2uDA|!JMNw$
z03PUJ;#km*u|yHaLg`$C&mP*U#jjBkU0vG@7(P9vYn{8r-azMH$7scNaDrsjaCrC#
zt^JS$86G${SV46Fq$4ZRU#va*=~C8`xPAla9?or$;Th6pP<gpm$rkiig3=-XNRK)8
zv^nzpKAtl8Bioj@_f%IZ_b%8}mbw3fM#R(91AfUm+lfit;04sEj~LGzi(wkisE)eA
zu9!}3*cm~c&|z_a<2uiQE8{X&(JRbj6)usbg;hLVr5>Gb&h1^-B@HfP>BMNoCpV(;
z<&*rbJbF$fD%y0_+Jm2+`wy%cUhTuqD?9UNYrnTNwb6XQU!dJ-6^A2oHgV}zH(~6O
zUmym#0%udd2a=l3;7AMnW2IZ-5sRMV83LMUF;}(g@1p+1=tW<fn6rDil38u5e{dp*
zWMN(UaKnr=u4ME-TKnp#DBpJ70TDz*1f&I&6zOhIq#Fil7&@g(8bm;)q`SMj8B##H
z8wL;o$&na3&O7{m-?zX0?X%BW>+JIvYq937=Y8(`zV7R~p65MMqdv1;lp#SrZ~kG>
zGcEvvFy>i2RTNN<oI6xDu9=zG?}j(}l{=DUZw_+3H<syFfmveN1ps%8pC*4I92hyq
z{@iJYl8l`FHumg8XlQuk@Vf{wf|HV6-qghT(W`nDonPEh@7sc>^G0FG<yb<Z>mH|$
zuB{EBpAeP)f@cj8`R_k%^6o>(CxR3C;4Qpmr^rgC_+7q)C3kroQiZs`h|-jp(F{-!
z@<d^&kssF9fIdJi=6EBchQ*OXO7ME)N1kx>T)`cDJm45@j=~jv&97v3;komNXYh7!
zT>TE~SUxr+r5^H;b`%OV7C=1l$w}WDP<sT%Oihm+<`IjR4YF=MxC1+lA_*!orIDa=
z-!YD6`qys8nB+8R5vEO%d^&M1>G?exvc7Dp>0|&Ex9eeH!h^U#eWG_uMy~}Vj;OEw
zWRBl8;rvI|dycXbBy@~ShS^#{E0sQWdxCNkb0l67<lVz{%3&q+&LO2XiIh;eI_%J~
z9oe=zem#dvzUUn#WkNiC2&w6dNFP9xFrf<~DB)^5Jk5V6_10NbW?0EnFGt^9WM3W^
zz&(22<tiKFZXx(nJVi{ks5d*2;~3O$6eqt#@`)0IjaS-4-XMmjREToO?G;DQViLbA
zR*EbHoch<^%<OiJc`>>OMR^10?fzzVgkBv}j;1)_gRtY_fZ5{9>ooNwp>?hrn`D<|
z*a_Xh6nx^`s^OcgIT<aKMO%1diMVqqhEBIrf97>#J-OX@Gq0vcITC_ja&60*Q+4ww
zi`xWEM|n~HI|Pz}kA4gNKVhIwH?Pk;5S#@zya}mF+@_47s2s--ND9a^pZ}w^&d~5S
zO3JEZz<R?)dPWiOZfe73QD(kEbDuHpWIb0)R)(fn$$9^Y*(sitZ`%<vRawXG5de3C
z%TXb?kuitEDMCEwf3hPE0gE}v%YF-wwZ$>0udk0OnCBLtqfr_>5LR*2iT$YF+L|%H
z>RF@D3C^>vTenS#HqILjyMo>aSFHTn+ypE`pTyF;=kWBRp+Uo=fnUAj84Aw7A4&xs
zsEGmqnH(W*OG~7xCwm)WpTX3wIOENi%G-5K&^Q9#NU7keyE?#QvkN5B?|I^>)0E?&
zZD(yB*6ktJ@)Svb%T2W1&IWQTDbU7rrSZFmiZ_-@dYIaNg1<Lt{0ONs2(^y9S6Pq-
zdKra{WU?y)4Mtx|VKxA#Cg#HH8GU*6H=GN`(>IQ3z3Enky~L`R5ICeDMx(RWXMI7u
z_)JUX8Xs+kn)M#Q*u?5>blCqSt(IRleQ3o3FgT*7DNJ(DLVn-@3c<$fUk;5EMN+Y$
zpLL%SX7&<><h)?}QHc+bkWwsEHB<4Uzgq$+D^Th#q#U3CXp!=>mEE$F*)04p;IOCQ
zU5~938)hY<&?-L$Rl{GA1ieZSP{jR`pW~)-hoAN8x}=NPw)?#jAxMN(<>AP5d<3S6
z#gv0O|8Shf$mm4ZlvL5X-ipPdiOWFq=iVLBOrgS)ld(fBy2i)VDk`b1{Ib~LZ2<CI
zBTX?&>^gQDlxDWC=z7M$L#dG3$R|M)XRAd=MTKg>Xy&`7U?o-`d@mGtZXiMFGu=w-
zr-|0UI6^@dbiS2VV=W&f921!;t-9y-rxIulBga3#R`T0_8Y0g-Tw{!@CB`+L+}_#d
z8Sq&?zO^ILS*?ce_xt^fjCPJn2dp=PA>ApbW0v}W(S_f6^|>?qg4Ib=99Vs#1M|T$
z^hJ1SLin%}Lq!a|8gE&qz*`4Wyzd;@DwdIpnMjf3dsY^bjq+(?-U!E<{krvlFyHo`
zgH34c`|#G}d#^20F)tLjn6*+0jc~R4)nLz&c<Mjo4*<wba8HL67k<g3BZ3;wJ(eVl
z^-Es6|JdW+++%TZlP#5TMlBA>@?wB-r%M{X$l3sC?Fl{T%lsIATzjXM3_ti&uC<Wl
zIVq_(D9b92uc)HH09N~f=lT)Sl+C%oe##CTQRM=pa5v{ubS*TXme>CL(!6ZKk8R^3
zp60l__8u%arnJHg(2&s$^nbz;Kdlfe`Jd;5!gMW9S6GYIE2^V9#dokq<Zw;PL#7J?
z&2ZBP1FTwqRUPFWnR+3ZHyj???}gd&H0mz8w&G6B&*-+dr96OI{bK#N!GXftI{-5<
zrEc-<Y?#=#(h&9SM+q08E<c^pu_c_E7<5aa|9L6vq-V+^XeXdVqpL11jt3rR40(NJ
z92SN)A=hl38yOlDt@+Z<BAaE8sU46ok_sg8U|I8Be2iE_F76gCku(Qk5Ok#c1FhjT
zq-o_0SK3gU(r;DM>tS8;tejcMfSba;cXzxxE6hzSZ}jucj3)Znb5!25_&8i(@!4(x
z??^E41q1kIU<UR@(z&7niKvSfL<fJz7S__5PBiGLaGr#9M$~&@57{aiXlnWVIY&yS
z+$0^K$KjJhNEsr+X0vVRr81vKvhwz}@XP>@=eEk+=Y&Z0Wi6*q^`FCNd%kz}(=(jb
ztRXxbR*G|MCFF^WXBzOTADyW7AN=ed@GE~T$Qh=z5f|2f9oC|C#L;>I4W$t5$h&^?
z&_OB*ZSm?*oEx$oA@rxV!bdbYrHyS^7*xbi%pv6%GF>gOk01+0XN!hewbt;C$9tED
zOqtr*Gb=06Zk7JW{X*RD*~Ddzoc)?sC46FK9i9_3cURr<)Pd?wr&Ms1fEO^?BPliL
zTWTcnvBViD|3=X;`_8@IiTO|TK9!-}^iCN+8F*B>f`|Tyusl`=IS^I29GPjM*hwlB
zZ?pyB&&el^xB%@AxvYoj>JfH=OT{`DO)1uA7XQyQ!}`Mr+paXTvG=QA-}zAz8#+JQ
zWe+KpjlnF}l))uNbja*DapI>B20pB7D&pT3>c|^5h{8R4T3b!h*cFgUP^S!Wg0kR7
z=E}&FGkLA%a72BUlr+Jwp5a#B=avk{rz?^ndKC5pfNVCQ{rzG+9wQ_&ZYs<RyHDIG
zYdi6H2;Ki&UVmUYD|ke|0dN>Ts3YI$JCQR*-0<3m2U;0j`lSf{J;JN9($Ck@=PQx6
z7&hv$fWDbT?|W7%tWnSQzS17W>~wxTjs5dge>;WZ>zPydX2e$!Ua7IqKg57^OW4{e
zn^p)FE2dW!QC3rXx<psE*uixj((|y;nF1XU6qOiPUww*kvtyHVkn_+3ScIMEkb)r#
zFAm;U_Rcir(Y5tx=OG)Ytzd)1+1WlUPHheOb1~z5+K!>)CdXxDGhV!u7~Pv^`;_>*
z3Za4dExx8*9Uu|CfDiT=N8^ZIyrk4^LTd+BVI6cbnkS^B3_4mI*?D<A^Fz|Z{CK#1
zHP~c4CV*p>UQ|?c;RVgp3#PPw92~C@B7Od@6^!`2_m@Cv6#}?}MDi^?X&Fa!W37VC
zKqiIT8)KqrA?{e^$DSL%*zwluJoxUGUi}h6wxLu8p>c#Mz2p^F3*LzzKZgHNa)J~b
zkS}7O|29K4Pvl=_mws;BIB<-Da(Z6i^H6N(MXC$QMN;Vi*>zs1>HcZLkF2aDU<p{{
zr@u?w`<C5i`8PwRX%B7lKAKLpY4(#<(%$#)I9ms8r$5)EbO%VD{E}S`9CThV`+8I8
zr}`jfw18sj0YUUF_{*L+CPLgVS%3H5514vu-CTZ|{-cJe(#5d@#9MKd%257RX~>)K
zRL$^WQI2JGh*<U!q0ejnGHH*yR(G)RUx>TC^|0~#=94w+CFvX}M3GNup_sq-v8fuR
zmRF=t7PA^x$i2$;&;;X1qEUgKO+*2z8N=-yB=<txgwFXzGN5nE&&C;=f84oYn7_Zj
zV$3gC94A4CK>Mv&DShnmb#D1Xbkc@S+!r@{K_;T=Tr!}3=S3n%ghXpdf5x%jN`MbV
z83{-*ljgo|{l1Dpm2($!StBjVugv?KP!boeF~RCkt8{)^4Ab~7h7{MCv;o>hZYK|6
zS&8Hacp#d$sTQsL3>#&SUSBza2$Hkm8=%psmSyn?K!Ziok$$DZM1_}`<Mx8&KlS$l
z2b}iNtatsr>Y+njAvtTvZ<QvJx&0aqpezwYr2m#84mM_EO7gCMNi(;pH33v2Y-e<b
zf+tRjcbtMhl2cGHDw&Zermy2GLaOM5;P9V<3*ya@iQ{>@R<KaS4b7WFZls{_slMQZ
zZMbo$DL&&L>K1&J9!d+|T?0YGIbbz}>O8K8Vsd#LlMkPDtV|A>^l)5A8{#6D`bGcE
z6buc@M*Wj1cy{2>aTQQ_C9`9(_OgDcEnB@*xl@q$;XjnJG_12%;OofkP0?mUumugz
z%EkvRY;q+_6LoPKB7n50`=L?rFEm2YUo?Um|7)bsz`JNXUfvMxMV@Z|v<XmQZL(GN
zG<4ad+)-NgxJc#%gVSYNge$W<R#kp7w7pWS8;Wbq4hSP*kI;vG%R%dCQX>6J-FOE1
zh{!$QcDuSsk?;bwqGa0uxvMi({B+gEuG5+#Bz^FMr<n%}U6chG^QDpcrEmvjL~At{
z3FJ+}rNGju6lrf3lBA`$@?pK#DzMvE$8R67z;)QI{L$L;TWq_{PBaE8uptINCWMoc
z0+UDb%dL~I{Ksbie%QtCzYr`q<~>R^-r=8BvD*#K@%9I*B%};(o__w48RCR;`_rkL
z$upG|T_^O2Ljn>>d~%d-%H2XK2oX2=qB<^!^-rba?EXG*zj6SyuzAmk&tP|{C2K58
z)e<v4txj~)4YzTtJgkUz?y<m+V>}xk=X$a{tq<tBnDY?`ZEXV8a})kFSJn?BW6eXh
zbT}gDgM(BaH5_U6-p@Uf0o7qhU*-kC$S!2n4M?*=Yns(bWg>HXTs#n^K0<rbOan<E
z(l4=<EI6esQ8cXw1E0cWJn`B4^e+z12gx;RD!*4L3cA=}KheP;7a^=(NJ~r$G5gLC
zHQqQ2FQoPL&GBS<$S|k*sTbZWxRT8xGc7oMgkE3dRz=120vJm*u(~Ntnz1d0RI2L@
z6*1RoqDr=PDB6OiVen0Xl+*p+!Yd}G=HFz=LJ<HL^)wKCLV&N?KE$fzhZy%+9#FJL
z7%Szo-n!V14V7|ahfyz)vr<K^Ij6(4kR*ll6OEFbA1UfE0~&%rM)gFnBr$o2ep$=p
z743V#sQ~&XNfG)&+^Sy7CvxF0l0woZ61O+j+N=UoiV;6#!R*$iBzX;pD{%AyK7|T)
zEeVvowP3)rEIvUmRt%u)7Ayd%en?9u6aJBZT=RPm9`z6)zKqYd`QQ+FVKxyr_rxZc
zQuTfx@Ux>k2xAv8y0Z45ee~C0z5b=CbK3Q+J25;59%1<`<PT3oD4h+j2b63VCrPF;
zvI1tcsFaKSpy9j_zjboi=$tz9V=(DZq=7;Ag_W(A?N@;fk@n;hMb0nSNVuq8&MI5X
z$_NaNx~pqd;bqgg$o5(newbG!EKVI_%<I+j!?wayMbGy+6~_$Mc=Kz3oWLb<l%=|o
z^Cd`R#taKRA$9q-Kr9?@QRCRe1d#}qOfr^wvUjI5e~DgxYaQL2!VV2V?-ffli}H@P
zzYRLgzvDg5x;Y4!opWo2Nwm+^c9Mpw62CejxsH4XromsL=iV3hLNEOGNn4#d6RPH?
zMzGA>J2XegHY_Yr?0T3Q3nwhVXBp(Mh7Mwar|64Ula`yTA2SVgL~rNx6y`$eYAznq
zk9xC`nPM|b=oX)_lll<|_crW#;7VybB=(&Qi}QvM;A+js9J4&J40{;l8%aj(dLmEP
z`Zp4M4VRU?0<8=2Cx@JNQhx>j6#18M7XJtFb}Q?;aGqVuvSs4Aqql!Qka>|#f+=0=
zmaLuUI3<yiogN^zzf0Nj3X9bKeU|IZ#FX8MdEetVylg}6nq19jx>x@+0HJ;Zp6XB+
z>ds4ou*v#an`ym0W5QWn;c=yS=n}84u!rptaeg))!u%AKad*k<H+)Sr)wPvap0`Z8
zikXUPObzx;!Wg>QDPw3UkZl-9`<`pyiO@7W%-h^jKrjFvnLRsaHp#bw_+(pLYvUT=
zyjng_4JnqAM2{9%nKAIm6&^bDL!%{Ky-qF>r=Gh3%vQh`+0gjcCl3hl&dt0DWwzw&
z%zJjeGHm_$kiw{GIjws(QMC4=DV+u$q1J1}q-q>>mi!QiBt!E+?24)C@@ibg$x%a*
ziP}GJ1XK7V%WE&513BC&-Bb+swsyWZcxbu`-RWA`&c4_~m~nh}z+&x}LbH~>p$D+&
zYoQFfdb%3p4ZN9$NW-uL&?k!T5?lkO5rs5I()qffCc8DN>~C9M3bvsFamP&-hy*v|
z2X5dbdrVKgd+RXQ78y9qU<-O@F`6iVr@UJHHx$@+A3Wh}5wK2Cnx@{l?3Lf*`S$Cz
z3~nuF=!m}dV|~^e#u?(`l&yPsW>vzRruqB!%+#f62~M0~2W?yGjj*MWgQ!-~aS=je
z-DV>g2TLC$T5@UU=Er4OR$u@NrIfW7KAJoT^sE-SFTlgWV3`{G(e)JzH4=Yzj%S9(
zfzV`LB`)oL(nr}dVR!+Z_RC;|sp10JmN^iW?Y5JLlHG|d<qaW6fS%3t4<KoA#vTq}
z$?pK>G<0<27|0xI16CS<05_ii29!03K7qE)J5j#Zh;O=3;^ud9IYyO<eR?3g0hjy|
zkdio_nA`sC3Jeqpp%wgZc#(cG8Y(Z|fM3)mQIF*cf7I+tS1OB3$}+I(nYe)}L=d)d
z>-+#G2L?LkJn5=$2T5bJ1r>)RR^`MIU8!5wR-52oSrwnVPYX;2cnKtGg{ljT;uFvQ
ziOpxSLnPwZXrV_vih4R^E_08m3(qiQe!OQYyS6O1&SoyAH>ZP9hFGV|nF0G1B-YFW
zTFU!y>O^CtB8uXABKk&H=B5;jSVC2GJlW6c7Vu<5bl{8^BXlO;cRQnng!%T0P5`Ei
z3?y{z)9S_%MLHc#{O>Fm{Fd{Wb_P$RI^2_zN5IOXZ{EDYczjBZy8nA-bt7)8XT*Kc
z|99)+{=h~HfQygy!u5ODiMj0;X5R0O==RJ*NV5Th#S^WccWgn3{y|rSWGw%n*C#jB
z<%>PEfPql7+Psi^EIZbe-iYKv(48qJg6W^IIP9Fw|HuQgW5ukx<9T?Jl08Cj@L~bL
zvm#<6sJqtt8GqsBYknkNrdx7cl0SYCRea~X<bM(KTN@wdyzQOMj5M~brQb>H7x=a`
zBsR70<_f~M1^__))rC2`wM+iny?|$exJxhqJsY3qZ;8?f;d_7n#TEu=Mbg6wdYyuL
z0HkfQch$)+HT138s58=czW<VOr`=Mq$kir48zx_r*lw9<t~q=G5*IyUjeY8EQJ@jW
zcPDQB#`dwdOv8uia1pfxjMSzY1Si&@qdUZSCpi`tM15|59$qEaP@LADP!lw4Zz&#H
z1E2ktg%e4uxQ-1>8+@@Z`-}7~um4SFmePAb)cv1iX7(_wF^;id>Bke&0A~Ib>Mrx4
zqruL0Gykck90BRf_sQd)0?jaJUI?n(-W%h5>MP|h{~HZAQa2Y#1re44>Z|E~2mUn1
z>ztz|Bd1@(k6tj#%%fH$#br=0(nJD>c8FtVUC5ol87_bqjBGW2WE~@}Wmrk@ald5W
zog`QE)rD@|+b59{;zU(n{!-ok-8FmHc>Sv^_m3acGey&G-5%c3Excgj17@WxTfrvJ
zm(S9+fEFsJ-jnt-=3Wge5bQH|rl)^h>4;o`87Z*^D8JmLCy?bfNCXR^R_9-LhyXgu
zaVxitUzPmfz+(ykm*2XWmD_>b#@vkV(3A2X)s7^w`V6#rAH`oCoVNAVT~<F$|AEJS
z)-g$B#F7xgeeW+Z%lD&)PF(=5mNszSoHbqw*EYV+>SfU$L(W3z$-Iwio0)<a;B_*|
z91na?1t#6>WMw&Azc-fMKnt<{HL&U17Z_1Zl=#_1`7XME2zvU|`IB$bC6mJ9X7&cb
zQ=ku(b~_hrz<D$ZNJbn^t;XL*e|is1`>+~oF&|5uTIwl^k7olybfMQ3SxXyCcCU4o
zBz=8Eu_yHKvB&IQ>x0)?*UOF#RLi^6d0ePxhyKP_bDPg~Nmbgug1^L>reO89^%g>s
zSw$i8bLE*ry0W;Ox0qofA%lx(!#lKS0m)(UNJ6=B5De(zpt=kC2h&p87>k<>jLm}q
z8&{J9tl3CfS6Uf1I<xo&Tjv3~hSY`2qw!^xP-ei>umLDLkDZXF5s@puS2W_|0JC87
zigG!RvLLl;^?AOnZzLzZ^$<;cyfr~>%paYby>j2Ld+qFxgIS*4h6OLNdYpCkY0Ijz
zV9n@XQsmz>L~>q+io4dpX)b-R*UdDL|Anz$F`J*P2XDfCuxVSVIIErkj+w}KAkY>3
z$WRKTzZuRKy;nQh3XR8j{tv=9uKJEIj-{#8ll1Q0u(XN&T+s4{Cg7`kUB5gRyp58K
zdhv!h6nAyBpls_VHYT@r*Y14EeL%=SqMPX4@hq0Tt%XmNqwNE=>89^cZK!SXXX5=8
zSIz!M{pi1krQ717f<Hcf_z<>=26!hyvtQlEBqqIrFBK)HUsc6eAjjaeJSTLadY2{-
z4aKh%96{lG#g3wW5NP7|>FmrH_0b`vFEkyj!ugfqmRQ!A9y9V$lY~=;;F<0MrYjRC
z-Tk}F;Za!wm^F2&a9N8=sDRPl#oK?igd%1l0L(`D^_vluoAdO;;^j$IBl6N~<n)GA
zPEOMu0i&EuztYY=yj#tZEJ@{kdp+CGxo3j8dwpu0%@;;2ZZeJf7w<ek743oQ>_mAv
zvDa1iNDyDQewuj}Xz*v>C>&XRWX{CH$hJN02uB*sdK|hGrLL=JQol+I0C}Dx*Q-vr
zHum<zfyrblX&K@w=7zpC!K_MakWyzp?6LlrrUK^1k;6cGbpENMg&!Ue!4BV?gb_QT
z-J3iqAmt^RA&n=k1snnQX5iT#+NmXG<<|W4R$1^)<*}a`(QZuZ*m`_>#bLmgC#Ggo
z^;BpHP87oqzsko0uEz0$M(ts*Wx3+o(C2&2G+uF{f`6=Cb>f&kjjZJr>>Oz!mpW;}
zH^Q#I6S{T4&b%BNQNzKdT*0j0@cV24mSJEqfc~y3f|ZJApEYv6l^)=VqrZ9lWVK8O
zU>1JncUftFe2h1Y7g+<?Ib>$8*Pn@KrJ0!R?hP2}`<6{Na0X1xjO)gE)$IxP)Qt{A
zKx-tY&U*%lw5VEJMfb8$adCl+gO%)nl5&a_d3mD0bIx#`ehVh8dX^|%cdatEnP*Ew
zWdx+Ss*Lq&8wmC7CU0A>t((QXdXiFHHC3`9kVcs}02?Kt(2+>n+~c=Eiou;&BKxn7
z$SMxkWNKjHht{7Mz`~(+^(XtzkSV?q8Ktik^|8YMNX!5;!`0%rt0{Q_kn>17A8UNy
z&TRh$*I(<uOx}=Q8C=XP(=?j_M6raAkI&sPxRLxBf93c8FxUw<X_{aHd^Z(U$n-xL
zzQo2SX7flM3DEwL>C#o~32p##v5D5?c;WfLymzAw5`a`!+X6Hf{l6B7YOcX8azQ@f
zls!H(hQAF`e|e_{j<u2-Mszk@l$Gr%+@8-J#&2OMVAXClKZCXye*;l*P~PQvrc%=p
z9np`Qz;Z90-99*w(TXwA$<YEHU+ASP8_USsAe26Bk{oqtnJI*p4%p>&&bt74c!<8?
z;_ZN4Z+HILrqhu)5_JI;ILJ^D{)3v8wPPfcBJ{f(uqdoM?TQ7-#+}8M_Dj0B`K8fx
zRn`^Vwm}6y*z7D$@ZF&8*V!}2<aPhx#Fs7<Bh7taJNO_J9C#3lA;=g=f*#qfU_h5b
zg`2-(Tl0sGj<;1)oXy|lxnzj-&sb*3iI@Ux)Bqs^tNIsK?4LR?CZ1xsC)}W<1)6M0
z$L$&SmSxQ><0SOJ-Iq^Wwzjv*+nmCcR`sUg`@$0DNgvt$sNep+4{E3&u+q7@Ds1a+
zu{yi{DUm$vDeVRCTW0%HJdP{nDYk+EbYY0fo0J$M|BDOhi<!5g9DdUepdb*@Nzt(Y
zbd-qxM&aUg<MpEdF63RwbyeP{O~xVYTW@H$;dykPEPS7Io;pr6(kRfQjn$7@0(r@m
zyzxshND5yjKK%{&w&5Idu?DF>XbAqPk!yFGk>XN!7_N7zL4feYK5f%THf*a{((u^4
zP3~tIm2Al;0s-53OWhve_-2zqmd5InlP|!{5l8Ab8kOy#MmlZUzQu={wNf*um1Z2b
z^bu<wvlon~u`uQOtGAFo{9+uf#0Qk6ARp>a`k;25A~pZp*&snB(G;OyI7(OgeM_xr
zgckw*>!MuYVef9|w%<O*HeMoSl(L#YxvB%6+ArK$n8NN97v%gzj1`nY4P8D`X^sG8
z1U&IT0e#c1rw6g2+t~rTo%tOg#|Xqmkx2yn>7hObK7-l*0k|j#L>w`S1^ijV1r7lj
z2m$8-fkLH#f%*S`5ZrA~1US3r`OPJK!5fV_Rn)h2Nebk{srKJoyGD+o{MQdaX^85N
zpZ6wie=qa{9mJ-3u=SaOl?(escPzeA#Qn6nx0@`fVO#VUKI$1W@-c!K7}8sc=iU`=
z_Q!ukyrZ&ZNS|Dq)A9!I;-3@n`5=d)I!1r1`JK%6SiboVx1JqD9-lsayAarjqp-QU
zI6N|>iQVCy5BIyTL=@I0Z%P@vb6cX^%xjA%^coKeTRnw}r{-HcK;;f9Hn2Ti0tsPq
z_mUnl*N8GN5^(wg2ao+A@&yEGPwg(@4)v=XARtrkWSEOPy7NihTQYj7i_iv(@tZQQ
zYj{hnPAWMIY}<O(YtL;}1<@cxn?-@|w|<6U7ESxf-92tdz0$2?ZoZ*~%SoA}q$C%|
zp_)XY3-|2p7Z%wl!sE9G&w2O9Vp-|A*x56`Xr{Mx+Z7SOE`(dWeKi_;w@X?rU2u3e
zwSBWoEueo~^o4HG8{Xy;fefA@AKE*ru9k@D_s*61Vfy03aD>$#L&nlu-Rp^9fw`>p
zl}$vHbCsq2Qg`E3`j+g7*gCvMptzY@r#eaJIfxHA)MgAKQ((Q)8#~|f3nTn(r%%gM
zygTD_IFr=xF6<);aHc&E-^vt6`AmDr9P%!$b*amo<s>`VT&KR;hW1XqRsyq*;_U%n
zSzKchq8R%Dmb&%Jwpr;n_v!OgPd||!FES&$cqH3TkF%B9cr)Uh>k$Fhf5*eL-c}~$
zHSHvkUi+$#Vnz3+n73#TM-a^t6jcDsZ27geA~GVP;}1JwSlq+6*O0=vv>|#Iw>o$I
zh_cDLrpyqmh}w#U1v1xP?;yK!4DK4$)o$;0#AaP^fYBG3oemA2_ghL$@ZoE8&#c#w
z3;al9S*SdE7Hw%`Yg>120rFe3Fg7+`m_1$gT@H#)fM1;-E%Y=sHO<@OWz?p})!$;k
z^y41hukEj^;RWXVzXjUCb289M>P*q;Q_x9(?El|zyVPsO0WSDvvDw=ia#Y^C=Fzs9
zO)Y@!=SB`h{%B=Sx_omI@3sBmXC>4XJ!F(S*HFek^c>cDwJR*;s*QunUfMTadzf&A
zO|;3qMlzn&NvfNsk`B+vzdy=y3=Hn*^W06#X2YDAv`M36#d|CE%3JqA8FVEbIgq(o
zYJzN?DSe&t4abLc(H{88#Y0TF)J>EF++|Bes0)_D4+a^&9W#>2O_`x_no5YVOl!cC
zBhX@86fKj$@*lX~Ex=SVVXn98Q`RdVGRLt%e&AaL>>%<f*tt~yt3tW!u!)v2lCb!J
z+H^q~1~Taa&E!b1t7(P^y|JZ3fsc1NqFem6mT#VjIe(#T?{x@fL|%+T39HqsDD}ei
zYs?l9Fb00;B8YD0(r${vmpbQtF&_^KT0Bb*PjGlYsWOywBxhcf3!6`tW=-4FPFBDa
zcif9Cq2Q1mn1yb~;@4@nCQ<vrF&OaK#p+SNkEG)RpK1zn_CL|sU=^3n<CJ(Wnv^4a
zQWp{ajVV<q5mng6hN)E&?}#k?ffU=$^FmKH$OYIrmkn&IvR>LUw5s5!wZ=JRVwI10
zUh&h>mv@Kpo#AbGhg-bJ5_wTyyMv_XJrIcbL$<^HE*U+msTT%q(`11Jzsu>z$3yr_
z8<)Jw5v@M=JNcR0D%=_<rc4=g3%|EGf!C>r(~Llh$fRn)kn>~kv2bN;fyhXS#pIa&
zsEw9_FtmBr3997HNpk%Y(}HWGML(kErn0JxR)`eAcT&qi>gI{QlEBY?ZSQy_W@oq8
zy##~+FmtS(oT`VU?tz|3k&%&=Z7el=%Mp2a>cKrT>+7pU-|E`<Hh6uvGy1)~Ezr5-
ze|>K0Zn$jyR~#Vkg?!6H2)0lCtWZ@T%T8%z61Jn`^>iXBcJ&4Kir-x^n;d(m6Hgc{
zgkbFmjBct1H{bUYMkbjL81C7eq!D$OPyP5mAjjnYu|V|_&4Ga1fbHpACix0eh!Uo=
zppTONdte+A?$%JCZ9LTk=6(56Fv=hjCHza6<l~7LYe(l=ud|<%u?$=HaOOw^a!d2w
z7r+w+3X8F~SLP|GSk)t~j!aT^ONN<ZI1*HY30%Y=C*VG;0zEoz+9*P;By7%uJ08rI
zsOC1YYt)&oGBT}{)!L)D<4>QJD4yeb{bpXBVd=~>fauG#XJd<oNb0NuGYBZo?Vur)
zQv?6{qT4+5u5c^(6}4&XWbRFmW%nm3aKA;1?(~Po62ZQ#JSxjrD^5obr;U-=9TGX~
zO1~SbP57`uDl%Z|X1E!rD&mtpr)GHT%|PFf74hx+`+DXpE)($PR3-0&Qe`Vf(L_}?
zir5+cZXgU^A!B3y8!xE_TYVN{URu-t#_Gs!gv7<}5?bKWEHcv=e`}_?)W{F5T3Q}(
zCvSJXt6e<{7_g%M6e@E=(T@G-Lj63UfiC^ixN6$I1D+^mR6u4CfJMj!c44(d<$PnI
z?P{WwV@P1&Rd=`6hOTE?>AV(s(584%2dql;MA^uOqJRuOm1bO?_}6!mg)tH<ZmSlT
zp+MB)OJnlzj>6yQoc}Ns5;tS?70lzd&cJhwIM#1Jc!Ml^p<NGK2wq5-x$Z|JB4S;b
zSH}u^*)2@Dd@?+_Ygsi(!Xo%Ryl@b}&z+4fR)1{-!eH|u&G8TKhlwUmZ)}()N1%iF
z!g+XjbcdSVF5_5^n|$TE0%7_hZ+FEsG&BJ8HF;IjQUT`%Vn*9k=hc4&X)3k%D=3kp
z^FIV#B5xmoeoLR)F3-<g?Hup{b2*$Hi2{v*J9Xu@18yES9j(1N9LPPtEks!ePExAX
zW<3?dQl|r6%Ujp_SD?q6Pi_kS*aEdM{sxk`!GM=owts@ij~H(KvKx~t;4LL3+MTX~
zTCkCzFe=A==uz%us;)ZCFXG2xTZ$Yd-I<@hs?rimjlY_TqvhHz)VID_x|_jX0+}ii
z`6m<4GJqVs)?{36vqL@u+QkB*03=fd*$L}>cx(qZ{&Rv;tRqpn5G8i+;l{>sf0-_$
zh@6b<mMQm-IDH~o>(@8)+bLCy_rm)t-{FiFp}`nTt!CgfAxVxw+d&B9aBq}djJs26
z>y0H{3CtC_&A4e(zRZWH(-unQck1}2%?!lkT9e3yjJzsz^QO&T@vByQHoc<I3l}!u
zMwv&Z_k*Ut=XC1yJ-#OtCig(Ei-N}ZftzaMKeK)3?@7~_U)^PBnm)vsWv*R={uEQD
zBh&m<xC|@UFtw>Mooq7(`$LQkG1g8Db5T?K3*=0GK2_8>!1^8`K?b(l>Q0&dHM725
zQSYm{<WwH3A=}~4E;G(=EzLS%eoQA+B6sOA5s+&wSj#+%aUKG)2s{C1293-M2@W7p
z`1fE8ZQuhHlp(<Z4ZLjW{Vjwcxyre0-hG9#qJ}uca>!R(zrOxh!evg@^3`ffW~tXF
zjA_3vNnon`F=w)ev8Im&_(^5gb^vdCKmopmy_G`C+<(xnZSnd|ZjS#oV)>%CzbWpI
z#pD`s4F{>{3Iye~HP9El?o9&VPaBR>!)nO(k)!j7<xQm{m<^jj71$a_BZU1{Q)|r_
zLlnyoicHXdr*_cq=g32)kf6hr66H0<>Kh(lmj91Bi~|xQ;EYXfm(#5qkci%MXLkG0
zyGvSM_0<%+PvFo;oYAn%-eHRhp==RIGtRwdqIb7(=}=g)nUdQHa{{LBhoG8SHp1x@
z!$38NT6%nTa0RYkKnBpifr1V4N)8;?Hx*U;(eYl@T`GfM$eQ3s{^2rs3P1vT7n<xq
zmN4JRr}!+l^;weIGM3b}A~xc~@pBuEc`_l~b-mr(bMZC(qc3|i!eNG9z0kV-WX1Pw
z=+BUC9mrJ~D0A8$G|h|mu<w#I->VJ@g?C+r$caZZQGXPUy+O&4T<y%?1Qj)^hdw1$
zX5-D*nDW#0ii?UFAJ~TF&Peo;ep?>WkIF%Y!_<y(txLOu7*Lkl4IK*f*}Z+t03e~_
zPTTj-OuZ;OsyEg|!;mA_S3#IQ1-3iTl3nUcUU<8a=85>N#)pg%OMO;wT$yRfJZy)N
z|2~Eo%V|_j&rZ9;_ki~x5Z@g_o7&!5#EQ+D8fBE}XCV9Ya-o8%1YGy)^xeV>qv-zS
zM}<nUY^0j7Ery1rlf$iDd(AoXqp!ac2}@BV#8mBnCw?^^A5hcGw{2S7VZPA=(Y-(-
z$dA5E*%{{2zA~pfN7|krbFG_eG0w7@-gh5g1(sGPO(YS_YznrX8=gX2uDiC=JWE?t
zA3hG1i5k{=>yapu&AV!-XEo~<58YJ{Xg_#+hmiuGYAMQA`k8IK!>(aapiNO|t~-qE
z!RWfW2LutyQUt&DlcdJE0U$g4GV0G`O2wKw)er5i*$~w$7iktEvR_m|CP4buf*XGR
z{L8l#3}AJWk`imo2ua~^es#&UVeZUi(nt9*?^NSmEwd5uMh)prCQB6r#x(G@lo<*c
z<l#^vKYU;|^0FSEOzST~)m~Zv6O@OScfpGb)b3DIS-ENjgD%uNXV%XkS^?a2{-_cQ
z*x-3j1XcNH;;^%IhKb;zRjf@RvJ#FXKC}Rh@#vd7SR_70*zN=DDgJ-KJuN^{1cBf=
zrEYsoz=yy{XO6r2;%1Pg9rVGx@%q6~bl<e0+g%O@!E3J7y>!lf+wK|PM7-Kk0Dc3K
Nkx&#b6EpbuzW_4K-bMfb

literal 0
HcmV?d00001

diff --git a/dtml/GRUFFolder_main.dtml b/dtml/GRUFFolder_main.dtml
new file mode 100644
index 0000000..feb89fb
--- /dev/null
+++ b/dtml/GRUFFolder_main.dtml
@@ -0,0 +1,275 @@
+<dtml-comment> -*- mode: dtml; dtml-top-element: "body" -*- </dtml-comment>
+<dtml-var manage_page_header>
+<dtml-var manage_tabs>
+
+<script type="text/javascript">
+<!-- 
+
+isSelected = false;
+
+function toggleSelect() {
+  if (isSelected == false) {
+    for (i = 0; i < document.objectItems.length; i++)
+      document.objectItems.elements[i].checked = true ;
+      isSelected = true;
+      document.objectItems.selectButton.value = "Deselect All";
+      return isSelected;
+  }
+  else {
+    for (i = 0; i < document.objectItems.length; i++)
+      document.objectItems.elements[i].checked = false ;
+      isSelected = false;
+      document.objectItems.selectButton.value = "Select All";
+      return isSelected;       
+  }
+}
+
+//-->
+</script>
+
+<dtml-unless skey><dtml-call expr="REQUEST.set('skey', 'id')"></dtml-unless>
+<dtml-unless rkey><dtml-call expr="REQUEST.set('rkey', '')"></dtml-unless>
+
+<!-- Free text -->
+<dtml-if header_text>
+  <p class="form-help">
+    <dtml-var header_text>
+  </p>
+</dtml-if>
+
+
+<!-- Add object widget -->
+<br />
+<dtml-if filtered_meta_types>
+  <table width="100%" cellspacing="0" cellpadding="0" border="0">
+  <tr>
+  <td align="left" valign="top">&nbsp;</td>
+  <td align="right" valign="top">
+  <div class="form-element">
+  <form action="&dtml-URL1;/" method="get">
+  <dtml-if "_.len(filtered_meta_types) > 1">
+    <select class="form-element" name=":action" 
+     onChange="location.href='&dtml-URL1;/'+this.options[this.selectedIndex].value">
+    <option value="manage_workspace" disabled>Select type to add...</option>
+    <dtml-in filtered_meta_types mapping sort=name>
+    <option value="&dtml.url_quote-action;">&dtml-name;</option>
+    </dtml-in>
+    </select>
+    <input class="form-element" type="submit" name="submit" value=" Add " />
+  <dtml-else>
+    <dtml-in filtered_meta_types mapping sort=name>
+    <input type="hidden" name=":method" value="&dtml.url_quote-action;" />
+    <input class="form-element" type="submit" name="submit" value=" Add &dtml-name;" />
+    </dtml-in>
+  </dtml-if>
+  </form>
+  </div>
+  </td>
+  </tr>
+  </table>
+</dtml-if>
+
+<form action="&dtml-URL1;/" name="objectItems" method="post">
+<dtml-if objectItems>
+<table width="100%" cellspacing="0" cellpadding="2" border="0">
+<tr class="list-header">
+  <td width="5%" align="right" colspan="2"><div 
+   class="list-item"><a href="./manage_main?skey=meta_type<dtml-if 
+   "rkey == ''">&rkey=meta_type</dtml-if>"
+   onMouseOver="window.status='Sort objects by type'; return true"
+   onMouseOut="window.status=''; return true"><dtml-if 
+   "skey == 'meta_type' or rkey == 'meta_type'"
+   ><strong>Type</strong><dtml-else>Type</dtml-if></a></div>
+  </td>
+  <td width="50%" align="left"><div class="list-item"><a 
+   href="./manage_main?skey=id<dtml-if 
+   "rkey == ''">&rkey=id</dtml-if>"
+   onMouseOver="window.status='Sort objects by name'; return true"
+   onMouseOut="window.status=''; return true"><dtml-if 
+   "skey == 'id' or rkey == 'id'"
+   ><strong>Name</strong><dtml-else>Name</dtml-if></a></div>
+  </td>
+  <td width="15%" align="left"><div class="list-item"><a 
+   href="./manage_main?skey=get_size<dtml-if 
+   "rkey == ''">&rkey=get_size</dtml-if>"
+   onMouseOver="window.status='Sort objects by size'; return true"
+   onMouseOut="window.status=''; return true"><dtml-if 
+   "skey == 'get_size' or rkey == 'get_size'"
+   ><strong>Size</strong><dtml-else>Size</dtml-if></a></div>
+  </td>
+  <td width="29%" align="left"><div class="list-item"><a 
+   href="./manage_main?skey=bobobase_modification_time<dtml-if 
+   "rkey == ''">&rkey=bobobase_modification_time</dtml-if
+   >"
+   onMouseOver="window.status='Sort objects by modification time'; return true"
+   onMouseOut="window.status=''; return true"><dtml-if 
+   "skey == 'bobobase_modification_time' or rkey == 'bobobase_modification_time'"
+   ><strong>Last Modified</strong><dtml-else>Last Modified</dtml-if></a></div>
+  </td>
+</tr>
+<dtml-in objectItems sort_expr="skey" reverse_expr="rkey">
+<dtml-if sequence-odd>
+<tr class="row-normal">
+<dtml-else>
+<tr class="row-hilite">
+</dtml-if>
+  <td align="left" valign="top" width="16">
+  <input type="checkbox" name="ids:list" value="&dtml-sequence-key;" />
+  </td>
+  <td align="left" valign="top" nowrap="1">
+
+  <dtml-if om_icons>
+  <a href="&dtml.url_quote-sequence-key;/manage_workspace">
+  <dtml-in om_icons mapping>
+  <img src="&dtml-BASEPATH1;/&dtml.url_quote-path;" alt="&dtml.missing-alt;" 
+   title="&dtml.missing-title;" border="0" /></dtml-in></a>
+  <dtml-else>
+
+  <dtml-if icon>
+  <a href="&dtml.url_quote-sequence-key;/manage_workspace">
+  <img src="&dtml-BASEPATH1;/&dtml-icon;" alt="&dtml-meta_type;" 
+   title="&dtml-meta_type;" border="0" /></a>
+  <dtml-else>
+  &nbsp;
+  </dtml-if>
+
+  </dtml-if>
+
+  </td>
+  <td align="left" valign="top">
+  <div class="list-item">
+  <a href="&dtml.url_quote-sequence-key;/manage_workspace">
+  &dtml-sequence-key; <dtml-if title>(&dtml-title;)</dtml-if>
+  </a>
+  <dtml-if locked_in_version>
+    <dtml-if modified_in_version>
+      <img src="&dtml-BASEPATH1;/p_/locked"
+       alt="This item has been modified in this version" />
+    <dtml-else>
+      <img src="&dtml-BASEPATH1;/p_/lockedo"
+       alt="This item has been modified in another version" />
+       (<em>&dtml-locked_in_version;</em>)
+    </dtml-if>
+  </dtml-if>
+  </div>
+  </td>
+
+  <dtml-with sequence-key>
+  <td>
+  <div class="list-item">
+  <dtml-try>
+  <dtml-if get_size>
+  <dtml-let ob_size=get_size>
+  <dtml-if "ob_size < 1024">
+  1 Kb
+  <dtml-elif "ob_size > 1048576">
+  <dtml-var "ob_size / 1048576.0" fmt="%0.02f"> Mb
+  <dtml-else>
+  <dtml-var "_.int(ob_size / 1024)"> Kb
+  </dtml-if>
+  </dtml-let>
+  <dtml-else>
+  &nbsp;
+  </dtml-if>
+  <dtml-except>
+  &nbsp;
+  </dtml-try>
+  </div>
+  </td>
+
+  <td>
+  <div class="list-item">
+  <dtml-var bobobase_modification_time fmt="%Y-%m-%d %H:%M">
+  </div>
+  </td>
+  </dtml-with>
+</tr>
+</dtml-in>
+</table>
+
+<table cellspacing="0" cellpadding="2" border="0">
+<tr>
+  <td align="left" valign="top" width="16"></td>
+  <td align="left" valign="top">
+  <div class="form-element">
+  <dtml-unless dontAllowCopyAndPaste>
+  <input class="form-element" type="submit" name="manage_renameForm:method" 
+   value="Rename" />
+  <input class="form-element" type="submit" name="manage_cutObjects:method" 
+   value="Cut" /> 
+  <input class="form-element" type="submit" name="manage_copyObjects:method" 
+   value="Copy" />
+  <dtml-if cb_dataValid>
+  <input class="form-element" type="submit" name="manage_pasteObjects:method" 
+   value="Paste" />
+  </dtml-if>
+  </dtml-unless>
+  <dtml-if "_.SecurityCheckPermission('Delete objects',this())">
+  <input class="form-element" type="submit" name="manage_delObjects:method" 
+   value="Delete" />
+  </dtml-if>
+  <dtml-if "_.SecurityCheckPermission('Import/Export objects', this())">
+  <input class="form-element" type="submit" 
+   name="manage_importExportForm:method" 
+   value="Import/Export" />
+  </dtml-if>
+<script type="text/javascript">
+<!-- 
+if (document.forms[0]) {
+  document.write('<input class="form-element" type="submit" name="selectButton" value="Select All" onClick="toggleSelect(); return false">')
+  }
+//-->
+</script>
+  </div>
+  </td>
+</tr>
+</table>
+
+<dtml-else>
+<table cellspacing="0" cellpadding="2" border="0">
+<tr>
+<td>
+<div class="std-text">
+There are currently no items in <em>&dtml-title_or_id;</em>
+<br /><br />
+</div>
+<dtml-unless dontAllowCopyAndPaste>
+<dtml-if cb_dataValid>
+<div class="form-element">
+<input class="form-element" type="submit" name="manage_pasteObjects:method" 
+ value="Paste" />
+</div>
+</dtml-if>
+</dtml-unless>
+<dtml-if "_.SecurityCheckPermission('Import/Export objects', this())">
+<input class="form-element" type="submit"
+  name="manage_importExportForm:method" value="Import/Export" />
+</dtml-if>
+</td>
+</tr>
+</table>
+</dtml-if>
+</form>
+
+<dtml-if update_menu>
+<script type="text/javascript">
+<!--
+window.parent.update_menu();
+//-->
+</script>
+</dtml-if>
+
+<dtml-var manage_page_footer>
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dtml/GRUF_audit.zpt b/dtml/GRUF_audit.zpt
new file mode 100644
index 0000000..26dfdc0
--- /dev/null
+++ b/dtml/GRUF_audit.zpt
@@ -0,0 +1,236 @@
+  <h1 tal:define="global print request/pp | nothing"></h1>
+  <h1 tal:replace="structure here/manage_page_header">Header</h1>
+  <h2 tal:condition="not: print" tal:define="manage_tabs_message options/manage_tabs_message | nothing"
+    tal:replace="structure here/manage_tabs">Tabs</h2>
+
+  
+  <div tal:condition="request/doIt | nothing">
+    <h4>Audit results</h4>
+
+    <table 
+           border="1"
+           class="list-item"
+           tal:define="
+      global users_and_roles here/listUsersAndRoles;
+      site_tree here/getSiteTree;
+      table_cache python:here.computeSecuritySettings(site_tree, users_and_roles, [('R', request.read_permission), ('W', request.write_permission)]);
+      "
+      tal:condition="users_and_roles"
+      >
+      <tr tal:define="width python:int(100/len(users_and_roles))">
+        <td width="0" tal:attributes="width string:$width%"></td>
+        <td width="0" align="center"
+            tal:repeat="s users_and_roles"
+          tal:attributes="width string:$width%"
+          >
+          <span tal:define="color python:test(s[0] == 'user', here.user_color, test(s[0] == 'group', here.group_color, here.role_color))">
+            <font color="" tal:attributes="color color">
+              <tal:block tal:condition="not:request/use_legend|nothing">
+                <b tal:content="structure python:s[4]" /><br />
+              </tal:block>
+              <tal:block tal:condition="request/use_legend|nothing">
+                <b tal:content="python:s[3]" />
+              </tal:block>
+            </font>
+            <span tal:condition="not:request/use_legend|nothing">
+              (<font color="" tal:attributes="color color"><span tal:replace="python:s[0]" /></font>)
+            </span>
+          </span>
+        </td>
+      </tr>
+
+      <tr tal:repeat="folder site_tree">
+        <td nowrap="1">
+          <span tal:repeat="x python:range(0,folder[1])" tal:omit-tag="">-</span>
+          <a href=""
+             tal:attributes="href python:folder[2]"
+            tal:content="python:folder[0]"
+            />
+            <tal:block 
+              tal:define="state python:here.portal_workflow.getInfoFor(here.restrictedTraverse(folder[2]), 'review_state')"
+              tal:on-error="nothing"
+              >
+              <br />
+                <span tal:repeat="x python:range(0,folder[1])" tal:omit-tag="">-</span>
+                <span tal:replace="state" />
+            </tal:block>
+        </td>
+        <td 
+            tal:repeat="s users_and_roles" 
+          >
+          <tal:block
+            tal:define="
+            R python:table_cache[folder[2]][s[:2]].get('R', None);
+            W python:table_cache[folder[2]][s[:2]].get('W', None)"
+            >
+            <span tal:condition="R">R</span>
+            <span tal:condition="W">W</span>
+            <span tal:condition="python: (not R) and (not W)">&nbsp;</span>
+          </tal:block>
+        </td>
+      </tr>
+    </table>
+  </div>
+
+  <div tal:condition="request/use_legend|nothing">
+    <h4>Legend</h4>
+    <ol>
+      <table>
+        <tr class="list-header">
+          <th class="list-header">Id</th>
+          <th class="list-header">Label</th>
+          <th class="list-header">Kind</th>
+        </tr>
+        
+        <tr tal:repeat="actor users_and_roles">
+          <span tal:define="color python:test(actor[0] == 'user', here.user_color, test(actor[0] == 'group', here.group_color, here.role_color))">
+            <td class="list-item"><font color="" tal:attributes="color color" tal:content="python:actor[3]">Id</font></td>
+            <td class="list-item"><font color="" tal:attributes="color color" tal:content="structure python:actor[4]">Label</font></td>
+            <td class="list-item"><font color="" tal:attributes="color color" tal:content="python:actor[0]">Kind</font></td>
+          </span>
+        </tr>
+
+      </table>
+    </ol>
+  </div>
+
+  <div tal:condition="not: print" tal:omit-tag="">
+    <h4>Audit settings</h4>
+    <ol>
+      <p>
+        See help below if you do not understand those settings.
+      </p>
+
+      <form action="manage_audit" method="GET">
+        <input type="hidden" name="doIt" value="1">
+          <table
+                 tal:define="default here/getDefaultPermissions"
+            >
+            <tr class="list-header">
+              <th>Parameter</th>
+              <th class="list-header">Setting</th>
+            </tr>
+            <tr>
+              <td><div class="list-item">Read permission</div></td>
+              <td>
+                <select name="read_permission" size="1">
+                  <option
+                          selected=0
+                          value=""
+                          tal:repeat="perm here/listAuditPermissions"
+                    tal:attributes="
+                    value perm;
+                    selected python:perm == default['R'];
+                    "
+                    tal:content="perm"
+                    />
+                </select>
+              </td>
+            </tr>
+            <tr>
+              <td><div class="list-item">Write permission</div></td>
+              <td>
+                <select name="write_permission" size="1">
+                  <option 
+                          selected=0
+                          value=""
+                          tal:repeat="perm here/listAuditPermissions"
+                    tal:attributes="
+                    value perm;
+                    selected python:perm == default['W'];
+                    "
+                    tal:content="perm"
+                    />
+                </select>
+              </td>
+            </tr>
+            <tr>
+              <td><div class="list-item">Displayed actors</div></td>
+              <td>
+                <div class="list-item">
+                  <input type="checkbox" name="display_roles" checked="" tal:attributes="checked request/display_roles|python:test(request.get('doIt',None), 0, 1)">
+                    <font color="" tal:attributes="color here/role_color">Roles</font><br />
+                      <input type="checkbox" name="display_groups" checked="" tal:attributes="checked request/display_groups|python:test(request.get('doIt',None), 0, 1)">
+                        <font color="" tal:attributes="color here/group_color">Groups</font><br />
+                      <input type="checkbox" name="display_users" checked="" tal:attributes="checked request/display_users|python:test(request.get('doIt',None), 0, 0)">
+                        <font color="" tal:attributes="color here/user_color">Users</font>
+                </div>
+              </td>
+            </tr>
+            <tr>
+              <td valign="top"><div class="list-item">Use a legend</div></td>
+              <td>
+                <div class="list-item">
+                  <input type="checkbox" name="use_legend" checked="" tal:attributes="checked request/use_legend|nothing">
+                    (Use this feature to display actors names outside the table. This will reduce the table width, which may be useful for printing, for example.)
+                </div>
+              </td>
+            </tr>
+            <tr>
+              <td><div class="list-item">Printable page</div></td>
+              <td>
+                <div class="list-item">
+                  <input type="checkbox" name="pp" checked="" tal:attributes="checked request/pp|nothing">
+                </div>
+              </td>
+            </tr>
+            <tr>
+              <td></td>
+              <td><input type="submit" value="View"></td>
+            </tr>
+          </table>
+      </form>
+    </ol>
+  </div>
+
+
+  <div tal:condition="not: print" tal:omit-tag="">
+    <div tal:condition="not:request/doIt | nothing">
+
+      <h4>About the audit table</h4>
+      <ol>
+        <p>
+          This management tab allows one to check how the site security is applied for the most useful cases.<br />
+            This allows you to have a precise abstract of the security settings for a little set of permissions as
+            if it simply were "Read" and "Write" permissions.
+        </p>
+
+        <p>
+          <strong>
+            This management tab won't change anything in your security settings. It is just intended to show information and not to modify anything.
+          </strong>
+        </p>
+        
+        <p>
+          Select, in the form below, the permissions you want to monitor and the kind of actors (roles, groups or users) you want to display.
+        </p>
+        
+        <ol>
+          <h4>Hint</h4>
+          <p>
+            Usually, for a regular Zope site, the
+            permission set would be mapped this way:
+          </p>
+          
+          <ul>
+            <li>Read: View</li>
+            <li>Write: Change Images and Files</li>
+          </ul>
+          <p>
+            For a Plone site, the
+            permission set would be mapped this way:
+          </p>
+          
+          <ul>
+            <li>Read: View</li>
+            <li>Write: Modify portal content</li>
+          </ul>
+          <p>
+            If you have <strong>a lot of users</strong>, rendering this audit can be very time-consuming.<br />
+              In such conditions, you can select only "roles" to make things a lot faster.
+        </ol>
+      </ol>
+    </div>
+  </div>
+
+  <h1 tal:replace="structure here/manage_page_footer">Footer</h1>
diff --git a/dtml/GRUF_contents.zpt b/dtml/GRUF_contents.zpt
new file mode 100644
index 0000000..7641a21
--- /dev/null
+++ b/dtml/GRUF_contents.zpt
@@ -0,0 +1,216 @@
+<h1 tal:replace="structure here/manage_page_header">Header</h1>
+<h2 tal:define="manage_tabs_message options/manage_tabs_message | nothing"
+    tal:replace="structure here/manage_tabs">Tabs</h2>
+
+  <ol>
+    <p class="form-help">
+      You are currently running <strong>GRUF v.<span tal:replace="here/getGRUFVersion">version</span></strong><br />
+      Information, latest version, documentation... see 
+      <a target="_blank" href="http://ingeniweb.sourceforge.net/Products/GroupUserFolder">The GRUF Webpage</a>.
+    </p>
+  </ol>
+
+  <!-- Show problems if it happens -->
+  <div tal:condition="request/GRUF_PROBLEM|nothing">
+    <font color="red"><strong><span tal:content="request/GRUF_PROBLEM">gruf message</span></strong></font>
+  </div>
+
+
+  <h4>Users folders management</h4>
+  <ol>
+    <p class="form-help">Use this form to check/manage the underlying user folders.</p>
+    <p class="form-help">BE CAREFUL THAT MISUSE OF THIS FORM CAN LEAD YOU TO UNRECOVERABLE LOSS OF USER DATA.</p>
+    <p class="form-help">For this reason, all destructive actions (ie. replacing or deleting) with existing UserFolders must be confirmed
+    by clicking the rightmost checkbox.</p>
+
+    <form action="" tal:attributes="action string:${here/absolute_url}" method="POST">
+      <!-- Users selection -->
+      <table bgcolor="#EEEEEE" tal:on-error="nothing">
+        <tr>
+          <td rowspan="2" valign="middle"></td>
+          <th class="list-header" rowspan="2" valign="middle">Type</th>
+          <th class="list-header" colspan="5">Actions</th>
+        </tr>
+        <tr class="list-header">
+          <th>Move</th>
+          <th>Enable</th>
+          <th>Replace</th>
+          <th>Delete</th>
+          <th>Confirm</th>
+        </tr>
+
+        <!-- Groups source row -->
+        <tr>
+          <th class="list-header">Groups source</th>
+          <td bgcolor="#EEEEEE">
+              <img src="" tal:attributes="src here/Groups/acl_users/icon">&nbsp;
+              <a href="Groups/acl_users/manage_workspace" tal:content="here/Groups/acl_users/meta_type">Type</a>
+          </td>
+          <td></td>
+          <td bgcolor="#EEEEEE">&nbsp;</td>
+          <td bgcolor="#EEEEEE">
+            <table border="0">
+              <tr>
+                <td align="left">
+                  <input type="hidden" name="source_rec.id:records" value="Groups" />
+                  <select name="source_rec.new_factory:records">
+                    <option value="">-- Select your source type --</option>
+                    <tal:block tal:repeat="source here/listAvailableUserSources">
+                      <option value="" 
+                              tal:condition="python:source[0] != path('here/Groups/acl_users/meta_type')"
+                        tal:attributes="value python:source[1]">
+                        <span tal:replace="python:source[0]">name</span>
+                      </option>
+                    </tal:block>
+                  </select>
+                </td>
+                <td align="right">
+                  <input type="submit" name="replaceUserSource:action" value="Ok" />
+                </td>
+              </tr>
+            </table>
+          </td>
+          <td class="list-item">(forbidden)</td>
+          <td bgcolor="#EEEEEE" class="list-item">
+            <input type="checkbox" name="id" value="Groups" />I'm sure
+          </td>
+        </tr>
+
+
+        <!-- Users sources row -->
+        <tr tal:repeat="source here/listUserSourceFolders">
+          <th class="list-header">Users source #<span tal:replace="repeat/source/number">1</span></th>
+          <td bgcolor="#EEEEEE" tal:condition="source/isValid">
+            <img src="" 
+	    tal:attributes="src source/acl_users/icon;
+	    title source/acl_users/meta_type;">&nbsp;
+              <a href="" 
+	      tal:attributes="
+	      href string:${source/acl_users/absolute_url}/manage_workspace;
+	      title source/acl_users/meta_type;" 
+	       tal:content="source/acl_users/title|source/acl_users/meta_type">Type</a>
+                <tal:block condition="not:source/isEnabled">
+                  <font color="red"><i>(disabled)</i></font>
+                </tal:block>
+          </td>
+          <td bgcolor="#EEEEEE" tal:condition="not:source/isValid">
+            <font color="red"><strong><i>(invalid or broken)</i></strong></font>
+          </td>
+          <td bgcolor="#EEEEEE" align="center">
+            <a tal:condition="not:repeat/source/start" 
+              tal:attributes="href string:${here/absolute_url}/moveUserSourceUp?id=${source/getUserSourceId}"
+              href=""><img src="img_up_arrow" border="0" alt="Move up"></a>
+            <span tal:condition="repeat/source/start"><img src="img_up_arrow_grey" border="0" alt="Move up"></span>
+            &nbsp;
+            <a tal:condition="not:repeat/source/end" 
+              tal:attributes="href string:${here/absolute_url}/moveUserSourceDown?id=${source/getUserSourceId}"
+              href=""><img src="img_down_arrow" border="0" alt="Move down"></a>
+            <span tal:condition="repeat/source/end"><img src="img_down_arrow_grey" border="0" alt="Move down"></span>
+          </td>
+              <td bgcolor="#EEEEEE">
+                <font size="-2">
+                  <a
+                    tal:condition="source/isEnabled"
+                    tal:attributes="href string:${here/absolute_url}/toggleSource?src_id=${source/getUserSourceId}"
+                    >Disable
+                  </a>
+                  <a
+                    tal:attributes="href string:${here/absolute_url}/toggleSource?src_id=${source/getUserSourceId}"
+                    tal:condition="not: source/isEnabled"
+                    >Enable
+                  </a>
+                </font>
+              </td>
+          <td bgcolor="#EEEEEE">
+            <table border="0">
+              <tr>
+                <td align="left">
+                  <input type="hidden" name="source_rec.id:records" value="" tal:attributes="value source/getUserSourceId" />
+                  <select name="source_rec.new_factory:records">
+                    <option value="">-- Select your source type --</option>
+                    <tal:block tal:repeat="new_source here/listAvailableUserSources">
+                      <option value="" 
+                              tal:condition="python:new_source[0] != path('source/acl_users/meta_type')"
+                        tal:attributes="value python:new_source[1]">
+                        <span tal:replace="python:new_source[0]">name</span>
+                      </option>
+                    </tal:block>
+                  </select>
+                </td>
+                <td align="right">
+                  <input type="submit" name="replaceUserSource:action" value="Ok" />
+                </td>
+              </tr>
+            </table>
+          </td>
+          <td bgcolor="#EEEEEE" tal:condition="python:repeat['source'].length > 1" class="list-item">
+            <input 
+                   type="submit" 
+                   name="deleteUserSource:action" 
+                   value="Delete" />
+          </td>
+          <td tal:condition="python:not repeat['source'].length > 1" class="list-item">
+            (forbidden)
+          </td>
+          <td bgcolor="#EEEEEE" class="list-item">
+            <input type="checkbox" name="id" value="" tal:attributes="value source/getUserSourceId" />I'm sure
+          </td>
+        </tr>
+
+
+        <!-- Blank row -->
+        <tr>
+          <td class="list-item" colspan="6">&nbsp;</td>
+        </tr>
+
+        <!-- New sources row -->
+        <tr>
+          <th class="list-header">Add...</th>
+          <td colspan="6" class="list-item">
+            <select name="factory_uri">
+              <option value="">-- Select your source type --</option>
+              <option value="" tal:repeat="source here/listAvailableUserSources" tal:attributes="value python:source[1]">
+                <span tal:replace="python:source[0]">name</span>
+              </option>
+            </select>
+            <input type="submit" name="addUserSource:method" value="Add" />
+          </td>
+        </tr>
+      </table>
+    </form>
+    
+  </ol>
+
+    <tal:block condition="here/hasLDAPUserFolderSource">
+      <h4>Special operations</h4>
+      <ol>
+          <p class="form-help">
+            To manage groups with a LDAPUserFolder, one must map LDAP groups to Zope Roles.<br />
+            You can do this mapping manually or click this button to have it done automatically.<br />
+            Please not that any previously existing ldap-group - to - zope-role mapping may be lost.
+          </p>
+          <p class="form-help">
+            To help you in this task, you can have a look at the following table, which summs up<br />
+            the mappings done (or not done!) in LDAPUserFolder.
+          </p>
+
+      <table>
+        <thead>
+          <th>GRUF group</th>
+          <th>LDAP group</th>
+        </thead>
+        <tbody>
+          <tr tal:repeat="group_info here/listLDAPUserFolderMapping">
+            <td tal:content="python:group_info[0]"></td>
+            <td tal:content="python:group_info[1]"></td>
+          </tr>
+        </tbody>
+      </table>
+          <form action="updateLDAPUserFolderMapping">
+            <input type="submit" value="Update LDAP mapping" />
+          </form>
+      </ol>
+    </tal:block>
+
+
+<h1 tal:replace="structure here/manage_page_footer">Footer</h1>
diff --git a/dtml/GRUF_groups.zpt b/dtml/GRUF_groups.zpt
new file mode 100644
index 0000000..64fe589
--- /dev/null
+++ b/dtml/GRUF_groups.zpt
@@ -0,0 +1,267 @@
+<h1 tal:replace="structure here/manage_page_header">Header</h1>
+<h2 tal:define="manage_tabs_message options/manage_tabs_message | nothing"
+    tal:replace="structure here/manage_tabs">Tabs</h2>
+
+  <h4>Groups sources</h4>
+  <!-- Groups source row -->
+  <ol>
+    <table cellspacing="10" width="90%" tal:define="groups here/getGroups">
+      <tr>
+        <th class="list-header">Groups source</th>
+        <td bgcolor="#EEEEEE">
+          <img src="" tal:attributes="src here/Groups/acl_users/icon">&nbsp;
+            <a href="Groups/acl_users/manage_workspace" tal:content="here/Groups/acl_users/meta_type">Type</a>
+        </td>
+      </tr>
+    </table>
+  </ol>
+
+  <h4>Groups management</h4>
+  <form action="" method="POST" tal:attributes="action here/absolute_url">
+    <ol>
+      <table cellspacing="10" width="90%" tal:define="groups here/getGroups">
+        <tr>
+          <!-- Groups selection -->
+          <td valign="top">
+            <table bgcolor="#EEEEEE" width="100%">
+              <tr class="list-header" tal:condition="groups">
+                <th>&nbsp;</th>
+                <th>Group</th>
+                <th class="list-header">Member <br>of groups</th>
+                <th class="list-header">Implicitly <br>member of*</th>
+                <th class="list-header">Has roles</th>
+                <th class="list-header">Implicitly <br>has roles**</th>
+              </tr>
+              
+              <tr 
+                  tal:repeat="group groups" class="" tal:attributes="class python:test(path('repeat/group/odd'), 'row-hilite', 'row-normal')"
+                >
+                <div tal:define="
+                  label_groups python:group.getGroups();
+                  label_groups_no_recurse python:group.getImmediateGroups();
+                  label_groups_recurse python:filter(lambda x: x not in label_groups_no_recurse, label_groups);
+                  groups_no_recurse python:map(lambda x: here.getUser(x), label_groups_no_recurse);
+                  groups_recurse python:map(lambda x: here.getUser(x), label_groups_recurse);
+                  roles python:filter(lambda x: x not in ('Authenticated', 'Shared'), group.getRoles());
+                  roles_no_recurse python:filter(lambda x: x not in ('Authenticated', 'Shared'), group.getUserRoles());
+                  roles_recurse python:filter(lambda x: x not in roles_no_recurse, roles);"
+                  tal:omit-tag="">
+                  <td><div class="list-item"><input type="checkbox" name="groups:list" value="" tal:attributes="value group"></td>
+                  <td>
+                    <div class="list-item">
+                      <img src="img_group">
+                      <strong tal:content="structure group/asHTML">
+                      </strong>
+                    </td>
+                    <td class="list-item">
+                      <span tal:repeat="group groups_no_recurse" >
+                        <span tal:replace="structure group/asHTML"></span><span tal:condition="not:repeat/group/end">, </span>
+                      </span>
+                    </td>
+                    <td class="list-item">
+                      <span tal:repeat="group groups_recurse" >
+                        <span tal:replace="structure python:group.asHTML(implicit=1)"></span><span tal:condition="not:repeat/group/end">, </span>
+                      </span>
+                    </td>
+                    <td class="list-item">
+                      <div class="list-item">
+                        <span tal:repeat="role roles_no_recurse" >
+                          <font color=""
+                                tal:attributes="color here/role_color">
+                            <span tal:replace="role"></span><span tal:condition="not:repeat/role/end">, </span>
+                          </font>
+                        </span>
+                      </div>
+                    </td>
+                    <td class="list-item">
+                      <div class="list-item">
+                        <span tal:repeat="role roles_recurse" >
+                          <font color=""
+                                tal:attributes="color here/role_color">
+                            <i><span tal:replace="role"></span></i><span tal:condition="not:repeat/role/end">, </span>
+                          </font>
+                        </span>
+                      </div>
+                    </td>
+                </div>
+              </tr>
+
+              <!-- New user -->
+              <tr>
+                <td><div class="list-item">&nbsp;</div></td>
+                <td><div class="list-item">Create groups:<br /><textarea name="new_groups:lines" cols="20" rows="3"></textarea></div></td>
+                <td colspan="4">
+                  <div class="list-item">
+                    Newly created groups will be affected groups and roles according to the table below.
+                  </div>
+                </td>
+              </tr>
+              <tr>
+                <td colspan="2" align="center">
+                  <input type="submit" name="changeOrCreateGroups:method" value="Create" />
+                    &nbsp;
+                    <input type="submit" name="deleteGroups:method" value="Delete" />
+                </td>
+              </tr>
+            </table>
+          </td>
+        </tr>
+        <tr>
+          <td align="center">
+            <div class="list-item">
+              Select one or more users in the upper table, select one or more groups / roles in the table below
+              and click "Change" to affect groups / roles to these users.
+            </div>
+          </td>
+        </tr>
+        <tr>
+          <td valign="top" align="center" colspan="6">
+            <table  bgcolor="#EEEEEE">
+              <tr>
+                <td valign="top">
+                  <!-- Groups selection -->
+                  <table width="100%">
+                    <tr class="list-header">
+                      <th colspan="2">Affect groups</th>
+                    </tr>
+                    
+                    <tr tal:repeat="group here/getGroups">
+                      <td>
+                        <input type="checkbox" name="nested_groups:list" value="" tal:attributes="value group">
+                      </td>
+                      <td>
+                        <div class="list-item" tal:content="structure group/asHTML"></div>
+                      </td>
+                    </tr>
+                    
+                    <!-- "(None)" item -->
+                    <tr>
+                      <td><div class="list-item"><input type="checkbox" name="nested_groups:list" value="__None__"></div></td>
+                      <td><div class="list-item"><i>(None)</i></div></td>
+                    </tr>
+                  </table>
+                </td>
+                <td valign="top">
+                    <!-- Roles selection -->
+                    <table width="100%">
+                      <tr class="list-header">
+                        <th colspan="2">Affect roles</th>
+                      </tr>
+                      
+                      <tr tal:repeat="role here/valid_roles">
+                        <td tal:condition="python:role not in ('Authenticated', 'Anonymous', 'Shared')">
+                          <input type="checkbox" name="roles:list" value="" tal:attributes="value role">
+                        </td>
+                        <td tal:condition="python:role not in ('Authenticated', 'Anonymous', 'Shared')">
+                          <div class="list-item"><font color="" tal:attributes="color here/role_color" tal:content="role">Role</font></div>
+                        </td>
+                      </tr>
+                      
+                      <!-- "(None)" item -->
+                      <tr>
+                        <td><div class="list-item"><input type="checkbox" name="roles:list" value="__None__"></div></td>
+                        <td><div class="list-item"><i>(None)</i></div></td>
+                      </tr>
+                    </table>
+                </td>
+              </tr>
+              <tr>
+                <td colspan="2" align="middle"><input type="submit" name="changeOrCreateGroups:method" value="Change" /></td>
+            </table>
+          </td>
+        </tr>
+      </table>
+
+
+
+
+        <tr tal:replace="nothing">
+          <td valign="top" bgcolor="#EEEEEE">
+            <!-- Groups selection -->
+            <table width="100%">
+              <tr class="list-header">
+                <th colspan="2">Affect groups</th>
+              </tr>
+              
+              <tr tal:repeat="group here/getGroups">
+                <td>
+                  <input type="checkbox" name="nested_groups:list" value="" tal:attributes="value group">
+                </td>
+                <td>
+                  <div class="list-item" tal:content="structure group/asHTML"></div>
+                </td>
+              </tr>
+              
+              <!-- "(None)" item -->
+              <tr>
+                <td><div class="list-item"><input type="checkbox" name="nested_groups:list" value="__None__"></div></td>
+                <td><div class="list-item"><i>(None)</i></div></td>
+              </tr>
+            </table>
+            
+            <br>
+              
+              <!-- Roles selection -->
+              <table width="100%">
+              <tr class="list-header">
+                <th colspan="2">Affect roles</th>
+              </tr>
+              
+              <tr tal:repeat="role here/valid_roles">
+                <td tal:condition="python:role not in ('Authenticated', 'Anonymous', 'Shared')">
+                  <input type="checkbox" name="roles:list" value="" tal:attributes="value role">
+                </td>
+                <td tal:condition="python:role not in ('Authenticated', 'Anonymous', 'Shared')">
+                  <div class="list-item"><font color="" tal:attributes="color here/role_color" tal:content="role">Role</font></div>
+                </td>
+              </tr>
+                <!-- "(None)" item -->
+                <tr>
+                  <td><div class="list-item"><input type="checkbox" name="roles:list" value="__None__"></div></td>
+                  <td><div class="list-item"><i>(None)</i></div></td>
+                </tr>
+            </table>
+          </td>
+        </tr>
+
+      <p class="form-help">
+        * According to the groups inheritance, this group is also recursively member of these groups. <br />This is what we call nested groups.
+      </p>
+      <p class="form-help">
+        ** Accorded to the groups inheritance, this group also has these roles - even if they are not defined explicitly on it.
+      </p>
+
+    </ol>
+  </form>
+
+
+  <h4>Instructions</h4>
+  <ol>
+
+      <p class="form-help">
+        To change roles for one or several groups, select them in the left form, select the roles you want to give them in the form on the right and click "Change".<br />
+        You can also create one or several groups by filling the text area (one group per line). the "Change" button will create them with the roles you've selected.<br />
+        If you are fed up with some groups, you can delete them by selecting them and clicking the "Delete" button.
+      </p>
+      <p class="form-help">
+        If you do not select any role, roles won't be reseted for the selected groups.<br />
+        If you do not select any group, groups won't be reseted for the selected groups.<br />
+        To explicitly reset groups or roles, just click the "(None)" entry (and no other entry).
+      </p>
+  </ol>
+
+  <h4>Important notice / disclaimer</h4>
+  
+  <ol>
+    <p class="form-help">
+      This form uses the regular Zope Security API from the underlying user folders. However, you may experience problems with some
+      of them, especially if they are not tuned to allow user adding. For example, an LDAPUserFolder can be configured to disable
+      users management. In case this form doesn't work, you'll have to do things by hand within the 'Users' and 'Groups' GRUF folders.
+    </p>
+
+    <p class="form-help">
+      This is not a GRUF limitation ! :-)
+    </p>
+  </ol>
+
+<h1 tal:replace="structure here/manage_page_footer">Footer</h1>
diff --git a/dtml/GRUF_newusers.zpt b/dtml/GRUF_newusers.zpt
new file mode 100644
index 0000000..93a1092
--- /dev/null
+++ b/dtml/GRUF_newusers.zpt
@@ -0,0 +1,32 @@
+  <h1 tal:replace="structure here/manage_page_header">Header</h1>
+
+  <p class="form-help">
+    This form appear because you've just created some users.<br />
+      GRUF has generated random passwords for them: here they are.
+  </p>
+
+  <p class="form-help">
+    <b><font color="red">IMPORTANT</font></b>: Take some time to write down this information
+    (a copy/paste within a notepad should do it) before clicking the "Ok" button below, as 
+    you won't have any (easy) way to retreive your user's passwords after!
+  </p>
+
+  <h4>Generated passwords</h4>
+  <ol>
+    <form action="" method="GET" tal:attributes="action string:${here/absolute_url}/manage_users">
+          <div tal:repeat="user request/USER_PASSWORDS">
+            <span tal:content="user/name">User name</span> :
+            <span class="list-item" tal:content="user/password">
+            </span>
+          </div>
+
+
+      <!-- Actions -->
+      <p align="left">
+        <input type="submit" name="changeOrCreateGroups:method" value="Ok" />
+      </p>
+    </form>
+  </ol>
+  
+
+  <h1 tal:replace="structure here/manage_page_footer">Footer</h1>
diff --git a/dtml/GRUF_overview.zpt b/dtml/GRUF_overview.zpt
new file mode 100644
index 0000000..9db51db
--- /dev/null
+++ b/dtml/GRUF_overview.zpt
@@ -0,0 +1,208 @@
+<div tal:replace="nothing"> -*- mode: dtml; dtml-top-element: "body" -*- </div>
+<div tal:replace="structure here/manage_page_header"></div>
+<div tal:replace="structure here/manage_tabs"></div>
+
+
+<!-- Help text -->
+<p class="form-help">Here is an overview of users, their groups and roles. See the legend below.</p>
+
+<h4>About GRUF</h4>
+  <ol>
+    <p class="form-help">
+      You are currently running <strong>GRUF v.<span tal:replace="here/getGRUFVersion">version</span></strong><br />
+      Information, latest version, documentation... see 
+      <a target="_blank" href="http://ingeniweb.sourceforge.net/Products/GroupUserFolder">The GRUF Webpage</a>.
+    </p>
+  </ol>
+
+<!-- Wizards -->
+<h4>What do you want to do from here ?</h4>
+<ol>
+    <p class="form-help">
+      Here is the list of common actions you can do with GRUF. <br />
+      Just follow the links !
+    </p>
+
+
+    <table width="90%">
+        <tr>
+          <th class="list-header" valign="top" width="30%">
+            I want to set the place where
+            my users/groups are stored.
+          </th>
+          <td class="list-item" valign="top" bgcolor="#EEEEEE">
+            <p>
+              Within GRUF, users are stored in one or more <i>User Source</i>. A source can be any
+              valid Zope User Folder derived object (for example the standard Zope User Folder but also LDAPUserFolder,
+              SimpleUserFolder, ...).<br />
+              Use the <strong><a href="manage_GRUFSources">sources tab</a></strong> to manage your user sources.
+            </p>
+          </td>
+        </tr>
+        <tr>
+          <th class="list-header" valign="top">
+            I want to connect my LDAP server to Plone
+          </th>
+          <td class="list-item" valign="top" bgcolor="#EEEEEE">
+            <p>
+              There are a few tasks you can automate with Plone (2.0.x or 2.1) in the <strong><a href="manage_wizard">LDAP Wizard</a></strong> section.
+            </p>
+          </td>
+        </tr>
+        <tr>
+          <th class="list-header" valign="top">
+            I want to create some users or some groups.
+          </th>
+          <td class="list-item" valign="top" bgcolor="#EEEEEE">
+            <p>
+              To create groups, use the <strong><a href="manage_groups">groups tab</a></strong><br />
+              If you want to create users, you can use the <strong><a href="manage_users">users tab</a></strong>
+            </p>
+          </td>
+        </tr>
+        <tr>
+          <th class="list-header" valign="top">
+            I need to check my website's security.
+          </th>
+          <td class="list-item" valign="top" bgcolor="#EEEEEE">
+            <p>
+              The <strong><a href="manage_audit">audit tab</a></strong> is certainly what you are looking for.<br />
+              With this tool you can issue personalized reports about your website security rules.
+            </p>
+          </td>
+        </tr>
+    </table>
+</ol>
+
+
+<!-- Users / Roles / Groups tabular view -->
+<h4>Users overview</h4>
+<ol>
+      <p class="form-help">
+      There may be more users in your system than the ones presented here. 
+      See the <a href="manage_users">users tab</a> for more information.
+      </p>
+  <tal:block 
+    tal:define="
+    global batch python:test(request.has_key('start'), 0, here.listUsersBatchTable());
+    global start python:request.get('start', 0);
+    "
+    ></tal:block>
+
+    <tal:block tal:condition="batch">
+      <p class="form-help">
+        To avoid too much overhead on this display, it is not possible to show more than 100 users
+        per screen. Please click the range of users you want to see in the table below.
+      </p>
+
+      <table tal:replace="nothing" cellpadding="2" width="90%">
+          <tr tal:repeat="rows batch">
+            <td  width="25%" bgcolor="#DDDDDD" tal:repeat="col rows">
+              <table height="100%" width="100%" bgcolor="#FFFFFF">
+                  <tr>
+                    <td nowrap="1" align="center">
+                      <div class="list-item">
+                        <a href="" 
+                           tal:attributes="href python:'%s/manage_overview?start:int=%d' % (here.absolute_url(), col[0])">
+                          <img src="img_user" border="0" align="middle"><span tal:replace="python:col[2]" /> ...
+                            <span tal:replace="python:col[3]" />
+                        </a>
+                      </div>
+                    </td>
+                  </tr>
+              </table>
+            </td>
+          </tr>
+      </table>
+    </tal:block>
+    
+    <tal:block tal:condition="not:batch">
+      <tal:block tal:define="users python:here.getUsersBatch(start)">
+        <table width="90%" tal:condition="users">
+            <tr class="list-header">
+              <th>User</th>
+              <th>Group(s)</th>
+              <th>Role(s)</th>
+            </tr>
+            
+        <tal:block tal:repeat="user users">
+          <tr class="row-hilite"
+              tal:define="
+            label_groups python:user.getGroups();
+            label_groups_no_recurse python:user.getGroups(no_recurse = 1);
+            label_groups_recurse python:filter(lambda x: x not in label_groups_no_recurse, label_groups);
+            groups_no_recurse python:map(lambda x: here.getUser(x), label_groups_no_recurse);
+            groups_recurse python:map(lambda x: here.getUser(x), label_groups_recurse);
+            roles python:filter(lambda x: x not in ('Authenticated', 'Shared'), user.getRoles());
+            roles_no_recurse python:filter(lambda x: x not in ('Authenticated', 'Shared'), user.getUserRoles());
+            roles_recurse python:filter(lambda x: x not in roles_no_recurse, roles)"
+            >
+            <td>
+              <div class="list-item">
+                <img src="img_user">&nbsp;<strong tal:content="structure user/asHTML"></strong>
+              </div>
+            </td>
+            <td>
+              <!-- Groups -->
+              <div class="list-item">
+                <span tal:repeat="group groups_no_recurse"
+                  ><span tal:replace="structure group/asHTML"></span><span tal:condition="not:repeat/group/end">, </span></span
+                  ><span tal:condition="python:groups_no_recurse and groups_recurse">,</span>
+                <span tal:repeat="group groups_recurse" >
+                  <span tal:replace="structure python:group.asHTML(implicit=1)"></span><span tal:condition="not:repeat/group/end">, </span>
+                </span>
+              </div>
+            </td>
+            <td>
+              <!-- Roles -->
+              <div class="list-item">
+                <span tal:repeat="role roles_no_recurse" >
+                  <font color=""
+                        tal:attributes="color here/role_color">
+                    <span tal:replace="role"></span><span tal:condition="not:repeat/role/end">, </span>
+                  </font>
+                </span>
+                <span tal:condition="python:roles_no_recurse and roles_recurse">, </span>
+                <span tal:repeat="role roles_recurse" >
+                  <font color=""
+                        tal:attributes="color here/role_color">
+                    <i><span tal:replace="role"></span></i><span tal:condition="not:repeat/role/end">, </span>
+                  </font>
+                </span>
+              </div>
+            </td>
+          </tr>
+        </tal:block>
+      </table>
+        
+        <table tal:condition="not:users">
+            <tr>
+              <td class="row-hilite" colspan="3">
+                <p>
+                  No user available. This happens either if you have no users defined or if
+                  the underlying UserFolder cannot retreive the entire users list.
+                </p>
+              </td>
+            </tr>
+        </table>
+      </tal:block>
+    </tal:block>
+</ol>
+
+
+<!-- Legend -->
+<h4>Legend</h4>
+<ol>
+    <p>
+      Just to make things clearer: <br>
+      &nbsp;<font color="" tal:attributes="color here/user_color"><img src="img_user">&nbsp;Users appear this way</font><br />
+      &nbsp;<font color="" tal:attributes="color here/group_color"><img src="img_group">&nbsp;Groups appear this way</font><br />
+    &nbsp;<font color="" tal:attributes="color here/group_color"><i><img src="img_group">&nbsp;Nested groups (ie. groups inside groups) appear this way</i></font><br />
+      &nbsp;<font color="" tal:attributes="color here/role_color">User roles appear this way</font><br />
+      &nbsp;<font color="" tal:attributes="color here/role_color"><i>Nested roles (ie. roles set on a group a user or group belongs to) appear this way</i></font><br />
+    </p>
+    <p class="form-help">In management forms, items only non-italic items can be set/unset directly. Italic items are dependencies.</p>
+</ol>
+
+<dtml-var manage_page_footer>
+
diff --git a/dtml/GRUF_user.zpt b/dtml/GRUF_user.zpt
new file mode 100644
index 0000000..56937b7
--- /dev/null
+++ b/dtml/GRUF_user.zpt
@@ -0,0 +1,247 @@
+  <h1 tal:replace="structure here/manage_page_header">Header</h1>
+  <h2 tal:define="manage_tabs_message options/manage_tabs_message | nothing"
+    tal:replace="structure here/manage_tabs">Tabs</h2>
+  <tal:block tal:define="
+    global user python:here.getUser(request.username); 
+    kind python:test(user.isGroup(), 'Group', 'User'); 
+    icon python:test(user.isGroup(), 'img_group', 'img_user');
+    color python:test(user.isGroup(), here.acl_users.group_color, here.acl_users.user_color);
+    ">
+    
+    <br />
+      
+      <div class="std-text">&nbsp;
+        <img src="" alt="kind" tal:attributes="src icon; alt kind" align="middle">
+          <strong tal:condition="user/isGroup" tal:content="structure string:${user/asHTML} (Group)">toto group management</strong>
+    <strong tal:condition="not: user/isGroup" tal:content="structure string:${user/asHTML} (User)">toto user management</strong>
+  </div>
+
+
+    <h4>Settings</h4>
+    
+    <form action="" method="POST" tal:attributes="action here/absolute_url">
+      <tal:block tal:define="
+        label_groups python:user.getGroups();
+        label_groups_no_recurse python:user.getImmediateGroups();
+        label_groups_recurse python:filter(lambda x: x not in label_groups_no_recurse, label_groups);
+        groups_no_recurse python:map(lambda x: here.getUser(x), label_groups_no_recurse);
+        groups_recurse python:map(lambda x: here.getUser(x), label_groups_recurse);
+        roles python:filter(lambda x: x not in ('Authenticated', 'Shared'), user.getRoles());
+        roles_no_recurse python:filter(lambda x: x not in ('Authenticated', 'Shared'), user.getUserRoles());
+        roles_recurse python:filter(lambda x: x not in roles_no_recurse, roles)
+        ">
+        <ol>
+          <table cellspacing="10">
+            <tr>
+              <!-- User info -->
+              <input type="hidden" name="user" value="" tal:attributes="value user/getUserName">
+                <td valign="top">
+                  <table bgcolor="#EEEEEE">
+                    <tr>
+                      <th class="list-header"><span tal:replace="kind" /> name</th>
+                      <td class="list-item">
+                        <strong tal:content="structure user/asHTML">
+                        </strong>
+                      </td>
+                    </tr>
+                    <tr>
+                      <th class="list-header">Member of groups</th>
+                      <td class="list-item">
+                        <span tal:repeat="group groups_no_recurse" >
+                          <span tal:replace="structure group/asHTML"></span><span tal:condition="not:repeat/group/end">, </span>
+                        </span>
+                      </td>
+                    </tr>
+                    <tr>
+                      <th class="list-header">Implicitly member of groups</th>
+                      <td class="list-item">
+                        <span tal:repeat="group groups_recurse" >
+                          <span tal:replace="structure python:group.asHTML(implicit=1)"></span><span tal:condition="not:repeat/group/end">, </span>
+                        </span>
+                      </td>
+                    </tr>
+                    <tr>
+                      <th class="list-header">Has roles</th>
+                      <td class="list-item">
+                        <div class="list-item">
+                          <span tal:repeat="role roles_no_recurse" >
+                            <font color=""
+                                  tal:attributes="color here/role_color">
+                              <span tal:replace="role"></span><span tal:condition="not:repeat/role/end">, </span>
+                            </font>
+                          </span>
+                        </div>
+                      </td>
+                    </tr>
+                    <tr>
+                      <th class="list-header">Implicitly has roles (from groups)</th>
+                      <td class="list-item">
+                        <div class="list-item">
+                          <span tal:repeat="role roles_recurse" >
+                            <font color=""
+                                  tal:attributes="color here/role_color">
+                              <i><span tal:replace="role"></span></i><span tal:condition="not:repeat/role/end">, </span>
+                            </font>
+                          </span>
+                        </div>
+                      </td>
+                    </tr>
+                    <tr>
+                      <td colspan="4" align="center"><br>
+                          <input type="submit" name="changeUser:method" value="Change" />
+                            <tal:block tal:replace="nothing">
+                              XXX have to make this work again
+                              &nbsp;
+                              <input type="submit" name="deleteUser:method" value="Delete" />
+                                <br>&nbsp;
+                            </tal:block>
+                      </td>
+                    </tr>
+                  </table>
+                </td>
+
+                <td valign="middle">
+                  =>
+                </td>
+
+                <td valign="top">
+                  <table  bgcolor="#EEEEEE">
+                    <tr>
+                      <td>
+                        <!-- Groups selection -->
+                        <table width="100%">
+                          <tr class="list-header">
+                            <th colspan="2">Set groups</th>
+                          </tr>
+                          
+                          <tr tal:repeat="group here/getGroups">
+                            <td>
+                              <input type="checkbox" name="groups:list" value="" checked=""
+                                     tal:condition="python: group.getUserName() != user.getUserName()"
+                                tal:attributes="
+                                value group/getUserName; 
+                                checked python:test(group.getId() in user.getGroupIds(), '1', '')">
+                            </td>
+                            <td>
+                              <div class="list-item" tal:content="structure group/asHTML"></div>
+                            </td>
+                          </tr>
+                        </table>
+
+                        <br>
+
+                          <!-- Roles selection -->
+                          <table width="100%">
+                            <tr class="list-header">
+                              <th colspan="2">Set roles</th>
+                            </tr>
+                            
+                            <tr tal:repeat="role here/valid_roles">
+                              <td tal:condition="python:role not in ('Authenticated', 'Anonymous', 'Shared')">
+                                <input type="checkbox" name="roles:list" value="" checked="" 
+                                       tal:attributes="value role; checked python:test(role in user.getUserRoles(), '1', '')">
+                              </td>
+                              <td tal:condition="python:role not in ('Authenticated', 'Anonymous', 'Shared')">
+                                <div class="list-item"><font color="" tal:attributes="color here/role_color" tal:content="role">Role</font></div>
+                              </td>
+                            </tr>
+                          </table>
+                      </td>
+                    </tr>
+                  </table>
+        </ol>
+      </tal:block>
+
+    </form>
+
+
+    <tal:block tal:condition="nothing|user/isGroup">
+      XXX TODO ! XXX
+      <h4>Group contents</h4>
+      <ol>      
+      <table bgcolor="#EEEEEE" tal:define="content python:list(user.getImmediateGroups())">
+        <tr class="list-header">
+          <th>Group/User</th>
+          <th class="list-header">Member <br>of groups</th>
+          <th class="list-header">Implicitly <br>member <br>of groups</th>
+          <th class="list-header">Has roles</th>
+          <th class="list-header">Implicitly <br>has roles <br>(from groups)</th>
+        </tr>
+        
+        <tr 
+            tal:repeat="user python:content" class="" tal:attributes="class python:test(path('repeat/user/odd'), 'row-hilite', 'row-normal')"
+          >
+          <div tal:define="
+            label_groups python:user.getGroups();
+            label_groups_no_recurse python:user.getImmediateGroups();
+            label_groups_recurse python:filter(lambda x: x not in label_groups_no_recurse, label_groups);
+            groups_no_recurse python:map(lambda x: here.getUser(x), label_groups_no_recurse);
+            groups_recurse python:map(lambda x: here.getUser(x), label_groups_recurse);
+            roles python:filter(lambda x: x not in ('Authenticated', 'Shared'), user.getRoles());
+            roles_no_recurse python:filter(lambda x: x not in ('Authenticated', 'Shared'), user.getUserRoles());
+            roles_recurse python:filter(lambda x: x not in roles_no_recurse, roles);"
+            tal:omit-tag="">
+            <td class="list-item">
+              <span tal:repeat="group groups_no_recurse" >
+                <span tal:replace="structure group/asHTML"></span><span tal:condition="not:repeat/group/end">, </span>
+              </span>
+            </td>
+            <td class="list-item">
+              <span tal:repeat="group groups_recurse" >
+                <span tal:replace="structure python:user.asHTML(implicit=1)"></span><span tal:condition="not:repeat/group/end">, </span>
+              </span>
+            </td>
+            <td class="list-item">
+              <div class="list-item">
+                <span tal:repeat="role roles_no_recurse" >
+                  <font color=""
+                        tal:attributes="color here/role_color">
+                    <span tal:replace="role"></span><span tal:condition="not:repeat/role/end">, </span>
+                  </font>
+                </span>
+              </div>
+            </td>
+            <td class="list-item">
+              <div class="list-item">
+                <span tal:repeat="role roles_recurse" >
+                  <font color=""
+                        tal:attributes="color here/role_color">
+                    <i><span tal:replace="role"></span></i><span tal:condition="not:repeat/role/end">, </span>
+                  </font>
+                </span>
+              </div>
+            </td>
+          </div>
+        </tr>
+      </table>
+      </ol>
+    </tal:block>
+
+
+    <h4>Instructions</h4>
+    <ol>
+      <p class="form-help">
+        To change roles for a <span tal:replace="kind" />, 
+          select the roles you want to give it and the groups it belongs to in the forms on the right and click "Change".<br />
+      </p>
+    </ol>
+
+    <h4>Important notice / disclaimer</h4>
+    
+    <ol>
+      <p class="form-help">
+        This form uses the regular Zope Security API from the underlying user folders. However, you may experience problems with some
+        of them, especially if they are not tuned to allow user adding. For example, an LDAPUserFolder can be configured to disable
+        users management. In case this form doesn't work, you'll have to do things by hand within the 'Users' and 'Groups' GRUF folders.
+      </p>
+
+      <p class="form-help">
+        This is not a GRUF limitation ! :-)
+      </p>
+    </ol>
+
+  </tal:block>
+
+  <h1 tal:replace="structure here/manage_page_footer">Footer</h1>
+
+  
diff --git a/dtml/GRUF_users.zpt b/dtml/GRUF_users.zpt
new file mode 100644
index 0000000..b80223d
--- /dev/null
+++ b/dtml/GRUF_users.zpt
@@ -0,0 +1,340 @@
+    <h1 tal:replace="structure here/manage_page_header">Header</h1>
+    <h2 tal:define="manage_tabs_message options/manage_tabs_message | nothing"
+      tal:replace="structure here/manage_tabs">Tabs</h2>
+
+    <h4>Users sources</h4>
+    <ol>
+      <table cellspacing="10" width="90%" tal:define="groups here/getGroups">
+        <tr tal:repeat="source here/listUserSourceFolders">
+          <th class="list-header">Users source #<span tal:replace="repeat/source/number">1</span></th>
+          <td bgcolor="#EEEEEE" tal:condition="source/isValid"
+	   tal:define="meta_type source/acl_users/meta_type|nothing;
+	               title_or_id source/acl_users/title|meta_type;">
+            <img src="" 
+	    tal:attributes="src source/acl_users/icon;
+	    title meta_type">
+	     &nbsp;
+            <a href="" 
+	    tal:attributes="
+	    href string:${source/acl_users/absolute_url}/manage_workspace;
+	    title meta_type" 
+	    tal:content="title_or_id">Title</a>
+            <tal:block condition="not:source/isEnabled">
+              <font color="red"><i>(disabled)</i></font>
+            </tal:block>
+          </td>
+          <td bgcolor="#EEEEEE" tal:condition="not:source/isValid">
+            <font color="red"><strong><i>(invalid or broken)</i></strong></font>
+          </td>
+        </tr>
+      </table>
+    </ol>
+
+    <tal:block 
+      tal:condition="not: search_userid"
+      tal:define="global search_userid request/search_userid|nothing"
+      >
+      <tal:block tal:define="global users here/getPureUsers">
+      </tal:block>
+    </tal:block>
+    <tal:block tal:condition="search_userid">
+      <tal:block 
+        tal:define="
+        uid search_userid;
+        global users python:[ here.getUser(uid) for uid in here.searchUsersById(uid) if uid ];
+        ">
+      </tal:block>
+    </tal:block>
+    
+    <h4>Search</h4>
+    <ol>
+      <div 
+        tal:define="have_users python: len(users);">
+        <div class="list-item" tal:condition="python: not have_users and not search_userid">
+          No user available. This happens either if you have no users defined or if
+          the underlying UserFolder cannot retreive the entire users list (for example, LDAPUserFolder
+          is limited in results size).
+        </div>
+        <div class="list-item">
+          Some more users may be available but do not show up there.. This happens if
+          the underlying UserFolder cannot retreive the entire users list (for example, 
+          LDAPUserFolder is limited in results size and will return only cached users).
+        </div>
+        <div class="list-item">
+          You can search users giving part of their id with this form.
+        </div>
+        <div>
+          <form action="" tal:attributes="action template/absolute_url">
+            <b>User name:</b> 
+            <input name="search_userid" type="text" tal:attributes="value search_userid" />
+            <input type="submit" value="Search" />
+          </form>
+        </div>
+      </div>
+    </ol>
+
+    <h4 tal:condition="not: search_userid">Users management</h4>
+    <h4 tal:condition="search_userid">Search results</h4>
+    <form action="" method="POST" tal:attributes="action request/URL1">
+      <ol>
+        <div tal:condition="python: not users and search_userid">
+          No user found.
+        </div>
+        <table cellspacing="10" width="90%">
+          <tr>
+            <!-- Users selection -->
+            <td valign="top">
+              <table bgcolor="#EEEEEE" width="100%">
+                <tr class="list-header" tal:condition="users">
+                  <th>&nbsp;</th>
+                  <th>User</th>
+                  <th class="list-header">Member <br>of groups</th>
+                  <th class="list-header">Implicitly <br>member of*</th>
+                  <th class="list-header">Has roles</th>
+                  <th class="list-header">Implicitly <br>has roles**</th>
+                </tr>
+                
+                <tr 
+                  tal:repeat="user users"
+                  class="" 
+                  tal:attributes="class python:test(path('repeat/user/odd'), 'row-hilite', 'row-normal')"
+                  >
+                  <div tal:condition="user"
+                    tal:omit-tag=""
+                    x:comment="We ignore empty/invalid users"
+                    >
+                    <div tal:define="
+                      label_groups python:user.getGroups();
+                      label_groups_no_recurse python:user.getGroups(no_recurse = 1);
+                      label_groups_recurse python:filter(lambda x: x not in label_groups_no_recurse, label_groups);
+                      groups_no_recurse python:map(lambda x: here.getUser(x), label_groups_no_recurse);
+                      groups_recurse python:map(lambda x: here.getUser(x), label_groups_recurse);
+                      roles python:filter(lambda x: x not in ('Authenticated', 'Shared'), user.getRoles());
+                      roles_no_recurse python:filter(lambda x: x not in ('Authenticated', 'Shared'), user.getUserRoles());
+                      roles_recurse python:filter(lambda x: x not in roles_no_recurse, roles);"
+                      tal:omit-tag="">
+                      <td><div class="list-item"><input type="checkbox" name="users:list" value="" tal:attributes="value user"></td>
+                      <td>
+                        <div class="list-item">
+                          <img src="img_user" />
+                          <strong tal:content="structure user/asHTML">
+                          </strong>
+                      </td>
+                      <td class="list-item">
+                        <span tal:repeat="group groups_no_recurse" >
+                          <span tal:replace="structure group/asHTML"></span><span tal:condition="not:repeat/group/end">, </span>
+                        </span>
+                      </td>
+                      <td class="list-item">
+                        <span tal:repeat="group groups_recurse" >
+                          <span tal:replace="structure python:group.asHTML(implicit=1)"></span><span tal:condition="not:repeat/group/end">, </span>
+                        </span>
+                      </td>
+                      <td class="list-item">
+                        <div class="list-item">
+                          <span tal:repeat="role roles_no_recurse" >
+                            <font color=""
+                              tal:attributes="color here/role_color">
+                              <span tal:replace="role"></span><span tal:condition="not:repeat/role/end">, </span>
+                            </font>
+                          </span>
+                        </div>
+                      </td>
+                      <td class="list-item">
+                        <div class="list-item">
+                          <span tal:repeat="role roles_recurse" >
+                            <font color=""
+                              tal:attributes="color here/role_color">
+                              <i><span tal:replace="role"></span></i><span tal:condition="not:repeat/role/end">, </span>
+                            </font>
+                          </span>
+                        </div>
+                      </td>
+                    </div>
+                  </div>
+                </tr>
+                <tr>
+                  <td colspan="5">
+                    <input type="submit" name="deleteUsers:method" value="Delete" /><br />
+                    You can also change group / roles with the form below.
+                  </td>
+                </tr>
+              </table>
+
+
+              <div tal:condition="python: not search_userid"
+                tal:define="have_users python: len(users);">
+                <div class="list-item" tal:condition="not: have_users">
+                  No user available. This happens either if you have no users defined or if
+                  the underlying UserFolder cannot retreive the entire users list (for example, LDAPUserFolder
+                  is limited in results size).<br />
+                  Use the above search form to search for specific users.
+                </div>
+              </div>
+      </ol>
+
+      <!-- New user -->
+      <h4>User creation</h4>
+      <ol>
+        <table>
+          <tr>
+            <td><div class="list-item">&nbsp;</div></td>
+            <td>
+              <div class="list-item">Batch user creation list:</div>
+            </td>
+          </tr>
+          <tr>
+            <td><div class="list-item">&nbsp;</div></td>
+            <td>
+              <div class="list-item">
+                <textarea name="new_users:lines" cols="20" rows="3"></textarea>
+              </div>
+            </td>
+            <td colspan="4">
+              <div class="list-item" valign="top">
+                Newly created users will be affected groups and roles according to the table below.
+              </div>
+            </td>
+          </tr>
+          <tr>
+            <td><div class="list-item">&nbsp;</div></td>
+            <td>
+              <div class="list-item">Default password:</div>
+            </td>
+          </tr>
+          <tr>
+            <td><div class="list-item">&nbsp;</div></td>
+            <td>
+              <div class="list-item">
+                <input name="default_password:string" size="20" />
+              </div>
+            </td>
+            <td colspan="4">
+              <div class="list-item">
+                Fill in this field to specify a default password for new users, 
+                or leave it empty to let GRUF generate random ones.
+              </div>
+            </td>
+          </tr>
+          <tr>
+            <td colspan="2" align="center">
+              <input type="submit" name="changeOrCreateUsers:method" value="Create" />
+            </td>
+          </tr>
+        </table>
+      </ol>
+      
+
+      <h4>Roles / groups management</h4>
+      <ol>
+      <table>
+        <tr>
+          <td align="center">
+            <div class="list-item">
+              Select one or more users in the upper table, select one or more groups / roles in the table below
+              and click "Change" to affect groups / roles to these users.
+            </div>
+          </td>
+        </tr>
+        <tr>
+          <td valign="top" align="center" colspan="6">
+            <table  bgcolor="#EEEEEE">
+              <tr>
+                <td valign="top">
+                  <!-- Groups selection -->
+                  <table width="100%">
+                    <tr class="list-header">
+                      <th colspan="2">Affect groups</th>
+                    </tr>
+                    
+                    <tr tal:repeat="group here/getGroups">
+                      <td>
+                        <input type="checkbox" name="groups:list" value="" tal:attributes="value group">
+                      </td>
+                      <td>
+                        <div class="list-item" tal:content="structure group/asHTML"></div>
+                      </td>
+                    </tr>
+                    
+                    <!-- "(None)" item -->
+                    <tr>
+                      <td><div class="list-item"><input type="checkbox" name="nested_groups:list" value="__None__"></div></td>
+                      <td><div class="list-item"><i>(None)</i></div></td>
+                    </tr>
+                  </table>
+                </td>
+                <td valign="top">
+                  <!-- Roles selection -->
+                  <table width="100%">
+                    <tr class="list-header">
+                      <th colspan="2">Affect roles</th>
+                    </tr>
+                    
+                    <tr tal:repeat="role here/valid_roles">
+                      <td tal:condition="python:role not in ('Authenticated', 'Anonymous', 'Shared')">
+                        <input type="checkbox" name="roles:list" value="" tal:attributes="value role">
+                      </td>
+                      <td tal:condition="python:role not in ('Authenticated', 'Anonymous', 'Shared')">
+                        <div class="list-item"><font color="" tal:attributes="color here/role_color" tal:content="role">Role</font></div>
+                      </td>
+                    </tr>
+                    
+                    <!-- "(None)" item -->
+                    <tr>
+                      <td><div class="list-item"><input type="checkbox" name="roles:list" value="__None__"></div></td>
+                      <td><div class="list-item"><i>(None)</i></div></td>
+                    </tr>
+                  </table>
+                </td>
+              </tr>
+              <tr>
+                <td colspan="2" align="middle"><input type="submit" name="changeOrCreateUsers:method" value="Change" /></td>
+            </table>
+          </td>
+        </tr>
+      </table>
+
+      <p class="form-help">
+        If you do not select a role, roles won't be reset for the selected users.<br />
+        If you do not select a group, groups won't be reset for the selected users.<br />
+        To explicitly reset groups or roles, just click the "(None)" entry (and no other entry).
+      </p>
+
+      <p class="form-help">
+        * According to the groups inheritance, this group is also recursively member of these groups. <br />This is what we call nested groups.
+      </p>
+      <p class="form-help">
+        ** Accorded to the groups inheritance, this group also has these roles - even if they are not defined explicitly on it.
+      </p>
+
+    </ol>
+    </form>
+
+
+    <h4>Instructions</h4>
+    <ol>
+        <p class="form-help">
+          To change roles for one or several users, select them in the left form, 
+          select the roles you want to give them and the groups they belong to in the forms on the right and click "Change".<br />
+          You can also create one or several users by filling the text area (one user per line). 
+          The "Change" button will create them with the roles and group affectation you've selected. 
+          A random password will be generated for them, and it will be shown in a page so that you can click/paste them somewhere.<br />
+          If you want to kill some users, you can delete them by selecting them and clicking the "Delete" button.
+        </p>
+    </ol>
+
+    <h4>Important notice / disclaimer</h4>
+    
+    <ol>
+        <p class="form-help">
+          This form uses the regular Zope Security API from the underlying user folders. However, you may experience problems with some
+          of them, especially if they are not tuned to allow user adding. For example, an LDAPUserFolder can be configured to disable
+          users management. In case this form doesn't work, you'll have to do things by hand within the 'Users' and 'Groups' GRUF folders.
+        </p>
+
+        <p class="form-help">
+          This is not a GRUF limitation ! :-)
+        </p>
+    </ol>
+
+    <h1 tal:replace="structure here/manage_page_footer">Footer</h1>
diff --git a/dtml/GRUF_wizard.zpt b/dtml/GRUF_wizard.zpt
new file mode 100644
index 0000000..6303879
--- /dev/null
+++ b/dtml/GRUF_wizard.zpt
@@ -0,0 +1,127 @@
+    <h1 tal:replace="structure here/manage_page_header">Header</h1>
+    <h2 tal:define="manage_tabs_message options/manage_tabs_message | nothing"
+      tal:replace="structure here/manage_tabs">Tabs</h2>
+
+    <h4>The LDAP Wizard section</h4>
+    <ol>
+        <p class="form-help">
+          Here's the place where you can perform a few actions with your LDAP configuration.<br />
+          Of course, if you do not plan to use LDAP with Plone, you can move away from here.<br />
+          First of all, here's a little list of links that you may find useful:
+        </p>
+        <ul>
+          <li><a href="http://ingeniweb.sourceforge.net/Products/GroupUserFolder/doc/README-LDAP.html">The official GRUF+LDAPUserFolder documentation</a> (a must-read !)</li>
+          <li><a href="http://www.dataflake.org/software/ldapuserfolder">The official LDAPUserFolder page</a></li>
+        </ul>
+    </ol>
+
+
+    <tal:block define="
+      have_LDAPUF python: 'LDAPUserFolder' in [ s[0] for s in here.listAvailableUserSources() ];
+      LDAPUF_installed here/hasLDAPUserFolderSource;
+      areLUFGroupsLocal python: LDAPUF_installed and here.areLUFGroupsLocal();
+      ">
+
+
+      <tal:block condition="python: not have_LDAPUF">
+        <h4>LDAPUserFolder status</h4>
+        <ol>
+            <p>
+              Looks like you don't have LDAPUserFolder installed.<br />
+              Please download the latest version from <a href="http://www.dataflake.org/software/ldapuserfolder">The official LDAPUserFolder page</a>.
+            </p>
+        </ol>
+      </tal:block>
+
+      <tal:block condition="python: have_LDAPUF and not LDAPUF_installed">
+        <h4>LDAPUserFolder status</h4>
+        <ol>
+            <p>
+              It seems that you don't have LDAPUserFolder installed or configured as a source for GRUF.<br />
+              Return to the 'sources' tab and add it.
+            </p>
+        </ol>
+      </tal:block>
+
+      <tal:block condition="python: have_LDAPUF and LDAPUF_installed">
+        <h4>Groups status</h4>
+        <ol>
+          <tal:block condition="areLUFGroupsLocal">
+            Your groups are reported to be stored in ZODB.<br />
+            You can create groups with <a href="manage_groups">this link</a>.
+            Once you've created groups, don't forget to come back here and see the 'update mapping' section below.<br />
+            <tal:block condition="here/haveLDAPGroupFolder">
+
+            <font color="red">
+              <dl>
+                <dt><b>WARNING</b></dt>
+                <dd>It seems that your groups source is LDAPGroupFolder.<br />
+                  This is not recommanded since this groups source is only for managing groups when
+                  they are stored on your LDAP Server. Please go back to the sources tab and change it.<br />
+                  A regular UserFolder instead should do it.
+                </dd>
+              </dl>
+            </font>
+
+            </tal:block>
+          </tal:block>
+          <tal:block condition="not: areLUFGroupsLocal">
+            Your groups are reported to be stored in your LDAP database.
+          </tal:block>
+        </ol>
+
+        <h4>Groups mapping</h4>
+        <ol>
+            <p class="form-help">
+              To manage groups with a LDAPUserFolder, one must <b>map</b> LDAP groups to Zope Roles.<br />
+              You can do this mapping manually or click this button to have it done automatically.<br />
+              Please not that any previously existing ldap-group - to - zope-role mapping may be lost.
+            </p>
+
+        <tal:block condition="here/getInvalidMappings">
+          <p class="form-help">
+            <strong>You must do this even if your groups are not stored on your LDAP database</strong>
+          </p>
+          <p class="form-help">
+            To help you in this task, you can have a look at the following table, which summs up<br />
+            the mappings done (or not done!) in LDAPUserFolder.
+          </p>
+
+          <font color="red">
+            <dl>
+              <dt><b>WARNING</b></dt>
+              <dd>Your mapping doesn't look good... You surely need to click the 'update mapping' button.<br />
+              </dd>
+            </dl>
+          </font>
+        </tal:block>
+
+        <tal:block condition="not: here/getInvalidMappings">
+          Your mapping looks good. It's not necessary to update it.
+        </tal:block>
+
+        <table bgcolor="#FFFFFF">
+          <thead>
+            <th class="list-header">LDAP group</th>
+            <th class="list-header">is mapped to</th>
+            <th class="list-header">GRUF group</th>
+          </thead>
+          <tbody>
+            <tr tal:repeat="group_info here/listLDAPUserFolderMapping">
+              <td bgcolor="#EEEEEE" tal:content="python:group_info[1]"></td>
+              <td align="center" bgcolor="#EEEEEE">
+                =>
+              </td>
+              <td bgcolor="#EEEEEE" tal:content="python:group_info[0]"></td>
+            </tr>
+          </tbody>
+        </table>
+        <form action="updateLDAPUserFolderMapping">
+          <input type="submit" value="Update LDAP mapping" />
+        </form>
+      </ol>
+      </tal:block>
+      
+    </tal:block>
+
+    <h1 tal:replace="structure here/manage_page_footer">Footer</h1>
diff --git a/dtml/addLDAPGroupFolder.dtml b/dtml/addLDAPGroupFolder.dtml
new file mode 100755
index 0000000..7a92739
--- /dev/null
+++ b/dtml/addLDAPGroupFolder.dtml
@@ -0,0 +1,55 @@
+<dtml-comment> -*- mode: dtml; dtml-top-element: "body" -*- </dtml-comment>
+<dtml-var manage_page_header>
+
+<dtml-var "manage_form_title(this(), _,
+           form_title='Add LDAP Group Folder',
+           )">
+
+<p class="form-help">
+  Add a new LDAPGroupFolder with this form.
+</p>
+
+<form action="manage_addLDAPGroupFolder" method="POST">
+    <table cellspacing="0" cellpadding="3">
+
+      <tr>
+        <td align="left" valign="TOP"><div class="form-optional">
+          Title
+        </div></td>
+        <td align="left" valign="TOP"><div class="form-element">
+          <input type="text" name="title" size="40" />
+        </div></td>
+      </tr>
+
+      <tr>
+        <td align="left" valign="TOP"><div class="form-label">LDAP User Folder</div></td>
+        <td align="left" valign="TOP"><div class="form-element">
+         <select name="luf">
+          <dtml-in "aq_parent.listUserSourceFolders()">
+           <dtml-with getUserFolder>
+            <dtml-if expr="meta_type=='LDAPUserFolder'">
+              <dtml-let luf_path="_.string.join( getPhysicalPath(), '/' )">
+              <dtml-let parentfolderid="aq_parent.id">
+                <option value="&dtml-parentfolderid;">&dtml-luf_path; (&dtml-meta_type;)</option>
+              </dtml-let>
+              </dtml-let>
+            </dtml-if>
+           </dtml-with>
+          </dtml-in>
+   
+        </div></td>
+      </tr>
+
+      <tr>
+        <td>&nbsp;</td>
+        <td>
+          <br />
+          <input type="SUBMIT" value=" Add ">
+        </td>
+      </tr>
+    
+    </table>
+</form>
+
+<dtml-var manage_page_footer>
+
diff --git a/dtml/configureGroupsTool.dtml b/dtml/configureGroupsTool.dtml
new file mode 100644
index 0000000..fd0fc6c
--- /dev/null
+++ b/dtml/configureGroupsTool.dtml
@@ -0,0 +1,52 @@
+<dtml-var manage_page_header>
+<dtml-var manage_tabs>
+
+<h2>Control Creation of Group Workspaces</h2>
+<p>
+  If "workspace creation" is on, workspaces will be automatically created (if they do not exist)
+  for groups upon creation.
+</p>
+<form action="toggleGroupWorkspacesCreation" method="post">
+  <p>Workspaces creation is <strong><dtml-var "getGroupWorkspacesCreationFlag() and 'on' or 'off'"></strong></p>
+  <input type="submit" value="Turn Workspace Creation <dtml-var "getGroupWorkspacesCreationFlag() and 'off' or 'on'">" />
+</form>
+
+<h2>Set Workspaces Folder Name</h2>
+<p>
+  Provides the name of the folder or object manager that will contain all group workspaces.
+  It will be created if it does not exist, and must be in the same container as the groups tool.
+  (If you really need a path here, contact the developers.)
+</p>
+<p>
+  The default is <em>GroupWorkspaces</em>.
+</p>
+<form action="manage_setGroupWorkspacesFolder" method="post">
+  <p><strong>Workspace container id</strong> <input type="text" name="id" value="&dtml-getGroupWorkspacesFolderId;" /></p>
+  <input type="submit" value="Change" />
+</form>
+
+
+<h2>Set Group Workspaces Container Type</h2>
+<p>
+  Provide the name of the Type that will be created when creating the first Group Workspace.
+  This object will be at the root of your Plone site, with the id "GroupWorkspaces".
+</p>
+<form action="manage_setGroupWorkspaceContainerType" method="post">
+  <p><strong>Create worspaces container as type</strong> <input type="text" name="type" value="&dtml-getGroupWorkspaceContainerType;" /></p>
+  <input type="submit" value="Change" />
+</form>
+
+
+<h2>Set Group Workspaces Type</h2>
+<p>
+  Provide the name of the Type that will be created to serve as the Group Workspaces. You may use
+  <code>Folder</code>, which is present by default, <code>GroupSpace</code>, which comes
+  with GRUF, or a type of you own definition. See the portal_types tool for types.
+</p>
+<form action="manage_setGroupWorkspaceType" method="post">
+  <p><strong>Create workspaces as type</strong> <input type="text" name="type" value="&dtml-getGroupWorkspaceType;" /></p>
+  <input type="submit" value="Change" />
+</form>
+
+
+<dtml-var manage_page_footer>
diff --git a/dtml/explainGroupDataTool.dtml b/dtml/explainGroupDataTool.dtml
new file mode 100644
index 0000000..c89c385
--- /dev/null
+++ b/dtml/explainGroupDataTool.dtml
@@ -0,0 +1,10 @@
+<dtml-var manage_page_header>
+<dtml-var manage_tabs>
+
+<h3> <code>portal_groupdata</code> Tool </h3>
+
+<p> This tool is responsible for handling the storage of properties on
+user groups.
+</p>
+
+<dtml-var manage_page_footer>
diff --git a/dtml/explainGroupsTool.dtml b/dtml/explainGroupsTool.dtml
new file mode 100644
index 0000000..fe1ae61
--- /dev/null
+++ b/dtml/explainGroupsTool.dtml
@@ -0,0 +1,11 @@
+<dtml-var manage_page_header>
+<dtml-var manage_tabs>
+
+<h3> <code>portal_groups</code> Tool </h3>
+
+<p> This tool provides user-group management functions for use in a
+CMF site.  Its interface provides a common front-end to various group
+implementations.  
+</p>
+
+<dtml-var manage_page_footer>
diff --git a/dtml/groups.dtml b/dtml/groups.dtml
new file mode 100755
index 0000000..432b875
--- /dev/null
+++ b/dtml/groups.dtml
@@ -0,0 +1,224 @@
+<dtml-var manage_page_header>
+
+<dtml-with "_(management_view='Groups')">
+  <dtml-var manage_tabs>
+</dtml-with>
+
+<p class="form-help">
+  This view shows all available groups at the specified branch 
+  and allows deletion and addition.
+</p>
+
+<dtml-in expr="getGroups()">
+
+  <dtml-if name="sequence-start">
+    <form action="&dtml-URL1;" method="post">
+    <table border="0" cellpadding="2" cellspacing="0" width="95%">
+      <tr class="list-header">
+        <td align="left" valign="top" width="16">&nbsp;</td>
+        <td><div class="form-label"> Friendly Name </div></td>
+        <td><div class="form-label"> Object Class </div></td>
+        <td><div class="form-label"> Distinguished Name </div></td>
+      </tr>
+  </dtml-if>
+
+  <dtml-if sequence-odd>
+    <tr class="row-normal">
+  <dtml-else>
+    <tr class="row-hilite">
+  </dtml-if>
+      <td align="left" valign="top" width="16">
+        <input type="checkbox" name="dns:list" value="&dtml-sequence-item;" />
+      </td>
+      <td><div class="form-text">
+        <dtml-var name="sequence-key">
+      </div></td>
+      <td><div class="form-text">
+        <dtml-var expr="getGroupType( _['sequence-item'] )">
+      </div></td>
+      <td><div class="form-text">
+        <dtml-var name="sequence-item" size="60" etc="...">
+      </div></td>
+    </tr>
+
+  <dtml-if name="sequence-end">
+      <tr>
+        <td align="left" valign="top" width="16">&nbsp;</td>
+        <td align="left" valign="top" colspan="2"><div class="form-element">
+          <input class="form-element" type="submit" 
+                 name="manage_deleteGroups:method" 
+                 value="Delete" />
+        </div></td>
+      </tr>
+    </table>
+    </form>
+  </dtml-if>
+
+<dtml-else>
+  <br />
+  <div class="form-label">
+    No groups found. 
+    Please check the settings "Group base DN" and "Groups search scope" 
+    and make sure your LDAP tree contains suitable group records.
+  </div>
+
+</dtml-in>
+
+<p><br></p>
+
+<form action="manage_addGroup" method="post">
+
+  <table cellspacing="0" cellpadding="2" width="95%">
+  
+    <tr class="section-bar">
+      <td colspan="2" align="left" valign="top"><div class="form-label">
+        Add Group
+      </div></td>
+    </tr>
+    
+    <tr>
+      <td colspan="2" align="left" valign="top"><div class="form-text">
+        Add a new group on this LDAP branch by specifying a group name
+        and hitting "Add". 
+        The name is a "friendly" name, meaning it 
+        is not a dn or does not contain any LDAP-sepecific elements.
+      </div></td>
+    </tr>
+    
+    <tr><td colspan="2">&nbsp;</td></tr><tr>
+      <td align="left" valign="absmiddle"><div class="form-label">
+        Group Name
+      </div></td>
+      <td align="LEFT" valign="TOP">
+        <input type="TEXT" name="newgroup_name" size="50" 
+               value="MyGroup" />&nbsp;
+      </td>
+    </tr>
+    
+    <tr>
+      <td align="left" valign="absmiddle"><div class="form-label">
+        Group object class
+      </div></td>
+      <td align="LEFT" valign="TOP">
+        <select name="newgroup_type">
+          <option value="groupOfUniqueNames"> groupOfUniqueNames </option>
+          <option value="groupOfNames"> groupOfNames </option>
+          <option value="accessGroup"> accessGroup </option>
+          <option value="group"> group </option>
+        </select>
+      </td>
+    </tr>
+    
+    <tr>
+      <td align="left" valign="top" colspan="2">
+        <input class="form-element" type="SUBMIT" value=" Add " />
+      </td>
+    </tr>
+  
+  </table>
+
+</form>
+
+<p><hr></p>
+
+<table cellspacing="0" cellpadding="2" width="95%">
+  <tr>
+    <td align="left" valign="top"><div class="form-text">
+      This section determines if LDAP groups are mapped to Zope roles
+      and what they map to.
+    </div></td>
+  </tr>
+</table>
+
+<br />
+
+<dtml-in getGroupMappings>
+
+  <dtml-if name="sequence-start">
+    <form action="&dtml-URL1;" method="post">
+    <table border="0" cellpadding="2" cellspacing="0" width="95%">
+      <tr class="list-header">
+        <td align="left" valign="top" width="16">&nbsp;</td>
+        <td><div class="form-label"> LDAP Group </div></td>
+        <td><div class="form-label"> Zope Role </div></td>
+      </tr>
+  </dtml-if>
+
+  <dtml-if sequence-odd>
+    <tr class="row-normal">
+  <dtml-else>
+    <tr class="row-hilite">
+  </dtml-if>
+      <td align="left" valign="top" width="16">
+        <input type="checkbox" name="group_names:list" value="&dtml-sequence-key;" />
+      </td>
+      <td><div class="form-text"> &dtml-sequence-key; </div></td>
+      <td><div class="form-text"> &dtml-sequence-item; </div></td>
+    </tr>
+
+  <dtml-if name="sequence-end">
+      <tr>
+        <td align="left" valign="top" width="16">&nbsp;</td>
+        <td align="left" valign="top" colspan="2"><div class="form-element">
+          <input class="form-element" type="submit"
+                 name="manage_deleteGroupMappings:method"
+                 value="Delete" />
+        </div></td>
+      </tr>
+    </table>
+  </dtml-if>
+
+<dtml-else>
+  <p>(No group mappings specified at this time.)</p>
+
+</dtml-in>
+
+<p>&nbsp;</p>
+
+<form action="&dtml-URL1;" method="post">
+
+  <table cellspacing="0" cellpadding="2" width="95%">
+  
+    <tr class="section-bar">
+      <td colspan="4" align="left" valign="top"><div class="form-label">
+        Add LDAP group to Zope role mapping
+      </div></td>
+    </tr>
+  
+    <tr>
+      <td align="left" valign="absmiddle"><div class="form-label">
+        Map this LDAP Group... &nbsp; 
+      </div></td>
+      <td align="LEFT" valign="TOP">
+        <select name="group_name">
+          <dtml-in getGroups sort>
+            <option>&dtml-sequence-key;</option>
+          </dtml-in>
+        </select>
+      </td>
+      <td align="left" valign="absmiddle"><div class="form-label">
+        ... to this Zope Role &nbsp;
+      </div></td>
+      <td align="LEFT" valign="TOP">
+        <select name="role_name">
+          <dtml-in expr="_.reorder( valid_roles()
+                                  , without=( 'Anonymous', 'Authenticated', 'Owner' )
+                                  )" sort>
+            <option>&dtml-sequence-item;</option>
+          </dtml-in>
+        </select>
+      </td>
+    </tr>
+  
+    <tr>
+      <td align="left" valign="top" colspan="4">
+        <input class="form-element" type="SUBMIT" value=" Add "
+               name="manage_addGroupMapping:method">
+      </td>
+    </tr>
+  
+  </table>
+
+</form>
+
+<dtml-var manage_page_footer>
diff --git a/dtml/roles.png b/dtml/roles.png
new file mode 100644
index 0000000000000000000000000000000000000000..fd2456b4688db7bbb57ae03eaed95a63bd12ca27
GIT binary patch
literal 26916
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
CfCmi#

literal 0
HcmV?d00001

diff --git a/global_symbols.py b/global_symbols.py
new file mode 100644
index 0000000..83d5321
--- /dev/null
+++ b/global_symbols.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: global_symbols.py 32384 2006-10-27 10:00:55Z encolpe $
+__docformat__ = 'restructuredtext'
+
+import os
+import string
+
+# Check if we have to be in debug mode
+import Log
+if os.path.isfile(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'debug.txt')):
+    Log.LOG_LEVEL = Log.LOG_DEBUG
+    DEBUG_MODE = 1
+else:
+    Log.LOG_LEVEL = Log.LOG_NOTICE
+    DEBUG_MODE = 0
+
+from Log import *
+
+# Retreive version
+if os.path.isfile(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'version.txt')):
+    __version_file_ = open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'version.txt'), 'r', )
+    version__ = __version_file_.read()[:-1]
+    __version_file_.close()
+else:
+    version__ = "(UNKNOWN)"
+
+# Check if we are in preview mode
+PREVIEW_PLONE21_IN_PLONE20_ = 0
+splitdir = os.path.split(os.path.abspath(os.path.dirname(__file__)))
+products = os.path.join(*splitdir[:-1])
+version_file = os.path.join(products, 'CMFPlone', 'version.txt')
+if os.path.isfile(version_file):
+    # We check if we have Plone 2.0
+    f = open(version_file, "r")
+    v = f.read()
+    f.close()
+    if string.find(v, "2.0.") != -1:
+        PREVIEW_PLONE21_IN_PLONE20_ = 1
+
+
+# Group prefix
+GROUP_PREFIX = "group_"
+GROUP_PREFIX_LEN = len(GROUP_PREFIX)
+
+# Batching range for ZMI pages
+MAX_USERS_PER_PAGE = 100
+
+# Max allowrd users or groups to enable tree view
+MAX_TREE_USERS_AND_GROUPS = 100
+
+# Users/groups tree cache time (in seconds)
+# This is used in management screens only
+TREE_CACHE_TIME = 10
+
+# List of user names that are likely not to be valid user names.
+# This list is for performance reasons in ZMI views. If some actual user names
+# are inside this list, management screens won't work for them but they
+# will still be able to authenticate.
+INVALID_USER_NAMES = [
+    'BASEPATH1', 'BASEPATH2', 'BASEPATH3', 'a_', 'URL', 'acl_users', 'misc_',
+    'management_view', 'management_page_charset', 'REQUEST', 'RESPONSE',
+    'MANAGE_TABS_NO_BANNER', 'tree-item-url', 'SCRIPT_NAME', 'n_', 'help_topic',
+    'Zope-Version', 'target',
+    ]
+
+# LDAPUserFolder-specific stuff
+LDAPUF_METHOD = "manage_addLDAPSchemaItem"      # sample method to determine if a uf is an ldapuf
+LDAP_GROUP_RDN = "cn"                           # rdn attribute for groups
+
+LOCALROLE_BLOCK_PROPERTY = "__ac_local_roles_block__"           # Property used for lr blocking
diff --git a/interfaces/.cvsignore b/interfaces/.cvsignore
new file mode 100644
index 0000000..f3d74a9
--- /dev/null
+++ b/interfaces/.cvsignore
@@ -0,0 +1,2 @@
+*.pyc
+*~
diff --git a/interfaces/IUserFolder.py b/interfaces/IUserFolder.py
new file mode 100644
index 0000000..9892274
--- /dev/null
+++ b/interfaces/IUserFolder.py
@@ -0,0 +1,614 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+VOCABULARY:
+
+  - [Pure] User: A user is a user atom who can log itself on, and
+    have additional properties such as domains and password.
+
+  - Group: A group is a user atom other atoms can belong to.
+
+  - User atom: Abstract representation of either a User or
+    a Group.
+
+  - Member (of a group): User atom inside a group.
+
+  - Name (of an atom): For a user, the name can be set by
+    the underlying user folder but usually id == name.
+    For a group, its id is prefixed, but its name is NOT prefixed by 'group_'.
+    For method taking a name instead of an id (eg. getUserByName()),
+    if a user and a group have the same name,
+    the USER will have precedence over the group.
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: IUserFolder.py 30098 2006-09-08 12:35:01Z encolpe $
+__docformat__ = 'restructuredtext'
+
+from Interface import Attribute
+try:
+    from Interface import Interface
+except ImportError:
+    # for Zope versions before 2.6.0
+    from Interface import Base as Interface
+
+
+
+class IUserFolder(Interface):
+
+    #                                                   #
+    #           Regular Zope UserFolder API             #
+    #                                                   #
+
+    # User atom access
+    
+    def getUserNames():
+        """
+        Return a list of all possible user atom names in the system.
+        Groups will be returned WITHOUT their prefix by this method.
+        So, there might be a collision between a user name and a group name.
+        [NOTA: This method is time-expensive !]
+        """
+
+    def getUserIds():
+        """
+        Return a list of all possible user atom ids in the system.
+        WARNING: Please see the id Vs. name consideration at the
+        top of this document. So, groups will be returned
+        WITH their prefix by this method
+        [NOTA: This method is time-expensive !]
+        """
+        
+    def getUser(name):
+        """Return the named user atom object or None
+        NOTA: If no user can be found, we try to append a group prefix
+        and fetch the user again before returning 'None'. This will ensure
+        backward compatibility. So in fact, both group id and group name can be
+        specified to this method.
+        """
+
+    def getUsers():
+        """Return a list of user atom objects in the users cache.
+        In case of some UF implementations, the returned object may only be a subset
+        of all possible users.
+        In other words, you CANNOT assert that len(getUsers()) equals len(getUserNames()).
+        With cache-support UserFolders, such as LDAPUserFolder, the getUser() method will
+        return only cached user objects instead of fetching all possible users.
+        So this method won't be very time-expensive, but won't be accurate !
+        """
+
+    def getUserById(id, default):
+        """Return the user atom corresponding to the given id.
+        If default is provided, return default if no user found, else return None.
+        """
+
+    def getUserByName(name, default):
+        """Same as getUserById() but works with a name instead of an id.
+        If default is provided, return default if no user found, else return None.
+        [NOTA: Theorically, the id is a handle, while the name is the actual login name.
+        But difference between a user id and a user name is unsignificant in
+        all current User Folder implementations... except for GROUPS.]        
+        """
+
+    def hasUsers():
+        """
+        From Zope 2.7's User.py:
+        This is not a formal API method: it is used only to provide
+        a way for the quickstart page to determine if the default user
+        folder contains any users to provide instructions on how to
+        add a user for newbies.  Using getUserNames or getUsers would have
+        posed a denial of service risk.
+        In GRUF, this method always return 1."""
+    
+
+    # Search interface for users; they won't return groups in any case.
+
+    def searchUsersByName(search_term):
+        """Return user ids which match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying user folder:
+        it may return all users, return only cached users (for LDAPUF) or return no users.
+        """
+
+    def searchUsersById(search_term):
+        """Return users whose id match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying user folder:
+        it may return all users, return only cached users (for LDAPUF) or return no users.
+        """
+        
+    def searchUsersByAttribute(attribute, search_term):
+        """Return user ids whose 'attribute' match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying user folder:
+        it may return all users, return only cached users (for LDAPUF) or return no users.
+        This will return all users whose name contains search_term (whaterver its case).
+        THIS METHOD MAY BE VERY EXPENSIVE ON USER FOLDER KINDS WHICH DO NOT PROVIDE A
+        SEARCHING METHOD (ie. every UF kind except LDAPUF).
+        'attribute' can be 'id' or 'name' for all UF kinds, or anything else for LDAPUF.
+        [NOTA: This method is time-expensive !]
+        """
+
+    # Search interface for groups;
+
+    def searchGroupsByName(search_term):
+        """Return group ids which match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying group folder:
+        it may return all groups, return only cached groups (for LDAPUF) or return no groups.
+        """
+
+    def searchGroupsById(search_term):
+        """Return groups whose id match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying group folder:
+        it may return all groups, return only cached groups (for LDAPUF) or return no groups.
+        """
+        
+    def searchGroupsByAttribute(attribute, search_term):
+        """Return group ids whose 'attribute' match the specified search_term.
+        If search_term is an empty string, behaviour depends on the underlying group folder:
+        it may return all groups, return only cached groups (for LDAPUF) or return no groups.
+        This will return all groups whose name contains search_term (whaterver its case).
+        THIS METHOD MAY BE VERY EXPENSIVE ON GROUP FOLDER KINDS WHICH DO NOT PROVIDE A
+        SEARCHING METHOD (ie. every UF kind except LDAPUF).
+        'attribute' can be 'id' or 'name' for all UF kinds, or anything else for LDAPUF.
+        [NOTA: This method is time-expensive !]
+        """
+
+
+    # User access
+
+    def getPureUserNames():
+        """Same as getUserNames() but without groups
+        """
+
+    def getPureUserIds():
+        """Same as getUserIds() but without groups
+        """
+
+    def getPureUsers():
+        """Same as getUsers() but without groups.
+        """
+
+    def getPureUser(id):
+        """Same as getUser() but forces returning a user and not a group
+        """
+        
+    # Group access
+
+    def getGroupNames():
+        """Same as getUserNames() but without pure users.
+        """
+
+    def getGroupIds():
+        """Same as getUserIds() but without pure users.
+        """
+
+    def getGroups():
+        """Same as getUsers() but without pure users.
+        In case of some UF implementations, the returned object may only be a subset
+        of all possible users.
+        In other words, you CANNOT assert that len(getUsers()) equals len(getUserNames()).
+        With cache-support UserFolders, such as LDAPUserFolder, the getUser() method will
+        return only cached user objects instead of fetching all possible users.
+        So this method won't be very time-expensive, but won't be accurate !
+        """
+
+    def getGroup(name):
+        """Return the named group object or None. As usual, 'id' is prefixed.
+        """
+
+    def getGroupById(id):
+        """Same as getUserById(id) but forces returning a group.
+        """
+
+    def getGroupByName(name):
+        """Same as getUserByName(name) but forces returning a group.
+        The specified name MUST NOT be prefixed !
+        """
+    
+
+    # Mutators
+
+    def userFolderAddUser(name, password, roles, domains, groups, **kw):
+        """API method for creating a new user object. Note that not all
+        user folder implementations support dynamic creation of user
+        objects.
+        Groups can be specified by name or by id (preferabily by name)."""
+
+    def userFolderEditUser(name, password, roles, domains, groups, **kw):
+        """API method for changing user object attributes. Note that not
+        all user folder implementations support changing of user object
+        attributes.
+        Groups can be specified by name or by id (preferabily by name)."""
+
+    def userFolderUpdateUser(name, password, roles, domains, groups, **kw):
+        """Same as userFolderEditUser, but with all arguments except name
+        being optional.
+        """
+
+    def userFolderDelUsers(names):
+        """API method for deleting one or more user atom objects. Note that not
+        all user folder implementations support deletion of user objects."""
+
+    def userFolderAddGroup(name, roles, groups, **kw):
+        """API method for creating a new group.
+        """
+        
+    def userFolderEditGroup(name, roles, groups, **kw):
+        """API method for changing group object attributes.
+        """
+
+    def userFolderUpdateGroup(name, roles, groups, **kw):
+        """Same as userFolderEditGroup but with all arguments (except name) being
+        optinal.
+        """
+
+    def userFolderDelGroups(names):
+        """API method for deleting one or more group objects.
+        Implem. note : All ids must be prefixed with 'group_',
+        so this method ends up beeing only a filter of non-prefixed ids
+        before calling userFolderDelUsers().
+        """
+
+    # User mutation
+
+    
+    # XXX do we have to allow a user to be renamed ?
+##    def setUserId(id, newId):
+##        """Change id of a user atom. The user name might be changed as well by this operation.
+##        """
+
+##    def setUserName(id, newName):
+##        """Change the name of a user atom. The user id might be changed as well by this operation.
+##        """
+
+    def userSetRoles(id, roles):
+        """Change the roles of a user atom
+        """
+
+    def userAddRole(id, role):
+        """Append a role for a user atom
+        """
+
+    def userRemoveRole(id, role):
+        """Remove the role of a user atom.
+        This will not, of course, affect implicitly-acquired roles from the user groups.
+        """
+
+    def userSetPassword(id, newPassword):
+        """Set the password of a user
+        """
+
+    def userSetDomains(id, domains):
+        """Set domains for a user
+        """
+
+    def userGetDomains(id, ):
+        """Get domains for a user
+        """
+
+    def userAddDomain(id, domain):
+        """Append a domain to a user
+        """
+
+    def userRemoveDomain(id, domain):
+        """Remove a domain from a user
+        """
+
+    def userSetGroups(userid, groupnames):
+        """Set the groups of a user. Groupnames are, as usual, not prefixed.
+        However, a groupid can be given as a fallback
+        """
+
+    def userAddGroup(id, groupname):
+        """add a group to a user atom. Groupnames are, as usual, not prefixed.
+        However, a groupid can be given as a fallback
+        """
+
+    def userRemoveGroup(id, groupname):
+        """remove a group from a user atom. Groupnames are, as usual, not prefixed.
+        However, a groupid can be given as a fallback
+        """
+
+
+    # Security management
+
+    def setRolesOnUsers(roles, userids):
+        """Set a common set of roles for a bunch of user atoms.
+        """
+
+##    def setUsersOfRole(usernames, role):
+##        """Sets the users of a role.
+##        XXX THIS METHOD SEEMS TO BE SEAMLESS.
+##        """
+
+    def getUsersOfRole(role, object = None):
+        """Gets the user (and group) ids having the specified role...
+        ...on the specified Zope object if it's not None
+        ...on their own information if the object is None.
+        NOTA: THIS METHOD IS VERY EXPENSIVE.
+        """
+
+    def getRolesOfUser(userid):
+        """Alias for user.getRoles()
+        """
+
+    def userFolderAddRole(role):
+        """Add a new role. The role will be appended, in fact, in GRUF's surrounding folder.
+        """
+
+    def userFolderDelRoles(roles):
+        """Delete roles.
+        The removed roles will be removed from the UserFolder's users and groups as well,
+        so this method can be very time consuming with a large number of users.
+        """
+
+    def userFolderGetRoles():
+        """List the roles defined at the top of GRUF's folder.
+        """
+
+
+    # Groups support
+    def setMembers(groupid, userids):
+        """Set the members of the group
+        """
+
+    def addMember(groupid, id):
+        """Add a member to a group
+        """
+
+    def removeMember(groupid, id):
+        """Remove a member from a group
+        """
+
+    def hasMember(groupid, id):
+        """Return true if the specified atom id is in the group.
+        This is the contrary of IUserAtom.isInGroup(groupid).
+        THIS CAN BE VERY EXPENSIVE"""
+
+    def getMemberIds(groupid):
+        """Return the list of member ids (groups and users) in this group.
+        It will unmangle nested groups as well.
+        THIS METHOD CAN BE VERY EXPENSIVE AS IT NEEDS TO FETCH ALL USERS.
+        """
+
+    def getUserMemberIds(groupid):
+        """Same as listMemberIds but only return user ids
+        THIS METHOD CAN BE VERY EXPENSIVE AS IT NEEDS TO FETCH ALL USERS.
+        """
+
+    def getGroupMemberIds(groupid):
+        """Same as listMemberUserIds but only return group ids.
+        THIS METHOD CAN BE VERY EXPENSIVE AS IT NEEDS TO FETCH ALL USERS.
+        """
+
+
+    # Local roles acquisition blocking support
+    def acquireLocalRoles(folder, status):
+        """Enable or disable local role acquisition on the specified folder.
+        If status is true, it will enable, else it will disable.
+        """
+
+    def isLocalRoleAcquired(folder):
+        """Return true if the specified folder allows local role acquisition.
+        """
+
+    # Audit & security checking methods
+
+    def getAllLocalRoles(object):
+        """getAllLocalRoles(self, object): return a dictionnary {user: roles} of local
+        roles defined AND herited at a certain point. This will handle lr-blocking
+        as well.
+        """
+        
+
+class IUserAtom(Interface):
+    """
+    This interface is an abstract representation of what both a User and a Group can do.
+    """
+    # Accessors
+    
+    def getId(unprefixed = 0):
+        """Get the ID of the user. The ID can be used, at least from
+        Python, to get the user from the user's UserDatabase.
+        If unprefixed, remove all prefixes in any case."""
+
+    def getUserName():
+        """Alias for getName()
+        """
+
+    def getName():
+        """Get user's or group's name.
+        For a user, the name can be set by the underlying user folder but usually id == name.
+        For a group, the ID is prefixed, but the NAME is NOT prefixed by 'group_'.
+        """
+
+    def getRoles():
+        """Return the list of roles assigned to a user atom.
+        This will never return gruf-related roles.
+        """
+
+    # Properties are defined depending on the underlying user folder: some support
+    # properties mutation (such as LDAPUserFolder), some do not (such as regular UF).
+
+    def getProperty(name):
+        """Get a property's value.
+        Will raise if not available.
+        """
+
+    def hasProperty(name):
+        """Return true if the underlying user object has a value for the property.
+        """
+
+    # Mutators
+
+    def setProperty(name, value):
+        """Set a property's value.
+        As some user folders cannot set properties, this method is not guaranteed to work
+        and will raise a NotImplementedError if the underlying user folder cannot store
+        properties (or _this_ particular property) for a user.
+        """
+        
+    # XXX We do not allow user name / id changes
+##    def setId(newId):
+##        """Set the id of the user or group. This might change its name as well.
+##        """
+
+##    def setName(newName):
+##        """Set the name of the user or group. Depending on the UserFolder implementation,
+##        this might change the id as well.
+##        """
+
+    def setRoles(roles):
+        """Change user's roles
+        """
+
+    def addRole(role):
+        """Append a role to the user
+        """
+
+    def removeRole(role):
+        """Remove a role from the user's ones
+        """
+
+    # Security-related methods
+
+    def getRolesInContext(object):
+        """Return the list of roles assigned to the user,
+           including local roles assigned in context of
+           the passed in object."""
+
+    def has_permission(permission, object):
+        """Check to see if a user has a given permission on an object."""
+
+    def allowed(object, object_roles=None):
+        """Check whether the user has access to object. The user must
+           have one of the roles in object_roles to allow access."""
+
+    def has_role(roles, object=None):
+        """Check to see if a user has a given role or roles."""
+
+
+
+    # Group management
+
+    # XXX TODO: CLARIFY ID VS. NAME
+
+    def isGroup():
+        """Return true if this atom is a group.
+        """
+
+    def getGroupNames():
+        """Return the names of the groups that the user or group is directly a member of.
+        Return an empty list if the user or group doesn't belong to any group.
+        Doesn't include transitive groups."""
+
+    def getGroupIds():
+        """Return the names of the groups that the user or group is a member of.
+        Return an empty list if the user or group doesn't belong to any group.
+        Doesn't include transitive groups."""
+
+    def getGroups():
+        """getAllGroupIds() alias.
+        Return the IDS (not names) of the groups that the user or group is a member of.
+        Return an empty list if the user or group doesn't belong to any group.
+        THIS WILL INCLUDE TRANSITIVE GROUPS AS WELL."""
+
+    def getAllGroupIds():
+        """Return the names of the groups that the user or group is a member of.
+        Return an empty list if the user or group doesn't belong to any group.
+        Include transitive groups."""
+
+    def getAllGroupNames():
+        """Return the names of the groups that the user or group is directly a member of.
+        Return an empty list if the user or group doesn't belong to any group.
+        Include transitive groups."""
+
+    def isInGroup(groupid):
+        """Return true if the user is member of the specified group id
+        (including transitive groups)"""
+
+    def setGroups(groupids):
+        """Set 'groupids' groups for the user or group.
+        """
+
+    def addGroup(groupid):
+        """Append a group to the current object's groups.
+        """
+
+    def removeGroup(groupid):
+        """Remove a group from the object's groups
+        """
+
+    def getRealId():
+        """Return group id WITHOUT group prefix.
+        For a user, return regular user id.
+        This method is essentially internal.
+        """
+
+
+class IUser(IUserAtom):
+    """
+    A user is a user atom who can log itself on, and
+    have additional properties such as domains and password.
+    """
+    
+    # Accessors
+
+    def getDomains():
+        """Return the list of domain restrictions for a user"""
+
+    # Mutators
+    
+    def setPassword(newPassword):
+        """Set user's password
+        """
+
+    def setDomains(domains):
+        """Replace domains for the user
+        """
+
+    def addDomain(domain):
+        """Append a domain for the user
+        """
+
+    def removeDomain(domain):
+        """Remove a domain for the user
+        """
+
+
+class IGroup(Interface):
+    """
+    A group is a user atom other atoms can belong to.
+    """
+    def getMemberIds(transitive = 1, ):
+        """Return the member ids (users and groups) of the atoms of this group.
+        This method can be very expensive !"""
+
+    def getUserMemberIds(transitive = 1, ):
+        """Return the member ids (users only) of the users of this group"""
+
+    def getGroupMemberIds(transitive = 1, ):
+        """Return the members ids (groups only) of the groups of this group"""
+
+    def hasMember(id):
+        """Return true if the specified atom id is in the group.
+        This is the contrary of IUserAtom.isInGroup(groupid)"""
+
+    def addMember(userid):
+         """Add a user the the current group"""
+         
+    def removeMember(userid):
+         """Remove a user from the current group"""
diff --git a/interfaces/__init__.py b/interfaces/__init__.py
new file mode 100644
index 0000000..cb3bafe
--- /dev/null
+++ b/interfaces/__init__.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+"""
+
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: __init__.py 30098 2006-09-08 12:35:01Z encolpe $
+__docformat__ = 'restructuredtext'
+
+# interface definitions for use by Plone
diff --git a/interfaces/portal_groupdata.py b/interfaces/portal_groupdata.py
new file mode 100644
index 0000000..c0786a2
--- /dev/null
+++ b/interfaces/portal_groupdata.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+## Copyright (c) 2003 The Connexions Project, All Rights Reserved
+## initially written by J Cameron Cooper, 11 June 2003
+## concept with Brent Hendricks, George Runyan
+"""Groups tool interface
+
+Goes along the lines of portal_memberdata, but for groups.
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: portal_groupdata.py 30098 2006-09-08 12:35:01Z encolpe $
+__docformat__ = 'restructuredtext'
+
+from Interface import Attribute
+try:
+    from Interface import Interface
+except ImportError:
+    # for Zope versions before 2.6.0
+    from Interface import Base as Interface
+
+class portal_groupdata(Interface):
+    """ A helper tool for portal_groups that transparently adds
+    properties to groups and provides convenience methods"""
+
+##    id = Attribute('id', "Must be set to 'portal_groupdata'")
+
+    def wrapGroup(g):
+        """ Returns an object implementing the GroupData interface"""
+
+
+class GroupData(Interface):
+    """ An abstract interface for accessing properties on a group object"""
+
+    def setProperties(properties=None, **kw):
+        """Allows setting of group properties en masse.
+        Properties can be given either as a dict or a keyword parameters list"""
+
+    def getProperty(id):
+        """ Return the value of the property specified by 'id' """
+
+    def getProperties():
+        """ Return the properties of this group. Properties are as usual in Zope."""
+
+    def getGroupId():
+        """ Return the string id of this group, WITHOUT group prefix."""
+
+    def getMemberId():
+        """This exists only for a basic user/group API compatibility
+        """
+
+    def getGroupName():
+        """ Return the name of the group."""
+
+    def getGroupMembers():
+        """ Return a list of the portal_memberdata-ish members of the group."""
+
+    def getAllGroupMembers():
+        """ Return a list of the portal_memberdata-ish members of the group
+        including transitive ones (ie. users or groups of a group in that group)."""
+
+    def getGroupMemberIds():
+        """ Return a list of the user ids of the group."""
+
+    def getAllGroupMemberIds():
+        """ Return a list of the user ids of the group.
+        including transitive ones (ie. users or groups of a group in that group)."""
+
+    def addMember(id):
+        """ Add the existing member with the given id to the group"""
+
+    def removeMember(id):
+        """ Remove the member with the provided id from the group """
+
+    def getGroup():
+        """ Returns the actual group implementation. Varies by group
+        implementation (GRUF/Nux/et al)."""
diff --git a/interfaces/portal_groups.py b/interfaces/portal_groups.py
new file mode 100644
index 0000000..2be03ae
--- /dev/null
+++ b/interfaces/portal_groups.py
@@ -0,0 +1,144 @@
+# -*- coding: utf-8 -*-
+## GroupUserFolder
+## Copyright (C)2006 Ingeniweb
+
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+
+## You should have received a copy of the GNU General Public License
+## along with this program; see the file COPYING. If not, write to the
+## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+## Copyright (c) 2003 The Connexions Project, All Rights Reserved
+## initially written by J Cameron Cooper, 11 June 2003
+## concept with Brent Hendricks, George Runyan
+"""Groups tool interface
+
+Goes along the lines of portal_membership, but for groups.
+"""
+__version__ = "$Revision:  $"
+# $Source:  $
+# $Id: portal_groups.py 30098 2006-09-08 12:35:01Z encolpe $
+__docformat__ = 'restructuredtext'
+
+
+from Interface import Attribute
+try:
+    from Interface import Interface
+except ImportError:
+    # for Zope versions before 2.6.0
+    from Interface import Base as Interface
+
+class portal_groups(Interface):
+    """Defines an interface for working with groups in an abstract manner.
+    Parallels the portal_membership interface of CMFCore"""
+##    id = Attribute('id','Must be set to "portal_groups"')
+
+    def isGroup(u):
+        """Test if a user/group object is a group or not.
+        You must pass an object you get earlier with wrapUser() or wrapGroup()
+        """
+
+    def getGroupById(id):
+        """Returns the portal_groupdata-ish object for a group corresponding
+        to this id."""
+
+    def getGroupsByUserId(userid):
+        """Returns a list of the groups the user corresponding to 'userid' belongs to."""
+
+    def listGroups():
+        """Returns a list of the available portal_groupdata-ish objects."""
+
+    def listGroupIds():
+        """Returns a list of the available groups' ids (WITHOUT prefixes)."""
+
+    def listGroupNames():
+        """Returns a list of the available groups' names (ie. without prefixes)."""
+
+##    def getPureUserNames():
+##        """Get the usernames (ids) of only users. """
+
+##    def getPureUsers():
+##        """Get the actual (unwrapped) user objects of only users. """
+
+    def searchForGroups(REQUEST, **kw):    # maybe searchGroups()?
+        """Return a list of groups meeting certain conditions. """
+        # arguments need to be better refined?
+
+    def addGroup(id, roles = [], groups = [], **kw):
+        """Create a group with the supplied id, roles, and groups.
+
+        Underlying user folder must support adding users via the usual Zope API.
+        Passwords for groups seem to be currently irrelevant in GRUF."""
+
+    def editGroup(id, roles = [], groups = [], **kw):
+        """Edit the given group with the supplied roles.
+
+        Underlying user folder must support editing users via the usual Zope API.
+        Passwords for groups seem to be currently irrelevant in GRUF.
+        One can supply additional named parameters to set group properties."""
+
+    def removeGroups(ids, keep_workspaces=0):
+        """Remove the group in the provided list (if possible).
+
+        Will by default remove this group's GroupWorkspace if it exists. You may
+        turn this off by specifying keep_workspaces=true.
+        Underlying user folder must support removing users via the usual Zope API."""
+
+    def setGroupOwnership(group, object):
+        """Make the object 'object' owned by group 'group' (a portal_groupdata-ish object)"""
+
+    def setGroupWorkspacesFolder(id=""):
+        """ Set the location of the Group Workspaces folder by id.
+
+        The Group Workspaces Folder contains all the group workspaces, just like the
+        Members folder contains all the member folders.
+
+        If anyone really cares, we can probably make the id work as a path as well,
+        but for the moment it's only an id for a folder in the portal root, just like the
+        corresponding MembershipTool functionality. """
+
+    def getGroupWorkspacesFolderId():
+        """ Get the Group Workspaces folder object's id.
+
+        The Group Workspaces Folder contains all the group workspaces, just like the
+        Members folder contains all the member folders. """
+
+    def getGroupWorkspacesFolder():
+        """ Get the Group Workspaces folder object.
+
+        The Group Workspaces Folder contains all the group workspaces, just like the
+        Members folder contains all the member folders. """
+
+    def toggleGroupWorkspacesCreation():
+        """ Toggles the flag for creation of a GroupWorkspaces folder upon first
+        use of the group. """
+
+    def getGroupWorkspacesCreationFlag():
+        """Return the (boolean) flag indicating whether the Groups Tool will create a group workspace
+        upon the next use of the group (if one doesn't exist). """
+
+    def getGroupWorkspaceType():
+        """Return the Type (as in TypesTool) to make the GroupWorkspace."""
+
+    def setGroupWorkspaceType(type):
+        """Set the Type (as in TypesTool) to make the GroupWorkspace. Expects the name of a Type."""
+
+    def createGrouparea(id):
+        """Create a space in the portal for the given group, much like member home
+        folders."""
+
+    def getGroupareaFolder(id):
+        """Returns the object of the group's work area."""
+
+    def getGroupareaURL(id):
+        """Returns the full URL to the group's work area."""
+
+    # and various roles things...
diff --git a/product.txt b/product.txt
new file mode 100644
index 0000000..aaad7b8
--- /dev/null
+++ b/product.txt
@@ -0,0 +1 @@
+GroupUserFolder
diff --git a/skins/gruf/GroupSpaceFolderishType_view.pt.old b/skins/gruf/GroupSpaceFolderishType_view.pt.old
new file mode 100644
index 0000000..79c1267
--- /dev/null
+++ b/skins/gruf/GroupSpaceFolderishType_view.pt.old
@@ -0,0 +1,16 @@
+<html metal:use-macro="here/main_template/macros/master">
+<body>
+<div metal:fill-slot="main" >
+
+        <div class="contentHeader">
+          <h1 tal:content="here/Title"> Title </h1>
+          <div class="contentBody">
+            <p>Here's the (unmutable) content of your MinimalFolderishType.</p>
+            <p>Have a nice plonish day ! :-)</p>
+          </div>
+        </div>
+
+</div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/skins/gruf/change_password.py b/skins/gruf/change_password.py
new file mode 100644
index 0000000..cd3f97f
--- /dev/null
+++ b/skins/gruf/change_password.py
@@ -0,0 +1,31 @@
+## Script (Python) "change_password"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=password, confirm, domains=None
+##title=Change password
+##
+
+pass
+
+## This code is there because there's a bug in CMF that prevents
+## passwords to be changed if the User Folder doesn't store it in a __
+## attribute.
+## This includes User Folders such as LDAPUF, SimpleUF, and, of course, GRUF.
+## This also includes standard UF with password encryption !
+
+mt = context.portal_membership
+failMessage=context.portal_registration.testPasswordValidity(password, confirm)
+
+if failMessage:
+    return context.password_form(context,
+                                 context.REQUEST,
+                                 error=failMessage)
+context.REQUEST['AUTHENTICATED_USER'].changePassword(password,REQUEST=context.REQUEST)
+mt.credentialsChanged(password)
+return context.personalize_form(context,
+                                context.REQUEST,
+                                portal_status_message='Password changed.')
+
diff --git a/skins/gruf/defaultGroup.gif b/skins/gruf/defaultGroup.gif
new file mode 100644
index 0000000000000000000000000000000000000000..eccbeb653185d386b3711a6aae6d7832d283398c
GIT binary patch
literal 1225
zcmV;)1UCCeNk%w1VM_pD0Oo%HiHV5>1Oz4~CVhQ<At51DR8&$@QZ6noKtMnk7#Ln&
zUMeaoK0ZD=Iyz5JPb@4fG&D3aGBO$(8WIu`78Vu`4h{+m3J?$w0s;aY92^V`3<wAa
zJv=-V6cqpe|NZ^_{{H@CWMudE_fSw!I5;?FW@acTC^<Pf<KyGv;^Ik3NlQyh($dm!
zaByX1Wg{aaOiWB-Vq#fYS#fc3`T6;*tgQ6(^g=>HU0q$Fp`mwocj)NoO-)T!R#s6_
zQSR>U&d$yr9v=Ms{L0G8jEszpjg7Rlv{zSGMn*<QM@PQCzOJsX_4W1U=H?q48`|31
zl9G~WXlQkHb&-*g^78WR?CjCe(SU$}g@uLJ*Vpm!@xj5t`}_N_u&~6$#MIQ(ot>TB
z+}z2@$(NUxo12?NL`2KW%hS`-SXfx9s;Y{Liln5ZrlzKunVI3?;gge-wY9bL^Yfsf
zpy1%(qN1YN+1bd*$hNk&xVX5vxw*Hux2dVAVPRohTwDME0RR90A^8LW004ggEC2ui
z080R1000O7fPaF6goTEOh>41ejE#<ukdcy;l$Dm3n3<ZJoSmMZprN9pq@|{(sHv)}
zhAA^A7Z@80XmqTV3mAU8yt@)cw~#{=y~Vo$0Ktu48^+E-%8WtI&I!?qHM-Qr*4Ky~
z+r|M^-i8X{&Sd3<0_esD>x2;Q#Rc($BlNv$_kue5yukGPQv{%&00aLt(MB(TmV_A$
zzDbD3;1Nq6GHT4o=>k9~ls??S66Hh&2@Gm*%w(e(Jpq$E5P86m*M>bSFWCq{4?`G9
zEKJlaW?@XuN@w&DkTfKLI}HOqc(9b?fWBmq>`c(NVa|t2IRGAr<i#mK2O_3Unx_v4
zBo-C_3?5tqV$u?x^bkaHA$J#pua0PJIbi{y2EdO<^z}mkaUv8x2)Hzefxwg<kIc$V
zz(vuF7kC%*3ecxfoVa}_P^oLk%~sLWOjud;1GQWYJA@db@ftQY3>+3YS#0444MQe;
zyQ|P8QS!bTYKSOQKzUCWhve9%yg>J$6rru78(U}r>=mp9wGa>RMtRr;)CzjQULOWH
zgjB$mA^%U|`t(SESRLCLq=S4j0BD~ci1{E|4D$7+AUX+5CrA&AZ7{$fW(+mqHWmzW
zg@p8201Q4K0z`-uaw&iq5aFd54k2Rv*HaPcu_dEBg}?w%0Bx|AMT|RAQ^*rGq7#7s
z5^5<{B#(qJK_ZAds5RV^=oG>Nk<?UR!&D*^h@~-w5b<LgQ;p%_n7t6<K|}zd=RpH(
z&iIE1DI~!qh<SiuRh`tJfI<sX(vtuiEtHYKnryz1<YE&bpnwXR0a_mh72FVl3IZ@{
zsc4uU5Cak(eG1?OFvviFr>6E500b^@uxG1_C4d7D8L&z#Ko*d|M-m$BDqaN+6hMHm
z*1cLn1jt%PKnQ;}3+f9YR4T1p7$7jhqu27<1AW~tCIAoN5?5`x*djnDx&|d+z^v>V
zvw;l6#M{#i=GjZn0_*BaArk(&bHNet1{?qm#|9h@1WqbUjS~nsyiEryNKB0;20mJB
nj0X8=OcV|t7ZX4XA@_no2qp7^K?y1I!g9-gybN=FfdBwIYUm))

literal 0
HcmV?d00001

diff --git a/skins/gruf/folder_localrole_form_plone1.pt b/skins/gruf/folder_localrole_form_plone1.pt
new file mode 100644
index 0000000..c4d8e19
--- /dev/null
+++ b/skins/gruf/folder_localrole_form_plone1.pt
@@ -0,0 +1,358 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US"
+      lang="en-US"
+      metal:use-macro="here/main_template/macros/master"
+      i18n:domain="plone">
+
+<body>
+
+  <div metal:fill-slot="main"
+       tal:define="Iterator python:modules['Products.CMFPlone'].IndexIterator;
+                   Batch python:modules['Products.CMFPlone'].Batch;
+                   group_submit request/group_submit|nothing;
+                   b_size python:12;b_start python:0;b_start request/b_start | b_start;
+                   search_submitted request/role_submit|nothing;
+                   search_results python:test(search_submitted, here.portal_membership.searchMembers(
+                                             search_param=request.get('search_param',''),
+                                             search_term=request.get('search_term', '') ), None);">
+
+    <h1 i18n:translate="heading_currently_assigned_localroles">
+      Currently assigned local roles in folder
+      <span tal:content="here/title_or_id" i18n:name="folder">title</span>
+    </h1>
+
+    <p i18n:translate="description_current_localroles">
+      These users currently have local roles assigned in this folder:
+    </p>
+
+    <form class="group"
+          method="post"
+          name="deleterole"
+          action="folder_localrole_edit"
+          tal:attributes="action string:${here/absolute_url}/folder_localrole_edit">
+    
+      <span class="legend" i18n:translate="legend_assigned_roles">
+        Assigned Roles
+        <span tal:content="here/title_or_id" i18n:name="folder">title</span>
+      </span>
+
+      <input type="hidden" name="change_type" value="delete" />
+      <input type="hidden" name="member_role" value="" />
+
+      <table class="listing" summary="Currently assigned local roles"
+             tal:define="username python:here.portal_membership.getAuthenticatedMember().getUserName();">
+        <thead>
+          <tr>
+            <th>&nbsp;</th>
+            <th i18n:translate="label_user_group_name">User/Group name</th>
+            <th i18n:translate="label_type">Type</th>
+            <th i18n:translate="label_roles">Role(s)</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr tal:repeat="lrole python:here.acl_users.getLocalRolesForDisplay(here)">
+            <td>
+              <input class="noborder" 
+                     type="checkbox"
+                     name="member_ids:list"
+                     id="#"
+                     value=""
+                     tal:condition="python:lrole[0]!=username"
+                     tal:attributes="value python:lrole[3];"
+                     />
+            </td>
+
+            <td tal:content="python:lrole[0]">
+              groupname
+            </td>
+
+            <td tal:condition="python:lrole[2]=='group'"
+                i18n:translate="label_group">
+              Group
+            </td>
+            <td tal:condition="python:lrole[2]=='user'"
+                i18n:translate="label_user">
+              User
+            </td>
+
+            <td>
+              <tal:block tal:repeat="role python:lrole[1]">
+                <span i18n:translate=""
+                      tal:content="role"
+                      tal:omit-tag="">Role</span>
+                <span tal:condition="not: repeat/role/end"
+                      tal:omit-tag="">, </span>
+              </tal:block>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+
+      <input class="context" 
+             type="submit" 
+             value="Delete Selected Role(s)"
+             i18n:attributes="value"
+             />
+    </form>
+
+    <metal:block tal:condition="python:test(search_submitted and not search_results, 1, 0)">
+      <h1 i18n:translate="heading_search_results">Search results</h1>
+      <p i18n:translate="no_members_found">
+        No members were found using your <strong>Search Criteria</strong>
+      </p>
+      <hr />
+    </metal:block>
+
+    <metal:block tal:condition="python:test(search_submitted and search_results, 1, 0)">
+
+      <h1 i18n:translate="heading_search_results">Search results</h1>
+
+      <p i18n:translate="description_localrole_select_member">
+        Select one or more Members, and a role to assign.
+      </p>
+
+      <metal:block tal:define="batch python:Batch(search_results, b_size, int(b_start), orphan=3)">
+
+        <form class="group"
+              method="post" 
+              name="change_type" 
+              action="folder_localrole_edit"
+              tal:attributes="action string:${here/absolute_url}/folder_localrole_edit">
+
+          <span class="legend" i18n:translate="legend_available_members">
+            Available Members
+          </span>
+
+          <input type="hidden" name="change_type" value="add" />
+
+          <!-- batch navigation -->
+          <div metal:use-macro="here/batch_macros/macros/navigation" />
+
+          <table class="listing" summary="Search results">
+            <thead>
+              <tr>
+                <th>&nbsp;</th>
+                <th i18n:translate="label_user_name">User Name</th>
+                <th i18n:translate="label_email_address">Email Address</th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr tal:repeat="member batch">
+                <td>
+                  <input class="noborder" 
+                         type="checkbox"
+                         name="member_ids:list"
+                         id="#"
+                         value=""
+                         tal:attributes="value member/username;"
+                         />
+                </td>
+
+                <td tal:content="member/username">username</td>
+                <td tal:content="member/email">email</td>
+              </tr>
+            </tbody>
+          </table>
+
+          <!-- batch navigation -->
+          <div metal:use-macro="here/batch_macros/macros/navigation" />
+
+          <div class="row">
+
+            <div class="label" i18n:translate="label_localrole_to_assign">
+              Role to assign
+            </div>
+
+            <div class="field">
+              <select name="member_role">
+                  <option tal:repeat="lroles python:container.portal_membership.getCandidateLocalRoles(here)"
+                          tal:attributes="value lroles"
+                          tal:content="lroles"
+                          i18n:translate="">
+                    Role name
+                  </option>
+              </select>
+            </div>
+
+          </div>
+
+          <div class="row">
+            <div class="label">&nbsp;</div>
+            <div class="field">
+              <input class="context" 
+                     type="submit" 
+                     value="Assign Local Role to Selected User(s)"
+                     i18n:attributes="value"
+                     />
+            </div>
+          </div>
+
+        </form>
+
+      </metal:block>
+    </metal:block>
+
+    <div>
+      <tal:block tal:condition="python: (not search_submitted or
+                                        (search_submitted and not search_results))">
+
+        <h1 i18n:translate="heading_assign_localrole">
+          Assign local roles to folder
+          <tal:block tal:content="here/title_or_id" i18n:name="folder">title</tal:block>
+        </h1>
+
+        <p i18n:translate="description_assign_localrole">
+          A local role is a way of allowing other users into some or
+          all of your folders. These users can edit items, publish
+          them - et cetera, depending on what permissions you give
+          them.
+          <br />
+                
+          Local roles are ideal in cooperation projects, and as every
+          item has a history and an undo option, it's easy to keep
+          track of the changes.
+                  
+          <br />
+                
+          To give a person a local role in this folder, just search
+          for the person's name or email address in the form below,
+          and you will be presented with a page that will show you the
+          options available.
+        </p>
+
+        <form class="group"
+              method="post" 
+              name="localrole" 
+              action="folder_localrole_form" 
+              tal:attributes="action string:${here/absolute_url}/${template/getId}" >
+
+          <span class="legend" i18n:translate="legend_search_terms">
+            Search Terms
+          </span>
+
+          <input type="hidden" name="role_submit" value="role_submit" />
+
+          <div class="row">
+            <div class="label" i18n:translate="label_search_by">
+              Search by
+            </div>
+                          
+            <div class="field">
+              <select name="search_param">
+                <option value="username" i18n:translate="label_user_name"> 
+                  User Name
+                </option>
+                <option value="email" i18n:translate="label_email_address">
+                  Email Address
+                </option>
+              </select>
+            </div>
+          </div>
+                      
+          <div class="row">
+            <div class="label"
+                 i18n:translate="label_search_term">
+              Search Term
+            </div>
+
+            <div class="field">
+              <input type="text"
+                     name="search_term"
+                     size="30"
+                     />
+            </div>
+          </div>
+
+          <div class="row">
+            <div class="label">&nbsp;</div>
+            <div class="field">
+              <input class="context" 
+                     type="submit" 
+                     value="Perform Search"
+                     i18n:attributes="value"
+                     />
+            </div>
+          </div>
+
+        </form>
+      </tal:block>
+
+      <h1 i18n:translate="heading_available_groups">Available groups</h1>
+
+      <p i18n:translate="description_available_groups">
+        Groups are a convenient way to assign roles to a common set of
+        users. Select one or more Groups, and a role to assign.
+      </p>
+        
+      <form class="group"
+            method="post" 
+            name="change_type" 
+            action="folder_localrole_edit"
+            tal:attributes="action string:${here/absolute_url}/folder_localrole_edit">
+            
+        <span class="legend" i18n:translate="legend_available_groups">
+          Available Groups
+        </span>
+
+        <input type="hidden" name="change_type" value="add" />
+
+        <table class="listing" summary="Available groups">
+          <thead>
+            <tr>
+              <th>&nbsp;</th>
+              <th i18n:translate="">Name</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr tal:repeat="member here/acl_users/getGroups">
+              <td>
+                <input class="noborder" 
+                       type="checkbox"
+                       name="member_ids:list"
+                       id="#"
+                       value=""
+                       tal:attributes="value member/getUserName;" />
+              </td>
+              <td tal:content="python:member.getUserNameWithoutGroupPrefix()">
+                groupname
+              </td>
+            </tr>
+          </tbody>
+        </table>
+            
+        <div class="row">
+          <div class="label" i18n:translate="label_localrole_to_assign">
+            Role to assign
+          </div>
+
+          <div class="field">
+            <select name="member_role">
+              <option tal:repeat="lroles python:container.portal_membership.getCandidateLocalRoles(here)"
+                      tal:attributes="value lroles"
+                      tal:content="lroles"
+                      i18n:translate="">
+                Role name
+              </option>
+            </select>
+          </div>        
+        </div>
+        
+        <div class="row">
+          <div class="label">&nbsp;</div>
+          <div class="field">
+            <input class="context" 
+                   type="submit" 
+                   value="Assign Local Role to Selected Group(s)"
+                   i18n:attributes="value"
+                   />
+          </div>
+        </div>
+
+      </form>
+
+    </div>    
+
+  </div> <!-- fill-slot -->
+
+</body>
+</html>
diff --git a/skins/gruf/getUsersInGroup.py b/skins/gruf/getUsersInGroup.py
new file mode 100644
index 0000000..358a744
--- /dev/null
+++ b/skins/gruf/getUsersInGroup.py
@@ -0,0 +1,21 @@
+## Script (Python) "getUsersInGroup"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=groupid
+##title=
+##
+
+users=context.acl_users.getUsers()
+prefix=context.acl_users.getGroupPrefix()
+
+avail=[]
+for user in users:
+    for group in user.getGroups():
+        if groupid==group or \
+           prefix+groupid==group:
+            avail.append(user)
+
+return avail
diff --git a/skins/gruf/gruf_ldap_required_fields.py b/skins/gruf/gruf_ldap_required_fields.py
new file mode 100755
index 0000000..0bb76f8
--- /dev/null
+++ b/skins/gruf/gruf_ldap_required_fields.py
@@ -0,0 +1,14 @@
+## Script (Python) "gruf_ldap_required_fields"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=login
+##title=Mandatory / default LDAP attribute values
+##
+
+return {
+  "sn": login,
+  "cn": login,
+  }
diff --git a/skins/gruf/prefs_group_manage.cpy b/skins/gruf/prefs_group_manage.cpy
new file mode 100755
index 0000000..cb9e104
--- /dev/null
+++ b/skins/gruf/prefs_group_manage.cpy
@@ -0,0 +1,26 @@
+## Script (Python) "prefs_group_manage"
+##bind container=container
+##bind context=context
+##bind namespace=
+##bind script=script
+##bind subpath=traverse_subpath
+##parameters=
+##title=Manage groups
+##
+REQUEST=context.REQUEST
+groupstool=context.portal_groups
+
+groups=[group[len('group_'):]
+        for group in REQUEST.keys()
+        if group.startswith('group_')]
+
+for group in groups:
+    roles=REQUEST['group_'+group]
+    groupstool.editGroup(group, roles = roles, REQUEST=context.REQUEST, )
+
+
+delete=REQUEST.get('delete',[])
+groupstool.removeGroups(delete, REQUEST=context.REQUEST,)
+
+portal_status_message="Changes made."
+return state.set(portal_status_message=portal_status_message)
diff --git a/skins/gruf/prefs_group_manage.cpy.metadata b/skins/gruf/prefs_group_manage.cpy.metadata
new file mode 100755
index 0000000..8bda807
--- /dev/null
+++ b/skins/gruf/prefs_group_manage.cpy.metadata
@@ -0,0 +1,6 @@
+[validators]
+validators = 
+
+[actions]
+action.success = traverse_to:string:prefs_groups_overview
+action.failure = traverse_to:string:prefs_groups_overview
\ No newline at end of file
diff --git a/skins/gruf_plone_2_0/README.txt b/skins/gruf_plone_2_0/README.txt
new file mode 100755
index 0000000..2b7785f
--- /dev/null
+++ b/skins/gruf_plone_2_0/README.txt
@@ -0,0 +1,4 @@
+Here is the placeholder for files providing Plone 2.0 compatibility for GRUF 3.
+This is the case, for example, for local roles form or control panel stuff.
+
+This skin is empty by now. You don't have to worry about it.
diff --git a/skins/gruf_plone_2_0/folder_localrole_form.pt b/skins/gruf_plone_2_0/folder_localrole_form.pt
new file mode 100644
index 0000000..ed0d362
--- /dev/null
+++ b/skins/gruf_plone_2_0/folder_localrole_form.pt
@@ -0,0 +1,445 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
+      lang="en"
+      metal:use-macro="here/main_template/macros/master"
+      i18n:domain="plone">
+
+<metal:block fill-slot="top_slot"
+             tal:define="dummy python:request.set('enable_border',1)" />
+
+<body>
+
+  <div metal:fill-slot="main"
+       tal:define="Batch python:modules['Products.CMFPlone'].Batch;
+                   username member/getUserName;
+                   group_submit request/group_submit|nothing;
+                   b_size python:12;b_start python:0;b_start request/b_start | b_start;
+                   search_submitted request/role_submit|nothing;
+                   search_results python:search_submitted and mtool.searchForMembers(
+                                         {request.get('search_param',''):
+                                         request.get('search_term', '')}) or None;">
+
+    <h1 i18n:translate="heading_currently_assigned_shares">
+        Current sharing permissions for
+        <span tal:content="here/title_or_id" i18n:name="folder">title</span>
+    </h1>
+
+    <p i18n:translate="description_share_folders_items_current_shares">
+        You can share the rights for both folders (including content) and single items.
+        These users have privileges here:
+    </p>
+
+    <fieldset tal:define="iroles python:here.plone_utils.getInheritedLocalRoles(here);"
+              tal:condition="iroles">
+
+        <legend i18n:translate="legend_acquired_roles">
+            Acquired roles
+        </legend>
+
+        <table class="listing" summary="Acquired roles">
+            <thead>
+            <tr>
+                <th i18n:translate="label_user_group_name">User/Group name</th>
+                <th i18n:translate="label_type">Type</th>
+                <th i18n:translate="label_roles">Role(s)</th>
+            </tr>
+            </thead>
+            <tbody>
+            <tr tal:repeat="irole iroles">
+                <td tal:content="python:irole[0]">
+                    groupname
+                </td>
+
+                <td tal:condition="python:irole[2]=='group'"
+                    i18n:translate="label_group">
+                    Group
+                </td>
+                <td tal:condition="python:irole[2]=='user'"
+                    i18n:translate="label_user">
+                    User
+                </td>
+
+                <td>
+                <tal:block tal:repeat="role python:irole[1]">
+                    <span i18n:translate=""
+                          tal:content="role"
+                          tal:omit-tag="">Role</span>
+                    <span tal:condition="not: repeat/role/end"
+                          tal:omit-tag="">, </span>
+                </tal:block>
+                </td>
+            </tr>
+            </tbody>
+        </table>
+
+    </fieldset>
+
+    <form method="post"
+          name="deleterole"
+          action="folder_localrole_edit"
+          tal:attributes="action string:$here_url/folder_localrole_edit">
+
+      <fieldset>
+
+        <legend i18n:translate="legend_assigned_roles">
+            Assigned Roles
+            <span tal:content="here/title_or_id" i18n:name="folder">title</span>
+        </legend>
+
+        <input type="hidden" name="change_type" value="delete" />
+        <input type="hidden" name="member_role" value="" />
+
+        <table class="listing" summary="Currently assigned local roles">
+            <thead>
+            <tr>
+                <th>
+                    <input type="checkbox"
+                       onclick="javascript:toggleSelect(this, 'member_ids:list', false, 'deleterole');"
+                       name="alr_toggle"
+                       value="#"
+                       id="alr_toggle"
+                       class="noborder"
+                       />
+                </th>
+                <th i18n:translate="label_user_group_name">User/Group name</th>
+                <th i18n:translate="label_type">Type</th>
+                <th i18n:translate="label_roles">Role(s)</th>
+            </tr>
+            </thead>
+            <tbody>
+            <tr tal:repeat="lrole python:here.acl_users.getLocalRolesForDisplay(here)">
+                <td class="field">
+                    <label class="hiddenLabel" for="member_ids:list"
+                           i18n:translate="label_select_usergroup">
+                        select user/group <span tal:content="python:lrole[3]" i18n:name="role"/>
+                    </label>
+                    <input class="formSelection"
+                           type="checkbox"
+                           name="member_ids:list"
+                           id="#"
+                           value=""
+                           tal:condition="python:lrole[0]!=username"
+                           tal:attributes="value python:lrole[3];"
+                           />
+                </td>
+
+                <td tal:content="python:lrole[0]">
+                    groupname
+                </td>
+
+                <td tal:condition="python:lrole[2]=='group'"
+                    i18n:translate="label_group">
+                    Group
+                </td>
+                <td tal:condition="python:lrole[2]=='user'"
+                    i18n:translate="label_user">
+                    User
+                </td>
+
+                <td>
+                <tal:block tal:repeat="role python:lrole[1]">
+                    <span i18n:translate=""
+                          tal:content="role"
+                          tal:omit-tag="">Role</span>
+                    <span tal:condition="not: repeat/role/end"
+                          tal:omit-tag="">, </span>
+                </tal:block>
+                </td>
+            </tr>
+            </tbody>
+        </table>
+
+        <div class="submit">
+            <input class="context"
+                type="submit"
+                value="Delete Selected Role(s)"
+                i18n:attributes="value"
+                />
+        </div>
+
+        </fieldset>
+
+    </form>
+
+    <metal:block tal:condition="python:test(search_submitted and not search_results, 1, 0)">
+        <h1 i18n:translate="heading_search_results">Search results</h1>
+        <p i18n:translate="no_members_found">
+            No members were found using your <strong>Search Criteria</strong>
+        </p>
+        <hr />
+    </metal:block>
+
+    <metal:block tal:condition="python:test(search_submitted and search_results, 1, 0)">
+
+        <h1 i18n:translate="heading_search_results">Search results</h1>
+
+        <p i18n:translate="description_localrole_select_member">
+            Select one or more people, and a role to assign.
+        </p>
+
+        <metal:block tal:define="batch python:Batch(search_results, b_size, int(b_start), orphan=3);
+                                 nResults python:len(search_results);">
+
+        <form method="post"
+              name="change_type"
+              action="folder_localrole_edit"
+              tal:attributes="action string:$here_url/folder_localrole_edit">
+
+            <fieldset>
+
+                <legend i18n:translate="legend_available_members">Available Members</legend>
+
+                <input type="hidden" name="change_type" value="add" />
+
+                <!-- batch navigation -->
+                <div metal:use-macro="here/batch_macros/macros/navigation" />
+
+                <table class="listing" summary="Search results">
+                    <thead>
+                    <tr>
+                        <th>
+                            <input type="checkbox"
+                               onclick="javascript:toggleSelect(this, 'member_ids:list', false, 'change_type');"
+                               name="alr_toggle"
+                               value="#"
+                               id="alr_toggle"
+                               class="noborder"
+                               />
+                        </th>
+                        <th i18n:translate="label_user_name">User Name</th>
+                        <th i18n:translate="label_email_address">Email Address</th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    <tr tal:repeat="member batch">
+                        <td class="field" tal:define="global member_username member/getUserName">
+                            <label class="hiddenLabel" for="member_ids:list"
+                                   i18n:translate="label_select_user">
+                                select user <span tal:content="member_username" i18n:name="user" />
+                            </label>
+                            <input class="formSelection"
+                                   type="checkbox"
+                                   name="member_ids:list"
+                                   id="#"
+                                   value=""
+                                   tal:attributes="value member_username;
+                                                   checked python:nResults==1;"
+                            />
+                        </td>
+
+                        <td tal:content="python:member_username">username</td>
+                        <td tal:content="member/email">email</td>
+                    </tr>
+                    </tbody>
+                </table>
+
+                <!-- batch navigation -->
+                <div metal:use-macro="here/batch_macros/macros/navigation" />
+
+                <div class="field">
+
+                    <label for="user_member_role" i18n:translate="label_localrole_to_assign">
+                        Role to assign
+                    </label>
+
+                    <select name="member_role:list"
+                            id="user_member_role"
+                            multiple="multiple">
+                        <option tal:repeat="lroles python:mtool.getCandidateLocalRoles(here)"
+                                tal:attributes="value lroles"
+                                tal:content="lroles"
+                                i18n:translate="">
+                            Role name
+                        </option>
+                    </select>
+
+                </div>
+
+                <div class="submit">
+                    <input class="context"
+                            type="submit"
+                            value="Assign Local Role to Selected User(s)"
+                            i18n:attributes="value"
+                            />
+                </div>
+
+            </fieldset>
+
+        </form>
+
+      </metal:block>
+    </metal:block>
+
+    <div>
+      <tal:block tal:condition="python: (not search_submitted or
+                                        (search_submitted and not search_results))">
+
+        <h1 i18n:translate="heading_add_sharing_permissions">
+          Add sharing permissions for
+          <tal:block tal:content="here/title_or_id" i18n:name="item">title</tal:block>
+        </h1>
+
+
+        <p i18n:translate="description_sharing_item">
+        Sharing is an easy way to allow others access to collaborate with you
+        on your content.
+
+        To share this item, search for the person's
+        name or email address in the form below, and assign them an appropriate role.
+        The most common use is to give people Manager permissions, which means they
+        have full control of this item and its contents (if any).
+        </p>
+
+        <form method="post"
+              name="localrole"
+              action="folder_localrole_form"
+              tal:attributes="action string:$here_url/${template/getId}" >
+
+            <fieldset>
+
+                <legend i18n:translate="legend_search_terms">Search Terms</legend>
+
+                <input type="hidden" name="role_submit" value="role_submit" />
+
+                <div class="field">
+                    <label for="search_param" i18n:translate="label_search_by">
+                        Search by
+                    </label>
+
+                    <select name="search_param"
+                            id="search_param">
+                        <option value="name" i18n:translate="label_user_name">
+                            User Name
+                        </option>
+                        <option value="email" i18n:translate="label_email_address">
+                            Email Address
+                        </option>
+                    </select>
+
+                </div>
+
+                <div class="field">
+                    <label for="search_term" i18n:translate="label_search_term">
+                        Search Term
+                    </label>
+
+                    <input type="text"
+                            id="search_term"
+                            name="search_term"
+                            size="30"
+                            />
+                </div>
+
+                <div class="submit">
+                    <input class="context"
+                            type="submit"
+                            value="Perform Search"
+                            i18n:attributes="value"
+                            />
+                </div>
+
+            </fieldset>
+
+        </form>
+      </tal:block>
+
+      <tal:groupshares define="grouplist gtool/listGroups"
+                       condition="grouplist">
+
+          <h1 i18n:translate="heading_group_shares">Add sharing permissions to groups</h1>
+
+          <p i18n:translate="description_group_shares">
+            Groups are a convenient way to share items to a common set of
+            users. Select one or more groups, and a role to assign.
+          </p>
+
+          <form method="post"
+                name="change_type_group"
+                action="folder_localrole_edit"
+                tal:attributes="action string:$here_url/folder_localrole_edit">
+
+            <fieldset>
+
+                    <legend i18n:translate="legend_available_groups">
+                        Available Groups
+                    </legend>
+
+                    <input type="hidden" name="change_type" value="add" />
+
+                    <table class="listing" summary="Available groups">
+                    <thead>
+                        <tr>
+                        <th>
+                            <input type="checkbox"
+                               onclick="javascript:toggleSelect(this, 'member_ids:list', false, 'change_type_group');"
+                               name="alr_toggle"
+                               value="#"
+                               id="alr_toggle"
+                               class="noborder"
+                               />
+                        </th>
+                        <th i18n:translate="listingheader_name">Name</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <tr tal:repeat="group grouplist">
+                        <td tal:define="global group_name group/getUserId">
+                            <label class="hiddenLabel" for="member_ids:list"
+                                   i18n:translate="label_select_group">
+                                select group <span tal:content="group_name" i18n:name="name"/>
+                            </label>
+                            <input class="formSelection"
+                                type="checkbox"
+                                name="member_ids:list"
+                                id="#"
+                                value=""
+                                tal:attributes="value group_name;" />
+                        </td>
+                        <td tal:content="group/getUserNameWithoutGroupPrefix">
+                            groupname
+                        </td>
+                        </tr>
+                    </tbody>
+                    </table>
+
+                    <div class="field">
+
+                        <label for="group_member_role" i18n:translate="label_localrole_to_assign">
+                            Role to assign
+                        </label>
+
+                        <select name="member_role:list"
+                                id="group_member_role"
+                                multiple="multiple">
+                            <option tal:repeat="lroles python:mtool.getCandidateLocalRoles(here)"
+                                    tal:attributes="value lroles"
+                                    tal:content="lroles"
+                                    i18n:translate="">
+                                Role name
+                            </option>
+                        </select>
+                    </div>
+
+                    <div class="submit">
+                        <input class="context"
+                            type="submit"
+                            value="Assign Local Role to Selected Group(s)"
+                            i18n:attributes="value"
+                            />
+                    </div>
+
+                </fieldset>
+
+            </form>
+
+        </tal:groupshares>
+
+      <div metal:use-macro="here/document_byline/macros/byline">
+        Get the byline - contains details about author and modification date.
+      </div>
+
+    </div>
+
+  </div>
+
+</body>
+</html>
diff --git a/svn-commit.tmp b/svn-commit.tmp
new file mode 100644
index 0000000..5a8a033
--- /dev/null
+++ b/svn-commit.tmp
@@ -0,0 +1,4 @@
+Création branche pour compat Zope-2.12
+--This line, and those below, will be ignored--
+
+A    http://svn.cri.ensmp.fr/svn/GroupUserFolder/branches/zope-2.12
diff --git a/tool.gif b/tool.gif
new file mode 100644
index 0000000000000000000000000000000000000000..8aa90b53b65ee410b8a9be3819fcba223c936c4e
GIT binary patch
literal 166
zcmZ?wbhEHb6krfwc+9}CY0HL=j*jye&gSIiPn<a6=+R>pRTa;lKc6*c#>$l|!@|OE
z+_;gLl<4i_^&bor|MB@IrlcyAXO?6rxO@5rFev_HVdP@qXV75)0+6W;ERG*edamB8
z5Tng@Gc-|#-=T@ALFE+FH0H}Z`3En&SC;VkC&SXpufZbA$LjKNlL8A@=P~XD7D8Nh
Qa~U7Jeo%8GoQ1&}0ANHx<p2Nx

literal 0
HcmV?d00001

diff --git a/version.txt b/version.txt
new file mode 100644
index 0000000..c5ddf26
--- /dev/null
+++ b/version.txt
@@ -0,0 +1 @@
+3.55.1
diff --git a/www/GRUFGroups.gif b/www/GRUFGroups.gif
new file mode 100644
index 0000000000000000000000000000000000000000..6a7fb9f7854132370967600d39d87e4ad0207ff4
GIT binary patch
literal 607
zcmZwEJ7^R^0LJmn!{jhQR}*0p6|*Afm`Jkj`B)!FCYr>Fidn_S7zknX)LTr%K{?n)
zHWMLan!q-Jt&~Z3U^AfLRl*h_Zn<Eyg@|W&U~^cFm)>9dHGlY~u1}tK7fi!6ei=rg
zP~bVQc>tgU2moxsfSE}mBT85kGFWfw?G1-iDMBbytR?nMR|G`lLI{`C2_s0MW@M)V
zvKmKJ#+kM)GpT1111;S=i31<0JV|1YtA?#?VVe*_OdG`4v_`e^R5e1La2zM47V&63
zfLdz{ryf(1qf`l(xl&43xX90aq4UW05|8o>;#M^9WT2~<b6%IoCW#W*DLE`-v>G(B
zYNog(R;gvX)NDms77es%npk4J;UEZzV%U=|w{eg+B8V}DIJTLsYaYZ&;#-I`(ioGV
zkt@H7O+rXk6TmE_zEvZ@LW!=C$TqdYAObLegi%bJgz37(fn`CQrm<)IkM*~+v)|Ch
z$j-R)iP8P#trITum3J%Fvm5959<%@I=yLaynZ0M1pWbrU=bU4`w0B@%I68N)d>Sk`
zujgOPk6hk7aT9>sW#`(`$kN0>IDXhKm&Zy@W$4?`(eTdmBfj5$Jp2S6@o{5uvCF<Y
zd}^xjkRRMN`{u#q{kEPSJbWR%;$7-H)c5!RnygI!`Ptq!So}TxeHP7xyZ0B1l^<U_
p<F<~I+q3KMLooE}Wo7k)vAQyNrt9P9j?J~Ti7~oW>@OG>{{e%N?3Vxl

literal 0
HcmV?d00001

diff --git a/www/GRUFUsers.gif b/www/GRUFUsers.gif
new file mode 100644
index 0000000000000000000000000000000000000000..cc199e6cb39bbf81e924769c2ca2cb8691e12b7c
GIT binary patch
literal 539
zcmdUsJx|*}06;HkT0+x;TM}@;5)RT9Lr6IiNUllvI3y4^-slZQ%5tNZN?uBEGI{iZ
z<cv4oQprPyW5W|-M4dX;W2V;XbX9&v-w$|t(mOjpZQ~0A7~mHG7NJv}CO#A#B8kk<
z{3&h2HA1i7SAA}y2g#KSlaNLMWei&Zvbp0sm?%OW)46xj5GTsfl2XBx;L^jG(^U4g
zAX;nbQ&;)Zh!SR-4%CV<ZZXBI&`Ol=a@4qPNY|PQGPoPW0gYW#M@(Ngpnz0SEO8n!
zBv9g8Dqzwlu?NM>V;i0kI9NwC7M#T_6$Jc;z`wa%5ol1Gwa=b_xi4QU!z*tzdcClk
zACI~PYvswNKg@SO72Z966jbnbueZOp|1vl}IYRG|c{u5mzm=DtPHMZ~ciW4T%1*YO
z{cuw$zIpa}erwV!y&Bd_cjJv_y`IVZXuTaba!s-YKQ2|l_~3DCZ*R`1uD`hb{pS*N
F{sB2>h*<yt

literal 0
HcmV?d00001

diff --git a/www/GroupUserFolder.gif b/www/GroupUserFolder.gif
new file mode 100644
index 0000000000000000000000000000000000000000..cbfdef2b52bfb39e01d14e9e3706eb3a131b2ba6
GIT binary patch
literal 600
zcmZ?wbhEHb6krfwc*Xz%m+!L8@DLVtIQsbYSD*02X$w}YezZeXTfct)*S9Ne3-1)A
zrKK(Fws(yEBs@*npu^zE()<gViai(g8mb!1TP=K+1RUBl*CKM!*EYop7n%S68J^xg
zap1rK74Pd6M<(ZM8?N5m<kJ><txqd;{`-f^y$+u9o0(>;C(ia@XZijrJFnwjAMOe%
zIZd9oDZ0L@;Q#;s>o=~>D=_zSbI5T_y19R<S<Ieo0UZB#285No2v}d3URd+;<;(s3
zk!r5HP2vt+>-GLW)A;je1|ELC`_+0gjTv&<{`(bdS+rznKtModU8Yy@v)3DZkL=tT
zoc-(a^4NRN7`}Y@@_&lf?P=COH)thpD>Y8KV&Hu<-^OTHjsK&^d>dcvU(;e?ImyzY
z-oY+t=7%R2+H3Q}3%<R7|9<AonOnDRy>p<ytg3pxHFrZp!@YYPG7e{N<jMcv9r)uL
z*Ru7?zO3^MD*pE932RGjAvkmi5Q;xpSVS29Gw6WSf#QULeQSe3Q*%pei)V*tTdSw6
zbYEAuL2v)m&go5_(guMx$u><1X{Is;43iC{Q+U`|t@8x^HDmObcNrQPM{22ytNL&$
zCvG#y;*gRIa?vTs%?&O-;^EAcBV_Iy;^=6be#FUBBO@b1SkZx(G4qJ5ny!M0y;U?z
zsF>FgJxezg&cZNxKAnh<4L2D0{f_7qHaNPp>Iv)-uue)oD=Htq<w(OPH$K*A&r2OL
Uo<UDE9T|dF#mp3PQ(&+L0J?wnkN^Mx

literal 0
HcmV?d00001

diff --git a/www/LDAPGroupFolder.gif b/www/LDAPGroupFolder.gif
new file mode 100644
index 0000000000000000000000000000000000000000..cbb71a41b288e439c4f10506f8c22a790905d8ae
GIT binary patch
literal 977
zcmZ?wbhEHb6krfw_|Cu(?P(O{X%gdM=;!B`J6*o4Bcx{GiODJ26VnW)rQ7bVGHEN0
zX)lSJTHrXP*nL)YNN-*4vU1;>by|1pb(*)Sw(r#L+GE@?H*r#9&ZPYgyXKZoKkPp1
zsPFvaVJpvOZoW`=Xlm4>sm{+Ahdo~!_I7K|hyB&_r}wnX-a2{tzQv0duV24@>Y<O*
z4}F|>?9<W{Zx){Xy!`a%wdcQVKL38-&2I-+cAVcj;l_^6LpNufdbIfZ^SzIcO#gVe
z`O~Ss@3&U}e6Z{8%`3<5em{Qi`>6-t&prBi@yU<NPk&y2@$=TpU%y|SfB5j>t5>i7
z|NqZ03PwW!M+hkXWMO1r@Mh2f$%FC)1IIds=^Qd18x|aFv^UT>@Zqt-Ayz&q1%V3_
z5)QT6%U!v^_=tg_TR@d-!NbMJ`B)h_j4qtGsN654q{5@X;plvFGNW6|j0p;yj>2Yf
z$7BQ+uJCX23VQKmWfRLJA!ari(G^QFT4XG^rtH{ou<44bXRt-9)q`WLe3DuL5)-y`
zOb}KLI8b2tfSH}2<=Qd}CzTUCB8D+r5(Kw?Xb^F0+ryxA*p-J_J59*M$8y!-(=5zP
G4AuZvXxKdf

literal 0
HcmV?d00001

diff --git a/www/down_arrow.gif b/www/down_arrow.gif
new file mode 100644
index 0000000000000000000000000000000000000000..f8da0c6efdee8dca2198c19f00678f1f21d0cc09
GIT binary patch
literal 56
zcmZ?wbhEHb<Y3@nXkcLY|NlQj+DyftEUY373=BFz3>4>JU|`~J5nk3%{qmNxN$AOp
JmIVw9)&P@L4-fzV

literal 0
HcmV?d00001

diff --git a/www/down_arrow_grey.gif b/www/down_arrow_grey.gif
new file mode 100644
index 0000000000000000000000000000000000000000..5e90141178cbca615b27921ff0bf5249af91fb7d
GIT binary patch
literal 56
zcmZ?wbhEHb<Y3@nXkcLY|NsA)GiMZkvapIUFfix<F;JX?fq{v?MR-|5^~+n%CZQ)Y
KS{5)cSOWmcQ4n<i

literal 0
HcmV?d00001

diff --git a/www/up_arrow.gif b/www/up_arrow.gif
new file mode 100644
index 0000000000000000000000000000000000000000..7ed42e2e0cc11bbbfb415fd26363e59de4f4e1bb
GIT binary patch
literal 54
zcmZ?wbhEHb<Y3@nXkcJqNSpcp|9{1wEUY37j0`$J0iZYs0|OIp596|q(|_j$Pi{*q
I)L^g%0CC0+e*gdg

literal 0
HcmV?d00001

diff --git a/www/up_arrow_grey.gif b/www/up_arrow_grey.gif
new file mode 100644
index 0000000000000000000000000000000000000000..7679aa43a0a9e4ed80a94f5206f46286862d12f9
GIT binary patch
literal 54
zcmZ?wbhEHb<Y3@nXkcJCbLPzd|Nj+#vapIUFf!;c00Bsbfr+<=aaqUdzw?48x1|+o
HFjxZsswWTR

literal 0
HcmV?d00001

-- 
2.20.1