Plone 工作流 : Publish an object, 以及所有使用/引用的对象

标签 plone plone-4.x

我有一个带有 Archetypes 对象的 Plone 站点,这些对象引用其他对象(通过 UID)。例如,当一个 news 对象被发布时,所有在 text 属性中引用的 image 对象也应该自动被发布。

共有三种不同类型的发布 - “for all”(对任何人可见)、“visible”(对经过身份验证的用户)和“restricted”(对特定组的成员)。选择其中的哪一个(首先)取决于对象的类型。用户仅“批准”该对象,并自动选择发布类型。

为了实现这一点,我有一个 changestate 浏览器:它从 text/html 字段中提取所有使用对象的 UID,并对它们应用适当的工作流转换。

这已经工作了几年,但现在不再工作了; 可能是因为一些交易问题?

但是,也许我目前的解决方案太复杂了。

这应该是一个很常见的情况:当发布“新闻”时,它的所有“页面要求”(仅引用,而不是包含,因为任何图像都可能被多个新闻使用object) 也应该发布。一定有一些“标准解决方案”,对吧?

如果还没有“标准”或“最佳实践”解决方案,我也对可能的交易陷阱等感兴趣。

最佳答案

假设您确定隐式发布引用文献不会导致意外结果(想象一个编辑会这样说:“我将其置于草稿状态并用 secret 评论对其进行修饰,这意味着在准备好之前是临时的为了发布,到底是谁发布了这个?”)并且所有内容类型都分配了工作流程,下面的代码是您描述的算法的实现。

如果您有未分配工作流的内容类型项目,则需要在分配有工作流的下一个上层父级上进行隐式发布。但这也会更改项目的 sibling 甚至堂 sibling 和阿姨的继承权限,如果第一个 parent 也没有分配工作流程的话。但是,您可以这样做,在代码中搜索“ruthless”并按照评论的说明进行操作,在这种情况下,但在此处为所有内容类型分配一个工作流似乎更值得推荐。

关于反向引用,当将引用的项目更改为比当前状态更低的公共(public)状态时,用户将收到一条警告,即引用项目的受众可能无法再访问它,因此自动正如 Luca Fabbri 指出的那样,“向下发布”是不可取的。

哪个状态被认为比另一个状态更公开的定义位于 PublicRank.states 中,您需要根据您使用的工作流程的状态进行调整。

好的,所以涉及到两个文件,首先在your.addon/your/addon/configure.zcml中注册两个事件处理器:

  <!--  A state has changed, execute 'publishReferences': -->
  <subscriber for="Products.CMFCore.interfaces.IContentish
                   Products.CMFCore.interfaces.IActionSucceededEvent"
          handler=".subscriber.publishReferences" />

  <!--  A state is about to be changed, execute 'warnAbout...': -->
  <subscriber for="Products.CMFCore.interfaces.IContentish
                   Products.DCWorkflow.interfaces.IBeforeTransitionEvent"
          handler=".subscriber.warnAboutPossiblyInaccessibleBackReferences" />

然后添加具有以下内容的your.addon/your/addon/subscriber.py:

from Products.statusmessages.interfaces import IStatusMessage
from zope.globalrequest import getRequest


class PublicRank:
    """
    Define which state is to be considered more public than another,
    most public first. Assume for now, only Plone's default workflow
    'simple_publication_workflow' is used in the portal.
    """
    states = ['published', 'pending', 'private']

def isMorePublic(state_one, state_two):
    """
    Check if state_one has a lesser index in the rank than state_two.
    """
    states = PublicRank.states
    if states.index(state_one) < states.index(state_two): return True
    else: return False

def getState(obj):
    """
    Return workflow-state-id or None, if no workflow is assigned.
    Show possible error on the console and log it.
    """
    if hasWorkflow(obj):
        try: return obj.portal_workflow.getInfoFor(obj, 'review_state')
        except ExceptionError as err: obj.plone_log(err)
    else: return None

def getTransitions(obj):
    """
    Return the identifiers of the available transitions as a list.
    """
    transitions = []
    trans_dicts = obj.portal_workflow.getTransitionsFor(obj)
    for trans_dict in trans_dicts:
        transitions.append(trans_dict['id'])
    return transitions

def hasWorkflow(obj):
    """
    Return boolean, indicating whether obj has a workflow assigned, or not.
    """
    return len(obj.portal_workflow.getWorkflowsFor(obj)) > 0

def hasTransition(obj, transition):
    if transition in getTransitions(obj): return True
    else: return False

def isSite(obj):
    return len(obj.getPhysicalPath()) == 2

def publishReferences(obj, eve, RUHTLESS=False):
    """
    If an obj gets published, publish its references, too.
    If an item doesn't have a workflow assigned and RUHTLESS
    is passed to be True, publish next upper parent with a workflow.
    """
    states = PublicRank.states
    state = getState(obj)
    transition = eve.action

    if state in states:
        refs = obj.getRefs()
        for ref in refs:
            ref_state = getState(ref)
            if ref_state:
                if isMorePublic(state, ref_state):
                    setState(ref, transition)
            else: # no workflow assigned
                if RUTHLESS:
                    setStateRelentlessly(ref, transition)

def setState(obj, transition):
    """
    Execute transition, return possible error as an UI-message,
    instead of consuming the whole content-area with a raised Exeption.
    """
    path = '/'.join(obj.getPhysicalPath())
    messages = IStatusMessage(getRequest())
    if hasWorkflow(obj):
        if hasTransition(obj, transition):
            try:
                obj.portal_workflow.doActionFor(obj, transition)
            except Exception as error:
                messages.add(error, type=u'error')
        else:
            message = 'The transition "%s" is not available for "%s".'\
                       % (transition, path)
            messages.add(message, type=u'warning')
    else:
        message = 'No workflow retrievable for "%s".' % path
        messages.add(message, type=u'warning')

def setStateRelentlessly(obj, transition):
    """
    If obj has no workflow, change state of next
    upper parent which has a workflow, instead.
    """
    while not getState(obj, state):
        obj = obj.getParentNode()
        if isSite(obj): break
    setState(obj, transition)

def warnAboutPossiblyInaccessibleBackReferences(obj, eve):
    """
    If an obj is about to switch to a lesser public state than it
    has and is referenced of other item(s), show a warning message
    with the URL(s) of the referencing item(s), so the user can check,
    if the link is still accessible for the intended audience.
    """
    states = PublicRank.states
    item_path = '/'.join(obj.getPhysicalPath())[2:]
    target_state = str(eve.new_state).split(' ')[-1][:-1]
    refs = obj.getBackReferences()

    for ref in refs:
        ref_state = getState(ref)
        if isMorePublic(ref_state, target_state):
            ref_path = '/'.join(ref.getPhysicalPath())[2:]
            messages = IStatusMessage(getRequest())
            message = u'This item "%s" is now in a less published state than \
            item "%s" of which it is referenced by. You might want to check, \
            if this item can still be accessed by the intended audience.' \
            % (item_path, ref_path)
            messages.add(message, type=u'warning')

关于Plone 工作流 : Publish an object, 以及所有使用/引用的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38440066/

相关文章:

xsd - plone.app.theming 包括来自外部站点的内容

plone - Plone 4.1 是否使用 KSS?我可以安全地禁用与 KSS 关联的 JS 和 CSS 文件吗?

描述字段后的 Plone 敏捷注入(inject)行为字段

非站点管理员用户的 Ploneboard 管理员角色

Plone 和 Twitter Bootstrap

python - 如何在 Plone 5 中移动搜索栏

plone - 如何以编程方式访问 Plone 5 注册表中的资源列表

plone - z3c.dependencychecker 列出的哪些依赖项不应该包含在我的包中?

plone - 在 Plone 中添加一个简单的 html 和 css 页面

python - 如何根据选择变量中的值将 PloneFormGen 重定向到页面?