diff --git a/screenshots/login.png b/.github/screenshots/login.png similarity index 100% rename from screenshots/login.png rename to .github/screenshots/login.png diff --git a/screenshots/permissions.png b/.github/screenshots/permissions.png similarity index 100% rename from screenshots/permissions.png rename to .github/screenshots/permissions.png diff --git a/screenshots/users.png b/.github/screenshots/users.png similarity index 100% rename from screenshots/users.png rename to .github/screenshots/users.png diff --git a/sponsors/nextgi.png b/.github/sponsors/nextgi.png similarity index 100% rename from sponsors/nextgi.png rename to .github/sponsors/nextgi.png diff --git a/sponsors/usor.png b/.github/sponsors/usor.png similarity index 100% rename from sponsors/usor.png rename to .github/sponsors/usor.png diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index 0275b540b..3ad8f5ce4 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - php_versions: ['7.1', '7.2', '7.3', '7.4'] + php_versions: ['7.2', '7.3', '7.4'] runs-on: ubuntu-latest name: PHPUnit - PHP ${{ matrix.php_versions }} - MySQL @@ -38,11 +38,11 @@ jobs: php-version: ${{ matrix.php_versions }} extensions: mbstring, dom, fileinfo, gd, memcached, redis, pdo_sqlite coverage: xdebug - tools: pecl, composer:v1 + tools: pecl, composer - uses: actions/setup-node@v2 with: - node-version: 10 + node-version: 14 - name: Setup Redis-server uses: supercharge/redis-github-action@1.1.0 @@ -83,7 +83,7 @@ jobs: run: php -r "copy('app/sprinkles.example.json', 'app/sprinkles.json');" - name: Install Dependencies - run: composer install --prefer-dist --no-progress --no-suggest + run: composer install --prefer-dist --no-progress - name: Bakery Debug run: php bakery debug @@ -109,7 +109,7 @@ jobs: strategy: fail-fast: false matrix: - php_versions: ['7.1', '7.2', '7.3', '7.4'] + php_versions: ['7.2', '7.3', '7.4'] runs-on: ubuntu-latest name: PHPUnit - PHP ${{ matrix.php_versions }} - SQLite @@ -129,11 +129,11 @@ jobs: php-version: ${{ matrix.php_versions }} extensions: mbstring, dom, fileinfo, gd, memcached, redis, pdo_sqlite coverage: xdebug - tools: pecl, composer:v1 + tools: pecl, composer - uses: actions/setup-node@v2 with: - node-version: 10 + node-version: 14 - name: Setup Redis-server uses: supercharge/redis-github-action@1.1.0 @@ -147,7 +147,7 @@ jobs: run: php -r "copy('app/sprinkles.example.json', 'app/sprinkles.json');" - name: Install Dependencies - run: composer install --prefer-dist --no-progress --no-suggest + run: composer install --prefer-dist --no-progress - name: Create SQLite Database run: | @@ -178,7 +178,7 @@ jobs: strategy: fail-fast: false matrix: - php_versions: ['7.1', '7.2', '7.3', '7.4'] + php_versions: ['7.2', '7.3', '7.4'] runs-on: ubuntu-latest name: PHPUnit - PHP ${{ matrix.php_versions }} - PostgreSQL @@ -202,7 +202,7 @@ jobs: php-version: ${{ matrix.php_versions }} extensions: mbstring, dom, fileinfo, gd, memcached, redis, pdo_sqlite, pdo_pgsql coverage: xdebug - tools: pecl, composer:v1 + tools: pecl, composer - name: Setup PostgreSQL uses: harmon758/postgresql-action@v1 @@ -213,7 +213,7 @@ jobs: - uses: actions/setup-node@v2 with: - node-version: 10 + node-version: 14 - name: Setup Redis-server uses: supercharge/redis-github-action@1.1.0 @@ -227,7 +227,7 @@ jobs: run: php -r "copy('app/sprinkles.example.json', 'app/sprinkles.json');" - name: Install Dependencies - run: composer install --prefer-dist --no-progress --no-suggest + run: composer install --prefer-dist --no-progress - name: Bakery Debug run: php bakery debug @@ -253,7 +253,7 @@ jobs: strategy: fail-fast: false matrix: - php_versions: ['7.1', '7.2', '7.3', '7.4'] + php_versions: ['7.2', '7.3', '7.4'] runs-on: windows-latest name: PHPUnit - PHP ${{ matrix.php_versions }} - Windows @@ -273,17 +273,17 @@ jobs: php-version: ${{ matrix.php_versions }} extensions: mbstring, dom, fileinfo, gd, pdo, sqlite, pdo_sqlite coverage: xdebug - tools: pecl, composer:v1 + tools: pecl, composer - uses: actions/setup-node@v2 with: - node-version: 10 + node-version: 14 - name: Copy .env run: php -r "copy('app/sprinkles.example.json', 'app/sprinkles.json');" - name: Install Dependencies - run: composer install --prefer-dist --no-progress --no-suggest + run: composer install --prefer-dist --no-progress - name: Create SQLite Database run: | @@ -314,7 +314,7 @@ jobs: fail-fast: false matrix: php_versions: ['7.4'] - node_versions: ['10', '12', '14'] + node_versions: ['12.17.0', '14', '15'] os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} @@ -329,7 +329,7 @@ jobs: php-version: ${{ matrix.php_versions }} extensions: mbstring, dom, fileinfo, gd coverage: xdebug - tools: pecl, composer:v1 + tools: pecl, composer - uses: actions/setup-node@v2 with: @@ -339,7 +339,7 @@ jobs: run: php -r "copy('app/sprinkles.example.json', 'app/sprinkles.json');" - name: Install Dependencies - run: composer install --prefer-dist --no-progress --no-suggest + run: composer install --prefer-dist --no-progress - name: Execute build run: php bakery build-assets \ No newline at end of file diff --git a/.lando.dist.yml b/.lando.dist.yml new file mode 100644 index 000000000..9a9e50626 --- /dev/null +++ b/.lando.dist.yml @@ -0,0 +1,69 @@ +# Lando is a development tool, do not use it hosting a production website + +name: userfrosting +recipe: lamp +config: + webroot: ./public + php: '7.4' + composer_version: '2-latest' + database: mariadb + xdebug: true + +services: + appserver: + build_as_root: + - apt-get update -y + - apt-get install -my wget gnupg + - a2enmod headers + # Patch to bring NodeJS into app server container + # https://docs.lando.dev/guides/installing-node-in-your-lando-php-service.html + - curl -sL https://deb.nodesource.com/setup_14.x | bash - + - apt-get install -y nodejs + overrides: + environment: + PHP_IDE_CONFIG: "serverName=userfrosting.lndo.site" + MAIL_MAILER: "mail" + DB_DRIVER: "mysql" + DB_HOST: "database" + DB_PORT: "3306" + DB_NAME: "lamp" + DB_USER: "lamp" + DB_PASSWORD: "lamp" + ssl: true + + # Redis cache + cache: + type: redis + + # phpMyAdmin + pma: + type: phpmyadmin + hosts: + - database + + # MailHog + mh: + type: mailhog:v1.0.0 + portforward: false + hogfrom: + # Set UF mailer config to 'mail' complete integration + - appserver + +proxy: + pma: + - pma.userfrosting.lndo.site + mh: + - mh.userfrosting.lndo.site + +tooling: + phpunit: + service: appserver + description: "Run PHP Unit tests" + cmd: app/vendor/bin/phpunit + redis-cli: + service: cache + description: "Redis cache CLI" + bakery: + service: appserver + description: "UserFrosting CLI" + cmd: php bakery \ No newline at end of file diff --git a/.php_cs b/.php_cs.dist similarity index 98% rename from .php_cs rename to .php_cs.dist index a12512f91..7c15cbfe2 100755 --- a/.php_cs +++ b/.php_cs.dist @@ -103,10 +103,11 @@ $rules = [ ]; $finder = PhpCsFixer\Finder::create() - ->exclude([ - 'vendor', - ]) - ->in([__DIR__ . '/app', __DIR__ . '/public']); + ->in([ + __DIR__ . '/app/sprinkles', + __DIR__ . '/app/system', + __DIR__ . '/public' + ]); return PhpCsFixer\Config::create() ->setRules($rules) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a312cb33..116c9a09e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,53 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [v4.5.0] + +### Changed Requirements +- Drop PHP 7.1 support. PHP 7.4 is now recommended. +- Raised NodeJS version requirement from `>=10.12.0` to `^12.17.0 || >=14.0.0` ([#1138]). +- Raised NPM version requirement from `>=6.0.0` to `>=6.14.4` ([#1138]). + +### Changed Composer Dependencies +- Updated `wikimedia/composer-merge-plugin` from `^1.4.0` to `^2.1.0` ([#1117]). + +### Added +- Composer 2 support ([#1117]). +- [Lando](https://lando.dev) support. +- Added more SMTP options in env and setup:smtp bakery command ([#1077]), +- Added new `MAIL_MAILER` environment variable to set mailer type. +- Added "Native mail" to `setup:mail` bakery command. + +### Changed +- Implement findInt ([#1117]). +- Replace `getenv()` with `env()` ([#1121]). +- Replaced `UserFrosting\Sprinkle\Core\Bakery\Helper\NodeVersionCheck` with new `UserFrosting\Sprinkle\Core\Util\VersionValidator` class. +- Bakery command `setup:smtp` renamed to `setup:mail`. The old command is still available as an alias for backward compatibility. +- Changed `.php_cs` to `.php_cs.dist`. +- Changed `phpunit.xml` to `phpunit.xml.dist`. + +### Fixed +- Replaced AdminLTE credit in default footer (old link was dead). +- Issue with path slashes on Windows ([#1133]). + +### Removed +- Removed deprecated `UserFrosting\System\Bakery\Migration` (deprecated in 4.2.0). +- Removed deprecated `UserFrosting\Tests\DatabaseTransactions` (deprecated in 4.2.0). +- Removed deprecated `UserFrosting\Sprinkle\Core\Tests\ControllerTestCase` (deprecated in 4.2.2). +- Removed deprecated `UserFrosting\Sprinkle\Core\Model\UFModel` (deprecated in 4.1). +- Removed deprecated `UserFrosting\Sprinkle\Core\Sprunje\Sprunje::getResults` (deprecated in 4.1.7). +- Removed deprecated `UserFrosting\Sprinkle\Account\Database\Models\User::exists` (deprecated in 4.1.7). +- Removed deprecated `UserFrosting\Sprinkle\Core\Database\Models\Model::export` (deprecated in 4.1.8). +- Removed deprecated `UserFrosting\Sprinkle\Core\Database\Models\Model::queryBuilder` (deprecated in 4.1.8). +- Removed deprecated `UserFrosting\Sprinkle\Core\Database\Relations\Concerns\Unique::withLimit` (deprecated in 4.1.7). +- Removed deprecated `UserFrosting\Sprinkle\Core\Database\Relations\Concerns\Unique::withOffset` (deprecated in 4.1.7). +- Removed deprecated `UserFrosting\Sprinkle\Core\Error\RendererWhoopsRenderer::getResourcesPath`. +- Removed deprecated `UserFrosting\Sprinkle\Core\Error\RendererWhoopsRenderer::setResourcesPath`. +- Removed deprecated Handlebar `ifCond` (Deprecated in 4.1). +- Removed migration seed. +- Removed support for migration with non static `$dependencies` properties. +- Removed support for deprecated `determineRedirectOnLogin` service (deprecated in 4.1.10). + ## [v4.4.5] ### Changed @@ -85,7 +132,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Improve ordering by activity date ([#1061] & [#1062]; Thanks @ktecho!) - Updated Vagrant config and documentation - Fixed a bug where `withTrashed` in `findUnique` was not available when `SoftDeletes` trait is not included in a model. -- CSRF global middleware is not loaded anymore if in a CLI envrionement. This will avoid sessions to be created for bakery and tests by default. +- CSRF global middleware is not loaded anymore if in a CLI environment. This will avoid sessions to be created for bakery and tests by default. - Browserified node modules not being correctly loaded. - Browserified node modules potentially colliding with real entrypoints. @@ -994,6 +1041,12 @@ See [http://learn.userfrosting.com/upgrading/40-to-41](Upgrading 4.0.x to 4.1.x [#1114]: https://github.com/userfrosting/UserFrosting/pull/1114 [#1126]: https://github.com/userfrosting/UserFrosting/pull/1126 [#1128]: https://github.com/userfrosting/UserFrosting/pull/1128 +[#1117]: https://github.com/userfrosting/UserFrosting/issues/1117 +[#1138]: https://github.com/userfrosting/UserFrosting/pull/1138 +[#1124]: https://github.com/userfrosting/UserFrosting/pull/1124 +[#1121]: https://github.com/userfrosting/UserFrosting/pull/1121 +[#1077]: https://github.com/userfrosting/UserFrosting/pull/1077 +[#1133]: https://github.com/userfrosting/UserFrosting/issues/1133 [v4.2.0]: https://github.com/userfrosting/UserFrosting/compare/v4.1.22...v4.2.0 [v4.2.1]: https://github.com/userfrosting/UserFrosting/compare/v4.2.0...v.4.2.1 @@ -1009,3 +1062,4 @@ See [http://learn.userfrosting.com/upgrading/40-to-41](Upgrading 4.0.x to 4.1.x [v4.4.3]: https://github.com/userfrosting/UserFrosting/compare/v4.4.2...v4.4.3 [v4.4.4]: https://github.com/userfrosting/UserFrosting/compare/v4.4.3...v4.4.4 [v4.4.5]: https://github.com/userfrosting/UserFrosting/compare/v4.4.4...v4.4.5 +[v4.5.0]: https://github.com/userfrosting/UserFrosting/compare/v4.4.5...v4.5.0 diff --git a/README.md b/README.md index b50f11f48..c0166ab96 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# UserFrosting 4.4 +# UserFrosting 4.5 [![Latest Version](https://img.shields.io/github/release/userfrosting/UserFrosting.svg)](https://github.com/userfrosting/UserFrosting/releases) ![PHP Version](https://img.shields.io/packagist/php-v/userfrosting/userfrosting.svg?color=brightgreen) @@ -11,8 +11,8 @@ | Branch | Version | Build | Coverage | Style | | ------ |:-------:|:-----:|:--------:|:-----:| | [master] | ![](https://img.shields.io/github/release/userfrosting/userfrosting.svg?color=success&label=Version) | [![](https://github.com/userfrosting/userfrosting/workflows/Build/badge.svg?branch=master)][UF-Build] | [![](https://codecov.io/gh/userfrosting/userfrosting/branch/master/graph/badge.svg)][UF-Codecov] | [![][style-master]][style] | -| [hotfix] | ![](https://img.shields.io/badge/Version-v4.4.x-yellow.svg) | [![](https://github.com/userfrosting/userfrosting/workflows/Build/badge.svg?branch=hotfix)][UF-Build] | [![](https://codecov.io/gh/userfrosting/userfrosting/branch/hotfix/graph/badge.svg)][UF-Codecov] | [![][style-hotfix]][style] | -| [develop] | ![](https://img.shields.io/badge/Version-v4.5.x-orange.svg) | [![](https://github.com/userfrosting/userfrosting/workflows/Build/badge.svg?branch=develop)][UF-Build] | [![](https://codecov.io/gh/userfrosting/userfrosting/branch/develop/graph/badge.svg)][UF-Codecov] | [![][style-develop]][style] | +| [hotfix] | ![](https://img.shields.io/badge/Version-v4.5.x-yellow.svg) | [![](https://github.com/userfrosting/userfrosting/workflows/Build/badge.svg?branch=hotfix)][UF-Build] | [![](https://codecov.io/gh/userfrosting/userfrosting/branch/hotfix/graph/badge.svg)][UF-Codecov] | [![][style-hotfix]][style] | +| [develop] | ![](https://img.shields.io/badge/Version-v4.6.x-orange.svg) | [![](https://github.com/userfrosting/userfrosting/workflows/Build/badge.svg?branch=develop)][UF-Build] | [![](https://codecov.io/gh/userfrosting/userfrosting/branch/develop/graph/badge.svg)][UF-Codecov] | [![][style-develop]][style] | [master]: https://github.com/userfrosting/UserFrosting @@ -38,13 +38,13 @@ UserFrosting is a secure, modern user management system written in PHP and built ## Features ### User login screen -![User login script](screenshots/login.png) +![User login script](.github/screenshots/login.png) ### User management page -![PHP user management script](screenshots/users.png) +![PHP user management script](.github/screenshots/users.png) ### Permissions management page -![UserFrosting permissions management](screenshots/permissions.png) +![UserFrosting permissions management](.github/screenshots/permissions.png) ## [Demo](https://demo.userfrosting.com) @@ -93,7 +93,7 @@ Louis's a civil engineer in Montréal, Québec who also has a passion for coding ### Jordan Mele -Jordan's an Australian PHP Developer at [4mation](https://www.4mation.com.au) in Surry Hills, NSW. His passion is creating simple yet intuitive software-based solutions for problems that would otherwise be tedious and overcomplicated to address, while keeping the user in control. +Jordan's an Australian Software Engineer at [Canva](https://canva.com). His passion is creating simple yet intuitive software-based solutions for problems that would otherwise be tedious and/or difficult to solve, while keeping the user in control. ### Sarah Baghdadi @@ -139,5 +139,5 @@ Backers help us continue to develop UserFrosting by pledging a regular monthly c Support this project by becoming a sponsor. Sponsors have contributed a total of $500 or more to UserFrosting (either as an ongoing backer or one-time contributions). Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/userfrosting#sponsor)] -[![USOR Games](sponsors/usor.png)](https://usorgames.com) -[![Next Generation Internet](sponsors/nextgi.png)](https://nextgi.com) +[![USOR Games](.github/sponsors/usor.png)](https://usorgames.com) +[![Next Generation Internet](.github/sponsors/nextgi.png)](https://nextgi.com) diff --git a/app/.env.example b/app/.env.example index 3d22da1bc..4d04f5390 100644 --- a/app/.env.example +++ b/app/.env.example @@ -8,6 +8,10 @@ DB_PORT="3306" DB_NAME="userfrosting" DB_USER="userfrosting" DB_PASSWORD="password" +MAIL_MAILER="smtp" # Set to one of 'smtp', 'mail', 'qmail', 'sendmail' SMTP_HOST="host.example.com" SMTP_USER="relay@example.com" SMTP_PASSWORD="password" +SMTP_PORT="587" +SMTP_AUTH="true" +SMTP_SECURE="tls" diff --git a/app/defines.php b/app/defines.php index 778df9d38..a98f329ee 100755 --- a/app/defines.php +++ b/app/defines.php @@ -11,12 +11,12 @@ namespace UserFrosting; // Some standard defines -define('UserFrosting\VERSION', '4.4.5'); +define('UserFrosting\VERSION', '4.5.0'); define('UserFrosting\DS', '/'); -define('UserFrosting\PHP_MIN_VERSION', '7.1'); -define('UserFrosting\PHP_RECOMMENDED_VERSION', '7.3'); -define('UserFrosting\NODE_MIN_VERSION', 'v10.12.0'); -define('UserFrosting\NPM_MIN_VERSION', '6.0.0'); +define('UserFrosting\PHP_MIN_VERSION', '^7.2'); +define('UserFrosting\PHP_RECOMMENDED_VERSION', '^7.4'); +define('UserFrosting\NODE_MIN_VERSION', '^12.17.0 || >=14.0.0'); +define('UserFrosting\NPM_MIN_VERSION', '>=6.14.4'); // Directories and Paths diff --git a/app/sprinkles/account/composer.json b/app/sprinkles/account/composer.json index 8f57eda1a..9df9a6c08 100644 --- a/app/sprinkles/account/composer.json +++ b/app/sprinkles/account/composer.json @@ -17,7 +17,7 @@ { "name": "Jordan Mele", "email": "SiliconSoldier@outlook.com.au", - "homepage": "https://blog.djmm.me" + "homepage": "https://djmm.me" } ], "require": { diff --git a/app/sprinkles/account/src/Account/Registration.php b/app/sprinkles/account/src/Account/Registration.php index 7d5968194..99ced2de4 100644 --- a/app/sprinkles/account/src/Account/Registration.php +++ b/app/sprinkles/account/src/Account/Registration.php @@ -156,7 +156,7 @@ public function validate() // Make sure all required fields are defined foreach ($this->requiredProperties as $property) { if (!isset($this->userdata[$property])) { - $e = new HttpException("Account can't be registrated as '$property' is required to create a new user."); + $e = new HttpException("Account can't be registered as '$property' is required to create a new user."); $e->addUserMessage('USERNAME.IN_USE'); throw $e; diff --git a/app/sprinkles/account/src/Authenticate/Authenticator.php b/app/sprinkles/account/src/Authenticate/Authenticator.php index f4593cfe1..2180c5532 100644 --- a/app/sprinkles/account/src/Authenticate/Authenticator.php +++ b/app/sprinkles/account/src/Authenticate/Authenticator.php @@ -359,7 +359,7 @@ protected function loginRememberedUser() // so we store the fact that the user was logged in via RememberMe (instead of login form) $this->viaRemember = true; } else { - // If $rememberMe->login() was not successfull, check if the token was invalid as well. This means the cookie was stolen. + // If $rememberMe->login() was not successful, check if the token was invalid as well. This means the cookie was stolen. if ($loginResult->hasPossibleManipulation()) { throw new AuthCompromisedException(); } diff --git a/app/sprinkles/account/src/Authorize/AccessConditionExpression.php b/app/sprinkles/account/src/Authorize/AccessConditionExpression.php index 9bfbbec77..3c35136a6 100644 --- a/app/sprinkles/account/src/Authorize/AccessConditionExpression.php +++ b/app/sprinkles/account/src/Authorize/AccessConditionExpression.php @@ -98,7 +98,7 @@ public function evaluateCondition($condition, $params) // Set the reserved `self` parameters. // This replaces any values of `self` specified in the arguments, thus preventing them from being overridden in malicious user input. // (For example, from an unfiltered request body). - $params['self'] = $this->user->export(); + $params['self'] = $this->user->toArray(); $this->nodeVisitor->setParams($params); diff --git a/app/sprinkles/account/src/Bakery/CreateAdminUser.php b/app/sprinkles/account/src/Bakery/CreateAdminUser.php index d3166e0f3..dbc53be61 100644 --- a/app/sprinkles/account/src/Bakery/CreateAdminUser.php +++ b/app/sprinkles/account/src/Bakery/CreateAdminUser.php @@ -272,7 +272,7 @@ protected function askLastName($lastName = '') /** * Validate the last name entered is valid. * - * @param string $lastName The lastname + * @param string $lastName The last name * * @return bool Input is valid or not */ diff --git a/app/sprinkles/account/src/Controller/AccountController.php b/app/sprinkles/account/src/Controller/AccountController.php index 92b7b3757..52e30eeb2 100644 --- a/app/sprinkles/account/src/Controller/AccountController.php +++ b/app/sprinkles/account/src/Controller/AccountController.php @@ -418,7 +418,7 @@ public function login(Request $request, Response $response, $args) throw $e; } - $ms->addMessageTranslated('success', 'WELCOME', $currentUser->export()); + $ms->addMessageTranslated('success', 'WELCOME', $currentUser->toArray()); // Set redirect, if relevant $redirectOnLogin = $this->ci->get('redirect.onLogin'); @@ -680,7 +680,7 @@ public function pageSetPassword(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function pageSettings(Request $request, Response $response, $args) { @@ -920,7 +920,7 @@ public function register(Request $request, Response $response, $args) } // Security measure: do not allow registering new users until the master account has been created. - if (!$classMapper->getClassMapping('user')::find($config['reserved_user_ids.master'])) { + if (!$classMapper->getClassMapping('user')::findInt($config['reserved_user_ids.master'])) { $ms->addMessageTranslated('danger', 'ACCOUNT.MASTER_NOT_EXISTS'); return $response->withJson([], 403); @@ -1191,7 +1191,7 @@ public function setPassword(Request $request, Response $response, $args) $user = $passwordReset->user; $authenticator->login($user); - $ms->addMessageTranslated('success', 'WELCOME', $user->export()); + $ms->addMessageTranslated('success', 'WELCOME', $user->toArray()); return $response->withJson([], 200); } diff --git a/app/sprinkles/account/src/Database/Migrations/v420/AddingForeignKeys.php b/app/sprinkles/account/src/Database/Migrations/v420/AddingForeignKeys.php index 41ab504b8..89467e290 100644 --- a/app/sprinkles/account/src/Database/Migrations/v420/AddingForeignKeys.php +++ b/app/sprinkles/account/src/Database/Migrations/v420/AddingForeignKeys.php @@ -73,7 +73,7 @@ public function up() public function down() { /* - * sqlite can't drop foreign key wihout dropping the entire table + * sqlite can't drop foreign key without dropping the entire table * since Laravel 5.7. Skip drop if an sqlite connection is detected * @see https://github.com/laravel/framework/issues/25475 */ diff --git a/app/sprinkles/account/src/Database/Models/User.php b/app/sprinkles/account/src/Database/Models/User.php index e4dd6493e..432a404b4 100644 --- a/app/sprinkles/account/src/Database/Models/User.php +++ b/app/sprinkles/account/src/Database/Models/User.php @@ -216,22 +216,6 @@ public function delete($hardDelete = false) return $result; } - /** - * Determines whether a user exists, including checking soft-deleted records. - * - * @deprecated since 4.1.7 This method conflicts with and overrides the Builder::exists() method. Use Model::findUnique instead. - * - * @param mixed $value - * @param string $identifier - * @param bool $checkDeleted set to true to include soft-deleted records - * - * @return User|null - */ - public static function exists($value, $identifier = 'user_name', $checkDeleted = true) - { - return static::findUnique($value, $identifier, $checkDeleted); - } - /** * Return a cache instance specific to that user. * diff --git a/app/sprinkles/account/src/I18n/SiteLocale.php b/app/sprinkles/account/src/I18n/SiteLocale.php index 6eaeb1b40..e58f2c91b 100644 --- a/app/sprinkles/account/src/I18n/SiteLocale.php +++ b/app/sprinkles/account/src/I18n/SiteLocale.php @@ -32,7 +32,7 @@ public function getLocaleIndentifier(): string /** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface */ $currentUser = $this->ci->currentUser; - // If user is note loged in, get original translator + // If user is note logged in, get original translator try { if (!$authenticator->check()) { return parent::getLocaleIndentifier(); diff --git a/app/sprinkles/account/src/ServicesProvider/ServicesProvider.php b/app/sprinkles/account/src/ServicesProvider/ServicesProvider.php index 27c8f9f7d..47ed47eb7 100644 --- a/app/sprinkles/account/src/ServicesProvider/ServicesProvider.php +++ b/app/sprinkles/account/src/ServicesProvider/ServicesProvider.php @@ -49,7 +49,7 @@ public function register(ContainerInterface $container) */ $container->extend('assets', function ($assets, $c) { - // Force load the current user to add it's theme assets ressources + // Force load the current user to add it's theme assets resources $currentUser = $c->currentUser; return $assets; @@ -263,7 +263,7 @@ public function register(ContainerInterface $container) * @return bool true if the user is in the group, false otherwise. */ 'in_group' => function ($user_id, $group_id) { - $user = User::find($user_id); + $user = User::findInt($user_id); return $user->group_id == $group_id; }, @@ -317,7 +317,7 @@ public function register(ContainerInterface $container) $authenticator = $c->authenticator; $currentUser = $authenticator->user(); - // Add user theme sprinkles ressources + // Add user theme sprinkles resources if ($authenticator->check() && $currentUser->theme) { $c->sprinkleManager->addSprinkleResources($currentUser->theme); } @@ -374,13 +374,6 @@ public function register(ContainerInterface $container) * @return \Psr\Http\Message\ResponseInterface */ return function (Request $request, Response $response, array $args) use ($c) { - // Backwards compatibility for the deprecated determineRedirectOnLogin service - if ($c->has('determineRedirectOnLogin')) { - $determineRedirectOnLogin = $c->determineRedirectOnLogin; - - return $determineRedirectOnLogin($response)->withStatus(200); - } - /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */ $authorizer = $c->authorizer; diff --git a/app/sprinkles/account/tests/Integration/AuthenticatorTest.php b/app/sprinkles/account/tests/Integration/AuthenticatorTest.php index 86aedfac1..c9ebd9664 100644 --- a/app/sprinkles/account/tests/Integration/AuthenticatorTest.php +++ b/app/sprinkles/account/tests/Integration/AuthenticatorTest.php @@ -216,7 +216,7 @@ public function testLoginWithRememberMe(Authenticator $authenticator) $this->assertNull($this->ci->session[$key]); $this->assertNotSame($testUser->id, $this->ci->session[$key]); - // Go througt the loginRememberedUser process + // Go through the loginRememberedUser process // First, we'll simulate a page refresh by creating a new authenticator $authenticator = $this->getAuthenticator(); $user = $authenticator->user(); @@ -344,7 +344,7 @@ public function testAttempt_withFlagVerifiedFalseNoEmailVerification(Authenticat // Force email verification to false $this->ci->config['site.registration.require_email_verification'] = false; - // Forcing config requires to recreate the authentificator + // Forcing config requires to recreate the authenticator $authenticator = $this->getAuthenticator(); $password = 'FooBar'; diff --git a/app/sprinkles/account/tests/Integration/Controller/AccountControllerTest.php b/app/sprinkles/account/tests/Integration/Controller/AccountControllerTest.php index 8d75655e0..296378125 100644 --- a/app/sprinkles/account/tests/Integration/Controller/AccountControllerTest.php +++ b/app/sprinkles/account/tests/Integration/Controller/AccountControllerTest.php @@ -136,7 +136,7 @@ public function testRegister() // Recreate controller to use fake config $controller = $this->getController(); - // Perfrom common test code + // Perform common test code $this->performActualRegisterTest($controller); } @@ -166,7 +166,7 @@ public function testRegisterWithNoEmailVerification() // Recreate controller to use fake config $controller = $this->getController(); - // Perfrom common test code + // Perform common test code $this->performActualRegisterTest($controller); } @@ -175,7 +175,7 @@ public function testRegisterWithNoEmailVerification() */ protected function performActualRegisterTest(AccountController $controller) { - // Genereate a captcha for next request. + // Generate a captcha for next request. $captcha = new Captcha($this->ci->session, $this->ci->config['session.keys.captcha']); $captcha->generateRandomCode(); @@ -494,7 +494,7 @@ public function testlogin(AccountController $controller) $result = $controller->login($request, $this->getResponse(), []); $this->assertInstanceOf(\Psr\Http\Message\ResponseInterface::class, $result); - // Can't assert the status code or data, as this can be overwrited by sprinkles + // Can't assert the status code or data, as this can be overwritten by sprinkles // Test message $ms = $this->ci->alerts; @@ -531,7 +531,7 @@ public function testloginWithEmail(AccountController $controller) $result = $controller->login($request, $this->getResponse(), []); $this->assertInstanceOf(\Psr\Http\Message\ResponseInterface::class, $result); - // Can't assert the status code or data, as this can be overwrited by sprinkles + // Can't assert the status code or data, as this can be overwritten by sprinkles // Test message $ms = $this->ci->alerts; @@ -676,7 +676,7 @@ public function testloginThrottlerDoesntCountSuccessfulLogins() $result = $controller->login($request, $this->getResponse(), []); $this->assertInstanceOf(\Psr\Http\Message\ResponseInterface::class, $result); - // Can't assert the status code or data, as this can be overwrited by sprinkles + // Can't assert the status code or data, as this can be overwritten by sprinkles // Test message $ms = $this->ci->alerts; diff --git a/app/sprinkles/account/tests/Integration/Database/Models/UserModelTest.php b/app/sprinkles/account/tests/Integration/Database/Models/UserModelTest.php index 6364ca519..fdc6a2a62 100644 --- a/app/sprinkles/account/tests/Integration/Database/Models/UserModelTest.php +++ b/app/sprinkles/account/tests/Integration/Database/Models/UserModelTest.php @@ -43,8 +43,8 @@ public function setUp(): void /** * Test user hard deletion with user relations. - * This is not a totaly acurate test, as each relations are added manually - * and new relations might not be added automatically to accuratly test + * This is not a totally accurate test, as each relations are added manually + * and new relations might not be added automatically to accurately test */ public function testUserHardDeleteWithUserRelations() { diff --git a/app/sprinkles/account/tests/Integration/RegistrationTest.php b/app/sprinkles/account/tests/Integration/RegistrationTest.php index 764ef1039..32be34100 100644 --- a/app/sprinkles/account/tests/Integration/RegistrationTest.php +++ b/app/sprinkles/account/tests/Integration/RegistrationTest.php @@ -89,7 +89,7 @@ public function testMissingFields() ]); $this->expectException(HttpException::class); - $this->expectExceptionMessage("Account can't be registrated as 'first_name' is required to create a new user."); + $this->expectExceptionMessage("Account can't be registered as 'first_name' is required to create a new user."); $registration->validate(); } @@ -135,7 +135,7 @@ public function testValidationWithDuplicateUsername() $registration = new Registration($this->ci, $this->fakeUserData); $this->expectException(HttpException::class); - $this->expectExceptionMessage("Username is already in use."); + $this->expectExceptionMessage('Username is already in use.'); $registration->validate(); } @@ -154,7 +154,7 @@ public function testValidationWithDuplicateEmail() //Set expectations $this->expectException(HttpException::class); - $this->expectExceptionMessage("Email is already in use."); + $this->expectExceptionMessage('Email is already in use.'); // Act $registration->validate(); diff --git a/app/sprinkles/account/tests/withTestUser.php b/app/sprinkles/account/tests/withTestUser.php index 0a370237d..e98e394f0 100644 --- a/app/sprinkles/account/tests/withTestUser.php +++ b/app/sprinkles/account/tests/withTestUser.php @@ -74,7 +74,7 @@ protected function createTestUser($isMaster = false, $login = false, array $para } /** - * Returns a random user id, exclusing th master id + * Returns a random user id, excluding the master id * @param int $masterId * @return int */ @@ -114,7 +114,7 @@ protected function giveUserTestPermission(UserInterface $user, $slug, $condition * Add the test permission to a Role, then the role to the user * @param UserInterface $user * @param Permission $permission - * @return Role The intermidiate role + * @return Role The intermediate role */ protected function giveUserPermission(UserInterface $user, Permission $permission) { diff --git a/app/sprinkles/admin/assets/userfrosting/js/handlebars-helpers.js b/app/sprinkles/admin/assets/userfrosting/js/handlebars-helpers.js index 96f47bb81..ba0874caa 100644 --- a/app/sprinkles/admin/assets/userfrosting/js/handlebars-helpers.js +++ b/app/sprinkles/admin/assets/userfrosting/js/handlebars-helpers.js @@ -37,7 +37,7 @@ Handlebars.registerHelper('ifx', function (v1, operator, v2, options) { /** * Perform simple calculations. - * + * * usage: {{calc x '+' 2}} */ Handlebars.registerHelper('calc', function (v1, operator, v2, options) { @@ -55,7 +55,7 @@ Handlebars.registerHelper('calc', function (v1, operator, v2, options) { /** * format an ISO date using Moment.js - * + * * moment syntax example: moment(Date("2011-07-18T15:50:52")).format("MMMM YYYY") * usage: {{dateFormat creation_date format="MMMM YYYY"}} * @requires momentjs http://momentjs.com/ @@ -103,17 +103,3 @@ Handlebars.registerHelper("currencyUsdFormat", function(amount) { Handlebars.registerHelper('slug', function(text) { return getSlug(text); }); - -/** - * Equality helper for Handlebars - * http://stackoverflow.com/questions/8853396/logical-operator-in-a-handlebars-js-if-conditional/21915381#21915381 - * @deprecated since 4.1 - use ifx instead - * usage: {{ifCond apple orange}} - */ -Handlebars.registerHelper('ifCond', function(v1, v2, options) { - if(v1 == v2) { - return options.fn(this); - } - - return options.inverse(this); -}); diff --git a/app/sprinkles/admin/composer.json b/app/sprinkles/admin/composer.json index de8c4418a..f5691407d 100644 --- a/app/sprinkles/admin/composer.json +++ b/app/sprinkles/admin/composer.json @@ -17,7 +17,7 @@ { "name": "Jordan Mele", "email": "SiliconSoldier@outlook.com.au", - "homepage": "https://blog.djmm.me" + "homepage": "https://djmm.me" } ], "autoload": { diff --git a/app/sprinkles/admin/src/Controller/GroupController.php b/app/sprinkles/admin/src/Controller/GroupController.php index 956143e37..59927ace0 100644 --- a/app/sprinkles/admin/src/Controller/GroupController.php +++ b/app/sprinkles/admin/src/Controller/GroupController.php @@ -47,7 +47,7 @@ class GroupController extends SimpleController * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function create(Request $request, Response $response, $args) { @@ -144,7 +144,7 @@ public function create(Request $request, Response $response, $args) * @param array $args * * @throws NotFoundException If group is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws BadRequestException */ public function delete(Request $request, Response $response, $args) @@ -228,7 +228,7 @@ public function delete(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws NotFoundException If group is not found */ public function getInfo(Request $request, Response $response, $args) @@ -276,7 +276,7 @@ public function getInfo(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getList(Request $request, Response $response, $args) { @@ -305,14 +305,14 @@ public function getList(Request $request, Response $response, $args) } /** - * Get deletetion confirmation modal. + * Get deletion confirmation modal. * * @param Request $request * @param Response $response * @param array $args * * @throws NotFoundException If group is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws BadRequestException */ public function getModalConfirmDelete(Request $request, Response $response, $args) @@ -372,7 +372,7 @@ public function getModalConfirmDelete(Request $request, Response $response, $arg * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getModalCreate(Request $request, Response $response, $args) { @@ -435,7 +435,7 @@ public function getModalCreate(Request $request, Response $response, $args) * @param array $args * * @throws NotFoundException If group is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getModalEdit(Request $request, Response $response, $args) { @@ -502,7 +502,7 @@ public function getModalEdit(Request $request, Response $response, $args) * @param array $args * * @throws NotFoundException If group is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getUsers(Request $request, Response $response, $args) { @@ -557,7 +557,7 @@ public function getUsers(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function pageInfo(Request $request, Response $response, $args) { @@ -637,7 +637,7 @@ public function pageInfo(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function pageList(Request $request, Response $response, $args) { @@ -673,7 +673,7 @@ public function pageList(Request $request, Response $response, $args) * @param array $args * * @throws NotFoundException If group is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function updateInfo(Request $request, Response $response, $args) { diff --git a/app/sprinkles/admin/src/Controller/PermissionController.php b/app/sprinkles/admin/src/Controller/PermissionController.php index de0991666..6a6b2ffd6 100644 --- a/app/sprinkles/admin/src/Controller/PermissionController.php +++ b/app/sprinkles/admin/src/Controller/PermissionController.php @@ -34,7 +34,7 @@ class PermissionController extends SimpleController * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws NotFoundException If permission is not found */ public function getInfo(Request $request, Response $response, $args) @@ -55,7 +55,7 @@ public function getInfo(Request $request, Response $response, $args) /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ $classMapper = $this->ci->classMapper; - $permission = $classMapper->getClassMapping('permission')::find($permissionId); + $permission = $classMapper->getClassMapping('permission')::findInt($permissionId); // If the permission doesn't exist, return 404 if (!$permission) { @@ -82,7 +82,7 @@ public function getInfo(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getList(Request $request, Response $response, $args) { @@ -122,7 +122,7 @@ public function getList(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getUsers(Request $request, Response $response, $args) { @@ -157,7 +157,7 @@ public function getUsers(Request $request, Response $response, $args) * * This checks that the currently logged-in user has permission to view permissions. * Note that permissions cannot be modified through the interface. This is because - * permissions are tighly coupled to the code and should only be modified by developers. + * permissions are tightly coupled to the code and should only be modified by developers. * This page requires authentication. * * Request type: GET @@ -166,7 +166,7 @@ public function getUsers(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws NotFoundException If permission is not found */ public function pageInfo(Request $request, Response $response, $args) @@ -187,7 +187,7 @@ public function pageInfo(Request $request, Response $response, $args) /** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */ $classMapper = $this->ci->classMapper; - $permission = $classMapper->getClassMapping('permission')::find($permissionId); + $permission = $classMapper->getClassMapping('permission')::findInt($permissionId); // If the permission doesn't exist, return 404 if (!$permission) { @@ -212,7 +212,7 @@ public function pageInfo(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function pageList(Request $request, Response $response, $args) { diff --git a/app/sprinkles/admin/src/Controller/RoleController.php b/app/sprinkles/admin/src/Controller/RoleController.php index 9c53f1d57..602789ece 100644 --- a/app/sprinkles/admin/src/Controller/RoleController.php +++ b/app/sprinkles/admin/src/Controller/RoleController.php @@ -47,7 +47,7 @@ class RoleController extends SimpleController * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function create(Request $request, Response $response, $args) { @@ -144,7 +144,7 @@ public function create(Request $request, Response $response, $args) * @param array $args * * @throws NotFoundException If role is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws BadRequestException */ public function delete(Request $request, Response $response, $args) @@ -227,7 +227,7 @@ public function delete(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws NotFoundException If role is not found */ public function getInfo(Request $request, Response $response, $args) @@ -275,7 +275,7 @@ public function getInfo(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getList(Request $request, Response $response, $args) { @@ -311,7 +311,7 @@ public function getList(Request $request, Response $response, $args) * @param array $args * * @throws NotFoundException If role is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws BadRequestException */ public function getModalConfirmDelete(Request $request, Response $response, $args) @@ -382,7 +382,7 @@ public function getModalConfirmDelete(Request $request, Response $response, $arg * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getModalCreate(Request $request, Response $response, $args) { @@ -446,7 +446,7 @@ public function getModalCreate(Request $request, Response $response, $args) * @param array $args * * @throws NotFoundException If role is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getModalEdit(Request $request, Response $response, $args) { @@ -518,7 +518,7 @@ public function getModalEdit(Request $request, Response $response, $args) * @param array $args * * @throws NotFoundException If role is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getModalEditPermissions(Request $request, Response $response, $args) { @@ -564,7 +564,7 @@ public function getModalEditPermissions(Request $request, Response $response, $a * @param array $args * * @throws NotFoundException If role is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getPermissions(Request $request, Response $response, $args) { @@ -617,7 +617,7 @@ public function getPermissions(Request $request, Response $response, $args) * @param array $args * * @throws NotFoundException If role is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getUsers(Request $request, Response $response, $args) { @@ -672,7 +672,7 @@ public function getUsers(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function pageInfo(Request $request, Response $response, $args) { @@ -752,7 +752,7 @@ public function pageInfo(Request $request, Response $response, $args) * @param Response $response * @param array $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function pageList(Request $request, Response $response, $args) { @@ -788,7 +788,7 @@ public function pageList(Request $request, Response $response, $args) * @param array $args * * @throws NotFoundException If role is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function updateInfo(Request $request, Response $response, $args) { @@ -910,7 +910,7 @@ public function updateInfo(Request $request, Response $response, $args) * @param array $args * * @throws NotFoundException If role is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws BadRequestException */ public function updateField(Request $request, Response $response, $args) diff --git a/app/sprinkles/admin/src/Controller/UserController.php b/app/sprinkles/admin/src/Controller/UserController.php index 3604d6fbf..7f0be6c32 100644 --- a/app/sprinkles/admin/src/Controller/UserController.php +++ b/app/sprinkles/admin/src/Controller/UserController.php @@ -51,7 +51,7 @@ class UserController extends SimpleController * @param Response $response * @param string[] $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function create(Request $request, Response $response, array $args) { @@ -209,7 +209,7 @@ public function create(Request $request, Response $response, array $args) * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function createPasswordReset(Request $request, Response $response, array $args) { @@ -283,7 +283,7 @@ public function createPasswordReset(Request $request, Response $response, array * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws BadRequestException */ public function delete(Request $request, Response $response, array $args) @@ -355,7 +355,7 @@ public function delete(Request $request, Response $response, array $args) * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getActivities(Request $request, Response $response, array $args) { @@ -408,7 +408,7 @@ public function getActivities(Request $request, Response $response, array $args) * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getInfo(Request $request, Response $response, array $args) { @@ -460,7 +460,7 @@ public function getInfo(Request $request, Response $response, array $args) * @param Response $response * @param string[] $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getList(Request $request, Response $response, array $args) { @@ -500,7 +500,7 @@ public function getList(Request $request, Response $response, array $args) * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws BadRequestException */ public function getModalConfirmDelete(Request $request, Response $response, array $args) @@ -562,7 +562,7 @@ public function getModalConfirmDelete(Request $request, Response $response, arra * @param Response $response * @param string[] $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getModalCreate(Request $request, Response $response, array $args) { @@ -660,7 +660,7 @@ public function getModalCreate(Request $request, Response $response, array $args * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getModalEdit(Request $request, Response $response, array $args) { @@ -761,7 +761,7 @@ public function getModalEdit(Request $request, Response $response, array $args) * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getModalEditPassword(Request $request, Response $response, array $args) { @@ -821,7 +821,7 @@ public function getModalEditPassword(Request $request, Response $response, array * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getModalEditRoles(Request $request, Response $response, array $args) { @@ -866,7 +866,7 @@ public function getModalEditRoles(Request $request, Response $response, array $a * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getPermissions(Request $request, Response $response, array $args) { @@ -916,7 +916,7 @@ public function getPermissions(Request $request, Response $response, array $args * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function getRoles(Request $request, Response $response, array $args) { @@ -971,7 +971,7 @@ public function getRoles(Request $request, Response $response, array $args) * @param Response $response * @param string[] $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function pageInfo(Request $request, Response $response, array $args) { @@ -1113,7 +1113,7 @@ public function pageInfo(Request $request, Response $response, array $args) * @param Response $response * @param string[] $args * - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function pageList(Request $request, Response $response, array $args) { @@ -1147,7 +1147,7 @@ public function pageList(Request $request, Response $response, array $args) * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page */ public function updateInfo(Request $request, Response $response, array $args) { @@ -1280,7 +1280,7 @@ public function updateInfo(Request $request, Response $response, array $args) * @param string[] $args * * @throws NotFoundException If user is not found - * @throws ForbiddenException If user is not authozied to access page + * @throws ForbiddenException If user is not authorized to access page * @throws BadRequestException */ public function updateField(Request $request, Response $response, array $args) @@ -1326,9 +1326,6 @@ public function updateField(Request $request, Response $response, array $args) // Make sure data is part of $_PUT data if (isset($put[$fieldName])) { $fieldData = $put[$fieldName]; - } elseif (isset($put['value'])) { - /** @deprecated - Fieldname should be used instead of `value` */ - $fieldData = $put['value']; } else { throw new BadRequestException(); } diff --git a/app/sprinkles/admin/src/ServicesProvider/ServicesProvider.php b/app/sprinkles/admin/src/ServicesProvider/ServicesProvider.php index 334c417b9..b8d94fca7 100644 --- a/app/sprinkles/admin/src/ServicesProvider/ServicesProvider.php +++ b/app/sprinkles/admin/src/ServicesProvider/ServicesProvider.php @@ -65,13 +65,6 @@ public function register(ContainerInterface $container) * @return \Psr\Http\Message\ResponseInterface */ return function (Request $request, Response $response, array $args) use ($c) { - // Backwards compatibility for the deprecated determineRedirectOnLogin service - if ($c->has('determineRedirectOnLogin')) { - $determineRedirectOnLogin = $c->determineRedirectOnLogin; - - return $determineRedirectOnLogin($response)->withStatus(200); - } - /** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager */ $authorizer = $c->authorizer; diff --git a/app/sprinkles/admin/src/Sprunje/PermissionUserSprunje.php b/app/sprinkles/admin/src/Sprunje/PermissionUserSprunje.php index 6794d55d7..791cf9a1c 100644 --- a/app/sprinkles/admin/src/Sprunje/PermissionUserSprunje.php +++ b/app/sprinkles/admin/src/Sprunje/PermissionUserSprunje.php @@ -10,7 +10,6 @@ namespace UserFrosting\Sprinkle\Admin\Sprunje; -use UserFrosting\Support\Exception\BadRequestException; use UserFrosting\Support\Exception\NotFoundException; /** @@ -29,12 +28,7 @@ class PermissionUserSprunje extends UserSprunje */ protected function baseQuery() { - // Requires a permission id - if (!isset($this->options['permission_id'])) { - throw new BadRequestException(); - } - - $permission = $this->classMapper->getClassMapping('permission')::find($this->options['permission_id']); + $permission = $this->classMapper->getClassMapping('permission')::findInt($this->options['permission_id']); // If the permission doesn't exist, return 404 if (!$permission) { diff --git a/app/sprinkles/admin/src/Sprunje/UserPermissionSprunje.php b/app/sprinkles/admin/src/Sprunje/UserPermissionSprunje.php index e0d2f898d..68670865b 100644 --- a/app/sprinkles/admin/src/Sprunje/UserPermissionSprunje.php +++ b/app/sprinkles/admin/src/Sprunje/UserPermissionSprunje.php @@ -10,7 +10,6 @@ namespace UserFrosting\Sprinkle\Admin\Sprunje; -use UserFrosting\Support\Exception\BadRequestException; use UserFrosting\Support\Exception\NotFoundException; /** @@ -29,12 +28,7 @@ class UserPermissionSprunje extends PermissionSprunje */ protected function baseQuery() { - // Requires a user id - if (!isset($this->options['user_id'])) { - throw new BadRequestException(); - } - - $user = $this->classMapper->getClassMapping('user')::find($this->options['user_id']); + $user = $this->classMapper->getClassMapping('user')::findInt($this->options['user_id']); // If the user doesn't exist, return 404 if (!$user) { diff --git a/app/sprinkles/admin/tests/Integration/Controller/ActivityControllerTest.php b/app/sprinkles/admin/tests/Integration/Controller/ActivityControllerTest.php index 933d059d5..5549cb4a1 100644 --- a/app/sprinkles/admin/tests/Integration/Controller/ActivityControllerTest.php +++ b/app/sprinkles/admin/tests/Integration/Controller/ActivityControllerTest.php @@ -12,15 +12,31 @@ use UserFrosting\Sprinkle\Account\Tests\withTestUser; use UserFrosting\Sprinkle\Admin\Controller\ActivityController; -use UserFrosting\Sprinkle\Core\Tests\ControllerTestCase; +use UserFrosting\Sprinkle\Core\Tests\RefreshDatabase; +use UserFrosting\Sprinkle\Core\Tests\TestDatabase; +use UserFrosting\Sprinkle\Core\Tests\withController; use UserFrosting\Support\Exception\ForbiddenException; +use UserFrosting\Tests\TestCase; /** * Tests ActivityController */ -class ActivityControllerTest extends ControllerTestCase +class ActivityControllerTest extends TestCase { use withTestUser; + use TestDatabase; + use RefreshDatabase; + use withController; + + /** + * Setup test database for controller tests + */ + public function setUp(): void + { + parent::setUp(); + $this->setupTestDatabase(); + $this->refreshDatabase(); + } /** * @return ActivityController diff --git a/app/sprinkles/admin/tests/Integration/Controller/AdminControllerTest.php b/app/sprinkles/admin/tests/Integration/Controller/AdminControllerTest.php index f681a1be5..cbe806974 100644 --- a/app/sprinkles/admin/tests/Integration/Controller/AdminControllerTest.php +++ b/app/sprinkles/admin/tests/Integration/Controller/AdminControllerTest.php @@ -12,15 +12,31 @@ use UserFrosting\Sprinkle\Account\Tests\withTestUser; use UserFrosting\Sprinkle\Admin\Controller\AdminController; -use UserFrosting\Sprinkle\Core\Tests\ControllerTestCase; +use UserFrosting\Sprinkle\Core\Tests\RefreshDatabase; +use UserFrosting\Sprinkle\Core\Tests\TestDatabase; +use UserFrosting\Sprinkle\Core\Tests\withController; use UserFrosting\Support\Exception\ForbiddenException; +use UserFrosting\Tests\TestCase; /** * Tests CoreController */ -class AdminControllerTest extends ControllerTestCase +class AdminControllerTest extends TestCase { use withTestUser; + use TestDatabase; + use RefreshDatabase; + use withController; + + /** + * Setup test database for controller tests + */ + public function setUp(): void + { + parent::setUp(); + $this->setupTestDatabase(); + $this->refreshDatabase(); + } /** * @return AdminController diff --git a/app/sprinkles/admin/tests/Integration/Controller/RoleControllerTest.php b/app/sprinkles/admin/tests/Integration/Controller/RoleControllerTest.php index a9e455907..bef5eacd5 100644 --- a/app/sprinkles/admin/tests/Integration/Controller/RoleControllerTest.php +++ b/app/sprinkles/admin/tests/Integration/Controller/RoleControllerTest.php @@ -807,7 +807,7 @@ public function testupdateFieldNoValue(RoleController $controller) */ public function testupdateFieldWithFailedValidation(RoleController $controller) { - // Create a string wich will be too long for validation + // Create a string which will be too long for validation $faker = Faker::getGenerator(); $value = $faker->text(500); @@ -843,7 +843,7 @@ public function testupdateFieldWithPermissionField(RoleController $controller) ]; $request = $this->getRequest()->withParsedBody($data); - // Check the default role has how many permisions + // Check the default role has how many permissions $role = Role::where('slug', 'foo')->first(); $this->assertEmpty($role->permissions); @@ -853,7 +853,7 @@ public function testupdateFieldWithPermissionField(RoleController $controller) $this->assertJson((string) $result->getBody()); $this->assertSame('[]', (string) $result->getBody()); - // Make sure role permisions was updated + // Make sure role permissions was updated $role = Role::where('slug', 'foo')->first(); $this->assertCount(1, $role->permissions); diff --git a/app/sprinkles/admin/tests/Integration/Controller/UserControllerTest.php b/app/sprinkles/admin/tests/Integration/Controller/UserControllerTest.php index d47573416..bd9edaa69 100644 --- a/app/sprinkles/admin/tests/Integration/Controller/UserControllerTest.php +++ b/app/sprinkles/admin/tests/Integration/Controller/UserControllerTest.php @@ -821,41 +821,6 @@ public function testUpdateField(UserController $controller) $this->assertSame('success', end($messages)['type']); } - /** - * @depends testControllerConstructorWithUser - * @depends testUpdateField - * @param UserController $controller - */ - public function testUpdateFieldWithDeprecatedSupport(UserController $controller) - { - // Create a user - $user = $this->createTestUser(); - - // Set post data - $data = [ - 'value' => 'deprecated', //<-- Use old `value` - ]; - $request = $this->getRequest()->withParsedBody($data); - - // Get controller stuff - $result = $controller->updateField($request, $this->getResponse(), ['user_name' => $user->user_name, 'field' => 'first_name']); - $this->assertSame($result->getStatusCode(), 200); - $this->assertJson((string) $result->getBody()); - $this->assertSame('[]', (string) $result->getBody()); - - // Make sure user was update - $editedUser = User::where('user_name', $user->user_name)->first(); - $this->assertSame('deprecated', $editedUser->first_name); - $this->assertNotSame($user->first_name, $editedUser->first_name); - $this->assertSame($user->last_name, $editedUser->last_name); - - // Test message - /** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */ - $ms = $this->ci->alerts; - $messages = $ms->getAndClearMessages(); - $this->assertSame('success', end($messages)['type']); - } - /** * @depends testControllerConstructorWithUser * @depends testUpdateField diff --git a/app/sprinkles/core/assets/userfrosting/js/attrchange.js b/app/sprinkles/core/assets/userfrosting/js/attrchange.js index 00878d30c..6cae77baa 100644 --- a/app/sprinkles/core/assets/userfrosting/js/attrchange.js +++ b/app/sprinkles/core/assets/userfrosting/js/attrchange.js @@ -4,7 +4,7 @@ http://meetselva.github.io/attrchange/ About License: Copyright (C) 2013-2014 Selvakumar Arumugam -You may use attrchange plugin under the terms of the MIT Licese. +You may use attrchange plugin under the terms of the MIT License. https://github.com/meetselva/attrchange/blob/master/MIT-License.txt */ (function($) { diff --git a/app/sprinkles/core/assets/userfrosting/js/uf-alerts.js b/app/sprinkles/core/assets/userfrosting/js/uf-alerts.js index 06a889c6a..b72931a9e 100644 --- a/app/sprinkles/core/assets/userfrosting/js/uf-alerts.js +++ b/app/sprinkles/core/assets/userfrosting/js/uf-alerts.js @@ -260,9 +260,9 @@ $.fn[pluginName] = function(methodOrOptions) { // Grab plugin instance var instance = $(this).data(pluginName); - // If undefined or object, initalise plugin. + // If undefined or object, initialize plugin. if (methodOrOptions === undefined || typeof methodOrOptions === 'object') { - // Only initalise if not previously done. + // Only initialize if not previously done. if (!instance) { $(this).data(pluginName, new Plugin(this, methodOrOptions)); } diff --git a/app/sprinkles/core/assets/userfrosting/js/uf-collection.js b/app/sprinkles/core/assets/userfrosting/js/uf-collection.js index 94ea416b1..0bf1874ec 100644 --- a/app/sprinkles/core/assets/userfrosting/js/uf-collection.js +++ b/app/sprinkles/core/assets/userfrosting/js/uf-collection.js @@ -320,9 +320,9 @@ $.fn[pluginName] = function(methodOrOptions) { // Grab plugin instance var instance = $(this).data(pluginName); - // If undefined or object, initalise plugin. + // If undefined or object, initialize plugin. if (methodOrOptions === undefined || typeof methodOrOptions === 'object') { - // Only initalise if not previously done. + // Only initialize if not previously done. if (!instance) { $(this).data(pluginName, new Plugin(this, methodOrOptions)); } diff --git a/app/sprinkles/core/assets/userfrosting/js/uf-form.js b/app/sprinkles/core/assets/userfrosting/js/uf-form.js index d77b02b37..e944fd371 100644 --- a/app/sprinkles/core/assets/userfrosting/js/uf-form.js +++ b/app/sprinkles/core/assets/userfrosting/js/uf-form.js @@ -406,9 +406,9 @@ $.fn[pluginName] = function (methodOrOptions) { // Grab plugin instance var instance = $(this).data(pluginName); - // If undefined or object, initalise plugin. + // If undefined or object, initialize plugin. if (methodOrOptions === undefined || typeof methodOrOptions === 'object') { - // Only initalise if not previously done. + // Only initialize if not previously done. if (!instance) { $(this).data(pluginName, new Plugin(this, methodOrOptions)); } diff --git a/app/sprinkles/core/assets/userfrosting/js/uf-modal.js b/app/sprinkles/core/assets/userfrosting/js/uf-modal.js index b84a59a21..919864226 100644 --- a/app/sprinkles/core/assets/userfrosting/js/uf-modal.js +++ b/app/sprinkles/core/assets/userfrosting/js/uf-modal.js @@ -38,7 +38,7 @@ return this; }; - /** #### INITIALISER #### */ + /** #### INITIALIZER #### */ Plugin.prototype._init = function ( target ) { var base = this; @@ -171,7 +171,7 @@ return instance[ methodOrOptions ]( Array.prototype.slice.call( arguments, 1 ) ); - // CASE: argument is options object or empty = initialise + // CASE: argument is options object or empty = initialize } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) { instance = new Plugin( $(this), methodOrOptions ); // ok to overwrite if this is a re-init @@ -180,7 +180,7 @@ // CASE: method called before init } else if ( !instance ) { - console.warn( 'Plugin must be initialised before using method: ' + methodOrOptions ); + console.warn( 'Plugin must be initialized before using method: ' + methodOrOptions ); // CASE: invalid method } else if ( methodOrOptions.indexOf('_') == 0 ) { diff --git a/app/sprinkles/core/assets/userfrosting/js/uf-table.js b/app/sprinkles/core/assets/userfrosting/js/uf-table.js index 74af41e22..4c8ad46c9 100644 --- a/app/sprinkles/core/assets/userfrosting/js/uf-table.js +++ b/app/sprinkles/core/assets/userfrosting/js/uf-table.js @@ -122,7 +122,7 @@ '.filter-select' : function() { return null; } }, - // apply disabled classname to the pager arrows when the rows at either extreme is visible + // apply disabled class name to the pager arrows when the rows at either extreme is visible pager_updateArrows: true, // starting page of the pager (zero based index) @@ -690,9 +690,9 @@ $.fn[pluginName] = function(methodOrOptions) { // Grab plugin instance var instance = $(this).data(pluginName); - // If undefined or object, initalise plugin. + // If undefined or object, initialize plugin. if (methodOrOptions === undefined || typeof methodOrOptions === 'object') { - // Only initalise if not previously done. + // Only initialize if not previously done. if (!instance) { $(this).data(pluginName, new Plugin(this, methodOrOptions)); } diff --git a/app/sprinkles/core/composer.json b/app/sprinkles/core/composer.json index 886617949..b8f821b55 100644 --- a/app/sprinkles/core/composer.json +++ b/app/sprinkles/core/composer.json @@ -17,7 +17,7 @@ { "name": "Jordan Mele", "email": "SiliconSoldier@outlook.com.au", - "homepage": "https://blog.djmm.me" + "homepage": "https://djmm.me" }, { "name": "Mike Jacobs" @@ -27,6 +27,7 @@ } ], "require": { + "composer/semver": "^3.2.4", "doctrine/dbal": "^2.5", "filp/whoops": "^2.3.1", "illuminate/cache": "5.8.*", @@ -44,15 +45,18 @@ "slim/twig-view": "^2.5", "symfony/http-foundation": "*", "twig/twig": "^2.11", - "userfrosting/assets": "^6.1.0", - "userfrosting/config": "~4.4.0", - "userfrosting/cache": "~4.4.0", - "userfrosting/fortress": "~4.4.0", - "userfrosting/i18n": "~4.4.0", - "userfrosting/session": "~4.4.0", - "userfrosting/support": "~4.4.0", + "userfrosting/assets": "^6.2.0", + "userfrosting/config": "~4.5.0", + "userfrosting/cache": "~4.5.0", + "userfrosting/fortress": "~4.5.0", + "userfrosting/i18n": "~4.5.0", + "userfrosting/session": "~4.5.0", + "userfrosting/support": "~4.5.0", "vlucas/phpdotenv": "^3.4.0" }, + "require-dev": { + "php-mock/php-mock-phpunit": "^2.6" + }, "autoload": { "files" : [ "defines.php" diff --git a/app/sprinkles/core/config/default.php b/app/sprinkles/core/config/default.php index adab1bb51..3dcb79835 100755 --- a/app/sprinkles/core/config/default.php +++ b/app/sprinkles/core/config/default.php @@ -25,7 +25,7 @@ */ 'address_book' => [ 'admin' => [ - 'email' => getenv('SMTP_USER') ?: null, + 'email' => env('SMTP_USER'), 'name' => 'Site Administrator', ], ], @@ -97,7 +97,7 @@ * Note : CSRF Middleware should only be disabled for dev or debug purposes. */ 'csrf' => [ - 'enabled' => (getenv('CSRF_ENABLED') !== false) ? getenv('CSRF_ENABLED') : true, + 'enabled' => env('CSRF_ENABLED', true), 'name' => 'csrf', 'storage_limit' => 200, 'strength' => 16, @@ -124,12 +124,12 @@ */ 'db' => [ 'default' => [ - 'driver' => getenv('DB_DRIVER') ?: 'mysql', - 'host' => getenv('DB_HOST') ?: 'localhost', - 'port' => getenv('DB_PORT') ?: null, - 'database' => getenv('DB_NAME') ?: null, - 'username' => getenv('DB_USER') ?: null, - 'password' => getenv('DB_PASSWORD') ?: null, + 'driver' => env('DB_DRIVER', 'mysql'), + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT'), + 'database' => env('DB_NAME'), + 'username' => env('DB_USER'), + 'password' => env('DB_PASSWORD'), 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '', @@ -160,8 +160,8 @@ * Supported Drivers for disk: "local", "ftp", "sftp", "s3", "rackspace" */ 'filesystems' => [ - 'default' => getenv('FILESYSTEM_DRIVER') ?: 'local', - 'cloud' => getenv('FILESYSTEM_CLOUD') ?: 's3', + 'default' => env('FILESYSTEM_DRIVER', 'local'), + 'cloud' => env('FILESYSTEM_CLOUD', 's3'), 'disks' => [ /* @@ -196,11 +196,11 @@ */ 's3' => [ 'driver' => 's3', - 'key' => getenv('AWS_ACCESS_KEY_ID') ?: '', - 'secret' => getenv('AWS_SECRET_ACCESS_KEY') ?: '', - 'region' => getenv('AWS_DEFAULT_REGION') ?: '', // See : http://docs.aws.amazon.com/general/latest/gr/rande.html - 'bucket' => getenv('AWS_BUCKET') ?: '', - 'url' => getenv('AWS_URL') ?: '', + 'key' => env('AWS_ACCESS_KEY_ID', ''), + 'secret' => env('AWS_SECRET_ACCESS_KEY', ''), + 'region' => env('AWS_DEFAULT_REGION', ''), // See : http://docs.aws.amazon.com/general/latest/gr/rande.html + 'bucket' => env('AWS_BUCKET', ''), + 'url' => env('AWS_URL', ''), ], /* * Rackspace Config. Config should go in .env file. see : @@ -213,12 +213,12 @@ */ 'rackspace' => [ 'driver' => 'rackspace', - 'username' => getenv('RACKSPACE_USERNAME') ?: '', - 'key' => getenv('RACKSPACE_KEY') ?: '', - 'container' => getenv('RACKSPACE_CONTAINER') ?: '', - 'endpoint' => getenv('RACKSPACE_ENDPOINT') ?: '', - 'region' => getenv('RACKSPACE_REGION') ?: '', - 'url_type' => getenv('RACKSPACE_URL_TYPE') ?: '', + 'username' => env('RACKSPACE_USERNAME', ''), + 'key' => env('RACKSPACE_KEY', ''), + 'container' => env('RACKSPACE_CONTAINER', ''), + 'endpoint' => env('RACKSPACE_ENDPOINT', ''), + 'region' => env('RACKSPACE_REGION', ''), + 'url_type' => env('RACKSPACE_URL_TYPE', ''), ], ], ], @@ -230,13 +230,13 @@ * See https://learn.userfrosting.com/mail/the-mailer-service */ 'mail' => [ - 'mailer' => 'smtp', // Set to one of 'smtp', 'mail', 'qmail', 'sendmail' - 'host' => getenv('SMTP_HOST') ?: null, - 'port' => 587, - 'auth' => true, - 'secure' => 'tls', // Enable TLS encryption. Set to `tls`, `ssl` or `false` (to disabled) - 'username' => getenv('SMTP_USER') ?: null, - 'password' => getenv('SMTP_PASSWORD') ?: null, + 'mailer' => env('MAIL_MAILER', 'smtp'), // Set to one of 'smtp', 'mail', 'qmail', 'sendmail' + 'host' => env('SMTP_HOST'), + 'port' => env('SMTP_PORT', 587), + 'auth' => env('SMTP_AUTH', true), + 'secure' => env('SMTP_SECURE', 'tls'), // Enable TLS encryption. Set to `tls`, `ssl` or `false` (to disabled) + 'username' => env('SMTP_USER'), + 'password' => env('SMTP_PASSWORD'), 'smtp_debug' => 4, 'message_options' => [ 'CharSet' => 'UTF-8', diff --git a/app/sprinkles/core/config/testing.php b/app/sprinkles/core/config/testing.php index 38a87253f..6d1231f88 100755 --- a/app/sprinkles/core/config/testing.php +++ b/app/sprinkles/core/config/testing.php @@ -61,12 +61,12 @@ * Disable native sessions in tests */ 'session' => [ - 'handler' => getenv('TEST_SESSION_HANDLER') ?: 'array', + 'handler' => env('TEST_SESSION_HANDLER', 'array'), ], /* * Database to use when using the TestDatabase Trait */ 'testing' => [ - 'dbConnection' => getenv('TEST_DB') ?: 'test_integration', + 'dbConnection' => env('TEST_DB', 'test_integration'), ], ]; diff --git a/app/sprinkles/core/src/Bakery/BakeCommand.php b/app/sprinkles/core/src/Bakery/BakeCommand.php index e1c42c416..89242df65 100644 --- a/app/sprinkles/core/src/Bakery/BakeCommand.php +++ b/app/sprinkles/core/src/Bakery/BakeCommand.php @@ -42,7 +42,7 @@ protected function configure() { $this->setName('bake') ->setDescription('UserFrosting installation command') - ->setHelp('This command combine the setup:db, setup:smtp, debug, migrate, create-admin and build-assets commands.'); + ->setHelp('This command combine the setup:db, setup:mail, debug, migrate, create-admin and build-assets commands.'); } /** @@ -70,7 +70,7 @@ protected function executeSetup(InputInterface $input, OutputInterface $output) $command = $this->getApplication()->find('setup:db'); $command->run($input, $output); - $command = $this->getApplication()->find('setup:smtp'); + $command = $this->getApplication()->find('setup:mail'); $command->run($input, $output); } diff --git a/app/sprinkles/core/src/Bakery/BuildAssets.php b/app/sprinkles/core/src/Bakery/BuildAssets.php index 358de51cb..c12ce0c26 100644 --- a/app/sprinkles/core/src/Bakery/BuildAssets.php +++ b/app/sprinkles/core/src/Bakery/BuildAssets.php @@ -13,7 +13,8 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use UserFrosting\Sprinkle\Core\Bakery\Helper\NodeVersionCheck; +use UserFrosting\Sprinkle\Core\Exceptions\VersionCompareException; +use UserFrosting\Sprinkle\Core\Util\VersionValidator; use UserFrosting\System\Bakery\BaseCommand; /** @@ -24,8 +25,6 @@ */ class BuildAssets extends BaseCommand { - use NodeVersionCheck; - /** * @var string Path to the build/ directory */ @@ -52,10 +51,15 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->io->title("UserFrosting's Assets Builder"); // Validate Node and npm version - $this->checkNodeVersion(false); - $this->checkNpmVersion(false); + try { + VersionValidator::validateNodeVersion(); + VersionValidator::validateNpmVersion(); + } catch (VersionCompareException $e) { + $this->io->error($e->getMessage()); + exit(1); + } - // Set $buildPath. We'll use the aboslute path for this task + // Set $buildPath. We'll use the absolute path for this task $this->buildPath = \UserFrosting\ROOT_DIR . \UserFrosting\DS . \UserFrosting\BUILD_DIR_NAME; // Delete cached data is requested diff --git a/app/sprinkles/core/src/Bakery/ClearCacheCommand.php b/app/sprinkles/core/src/Bakery/ClearCacheCommand.php index 11221ae21..e695962fa 100644 --- a/app/sprinkles/core/src/Bakery/ClearCacheCommand.php +++ b/app/sprinkles/core/src/Bakery/ClearCacheCommand.php @@ -72,7 +72,7 @@ protected function clearIlluminateCache() /** * Clear the Twig cache using the Twig CacheHelper class. * - * @return bool true/false if operation is successfull + * @return bool true/false if operation is successful */ protected function clearTwigCache() { @@ -84,7 +84,7 @@ protected function clearTwigCache() /** * Clear the Router cache data file. * - * @return bool true/false if operation is successfull + * @return bool true/false if operation is successful */ protected function clearRouterCache() { diff --git a/app/sprinkles/core/src/Bakery/DebugCommand.php b/app/sprinkles/core/src/Bakery/DebugCommand.php index 083db3ea2..394c8b74c 100644 --- a/app/sprinkles/core/src/Bakery/DebugCommand.php +++ b/app/sprinkles/core/src/Bakery/DebugCommand.php @@ -13,7 +13,8 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use UserFrosting\Sprinkle\Core\Bakery\Helper\DatabaseTest; -use UserFrosting\Sprinkle\Core\Bakery\Helper\NodeVersionCheck; +use UserFrosting\Sprinkle\Core\Exceptions\VersionCompareException; +use UserFrosting\Sprinkle\Core\Util\VersionValidator; use UserFrosting\System\Bakery\BaseCommand; /** @@ -24,7 +25,6 @@ class DebugCommand extends BaseCommand { use DatabaseTest; - use NodeVersionCheck; /** * {@inheritdoc} @@ -45,17 +45,34 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->io->title('UserFrosting'); // Need to touch the config service first to load dotenv values - $config = $this->ci->config; + $this->ci->config; + + // Validate PHP, Node and npm version + try { + VersionValidator::validatePhpVersion(); + VersionValidator::validateNodeVersion(); + VersionValidator::validateNpmVersion(); + } catch (VersionCompareException $e) { + $this->io->error($e->getMessage()); + exit(1); + } + + // Validate deprecated versions + try { + VersionValidator::validatePhpDeprecation(); + } catch (VersionCompareException $e) { + $this->io->warning($e->getMessage()); + } // Perform tasks & display info $this->io->definitionList( - ['UserFrosing version' => \UserFrosting\VERSION], + ['UserFrosting version' => \UserFrosting\VERSION], ['OS Name' => php_uname('s')], ['Project Root' => \UserFrosting\ROOT_DIR], - ['Environment mode' => getenv('UF_MODE')], - ['PHP Version' => $this->checkPhpVersion()], - ['Node Version' => $this->checkNodeVersion()], - ['NPM Version' => $this->checkNpmVersion()] + ['Environment mode' => env('UF_MODE', 'default')], + ['PHP Version' => VersionValidator::getPhpVersion()], + ['Node Version' => VersionValidator::getNodeVersion()], + ['NPM Version' => VersionValidator::getNpmVersion()] ); // Now we list Sprinkles @@ -64,7 +81,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // Show the DB config $this->showConfig(); - // Check database connexion + // Check database connection $this->checkDatabase(); // If all went well and there's no fatal errors, we are ready to bake @@ -74,29 +91,6 @@ protected function execute(InputInterface $input, OutputInterface $output) return 0; } - /** - * Check the minimum version of php. - * This is done by composer itself, but we do it again for good mesure. - * - * @return string The current PHP Version - */ - protected function checkPhpVersion(): string - { - $phpVersion = (string) phpversion(); - - if (version_compare($phpVersion, \UserFrosting\PHP_MIN_VERSION, '<')) { - $this->io->error('UserFrosting requires php version ' . \UserFrosting\PHP_MIN_VERSION . " or above. You'll need to update you PHP version before you can continue."); - exit(1); - } - - // Check for deprecated versions - if (version_compare($phpVersion, \UserFrosting\PHP_RECOMMENDED_VERSION, '<')) { - $this->io->warning('While your PHP version is still supported by UserFrosting, we recommend version ' . \UserFrosting\PHP_RECOMMENDED_VERSION . ' or above as ' . $phpVersion . ' will soon be unsupported. See http://php.net/supported-versions.php for more info.'); - } - - return $phpVersion; - } - /** * List all sprinkles defined in the Sprinkles schema file, * making sure this file exist at the same time. @@ -127,7 +121,7 @@ protected function listSprinkles(InputInterface $input, OutputInterface $output) } /** - * Check the database connexion and setup the `.env` file if we can't + * Check the database connection and setup the `.env` file if we can't * connect and there's no one found. */ protected function checkDatabase(): void diff --git a/app/sprinkles/core/src/Bakery/Helper/DatabaseTest.php b/app/sprinkles/core/src/Bakery/Helper/DatabaseTest.php index 6d5a73fe0..c5be6fa62 100644 --- a/app/sprinkles/core/src/Bakery/Helper/DatabaseTest.php +++ b/app/sprinkles/core/src/Bakery/Helper/DatabaseTest.php @@ -13,14 +13,14 @@ use Illuminate\Database\Capsule\Manager as Capsule; /** - * Database Test Trait. Include method to test the db connexion + * Database Test Trait. Include method to test the db connection * * @author Alex Weissman (https://alexanderweissman.com) */ trait DatabaseTest { /** - * Function to test the db connexion. + * Function to test the db connection. * * @return bool True if success */ @@ -32,7 +32,7 @@ protected function testDB() // Get config $config = $this->ci->config; - // Check params are valids + // Check params are valid $dbParams = $config['db.default']; if (!$dbParams) { throw new \Exception("'default' database connection not found. Please double-check your configuration."); diff --git a/app/sprinkles/core/src/Bakery/Helper/LocaleOption.php b/app/sprinkles/core/src/Bakery/Helper/LocaleOption.php index fa74802ca..8c2a40ebe 100644 --- a/app/sprinkles/core/src/Bakery/Helper/LocaleOption.php +++ b/app/sprinkles/core/src/Bakery/Helper/LocaleOption.php @@ -24,7 +24,7 @@ trait LocaleOption /** * Display locale selection question. * - * @return string Selected locale indentifier + * @return string Selected locale identifier */ protected function askForLocale(string $name, bool $default = true): string { diff --git a/app/sprinkles/core/src/Bakery/Helper/NodeVersionCheck.php b/app/sprinkles/core/src/Bakery/Helper/NodeVersionCheck.php deleted file mode 100644 index 07f0f860c..000000000 --- a/app/sprinkles/core/src/Bakery/Helper/NodeVersionCheck.php +++ /dev/null @@ -1,53 +0,0 @@ -io->error('UserFrosting requires Node version ' . \UserFrosting\NODE_MIN_VERSION . ' or above. Check the documentation for more details.'); - exit(1); - } - - return $npmVersion; - } - - /** - * Check the minimum version requirement for Npm. - * - * @return string NPM version - */ - protected function checkNpmVersion() - { - $npmVersion = trim(exec('npm -v')); - - if (version_compare($npmVersion, \UserFrosting\NPM_MIN_VERSION, '<')) { - $this->io->error('UserFrosting requires npm version ' . \UserFrosting\NPM_MIN_VERSION . ' or above. Check the documentation for more details.'); - exit(1); - } - - return $npmVersion; - } -} diff --git a/app/sprinkles/core/src/Bakery/LocaleCompareCommand.php b/app/sprinkles/core/src/Bakery/LocaleCompareCommand.php index 75289b79d..a721e4248 100644 --- a/app/sprinkles/core/src/Bakery/LocaleCompareCommand.php +++ b/app/sprinkles/core/src/Bakery/LocaleCompareCommand.php @@ -72,14 +72,14 @@ protected function execute(InputInterface $input, OutputInterface $output) } /** - * Display dictionary comparaison table. + * Display dictionary comparison table. * * @param DictionaryInterface $leftDictionary * @param DictionaryInterface $rightDictionary */ protected function compareDictionaries(DictionaryInterface $leftDictionary, DictionaryInterface $rightDictionary): void { - $this->io->section("Comparaison between {$rightDictionary->getLocale()->getName()} and {$leftDictionary->getLocale()->getName()}"); + $this->io->section("Comparison between {$rightDictionary->getLocale()->getName()} and {$leftDictionary->getLocale()->getName()}"); $diff = Compare::dictionaries($leftDictionary, $rightDictionary); @@ -104,7 +104,7 @@ protected function compareDictionaries(DictionaryInterface $leftDictionary, Dict } /** - * Display dictionary keys comparaison table. + * Display dictionary keys comparison table. * * @param DictionaryInterface $leftDictionary * @param DictionaryInterface $rightDictionary @@ -135,7 +135,7 @@ protected function dictionariesKeys(DictionaryInterface $leftDictionary, Diction } /** - * Display dictionary values comparaison table. + * Display dictionary values comparison table. * * @param DictionaryInterface $leftDictionary * @param DictionaryInterface $rightDictionary diff --git a/app/sprinkles/core/src/Bakery/LocaleDictionaryCommand.php b/app/sprinkles/core/src/Bakery/LocaleDictionaryCommand.php index f0a9a5337..cf52f787d 100644 --- a/app/sprinkles/core/src/Bakery/LocaleDictionaryCommand.php +++ b/app/sprinkles/core/src/Bakery/LocaleDictionaryCommand.php @@ -34,7 +34,7 @@ class LocaleDictionaryCommand extends BaseCommand protected function configure() { $this->setName('locale:dictionary') - ->setHelp('This command shows the compiled dictionnary for the selected locale.') + ->setHelp('This command shows the compiled dictionary for the selected locale.') ->addOption('locale', 'l', InputOption::VALUE_REQUIRED, 'The selected locale.') ->addOption('width', 'w', InputOption::VALUE_REQUIRED, 'Set the length for preview column text.', 100) ->setDescription('Display locale dictionary'); diff --git a/app/sprinkles/core/src/Bakery/LocaleInfoCommand.php b/app/sprinkles/core/src/Bakery/LocaleInfoCommand.php index c9991d493..de5c1c127 100644 --- a/app/sprinkles/core/src/Bakery/LocaleInfoCommand.php +++ b/app/sprinkles/core/src/Bakery/LocaleInfoCommand.php @@ -29,8 +29,8 @@ class LocaleInfoCommand extends BaseCommand protected function configure() { $this->setName('locale:info') - ->setHelp('This command list all available locale as well as the defaut locale.') - ->setDescription('Informations about available locales'); + ->setHelp('This command list all available locale as well as the default locale.') + ->setDescription('Information about available locales'); } /** diff --git a/app/sprinkles/core/src/Bakery/MigrateCleanCommand.php b/app/sprinkles/core/src/Bakery/MigrateCleanCommand.php index 13b371612..b94fb50d4 100644 --- a/app/sprinkles/core/src/Bakery/MigrateCleanCommand.php +++ b/app/sprinkles/core/src/Bakery/MigrateCleanCommand.php @@ -81,7 +81,7 @@ protected function execute(InputInterface $input, OutputInterface $output) /** * Delete stale migrations from the database. * - * @param Collection $stale Collection of stale migartion classes. + * @param Collection $stale Collection of stale migration classes. * @param Migrator $migrator Migrator object */ protected function cleanStaleRecords(Collection $stale, Migrator $migrator) diff --git a/app/sprinkles/core/src/Bakery/MigrateResetCommand.php b/app/sprinkles/core/src/Bakery/MigrateResetCommand.php index 5a52b5dfa..c83027199 100644 --- a/app/sprinkles/core/src/Bakery/MigrateResetCommand.php +++ b/app/sprinkles/core/src/Bakery/MigrateResetCommand.php @@ -104,7 +104,7 @@ protected function performReset(InputInterface $input) // If all went well, there's no fatal errors and we have migrated // something, show some success if (empty($resetted)) { - $this->io->warning('Nothing was reseted !'); + $this->io->warning('Nothing was reset !'); } else { $this->io->success('Reset successful !'); } @@ -127,7 +127,7 @@ protected function performHardReset(InputInterface $input) exit(1); } - // Get shema Builder + // Get schema Builder $connection = $this->ci->db->connection($database); $schema = $connection->getSchemaBuilder(); diff --git a/app/sprinkles/core/src/Bakery/MigrateRollbackCommand.php b/app/sprinkles/core/src/Bakery/MigrateRollbackCommand.php index 4c1c64e77..ec5414073 100644 --- a/app/sprinkles/core/src/Bakery/MigrateRollbackCommand.php +++ b/app/sprinkles/core/src/Bakery/MigrateRollbackCommand.php @@ -91,7 +91,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // If all went well, there's no fatal errors and we have migrated // something, show some success if (empty($migrated)) { - $this->io->warning('Nothing was rollbacked !'); + $this->io->warning('Nothing was rolled back !'); } else { $this->io->success('Rollback successful !'); } diff --git a/app/sprinkles/core/src/Bakery/SeedCommand.php b/app/sprinkles/core/src/Bakery/SeedCommand.php index dffe4df39..e8a861a06 100644 --- a/app/sprinkles/core/src/Bakery/SeedCommand.php +++ b/app/sprinkles/core/src/Bakery/SeedCommand.php @@ -61,7 +61,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // Seeds list $seeds = []; - // Start by gettings seeds + // Start by getting seeds foreach ($classes as $className) { // Get seed class and diff --git a/app/sprinkles/core/src/Bakery/SetupCommand.php b/app/sprinkles/core/src/Bakery/SetupCommand.php index 411e1bf7b..6d0ac9afe 100644 --- a/app/sprinkles/core/src/Bakery/SetupCommand.php +++ b/app/sprinkles/core/src/Bakery/SetupCommand.php @@ -29,7 +29,7 @@ protected function configure() { $this->setName('setup') ->setDescription('UserFrosting Configuration Wizard') - ->setHelp('This command combine the setup:env, setup:db and setup:smtp commands.'); + ->setHelp('This command combine the setup:env, setup:db and setup:mail commands.'); } /** @@ -40,7 +40,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $command = $this->getApplication()->find('setup:db'); $command->run($input, $output); - $command = $this->getApplication()->find('setup:smtp'); + $command = $this->getApplication()->find('setup:mail'); $command->run($input, $output); $command = $this->getApplication()->find('setup:env'); diff --git a/app/sprinkles/core/src/Bakery/SetupDbCommand.php b/app/sprinkles/core/src/Bakery/SetupDbCommand.php index b405e2e3d..c96364328 100644 --- a/app/sprinkles/core/src/Bakery/SetupDbCommand.php +++ b/app/sprinkles/core/src/Bakery/SetupDbCommand.php @@ -136,11 +136,11 @@ protected function execute(InputInterface $input, OutputInterface $output) } /** - * Ask for database crendentials. + * Ask for database credentials. * * @param InputInterface $args Command arguments * - * @return array The databse credentials + * @return array The database credentials */ protected function askForDatabase(InputInterface $args) { @@ -201,7 +201,7 @@ protected function askForDatabase(InputInterface $args) } /** - * Test new database connecion. + * Test new database connection. * * @param array $dbParams Database params * @param bool $displayMessage Display io message @@ -214,7 +214,7 @@ protected function testDatabase($dbParams, $displayMessage = true) $capsule = new Capsule(); $capsule->addConnection($dbParams); - // Test the db connexion. + // Test the db connection. try { $conn = $capsule->getConnection(); $conn->getPdo(); diff --git a/app/sprinkles/core/src/Bakery/SetupEnvCommand.php b/app/sprinkles/core/src/Bakery/SetupEnvCommand.php index e7beb28d7..877c6fb45 100644 --- a/app/sprinkles/core/src/Bakery/SetupEnvCommand.php +++ b/app/sprinkles/core/src/Bakery/SetupEnvCommand.php @@ -41,7 +41,7 @@ protected function configure() { $this->setName('setup:env') ->setDescription('UserFrosting Environment Configuration Wizard') - ->setHelp('Helper command to setup environement mode. This can also be done manually by editing the app/.env file or using global server environment variables.') + ->setHelp('Helper command to setup environment mode. This can also be done manually by editing the app/.env file or using global server environment variables.') ->addOption('mode', null, InputOption::VALUE_OPTIONAL, 'The environment to use'); } @@ -53,7 +53,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // Display header, $this->io->title("UserFrosting's Environment Setup Wizard"); $this->io->note("Environment mode will be saved in `{$this->envPath}`"); - $this->io->write('Select desired envrionement mode. Production should only be used when deploying a live app.'); + $this->io->write('Select desired environment mode. Production should only be used when deploying a live app.'); // Get an instance of the DotenvEditor $dotenvEditor = new DotenvEditor(\UserFrosting\APP_DIR, false); diff --git a/app/sprinkles/core/src/Bakery/SetupSmtpCommand.php b/app/sprinkles/core/src/Bakery/SetupSmtpCommand.php index 938c1b8ae..ef418a5b3 100644 --- a/app/sprinkles/core/src/Bakery/SetupSmtpCommand.php +++ b/app/sprinkles/core/src/Bakery/SetupSmtpCommand.php @@ -40,6 +40,11 @@ class SetupSmtpCommand extends BaseCommand */ const Setup_Gmail = 'Gmail'; + /** + * @var string Native mail setup string + */ + const Setup_Native = 'Native Mail'; + /** * @var string No email setup string */ @@ -50,13 +55,17 @@ class SetupSmtpCommand extends BaseCommand */ protected function configure() { - $this->setName('setup:smtp') + $this->setName('setup:mail') + ->setAliases(['setup:smtp']) ->setDescription('UserFrosting SMTP Configuration Wizard') ->setHelp('Helper command to setup outgoing email configuration. This can also be done manually by editing the app/.env file or using global server environment variables.') ->addOption('force', null, InputOption::VALUE_NONE, 'Force setup if SMTP appears to be already configured') ->addOption('smtp_host', null, InputOption::VALUE_OPTIONAL, 'The SMTP server hostname') ->addOption('smtp_user', null, InputOption::VALUE_OPTIONAL, 'The SMTP server user') - ->addOption('smtp_password', null, InputOption::VALUE_OPTIONAL, 'The SMTP server password'); + ->addOption('smtp_password', null, InputOption::VALUE_OPTIONAL, 'The SMTP server password') + ->addOption('smtp_port', null, InputOption::VALUE_OPTIONAL, 'The SMTP server port') + ->addOption('smtp_auth', null, InputOption::VALUE_OPTIONAL, 'The SMTP server authentication') + ->addOption('smtp_secure', null, InputOption::VALUE_OPTIONAL, 'The SMTP server security type'); } /** @@ -70,7 +79,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $config = $this->ci->config; // Display header, - $this->io->title("UserFrosting's SMTP Setup Wizard"); + $this->io->title("UserFrosting's Mail Setup Wizard"); // Get an instance of the DotenvEditor $dotenvEditor = new DotenvEditor(\UserFrosting\APP_DIR, false); @@ -79,34 +88,43 @@ protected function execute(InputInterface $input, OutputInterface $output) // Check if db is already setup if (!$input->getOption('force') && $this->isSmtpConfigured($dotenvEditor)) { - $this->io->note('SMTP already setup. Use the `php bakery setup:smtp --force` command to run SMTP setup again.'); + $this->io->note('Mail is already setup. Use the `php bakery setup:mail --force` command to run setup again.'); return; } // Get keys $keys = [ + 'MAIL_MAILER' => ($dotenvEditor->keyExists('MAIL_MAILER')) ? $dotenvEditor->getValue('MAIL_MAILER') : '', 'SMTP_HOST' => ($dotenvEditor->keyExists('SMTP_HOST')) ? $dotenvEditor->getValue('SMTP_HOST') : '', 'SMTP_USER' => ($dotenvEditor->keyExists('SMTP_USER')) ? $dotenvEditor->getValue('SMTP_USER') : '', 'SMTP_PASSWORD' => ($dotenvEditor->keyExists('SMTP_PASSWORD')) ? $dotenvEditor->getValue('SMTP_PASSWORD') : '', + 'SMTP_PORT' => ($dotenvEditor->keyExists('SMTP_PORT')) ? $dotenvEditor->getValue('SMTP_PORT') : '', + 'SMTP_AUTH' => ($dotenvEditor->keyExists('SMTP_AUTH')) ? $dotenvEditor->getValue('SMTP_AUTH') : '', + 'SMTP_SECURE' => ($dotenvEditor->keyExists('SMTP_SECURE')) ? $dotenvEditor->getValue('SMTP_SECURE') : '', ]; // There may be some custom config or global env values defined on the server. // We'll check for that and ask for confirmation in this case. - if ($config['mail.host'] != $keys['SMTP_HOST'] || + if ($config['mail.mailer'] != $keys['MAIL_MAILER'] || + $config['mail.host'] != $keys['SMTP_HOST'] || $config['mail.username'] != $keys['SMTP_USER'] || - $config['mail.password'] != $keys['SMTP_PASSWORD']) { - $this->io->warning("Current SMTP configuration differ from the configuration defined in `{$this->envPath}`. Global system environment variables might be defined."); - - if (!$this->io->confirm('Continue?', false)) { + $config['mail.password'] != $keys['SMTP_PASSWORD'] || + $config['mail.port'] != $keys['SMTP_PORT'] || + $config['mail.auth'] != $keys['SMTP_AUTH'] || + $config['mail.secure'] != $keys['SMTP_SECURE'] + ) { + $this->io->warning("Current mail configuration from config service differ from the configuration defined in `{$this->envPath}`. Global system environment variables might be defined, and it might not be required to setup mail again."); + + if (!$this->io->confirm('Continue with mail setup?', false)) { return; } } - $this->io->note("SMTP credentials will be saved in `{$this->envPath}`"); + $this->io->note("Mail configuration and SMTP credentials will be saved in `{$this->envPath}`"); // Ask for SMTP info - $smtpParams = $this->askForSmtpMethod($input); + $smtpParams = $this->askForMailMethod($input); // Time to save $this->io->section('Saving data'); @@ -117,7 +135,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $dotenvEditor->save(); // Success - $this->io->success("SMTP credentials saved to `{$this->envPath}`"); + $this->io->success("Mail configuration saved to `{$this->envPath}`.\nYou can test outgoing mail using `test:mail` command."); } /** @@ -127,17 +145,23 @@ protected function execute(InputInterface $input, OutputInterface $output) * * @return array The SMTP connection info */ - protected function askForSmtpMethod(InputInterface $input) + protected function askForMailMethod(InputInterface $input) { // If the user defined any of the command input argument, skip right to SMTP method - if ($input->getOption('smtp_host') || $input->getOption('smtp_user') || $input->getOption('smtp_password')) { + if ($input->getOption('smtp_host') || + $input->getOption('smtp_user') || + $input->getOption('smtp_password') || + $input->getOption('smtp_port') || + $input->getOption('smtp_auth') || + $input->getOption('smtp_secure') + ) { return $this->askForSmtp($input); } - // Display nice explanation and ask wich method to use - $this->io->write("In order to send registration emails, UserFrosting requires an outgoing mail server. When using UserFrosting in a production environment, a SMTP server should be used. A Gmail account can be used if you're only playing with UserFrosting or on a local dev environment. You can also choose to not setup an outgoing mail server at the moment, but account registration won't work. You can always re-run this setup or edit `{$this->envPath}` if you have problems sending email later."); + // Display nice explanation and ask which method to use + $this->io->write("In order to send registration emails, UserFrosting requires an outgoing mail server. When using UserFrosting in a production environment, a SMTP server should be used. A Gmail account or native mail command can be used if you're only playing with UserFrosting or on a local dev environment. You can also choose to not setup an outgoing mail server at the moment, but account registration won't work. You can always re-run this setup or edit `{$this->envPath}` if you have problems sending email later."); - $choice = $this->io->choice('Select setup method', [self::Setup_SMTP, self::Setup_Gmail, self::Setup_None], self::Setup_SMTP); + $choice = $this->io->choice('Select setup method', [self::Setup_SMTP, self::Setup_Gmail, self::Setup_Native, self::Setup_None], self::Setup_SMTP); switch ($choice) { case self::Setup_SMTP: @@ -146,6 +170,9 @@ protected function askForSmtpMethod(InputInterface $input) case self::Setup_Gmail: return $this->askForGmail($input); break; + case self::Setup_Native: + return $this->askForNative($input); + break; case self::Setup_None: default: return $this->askForNone($input); @@ -169,11 +196,23 @@ protected function askForSmtp(InputInterface $input) // Use custom validator to accept empty password return $password; }); + $smtpPort = ($input->getOption('smtp_port')) ?: $this->io->ask('SMTP Server Port', 587); + $smtpAuth = ($input->getOption('smtp_auth')) ?: $this->io->confirm('SMTP Server Authentication', true); + $smtpSecure = ($input->getOption('smtp_secure')) ?: $this->io->choice('SMTP Server Security type', ['tls', 'ssl', 'Other...'], 'tls'); + + // Ask for custom input if 'other' was chosen + if ($smtpSecure == 'Other...') { + $smtpSecure = $this->io->ask('Enter custom SMTP Server Security type'); + } return [ + 'MAIL_MAILER' => 'smtp', 'SMTP_HOST' => $smtpHost, 'SMTP_USER' => $smtpUser, 'SMTP_PASSWORD' => $smtpPassword, + 'SMTP_PORT' => $smtpPort, + 'SMTP_AUTH' => ($smtpAuth) ? 'true' : 'false', + 'SMTP_SECURE' => $smtpSecure, ]; } @@ -193,12 +232,40 @@ protected function askForGmail(InputInterface $input) }); return [ + 'MAIL_MAILER' => 'smtp', 'SMTP_HOST' => 'smtp.gmail.com', 'SMTP_USER' => $smtpUser, 'SMTP_PASSWORD' => $smtpPassword, ]; } + /** + * Process the "native mail" setup option. + * + * @param InputInterface $input + * + * @return array The SMTP connection info + */ + protected function askForNative(InputInterface $input) + { + // Display big warning and confirmation + $this->io->warning('Native mail function should only be used locally, inside containers or for development purposes.'); + + if ($this->io->confirm('Continue ?', false)) { + return [ + 'MAIL_MAILER' => 'mail', + 'SMTP_HOST' => '', + 'SMTP_USER' => '', + 'SMTP_PASSWORD' => '', + 'SMTP_PORT' => '', + 'SMTP_AUTH' => '', + 'SMTP_SECURE' => '', + ]; + } else { + $this->askForMailMethod($input); + } + } + /** * Process the "no email support" setup option. * @@ -213,12 +280,16 @@ protected function askForNone(InputInterface $input) if ($this->io->confirm('Continue ?', false)) { return [ + 'MAIL_MAILER' => 'smtp', 'SMTP_HOST' => '', 'SMTP_USER' => '', 'SMTP_PASSWORD' => '', + 'SMTP_PORT' => '', + 'SMTP_AUTH' => '', + 'SMTP_SECURE' => '', ]; } else { - $this->askForSmtpMethod($input); + $this->askForMailMethod($input); } } @@ -231,7 +302,14 @@ protected function askForNone(InputInterface $input) */ protected function isSmtpConfigured(DotenvEditor $dotenvEditor) { - if ($dotenvEditor->keyExists('SMTP_HOST') && $dotenvEditor->keyExists('SMTP_USER') && $dotenvEditor->keyExists('SMTP_PASSWORD')) { + if ($dotenvEditor->keyExists('MAIL_MAILER') || ( + $dotenvEditor->keyExists('SMTP_HOST') && + $dotenvEditor->keyExists('SMTP_USER') && + $dotenvEditor->keyExists('SMTP_PASSWORD') && + $dotenvEditor->keyExists('SMTP_PORT') && + $dotenvEditor->keyExists('SMTP_AUTH') && + $dotenvEditor->keyExists('SMTP_SECURE') + )) { return true; } else { return false; diff --git a/app/sprinkles/core/src/Bakery/Test.php b/app/sprinkles/core/src/Bakery/Test.php index 211fccc84..21ea3ab8c 100644 --- a/app/sprinkles/core/src/Bakery/Test.php +++ b/app/sprinkles/core/src/Bakery/Test.php @@ -106,7 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output) /** * Return the sprinkle test class * - * @param string $testscope Testscope received from command line + * @param string $testscope Test scope received from command line * @return string */ protected function parseSprinkleTestScope($testscope) diff --git a/app/sprinkles/core/src/Core.php b/app/sprinkles/core/src/Core.php index 93fcad518..8bd030f8e 100644 --- a/app/sprinkles/core/src/Core.php +++ b/app/sprinkles/core/src/Core.php @@ -10,7 +10,6 @@ namespace UserFrosting\Sprinkle\Core; -use Psr\Container\ContainerInterface; use RocketTheme\Toolbox\Event\Event; use UserFrosting\Sprinkle\Core\Csrf\SlimCsrfProvider; use UserFrosting\Sprinkle\Core\Database\Models\Model; @@ -35,18 +34,6 @@ class Core extends Sprinkle TranslatorServicesProvider::class, ]; - /** - * Create a new Sprinkle object. - * - * @param ContainerInterface $ci The global container object, which holds all your services. - */ - public function __construct(ContainerInterface $ci) - { - $this->ci = $ci; - - $this->registerStreams(); - } - /** * Defines which events in the UF lifecycle our Sprinkle should hook into. */ @@ -70,6 +57,8 @@ public function onSprinklesInitialized() // Set container for environment info class EnvironmentInfo::$ci = $this->ci; + + $this->registerStreams(); } /** diff --git a/app/sprinkles/core/src/Database/EloquentBuilder.php b/app/sprinkles/core/src/Database/EloquentBuilder.php index 78676b2ff..11b839b1d 100644 --- a/app/sprinkles/core/src/Database/EloquentBuilder.php +++ b/app/sprinkles/core/src/Database/EloquentBuilder.php @@ -13,6 +13,7 @@ use Illuminate\Database\Eloquent\Builder as LaravelEloquentBuilder; use Illuminate\Database\Query\Expression; use Illuminate\Support\Str; +use UserFrosting\Support\Exception\BadRequestException; /** * UserFrosting's custom Eloquent Builder Class. @@ -21,6 +22,26 @@ */ class EloquentBuilder extends LaravelEloquentBuilder { + /** + * Find a model by its primary integer-valued key or throw an exception if + * something other than a nonnegative integer is provided. + * + * @param int $id + * @param array $columns + * + * @throws \UserFrosting\Support\Exception\BadRequestException + * + * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|null + */ + public function findInt($id, $columns = ['*']) + { + if (!isset($id) || (filter_var($id, FILTER_VALIDATE_INT) === false)) { + throw new BadRequestException(); + } + + return $this->find($id, $columns); + } + /** * Add subselect queries to sum the relations. * diff --git a/app/sprinkles/core/src/Database/Migration.php b/app/sprinkles/core/src/Database/Migration.php index cdadb67ed..94acc2bab 100644 --- a/app/sprinkles/core/src/Database/Migration.php +++ b/app/sprinkles/core/src/Database/Migration.php @@ -28,7 +28,7 @@ abstract class Migration implements MigrationInterface * List of dependencies for this migration. * Should return an array of class required to be run before this migration. * - * N.B.: Uncomment the next line when the static $dependencie deprecation is removed + * N.B.: Uncomment the next line when the static $dependencies deprecation is removed */ //public static $dependencies = []; diff --git a/app/sprinkles/core/src/Database/Migrator/MigrationDependencyAnalyser.php b/app/sprinkles/core/src/Database/Migrator/MigrationDependencyAnalyser.php index 3b30dd7d5..e91ce87a2 100644 --- a/app/sprinkles/core/src/Database/Migrator/MigrationDependencyAnalyser.php +++ b/app/sprinkles/core/src/Database/Migrator/MigrationDependencyAnalyser.php @@ -10,9 +10,6 @@ namespace UserFrosting\Sprinkle\Core\Database\Migrator; -use ReflectionClass; -use UserFrosting\Sprinkle\Core\Facades\Config; -use UserFrosting\Sprinkle\Core\Facades\Debug; use UserFrosting\Sprinkle\Core\Util\BadClassNameException; /** @@ -65,8 +62,6 @@ public function __construct(array $pending = [], array $installed = []) /** * Analyse the dependencies. - * - * @return void */ public function analyse(): void { @@ -89,10 +84,10 @@ public function analyse(): void * dependencies. This is very important as the order the migrations needs * to be run is defined by this recursion. By waiting for the dependency * to be marked as fulfillable to mark the parent as fulfillable, the - * parent class will be automatocally placed after it's dependencies - * in the `fullfillable` property. + * parent class will be automatically placed after it's dependencies + * in the `fulfillable` property. * - * @param string $migrationName The migration classname + * @param string $migrationName The migration class name * * @return bool True/False if the migration is fulfillable */ @@ -128,7 +123,7 @@ protected function validateClassDependencies(string $migrationName): bool } // Check is the dependency is pending installation. If so, check for it's dependencies. - // If the dependency is not fullfillable, then this one isn't either + // If the dependency is not fulfillable, then this one isn't either if (!$this->pending->contains($dependency) || !$this->validateClassDependencies($dependency)) { return $this->markAsUnfulfillable($migrationName, $dependency); } @@ -169,7 +164,7 @@ public function getUnfulfillable(): array /** * Mark a dependency as fulfillable. Removes it from the pending list and add it to the fulfillable list. * - * @param string $migration The migration classname + * @param string $migration The migration class name * * @return bool True, it's fulfillable */ @@ -183,10 +178,10 @@ protected function markAsFulfillable(string $migration): bool /** * Mark a dependency as unfulfillable. Removes it from the pending list and add it to the unfulfillable list. * - * @param string $migration The migration classname - * @param string|array $dependency The problematic dependecy + * @param string $migration The migration class name + * @param string|array $dependency The problematic dependency * - * @return bool False, it's not fullfillable + * @return bool False, it's not fulfillable */ protected function markAsUnfulfillable(string $migration, $dependency): bool { @@ -200,8 +195,7 @@ protected function markAsUnfulfillable(string $migration, $dependency): bool } /** - * Returns the migration dependency list - * Also handles the old deprecated behaviour where dependencies where not in a static property. + * Returns the migration dependency list. * * @param string $migration The migration class * @@ -214,19 +208,9 @@ protected function getMigrationDependencies(string $migration): array throw new BadClassNameException("Unable to find the migration class '$migration'. Run 'php bakery migrate:clean' to remove stale migrations."); } - // If the `dependencies` property exist and is static, use this one. - // Otherwise, get a class instance and the non static property - // We can remove this one the non static property is removed - $reflectionClass = new ReflectionClass($migration); - if ($reflectionClass->hasProperty('dependencies') && $reflectionClass->getProperty('dependencies')->isStatic()) { + // If the `dependencies` property exist, use it + if (property_exists($migration, 'dependencies')) { return $this->normalizeClasses($migration::$dependencies); - } elseif (property_exists($migration, 'dependencies')) { - if (Config::get('debug.deprecation')) { - Debug::warning("`$migration` uses a non static `dependencies` property. Please change the `dependencies` property to a static property."); - } - $instance = new $migration(); - - return $this->normalizeClasses($instance->dependencies); } else { return []; } diff --git a/app/sprinkles/core/src/Database/Migrator/MigrationLocator.php b/app/sprinkles/core/src/Database/Migrator/MigrationLocator.php index 7059be976..740218313 100644 --- a/app/sprinkles/core/src/Database/Migrator/MigrationLocator.php +++ b/app/sprinkles/core/src/Database/Migrator/MigrationLocator.php @@ -65,7 +65,7 @@ public function getMigrations() } /** - * Return an array of migration details inclusing the classname and the sprinkle name. + * Return an array of migration details including the class name and the sprinkle name. * * @param ResourceInstance $file The migration file * @@ -77,7 +77,7 @@ protected function getMigrationDetails(ResourceInstance $file) $sprinkleName = $file->getLocation()->getName(); $sprinkleName = Str::studly($sprinkleName); - // Getting base path, name and classname + // Getting base path, name and class name $basePath = str_replace($file->getBasename(), '', $file->getBasePath()); $name = $basePath . $file->getFilename(); $className = str_replace('/', '\\', $basePath) . $file->getFilename(); diff --git a/app/sprinkles/core/src/Database/Migrator/MigrationRollbackDependencyAnalyser.php b/app/sprinkles/core/src/Database/Migrator/MigrationRollbackDependencyAnalyser.php index 6d5bd50ab..7503cb669 100644 --- a/app/sprinkles/core/src/Database/Migrator/MigrationRollbackDependencyAnalyser.php +++ b/app/sprinkles/core/src/Database/Migrator/MigrationRollbackDependencyAnalyser.php @@ -27,7 +27,7 @@ class MigrationRollbackDependencyAnalyser extends MigrationDependencyAnalyser * represent the same thing as "up" dependencies. fulfillable can be * rolledback, unfulfillable cannot. * - * @param string $migrationName The migration classname + * @param string $migrationName The migration class name * * @return bool True/False if the migration is fulfillable */ diff --git a/app/sprinkles/core/src/Database/Migrator/Migrator.php b/app/sprinkles/core/src/Database/Migrator/Migrator.php index 2511dab6a..206cc4dd6 100644 --- a/app/sprinkles/core/src/Database/Migrator/Migrator.php +++ b/app/sprinkles/core/src/Database/Migrator/Migrator.php @@ -15,8 +15,6 @@ use UserFrosting\Sprinkle\Core\Database\MigrationInterface; use UserFrosting\Sprinkle\Core\Database\Migrator\MigrationDependencyAnalyser as Analyser; use UserFrosting\Sprinkle\Core\Database\Migrator\MigrationRollbackDependencyAnalyser as RollbackAnalyser; -use UserFrosting\Sprinkle\Core\Facades\Config; -use UserFrosting\Sprinkle\Core\Facades\Debug; use UserFrosting\Sprinkle\Core\Util\BadClassNameException; /** @@ -158,7 +156,7 @@ protected function runPending(array $migrations, array $options = []) * Run "up" a migration class. * * @param string $migrationClassName The migration class name - * @param int $batch The current bacth number + * @param int $batch The current batch number * @param bool $pretend If this operation should be pretended / faked */ protected function runUp($migrationClassName, $batch, $pretend) @@ -173,7 +171,7 @@ protected function runUp($migrationClassName, $batch, $pretend) return $this->pretendToRun($migration, 'up'); } - // Run the actuall migration + // Run the actual migration $this->runMigration($migration, 'up'); // Once we have run a migrations class, we will log that it was run in this @@ -182,18 +180,6 @@ protected function runUp($migrationClassName, $batch, $pretend) $this->repository->log($migrationClassName, $batch); $this->note("Migrated: {$migrationClassName}"); - - /* - * If the migration has a `seed` method, run it - * @deprecated Since 4.2.0. Use a seeder instead - */ - if (method_exists($migration, 'seed')) { - if (Config::get('debug.deprecation')) { - Debug::warning('Migration `seed` method has been deprecated and will be removed in future versions. Please use a Seeder instead.'); - } - $this->runMigration($migration, 'seed'); - $this->note("Seeded: {$migrationClassName}"); - } } /** @@ -264,7 +250,7 @@ protected function getMigrationsForRollback(array $options) /** * Rollback the given migrations. * - * @param array $migrations An array of migrations to rollback formated as an eloquent collection + * @param array $migrations An array of migrations to rollback formatted as an eloquent collection * @param array $options The options for the current operation * * @return array The list of rolledback migration classes @@ -284,14 +270,14 @@ protected function rollbackMigrations(array $migrations, array $options) // Next we will run through all of the migrations and call the "down" method // which will reverse each migration in order. This getLast method on the - // repository already returns these migration's classenames in reverse order. + // repository already returns these migration's class names in reverse order. foreach ($migrations as $migration) { // We have to make sure the class exist first if (!$availableMigrations->contains($migration)) { // NOTE This next was commented because if a class doesn't exist, - // you'll get stuck and prevent futher classes to be rolledback - // until this class is put back in the system. Might wan't to + // you'll get stuck and prevent further classes to be rolledback + // until this class is put back in the system. Might want to // display a warning instead of silently skipping it. See related "todo" in "reset" method //throw new \Exception("Can't rollback migrations `$migration`. The migration class doesn't exist"); $this->note("WARNING: Can't rollback migrations `$migration`. The migration class doesn't exist"); @@ -350,7 +336,7 @@ public function reset($pretend = false) // this database. This will allow us to get the database back into its // "empty" state and ready to be migrated "up" again. // - // !TODO :: Should compare to the install list to make sure no outstanding migration (ran, but with no migraiton class anymore) still exist in the db + // !TODO :: Should compare to the install list to make sure no outstanding migration (ran, but with no migration class anymore) still exist in the db $migrations = array_reverse($this->getRanMigrations()); if (count($migrations) === 0) { @@ -396,10 +382,7 @@ protected function runDown($migrationClassName, $pretend) protected function runMigration(MigrationInterface $migration, $method) { $callback = function () use ($migration, $method) { - // We keep this for seed... - if (method_exists($migration, $method)) { - $migration->{$method}(); - } + $migration->{$method}(); }; if ($this->getSchemaGrammar()->supportsSchemaTransactions()) { diff --git a/app/sprinkles/core/src/Database/Models/Model.php b/app/sprinkles/core/src/Database/Models/Model.php index 014ba60ee..987ae9139 100644 --- a/app/sprinkles/core/src/Database/Models/Model.php +++ b/app/sprinkles/core/src/Database/Models/Model.php @@ -141,32 +141,4 @@ protected function newBaseQueryBuilder() $connection->getPostProcessor() ); } - - /** - * Get the properties of this object as an associative array. Alias for toArray(). - * - * @deprecated since 4.1.8 There is no point in having this alias. - * - * @return array - */ - public function export() - { - return $this->toArray(); - } - - /** - * For raw array fetching. Must be static, otherwise PHP gets confused about where to find $table. - * - * @deprecated since 4.1.8 setFetchMode is no longer available as of Laravel 5.4. - * @link https://github.com/laravel/framework/issues/17728 - * - * @return Builder - */ - public static function queryBuilder() - { - // Set query builder to fetch result sets as associative arrays (instead of creating stdClass objects) - DB::connection()->setFetchMode(\PDO::FETCH_ASSOC); - - return DB::table(static::$table); - } } diff --git a/app/sprinkles/core/src/Database/Relations/Concerns/Syncable.php b/app/sprinkles/core/src/Database/Relations/Concerns/Syncable.php index f53917e93..757a72aed 100644 --- a/app/sprinkles/core/src/Database/Relations/Concerns/Syncable.php +++ b/app/sprinkles/core/src/Database/Relations/Concerns/Syncable.php @@ -46,7 +46,7 @@ public function sync($data, $deleting = true, $forceCreate = false, $relatedKeyN $updateRows = []; $newRows = []; foreach ($data as $row) { - // We determine "updateable" rows as those whose $relatedKeyName (usually 'id') is set, not empty, and + // We determine "updatable" rows as those whose $relatedKeyName (usually 'id') is set, not empty, and // match a related row in the database. if (isset($row[$relatedKeyName]) && !empty($row[$relatedKeyName]) && in_array($row[$relatedKeyName], $current)) { $id = $row[$relatedKeyName]; diff --git a/app/sprinkles/core/src/Database/Relations/Concerns/Unique.php b/app/sprinkles/core/src/Database/Relations/Concerns/Unique.php index ff8eee692..ed3b9acdb 100644 --- a/app/sprinkles/core/src/Database/Relations/Concerns/Unique.php +++ b/app/sprinkles/core/src/Database/Relations/Concerns/Unique.php @@ -126,34 +126,6 @@ public function limit($value) return $this; } - /** - * Set the limit on the number of intermediate models to load. - * - * @deprecated since 4.1.7 - * - * @param int $value - * - * @return $this - */ - public function withLimit($value) - { - return $this->limit($value); - } - - /** - * Set the offset when loading the intermediate models. - * - * @deprecated since 4.1.7 - * - * @param int $value - * - * @return $this - */ - public function withOffset($value) - { - return $this->offset($value); - } - /** * Add a query to load the nested tertiary models for this relationship. * diff --git a/app/sprinkles/core/src/Database/Seeder/BaseSeed.php b/app/sprinkles/core/src/Database/Seeder/BaseSeed.php index 2feb03801..cb141cb8b 100644 --- a/app/sprinkles/core/src/Database/Seeder/BaseSeed.php +++ b/app/sprinkles/core/src/Database/Seeder/BaseSeed.php @@ -38,7 +38,7 @@ public function __construct(ContainerInterface $ci) /** * Validate if a specific set of migrations have been ran. * - * @param string|array $migrations List of migraiton or specific migration required + * @param string|array $migrations List of migration or specific migration required * * @throws \Exception If dependent migration is not available * diff --git a/app/sprinkles/core/src/Database/Seeder/Seeder.php b/app/sprinkles/core/src/Database/Seeder/Seeder.php index b9edd8259..84f14f0b1 100644 --- a/app/sprinkles/core/src/Database/Seeder/Seeder.php +++ b/app/sprinkles/core/src/Database/Seeder/Seeder.php @@ -148,7 +148,7 @@ protected function loadSeeders(array $seedFiles) } /** - * Return an array of seed details inclusing the classname and the sprinkle name. + * Return an array of seed details including the class name and the sprinkle name. * * @param ResourceInstance $file The seed file * @@ -160,7 +160,7 @@ protected function getSeedDetails(ResourceInstance $file) $sprinkleName = $file->getLocation()->getName(); $sprinkleName = Str::studly($sprinkleName); - // Getting base path, name and classname + // Getting base path, name and class name $basePath = str_replace($file->getBasename(), '', $file->getBasePath()); $name = $basePath . $file->getFilename(); $className = str_replace('/', '\\', $basePath) . $file->getFilename(); diff --git a/app/sprinkles/core/src/Error/Renderer/WhoopsRenderer.php b/app/sprinkles/core/src/Error/Renderer/WhoopsRenderer.php index 1a64198da..52294c15f 100644 --- a/app/sprinkles/core/src/Error/Renderer/WhoopsRenderer.php +++ b/app/sprinkles/core/src/Error/Renderer/WhoopsRenderer.php @@ -559,29 +559,6 @@ public function getResourcePaths() return $this->searchPaths; } - /** - * @deprecated - * - * @return string - */ - public function getResourcesPath() - { - $allPaths = $this->getResourcePaths(); - - // Compat: return only the first path added - return end($allPaths) ?: null; - } - - /** - * @deprecated - * - * @param string $resourcesPath - */ - public function setResourcesPath($resourcesPath) - { - $this->addResourcePath($resourcesPath); - } - /** * Return the application paths. * @@ -716,9 +693,9 @@ protected function getResource($resource) /** * Checks all values within the given superGlobal array. - * Blacklisted values will be replaced by a equal length string cointaining only '*' characters. + * Blacklisted values will be replaced by a equal length string containing only '*' characters. * - * We intentionally dont rely on $GLOBALS as it depends on 'auto_globals_jit' php.ini setting. + * We intentionally don't rely on $GLOBALS as it depends on 'auto_globals_jit' php.ini setting. * * @param array $superGlobal One of the superglobal arrays * @param string $superGlobalName the name of the superglobal array, e.g. '_GET' diff --git a/app/sprinkles/core/src/Exceptions/VersionCompareException.php b/app/sprinkles/core/src/Exceptions/VersionCompareException.php new file mode 100644 index 000000000..f81ca43b0 --- /dev/null +++ b/app/sprinkles/core/src/Exceptions/VersionCompareException.php @@ -0,0 +1,55 @@ +constraint = $constraint; + + return $this; + } + + public function getVersion(): string + { + return $this->version; + } + + /** + * @return self + */ + public function setVersion(string $version) + { + $this->version = $version; + + return $this; + } + + public function getConstraint(): string + { + return $this->constraint; + } +} diff --git a/app/sprinkles/core/src/I18n/SiteLocale.php b/app/sprinkles/core/src/I18n/SiteLocale.php index 71c61f11f..508c7e671 100644 --- a/app/sprinkles/core/src/I18n/SiteLocale.php +++ b/app/sprinkles/core/src/I18n/SiteLocale.php @@ -22,6 +22,8 @@ class SiteLocale { /** * @var ContainerInterface + * + * @todo Change this to only the config service */ protected $ci; @@ -121,9 +123,11 @@ public function getDefaultLocale(): string } /** - * Returns the locale intentifier (ie. en_US) to use. + * Returns the locale identifier (ie. en_US) to use. + * + * @return string Locale identifier * - * @return string Locale intentifier + * @todo This should accept the request service as argument, or null, in which case the `getBrowserLocale` method would be skipped */ public function getLocaleIndentifier(): string { @@ -142,6 +146,8 @@ public function getLocaleIndentifier(): string * Return the browser locale. * * @return string|null Returns null if no valid locale can be found + * + * @todo This should accept the request service as argument. */ protected function getBrowserLocale(): ?string { diff --git a/app/sprinkles/core/src/Log/MixedFormatter.php b/app/sprinkles/core/src/Log/MixedFormatter.php index 880705567..8867393ec 100644 --- a/app/sprinkles/core/src/Log/MixedFormatter.php +++ b/app/sprinkles/core/src/Log/MixedFormatter.php @@ -56,10 +56,6 @@ protected function toJson($data, $ignoreErrors = false) */ private function jsonEncodePretty($data) { - if (version_compare(PHP_VERSION, '5.4.0', '>=')) { - return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); - } - - return json_encode($data); + return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); } } diff --git a/app/sprinkles/core/src/Model/UFModel.php b/app/sprinkles/core/src/Model/UFModel.php deleted file mode 100644 index d85e62d4f..000000000 --- a/app/sprinkles/core/src/Model/UFModel.php +++ /dev/null @@ -1,31 +0,0 @@ -count(); } - - /** - * Executes the sprunje query, applying all sorts, filters, and pagination. - * - * Returns an array containing `count` (the total number of rows, before filtering), `count_filtered` (the total number of rows after filtering), - * and `rows` (the filtered result set). - * - * @deprecated since 4.1.7 Use getArray() instead. - * - * @return mixed[] - */ - public function getResults() - { - return $this->getArray(); - } } diff --git a/app/sprinkles/core/src/Twig/CacheHelper.php b/app/sprinkles/core/src/Twig/CacheHelper.php index 460248f14..0b1abe04d 100755 --- a/app/sprinkles/core/src/Twig/CacheHelper.php +++ b/app/sprinkles/core/src/Twig/CacheHelper.php @@ -22,6 +22,8 @@ class CacheHelper { /** * @var ContainerInterface The global container object, which holds all your services. + * + * @todo Change this, only the locator service is required */ protected $ci; @@ -38,7 +40,7 @@ public function __construct(ContainerInterface $ci) /** * Function that delete the Twig cache directory content. * - * @return bool true/false if operation is successfull + * @return bool true/false if operation is successful */ public function clearCache() { diff --git a/app/sprinkles/core/src/Util/CheckEnvironment.php b/app/sprinkles/core/src/Util/CheckEnvironment.php index 055fe4ec8..40c8a73dc 100644 --- a/app/sprinkles/core/src/Util/CheckEnvironment.php +++ b/app/sprinkles/core/src/Util/CheckEnvironment.php @@ -14,6 +14,7 @@ use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Slim\Views\Twig; +use UserFrosting\Sprinkle\Core\Exceptions\VersionCompareException; use UserFrosting\UniformResourceLocator\ResourceLocator; /** @@ -102,8 +103,10 @@ public function __invoke(Request $request, Response $response, $next) /** * Run through all pre-flight checks. + * + * @return bool True if problem(s) found. */ - public function checkAll() + public function checkAll(): bool { $problemsFound = false; @@ -142,8 +145,10 @@ public function checkAll() /** * For Apache environments, check that required Apache modules are installed. + * + * @return bool True if problem(s) found. */ - public function checkApache() + public function checkApache(): bool { $problemsFound = false; @@ -202,10 +207,11 @@ public function checkGd() /** * Check that all image* functions used by Captcha exist. - * * Some versions of GD are missing one or more of these functions, thus why we check for them explicitly. + * + * @return bool True if problem(s) found. */ - public function checkImageFunctions() + public function checkImageFunctions(): bool { $problemsFound = false; @@ -242,8 +248,10 @@ public function checkImageFunctions() /** * Check that PDO is installed and enabled. + * + * @return bool True if problem(s) found. */ - public function checkPdo() + public function checkPdo(): bool { $problemsFound = false; @@ -267,8 +275,10 @@ public function checkPdo() /** * Check that log, cache, and session directories exist. + * + * @return bool True if problem(s) found. */ - public function checkDirectories() + public function checkDirectories(): bool { $problemsFound = false; @@ -299,9 +309,12 @@ public function checkDirectories() } /** - * Check that log, cache, and session directories are writable, and that other directories are set appropriately for the environment. + * Check that log, cache, and session directories are writable, + * and that other directories are set appropriately for the environment. + * + * @return bool True if problem(s) found. */ - public function checkPermissions() + public function checkPermissions(): bool { $problemsFound = false; @@ -351,28 +364,31 @@ public function checkPermissions() /** * Check that PHP meets the minimum required version. + * + * @return bool True if problem(s) found. */ - public function checkPhp() + public function checkPhp(): bool { - $problemsFound = false; - - // Check PHP version - if (version_compare(phpversion(), \UserFrosting\PHP_MIN_VERSION, '<')) { - $problemsFound = true; + try { + VersionValidator::validatePhpVersion(); + } catch (VersionCompareException $e) { $this->resultsFailed['phpVersion'] = [ - 'title' => " You need to upgrade your PHP installation.", - 'message' => "I'm sorry, UserFrosting requires version " . \UserFrosting\PHP_MIN_VERSION . ' or greater. Please upgrade your version of PHP, or contact your web hosting service and ask them to upgrade it for you.', + 'title' => " Your PHP version does not satisfy UserFrosting required constraint.", + 'message' => $e->getMessage(), 'success' => false, ]; - } else { - $this->resultsSuccess['phpVersion'] = [ - 'title' => " PHP version checks out!", - 'message' => "You're using PHP " . \UserFrosting\PHP_MIN_VERSION . 'or higher. Great!', - 'success' => true, - ]; + + return true; } - return $problemsFound; + // No problem found ! + $this->resultsSuccess['phpVersion'] = [ + 'title' => " PHP version checks out!", + 'message' => 'Your PHP version satisfy UserFrosting required constraint. Great!', + 'success' => true, + ]; + + return false; } /** @@ -380,9 +396,9 @@ public function checkPhp() * * @return bool */ - public function isProduction() + public function isProduction(): bool { - return getenv('UF_MODE') == 'production'; + return env('UF_MODE') == 'production'; } /** @@ -390,8 +406,8 @@ public function isProduction() * * @return bool True if we should skip the check, false will proceed. */ - public function skipPermissionsCheck() + public function skipPermissionsCheck(): bool { - return getenv('SKIP_PERMISSION_CHECK') ? true : false; + return env('SKIP_PERMISSION_CHECK', false); } } diff --git a/app/sprinkles/core/src/Util/ClassMapper.php b/app/sprinkles/core/src/Util/ClassMapper.php index c20e3202e..a8eaeee43 100644 --- a/app/sprinkles/core/src/Util/ClassMapper.php +++ b/app/sprinkles/core/src/Util/ClassMapper.php @@ -13,7 +13,7 @@ /** * UserFrosting class mapper. * - * This creates an abstraction layer for overrideable classes. + * This creates an abstraction layer for overridable classes. * For example, if we want to replace usages of the User class with MyUser, this abstraction layer handles that. * * @author Alex Weissman (https://alexanderweissman.com) diff --git a/app/sprinkles/core/src/Util/RawAssetBundles.php b/app/sprinkles/core/src/Util/RawAssetBundles.php index 4d0277fed..7c3181a6f 100644 --- a/app/sprinkles/core/src/Util/RawAssetBundles.php +++ b/app/sprinkles/core/src/Util/RawAssetBundles.php @@ -88,18 +88,18 @@ public function extend($filePath) */ protected function addWithCollisionRule(&$bundle, $bundleName, $collisionRule, &$bundleStore) { - $standardisedBundle = $this->standardiseBundle($bundle); + $standardizedBundle = $this->standardiseBundle($bundle); if (!array_key_exists($bundleName, $bundleStore)) { - $bundleStore[$bundleName] = $standardisedBundle; + $bundleStore[$bundleName] = $standardizedBundle; } else { switch ($collisionRule) { case 'replace': // Replaces the existing bundle. - $bundleStore[$bundleName] = $standardisedBundle; + $bundleStore[$bundleName] = $standardizedBundle; break; case 'merge': // Merge with existing bundle. - foreach ($standardisedBundle as $assetPath) { + foreach ($standardizedBundle as $assetPath) { if (!in_array($assetPath, $bundleStore[$bundleName])) { $bundleStore[$bundleName][] = $assetPath; } diff --git a/app/sprinkles/core/src/Util/VersionValidator.php b/app/sprinkles/core/src/Util/VersionValidator.php new file mode 100644 index 000000000..eb4899acf --- /dev/null +++ b/app/sprinkles/core/src/Util/VersionValidator.php @@ -0,0 +1,189 @@ +setConstraint($constraint)->setVersion($phpVersion); + + throw $exception; + } + + return true; + } + + /** + * Check the minimum version of php. + * This should be done by composer itself, but we do it again for good measure. + * + * @throws VersionCompareException If constraint version is not matched. + * + * @return true Version is valid + */ + public static function validatePhpDeprecation(): bool + { + $phpVersion = static::getPhpVersion(); + $constraint = static::getPhpRecommended(); + + if (!Semver::satisfies($phpVersion, $constraint)) { + $message = 'UserFrosting recommend a PHP version that satisfies "' . $constraint . '". While your PHP version (' . $phpVersion . ') is still supported by UserFrosting, we recommend upgrading as your current version will soon be unsupported. See http://php.net/supported-versions.php for more info.'; + $exception = new VersionCompareException($message); + $exception->setConstraint($constraint)->setVersion($phpVersion); + + throw $exception; + } + + return true; + } + + /** + * Check the minimum version requirement of Node installed. + * + * @throws VersionCompareException If constraint version is not matched. + * + * @return true Version is valid + */ + public static function validateNodeVersion(): bool + { + $nodeVersion = static::getNodeVersion(); + $constraint = static::getNodeConstraint(); + + if (!Semver::satisfies($nodeVersion, $constraint)) { + $message = 'UserFrosting requires a Node version that satisfies "' . $constraint . '", but found ' . $nodeVersion . '. Check the documentation for more details.'; + $exception = new VersionCompareException($message); + $exception->setConstraint($constraint)->setVersion($nodeVersion); + + throw $exception; + } + + return true; + } + + /** + * Check the minimum version requirement for Npm. + * + * @throws VersionCompareException If constraint version is not matched. + * + * @return true Version is valid + */ + public static function validateNpmVersion(): bool + { + $npmVersion = static::getNpmVersion(); + $constraint = static::getNpmConstraint(); + + if (!Semver::satisfies($npmVersion, $constraint)) { + $message = 'UserFrosting requires a NPM version that satisfies "' . $constraint . '", but found ' . $npmVersion . '. Check the documentation for more details.'; + $exception = new VersionCompareException($message); + $exception->setConstraint($constraint)->setVersion($npmVersion); + + throw $exception; + } + + return true; + } + + /** + * Returns system php version. + * Handle non semver compliant version of PHP returned by some OS. + * + * @see https://github.com/composer/semver/issues/125 + * + * @return string + */ + public static function getPhpVersion(): string + { + $version = (string) phpversion(); + $version = preg_replace('#^([^~+-]+).*$#', '$1', $version); + + return $version; + } + + /** + * Returns system Node version. + * + * @return string + */ + public static function getNodeVersion(): string + { + return trim(exec('node -v')); + } + + /** + * Returns system NPM version. + * + * @return string + */ + public static function getNpmVersion(): string + { + return trim(exec('npm -v')); + } + + /** + * Returns the required PHP semver range. + * + * @return string + */ + public static function getPhpConstraint(): string + { + return \UserFrosting\PHP_MIN_VERSION; + } + + /** + * Returns the recommended PHP semver range. + * + * @return string + */ + public static function getPhpRecommended(): string + { + return \UserFrosting\PHP_RECOMMENDED_VERSION; + } + + /** + * Returns the required Node semver range. + * + * @return string + */ + public static function getNodeConstraint(): string + { + return \UserFrosting\NODE_MIN_VERSION; + } + + /** + * Returns the required NPM semver range. + * + * @return string + */ + public static function getNpmConstraint(): string + { + return \UserFrosting\NPM_MIN_VERSION; + } +} diff --git a/app/sprinkles/core/tests/ControllerTestCase.php b/app/sprinkles/core/tests/ControllerTestCase.php deleted file mode 100644 index 2872935b5..000000000 --- a/app/sprinkles/core/tests/ControllerTestCase.php +++ /dev/null @@ -1,37 +0,0 @@ -setupTestDatabase(); - $this->refreshDatabase(); - } -} diff --git a/app/sprinkles/core/tests/Integration/Bakery/data/locale/en_US/messages.php b/app/sprinkles/core/tests/Integration/Bakery/data/locale/en_US/messages.php index 0109fcb74..d9136dd5e 100644 --- a/app/sprinkles/core/tests/Integration/Bakery/data/locale/en_US/messages.php +++ b/app/sprinkles/core/tests/Integration/Bakery/data/locale/en_US/messages.php @@ -13,7 +13,7 @@ 'test' => [ '@TRANSLATION' => 'Test', 'bbb' => 'BBB', - 'ccc' => 'CCC', // Overwriten by "" - 'ddd' => 'ddd', //Overwriten by "DDD" + 'ccc' => 'CCC', // Overwritten by "" + 'ddd' => 'ddd', // Overwritten by "DDD" ], ]; diff --git a/app/sprinkles/core/tests/Integration/Controllers/CoreControllerTest.php b/app/sprinkles/core/tests/Integration/Controllers/CoreControllerTest.php index 33bd93a12..5032cfcf4 100644 --- a/app/sprinkles/core/tests/Integration/Controllers/CoreControllerTest.php +++ b/app/sprinkles/core/tests/Integration/Controllers/CoreControllerTest.php @@ -12,12 +12,15 @@ use UserFrosting\Sprinkle\Core\Controller\CoreController; use UserFrosting\Support\Exception\NotFoundException; +use UserFrosting\Tests\TestCase; /** * Tests CoreController */ -class CoreControllerTest extends ControllerTestCase +class CoreControllerTest extends TestCase { + use withController; + /** * @return CoreController */ diff --git a/app/sprinkles/core/tests/Integration/Database/DatabaseTests.php b/app/sprinkles/core/tests/Integration/Database/DatabaseTests.php index 9ef6a6b86..ec3753366 100644 --- a/app/sprinkles/core/tests/Integration/Database/DatabaseTests.php +++ b/app/sprinkles/core/tests/Integration/Database/DatabaseTests.php @@ -10,10 +10,11 @@ namespace UserFrosting\Sprinkle\Core\Tests\Integration\Database; -use UserFrosting\Tests\TestCase; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Schema\Blueprint; use UserFrosting\Sprinkle\Core\Database\Models\Model; +use UserFrosting\Support\Exception\BadRequestException; +use UserFrosting\Tests\TestCase; class DatabaseTests extends TestCase { @@ -886,6 +887,37 @@ public function testQueryExcludeWildcard() ], $job->toArray()); } + /** + * testFindInt + */ + public function testFindInt() + { + $this->generateTasks(); + $task = EloquentTestTask::findInt(1); + + $this->assertEquals($task, EloquentTestTask::find(1)); + } + + /** + * testFindIntThrowsExceptionOnNull + */ + public function testFindIntThrowsExceptionOnNull() + { + $this->generateTasks(); + $this->expectException(BadRequestException::class); + EloquentTestTask::findInt(null); + } + + /** + * testFindIntThrowsExceptionOnNonInteger + */ + public function testFindIntThrowsExceptionOnNonInteger() + { + $this->generateTasks(); + $this->expectException(BadRequestException::class); + EloquentTestTask::findInt('hi'); + } + /** * Helpers... */ diff --git a/app/sprinkles/core/tests/Integration/Database/Migrator/DatabaseMigratorIntegrationTest.php b/app/sprinkles/core/tests/Integration/Database/Migrator/DatabaseMigratorIntegrationTest.php index db726c354..d58bc8f58 100644 --- a/app/sprinkles/core/tests/Integration/Database/Migrator/DatabaseMigratorIntegrationTest.php +++ b/app/sprinkles/core/tests/Integration/Database/Migrator/DatabaseMigratorIntegrationTest.php @@ -122,7 +122,7 @@ public function testMigrationsCanBeRolledBack() // Make sure the data returned from migrator is accurate. // N.B.: The order returned by the rollback method is ordered by which - // migration was rollbacked first (reversed from the order they where ran up) + // migration was rolled back first (reversed from the order they where ran up) $this->assertEquals(array_reverse($this->locator->getMigrations()), $rolledBack); } @@ -187,25 +187,6 @@ public function testPretendRollback() $this->assertEquals(array_reverse($expected), $rolledBack); } - public function testChangeRepositoryAndDeprecatedClass() - { - // Change the repository so we can test with the DeprecatedMigrationLocatorStub - $locator = new DeprecatedMigrationLocatorStub($this->ci->locator); - $this->migrator->setLocator($locator); - - // Run up. Should also run the seeder - $this->migrator->run(); - $this->assertTrue($this->schema->hasTable('deprecated_table')); - - // Make sure the seeder ran. - // Easiest way to do so it asking the seeder to change the table structure - $this->assertTrue($this->schema->hasColumn('deprecated_table', 'foo')); - - // Rollback - $this->migrator->rollback(); - $this->assertFalse($this->schema->hasTable('deprecated_table')); - } - public function testWithInvalidClass() { // Change the repository so we can test with the InvalidMigrationLocatorStub @@ -234,7 +215,7 @@ public function testDependableMigrations() // Note here the `two` migration has been placed at the bottom even if // it was supposed to be migrated first from the order the locator // returned them. This is because `two` migration depends on `one` migrations - // We only check the last one, we don't care about the order the first two are since they are not dependendent on eachother + // We only check the last one, we don't care about the order the first two are since they are not dependent on each other $this->assertEquals('\\UserFrosting\\Tests\\Integration\\Migrations\\two\\CreateFlightsTable', $migrated[2]); } @@ -261,7 +242,7 @@ public function testDependableMigrationsWithInstalled() public function testUnfulfillableMigrations() { - // Change the repository so we can test with the DeprecatedStub + // Change the repository so we can test with the unfulfillable Stub $locator = new UnfulfillableMigrationLocatorStub($this->ci->locator); $this->migrator->setLocator($locator); @@ -384,17 +365,3 @@ public function getMigrations() ]; } } - -/** - * This stub contain migration which order they need to be run is different - * than the order the file are returned because of dependencies management - */ -class DeprecatedMigrationLocatorStub extends MigrationLocator -{ - public function getMigrations() - { - return [ - '\\UserFrosting\\Tests\\Integration\\Migrations\\DeprecatedClassTable', - ]; - } -} diff --git a/app/sprinkles/core/tests/Integration/Database/Migrator/DatabaseMigratorTest.php b/app/sprinkles/core/tests/Integration/Database/Migrator/DatabaseMigratorTest.php index ff7353aef..27886b059 100644 --- a/app/sprinkles/core/tests/Integration/Database/Migrator/DatabaseMigratorTest.php +++ b/app/sprinkles/core/tests/Integration/Database/Migrator/DatabaseMigratorTest.php @@ -24,7 +24,7 @@ * Tests for the Migrator Class * * Theses tests make sure the Migrator works correctly, without validating - * agaist a simulated database. Those tests are performed by `DatabaseMigratorIntegrationTest` + * against a simulated database. Those tests are performed by `DatabaseMigratorIntegrationTest` * * @author Louis Charette */ @@ -76,7 +76,7 @@ public function setUp(): void $capsule = m::mock(Capsule::class); $this->connection = m::mock(Connection::class); - // Set global expections for $capule and $connection + // Set global expectations for $capsule and $connection $capsule->shouldReceive('getConnection')->andReturn($this->connection); $this->connection->shouldReceive('getSchemaBuilder')->andReturn($this->schema); @@ -85,11 +85,11 @@ public function setUp(): void } /** - * Basic test to make sure the base method syntaxt is ok + * Basic test to make sure the base method syntax is ok */ public function testMigratorUpWithNoMigrations() { - // Locator will be asked to return the avaialble migrations + // Locator will be asked to return the available migrations $this->locator->shouldReceive('getMigrations')->once()->andReturn([]); // Repository will be asked to return the ran migrations @@ -100,7 +100,7 @@ public function testMigratorUpWithNoMigrations() } /** - * Basic test where all avaialble migrations are pending and fulfillable + * Basic test where all available migrations are pending and fulfillable */ public function testMigratorUpWithOnlyPendingMigrations() { @@ -135,7 +135,7 @@ public function testMigratorUpWithOnlyPendingMigrations() } /** - * Test where one of the avaialble migrations is already installed + * Test where one of the available migrations is already installed */ public function testMigratorUpWithOneInstalledMigrations() { @@ -164,7 +164,7 @@ public function testMigratorUpWithOneInstalledMigrations() // Run migrations up $migrations = $this->migrator->run(); - // The migration already ran shoudn't be in the pending ones + // The migration already ran shouldn't be in the pending ones $this->assertEquals([ '\\UserFrosting\\Tests\\Integration\\Migrations\\one\\CreatePasswordResetsTable', '\\UserFrosting\\Tests\\Integration\\Migrations\\two\\CreateFlightsTable', @@ -172,7 +172,7 @@ public function testMigratorUpWithOneInstalledMigrations() } /** - * Test where all avaialble migrations have been ran + * Test where all available migrations have been ran */ public function testMigratorUpWithNoPendingMigrations() { @@ -197,7 +197,7 @@ public function testMigratorUpWithNoPendingMigrations() // Run migrations up $migrations = $this->migrator->run(); - // The migration already ran shoudn't be in the pending ones + // The migration already ran shouldn't be in the pending ones $this->assertEquals([], $migrations); } @@ -217,7 +217,7 @@ public function testMigratorRollbackWithNoInstalledMigrations() // Run migrations up $migrations = $this->migrator->rollback(); - // The migration already ran shoudn't be in the pending ones + // The migration already ran shouldn't be in the pending ones $this->assertEquals([], $migrations); } @@ -252,7 +252,7 @@ public function testMigratorRollbackAllInstalledMigrations() // Run migrations up $migrations = $this->migrator->rollback(); - // The migration already ran shoudn't be in the pending ones + // The migration already ran shouldn't be in the pending ones $this->assertEquals($testMigrations, $migrations); } @@ -287,14 +287,14 @@ public function testMigratorRollbackAllInstalledMigrationsWithOneMissing() // Rollback migrations $migrations = $this->migrator->rollback(); - // The migration not available from the locator shouldn't have been run dowm + // The migration not available from the locator shouldn't have been run $this->assertEquals([ '\\UserFrosting\\Tests\\Integration\\Migrations\\one\\CreatePasswordResetsTable', ], $migrations); } /** - * Test a specific migration with no dependencies can be rollbacked + * Test a specific migration with no dependencies can be rolled back */ public function testMigratorRollbackSpecific() { @@ -330,12 +330,12 @@ public function testMigratorRollbackSpecific() // Rollback only the Flights table. Should work as no other depends on it $rolledback = $this->migrator->rollbackMigration($migration); - // The migration already ran shoudn't be in the pending ones + // The migration already ran shouldn't be in the pending ones $this->assertEquals([$migration], $rolledback); } /** - * Test a specific migration with some dependencies can be rollbacked + * Test a specific migration with some dependencies can be rolled back */ public function testMigratorRollbackSpecificWithDependencies() { @@ -401,7 +401,7 @@ public function testMigratorResetAllInstalledMigrations() $this->connection->shouldReceive('getSchemaGrammar')->andReturn($grammar); $grammar->shouldReceive('supportsSchemaTransactions')->andReturn(false); - // Reset mgirations + // Reset migrations $migrations = $this->migrator->reset(); // All the migrations should have been rolledback diff --git a/app/sprinkles/core/tests/Integration/Database/Migrator/MigrationLocatorTest.php b/app/sprinkles/core/tests/Integration/Database/Migrator/MigrationLocatorTest.php index 144ce51a3..d9b3b248f 100644 --- a/app/sprinkles/core/tests/Integration/Database/Migrator/MigrationLocatorTest.php +++ b/app/sprinkles/core/tests/Integration/Database/Migrator/MigrationLocatorTest.php @@ -91,7 +91,7 @@ public function testGetMigrations() new Resource($resourceStream, $resourceAccountLocation, 'two/CreateFlightsTable.php'), new Resource($resourceStream, $resourceAccountLocation, 'CreateMainTable.php'), - // Theses shoudn't be returned by the migrator + // Theses shouldn't be returned by the migrator new Resource($resourceStream, $resourceAccountLocation, 'README.md'), new Resource($resourceStream, $resourceAccountLocation, 'php.md'), new Resource($resourceStream, $resourceAccountLocation, 'foo.foophp'), @@ -122,7 +122,7 @@ public function testGetMigrations() } /** - * Test MigratonLocator against the real thing, no Mockery + * Test MigrationLocator against the real thing, no Mockery */ public function testActualInstance() { diff --git a/app/sprinkles/core/tests/Integration/Database/Migrator/MigrationRepositoryTest.php b/app/sprinkles/core/tests/Integration/Database/Migrator/MigrationRepositoryTest.php index 9f0e92cc6..d61ae25c8 100644 --- a/app/sprinkles/core/tests/Integration/Database/Migrator/MigrationRepositoryTest.php +++ b/app/sprinkles/core/tests/Integration/Database/Migrator/MigrationRepositoryTest.php @@ -17,7 +17,7 @@ use UserFrosting\Tests\TestCase; /** - * DatabaseMigrationRespository Test + * DatabaseMigrationRepository Test */ class MigrationRepositoryTest extends TestCase { @@ -44,7 +44,7 @@ public function setUp(): void // Create repository instance $this->repository = new DatabaseMigrationRepository($capsule, 'migrations'); - // Set global expections for $capule and $connection + // Set global expectations for $capsule and $connection // Repository -> capsule -> connection -> Schema // When repository call `getConnection`, it will receive the connection mock // When repository call `getSchemaBuilder`, it will receive the schema builder mock @@ -141,7 +141,7 @@ public function testGetNextBatchNumberReturnsLastBatchNumberPlusOne() public function testCreateRepositoryCreatesProperDatabaseTable() { - // Setup expectations for SchemaBuilder. When asked to create the repository, the schema should reeceive the create command + // Setup expectations for SchemaBuilder. When asked to create the repository, the schema should receive the create command $this->repository->getSchemaBuilder()->shouldReceive('create')->once()->with('migrations', m::type('Closure')); $this->repository->createRepository(); } diff --git a/app/sprinkles/core/tests/Integration/Migrations/DeprecatedClassTable.php b/app/sprinkles/core/tests/Integration/Migrations/DeprecatedClassTable.php deleted file mode 100644 index 19055ed5f..000000000 --- a/app/sprinkles/core/tests/Integration/Migrations/DeprecatedClassTable.php +++ /dev/null @@ -1,47 +0,0 @@ -schema->create('deprecated_table', function (Blueprint $table) { - $table->string('email')->index(); - $table->string('token')->index(); - $table->timestamp('created_at'); - }); - } - - /** - * Reverse the migrations. - */ - public function down() - { - $this->schema->dropIfExists('deprecated_table'); - } - - /** - * Seed the database. - */ - public function seed() - { - $this->schema->table('deprecated_table', function (Blueprint $table) { - $table->string('foo')->nullable(); - }); - } -} diff --git a/app/sprinkles/core/tests/Integration/Migrations/UnfulfillableTable.php b/app/sprinkles/core/tests/Integration/Migrations/UnfulfillableTable.php index cbaf5bb0d..91d88cf0a 100644 --- a/app/sprinkles/core/tests/Integration/Migrations/UnfulfillableTable.php +++ b/app/sprinkles/core/tests/Integration/Migrations/UnfulfillableTable.php @@ -14,7 +14,7 @@ use UserFrosting\Sprinkle\Core\Database\Migration; /** - * This migration is not fulfulable because it's dependencie are not met ! + * This migration is not fulfillable because it's dependencies are not met ! */ class UnfulfillableTable extends Migration { diff --git a/app/sprinkles/core/tests/Integration/Migrations/one/CreateUsersTable.php b/app/sprinkles/core/tests/Integration/Migrations/one/CreateUsersTable.php index a5aed5a61..11445ce3b 100644 --- a/app/sprinkles/core/tests/Integration/Migrations/one/CreateUsersTable.php +++ b/app/sprinkles/core/tests/Integration/Migrations/one/CreateUsersTable.php @@ -17,10 +17,8 @@ class CreateUsersTable extends Migration { /** * {@inheritdoc} - * - * N.B.: Not using static here to test old deprecated behavior */ - public $dependencies = []; + public static $dependencies = []; /** * Run the migrations. diff --git a/app/sprinkles/core/tests/Integration/Session/SessionDatabaseHandlerTest.php b/app/sprinkles/core/tests/Integration/Session/SessionDatabaseHandlerTest.php index 20f9e76e9..71137c1ef 100644 --- a/app/sprinkles/core/tests/Integration/Session/SessionDatabaseHandlerTest.php +++ b/app/sprinkles/core/tests/Integration/Session/SessionDatabaseHandlerTest.php @@ -36,7 +36,7 @@ public function setUp(): void } /** - * Test session table connection & existance + * Test session table connection & existence */ public function testSessionTable() { @@ -44,7 +44,7 @@ public function testSessionTable() $config = $this->ci->config; $table = $config['session.database.table']; - // Check connexion is ok and returns what's expected from DatabaseSessionHandler + // Check connection is ok and returns what's expected from DatabaseSessionHandler $this->assertInstanceOf(\Illuminate\Database\ConnectionInterface::class, $connection); $this->assertInstanceOf(\Illuminate\Database\Query\Builder::class, $connection->table($table)); diff --git a/app/sprinkles/core/tests/Integration/Twig/CoreExtensionTest.php b/app/sprinkles/core/tests/Integration/Twig/CoreExtensionTest.php index 550b8cfb1..5d231a514 100644 --- a/app/sprinkles/core/tests/Integration/Twig/CoreExtensionTest.php +++ b/app/sprinkles/core/tests/Integration/Twig/CoreExtensionTest.php @@ -17,7 +17,7 @@ /** * CoreExtensionTest class. - * Tests Core twig extentions + * Tests Core twig extensions */ class CoreExtensionTest extends TestCase { @@ -67,16 +67,16 @@ public function testPhoneFilter(): void public function testUnescapeFilter(): void { $string = "I'll \"walk\" the dog now"; - $this->assertNotSame($string, $this->ci->view->fetchFromString("{{ foo }}", ['foo' => htmlentities($string)])); - $this->assertNotSame($string, $this->ci->view->fetchFromString("{{ foo|unescape }}", ['foo' => htmlentities($string)])); - $this->assertNotSame($string, $this->ci->view->fetchFromString("{{ foo|raw }}", ['foo' => htmlentities($string)])); - $this->assertSame($string, $this->ci->view->fetchFromString("{{ foo|unescape|raw }}", ['foo' => htmlentities($string)])); + $this->assertNotSame($string, $this->ci->view->fetchFromString('{{ foo }}', ['foo' => htmlentities($string)])); + $this->assertNotSame($string, $this->ci->view->fetchFromString('{{ foo|unescape }}', ['foo' => htmlentities($string)])); + $this->assertNotSame($string, $this->ci->view->fetchFromString('{{ foo|raw }}', ['foo' => htmlentities($string)])); + $this->assertSame($string, $this->ci->view->fetchFromString('{{ foo|unescape|raw }}', ['foo' => htmlentities($string)])); } public function testCurrentLocaleGlobal(): void { $this->ci->locale = Mockery::mock(SiteLocale::class)->shouldReceive('getLocaleIndentifier')->once()->andReturn('zz-ZZ')->getMock(); - $this->assertSame('zz-ZZ', $this->ci->view->fetchFromString("{{ currentLocale }}")); + $this->assertSame('zz-ZZ', $this->ci->view->fetchFromString('{{ currentLocale }}')); } } diff --git a/app/sprinkles/core/tests/Integration/Util/CheckEnvironmentTest.php b/app/sprinkles/core/tests/Integration/Util/CheckEnvironmentTest.php index 140dc2e93..272e2ed1f 100644 --- a/app/sprinkles/core/tests/Integration/Util/CheckEnvironmentTest.php +++ b/app/sprinkles/core/tests/Integration/Util/CheckEnvironmentTest.php @@ -14,7 +14,7 @@ use UserFrosting\Tests\TestCase; /** - * CheckEnvironement Test + * CheckEnvironment Test */ class CheckEnvironmentTest extends TestCase { diff --git a/app/sprinkles/core/tests/Unit/Util/VersionValidatorTest.php b/app/sprinkles/core/tests/Unit/Util/VersionValidatorTest.php new file mode 100644 index 000000000..684faa211 --- /dev/null +++ b/app/sprinkles/core/tests/Unit/Util/VersionValidatorTest.php @@ -0,0 +1,200 @@ +getNamespaceName(); + $mock = $this->getFunctionMock($namespace, 'phpversion'); + $mock->expects($this->any())->willReturn($version); + + // Assert GetPHPVersion + $this->assertSame($sanitized, VersionValidator::getPhpVersion()); + + // Assert validatePhpVersion + if ($valid) { + $this->assertTrue(VersionValidator::validatePhpVersion()); + } else { + try { + VersionValidator::validatePhpVersion(); + } catch (VersionCompareException $e) { + $this->assertSame(VersionValidator::getPhpConstraint(), $e->getConstraint()); + $this->assertSame($sanitized, $e->getVersion()); + + return; + } + + $this->fail(); + } + + // Assert validatePhpDeprecation + if (!$deprecated) { + $this->assertTrue(VersionValidator::validatePhpDeprecation()); + } else { + try { + VersionValidator::validatePhpDeprecation(); + } catch (VersionCompareException $e) { + $this->assertSame(VersionValidator::getPhpRecommended(), $e->getConstraint()); + $this->assertSame($sanitized, $e->getVersion()); + + return; + } + + $this->fail(); + } + } + + /** + * Assert Node related methods. + * + * @dataProvider nodeVersionProvider + * @runInSeparateProcess + * + * @param string $version + * @param string $sanitized + * @param bool $valid + */ + public function testNode(string $version, string $sanitized, bool $valid): void + { + // Mock `exec` function + $class = new \ReflectionClass(VersionValidator::class); + $namespace = $class->getNamespaceName(); + $mock = $this->getFunctionMock($namespace, 'exec'); + $mock->expects($this->any())->willReturn($version); + + // Assert getNodeVersion + $this->assertSame($sanitized, VersionValidator::getNodeVersion()); + + // Assert validateNodeVersion + if ($valid) { + $this->assertTrue(VersionValidator::validateNodeVersion()); + } else { + try { + VersionValidator::validateNodeVersion(); + } catch (VersionCompareException $e) { + $this->assertSame(VersionValidator::getNodeConstraint(), $e->getConstraint()); + $this->assertSame($sanitized, $e->getVersion()); + + return; + } + + $this->fail(); + } + } + + /** + * Assert Npm related methods. + * + * @dataProvider npmVersionProvider + * @runInSeparateProcess + * + * @param string $version + * @param string $sanitized + * @param bool $valid + */ + public function testNpm(string $version, string $sanitized, bool $valid): void + { + // Mock `exec` function + $class = new \ReflectionClass(VersionValidator::class); + $namespace = $class->getNamespaceName(); + $mock = $this->getFunctionMock($namespace, 'exec'); + $mock->expects($this->any())->willReturn($version); + + // Assert getNpmVersion + $this->assertSame($sanitized, VersionValidator::getNpmVersion()); + + // Assert validateNpmVersion + if ($valid) { + $this->assertTrue(VersionValidator::validateNpmVersion()); + } else { + try { + VersionValidator::validateNpmVersion(); + } catch (VersionCompareException $e) { + $this->assertSame(VersionValidator::getNpmConstraint(), $e->getConstraint()); + $this->assertSame($sanitized, $e->getVersion()); + + return; + } + + $this->fail(); + } + } + + /** + * PHP version provider. + * + * @return array [version, sanitized, valid, deprecated] + */ + public function phpVersionProvider(): array + { + return [ + ['7.1.4', '7.1.4', false, true], + ['7.2.1', '7.2.1', true, true], + ['7.2', '7.2', true, true], + ['7.3.14', '7.3.14', true, true], + ['7.4.13', '7.4.13', true, false], + ['7.2.34-18+ubuntu20.04.1+deb.sury.org+1', '7.2.34', true, true], + ]; + } + + /** + * Node version provider. + * + * @return array [version, sanitized, valid] + */ + public function nodeVersionProvider(): array + { + return [ + ['v12.18.1', 'v12.18.1', true], + ['v13.12.3', 'v13.12.3', false], + ['v14.0.0 ', 'v14.0.0', true], // Test trim here + ]; + } + + /** + * Node version provider. + * + * @return array [version, sanitized, valid] + */ + public function npmVersionProvider(): array + { + return [ + [' 6.14.10 ', '6.14.10', true], // Trim + ['6.14.4', '6.14.4', true], + ['5.12.14', '5.12.14', false], + ]; + } +} diff --git a/app/sprinkles/core/tests/withDatabaseSessionHandler.php b/app/sprinkles/core/tests/withDatabaseSessionHandler.php index d92e1053f..343f6461a 100644 --- a/app/sprinkles/core/tests/withDatabaseSessionHandler.php +++ b/app/sprinkles/core/tests/withDatabaseSessionHandler.php @@ -25,7 +25,7 @@ trait withDatabaseSessionHandler public function useDatabaseSessionHandler() { // Skip test if using in-memory database. - // However we tell UF to use database session handler and in-memroy + // However we tell UF to use database session handler and in-memory // database, the session will always be created before the db can be // migrate, causing "table not found" errors if ($this->usingInMemoryDatabase()) { diff --git a/app/system/Bakery/BaseCommand.php b/app/system/Bakery/BaseCommand.php index e91fe136a..3ce30f816 100644 --- a/app/system/Bakery/BaseCommand.php +++ b/app/system/Bakery/BaseCommand.php @@ -62,7 +62,7 @@ protected function isProduction(): bool { // Need to touch the config service first to load dotenv values $config = $this->ci->config; - $mode = getenv('UF_MODE') ?: ''; + $mode = env('UF_MODE', ''); return $mode === 'production'; } diff --git a/app/system/Bakery/Migration.php b/app/system/Bakery/Migration.php deleted file mode 100644 index 3eacdef35..000000000 --- a/app/system/Bakery/Migration.php +++ /dev/null @@ -1,42 +0,0 @@ -assertTrue(true); - } } diff --git a/app/system/Sprinkle/SprinkleManager.php b/app/system/Sprinkle/SprinkleManager.php index 3a77cc832..516b8ddc8 100644 --- a/app/system/Sprinkle/SprinkleManager.php +++ b/app/system/Sprinkle/SprinkleManager.php @@ -31,7 +31,7 @@ class SprinkleManager protected $ci; /** - * @var Sprinkle[] An array of sprinkles. + * @var (Sprinkle|null)[] An array of sprinkles : array. */ protected $sprinkles = []; @@ -54,7 +54,7 @@ public function __construct(ContainerInterface $ci) * Register resource streams for all base sprinkles. * For each sprinkle, register its resources and then run its initializer. */ - public function addResources() + public function addResources(): void { foreach ($this->sprinkles as $sprinkleName => $sprinkle) { $this->addSprinkleResources($sprinkleName); @@ -66,7 +66,7 @@ public function addResources() * * @param string $sprinkleName */ - public function addSprinkleResources($sprinkleName) + public function addSprinkleResources(string $sprinkleName): void { /** @var \UserFrosting\UniformResourceLocator\ResourceLocator $locator */ $locator = $this->ci->locator; @@ -80,7 +80,7 @@ public function addSprinkleResources($sprinkleName) * * @return string */ - public function getSprinklePath($sprinkleName) + public function getSprinklePath(string $sprinkleName): string { // Get Sprinkle and make sure it exist $sprinkle = $this->getSprinkle($sprinkleName); @@ -104,7 +104,7 @@ public function getSprinklePath($sprinkleName) * * @return string */ - protected function getSprinkleClass($sprinkleName) + protected function getSprinkleClass(string $sprinkleName): string { $className = Str::studly($sprinkleName); @@ -118,7 +118,7 @@ protected function getSprinkleClass($sprinkleName) * * @return string The Sprinkle Namespace */ - public function getSprinkleClassNamespace($sprinkleName) + public function getSprinkleClassNamespace(string $sprinkleName): string { $className = Str::studly($sprinkleName); @@ -132,7 +132,7 @@ public function getSprinkleClassNamespace($sprinkleName) * * @return string */ - protected function getSprinkleDefaultServiceProvider($sprinkleName) + protected function getSprinkleDefaultServiceProvider(string $sprinkleName): string { return $this->getSprinkleClassNamespace($sprinkleName) . '\\ServicesProvider\\ServicesProvider'; } @@ -145,9 +145,9 @@ protected function getSprinkleDefaultServiceProvider($sprinkleName) * * @param string $sprinkleName The name of the Sprinkle to initialize. * - * @return mixed Sprinkle class instance or null if no such class exist + * @return Sprinkle|null Sprinkle class instance or null if no such class exist */ - public function bootSprinkle($sprinkleName) + public function bootSprinkle(string $sprinkleName): ?Sprinkle { $fullClassName = $this->getSprinkleClass($sprinkleName); @@ -155,9 +155,13 @@ public function bootSprinkle($sprinkleName) if (class_exists($fullClassName)) { $sprinkle = new $fullClassName($this->ci); + if (!$sprinkle instanceof Sprinkle) { + throw new SprinkleClassException("$fullClassName must be an instance of " . Sprinkle::class); + } + return $sprinkle; } else { - return; + return null; } } @@ -166,7 +170,7 @@ public function bootSprinkle($sprinkleName) * * @return string[] */ - public function getSprinkleNames() + public function getSprinkleNames(): array { return array_keys($this->sprinkles); } @@ -174,9 +178,9 @@ public function getSprinkleNames() /** * Returns a list of available sprinkles. * - * @return Sprinkle[] + * @return (Sprinkle|null)[] */ - public function getSprinkles() + public function getSprinkles(): array { return $this->sprinkles; } @@ -187,7 +191,7 @@ public function getSprinkles() * * @param string[] $sprinkleNames */ - public function init(array $sprinkleNames) + public function init(array $sprinkleNames): void { foreach ($sprinkleNames as $sprinkleName) { $sprinkle = $this->bootSprinkle($sprinkleName); @@ -206,9 +210,9 @@ public function init(array $sprinkleNames) * * @param string $schemaPath */ - public function initFromSchema($schemaPath) + public function initFromSchema(string $schemaPath): void { - $baseSprinkleNames = $this->loadSchema($schemaPath)->base; + $baseSprinkleNames = $this->loadSchema($schemaPath); $this->init($baseSprinkleNames); } @@ -220,7 +224,7 @@ public function initFromSchema($schemaPath) * * @return bool */ - public function isAvailable($sprinkleName) + public function isAvailable(string $sprinkleName): bool { return (bool) $this->getSprinkle($sprinkleName); } @@ -232,7 +236,7 @@ public function isAvailable($sprinkleName) * * @return string|false Return sprinkle name or false if sprinkle not found */ - public function getSprinkle($sprinkleName) + public function getSprinkle(string $sprinkleName) { $mathches = preg_grep("/^$sprinkleName$/i", $this->getSprinkleNames()); @@ -246,7 +250,7 @@ public function getSprinkle($sprinkleName) /** * Interate through the list of loaded Sprinkles, and invoke their ServiceProvider classes. */ - public function registerAllServices() + public function registerAllServices(): void { foreach ($this->getSprinkleNames() as $sprinkleName) { $this->registerServices($sprinkleName); @@ -258,7 +262,7 @@ public function registerAllServices() * * @param string $sprinkleName */ - public function registerServices($sprinkleName) + public function registerServices(string $sprinkleName): void { //Register the default services $fullClassName = $this->getSprinkleDefaultServiceProvider($sprinkleName); @@ -281,7 +285,7 @@ public function registerServices($sprinkleName) * * @return string */ - public function getSprinklesPath() + public function getSprinklesPath(): string { return $this->sprinklesPath; } @@ -293,7 +297,7 @@ public function getSprinklesPath() * * @return static */ - public function setSprinklesPath($sprinklesPath) + public function setSprinklesPath(string $sprinklesPath) { $this->sprinklesPath = $sprinklesPath; @@ -307,7 +311,7 @@ public function setSprinklesPath($sprinklesPath) * * @return string[] */ - protected function loadSchema($schemaPath) + protected function loadSchema(string $schemaPath): array { $sprinklesFile = @file_get_contents($schemaPath); @@ -324,6 +328,14 @@ protected function loadSchema($schemaPath) throw new JsonException($errorMessage); } - return $data; + // Remove duplicates, keep the first one + // @see https://stackoverflow.com/a/2276400/445757 + $sprinkles = $data->base; + $sprinkles = array_intersect_key( + $sprinkles, + array_unique(array_map('strtolower', $sprinkles)) + ); + + return $sprinkles; } } diff --git a/app/system/UserFrosting.php b/app/system/UserFrosting.php index 20b378032..d16e96552 100644 --- a/app/system/UserFrosting.php +++ b/app/system/UserFrosting.php @@ -63,7 +63,7 @@ public function __construct(bool $cli = false) */ public function fireEvent($eventName, Event $event = null) { - /** @var EventDispatcher $events */ + /** @var EventDispatcher */ $eventDispatcher = $this->ci->eventDispatcher; return $eventDispatcher->dispatch($eventName, $event); @@ -74,7 +74,7 @@ public function fireEvent($eventName, Event $event = null) * * @return App */ - public function getApp() + public function getApp(): App { return $this->app; } @@ -84,7 +84,7 @@ public function getApp() * * @return Container */ - public function getContainer() + public function getContainer(): Container { return $this->ci; } @@ -92,7 +92,7 @@ public function getContainer() /** * Initialize the application. Set up Sprinkles and the Slim app, define routes, register global middleware, and run Slim. */ - public function run() + public function run(): void { $this->app->run(); } @@ -100,13 +100,14 @@ public function run() /** * Register system services, load all sprinkles, and add their resources and services. */ - protected function setupSprinkles() + protected function setupSprinkles(): void { // Register system services $serviceProvider = new ServicesProvider(); $serviceProvider->register($this->ci); // Boot the Sprinkle manager, which creates Sprinkle classes and subscribes them to the event dispatcher + /** @var \UserFrosting\System\Sprinkle\SprinkleManager */ $sprinkleManager = $this->ci->sprinkleManager; try { @@ -133,7 +134,7 @@ protected function setupSprinkles() /** * Setup UserFrosting App, load sprinkles, load routes, etc. */ - protected function setupApp() + protected function setupApp(): void { // Setup sprinkles $this->setupSprinkles(); @@ -157,7 +158,7 @@ protected function setupApp() * * @param string $errorMessage Message to display [Default ""] */ - protected function renderSprinkleErrorPage($errorMessage = '') + protected function renderSprinkleErrorPage(string $errorMessage = '') { ob_clean(); $title = 'UserFrosting Application Error'; @@ -181,7 +182,7 @@ protected function renderSprinkleErrorPage($errorMessage = '') * * @param string $errorMessage Message to display [Default ""] */ - protected function renderSprinkleErrorCli($errorMessage = '') + protected function renderSprinkleErrorCli(string $errorMessage = '') { exit($errorMessage . PHP_EOL); } diff --git a/app/tests/DatabaseTransactions.php b/app/tests/DatabaseTransactions.php deleted file mode 100644 index 5f914c186..000000000 --- a/app/tests/DatabaseTransactions.php +++ /dev/null @@ -1,49 +0,0 @@ -ci->db; - - foreach ($this->connectionsToTransact() as $name) { - $database->connection($name)->beginTransaction(); - } - - $this->beforeApplicationDestroyed(function () use ($database) { - foreach ($this->connectionsToTransact() as $name) { - $database->connection($name)->rollBack(); - } - }); - } - - /** - * The database connections that should have transactions. - * - * @return array - */ - protected function connectionsToTransact() - { - return property_exists($this, 'connectionsToTransact') - ? $this->connectionsToTransact : [null]; - } -} diff --git a/app/tests/TestCase.php b/app/tests/TestCase.php index c5b5ce077..c3fee8925 100644 --- a/app/tests/TestCase.php +++ b/app/tests/TestCase.php @@ -58,8 +58,6 @@ protected function setUp(): void $this->refreshApplication(); } - $this->setUpTraits(); - foreach ($this->afterApplicationCreatedCallbacks as $callback) { call_user_func($callback); } @@ -67,21 +65,6 @@ protected function setUp(): void $this->setUpHasRun = true; } - /** - * Boot the testing helper traits. - * - * @deprecated - */ - protected function setUpTraits() - { - $uses = array_flip(class_uses_recursive(static::class)); - - if (isset($uses[DatabaseTransactions::class])) { - Debug::warning("`UserFrosting\Tests\DatabaseTransactions` has been deprecated and will be removed in future versions. Please use `UserFrosting\Sprinkle\Core\Tests\DatabaseTransactions` class instead."); - $this->beginDatabaseTransaction(); - } - } - /** * Refresh the application instance. */ diff --git a/app/tests/Unit/SprinkleManagerTest.php b/app/tests/Unit/SprinkleManagerTest.php index 6c74dc92b..efcd202bc 100644 --- a/app/tests/Unit/SprinkleManagerTest.php +++ b/app/tests/Unit/SprinkleManagerTest.php @@ -12,6 +12,10 @@ use Psr\Container\ContainerInterface; use Mockery as m; +use UserFrosting\Support\Exception\FileNotFoundException; +use UserFrosting\Support\Exception\JsonException; +use UserFrosting\System\Sprinkle\Sprinkle; +use UserFrosting\System\Sprinkle\SprinkleClassException; use UserFrosting\Tests\TestCase; use UserFrosting\System\Sprinkle\SprinkleManager; @@ -20,6 +24,9 @@ class SprinkleManagerTest extends TestCase /** @var ContainerInterface $fakeCi Our mocked CI used for testing */ protected $fakeCi; + /** @var string */ + protected $basePath = __DIR__ . '/data/'; + public function setUp(): void { // We don't call parent function to cancel CI creation and get accurate test coverage @@ -27,9 +34,6 @@ public function setUp(): void $this->fakeCi = m::mock(ContainerInterface::class); $this->fakeCi->eventDispatcher = new eventDispatcherStub(); $this->fakeCi->locator = new ResourceLocatorStub(); - - // Setup test sprinkle mock class so it can be found by `class_exist` - m::mock('UserFrosting\Sprinkle\Test\Test'); } public function tearDown(): void @@ -41,7 +45,7 @@ public function tearDown(): void /** * @return SprinkleManager */ - public function testConstructor() + public function testConstructor(): SprinkleManager { $sprinkleManager = new SprinkleManager($this->fakeCi); $this->assertInstanceOf(SprinkleManager::class, $sprinkleManager); @@ -53,7 +57,7 @@ public function testConstructor() * @depends testConstructor * @param SprinkleManager $sprinkleManager */ - public function testGetSetSprinklesPath(SprinkleManager $sprinkleManager) + public function testGetSetSprinklesPath(SprinkleManager $sprinkleManager): void { $sprinkleManager->setSprinklesPath('/foo'); $this->assertSame('/foo', $sprinkleManager->getSprinklesPath()); @@ -62,27 +66,48 @@ public function testGetSetSprinklesPath(SprinkleManager $sprinkleManager) /** * @depends testConstructor */ - public function testLoadSprinkleWithNonExistingFile() + public function testLoadSprinkleWithNonExistingFile(): void { $sprinkleManager = new SprinkleManager($this->fakeCi); - $this->expectException(\UserFrosting\Support\Exception\FileNotFoundException::class); + $this->expectException(FileNotFoundException::class); $sprinkleManager->initFromSchema('foo.json'); } + /** + * Will test the instanceof sprinkle check is done + that the right class + * is gnerated with the exception message assertion + * + * @depends testConstructor + * @param SprinkleManager $sprinkleManager + */ + public function testGetSprinklesWihoutMockClass(SprinkleManager $sprinkleManager): void + { + // Setup test sprinkle mock class so it can be found by `class_exist` + class_alias(BlahSprinkleStub::class, 'UserFrosting\Sprinkle\Blah\Blah'); + + $this->expectException(SprinkleClassException::class); + $this->expectExceptionMessage("UserFrosting\Sprinkle\Blah\Blah must be an instance of UserFrosting\System\Sprinkle\Sprinkle"); + $sprinkleManager->bootSprinkle('blah'); + } + /** * @depends testConstructor * @param SprinkleManager $sprinkleManager * @return SprinkleManager */ - public function testGetSprinkles(SprinkleManager $sprinkleManager) + public function testGetSprinkles(SprinkleManager $sprinkleManager): SprinkleManager { + // Setup test sprinkle class alias so it can be found by `class_exist` + class_alias(TestSprinkleStub::class, 'UserFrosting\Sprinkle\Test\Test'); + $sprinkleManager->initFromSchema(__DIR__ . '/data/sprinkles.json'); + $sprinkles = $sprinkleManager->getSprinkles(); - $this->assertEquals([ - 'foo' => null, - 'bar' => null, - 'test' => new \UserFrosting\Sprinkle\Test\Test(), - ], $sprinkles); + $this->assertIsArray($sprinkles); + $this->assertCount(3, $sprinkles); + $this->assertNull($sprinkles['foo']); + $this->assertNull($sprinkles['bar']); + $this->assertInstanceOf(Sprinkle::class, $sprinkles['test']); return $sprinkleManager; } @@ -91,7 +116,7 @@ public function testGetSprinkles(SprinkleManager $sprinkleManager) * @depends testGetSprinkles * @param SprinkleManager $sprinkleManager */ - public function testGetSprinkleNames(SprinkleManager $sprinkleManager) + public function testGetSprinkleNames(SprinkleManager $sprinkleManager): void { $sprinkles = $sprinkleManager->getSprinkleNames(); $this->assertSame(['foo', 'bar', 'test'], $sprinkles); @@ -119,7 +144,7 @@ public function testGetSprinkleNames(SprinkleManager $sprinkleManager) * ["barfoo", false] * ["blah", false] */ - public function testIsAvailable($sprinkleName, $isAvailable, SprinkleManager $sprinkleManager) + public function testIsAvailable($sprinkleName, $isAvailable, SprinkleManager $sprinkleManager): void { $this->assertSame($isAvailable, $sprinkleManager->isAvailable($sprinkleName)); } @@ -132,11 +157,10 @@ public function testIsAvailable($sprinkleName, $isAvailable, SprinkleManager $sp * ["bar"] * ["test"] */ - public function testGetSprinklePath($sprinkleName, SprinkleManager $sprinkleManager) + public function testGetSprinklePath($sprinkleName, SprinkleManager $sprinkleManager): void { - $basePath = 'app/tests/Unit/data/'; - $sprinkleManager->setSprinklesPath($basePath); - $this->assertSame($basePath . $sprinkleName, $sprinkleManager->getSprinklePath($sprinkleName)); + $sprinkleManager->setSprinklesPath($this->basePath); + $this->assertSame($this->basePath . $sprinkleName, $sprinkleManager->getSprinklePath($sprinkleName)); } /** @@ -144,12 +168,11 @@ public function testGetSprinklePath($sprinkleName, SprinkleManager $sprinkleMana * @depends testGetSprinklePath * @param SprinkleManager $sprinkleManager */ - public function testGetSprinklePathWherePathDoesntExist(SprinkleManager $sprinkleManager) + public function testGetSprinklePathWherePathDoesntExist(SprinkleManager $sprinkleManager): void { - $basePath = 'app/tests/Unit/foo/'; - $sprinkleManager->setSprinklesPath($basePath); + $sprinkleManager->setSprinklesPath(__DIR__ . '/foo/'); - $this->expectException(\UserFrosting\Support\Exception\FileNotFoundException::class); + $this->expectException(FileNotFoundException::class); $sprinkleManager->getSprinklePath('foo'); } @@ -158,9 +181,9 @@ public function testGetSprinklePathWherePathDoesntExist(SprinkleManager $sprinkl * @depends testGetSprinklePath * @param SprinkleManager $sprinkleManager */ - public function testGetSprinklePathWhereSprinkleDoesntExist(SprinkleManager $sprinkleManager) + public function testGetSprinklePathWhereSprinkleDoesntExist(SprinkleManager $sprinkleManager): void { - $this->expectException(\UserFrosting\Support\Exception\FileNotFoundException::class); + $this->expectException(FileNotFoundException::class); $sprinkleManager->getSprinklePath('blah'); } @@ -168,15 +191,15 @@ public function testGetSprinklePathWhereSprinkleDoesntExist(SprinkleManager $spr * @depends testGetSprinkles * @param SprinkleManager $sprinkleManager */ - public function testRegisterAllServices(SprinkleManager $sprinkleManager) + public function testRegisterAllServices(SprinkleManager $sprinkleManager): void { // Set Expectations for test sprinkle ServiceProvider // @see https://stackoverflow.com/a/13390001/445757 $this->getMockBuilder('nonexistant') - ->setMockClassName('foo') + ->setMockClassName('FooService') // MockClassName doesn't accept namespace ->setMethods(['register']) ->getMock(); - class_alias('foo', 'UserFrosting\Sprinkle\Test\ServicesProvider\ServicesProvider'); + class_alias('FooService', 'UserFrosting\Sprinkle\Test\ServicesProvider\ServicesProvider'); $this->assertNull($sprinkleManager->registerAllServices()); } @@ -186,10 +209,9 @@ class_alias('foo', 'UserFrosting\Sprinkle\Test\ServicesProvider\ServicesProvider * @depends testGetSprinklePath * @param SprinkleManager $sprinkleManager */ - public function testAddResources(SprinkleManager $sprinkleManager) + public function testAddResources(SprinkleManager $sprinkleManager): void { - $basePath = 'app/tests/Unit/data/'; - $sprinkleManager->setSprinklesPath($basePath); + $sprinkleManager->setSprinklesPath($this->basePath); $this->assertNull($sprinkleManager->addResources()); } @@ -200,7 +222,7 @@ public function testAddResources(SprinkleManager $sprinkleManager) * @depends testConstructor * @depends testGetSprinkles */ - public function testLoadSprinkleWithTxtFile() + public function testLoadSprinkleWithTxtFile(): void { $sprinkleManager = new SprinkleManager($this->fakeCi); $sprinkleManager->initFromSchema(__DIR__ . '/data/sprinkles.txt'); @@ -210,10 +232,10 @@ public function testLoadSprinkleWithTxtFile() /** * @depends testConstructor */ - public function testLoadSprinkleWithBadJson() + public function testLoadSprinkleWithBadJson(): void { $sprinkleManager = new SprinkleManager($this->fakeCi); - $this->expectException(\UserFrosting\Support\Exception\JsonException::class); + $this->expectException(JsonException::class); $sprinkleManager->initFromSchema(__DIR__ . '/data/sprinkles-bad.json'); } @@ -221,17 +243,31 @@ public function testLoadSprinkleWithBadJson() * @depends testConstructor * @depends testIsAvailable */ - public function testLoadSprinkleWithDuplicateSprinkles() + public function testLoadSprinkleWithDuplicateSprinkles(): void { - $sprinkleManager = new SprinkleManager($this->fakeCi); + $sprinkleManager = m::mock(SprinkleManager::class, [$this->fakeCi])->makePartial(); + + // Make sure the sprinkles are not booted twice + $sprinkleManager->shouldReceive('bootSprinkle')->with('foo')->once(); + $sprinkleManager->shouldReceive('bootSprinkle')->with('bar')->once(); + + $sprinkleManager->setSprinklesPath($this->basePath); $sprinkleManager->initFromSchema(__DIR__ . '/data/sprinkles-duplicate.json'); + $sprinkles = $sprinkleManager->getSprinkles(); + $this->assertEquals([ 'foo' => null, - 'FOO' => null, 'bar' => null, - ], $sprinkleManager->getSprinkles()); + ], $sprinkles); + // Test isAvailable work with only the correct case. + $this->assertTrue($sprinkleManager->isAvailable('foo')); $this->assertTrue($sprinkleManager->isAvailable('Foo')); + $this->assertTrue($sprinkleManager->isAvailable('FOO')); + + // Test getSprinklePath get the right case + $this->assertsame(__DIR__ . '/data/bar', $sprinkleManager->getSprinklePath('bar')); + $this->assertsame(__DIR__ . '/data/foo', $sprinkleManager->getSprinklePath('foo')); } } @@ -248,3 +284,11 @@ public function registerLocation() { } } + +class TestSprinkleStub extends Sprinkle +{ +} + +class BlahSprinkleStub +{ +} \ No newline at end of file diff --git a/build/package.json b/build/package.json index c2215a0bc..4bdfec1d6 100755 --- a/build/package.json +++ b/build/package.json @@ -5,31 +5,32 @@ "@userfrosting/gulp-bundle-assets": "^4.0.1", "@userfrosting/merge-package-dependencies": "^2.1.0", "@userfrosting/vinyl-fs-vpath": "^2.0.0", - "bower": "^1.8.8", + "@yarnpkg/shell": "^2.4.1", + "bower": "^1.8.12", "del": "^6.0.0", "dotenv": "^8.2.0", "esm": "^3.2.25", "gulp": "^4.0.2", - "gulp-clean-css": "^4.2.0", + "gulp-clean-css": "^4.3.0", "gulp-concat": "^2.6.1", "gulp-concat-css": "^3.1.0", "gulp-if": "^3.0.0", "gulp-prune": "^0.2.0", "gulp-rev": "^9.0.0", - "gulp-sourcemaps": "^2.6.5", - "gulp-terser": "^1.2.0", + "gulp-sourcemaps": "^3.0.0", + "gulp-terser": "^2.0.1", "gulplog": "^1.0.0", "strip-ansi": "^6.0.0" }, "scripts": { - "uf-bundle": "./node_modules/.bin/gulp build", - "uf-assets-install": "./node_modules/.bin/gulp assetsInstall", - "uf-frontend": "./node_modules/.bin/gulp frontend", - "uf-clean": "./node_modules/.bin/gulp clean" + "uf-bundle": "shell './node_modules/.bin/gulp build'", + "uf-assets-install": "shell './node_modules/.bin/gulp assetsInstall'", + "uf-frontend": "shell './node_modules/.bin/gulp frontend'", + "uf-clean": "shell './node_modules/.bin/gulp clean'" }, "engines": { - "node": ">=10.12.0", - "npm": ">=6.0.0" + "node": "^12.17.0 || >=14.0.0", + "npm": ">=6.14.4" }, "engineStrict": true } diff --git a/composer.json b/composer.json index 47d3b9d16..34b3c18e8 100755 --- a/composer.json +++ b/composer.json @@ -17,19 +17,19 @@ { "name": "Jordan Mele", "email": "SiliconSoldier@outlook.com.au", - "homepage": "https://blog.djmm.me" + "homepage": "https://djmm.me" } ], "config": { "vendor-dir": "app/vendor" }, "require": { - "php": ">=7.1", + "php": ">=7.2", "ext-gd": "*", "composer/installers": "^1.4.0", - "userfrosting/uniformresourcelocator": "~4.4.0", + "userfrosting/uniformresourcelocator": "~4.5.0", "symfony/console": "^4.3", - "wikimedia/composer-merge-plugin": "^1.4.0" + "wikimedia/composer-merge-plugin": "^2.1.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.13", diff --git a/phpunit.xml b/phpunit.xml.dist similarity index 97% rename from phpunit.xml rename to phpunit.xml.dist index 044b4fe72..161175b00 100644 --- a/phpunit.xml +++ b/phpunit.xml.dist @@ -8,7 +8,7 @@ convertWarningsToExceptions="true" processIsolation="false" stderr="true" - stopOnFailure="false"> + stopOnFailure="true"> app/tests/Unit