[Zope-CVS] SVN: zc.sharing/trunk/ import code
Jim Fulton
jim at zope.com
Wed Mar 1 14:14:49 EST 2006
Log message for revision 65678:
import code
Changed:
A zc.sharing/trunk/
A zc.sharing/trunk/src/
A zc.sharing/trunk/src/zc/
A zc.sharing/trunk/src/zc/sharing/
A zc.sharing/trunk/src/zc/sharing/__init__.py
A zc.sharing/trunk/src/zc/sharing/browser/
A zc.sharing/trunk/src/zc/sharing/browser/__init__.py
A zc.sharing/trunk/src/zc/sharing/browser/configure.zcml
A zc.sharing/trunk/src/zc/sharing/browser/ftesting.zcml
A zc.sharing/trunk/src/zc/sharing/browser/functional.txt
A zc.sharing/trunk/src/zc/sharing/browser/group_icon.gif
A zc.sharing/trunk/src/zc/sharing/browser/ntests.py
A zc.sharing/trunk/src/zc/sharing/browser/sharing.pt
A zc.sharing/trunk/src/zc/sharing/browser/sharing.py
A zc.sharing/trunk/src/zc/sharing/browser/sharing.txt
A zc.sharing/trunk/src/zc/sharing/browser/test_template.pt
A zc.sharing/trunk/src/zc/sharing/browser/tests.py
A zc.sharing/trunk/src/zc/sharing/browser/user_icon.gif
A zc.sharing/trunk/src/zc/sharing/configure.zcml
A zc.sharing/trunk/src/zc/sharing/generation/
A zc.sharing/trunk/src/zc/sharing/generation/__init__.py
A zc.sharing/trunk/src/zc/sharing/generation/install.py
A zc.sharing/trunk/src/zc/sharing/i18n.py
A zc.sharing/trunk/src/zc/sharing/index.py
A zc.sharing/trunk/src/zc/sharing/index.txt
A zc.sharing/trunk/src/zc/sharing/interfaces.py
A zc.sharing/trunk/src/zc/sharing/meta.zcml
A zc.sharing/trunk/src/zc/sharing/policy.py
A zc.sharing/trunk/src/zc/sharing/policy.txt
A zc.sharing/trunk/src/zc/sharing/sharing.py
A zc.sharing/trunk/src/zc/sharing/sharing.txt
A zc.sharing/trunk/src/zc/sharing/tests.py
A zc.sharing/trunk/src/zc/sharing/utils.py
A zc.sharing/trunk/src/zc/sharing/zcml.py
A zc.sharing/trunk/src/zc/sharing/zcml.txt
-=-
Added: zc.sharing/trunk/src/zc/sharing/__init__.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/__init__.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/__init__.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1 @@
+#
Property changes on: zc.sharing/trunk/src/zc/sharing/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/__init__.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/browser/__init__.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/browser/__init__.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1 @@
+#
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/configure.zcml
===================================================================
--- zc.sharing/trunk/src/zc/sharing/browser/configure.zcml 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/browser/configure.zcml 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,29 @@
+<configure
+ xmlns:zope="http://namespaces.zope.org/zope"
+ xmlns="http://namespaces.zope.org/browser"
+ i18n_domain="zc.sharing">
+
+
+<page
+ for="..interfaces.ISharable"
+ name="sharing.html"
+ menu="zmi_views"
+ title="Sharing"
+ template="sharing.pt"
+ class=".sharing.SharingTab"
+ permission="zope.Security"
+ />
+
+<resource
+ name="user_icon.gif"
+ image="user_icon.gif"
+ permission="zope.Public"
+ />
+
+<resource
+ name="group_icon.gif"
+ image="group_icon.gif"
+ permission="zope.Public"
+ />
+
+</configure>
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/ftesting.zcml
===================================================================
--- zc.sharing/trunk/src/zc/sharing/browser/ftesting.zcml 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/browser/ftesting.zcml 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,118 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ xmlns:zc="http://namespaces.zope.com/zc"
+ i18n_domain="zope"
+ package="zc.sharing"
+ >
+
+ <!-- This file is the equivalent of site.zcml and it is -->
+ <!-- used for functional testing setup -->
+
+ <include package="zope.app" />
+ <include package="zope.app.authentication" />
+
+ <!-- Principals -->
+
+ <unauthenticatedPrincipal
+ id="zope.anybody"
+ title="Unauthenticated User" />
+
+ <authenticatedGroup
+ id="zope.Authenticated"
+ title="Everybody"
+ />
+
+ <!-- Principal that tests generally run as -->
+ <principal
+ id="zope.mgr"
+ title="Manager"
+ login="mgr"
+ password="mgrpw" />
+
+ <!-- Bootstrap principal used to make local grant to the principal above -->
+ <principal
+ id="zope.globalmgr"
+ title="Manager"
+ login="globalmgr"
+ password="globalmgrpw" />
+
+ <include package="zc.sharing" file="meta.zcml" />
+ <include package="zc.sharing" />
+
+ <zc:privilege bit="0" title="Read"
+ description="Read or view content"
+ />
+
+ <zc:permissionPrivilege permission="zope.View"
+ privilege="0"
+ />
+
+ <zc:permissionPrivilege permission="zope.app.dublincore.view"
+ privilege="0"
+ />
+
+ <zc:privilege bit="2" title="Write"
+ description="Modify content"
+ />
+
+ <zc:permissionPrivilege permission="zope.ManageContent"
+ privilege="2"
+ />
+ <zc:permissionPrivilege permission="zope.app.dublincore.change"
+ privilege="2"
+ />
+
+ <zc:privilege bit="4" title="Share"
+ description="Share content"
+ />
+
+ <zc:permissionPrivilege permission="zope.Security"
+ privilege="4"
+ />
+
+ <zc:privileges for="zc.sharing.interfaces.ISharable"
+ titles="Read Write Share"
+ />
+
+ <zc:subobjectPrivileges
+ for="zope.app.container.interfaces.IContainer"
+ titles="Read Write Share"
+ />
+
+ <securityPolicy component=".policy.SecurityPolicy" />
+
+
+ <zc:systemAdministrators principals="zope.globalmgr zope.mgr" />
+
+
+ <!-- XXX We need to explain/rationalize this better. -->
+ <!-- If the root object or other objects on the way to sharable -->
+ <!-- objects are not sharable, then we need to either: -->
+ <!-- -->
+ <!-- o Make reading them public or -->
+ <!-- -->
+ <!-- o Provide public traversal adapters -->
+ <content class="zope.app.folder.Folder">
+ <require
+ permission="zope.Public"
+ interface="zope.app.container.interfaces.IReadContainer"
+ />
+ <implements interface="zc.sharing.interfaces.ISharable" />
+ </content>
+
+ <utility component=".browser.ntests.formatterFactory" />
+ <utility
+ factory=".browser.ntests.Authentication"
+ provides="zope.app.security.interfaces.IAuthentication"
+ />
+
+ <browser:page
+ for="zope.app.folder.interfaces.IFolder"
+ name="test_greet"
+ permission="zope.View"
+ template="browser/test_template.pt"
+ />
+
+
+</configure>
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/ftesting.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/functional.txt
===================================================================
--- zc.sharing/trunk/src/zc/sharing/browser/functional.txt 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/browser/functional.txt 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,175 @@
+Basic Functional Demonstration
+==============================
+
+We can try to log on as jim:
+
+
+We were able to log in as Jim, but we weren't able to access the site,
+because it hasn't been shared to anyone but the management user.
+
+Let's start by sharing it to everybody (authenticated):
+
+ >>> print http(r"""
+ ... POST /@@sharing.html HTTP/1.1
+ ... Authorization: Basic mgr:mgrpw
+ ... Content-Type: application/x-www-form-urlencoded
+ ... Referer: http://localhost:8081/@@sharing.html
+ ...
+ ... sharing.em9wZS5BdXRoZW50aWNhdGVk.0.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.0=on"""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.2.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.4.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.6.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.8.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.10.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.12.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.14.used="""
+ ... """&effective_principal_type=group"""
+ ... """&setRecursiveSharingInfo=+Share+"""
+ ... """&effective_principal_text=""", handle_errors=False)
+ HTTP/1.1 200 Ok
+ ...
+
+Now, with this, Jim can see /, but not /manage
+
+ >>> print http(r"""
+ ... GET /test_greet HTTP/1.1
+ ... Authorization: Basic jim:eek
+ ... """, handle_errors=False)
+ HTTP/1.1 200 Ok
+ ...
+
+ >>> print http(r"""
+ ... GET /manage HTTP/1.1
+ ... Authorization: Basic jim:eek
+ ... """)
+ HTTP/1.1 401 Unauthorized
+ ...
+
+Now, we'll share all privileges but "Share" with Jim:
+
+ >>> print http(r"""
+ ... POST /@@sharing.html HTTP/1.1
+ ... Authorization: Basic mgr:mgrpw
+ ... Content-Type: application/x-www-form-urlencoded
+ ...
+ ... sharing.Mg%3D%3D.0.used="""
+ ... """&sharing.MQ%3D%3D.0=on"""
+ ... """&sharing.MQ%3D%3D.2.used="""
+ ... """&sharing.MQ%3D%3D.2=on"""
+ ... """&sharing.MQ%3D%3D.4.used="""
+ ... """&sharing.MQ%3D%3D.6.used="""
+ ... """&sharing.MQ%3D%3D.6=on"""
+ ... """&sharing.MQ%3D%3D.8.used="""
+ ... """&sharing.MQ%3D%3D.8=on"""
+ ... """&sharing.MQ%3D%3D.10.used="""
+ ... """&sharing.MQ%3D%3D.10=on"""
+ ... """&sharing.MQ%3D%3D.12.used="""
+ ... """&sharing.MQ%3D%3D.12=on"""
+ ... """&sharing.MQ%3D%3D.14.used="""
+ ... """&sharing.MQ%3D%3D.14=on"""
+ ... """&sharing.Mw%3D%3D.0.used="""
+ ... """&sharing.Mw%3D%3D.2.used="""
+ ... """&sharing.Mw%3D%3D.4.used="""
+ ... """&sharing.Mw%3D%3D.6.used="""
+ ... """&sharing.Mw%3D%3D.8.used="""
+ ... """&sharing.Mw%3D%3D.10.used="""
+ ... """&sharing.Mw%3D%3D.12.used="""
+ ... """&sharing.Mw%3D%3D.14.used="""
+ ... """&effective_principal_type=user"""
+ ... """&effective_principal_text=i"""
+ ... """&setSharingInfo=+Share+""")
+ HTTP/1.1 200 Ok
+ ...
+
+which lets Jim get /manage and /@@contents:
+
+ >>> print http(r"""
+ ... GET /manage HTTP/1.1
+ ... Authorization: Basic jim:eek
+ ... """, handle_errors=False)
+ HTTP/1.1 303 See Other
+ Content-Length: 0
+ Content-Type: text/plain;charset=utf-8
+ Location: @@contents.html
+ <BLANKLINE>
+
+
+ >>> contents = http(r"""
+ ... GET /@@contents.html HTTP/1.1
+ ... Authorization: Basic jim:eek
+ ... Cache-Control: max-age=0
+ ... """, handle_errors=False)
+ >>> print contents
+ HTTP/1.1 200 Ok
+ ...
+
+ >>> 'Sharing' not in str(contents)
+ True
+
+Now if we also share "Sharing" with Jim:
+
+ >>> print http(r"""
+ ... POST /@@sharing.html HTTP/1.1
+ ... Authorization: Basic mgr:mgrpw
+ ... Content-Type: application/x-www-form-urlencoded
+ ... Referer: http://localhost:8081/@@sharing.html
+ ...
+ ... sharing.MQ%3D%3D.select.used="""
+ ... """&sharing.MQ%3D%3D.0.used="""
+ ... """&sharing.MQ%3D%3D.0=on"""
+ ... """&sharing.MQ%3D%3D.2.used="""
+ ... """&sharing.MQ%3D%3D.2=on"""
+ ... """&sharing.MQ%3D%3D.4.used="""
+ ... """&sharing.MQ%3D%3D.4=on"""
+ ... """&sharing.MQ%3D%3D.6.used="""
+ ... """&sharing.MQ%3D%3D.6=on"""
+ ... """&sharing.MQ%3D%3D.8.used="""
+ ... """&sharing.MQ%3D%3D.8=on"""
+ ... """&sharing.MQ%3D%3D.10.used="""
+ ... """&sharing.MQ%3D%3D.10=on"""
+ ... """&sharing.MQ%3D%3D.12.used="""
+ ... """&sharing.MQ%3D%3D.12=on"""
+ ... """&sharing.MQ%3D%3D.14.used="""
+ ... """&sharing.MQ%3D%3D.14=on"""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.select.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.0.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.0=on"""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.2.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.4.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.6.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.8.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.10.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.12.used="""
+ ... """&sharing.em9wZS5BdXRoZW50aWNhdGVk.14.used="""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.select.used="""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.0.used="""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.0=on"""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.2.used="""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.2=on"""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.4.used="""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.4=on"""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.6.used="""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.6=on"""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.8.used="""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.8=on"""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.10.used="""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.10=on"""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.12.used="""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.12=on"""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.14.used="""
+ ... """&sharing.em9wZS5tYW5hZ2Vy.14=on"""
+ ... """&setSharingInfo=+Apply+""")
+ HTTP/1.1 200 Ok
+ ...
+
+Then if we visit contents, the sharing tab will be included:
+
+ >>> print http(r"""
+ ... GET /@@contents.html HTTP/1.1
+ ... Authorization: Basic jim:eek
+ ... Cache-Control: max-age=0
+ ... """)
+ HTTP/1.1 200 Ok
+ ...Sharing...
+
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/functional.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/group_icon.gif
===================================================================
(Binary files differ)
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/group_icon.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/ntests.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/browser/ntests.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/browser/ntests.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,92 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""
+
+$Id$
+"""
+import os
+import unittest
+from zope import component, interface
+import zope.security.interfaces
+import zope.app.security.interfaces
+from zope.app.testing import functional
+
+import zc.security.interfaces
+import zc.testlayer.ftesting
+import zc.table.table
+import zc.table.interfaces
+
+class Principal:
+ interface.implements(zope.security.interfaces.IPrincipal)
+
+ def __init__(self, id, title):
+ self.id = id
+ self.title = title
+ self.groups = 'zope.Authenticated',
+
+
+class Authentication:
+ interface.implements(
+ zope.app.security.interfaces.IAuthentication,
+ zc.security.interfaces.ISimpleUserSearch,
+ zc.security.interfaces.ISimpleGroupSearch,
+ )
+
+ def __init__(self):
+ self.byId = dict(
+ [(p.id, p) for p in [
+ Principal('1', 'jim'),
+ Principal('2', 'bob'),
+ Principal('3', 'sally'),
+ Principal('zope.manager', 'manager'),
+ Principal('zope.Authenticated', 'Everybody'),
+ ]
+ ])
+
+ self.byCred = {
+ 'jim:eek': self.byId['1'],
+ }
+
+ def searchUsers(self, filter, start, size):
+ return '1', '2', '3'
+
+ def searchGroups(self, filter, start, size):
+ return 'zope.manager', 'zope.Authenticated'
+
+ def authenticate(self, request):
+ if request._auth:
+ credentials = request._auth.split()[-1]
+ return self.byCred.get(credentials.decode('base64'))
+
+ def getPrincipal(self, id):
+ return self.byId.get(id)
+
+def formatterFactory(*args, **kw):
+ return zc.table.table.FormFullFormatter(*args, **kw)
+interface.directlyProvides(formatterFactory,
+ zc.table.interfaces.IFormatterFactory)
+
+SharingLayer = zc.testlayer.ftesting.FTestingLayer(
+ os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'),
+ __name__, 'SharingLayer')
+
+def test_suite():
+ suite = functional.FunctionalDocFileSuite('functional.txt')
+ suite.layer = SharingLayer
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
+
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/ntests.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/sharing.pt
===================================================================
--- zc.sharing/trunk/src/zc/sharing/browser/sharing.pt 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/browser/sharing.pt 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,97 @@
+<html metal:use-macro="context/@@standard_macros/view"
+ i18n:domain="zc.intranet">
+<head>
+</head>
+<body>
+<div metal:fill-slot="body">
+
+ <form name="sharingform" id="sharingform" method="post"
+ tal:attributes="action request/URL">
+
+ <div id="viewspace">
+ <h1 i18n:translate="">Sharing</h1>
+
+ <div class="message"
+ i18n:translate=""
+ tal:condition="view/message"
+ tal:content="view/message">
+ </div>
+ <div style="width: 100%"> <!-- this is a workaround for an IE bug -->
+ <table class="listingdescription" style="width:100%">
+ <col width="1%">
+ <col class="principal">
+ <col span="3"
+ tal:attributes="span view/nPrivileges"
+ tal:condition="view/nPrivileges"
+ class="privileges"
+ >
+ <col span="3"
+ tal:attributes="span view/nSubobjectPrivileges"
+ tal:condition="view/nSubobjectPrivileges"
+ class="subobjectPrivileges"
+ >
+ <thead tal:content="structure view/formatter/renderHeaderRow">
+ </thead>
+ <tbody tal:content="structure view/formatter/renderRows">
+ </tbody>
+ </table>
+ <div tal:content="structure view/formatter/renderExtra" />
+ </div> <!-- IE bug workaround -->
+ </div> <!-- id="viewspace" -->
+
+
+
+ <div id="actionsView">
+ <div class="action-buttons">
+ <input value="Apply" name="setSharingInfo" type="submit"
+ class="submit" i18n:attributes="value" />
+ <input value="Apply to this and all subobjects"
+ name="setRecursiveSharingInfo" type="submit" class="submit"
+ tal:condition="view/subobjects" i18n:attributes="value" />
+ <span class="createSelect">
+ <input type="hidden" name="effective_principal_text" value=""
+ tal:attributes="value view/effective_principal_text" >
+ <input type="hidden" name="effective_principal_type" value=""
+ tal:attributes="value view/effective_principal_type" >
+ <input type="text" name="principal_text"
+ tal:attributes="value view/principal_text|nothing" />
+ <select name="principal_type">
+ <option value="user" i18n:translate=""
+ tal:attributes="
+ selected python:view.principal_type!='group' and 'selected' or nothing"
+ >user</option>
+ <option value="group" i18n:translate=""
+ tal:attributes="
+ selected python:view.principal_type=='group' and 'selected' or nothing"
+ >group</option>
+ </select>
+ <input type="submit" value="Search" name="principal_search"
+ i18n:attributes="value"/>
+ </span>
+ <span class="createSelect" tal:condition="view/macros">
+ <label for="macros">Share with:</label>
+ <select name="macro" id="macro">
+ <option
+ i18n:translate>Select macro...</option>
+ <option tal:repeat="macro view/macros"
+ tal:content="macro"
+ i18n:translate=""
+ >Everyone readable (Public)</option>
+ </select>
+ <input type="submit" name="apply_sharing_macro"
+ value="Apply Macro" i18n:attributes="value"/>
+ </span>
+
+ </div> <!-- class="action-buttons" -->
+ </div> <!-- id="actionsview" -->
+
+</form>
+
+<script language="Javascript1.1">
+ // If the skin provides a trackChanges function, call it.
+ var trackChanges;
+ if (trackChanges) trackChanges(document.getElementById('sharingform'));
+</script>
+</div>
+</body>
+</html>
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/sharing.pt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/sharing.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/browser/sharing.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/browser/sharing.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,293 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""Sharing view
+
+
+$Id$
+"""
+import datetime
+
+from zc.sharing import interfaces
+from zc.table import table, column
+import zc.table.interfaces
+from zope import schema, component, interface
+from zope.app import zapi
+from zope.app.location.interfaces import ISublocations
+from zope.interface import Interface
+from zope.interface.common.idatetime import ITZInfo
+from zope.security.interfaces import IGroup
+import zope.app.security.interfaces
+
+from zc.sharing.i18n import _
+from zc.security.interfaces import ISimpleGroupSearch
+from zc.security.interfaces import ISimpleUserSearch
+from zc.sharing import policy
+from zc.sharing.sharing import sharingMask, getPrivilege
+
+class IPrivilegeColumn(interface.Interface):
+ """Marker interface for internal use."""
+
+class PrincipalColumn(column.SortingColumn):
+ # we don't want a sorting header, just the convenience of sorting,
+ # so remove the declaration of sortable headers (XXX is this really
+ # what we want)
+ interface.implementsOnly(zc.table.interfaces.IColumn)
+
+ def renderCell(self, item, formatter):
+ principal_id, setting = item
+ principals = zapi.principals()
+ principal = principals.getPrincipal(principal_id)
+ if IGroup.providedBy(principal):
+ icon = 'group_icon.gif'
+ else:
+ icon = 'user_icon.gif'
+ resource = component.getAdapter(formatter.request, Interface,
+ icon)
+ return '<img src="%s"> %s' % (resource(), principal.title)
+
+ def getSortKey(self, item, formatter):
+ principal_id, setting = item
+ principals = zapi.principals()
+ principal = principals.getPrincipal(principal_id)
+ return principal.title.lower()
+
+
+def _getgetbit(sharing, bit):
+ v = 2**bit
+ def getbit(data):
+ return bool(sharing.getBinaryPrivileges(data[0]) & v)
+ return getbit
+
+def _getsetbit(sharing, bit):
+ mask = 1 << bit
+ def setbit(data, v):
+ v = bool(v) << bit
+ current = sharing.getBinaryPrivileges(data[0])
+ result = ((current | mask) ^ mask) | v
+ sharing.setBinaryPrivileges(data[0], result)
+ return setbit
+
+def _privilegeColumn(priv, sharing):
+ col = column.FieldEditColumn(
+ priv['title'], "sharing",
+ schema.Bool(__name__=str(priv['id'])),
+ lambda data: data[0],
+ getter = _getgetbit(sharing, priv['id']),
+ setter = _getsetbit(sharing, priv['id']),
+ )
+ interface.alsoProvides(col, IPrivilegeColumn)
+ return col
+
+
+
+class SharingTab:
+ # This view has some weird implementation details due to the
+ # dynamic computation of the items passed to the table formatter.
+ #
+ # The table formatter is created twice; the first time to read
+ # input from the table using the initial set of items, and the
+ # second time with the final set of items; only the latter is used
+ # for rendering.
+ #
+ # Future changes to the table formatter API may make it possible
+ # for this view to be less weird.
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+ self.sharing = sharing = interfaces.IBaseSharing(self.context)
+
+ privids = interfaces.ISharingPrivileges(self.context).privileges
+ privs = [getPrivilege(id) for id in privids]
+ self.nPrivileges = len(privs)
+
+ columns = [_privilegeColumn(priv, sharing) for priv in privs]
+
+ sprivids = interfaces.ISubobjectSharingPrivileges(self.context, None)
+ if sprivids is not None:
+ sprivids = [id for id in sprivids.subobjectPrivileges
+ if id not in privids]
+ if sprivids:
+ sprivs = [getPrivilege(id) for id in sprivids]
+ columns.extend([_privilegeColumn(priv, sharing)
+ for priv in sprivs])
+ self.nSubobjectPrivileges = len(sprivids)
+ else:
+ self.nSubobjectPrivileges = 0
+
+ self.columns = [
+ column.SubmitColumn(
+ "",
+ prefix="remove",
+ idgetter=lambda data: str(data[0]),
+ action=lambda d: self.sharing.setBinaryPrivileges(
+ d[0], 0),
+ labelgetter=lambda data, formatter: _('Remove'),
+ condition=lambda d: d[0] in self.sharing.getPrincipals()
+ ),
+ PrincipalColumn(_("Name"))] + columns
+
+ self.factory = component.getUtility(
+ zc.table.interfaces.IFormatterFactory)
+
+ self.processInput()
+
+ self.formatter = self.factory(
+ context, request, self.settings, columns=self.columns,
+ sort_on=[('Name', False)])
+
+ def processInput(self):
+ request = self.request
+ sharing = self.sharing
+
+ # XXX completely untested, afaik; we should also set up an i18n domain
+ # and provide a facility on the view for 'translating' the names for
+ # labels.
+ macro_name = request.form.get('apply_sharing_macro')
+ if macro_name:
+ macro = component.getAdapter(
+ self.context, interfaces.ISharingMacro,
+ macro_name)
+ macro.share(sharing)
+
+ updated = False
+ form = request.form
+ settings = []
+ effective_principal_text = form.get('effective_principal_text', '')
+ effective_principal_type = form.get('effective_principal_type', '')
+ principal_text = form.get('principal_text', '')
+ principal_type = form.get('principal_type', '')
+ if 'principal_search' in form:
+ effective_principal_text = principal_text
+ effective_principal_type = principal_type
+ self.effective_principal_text = effective_principal_text
+ self.effective_principal_type = effective_principal_type
+ self.principal_text = principal_text
+ self.principal_type = principal_type
+
+ settingsFactory = lambda: []
+
+ if effective_principal_type=='group':
+ searcher = ISimpleGroupSearch(zapi.principals(), None)
+ # TODO the absence of a searcher should be logged as an error
+ if searcher is not None:
+ res = [[pid, 0]
+ for pid in searcher.searchGroups(
+ effective_principal_text, 0, 999999999)]
+ settingsFactory = lambda: res
+ elif effective_principal_type=='user' and effective_principal_text:
+ searcher = ISimpleUserSearch(zapi.principals(), None)
+ # TODO the absence of a searcher should be logged as an error
+ if searcher is not None:
+ res = [[pid, 0]
+ for pid in searcher.searchUsers(
+ effective_principal_text, 0, 999999999)]
+ settingsFactory = lambda: res
+ else:
+ if effective_principal_type=='user':
+ self.message = _('You must supply search text to find a user')
+ def settingsFactory():
+ return [[pid, sharing.getBinaryPrivileges(pid)]
+ for pid in sharing.getPrincipals()]
+
+ settings = settingsFactory()
+ input = self.columns[0].input(settings, request)
+ if input:
+ self.columns[0].update(settings, input) # inefficient :-(
+ settings = settingsFactory()
+
+ if 'setSharingInfo' in request or 'setRecursiveSharingInfo' in request:
+ # apply button
+ for column in self.columns:
+ if not IPrivilegeColumn.providedBy(column):
+ continue
+ input = column.input(settings, request)
+ if input:
+ updated = column.update(settings, input) or updated
+
+ # Reset settings, since we always show existing settings
+ # after an update
+ settings = [[pid, sharing.getBinaryPrivileges(pid)]
+ for pid in sharing.getPrincipals()]
+ self.effective_principal_text = ''
+ self.effective_principal_type = ''
+
+ if 'setRecursiveSharingInfo' in request:
+ applyToSubobjects(settings, self.context, {})
+ updated = True # we'll guess :-/
+
+ self.settings = settings
+ self.updated = updated
+
+ def subobjects(self):
+ subs = ISublocations(self.context, None)
+ if subs is None:
+ return False
+ subs = iter(subs.sublocations())
+ try:
+ subs.next()
+ except StopIteration:
+ return False
+ return True
+
+ def macros(self):
+ macros = [
+ (macro.order, name, macro)
+ for (name, macro)
+ in component.getAdapters((self.context,), interfaces.ISharingMacro)
+ ]
+ macros.sort()
+ return [name for (order, name, macro) in macros]
+
+ @property
+ def message(self):
+ if self.updated:
+ formatter = self.request.locale.dates.getFormatter('dateTime',
+ 'medium')
+ status = _("Updated on ${date_time}",
+ mapping={'date_time': formatter.format(
+ datetime.datetime.now(ITZInfo(self.request, None)))})
+
+ return status
+ else:
+ return ''
+
+ def newTableFormatter(self, settings, columns=None):
+ if columns is None:
+ columns = self.columns
+ return self.factory(
+ self.context, self.request, settings, columns=columns,
+ sort_on=[('Name', False)])
+
+def applyToSubobjects(settings, ob, seen):
+ obid = id(ob)
+ if obid in seen:
+ return
+ seen[obid] = ob
+
+ sharing = interfaces.IBaseSharing(ob, None)
+ if sharing is not None:
+ mask = sharingMask(ob)
+ for principal in sharing.getPrincipals():
+ sharing.setBinaryPrivileges(principal, 0)
+ for principal, setting in settings:
+ sharing.setBinaryPrivileges(principal, setting & mask)
+
+ subs = ISublocations(ob, None)
+ if subs is None:
+ return
+
+ for sub in subs.sublocations():
+ applyToSubobjects(settings, sub, seen)
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/sharing.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/sharing.txt
===================================================================
--- zc.sharing/trunk/src/zc/sharing/browser/sharing.txt 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/browser/sharing.txt 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,1239 @@
+Sharing Tab Support
+===================
+
+The sharing tab class provides a form for displaying and manipulating
+sharing settings. To demonstrate this, we'll set up a fake
+authentication utility:
+
+ >>> class Principal:
+ ... def __init__(self, id, title):
+ ... self.id, self.title = id, title
+
+ >>> import zope.security.interfaces
+ >>> from zope import interface
+ >>> class Group(Principal):
+ ... interface.implements(zope.security.interfaces.IGroup)
+
+ >>> import zope.app.security.interfaces
+ >>> from zc.sharing import interfaces
+ >>> from zc.security.interfaces import ISimpleUserSearch
+ >>> from zc.security.interfaces import ISimpleGroupSearch
+ >>> class Principals:
+ ... interface.implements(zope.app.security.interfaces.IAuthentication,
+ ... ISimpleGroupSearch, ISimpleUserSearch,
+ ... )
+ ... def __init__(self):
+ ... self.users = {
+ ... 'p1': Principal("Grace", "Grace Slick"),
+ ... 'p2': Principal("Alice", "Alice Cooper"),
+ ... 'p3': Principal('baba', 'Baba'),
+ ... 'p4': Principal('baback', 'Baback'),
+ ... 'p5': Principal('barney', 'Barney'),
+ ... 'p6': Principal('bash', 'Bash'),
+ ... 'p7': Principal('bat', 'Bat'),
+ ... 'p8': Principal('bathsheba', 'Bathsheba'),
+ ... 'p9': Principal('basil', 'Basil'),
+ ... }
+ ... self.groups = {
+ ... 'rockers': Group("Rockers", "Rock Performers"),
+ ... 'g2': Group('zane', 'zane'),
+ ... 'g3': Group('zulu', 'zulu'),
+ ... 'g4': Group('zandra', 'zandra'),
+ ... 'g5': Group('zorina', 'zorina'),
+ ... 'g6': Group('zubaida', 'zubaida'),
+ ... 'g7': Group('zan', 'zan'),
+ ... }
+ ...
+ ... def getPrincipal(self, pid):
+ ... return self.users.get(pid, self.groups.get(pid))
+ ...
+ ... def searchUsers(self, filter, start, size):
+ ... return [i for (i, p) in self.users.items()
+ ... if filter in p.title][start:start+size]
+ ...
+ ... def searchGroups(self, filter, start, size):
+ ... return [i for (i, p) in self.groups.items()
+ ... if filter in p.title][start:start+size]
+
+ >>> from zope import component
+ >>> component.provideUtility(Principals(),
+ ... zope.app.security.interfaces.IAuthentication)
+
+We'll also create a sample content object that can be adapted to ISharing:
+
+ >>> from zc.sharing import interfaces
+ >>> class SharingSample:
+ ... """Sample content class
+ ...
+ ... Normally, we adapt content objects to ISharing. To keep this
+ ... example simple, we'll implement ISharing directly.
+ ... """
+ ... interface.implements(interfaces.IBaseSharing)
+ ...
+ ... def __init__(self, **data):
+ ... self.data = data
+ ...
+ ... def getPrincipals(self):
+ ... return self.data.keys()
+ ... def getBinaryPrivileges(self, principal_id):
+ ... return self.data.get(principal_id, 0)
+ ... def setBinaryPrivileges(self, principal_id, privileges):
+ ... if privileges:
+ ... self.data[principal_id] = privileges
+ ... else:
+ ... del self.data[principal_id]
+
+ >>> sharing = SharingSample(p1=7, rockers=4)
+
+We need to define some privileges:
+
+ >>> import zc.sharing.sharing
+ >>> zc.sharing.sharing.definePrivilege(0, "Share")
+ >>> zc.sharing.sharing.definePrivilege(1, "Work")
+ >>> zc.sharing.sharing.definePrivilege(2, "Play")
+
+And we need to define the privileges used by content
+
+ >>> from zc.sharing import policy
+ >>> policy.sharingPrivileges(SharingSample, ["Play", "Work", "Share"])
+
+Now we can create a sharing tab:
+
+ >>> from zc.sharing.browser.sharing import SharingTab
+ >>> from zope.publisher.browser import TestRequest
+ >>> request = TestRequest()
+ >>> tabs = SharingTab(sharing, request)
+
+The component generates components of the settings form, most notably,
+the table headers:
+
+ >>> def output_row(row):
+ ... for cell in row:
+ ... print ' cell:'
+ ... print ' '+cell
+
+ >>> output_row(tabs.formatter.getHeaders())
+ cell:
+ <BLANKLINE>
+ cell:
+ Name
+ cell:
+ Play
+ cell:
+ Work
+ cell:
+ Share
+
+and the rows:
+
+ >>> def output(rows):
+ ... for row in rows:
+ ... print ' row:'
+ ... output_row(row)
+
+ >>> output(tabs.formatter.getRows())
+ row:
+ cell:
+ <input type='submit' name="remove.cDE=" value="Remove" />
+ cell:
+ <img src="http://mysite/user_icon.gif"> Grace Slick
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.2.used"
+ name="sharing.cDE=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.2"
+ name="sharing.cDE=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.1.used"
+ name="sharing.cDE=.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.1"
+ name="sharing.cDE=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.0.used"
+ name="sharing.cDE=.0.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.0"
+ name="sharing.cDE=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.cm9ja2Vycw==" value="Remove" />
+ cell:
+ <img src="http://mysite/group_icon.gif"> Rock Performers
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.2.used"
+ name="sharing.cm9ja2Vycw==.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.2"
+ name="sharing.cm9ja2Vycw==.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.1.used"
+ name="sharing.cm9ja2Vycw==.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cm9ja2Vycw==.1"
+ name="sharing.cm9ja2Vycw==.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.0.used"
+ name="sharing.cm9ja2Vycw==.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cm9ja2Vycw==.0"
+ name="sharing.cm9ja2Vycw==.0" type="checkbox" value="on" />
+
+We can update the settings. The request has to have data for the
+settings and the 'setSharingInfo' key needs to be in the request. Let's
+add the write privilege to the rockers group. First, we'll include the
+data in the request:
+
+ >>> request.form["sharing.cm9ja2Vycw==.1.used"] = ""
+ >>> request.form["sharing.cm9ja2Vycw==.1"] = "on"
+ >>> request.form["sharing.cm9ja2Vycw==.0.used"] = ""
+ >>> request.form["sharing.cm9ja2Vycw==.0"] = "on"
+ >>> tabs = SharingTab(sharing, request)
+
+With this, the form is updated:
+
+ >>> output(tabs.formatter.getRows())
+ ... # doctest: +ELLIPSIS
+ row:
+ ...
+ cell:
+ <img src="http://mysite/group_icon.gif"> Rock Performers
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.2.used"
+ name="sharing.cm9ja2Vycw==.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.2"
+ name="sharing.cm9ja2Vycw==.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.1.used"
+ name="sharing.cm9ja2Vycw==.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.1"
+ name="sharing.cm9ja2Vycw==.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.0.used"
+ name="sharing.cm9ja2Vycw==.0.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.0"
+ name="sharing.cm9ja2Vycw==.0" type="checkbox" value="on" />
+
+But the data are unaffected:
+
+ >>> sharing.getBinaryPrivileges('rockers')
+ 4
+
+Now, if we include the 'setSharingInfo' key, we'll get the same
+output, but we'll also have the data updated:
+
+ >>> request.form['setSharingInfo'] = ""
+ >>> tabs = SharingTab(sharing, request)
+
+ >>> output(tabs.formatter.getRows())
+ ... # doctest: +ELLIPSIS
+ row:
+ ...
+ cell:
+ <img src="http://mysite/group_icon.gif"> Rock Performers
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.2.used"
+ name="sharing.cm9ja2Vycw==.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.2"
+ name="sharing.cm9ja2Vycw==.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.1.used"
+ name="sharing.cm9ja2Vycw==.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.1"
+ name="sharing.cm9ja2Vycw==.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.0.used"
+ name="sharing.cm9ja2Vycw==.0.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.0"
+ name="sharing.cm9ja2Vycw==.0" type="checkbox" value="on" />
+ <BLANKLINE>
+
+
+ >>> sharing.getBinaryPrivileges('rockers')
+ 7
+
+
+Sharing to groups
+-----------------
+
+Normally, we show the principals for which we have settings. We cant
+change settings for a principal if we don't already have settings for
+them. We can add settings for new principals by searching for them. For
+groups, you need to select 'group' from the search dropdown, and optionally
+include a search string, and then click 'Search'. This means in the request
+that 'principal_type' must be 'group' and the 'principal_search' search button
+is in the request:
+
+ >>> request = TestRequest()
+ >>> request.form['principal_type'] = 'group'
+ >>> request.form['principal_search'] = ''
+ >>> tabs = SharingTab(sharing, request)
+
+The form stills allow you to remove principals if they have settings, so the
+headings remain the same:
+
+ >>> output_row(tabs.formatter.getHeaders())
+ cell:
+ <BLANKLINE>
+ cell:
+ Name
+ cell:
+ Play
+ cell:
+ Work
+ cell:
+ Share
+
+And the rows still have a selection column. Note that now only groups are
+listed--both 'Rock Performers', which currently has settings, and the other
+groups.
+
+ >>> output(tabs.formatter.getRows())
+ row:
+ cell:
+ <input type='submit' name="remove.cm9ja2Vycw==" value="Remove" />
+ cell:
+ <img src="http://mysite/group_icon.gif"> Rock Performers
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.2.used"
+ name="sharing.cm9ja2Vycw==.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.2"
+ name="sharing.cm9ja2Vycw==.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.1.used"
+ name="sharing.cm9ja2Vycw==.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.1"
+ name="sharing.cm9ja2Vycw==.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.0.used"
+ name="sharing.cm9ja2Vycw==.0.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.0"
+ name="sharing.cm9ja2Vycw==.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <BLANKLINE>
+ cell:
+ <img src="http://mysite/group_icon.gif"> zan
+ cell:
+ <input class="hiddenType" id="sharing.Zzc=.2.used"
+ name="sharing.Zzc=.2.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.Zzc=.2"
+ name="sharing.Zzc=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.Zzc=.1.used"
+ name="sharing.Zzc=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.Zzc=.1"
+ name="sharing.Zzc=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.Zzc=.0.used"
+ name="sharing.Zzc=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.Zzc=.0"
+ name="sharing.Zzc=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <BLANKLINE>
+ cell:
+ <img src="http://mysite/group_icon.gif"> zandra
+ cell:
+ <input class="hiddenType" id="sharing.ZzQ=.2.used"
+ name="sharing.ZzQ=.2.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzQ=.2"
+ name="sharing.ZzQ=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzQ=.1.used"
+ name="sharing.ZzQ=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzQ=.1"
+ name="sharing.ZzQ=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzQ=.0.used"
+ name="sharing.ZzQ=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzQ=.0"
+ name="sharing.ZzQ=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <BLANKLINE>
+ cell:
+ <img src="http://mysite/group_icon.gif"> zane
+ cell:
+ <input class="hiddenType" id="sharing.ZzI=.2.used"
+ name="sharing.ZzI=.2.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzI=.2"
+ name="sharing.ZzI=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzI=.1.used"
+ name="sharing.ZzI=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzI=.1"
+ name="sharing.ZzI=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzI=.0.used"
+ name="sharing.ZzI=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzI=.0"
+ name="sharing.ZzI=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <BLANKLINE>
+ cell:
+ <img src="http://mysite/group_icon.gif"> zorina
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.2.used"
+ name="sharing.ZzU=.2.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.2"
+ name="sharing.ZzU=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.1.used"
+ name="sharing.ZzU=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.1"
+ name="sharing.ZzU=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.0.used"
+ name="sharing.ZzU=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.0"
+ name="sharing.ZzU=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <BLANKLINE>
+ cell:
+ <img src="http://mysite/group_icon.gif"> zubaida
+ cell:
+ <input class="hiddenType" id="sharing.ZzY=.2.used"
+ name="sharing.ZzY=.2.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzY=.2"
+ name="sharing.ZzY=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzY=.1.used"
+ name="sharing.ZzY=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzY=.1"
+ name="sharing.ZzY=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzY=.0.used"
+ name="sharing.ZzY=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzY=.0"
+ name="sharing.ZzY=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <BLANKLINE>
+ cell:
+ <img src="http://mysite/group_icon.gif"> zulu
+ cell:
+ <input class="hiddenType" id="sharing.ZzM=.2.used"
+ name="sharing.ZzM=.2.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzM=.2"
+ name="sharing.ZzM=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzM=.1.used"
+ name="sharing.ZzM=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzM=.1"
+ name="sharing.ZzM=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzM=.0.used"
+ name="sharing.ZzM=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzM=.0"
+ name="sharing.ZzM=.0" type="checkbox" value="on" />
+ <BLANKLINE>
+
+We can filter the groups presented by actually providing search text:
+
+ >>> request.form['principal_text'] = 'zor'
+ >>> tabs = SharingTab(sharing, request)
+ >>> output(tabs.formatter.getRows())
+ row:
+ cell:
+ <BLANKLINE>
+ cell:
+ <img src="http://mysite/group_icon.gif"> zorina
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.2.used"
+ name="sharing.ZzU=.2.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.2"
+ name="sharing.ZzU=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.1.used"
+ name="sharing.ZzU=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.1"
+ name="sharing.ZzU=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.0.used"
+ name="sharing.ZzU=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.0"
+ name="sharing.ZzU=.0" type="checkbox" value="on" />
+
+The view provides two attributes, `effective_principal_text` and
+`effective_principal_type`, that should be used to persist the search text and
+type in the request. `principal_text` and `principal_type` are used to persist
+the values in the fields themselves, and don't affect the effective values
+unless the `principal_search` submit button is in the request.
+
+ >>> tabs.effective_principal_type
+ 'group'
+ >>> tabs.effective_principal_text
+ 'zor'
+ >>> tabs.principal_type
+ 'group'
+ >>> tabs.principal_text
+ 'zor'
+
+We can supply data for a group. Note again that `principal_text` and
+`principal_type` are ignored unless `principal_search` is in the request, which
+allows the form to keep state with what the user changes in the search fields
+while not getting the data confused with the effective search:
+
+ >>> request = TestRequest()
+ >>> request.form['principal_type'] = 'user'
+ >>> request.form['principal_text'] = 'foo'
+ >>> request.form['effective_principal_type'] = 'group'
+ >>> request.form['effective_principal_text'] = 'zor'
+ >>> request.form["sharing.ZzU=.2.used"] = ""
+ >>> request.form["sharing.ZzU=.2"] = "on"
+ >>> request.form['setSharingInfo'] = ""
+ >>> tabs = SharingTab(sharing, request)
+ >>> output(tabs.formatter.getRows())
+ row:
+ cell:
+ <input type='submit' name="remove.cDE=" value="Remove" />
+ cell:
+ <img src="http://mysite/user_icon.gif"> Grace Slick
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.2.used"
+ name="sharing.cDE=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.2"
+ name="sharing.cDE=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.1.used"
+ name="sharing.cDE=.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.1"
+ name="sharing.cDE=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.0.used"
+ name="sharing.cDE=.0.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.0"
+ name="sharing.cDE=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.cm9ja2Vycw==" value="Remove" />
+ cell:
+ <img src="http://mysite/group_icon.gif"> Rock Performers
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.2.used"
+ name="sharing.cm9ja2Vycw==.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.2"
+ name="sharing.cm9ja2Vycw==.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.1.used"
+ name="sharing.cm9ja2Vycw==.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.1"
+ name="sharing.cm9ja2Vycw==.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.0.used"
+ name="sharing.cm9ja2Vycw==.0.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.0"
+ name="sharing.cm9ja2Vycw==.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.ZzU=" value="Remove" />
+ cell:
+ <img src="http://mysite/group_icon.gif"> zorina
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.2.used"
+ name="sharing.ZzU=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.ZzU=.2"
+ name="sharing.ZzU=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.1.used"
+ name="sharing.ZzU=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.1"
+ name="sharing.ZzU=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.0.used"
+ name="sharing.ZzU=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.0"
+ name="sharing.ZzU=.0" type="checkbox" value="on" />
+
+The settings, rather than groups, are displayed again, so the
+effective search is cleared (but the field values remain):
+
+ >>> tabs.effective_principal_text
+ ''
+ >>> tabs.effective_principal_type
+ ''
+ >>> tabs.principal_text
+ 'foo'
+ >>> tabs.principal_type
+ 'user'
+
+
+Sharing to users
+----------------
+
+Just as we can share to groups, we can also share to users. The only
+difference in behavior from the group story is that search text must be
+provided.
+
+ >>> request = TestRequest()
+ >>> request.form['principal_type'] = 'user'
+ >>> request.form['principal_text'] = 'c'
+ >>> request.form['principal_search'] = ''
+ >>> tabs = SharingTab(sharing, request)
+
+We only got results with a "c" in the user name:
+
+ >>> output(tabs.formatter.getRows())
+ row:
+ cell:
+ <BLANKLINE>
+ cell:
+ <img src="http://mysite/user_icon.gif"> Alice Cooper
+ cell:
+ <input class="hiddenType" id="sharing.cDI=.2.used"
+ name="sharing.cDI=.2.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDI=.2"
+ name="sharing.cDI=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDI=.1.used"
+ name="sharing.cDI=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDI=.1"
+ name="sharing.cDI=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDI=.0.used"
+ name="sharing.cDI=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDI=.0"
+ name="sharing.cDI=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <BLANKLINE>
+ cell:
+ <img src="http://mysite/user_icon.gif"> Baback
+ cell:
+ <input class="hiddenType" id="sharing.cDQ=.2.used"
+ name="sharing.cDQ=.2.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDQ=.2"
+ name="sharing.cDQ=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDQ=.1.used"
+ name="sharing.cDQ=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDQ=.1"
+ name="sharing.cDQ=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDQ=.0.used"
+ name="sharing.cDQ=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDQ=.0"
+ name="sharing.cDQ=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.cDE=" value="Remove" />
+ cell:
+ <img src="http://mysite/user_icon.gif"> Grace Slick
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.2.used"
+ name="sharing.cDE=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.2"
+ name="sharing.cDE=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.1.used"
+ name="sharing.cDE=.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.1"
+ name="sharing.cDE=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.0.used"
+ name="sharing.cDE=.0.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.0"
+ name="sharing.cDE=.0" type="checkbox" value="on" />
+
+The view provides two attributes, `effective_principal_text` and
+`effective_principal_type`, that should be used to persist the search text and
+type in the request. `principal_text` and `principal_type` are used to persist
+the values in the fields themselves, and don't affect the effective values
+unless the `principal_search` submit button is in the request.
+
+ >>> tabs.effective_principal_type
+ 'user'
+ >>> tabs.effective_principal_text
+ 'c'
+ >>> tabs.principal_type
+ 'user'
+ >>> tabs.principal_text
+ 'c'
+
+We can supply data for a user (Alice Cooper, while leaving Grace Slick's
+permissions in place):
+
+ >>> request = TestRequest()
+ >>> request.form['effective_principal_type'] = 'user'
+ >>> request.form['effective_principal_text'] = 'c'
+ >>> request.form['principal_type'] = 'user'
+ >>> request.form['principal_text'] = 'c'
+ >>> request.form['sharing.cDI=.2.used'] = ""
+ >>> request.form['sharing.cDI=.2'] = "on"
+ >>> request.form['sharing.cDE=.1.used'] = ""
+ >>> request.form['sharing.cDE=.1'] = "on"
+ >>> request.form['sharing.cDE=.2.used'] = ""
+ >>> request.form['sharing.cDE=.2'] = "on"
+ >>> request.form['sharing.cDE=.3.used'] = ""
+ >>> request.form['sharing.cDE=.3'] = "on"
+ >>> request.form['setSharingInfo'] = ""
+ >>> tabs = SharingTab(sharing, request)
+ >>> output(tabs.formatter.getRows())
+ row:
+ cell:
+ <input type='submit' name="remove.cDI=" value="Remove" />
+ cell:
+ <img src="http://mysite/user_icon.gif"> Alice Cooper
+ cell:
+ <input class="hiddenType" id="sharing.cDI=.2.used"
+ name="sharing.cDI=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDI=.2"
+ name="sharing.cDI=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDI=.1.used"
+ name="sharing.cDI=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDI=.1"
+ name="sharing.cDI=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDI=.0.used"
+ name="sharing.cDI=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDI=.0"
+ name="sharing.cDI=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.cDE=" value="Remove" />
+ cell:
+ <img src="http://mysite/user_icon.gif"> Grace Slick
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.2.used"
+ name="sharing.cDE=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.2"
+ name="sharing.cDE=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.1.used"
+ name="sharing.cDE=.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.1"
+ name="sharing.cDE=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.0.used"
+ name="sharing.cDE=.0.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.0"
+ name="sharing.cDE=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.cm9ja2Vycw==" value="Remove" />
+ cell:
+ <img src="http://mysite/group_icon.gif"> Rock Performers
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.2.used"
+ name="sharing.cm9ja2Vycw==.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.2"
+ name="sharing.cm9ja2Vycw==.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.1.used"
+ name="sharing.cm9ja2Vycw==.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.1"
+ name="sharing.cm9ja2Vycw==.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.0.used"
+ name="sharing.cm9ja2Vycw==.0.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.0"
+ name="sharing.cm9ja2Vycw==.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.ZzU=" value="Remove" />
+ cell:
+ <img src="http://mysite/group_icon.gif"> zorina
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.2.used"
+ name="sharing.ZzU=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.ZzU=.2"
+ name="sharing.ZzU=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.1.used"
+ name="sharing.ZzU=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.1"
+ name="sharing.ZzU=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.0.used"
+ name="sharing.ZzU=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.0"
+ name="sharing.ZzU=.0" type="checkbox" value="on" />
+
+The settings, rather than groups, are displayed again, so the
+effective search is cleared (but the field values remain):
+
+ >>> tabs.effective_principal_text
+ ''
+ >>> tabs.effective_principal_type
+ ''
+ >>> tabs.principal_text
+ 'c'
+ >>> tabs.principal_type
+ 'user'
+
+Removing settings for principals
+--------------------------------
+
+We can remove all settings for a given principal by clicking the `Remove`
+button next to the principal. Here we'll remove the settings for the rockers
+group:
+
+ >>> request = TestRequest()
+ >>> request.form["remove.cm9ja2Vycw=="] = 'Remove'
+ >>> tabs = SharingTab(sharing, request)
+ >>> output(tabs.formatter.getRows())
+ row:
+ cell:
+ <input type='submit' name="remove.cDI=" value="Remove" />
+ cell:
+ <img src="http://mysite/user_icon.gif"> Alice Cooper
+ cell:
+ <input class="hiddenType" id="sharing.cDI=.2.used"
+ name="sharing.cDI=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDI=.2"
+ name="sharing.cDI=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDI=.1.used"
+ name="sharing.cDI=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDI=.1"
+ name="sharing.cDI=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDI=.0.used"
+ name="sharing.cDI=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDI=.0"
+ name="sharing.cDI=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.cDE=" value="Remove" />
+ cell:
+ <img src="http://mysite/user_icon.gif"> Grace Slick
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.2.used"
+ name="sharing.cDE=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.2"
+ name="sharing.cDE=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.1.used"
+ name="sharing.cDE=.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.1"
+ name="sharing.cDE=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.0.used"
+ name="sharing.cDE=.0.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.0"
+ name="sharing.cDE=.0" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.ZzU=" value="Remove" />
+ cell:
+ <img src="http://mysite/group_icon.gif"> zorina
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.2.used"
+ name="sharing.ZzU=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.ZzU=.2"
+ name="sharing.ZzU=.2" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.1.used"
+ name="sharing.ZzU=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.1"
+ name="sharing.ZzU=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.0.used"
+ name="sharing.ZzU=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.0"
+ name="sharing.ZzU=.0" type="checkbox" value="on" />
+
+Subobjects
+----------
+
+If an object has subobjects (sublocations). then an option is provided
+to share with subobjects. Our existing sample object doesn't have
+subobjects, which we can determine by calling the subobjects method on
+the view:
+
+ >>> tabs.subobjects()
+ False
+
+Let's create a new sample object that supports sublocations.
+Normally, objects are adapted to ISublocations, however, to keep the
+example simple, we'll just extend our sample class to support
+sublocations:
+
+ >>> from zope.app.location.interfaces import ISublocations
+ >>> class SharingSampleWithSublocations(SharingSample):
+ ... interface.implements(ISublocations)
+ ... subs = ()
+ ... def sublocations(self):
+ ... return self.subs
+
+
+For our new objects, we'll define some different privileges:
+
+ >>> zc.sharing.sharing.definePrivilege(3, "List")
+ >>> zc.sharing.sharing.definePrivilege(4, "Modify")
+ >>> policy.sharingPrivileges(SharingSampleWithSublocations,
+ ... ["List", "Modify", "Share"])
+
+Because these are containers, we also need to specify the privileges
+that subobjects can have:
+
+ >>> policy.subobjectSharingPrivileges(SharingSampleWithSublocations,
+ ... ["Work", "Play", "Share"])
+
+This is necessary so that we can use the sharing tab to specify
+settings that we will share with subobjects, as well as the settings
+that we apply to the container.
+
+Now, we can create a container:
+
+ >>> container = SharingSampleWithSublocations(p1=31)
+ >>> request = TestRequest()
+ >>> tabs = SharingTab(container, request)
+ >>> tabs.subobjects()
+ False
+
+ >>> container2 = SharingSampleWithSublocations(p2=7)
+ >>> container3 = SharingSampleWithSublocations(p3=7)
+ >>> container.subs = container2, container3
+ >>> container3.subs = (sharing, )
+
+ >>> tabs.subobjects()
+ True
+
+If we select the 'setRecursiveSharingInfo' button, then, settings are
+applied to the current object and the ones below it. Note that this example
+cheats: we should really duplicate the information from the form, and are
+using our knowledge of the implementation to bypass the requirement.
+Converting this to testbrowser will be a better solution in the future.
+
+ >>> request.form['setRecursiveSharingInfo'] = ''
+ >>> tabs = SharingTab(container, request)
+ >>> output(tabs.formatter.getRows())
+ row:
+ cell:
+ <input type='submit' name="remove.cDE=" value="Remove" />
+ cell:
+ <img src="http://mysite/user_icon.gif"> Grace Slick
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.3.used"
+ name="sharing.cDE=.3.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.3"
+ name="sharing.cDE=.3" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.4.used"
+ name="sharing.cDE=.4.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.4"
+ name="sharing.cDE=.4" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.0.used"
+ name="sharing.cDE=.0.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.0"
+ name="sharing.cDE=.0" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.1.used"
+ name="sharing.cDE=.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.1"
+ name="sharing.cDE=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.2.used"
+ name="sharing.cDE=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.2"
+ name="sharing.cDE=.2" type="checkbox" value="on" />
+
+ Now, if we look at the subobjects, we'll see that their settings were
+ changed to match the container:
+
+ >>> [(p, container2.getBinaryPrivileges(p)) for p in container2.getPrincipals()]
+ [('p1', 31)]
+
+ >>> [(p, container3.getBinaryPrivileges(p)) for p in container3.getPrincipals()]
+ [('p1', 31)]
+
+ >>> [(p, sharing.getBinaryPrivileges(p)) for p in sharing.getPrincipals()]
+ [('p1', 7)]
+
+Note that the simple (non-container) sharing object only has bits set
+for the privileges defined for it.
+
+If we make changes when supplying the recursive option, then the
+settings are duplicated after applying the changes. Note that this example
+cheats: we should really duplicate the information from the form, and are
+using our knowledge of the implementation to bypass the requirement.
+Converting this to testbrowser will be a better solution in the future.
+
+ >>> request = TestRequest()
+ >>> request.form['setRecursiveSharingInfo'] = ''
+ >>> request.form["sharing.cDE=.0.used"] = ''
+ >>> tabs = SharingTab(container, request)
+ >>> output(tabs.formatter.getRows())
+ row:
+ cell:
+ <input type='submit' name="remove.cDE=" value="Remove" />
+ cell:
+ <img src="http://mysite/user_icon.gif"> Grace Slick
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.3.used"
+ name="sharing.cDE=.3.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.3"
+ name="sharing.cDE=.3" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.4.used"
+ name="sharing.cDE=.4.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.4"
+ name="sharing.cDE=.4" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.0.used"
+ name="sharing.cDE=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDE=.0"
+ name="sharing.cDE=.0" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.1.used"
+ name="sharing.cDE=.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.1"
+ name="sharing.cDE=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.2.used"
+ name="sharing.cDE=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.2"
+ name="sharing.cDE=.2" type="checkbox" value="on" />
+
+ >>> [(p, container2.getBinaryPrivileges(p)) for p in container2.getPrincipals()]
+ [('p1', 30)]
+
+ >>> [(p, container3.getBinaryPrivileges(p)) for p in container3.getPrincipals()]
+ [('p1', 30)]
+
+ >>> [(p, sharing.getBinaryPrivileges(p)) for p in sharing.getPrincipals()]
+ [('p1', 6)]
+
+ >>> request = TestRequest()
+ >>> request.form['setRecursiveSharingInfo'] = ''
+ >>> request.form['effective_principal_type'] = 'group'
+ >>> request.form["sharing.ZzU=.2.used"] = ""
+ >>> request.form["sharing.ZzU=.2"] = "on"
+ >>> tabs = SharingTab(container, request)
+ >>> output(tabs.formatter.getRows())
+ row:
+ cell:
+ <input type='submit' name="remove.cDE=" value="Remove" />
+ cell:
+ <img src="http://mysite/user_icon.gif"> Grace Slick
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.3.used"
+ name="sharing.cDE=.3.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.3"
+ name="sharing.cDE=.3" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.4.used"
+ name="sharing.cDE=.4.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.4"
+ name="sharing.cDE=.4" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.0.used"
+ name="sharing.cDE=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDE=.0"
+ name="sharing.cDE=.0" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.1.used"
+ name="sharing.cDE=.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.1"
+ name="sharing.cDE=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.2.used"
+ name="sharing.cDE=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.2"
+ name="sharing.cDE=.2" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.ZzU=" value="Remove" />
+ cell:
+ <img src="http://mysite/group_icon.gif"> zorina
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.3.used"
+ name="sharing.ZzU=.3.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.3"
+ name="sharing.ZzU=.3" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.4.used"
+ name="sharing.ZzU=.4.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.4"
+ name="sharing.ZzU=.4" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.0.used"
+ name="sharing.ZzU=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.0"
+ name="sharing.ZzU=.0" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.1.used"
+ name="sharing.ZzU=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.1"
+ name="sharing.ZzU=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.2.used"
+ name="sharing.ZzU=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.ZzU=.2"
+ name="sharing.ZzU=.2" type="checkbox" value="on" />
+
+
+ >>> def sorted(l):
+ ... tmp = list(l)
+ ... tmp.sort()
+ ... return tmp
+
+ >>> for ob in container2, container3, sharing:
+ ... print sorted([(p, container2.getBinaryPrivileges(p))
+ ... for p in container2.getPrincipals()])
+ [('g5', 4), ('p1', 30)]
+ [('g5', 4), ('p1', 30)]
+ [('g5', 4), ('p1', 30)]
+
+Typically, columns for privileges applicable to the container are
+displayed differently than the columns applicable to just subobjects.
+To aid with the display of the columns, the view has variables giving
+the number of columns of column privileges:
+
+ >>> tabs.nPrivileges
+ 3
+
+and the number of columns of privileges for subobjects:
+
+ >>> tabs.nSubobjectPrivileges
+ 2
+
+Sharing "macros"
+================
+
+Sharing macros are simply named adapters from content to
+`ISharingMacro`.
+
+ >>> class RockersPlayable:
+ ... component.adapts(interface.Interface)
+ ... interface.implements(interfaces.ISharingMacro)
+ ...
+ ... order = 1
+ ...
+ ... def __init__(self, ignored):
+ ... pass # we don't need the content for this example
+ ...
+ ... def share(self, sharing):
+ ... sharing.setBinaryPrivileges('rockers', 4)
+
+ >>> component.provideAdapter(RockersPlayable, name='Rockers Playable')
+
+ >>> class AliceAll(RockersPlayable):
+ ...
+ ... order = 9
+ ...
+ ... def share(self, sharing):
+ ... sharing.setBinaryPrivileges('p2', 7)
+
+ >>> component.provideAdapter(AliceAll, name='Alice All')
+
+
+The sharing tab provides a macros method for getting the macro names:
+
+ >>> list(tabs.macros())
+ [u'Rockers Playable', u'Alice All']
+
+If a request is provided with a 'apply_sharing_macro' value,
+then the macro is executed and the output reflects the changes.
+
+ >>> request = TestRequest()
+ >>> request.form['apply_sharing_macro'] = u'Rockers Playable'
+ >>> tabs = SharingTab(container, request)
+ >>> output(tabs.formatter.getRows())
+ row:
+ cell:
+ <input type='submit' name="remove.cDE=" value="Remove" />
+ cell:
+ <img src="http://mysite/user_icon.gif"> Grace Slick
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.3.used"
+ name="sharing.cDE=.3.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.3"
+ name="sharing.cDE=.3" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.4.used"
+ name="sharing.cDE=.4.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.4"
+ name="sharing.cDE=.4" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.0.used"
+ name="sharing.cDE=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cDE=.0"
+ name="sharing.cDE=.0" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.1.used"
+ name="sharing.cDE=.1.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.1"
+ name="sharing.cDE=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cDE=.2.used"
+ name="sharing.cDE=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.cDE=.2"
+ name="sharing.cDE=.2" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.cm9ja2Vycw==" value="Remove" />
+ cell:
+ <img src="http://mysite/group_icon.gif"> Rock Performers
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.3.used"
+ name="sharing.cm9ja2Vycw==.3.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cm9ja2Vycw==.3"
+ name="sharing.cm9ja2Vycw==.3" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.4.used"
+ name="sharing.cm9ja2Vycw==.4.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cm9ja2Vycw==.4"
+ name="sharing.cm9ja2Vycw==.4" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.0.used"
+ name="sharing.cm9ja2Vycw==.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cm9ja2Vycw==.0"
+ name="sharing.cm9ja2Vycw==.0" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.1.used"
+ name="sharing.cm9ja2Vycw==.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.cm9ja2Vycw==.1"
+ name="sharing.cm9ja2Vycw==.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.cm9ja2Vycw==.2.used"
+ name="sharing.cm9ja2Vycw==.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked"
+ id="sharing.cm9ja2Vycw==.2"
+ name="sharing.cm9ja2Vycw==.2" type="checkbox" value="on" />
+ row:
+ cell:
+ <input type='submit' name="remove.ZzU=" value="Remove" />
+ cell:
+ <img src="http://mysite/group_icon.gif"> zorina
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.3.used"
+ name="sharing.ZzU=.3.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.3"
+ name="sharing.ZzU=.3" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.4.used"
+ name="sharing.ZzU=.4.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.4"
+ name="sharing.ZzU=.4" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.0.used"
+ name="sharing.ZzU=.0.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.0"
+ name="sharing.ZzU=.0" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.1.used"
+ name="sharing.ZzU=.1.used" type="hidden" value="" />
+ <input class="checkboxType" id="sharing.ZzU=.1"
+ name="sharing.ZzU=.1" type="checkbox" value="on" />
+ cell:
+ <input class="hiddenType" id="sharing.ZzU=.2.used"
+ name="sharing.ZzU=.2.used" type="hidden" value="" />
+ <input class="checkboxType" checked="checked" id="sharing.ZzU=.2"
+ name="sharing.ZzU=.2" type="checkbox" value="on" />
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/sharing.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/test_template.pt
===================================================================
--- zc.sharing/trunk/src/zc/sharing/browser/test_template.pt 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/browser/test_template.pt 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,2 @@
+This is a test template used in the sharing functional tests.
+
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/test_template.pt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/tests.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/browser/tests.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/browser/tests.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,78 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""
+
+$Id$
+"""
+import unittest
+from zope.app.testing import placelesssetup
+from zope import component, interface
+import zope.publisher.interfaces.browser
+import zope.schema.interfaces
+import zope.app.form.browser
+from zc.sharing import policy
+import zc.sharing.sharing
+import zc.table.interfaces
+import zc.table.table
+
+class ICon:
+
+ def __init__(self, name):
+ self.name = name
+
+ def __call__(self, request=None):
+ if request is None:
+ return 'http://mysite/%s' % self.name
+
+ return self
+
+def sharingSetUp(test):
+ placelesssetup.setUp(test)
+ component.provideAdapter(
+ zope.app.form.browser.CheckBoxWidget,
+ (zope.schema.interfaces.IBool,
+ zope.publisher.interfaces.browser.IBrowserRequest,
+ ),
+ zope.app.form.interfaces.IInputWidget)
+ component.provideAdapter(
+ ICon('user_icon.gif'),
+ [zope.publisher.interfaces.browser.IBrowserRequest],
+ interface.Interface, 'user_icon.gif')
+ component.provideAdapter(
+ ICon('group_icon.gif'),
+ [zope.publisher.interfaces.browser.IBrowserRequest],
+ interface.Interface, 'group_icon.gif')
+ interface.directlyProvides(zc.table.table.FormFullFormatter,
+ zc.table.interfaces.IFormatterFactory)
+ component.provideUtility(zc.table.table.FormFullFormatter,
+ zc.table.interfaces.IFormatterFactory)
+
+def sharingTearDown(test):
+ placelesssetup.tearDown()
+ zc.sharing.sharing.clearPrivileges()
+
+def test_suite():
+ from zope.testing import doctest
+ return unittest.TestSuite((
+ doctest.DocFileSuite(
+ 'sharing.txt',
+ setUp=sharingSetUp, tearDown=sharingTearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE,
+ ),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
+
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/tests.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/browser/user_icon.gif
===================================================================
(Binary files differ)
Property changes on: zc.sharing/trunk/src/zc/sharing/browser/user_icon.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/configure.zcml
===================================================================
--- zc.sharing/trunk/src/zc/sharing/configure.zcml 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/configure.zcml 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,36 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:zc="http://namespaces.zope.com/zc"
+ i18n_domain="zc.sharing">
+
+<content class=".index.Index">
+ <require permission="zope.ManageServices"
+ interface="zope.index.interfaces.IStatistics"
+ />
+</content>
+
+<adapter factory=".sharing.BaseSharing"
+ trusted="1" permission="zope.Security" />
+
+<adapter factory=".sharing.Sharing"
+ trusted="1" permission="zope.Security" />
+
+<adapter factory=".sharing.InitialSharing" />
+
+<subscriber for=".interfaces.ISharable
+ zope.app.container.interfaces.IObjectAddedEvent"
+ handler=".sharing.initialSharing"
+ />
+
+<securityPolicy component=".policy.SecurityPolicy" />
+
+
+<include package=".browser" />
+
+<utility
+ name="zc.sharing.generation"
+ provides="zope.app.generations.interfaces.ISchemaManager"
+ component=".generation.manager"
+ />
+
+</configure>
Property changes on: zc.sharing/trunk/src/zc/sharing/configure.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/generation/__init__.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/generation/__init__.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/generation/__init__.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,26 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+import zope.app.generations
+
+minimum_generation = 0
+generation = 0
+
+manager = zope.app.generations.generations.SchemaManager(
+ minimum_generation, generation, 'zc.sharing.generation')
Property changes on: zc.sharing/trunk/src/zc/sharing/generation/__init__.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/generation/install.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/generation/install.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/generation/install.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,22 @@
+import zc.sharing.interfaces
+import ZODB.FileStorage.FileStorage
+
+def evolve(context):
+ # specifically for FileStorage, since all legacy instances are FileStorage
+ storage = context.connection.db()._storage
+ if not isinstance(storage, ZODB.FileStorage.FileStorage):
+ return
+ key = None
+ next_oid = 0
+ while next_oid is not None:
+ oid, tid, data, next_oid = storage.record_iternext(key)
+ obj = context.connection.get(oid)
+ sharing = zc.sharing.interfaces.IBaseSharing(obj, None)
+ if sharing is not None:
+ try:
+ data = sharing.annotations.pop('instranet.sharing.sharing')
+ except KeyError:
+ pass
+ else:
+ sharing.annotations['zc.sharing.sharing'] = data
+ key = next_oid
Property changes on: zc.sharing/trunk/src/zc/sharing/generation/install.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/i18n.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/i18n.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/i18n.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,32 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""I18N support for sharing.
+
+This defines a `MessageFactory` for the I18N domain for the sharing
+package. This is normally used with this import::
+
+ from i18n import MessageFactory as _
+
+The factory is then used normally. Two examples::
+
+ text = _('some internationalized text')
+ text = _('helpful-descriptive-message-id', 'default text')
+"""
+__docformat__ = "reStructuredText"
+
+
+from zope import i18nmessageid
+
+MessageFactory = _ = i18nmessageid.MessageFactory("zc.sharing")
Property changes on: zc.sharing/trunk/src/zc/sharing/i18n.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/index.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/index.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/index.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,111 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""
+
+$Id$
+"""
+
+import BTrees.IFBTree
+import BTrees.OOBTree
+import BTrees.IOBTree
+import persistent
+
+from zope import component, interface
+import zope.security.interfaces
+import zope.security.proxy
+import zope.index.interfaces
+
+import zope.app.catalog.interfaces
+from zope.app import zapi
+
+from zc.sharing import interfaces, policy
+
+class Index(persistent.Persistent):
+
+ interface.implements(zope.app.catalog.interfaces.ICatalogIndex,
+ zope.index.interfaces.IStatistics,
+ )
+
+ def __init__(self, privilege_id):
+ self.privilege_id = privilege_id
+ self.privileges = 1 << privilege_id
+ self.clear()
+
+ def index_doc(self, doc_id, value):
+ self.unindex_doc(doc_id)
+
+ unproxied = zope.security.proxy.removeSecurityProxy(value)
+ sharing = interfaces.IBaseSharing(unproxied, None)
+ if sharing is None:
+ return
+
+ document_principals = self.document_principals
+ principal_documents = self.principal_documents
+
+ for principal_id in sharing.getPrincipals():
+ if self.privileges & sharing.getBinaryPrivileges(principal_id):
+ docs = principal_documents.get(principal_id)
+ if docs is None:
+ docs = BTrees.IFBTree.IFTreeSet()
+ principal_documents[principal_id] = docs
+ docs.insert(doc_id)
+
+ principals = document_principals.get(doc_id)
+ if principals is None:
+ principals = BTrees.OOBTree.OOTreeSet()
+ document_principals[doc_id] = principals
+ principals.insert(principal_id)
+
+
+ def unindex_doc(self, doc_id):
+ principals = self.document_principals.get(doc_id)
+ if principals is None:
+ return
+
+ for principal_id in principals:
+ docs = self.principal_documents.get(principal_id)
+ if docs and (doc_id in docs):
+ docs.remove(doc_id)
+
+ del self.document_principals[doc_id]
+
+ def clear(self):
+ self.principal_documents = BTrees.OOBTree.OOBTree()
+ self.document_principals = BTrees.IOBTree.IOBTree()
+
+ def apply(self, principal):
+ principal_documents = self.principal_documents
+ result = principal_documents.get(principal.id)
+
+ groups = {}
+ getPrincipal = zapi.principals().getPrincipal
+ policy._findGroupsFor(principal, getPrincipal, groups)
+
+ for gid in groups:
+ result = BTrees.IFBTree.union(result, principal_documents.get(gid))
+
+ if result is None:
+ result = BTrees.IFBTree.IFSet()
+
+ return result
+
+ # XXX need Length objects for scalability
+
+ def documentCount(self):
+ return len(self.document_principals)
+
+ def wordCount(self):
+ return len(self.principal_documents)
+
Property changes on: zc.sharing/trunk/src/zc/sharing/index.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/index.txt
===================================================================
--- zc.sharing/trunk/src/zc/sharing/index.txt 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/index.txt 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,114 @@
+Sharing Index
+=============
+
+The sharing index supports "security-filtered search". A sharing
+index keeps track of the principals that have the read priviledge for
+objects. The index can then provide a set of all of the objects a
+principal can access, taking the principal's groups into account.
+
+Sharing indexes adapt their inputs to ISharing. For illustrative
+purposes, we'll provide objects that provide ISharing directly. We'll
+only bother implementing the methods that the sharing index actually
+uses:
+
+ >>> from zope import interface
+ >>> import zc.sharing.sharing
+ >>> from zc.sharing import interfaces
+ >>> zc.sharing.sharing.definePrivilege(0, "Read")
+ >>> class SampleDoc:
+ ... interface.implements(interfaces.IBaseSharing)
+ ...
+ ... def __init__(self, *principal_ids):
+ ... self.principal_ids = principal_ids
+ ...
+ ... def getPrincipals(self):
+ ... return self.principal_ids
+ ...
+ ... def getBinaryPrivileges(self, principal_id):
+ ... if principal_id in self.principal_ids:
+ ... return 2
+ ... return 0
+
+
+Now we can try indexing some documents:
+
+ >>> import zc.sharing.index
+ >>> index = zc.sharing.index.Index(1)
+
+ >>> index.index_doc(1, SampleDoc('bob', 'Everyone'))
+ >>> index.index_doc(2, SampleDoc('bob'))
+ >>> index.index_doc(3, SampleDoc('sally', 'Editors'))
+ >>> index.index_doc(4, SampleDoc('sally', 'Reviewers'))
+ >>> index.index_doc(5, SampleDoc('sally'))
+ >>> index.index_doc(6, SampleDoc('sally', 'Everyone'))
+ >>> index.index_doc(7, SampleDoc('Workers'))
+
+Note that, when we created the index, we passed the privilege id of
+the privilege to be used for the index.
+
+Now, we can search it to find the documents accessable to a principal.
+First we'll define a principal class. Principals have a groups
+attribute that has the groups that the principal is contained in
+directly:
+
+ >>> class Principal:
+ ... def __init__(self, id, *groups):
+ ... self.id, self.groups = id, (groups + ('Everyone', ))
+
+The sharing index determines all of the groups a principal is in
+by loading information about their groups from an authentication
+utility. We'll provide one that knows about our groups:
+
+ >>> from zope import component
+ >>> from zope.app.security.interfaces import IAuthentication
+ >>> class Authentication:
+ ... interface.implements(IAuthentication)
+ ... def getPrincipal(self, id):
+ ... if id in ('Editors', 'Reviewers'):
+ ... return Principal(id, 'Workers')
+ ... else:
+ ... return Principal(id)
+
+ >>> component.provideUtility(Authentication())
+
+ >>> r = index.apply(Principal('sally', 'Editors', 'Reviewers'))
+ >>> r.__class__.__name__
+ 'IFSet'
+
+ >>> list(r)
+ [1, 3, 4, 5, 6, 7]
+
+ >>> list(index.apply(Principal('fred')))
+ [1, 6]
+
+If we modify a document, so that different principals have access:
+
+ >>> index.index_doc(4, SampleDoc('fred', 'Reviewers'))
+
+Then the results change accordingly:
+
+ >>> list(index.apply(Principal('sally', 'Editors')))
+ [1, 3, 5, 6, 7]
+
+ >>> list(index.apply(Principal('fred')))
+ [1, 4, 6]
+
+And if we remove documents:
+
+ >>> index.unindex_doc(1)
+ >>> index.unindex_doc(4)
+
+ >>> list(index.apply(Principal('sally', 'Editors')))
+ [3, 5, 6, 7]
+
+ >>> list(index.apply(Principal('fred')))
+ [6]
+
+Of course, if we clear the index:
+
+ >>> index.clear()
+ >>> list(index.apply(Principal('sally', 'Editors')))
+ []
+
+ >>> list(index.apply(Principal('fred')))
+ []
Property changes on: zc.sharing/trunk/src/zc/sharing/index.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/interfaces.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/interfaces.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/interfaces.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,195 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""Z4I Security Policy (Sharing) APIs.
+
+$Id$
+"""
+
+from zope import interface, schema
+import zope.app.annotation.interfaces
+import zope.app.event.interfaces
+import zope.app.event.objectevent
+
+class ISharable(zope.app.annotation.interfaces.IAttributeAnnotatable):
+ """Sharable content
+
+ Sharable can be adapted to ISharing.
+ """
+
+class IBaseSharing(ISharable):
+
+ def getPrincipals():
+ """Return the principal ids for the principals that have privileges
+
+ The return value is an iterable.
+ """
+
+ def getBinaryPrivileges(principal_id):
+ """Get the principal's privileges
+
+ An integer privileges value is returned.
+ """
+
+ def setBinaryPrivileges(principal_id, privileges):
+ """Set the principal's privileges to the privileges passed
+
+ The privileges argument is an integer.
+ """
+
+ def sharedTo(id, principal_ids):
+ """Test whether the collection of principals have a privilege.
+
+ privileges are identified with a bit position
+
+ Return a boolean value indicating whether the privilege has been
+ shared to any of the principals given by the principal_ids.
+
+ The principal_ids argument is an iterable of principal ids.
+ """
+
+class ISharing(IBaseSharing):
+
+ def removeBinaryPrivileges(principal_id, mask):
+ "Remove the privileges in the bit mask for principal_id"
+
+ def addBinaryPrivileges(principal_id, mask):
+ "Add the privileges in the bit mask for principal_id"
+
+ def getIdPrivilege(principal_id, id):
+ """Test whether the privilege is shared to a principal
+
+ Return a boolean value indicating whether the privilege has been
+ shared to the principal_id.
+ """
+
+ def setIdPrivilege(principal_id, id, value):
+ """set the privilege for the principal.
+
+ Leaves all other privileges alone.
+ """
+
+ def getIdPrivileges(principal_id):
+ """Return a sequence of the bit positions for the given principal
+ """
+
+ def setIdPrivileges(principal_id, ids):
+ "Set the principals privileges to those specified by the bit positions"
+
+ def addIdPrivileges(principal_id, ids):
+ """Add privileges, specified by bit positions, for principal.
+
+ If principal already has privilege, it is silently ignored"""
+
+ def removeIdPrivileges(principal_id, ids):
+ """Remove privileges, specified by bit positions, for principal.
+
+ If principal already does not have privilege, it is silently ignored
+ """
+
+ def getPrivilege(principal_id, title):
+ """Test whether the privilege is shared to a principal
+
+ Return a boolean value indicating whether the privilege has been
+ shared to the principal_id.
+ """
+
+ def setPrivilege(principal_id, title, value):
+ """set the privilege for the principal.
+
+ Leaves all other privileges alone.
+ """
+
+ def getPrivileges(principal_id):
+ "Return a sequence of the sharing titles for the principal_id"
+
+ def setPrivileges(principal_id, titles):
+ "Set the principals privileges to those specified by the titles"
+
+ def addPrivileges(principal_id, titles):
+ """Add privileges, specified by titles, for principal.
+
+ If principal already has privilege, it is silently ignored"""
+
+ def removePrivileges(principal_id, titles):
+ """Remove privileges, specified by titles, for principal.
+
+ If principal already does not have privilege, it is silently ignored
+ """
+
+class ISharingPrivileges(interface.Interface):
+
+ privileges = schema.Tuple(
+ title=u"Ids of privileges used by a content type",
+ value_type=schema.Int(),
+ )
+
+class ISubobjectSharingPrivileges(interface.Interface):
+
+ subobjectPrivileges = schema.Tuple(
+ title=u"Ids of privileges used by subobjects of a content type",
+ value_type=schema.Int(),
+ )
+
+class IInitialSharing(interface.Interface):
+ """Adapter to set sharing for newly added objects
+ """
+
+ def share():
+ """Set the initial sharing for a sharable object
+ """
+
+class ISharingMacro(interface.Interface):
+ """Sharing macros provide pluggable rules for creating sharing settings.
+
+ They are registered as named adapters for a context and a request.
+ """
+
+ def share(sharing):
+ """Modify the settings for a sharing object.
+
+ Return a boolean True if the macro changed sharing, False otherwise.
+ """
+
+ order = schema.Int(title=u"Order in which item should be displayed")
+
+ title = schema.TextLine(title=u'title', description=
+ u'''The display title of the sharing macro.
+ Will typically be a zope.i18n.Message. If None, the registered
+ adapter name will be used.''', required=False)
+
+class ISharingEvent(zope.app.event.interfaces.IObjectModifiedEvent):
+ "An event fired by the sharing package"
+
+class ISharingChanged(ISharingEvent):
+ """Sharing settings were changed for an object
+ """
+
+ principal_id = schema.TextLine(
+ title=u"The id of the principal who's sharing has changed",
+ )
+
+ old = schema.Int(title=u"Old settings")
+
+ new = schema.Int(title=u"Old settings")
+
+class SharingChanged(zope.app.event.objectevent.ObjectModifiedEvent):
+
+ interface.implements(ISharingChanged)
+
+ def __init__(self, object, principal_id, old, new):
+ zope.app.event.objectevent.ObjectModifiedEvent.__init__(self, object)
+ self.principal_id = principal_id
+ self.old = old
+ self.new = new
Property changes on: zc.sharing/trunk/src/zc/sharing/interfaces.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/meta.zcml
===================================================================
--- zc.sharing/trunk/src/zc/sharing/meta.zcml 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/meta.zcml 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,29 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:meta="http://namespaces.zope.org/meta">
+
+ <meta:directive namespace="http://namespaces.zope.com/zc"
+ name="privilege"
+ schema=".zcml.IdefinePrivilege"
+ handler=".zcml.definePrivilege" />
+
+ <meta:directive namespace="http://namespaces.zope.com/zc"
+ name="permissionPrivilege"
+ schema=".zcml.IpermissionPrivilege"
+ handler=".zcml.permissionPrivilege" />
+
+ <meta:directive namespace="http://namespaces.zope.com/zc"
+ name="systemAdministrators"
+ schema=".zcml.IsystemAdministrators"
+ handler=".zcml.systemAdministrators" />
+
+ <meta:directive namespace="http://namespaces.zope.com/zc"
+ name="privileges"
+ schema=".zcml.Iprivileges"
+ handler=".zcml.privileges" />
+
+ <meta:directive namespace="http://namespaces.zope.com/zc"
+ name="subobjectPrivileges"
+ schema=".zcml.Iprivileges"
+ handler=".zcml.subobjectPrivileges" />
+
+</configure>
Property changes on: zc.sharing/trunk/src/zc/sharing/meta.zcml
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/policy.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/policy.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/policy.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,210 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""Zope4Intranets security policy
+
+$Id$
+"""
+
+from zope import interface, component
+
+from zope.app import zapi
+from zope.app.security.interfaces import PrincipalLookupError
+
+from zope.security.checker import CheckerPublic
+from zope.security.management import system_user
+from zope.security.simplepolicies import ParanoidSecurityPolicy
+from zope.security.interfaces import ISecurityPolicy
+from zope.security.proxy import removeSecurityProxy
+from zope.app.security.interfaces import PrincipalLookupError
+
+from zc.sharing import interfaces, sharing
+
+admin_group = 'zc.intranet.policy.admingroup'
+
+class CacheEntry:
+ pass
+
+class SecurityPolicy(ParanoidSecurityPolicy):
+ interface.classProvides(ISecurityPolicy)
+
+ def __init__(self, *args, **kw):
+ ParanoidSecurityPolicy.__init__(self, *args, **kw)
+ self._cache = {}
+
+ def invalidateCache(self):
+ self._cache = {}
+
+ def cache(self, parent):
+ cache = self._cache.get(id(parent))
+ if cache:
+ cache = cache[0]
+ else:
+ cache = CacheEntry()
+ self._cache[id(parent)] = cache, parent
+ return cache
+
+ def cachedDecision(self, parent, principal, groups, privilege):
+ # Return the decision for a principal and permission
+
+ cache = self.cache(parent)
+ try:
+ cache_decision = cache.decision
+ except AttributeError:
+ cache_decision = cache.decision = {}
+
+ cache_decision_prin = cache_decision.get(principal)
+ if not cache_decision_prin:
+ cache_decision_prin = cache_decision[principal] = {}
+
+ try:
+ return cache_decision_prin[privilege]
+ except KeyError:
+ pass
+
+ sharing = interfaces.IBaseSharing(parent, None)
+ if sharing is not None:
+ decision = sharing.sharedTo(privilege, groups)
+ elif parent is None:
+ decision = False
+ else:
+ parent = removeSecurityProxy(getattr(parent, '__parent__', None))
+ decision = self.cachedDecision(
+ parent, principal, groups, privilege)
+
+
+ cache_decision_prin[privilege] = decision
+ return decision
+
+
+ def checkPermission(self, permission, object):
+ if permission is CheckerPublic:
+ return True
+
+ object = removeSecurityProxy(object)
+ seen = {}
+ for participation in self.participations:
+ principal = participation.principal
+ if principal is system_user:
+ continue # always allow system_user
+
+ if principal.id in seen or principal.id in systemAdministrators:
+ continue
+
+
+ groups = self._groupsFor(principal)
+
+ privilege = _permissionPrivileges.get(permission, -1)
+ if privilege < 0:
+ # No privilege
+ return False
+
+ if admin_group not in groups:
+ # admins have all privileges:
+
+ if not self.cachedDecision(
+ object, principal.id, groups, privilege,
+ ):
+ return False
+
+ seen[principal.id] = 1
+
+ return True
+
+ def _groupsFor(self, principal):
+ groups = self._cache.get(principal.id)
+ if groups is None:
+ groups = getattr(principal, 'groups', ())
+ if groups:
+ groups = {}
+ getPrincipal = zapi.principals().getPrincipal
+ _findGroupsFor(principal, getPrincipal, groups)
+ else:
+ groups = {}
+
+ groups[principal.id] = 1
+
+ self._cache[principal.id] = groups
+
+ return groups
+
+def _findGroupsFor(principal, getPrincipal, seen):
+ for group_id in getattr(principal, 'groups', ()):
+ if group_id in seen:
+ # Dang, we have a cycle. We don't want to
+ # raise an exception here (or do we), so we'll skip it
+ continue
+ seen[group_id] = 1
+
+ try:
+ group = getPrincipal(group_id)
+ except PrincipalLookupError:
+ # It's bad if we have an undefined principal,
+ # but we don't want to fail here. But we won't
+ # honor any grants for the group. We'll just skip it.
+ continue
+
+ _findGroupsFor(group, getPrincipal, seen)
+
+_permissionPrivileges = {}
+permissionPrivilege = _permissionPrivileges.__setitem__
+getPermissionPrivilege = _permissionPrivileges.__getitem__
+removePermissionPrivilege = _permissionPrivileges.__delitem__
+
+def fixedAdapter(adapter):
+
+ def adapt(ob):
+ return adapter
+
+ return adapt
+
+class SharingPrivileges:
+ interface.implements(interfaces.ISharingPrivileges)
+
+ def __init__(self, privileges):
+ self.privileges = tuple(privileges)
+
+def sharingPrivileges(for_, titles):
+ defined = dict([(p['title'], p) for p in sharing.getPrivileges()])
+
+ ids = []
+ for title in titles:
+ try:
+ ids.append(defined[title]['id'])
+ except KeyError:
+ raise ValueError("Undefined privilege", title)
+
+ component.provideAdapter(fixedAdapter(SharingPrivileges(ids)),
+ (for_, ), interfaces.ISharingPrivileges)
+
+class SubobjectSharingPrivileges:
+ interface.implements(interfaces.ISharingPrivileges)
+
+ def __init__(self, privileges):
+ self.subobjectPrivileges = tuple(privileges)
+
+def subobjectSharingPrivileges(for_, titles):
+ defined = dict([(p['title'], p) for p in sharing.getPrivileges()])
+
+ ids = []
+ for title in titles:
+ try:
+ ids.append(defined[title]['id'])
+ except KeyError:
+ raise ValueError("Undefined privilege", title)
+
+ component.provideAdapter(fixedAdapter(SubobjectSharingPrivileges(ids)),
+ (for_, ), interfaces.ISubobjectSharingPrivileges)
+
+systemAdministrators = ()
Property changes on: zc.sharing/trunk/src/zc/sharing/policy.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/policy.txt
===================================================================
--- zc.sharing/trunk/src/zc/sharing/policy.txt 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/policy.txt 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,270 @@
+Zope for Intranets Security Policy
+==================================
+
+This package implements a security policy based on privileges. A
+privilege is a very abstract permission. The security policy is
+responsible for deciding whether an interaction has a permission on an
+object. This security policy does this using privilege-grant
+information. Users with the sharing privilege can grant privileges
+to users or groups.
+
+Privileges
+-----------
+
+First we must define our privileges, as discussed in sharing.txt, and then
+map our permissions onto them.
+
+ >>> import zc.sharing.sharing
+ >>> zc.sharing.sharing.definePrivilege(0, "Read", "Read content")
+ >>> zc.sharing.sharing.definePrivilege(2, "Write", "Write content")
+ >>> zc.sharing.sharing.definePrivilege(
+ ... 4, "Share", "Share content (grant privileges)")
+ >>> from zc.sharing import policy
+ >>> policy.permissionPrivilege('R1', 0)
+ >>> policy.permissionPrivilege('R2', 0)
+ >>> policy.permissionPrivilege('W1', 2)
+ >>> policy.permissionPrivilege('W2', 2)
+ >>> policy.permissionPrivilege('W3', 2)
+ >>> policy.permissionPrivilege('S1', 4)
+
+Principals and Interactions
+---------------------------
+
+We use objects to represent principals. These objects implement an
+interface named `IPrincipal`, but the security policy only uses the `id`
+and `groups` attributes:
+
+ >>> class Principal:
+ ... def __init__(self, id):
+ ... self.id = id
+ ... self.groups = []
+
+ >>> principal = Principal('bob')
+
+Privileges and permissions are also represented by objects, however, for
+the purposes of the security policy, only string `ids` are used.
+
+The security policy provides a factory for creating interactions:
+
+ >>> interaction = policy.SecurityPolicy()
+
+An interaction represents a specific interaction between some
+principals (normally users) and the system. Normally, we are only
+concerned with the interaction of one principal with the system, although
+we can have interactions of multiple principals. Multiple-principal
+interactions normally occur when untrusted users store code on a
+system for later execution. When untrusted code is executing, the
+authors of the code participate in the interaction. An
+interaction has a permission on an object only if all of the
+principals participating in the interaction have access to the object.
+
+The `checkPermission` method on interactions is used to test whether
+an interaction has a permission for an object. An interaction without
+participants always has every permission:
+
+ >>> import zope.interface
+ >>> class Ob:
+ ... pass
+
+ >>> ob = Ob()
+
+ >>> interaction.checkPermission('W1', ob)
+ True
+
+In this example, 'W1' is a permission id.
+
+Normally, interactions have participants:
+
+ >>> class Participation:
+ ... interaction = None
+ >>> participation = Participation()
+ >>> participation.principal = principal
+ >>> interaction.add(participation)
+
+If we have participants, then we don't have a permission unless there
+are grants:
+
+ >>> interaction.checkPermission('W1', ob)
+ False
+
+Note, however, that we always have the CheckerPublic permission:
+
+ >>> from zope.security.checker import CheckerPublic
+ >>> interaction.checkPermission(CheckerPublic, ob)
+ True
+
+Grants
+------
+
+We make grants on objects by adapting them to ISharing. Here's a very
+simple adapter that implements ISharing:
+
+ >>> from zc.sharing import interfaces
+ >>> from zope import component
+ >>> class Sharing:
+ ...
+ ... component.adapts(Ob)
+ ... zope.interface.implements(interfaces.ISharing)
+ ...
+ ... def __init__(self, context):
+ ... self.context = context
+ ...
+ ... def getPrincipals(self):
+ ... return getattr(self.context, 'privileges', {}).keys()
+ ...
+ ... def getPrivileges(self, principal_id):
+ ... privileges = getattr(self.context, 'privileges', {})
+ ... return privileges.get(principal_id, 0)
+ ...
+ ... def setPrivileges(self, principal_id, privileges):
+ ... current = getattr(self.context, 'privileges', None)
+ ... if current is None:
+ ... self.context.privileges = current = {}
+ ... if privileges:
+ ... current[principal_id] = privileges
+ ... else:
+ ... if principal_id in current:
+ ... del current[principal_id]
+ ... interaction.invalidateCache()
+ ...
+ ... def sharedTo(self, privilege, principal_ids):
+ ... privileges = getattr(self.context, 'privileges', {})
+ ... for principal_id in principal_ids:
+ ... if 2**privilege & privileges.get(principal_id, 0):
+ ... return True
+ ... return False
+
+ >>> component.provideAdapter(Sharing)
+
+(Note that in setPrivileges, we invalidated the interaction cache.)
+
+Now we can grant privileges to our object. Let's grant the write
+privilege to bob:
+
+ >>> sharing = interfaces.ISharing(ob)
+ >>> sharing.setPrivileges('bob', 2**2)
+
+Now, we have the permission:
+
+ >>> interaction.checkPermission('W1', ob)
+ True
+
+because the write privilege has the permission and our principal,
+'bob', has the privilege. Of course, we still don't have the write
+privilege:
+
+ >>> interaction.checkPermission('R1', ob)
+ False
+
+
+Non-Sharing objects
+-------------------
+
+If an object doesn't support sharing, then access isn't granted:
+
+ >>> class Other:
+ ... pass
+
+ >>> other = Other()
+ >>> interaction.checkPermission('W1', other)
+ False
+
+However, if the object has a parent that supports sharing, then access
+depends on the parent's sharing:
+
+ >>> other = Other()
+ >>> other.__parent__ = ob
+ >>> interaction.checkPermission('W1', other)
+ True
+
+This applies to ancestors too:
+
+ >>> other = Other()
+ >>> other.__parent__ = Other()
+ >>> interaction.checkPermission('W1', other)
+ False
+
+ >>> other = Other()
+ >>> other.__parent__ = Other()
+ >>> other.__parent__.__parent__ = ob
+ >>> interaction.checkPermission('W1', other)
+ True
+
+Groups
+------
+
+Principals may have groups. Groups are also principals (and, thus,
+may have groups).
+
+If a principal has groups, the groups are available as group ids in
+the principal's `groups` attribute. The interaction has to convert
+these group ids to group objects, so that it can tell whether the
+groups have groups. It does this by calling the `getPrincipal` method
+on the principal authentication service, which is responsible for,
+among other things, converting a principal id to a principal.
+For our examples here, we'll create and register a stub principal
+authentication service:
+
+ >>> from zope.app.security.interfaces import IAuthentication
+ >>> class FauxPrincipals(dict):
+ ... zope.interface.implements(IAuthentication)
+ ... def getPrincipal(self, id):
+ ... return self[id]
+
+ >>> auth = FauxPrincipals()
+
+ >>> from zope.app.tests import ztapi
+ >>> ztapi.provideUtility(IAuthentication, auth)
+ >>> from zope.app import zapi
+
+Let's define a group and assign it the read privilege:
+
+ >>> auth['g1'] = Principal('g1')
+ >>> sharing.setPrivileges('g1', 2**0)
+
+Let's put the principal in our group. We do that by adding the group id
+to the new principal's groups:
+
+ >>> principal.groups.append('g1')
+
+And now we have the read permission:
+
+ >>> interaction.checkPermission('R1', ob)
+ True
+
+Of course, this works with the non-sharable object that has this
+object as an ancestor:
+
+ >>> interaction.checkPermission('R1', other)
+ True
+
+Administrative groups
+---------------------
+
+There is a special administrative groups, defined by the
+policy, that has all privileges:
+
+ >>> auth[policy.admin_group] = Principal(policy.admin_group)
+
+Our principal doesn't have the share privilege:
+
+ >>> interaction.checkPermission('S1', ob)
+ False
+
+But if we put them in the admin group, they do
+
+ >>> principal.groups.append(policy.admin_group)
+ >>> interaction.invalidateCache()
+ >>> interaction.checkPermission('S1', ob)
+ True
+
+There is a collection of system administrators that have all
+permissions, including those that aren't associated with privileges:
+
+ >>> interaction.checkPermission('P1', ob)
+ False
+
+ >>> policy.systemAdministrators = ('bob', )
+
+ >>> interaction.checkPermission('P1', ob)
+ True
Property changes on: zc.sharing/trunk/src/zc/sharing/policy.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/sharing.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/sharing.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/sharing.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,280 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""Sharing adapter
+
+$Id$
+"""
+
+import persistent
+import persistent.dict
+
+from zope import component, interface, event
+
+from zope.security.management import queryInteraction
+from zope.publisher.interfaces import IRequest
+
+from zope.app.annotation.interfaces import IAnnotations
+from zope.app.container.interfaces import IObjectAddedEvent
+
+from zc.sharing import interfaces
+from zc.sharing.i18n import _
+
+key = 'zc.sharing.sharing'
+
+class SharingData(persistent.dict.PersistentDict):
+ """Sharing Data
+ """
+
+class BaseSharing(object):
+
+ component.adapts(interfaces.ISharable)
+ interface.implements(interfaces.IBaseSharing)
+
+ def __init__(self, context):
+ self.context = context
+ self.annotations = IAnnotations(self.context)
+
+ def getPrincipals(self):
+ privileges = self.annotations.get(key)
+ if privileges:
+ return privileges.keys()
+ return ()
+
+ def getBinaryPrivileges(self, principal_id):
+ privileges = self.annotations.get(key)
+ if privileges:
+ return privileges.get(principal_id, 0)
+ return 0
+
+ def setBinaryPrivileges(self, principal_id, privileges):
+ saved = self.annotations.get(key)
+ if saved is None:
+ self.annotations[key] = saved = SharingData()
+
+ old = saved.get(principal_id, 0)
+ if privileges:
+ saved[principal_id] = privileges
+ else:
+ if principal_id in saved:
+ del saved[principal_id]
+
+ interaction = queryInteraction()
+ if interaction is not None:
+ try:
+ invalidateCache = interaction.invalidateCache
+ except AttributeError:
+ pass
+ else:
+ invalidateCache()
+
+ if old != privileges:
+ event.notify(
+ interfaces.SharingChanged(
+ self.context, principal_id, old, privileges,
+ )
+ )
+
+ def sharedTo(self, id, principal_ids):
+ privileges = self.annotations.get(key)
+ if privileges:
+ bit = 2**id
+ for principal_id in principal_ids:
+ if bit & privileges.get(principal_id, 0):
+ return True
+ return False
+
+class Sharing(object):
+
+ component.adapts(interfaces.ISharable)
+ interface.implements(interfaces.ISharing)
+
+ def __init__(self, context):
+ self.context = context
+ self.base = interfaces.IBaseSharing(context)
+
+ # look transparently through to base for IBaseSharing methods
+ def __getattr__(self, name):
+ if name in (
+ 'getPrincipals', 'getBinaryPrivileges', 'setBinaryPrivileges',
+ 'sharedTo'):
+ return getattr(self.base, name)
+
+ # additional ISharing methods
+ def removeBinaryPrivileges(self, principal_id, mask):
+ privs = self.base.getBinaryPrivileges(principal_id)
+ self.base.setBinaryPrivileges(principal_id, (privs | mask) ^ mask)
+
+ def addBinaryPrivileges(self, principal_id, mask):
+ privs = self.base.getBinaryPrivileges(principal_id)
+ self.base.setBinaryPrivileges(principal_id, privs | mask)
+
+ def getIdPrivilege(self, principal_id, id):
+ return self.base.sharedTo(id, (principal_id,))
+
+ def setIdPrivilege(self, principal_id, id, value):
+ if value:
+ return self.addIdPrivileges(principal_id, (id,))
+ else:
+ return self.removeIdPrivileges(principal_id, (id,))
+
+ def getIdPrivileges(self, principal_id):
+ return idsFromSetting(self.base.getBinaryPrivileges(principal_id))
+
+ def setIdPrivileges(self, principal_id, ids):
+ return self.base.setBinaryPrivileges(
+ principal_id, settingFromIds(ids))
+
+ def addIdPrivileges(self, principal_id, ids):
+ return self.addBinaryPrivileges(
+ principal_id, settingFromIds(ids))
+
+ def removeIdPrivileges(self, principal_id, ids):
+ return self.removeBinaryPrivileges(
+ principal_id, settingFromIds(ids))
+
+ def getPrivilege(self, principal_id, title):
+ return self.getIdPrivilege(
+ principal_id, getIdByTitle(title))
+
+ def setPrivilege(self, principal_id, title, value):
+ return self.setIdPrivilege(
+ principal_id, getIdByTitle(title), value)
+
+ def getPrivileges(self, principal_id):
+ return [getPrivilege(bit)['title'] for bit
+ in self.getIdPrivileges(principal_id)]
+
+ def setPrivileges(self, principal_id, titles):
+ return self.setIdPrivileges(
+ principal_id, (getIdByTitle(title) for title in titles))
+
+ def addPrivileges(self, principal_id, titles):
+ return self.addIdPrivileges(
+ principal_id, (getIdByTitle(title) for title in titles))
+
+ def removePrivileges(self, principal_id, titles):
+ return self.removeIdPrivileges(
+ principal_id, (getIdByTitle(title) for title in titles))
+
+octToBit = {
+ '0': (),
+ '1': (0,),
+ '2': (1,),
+ '3': (0, 1),
+ '4': (2,),
+ '5': (0, 2),
+ '6': (1, 2),
+ '7': (0, 1, 2)
+ }
+def idsFromSetting(i):
+ "given an integer, return a sequence of the bits that are on"
+ res = []
+ for pos, c in enumerate(oct(i)[-1:0:-1]):
+ place = pos*3
+ res.extend(place+bit for bit in octToBit[c])
+ return res
+
+def settingFromIds(bits):
+ "Given any number of bit positions, generate a corresponding integer"
+ val = 0
+ for bit in bits:
+ val |= 1<<bit
+ return val
+
+def settingFromTitles(titles):
+ return settingFromIds(getIdByTitle(t) for t in titles)
+
+def titlesFromSetting(setting):
+ return (getPrivilege(i)['title'] for i in idsFromSetting(setting))
+
+def sharingMask(ob):
+ mask = 0
+ for bit in interfaces.ISharingPrivileges(ob).privileges:
+ mask |= 1 << bit
+ privs = interfaces.ISubobjectSharingPrivileges(ob, None)
+ if privs is not None:
+ for bit in privs.subobjectPrivileges:
+ mask |= 1 << bit
+
+ return mask
+
+
+_privileges_by_bit = {}
+_privileges_by_title = {}
+
+def definePrivilege(id, title, description='', info=None):
+ if title in _privileges_by_title:
+ raise ValueError("Duplicate title") # TODO should be catchable in zcml
+ if id in _privileges_by_bit:
+ raise ValueError("Duplicate id") # is caught in zcml
+ _privileges_by_bit[id] = {
+ 'id': id,
+ 'title': title,
+ 'description': description,
+ 'info': info,
+ }
+ _privileges_by_title[title] = id
+def removePrivilege(id):
+ data = _privileges_by_bit.pop(id)
+ del _privileges_by_title[data['title']]
+def clearPrivileges():
+ _privileges_by_bit.clear()
+ _privileges_by_title.clear()
+getPrivileges = _privileges_by_bit.values
+getPrivilege = _privileges_by_bit.get
+getIdByTitle = _privileges_by_title.__getitem__
+
+class InitialSharing(object):
+
+ component.adapts(interfaces.ISharable, IObjectAddedEvent)
+ interface.implements(interfaces.IInitialSharing)
+
+ def __init__(self, *contexts):
+ self.ob, self.event = contexts
+
+ def sharingMask(self):
+ return sharingMask(self.ob)
+
+ def share(self):
+ ob = self.ob
+ event = self.event
+
+ sharing = interfaces.IBaseSharing(ob)
+ if tuple(sharing.getPrincipals()):
+ return # already shared
+
+
+ mask = self.sharingMask()
+
+ # Get the parent settings:
+ parent = event.newParent
+ psharing = interfaces.IBaseSharing(parent, None)
+ if psharing is not None:
+ for p in psharing.getPrincipals():
+ sharing.setBinaryPrivileges(
+ p, psharing.getBinaryPrivileges(p) & mask)
+
+ # Share everything with current user, if any
+ interactions = queryInteraction()
+ if interactions is not None:
+ for participation in interactions.participations:
+ if IRequest.providedBy(participation):
+ sharing.setBinaryPrivileges(
+ participation.principal.id, mask)
+
+def initialSharing(ob, event):
+ adapter = component.getMultiAdapter((ob, event),
+ interfaces.IInitialSharing)
+ adapter.share()
Property changes on: zc.sharing/trunk/src/zc/sharing/sharing.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/sharing.txt
===================================================================
--- zc.sharing/trunk/src/zc/sharing/sharing.txt 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/sharing.txt 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,509 @@
+Sharing Adapters
+================
+
+BaseSharing Adapter
+-------------------
+
+The BaseSharing adapter implements the `IBaseSharing` interface for
+`ISharable` objects.
+
+IBaseSharing is an interface that provides the minimum tools needed to work
+with sharing. The ISharing interface, an extension of IBaseSharing discussed
+below, offers more convenient access to the sharing capabilities; ISharing's
+getPrivilege, setPrivilege, getPrivileges, setPrivileges, addPrivileges, and
+removePrivileges, combined with IBaseSharing's getPrincipals, are intended to
+be the most common ways for non-security-policy code to use the interface.
+The sharedTo method is important for policy code. The other methods expose
+the core sharing design as used for storage and security checking.
+
+We begin by discussing the IBaseSharing interface, because it is the best way
+to teach sharing thoroughly. If you are interested more in a quick start, skim
+to find the discussion of the more convenient methods below.
+
+The IBaseSharing interface uses bits to store privileges. These bits are
+exposed all together in a combined integer by the getBinaryPrivileges and
+setBinaryPrivileges methods, and by individual bit position in sharedTo (as
+well as in many of the methods in ISharing, discussed below). The bit
+position is called 'id' in the code. For instance, bit position (or 'id')
+0, 1, 2, 3, and 4 are exposed in get- and setBinaryPrivileges as bits 1,
+2, 4, 8, and 16. Below, when we set privileges to 21, that indicates privilege
+ids 0, 2, and 4 (1+4+16=21).
+
+By convention, product-level bit positions are even, and customization-level
+ids are odd.
+
+Setting privileges fires interfaces.SharingChanged events if the sharing
+changed.
+
+ >>> import zope.interface
+ >>> from zope.interface.verify import verifyObject
+ >>> from zc.sharing import interfaces
+
+ >>> class MyContent:
+ ... zope.interface.implements(interfaces.ISharable)
+
+ >>> import zc.sharing.sharing
+ >>> content = MyContent()
+ >>> sharing = zc.sharing.sharing.BaseSharing(content)
+ >>> verifyObject(interfaces.IBaseSharing, sharing)
+ True
+
+ >>> tuple(sharing.getPrincipals())
+ ()
+ >>> sharing.getBinaryPrivileges('bob')
+ 0
+ >>> sharing.sharedTo(0, 'bob')
+ False
+
+ >>> sharing.setBinaryPrivileges('bob', 21)
+ >>> ev = events[-1]
+ >>> verifyObject(interfaces.ISharingChanged, ev)
+ True
+ >>> ev.object is content
+ True
+ >>> ev.principal_id
+ 'bob'
+ >>> ev.old
+ 0
+ >>> ev.new
+ 21
+
+ >>> sharing.setBinaryPrivileges('mary', 1)
+ >>> ev = events[-1]
+ >>> verifyObject(interfaces.ISharingChanged, ev)
+ True
+ >>> ev.object is content
+ True
+ >>> ev.principal_id
+ 'mary'
+ >>> ev.old
+ 0
+ >>> ev.new
+ 1
+
+ >>> sorted(sharing.getPrincipals())
+ ['bob', 'mary']
+
+ >>> sharing.getBinaryPrivileges('bob')
+ 21
+
+ >>> sharing.sharedTo(0, ['bob'])
+ True
+
+(This example is misleading: sharedTo generally takes a principal and all of
+his or her groups, so this would be an example of user who is simultaneously
+both 'bob' and 'mary'. The policy module handles multiple users in an
+interaction.)
+
+ >>> sharing.sharedTo(4, ['bob', 'mary'])
+ True
+
+ >>> sharing.sharedTo(1, ['bob', 'mary'])
+ False
+
+
+The sharing data are stored persistently:
+
+ >>> import ZODB.tests.util
+ >>> db = ZODB.tests.util.DB()
+ >>> conn = db.open()
+ >>> root = conn.root()
+ >>> root['spam'] = content
+ >>> ZODB.tests.util.commit()
+ >>> conn.cacheMinimize()
+
+ >>> sorted(sharing.getPrincipals())
+ ['bob', 'mary']
+
+ >>> sharing.getBinaryPrivileges('bob')
+ 21
+
+ >>> sharing.sharedTo(0, ['bob'])
+ True
+
+ >>> sharing.sharedTo(1, ['bob'])
+ False
+
+ >>> sharing.setBinaryPrivileges('bob', 18)
+ >>> ev = events[-1]
+ >>> verifyObject(interfaces.ISharingChanged, ev)
+ True
+ >>> ev.object is content
+ True
+ >>> ev.principal_id
+ 'bob'
+ >>> ev.old
+ 21
+ >>> ev.new
+ 18
+
+ >>> ZODB.tests.util.commit()
+ >>> conn.cacheMinimize()
+
+ >>> sharing.sharedTo(0, ['bob'])
+ False
+
+ >>> sharing.sharedTo(1, ['bob'])
+ True
+
+ >>> sharing.setBinaryPrivileges('sally', 4)
+ >>> sharing.setBinaryPrivileges('bob', 0)
+ >>> ZODB.tests.util.commit()
+ >>> conn.cacheMinimize()
+ >>> sharing.sharedTo(0, ['bob'])
+ False
+ >>> sharing.sharedTo(1, ['bob'])
+ False
+
+ >>> sorted(sharing.getPrincipals())
+ ['mary', 'sally']
+
+Initial sharing
+---------------
+
+An adapter is used to set the initial sharing for objects.
+The default adapter:
+
+- Copies sharing settings from the object's container
+
+- Gives the current user all privileges
+
+The adapter provides IInitialSharing and is called when an object is
+added (not moved) to a container. The default adapter is provided by
+the InitialSharing class:
+
+ >>> from zc.sharing.sharing import InitialSharing
+
+Let's look at an example. The InitialSharing adapter will adapt the
+object it adapts to IBaseSharing. Here's an example object that is
+adaptable to IBaseSharing because it already provides it:
+
+ >>> from zope import interface
+ >>> from zc.sharing.interfaces import IBaseSharing
+ >>> class MyOb:
+ ...
+ ... interface.implements(IBaseSharing)
+ ...
+ ... def __init__(self):
+ ... self.privileges = {}
+ ...
+ ... def getPrincipals(self):
+ ... return self.privileges.keys()
+ ...
+ ... def getBinaryPrivileges(self, principal):
+ ... return self.privileges.get(principal, 0)
+ ...
+ ... def setBinaryPrivileges(self, principal, privileges):
+ ... if privileges:
+ ... self.privileges[principal] = privileges
+ ... else:
+ ... del self.privileges[principal]
+
+ >>> class Container(MyOb):
+ ... pass
+
+Now we'll create a container, and give it some privileges:
+
+ >>> container = Container()
+ >>> container.setBinaryPrivileges('p1', 31)
+
+Now, if we create a subobject, privileges will be copied from the
+container to the subobject, however, only those privileges that
+pertain to the subobject are copied. This is determined by the
+per-type privilege definitions. Let's define some privileges and set
+which ones apply to our type:
+
+ >>> zc.sharing.sharing.definePrivilege(0, "Share")
+ >>> zc.sharing.sharing.definePrivilege(1, "Work")
+ >>> zc.sharing.sharing.definePrivilege(2, "Play")
+ >>> zc.sharing.sharing.definePrivilege(3, "Read")
+ >>> zc.sharing.sharing.definePrivilege(4, "Write")
+
+We'll define the read, write, and share privileges on the container:
+
+ >>> from zc.sharing import policy
+ >>> policy.sharingPrivileges(Container, ["Read", "Write", "Share"])
+
+And we'll set the work, play, and share privileges on the subobject:
+
+ >>> policy.sharingPrivileges(MyOb, ["Play", "Work", "Share"])
+
+Because the container will contain MyOb instances, we'll define play,
+work, and share as subobject privileges:
+
+ >>> policy.subobjectSharingPrivileges(Container, ["Play", "Work", "Share"])
+
+Now, we'll add an object to the container:
+
+ >>> container.x = MyOb()
+ >>> container.x.__parent__ = MyOb()
+
+The new object has no privileges:
+
+ >>> container.x.getPrincipals()
+ []
+
+We generate an add event:
+
+ >>> from zope.app.container.contained import ObjectAddedEvent
+ >>> event = ObjectAddedEvent(container.x, container, 'x')
+
+Now, we call our adapter:
+
+ >>> adapter = InitialSharing(container.x, event)
+ >>> adapter.share()
+
+And we see that the privileges have been set:
+
+ >>> [(p, container.x.getBinaryPrivileges(p))
+ ... for p in container.x.getPrincipals()]
+ [('p1', 7)]
+
+Note that only the privileges defined for MyOb instances were set.
+
+Privileges are only copied if they were not previously set. Let's
+change the settings on out container:
+
+ >>> container.setBinaryPrivileges('p1', 28)
+ >>> container.setBinaryPrivileges('p2', 31)
+
+Now if we call the share method, there is no effect:
+
+ >>> adapter.share()
+ >>> [(p, container.x.getBinaryPrivileges(p))
+ ... for p in container.x.getPrincipals()]
+ [('p1', 7)]
+
+Unless we remove the settings in our subobject:
+
+ >>> container.x.setBinaryPrivileges('p1', 0)
+ >>> list(container.x.getPrincipals())
+ []
+
+ >>> adapter.share()
+ >>> privs = [(p, container.x.getBinaryPrivileges(p))
+ ... for p in container.x.getPrincipals()]
+ >>> privs.sort()
+ >>> privs
+ [('p1', 4), ('p2', 7)]
+
+If there is an interaction, and if any of the participations are
+requests (iow, if any of the participants are participating by way of
+a UI), then these participats will be given fill access:
+
+ >>> class Principal:
+ ... def __init__(self, id):
+ ... self.id = id
+
+ >>> from zope.publisher.interfaces import IRequest
+ >>> class Request:
+ ... interface.implements(IRequest)
+ ... def __init__(self, principal):
+ ... self.principal = principal
+ ... self.interaction = None
+
+ >>> from zope.security.management import newInteraction
+ >>> newInteraction(Request(Principal('p3')))
+
+ >>> container.x.setBinaryPrivileges('p1', 0)
+ >>> container.x.setBinaryPrivileges('p2', 0)
+ >>> list(container.x.getPrincipals())
+ []
+
+ >>> adapter.share()
+ >>> privs = [(p, container.x.getBinaryPrivileges(p))
+ ... for p in container.x.getPrincipals()]
+ >>> privs.sort()
+ >>> privs
+ [('p1', 4), ('p2', 7), ('p3', 7)]
+
+There's a subscriber for object-added events on sharables that
+looks up and uses the adapter.
+
+ >>> from zope import component
+ >>> component.provideAdapter(InitialSharing)
+
+ >>> for p in container.x.getPrincipals():
+ ... container.x.setBinaryPrivileges(p, 0)
+ >>> list(container.x.getPrincipals())
+ []
+
+ >>> from zc.sharing.sharing import initialSharing
+ >>> initialSharing(container.x, event)
+
+ >>> privs = [(p, container.x.getBinaryPrivileges(p))
+ ... for p in container.x.getPrincipals()]
+ >>> privs.sort()
+ >>> privs
+ [('p1', 4), ('p2', 7), ('p3', 7)]
+
+To implement a different initial sharing policy, simply register a
+different adapter.
+
+
+Sharing Adapter
+---------------
+
+The IBaseSharing interface is the core sharing functionality needed. Relying
+on it, however, requires knowledge of the binary interface and sometimes some
+binary arithmetic that is not at the tip of a Python programmer's mind. The
+ISharing interface extends IBaseSharing and provides a number of conveniences.
+
+The most convenient methods are those that use the titles of the privileges.
+Above, we have already defined five privileges: "Share", at bit position 0;
+"Work", at 1; "Play", at 2; "Read", at 3; and "Write", at 4.
+
+ >>> content = MyContent()
+ >>> basesharing = zc.sharing.sharing.BaseSharing(content)
+ >>> sharing = zc.sharing.sharing.Sharing(basesharing)
+ >>> verifyObject(interfaces.ISharing, sharing)
+ True
+ >>> sharing.getPrivileges('bob')
+ []
+ >>> sharing.setPrivilege('bob', 'Read', True)
+ >>> sharing.getPrivileges('bob')
+ ['Read']
+ >>> sharing.getPrivilege('bob', 'Read')
+ True
+ >>> sharing.getPrivilege('bob', 'Write')
+ False
+ >>> sharing.addPrivileges('bob', ('Write', 'Work'))
+ >>> sharing.getPrivileges('bob') # in bit position order
+ ['Work', 'Read', 'Write']
+ >>> sharing.removePrivileges('bob', ('Share', 'Write'))
+ >>> sharing.getPrivileges('bob')
+ ['Work', 'Read']
+ >>> sharing.setPrivileges('bob', ('Play',))
+ >>> sharing.getPrivileges('bob')
+ ['Play']
+ >>> sharing.setPrivileges('bob', ())
+ >>> sharing.getPrivileges('bob')
+ []
+
+The bit positions are used with the sharedTo method, the method used by the
+security policy. They can also be used to modify privileges. They are called
+'id'.
+
+ >>> sharing.getIdPrivileges('bob')
+ []
+ >>> sharing.setIdPrivilege('bob', 3, True)
+ >>> sharing.getIdPrivileges('bob')
+ [3]
+ >>> sharing.getIdPrivilege('bob', 3)
+ True
+ >>> sharing.getIdPrivilege('bob', 4)
+ False
+ >>> sharing.addIdPrivileges('bob', (4, 1))
+ >>> sharing.getIdPrivileges('bob') # in bit position order
+ [1, 3, 4]
+ >>> sharing.removeIdPrivileges('bob', (0, 4))
+ >>> sharing.getIdPrivileges('bob')
+ [1, 3]
+ >>> sharing.setIdPrivileges('bob', (2,))
+ >>> sharing.getIdPrivileges('bob')
+ [2]
+ >>> sharing.setIdPrivileges('bob', ())
+ >>> sharing.getIdPrivileges('bob')
+ []
+
+ISharing also provides two additional methods to work with the full binary set.
+
+ >>> sharing.getBinaryPrivileges('bob')
+ 0
+ >>> sharing.setBinaryPrivileges('bob', 8)
+ >>> sharing.addBinaryPrivileges('bob', 18)
+ >>> sharing.getBinaryPrivileges('bob')
+ 26
+ >>> sharing.removeBinaryPrivileges('bob', 17)
+ >>> sharing.getBinaryPrivileges('bob')
+ 10
+
+Module Functions
+================
+
+Privileges
+----------
+
+Privileges are simplified permissions. Developers define
+permissions. Site managers define privileges and collect numerous
+permissions into each privilege.
+
+Before a privilege can be used, it must be defined.
+
+ >>> zc.sharing.sharing.clearPrivileges() # clean from previous examples
+ >>> zc.sharing.sharing.getPrivileges()
+ []
+ >>> zc.sharing.sharing.definePrivilege(0, "Read", "Read content")
+ >>> zc.sharing.sharing.definePrivilege(2, "Write", "Write content")
+ >>> zc.sharing.sharing.definePrivilege(
+ ... 4, "Share", "Share content (grant privileges)")
+ >>> import pprint
+ >>> pprint.pprint(zc.sharing.sharing.getPrivileges())
+ [{'info': None, 'description': 'Read content', 'id': 0, 'title': 'Read'},
+ {'info': None, 'description': 'Write content', 'id': 2, 'title': 'Write'},
+ {'description': 'Share content (grant privileges)',
+ 'id': 4,
+ 'info': None,
+ 'title': 'Share'}]
+
+The `definePrivilege` method takes a privilege ID, a title, and a
+description [#i18n]_. The privilege ID should be a small positive
+integer, as it is actually a bit identifier [#bit]_. User privileges
+are stored as bits in a sharing value.
+
+`clearPrivileges` has already been demonstrated: it removes all privilege
+definitions. `getPrivileges` has also already been introduced by example: it
+returns a sequence of information dicts, one for each privilege. The `id`
+is the bit position, and the `title` is the value used for all of the most
+convenient ISharing methods, as illustrated above.
+
+`getPrivilege` returns the information for a given bit positition (`id`):
+
+ >>> pprint.pprint(zc.sharing.sharing.getPrivilege(2))
+ {'info': None, 'description': 'Write content', 'id': 2, 'title': 'Write'}
+
+`getIdByTitle` returns the bit position of a privilege by the title.
+
+ >>> [zc.sharing.sharing.getIdByTitle(t) for t
+ ... in ('Read', 'Write', 'Share')]
+ [0, 2, 4]
+
+`removePrivilege` removes the registration of a single privilege, using its id.
+
+ >>> zc.sharing.sharing.removePrivilege(2)
+
+ >>> pprint.pprint(zc.sharing.sharing.getPrivileges())
+ [{'info': None, 'description': 'Read content', 'id': 0, 'title': 'Read'},
+ {'description': 'Share content (grant privileges)',
+ 'id': 4,
+ 'info': None,
+ 'title': 'Share'}]
+
+
+Other Functions
+---------------
+
+The idsFromSetting and settingFromIds are utilities to convert values from
+sequences of "on" bits--privilege ids--to an integer, and back.
+
+ >>> zc.sharing.sharing.settingFromIds((0, 2, 4, 8)) # 1 + 4 + 16 + 256
+ 277
+ >>> zc.sharing.sharing.idsFromSetting(277)
+ [0, 2, 4, 8]
+
+settingFromTitles converts titles to an int, and titlesFromSetting does the
+reverse.
+
+ >>> zc.sharing.sharing.settingFromTitles(('Read', 'Share'))
+ 17
+ >>> tuple(zc.sharing.sharing.titlesFromSetting(17))
+ ('Read', 'Share')
+
+
+.. [#i18n] The title and descriptions should be message ids, as they
+ may need to be translated.
+
+.. [#bit] Zope Corporation reserves even bits for it's own use. Odd
+ bits are used for customer-specific privileges. (VAR's
+ might then decide to subdivide the odd bits.) XXX Is this still
+ the plan?
Property changes on: zc.sharing/trunk/src/zc/sharing/sharing.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/tests.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/tests.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/tests.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,131 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""
+
+$Id$
+"""
+import unittest
+import zope.event
+from zope.app.tests import placelesssetup
+from zope.configuration import xmlconfig
+from zc.sharing import policy
+import zc.sharing
+import zc.sharing.sharing
+import zope.app.security
+from zope.app.tests import ztapi
+import zope.app.annotation.interfaces
+import zope.app.annotation.attribute
+from zope.testing import module
+import zope.security.management
+
+def zcml(s):
+ context = xmlconfig.file('meta.zcml', package=zope.app.security)
+ context = xmlconfig.file('meta.zcml', context=context,
+ package=zc.sharing)
+ xmlconfig.string(s, context)
+
+def zcmlSetUp(test):
+ placelesssetup.setUp()
+ module.setUp(test, 'zc.sharing.zcml_text')
+ test.globs['__old'] = policy.systemAdministrators
+
+def zcmlTearDown(test):
+ placelesssetup.tearDown()
+ zc.sharing.sharing.clearPrivileges()
+ module.tearDown(test, 'zc.sharing.zcml_text')
+ policy.systemAdministrators = test.globs['__old']
+
+def setUpSharing(test):
+ placelesssetup.setUp()
+ module.setUp(test, 'zc.sharing.SHARING')
+ ztapi.provideAdapter(
+ [zope.app.annotation.interfaces.IAttributeAnnotatable],
+ zope.app.annotation.interfaces.IAnnotations,
+ zope.app.annotation.attribute.AttributeAnnotations,
+ )
+ events = test.globs['events'] = []
+ zope.event.subscribers.append(events.append)
+ zope.security.management.endInteraction()
+
+def tearDownSharing(test):
+ placelesssetup.tearDown()
+ module.tearDown(test, 'zc.sharing.SHARING')
+ removed = zope.event.subscribers.pop()
+ assert test.globs['events'] is removed.__self__
+ del test.globs['events'] # just to be sure
+ zc.sharing.sharing.clearPrivileges()
+
+def tearDownIndex(test):
+ placelesssetup.tearDown()
+ zc.sharing.sharing.clearPrivileges()
+
+def make_sure_sharing_uses_instance():
+ """
+ >>> import zope.interface
+ >>> from zc.sharing import interfaces
+
+ >>> class MyContent:
+ ... zope.interface.implements(interfaces.ISharable)
+
+ >>> import zc.sharing.sharing
+ >>> content = MyContent()
+ >>> basesharing = zc.sharing.sharing.BaseSharing(content)
+ >>> sharing = zc.sharing.sharing.Sharing(basesharing)
+
+ >>> sharing.setBinaryPrivileges('bob', 21)
+
+ >>> from zope.app.annotation.interfaces import IAnnotations
+ >>> annotations = IAnnotations(content)
+ >>> annotations[zc.sharing.sharing.key].__class__
+ <class 'zc.sharing.sharing.SharingData'>
+
+ """
+
+def setUpPolicy(test):
+ placelesssetup.setUp()
+ test.globs['__old'] = policy.systemAdministrators
+
+def tearDownPolicy(test):
+ placelesssetup.tearDown()
+ policy.systemAdministrators = test.globs['__old']
+ zc.sharing.sharing.clearPrivileges()
+
+def test_suite():
+ from zope.testing import doctest
+ return unittest.TestSuite((
+ doctest.DocFileSuite(
+ 'policy.txt',
+ setUp=setUpPolicy, tearDown=tearDownPolicy),
+ doctest.DocFileSuite(
+ 'zcml.txt', globs={'zcml': zcml},
+ setUp=zcmlSetUp, tearDown=zcmlTearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE,
+ ),
+ doctest.DocFileSuite(
+ 'sharing.txt',
+ setUp=setUpSharing, tearDown=tearDownSharing,
+ ),
+ doctest.DocTestSuite(
+ setUp=setUpSharing, tearDown=tearDownSharing,
+ ),
+ doctest.DocFileSuite(
+ 'index.txt',
+ setUp=placelesssetup.setUp, tearDown=tearDownIndex,
+ ),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
+
Property changes on: zc.sharing/trunk/src/zc/sharing/tests.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/utils.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/utils.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/utils.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,59 @@
+##############################################################################
+#
+# Copyright (c) 2005 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""Sharing utility functions
+
+$Id$
+"""
+from zope.app.location.interfaces import ISublocations
+
+from zc.sharing.interfaces import ISharing
+from zc.sharing.sharing import sharingMask
+
+def shareAll(ob, principal_id):
+ sharing = ISharing(ob)
+ mask = sharingMask(ob)
+ sharing.setBinaryPrivileges(principal_id, mask)
+
+def applyToSubobjects(settings, ob, seen=None, clobber=False):
+ """Apply the given sharing settings to all subobjects of `ob`
+
+ clobber - a boolean, if true no sharing bits will be left enabled for an
+ object unless they exist in that objects sharing mask
+ """
+ if seen is None:
+ seen = {}
+
+ obid = id(ob)
+ if obid in seen:
+ return
+ seen[obid] = ob
+
+ sharing = ISharing(ob, None)
+ if sharing is not None:
+ mask = sharingMask(ob)
+ for principal_id, setting in settings:
+ if not clobber:
+ value = sharing.getBinaryPrivileges(principal_id)
+ # unmasked_value represents all of the bits of value that fall
+ # outside of the mask
+ unmasked_value = (value & mask) ^ value
+ setting |= unmasked_value
+
+ sharing.setBinaryPrivileges(principal_id, setting)
+
+ subs = ISublocations(ob, None)
+ if subs is not None:
+ for sub in subs.sublocations():
+ applyToSubobjects(settings, sub, seen, clobber)
Property changes on: zc.sharing/trunk/src/zc/sharing/utils.py
___________________________________________________________________
Name: svn:executable
+
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/zcml.py
===================================================================
--- zc.sharing/trunk/src/zc/sharing/zcml.py 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/zcml.py 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,134 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation. All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Visible Source
+# License, Version 1.0 (ZVSL). A copy of the ZVSL should accompany this
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE
+#
+##############################################################################
+"""ZCML directives for defining privileges.
+
+$Id$
+"""
+
+from types import ClassType
+
+from zope import component, interface, schema
+import zope.configuration.fields
+import zope.app.security.fields
+
+from zc.sharing import policy, sharing, interfaces
+
+class IdefinePrivilege(interface.Interface):
+
+ bit = schema.Int(
+ title=u"Privilege bit",
+ description=(u"The privilege ID should be a small positive "
+ u"integer, as it is actually a bit "
+ u"identifier. User privileges are stored as bits "
+ u"in a sharing value."),
+ )
+
+ title = zope.configuration.fields.MessageID(
+ title=u"Title",
+ description=u"A user-friendly name for the privilege",
+ )
+
+ description = zope.configuration.fields.MessageID(
+ title=u"Description",
+ description=(u"A description of the privilege, including "
+ u"the capabilities the privilege provides"),
+ required=False,
+ )
+
+def definePrivilege(_context, bit, title, description=''):
+ _context.action(
+ discriminator=('zc.intranet:privilege', bit),
+ callable=sharing.definePrivilege,
+ args=(bit, title, description, _context.info),
+ )
+
+class IpermissionPrivilege(interface.Interface):
+
+ permission = zope.app.security.fields.Permission(
+ title=u"Permission",
+ description=u"Permission for which a privilege is being defined",
+ )
+
+ privilege = schema.Int(
+ title=u"Privilege",
+ description=u"The privilege ID that provides the permission",
+ )
+
+def checkPrivilege(privilege):
+ if sharing.getPrivilege(privilege, -1) < 0:
+ raise ValueError("Undefined privilege", privilege)
+
+def permissionPrivilege(_context, permission, privilege):
+ _context.action(
+ discriminator=None,
+ callable=checkPrivilege,
+ args=(privilege, )
+ )
+ _context.action(
+ discriminator=('zc:permissionPrivilege', permission),
+ callable=policy.permissionPrivilege,
+ args=(permission, privilege),
+ )
+
+class Iprivileges(interface.Interface):
+
+ for_ = zope.configuration.fields.GlobalObject(
+ title=u"Type the privileges are used for"
+ )
+
+ titles = zope.configuration.fields.Tokens(
+ title=u"Privileges",
+ description=u"List of privilege titles",
+ value_type=schema.TextLine(),
+ )
+
+for_types = type(None), type, type(interface.Interface), ClassType
+def privileges(_context, for_, titles):
+
+ if not isinstance(for_, for_types):
+ raise TypeError("Invalid type for for", type(for_))
+ _context.action(
+ discriminator=('zc:privileges', for_),
+ callable=policy.sharingPrivileges,
+ args=(for_, titles),
+ )
+
+def subobjectPrivileges(_context, for_, titles):
+
+ if not isinstance(for_, for_types):
+ raise TypeError("Invalid type for for", type(for_))
+ _context.action(
+ discriminator=('zc:subobjectPrivileges', for_),
+ callable=policy.subobjectSharingPrivileges,
+ args=(for_, titles),
+ )
+
+class IsystemAdministrators(interface.Interface):
+
+ principals = zope.configuration.fields.Tokens(
+ title=u"Principals",
+ description=u"System administrator principal ids",
+ value_type=schema.TextLine(),
+ )
+
+def setSystemAdministrators(principals):
+ policy.systemAdministrators = principals
+
+def systemAdministrators(_context, principals):
+ _context.action(
+ discriminator='zc:sysAdmins',
+ callable=setSystemAdministrators,
+ args=(tuple(principals), )
+ )
Property changes on: zc.sharing/trunk/src/zc/sharing/zcml.py
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Added: zc.sharing/trunk/src/zc/sharing/zcml.txt
===================================================================
--- zc.sharing/trunk/src/zc/sharing/zcml.txt 2006-03-01 18:36:57 UTC (rev 65677)
+++ zc.sharing/trunk/src/zc/sharing/zcml.txt 2006-03-01 19:14:48 UTC (rev 65678)
@@ -0,0 +1,243 @@
+Sharing Configuration directives
+================================
+
+This package provides several ZCML configuration directives for setting up the
+sharing security model.
+
+Defining privileges
+-------------------
+
+To define a privilege, use the privilege directive:
+
+ >>> zcml("""
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:zc="http://namespaces.zope.com/zc"
+ ... i18n_domain="test"
+ ... >
+ ...
+ ... <zc:privilege bit="0" title="Read" />
+ ... <zc:privilege bit="2" title="Write"
+ ... description="Modify content" />
+ ... <zc:privilege bit="4" title="Share" />
+ ... </configure>
+ ... """)
+
+Now, having defined these, we can get privilege definitions:
+
+ >>> import zc.sharing.sharing
+ >>> from zope.testing.doctestunit import pprint
+ >>> pprint(zc.sharing.sharing.getPrivilege(0))
+ {'description': '',
+ 'id': 0,
+ 'info': File "<string>", line 8.5-8.42,
+ 'title': u'Read'}
+
+ >>> pprint(zc.sharing.sharing.getPrivilege(2))
+ {'description': u'Modify content',
+ 'id': 2,
+ 'info': File "<string>", line 9.5-10.51,
+ 'title': u'Write'}
+
+ >>> zc.sharing.sharing.getPrivilege(2)['title'].domain
+ 'test'
+
+
+It's an error to try to define the same privilege more than once:
+
+ >>> zcml("""
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:zc="http://namespaces.zope.com/zc"
+ ... i18n_domain="test"
+ ... >
+ ...
+ ... <zc:privilege bit="6" title="Share" />
+ ... <zc:privilege bit="6" title="Write"
+ ... description="Modify content" />
+ ... </configure>
+ ... """)
+ Traceback (most recent call last):
+ ...
+ ConfigurationConflictError: Conflicting configuration actions
+ For: ('zc.intranet:privilege', 6)
+ File "<string>", line 8.5-8.43
+ Could not read source.
+ File "<string>", line 9.5-10.51
+ Could not read source.
+
+Associating privileges with permissions
+---------------------------------------
+
+Having defined a privilege, we can associate one or more permissions
+with it. Note that we have to define the permissions before we can associate
+them with privileges:
+
+ >>> zcml("""
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:zc="http://namespaces.zope.com/zc"
+ ... i18n_domain="test"
+ ... >
+ ...
+ ... <zc:permissionPrivilege permission="foo.p1" privilege="0" />
+ ... </configure>
+ ... """)
+ Traceback (most recent call last):
+ ...
+ ConfigurationExecutionError: exceptions.ValueError:
+ ('Undefined permission id', 'foo.p1')
+ in:
+ File "<string>", line 8.5-8.65
+ Could not read source.
+
+ >>> zcml("""
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:zc="http://namespaces.zope.com/zc"
+ ... i18n_domain="test"
+ ... >
+ ... <permission id="foo.p1" title="Permission 1" />
+ ... <zc:permissionPrivilege permission="foo.p1" privilege="0" />
+ ... </configure>
+ ... """)
+
+ >>> from zc.sharing import policy
+ >>> policy.getPermissionPrivilege("foo.p1")
+ 0
+
+Of course, if you don't define a privilege before you use it, you'll
+get an error:
+
+ >>> zcml("""
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:zc="http://namespaces.zope.com/zc"
+ ... i18n_domain="test"
+ ... >
+ ... <permission id="foo.p2" title="Permission 1" />
+ ... <zc:permissionPrivilege permission="foo.p2" privilege="10" />
+ ... </configure>
+ ... """)
+ Traceback (most recent call last):
+ ...
+ ConfigurationExecutionError: exceptions.ValueError:
+ ('Undefined privilege', 10)
+ in:
+ File "<string>", line 8.5-8.66
+ Could not read source.
+
+Associating privileges with content
+-----------------------------------
+
+Different content types can have different privileges. We use the
+privileges directive to define privileges for a type. We can define
+default privileges:
+
+ >>> zcml("""
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:zc="http://namespaces.zope.com/zc"
+ ... i18n_domain="test"
+ ... >
+ ... <zc:privileges for="*" titles="Read Write Share" />
+ ... </configure>
+ ... """)
+
+
+Now, we'll define a new content type:
+
+ >>> from zope import interface
+ >>> class IMyContent(interface.Interface):
+ ... "sample type"
+ >>> class MyContent:
+ ... interface.implements(IMyContent)
+
+We can find out what privileges it has by adapting it to
+ISharingPrivileges:
+
+ >>> from zc.sharing import interfaces
+ >>> interfaces.ISharingPrivileges(MyContent()).privileges
+ (0, 2, 4)
+
+We may want out content type to have different privileges. If so, we
+simply use a specific interface in the configuration directive:
+
+ >>> zcml("""
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:zc="http://namespaces.zope.com/zc"
+ ... i18n_domain="test"
+ ... >
+ ... <zc:privileges for="zc.sharing.zcml_text.IMyContent"
+ ... titles="Read Share" />
+ ... </configure>
+ ... """)
+
+ >>> interfaces.ISharingPrivileges(MyContent()).privileges
+ (0, 4)
+
+For containers, we can privileges that may apply to subobjects. On
+container sharing tabs, we need to include privileges that apply to
+subobjects as well as privileges that apply to the container. This is
+necessary so that we can create settings on a container and duplicate
+them on subobjects, whether explicitly in the sharing tab, or when
+objects are added. To specify subobject privileges, use the
+subobjectPrivileges directive:
+
+ >>> zcml("""
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:zc="http://namespaces.zope.com/zc"
+ ... i18n_domain="test"
+ ... >
+ ... <zc:subobjectPrivileges
+ ... for="zc.sharing.zcml_text.IMyContent"
+ ... titles="Write Read" />
+ ... </configure>
+ ... """)
+
+We can find out what these settings are by adapting an object
+to ISubobjectSharingPrivileges:
+
+ >>> interfaces.ISubobjectSharingPrivileges(MyContent()).subobjectPrivileges
+ (2, 0)
+
+Defining system adminstrators
+-----------------------------
+
+System adminstrators are defined using the systemAdministrators
+directive:
+
+ >>> zcml("""
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:zc="http://namespaces.zope.com/zc"
+ ... >
+ ... <zc:systemAdministrators principals="sally bob" />
+ ... </configure>
+ ... """)
+
+ >>> policy.systemAdministrators
+ (u'sally', u'bob')
+
+It is an error to use the directive more than once:
+
+ >>> zcml("""
+ ... <configure
+ ... xmlns="http://namespaces.zope.org/zope"
+ ... xmlns:zc="http://namespaces.zope.com/zc"
+ ... >
+ ... <zc:systemAdministrators principals="sally bob" />
+ ... <zc:systemAdministrators principals="ted mary sam" />
+ ... </configure>
+ ... """)
+ Traceback (most recent call last):
+ ...
+ ConfigurationConflictError: Conflicting configuration actions
+ For: zc:sysAdmins
+ File "<string>", line 6.5-6.55
+ Could not read source.
+ File "<string>", line 7.5-7.58
+ Could not read source.
+
Property changes on: zc.sharing/trunk/src/zc/sharing/zcml.txt
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Zope-CVS
mailing list