diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..91b71a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +**/node_modules +.vscode/ \ No newline at end of file diff --git a/README.md b/README.md index acafb58..2546788 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,48 @@ -# SilverStripe Event Calendar Module +# Silverstripe Event Calendar -The Event Calendar module for SilverStripe +## Introduction -## Features +This is an event calendar module for Silverstripe CMS, with the following features: - * **Calendar** - This Page is used to hold/present events and announcments. - * **Calendar Event** - This page represents an event, which can have many DateTimes. - * **Recurring Events** - Calendar Events can also be set up reoccur automatically. - * **Calendar Announcements** - Entries in a Calendar which don't have a page associated. - * **ICS feeds** - Add multiple ICS feeds to a Calendar to display these events in the feed. - * **ICS output** - Download an ics file for easy importing into calendar apps. - * **RSS feed** - RSS feed of calendar events - * **Calendar Widget** - Display a calendar view in a Widget. - * **Caching** +* **Calendar** - A page type used to hold/present events and announcements. +* **Calendar event** - A page type which represents an event, with one or more DateTimes (an instance of an event). +* **Recurring events** - Calendar events can be set up reoccur automatically. +* **Calendar announcements** - Entries in a calendar which don't have an event page associated. +* **ICS feeds** - Add external ICS feeds to a calendar to display these events. +* **ICS output** - Download an ics file for easy importing into calendar apps. +* **RSS feed** - RSS feed of calendar events. +* **Calendar widget** - Display a calendar view in a widget, so website users can select to view events by year/month/week/day periods. +* **Caching** + +## Requirements + +Silverstripe CMS 4.4 or greater + +Carbon ( version 1 - https://github.com/briannesbitt/carbon ) ## Configuration Options -Enable jquery +Enable jQuery (that is, do not request a local copy) + ```yaml -Calendar: +UncleCheese\EventCalendar\Pages\Calendar: jquery_included: true ``` -Enable caching, and years worth of data to cache +Caching options + ```yaml -Calendar: +UncleCheese\EventCalendar\Pages\Calendar: caching_enabled: true cache_future_years: 2 ``` -Set default timezone, lang for ICS output: +Set default time zone and language for ICS output + ```yaml -Calendar: +UncleCheese\EventCalendar\Pages\Calendar: timezone: America/New_York language: EN ``` + + diff --git a/_config/calendar.yml b/_config/calendar.yml index ea03b87..8dac74b 100755 --- a/_config/calendar.yml +++ b/_config/calendar.yml @@ -1,2 +1,5 @@ +--- +Name: 'event-calendar' +--- CachedCalendarTask: cache_future_years: 2 \ No newline at end of file diff --git a/_config/legacy.yml b/_config/legacy.yml new file mode 100644 index 0000000..c57be0f --- /dev/null +++ b/_config/legacy.yml @@ -0,0 +1,14 @@ +--- +Name: event-calendar-legacy +--- +SilverStripe\ORM\DatabaseAdmin: + classname_value_remapping: + CachedCalendarEntry: UncleCheese\EventCalendar\Models\CachedCalendarEntry + Calendar: UncleCheese\EventCalendar\Pages\Calendar + CalendarAnnouncement: UncleCheese\EventCalendar\Models\CalendarAnnouncement + CalendarDateTime: UncleCheese\EventCalendar\Models\CalendarDateTime + CalendarEvent: UncleCheese\EventCalendar\Pages\CalendarEvent + ICSFeed: UncleCheese\EventCalendar\Models\ICSFeed + RecurringDayOfMonth: UncleCheese\EventCalendar\Models\RecurringDayOfMonth + RecurringDayOfWeek: UncleCheese\EventCalendar\Models\RecurringDayOfWeek + RecurringException: UncleCheese\EventCalendar\Models\RecurringException \ No newline at end of file diff --git a/client/dist/css/calendar.css b/client/dist/css/calendar.css new file mode 100644 index 0000000..754221f --- /dev/null +++ b/client/dist/css/calendar.css @@ -0,0 +1 @@ +.event-calendar-events-list ul,.event-calendar-events-list li{list-style:none;margin:0;padding:0}.event-calendar-events-list .event{padding:0 0 1em;margin:0 0 1em;border-bottom:1px solid #ccc}.event-calendar-events-list .event-dates{font-weight:700}.event-calendar-events-list .event-other-dates{margin:1em 0 0}.event-calendar-events-list .event-other-dates ul,.event-calendar-events-list .event-other-dates li{list-style:none;margin:0;padding:0}.event-calendar-events-list .event-other-dates-title{margin:0}.calendar-view-more.loading{color:#ccc} diff --git a/client/dist/css/calendar_cms.css b/client/dist/css/calendar_cms.css new file mode 100644 index 0000000..726224f --- /dev/null +++ b/client/dist/css/calendar_cms.css @@ -0,0 +1,6 @@ + +#Repeat_Alert_Message { background:#fff3a7; padding:2px 4px; border-top:1px solid #d3af22; border-bottom:1px solid #d3af22; } + +.optionset .radio.val { + display: none; +} \ No newline at end of file diff --git a/client/dist/css/calendar_widget.css b/client/dist/css/calendar_widget.css new file mode 100644 index 0000000..3dabeae --- /dev/null +++ b/client/dist/css/calendar_widget.css @@ -0,0 +1 @@ +.calendar-widget{margin:0 0 2em}.calendar-widget-table{width:100%;border:1px solid #ddd;border-collapse:collapse;border-spacing:0}.calendar-widget-table td,.calendar-widget-table th{border:1px solid #ddd}.calendar-widget-table th{text-align:center;padding:5px;font-size:1.2em}.calendar-widget-table tbody td{background-color:#fff;padding:4px;width:13%;font-size:1em}.calendar-widget-table tbody .calendar-header td{background-color:#555;color:#fff;border-color:#555}.calendar-widget-table tbody .calendar-day{text-align:right}.calendar-widget-table tbody .calendar-day:hover{background-color:#d9edf7;color:#3a87ad;cursor:pointer}.calendar-widget-table tbody .show-week{width:9%;text-align:center}.calendar-widget-table tbody .show-week:hover{background-color:#d9edf7;color:#3a87ad;cursor:pointer}.calendar-widget-table tbody.selected{background-color:#d9edf7;color:#3a87ad;cursor:pointer}.calendar-widget-table tbody .out-of-month{background-color:#eee;color:#999}.calendar-widget-table tbody .today{font-weight:700;color:#3a87ad}.calendar-widget-table tbody .hasEvent{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAAHe9q7oAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFVJREFUeNpimD9/fjVAADGkp6f/Bwggxuzs7P/MzMwMAAHEWF5e/vTjx49STJ8/f5bi5+d/CxBgjNXV1Q9ev34tD5Lm4OD4zwLisLOzgzjPxcTENgAAKIIbWtDRU4YAAAAASUVORK5CYII=);background-repeat:no-repeat;background-position:center center} diff --git a/css/live_calendar_widget.css b/client/dist/css/live_calendar_widget.css old mode 100755 new mode 100644 similarity index 100% rename from css/live_calendar_widget.css rename to client/dist/css/live_calendar_widget.css diff --git a/images/calendar-file.gif b/client/dist/images/calendar-file.gif old mode 100755 new mode 100644 similarity index 100% rename from images/calendar-file.gif rename to client/dist/images/calendar-file.gif diff --git a/images/calendar__plus.png b/client/dist/images/calendar__plus.png old mode 100755 new mode 100644 similarity index 100% rename from images/calendar__plus.png rename to client/dist/images/calendar__plus.png diff --git a/images/check.png b/client/dist/images/check.png old mode 100755 new mode 100644 similarity index 100% rename from images/check.png rename to client/dist/images/check.png diff --git a/images/event-file.gif b/client/dist/images/event-file.gif old mode 100755 new mode 100644 similarity index 100% rename from images/event-file.gif rename to client/dist/images/event-file.gif diff --git a/images/feed.png b/client/dist/images/feed.png old mode 100755 new mode 100644 similarity index 100% rename from images/feed.png rename to client/dist/images/feed.png diff --git a/images/loader.gif b/client/dist/images/loader.gif old mode 100755 new mode 100644 similarity index 100% rename from images/loader.gif rename to client/dist/images/loader.gif diff --git a/javascript/calendar.js b/client/dist/js/calendar.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/calendar.js rename to client/dist/js/calendar.js diff --git a/client/dist/js/calendar_cms.js b/client/dist/js/calendar_cms.js new file mode 100644 index 0000000..73507aa --- /dev/null +++ b/client/dist/js/calendar_cms.js @@ -0,0 +1,103 @@ +;(function($) { + + $(function() { + + $('.field.defaultView select').entwine({ + onmatch: function() { + if ($(this).val() != 'upcoming') { + $('.field.defaultFutureMonths').hide(); + } + }, + onchange: function() { + $('.field.defaultFutureMonths').hide(); + if ($(this).val() == 'upcoming') { + $('.field.defaultFutureMonths').show(); + } + } + }); + + $('.field.checkbox.recursion').entwine({ + onmatch: function() { + var fieldHolderID = function(field) { + return '#Form_EditForm_' + field + '_Holder'; + } + var $recursion = this; + var $tab = $recursion.closest('.tab-pane'); + var $customRecursionType = $tab.find(fieldHolderID('CustomRecursionType')).hide(); + var $dailyInterval = $tab.find('.dailyinterval').hide(); + var $weeklyInterval = $tab.find('.weeklyinterval').hide(); + var $monthlyInterval = $tab.find('.monthlyinterval').hide(); + var $monthlyIndex = $tab.find('.monthlyindex').hide(); + var $recurringDaysOfWeek = $tab.find(fieldHolderID('RecurringDaysOfWeek')).hide(); + var $recurringDaysOfMonth = $tab.find(fieldHolderID('RecurringDaysOfMonth')).hide(); + var $monthlyRecursionType1 = $tab.find(fieldHolderID('MonthlyRecursionType1')).hide(); + var $monthlyRecursionType2 = $tab.find(fieldHolderID('MonthlyRecursionType2')).hide(); + + var resetPanels = function() { + $dailyInterval.hide(); + $weeklyInterval.hide(); + $monthlyInterval.hide(); + $recurringDaysOfWeek.hide(); + $recurringDaysOfMonth.hide().find(':checkbox').attr('disabled', true); + $monthlyRecursionType1.hide(); + $monthlyRecursionType2.hide(); + $monthlyIndex.hide().find('select').attr('disabled', true); + }; + + var resetSubPanels = function() { + $recurringDaysOfMonth.hide().find(':checkbox').attr('disabled', true); + $monthlyIndex.hide().find('select').attr('disabled', true); + }; + + $recursion.find('input').change(function() { + if ($(this).is(':checked')) { + $customRecursionType.show(); + } else { + $tab.find(':checkbox, :radio').attr('checked', false); + $customRecursionType.hide(); + resetPanels(); + } + }).change(); + + $customRecursionType.find('input').change(function() { + if ($(this).is(':checked')) { + resetPanels(); + switch($(this).val()) { + case "1": + $dailyInterval.show(); + break; + case "2": + $weeklyInterval.show(); + $recurringDaysOfWeek.show(); + break; + case "3": + $monthlyInterval.show(); + $monthlyRecursionType1.show(); + $monthlyRecursionType2.show(); + break; + } + } + }).change(); + + $monthlyRecursionType1.find('input').change(function() { + if ($(this).is(':checked')) { + resetSubPanels(); + $recurringDaysOfMonth.show().find(':checkbox').attr('disabled', false); + $monthlyIndex.find('select').attr('disabled', true); + $monthlyRecursionType2.find('input').attr('checked', false).change(); + } + }).change(); + + $monthlyRecursionType2.find('input').change(function() { + if ($(this).is(':checked')) { + resetSubPanels(); + $monthlyIndex.show(); + $recurringDaysOfMonth.find(':checkbox').attr('disabled', true); + $monthlyIndex.find('select').attr('disabled', false); + $monthlyRecursionType1.find('input').attr('checked', false).change(); + } + }).change(); + } + }); + }); +})(jQuery); \ No newline at end of file diff --git a/javascript/calendar_core.js b/client/dist/js/calendar_core.js old mode 100755 new mode 100644 similarity index 99% rename from javascript/calendar_core.js rename to client/dist/js/calendar_core.js index ec9f0e3..0a14366 --- a/javascript/calendar_core.js +++ b/client/dist/js/calendar_core.js @@ -13,5 +13,4 @@ function navigateToDate(date) function zeroPad(num) { var s = '0'+num; return s.substring(s.length-2) -} - +} \ No newline at end of file diff --git a/javascript/calendar_interface.js b/client/dist/js/calendar_interface.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/calendar_interface.js rename to client/dist/js/calendar_interface.js diff --git a/javascript/calendar_widget.js b/client/dist/js/calendar_widget.js old mode 100755 new mode 100644 similarity index 98% rename from javascript/calendar_widget.js rename to client/dist/js/calendar_widget.js index 4d2b15c..2866848 --- a/javascript/calendar_widget.js +++ b/client/dist/js/calendar_widget.js @@ -12,13 +12,9 @@ attachTo: function(element) { this.element = element; }, - - getContainer: function() { return this.element; }, - - setMonth: function(month, year) { this.month = month; this.year = year; @@ -35,51 +31,33 @@ this.monthLength = 29; } } - + html = this.generateHTML(); $(this.element).html(html); var calendar = this; - $('.prev', this.element).click(function() { calendar.previousMonth(); }); - - $('.next', this.element).click(function() { calendar.nextMonth(); }); - - $('.calendar-day',this.element).click(function() { calendar.showDay(this); }); - - $('.show-week', this.element).click(function() { calendar.showWeek(this); }); - - $('.show-month',this.element).click(function() { calendar.showMonth(this); }); }, - - - getPaddedMonth: function() { return this.pad(this.month+1); }, - - - setOptions: function(options) { $.extend(this.settings, options); }, - - - getNextMonthYear: function() { var m = this.month+1; var y = this.year; @@ -89,9 +67,6 @@ } return [m, y]; }, - - - getPrevMonthYear: function() { var m = this.month-1; var y = this.year; @@ -101,47 +76,30 @@ } return [m, y]; }, - - - previousMonth: function() { result = this.getPrevMonthYear(); this.setMonth(result[0], result[1]); this.settings.onMonthChange(result[0]+1, result[1], this); }, - - - nextMonth: function() { result = this.getNextMonthYear(); this.setMonth(result[0], result[1]); this.settings.onMonthChange(result[0]+1, result[1], this); }, - - - showDay: function(element) { this.settings.onShowDay($(element).data('date'), this); }, - - - showWeek: function(element) { var start = $(element).closest('tr').find('td').eq(0); var end = $(element).closest('tr').find('td').eq(6); this.settings.onShowWeek(start.data('date'), end.data('date'), this); }, - - - showMonth: function(element) { month = this.getPaddedMonth(); start = this.year + '-' + month + '-01'; end = this.year + '-' + month + '-' + this.monthLength; this.settings.onShowMonth(start, end, this); }, - - generateHTML: function() { // do the header var monthName = this.settings.calMonthsLabels[this.month] @@ -212,17 +170,12 @@ html += ''; return html; }, - pad: function(num) { str = new String(num); return (str.length == 1) ? "0"+str : str; } - - }); - - $.fn.extend({ CalendarWidget: function(options) { return this.each(function() { @@ -252,4 +205,4 @@ calMonthsLabels: [] }); -})( jQuery ); +})(jQuery); diff --git a/javascript/calendar_widget_init.js b/client/dist/js/calendar_widget_init.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/calendar_widget_init.js rename to client/dist/js/calendar_widget_init.js diff --git a/javascript/jquery-1.2.6.min.js b/client/dist/js/jquery-1.2.6.min.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/jquery-1.2.6.min.js rename to client/dist/js/jquery-1.2.6.min.js diff --git a/javascript/jquery.date.js b/client/dist/js/jquery.date.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/jquery.date.js rename to client/dist/js/jquery.date.js diff --git a/javascript/jquery.datePicker.js b/client/dist/js/jquery.datePicker.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/jquery.datePicker.js rename to client/dist/js/jquery.datePicker.js diff --git a/javascript/lang/calendar_en.js b/client/dist/js/lang/calendar_en.js old mode 100755 new mode 100644 similarity index 97% rename from javascript/lang/calendar_en.js rename to client/dist/js/lang/calendar_en.js index 3ae35fc..75ce3fa --- a/javascript/lang/calendar_en.js +++ b/client/dist/js/lang/calendar_en.js @@ -3,6 +3,5 @@ startOnMonday: false, calDaysLabels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], calMonthsLabels: ['January', 'February', 'March', 'April','May', 'June', 'July', 'August', 'September','October', 'November', 'December'] - - }) + }); })(jQuery); \ No newline at end of file diff --git a/javascript/lang/calendar_nb.js b/client/dist/js/lang/calendar_nb.js similarity index 97% rename from javascript/lang/calendar_nb.js rename to client/dist/js/lang/calendar_nb.js index 03a4a67..133931f 100644 --- a/javascript/lang/calendar_nb.js +++ b/client/dist/js/lang/calendar_nb.js @@ -3,6 +3,5 @@ startOnMonday: true, calDaysLabels: ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'], calMonthsLabels: ['Januar', 'Februar', 'Mars', 'April','Mai', 'Juni', 'Juli', 'August', 'September','Oktober', 'November', 'Desember'] - - }) + }); })(jQuery); \ No newline at end of file diff --git a/javascript/lang/calendar_sk.js b/client/dist/js/lang/calendar_sk.js similarity index 97% rename from javascript/lang/calendar_sk.js rename to client/dist/js/lang/calendar_sk.js index d14bed8..5d2b374 100644 --- a/javascript/lang/calendar_sk.js +++ b/client/dist/js/lang/calendar_sk.js @@ -3,5 +3,5 @@ startOnMonday: true, calDaysLabels: ['Po', 'Ut', 'St', 'Št', 'Pi', 'So', 'Ne'], calMonthsLabels: ['Január', 'Február', 'Marec', 'Apríl','Máj', 'Jún', 'Júl', 'August', 'September','Október', 'November', 'December'] - }) + }); })(jQuery); diff --git a/client/dist/js/live_calendar_widget.js b/client/dist/js/live_calendar_widget.js new file mode 100644 index 0000000..b36e47b --- /dev/null +++ b/client/dist/js/live_calendar_widget.js @@ -0,0 +1,20 @@ +(function($){ + $(function(){ + var refreshLink = function() { + $('#live-calendar-widget').load($(this).attr('href'),bind); + return false; + } + var refreshSelect = function() { + $t = $(this); + if($t.val().match('LiveCalendarWidget_Controller')) + $('#live-calendar-widget').load($t.val(),bind); + else + document.location = $t.val(); + } + var bind = function() { + $('.month-nav').click(refreshLink); + $('#live-calendar-widget-navigator').change(refreshSelect); + } + bind(); + }); +})(jQuery); \ No newline at end of file diff --git a/javascript/locale/date_be_utf8.js b/client/dist/js/locale/date_be_utf8.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_be_utf8.js rename to client/dist/js/locale/date_be_utf8.js diff --git a/javascript/locale/date_de.js b/client/dist/js/locale/date_de.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_de.js rename to client/dist/js/locale/date_de.js diff --git a/javascript/locale/date_en.js b/client/dist/js/locale/date_en.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_en.js rename to client/dist/js/locale/date_en.js diff --git a/javascript/locale/date_es.js b/client/dist/js/locale/date_es.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_es.js rename to client/dist/js/locale/date_es.js diff --git a/javascript/locale/date_fr.js b/client/dist/js/locale/date_fr.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_fr.js rename to client/dist/js/locale/date_fr.js diff --git a/javascript/locale/date_it.js b/client/dist/js/locale/date_it.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_it.js rename to client/dist/js/locale/date_it.js diff --git a/javascript/locale/date_nb.js b/client/dist/js/locale/date_nb.js similarity index 100% rename from javascript/locale/date_nb.js rename to client/dist/js/locale/date_nb.js diff --git a/javascript/locale/date_nl.js b/client/dist/js/locale/date_nl.js old mode 100755 new mode 100644 similarity index 98% rename from javascript/locale/date_nl.js rename to client/dist/js/locale/date_nl.js index 9309293..c22de4c --- a/javascript/locale/date_nl.js +++ b/client/dist/js/locale/date_nl.js @@ -1,6 +1,6 @@ -// date localization for locale 'nl' -// generated by Stefan Kip -Date.dayNames = ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag']; -Date.abbrDayNames = ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za']; -Date.monthNames = ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December']; +// date localization for locale 'nl' +// generated by Stefan Kip +Date.dayNames = ['Zondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrijdag', 'Zaterdag']; +Date.abbrDayNames = ['Zo', 'Ma', 'Di', 'Wo', 'Do', 'Vr', 'Za']; +Date.monthNames = ['Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December']; Date.abbrMonthNames = ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec']; \ No newline at end of file diff --git a/javascript/locale/date_pl.js b/client/dist/js/locale/date_pl.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_pl.js rename to client/dist/js/locale/date_pl.js diff --git a/javascript/locale/date_pt-br.js b/client/dist/js/locale/date_pt-br.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_pt-br.js rename to client/dist/js/locale/date_pt-br.js diff --git a/javascript/locale/date_ru_utf8.js b/client/dist/js/locale/date_ru_utf8.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_ru_utf8.js rename to client/dist/js/locale/date_ru_utf8.js diff --git a/javascript/locale/date_ru_win1251.js b/client/dist/js/locale/date_ru_win1251.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_ru_win1251.js rename to client/dist/js/locale/date_ru_win1251.js diff --git a/javascript/locale/date_se.js b/client/dist/js/locale/date_se.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_se.js rename to client/dist/js/locale/date_se.js diff --git a/javascript/locale/date_ua_utf8.js b/client/dist/js/locale/date_ua_utf8.js old mode 100755 new mode 100644 similarity index 100% rename from javascript/locale/date_ua_utf8.js rename to client/dist/js/locale/date_ua_utf8.js diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..b608302 --- /dev/null +++ b/client/package.json @@ -0,0 +1,20 @@ +{ + "name": "silverstripe-event-calendar", + "version": "1.0.0", + "main": "index.js", + "repository": "https://github.com/gheggie/silverstripe-event-calendar", + "author": "Grant Heggie", + "license": "BSD-3-Clause", + "private": false, + "devDependencies": { + "@babel/core": "7", + "@babel/preset-env": "7", + "@rollup/plugin-node-resolve": "^6.0.0", + "@rollup/plugin-replace": "^2.2.1", + "rollup": "1.20", + "rollup-plugin-babel": "^4.3.3" + }, + "dependencies": { + "jquery": "3.1.1" + } +} diff --git a/client/src/calendar.scss b/client/src/calendar.scss new file mode 100644 index 0000000..fe27fd4 --- /dev/null +++ b/client/src/calendar.scss @@ -0,0 +1,43 @@ +@mixin reset-list-styles { + list-style: none; + margin: 0; + padding: 0; +} + +.event-calendar-events-list { + + ul, + li { + @include reset-list-styles(); + } + + .event { + padding: 0 0 1em; + margin: 0 0 1em; + border-bottom: 1px solid #ccc; + + &-dates { + font-weight: 700; + } + + &-other-dates { + margin: 1em 0 0; + + ul, li { + @include reset-list-styles(); + } + + &-title { + margin:0; + } + } + } +} + +.calendar-view-more { + + &.loading { + color: #ccc; + } +} + diff --git a/client/src/calendar_widget.scss b/client/src/calendar_widget.scss new file mode 100644 index 0000000..2fefde4 --- /dev/null +++ b/client/src/calendar_widget.scss @@ -0,0 +1,89 @@ +@mixin border($colour) { + border: 1px solid $colour; +} + +@mixin active() { + background-color: #d9edf7; + color: #3a87ad; + cursor: pointer; +} + +@mixin hover-state() { + + &:hover { + @include active(); + } +} + +.calendar-widget { + margin: 0 0 2em; + + &-table { + width: 100%; + @include border(#ddd); + border-collapse: collapse; + border-spacing: 0; + + td, + th { + @include border(#ddd); + } + + th { + text-align: center; + padding: 5px; + font-size: 1.2em; + } + + tbody { + + td { + background-color: #fff; + padding: 4px; + width: 13%; + font-size: 1em; + + } + + .calendar-header { + + td { + background-color: #555; + color: #fff; + border-color: #555; + } + } + + .calendar-day { + text-align: right; + @include hover-state(); + } + + .show-week { + width: 9%; + text-align: center; + @include hover-state(); + } + + &.selected { + @include active(); + } + + .out-of-month { + background-color: #eee; + color: #999; + } + + .today { + font-weight: 700; + color: #3a87ad; + } + + .hasEvent { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAAHe9q7oAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFVJREFUeNpimD9/fjVAADGkp6f/Bwggxuzs7P/MzMwMAAHEWF5e/vTjx49STJ8/f5bi5+d/CxBgjNXV1Q9ev34tD5Lm4OD4zwLisLOzgzjPxcTENgAAKIIbWtDRU4YAAAAASUVORK5CYII=); + background-repeat: no-repeat; + background-position: center center; + } + } + } +} \ No newline at end of file diff --git a/client/yarn.lock b/client/yarn.lock new file mode 100644 index 0000000..edaea17 --- /dev/null +++ b/client/yarn.lock @@ -0,0 +1,1141 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" + integrity sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== + dependencies: + "@babel/highlight" "^7.8.3" + +"@babel/compat-data@^7.8.0", "@babel/compat-data@^7.8.1": + version "7.8.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.8.1.tgz#fc0bbbb7991e4fb2b47e168e60f2cc2c41680be9" + integrity sha512-Z+6ZOXvyOWYxJ50BwxzdhRnRsGST8Y3jaZgxYig575lTjVSs3KtJnmESwZegg6e2Dn0td1eDhoWlp1wI4BTCPw== + dependencies: + browserslist "^4.8.2" + invariant "^2.2.4" + semver "^5.5.0" + +"@babel/core@7": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.3.tgz#30b0ebb4dd1585de6923a0b4d179e0b9f5d82941" + integrity sha512-4XFkf8AwyrEG7Ziu3L2L0Cv+WyY47Tcsp70JFmpftbAA1K7YL/sgE9jh9HyNj08Y/U50ItUchpN0w6HxAoX1rA== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.8.3" + "@babel/helpers" "^7.8.3" + "@babel/parser" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.0" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.3.tgz#0e22c005b0a94c1c74eafe19ef78ce53a4d45c03" + integrity sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug== + dependencies: + "@babel/types" "^7.8.3" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" + integrity sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz#c84097a427a061ac56a1c30ebf54b7b22d241503" + integrity sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-call-delegate@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.8.3.tgz#de82619898aa605d409c42be6ffb8d7204579692" + integrity sha512-6Q05px0Eb+N4/GTyKPPvnkig7Lylw+QzihMpws9iiZQv7ZImf84ZsZpQH7QoWN4n4tm81SnSzPgHw2qtO0Zf3A== + dependencies: + "@babel/helper-hoist-variables" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-compilation-targets@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.3.tgz#2deedc816fd41dca7355ef39fd40c9ea69f0719a" + integrity sha512-JLylPCsFjhLN+6uBSSh3iYdxKdeO9MNmoY96PE/99d8kyBFaXLORtAVhqN6iHa+wtPeqxKLghDOZry0+Aiw9Tw== + dependencies: + "@babel/compat-data" "^7.8.1" + browserslist "^4.8.2" + invariant "^2.2.4" + levenary "^1.1.0" + semver "^5.5.0" + +"@babel/helper-create-regexp-features-plugin@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.3.tgz#c774268c95ec07ee92476a3862b75cc2839beb79" + integrity sha512-Gcsm1OHCUr9o9TcJln57xhWHtdXbA2pgQ58S0Lxlks0WMGNXuki4+GLfX0p+L2ZkINUGZvfkz8rzoqJQSthI+Q== + dependencies: + "@babel/helper-regex" "^7.8.3" + regexpu-core "^4.6.0" + +"@babel/helper-define-map@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz#a0655cad5451c3760b726eba875f1cd8faa02c15" + integrity sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/types" "^7.8.3" + lodash "^4.17.13" + +"@babel/helper-explode-assignable-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz#a728dc5b4e89e30fc2dfc7d04fa28a930653f982" + integrity sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw== + dependencies: + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-function-name@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" + integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA== + dependencies: + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-get-function-arity@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" + integrity sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-hoist-variables@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz#1dbe9b6b55d78c9b4183fc8cdc6e30ceb83b7134" + integrity sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-member-expression-to-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c" + integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498" + integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-module-transforms@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.8.3.tgz#d305e35d02bee720fbc2c3c3623aa0c316c01590" + integrity sha512-C7NG6B7vfBa/pwCOshpMbOYUmrYQDfCpVL/JCRu0ek8B5p8kue1+BCXpg2vOYs7w5ACB9GTOBYQ5U6NwrMg+3Q== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + lodash "^4.17.13" + +"@babel/helper-optimise-call-expression@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" + integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670" + integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ== + +"@babel/helper-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.8.3.tgz#139772607d51b93f23effe72105b319d2a4c6965" + integrity sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ== + dependencies: + lodash "^4.17.13" + +"@babel/helper-remap-async-to-generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz#273c600d8b9bf5006142c1e35887d555c12edd86" + integrity sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-wrap-function" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-replace-supers@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz#91192d25f6abbcd41da8a989d4492574fb1530bc" + integrity sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-simple-access@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" + integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw== + dependencies: + "@babel/template" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helper-split-export-declaration@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" + integrity sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA== + dependencies: + "@babel/types" "^7.8.3" + +"@babel/helper-wrap-function@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" + integrity sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/helpers@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.3.tgz#382fbb0382ce7c4ce905945ab9641d688336ce85" + integrity sha512-LmU3q9Pah/XyZU89QvBgGt+BCsTPoQa+73RxAQh8fb8qkDyIfeQnmgs+hvzhTCKTzqOyk7JTkS3MS1S8Mq5yrQ== + dependencies: + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/highlight@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" + integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081" + integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ== + +"@babel/plugin-proposal-async-generator-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" + integrity sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-remap-async-to-generator" "^7.8.3" + "@babel/plugin-syntax-async-generators" "^7.8.0" + +"@babel/plugin-proposal-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz#38c4fe555744826e97e2ae930b0fb4cc07e66054" + integrity sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + +"@babel/plugin-proposal-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz#da5216b238a98b58a1e05d6852104b10f9a70d6b" + integrity sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.0" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz#e4572253fdeed65cddeecfdab3f928afeb2fd5d2" + integrity sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + +"@babel/plugin-proposal-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz#eb5ae366118ddca67bed583b53d7554cad9951bb" + integrity sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + +"@babel/plugin-proposal-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz#9dee96ab1650eed88646ae9734ca167ac4a9c5c9" + integrity sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + +"@babel/plugin-proposal-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz#ae10b3214cb25f7adb1f3bc87ba42ca10b7e2543" + integrity sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + +"@babel/plugin-proposal-unicode-property-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz#b646c3adea5f98800c9ab45105ac34d06cd4a47f" + integrity sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-async-generators@^7.8.0": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-dynamic-import@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-json-strings@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-object-rest-spread@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.0": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz#3acdece695e6b13aaf57fc291d1a800950c71391" + integrity sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-arrow-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz#82776c2ed0cd9e1a49956daeb896024c9473b8b6" + integrity sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-async-to-generator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz#4308fad0d9409d71eafb9b1a6ee35f9d64b64086" + integrity sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-remap-async-to-generator" "^7.8.3" + +"@babel/plugin-transform-block-scoped-functions@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz#437eec5b799b5852072084b3ae5ef66e8349e8a3" + integrity sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-block-scoping@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz#97d35dab66857a437c166358b91d09050c868f3a" + integrity sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + lodash "^4.17.13" + +"@babel/plugin-transform-classes@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.3.tgz#46fd7a9d2bb9ea89ce88720477979fe0d71b21b8" + integrity sha512-SjT0cwFJ+7Rbr1vQsvphAHwUHvSUPmMjMU/0P59G8U2HLFqSa082JO7zkbDNWs9kH/IUqpHI6xWNesGf8haF1w== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-define-map" "^7.8.3" + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz#96d0d28b7f7ce4eb5b120bb2e0e943343c86f81b" + integrity sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-destructuring@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz#20ddfbd9e4676906b1056ee60af88590cc7aaa0b" + integrity sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-dotall-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz#c3c6ec5ee6125c6993c5cbca20dc8621a9ea7a6e" + integrity sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-duplicate-keys@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz#8d12df309aa537f272899c565ea1768e286e21f1" + integrity sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz#581a6d7f56970e06bf51560cd64f5e947b70d7b7" + integrity sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-for-of@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.3.tgz#15f17bce2fc95c7d59a24b299e83e81cedc22e18" + integrity sha512-ZjXznLNTxhpf4Q5q3x1NsngzGA38t9naWH8Gt+0qYZEJAcvPI9waSStSh56u19Ofjr7QmD0wUsQ8hw8s/p1VnA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-function-name@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz#279373cb27322aaad67c2683e776dfc47196ed8b" + integrity sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ== + dependencies: + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz#aef239823d91994ec7b68e55193525d76dbd5dc1" + integrity sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-member-expression-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz#963fed4b620ac7cbf6029c755424029fa3a40410" + integrity sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-modules-amd@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.8.3.tgz#65606d44616b50225e76f5578f33c568a0b876a5" + integrity sha512-MadJiU3rLKclzT5kBH4yxdry96odTUwuqrZM+GllFI/VhxfPz+k9MshJM+MwhfkCdxxclSbSBbUGciBngR+kEQ== + dependencies: + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-commonjs@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.8.3.tgz#df251706ec331bd058a34bdd72613915f82928a5" + integrity sha512-JpdMEfA15HZ/1gNuB9XEDlZM1h/gF/YOH7zaZzQu2xCFRfwc01NXBMHHSTT6hRjlXJJs5x/bfODM3LiCk94Sxg== + dependencies: + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-simple-access" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-systemjs@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.8.3.tgz#d8bbf222c1dbe3661f440f2f00c16e9bb7d0d420" + integrity sha512-8cESMCJjmArMYqa9AO5YuMEkE4ds28tMpZcGZB/jl3n0ZzlsxOAi3mC+SKypTfT8gjMupCnd3YiXCkMjj2jfOg== + dependencies: + "@babel/helper-hoist-variables" "^7.8.3" + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + babel-plugin-dynamic-import-node "^2.3.0" + +"@babel/plugin-transform-modules-umd@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.8.3.tgz#592d578ce06c52f5b98b02f913d653ffe972661a" + integrity sha512-evhTyWhbwbI3/U6dZAnx/ePoV7H6OUG+OjiJFHmhr9FPn0VShjwC2kdxqIuQ/+1P50TMrneGzMeyMTFOjKSnAw== + dependencies: + "@babel/helper-module-transforms" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz#a2a72bffa202ac0e2d0506afd0939c5ecbc48c6c" + integrity sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + +"@babel/plugin-transform-new-target@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz#60cc2ae66d85c95ab540eb34babb6434d4c70c43" + integrity sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-object-super@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz#ebb6a1e7a86ffa96858bd6ac0102d65944261725" + integrity sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.3.tgz#7890576a13b17325d8b7d44cb37f21dc3bbdda59" + integrity sha512-/pqngtGb54JwMBZ6S/D3XYylQDFtGjWrnoCF4gXZOUpFV/ujbxnoNGNvDGu6doFWRPBveE72qTx/RRU44j5I/Q== + dependencies: + "@babel/helper-call-delegate" "^7.8.3" + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-property-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz#33194300d8539c1ed28c62ad5087ba3807b98263" + integrity sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-regenerator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.3.tgz#b31031e8059c07495bf23614c97f3d9698bc6ec8" + integrity sha512-qt/kcur/FxrQrzFR432FGZznkVAjiyFtCOANjkAKwCbt465L6ZCiUQh2oMYGU3Wo8LRFJxNDFwWn106S5wVUNA== + dependencies: + regenerator-transform "^0.14.0" + +"@babel/plugin-transform-reserved-words@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz#9a0635ac4e665d29b162837dd3cc50745dfdf1f5" + integrity sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-shorthand-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8" + integrity sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz#9c8ffe8170fdfb88b114ecb920b82fb6e95fe5e8" + integrity sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-sticky-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz#be7a1290f81dae767475452199e1f76d6175b100" + integrity sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/helper-regex" "^7.8.3" + +"@babel/plugin-transform-template-literals@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz#7bfa4732b455ea6a43130adc0ba767ec0e402a80" + integrity sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-typeof-symbol@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.3.tgz#5cffb216fb25c8c64ba6bf5f76ce49d3ab079f4d" + integrity sha512-3TrkKd4LPqm4jHs6nPtSDI/SV9Cm5PRJkHLUgTcqRQQTMAZ44ZaAdDZJtvWFSaRcvT0a1rTmJ5ZA5tDKjleF3g== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-transform-unicode-regex@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz#0cef36e3ba73e5c57273effb182f46b91a1ecaad" + integrity sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/preset-env@7": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.8.3.tgz#dc0fb2938f52bbddd79b3c861a4b3427dd3a6c54" + integrity sha512-Rs4RPL2KjSLSE2mWAx5/iCH+GC1ikKdxPrhnRS6PfFVaiZeom22VFKN4X8ZthyN61kAaR05tfXTbCvatl9WIQg== + dependencies: + "@babel/compat-data" "^7.8.0" + "@babel/helper-compilation-targets" "^7.8.3" + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-proposal-async-generator-functions" "^7.8.3" + "@babel/plugin-proposal-dynamic-import" "^7.8.3" + "@babel/plugin-proposal-json-strings" "^7.8.3" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-proposal-object-rest-spread" "^7.8.3" + "@babel/plugin-proposal-optional-catch-binding" "^7.8.3" + "@babel/plugin-proposal-optional-chaining" "^7.8.3" + "@babel/plugin-proposal-unicode-property-regex" "^7.8.3" + "@babel/plugin-syntax-async-generators" "^7.8.0" + "@babel/plugin-syntax-dynamic-import" "^7.8.0" + "@babel/plugin-syntax-json-strings" "^7.8.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.0" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + "@babel/plugin-transform-arrow-functions" "^7.8.3" + "@babel/plugin-transform-async-to-generator" "^7.8.3" + "@babel/plugin-transform-block-scoped-functions" "^7.8.3" + "@babel/plugin-transform-block-scoping" "^7.8.3" + "@babel/plugin-transform-classes" "^7.8.3" + "@babel/plugin-transform-computed-properties" "^7.8.3" + "@babel/plugin-transform-destructuring" "^7.8.3" + "@babel/plugin-transform-dotall-regex" "^7.8.3" + "@babel/plugin-transform-duplicate-keys" "^7.8.3" + "@babel/plugin-transform-exponentiation-operator" "^7.8.3" + "@babel/plugin-transform-for-of" "^7.8.3" + "@babel/plugin-transform-function-name" "^7.8.3" + "@babel/plugin-transform-literals" "^7.8.3" + "@babel/plugin-transform-member-expression-literals" "^7.8.3" + "@babel/plugin-transform-modules-amd" "^7.8.3" + "@babel/plugin-transform-modules-commonjs" "^7.8.3" + "@babel/plugin-transform-modules-systemjs" "^7.8.3" + "@babel/plugin-transform-modules-umd" "^7.8.3" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.8.3" + "@babel/plugin-transform-new-target" "^7.8.3" + "@babel/plugin-transform-object-super" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.8.3" + "@babel/plugin-transform-property-literals" "^7.8.3" + "@babel/plugin-transform-regenerator" "^7.8.3" + "@babel/plugin-transform-reserved-words" "^7.8.3" + "@babel/plugin-transform-shorthand-properties" "^7.8.3" + "@babel/plugin-transform-spread" "^7.8.3" + "@babel/plugin-transform-sticky-regex" "^7.8.3" + "@babel/plugin-transform-template-literals" "^7.8.3" + "@babel/plugin-transform-typeof-symbol" "^7.8.3" + "@babel/plugin-transform-unicode-regex" "^7.8.3" + "@babel/types" "^7.8.3" + browserslist "^4.8.2" + core-js-compat "^3.6.2" + invariant "^2.2.2" + levenary "^1.1.0" + semver "^5.5.0" + +"@babel/template@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" + integrity sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/parser" "^7.8.3" + "@babel/types" "^7.8.3" + +"@babel/traverse@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.3.tgz#a826215b011c9b4f73f3a893afbc05151358bf9a" + integrity sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.8.3" + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/parser" "^7.8.3" + "@babel/types" "^7.8.3" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + +"@babel/types@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" + integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg== + dependencies: + esutils "^2.0.2" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + +"@rollup/plugin-node-resolve@^6.0.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-6.1.0.tgz#0d2909f4bf606ae34d43a9bc8be06a9b0c850cf0" + integrity sha512-Cv7PDIvxdE40SWilY5WgZpqfIUEaDxFxs89zCAHjqyRwlTSuql4M5hjIuc5QYJkOH0/vyiyNXKD72O+LhRipGA== + dependencies: + "@rollup/pluginutils" "^3.0.0" + "@types/resolve" "0.0.8" + builtin-modules "^3.1.0" + is-module "^1.0.0" + resolve "^1.11.1" + +"@rollup/plugin-replace@^2.2.1": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.3.0.tgz#86d88746383e40dd81cffb5216449cc51a734eb9" + integrity sha512-rzWAMqXAHC1w3eKpK6LxRqiF4f3qVFaa1sGii6Bp3rluKcwHNOpPt+hWRCmAH6SDEPtbPiLFf0pfNQyHs6Btlg== + dependencies: + magic-string "^0.25.2" + rollup-pluginutils "^2.6.0" + +"@rollup/pluginutils@^3.0.0": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.0.4.tgz#3a104a41a90f8d1dcf308e18f8fa402d1cc6576e" + integrity sha512-buc0oeq2zqQu2mpMyvZgAaQvitikYjT/4JYhA4EXwxX8/g0ZGHoGiX+0AwmfhrNqH4oJv67gn80sTZFQ/jL1bw== + dependencies: + estree-walker "^0.6.1" + +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + +"@types/node@*": + version "13.5.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.5.0.tgz#4e498dbf355795a611a87ae5ef811a8660d42662" + integrity sha512-Onhn+z72D2O2Pb2ql2xukJ55rglumsVo1H6Fmyi8mlU9SvKdBk/pUSUAiBY/d9bAOF7VVWajX3sths/+g6ZiAQ== + +"@types/node@^12.7.2": + version "12.12.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.25.tgz#792c0afb798f1dd681dce9c4b4c431f7245a0a42" + integrity sha512-nf1LMGZvgFX186geVZR1xMZKKblJiRfiASTHw85zED2kI1yDKHDwTKMdkaCbTlXoRKlGKaDfYywt+V0As30q3w== + +"@types/resolve@0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" + integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== + dependencies: + "@types/node" "*" + +acorn@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" + integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +babel-plugin-dynamic-import-node@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz#f00f507bdaa3c3e3ff6e7e5e98d90a7acab96f7f" + integrity sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ== + dependencies: + object.assign "^4.1.0" + +browserslist@^4.8.2, browserslist@^4.8.3: + version "4.8.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.5.tgz#691af4e327ac877b25e7a3f7ee869c4ef36cdea3" + integrity sha512-4LMHuicxkabIB+n9874jZX/az1IaZ5a+EUuvD7KFOu9x/Bd5YHyO0DIz2ls/Kl8g0ItS4X/ilEgf4T1Br0lgSg== + dependencies: + caniuse-lite "^1.0.30001022" + electron-to-chromium "^1.3.338" + node-releases "^1.1.46" + +builtin-modules@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484" + integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw== + +caniuse-lite@^1.0.30001022: + version "1.0.30001022" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001022.tgz#9eeffe580c3a8f110b7b1742dcf06a395885e4c6" + integrity sha512-FjwPPtt/I07KyLPkBQ0g7/XuZg6oUkYBVnPHNj3VHJbOjmmJ/GdSo/GUY6MwINEQvjhP6WZVbX8Tvms8xh0D5A== + +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +convert-source-map@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" + integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== + dependencies: + safe-buffer "~5.1.1" + +core-js-compat@^3.6.2: + version "3.6.4" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.6.4.tgz#938476569ebb6cda80d339bcf199fae4f16fff17" + integrity sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA== + dependencies: + browserslist "^4.8.3" + semver "7.0.0" + +debug@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +define-properties@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +electron-to-chromium@^1.3.338: + version "1.3.340" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.340.tgz#5d4fe78e984d4211194cf5a52e08069543da146f" + integrity sha512-hRFBAglhcj5iVYH+o8QU0+XId1WGoc0VGowJB1cuJAt3exHGrivZvWeAO5BRgBZqwZtwxjm8a5MQeGoT/Su3ww== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +estree-walker@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +gensync@^1.0.0-beta.1: + version "1.0.0-beta.1" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" + integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-symbols@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +invariant@^2.2.2, invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== + dependencies: + loose-envify "^1.0.0" + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + +jquery@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.1.1.tgz#347c1c21c7e004115e0a4da32cece041fad3c8a3" + integrity sha1-NHwcIcfgBBFeCk2jLOzgQfrTyKM= + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json5@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" + integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== + dependencies: + minimist "^1.2.0" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levenary@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/levenary/-/levenary-1.1.0.tgz#fc146fe75f32dc483a0a2c64aef720f602cd6210" + integrity sha512-VHcwhO0UTpUW7rLPN2/OiWJdgA1e9BqEDALhrgCe/F+uUJnep6CoUsTzMeP8Rh0NGr9uKquXxqe7lwLZo509nQ== + dependencies: + leven "^3.1.0" + +lodash@^4.17.13: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" + integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +magic-string@^0.25.2: + version "0.25.6" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.6.tgz#5586387d1242f919c6d223579cc938bf1420795e" + integrity sha512-3a5LOMSGoCTH5rbqobC2HuDNRtE2glHZ8J7pK+QZYppyWA36yuNpsX994rIY2nCuyP7CZYy7lQq/X2jygiZ89g== + dependencies: + sourcemap-codec "^1.4.4" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +node-releases@^1.1.46: + version "1.1.47" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.47.tgz#c59ef739a1fd7ecbd9f0b7cf5b7871e8a8b591e4" + integrity sha512-k4xjVPx5FpwBUj0Gw7uvFOTF4Ep8Hok1I6qjwL3pLfwe7Y0REQSAqOwwv9TWBCUtMHxcXfY4PgRLRozcChvTcA== + dependencies: + semver "^6.3.0" + +object-keys@^1.0.11, object-keys@^1.0.12: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +private@^0.1.6: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + +regenerate-unicode-properties@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" + integrity sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + +regenerator-transform@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.1.tgz#3b2fce4e1ab7732c08f665dfdb314749c7ddd2fb" + integrity sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ== + dependencies: + private "^0.1.6" + +regexpu-core@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6" + integrity sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.1.0" + regjsgen "^0.5.0" + regjsparser "^0.6.0" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.1.0" + +regjsgen@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.1.tgz#48f0bf1a5ea205196929c0d9798b42d1ed98443c" + integrity sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg== + +regjsparser@^0.6.0: + version "0.6.2" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.2.tgz#fd62c753991467d9d1ffe0a9f67f27a529024b96" + integrity sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q== + dependencies: + jsesc "~0.5.0" + +resolve@^1.11.1, resolve@^1.3.2: + version "1.15.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5" + integrity sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw== + dependencies: + path-parse "^1.0.6" + +rollup-plugin-babel@^4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-4.3.3.tgz#7eb5ac16d9b5831c3fd5d97e8df77ba25c72a2aa" + integrity sha512-tKzWOCmIJD/6aKNz0H1GMM+lW1q9KyFubbWzGiOG540zxPPifnEAHTZwjo0g991Y+DyOZcLqBgqOdqazYE5fkw== + dependencies: + "@babel/helper-module-imports" "^7.0.0" + rollup-pluginutils "^2.8.1" + +rollup-pluginutils@^2.6.0, rollup-pluginutils@^2.8.1: + version "2.8.2" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== + dependencies: + estree-walker "^0.6.1" + +rollup@1.20: + version "1.20.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.20.3.tgz#6243f6c118ca05f56b2d9433112400cd834a1eb8" + integrity sha512-/OMCkY0c6E8tleeVm4vQVDz24CkVgvueK3r8zTYu2AQNpjrcaPwO9hE+pWj5LTFrvvkaxt4MYIp2zha4y0lRvg== + dependencies: + "@types/estree" "0.0.39" + "@types/node" "^12.7.2" + acorn "^7.0.0" + +safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^5.4.1, semver@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +sourcemap-codec@^1.4.4: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" + integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== diff --git a/code/CachedCalendarEntry.php b/code/CachedCalendarEntry.php deleted file mode 100755 index bf6d96f..0000000 --- a/code/CachedCalendarEntry.php +++ /dev/null @@ -1,74 +0,0 @@ - 'Date', - 'StartTime' => 'Time', - 'EndDate' => 'Date', - 'EndTime' => 'Time', - 'AllDay' => 'Boolean', - 'Announcement' => 'Boolean', - 'DateRange' => 'HTMLText', - 'TimeRange' => 'HTMLText', - 'ICSLink' => 'Varchar(100)', - 'Title' => 'Varchar(255)', - 'Content' => 'HTMLText' - ); - - private static $has_one = array ( - 'CachedCalendar' => 'Calendar', - 'Calendar' => 'Calendar', - 'Event' => 'CalendarEvent' - ); - - private static $default_sort = "StartDate ASC, StartTime ASC"; - - public static function create_from_datetime(CalendarDateTime $dt, Calendar $calendar) { - $cached = CachedCalendarEntry::create(); - $cached->hydrate($dt, $calendar); - return $cached; - } - - public static function create_from_announcement(CalendarAnnouncement $dt, Calendar $calendar) { - $cached = CachedCalendarEntry::create(); - $cached->hydrate($dt, $calendar); - $cached->CalendarID = $dt->CalendarID; - $cached->Announcement = 1; - return $cached; - } - - public function OtherDates() { - if($this->Announcement) { - return false; - } - - return CachedCalendarEntry::get() - ->filter(array( - "EventID" => $this->EventID - )) - ->exclude(array( - "StartDate" => $this->StartDate - )) - ->limit($this->CachedCalendar()->OtherDatesCount); - } - - - - public function hydrate(CalendarDateTime $dt, Calendar $calendar) { - $this->CachedCalendarID = $calendar->ID; - foreach($dt->db() as $field => $type) { - $this->$field = $dt->$field; - } - foreach($dt->has_one() as $field => $type) { - $this->{$field."ID"} = $dt->{$field."ID"}; - } - $this->DateRange = $dt->DateRange(); - $this->TimeRange = $dt->TimeRange(); - $this->ICSLink = $dt->ICSLink(); - $this->Title = $dt->getTitle(); - $this->Content = $dt->getContent(); - return $this; - } - -} diff --git a/code/Calendar.php b/code/Calendar.php deleted file mode 100755 index 1dcdaa7..0000000 --- a/code/Calendar.php +++ /dev/null @@ -1,975 +0,0 @@ - 'Varchar(50)', - 'OtherDatesCount' => 'Int', - 'RSSTitle' => 'Varchar(255)', - 'DefaultFutureMonths' => 'Int', - 'EventsPerPage' => 'Int', - 'DefaultView' => "Enum('today,week,month,weekend,upcoming','upcoming')" - ); - - private static $has_many = array ( - 'Announcements' => 'CalendarAnnouncement', - 'Feeds' => 'ICSFeed' - ); - - private static $many_many = array ( - 'NestedCalendars' => 'Calendar' - ); - - private static $belongs_many_many = array ( - 'ParentCalendars' => 'Calendar' - ); - - private static $allowed_children = array ( - 'CalendarEvent' - ); - - private static $defaults = array ( - 'DefaultDateHeader' => 'Upcoming Events', - 'OtherDatesCount' => '3', - 'DefaultFutureMonths' => '6', - 'EventsPerPage' => '10', - 'DefaultView' => 'upcoming' - ); - - private static $reccurring_event_index = 0; - - private static $icon = "event_calendar/images/calendar"; - - private static $description = "A collection of Calendar Events"; - - private static $event_class = "CalendarEvent"; - - private static $announcement_class = "CalendarAnnouncement"; - - private static $timezone = "America/New_York"; - - private static $language = "EN"; - - public static $jquery_included = false; - - private static $caching_enabled = false; - - protected $eventClass_cache, - $announcementClass_cache, - $datetimeClass_cache, - $dateToEventRelation_cache, - $announcementToCalendarRelation_cache, - $EventList_cache; - - public static function set_jquery_included($bool = true) { - self::$jquery_included = $bool; - } - - public static function enable_caching() { - self::config()->caching_enabled = true; - } - - public function getCMSFields() { - - $self = $this; - - $this->beforeUpdateCMSFields(function($f) use ($self) { - - Requirements::javascript('event_calendar/javascript/calendar_cms.js'); - - $configuration = _t('Calendar.CONFIGURATION','Configuration'); - $f->addFieldsToTab("Root.$configuration", array( - DropdownField::create('DefaultView',_t('Calendar.DEFAULTVIEW','Default view'), array ( - 'upcoming' => _t('Calendar.UPCOMINGVIEW',"Show a list of upcoming events."), - 'month' => _t('Calendar.MONTHVIEW',"Show this month's events."), - 'week' => _t('Calendar.WEEKVIEW',"Show this week's events. If none, fall back on this month's"), - 'today' => _t('Calendar.TODAYVIEW',"Show today's events. If none, fall back on this week's events"), - 'weekend' => _t('Calendar.WEEKENDVIEW',"Show this weekend's events.") - ))->addExtraClass('defaultView'), - NumericField::create('DefaultFutureMonths', _t('Calendar.DEFAULTFUTUREMONTHS','Number maximum number of future months to show in default view'))->addExtraClass('defaultFutureMonths'), - NumericField::create('EventsPerPage', _t('Calendar.EVENTSPERPAGE','Events per page')), - TextField::create('DefaultDateHeader', _t('Calendar.DEFAULTDATEHEADER','Default date header (displays when no date range has been selected)')), - NumericField::create('OtherDatesCount', _t('Calendar.NUMBERFUTUREDATES','Number of future dates to show for repeating events')) - )); - - // Announcements - $announcements = _t('Calendar.Announcements','Announcements'); - $f->addFieldToTab("Root.$announcements", $announcementsField = GridField::create( - "Announcements", - $announcements, - $self->Announcements(), - GridFieldConfig_RecordEditor::create() - )); - $announcementsField->setDescription(_t('Calendar.ANNOUNCEMENTDESCRIPTION','Announcements are simple entries you can add to your calendar that do not have detail pages, e.g. "Office closed"')); - - // Feeds - $feeds = _t('Calendar.FEEDS','Feeds'); - $f->addFieldToTab("Root.$feeds", $feedsField = GridField::create( - "Feeds", - $feeds, - $self->Feeds(), - GridFieldConfig_RecordEditor::create() - )); - $feedsField->setDescription(_t('Calendar.ICSFEEDDESCRIPTION','Add ICS feeds to your calendar to include events from external sources, e.g. a Google Calendar')); - - $otherCals = Calendar::get()->exclude(array("ID" => $self->ID)); - if($otherCals->exists()) { - $f->addFieldToTab("Root.$feeds", new CheckboxSetField( - 'NestedCalendars', - _t('Calendar.NESTEDCALENDARS','Include events from these calendars'), - $otherCals->map('ID', 'Link') - )); - } - - $f->addFieldToTab("Root.Main", new TextField('RSSTitle', _t('Calendar.RSSTITLE','Title of RSS Feed')),'Content'); - - }); - - $f = parent::getCMSFields(); - - return $f; - } - - public function getEventClass() { - if($this->eventClass_cache) return $this->eventClass_cache; - $this->eventClass_cache = $this->stat('event_class'); - return $this->eventClass_cache; - } - - public function getAnnouncementClass() { - if($this->announcementClass_cache) return $this->announcementClass_cache; - $this->announcementClass_cache = $this->stat('announcement_class'); - return $this->announcementClass_cache; - } - - public function getDateTimeClass() { - if($this->datetimeClass_cache) return $this->datetimeClass_cache; - $this->datetimeClass_cache = singleton($this->getEventClass())->stat('datetime_class'); - return $this->datetimeClass_cache; - } - - public function getDateToEventRelation() { - if($this->dateToEventRelation_cache) return $this->dateToEventRelation_cache; - $this->dateToEventRelation_cache = singleton($this->getDateTimeClass())->getReverseAssociation($this->getEventClass())."ID"; - return $this->dateToEventRelation_cache; - } - - public function getCachedEventList($start, $end, $filter = null, $limit = null) { - return CachedCalendarEntry::get() - ->filter(array( - "CachedCalendarID" => $this->ID - )) - ->exclude(array( - "StartDate:LessThan" => $end, - "EndDate:GreaterThan" => $start, - )) - ->sort(array( - "StartDate" => "ASC", - "StartTime" => "ASC" - )) - ->limit($limit); - - } - - public function getEventList($start, $end, $filter = null, $limit = null, $announcement_filter = null) { - if(Config::inst()->get("Calendar", "caching_enabled")) { - return $this->getCachedEventList($start, $end, $filter, $limit); - } - - $eventList = new ArrayList(); - - foreach($this->getAllCalendars() as $calendar) { - if($events = $calendar->getStandardEvents($start, $end, $filter)) { - $eventList->merge($events); - } - - $announcements = DataList::create($this->getAnnouncementClass()) - ->filter(array( - "CalendarID" => $calendar->ID, - "StartDate:LessThan:Not" => $start, - "EndDate:GreaterThan:Not" => $end, - )); - if($announcement_filter) { - $announcements = $announcements->where($announcement_filter); - } - - if($announcements) { - foreach($announcements as $announcement) { - $eventList->push($announcement); - } - } - - if($recurring = $calendar->getRecurringEvents($filter)) { - $eventList = $calendar->addRecurringEvents($start, $end, $recurring, $eventList); - } - - if($feedevents = $calendar->getFeedEvents($start,$end)) { - $eventList->merge($feedevents); - } - } - - $eventList = $eventList->sort(array("StartDate" => "ASC", "StartTime" => "ASC")); - $eventList = $eventList->limit($limit); - - return $this->EventList_cache = $eventList; - } - - protected function getStandardEvents($start, $end, $filter = null) { - $children = $this->AllChildren(); - $ids = $children->column('ID'); - $datetimeClass = $this->getDateTimeClass(); - $relation = $this->getDateToEventRelation(); - $eventClass = $this->getEventClass(); - - $list = DataList::create($datetimeClass) - ->filter(array( - $relation => $ids - )) - ->innerJoin($eventClass, "$relation = \"{$eventClass}\".\"ID\"") - ->innerJoin("SiteTree", "\"SiteTree\".\"ID\" = \"{$eventClass}\".\"ID\"") - ->where("Recursion != 1"); - if($start && $end) { - $list = $list->where(" - (StartDate <= '$start' AND EndDate >= '$end') OR - (StartDate BETWEEN '$start' AND '$end') OR - (EndDate BETWEEN '$start' AND '$end') - "); - } - else if($start) { - $list = $list->where("(StartDate >= '$start' OR EndDate > '$start')"); - } - - else if($end) { - $list = $list->where("(EndDate <= '$end' OR StartDate < '$end')"); - } - - if($filter) { - $list = $list->where($filter); - } - - return $list; - } - - protected function getRecurringEvents($filter = null) { - $event_class = $this->getEventClass(); - $datetime_class = $this->getDateTimeClass(); - if($relation = $this->getDateToEventRelation()) { - $events = DataList::create($event_class) - ->filter("Recursion", "1") - ->filter("ParentID", $this->ID) - ->innerJoin($datetime_class, "\"{$datetime_class}\".{$relation} = \"SiteTree\".ID"); - if($filter) { - $events = $events->where($filter); - } - return $events; - } - return false; - } - - public function getNextRecurringEvents($event_obj, $datetime_obj, $limit = null) { - $counter = sfDate::getInstance($datetime_obj->StartDate); - if($event = $datetime_obj->Event()->DateTimes()->First()) { - $end_date = strtotime($event->EndDate); - } - else { - $end_date = false; - } - $counter->tomorrow(); - $dates = new ArrayList(); - while($dates->Count() != $this->OtherDatesCount) { - // check the end date - if($end_date) { - if($end_date > 0 && $end_date <= $counter->get()) - break; - } - if($event_obj->getRecursionReader()->recursionHappensOn($counter->get())) { - $dates->push($this->newRecursionDateTime($datetime_obj,$counter->date())); - } - $counter->tomorrow(); - } - return $dates; - } - - protected function addRecurringEvents($start_date, $end_date, $recurring_events,$all_events) { - $date_counter = sfDate::getInstance($start_date); - $end = sfDate::getInstance($end_date); - foreach($recurring_events as $recurring_event) { - $reader = $recurring_event->getRecursionReader(); - $relation = $recurring_event->getReverseAssociation($this->getDateTimeClass()); - if(!$relation) continue; - - $recurring_event_datetimes = $recurring_event->$relation()->filter(array( - 'StartDate:LessThanOrEqual' => $end->date(), - 'EndDate:GreaterThanOrEqual' => $date_counter->date(), - )); - - foreach ($recurring_event_datetimes as $recurring_event_datetime) { - $date_counter = sfDate::getInstance($start_date); - $start = sfDate::getInstance($recurring_event_datetime->StartDate); - if ($start->get() > $date_counter->get()) { - $date_counter = $start; - } - while($date_counter->get() <= $end->get()){ - // check the end date - if($recurring_event_datetime->EndDate) { - $end_stamp = strtotime($recurring_event_datetime->EndDate); - if($end_stamp > 0 && $end_stamp < $date_counter->get()) { - break; - } - } - if($reader->recursionHappensOn($date_counter->get())) { - $e = $this->newRecursionDateTime($recurring_event_datetime, $date_counter->date()); - $all_events->push($e); - } - $date_counter->tomorrow(); - } - $date_counter->reset(); - } - } - return $all_events; - } - - public function newRecursionDateTime($recurring_event_datetime, $start_date) { - $c = $this->getDateTimeClass(); - $relation = $this->getDateToEventRelation(); - $e = new $c(); - foreach($recurring_event_datetime->db() as $field => $type) { - $e->$field = $recurring_event_datetime->$field; - } - $e->DateTimeID = $recurring_event_datetime->ID; - $e->StartDate = $start_date; - $e->EndDate = $start_date; - $e->$relation = $recurring_event_datetime->$relation; - $e->ID = "recurring" . self::$reccurring_event_index; - self::$reccurring_event_index++; - return $e; - } - - - public function getFeedEvents($start_date, $end_date) { - $start = new DateTime($start_date); - // single day views don't pass end dates - if ($end_date) { - $end = new DateTime($end_date); - } else { - $end = $start; - } - - $feeds = $this->Feeds(); - $feedevents = new ArrayList(); - foreach( $feeds as $feed ) { - $feedreader = new ICal( $feed->URL ); - $events = $feedreader->events(); - foreach ( $events as $event ) { - // translate iCal schema into CalendarAnnouncement schema (datetime + title/content) - $feedevent = new CalendarAnnouncement; - //pass ICS feed ID to event list - $feedevent->ID = 'ICS_'.$feed->ID; - $feedevent->Feed = true; - $feedevent->CalendarID = $this->ID; - $feedevent->Title = $event['SUMMARY']; - if ( isset($event['DESCRIPTION']) ) { - $feedevent->Content = $event['DESCRIPTION']; - } - $startdatetime = $this->iCalDateToDateTime($event['DTSTART']);//->setTimezone(new DateTimeZone($this->stat('timezone'))); - $enddatetime = $this->iCalDateToDateTime($event['DTEND']);//->setTimezone(new DateTimeZone($this->stat('timezone'))); - - //Set event start/end to midnight to allow comparisons below to work - $startdatetime->modify('00:00:00'); - $enddatetime->modify('00:00:00'); - - if ( ($startdatetime < $start && $enddatetime < $start) - || $startdatetime > $end && $enddatetime > $end) { - // do nothing; dates outside range - } else { - $feedevent->StartDate = $startdatetime->format('Y-m-d'); - $feedevent->StartTime = $startdatetime->format('H:i:s'); - - $feedevent->EndDate = $enddatetime->format('Y-m-d'); - $feedevent->EndTime = $enddatetime->format('H:i:s'); - - $feedevents->push($feedevent); - } - } - } - return $feedevents; - } - - public function iCalDateToDateTime($date) { - $dt = new DateTime($date); - $dt->setTimeZone( new DateTimezone($this->stat('timezone')) ); - return $dt; - } - - - public function getAllCalendars() { - $calendars = new ArrayList(); - $calendars->push($this); - $calendars->merge($this->NestedCalendars()); - return $calendars; - } - - public function UpcomingEvents($limit = 5, $filter = null) { - $all = $this->getEventList( - sfDate::getInstance()->date(), - sfDate::getInstance()->addMonth($this->DefaultFutureMonths)->date(), - $filter, - $limit - ); - return $all->limit($limit); - } - - public function UpcomingAnnouncements($limit = 5, $filter = null) { - return $this->Announcements() - ->filter(array( - 'StartDate:GreaterThan' => 'NOW' - )) - ->where($filter) - ->limit($limit); - } - - public function RecentEvents($limit = null, $filter = null) { - $start_date = sfDate::getInstance(); - $end_date = sfDate::getInstance(); - $l = ($limit === null) ? "9999" : $limit; - $events = $this->getEventList( - $start_date->subtractMonth($this->DefaultFutureMonths)->date(), - $end_date->yesterday()->date(), - $filter, - $l - ); - $events->sort('StartDate','DESC'); - return $events->limit($limit); - } - - public function CalendarWidget() { - $calendar = CalendarWidget::create($this); - $controller = Controller::curr(); - if($controller->class == "Calendar_Controller" || is_subclass_of($controller, "Calendar_Controller")) { - if($controller->getView() != "default") { - if($startDate = $controller->getStartDate()) { - $calendar->setOption('start', $startDate->format('Y-m-d')); - } - if($endDate = $controller->getEndDate()) { - $calendar->setOption('end', $endDate->format('Y-m-d')); - } - } - } - return $calendar; - } - - public function MonthJumpForm() { - $controller = Controller::curr(); - if($controller->class == "Calendar_Controller" || is_subclass_of($controller, "Calendar_Controller")) { - return Controller::curr()->MonthJumpForm(); - } - $c = new Calendar_Controller($this); - return $c->MonthJumpForm(); - } - -} - -class Calendar_Controller extends Page_Controller { - - private static $allowed_actions = array ( - 'show', - 'month', - 'year', - 'rss', - 'today', - 'week', - 'weekend', - 'ical', - 'ics', - 'monthjson', - 'MonthJumpForm' - ); - - protected $view; - - protected $startDate; - - protected $endDate; - - public function init() { - parent::init(); - RSSFeed::linkToFeed($this->Link() . "rss", $this->RSSTitle ? $this->RSSTitle : $this->Title); - Requirements::themedCSS('calendar','event_calendar'); - if(!Calendar::config()->jquery_included) { - Requirements::javascript(THIRDPARTY_DIR.'/jquery/jquery.js'); - } - Requirements::javascript('event_calendar/javascript/calendar.js'); - } - - public function getStartDate() { - return $this->startDate; - } - - public function getEndDate() { - return $this->endDate; - } - - public function getView() { - return $this->view; - } - - public function setDefaultView() { - $this->view = "default"; - $this->startDate = sfDate::getInstance(); - $this->endDate = sfDate::getInstance()->addMonth($this->DefaultFutureMonths); - } - - public function setTodayView() { - $this->view = "day"; - $this->startDate = sfDate::getInstance(); - $this->endDate = sfDate::getInstance(); - } - - public function setWeekView() { - $this->view = "week"; - $this->startDate = sfDate::getInstance()->firstDayOfWeek(); - $this->endDate = sfDate::getInstance()->finalDayOfWeek(); - if(CalendarUtil::get_first_day_of_week() == sfTime::MONDAY) { - $this->startDate->tomorrow(); - $this->endDate->tomorrow(); - } - } - - public function setWeekendView() { - $this->view = "weekend"; - $start = sfDate::getInstance(); - if($start->format('w') == sfTime::SATURDAY) { - $start->yesterday(); - } - elseif($start->format('w') != sfTime::FRIDAY) { - $start->nextDay(sfTime::FRIDAY); - } - $this->startDate = $start; - $this->endDate = sfDate::getInstance($start)->nextDay(sfTime::SUNDAY); - } - - public function setMonthView() { - $this->view = "month"; - $this->startDate = sfDate::getInstance()->firstDayOfMonth(); - $this->endDate = sfDate::getInstance($this->startDate)->finalDayOfMonth(); - } - - public function getOffset() { - if(!isset($_REQUEST['start'])) { - $_REQUEST['start'] = 0; - } - return $_REQUEST['start']; - } - - protected function getRangeLink(sfDate $start, sfDate $end) { - return Controller::join_links($this->Link(), "show", $start->format('Ymd'), $end->format('Ymd')); - } - - public function respond() { - if(Director::is_ajax()) { - return $this->renderWith('EventList'); - } - return array(); - } - - public function index(SS_HTTPRequest $r) { - $this->extend('index',$r); - switch($this->DefaultView) { - case "month": - return $this->redirect($this->Link('show/month')); - break; - - case "week": - $this->setWeekView(); - // prevent pagination on these default views - $this->EventsPerPage = 999; - $e = $this->Events(); - if($e->count() > 0) { - return array('Events' => $e); - } - else { - $this->setMonthView(); - return array(); - } - break; - - case "today": - // prevent pagination on these default views - $this->EventsPerPage = 999; - $this->setTodayView(); - $e = $this->Events(); - if($e->count() > 0) { - return array('Events' => $e); - } - else { - $this->setWeekView(); - return array(); - } - break; - - default: - $this->setDefaultView(); - return $this->respond(); - break; - - - } - } - - public function today(SS_HTTPRequest $r) { - $this->setTodayView(); - return $this->respond(); - } - - public function week(SS_HTTPRequest $r) { - $this->setWeekView(); - return $this->respond(); - } - - public function weekend(SS_HTTPRequest $r) { - $this->setWeekendView(); - return $this->respond(); - } - - - public function month(SS_HTTPRequest $r) { - $this->setMonthView(); - return $this->respond(); - } - - public function show(SS_HTTPRequest $r) { - $this->parseURL($r); - return $this->respond(); - } - - public function rss() { - $this->setDefaultView(); - $events = $this->Events(); - foreach($events as $event) { - $event->Title = strip_tags($event->DateRange()) . " : " . $event->getTitle(); - $event->Description = $event->getContent(); - } - $rss_title = $this->RSSTitle ? $this->RSSTitle : sprintf(_t("Calendar.UPCOMINGEVENTSFOR","Upcoming Events for %s"),$this->Title); - $rss = new RSSFeed($events, $this->Link(), $rss_title, "", "Title", "Description"); - - if(is_int($rss->lastModified)) { - HTTP::register_modification_timestamp($rss->lastModified); - header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $rss->lastModified) . ' GMT'); - } - if(!empty($rss->etag)) { - HTTP::register_etag($rss->etag); - } - $xml = str_replace(' ', ' ', $rss->renderWith('RSSFeed')); - $xml = preg_replace('//', '', $xml); - $xml = trim($xml); - HTTP::add_cache_headers(); - $this->getResponse()->addHeader('Content-Type', 'application/rss+xml'); - $this->getResponse()->setBody($xml); - return $this->getResponse(); - } - - public function monthjson(SS_HTTPRequest $r) { - if(!$r->param('ID')) return false; - - //Increase the per page limit to 500 as the AJAX request won't look for further pages - $this->EventsPerPage = 500; - - $this->startDate = sfDate::getInstance(CalendarUtil::get_date_from_string($r->param('ID'))); - $this->endDate = sfDate::getInstance($this->startDate)->finalDayOfMonth(); - - $json = array (); - $counter = clone $this->startDate; - while($counter->get() <= $this->endDate->get()) { - $d = $counter->format('Y-m-d'); - $json[$d] = array ( - 'events' => array () - ); - $counter->tomorrow(); - } - $list = $this->Events(); - foreach($list as $e) { - foreach($e->getAllDatesInRange() as $date) { - if(isset($json[$date])) { - $json[$date]['events'][] = $e->getTitle(); - } - } - } - return Convert::array2json($json); - } - - /** - * Send ical file of multiple upcoming events using ICSWriter - * - * @todo Support recurring events. - * @see ICSWriter - * @author Alex Hayes - */ - public function ical() { - $writer = new ICSWriter($this->data(), Director::absoluteURL('/')); - $writer->sendDownload(); - } - - public function ics(SS_HTTPRequest $r) { - $feed = false; - $announcement = false; - $id = $r->param('ID'); - $oid = $r->param('OtherID'); - - if(stristr($id, "ICS_") !== false) { - $id = str_replace("ICS_","",$id); - $feed = true; - } - else if(stristr($id, "announcement-") !== false) { - $id = str_replace("announcement-","",$id); - $announcement = true; - } - else { - $announcement = false; - } - if(is_numeric($id) && $oid) { - if(!$feed) { - $event = DataObject::get_by_id($announcement ? $this->data()->getDateTimeClass() : $this->data()->getEventClass(), $id); - // return if not found - if (!$event) { - return $this->httpError(404); - } - $FILENAME = $announcement ? preg_replace("/[^a-zA-Z0-9s]/", "", $event->Title) : $event->URLSegment; - } - else { - $FILENAME = preg_replace("/[^a-zA-Z0-9s]/", "", urldecode($_REQUEST['title'])); - } - - $FILENAME .= ".ics"; - $HOST = $_SERVER['HTTP_HOST']; - $TIMEZONE = Calendar::config()->timezone; - $LANGUAGE = Calendar::config()->language; - $CALSCALE = "GREGORIAN"; - $parts = explode('-',$oid); - $START_TIMESTAMP = $parts[0]; - $END_TIMESTAMP = $parts[1]; - if(!$feed) { - $URL = $announcement ? $event->Calendar()->AbsoluteLink() : $event->AbsoluteLink(); - } - else { - $URL = ""; - } - $TITLE = $feed ? $_REQUEST['title'] : $event->Title; - $CONTENT = $feed ? $_REQUEST['content'] : $event->obj('Content')->Summary(); - $LOCATION = $feed ? $_REQUEST['location'] : $event->Location; - $this->getResponse()->addHeader('Cache-Control','private'); - $this->getResponse()->addHeader('Content-Description','File Transfer'); - $this->getResponse()->addHeader('Content-Type','text/calendar'); - $this->getResponse()->addHeader('Content-Transfer-Encoding','binary'); - if(stristr($_SERVER['HTTP_USER_AGENT'], "MSIE")) { - $this->getResponse()->addHeader("Content-disposition","filename=".$FILENAME."; attachment;"); - } - else { - $this->getResponse()->addHeader("Content-disposition","attachment; filename=".$FILENAME); - } - $result = trim(strip_tags($this->customise(array( - 'HOST' => $HOST, - 'LANGUAGE' => $LANGUAGE, - 'TIMEZONE' => $TIMEZONE, - 'CALSCALE' => $CALSCALE, - 'START_TIMESTAMP' => $START_TIMESTAMP, - 'END_TIMESTAMP' => $END_TIMESTAMP, - 'URL' => $URL, - 'TITLE' => $TITLE, - 'CONTENT' => $CONTENT, - 'LOCATION' => $LOCATION - ))->renderWith(array('ics')))); - return $result; - } - else { - $this->redirectBack(); - } - } - - public function parseURL(SS_HTTPRequest $r) { - if(!$r->param('ID')) return; - $this->startDate = sfDate::getInstance(CalendarUtil::get_date_from_string($r->param('ID'))); - if($r->param('OtherID')) { - $this->view = "range"; - $this->endDate = sfDate::getInstance(CalendarUtil::get_date_from_string($r->param('OtherID'))); - } - else { - $d = clone $this->startDate; - switch(strlen(str_replace("-","",$r->param('ID')))) { - case 8: - $this->view = "day"; - $this->endDate = sfDate::getInstance($d->get()+1); - break; - - case 6: - $this->view = "month"; - $this->endDate = sfDate::getInstance($d->finalDayOfMonth()->date()); - break; - - case 4: - $this->view = "year"; - $this->endDate = sfDate::getInstance($d->finalDayOfYear()->date()); - break; - - default: - $this->view = "default"; - $this->endDate = sfDate::getInstance($d->addMonth($this->DefaultFutureMonths)->date()); - break; - } - } - } - - public function Events() { - $event_filter = null; - $announcement_filter = null; - $endDate = $this->endDate; - - if($search = $this->getRequest()->getVar('s')) { - $s = Convert::raw2sql($search); - $event_filter = "\"SiteTree\".\"Title\" LIKE '%$s%' OR \"SiteTree\".\"Content\" LIKE '%$s%'"; - $announcement_filter = "\"CalendarAnnouncement\".\"Title\" LIKE '%$s%' OR \"CalendarAnnouncement\".\"Content\" LIKE '%$s%'"; - $this->SearchQuery = $search; - $endDate = sfDate::getInstance()->addMonth($this->DefaultFutureMonths); - } - - $all = $this->data()->getEventList( - $this->startDate ? $this->startDate->date() : null, - $endDate ? $endDate->date() : null, - $event_filter, - null, - $announcement_filter - ); - - $all_events_count = $all->count(); - $list = $all->limit($this->EventsPerPage, $this->getOffset()); - $next = $this->getOffset()+$this->EventsPerPage; - $this->MoreEvents = ($next < $all_events_count); - $this->MoreLink = HTTP::setGetVar("start", $next); - return $list; - } - - public function DateHeader() { - switch($this->view) { - case "day": - return CalendarUtil::localize($this->startDate->get(), null, CalendarUtil::ONE_DAY_HEADER); - break; - - case "month": - return CalendarUtil::localize($this->startDate->get(), null, CalendarUtil::MONTH_HEADER); - break; - - case "year": - return CalendarUtil::localize($this->startDate->get(), null, CalendarUtil::YEAR_HEADER); - break; - - case "range": - case "week": - case "weekend": - list($strStartDate,$strEndDate) = CalendarUtil::get_date_string($this->startDate->date(),$this->endDate->date()); - return $strStartDate.$strEndDate; - break; - - default: - return $this->DefaultDateHeader; - break; - } - } - - public function CurrentAction($a) { - return $this->getAction() == $a; - } - - public function PreviousDayLink() { - $s = sfDate::getInstance($this->startDate)->yesterday(); - return $this->getRangeLink($s, $s); - } - - public function NextDayLink() { - $s = sfDate::getInstance($this->startDate)->tomorrow(); - return $this->getRangeLink($s, $s); - } - - public function PreviousWeekLink() { - $s = sfDate::getInstance($this->startDate)->subtractWeek(); - $e = sfDate::getInstance($this->endDate)->subtractWeek(); - return $this->getRangeLink($s, $e); - } - - public function NextWeekLink() { - $s = sfDate::getInstance($this->startDate)->addWeek(); - $e = sfDate::getInstance($this->endDate)->addWeek(); - return $this->getRangeLink($s, $e); - } - - public function NextMonthLink() { - $s = sfDate::getInstance($this->startDate)->addMonth(); - $e = sfDate::getInstance($s)->finalDayOfMonth(); - return $this->getRangeLink($s, $e); - } - - public function PreviousMonthLink() { - $s = sfDate::getInstance($this->startDate)->subtractMonth(); - $e = sfDate::getInstance($s)->finalDayOfMonth(); - return $this->getRangeLink($s, $e); - } - - public function NextWeekendLink() { - return $this->NextWeekLink(); - } - - public function PreviousWeekendLink() { - return $this->PreviousWeekLink(); - } - - public function IsSegment($segment) { - switch($segment) { - case "today": - return $this->startDate->date() == $this->endDate->date(); - case "week": - if(CalendarUtil::get_first_day_of_week() == sfTime::MONDAY) { - return ($this->startDate->format('w') == sfTime::MONDAY) && ($this->startDate->format('w') == sfTime::SUNDAY); - } - return ($this->startDate->format('w') == sfTime::SUNDAY) && ($this->endDate->format('w') == sfTime::SATURDAY); - case "month": - return ($this->startDate->format('j') == 1) && (sfDate::getInstance($this->startDate)->finalDayOfMonth()->format('j') == $this->endDate->format('j')); - case "weekend": - return ($this->startDate->format('w') == sfTime::FRIDAY) && ($this->endDate->format('w') == sfTime::SUNDAY); - } - } - - public function MonthJumper() { - return $this->renderWith('MonthJumper'); - } - - public function MonthJumpForm() { - $this->parseURL($this->getRequest()); - $dummy = sfDate::getInstance($this->startDate); - $range = range(($dummy->subtractYear(3)->format('Y')), ($dummy->addYear(6)->format('Y'))); - $year_map = array_combine($range, $range); - $f = new Form( - $this, - "MonthJumpForm", - new FieldList ( - $m = new DropdownField('Month','', CalendarUtil::get_months_map('%B')), - $y = new DropdownField('Year','', $year_map) - ), - new FieldList ( - new FormAction('doMonthJump', _t('Calendar.JUMP','Go')) - ) - ); - - if($this->startDate) { - $m->setValue($this->startDate->format('m')); - $y->setValue($this->startDate->format('Y')); - } - else { - $m->setValue(date('m')); - $y->setValue(date('Y')); - } - return $f; - } - - public function doMonthJump($data, $form) { - return $this->redirect($this->Link('show').'/'.$data['Year'].$data['Month']); - } - -} diff --git a/code/CalendarAnnouncement.php b/code/CalendarAnnouncement.php deleted file mode 100755 index 0209f45..0000000 --- a/code/CalendarAnnouncement.php +++ /dev/null @@ -1,49 +0,0 @@ - 'Varchar(255)', - 'Content' => 'Text' - ); - - private static $has_one = array ( - 'Calendar' => 'Calendar' - ); - - public function getCMSFields() { - - $self = $this; - - $this->beforeUpdateCMSFields(function($f) use ($self) { - - $f->insertBefore(new TextField('Title', _t('CalendarAnnouncement.TITLE','Title of announcement')), "StartDate"); - $f->insertBefore(new TextareaField('Content', _t('CalendarAnnouncement.CONTENT','Announcement content')), "StartDate"); - - }); - - $f = parent::getCMSFields(); - - return $f; - } - - public function summaryFields() { - return array ( - 'Title' => _t('CalendarAnnouncement.TITLE','Title of announcement'), - 'FormattedStartDate' => _t('Calendar.STARTDATE','Start date'), - 'FormattedEndDate' => _t('Calendar.ENDDATE','End date'), - 'FormattedStartTime' => _t('Calendar.STARTTIME','Start time'), - 'FormattedEndTime' => _t('Calendar.ENDTIME','End time'), - 'FormattedAllDay' => _t('Calendar.ALLDAY','All day'), - ); - } - - public function getTitle() { - return $this->getField('Title'); - } - - public function getContent() { - return $this->getField('Content'); - } - -} diff --git a/code/CalendarDateTime.php b/code/CalendarDateTime.php deleted file mode 100755 index 7f6084b..0000000 --- a/code/CalendarDateTime.php +++ /dev/null @@ -1,246 +0,0 @@ - 'Date', - 'StartTime' => 'Time', - 'EndDate' => 'Date', - 'EndTime' => 'Time', - 'AllDay' => 'Boolean' - ); - - private static $has_one = array ( - 'Event' => 'CalendarEvent' - ); - - private static $date_format_override; - - private static $time_format_override; - - private static $default_sort = "StartDate ASC, StartTime ASC"; - - /** - * Set to the timezone offset (E.g. +12:00 for GMT+12). Must be in ISO 8601 format - * - * @config - * @see http://php.net/manual/en/function.date.php - * @var string - */ - private static $offset = "+00:00"; - - public function getCMSFields() { - DateField::set_default_config('showcalendar', true); - $f = new FieldList( - new DateField('StartDate',_t('CalendarDateTime.STARTDATE','Start date')), - new DateField('EndDate',_t('CalendarDateTime.ENDDATE','End date')), - new TimeField('StartTime', _t('CalendarDateTime.STARTTIME','Start time')), - new TimeField('EndTime', _t('CalendarDateTime.ENDTIME','End time')), - new CheckboxField('AllDay', _t('CalendarDateTime.ALLDAY','This event lasts all day')) - ); - - $this->extend('updateCMSFields', $f); - - return $f; - } - - public function summaryFields() { - return array ( - 'FormattedStartDate' => _t('Calendar.STARTDATE','Start date'), - 'FormattedEndDate' => _t('Calendar.ENDDATE','End date'), - 'FormattedStartTime' => _t('Calendar.STARTTIME','Start time'), - 'FormattedEndTime' => _t('Calendar.ENDTIME','End time'), - 'FormattedAllDay' => _t('Calendar.ALLDAY','All day'), - ); - } - - public function Link() { - return Controller::join_links($this->Event()->Link(),"?date=".$this->StartDate); - } - - public function DateRange() { - list($strStartDate,$strEndDate) = CalendarUtil::get_date_string($this->StartDate,$this->EndDate); - $html = "" . $strStartDate . ""; - $html .= ($strEndDate != "") ? "-" : ""; - $html .= ""; - $html .= ($strEndDate != "") ? $strEndDate : ""; - $html .= ""; - - return $html; - } - - public function TimeRange() { - $func = CalendarUtil::get_time_format() == "24" ? "Nice24" : "Nice"; - $ret = $this->obj('StartTime')->$func(); - $ret .= $this->EndTime ? " — " . $this->obj('EndTime')->$func() : ""; - return $ret; - } - - public function Announcement() { - return $this->ClassName == "CalendarAnnouncement"; - } - - public function OtherDates() { - if($this->Announcement()) { - return false; - } - - if($this->Event()->Recursion) { - return $this->Event()->Parent()->getNextRecurringEvents($this->Event(), $this); - } - - return DataList::create($this->class) - ->where("EventID = {$this->EventID}") - ->where("StartDate != '{$this->StartDate}'") - ->limit($this->Event()->Parent()->OtherDatesCount); - } - - public function MicroformatStart($offset = true) { - if(!$this->StartDate) - return ""; - - $date = $this->StartDate; - - if($this->AllDay) - $time = "00:00:00"; - else - $time = $this->StartTime ? $this->StartTime : "00:00:00"; - - return CalendarUtil::microformat($date, $time, self::config()->offset); - } - - public function MicroformatEnd($offset = true) { - if($this->AllDay && $this->StartDate) { - $time = "00:00:00"; - $end = sfDate::getInstance($this->StartDate); - $date = $end->tomorrow()->date(); - unset($end); - } - else { - $date = $this->EndDate ? $this->EndDate : $this->StartDate; - $time = $this->EndTime && $this->StartTime ? $this->EndTime : (!$this->EndTime && $this->StartTime ? $this->StartTime : "00:00:00"); - } - - return CalendarUtil::microformat($date, $time, self::config()->offset); - } - - public function ICSLink() { - $ics_start = $this->obj('StartDate')->Format('Ymd')."T".$this->obj('StartTime')->Format('His'); - if($this->EndDate) { - $ics_end = $this->obj('EndDate')->Format('Ymd')."T".$this->obj('EndTime')->Format('His'); - } - else { - $ics_end = $ics_start; - } - if($this->Feed) { - return Controller::join_links( - $this->Calendar()->Link(), - "ics", - $this->ID, - $ics_start . "-" . $ics_end, - "?title=".urlencode($this->Title) - ); - } - else if($this->Announcement()) { - return Controller::join_links( - $this->Calendar()->Link(), - "ics","announcement-".$this->ID, - $ics_start . "-" . $ics_end - ); - } - return Controller::join_links( - $this->Event()->Parent()->Link(), - "ics", - $this->Event()->ID, - $ics_start . "-" . $ics_end - ); - } - - public function getFormattedStartDate() { - if(!$this->StartDate) return "--"; - return CalendarUtil::get_date_format() == "mdy" ? $this->obj('StartDate')->Format('m-d-Y') : $this->obj('StartDate')->Format('d-m-Y'); - } - - public function getFormattedEndDate() { - if(!$this->EndDate) return "--"; - return CalendarUtil::get_date_format() == "mdy" ? $this->obj('EndDate')->Format('m-d-Y') : $this->obj('EndDate')->Format('d-m-Y'); - } - - public function getFormattedStartTime() { - if(!$this->StartTime) return "--"; - return CalendarUtil::get_time_format() == "12" ? $this->obj('StartTime')->Nice() : $this->obj('StartTime')->Nice24(); - } - - public function getFormattedEndTime() { - if(!$this->EndTime) return "--"; - return CalendarUtil::get_time_format() == "12" ? $this->obj('EndTime')->Nice() : $this->obj('EndTime')->Nice24(); - } - - public function getFormattedAllDay() { - return $this->AllDay == 1 ? _t('YES','Yes') : _t('NO','No'); - } - - public function getTitle() { - return $this->Event()->Title; - } - - public function getContent() { - return $this->Event()->Content; - } - - public function getAllDatesInRange() { - $start = sfDate::getInstance($this->StartDate); - $end = sfDate::getInstance($this->EndDate); - $dates = array (); - do { - $dates[] = $start->format('Y-m-d'); - $start->tomorrow(); - } while($start->get() <= $end->get()); - return $dates; - } - - public function canCreate($member = null) { - if (!$member) { - $member = Member::currentUser(); - } - $extended = $this->extendedCan(__FUNCTION__, $member); - if($extended !== null) { - return $extended; - } - return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); - } - - public function canEdit($member = null) { - if (!$member) { - $member = Member::currentUser(); - } - $extended = $this->extendedCan(__FUNCTION__, $member); - if($extended !== null) { - return $extended; - } - return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); - } - - public function canDelete($member = null) { - if (!$member) { - $member = Member::currentUser(); - } - $extended = $this->extendedCan(__FUNCTION__, $member); - if($extended !== null) { - return $extended; - } - return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); - } - - public function canView($member = null) { - if (!$member) { - $member = Member::currentUser(); - } - $extended = $this->extendedCan(__FUNCTION__, $member); - if($extended !== null) { - return $extended; - } - return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); - } - -} diff --git a/code/CalendarEvent.php b/code/CalendarEvent.php deleted file mode 100755 index 4f63329..0000000 --- a/code/CalendarEvent.php +++ /dev/null @@ -1,237 +0,0 @@ - 'Text', - 'Recursion' => 'Boolean', - 'CustomRecursionType' => 'Int', - 'DailyInterval' => 'Int', - 'WeeklyInterval' => 'Int', - 'MonthlyInterval' => 'Int', - 'MonthlyRecursionType1' => 'Int', - 'MonthlyRecursionType2' => 'Int', - 'MonthlyIndex' => 'Int', - 'MonthlyDayOfWeek' => 'Int' - ); - - private static $has_many = array ( - 'DateTimes' => 'CalendarDateTime', - 'Exceptions' => 'RecurringException' - ); - - private static $many_many = array ( - 'RecurringDaysOfWeek' => 'RecurringDayOfWeek', - 'RecurringDaysOfMonth' => 'RecurringDayOfMonth' - ); - - private static $icon = "event_calendar/images/event"; - - private static $description = "An individual event entry"; - - private static $datetime_class = "CalendarDateTime"; - - private static $can_be_root = false; - - public function getCMSFields() { - - $self = $this; - - $this->beforeUpdateCMSFields(function($f) use ($self) { - Requirements::javascript('event_calendar/javascript/calendar_cms.js'); - Requirements::css('event_calendar/css/calendar_cms.css'); - - $f->addFieldToTab("Root.Main", - TextField::create( - "Location", - _t('Calendar.LOCATIONDESCRIPTION','The location for this event') - ), 'Content' - ); - - $dt = _t('CalendarEvent.DATESANDTIMES','Dates and Times'); - $recursion = _t('CalendarEvent.RECURSION','Recursion'); - - $f->addFieldToTab("Root.$dt", - GridField::create( - "DateTimes", - _t('Calendar.DATETIMEDESCRIPTION','Add dates for this event'), - $self->DateTimes(), - GridFieldConfig_RecordEditor::create() - ) - ); - - $f->addFieldsToTab("Root.$recursion", array( - CheckboxField::create('Recursion',_t('CalendarEvent.REPEATEVENT','Repeat this event'))->addExtraClass('recursion'), - OptionsetField::create( - 'CustomRecursionType', - _t('CalendarEvent.DESCRIBEINTERVAL','Describe the interval at which this event recurs.'), - array ( - '1' => _t('CalendarEvent.DAILY','Daily'), - '2' => _t('CalendarEvent.WEEKLY','Weekly'), - '3' => _t('CalendarEvent.MONTHLY','Monthly') - ) - )->setHasEmptyDefault(true) - )); - - $f->addFieldToTab("Root.$recursion", $dailyInterval = FieldGroup::create( - LabelField::create($name = "every1", $title = _t("CalendarEvent.EVERY","Every ")), - DropdownField::create('DailyInterval', '', array_combine(range(1,10), range(1,10))), - LabelField::create($name = "days",$title = _t("CalendarEvent.DAYS"," day(s)")) - )); - - $f->addFieldToTab("Root.$recursion", $weeklyInterval = FieldGroup::create( - LabelField::create($name = "every2", $title = _t("CalendarEvent.EVERY","Every ")), - DropdownField::create('WeeklyInterval', '', array_combine(range(1,10), range(1,10))), - LabelField::create($name = "weeks", $title = _t("CalendarEvent.WEEKS", " weeks")) - )); - - $f->addFieldToTab("Root.$recursion", CheckboxSetField::create( - 'RecurringDaysOfWeek', - _t('CalendarEvent.ONFOLLOWINGDAYS','On the following day(s)...'), - DataList::create("RecurringDayOfWeek")->map("ID", "Title") - )); - - $f->addFieldToTab("Root.$recursion", $monthlyInterval = FieldGroup::create( - LabelField::create($name="every3", $title = _t("CalendarEvent.EVERY", "Every ")), - DropdownField::create('MonthlyInterval', '', array_combine(range(1,10), range(1,10))), - LabelField::create($name = "months", $title = _t("CalendarEvent.MONTHS", " month(s)")) - )); - - $f->addFieldsToTab("Root.$recursion", array ( - OptionsetField::create('MonthlyRecursionType1','', array('1' => _t('CalendarEvent.ONTHESEDATES','On these date(s)...')))->setHasEmptyDefault(true), - CheckboxSetField::create('RecurringDaysOfMonth', '', DataList::create("RecurringDayOfMonth")->map("ID", "Value")), - OptionsetField::create('MonthlyRecursionType2','', array('1' => _t('CalendarEvent.ONTHE','On the...')))->setHasEmptyDefault(true) - )); - - $f->addFieldToTab("Root.$recursion", $monthlyIndex = FieldGroup::create( - DropdownField::create('MonthlyIndex', '', array ( - '1' => _t('CalendarEvent.FIRST', 'First'), - '2' => _t('CalendarEvent.SECOND', 'Second'), - '3' => _t('CalendarEvent.THIRD', 'Third'), - '4' => _t('CalendarEvent.FOURTH', 'Fourth'), - '5' => _t('CalendarEvent.LAST', 'Last') - ))->setHasEmptyDefault(true), - DropdownField::create('MonthlyDayOfWeek','', DataList::create('RecurringDayOfWeek')->map('Value', 'Title'))->setHasEmptyDefault(true), - LabelField::create( $name = "ofthemonth", $title = _t("CalendarEvent.OFTHEMONTH"," of the month.")) - )); - $f->addFieldToTab("Root.$recursion", - GridField::create( - 'Exceptions', - _t('CalendarEvent.ANYEXCEPTIONS','Any exceptions to this pattern? Add the dates below.'), - $self->Exceptions(), - GridFieldConfig_RecordEditor::create() - ) - ); - $dailyInterval->addExtraClass('dailyinterval'); - $weeklyInterval->addExtraClass('weeklyinterval'); - $monthlyInterval->addExtraClass('monthlyinterval'); - $monthlyIndex->addExtraClass('monthlyindex'); - - }); - - $f = parent::getCMSFields(); - - return $f; - } - - public function getRecursionReader() { - return new RecursionReader($this); - } - - public function getDateTimeClass() { - return $this->stat('datetime_class'); - } - - public function CalendarWidget() { - return $this->Parent()->CalendarWidget(); - } - -} - -class CalendarEvent_Controller extends Page_Controller { - - public function init() { - parent::init(); - Requirements::themedCSS('calendar','event_calendar'); - } - - public function MultipleDates() { - return DataList::create($this->data()->getDateTimeClass()) - ->filter("EventID", $this->ID) - ->sort("\"StartDate\" ASC") - ->count() > 1; - } - - public function DateAndTime() { - return DataList::create($this->data()->getDateTimeClass()) - ->filter("EventID", $this->ID) - ->sort("\"StartDate\" ASC"); - } - - public function UpcomingDates($limit = 3) { - return DataList::create($this->data()->getDateTimeClass()) - ->filter("EventID", $this->ID) - ->where("\"StartDate\" >= DATE(NOW())") - ->sort("\"StartDate\" ASC") - ->limit($limit); - } - - public function OtherDates() { - if(!isset($_REQUEST['date'])) { - $date_obj = $this->DateAndTime()->first(); - if(!$date_obj) return false; - else $date = $date_obj->StartDate; - } - elseif(strtotime($_REQUEST['date']) > 0) { - $date = date('Y-m-d', strtotime($_REQUEST['date'])); - } - - $cal = $this->Parent(); - - if($this->Recursion == 1) { - $datetime_obj = DataList::create($this->data()->getDateTimeClass()) - ->where("EventID = {$this->ID}") - ->first(); - $datetime_obj->StartDate = $date; - return $cal->getNextRecurringEvents($this, $datetime_obj); - } - else { - return DataList::create($this->data()->getDateTimeClass()) - ->filter(array( - "EventID" => $this->ID - )) - ->exclude(array( - "StartDate" => $date - )) - ->sort("StartDate ASC") - ->limit($cal->OtherDatesCount); - } - return false; - } - - - - public function CurrentDate() { - $allDates = DataList::create($this->data()->getDateTimeClass()) - ->filter("EventID", $this->ID) - ->sort("\"StartDate\" ASC"); - if(!isset($_REQUEST['date'])) { - // If no date filter specified, return the first one - return $allDates->first(); - } elseif(strtotime($_REQUEST['date']) > 0) { - $date = date('Y-m-d', strtotime($_REQUEST['date'])); - if($this->Recursion) { - $datetime = $allDates->first(); - if($datetime) { - $datetime->StartDate = $date; - $datetime->EndDate = $date; - return $datetime; - } - } - return $allDates - ->filter("StartDate", $date) - ->first(); - } - } - -} diff --git a/code/CalendarTimeField.php b/code/CalendarTimeField.php deleted file mode 100755 index e8d0674..0000000 --- a/code/CalendarTimeField.php +++ /dev/null @@ -1,21 +0,0 @@ - 'text', - 'class' => 'text' . ($this->extraClass() ? $this->extraClass() : ''), - 'id' => $this->id(), - 'name' => $this->Name(), - 'value' => $this->attrValue(), - 'tabindex' => $this->getTabIndex(), - 'maxlength' => ($this->maxLength) ? $this->maxLength : null, - 'size' => ($this->maxLength) ? min( $this->maxLength, 30 ) : null - ); - - if($this->disabled) $attributes['disabled'] = 'disabled'; - - return $this->createTag('input', $attributes); - } -} diff --git a/code/CalendarUtil.php b/code/CalendarUtil.php deleted file mode 100755 index 68c9488..0000000 --- a/code/CalendarUtil.php +++ /dev/null @@ -1,215 +0,0 @@ - 0) { - while($missing > 0) {$str .= "01";$missing-=2;} - } - return substr($str,0,4) . "-" . substr($str,4,2) . "-" . substr($str,6,2); - } - else { - return date('Y-m-d'); - } - } - - static function get_date_string($start_date,$end_date) { - $strStartDate = null; - $strEndDate = null; - - $start = strtotime($start_date); - $end = strtotime($end_date); - - $start_year = date("Y", $start); - $start_month = date("m", $start); - - $end_year = date("Y", $end); - $end_month = date("m", $end); - - // Invalid date. Get me out of here! - if($start < 1) return; - - // Only one day long! - else if($start == $end || !$end || $end < 1) { - $key = self::ONE_DAY; - } - - else { - if($start_year == $end_year) { - $key = ($start_month == $end_month) ? self::SAME_MONTH_SAME_YEAR : self::DIFF_MONTH_SAME_YEAR; - } - else { - $key = self::DIFF_MONTH_DIFF_YEAR; - } - } - $date_string = self::localize($start, $end, $key); - $break = strpos($date_string, '$End'); - if($break !== FALSE) { - $strStartDate = substr($date_string, 0, $break); - $strEndDate = substr($date_string, $break+1, strlen($date_string) - strlen($strStartDate)); - return array($strStartDate, $strEndDate); - } - - return array($date_string, ""); - } - - public static function microformat($date, $time, $offset = null) { - if(!$date) - return ""; - - $ts = strtotime($date . " " . $time); - - if($ts < 1) - return ""; - - $ret = date('c', $ts); // ISO 8601 datetime - - if($offset) { - // Swap out timezine with specified $offset - $ret = preg_replace('/((\+)|(-))[\d:]*$/', $offset, $ret); - } - return $ret; - } - - public static function get_months_map($key = '%b') { - return array ( - '01' => strftime($key,strtotime('2000-01-01')), - '02' => strftime($key,strtotime('2000-02-01')), - '03' => strftime($key,strtotime('2000-03-01')), - '04' => strftime($key,strtotime('2000-04-01')), - '05' => strftime($key,strtotime('2000-05-01')), - '06' => strftime($key,strtotime('2000-06-01')), - '07' => strftime($key,strtotime('2000-07-01')), - '08' => strftime($key,strtotime('2000-08-01')), - '09' => strftime($key,strtotime('2000-09-01')), - '10' => strftime($key,strtotime('2000-10-01')), - '11' => strftime($key,strtotime('2000-11-01')), - '12' => strftime($key,strtotime('2000-12-01')) - ); - } - - public static function get_date_format() { - if($dateFormat = CalendarDateTime::config()->date_format_override) { - return $dateFormat; - } - return _t('CalendarDateTime.DATEFORMAT','mdy'); - } - - public static function get_time_format() { - if($timeFormat = CalendarDateTime::config()->time_format_override) { - return $timeFormat; - } - return _t('CalendarDateTime.TIMEFORMAT','24'); - } - - public static function get_first_day_of_week() { - $result = strtolower(_t('CalendarDateTime.FIRSTDAYOFWEEK','monday')); - return ($result == "monday") ? sfTime::MONDAY : sfTime::SUNDAY; - } - - public static function date_sort(&$data) { - uasort($data, array("CalendarUtil","date_sort_callback")); - } - - /** - * Callback used by column_sort - */ - public static function date_sort_callback($a, $b) { - if($a->StartDate == $b->StartDate) { - if($a->StartTime == $b->StartTime) - return 0; - else if(strtotime($a->StartTime) > strtotime($b->StartTime)) - return 1; - else - return -1; - } - else if(strtotime($a->StartDate) > strtotime($b->StartDate)) - return 1; - else - return -1; - - } - -} diff --git a/code/CalendarWidget.php b/code/CalendarWidget.php deleted file mode 100755 index 53640c9..0000000 --- a/code/CalendarWidget.php +++ /dev/null @@ -1,49 +0,0 @@ -calendar = $calendar; - } - - public function setOption($k, $v) { - $this->options[$k] = $v; - } - - public function getDataAttributes() { - $attributes = ""; - $this->options['url'] = $this->calendar->Link(); - - foreach($this->options as $opt => $value) { - $attributes .= sprintf('data-%s="%s" ', $opt, Convert::raw2att($value)); - } - return $attributes; - } - - public function setSelectionStart($date) { - $this->selectionStart = $date; - } - - public function setSelectionEnd($date) { - $this->selectionEnd = $date; - } - - public function forTemplate() { - Requirements::javascript(THIRDPARTY_DIR."/jquery/jquery.js"); - Requirements::javascript("event_calendar/javascript/calendar_widget.js"); - $locale_file = _t('Calendar.DATEJSFILE','calendar_en.js'); - Requirements::javascript("event_calendar/javascript/lang/{$locale_file}"); - Requirements::javascript("event_calendar/javascript/calendar_widget_init.js"); - Requirements::css("event_calendar/css/calendar_widget.css"); - return '
getDataAttributes() . '>
'; - } -} diff --git a/code/ICSFeed.php b/code/ICSFeed.php deleted file mode 100755 index 65df315..0000000 --- a/code/ICSFeed.php +++ /dev/null @@ -1,30 +0,0 @@ - 'Varchar(100)', - 'URL' => 'Varchar(255)' - ); - - private static $has_one = array ( - 'Calendar' => 'Calendar' - ); - - public function getCMSFields() { - $f = new FieldList ( - new TextField('Title',_t('ICSFeed.TITLEOFFEED','Title of feed')), - new TextField('URL',_t('ICSFeed.URLLINK','URL'),'http://') - ); - - $this->extend('updateCMSFields', $f); - - return $f; - } - - public function summaryFields() { - return array ( - 'Title' => _t('ICSFeed.TITLE','Title'), - ); - } -} diff --git a/code/RecurringDayOfMonth.php b/code/RecurringDayOfMonth.php deleted file mode 100755 index 79ec439..0000000 --- a/code/RecurringDayOfMonth.php +++ /dev/null @@ -1,54 +0,0 @@ - 'Int' - ); - - private static $belongs_many_many = array ( - 'CalendarEvent' => 'CalendarEvent' - ); - - private static $default_sort = "Value ASC"; - - static function create_default_records() { - for($i = 1; $i <= 31; $i++) { - $record = new RecurringDayOfMonth(); - $record->Value = $i; - $record->write(); - } - } - - public function requireDefaultRecords() { - parent::requireDefaultRecords(); - $records = DataList::create("RecurringDayOfMonth"); - if(!$records->exists()) { - self::create_default_records(); - } - elseif($records->count() != 31) { - foreach($records as $record) { - $record->delete(); - } - self::create_default_records(); - } - } - - - public function canCreate($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } - - public function canEdit($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } - - public function canDelete($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } - - public function canView($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } - -} diff --git a/code/RecurringDayOfWeek.php b/code/RecurringDayOfWeek.php deleted file mode 100755 index 4c66585..0000000 --- a/code/RecurringDayOfWeek.php +++ /dev/null @@ -1,57 +0,0 @@ - 'Int' - ); - - private static $default_sort = "Value ASC"; - - private static $belongs_many_many = array ( - 'CalendarEvent' => 'CalendarEvent' - ); - - static function create_default_records() { - for($i = 0; $i <= 6; $i++) { - $record = new RecurringDayOfWeek(); - $record->Value = $i; - $record->write(); - } - } - - public function requireDefaultRecords() { - parent::requireDefaultRecords(); - $records = DataList::create("RecurringDayOfWeek"); - if(!$records->exists()) { - self::create_default_records(); - } - elseif($records->count() != 7) { - foreach($records as $record) { - $record->delete(); - } - self::create_default_records(); - } - } - - public function getTitle() { - return strftime("%a", sfDate::getInstance()->nextDay($this->Value)->get()); - } - - - public function canCreate($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } - - public function canEdit($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } - - public function canDelete($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } - - public function canView($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } -} diff --git a/code/RecurringException.php b/code/RecurringException.php deleted file mode 100755 index c635f71..0000000 --- a/code/RecurringException.php +++ /dev/null @@ -1,55 +0,0 @@ - 'Date' - ); - - private static $has_one = array ( - 'CalendarEvent' => 'CalendarEvent' - ); - - - private static $default_sort = "ExceptionDate ASC"; - - - public function getCMSFields() { - DateField::set_default_config('showcalendar', true); - $f = new FieldList( - new DateField('ExceptionDate',_t('CalendarDateTime.EXCEPTIONDATE','Exception Date')) - ); - - $this->extend('updateCMSFields', $f); - - return $f; - } - - public function summaryFields() { - return array ( - 'FormattedExceptionDate' => _t('Calendar.EXCEPTIONDATE','Exception date') - ); - } - - public function getFormattedExceptionDate() { - if(!$this->ExceptionDate) return "--"; - return CalendarUtil::get_date_format() == "mdy" ? $this->obj('ExceptionDate')->Format('m-d-Y') : $this->obj('ExceptionDate')->Format('d-m-Y'); - } - - - public function canCreate($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } - - public function canEdit($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } - - public function canDelete($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } - - public function canView($member = null) { - return Permission::check("CMS_ACCESS_CMSMain"); - } -} diff --git a/code/RecursionReader.php b/code/RecursionReader.php deleted file mode 100755 index 7330a4f..0000000 --- a/code/RecursionReader.php +++ /dev/null @@ -1,107 +0,0 @@ -format('Y') * 12) + $dateObj1->format('n')) - (($dateObj2->format('Y') * 12) + $dateObj2->format('n')); - } - - public function __construct(CalendarEvent $event) { - $this->event = $event; - $this->datetimeClass = $event->Parent()->getDateTimeClass(); - $this->eventClass = $event->Parent()->getEventClass(); - $relation = $event->Parent()->getDateToEventRelation(); - - if($datetime = DataList::create($this->datetimeClass)->where("{$relation} = {$event->ID}")->first()) { - $this->ts = strtotime($datetime->StartDate); - } - - if($event->CustomRecursionType == 2) { - if($days_of_week = $event->getManyManyComponents('RecurringDaysOfWeek')) { - foreach($days_of_week as $day) { - $this->allowedDaysOfWeek[] = $day->Value; - } - } - } - - else if($event->CustomRecursionType == 3) { - if($days_of_month = $event->getManyManyComponents('RecurringDaysOfMonth')) { - foreach($days_of_month as $day) { - $this->allowedDaysOfMonth[] = $day->Value; - } - } - } - - if($exceptions = $event->getComponents('Exceptions')) { - foreach($exceptions as $exception) { - $this->exceptions[] = $exception->ExceptionDate; - } - } - } - - - - public function recursionHappensOn($ts) - { - - $objTestDate = new sfDate($ts); - $objStartDate = new sfDate($this->ts); - - // Current date is before the recurring event begins. - if($objTestDate->get() < $objStartDate->get()) - return false; - elseif(in_array($objTestDate->date(), $this->exceptions)) - return false; - - switch($this->event->CustomRecursionType) - { - // Daily - case 1: - return $this->event->DailyInterval ? (($ts - $this->ts) / self::DAY) % $this->event->DailyInterval == 0 : false; - break; - // Weekly - case 2: - return ((($objTestDate->firstDayOfWeek()->get() - $objStartDate->firstDayOfWeek()->get()) / self::WEEK) % $this->event->WeeklyInterval == 0) - && - (in_array($objTestDate->reset()->format('w'), $this->allowedDaysOfWeek)); - break; - // Monthly - case 3: - if(self::difference_in_months($objTestDate,$objStartDate) % $this->event->MonthlyInterval == 0) { - // A given set of dates in the month e.g. 2 and 15. - if($this->event->MonthlyRecursionType1 == 1) { - return (in_array($objTestDate->reset()->format('j'), $this->allowedDaysOfMonth)); - } - // e.g. "First Monday of the month" - elseif($this->event->MonthlyRecursionType2 == 1) { - // Last day of the month? - if($this->event->MonthlyIndex == 5) { - $targetDate = $objTestDate->addMonth()->firstDayOfMonth()->previousDay($this->event->MonthlyDayOfWeek)->dump(); - } - else { - $objTestDate->subtractMonth()->finalDayOfMonth(); - for($i=0; $i < $this->event->MonthlyIndex; $i++) { - $objTestDate->nextDay($this->event->MonthlyDayOfWeek)->dump(); - } - $targetDate = $objTestDate->dump(); - } - return $objTestDate->reset()->dump() == $targetDate; - } - } - return false; - } - } - -} diff --git a/code/sfdate/sfDate.php b/code/sfdate/sfDate.php deleted file mode 100755 index 13a0581..0000000 --- a/code/sfdate/sfDate.php +++ /dev/null @@ -1,253 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * - * sfDate class. - * - * A class for representing a date/time value as an object. - * - * This class allows for chainable calculations using the sfTime utility class. - * - * @package sfDateTimePlugin - * @author Stephen Riesenberg - * @version SVN: $Id$ - */ -class sfDate -{ - /** - * The timestamp for this sfDate instance. - */ - private $ts = null; - - /** - * The original timestamp for this sfDate instance. - */ - private $init = null; - - /** - * Retrieves a new instance of this class. - * - * NOTE: This is not the singleton pattern. Instead, it is for chainability ease-of-use. - * - * Example: - * - * echo sfDate::getInstance()->getFirstDayOfWeek()->addDay()->format('Y-m-d'); - * - * - * @param mixed timestamp, string, or sfDate object - * @return sfDate - */ - public static function getInstance($value = null) - { - return new sfDate($value); - } - - /** - * Construct an sfDate object. - * - * @param mixed timestamp, string, or sfDate object - */ - public function __construct($value = null) - { - $this->set($value); - } - - /** - * Format the date according to the date function. - * - * @return string - */ - public function format($format) - { - return date($format, $this->ts); - } - - /** - * Formats the date according to the format_date helper of the Date helper group. - * - * @return string - */ - public function date($format = 'd') - { - return date('Y-m-d', $this->ts); - } - - /** - * Formats the date according to the format_datetime helper of the Date helper group. - * - * @return string - */ - public function datetime($format = 'F') - { - return date('Y-m-d H:i:s', $this->ts); - } - - /** - * Format the date as a datetime value. - * - * @return string - */ - public function dump() - { - return date('Y-m-d H:i:s', $this->ts); - } - - /** - * Retrieves the given unit of time from the timestamp. - * - * @param int unit of time (accepts sfTime constants). - * @return int the unit of time - * - * @throws sfDateTimeException - */ - public function retrieve($unit = sfTime::DAY) - { - switch ($unit) - { - case sfTime::SECOND: - return date('s', $this->ts); - case sfTime::MINUTE: - return date('i', $this->ts); - case sfTime::HOUR: - return date('H', $this->ts); - case sfTime::DAY: - return date('d', $this->ts); - case sfTime::WEEK: - return date('W', $this->ts); - case sfTime::MONTH: - return date('m', $this->ts); - case sfTime::QUARTER: - return ceil(date('m', $this->ts) / 3); - case sfTime::YEAR: - return date('Y', $this->ts); - case sfTime::DECADE: - return ceil((date('Y', $this->ts) % 100) / 10); - case sfTime::CENTURY: - return ceil(date('Y', $this->ts) / 100); - case sfTime::MILLENIUM: - return ceil(date('Y', $this->ts) / 1000); - default: - throw new sfDateTimeException(sprintf('The unit of time provided is not valid: %s', $unit)); - } - } - - /** - * Retrieve the timestamp value of this sfDate instance. - * - * @return timestamp - */ - public function get() - { - return $this->ts; - } - - /** - * Sets the timestamp value of this sfDate instance. - * - * This function accepts several froms of a date value: - * - timestamp - * - string, parsed with strtotime - * - sfDate object - * - * @return sfDate the modified object, for chainability - */ - public function set($value = null) - { - $ts = sfDateTimeToolkit::getTS($value); - - $this->ts = $ts; - if ($this->init === null) - { - $this->init = $ts; - } - - return $this; - } - - /** - * Resets the timestamp value of this sfDate instance to its original value. - * - * @return sfDate the reset object, for chainability - */ - public function reset() - { - $this->ts = $this->init; - - return $this; - } - - /** - * Compares two date values. - * - * @param mixed timestamp, string, or sfDate object - * @return int -1, 0, or 1 - */ - public function cmp($value) - { - $ts = sfDateTimeToolkit::getTS($value); - - if ($this->ts < $ts) - { - // less than - return -1; - } - else if ($this->ts > $ts) - { - // greater than - return 1; - } - - // equal to - return 0; - } - - /** - * Gets the difference of two date values in seconds. - * - * @param mixed timestamp, string, or sfDate object - * @param int the difference in seconds - */ - public function diff($value) - { - $ts = sfDateTimeToolkit::getTS($value); - - return $this->ts - $ts; - } - - /** - * Call any function available in the sfTime library, but without the ts parameter. - * - * Example: - * - * $ts = sfTime::firstDayOfMonth(sfTime::addMonth(time(), 5)); - * // equivalent - * $dt = new sfDate(); - * $ts = $dt->addMonth(5)->firstDayOfMonth()->get(); - * - * - * @return sfDate the modified object, for chainability - */ - public function __call($method, $arguments) - { - $callable = array('sfTime', $method); - - if (!is_callable($callable)) - { - throw new sfDateTimeException(sprintf('Call to undefined function: %s::%s', 'sfDate', $method)); - } - - array_unshift($arguments, $this->ts); - - $this->ts = call_user_func_array($callable, $arguments); - - return $this; - } -} \ No newline at end of file diff --git a/code/sfdate/sfDateTimeToolkit.php b/code/sfdate/sfDateTimeToolkit.php deleted file mode 100755 index 3632fac..0000000 --- a/code/sfdate/sfDateTimeToolkit.php +++ /dev/null @@ -1,83 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * - * sfDateTimeToolkit class. - * - * A toolkit for the sfDateTimePlugin. - * - * @package sfDateTimePlugin - * @author Stephen Riesenberg - * @version SVN: $Id$ - */ -class sfDateTimeToolkit -{ - /** - * Breaks down the individual components of the timestamp. - * - * @param timestamp - * @return array - */ - public static function breakdown($ts = null) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - // gather individual variables - $H = date('H', $ts); // hour - $i = date('i', $ts); // minute - $s = date('s', $ts); // second - $m = date('m', $ts); // month - $d = date('d', $ts); // day - $Y = date('Y', $ts); // year - - return array($H, $i, $s, $m, $d, $Y); - } - - /** - * Returns the current timestamp. - * - * @return timestamp - * - * @see time - */ - public static function now() - { - return time(); - } - - /** - * Retrieve the timestamp from a number of different formats. - * - * @param mixed value to use for timestamp retrieval - */ - public static function getTS($value = null) - { - if ($value === null) - { - return sfDateTimeToolkit::now(); - } - else if ($value instanceof sfDate) - { - return $value->get(); - } - else if (!is_numeric($value)) - { - return strtotime($value); - } - else if (is_numeric($value)) - { - return $value; - } - - throw new sfDateTimeException(sprintf('A timestamp could not be retrieved from the value: %s', $value)); - } -} \ No newline at end of file diff --git a/code/sfdate/sfTime.php b/code/sfdate/sfTime.php deleted file mode 100755 index 34530be..0000000 --- a/code/sfdate/sfTime.php +++ /dev/null @@ -1,802 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * - * sfTime class. - * - * A library for manipulating dates in symfony (php). - * - * @package sfDateTimePlugin - * @author Stephen Riesenberg - * @version SVN: $Id$ - */ -class sfTime -{ - /** - * Units of time - */ - const SECOND = 0; - const MINUTE = 1; - const HOUR = 2; - const DAY = 3; - const WEEK = 4; - const MONTH = 5; - const QUARTER = 6; - const YEAR = 7; - const DECADE = 8; - const CENTURY = 9; - const MILLENIUM = 10; - - /** - * Days of the week - */ - const SUNDAY = 0; - const MONDAY = 1; - const TUESDAY = 2; - const WEDNESDAY = 3; - const THURSDAY = 4; - const FRIDAY = 5; - const SATURDAY = 6; - - /** - * Months of the year - */ - const JANUARY = 1; - const FEBRUARY = 2; - const MARCH = 3; - const APRIL = 4; - const MAY = 5; - const JUNE = 6; - const JULY = 7; - const AUGUST = 8; - const SEPTEMBER = 9; - const OCTOBER = 10; - const NOVEMBER = 11; - const DECEMBER = 12; - - /** - * Adds the specified number of given units of time to the given date. - * - * Example: - * - * // tomorrow - * $dt = sfTime::add(); - * // day after - * $dt = sfTime::add($mydate); - * // 5 days after - * $dt = sfTime::add($mydate, 5); - * // 2 months after - * $dt = sfTime::add($mydate, 2, sfTime::MONTH); - * // 4 weeks after - * $dt = sfTime::add($mydate, 4, sfTime::WEEK); - * - * - * @param timestamp a timestamp for the calculation - * @param int the number of units to add to the given date - * @param int the unit to add by - * @return timestamp the timestamp result of the calculation - * - * @throws sfDateTimeException - */ - public static function add($ts = null, $num = 1, $unit = sfTime::DAY) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - // gather individual variables for readability and maintainability - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - // determine which unit of time to add by - switch ($unit) - { - case sfTime::SECOND: - return mktime($H, $i, $s + $num, $m, $d, $Y); - case sfTime::MINUTE: - return mktime($H, $i + $num, $s, $m, $d, $Y); - case sfTime::HOUR: - return mktime($H + $num, $i, $s, $m, $d, $Y); - case sfTime::DAY: - return mktime($H, $i, $s, $m, $d + $num, $Y); - case sfTime::WEEK: - return mktime($H, $i, $s, $m, $d + (7 * $num), $Y); - case sfTime::MONTH: - return mktime($H, $i, $s, $m + $num, $d, $Y); - case sfTime::QUARTER: - return mktime($H, $i, $s, $m + (3 * $num), $d, $Y); - case sfTime::YEAR: - return mktime($H, $i, $s, $m, $d, $Y + $num); - case sfTime::DECADE: - return mktime($H, $i, $s, $m, $d, $Y + (10 * $num)); - case sfTime::CENTURY: - return mktime($H, $i, $s, $m, $d, $Y + (100 * $num)); - case sfTime::MILLENIUM: - return mktime($H, $i, $s, $m, $d, $Y + (1000 * $num)); - default: - throw new sfDateTimeException(sprintf('The unit of time provided is not valid: %s', $unit)); - } - } - - /** - * Subtracts the specified number of given units of time from the given date. - * - * Example: - * - * // yesterday - * $dt = sfTime::subtract(); - * // day before - * $dt = sfTime::subtract($mydate); - * // 5 days before - * $dt = sfTime::subtract($mydate, 5); - * // 2 months before - * $dt = sfTime::subtract($mydate, 2, sfTime::MONTH); - * // 4 weeks before - * $dt = sfTime::subtract($mydate, 4, sfTime::WEEK); - * - * - * @param timestamp a timestamp for the calculation - * @param int the number of units to add to the given date - * @param int the unit to add by - * @return timestamp the timestamp result of the calculation - * - * @see add - */ - public static function subtract($ts = null, $num = 1, $unit = sfTime::DAY) - { - return sfTime::add($ts, $num * -1, $unit); - } - - /** - * Returns the timestamp with the date but without the time of day. - * - * @param timestamp - * @return timestamp - */ - public static function clearTime($ts = null) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - return mktime(0, 0, 0, $m, $d, $Y); - } - - /** - * Returns the timestamp with the time of day but without the date. - * - * @deprecated This is a deprecated function. Do not use! - * - * @param timestamp - * @return timestamp - */ - public static function clearDate($ts = null) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - return mktime($H, $i, $s, 0, 0, 0); - } - - /** - * Clear the second value of this timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function clearSecond($ts = null) - { - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - return mktime($H, $i, 0, $m, $d, $Y); - } - - /** - * Clear the minute value of this timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function clearMinute($ts = null) - { - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - return mktime($H, 0, $s, $m, $d, $Y); - } - - /** - * Clear the hour value of this timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function clearHour($ts = null) - { - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - return mktime(0, $i, $s, $m, $d, $Y); - } - - /** - * Set the second value of this timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function setSecond($ts = null, $second = 0) - { - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - return mktime($H, $i, $second, $m, $d, $Y); - } - - /** - * Set the minute value of this timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function setMinute($ts = null, $minute = 0) - { - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - return mktime($H, $minute, $s, $m, $d, $Y); - } - - /** - * Set the hour value of this timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function setHour($ts = null, $hour = 0) - { - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - return mktime($hour, $i, $s, $m, $d, $Y); - } - - /** - * Set the day value of this timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function setDay($ts = null, $day = 1) - { - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - return mktime($H, $i, $s, $m, $day, $Y); - } - - /** - * Set the month value of this timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function setMonth($ts = null, $month = 1) - { - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - return mktime($H, $i, $s, $month, $d, $Y); - } - - /** - * Set the year value of this timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function setYear($ts = null, $year = 1970) - { - list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); - - return mktime($H, $i, $s, $m, $d, $year); - } - - /** - * Returns the timestamp for tomorrow. - * - * Alias for sfTime::addDay - * - * @param timestamp - * @return timestamp - */ - public static function tomorrow($ts = null) - { - return sfTime::add($ts); - } - - /** - * Returns the timestamp for yesterday. - * - * Alias for sfTime::subtractDay - * - * @param timestamp - * @return timestamp - */ - public static function yesterday($ts = null) - { - return sfTime::subtract($ts); - } - - /** - * Adds the specified number of seconds to the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function addSecond($ts = null, $num = 1) - { - return sfTime::add($ts, $num, sfTime::SECOND); - } - - /** - * Subtracts the specified number of seconds from the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function subtractSecond($ts = null, $num = 1) - { - return sfTime::subtract($ts, $num, sfTime::SECOND); - } - - /** - * Adds the specified number of minutes to the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function addMinute($ts = null, $num = 1) - { - return sfTime::add($ts, $num, sfTime::MINUTE); - } - - /** - * Subtracts the specified number of minutes from the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function subtractMinute($ts = null, $num = 1) - { - return sfTime::subtract($ts, $num, sfTime::MINUTE); - } - - /** - * Adds the specified number of hours to the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function addHour($ts = null, $num = 1) - { - return sfTime::add($ts, $num, sfTime::HOUR); - } - - /** - * Subtracts the specified number of hours from the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function subtractHour($ts = null, $num = 1) - { - return sfTime::subtract($ts, $num, sfTime::HOUR); - } - - /** - * Adds the specified number of days to the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function addDay($ts = null, $num = 1) - { - return sfTime::add($ts, $num, sfTime::DAY); - } - - /** - * Subtracts the specified number of days from the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function subtractDay($ts = null, $num = 1) - { - return sfTime::subtract($ts, $num, sfTime::DAY); - } - - /** - * Adds the specified number of weeks to the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function addWeek($ts = null, $num = 1) - { - return sfTime::add($ts, $num, sfTime::WEEK); - } - - /** - * Subtracts the specified number of weeks from the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function subtractWeek($ts = null, $num = 1) - { - return sfTime::subtract($ts, $num, sfTime::WEEK); - } - - /** - * Adds the specified number of months to the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function addMonth($ts = null, $num = 1) - { - return sfTime::add($ts, $num, sfTime::MONTH); - } - - /** - * Subtracts the specified number of months from the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function subtractMonth($ts = null, $num = 1) - { - return sfTime::subtract($ts, $num, sfTime::MONTH); - } - - /** - * Adds the specified number of quarters to the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function addQuarter($ts = null, $num = 1) - { - return sfTime::add($ts, $num, sfTime::QUARTER); - } - - /** - * Subtracts the specified number of quarters from the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function subtractQuarter($ts = null, $num = 1) - { - return sfTime::subtract($ts, $num, sfTime::QUARTER); - } - - /** - * Adds the specified number of years to the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function addYear($ts = null, $num = 1) - { - return sfTime::add($ts, $num, sfTime::YEAR); - } - - /** - * Subtracts the specified number of years from the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function subtractYear($ts = null, $num = 1) - { - return sfTime::subtract($ts, $num, sfTime::YEAR); - } - - /** - * Adds the specified number of decades to the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function addDecade($ts = null, $num = 1) - { - return sfTime::add($ts, $num, sfTime::DECADE); - } - - /** - * Subtracts the specified number of decades from the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function subtractDecade($ts = null, $num = 1) - { - return sfTime::subtract($ts, $num, sfTime::DECADE); - } - - /** - * Adds the specified number of centuries to the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function addCentury($ts = null, $num = 1) - { - return sfTime::add($ts, $num, sfTime::CENTURY); - } - - /** - * Subtracts the specified number of centuries from the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function subtractCentury($ts = null, $num = 1) - { - return sfTime::subtract($ts, $num, sfTime::CENTURY); - } - - /** - * Adds the specified number of millenia to the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function addMillenium($ts = null, $num = 1) - { - return sfTime::add($ts, $num, sfTime::MILLENIUM); - } - - /** - * Subtracts the specified number of millenia from the timestamp. - * - * @param timestamp - * @param int - * @return timestamp - */ - public static function subtractMillenium($ts = null, $num = 1) - { - return sfTime::subtract($ts, $num, sfTime::MILLENIUM); - } - - /** - * Returns the timestamp for first day of the week for the given date. - * - * @param timestamp - * @return timestamp - */ - public static function firstDayOfWeek($ts = null) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - return sfTime::subtractDay($ts, date('w', $ts)); - } - - /** - * Returns the timestamp for last day of the week for the given date. - * - * @param timestamp - * @return timestamp - */ - public static function finalDayOfWeek($ts = null) - { - return sfTime::subtractDay(sfTime::firstDayOfWeek(sfTime::addWeek($ts))); - } - - /** - * Returns the timestamp for first day of the month for the given date. - * - * @param timestamp - * @return timestamp - */ - public static function firstDayOfMonth($ts = null) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - return sfTime::subtractDay($ts, date('d', $ts) - 1); - } - - /** - * Returns the timestamp for last day of the month for the given date. - * - * @param timestamp - * @return timestamp - */ - public static function finalDayOfMonth($ts = null) - { - return sfTime::subtractDay(sfTime::firstDayOfMonth(sfTime::addMonth($ts))); - } - - /** - * Returns the timestamp for first day of thequarter for the given date. - * - * NOTE: Computes the quarter as: - * - * $quarter = ceil(date('m', $ts) / 3); // 1 - 4 - * - * - * @param timestamp - * @return timestamp - */ - public static function firstDayOfQuarter($ts = null) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - // variables for computation - $month = date('m', $ts); - $quarter = ceil($month / 3) - 1; // zero based quarter - - return sfTime::subtractMonth(sfTime::firstDayOfMonth($ts), $month - ($quarter * 3) - 1); - } - - /** - * Returns the timestamp for last day of the quarter for the given date. - * - * @param timestamp - * @return timestamp - */ - public static function finalDayOfQuarter($ts = null) - { - return sfTime::subtractDay(sfTime::firstDayOfQuarter(sfTime::addQuarter($ts))); - } - - /** - * Returns the timestamp for first day of the year for the given date. - * - * @param timestamp - * @return timestamp - */ - public static function firstDayOfYear($ts = null) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - return sfTime::subtractMonth(sfTime::firstDayOfMonth($ts), date('m', $ts) - 1); - } - - /** - * Returns the timestamp for last day of the year for the given date. - * - * @param timestamp - * @return timestamp - */ - public static function finalDayOfYear($ts = null) - { - return sfTime::subtractDay(sfTime::firstDayOfYear(sfTime::addYear($ts))); - } - - /** - * Returns the timestamp for the next occurance of [day]. - * - * @param timestamp - * @param int the day of week - * @return timestamp - */ - public static function nextDay($ts = null, $day = sfTime::SUNDAY) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - // get offsets from sunday - $offset1 = date('w', $ts); - $offset2 = $day; - - // adjust if date wraps into next week - $offset2 += $offset2 > $offset1 ? 0 : 7; - - return sfTime::addDay($ts, $offset2 - $offset1); - } - - /** - * Returns the timestamp for the most recent (previous) occurance of [day]. - * - * @param timestamp - * @param int the day of week - * @return timestamp - */ - public static function previousDay($ts = null, $day = sfTime::SUNDAY) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - // get offsets from sunday - $offset1 = date('w', $ts); - $offset2 = $day; - - // adjust if date wraps into last week - $offset1 += $offset1 > $offset2 ? 0 : 7; - - return sfTime::subtractDay($ts, $offset1 - $offset2); - } - - /** - * Returns the timestamp for the next occurance of [month]. - * - * @param timestamp - * @param int the month of year - * @return timestamp - */ - public static function nextMonth($ts = null, $month = sfTime::JANUARY) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - // get offsets from january - $offset1 = date('m', $ts); - $offset2 = $month; - - // adjust if date wraps into next year - $offset2 += $offset2 > $offset1 ? 0 : 12; - - return sfTime::addMonth($ts, $offset2 - $offset1); - } - - /** - * Returns the timestamp for the most recent (previous) occurance of [month]. - * - * @param timestamp - * @param int the month of year - * @return timestamp - */ - public static function previousMonth($ts = null, $month = sfTime::JANUARY) - { - // default to now - if ($ts === null) $ts = sfDateTimeToolkit::now(); - - // get offsets from january - $offset1 = date('m', $ts); - $offset2 = $month; - - // adjust if date wraps into last year - $offset1 += $offset1 > $offset2 ? 0 : 12; - - return sfTime::subtractMonth($ts, $offset1 - $offset2); - } -} \ No newline at end of file diff --git a/composer.json b/composer.json index 401617b..7a60e84 100644 --- a/composer.json +++ b/composer.json @@ -1,28 +1,38 @@ { - "name":"unclecheese/event-calendar", - "type": "silverstripe-module", - "description": "Event Calendar for the SilverStripe CMS", - "keywords": ["silverstripe", "events"], + "name":"unclecheese/silverstripe-event-calendar", + "type": "silverstripe-vendormodule", + "description": "Event calendar for the Silverstripe CMS", + "keywords": [ + "silverstripe", + "events", + "calendar" + ], "license": "BSD-3-Clause", "authors":[ { - "name": "Aaron Carlino", - "homepage": "http://leftandmain.com" + "name": "Aaron Carlino" + }, + { + "name": "Grant Heggie", + "email": "grant@grantheggie.design" } ], "require": { - "php": ">=5.3.2", - "silverstripe/framework": ">=3.1", - "silverstripe/cms": ">=3.1", - "johngrogg/ics-parser" : "1.0.3" + "silverstripe/cms": "^4", + "johngrogg/ics-parser": "^2", + "nesbot/carbon": "1.39.1" }, "support": { "issues": "https://github.com/unclecheese/silverstripe-event-calendar/issues" }, "extra": { - "installer-name": "event_calendar" + "expose": [ + "client" + ] }, "replace": { "silverstripe/event-calendar": "*" - } + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/css/calendar.css b/css/calendar.css deleted file mode 100755 index 8b4609d..0000000 --- a/css/calendar.css +++ /dev/null @@ -1,13 +0,0 @@ - -#event-calendar-events ul, .event-calendar-events ul li { list-style:none; margin:0; padding:0; } - -#event-calendar-events .vevent { padding:0 0 1em; margin:0 0 1em; border-bottom:1px solid #ccc; } - -.event-calendar-other-dates { margin:1em 0 0; } -.event-calendar-other-dates ul, .event-calendar-other-dates ul li { list-style:none; margin:0; padding:0; } -.event-calendar-other-dates h4 { margin:0; } - -.vevent .dates { font-weight:700; } - -.calendar-view-more.loading {color: #ccc;} - diff --git a/css/calendar_cms.css b/css/calendar_cms.css deleted file mode 100755 index 610b6b5..0000000 --- a/css/calendar_cms.css +++ /dev/null @@ -1,23 +0,0 @@ - -#Form_EditForm_RecurringDaysOfWeek { width:400px; } -#Form_EditForm_RecurringDaysOfMonth { width:360px; } -#Form_EditForm_RecurringDaysOfWeek li, #Form_EditForm_RecurringDaysOfMonth li { float:left; padding:2px 9px; } -#Form_EditForm_RecurringDaysOfWeek li:hover, #Form_EditForm_RecurringDaysOfMonth li:hover { background:#ccc; } - -#RecurringDaysOfMonth div.middleColumn, #RecurringDaysOfWeek div.middleColumn, #RecurringDaysOfMonth span.middleColumn, #RecurringDaysOfWeek span.middleColumn { overflow:hidden; } - -#RecurringDaysOfMonth .optionset li, #RecurringDaysOfWeek .optionset li { position: relative; width:50px; padding:0; margin:0; height:50px; text-align:center; font-weight:bold; padding-top: 0; } - -#RecurringDaysOfMonth .optionset li input, #RecurringDaysOfWeek .optionset li input { position: absolute; top: 5px; left: 50%; -webkit-transform: -webkit-translateX(-50%); -moz-transform: -moz-translateX(-50%); -ms-transform: -ms-translateX(-50%); -o-transform: -o-translateX(-50%); transform: translateX(-50%); } -#RecurringDaysOfMonth .optionset li label, #RecurringDaysOfWeek .optionset li label { display:block; height:30px; margin-top:0; text-align: center; padding: 27px 0 0 0; } - -#RecurringDaysOfMonth .optionset li.selected, #RecurringDaysOfWeek .optionset li.selected { background:#aaa; } - -#RecurringDaysOfMonth .optionset li.b-btm, #RecurringDaysOfWeek .optionset li.b-btm { border-bottom:1px solid #666; } -#RecurringDaysOfMonth .optionset li.b-top, #RecurringDaysOfWeek .optionset li.b-top { border-top:1px solid #666; } -#RecurringDaysOfMonth .optionset li.b-lft, #RecurringDaysOfWeek .optionset li.b-lft { border-left:1px solid #666; } -#RecurringDaysOfMonth .optionset li.b-rgt, #RecurringDaysOfWeek .optionset li.b-rgt { border-right:1px solid #666; } - -#Repeat_Alert_Message { background:#fff3a7; padding:2px 4px; border-top:1px solid #d3af22; border-bottom:1px solid #d3af22; } - -.optionset li.val { display: none; } \ No newline at end of file diff --git a/css/calendar_widget.css b/css/calendar_widget.css deleted file mode 100755 index cc9d124..0000000 --- a/css/calendar_widget.css +++ /dev/null @@ -1,19 +0,0 @@ - -.calendar-widget-table { margin:0 0 2em; } -.calendar-widget-table table { width:100%; border-collapse:collapse; border:1px solid #ddd; border-spacing:0; margin:0; } -.calendar-widget-table table td, .calendar-widget table th { border:1px solid #ddd; } - -.calendar-widget-table thead th { text-align:center; padding:5px; font-size:1.2em; } - -.calendar-widget-table tbody td { background-color:#fff; padding:4px; width:13%; font-size:1em; } - -.calendar-widget-table tbody .calendar-header td { background-color:#555; color:#fff; border-color:#555; } - -.calendar-widget-table tbody .calendar-day { text-align:right; } -.calendar-widget-table tbody .show-week { width:9%; text-align:center; } -.calendar-widget-table tbody .calendar-day:hover, -.calendar-widget tbody .show-week:hover, -.calendar-widget tbody .selected { background-color:#d9edf7; cursor:pointer; color:#3a87ad; } -.calendar-widget-table tbody .out-of-month { background-color:#eee; color:#999; } -.calendar-widget-table tbody .today { font-weight:700; color:#3a87ad; } -.calendar-widget-table tbody .hasEvent {background-image:url(../images/dot.png);background-repeat:no-repeat;background-position:center;} diff --git a/images/calendar_widget_bg.gif b/images/calendar_widget_bg.gif deleted file mode 100755 index 4a06e13..0000000 Binary files a/images/calendar_widget_bg.gif and /dev/null differ diff --git a/images/dot.png b/images/dot.png deleted file mode 100755 index 8dd57c1..0000000 Binary files a/images/dot.png and /dev/null differ diff --git a/javascript/calendar_cms.js b/javascript/calendar_cms.js deleted file mode 100755 index 9d02fb7..0000000 --- a/javascript/calendar_cms.js +++ /dev/null @@ -1,111 +0,0 @@ -(function($) { -$(function() { - - $('.field.defaultView select').entwine({ - onmatch: function() { - if ($(this).val() != 'upcoming') { - $('.field.defaultFutureMonths').hide(); - } - }, - onchange: function() { - $('.field.defaultFutureMonths').hide(); - if ($(this).val() == 'upcoming') { - $('.field.defaultFutureMonths').show(); - } - } - }); - - - $('.field.checkbox.recursion').entwine({ - onmatch: function() { - - var $tab = this.closest('.tab'); - var $recursion = this; - var $customRecursionType = $tab.find('#CustomRecursionType').hide(); - var $dailyInterval = $tab.find('.dailyinterval').hide(); - var $weeklyInterval = $tab.find('.weeklyinterval').hide(); - var $monthlyInterval = $tab.find('.monthlyinterval').hide(); - var $monthlyIndex = $tab.find('.monthlyindex').hide(); - var $recurringDaysOfWeek = $tab.find('#RecurringDaysOfWeek').hide(); - var $recurringDaysOfMonth = $tab.find('#RecurringDaysOfMonth').hide(); - var $monthlyRecursionType1 = $tab.find('#MonthlyRecursionType1').hide(); - var $monthlyRecursionType2 = $tab.find('#MonthlyRecursionType2').hide(); - - var resetPanels = function () { - $dailyInterval.hide(); - $weeklyInterval.hide(); - $monthlyInterval.hide(); - $recurringDaysOfWeek.hide(); - $recurringDaysOfMonth.hide().find(':checkbox').attr('disabled', true); - $monthlyRecursionType1.hide(); - $monthlyRecursionType2.hide(); - $monthlyIndex.hide().find('select').attr('disabled', true); - }; - - var resetSubPanels = function () { - $recurringDaysOfMonth.hide().find(':checkbox').attr('disabled', true); - $monthlyIndex.hide().find('select').attr('disabled', true); - }; - - $recursion.find('input').change(function() { - if($(this).is(':checked')) { - $customRecursionType.show(); - } - else { - $tab.find(':checkbox, :radio').attr('checked', false); - $customRecursionType.hide(); - resetPanels(); - } - }).change(); - - $customRecursionType.find('input').change(function() { - if($(this).is(':checked')) { - resetPanels(); - switch($(this).val()) { - case "1": - $dailyInterval.show(); - break; - - case "2": - $weeklyInterval.show(); - $recurringDaysOfWeek.show(); - break; - - case "3": - $monthlyInterval.show(); - $monthlyRecursionType1.show(); - $monthlyRecursionType2.show(); - break; - } - } - }).change(); - - $monthlyRecursionType1.find('input').change(function() { - if($(this).is(':checked')) { - - resetSubPanels(); - $recurringDaysOfMonth.show(); - - $recurringDaysOfMonth.find(':checkbox').attr('disabled', false); - $monthlyIndex.find('select').attr('disabled', true); - $monthlyRecursionType2.find('input').attr('checked', false).change(); - } - }).change(); - - $monthlyRecursionType2.find('input').change(function() { - if($(this).is(':checked')) { - - resetSubPanels(); - $monthlyIndex.show(); - - $recurringDaysOfMonth.find(':checkbox').attr('disabled', true); - $monthlyIndex.find('select').attr('disabled', false); - $monthlyRecursionType1.find('input').attr('checked', false).change(); - } - }).change(); - - } - }); - -}); -})(jQuery); \ No newline at end of file diff --git a/javascript/live_calendar_widget.js b/javascript/live_calendar_widget.js deleted file mode 100755 index 4893d7a..0000000 --- a/javascript/live_calendar_widget.js +++ /dev/null @@ -1,20 +0,0 @@ -(function($){ - $(function(){ - var refreshLink = function() { - $('#live-calendar-widget').load($(this).attr('href'),bind); - return false; - } - var refreshSelect = function() { - $t = $(this); - if($t.val().match('LiveCalendarWidget_Controller')) - $('#live-calendar-widget').load($t.val(),bind); - else - document.location = $t.val(); - } - var bind = function() { - $('.month-nav').click(refreshLink); - $('#live-calendar-widget-navigator').change(refreshSelect); - } - bind(); - }); -})(jQuery); \ No newline at end of file diff --git a/javascript/month_navigator.js b/javascript/month_navigator.js deleted file mode 100755 index d450ef9..0000000 --- a/javascript/month_navigator.js +++ /dev/null @@ -1,6 +0,0 @@ -jQuery(function() { - jQuery('#MonthNavigator').change(function() { - navigateToDate(this.value); - }); -}); - diff --git a/lang/de.yml b/lang/de.yml index 99f203d..60b1918 100755 --- a/lang/de.yml +++ b/lang/de.yml @@ -1,5 +1,5 @@ de: - Calendar: + UncleCheese\EventCalendar\Pages\Calendar: OneDay: '$StartDayNumberShort. $StartMonthNameShort. $StartYearLong' #e.g. 4. Okt. 2009 SameMonthSameYear: '$StartDayNumberShort. - $EndDayNumberShort. $StartMonthNameShort. $EndYearLong' #e.g. 4. - 6. Okt. 2009 DiffMonthSameYear: '$StartDayNumberShort. $StartMonthNameShort - $EndDayNumberShort. $EndMonthNameShort. $EndYearFull' #e.g. 4. Okt. - 6. Nov. 2009 @@ -7,15 +7,23 @@ de: OneDayHeader: '$StartDayNumberShort. $StartMonthNameLong $StartYearLong' #"Headers" control the display when a date range is given to the calendar through the URL. MonthHeader: '$StartMonthNameLong $StartYearLong' YearHeader: '$StartYearLong' - Calendar: NUMBEROFEVENTS: 'Anzahl anzuzeigender Anlässe (Standard-Ansicht).' DEFAULTDATEHEADER: 'Standard-Titel (wird angezeigt wenn kein Datum selektiert worden ist).' NUMBERFUTUREDATES: 'Maximal anzuzeigende Daten bei sich wiederholenden Anlässen' UPCOMINGEVENTSFOR: 'Kommende Anlässe für %s' FILTER: 'Filtern' - CalendarDateTime: + ADD: 'Zu meinem Kalender hinzufügen' + ALLDAY: 'Den ganzen Tag' + NOEVENTS: 'Es wurden keine Anlässe gefunden.' + SUBSCRIBE: 'Diesen Kalender abonnieren.' + BROWSECALENDAR: 'Durchsuche den Kalender' + USECALENDAR: 'Kalender benutzen um Anlässe zu finden.' + TIME: 'Zeit' + MORE: 'mehr...' + SEEALSO: 'Siehe auch' + UncleCheese\EventCalendar\Models\CalendarDateTime: INVALIDFORMAT: 'Ungültiges Datums-Format. Gültig sind "dmy" oder "mdy".' - CalendarEvent: + UncleCheese\EventCalendar\Pages\CalendarEvent: REPEATEVENT: 'Wiederhole diesen Anlass' DESCRIBEINTERVAL: 'Definiere den Zeitraum in dem dieser Anlass wieder auftritt:' EVERY: 'Jede(n) ' @@ -29,22 +37,11 @@ de: ANYEXCEPTIONS: 'Gibt es Ausnahmen bei den sich wiederholenden Anlässen? Wenn ja, allfällige Ausnahmen unten eintragen.' DATE: 'Datum' RSSFEED: 'RSS-Feed dieses Kalenders' - Calendar.ss: - BROWSECALENDAR: 'Durchsuche den Kalender' - USECALENDAR: 'Kalender benutzen um Anlässe zu finden.' - SUBSCRIBE: 'Diesen Kalender abonnieren.' - ALLDAY: 'Den ganzen Tag' - TIME: 'Zeit' - MORE: 'mehr...' - SEEALSO: 'Siehe auch' - ADD: 'Zu meinem Kalender hinzufügen' - NOEVENTS: 'Es wurden keine Anlässe gefunden.' - CalendarEvent.ss: BROWSECALENDAR: $lang['de_DE']['Calendar.ss']['BROWSECALENDAR'] USECALENDAR: $lang['de_DE']['Calendar.ss']['USECALENDAR'] FILTERCALENDAR: 'Kalender filtrieren' BACKTO: 'Zurück zu' SUBSCRIBE: $lang['de_DE']['Calendar.ss']['SUBSCRIBE'] ADDITIONALDATES: 'Zusätzliche Daten' - CalendarWidget: + UncleCheese\EventCalendar\Views\CalendarWidget: LOCALEFILE: 'date_de.js' diff --git a/lang/en.yml b/lang/en.yml index 3e1dfbd..11de518 100755 --- a/lang/en.yml +++ b/lang/en.yml @@ -1,5 +1,5 @@ en: - Calendar: + UncleCheese\EventCalendar\Pages\Calendar: OneDay: '$StartDayNameShort, $StartMonthNameShort. $StartDayNumberShort, $StartYearLong' SameMonthSameYear: '$StartMonthNameShort. $StartDayNumberShort — $EndDayNumberShort, $EndYearLong' DiffMonthSameYear: '$StartMonthNameShort. $StartDayNumberShort — $EndMonthNameShort. $EndDayNumberShort, $EndYearLong' @@ -7,7 +7,7 @@ en: OneDayHeader: '$StartMonthNameLong $StartDayNumberShort$StartDaySuffix, $StartYearLong' MonthHeader: '$StartMonthNameLong, $StartYearLong' YearHeader: '$StartYearLong' - CalendarDateTime: + UncleCheese\EventCalendar\Models\CalendarDateTime: DATEFORMAT: mdy TIMEFORMAT: 12 FIRSTDAYOFWEEK: Sunday diff --git a/lang/fi.yml b/lang/fi.yml index 2752b18..cfa5a00 100644 --- a/lang/fi.yml +++ b/lang/fi.yml @@ -1,5 +1,5 @@ fi: - Calendar: + UncleCheese\EventCalendar\Pages\Calendar: OneDay: '$StartDayNameLong, $StartDayNumberShort.$StartMonthNumberShort.$StartYearLong' SameMonthSameYear: '$StartDayNumberShort.-$EndDayNumberShort.$StartMonthNumberShort.$EndYearLong' DiffMonthSameYear: '$StartMonthNameShort. $StartDayNumberShort - $EndMonthNameShort. $EndDayNumberShort, $EndYearLong' @@ -28,13 +28,21 @@ fi: PREVIOUSMONTH: "Edellinen kuukausi" NEXTMONTH: "Seuraava kuukausi" PREVIOUSWEEKEND: "Edellinen viikonloppu" - NEXTWEEKEND: "Seuraava viikonloppu" - CalendarDateTime: + NEXTWEEKEND: "Seuraava viikonloppu" + BROWSECALENDAR: 'Selaa kalenteria' + USECALENDAR: 'KÄytä kalenteria' + SUBSCRIBE: 'Tilaa RSS-syöte.' + ALLDAY: 'Koko päivä' + TIME: 'Aika' + MORE: 'Lisää...' + SEEALSO: 'Katso myös' + NOEVENTS: 'Ei tapahtumia' + UncleCheese\EventCalendar\Models\CalendarDateTime: DATEFORMAT: DMy TIMEFORMAT: 24 FIRSTDAYOFWEEK: Monday INVALIDFORMAT: 'Epäkelpo formaatti.' - CalendarEvent: + UncleCheese\EventCalendar\Pages\CalendarEvent: REPEATEVENT: 'Toistuva tapahtuma' DESCRIBEINTERVAL: 'Toistuvuus:' EVERY: 'Joka ' @@ -50,41 +58,12 @@ fi: RSSFEED: 'RSS-styöte' ADD: 'Lisää tapahtuma omaan kalenteriisi' ADDITIONALDATES: 'Tapahtuman muut päivät' - Calendar.ss: - BROWSECALENDAR: 'Selaa kalenteria' - USECALENDAR: 'KÄytä kalenteria' - SUBSCRIBE: 'Tilaa RSS-syöte.' - ALLDAY: 'Koko päivä' - TIME: 'Aika' - MORE: 'Lisää...' - SEEALSO: 'Katso myös' - NOEVENTS: 'Ei tapahtumia' - CalendarEvent.ss: BROWSECALENDAR: 'Selaa kalenteria' USECALENDAR: 'Käytä kalentria' FILTERCALENDAR: 'Suodata' BACKTO: 'Takaisin' SUBSCRIBE: 'Tilaa kalenteri.' ADDITIONALDATES: 'Lisäpäivät' - ADD: 'Lisää kalenteriin' LOCALEFILE: date_fi.js - ADDITIONALDATES: 'Tapahtuman muut päivät' - EventList.ss: - ADD: 'Lisää kalenteriin' - MORE: 'Lisää' - ALLDAY: 'Koko päivä' - EventList: - ADD: 'Lisää kalenteriisi' - MORE: 'Lue lisää tapahtumasta' - ALLDAY: 'Koko päivä' - QuickNav.ss: - PREVIOUSDAY: "Edellinen päivä" - NEXTDAY: "Seuraava päivä" - PREVIOUSWEEK: "Edellinen viikko" - NEXTWEEK: "Seuraava viikko" - PREVIOUSMONTH: "Edellinen kuukausi" - NEXTMONTH: "Seuraava kuukausi" - PREVIOUSWEEKEND: "Edellinen viikonloppu" - NEXTWEEKEND: "Seuraava viikonloppu" - CalendarWidget: + UncleCheese\EventCalendar\Views\CalendarWidget: LOCALEFILE: 'date_fi.js' \ No newline at end of file diff --git a/lang/nb.yml b/lang/nb.yml index ec39da3..6983412 100644 --- a/lang/nb.yml +++ b/lang/nb.yml @@ -1,5 +1,5 @@ nb: - Calendar: + UncleCheese\EventCalendar\Pages\Calendar: OneDay: '$StartDayNumberShort. $StartMonthNameShort. $StartYearLong' #e.g. 4. Okt. 2009 SameMonthSameYear: '$StartDayNumberShort. - $EndDayNumberShort. $StartMonthNameShort. $EndYearLong' #e.g. 4. - 6. Okt. 2009 DiffMonthSameYear: '$StartDayNumberShort. $StartMonthNameShort - $EndDayNumberShort. $EndMonthNameShort. $EndYearLong' #e.g. 4. Okt. - 6. Nov. 2009 @@ -30,10 +30,19 @@ nb: MORE: 'Les mer…' ADDITIONALDATES: 'Ytterligere datoer for denne begivenheten' VIEWMOREEVENTS: 'Se flere begivenheter...' - DATEJSFILE: 'calendar_nb.js' - CalendarDateTime: + BROWSECALENDAR: 'Bla i kalender' + USECALENDAR: 'Bruk kalenderen.' + SUBSCRIBE: 'Abonnér på denne kalenderen.' + TIME: 'Tid' + MORE: 'Les mer...' + SEEALSO: 'Se også' + NOEVENTS: 'Ingen begivenheter funnet.' + UncleCheese\EventCalendar\Models\CalendarDateTime: INVALIDFORMAT: 'Ugyldig datoformat. Gyldig: "dmy" eller "mdy".' - CalendarEvent: + DATEFORMAT: dmy + TIMEFORMAT: 24 + FIRSTDAYOFWEEK: Mandag + UncleCheese\EventCalendar\Pages\CalendarEvent: REPEATEVENT: 'Gjenta begivenheten' DESCRIBEINTERVAL: 'Definér intervallet:' EVERY: 'Hver ' @@ -49,24 +58,11 @@ nb: RSSFEED: 'RSS-feed for denne kalenderen' ADD: 'Legg til i kalender' ADDITIONALDATES: 'Flere datoer' - Calendar.ss: - BROWSECALENDAR: 'Bla i kalender' - USECALENDAR: 'Bruk kalenderen.' - SUBSCRIBE: 'Abonnér på denne kalenderen.' - ALLDAY: 'Hele dagen' - TIME: 'Tid' - MORE: 'Les mer...' - SEEALSO: 'Se også' - NOEVENTS: 'Ingen begivenheter funnet.' - CalendarEvent.ss: BROWSECALENDAR: $lang['nb_NO']['Calendar.ss']['BROWSECALENDAR'] USECALENDAR: $lang['nb_NO']['Calendar.ss']['USECALENDAR'] FILTERCALENDAR: 'Filtrér kalenderen' BACKTO: 'Tilbake til' SUBSCRIBE: $lang['nb_NO']['Calendar.ss']['SUBSCRIBE'] - CalendarDateTime: - DATEFORMAT: dmy - TIMEFORMAT: 24 - FIRSTDAYOFWEEK: Mandag - CalendarWidget: - LOCALEFILE: 'date_nb.js' \ No newline at end of file + UncleCheese\EventCalendar\Views\CalendarWidget: + LOCALEFILE: 'date_nb.js' + DATEJSFILE: 'unclecheese/silverstripe-event-calendar:client/dist/js/lang/calendar_nb.js' \ No newline at end of file diff --git a/lang/nl.yml b/lang/nl.yml index ac1a626..1c11eae 100644 --- a/lang/nl.yml +++ b/lang/nl.yml @@ -1,5 +1,5 @@ nl: - Calendar: + UncleCheese\EventCalendar\Pages\Calendar: PLURALNAME: Kalenders SINGULARNAME: Kalender DESCRIPTION: Agenda met verzameling kalender items en aankondigingen @@ -35,26 +35,35 @@ nl: NUMBERFUTUREDATES: Aantal toekomstige data voor herhalende evenementen FEEDS: ICS Feeds ICSFEEDDESCRIPTION: Voeg een ICS feeds toe aan je kalender voor het importeren van een externe kalender, bijvoorbeeld Google Calendar - RSSTITLE: Titel van RSS Feed - CalendarAnnouncement: + RSSTITLE: Titel van RSS Feed + BROWSECALENDAR: 'Doorblader de Kalender' + USECALENDAR: 'Kalender gebruiken.' + SUBSCRIBE: 'Op deze kalender abonneren.' + ALLDAY: 'De hele dag' + TIME: Tijd + MORE: meer... + SEEALSO: 'Zie ook' + ADD: 'Aan mijn kalender toevoegen' + NOEVENTS: 'Geen events gevonden.' + UncleCheese\EventCalendar\Models\CalendarAnnouncement: PLURALNAME: Aankondigingen SINGULARNAME: Aankondiging TITLE: Naam CONTENT: Omschrijving - ICSFeed: + UncleCheese\EventCalendar\Models\ICSFeed: PLURALNAME: ICS Feeds SINGULARNAME: ICS Feed TITLE: Titel TITLEOFFEED: Titel van Feed URLLINK: URL - CalendarDateTime: + UncleCheese\EventCalendar\Models\CalendarDateTime: INVALIDFORMAT: 'Foutief datumformaat. Gebruik "dmy" of "mdy".' STARTDATE: Startdatum ENDDATE: Einddatum STARTTIME: Starttijd ENDTIME: Eindtijd ALLDAY: Hele dag - CalendarEvent: + UncleCheese\EventCalendar\Pages\CalendarEvent: PLURALNAME: Kalender items SINGULARNAME: Kalender item DESCRIPTION: Individueel kalender item @@ -71,17 +80,6 @@ nl: ANYEXCEPTIONS: 'Zijn er uitzonderingen bij het herhalen? Zo ja, vul deze hieronder in.' DATE: Datum RSSFEED: 'RSS-Feed van deze kalender' - Calendar.ss: - BROWSECALENDAR: 'Doorblader de Kalender' - USECALENDAR: 'Kalender gebruiken.' - SUBSCRIBE: 'Op deze kalender abonneren.' - ALLDAY: 'De hele dag' - TIME: Tijd - MORE: meer... - SEEALSO: 'Zie ook' - ADD: 'Aan mijn kalender toevoegen' - NOEVENTS: 'Geen events gevonden.' - CalendarEvent.ss: BROWSECALENDAR: 'Doorblader de Kalender' USECALENDAR: 'Kalender gebruiken.' FILTERCALENDAR: 'Kalender filteren' @@ -89,5 +87,5 @@ nl: SUBSCRIBE: 'Op deze kalender abonneren.' ADDITIONALDATES: 'Extra datums' ADD: 'Aan mijn kalender toevoegen' - CalendarWidget: + UncleCheese\EventCalendar\Views\CalendarWidget: LOCALEFILE: date_nl.js diff --git a/lang/sk.yml b/lang/sk.yml index 3eb7474..4fdb4e1 100644 --- a/lang/sk.yml +++ b/lang/sk.yml @@ -1,12 +1,12 @@ sk: - Calendar: + UncleCheese\EventCalendar\Pages\Calendar: ADD: 'Pridať do môjho kalendára (ICS)' ADDITIONALDATES: 'Dalšie dátumy pre túto udalosť' ALLDAY: 'Celý deň' ANNOUNCEMENTDESCRIPTION: 'Oznámenia sú jednoduché záznamy bez stránky s podrobnosťami, ktoré môžete pridať do kalendára (napr. "Kancelária zatvorená)"' Announcements: 'Oznámenia' CONFIGURATION: 'Konfigurácia' - DATEJSFILE: 'calendar_sk.js' + DATEJSFILE: 'unclecheese/silverstripe-event-calendar:client/dist/js/lang/calendar_sk.js' DATETIMEDESCRIPTION: 'Pridať termíny pre túto udalosť' DEFAULTDATEHEADER: 'Predvolený titulok (zobrazuje sa keď nie je vybraté žiadne časové obdobie)' DEFAULTFUTUREMONTHS: 'Maximálny počet budúcich mesiacov na predvolenom zobrazení' @@ -31,6 +31,7 @@ sk: NEXTMONTH: 'Nasledujúci mesiac' NEXTWEEK: 'Nasledujúci týždeň' NEXTWEEKEND: 'Nasledujúci víkend' + NOEVENTS: 'K dispozícií nie sú žiadne udalosti.' NUMBERFUTUREDATES: 'Počet budúcich dátumov, ktoré sa zobrazujú pre opakujúce udalosti' OneDay: '$StartDayNameLong, $StartDayNumberLong. $StartMonthNameLong $StartYearLong' OneDayHeader: '$StartDayNameLong, $StartDayNumberLong. $StartMonthNumberLong. $StartYearLong' @@ -48,6 +49,7 @@ sk: SINGULARNAME: 'Kalendár' STARTDATE: 'Dátum začiatku' STARTTIME: 'Čas začiatku' + SUBSCRIBE: 'Kalendár RSS' TODAYVIEW: "Zobraziť dnešné udalosti. Ak nie sú, zobraziť na tento týždeň." UPCOMINGEVENTSFOR: 'Pripravované udalosti pre %s' UPCOMINGVIEW: "Zobraziť list pripravovaných udalostí." @@ -55,7 +57,7 @@ sk: WEEKENDVIEW: "Zobraziť udalosti na tento víkend." WEEKVIEW: "Zobraziť udalosti na tento týždeň. Ak nie sú, zobraziť na tento mesiac." YearHeader: '$StartYearLong' - CalendarEvent: + UncleCheese\EventCalendar\Pages\CalendarEvent: ADD: 'Pridať do môjho kalendára (ICS)' ADDITIONALDATES: 'Dalšie dátumy pre túto udalosť' ANYEXCEPTIONS: 'Existujú výnimky pri opakovaní udalosti? Ak áno, pridajte termíny nižšie.' @@ -82,7 +84,7 @@ sk: THIRD: 'Tretiu' WEEKLY: 'Týždenne' WEEKS: 'týždňov' - CalendarDateTime: + UncleCheese\EventCalendar\Models\CalendarDateTime: ALLDAY: 'Udalosť trvá celý deň' DATEFORMAT: dmy ENDDATE: 'Dátum ukončenia' @@ -94,20 +96,17 @@ sk: STARTDATE: 'Dátum začiatku' STARTTIME: 'Čas začiatku' TIMEFORMAT: 24 - CalendarAnnouncement: + UncleCheese\EventCalendar\Models\CalendarAnnouncement: CONTENT: 'Obsah oznámenia' TITLE: 'Názov oznámenia' PLURALNAME: 'Oznámenia' SINGULARNAME: 'Oznámenie' - ICSFeed: + UncleCheese\EventCalendar\Models\ICSFeed: TITLE: 'Názov' TITLEOFFEED: 'Názov kanálu' PLURALNAME: 'ICS kanály' SINGULARNAME: 'ICS kanál' URLLINK: 'URL adresa' - Calendar.ss: - NOEVENTS: 'K dispozícií nie sú žiadne udalosti.' - SUBSCRIBE: 'Kalendár RSS' LiveCalendarWidget.ss: JUMPTOMONTH: 'Prejsť na...' QUICKLINKS: 'Rýchle odkazy' diff --git a/src/Forms/CalendarTimeField.php b/src/Forms/CalendarTimeField.php new file mode 100644 index 0000000..d60731c --- /dev/null +++ b/src/Forms/CalendarTimeField.php @@ -0,0 +1,27 @@ + 'text', + 'class' => 'text' . ($this->extraClass() ? $this->extraClass() : ''), + 'id' => $this->id(), + 'name' => $this->Name(), + 'value' => $this->attrValue(), + 'tabindex' => $this->getTabIndex(), + 'maxlength' => ($this->maxLength) ? $this->maxLength : null, + 'size' => ($this->maxLength) ? min( $this->maxLength, 30 ) : null + ]; + + if ($this->disabled) { + $attributes['disabled'] = 'disabled'; + } + + return $this->createTag('input', $attributes); + } +} diff --git a/src/Helpers/CalendarUtil.php b/src/Helpers/CalendarUtil.php new file mode 100644 index 0000000..d7d583e --- /dev/null +++ b/src/Helpers/CalendarUtil.php @@ -0,0 +1,263 @@ + 0) { + while ($missing > 0) { + $str .= "01"; + $missing -= 2; + } + } + return substr($str,0,4) . "-" . substr($str,4,2) . "-" . substr($str,6,2); + } + + return date('Y-m-d'); + } + + /** + * @return array|null + */ + public static function get_date_string($startDate, $endDate) + { + $strStartDate = null; + $strEndDate = null; + + $start = strtotime($startDate); + $end = strtotime($endDate); + + $startYear = date("Y", $start); + $startMonth = date("m", $start); + + $endYear = date("Y", $end); + $endMonth = date("m", $end); + + // Invalid date. Get me out of here! + if ($start < 1) { + return; + } + + // Only one day long! + if ($start == $end || !$end || $end < 1) { + $key = self::ONE_DAY; + } elseif ($startYear == $endYear) { + $key = ($startMonth == $endMonth) ? self::SAME_MONTH_SAME_YEAR : self::DIFF_MONTH_SAME_YEAR; + } else { + $key = self::DIFF_MONTH_DIFF_YEAR; + } + $dateString = self::localize($start, $end, $key); + $break = strpos($dateString, '$End'); + if ($break !== false) { + $strStartDate = substr($dateString, 0, $break); + $strEndDate = substr($dateString, $break+1, strlen($dateString) - strlen($strStartDate)); + return [$strStartDate, $strEndDate]; + } + + return [$dateString, ""]; + } + + /** + * @return string + */ + public static function microformat($date, $time, $offset = null) + { + if (!$date) { + return ""; + } + $ts = strtotime($date . " " . $time); + if ($ts < 1) { + return ""; + } + $ret = date('c', $ts); // ISO 8601 datetime + if ($offset) { + // Swap out timezine with specified $offset + $ret = preg_replace('/((\+)|(-))[\d:]*$/', $offset, $ret); + } + return $ret; + } + + /** + * @return array + */ + public static function get_months_map($key = '%b') + { + return [ + '01' => strftime($key, strtotime('2000-01-01')), + '02' => strftime($key, strtotime('2000-02-01')), + '03' => strftime($key, strtotime('2000-03-01')), + '04' => strftime($key, strtotime('2000-04-01')), + '05' => strftime($key, strtotime('2000-05-01')), + '06' => strftime($key, strtotime('2000-06-01')), + '07' => strftime($key, strtotime('2000-07-01')), + '08' => strftime($key, strtotime('2000-08-01')), + '09' => strftime($key, strtotime('2000-09-01')), + '10' => strftime($key, strtotime('2000-10-01')), + '11' => strftime($key, strtotime('2000-11-01')), + '12' => strftime($key, strtotime('2000-12-01')) + ]; + } + + /** + * @return string + */ + public static function get_date_format() + { + if ($dateFormat = CalendarDateTime::config()->date_format_override) { + return $dateFormat; + } + return _t(__CLASS__.'.DATEFORMAT', 'mdy'); + } + + /** + * @return string + */ + public static function get_time_format() + { + if ($timeFormat = CalendarDateTime::config()->time_format_override) { + return $timeFormat; + } + return _t(__CLASS__.'.TIMEFORMAT', '24'); + } + + /** + * @return int + */ + public static function get_first_day_of_week() + { + $result = strtolower(_t(__CLASS__.'.FIRSTDAYOFWEEK', 'monday')); + return ($result == "monday") ? Carbon::MONDAY : Carbon::SUNDAY; + } + + public static function date_sort(&$data) + { + uasort($data, [self::class, "date_sort_callback"]); + } + + /** + * Callback used by column_sort + */ + public static function date_sort_callback($a, $b) + { + if ($a->StartDate == $b->StartDate) { + if ($a->StartTime == $b->StartTime) { + return 0; + } elseif (strtotime($a->StartTime) > strtotime($b->StartTime)) { + return 1; + } + return -1; + } + elseif (strtotime($a->StartDate) > strtotime($b->StartDate)) { + return 1; + } + return -1; + } + + /** + * @return string + */ + public static function format_time($timeObj) + { + return self::get_time_format() == '24' + ? $timeObj->Format('HH:mm') + : $timeObj->Nice(); + } +} \ No newline at end of file diff --git a/code/ICSWriter.php b/src/Helpers/ICSWriter.php old mode 100755 new mode 100644 similarity index 81% rename from code/ICSWriter.php rename to src/Helpers/ICSWriter.php index 06b4faf..bd7c493 --- a/code/ICSWriter.php +++ b/src/Helpers/ICSWriter.php @@ -23,22 +23,31 @@ * @author Alex Hayes * @link https://github.com/dimension27/EventCalendar */ + +namespace UncleCheese\EventCalendar\Helpers; + +use SilverStripe\Control\Director; +use SilverStripe\Core\Injector\Injectable; +use UncleCheese\EventCalendar\Models\CalendarDateTime; +use UncleCheese\EventCalendar\Pages\Calendar; + class ICSWriter { + use Injectable; /** * @var Calendar */ - public $calendar; + private $calendar; - public $host; - public $prodid; - public $limit; + private $host; + private $prodid; + private $limit; /** * @var array */ - protected $lines = array(); + private $lines = []; /** * Construct an ICSWriter instance. @@ -51,14 +60,16 @@ class ICSWriter * * @author Alex Hayes */ - public function __construct( Calendar $calendar, $host, $prodid = null, $limit = 100 ) { + public function __construct(Calendar $calendar, $host, $prodid = null, $limit = 100) + { $this->calendar = $calendar; $this->host = $host; $this->prodid = $prodid; $this->limit = $limit; } - public function sendDownload() { + public function sendDownload() + { header("Cache-Control: private"); header("Content-Description: File Transfer"); header("Content-Type: text/calendar"); @@ -77,21 +88,22 @@ public function sendDownload() { * * @author Alex Hayes */ - public function getOutput() { - $this->lines = array(); + public function getOutput() + { + $this->lines = []; $this->addLine('BEGIN:VCALENDAR'); $this->addLine('VERSION:2.0'); - if( is_null($this->prodid) ) { + if (is_null($this->prodid)) { $this->addLine("PRODID:" . '-//'.$this->host.'//NONSGML v1.0//EN'); } - elseif( !is_null($this->prodid) ) { + elseif (!is_null($this->prodid)) { $this->addLine("PRODID:" . $this->prodid); } - $upcomingEvents = $this->calendar->UpcomingEvents($this->limit); /* @var $upcomingEvents DataObjectSet */ - foreach($upcomingEvents as $dateTime) { /* @var $event CalendarDateTime */ + $upcomingEvents = $this->calendar->UpcomingEvents($this->limit); + foreach ($upcomingEvents as $dateTime) { $this->addDateTime($dateTime); } @@ -108,7 +120,8 @@ public function getOutput() { * * @author Alex Hayes */ - protected function addLine($line) { + protected function addLine($line) + { $this->lines[] = $line; } @@ -120,7 +133,8 @@ protected function addLine($line) { * * @author Alex Hayes */ - protected function getUID( CalendarDateTime $dateTime ) { + protected function getUID(CalendarDateTime $dateTime) + { return $dateTime->ID.'@'.$this->host; } @@ -135,12 +149,12 @@ protected function getUID( CalendarDateTime $dateTime ) { * * @author Alex Hayes */ - protected function getFormatedDateTime( Date $date = null, Time $time = null ) { + protected function getFormatedDateTime(Date $date = null, Time $time = null) + { $timestamp = null; - if($date && $time) { + if ($date && $time) { $timestamp = strtotime($date . ' ' . $time); - } - else { + } else { $timestamp = time(); } return gmdate('Ymd\THis\Z', $timestamp); @@ -154,7 +168,8 @@ protected function getFormatedDateTime( Date $date = null, Time $time = null ) { * * @author Alex Hayes */ - protected function addDateTime( CalendarDateTime $dateTime ) { + protected function addDateTime(CalendarDateTime $dateTime) + { $this->addLine('BEGIN:VEVENT'); $this->addLine('UID:' . $this->getUID($dateTime) ); $this->addLine('DTSTAMP;TZID=' . Calendar::config()->timezone . ':' . $this->getFormatedDateTime()); diff --git a/src/Helpers/RecursionReader.php b/src/Helpers/RecursionReader.php new file mode 100644 index 0000000..1499846 --- /dev/null +++ b/src/Helpers/RecursionReader.php @@ -0,0 +1,170 @@ +format('Y') * 12) + $dateObj1->format('n')) - (($dateObj2->format('Y') * 12) + $dateObj2->format('n')); + } + + public function __construct(CalendarEvent $event) + { + $this->event = $event; + $cal = $event->Parent(); + + $this->datetimeClass = $cal->getDateTimeClass(); + $this->eventClass = $cal->getEventClass(); + $relation = $cal->getDateToEventRelation(); + + if ($datetime = DataList::create($this->datetimeClass) + ->filter($relation, $event->ID)->first() + ) { + $this->ts = strtotime($datetime->StartDate); + } + + if ($event->CustomRecursionType == CalendarEvent::RECUR_INTERVAL_WEEKLY) { + if ($daysOfWeek = $event->getManyManyComponents('RecurringDaysOfWeek')) { + foreach ($daysOfWeek as $day) { + $this->allowedDaysOfWeek[] = $day->Value; + } + } + } elseif ($event->CustomRecursionType == CalendarEvent::RECUR_INTERVAL_MONTHLY) { + if ($daysOfMonth = $event->getManyManyComponents('RecurringDaysOfMonth')) { + foreach ($daysOfMonth as $day) { + $this->allowedDaysOfMonth[] = $day->Value; + } + } + } + + if ($exceptions = $event->getComponents('Exceptions')) { + foreach ($exceptions as $exception) { + $this->exceptions[] = $exception->ExceptionDate; + } + } + } + + /** + * @param int $ts The timestamp to check + * @return bool + */ + public function recursionHappensOn($ts) + { + $testDate = Carbon::createFromTimestamp($ts); + $startDate = Carbon::createFromTimestamp($this->ts); + $result = false; + + // Current date is before the recurring event begins. + if ($testDate->getTimestamp() < $startDate->getTimestamp() + || in_array($testDate->toDateString(), $this->exceptions) + ) { + return $result; + } + + switch ($this->event->CustomRecursionType) { + + // Daily + case CalendarEvent::RECUR_INTERVAL_DAILY: + if ($this->event->DailyInterval + && ((($ts - $this->ts) / self::DAY_SECONDS) % $this->event->DailyInterval == 0) + ) { + $result = true; + } + break; + + // Weekly + case CalendarEvent::RECUR_INTERVAL_WEEKLY: + $testFirstDay = clone $testDate; + $testFirstDay->modify(($testFirstDay->format('l') == 'Sunday') + ? 'Monday last week' + : 'Monday this week' + ); + if ((($testFirstDay->getTimestamp() - $startDate->startOfWeek()->getTimestamp()) / self::WEEK_SECONDS) % $this->event->WeeklyInterval == 0 + && in_array($testDate->format('w'), $this->allowedDaysOfWeek) + ) { + $result = true; + }; + break; + + // Monthly + case CalendarEvent::RECUR_INTERVAL_MONTHLY: + if (self::difference_in_months($testDate, $startDate) % $this->event->MonthlyInterval == 0) { + + if ($this->event->MonthlyRecursionType1 == 1) { + + // A given set of dates in the month e.g. 2 and 15. + if (in_array($testDate->reset()->format('j'), $this->allowedDaysOfMonth)) { + $result = true; + } + + } elseif ($this->event->MonthlyRecursionType2 == 1) { + + // e.g. "First Monday of the month" + if ($this->event->MonthlyIndex == 5) { + // Last day of the month? + $targetDate = $testDate->addMonth()->startOfMonth()->previous($this->event->MonthlyDayOfWeek)->dump(); + } else { + $testDate->modify("last day of previous month"); + for ($i = 0; $i < $this->event->MonthlyIndex; $i++) { + $testDate->next($this->event->MonthlyDayOfWeek)->dump(); + } + $targetDate = $testDate->dump(); + } + return $testDate->reset()->dump() == $targetDate; + } + } + break; + } + + return $result; + } +} \ No newline at end of file diff --git a/code/iCal.php b/src/Helpers/iCal.php old mode 100755 new mode 100644 similarity index 52% rename from code/iCal.php rename to src/Helpers/iCal.php index a297217..b022b7f --- a/code/iCal.php +++ b/src/Helpers/iCal.php @@ -1,55 +1,66 @@ -ics_files = $ics_files; } - function iCalList() { - return array_filter($this->ics_files, array($this,"iCalClean")); + public function iCalList() + { + return array_filter($this->ics_files, [$this, "iCalClean"]); } - function iCalClean($file) { - return strpos($file, '.ics'); + public function iCalClean($file) + { + return strpos($file, '.ics'); } - function iCalReader() { - $iCaltoArray = array(); + public function iCalReader() + { + $iCaltoArray = []; $array = $this->iCalList(); foreach ($array as $icalfile) { $iCaltoArray[$icalfile] = $this->iCalDecoder($icalfile); } return $iCaltoArray; - } + } - function iCalDecoder($file) { + public function iCalDecoder($file) + { $ical = file_get_contents($file); // unfold long lines http://tools.ietf.org/html/rfc5545#section-3.1 $ical = str_replace("\r\n ", "", $ical); preg_match_all('/(BEGIN:VEVENT.*?END:VEVENT)/si', $ical, $result, PREG_PATTERN_ORDER); for ($i = 0; $i < count($result[0]); $i++) { $tmpbyline = explode("\r\n", $result[0][$i]); - foreach ($tmpbyline as $item) { $tmpholderarray = explode(":",$item, 2); //value can contain ":", so not strip URLs for example - if (count($tmpholderarray) >1) { + if (count($tmpholderarray) > 1) { $majorarray[$tmpholderarray[0]] = $tmpholderarray[1]; - } - + } } - foreach (array ("DESCRIPTION", "SUMMARY", "LOCATION") as $key) { - $majorarray[$key] = str_replace(array('\\\\', '\\,', '\\;', '\\n', '\\N'), - array('\\', ',', ';', "\n", "\n"), $majorarray[$key]); + foreach (["DESCRIPTION", "SUMMARY", "LOCATION"] as $key) { + $majorarray[$key] = str_replace( + ['\\\\', '\\,', '\\;', '\\n', '\\N'], + ['\\', ',', ';', "\n", "\n"], + $majorarray[$key] + ); } - $icalarray[] = $majorarray; unset($majorarray); - - - } + } return $icalarray; } - } diff --git a/src/Models/CachedCalendarEntry.php b/src/Models/CachedCalendarEntry.php new file mode 100644 index 0000000..77d16c7 --- /dev/null +++ b/src/Models/CachedCalendarEntry.php @@ -0,0 +1,101 @@ + 'Date', + 'StartTime' => 'Time', + 'EndDate' => 'Date', + 'EndTime' => 'Time', + 'AllDay' => 'Boolean', + 'Announcement' => 'Boolean', + 'DateRange' => 'HTMLText', + 'TimeRange' => 'HTMLText', + 'ICSLink' => 'Varchar(100)', + 'Title' => 'Varchar(255)', + 'Content' => 'HTMLText' + ]; + + private static $has_one = [ + 'CachedCalendar' => Calendar::class, + 'Calendar' => Calendar::class, + 'Event' => CalendarEvent::class + ]; + + private static $default_sort = "StartDate ASC, StartTime ASC"; + + /** + * @return CachedCalendarEntry + */ + public static function create_from_datetime(CalendarDateTime $dt, Calendar $calendar) + { + $cached = self::create(); + $cached->hydrate($dt, $calendar); + return $cached; + } + + /** + * @return CachedCalendarEntry + */ + public static function create_from_announcement(CalendarAnnouncement $dt, Calendar $calendar) + { + $cached = self::create(); + $cached->hydrate($dt, $calendar); + $cached->CalendarID = $dt->CalendarID; + $cached->Announcement = 1; + return $cached; + } + + /** + * @return \SilverStripe\ORM\DataList + */ + public function OtherDates() + { + if ($this->Announcement) { + return false; + } + return self::get() + ->filter('EventID', $this->EventID) + ->exclude( + [ + 'StartDate' => $this->StartDate + ] + )->limit($this->CachedCalendar()->OtherDatesCount); + } + + public function hydrate(CalendarDateTime $dt, Calendar $calendar) + { + $this->CachedCalendarID = $calendar->ID; + foreach ($dt->config()->db as $field => $type) { + $this->$field = $dt->$field; + } + foreach ($dt->config()->has_one as $field => $type) { + $this->{$field."ID"} = $dt->{$field."ID"}; + } + $this->DateRange = $dt->DateRange(); + $this->TimeRange = $dt->TimeRange(); + $this->ICSLink = $dt->ICSLink(); + $this->Title = $dt->getTitle(); + $this->Content = $dt->getContent(); + return $this; + } + +} diff --git a/src/Models/CalendarAnnouncement.php b/src/Models/CalendarAnnouncement.php new file mode 100644 index 0000000..cc81ecf --- /dev/null +++ b/src/Models/CalendarAnnouncement.php @@ -0,0 +1,75 @@ + 'Varchar(255)', + 'Content' => 'Text' + ]; + + private static $has_one = [ + 'Calendar' => Calendar::class + ]; + + public function getCMSFields() + { + $self = $this; + + $this->beforeUpdateCMSFields(function($f) use ($self) { + $f->insertBefore( + 'StartDate', + TextField::create('Title', _t(__CLASS__.'.TITLE', 'Title of announcement')) + ); + $f->insertBefore( + 'StartDate', + TextareaField::create('Content', _t(__CLASS__.'.CONTENT', 'Announcement content')) + ); + }); + + return $f = parent::getCMSFields();; + } + + public function summaryFields() + { + return [ + 'Title' => _t(__CLASS__.'.TITLE','Title of announcement'), + 'FormattedStartDate' => _t(Calendar::class.'.STARTDATE','Start date'), + 'FormattedEndDate' => _t(Calendar::class.'.ENDDATE','End date'), + 'FormattedStartTime' => _t(Calendar::class.'.STARTTIME','Start time'), + 'FormattedEndTime' => _t(Calendar::class.'.ENDTIME','End time'), + 'FormattedAllDay' => _t(Calendar::class.'.ALLDAY','All day'), + ]; + } + + public function getTitle() + { + return $this->getField('Title'); + } + + public function getContent() + { + return $this->getField('Content'); + } + +} diff --git a/src/Models/CalendarDateTime.php b/src/Models/CalendarDateTime.php new file mode 100644 index 0000000..7f20288 --- /dev/null +++ b/src/Models/CalendarDateTime.php @@ -0,0 +1,381 @@ + 'Date', + 'StartTime' => 'Time', + 'EndDate' => 'Date', + 'EndTime' => 'Time', + 'AllDay' => 'Boolean' + ]; + + private static $has_one = [ + 'Event' => CalendarEvent::class + ]; + + private static $singular_name = "Event date and time"; + + private static $plural_name = "Event dates and times"; + + /** + * @var string + */ + private static $date_format_override; + + /** + * @var string + */ + private static $time_format_override; + + private static $default_sort = "StartDate ASC, StartTime ASC"; + + private static $formatted_field_empty_string = '--'; + + private static $time_range_separator = ' — '; + + /** + * Set to the timezone offset (E.g. +12:00 for GMT+12). Must be in ISO 8601 format + * + * @config + * @see http://php.net/manual/en/function.date.php + * @var string + */ + private static $offset = "+00:00"; + + public function getCMSFields() + { + $fields = FieldList::create( + DateField::create('StartDate', _t(__CLASS__.'.STARTDATE','Start date')), + DateField::create('EndDate', _t(__CLASS__.'.ENDDATE','End date')), + TimeField::create('StartTime', _t(__CLASS__.'.STARTTIME','Start time')), + TimeField::create('EndTime', _t(__CLASS__.'.ENDTIME','End time')), + CheckboxField::create('AllDay', _t(__CLASS__.'.ALLDAY','This event lasts all day')) + ); + + $this->extend('updateCMSFields', $fields); + + return $fields; + } + + public function summaryFields() + { + return [ + 'FormattedStartDate' => _t(__CLASS__.'.STARTDATE','Start date'), + 'FormattedEndDate' => _t(__CLASS__.'.ENDDATE','End date'), + 'FormattedStartTime' => _t(__CLASS__.'.STARTTIME','Start time'), + 'FormattedEndTime' => _t(__CLASS__.'.ENDTIME','End time'), + 'FormattedAllDay' => _t(__CLASS__.'.ALLDAY','All day'), + ]; + } + + /** + * @return string + */ + public function Link() + { + return Controller::join_links($this->Event()->Link(),"?date=".$this->StartDate); + } + + /** + * @return string + */ + public function getDateRange() + { + list($startDate, $endDate) = CalendarUtil::get_date_string($this->StartDate, $this->EndDate); + return $this->customise( + [ + 'StartDate' => $startDate, + 'EndDate' => $endDate + ] + )->renderWith(__CLASS__ .'\DateRange'); + } + + /** + * @return string + */ + public function getTimeRange() + { + $ret = CalendarUtil::format_time($this->obj('StartTime')); + if ($this->EndTime) { + $ret .= self::config()->time_range_separator . CalendarUtil::format_time($this->obj('EndTime')); + } + return DBField::create_field('HTMLFragment', $ret); + } + + /** + * @return bool + */ + public function Announcement() + { + return $this->ClassName == CalendarAnnouncement::class; + } + + /** + * @return bool|SilverStripe\ORM\DataList + */ + public function getOtherDates() + { + if ($this->Announcement()) { + return false; + } + + if ($this->Event()->Recursion) { + return $this->Event()->Parent()->getNextRecurringEvents($this->Event(), $this); + } + + return self::get()->filter( + [ + 'EventID' => $this->EventID, + 'StartDate:Not' => $this->StartDate + ] + )->limit($this->Event()->Parent()->OtherDatesCount); + } + + /** + * @return string + */ + public function MicroformatStart($offset = true) + { + if (!$this->StartDate) { + return ""; + } + $time = (!$this->AllDay && $this->StartTime) ? $this->StartTime : "00:00:00"; + return CalendarUtil::microformat($this->StartDate, $time, self::config()->offset); + } + + /** + * @return string + */ + public function MicroformatEnd($offset = true) + { + if ($this->AllDay && $this->StartDate) { + $time = "00:00:00"; + $date = new Carbon($this->StartDate); + $date = $date->addDay()->toDateString(); + } else { + $date = $this->EndDate ? $this->EndDate : $this->StartDate; + if ($this->EndTime && $this->StartTime) { + $time = $this->EndTime; + } else { + $time = (!$this->EndTime && $this->StartTime) ? $this->StartTime : "00:00:00"; + } + } + return CalendarUtil::microformat($date, $time, self::config()->offset); + } + + /** + * @return string + */ + public function ICSLink() + { + $icsStart = $this->obj('StartDate')->Format('YMMdd')."T".$this->obj('StartTime')->Format('Hmm'); + if ($this->EndDate) { + $icsEnd = $this->obj('EndDate')->Format('YMMdd')."T".$this->obj('EndTime')->Format('Hmm'); + } else { + $icsEnd = $icsStart; + } + if ($this->Feed) { + return Controller::join_links( + $this->Calendar()->Link(), + "ics", + $this->ID, + $icsStart . "-" . $icsEnd, + "?title=".urlencode($this->Title) + ); + } elseif ($this->Announcement()) { + return Controller::join_links( + $this->Calendar()->Link(), + "ics", + "announcement-".$this->ID, + $icsStart . "-" . $icsEnd + ); + } + return Controller::join_links( + $this->Event()->Parent()->Link(), + "ics", + $this->Event()->ID, + $icsStart . "-" . $icsEnd + ); + } + + /** + * @return string + */ + public function getFormattedStartDate() + { + if (!$this->StartDate) { + return $this->config()->formatted_field_empty_string; + } + return CalendarUtil::get_date_format() == "mdy" + ? $this->obj('StartDate')->Format('MM-dd-Y') + : $this->obj('StartDate')->Format('dd-MM-Y'); + } + + /** + * @return string + */ + public function getFormattedEndDate() + { + if (!$this->EndDate) { + return $this->config()->formatted_field_empty_string; + } + return CalendarUtil::get_date_format() == "mdy" + ? $this->obj('EndDate')->Format('MM-dd-Y') + : $this->obj('EndDate')->Format('dd-MM-Y'); + } + + /** + * @return string + */ + public function getFormattedStartTime() + { + if (!$this->StartTime) { + return $this->config()->formatted_field_empty_string; + } + return CalendarUtil::get_time_format() == "12" + ? $this->obj('StartTime')->Nice() + : Carbon::createFromTimeString($this->StartTime)->format('H:i'); + } + + /** + * @return string + */ + public function getFormattedEndTime() + { + if (!$this->EndTime) { + return $this->config()->formatted_field_empty_string; + } + return CalendarUtil::get_time_format() == "12" + ? $this->obj('EndTime')->Nice() + : Carbon::createFromTimeString($this->EndTime)->format('H:i'); + } + + /** + * @return string + */ + public function getFormattedAllDay() + { + return $this->AllDay == 1 ? _t(__CLASS__.'.YES', 'Yes') : _t(__CLASS__.'.NO', 'No'); + } + + /** + * @return string + */ + public function getTitle() + { + return $this->Event()->Title; + } + + /** + * @return string + */ + public function getContent() + { + return $this->Event()->Content; + } + + /** + * @return array + */ + public function getAllDatesInRange() + { + $start = new \DateTime($this->StartDate); + $end = new \DateTime($this->EndDate); + $end = $end->getTimestamp(); + $dates = []; + do { + $dates[] = $start->format('Y-m-d'); + $start->add(new \DateInterval('P1D')); + } while ($start->getTimestamp() <= $end); + + return $dates; + } + + /** + * @return bool + */ + public function canCreate($member = null, $context = []) + { + if (!$member) { + $member = Member::currentUser(); + } + $extended = $this->extendedCan(__FUNCTION__, $member); + if($extended !== null) { + return $extended; + } + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + + /** + * @return bool + */ + public function canEdit($member = null) + { + if (!$member) { + $member = Member::currentUser(); + } + $extended = $this->extendedCan(__FUNCTION__, $member); + if($extended !== null) { + return $extended; + } + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + + /** + * @return bool + */ + public function canDelete($member = null) + { + if (!$member) { + $member = Member::currentUser(); + } + $extended = $this->extendedCan(__FUNCTION__, $member); + if($extended !== null) { + return $extended; + } + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + + /** + * @return bool + */ + public function canView($member = null) + { + if (!$member) { + $member = Member::currentUser(); + } + $extended = $this->extendedCan(__FUNCTION__, $member); + if($extended !== null) { + return $extended; + } + return Permission::check('CMS_ACCESS_CMSMain', 'any', $member); + } + +} diff --git a/src/Models/ICSFeed.php b/src/Models/ICSFeed.php new file mode 100644 index 0000000..00039c3 --- /dev/null +++ b/src/Models/ICSFeed.php @@ -0,0 +1,49 @@ + 'Varchar(100)', + 'URL' => 'Varchar(255)' + ]; + + private static $has_one = [ + 'Calendar' => Calendar::class + ]; + + public function getCMSFields() + { + $fields = FieldList::create( + TextField::create('Title', _t(__CLASS__.'.TITLEOFFEED', 'Title of feed')), + TextField::create('URL', _t(__CLASS__.'.URLLINK', 'URL'), 'http://') + ); + + $this->extend('updateCMSFields', $fields); + + return $fields; + } + + public function summaryFields() + { + return [ + 'Title' => _t(__CLASS__.'.TITLE', 'Title') + ]; + } +} \ No newline at end of file diff --git a/src/Models/RecurringDayOfMonth.php b/src/Models/RecurringDayOfMonth.php new file mode 100644 index 0000000..908438b --- /dev/null +++ b/src/Models/RecurringDayOfMonth.php @@ -0,0 +1,70 @@ + 'Int' + ]; + + private static $belongs_many_many = [ + 'CalendarEvent' => CalendarEvent::class + ]; + + private static $default_sort = "Value ASC"; + + private static function create_default_records() + { + for ($i = 1; $i <= 31; $i++) { + $record = self::create(); + $record->Value = $i; + $record->write(); + } + } + + public function requireDefaultRecords() { + parent::requireDefaultRecords(); + $records = self::get(); + if (!$records->exists()) { + self::create_default_records(); + } elseif ($records->count() != 31) { + foreach($records as $record) { + $record->delete(); + } + self::create_default_records(); + } + } + + + public function canCreate($member = null, $context = []) { + return Permission::check("CMS_ACCESS_CMSMain"); + } + + public function canEdit($member = null) { + return Permission::check("CMS_ACCESS_CMSMain"); + } + + public function canDelete($member = null) { + return Permission::check("CMS_ACCESS_CMSMain"); + } + + public function canView($member = null) { + return Permission::check("CMS_ACCESS_CMSMain"); + } + +} diff --git a/src/Models/RecurringDayOfWeek.php b/src/Models/RecurringDayOfWeek.php new file mode 100644 index 0000000..fc64f24 --- /dev/null +++ b/src/Models/RecurringDayOfWeek.php @@ -0,0 +1,89 @@ + 'Int' + ]; + + private static $belongs_many_many = [ + 'CalendarEvent' => CalendarEvent::class + ]; + + private static $default_sort = "Value ASC"; + + /** + * Add week recurrence records + * + * @return void + */ + private static function create_default_records() + { + for ($i = 0; $i <= 6; $i++) { + $record = self::create(); + $record->Value = $i; + $record->write(); + } + } + + public function requireDefaultRecords() + { + parent::requireDefaultRecords(); + $records = self::get(); + if (!$records->exists()) { + self::create_default_records(); + } elseif ($records->count() != 7) { + foreach ($records as $record) { + $record->delete(); + } + self::create_default_records(); + } + } + + public function getTitle() + { + return strftime("%a", Carbon::now()->next($this->Value)->getTimestamp()); + } + + + public function canCreate($member = null, $context = []) + { + return Permission::check("CMS_ACCESS_CMSMain"); + } + + public function canEdit($member = null) + { + return Permission::check("CMS_ACCESS_CMSMain"); + } + + public function canDelete($member = null) + { + return Permission::check("CMS_ACCESS_CMSMain"); + } + + public function canView($member = null) + { + return Permission::check("CMS_ACCESS_CMSMain"); + } +} diff --git a/src/Models/RecurringException.php b/src/Models/RecurringException.php new file mode 100644 index 0000000..abdeeef --- /dev/null +++ b/src/Models/RecurringException.php @@ -0,0 +1,88 @@ + 'Date' + ]; + + private static $has_one = [ + 'CalendarEvent' => CalendarEvent::class + ]; + + private static $default_sort = "ExceptionDate ASC"; + + public function getCMSFields() + { + //DateField::set_default_config('showcalendar', true); + $fields = FieldList::create( + DateField::create('ExceptionDate', _t(__CLASS__.'.EXCEPTIONDATE','Exception date')) + ); + + $this->extend('updateCMSFields', $fields); + + return $fields; + } + + public function summaryFields() + { + return [ + 'FormattedExceptionDate' => _t(__CLASS__.'.EXCEPTIONDATE','Exception date') + ]; + } + + public function getFormattedExceptionDate() + { + if (!$this->ExceptionDate) { + return CalendarDateTime::config()->formatted_field_empty_string; + } + return CalendarUtil::get_date_format() == "mdy" + ? $this->obj('ExceptionDate')->Format('m-d-Y') + : $this->obj('ExceptionDate')->Format('d-m-Y'); + } + + + public function canCreate($member = null, $context = []) + { + return Permission::check("CMS_ACCESS_CMSMain"); + } + + public function canEdit($member = null) + { + return Permission::check("CMS_ACCESS_CMSMain"); + } + + public function canDelete($member = null) + { + return Permission::check("CMS_ACCESS_CMSMain"); + } + + public function canView($member = null, $context = []) + { + return Permission::check("CMS_ACCESS_CMSMain"); + } +} diff --git a/src/Pages/Calendar.php b/src/Pages/Calendar.php new file mode 100644 index 0000000..3f28595 --- /dev/null +++ b/src/Pages/Calendar.php @@ -0,0 +1,716 @@ + 'Varchar(50)', + 'OtherDatesCount' => 'Int', + 'RSSTitle' => 'Varchar(255)', + 'DefaultFutureMonths' => 'Int', + 'EventsPerPage' => 'Int', + 'DefaultView' => "Enum('today,week,month,weekend,upcoming','upcoming')" + ]; + + private static $has_many = [ + 'Announcements' => CalendarAnnouncement::class, + 'Feeds' => ICSFeed::class + ]; + + private static $many_many = [ + 'NestedCalendars' => self::class + ]; + + private static $belongs_many_many = [ + 'ParentCalendars' => self::class + ]; + + private static $allowed_children = [ + CalendarEvent::class + ]; + + private static $defaults = [ + 'DefaultDateHeader' => 'Upcoming events', + 'OtherDatesCount' => 3, + 'DefaultFutureMonths' => 6, + 'EventsPerPage' => 10, + 'DefaultView' => 'upcoming' + ]; + + private static $icon = "unclecheese/silverstripe-event-calendar:client/dist/images/calendar-file.gif"; + + private static $description = "A collection of calendar events"; + + /** + * @var int + */ + private static $reccurring_event_index = 0; + + /** + * @var int + */ + private static $recent_events_default_limit = 9999; + + /** + * @var int + */ + private static $max_recurring_events_listed = 0; + + /** + * @var string + */ + private static $event_class = CalendarEvent::class; + + /** + * @var string + */ + private static $announcement_class = CalendarAnnouncement::class; + + /** + * @var string + */ + private static $timezone = "America/New_York"; + + /** + * @var string + */ + private static $language = "EN"; + + /** + * @var bool + */ + private static $jquery_included = false; + + /** + * @var bool + */ + private static $include_default_css = false; + + /** + * @var bool + */ + private static $include_calendar_js = false; + + /** + * @var bool + */ + private static $caching_enabled = false; + + protected $eventClass_cache, + $announcementClass_cache, + $datetimeClass_cache, + $dateToEventRelation_cache, + $announcementToCalendarRelation_cache, + $eventList_cache; + + /** + * @return void + */ + public static function set_jquery_included($bool = true) + { + Config::modify()->set(self::class, 'jquery_included', $bool); + } + + /** + * @return void + */ + public static function enable_caching() + { + Config::modify()->set(self::class, 'caching_enabled', true); + } + + public function getCMSFields() + { + $self = $this; + + $this->beforeUpdateCMSFields(function($f) use ($self) { + + Requirements::javascript('unclecheese/silverstripe-event-calendar:client/dist/js/calendar_cms.js'); + + $configuration = _t(__CLASS__.'.CONFIGURATION','Configuration'); + $f->addFieldsToTab( + "Root.$configuration", + [ + DropdownField::create( + 'DefaultView', + _t(__CLASS__.'.DEFAULTVIEW','Default view'), + [ + 'upcoming' => _t(__CLASS__.'.UPCOMINGVIEW',"Show a list of upcoming events."), + 'month' => _t(__CLASS__.'.MONTHVIEW',"Show this month's events."), + 'week' => _t(__CLASS__.'.WEEKVIEW',"Show this week's events. If none, fall back on this month's"), + 'today' => _t(__CLASS__.'.TODAYVIEW',"Show today's events. If none, fall back on this week's events"), + 'weekend' => _t(__CLASS__.'.WEEKENDVIEW',"Show this weekend's events.") + ] + )->addExtraClass('defaultView'), + NumericField::create('DefaultFutureMonths', _t(__CLASS__.'.DEFAULTFUTUREMONTHS','Number maximum number of future months to show in default view'))->addExtraClass('defaultFutureMonths'), + NumericField::create('EventsPerPage', _t(__CLASS__.'.EVENTSPERPAGE','Events per page')), + TextField::create('DefaultDateHeader', _t(__CLASS__.'.DEFAULTDATEHEADER','Default date header (displays when no date range has been selected)')), + NumericField::create('OtherDatesCount', _t(__CLASS__.'.NUMBERFUTUREDATES','Number of future dates to show for repeating events')) + ] + ); + + // Announcements + $announcements = _t(__CLASS__.'.Announcements', 'Announcements'); + $f->addFieldToTab( + "Root.$announcements", + GridField::create( + "Announcements", + $announcements, + $self->Announcements(), + GridFieldConfig_RecordEditor::create() + )->setDescription( + _t(__CLASS__.'.ANNOUNCEMENTDESCRIPTION','Announcements are simple entries you can add to your calendar that do not have detail pages, e.g. "Office closed"') + ) + ); + + // Feeds + $feeds = _t(__CLASS__.'.FEEDS', 'Feeds'); + $f->addFieldToTab( + "Root.$feeds", + GridField::create( + "Feeds", + $feeds, + $self->Feeds(), + GridFieldConfig_RecordEditor::create() + )->setDescription( + _t(__CLASS__.'.ICSFEEDDESCRIPTION','Add ICS feeds to your calendar to include events from external sources') + ) + ); + + $otherCals = self::get()->exclude("ID", $self->ID); + if ($otherCals->exists()) { + $f->addFieldToTab( + "Root.$feeds", + CheckboxsetField::create( + 'NestedCalendars', + _t(__CLASS__.'.NESTEDCALENDARS','Include events from these calendars'), + $otherCals->map('ID', 'Link') + ) + ); + } + $f->addFieldToTab( + "Root.Main", + TextField::create('RSSTitle', _t(__CLASS__.'.RSSTITLE', 'Title of RSS feed')), + 'Content' + ); + + }); + + return $f = parent::getCMSFields(); + } + + /** + * @return string + */ + public function getEventClass() + { + if ($this->eventClass_cache) { + return $this->eventClass_cache; + } + return $this->eventClass_cache = self::config()->event_class; + } + + /** + * @return string + */ + public function getAnnouncementClass() + { + if ($this->announcementClass_cache) { + return $this->announcementClass_cache; + } + return $this->announcementClass_cache = self::config()->announcement_class; + } + + /** + * @return string + */ + public function getDateTimeClass() + { + if ($this->datetimeClass_cache) { + return $this->datetimeClass_cache; + }; + return $this->datetimeClass_cache = Config::inst() + ->get($this->getEventClass(), 'datetime_class'); + } + + /** + * @return string + */ + public function getDateToEventRelation() + { + if ($this->dateToEventRelation_cache) { + return $this->dateToEventRelation_cache; + } + $dateTime = Injector::inst()->get($this->getDateTimeClass()); + foreach ($dateTime->config()->has_one as $rel => $class) { + if ($class == $this->getEventClass()) { + return $this->dateToEventRelation_cache = $rel.'ID'; + } + } + } + + /** + * @param string $start Start date + * @param string $end End date + * @param string $filter + * @param int $limit Limit result set size + * @return DataList + */ + public function getCachedEventList($start, $end, $filter = null, $limit = null) + { + return CachedCalendarEntry::get() + ->filter("CachedCalendarID", $this->ID) + ->exclude( + [ + "StartDate:LessThan" => $end, + "EndDate:GreaterThan" => $start + ] + ) + ->sort( + [ + "StartDate" => "ASC", + "StartTime" => "ASC" + ] + ) + ->limit($limit); + } + + /** + * Get the event list for this calendar, including standard, recurring and announcements + * + * @param string $start Start date + * @param string $end End date + * @param string $filter Additional filters + * @param int $limit Limit result set size + * @param int $announcementFilter Filter announcements prior to their inclusion + * @return DataList + */ + public function getEventList( + $start, + $end, + $filter = null, + $limit = null, + $announcementFilter = null + ) { + if (self::config()->caching_enabled) { + return $this->getCachedEventList($start, $end, $filter, $limit); + } + + $eventList = ArrayList::create(); + + foreach ($this->getAllCalendars() as $calendar) { + if ($events = $calendar->getStandardEvents($start, $end, $filter)) { + $eventList->merge($events); + } + $announcements = DataList::create($this->getAnnouncementClass()) + ->filter( + [ + "CalendarID" => $calendar->ID, + "StartDate:GreaterThanOrEqual" => $start, + "EndDate:LessThanOrEqual" => $end + ] + ); + if ($announcementFilter) { + $announcements = $announcements->where($announcementFilter); + } + if ($announcements) { + foreach($announcements as $announcement) { + $eventList->push($announcement); + } + } + if ($recurring = $calendar->getRecurringEvents($filter)) { + $calendar->addRecurringEvents($start, $end, $recurring, $eventList); + } + if ($feedevents = $calendar->getFeedEvents($start,$end)) { + $eventList->merge($feedevents); + } + } + + $eventList = $eventList->sort( + [ + 'StartDate' => 'ASC', + 'StartTime' => 'ASC' + ] + )->limit($limit); + + return $this->eventList_cache = $eventList; + } + + /** + * Get non-recurring events for this calendar that fall within a start and end date + * + * @param string $start Start date + * @param string $end End date + * @param string $filter Additional filters + * @return DataList + */ + protected function getStandardEvents($start, $end, $filter = null) + { + $eventTable = Config::inst()->get($this->getEventClass(), 'table_name'); + $relation = $this->getDateToEventRelation(); + $siteTreeTable = SiteTree::config()->table_name; + + $list = DataList::create($this->getDateTimeClass()) + ->innerJoin($eventTable, "\"$relation\" = \"$eventTable\".\"ID\"") + ->innerJoin($siteTreeTable, "\"$siteTreeTable\".\"ID\" = \"$eventTable\".\"ID\""); + + $filters = [ + 'Recursion:not' => 1 + ]; + + $ids = $this->AllChildren()->column('ID'); + if ($ids) { + $filters[$relation] = $ids; + } + + $list = $list->filter($filters); + + if ($start && $end) { + $list = $list->where(" + (StartDate <= '$start' AND EndDate >= '$end') OR + (StartDate BETWEEN '$start' AND '$end') OR + (EndDate BETWEEN '$start' AND '$end')" + ); + } elseif($start) { + $list = $list->where("(StartDate >= '$start' OR EndDate > '$start')"); + } elseif($end) { + $list = $list->where("(EndDate <= '$end' OR StartDate < '$end')"); + } + if ($filter) { + $list = $list->where($filter); + } + + return $list; + } + + /** + * Get the list of events in this calendar that are set to recur + * + * @param string $filter Additional filters + * @return DataList|false + */ + protected function getRecurringEvents($filter = null) + { + if ($relation = $this->getDateToEventRelation()) { + $dtTable = Config::inst()->get($this->getDateTimeClass(), 'table_name'); + $siteTreeTable = SiteTree::config()->table_name; + $events = DataList::create($this->getEventClass()) + ->filter( + [ + "Recursion" => "1", + "ParentID" => $this->ID + ] + ) + ->innerJoin($dtTable, "\"$dtTable\".\"$relation\" = \"$siteTreeTable\".\"ID\""); + if ($filter) { + $events = $events->where($filter); + } + return $events; + } + + return false; + } + + /** + * Get the next set of recurring datetimes for an event + * + * @param CalendarEvent $eventObj Event record + * @param CalendarDateTime $datetimeObj Datetime record + * @param int $limit Limit the result set size + * @return ArrayList + */ + public function getNextRecurringEvents($eventObj, $datetimeObj, $limit = null) + { + $counter = Carbon::parse($datetimeObj->StartDate); + + if ($event = $datetimeObj->Event()->DateTimes()->First()) { + $endDate = strtotime($event->EndDate); + } else { + $endDate = false; + } + $counter->addDay()->startOfDay(); + $dates = ArrayList::create(); + while ($dates->Count() != $this->OtherDatesCount) { + // check the end date + if ($endDate && $endDate > 0 && $endDate <= $counter->getTimestamp()) { + break; + } + if ($eventObj->getRecursionReader()->recursionHappensOn($counter->getTimestamp())) { + $dates->push($this->newRecursionDateTime($datetimeObj, $counter->toDateString())); + } + $counter->addDay()->startOfDay(); + } + + return $dates; + } + + /** + * Add recurring events to a list of events + * + * @param string $startDate Start date + * @param string $endDate End date + * @param DataList $recurringEvents Set of recurring events + * @param ArrayList $allEvents The list to add events to + */ + protected function addRecurringEvents($startDate, $endDate, $recurringEvents, $allEvents) + { + $dateCounter = Carbon::parse($startDate); + $end = Carbon::parse($endDate); + + foreach ($recurringEvents as $recurringEvent) { + $reader = $recurringEvent->getRecursionReader(); + $relation = null; + foreach ($recurringEvent->config()->has_many as $rel => $class) { + if ($class == $this->getDateTimeClass()) { + $relation = $rel; + } + } + if (!$relation) { + continue; + } + $recurringEventDatetimes = $recurringEvent->$relation()->filter( + [ + 'StartDate:LessThanOrEqual' => $end->toDateString(), + 'EndDate:GreaterThanOrEqual' => $dateCounter->toDateString() + ] + ); + + foreach ($recurringEventDatetimes as $recurringEventDatetime) { + $start = Carbon::parse($recurringEventDatetime->StartDate); + if ($start->getTimestamp() > $dateCounter->getTimestamp()) { + $dateCounter = $start; + } + $recurrenceAdded = 0; + while ($dateCounter <= $end) { + if (self::config()->max_recurring_events_listed + && ($recurrenceAdded == self::config()->max_recurring_events_listed) + ) { + break; + } + // check the end date + if ($recurringEventDatetime->EndDate) { + $endStamp = strtotime($recurringEventDatetime->EndDate); + if ($endStamp > 0 && $endStamp < $dateCounter->getTimestamp()) { + break; + } + } + if ($reader->recursionHappensOn($dateCounter->getTimestamp())) { + $e = $this->newRecursionDateTime($recurringEventDatetime, $dateCounter->toDateString()); + $allEvents->push($e); + $recurrenceAdded++; + } + $dateCounter->addDay()->startOfDay(); + } + $dateCounter = Carbon::parse($startDate); + } + } + } + + /** + * Create a Datetime for a recursion instance (recurrence) + * + * @param mixed $recurringEventDatetime The Datetime to base the recurrence off + * @param string $startDate The start date + * @return mixed An instance of the class defined in CalendarEvent::datetime_class + */ + public function newRecursionDateTime($recurringEventDatetime, $startDate) + { + $relation = $this->getDateToEventRelation(); + $e = Injector::inst()->get($this->getDateTimeClass(), false); + foreach ($recurringEventDatetime->config()->db as $field => $type) { + $e->$field = $recurringEventDatetime->$field; + } + $e->DateTimeID = $recurringEventDatetime->ID; + $e->StartDate = $startDate; + $e->EndDate = $startDate; + $e->$relation = $recurringEventDatetime->$relation; + $e->ID = "recurring" . self::$reccurring_event_index; + self::$reccurring_event_index++; + return $e; + } + + /** + * Get events from specified feeds that fall within a date range + * + * @param string $stardDate Range start date + * @param string $endDate Range end date + * @return ArrayList + */ + public function getFeedEvents($startDate, $endDate) + { + $start = new \DateTime($startDate); + // single day views don't pass end dates + $end = $endDate ? new \DateTime($endDate) : $start; + $feeds = $this->Feeds(); + $feedevents = ArrayList::create(); + foreach ($feeds as $feed) { + $feedreader = new ICal($feed->URL); + foreach ($feedreader->events() as $event) { + // translate iCal schema into CalendarAnnouncement schema (datetime + title/content) + $feedevent = CalendarAnnouncement::create() + ->update( + [ + 'ID' => 'ICS_'.$feed->ID, + 'Feed' => true, + 'CalendarID' => $this->ID, + 'Title' => $event['SUMMARY'] + ] + ); + if (isset($event['DESCRIPTION'])) { + $feedevent->Content = $event['DESCRIPTION']; + } + $startdatetime = $this->iCalDateToDateTime($event['DTSTART']); + $enddatetime = $this->iCalDateToDateTime($event['DTEND']); + + //Set event start/end to midnight to allow comparisons below to work + $startdatetime->modify('00:00:00'); + $enddatetime->modify('00:00:00'); + + if (($startdatetime < $start && $enddatetime < $start) + || $startdatetime > $end && $enddatetime > $end) { + // do nothing; dates outside range + } else { + $feedevent->update( + [ + 'StartDate' => $startdatetime->format('Y-m-d'), + 'StartTime' => $startdatetime->format('H:i:s'), + 'EndDate' => $enddatetime->format('Y-m-d'), + 'EndTime' => $enddatetime->format('H:i:s') + ] + ); + $feedevents->push($feedevent); + } + } + } + return $feedevents; + } + + /** + * @return \DateTime + */ + public function iCalDateToDateTime($date) + { + $dt = new \DateTime($date); + $dt->setTimeZone(new \DateTimezone($this->config()->timezone)); + return $dt; + } + + /** + * @return ArrayList + */ + public function getAllCalendars() + { + $calendars = ArrayList::create(); + $calendars->push($this); + $calendars->merge($this->NestedCalendars()); + return $calendars; + } + + /** + * @return DataList + */ + public function UpcomingEvents($limit = 5, $filter = null) + { + $date = Carbon::now(); + return $this->getEventList( + $date->toDateString(), + $date->addMonths($this->DefaultFutureMonths)->toDateString(), + $filter, + $limit + )->limit($limit); + } + + public function UpcomingAnnouncements($limit = 5, $filter = null) + { + return $this->Announcements() + ->filter('StartDate:GreaterThan', 'NOW()') + ->where($filter) + ->limit($limit); + } + + /** + * @return DataList + */ + public function RecentEvents($limit = null, $filter = null) + { + $startDate = Carbon::now(); + $endDate = Carbon::now(); + $l = ($limit === null) ? $this->config()->recent_events_default_limit : $limit; + $events = $this->getEventList( + $startDate->subMonths($this->DefaultFutureMonths)->toDateString(), + $endDate->yesterday()->toDateString(), + $filter, + $l + )->sort('StartDate DESC'); + + return $events->limit($limit); + } + + /** + * @return CalendarWidget + */ + public function getCalendarWidget() + { + $calendar = CalendarWidget::create($this); + $controller = Controller::curr(); + if ($controller instanceof CalendarController) { + if ($controller->getView() != "default") { + if ($startDate = $controller->getStartDate()) { + $calendar->setOption('start', $startDate->toDateString()); + } + if ($endDate = $controller->getEndDate()) { + $calendar->setOption('end', $endDate->toDateString()); + } + } + } + return $calendar; + } + + public function getMonthJumpForm() + { + $controller = Controller::curr(); + if (!($controller instanceof CalendarController)) { + $controller = CalendarController::create($this); + } + return $controller->MonthJumpForm(); + } + +} \ No newline at end of file diff --git a/src/Pages/CalendarController.php b/src/Pages/CalendarController.php new file mode 100644 index 0000000..0bcc1f0 --- /dev/null +++ b/src/Pages/CalendarController.php @@ -0,0 +1,691 @@ +Link() . "rss", $this->RSSTitle ? $this->RSSTitle : $this->Title); + if (Calendar::config()->include_default_css) { + Requirements::css('unclecheese/silverstripe-event-calendar:client/dist/css/calendar.css'); + } + if (Calendar::config()->include_calendar_js) { + if (!Calendar::config()->jquery_included) { + Requirements::javascript('silverstripe/admin:thirdparty/jquery/jquery.min.js'); + } + Requirements::javascript('unclecheese/silverstripe-event-calendar:client/dist/js/calendar.js'); + } + } + + public function index(HTTPRequest $r) { + + $this->extend('index', $r); + + switch ($this->DefaultView) { + case "month": + return $this->redirect($this->Link('show/month')); + break; + case "week": + $this->setWeekView(); + // prevent pagination on these default views + $this->EventsPerPage = 999; + $e = $this->getEvents(); + if ($e->count() > 0) { + return ['Events' => $e]; + } else { + $this->setMonthView(); + return []; + } + break; + case "today": + // prevent pagination on these default views + $this->EventsPerPage = 999; + $this->setTodayView(); + $e = $this->getEvents(); + if ($e->count() > 0) { + return ['Events' => $e]; + } else { + $this->setWeekView(); + return []; + } + break; + default: + $this->setDefaultView(); + return $this->respond(); + break; + } + } + + public function today(HTTPRequest $r) + { + $this->setTodayView(); + return $this->respond(); + } + + public function week(HTTPRequest $r) + { + $this->setWeekView(); + return $this->respond(); + } + + public function weekend(HTTPRequest $r) { + $this->setWeekendView(); + return $this->respond(); + } + + public function month(HTTPRequest $r) { + $this->setMonthView(); + return $this->respond(); + } + + public function show(HTTPRequest $r) { + $this->parseURL($r); + return $this->respond(); + } + + public function rss() { + $this->setDefaultView(); + $events = $this->getEvents(); + foreach($events as $event) { + $event->Title = strip_tags($event->DateRange()) . " : " . $event->getTitle(); + $event->Description = $event->getContent(); + } + $rssTitle = $this->RSSTitle + ? $this->RSSTitle + : sprintf( + _t(Calendar::class.'.UPCOMINGEVENTSFOR', "Upcoming Events for %s"), + $this->Title + ); + $rss = RSSFeed::create($events, $this->Link(), $rssTitle, "", "Title", "Description"); + + if (is_int($rss->lastModified)) { + HTTP::register_modification_timestamp($rss->lastModified); + header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $rss->lastModified) . ' GMT'); + } + if (!empty($rss->etag)) { + HTTP::register_etag($rss->etag); + } + $xml = str_replace(' ', ' ', $rss->renderWith('SilverStripe\Control\RSS\RSSFeed')); + $xml = preg_replace('//', '', $xml); + $xml = trim($xml); + HTTP::add_cache_headers(); + + return $this->getResponse() + ->addHeader('Content-Type', 'application/rss+xml') + ->setBody($xml); + + } + + /** + * @return string + */ + public function monthjson(HTTPRequest $r) { + $json = []; + if (!$r->param('ID')) { + return json_encode($json); + } + //Increase the per page limit to 500 as the AJAX request won't look for further pages + $this->EventsPerPage = 500; + $this->startDate = Carbon::parse( + CalendarUtil::get_date_from_string($r->param('ID')) + ); + $this->endDate = Carbon::parse($this->startDate)->endOfMonth(); + + + $counter = clone $this->startDate; + while ($counter->getTimestamp() <= $this->endDate->getTimestamp()) { + $d = $counter->toDateString(); + $json[$d] = [ + 'events' => [] + ]; + $counter->addDay(); + } + $list = $this->getEvents(); + foreach ($list as $e) { + foreach ($e->getAllDatesInRange() as $date) { + if (isset($json[$date])) { + $json[$date]['events'][] = $e->getTitle(); + } + } + } + + return json_encode($json); + } + + /** + * @return Carbon + */ + public function getStartDate() + { + return $this->startDate; + } + + /** + * @return Carbon + */ + public function getEndDate() + { + return $this->endDate; + } + + public function getView() + { + return $this->view; + } + + public function setDefaultView() + { + $this->view = 'default'; + $this->startDate = Carbon::now(); + $this->endDate = Carbon::now()->addMonths($this->DefaultFutureMonths); + } + + public function setTodayView() + { + $this->view = 'day'; + $this->startDate = Carbon::now(); + $this->endDate = Carbon::now(); + } + + public function setWeekView() + { + $this->view = 'week'; + $this->startDate = Carbon::now()->startOfWeek(); + $this->endDate = Carbon::now()->endOfWeek(); + if (CalendarUtil::get_first_day_of_week() == Carbon::MONDAY) { + $this->startDate = $this->startDate->tomorrow(); + $this->endDate = $this->endDate->tomorrow(); + } + } + + public function setWeekendView() + { + $this->view = 'weekend'; + $start = Carbon::now(); + if ($start->format('w') == Carbon::SATURDAY) { + $start = $start->yesterday(); + } elseif ($start->format('w') != Carbon::FRIDAY) { + $start = $start->next(Carbon::FRIDAY); + } + $this->startDate = $start; + $this->endDate = Carbon::parse($this->startDate)->next(Carbon::SUNDAY); + } + + public function setMonthView() + { + $this->view = 'month'; + $this->startDate = Carbon::now()->startOfMonth(); + $this->endDate = Carbon::parse($this->startDate)->endOfMonth(); + } + + public function getOffset() + { + if (!isset($_REQUEST['start'])) { + $_REQUEST['start'] = 0; + } + return (int)$_REQUEST['start']; + } + + protected function getRangeLink($start, $end) + { + return parent::join_links( + $this->Link(), + "show", + $start->format('Ymd'), + $end->format('Ymd') + ); + } + + public function respond() + { + if (Director::is_ajax()) { + return $this->renderWith('EventList'); + } + return []; + } + + /** + * Send ical file of multiple upcoming events using ICSWriter + * + * @todo Support recurring events. + * @see ICSWriter + * @author Alex Hayes + */ + public function ical() + { + $writer = ICSWriter::create($this->data(), Director::absoluteURL('/')); + $writer->sendDownload(); + } + + /** + * @return string + */ + public function ics(HTTPRequest $r) + { + $feed = false; + $announcement = false; + $id = $r->param('ID'); + $oid = $r->param('OtherID'); + + if (stristr($id, "ICS_") !== false) { + $id = str_replace("ICS_", "", $id); + $feed = true; + } elseif(stristr($id, "announcement-") !== false) { + $id = str_replace("announcement-","",$id); + $announcement = true; + } + if (is_numeric($id) && $oid) { + if (!$feed) { + $event = DataObject::get( + $announcement ? $this->data()->getDateTimeClass() : $this->data()->getEventClass() + )->byID($id); + if (!$event) { + // No event found + return $this->httpError(404); + } + $FILENAME = $announcement ? preg_replace("/[^a-zA-Z0-9s]/", "", $event->Title) : $event->URLSegment; + } else { + $FILENAME = preg_replace("/[^a-zA-Z0-9s]/", "", urldecode($_REQUEST['title'])); + } + + $FILENAME .= ".ics"; + $HOST = $_SERVER['HTTP_HOST']; + $TIMEZONE = Calendar::config()->timezone; + $LANGUAGE = Calendar::config()->language; + $CALSCALE = "GREGORIAN"; + $parts = explode('-', $oid); + $START_TIMESTAMP = $parts[0]; + $END_TIMESTAMP = $parts[1]; + if (!$feed) { + $URL = $announcement ? $event->Calendar()->AbsoluteLink() : $event->AbsoluteLink(); + } else { + $URL = ""; + } + $UID = sprintf('%s-%s@%s', $r->param('ID'), $r->param('OtherID'), $HOST); + $TITLE = $feed ? $_REQUEST['title'] : $event->Title; + $CONTENT = $feed ? $_REQUEST['content'] : $event->obj('Content')->Summary(); + $LOCATION = $feed ? $_REQUEST['location'] : $event->Location; + $ORGANIZER = $this->getOrganiser(); + + $this->getResponse() + ->addHeader('Cache-Control','private') + ->addHeader('Content-Description','File Transfer') + ->addHeader('Content-Type','text/calendar') + ->addHeader('Content-Transfer-Encoding','binary'); + + if (stristr($_SERVER['HTTP_USER_AGENT'], "MSIE")) { + $this->getResponse()->addHeader("Content-disposition", "filename=".$FILENAME."; attachment;"); + } else { + $this->getResponse()->addHeader("Content-disposition", "attachment; filename=".$FILENAME); + } + + $result = trim(strip_tags($this->customise( + [ + 'HOST' => $HOST, + 'LANGUAGE' => $LANGUAGE, + 'TIMEZONE' => $TIMEZONE, + 'CALSCALE' => $CALSCALE, + 'UID' => $UID, + 'DTSTAMP' => date("Ymd\THis"), + 'START_TIMESTAMP' => $START_TIMESTAMP, + 'END_TIMESTAMP' => $END_TIMESTAMP, + 'URL' => $URL, + 'TITLE' => $TITLE, + 'CONTENT' => $CONTENT, + 'LOCATION' => $LOCATION, + 'ORGANIZER' => $ORGANIZER + ] + )->renderWith(['UncleCheese\EventCalendar\ics']))); + + return $result; + } + + $this->redirectBack(); + } + + /** + * @return void + */ + public function parseURL(HTTPRequest $r) + { + if (!$r->param('ID')) { + return; + } + $this->startDate = Carbon::parse(CalendarUtil::get_date_from_string($r->param('ID'))); + if ($r->param('OtherID')) { + $this->view = "range"; + $this->endDate = Carbon::parse(CalendarUtil::get_date_from_string($r->param('OtherID'))); + } else { + $d = clone $this->startDate; + switch(strlen(str_replace("-", "", $r->param('ID')))) { + case 8: + $this->view = "day"; + $this->endDate = Carbon::createFromTimestamp($d->getTimestamp()+1); + break; + + case 6: + $this->view = "month"; + $this->endDate = Carbon::parse($d->endOfMonth()->toDateString()); + break; + + case 4: + $this->view = "year"; + $this->endDate = Carbon::parse($d->endOfMonth()->toDateString()); + break; + + default: + $this->view = "default"; + $this->endDate = Carbon::parse($d->addMonths($this->DefaultFutureMonths)->toDateString()); + break; + } + } + } + + public function getEvents() + { + $eventFilter = null; + $announcementFilter = null; + $endDate = $this->endDate; + + if ($search = $this->getRequest()->getVar('s')) { + $s = Convert::raw2sql($search); + $eventFilter = "\"SiteTree\".\"Title\" LIKE '%$s%' OR \"SiteTree\".\"Content\" LIKE '%$s%'"; + $announcementFilter = "\"CalendarAnnouncement\".\"Title\" LIKE '%$s%' OR \"CalendarAnnouncement\".\"Content\" LIKE '%$s%'"; + $this->SearchQuery = $search; + $endDate = Carbon::now()->addMonths($this->DefaultFutureMonths); + } + + $all = $this->data()->getEventList( + $this->startDate ? $this->startDate->toDateString() : null, + $endDate ? $endDate->toDateString() : null, + $eventFilter, + null, + $announcementFilter + ); + + $allEventsCount = $all->count(); + $list = $all->limit($this->EventsPerPage, $this->getOffset()); + $next = $this->getOffset() + $this->EventsPerPage; + $this->MoreEvents = ($next < $allEventsCount); + $this->MoreLink = HTTP::setGetVar("start", $next); + + return $list; + } + + /** + * @return string + */ + public function DateHeader() + { + switch ($this->view) { + case "day": + return CalendarUtil::localize( + $this->startDate->getTimestamp(), + null, + CalendarUtil::ONE_DAY_HEADER + ); + break; + case "month": + return CalendarUtil::localize( + $this->startDate->getTimestamp(), + null, + CalendarUtil::MONTH_HEADER + ); + break; + case "year": + return CalendarUtil::localize( + $this->startDate->getTimestamp(), + null, + CalendarUtil::YEAR_HEADER + ); + break; + case "range": + case "week": + case "weekend": + list($strStartDate, $strEndDate) = CalendarUtil::get_date_string( + $this->startDate->toDateString(), $this->endDate->toDateString() + ); + return $strStartDate.$strEndDate; + break; + + default: + return $this->DefaultDateHeader; + break; + } + } + + /** + * @return bool + */ + public function CurrentAction($a) + { + return $this->getAction() == $a; + } + + /** + * @return string + */ + public function PreviousDayLink() + { + $start = Carbon::parse($this->startDate)->yesterday(); + return $this->getRangeLink($start, $start); + } + + /** + * @return string + */ + public function NextDayLink() + { + $start = Carbon::parse($this->startDate)->tomorrow(); + return $this->getRangeLink($start, $start); + } + + /** + * @return string + */ + public function PreviousWeekLink() + { + $start = Carbon::parse($this->startDate)->subtractWeek(); + $end = Carbon::parse($this->endDate)->subtractWeek(); + return $this->getRangeLink($start, $end); + } + + /** + * @return string + */ + public function NextWeekLink() + { + $start = Carbon::parse($this->startDate)->addWeek(); + $end = Carbon::parse($this->endDate)->addWeek(); + return $this->getRangeLink($start, $end); + } + + /** + * @return string + */ + public function NextMonthLink() + { + $start = Carbon::parse($this->startDate)->addMonth(); + $end = Carbon::parse($start)->endOfMonth(); + return $this->getRangeLink($start, $end); + } + + /** + * @return string + */ + public function PreviousMonthLink() + { + $start = Carbon::parse($this->startDate)->subMonth(); + $end = Carbon::parse($start)->endOfMonth(); + return $this->getRangeLink($start, $end); + } + + /** + * @return string + */ + public function NextWeekendLink() + { + return $this->NextWeekLink(); + } + + /** + * @return string + */ + public function PreviousWeekendLink() + { + return $this->PreviousWeekLink(); + } + + /** + * @return bool + */ + public function IsSegment($segment) + { + switch ($segment) { + case "today": + return $this->startDate->toDateString() == $this->endDate->toDateString(); + case "week": + if (CalendarUtil::get_first_day_of_week() == Carbon::MONDAY) { + return + ($this->startDate->format('w') == Carbon::MONDAY) + && ($this->startDate->format('w') == Carbon::SUNDAY); + } + return + ($this->startDate->format('w') == Carbon::SUNDAY) + && ($this->endDate->format('w') == Carbon::SATURDAY); + case "month": + return + ($this->startDate->format('j') == 1) + && (Carbon::parse($this->startDate)->endOfMonth()->format('j') == $this->endDate->format('j')); + case "weekend": + return + ($this->startDate->format('w') == Carbon::FRIDAY) + && ($this->endDate->format('w') == Carbon::SUNDAY); + } + } + + /** + * @return string + */ + public function MonthJumper() + { + return $this->renderWith('UncleCheese\EventCalendar\Includes\MonthJumper'); + } + + /** + * @return Form + */ + public function MonthJumpForm() + { + $this->parseURL($this->getRequest()); + $dummy = Carbon::parse($this->startDate); + $yearRange = range(($dummy->subYears(3)->format('Y')), ($dummy->addYears(6)->format('Y'))); + $form = Form::create( + $this, + __FUNCTION__, + FieldList::create( + $m = DropdownField::create('Month','', CalendarUtil::get_months_map('%B')), + $y = DropdownField::create('Year','', array_combine($yearRange, $yearRange)) + ), + FieldList::create( + FormAction::create('doMonthJump', _t(__CLASS__.'.JUMP', 'Go')) + ) + ); + + if ($this->startDate) { + $m->setValue($this->startDate->format('m')); + $y->setValue($this->startDate->format('Y')); + } else { + $m->setValue(date('m')); + $y->setValue(date('Y')); + } + + return $form; + } + + public function doMonthJump($data, $form) + { + return $this->redirect( + parent::join_links( + $this->Link('show'), + $data['Year'].$data['Month'] + ) + ); + } + + public function getOrganiser() + { + $organiser = $this->config()->default_organiser + ? $this->config()->default_organiser + : ":MAILTO:".Email::config()->admin_email; + + $this->extend('updateOrganiser', $organiser); + return $organiser; + } +} diff --git a/src/Pages/CalendarEvent.php b/src/Pages/CalendarEvent.php new file mode 100644 index 0000000..6f684b5 --- /dev/null +++ b/src/Pages/CalendarEvent.php @@ -0,0 +1,230 @@ + 'Text', + 'Recursion' => 'Boolean', + 'CustomRecursionType' => 'Int', + 'DailyInterval' => 'Int', + 'WeeklyInterval' => 'Int', + 'MonthlyInterval' => 'Int', + 'MonthlyRecursionType1' => 'Int', + 'MonthlyRecursionType2' => 'Int', + 'MonthlyIndex' => 'Int', + 'MonthlyDayOfWeek' => 'Int' + ]; + + private static $has_many = [ + 'DateTimes' => CalendarDateTime::class, + 'Exceptions' => RecurringException::class + ]; + + private static $many_many = [ + 'RecurringDaysOfWeek' => RecurringDayOfWeek::class, + 'RecurringDaysOfMonth' => RecurringDayOfMonth::class + ]; + + private static $icon = "unclecheese/silverstripe-event-calendar:client/dist/images/event-file.gif"; + + private static $description = "An individual event entry"; + + private static $datetime_class = CalendarDateTime::class; + + private static $can_be_root = false; + + public function getCMSFields() { + + $self = $this; + + $this->beforeUpdateCMSFields(function($f) use ($self) { + Requirements::javascript('unclecheese/silverstripe-event-calendar:client/dist/js/calendar_cms.js'); + Requirements::css('unclecheese/silverstripe-event-calendar:client/dist/css/calendar_cms.css'); + + $f->addFieldToTab("Root.Main", + TextField::create( + 'Location', + _t(Calendar::class.'.LOCATIONDESCRIPTION', 'The location for this event') + ), 'Content' + ); + + $dt = _t(__CLASS__.'DATESANDTIMES', 'Dates and Times'); + $recursion = _t(__CLASS__.'.RECURSION', 'Recursion'); + + $f->addFieldToTab("Root.$dt", + GridField::create( + 'DateTimes', + _t(Calendar::class.'.DATETIMEDESCRIPTION','Add dates for this event'), + $self->DateTimes(), + GridFieldConfig_RecordEditor::create() + ) + ); + + $f->addFieldsToTab("Root.$recursion", array( + CheckboxField::create('Recursion', _t(__CLASS__.'.REPEATEVENT','Repeat this event'))->addExtraClass('recursion'), + OptionsetField::create( + 'CustomRecursionType', + _t(__CLASS__.'.DESCRIBEINTERVAL','Describe the interval at which this event recurs.'), + [ + self::RECUR_INTERVAL_DAILY => _t(__CLASS__.'.DAILY', 'Daily'), + self::RECUR_INTERVAL_WEEKLY => _t(__CLASS__.'.WEEKLY', 'Weekly'), + self::RECUR_INTERVAL_MONTHLY => _t(__CLASS__.'.MONTHLY', 'Monthly') + ] + )->setHasEmptyDefault(true) + )); + + $f->addFieldToTab( + "Root.$recursion", + FieldGroup::create( + DropdownField::create( + 'DailyInterval', + _t(__CLASS__.'.EVERY', 'Every'), + array_combine(range(1,10), range(1,10)) + ), + LabelField::create("days", _t(__CLASS__.'.DAYS', ' day(s)')) + )->addExtraClass('dailyinterval') + ); + + $f->addFieldToTab( + "Root.$recursion", + FieldGroup::create( + DropdownField::create( + 'WeeklyInterval', + _t(__CLASS__.'.EVERY', 'Every'), + array_combine(range(1,10), range(1,10)) + ), + LabelField::create("weeks", _t(__CLASS__.'.WEEKS', ' weeks')) + )->addExtraClass('weeklyinterval') + ); + + $f->addFieldToTab( + "Root.$recursion", + ListboxField::create( + 'RecurringDaysOfWeek', + _t(__CLASS__.'.ONFOLLOWINGDAYS', 'On the following day(s)...'), + RecurringDayOfWeek::get()->map("ID", "Title")->toArray() + ) + ); + + $f->addFieldToTab( + "Root.$recursion", + FieldGroup::create( + DropdownField::create( + 'MonthlyInterval', + _t(__CLASS__.'.EVERY', 'Every'), + array_combine(range(1,10), range(1,10)) + ), + LabelField::create("months", _t(__CLASS__.'.MONTHS', ' month(s)')) + )->addExtraClass('monthlyinterval') + ); + + $f->addFieldsToTab( + "Root.$recursion", + [ + OptionsetField::create( + 'MonthlyRecursionType1', + '', + ['1' => _t(__CLASS__.'.ONTHESEDATES','On these date(s)...')] + )->setHasEmptyDefault(true), + ListboxField::create( + 'RecurringDaysOfMonth', + '', + RecurringDayOfMonth::get()->map("ID", "Value")->toArray() + ), + OptionsetField::create( + 'MonthlyRecursionType2', + '', + ['1' => _t(__CLASS__.'.ONTHE','On the...')] + )->setHasEmptyDefault(true) + ] + ); + + $f->addFieldToTab( + "Root.$recursion", + FieldGroup::create( + DropdownField::create('MonthlyIndex', '', [ + '1' => _t(__CLASS__.'.FIRST', 'First'), + '2' => _t(__CLASS__.'.SECOND', 'Second'), + '3' => _t(__CLASS__.'.THIRD', 'Third'), + '4' => _t(__CLASS__.'.FOURTH', 'Fourth'), + '5' => _t(__CLASS__.'.LAST', 'Last') + ])->setHasEmptyDefault(true), + DropdownField::create( + 'MonthlyDayOfWeek', + '', + RecurringDayOfWeek::get()->map('Value', 'Title')->toArray() + )->setHasEmptyDefault(true), + LabelField::create("ofthemonth", _t(__CLASS__.'.OFTHEMONTH', " of the month.")) + )->addExtraClass('monthlyindex') + ); + $f->addFieldToTab("Root.$recursion", + GridField::create( + 'Exceptions', + _t(__CLASS__.'.ANYEXCEPTIONS', 'Any exceptions to this pattern? Add the dates below.'), + $self->Exceptions(), + GridFieldConfig_RecordEditor::create() + ) + ); + + }); + + $f = parent::getCMSFields(); + + return $f; + } + + public function getRecursionReader() + { + return RecursionReader::create($this); + } + + public function getDateTimeClass() + { + return $this->config()->datetime_class; + } + + public function getCalendarWidget() + { + return $this->Parent()->getCalendarWidget(); + } + +} \ No newline at end of file diff --git a/src/Pages/CalendarEventController.php b/src/Pages/CalendarEventController.php new file mode 100644 index 0000000..dd62094 --- /dev/null +++ b/src/Pages/CalendarEventController.php @@ -0,0 +1,111 @@ +include_default_css) { + Requirements::css('unclecheese/silverstripe-event-calendar:client/dist/css/calendar.css'); + } + } + + /** + * @return bool + */ + public function MultipleDates() + { + return $this->DateAndTime()->count() > 1; + } + + /** + * @return DataList + */ + public function DateAndTime() + { + return DataList::create($this->data()->getDateTimeClass()) + ->filter("EventID", $this->ID) + ->sort("StartDate ASC"); + } + + /** + * @return DataList + */ + public function UpcomingDates($limit = 3) + { + return $this->DateAndTime() + ->where("StartDate:GreaterThanOrEqual", "DATE(NOW())") + ->limit($limit); + } + + /** + * @return DataList + */ + public function getOtherDates() + { + if (!isset($_REQUEST['date'])) { + $dateObj = $this->DateAndTime()->first(); + if (!$date_obj) { + return false; + } + $date = $dateObj->StartDate; + } elseif (strtotime($_REQUEST['date']) > 0) { + $date = date('Y-m-d', strtotime($_REQUEST['date'])); + } + + $cal = $this->Parent(); + + if ($this->Recursion == 1) { + $datetimeObj = DataList::create($this->data()->getDateTimeClass()) + ->filter('EventID', $this->ID) + ->first(); + $datetimeObj->StartDate = $date; + return $cal->getNextRecurringEvents($this, $datetimeObj); + } + + return $this->DateAndTime() + ->exclude("StartDate", $date) + ->limit($cal->OtherDatesCount); + } + + /** + * @return mixed + */ + public function getCurrentDate() + { + $allDates = DataList::create($this->data()->getDateTimeClass()) + ->filter("EventID", $this->ID) + ->sort("StartDate ASC"); + if (!isset($_REQUEST['date'])) { + // If no date filter specified, return the first one + return $allDates->first(); + } + if (strtotime($_REQUEST['date']) > 0) { + $date = date('Y-m-d', strtotime($_REQUEST['date'])); + if ($this->Recursion) { + $datetime = $allDates->first(); + if ($datetime) { + $datetime->StartDate = $date; + $datetime->EndDate = $date; + return $datetime; + } + } + return $allDates->filter("StartDate", $date)->first(); + } + } +} \ No newline at end of file diff --git a/code/CachedCalendarBuildTask.php b/src/Tasks/CachedCalendarBuildTask.php old mode 100755 new mode 100644 similarity index 64% rename from code/CachedCalendarBuildTask.php rename to src/Tasks/CachedCalendarBuildTask.php index afd2c66..2e5894d --- a/code/CachedCalendarBuildTask.php +++ b/src/Tasks/CachedCalendarBuildTask.php @@ -1,7 +1,12 @@ process(); } - -} +} \ No newline at end of file diff --git a/code/CachedCalendarTask.php b/src/Tasks/CachedCalendarTask.php.obselete old mode 100755 new mode 100644 similarity index 75% rename from code/CachedCalendarTask.php rename to src/Tasks/CachedCalendarTask.php.obselete index 21b2472..b32945c --- a/code/CachedCalendarTask.php +++ b/src/Tasks/CachedCalendarTask.php.obselete @@ -1,25 +1,32 @@ table_name); $future_years = $this->config()->cache_future_years; - foreach(Calendar::get() as $calendar) { + foreach (Calendar::get() as $calendar) { echo "

Caching calendar '$calendar->Title'

\n"; - foreach($calendar->getAllCalendars() as $c) { - foreach($c->AllChildren() as $event) { + foreach ($calendar->getAllCalendars() as $c) { + foreach ($c->AllChildren() as $event) { // All the dates of regular events if($event->Recursion) { echo "

Creating recurring events for '$event->Title'

\n"; $i = 0; $dt = $event->DateTimes()->first(); - if(!$dt) continue; - if($dt->EndDate) { - $end_date = sfDate::getInstance($dt->EndDate); + if (!$dt) { + continue; } - else { + if ($dt->EndDate) { + $end_date = sfDate::getInstance($dt->EndDate); + } else { $end_date = sfDate::getInstance()->addYear($future_years); } $start_date = sfDate::getInstance($dt->StartDate); diff --git a/src/View/CalendarWidget.php b/src/View/CalendarWidget.php new file mode 100644 index 0000000..7177c55 --- /dev/null +++ b/src/View/CalendarWidget.php @@ -0,0 +1,68 @@ +calendar = $calendar; + } + + public function setOption($k, $v) + { + $this->options[$k] = $v; + } + + public function getDataAttributes() + { + $attributes = ""; + $this->options['url'] = $this->calendar->Link(); + foreach ($this->options as $opt => $value) { + $attributes .= sprintf('data-%s="%s" ', $opt, Convert::raw2att($value)); + } + return $attributes; + } + + public function setSelectionStart($date) + { + $this->selectionStart = $date; + return $this; + } + + public function setSelectionEnd($date) + { + $this->selectionEnd = $date; + return $this; + } + + public function forTemplate() + { + if (!Calendar::config()->jquery_included) { + Requirements::javascript('silverstripe/admin:thirdparty/jquery/jquery.min.js'); + } + Requirements::javascript("unclecheese/silverstripe-event-calendar:client/dist/js/calendar_widget.js"); + + $localeFile = _t(__CLASS__.'.DATEJSFILE', 'unclecheese/silverstripe-event-calendar:client/dist/js/lang/calendar_en.js'); + Requirements::javascript($localeFile); + + Requirements::javascript("unclecheese/silverstripe-event-calendar:client/dist/js/calendar_widget_init.js"); + Requirements::css("unclecheese/silverstripe-event-calendar:client/dist/css/calendar_widget.css"); + + return '
getDataAttributes() . '>
'; + } +} diff --git a/templates/.gitignore b/templates/.gitignore deleted file mode 100755 index aab4112..0000000 --- a/templates/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.tmp* diff --git a/templates/CalendarWidget.ss b/templates/CalendarWidget.ss deleted file mode 100755 index 647400d..0000000 --- a/templates/CalendarWidget.ss +++ /dev/null @@ -1,7 +0,0 @@ -<% require javascript(sapphire/thirdparty/jquery/jquery.js) %> -<% require javascript(event_calendar/javascript/jquery.date.js) %> -<% require javascript(event_calendar/javascript/jquery.datePicker.js) %> -<% require javascript(event_calendar/javascript/calendar_core.js) %> -<% require javascript(event_calendar/javascript/calendar_widget.js) %> -<% require css(event_calendar/css/calendar_widget.css) %> -
loading
diff --git a/templates/Includes/EventList.ss b/templates/Includes/EventList.ss deleted file mode 100755 index 91ed66e..0000000 --- a/templates/Includes/EventList.ss +++ /dev/null @@ -1,27 +0,0 @@ - -<% if MoreEvents %> -<% _t('Calendar.VIEWMOREEVENTS','View more events...') %> -<% end_if %> diff --git a/templates/Includes/MonthJumper.ss b/templates/Includes/MonthJumper.ss deleted file mode 100755 index aa8af85..0000000 --- a/templates/Includes/MonthJumper.ss +++ /dev/null @@ -1,4 +0,0 @@ -

<% _t('Calendar.JUMPTOMONTH','Jump to a Month') %>

-
- $MonthJumpForm -
\ No newline at end of file diff --git a/templates/Includes/QuickNav.ss b/templates/Includes/QuickNav.ss deleted file mode 100755 index c598eb0..0000000 --- a/templates/Includes/QuickNav.ss +++ /dev/null @@ -1,18 +0,0 @@ - - - \ No newline at end of file diff --git a/templates/Layout/Calendar.ss b/templates/Layout/Calendar.ss deleted file mode 100755 index 78b1ddf..0000000 --- a/templates/Layout/Calendar.ss +++ /dev/null @@ -1,19 +0,0 @@ - -

$Title

-

<% _t('SUBSCRIBE','Calendar RSS Feed') %>

-$Content - -
- $CalendarWidget - $MonthJumper - <% include QuickNav %> -
- -

$DateHeader

-<% if Events %> -
- <% include EventList %> -
-<% else %> -

<% _t('NOEVENTS','There are no events') %>.

-<% end_if %> \ No newline at end of file diff --git a/templates/Layout/CalendarEvent.ss b/templates/Layout/CalendarEvent.ss deleted file mode 100755 index 5a1e8a0..0000000 --- a/templates/Layout/CalendarEvent.ss +++ /dev/null @@ -1,26 +0,0 @@ -$CalendarWidget -$MonthJumper -

« Back to $Parent.Title

-
-

$Title

- - <% with CurrentDate %> -

$DateRange<% if AllDay %> <% _t('Calendar.ALLDAY','All Day') %><% else %><% if StartTime %> $TimeRange<% end_if %><% end_if %>

-

<% _t('CalendarEvent.ADD','Add this to Calendar') %>

- <% end_with %> - - $Content - - <% if OtherDates %> -
-

<% _t('CalendarEvent.ADDITIONALDATES','Additional Dates for this Event') %>

- -
- <% end_if %> -
-$Form -$PageComments diff --git a/templates/MonthJumper.ss b/templates/MonthJumper.ss deleted file mode 100755 index 267741a..0000000 --- a/templates/MonthJumper.ss +++ /dev/null @@ -1,4 +0,0 @@ -

<% _t('Calendar.JUMPTOMONTH','Jump to a Month') %>

-
- $MonthJumpForm -
diff --git a/templates/MonthNavigator.ss b/templates/MonthNavigator.ss deleted file mode 100755 index 19cbe1a..0000000 --- a/templates/MonthNavigator.ss +++ /dev/null @@ -1,5 +0,0 @@ -<% require javascript(event_calendar/javascript/calendar_core.js) %> -<% require javascript(event_calendar/javascript/month_navigator.js) %> -
-$Dropdown.Field -
\ No newline at end of file diff --git a/templates/UncleCheese/EventCalendar/Includes/CalendarWidget.ss b/templates/UncleCheese/EventCalendar/Includes/CalendarWidget.ss new file mode 100644 index 0000000..0032f1f --- /dev/null +++ b/templates/UncleCheese/EventCalendar/Includes/CalendarWidget.ss @@ -0,0 +1,7 @@ +<% require javascript('silverstripe/admin/thirdparty/jquery/jquery.min.js) %> +<% require javascript('unclecheese/event-calendar/client/dist/js/jquery.date.js') %> +<% require javascript('unclecheese/event-calendar/client/dist/js/jquery.datePicker.js') %> +<% require javascript('unclecheese/event-calendar/client/dist/js/calendar_core.js') %> +<% require javascript('unclecheese/event-calendar/client/dist/js/calendar_widget.js') %> +<% require css('unclecheese/event-calendar/client/dist/css/calendar_widget.css') %> +
loading
diff --git a/templates/UncleCheese/EventCalendar/Includes/EventList.ss b/templates/UncleCheese/EventCalendar/Includes/EventList.ss new file mode 100644 index 0000000..78ad09b --- /dev/null +++ b/templates/UncleCheese/EventCalendar/Includes/EventList.ss @@ -0,0 +1,45 @@ + +<% if $MoreEvents %> + <% _t('UncleCheese\EventCalendar\Pages\Calendar.VIEWMOREEVENTS', 'View more events...') %> +<% end_if %> diff --git a/templates/UncleCheese/EventCalendar/Includes/MonthJumper.ss b/templates/UncleCheese/EventCalendar/Includes/MonthJumper.ss new file mode 100644 index 0000000..b480b0c --- /dev/null +++ b/templates/UncleCheese/EventCalendar/Includes/MonthJumper.ss @@ -0,0 +1,4 @@ +

<% _t('UncleCheese\EventCalendar\Pages\Calendar.JUMPTOMONTH','Jump to a Month') %>

+
+ $MonthJumpForm +
\ No newline at end of file diff --git a/templates/UncleCheese/EventCalendar/Includes/QuickNav.ss b/templates/UncleCheese/EventCalendar/Includes/QuickNav.ss new file mode 100644 index 0000000..dc25851 --- /dev/null +++ b/templates/UncleCheese/EventCalendar/Includes/QuickNav.ss @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff --git a/templates/UncleCheese/EventCalendar/Models/CalendarDateTime/DateRange.ss b/templates/UncleCheese/EventCalendar/Models/CalendarDateTime/DateRange.ss new file mode 100644 index 0000000..28f2596 --- /dev/null +++ b/templates/UncleCheese/EventCalendar/Models/CalendarDateTime/DateRange.ss @@ -0,0 +1 @@ +$StartDate<% if $EndDate %>-<% end_if %>$EndDate \ No newline at end of file diff --git a/templates/UncleCheese/EventCalendar/Pages/Layout/Calendar.ss b/templates/UncleCheese/EventCalendar/Pages/Layout/Calendar.ss new file mode 100644 index 0000000..e87554a --- /dev/null +++ b/templates/UncleCheese/EventCalendar/Pages/Layout/Calendar.ss @@ -0,0 +1,21 @@ + +

$Title

+ +

<% _t('UncleCheese\EventCalendar\Pages\Calendar.SUBSCRIBE', 'Calendar RSS Feed') %>

+ +$Content + +
+ $CalendarWidget + <% include UncleCheese\EventCalendar\Includes\MonthJumper %> + <% include UncleCheese\EventCalendar\Includes\QuickNav %> +
+ +

$DateHeader

+<% if $Events %> +
+ <% include UncleCheese\EventCalendar\Includes\EventList %> +
+<% else %> +

<% _t('UncleCheese\EventCalendar\Pages\Calendar.NOEVENTS','There are no events') %>.

+<% end_if %> \ No newline at end of file diff --git a/templates/UncleCheese/EventCalendar/Pages/Layout/CalendarEvent.ss b/templates/UncleCheese/EventCalendar/Pages/Layout/CalendarEvent.ss new file mode 100644 index 0000000..fddaf2b --- /dev/null +++ b/templates/UncleCheese/EventCalendar/Pages/Layout/CalendarEvent.ss @@ -0,0 +1,27 @@ +$CalendarWidget +$MonthJumper +

« Back to $Parent.Title

+ +
+

$Title

+ + <% with CurrentDate %> +

$DateRange<% if AllDay %> <% _t('UncleCheese\EventCalendar\Pages\Calendar.ALLDAY','All Day') %><% else %><% if StartTime %> $TimeRange<% end_if %><% end_if %>

+

<% _t('UncleCheese\EventCalendar\Pages\CalendarEvent.ADD','Add this to Calendar') %>

+ <% end_with %> + + $Content + + <% if OtherDates %> +
+

<% _t('UncleCheese\EventCalendar\Pages\CalendarEvent.ADDITIONALDATES','Additional Dates for this Event') %>

+ +
+ <% end_if %> +
+$Form +$PageComments diff --git a/templates/ics.ss b/templates/UncleCheese/EventCalendar/ics.ss old mode 100755 new mode 100644 similarity index 100% rename from templates/ics.ss rename to templates/UncleCheese/EventCalendar/ics.ss diff --git a/templates/LiveCalendarWidget.ss b/templates/UncleCheese/LiveCalendarWidget.ss old mode 100755 new mode 100644 similarity index 91% rename from templates/LiveCalendarWidget.ss rename to templates/UncleCheese/LiveCalendarWidget.ss index acd8bca..c2e2baa --- a/templates/LiveCalendarWidget.ss +++ b/templates/UncleCheese/LiveCalendarWidget.ss @@ -1,7 +1,7 @@ <% require javascript(event_calendar/javascript/jquery-1.2.6.min.js) %> <% require javascript(event_calendar/javascript/live_calendar_widget.js) %> <% require css(event_calendar/css/live_calendar_widget.css) %> -<% if Ajax %><% else %>
<% end_if %> +<% if not $Ajax %>
<% end_if %>
@@ -11,7 +11,7 @@   - <% loop Weeks %> + <% loop $Weeks %> <% loop Days %>
@@ -57,4 +57,4 @@
-<% if Ajax %><% else %>
<% end_if %> +<% if not $Ajax %>
<% end_if %>