ZAM(Zope应用管理)的API
项目描述
此软件包提供ZAM(Zope应用管理)的API。
CHANGES
0.7.0 (2011-01-13)
更新测试设置和测试,以与ZTK 1.0和当前z3c.form版本一起运行。
移除了对zope.app.twisted、zc.configuration以及大部分zope.app.*软件包的依赖。
使用Python的doctest模块代替已废弃的zope.testing.doctestunit。
修复了软件包元数据,在long_description中添加了doctests。
0.6.1 (2009-07-06)
移除了弃用警告。
0.6.0 (2009-07-06)
更新测试和依赖项以与软件包的最新版本一起工作。
0.5.3 (2008-06-07)
测试依赖项(zope.app.session)缺失(仍在与KGS 3.4进行验证)
0.5.2 (2008-04-11)
简化ftesting设置,删除了重复的配置。使其更具可重用性。现在我们可以在插件测试中同时包含app.zcml和mixin ftesting.zcml。
0.5.1 (2008-04-13)
为zamplugin.contents插件添加了新的插件层
0.5.0 (2008-04-11)
现在插件提供它自己的管理表单。默认情况下,可以使用PluginManagement页面,它是一个IContentProvider和IForm的混合体。这使得编写能够执行更多操作(而不仅仅是安装和卸载)的智能插件管理视图成为可能。
初始发布
zam.api
本包包含Zope应用程序管理API。我们为此包提供测试皮肤,允许我们测试插件管理页面。还有一个可供此测试使用的ZAMTest站点。此测试站点也可以用于任何其他zam.*或zamplugin.*包。
首先以管理员的身份登录
>>> from zope.testbrowser.testing import Browser >>> manager = Browser() >>> manager.addHeader('Authorization', 'Basic mgr:mgrpw')
检查我们是否可以访问在ftesting.zcml文件中注册的page.html视图,并使用我们的皮肤
>>> manager = Browser() >>> manager.handleErrors = False >>> manager.addHeader('Authorization', 'Basic mgr:mgrpw') >>> skinURL = 'https://127.0.0.1/++skin++ZAMTest/index.html' >>> manager.open(skinURL) >>> manager.url 'https://127.0.0.1/++skin++ZAMTest/index.html'
现在让我们创建一个名为first的测试站点并将其添加到根目录
>>> import zam.api.testing >>> root = getRootFolder() >>> firstSite = zam.api.testing.ZAMTestSite(u'first') >>> root['first'] = firstSite
再创建一个名为second的站点
>>> secondSite = zam.api.testing.ZAMTestSite(u'second') >>> root['second'] = secondSite
转到新的zam测试站点
>>> firstSiteURL = 'https://127.0.0.1/++skin++ZAMTest/first' >>> manager.open(firstSiteURL + '/index.html') >>> manager.url 'https://127.0.0.1/++skin++ZAMTest/first/index.html'
并转到second站点
>>> secondSiteURL = 'https://127.0.0.1/++skin++ZAMTest/second' >>> manager.open(secondSiteURL + '/index.html') >>> manager.url 'https://127.0.0.1/++skin++ZAMTest/second/index.html'
然后访问plugins.html页面
>>> manager.open(firstSiteURL + '/plugins.html')
现在我们看到了plugins.html页面
>>> print manager.contents <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>ZAM</title><meta http-equiv="cache-control" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" /> </head> <body> <form action="./plugins.html" method="post" enctype="multipart/form-data" class="plugin-form"> <h1>ZAM Plugin Management</h1> <fieldset id="pluginManagement"> <strong class="notInstalledPlugin">ZAM test plugin</strong> <div class="description">ZAM test plugin.</div> <div class="viewspace"> <div> </div> </div> <div> <div class="buttons"> <input id="zam-api-testing-buttons-install" name="zam.api.testing.buttons.install" class="submit-widget button-field" value="Install" type="submit" /> </div> </div> </fieldset> </form> </body> </html>
在我们安装插件之前,我们尝试访问只有安装了zam测试插件才能访问的页面
>>> manager.open(firstSiteURL + '/test.html') Traceback (most recent call last): ... NotFound: Object: <ZAMTestSite u'first'>, name: u'test.html'
第二个站点也没有提供这样的测试页面
>>> manager.open(secondSiteURL + '/test.html') Traceback (most recent call last): ... NotFound: Object: <ZAMTestSite u'second'>, name: u'test.html'
如您所见,没有这样的test.html页面。让我们安装我们的zam测试插件
>>> manager.open(firstSiteURL + '/plugins.html') >>> manager.getControl(name='zam.api.testing.buttons.install').click()
现在我们可以看到插件已安装
>>> print manager.contents <!DOCTYPE... <h1>ZAM Plugin Management</h1> <fieldset id="pluginManagement"> <strong class="installedPlugin">ZAM test plugin</strong> <div class="description">ZAM test plugin.</div> <div class="viewspace"> ... <div> <div class="buttons"> <input id="zam-api-testing-buttons-uninstall" name="zam.api.testing.buttons.uninstall" class="submit-widget button-field" value="Uninstall" type="submit" /> </div> </div> ...
现在让测试覆盖满意,测试不同的事情。zam插件测试页面可在first站点找到
>>> manager.open(firstSiteURL + '/test.html') >>> manager.url 'https://127.0.0.1/++skin++ZAMTest/first/test.html'
但在second站点不可用
>>> manager.open(secondSiteURL + '/test.html') Traceback (most recent call last): ... NotFound: Object: <ZAMTestSite u'second'>, name: u'test.html'
让我们卸载插件
>>> manager.open(firstSiteURL + '/plugins.html') >>> manager.getControl(name='zam.api.testing.buttons.uninstall').click()
并检查站点是否不再可用
>>> manager.open(firstSiteURL + '/test.html') Traceback (most recent call last): ... NotFound: Object: <ZAMTestSite u'first'>, name: u'test.html'
ZAM插件框架
插件框架允许我们编写依赖于基础系统API的“第三方”软件,而基础系统不会以任何方式依赖于新软件。这使我们能够保持基础系统紧凑,并将可选功能分开到清晰分开的包中。
提供了两种不同类型的插件。简单插件在安装和卸载过程中完成它们需要完成的工作。支持基本注册的插件将安装自定义组件注册。
该包的基本概念是,可以为特定站点安装插件。在任何时候,您都可以询问插件,它是否为特定站点安装了。第三个API方法允许您从站点卸载插件。
所以让我们实现一个简单的插件,它存储一个属性
>>> from zam.api import plugin>>> class SamplePlugin(plugin.Plugin): ... title = u'Sample' ... description = u'Sample Attribute Plugin' ... ... def isInstalled(self, site): ... """See interfaces.IPlugin""" ... return hasattr(site, 'sample') ... ... def install(self, site): ... """See interfaces.IPlugin""" ... if not self.isInstalled(site): ... setattr(site, 'sample', 1) ... ... def uninstall(self, site): ... """See interfaces.IPlugin""" ... if self.isInstalled(site): ... delattr(site, 'sample')
插件标题和描述作为用户的信息片段,通常在UI中使用。
所以让我们使用示例插件
>>> from zam.api import testing >>> site = testing.ZAMTestSite(u'ZAM Test Site') >>> sm = site.getSiteManager()>>> sample = SamplePlugin()
最初插件未安装,所以让我们处理这个问题。
>>> sample.isInstalled(site) False>>> sample.install(site) >>> site.sample 1>>> sample.isInstalled(site) True
然而,一旦插件安装,就不能再次安装
>>> site.sample = 2>>> sample.install(site) >>> site.sample 2
这是API的要求。现在您也可以卸载插件
>>> sample.uninstall(site) >>> sample.isInstalled(site) False >>> site.sample Traceback (most recent call last): ... AttributeError: 'ZAMTestSite' object has no attribute 'sample'
不能再次卸载插件
>>> sample.uninstall(site)
基本注册插件
一个重要的基本实现是安装新基本注册到站点的插件。
我们还需要为插件提供一个基本注册
>>> import zope.component >>> from z3c.baseregistry import baseregistry>>> sampleRegistry = baseregistry.BaseComponents( ... zope.component.globalSiteManager, 'sampleRegistry')
现在我们可以创建插件,无论是通过实例化还是通过子类化
>>> class SampleRegistryPlugin(plugin.BaseRegistryPlugin): ... title = u'Sample Registry' ... description = u'Sample Registry Plugin' ... registry = sampleRegistry>>> regPlugin = SampleRegistryPlugin()
我们使用之前相同的API方法。最初插件未安装
>>> sampleRegistry in sm.__bases__ False >>> regPlugin.isInstalled(site) False
现在我们安装插件
>>> regPlugin.install(site)>>> sampleRegistry in sm.__bases__ True >>> regPlugin.isInstalled(site) True
就像以前一样,再次安装插件没有任何作用
>>> len(sm.__bases__) 2>>> regPlugin.install(site)>>> len(sm.__bases__) 2
卸载插件同样简单
>>> regPlugin.uninstall(site)>>> sampleRegistry in sm.__bases__ False >>> regPlugin.isInstalled(site) False >>> len(sm.__bases__) 1
第二次卸载没有任何作用
>>> regPlugin.uninstall(site)>>> sampleRegistry in sm.__bases__ False >>> len(sm.__bases__) 1
层
我们提供了一个细粒度的层概念,允许您使用开箱即用的ZAM皮肤,或者让您定义自己的皮肤,提供您所需的功能。每个ZAM插件应为其组件配置IZAMBrowserLayer,而不是IZAMCoreLayer。这允许其他人使用IZAMCoreLayer而无需任何插件配置。有关ZAM层概念的更多信息,请参阅下面的不同层描述。
重要提示
这仅在你想要定义自己的皮肤,该皮肤使用选定的zam插件时才重要。
在适配器查找方面,层概念有一些局限性。无法定义一个自定义层,并使现有层表现得像它继承了该层。只有类上的实现和提供概念起作用,但不是接口。让我们更精确地说:它们起作用,但不会影响请求。这意味着请求不知道有这些应用层。这意味着没有[*]方法可以将后来定义的层应用于现有层。这就是为什么我们在zam.api.layer包中提供所有插件层的原因。但如果你想定义自定义插件及其层呢?你必须定义自己的皮肤,并在该皮肤中继承你的新层。你可以跳过命名的皮肤配置,并配置你的自定义皮肤。
[*] 好吧,有一种方法可以将层应用于现有层,或者至少它将产生相同的效果。有两种方法:你可以添加一个SkinChangedEvent,这将执行alsoProvide并注入你的层,或者你可以使用一个“遍历前事件”订阅者来完成同样的工作。我决定不在这里使用这些模式作为默认设置,因为这样的订阅者将影响每个皮肤,并且将在每个请求上消耗处理时间。我们定义一个自定义皮肤显式配置的选项太小,不足以付出这个代价。
IZAMCoreLayer
核心层提供了ZAM核心管理视图,但没有插件和皮肤配置。这允许我们编写具有选择性插件选择的皮肤。当然,每个插件都必须为你的自定义皮肤重新配置。默认情况下,没有方法在不使用两个不同层的情况下配置插件集以使其工作。
IZAMPluginLayer
zam插件层不应在插件中使用。您需要在zam.api.layer中为您的插件定义一个插件层,并使用这个新定义的层。这个层然后成为IZAMPluginLayer的一部分。这使得使用IZAMPluginLayer并获得所有配置成为可能。
但如果你不在zamplugin.*命名空间下开发会发生什么?那么你只能选择为额外的层配置你的插件,并使用另一个使用IZAMPluginLayer和你的自定义层的皮肤。使用IAZMPluginLayer进行配置并共享这样的包会导致配置不良,其他人如果他们的皮肤不依赖于IZAMPluginLayer且不需要使用你的配置,则需要排除你的配置。当然,你可以在自己的私有项目中这样做,但请不要将其用于公共共享的包。帮助我们提供一个干净的IZAMPluginLayer!
如果它不需要配置额外的订阅者,任何提供更好层使用概念的改进都受到欢迎。
IZAMBrowserLayer
这是一个“一应俱全”的层,可用于构建了解所有插件配置的皮肤。所有插件都应该使用这个层。
IZAMSkinLayer
IZAMSkinLayer提供了ZAM的UI部分,但未注册为皮肤。如果你想要开发一个自定义皮肤,可以使用这个层作为基础。这个层包含嵌套的div菜单实现。
IZAMBrowserSkin
IZAMBrowserSkin使用IZAMSkinLayer和IZAMBrowserLayer,作为命名皮肤的UI部分。这意味着IZAMBrowserSkin可以通过++skin++ZAM访问。