# -*- coding: utf-8 -*-
from plone.app.contentmenu.interfaces import IActionsMenu
from plone.app.contentmenu.interfaces import IDisplayMenu
from plone.app.contentmenu.interfaces import IFactoriesMenu
from plone.app.contentmenu.interfaces import IPortletManagerMenu
from plone.app.contentmenu.interfaces import IWorkflowMenu
from plone.app.contentmenu.testing import PLONE_APP_CONTENTMENU_AT_INTEGRATION_TESTING  # noqa
from plone.app.contentmenu.testing import PLONE_APP_CONTENTMENU_DX_INTEGRATION_TESTING  # noqa
from plone.app.contenttypes.testing import set_browserlayer
from plone.app.testing import login
from plone.app.testing import logout
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
from plone.locking.interfaces import ILockable
from Products.CMFCore.Expression import Expression
from Products.CMFCore.utils import getToolByName
from Products.CMFPlone.interfaces import INonStructuralFolder
from Products.CMFPlone.interfaces import ISelectableConstrainTypes
from Products.CMFPlone.tests import dummy
from Products.CMFPlone.utils import _createObjectByType
from zope.browsermenu.interfaces import IBrowserMenu
from zope.component import getUtility
from zope.interface import directlyProvides
import unittest


class TestActionsMenuAT(unittest.TestCase):
    layer = PLONE_APP_CONTENTMENU_AT_INTEGRATION_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        self.portal.invokeFactory('Folder', 'folder')
        self.folder = self.portal['folder']
        self.menu = getUtility(
            IBrowserMenu, name='plone_contentmenu_actions',
            context=self.folder)
        self.request = self.layer['request']

    def test_actionsMenuImplementsIBrowserMenu(self):
        self.failUnless(IBrowserMenu.providedBy(self.menu))

    def test_actionsMenuImplementsIActionsMenu(self):
        self.failUnless(IActionsMenu.providedBy(self.menu))

    def test_actionsMenuFindsActions(self):
        actions = self.menu.getMenuItems(self.folder, self.request)
        self.failUnless(
            'plone-contentmenu-actions-copy'
            in [a['extra']['id'] for a in actions]
        )


class TestActionsMenuDX(TestActionsMenuAT):
    layer = PLONE_APP_CONTENTMENU_DX_INTEGRATION_TESTING

    def setUp(self):
        super(TestActionsMenuDX, self).setUp()
        set_browserlayer(self.request)


class TestDisplayMenuAT(unittest.TestCase):
    layer = PLONE_APP_CONTENTMENU_AT_INTEGRATION_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        self.portal.invokeFactory('Folder', 'folder')
        self.folder = self.portal['folder']
        self.menu = getUtility(
            IBrowserMenu, name='plone_contentmenu_display',
            context=self.folder)
        self.request = self.layer['request']
        self.is_dx = self.folder.meta_type == 'Dexterity Container'

    def testActionsMenuImplementsIBrowserMenu(self):
        self.failUnless(IBrowserMenu.providedBy(self.menu))

    def testActionsMenuImplementsIActionsMenu(self):
        self.failUnless(IDisplayMenu.providedBy(self.menu))

    # Template selection

    def testTemplatesIncluded(self):
        actions = self.menu.getMenuItems(self.folder, self.request)
        templates = [a['extra']['id'] for a in actions]
        self.failUnless(
            'plone-contentmenu-display-folder_listing' in templates
            or
            'plone-contentmenu-display-listing_view' in templates
            # plone.app.contenttypes has unified views
        )

    def testSingleTemplateIncluded(self):
        self.folder.invokeFactory('Document', 'doc1')
        if self.is_dx:
            set_browserlayer(self.request)
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.assertEqual(len(actions), 1)
        self.assertEqual(
            actions[0]['extra']['id'],
            'plone-contentmenu-display-document_view'
        )

    def testNonBrowserDefaultReturnsNothing(self):
        f = dummy.Folder()
        self.folder._setObject('f1', f)
        actions = self.menu.getMenuItems(self.folder.f1, self.request)
        self.assertEqual(len(actions), 0)

    def testDefaultPageIncludesParentOnlyWhenItemHasSingleView(self):
        self.folder.invokeFactory('Document', 'doc1')
        self.folder.setDefaultPage('doc1')
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.failUnless('folderDefaultPageDisplay' in
                        [a['extra']['id'] for a in actions])
        self.failIf('document_view' in [a['extra']['id'] for a in actions])

    def testDefaultPageIncludesParentAndItemViewsWhenItemHasMultipleViews(self):  # noqa
        fti = self.portal.portal_types['Document']
        if self.is_dx:
            documentViews = fti.view_methods + ('content-core',)
            set_browserlayer(self.request)
        else:
            documentViews = fti.view_methods + ('base_view',)
        fti.manage_changeProperties(view_methods=documentViews)
        self.folder.invokeFactory('Document', 'doc1')
        self.folder.setDefaultPage('doc1')
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.assertIn(
            'folderDefaultPageDisplay',
            [a['extra']['id'] for a in actions]
        )
        self.assertIn(
            'plone-contentmenu-display-document_view',
            [a['extra']['id'] for a in actions]
        )
        if self.is_dx:
            self.assertIn(
                'plone-contentmenu-display-content-core',
                [a['extra']['id'] for a in actions]
            )
        else:
            self.assertIn(
                'plone-contentmenu-display-base_view',
                [a['extra']['id'] for a in actions]
            )

    def testCurrentTemplateSelected(self):
        self.folder.getLayout()
        actions = self.menu.getMenuItems(self.folder, self.request)
        selected = [a['extra']['id'] for a in actions if a['selected']]
        self.assertTrue(
            selected == ['plone-contentmenu-display-folder_listing']
            or
            selected == ['plone-contentmenu-display-listing_view']
            # plone.app.contenttypes has unified views
        )

    # Default-page selection

    def testFolderCanSetDefaultPage(self):
        self.folder.invokeFactory('Folder', 'f1')
        self.failUnless(self.folder.f1.canSetDefaultPage())
        actions = self.menu.getMenuItems(self.folder.f1, self.request)
        self.failUnless('contextSetDefaultPage' in
                        [a['extra']['id'] for a in actions])

    def testWithCanSetDefaultPageFalse(self):
        self.folder.invokeFactory('Folder', 'f1')
        self.folder.f1.manage_permission('Modify view template', ('Manager',))
        setRoles(self.portal, TEST_USER_ID, ['Contributor'])
        self.assertFalse(self.folder.f1.canSetDefaultPage())
        actions = self.menu.getMenuItems(self.folder.f1, self.request)
        self.assertNotIn(
            'contextSetDefaultPage',
            [a['extra']['id'] for a in actions]
        )

    def testSelectItemNotIncludedInNonStructuralFolder(self):
        self.folder.invokeFactory('Folder', 'f1')
        directlyProvides(self.folder.f1, INonStructuralFolder)
        actions = self.menu.getMenuItems(self.folder.f1, self.request)
        self.failIf('contextSetDefaultPage' in
                    [a['extra']['id'] for a in actions])

    def testDefaultPageSelectedAndOverridesLayout(self):
        self.folder.invokeFactory('Document', 'doc1')
        self.folder.setDefaultPage('doc1')
        actions = self.menu.getMenuItems(self.folder, self.request)
        selected = [a['extra']['id'] for a in actions if a['selected']]
        self.assertEqual(selected, ['contextDefaultPageDisplay'])

    def testDefaultPageCanBeChangedInContext(self):
        self.folder.invokeFactory('Document', 'doc1')
        self.folder.setDefaultPage('doc1')
        actions = self.menu.getMenuItems(self.folder, self.request)
        self.failUnless('contextChangeDefaultPage' in
                        [a['extra']['id'] for a in actions])

    def testDefaultPageCanBeChangedInFolder(self):
        self.folder.invokeFactory('Document', 'doc1')
        self.folder.setDefaultPage('doc1')
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.failUnless('folderChangeDefaultPage' in
                        [a['extra']['id'] for a in actions])
        self.failIf('contextChangeDefaultPage' in
                    [a['extra']['id'] for a in actions])

    # Headers/separators

    def testSeparatorsIncludedWhenViewingDefaultPageWithViews(self):
        fti = self.portal.portal_types['Document']
        if self.is_dx:
            documentViews = fti.view_methods + ('content-core',)
            set_browserlayer(self.request)
        else:
            documentViews = fti.view_methods + ('base_view',)
        fti.manage_changeProperties(view_methods=documentViews)
        self.folder.invokeFactory('Document', 'doc1')
        self.folder.setDefaultPage('doc1')
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        ids = [a['extra']['id'] for a in actions]
        self.failUnless('folderHeader' in ids)
        self.failUnless('contextHeader' in ids)

    def testSeparatorsNotIncludedWhenViewingDefaultPageWithoutViews(self):
        self.folder.invokeFactory('Document', 'doc1')
        self.folder.setDefaultPage('doc1')
        if self.is_dx:
            set_browserlayer(self.request)
        self.assertEqual(len(self.folder.doc1.getAvailableLayouts()), 1)
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        ids = [a['extra']['id'] for a in actions]
        self.failIf('folderHeader' in ids)
        self.failIf('contextHeader' in ids)

    def testSeparatorsNotDisplayedWhenViewingFolder(self):
        fti = self.portal.portal_types['Document']
        documentViews = fti.view_methods + ('base_view',)
        fti.manage_changeProperties(view_methods=documentViews)
        self.folder.invokeFactory('Document', 'doc1')
        self.folder.setDefaultPage('doc1')
        actions = self.menu.getMenuItems(self.folder, self.request)
        ids = [a['extra']['id'] for a in actions]
        self.failIf('folderHeader' in ids)
        self.failIf('contextHeader' in ids)

    # Regressions

    def testDefaultPageTemplateTitle(self):
        self.folder.invokeFactory('Document', 'doc1')
        self.folder.doc1.setTitle("New Document")
        self.folder.setDefaultPage('doc1')
        actions = self.menu.getMenuItems(self.folder, self.request)
        changeAction = [x for x in actions if
                        x['extra']['id'] == 'contextDefaultPageDisplay'][0]
        changeAction['title'].default
        self.assertEqual(
            u"New Document",
            changeAction['title'].mapping['contentitem']
        )


class TestDisplayMenuDX(TestDisplayMenuAT):
    layer = PLONE_APP_CONTENTMENU_DX_INTEGRATION_TESTING

    def setUp(self):
        super(TestDisplayMenuDX, self).setUp()
        set_browserlayer(self.request)


class TestFactoriesMenuAT(unittest.TestCase):
    layer = PLONE_APP_CONTENTMENU_AT_INTEGRATION_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        self.portal.invokeFactory('Folder', 'folder')
        self.folder = self.portal['folder']
        self.folder.invokeFactory('Document', 'doc1')
        self.menu = getUtility(
            IBrowserMenu, name='plone_contentmenu_factory',
            context=self.folder)
        self.request = self.layer['request']
        self.is_dx = self.folder.meta_type == 'Dexterity Container'

    def testMenuImplementsIBrowserMenu(self):
        self.assertTrue(IBrowserMenu.providedBy(self.menu))

    def testMenuImplementsIFactoriesMenu(self):
        self.assertTrue(IFactoriesMenu.providedBy(self.menu))

    def testMenuIncludesFactories(self):
        actions = self.menu.getMenuItems(self.folder, self.request)
        self.assertIn('image', [a['extra']['id'] for a in actions])

    def testAddViewExpressionUsedInMenu(self):
        self.folder
        self.portal.portal_types['Image']._setPropValue(
            'add_view_expr', 'string:custom_expr')
        actions = self.menu.getMenuItems(self.folder, self.request)
        urls = [a['action'] for a in actions]
        self.assertIn('custom_expr', urls)
        if self.is_dx:
            self.assertIn('%s/++add++File' % self.folder.absolute_url(), urls)
        else:
            found = False
            for url in urls:
                if '%s/createObject?type_name=File' % self.folder.absolute_url() in url:  # noqa
                    found = True
            self.assertTrue(found)

    def testFrontPageExpressionContext(self):
        # If the expression context uses the front-page instead of the
        # folder using the front-page, then the expression values will
        # be incorrect.
        self.portal.portal_types['Event']._setPropValue(
            'add_view_expr', 'string:${folder_url}/+/addATEvent')
        self.folder.invokeFactory('Collection', 'aggregator')
        aggregator = self.folder['aggregator']
        self.folder.setDefaultPage('aggregator')
        actions = self.menu.getMenuItems(aggregator, self.request)
        self.failUnless(
            'http://nohost/plone/folder/+/addATEvent' in
            [a['action'] for a in actions]
        )
        self.failIf(
            'http://nohost/plone/folder/aggregator/+/addATEvent' in
            [a['action'] for a in actions])

    def testTypeNameIsURLQuoted(self):
        if self.is_dx:
            # DX does not use plusquote
            return
        actions = self.menu.getMenuItems(self.folder, self.request)
        found = False
        for url in [a['action'] for a in actions]:
            if self.folder.absolute_url() + '/createObject?type_name=News+Item' in url:  # noqa
                found = True
        self.assertTrue(found)

    def testMenuIncludesFactoriesOnNonFolderishContext(self):
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        img = None
        for a in actions:
            if a['extra']['id'] == 'image':
                img = a
                break
        self.failIf(img is None)
        action = img['action']
        url = self.folder.absolute_url()
        self.failUnless(action.startswith(url))
        url = self.folder.doc1.absolute_url()
        self.failIf(action.startswith(url))

    def testNoAddableTypes(self):
        actions = self.menu.getMenuItems(self.portal, self.request)
        if self.is_dx:
            self.assertEqual(len(actions), 8)
        else:
            self.assertEqual(len(actions), 9)

        # set no types for folders and check the menu is not shown
        folder_fti = self.portal.portal_types['Folder']
        folder_fti.manage_changeProperties(
            filter_content_types=True, allowed_content_types=[])
        actions = self.menu.getMenuItems(self.folder, self.request)
        self.assertEqual(len(actions), 0)

    def testMenuForFolderishDefaultPages(self):
        self.portal.invokeFactory('Folder', 'folder1')
        self.portal.invokeFactory('Folder', 'folder2')
        self.portal.invokeFactory('Folder', 'folder3')
        self.portal.invokeFactory('Document', 'doc1')
        folder1 = self.portal['folder1']
        folder2 = self.portal['folder2']
        folder3 = self.portal['folder3']
        doc1 = self.portal['doc1']

        # test normal folder
        actions = self.menu.getMenuItems(folder1, self.request)
        self.assertEqual(
            'http://nohost/plone/folder1/folder_constraintypes_form',
            actions[-1]['action'])
        if self.is_dx:
            # DX has no Topics
            self.assertEqual(len(actions), 9)
            self.assertEqual(
                'http://nohost/plone/folder1/++add++Document',
                actions[-2]['action'])
        else:
            self.assertEqual(len(actions), 10)
            self.assertTrue(
                'http://nohost/plone/folder1/createObject?type_name=Document' in actions[-2]['action'])  # noqa

        # test non-folderish default_page
        self.portal.setDefaultPage('doc1')
        actions = self.menu.getMenuItems(doc1, self.request)
        if self.is_dx:
            self.assertEqual(
                'http://nohost/plone/++add++Document',
                actions[-1]['action'])
        else:
            self.assertTrue(
                'http://nohost/plone/createObject?type_name=Document' in actions[-1]['action'])  # noqa

        # test folderish default_page
        # We need to test a different folder than folder1 to beat memoize.
        self.portal.setDefaultPage('folder2')
        actions = self.menu.getMenuItems(folder2, self.request)
        self.assertEqual(
            'http://nohost/plone/folder2/@@folder_factories',
            actions[-1]['action'])

        # test folderish default_page to which no content can be added
        # set no types for folders and check the link to factories menu-item
        # is not shown
        folder_fti = self.portal.portal_types['Folder']
        folder_fti.manage_changeProperties(
            filter_content_types=True, allowed_content_types=[])
        self.portal.setDefaultPage('folder3')
        actions = self.menu.getMenuItems(folder3, self.request)
        if self.is_dx:
            self.assertEqual(
                'http://nohost/plone/++add++Document',
                actions[-1]['action'])
        else:
            self.assertTrue(
                'http://nohost/plone/createObject?type_name=Document' in actions[-1]['action'])  # noqa

    def testConstrainTypes(self):
        constraints = ISelectableConstrainTypes(self.folder)
        constraints.setConstrainTypesMode(1)
        constraints.setLocallyAllowedTypes(('Document',))
        constraints.setImmediatelyAddableTypes(('Document',))
        actions = self.menu.getMenuItems(self.folder, self.request)
        self.assertEqual(len(actions), 2)
        self.assertEqual(actions[0]['extra']['id'], 'document')
        self.assertEqual(
            actions[1]['extra']['id'], 'plone-contentmenu-settings'
        )

    def testSettingsIncluded(self):
        actions = self.menu.getMenuItems(self.folder, self.request)
        self.assertEqual(
            actions[-1]['extra']['id'], 'plone-contentmenu-settings'
        )

    def testSettingsNotIncludedWhereNotSupported(self):
        self.folder.manage_permission('Modify constrain types', ('Manager',))
        actions = self.menu.getMenuItems(self.folder, self.request)
        self.failIf('_settings' in [a['extra']['id'] for a in actions])

    def testMoreIncluded(self):
        constraints = ISelectableConstrainTypes(self.folder)
        constraints.setConstrainTypesMode(1)
        constraints.setLocallyAllowedTypes(('Document', 'Image',))
        constraints.setImmediatelyAddableTypes(('Document',))
        actions = self.menu.getMenuItems(self.folder, self.request)
        self.failIf('image' in [a['extra']['id'] for a in actions])
        self.failUnless('document' in [a['extra']['id'] for a in actions])
        self.failUnless(
            'plone-contentmenu-more' in [a['extra']['id'] for a in actions]
        )
        self.failUnless(
            'plone-contentmenu-settings' in [a['extra']['id'] for a in actions]
        )

    def testMoreNotIncludedWhenNotNecessary(self):
        constraints = ISelectableConstrainTypes(self.folder)
        constraints.setConstrainTypesMode(1)
        constraints.setLocallyAllowedTypes(('Document',))
        constraints.setImmediatelyAddableTypes(('Document',))
        actions = self.menu.getMenuItems(self.folder, self.request)
        self.assertEqual(len(actions), 2)
        self.assertEqual(actions[0]['extra']['id'], 'document')
        self.assertEqual(
            actions[1]['extra']['id'], 'plone-contentmenu-settings'
        )

    def testNonStructualFolderShowsParent(self):
        self.folder.invokeFactory('Folder', 'folder1')
        directlyProvides(self.folder.folder1, INonStructuralFolder)
        constraints = ISelectableConstrainTypes(self.folder.folder1)
        constraints.setConstrainTypesMode(1)
        constraints.setLocallyAllowedTypes(('Document',))
        constraints.setImmediatelyAddableTypes(('Document',))
        actions = self.menu.getMenuItems(self.folder.folder1, self.request)
        action_ids = [a['extra']['id'] for a in actions]
        self.failUnless('event' in action_ids)

    def testImgConditionalOnTypeIcon(self):
        """The <img> element should not render if the content type has
        no icon expression"""
        folder_fti = self.portal.portal_types['Folder']
        folder_fti.manage_changeProperties(icon_expr='')
        for item in self.menu.getMenuItems(self.folder, self.request):
            if item['id'] == folder_fti.getId():
                break
        self.failIf(item['icon'])


class TestFactoriesMenuDX(TestFactoriesMenuAT):
    layer = PLONE_APP_CONTENTMENU_DX_INTEGRATION_TESTING

    def setUp(self):
        super(TestFactoriesMenuDX, self).setUp()
        set_browserlayer(self.request)


class TestWorkflowMenuAT(unittest.TestCase):
    layer = PLONE_APP_CONTENTMENU_AT_INTEGRATION_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        self.portal.invokeFactory('Folder', 'folder')
        self.folder = self.portal['folder']
        self.folder.invokeFactory('Document', 'doc1')
        self.menu = getUtility(
            IBrowserMenu, name='plone_contentmenu_workflow',
            context=self.folder)
        self.request = self.layer['request']
        self.is_dx = self.folder.meta_type == 'Dexterity Container'

    def testMenuImplementsIBrowserMenu(self):
        self.failUnless(IBrowserMenu.providedBy(self.menu))

    def testMenuImplementsIActionsMenu(self):
        self.failUnless(IWorkflowMenu.providedBy(self.menu))

    def testMenuIncludesActions(self):
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.assertIn('workflow-transition-submit',
                      [a['extra']['id'] for a in actions])
        found = False
        for item in actions:
            if ('http://nohost/plone/folder/doc1/'
                    'content_status_modify?'
                    'workflow_action=submit') in item['action']:
                found = True
                break
        self.assertTrue(found)

        # Let us try that again but with an empty url action, like is
        # usual in older workflows, and which is nice to keep
        # supporting.
        context = self.folder.doc1
        wf_tool = getToolByName(context, "portal_workflow")
        submit = wf_tool.plone_workflow.transitions['submit']
        submit.actbox_url = ""
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.failUnless('workflow-transition-submit' in
                        [a['extra']['id'] for a in actions])
        found = False
        for item in actions:
            if ('http://nohost/plone/folder/doc1/'
                    'content_status_modify?'
                    'workflow_action=submit') in item['action']:
                found = True
                break
        self.assertTrue(found)

    def testNoTransitions(self):
        logout()
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.assertEqual(len(actions), 0)

    def testLockedItem(self):
        if self.is_dx:
            # dexterity has no locking ootb
            # see https://github.com/plone/plone.app.contenttypes/issues/140
            return
        membership_tool = getToolByName(self.folder, 'portal_membership')
        membership_tool.addMember('anotherMember', 'secret', ['Member'], [])
        locking = ILockable(self.folder.doc1)
        locking.lock()
        login(self.portal, 'anotherMember')
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.assertEqual(len(actions), 0)

    def testAdvancedIncluded(self):
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        url = self.folder.doc1.absolute_url() + '/content_status_history'
        self.assertIn(url, [a['action'] for a in actions])

    def testPolicyIncludedIfCMFPWIsInstalled(self):
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        url = self.folder.doc1.absolute_url()\
            + '/placeful_workflow_configuration'
        self.failIf(url in [a['action'] for a in actions])
        self.portal.portal_quickinstaller.installProduct('CMFPlacefulWorkflow')

        # item needs permission
        logout()
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.assertNotIn(url, [a['action'] for a in actions])
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.assertNotIn(url, [a['action'] for a in actions])


class TestWorkflowMenuDX(TestWorkflowMenuAT):
    layer = PLONE_APP_CONTENTMENU_DX_INTEGRATION_TESTING

    def setUp(self):
        super(TestWorkflowMenuDX, self).setUp()
        set_browserlayer(self.request)


class TestManagePortletsMenuAT(unittest.TestCase):
    layer = PLONE_APP_CONTENTMENU_AT_INTEGRATION_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        self.portal.invokeFactory('Folder', 'folder')
        self.folder = self.portal['folder']
        self.folder.invokeFactory('Document', 'doc1')
        self.menu = getUtility(
            IBrowserMenu, name='plone_contentmenu_portletmanager',
            context=self.folder)
        self.request = self.layer['request']
        self.is_dx = self.folder.meta_type == 'Dexterity Container'

    def testMenuImplementsIBrowserMenu(self):
        self.failUnless(IBrowserMenu.providedBy(self.menu))

    def testMenuImplementsIActionsMenu(self):
        self.failUnless(IPortletManagerMenu.providedBy(self.menu))

    def testMenuIncludesActions(self):
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.assertIn('portlet-manager-plone.leftcolumn',
                      [a['extra']['id'] for a in actions])
        self.assertIn(
            ('http://nohost/plone/folder/doc1'
             '/@@topbar-manage-portlets/plone.leftcolumn'),
            [a['action'] for a in actions][1]
        )

    def testNoTransitions(self):
        logout()
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.assertEqual(len(actions), 0)

    def testAdvancedIncluded(self):
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        base_url = self.folder.doc1.absolute_url()
        url_plone5 = '%s/@@topbar-manage-portlets/plone.leftcolumn' % base_url
        url_plone4 = '%s/manage-portlets' % base_url
        self.assertTrue(
            url_plone5 in [a['action'] for a in actions][1]
            or
            url_plone4 in [a['action'] for a in actions][1]
        )


class TestManagePortletsMenuDX(TestManagePortletsMenuAT):
    layer = PLONE_APP_CONTENTMENU_DX_INTEGRATION_TESTING

    def setUp(self):
        super(TestManagePortletsMenuDX, self).setUp()
        set_browserlayer(self.request)


class TestContentMenuAT(unittest.TestCase):
    layer = PLONE_APP_CONTENTMENU_AT_INTEGRATION_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        self.portal.invokeFactory('Folder', 'folder')
        self.folder = self.portal['folder']
        # self.folder.invokeFactory('Document', 'doc1')
        self.menu = getUtility(
            IBrowserMenu, name='plone_contentmenu', context=self.folder)
        self.request = self.layer['request']
        self.is_dx = self.folder.meta_type == 'Dexterity Container'

    # Actions sub-menu

    def testActionsSubMenuIncluded(self):
        items = self.menu.getMenuItems(self.folder, self.request)
        actionsMenuItem = [i for i in items if
                           i['extra']['id'] == 'plone-contentmenu-actions'][0]
        self.assertEqual(actionsMenuItem['action'],
                         self.folder.absolute_url() + '/folder_contents')
        self.failUnless(len(actionsMenuItem['submenu']) > 0)

    # Display sub-menu

    def testDisplayMenuIncluded(self):
        items = self.menu.getMenuItems(self.folder, self.request)
        displayMenuItem = [i for i in items if
                           i['extra']['id'] == 'plone-contentmenu-display'][0]
        self.assertEqual(displayMenuItem['action'],
                         self.folder.absolute_url() + '/select_default_view')
        self.failUnless(len(displayMenuItem['submenu']) > 0)

    def testDisplayMenuNotIncludedIfContextDoesNotSupportBrowserDefault(self):
        if self.is_dx:
            # DX has no ATListCriterion
            return
        # We need to create an object that does not have
        # IBrowserDefault enabled
        _createObjectByType('ATListCriterion', self.folder, 'c1')
        items = self.menu.getMenuItems(self.folder.c1, self.request)
        self.assertEqual([i for i in items if
                          i['extra']['id'] == 'plone-contentmenu-display'], [])

    def testWhenContextDoesNotSupportSelectableBrowserDefault(self):
        """Display Menu Show Folder Default Page When Context Does Not
        Support Selectable Browser Default"""
        if self.is_dx:
            # DX has no ATListCriterion
            return
        # We need to create an object that is not
        # ISelectableBrowserDefault aware
        _createObjectByType('ATListCriterion', self.folder, 'c1')
        self.folder.c1.setTitle('Foo')
        self.folder.setDefaultPage('c1')
        items = self.menu.getMenuItems(self.folder.c1, self.request)
        displayMenuItem = [i for i in items if
                           i['extra']['id'] == 'plone-contentmenu-display'][0]
        selected = [a for a in displayMenuItem['submenu']
                    if a['selected']][0]
        self.assertEqual(u'Foo', selected['title'].mapping['contentitem'])

    def testDisplayMenuNotIncludedIfNoActionsAvailable(self):
        self.folder.invokeFactory('Document', 'doc1')
        items = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.assertEqual([i for i in items if
                          i['extra']['id'] == 'plone-contentmenu-display'], [])

    def testDisplayMenuDisabledIfIndexHtmlInFolder(self):
        self.folder.invokeFactory('Document', 'index_html')
        items = self.menu.getMenuItems(self.folder, self.request)
        displayMenuItems = [i for i in items if
                            i['extra']['id'] == 'plone-contentmenu-display']
        self.assertEqual(len(displayMenuItems), 0)

    def testDisplayMenuDisabledIfIndexHtmlInFolderAndContextIsIndexHtml(self):
        self.folder.invokeFactory('Document', 'index_html')
        items = self.menu.getMenuItems(self.folder.index_html, self.request)
        displayMenuItems = [i for i in items if
                            i['extra']['id'] == 'plone-contentmenu-display']
        self.assertEqual(len(displayMenuItems), 0)

    def testDisplayMenuAddPrefixFolderForContainerPart(self):
        prefix = 'folder-'
        self.folder.invokeFactory('Folder', 'subfolder1')
        self.folder.setDefaultPage('subfolder1')
        items = self.menu.getMenuItems(self.folder.subfolder1, self.request)
        displayMenuItems = [i for i in items if
                            i['extra']['id'] == 'plone-contentmenu-display'][0]
        extras = [i['extra'] for i in displayMenuItems['submenu']]
        for extra in extras[1:]:
            if not extra['separator'] is None:
                break
            else:
                self.assertEqual(extra['id'][0:len(prefix)], prefix)

    # Add sub-menu

    def testAddMenuIncluded(self):
        items = self.menu.getMenuItems(self.folder, self.request)
        factoriesMenuItem = [
            i for i in items if
            i['extra']['id'] == 'plone-contentmenu-factories'][0]
        self.assertIn(self.folder.absolute_url() + '/folder_factories',
                      factoriesMenuItem['action'])
        self.failUnless(len(factoriesMenuItem['submenu']) > 0)

    def testAddMenuNotIncludedIfNothingToAdd(self):
        logout()
        items = self.menu.getMenuItems(self.folder, self.request)
        self.assertEqual(
            [i for i in items if
             i['extra']['id'] == 'plone-contentmenu-factories'], [])

    def testAddMenuWithNothingToAddButWithAvailableConstrainSettings(self):
        constraints = ISelectableConstrainTypes(self.folder)
        constraints.setConstrainTypesMode(1)
        constraints.setLocallyAllowedTypes(())
        constraints.setImmediatelyAddableTypes(())
        items = self.menu.getMenuItems(self.folder, self.request)
        factoriesMenuItem = [
            i for i in items if
            i['extra']['id'] == 'plone-contentmenu-factories'][0]
        self.assertEqual(len(factoriesMenuItem['submenu']), 1)
        self.assertEqual(factoriesMenuItem['submenu'][0]['extra']['id'],
                         'plone-contentmenu-settings')

    def testAddMenuWithNothingToAddButWithAvailableMorePage(self):
        constraints = ISelectableConstrainTypes(self.folder)
        constraints.setConstrainTypesMode(1)
        constraints.setLocallyAllowedTypes(('Document',))
        constraints.setImmediatelyAddableTypes(())
        self.folder.manage_permission('Modify constrain types', ('Manager',))
        setRoles(self.portal, TEST_USER_ID, ['Contributor'])
        items = self.menu.getMenuItems(self.folder, self.request)
        factoriesMenuItem = [
            i for i in items
            if i['extra']['id'] == 'plone-contentmenu-factories'
        ][0]
        self.assertEqual(len(factoriesMenuItem['submenu']), 1)
        self.assertEqual(factoriesMenuItem['submenu'][0]['extra']['id'],
                         'plone-contentmenu-more')

    def testAddMenuRelativeToNonStructuralFolder(self):
        self.folder.invokeFactory('Folder', 'f1')
        directlyProvides(self.folder.f1, INonStructuralFolder)
        items = self.menu.getMenuItems(self.folder.f1, self.request)
        factoriesMenuItem = [i for i in items if
                             i['extra']['id'] == 'plone-contentmenu-factories']
        self.failIf(factoriesMenuItem)

    def testAddMenuWithAddViewExpr(self):
        # we need a dummy to test this - should test that if the item does not
        # support constrain types and there is
        constraints = ISelectableConstrainTypes(self.folder)
        constraints.setConstrainTypesMode(1)
        constraints.setLocallyAllowedTypes(('Document',))
        constraints.setImmediatelyAddableTypes(('Document',))
        self.folder.manage_permission('Modify constrain types', ('Manager',))
        self.portal.portal_types['Document']._setPropValue(
            'add_view_expr', 'string:custom_expr')
        items = self.menu.getMenuItems(self.folder, self.request)
        factoriesMenuItem = [
            i for i in items if
            i['extra']['id'] == 'plone-contentmenu-factories'][0]
        self.assertEqual(factoriesMenuItem['submenu'][0]['action'],
                         'custom_expr')

    # Workflow sub-menu

    def testWorkflowMenuIncluded(self):
        items = self.menu.getMenuItems(self.folder, self.request)
        workflowMenuItem = [
            i for i in items if
            i['extra']['id'] == 'plone-contentmenu-workflow'][0]
        self.assertEqual(
            workflowMenuItem['action'],
            self.folder.absolute_url() + '/content_status_history')
        self.failUnless(len(workflowMenuItem['submenu']) > 0)

    def testWorkflowMenuWithNoTransitionsDisabled(self):
        logout()
        items = self.menu.getMenuItems(self.folder, self.request)
        workflowMenuItem = [
            i for i in items if
            i['extra']['id'] == 'plone-contentmenu-workflow'][0]
        self.assertEqual(workflowMenuItem['action'], '')

    @unittest.skip("Unable to write a proper test so far")
    def testWorkflowMenuWithNoTransitionsEnabledAsManager(self):
        # set workflow guard condition that fails, so there are no transitions.
        # then show that manager will get a drop-down with settings whilst
        # regular users won't

        self.portal.portal_workflow.doActionFor(self.folder, 'hide')
        wf = self.portal.portal_workflow['folder_workflow']
        wf.transitions['show'].guard.expr = Expression('python: False')
        wf.transitions['publish'].guard.expr = Expression('python: False')

        items = self.menu.getMenuItems(self.folder, self.request)
        workflowMenuItem = [
            i for i in items if
            i['extra']['id'] == 'plone-contentmenu-workflow'][0]

        # A regular user doesn't see any actions
        self.failUnless(workflowMenuItem['action'] == '')
        self.failUnless(workflowMenuItem['submenu'] is None)

        self.fail('Unable to write a proper test so far')

    def testWorkflowMenuWithNoWorkflowNotIncluded(self):
        self.portal.portal_workflow.setChainForPortalTypes(('Document',), ())
        self.folder.invokeFactory('Document', 'doc1')
        actions = self.menu.getMenuItems(self.folder.doc1, self.request)
        self.failIf('plone_contentmenu_workflow' in
                    [a['extra']['id'] for a in actions])


class TestContentMenuDX(TestContentMenuAT):
    layer = PLONE_APP_CONTENTMENU_DX_INTEGRATION_TESTING

    def setUp(self):
        super(TestContentMenuDX, self).setUp()
        set_browserlayer(self.request)


class TestDisplayViewsMenuDX(unittest.TestCase):
    layer = PLONE_APP_CONTENTMENU_DX_INTEGRATION_TESTING

    def setUp(self):
        self.portal = self.layer['portal']
        setRoles(self.portal, TEST_USER_ID, ['Manager'])
        self.portal.invokeFactory('Folder', 'folder')
        self.folder = self.portal['folder']
        self.request = self.layer['request']
        set_browserlayer(self.request)
        self.menu = getUtility(IBrowserMenu, 'plone_displayviews')

    def _getMenuItemByAction(self, action):
        context = self.folder
        request = self.request
        return self.menu.getMenuItemByAction(context, request, action)

    def testInterface(self):
        """A DisplayViewsMenu implements an extended interface"""
        from plone.app.contentmenu.interfaces import IDisplayViewsMenu
        self.assertTrue(IDisplayViewsMenu.providedBy(self.menu))

    def testSimpleAction(self):
        """Retrieve a registered IBrowserMenuItem"""
        item = self._getMenuItemByAction('summary_view')
        if item is None:
            # Pre Plone 5
            item = self._getMenuItemByAction('folder_summary_view')
        self.assertFalse(item is None)
        self.assertEqual(item.title, u'Summary view')

    def testViewAction(self):
        """Retrieve a registered IBrowserMenuItem"""
        item = self._getMenuItemByAction('listing_view')
        if item is None:
            # Pre Plone 5
            item = self._getMenuItemByAction('folder_listing')
        self.assertFalse(item is None)
        self.assertEqual(item.title, 'Standard view')
        item = self._getMenuItemByAction('@@listing_view')
        if item is None:
            # Pre Plone 5
            item = self._getMenuItemByAction('@@folder_listing')
        self.assertEqual(item.title, 'Standard view')
        item = self._getMenuItemByAction('++view++listing_view')
        if item is None:
            # Pre Plone 5
            item = self._getMenuItemByAction('++view++folder_listing')
        self.assertEqual(item.title, 'Standard view')

    def testNonExisting(self):
        """Attempt to retrieve a non-registered IBrowserMenuItem"""
        item = self._getMenuItemByAction('nonesuch.html')
        self.assertTrue(item is None)
