跳转到主要内容

在Plone站点中透明镜像Plone内容

项目描述

作者

seletz

日期
2008-01-17
修订版
57037

摘要

此软件包提供了一种方法,可以将Plone内容实例透明地镜像到一个或多个位置。为此,我们基本上需要

  • 在遍历中定位要镜像的内容

  • 将定位的对象插入到遍历上下文的获取链中

为此,我们将实现Zope 3遍历机制的适配器,在此适配器中查找要镜像的内容,并将对象插入到适配上下文的获取链中。

注意事项,限制

由于我们以这种方式添加了镜像对象,Plone最终会多次重新索引对象(例如,如果在一个镜像路径上编辑对象)。这有以下后果

搜索 – 在搜索中,对象被列出两次,因为它

在目录中两次。在我看来,这没关系,因为这正是我们想要的,我们希望对象出现在两个地方。

编辑 – KSS编辑和普通编辑的镜像内容按预期工作。在

编辑时,内容将被重新索引。查看同一对象(在另一路径上可见)的用户将在页面重新加载时看到更改。这里没有什么不同寻常的。

删除、重命名 – 这里变得复杂。因为对象在

两次,类似 folder_listing 这样的功能会在对象不再存在的情况下在镜像路径上显示对象。我们做的只是镜像,而不是复制。我将尝试使用事件订阅者来处理这种情况。

UID目录 – 由于对象可能被编目多次,UID

目录将包含UID多次。此外,UID目录中额外条目的目录大脑在执行getObject()时将返回None。我对这一点的确切含义尚不清楚,这很可能是UID目录中的一个错误(为什么它最初要插入UID两次?)

基本设置

此doctest所需的一些导入

>>> from zope import interface
>>> from zope import component
>>> from zope.app.testing import ztapi
>>> from zope.publisher.browser import TestRequest

定位内容

为了定位内容,我们提供了一个接口

>>> from inquant.contentmirror.base.interfaces import IMirrorContentLocator

现在我们可以定义一个适配器,该适配器能够从其他地方定位内容

>>> class TestLocator(object):
...     def __init__(self, context):
...         self.context = context
...     def locate( self, name):
...         return self.source.get(name)

所以基本上这个适配器只需为给定名称返回一个对象。让我们试试。我们需要为此设置一些plone内容

>>> _ = self.folder.invokeFactory("Folder", "src")
>>> _ = self.folder.src.invokeFactory("Document", "doc", title="Muha")
>>> _ = self.folder.invokeFactory("Folder", "target")

现在我们可以提供适配器

>>> from Products.ATContentTypes.content.folder import ATFolder
>>> ztapi.provideAdapter(ATFolder,
...    IMirrorContentLocator, TestLocator)

并查找适配器

>>> locator = IMirrorContentLocator(self.folder.target)
>>> locator.source = self.folder.src

现在我们可以通过名称获取内容

>>> locator.locate("doc")
<ATDocument at /plone/Members/test_user_1_/src/doc>

好吧,这成功了。

插入(镜像)内容

基本上我们所做的就是从要镜像的内容中剥离其acquisition上下文,并将其插入目标上下文的acquisition链中。让我们试试

>>> from Acquisition import aq_inner, aq_base, aq_chain
>>> obj = self.folder.src.doc
>>> aq_chain(obj)
[<ATDocument at /plone/Members/test_user_1_/src/doc>, <ATFolder at /plone/Members/test_user_1_/src>, ...

我们看到,obj有一个正常的acquisition链,正如我们所期望的那样。接下来,我们将伪造acquisition链,使得obj_mirrored将看起来位于目标文件夹下方

>>> obj_mirrored = aq_base(obj).__of__(self.folder.target)
>>> aq_chain(obj_mirrored)
[<ATDocument at /plone/Members/test_user_1_/target/doc>, <ATFolder at /plone/Members/test_user_1_/target>, ...

太棒了。

遍历

现在我们只需提供一个方法来挂钩到Plone的对象遍历机制,并修改它,以便我们可以返回镜像对象。我们将提供的遍历器使用的是IPublishTraverse接口,这是Zope 3的方式

>>> from zope.publisher.interfaces import IPublishTraverse

Zope 2过去使用__bobo_traverse__来遍历对象。如今,遍历是通过提供一个适配器到IPublishTraverse来完成的。默认遍历器是DefaultPublishTraverse,它定义在Zope 2发布者中

>>> from ZPublisher.BaseRequest import DefaultPublishTraverse

此适配器最终会调用__bobo_traverse__。因此,不再需要覆盖__bobo_traverse__了。太好了。

我们为镜像内容提供的特殊适配器将执行以下操作

  • 尝试将遍历的上下文适配到IMirrorContentLocator,并定位当前遍历名称的内容

  • 从定位的内容对象中剥离acquisition链,并将其插入遍历上下文的acquisition链中,并返回它

好吧,让我们试试。

首先,我们需要创建一个IPublishTraverse适配器。请注意,这是一个多适配器,它将一个接口和一个IHTTPRequest适配到IPublishTraverse

>>> class MirrorTraverse(object):
...     def __init__(self,context,request):
...         self.context = context
...         self.request = request
...         self.locator = IMirrorContentLocator(context)
...     def publishTraverse(self, request, name):
...         obj = locator.locate(name)
...         return aq_base(aq_inner(obj)).__of__(self.context)

现在,我们想要提供适配器。虽然我们不希望覆盖默认行为。这就是我们定义一个标记接口来适配到IMirrorContentProvider的原因。我们提供适配器

>>> from inquant.contentmirror.base.interfaces import IMirrorContentProvider
>>> from zope.publisher.interfaces.http import IHTTPRequest
>>> ztapi.provideAdapter(
...     (IMirrorContentProvider,IHTTPRequest),
...     IPublishTraverse,
...     MirrorTraverse)

现在我们应该能够遍历。但是,我们需要一个测试请求来调用适配器

>>> request = TestRequest()
>>> IHTTPRequest.providedBy(request)
True

查询ZCA以获取适配器

>>> traverser = component.getMultiAdapter(
...     (self.folder.target, request), IPublishTraverse )
Traceback (most recent call last):
...
ComponentLookupError: ...

哎呀!啊,我们需要首先提供IMirrorContentProvider

>>> interface.alsoProvides(self.folder.target, IMirrorContentProvider)
>>> IMirrorContentProvider.providedBy(self.folder.target)
True

再试一次

>>> traverser = component.queryMultiAdapter(
...     (self.folder.target, request), IPublishTraverse )

啊哈!

不幸的是,为了这个测试,我们需要手动修补源。实际上,定位适配器当然会自己确定源。

>>> traverser.locator.source = self.folder.src

现在尝试遍历

>>> traverser.publishTraverse(request, "doc")
<ATDocument at /plone/Members/test_user_1_/target/doc>

太好了!请注意,返回的对象似乎来自目标文件夹,但实际上位于src文件夹。

清理

删除适配器

>>> gsm = component.getGlobalSiteManager()
>>> gsm.unregisterAdapter(
...     MirrorTraverse,
...     (IMirrorContentProvider,IHTTPRequest),
...     IPublishTraverse)
True
>>> gsm.unregisterAdapter(TestLocator, (ATFolder,),
...    IMirrorContentLocator)
True

项目详情


下载文件

为您的平台下载文件。如果您不确定要选择哪个,请了解更多关于安装包的信息。

源分发

inquant.contentmirror.base-0.3.tar.gz (14.3 kB 查看哈希值)

上传于 源码

构建的发行版

inquant.contentmirror.base-0.3-py2.4.egg (43.0 kB 查看哈希值)

上传于 源码

由以下支持