diff --git a/docs/ZMI.rst b/docs/ZMI.rst index 9531d5577f..a27b5a256d 100644 --- a/docs/ZMI.rst +++ b/docs/ZMI.rst @@ -76,3 +76,23 @@ Example taken from `cmf.zmiicons`_: .. _`cmf.zmiicons` : https://github.com/zopefoundation/cmf.zmiicons + + +Use custom resources via ZMI +++++++++++++++++++++++++++++ + +To add custom CSS or JavaScript resources via ZMI you have to add a property: + +* ``zmi_additional_css_paths`` for additional CSS +* ``zmi_additional_css_paths`` for additional JavaScript + +The properties can have one of the following types: + +* ``string`` +* ``ustring`` +* ``lines`` +* ``ulines`` + +The value of the properties have to be paths or URLs to CSS resp. JavaScript +which will be included in the HTML of the ZMI. (Paths have to be resolvable by +the browser aka not simple file system paths.) diff --git a/src/App/Management.py b/src/App/Management.py index 628b03037a..b63fe8068f 100644 --- a/src/App/Management.py +++ b/src/App/Management.py @@ -26,6 +26,7 @@ from zExceptions import Redirect from zope.interface import implementer import itertools +import six import zope.event try: @@ -163,9 +164,11 @@ class Navigation(Base): def manage_page_header(self, *args, **kw): """manage_page_header.""" kw['css_urls'] = itertools.chain( - *zope.component.subscribers((self,), ICSSPaths)) + itertools.chain(*zope.component.subscribers((self,), ICSSPaths)), + self._get_zmi_additionals('zmi_additional_css_paths')) kw['js_urls'] = itertools.chain( - *zope.component.subscribers((self,), IJSPaths)) + itertools.chain(*zope.component.subscribers((self,), IJSPaths)), + self._get_zmi_additionals('zmi_additional_js_paths')) return self._manage_page_header(*args, **kw) security.declarePublic('manage_zmi_logout') @@ -188,6 +191,13 @@ def manage_zmi_logout(self, REQUEST, RESPONSE): """) return + def _get_zmi_additionals(self, attrib): + # Get additional assets for styling ZMI defined on properties in ZMI. + additionals = getattr(self, attrib, ()) or () + if isinstance(additionals, six.string_types): + additionals = (additionals, ) + return additionals + # Navigation doesn't have an inherited __class_init__ so doesn't get # initialized automatically. diff --git a/src/App/tests/testManagement.py b/src/App/tests/testManagement.py index ee1ae90496..ee5b7429de 100644 --- a/src/App/tests/testManagement.py +++ b/src/App/tests/testManagement.py @@ -1,7 +1,7 @@ -import unittest +import Testing.ZopeTestCase -class TestNavigation(unittest.TestCase): +class TestNavigation(Testing.ZopeTestCase.ZopeTestCase): def test_interfaces(self): from App.interfaces import INavigation @@ -9,3 +9,35 @@ def test_interfaces(self): from zope.interface.verify import verifyClass verifyClass(INavigation, Navigation) + + def test_Management__Navigation__manage_page_header__1(self): + """It respects `zmi_additional_css_paths` string property.""" + self.folder.manage_addProperty( + 'zmi_additional_css_paths', '/foo/bar.css', 'string') + self.assertIn('href="/foo/bar.css"', self.folder.manage_page_header()) + + def test_Management__Navigation__manage_page_header__2(self): + """It respects `zmi_additional_css_paths` ustring property.""" + self.folder.manage_addProperty( + 'zmi_additional_css_paths', '/foo/bar.css', 'ustring') + self.assertIn('href="/foo/bar.css"', self.folder.manage_page_header()) + + def test_Management__Navigation__manage_page_header__3(self): + """It respects `zmi_additional_css_paths` lines property.""" + self.folder.manage_addProperty( + 'zmi_additional_css_paths', ['/foo/bar.css', '/baz.css'], 'lines') + self.assertIn('href="/foo/bar.css"', self.folder.manage_page_header()) + self.assertIn('href="/baz.css"', self.folder.manage_page_header()) + + def test_Management__Navigation__manage_page_header__4(self): + """It respects `zmi_additional_css_paths` ulines property.""" + self.folder.manage_addProperty( + 'zmi_additional_css_paths', ['/foo/bar.css', '/baz.css'], 'lines') + self.assertIn('href="/foo/bar.css"', self.folder.manage_page_header()) + self.assertIn('href="/baz.css"', self.folder.manage_page_header()) + + def test_Management__Navigation__manage_page_header__5(self): + """It ignores an empty `zmi_additional_css_paths` property.""" + self.folder.manage_addProperty( + 'zmi_additional_css_paths', '', 'string') + self.assertNotIn('href=""', self.folder.manage_page_header())