diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4622895 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,94 @@ +name: CI + +on: push + +jobs: + check-codestyle: + name: Check codestyle + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + extensions: bcmath, dom, fileinfo, filter, gd, hash, intl, json, mbstring, mysqli, pcre, pdo_mysql, zip, zlib + coverage: none + + env: + COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install PHP dependencies + run: composer install --no-interaction --no-progress + + - name: Check PHP codestyle + run: | + vendor-bin/php-cs-fixer/vendor/bin/php-cs-fixer fix --dry-run -vvv --ansi + + - name: Run static analysis + run: vendor-bin/phpstan/vendor/bin/phpstan analyze src/ --level max --memory-limit=-1 --ansi + + unit-tests: + name: Unit tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + extensions: bcmath, dom, fileinfo, filter, gd, hash, intl, json, mbstring, mysqli, pcre, pdo_mysql, zip, zlib + coverage: none + + env: + COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install the dependencies + run: composer install --no-interaction --no-progress + + - name: Run unit tests + run: vendor-bin/phpunit/vendor/bin/phpunit --colors=always + + check-composer-requirements: + name: Check Composer requirements + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + extensions: bcmath, dom, fileinfo, filter, gd, hash, intl, json, mbstring, mysqli, pcre, pdo_mysql, zip, zlib + coverage: none + + env: + COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install the dependencies + run: composer install --no-interaction --no-progress + + - name: Check the dependencies + run: tools/require-checker/vendor/bin/composer-require-checker check --config-file=tools/require-checker/config.json composer.json --ansi + + security-check: + name: PHP Security Check + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup cache + uses: actions/cache@v2 + id: cache-db + with: + path: ~/.symfony/cache + key: db + + - name: Run security check + uses: symfonycorp/security-checker-action@v3 diff --git a/.gitignore b/.gitignore index 3da35e3..c0d3b09 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,6 @@ -# OS -.DS_Store -Thumbs.db - -# IDEs -.buildpath -.project -.settings/ -.build/ .idea/ - -# Composer -vendor/ -/vendor-bin/**/vendor +/vendor/ +/vendor-bin/*/vendor +/vendor-bin/*/composer.lock +.phpunit.result.cache composer.lock -composer.phar diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index e85922c..71b699d 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -3,7 +3,7 @@ $finder = PhpCsFixer\Finder::create() ->exclude('Resources') ->exclude('Fixtures') - ->in([__DIR__ . '/src', __DIR__ . '/contao', __DIR__ . '/config']) + ->in([__DIR__ . '/config', __DIR__ . '/src', __DIR__ . '/tests']) ; $config = new PhpCsFixer\Config(); diff --git a/README.md b/README.md new file mode 100644 index 0000000..e3be50f --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +contao-backend-limited-width +============================ +Simply because https://github.com/contao/contao/pull/4552. diff --git a/assets/limited-width.css b/assets/limited-width.css new file mode 100644 index 0000000..e797a5d --- /dev/null +++ b/assets/limited-width.css @@ -0,0 +1,22 @@ +:root { + --body-bg: #cfcfd3; + --main-bg: #eaeaec; +} + +html[data-color-scheme="dark"] { + +} + +body { + background: var(--body-bg) !important; +} + +#main { + background: var(--main-bg) !important; +} + +#container, +#header { + max-width:1440px; + margin:0 auto; +} diff --git a/composer.json b/composer.json index 083e1d1..c26acc3 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,12 @@ ], "require": { "php": "^8.1", - "contao/core-bundle": "^5.0" + "contao/core-bundle": "^5.0", + "doctrine/dbal": "^3.3", + "symfony/config": "^5.4 || ^6.0", + "symfony/dependency-injection": "^5.4 || ^6.0", + "symfony/http-kernel": "^5.4 || ^6.0", + "symfony/security-bundle": "^6.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.4", @@ -55,7 +60,8 @@ "contao-components/installer": true, "contao/manager-plugin": true, "php-http/discovery": true - } + }, + "sort-packages": true }, "scripts": { "post-install-cmd": [ @@ -70,6 +76,9 @@ "phpstan": [ "@php vendor-bin/phpstan/vendor/bin/phpstan analyze src/ --level max --memory-limit=-1 --ansi" ], + "phpunit": [ + "@php vendor-bin/phpunit/vendor/bin/phpunit --colors=always" + ], "rector": [ "@php vendor-bin/rector/vendor/bin/rector process --clear-cache --dry-run" ], @@ -77,7 +86,7 @@ "@php vendor-bin/rector/vendor/bin/rector --clear-cache process" ], "require-checker": [ - "@php vendor-bin/require-checker/vendor/bin/composer-require-checker check --config-file=tools/require-checker/config.json composer.json --ansi" + "@php vendor-bin/require-checker/vendor/bin/composer-require-checker check --config-file=vendor-bin/require-checker/config.json composer.json --ansi" ] } } diff --git a/config/services.yaml b/config/services.yaml index e69de29..fc7d9e9 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -0,0 +1,7 @@ +services: + Oneup\ContaoBackendLimitedWidthBundle\EventListener\ParseTemplateListener: + arguments: + - '@security.helper' + - '@contao.framework' + tags: + - { name: contao.hook, hook: parseTemplate } diff --git a/phpstan.neon b/phpstan.neon index 37f072b..238f901 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,5 +1,5 @@ includes: - - tools/phpstan/vendor/phpstan/phpstan-symfony/extension.neon + - vendor-bin/phpstan/vendor/phpstan/phpstan-symfony/extension.neon parameters: bootstrapFiles: @@ -8,5 +8,6 @@ parameters: checkMissingIterableValueType: false reportUnmatchedIgnoredErrors: false universalObjectCratesClasses: + - Contao\BackendUser ignoreErrors: diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..6bbb929 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,13 @@ + + + + + src + + + + + tests + + + diff --git a/public/limited-width.min.css b/public/limited-width.min.css new file mode 100644 index 0000000..fe98c96 --- /dev/null +++ b/public/limited-width.min.css @@ -0,0 +1 @@ +:root{--body-bg:#cfcfd3;--main-bg:#eaeaec}html[data-color-scheme="dark"]{--main-bg:#32363e}body{background:var(--body-bg)!important}#main{background:var(--main-bg)!important}#container,#header{max-width:1440px;margin:0 auto} diff --git a/src/DependencyInjection/OneupContaoBackendLimitedWidthExtension.php b/src/DependencyInjection/OneupContaoBackendLimitedWidthExtension.php index f503cfc..89c1fd0 100644 --- a/src/DependencyInjection/OneupContaoBackendLimitedWidthExtension.php +++ b/src/DependencyInjection/OneupContaoBackendLimitedWidthExtension.php @@ -13,7 +13,7 @@ final class OneupContaoBackendLimitedWidthExtension extends Extension { public function load(array $configs, ContainerBuilder $container): void { - (new YamlFileLoader($container, new FileLocator(__DIR__.'/../../config'))) + (new YamlFileLoader($container, new FileLocator(__DIR__ . '/../../config'))) ->load('services.yaml') ; } diff --git a/src/EventListener/ParseTemplateListener.php b/src/EventListener/ParseTemplateListener.php new file mode 100644 index 0000000..ba23d6c --- /dev/null +++ b/src/EventListener/ParseTemplateListener.php @@ -0,0 +1,50 @@ +getName() && !str_starts_with($template->getName(), 'be_main_')) { + return; + } + + $user = $this->security->getUser(); + + if (!$user instanceof BackendUser) { + return; + } + + if (false === (bool) $user->limitedWidth) { + return; + } + + /** @var Template $templateAdapter */ + $templateAdapter = $this->contaoFramework->getAdapter(Template::class); + + /** @var Controller $controllerAdapter */ + $controllerAdapter = $this->contaoFramework->getAdapter(Controller::class); + + $template->stylesheets .= $templateAdapter->generateStyleTag($controllerAdapter->addStaticUrlTo('bundles/oneupcontaobackendlimitedwidth/limited-width.min.css'), null, null); + } +} diff --git a/src/OneupContaoBackendLimitedWidthBundle.php b/src/OneupContaoBackendLimitedWidthBundle.php index e9157b2..be4d6cc 100644 --- a/src/OneupContaoBackendLimitedWidthBundle.php +++ b/src/OneupContaoBackendLimitedWidthBundle.php @@ -8,4 +8,8 @@ class OneupContaoBackendLimitedWidthBundle extends Bundle { + public function getPath(): string + { + return \dirname(__DIR__); + } } diff --git a/tests/ContaoManager/PluginTest.php b/tests/ContaoManager/PluginTest.php new file mode 100644 index 0000000..677770a --- /dev/null +++ b/tests/ContaoManager/PluginTest.php @@ -0,0 +1,30 @@ +getBundles($this->createMock(ParserInterface::class)); + + self::assertCount(1, $bundles); + + /** @var BundleConfig $config */ + $config = $bundles[0]; + + self::assertSame(OneupContaoBackendLimitedWidthBundle::class, $config->getName()); + self::assertSame([ContaoCoreBundle::class], $config->getLoadAfter()); + } +} diff --git a/tests/DependencyInjection/OneupContaoBackendLimitedWidthExtensionTest.php b/tests/DependencyInjection/OneupContaoBackendLimitedWidthExtensionTest.php new file mode 100644 index 0000000..cbbe268 --- /dev/null +++ b/tests/DependencyInjection/OneupContaoBackendLimitedWidthExtensionTest.php @@ -0,0 +1,25 @@ +load([], $containerBuilder); + $definitions = array_keys($containerBuilder->getDefinitions()); + + self::assertContains(ParseTemplateListener::class, $definitions); + self::assertCount(2, $definitions); + } +} diff --git a/tests/EventListener/ParseTemplateListenerTest.php b/tests/EventListener/ParseTemplateListenerTest.php new file mode 100644 index 0000000..70dfa13 --- /dev/null +++ b/tests/EventListener/ParseTemplateListenerTest.php @@ -0,0 +1,147 @@ +createMock(Security::class); + $contaoFramework = $this->createMock(ContaoFramework::class); + + $template = $this->createMock(Template::class); + $template + ->expects($this->never()) + ->method('getName') + ; + + $listener = new ParseTemplateListener($security, $contaoFramework); + $listener($template); + } + + public function testDoesNothingIfNotBackendMainTemplate(): void + { + $security = $this->createMock(Security::class); + $security + ->expects($this->never()) + ->method('getUser') + ; + + $contaoFramework = $this->createMock(ContaoFramework::class); + + $template = $this->createMock(BackendTemplate::class); + $template + ->expects($this->exactly(2)) + ->method('getName') + ->willReturn('fe_page') + ; + + $listener = new ParseTemplateListener($security, $contaoFramework); + $listener($template); + } + + public function testDoesNothingIfNoBackendUserIsPresent(): void + { + $security = $this->createMock(Security::class); + $security + ->expects($this->once()) + ->method('getUser') + ->willReturn($this->createMock(FrontendUser::class)) + ; + + $contaoFramework = $this->createMock(ContaoFramework::class); + $contaoFramework + ->expects($this->never()) + ->method('getAdapter') + ; + + $template = $this->createMock(BackendTemplate::class); + $template + ->expects($this->atLeastOnce()) + ->method('getName') + ->willReturn('be_main') + ; + + $listener = new ParseTemplateListener($security, $contaoFramework); + $listener($template); + } + + public function testDoesNothingIfLimitedWidthIsNotChecked(): void + { + $user = $this->createMock(BackendUser::class); + $user + ->expects($this->once()) + ->method('__get') + ->with('limitedWidth') + ->willReturn(false) + ; + + $security = $this->createMock(Security::class); + $security + ->expects($this->once()) + ->method('getUser') + ->willReturn($user) + ; + + $contaoFramework = $this->createMock(ContaoFramework::class); + $contaoFramework + ->expects($this->never()) + ->method('getAdapter') + ; + + $template = $this->createMock(BackendTemplate::class); + $template + ->expects($this->atLeastOnce()) + ->method('getName') + ->willReturn('be_main') + ; + + $listener = new ParseTemplateListener($security, $contaoFramework); + $listener($template); + } + + public function testAddsStyleSheet(): void + { + $user = $this->createMock(BackendUser::class); + $user + ->expects($this->once()) + ->method('__get') + ->with('limitedWidth') + ->willReturn(true) + ; + + $security = $this->createMock(Security::class); + $security + ->expects($this->once()) + ->method('getUser') + ->willReturn($user) + ; + + $contaoFramework = $this->createMock(ContaoFramework::class); + $contaoFramework + ->expects($this->exactly(2)) + ->method('getAdapter') + ; + + $template = $this->createMock(BackendTemplate::class); + $template + ->expects($this->atLeastOnce()) + ->method('getName') + ->willReturn('be_main') + ; + + $listener = new ParseTemplateListener($security, $contaoFramework); + $listener($template); + } +} diff --git a/vendor-bin/phpunit/composer.json b/vendor-bin/phpunit/composer.json new file mode 100644 index 0000000..24769c6 --- /dev/null +++ b/vendor-bin/phpunit/composer.json @@ -0,0 +1,8 @@ +{ + "license": "proprietary", + "type": "project", + "php": ">=8.1", + "require-dev": { + "phpunit/phpunit": "^9.5" + } +}