如何在Plone站点外部触发portal_quickinstaller.reinstallProducts表单?

如何在Plone站点外部触发portal_quickinstaller.reinstallProducts表单?,plone,zope,Plone,Zope,我们正在运行一个Zope服务器,最终会有少量的Plone(4)站点。不时会出现扩展产品更新,需要重新安装以获取配置文件设置中的更改,例如新内容类型 手动,这意味着点击每个Plone站点的门户\u quickinstaller,勾选产品,按更新。如果我们讨论的是几十个站点,那么这是不太可行的,所以我尝试将其自动化。基本上到目前为止,我在Zope根目录中以脚本(Python)的形式实现了以下功能: a = context.restrictedTraverse('/') p = a['Plone']

我们正在运行一个Zope服务器,最终会有少量的Plone(4)站点。不时会出现扩展产品更新,需要重新安装以获取配置文件设置中的更改,例如新内容类型

手动,这意味着点击每个Plone站点的
门户\u quickinstaller
,勾选产品,按更新。如果我们讨论的是几十个站点,那么这是不太可行的,所以我尝试将其自动化。基本上到目前为止,我在Zope根目录中以脚本(Python)的形式实现了以下功能:

a = context.restrictedTraverse('/')

p = a['Plone']
print p.getSiteManager()
qi = p.restrictedTraverse('portal_quickinstaller')
print qi
qi.reinstallProducts('LinguaPlone')
(简化了;实际上,我有一个较长的列表,而不是单Plone实例,并且我可能希望重新安装较长的产品列表。) 此操作失败,原因如下:

  Module Products.CMFQuickInstallerTool.QuickInstallerTool, line 613, in uninstallProducts
  Module Products.CMFQuickInstallerTool.InstalledProduct, line 272, in uninstall
  Module Products.CMFQuickInstallerTool.InstalledProduct, line 351, in _cascadeRemove
AttributeError: 'BaseGlobalComponents' object has no attribute 'objectItems'
从我到目前为止的调试尝试来看,
BaseGlobalComponents
是由
Zope.component.getSiteManager
返回的Zope站点管理器。我如何说服quickinstaller选择正确的安装程序,即它所在的Plone站点上的安装程序


或者,我将如何以一种对更大的安装仍然模糊可行的方式自动化重新安装产品?(ETA:我知道这不是使用cronjob自动执行的操作,但恐怕无法避免内部开发产品的更新。)

以下是如何更改活动的本地站点管理器。您将无法在受限Python中执行此操作,因此需要将Python脚本转换为外部方法或浏览器视图

from zope.app.component.hooks import setHooks, setSite
setHooks()
setSite(site)
setHooks调用只需要执行一次。在Zope2.12中,这些调用应该从Zope.site.hooks导入,而在Zope2.13中应该从Zope.component.hooks导入


请记住,调用“重新安装产品”并不适用于所有附加产品,除非您仔细检查了“重新安装”的功能,并确定它不会导致问题,否则不建议调用“重新安装产品”。某些产品提供的升级步骤可以更有选择性地运行操作。

免责声明:确实要这样做吗?自动地重新安装产品并将其升级到最新版本,盲目地并且不在登台实例上进行任何测试,这是自找麻烦

无论如何,您可以使用XML-RPC和一些调整来完成这项工作。以下是如何使用XML-RPC在实时运行的实例上安装产品:

>>> import xmlrpclib
>>> proxy = xmlrpclib.ServerProxy(
        "http://admin:passwd@localhost:8080/Plone/portal_quickinstaller"
    )
>>> proxy.getProductVersion('Marshall')
'2.0'
>>> proxy.isProductInstalled('Marshall')
'False'
>>> proxy.installProduct('Marshall')
'Registry installed sucessfully.\n'
>>> proxy.isProductInstalled('Marshall')
'True'
要重新安装,需要子类Products.CMFQuickInstallerTool.QuickInstallerTool.py,并为自定义QuickInstallerTool提供一个默认情况下关键字参数“reinstall”设置为“True”的方法;比如:

442c442
<                        swallowExceptions=None, reinstall=False,
---
>                        swallowExceptions=None, reinstall=True,
452,457c452,457
<         if self.isProductInstalled(p):
<             prod = self._getOb(p)
<             msg = ('This product is already installed, '
<                    'please uninstall before reinstalling it.')
<             prod.log(msg)
<             return msg
---
>         #if self.isProductInstalled(p):
>         #    prod = self._getOb(p)
>         #    msg = ('This product is already installed, '
>         #           'please uninstall before reinstalling it.')
>         #    prod.log(msg)
>         #    return msg
442c442
<0=无,重新安装=假,
---
>异常=无,重新安装=真,
452457C452457
<如果安装了self.isProduct(p):
#如果安装了self.isproduct(p):
>#prod=self._getOb(p)
>#msg=(“此产品已安装,”
>#“请先卸载,然后再重新安装。”)
>#prod.log(msg)
>#返回消息
更好的方法是:提供自己的方法来收集有关版本的信息并重新安装与XML-RPC协议兼容的产品(因为您不能传递关键字参数)


可能有更干净的方法通过XML-RPC实现这一点,但portal_quickinstaller并不打算以这种方式使用,而且可能存在一些警告小心使用

首先不要重新安装,在许多情况下,它可能会破坏您的网站

下一步你必须考虑,加载项可以提供升级步骤(通常它们会)。使用quickinstaller api在PythonScript中实现这一点。这很好,但也可以通过文件系统上的脚本来实现。检查此处的示例:


另一个解决方案是使用Selenium IDE在一个站点中记录quickinstaller的内容,并制作一个副本粘贴测试结果以在另一个站点上运行它(非常奇怪,不是吗?

我在一个具有7个Plone站点的实例的Zope根目录中获得了此python脚本。看起来和你的差不多。它可能只在Plone 2.5站点上工作(是的,旧的),但我认为它也应该在3.x和4.x上工作。也许一个天真的差异(我忽略了)导致了你的脚本中的错误;也许是你的行程被限制绊倒了。(为清晰起见,对脚本进行了编辑。)


谢谢,看起来这可能是我一直在寻找的魔法召唤,我明天早上会试试。由于我对许多产品都有一定程度的控制权,因此任何问题都至少是一次学习经历恐怕还是4.0.5上的错误,但感谢您查找。没有手动设置站点,quickinstaller api只有在从Plone站点内部调用时才能工作,这正是我的绊脚石。;)
SITES = ['site-1', 'site-2']
for site in SITES:
    print "Reinstalling LinguaPlone in %s." % site
    portal = context[site]
    qi = portal.portal_quickinstaller
    qi.reinstallProducts(['LinguaPlone'])