Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
Zope
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
Zope
Commits
058f639b
Commit
058f639b
authored
Apr 25, 2005
by
Andreas Jung
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
added BTreeFolder2
parent
15d452be
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
1248 additions
and
0 deletions
+1248
-0
lib/python/Products/BTreeFolder2/BTreeFolder2.py
lib/python/Products/BTreeFolder2/BTreeFolder2.py
+514
-0
lib/python/Products/BTreeFolder2/BTreeFolder2.pyc
lib/python/Products/BTreeFolder2/BTreeFolder2.pyc
+0
-0
lib/python/Products/BTreeFolder2/CHANGES.txt
lib/python/Products/BTreeFolder2/CHANGES.txt
+21
-0
lib/python/Products/BTreeFolder2/CMFBTreeFolder.py
lib/python/Products/BTreeFolder2/CMFBTreeFolder.py
+68
-0
lib/python/Products/BTreeFolder2/README.txt
lib/python/Products/BTreeFolder2/README.txt
+124
-0
lib/python/Products/BTreeFolder2/__init__.py
lib/python/Products/BTreeFolder2/__init__.py
+51
-0
lib/python/Products/BTreeFolder2/__init__.pyc
lib/python/Products/BTreeFolder2/__init__.pyc
+0
-0
lib/python/Products/BTreeFolder2/btreefolder2.gif
lib/python/Products/BTreeFolder2/btreefolder2.gif
+0
-0
lib/python/Products/BTreeFolder2/contents.dtml
lib/python/Products/BTreeFolder2/contents.dtml
+164
-0
lib/python/Products/BTreeFolder2/folderAdd.dtml
lib/python/Products/BTreeFolder2/folderAdd.dtml
+67
-0
lib/python/Products/BTreeFolder2/tests/__init__.py
lib/python/Products/BTreeFolder2/tests/__init__.py
+1
-0
lib/python/Products/BTreeFolder2/tests/__init__.pyc
lib/python/Products/BTreeFolder2/tests/__init__.pyc
+0
-0
lib/python/Products/BTreeFolder2/tests/testBTreeFolder2.py
lib/python/Products/BTreeFolder2/tests/testBTreeFolder2.py
+237
-0
lib/python/Products/BTreeFolder2/tests/testBTreeFolder2.pyc
lib/python/Products/BTreeFolder2/tests/testBTreeFolder2.pyc
+0
-0
lib/python/Products/BTreeFolder2/version.txt
lib/python/Products/BTreeFolder2/version.txt
+1
-0
No files found.
lib/python/Products/BTreeFolder2/BTreeFolder2.py
0 → 100644
View file @
058f639b
##############################################################################
#
# 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.0 (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
#
##############################################################################
"""BTreeFolder2
$Id: BTreeFolder2.py,v 1.27 2004/03/17 22:49:25 urbanape Exp $
"""
import
sys
from
cgi
import
escape
from
urllib
import
quote
from
random
import
randint
from
types
import
StringType
import
Globals
from
Globals
import
DTMLFile
from
Globals
import
Persistent
from
Acquisition
import
aq_base
from
BTrees.OOBTree
import
OOBTree
from
BTrees.OIBTree
import
OIBTree
,
union
from
BTrees.Length
import
Length
from
ZODB.POSException
import
ConflictError
from
OFS.ObjectManager
import
BadRequestException
,
BeforeDeleteException
from
OFS.Folder
import
Folder
from
AccessControl
import
getSecurityManager
,
ClassSecurityInfo
from
AccessControl.Permissions
import
access_contents_information
,
\
view_management_screens
from
zLOG
import
LOG
,
INFO
,
ERROR
,
WARNING
from
Products.ZCatalog.Lazy
import
LazyMap
manage_addBTreeFolderForm
=
DTMLFile
(
'folderAdd'
,
globals
())
def
manage_addBTreeFolder
(
dispatcher
,
id
,
title
=
''
,
REQUEST
=
None
):
"""Adds a new BTreeFolder object with id *id*.
"""
id
=
str
(
id
)
ob
=
BTreeFolder2
(
id
)
ob
.
title
=
str
(
title
)
dispatcher
.
_setObject
(
id
,
ob
)
ob
=
dispatcher
.
_getOb
(
id
)
if
REQUEST
is
not
None
:
return
dispatcher
.
manage_main
(
dispatcher
,
REQUEST
,
update_menu
=
1
)
listtext0
=
'''<select name="ids:list" multiple="multiple" size="%s">
'''
listtext1
=
'''<option value="%s">%s</option>
'''
listtext2
=
'''</select>
'''
_marker
=
[]
# Create a new marker object.
MAX_UNIQUEID_ATTEMPTS
=
1000
class
ExhaustedUniqueIdsError
(
Exception
):
pass
class
BTreeFolder2Base
(
Persistent
):
"""Base for BTree-based folders.
"""
security
=
ClassSecurityInfo
()
manage_options
=
(
({
'label'
:
'Contents'
,
'action'
:
'manage_main'
,},
)
+
Folder
.
manage_options
[
1
:]
)
security
.
declareProtected
(
view_management_screens
,
'manage_main'
)
manage_main
=
DTMLFile
(
'contents'
,
globals
())
_tree
=
None
# OOBTree: { id -> object }
_count
=
None
# A BTrees.Length
_v_nextid
=
0
# The integer component of the next generated ID
_mt_index
=
None
# OOBTree: { meta_type -> OIBTree: { id -> 1 } }
title
=
''
def
__init__
(
self
,
id
=
None
):
if
id
is
not
None
:
self
.
id
=
id
self
.
_initBTrees
()
def
_initBTrees
(
self
):
self
.
_tree
=
OOBTree
()
self
.
_count
=
Length
()
self
.
_mt_index
=
OOBTree
()
def
_populateFromFolder
(
self
,
source
):
"""Fill this folder with the contents of another folder.
"""
for
name
in
source
.
objectIds
():
value
=
source
.
_getOb
(
name
,
None
)
if
value
is
not
None
:
self
.
_setOb
(
name
,
aq_base
(
value
))
security
.
declareProtected
(
view_management_screens
,
'manage_fixCount'
)
def
manage_fixCount
(
self
):
"""Calls self._fixCount() and reports the result as text.
"""
old
,
new
=
self
.
_fixCount
()
path
=
'/'
.
join
(
self
.
getPhysicalPath
())
if
old
==
new
:
return
"No count mismatch detected in BTreeFolder2 at %s."
%
path
else
:
return
(
"Fixed count mismatch in BTreeFolder2 at %s. "
"Count was %d; corrected to %d"
%
(
path
,
old
,
new
))
def
_fixCount
(
self
):
"""Checks if the value of self._count disagrees with
len(self.objectIds()). If so, corrects self._count. Returns the
old and new count values. If old==new, no correction was
performed.
"""
old
=
self
.
_count
()
new
=
len
(
self
.
objectIds
())
if
old
!=
new
:
self
.
_count
.
set
(
new
)
return
old
,
new
security
.
declareProtected
(
view_management_screens
,
'manage_cleanup'
)
def
manage_cleanup
(
self
):
"""Calls self._cleanup() and reports the result as text.
"""
v
=
self
.
_cleanup
()
path
=
'/'
.
join
(
self
.
getPhysicalPath
())
if
v
:
return
"No damage detected in BTreeFolder2 at %s."
%
path
else
:
return
(
"Fixed BTreeFolder2 at %s. "
"See the log for more details."
%
path
)
def
_cleanup
(
self
):
"""Cleans up errors in the BTrees.
Certain ZODB bugs have caused BTrees to become slightly insane.
Fortunately, there is a way to clean up damaged BTrees that
always seems to work: make a new BTree containing the items()
of the old one.
Returns 1 if no damage was detected, or 0 if damage was
detected and fixed.
"""
from
BTrees.check
import
check
path
=
'/'
.
join
(
self
.
getPhysicalPath
())
try
:
check
(
self
.
_tree
)
for
key
in
self
.
_tree
.
keys
():
if
not
self
.
_tree
.
has_key
(
key
):
raise
AssertionError
(
"Missing value for key: %s"
%
repr
(
key
))
check
(
self
.
_mt_index
)
for
key
,
value
in
self
.
_mt_index
.
items
():
if
(
not
self
.
_mt_index
.
has_key
(
key
)
or
self
.
_mt_index
[
key
]
is
not
value
):
raise
AssertionError
(
"Missing or incorrect meta_type index: %s"
%
repr
(
key
))
check
(
value
)
for
k
in
value
.
keys
():
if
not
value
.
has_key
(
k
):
raise
AssertionError
(
"Missing values for meta_type index: %s"
%
repr
(
key
))
return
1
except
AssertionError
:
LOG
(
'BTreeFolder2'
,
WARNING
,
'Detected damage to %s. Fixing now.'
%
path
,
error
=
sys
.
exc_info
())
try
:
self
.
_tree
=
OOBTree
(
self
.
_tree
)
mt_index
=
OOBTree
()
for
key
,
value
in
self
.
_mt_index
.
items
():
mt_index
[
key
]
=
OIBTree
(
value
)
self
.
_mt_index
=
mt_index
except
:
LOG
(
'BTreeFolder2'
,
ERROR
,
'Failed to fix %s.'
%
path
,
error
=
sys
.
exc_info
())
raise
else
:
LOG
(
'BTreeFolder2'
,
INFO
,
'Fixed %s.'
%
path
)
return
0
def
_getOb
(
self
,
id
,
default
=
_marker
):
"""Return the named object from the folder.
"""
tree
=
self
.
_tree
if
default
is
_marker
:
ob
=
tree
[
id
]
return
ob
.
__of__
(
self
)
else
:
ob
=
tree
.
get
(
id
,
_marker
)
if
ob
is
_marker
:
return
default
else
:
return
ob
.
__of__
(
self
)
def
_setOb
(
self
,
id
,
object
):
"""Store the named object in the folder.
"""
tree
=
self
.
_tree
if
tree
.
has_key
(
id
):
raise
KeyError
(
'There is already an item named "%s".'
%
id
)
tree
[
id
]
=
object
self
.
_count
.
change
(
1
)
# Update the meta type index.
mti
=
self
.
_mt_index
meta_type
=
getattr
(
object
,
'meta_type'
,
None
)
if
meta_type
is
not
None
:
ids
=
mti
.
get
(
meta_type
,
None
)
if
ids
is
None
:
ids
=
OIBTree
()
mti
[
meta_type
]
=
ids
ids
[
id
]
=
1
def
_delOb
(
self
,
id
):
"""Remove the named object from the folder.
"""
tree
=
self
.
_tree
meta_type
=
getattr
(
tree
[
id
],
'meta_type'
,
None
)
del
tree
[
id
]
self
.
_count
.
change
(
-
1
)
# Update the meta type index.
if
meta_type
is
not
None
:
mti
=
self
.
_mt_index
ids
=
mti
.
get
(
meta_type
,
None
)
if
ids
is
not
None
and
ids
.
has_key
(
id
):
del
ids
[
id
]
if
not
ids
:
# Removed the last object of this meta_type.
# Prune the index.
del
mti
[
meta_type
]
security
.
declareProtected
(
view_management_screens
,
'getBatchObjectListing'
)
def
getBatchObjectListing
(
self
,
REQUEST
=
None
):
"""Return a structure for a page template to show the list of objects.
"""
if
REQUEST
is
None
:
REQUEST
=
{}
pref_rows
=
int
(
REQUEST
.
get
(
'dtpref_rows'
,
20
))
b_start
=
int
(
REQUEST
.
get
(
'b_start'
,
1
))
b_count
=
int
(
REQUEST
.
get
(
'b_count'
,
1000
))
b_end
=
b_start
+
b_count
-
1
url
=
self
.
absolute_url
()
+
'/manage_main'
idlist
=
self
.
objectIds
()
# Pre-sorted.
count
=
self
.
objectCount
()
if
b_end
<
count
:
next_url
=
url
+
'?b_start=%d'
%
(
b_start
+
b_count
)
else
:
b_end
=
count
next_url
=
''
if
b_start
>
1
:
prev_url
=
url
+
'?b_start=%d'
%
max
(
b_start
-
b_count
,
1
)
else
:
prev_url
=
''
formatted
=
[]
formatted
.
append
(
listtext0
%
pref_rows
)
for
i
in
range
(
b_start
-
1
,
b_end
):
optID
=
escape
(
idlist
[
i
])
formatted
.
append
(
listtext1
%
(
escape
(
optID
,
quote
=
1
),
optID
))
formatted
.
append
(
listtext2
)
return
{
'b_start'
:
b_start
,
'b_end'
:
b_end
,
'prev_batch_url'
:
prev_url
,
'next_batch_url'
:
next_url
,
'formatted_list'
:
''
.
join
(
formatted
)}
security
.
declareProtected
(
view_management_screens
,
'manage_object_workspace'
)
def
manage_object_workspace
(
self
,
ids
=
(),
REQUEST
=
None
):
'''Redirects to the workspace of the first object in
the list.'''
if
ids
and
REQUEST
is
not
None
:
REQUEST
.
RESPONSE
.
redirect
(
'%s/%s/manage_workspace'
%
(
self
.
absolute_url
(),
quote
(
ids
[
0
])))
else
:
return
self
.
manage_main
(
self
,
REQUEST
)
security
.
declareProtected
(
access_contents_information
,
'tpValues'
)
def
tpValues
(
self
):
"""Ensures the items don't show up in the left pane.
"""
return
()
security
.
declareProtected
(
access_contents_information
,
'objectCount'
)
def
objectCount
(
self
):
"""Returns the number of items in the folder."""
return
self
.
_count
()
security
.
declareProtected
(
access_contents_information
,
'has_key'
)
def
has_key
(
self
,
id
):
"""Indicates whether the folder has an item by ID.
"""
return
self
.
_tree
.
has_key
(
id
)
security
.
declareProtected
(
access_contents_information
,
'objectIds'
)
def
objectIds
(
self
,
spec
=
None
):
# Returns a list of subobject ids of the current object.
# If 'spec' is specified, returns objects whose meta_type
# matches 'spec'.
if
spec
is
not
None
:
if
isinstance
(
spec
,
StringType
):
spec
=
[
spec
]
mti
=
self
.
_mt_index
set
=
None
for
meta_type
in
spec
:
ids
=
mti
.
get
(
meta_type
,
None
)
if
ids
is
not
None
:
set
=
union
(
set
,
ids
)
if
set
is
None
:
return
()
else
:
return
set
.
keys
()
else
:
return
self
.
_tree
.
keys
()
security
.
declareProtected
(
access_contents_information
,
'objectValues'
)
def
objectValues
(
self
,
spec
=
None
):
# Returns a list of actual subobjects of the current object.
# If 'spec' is specified, returns only objects whose meta_type
# match 'spec'.
return
LazyMap
(
self
.
_getOb
,
self
.
objectIds
(
spec
))
security
.
declareProtected
(
access_contents_information
,
'objectItems'
)
def
objectItems
(
self
,
spec
=
None
):
# Returns a list of (id, subobject) tuples of the current object.
# If 'spec' is specified, returns only objects whose meta_type match
# 'spec'
return
LazyMap
(
lambda
id
,
_getOb
=
self
.
_getOb
:
(
id
,
_getOb
(
id
)),
self
.
objectIds
(
spec
))
security
.
declareProtected
(
access_contents_information
,
'objectMap'
)
def
objectMap
(
self
):
# Returns a tuple of mappings containing subobject meta-data.
return
LazyMap
(
lambda
(
k
,
v
):
{
'id'
:
k
,
'meta_type'
:
getattr
(
v
,
'meta_type'
,
None
)},
self
.
_tree
.
items
(),
self
.
_count
())
# superValues() looks for the _objects attribute, but the implementation
# would be inefficient, so superValues() support is disabled.
_objects
=
()
security
.
declareProtected
(
access_contents_information
,
'objectIds_d'
)
def
objectIds_d
(
self
,
t
=
None
):
ids
=
self
.
objectIds
(
t
)
res
=
{}
for
id
in
ids
:
res
[
id
]
=
1
return
res
security
.
declareProtected
(
access_contents_information
,
'objectMap_d'
)
def
objectMap_d
(
self
,
t
=
None
):
return
self
.
objectMap
()
def
_checkId
(
self
,
id
,
allow_dup
=
0
):
if
not
allow_dup
and
self
.
has_key
(
id
):
raise
BadRequestException
,
(
'The id "%s" is invalid--'
'it is already in use.'
%
id
)
def
_setObject
(
self
,
id
,
object
,
roles
=
None
,
user
=
None
,
set_owner
=
1
):
v
=
self
.
_checkId
(
id
)
if
v
is
not
None
:
id
=
v
# If an object by the given id already exists, remove it.
if
self
.
has_key
(
id
):
self
.
_delObject
(
id
)
self
.
_setOb
(
id
,
object
)
object
=
self
.
_getOb
(
id
)
if
set_owner
:
object
.
manage_fixupOwnershipAfterAdd
()
# Try to give user the local role "Owner", but only if
# no local roles have been set on the object yet.
if
hasattr
(
object
,
'__ac_local_roles__'
):
if
object
.
__ac_local_roles__
is
None
:
user
=
getSecurityManager
().
getUser
()
if
user
is
not
None
:
userid
=
user
.
getId
()
if
userid
is
not
None
:
object
.
manage_setLocalRoles
(
userid
,
[
'Owner'
])
object
.
manage_afterAdd
(
object
,
self
)
return
id
def
_delObject
(
self
,
id
,
dp
=
1
):
object
=
self
.
_getOb
(
id
)
try
:
object
.
manage_beforeDelete
(
object
,
self
)
except
BeforeDeleteException
,
ob
:
raise
except
ConflictError
:
raise
except
:
LOG
(
'Zope'
,
ERROR
,
'manage_beforeDelete() threw'
,
error
=
sys
.
exc_info
())
self
.
_delOb
(
id
)
# Aliases for mapping-like access.
__len__
=
objectCount
keys
=
objectIds
values
=
objectValues
items
=
objectItems
# backward compatibility
hasObject
=
has_key
security
.
declareProtected
(
access_contents_information
,
'get'
)
def
get
(
self
,
name
,
default
=
None
):
return
self
.
_getOb
(
name
,
default
)
# Utility for generating unique IDs.
security
.
declareProtected
(
access_contents_information
,
'generateId'
)
def
generateId
(
self
,
prefix
=
'item'
,
suffix
=
''
,
rand_ceiling
=
999999999
):
"""Returns an ID not used yet by this folder.
The ID is unlikely to collide with other threads and clients.
The IDs are sequential to optimize access to objects
that are likely to have some relation.
"""
tree
=
self
.
_tree
n
=
self
.
_v_nextid
attempt
=
0
while
1
:
if
n
%
4000
!=
0
and
n
<=
rand_ceiling
:
id
=
'%s%d%s'
%
(
prefix
,
n
,
suffix
)
if
not
tree
.
has_key
(
id
):
break
n
=
randint
(
1
,
rand_ceiling
)
attempt
=
attempt
+
1
if
attempt
>
MAX_UNIQUEID_ATTEMPTS
:
# Prevent denial of service
raise
ExhaustedUniqueIdsError
self
.
_v_nextid
=
n
+
1
return
id
def
__getattr__
(
self
,
name
):
# Boo hoo hoo! Zope 2 prefers implicit acquisition over traversal
# to subitems, and __bobo_traverse__ hooks don't work with
# restrictedTraverse() unless __getattr__() is also present.
# Oh well.
res
=
self
.
_tree
.
get
(
name
)
if
res
is
None
:
raise
AttributeError
,
name
return
res
Globals
.
InitializeClass
(
BTreeFolder2Base
)
class
BTreeFolder2
(
BTreeFolder2Base
,
Folder
):
"""BTreeFolder2 based on OFS.Folder.
"""
meta_type
=
'BTreeFolder2'
def
_checkId
(
self
,
id
,
allow_dup
=
0
):
Folder
.
_checkId
(
self
,
id
,
allow_dup
)
BTreeFolder2Base
.
_checkId
(
self
,
id
,
allow_dup
)
Globals
.
InitializeClass
(
BTreeFolder2
)
lib/python/Products/BTreeFolder2/BTreeFolder2.pyc
0 → 100644
View file @
058f639b
File added
lib/python/Products/BTreeFolder2/CHANGES.txt
0 → 100644
View file @
058f639b
Version 1.0.1
- ConflictError was swallowed by _delObject. This could break code
expecting to do cleanups before deletion.
- Renamed hasObject() to has_key(). hasObject() conflicted with
another product.
- You can now visit objects whose names have a trailing space.
Version 1.0
- BTreeFolder2s now use an icon contributed by Chris Withers.
- Since recent ZODB releases have caused minor corruption in BTrees,
there is now a manage_cleanup method for fixing damaged BTrees
contained in BTreeFolders.
Version 0.5.1
- Fixed the CMFBTreeFolder constructor.
lib/python/Products/BTreeFolder2/CMFBTreeFolder.py
0 → 100644
View file @
058f639b
##############################################################################
#
# 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.0 (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
#
##############################################################################
"""CMFBTreeFolder
$Id: CMFBTreeFolder.py,v 1.2 2002/10/30 14:54:18 shane Exp $
"""
import
Globals
from
BTreeFolder2
import
BTreeFolder2Base
from
Products.CMFCore.PortalFolder
import
PortalFolder
import
Products.CMFCore.PortalFolder
_actions
=
Products
.
CMFCore
.
PortalFolder
.
factory_type_information
[
0
][
'actions'
]
factory_type_information
=
(
{
'id'
:
'CMF BTree Folder'
,
'meta_type'
:
'CMF BTree Folder'
,
'description'
:
"""
\
CMF folder designed to hold a lot of objects."""
,
'icon'
:
'folder_icon.gif'
,
'product'
:
'BTreeFolder2'
,
'factory'
:
'manage_addCMFBTreeFolder'
,
'filter_content_types'
:
0
,
'immediate_view'
:
'folder_edit_form'
,
'actions'
:
_actions
,
},
)
def
manage_addCMFBTreeFolder
(
dispatcher
,
id
,
title
=
''
,
REQUEST
=
None
):
"""Adds a new BTreeFolder object with id *id*.
"""
id
=
str
(
id
)
ob
=
CMFBTreeFolder
(
id
)
ob
.
title
=
str
(
title
)
dispatcher
.
_setObject
(
id
,
ob
)
ob
=
dispatcher
.
_getOb
(
id
)
if
REQUEST
is
not
None
:
REQUEST
[
'RESPONSE'
].
redirect
(
ob
.
absolute_url
()
+
'/manage_main'
)
class
CMFBTreeFolder
(
BTreeFolder2Base
,
PortalFolder
):
"""BTree folder for CMF sites.
"""
meta_type
=
'CMF BTree Folder'
def
__init__
(
self
,
id
,
title
=
''
):
PortalFolder
.
__init__
(
self
,
id
,
title
)
BTreeFolder2Base
.
__init__
(
self
,
id
)
def
_checkId
(
self
,
id
,
allow_dup
=
0
):
PortalFolder
.
_checkId
(
self
,
id
,
allow_dup
)
BTreeFolder2Base
.
_checkId
(
self
,
id
,
allow_dup
)
Globals
.
InitializeClass
(
CMFBTreeFolder
)
lib/python/Products/BTreeFolder2/README.txt
0 → 100644
View file @
058f639b
Contact
=======
Shane Hathaway
Zope Corporation
shane at zope dot com
BTreeFolder2 Product
====================
BTreeFolder2 is a Zope product that acts like a Zope folder but can
store many more items.
When you fill a Zope folder with too many items, both Zope and your
browser get overwhelmed. Zope has to load and store a large folder
object, and the browser has to render large HTML tables repeatedly.
Zope can store a lot of objects, but it has trouble storing a lot of
objects in a single standard folder.
Zope Corporation once had an extensive discussion on the subject. It
was decided that we would expand standard folders to handle large
numbers of objects gracefully. Unfortunately, Zope folders are used
and extended in so many ways today that it would be difficult to
modify standard folders in a way that would be compatible with all
Zope products.
So the BTreeFolder product was born. It stored all subobjects in a
ZODB BTree, a structure designed to allow many items without loading
them all into memory. It also rendered the contents of the folder as
a simple select list rather than a table. Most browsers have no
trouble rendering large select lists.
But there was still one issue remaining. BTreeFolders still stored
the ID of all subobjects in a single database record. If you put tens
of thousands of items in a single BTreeFolder, you would still be
loading and storing a multi-megabyte folder object. Zope can do this,
but not quickly, and not without bloating the database.
BTreeFolder2 solves this issue. It stores not only the subobjects but
also the IDs of the subobjects in a BTree. It also batches the list
of items in the UI, showing only 1000 items at a time. So if you
write your application carefully, you can use a BTreeFolder2 to store
as many items as will fit in physical storage.
There are products that depend on the internal structure of the
original BTreeFolder, however. So rather than risk breaking those
products, the product has been renamed. You can have both products
installed at the same time. If you're developing new applications,
you should use BTreeFolder2.
Installation
============
Untar BTreeFolder2 in your Products directory and restart Zope.
BTreeFolder2 will now be available in your "Add" drop-down.
Additionally, if you have CMF installed, the BTreeFolder2 product also
provides the "CMF BTree Folder" addable type.
Usage
=====
The BTreeFolder2 user interface shows a list of items rather than a
series of checkboxes. To visit an item, select it in the list and
click the "edit" button.
BTreeFolder2 objects provide Python dictionary-like methods to make them
easier to use in Python code than standard folders::
has_key(key)
keys()
values()
items()
get(key, default=None)
__len__()
keys(), values(), and items() return sequences, but not necessarily
tuples or lists. Use len(folder) to call the __len__() method. The
objects returned by values() and items() have acquisition wrappers.
BTreeFolder2 also provides a method for generating unique,
non-overlapping IDs::
generateId(prefix='item', suffix='', rand_ceiling=999999999)
The ID returned by this method is guaranteed to not clash with any
other ID in the folder. Use the returned value as the ID for new
objects. The generated IDs tend to be sequential so that objects that
are likely related in some way get loaded together.
BTreeFolder2 implements the full Folder interface, with the exception
that the superValues() method does not return any items. To implement
the method in the way the Zope codebase expects would undermine the
performance benefits gained by using BTreeFolder2.
Repairing BTree Damage
======================
Certain ZODB bugs in the past have caused minor corruption in BTrees.
Fortunately, the damage is apparently easy to repair. As of version
1.0, BTreeFolder2 provides a 'manage_cleanup' method that will check
the internal structure of existing BTreeFolder2 instances and repair
them if necessary. Many thanks to Tim Peters, who fixed the BTrees
code and provided a function for checking a BTree.
Visit a BTreeFolder2 instance through the web as a manager. Add
"manage_cleanup" to the end of the URL and request that URL. It may
take some time to load and fix the entire structure. If problems are
detected, information will be added to the event log.
Future
======
BTreeFolder2 will be maintained for Zope 2. Zope 3, however, is not
likely to require BTreeFolder, since the intention is to make Zope 3
folders gracefully expand to support many items.
lib/python/Products/BTreeFolder2/__init__.py
0 → 100644
View file @
058f639b
##############################################################################
#
# 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.0 (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
#
##############################################################################
__doc__
=
'''BTreeFolder2 Product Initialization
$Id: __init__.py,v 1.4 2003/08/21 17:03:52 shane Exp $'''
__version__
=
'$Revision: 1.4 $'
[
11
:
-
2
]
import
BTreeFolder2
def
initialize
(
context
):
context
.
registerClass
(
BTreeFolder2
.
BTreeFolder2
,
constructors
=
(
BTreeFolder2
.
manage_addBTreeFolderForm
,
BTreeFolder2
.
manage_addBTreeFolder
),
icon
=
'btreefolder2.gif'
,
)
#context.registerHelp()
#context.registerHelpTitle('Zope Help')
context
.
registerBaseClass
(
BTreeFolder2
.
BTreeFolder2
)
try
:
from
Products.CMFCore
import
utils
except
ImportError
:
# CMF not installed
pass
else
:
# CMF installed; make available a special folder type.
import
CMFBTreeFolder
ADD_FOLDERS_PERMISSION
=
'Add portal folders'
utils
.
ContentInit
(
'CMF BTree Folder'
,
content_types
=
(
CMFBTreeFolder
.
CMFBTreeFolder
,),
permission
=
ADD_FOLDERS_PERMISSION
,
extra_constructors
=
(
CMFBTreeFolder
.
manage_addCMFBTreeFolder
,),
fti
=
CMFBTreeFolder
.
factory_type_information
).
initialize
(
context
)
lib/python/Products/BTreeFolder2/__init__.pyc
0 → 100644
View file @
058f639b
File added
lib/python/Products/BTreeFolder2/btreefolder2.gif
0 → 100644
View file @
058f639b
179 Bytes
lib/python/Products/BTreeFolder2/contents.dtml
0 → 100644
View file @
058f639b
<dtml-let
form_title=
"'Contents'"
>
<dtml-if
manage_page_header
>
<dtml-var
manage_page_header
>
<dtml-else>
<html><head><title>
&dtml-form_title;
</title></head>
<body
bgcolor=
"#ffffff"
>
</dtml-if>
</dtml-let>
<dtml-var
manage_tabs
>
<script
type=
"text/javascript"
>
<!--
isSelected
=
false
;
function
toggleSelect
()
{
elem
=
document
.
objectItems
.
elements
[
'
ids:list
'
];
if
(
isSelected
==
false
)
{
for
(
i
=
0
;
i
<
elem
.
options
.
length
;
i
++
)
{
elem
.
options
[
i
].
selected
=
true
;
}
isSelected
=
true
;
document
.
objectItems
.
selectButton
.
value
=
"
Deselect All
"
;
return
isSelected
;
}
else
{
for
(
i
=
0
;
i
<
elem
.
options
.
length
;
i
++
)
{
elem
.
options
[
i
].
selected
=
false
;
}
isSelected
=
false
;
document
.
objectItems
.
selectButton
.
value
=
"
Select All
"
;
return
isSelected
;
}
}
//-->
</script>
<dtml-unless
skey
><dtml-call
expr=
"REQUEST.set('skey', 'id')"
></dtml-unless>
<dtml-unless
rkey
><dtml-call
expr=
"REQUEST.set('rkey', '')"
></dtml-unless>
<!-- Add object widget -->
<br
/>
<dtml-if
filtered_meta_types
>
<table
width=
"100%"
cellspacing=
"0"
cellpadding=
"0"
border=
"0"
>
<tr>
<td
align=
"left"
valign=
"top"
>
</td>
<td
align=
"right"
valign=
"top"
>
<div
class=
"form-element"
>
<form
action=
"&dtml-URL1;/"
method=
"get"
>
<dtml-if
"
_
.
len
(
filtered_meta_types
)
>
1">
<select
class=
"form-element"
name=
":action"
onChange=
"location.href='&dtml-URL1;/'+this.options[this.selectedIndex].value"
>
<option
value=
"manage_workspace"
disabled
>
Select type to add...
</option>
<dtml-in
filtered_meta_types
mapping
sort=
name
>
<option
value=
"&dtml.url_quote-action;"
>
&dtml-name;
</option>
</dtml-in>
</select>
<input
class=
"form-element"
type=
"submit"
name=
"submit"
value=
" Add "
/>
<dtml-else>
<dtml-in
filtered_meta_types
mapping
sort=
name
>
<input
type=
"hidden"
name=
":method"
value=
"&dtml.url_quote-action;"
/>
<input
class=
"form-element"
type=
"submit"
name=
"submit"
value=
" Add &dtml-name;"
/>
</dtml-in>
</dtml-if>
</form>
</div>
</td>
</tr>
</table>
</dtml-if>
<form
action=
"&dtml-URL1;/"
name=
"objectItems"
method=
"post"
>
<dtml-if
objectCount
>
<dtml-with
expr=
"getBatchObjectListing(REQUEST)"
mapping
>
<p>
<dtml-if
prev_batch_url
><a
href=
"&dtml-prev_batch_url;"
>
<<
</a></dtml-if>
<em>
Items
<dtml-var
b_start
>
through
<dtml-var
b_end
>
of
<dtml-var
objectCount
></em>
<dtml-if
next_batch_url
><a
href=
"&dtml-next_batch_url;"
>
>>
</a></dtml-if>
</p>
<dtml-var
formatted_list
>
<table
cellspacing=
"0"
cellpadding=
"2"
border=
"0"
>
<tr>
<td
align=
"left"
valign=
"top"
width=
"16"
></td>
<td
align=
"left"
valign=
"top"
>
<div
class=
"form-element"
>
<input
class=
"form-element"
type=
"submit"
name=
"manage_object_workspace:method"
value=
"Edit"
/>
<dtml-unless
dontAllowCopyAndPaste
>
<input
class=
"form-element"
type=
"submit"
name=
"manage_renameForm:method"
value=
"Rename"
/>
<input
class=
"form-element"
type=
"submit"
name=
"manage_cutObjects:method"
value=
"Cut"
/>
<input
class=
"form-element"
type=
"submit"
name=
"manage_copyObjects:method"
value=
"Copy"
/>
<dtml-if
cb_dataValid
>
<input
class=
"form-element"
type=
"submit"
name=
"manage_pasteObjects:method"
value=
"Paste"
/>
</dtml-if>
</dtml-unless>
<dtml-if
"
_
.
SecurityCheckPermission
('
Delete
objects
',
this
())"
>
<input
class=
"form-element"
type=
"submit"
name=
"manage_delObjects:method"
value=
"Delete"
/>
</dtml-if>
<dtml-if
"
_
.
SecurityCheckPermission
('
Import
/
Export
objects
',
this
())"
>
<input
class=
"form-element"
type=
"submit"
name=
"manage_importExportForm:method"
value=
"Import/Export"
/>
</dtml-if>
<script
type=
"text/javascript"
>
<!--
if
(
document
.
forms
[
0
])
{
document
.
write
(
'
<input class="form-element" type="submit" name="selectButton" value="Select All" onClick="toggleSelect(); return false">
'
)
}
//-->
</script>
</div>
</td>
</tr>
</table>
</dtml-with>
<dtml-else>
<table
cellspacing=
"0"
cellpadding=
"2"
border=
"0"
>
<tr>
<td>
<div
class=
"std-text"
>
There are currently no items in
<em>
&dtml-title_or_id;
</em>
<br
/><br
/>
</div>
<dtml-unless
dontAllowCopyAndPaste
>
<dtml-if
cb_dataValid
>
<div
class=
"form-element"
>
<input
class=
"form-element"
type=
"submit"
name=
"manage_pasteObjects:method"
value=
"Paste"
/>
</div>
</dtml-if>
</dtml-unless>
<dtml-if
"
_
.
SecurityCheckPermission
('
Import
/
Export
objects
',
this
())"
>
<input
class=
"form-element"
type=
"submit"
name=
"manage_importExportForm:method"
value=
"Import/Export"
/>
</dtml-if>
</td>
</tr>
</table>
</dtml-if>
</form>
<dtml-if
update_menu
>
<script
type=
"text/javascript"
>
<!--
window
.
parent
.
update_menu
();
//-->
</script>
</dtml-if>
<dtml-if
manage_page_footer
>
<dtml-var
manage_page_footer
>
<dtml-else>
</body></html>
</dtml-if>
lib/python/Products/BTreeFolder2/folderAdd.dtml
0 → 100644
View file @
058f639b
<dtml-let
form_title=
"'Add BTreeFolder2'"
>
<dtml-if
manage_page_header
>
<dtml-var
manage_page_header
>
<dtml-var
manage_form_title
>
<dtml-else>
<html><head><title>
&dtml-form_title;
</title></head>
<body
bgcolor=
"#ffffff"
>
<h2>
&dtml-form_title;
</h2>
</dtml-if>
</dtml-let>
<p
class=
"form-help"
>
A Folder contains other objects. Use Folders to organize your
web objects in to logical groups.
</p>
<p
class=
"form-help"
>
A BTreeFolder2 may be able to handle a larger number
of objects than a standard folder because it does not need to
activate other subobjects in order to access a single subobject.
It is more efficient than the original BTreeFolder product,
but does not provide attribute access.
</p>
<FORM
ACTION=
"manage_addBTreeFolder"
METHOD=
"POST"
>
<table
cellspacing=
"0"
cellpadding=
"2"
border=
"0"
>
<tr>
<td
align=
"left"
valign=
"top"
>
<div
class=
"form-label"
>
Id
</div>
</td>
<td
align=
"left"
valign=
"top"
>
<input
type=
"text"
name=
"id"
size=
"40"
/>
</td>
</tr>
<tr>
<td
align=
"left"
valign=
"top"
>
<div
class=
"form-optional"
>
Title
</div>
</td>
<td
align=
"left"
valign=
"top"
>
<input
type=
"text"
name=
"title"
size=
"40"
/>
</td>
</tr>
<tr>
<td
align=
"left"
valign=
"top"
>
</td>
<td
align=
"left"
valign=
"top"
>
<div
class=
"form-element"
>
<input
class=
"form-element"
type=
"submit"
name=
"submit"
value=
"Add"
/>
</div>
</td>
</tr>
</table>
</form>
<dtml-if
manage_page_footer
>
<dtml-var
manage_page_footer
>
<dtml-else>
</body></html>
</dtml-if>
lib/python/Products/BTreeFolder2/tests/__init__.py
0 → 100644
View file @
058f639b
"""Python package."""
lib/python/Products/BTreeFolder2/tests/__init__.pyc
0 → 100644
View file @
058f639b
File added
lib/python/Products/BTreeFolder2/tests/testBTreeFolder2.py
0 → 100644
View file @
058f639b
##############################################################################
#
# 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.0 (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 tests for BTreeFolder2.
$Id: testBTreeFolder2.py,v 1.8 2004/03/15 20:31:40 shane Exp $
"""
import
unittest
import
ZODB
import
Testing
import
Zope2
from
Products.BTreeFolder2.BTreeFolder2
\
import
BTreeFolder2
,
ExhaustedUniqueIdsError
from
OFS.ObjectManager
import
BadRequestException
from
OFS.Folder
import
Folder
from
Acquisition
import
aq_base
class
BTreeFolder2Tests
(
unittest
.
TestCase
):
def
getBase
(
self
,
ob
):
# This is overridden in subclasses.
return
aq_base
(
ob
)
def
setUp
(
self
):
self
.
f
=
BTreeFolder2
(
'root'
)
ff
=
BTreeFolder2
(
'item'
)
self
.
f
.
_setOb
(
ff
.
id
,
ff
)
self
.
ff
=
self
.
f
.
_getOb
(
ff
.
id
)
def
testAdded
(
self
):
self
.
assertEqual
(
self
.
ff
.
id
,
'item'
)
def
testCount
(
self
):
self
.
assertEqual
(
self
.
f
.
objectCount
(),
1
)
self
.
assertEqual
(
self
.
ff
.
objectCount
(),
0
)
self
.
assertEqual
(
len
(
self
.
f
),
1
)
self
.
assertEqual
(
len
(
self
.
ff
),
0
)
def
testObjectIds
(
self
):
self
.
assertEqual
(
list
(
self
.
f
.
objectIds
()),
[
'item'
])
self
.
assertEqual
(
list
(
self
.
f
.
keys
()),
[
'item'
])
self
.
assertEqual
(
list
(
self
.
ff
.
objectIds
()),
[])
f3
=
BTreeFolder2
(
'item3'
)
self
.
f
.
_setOb
(
f3
.
id
,
f3
)
lst
=
list
(
self
.
f
.
objectIds
())
lst
.
sort
()
self
.
assertEqual
(
lst
,
[
'item'
,
'item3'
])
def
testObjectIdsWithMetaType
(
self
):
f2
=
Folder
()
f2
.
id
=
'subfolder'
self
.
f
.
_setOb
(
f2
.
id
,
f2
)
mt1
=
self
.
ff
.
meta_type
mt2
=
Folder
.
meta_type
self
.
assertEqual
(
list
(
self
.
f
.
objectIds
(
mt1
)),
[
'item'
])
self
.
assertEqual
(
list
(
self
.
f
.
objectIds
((
mt1
,))),
[
'item'
])
self
.
assertEqual
(
list
(
self
.
f
.
objectIds
(
mt2
)),
[
'subfolder'
])
lst
=
list
(
self
.
f
.
objectIds
([
mt1
,
mt2
]))
lst
.
sort
()
self
.
assertEqual
(
lst
,
[
'item'
,
'subfolder'
])
self
.
assertEqual
(
list
(
self
.
f
.
objectIds
(
'blah'
)),
[])
def
testObjectValues
(
self
):
values
=
self
.
f
.
objectValues
()
self
.
assertEqual
(
len
(
values
),
1
)
self
.
assertEqual
(
values
[
0
].
id
,
'item'
)
# Make sure the object is wrapped.
self
.
assert_
(
values
[
0
]
is
not
self
.
getBase
(
values
[
0
]))
def
testObjectItems
(
self
):
items
=
self
.
f
.
objectItems
()
self
.
assertEqual
(
len
(
items
),
1
)
id
,
val
=
items
[
0
]
self
.
assertEqual
(
id
,
'item'
)
self
.
assertEqual
(
val
.
id
,
'item'
)
# Make sure the object is wrapped.
self
.
assert_
(
val
is
not
self
.
getBase
(
val
))
def
testHasKey
(
self
):
self
.
assert_
(
self
.
f
.
hasObject
(
'item'
))
# Old spelling
self
.
assert_
(
self
.
f
.
has_key
(
'item'
))
# New spelling
def
testDelete
(
self
):
self
.
f
.
_delOb
(
'item'
)
self
.
assertEqual
(
list
(
self
.
f
.
objectIds
()),
[])
self
.
assertEqual
(
self
.
f
.
objectCount
(),
0
)
def
testObjectMap
(
self
):
map
=
self
.
f
.
objectMap
()
self
.
assertEqual
(
list
(
map
),
[{
'id'
:
'item'
,
'meta_type'
:
self
.
ff
.
meta_type
}])
# I'm not sure why objectMap_d() exists, since it appears to be
# the same as objectMap(), but it's implemented by Folder.
self
.
assertEqual
(
list
(
self
.
f
.
objectMap_d
()),
list
(
self
.
f
.
objectMap
()))
def
testObjectIds_d
(
self
):
self
.
assertEqual
(
self
.
f
.
objectIds_d
(),
{
'item'
:
1
})
def
testCheckId
(
self
):
self
.
assertEqual
(
self
.
f
.
_checkId
(
'xyz'
),
None
)
self
.
assertRaises
(
BadRequestException
,
self
.
f
.
_checkId
,
'item'
)
self
.
assertRaises
(
BadRequestException
,
self
.
f
.
_checkId
,
'REQUEST'
)
def
testSetObject
(
self
):
f2
=
BTreeFolder2
(
'item2'
)
self
.
f
.
_setObject
(
f2
.
id
,
f2
)
self
.
assert_
(
self
.
f
.
has_key
(
'item2'
))
self
.
assertEqual
(
self
.
f
.
objectCount
(),
2
)
def
testWrapped
(
self
):
# Verify that the folder returns wrapped versions of objects.
base
=
self
.
getBase
(
self
.
f
.
_getOb
(
'item'
))
self
.
assert_
(
self
.
f
.
_getOb
(
'item'
)
is
not
base
)
self
.
assert_
(
self
.
f
[
'item'
]
is
not
base
)
self
.
assert_
(
self
.
f
.
get
(
'item'
)
is
not
base
)
self
.
assert_
(
self
.
getBase
(
self
.
f
.
_getOb
(
'item'
))
is
base
)
def
testGenerateId
(
self
):
ids
=
{}
for
n
in
range
(
10
):
ids
[
self
.
f
.
generateId
()]
=
1
self
.
assertEqual
(
len
(
ids
),
10
)
# All unique
for
id
in
ids
.
keys
():
self
.
f
.
_checkId
(
id
)
# Must all be valid
def
testGenerateIdDenialOfServicePrevention
(
self
):
for
n
in
range
(
10
):
item
=
Folder
()
item
.
id
=
'item%d'
%
n
self
.
f
.
_setOb
(
item
.
id
,
item
)
self
.
f
.
generateId
(
'item'
,
rand_ceiling
=
20
)
# Shouldn't be a problem
self
.
assertRaises
(
ExhaustedUniqueIdsError
,
self
.
f
.
generateId
,
'item'
,
rand_ceiling
=
9
)
def
testReplace
(
self
):
old_f
=
Folder
()
old_f
.
id
=
'item'
inner_f
=
BTreeFolder2
(
'inner'
)
old_f
.
_setObject
(
inner_f
.
id
,
inner_f
)
self
.
ff
.
_populateFromFolder
(
old_f
)
self
.
assertEqual
(
self
.
ff
.
objectCount
(),
1
)
self
.
assert_
(
self
.
ff
.
has_key
(
'inner'
))
self
.
assertEqual
(
self
.
getBase
(
self
.
ff
.
_getOb
(
'inner'
)),
inner_f
)
def
testObjectListing
(
self
):
f2
=
BTreeFolder2
(
'somefolder'
)
self
.
f
.
_setObject
(
f2
.
id
,
f2
)
# Hack in an absolute_url() method that works without context.
self
.
f
.
absolute_url
=
lambda
:
''
info
=
self
.
f
.
getBatchObjectListing
()
self
.
assertEqual
(
info
[
'b_start'
],
1
)
self
.
assertEqual
(
info
[
'b_end'
],
2
)
self
.
assertEqual
(
info
[
'prev_batch_url'
],
''
)
self
.
assertEqual
(
info
[
'next_batch_url'
],
''
)
self
.
assert_
(
info
[
'formatted_list'
].
find
(
'</select>'
)
>
0
)
self
.
assert_
(
info
[
'formatted_list'
].
find
(
'item'
)
>
0
)
self
.
assert_
(
info
[
'formatted_list'
].
find
(
'somefolder'
)
>
0
)
# Ensure batching is working.
info
=
self
.
f
.
getBatchObjectListing
({
'b_count'
:
1
})
self
.
assertEqual
(
info
[
'b_start'
],
1
)
self
.
assertEqual
(
info
[
'b_end'
],
1
)
self
.
assertEqual
(
info
[
'prev_batch_url'
],
''
)
self
.
assert_
(
info
[
'next_batch_url'
]
!=
''
)
self
.
assert_
(
info
[
'formatted_list'
].
find
(
'item'
)
>
0
)
self
.
assert_
(
info
[
'formatted_list'
].
find
(
'somefolder'
)
<
0
)
info
=
self
.
f
.
getBatchObjectListing
({
'b_start'
:
2
})
self
.
assertEqual
(
info
[
'b_start'
],
2
)
self
.
assertEqual
(
info
[
'b_end'
],
2
)
self
.
assert_
(
info
[
'prev_batch_url'
]
!=
''
)
self
.
assertEqual
(
info
[
'next_batch_url'
],
''
)
self
.
assert_
(
info
[
'formatted_list'
].
find
(
'item'
)
<
0
)
self
.
assert_
(
info
[
'formatted_list'
].
find
(
'somefolder'
)
>
0
)
def
testObjectListingWithSpaces
(
self
):
# The option list must use value attributes to preserve spaces.
name
=
" some folder "
f2
=
BTreeFolder2
(
name
)
self
.
f
.
_setObject
(
f2
.
id
,
f2
)
self
.
f
.
absolute_url
=
lambda
:
''
info
=
self
.
f
.
getBatchObjectListing
()
expect
=
'<option value="%s">%s</option>'
%
(
name
,
name
)
self
.
assert_
(
info
[
'formatted_list'
].
find
(
expect
)
>
0
)
def
testCleanup
(
self
):
self
.
assert_
(
self
.
f
.
_cleanup
())
key
=
TrojanKey
(
'a'
)
self
.
f
.
_tree
[
key
]
=
'b'
self
.
assert_
(
self
.
f
.
_cleanup
())
key
.
value
=
'z'
# With a key in the wrong place, there should now be damage.
self
.
assert_
(
not
self
.
f
.
_cleanup
())
# Now it's fixed.
self
.
assert_
(
self
.
f
.
_cleanup
())
# Verify the management interface also works,
# but don't test return values.
self
.
f
.
manage_cleanup
()
key
.
value
=
'a'
self
.
f
.
manage_cleanup
()
class
TrojanKey
:
"""Pretends to be a consistent, immutable, humble citizen...
then sweeps the rug out from under the BTree.
"""
def
__init__
(
self
,
value
):
self
.
value
=
value
def
__cmp__
(
self
,
other
):
return
cmp
(
self
.
value
,
other
)
def
__hash__
(
self
):
return
hash
(
self
.
value
)
def
test_suite
():
return
unittest
.
TestSuite
((
unittest
.
makeSuite
(
BTreeFolder2Tests
),
))
if
__name__
==
'__main__'
:
unittest
.
main
(
defaultTest
=
'test_suite'
)
lib/python/Products/BTreeFolder2/tests/testBTreeFolder2.pyc
0 → 100644
View file @
058f639b
File added
lib/python/Products/BTreeFolder2/version.txt
0 → 100644
View file @
058f639b
BTreeFolder2-1.0.1
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment