From: Julian I. <svn...@pl...> - 2012-05-10 07:11:09
|
Author: jinfanger Date: Thu May 10 07:10:55 2012 New Revision: 249875 Added: simplelayout/simplelayout.base/branches/jin_indexed_blocks/bootstrap.py simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/indexer.py simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/testing.py simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/tests/__init__.py simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/tests/delete_blocks.txt simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/tests/test_doctest.py simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/tests/test_seachabletext.py simplelayout/simplelayout.base/branches/jin_indexed_blocks/test-plone-4.1.x.cfg Modified: simplelayout/simplelayout.base/branches/jin_indexed_blocks/ (props changed) simplelayout/simplelayout.base/branches/jin_indexed_blocks/IGNORE.txt simplelayout/simplelayout.base/branches/jin_indexed_blocks/docs/HISTORY.txt simplelayout/simplelayout.base/branches/jin_indexed_blocks/setup.py simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/configure.zcml simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/events.py Log: Created indexer which adds the block SearchableText to the pages SearchableText. Also added eventhandlers to make sure the index is up-to-date. Modified: simplelayout/simplelayout.base/branches/jin_indexed_blocks/IGNORE.txt ============================================================================== --- simplelayout/simplelayout.base/branches/jin_indexed_blocks/IGNORE.txt (original) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/IGNORE.txt Thu May 10 07:10:55 2012 @@ -3,3 +3,9 @@ simplelayout.base.egg-info build dist +bin +parts +src +develop-eggs +buildout.cfg +.installed.cfg Added: simplelayout/simplelayout.base/branches/jin_indexed_blocks/bootstrap.py ============================================================================== --- (empty file) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/bootstrap.py Thu May 10 07:10:55 2012 @@ -0,0 +1,55 @@ +############################################################################## +# +# Copyright (c) 2006 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. +# +############################################################################## +"""Bootstrap a buildout-based project + +Simply run this script in a directory containing a buildout.cfg. +The script accepts buildout command-line options, so you can +use the -c option to specify an alternate configuration file. + +$Id$ +""" + +import os, shutil, sys, tempfile, urllib2 + +tmpeggs = tempfile.mkdtemp() + +try: + import pkg_resources +except ImportError: + ez = {} + exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' + ).read() in ez + ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) + + import pkg_resources + +cmd = 'from setuptools.command.easy_install import main; main()' +if sys.platform == 'win32': + cmd = '"%s"' % cmd # work around spawn lamosity on windows + +ws = pkg_resources.working_set +assert os.spawnle( + os.P_WAIT, sys.executable, sys.executable, + '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout', + dict(os.environ, + PYTHONPATH= + ws.find(pkg_resources.Requirement.parse('setuptools')).location + ), + ) == 0 + +ws.add_entry(tmpeggs) +ws.require('zc.buildout') +import zc.buildout.buildout +zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap']) +shutil.rmtree(tmpeggs) Modified: simplelayout/simplelayout.base/branches/jin_indexed_blocks/docs/HISTORY.txt ============================================================================== --- simplelayout/simplelayout.base/branches/jin_indexed_blocks/docs/HISTORY.txt (original) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/docs/HISTORY.txt Thu May 10 07:10:55 2012 @@ -5,7 +5,10 @@ 3.0b7 (unreleased) ------------------ -- Nothing changed yet. +- Created indexer which adds the block SearchableText to the pages + SearchableText. Also added eventhandlers to make sure the index is + up-to-date. + [Julian Infanger] 3.0b6 (2012-05-09) Modified: simplelayout/simplelayout.base/branches/jin_indexed_blocks/setup.py ============================================================================== --- simplelayout/simplelayout.base/branches/jin_indexed_blocks/setup.py (original) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/setup.py Thu May 10 07:10:55 2012 @@ -3,6 +3,11 @@ version = open('simplelayout/base/version.txt').read().strip() +tests_require = [ + 'plone.app.testing', + 'plone.mocktestcase', +] + setup(name='simplelayout.base', version=version, description="SimpleLayout is an easy to use plone package for creating content pages", @@ -30,6 +35,8 @@ 'simplelayout.ui.base', 'simplelayout.ui.dragndrop' ], + tests_require=tests_require, + extras_require=dict(tests=tests_require), entry_points=""" # -*- Entry points: -*- """, Modified: simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/configure.zcml ============================================================================== --- simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/configure.zcml (original) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/configure.zcml Thu May 10 07:10:55 2012 @@ -13,6 +13,40 @@ <include package=".configlet" /> + <adapter factory=".indexer.SearchableText" name="SearchableText" /> + + <!-- events for reindexing --> + <subscriber + for="simplelayout.base.interfaces.ISimpleLayoutBlock + zope.app.container.interfaces.IObjectRemovedEvent" + handler=".events.reindexContainer" + /> + + <subscriber + for="simplelayout.base.interfaces.ISimpleLayoutBlock + Products.Archetypes.interfaces.IObjectInitializedEvent" + handler=".events.reindexContainer" + /> + + <subscriber + for="simplelayout.base.interfaces.ISimpleLayoutBlock + Products.Archetypes.interfaces.IObjectEditedEvent" + handler=".events.reindexContainer" + /> + + <subscriber + for="simplelayout.base.interfaces.ISimpleLayoutBlock + zope.app.container.interfaces.IObjectAddedEvent" + handler=".events.reindexContainer" + /> + + <subscriber + for="simplelayout.base.interfaces.ISimpleLayoutBlock + zope.app.container.interfaces.IObjectMovedEvent" + handler=".events.blockMoved" + /> + + <!-- Register the installation GenericSetup extension profile --> <genericsetup:registerProfile name="default" Modified: simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/events.py ============================================================================== --- simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/events.py (original) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/events.py Thu May 10 07:10:55 2012 @@ -1,28 +1,32 @@ -from zope.component import getUtility, getMultiAdapter +from Acquisition import aq_parent, aq_inner +from DateTime import DateTime from Products.CMFCore.utils import getToolByName -from simplelayout.base.utils import IBlockControl -from simplelayout.base.interfaces import IScaleImage, IBlockConfig, ISlUtils -from simplelayout.base.interfaces import ISimplelayoutView, ISimpleLayoutCapable from simplelayout.base.config import INIT_INTERFACES_MAP,VIEW_INTERFACES_MAP -from zope.interface import providedBy, alsoProvides -from DateTime import DateTime +from simplelayout.base.interfaces import IBlockConfig, ISlUtils +from simplelayout.base.interfaces import ISimplelayoutView, ISimpleLayoutCapable +from simplelayout.base.utils import IBlockControl +from simplelayout.types.common.interfaces import IPage +from zope.component import getUtility +from zope.component.interfaces import ComponentLookupError +from zope.interface import alsoProvides + def isWorkflowEnabled(): conf = getUtility(ISlUtils, name='simplelayout.utils') - return conf.isBlockWorkflowEnabled() + return conf.isBlockWorkflowEnabled() def set_initial_layout(object, event): content = event.object parent = content.aq_parent - + if not ISimpleLayoutCapable.providedBy(parent): return - + blockconf = IBlockConfig(content) types_tool = getToolByName(content, 'portal_types') - actions = types_tool.listActions(object=content) + actions = types_tool.listActions(object=content) category = 'sl-layouts' #we use the the first layout as default value layout = blockconf.image_layout @@ -38,13 +42,13 @@ if layout: converter = getUtility(IBlockControl, name='block-layout') converter.update(content, content, content.REQUEST, layout=layout, viewname=viewname) - + def changeBlockStates(obj, event): """ """ if not ISimpleLayoutCapable.providedBy(obj): return - + if not isWorkflowEnabled(): pm = getToolByName(obj, 'portal_membership') current_user = pm.getAuthenticatedMember().getId() @@ -55,7 +59,7 @@ comment = 'state set to: %s' % container_status for item in obj.getFolderContents(cf, full_objects=True): wt.setStatusOf(wf_id, item, {'review_state': container_status, - 'action' : container_status, + 'action' : container_status, 'actor': current_user, 'time': DateTime(), 'comments': comment,}) @@ -70,7 +74,7 @@ parent = obj.aq_parent if not ISimpleLayoutCapable.providedBy(parent): return - + if not isWorkflowEnabled(): pm = getToolByName(obj, 'portal_membership') current_user = pm.getAuthenticatedMember().getId() @@ -79,40 +83,52 @@ container_status = wt.getInfoFor(obj.aq_inner.aq_parent, 'review_state') comment = 'state set to: %s' % container_status wt.setStatusOf(wf_id, obj, {'review_state': container_status, - 'action' : container_status, + 'action' : container_status, 'actor': current_user, 'time': DateTime(), 'comments': comment,}) wf = wt.getWorkflowById(wf_id) wf.updateRoleMappingsFor(obj) - obj.reindexObject(idxs=['allowRolesAndUsers', 'review_state']) + obj.reindexObject(idxs=['allowRolesAndUsers', 'review_state']) def setDefaultDesignInterface(obj,event): if not ISimplelayoutView.providedBy(obj): alsoProvides(obj, ISimplelayoutView) - + def setDefaultBlockInterfaces(obj,event): parent = obj.aq_parent parent_iface = None for i in VIEW_INTERFACES_MAP.values(): if i.providedBy(parent): parent_iface = i - + if parent_iface is None: - return - name = parent_iface['name'].__name__ + return + name = parent_iface['name'].__name__ ifaces = [] if INIT_INTERFACES_MAP.has_key(name): ifaces = INIT_INTERFACES_MAP[name] for iface in ifaces: alsoProvides(obj, iface) obj.reindexObject(idxs=['object_provides']) - + #XXX this should be done earlier, we do now twice... once is enought #calc new images sizes and store them try: converter = getUtility(IBlockControl, name='block-layout') except ComponentLookupError: pass - converter.update(parent, obj, obj.REQUEST) + converter.update(parent, obj, obj.REQUEST) + + +def reindexContainer(obj, event, parent=None): + if not isWorkflowEnabled(): + if not parent: + parent = aq_parent(aq_inner(obj)) + if IPage.providedBy(parent): + parent.reindexObject() + +def blockMoved(obj, event): + reindexContainer(obj, event, parent=event.oldParent) + reindexContainer(obj, event, parent=event.newParent) Added: simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/indexer.py ============================================================================== --- (empty file) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/indexer.py Thu May 10 07:10:55 2012 @@ -0,0 +1,22 @@ +from simplelayout.base.config import BLOCK_INTERFACES +from zope.component import getUtility +from simplelayout.base.interfaces import ISlUtils +from plone.indexer import indexer +from simplelayout.types.common.interfaces import IPage + + +@indexer(IPage) +def SearchableText(obj): + searchable_text = obj.SearchableText() + # only index sub-blocks if blockworkflow is not enabled + conf = getUtility(ISlUtils, name='simplelayout.utils') + if not conf.isBlockWorkflowEnabled(): + contents = obj.getFolderContents( + {'object_provides':BLOCK_INTERFACES, + 'sort_order':'getObjPositionInParent'}, + full_objects=True) + for content in contents: + searchable_text += content.SearchableText() + if isinstance(searchable_text, unicode): + searchable_text = searchable_text.encode('utf8') + return searchable_text Added: simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/testing.py ============================================================================== --- (empty file) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/testing.py Thu May 10 07:10:55 2012 @@ -0,0 +1,33 @@ +from plone.app.testing import PloneSandboxLayer +from plone.app.testing import applyProfile +from plone.app.testing import PLONE_FIXTURE +from plone.app.testing import IntegrationTesting +from plone.app.testing import FunctionalTesting +from zope.configuration import xmlconfig +from plone.testing.z2 import installProduct + + +class SimplelayoutBaseLayer(PloneSandboxLayer): + + defaultBases = (PLONE_FIXTURE, ) + + def setUpZope(self, app, configurationContext): + # Load ZCML + import simplelayout.base + xmlconfig.file( + 'configure.zcml', simplelayout.base, context=configurationContext) + installProduct(app, 'simplelayout.base') + import simplelayout.types.common + xmlconfig.file( + 'configure.zcml', simplelayout.types.common, context=configurationContext) + installProduct(app, 'simplelayout.types.common') + + def setUpPloneSite(self, portal): + # Install into Plone site using portal_setup + applyProfile(portal, 'simplelayout.base:default') + +SL_BASE_FIXTURE = SimplelayoutBaseLayer() +SL_BASE_INTEGRATION_TESTING = IntegrationTesting( + bases=(SL_BASE_FIXTURE, ), name="ftw.file:Integration") +SL_BASE_FUNCTIONAL_TESTING = FunctionalTesting( + bases=(SL_BASE_FIXTURE, ), name="ftw.file:Functional") Added: simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/tests/__init__.py ============================================================================== Added: simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/tests/delete_blocks.txt ============================================================================== --- (empty file) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/tests/delete_blocks.txt Thu May 10 07:10:55 2012 @@ -0,0 +1,59 @@ +Set manager role for test user + + >>> from plone.app.testing import TEST_USER_ID, TEST_USER_PASSWORD + >>> from plone.app.testing import TEST_USER_NAME + >>> from plone.app.testing import setRoles + >>> from plone.testing.z2 import Browser + >>> import transaction + + >>> portal = layer['portal'] + >>> portal_url = portal.absolute_url() + >>> setRoles(portal, TEST_USER_ID, ['Manager',]) + >>> transaction.commit() + +Make sure that the blocks dont have their own workflow + >>> from zope.component import getUtility + >>> from simplelayout.base.interfaces import ISlUtils + >>> conf = getUtility(ISlUtils, name='simplelayout.utils') + >>> setattr(conf, 'isBlockWorkflowEnabled', lambda:False) + >>> transaction.commit() + +Create some content + >>> portal.invokeFactory('Page', 'p1') + 'p1' + >>> transaction.commit() + +Define browser client + >>> browser = Browser(layer['app']) + +Login + >>> browser.open(portal_url + '/login_form') + >>> browser.getControl(name='__ac_name').value = TEST_USER_NAME + >>> browser.getControl(name='__ac_password').value = TEST_USER_PASSWORD + >>> browser.getControl(name='submit').click() + +Create a new block and search for the page + >>> browser.open(portal.p1.absolute_url() + '/createObject?type_name=Paragraph') + >>> browser.getControl(name='title').value = 'block1' + >>> browser.getControl(name='text').value = 'loremipsum' + >>> browser.getControl(name='form.button.save').click() + + >>> len(portal.portal_catalog(SearchableText='loremipsum', portal_type='Page')) + 1 + +There should be 2 items in the catalog + >>> len(portal.portal_catalog()) + 2 + +Delete block1 + >>> block = portal.p1.objectValues()[0] + >>> browser.open(block.absolute_url() + '/delete_confirmation') + >>> browser.getControl('Delete').click() + +Search for block1 (do NOT find any object) + >>> len(portal.portal_catalog(SearchableText='loremipsum')) + 0 + +There should be only one item in catalog + >>> len(portal.portal_catalog()) + 1 Added: simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/tests/test_doctest.py ============================================================================== --- (empty file) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/tests/test_doctest.py Thu May 10 07:10:55 2012 @@ -0,0 +1,19 @@ +import unittest2 as unittest +import doctest +from plone.testing import layered +from simplelayout.base.testing import SL_BASE_INTEGRATION_TESTING + + +DOCTEST_FILES = [ + 'delete_blocks.txt', + ] + + +def test_suite(): + suite = unittest.TestSuite() + for doctest_file in DOCTEST_FILES: + suite.addTests([ + layered(doctest.DocFileSuite(doctest_file), + layer=SL_BASE_INTEGRATION_TESTING), + ]) + return suite Added: simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/tests/test_seachabletext.py ============================================================================== --- (empty file) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/simplelayout/base/tests/test_seachabletext.py Thu May 10 07:10:55 2012 @@ -0,0 +1,85 @@ +import transaction +from plone.app.testing import setRoles +from zope.component import getUtility +from simplelayout.base.interfaces import ISlUtils +from plone.app.testing import TEST_USER_ID +from Products.Archetypes.event import ObjectEditedEvent +from Products.CMFCore.utils import getToolByName +from simplelayout.base.indexer import SearchableText +from simplelayout.base.testing import SL_BASE_FUNCTIONAL_TESTING +from unittest2 import TestCase +from zope import event + + +class TestSearchableText(TestCase): + + layer = SL_BASE_FUNCTIONAL_TESTING + + def setUp(self): + self.portal = self.layer['portal'] + setRoles(self.portal, TEST_USER_ID, ['Manager']) + self.portal.invokeFactory('Page', 'p1') + self.p1 = self.portal.p1 + self.p1.invokeFactory('Paragraph', 'block1') + self.catalog = getToolByName(self.portal, 'portal_catalog') + # blocks should not have a workflow + conf = getUtility(ISlUtils, name='simplelayout.utils') + setattr(conf, 'isBlockWorkflowEnabled', lambda:False) + + + def search_for(self, term, path=None): + query = {'SearchableText':term, 'portal_type':'Page'} + if path: + query['path'] = path + return len(self.catalog(query)) + + def test_blocks(self): + self.assertEqual(SearchableText(self.p1)(), 'p1 block1 ') + self.p1.invokeFactory('Paragraph', 'another') + self.assertEqual(SearchableText(self.p1)(), 'p1 block1 another ') + + def test_block_deleted(self): + # search for block + self.assertTrue(self.search_for('block1') == 1) + # delete object (manage_delObject fires event) + self.p1.manage_delObjects(['block1']) + self.assertTrue(self.search_for('block1') == 0) + + def test_block_edited(self): + self.p1.block1.edit(description='lorem') + event.notify(ObjectEditedEvent(self.p1.block1)) + self.assertTrue(self.search_for('lorem') == 1) + + def test_cut_paste(self): + self.portal.invokeFactory('Page', 'p2') + p2 = self.portal.p2 + p1_path = '/'.join(self.p1.getPhysicalPath()) + p2_path = '/'.join(p2.getPhysicalPath()) + + self.assertTrue(self.search_for('block1', path=p2_path) == 0) + self.assertTrue(self.search_for('block1', path=p1_path) == 1) + + #cut and paste + transaction.commit() + cookie = self.p1.manage_cutObjects(['block1']) + transaction.commit() + p2.manage_pasteObjects(cookie) + transaction.commit() + + self.assertTrue(self.search_for('block1', path=p2_path) == 1) + self.assertTrue(self.search_for('block1', path=p1_path) == 0) + + def test_umlauts(self): + self.p1.block1.setDescription('utf8 \xc3\xa4') + self.p1.invokeFactory('Paragraph', 'block2') + self.p1.block2.setDescription(u'unicode \xfc') + self.p1.reindexObject() + self.assertTrue(self.search_for('\xc3\xa4') == 1) + self.assertTrue(self.search_for('\xc3\xbc') == 1) + + def test_same_workflow(self): + self.assertEqual(SearchableText(self.p1)(), 'p1 block1 ') + conf = getUtility(ISlUtils, name='simplelayout.utils') + setattr(conf, 'isBlockWorkflowEnabled', lambda:True) + self.p1.reindexObject() + self.assertEqual(SearchableText(self.p1)(), 'p1 ') Added: simplelayout/simplelayout.base/branches/jin_indexed_blocks/test-plone-4.1.x.cfg ============================================================================== --- (empty file) +++ simplelayout/simplelayout.base/branches/jin_indexed_blocks/test-plone-4.1.x.cfg Thu May 10 07:10:55 2012 @@ -0,0 +1,5 @@ +[buildout] +extends = + https://raw.github.com/4teamwork/ftw-buildouts/master/test-plone-4.1.x.cfg + +package-name = simplelayout.base |