diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index 3ad8f5ce4..54b127bac 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - php_versions: ['7.2', '7.3', '7.4'] + php_versions: ['7.3', '7.4', '8.0'] runs-on: ubuntu-latest name: PHPUnit - PHP ${{ matrix.php_versions }} - MySQL @@ -42,7 +42,7 @@ jobs: - uses: actions/setup-node@v2 with: - node-version: 14 + node-version: ^14.0.0 - name: Setup Redis-server uses: supercharge/redis-github-action@1.1.0 @@ -56,7 +56,7 @@ jobs: run: sudo service mysql stop # Shutdown the Default MySQL, "sudo" is necessary, please not remove it - name: Set up MySQL (PHP <= 7.3 -> MySQL 5) - if: ${{ matrix.php_versions != 7.4 }} + if: ${{ matrix.php_versions == 7.3 }} uses: mirromutth/mysql-action@v1.1 with: mysql version: '5' @@ -65,7 +65,7 @@ jobs: mysql password: 'password' - name: Set up MySQL (PHP >= 7.4 -> MySQL 8) - if: ${{ matrix.php_versions == 7.4 }} + if: ${{ matrix.php_versions != 7.3 }} uses: mirromutth/mysql-action@v1.1 with: mysql version: '8' @@ -95,7 +95,9 @@ jobs: run: php bakery build-assets - name: Execute tests - run: app/vendor/bin/phpunit --coverage-clover=coverage.xml + run: | + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + app/vendor/bin/phpunit --coverage-clover=coverage.xml - name: Upload coverage to Codecov if: github.event_name != 'schedule' @@ -109,7 +111,7 @@ jobs: strategy: fail-fast: false matrix: - php_versions: ['7.2', '7.3', '7.4'] + php_versions: ['7.3', '7.4', '8.0'] runs-on: ubuntu-latest name: PHPUnit - PHP ${{ matrix.php_versions }} - SQLite @@ -133,7 +135,7 @@ jobs: - uses: actions/setup-node@v2 with: - node-version: 14 + node-version: ^14.0.0 - name: Setup Redis-server uses: supercharge/redis-github-action@1.1.0 @@ -164,7 +166,9 @@ jobs: run: php bakery build-assets - name: Execute tests - run: app/vendor/bin/phpunit --coverage-clover=coverage.xml + run: | + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + app/vendor/bin/phpunit --coverage-clover=coverage.xml - name: Upload coverage to Codecov if: github.event_name != 'schedule' @@ -178,7 +182,7 @@ jobs: strategy: fail-fast: false matrix: - php_versions: ['7.2', '7.3', '7.4'] + php_versions: ['7.3', '7.4', '8.0'] runs-on: ubuntu-latest name: PHPUnit - PHP ${{ matrix.php_versions }} - PostgreSQL @@ -213,7 +217,7 @@ jobs: - uses: actions/setup-node@v2 with: - node-version: 14 + node-version: ^14.0.0 - name: Setup Redis-server uses: supercharge/redis-github-action@1.1.0 @@ -239,7 +243,9 @@ jobs: run: php bakery build-assets - name: Execute tests - run: app/vendor/bin/phpunit --coverage-clover=coverage.xml + run: | + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + app/vendor/bin/phpunit --coverage-clover=coverage.xml - name: Upload coverage to Codecov if: github.event_name != 'schedule' @@ -253,7 +259,7 @@ jobs: strategy: fail-fast: false matrix: - php_versions: ['7.2', '7.3', '7.4'] + php_versions: ['7.3', '7.4', '8.0'] runs-on: windows-latest name: PHPUnit - PHP ${{ matrix.php_versions }} - Windows @@ -300,7 +306,9 @@ jobs: run: php bakery build-assets - name: Execute tests - run: app/vendor/bin/phpunit --coverage-clover=coverage.xml + run: | + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + app/vendor/bin/phpunit --coverage-clover=coverage.xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 @@ -313,8 +321,8 @@ jobs: strategy: fail-fast: false matrix: - php_versions: ['7.4'] - node_versions: ['12.17.0', '14', '15'] + php_versions: [ '8.0' ] + node_versions: [ ^12.17.0, ^14, ^15 ] os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} @@ -342,4 +350,24 @@ jobs: run: composer install --prefer-dist --no-progress - name: Execute build - run: php bakery build-assets \ No newline at end of file + run: php bakery build-assets + + Asset-Build-Inspect: + + name: Assets Build Inspection + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - uses: actions/setup-node@v2 + with: + node-version: ^14.0.0 + + - name: Install Dependencies + working-directory: build + run: npm i + + - name: Type Validation + working-directory: build + run: node_modules/.bin/tsc -p ./tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dbfb8f74..400e1d3ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,27 @@ 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.6.0] + +### Changed Requirements +- Drop PHP 7.2 support. Gain PHP 8.0 support. PHP 8.0 is now recommended. + +### Dependencies upgrade +- Replaced individual UserFrosting Assets, Cache, Config, Fortress, i18n, Session, Support and UniformResouceLocator repos with monolitic `userfrosting/framework` repo. +- Upgrade all Laravel packages to ^8.x from ^5.8. +- Upgrade `vlucas/phpdotenv`to ^5.3 from ^3.4. +- Upgrade `symfony/console` to ^5.1 from ^4.3. +- Upgrade `phpunit/phpunit` to ^9.5 + +### New Feature +- Added support for built-in PHP Server. + +### Changes +- Per user theme (`$user->theme`) is now deprecated and disabled by default. To enable back, change `per_user_theme` config to `true` ([#1131](https://github.com/userfrosting/UserFrosting/issues/1131)). This feature will be removed in future version. +- Bakery command `execute` method now requires to return an int (Symfony 4.4 upgrade : https://symfony.com/blog/new-in-symfony-4-4-console-improvements). +- `UserFrosting\Sprinkle\Core\Database\EloquentBuilder` now uses `Illuminate\Database\Eloquent\Concerns\QueriesRelationships` Trait instead of manually implementing `withSum`, `withAvg`, `withMin`, `withMax` & `withAggregate`. See Laravel documentation for usage change. +- Migrate `uf-modal.js` to jQuery Boilerplate ([#740](https://github.com/userfrosting/UserFrosting/issues/740)) + ## [v4.5.1] ### Fixed @@ -1076,3 +1097,4 @@ See [http://learn.userfrosting.com/upgrading/40-to-41](Upgrading 4.0.x to 4.1.x [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 [v4.5.1]: https://github.com/userfrosting/UserFrosting/compare/v4.5.0...v4.5.1 +[v4.6.0]: https://github.com/userfrosting/UserFrosting/compare/v4.5.0...v4.6.0 diff --git a/README.md b/README.md index c0166ab96..c330f1220 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# UserFrosting 4.5 +# UserFrosting 4.6 [![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) +![PHP Version](https://img.shields.io/badge/php-%5E7.4%20%7C%20%5E8.0-brightgreen) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE.md) [![Join the chat at https://chat.userfrosting.com/channel/support](https://chat.userfrosting.com/api/v1/shield.svg?name=UserFrosting)](https://chat.userfrosting.com/channel/support) [![Backers on Open Collective](https://opencollective.com/userfrosting/backers/badge.svg)](#backers) diff --git a/app/defines.php b/app/defines.php index 858ef9aba..0a4109232 100755 --- a/app/defines.php +++ b/app/defines.php @@ -11,10 +11,10 @@ namespace UserFrosting; // Some standard defines -define('UserFrosting\VERSION', '4.5.1'); +define('UserFrosting\VERSION', '4.6.0'); define('UserFrosting\DS', '/'); -define('UserFrosting\PHP_MIN_VERSION', '^7.2'); -define('UserFrosting\PHP_RECOMMENDED_VERSION', '^7.4'); +define('UserFrosting\PHP_MIN_VERSION', '^7.3 | ^8.0'); +define('UserFrosting\PHP_RECOMMENDED_VERSION', '^8.0'); define('UserFrosting\NODE_MIN_VERSION', '^12.17.0 || >=14.0.0'); define('UserFrosting\NPM_MIN_VERSION', '>=6.14.4'); diff --git a/app/sprinkles/account/asset-bundles.json b/app/sprinkles/account/asset-bundles.json index 77ee55922..985b68053 100644 --- a/app/sprinkles/account/asset-bundles.json +++ b/app/sprinkles/account/asset-bundles.json @@ -3,77 +3,35 @@ "js/pages/account-settings": { "scripts": [ "userfrosting/js/pages/account-settings.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/forgot-password": { "scripts": [ "userfrosting/js/pages/forgot-password.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/resend-verification": { "scripts": [ "userfrosting/js/pages/resend-verification.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/set-or-reset-password": { "scripts": [ "userfrosting/js/pages/set-or-reset-password.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/register": { "scripts": [ "vendor/speakingurl/speakingurl.min.js", "userfrosting/js/uf-captcha.js", "userfrosting/js/pages/register.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/sign-in": { "scripts": [ "vendor/urijs/src/URI.js", "userfrosting/js/pages/sign-in.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] } } } \ No newline at end of file diff --git a/app/sprinkles/account/config/default.php b/app/sprinkles/account/config/default.php index 741cc4059..1ff79fb38 100644 --- a/app/sprinkles/account/config/default.php +++ b/app/sprinkles/account/config/default.php @@ -175,4 +175,13 @@ 'algorithm' => 'sha512', 'timeout' => 10800, ], + + /* + * ---------------------------------------------------------------------- + * Enable or disable per user theme defined in `$currentUser->theme` + * ---------------------------------------------------------------------- + * @deprecated 4.6.0 Per user theme will be removed in future version + * This config disable the feature before it is removed + */ + 'per_user_theme' => false, ]; diff --git a/app/sprinkles/account/src/Authorize/AuthorizationManager.php b/app/sprinkles/account/src/Authorize/AuthorizationManager.php index 6f2902d45..2090abb66 100644 --- a/app/sprinkles/account/src/Authorize/AuthorizationManager.php +++ b/app/sprinkles/account/src/Authorize/AuthorizationManager.php @@ -10,6 +10,7 @@ namespace UserFrosting\Sprinkle\Account\Authorize; +use Illuminate\Support\Arr; use Psr\Container\ContainerInterface; use UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface; @@ -158,7 +159,7 @@ protected function getPermissionsArrayDebugInfo($permissions) { $permissionsInfo = []; foreach ($permissions as $permission) { - $permissionData = array_only($permission->toArray(), ['id', 'slug', 'name', 'conditions', 'description']); + $permissionData = Arr::only($permission->toArray(), ['id', 'slug', 'name', 'conditions', 'description']); // Remove this until we can find an efficient way to only load these once during debugging //$permissionData['roles_via'] = $permission->roles_via->pluck('id')->all(); $permissionsInfo[] = $permissionData; diff --git a/app/sprinkles/account/src/Bakery/CreateAdminUser.php b/app/sprinkles/account/src/Bakery/CreateAdminUser.php index dbc53be61..3c2532969 100644 --- a/app/sprinkles/account/src/Bakery/CreateAdminUser.php +++ b/app/sprinkles/account/src/Bakery/CreateAdminUser.php @@ -126,6 +126,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->io->success('Root user creation successful!'); } + + return self::SUCCESS; } /** diff --git a/app/sprinkles/account/src/ServicesProvider/ServicesProvider.php b/app/sprinkles/account/src/ServicesProvider/ServicesProvider.php index 47ed47eb7..0e95c8d26 100644 --- a/app/sprinkles/account/src/ServicesProvider/ServicesProvider.php +++ b/app/sprinkles/account/src/ServicesProvider/ServicesProvider.php @@ -48,9 +48,10 @@ public function register(ContainerInterface $container) * @return \UserFrosting\Assets\Assets */ $container->extend('assets', function ($assets, $c) { - - // Force load the current user to add it's theme assets resources - $currentUser = $c->currentUser; + if ($c->config['per_user_theme']) { + // Force load the current user to add it's theme assets resources + $c->currentUser; + } return $assets; }); @@ -318,7 +319,7 @@ public function register(ContainerInterface $container) $currentUser = $authenticator->user(); // Add user theme sprinkles resources - if ($authenticator->check() && $currentUser->theme) { + if ($c->config['per_user_theme'] && $authenticator->check() && $currentUser->theme) { $c->sprinkleManager->addSprinkleResources($currentUser->theme); } diff --git a/app/sprinkles/admin/asset-bundles.json b/app/sprinkles/admin/asset-bundles.json index 5b1b6b85f..38e3d5857 100644 --- a/app/sprinkles/admin/asset-bundles.json +++ b/app/sprinkles/admin/asset-bundles.json @@ -12,144 +12,67 @@ "vendor/tablesorter/dist/js/widgets/widget-pager.min.js", "userfrosting/js/query-string.js", "userfrosting/js/uf-table.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/activities": { "scripts": [ "userfrosting/js/pages/activities.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/dashboard": { "scripts": [ "userfrosting/js/widgets/users.js", "userfrosting/js/pages/dashboard.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/group": { "scripts": [ "userfrosting/js/widgets/users.js", "userfrosting/js/widgets/groups.js", "userfrosting/js/pages/group.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/permission": { "scripts": [ "userfrosting/js/widgets/users.js", "userfrosting/js/pages/permission.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/role": { "scripts": [ "userfrosting/js/widgets/users.js", "userfrosting/js/widgets/roles.js", "userfrosting/js/pages/role.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/user": { "scripts": [ "userfrosting/js/widgets/users.js", "userfrosting/js/pages/user.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/users": { "scripts": [ "userfrosting/js/widgets/users.js", "userfrosting/js/pages/users.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/groups": { "scripts": [ "userfrosting/js/widgets/groups.js", "userfrosting/js/pages/groups.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/permissions": { "scripts": [ "userfrosting/js/pages/permissions.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/pages/roles": { "scripts": [ "userfrosting/js/widgets/roles.js", "userfrosting/js/pages/roles.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "css/admin": { "styles": [ @@ -158,14 +81,7 @@ "vendor/tablesorter/dist/css/jquery.tablesorter.pager.min.css", "userfrosting/css/tablesorter-reflow.css", "userfrosting/css/tablesorter-custom.css" - ], - "options": { - "result": { - "type": { - "styles": "plain" - } - } - } + ] } } } diff --git a/app/sprinkles/core/asset-bundles.json b/app/sprinkles/core/asset-bundles.json index dffe1ea5a..16a063010 100644 --- a/app/sprinkles/core/asset-bundles.json +++ b/app/sprinkles/core/asset-bundles.json @@ -21,28 +21,14 @@ "userfrosting/js/uf-modal.js", "userfrosting/js/uf-copy.js", "userfrosting/js/uf-init.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "js/form-widgets": { "scripts": [ "vendor/speakingurl/speakingurl.min.js", "userfrosting/js/uf-collection.js", "vendor/fontawesome-iconpicker/dist/js/fontawesome-iconpicker.js" - ], - "options": { - "result": { - "type": { - "scripts": "plain" - } - } - } + ] }, "css/main": { "styles": [ @@ -57,26 +43,12 @@ "userfrosting/css/AdminLTE.css", "userfrosting/css/AdminLTE-skins-all.css", "userfrosting/css/userfrosting.css" - ], - "options": { - "result": { - "type": { - "styles": "plain" - } - } - } + ] }, "css/form-widgets": { "styles": [ "userfrosting/css/uf-collection.css" - ], - "options": { - "result": { - "type": { - "styles": "plain" - } - } - } + ] } } } diff --git a/app/sprinkles/core/assets/userfrosting/js/uf-modal.js b/app/sprinkles/core/assets/userfrosting/js/uf-modal.js index 919864226..e2e34ffd5 100644 --- a/app/sprinkles/core/assets/userfrosting/js/uf-modal.js +++ b/app/sprinkles/core/assets/userfrosting/js/uf-modal.js @@ -1,192 +1,155 @@ +/// /** * ufModal plugin. Handles modal windows that dynamically their fetch content from a specified URL. * - * * UserFrosting https://www.userfrosting.com - * @author Alexander Weissman https://alexanderweissman.com + * @author Alexander Weissman */ -(function( $ ) -{ +;(function($, document, undefined) { + 'use strict'; + + // Define plugin name and defaults. + var pluginName = 'ufModal'; /** - * The plugin namespace, ie for $('.selector').ufModal(options) - * - * Also the id for storing the object state via $('.selector').data() + * @typedef {{ + * sourceUrl: string, + * ajaxParams: JQuery.PlainObject | string, + * msgTarget?: JQuery|null + * DEBUG?: boolean, + * }} Options + * @type {Options} */ - var PLUGIN_NS = 'ufModal'; - - var Plugin = function ( target, options ) - { + var defaults = { + sourceUrl : '', + ajaxParams: {}, + msgTarget : null, + DEBUG : false + }; - this.$T = $(target); + /** + * @param {ArrayLike} inElement + * @param {Options|undefined} options + */ + function createPlugin(inElement, options) { + var element = inElement[0]; + var $element = $(element); + var settings = $.extend(true, defaults, options); - /** #### OPTIONS #### */ - this.options= $.extend( - true, // deep extend - { - sourceUrl : "", - ajaxParams: {}, - msgTarget : null, - DEBUG: false - }, - options - ); + // True plugin initialization commences + /** @type {JQuery|null} */ + var modal = null; - this.modal = null; + // Delete any existing modals attached to the element (should have been deleted already anyway) + if ($element.find('.modal').length) $element.find('.modal').remove(); - this._init( target ); + function destroy() { + // Remove modal from selector + if (modal) modal.remove(); - return this; - }; + // Unbind plugin events + $element.off('.' + pluginName); - /** #### INITIALIZER #### */ - Plugin.prototype._init = function ( target ) - { - var base = this; - var $el = $(target); + // Remove plugin data from internal jQuery store (jQuery doesn't store with data-*, but can access it) + $element.removeData(pluginName); - // Delete any existing modals attached to the element (should have been deleted already anyway) - if ($el.find(".modal").length) { - $el.find(".modal").remove(); + return $element; } - // Fetch and render the form + // Fetch and render $.ajax({ - type: "GET", - url: base.options.sourceUrl, - data: base.options.ajaxParams, - cache: false - }) - .then( - // Fetch successful + type: 'GET', + url: settings.sourceUrl, + data: settings.ajaxParams, + cache: false, + }).then( + // Success function (data) { - // Append the form as a modal dialog to the body - base.modal = $(data); - $el.append(base.modal); + // Append the data as a modal dialog to the target element + modal = $(data); + $element.append(modal); - base.modal.modal('show'); + // Trigger modal dialog + modal.modal('show'); - // Bind modal to be deleted when closed - base.modal.on("hidden.bs.modal", function () { - base.destroy(); - }); + // Bind destroy function to close event + modal.on('hidden.bs.modal', function () { destroy(); }); - base.$T.trigger('renderSuccess.ufModal'); - return data; + // Trigger success event + $element.trigger('renderSuccess.ufModal'); }, - // Fetch failed + // Failure function (data) { - // Error messages - if ((typeof site !== "undefined") && site.debug.ajax && data.responseText) { - base.$T.trigger('renderError.ufModal'); + // Handle error messages + if (site !== undefined && site.debug.ajax && data.responseText) { + // Trigger failure event + $element.trigger('renderError.ufModal'); + + // Replace document content with response, and handle browser quirks document.write(data.responseText); document.close(); } else { - if (base.options.DEBUG) { - console.log("Error (" + data.status + "): " + data.responseText ); - } - // Display errors on failure - // TODO: ufAlerts widget should have a 'destroy' method - if (!base.options.msgTarget.data('ufAlerts')) { - base.options.msgTarget.ufAlerts(); + // Debug logging + if (settings.DEBUG) console.log('Error (' + data.status + '): ' + data.responseText); + + // Refresh ufAlerts for errors if target defined + if (settings.msgTarget) { + // Check if ufAlerts is instanced and empty + if (!settings.msgTarget.data('ufAlerts')) settings.msgTarget.ufAlerts(); + else settings.msgTarget.ufAlerts('clear'); + + // Trigger failure event on render.ufAlerts event + settings.msgTarget.on('render.ufAlerts', function () { + $element.trigger('renderError.ufModal'); + }); + + // Pull alerts + settings.msgTarget.ufAlerts('fetch').ufAlerts('render'); } else { - base.options.msgTarget.ufAlerts('clear'); + // renderError.ufModal event should always be able to trigger + $element.trigger('renderError.ufModal'); } - - base.options.msgTarget.ufAlerts('fetch').ufAlerts('render'); - base.options.msgTarget.on("render.ufAlerts", function () { - base.$T.trigger('renderError.ufModal'); - }); } - - base.destroy(); - - return data; } - ); - }; - - Plugin.prototype.destroy = function () { - var base = this; - var $el = base.$T; + ) - // Delete the plugin object - base.delete; - - // Remove the modal from the selector - if (base.modal) { - base.modal.remove(); + /** + * Returns underlying modal + */ + function getModal() { + return modal; } - // Unbind any modal events bound to the selector - $el.off('.ufModal'); - - // Remove plugin name from selector's data-* attribute - $el.removeData(PLUGIN_NS); - }; - - Plugin.prototype.getModal = function () { - return this.modal; - }; - - /** - * EZ Logging/Warning (technically private but saving an '_' is worth it imo) - */ - Plugin.prototype.DLOG = function () - { - if (!this.DEBUG) return; - for (var i in arguments) { - console.log( PLUGIN_NS + ': ', arguments[i] ); - } - } - Plugin.prototype.DWARN = function () - { - this.DEBUG && console.warn( arguments ); + return { + destroy, + getModal, + }; } - -/*################################################################################### - * JQUERY HOOK - ###################################################################################*/ - /** - * Generic jQuery plugin instantiation method call logic - * - * Method options are stored via jQuery's data() method in the relevant element(s) - * Notice, myActionMethod mustn't start with an underscore (_) as this is used to - * indicate private methods on the PLUGIN class. + * Handles instantiation and access to non-private methods. + * @param {Options|keyof ReturnType|undefined} methodOrOptions */ - $.fn[ PLUGIN_NS ] = function( methodOrOptions ) - { - if (!$(this).length) { - return $(this); + function interop(methodOrOptions) { + // Grab plugin instance + /** @type {ReturnType|undefined} */ + var instance = $(this).data(pluginName); + + // If undefined or object, initialize plugin. + if (typeof methodOrOptions === 'undefined' || typeof methodOrOptions === 'object') { + // Only initialize if not previously done. + if (!instance) { + $(this).data(pluginName, createPlugin(this, methodOrOptions)); + } + return this; } - var instance = $(this).data(PLUGIN_NS); - - // CASE: action method (public method on PLUGIN class) - if ( instance - && methodOrOptions.indexOf('_') != 0 - && instance[ methodOrOptions ] - && typeof( instance[ methodOrOptions ] ) == 'function' ) { - - return instance[ methodOrOptions ]( Array.prototype.slice.call( arguments, 1 ) ); - - - // 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 - $(this).data( PLUGIN_NS, instance ); - return $(this); - - // CASE: method called before init - } else if ( !instance ) { - console.warn( 'Plugin must be initialized before using method: ' + methodOrOptions ); - - // CASE: invalid method - } else if ( methodOrOptions.indexOf('_') == 0 ) { - console.warn( 'Method ' + methodOrOptions + ' is private!' ); - } else { - console.warn( 'Method ' + methodOrOptions + ' does not exist.' ); + // Otherwise ensure first parameter is a valid string, and is the name of an actual function. + else if (typeof methodOrOptions === 'string' && typeof instance[methodOrOptions] === 'function') { + return instance[methodOrOptions](); + } + else { + console.error('Method ' + methodOrOptions + ' does not exist.'); } }; -})(jQuery); \ No newline at end of file + + $.fn[pluginName] = interop; +})(jQuery, document); \ No newline at end of file diff --git a/app/sprinkles/core/composer.json b/app/sprinkles/core/composer.json index 956c4b40b..be835d9c7 100644 --- a/app/sprinkles/core/composer.json +++ b/app/sprinkles/core/composer.json @@ -30,10 +30,10 @@ "composer/semver": "^3.2.4", "doctrine/dbal": "^2.5", "filp/whoops": "^2.14.0", - "illuminate/cache": "5.8.*", - "illuminate/database": "5.8.*", - "illuminate/events": "5.8.*", - "illuminate/filesystem": "5.8.*", + "illuminate/cache": "^8.5", + "illuminate/database": "^8.5", + "illuminate/events": "^8.5", + "illuminate/filesystem": "^8.5", "league/csv": "^9.2.1", "league/flysystem": "~1.0", "league/flysystem-cached-adapter": "~1.0", @@ -45,14 +45,7 @@ "slim/twig-view": "^2.5", "symfony/http-foundation": "*", "twig/twig": "^2.11", - "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" + "vlucas/phpdotenv": "^5.3" }, "require-dev": { "php-mock/php-mock-phpunit": "^2.6" diff --git a/app/sprinkles/core/package.json b/app/sprinkles/core/package.json index 197a3bc0c..fd30ecdad 100644 --- a/app/sprinkles/core/package.json +++ b/app/sprinkles/core/package.json @@ -12,11 +12,11 @@ "jquery": "^3.2.1", "jquery-slimscroll": "~1.3.8", "jquery-ui": "^1.12.0", - "jquery-validation": "~1.14.0", + "jquery-validation": "~1.19.3", "moment": "^2.17.1", "select2": "^4.0.5", "speakingurl": "^11.0.0", "tablesorter": "^2.28.5", - "urijs": "^1.18.4" + "urijs": "^1.19.6" } } diff --git a/app/sprinkles/core/src/Bakery/BakeCommand.php b/app/sprinkles/core/src/Bakery/BakeCommand.php index 89242df65..51d67bc5c 100644 --- a/app/sprinkles/core/src/Bakery/BakeCommand.php +++ b/app/sprinkles/core/src/Bakery/BakeCommand.php @@ -57,6 +57,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->executeConfiguration($input, $output); $this->executeAsset($input, $output); $this->executeCleanup($input, $output); + + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/BuildAssets.php b/app/sprinkles/core/src/Bakery/BuildAssets.php index c12ce0c26..20347348c 100644 --- a/app/sprinkles/core/src/Bakery/BuildAssets.php +++ b/app/sprinkles/core/src/Bakery/BuildAssets.php @@ -78,6 +78,8 @@ protected function execute(InputInterface $input, OutputInterface $output) // If all went well and there's no fatal errors, we are successful $this->io->success('Assets install looks successful, check output for specifics'); + + return self::SUCCESS; } /** @@ -109,7 +111,7 @@ protected function npmInstall($force) // Ensure extraneous dependencies are cleared out $exitCode = 0; - passthru('npm prune'); + passthru('npm prune --production'); if ($exitCode !== 0) { $this->io->error('npm dependency installation has failed'); @@ -118,7 +120,7 @@ protected function npmInstall($force) // Install the new dependencies $exitCode = 0; - passthru('npm install', $exitCode); + passthru('npm install --production', $exitCode); if ($exitCode !== 0) { $this->io->error('npm dependency installation has failed'); diff --git a/app/sprinkles/core/src/Bakery/ClearCacheCommand.php b/app/sprinkles/core/src/Bakery/ClearCacheCommand.php index e695962fa..4c239540f 100644 --- a/app/sprinkles/core/src/Bakery/ClearCacheCommand.php +++ b/app/sprinkles/core/src/Bakery/ClearCacheCommand.php @@ -59,6 +59,8 @@ protected function execute(InputInterface $input, OutputInterface $output) } $this->io->success('Cache cleared !'); + + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/DebugCommand.php b/app/sprinkles/core/src/Bakery/DebugCommand.php index 394c8b74c..fe10f1f1b 100644 --- a/app/sprinkles/core/src/Bakery/DebugCommand.php +++ b/app/sprinkles/core/src/Bakery/DebugCommand.php @@ -88,7 +88,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->io->success('Ready to bake !'); // Command return success - return 0; + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/LocaleCompareCommand.php b/app/sprinkles/core/src/Bakery/LocaleCompareCommand.php index a721e4248..0d782f11a 100644 --- a/app/sprinkles/core/src/Bakery/LocaleCompareCommand.php +++ b/app/sprinkles/core/src/Bakery/LocaleCompareCommand.php @@ -68,7 +68,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->emptyValues($rightDictionary); // Done - return 0; + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/LocaleDictionaryCommand.php b/app/sprinkles/core/src/Bakery/LocaleDictionaryCommand.php index cf52f787d..db5ca265f 100644 --- a/app/sprinkles/core/src/Bakery/LocaleDictionaryCommand.php +++ b/app/sprinkles/core/src/Bakery/LocaleDictionaryCommand.php @@ -65,6 +65,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $table->render(); // Everything went fine, return 0 exit code - return 0; + return self::SUCCESS; } } diff --git a/app/sprinkles/core/src/Bakery/LocaleInfoCommand.php b/app/sprinkles/core/src/Bakery/LocaleInfoCommand.php index de5c1c127..334e43fd1 100644 --- a/app/sprinkles/core/src/Bakery/LocaleInfoCommand.php +++ b/app/sprinkles/core/src/Bakery/LocaleInfoCommand.php @@ -64,6 +64,6 @@ protected function execute(InputInterface $input, OutputInterface $output) $table->render(); // Everything went fine, return 0 exit code - return 0; + return self::SUCCESS; } } diff --git a/app/sprinkles/core/src/Bakery/MigrateCleanCommand.php b/app/sprinkles/core/src/Bakery/MigrateCleanCommand.php index b94fb50d4..41e0b7281 100644 --- a/app/sprinkles/core/src/Bakery/MigrateCleanCommand.php +++ b/app/sprinkles/core/src/Bakery/MigrateCleanCommand.php @@ -76,6 +76,8 @@ protected function execute(InputInterface $input, OutputInterface $output) } else { $this->io->note('No stale migrations'); } + + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/MigrateCommand.php b/app/sprinkles/core/src/Bakery/MigrateCommand.php index 390abf1b9..49143c6f3 100644 --- a/app/sprinkles/core/src/Bakery/MigrateCommand.php +++ b/app/sprinkles/core/src/Bakery/MigrateCommand.php @@ -62,7 +62,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (empty($pending)) { $this->io->success('Nothing to migrate'); - return; + return self::SUCCESS; } // Show migrations about to be ran when in production mode @@ -95,6 +95,8 @@ protected function execute(InputInterface $input, OutputInterface $output) } else { $this->io->success('Migration successful !'); } + + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/MigrateRefreshCommand.php b/app/sprinkles/core/src/Bakery/MigrateRefreshCommand.php index 2f932eb6d..1f4f4bed9 100644 --- a/app/sprinkles/core/src/Bakery/MigrateRefreshCommand.php +++ b/app/sprinkles/core/src/Bakery/MigrateRefreshCommand.php @@ -83,7 +83,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (empty($rolledback)) { $this->io->warning('Nothing was refreshed !'); - return; + return self::SUCCESS; } // Run back up again @@ -95,5 +95,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (!empty($migrated)) { $this->io->success('Refresh successful !'); } + + return self::SUCCESS; } } diff --git a/app/sprinkles/core/src/Bakery/MigrateResetCommand.php b/app/sprinkles/core/src/Bakery/MigrateResetCommand.php index c83027199..0c1706935 100644 --- a/app/sprinkles/core/src/Bakery/MigrateResetCommand.php +++ b/app/sprinkles/core/src/Bakery/MigrateResetCommand.php @@ -48,6 +48,8 @@ protected function execute(InputInterface $input, OutputInterface $output) } else { $this->performReset($input); } + + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/MigrateRollbackCommand.php b/app/sprinkles/core/src/Bakery/MigrateRollbackCommand.php index ec5414073..49fb54743 100644 --- a/app/sprinkles/core/src/Bakery/MigrateRollbackCommand.php +++ b/app/sprinkles/core/src/Bakery/MigrateRollbackCommand.php @@ -95,5 +95,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } else { $this->io->success('Rollback successful !'); } + + return self::SUCCESS; } } diff --git a/app/sprinkles/core/src/Bakery/MigrateStatusCommand.php b/app/sprinkles/core/src/Bakery/MigrateStatusCommand.php index ea78fc3c4..19f1a9e08 100644 --- a/app/sprinkles/core/src/Bakery/MigrateStatusCommand.php +++ b/app/sprinkles/core/src/Bakery/MigrateStatusCommand.php @@ -77,6 +77,8 @@ protected function execute(InputInterface $input, OutputInterface $output) } else { $this->io->note('No pending migrations'); } + + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/RouteListCommand.php b/app/sprinkles/core/src/Bakery/RouteListCommand.php index b3f50d43b..c2b9d4e50 100644 --- a/app/sprinkles/core/src/Bakery/RouteListCommand.php +++ b/app/sprinkles/core/src/Bakery/RouteListCommand.php @@ -59,7 +59,9 @@ protected function execute(InputInterface $input, OutputInterface $output) // If not route, don't go further if (count($routes) === 0) { - return $this->io->error("Your application doesn't have any routes."); + $this->io->error("Your application doesn't have any routes."); + + return self::FAILURE; } // Compile the routes into a displayable format @@ -79,6 +81,8 @@ protected function execute(InputInterface $input, OutputInterface $output) // Display routes $this->io->table($this->headers, $routes); + + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/SeedCommand.php b/app/sprinkles/core/src/Bakery/SeedCommand.php index e8a861a06..2192f3daf 100644 --- a/app/sprinkles/core/src/Bakery/SeedCommand.php +++ b/app/sprinkles/core/src/Bakery/SeedCommand.php @@ -96,5 +96,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // Success $this->io->success('Seed successful !'); + + return self::SUCCESS; } } diff --git a/app/sprinkles/core/src/Bakery/SeedListCommand.php b/app/sprinkles/core/src/Bakery/SeedListCommand.php index bc15f252b..dc4cfb366 100644 --- a/app/sprinkles/core/src/Bakery/SeedListCommand.php +++ b/app/sprinkles/core/src/Bakery/SeedListCommand.php @@ -40,5 +40,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->io->title('Database Seeds List'); $seeds = $this->ci->seeder->getSeeds(); $this->io->table(['Name', 'Namespace', 'Sprinkle'], $seeds); + + return self::SUCCESS; } } diff --git a/app/sprinkles/core/src/Bakery/SetupCommand.php b/app/sprinkles/core/src/Bakery/SetupCommand.php index 6d0ac9afe..444e66996 100644 --- a/app/sprinkles/core/src/Bakery/SetupCommand.php +++ b/app/sprinkles/core/src/Bakery/SetupCommand.php @@ -45,5 +45,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $command = $this->getApplication()->find('setup:env'); $command->run($input, $output); + + return self::SUCCESS; } } diff --git a/app/sprinkles/core/src/Bakery/SetupDbCommand.php b/app/sprinkles/core/src/Bakery/SetupDbCommand.php index c96364328..ebb7eb52e 100644 --- a/app/sprinkles/core/src/Bakery/SetupDbCommand.php +++ b/app/sprinkles/core/src/Bakery/SetupDbCommand.php @@ -65,7 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (!$input->getOption('force') && $this->testDatabase($config['db.default'], false)) { $this->io->note('Database already setup. Use the `php bakery setup:db --force` command to run db setup again.'); - return; + return self::SUCCESS; } $this->io->note("Database credentials will be saved in `{$this->envPath}`"); @@ -92,7 +92,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $this->io->warning("Current database configuration differ from the configuration defined in `{$this->envPath}`. Global system environment variables might be defined."); if (!$this->io->confirm('Continue?', false)) { - return; + return self::SUCCESS; } } @@ -133,6 +133,8 @@ protected function execute(InputInterface $input, OutputInterface $output) // Success $this->io->success("Database config successfully saved in `{$this->envPath}`"); + + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/SetupEnvCommand.php b/app/sprinkles/core/src/Bakery/SetupEnvCommand.php index 877c6fb45..e1a95e7cf 100644 --- a/app/sprinkles/core/src/Bakery/SetupEnvCommand.php +++ b/app/sprinkles/core/src/Bakery/SetupEnvCommand.php @@ -68,6 +68,8 @@ protected function execute(InputInterface $input, OutputInterface $output) // Success $this->io->success("Environment mode successfully changed to `$newEnvMode` in `{$this->envPath}`"); + + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/SetupSmtpCommand.php b/app/sprinkles/core/src/Bakery/SetupSmtpCommand.php index ef418a5b3..bfa1780c8 100644 --- a/app/sprinkles/core/src/Bakery/SetupSmtpCommand.php +++ b/app/sprinkles/core/src/Bakery/SetupSmtpCommand.php @@ -90,7 +90,7 @@ protected function execute(InputInterface $input, OutputInterface $output) if (!$input->getOption('force') && $this->isSmtpConfigured($dotenvEditor)) { $this->io->note('Mail is already setup. Use the `php bakery setup:mail --force` command to run setup again.'); - return; + return self::SUCCESS; } // Get keys @@ -117,7 +117,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $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; + return self::SUCCESS; } } @@ -136,6 +136,8 @@ protected function execute(InputInterface $input, OutputInterface $output) // Success $this->io->success("Mail configuration saved to `{$this->envPath}`.\nYou can test outgoing mail using `test:mail` command."); + + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/SprinkleListCommand.php b/app/sprinkles/core/src/Bakery/SprinkleListCommand.php index 284b2e777..4e337f4f5 100644 --- a/app/sprinkles/core/src/Bakery/SprinkleListCommand.php +++ b/app/sprinkles/core/src/Bakery/SprinkleListCommand.php @@ -57,5 +57,7 @@ protected function execute(InputInterface $input, OutputInterface $output) // Display table $this->io->table($this->headers, $sprinklesTable); + + return self::SUCCESS; } } diff --git a/app/sprinkles/core/src/Bakery/Test.php b/app/sprinkles/core/src/Bakery/Test.php index 21ea3ab8c..66bf4dd8d 100644 --- a/app/sprinkles/core/src/Bakery/Test.php +++ b/app/sprinkles/core/src/Bakery/Test.php @@ -101,6 +101,8 @@ protected function execute(InputInterface $input, OutputInterface $output) // Execute $this->io->writeln("> $command"); passthru($command); + + return self::SUCCESS; } /** diff --git a/app/sprinkles/core/src/Bakery/TestMailCommand.php b/app/sprinkles/core/src/Bakery/TestMailCommand.php index 584776730..35e7e4936 100644 --- a/app/sprinkles/core/src/Bakery/TestMailCommand.php +++ b/app/sprinkles/core/src/Bakery/TestMailCommand.php @@ -65,5 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output) } $this->io->success("Test email sent to $to !"); + + return self::SUCCESS; } } diff --git a/app/sprinkles/core/src/Database/EloquentBuilder.php b/app/sprinkles/core/src/Database/EloquentBuilder.php index 11b839b1d..ca7625d83 100644 --- a/app/sprinkles/core/src/Database/EloquentBuilder.php +++ b/app/sprinkles/core/src/Database/EloquentBuilder.php @@ -11,17 +11,20 @@ namespace UserFrosting\Sprinkle\Core\Database; use Illuminate\Database\Eloquent\Builder as LaravelEloquentBuilder; -use Illuminate\Database\Query\Expression; -use Illuminate\Support\Str; +use Illuminate\Database\Eloquent\Concerns\QueriesRelationships; use UserFrosting\Support\Exception\BadRequestException; /** * UserFrosting's custom Eloquent Builder Class. * * @author Alex Weissman (https://alexanderweissman.com) + * + * @todo This class overlaped `Illuminate\Database\Eloquent\Builder` trait and was adapted for it. It should be further improved and maybe made a Trait to reflect Laravel Trait. */ class EloquentBuilder extends LaravelEloquentBuilder { + use QueriesRelationships; + /** * Find a model by its primary integer-valued key or throw an exception if * something other than a nonnegative integer is provided. @@ -41,123 +44,4 @@ public function findInt($id, $columns = ['*']) return $this->find($id, $columns); } - - /** - * Add subselect queries to sum the relations. - * - * @param mixed $relations - * - * @return self - */ - public function withSum($relations) - { - $relations = is_array($relations) ? $relations : func_get_args(); - - return $this->withAggregate($relations, 'SUM'); - } - - /** - * Add subselect queries to max the relations. - * - * @param mixed $relations - * - * @return self - */ - public function withMax($relations) - { - $relations = is_array($relations) ? $relations : func_get_args(); - - return $this->withAggregate($relations, 'MAX'); - } - - /** - * Add subselect queries to min the relations. - * - * @param mixed $relations - * - * @return self - */ - public function withMin($relations) - { - $relations = is_array($relations) ? $relations : func_get_args(); - - return $this->withAggregate($relations, 'MIN'); - } - - /** - * Add subselect queries to min the relations. - * - * @param mixed $relations - * - * @return self - */ - public function withAvg($relations) - { - $relations = is_array($relations) ? $relations : func_get_args(); - - return $this->withAggregate($relations, 'AVG'); - } - - /** - * use the MySQL aggregate functions including AVG COUNT, SUM, MAX and MIN. - * - * @param array $relations - * @param string $function - * - * @return self - */ - public function withAggregate($relations, $function = 'COUNT') - { - if (is_null($this->query->columns)) { - $this->query->select([$this->query->from . '.*']); - } - - $function = Str::lower($function); - - foreach ($this->parseWithRelations($relations) as $name => $constraints) { - // First we will determine if the name has been aliased using an "as" clause on the name - // and if it has we will extract the actual relationship name and the desired name of - // the resulting column. This allows multiple counts on the same relationship name. - $segments = explode(' ', $name); - - unset($alias); - - if (count($segments) == 3 && Str::lower($segments[1]) == 'as') { - list($name, $alias) = [$segments[0], $segments[2]]; - } - - // set the default column as * or primary key - $column = ($function == 'count') ? '*' : $this->model->getKeyName(); - - if (Str::contains($name, '~')) { - list($name, $column) = explode('~', $name); - } - - $relation = $this->getRelationWithoutConstraints($name); - - $expression = new Expression($function . '(' . $this->query->getGrammar()->wrap($column) . ')'); - - // Here we will get the relationship aggregate query and prepare to add it to the main query - // as a sub-select. First, we'll get the "has" query and use that to get the relation - // count query. We will normalize the relation name then append the aggregate type as the name. - $query = $relation->getRelationExistenceQuery( - $relation->getRelated()->newQuery(), - $this, - $expression - ); - - $query->callScope($constraints); - - $query->mergeConstraintsFrom($relation->getQuery()); - - // Finally we will add the proper result column alias to the query and run the subselect - // statement against the query builder. Then we will return the builder instance back - // to the developer for further constraint chaining that needs to take place on it. - $column = snake_case(isset($alias) ? $alias : $name) . '_' . $function; - - $this->selectSub($query->toBase(), $column); - } - - return $this; - } } diff --git a/app/sprinkles/core/src/ServicesProvider/ServicesProvider.php b/app/sprinkles/core/src/ServicesProvider/ServicesProvider.php index 58bf291e9..a92eb8778 100755 --- a/app/sprinkles/core/src/ServicesProvider/ServicesProvider.php +++ b/app/sprinkles/core/src/ServicesProvider/ServicesProvider.php @@ -233,7 +233,7 @@ public function register(ContainerInterface $container) $container['config'] = function ($c) { // Grab any relevant dotenv variables from the .env file try { - $dotenv = Dotenv::create(\UserFrosting\APP_DIR); + $dotenv = Dotenv::createImmutable(\UserFrosting\APP_DIR); $dotenv->load(); } catch (InvalidPathException $e) { // Skip loading the environment config file if it doesn't exist. diff --git a/app/sprinkles/core/src/Util/Util.php b/app/sprinkles/core/src/Util/Util.php index b51dbf616..27b050aa3 100644 --- a/app/sprinkles/core/src/Util/Util.php +++ b/app/sprinkles/core/src/Util/Util.php @@ -10,6 +10,9 @@ namespace UserFrosting\Sprinkle\Core\Util; +use Illuminate\Support\Arr; +use Illuminate\Support\Str; + /** * Util Class. * @@ -167,11 +170,11 @@ public static function randomPhrase($numAdjectives, $maxLength = 9999999, $maxTr for ($n = 0; $n < $maxTries; $n++) { $keys = array_rand($adjectives, $numAdjectives); - $matches = array_only($adjectives, $keys); + $matches = Arr::only($adjectives, $keys); $result = implode($separator, $matches); $result .= $separator . $nouns[array_rand($nouns)]; - $result = str_slug($result, $separator); + $result = Str::slug($result, $separator); if (strlen($result) < $maxLength) { return $result; } diff --git a/app/sprinkles/core/templates/pages/about.html.twig b/app/sprinkles/core/templates/pages/about.html.twig index 1c7d8ef62..06cd02f36 100755 --- a/app/sprinkles/core/templates/pages/about.html.twig +++ b/app/sprinkles/core/templates/pages/about.html.twig @@ -12,7 +12,7 @@
- +

About Modern Business

@@ -30,7 +30,7 @@
- +

John Smith
Job Title @@ -49,7 +49,7 @@

- +

John Smith
Job Title @@ -68,7 +68,7 @@

- +

John Smith
Job Title @@ -87,7 +87,7 @@

- +

John Smith
Job Title @@ -106,7 +106,7 @@

- +

John Smith
Job Title @@ -125,7 +125,7 @@

- +

John Smith
Job Title @@ -151,22 +151,22 @@

- +
- +
- +
- +
- +
- +
diff --git a/app/sprinkles/core/templates/pages/index.html.twig b/app/sprinkles/core/templates/pages/index.html.twig index 3162aa79b..703fdd557 100755 --- a/app/sprinkles/core/templates/pages/index.html.twig +++ b/app/sprinkles/core/templates/pages/index.html.twig @@ -20,21 +20,21 @@
- +
diff --git a/app/sprinkles/core/tests/Integration/Database/DatabaseTests.php b/app/sprinkles/core/tests/Integration/Database/DatabaseTests.php index ec3753366..ea98fa0e1 100644 --- a/app/sprinkles/core/tests/Integration/Database/DatabaseTests.php +++ b/app/sprinkles/core/tests/Integration/Database/DatabaseTests.php @@ -648,8 +648,11 @@ public function testBelongsToManyThrough() // Test counting related models (withCount) $users = EloquentTestUser::withCount('permissions')->get(); - $this->assertEquals(3, $users[0]->permissions_count); - $this->assertEquals(3, $users[1]->permissions_count); + + // N.B.: Changed behavior in UF 4.6 due to change in Laravel API. Duplicated permission now count as two, instead of 1. + // See : https://github.com/laravel/framework/issues/30575#issuecomment-554096259 + $this->assertEquals(4, $users[0]->permissions_count); // Used to be 3 + $this->assertEquals(4, $users[1]->permissions_count); $this->assertInstanceOf(EloquentTestPermission::class, $users[0]->permissions[0]); $this->assertInstanceOf(EloquentTestPermission::class, $users[0]->permissions[1]); diff --git a/app/sprinkles/core/tests/Integration/Filesystem/FilesystemTest.php b/app/sprinkles/core/tests/Integration/Filesystem/FilesystemTest.php index 481d49a21..bf2c97356 100644 --- a/app/sprinkles/core/tests/Integration/Filesystem/FilesystemTest.php +++ b/app/sprinkles/core/tests/Integration/Filesystem/FilesystemTest.php @@ -89,7 +89,7 @@ public function testAdapter(FilesystemAdapter $files) // We'll delete the test file now $this->assertTrue($files->delete('file.txt')); - $this->assertFileNotExists($this->testDir . '/file.txt'); + $this->assertFileDoesNotExist($this->testDir . '/file.txt'); } /** @@ -117,7 +117,7 @@ public function testUrl(FilesystemAdapter $files) $url = $files->url('file.txt'); $this->assertEquals('files/testing/file.txt', $url); $this->assertTrue($files->delete('file.txt')); - $this->assertFileNotExists($this->testDir . '/file.txt'); + $this->assertFileDoesNotExist($this->testDir . '/file.txt'); } /** diff --git a/app/sprinkles/core/tests/Unit/Database/Relations/BelongsToManyThroughTest.php b/app/sprinkles/core/tests/Unit/Database/Relations/BelongsToManyThroughTest.php index 8508ed415..38d6fe11c 100644 --- a/app/sprinkles/core/tests/Unit/Database/Relations/BelongsToManyThroughTest.php +++ b/app/sprinkles/core/tests/Unit/Database/Relations/BelongsToManyThroughTest.php @@ -69,10 +69,11 @@ protected function getRelation() { // We simulate a BelongsToManyThrough relationship that gets all related users for a specified permission(s). $builder = m::mock(EloquentBuilder::class); - $related = m::mock('Illuminate\Database\Eloquent\Model'); + $related = m::mock('Illuminate\Database\Eloquent\Model')->makePartial(); $related->shouldReceive('getKey')->andReturn(1); $related->shouldReceive('getTable')->andReturn('users'); $related->shouldReceive('getKeyName')->andReturn('id'); + // Tie the mocked builder to the mocked related model $builder->shouldReceive('getModel')->andReturn($related); diff --git a/app/sprinkles/core/tests/Unit/Util/VersionValidatorTest.php b/app/sprinkles/core/tests/Unit/Util/VersionValidatorTest.php index 684faa211..a560c71b1 100644 --- a/app/sprinkles/core/tests/Unit/Util/VersionValidatorTest.php +++ b/app/sprinkles/core/tests/Unit/Util/VersionValidatorTest.php @@ -161,12 +161,13 @@ public function testNpm(string $version, string $sanitized, bool $valid): void 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.2.3', '7.2.3', false, 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], + ['7.3', '7.3', true, true], + ['7.4', '7.4', true, true], + ['7.4.13', '7.4.13', true, true], + ['8.0.3', '8.0.3', true, false], + ['7.4.34-18+ubuntu20.04.1+deb.sury.org+1', '7.4.34', true, true], ]; } diff --git a/build/gulpfile.esm.js b/build/gulpfile.mjs similarity index 98% rename from build/gulpfile.esm.js rename to build/gulpfile.mjs index 74188d09d..7034c36bd 100644 --- a/build/gulpfile.esm.js +++ b/build/gulpfile.mjs @@ -1,4 +1,3 @@ -// @ts-check import { exec as _exec } from "child_process"; import { config as envConfig } from "dotenv"; import { writeFileSync } from "fs"; diff --git a/build/missing-types.d.ts b/build/missing-types.d.ts new file mode 100644 index 000000000..27a95d139 --- /dev/null +++ b/build/missing-types.d.ts @@ -0,0 +1,52 @@ +// TODO Remove pending merging of https://github.com/gulpjs/gulplog/pull/12 +declare module "gulplog" { + /** + * Highest log level. Typically used for debugging purposes. + * + * If the first argument is a string, all arguments are passed to node's util.format() before being emitted. + * @param msg Message to log + * @param args Arguments to format message with via util.format() + */ + export function debug(msg: string, ...args: any[]): void; + export function debug(msg: any): void; + /** + * Standard log level. Typically used for user information. + * + * If the first argument is a string, all arguments are passed to node's util.format() before being emitted. + * @param msg Message to log + * @param args Arguments to format message with via util.format() + */ + export function info(msg: string, ...args: any[]): void; + export function info(msg: any): void; + /** + * Warning log level. Typically used for warnings. + * + * If the first argument is a string, all arguments are passed to node's util.format() before being emitted. + * @param msg Message to log + * @param args Arguments to format message with via util.format() + */ + export function warn(msg: string, ...args: any[]): void; + export function warn(msg: any): void; + /** + * Error log level. Typically used when things went horribly wrong. + * + * If the first argument is a string, all arguments are passed to node's util.format() before being emitted. + * @param msg Message to log + * @param args Arguments to format message with via util.format() + */ + export function error(msg: string, ...args: any[]): void; + export function error(msg: any): void; + + const _default = { + debug, + info, + warn, + error, + }; + + export default _default; +} + +declare module "gulp-concat-css" { + export = (...args?: unknown[]) => unknown; +} diff --git a/build/package.json b/build/package.json index 4bdfec1d6..99f10c9de 100755 --- a/build/package.json +++ b/build/package.json @@ -1,26 +1,27 @@ { "private": true, + "type": "module", "dependencies": { - "@userfrosting/browserify-dependencies": "^3.1.0", - "@userfrosting/gulp-bundle-assets": "^4.0.1", - "@userfrosting/merge-package-dependencies": "^2.1.0", - "@userfrosting/vinyl-fs-vpath": "^2.0.0", + "@userfrosting/browserify-dependencies": "^4.0.0", + "@userfrosting/gulp-bundle-assets": "^5.0.2", + "@userfrosting/merge-package-dependencies": "^3.0.1", + "@userfrosting/ts-log-adapter-gulplog": "^1.0.0", + "@userfrosting/vinyl-fs-vpath": "^3.0.2", "@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", + "dotenv": "^10.0.0", "gulp-clean-css": "^4.3.0", - "gulp-concat": "^2.6.1", "gulp-concat-css": "^3.1.0", + "gulp-concat": "^2.6.1", "gulp-if": "^3.0.0", - "gulp-prune": "^0.2.0", + "gulp-prune": "^2.0.0", "gulp-rev": "^9.0.0", "gulp-sourcemaps": "^3.0.0", "gulp-terser": "^2.0.1", + "gulp": "^4.0.2", "gulplog": "^1.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^7.0.0" }, "scripts": { "uf-bundle": "shell './node_modules/.bin/gulp build'", @@ -32,5 +33,17 @@ "node": "^12.17.0 || >=14.0.0", "npm": ">=6.14.4" }, - "engineStrict": true + "engineStrict": true, + "devDependencies": { + "@types/gulp": "^4.0.9", + "@types/gulp-clean-css": "^4.3.0", + "@types/gulp-concat": "^0.0.33", + "@types/gulp-if": "^0.0.34", + "@types/gulp-rev": "^5.0.32", + "@types/gulp-sourcemaps": "0.0.35", + "@types/gulp-terser": "^1.2.1", + "@types/node": "^16.3.1", + "@types/vinyl": "^2.0.5", + "typescript": "^4.3.5" + } } diff --git a/build/tasks/assets-install.js b/build/tasks/assets-install.js index 85843a529..fc19dc7c6 100644 --- a/build/tasks/assets-install.js +++ b/build/tasks/assets-install.js @@ -1,32 +1,35 @@ -// @ts-check -import { Logger, legacyVendorAssetsGlob, sprinkles, sprinklesDir, vendorAssetsDir } from "./util.js"; +import { legacyVendorAssetsGlob, sprinkles, sprinklesDir, vendorAssetsDir } from "./util.js"; import { bower as mergeBowerDeps, npm as mergeNpmDeps } from "@userfrosting/merge-package-dependencies"; -import browserifyDependencies from "@userfrosting/browserify-dependencies"; +import { browserifyDependencies } from "@userfrosting/browserify-dependencies"; import { sync as deleteSync } from "del"; import childProcess, { exec as _exec } from "child_process"; import { existsSync } from "fs"; import { promisify } from "util"; +import { GulpLogLogger } from "@userfrosting/ts-log-adapter-gulplog"; // Promisify exec const exec = promisify(_exec); /** * Runs the provided command and captures output. + * @todo Add support for realtime logging * @param {string} source Used to annotate logs. * @param {string} cmd Command to execute. * @param {childProcess.ExecOptions} options Options to pass to `exec`. */ async function runCommand(source, cmd, options) { - const log = new Logger(`${source}> ${cmd}`) + const log = new GulpLogLogger(`${source}> ${cmd}`) log.info("Running command"); try { const result = await exec(cmd, options); - if (result.stdout) log.info(result.stdout); - if (result.stderr) log.error(result.stderr); + if (result.stdout) result.stdout.split("\n").forEach(msg => log.info(msg)); + if (result.stderr) result.stderr.split("\n").forEach(msg => log.error(msg)); } catch (e) { - if (e.stdout) log.info(e.stdout); - if (e.stderr) log.error(e.stderr); + /** @type {{ stdout: string, stderr: string }} */ + const err = e; + if (e.stdout) err.stdout.split("\n").forEach(msg => log.info(msg)); + if (e.stderr) err.stderr.split("\n").forEach(msg => log.error(msg)); log.error("Command has completed with an error"); throw e; } @@ -38,7 +41,7 @@ async function runCommand(source, cmd, options) { * Installs vendor assets. Mapped to npm script "uf-assets-install". */ export async function assetsInstall() { - const log = new Logger(assetsInstall.name); + const log = new GulpLogLogger(assetsInstall.name); // Clean up any legacy assets if (deleteSync(legacyVendorAssetsGlob, { force: true })) @@ -66,6 +69,7 @@ export async function assetsInstall() { private: true }; log.info("Collating dependencies..."); + /** @type {{ private: boolean, dependencies?: { [x: string]: string } }} */ const pkg = mergeNpmDeps(npmTemplate, npmPaths, vendorAssetsDir, true); log.info("Dependency collation complete."); @@ -85,14 +89,14 @@ export async function assetsInstall() { await runCommand(assetsInstall.name, "npm audit", { cwd: vendorAssetsDir }); } catch { - log.warn("There appear to be some vulerabilities within your dependencies. Updating is recommended."); + log.warn("There appear to be some vulnerabilities within your dependencies. Updating is recommended."); } // Browserify dependencies log.info("Compiling compatible node modules into UMD bundles with browserify"); deleteSync(vendorAssetsDir + "browser_modules/", { force: true }); await browserifyDependencies({ - dependencies: Object.keys(pkg.dependencies), + dependencies: Object.keys(pkg.dependencies || []), inputDir: vendorAssetsDir + "node_modules/", outputDir: vendorAssetsDir + "browser_modules/", silentFailures: true, @@ -114,8 +118,7 @@ export async function assetsInstall() { for (const sprinkle of sprinkles) { const path = sprinklesDir + sprinkle + "/bower.json"; if (existsSync(path)) { - // TODO: We should really have a link to docs in the message - log.warn(`DEPRECATED: Detected bower.json in ${sprinkle} Sprinkle. Support for bower (bower.json) will be removed in the future, please use npm/yarn (package.json) instead.`); + log.warn(`DEPRECATED: Detected bower.json in ${sprinkle} Sprinkle. Support for bower (bower.json) will be removed in the future, please use npm/yarn (package.json) instead. https://learn.userfrosting.com/upgrading/41-to-42#bower-deprecation-new-npm-support`); bowerPaths.push(path); } } diff --git a/build/tasks/build.js b/build/tasks/build.js index fb0d8fe25..9019cc023 100644 --- a/build/tasks/build.js +++ b/build/tasks/build.js @@ -1,24 +1,24 @@ -// @ts-check import gulp from "gulp"; import minifyCss from "gulp-clean-css"; import concatJs from "gulp-concat"; import concatCss from "gulp-concat-css"; -import prune from "gulp-prune"; +import { prune } from "gulp-prune"; import rev from "gulp-rev"; import minifyJs from "gulp-terser"; import { relative as relativePath } from "path"; import Bundler, { MergeRawConfigs, ValidateRawConfig } from "@userfrosting/gulp-bundle-assets"; import { readFileSync, writeFileSync } from "fs"; import { src } from "@userfrosting/vinyl-fs-vpath"; -import { Logger, vendorAssetsDir, sprinklesDir, sprinkles, sprinkleBundleFile, publicAssetsDir } from "./util.js"; +import { vendorAssetsDir, sprinklesDir, sprinkles, sprinkleBundleFile, publicAssetsDir } from "./util.js"; import gulpIf from "gulp-if"; +import { GulpLogLogger } from "@userfrosting/ts-log-adapter-gulplog"; import gulpSourcemaps from "gulp-sourcemaps"; /** * Compiles frontend assets. Mapped to npm script "uf-bundle". */ export function build() { - const log = new Logger(build.name); + const log = new GulpLogLogger(build.name); // Build sources list const sources = []; @@ -35,6 +35,7 @@ export function build() { sources.push("!**.php"); // Create bundle stream factories object + /** @type {import("@userfrosting/gulp-bundle-assets").Bundlers} */ const bundleBuilder = { Scripts: (src, name) => { return src @@ -131,7 +132,7 @@ export function build() { log.info("Finished writing results file.") }; - rawConfig.Logger = new Logger(`${build.name} - @userfrosting/gulp-bundle-assets`); + rawConfig.Logger = new GulpLogLogger(`${build.name} - @userfrosting/gulp-bundle-assets`); rawConfig.cwd = publicAssetsDir; // Open stream @@ -149,7 +150,7 @@ export function build() { /** * Used to filter to just styles and scripts. - * @param {import("vinyl").NullFile} fs + * @param {import("vinyl")} fs */ function stylesAndScriptsFilter(fs) { return scriptsFilter(fs) || stylesFilter(fs); @@ -157,7 +158,7 @@ function stylesAndScriptsFilter(fs) { /** * Used to filter to just styles. - * @param {import("vinyl").NullFile} fs + * @param {import("vinyl")} fs */ function stylesFilter(fs) { return fs.extname === ".css"; @@ -165,7 +166,7 @@ function stylesFilter(fs) { /** * Used to filter to just scripts. - * @param {import("vinyl").NullFile} fs + * @param {import("vinyl")} fs */ function scriptsFilter(fs) { return fs.extname === ".js"; diff --git a/build/tasks/clean.js b/build/tasks/clean.js index d7c6d3e25..628316cb8 100644 --- a/build/tasks/clean.js +++ b/build/tasks/clean.js @@ -1,13 +1,13 @@ -// @ts-check import { sync as deleteSync } from "del"; -import { vendorAssetsDir, publicAssetsDir, Logger } from "./util.js"; +import { vendorAssetsDir, publicAssetsDir } from "./util.js"; +import { GulpLogLogger } from "@userfrosting/ts-log-adapter-gulplog"; /** * Clean vendor and public asset folders. - * @param {(err?) => {}} done Used to mark task completion. + * @param {(err?: unknown) => {}} done Used to mark task completion. */ export function clean(done) { - const log = new Logger(clean.name); + const log = new GulpLogLogger(clean.name); try { log.info("Cleaning vendor assets..."); @@ -16,11 +16,12 @@ export function clean(done) { log.info("Cleaning public assets..."); deleteSync(publicAssetsDir, { force: true }) - log.info("Finsihed cleaning public assets."); + log.info("Finished cleaning public assets."); done(); } catch (error) { + log.error("Completed with error"); done(error); } }; diff --git a/build/tasks/util.js b/build/tasks/util.js index e43fc37e9..2bf0182b7 100644 --- a/build/tasks/util.js +++ b/build/tasks/util.js @@ -1,4 +1,3 @@ -// @ts-check import gulplog from "gulplog"; import { readFileSync } from "fs"; @@ -13,93 +12,14 @@ export const vendorAssetsDir = `${rootDir}/app/assets/`; export const logFile = `${rootDir}/app/logs/build.log`; // Load sprinkles +/** @type {string[]} */ let sprinkles; try { sprinkles = JSON.parse(readFileSync(sprinklesSchemaPath).toString()).base; } catch (error) { - gulplog.info(sprinklesSchemaPath + " could not be loaded, does it exist?"); + gulplog.error(sprinklesSchemaPath + " could not be loaded, does it exist?"); throw error; } export { sprinkles }; - -/** - * Log adapter for "ts-log" to "gulplog". - */ -export class Logger { - /** - * @param {string} source - */ - constructor(source) { - this.source = source; - } - - /** - * Composes complete message to log. - * @private - * @param {(x: string) => void} logFunc Logging function. - * @param {string} message Message to log. - * @param {any[]} optionalParams Values to log, encoded with `JSON.stringify`. - */ - log(logFunc, message, optionalParams) { - const messageLines = message.split("\n"); - - if (optionalParams.length > 0) { - if (messageLines.length > 1) { - messageLines.push(JSON.stringify(optionalParams)); - } else { - messageLines[0] = `${messageLines[0]} ${JSON.stringify(optionalParams)}`; - } - } - - for (const messageLine of messageLines) { - logFunc(`${this.source}: ${messageLine}`) - } - } - - /** - * Debug log level. - * @param {string} message Message to log. - * @param {...any} optionalParams Values to log, encoded with `JSON.stringify`. - */ - debug(message, ...optionalParams) { - this.log(gulplog.debug, message, optionalParams); - } - - /** - * Trace log level. - * @param {string} message Message to log. - * @param {...any} optionalParams Values to log, encoded with `JSON.stringify`. - */ - trace(message, ...optionalParams) { - this.log(gulplog.debug, message, optionalParams); - } - - /** - * "Standard" log level. - * @param {string} message Message to log. - * @param {...any} optionalParams Values to log, encoded with `JSON.stringify`. - */ - info(message, ...optionalParams) { - this.log(gulplog.info, message, optionalParams); - } - - /** - * Warning log level. - * @param {string} message Message to log. - * @param {...any} optionalParams Values to log, encoded with `JSON.stringify`. - */ - warn(message, ...optionalParams) { - this.log(gulplog.warn, message, optionalParams); - } - - /** - * Error log level. - * @param {string} message Message to log. - * @param {...any} optionalParams Values to log, encoded with `JSON.stringify`. - */ - error(message, ...optionalParams) { - this.log(gulplog.error, message, optionalParams); - } -} diff --git a/build/tsconfig.json b/build/tsconfig.json new file mode 100644 index 000000000..590f675f4 --- /dev/null +++ b/build/tsconfig.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "skipLibCheck": true, + "strict": true, + "module": "es2020", + "moduleResolution": "node", + "target": "es2020", + "noEmit": true, + "allowSyntheticDefaultImports": true + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index 34b3c18e8..0b4a6edb3 100755 --- a/composer.json +++ b/composer.json @@ -24,16 +24,16 @@ "vendor-dir": "app/vendor" }, "require": { - "php": ">=7.2", + "php": "^7.3 | ^8.0", "ext-gd": "*", "composer/installers": "^1.4.0", - "userfrosting/uniformresourcelocator": "~4.5.0", - "symfony/console": "^4.3", + "userfrosting/framework": "^4.6", + "symfony/console": "^5.1", "wikimedia/composer-merge-plugin": "^2.1.0" }, "require-dev": { "friendsofphp/php-cs-fixer": "^2.13", - "phpunit/phpunit": "^7.5 | ^8.5", + "phpunit/phpunit": "^9.5", "mockery/mockery": "^1.2", "league/factory-muffin": "^3.0", "league/factory-muffin-faker": "^2.0" diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 161175b00..0b1d42483 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,30 +1,21 @@ - - - - app/tests/Unit - app/sprinkles/*/tests/Unit - - - app/sprinkles/*/tests/Integration - - - - - - - - app/sprinkles/*/src/ - app/system/ - - + + + + app/sprinkles/*/src/ + app/system/ + + + + + app/tests/Unit + app/sprinkles/*/tests/Unit + + + app/sprinkles/*/tests/Integration + + + + + diff --git a/public/index.php b/public/index.php index a26563f20..e5dfb4537 100755 --- a/public/index.php +++ b/public/index.php @@ -17,6 +17,12 @@ // First off, we'll grab the Composer dependencies require_once __DIR__ . '/../app/vendor/autoload.php'; +// Workaround to get php built-in server to access assets +// @see : https://github.com/slimphp/Slim/issues/359#issuecomment-363076423 +if (PHP_SAPI == 'cli-server') { + $_SERVER['SCRIPT_NAME'] = '/index.php'; +} + use UserFrosting\System\UserFrosting; $uf = new UserFrosting(); diff --git a/vagrant/after.sh b/vagrant/after.sh index 1c741166c..2145f4750 100644 --- a/vagrant/after.sh +++ b/vagrant/after.sh @@ -14,14 +14,9 @@ npm cache clean -f npm install -g n n -q lts -# Ensuite PHP 7.4 is used (UF doesn't support PHP 8 yet...) -echo "\n\n >> Forcing PHP 7.4 in CLI\n" -sudo update-alternatives --set php /usr/bin/php7.4 - # Ensure composer deps are installed echo "\n\n >> Installating Composer\n" cd ${BASE_PATH} -composer self-update --1 composer install # Setup .env