我有一个带有 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/