diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 0000000..46c68e4
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,163 @@
+name: CI
+on: [push]
+jobs:
+ check-composer:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Validate composer.json
+ run: composer validate
+
+ php-linting:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ php-version:
+ - 7.3
+ - 7.4
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Install PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: "${{ matrix.php-version }}"
+
+ - name: PHP lint
+ run: "find *.php Classes Tests -name '*.php' -print0 | xargs -0 -n 1 -P 4 php -l"
+
+ check-dependencies:
+ runs-on: ubuntu-latest
+ needs: [check-composer]
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Keep composer at 1.x
+ run: sudo composer selfupdate --1
+
+ - name: Get Composer Cache Directory
+ id: composer-cache
+ run: |
+ echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - uses: actions/cache@v1
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-composer-
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress --no-suggest --no-plugins
+
+ - name: Missing composer requirements
+ run: ./vendor/bin/composer-require-checker check
+
+ xml-linting:
+ runs-on: ubuntu-latest
+ needs: [check-composer]
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Install xmllint
+ run: sudo apt-get install libxml2-utils
+
+ - name: Keep composer at 1.x
+ run: sudo composer selfupdate --1
+
+ - name: Get Composer Cache Directory
+ id: composer-cache
+ run: |
+ echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - uses: actions/cache@v1
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-composer-
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress --no-suggest
+
+ - name: PHPUnit configuration file
+ run: xmllint --schema vendor/phpunit/phpunit/phpunit.xsd --noout phpunit.xml.dist
+
+ - name: PHPCodeSniffer configuration file
+ run: xmllint --schema vendor/squizlabs/php_codesniffer/phpcs.xsd --noout phpcs.xml.dist
+
+ - name: Fetch schema for xliff
+ run: wget https://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd --output-document=.Build/xliff-core-1.2-strict.xsd
+
+ - name: TYPO3 language files
+ run: xmllint --schema .Build/xliff-core-1.2-strict.xsd --noout $(find Resources -name '*.xlf')
+
+ coding-guideline:
+ runs-on: ubuntu-latest
+ needs:
+ - check-dependencies
+ - xml-linting
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Keep composer at 1.x
+ run: sudo composer selfupdate --1
+
+ - name: Get Composer Cache Directory
+ id: composer-cache
+ run: |
+ echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - uses: actions/cache@v1
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-composer-
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress --no-suggest
+
+ - name: Coding Guideline
+ run: ./vendor/bin/phpcs
+
+ tests:
+ runs-on: ubuntu-latest
+ needs:
+ - check-dependencies
+ - xml-linting
+ strategy:
+ matrix:
+ php-version:
+ - 7.3
+ - 7.4
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Install PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: "${{ matrix.php-version }}"
+
+ - name: Keep composer at 1.x
+ run: sudo composer selfupdate --1
+
+ - name: Get Composer Cache Directory
+ id: composer-cache
+ run: |
+ echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+ - uses: actions/cache@v1
+ with:
+ path: ${{ steps.composer-cache.outputs.dir }}
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-composer-
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress --no-suggest
+
+ - name: PHPUnit Tests
+ run: ./vendor/bin/phpunit --testdox
diff --git a/.gitignore b/.gitignore
index 752676a..8b32ebe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/.Build/
/vendor/
+/composer.lock
diff --git a/Classes/Controller/Frontend/CalendarController.php b/Classes/Controller/Frontend/CalendarController.php
new file mode 100644
index 0000000..9a4f34e
--- /dev/null
+++ b/Classes/Controller/Frontend/CalendarController.php
@@ -0,0 +1,115 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use TYPO3\CMS\Extbase\Annotation as Extbase;
+use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
+use TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter;
+use WerkraumMedia\Calendar\Domain\Model\Day;
+use WerkraumMedia\Calendar\Domain\Model\Month;
+use WerkraumMedia\Calendar\Domain\Model\Week;
+
+class CalendarController extends ActionController
+{
+ public function initializeMonthAction()
+ {
+ if ($this->request->hasArgument('month') === false) {
+ $this->request->setArguments([
+ 'month' => [
+ 'month' => date('m'),
+ 'year' => date('Y'),
+ ],
+ ]);
+ }
+
+ $this->arguments->getArgument('month')
+ ->getPropertyMappingConfiguration()
+ ->allowAllProperties();
+ }
+
+ /**
+ * @Extbase\IgnoreValidation("month")
+ */
+ public function monthAction(Month $month)
+ {
+ $this->view->assignMultiple([
+ 'month' => $month,
+ ]);
+ }
+
+ public function initializeWeekAction()
+ {
+ if ($this->request->hasArgument('week') === false) {
+ $this->request->setArguments([
+ 'week' => [
+ 'week' => date('W'),
+ 'year' => date('Y'),
+ ],
+ ]);
+ }
+
+ $this->arguments->getArgument('week')
+ ->getPropertyMappingConfiguration()
+ ->allowAllProperties();
+ }
+
+ /**
+ * @Extbase\IgnoreValidation("week")
+ */
+ public function weekAction(Week $week)
+ {
+ $this->view->assignMultiple([
+ 'week' => $week,
+ ]);
+ }
+
+ public function initializeDayAction()
+ {
+ if ($this->request->hasArgument('day') === false) {
+ $this->request->setArguments([
+ 'day' => new \DateTimeImmutable(),
+ ]);
+ }
+
+ $propertyMappingConfiguration = $this->arguments->getArgument('day')
+ ->getPropertyMappingConfiguration();
+
+ $propertyMappingConfiguration->allowAllProperties();
+ $propertyMappingConfiguration
+ ->forProperty('day')
+ ->setTypeConverterOption(
+ DateTimeConverter::class,
+ DateTimeConverter::CONFIGURATION_DATE_FORMAT,
+ 'Y-m-d'
+ );
+ }
+
+ /**
+ * @Extbase\IgnoreValidation("day")
+ */
+ public function dayAction(Day $day)
+ {
+ $this->view->assignMultiple([
+ 'day' => $day,
+ ]);
+ }
+}
diff --git a/Classes/Domain/Model/Day.php b/Classes/Domain/Model/Day.php
new file mode 100644
index 0000000..598c9f5
--- /dev/null
+++ b/Classes/Domain/Model/Day.php
@@ -0,0 +1,89 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
+
+class Day
+{
+ /**
+ * @var \DateTimeImmutable
+ */
+ private $day;
+
+ /**
+ * @var mixed
+ */
+ private $foreignData;
+
+ /**
+ * @var bool
+ */
+ private $initialized = false;
+
+ public function __construct(
+ \DateTime $day
+ ) {
+ $this->periods = new ObjectStorage();
+ $this->day = \DateTimeImmutable::createFromMutable($day)->modify('midnight');
+ }
+
+ public function isActive(): bool
+ {
+ $foreignData = $this->getForeignData();
+ if ($foreignData instanceof IsDayActive) {
+ return $foreignData->isActive($this->getDateTimeInstance());
+ }
+
+ return false;
+ }
+
+ public function getForeignData()
+ {
+ $this->initializeForeignData();
+
+ return $this->foreignData;
+ }
+
+ public function getDateTimeInstance(): \DateTimeImmutable
+ {
+ return $this->day;
+ }
+
+ public function getAsUrlArgument(): array
+ {
+ return [
+ 'day' => $this->day->format('Y-m-d'),
+ ];
+ }
+
+ private function initializeForeignData(): void
+ {
+ if ($this->initialized) {
+ return;
+ }
+
+ $this->foreignData = GeneralUtility::makeInstance(ForeignDataFactory::class)
+ ->getData($this);
+ }
+}
diff --git a/Classes/Domain/Model/ForeignDataFactory.php b/Classes/Domain/Model/ForeignDataFactory.php
new file mode 100644
index 0000000..6eceab2
--- /dev/null
+++ b/Classes/Domain/Model/ForeignDataFactory.php
@@ -0,0 +1,30 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+interface ForeignDataFactory
+{
+ /**
+ *
+ */
+ public function getData(Day $day);
+}
diff --git a/Classes/Domain/Model/IsDayActive.php b/Classes/Domain/Model/IsDayActive.php
new file mode 100644
index 0000000..8ef63ce
--- /dev/null
+++ b/Classes/Domain/Model/IsDayActive.php
@@ -0,0 +1,27 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+interface IsDayActive
+{
+ public function isActive(\DateTimeImmutable $dateTime): bool;
+}
diff --git a/Classes/Domain/Model/Month.php b/Classes/Domain/Model/Month.php
new file mode 100644
index 0000000..1180081
--- /dev/null
+++ b/Classes/Domain/Model/Month.php
@@ -0,0 +1,129 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
+
+class Month
+{
+ /**
+ * @var int
+ */
+ private $month;
+
+ /**
+ * @var int
+ */
+ private $year;
+
+ /**
+ * @var Week[]
+ */
+ private $weeks = [];
+
+ public function __construct(
+ int $month,
+ int $year
+ ) {
+ $this->month = $month;
+ $this->year = $year;
+ }
+
+ public function isActive(): bool
+ {
+ foreach ($this->getWeeks() as $week) {
+ if ($week->isActive()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function getWeeks(): array
+ {
+ if ($this->weeks !== []) {
+ return $this->weeks;
+ }
+
+ $month = $this->getDateTimeInstance();
+ $lastDay = $month->modify('last day of this month')->modify('sunday this week');
+ $currentDay = $month->modify('monday this week');
+
+ while ($currentDay <= $lastDay) {
+ $this->weeks[] = new Week(
+ (int) $currentDay->format('W'),
+ (int) $currentDay->format('Y')
+ );
+
+ $currentDay = $currentDay->modify('+7 days');
+ }
+
+ return $this->weeks;
+ }
+
+ public function getPreviousMonth(): Month
+ {
+ $previousMonth = $this->month - 1;
+ $previousYear = $this->year;
+
+ if ($previousMonth <= 0) {
+ $previousMonth = 12;
+ --$previousYear;
+ }
+
+ return new self(
+ $previousMonth,
+ $previousYear
+ );
+ }
+
+ public function getNextMonth(): Month
+ {
+ $nextMonth = $this->month + 1;
+ $nextYear = $this->year;
+
+ if ($nextMonth > 12) {
+ $nextMonth = 1;
+ ++$nextYear;
+ }
+
+ return new self(
+ $nextMonth,
+ $nextYear
+ );
+ }
+
+ public function getAsUrlArgument(): array
+ {
+ return [
+ 'month' => $this->month,
+ 'year' => $this->year,
+ ];
+ }
+
+ public function getDateTimeInstance(): \DateTimeImmutable
+ {
+ return new \DateTimeImmutable($this->year . '-' . $this->month . '-01');
+ }
+}
diff --git a/Classes/Domain/Model/NullDataFactory.php b/Classes/Domain/Model/NullDataFactory.php
new file mode 100644
index 0000000..0b55153
--- /dev/null
+++ b/Classes/Domain/Model/NullDataFactory.php
@@ -0,0 +1,30 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+class NullDataFactory implements ForeignDataFactory
+{
+ public function getData(Day $day)
+ {
+ return null;
+ }
+}
diff --git a/Classes/Domain/Model/Week.php b/Classes/Domain/Model/Week.php
new file mode 100644
index 0000000..f02ab53
--- /dev/null
+++ b/Classes/Domain/Model/Week.php
@@ -0,0 +1,118 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+class Week
+{
+ /**
+ * @var int
+ */
+ private $week;
+
+ /**
+ * @var int
+ */
+ private $year;
+
+ /**
+ * @var Day[]
+ */
+ private $days = [];
+
+ public function __construct(
+ int $week,
+ int $year
+ ) {
+ $this->week = $week;
+ $this->year = $year;
+ }
+
+ public function isActive(): bool
+ {
+ foreach ($this->getDays() as $day) {
+ if ($day->isActive()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function getDays(): array
+ {
+ if ($this->days !== []) {
+ return $this->days;
+ }
+
+ $currentDay = $this->getWeek()->modify('monday this week');
+ $endOfWeek = $currentDay->modify('sunday this week');
+
+ while ($currentDay <= $endOfWeek) {
+ $this->days[] = new Day(\DateTime::createFromImmutable($currentDay));
+ $currentDay = $currentDay->modify('+1 day');
+ }
+
+ return $this->days;
+ }
+
+ public function getPreviousWeek(): Week
+ {
+ $newDay = $this->getWeek()->modify('-1 week');
+
+ return new self(
+ (int) $newDay->format('W'),
+ (int) $newDay->format('Y')
+ );
+ }
+
+ public function getNextWeek(): Week
+ {
+ $newDay = $this->getWeek()->modify('+1 week');
+
+ return new self(
+ (int) $newDay->format('W'),
+ (int) $newDay->format('Y')
+ );
+ }
+
+ public function getDateTimeInstance(): \DateTimeImmutable
+ {
+ return $this->getWeek();
+ }
+
+ public function getAsUrlArgument(): array
+ {
+ return [
+ 'week' => $this->week,
+ 'year' => $this->year,
+ ];
+ }
+
+ private function getWeek(): \DateTimeImmutable
+ {
+ $week = new \DateTimeImmutable();
+ $week = $week->setISODate($this->year, $this->week);
+ $week = $week->modify('thursday');
+
+ return $week;
+ }
+}
diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml
new file mode 100644
index 0000000..ea2f924
--- /dev/null
+++ b/Configuration/Services.yaml
@@ -0,0 +1,8 @@
+services:
+ _defaults:
+ autowire: true
+ autoconfigure: true
+ public: false
+
+ WerkraumMedia\Calendar\:
+ resource: '../Classes/*'
diff --git a/Resources/Private/Templates/Frontend/Calendar/Day.html b/Resources/Private/Templates/Frontend/Calendar/Day.html
new file mode 100644
index 0000000..687c6e5
--- /dev/null
+++ b/Resources/Private/Templates/Frontend/Calendar/Day.html
@@ -0,0 +1,8 @@
+
+
+
{day.dateTimeInstance -> f:format.date(format: 'd.m.Y')}
+
+ TODO: Show periods
+ Maybe remove and instead link to filtered list
+
diff --git a/Resources/Private/Templates/Frontend/Calendar/Month.html b/Resources/Private/Templates/Frontend/Calendar/Month.html
new file mode 100644
index 0000000..9016deb
--- /dev/null
+++ b/Resources/Private/Templates/Frontend/Calendar/Month.html
@@ -0,0 +1,42 @@
+
+
+ {month.dateTimeInstance -> f:format.date(format: '%B %Y')}
+
+
+ Prev Month
+
+
+ Next Month
+
+
+
+
+ KW |
+ Mo |
+ Di |
+ Mi |
+ Do |
+ Fr |
+ Sa |
+ So |
+
+
+
+
+
+ {week.dateTimeInstance -> f:format.date(format: '%V')}
+
+ |
+
+
+
+ {day.dateTimeInstance -> f:format.date(format: 'd')}
+
+ |
+
+
+
+
+
+
diff --git a/Resources/Private/Templates/Frontend/Calendar/Week.html b/Resources/Private/Templates/Frontend/Calendar/Week.html
new file mode 100644
index 0000000..e3357e0
--- /dev/null
+++ b/Resources/Private/Templates/Frontend/Calendar/Week.html
@@ -0,0 +1,33 @@
+
+
+ {week.dateTimeInstance -> f:format.date(format: '%V %Y')}
+
+
+ Prev Week
+
+
+ Next Week
+
+
+
+
+ Mo |
+ Di |
+ Mi |
+ Do |
+ Fr |
+ Sa |
+ So |
+
+
+
+
+
+ {day.dateTimeInstance -> f:format.date(format: 'd')}
+
+ |
+
+
+
+
diff --git a/Tests/ForcePropertyTrait.php b/Tests/ForcePropertyTrait.php
new file mode 100644
index 0000000..1af013f
--- /dev/null
+++ b/Tests/ForcePropertyTrait.php
@@ -0,0 +1,33 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+trait ForcePropertyTrait
+{
+ protected function forceProperty($subject, string $name, $value)
+ {
+ $objectReflection = new \ReflectionObject($subject);
+ $property = $objectReflection->getProperty($name);
+ $property->setAccessible(true);
+ $property->setValue($subject, $value);
+ }
+}
diff --git a/Tests/Unit/Controller/Frontend/CalendarControllerTest.php b/Tests/Unit/Controller/Frontend/CalendarControllerTest.php
new file mode 100644
index 0000000..9080b8f
--- /dev/null
+++ b/Tests/Unit/Controller/Frontend/CalendarControllerTest.php
@@ -0,0 +1,275 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use PHPUnit\Framework\TestCase;
+use Prophecy\Argument as ProphetArgument;
+use Prophecy\PhpUnit\ProphecyTrait;
+use Prophecy\Prophecy\ObjectProphecy;
+use TYPO3\CMS\Extbase\Mvc\Controller\Argument;
+use TYPO3\CMS\Extbase\Mvc\Controller\Arguments;
+use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
+use TYPO3\CMS\Extbase\Mvc\Web\Request;
+use TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration;
+use WerkraumMedia\Calendar\Controller\Frontend\CalendarController;
+use WerkraumMedia\Calendar\Domain\Model\Day;
+use WerkraumMedia\Calendar\Domain\Model\Month;
+use WerkraumMedia\Calendar\Domain\Model\Week;
+use WerkraumMedia\Calendar\Tests\ForcePropertyTrait;
+
+/**
+ * @covers WerkraumMedia\Calendar\Controller\Frontend\CalendarController
+ * @testdox The calendar controller
+ */
+class CalendarControllerTest extends TestCase
+{
+ use ProphecyTrait;
+ use ForcePropertyTrait;
+
+ /**
+ * @test
+ */
+ public function setsCurrentMonthAsDefaultArgument(): void
+ {
+ $subject = new CalendarController();
+
+ $arguments = $this->allowsMappingOfAllPropertiesForArgument('month')['arguments'];
+
+ $request = $this->prophesize(Request::class);
+ $request->hasArgument('month')->willReturn(false);
+ $request->setArguments([
+ 'month' => [
+ 'month' => date('m'),
+ 'year' => date('Y'),
+ ],
+ ])->shouldBeCalled();
+
+ $this->forceProperty($subject, 'request', $request->reveal());
+ $this->forceProperty($subject, 'arguments', $arguments->reveal());
+
+ $subject->initializeMonthAction();
+ $request->checkProphecyMethodsPredictions();
+ }
+
+ /**
+ * @test
+ */
+ public function allowsMonthToBeMapped(): void
+ {
+ $subject = new CalendarController();
+
+ $arguments = $this->allowsMappingOfAllPropertiesForArgument('month')['arguments'];
+
+ $request = $this->prophesize(Request::class);
+ $request->hasArgument('month')->willReturn(true);
+
+ $this->forceProperty($subject, 'request', $request->reveal());
+ $this->forceProperty($subject, 'arguments', $arguments->reveal());
+
+ $subject->initializeMonthAction();
+ $arguments->checkProphecyMethodsPredictions();
+ }
+
+ /**
+ * @test
+ */
+ public function addsMonthToView(): void
+ {
+ $subject = new CalendarController();
+
+ $month = $this->prophesize(Month::class);
+ $view = $this->prophesize(ViewInterface::class);
+ $view->assignMultiple([
+ 'month' => $month->reveal(),
+ ])->shouldBeCalled();
+
+ $this->forceProperty($subject, 'view', $view->reveal());
+
+ $subject->monthAction($month->reveal());
+ $view->checkProphecyMethodsPredictions();
+ }
+
+ /**
+ * @test
+ */
+ public function setsCurrentWeekAsDefaultArgument(): void
+ {
+ $subject = new CalendarController();
+
+ $arguments = $this->allowsMappingOfAllPropertiesForArgument('week')['arguments'];
+
+ $request = $this->prophesize(Request::class);
+ $request->hasArgument('week')->willReturn(false);
+ $request->setArguments([
+ 'week' => [
+ 'week' => date('W'),
+ 'year' => date('Y'),
+ ],
+ ])->shouldBeCalled();
+
+ $this->forceProperty($subject, 'request', $request->reveal());
+ $this->forceProperty($subject, 'arguments', $arguments->reveal());
+
+ $subject->initializeWeekAction();
+ $request->checkProphecyMethodsPredictions();
+ }
+
+ /**
+ * @test
+ */
+ public function allowsWeekToBeMapped(): void
+ {
+ $subject = new CalendarController();
+
+ $arguments = $this->allowsMappingOfAllPropertiesForArgument('week')['arguments'];
+
+ $request = $this->prophesize(Request::class);
+ $request->hasArgument('week')->willReturn(true);
+
+ $this->forceProperty($subject, 'request', $request->reveal());
+ $this->forceProperty($subject, 'arguments', $arguments->reveal());
+
+ $subject->initializeWeekAction();
+ $arguments->checkProphecyMethodsPredictions();
+ }
+
+ /**
+ * @test
+ */
+ public function addsWeekToView(): void
+ {
+ $subject = new CalendarController();
+
+ $week = $this->prophesize(Week::class);
+ $view = $this->prophesize(ViewInterface::class);
+ $view->assignMultiple([
+ 'week' => $week->reveal(),
+ ])->shouldBeCalled();
+
+ $this->forceProperty($subject, 'view', $view->reveal());
+
+ $subject->weekAction($week->reveal());
+ $view->checkProphecyMethodsPredictions();
+ }
+
+ /**
+ * @test
+ */
+ public function setsCurrentDayAsDefaultArgument(): void
+ {
+ $subject = new CalendarController();
+
+ $prophecies = $this->allowsMappingOfAllPropertiesForArgument('day');
+ $propertyConfiguration = $prophecies['propertyMappingConfiguration'];
+ $arguments = $prophecies['arguments'];
+
+ $request = $this->prophesize(Request::class);
+ $request->hasArgument('day')->willReturn(false);
+ $request->setArguments(ProphetArgument::that(function (array $arguments) {
+ return count($arguments) === 1
+ && isset($arguments['day'])
+ && $arguments['day'] instanceof \DateTimeImmutable
+ && $arguments['day']->format('Y-m-d') === date('Y-m-d')
+ ;
+ }))->shouldBeCalled();
+
+ $configuration = $this->prophesize(PropertyMappingConfiguration::class);
+ $configuration->setTypeConverterOption(
+ '',
+ '',
+ 'Y-m-d'
+ );
+ $propertyConfiguration->forProperty('day')->willReturn($configuration);
+
+ $this->forceProperty($subject, 'request', $request->reveal());
+ $this->forceProperty($subject, 'arguments', $arguments->reveal());
+
+ $subject->initializeDayAction();
+ $request->checkProphecyMethodsPredictions();
+ }
+
+ /**
+ * @test
+ */
+ public function configuredMappingForDay(): void
+ {
+ $subject = new CalendarController();
+
+ $prophecies = $this->allowsMappingOfAllPropertiesForArgument('day');
+ $propertyConfiguration = $prophecies['propertyMappingConfiguration'];
+ $arguments = $prophecies['arguments'];
+
+ $request = $this->prophesize(Request::class);
+ $request->hasArgument('day')->willReturn(true);
+
+ $configuration = $this->prophesize(PropertyMappingConfiguration::class);
+ $configuration->setTypeConverterOption(
+ '',
+ '',
+ 'Y-m-d'
+ );
+ $propertyConfiguration->forProperty('day')->willReturn($configuration);
+
+ $this->forceProperty($subject, 'request', $request->reveal());
+ $this->forceProperty($subject, 'arguments', $arguments->reveal());
+
+ $subject->initializeDayAction();
+ $arguments->checkProphecyMethodsPredictions();
+ }
+
+ /**
+ * @test
+ */
+ public function addsDayToView(): void
+ {
+ $subject = new CalendarController();
+
+ $day = $this->prophesize(Day::class);
+ $view = $this->prophesize(ViewInterface::class);
+ $view->assignMultiple([
+ 'day' => $day->reveal(),
+ ])->shouldBeCalled();
+
+ $this->forceProperty($subject, 'view', $view->reveal());
+
+ $subject->dayAction($day->reveal());
+ $view->checkProphecyMethodsPredictions();
+ }
+
+ private function allowsMappingOfAllPropertiesForArgument(string $argumentName): array
+ {
+ $propertyMappingConfiguration = $this->prophesize(PropertyMappingConfiguration::class);
+ $propertyMappingConfiguration->allowAllProperties()->shouldBeCalled();
+
+ $argument = $this->prophesize(Argument::class);
+ $argument->getPropertyMappingConfiguration()->willReturn($propertyMappingConfiguration);
+
+ $arguments = $this->prophesize(Arguments::class);
+ $arguments->getArgument($argumentName)->willReturn($argument->reveal());
+
+ return [
+ 'propertyMappingConfiguration' => $propertyMappingConfiguration,
+ 'argument' => $argument,
+ 'arguments' => $arguments,
+ ];
+ }
+}
diff --git a/Tests/Unit/Domain/Model/DayTest.php b/Tests/Unit/Domain/Model/DayTest.php
new file mode 100644
index 0000000..961932b
--- /dev/null
+++ b/Tests/Unit/Domain/Model/DayTest.php
@@ -0,0 +1,144 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use PHPUnit\Framework\TestCase;
+use Prophecy\Argument;
+use Prophecy\PhpUnit\ProphecyTrait;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
+use WerkraumMedia\Calendar\Domain\Model\Day;
+use WerkraumMedia\Calendar\Domain\Model\ForeignDataFactory;
+use WerkraumMedia\Calendar\Domain\Model\IsDayActive;
+use WerkraumMedia\Calendar\Domain\Model\NullDataFactory;
+use WerkraumMedia\Calendar\Tests\ForcePropertyTrait;
+
+/**
+ * @covers WerkraumMedia\Calendar\Domain\Model\Day
+ * @testdox A day
+ */
+class DayTest extends TestCase
+{
+ use ProphecyTrait;
+ use ForcePropertyTrait;
+
+ public function tearDown(): void
+ {
+ GeneralUtility::purgeInstances();
+ parent::tearDown();
+ }
+
+ /**
+ * @test
+ */
+ public function canBeCreated(): void
+ {
+ $subject = new Day(
+ new \DateTime()
+ );
+
+ self::assertInstanceOf(Day::class, $subject);
+ }
+
+ /**
+ * @test
+ */
+ public function providesDateTimeInstance(): void
+ {
+ $dateTimeInstance = new \DateTime();
+ $subject = new Day(
+ $dateTimeInstance
+ );
+
+ self::assertInstanceOf(\DateTimeImmutable::class, $subject->getDateTimeInstance());
+ self::assertSame($dateTimeInstance->format('U'), $subject->getDateTimeInstance()->format('U'));
+ }
+
+ /**
+ * @test
+ */
+ public function providedDateTimeInstanceHasMidnight(): void
+ {
+ $dateTimeInstance = new \DateTime();
+ $subject = new Day(
+ $dateTimeInstance
+ );
+
+ self::assertSame('00:00:00', $subject->getDateTimeInstance()->format('H:i:s'));
+ }
+
+ /**
+ * @test
+ */
+ public function providesItselfAsUrlArgument(): void
+ {
+ $subject = new Day(new \DateTime('2020-10-19'));
+
+ self::assertSame(['day' => '2020-10-19'], $subject->getAsUrlArgument());
+ }
+
+ /**
+ * @test
+ */
+ public function isNotActiveIfNoForeignDataWithInterfaceExists(): void
+ {
+ $subject = new Day(new \DateTime('2020-10-19'));
+
+ $this->forceProperty($subject, 'initialized', true);
+
+ self::assertFalse($subject->isActive());
+ }
+
+ /**
+ * @test
+ */
+ public function isNotActiveIfForeignDataIsNotActive(): void
+ {
+ $subject = new Day(new \DateTime('2020-10-19'));
+
+ $foreignData = $this->prophesize(IsDayActive::class);
+ $foreignData->isActive(Argument::any())->willReturn(false);
+
+ $this->forceProperty($subject, 'initialized', true);
+ $this->forceProperty($subject, 'foreignData', $foreignData->reveal());
+
+ self::assertFalse($subject->isActive());
+ }
+
+ /**
+ * @test
+ */
+ public function initializesForeignDataViaFactory(): void
+ {
+ $subject = new Day(new \DateTime('2020-10-19'));
+
+ $foreignData = $this->prophesize(IsDayActive::class);
+ $foreignData->isActive(Argument::any())->willReturn(true);
+
+ $factory = $this->prophesize(ForeignDataFactory::class);
+ $factory->getData($subject)->willReturn($foreignData->reveal());
+
+ GeneralUtility::addInstance(ForeignDataFactory::class, $factory->reveal());
+
+ self::assertTrue($subject->isActive());
+ }
+}
diff --git a/Tests/Unit/Domain/Model/MonthTest.php b/Tests/Unit/Domain/Model/MonthTest.php
new file mode 100644
index 0000000..c385303
--- /dev/null
+++ b/Tests/Unit/Domain/Model/MonthTest.php
@@ -0,0 +1,183 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use PHPUnit\Framework\TestCase;
+use Prophecy\PhpUnit\ProphecyTrait;
+use WerkraumMedia\Calendar\Domain\Model\Month;
+use WerkraumMedia\Calendar\Domain\Model\Week;
+use WerkraumMedia\Calendar\Tests\ForcePropertyTrait;
+
+/**
+ * @covers WerkraumMedia\Calendar\Domain\Model\Month
+ * @testdox A month
+ */
+class MonthTest extends TestCase
+{
+ use ProphecyTrait;
+ use ForcePropertyTrait;
+
+ /**
+ * @test
+ */
+ public function canBeCreated(): void
+ {
+ $subject = new Month(1, 2020);
+
+ self::assertInstanceOf(Month::class, $subject);
+ }
+
+ /**
+ * @test
+ */
+ public function returnsPreviousMonthForSameYear(): void
+ {
+ $subject = new Month(2, 2020);
+
+ self::assertSame('01', $subject->getPreviousMonth()->getDateTimeInstance()->format('m'));
+ self::assertSame('2020', $subject->getPreviousMonth()->getDateTimeInstance()->format('Y'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsPreviousMonthForPreviousYear(): void
+ {
+ $subject = new Month(1, 2020);
+
+ self::assertSame('12', $subject->getPreviousMonth()->getDateTimeInstance()->format('m'));
+ self::assertSame('2019', $subject->getPreviousMonth()->getDateTimeInstance()->format('Y'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsNextMonthForSameYear(): void
+ {
+ $subject = new Month(1, 2020);
+
+ self::assertSame('02', $subject->getNextMonth()->getDateTimeInstance()->format('m'));
+ self::assertSame('2020', $subject->getNextMonth()->getDateTimeInstance()->format('Y'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsNextMonthForNextYear(): void
+ {
+ $subject = new Month(12, 2020);
+
+ self::assertSame('01', $subject->getNextMonth()->getDateTimeInstance()->format('m'));
+ self::assertSame('2021', $subject->getNextMonth()->getDateTimeInstance()->format('Y'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsFiveWeeksForDecember2020(): void
+ {
+ $subject = new Month(12, 2020);
+ $weeks = $subject->getWeeks();
+
+ self::assertCount(5, $weeks);
+ self::assertSame('2020-11-30', $weeks[0]->getDays()[0]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2021-01-03', $weeks[4]->getDays()[6]->getDateTimeInstance()->format('Y-m-d'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsSixWeeksForNovember2020(): void
+ {
+ $subject = new Month(11, 2020);
+ $weeks = $subject->getWeeks();
+
+ self::assertCount(6, $weeks);
+ self::assertSame('2020-10-26', $weeks[0]->getDays()[0]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2020-12-06', $weeks[5]->getDays()[6]->getDateTimeInstance()->format('Y-m-d'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsSameWeeksOnSecondCall(): void
+ {
+ $subject = new Month(11, 2020);
+
+ self::assertSame($subject->getWeeks(), $subject->getWeeks());
+ }
+
+ /**
+ * @test
+ */
+ public function providesDateTimeInstance(): void
+ {
+ $subject = new Month(02, 2018);
+
+ self::assertSame('2018-02-01', $subject->getDateTimeInstance()->format('Y-m-d'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsAsUrlArguments(): void
+ {
+ $subject = new Month(02, 2018);
+
+ self::assertSame([
+ 'month' => 2,
+ 'year' => 2018,
+ ], $subject->getAsUrlArgument());
+ }
+
+ /**
+ * @test
+ */
+ public function returnsNotActiveIfAllWeeksAreInactive(): void
+ {
+ $subject = new Month(02, 2018);
+
+ $week = $this->prophesize(Week::class);
+ $week->isActive()->willReturn(false);
+ $weeks = [$week->reveal()];
+ $this->forceProperty($subject, 'weeks', $weeks);
+
+ self::assertFalse($subject->isActive());
+ }
+
+ /**
+ * @test
+ */
+ public function returnsActiveIfASingleWeekIsActive(): void
+ {
+ $subject = new Month(02, 2018);
+
+ $week = $this->prophesize(Week::class);
+ $week->isActive()->willReturn(true);
+ $week2 = $this->prophesize(Week::class);
+ $week2->isActive()->willReturn(false);
+ $weeks = [$week->reveal(), $week2->reveal()];
+ $this->forceProperty($subject, 'weeks', $weeks);
+
+ self::assertTrue($subject->isActive());
+ }
+}
diff --git a/Tests/Unit/Domain/Model/NullDataFactoryTest.php b/Tests/Unit/Domain/Model/NullDataFactoryTest.php
new file mode 100644
index 0000000..7e4ea07
--- /dev/null
+++ b/Tests/Unit/Domain/Model/NullDataFactoryTest.php
@@ -0,0 +1,45 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use Prophecy\PhpUnit\ProphecyTrait;
+use WerkraumMedia\Calendar\Domain\Model\Day;
+use WerkraumMedia\Calendar\Domain\Model\NullDataFactory;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers WerkraumMedia\Calendar\Domain\Model\NullDataFactory
+ */
+class NullDataFactoryTest extends TestCase
+{
+ use ProphecyTrait;
+
+ /**
+ * @test
+ */
+ public function returnsNull(): void
+ {
+ $subject = new NullDataFactory();
+ $day = $this->prophesize(Day::class);
+ self::assertNull($subject->getData($day->reveal()));
+ }
+}
diff --git a/Tests/Unit/Domain/Model/WeekTest.php b/Tests/Unit/Domain/Model/WeekTest.php
new file mode 100644
index 0000000..be5a8cf
--- /dev/null
+++ b/Tests/Unit/Domain/Model/WeekTest.php
@@ -0,0 +1,235 @@
+
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use PHPUnit\Framework\TestCase;
+use Prophecy\PhpUnit\ProphecyTrait;
+use WerkraumMedia\Calendar\Domain\Model\Day;
+use WerkraumMedia\Calendar\Domain\Model\Week;
+use WerkraumMedia\Calendar\Tests\ForcePropertyTrait;
+
+/**
+ * @covers WerkraumMedia\Calendar\Domain\Model\Week
+ * @testdox A week
+ */
+class WeekTest extends TestCase
+{
+ use ProphecyTrait;
+ use ForcePropertyTrait;
+
+ /**
+ * @test
+ */
+ public function canBeCreated(): void
+ {
+ $subject = new Week(1, 2020);
+
+ self::assertInstanceOf(Week::class, $subject);
+ }
+
+ /**
+ * @test
+ */
+ public function returnsSevenDays(): void
+ {
+ $subject = new Week(1, 2020);
+
+ self::assertCount(7, $subject->getDays());
+ }
+
+ /**
+ * @test
+ */
+ public function returnsSameDaysOnSecondCall(): void
+ {
+ $subject = new Week(1, 2020);
+
+ self::assertSame($subject->getDays(), $subject->getDays());
+ }
+
+ /**
+ * @test
+ */
+ public function returnsDaysForWeek1(): void
+ {
+ $subject = new Week(1, 2020);
+ $days = $subject->getDays();
+
+ self::assertSame('2019-12-30', $days[0]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2019-12-31', $days[1]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2020-01-01', $days[2]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2020-01-02', $days[3]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2020-01-03', $days[4]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2020-01-04', $days[5]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2020-01-05', $days[6]->getDateTimeInstance()->format('Y-m-d'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsDaysForWeek53(): void
+ {
+ $subject = new Week(53, 2020);
+ $days = $subject->getDays();
+
+ self::assertSame('2020-12-28', $days[0]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2020-12-29', $days[1]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2020-12-30', $days[2]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2020-12-31', $days[3]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2021-01-01', $days[4]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2021-01-02', $days[5]->getDateTimeInstance()->format('Y-m-d'));
+ self::assertSame('2021-01-03', $days[6]->getDateTimeInstance()->format('Y-m-d'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsPreviousWeekForSameYear(): void
+ {
+ $subject = new Week(2, 2020);
+
+ self::assertSame('01', $subject->getPreviousWeek()->getDateTimeInstance()->format('W'));
+ self::assertSame('2020', $subject->getPreviousWeek()->getDateTimeInstance()->format('Y'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsPreviousWeekForYearSwitchFrom2019To2018(): void
+ {
+ $subject = new Week(1, 2019);
+
+ self::assertSame('52', $subject->getPreviousWeek()->getDateTimeInstance()->format('W'));
+ self::assertSame('2018', $subject->getPreviousWeek()->getDateTimeInstance()->format('Y'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsPreviousWeekForYearSwitchFrom2018To2017(): void
+ {
+ $subject = new Week(1, 2018);
+
+ self::assertSame('52', $subject->getPreviousWeek()->getDateTimeInstance()->format('W'));
+ self::assertSame('2017', $subject->getPreviousWeek()->getDateTimeInstance()->format('Y'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsPreviousWeekForPreviousYear(): void
+ {
+ $subject = new Week(1, 2021);
+
+ self::assertSame('53', $subject->getPreviousWeek()->getDateTimeInstance()->format('W'));
+ self::assertSame('2020', $subject->getPreviousWeek()->getDateTimeInstance()->format('Y'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsNextWeekForSameYear(): void
+ {
+ $subject = new Week(1, 2020);
+
+ self::assertSame('02', $subject->getNextWeek()->getDateTimeInstance()->format('W'));
+ self::assertSame('2020', $subject->getNextWeek()->getDateTimeInstance()->format('Y'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsNextWeekForNextYear(): void
+ {
+ $subject = new Week(53, 2020);
+
+ self::assertSame('01', $subject->getNextWeek()->getDateTimeInstance()->format('W'));
+ self::assertSame('2021', $subject->getNextWeek()->getDateTimeInstance()->format('Y'));
+ }
+
+ /**
+ * @test
+ */
+ public function returnsNextWeekForNextYearFrom2018To2019(): void
+ {
+ $subject = new Week(52, 2018);
+
+ self::assertSame('01', $subject->getNextWeek()->getDateTimeInstance()->format('W'));
+ self::assertSame('2019', $subject->getNextWeek()->getDateTimeInstance()->format('Y'));
+ }
+
+ /**
+ * @test
+ */
+ public function providesDateTimeInstance(): void
+ {
+ $subject = new Week(52, 2018);
+
+ self::assertSame('52', $subject->getDateTimeInstance()->format('W'));
+ self::assertSame('2018-12-27 Thursday', $subject->getDateTimeInstance()->format('Y-m-d l'));
+ }
+
+ /**
+ * @test
+ */
+ public function providesItselfAsUrlArgument(): void
+ {
+ $subject = new Week(52, 2018);
+
+ self::assertSame([
+ 'week' => 52,
+ 'year' => 2018,
+ ], $subject->getAsUrlArgument());
+ }
+
+ /**
+ * @test
+ */
+ public function returnsNotActiveIfAllDaysAreInactive(): void
+ {
+ $subject = new Week(02, 2018);
+
+ $day = $this->prophesize(Day::class);
+ $day->isActive()->willReturn(false);
+ $days = [$day->reveal()];
+ $this->forceProperty($subject, 'days', $days);
+
+ self::assertFalse($subject->isActive());
+ }
+
+ /**
+ * @test
+ */
+ public function returnsActiveIfASingleDayActive(): void
+ {
+ $subject = new Week(02, 2018);
+
+ $day = $this->prophesize(Day::class);
+ $day->isActive()->willReturn(true);
+ $day2 = $this->prophesize(Day::class);
+ $day2->isActive()->willReturn(false);
+ $days = [$day->reveal(), $day2->reveal()];
+ $this->forceProperty($subject, 'days', $days);
+
+ self::assertTrue($subject->isActive());
+ }
+}
diff --git a/composer.json b/composer.json
index 6f5d9ae..584e6e4 100644
--- a/composer.json
+++ b/composer.json
@@ -13,6 +13,7 @@
"sort-packages": true
},
"require": {
+ "php": "^7.3.0 || ^7.4.0",
"typo3/cms-core": "^10.4",
"typo3/cms-extbase": "^10.4"
},
@@ -28,8 +29,10 @@
},
"require-dev": {
"jangregor/phpstan-prophecy": "^0.6.2",
+ "maglnet/composer-require-checker": "^2.1",
"phpspec/prophecy-phpunit": "^2.0",
- "phpunit/phpunit": "^9.4"
+ "phpunit/phpunit": "^9.4",
+ "squizlabs/php_codesniffer": "^3.5"
},
"extra": {
"typo3/cms": {
diff --git a/ext_emconf.php b/ext_emconf.php
new file mode 100644
index 0000000..166046b
--- /dev/null
+++ b/ext_emconf.php
@@ -0,0 +1,21 @@
+ 'Calendar',
+ 'description' => 'API for extensions to create calendar',
+ 'category' => 'misc',
+ 'author' => 'Daniel Siepmann',
+ 'author_email' => 'coding@daniel-siepmann.de',
+ 'author_company' => 'Codappix GmbH',
+ 'state' => 'alpha',
+ 'uploadfolder' => 0,
+ 'clearCacheOnLoad' => 0,
+ 'version' => '1.0.0',
+ 'constraints' => [
+ 'depends' => [
+ 'typo3' => '*',
+ ],
+ 'conflicts' => [],
+ 'suggests' => [],
+ ],
+];
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100644
index 0000000..68b095a
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,19 @@
+
+
+ This project coding standard
+
+ Classes/
+ Tests/
+
+
+
+
+
+
+
+
+
+
+ /Tests/*
+
+
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..7b93a5c
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,30 @@
+
+
+
+
+
+ Tests/Unit/
+
+
+
+
+
+ Classes
+
+
+