Plone工作流程:发布一个对象,以及所有使用/引用的对象

时间:2016-07-18 15:02:57

标签: plone plone-4.x

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

有三种不同的出版物 - “为所有人”(对任何人都可见),“可见”(对于经过身份验证的用户)和“受限制”(对于某些群组的成员)。选择哪一个(首先)是根据对象的类型确定的。用户只“批准”该对象,并自动选择发布类型。

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

这已经工作了几年但不再起作用了; 也许是因为一些交易问题?

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

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

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

2 个答案:

答案 0 :(得分:4)

假设您确定,隐式发布引用不会导致意外结果(想象一下编辑器就像:“我将其置于草稿状态并用机密注释装饰它,在准备好之前是暂时的对于发布,谁发布了这个?“)并且所有内容类型都分配了工作流程,下面的代码是您描述的算法的实现。

如果您的内容类型项目未分配工作流程,则在分配了工作流程的下一个上级父级上需要隐式发布。但是,如果第一个父级也没有分配工作流,那么这也会更改项目的兄弟姐妹甚至表兄弟和阿姨的继承权限。但是,您可以这样做,在代码中搜索“无情”并遵循注释的说明,在这种情况下,但是在这里为所有内容类型分配工作流似乎更值得推荐。

要考虑反向引用,当将引用的项目更改为比当前状态更低的公共状态时,将通知用户警告它可能不再可访问引用项目的受众,因此是自动的正如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')

答案 1 :(得分:0)

这是我最后所做的:

我重构了我的工作流程如下:

  • 它不再自动选择转换(对于发布级别),但允许用户明确指定for allvisiblerestricted
  • 我添加了切换到切换发布级别(对于错误的情况)。
  • 这些过渡根据方向的不同命名;例如,要将restricted项切换为visible,使用make_visible,而调用publishedvisible的过渡称为make_visible_again 。这样,所提到的项目只会在正确的方向上受到影响(不完美而是改进)。

提交对象的非工作出版物的问题是由交易引起的;将那些从/temp/移动到其公共位置的内容也涉及transaction.commit()

最后,一点点问题:如果切换工作流程,所有审核状态都会重新初始化 - 变量名称和状态都保持不变并不重要。因此,在这种情况下重命名工作流程并不起作用。可以通过切换回旧工作流程(具有相同名称但更新的功能)来恢复原始工作流程状态。