Example #1
0
def make_query(quote_params=QUOTE_PARAMS, **kw):
    query = list()
    for name, param in sorted(kw.items()):
        if param is None:
            continue
        if isinstance(param, compat.STR_TYPE):
            param = [param]
        if type(param) in compat.NUMBER_TYPES:
            param = [str(param)]
        quote = name in quote_params
        for p in param:
            p = safe_encode(p) if compat.IS_PY2 else p
            query.append('{}={}'.format(name, compat.quote(p) if quote else p))
    query = '&'.join(query)
    if query:
        return '?{}'.format(query)
Example #2
0
def make_query(quote_params=QUOTE_PARAMS, **kw):
    query = list()
    for name, param in sorted(kw.items()):
        if param is None:
            continue
        if isinstance(param, compat.STR_TYPE):
            param = [param]
        if type(param) in compat.NUMBER_TYPES:
            param = [str(param)]
        quote = name in quote_params
        for p in param:
            p = safe_encode(p) if compat.IS_PY2 else p
            query.append('{}={}'.format(name, compat.quote(p) if quote else p))
    query = '&'.join(query)
    if query:
        return '?{}'.format(query)
Example #3
0
def make_url(request, path=None, node=None, resource=None, query=None):
    # if path=[] in signature, path gets aggregated in recursive calls ???
    # happens on icon lookup in navtree.
    # ^^^ that is because the [] (a list, mutable) is generated at compile
    # time. mutable values should not be in function signatures to avoid this.
    if path is None:
        path = []
    else:
        path = copy.copy(path)
    if node is not None:
        path = node_path(node)
    if resource is not None:
        path.append(resource)
    path = [compat.quote(safe_encode(it)) for it in path]
    url = '{}/{}'.format(request.application_url, '/'.join(path))
    if not query:
        return url
    return '{}{}'.format(url, query)
Example #4
0
def make_url(request, path=None, node=None, resource=None, query=None):
    # if path=[] in signature, path gets aggregated in recursive calls ???
    # happens on icon lookup in navtree.
    # ^^^ that is because the [] (a list, mutable) is generated at compile
    # time. mutable values should not be in function signatures to avoid this.
    if path is None:
        path = []
    else:
        path = copy.copy(path)
    if node is not None:
        path = node_path(node)
    if resource is not None:
        path.append(resource)
    path = [compat.quote(safe_encode(it)) for it in path]
    url = '{}/{}'.format(request.application_url, '/'.join(path))
    if not query:
        return url
    return '{}{}'.format(url, query)
Example #5
0
    def test_copysupport(self):
        # Copysupport Attributes
        model = CopySupportNode()
        model['child'] = CopySupportNode()
        request = self.layer.new_request()

        with self.layer.authenticated('manager'):
            rendered = render_tile(model, request, 'contents')

        expected = 'class="selectable copysupportitem"'
        self.assertTrue(rendered.find(expected) > -1)

        with self.layer.authenticated('manager'):
            request = self.layer.new_request()
            cut_url = compat.quote(make_url(request, node=model['child']))
            request.cookies['cone.app.copysupport.cut'] = cut_url
            rendered = render_tile(model, request, 'contents')

        expected = 'class="selectable copysupportitem copysupport_cut"'
        self.assertTrue(rendered.find(expected) > -1)
    def test_copysupport(self):
        # Copysupport Attributes
        model = CopySupportNode()
        model['child'] = CopySupportNode()
        request = self.layer.new_request()

        with self.layer.authenticated('manager'):
            rendered = render_tile(model, request, 'contents')

        expected = 'class="selectable copysupportitem"'
        self.assertTrue(rendered.find(expected) > -1)

        with self.layer.authenticated('manager'):
            request = self.layer.new_request()
            cut_url = compat.quote(make_url(request, node=model['child']))
            request.cookies['cone.app.copysupport.cut'] = cut_url
            rendered = render_tile(model, request, 'contents')

        expected = 'class="selectable copysupportitem copysupport_cut"'
        self.assertTrue(rendered.find(expected) > -1)
Example #7
0
    def test_CameFromNext(self):
        # Plumbing behavior to hook up redirection after successful form
        # processing
        with self.layer.hook_tile_reg():
            @tile(name='camefromnextform')
            @plumbing(CameFromNext)
            class CameFromNextForm(Form):
                def prepare(self):
                    form = factory(
                        u'form',
                        name='camefromnextform',
                        props={'action': self.nodeurl})
                    form['next'] = factory(
                        'submit',
                        props={
                            'action': 'next',
                            'expression': True,
                            'next': self.next,
                            'label': 'Next',
                        })
                    self.form = form

        # Check behavior config defaults
        self.assertTrue(CameFromNextForm.default_came_from is None)
        self.assertTrue(CameFromNextForm.write_history_on_next is False)

        # Create a test model and login
        root = BaseNode()
        model = root['child'] = BaseNode()

        # Check whether ``came_from`` is rendered on form as proxy field
        with self.layer.authenticated('manager'):
            request = self.layer.new_request()
            came_from = compat.quote('http://example.com/some/path?foo=bar')
            request.params['came_from'] = came_from
            res = render_tile(model, request, 'camefromnextform')

        self.checkOutput("""
        ...<input id="input-camefromnextform-came_from"
        name="came_from" type="hidden"
        value="http%3A//example.com/some/path%3Ffoo%3Dbar" />...
        """, res)

        # No ``came_from`` on request, no ``default_came_from``, no ajax request
        with self.layer.authenticated('manager'):
            request = self.layer.new_request()
            request.params['action.camefromnextform.next'] = '1'
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(isinstance(request.environ['redirect'], HTTPFound))
        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/child'
        )

        # No ``came_from`` on request, ``default_came_from`` set to ``parent``,
        # no ajax request
        with self.layer.authenticated('manager'):
            CameFromNextForm.default_came_from = 'parent'
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(isinstance(request.environ['redirect'], HTTPFound))
        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/'
        )

        # No ``came_from`` on request, ``default_came_from`` set to URL, no
        # ajax request
        with self.layer.authenticated('manager'):
            came_from = compat.quote('http://example.com/foo/bar?baz=1')
            CameFromNextForm.default_came_from = came_from
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(isinstance(request.environ['redirect'], HTTPFound))
        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/foo/bar?baz=1'
        )

        # No ``came_from`` on request, ``default_came_from`` set to wrong
        # domain, no ajax request
        with self.layer.authenticated('manager'):
            CameFromNextForm.default_came_from = 'http://other.com'
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(isinstance(request.environ['redirect'], HTTPFound))
        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/child'
        )

        # ``came_from`` set to empty value on request, overrules
        # ``default_came_from``, no ajax request
        with self.layer.authenticated('manager'):
            CameFromNextForm.default_came_from = 'parent'
            request.params['came_from'] = ''
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(isinstance(request.environ['redirect'], HTTPFound))
        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/child'
        )

        # ``came_from`` set to ``parent`` on request, overrules
        # ``default_came_from``, no ajax request
        with self.layer.authenticated('manager'):
            CameFromNextForm.default_came_from = None
            request.params['came_from'] = 'parent'
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(isinstance(request.environ['redirect'], HTTPFound))
        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/'
        )

        # ``came_from`` set to URL on request, overrules ``default_came_from``,
        # no ajax request
        with self.layer.authenticated('manager'):
            came_from = compat.quote('http://example.com/default')
            CameFromNextForm.default_came_from = came_from

            came_from = compat.quote('http://example.com/other')
            request.params['came_from'] = came_from
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(isinstance(request.environ['redirect'], HTTPFound))
        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/other'
        )

        # Reset ``default_came_from``
        CameFromNextForm.default_came_from = None

        # ``came_from`` set to empty value on request, ajax request, no ajax
        # path continuation
        with self.layer.authenticated('manager'):
            request = self.layer.new_request()
            request.params['ajax'] = '1'
            request.params['action.camefromnextform.next'] = '1'
            request.params['came_from'] = ''
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(len(request.environ['cone.app.continuation']) == 1)
        continuation = request.environ['cone.app.continuation'][0]
        self.assertTrue(isinstance(continuation, AjaxEvent))
        self.assertEqual(
            (continuation.target, continuation.name, continuation.selector),
            ('http://example.com/child', 'contextchanged', '#layout')
        )

        # ``came_from`` set to ``parent`` on request, ajax request, no ajax
        # path continuation
        with self.layer.authenticated('manager'):
            request.params['came_from'] = 'parent'
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(len(request.environ['cone.app.continuation']) == 1)
        continuation = request.environ['cone.app.continuation'][0]
        self.assertTrue(isinstance(continuation, AjaxEvent))
        self.assertEqual(
            (continuation.target, continuation.name, continuation.selector),
            ('http://example.com/', 'contextchanged', '#layout')
        )

        # ``came_from`` set to URL on request, ajax request, no ajax path
        # continuation
        with self.layer.authenticated('manager'):
            came_from = compat.quote('http://example.com/some/path?foo=bar')
            request.params['came_from'] = came_from
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(len(request.environ['cone.app.continuation']) == 1)
        continuation = request.environ['cone.app.continuation'][0]
        self.assertTrue(isinstance(continuation, AjaxEvent))
        self.assertEqual(
            (continuation.target, continuation.name, continuation.selector),
            ('http://example.com/some/path?foo=bar', 'contextchanged', '#layout')
        )

        # ``came_from`` set to wrong domain on request, ajax request, no ajax
        # path continuation
        with self.layer.authenticated('manager'):
            came_from = compat.quote('http://other.com')
            request.params['came_from'] = came_from
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(len(request.environ['cone.app.continuation']) == 1)
        continuation = request.environ['cone.app.continuation'][0]
        self.assertTrue(isinstance(continuation, AjaxEvent))
        self.assertEqual(
            (continuation.target, continuation.name, continuation.selector),
            ('http://example.com/child', 'contextchanged', '#layout')
        )

        # ``came_from`` set to empty value on request, ajax request, setting
        # browser history configured
        with self.layer.authenticated('manager'):
            CameFromNextForm.write_history_on_next = True
            request = self.layer.new_request()
            request.params['ajax'] = '1'
            request.params['action.camefromnextform.next'] = '1'
            request.params['came_from'] = ''
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(len(request.environ['cone.app.continuation']) == 2)

        path = request.environ['cone.app.continuation'][0]
        self.assertTrue(isinstance(path, AjaxPath))
        self.assertEqual(
            (path.path, path.target, path.event),
            (u'child', 'http://example.com/child', 'contextchanged:#layout')
        )

        event = request.environ['cone.app.continuation'][1]
        self.assertTrue(isinstance(event, AjaxEvent))
        self.assertEqual(
            (event.target, continuation.name, continuation.selector),
            ('http://example.com/child', 'contextchanged', '#layout')
        )

        # ``came_from`` set to ``parent`` on request, ajax request, setting
        # browser history configured
        with self.layer.authenticated('manager'):
            request.params['came_from'] = 'parent'
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(len(request.environ['cone.app.continuation']) == 2)

        path = request.environ['cone.app.continuation'][0]
        self.assertTrue(isinstance(path, AjaxPath))
        self.assertEqual(
            (path.path, path.target, path.event),
            ('', 'http://example.com/', 'contextchanged:#layout')
        )

        event = request.environ['cone.app.continuation'][1]
        self.assertTrue(isinstance(event, AjaxEvent))
        self.assertEqual(
            (event.target, continuation.name, continuation.selector),
            ('http://example.com/', 'contextchanged', '#layout')
        )

        # ``came_from`` set to URL on request, ajax request, setting browser
        # history configured
        with self.layer.authenticated('manager'):
            came_from = compat.quote('http://example.com/some/path')
            request.params['came_from'] = came_from
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(len(request.environ['cone.app.continuation']) == 2)

        path = request.environ['cone.app.continuation'][0]
        self.assertTrue(isinstance(path, AjaxPath))
        self.assertEqual(
            (path.path, path.target, path.event),
            ('/some/path', 'http://example.com/some/path', 'contextchanged:#layout')
        )

        event = request.environ['cone.app.continuation'][1]
        self.assertTrue(isinstance(event, AjaxEvent))
        self.assertEqual(
            (event.target, continuation.name, continuation.selector),
            ('http://example.com/some/path', 'contextchanged', '#layout')
        )

        # ``came_from`` set to to wrong on request, ajax request, setting
        # browser history configured
        with self.layer.authenticated('manager'):
            came_from = compat.quote('http://other.com')
            request.params['came_from'] = came_from
            res = render_tile(model, request, 'camefromnextform')

        self.assertTrue(len(request.environ['cone.app.continuation']) == 2)

        path = request.environ['cone.app.continuation'][0]
        self.assertTrue(isinstance(path, AjaxPath))
        self.assertEqual(
            (path.path, path.target, path.event),
            (u'child', 'http://example.com/child', 'contextchanged:#layout')
        )

        event = request.environ['cone.app.continuation'][1]
        self.assertTrue(isinstance(event, AjaxEvent))
        self.assertEqual(
            (event.target, continuation.name, continuation.selector),
            ('http://example.com/child', 'contextchanged', '#layout')
        )

        # Reset ``write_history_on_next``
        CameFromNextForm.write_history_on_next = False
Example #8
0
    def test_editing(self):
        @node_info(
            name='mynode',
            title='My Node')
        class MyNode(BaseNode):
            pass

        # Create and register an ``editform`` named form tile
        with self.layer.hook_tile_reg():
            @tile(name='editform', interface=MyNode)
            @plumbing(ContentEditForm)
            class MyEditForm(Form):
                def prepare(self):
                    form = factory(
                        u'form',
                        name='editform',
                        props={
                            'action': self.nodeurl
                        })
                    form['title'] = factory(
                        'field:label:text',
                        value=self.model.attrs.title,
                        props={
                            'label': 'Title',
                        })
                    form['update'] = factory(
                        'submit',
                        props={
                            'action': 'update',
                            'expression': True,
                            'handler': self.update,
                            'next': self.next,
                            'label': 'Update',
                        })
                    self.form = form

                def update(self, widget, data):
                    fetch = self.request.params.get
                    self.model.attrs.title = fetch('editform.title')

        # Dummy model
        root = MyNode()
        child = root['somechild'] = MyNode()
        child.attrs.title = 'My Node'

        # Render form with value from model
        with self.layer.authenticated('editor'):
            request = self.layer.new_request()
            res = render_tile(root['somechild'], request, 'edit')

        self.checkOutput("""
        ...<span class="label label-primary">Edit: My Node</span>...
        <form action="http://example.com/somechild"...
        """, res)

        # Render with submitted data. Default next URL of EditForm is the
        # edited node
        with self.layer.authenticated('editor'):
            request = self.layer.new_request()
            request.params['action.editform.update'] = '1'
            request.params['editform.title'] = 'Changed title'
            res = render_tile(root['somechild'], request, 'edit')

        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/somechild'
        )

        # Check next URL with ``parent`` as ``came_from`` value
        with self.layer.authenticated('editor'):
            request = self.layer.new_request()
            request.params['action.editform.update'] = '1'
            request.params['editform.title'] = 'Changed title'
            request.params['came_from'] = 'parent'
            res = render_tile(root['somechild'], request, 'edit')

        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/'
        )

        # Check next URL with URL as ``came_from`` value
        with self.layer.authenticated('editor'):
            request = self.layer.new_request()
            request.params['action.editform.update'] = '1'
            request.params['editform.title'] = 'Changed title'
            came_from = compat.quote('http://example.com/other/node/in/tree')
            request.params['came_from'] = came_from
            res = render_tile(root['somechild'], request, 'edit')

        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/other/node/in/tree'
        )

        # Render with ajax flag
        with self.layer.authenticated('editor'):
            request = self.layer.new_request()
            request.params['action.editform.update'] = '1'
            request.params['editform.title'] = 'Changed title'
            request.params['ajax'] = '1'
            res = render_tile(root['somechild'], request, 'edit')

        self.assertTrue(isinstance(
            request.environ['cone.app.continuation'][0],
            AjaxEvent
        ))

        # URL computing is the same as if ``HTTPFound`` instance is returned.
        # In Ajax case, the URL is used as ajax target
        self.assertEqual(
            request.environ['cone.app.continuation'][0].target,
            'http://example.com/somechild'
        )

        with self.layer.authenticated('editor'):
            request = self.layer.new_request()
            request.params['action.editform.update'] = '1'
            request.params['editform.title'] = 'Changed title'
            came_from = compat.quote('http://example.com/other/node/in/tree')
            request.params['came_from'] = came_from
            request.params['ajax'] = '1'
            res = render_tile(root['somechild'], request, 'edit')

        self.assertEqual(
            request.environ['cone.app.continuation'][0].target,
            'http://example.com/other/node/in/tree'
        )
        # Check the updated node
        self.assertEqual(root['somechild'].attrs.title, 'Changed title')

        # Edit view
        with self.layer.authenticated('editor'):
            request = self.layer.new_request()
            request.params['action.editform.update'] = '1'
            request.params['editform.title'] = 'Changed title'
            root.attrs.title = 'Foo'
            res = edit(root, request)

        self.assertTrue(isinstance(res, HTTPFound))

        with self.layer.authenticated('editor'):
            request = self.layer.new_request()
            request.params['action.editform.update'] = '1'
            request.params['editform.title'] = 'Changed title'
            request.params['ajax'] = '1'
            res = str(edit(root, request))

        self.assertTrue(res.find('parent.bdajax.render_ajax_form') != -1)
Example #9
0
    def test_adding(self):
        # Provide a node interface needed for different node style binding to
        # test form
        class ITestAddingNode(Interface):
            pass

        # Create dummy node
        @node_info(
            name='mynode',
            title='My Node',
            description='This is My node.',
            addables=['mynode'])  # self containment
        @implementer(ITestAddingNode)
        class MyNode(BaseNode):
            pass

        # Create another dummy node inheriting from AdapterNode
        @node_info(
            name='myadapternode',
            title='My Adapter Node',
            description='This is My adapter node.',
            addables=['myadapternode'])  # self containment
        @implementer(ITestAddingNode)
        class MyAdapterNode(AdapterNode):
            pass

        # Create and register an ``addform`` named form tile
        with self.layer.hook_tile_reg():
            @tile(name='addform', interface=ITestAddingNode)
            @plumbing(ContentAddForm)
            class MyAddForm(Form):
                def prepare(self):
                    form = factory(
                        u'form',
                        name='addform',
                        props={
                            'action': self.nodeurl
                        })
                    form['id'] = factory(
                        'field:label:text',
                        props={
                            'label': 'Id',
                        })
                    form['title'] = factory(
                        'field:label:text',
                        props={
                            'label': 'Title',
                        })
                    form['add'] = factory(
                        'submit',
                        props={
                            'action': 'add',
                            'expression': True,
                            'handler': self.add,
                            'next': self.next,
                            'label': 'Add',
                        })
                    self.form = form

                def add(self, widget, data):
                    fetch = self.request.params.get
                    child = MyNode()
                    child.attrs.title = fetch('addform.title')
                    self.model.parent[fetch('addform.id')] = child
                    self.model = child

        # Create dummy container
        root = MyNode()

        # Render without factory
        with self.layer.authenticated('manager'):
            request = self.layer.new_request()
            self.assertEqual(
                render_tile(root, request, 'add'),
                u'unknown_factory'
            )

        # Render with valid factory
        with self.layer.authenticated('manager'):
            request.params['factory'] = 'mynode'
            result = render_tile(root, request, 'add')

        self.assertTrue(result.find(u'<form action="http://example.com"') != -1)

        # Render with valid factory on adapter node
        with self.layer.authenticated('manager'):
            adapterroot = MyAdapterNode(None, None, None)
            request.params['factory'] = 'myadapternode'
            result = render_tile(adapterroot, request, 'add')

        self.assertTrue(result.find(u'<form action="http://example.com"') != -1)

        # Render with submitted data
        with self.layer.authenticated('manager'):
            request = self.layer.current_request
            request.params['factory'] = 'mynode'
            request.params['action.addform.add'] = '1'
            request.params['addform.id'] = 'somechild'
            request.params['addform.title'] = 'Some Child'
            render_tile(root, request, 'add')

        self.assertTrue(isinstance(request.environ['redirect'], HTTPFound))
        self.checkOutput("""
        <class '...MyNode'>: None
          <class '...MyNode'>: somechild
        """, root.treerepr())

        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/somechild'
        )
        del request.environ['redirect']

        # Render with 'came_from' set
        with self.layer.authenticated('manager'):
            request.params['came_from'] = 'parent'
            render_tile(root, request, 'add')

        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/'
        )
        del request.environ['redirect']

        with self.layer.authenticated('manager'):
            came_from = compat.quote('http://example.com/foo/bar?baz=1')
            request.params['came_from'] = came_from
            render_tile(root, request, 'add')

        self.assertEqual(
            request.environ['redirect'].location,
            'http://example.com/foo/bar?baz=1'
        )

        # Render with ajax flag
        with self.layer.authenticated('manager'):
            request.params['ajax'] = '1'
            render_tile(root, request, 'add')

        self.assertTrue(isinstance(
            request.environ['cone.app.continuation'][0],
            AjaxEvent
        ))

        # Check the modified model
        self.assertEqual(root.keys(), ['somechild'])
        self.assertEqual(root['somechild'].attrs.title, 'Some Child')

        # Add view
        with self.layer.authenticated('manager'):
            request = self.layer.new_request()
            request.params['factory'] = 'mynode'
            request.params['action.addform.add'] = '1'
            request.params['addform.id'] = 'somechild'
            request.params['addform.title'] = 'Some Child'
            res = add(root, request)

        self.assertTrue(isinstance(res, HTTPFound))

        with self.layer.authenticated('manager'):
            request.params['ajax'] = '1'
            res = str(add(root, request))

        self.assertTrue(res.find('parent.bdajax.render_ajax_form') != -1)
    def test_copysupport(self):
        @node_info(
            name='copy_support_node_a',
            title='CopySupportNodeA',
            addables=['copy_support_node_a', 'copy_support_node_b'])
        class CopySupportNodeA(CopySupportNode):
            pass

        @node_info(
            name='copy_support_node_b',
            title='CopySupportNodeB',
            addables=['copy_support_node_b'])
        class CopySupportNodeB(CopySupportNode):
            pass

        root = CopySupportNodeA()
        source = root['source'] = CopySupportNodeA()
        source['a_child'] = CopySupportNodeA()
        source['b_child'] = CopySupportNodeB()
        target = root['target'] = CopySupportNodeB()

        request = self.layer.new_request()
        copy_url = compat.quote(make_url(request, node=source['a_child']))
        request.cookies['cone.app.copysupport.copy'] = copy_url

        paste_tile = PasteAction(None, 'render', '')
        paste_tile(target, request)

        self.checkOutput("""
        Pasted 0 items<br /><strong>Pasting of 1 items
        failed</strong><br />Violation. 'CopySupportNodeB' is not allowed
        to contain 'CopySupportNodeA'
        """, request.environ['cone.app.continuation'][0].payload)

        copy_url = compat.quote(make_url(request, node=source['b_child']))
        request.cookies['cone.app.copysupport.copy'] = copy_url
        del request.environ['cone.app.continuation']

        paste_tile(target, request)
        self.assertEqual(target.messages, ['Called: target'])
        target.messages = []

        self.checkOutput("""
        cone.app.copysupport.copy=; Max-Age=0; Path=/; expires=...
        """, request.response.headers['Set-Cookie'])

        self.assertTrue(isinstance(
            request.environ['cone.app.continuation'][0],
            AjaxMessage
        ))
        self.assertTrue(isinstance(
            request.environ['cone.app.continuation'][1],
            AjaxAction
        ))
        self.assertTrue(isinstance(
            request.environ['cone.app.continuation'][2],
            AjaxEvent
        ))

        self.checkOutput("""
        <class '...CopySupportNodeA'>: None
          <class '...CopySupportNodeA'>: source
            <class '...CopySupportNodeA'>: a_child
            <class '...CopySupportNodeB'>: b_child
          <class '...CopySupportNodeB'>: target
            <class '...CopySupportNodeB'>: b_child
        """, root.treerepr())

        paste_tile(target, request)
        self.assertEqual(target.messages, ['Called: target'])
        target.messages = []

        self.checkOutput("""
        <class '...CopySupportNodeA'>: None
          <class '...CopySupportNodeA'>: source
            <class '...CopySupportNodeA'>: a_child
            <class '...CopySupportNodeB'>: b_child
          <class '...CopySupportNodeB'>: target
            <class '...CopySupportNodeB'>: b_child
            <class '...CopySupportNodeB'>: b_child-1
        """, root.treerepr())

        cut_url = compat.quote(make_url(request, node=source['b_child']))
        request.cookies['cone.app.copysupport.cut'] = cut_url
        del request.cookies['cone.app.copysupport.copy']
        paste_tile(target, request)
        self.assertEqual(target.messages, ['Called: target'])
        self.assertEqual(source.messages, ['Called: source'])
        target.messages = []
        source.messages = []

        self.checkOutput("""
        cone.app.copysupport.cut=; Max-Age=0; Path=/; expires=...
        """, request.response.headers['Set-Cookie'])

        self.checkOutput("""
        <class '...CopySupportNodeA'>: None
          <class '...CopySupportNodeA'>: source
            <class '...CopySupportNodeA'>: a_child
          <class '...CopySupportNodeB'>: target
            <class '...CopySupportNodeB'>: b_child
            <class '...CopySupportNodeB'>: b_child-1
            <class '...CopySupportNodeB'>: b_child-2
        """, root.treerepr())

        cut_url = compat.quote(make_url(request, node=source['a_child']))
        request.cookies['cone.app.copysupport.cut'] = cut_url
        del request.environ['cone.app.continuation']
        paste_tile(target, request)
        self.checkOutput("""
        <class '...CopySupportNodeA'>: None
          <class '...CopySupportNodeA'>: source
            <class '...CopySupportNodeA'>: a_child
          <class '...CopySupportNodeB'>: target
            <class '...CopySupportNodeB'>: b_child
            <class '...CopySupportNodeB'>: b_child-1
            <class '...CopySupportNodeB'>: b_child-2
        """, root.treerepr())

        self.checkOutput("""
        Pasted 0 items<br /><strong>Pasting of 1 items
        failed</strong><br />Violation. 'CopySupportNodeB' is not
        allowed to contain 'CopySupportNodeA'
        """, request.environ['cone.app.continuation'][0].payload)

        cut_url = compat.quote(make_url(request, node=source))
        del request.environ['cone.app.continuation']
        request.cookies['cone.app.copysupport.cut'] = cut_url
        paste_tile(root['source']['a_child'], request)

        self.checkOutput("""
        Pasted 0 items<br /><strong>Pasting of 1 items
        failed</strong><br />Cannot paste cut object to child of it: source
        """, request.environ['cone.app.continuation'][0].payload)

        cut_url = '::'.join([
            compat.quote(make_url(request, node=target['b_child'])),
            compat.quote(make_url(request, node=target['b_child-1'])),
        ])
        request.cookies['cone.app.copysupport.cut'] = cut_url
        del request.environ['cone.app.continuation']
        paste_tile(source, request)
        self.assertEqual(source.messages, ['Called: source'])
        self.assertEqual(target.messages, ['Called: target'])
        source.messages = []
        target.messages = []

        self.checkOutput("""
        <class '...CopySupportNodeA'>: None
          <class '...CopySupportNodeA'>: source
            <class '...CopySupportNodeA'>: a_child
            <class '...CopySupportNodeB'>: b_child
            <class '...CopySupportNodeB'>: b_child-1
          <class '...CopySupportNodeB'>: target
            <class '...CopySupportNodeB'>: b_child-2
        """, root.treerepr())

        root['unknown_source'] = BaseNode()
        root['unknown_target'] = BaseNode()

        cut_url = compat.quote(make_url(request, node=root['unknown_source']))
        request.cookies['cone.app.copysupport.cut'] = cut_url
        del request.environ['cone.app.continuation']
        paste_tile(target, request)

        self.checkOutput("""
        Pasted 0 items<br /><strong>Pasting of 1 items
        failed</strong><br />Cannot paste 'unknown_source'. Unknown source
        """, request.environ['cone.app.continuation'][0].payload)

        cut_url = compat.quote(make_url(request, node=source['b_child']))
        request.cookies['cone.app.copysupport.cut'] = cut_url
        del request.environ['cone.app.continuation']
        paste_tile(root['unknown_target'], request)

        self.checkOutput("""
        Pasted 0 items<br /><strong>Pasting of 1 items
        failed</strong><br />Cannot paste to 'unknown_target'. Unknown target
        """, request.environ['cone.app.continuation'][0].payload)

        del request.cookies['cone.app.copysupport.cut']
        del request.environ['cone.app.continuation']
        paste_tile(root['unknown_target'], request)

        self.assertEqual(
            request.environ['cone.app.continuation'][0].payload,
            u'Nothing to paste'
        )
    def test_copysupport(self):
        @node_info(name='copy_support_node_a',
                   title='CopySupportNodeA',
                   addables=['copy_support_node_a', 'copy_support_node_b'])
        class CopySupportNodeA(CopySupportNode):
            pass

        @node_info(name='copy_support_node_b',
                   title='CopySupportNodeB',
                   addables=['copy_support_node_b'])
        class CopySupportNodeB(CopySupportNode):
            pass

        root = CopySupportNodeA()
        source = root['source'] = CopySupportNodeA()
        source['a_child'] = CopySupportNodeA()
        source['b_child'] = CopySupportNodeB()
        target = root['target'] = CopySupportNodeB()

        request = self.layer.new_request()
        copy_url = compat.quote(make_url(request, node=source['a_child']))
        request.cookies['cone.app.copysupport.copy'] = copy_url

        paste_tile = PasteAction(None, 'render', '')
        paste_tile(target, request)

        self.checkOutput(
            """
        Pasted 0 items<br /><strong>Pasting of 1 items
        failed</strong><br />Violation. 'CopySupportNodeB' is not allowed
        to contain 'CopySupportNodeA'
        """, request.environ['cone.app.continuation'][0].payload)

        copy_url = compat.quote(make_url(request, node=source['b_child']))
        request.cookies['cone.app.copysupport.copy'] = copy_url
        del request.environ['cone.app.continuation']

        paste_tile(target, request)
        self.assertEqual(target.messages, ['Called: target'])
        target.messages = []

        self.checkOutput(
            """
        cone.app.copysupport.copy=; Max-Age=0; Path=/; expires=...
        """, request.response.headers['Set-Cookie'])

        self.assertTrue(
            isinstance(request.environ['cone.app.continuation'][0],
                       AjaxMessage))
        self.assertTrue(
            isinstance(request.environ['cone.app.continuation'][1], AjaxEvent))
        self.assertEqual(request.environ['cone.app.continuation'][1].target,
                         'http://example.com/target?contenttile=listing')

        self.checkOutput(
            """
        <class '...CopySupportNodeA'>: None
          <class '...CopySupportNodeA'>: source
            <class '...CopySupportNodeA'>: a_child
            <class '...CopySupportNodeB'>: b_child
          <class '...CopySupportNodeB'>: target
            <class '...CopySupportNodeB'>: b_child
        """, root.treerepr())

        target.properties.action_paste_tile = 'custom'
        del request.environ['cone.app.continuation']

        paste_tile(target, request)
        self.assertEqual(target.messages, ['Called: target'])
        target.messages = []

        self.assertEqual(request.environ['cone.app.continuation'][1].target,
                         'http://example.com/target?contenttile=custom')

        self.checkOutput(
            """
        <class '...CopySupportNodeA'>: None
          <class '...CopySupportNodeA'>: source
            <class '...CopySupportNodeA'>: a_child
            <class '...CopySupportNodeB'>: b_child
          <class '...CopySupportNodeB'>: target
            <class '...CopySupportNodeB'>: b_child
            <class '...CopySupportNodeB'>: b_child-1
        """, root.treerepr())

        cut_url = compat.quote(make_url(request, node=source['b_child']))
        request.cookies['cone.app.copysupport.cut'] = cut_url
        del request.cookies['cone.app.copysupport.copy']
        del request.environ['cone.app.continuation']

        paste_tile(target, request)
        self.assertEqual(target.messages, ['Called: target'])
        self.assertEqual(source.messages, ['Called: source'])
        target.messages = []
        source.messages = []

        self.checkOutput(
            """
        cone.app.copysupport.cut=; Max-Age=0; Path=/; expires=...
        """, request.response.headers['Set-Cookie'])

        self.checkOutput(
            """
        <class '...CopySupportNodeA'>: None
          <class '...CopySupportNodeA'>: source
            <class '...CopySupportNodeA'>: a_child
          <class '...CopySupportNodeB'>: target
            <class '...CopySupportNodeB'>: b_child
            <class '...CopySupportNodeB'>: b_child-1
            <class '...CopySupportNodeB'>: b_child-2
        """, root.treerepr())

        cut_url = compat.quote(make_url(request, node=source['a_child']))
        request.cookies['cone.app.copysupport.cut'] = cut_url
        del request.environ['cone.app.continuation']

        paste_tile(target, request)
        self.checkOutput(
            """
        <class '...CopySupportNodeA'>: None
          <class '...CopySupportNodeA'>: source
            <class '...CopySupportNodeA'>: a_child
          <class '...CopySupportNodeB'>: target
            <class '...CopySupportNodeB'>: b_child
            <class '...CopySupportNodeB'>: b_child-1
            <class '...CopySupportNodeB'>: b_child-2
        """, root.treerepr())

        self.checkOutput(
            """
        Pasted 0 items<br /><strong>Pasting of 1 items
        failed</strong><br />Violation. 'CopySupportNodeB' is not
        allowed to contain 'CopySupportNodeA'
        """, request.environ['cone.app.continuation'][0].payload)

        cut_url = compat.quote(make_url(request, node=source))
        del request.environ['cone.app.continuation']
        request.cookies['cone.app.copysupport.cut'] = cut_url

        paste_tile(root['source']['a_child'], request)
        self.checkOutput(
            """
        Pasted 0 items<br /><strong>Pasting of 1 items
        failed</strong><br />Cannot paste cut object to child of it: source
        """, request.environ['cone.app.continuation'][0].payload)

        cut_url = '::'.join([
            compat.quote(make_url(request, node=target['b_child'])),
            compat.quote(make_url(request, node=target['b_child-1'])),
        ])
        request.cookies['cone.app.copysupport.cut'] = cut_url
        del request.environ['cone.app.continuation']

        paste_tile(source, request)
        self.assertEqual(source.messages, ['Called: source'])
        self.assertEqual(target.messages, ['Called: target'])
        source.messages = []
        target.messages = []

        self.checkOutput(
            """
        <class '...CopySupportNodeA'>: None
          <class '...CopySupportNodeA'>: source
            <class '...CopySupportNodeA'>: a_child
            <class '...CopySupportNodeB'>: b_child
            <class '...CopySupportNodeB'>: b_child-1
          <class '...CopySupportNodeB'>: target
            <class '...CopySupportNodeB'>: b_child-2
        """, root.treerepr())

        root['unknown_source'] = BaseNode()
        root['unknown_target'] = BaseNode()

        cut_url = compat.quote(make_url(request, node=root['unknown_source']))
        request.cookies['cone.app.copysupport.cut'] = cut_url
        del request.environ['cone.app.continuation']

        paste_tile(target, request)
        self.checkOutput(
            """
        Pasted 0 items<br /><strong>Pasting of 1 items
        failed</strong><br />Cannot paste 'unknown_source'. Unknown source
        """, request.environ['cone.app.continuation'][0].payload)

        cut_url = compat.quote(make_url(request, node=source['b_child']))
        request.cookies['cone.app.copysupport.cut'] = cut_url
        del request.environ['cone.app.continuation']

        paste_tile(root['unknown_target'], request)
        self.checkOutput(
            """
        Pasted 0 items<br /><strong>Pasting of 1 items
        failed</strong><br />Cannot paste to 'unknown_target'. Unknown target
        """, request.environ['cone.app.continuation'][0].payload)

        del request.cookies['cone.app.copysupport.cut']
        del request.environ['cone.app.continuation']

        paste_tile(root['unknown_target'], request)
        self.assertEqual(request.environ['cone.app.continuation'][0].payload,
                         u'Nothing to paste')