diff --git a/CHANGELOG.md b/CHANGELOG.md index 7089ab8..4cc8c3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Solspace Freeform Changelog +## 2.0.0-beta.6 - 2018-03-20 +### Fixed +- Actually added Calendar 1.x to 2.x (Craft 2.x to 3.x) migration path (sorry!). +- Fixed a bug where Live Preview would show duplicates of some Calendar fields if the calendar layout didn't have any custom fields assigned to it. +- Fixed a bug where the `calendar.events` function was localizing date ranges. +- Fixed a bug where translations were not correctly being rendered in some areas. +- Fixed a bug where reinstalling Demo Templates would generate extra duplicate routes. +- Fixed a bug where Calendar CP would not respect non-US date formatting. +- Fixed a bug where adding new Sites wouldn't populate the necessary event and calendar sites tables. + ## 2.0.0-beta.5 - 2018-03-19 ### Fixed - Added Live Preview functionality back. diff --git a/README.md b/README.md index fd8bab2..b065f6e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Calendar is the most powerful event management and calendaring plugin on the market for Craft CMS. The intuitive interface allows you to create events with complex recurring event rules and exclusions, while the flexible templating offers a variety of options to satisfy your calendaring needs. -🚨 **IMPORTANT: The first few releases will be a bit rough around the edges, and there are a number of known issues. We don't recommend using Calendar in production environments just yet, but feel free to try it out now. Any issues can be reported on GitHub Issues only please.** 🚨 +🚨 **IMPORTANT:** *Calendar is becoming more stable now, but we still don't yet recommend using in production environments. If you do use Calendar, please take caution and always backup your site before upgrading, etc. 🐛 Any issues can be reported on [GitHub Issues](https://github.com/solspace/craft3-calendar/issues) only please.* 🚨 ![Screenshot](src/icon.svg) diff --git a/composer.json b/composer.json index 3a709ee..0a3dde8 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.0-beta.5", + "version": "2.0.0-beta.6", "type": "craft-plugin", "minimum-stability": "dev", "authors": [ @@ -25,7 +25,7 @@ } }, "extra": { - "schemaVersion": "2.0.0", + "schemaVersion": "2.0.1", "handle": "calendar", "class": "Solspace\\Calendar\\Calendar", "name": "Calendar", diff --git a/src/Calendar.php b/src/Calendar.php index bc5b8ff..53f742b 100644 --- a/src/Calendar.php +++ b/src/Calendar.php @@ -8,6 +8,7 @@ use craft\events\RegisterUserPermissionsEvent; use craft\services\Dashboard; use craft\services\Fields; +use craft\services\Sites; use craft\services\UserPermissions; use craft\web\twig\variables\CraftVariable; use craft\web\UrlManager; @@ -162,6 +163,9 @@ function (RegisterComponentTypesEvent $event) { // craft()->on('i18n.onAddLocale', [craft()->calendar_events, 'addLocaleHandler']); // craft()->on('i18n.onAddLocale', [craft()->calendar_calendars, 'addLocaleHandler']); + Event::on(Sites::class, Sites::EVENT_AFTER_SAVE_SITE, [$this->events, 'addSiteHandler']); + Event::on(Sites::class, Sites::EVENT_AFTER_SAVE_SITE, [$this->calendars, 'addSiteHandler']); + if (\Craft::$app->request->getIsCpRequest() && \Craft::$app->getUser()->getId()) { \Craft::$app->view->registerTranslations(self::TRANSLATION_CATEGORY, self::$javascriptTranslationKeys); } diff --git a/src/Controllers/EventsController.php b/src/Controllers/EventsController.php index 0bfdb80..f8e29ca 100644 --- a/src/Controllers/EventsController.php +++ b/src/Controllers/EventsController.php @@ -7,6 +7,7 @@ use craft\events\ElementEvent; use craft\helpers\Json; use craft\helpers\UrlHelper; +use craft\i18n\Locale; use Solspace\Calendar\Calendar; use Solspace\Calendar\Elements\Event; use Solspace\Calendar\Library\CalendarPermissionHelper; @@ -186,12 +187,24 @@ public function actionSaveEvent() CalendarPermissionHelper::requireCalendarEditPermissions($event->getCalendar()); } + $dateFormat = \Craft::$app->locale->getDateFormat('short', Locale::FORMAT_PHP); + $timeFormat = \Craft::$app->locale->getTimeFormat('short', Locale::FORMAT_PHP); + $format = "$dateFormat $timeFormat"; + if (isset($values['startDate'])) { - $event->startDate = new Carbon($values['startDate']['date'] . ' ' . $values['startDate']['time'], DateHelper::UTC); + $event->startDate = Carbon::createFromFormat( + $format, + $values['startDate']['date'] . ' ' . $values['startDate']['time'], + DateHelper::UTC + ); } if (isset($values['endDate'])) { - $event->endDate = new Carbon($values['endDate']['date'] . ' ' . $values['endDate']['time'], DateHelper::UTC); + $event->endDate = Carbon::createFromFormat( + $format, + $values['endDate']['date'] . ' ' . $values['endDate']['time'], + DateHelper::UTC + ); } if (isset($values['allDay'])) { @@ -469,7 +482,7 @@ private function populateEventModel(Event $event) $event->title = $request->getBodyParam('title', $event->title); $event->fieldLayoutId = null; - $fieldsLocation = $request->getParam('fieldsLocation', 'fields'); + $fieldsLocation = $request->getParam('fieldsLocation', 'fields'); $event->setFieldValuesFromRequest($fieldsLocation); $authorId = \Craft::$app->getRequest()->getBodyParam('author', ($event->authorId ?: \Craft::$app->getUser()->getIdentity()->id)); @@ -524,7 +537,7 @@ private function populateEventModel(Event $event) if (isset($values['exceptions'])) { foreach ($values['exceptions'] as $date) { - $exception = new ExceptionModel(); + $exception = new ExceptionModel(); $exception->eventId = $event->id; $exception->date = new Carbon($date, DateHelper::UTC); @@ -534,7 +547,7 @@ private function populateEventModel(Event $event) if (isset($values['selectDates'])) { foreach ($values['selectDates'] as $date) { - $selectDate = new SelectDateModel(); + $selectDate = new SelectDateModel(); $selectDate->eventId = $event->id; $selectDate->date = new Carbon($date, DateHelper::UTC); @@ -649,7 +662,11 @@ private function handleRepeatRules(Event $event, array $postedValues) if ($untilType === Event::UNTIL_TYPE_UNTIL) { $until = null; if (isset($postedValues['untilDate']['date']) && $postedValues['untilDate']['date']) { - $until = new Carbon($postedValues['untilDate']['date'], DateHelper::UTC); + $until = Carbon::createFromFormat( + \Craft::$app->locale->getDateFormat('short', Locale::FORMAT_PHP), + $postedValues['untilDate']['date'], + DateHelper::UTC + ); $until->setTime(23, 59, 59); $event->until = $until; } else { diff --git a/src/Elements/Db/EventQuery.php b/src/Elements/Db/EventQuery.php index 1890cf8..e39ebc0 100644 --- a/src/Elements/Db/EventQuery.php +++ b/src/Elements/Db/EventQuery.php @@ -1043,11 +1043,15 @@ private function parseCarbon($value = null) return $value; } - if ($value instanceof \DateTime) { - return Carbon::createFromTimestampUTC($value->getTimestamp()); + if (!$value instanceof \DateTime) { + $value = new \DateTime($value); } - return new Carbon($value, DateHelper::UTC); + return Carbon::createFromFormat( + 'Y-m-d H:i:s', + $value->format('Y-m-d H:i:s'), + DateHelper::UTC + ); } /** diff --git a/src/Elements/Event.php b/src/Elements/Event.php index 15d65e3..76cd070 100644 --- a/src/Elements/Event.php +++ b/src/Elements/Event.php @@ -730,6 +730,16 @@ public function getUntil() return $this->until; } + /** + * An alias for getUntil() + * + * @return Carbon|null + */ + public function getUntilDate() + { + return $this->getUntil(); + } + /** * Returns the repeats ON rule, which could be -1, 1, 2, 3 or 4 * Or 0 if no rule is set @@ -930,7 +940,7 @@ public function getDuration(): EventDuration */ public function isAllDay(): bool { - return $this->allDay; + return (bool) $this->allDay; } /** diff --git a/src/Library/CodePack/Components/RoutesComponent.php b/src/Library/CodePack/Components/RoutesComponent.php index 08053c3..1722a07 100644 --- a/src/Library/CodePack/Components/RoutesComponent.php +++ b/src/Library/CodePack/Components/RoutesComponent.php @@ -2,6 +2,9 @@ namespace Solspace\Calendar\Library\CodePack\Components; +use craft\db\Query; +use craft\helpers\Json; + class RoutesComponent extends AbstractJsonComponent { /** @@ -20,7 +23,7 @@ public function install(string $prefix = null) if (isset($route->urlParts, $route->template) && \is_array($route->urlParts)) { $urlParts = $route->urlParts; - array_walk_recursive($urlParts, function(&$value) { + array_walk_recursive($urlParts, function (&$value) { $value = stripslashes($value); }); @@ -29,7 +32,50 @@ public function install(string $prefix = null) $pattern = "/(\/?)(.*)/"; $template = preg_replace($pattern, "$1$demoFolder$2", $route->template, 1); - $routeService->saveRoute($urlParts, $template); + // Compile the URI parts into a regex pattern + $uriPattern = ''; + $uriParts = array_filter($urlParts); + $subpatternNameCounts = []; + + foreach ($uriParts as $part) { + if (\is_string($part)) { + $uriPattern .= preg_quote($part, '/'); + } else if (\is_array($part)) { + // Is the name a valid handle? + if (preg_match('/^[a-zA-Z]\w*$/', $part[0])) { + $subpatternName = $part[0]; + } else { + $subpatternName = 'any'; + } + + // Make sure it's unique + if (isset($subpatternNameCounts[$subpatternName])) { + $subpatternNameCounts[$subpatternName]++; + + // Append the count to the end of the name + $subpatternName .= $subpatternNameCounts[$subpatternName]; + } else { + $subpatternNameCounts[$subpatternName] = 1; + } + + // Add the var as a named subpattern + $uriPattern .= '<' . preg_quote($subpatternName, '/') . ':' . $part[1] . '>'; + } + } + + $id = (new Query()) + ->select('id') + ->from('{{%routes}}') + ->where( + [ + 'uriParts' => Json::encode($uriParts), + 'uriPattern' => $uriPattern, + 'template' => $template, + ] + ) + ->scalar(); + + $routeService->saveRoute($urlParts, $template, null, $id ?: null); } } } diff --git a/src/Services/CalendarsService.php b/src/Services/CalendarsService.php index ce8d0e6..a686794 100644 --- a/src/Services/CalendarsService.php +++ b/src/Services/CalendarsService.php @@ -4,6 +4,7 @@ use craft\base\Component; use craft\db\Query; +use craft\events\SiteEvent; use craft\queue\jobs\ResaveElements; use Solspace\Calendar\Calendar; use Solspace\Calendar\Elements\Event; @@ -554,6 +555,47 @@ public function isEventTemplateValid(CalendarModel $calendar, int $siteId): bool return $templateExists; } + /** + * @param SiteEvent $event + * + * @return bool + */ + public function addSiteHandler(SiteEvent $event): bool + { + $siteId = $event->site->id; + + $rows = []; + $calendars = $this->getAllCalendars(); + foreach ($calendars as $calendar) { + $rows[] = [ + $calendar->id, + $siteId, + 0, + 0, + null, + null, + ]; + } + + (new Query()) + ->createCommand() + ->batchInsert( + CalendarSiteSettingsRecord::TABLE, + [ + 'calendarId', + 'siteId', + 'enabledByDefault', + 'hasUrls', + 'uriFormat', + 'template', + ], + $rows + ) + ->execute(); + + return true; + } + /** * @return Query */ diff --git a/src/Services/EventsService.php b/src/Services/EventsService.php index 15ba8eb..9b42593 100644 --- a/src/Services/EventsService.php +++ b/src/Services/EventsService.php @@ -5,15 +5,13 @@ use craft\base\Component; use craft\base\ElementInterface; use craft\db\Query; -use craft\helpers\ElementHelper; +use craft\events\SiteEvent; use Solspace\Calendar\Calendar; use Solspace\Calendar\Elements\Db\EventQuery; use Solspace\Calendar\Elements\Event; use Solspace\Calendar\Events\DeleteElementEvent; use Solspace\Calendar\Events\SaveElementEvent; -use Solspace\Calendar\Library\DataObjects\EventListOptions; use Solspace\Calendar\Library\DateHelper; -use Solspace\Calendar\Library\Events\Event as EventObject; use Solspace\Calendar\Library\Events\EventList; use Solspace\Calendar\Models\EventCriteria; use Solspace\Commons\Helpers\PermissionHelper; @@ -287,28 +285,28 @@ public function bumpRecurrenceRule(Event $event, int $amountOfDays, int $amountO } /** - * @param Event $event + * @param SiteEvent $event * * @return bool * @throws \yii\db\Exception */ - public function addLocaleHandler(Event $event): bool + public function addSiteHandler(SiteEvent $event): bool { - $localeId = $event->params['localeId']; - $primaryLocale = \Craft::$app->i18n->getPrimarySiteLocaleId(); + $siteId = $event->site->id; + $primarySiteId = \Craft::$app->sites->getPrimarySite()->id; $elementRows = (new Query()) ->select(['ei18n.*']) - ->from('elements_i18n ei18n') - ->join(Event::TABLE . ' e', 'ei18n.elementId = e.id') - ->where(['ei18n.locale' => $primaryLocale]) + ->from('{{%elements_sites}} ei18n') + ->innerJoin(Event::TABLE . ' e', 'ei18n.elementId = e.id') + ->where(['ei18n.siteId' => $primarySiteId]) ->all(); $contentRows = (new Query()) ->select(['c.*']) - ->from('content c') - ->join(Event::TABLE . ' e', 'c.elementId = e.id') - ->where(['c.locale' => $primaryLocale]) + ->from('{{%content}} c') + ->innerJoin(Event::TABLE . ' e', 'c.elementId = e.id') + ->where(['c.siteId' => $primarySiteId]) ->all(); $elementDataById = []; @@ -319,7 +317,7 @@ public function addLocaleHandler(Event $event): bool $contentDataById = []; foreach ($contentRows as $content) { unset( - $content['locale'], + $content['siteId'], $content['id'], $content['dateCreated'], $content['dateUpdated'], @@ -334,9 +332,9 @@ public function addLocaleHandler(Event $event): bool \Craft::$app->db ->createCommand() ->batchInsert( - 'elements_i18n', - ['elementId', 'locale', 'slug', 'enabled'], - [[$elementId, $localeId, $elementData['slug'], true]] + '{{%elements_sites}}', + ['elementId', 'siteId', 'slug', 'enabled'], + [[$elementId, $siteId, $elementData['slug'], true]] ) ->execute(); @@ -346,12 +344,12 @@ public function addLocaleHandler(Event $event): bool $columns = array_keys($content); $values = array_values($content); - $columns[] = 'locale'; - $values[] = $localeId; + $columns[] = 'siteId'; + $values[] = $siteId; \Craft::$app->db ->createCommand() - ->batchInsert('content', $columns, [$values]) + ->batchInsert('{{%content}}', $columns, [$values]) ->execute(); } } diff --git a/src/Services/ExceptionsService.php b/src/Services/ExceptionsService.php index 62f3b8d..7a952a5 100644 --- a/src/Services/ExceptionsService.php +++ b/src/Services/ExceptionsService.php @@ -6,6 +6,7 @@ use craft\base\Component; use craft\db\Query; use Solspace\Calendar\Elements\Event; +use Solspace\Calendar\Library\DateHelper; use Solspace\Calendar\Models\ExceptionModel; use Solspace\Calendar\Records\ExceptionRecord; @@ -92,9 +93,16 @@ public function saveExceptions(Event $event, array $exceptions) ->execute(); foreach ($exceptions as $exceptionDate) { + $exceptionDate = preg_replace('/^(\d{4}\-\d{2}\-\d{2}).*/', '$1', $exceptionDate); + $exceptionRecord = new ExceptionRecord(); $exceptionRecord->eventId = $event->id; - $exceptionRecord->date = new \DateTime($exceptionDate); + $exceptionRecord->date = Carbon::createFromFormat( + 'Y-m-d', + $exceptionDate, + DateHelper::UTC + ) + ->setTime(0, 0, 0); $exceptionRecord->save(); } diff --git a/src/codepack/templates/edit.html b/src/codepack/templates/edit.html index 283e1eb..226915a 100644 --- a/src/codepack/templates/edit.html +++ b/src/codepack/templates/edit.html @@ -3,10 +3,12 @@ {% set pageTitle = "Event" %} {% set page = "edit" %} -{% block content %} +{% set dateFormat = craft.app.locale.getDateFormat('short', 'php') %} +{% set dateFormatJs = craft.app.locale.getDateFormat('short', 'jui') %} +{% set timeFormat = craft.app.locale.getTimeFormat('short', 'php') %} +{% set timeFormatJs = craft.app.locale.getTimeFormat('short', 'jui') %} - {% set dateFormat = craft.app.locale.getDateFormat('short', 'php') %} - {% set timeFormat = craft.app.locale.getTimeFormat('short', 'php') %} +{% block content %} {# Acquire URL segments #} {% set baseUrlSegments = 1 %} @@ -45,7 +47,7 @@