"""Object editing testing module"""

import sys
from unittest import TestSuite, makeSuite

from zope.annotation.interfaces import IAnnotations

from AccessControl.SecurityManager import setSecurityPolicy
from AccessControl import ZopeGuards
from AccessControl.ZopeGuards import guarded_getattr, get_safe_globals
from AccessControl.ImplPython import ZopeSecurityPolicy
from AccessControl import Unauthorized
from Acquisition import aq_base

from Testing.ZopeTestCase import user_name

# Restricted Python imports
from RestrictedPython import compile_restricted
from RestrictedPython.Guards import safe_builtins
from RestrictedPython.SafeMapping import SafeMapping

from base import eduCommonsTestCase

class TestEditing(eduCommonsTestCase):
    """Test object editing for different roles and workflow states"""

    def afterSetUp(self):
        self.catalog = self.portal.portal_catalog
        self.workflow = self.portal.portal_workflow
        self.membership = self.portal.portal_membership

        setSecurityPolicy(ZopeSecurityPolicy(verbose=True))

        # We need to manually register the roles from the rolemap with
        # the PAS plugin.
        self.portal.acl_users.portal_role_manager.addRole('Producer')

        self.membership.addMember('producer', 'secret',
                                  ['Producer', 'Member',], [])
        self.membership.addMember('member', 'secret',
                                  ['Member',], [])

        self.loginAsPortalOwner()
        self.portal.invokeFactory('Division', 'division')
        self.portal.division.invokeFactory('Course', 'course')
        self.division = self.portal.division
        self.course = self.division.course

        self.logout()

    def beforeTearDown(self):
        self.loginAsPortalOwner()
        self.portal.manage_delObjects([self.division.id])
        self.logout()

    def execUntrusted(self, func, *args, **kwargs):
        """Set up a Python environment with Zope security in place"""
        globals = get_safe_globals()
        globals['__builtins__'] = safe_builtins
        globals['_getattr_'] = guarded_getattr

        data = {'func' : func,
                'args' : ZopeGuards.SafeIter(args),
                'kwargs' : kwargs
               }

        globals.update(data)
        body = """func(*args, **kwargs)"""
        code = compile_restricted(body, "<string>", "eval")
        eval(code, globals)

    def assertUnauthorized(self, func, *args, **kwargs):
        """Check that calling func with currently effective roles will
        raise Unauthroized.
        """
        try:
            self.execUntrusted(func, *args, **kwargs)
        except Unauthorized, e:
            return

        raise AssertionError, 'Unauthorized exception was expected'

    def assertAuthorized(self, func, *args, **kwargs):
        """Check that calling func with currently effective roles will
        not raise Unauthroized.
        """
        try:
            self.execUntrusted(func, *args, **kwargs)
        except Unauthorized, e:
            raise AssertionError, 'Unauthorized exception was not expected'

    def check_course_edit(self, obj):
        # XXX: this doesn't look good enough. Shouldn't the edit method
        # raise an Unauthorized exception by itself?
        if self.membership.checkPermission('Modify portal content', obj):
            obj.edit(text='test Text')
        else:
            raise Unauthorized

    def test_producer_can_edit_inprogress_course(self):
        self.login('producer')
        self.assertAuthorized(self.check_course_edit, self.course)
        self.logout()

    def test_producer_cannot_edit_qa_course(self):
        self.loginAsPortalOwner()
        self.workflow.doActionFor(self.course, 'submit')
        self.assertEqual(
            self.workflow.getInfoFor(self.course, 'review_state'), 'QA')
        self.logout()

        self.login('producer')
        self.assertUnauthorized(self.check_course_edit, self.course)
        self.logout()


def test_suite():
    from unittest import TestSuite, makeSuite
    suite = TestSuite()
    suite.addTest(makeSuite(TestEditing))
    return suite

Some of the helper methods are based on code from this blog post.