Plone/dexterity- 在某个事件中,如何更改只有所有者具有查看权限的内容类型对象的所有权角色?

Plone/dexterity- In an event, how can I change the ownership role of a content type object that only an owner has view permission for?

我正在尝试将内容类型的所有者更改为在事件订阅者 (IObjectAddedEvent) 中移动后进行更改。

在我的示例中,我将内容类型称为合同。 创建合约后,它会移动到容器中

我有两种类型的用户角色:Contractor 和 ContractManager

合同中有一个名为 'contractor' 的字段,所有具有承包商角色的用户都可以选择该字段。 ContractManager 也是一个 Contractor。 只有 ContractManager 可以更改承包商字段,否则当承包商尝试创建合同时,只会出现他们的名字。 此外,ContractManager 无法对其进行一次编辑,它已被创建,除非 ContractManager 恰好是该合同的 Contractor/Owner。

我为合同创建了一个工作流,在工作流中,我将其设置为只有具有所有者角色的用户才具有查看权限。

在我用于事件订阅者的 events.py 文件中,我有这个:

@grok.subscriber(Contract, IObjectAddedEvent)
def contractAdded(obj, event):
    #generate id of container contract will be moved into when its created
    #create a container (representing a week, i.e. contracts_12_5_2015)
    #if it doesn't already exist
    #copy and paste contract into container
    moved_contract = contracts[contract_container_id][obj.id]
    changeOwnership(moved_contract, obj.contractor)


def changeOwnership(user_id, obj):
    aq_base(obj).__ac_local_roles_block__ = True
    userid = u"%s" % userid
    membership = getToolByName(obj, 'portal_membership')
    user = membership.getMemberById(userid)
    obj.changeOwnership(user)
    obj.setCreators(userid,)
    owners = [o for o in obj.users_with_local_role('Owner')]
    for owner in owners:
        roles = list(obj.get_local_roles_for_userid(owner))
        roles.remove('Owner')
        if roles:
            obj.manage_setLocalRoles(owner, roles)
        else:
            obj.manage_delLocalRoles([owner])
    roles = list(obj.get_local_roles_for_userid(userid))
    if 'Owner' not in roles:
        roles.append('Owner')
        obj.manage_setLocalRoles(userid, roles)
    obj.reindexObjectSecurity()

不幸的是,当我以 ContractManager 身份登录并更改承包商是谁时,我收到此错误:

Traceback (innermost last):
Module ZPublisher.Publish, line 138, in publish
Module ZPublisher.mapply, line 77, in mapply
Module ZPublisher.Publish, line 48, in call_object
Module Solgema.fullcalendar.browser.dx, line 68, in __call__
Module plone.z3cform.layout, line 50, in update
Module plone.dexterity.browser.add, line 117, in update
Module plone.z3cform.fieldsets.extensible, line 59, in update
Module plone.z3cform.patch, line 30, in GroupForm_update
Module z3c.form.group, line 145, in update
Module plone.app.z3cform.csrf, line 21, in execute
Module z3c.form.action, line 98, in execute
Module z3c.form.button, line 315, in __call__
Module z3c.form.button, line 170, in __call__
Module plone.dexterity.browser.add, line 100, in handleAdd
Module z3c.form.form, line 250, in createAndAdd
Module plone.dexterity.browser.add, line 79, in add
AttributeError: 'NoneType' object has no attribute 'id'

我假设登录用户需要查看权限才能使 reindexObjectSecurity 正常工作。当我进入工作流管理器(通过 ZMI)并为 ContractManager 添加查看权限时,所有者成功更改,但在我的情况下,我试图做到只有所有者才能在导航中看到。如果是这样,有没有办法解决这个问题?

您可以在此操作期间使用 context manager 更改 SecurityManager。

这样您就可以运行将一些特定的代码行作为具有必要权限的不同用户。

此示例上下文管理器 运行s 代码作为系统用户:

import AccessControl


class SwitchedToSystemUser(object):
    """Switch temp. to System user
    """

    def __init__(self):
        self._original_security = None

    def __enter__(self):
        assert self._original_security is None

        self._original_security = AccessControl.getSecurityManager()

        _system_user = AccessControl.SecurityManagement.SpecialUsers.system
        AccessControl.SecurityManagement.newSecurityManager(None, _system_user)

    def __exit__(self, _exc_type, _exc_value, _traceback):
        AccessControl.SecurityManagement.setSecurityManager(
            self._original_security)
        self._original_security = None

用法示例:

// CODE FOR LOGGED-IN USER
...


with SwitchedToSystemUser():
    // DO STUFF AS SYSTEM USER


// MORE CODE FOR LOGGED-IN USER

您还可以检查 plone.api.env,它提供上下文管理器来采用用户或角色。