From f7985bac61d5f01a84220f4acb1baac6bd7ec897 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov <38059171+askvortsov1@users.noreply.github.com> Date: Tue, 19 May 2020 18:45:56 -0400 Subject: [PATCH] Use drivers for display names, add display name extender (#2174) * Deprecate GetDisplayName event * Add interface for display name driver * Add username driver as default * Add code to register supported drivers / used driver as singletons * Configured User class to use new driver-based system for display names * Add extender for adding display name driver * Add integration test for user display name driver * Add frontend UI for selecting display name driver --- .../js/src/admin/components/BasicsPage.js | 23 +++++++ .../core/src/Admin/Content/AdminPayload.php | 19 ++++- framework/core/src/Extend/User.php | 38 ++++++++++ .../src/User/DisplayName/DriverInterface.php | 25 +++++++ .../src/User/DisplayName/UsernameDriver.php | 23 +++++++ .../core/src/User/Event/GetDisplayName.php | 3 + framework/core/src/User/User.php | 21 +++++- .../core/src/User/UserServiceProvider.php | 29 ++++++++ .../tests/integration/extenders/UserTest.php | 69 +++++++++++++++++++ 9 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 framework/core/src/Extend/User.php create mode 100644 framework/core/src/User/DisplayName/DriverInterface.php create mode 100644 framework/core/src/User/DisplayName/UsernameDriver.php create mode 100644 framework/core/tests/integration/extenders/UserTest.php diff --git a/framework/core/js/src/admin/components/BasicsPage.js b/framework/core/js/src/admin/components/BasicsPage.js index 746bcce8b..fb692c4fc 100644 --- a/framework/core/js/src/admin/components/BasicsPage.js +++ b/framework/core/js/src/admin/components/BasicsPage.js @@ -21,6 +21,7 @@ export default class BasicsPage extends Page { 'default_route', 'welcome_title', 'welcome_message', + 'display_name_driver', ]; this.values = {}; @@ -33,6 +34,14 @@ export default class BasicsPage extends Page { this.localeOptions[i] = `${locales[i]} (${i})`; } + this.displayNameOptions = {}; + const displayNameDrivers = app.data.displayNameDrivers; + displayNameDrivers.forEach(function (identifier) { + this.displayNameOptions[identifier] = identifier; + }, this); + + if (!this.values.display_name_driver() && displayNameDrivers.includes('username')) this.values.display_name_driver('username'); + if (typeof this.values.show_language_selector() !== 'number') this.values.show_language_selector(1); } @@ -114,6 +123,20 @@ export default class BasicsPage extends Page { ], })} + {Object.keys(this.displayNameOptions).length > 1 + ? FieldSet.component({ + label: app.translator.trans('core.admin.basics.display_name_heading'), + children: [ +
{app.translator.trans('core.admin.basics.display_name_text')}
, + Select.component({ + options: this.displayNameOptions, + value: this.values.display_name_driver(), + onchange: this.values.display_name_driver, + }), + ], + }) + : ''} + {Button.component({ type: 'submit', className: 'Button Button--primary', diff --git a/framework/core/src/Admin/Content/AdminPayload.php b/framework/core/src/Admin/Content/AdminPayload.php index 685289107..e766c072a 100644 --- a/framework/core/src/Admin/Content/AdminPayload.php +++ b/framework/core/src/Admin/Content/AdminPayload.php @@ -14,12 +14,18 @@ use Flarum\Frontend\Document; use Flarum\Group\Permission; use Flarum\Settings\Event\Deserializing; use Flarum\Settings\SettingsRepositoryInterface; +use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Database\ConnectionInterface; use Psr\Http\Message\ServerRequestInterface as Request; class AdminPayload { + /** + * @var Container; + */ + protected $container; + /** * @var SettingsRepositoryInterface */ @@ -36,13 +42,20 @@ class AdminPayload protected $db; /** + * @param Container $container * @param SettingsRepositoryInterface $settings * @param ExtensionManager $extensions * @param ConnectionInterface $db * @param Dispatcher $events */ - public function __construct(SettingsRepositoryInterface $settings, ExtensionManager $extensions, ConnectionInterface $db, Dispatcher $events) - { + public function __construct( + Container $container, + SettingsRepositoryInterface $settings, + ExtensionManager $extensions, + ConnectionInterface $db, + Dispatcher $events + ) { + $this->container = $container; $this->settings = $settings; $this->extensions = $extensions; $this->db = $db; @@ -61,6 +74,8 @@ class AdminPayload $document->payload['permissions'] = Permission::map(); $document->payload['extensions'] = $this->extensions->getExtensions()->toArray(); + $document->payload['displayNameDrivers'] = array_keys($this->container->make('flarum.user.display_name.supported_drivers')); + $document->payload['phpVersion'] = PHP_VERSION; $document->payload['mysqlVersion'] = $this->db->selectOne('select version() as version')->version; } diff --git a/framework/core/src/Extend/User.php b/framework/core/src/Extend/User.php new file mode 100644 index 000000000..6882757d0 --- /dev/null +++ b/framework/core/src/Extend/User.php @@ -0,0 +1,38 @@ +drivers[$identifier] = $driver; + + return $this; + } + + public function extend(Container $container, Extension $extension = null) + { + $container->extend('flarum.user.display_name.supported_drivers', function ($existingDrivers) { + return array_merge($existingDrivers, $this->drivers); + }); + } +} diff --git a/framework/core/src/User/DisplayName/DriverInterface.php b/framework/core/src/User/DisplayName/DriverInterface.php new file mode 100644 index 000000000..f04207532 --- /dev/null +++ b/framework/core/src/User/DisplayName/DriverInterface.php @@ -0,0 +1,25 @@ +username; + } +} diff --git a/framework/core/src/User/Event/GetDisplayName.php b/framework/core/src/User/Event/GetDisplayName.php index ff35307c0..73106c70a 100644 --- a/framework/core/src/User/Event/GetDisplayName.php +++ b/framework/core/src/User/Event/GetDisplayName.php @@ -11,6 +11,9 @@ namespace Flarum\User\Event; use Flarum\User\User; +/** + * @deprecated beta 14, removed beta 15. + */ class GetDisplayName { /** diff --git a/framework/core/src/User/User.php b/framework/core/src/User/User.php index 8fc28e74c..a9d0eeb1b 100644 --- a/framework/core/src/User/User.php +++ b/framework/core/src/User/User.php @@ -23,6 +23,7 @@ use Flarum\Http\AccessToken; use Flarum\Http\UrlGenerator; use Flarum\Notification\Notification; use Flarum\Post\Post; +use Flarum\User\DisplayName\DriverInterface; use Flarum\User\Event\Activated; use Flarum\User\Event\AvatarChanged; use Flarum\User\Event\CheckingPassword; @@ -93,6 +94,13 @@ class User extends AbstractModel */ protected static $preferences = []; + /** + * A driver for getting display names. + * + * @var DriverInterface + */ + protected static $displayNameDriver; + /** * The hasher with which to hash passwords. * @@ -172,6 +180,16 @@ class User extends AbstractModel static::$gate = $gate; } + /** + * Set the display name driver. + * + * @param DriverInterface $driver + */ + public static function setDisplayNameDriver(DriverInterface $driver) + { + static::$displayNameDriver = $driver; + } + /** * Rename the user. * @@ -309,7 +327,8 @@ class User extends AbstractModel */ public function getDisplayNameAttribute() { - return static::$dispatcher->until(new GetDisplayName($this)) ?: $this->username; + // Event is deprecated in beta 14, remove in beta 15. + return static::$dispatcher->until(new GetDisplayName($this)) ?: static::$displayNameDriver->displayName($this); } /** diff --git a/framework/core/src/User/UserServiceProvider.php b/framework/core/src/User/UserServiceProvider.php index 88f5b93ec..881f15b79 100644 --- a/framework/core/src/User/UserServiceProvider.php +++ b/framework/core/src/User/UserServiceProvider.php @@ -12,12 +12,16 @@ namespace Flarum\User; use Flarum\Event\ConfigureUserPreferences; use Flarum\Event\GetPermission; use Flarum\Foundation\AbstractServiceProvider; +use Flarum\Settings\SettingsRepositoryInterface; +use Flarum\User\DisplayName\DriverInterface; +use Flarum\User\DisplayName\UsernameDriver; use Flarum\User\Event\EmailChangeRequested; use Flarum\User\Event\Registered; use Flarum\User\Event\Saving; use Illuminate\Contracts\Auth\Access\Gate as GateContract; use Illuminate\Contracts\Container\Container; use Illuminate\Contracts\Filesystem\Factory; +use Illuminate\Support\Arr; use League\Flysystem\FilesystemInterface; use RuntimeException; @@ -30,6 +34,30 @@ class UserServiceProvider extends AbstractServiceProvider { $this->registerGate(); $this->registerAvatarsFilesystem(); + $this->registerDisplayNameDrivers(); + } + + protected function registerDisplayNameDrivers() + { + $this->app->singleton('flarum.user.display_name.supported_drivers', function () { + return [ + 'username' => UsernameDriver::class, + ]; + }); + + $this->app->singleton('flarum.user.display_name.driver', function () { + $drivers = $this->app->make('flarum.user.display_name.supported_drivers'); + $settings = $this->app->make(SettingsRepositoryInterface::class); + $driverName = $settings->get('display_name_driver', ''); + + $driverClass = Arr::get($drivers, $driverName); + + return $driverClass + ? $this->app->make($driverClass) + : $this->app->make(UsernameDriver::class); + }); + + $this->app->alias('flarum.user.display_name.driver', DriverInterface::class); } protected function registerGate() @@ -84,6 +112,7 @@ class UserServiceProvider extends AbstractServiceProvider User::setHasher($this->app->make('hash')); User::setGate($this->app->make('flarum.gate')); + User::setDisplayNameDriver($this->app->make('flarum.user.display_name.driver')); $events = $this->app->make('events'); diff --git a/framework/core/tests/integration/extenders/UserTest.php b/framework/core/tests/integration/extenders/UserTest.php new file mode 100644 index 000000000..78449b373 --- /dev/null +++ b/framework/core/tests/integration/extenders/UserTest.php @@ -0,0 +1,69 @@ +prepareDatabase([ + 'users' => [ + $this->adminUser(), + ], 'settings' => [ + ['key' => 'display_name_driver', 'value' => 'custom'], + ], + ]); + } + + /** + * @test + */ + public function username_display_name_driver_used_by_default() + { + $this->prepDb(); + + $user = User::find(1); + + $this->assertEquals('admin', $user->displayName); + } + + /** + * @test + */ + public function can_use_custom_display_name_driver() + { + $this->extend( + (new Extend\User) + ->displayNameDriver('custom', CustomDisplayNameDriver::class) + ); + + $this->prepDb(); + + $user = User::find(1); + + $this->assertEquals('admin@machine.local$$$suffix', $user->displayName); + } +} + +class CustomDisplayNameDriver implements DriverInterface +{ + public function displayName(User $user): string + { + return $user->email.'$$$suffix'; + } +}