diff --git a/CHANGELOG.md b/CHANGELOG.md index d9767a2c..b9b22c42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Solspace Calendar Changelog +## 2.0.4 - 2018-07-04 +### Added +- Added `endsBefore`, `endsBeforeOrAt`, `startsAfter` and `startsAfterOrAt` parameters to `calendar.events` function, for more flexibility to narrow down results. + +### Fixed +- Fixed a bug where editing events would display a localized time in time pickers for start and end dates in recent versions of Craft. +- Fixed a bug where events with Select Dates rule were not having selected dates show up in calendars. +- Fixed a bug where the `rangeEnd` parameter was not correctly setting end time to `23:59:59`. +- Fixed a bug where dragging and dropping disabled events in CP Month/Week/Day views was not working. +- Fixed a bug where the Calendar 1.x to 2.x (Craft 2.x to 3.x) migration was not correctly fully migrating the Calendar fieldtype. + ## 2.0.3 - 2018-06-12 ### Changed - Updated Demo Templates routes to be extension agnostic (no longer specifically include `.html` in route path). diff --git a/composer.json b/composer.json index 685b3536..5e6215f0 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "solspace/craft3-calendar", "description": "The most powerful event management plugin for Craft.", - "version": "2.0.3", + "version": "2.0.4", "type": "craft-plugin", "minimum-stability": "dev", "authors": [ @@ -25,7 +25,7 @@ } }, "extra": { - "schemaVersion": "2.0.2", + "schemaVersion": "2.0.4", "handle": "calendar", "class": "Solspace\\Calendar\\Calendar", "name": "Calendar", diff --git a/src/Controllers/EventsApiController.php b/src/Controllers/EventsApiController.php index fff6adcb..b3b3dd30 100644 --- a/src/Controllers/EventsApiController.php +++ b/src/Controllers/EventsApiController.php @@ -298,7 +298,7 @@ private function validateAndReturnModificationData(): array $isAllDay = \Craft::$app->request->post('isAllDay') === 'true'; $deltaSeconds = \Craft::$app->request->post('deltaSeconds'); - $event = $this->getEventsService()->getEventById($eventId, $siteId); + $event = $this->getEventsService()->getEventById($eventId, $siteId, true); if ($event) { CalendarPermissionHelper::requireCalendarEditPermissions($event->getCalendar()); diff --git a/src/Controllers/EventsController.php b/src/Controllers/EventsController.php index 9c92aabb..d4a121d7 100644 --- a/src/Controllers/EventsController.php +++ b/src/Controllers/EventsController.php @@ -77,6 +77,11 @@ public function actionCreateEvent(string $handle, string $siteHandle = null): Re $site = \Craft::$app->sites->currentSite; } + $locale = $site->language; + $locale = str_replace('_', '-', strtolower($locale)); + + EventEditBundle::$locale = $locale; + $event = Event::create($site->id); $event->calendarId = $calendar->id; @@ -109,9 +114,8 @@ public function actionEditEvent(int $id, string $siteHandle = null): Response $siteId = $site->id; $locale = $site->language; $locale = str_replace('_', '-', strtolower($locale)); - if (file_exists(__DIR__ . '/../Resources/js/lib/moment/locale/' . $locale . '.js')) { - \Craft::$app->view->registerJsFile('calendar/js/lib/moment/locale/' . $locale . '.js'); - } + + EventEditBundle::$locale = $locale; } $event = $this->getEventsService()->getEventById($id, $siteId); diff --git a/src/Elements/Db/EventQuery.php b/src/Elements/Db/EventQuery.php index b466b886..1a3b7bfd 100644 --- a/src/Elements/Db/EventQuery.php +++ b/src/Elements/Db/EventQuery.php @@ -48,12 +48,24 @@ class EventQuery extends ElementQuery implements \Countable /** @var \DateTime */ private $startsBeforeOrAt; + /** @var \DateTime */ + private $startsAfter; + + /** @var \DateTime */ + private $startsAfterOrAt; + /** @var \DateTime */ private $endsAfter; /** @var \DateTime */ private $endsAfterOrAt; + /** @var \DateTime */ + private $endsBefore; + + /** @var \DateTime */ + private $endsBeforeOrAt; + /** @var bool */ private $allDay = false; @@ -208,6 +220,30 @@ public function setStartsBeforeOrAt($startsBeforeOrAt): EventQuery return $this; } + /** + * @param \DateTime $startsAfter + * + * @return EventQuery + */ + public function setStartsAfter($startsAfter): EventQuery + { + $this->startsAfter = $this->parseCarbon($startsAfter); + + return $this; + } + + /** + * @param \DateTime $startsAfterOrAt + * + * @return EventQuery + */ + public function setStartsAfterOrAt($startsAfterOrAt): EventQuery + { + $this->startsAfterOrAt = $this->parseCarbon($startsAfterOrAt); + + return $this; + } + /** * @param \DateTime $endsAfter * @@ -232,6 +268,30 @@ public function setEndsAfterOrAt($endsAfterOrAt): EventQuery return $this; } + /** + * @param \DateTime $endsBefore + * + * @return EventQuery + */ + public function setEndsBefore($endsBefore): EventQuery + { + $this->endsBefore = $this->parseCarbon($endsBefore); + + return $this; + } + + /** + * @param \DateTime $endsBeforeOrAt + * + * @return EventQuery + */ + public function setEndsBeforeOrAt($endsBeforeOrAt): EventQuery + { + $this->endsBeforeOrAt = $this->parseCarbon($endsBeforeOrAt); + + return $this; + } + /** * @param bool $value * @@ -288,6 +348,9 @@ public function setRangeStart($rangeStart = null): EventQuery public function setRangeEnd($rangeEnd = null): EventQuery { $this->rangeEnd = $this->parseCarbon($rangeEnd); + if ($this->rangeEnd->format('His') === '000000') { + $this->rangeEnd->setTime(23, 59, 59); + } return $this; } @@ -619,6 +682,26 @@ protected function beforePrepare(): bool ); } + if ($this->startsAfter) { + $this->subQuery->andWhere( + Db::parseParam( + $table . '.[[startDate]]', + $this->extractDateAsFormattedString($this->startsAfter), + '>' + ) + ); + } + + if ($this->startsAfterOrAt) { + $this->subQuery->andWhere( + Db::parseParam( + $table . '.[[startDate]]', + $this->extractDateAsFormattedString($this->startsAfterOrAt), + '>=' + ) + ); + } + if ($this->endsAfter) { $this->subQuery->andWhere( Db::parseParam( @@ -639,6 +722,26 @@ protected function beforePrepare(): bool ); } + if ($this->endsBefore) { + $this->subQuery->andWhere( + Db::parseParam( + $table . '.[[endDate]]', + $this->extractDateAsFormattedString($this->endsBefore), + '<' + ) + ); + } + + if ($this->endsBeforeOrAt) { + $this->subQuery->andWhere( + Db::parseParam( + $table . '.[[endDate]]', + $this->extractDateAsFormattedString($this->endsBeforeOrAt), + '<=' + ) + ); + } + if ($this->endDate) { $this->subQuery->andWhere( Db::parseParam( @@ -664,13 +767,7 @@ protected function beforePrepare(): bool } if ($this->rangeEnd) { - $rangeEnd = $this->rangeEnd->copy(); - - if ($rangeEnd->format('His') === '000000') { - $rangeEnd->setTime(23, 59, 59); - } - - $rangeEndString = $this->extractDateAsFormattedString($rangeEnd); + $rangeEndString = $this->extractDateAsFormattedString($this->rangeEnd); $this->subQuery->andWhere( "$table.[[startDate]] <= :rangeEnd OR $table.[[freq]] = :freq", diff --git a/src/Resources/Bundles/EventEditBundle.php b/src/Resources/Bundles/EventEditBundle.php index e0ad2520..76e89066 100644 --- a/src/Resources/Bundles/EventEditBundle.php +++ b/src/Resources/Bundles/EventEditBundle.php @@ -7,15 +7,29 @@ class EventEditBundle extends CalendarAssetBundle { + /** @var string - worst hack ever made */ + static public $locale; + /** * @return array */ public function getScripts(): array { - return [ + $scripts = [ 'js/lib/fullcalendar/lib/moment.min.js', 'js/src/event-edit.js', ]; + + if (self::$locale) { + $locale = self::$locale; + + $localeJsPath = __DIR__ . '/../Resources/js/lib/moment/locale/' . $locale . '.js'; + if (file_exists($localeJsPath)) { + $scripts[] = 'js/lib/moment/locale/' . $locale . '.js'; + } + } + + return $scripts; } /** diff --git a/src/Services/EventsService.php b/src/Services/EventsService.php index 2301ef2b..07f10acd 100644 --- a/src/Services/EventsService.php +++ b/src/Services/EventsService.php @@ -3,6 +3,7 @@ namespace Solspace\Calendar\Services; use craft\base\Component; +use craft\base\Element; use craft\base\ElementInterface; use craft\db\Query; use craft\events\SiteEvent; @@ -27,16 +28,18 @@ class EventsService extends Component /** * Returns an event by its ID. * - * @param int $eventId - * @param int $siteId + * @param int $eventId + * @param int $siteId + * @param bool $includeDisabled * * @return Event|ElementInterface */ - public function getEventById(int $eventId, int $siteId = null): Event + public function getEventById(int $eventId, int $siteId = null, bool $includeDisabled = false): Event { $query = Event::find() ->setAllowedCalendarsOnly(false) ->enabledForSite(false) + ->status($includeDisabled ? null : Element::STATUS_ENABLED) ->id($eventId); if (null !== $siteId) { @@ -51,15 +54,17 @@ public function getEventById(int $eventId, int $siteId = null): Event * * @param string $slug * @param int $siteId + * @param bool $includeDisabled * * @return Event|ElementInterface */ - public function getEventBySlug(string $slug, int $siteId = null): Event + public function getEventBySlug(string $slug, int $siteId = null, bool $includeDisabled = false): Event { return Event::find() ->slug($slug) ->setAllowedCalendarsOnly(false) ->enabledForSite(false) + ->status($includeDisabled ? null : Element::STATUS_ENABLED) ->siteId($siteId) ->one(); } diff --git a/src/Services/SelectDatesService.php b/src/Services/SelectDatesService.php index f03419a2..ef2e33c3 100644 --- a/src/Services/SelectDatesService.php +++ b/src/Services/SelectDatesService.php @@ -12,6 +12,8 @@ class SelectDatesService extends Component { + static private $cachedDates = []; + /** * Returns a list of Calendar_SelectDateModel's if any are found * @@ -47,39 +49,38 @@ public function getSelectDatesForEventId( return []; } - $conditions = ['eventId' => $eventId]; + $hash = $this->getCacheHash($eventId, $rangeStart, $rangeEnd); + if (!isset(self::$cachedDates[$hash])) { + $query = (new Query()) + ->select('*') + ->from(SelectDateRecord::TABLE) + ->where(['eventId' => $eventId]) + ->orderBy(['date' => SORT_ASC]); - if ($rangeStart) { - $conditions[] = '>='; - $conditions[] = 'date'; - $conditions[] = $rangeStart->format('Y-m-d'); - } + if ($rangeStart) { + $query->andWhere('date >= :startRange', ['startRange' => $rangeStart->format('Y-m-d')]); + } - if ($rangeEnd) { - $conditions[] = '<='; - $conditions[] = 'date'; - $conditions[] = $rangeStart->format('Y-m-d'); - } + if ($rangeEnd) { + $query->andWhere('date <= :endRange', ['endRange' => $rangeEnd->format('Y-m-d')]); + } - $selectDateRecords = SelectDateRecord::findAll($conditions); - $selectDateModels = []; - foreach ($selectDateRecords as $record) { - $model = new SelectDateModel(); - $model->id = $record->id; - $model->eventId = $record->eventId; - $model->date = new Carbon($record->date, DateHelper::UTC); + $dates = $query->all(); - $selectDateModels[] = $model; - } + $selectDateModels = []; + foreach ($dates as $data) { + $model = new SelectDateModel(); + $model->id = (int) $data['id']; + $model->eventId = (int) $data['eventId']; + $model->date = new Carbon($data['date'], DateHelper::UTC); - usort( - $selectDateModels, - function (SelectDateModel $dateA, SelectDateModel $dateB) { - return $dateA <=> $dateB; + $selectDateModels[] = $model; } - ); - return $selectDateModels; + self::$cachedDates[$hash] = $selectDateModels; + } + + return self::$cachedDates[$hash]; } /** @@ -123,7 +124,7 @@ public function saveDates(Event $event, array $dates) foreach ($dates as $selectDate) { $selectDateRecord = new SelectDateRecord(); $selectDateRecord->eventId = $event->id; - $selectDateRecord->date = new \DateTime($selectDate); + $selectDateRecord->date = new Carbon($selectDate, DateHelper::UTC); $selectDateRecord->save(); } @@ -146,4 +147,26 @@ public function removeDate(Event $event, \DateTime $date) $record->delete(); } } + + /** + * @param int $eventId + * @param \DateTime $rangeStart + * @param \DateTime $rangeEnd + * + * @return string + */ + private function getCacheHash(int $eventId, \DateTime $rangeStart = null, \DateTime $rangeEnd = null): string + { + $string = $eventId; + + if ($rangeStart) { + $string .= $rangeStart->format('YmdHis'); + } + + if ($rangeEnd) { + $string .= $rangeEnd->format('YmdHis'); + } + + return sha1($string); + } } diff --git a/src/codepack/templates/edit.html b/src/codepack/templates/edit.html index 226915a5..44dbd8df 100644 --- a/src/codepack/templates/edit.html +++ b/src/codepack/templates/edit.html @@ -397,7 +397,7 @@

Event Edit

style="color: darkred; text-decoration: none; position: relative; top: 1px;"> - {{ exception.date|date('l, F j, Y') }} + {{ exception.date.format('l, F j, Y') }} {% endfor %} @@ -423,7 +423,7 @@

Event Edit

style="color: darkred; text-decoration: none; position: relative; top: 1px;"> - {{ selectDate|date('l, F j, Y') }} + {{ selectDate.format('l, F j, Y') }} {% endfor %} diff --git a/src/codepack/templates/fullcalendar.html b/src/codepack/templates/fullcalendar.html index 9ce90b00..52aa9834 100644 --- a/src/codepack/templates/fullcalendar.html +++ b/src/codepack/templates/fullcalendar.html @@ -49,7 +49,7 @@

Full Calendar

var $calendar = $("#calendar-wrapper"); var canEditEvents = {{ currentUser and currentUser.can('calendar-manageEvents') ? 'true' : 'false' }}; - {% set defaultDate = now|date("Y-m-d") %} + {% set defaultDate = now.format("Y-m-d") %} {% if segment3 and segment4 and segment5 %} {% set defaultDate = segment3 ~ "-" ~ segment4 ~ "-" ~ segment5 %} {% endif %} diff --git a/src/codepack/templates/layouts/_layout.html b/src/codepack/templates/layouts/_layout.html index de8bb65a..a37580cf 100755 --- a/src/codepack/templates/layouts/_layout.html +++ b/src/codepack/templates/layouts/_layout.html @@ -34,7 +34,7 @@ {% block footer %} {% endblock %} diff --git a/src/migrations/m180619_120655_MigrateCalendarElementsAndLayouts.php b/src/migrations/m180619_120655_MigrateCalendarElementsAndLayouts.php new file mode 100644 index 00000000..4f2505b4 --- /dev/null +++ b/src/migrations/m180619_120655_MigrateCalendarElementsAndLayouts.php @@ -0,0 +1,99 @@ +update( + '{{%elements}}', + ['type' => EventFieldType::class], + ['type' => 'Calendar_Event'], + [], + false + ); + + $this->update( + '{{%fieldlayouts}}', + ['type' => EventFieldType::class], + ['type' => 'Calendar_Event'], + [], + false + ); + + $items = (new Query()) + ->select(['id', 'settings']) + ->from('{{%fields}}') + ->where(['type' => EventFieldType::class]) + ->all(); + + foreach ($items as $item) { + $id = $item['id']; + $settings = $item['settings']; + + if (false !== strpos($settings, 'targetLocale')) { + $settings = str_replace('targetLocale', 'targetSiteId', $settings); + + $this->update( + '{{%fields}}', + ['settings' => $settings], + ['id' => $id] + ); + } + } + } + + /** + * @inheritdoc + */ + public function safeDown() + { + $this->update( + '{{%elements}}', + ['type' => 'Calendar_Event'], + ['type' => EventFieldType::class], + [], + false + ); + + $this->update( + '{{%fieldlayouts}}', + ['type' => 'Calendar_Event'], + ['type' => EventFieldType::class], + [], + false + ); + + $items = (new Query()) + ->select(['id', 'settings']) + ->from('{{%fields}}') + ->where(['type' => EventFieldType::class]) + ->all(); + + foreach ($items as $item) { + $id = $item['id']; + $settings = $item['settings']; + + if (false !== strpos($settings, 'targetSiteId')) { + $settings = str_replace('targetSiteId', 'targetLocale', $settings); + + $this->update( + '{{%fields}}', + ['settings' => $settings], + ['id' => $id] + ); + } + } + } +} diff --git a/src/migrations/m180628_091905_MigrateSelectDates.php b/src/migrations/m180628_091905_MigrateSelectDates.php new file mode 100644 index 00000000..3dd5adf1 --- /dev/null +++ b/src/migrations/m180628_091905_MigrateSelectDates.php @@ -0,0 +1,51 @@ +select('*') + ->from($table) + ->orderBy(['date' => SORT_ASC]) + ->all(); + + foreach ($query as $data) { + $date = $data['date']; + $carb = new Carbon($date, DateHelper::UTC); + $carb->setTimezone(date_default_timezone_get()); + + $string = $carb->toDateTimeString(); + + $this->update( + $table, + ['date' => $string], + ['id' => $data['id']] + ); + } + } + + /** + * @inheritdoc + */ + public function safeDown() + { + echo "m180628_091905_MigrateSelectDates cannot be reverted.\n"; + return false; + } +} diff --git a/src/templates/field/_event.html b/src/templates/field/_event.html index 8bd04cc5..9cab01a4 100644 --- a/src/templates/field/_event.html +++ b/src/templates/field/_event.html @@ -43,10 +43,10 @@