Skip to content

Commit

Permalink
Add control panel configlet to define global configuration options fo…
Browse files Browse the repository at this point in the history
…r the Cycle2 slideshow plugin (closes #3)
  • Loading branch information
hvelarde committed May 25, 2015
1 parent f5942ef commit 0a969a0
Show file tree
Hide file tree
Showing 13 changed files with 269 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ There's a frood who really knows where his towel is.
1.0a2 (unreleased)
------------------

- Add control panel configlet to define global options for the Cycle2 slideshow plugin (closes `#3`_).
[hvelarde]

- Remove browser layer and JS resource registry registrations.
Include all Cycle2 and plugins code as browser resources (closes `#1`_).
[rodfersou]
Expand All @@ -17,3 +20,4 @@ There's a frood who really knows where his towel is.
- Initial release.

.. _`#1`: https://github.com/collective/collective.js.cycle2/issues/1
.. _`#3`: https://github.com/collective/collective.js.cycle2/issues/3
8 changes: 8 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,25 @@
zip_safe=False,
install_requires=[
'plone.api',
'plone.app.registry',
'plone.directives.form',
'Products.CMFPlone >=4.2',
'Products.GenericSetup',
'setuptools',
'zope.i18nmessageid',
'zope.interface',
'zope.schema',
],
extras_require={
'test': [
'AccessControl',
'plone.app.robotframework',
'plone.app.testing [robot] >=4.2.2',
'plone.browserlayer',
'plone.registry',
'plone.testing',
'robotsuite',
'zope.component',
],
},
entry_points="""
Expand Down
11 changes: 11 additions & 0 deletions src/collective/js/cycle2/Extensions/Install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
from collective.js.cycle2.config import PROJECTNAME
from plone import api


def uninstall(portal, reinstall=False):
if not reinstall:
profile = 'profile-%s:uninstall' % PROJECTNAME
setup_tool = api.portal.get_tool('portal_setup')
setup_tool.runAllImportStepsFromProfile(profile)
return 'Ran all uninstall steps.'
1 change: 1 addition & 0 deletions src/collective/js/cycle2/Extensions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
3 changes: 3 additions & 0 deletions src/collective/js/cycle2/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
# -*- coding: utf-8 -*-
from zope.i18nmessageid import MessageFactory

_ = MessageFactory('collective.js.cycle2')
2 changes: 2 additions & 0 deletions src/collective/js/cycle2/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
PROJECTNAME = 'collective.js.cycle2'
18 changes: 18 additions & 0 deletions src/collective/js/cycle2/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

<five:registerPackage package="." />

<include package="plone.directives.form" file="meta.zcml" />
<include package="plone.directives.form" />

<genericsetup:registerProfile
name="default"
title="collective.js.cycle2"
Expand All @@ -15,6 +18,21 @@
provides="Products.GenericSetup.interfaces.EXTENSION"
/>

<genericsetup:registerProfile
name="uninstall"
title="collective.js.cycle2"
directory="profiles/uninstall"
description="Uninstall profile for the collective.js.cycle2 package."
provides="Products.GenericSetup.interfaces.EXTENSION"
/>

<browser:page
name="cycle2-settings"
for="Products.CMFPlone.interfaces.IPloneSiteRoot"
class=".controlpanel.Cycle2SettingsControlPanel"
permission="cmf.ManagePortal"
/>

<browser:resourceDirectory name="collective.js.cycle2" directory="resources" />

<include package=".upgrades" />
Expand Down
88 changes: 88 additions & 0 deletions src/collective/js/cycle2/controlpanel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-
from collective.js.cycle2 import _
from plone.app.registry.browser import controlpanel
from plone.directives import form
from zope import schema
from zope.schema.vocabulary import SimpleTerm
from zope.schema.vocabulary import SimpleVocabulary

fx = ['fade', 'fadeout', 'none', 'scrollHorz']
fx = SimpleVocabulary([SimpleTerm(f) for f in fx])

# TODO: get this from portal_properties/imaging_properties
scales = ['large', 'preview', 'mini', 'thumb', 'tile', 'icon', 'listing']
scales = SimpleVocabulary([SimpleTerm(s) for s in scales])


class ICycle2Settings(form.Schema):

"""Interface for the control panel form."""

paused = schema.Bool(
title=_(u'Paused'),
description=_(u'If selected the slideshow will begin in a paused state.'),
default=True,
)

allow_wrap = schema.Bool(
title=_(u'Allow wrap'),
description=_(u'Determines whether or not a slideshow can advance from the last slide to the first (or vice versa).'),
default=True,
)

caption_template = schema.TextLine(
title=_(u'Caption Template'),
description=_(
u'A template string which defines how the slideshow caption should be formatted. '
u'',
),
default=u'{{slideNum}} / {{slideCount}}',
)

pager_template = schema.TextLine(
title=_(u'Pager Template'),
description=_(
u'A template string which defines how the pager links should be formatted. '
u'',
),
default=u'<span>&bull;</span>',
)

speed = schema.Int(
title=_(u'Speed'),
description=_(u'The speed of the transition effect in milliseconds.'),
default=500,
)

timeout = schema.Int(
title=_(u'Timeout'),
description=_(u'The time between slide transitions in milliseconds.'),
default=4000,
)

transition_effect = schema.Choice(
title=_(u'Transition effect'),
description=_(u'The name of the slideshow transition to use.'),
required=True,
vocabulary=fx,
default='fade',
)

default_image_size = schema.Choice(
title=_(u'Default Image Size'),
description=_(u''),
required=True,
vocabulary=scales,
default='preview',
)


class Cycle2SettingsEditForm(controlpanel.RegistryEditForm):
schema = ICycle2Settings
label = _(u'Cycle2')
description = _(
u'Here you can define global configuration options for the Cycle2 slideshow plugin.')


class Cycle2SettingsControlPanel(controlpanel.ControlPanelFormWrapper):
form = Cycle2SettingsEditForm
17 changes: 17 additions & 0 deletions src/collective/js/cycle2/profiles/default/controlpanel.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0"?>
<object name="portal_controlpanel"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
i18n:domain="collective.js.cycle2"
purge="False">
<configlet title="Cycle2"
action_id="cycle2"
appId="collective.js.cycle2"
category="Products"
condition_expr=""
url_expr="string:${portal_url}/@@cycle2-settings"
icon_expr="string:${portal_url}/++resource++collective.js.cycle2/cycle2-icon.png"
visible="True"
i18n:attributes="title">
<permission>Manage portal</permission>
</configlet>
</object>
4 changes: 4 additions & 0 deletions src/collective/js/cycle2/profiles/default/registry.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<registry>
<records interface="collective.js.cycle2.controlpanel.ICycle2Settings" />
</registry>
4 changes: 4 additions & 0 deletions src/collective/js/cycle2/profiles/uninstall/registry.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<registry>
<records interface="collective.js.cycle2.controlpanel.ICycle2Settings" remove="true" />
</registry>
108 changes: 108 additions & 0 deletions src/collective/js/cycle2/tests/test_controlpanel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# -*- coding: utf-8 -*-
from collective.js.cycle2.config import PROJECTNAME
from collective.js.cycle2.controlpanel import ICycle2Settings
from collective.js.cycle2.testing import INTEGRATION_TESTING
from plone import api
from plone.app.testing import logout
from plone.registry.interfaces import IRegistry
from zope.component import getUtility

import unittest


class ControlPanelTestCase(unittest.TestCase):

layer = INTEGRATION_TESTING

def setUp(self):
self.portal = self.layer['portal']
self.controlpanel = self.portal['portal_controlpanel']

def test_controlpanel_has_view(self):
request = self.layer['request']
view = api.content.get_view(u'cycle2-settings', self.portal, request)
view = view.__of__(self.portal)
self.assertTrue(view())

def test_controlpanel_view_is_protected(self):
from AccessControl import Unauthorized
logout()
with self.assertRaises(Unauthorized):
self.portal.restrictedTraverse('@@cycle2-settings')

def test_controlpanel_installed(self):
actions = [a.getAction(self)['id']
for a in self.controlpanel.listActions()]
self.assertIn('cycle2', actions, 'control panel not installed')

def test_controlpanel_removed_on_uninstall(self):
qi = self.portal['portal_quickinstaller']

with api.env.adopt_roles(['Manager']):
qi.uninstallProducts(products=[PROJECTNAME])

actions = [a.getAction(self)['id']
for a in self.controlpanel.listActions()]
self.assertNotIn('cycle2', actions, 'control panel not removed')


class RegistryTestCase(unittest.TestCase):

layer = INTEGRATION_TESTING

def setUp(self):
self.portal = self.layer['portal']
self.registry = getUtility(IRegistry)
self.settings = self.registry.forInterface(ICycle2Settings)

def test_paused_record_in_registry(self):
self.assertTrue(hasattr(self.settings, 'paused'))
self.assertEqual(self.settings.paused, True)

def test_allow_wrap_record_in_registry(self):
self.assertTrue(hasattr(self.settings, 'allow_wrap'))
self.assertEqual(self.settings.allow_wrap, True)

def test_caption_template_record_in_registry(self):
self.assertTrue(hasattr(self.settings, 'caption_template'))
self.assertEqual(self.settings.caption_template, u'{{slideNum}} / {{slideCount}}')

def test_pager_template_record_in_registry(self):
self.assertTrue(hasattr(self.settings, 'pager_template'))
self.assertEqual(self.settings.pager_template, u'<span>&bull;</span>')

def test_speed_record_in_registry(self):
self.assertTrue(hasattr(self.settings, 'speed'))
self.assertEqual(self.settings.speed, 500)

def test_timeout_record_in_registry(self):
self.assertTrue(hasattr(self.settings, 'timeout'))
self.assertEqual(self.settings.timeout, 4000)

def test_transition_effect_record_in_registry(self):
self.assertTrue(hasattr(self.settings, 'transition_effect'))
self.assertEqual(self.settings.transition_effect, 'fade')

def test_default_image_size_record_in_registry(self):
self.assertTrue(hasattr(self.settings, 'default_image_size'))
self.assertEqual(self.settings.default_image_size, 'preview')

def test_records_removed_on_uninstall(self):
qi = self.portal['portal_quickinstaller']

with api.env.adopt_roles(['Manager']):
qi.uninstallProducts(products=[PROJECTNAME])

records = [
ICycle2Settings.__identifier__ + '.paused',
ICycle2Settings.__identifier__ + '.allow_wrap',
ICycle2Settings.__identifier__ + '.caption_template',
ICycle2Settings.__identifier__ + '.pager_template',
ICycle2Settings.__identifier__ + '.speed',
ICycle2Settings.__identifier__ + '.timeout',
ICycle2Settings.__identifier__ + '.transition_effect',
ICycle2Settings.__identifier__ + '.default_image_size',
]

for r in records:
self.assertNotIn(r, self.registry)
3 changes: 1 addition & 2 deletions src/collective/js/cycle2/tests/test_setup.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
from collective.js.cycle2.config import PROJECTNAME
from collective.js.cycle2.testing import INTEGRATION_TESTING

import unittest

PROJECTNAME = 'collective.js.cycle2'


class InstallTestCase(unittest.TestCase):

Expand Down

0 comments on commit 0a969a0

Please sign in to comment.