Commit 160df616 authored by Christian Theune's avatar Christian Theune

Moving code to satellite.

parent df23e50e
# Extension information for zpkg:
<extension _zope_app_container_contained>
source _zope_app_container_contained.c
</extension>
#
# This file is necessary to make this directory a package.
/*############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
#define _ZOPE_APP_CONTAINER_CONTAINED_C "$Id$\n"
/* Contained Proxy Base class
Contained proxies provide __parent__ and __name__ attributes for
objects without them.
There is something strange and, possibly cool, going on here, wrt
persistence. To reuse the base proxy implementation we don't treat
the proxied object as part of the persistent state of the proxy.
This means that the proxy still operates as a proxy even if it is a
ghost.
The proxy will only be unghostified if you need to access one of the
attributes provided by the proxy.
*/
#include "Python.h"
#include "persistent/cPersistence.h"
static PyObject *str_p_deactivate;
typedef struct {
cPersistent_HEAD
PyObject *po_weaklist;
PyObject *proxy_object;
PyObject *__parent__;
PyObject *__name__;
} ProxyObject;
typedef struct {
PyTypeObject *proxytype;
int (*check)(PyObject *obj);
PyObject *(*create)(PyObject *obj);
PyObject *(*getobject)(PyObject *proxy);
} ProxyInterface;
#define OBJECT(O) ((PyObject*)(O))
#define Proxy_GET_OBJECT(ob) (((ProxyObject *)(ob))->proxy_object)
#define CLEAR(O) \
if (O) {PyObject *clr__tmp = O; O = NULL; Py_DECREF(clr__tmp); }
/* Supress inclusion of the original proxy.h */
#define _proxy_H_ 1
/* Incude the proxy C source */
#include "_zope_proxy_proxy.c"
#define SPECIAL(NAME) ( \
*(NAME) == '_' && \
(((NAME)[1] == 'p' && (NAME)[2] == '_') \
|| \
((NAME)[1] == '_' && ( \
strcmp((NAME), "__parent__") == 0 \
|| \
strcmp((NAME), "__name__") == 0 \
|| \
strcmp((NAME), "__getstate__") == 0 \
|| \
strcmp((NAME), "__setstate__") == 0 \
|| \
strcmp((NAME), "__getnewargs__") == 0 \
|| \
strcmp((NAME), "__reduce__") == 0 \
|| \
strcmp((NAME), "__reduce_ex__") == 0 \
)) \
))
static PyObject *
CP_getattro(PyObject *self, PyObject *name)
{
char *cname;
cname = PyString_AsString(name);
if (cname == NULL)
return NULL;
if (SPECIAL(cname))
/* delegate to persistent */
return cPersistenceCAPI->pertype->tp_getattro(self, name);
/* Use the wrapper version to delegate */
return wrap_getattro(self, name);
}
static int
CP_setattro(PyObject *self, PyObject *name, PyObject *v)
{
char *cname;
cname = PyString_AsString(name);
if (cname == NULL)
return -1;
if (SPECIAL(cname))
/* delegate to persistent */
return cPersistenceCAPI->pertype->tp_setattro(self, name, v);
/* Use the wrapper version to delegate */
return wrap_setattro(self, name, v);
}
static PyObject *
CP_getstate(ProxyObject *self)
{
return Py_BuildValue("OO",
self->__parent__ ? self->__parent__ : Py_None,
self->__name__ ? self->__name__ : Py_None
);
}
static PyObject *
CP_getnewargs(ProxyObject *self)
{
return Py_BuildValue("(O)", self->proxy_object);
}
static PyObject *
CP_setstate(ProxyObject *self, PyObject *state)
{
PyObject *parent, *name;
if(! PyArg_ParseTuple(state, "OO", &parent, &name))
return NULL;
CLEAR(self->__parent__);
CLEAR(self->__name__);
Py_INCREF(parent);
Py_INCREF(name);
self->__parent__ = parent;
self->__name__ = name;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
CP_reduce(ProxyObject *self)
{
PyObject *result;
if (! PER_USE(self))
return NULL;
result = Py_BuildValue("O(O)(OO)",
self->ob_type,
self->proxy_object,
self->__parent__ ? self->__parent__ : Py_None,
self->__name__ ? self->__name__ : Py_None
);
PER_ALLOW_DEACTIVATION(self);
return result;
}
static PyObject *
CP_reduce_ex(ProxyObject *self, PyObject *proto)
{
return CP_reduce(self);
}
static PyObject *
CP__p_deactivate(ProxyObject *self)
{
PyObject *result;
result = PyObject_CallMethodObjArgs(OBJECT(cPersistenceCAPI->pertype),
str_p_deactivate,
self, NULL);
if (result == NULL)
return NULL;
if (self->jar && self->oid && self->state == cPersistent_UPTODATE_STATE)
{
Py_XDECREF(self->__parent__);
self->__parent__ = NULL;
Py_XDECREF(self->__name__);
self->__name__ = NULL;
}
return result;
}
static PyMethodDef
CP_methods[] = {
{"__getstate__", (PyCFunction)CP_getstate, METH_NOARGS,
"Get the object state"},
{"__setstate__", (PyCFunction)CP_setstate, METH_O,
"Set the object state"},
{"__getnewargs__", (PyCFunction)CP_getnewargs, METH_NOARGS,
"Get the arguments that must be passed to __new__"},
{"__reduce__", (PyCFunction)CP_reduce, METH_NOARGS,
"Reduce the object to constituent parts."},
{"__reduce_ex__", (PyCFunction)CP_reduce_ex, METH_O,
"Reduce the object to constituent parts."},
{"_p_deactivate", (PyCFunction)CP__p_deactivate, METH_NOARGS,
"Deactivate the object."},
{NULL, NULL},
};
/* Code to access structure members by accessing attributes */
#include "structmember.h"
static PyMemberDef CP_members[] = {
{"__parent__", T_OBJECT, offsetof(ProxyObject, __parent__)},
{"__name__", T_OBJECT, offsetof(ProxyObject, __name__)},
{NULL} /* Sentinel */
};
static int
CP_traverse(ProxyObject *self, visitproc visit, void *arg)
{
if (cPersistenceCAPI->pertype->tp_traverse((PyObject *)self, visit, arg) < 0)
return -1;
if (self->proxy_object != NULL && visit(self->proxy_object, arg) < 0)
return -1;
if (self->__parent__ != NULL && visit(self->__parent__, arg) < 0)
return -1;
if (self->__name__ != NULL && visit(self->__name__, arg) < 0)
return -1;
return 0;
}
static int
CP_clear(ProxyObject *self)
{
/* Drop references that may have created reference
cycles. Immutable objects do not have to define this method
since they can never directly create reference cycles. Note
that the object must still be valid after calling this
method (don't just call Py_DECREF() on a reference). The
collector will call this method if it detects that this
object is involved in a reference cycle.
*/
if (cPersistenceCAPI->pertype->tp_clear != NULL)
cPersistenceCAPI->pertype->tp_clear((PyObject*)self);
CLEAR(self->proxy_object);
CLEAR(self->__parent__);
CLEAR(self->__name__);
return 0;
}
static void
CP_dealloc(ProxyObject *self)
{
if (self->po_weaklist != NULL)
PyObject_ClearWeakRefs((PyObject *)self);
CLEAR(self->proxy_object);
CLEAR(self->__parent__);
CLEAR(self->__name__);
cPersistenceCAPI->pertype->tp_dealloc((PyObject*)self);
}
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
init_zope_app_container_contained(void)
{
PyObject *m;
str_p_deactivate = PyString_FromString("_p_deactivate");
if (str_p_deactivate == NULL)
return;
/* Try to fake out compiler nag function */
if (0) init_zope_proxy_proxy();
m = Py_InitModule3("_zope_app_container_contained",
module_functions, module___doc__);
if (m == NULL)
return;
if (empty_tuple == NULL)
empty_tuple = PyTuple_New(0);
/* Initialize the PyPersist_C_API and the type objects. */
cPersistenceCAPI = PyCObject_Import("persistent.cPersistence", "CAPI");
if (cPersistenceCAPI == NULL)
return;
ProxyType.tp_name = "zope.app.container.contained.ContainedProxyBase";
ProxyType.ob_type = &PyType_Type;
ProxyType.tp_base = cPersistenceCAPI->pertype;
ProxyType.tp_getattro = CP_getattro;
ProxyType.tp_setattro = CP_setattro;
ProxyType.tp_members = CP_members;
ProxyType.tp_methods = CP_methods;
ProxyType.tp_traverse = (traverseproc) CP_traverse;
ProxyType.tp_clear = (inquiry) CP_clear;
ProxyType.tp_dealloc = (destructor) CP_dealloc;
ProxyType.tp_weaklistoffset = offsetof(ProxyObject, po_weaklist);
if (PyType_Ready(&ProxyType) < 0)
return;
Py_INCREF(&ProxyType);
PyModule_AddObject(m, "ContainedProxyBase", (PyObject *)&ProxyType);
}
/*############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
/*
* This file is also used as a really extensive macro in
* ../app/container/_zope_app_container_contained.c. If you need to
* change this file, you need to "svn copy" it to ../app/container/.
*
* This approach is taken to allow the sources for the two packages
* to be compilable when the relative locations of these aren't
* related in the same way as they are in a checkout.
*
* This will be revisited in the future, but works for now.
*/
#include "Python.h"
#include "modsupport.h"
#define PROXY_MODULE
#include "zope.proxy/proxy.h"
static PyTypeObject ProxyType;
#define Proxy_Check(wrapper) (PyObject_TypeCheck((wrapper), &ProxyType))
static PyObject *
empty_tuple = NULL;
/*
* Slot methods.
*/
static PyObject *
wrap_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyObject *result = NULL;
PyObject *object;
if (PyArg_UnpackTuple(args, "__new__", 1, 1, &object)) {
if (kwds != NULL && PyDict_Size(kwds) != 0) {
PyErr_SetString(PyExc_TypeError,
"proxy.__new__ does not accept keyword args");
return NULL;
}
result = PyType_GenericNew(type, args, kwds);
if (result != NULL) {
ProxyObject *wrapper = (ProxyObject *) result;
Py_INCREF(object);
wrapper->proxy_object = object;
}
}
return result;
}
static int
wrap_init(PyObject *self, PyObject *args, PyObject *kwds)
{
int result = -1;
PyObject *object;
if (PyArg_UnpackTuple(args, "__init__", 1, 1, &object)) {
ProxyObject *wrapper = (ProxyObject *)self;
if (kwds != NULL && PyDict_Size(kwds) != 0) {
PyErr_SetString(PyExc_TypeError,
"proxy.__init__ does not accept keyword args");
return -1;
}
/* If the object in this proxy is not the one we
* received in args, replace it with the new one.
*/
if (wrapper->proxy_object != object) {
PyObject *temp = wrapper->proxy_object;
Py_INCREF(object);
wrapper->proxy_object = object;
Py_DECREF(temp);
}
result = 0;
}
return result;
}
static int
wrap_traverse(PyObject *self, visitproc visit, void *arg)
{
PyObject *ob = Proxy_GET_OBJECT(self);
if (ob != NULL)
return visit(ob, arg);
else
return 0;
}
static int
wrap_clear(PyObject *self)
{
ProxyObject *proxy = (ProxyObject *)self;
PyObject *temp = proxy->proxy_object;
if (temp != NULL) {
proxy->proxy_object = NULL;
Py_DECREF(temp);
}
return 0;
}
static PyObject *
wrap_richcompare(PyObject* self, PyObject* other, int op)
{
if (Proxy_Check(self)) {
self = Proxy_GET_OBJECT(self);
}
else {
other = Proxy_GET_OBJECT(other);
}
return PyObject_RichCompare(self, other, op);
}
static PyObject *
wrap_iter(PyObject *self)
{
return PyObject_GetIter(Proxy_GET_OBJECT(self));
}
static PyObject *
wrap_iternext(PyObject *self)
{
return PyIter_Next(Proxy_GET_OBJECT(self));
}
static void
wrap_dealloc(PyObject *self)
{
(void) wrap_clear(self);
self->ob_type->tp_free(self);
}
/* A variant of _PyType_Lookup that doesn't look in ProxyType.
*
* If argument search_wrappertype is nonzero, we can look in WrapperType.
*/
PyObject *
WrapperType_Lookup(PyTypeObject *type, PyObject *name)
{
int i, n;
PyObject *mro, *res, *base, *dict;
/* Look in tp_dict of types in MRO */
mro = type->tp_mro;
/* If mro is NULL, the type is either not yet initialized
by PyType_Ready(), or already cleared by type_clear().
Either way the safest thing to do is to return NULL. */
if (mro == NULL)
return NULL;
assert(PyTuple_Check(mro));
n = PyTuple_GET_SIZE(mro)
- 1; /* We don't want to look at the last item, which is object. */
for (i = 0; i < n; i++) {
base = PyTuple_GET_ITEM(mro, i);
if (((PyTypeObject *)base) != &ProxyType) {
if (PyClass_Check(base))
dict = ((PyClassObject *)base)->cl_dict;
else {
assert(PyType_Check(base));
dict = ((PyTypeObject *)base)->tp_dict;
}
assert(dict && PyDict_Check(dict));
res = PyDict_GetItem(dict, name);
if (res != NULL)
return res;
}
}
return NULL;
}
static PyObject *
wrap_getattro(PyObject *self, PyObject *name)
{
PyObject *wrapped;
PyObject *descriptor;
PyObject *res = NULL;
char *name_as_string;
int maybe_special_name;
#ifdef Py_USING_UNICODE
/* The Unicode to string conversion is done here because the
existing tp_getattro slots expect a string object as name
and we wouldn't want to break those. */
if (PyUnicode_Check(name)) {
name = PyUnicode_AsEncodedString(name, NULL, NULL);
if (name == NULL)
return NULL;
}
else
#endif
if (!PyString_Check(name)){
PyErr_SetString(PyExc_TypeError, "attribute name must be string");
return NULL;
}
else
Py_INCREF(name);
name_as_string = PyString_AS_STRING(name);
wrapped = Proxy_GET_OBJECT(self);
if (wrapped == NULL) {
PyErr_Format(PyExc_RuntimeError,
"object is NULL; requested to get attribute '%s'",
name_as_string);
goto finally;
}
maybe_special_name = name_as_string[0] == '_' && name_as_string[1] == '_';
if (!(maybe_special_name && strcmp(name_as_string, "__class__") == 0)) {
descriptor = WrapperType_Lookup(self->ob_type, name);
if (descriptor != NULL) {
if (PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS)
&& descriptor->ob_type->tp_descr_get != NULL) {
res = descriptor->ob_type->tp_descr_get(
descriptor,
self,
(PyObject *)self->ob_type);
} else {
Py_INCREF(descriptor);
res = descriptor;
}
goto finally;
}
}
res = PyObject_GetAttr(wrapped, name);
finally:
Py_DECREF(name);
return res;
}
static int
wrap_setattro(PyObject *self, PyObject *name, PyObject *value)
{
PyObject *wrapped;
PyObject *descriptor;
int res = -1;
#ifdef Py_USING_UNICODE
/* The Unicode to string conversion is done here because the
existing tp_setattro slots expect a string object as name
and we wouldn't want to break those. */
if (PyUnicode_Check(name)) {
name = PyUnicode_AsEncodedString(name, NULL, NULL);
if (name == NULL)
return -1;
}
else
#endif
if (!PyString_Check(name)){
PyErr_SetString(PyExc_TypeError, "attribute name must be string");
return -1;
}
else
Py_INCREF(name);
descriptor = WrapperType_Lookup(self->ob_type, name);
if (descriptor != NULL) {
if (PyType_HasFeature(descriptor->ob_type, Py_TPFLAGS_HAVE_CLASS) &&
descriptor->ob_type->tp_descr_set != NULL) {
res = descriptor->ob_type->tp_descr_set(descriptor, self, value);
} else {
PyErr_Format(PyExc_TypeError,
"Tried to set attribute '%s' on wrapper, but it is not"
" a data descriptor", PyString_AS_STRING(name));
}
goto finally;
}
wrapped = Proxy_GET_OBJECT(self);
if (wrapped == NULL) {
PyErr_Format(PyExc_RuntimeError,
"object is NULL; requested to set attribute '%s'",
PyString_AS_STRING(name));
goto finally;
}
res = PyObject_SetAttr(wrapped, name, value);
finally:
Py_DECREF(name);
return res;
}
static int
wrap_print(PyObject *wrapper, FILE *fp, int flags)
{
return PyObject_Print(Proxy_GET_OBJECT(wrapper), fp, flags);
}
static PyObject *
wrap_str(PyObject *wrapper) {
return PyObject_Str(Proxy_GET_OBJECT(wrapper));
}
static PyObject *
wrap_repr(PyObject *wrapper)
{
return PyObject_Repr(Proxy_GET_OBJECT(wrapper));
}
static int
wrap_compare(PyObject *wrapper, PyObject *v)
{
return PyObject_Compare(Proxy_GET_OBJECT(wrapper), v);
}
static long
wrap_hash(PyObject *self)
{
return PyObject_Hash(Proxy_GET_OBJECT(self));
}
static PyObject *
wrap_call(PyObject *self, PyObject *args, PyObject *kw)
{
if (kw)
return PyEval_CallObjectWithKeywords(Proxy_GET_OBJECT(self),
args, kw);
else
return PyObject_CallObject(Proxy_GET_OBJECT(self), args);
}
/*
* Number methods
*/
/*
* Number methods.
*/
static PyObject *
call_int(PyObject *self)
{
PyNumberMethods *nb = self->ob_type->tp_as_number;
if (nb == NULL || nb->nb_int == NULL) {
PyErr_SetString(PyExc_TypeError,
"object can't be converted to int");
return NULL;
}
return nb->nb_int(self);
}
static PyObject *
call_long(PyObject *self)
{
PyNumberMethods *nb = self->ob_type->tp_as_number;
if (nb == NULL || nb->nb_long == NULL) {
PyErr_SetString(PyExc_TypeError,
"object can't be converted to long");
return NULL;
}
return nb->nb_long(self);
}
static PyObject *
call_float(PyObject *self)
{
PyNumberMethods *nb = self->ob_type->tp_as_number;
if (nb == NULL || nb->nb_float== NULL) {
PyErr_SetString(PyExc_TypeError,
"object can't be converted to float");
return NULL;
}
return nb->nb_float(self);
}
static PyObject *
call_oct(PyObject *self)
{
PyNumberMethods *nb = self->ob_type->tp_as_number;
if (nb == NULL || nb->nb_oct== NULL) {
PyErr_SetString(PyExc_TypeError,
"object can't be converted to oct");
return NULL;
}
return nb->nb_oct(self);
}
static PyObject *
call_hex(PyObject *self)
{
PyNumberMethods *nb = self->ob_type->tp_as_number;
if (nb == NULL || nb->nb_hex == NULL) {
PyErr_SetString(PyExc_TypeError,
"object can't be converted to hex");
return NULL;
}
return nb->nb_hex(self);
}
static PyObject *
call_ipow(PyObject *self, PyObject *other)
{
/* PyNumber_InPlacePower has three args. How silly. :-) */
return PyNumber_InPlacePower(self, other, Py_None);
}
typedef PyObject *(*function1)(PyObject *);
static PyObject *
check1(ProxyObject *self, char *opname, function1 operation)
{
PyObject *result = NULL;
result = operation(Proxy_GET_OBJECT(self));
#if 0
if (result != NULL)
/* ??? create proxy for result? */
;
#endif
return result;
}
static PyObject *
check2(PyObject *self, PyObject *other,
char *opname, char *ropname, binaryfunc operation)
{
PyObject *result = NULL;
PyObject *object;
if (Proxy_Check(self)) {
object = Proxy_GET_OBJECT(self);
result = operation(object, other);
}
else if (Proxy_Check(other)) {
object = Proxy_GET_OBJECT(other);
result = operation(self, object);
}
else {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
#if 0
if (result != NULL)
/* ??? create proxy for result? */
;
#endif
return result;
}
static PyObject *
check2i(ProxyObject *self, PyObject *other,
char *opname, binaryfunc operation)
{
PyObject *result = NULL;
PyObject *object = Proxy_GET_OBJECT(self);
result = operation(object, other);
if (result == object) {
/* If the operation was really carried out inplace,
don't create a new proxy, but use the old one. */
Py_INCREF(self);
Py_DECREF(object);
result = (PyObject *)self;
}
#if 0
else if (result != NULL)
/* ??? create proxy for result? */
;
#endif
return result;
}
#define UNOP(NAME, CALL) \
static PyObject *wrap_##NAME(PyObject *self) \
{ return check1((ProxyObject *)self, "__"#NAME"__", CALL); }
#define BINOP(NAME, CALL) \
static PyObject *wrap_##NAME(PyObject *self, PyObject *other) \
{ return check2(self, other, "__"#NAME"__", "__r"#NAME"__", CALL); }
#define INPLACE(NAME, CALL) \
static PyObject *wrap_i##NAME(PyObject *self, PyObject *other) \
{ return check2i((ProxyObject *)self, other, "__i"#NAME"__", CALL); }
BINOP(add, PyNumber_Add)
BINOP(sub, PyNumber_Subtract)
BINOP(mul, PyNumber_Multiply)
BINOP(div, PyNumber_Divide)
BINOP(mod, PyNumber_Remainder)
BINOP(divmod, PyNumber_Divmod)
static PyObject *
wrap_pow(PyObject *self, PyObject *other, PyObject *modulus)
{
PyObject *result = NULL;
PyObject *object;
if (Proxy_Check(self)) {
object = Proxy_GET_OBJECT(self);
result = PyNumber_Power(object, other, modulus);
}
else if (Proxy_Check(other)) {
object = Proxy_GET_OBJECT(other);
result = PyNumber_Power(self, object, modulus);
}
else if (modulus != NULL && Proxy_Check(modulus)) {
object = Proxy_GET_OBJECT(modulus);
result = PyNumber_Power(self, other, modulus);
}
else {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
return result;
}
BINOP(lshift, PyNumber_Lshift)
BINOP(rshift, PyNumber_Rshift)
BINOP(and, PyNumber_And)
BINOP(xor, PyNumber_Xor)
BINOP(or, PyNumber_Or)
static int
wrap_coerce(PyObject **p_self, PyObject **p_other)
{
PyObject *self = *p_self;
PyObject *other = *p_other;
PyObject *object;
PyObject *left;
PyObject *right;
int r;
assert(Proxy_Check(self));
object = Proxy_GET_OBJECT(self);
left = object;
right = other;
r = PyNumber_CoerceEx(&left, &right);
if (r != 0)
return r;
/* Now left and right have been INCREF'ed. Any new value that
comes out is proxied; any unchanged value is left unchanged. */
if (left == object) {
/* Keep the old proxy */
Py_INCREF(self);
Py_DECREF(left);
left = self;
}
#if 0
else {
/* ??? create proxy for left? */
}
if (right != other) {
/* ??? create proxy for right? */
}
#endif
*p_self = left;
*p_other = right;
return 0;
}
UNOP(neg, PyNumber_Negative)
UNOP(pos, PyNumber_Positive)
UNOP(abs, PyNumber_Absolute)
UNOP(invert, PyNumber_Invert)
UNOP(int, call_int)
UNOP(long, call_long)
UNOP(float, call_float)
UNOP(oct, call_oct)
UNOP(hex, call_hex)
INPLACE(add, PyNumber_InPlaceAdd)
INPLACE(sub, PyNumber_InPlaceSubtract)
INPLACE(mul, PyNumber_InPlaceMultiply)
INPLACE(div, PyNumber_InPlaceDivide)
INPLACE(mod, PyNumber_InPlaceRemainder)
INPLACE(pow, call_ipow)
INPLACE(lshift, PyNumber_InPlaceLshift)
INPLACE(rshift, PyNumber_InPlaceRshift)
INPLACE(and, PyNumber_InPlaceAnd)
INPLACE(xor, PyNumber_InPlaceXor)
INPLACE(or, PyNumber_InPlaceOr)
BINOP(floordiv, PyNumber_FloorDivide)
BINOP(truediv, PyNumber_TrueDivide)
INPLACE(floordiv, PyNumber_InPlaceFloorDivide)
INPLACE(truediv, PyNumber_InPlaceTrueDivide)
static int
wrap_nonzero(PyObject *self)
{
return PyObject_IsTrue(Proxy_GET_OBJECT(self));
}
/*
* Sequence methods
*/
static int
wrap_length(PyObject *self)
{
return PyObject_Length(Proxy_GET_OBJECT(self));
}
static PyObject *
wrap_slice(PyObject *self, int start, int end)
{
return PySequence_GetSlice(Proxy_GET_OBJECT(self), start, end);
}
static int
wrap_ass_slice(PyObject *self, int i, int j, PyObject *value)
{
return PySequence_SetSlice(Proxy_GET_OBJECT(self), i, j, value);
}
static int
wrap_contains(PyObject *self, PyObject *value)
{
return PySequence_Contains(Proxy_GET_OBJECT(self), value);
}
/*
* Mapping methods
*/
static PyObject *
wrap_getitem(PyObject *wrapper, PyObject *v) {
return PyObject_GetItem(Proxy_GET_OBJECT(wrapper), v);
}
static int
wrap_setitem(PyObject *self, PyObject *key, PyObject *value)
{
if (value == NULL)
return PyObject_DelItem(Proxy_GET_OBJECT(self), key);
else
return PyObject_SetItem(Proxy_GET_OBJECT(self), key, value);
}
/*
* Normal methods
*/
static char
reduce__doc__[] =
"__reduce__()\n"
"Raise an exception; this prevents proxies from being picklable by\n"
"default, even if the underlying object is picklable.";
static PyObject *
wrap_reduce(PyObject *self)
{
PyObject *pickle_error = NULL;
PyObject *pickle = PyImport_ImportModule("pickle");
if (pickle == NULL)
PyErr_Clear();
else {
pickle_error = PyObject_GetAttrString(pickle, "PicklingError");
if (pickle_error == NULL)
PyErr_Clear();
}
if (pickle_error == NULL) {
pickle_error = PyExc_RuntimeError;
Py_INCREF(pickle_error);
}
PyErr_SetString(pickle_error,
"proxy instances cannot be pickled");
Py_DECREF(pickle_error);
return NULL;
}
static PyNumberMethods
wrap_as_number = {
wrap_add, /* nb_add */
wrap_sub, /* nb_subtract */
wrap_mul, /* nb_multiply */
wrap_div, /* nb_divide */
wrap_mod, /* nb_remainder */
wrap_divmod, /* nb_divmod */
wrap_pow, /* nb_power */
wrap_neg, /* nb_negative */
wrap_pos, /* nb_positive */
wrap_abs, /* nb_absolute */
wrap_nonzero, /* nb_nonzero */
wrap_invert, /* nb_invert */
wrap_lshift, /* nb_lshift */
wrap_rshift, /* nb_rshift */
wrap_and, /* nb_and */
wrap_xor, /* nb_xor */
wrap_or, /* nb_or */
wrap_coerce, /* nb_coerce */
wrap_int, /* nb_int */
wrap_long, /* nb_long */
wrap_float, /* nb_float */
wrap_oct, /* nb_oct */
wrap_hex, /* nb_hex */
/* Added in release 2.0 */
/* These require the Py_TPFLAGS_HAVE_INPLACEOPS flag */
wrap_iadd, /* nb_inplace_add */
wrap_isub, /* nb_inplace_subtract */
wrap_imul, /* nb_inplace_multiply */
wrap_idiv, /* nb_inplace_divide */
wrap_imod, /* nb_inplace_remainder */
(ternaryfunc)wrap_ipow, /* nb_inplace_power */
wrap_ilshift, /* nb_inplace_lshift */
wrap_irshift, /* nb_inplace_rshift */
wrap_iand, /* nb_inplace_and */
wrap_ixor, /* nb_inplace_xor */
wrap_ior, /* nb_inplace_or */
/* Added in release 2.2 */
/* These require the Py_TPFLAGS_HAVE_CLASS flag */
wrap_floordiv, /* nb_floor_divide */
wrap_truediv, /* nb_true_divide */
wrap_ifloordiv, /* nb_inplace_floor_divide */
wrap_itruediv, /* nb_inplace_true_divide */
};
static PySequenceMethods
wrap_as_sequence = {
wrap_length, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
wrap_slice, /* sq_slice */
0, /* sq_ass_item */
wrap_ass_slice, /* sq_ass_slice */
wrap_contains, /* sq_contains */
};
static PyMappingMethods
wrap_as_mapping = {
wrap_length, /* mp_length */
wrap_getitem, /* mp_subscript */
wrap_setitem, /* mp_ass_subscript */
};
static PyMethodDef
wrap_methods[] = {
{"__reduce__", (PyCFunction)wrap_reduce, METH_NOARGS, reduce__doc__},
{NULL, NULL},
};
/*
* Note that the numeric methods are not supported. This is primarily
* because of the way coercion-less operations are performed with
* new-style numbers; since we can't tell which side of the operation
* is 'self', we can't ensure we'd unwrap the right thing to perform
* the actual operation. We also can't afford to just unwrap both
* sides the way weakrefs do, since we don't know what semantics will
* be associated with the wrapper itself.
*/
statichere PyTypeObject
ProxyType = {
PyObject_HEAD_INIT(NULL) /* PyObject_HEAD_INIT(&PyType_Type) */
0,
"zope.proxy.ProxyBase",
sizeof(ProxyObject),
0,
wrap_dealloc, /* tp_dealloc */
wrap_print, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
wrap_compare, /* tp_compare */
wrap_repr, /* tp_repr */
&wrap_as_number, /* tp_as_number */
&wrap_as_sequence, /* tp_as_sequence */
&wrap_as_mapping, /* tp_as_mapping */
wrap_hash, /* tp_hash */
wrap_call, /* tp_call */
wrap_str, /* tp_str */
wrap_getattro, /* tp_getattro */
wrap_setattro, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC
| Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
wrap_traverse, /* tp_traverse */
wrap_clear, /* tp_clear */
wrap_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
wrap_iter, /* tp_iter */
wrap_iternext, /* tp_iternext */
wrap_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
wrap_init, /* tp_init */
0, /* tp_alloc */
wrap_new, /* tp_new */
0, /*_PyObject_GC_Del,*/ /* tp_free */
};
static PyObject *
create_proxy(PyObject *object)
{
PyObject *result = NULL;
PyObject *args;
args = PyTuple_New(1);
if (args != NULL) {
Py_INCREF(object);
PyTuple_SET_ITEM(args, 0, object);
result = PyObject_CallObject((PyObject *)&ProxyType, args);
Py_DECREF(args);
}
return result;
}
static int
api_check(PyObject *obj)
{
return obj ? Proxy_Check(obj) : 0;
}
static PyObject *
api_create(PyObject *object)
{
if (object == NULL) {
PyErr_SetString(PyExc_ValueError,
"cannot create proxy around NULL");
return NULL;
}
return create_proxy(object);
}
static PyObject *
api_getobject(PyObject *proxy)
{
if (proxy == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"cannot pass NULL to ProxyAPI.getobject()");
return NULL;
}
if (Proxy_Check(proxy))
return Proxy_GET_OBJECT(proxy);
else {
PyErr_Format(PyExc_TypeError, "expected proxy object, got %s",
proxy->ob_type->tp_name);
return NULL;
}
}
static ProxyInterface
wrapper_capi = {
&ProxyType,
api_check,
api_create,
api_getobject,
};
static PyObject *api_object = NULL;
static char
getobject__doc__[] =
"getProxiedObject(proxy) --> object\n"
"\n"
"Get the underlying object for proxy, or the object itself, if it is\n"
"not a proxy.";
static PyObject *
wrapper_getobject(PyObject *unused, PyObject *obj)
{
if (Proxy_Check(obj))
obj = Proxy_GET_OBJECT(obj);
if (obj == NULL)
obj = Py_None;
Py_INCREF(obj);
return obj;
}
static char
isProxy__doc__[] =
"Check whether the given object is a proxy\n"
"\n"
"If proxytype is not None, checkes whether the object is\n"
"proxied by the given proxytype.\n"
;
static PyObject *
wrapper_isProxy(PyObject *unused, PyObject *args)
{
PyObject *obj, *result;
PyTypeObject *proxytype=&ProxyType;
if (! PyArg_ParseTuple(args, "O|O!:isProxy",
&obj, &PyType_Type, &proxytype)
)
return NULL;
while (obj && Proxy_Check(obj))
{
if (PyObject_TypeCheck(obj, proxytype))
{
result = Py_True;
Py_INCREF(result);
return result;
}
obj = Proxy_GET_OBJECT(obj);
}
result = Py_False;
Py_INCREF(result);
return result;
}
static char
removeAllProxies__doc__[] =
"removeAllProxies(proxy) --> object\n"
"\n"
"Get the proxied object with no proxies\n"
"\n"
"If obj is not a proxied object, return obj.\n"
"\n"
"The returned object has no proxies.\n"
;
static PyObject *
wrapper_removeAllProxies(PyObject *unused, PyObject *obj)
{
while (obj && Proxy_Check(obj))
obj = Proxy_GET_OBJECT(obj);
if (obj == NULL)
obj = Py_None;
Py_INCREF(obj);
return obj;
}
static char
sameProxiedObjects__doc__[] =
"Check whether two objects are the same or proxies of the same object";
static PyObject *
wrapper_sameProxiedObjects(PyObject *unused, PyObject *args)
{
PyObject *ob1, *ob2;
if (! PyArg_ParseTuple(args, "OO:sameProxiedObjects", &ob1, &ob2))
return NULL;
while (ob1 && Proxy_Check(ob1))
ob1 = Proxy_GET_OBJECT(ob1);
while (ob2 && Proxy_Check(ob2))
ob2 = Proxy_GET_OBJECT(ob2);
if (ob1 == ob2)
ob1 = Py_True;
else
ob1 = Py_False;
Py_INCREF(ob1);
return ob1;
}
static char
queryProxy__doc__[] =
"Look for a proxy of the given type around the object\n"
"\n"
"If no such proxy can be found, return the default.\n"
;
static PyObject *
wrapper_queryProxy(PyObject *unused, PyObject *args)
{
PyObject *obj, *result=Py_None;
PyTypeObject *proxytype=&ProxyType;
if (! PyArg_ParseTuple(args, "O|O!O:queryProxy",
&obj, &PyType_Type, &proxytype, &result)
)
return NULL;
while (obj && Proxy_Check(obj))
{
if (PyObject_TypeCheck(obj, proxytype))
{
Py_INCREF(obj);
return obj;
}
obj = Proxy_GET_OBJECT(obj);
}
Py_INCREF(result);
return result;
}
static char
queryInnerProxy__doc__[] =
"Look for the inner-most proxy of the given type around the object\n"
"\n"
"If no such proxy can be found, return the default.\n"
"\n"
"If there is such a proxy, return the inner-most one.\n"
;
static PyObject *
wrapper_queryInnerProxy(PyObject *unused, PyObject *args)
{
PyObject *obj, *result=Py_None;
PyTypeObject *proxytype=&ProxyType;
if (! PyArg_ParseTuple(args, "O|O!O:queryInnerProxy",
&obj, &PyType_Type, &proxytype, &result)
)
return NULL;
while (obj && Proxy_Check(obj))
{
if (PyObject_TypeCheck(obj, proxytype))
result = obj;
obj = Proxy_GET_OBJECT(obj);
}
Py_INCREF(result);
return result;
}
static char
module___doc__[] =
"Association between an object, a context object, and a dictionary.\n\
\n\
The context object and dictionary give additional context information\n\
associated with a reference to the basic object. The wrapper objects\n\
act as proxies for the original object.";
static PyMethodDef
module_functions[] = {
{"getProxiedObject", wrapper_getobject, METH_O, getobject__doc__},
{"isProxy", wrapper_isProxy, METH_VARARGS, isProxy__doc__},
{"sameProxiedObjects", wrapper_sameProxiedObjects, METH_VARARGS,
sameProxiedObjects__doc__},
{"queryProxy", wrapper_queryProxy, METH_VARARGS, queryProxy__doc__},
{"queryInnerProxy", wrapper_queryInnerProxy, METH_VARARGS,
queryInnerProxy__doc__},
{"removeAllProxies", wrapper_removeAllProxies, METH_O,
removeAllProxies__doc__},
{NULL}
};
void
init_zope_proxy_proxy(void)
{
PyObject *m = Py_InitModule3("_zope_proxy_proxy",
module_functions, module___doc__);
if (m == NULL)
return;
if (empty_tuple == NULL)
empty_tuple = PyTuple_New(0);
ProxyType.tp_free = _PyObject_GC_Del;
if (PyType_Ready(&ProxyType) < 0)
return;
Py_INCREF(&ProxyType);
PyModule_AddObject(m, "ProxyBase", (PyObject *)&ProxyType);
if (api_object == NULL) {
api_object = PyCObject_FromVoidPtr(&wrapper_capi, NULL);
if (api_object == NULL)
return;
}
Py_INCREF(api_object);
PyModule_AddObject(m, "_CAPI", api_object);
}
#
# This file is necessary to make this directory a package.
<html metal:use-macro="context/@@standard_macros/addingdialog"
i18n:domain="zope">
<body>
<div metal:fill-slot="body">
<form method="post" action="action.html">
<table class="TypeListing" cellpadding="3">
<tal:block define="title view/title | nothing">
<caption tal:condition="title" tal:content="title"
i18n:translate="">Inserted title</caption>
<caption tal:condition="not:title"
i18n:translate="">Add Content</caption>
</tal:block>
<tbody tal:define="infos view/addingInfo">
<tr tal:repeat="info infos">
<td class="Selector">
<input type="radio" name="type_name"
tal:attributes="value info/action;
id info/action;
checked python:len(infos)==1" />
</td>
<td class="TypeName">
<label style="font-weight: bold;"
tal:attributes="for info/action">
<span tal:replace="info/title" i18n:translate="">Folder</span>
</label>
<div class="TypeDescription" tal:content="info/description"
i18n:translate="">
Folders are generic containers for content, including other
folders.
</div>
</td>
</tr>
<tr>
<td><br /></td>
<td><input type="text" name="id"
tal:condition="view/nameAllowed"
tal:attributes="value request/id | nothing" />
<input type="submit" name="add" value=" Add "
i18n:attributes="value add-button" />
</td>
</tr>
</tbody>
</table>
</form>
</div>
</body>
</html>
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Adding View
The Adding View is used to add new objects to a container. It is sort of a
factory screen.
$Id$
"""
__docformat__ = 'restructuredtext'
import zope.security.checker
from zope.component.interfaces import IFactory
from zope.event import notify
from zope.interface import implements
from zope.publisher.interfaces import IPublishTraverse
from zope.publisher.browser import BrowserView
from zope.security.proxy import removeSecurityProxy
from zope.exceptions.interfaces import UserError
from zope.location import LocationProxy
from zope.lifecycleevent import ObjectCreatedEvent
from zope.app.container.interfaces import IAdding, INameChooser
from zope.app.container.interfaces import IContainerNamesContainer
from zope.app.container.constraints import checkFactory, checkObject
from zope.app import zapi
from zope.app.container.i18n import ZopeMessageFactory as _
from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
from zope.app.publisher.browser.menu import getMenu
class Adding(BrowserView):
implements(IAdding, IPublishTraverse)
def add(self, content):
"""See zope.app.container.interfaces.IAdding
"""
container = self.context
name = self.contentName
chooser = INameChooser(container)
# check precondition
checkObject(container, name, content)
if IContainerNamesContainer.providedBy(container):
# The container picks its own names.
# We need to ask it to pick one.
name = chooser.chooseName(self.contentName or '', content)
else:
request = self.request
name = request.get('add_input_name', name)
if name is None:
name = chooser.chooseName(self.contentName or '', content)
elif name == '':
name = chooser.chooseName('', content)
chooser.checkName(name, content)
container[name] = content
self.contentName = name # Set the added object Name
return container[name]
contentName = None # usually set by Adding traverser
def nextURL(self):
"""See zope.app.container.interfaces.IAdding"""
return zapi.absoluteURL(self.context, self.request) + '/@@contents.html'
# set in BrowserView.__init__
request = None
context = None
def publishTraverse(self, request, name):
"""See zope.app.container.interfaces.IAdding"""
if '=' in name:
view_name, content_name = name.split("=", 1)
self.contentName = content_name
if view_name.startswith('@@'):
view_name = view_name[2:]
return zapi.getMultiAdapter((self, request), name=view_name)
if name.startswith('@@'):
view_name = name[2:]
else:
view_name = name
view = zapi.queryMultiAdapter((self, request), name=view_name)
if view is not None:
return view
factory = zapi.queryUtility(IFactory, name)
if factory is None:
return super(Adding, self).publishTraverse(request, name)
return factory
def action(self, type_name='', id=''):
if not type_name:
raise UserError(_(u"You must select the type of object to add."))
if type_name.startswith('@@'):
type_name = type_name[2:]
if '/' in type_name:
view_name = type_name.split('/', 1)[0]
else:
view_name = type_name
if zapi.queryMultiAdapter((self, self.request),
name=view_name) is not None:
url = "%s/%s=%s" % (
zapi.absoluteURL(self, self.request), type_name, id)
self.request.response.redirect(url)
return
if not self.contentName:
self.contentName = id
# TODO: If the factory wrapped by LocationProxy is already a Proxy,
# then ProxyFactory does not do the right thing and the
# original's checker info gets lost. No factory that was
# registered via ZCML and was used via addMenuItem worked
# here. (SR)
factory = zapi.getUtility(IFactory, type_name)
if not type(factory) is zope.security.checker.Proxy:
factory = LocationProxy(factory, self, type_name)
factory = zope.security.checker.ProxyFactory(factory)
content = factory()
# Can't store security proxies.
# Note that it is important to do this here, rather than
# in add, otherwise, someone might be able to trick add
# into unproxying an existing object,
content = removeSecurityProxy(content)
notify(ObjectCreatedEvent(content))
self.add(content)
self.request.response.redirect(self.nextURL())
def nameAllowed(self):
"""Return whether names can be input by the user."""
return not IContainerNamesContainer.providedBy(self.context)
menu_id = None
index = ViewPageTemplateFile("add.pt")
def addingInfo(self):
"""Return menu data.
This is sorted by title.
"""
container = self.context
result = []
for menu_id in (self.menu_id, 'zope.app.container.add'):
if not menu_id:
continue
for item in getMenu(menu_id, self, self.request):
extra = item.get('extra')
if extra:
factory = extra.get('factory')
if factory:
factory = zapi.getUtility(IFactory, factory)
if not checkFactory(container, None, factory):
continue
elif item['extra']['factory'] != item['action']:
item['has_custom_add_view']=True
result.append(item)
result.sort(lambda a, b: cmp(a['title'], b['title']))
return result
def isSingleMenuItem(self):
"Return whether there is single menu item or not."
return len(self.addingInfo()) == 1
def hasCustomAddView(self):
"This should be called only if there is `singleMenuItem` else return 0"
if self.isSingleMenuItem():
menu_item = self.addingInfo()[0]
if 'has_custom_add_view' in menu_item:
return True
return False
<tal:block define="addingInfo context/@@+/addingInfo|nothing"
condition="addingInfo" i18n:domain="zope">
<tal:block repeat="info addingInfo"
define="namesRequired context/@@+/nameAllowed">
<div tal:define="oddrow repeat/info/odd;
namesRequired context/@@+/nameAllowed;
has_custom_add_view python:'has_custom_add_view' in info"
tal:attributes="class python:oddrow and 'content even' or 'content odd'"
class="even">
<a href="#"
tal:define="baseurl python:request.getURL(1)"
tal:condition="python: not info['action'].startswith('../')
and namesRequired and not has_custom_add_view"
tal:attributes="
href string:${baseurl}/@@contents.html?type_name=${info/action};
class info/selected"
tal:content="info/title" i18n:translate="">Folder
</a>
<a href="#"
tal:define="baseurl python:request.getURL(1)"
tal:condition="python: not info['action'].startswith('../')
and (has_custom_add_view or not namesRequired)"
tal:attributes="
href string:${baseurl}/@@+/action.html?type_name=${info/action};
class info/selected"
tal:content="info/title" i18n:translate="">Folder
</a>
<a href="#"
tal:define="baseurl python:request.getURL(1)"
tal:condition="python: info['action'].startswith('../')"
tal:attributes="
href python: info['action'][3:];
class info/selected"
tal:content="info/title" i18n:translate="">Folder
</a>
</div>
</tal:block>
</tal:block>
<zope:configure
xmlns:zope="http://namespaces.zope.org/zope"
xmlns="http://namespaces.zope.org/browser">
<page
for="zope.app.container.interfaces.IReadContainer"
name="find.html"
permission="zope.ManageContent"
class="zope.app.container.browser.find.Find"
template="find.pt"
menu="zmi_actions" title="Find" />
<page
for="zope.app.container.interfaces.IWriteContainer"
permission="zope.ManageContent"
name="commonTasks"
class="zope.app.container.browser.contents.Contents"
template="commontasks.pt" />
</zope:configure>
<html metal:use-macro="context/@@standard_macros/view"
i18n:domain="zope">
<body>
<div metal:fill-slot="body">
<div metal:define-macro="contents">
<form name="containerContentsForm" method="post" action="."
tal:attributes="action request/URL"
tal:define="container_contents view/listContentInfo">
<input type="hidden" name="type_name" value=""
tal:attributes="value request/type_name"
tal:condition="request/type_name|nothing"
/>
<input type="hidden" name="retitle_id" value=""
tal:attributes="value request/retitle_id"
tal:condition="request/retitle_id|nothing"
/>
<div class="page_error"
tal:condition="view/error"
tal:content="view/error"
i18n:translate="">
Error message
</div>
<table id="sortable" class="listing" summary="Content listing"
i18n:attributes="summary">
<thead>
<tr>
<th><input type="checkbox" onchange="updateCheckboxes(this, 'slaveBox');" /></th>
<th i18n:translate="">Name</th>
<th i18n:translate="">Title</th>
<th i18n:translate="">Size</th>
<th i18n:translate="">Created</th>
<th i18n:translate="">Modified</th>
</tr>
</thead>
<tbody>
<metal:block tal:condition="view/hasAdding">
<tr tal:define="names_required context/@@+/nameAllowed"
tal:condition="python:names_required and request.has_key('type_name')">
<td></td>
<td><input name="new_value" id="focusid" value="" /></td>
<td></td>
<td></td>
<td></td>
</tr>
</metal:block>
<metal:block tal:define="supportsRename view/supportsRename"
tal:repeat="item container_contents">
<tr tal:define="oddrow repeat/item/odd; url item/url;
id_quoted item/id/url:quote"
tal:attributes="class python:oddrow and 'even' or 'odd'" >
<td>
<input type="checkbox" class="noborder slaveBox" name="ids:list" id="#"
value="#"
tal:attributes="value item/id;
id item/cb_id;
checked request/ids_checked|nothing;"/>
</td>
<td><a href="#"
tal:attributes="href
string:${url}/@@SelectedManagementView.html"
tal:content="structure item/icon|default">
</a
><span tal:condition="item/rename"
><input name="new_value:list"
tal:attributes="value item/id"
/><input type="hidden" name="rename_ids:list" value=""
tal:attributes="value item/rename"
/></span
><span tal:condition="not:item/rename">
<a href="#"
tal:attributes="href
string:${url}/@@SelectedManagementView.html"
tal:content="item/id"
>foo</a
><a href="#"
tal:attributes="href
string:${request/URL}?rename_ids:list=${id_quoted}"
tal:condition="supportsRename"
>&nbsp;&nbsp;</a
></span
></td>
<td>
<input name="new_value" id="focusid"
tal:attributes="value item/title|nothing"
tal:condition="item/retitle"
/>
<a href="#"
tal:attributes="href
string:${request/URL}?retitle_id=${id_quoted}"
tal:condition="item/retitleable"
tal:content="item/title|default"
i18n:translate=""
>&nbsp;&nbsp;&nbsp;&nbsp;</a>
<span
tal:condition="item/plaintitle"
tal:content="item/title|default"
i18n:translate=""
>&nbsp;&nbsp;&nbsp;&nbsp;</span>
</td>
<td><span tal:content="item/size/sizeForDisplay|nothing"
i18n:translate="">
&nbsp;</span></td>
<td><span tal:define="created item/created|default"
tal:content="created"
i18n:translate="">&nbsp;</span></td>
<td><span tal:define="modified item/modified|default"
tal:content="modified"
i18n:translate="">&nbsp;</span></td>
</tr>
</metal:block>
</tbody>
</table>
<tal:block tal:condition="view/normalButtons">
<input type="submit" name="container_rename_button" value="Rename"
i18n:attributes="value container-rename-button"
tal:condition="view/supportsRename"
/>
<input type="submit" name="container_cut_button" value="Cut"
i18n:attributes="value container-cut-button"
tal:condition="view/supportsCut"
/>
<input type="submit" name="container_copy_button" value="Copy"
i18n:attributes="value container-copy-button"
tal:condition="view/supportsCopy"
/>
<input type="submit" name="container_paste_button" value="Paste"
tal:condition="view/hasClipboardContents"
i18n:attributes="value container-paste-button"
/>
<input type="submit" name="container_delete_button" value="Delete"
i18n:attributes="value container-delete-button"
tal:condition="view/supportsDelete"
i18n:domain="zope"
/>
<div tal:condition="view/hasAdding" tal:omit-tag="">
<div tal:omit-tag=""
tal:define="adding nocall:context/@@+;
addingInfo adding/addingInfo;
has_custom_add_view adding/hasCustomAddView;
names_required adding/nameAllowed"
tal:condition="adding/isSingleMenuItem">
<input type="submit" name="container_add_button" value="Add"
i18n:attributes="value add-button"
i18n:domain="zope"
/>
<input type="text" name="single_new_value" id="focusid"
tal:condition="python:names_required and not has_custom_add_view"
i18n:domain="zope"
/>
<input type="hidden" name="single_type_name"
value=""
tal:attributes="value python:addingInfo[0]['action']"
/>
</div>
</div>
</tal:block>
<div tal:condition="view/specialButtons">
<input type="submit" value="Apply"
i18n:attributes="value container-apply-button"
/>
<input type="submit" name="container_cancel_button" value="Cancel"
i18n:attributes="value container-cancel-button"
/>
</div>
</form>
<script type="text/javascript"><!--
if (document.containerContentsForm.new_value)
document.containerContentsForm.new_value.focus();
//-->
</script>
</div>
</div>
</body>
</html>
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""View Class for the Container's Contents view.
$Id$
"""
__docformat__ = 'restructuredtext'
import urllib
from zope.exceptions.interfaces import UserError
from zope.security.interfaces import Unauthorized
from zope.security import canWrite
from zope.size.interfaces import ISized
from zope.traversing.interfaces import TraversalError
from zope.publisher.browser import BrowserView
from zope.dublincore.interfaces import IZopeDublinCore
from zope.dublincore.interfaces import IDCDescriptiveProperties
from zope.copypastemove.interfaces import IPrincipalClipboard
from zope.copypastemove.interfaces import IObjectCopier, IObjectMover
from zope.copypastemove.interfaces import IContainerItemRenamer
from zope.annotation.interfaces import IAnnotations
from zope.app import zapi
from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
from zope.app.container.i18n import ZopeMessageFactory as _
from zope.app.container.browser.adding import Adding
from zope.app.container.interfaces import IContainer, DuplicateIDError
from zope.app.container.interfaces import IContainerNamesContainer
class Contents(BrowserView):
__used_for__ = IContainer
error = ''
message = ''
normalButtons = False
specialButtons = False
supportsRename = False
def listContentInfo(self):
request = self.request
if "container_cancel_button" in request:
if "type_name" in request:
del request.form['type_name']
if "rename_ids" in request and "new_value" in request:
del request.form['rename_ids']
if "retitle_id" in request and "new_value" in request:
del request.form['retitle_id']
return self._normalListContentsInfo()
elif "container_rename_button" in request and not request.get("ids"):
self.error = _("You didn't specify any ids to rename.")
elif "container_add_button" in request:
if "single_type_name" in request \
and "single_new_value" in request:
request.form['type_name'] = request['single_type_name']
request.form['new_value'] = request['single_new_value']
self.addObject()
elif 'single_type_name' in request \
and 'single_new_value' not in request:
request.form['type_name'] = request['single_type_name']
request.form['new_value'] = ""
self.addObject()
elif "type_name" in request and "new_value" in request:
self.addObject()
elif "rename_ids" in request and "new_value" in request:
self.renameObjects()
elif "retitle_id" in request and "new_value" in request:
self.changeTitle()
elif "container_cut_button" in request:
self.cutObjects()
elif "container_copy_button" in request:
self.copyObjects()
elif "container_paste_button" in request:
self.pasteObjects()
elif "container_delete_button" in request:
self.removeObjects()
else:
return self._normalListContentsInfo()
if self.error:
return self._normalListContentsInfo()
status = request.response.getStatus()
if status not in (302, 303):
# Only redirect if nothing else has
request.response.redirect(request.URL)
return ()
def normalListContentInfo(self):
return self._normalListContentsInfo()
def _normalListContentsInfo(self):
request = self.request
self.specialButtons = (
'type_name' in request or
'rename_ids' in request or
('container_rename_button' in request
and request.get("ids")) or
'retitle_id' in request
)
self.normalButtons = not self.specialButtons
info = map(self._extractContentInfo, self.context.items())
self.supportsCut = info
self.supportsCopy = info
self.supportsDelete = info
self.supportsPaste = self.pasteable()
self.supportsRename = (
self.supportsCut and
not IContainerNamesContainer.providedBy(self.context)
)
return info
def _extractContentInfo(self, item):
request = self.request
rename_ids = {}
if "container_rename_button" in request:
for rename_id in request.get('ids', ()):
rename_ids[rename_id] = rename_id
elif "rename_ids" in request:
for rename_id in request.get('rename_ids', ()):
rename_ids[rename_id] = rename_id
retitle_id = request.get('retitle_id')
id, obj = item
info = {}
info['id'] = info['cb_id'] = id
info['object'] = obj
info['url'] = urllib.quote(id.encode('utf-8'))
info['rename'] = rename_ids.get(id)
info['retitle'] = id == retitle_id
zmi_icon = zapi.queryMultiAdapter((obj, self.request), name='zmi_icon')
if zmi_icon is None:
info['icon'] = None
else:
info['icon'] = zmi_icon()
dc = IZopeDublinCore(obj, None)
if dc is not None:
info['retitleable'] = canWrite(dc, 'title')
info['plaintitle'] = not info['retitleable']
title = self.safe_getattr(dc, 'title', None)
if title:
info['title'] = title
formatter = self.request.locale.dates.getFormatter(
'dateTime', 'short')
created = self.safe_getattr(dc, 'created', None)
if created is not None:
info['created'] = formatter.format(created)
modified = self.safe_getattr(dc, 'modified', None)
if modified is not None:
info['modified'] = formatter.format(modified)
else:
info['retitleable'] = 0
info['plaintitle'] = 1
sized_adapter = ISized(obj, None)
if sized_adapter is not None:
info['size'] = sized_adapter
return info
def safe_getattr(self, obj, attr, default):
"""Attempts to read the attr, returning default if Unauthorized."""
try:
return getattr(obj, attr, default)
except Unauthorized:
return default
def renameObjects(self):
"""Given a sequence of tuples of old, new ids we rename"""
request = self.request
ids = request.get("rename_ids")
newids = request.get("new_value")
renamer = IContainerItemRenamer(self.context)
for oldid, newid in zip(ids, newids):
if newid != oldid:
renamer.renameItem(oldid, newid)
def changeTitle(self):
"""Given a sequence of tuples of old, new ids we rename"""
request = self.request
id = request.get("retitle_id")
new = request.get("new_value")
item = self.context[id]
dc = IDCDescriptiveProperties(item)
dc.title = new
def hasAdding(self):
"""Returns true if an adding view is available."""
adding = zapi.queryMultiAdapter((self.context, self.request), name="+")
return (adding is not None)
def addObject(self):
request = self.request
if IContainerNamesContainer.providedBy(self.context):
new = ""
else:
new = request["new_value"]
adding = zapi.queryMultiAdapter((self.context, self.request), name="+")
if adding is None:
adding = Adding(self.context, request)
else:
# Set up context so that the adding can build a url
# if the type name names a view.
# Note that we can't so this for the "adding is None" case
# above, because there is no "+" view.
adding.__parent__ = self.context
adding.__name__ = '+'
adding.action(request['type_name'], new)
def removeObjects(self):
"""Remove objects specified in a list of object ids"""
request = self.request
ids = request.get('ids')
if not ids:
self.error = _("You didn't specify any ids to remove.")
return
container = self.context
for id in ids:
del container[id]
def copyObjects(self):
"""Copy objects specified in a list of object ids"""
request = self.request
ids = request.get('ids')
if not ids:
self.error = _("You didn't specify any ids to copy.")
return
container_path = zapi.getPath(self.context)
# For each item, check that it can be copied; if so, save the
# path of the object for later copying when a destination has
# been selected; if not copyable, provide an error message
# explaining that the object can't be copied.
items = []
for id in ids:
ob = self.context[id]
copier = IObjectCopier(ob)
if not copier.copyable():
m = {"name": id}
title = getDCTitle(ob)
if title:
m["title"] = title
self.error = _(
"Object '${name}' (${title}) cannot be copied",
mapping=m)
else:
self.error = _("Object '${name}' cannot be copied",
mapping=m)
return
items.append(zapi.joinPath(container_path, id))
# store the requested operation in the principal annotations:
clipboard = getPrincipalClipboard(self.request)
clipboard.clearContents()
clipboard.addItems('copy', items)
def cutObjects(self):
"""move objects specified in a list of object ids"""
request = self.request
ids = request.get('ids')
if not ids:
self.error = _("You didn't specify any ids to cut.")
return
container_path = zapi.getPath(self.context)
# For each item, check that it can be moved; if so, save the
# path of the object for later moving when a destination has
# been selected; if not movable, provide an error message
# explaining that the object can't be moved.
items = []
for id in ids:
ob = self.context[id]
mover = IObjectMover(ob)
if not mover.moveable():
m = {"name": id}
title = getDCTitle(ob)
if title:
m["title"] = title
self.error = _(
"Object '${name}' (${title}) cannot be moved",
mapping=m)
else:
self.error = _("Object '${name}' cannot be moved",
mapping=m)
return
items.append(zapi.joinPath(container_path, id))
# store the requested operation in the principal annotations:
clipboard = getPrincipalClipboard(self.request)
clipboard.clearContents()
clipboard.addItems('cut', items)
def pasteable(self):
"""Decide if there is anything to paste
"""
target = self.context
clipboard = getPrincipalClipboard(self.request)
items = clipboard.getContents()
for item in items:
try:
obj = zapi.traverse(target, item['target'])
except TraversalError:
pass
else:
if item['action'] == 'cut':
mover = IObjectMover(obj)
moveableTo = self.safe_getattr(mover, 'moveableTo', None)
if moveableTo is None or not moveableTo(target):
return False
elif item['action'] == 'copy':
copier = IObjectCopier(obj)
copyableTo = self.safe_getattr(copier, 'copyableTo', None)
if copyableTo is None or not copyableTo(target):
return False
else:
raise
return True
def pasteObjects(self):
"""Paste ojects in the user clipboard to the container
"""
target = self.context
clipboard = getPrincipalClipboard(self.request)
items = clipboard.getContents()
moved = False
not_pasteable_ids = []
for item in items:
duplicated_id = False
try:
obj = zapi.traverse(target, item['target'])
except TraversalError:
pass
else:
if item['action'] == 'cut':
mover = IObjectMover(obj)
try:
mover.moveTo(target)
moved = True
except DuplicateIDError:
duplicated_id = True
elif item['action'] == 'copy':
copier = IObjectCopier(obj)
try:
copier.copyTo(target)
except DuplicateIDError:
duplicated_id = True
else:
raise
if duplicated_id:
not_pasteable_ids.append(zapi.getName(obj))
if moved:
# Clear the clipboard if we do a move, but not if we only do a copy
clipboard.clearContents()
if not_pasteable_ids != []:
# Show the ids of objects that can't be pasted because
# their ids are already taken.
# TODO Can't we add a 'copy_of' or something as a prefix
# instead of raising an exception ?
raise UserError(
_("The given name(s) %s is / are already being used" %(
str(not_pasteable_ids))))
def hasClipboardContents(self):
"""Interogate the ``PrinicipalAnnotation`` to see if clipboard
contents exist."""
if not self.supportsPaste:
return False
# touch at least one item to in clipboard confirm contents
clipboard = getPrincipalClipboard(self.request)
items = clipboard.getContents()
for item in items:
try:
zapi.traverse(self.context, item['target'])
except TraversalError:
pass
else:
return True
return False
contents = ViewPageTemplateFile('contents.pt')
contentsMacros = contents
_index = ViewPageTemplateFile('index.pt')
def index(self):
if 'index.html' in self.context:
self.request.response.redirect('index.html')
return ''
return self._index()
class JustContents(Contents):
"""Like Contents, but does't delegate to item named index.html"""
def index(self):
return self._index()
def getDCTitle(ob):
dc = IDCDescriptiveProperties(ob, None)
if dc is None:
return None
else:
return dc.title
def getPrincipalClipboard(request):
"""Return the clipboard based on the request."""
user = request.principal
annotations = IAnnotations(user)
return IPrincipalClipboard(annotations)
<html metal:use-macro="context/@@standard_macros/dialog"
i18n:domain="zope">
<body>
<div metal:fill-slot="body" >
<form action="@@find.html" method="get">
<input type="text" name="ids" value="" /><br />
<input type="submit" name="find_submit" value=" Find "
i18n:attributes="value find-button"/>
</form>
<table tal:condition="request/ids|nothing">
<tr tal:repeat="item python:view.findByIds(request['ids'])">
<td>
<a href="" tal:attributes="href item/url" tal:content="item/id">id</a>
</td>
</tr>
</table>
</div>
</body>
</html>
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Find View Class
$Id$
"""
__docformat__ = 'restructuredtext'
from zope.traversing.api import getName
from zope.traversing.browser.absoluteurl import absoluteURL
from zope.publisher.browser import BrowserView
from zope.app.container.find import SimpleIdFindFilter
from zope.app.container.interfaces import IFind
# Very simple implementation right now
class Find(BrowserView):
def findByIds(self, ids):
"""Do a find for the `ids` listed in `ids`, which is a string."""
finder = IFind(self.context)
ids = ids.split()
# if we don't have any ids listed, don't search at all
if not ids:
return []
request = self.request
result = []
for object in finder.find([SimpleIdFindFilter(ids)]):
url = absoluteURL(object, request)
result.append({ 'id': getName(object), 'url': url})
return result
#
# This file is necessary to make this directory a package.
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<class class=".test_contents.ReadOnlyContainer">
<require
permission="zope.ManageContent"
interface="zope.app.container.interfaces.IReadContainer"
/>
</class>
<browser:containerViews
for="zope.app.container.interfaces.IReadContainer"
contents="zope.ManageContent" />
</configure>
The containerViews directive lets us associate some standard forms
for containers with an interface. There's an "index.html" view that
provides a listing of the contained objects without provinding any way
to manage them (though it allows us to visit them by clicking on
links).
We can get this view from the root folder easily::
>>> response = http(r"""
... GET / HTTP/1.1
... """)
And we can check that there isn't a form (where the management
operations would have buttons)::
>>> body = response.getBody().lower()
>>> "<form" in body
False
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Functional tests for the Container's 'Contents' view
$Id$
"""
import unittest
from persistent import Persistent
import transaction
from zope import copypastemove
from zope.interface import implements, Interface
from zope.annotation.interfaces import IAttributeAnnotatable
from zope.dublincore.interfaces import IZopeDublinCore
from zope.app.container.interfaces import IReadContainer, IContained
from zope.app.testing import ztapi
from zope.app.testing.functional import BrowserTestCase
from zope.app.testing.functional import FunctionalDocFileSuite
from zope.app.container.testing import AppContainerLayer
class IImmovable(Interface):
"""Marker interface for immovable objects."""
class IUncopyable(Interface):
"""Marker interface for uncopyable objects."""
class File(Persistent):
implements(IAttributeAnnotatable)
class ImmovableFile(File):
implements(IImmovable)
class UncopyableFile(File):
implements(IUncopyable)
class ObjectNonCopier(copypastemove.ObjectCopier):
def copyable(self):
return False
class ObjectNonMover(copypastemove.ObjectMover):
def moveable(self):
return False
class ReadOnlyContainer(Persistent):
implements(IReadContainer, IContained)
__parent__ = __name__ = None
def __init__(self): self.data = {}
def keys(self): return self.data.keys()
def __getitem__(self, key): return self.data[key]
def get(self, key, default=None): return self.data.get(key, default)
def __iter__(self): return iter(self.data)
def values(self): return self.data.values()
def __len__(self): return len(self.data)
def items(self): return self.data.items()
def __contains__(self, key): return key in self.data
def has_key(self, key): return self.data.has_key(key)
class Test(BrowserTestCase):
def test_inplace_add(self):
root = self.getRootFolder()
self.assert_('foo' not in root)
response = self.publish('/@@contents.html',
basic='mgr:mgrpw',
form={'type_name': u'zope.app.content.File'})
body = ' '.join(response.getBody().split())
self.assert_(body.find('type="hidden" name="type_name"') >= 0)
self.assert_(body.find('input name="new_value"') >= 0)
self.assert_(body.find('type="submit" name="container_cancel_button"')
>= 0)
self.assert_(body.find('type="submit" name="container_rename_button"')
< 0)
response = self.publish('/@@contents.html',
basic='mgr:mgrpw',
form={'type_name': u'zope.app.content.File',
'new_value': 'foo'})
self.assertEqual(response.getStatus(), 302)
self.assertEqual(response.getHeader('Location'),
'http://localhost/@@contents.html')
root._p_jar.sync()
self.assert_('foo' in root)
def test_inplace_rename_multiple(self):
root = self.getRootFolder()
root['foo'] = File()
self.assert_('foo' in root)
transaction.commit()
# Check that we don't change mode if there are no items selected
response = self.publish('/@@contents.html',
basic='mgr:mgrpw',
form={'container_rename_button': u''})
body = ' '.join(response.getBody().split())
self.assert_(body.find('input name="new_value:list"') < 0)
self.assert_(body.find('type="submit" name="container_cancel_button"')
< 0)
self.assert_(body.find('type="submit" name="container_rename_button"')
>= 0)
self.assert_(body.find('div class="page_error"')
>= 0)
# Check normal multiple select
response = self.publish('/@@contents.html',
basic='mgr:mgrpw',
form={'container_rename_button': u'',
'ids': ['foo']})
body = ' '.join(response.getBody().split())
self.assert_(body.find('input name="new_value:list"') >= 0)
self.assert_(body.find('type="submit" name="container_cancel_button"')
>= 0)
self.assert_(body.find('type="submit" name="container_rename_button"')
< 0)
response = self.publish('/@@contents.html',
basic='mgr:mgrpw',
form={'rename_ids': ['foo'],
'new_value': ['bar']})
self.assertEqual(response.getStatus(), 302)
self.assertEqual(response.getHeader('Location'),
'http://localhost/@@contents.html')
root._p_jar.sync()
self.assert_('foo' not in root)
self.assert_('bar' in root)
def test_inplace_rename_single(self):
root = self.getRootFolder()
root['foo'] = File()
self.assert_('foo' in root)
transaction.commit()
response = self.publish('/@@contents.html',
basic='mgr:mgrpw',
form={'rename_ids': ['foo']})
body = ' '.join(response.getBody().split())
self.assert_(body.find('input name="new_value:list"') >= 0)
self.assert_(body.find('type="submit" name="container_cancel_button"')
>= 0)
self.assert_(body.find('type="submit" name="container_rename_button"')
< 0)
response = self.publish('/@@contents.html',
basic='mgr:mgrpw',
form={'rename_ids': ['foo'],
'new_value': ['bar']})
self.assertEqual(response.getStatus(), 302)
self.assertEqual(response.getHeader('Location'),
'http://localhost/@@contents.html')
root._p_jar.sync()
self.assert_('foo' not in root)
self.assert_('bar' in root)
def test_inplace_change_title(self):
root = self.getRootFolder()
root['foo'] = File()
transaction.commit()
self.assert_('foo' in root)
dc = IZopeDublinCore(root['foo'])
self.assert_(dc.title == '')
response = self.publish('/@@contents.html',
basic='mgr:mgrpw',
form={'retitle_id': u'foo'})
body = ' '.join(response.getBody().split())
self.assert_(body.find('type="hidden" name="retitle_id"') >= 0)
self.assert_(body.find('input name="new_value"') >= 0)
self.assert_(body.find('type="submit" name="container_cancel_button"')
>= 0)
self.assert_(body.find('type="submit" name="container_rename_button"')
< 0)
response = self.publish('/@@contents.html',
basic='mgr:mgrpw',
form={'retitle_id': u'foo',
'new_value': u'test title'})
self.assertEqual(response.getStatus(), 302)
self.assertEqual(response.getHeader('Location'),
'http://localhost/@@contents.html')
root._p_jar.sync()
self.assert_('foo' in root)
dc = IZopeDublinCore(root['foo'])
self.assert_(dc.title == 'test title')
def test_pasteable_for_deleted_clipboard_item(self):
"""Tests Paste button visibility when copied item is deleted."""
root = self.getRootFolder()
root['foo'] = File() # item to be copied/deleted
root['bar'] = File() # ensures that there's always an item in
# the collection view
transaction.commit()
# confirm foo in contents, Copy button visible, Paste not visible
response = self.publish('/@@contents.html', basic='mgr:mgrpw')
self.assertEqual(response.getStatus(), 200)
self.assert_(response.getBody().find(
'<a href="foo/@@SelectedManagementView.html">foo</a>') != -1)
self.assert_(response.getBody().find(
'<input type="submit" name="container_copy_button"') != -1)
self.assert_(response.getBody().find(
'<input type="submit" name="container_paste_button"') == -1)
# copy foo - confirm Paste visible
response = self.publish('/@@contents.html', basic='mgr:mgrpw', form={
'ids' : ('foo',),
'container_copy_button' : '' })
self.assertEqual(response.getStatus(), 302)
self.assertEqual(response.getHeader('Location'),
'http://localhost/@@contents.html')
response = self.publish('/@@contents.html', basic='mgr:mgrpw')
self.assertEqual(response.getStatus(), 200)
self.assert_(response.getBody().find(
'<input type="submit" name="container_paste_button"') != -1)
# delete foo -> nothing valid to paste -> Paste should not be visible
del root['foo']
transaction.commit()
response = self.publish('/@@contents.html', basic='mgr:mgrpw')
self.assertEqual(response.getStatus(), 200)
self.assert_(response.getBody().find(
'<input type="submit" name="container_paste_button"') == -1)
def test_paste_for_deleted_clipboard_item(self):
"""Tests paste operation when one of two copied items is deleted."""
root = self.getRootFolder()
root['foo'] = File()
root['bar'] = File()
transaction.commit()
# confirm foo/bar in contents, Copy button visible, Paste not visible
response = self.publish('/@@contents.html', basic='mgr:mgrpw')
self.assertEqual(response.getStatus(), 200)
self.assert_(response.getBody().find(
'<a href="foo/@@SelectedManagementView.html">foo</a>') != -1)
self.assert_(response.getBody().find(
'<a href="bar/@@SelectedManagementView.html">bar</a>') != -1)
self.assert_(response.getBody().find(
'<input type="submit" name="container_copy_button"') != -1)
self.assert_(response.getBody().find(
'<input type="submit" name="container_paste_button"') == -1)
# copy foo and bar - confirm Paste visible
response = self.publish('/@@contents.html', basic='mgr:mgrpw', form={
'ids' : ('foo', 'bar'),
'container_copy_button' : '' })
self.assertEqual(response.getStatus(), 302)
self.assertEqual(response.getHeader('Location'),
'http://localhost/@@contents.html')
response = self.publish('/@@contents.html', basic='mgr:mgrpw')
self.assertEqual(response.getStatus(), 200)
self.assert_(response.getBody().find(
'<input type="submit" name="container_paste_button"') != -1)
# delete only foo -> bar still available -> Paste should be visible
del root['foo']
transaction.commit()
response = self.publish('/@@contents.html', basic='mgr:mgrpw')
self.assertEqual(response.getStatus(), 200)
self.assert_(response.getBody().find(
'<input type="submit" name="container_paste_button"') != -1)
# paste clipboard contents - only bar should be copied
response = self.publish('/@@contents.html', basic='mgr:mgrpw', form={
'container_paste_button' : '' })
self.assertEqual(response.getStatus(), 302)
self.assertEqual(response.getHeader('Location'),
'http://localhost/@@contents.html')
response = self.publish('/@@contents.html', basic='mgr:mgrpw')
self.assertEqual(response.getStatus(), 200)
root._p_jar.sync()
self.assertEqual(tuple(root.keys()), ('bar', 'bar-2'))
def test_readonly_display(self):
root = self.getRootFolder()
root['foo'] = ReadOnlyContainer()
transaction.commit()
response = self.publish('/foo/@@contents.html', basic='mgr:mgrpw')
self.assertEqual(response.getStatus(), 200)
def test_uncopyable_object(self):
ztapi.provideAdapter(IUncopyable,
copypastemove.interfaces.IObjectCopier,
ObjectNonCopier)
root = self.getRootFolder()
root['uncopyable'] = UncopyableFile()
transaction.commit()
response = self.publish('/@@contents.html',
basic='mgr:mgrpw',
form={'ids': [u'uncopyable'],
'container_copy_button': u'Copy'})
self.assertEqual(response.getStatus(), 200)
body = response.getBody()
self.assert_("cannot be copied" in body)
def test_unmoveable_object(self):
ztapi.provideAdapter(IImmovable,
copypastemove.interfaces.IObjectMover,
ObjectNonMover)
root = self.getRootFolder()
root['immovable'] = ImmovableFile()
transaction.commit()
response = self.publish('/@@contents.html',
basic='mgr:mgrpw',
form={'ids': [u'immovable'],
'container_cut_button': u'Cut'})
self.assertEqual(response.getStatus(), 200)
body = response.getBody()
self.assert_("cannot be moved" in body)
def test_suite():
suite = unittest.TestSuite()
Test.layer = AppContainerLayer
suite.addTest(unittest.makeSuite(Test))
index = FunctionalDocFileSuite("index.txt")
index.layer = AppContainerLayer
suite.addTest(index)
return suite
if __name__=='__main__':
unittest.main(defaultTest='test_suite')
<html metal:use-macro="context/@@standard_macros/page"
i18n:domain="zope">
<head>
<style metal:fill-slot="headers" type="text/css">
<!--
.ContentIcon {
width: 20px;
}
.ContentTitle {
text-align: left;
}
-->
</style>
</head>
<body>
<div metal:fill-slot="body">
<table
id="sortable" class="listing" summary="Content listing"
cellpadding="2" cellspacing="0"
i18n:attributes="summary">
<thead>
<tr>
<th>&nbsp;</th>
<th i18n:translate="">Name</th>
<th i18n:translate="">Title</th>
<th i18n:translate="">Created</th>
<th i18n:translate="">Modified</th>
</tr>
</thead>
<tbody>
<tr tal:repeat="info view/listContentInfo">
<td>
<a
href="#"
tal:attributes="href info/url"
tal:content="structure info/icon|default" />
</td>
<td class="ContentTitle">
<a href="subfolder_id"
tal:attributes="href info/url"
tal:content="info/id"
i18n:translate=""
>ID here</a>
</td>
<td><span tal:content="info/title|default"
i18n:translate="">&nbsp;</span></td>
<td><span tal:content="info/created|default"
i18n:translate="">&nbsp;</span></td>
<td><span tal:content="info/modified|default"
i18n:translate="">&nbsp;</span></td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta">
<meta:directives namespace="http://namespaces.zope.org/browser">
<!-- browser cotainer views -->
<meta:directive
name="containerViews"
schema=".metaconfigure.IContainerViews"
handler=".metaconfigure.containerViews"
/>
</meta:directives>
</configure>
\ No newline at end of file
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Container-specific browser ZCML namespace directive handlers
$Id$
"""
__docformat__ = 'restructuredtext'
from zope.interface import Interface
from zope.configuration.fields import GlobalObject
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
from zope.schema import Id
from zope.security.zcml import Permission
from zope.app.publisher.browser.viewmeta import page, view
from zope.app.container.browser.contents import Contents
from zope.app.container.browser.adding import Adding
from zope.app.component.back35 import LayerField
from zope.app.container.i18n import ZopeMessageFactory as _
class IContainerViews(Interface):
"""Define several container views for an `IContainer` implementation."""
for_ = GlobalObject(
title=u"The declaration this containerViews are for.",
description=u"""
The containerViews will be available for all objects that
provide this declaration.
""",
required=True)
contents = Permission(
title=u"The permission needed for content page.",
required=False)
index = Permission(
title=u"The permission needed for index page.",
required=False)
add = Permission(
title=u"The permission needed for add page.",
required=False)
layer = LayerField(
title=_("The layer the view is in."),
description=_("""A skin is composed of layers. It is common to put
skin specific views in a layer named after the skin. If the 'layer'
attribute is not supplied, it defaults to 'default'."""),
required=False
)
def containerViews(_context, for_, contents=None, add=None, index=None,
layer=IDefaultBrowserLayer):
"""Set up container views for a given content type."""
if for_ is None:
raise ValueError("A for interface must be specified.")
if contents is not None:
from zope.app.menus import zmi_views
page(_context, name='contents.html', permission=contents,
for_=for_, layer=layer, class_=Contents, attribute='contents',
menu=zmi_views, title=_('Contents'))
if index is not None:
page(_context, name='index.html', permission=index, for_=for_,
layer=layer, class_=Contents, attribute='index')
if add is not None:
from zope.app.menus import zmi_actions
viewObj = view(_context, name='+', layer=layer, menu=zmi_actions,
title=_('Add'), for_=for_, permission=add,
class_=Adding)
viewObj.page(_context, name='index.html', attribute='index')
viewObj.page(_context, name='action.html', attribute='action')
viewObj()
#
# This file is necessary to make this directory a package.
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Adding implementation tests
$Id$
"""
import unittest
import zope.interface
import zope.security.checker
from zope.component.interfaces import IFactory
from zope.component.interfaces import ComponentLookupError
from zope.interface import implements, Interface, directlyProvides
from zope.publisher.browser import TestRequest
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.publisher.browser import BrowserView
from zope.security.interfaces import ForbiddenAttribute
from zope.testing.doctestunit import DocTestSuite
from zope.exceptions.interfaces import UserError
from zope.traversing.browser import AbsoluteURL
from zope.traversing.browser.interfaces import IAbsoluteURL
from zope.traversing.interfaces import IContainmentRoot
from zope.app import zapi
from zope.app.testing import ztapi
from zope.app.testing.placelesssetup import PlacelessSetup, setUp, tearDown
from zope.app.publisher.interfaces.browser import AddMenu
from zope.app.publisher.interfaces.browser import IMenuItemType, IBrowserMenu
from zope.app.publisher.browser.menu import BrowserMenuItem, BrowserMenu
from zope.app.container.interfaces import IAdding
from zope.app.container.interfaces import IObjectAddedEvent
from zope.app.container.interfaces import IContainerNamesContainer
from zope.app.container.interfaces import INameChooser
from zope.app.container.interfaces import IContainer
from zope.app.container.contained import contained
from zope.app.container.browser.adding import Adding
from zope.app.container.sample import SampleContainer
class Root(object):
implements(IContainmentRoot)
class Container(SampleContainer):
pass
class CreationView(BrowserView):
def action(self):
return 'been there, done that'
class Content(object):
pass
class Factory(object):
implements(IFactory)
title = ''
description = ''
def getInterfaces(self):
return ()
def __call__(self):
return Content()
class AbsoluteURL(BrowserView):
def __str__(self):
if IContainmentRoot.providedBy(self.context):
return ''
name = self.context.__name__
url = zapi.absoluteURL(zapi.getParent(self.context), self.request)
url += '/' + name
return url
__call__ = __str__
def defineMenuItem(menuItemType, for_, action, title=u'', extra=None):
newclass = type(title, (BrowserMenuItem,),
{'title':title, 'action':action,
'_for': for_, 'extra':extra})
zope.interface.classImplements(newclass, menuItemType)
ztapi.provideAdapter((for_, IBrowserRequest), menuItemType, newclass, title)
def registerAddMenu():
ztapi.provideUtility(IMenuItemType, AddMenu, 'zope.app.container.add')
ztapi.provideUtility(IBrowserMenu,
BrowserMenu('zope.app.container.add', u'', u''),
'zope.app.container.add')
class Test(PlacelessSetup, unittest.TestCase):
def setUp(self):
super(Test, self).setUp()
def test(self):
container = Container()
request = TestRequest()
adding = Adding(container, request)
ztapi.browserView(IAdding, "Thing", CreationView)
self.assertEqual(adding.contentName, None)
view = adding.publishTraverse(request, 'Thing=foo')
self.assertEqual(view.action(), 'been there, done that')
self.assertEqual(adding.contentName, 'foo')
o = object()
result = adding.add(o)
# Check the state of the container and result
self.assertEqual(container["foo"], o)
self.assertEqual(result, o)
def testNoNameGiven(self):
container = Container()
request = TestRequest()
adding = Adding(container, request)
ztapi.browserView(IAdding, "Thing", CreationView)
self.assertEqual(adding.contentName, None)
view = adding.publishTraverse(request, 'Thing=')
self.assertEqual(adding.contentName, '')
def testAction(self):
# make a private factory
ztapi.provideUtility(IFactory, Factory(), 'fooprivate')
factory = Factory()
factory.__Security_checker__ = zope.security.checker.NamesChecker(
['__call__'])
ztapi.provideUtility(IFactory, factory, 'foo')
container = Container()
adding = Adding(container, TestRequest())
adding.nextURL = lambda: '.'
adding.nameAllowed = lambda: True
# we can't use a private factory:
self.assertRaises(ForbiddenAttribute,
adding.action, type_name='fooprivate', id='bar')
# typical add - id is provided by user
adding.action(type_name='foo', id='bar')
self.assert_('bar' in container)
# missing type_name
self.assertRaises(UserError, adding.action, id='bar')
# missing id
self.assertRaises(UserError, adding.action, type_name='foo')
# bad type_name
self.assertRaises(ComponentLookupError, adding.action,
type_name='***', id='bar')
# alternative add - id is provided internally instead of from user
adding.nameAllowed = lambda: False
adding.contentName = 'baz'
adding.action(type_name='foo')
self.assert_('baz' in container)
# alternative add w/missing contentName
# Note: Passing is None as object name might be okay, if the container
# is able to hand out ids itself. Let's not require a content
# name to be specified!
# For the container, (or really, the chooser, to choose, we have to
# marke the container as a ContainerNamesContainer
directlyProvides(container, IContainerNamesContainer)
adding.contentName = None
adding.action(type_name='foo')
self.assert_('Content' in container)
def test_action(self):
container = Container()
container = contained(container, Root(), "container")
request = TestRequest()
adding = Adding(container, request)
adding.__name__ = '+'
ztapi.browserView(IAdding, "Thing", CreationView)
ztapi.browserView(Interface, "absolute_url", AbsoluteURL)
ztapi.browserView(None, '', AbsoluteURL, providing=IAbsoluteURL)
self.assertRaises(UserError, adding.action, '', 'foo')
adding.action('Thing', 'foo')
self.assertEqual(adding.request.response.getHeader('location'),
'/container/+/Thing=foo')
adding.action('Thing/screen1', 'foo')
self.assertEqual(adding.request.response.getHeader('location'),
'/container/+/Thing/screen1=foo')
def test_publishTraverse_factory(self):
factory = Factory()
ztapi.provideUtility(IFactory, factory, 'foo')
container = Container()
request = TestRequest()
adding = Adding(container, request)
self.assert_(adding.publishTraverse(request, 'foo') is factory)
def test_constraint_driven_addingInfo():
"""
>>> registerAddMenu()
>>> class TestMenu(zope.interface.Interface):
... pass
>>> zope.interface.directlyProvides(TestMenu, IMenuItemType)
>>> ztapi.provideUtility(IMenuItemType, TestMenu, 'TestMenu')
>>> ztapi.provideUtility(IBrowserMenu, BrowserMenu('TestMenu', u'', u''),
... 'TestMenu')
>>> defineMenuItem(TestMenu, IAdding, '', 'item1')
>>> defineMenuItem(TestMenu, IAdding, '', 'item2')
>>> defineMenuItem(AddMenu, IAdding, '', 'item3', extra={'factory': 'f1'})
>>> defineMenuItem(AddMenu, IAdding, '', 'item4', extra={'factory': 'f2'})
>>> class F1(object):
... pass
>>> class F2(object):
... pass
>>> def pre(container, name, object):
... if not isinstance(object, F1):
... raise zope.interface.Invalid()
>>> def prefactory(container, name, factory):
... if factory._callable is not F1:
... raise zope.interface.Invalid()
>>> pre.factory = prefactory
>>> class IContainer(zope.interface.Interface):
... def __setitem__(name, object):
... pass
... __setitem__.precondition = pre
>>> class Container(object):
... zope.interface.implements(IContainer)
>>> from zope.component.factory import Factory
>>> ztapi.provideUtility(IFactory, Factory(F1), 'f1')
>>> ztapi.provideUtility(IFactory, Factory(F2), 'f2')
>>> from zope.app.container.browser.adding import Adding
>>> adding = Adding(Container(), TestRequest())
>>> items = adding.addingInfo()
>>> len(items)
1
>>> items[0]['title']
'item3'
>>> adding.menu_id = 'TestMenu'
>>> items = adding.addingInfo()
>>> len(items)
3
>>> items[0]['title']
'item1'
>>> items[1]['title']
'item2'
>>> items[2]['title']
'item3'
"""
def test_constraint_driven_add():
"""
>>> from zope.app.container.sample import SampleContainer
>>> from zope.app.container.browser.adding import Adding
>>> class F1(object):
... pass
>>> class F2(object):
... pass
>>> def pre(container, name, object):
... "a mock item constraint "
... if not isinstance(object, F1):
... raise zope.interface.Invalid('not a valid child')
>>> class ITestContainer(zope.interface.Interface):
... def __setitem__(name, object):
... pass
... __setitem__.precondition = pre
>>> class Container(SampleContainer):
... zope.interface.implements(ITestContainer)
>>> adding = Adding(Container(), TestRequest())
>>> c = adding.add(F1())
This test should fail, because the container only
accepts instances of F1
>>> adding.add(F2())
Traceback (most recent call last):
...
Invalid: not a valid child
>>> class ValidContainer(SampleContainer):
... zope.interface.implements(ITestContainer)
>>> def constr(container):
... "a mock container constraint"
... if not isinstance(container, ValidContainer):
... raise zope.interface.Invalid('not a valid container')
... return True
>>> class I2(zope.interface.Interface):
... __parent__ = zope.schema.Field(constraint = constr)
>>> zope.interface.classImplements(F1, I2)
This adding now fails, because the Container is not a valid
parent for F1
>>> c = adding.add(F1())
Traceback (most recent call last):
...
Invalid: not a valid container
>>> adding = Adding(ValidContainer(), TestRequest())
>>> c = adding.add(F1())
"""
def test_nameAllowed():
"""
Test for nameAllowed in adding.py
>>> from zope.app.container.browser.adding import Adding
>>> from zope.app.container.interfaces import IContainerNamesContainer
Class implements IContainerNamesContainer
>>> class FakeContainer(object):
... zope.interface.implements(IContainerNamesContainer)
nameAllowed returns False if the class imlements
IContainerNamesContainer
>>> adding = Adding(FakeContainer(),TestRequest())
>>> adding.nameAllowed()
False
Fake class without IContainerNamesContainer
>>> class Fake(object):
... pass
nameAllowed returns True if the class
doesn't imlement IContainerNamesContainer
>>> adding = Adding(Fake(),TestRequest())
>>> adding.nameAllowed()
True
"""
def test_chooseName():
"""If user don't enter name, pick one
>>> class MyContainer(object):
... args = {}
... zope.interface.implements(INameChooser, IContainer)
... def chooseName(self, name, object):
... self.args["choose"] = name, object
... return 'pickone'
... def checkName(self, name, object):
... self.args["check"] = name, object
... def __setitem__(self, name, object):
... setattr(self, name, object)
... self.name = name
... def __getitem__(self, key):
... return getattr(self, key)
>>> request = TestRequest()
>>> mycontainer = MyContainer()
>>> adding = Adding(mycontainer, request)
>>> o = object()
>>> add_obj = adding.add(o)
>>> mycontainer.name
'pickone'
>>> add_obj is o
True
Make sure right arguments passed to INameChooser adapter:
>>> name, obj = mycontainer.args["choose"]
>>> name
''
>>> obj is o
True
>>> name, obj = mycontainer.args["check"]
>>> name
'pickone'
>>> obj is o
True
"""
def test_SingleMenuItem_and_CustomAddView_NonICNC():
"""
This tests the condition if the content has Custom Add views and
the container contains only a single content object
>>> registerAddMenu()
>>> defineMenuItem(AddMenu, IAdding, '', 'item3', extra={'factory': 'f1'})
>>> class F1(object):
... pass
>>> class F2(object):
... pass
>>> def pre(container, name, object):
... if not isinstance(object, F1):
... raise zope.interface.Invalid()
>>> def prefactory(container, name, factory):
... if factory._callable is not F1:
... raise zope.interface.Invalid()
>>> pre.factory = prefactory
>>> class IContainer(zope.interface.Interface):
... def __setitem__(name, object):
... pass
... __setitem__.precondition = pre
>>> class Container(object):
... zope.interface.implements(IContainer)
>>> from zope.component.factory import Factory
>>> ztapi.provideUtility(IFactory, Factory(F1), 'f1')
>>> ztapi.provideUtility(IFactory, Factory(F2), 'f2')
>>> from zope.app.container.browser.adding import Adding
>>> adding = Adding(Container(), TestRequest())
>>> items = adding.addingInfo()
>>> len(items)
1
isSingleMenuItem returns True if there is only one content class
inside the Container
>>> adding.isSingleMenuItem()
True
hasCustomAddView will return False as the content does not have
a custom Add View
>>> adding.hasCustomAddView()
True
"""
def test_SingleMenuItem_and_NoCustomAddView_NonICNC():
"""
This function checks the case where there is a single content object
and there is non custom add view . Also the container does not
implement IContainerNamesContainer
>>> registerAddMenu()
>>> defineMenuItem(AddMenu, None, '', 'item3', extra={'factory': ''})
>>> class F1(object):
... pass
>>> class F2(object):
... pass
>>> def pre(container, name, object):
... if not isinstance(object, F1):
... raise zope.interface.Invalid()
>>> def prefactory(container, name, factory):
... if factory._callable is not F1:
... raise zope.interface.Invalid()
>>> pre.factory = prefactory
>>> class IContainer(zope.interface.Interface):
... def __setitem__(name, object):
... pass
... __setitem__.precondition = pre
>>> class Container(object):
... zope.interface.implements(IContainer)
>>> from zope.component.factory import Factory
>>> ztapi.provideUtility(IFactory, Factory(F1), 'f1')
>>> ztapi.provideUtility(IFactory, Factory(F2), 'f2')
>>> from zope.app.container.browser.adding import Adding
>>> adding = Adding(Container(), TestRequest())
>>> items = adding.addingInfo()
>>> len(items)
1
The isSingleMenuItem will return True if there is one single content
that can be added inside the Container
>>> adding.isSingleMenuItem()
True
hasCustomAddView will return False as the content does not have
a custom Add View
>>> adding.hasCustomAddView()
False
"""
def test_isSingleMenuItem_with_ICNC():
"""
This test checks for whether there is a single content that can be added
and the container uses IContainerNamesContaienr
>>> registerAddMenu()
>>> defineMenuItem(AddMenu, None, '', 'item3', extra={'factory': ''})
>>> class F1(object):
... pass
>>> class F2(object):
... pass
>>> def pre(container, name, object):
... if not isinstance(object, F1):
... raise zope.interface.Invalid()
>>> def prefactory(container, name, factory):
... if factory._callable is not F1:
... raise zope.interface.Invalid()
>>> pre.factory = prefactory
>>> class IContainer(zope.interface.Interface):
... def __setitem__(name, object):
... pass
... __setitem__.precondition = pre
>>> class Container(object):
... zope.interface.implements(IContainer, IContainerNamesContainer)
>>> from zope.app.container.browser.adding import Adding
>>> adding = Adding(Container(), TestRequest())
>>> items = adding.addingInfo()
>>> len(items)
1
>>> adding.isSingleMenuItem()
True
>>> adding.hasCustomAddView()
False
"""
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(Test),
DocTestSuite(setUp=setUp, tearDown=tearDown),
))
if __name__=='__main__':
unittest.main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Test Container Contents
$Id$
"""
from unittest import TestCase, TestSuite, main, makeSuite
from zope.interface import Interface, implements
from zope.security import checker
from zope.traversing.api import traverse
from zope.annotation.interfaces import IAnnotations
from zope.copypastemove import ContainerItemRenamer
from zope.copypastemove import ObjectMover, ObjectCopier
from zope.copypastemove import PrincipalClipboard
from zope.copypastemove.interfaces import IContainerItemRenamer
from zope.copypastemove.interfaces import IObjectMover, IObjectCopier
from zope.copypastemove.interfaces import IPrincipalClipboard
from zope.app.component.testing import PlacefulSetup
from zope.app.container.contained import contained
from zope.app.testing import ztapi
from zope.app.container.interfaces import IContainer, IContained
class BaseTestContentsBrowserView(PlacefulSetup):
"""Base class for testing browser contents.
Subclasses need to define a method, '_TestView__newContext', that
takes no arguments and that returns a new empty test view context.
Subclasses need to define a method, '_TestView__newView', that
takes a context object and that returns a new test view.
"""
def setUp(self):
PlacefulSetup.setUp(self)
PlacefulSetup.buildFolders(self)
ztapi.provideAdapter(IContained, IObjectCopier, ObjectCopier)
ztapi.provideAdapter(IContained, IObjectMover, ObjectMover)
ztapi.provideAdapter(IContainer, IContainerItemRenamer,
ContainerItemRenamer)
ztapi.provideAdapter(IAnnotations, IPrincipalClipboard,
PrincipalClipboard)
ztapi.provideAdapter(Principal, IAnnotations,
PrincipalAnnotations)
def testInfo(self):
# Do we get the correct information back from ContainerContents?
container = self._TestView__newContext()
subcontainer = self._TestView__newContext()
container['subcontainer'] = subcontainer
document = Document()
container['document'] = document
fc = self._TestView__newView(container)
info_list = fc.listContentInfo()
self.assertEquals(len(info_list), 2)
ids = map(lambda x: x['id'], info_list)
self.assert_('subcontainer' in ids)
objects = map(lambda x: x['object'], info_list)
self.assert_(subcontainer in objects)
urls = map(lambda x: x['url'], info_list)
self.assert_('subcontainer' in urls)
self.failIf(filter(None, map(lambda x: x['icon'], info_list)))
def testInfoUnicode(self):
# If the id contains non-ASCII characters, url has to be quoted
container = self._TestView__newContext()
subcontainer = self._TestView__newContext()
container[u'f\xf6\xf6'] = subcontainer
fc = self._TestView__newView(container)
info_list = fc.listContentInfo()
urls = map(lambda x: x['url'], info_list)
self.assert_('f%C3%B6%C3%B6' in urls)
def testInfoWDublinCore(self):
container = self._TestView__newContext()
document = Document()
container['document'] = document
from datetime import datetime
from zope.dublincore.interfaces import IZopeDublinCore
class FauxDCAdapter(object):
implements(IZopeDublinCore)
__Security_checker__ = checker.Checker(
{"created": "zope.Public",
"modified": "zope.Public",
"title": "zope.Public",
},
{"title": "zope.app.dublincore.change"})
def __init__(self, context):
pass
title = 'faux title'
size = 1024
created = datetime(2001, 1, 1, 1, 1, 1)
modified = datetime(2002, 2, 2, 2, 2, 2)
ztapi.provideAdapter(IDocument, IZopeDublinCore, FauxDCAdapter)
fc = self._TestView__newView(container)
info = fc.listContentInfo()[0]
self.assertEqual(info['id'], 'document')
self.assertEqual(info['url'], 'document')
self.assertEqual(info['object'], document)
self.assertEqual(info['title'], 'faux title')
self.assertEqual(info['created'], '01/01/01 01:01')
self.assertEqual(info['modified'], '02/02/02 02:02')
def testRemove(self):
container = self._TestView__newContext()
subcontainer = self._TestView__newContext()
container['subcontainer'] = subcontainer
document = Document()
container['document'] = document
document2 = Document()
container['document2'] = document2
fc = self._TestView__newView(container)
fc.request.form.update({'ids': ['document2']})
fc.removeObjects()
info_list = fc.listContentInfo()
self.assertEquals(len(info_list), 2)
ids = map(lambda x: x['id'], info_list)
self.assert_('subcontainer' in ids)
objects = map(lambda x: x['object'], info_list)
self.assert_(subcontainer in objects)
urls = map(lambda x: x['url'], info_list)
self.assert_('subcontainer' in urls)
class IDocument(Interface):
pass
class Document(object):
implements(IDocument)
class Principal(object):
id = 'bob'
class PrincipalAnnotations(dict):
implements(IAnnotations)
data = {}
def __new__(class_, context):
try:
annotations = class_.data[context.id]
except KeyError:
annotations = dict.__new__(class_)
class_.data[context.id] = annotations
return annotations
def __init__(self, context):
pass
def __repr__(self):
return "<%s.PrincipalAnnotations object>" % __name__
class TestCutCopyPaste(PlacefulSetup, TestCase):
def setUp(self):
PlacefulSetup.setUp(self)
PlacefulSetup.buildFolders(self)
ztapi.provideAdapter(IContained, IObjectCopier, ObjectCopier)
ztapi.provideAdapter(IContained, IObjectMover, ObjectMover)
ztapi.provideAdapter(IContainer, IContainerItemRenamer,
ContainerItemRenamer)
ztapi.provideAdapter(IAnnotations, IPrincipalClipboard,
PrincipalClipboard)
ztapi.provideAdapter(Principal, IAnnotations,
PrincipalAnnotations)
def testRename(self):
container = traverse(self.rootFolder, 'folder1')
fc = self._TestView__newView(container)
ids=['document1', 'document2']
for id in ids:
document = Document()
container[id] = document
fc.request.form.update({'rename_ids': ids,
'new_value': ['document1_1', 'document2_2']
})
fc.renameObjects()
self.failIf('document1_1' not in container)
self.failIf('document1' in container)
def testCopyPaste(self):
container = traverse(self.rootFolder, 'folder1')
fc = self._TestView__newView(container)
ids=['document1', 'document2']
for id in ids:
document = Document()
container[id] = document
fc.request.form['ids'] = ids
fc.copyObjects()
fc.pasteObjects()
self.failIf('document1' not in container)
self.failIf('document2' not in container)
self.failIf('document1-2' not in container)
self.failIf('document2-2' not in container)
def testCopyFolder(self):
container = traverse(self.rootFolder, 'folder1')
fc = self._TestView__newView(container)
ids = ['folder1_1']
fc.request.form['ids'] = ids
fc.copyObjects()
fc.pasteObjects()
self.failIf('folder1_1' not in container)
self.failIf('folder1_1-2' not in container)
def testCopyFolder2(self):
container = traverse(self.rootFolder, '/folder1/folder1_1')
fc = self._TestView__newView(container)
ids = ['folder1_1_1']
fc.request.form['ids'] = ids
fc.copyObjects()
fc.pasteObjects()
self.failIf('folder1_1_1' not in container)
self.failIf('folder1_1_1-2' not in container)
def testCopyFolder3(self):
container = traverse(self.rootFolder, '/folder1/folder1_1')
target = traverse(self.rootFolder, '/folder2/folder2_1')
fc = self._TestView__newView(container)
tg = self._TestView__newView(target)
ids = ['folder1_1_1']
fc.request.form['ids'] = ids
fc.copyObjects()
tg.pasteObjects()
self.failIf('folder1_1_1' not in container)
self.failIf('folder1_1_1' not in target)
def testCutPaste(self):
container = traverse(self.rootFolder, 'folder1')
fc = self._TestView__newView(container)
ids=['document1', 'document2']
for id in ids:
document = Document()
container[id] = document
fc.request.form['ids'] = ids
fc.cutObjects()
fc.pasteObjects()
self.failIf('document1' not in container)
self.failIf('document2' not in container)
def testCutFolder(self):
container = traverse(self.rootFolder, 'folder1')
fc = self._TestView__newView(container)
ids = ['folder1_1']
fc.request.form['ids'] = ids
fc.cutObjects()
fc.pasteObjects()
self.failIf('folder1_1' not in container)
def testCutFolder2(self):
container = traverse(self.rootFolder, '/folder1/folder1_1')
fc = self._TestView__newView(container)
ids = ['folder1_1_1']
fc.request.form['ids'] = ids
fc.cutObjects()
fc.pasteObjects()
self.failIf('folder1_1_1' not in container)
def testCutFolder3(self):
container = traverse(self.rootFolder, '/folder1/folder1_1')
target = traverse(self.rootFolder, '/folder2/folder2_1')
fc = self._TestView__newView(container)
tg = self._TestView__newView(target)
ids = ['folder1_1_1']
fc.request.form['ids'] = ids
fc.cutObjects()
tg.pasteObjects()
self.failIf('folder1_1_1' in container)
self.failIf('folder1_1_1' not in target)
def _TestView__newView(self, container):
from zope.app.container.browser.contents import Contents
from zope.publisher.browser import TestRequest
request = TestRequest()
request.setPrincipal(Principal())
return Contents(container, request)
class Test(BaseTestContentsBrowserView, TestCase):
def _TestView__newContext(self):
from zope.app.container.sample import SampleContainer
from zope.app.folder import rootFolder
root = rootFolder()
container = SampleContainer()
return contained(container, root, 'sample')
def _TestView__newView(self, container):
from zope.app.container.browser.contents import Contents
from zope.publisher.browser import TestRequest
request = TestRequest()
request.setPrincipal(Principal())
return Contents(container, request)
def test_suite():
return TestSuite((
makeSuite(Test),
makeSuite(TestCutCopyPaste),
))
if __name__=='__main__':
main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""'containerView' directive test
$Id$
"""
import re
import pprint
import cStringIO
import unittest
from zope.interface import Interface
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.testing.doctestunit import DocTestSuite
from zope.app.container.browser.metaconfigure import containerViews
atre = re.compile(' at [0-9a-fA-Fx]+')
class Context(object):
actions = ()
info = ''
def action(self, discriminator, callable, args):
self.actions += ((discriminator, callable, args), )
self.info = 'info'
def __repr__(self):
stream = cStringIO.StringIO()
pprinter = pprint.PrettyPrinter(stream=stream, width=60)
pprinter.pprint(self.actions)
r = stream.getvalue()
return (''.join(atre.split(r))).strip()
class I(Interface):
pass
class ITestLayer(IBrowserRequest):
pass
def test_containerViews():
"""
>>> from zope.app.publisher.browser.menumeta import menus
>>> from zope.interface.interface import InterfaceClass
>>> zmi_views = InterfaceClass('zmi_views', __module__='zope.app.menus')
>>> menus.zmi_views = zmi_views
>>> zmi_actions = InterfaceClass('zmi_actions', __module__='zope.app.menus')
>>> menus.zmi_actions = zmi_actions
>>> context = Context()
>>> containerViews(context, for_=I, contents='zope.ManageContent',
... add='zope.ManageContent', index='zope.View')
>>> context
((('adapter',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.app.menus.zmi_views>,
u'Contents'),
<function handler>,
('registerAdapter',
<zope.app.publisher.browser.menumeta.MenuItemFactory object>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.app.menus.zmi_views>,
u'Contents',
'')),
(None,
<function provideInterface>,
('', <InterfaceClass zope.app.menus.zmi_views>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(('view',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
'contents.html',
<InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.Contents'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.interface.Interface>,
'contents.html',
'info')),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(('view',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
'index.html',
<InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.Contents'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.interface.Interface>,
'index.html',
'info')),
(('adapter',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.app.menus.zmi_actions>,
u'Add'),
<function handler>,
('registerAdapter',
<zope.app.publisher.browser.menumeta.MenuItemFactory object>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.app.menus.zmi_actions>,
u'Add',
'info')),
(None,
<function provideInterface>,
('', <InterfaceClass zope.app.menus.zmi_actions>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('', <InterfaceClass zope.interface.Interface>)),
(('view',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
'+',
<InterfaceClass zope.interface.Interface>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.+'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
<InterfaceClass zope.interface.Interface>,
'+',
'info')))
"""
def test_containerViews_layer():
"""
>>> from zope.app.publisher.browser.menumeta import menus
>>> from zope.interface.interface import InterfaceClass
>>> zmi_views = InterfaceClass('zmi_views', __module__='zope.app.menus')
>>> menus.zmi_views = zmi_views
>>> zmi_actions = InterfaceClass('zmi_actions', __module__='zope.app.menus')
>>> menus.zmi_actions = zmi_actions
>>> context = Context()
>>> containerViews(context, for_=I, contents='zope.ManageContent',
... add='zope.ManageContent', index='zope.View', layer=ITestLayer)
>>> context
((('adapter',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.app.menus.zmi_views>,
u'Contents'),
<function handler>,
('registerAdapter',
<zope.app.publisher.browser.menumeta.MenuItemFactory object>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.app.menus.zmi_views>,
u'Contents',
'')),
(None,
<function provideInterface>,
('', <InterfaceClass zope.app.menus.zmi_views>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(('view',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
'contents.html',
<InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.Contents'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.interface.Interface>,
'contents.html',
'info')),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(('view',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
'index.html',
<InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.Contents'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.interface.Interface>,
'index.html',
'info')),
(('adapter',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.app.menus.zmi_actions>,
u'Add'),
<function handler>,
('registerAdapter',
<zope.app.publisher.browser.menumeta.MenuItemFactory object>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.app.menus.zmi_actions>,
u'Add',
'info')),
(None,
<function provideInterface>,
('', <InterfaceClass zope.app.menus.zmi_actions>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>)),
(None,
<function provideInterface>,
('',
<InterfaceClass zope.app.container.browser.tests.test_directive.I>)),
(None,
<function provideInterface>,
('', <InterfaceClass zope.interface.Interface>)),
(('view',
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
'+',
<InterfaceClass zope.interface.Interface>),
<function handler>,
('registerAdapter',
<class 'zope.app.publisher.browser.viewmeta.+'>,
(<InterfaceClass zope.app.container.browser.tests.test_directive.I>,
<InterfaceClass zope.app.container.browser.tests.test_directive.ITestLayer>),
<InterfaceClass zope.interface.Interface>,
'+',
'info')))
"""
def test_suite():
return unittest.TestSuite((
DocTestSuite(),
))
if __name__ == '__main__':
unittest.main()
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""This module provides a sample container implementation.
This is primarily for testing purposes.
It might be useful as a mix-in for some classes, but many classes will
need a very different implementation.
$Id$
"""
__docformat__ = 'restructuredtext'
from persistent import Persistent
from BTrees.OOBTree import OOBTree
from zope.app.container.sample import SampleContainer
class BTreeContainer(SampleContainer, Persistent):
# implements(what my base classes implement)
# TODO: It appears that BTreeContainer uses SampleContainer only to
# get the implementation of __setitem__(). All the other methods
# provided by that base class are just slower replacements for
# operations on the BTree itself. It would probably be clearer to
# just delegate those methods directly to the btree.
def _newContainerData(self):
"""Construct an item-data container
Subclasses should override this if they want different data.
The value returned is a mapping object that also has get,
has_key, keys, items, and values methods.
"""
return OOBTree()
def __contains__(self, key):
'''See interface IReadContainer
Reimplement this method, since has_key() returns the key if available,
while we expect True or False.
>>> c = BTreeContainer()
>>> "a" in c
False
>>> c["a"] = 1
>>> "a" in c
True
>>> "A" in c
False
'''
return key in self._SampleContainer__data
has_key = __contains__
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
i18n_domain="zope"
>
<adapter
provides=".interfaces.IFind"
for=".interfaces.IReadContainer"
permission="zope.ManageContent"
factory="zope.app.container.find.FindAdapter"
/>
<adapter
for=".interfaces.IReadContainer"
provides="zope.filerepresentation.interfaces.IReadDirectory"
factory=".directory.noop"
/>
<adapter
for=".interfaces.IWriteContainer"
provides="zope.filerepresentation.interfaces.IWriteDirectory"
factory=".directory.noop"
/>
<adapter
factory="zope.app.container.traversal.ContainerTraversable"
provides="zope.traversing.interfaces.ITraversable"
for="zope.app.container.interfaces.IReadContainer"
/>
<adapter
factory="zope.app.container.size.ContainerSized"
provides="zope.size.interfaces.ISized"
for="zope.app.container.interfaces.IReadContainer"
/>
<adapter
provides=".interfaces.INameChooser"
for="zope.app.container.interfaces.IWriteContainer"
factory=".contained.NameChooser"
/>
<subscriber
handler=".dependency.CheckDependency"
for="zope.app.container.interfaces.IObjectRemovedEvent"
trusted="y"
/>
<subscriber
for="zope.location.interfaces.ILocation
zope.app.container.interfaces.IObjectMovedEvent"
handler=".contained.dispatchToSublocations"
>
Handler dispatches moved events to sublocations of the original object.
</subscriber>
<adapter
provides="zope.location.interfaces.ISublocations"
for="zope.app.container.interfaces.IReadContainer"
factory=".contained.ContainerSublocations"
/>
<class class=".constraints.ItemTypePrecondition">
<allow interface=".constraints.IItemTypePrecondition" />
</class>
<view
for="zope.app.container.interfaces.IItemContainer"
type="zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.publisher.interfaces.browser.IBrowserPublisher"
factory="zope.app.container.traversal.ItemTraverser"
permission="zope.Public"
allowed_interface="zope.publisher.interfaces.browser.IBrowserPublisher"
/>
<view
for="zope.app.container.interfaces.ISimpleReadContainer"
type="zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.publisher.interfaces.browser.IBrowserPublisher"
factory="zope.app.container.traversal.ItemTraverser"
permission="zope.Public"
allowed_interface="zope.publisher.interfaces.browser.IBrowserPublisher"
/>
</configure>
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Support for containment constraints
Either a container or an object can provide constraints on the
containment relationship.
A container expresses constraints through a precondition on it's
`__setitem__` method in it's interface.
Preconditions can be simple callable objects, like functions. They
should raise a ``zope.interface.Invalid`` exception to indicate that a
constraint isn't satisfied:
>>> def preNoZ(container, name, ob):
... "Silly precondition example"
... if name.startswith("Z"):
... raise zope.interface.Invalid("Names can not start with Z")
>>> class I1(zope.interface.Interface):
... def __setitem__(name, on):
... "Add an item"
... __setitem__.precondition = preNoZ
>>> from zope.app.container.interfaces import IContainer
>>> class C1(object):
... zope.interface.implements(I1, IContainer)
... def __repr__(self):
... return 'C1'
Given such a precondition, we can then check whether an object can be
added:
>>> c1 = C1()
>>> checkObject(c1, "bob", None)
>>> checkObject(c1, "Zbob", None)
Traceback (most recent call last):
...
Invalid: Names can not start with Z
We can also express constaints on the containers an object can be
added to. We do this by setting a field constraint on an object's
`__parent__` attribute:
>>> import zope.schema
A field constraint is a callable object that returns a boolean value:
>>> def con1(container):
... "silly container constraint"
... if not hasattr(container, 'x'):
... return False
... return True
>>> class I2(zope.interface.Interface):
... __parent__ = zope.schema.Field(constraint = con1)
>>> class O(object):
... zope.interface.implements(I2)
If the constraint isn't satisfied, we'll get a validation error when we
check whether the object can be added:
>>> checkObject(c1, "bob", O())
Traceback (most recent call last):
...
ConstraintNotSatisfied: C1
Note that the validation error isn't very informative. For that
reason, it's better for constraints to raise Invalid errors when they
aren't satisfied:
>>> def con1(container):
... "silly container constraint"
... if not hasattr(container, 'x'):
... raise zope.interface.Invalid("What, no x?")
... return True
>>> class I2(zope.interface.Interface):
... __parent__ = zope.schema.Field(constraint = con1)
>>> class O(object):
... zope.interface.implements(I2)
>>> checkObject(c1, "bob", O())
Traceback (most recent call last):
...
Invalid: What, no x?
>>> c1.x = 1
>>> checkObject(c1, "bob", O())
The `checkObject` function is handy when checking whether we can add an
existing object to a container, but, sometimes, we want to check
whether an object produced by a factory can be added. To do this, we
use `checkFactory`:
>>> class Factory(object):
... def __call__(self):
... return O()
... def getInterfaces(self):
... return zope.interface.implementedBy(O)
>>> factory = Factory()
>>> checkFactory(c1, "bob", factory)
True
>>> del c1.x
>>> checkFactory(c1, "bob", factory)
False
Unlike `checkObject`, `checkFactory`:
- Returns a boolean value
- Takes a factory (e.g. a class) rather than an argument.
The container constraint we defined for C1 isn't actually used to
check the factory:
>>> c1.x = 1
>>> checkFactory(c1, "Zbob", factory)
True
To work with `checkFactory`, a container precondition has to
implement a factory method. This is because a factory, rather than
an object is passed. To illustrate this, we'll make preNoZ its own
factory method:
>>> preNoZ.factory = preNoZ
We can do this (silly thing) because preNoZ doesn't use the object
argument.
>>> checkFactory(c1, "Zbob", factory)
False
$Id$
"""
__docformat__ = 'restructuredtext'
import sys
from zope.cachedescriptors.property import readproperty
from zope.dottedname.resolve import resolve
import zope.schema
from zope.interface import providedBy
from zope.app.container.interfaces import InvalidItemType, InvalidContainerType
from zope.app.container.i18n import ZopeMessageFactory as _
from zope.app.container.interfaces import IContainer
def checkObject(container, name, object):
"""Check containement constraints for an object and container
"""
# check __setitem__ precondition
containerProvided = providedBy(container)
__setitem__ = containerProvided.get('__setitem__')
if __setitem__ is not None:
precondition = __setitem__.queryTaggedValue('precondition')
if precondition is not None:
precondition(container, name, object)
# check the constraint on __parent__
__parent__ = providedBy(object).get('__parent__')
if __parent__ is not None:
try:
validate = __parent__.validate
except AttributeError:
pass
else:
validate(container)
if not containerProvided.extends(IContainer):
# If it doesn't implement IContainer, it can't contain stuff.
raise TypeError(
_('Container is not a valid Zope container.')
)
def checkFactory(container, name, factory):
__setitem__ = providedBy(container).get('__setitem__')
if __setitem__ is not None:
precondition = __setitem__.queryTaggedValue('precondition')
if precondition is not None:
try:
precondition = precondition.factory
except AttributeError:
pass
else:
try:
precondition(container, name, factory)
except zope.interface.Invalid:
return False
# check the constraint on __parent__
__parent__ = factory.getInterfaces().get('__parent__')
if __parent__ is not None:
try:
validate = __parent__.validate
except AttributeError:
pass
else:
try:
validate(container)
except zope.interface.Invalid:
return False
return True
class IItemTypePrecondition(zope.interface.Interface):
def __call__(container, name, object):
"""Test whether container setitem arguments are valid.
Raise zope.interface.Invalid if the object is invalid.
"""
def factory(container, name, factory):
"""Test whether objects provided by the factory are acceptable
Return a boolean value.
"""
class _TypesBased(object):
@readproperty
def types(self):
raw_types, module = self.raw_types
types = []
for t in raw_types:
if isinstance(t, str):
t = resolve(t, module)
types.append(t)
self.types = types
return types
def __init__(self, *types, **kw):
if [t for t in types if isinstance(t, str)]:
# have dotted names
module = kw.get('module', sys._getframe(1).f_globals['__name__'])
self.raw_types = types, module
else:
self.types = types
class ItemTypePrecondition(_TypesBased):
"""Specify a `__setitem__` precondition that restricts item types
Items must be one of the given types.
>>> class I1(zope.interface.Interface):
... pass
>>> class I2(zope.interface.Interface):
... pass
>>> precondition = ItemTypePrecondition(I1, I2)
>>> class Ob(object):
... pass
>>> ob = Ob()
>>> class Factory(object):
... def __call__(self):
... return Ob()
... def getInterfaces(self):
... return zope.interface.implementedBy(Ob)
>>> factory = Factory()
>>> try:
... precondition(None, 'foo', ob)
... except InvalidItemType, v:
... print v[0], (v[1] is ob), (v[2] == (I1, I2))
... else:
... print 'Should have failed'
None True True
>>> try:
... precondition.factory(None, 'foo', factory)
... except InvalidItemType, v:
... print v[0], (v[1] is factory), (v[2] == (I1, I2))
... else:
... print 'Should have failed'
None True True
>>> zope.interface.classImplements(Ob, I2)
>>> precondition(None, 'foo', ob)
>>> precondition.factory(None, 'foo', factory)
"""
zope.interface.implements(IItemTypePrecondition)
def __call__(self, container, name, object):
for iface in self.types:
if iface.providedBy(object):
return
raise InvalidItemType(container, object, self.types)
def factory(self, container, name, factory):
implemented = factory.getInterfaces()
for iface in self.types:
if implemented.isOrExtends(iface):
return
raise InvalidItemType(container, factory, self.types)
def contains(*types):
"""Declare that a container type contains only the given types
This is used within a class suite defining an interface to create
a __setitem__ specification with a precondition allowing only the
given types:
>>> class IFoo(zope.interface.Interface):
... pass
>>> class IBar(zope.interface.Interface):
... pass
>>> class IFooBarContainer(IContainer):
... contains(IFoo, IBar)
>>> __setitem__ = IFooBarContainer['__setitem__']
>>> __setitem__.getTaggedValue('precondition').types == (IFoo, IBar)
True
It is invalid to call contains outside a class suite:
>>> contains(IFoo, IBar)
Traceback (most recent call last):
...
TypeError: contains not called from suite
"""
frame = sys._getframe(1)
f_locals = frame.f_locals
f_globals = frame.f_globals
if not (f_locals is not f_globals
and f_locals.get('__module__')
and f_locals.get('__module__') == f_globals.get('__name__')
):
raise TypeError("contains not called from suite")
def __setitem__(key, value):
pass
__setitem__.__doc__ = IContainer['__setitem__'].__doc__
__setitem__.precondition = ItemTypePrecondition(
*types,
**dict(module=f_globals['__name__'])
)
f_locals['__setitem__'] = __setitem__
class IContainerTypesConstraint(zope.interface.Interface):
def __call__(object):
"""Test whether object is valid.
Return True if valid.
Raise zope.interface.Invalid if the objet is invalid.
"""
class ContainerTypesConstraint(_TypesBased):
"""Constrain a container to be one of a number of types
>>> class I1(zope.interface.Interface):
... pass
>>> class I2(zope.interface.Interface):
... pass
>>> class Ob(object):
... pass
>>> ob = Ob()
>>> constraint = ContainerTypesConstraint(I1, I2)
>>> try:
... constraint(ob)
... except InvalidContainerType, v:
... print (v[0] is ob), (v[1] == (I1, I2))
... else:
... print 'Should have failed'
True True
>>> zope.interface.classImplements(Ob, I2)
>>> constraint(Ob())
True
"""
zope.interface.implements(IContainerTypesConstraint)
def __call__(self, object):
for iface in self.types:
if iface.providedBy(object):
return True
else:
raise InvalidContainerType(object, self.types)
def containers(*types):
"""Declare the container types a type can be contained in
This is used within a class suite defining an interface to create
a __parent__ specification with a constraint allowing only the
given types:
>>> class IFoo(IContainer):
... pass
>>> class IBar(IContainer):
... pass
>>> from zope.app.container.interfaces import IContained
>>> class IFooBarContained(IContained):
... containers(IFoo, IBar)
>>> __parent__ = IFooBarContained['__parent__']
>>> __parent__.constraint.types == (IFoo, IBar)
True
It is invalid to call containers outside a class suite:
>>> containers(IFoo, IBar)
Traceback (most recent call last):
...
TypeError: containers not called from suite
"""
frame = sys._getframe(1)
f_locals = frame.f_locals
f_globals = frame.f_globals
if not (f_locals is not f_globals
and f_locals.get('__module__')
and f_locals.get('__module__') == f_globals.get('__name__')
):
raise TypeError("containers not called from suite")
__parent__ = zope.schema.Field(
constraint = ContainerTypesConstraint(
*types,
**dict(module=f_globals['__name__'])
)
)
f_locals['__parent__'] = __parent__
Containment constraints
=======================
Containment constraints allow us to express restrictions on the types
of items that can be placed in containers or on the types of
containers an item can be placed in. We express these constraints in
interfaces. Let's define some container and item interfaces:
>>> from zope.app.container.interfaces import IContainer, IContained
>>> from zope.app.container.constraints import containers, contains
>>> class IBuddyFolder(IContainer):
... contains('.IBuddy')
In this example, we used the contains function to declare that objects
that provide IBuddyFolder can only contain items that provide IBuddy.
Note that we used a string containing a dotted name for the IBuddy
interface. This is because IBuddy hasn't been defined yet. When we
define IBuddy, we can use IBuddyFolder directly:
>>> class IBuddy(IContained):
... containers(IBuddyFolder)
Now, with these interfaces in place, we can define Buddy and
BuddyFolder classes and verify that we can put buddies in buddy
folders:
>>> from zope import interface
>>> class Buddy:
... interface.implements(IBuddy)
>>> class BuddyFolder:
... interface.implements(IBuddyFolder)
>>> from zope.app.container.constraints import checkObject, checkFactory
>>> from zope.component.factory import Factory
>>> checkObject(BuddyFolder(), 'x', Buddy())
>>> checkFactory(BuddyFolder(), 'x', Factory(Buddy))
True
If we try to use other containers or folders, we'll get errors:
>>> class Container:
... interface.implements(IContainer)
>>> class Contained:
... interface.implements(IContained)
>>> checkObject(Container(), 'x', Buddy())
... # doctest: +ELLIPSIS
Traceback (most recent call last):
InvalidContainerType: ...
>>> checkFactory(Container(), 'x', Factory(Buddy))
False
>>> checkObject(BuddyFolder(), 'x', Contained())
... # doctest: +ELLIPSIS
Traceback (most recent call last):
InvalidItemType: ...
>>> checkFactory(BuddyFolder(), 'x', Factory(Contained))
False
In the example, we defined the container first and then the items. We
could have defined these in the opposite order:
>>> class IContact(IContained):
... containers('.IContacts')
>>> class IContacts(IContainer):
... contains(IContact)
>>> class Contact:
... interface.implements(IContact)
>>> class Contacts:
... interface.implements(IContacts)
>>> checkObject(Contacts(), 'x', Contact())
>>> checkFactory(Contacts(), 'x', Factory(Contact))
True
>>> checkObject(Contacts(), 'x', Buddy())
... # doctest: +ELLIPSIS
Traceback (most recent call last):
InvalidItemType: ...
>>> checkFactory(Contacts(), 'x', Factory(Buddy))
False
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Classes to support implementing `IContained`
$Id$
"""
__docformat__ = 'restructuredtext'
import zope.component
import zope.interface.declarations
from zope.interface import providedBy
from zope.interface.declarations import getObjectSpecification
from zope.interface.declarations import ObjectSpecification
from zope.event import notify
from zope.component.interfaces import ObjectEvent
from zope.location.interfaces import ILocation, ISublocations
from zope.exceptions.interfaces import DuplicationError, UserError
from zope.security.checker import selectChecker, CombinedChecker
from zope.lifecycleevent import ObjectModifiedEvent
from zope.app.container.i18n import ZopeMessageFactory as _
from zope.app.container.interfaces import IContained
from zope.app.container.interfaces import INameChooser
from zope.app.container.interfaces import IObjectAddedEvent
from zope.app.container.interfaces import IObjectMovedEvent
from zope.app.container.interfaces import IObjectRemovedEvent
from zope.app.container.interfaces import IContainerModifiedEvent
from zope.app.container._zope_app_container_contained import ContainedProxyBase
from zope.app.container._zope_app_container_contained import getProxiedObject
from zope.app.broken.interfaces import IBroken
class Contained(object):
"""Stupid mix-in that defines `__parent__` and `__name__` attributes"""
zope.interface.implements(IContained)
__parent__ = __name__ = None
class ObjectMovedEvent(ObjectEvent):
"""An object has been moved"""
zope.interface.implements(IObjectMovedEvent)
def __init__(self, object, oldParent, oldName, newParent, newName):
ObjectEvent.__init__(self, object)
self.oldParent = oldParent
self.oldName = oldName
self.newParent = newParent
self.newName = newName
class ObjectAddedEvent(ObjectMovedEvent):
"""An object has been added to a container"""
zope.interface.implements(IObjectAddedEvent)
def __init__(self, object, newParent=None, newName=None):
if newParent is None:
newParent = object.__parent__
if newName is None:
newName = object.__name__
ObjectMovedEvent.__init__(self, object, None, None, newParent, newName)
class ObjectRemovedEvent(ObjectMovedEvent):
"""An object has been removed from a container"""
zope.interface.implements(IObjectRemovedEvent)
def __init__(self, object, oldParent=None, oldName=None):
if oldParent is None:
oldParent = object.__parent__
if oldName is None:
oldName = object.__name__
ObjectMovedEvent.__init__(self, object, oldParent, oldName, None, None)
class ContainerModifiedEvent(ObjectModifiedEvent):
"""The container has been modified."""
zope.interface.implements(IContainerModifiedEvent)
def dispatchToSublocations(object, event):
"""Dispatch an event to sublocations of a given object
When a move event happens for an object, it's important to notify
subobjects as well.
We do this based on locations.
Suppose, for example, that we define some location objects.
>>> class L(object):
... zope.interface.implements(ILocation)
... def __init__(self, name):
... self.__name__ = name
... self.__parent__ = None
... def __repr__(self):
... return '%s(%s)' % (
... self.__class__.__name__, str(self.__name__))
>>> class C(L):
... zope.interface.implements(ISublocations)
... def __init__(self, name, *subs):
... L.__init__(self, name)
... self.subs = subs
... for sub in subs:
... sub.__parent__ = self
... def sublocations(self):
... return self.subs
>>> c = C(1,
... C(11,
... L(111),
... L(112),
... ),
... C(12,
... L(121),
... L(122),
... L(123),
... L(124),
... ),
... L(13),
... )
Now, if we call the dispatcher, it should call event handlers
for all of the objects.
Lets create an event handler that records the objects it sees:
>>> seen = []
>>> def handler(ob, event):
... seen.append((ob, event.object))
Note that we record the the object the handler is called on as
well as the event object:
Now we'll register it:
>>> from zope.app.testing import ztapi
>>> ztapi.subscribe([None, IObjectMovedEvent], None, handler)
We also register our dispatcher:
>>> ztapi.subscribe([None, IObjectMovedEvent], None,
... dispatchToSublocations)
We can then call the dispatcher for the root object:
>>> event = ObjectRemovedEvent(c)
>>> dispatchToSublocations(c, event)
Now, we should have seen all of the subobjects:
>>> seenreprs = map(repr, seen)
>>> seenreprs.sort()
>>> seenreprs
['(C(11), C(1))', '(C(12), C(1))', '(L(111), C(1))',""" \
""" '(L(112), C(1))', '(L(121), C(1))', '(L(122), C(1))',""" \
""" '(L(123), C(1))', '(L(124), C(1))', '(L(13), C(1))']
We see that we get entries for each of the subobjects and
that,for each entry, the event object is top object.
This suggests that location event handlers need to be aware that
the objects they are called on and the event objects could be
different.
"""
subs = ISublocations(object, None)
if subs is not None:
for sub in subs.sublocations():
for ignored in zope.component.subscribers((sub, event), None):
pass # They do work in the adapter fetch
class ContainerSublocations(object):
"""Get the sublocations for a container
Obviously, this is the container values:
>>> class MyContainer(object):
... def __init__(self, **data):
... self.data = data
... def __iter__(self):
... return iter(self.data)
... def __getitem__(self, key):
... return self.data[key]
>>> container = MyContainer(x=1, y=2, z=42)
>>> adapter = ContainerSublocations(container)
>>> sublocations = list(adapter.sublocations())
>>> sublocations.sort()
>>> sublocations
[1, 2, 42]
"""
def __init__(self, container):
self.container = container
def sublocations(self):
container = self.container
for key in container:
yield container[key]
def containedEvent(object, container, name=None):
"""Establish the containment of the object in the container
The object and necessary event are returned. The object may be a
`ContainedProxy` around the original object. The event is an added
event, a moved event, or None.
If the object implements `IContained`, simply set its `__parent__`
and `__name__` attributes:
>>> container = {}
>>> item = Contained()
>>> x, event = containedEvent(item, container, u'foo')
>>> x is item
True
>>> item.__parent__ is container
True
>>> item.__name__
u'foo'
We have an added event:
>>> event.__class__.__name__
'ObjectAddedEvent'
>>> event.object is item
True
>>> event.newParent is container
True
>>> event.newName
u'foo'
>>> event.oldParent
>>> event.oldName
Now if we call contained again:
>>> x2, event = containedEvent(item, container, u'foo')
>>> x2 is item
True
>>> item.__parent__ is container
True
>>> item.__name__
u'foo'
We don't get a new added event:
>>> event
If the object already had a parent but the parent or name was
different, we get a moved event:
>>> x, event = containedEvent(item, container, u'foo2')
>>> event.__class__.__name__
'ObjectMovedEvent'
>>> event.object is item
True
>>> event.newParent is container
True
>>> event.newName
u'foo2'
>>> event.oldParent is container
True
>>> event.oldName
u'foo'
If the `object` implements `ILocation`, but not `IContained`, set it's
`__parent__` and `__name__` attributes *and* declare that it
implements `IContained`:
>>> from zope.location import Location
>>> item = Location()
>>> IContained.providedBy(item)
False
>>> x, event = containedEvent(item, container, 'foo')
>>> x is item
True
>>> item.__parent__ is container
True
>>> item.__name__
'foo'
>>> IContained.providedBy(item)
True
If the `object` doesn't even implement `ILocation`, put a
`ContainedProxy` around it:
>>> item = []
>>> x, event = containedEvent(item, container, 'foo')
>>> x is item
False
>>> x.__parent__ is container
True
>>> x.__name__
'foo'
Make sure we don't lose existing directly provided interfaces.
>>> from zope.interface import Interface, directlyProvides
>>> class IOther(Interface):
... pass
>>> from zope.location import Location
>>> item = Location()
>>> directlyProvides(item, IOther)
>>> IOther.providedBy(item)
True
>>> x, event = containedEvent(item, container, 'foo')
>>> IOther.providedBy(item)
True
"""
if not IContained.providedBy(object):
if ILocation.providedBy(object):
zope.interface.directlyProvides(
object, IContained,
zope.interface.directlyProvidedBy(object))
else:
object = ContainedProxy(object)
oldparent = object.__parent__
oldname = object.__name__
if oldparent is container and oldname == name:
# No events
return object, None
object.__parent__ = container
object.__name__ = name
if oldparent is None or oldname is None:
event = ObjectAddedEvent(object, container, name)
else:
event = ObjectMovedEvent(object, oldparent, oldname, container, name)
return object, event
def contained(object, container, name=None):
"""Establish the containment of the object in the container
Just return the contained object without an event. This is a convenience
"macro" for:
``containedEvent(object, container, name)[0]``
This function is only used for tests.
"""
return containedEvent(object, container, name)[0]
def notifyContainerModified(object, *descriptions):
"""Notify that the container was modified."""
notify(ContainerModifiedEvent(object, *descriptions))
def setitem(container, setitemf, name, object):
"""Helper function to set an item and generate needed events
This helper is needed, in part, because the events need to get
published after the `object` has been added to the `container`.
If the item implements `IContained`, simply set its `__parent__`
and `__name__` attributes:
>>> class IItem(zope.interface.Interface):
... pass
>>> class Item(Contained):
... zope.interface.implements(IItem)
... def setAdded(self, event):
... self.added = event
... def setMoved(self, event):
... self.moved = event
>>> from zope.app.container.interfaces import IObjectAddedEvent
>>> from zope.app.container.interfaces import IObjectMovedEvent
>>> from zope.app.testing import ztapi
>>> ztapi.subscribe([IItem, IObjectAddedEvent], None,
... lambda obj, event: obj.setAdded(event))
>>> ztapi.subscribe([IItem, IObjectMovedEvent], None,
... lambda obj, event: obj.setMoved(event))
>>> item = Item()
>>> container = {}
>>> setitem(container, container.__setitem__, u'c', item)
>>> container[u'c'] is item
1
>>> item.__parent__ is container
1
>>> item.__name__
u'c'
If we run this using the testing framework, we'll use `getEvents` to
track the events generated:
>>> from zope.component.eventtesting import getEvents
>>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent
We have an added event:
>>> len(getEvents(IObjectAddedEvent))
1
>>> event = getEvents(IObjectAddedEvent)[-1]
>>> event.object is item
1
>>> event.newParent is container
1
>>> event.newName
u'c'
>>> event.oldParent
>>> event.oldName
As well as a modification event for the container:
>>> len(getEvents(IObjectModifiedEvent))
1
>>> getEvents(IObjectModifiedEvent)[-1].object is container
1
The item's hooks have been called:
>>> item.added is event
1
>>> item.moved is event
1
We can suppress events and hooks by setting the `__parent__` and
`__name__` first:
>>> item = Item()
>>> item.__parent__, item.__name__ = container, 'c2'
>>> setitem(container, container.__setitem__, u'c2', item)
>>> len(container)
2
>>> len(getEvents(IObjectAddedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
1
>>> getattr(item, 'added', None)
>>> getattr(item, 'moved', None)
If the item had a parent or name (as in a move or rename),
we generate a move event, rather than an add event:
>>> setitem(container, container.__setitem__, u'c3', item)
>>> len(container)
3
>>> len(getEvents(IObjectAddedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
2
>>> len(getEvents(IObjectMovedEvent))
2
(Note that we have 2 move events because add are move events.)
We also get the move hook called, but not the add hook:
>>> event = getEvents(IObjectMovedEvent)[-1]
>>> getattr(item, 'added', None)
>>> item.moved is event
1
If we try to replace an item without deleting it first, we'll get
an error:
>>> setitem(container, container.__setitem__, u'c', [])
Traceback (most recent call last):
...
DuplicationError: c
>>> del container[u'c']
>>> setitem(container, container.__setitem__, u'c', [])
>>> len(getEvents(IObjectAddedEvent))
2
>>> len(getEvents(IObjectModifiedEvent))
3
If the object implements `ILocation`, but not `IContained`, set it's
`__parent__` and `__name__` attributes *and* declare that it
implements `IContained`:
>>> from zope.location import Location
>>> item = Location()
>>> IContained.providedBy(item)
0
>>> setitem(container, container.__setitem__, u'l', item)
>>> container[u'l'] is item
1
>>> item.__parent__ is container
1
>>> item.__name__
u'l'
>>> IContained.providedBy(item)
1
We get new added and modification events:
>>> len(getEvents(IObjectAddedEvent))
3
>>> len(getEvents(IObjectModifiedEvent))
4
If the object doesn't even implement `ILocation`, put a
`ContainedProxy` around it:
>>> item = []
>>> setitem(container, container.__setitem__, u'i', item)
>>> container[u'i']
[]
>>> container[u'i'] is item
0
>>> item = container[u'i']
>>> item.__parent__ is container
1
>>> item.__name__
u'i'
>>> IContained.providedBy(item)
1
>>> len(getEvents(IObjectAddedEvent))
4
>>> len(getEvents(IObjectModifiedEvent))
5
We'll get type errors if we give keys that aren't unicode or ascii keys:
>>> setitem(container, container.__setitem__, 42, item)
Traceback (most recent call last):
...
TypeError: name not unicode or ascii string
>>> setitem(container, container.__setitem__, None, item)
Traceback (most recent call last):
...
TypeError: name not unicode or ascii string
>>> setitem(container, container.__setitem__, 'hello ' + chr(200), item)
Traceback (most recent call last):
...
TypeError: name not unicode or ascii string
and we'll get a value error of we give an empty string or unicode:
>>> setitem(container, container.__setitem__, '', item)
Traceback (most recent call last):
...
ValueError: empty names are not allowed
>>> setitem(container, container.__setitem__, u'', item)
Traceback (most recent call last):
...
ValueError: empty names are not allowed
"""
# Do basic name check:
if isinstance(name, str):
try:
name = unicode(name)
except UnicodeError:
raise TypeError("name not unicode or ascii string")
elif not isinstance(name, unicode):
raise TypeError("name not unicode or ascii string")
if not name:
raise ValueError("empty names are not allowed")
old = container.get(name)
if old is object:
return
if old is not None:
raise DuplicationError(name)
object, event = containedEvent(object, container, name)
setitemf(name, object)
if event:
notify(event)
notifyContainerModified(container)
fixing_up = False
def uncontained(object, container, name=None):
"""Clear the containment relationship between the `object` and
the `container`.
If we run this using the testing framework, we'll use `getEvents` to
track the events generated:
>>> from zope.component.eventtesting import getEvents
>>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent
>>> from zope.app.container.interfaces import IObjectRemovedEvent
We'll start by creating a container with an item:
>>> class Item(Contained):
... pass
>>> item = Item()
>>> container = {u'foo': item}
>>> x, event = containedEvent(item, container, u'foo')
>>> item.__parent__ is container
1
>>> item.__name__
u'foo'
Now we'll remove the item. It's parent and name are cleared:
>>> uncontained(item, container, u'foo')
>>> item.__parent__
>>> item.__name__
We now have a new removed event:
>>> len(getEvents(IObjectRemovedEvent))
1
>>> event = getEvents(IObjectRemovedEvent)[-1]
>>> event.object is item
1
>>> event.oldParent is container
1
>>> event.oldName
u'foo'
>>> event.newParent
>>> event.newName
As well as a modification event for the container:
>>> len(getEvents(IObjectModifiedEvent))
1
>>> getEvents(IObjectModifiedEvent)[-1].object is container
1
Now if we call uncontained again:
>>> uncontained(item, container, u'foo')
We won't get any new events, because __parent__ and __name__ are None:
>>> len(getEvents(IObjectRemovedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
1
But, if either the name or parent are not ``None`` and they are not the
container and the old name, we'll get a modified event but not a removed
event.
>>> item.__parent__, item.__name__ = container, None
>>> uncontained(item, container, u'foo')
>>> len(getEvents(IObjectRemovedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
2
>>> item.__parent__, item.__name__ = None, u'bar'
>>> uncontained(item, container, u'foo')
>>> len(getEvents(IObjectRemovedEvent))
1
>>> len(getEvents(IObjectModifiedEvent))
3
"""
try:
oldparent = object.__parent__
oldname = object.__name__
except AttributeError:
# The old object doesn't implements IContained
# Maybe we're converting old data:
if not fixing_up:
raise
oldparent = None
oldname = None
if oldparent is not container or oldname != name:
if oldparent is not None or oldname is not None:
notifyContainerModified(container)
return
event = ObjectRemovedEvent(object, oldparent, oldname)
notify(event)
if not IBroken.providedBy(object):
object.__parent__ = None
object.__name__ = None
notifyContainerModified(container)
class NameChooser(object):
zope.interface.implements(INameChooser)
def __init__(self, context):
self.context = context
def checkName(self, name, object):
"See zope.app.container.interfaces.INameChooser"
if not name:
raise UserError(
_("An empty name was provided. Names cannot be empty.")
)
if isinstance(name, str):
name = unicode(name)
elif not isinstance(name, unicode):
raise TypeError("Invalid name type", type(name))
if name[:1] in '+@' or '/' in name:
raise UserError(
_("Names cannot begin with '+' or '@' or contain '/'")
)
if name in self.context:
raise UserError(
_("The given name is already being used")
)
return True
def chooseName(self, name, object):
"See zope.app.container.interfaces.INameChooser"
container = self.context
if not name:
name = object.__class__.__name__
dot = name.rfind('.')
if dot >= 0:
suffix = name[dot:]
name = name[:dot]
else:
suffix = ''
n = name + suffix
i = 1
while n in container:
i += 1
n = name + u'-' + unicode(i) + suffix
# Make sure the name is valid. We may have started with something bad.
self.checkName(n, object)
return n
class DecoratorSpecificationDescriptor(
zope.interface.declarations.ObjectSpecificationDescriptor):
"""Support for interface declarations on decorators
>>> from zope.interface import *
>>> class I1(Interface):
... pass
>>> class I2(Interface):
... pass
>>> class I3(Interface):
... pass
>>> class I4(Interface):
... pass
>>> class D1(ContainedProxy):
... implements(I1)
>>> class D2(ContainedProxy):
... implements(I2)
>>> class X:
... implements(I3)
>>> x = X()
>>> directlyProvides(x, I4)
Interfaces of X are ordered with the directly-provided interfaces first
>>> [interface.getName() for interface in list(providedBy(x))]
['I4', 'I3']
When we decorate objects, what order should the interfaces come in? One
could argue that decorators are less specific, so they should come last.
>>> [interface.getName() for interface in list(providedBy(D1(x)))]
['I4', 'I3', 'I1', 'IContained', 'IPersistent']
>>> [interface.getName() for interface in list(providedBy(D2(D1(x))))]
['I4', 'I3', 'I1', 'IContained', 'IPersistent', 'I2']
"""
def __get__(self, inst, cls=None):
if inst is None:
return getObjectSpecification(cls)
else:
provided = providedBy(getProxiedObject(inst))
# Use type rather than __class__ because inst is a proxy and
# will return the proxied object's class.
cls = type(inst)
return ObjectSpecification(provided, cls)
class DecoratedSecurityCheckerDescriptor(object):
"""Descriptor for a Decorator that provides a decorated security checker.
"""
def __get__(self, inst, cls=None):
if inst is None:
return self
else:
proxied_object = getProxiedObject(inst)
checker = getattr(proxied_object, '__Security_checker__', None)
if checker is None:
checker = selectChecker(proxied_object)
wrapper_checker = selectChecker(inst)
if wrapper_checker is None:
return checker
elif checker is None:
return wrapper_checker
else:
return CombinedChecker(wrapper_checker, checker)
class ContainedProxyClassProvides(zope.interface.declarations.ClassProvides):
def __set__(self, inst, value):
inst = getProxiedObject(inst)
inst.__provides__ = value
def __delete__(self, inst):
inst = getProxiedObject(inst)
del inst.__provides__
class ContainedProxy(ContainedProxyBase):
# Prevent proxies from having their own instance dictionaries:
__slots__ = ()
__safe_for_unpickling__ = True
zope.interface.implements(IContained)
__providedBy__ = DecoratorSpecificationDescriptor()
__Security_checker__ = DecoratedSecurityCheckerDescriptor()
ContainedProxy.__provides__ = ContainedProxyClassProvides(ContainedProxy, type)
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Subscriber function checking dependencies if a removal is performed
on an object having dependencies. It raises an exception if it's the
case.
$Id$
"""
__docformat__ = 'restructuredtext'
from zope.app import zapi
from zope.i18nmessageid import Message
from zope.app.container.i18n import ZopeMessageFactory as _
from zope.app.dependable.interfaces import IDependable, DependencyError
exception_msg = _("""
Removal of object (${object}) which has dependents (${dependents})
is not possible !
You must deactivate this object before trying to remove it.
""")
def CheckDependency(event):
object = event.object
dependency = IDependable(object, None)
if dependency is not None:
dependents = dependency.dependents()
if dependents:
mapping = {
"object": zapi.getPath(object),
"dependents": ", ".join(dependents)
}
raise DependencyError(Message(exception_msg, mapping=mapping))
##############################################################################
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
##############################################################################
"""File-system representation adapters for containers
This module includes two adapters (adapter factories, really) for
providing a file-system representation for containers:
`noop`
Factory that "adapts" `IContainer` to `IWriteDirectory`.
This is a lie, since it just returns the original object.
`Cloner`
An `IDirectoryFactory` adapter that just clones the original object.
$Id$
"""
__docformat__ = 'restructuredtext'
import zope.filerepresentation.interfaces
from zope.security.proxy import removeSecurityProxy
from zope.interface import implements
def noop(container):
"""Adapt an `IContainer` to an `IWriteDirectory` by just returning it
This "works" because `IContainer` and `IWriteDirectory` have the same
methods, however, the output doesn't actually implement `IWriteDirectory`.
"""
return container
class Cloner(object):
"""`IContainer` to `IDirectoryFactory` adapter that clones
This adapter provides a factory that creates a new empty container
of the same class as it's context.
"""
implements(zope.filerepresentation.interfaces.IDirectoryFactory)
def __init__(self, context):
self.context = context
def __call__(self, name):
# We remove the security proxy so we can actually call the
# class and return an unproxied new object. (We can't use a
# trusted adapter, because the result must be unproxied.) By
# registering this adapter, one effectively gives permission
# to clone the class. Don't use this for classes that have
# exciting side effects as a result of instantiation. :)
return removeSecurityProxy(self.context).__class__()
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Find Support
$Id$
"""
__docformat__ = 'restructuredtext'
from zope.interface import implements
from interfaces import IFind, IIdFindFilter, IObjectFindFilter
from interfaces import IReadContainer
class FindAdapter(object):
implements(IFind)
__used_for__ = IReadContainer
def __init__(self, context):
self._context = context
def find(self, id_filters=None, object_filters=None):
'See IFind'
id_filters = id_filters or []
object_filters = object_filters or []
result = []
container = self._context
for id, object in container.items():
_find_helper(id, object, container,
id_filters, object_filters,
result)
return result
def _find_helper(id, object, container, id_filters, object_filters, result):
for id_filter in id_filters:
if not id_filter.matches(id):
break
else:
# if we didn't break out of the loop, all name filters matched
# now check all object filters
for object_filter in object_filters:
if not object_filter.matches(object):
break
else:
# if we didn't break out of the loop, all filters matched
result.append(object)
if not IReadContainer.providedBy(object):
return
container = object
for id, object in container.items():
_find_helper(id, object, container, id_filters, object_filters, result)
class SimpleIdFindFilter(object):
implements(IIdFindFilter)
def __init__(self, ids):
self._ids = ids
def matches(self, id):
'See INameFindFilter'
return id in self._ids
class SimpleInterfacesFindFilter(object):
"""Filter objects on the provided interfaces"""
implements(IObjectFindFilter)
def __init__(self, *interfaces):
self.interfaces = interfaces
def matches(self, object):
for iface in self.interfaces:
if iface.providedBy(object):
return True
return False
<configure
xmlns="http://namespaces.zope.org/zope"
i18n_domain="zope"
package="zope.app.container"
>
<!-- This file is the equivalent of site.zcml and it is -->
<!-- used for functional testing setup -->
<include package="zope.app.zcmlfiles" />
<include package="zope.app.container.browser.ftests" />
<include package="zope.app.file"/>
<include package="zope.app.authentication" />
<include package="zope.app.securitypolicy" file="meta.zcml" />
<include package="zope.app.securitypolicy.tests" file="functional.zcml" />
<include package="zope.app.securitypolicy" />
<securityPolicy
component="zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
<role id="zope.Anonymous" title="Everybody"
description="All users have this role implicitly" />
<role id="zope.Manager" title="Site Manager" />
<!-- Replace the following directive if you don't want public access -->
<grant permission="zope.View"
role="zope.Anonymous" />
<grant permission="zope.app.dublincore.view"
role="zope.Anonymous" />
<grantAll role="zope.Manager" />
<!-- Principals -->
<unauthenticatedPrincipal
id="zope.anybody"
title="Unauthenticated User" />
<!-- 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" />
<grant role="zope.Manager" principal="zope.globalmgr" />
</configure>
#
# This file is necessary to make this directory a package.
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta"
i18n_domain="zope"
>
<!-- This file is the equivalent of site.zcml and it is -->
<!-- used for functional testing setup -->
<include package="zope.app.zcmlfiles" />
<include package="zope.app.container.browser.ftests" />
<include package="zope.app.file"/>
<include package="zope.app.authentication" />
<include package="zope.app.securitypolicy" file="meta.zcml" />
<include package="zope.app.securitypolicy.tests" file="functional.zcml" />
<include package="zope.app.securitypolicy" />
<securityPolicy
component="zope.app.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
<role id="zope.Anonymous" title="Everybody"
description="All users have this role implicitly" />
<role id="zope.Manager" title="Site Manager" />
<!-- Replace the following directive if you don't want public access -->
<grant permission="zope.View"
role="zope.Anonymous" />
<grant permission="zope.app.dublincore.view"
role="zope.Anonymous" />
<grantAll role="zope.Manager" />
<!-- Principals -->
<unauthenticatedPrincipal
id="zope.anybody"
title="Unauthenticated User" />
<!-- 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" />
<grant role="zope.Manager" principal="zope.globalmgr" />
</configure>
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Container View Permissions Tests
$Id$
"""
import unittest
import transaction
from zope.security.interfaces import Unauthorized
from zope.app.testing.functional import BrowserTestCase
from zope.app.file import File
from zope.dublincore.interfaces import IZopeDublinCore
from zope.app.securitypolicy.interfaces import IRolePermissionManager
from zope.app.container.testing import AppContainerLayer
class Tests(BrowserTestCase):
def test_default_view_permissions(self):
"""Tests the default view permissions.
See zope/app/securitypolicy/configure.zcml for the grants of
zope.View and zope.app.dublincore.view to zope.Anonymous. These
ensure that, by default, anonymous users can view container contents.
"""
# add an item that can be viewed from the root folder
file = File()
self.getRootFolder()['file'] = file
IZopeDublinCore(file).title = u'My File'
transaction.commit()
response = self.publish('/')
self.assertEquals(response.getStatus(), 200)
body = response.getBody()
# confirm we can see the file name
self.assert_(body.find('<a href="file">file</a>') != -1)
# confirm we can see the metadata title
self.assert_(body.find('<td><span>My File</span></td>') != -1)
def test_deny_view(self):
"""Tests the denial of view permissions to anonymous.
This test uses the ZMI interface to deny anonymous zope.View permission
to the root folder.
"""
# deny zope.View to zope.Anonymous
prm = IRolePermissionManager(self.getRootFolder())
prm.denyPermissionToRole('zope.View', 'zope.Anonymous')
transaction.commit()
# confirm Unauthorized when viewing root folder
self.assertRaises(Unauthorized, self.publish, '/')
def test_deny_dublincore_view(self):
"""Tests the denial of dublincore view permissions to anonymous.
Users who can view a folder contents page but cannot view dublin core
should still be able to see the folder items' names, but not their
title, modified, and created info.
"""
# add an item that can be viewed from the root folder
file = File()
self.getRootFolder()['file'] = file
IZopeDublinCore(file).title = u'My File'
# deny zope.app.dublincore.view to zope.Anonymous
prm = IRolePermissionManager(self.getRootFolder())
prm.denyPermissionToRole('zope.app.dublincore.view', 'zope.Anonymous')
transaction.commit()
response = self.publish('/')
self.assertEquals(response.getStatus(), 200)
body = response.getBody()
# confirm we can see the file name
self.assert_(body.find('<a href="file">file</a>') != -1)
# confirm we *cannot* see the metadata title
self.assert_(body.find('My File') == -1)
def test_suite():
suite = unittest.TestSuite()
Tests.layer = AppContainerLayer
suite.addTest(unittest.makeSuite(Tests))
return suite
if __name__=='__main__':
unittest.main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Customization of zope.i18n for the Zope application server
$Id$
"""
__docformat__ = 'restructuredtext'
# import this as _ to create i18n messages in the zope domain
from zope.i18nmessageid import MessageFactory
ZopeMessageFactory = MessageFactory('zope')
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Container-related interfaces
$Id$
"""
__docformat__ = 'restructuredtext'
from zope.deprecation import deprecated
from zope.interface import Interface, Attribute, Invalid
from zope.component.interfaces import IView, IObjectEvent
from zope.interface.common.mapping import IItemMapping
from zope.interface.common.mapping import IReadMapping, IEnumerableMapping
from zope.location.interfaces import ILocation
from zope.lifecycleevent.interfaces import IObjectModifiedEvent
deprecated('IContentContainer',
'This interface has been deprecated. '
'Check the "containerViews" zcml directive. '
'The reference will be gone in 3.3')
class DuplicateIDError(KeyError):
pass
class ContainerError(Exception):
"""An error of a container with one of its components."""
class InvalidContainerType(Invalid, TypeError):
"""The type of a container is not valid."""
class InvalidItemType(Invalid, TypeError):
"""The type of an item is not valid."""
class InvalidType(Invalid, TypeError):
"""The type of an object is not valid."""
class IContained(ILocation):
"""Objects contained in containers."""
class IItemContainer(IItemMapping):
"""Minimal readable container."""
class ISimpleReadContainer(IItemContainer, IReadMapping):
"""Readable content containers."""
class IReadContainer(ISimpleReadContainer, IEnumerableMapping):
"""Readable containers that can be enumerated."""
class IWriteContainer(Interface):
"""An interface for the write aspects of a container."""
def __setitem__(name, object):
"""Add the given `object` to the container under the given name.
Raises a ``TypeError`` if the key is not a unicode or ascii string.
Raises a ``ValueError`` if key is empty.
The container might choose to add a different object than the
one passed to this method.
If the object doesn't implement `IContained`, then one of two
things must be done:
1. If the object implements `ILocation`, then the `IContained`
interface must be declared for the object.
2. Otherwise, a `ContainedProxy` is created for the object and
stored.
The object's `__parent__` and `__name__` attributes are set to the
container and the given name.
If the old parent was ``None``, then an `IObjectAddedEvent` is
generated, otherwise, an `IObjectMovedEvent` is generated. An
`IContainerModifiedEvent` is generated for the container.
If the object replaces another object, then the old object is
deleted before the new object is added, unless the container
vetos the replacement by raising an exception.
If the object's `__parent__` and `__name__` were already set to
the container and the name, then no events are generated and
no hooks. This allows advanced clients to take over event
generation.
"""
def __delitem__(name):
"""Delete the named object from the container.
Raises a ``KeyError`` if the object is not found.
If the deleted object's `__parent__` and `__name__` match the
container and given name, then an `IObjectRemovedEvent` is
generated and the attributes are set to ``None``. If the object
can be adapted to `IObjectMovedEvent`, then the adapter's
`moveNotify` method is called with the event.
Unless the object's `__parent__` and `__name__` attributes were
initially ``None``, generate an `IContainerModifiedEvent` for the
container.
If the object's `__parent__` and `__name__` were already set to
``None``, then no events are generated. This allows advanced
clients to take over event generation.
"""
class IItemWriteContainer(IWriteContainer, IItemContainer):
"""A write container that also supports minimal reads."""
class IContentContainer(IWriteContainer):
"""Containers (like folders) that contain ordinary content."""
class IContainer(IReadContainer, IWriteContainer):
"""Readable and writable content container."""
class IOrderedContainer(IContainer):
"""Containers whose contents are maintained in order."""
def updateOrder(order):
"""Revise the order of keys, replacing the current ordering.
order is a list or a tuple containing the set of existing keys in
the new order. `order` must contain ``len(keys())`` items and cannot
contain duplicate keys.
Raises ``TypeError`` if order is not a tuple or a list.
Raises ``ValueError`` if order contains an invalid set of keys.
"""
class IContainerNamesContainer(IContainer):
"""Containers that always choose names for their items."""
##############################################################################
# Moving Objects
class IObjectMovedEvent(IObjectEvent):
"""An object has been moved."""
oldParent = Attribute("The old location parent for the object.")
oldName = Attribute("The old location name for the object.")
newParent = Attribute("The new location parent for the object.")
newName = Attribute("The new location name for the object.")
##############################################################################
# Adding objects
class UnaddableError(ContainerError):
"""An object cannot be added to a container."""
def __init__(self, container, obj, message=""):
self.container = container
self.obj = obj
self.message = message and ": %s" % message
def __str__(self):
return ("%(obj)s cannot be added "
"to %(container)s%(message)s" % self.__dict__)
class IObjectAddedEvent(IObjectMovedEvent):
"""An object has been added to a container."""
class IAdding(IView):
def add(content):
"""Add content object to container.
Add using the name in `contentName`. Returns the added object
in the context of its container.
If `contentName` is already used in container, raises
``DuplicateIDError``.
"""
contentName = Attribute(
"""The content name, as usually set by the Adder traverser.
If the content name hasn't been defined yet, returns ``None``.
Some creation views might use this to optionally display the
name on forms.
"""
)
def nextURL():
"""Return the URL that the creation view should redirect to.
This is called by the creation view after calling add.
It is the adder's responsibility, not the creation view's to
decide what page to display after content is added.
"""
def nameAllowed():
"""Return whether names can be input by the user."""
def addingInfo():
"""Return add menu data as a sequence of mappings.
Each mapping contains 'action', 'title', and possibly other keys.
The result is sorted by title.
"""
def isSingleMenuItem():
"""Return whether there is single menu item or not."""
def hasCustomAddView():
"This should be called only if there is `singleMenuItem` else return 0"
class INameChooser(Interface):
def checkName(name, object):
"""Check whether an object name is valid.
Raises a user error if the name is not valid.
"""
def chooseName(name, object):
"""Choose a unique valid name for the object
The given name and object may be taken into account when
choosing the name.
"""
##############################################################################
# Removing objects
class IObjectRemovedEvent(IObjectMovedEvent):
"""An object has been removed from a container."""
##############################################################################
# Modifying containers
class IContainerModifiedEvent(IObjectModifiedEvent):
"""The container has been modified.
This event is specific to "containerness" modifications, which means
addition, removal or reordering of sub-objects.
"""
##############################################################################
# Finding objects
class IFind(Interface):
"""
Find support for containers.
"""
def find(id_filters=None, object_filters=None):
"""Find object that matches all filters in all sub-objects.
This container itself is not included.
"""
class IObjectFindFilter(Interface):
def matches(object):
"""Return True if the object matches the filter criteria."""
class IIdFindFilter(Interface):
def matches(id):
"""Return True if the id matches the filter criteria."""
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Ordered container implementation.
$Id$
"""
__docformat__ = 'restructuredtext'
from zope.app.container.interfaces import IOrderedContainer
from zope.interface import implements
from persistent import Persistent
from persistent.dict import PersistentDict
from persistent.list import PersistentList
from types import StringTypes, TupleType, ListType
from zope.app.container.contained import Contained, setitem, uncontained
from zope.app.container.contained import notifyContainerModified
class OrderedContainer(Persistent, Contained):
""" `OrderedContainer` maintains entries' order as added and moved.
>>> oc = OrderedContainer()
>>> int(IOrderedContainer.providedBy(oc))
1
>>> len(oc)
0
"""
implements(IOrderedContainer)
def __init__(self):
self._data = PersistentDict()
self._order = PersistentList()
def keys(self):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc.keys()
['foo']
>>> oc['baz'] = 'quux'
>>> oc.keys()
['foo', 'baz']
>>> int(len(oc._order) == len(oc._data))
1
"""
return self._order[:]
def __iter__(self):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc['baz'] = 'quux'
>>> [i for i in oc]
['foo', 'baz']
>>> int(len(oc._order) == len(oc._data))
1
"""
return iter(self.keys())
def __getitem__(self, key):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc['foo'] = 'bar'
>>> oc['foo']
'bar'
"""
return self._data[key]
def get(self, key, default=None):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc['foo'] = 'bar'
>>> oc.get('foo')
'bar'
>>> oc.get('funky', 'No chance, dude.')
'No chance, dude.'
"""
return self._data.get(key, default)
def values(self):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc.values()
['bar']
>>> oc['baz'] = 'quux'
>>> oc.values()
['bar', 'quux']
>>> int(len(oc._order) == len(oc._data))
1
"""
return [self._data[i] for i in self._order]
def __len__(self):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> int(len(oc) == 0)
1
>>> oc['foo'] = 'bar'
>>> int(len(oc) == 1)
1
"""
return len(self._data)
def items(self):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc.items()
[('foo', 'bar')]
>>> oc['baz'] = 'quux'
>>> oc.items()
[('foo', 'bar'), ('baz', 'quux')]
>>> int(len(oc._order) == len(oc._data))
1
"""
return [(i, self._data[i]) for i in self._order]
def __contains__(self, key):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc['foo'] = 'bar'
>>> int('foo' in oc)
1
>>> int('quux' in oc)
0
"""
return self._data.has_key(key)
has_key = __contains__
def __setitem__(self, key, object):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc._order
['foo']
>>> oc['baz'] = 'quux'
>>> oc._order
['foo', 'baz']
>>> int(len(oc._order) == len(oc._data))
1
"""
existed = self._data.has_key(key)
bad = False
if isinstance(key, StringTypes):
try:
unicode(key)
except UnicodeError:
bad = True
else:
bad = True
if bad:
raise TypeError("'%s' is invalid, the key must be an "
"ascii or unicode string" % key)
if len(key) == 0:
raise ValueError("The key cannot be an empty string")
# We have to first update the order, so that the item is available,
# otherwise most API functions will lie about their available values
# when an event subscriber tries to do something with the container.
if not existed:
self._order.append(key)
# This function creates a lot of events that other code listens to.
setitem(self, self._data.__setitem__, key, object)
return key
def __delitem__(self, key):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc.keys()
[]
>>> oc['foo'] = 'bar'
>>> oc['baz'] = 'quux'
>>> oc['zork'] = 'grue'
>>> oc.items()
[('foo', 'bar'), ('baz', 'quux'), ('zork', 'grue')]
>>> int(len(oc._order) == len(oc._data))
1
>>> del oc['baz']
>>> oc.items()
[('foo', 'bar'), ('zork', 'grue')]
>>> int(len(oc._order) == len(oc._data))
1
"""
uncontained(self._data[key], self, key)
del self._data[key]
self._order.remove(key)
def updateOrder(self, order):
""" See `IOrderedContainer`.
>>> oc = OrderedContainer()
>>> oc['foo'] = 'bar'
>>> oc['baz'] = 'quux'
>>> oc['zork'] = 'grue'
>>> oc.keys()
['foo', 'baz', 'zork']
>>> oc.updateOrder(['baz', 'foo', 'zork'])
>>> oc.keys()
['baz', 'foo', 'zork']
>>> oc.updateOrder(['baz', 'zork', 'foo'])
>>> oc.keys()
['baz', 'zork', 'foo']
>>> oc.updateOrder(['baz', 'zork', 'foo'])
>>> oc.keys()
['baz', 'zork', 'foo']
>>> oc.updateOrder(('zork', 'foo', 'baz'))
>>> oc.keys()
['zork', 'foo', 'baz']
>>> oc.updateOrder(['baz', 'zork'])
Traceback (most recent call last):
...
ValueError: Incompatible key set.
>>> oc.updateOrder(['foo', 'bar', 'baz', 'quux'])
Traceback (most recent call last):
...
ValueError: Incompatible key set.
>>> oc.updateOrder(1)
Traceback (most recent call last):
...
TypeError: order must be a tuple or a list.
>>> oc.updateOrder('bar')
Traceback (most recent call last):
...
TypeError: order must be a tuple or a list.
>>> oc.updateOrder(['baz', 'zork', 'quux'])
Traceback (most recent call last):
...
ValueError: Incompatible key set.
>>> del oc['baz']
>>> del oc['zork']
>>> del oc['foo']
>>> len(oc)
0
"""
if not isinstance(order, ListType) and \
not isinstance(order, TupleType):
raise TypeError('order must be a tuple or a list.')
if len(order) != len(self._order):
raise ValueError("Incompatible key set.")
was_dict = {}
will_be_dict = {}
new_order = PersistentList()
for i in range(len(order)):
was_dict[self._order[i]] = 1
will_be_dict[order[i]] = 1
new_order.append(order[i])
if will_be_dict != was_dict:
raise ValueError("Incompatible key set.")
self._order = new_order
notifyContainerModified(self)
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Sample container implementation.
This is primarily for testing purposes.
It might be useful as a mix-in for some classes, but many classes will
need a very different implementation.
$Id$
"""
__docformat__ = 'restructuredtext'
from zope.app.container.interfaces import IContainer
from zope.interface import implements
from zope.app.container.contained import Contained, setitem, uncontained
class SampleContainer(Contained):
"""Sample container implementation suitable for testing.
It is not suitable, directly as a base class unless the subclass
overrides `_newContainerData` to return a persistent mapping object.
"""
implements(IContainer)
def __init__(self):
self.__data = self._newContainerData()
def _newContainerData(self):
"""Construct an item-data container
Subclasses should override this if they want different data.
The value returned is a mapping object that also has `get`,
`has_key`, `keys`, `items`, and `values` methods.
"""
return {}
def keys(self):
'''See interface `IReadContainer`'''
return self.__data.keys()
def __iter__(self):
return iter(self.__data)
def __getitem__(self, key):
'''See interface `IReadContainer`'''
return self.__data[key]
def get(self, key, default=None):
'''See interface `IReadContainer`'''
return self.__data.get(key, default)
def values(self):
'''See interface `IReadContainer`'''
return self.__data.values()
def __len__(self):
'''See interface `IReadContainer`'''
return len(self.__data)
def items(self):
'''See interface `IReadContainer`'''
return self.__data.items()
def __contains__(self, key):
'''See interface `IReadContainer`'''
return self.__data.has_key(key)
has_key = __contains__
def __setitem__(self, key, object):
'''See interface `IWriteContainer`'''
setitem(self, self.__data.__setitem__, key, object)
def __delitem__(self, key):
'''See interface `IWriteContainer`'''
uncontained(self.__data[key], self, key)
del self.__data[key]
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Adapters that give the size of an object.
$Id$
"""
__docformat__ = 'restructuredtext'
from zope.app.container.i18n import ZopeMessageFactory as _
from zope.size.interfaces import ISized
from zope.interface import implements
class ContainerSized(object):
implements(ISized)
def __init__(self, container):
self._container = container
def sizeForSorting(self):
"""See `ISized`"""
return ('item', len(self._container))
def sizeForDisplay(self):
"""See `ISized`"""
num_items = len(self._container)
if num_items == 1:
return _('1 item')
return _('${items} items', mapping={'items': str(num_items)})
import os
from zope.app.testing.functional import ZCMLLayer
AppContainerLayer = ZCMLLayer(
os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'),
__name__, 'AppContainerLayer', allow_teardown=True)
#
# This file is necessary to make this directory a package.
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Unit test logic for setting up and tearing down basic infrastructure
$Id$
"""
from zope.app.testing import ztapi
from zope.app.container.interfaces import IWriteContainer, INameChooser
from zope.app.container.contained import NameChooser
class PlacelessSetup(object):
def setUp(self):
ztapi.provideAdapter(IWriteContainer, INameChooser, NameChooser)
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""BTree Container Tests
$Id$
"""
from unittest import TestCase, main, makeSuite, TestSuite
from zope.testing.doctestunit import DocTestSuite
from zope.app.testing import placelesssetup
from test_icontainer import TestSampleContainer
class TestBTreeContainer(TestSampleContainer, TestCase):
def makeTestObject(self):
from zope.app.container.btree import BTreeContainer
return BTreeContainer()
def test_suite():
return TestSuite((
makeSuite(TestBTreeContainer),
DocTestSuite('zope.app.container.btree',
setUp=placelesssetup.setUp,
tearDown=placelesssetup.tearDown),
))
if __name__=='__main__':
main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Container constraint tests
$Id$
"""
import unittest
from zope.testing import doctest, module
def setUp(test):
module.setUp(test, 'zope.app.container.constraints_txt')
def tearDown(test):
module.tearDown(test, 'zope.app.container.constraints_txt')
def test_suite():
return unittest.TestSuite((
doctest.DocTestSuite('zope.app.container.constraints'),
doctest.DocFileSuite('../constraints.txt',
setUp=setUp, tearDown=tearDown),
))
if __name__ == '__main__': unittest.main()
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Contained Tests
$Id$
"""
import unittest
import gc
from ZODB.DemoStorage import DemoStorage
from ZODB.DB import DB
import transaction
from persistent import Persistent
import zope.interface
from zope.testing import doctest
from zope.app.container.contained import ContainedProxy
from zope.app.testing import placelesssetup
class MyOb(Persistent):
pass
def test_basic_proxy_attribute_management_and_picklability():
"""Contained-object proxy
This is a picklable proxy that can be put around objects that
don't implement IContained.
>>> l = [1, 2, 3]
>>> p = ContainedProxy(l)
>>> p.__parent__ = 'Dad'
>>> p.__name__ = 'p'
>>> p
[1, 2, 3]
>>> p.__parent__
'Dad'
>>> p.__name__
'p'
>>> import pickle
>>> p2 = pickle.loads(pickle.dumps(p))
>>> p2
[1, 2, 3]
>>> p2.__parent__
'Dad'
>>> p2.__name__
'p'
"""
def test_basic_persistent_w_non_persistent_proxied():
"""
>>> p = ContainedProxy([1])
>>> p.__parent__ = 2
>>> p.__name__ = 'test'
>>> db = DB(DemoStorage('test_storage'))
>>> c = db.open()
>>> c.root()['p'] = p
>>> transaction.commit()
>>> c2 = db.open()
>>> p2 = c2.root()['p']
>>> p2
[1]
>>> p2.__parent__
2
>>> p2.__name__
'test'
>>> p2._p_changed
0
>>> p2._p_deactivate()
>>> p2._p_changed
>>> p2.__name__
'test'
>>> db.close()
"""
def test_declarations_on_ContainedProxy():
r"""
It is possible to make declarations on ContainedProxy objects.
>>> class I1(zope.interface.Interface):
... pass
>>> class C(object):
... zope.interface.implements(I1)
>>> c = C()
>>> p = ContainedProxy(c)
ContainedProxy provides no interfaces on it's own:
>>> tuple(zope.interface.providedBy(ContainedProxy))
()
It implements IContained and IPersistent:
>>> tuple(zope.interface.implementedBy(ContainedProxy))
(<InterfaceClass zope.app.container.interfaces.IContained>,
<InterfaceClass persistent.interfaces.IPersistent>)
A proxied object has IContainer, in addition to what the unproxied
object has:
>>> tuple(zope.interface.providedBy(p))
(<InterfaceClass zope.app.container.tests.test_contained.I1>,
<InterfaceClass zope.app.container.interfaces.IContained>,
<InterfaceClass persistent.interfaces.IPersistent>)
>>> class I2(zope.interface.Interface):
... pass
>>> zope.interface.directlyProvides(c, I2)
>>> tuple(zope.interface.providedBy(p))
(<InterfaceClass zope.app.container.tests.test_contained.I2>,
<InterfaceClass zope.app.container.tests.test_contained.I1>,
<InterfaceClass zope.app.container.interfaces.IContained>,
<InterfaceClass persistent.interfaces.IPersistent>)
We can declare interfaces through the proxy:
>>> class I3(zope.interface.Interface):
... pass
>>> zope.interface.directlyProvides(p, I3)
>>> tuple(zope.interface.providedBy(p))
(<InterfaceClass zope.app.container.tests.test_contained.I3>,
<InterfaceClass zope.app.container.tests.test_contained.I1>,
<InterfaceClass zope.app.container.interfaces.IContained>,
<InterfaceClass persistent.interfaces.IPersistent>)
"""
def test_basic_persistent_w_persistent_proxied():
"""
Here, we'll verify that shared references work and
that updates to both the proxies and the proxied objects
are made correctly.
----------------------
| |
parent other
| /
ob <--------------
Here we have an object, parent, that contains ob. There is another
object, other, that has a non-container reference to ob.
>>> parent = MyOb()
>>> parent.ob = ContainedProxy(MyOb())
>>> parent.ob.__parent__ = parent
>>> parent.ob.__name__ = 'test'
>>> other = MyOb()
>>> other.ob = parent.ob
We can change ob through either parent or other
>>> parent.ob.x = 1
>>> other.ob.y = 2
Now we'll save the data:
>>> db = DB(DemoStorage('test_storage'))
>>> c1 = db.open()
>>> c1.root()['parent'] = parent
>>> c1.root()['other'] = other
>>> transaction.commit()
We'll open a second connection and verify that we have the data we
expect:
>>> c2 = db.open()
>>> p2 = c2.root()['parent']
>>> p2.ob.__parent__ is p2
1
>>> p2.ob.x
1
>>> p2.ob.y
2
>>> o2 = c2.root()['other']
>>> o2.ob is p2.ob
1
>>> o2.ob is p2.ob
1
>>> o2.ob.__name__
'test'
Now we'll change things around a bit. We'll move things around
a bit. We'll also add an attribute to ob
>>> o2.ob.__name__ = 'test 2'
>>> o2.ob.__parent__ = o2
>>> o2.ob.z = 3
>>> p2.ob.__parent__ is p2
0
>>> p2.ob.__parent__ is o2
1
And save the changes:
>>> transaction.commit()
Now we'll reopen the first connection and verify that we can see
the changes:
>>> c1.close()
>>> c1 = db.open()
>>> p2 = c1.root()['parent']
>>> p2.ob.__name__
'test 2'
>>> p2.ob.z
3
>>> p2.ob.__parent__ is c1.root()['other']
1
>>> db.close()
"""
def test_proxy_cache_interaction():
"""Test to make sure the proxy properly interacts with the object cache
Persistent objects are their own weak refs. Thier deallocators
need to notify their connection's cache that their object is being
deallocated, so that it is removed from the cache.
>>> from ZODB.tests.util import DB
>>> db = DB()
>>> db.setCacheSize(5)
>>> conn = db.open()
>>> conn.root()['p'] = ContainedProxy(None)
We need to create some filler objects to push our proxy out of the cache:
>>> for i in range(10):
... conn.root()[i] = MyOb()
>>> transaction.commit()
Let's get the oid of our proxy:
>>> oid = conn.root()['p']._p_oid
Now, we'll access the filler object's:
>>> x = [getattr(conn.root()[i], 'x', 0) for i in range(10)]
We've also accessed the root object. If we garbage-collect the
cache:
>>> conn._cache.incrgc()
Then the root object will still be active, because it was accessed
recently:
>>> conn.root()._p_changed
0
And the proxy will be in the cache, because it's refernced from
the root object:
>>> conn._cache.get(oid) is not None
True
But it's a ghost:
>>> conn.root()['p']._p_changed
If we deactivate the root object:
>>> conn.root()._p_deactivate()
Then we'll release the last reference to the proxy and it should
no longer be in the cache. To be sure, we'll call gc:
>>> x = gc.collect()
>>> conn._cache.get(oid) is not None
False
"""
def test_ContainedProxy_instances_have_no_instance_dictionaries():
"""Make sure that proxies don't introduce extra instance dictionaries
>>> from zope.app.container.contained import ContainedProxy
>>> class C:
... pass
>>> c = C()
>>> c.x = 1
>>> c.__dict__
{'x': 1}
>>> p = ContainedProxy(c)
>>> p.__dict__
{'x': 1}
>>> p.y = 3
>>> p.__dict__
{'y': 3, 'x': 1}
>>> c.__dict__
{'y': 3, 'x': 1}
>>> p.__dict__ is c.__dict__
True
"""
def test_suite():
return unittest.TestSuite((
doctest.DocTestSuite('zope.app.container.contained',
setUp=placelesssetup.setUp,
tearDown=placelesssetup.tearDown),
doctest.DocTestSuite(optionflags=doctest.NORMALIZE_WHITESPACE),
))
if __name__ == '__main__': unittest.main()
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Container Traverser tests.
$Id$
"""
import unittest
from zope.testing.cleanup import CleanUp
from zope.interface import implements
from zope.traversing.interfaces import TraversalError
from zope.app.container.traversal import ContainerTraversable
from zope.app.container.interfaces import IContainer
class Container(object):
implements(IContainer)
def __init__(self, attrs={}, objs={}):
for attr,value in attrs.iteritems():
setattr(self, attr, value)
self.__objs = {}
for name,value in objs.iteritems():
self.__objs[name] = value
def __getitem__(self, name):
return self.__objs[name]
def get(self, name, default=None):
return self.__objs.get(name, default)
def __contains__(self, name):
return self.__objs.has_key(name)
class Test(CleanUp, unittest.TestCase):
def testAttr(self):
# test container path traversal
foo = Container()
bar = Container()
baz = Container()
c = Container({'foo': foo}, {'bar': bar, 'foo': baz})
T = ContainerTraversable(c)
self.failUnless(T.traverse('foo', []) is baz)
self.failUnless(T.traverse('bar', []) is bar)
self.assertRaises(TraversalError , T.traverse, 'morebar', [])
def test_suite():
loader = unittest.TestLoader()
return loader.loadTestsFromTestCase(Test)
if __name__ == '__main__':
unittest.TextTestRunner().run(test_suite())
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Container Traverser Tests
$Id$
"""
import unittest
from zope.app.container.traversal import ContainerTraverser
from zope.app.container.interfaces import IReadContainer
from zope.app.testing import ztapi, placelesssetup
from zope.publisher.interfaces import NotFound
from zope.publisher.browser import TestRequest
from zope.interface import implements
class TestContainer(object):
implements(IReadContainer)
def __init__(self, **kw):
for name, value in kw.items():
setattr(self, name , value)
def get(self, name, default=None):
return getattr(self, name, default)
class View(object):
def __init__(self, context, request):
self.context = context
self.request = request
class TraverserTest(placelesssetup.PlacelessSetup, unittest.TestCase):
# The following two methods exist, so that other container traversers can
# use these tests as a base.
def _getTraverser(self, context, request):
return ContainerTraverser(context, request)
def _getContainer(self, **kw):
return TestContainer(**kw)
def setUp(self):
super(TraverserTest, self).setUp()
# Create a small object tree
self.foo = self._getContainer()
foo2 = self._getContainer(Foo=self.foo)
# Initiate a request
self.request = TestRequest()
# Create the traverser
self.traverser = self._getTraverser(foo2, self.request)
# Define a simple view for the container
ztapi.browserView(IReadContainer, 'viewfoo', View)
def test_itemTraversal(self):
self.assertEqual(
self.traverser.publishTraverse(self.request, 'Foo'),
self.foo)
self.assertRaises(
NotFound,
self.traverser.publishTraverse, self.request, 'morebar')
def test_viewTraversal(self):
self.assertEquals(
self.traverser.publishTraverse(self.request, 'viewfoo').__class__,
View)
self.assertEquals(
self.traverser.publishTraverse(self.request, 'Foo'),
self.foo)
self.assertRaises(
NotFound,
self.traverser.publishTraverse, self.request, 'morebar')
self.assertRaises(
NotFound,
self.traverser.publishTraverse, self.request, '@@morebar')
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(TraverserTest),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""FS-based directory implementation tests for containers
$Id$
"""
from unittest import TestCase, TestSuite, main, makeSuite
import zope.app.container.directory
class Directory(object):
pass
class Test(TestCase):
def test_Cloner(self):
d = Directory()
d.a = 1
clone = zope.app.container.directory.Cloner(d)('foo')
self.assert_(clone != d)
self.assertEqual(clone.__class__, d.__class__)
def test_suite():
return TestSuite((
makeSuite(Test),
))
if __name__=='__main__':
main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Find functionality tests
$Id$
"""
from unittest import TestCase, main, makeSuite
from zope.app.container.interfaces import IReadContainer
from zope.app.container.interfaces import IObjectFindFilter
from zope.app.container.find import FindAdapter, SimpleIdFindFilter
from zope.app.container.find import SimpleInterfacesFindFilter
from zope.interface import implements, Interface, directlyProvides
class FakeContainer(object):
implements(IReadContainer)
def __init__(self, id, objects):
self._id = id
self._objects = objects
def keys(self):
return [object._id for object in self._objects]
def values(self):
return self._objects
def items(self):
return [(object._id, object) for object in self._objects]
def __getitem__(self, id):
for object in self._objects:
if object._id == id:
return object
raise KeyError("Could not find %s" % id)
def get(self, id, default=None):
for object in self._objects:
if object._id == id:
return object
return default
def __contains__(self, id):
for object in self._objects:
if object.id == id:
return True
return False
def __len__(self):
return len(self._objects)
class FakeInterfaceFoo(Interface):
"""Test interface Foo"""
class FakeInterfaceBar(Interface):
"""Test interface Bar"""
class FakeInterfaceSpam(Interface):
"""Test interface Spam"""
class TestObjectFindFilter(object):
implements(IObjectFindFilter)
def __init__(self, count):
self._count = count
def matches(self, object):
if IReadContainer.providedBy(object):
return len(object) == self._count
else:
return False
class Test(TestCase):
def test_idFind(self):
alpha = FakeContainer('alpha', [])
delta = FakeContainer('delta', [])
beta = FakeContainer('beta', [delta])
gamma = FakeContainer('gamma', [])
tree = FakeContainer(
'tree',
[alpha, beta, gamma])
find = FindAdapter(tree)
# some simple searches
result = find.find([SimpleIdFindFilter(['beta'])])
self.assertEquals([beta], result)
result = find.find([SimpleIdFindFilter(['gamma'])])
self.assertEquals([gamma], result)
result = find.find([SimpleIdFindFilter(['delta'])])
self.assertEquals([delta], result)
# we should not find the container we search on
result = find.find([SimpleIdFindFilter(['tree'])])
self.assertEquals([], result)
# search for multiple ids
result = find.find([SimpleIdFindFilter(['alpha', 'beta'])])
self.assertEquals([alpha, beta], result)
result = find.find([SimpleIdFindFilter(['beta', 'delta'])])
self.assertEquals([beta, delta], result)
# search without any filters, find everything
result = find.find([])
self.assertEquals([alpha, beta, delta, gamma], result)
# search for something that doesn't exist
result = find.find([SimpleIdFindFilter(['foo'])])
self.assertEquals([], result)
# find for something that has two ids at the same time,
# can't ever be the case
result = find.find([SimpleIdFindFilter(['alpha']),
SimpleIdFindFilter(['beta'])])
self.assertEquals([], result)
def test_objectFind(self):
alpha = FakeContainer('alpha', [])
delta = FakeContainer('delta', [])
beta = FakeContainer('beta', [delta])
gamma = FakeContainer('gamma', [])
tree = FakeContainer(
'tree',
[alpha, beta, gamma])
find = FindAdapter(tree)
result = find.find(object_filters=[TestObjectFindFilter(0)])
self.assertEquals([alpha, delta, gamma], result)
result = find.find(object_filters=[TestObjectFindFilter(1)])
self.assertEquals([beta], result)
result = find.find(object_filters=[TestObjectFindFilter(2)])
self.assertEquals([], result)
def test_combinedFind(self):
alpha = FakeContainer('alpha', [])
delta = FakeContainer('delta', [])
beta = FakeContainer('beta', [delta])
gamma = FakeContainer('gamma', [])
tree = FakeContainer(
'tree',
[alpha, beta, gamma])
find = FindAdapter(tree)
result = find.find(id_filters=[SimpleIdFindFilter(['alpha'])],
object_filters=[TestObjectFindFilter(0)])
self.assertEquals([alpha], result)
result = find.find(id_filters=[SimpleIdFindFilter(['alpha'])],
object_filters=[TestObjectFindFilter(1)])
self.assertEquals([], result)
def test_interfaceFind(self):
alpha = FakeContainer('alpha', [])
directlyProvides(alpha, FakeInterfaceBar)
delta = FakeContainer('delta', [])
directlyProvides(delta, FakeInterfaceFoo)
beta = FakeContainer('beta', [delta])
directlyProvides(beta, FakeInterfaceSpam)
gamma = FakeContainer('gamma', [])
tree = FakeContainer(
'tree',
[alpha, beta, gamma])
find = FindAdapter(tree)
result = find.find(object_filters=[
SimpleInterfacesFindFilter(FakeInterfaceFoo, FakeInterfaceSpam)])
self.assertEqual([beta, delta], result)
def test_suite():
return makeSuite(Test)
if __name__=='__main__':
main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Test the IContainer interface.
$Id$
"""
from unittest import TestCase, main, makeSuite
from zope.interface.verify import verifyObject
from zope.app.container.interfaces import IContainer
from zope.app.testing import placelesssetup
def DefaultTestData():
return [('3', '0'), ('2', '1'), ('4', '2'), ('6', '3'), ('0', '4'),
('5', '5'), ('1', '6'), ('8', '7'), ('7', '8'), ('9', '9')]
class BaseTestIContainer(placelesssetup.PlacelessSetup):
"""Base test cases for containers.
Subclasses must define a makeTestObject that takes no
arguments and that returns a new empty test container,
and a makeTestData that also takes no arguments and returns
a sequence of (key, value) pairs that may be stored in
the test container. The list must be at least ten items long.
'NoSuchKey' may not be used as a key value in the returned list.
"""
def __setUp(self):
self.__container = container = self.makeTestObject()
self.__data = data = self.makeTestData()
for k, v in data:
container[k] = v
return container, data
############################################################
# Interface-driven tests:
def testIContainerVerify(self):
verifyObject(IContainer, self.makeTestObject())
def test_keys(self):
# See interface IReadContainer
container = self.makeTestObject()
keys = container.keys()
self.assertEqual(list(keys), [])
container, data = self.__setUp()
keys = container.keys()
keys = list(keys); keys.sort() # convert to sorted list
ikeys = [ k for k, v in data ]; ikeys.sort() # sort input keys
self.assertEqual(keys, ikeys)
def test_get(self):
# See interface IReadContainer
default = object()
data = self.makeTestData()
container = self.makeTestObject()
self.assertRaises(KeyError, container.__getitem__, data[0][0])
self.assertEqual(container.get(data[0][0], default), default)
container, data = self.__setUp()
self.assertRaises(KeyError, container.__getitem__,
self.getUnknownKey())
self.assertEqual(container.get(self.getUnknownKey(), default), default)
for i in (1, 8, 7, 3, 4):
self.assertEqual(container.get(data[i][0], default), data[i][1])
self.assertEqual(container.get(data[i][0]), data[i][1])
def test_values(self):
# See interface IReadContainer
container = self.makeTestObject()
values = container.values()
self.assertEqual(list(values), [])
container, data = self.__setUp()
values = list(container.values())
for k, v in data:
try:
values.remove(v)
except ValueError:
self.fail('Value not in list')
self.assertEqual(values, [])
def test_len(self):
# See interface IReadContainer
container = self.makeTestObject()
self.assertEqual(len(container), 0)
container, data = self.__setUp()
self.assertEqual(len(container), len(data))
def test_items(self):
# See interface IReadContainer
container = self.makeTestObject()
items = container.items()
self.assertEqual(list(items), [])
container, data = self.__setUp()
items = container.items()
items = list(items); items.sort() # convert to sorted list
data.sort() # sort input data
self.assertEqual(items, data)
def test___contains__(self):
# See interface IReadContainer
container = self.makeTestObject()
data = self.makeTestData()
self.assertEqual(not not (data[6][0] in container), False)
container, data = self.__setUp()
self.assertEqual(not not (data[6][0] in container), True)
for i in (1, 8, 7, 3, 4):
self.assertEqual(not not (data[i][0] in container), 1)
def test_delObject(self):
# See interface IWriteContainer
default = object()
data = self.makeTestData()
container = self.makeTestObject()
self.assertRaises(KeyError, container.__delitem__, data[0][0])
container, data = self.__setUp()
self.assertRaises(KeyError, container.__delitem__,
self.getUnknownKey())
for i in (1, 8, 7, 3, 4):
del container[data[i][0]]
for i in (1, 8, 7, 3, 4):
self.assertRaises(KeyError, container.__getitem__, data[i][0])
self.assertEqual(container.get(data[i][0], default), default)
for i in (0, 2, 9, 6, 5):
self.assertEqual(container[data[i][0]], data[i][1])
############################################################
# Tests from Folder
def testEmpty(self):
folder = self.makeTestObject()
data = self.makeTestData()
self.failIf(folder.keys())
self.failIf(folder.values())
self.failIf(folder.items())
self.failIf(len(folder))
self.failIf(data[6][0] in folder)
self.assertEquals(folder.get(data[6][0], None), None)
self.assertRaises(KeyError, folder.__getitem__, data[6][0])
self.assertRaises(KeyError, folder.__delitem__, data[6][0])
def testBadKeyTypes(self):
folder = self.makeTestObject()
data = self.makeTestData()
value = data[1][1]
for name in self.getBadKeyTypes():
self.assertRaises(TypeError, folder.__setitem__, name, value)
def testOneItem(self):
folder = self.makeTestObject()
data = self.makeTestData()
foo = data[0][1]
name = data[0][0]
folder[name] = foo
self.assertEquals(len(folder.keys()), 1)
self.assertEquals(folder.keys()[0], name)
self.assertEquals(len(folder.values()), 1)
self.assertEquals(folder.values()[0], foo)
self.assertEquals(len(folder.items()), 1)
self.assertEquals(folder.items()[0], (name, foo))
self.assertEquals(len(folder), 1)
self.failUnless(name in folder)
# Use an arbitrary id frpm the data set; don;t just use any id, since
# there might be restrictions on their form
self.failIf(data[6][0] in folder)
self.assertEquals(folder.get(name, None), foo)
self.assertEquals(folder[name], foo)
self.assertRaises(KeyError, folder.__getitem__, data[6][0])
foo2 = data[1][1]
name2 = data[1][0]
folder[name2] = foo2
self.assertEquals(len(folder.keys()), 2)
self.assertEquals(not not name2 in folder.keys(), True)
self.assertEquals(len(folder.values()), 2)
self.assertEquals(not not foo2 in folder.values(), True)
self.assertEquals(len(folder.items()), 2)
self.assertEquals(not not (name2, foo2) in folder.items(), True)
self.assertEquals(len(folder), 2)
del folder[name]
del folder[name2]
self.failIf(folder.keys())
self.failIf(folder.values())
self.failIf(folder.items())
self.failIf(len(folder))
self.failIf(name in folder)
self.assertRaises(KeyError, folder.__getitem__, name)
self.assertEquals(folder.get(name, None), None)
self.assertRaises(KeyError, folder.__delitem__, name)
def testManyItems(self):
folder = self.makeTestObject()
data = self.makeTestData()
objects = [ data[i][1] for i in range(4) ]
name0 = data[0][0]
name1 = data[1][0]
name2 = data[2][0]
name3 = data[3][0]
folder[name0] = objects[0]
folder[name1] = objects[1]
folder[name2] = objects[2]
folder[name3] = objects[3]
self.assertEquals(len(folder.keys()), len(objects))
self.failUnless(name0 in folder.keys())
self.failUnless(name1 in folder.keys())
self.failUnless(name2 in folder.keys())
self.failUnless(name3 in folder.keys())
self.assertEquals(len(folder.values()), len(objects))
self.failUnless(objects[0] in folder.values())
self.failUnless(objects[1] in folder.values())
self.failUnless(objects[2] in folder.values())
self.failUnless(objects[3] in folder.values())
self.assertEquals(len(folder.items()), len(objects))
self.failUnless((name0, objects[0]) in folder.items())
self.failUnless((name1, objects[1]) in folder.items())
self.failUnless((name2, objects[2]) in folder.items())
self.failUnless((name3, objects[3]) in folder.items())
self.assertEquals(len(folder), len(objects))
self.failUnless(name0 in folder)
self.failUnless(name1 in folder)
self.failUnless(name2 in folder)
self.failUnless(name3 in folder)
self.failIf(data[5][0] in folder)
self.assertEquals(folder.get(name0, None), objects[0])
self.assertEquals(folder[name0], objects[0])
self.assertEquals(folder.get(name1, None), objects[1])
self.assertEquals(folder[name1], objects[1])
self.assertEquals(folder.get(name2, None), objects[2])
self.assertEquals(folder[name2], objects[2])
self.assertEquals(folder.get(name3, None), objects[3])
self.assertEquals(folder[name3], objects[3])
self.assertEquals(folder.get(data[5][0], None), None)
self.assertRaises(KeyError, folder.__getitem__, data[5][0])
del folder[name0]
self.assertEquals(len(folder), len(objects) - 1)
self.failIf(name0 in folder)
self.failIf(name0 in folder.keys())
self.failIf(objects[0] in folder.values())
self.failIf((name0, objects[0]) in folder.items())
self.assertEquals(folder.get(name0, None), None)
self.assertRaises(KeyError, folder.__getitem__, name0)
self.assertRaises(KeyError, folder.__delitem__, name0)
del folder[name1]
del folder[name2]
del folder[name3]
self.failIf(folder.keys())
self.failIf(folder.values())
self.failIf(folder.items())
self.failIf(len(folder))
self.failIf(name0 in folder)
self.failIf(name1 in folder)
self.failIf(name2 in folder)
self.failIf(name3 in folder)
class TestSampleContainer(BaseTestIContainer, TestCase):
def makeTestObject(self):
from zope.app.container.sample import SampleContainer
return SampleContainer()
def makeTestData(self):
return DefaultTestData()
def getUnknownKey(self):
return '10'
def getBadKeyTypes(self):
return [None, ['foo'], 1, '\xf3abc']
def test_suite():
return makeSuite(TestSampleContainer)
if __name__=='__main__':
main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Object Copier Tests
$Id$
"""
from unittest import TestCase, TestSuite, main, makeSuite
import zope.component
from zope.testing import doctest
from zope.traversing.api import traverse
from zope.component.eventtesting import getEvents, clearEvents
from zope.copypastemove import ObjectCopier
from zope.copypastemove.interfaces import IObjectCopier
from zope.app.component.testing import PlacefulSetup
from zope.app.testing import setup
from zope.app.folder import Folder
class File(object):
pass
def test_copy_events():
"""
Prepare the setup::
>>> root = setup.placefulSetUp(site=True)
>>> zope.component.provideAdapter(ObjectCopier, (None,), IObjectCopier)
Prepare some objects::
>>> folder = Folder()
>>> root[u'foo'] = File()
>>> root[u'folder'] = folder
>>> list(folder.keys())
[]
>>> foo = traverse(root, 'foo') # wrap in ContainedProxy
Now make a copy::
>>> clearEvents()
>>> copier = IObjectCopier(foo)
>>> copier.copyTo(folder, u'bar')
u'bar'
Check that the copy has been done::
>>> list(folder.keys())
[u'bar']
Check what events have been sent::
>>> events = getEvents()
>>> [event.__class__.__name__ for event in events]
['ObjectCopiedEvent', 'ObjectAddedEvent', 'ContainerModifiedEvent']
Check that the ObjectCopiedEvent includes the correct data::
>>> events[0].object is folder[u'bar']
True
>>> events[0].original is root[u'foo']
True
Finally, tear down::
>>> setup.placefulTearDown()
"""
class ObjectCopierTest(PlacefulSetup, TestCase):
def setUp(self):
PlacefulSetup.setUp(self)
PlacefulSetup.buildFolders(self)
zope.component.provideAdapter(ObjectCopier, (None,), IObjectCopier)
def test_copytosame(self):
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
file = traverse(root, 'folder1/file1')
copier = IObjectCopier(file)
copier.copyTo(container, 'file1')
self.failUnless('file1' in container)
self.failUnless('file1-2' in container)
def test_copytosamewithnewname(self):
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
file = traverse(root, 'folder1/file1')
copier = IObjectCopier(file)
copier.copyTo(container, 'file2')
self.failUnless('file1' in container)
self.failUnless('file2' in container)
def test_copytoother(self):
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
target = traverse(root, 'folder2')
file = traverse(root, 'folder1/file1')
copier = IObjectCopier(file)
copier.copyTo(target, 'file1')
self.failUnless('file1' in container)
self.failUnless('file1' in target)
def test_copytootherwithnewname(self):
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
target = traverse(root, 'folder2')
file = traverse(root, 'folder1/file1')
copier = IObjectCopier(file)
copier.copyTo(target, 'file2')
self.failUnless('file1' in container)
self.failUnless('file2' in target)
def test_copytootherwithnamecollision(self):
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
target = traverse(root, 'folder2')
target['file1'] = File()
file = traverse(root, 'folder1/file1')
copier = IObjectCopier(file)
copier.copyTo(target, 'file1')
# we do it twice, just to test auto-name generation
copier.copyTo(target, 'file1')
self.failUnless('file1' in container)
self.failUnless('file1' in target)
self.failUnless('file1-2' in target)
self.failUnless('file1-3' in target)
def test_copyable(self):
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
file = traverse(root, 'folder1/file1')
copier = IObjectCopier(file)
self.failUnless(copier.copyable())
def test_copyableTo(self):
# A file should be copyable to a folder that has an
# object with the same id.
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
file = traverse(root, 'folder1/file1')
copier = IObjectCopier(file)
self.failUnless(copier.copyableTo(container, 'file1'))
def test_copyfoldertosibling(self):
root = self.rootFolder
target = traverse(root, '/folder2')
source = traverse(root, '/folder1/folder1_1')
copier = IObjectCopier(source)
copier.copyTo(target)
self.failUnless('folder1_1' in target)
def test_copyfoldertosame(self):
root = self.rootFolder
target = traverse(root, '/folder1')
source = traverse(root, '/folder1/folder1_1')
copier = IObjectCopier(source)
copier.copyTo(target)
self.failUnless('folder1_1' in target)
def test_copyfoldertosame2(self):
root = self.rootFolder
target = traverse(root, '/folder1/folder1_1')
source = traverse(root, '/folder1/folder1_1/folder1_1_1')
copier = IObjectCopier(source)
copier.copyTo(target)
self.failUnless('folder1_1_1' in target)
def test_copyfolderfromroot(self):
root = self.rootFolder
target = traverse(root, '/folder2')
source = traverse(root, '/folder1')
copier = IObjectCopier(source)
copier.copyTo(target)
self.failUnless('folder1' in target)
def test_copyfolderfromroot2(self):
root = self.rootFolder
target = traverse(root, '/folder2/folder2_1/folder2_1_1')
source = traverse(root, '/folder1')
copier = IObjectCopier(source)
copier.copyTo(target)
self.failUnless('folder1' in target)
def test_suite():
return TestSuite((
makeSuite(ObjectCopierTest),
doctest.DocTestSuite(),
))
if __name__=='__main__':
main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Object Mover Tests
$Id$
"""
from unittest import TestCase, TestSuite, main, makeSuite
import zope.component
from zope.testing import doctest
from zope.traversing.api import traverse
from zope.component.eventtesting import getEvents, clearEvents
from zope.copypastemove import ObjectMover
from zope.copypastemove.interfaces import IObjectMover
from zope.app.component.testing import PlacefulSetup
from zope.app.testing import setup
from zope.app.folder import Folder
class File(object):
pass
def test_move_events():
"""
Prepare the setup::
>>> root = setup.placefulSetUp(site=True)
>>> zope.component.provideAdapter(ObjectMover, (None,), IObjectMover)
Prepare some objects::
>>> folder = Folder()
>>> root[u'foo'] = File()
>>> root[u'folder'] = folder
>>> list(folder.keys())
[]
>>> foo = traverse(root, 'foo') # wrap in ContainedProxy
Now move it::
>>> clearEvents()
>>> mover = IObjectMover(foo)
>>> mover.moveableTo(folder)
True
>>> mover.moveTo(folder, u'bar')
u'bar'
Check that the move has been done::
>>> list(root.keys())
[u'folder']
>>> list(folder.keys())
[u'bar']
Check what events have been sent::
>>> events = getEvents()
>>> [event.__class__.__name__ for event in events]
['ObjectMovedEvent', 'ContainerModifiedEvent', 'ContainerModifiedEvent']
Verify that the ObjectMovedEvent includes the correct data::
>>> events[0].oldName, events[0].newName
(u'foo', u'bar')
>>> events[0].oldParent is root
True
>>> events[0].newParent is folder
True
Let's look the other events:
>>> events[1].object is folder
True
>>> events[2].object is root
True
Finally, tear down::
>>> setup.placefulTearDown()
"""
class ObjectMoverTest(PlacefulSetup, TestCase):
def setUp(self):
PlacefulSetup.setUp(self)
PlacefulSetup.buildFolders(self)
zope.component.provideAdapter(ObjectMover, (None,), )
def test_movetosame(self):
# Should be a noop, because "moving" to same location
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
file = traverse(root, 'folder1/file1')
mover = IObjectMover(file)
mover.moveTo(container, 'file1')
self.failUnless('file1' in container)
self.assertEquals(len(container), 3)
def test_movetosamewithnewname(self):
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
file = traverse(root, 'folder1/file1')
mover = IObjectMover(file)
mover.moveTo(container, 'file2')
self.failIf('file1' in container)
self.failUnless('file2' in container)
def test_movetoother(self):
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
target = traverse(root, 'folder2')
file = traverse(root, 'folder1/file1')
mover = IObjectMover(file)
mover.moveTo(target, 'file1')
self.failIf('file1' in container)
self.failUnless('file1' in target)
def test_movetootherwithnewname(self):
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
target = traverse(root, 'folder2')
file = traverse(root, 'folder1/file1')
mover = IObjectMover(file)
mover.moveTo(target, 'file2')
self.failIf('file1' in container)
self.failUnless('file2' in target)
def test_movetootherwithnamecollision(self):
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
target = traverse(root, 'folder2')
target['file1'] = File()
file = traverse(root, 'folder1/file1')
mover = IObjectMover(file)
mover.moveTo(target, 'file1')
self.failIf('file1' in container)
self.failUnless('file1' in target)
self.failUnless('file1-2' in target)
def test_moveable(self):
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
file = traverse(root, 'folder1/file1')
mover = IObjectMover(file)
self.failUnless(mover.moveable())
def test_moveableTo(self):
# A file should be moveable to a folder that has an
# object with the same id.
root = self.rootFolder
container = traverse(root, 'folder1')
container['file1'] = File()
file = traverse(root, 'folder1/file1')
mover = IObjectMover(file)
self.failUnless(mover.moveableTo(container, 'file1'))
def test_movefoldertosibling(self):
root = self.rootFolder
target = traverse(root, '/folder2')
source = traverse(root, '/folder1/folder1_1')
mover = IObjectMover(source)
mover.moveTo(target)
self.failUnless('folder1_1' in target)
def test_movefoldertosame(self):
# Should be a noop, because "moving" to same location
root = self.rootFolder
target = traverse(root, '/folder1')
source = traverse(root, '/folder1/folder1_1')
mover = IObjectMover(source)
mover.moveTo(target)
self.failUnless('folder1_1' in target)
self.assertEquals(len(target), 2)
def test_movefoldertosame2(self):
# Should be a noop, because "moving" to same location
root = self.rootFolder
target = traverse(root, '/folder1/folder1_1')
source = traverse(root, '/folder1/folder1_1/folder1_1_1')
mover = IObjectMover(source)
mover.moveTo(target)
self.failUnless('folder1_1_1' in target)
self.assertEquals(len(target), 2)
def test_movefolderfromroot(self):
root = self.rootFolder
target = traverse(root, '/folder2')
source = traverse(root, '/folder1')
mover = IObjectMover(source)
mover.moveTo(target)
self.failUnless('folder1' in target)
def test_movefolderfromroot2(self):
root = self.rootFolder
target = traverse(root, '/folder2/folder2_1/folder2_1_1')
source = traverse(root, '/folder1')
mover = IObjectMover(source)
mover.moveTo(target)
self.failUnless('folder1' in target)
def test_suite():
return TestSuite((
makeSuite(ObjectMoverTest),
doctest.DocTestSuite(),
))
if __name__=='__main__':
main(defaultTest='test_suite')
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Test the OrderedContainer.
$Id$
"""
import unittest
from zope.testing.doctestunit import DocTestSuite
from zope.component.eventtesting import getEvents, clearEvents
from zope.app.testing import placelesssetup, setup
def test_order_events():
"""
Prepare the setup::
>>> root = setup.placefulSetUp(site=True)
Prepare some objects::
>>> from zope.app.container.ordered import OrderedContainer
>>> oc = OrderedContainer()
>>> oc['foo'] = 'bar'
>>> oc['baz'] = 'quux'
>>> oc['zork'] = 'grue'
>>> oc.keys()
['foo', 'baz', 'zork']
Now change the order::
>>> clearEvents()
>>> oc.updateOrder(['baz', 'foo', 'zork'])
>>> oc.keys()
['baz', 'foo', 'zork']
Check what events have been sent::
>>> events = getEvents()
>>> [event.__class__.__name__ for event in events]
['ContainerModifiedEvent']
This is in fact a specialized modification event::
>>> from zope.lifecycleevent.interfaces import IObjectModifiedEvent
>>> IObjectModifiedEvent.providedBy(events[0])
True
Finally, tear down::
>>> setup.placefulTearDown()
"""
def test_all_items_available_at_object_added_event():
"""
Prepare the setup::
>>> root = setup.placefulSetUp(site=True)
Now register an event subscriber to object added events.
>>> import zope.component
>>> from zope.app.container import interfaces
>>> @zope.component.adapter(interfaces.IObjectAddedEvent)
... def printContainerKeys(event):
... print event.newParent.keys()
>>> zope.component.provideHandler(printContainerKeys)
Now we are adding an object to the container.
>>> from zope.app.container.ordered import OrderedContainer
>>> oc = OrderedContainer()
>>> oc['foo'] = 'FOO'
['foo']
Finally, tear down::
>>> setup.placefulTearDown()
"""
def test_suite():
suite = unittest.TestSuite()
suite.addTest(DocTestSuite("zope.app.container.ordered",
setUp=placelesssetup.setUp,
tearDown=placelesssetup.tearDown))
suite.addTest(DocTestSuite())
return suite
if __name__ == '__main__':
unittest.main()
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Test container ISized adapter.
$Id$
"""
import unittest
from zope.interface import implements
from zope.size.interfaces import ISized
from zope.app.container.interfaces import IContainer
class DummyContainer(object):
implements(IContainer)
def __init__(self, numitems):
self._numitems = numitems
def __len__(self):
return self._numitems
class Test(unittest.TestCase):
def testImplementsISized(self):
from zope.app.container.size import ContainerSized
sized = ContainerSized(DummyContainer(23))
self.assert_(ISized.providedBy(sized))
def testEmptyContainer(self):
from zope.app.container.size import ContainerSized
obj = DummyContainer(0)
sized = ContainerSized(obj)
self.assertEqual(sized.sizeForSorting(), ('item', 0))
self.assertEqual(sized.sizeForDisplay(), u'${items} items')
self.assertEqual(sized.sizeForDisplay().mapping['items'], '0')
def testOneItem(self):
from zope.app.container.size import ContainerSized
obj = DummyContainer(1)
sized = ContainerSized(obj)
self.assertEqual(sized.sizeForSorting(), ('item', 1))
self.assertEqual(sized.sizeForDisplay(), u'1 item')
def testSeveralItems(self):
from zope.app.container.size import ContainerSized
obj = DummyContainer(2)
sized = ContainerSized(obj)
self.assertEqual(sized.sizeForSorting(), ('item', 2))
self.assertEqual(sized.sizeForDisplay(), u'${items} items')
self.assertEqual(sized.sizeForDisplay().mapping['items'], '2')
def test_suite():
loader = unittest.TestLoader()
return loader.loadTestsFromTestCase(Test)
if __name__=='__main__':
unittest.TextTestRunner().run(test_suite())
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Traversal components for containers
$Id$
"""
__docformat__ = 'restructuredtext'
from zope.interface import implements
from zope.component import queryMultiAdapter
from zope.traversing.interfaces import TraversalError, ITraversable
from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.publisher.interfaces.xmlrpc import IXMLRPCPublisher
from zope.publisher.interfaces import NotFound
from zope.app import zapi
from zope.app.container.interfaces import ISimpleReadContainer, IItemContainer
from zope.app.container.interfaces import IReadContainer
# Note that the next two classes are included here because they
# can be used for multiple view types.
class ContainerTraverser(object):
"""A traverser that knows how to look up objects by name in a container."""
implements(IBrowserPublisher, IXMLRPCPublisher)
__used_for__ = ISimpleReadContainer
def __init__(self, container, request):
self.context = container
self.request = request
def publishTraverse(self, request, name):
"""See zope.publisher.interfaces.IPublishTraverse"""
subob = self.context.get(name, None)
if subob is None:
view = queryMultiAdapter((self.context, request), name=name)
if view is not None:
return view
raise NotFound(self.context, name, request)
return subob
def browserDefault(self, request):
"""See zope.publisher.browser.interfaces.IBrowserPublisher"""
view_name = zapi.getDefaultViewName(self.context, request)
view_uri = "@@%s" %view_name
return self.context, (view_uri,)
class ItemTraverser(ContainerTraverser):
"""A traverser that knows how to look up objects by name in an item
container."""
__used_for__ = IItemContainer
def publishTraverse(self, request, name):
"""See zope.publisher.interfaces.IPublishTraverse"""
try:
return self.context[name]
except KeyError:
view = queryMultiAdapter((self.context, request), name=name)
if view is not None:
return view
raise NotFound(self.context, name, request)
_marker = object()
class ContainerTraversable(object):
"""Traverses containers via `getattr` and `get`."""
implements(ITraversable)
__used_for__ = IReadContainer
def __init__(self, container):
self._container = container
def traverse(self, name, furtherPath):
container = self._container
v = container.get(name, _marker)
if v is _marker:
v = getattr(container, name, _marker)
if v is _marker:
raise TraversalError(container, name)
return v
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment