Commit 9b7f05cb authored by Aurel's avatar Aurel

initial import of HBTreeFolder product


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@16118 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 914dad8e
Version 1.0.0
- Initial version
##############################################################################
#
# 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
#
##############################################################################
from AccessControl.SecurityInfo import ClassSecurityInfo
from Globals import InitializeClass
from Products.HBTreeFolder2.HBTreeFolder2 import HBTreeFolder2Base
try:
from Products.CMFCore.PortalFolder import PortalFolderBase as PortalFolder
except ImportError:
from Products.CMFCore.PortalFolder import PortalFolder
from Products.CMFCore.PortalFolder import factory_type_information as PortalFolder_FTI
from Products.CMFCore.utils import getToolByName
_actions = PortalFolder_FTI[0]['actions']
factory_type_information = ( { 'id' : 'CMF HBTree Folder',
'meta_type' : 'CMF HBTree Folder',
'description' : """\
CMF folder designed to hold a lot of objects.""",
'icon' : 'folder_icon.gif',
'product' : 'CMFCore',
'factory' : 'manage_addCMFHBTreeFolder',
'filter_content_types' : 0,
'immediate_view' : 'folder_edit_form',
'actions' : _actions,
},
)
def manage_addCMFHBTreeFolder(dispatcher, id, title='', REQUEST=None):
"""Adds a new HBTreeFolder object with id *id*.
"""
id = str(id)
ob = CMFHBTreeFolder(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 CMFHBTreeFolder(HBTreeFolder2Base, PortalFolder):
"""HBTree folder for CMF sites.
"""
meta_type = 'CMF HBTree Folder'
security = ClassSecurityInfo()
def __init__(self, id, title=''):
PortalFolder.__init__(self, id, title)
HBTreeFolder2Base.__init__(self, id)
def _checkId(self, id, allow_dup=0):
PortalFolder._checkId(self, id, allow_dup)
HBTreeFolder2Base._checkId(self, id, allow_dup)
def allowedContentTypes(self):
"""
List type info objects for types which can be added in
this folder.
"""
result = []
portal_types = getToolByName(self, 'portal_types')
myType = portal_types.getTypeInfo(self)
if myType is not None:
allowed_types_to_check = []
if myType.filter_content_types:
for portal_type in myType.allowed_content_types:
contentType = portal_types.getTypeInfo(portal_type)
if contentType is None:
raise AttributeError, "Portal type '%s' does not exist " \
"and should not be allowed in '%s'" % \
(portal_type, self.getPortalType())
result.append(contentType)
else:
for contentType in portal_types.listTypeInfo(self):
if myType.allowType(contentType.getId()):
result.append(contentType)
else:
result = portal_types.listTypeInfo()
return filter(
lambda typ, container=self: typ.isConstructionAllowed(container),
result)
InitializeClass(CMFHBTreeFolder)
This diff is collapsed.
##############################################################################
#
# 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
#
##############################################################################
import HBTreeFolder2
def initialize(context):
context.registerClass(
HBTreeFolder2.HBTreeFolder2,
constructors=(HBTreeFolder2.manage_addHBTreeFolder2Form,
HBTreeFolder2.manage_addHBTreeFolder2),
icon='btreefolder2.gif',
)
#context.registerHelp()
#context.registerHelpTitle('Zope Help')
context.registerBaseClass(HBTreeFolder2.HBTreeFolder2)
try:
from Products.CMFCore import utils
except ImportError:
# CMF not installed
pass
else:
# CMF installed; make available a special folder type.
import CMFHBTreeFolder
ADD_FOLDERS_PERMISSION = 'Add portal folders'
utils.ContentInit(
'CMF HBTree Folder',
content_types=(CMFHBTreeFolder.CMFHBTreeFolder,),
permission=ADD_FOLDERS_PERMISSION,
extra_constructors=(CMFHBTreeFolder.manage_addCMFHBTreeFolder,),
fti=CMFHBTreeFolder.factory_type_information
).initialize(context)
<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">&nbsp;</td>
<td align="right" valign="top">
<div class="form-element">
<form action="&dtml-URL1;/" method="get">
<dtml-if "_.len(filtered_meta_types) > 1">
<select class="form-element" name=":action"
onChange="location.href='&dtml-URL1;/'+this.options[this.selectedIndex].value">
<option value="manage_workspace" disabled>Select type to add...</option>
<dtml-in filtered_meta_types mapping sort=name>
<option value="&dtml.url_quote-action;">&dtml-name;</option>
</dtml-in>
</select>
<input class="form-element" type="submit" name="submit" value=" Add " />
<dtml-else>
<dtml-in filtered_meta_types mapping sort=name>
<input type="hidden" name=":method" value="&dtml.url_quote-action;" />
<input class="form-element" type="submit" name="submit" value=" Add &dtml-name;" />
</dtml-in>
</dtml-if>
</form>
</div>
</td>
</tr>
</table>
</dtml-if>
<form action="&dtml-URL1;/" name="objectItems" method="post">
<dtml-if objectCount>
<dtml-with expr="getBatchObjectListing(REQUEST)" mapping>
<p>
<dtml-if prev_batch_url><a href="&dtml-prev_batch_url;">&lt;&lt;</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;">&gt;&gt;</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>
<dtml-let form_title="'Add HBTreeFolder2'">
<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 HBTreeFolder2 may be able to handle a larger number
of objects than a standard BTreeFolder because it use btree of
btree to store objects.
It is more efficient than the original BTreeFolder2 product,
but you must provide well constructed id according to hashId method
</p>
<FORM ACTION="manage_addHBTreeFolder2" 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>
##############################################################################
#
# 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
#
##############################################################################
import unittest
import ZODB
import Testing
import Zope
from Products.HBTreeFolder2.HBTreeFolder2 \
import HBTreeFolder2, ExhaustedUniqueIdsError
from OFS.ObjectManager import BadRequestException
from OFS.Folder import Folder
from Acquisition import aq_base
class HBTreeFolder2Tests(unittest.TestCase):
def getBase(self, ob):
# This is overridden in subclasses.
return aq_base(ob)
def setUp(self):
self.f = HBTreeFolder2('root')
ff = HBTreeFolder2('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 = HBTreeFolder2('item3')
self.f._setOb(f3.id, f3)
lst = list(self.f.objectIds())
lst.sort()
self.assertEqual(lst, ['item', 'item3'])
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 = HBTreeFolder2('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 = HBTreeFolder2('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 = HBTreeFolder2('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 = HBTreeFolder2(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._htree[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 HBTree.
"""
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(HBTreeFolder2Tests),
))
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
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