Skip to content

Commit

Permalink
Merge pull request #20 from 4teamwork/mle-add-attr-to-meta
Browse files Browse the repository at this point in the history
Add new options to meta.py since we need to support more usecases
  • Loading branch information
maethu committed Apr 4, 2019
2 parents b31db28 + a37483f commit 693ec4a
Show file tree
Hide file tree
Showing 13 changed files with 172 additions and 27 deletions.
17 changes: 17 additions & 0 deletions README.rst
Expand Up @@ -143,6 +143,23 @@ The next block will override the previous config.
<logo:logo base="custom_logo.svg" layer="your.product.interfaces.IYourProductLayer" />
It's also possible to define a pre scaled image for `logo`, `mobile` and `favicon`.

.. code-block:: xml
<logo:logo base="resources/min.svg"
logo="tests/fixtures/logo.png"
mobile="mobile.png"
primary_logo_scale="logo" />
<logo:icon base="icon.svg" favicon="favicon.ico" />
Please remember a base svg is required anyway. If you can't supply one, simply put in a transparent empty svg.
If you dont't have one you can use the one from this package, which is located in the resources folder. It's called min.svg. Also set the primary_logo_scale to "logo", since ftw.logo always prefers the svg over all other scales.


Logo View
=========

Expand Down
2 changes: 0 additions & 2 deletions development.cfg
Expand Up @@ -3,5 +3,3 @@ extends =
test-plone-4.3.x.cfg
https://raw.githubusercontent.com/4teamwork/ftw-buildouts/master/plone-development.cfg

[versions]
zc.buildout = 2.11.0
6 changes: 6 additions & 0 deletions ftw/logo/browser/logo_view.py
Expand Up @@ -115,6 +115,12 @@ def handle_get_logo(self):
return self.get_dx_overridden_image()
else:
self.scale = 'BASE'

config = getMultiAdapter(
(self.context, self.request), ILogo).get_config(self.config)
if config.primary_logo_scale:
self.scale = config.primary_logo_scale.upper()

return self.get_zcml_configured_image()

def has_dx_logo(self):
Expand Down
4 changes: 2 additions & 2 deletions ftw/logo/interfaces.py
Expand Up @@ -21,7 +21,7 @@ class IIconConfig(Interface):
"""Adapter interface for logo customization adapter.
"""

def __init__(file):
def __init__():
"""Adapts the zope application.
"""

Expand All @@ -30,6 +30,6 @@ class ILogoConfig(Interface):
"""Adapter interface for logo customization adapter.
"""

def __init__(file):
def __init__():
"""Adapts the zope application.
"""
62 changes: 49 additions & 13 deletions ftw/logo/logoconfig.py
Expand Up @@ -3,44 +3,77 @@
from ftw.logo.image import Image
from ftw.logo.interfaces import IIconConfig
from ftw.logo.interfaces import ILogoConfig
from hashlib import sha256
from textwrap import wrap
from zope.configuration.exceptions import ConfigurationError
from zope.interface import implements
from hashlib import sha256

def get_cachekey_from_blob(blob):

def get_cachekey_from_blob(*args):
cachekey = sha256()
for chunk in wrap(blob, 4096):
cachekey.update(chunk)
for blob in args:
if blob:
for chunk in wrap(blob, 4096):
cachekey.update(chunk)
return cachekey.hexdigest()


class AbstractConfig(object):

def __init__(self, base):
base_img = Image(filename=base)
self.cachekey = get_cachekey_from_blob(base_img.make_blob())
base = None
logo = None
mobile = None
favicon = None
primary_logo_scale = None

def __init__(self, **kwargs):
if 'base' not in kwargs:
raise ConfigurationError('A base svg is required')

self.base = Image(filename=kwargs['base'])

if 'logo' in kwargs:
self.logo = Image(filename=kwargs['logo'])
if 'mobile' in kwargs:
self.mobile = Image(filename=kwargs['mobile'])
if 'favicon' in kwargs:
self.favicon = Image(filename=kwargs['favicon'])

self.cachekey = get_cachekey_from_blob(
self.base.make_blob(),
self.logo and self.logo.make_blob() or None,
self.mobile and self.mobile.make_blob() or None,
self.favicon and self.favicon.make_blob() or None,)
self.scales = {}
self.collect_scales(base_img)
self.collect_scales()
self.set_primary_logo_scale(**kwargs)

def add_scale(self, name, scale):
self.scales[name] = scale

def collect_scales(self, base_img):
def collect_scales(self):
raise NotImplemented() # pragma: no cover

def get_scale(self, name):
return self.scales[name]

def set_primary_logo_scale(self, **kwargs):
if 'primary_logo_scale' in kwargs:
self.primary_logo_scale = kwargs['primary_logo_scale']


class LogoConfig(AbstractConfig):
"""Logo config entry.
"""

implements(ILogoConfig)

def collect_scales(self, base_img):
def collect_scales(self):
for scale in SCALES['LOGOS']:
self.add_scale(scale, convert(base_img, scale))
if getattr(self, scale.lower(), None):
self.add_scale(scale, convert(getattr(self, scale.lower()), scale))
else:
self.add_scale(scale, convert(self.base, scale))


class IconConfig(AbstractConfig):
Expand All @@ -49,9 +82,12 @@ class IconConfig(AbstractConfig):

implements(IIconConfig)

def collect_scales(self, base_img):
def collect_scales(self):
for scale in SCALES['ICONS']:
self.add_scale(scale, convert(base_img, scale))
if getattr(self, scale.lower(), None):
self.add_scale(scale, convert(getattr(self, scale.lower()), scale))
else:
self.add_scale(scale, convert(self.base, scale))


class AbstractConfigOverride(AbstractConfig):
Expand Down
23 changes: 20 additions & 3 deletions ftw/logo/meta.py
Expand Up @@ -2,6 +2,7 @@
from ftw.logo.interfaces import ILogoConfig
from ftw.logo.logoconfig import IconConfig
from ftw.logo.logoconfig import LogoConfig
from zope import schema
from zope.component.zcml import handler
from zope.configuration import fields
from zope.configuration.fields import GlobalInterface
Expand All @@ -19,9 +20,21 @@ class ILogoDirective(Interface):
required=False)

base = fields.Path(
title=u'Relative path to the logo file.',
title=u'Relative path to the logo svg file.',
required=True)

logo = fields.Path(
title=u'Relative path to the logo image file.',
required=False)

mobile = fields.Path(
title=u'Relative path to the mobile logo image file.',
required=False)

primary_logo_scale = schema.Text(
title=u'Relative path to the mobile logo image file.',
required=False)


class IIconDirective(Interface):

Expand All @@ -37,9 +50,13 @@ class IIconDirective(Interface):
title=u'Relative path to the icon file.',
required=True)

favicon = fields.Path(
title=u'Relative path to the favicon file.',
required=False)


def registerLogo(_context, **kwargs):
component = LogoConfig(kwargs['base'])
component = LogoConfig(**kwargs)

def adapter_factory(context, request):
return component
Expand All @@ -54,7 +71,7 @@ def adapter_factory(context, request):


def registerIcon(_context, **kwargs):
component = IconConfig(kwargs['base'])
component = IconConfig(**kwargs)

def adapter_factory(context, request):
return component
Expand Down
7 changes: 7 additions & 0 deletions ftw/logo/resources/min.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ftw/logo/tests/fixtures/logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ftw/logo/tests/fixtures/mobile.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 17 additions & 2 deletions ftw/logo/tests/test_collection.py
Expand Up @@ -5,6 +5,7 @@


source = os.path.join(os.path.dirname(__file__), 'fixtures/logo.svg')
img_source = os.path.join(os.path.dirname(__file__), 'fixtures/logo.png')


class TestCollecter(TestCase):
Expand All @@ -16,15 +17,15 @@ def assertImage(self, img, width, height, format=None):
self.assertEqual(img['format'].lower(), format.lower())

def test_all_logos_are_collected(self):
component = LogoConfig(source)
component = LogoConfig(base=source)

self.assertEqual(len(component.scales), 3, 'Should store three scales')
self.assertImage(component.get_scale('BASE'), 1, 1, 'svg')
self.assertImage(component.get_scale('LOGO'), 80, 80, 'png')
self.assertImage(component.get_scale('MOBILE_LOGO'), 50, 50, 'png')

def test_all_icons_are_collected(self):
component = IconConfig(source)
component = IconConfig(base=source)

self.assertEqual(len(component.scales), 8, 'Should store eight scales')
self.assertImage(component.get_scale('BASE'), 1, 1, 'svg')
Expand All @@ -35,3 +36,17 @@ def test_all_icons_are_collected(self):
self.assertImage(component.get_scale('ANDROID_192X192'), 192, 192, 'png')
self.assertImage(component.get_scale('ANDROID_512X512'), 512, 512, 'png')
self.assertImage(component.get_scale('FAVICON'), 16, 16, 'ico')

def test_add_specific_logo_as_config(self):
component = LogoConfig(base=source, logo=img_source)

self.assertEqual(len(component.scales), 3, 'Should store one scales')
self.assertImage(component.get_scale('LOGO'), 762, 80, 'png')

self.assertImage(component.get_scale('BASE'), 1, 1, 'svg')
self.assertImage(component.get_scale('MOBILE_LOGO'), 50, 50, 'png')

def test_primary_logo_scale(self):
component = LogoConfig(base=source, logo=img_source,
primary_logo_scale='logo')
self.assertEquals('logo', component.primary_logo_scale)
27 changes: 26 additions & 1 deletion ftw/logo/tests/test_logo_view.py
@@ -1,13 +1,21 @@
from ftw.logo.testing import get_etag_value_for
from ftw.logo.tests import FunctionalTestCase
from ftw.testbrowser import browsing
import os
from wand.exceptions import CorruptImageError
from wand.image import Image
from zope.interface import alsoProvides
from zope.interface import Interface
import os


source_path = os.path.join(os.path.dirname(__file__), 'fixtures')
custom = os.path.join(source_path, 'custom.svg')
png = os.path.join(source_path, 'green.png')
logo = os.path.join(source_path, 'logo.png')


class IDummyLayer(Interface):
pass


class TestLogoView(FunctionalTestCase):
Expand Down Expand Up @@ -93,3 +101,20 @@ def test_special_get_logo_scale_name_returns_uploaded_svg(self, browser):
with open(png) as green_png:
browser.fill({'Standard (desktop) logo (PNG)': green_png}).submit()
self.verify_image_format(browser, '@@logo/logo/get_logo', 'png')

def test_primary_logo_scale(self):
self.layer['load_zcml_string']('''
<configure
xmlns:logo="https://namespaces.4teamwork.ch/ftw.logo"
i18n_domain="my.package"
package="ftw.logo.tests">
<logo:logo base="{}" primary_logo_scale="logo" />
</configure>
'''.format(custom))

# Simulate publishTraverse of '@@logo/logo/get_logo'
view = self.portal.restrictedTraverse('@@logo')
view.publishTraverse(view.request, u"logo")
view.publishTraverse(view.request, u"get_logo")
view()
self.assertEquals('LOGO', view.scale)
6 changes: 2 additions & 4 deletions ftw/logo/tests/test_manual_overrides.py
Expand Up @@ -5,15 +5,13 @@
from ftw.logo.testing import get_etag_value_for
from ftw.logo.tests import FunctionalTestCase
from ftw.testbrowser import browsing
import os
from plone.app.layout.navigation.interfaces import INavigationRoot
from Products.CMFCore.utils import getToolByName
import transaction
from wand.color import Color
from wand.exceptions import CorruptImageError
from wand.image import Image
from zope.annotation.interfaces import IAnnotations
from zope.interface import alsoProvides
import os


GREEN = '#0f0'

Expand Down
26 changes: 26 additions & 0 deletions ftw/logo/tests/test_zcml.py
Expand Up @@ -3,6 +3,7 @@
from ftw.logo.testing import META_ZCML
from unittest2 import TestCase
from zope.component import getMultiAdapter
from zope.configuration.xmlconfig import ZopeXMLConfigurationError
from zope.interface import implementer
from zope.interface import Interface
from zope.interface.verify import verifyObject
Expand All @@ -14,6 +15,9 @@
icon = os.path.join(source_path, 'logo.svg')
custom = os.path.join(source_path, 'custom.svg')

logo_img = os.path.join(source_path, 'logo.png')
mobile_img = os.path.join(source_path, 'mobile.png')


class IDummyLayer(Interface):
pass
Expand All @@ -32,6 +36,15 @@ def test_logo_component(self):
registry = getMultiAdapter((None, None), ILogoConfig)
verifyObject(ILogoConfig, registry)

def test_logo_component_with_other_image_attr(self):
self.load_zcml('<logo:logo base="{}" logo="{}" />'.format(logo, logo_img))
registry = getMultiAdapter((None, None), ILogoConfig)
verifyObject(ILogoConfig, registry)

self.load_zcml('<logo:logo base="{}" mobile="{}" />'.format(logo, mobile_img))
registry = getMultiAdapter((None, None), ILogoConfig)
verifyObject(ILogoConfig, registry)

def test_logo_component_is_overwriteable(self):
self.load_zcml(
'<logo:logo base="{}" />'.format(logo),
Expand Down Expand Up @@ -63,3 +76,16 @@ def load_zcml(self, *lines):
) + lines + (
'</configure>',
)))

def test_fail_to_load_if_no_valid_attr_is_present(self):
with self.assertRaises(ZopeXMLConfigurationError):
self.load_zcml('<logo:logo />'.format(logo))

with self.assertRaises(ZopeXMLConfigurationError):
self.load_zcml('<logo:logo dummy="nothing" />'.format(logo))

with self.assertRaises(ZopeXMLConfigurationError):
self.load_zcml('<logo:icon />'.format(logo))

with self.assertRaises(ZopeXMLConfigurationError):
self.load_zcml('<logo:icon dummy="nothing" />'.format(logo))

0 comments on commit 693ec4a

Please sign in to comment.