diff --git a/composer.json b/composer.json index 364cf92..6070168 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,8 @@ "symfony/process": "^6.3", "yii2-extensions/debug": "dev-improve-config", "yii2-extensions/gii": "dev-main", - "yii2-extensions/phpstan": "dev-main" + "yii2-extensions/phpstan": "dev-main", + "codeception/module-rest": "^3.3" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 503ead5..0f4eaca 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "508854391f777d23a09762490ada9e7a", + "content-hash": "fd8c01c2122acfc766ba5d07791f7943", "packages": [ { "name": "bower-asset/inputmask", @@ -2199,12 +2199,12 @@ "source": { "type": "git", "url": "https://github.com/yii2-extensions/asset-bootstrap5.git", - "reference": "4fced22d51977b157dffd1a3c1868b2990b0d689" + "reference": "c5e43b3a3e1a1ee89e5479cb2b112b61d2aa45c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yii2-extensions/asset-bootstrap5/zipball/4fced22d51977b157dffd1a3c1868b2990b0d689", - "reference": "4fced22d51977b157dffd1a3c1868b2990b0d689", + "url": "https://api.github.com/repos/yii2-extensions/asset-bootstrap5/zipball/c5e43b3a3e1a1ee89e5479cb2b112b61d2aa45c6", + "reference": "c5e43b3a3e1a1ee89e5479cb2b112b61d2aa45c6", "shasum": "" }, "require": { @@ -2212,12 +2212,13 @@ "npm-asset/popperjs--core": "^2.11", "oomphinc/composer-installers-extender": "^2.0", "php": ">=8.1", - "yiisoft/yii2": "^2.2" + "yiisoft/yii2": "*" }, "require-dev": { "maglnet/composer-require-checker": "^4.6", "php-forge/support": "dev-main", "phpunit/phpunit": "^10.2", + "roave/infection-static-analysis-plugin": "^1.32", "yii2-extensions/phpstan": "dev-main" }, "default-branch": true, @@ -2256,7 +2257,7 @@ "issues": "https://github.com/yii2-extensions/asset-bootstrap5/issues", "source": "https://github.com/yii2-extensions/asset-bootstrap5/tree/main" }, - "time": "2023-11-17T21:00:37+00:00" + "time": "2023-11-19T14:17:44+00:00" }, { "name": "yii2-extensions/bootstrap5", @@ -2332,18 +2333,18 @@ "source": { "type": "git", "url": "https://github.com/yii2-extensions/localeurls.git", - "reference": "dd92af04aabeb32a027c2bffdb0a6563ce74e37a" + "reference": "dd886bae0b41615f5917602225e2dbd766f8960f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yii2-extensions/localeurls/zipball/dd92af04aabeb32a027c2bffdb0a6563ce74e37a", - "reference": "dd92af04aabeb32a027c2bffdb0a6563ce74e37a", + "url": "https://api.github.com/repos/yii2-extensions/localeurls/zipball/dd886bae0b41615f5917602225e2dbd766f8960f", + "reference": "dd886bae0b41615f5917602225e2dbd766f8960f", "shasum": "" }, "require": { "ext-mbstring": "*", "php": ">=8.1", - "yiisoft/yii2": "^2.2" + "yiisoft/yii2": "*" }, "require-dev": { "maglnet/composer-require-checker": "^4.6", @@ -2351,7 +2352,7 @@ "yii2-extensions/phpstan": "dev-main" }, "default-branch": true, - "type": "yii2-extension", + "type": "library", "extra": { "branch-alias": { "dev-main": "1.0.x-dev" @@ -2366,7 +2367,7 @@ }, "autoload": { "psr-4": { - "yii\\localeurls\\": "src" + "Yii2\\Extensions\\LocaleUrls\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2383,7 +2384,7 @@ "issues": "https://github.com/yii2-extensions/localeurls/issues", "source": "https://github.com/yii2-extensions/localeurls/tree/main" }, - "time": "2023-11-18T11:58:39+00:00" + "time": "2023-11-20T10:48:28+00:00" }, { "name": "yiisoft/arrays", @@ -3338,6 +3339,56 @@ }, "time": "2023-04-18T20:32:51+00:00" }, + { + "name": "codeception/lib-xml", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/Codeception/lib-xml.git", + "reference": "d6e4c094fb83958bcf254a20815cea5ac31e98d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/lib-xml/zipball/d6e4c094fb83958bcf254a20815cea5ac31e98d0", + "reference": "d6e4c094fb83958bcf254a20815cea5ac31e98d0", + "shasum": "" + }, + "require": { + "codeception/lib-web": "^1.0", + "ext-dom": "*", + "php": "^8.0", + "phpunit/phpunit": "^9.5 | ^10.0", + "symfony/css-selector": ">=4.4.24 <7.0" + }, + "conflict": { + "codeception/codeception": "<5.0.0-alpha3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gintautas Miselis" + } + ], + "description": "Files used by module-rest and module-soap", + "homepage": "https://codeception.com/", + "keywords": [ + "codeception" + ], + "support": { + "issues": "https://github.com/Codeception/lib-xml/issues", + "source": "https://github.com/Codeception/lib-xml/tree/1.0.1" + }, + "time": "2022-09-11T14:09:09+00:00" + }, { "name": "codeception/module-asserts", "version": "3.0.0", @@ -3447,6 +3498,66 @@ }, "time": "2022-03-14T18:48:55+00:00" }, + { + "name": "codeception/module-rest", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/Codeception/module-rest.git", + "reference": "bb545d4f7c261472472da8730267d9df162199cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Codeception/module-rest/zipball/bb545d4f7c261472472da8730267d9df162199cb", + "reference": "bb545d4f7c261472472da8730267d9df162199cb", + "shasum": "" + }, + "require": { + "codeception/codeception": "^5.0.8", + "codeception/lib-xml": "^1.0", + "ext-dom": "*", + "ext-json": "*", + "justinrainbow/json-schema": "~5.2.9", + "php": "^8.0", + "softcreatr/jsonpath": "^0.8" + }, + "require-dev": { + "codeception/lib-innerbrowser": "^3.0 | ^4.0", + "codeception/stub": "^4.0", + "codeception/util-universalframework": "^1.0", + "ext-libxml": "*", + "ext-simplexml": "*" + }, + "suggest": { + "aws/aws-sdk-php": "For using AWS Auth" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gintautas Miselis" + } + ], + "description": "REST module for Codeception", + "homepage": "https://codeception.com/", + "keywords": [ + "codeception", + "rest" + ], + "support": { + "issues": "https://github.com/Codeception/module-rest/issues", + "source": "https://github.com/Codeception/module-rest/tree/3.3.2" + }, + "time": "2023-02-09T18:11:19+00:00" + }, { "name": "codeception/module-yii2", "version": "1.1.9", @@ -3713,6 +3824,76 @@ ], "time": "2023-08-27T10:13:57+00:00" }, + { + "name": "justinrainbow/json-schema", + "version": "v5.2.13", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/v5.2.13" + }, + "time": "2023-09-26T02:20:38+00:00" + }, { "name": "maglnet/composer-require-checker", "version": "4.7.1", @@ -4129,16 +4310,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.42", + "version": "1.10.43", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "fc2316508de5453140b5cb3d3f8683a33e92f26a" + "reference": "2c4129f6ca8c7cfa870098884b8869b410a5a361" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/fc2316508de5453140b5cb3d3f8683a33e92f26a", - "reference": "fc2316508de5453140b5cb3d3f8683a33e92f26a", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/2c4129f6ca8c7cfa870098884b8869b410a5a361", + "reference": "2c4129f6ca8c7cfa870098884b8869b410a5a361", "shasum": "" }, "require": { @@ -4187,7 +4368,7 @@ "type": "tidelift" } ], - "time": "2023-11-17T15:26:57+00:00" + "time": "2023-11-19T19:55:25+00:00" }, { "name": "phpunit/php-code-coverage", @@ -5758,6 +5939,74 @@ ], "time": "2023-02-07T11:34:05+00:00" }, + { + "name": "softcreatr/jsonpath", + "version": "0.8.3", + "source": { + "type": "git", + "url": "https://github.com/SoftCreatR/JSONPath.git", + "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SoftCreatR/JSONPath/zipball/fc12dee0b46f3fa3a175c4051dbab60984acef4b", + "reference": "fc12dee0b46f3fa3a175c4051dbab60984acef4b", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=8.0" + }, + "replace": { + "flow/jsonpath": "*" + }, + "require-dev": { + "phpunit/phpunit": "^9.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "autoload": { + "psr-4": { + "Flow\\JSONPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Stephen Frank", + "email": "stephen@flowsa.com", + "homepage": "https://prismaticbytes.com", + "role": "Developer" + }, + { + "name": "Sascha Greuel", + "email": "hello@1-2.dev", + "homepage": "https://1-2.dev", + "role": "Developer" + } + ], + "description": "JSONPath implementation for parsing, searching and flattening arrays", + "support": { + "email": "hello@1-2.dev", + "forum": "https://github.com/SoftCreatR/JSONPath/discussions", + "issues": "https://github.com/SoftCreatR/JSONPath/issues", + "source": "https://github.com/SoftCreatR/JSONPath" + }, + "funding": [ + { + "url": "https://ecologi.com/softcreatr?r=61212ab3fc69b8eb8a2014f4", + "type": "custom" + }, + { + "url": "https://github.com/softcreatr", + "type": "github" + } + ], + "time": "2023-08-17T20:14:00+00:00" + }, { "name": "symfony/browser-kit", "version": "v6.3.8", @@ -6499,16 +6748,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", - "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", "shasum": "" }, "require": { @@ -6537,7 +6786,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" }, "funding": [ { @@ -6545,7 +6794,7 @@ "type": "github" } ], - "time": "2021-07-28T10:34:58+00:00" + "time": "2023-11-20T00:12:19+00:00" }, { "name": "webmozart/assert", @@ -6781,19 +7030,19 @@ "source": { "type": "git", "url": "https://github.com/yii2-extensions/phpstan.git", - "reference": "a2cf494866c35b3586ee8bc4d6824d91abb12141" + "reference": "88b84a5851cb12361c93b1cae6dcb9b95a2dedd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yii2-extensions/phpstan/zipball/a2cf494866c35b3586ee8bc4d6824d91abb12141", - "reference": "a2cf494866c35b3586ee8bc4d6824d91abb12141", + "url": "https://api.github.com/repos/yii2-extensions/phpstan/zipball/88b84a5851cb12361c93b1cae6dcb9b95a2dedd3", + "reference": "88b84a5851cb12361c93b1cae6dcb9b95a2dedd3", "shasum": "" }, "require": { "nikic/php-parser": "^4.1.0", "php": ">=8.1", "phpstan/phpstan": "^1.0", - "yiisoft/yii2": "^2.2" + "yiisoft/yii2": "*" }, "require-dev": { "maglnet/composer-require-checker": "^4.6", @@ -6825,7 +7074,7 @@ "issues": "https://github.com/yii2-extensions/phpstan/issues", "source": "https://github.com/yii2-extensions/phpstan/tree/main" }, - "time": "2023-10-11T09:59:22+00:00" + "time": "2023-11-19T14:24:04+00:00" } ], "aliases": [], diff --git a/config/params-web.php b/config/params-web.php index 9d6ac36..eda77cd 100644 --- a/config/params-web.php +++ b/config/params-web.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use App\UseCase\Api\ApiController; use App\UseCase\Contact\ContactController; use App\UseCase\Site\SiteController; @@ -16,6 +17,9 @@ ], 'app.assetManager.basePath' => '@public/assets', 'app.controllerMap' => [ + 'api' => [ + 'class' => ApiController::class, + ], 'contact' => [ 'class' => ContactController::class, ], diff --git a/public/index.php b/public/index.php index f567043..7a65744 100644 --- a/public/index.php +++ b/public/index.php @@ -8,7 +8,7 @@ use Yiisoft\Config\Modifier\RecursiveMerge; // comment out the following two lines when deployed to production -defined('YII_DEBUG') or define('YII_DEBUG', false); +defined('YII_DEBUG') or define('YII_DEBUG', true); if (getenv('YII_ENV')) { defined('YII_ENV') or define('YII_ENV', getenv('YII_ENV')); diff --git a/src/Framework/resource/js/toggle-theme.js b/src/Framework/resource/js/toggle-theme.js index a48b6be..1ca5e91 100644 --- a/src/Framework/resource/js/toggle-theme.js +++ b/src/Framework/resource/js/toggle-theme.js @@ -4,86 +4,114 @@ * Licensed under the Creative Commons Attribution 3.0 Unported License. */ (() => { - 'use strict' - - const getStoredTheme = () => localStorage.getItem('theme') - const setStoredTheme = theme => localStorage.setItem('theme', theme) - - const getPreferredTheme = () => { - const storedTheme = getStoredTheme() - if (storedTheme) { - return storedTheme - } - - return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' - } - - const setTheme = theme => { - if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) { - document.documentElement.setAttribute('data-bs-theme', 'dark') - } else { - document.documentElement.setAttribute('data-bs-theme', theme) - } - } - - setTheme(getPreferredTheme()) - - const showActiveTheme = (theme, focus = false) => { - const themeSwitcher = document.querySelector('#toggle-theme') - - if (!themeSwitcher) { - return - } - - const themeSwitcherText = document.querySelector('#toggle-theme-text') - const activeThemeIcon = document.querySelector('.theme-icon-active use') - const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`) - const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href') - - // Añade las siguientes líneas para mostrar el icono de verificación en el botón activo. - const activeCheckIcon = btnToActive.querySelector('.check'); - activeCheckIcon.classList.remove('d-none'); - - document.querySelectorAll('[data-bs-theme-value]').forEach(element => { - const checkIcon = element.querySelector('.check'); - if (element === btnToActive) { - checkIcon.classList.remove('d-none'); - } else { - checkIcon.classList.add('d-none'); - } - element.classList.remove('active') - element.setAttribute('aria-pressed', 'false') - }) - - btnToActive.classList.add('active') - btnToActive.setAttribute('aria-pressed', 'true') - activeThemeIcon.setAttribute('href', svgOfActiveBtn) - const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})` - themeSwitcher.setAttribute('aria-label', themeSwitcherLabel) - - if (focus) { - themeSwitcher.focus() - } - } - - window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { - const storedTheme = getStoredTheme() - if (storedTheme !== 'light' && storedTheme !== 'dark') { - setTheme(getPreferredTheme()) - } - }) - - window.addEventListener('DOMContentLoaded', () => { - showActiveTheme(getPreferredTheme()) - - document.querySelectorAll('[data-bs-theme-value]') - .forEach(toggle => { - toggle.addEventListener('click', () => { - const theme = toggle.getAttribute('data-bs-theme-value') - setStoredTheme(theme) - setTheme(theme) - showActiveTheme(theme, true) + 'use strict'; + + const getStoredTheme = () => localStorage.getItem('theme'); + const setStoredTheme = (theme) => localStorage.setItem('theme', theme); + + const getPreferredTheme = () => { + const storedTheme = getStoredTheme(); + + if (storedTheme) { + return storedTheme; + } + + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; + }; + + const setTheme = (theme) => { + if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) { + document.documentElement.setAttribute('data-bs-theme', 'dark'); + } else { + document.documentElement.setAttribute('data-bs-theme', theme); + } + }; + + setTheme(getPreferredTheme()); + + const showActiveTheme = (theme, focus = false) => { + const themeSwitcher = document.querySelector('#toggle-theme'); + + if (!themeSwitcher) { + return; + } + + const themeSwitcherText = document.querySelector('#toggle-theme-text'); + const activeThemeIcon = document.querySelector('.theme-icon-active use'); + const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`); + const svgOfActiveBtn = btnToActive.querySelector('svg use').getAttribute('href'); + + // Añade las siguientes líneas para mostrar el icono de verificación en el botón activo. + const activeCheckIcon = btnToActive.querySelector('.check'); + activeCheckIcon.classList.remove('d-none'); + + document.querySelectorAll('[data-bs-theme-value]').forEach((element) => { + const checkIcon = element.querySelector('.check'); + + if (element === btnToActive) { + checkIcon.classList.remove('d-none'); + } else { + checkIcon.classList.add('d-none'); + } + + element.classList.remove('active'); + element.setAttribute('aria-pressed', 'false'); + }); + + btnToActive.classList.add('active'); + btnToActive.setAttribute('aria-pressed', 'true'); + activeThemeIcon.setAttribute('href', svgOfActiveBtn); + const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`; + themeSwitcher.setAttribute('aria-label', themeSwitcherLabel); + + if (focus) { + themeSwitcher.focus(); + } + }; + + const sendTheme = (theme) => { + const languageCode = document.documentElement.getAttribute('lang').substring(0, 2); + const baseUrl = '/api/theme'; + const url = `/${languageCode}${baseUrl}`; + + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), + }, + body: JSON.stringify({ theme }), + }) + .then((response) => { + if (!response.ok) { + console.error('Failed to update theme'); + } }) - }) - }) -})() + .catch((error) => { + console.error('Error updating theme:', error); + }); + }; + + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + const storedTheme = getStoredTheme(); + + if (storedTheme !== 'light' && storedTheme !== 'dark') { + setTheme(getPreferredTheme()); + } + }); + + window.addEventListener('DOMContentLoaded', () => { + showActiveTheme(getPreferredTheme()); + + document.querySelectorAll('[data-bs-theme-value]').forEach((toggle) => { + toggle.addEventListener('click', () => { + const theme = toggle.getAttribute('data-bs-theme-value'); + + setStoredTheme(theme); + setTheme(theme); + showActiveTheme(theme, true); + sendTheme(theme); + }); + }); + }); + })(); diff --git a/src/Framework/resource/layout/component/toggle_theme.php b/src/Framework/resource/layout/component/toggle_theme.php index 5d5e721..92c3008 100644 --- a/src/Framework/resource/layout/component/toggle_theme.php +++ b/src/Framework/resource/layout/component/toggle_theme.php @@ -7,6 +7,7 @@ use PHPForge\Component\Item; use PHPForge\Html\Span; use sjaakp\icon\Icon; +use yii\helpers\Url; use yii\web\View; /** @@ -14,6 +15,9 @@ */ ToggleThemeAsset::register($this); +/** @phpstan-ignore-next-line */ +$link = $this->context->action->getUniqueId() === 'site/index' ? Url::home() : Url::current([]); + echo Dropdown::widget() ->container(true) ->containerClass('btn-group dropup bd-mode-toggle ms-3') @@ -28,7 +32,7 @@ ['class' => 'check ms-auto d-none', 'width' => '1em', 'height' => '1em'], ), ) - ->link('#') + ->link($link) ->linkAttributes(['aria-pressed' => 'false', 'data-bs-theme-value' => 'light']) ->linkClass('dropdown-item d-flex align-items-center'), Item::create() @@ -41,7 +45,7 @@ ['class' => 'check ms-auto d-none', 'width' => '1em', 'height' => '1em'], ), ) - ->link('#') + ->link($link) ->linkAttributes(['aria-pressed' => 'false', 'data-bs-theme-value' => 'dark']) ->linkClass('dropdown-item d-flex align-items-center'), Item::create() @@ -54,7 +58,7 @@ ['class' => 'check ms-auto d-none', 'width' => '1em', 'height' => '1em'], ), ) - ->link('#') + ->link($link) ->linkAttributes(['aria-pressed' => 'false', 'data-bs-theme-value' => 'auto']) ->linkClass('dropdown-item d-flex align-items-center'), ) diff --git a/src/UseCase/Api/ApiController.php b/src/UseCase/Api/ApiController.php new file mode 100644 index 0000000..b4cd329 --- /dev/null +++ b/src/UseCase/Api/ApiController.php @@ -0,0 +1,42 @@ + [ + 'class' => ThemeAction::class, + ], + ]; + } + + public function beforeAction($action): bool + { + if (YII_ENV == 'test' && $action->id == 'theme') { + $this->enableCsrfValidation = false; + } + + return parent::beforeAction($action); + } + + public function behaviors(): array + { + return [ + 'verbs' => [ + 'class' => VerbFilter::class, + 'actions' => [ + 'theme' => ['POST'], + ], + ], + ]; + } +} diff --git a/src/UseCase/Api/Theme/ThemeAction.php b/src/UseCase/Api/Theme/ThemeAction.php new file mode 100644 index 0000000..684bed8 --- /dev/null +++ b/src/UseCase/Api/Theme/ThemeAction.php @@ -0,0 +1,46 @@ +controller->response instanceof Response && $this->controller->request instanceof Request) { + $this->controller->response->format = Response::FORMAT_JSON; + + $this->controller->response->format = Response::FORMAT_JSON; + + $theme = Json::decode($this->controller->request->getRawBody()); + + if (isset($theme['theme'])) { + $cookie = $this->controller->request->cookies->get('theme'); + + if ($cookie !== null) { + $cookie->value = $theme['theme']; + $this->controller->response->cookies->add($cookie); + } else { + $cookie = new Cookie( + [ + 'name' => 'theme', + 'value' => $theme['theme'], + 'expire' => time() + 86400, // 1 day + ], + ); + + $this->controller->response->cookies->add($cookie); + } + } + } + + return $theme ?? []; + } +} diff --git a/src/UseCase/Contact/ContactController.php b/src/UseCase/Contact/ContactController.php index f8dcd20..8d54377 100644 --- a/src/UseCase/Contact/ContactController.php +++ b/src/UseCase/Contact/ContactController.php @@ -20,7 +20,7 @@ public function actions(): array [ 'captcha' => [ 'class' => CaptchaAction::class, - 'fixedVerifyCode' => (YII_ENV === 'tests') ? 'testme' : null, + 'fixedVerifyCode' => (YII_ENV === 'test') ? 'testme' : null, ], ], parent::actions(), @@ -43,8 +43,7 @@ public function actionIndex(): Response|string { if ( $this->request instanceof Request && - $this->contactForm->load($this->request->post()) && - $this->contactForm->validate() + $this->contactForm->load($this->request->post()) && $this->contactForm->validate() ) { if ($this->contactForm->sendContact($this->mailer, $this->module->params)) { $this->session->setFlash('contactFormSubmitted'); diff --git a/tests/Functional.suite.yml b/tests/Functional.suite.yml index 3e33c70..36becf6 100644 --- a/tests/Functional.suite.yml +++ b/tests/Functional.suite.yml @@ -10,4 +10,10 @@ modules: - Filesystem - Yii2 - Asserts + - REST: + url: http://localhost:8080 + depends: Yii2 + part: Json + headers: + Content-Type: application/json step_decorators: ~ diff --git a/tests/Functional/ThemeActionCest.php b/tests/Functional/ThemeActionCest.php new file mode 100644 index 0000000..434eb98 --- /dev/null +++ b/tests/Functional/ThemeActionCest.php @@ -0,0 +1,36 @@ + 'dark']; + + $I->sendPost('/api/theme', json_encode($theme)); + $I->seeResponseCodeIsSuccessful(); + $I->seeResponseContainsJson($theme); + $I->seeCookie('theme'); + } + + public function testThemeWithCookie(FunctionalTester $I): void + { + $theme = ['theme' => 'dark']; + + $I->sendPost('/api/theme', json_encode($theme)); + $I->seeResponseCodeIsSuccessful(); + $I->seeResponseContainsJson($theme); + $I->seeCookie('theme'); + + $I->sendPost('/api/theme', json_encode($theme)); + $I->seeResponseCodeIsSuccessful(); + $I->seeResponseContainsJson($theme); + $I->seeCookie('theme'); + } +} diff --git a/tests/Support/ApiTester.php b/tests/Support/ApiTester.php new file mode 100644 index 0000000..bdf017b --- /dev/null +++ b/tests/Support/ApiTester.php @@ -0,0 +1,29 @@ +