From 1b430824c67b38fdd20eb4f762f022daa9797d1f Mon Sep 17 00:00:00 2001 From: Rodrigo Ferreira de Souza Date: Thu, 18 Aug 2016 18:02:32 -0300 Subject: [PATCH 1/8] Remove dependency of portal_calendar tool --- CHANGES.rst | 3 + src/collective/cover/testing.py | 2 +- .../cover/tests/test_calendar_tile.py | 101 +++++++++++++++-- src/collective/cover/tests/test_upgrades.py | 2 - .../cover/tests/test_vocabularies.py | 9 -- src/collective/cover/tiles/calendar.py | 106 +++++++++++------- src/collective/cover/vocabularies.py | 9 -- 7 files changed, 161 insertions(+), 71 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index b72fa6722..bcfb868e9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,9 @@ There's a frood who really knows where his towel is. All Layout tab helper views use now ``collective.cover.CanEditLayout`` permission. The ``BaseGrid`` class is now located in the ``collective.cover.grids`` module. +- Remove dependency on portal_calendar tool for tile calendar. + [rodfersou] + - Remove dependency on plone.directives.form. [l34marr] diff --git a/src/collective/cover/testing.py b/src/collective/cover/testing.py index 05d36af13..2687147d5 100644 --- a/src/collective/cover/testing.py +++ b/src/collective/cover/testing.py @@ -169,7 +169,7 @@ def setUpPloneSite(self, portal): portal_workflow = portal.portal_workflow portal_workflow.setChainForPortalTypes( - ['Collection'], ['simple_publication_workflow']) + ['Event', 'Collection'], ['simple_publication_workflow']) # Prevent kss validation errors in Plone 4.2 portal_kss = getattr(portal, 'portal_kss', None) diff --git a/src/collective/cover/tests/test_calendar_tile.py b/src/collective/cover/tests/test_calendar_tile.py index 8e5c32af3..a0bc1b977 100644 --- a/src/collective/cover/tests/test_calendar_tile.py +++ b/src/collective/cover/tests/test_calendar_tile.py @@ -2,6 +2,8 @@ from collective.cover.tests.base import TestTileMixin from collective.cover.tiles.calendar import CalendarTile from collective.cover.tiles.calendar import ICalendarTile +from datetime import datetime +from plone import api import unittest @@ -10,10 +12,28 @@ class CalendarTileTestCase(TestTileMixin, unittest.TestCase): def setUp(self): super(CalendarTileTestCase, self).setUp() + + # Pin calendar to month 08/2016 + self.request['year'] = 2016 + self.request['month'] = 8 + self.tile = CalendarTile(self.cover, self.request) self.tile.__name__ = u'collective.cover.calendar' self.tile.id = u'test' + # Pin today as day 18 + self.tile.isToday = lambda day: day == 18 + + # create some events + with api.env.adopt_roles(['Manager']): + for i in [3, 12, 21, 30]: + start = datetime(2016, 8, i, 10, 30) + end = datetime(2016, 8, i, 11, 30) + obj = api.content.create( + container=self.portal, type='Event', id='e{0}'.format(i), + startDate=start, endDate=end, start=start, end=end) + api.content.transition(obj, 'publish') + @unittest.expectedFailure # FIXME: raises BrokenImplementation def test_interface(self): self.interface = ICalendarTile @@ -28,12 +48,79 @@ def test_default_configuration(self): def test_accepted_content_types(self): self.assertEqual(self.tile.accepted_ct(), []) + def test_getEventsForCalendar(self): + self.assertEqual(self.tile.getEventsForCalendar(), EXPECTED_CALENDAR) + + def test_getYearAndMonthToDisplay(self): + self.assertEqual(self.tile.getYearAndMonthToDisplay(), (2016, 8)) + + def test_getWeekdays(self): + expected = [ + u'weekday_mon_short', u'weekday_tue_short', u'weekday_wed_short', + u'weekday_thu_short', u'weekday_fri_short', u'weekday_sat_short', + u'weekday_sun_short'] + self.assertEqual(self.tile.getWeekdays(), expected) + + def test_getReviewStateString(self): + self.assertEqual(self.tile.getReviewStateString(), 'review_state=published&') + + def test_getEventTypes(self): + self.assertEqual(self.tile.getEventTypes(), 'Type=Event&') -# load tests only in Plone < 5 -def test_suite(): - # FIXME: https://github.com/collective/collective.cover/issues/633 - from collective.cover.config import IS_PLONE_5 - if IS_PLONE_5: - return unittest.TestSuite() - return unittest.defaultTestLoader.loadTestsFromName(__name__) +EXPECTED_CALENDAR = [ + [{'day': 1, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 2, 'event': 0, 'eventslist': [], 'is_today': False}, + {'date_string': '2016-8-3', + 'day': 3, + 'event': 1, + 'eventslist': [{'end': '11:30:00', 'start': '10:30:00', 'title': 'e3'}], + 'eventstring': u'Aug 03, 2016\n 10:30-11:30 e3', + 'is_today': False}, + {'day': 4, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 5, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 6, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 7, 'event': 0, 'eventslist': [], 'is_today': False}], + [{'day': 8, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 9, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 10, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 11, 'event': 0, 'eventslist': [], 'is_today': False}, + {'date_string': '2016-8-12', + 'day': 12, + 'event': 1, + 'eventslist': [{'end': '11:30:00', 'start': '10:30:00', 'title': 'e12'}], + 'eventstring': u'Aug 12, 2016\n 10:30-11:30 e12', + 'is_today': False}, + {'day': 13, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 14, 'event': 0, 'eventslist': [], 'is_today': False}], + [{'day': 15, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 16, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 17, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 18, 'event': 0, 'eventslist': [], 'is_today': True}, + {'day': 19, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 20, 'event': 0, 'eventslist': [], 'is_today': False}, + {'date_string': '2016-8-21', + 'day': 21, + 'event': 1, + 'eventslist': [{'end': '11:30:00', 'start': '10:30:00', 'title': 'e21'}], + 'eventstring': u'Aug 21, 2016\n 10:30-11:30 e21', + 'is_today': False}], + [{'day': 22, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 23, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 24, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 25, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 26, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 27, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 28, 'event': 0, 'eventslist': [], 'is_today': False}], + [{'day': 29, 'event': 0, 'eventslist': [], 'is_today': False}, + {'date_string': '2016-8-30', + 'day': 30, + 'event': 1, + 'eventslist': [{'end': '11:30:00', 'start': '10:30:00', 'title': 'e30'}], + 'eventstring': u'Aug 30, 2016\n 10:30-11:30 e30', + 'is_today': False}, + {'day': 31, 'event': 0, 'eventslist': [], 'is_today': False}, + {'day': 0, 'event': 0, 'eventslist': []}, + {'day': 0, 'event': 0, 'eventslist': []}, + {'day': 0, 'event': 0, 'eventslist': []}, + {'day': 0, 'event': 0, 'eventslist': []}]] diff --git a/src/collective/cover/tests/test_upgrades.py b/src/collective/cover/tests/test_upgrades.py index 4f96c199a..9420d13bd 100644 --- a/src/collective/cover/tests/test_upgrades.py +++ b/src/collective/cover/tests/test_upgrades.py @@ -524,8 +524,6 @@ def test_registrations(self): self.assertGreaterEqual(int(version), int(self.to_version)) self.assertEqual(self._how_many_upgrades_to_do(), 4) - # FIXME: https://github.com/collective/collective.cover/issues/633 - @unittest.skipIf(IS_PLONE_5, 'Upgrade step not supported under Plone 5') def test_register_calendar_tile(self): # address also an issue with Setup permission title = u'Register calendar tile' diff --git a/src/collective/cover/tests/test_vocabularies.py b/src/collective/cover/tests/test_vocabularies.py index 75256ec19..894d796db 100644 --- a/src/collective/cover/tests/test_vocabularies.py +++ b/src/collective/cover/tests/test_vocabularies.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from collective.cover.config import IS_PLONE_5 from collective.cover.controlpanel import ICoverSettings from collective.cover.testing import INTEGRATION_TESTING from plone.registry.interfaces import IRegistry @@ -46,10 +45,6 @@ def test_available_tiles_vocabulary(self): 'collective.cover.richtext', ] - # FIXME: https://github.com/collective/collective.cover/issues/633 - if IS_PLONE_5: - expected.remove('collective.cover.calendar') - self.assertEqual(len(tiles), len(expected)) for i in expected: self.assertIn(i, tiles) @@ -73,10 +68,6 @@ def test_enabled_tiles_vocabulary(self): 'collective.cover.richtext', ] - # FIXME: https://github.com/collective/collective.cover/issues/633 - if IS_PLONE_5: - expected.remove('collective.cover.calendar') - self.assertEqual(len(tiles), len(expected)) for i in expected: self.assertIn(i, tiles) diff --git a/src/collective/cover/tiles/calendar.py b/src/collective/cover/tiles/calendar.py index e0eca847c..841f10b5a 100644 --- a/src/collective/cover/tiles/calendar.py +++ b/src/collective/cover/tiles/calendar.py @@ -1,20 +1,28 @@ # -*- coding: utf-8 -*- +# Avoid to import wrong calendar module +# http://stackoverflow.com/a/8280677/2116850 +from __future__ import absolute_import from Acquisition import aq_inner from collective.cover import _ from collective.cover.tiles.base import IPersistentCoverTile from collective.cover.tiles.base import PersistentCoverTile from DateTime import DateTime +from plone import api +from plone.api.exc import InvalidParameterError from Products.CMFCore.utils import getToolByName from Products.CMFPlone.utils import safe_unicode from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from time import localtime from urllib import quote_plus -from zope.component import getMultiAdapter from zope.i18nmessageid import MessageFactory from zope.interface import implementer +import calendar + PLMF = MessageFactory('plonelocales') +EVENT_INTERFACES = [ + 'plone.event.interfaces.IEvent', 'Products.ATContentTypes.interfaces.event.IATEvent'] class ICalendarTile(IPersistentCoverTile): @@ -39,7 +47,6 @@ def __init__(self, context, request): def _setup(self): context = aq_inner(self.context) - self.calendar = getToolByName(context, 'portal_calendar') self._ts = getToolByName(context, 'translation_service') self.url_quote_plus = quote_plus @@ -56,31 +63,57 @@ def _setup(self): self.monthName = PLMF(self._ts.month_msgid(month), default=self._ts.month_english(month)) + self._set_first_weekday() + + def _set_first_weekday(self): + try: # Plone 5.x + first_weekday = api.portal.get_registry_record('plone.first_weekday') + except InvalidParameterError: + try: # plone.app.event + first_weekday = api.portal.get_registry_record('plone.app.event.first_weekday') + except InvalidParameterError: # Plone 4.x + portal_calendar = api.portal.get_tool('portal_calendar') + first_weekday = getattr(portal_calendar, 'firstweekday', calendar.SUNDAY) + calendar.setfirstweekday(first_weekday) def accepted_ct(self): """Return an empty list as no content types are accepted.""" return [] - def getEventsForCalendar(self): - context = aq_inner(self.context) - year = self.year - month = self.month - portal_state = getMultiAdapter((self.context, self.request), name='plone_portal_state') - navigation_root_path = portal_state.navigation_root_path() - weeks = self.calendar.getEventsForCalendar(month, year, path=navigation_root_path) - for week in weeks: - for day in week: - daynumber = day['day'] - if daynumber == 0: - continue - day['is_today'] = self.isToday(daynumber) - if day['event']: - cur_date = DateTime(year, month, daynumber) - localized_date = [self._ts.ulocalized_time(cur_date, context=context, request=self.request)] - day['eventstring'] = '\n'.join(localized_date + [ - ' {0}'.format(self.getEventString(e)) for e in day['eventslist']]) - day['date_string'] = '{0}-{1}-{2}'.format(year, month, daynumber) + def addEvents(self, day): + catalog = api.portal.get_tool('portal_catalog') + start = DateTime(self.year, self.month, day['day']) + end = start + 1 + query = dict( + object_provides=EVENT_INTERFACES, review_state='published', + start={'query': (start, end), 'range': 'min:max'}, sort_on='start') + for brain in catalog(**query): + day['event'] += 1 + day['date_string'] = '{0}-{1}-{2}'.format(self.year, self.month, day['day']) + if 'eventslist' not in day: + day['eventslist'] = [] + event = dict( + start=brain.start.Time(), end=brain.end.Time(), title=brain.Title or brain.id) + day['eventslist'].append(event) + if 'eventstring' not in day: + localized_date = self._ts.ulocalized_time( + start, context=self.context, request=self.request) + day['eventstring'] = localized_date + day['eventstring'] += '\n {0}'.format(self.getEventString(event)) + return day + def getEventsForCalendar(self): + monthcalendar = calendar.monthcalendar(self.year, self.month) + weeks = [] + for w in monthcalendar: + week = [] + for d in w: + day = {'day': d, 'event': 0, 'eventslist': []} + if d > 0: + day['is_today'] = self.isToday(d) + day = self.addEvents(day) + week.append(day) + weeks.append(week) return weeks def getEventString(self, event): @@ -100,22 +133,12 @@ def getEventString(self, event): return eventstring def getYearAndMonthToDisplay(self): - session = None request = self.request # First priority goes to the data in the REQUEST year = request.get('year', None) month = request.get('month', None) - # Next get the data from the SESSION - if self.calendar.getUseSession(): - session = request.get('SESSION', None) - if session: - if not year: - year = session.get('calendar_year', None) - if not month: - month = session.get('calendar_month', None) - # Last resort to today if not year: year = self.now[0] @@ -129,11 +152,6 @@ def getYearAndMonthToDisplay(self): except (TypeError, ValueError): year, month = self.now[:2] - # Store the results in the session for next time - if session: - session.set('calendar_year', year) - session.set('calendar_month', month) - # Finally return the results return year, month @@ -153,12 +171,12 @@ def getNextMonth(self, year, month): def getWeekdays(self): """Returns a list of Messages for the weekday names.""" - weekdays = [] - # list of ordered weekdays as numbers - for day in self.calendar.getDayNumbers(): - weekdays.append(PLMF(self._ts.day_msgid(day, format='s'), - default=self._ts.weekday_english(day, format='a'))) + weekheaders = calendar.weekheader(3).split() + weekdays = [] + for header in weekheaders: + weekdays.append(PLMF('weekday_{0}_short'.format(header.lower()), + default=header)) return weekdays def isToday(self, day): @@ -169,11 +187,13 @@ def isToday(self, day): self.now[2] == day and self.now[1] == self.month and self.now[0] == self.year) def getReviewStateString(self): - states = self.calendar.getCalendarStates() + states = ['published'] return ''.join(map(lambda x: 'review_state={0}&'.format(self.url_quote_plus(x)), states)) def getEventTypes(self): - types = self.calendar.getCalendarTypes() + catalog = api.portal.get_tool('portal_catalog') + query = dict(object_provides=EVENT_INTERFACES) + types = set([b.portal_type for b in catalog(**query)]) return ''.join(map(lambda x: 'Type={0}&'.format(self.url_quote_plus(x)), types)) def getQueryString(self): diff --git a/src/collective/cover/vocabularies.py b/src/collective/cover/vocabularies.py index 69126afd9..cb2e43899 100644 --- a/src/collective/cover/vocabularies.py +++ b/src/collective/cover/vocabularies.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from collective.cover.config import IS_PLONE_5 from collective.cover.controlpanel import ICoverSettings from collective.cover.interfaces import IGridSystem from collective.cover.tiles.base import IPersistentCoverTile @@ -36,10 +35,6 @@ def __call__(self, context): settings = registry.forInterface(ICoverSettings) tiles = settings.available_tiles - # FIXME: https://github.com/collective/collective.cover/issues/633 - if IS_PLONE_5 and 'collective.cover.calendar' in tiles: - tiles.remove('collective.cover.calendar') - items = [SimpleTerm(value=i, title=i) for i in tiles] return SimpleVocabulary(items) @@ -59,10 +54,6 @@ class EnabledTilesVocabulary(object): """Return a list of tiles ready to work with collective.cover.""" def _enabled(self, name): - # FIXME: https://github.com/collective/collective.cover/issues/633 - if IS_PLONE_5 and name == 'collective.cover.calendar': - return False - tile_type = queryUtility(ITileType, name) if tile_type: return issubclass(tile_type.schema, IPersistentCoverTile) From 81c34934af52fe5262db13db33eec4467eea94a0 Mon Sep 17 00:00:00 2001 From: Rodrigo Ferreira de Souza Date: Mon, 22 Aug 2016 09:18:16 -0300 Subject: [PATCH 2/8] Review events catalog and add cache --- src/collective/cover/tiles/calendar.py | 52 +++++++++++++++++--------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/src/collective/cover/tiles/calendar.py b/src/collective/cover/tiles/calendar.py index 841f10b5a..d600fcdda 100644 --- a/src/collective/cover/tiles/calendar.py +++ b/src/collective/cover/tiles/calendar.py @@ -9,6 +9,7 @@ from DateTime import DateTime from plone import api from plone.api.exc import InvalidParameterError +from plone.memoize import ram from Products.CMFCore.utils import getToolByName from Products.CMFPlone.utils import safe_unicode from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile @@ -25,6 +26,12 @@ 'plone.event.interfaces.IEvent', 'Products.ATContentTypes.interfaces.event.IATEvent'] +def _catalog_counter_cachekey(method, self): + """Return a cachekey based on catalog updates.""" + catalog = api.portal.get_tool('portal_catalog') + return str(catalog.getCounter()) + + class ICalendarTile(IPersistentCoverTile): pass @@ -80,29 +87,25 @@ def accepted_ct(self): """Return an empty list as no content types are accepted.""" return [] - def addEvents(self, day): + @ram.cache(_catalog_counter_cachekey) + def getEvents(self): catalog = api.portal.get_tool('portal_catalog') - start = DateTime(self.year, self.month, day['day']) - end = start + 1 + _, last_day = calendar.monthrange(self.year, self.month) + start = DateTime(self.year, self.month, 1) + end = DateTime(self.year, self.month, last_day, 23, 59) query = dict( object_provides=EVENT_INTERFACES, review_state='published', start={'query': (start, end), 'range': 'min:max'}, sort_on='start') + events = {} for brain in catalog(**query): - day['event'] += 1 - day['date_string'] = '{0}-{1}-{2}'.format(self.year, self.month, day['day']) - if 'eventslist' not in day: - day['eventslist'] = [] - event = dict( - start=brain.start.Time(), end=brain.end.Time(), title=brain.Title or brain.id) - day['eventslist'].append(event) - if 'eventstring' not in day: - localized_date = self._ts.ulocalized_time( - start, context=self.context, request=self.request) - day['eventstring'] = localized_date - day['eventstring'] += '\n {0}'.format(self.getEventString(event)) - return day + day = brain.start.day() + if day not in events: + events[day] = [] + events[day].append(brain) + return events def getEventsForCalendar(self): + events = self.getEvents() monthcalendar = calendar.monthcalendar(self.year, self.month) weeks = [] for w in monthcalendar: @@ -111,7 +114,20 @@ def getEventsForCalendar(self): day = {'day': d, 'event': 0, 'eventslist': []} if d > 0: day['is_today'] = self.isToday(d) - day = self.addEvents(day) + day_events = [] + if d in events: + day_events = events[d] + day['date_string'] = '{0}-{1}-{2}'.format(self.year, self.month, d) + day['event'] += len(day_events) + day['eventslist'] = [] + localized_date = self._ts.ulocalized_time( + day_events[0].start, context=self.context, request=self.request) + day['eventstring'] = localized_date + for e in day_events: + event = dict( + start=e.start.Time(), end=e.end.Time(), title=e.Title or e.id) + day['eventslist'].append(event) + day['eventstring'] += '\n {0}'.format(self.getEventString(event)) week.append(day) weeks.append(week) return weeks @@ -133,6 +149,7 @@ def getEventString(self, event): return eventstring def getYearAndMonthToDisplay(self): + """Return calendar year and month.""" request = self.request # First priority goes to the data in the REQUEST @@ -190,6 +207,7 @@ def getReviewStateString(self): states = ['published'] return ''.join(map(lambda x: 'review_state={0}&'.format(self.url_quote_plus(x)), states)) + @ram.cache(_catalog_counter_cachekey) def getEventTypes(self): catalog = api.portal.get_tool('portal_catalog') query = dict(object_provides=EVENT_INTERFACES) From 93fd0655c7e549d2df21a584a810bba0bb8f6888 Mon Sep 17 00:00:00 2001 From: Rodrigo Ferreira de Souza Date: Mon, 22 Aug 2016 09:29:14 -0300 Subject: [PATCH 3/8] Add docstrings --- src/collective/cover/tiles/calendar.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/collective/cover/tiles/calendar.py b/src/collective/cover/tiles/calendar.py index d600fcdda..2e1367bb7 100644 --- a/src/collective/cover/tiles/calendar.py +++ b/src/collective/cover/tiles/calendar.py @@ -73,6 +73,7 @@ def _setup(self): self._set_first_weekday() def _set_first_weekday(self): + """Set calendar library first weekday for calendar.""" try: # Plone 5.x first_weekday = api.portal.get_registry_record('plone.first_weekday') except InvalidParameterError: @@ -89,6 +90,7 @@ def accepted_ct(self): @ram.cache(_catalog_counter_cachekey) def getEvents(self): + """Return all events in the selected month.""" catalog = api.portal.get_tool('portal_catalog') _, last_day = calendar.monthrange(self.year, self.month) start = DateTime(self.year, self.month, 1) @@ -105,6 +107,7 @@ def getEvents(self): return events def getEventsForCalendar(self): + """Return the month data filled with events.""" events = self.getEvents() monthcalendar = calendar.monthcalendar(self.year, self.month) weeks = [] @@ -133,6 +136,7 @@ def getEventsForCalendar(self): return weeks def getEventString(self, event): + """Get event string to make event list hint on mouse over.""" start = event['start'] and ':'.join(event['start'].split(':')[:2]) or '' end = event['end'] and ':'.join(event['end'].split(':')[:2]) or '' title = safe_unicode(event['title']) or u'event' @@ -173,6 +177,7 @@ def getYearAndMonthToDisplay(self): return year, month def getPreviousMonth(self, year, month): + """Get previous month.""" if month == 0 or month == 1: month, year = 12, year - 1 else: @@ -180,6 +185,7 @@ def getPreviousMonth(self, year, month): return (year, month) def getNextMonth(self, year, month): + """Get next month.""" if month == 12: month, year = 1, year + 1 else: @@ -204,17 +210,20 @@ def isToday(self, day): self.now[2] == day and self.now[1] == self.month and self.now[0] == self.year) def getReviewStateString(self): + """Get Review State String.""" states = ['published'] return ''.join(map(lambda x: 'review_state={0}&'.format(self.url_quote_plus(x)), states)) @ram.cache(_catalog_counter_cachekey) def getEventTypes(self): + """Get a list of events portal types.""" catalog = api.portal.get_tool('portal_catalog') query = dict(object_provides=EVENT_INTERFACES) types = set([b.portal_type for b in catalog(**query)]) return ''.join(map(lambda x: 'Type={0}&'.format(self.url_quote_plus(x)), types)) def getQueryString(self): + """Get current url get parameters.""" request = self.request query_string = request.get('orig_query', request.get('QUERY_STRING', None)) From 5f047246c8e9e4cdc3791600fc90aa6a76bf739b Mon Sep 17 00:00:00 2001 From: Rodrigo Ferreira de Souza Date: Mon, 22 Aug 2016 10:50:50 -0300 Subject: [PATCH 4/8] Fix tests --- .../cover/tests/test_calendar_tile.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/collective/cover/tests/test_calendar_tile.py b/src/collective/cover/tests/test_calendar_tile.py index a0bc1b977..f8ea4559d 100644 --- a/src/collective/cover/tests/test_calendar_tile.py +++ b/src/collective/cover/tests/test_calendar_tile.py @@ -4,10 +4,16 @@ from collective.cover.tiles.calendar import ICalendarTile from datetime import datetime from plone import api +from plone.api.exc import InvalidParameterError +from tzlocal import get_localzone +import calendar import unittest +TZNAME = get_localzone().zone + + class CalendarTileTestCase(TestTileMixin, unittest.TestCase): def setUp(self): @@ -17,6 +23,17 @@ def setUp(self): self.request['year'] = 2016 self.request['month'] = 8 + # Pin first weekday to monday + try: # Plone 5.x + api.portal.set_registry_record('plone.first_weekday', calendar.MONDAY) + except InvalidParameterError: + try: # plone.app.event + api.portal.set_registry_record('plone.app.event.first_weekday', calendar.MONDAY) + except InvalidParameterError: # Plone 4.x + portal_calendar = api.portal.get_tool('portal_calendar') + portal_calendar.firstweekday = calendar.MONDAY + + # Create tile self.tile = CalendarTile(self.cover, self.request) self.tile.__name__ = u'collective.cover.calendar' self.tile.id = u'test' @@ -31,7 +48,7 @@ def setUp(self): end = datetime(2016, 8, i, 11, 30) obj = api.content.create( container=self.portal, type='Event', id='e{0}'.format(i), - startDate=start, endDate=end, start=start, end=end) + startDate=start, endDate=end, start=start, end=end, timezone=TZNAME) api.content.transition(obj, 'publish') @unittest.expectedFailure # FIXME: raises BrokenImplementation From 86c4b3d8490b4493b6a0f57190330da2d1114a63 Mon Sep 17 00:00:00 2001 From: hvelarde Date: Mon, 22 Aug 2016 18:57:58 -0300 Subject: [PATCH 5/8] Refactor Code simplification by using the calendar portlet as a helper view; we need to generalize this approach. --- CHANGES.rst | 4 +- src/collective/cover/testing.py | 4 +- .../cover/tests/test_calendar_tile.py | 26 +- src/collective/cover/tiles/calendar.py | 248 +++--------------- src/collective/cover/tiles/tiles.zcml | 2 +- 5 files changed, 55 insertions(+), 229 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index bcfb868e9..1190600c4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,8 +13,8 @@ There's a frood who really knows where his towel is. All Layout tab helper views use now ``collective.cover.CanEditLayout`` permission. The ``BaseGrid`` class is now located in the ``collective.cover.grids`` module. -- Remove dependency on portal_calendar tool for tile calendar. - [rodfersou] +- Remove dependency on ``portal_calendar`` tool for calendar tile. + [rodfersou, hvelarde] - Remove dependency on plone.directives.form. [l34marr] diff --git a/src/collective/cover/testing.py b/src/collective/cover/testing.py index 2687147d5..97a819ae5 100644 --- a/src/collective/cover/testing.py +++ b/src/collective/cover/testing.py @@ -167,9 +167,7 @@ def setUpPloneSite(self, portal): set_image_field(portal['my-image2'], generate_jpeg(50, 50)) set_image_field(portal['my-news-item'], generate_jpeg(50, 50)) - portal_workflow = portal.portal_workflow - portal_workflow.setChainForPortalTypes( - ['Event', 'Collection'], ['simple_publication_workflow']) + portal.portal_workflow.setDefaultChain('simple_publication_workflow') # Prevent kss validation errors in Plone 4.2 portal_kss = getattr(portal, 'portal_kss', None) diff --git a/src/collective/cover/tests/test_calendar_tile.py b/src/collective/cover/tests/test_calendar_tile.py index f8ea4559d..673d99e12 100644 --- a/src/collective/cover/tests/test_calendar_tile.py +++ b/src/collective/cover/tests/test_calendar_tile.py @@ -4,10 +4,8 @@ from collective.cover.tiles.calendar import ICalendarTile from datetime import datetime from plone import api -from plone.api.exc import InvalidParameterError from tzlocal import get_localzone -import calendar import unittest @@ -23,16 +21,6 @@ def setUp(self): self.request['year'] = 2016 self.request['month'] = 8 - # Pin first weekday to monday - try: # Plone 5.x - api.portal.set_registry_record('plone.first_weekday', calendar.MONDAY) - except InvalidParameterError: - try: # plone.app.event - api.portal.set_registry_record('plone.app.event.first_weekday', calendar.MONDAY) - except InvalidParameterError: # Plone 4.x - portal_calendar = api.portal.get_tool('portal_calendar') - portal_calendar.firstweekday = calendar.MONDAY - # Create tile self.tile = CalendarTile(self.cover, self.request) self.tile.__name__ = u'collective.cover.calendar' @@ -47,8 +35,15 @@ def setUp(self): start = datetime(2016, 8, i, 10, 30) end = datetime(2016, 8, i, 11, 30) obj = api.content.create( - container=self.portal, type='Event', id='e{0}'.format(i), - startDate=start, endDate=end, start=start, end=end, timezone=TZNAME) + container=self.portal, + type='Event', + id='e{0}'.format(i), + startDate=start, # Archetypes + endDate=end, + start=start, # Dexterity + end=end, + timezone=TZNAME, + ) api.content.transition(obj, 'publish') @unittest.expectedFailure # FIXME: raises BrokenImplementation @@ -79,7 +74,8 @@ def test_getWeekdays(self): self.assertEqual(self.tile.getWeekdays(), expected) def test_getReviewStateString(self): - self.assertEqual(self.tile.getReviewStateString(), 'review_state=published&') + self.assertEqual( + self.tile.getReviewStateString(), 'review_state=published&') def test_getEventTypes(self): self.assertEqual(self.tile.getEventTypes(), 'Type=Event&') diff --git a/src/collective/cover/tiles/calendar.py b/src/collective/cover/tiles/calendar.py index 2e1367bb7..3a2f37730 100644 --- a/src/collective/cover/tiles/calendar.py +++ b/src/collective/cover/tiles/calendar.py @@ -1,234 +1,66 @@ # -*- coding: utf-8 -*- -# Avoid to import wrong calendar module -# http://stackoverflow.com/a/8280677/2116850 -from __future__ import absolute_import -from Acquisition import aq_inner from collective.cover import _ from collective.cover.tiles.base import IPersistentCoverTile from collective.cover.tiles.base import PersistentCoverTile -from DateTime import DateTime -from plone import api -from plone.api.exc import InvalidParameterError -from plone.memoize import ram -from Products.CMFCore.utils import getToolByName -from Products.CMFPlone.utils import safe_unicode from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile -from time import localtime -from urllib import quote_plus -from zope.i18nmessageid import MessageFactory from zope.interface import implementer -import calendar - - -PLMF = MessageFactory('plonelocales') -EVENT_INTERFACES = [ - 'plone.event.interfaces.IEvent', 'Products.ATContentTypes.interfaces.event.IATEvent'] - - -def _catalog_counter_cachekey(method, self): - """Return a cachekey based on catalog updates.""" - catalog = api.portal.get_tool('portal_catalog') - return str(catalog.getCounter()) +try: # plone.app.event + from plone.app.event.portlets.portlet_calendar import Assignment + from plone.app.event.portlets.portlet_calendar import Renderer +except ImportError: # Plone 4.x + from plone.app.portlets.portlets.calendar import Assignment + from plone.app.portlets.portlets.calendar import Renderer class ICalendarTile(IPersistentCoverTile): - pass + + """A tile that shows a calendar of published events.""" @implementer(ICalendarTile) class CalendarTile(PersistentCoverTile): - """Calendar Tile code is coppied from plone.app.portlet Portlet Calendar + + """A tile that shows a calendar of published events. + + The implementation is a wraper of the calendar portlet. """ index = ViewPageTemplateFile('templates/calendar.pt') - is_configurable = False is_editable = False is_droppable = False short_name = _(u'msg_short_name_calendar', default=u'Calendar') - def __init__(self, context, request): - super(CalendarTile, self).__init__(context, request) - self._setup() - - def _setup(self): - context = aq_inner(self.context) - self._ts = getToolByName(context, 'translation_service') - self.url_quote_plus = quote_plus - - self.now = localtime() - self.yearmonth = yearmonth = self.getYearAndMonthToDisplay() - self.year = year = yearmonth[0] - self.month = month = yearmonth[1] - - self.showPrevMonth = yearmonth > (self.now[0] - 1, self.now[1]) - self.showNextMonth = yearmonth < (self.now[0] + 1, self.now[1]) - - self.prevMonthYear, self.prevMonthMonth = self.getPreviousMonth(year, month) - self.nextMonthYear, self.nextMonthMonth = self.getNextMonth(year, month) - - self.monthName = PLMF(self._ts.month_msgid(month), - default=self._ts.month_english(month)) - self._set_first_weekday() - - def _set_first_weekday(self): - """Set calendar library first weekday for calendar.""" - try: # Plone 5.x - first_weekday = api.portal.get_registry_record('plone.first_weekday') - except InvalidParameterError: - try: # plone.app.event - first_weekday = api.portal.get_registry_record('plone.app.event.first_weekday') - except InvalidParameterError: # Plone 4.x - portal_calendar = api.portal.get_tool('portal_calendar') - first_weekday = getattr(portal_calendar, 'firstweekday', calendar.SUNDAY) - calendar.setfirstweekday(first_weekday) + def __call__(self, *args, **kwargs): + self.setup() + return super(CalendarTile, self).__call__(*args, **kwargs) + + def setup(self): + helper = Renderer(self.context, self.request, None, None, Assignment()) + helper.update() + + self._ts = helper._ts + self.url_quote_plus = helper.url_quote_plus + + self.now = helper.now + self.year = year = helper.year + self.month = month = helper.month + + self.showPrevMonth = helper.showPrevMonth + self.showNextMonth = helper.showNextMonth + + self.prevMonthYear, self.prevMonthMonth = helper.getPreviousMonth(year, month) + self.nextMonthYear, self.nextMonthMonth = helper.getNextMonth(year, month) + + self.monthName = helper.monthName + + self.getQueryString = helper.getQueryString + self.getWeekdays = helper.getWeekdays + self.getEventsForCalendar = helper.getEventsForCalendar + self.getReviewStateString = helper.getReviewStateString + self.getEventTypes = helper.getEventTypes def accepted_ct(self): """Return an empty list as no content types are accepted.""" return [] - - @ram.cache(_catalog_counter_cachekey) - def getEvents(self): - """Return all events in the selected month.""" - catalog = api.portal.get_tool('portal_catalog') - _, last_day = calendar.monthrange(self.year, self.month) - start = DateTime(self.year, self.month, 1) - end = DateTime(self.year, self.month, last_day, 23, 59) - query = dict( - object_provides=EVENT_INTERFACES, review_state='published', - start={'query': (start, end), 'range': 'min:max'}, sort_on='start') - events = {} - for brain in catalog(**query): - day = brain.start.day() - if day not in events: - events[day] = [] - events[day].append(brain) - return events - - def getEventsForCalendar(self): - """Return the month data filled with events.""" - events = self.getEvents() - monthcalendar = calendar.monthcalendar(self.year, self.month) - weeks = [] - for w in monthcalendar: - week = [] - for d in w: - day = {'day': d, 'event': 0, 'eventslist': []} - if d > 0: - day['is_today'] = self.isToday(d) - day_events = [] - if d in events: - day_events = events[d] - day['date_string'] = '{0}-{1}-{2}'.format(self.year, self.month, d) - day['event'] += len(day_events) - day['eventslist'] = [] - localized_date = self._ts.ulocalized_time( - day_events[0].start, context=self.context, request=self.request) - day['eventstring'] = localized_date - for e in day_events: - event = dict( - start=e.start.Time(), end=e.end.Time(), title=e.Title or e.id) - day['eventslist'].append(event) - day['eventstring'] += '\n {0}'.format(self.getEventString(event)) - week.append(day) - weeks.append(week) - return weeks - - def getEventString(self, event): - """Get event string to make event list hint on mouse over.""" - start = event['start'] and ':'.join(event['start'].split(':')[:2]) or '' - end = event['end'] and ':'.join(event['end'].split(':')[:2]) or '' - title = safe_unicode(event['title']) or u'event' - - if start and end: - eventstring = '{0}-{1} {2}'.format(start, end, title) - elif start: # can assume not event['end'] - eventstring = '{0} - {1}'.format(start, title) - elif event['end']: # can assume not event['start'] - eventstring = '{0} - {1}'.format(title, end) - else: # can assume not event['start'] and not event['end'] - eventstring = title - - return eventstring - - def getYearAndMonthToDisplay(self): - """Return calendar year and month.""" - request = self.request - - # First priority goes to the data in the REQUEST - year = request.get('year', None) - month = request.get('month', None) - - # Last resort to today - if not year: - year = self.now[0] - if not month: - month = self.now[1] - - # try to transform to number but fall back to current - # date if this is ambiguous - try: - year, month = int(year), int(month) - except (TypeError, ValueError): - year, month = self.now[:2] - - # Finally return the results - return year, month - - def getPreviousMonth(self, year, month): - """Get previous month.""" - if month == 0 or month == 1: - month, year = 12, year - 1 - else: - month -= 1 - return (year, month) - - def getNextMonth(self, year, month): - """Get next month.""" - if month == 12: - month, year = 1, year + 1 - else: - month += 1 - return (year, month) - - def getWeekdays(self): - """Returns a list of Messages for the weekday names.""" - - weekheaders = calendar.weekheader(3).split() - weekdays = [] - for header in weekheaders: - weekdays.append(PLMF('weekday_{0}_short'.format(header.lower()), - default=header)) - return weekdays - - def isToday(self, day): - """Returns True if the given day and the current month and year equals - today, otherwise False. - """ - return ( - self.now[2] == day and self.now[1] == self.month and self.now[0] == self.year) - - def getReviewStateString(self): - """Get Review State String.""" - states = ['published'] - return ''.join(map(lambda x: 'review_state={0}&'.format(self.url_quote_plus(x)), states)) - - @ram.cache(_catalog_counter_cachekey) - def getEventTypes(self): - """Get a list of events portal types.""" - catalog = api.portal.get_tool('portal_catalog') - query = dict(object_provides=EVENT_INTERFACES) - types = set([b.portal_type for b in catalog(**query)]) - return ''.join(map(lambda x: 'Type={0}&'.format(self.url_quote_plus(x)), types)) - - def getQueryString(self): - """Get current url get parameters.""" - request = self.request - query_string = request.get('orig_query', - request.get('QUERY_STRING', None)) - if len(query_string) == 0: - query_string = '' - else: - query_string = '{0}&'.format(query_string) - return query_string diff --git a/src/collective/cover/tiles/tiles.zcml b/src/collective/cover/tiles/tiles.zcml index b3105e297..d3e3fa7e0 100644 --- a/src/collective/cover/tiles/tiles.zcml +++ b/src/collective/cover/tiles/tiles.zcml @@ -129,7 +129,7 @@ Date: Tue, 23 Aug 2016 10:54:24 -0300 Subject: [PATCH 6/8] Fix tests --- src/collective/cover/testing.py | 4 +- .../cover/tests/test_calendar_tile.py | 111 ------------------ 2 files changed, 2 insertions(+), 113 deletions(-) diff --git a/src/collective/cover/testing.py b/src/collective/cover/testing.py index 97a819ae5..0d9910d6e 100644 --- a/src/collective/cover/testing.py +++ b/src/collective/cover/testing.py @@ -166,8 +166,8 @@ def setUpPloneSite(self, portal): set_image_field(portal['my-image1'], generate_jpeg(50, 50)) set_image_field(portal['my-image2'], generate_jpeg(50, 50)) set_image_field(portal['my-news-item'], generate_jpeg(50, 50)) - - portal.portal_workflow.setDefaultChain('simple_publication_workflow') + portal_workflow = portal.portal_workflow + portal_workflow.setChainForPortalTypes(['Collection'], ['simple_publication_workflow']) # Prevent kss validation errors in Plone 4.2 portal_kss = getattr(portal, 'portal_kss', None) diff --git a/src/collective/cover/tests/test_calendar_tile.py b/src/collective/cover/tests/test_calendar_tile.py index 673d99e12..1c5874be3 100644 --- a/src/collective/cover/tests/test_calendar_tile.py +++ b/src/collective/cover/tests/test_calendar_tile.py @@ -2,51 +2,18 @@ from collective.cover.tests.base import TestTileMixin from collective.cover.tiles.calendar import CalendarTile from collective.cover.tiles.calendar import ICalendarTile -from datetime import datetime -from plone import api -from tzlocal import get_localzone import unittest -TZNAME = get_localzone().zone - - class CalendarTileTestCase(TestTileMixin, unittest.TestCase): def setUp(self): super(CalendarTileTestCase, self).setUp() - - # Pin calendar to month 08/2016 - self.request['year'] = 2016 - self.request['month'] = 8 - - # Create tile self.tile = CalendarTile(self.cover, self.request) self.tile.__name__ = u'collective.cover.calendar' self.tile.id = u'test' - # Pin today as day 18 - self.tile.isToday = lambda day: day == 18 - - # create some events - with api.env.adopt_roles(['Manager']): - for i in [3, 12, 21, 30]: - start = datetime(2016, 8, i, 10, 30) - end = datetime(2016, 8, i, 11, 30) - obj = api.content.create( - container=self.portal, - type='Event', - id='e{0}'.format(i), - startDate=start, # Archetypes - endDate=end, - start=start, # Dexterity - end=end, - timezone=TZNAME, - ) - api.content.transition(obj, 'publish') - - @unittest.expectedFailure # FIXME: raises BrokenImplementation def test_interface(self): self.interface = ICalendarTile self.klass = CalendarTile @@ -59,81 +26,3 @@ def test_default_configuration(self): def test_accepted_content_types(self): self.assertEqual(self.tile.accepted_ct(), []) - - def test_getEventsForCalendar(self): - self.assertEqual(self.tile.getEventsForCalendar(), EXPECTED_CALENDAR) - - def test_getYearAndMonthToDisplay(self): - self.assertEqual(self.tile.getYearAndMonthToDisplay(), (2016, 8)) - - def test_getWeekdays(self): - expected = [ - u'weekday_mon_short', u'weekday_tue_short', u'weekday_wed_short', - u'weekday_thu_short', u'weekday_fri_short', u'weekday_sat_short', - u'weekday_sun_short'] - self.assertEqual(self.tile.getWeekdays(), expected) - - def test_getReviewStateString(self): - self.assertEqual( - self.tile.getReviewStateString(), 'review_state=published&') - - def test_getEventTypes(self): - self.assertEqual(self.tile.getEventTypes(), 'Type=Event&') - - -EXPECTED_CALENDAR = [ - [{'day': 1, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 2, 'event': 0, 'eventslist': [], 'is_today': False}, - {'date_string': '2016-8-3', - 'day': 3, - 'event': 1, - 'eventslist': [{'end': '11:30:00', 'start': '10:30:00', 'title': 'e3'}], - 'eventstring': u'Aug 03, 2016\n 10:30-11:30 e3', - 'is_today': False}, - {'day': 4, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 5, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 6, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 7, 'event': 0, 'eventslist': [], 'is_today': False}], - [{'day': 8, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 9, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 10, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 11, 'event': 0, 'eventslist': [], 'is_today': False}, - {'date_string': '2016-8-12', - 'day': 12, - 'event': 1, - 'eventslist': [{'end': '11:30:00', 'start': '10:30:00', 'title': 'e12'}], - 'eventstring': u'Aug 12, 2016\n 10:30-11:30 e12', - 'is_today': False}, - {'day': 13, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 14, 'event': 0, 'eventslist': [], 'is_today': False}], - [{'day': 15, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 16, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 17, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 18, 'event': 0, 'eventslist': [], 'is_today': True}, - {'day': 19, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 20, 'event': 0, 'eventslist': [], 'is_today': False}, - {'date_string': '2016-8-21', - 'day': 21, - 'event': 1, - 'eventslist': [{'end': '11:30:00', 'start': '10:30:00', 'title': 'e21'}], - 'eventstring': u'Aug 21, 2016\n 10:30-11:30 e21', - 'is_today': False}], - [{'day': 22, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 23, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 24, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 25, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 26, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 27, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 28, 'event': 0, 'eventslist': [], 'is_today': False}], - [{'day': 29, 'event': 0, 'eventslist': [], 'is_today': False}, - {'date_string': '2016-8-30', - 'day': 30, - 'event': 1, - 'eventslist': [{'end': '11:30:00', 'start': '10:30:00', 'title': 'e30'}], - 'eventstring': u'Aug 30, 2016\n 10:30-11:30 e30', - 'is_today': False}, - {'day': 31, 'event': 0, 'eventslist': [], 'is_today': False}, - {'day': 0, 'event': 0, 'eventslist': []}, - {'day': 0, 'event': 0, 'eventslist': []}, - {'day': 0, 'event': 0, 'eventslist': []}, - {'day': 0, 'event': 0, 'eventslist': []}]] From 40fb5db97c4d3bb99dddf1c86ca056543aee201f Mon Sep 17 00:00:00 2001 From: Rodrigo Ferreira de Souza Date: Wed, 24 Aug 2016 13:03:54 -0300 Subject: [PATCH 7/8] Add plone.app.event compatibility --- src/collective/cover/static/css/cover.css | 19 ++-- src/collective/cover/static/js/main.js | 53 ++++++++++- src/collective/cover/testing.py | 3 +- .../cover/tests/test_calendar_tile.robot | 40 +++++++-- src/collective/cover/tests/test_upgrades.py | 1 + src/collective/cover/tiles/calendar.py | 26 ++---- .../cover/tiles/templates/calendar.pt | 90 +------------------ 7 files changed, 99 insertions(+), 133 deletions(-) diff --git a/src/collective/cover/static/css/cover.css b/src/collective/cover/static/css/cover.css index b8e0727d5..3a38cdf64 100644 --- a/src/collective/cover/static/css/cover.css +++ b/src/collective/cover/static/css/cover.css @@ -249,30 +249,23 @@ table.invisible } /* Tile Calendar */ -.cover-calendar-tile .calendar-tile-header +.cover-calendar-tile .portletCalendar { - font-weight: bold; - line-height: normal; - - padding: .42em 1em; - - text-align: center; - - background: #ddd; + font-size: 1em; } -#content .cover-calendar-tile a.calendar-tile-prev, -#content .cover-calendar-tile a.calendar-tile-next +#content .cover-calendar-tile a.calendarPrevious, +#content .cover-calendar-tile a.calendarNext { margin: 0 -.5em; padding: 0 1em; border-bottom: none; } -.cover-calendar-tile a.calendar-tile-prev +.cover-calendar-tile a.calendarPrevious { float: left; } -.cover-calendar-tile a.calendar-tile-next +.cover-calendar-tile a.calendarNext { float: right; } diff --git a/src/collective/cover/static/js/main.js b/src/collective/cover/static/js/main.js index 417de82d0..32c53a3e2 100644 --- a/src/collective/cover/static/js/main.js +++ b/src/collective/cover/static/js/main.js @@ -1,14 +1,61 @@ $(function() { + // override jquery portlet event + // http://stackoverflow.com/a/14063574/2116850 + function refreshPortlet(hash, _options){ + var options = { + data: {}, + success: function(){}, + error: function(){}, + ajaxOptions: {}}; + $.extend(options, _options); + options.data.portlethash = hash; + ajaxOptions = options.ajaxOptions; + ajaxOptions.url = $('base').attr('href') + '/@@render-portlet'; + ajaxOptions.success = function(data){ + var container = $('[data-portlethash="' + hash + '"]'); + var portlet = $(data); + container.html(portlet); + options.success(data, portlet); + } + ajaxOptions.error = function(){ + options.error(); + } + ajaxOptions.data = options.data; + $.ajax(ajaxOptions); + } + $('body').undelegate('#calendar-next, #calendar-previous', 'click') + .delegate( + '.portletWrapper #calendar-next, ' + + '.portletWrapper #calendar-previous', + 'click', + function(e) { + e.preventDefault(); + var el = $(this); + var container = el.parents('.portletWrapper'); + refreshPortlet(container.data('portlethash'), { + data: { + month: el.data('month'), + year: el.data('year') + } + }); + return false; + }); + // override jquery portlet event + $('#content').on( 'click', - '.cover-calendar-tile a.calendar-tile-prev, ' + - '.cover-calendar-tile a.calendar-tile-next', + '.cover-calendar-tile .calendarPrevious, ' + + '.cover-calendar-tile .calendarNext', function(e) { e.preventDefault(); var $a = $(this); var $tile = $a.parents('.tile'); + var url = '@@updatetile' + if ($a.hasClass('kssCalendarChange')) { + url = $a.attr('href'); + } $.ajax({ - url: '@@updatetile', + url: url, data: { 'tile-id': $tile.attr('id'), 'month:int': $a.attr('data-month'), diff --git a/src/collective/cover/testing.py b/src/collective/cover/testing.py index 0d9910d6e..07d58c4eb 100644 --- a/src/collective/cover/testing.py +++ b/src/collective/cover/testing.py @@ -167,7 +167,8 @@ def setUpPloneSite(self, portal): set_image_field(portal['my-image2'], generate_jpeg(50, 50)) set_image_field(portal['my-news-item'], generate_jpeg(50, 50)) portal_workflow = portal.portal_workflow - portal_workflow.setChainForPortalTypes(['Collection'], ['simple_publication_workflow']) + portal_workflow.setChainForPortalTypes( + ['Collection'], ['simple_publication_workflow']) # Prevent kss validation errors in Plone 4.2 portal_kss = getattr(portal, 'portal_kss', None) diff --git a/src/collective/cover/tests/test_calendar_tile.robot b/src/collective/cover/tests/test_calendar_tile.robot index e9132b55c..0be8535d6 100644 --- a/src/collective/cover/tests/test_calendar_tile.robot +++ b/src/collective/cover/tests/test_calendar_tile.robot @@ -35,20 +35,44 @@ Test Calendar Tile # test next / prev buttons ${nextyear} = Execute Javascript ... return (function() { - ... var $next = jQuery('a.calendar-tile-next'); + ... var $next = jQuery('a.calendarNext'); + ... var str_klasses = $next.attr('class'); + ... if (str_klasses.indexOf('kssattr') >= 0) { + ... var klasses = str_klasses.split(' '); + ... var i, klass, len, year, year_check; + ... for (i = 0, len = klasses.length; i < len; i++) { + ... klass = klasses[i]; + ... year_check = 'kssattr-year-'; + ... if (klass.indexOf(year_check) === 0) { + ... return klass.slice(year_check.length); + ... } + ... } + ... } ... return $next.attr('data-year'); ... })(); ${nextmonth} = Execute Javascript ... return (function() { - ... var $next = jQuery('a.calendar-tile-next'); + ... var $next = jQuery('a.calendarNext'); + ... var str_klasses = $next.attr('class'); + ... if (str_klasses.indexOf('kssattr') >= 0) { + ... var klasses = str_klasses.split(' '); + ... var i, klass, len, month, month_check; + ... for (i = 0, len = klasses.length; i < len; i++) { + ... klass = klasses[i]; + ... month_check = 'kssattr-month-'; + ... if (klass.indexOf(month_check) === 0) { + ... return klass.slice(month_check.length); + ... } + ... } + ... } ... return $next.attr('data-month'); ... })(); - Wait Until Keyword Succeeds 5 sec 1 sec Click Link css=a.calendar-tile-next - Wait Until Keyword Succeeds 5 sec 1 sec Click Link css=a.calendar-tile-next - Page Should Contain Element xpath=.//a[@class='calendar-tile-prev'][@data-month='${nextmonth}'][@data-year='${nextyear}'] - Wait Until Keyword Succeeds 5 sec 1 sec Click Link css=a.calendar-tile-prev - Wait Until Keyword Succeeds 5 sec 1 sec Click Link css=a.calendar-tile-prev - Page Should Contain Element xpath=.//a[@class='calendar-tile-next'][@data-month='${nextmonth}'][@data-year='${nextyear}'] + Wait Until Keyword Succeeds 5 sec 1 sec Click Link css=a.calendarNext + Wait Until Keyword Succeeds 5 sec 1 sec Click Link css=a.calendarNext + Page Should Contain Element jquery=a.calendarPrevious[data-month=${nextmonth}][data-year=${nextyear}],a.calendarPrevious.kssattr-month-${nextmonth}.kssattr-year-${nextyear} + Wait Until Keyword Succeeds 5 sec 1 sec Click Link css=a.calendarPrevious + Wait Until Keyword Succeeds 5 sec 1 sec Click Link css=a.calendarPrevious + Page Should Contain Element jquery=a.calendarNext[data-month=${nextmonth}][data-year=${nextyear}],a.calendarNext.kssattr-month-${nextmonth}.kssattr-year-${nextyear} # delete the tile Open Layout Tab diff --git a/src/collective/cover/tests/test_upgrades.py b/src/collective/cover/tests/test_upgrades.py index 9420d13bd..c6c76bc29 100644 --- a/src/collective/cover/tests/test_upgrades.py +++ b/src/collective/cover/tests/test_upgrades.py @@ -524,6 +524,7 @@ def test_registrations(self): self.assertGreaterEqual(int(version), int(self.to_version)) self.assertEqual(self._how_many_upgrades_to_do(), 4) + @unittest.skipIf(IS_PLONE_5, 'Upgrade step not supported under Plone 5') def test_register_calendar_tile(self): # address also an issue with Setup permission title = u'Register calendar tile' diff --git a/src/collective/cover/tiles/calendar.py b/src/collective/cover/tiles/calendar.py index 3a2f37730..8ce9d6a74 100644 --- a/src/collective/cover/tiles/calendar.py +++ b/src/collective/cover/tiles/calendar.py @@ -8,9 +8,11 @@ try: # plone.app.event from plone.app.event.portlets.portlet_calendar import Assignment from plone.app.event.portlets.portlet_calendar import Renderer + HAS_PLONE_APP_EVENT = True except ImportError: # Plone 4.x from plone.app.portlets.portlets.calendar import Assignment from plone.app.portlets.portlets.calendar import Renderer + HAS_PLONE_APP_EVENT = False class ICalendarTile(IPersistentCoverTile): @@ -40,26 +42,10 @@ def setup(self): helper = Renderer(self.context, self.request, None, None, Assignment()) helper.update() - self._ts = helper._ts - self.url_quote_plus = helper.url_quote_plus - - self.now = helper.now - self.year = year = helper.year - self.month = month = helper.month - - self.showPrevMonth = helper.showPrevMonth - self.showNextMonth = helper.showNextMonth - - self.prevMonthYear, self.prevMonthMonth = helper.getPreviousMonth(year, month) - self.nextMonthYear, self.nextMonthMonth = helper.getNextMonth(year, month) - - self.monthName = helper.monthName - - self.getQueryString = helper.getQueryString - self.getWeekdays = helper.getWeekdays - self.getEventsForCalendar = helper.getEventsForCalendar - self.getReviewStateString = helper.getReviewStateString - self.getEventTypes = helper.getEventTypes + if HAS_PLONE_APP_EVENT: + self.helper_template = helper.render + else: + self.helper_template = helper._template def accepted_ct(self): """Return an empty list as no content types are accepted.""" diff --git a/src/collective/cover/tiles/templates/calendar.pt b/src/collective/cover/tiles/templates/calendar.pt index 498c13e89..07ff08837 100644 --- a/src/collective/cover/tiles/templates/calendar.pt +++ b/src/collective/cover/tiles/templates/calendar.pt @@ -1,96 +1,10 @@ + i18n:domain="collective.cover">
-
- « - - monthname - year - - » -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Month name
Su
- - 31 - - - 31 - 31
-
+ tal:content="structure view/helper_template">
From bc494bc8a648845fdb7695dafdd369170f06a71d Mon Sep 17 00:00:00 2001 From: Rodrigo Ferreira de Souza Date: Fri, 26 Aug 2016 17:13:46 -0300 Subject: [PATCH 8/8] Add upgrade step --- .../cover/profiles/default/metadata.xml | 2 +- src/collective/cover/tests/test_upgrades.py | 11 ++++++++ src/collective/cover/upgrades/configure.zcml | 1 + src/collective/cover/upgrades/v15/__init__.py | 1 + .../cover/upgrades/v15/configure.zcml | 26 +++++++++++++++++++ 5 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/collective/cover/upgrades/v15/__init__.py create mode 100644 src/collective/cover/upgrades/v15/configure.zcml diff --git a/src/collective/cover/profiles/default/metadata.xml b/src/collective/cover/profiles/default/metadata.xml index 82e7b6ccc..e445d598e 100644 --- a/src/collective/cover/profiles/default/metadata.xml +++ b/src/collective/cover/profiles/default/metadata.xml @@ -1,6 +1,6 @@ - 14 + 15 profile-collective.js.galleria:default profile-collective.js.jqueryui:default diff --git a/src/collective/cover/tests/test_upgrades.py b/src/collective/cover/tests/test_upgrades.py index c6c76bc29..35bdcf907 100644 --- a/src/collective/cover/tests/test_upgrades.py +++ b/src/collective/cover/tests/test_upgrades.py @@ -576,3 +576,14 @@ def test_register_calendar_script(self): self._do_upgrade_step(step) self.assertIn(script, js_tool.getResourceIds()) + + +class Upgrade14to15TestCase(UpgradeTestCaseBase): + + def setUp(self): + UpgradeTestCaseBase.setUp(self, u'14', u'15') + + def test_registrations(self): + version = self.setup.getLastVersionForProfile(self.profile_id)[0] + self.assertGreaterEqual(int(version), int(self.to_version)) + self.assertEqual(self._how_many_upgrades_to_do(), 2) diff --git a/src/collective/cover/upgrades/configure.zcml b/src/collective/cover/upgrades/configure.zcml index 46366f154..2acb79f4e 100644 --- a/src/collective/cover/upgrades/configure.zcml +++ b/src/collective/cover/upgrades/configure.zcml @@ -4,5 +4,6 @@ + diff --git a/src/collective/cover/upgrades/v15/__init__.py b/src/collective/cover/upgrades/v15/__init__.py new file mode 100644 index 000000000..40a96afc6 --- /dev/null +++ b/src/collective/cover/upgrades/v15/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/src/collective/cover/upgrades/v15/configure.zcml b/src/collective/cover/upgrades/v15/configure.zcml new file mode 100644 index 000000000..6e5f304c4 --- /dev/null +++ b/src/collective/cover/upgrades/v15/configure.zcml @@ -0,0 +1,26 @@ + + + + + + + + + + +