Рабочий процесс Plone: ​​публикация объекта, а также всех использованных / упомянутых объектов

plone plone-4.x

225 просмотра

2 ответа

1552 Репутация автора

У меня есть сайт Plone с объектами Archetypes, которые ссылаются на другие объекты (по UID). Когда, скажем, newsобъект опубликован, все imageобъекты, на которые есть ссылки в textатрибуте, тоже должны автоматически публиковаться.

Существует три вида публикаций - «для всех» (видимые для всех), «видимые» (для аутентифицированных пользователей) и «ограниченные» (для членов определенных групп). Какой из них выбран (в первую очередь), определяется типом объектов. Пользователь только «одобряет» объект, и тип публикации выбирается автоматически.

Для этого у меня есть changestateбраузер: он извлекает UID всех используемых объектов из text/htmlполей и применяет к ним соответствующие переходы рабочего процесса.

Это работало в течение нескольких лет, но больше не работает; возможно из-за проблем с транзакциями?

Однако, возможно, мое текущее решение слишком сложное.

Это должна быть довольно распространенная ситуация: когда публикуется «новость», все ее «реквизиты страницы» (которые только упоминаются, а не содержатся, поскольку любое изображение может использоваться более чем одним новостным объектом) должны быть опубликованы как Что ж. Должно быть какое-то «стандартное решение», верно?

Если пока нет «стандартного» или «передового опыта», меня интересуют возможные ошибки транзакций и т. Д.

Автор: Tobias Источник Размещён: 18.07.2016 03:02

Ответы (2)


4 плюса

3847 Репутация автора

Предполагая, что вы уверены, что неявная публикация ссылок не приводит к непредвиденным результатам (представьте, что редактор выглядит так: «Я бы поместил это в черновик и украсил его конфиденциальными комментариями, которые должны были быть временными до готовности к публикации»). кто, чёрт возьми, это опубликовал? ") и что всем типам контента присвоен рабочий процесс, код ниже представляет собой реализацию алгоритма, который вы описываете.

Если у вас есть элементы типа контента, которым не назначен рабочий процесс, неявная публикация будет необходима для следующего старшего родителя с назначенным рабочим процессом. Но это также изменяет унаследованные разрешения братьев и сестер или даже двоюродных братьев и сестер, если первому родителю также не назначен рабочий процесс. Тем не менее, вы могли бы сделать это, поискать «безжалостный» в коде и в этом случае следовать инструкциям комментария, но назначение рабочего процесса для всех типов контента здесь более рекомендуется.

Что касается обратных ссылок, при изменении ссылочного элемента на более низкое общедоступное состояние, чем текущее состояние, пользователь будет проинформирован с предупреждением о том, что он может быть недоступен для аудитории ссылочного элемента, таким образом, происходит автоматическое «понижение». публикация "не желательна, как указывает Лука Фаббри.

Определение того, какое состояние считается более общедоступным, чем другое, в котором 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')
Автор: Ida Ebkes Размещён: 21.07.2016 10:08

0 плюса

1552 Репутация автора

Вот что я сделал в конце:

Я реорганизовал мой рабочий процесс следующим образом:

  • Он больше не выбирает переходы автоматически (для уровней публикации), но позволяет пользователю явно указать for all, visibleили restricted.
  • Я добавил переходы для переключения уровней публикации (на случай ошибок).
  • Эти переходы называются по-разному в зависимости от направления; например, переключить restrictedэлемент на visible, make_visibleиспользуется, в то время как переход сделать publishedпункт visibleназывается make_visible_again. Таким образом, упомянутые предметы будут затронуты только в правильном направлении (не совершенство, а улучшение).

Проблема с нерабочими публикациями ссылочных объектов была вызвана транзакциями; перемещение тех из них /temp/в их общественное местоположение также было вовлечено transaction.commit().

Напоследок, небольшой вопрос: если рабочий процесс переключен, все состояния просмотра инициализируются заново - не имеет значения, что и имя переменной, и состояния остались неизменными. Таким образом, это не работает, чтобы переименовать рабочий процесс в этом случае. Исходные состояния рабочего процесса можно восстановить, вернувшись к старому рабочему процессу (с тем же именем, но с обновленной функциональностью).

Автор: Tobias Размещён: 28.08.2016 12:49
Вопросы из категории :
32x32