From 2b8b63cb012beb04b8b9acbbda45ed6b719e9ab9 Mon Sep 17 00:00:00 2001 From: epoxa Date: Sat, 19 Aug 2017 12:13:21 +0700 Subject: [PATCH 01/72] Fix oauth controller wrong session method call (#1226) --- .../core/src/Forum/Controller/AbstractOAuth2Controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/core/src/Forum/Controller/AbstractOAuth2Controller.php b/framework/core/src/Forum/Controller/AbstractOAuth2Controller.php index 5083e793d..52f8c1e17 100644 --- a/framework/core/src/Forum/Controller/AbstractOAuth2Controller.php +++ b/framework/core/src/Forum/Controller/AbstractOAuth2Controller.php @@ -66,7 +66,7 @@ abstract class AbstractOAuth2Controller implements ControllerInterface return new RedirectResponse($authUrl.'&display=popup'); } elseif (! $state || $state !== $session->get('oauth2state')) { - $session->forget('oauth2state'); + $session->remove('oauth2state'); echo 'Invalid state. Please close the window and try again.'; exit; } From 08024b455d2d588a7b22069503f8ffd3f9e3c5b1 Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Tue, 12 Sep 2017 20:41:17 +0200 Subject: [PATCH 02/72] Issue template: Explanation first --- framework/core/.github/ISSUE_TEMPLATE.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/framework/core/.github/ISSUE_TEMPLATE.md b/framework/core/.github/ISSUE_TEMPLATE.md index 4f940e093..e7a082240 100644 --- a/framework/core/.github/ISSUE_TEMPLATE.md +++ b/framework/core/.github/ISSUE_TEMPLATE.md @@ -2,7 +2,13 @@ --- > Try to complete the below form as far as you are able and are willing to share. Add a screenshot of the issue if you can. -## Bug report +## Explanation + +Explain, in simple terms, but with as much detail as possible, your issue. +Be specific: What happened? What would you expect to happen? What have you tried so far? + +## Technical details + - Version of Flarum: x.y.z - Website URL where the bug is visible: http://example.com - The webserver you are running: apache, nginx or something else @@ -16,9 +22,6 @@ Output of "php flarum info", run this in terminal in your Flarum directory. ``` -## Additional comments -Some additional information you'd like to share, eg what have you tried so far. - ## Log files ``` From c85a3ab762dfca7e01a4b90450f4c6b05fdaa434 Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Fri, 15 Sep 2017 13:27:11 +0200 Subject: [PATCH 03/72] Update zend-diactoros to v1.6 This release contains a useful fix for Content-Length problems that we have experienced before. See https://github.com/zendframework/zend-diactoros/releases/tag/1.6.0. --- framework/core/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/core/composer.json b/framework/core/composer.json index efdb49182..1ee4195f0 100644 --- a/framework/core/composer.json +++ b/framework/core/composer.json @@ -52,7 +52,7 @@ "symfony/yaml": "^2.7", "s9e/text-formatter": "^0.8.1", "tobscure/json-api": "^0.3.0", - "zendframework/zend-diactoros": "^1.1", + "zendframework/zend-diactoros": "^1.6", "zendframework/zend-stratigility": "^1.3" }, "require-dev": { From 5bfe75c76323b851c16deb60c7cca04f961693b5 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Tue, 19 Sep 2017 12:13:24 +0930 Subject: [PATCH 04/72] Allow setting the raw content of a CommentPost --- framework/core/src/Core/Post/CommentPost.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/framework/core/src/Core/Post/CommentPost.php b/framework/core/src/Core/Post/CommentPost.php index 492088552..3db0c7555 100755 --- a/framework/core/src/Core/Post/CommentPost.php +++ b/framework/core/src/Core/Post/CommentPost.php @@ -153,6 +153,16 @@ class CommentPost extends Post $this->attributes['content'] = $value ? static::$formatter->parse($value, $this) : null; } + /** + * Set the parsed/raw content. + * + * @param string $value + */ + public function setParsedContentAttribute($value) + { + $this->attributes['content'] = $value; + } + /** * Get the content rendered as HTML. * From 5fc3ea94b544f5f43b2db3d14a62a4816bbdc1ec Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Tue, 19 Sep 2017 19:03:12 +0930 Subject: [PATCH 05/72] Allow full URLs to be used as the avatar path This is useful for forums integrating with an external website (eg. a WordPress site), so they can reference existing avatars directly. For alternative storage locations (eg. S3) the best practice will still be to store a relative path and then configure an external base "assets URL" (this is not currently possible - TODO). Given this change, I think it would probably make sense to rename the column to `avatar_url` in the upcoming batch of database naming changes - then it can contain either a relative or an absolute URL - @franzliedke do you agree? --- framework/core/src/Core/User.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/framework/core/src/Core/User.php b/framework/core/src/Core/User.php index 32f58e86a..338d38d47 100755 --- a/framework/core/src/Core/User.php +++ b/framework/core/src/Core/User.php @@ -323,7 +323,14 @@ class User extends AbstractModel */ public function getAvatarUrlAttribute() { - $urlGenerator = app('Flarum\Forum\UrlGenerator'); + if ($this->avatar_path) { + if (strpos($this->avatar_path, '://') !== false) { + return $this->avatar_path; + } else { + return app('Flarum\Forum\UrlGenerator')->toPath('assets/avatars/'.$this->avatar_path); + } + } + } return $this->avatar_path ? $urlGenerator->toPath('assets/avatars/'.$this->avatar_path) : null; } From 705ef7536b80bc72ddfce340c5840a4f687c389d Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Tue, 19 Sep 2017 19:10:07 +0930 Subject: [PATCH 06/72] Don't include post content in the "basic" serializer Currently all of a post's replies are loaded in full whenever the post is loaded, which is kind of overkill - we really just need to know that they exist (and who posted them) in order to render the "X replied to this" line. --- .../core/src/Api/Serializer/DiscussionBasicSerializer.php | 6 +++--- framework/core/src/Api/Serializer/PostBasicSerializer.php | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/framework/core/src/Api/Serializer/DiscussionBasicSerializer.php b/framework/core/src/Api/Serializer/DiscussionBasicSerializer.php index 39bee72ff..99416b10e 100644 --- a/framework/core/src/Api/Serializer/DiscussionBasicSerializer.php +++ b/framework/core/src/Api/Serializer/DiscussionBasicSerializer.php @@ -54,7 +54,7 @@ class DiscussionBasicSerializer extends AbstractSerializer */ protected function startPost($discussion) { - return $this->hasOne($discussion, 'Flarum\Api\Serializer\PostBasicSerializer'); + return $this->hasOne($discussion, 'Flarum\Api\Serializer\PostSerializer'); } /** @@ -70,7 +70,7 @@ class DiscussionBasicSerializer extends AbstractSerializer */ protected function lastPost($discussion) { - return $this->hasOne($discussion, 'Flarum\Api\Serializer\PostBasicSerializer'); + return $this->hasOne($discussion, 'Flarum\Api\Serializer\PostSerializer'); } /** @@ -86,6 +86,6 @@ class DiscussionBasicSerializer extends AbstractSerializer */ protected function relevantPosts($discussion) { - return $this->hasMany($discussion, 'Flarum\Api\Serializer\PostBasicSerializer'); + return $this->hasMany($discussion, 'Flarum\Api\Serializer\PostSerializer'); } } diff --git a/framework/core/src/Api/Serializer/PostBasicSerializer.php b/framework/core/src/Api/Serializer/PostBasicSerializer.php index 962a7b28b..c5ef11168 100644 --- a/framework/core/src/Api/Serializer/PostBasicSerializer.php +++ b/framework/core/src/Api/Serializer/PostBasicSerializer.php @@ -43,12 +43,6 @@ class PostBasicSerializer extends AbstractSerializer 'contentType' => $post->type ]; - if ($post instanceof CommentPost) { - $attributes['contentHtml'] = $post->content_html; - } else { - $attributes['content'] = $post->content; - } - return $attributes; } From f1480131088870a3f2c69822e865318d39ddcd53 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Tue, 19 Sep 2017 19:13:02 +0930 Subject: [PATCH 07/72] =?UTF-8?q?=F0=9F=98=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- framework/core/src/Core/User.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/framework/core/src/Core/User.php b/framework/core/src/Core/User.php index 338d38d47..fa4568df7 100755 --- a/framework/core/src/Core/User.php +++ b/framework/core/src/Core/User.php @@ -332,9 +332,6 @@ class User extends AbstractModel } } - return $this->avatar_path ? $urlGenerator->toPath('assets/avatars/'.$this->avatar_path) : null; - } - /** * Get the user's locale, falling back to the forum's default if they * haven't set one. From cb0e9bd5074904833d7e1d46e6d3290210309f6e Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Tue, 19 Sep 2017 19:44:43 +0930 Subject: [PATCH 08/72] Remove unnecessary else statement --- framework/core/src/Core/User.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/core/src/Core/User.php b/framework/core/src/Core/User.php index fa4568df7..4f6238278 100755 --- a/framework/core/src/Core/User.php +++ b/framework/core/src/Core/User.php @@ -326,9 +326,9 @@ class User extends AbstractModel if ($this->avatar_path) { if (strpos($this->avatar_path, '://') !== false) { return $this->avatar_path; - } else { - return app('Flarum\Forum\UrlGenerator')->toPath('assets/avatars/'.$this->avatar_path); } + + return app('Flarum\Forum\UrlGenerator')->toPath('assets/avatars/'.$this->avatar_path); } } From 65ffee8696d6e8625fe3321e800f0cdd55ee1190 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Tue, 19 Sep 2017 19:45:31 +0930 Subject: [PATCH 09/72] Use ::class --- framework/core/src/Core/User.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/core/src/Core/User.php b/framework/core/src/Core/User.php index 4f6238278..dd50ab449 100755 --- a/framework/core/src/Core/User.php +++ b/framework/core/src/Core/User.php @@ -29,6 +29,7 @@ use Flarum\Event\UserWasActivated; use Flarum\Event\UserWasDeleted; use Flarum\Event\UserWasRegistered; use Flarum\Event\UserWasRenamed; +use Flarum\Forum\UrlGenerator; use Flarum\Foundation\Application; use Illuminate\Contracts\Hashing\Hasher; use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -328,7 +329,7 @@ class User extends AbstractModel return $this->avatar_path; } - return app('Flarum\Forum\UrlGenerator')->toPath('assets/avatars/'.$this->avatar_path); + return app(UrlGenerator::class)->toPath('assets/avatars/'.$this->avatar_path); } } From 6f75d95e9f98e96d2adfeb7d61904bc2fbc10678 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Wed, 20 Sep 2017 16:42:18 +0930 Subject: [PATCH 10/72] User display names (#1246) * Introduce user display names It is not uncommon for forums to be intergrated with sites where users don't have a unique "handle" - they might just have their first name, or a full name, which is not guaranteed to be unique. This commit introduces the concept of "display names" for users. By default display names are the same as usernames, but extensions may override this and set them to something different. The important thing is that all code should use `display_name` whenever intending to output a human-readable name - `username` is reserved for cases where you want to output a unique identifier (which may or may not be human-friendly). The new "GetDisplayName" API is probably sub-optimal, but I didn't worry too much because we can come up with something better in `next-back`. ref #557 * Apply fixes from StyleCI [ci skip] [skip ci] --- framework/core/js/forum/dist/app.js | 7 +++-- .../forum/src/components/UsersSearchSource.js | 2 +- framework/core/js/lib/helpers/username.js | 2 +- framework/core/js/lib/models/User.js | 1 + .../Api/Serializer/PostBasicSerializer.php | 1 - .../Api/Serializer/UserBasicSerializer.php | 5 ++-- .../Command/RequestPasswordResetHandler.php | 2 +- .../Core/Listener/EmailConfirmationMailer.php | 2 +- framework/core/src/Core/User.php | 11 +++++++ framework/core/src/Event/GetDisplayName.php | 30 +++++++++++++++++++ 10 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 framework/core/src/Event/GetDisplayName.php diff --git a/framework/core/js/forum/dist/app.js b/framework/core/js/forum/dist/app.js index 16374dc0b..fffd65333 100644 --- a/framework/core/js/forum/dist/app.js +++ b/framework/core/js/forum/dist/app.js @@ -28942,7 +28942,9 @@ System.register('flarum/components/UsersSearchSource', ['flarum/helpers/highligh query = query.toLowerCase(); var results = app.store.all('users').filter(function (user) { - return user.username().toLowerCase().substr(0, query.length) === query; + return [user.username(), user.displayName()].some(function (value) { + return value.toLowerCase().substr(0, query.length) === query; + }); }); if (!results.length) return ''; @@ -29534,7 +29536,7 @@ System.register("flarum/helpers/username", [], function (_export, _context) { "use strict"; function username(user) { - var name = user && user.username() || app.translator.trans('core.lib.username.deleted_text'); + var name = user && user.displayName() || app.translator.trans('core.lib.username.deleted_text'); return m( "span", @@ -30533,6 +30535,7 @@ System.register('flarum/models/User', ['flarum/Model', 'flarum/utils/stringToCol babelHelpers.extends(User.prototype, { username: Model.attribute('username'), + displayName: Model.attribute('displayName'), email: Model.attribute('email'), isActivated: Model.attribute('isActivated'), password: Model.attribute('password'), diff --git a/framework/core/js/forum/src/components/UsersSearchSource.js b/framework/core/js/forum/src/components/UsersSearchSource.js index f05fbb30d..7f687b811 100644 --- a/framework/core/js/forum/src/components/UsersSearchSource.js +++ b/framework/core/js/forum/src/components/UsersSearchSource.js @@ -20,7 +20,7 @@ export default class UsersSearchResults { query = query.toLowerCase(); const results = app.store.all('users') - .filter(user => user.username().toLowerCase().substr(0, query.length) === query); + .filter(user => [user.username(), user.displayName()].some(value => value.toLowerCase().substr(0, query.length) === query)); if (!results.length) return ''; diff --git a/framework/core/js/lib/helpers/username.js b/framework/core/js/lib/helpers/username.js index 16e12654f..7e05ca4d6 100644 --- a/framework/core/js/lib/helpers/username.js +++ b/framework/core/js/lib/helpers/username.js @@ -6,7 +6,7 @@ * @return {Object} */ export default function username(user) { - const name = (user && user.username()) || app.translator.trans('core.lib.username.deleted_text'); + const name = (user && user.displayName()) || app.translator.trans('core.lib.username.deleted_text'); return {name}; } diff --git a/framework/core/js/lib/models/User.js b/framework/core/js/lib/models/User.js index a28173c3b..07d5aaf1b 100644 --- a/framework/core/js/lib/models/User.js +++ b/framework/core/js/lib/models/User.js @@ -10,6 +10,7 @@ export default class User extends Model {} Object.assign(User.prototype, { username: Model.attribute('username'), + displayName: Model.attribute('displayName'), email: Model.attribute('email'), isActivated: Model.attribute('isActivated'), password: Model.attribute('password'), diff --git a/framework/core/src/Api/Serializer/PostBasicSerializer.php b/framework/core/src/Api/Serializer/PostBasicSerializer.php index c5ef11168..3a79eed84 100644 --- a/framework/core/src/Api/Serializer/PostBasicSerializer.php +++ b/framework/core/src/Api/Serializer/PostBasicSerializer.php @@ -12,7 +12,6 @@ namespace Flarum\Api\Serializer; use Flarum\Core\Post; -use Flarum\Core\Post\CommentPost; use InvalidArgumentException; class PostBasicSerializer extends AbstractSerializer diff --git a/framework/core/src/Api/Serializer/UserBasicSerializer.php b/framework/core/src/Api/Serializer/UserBasicSerializer.php index 2699c8714..f5919ebf5 100644 --- a/framework/core/src/Api/Serializer/UserBasicSerializer.php +++ b/framework/core/src/Api/Serializer/UserBasicSerializer.php @@ -36,8 +36,9 @@ class UserBasicSerializer extends AbstractSerializer } return [ - 'username' => $user->username, - 'avatarUrl' => $user->avatar_url + 'username' => $user->username, + 'displayName' => $user->display_name, + 'avatarUrl' => $user->avatar_url ]; } diff --git a/framework/core/src/Core/Command/RequestPasswordResetHandler.php b/framework/core/src/Core/Command/RequestPasswordResetHandler.php index fb484fbda..3318c9882 100644 --- a/framework/core/src/Core/Command/RequestPasswordResetHandler.php +++ b/framework/core/src/Core/Command/RequestPasswordResetHandler.php @@ -107,7 +107,7 @@ class RequestPasswordResetHandler $token->save(); $data = [ - '{username}' => $user->username, + '{username}' => $user->display_name, '{url}' => $this->url->toRoute('resetPassword', ['token' => $token->id]), '{forum}' => $this->settings->get('forum_title'), ]; diff --git a/framework/core/src/Core/Listener/EmailConfirmationMailer.php b/framework/core/src/Core/Listener/EmailConfirmationMailer.php index 295d45d7c..6c7583cad 100755 --- a/framework/core/src/Core/Listener/EmailConfirmationMailer.php +++ b/framework/core/src/Core/Listener/EmailConfirmationMailer.php @@ -130,7 +130,7 @@ class EmailConfirmationMailer $token = $this->generateToken($user, $email); return [ - '{username}' => $user->username, + '{username}' => $user->display_name, '{url}' => $this->url->toRoute('confirmEmail', ['token' => $token->id]), '{forum}' => $this->settings->get('forum_title') ]; diff --git a/framework/core/src/Core/User.php b/framework/core/src/Core/User.php index dd50ab449..e8325b0e5 100755 --- a/framework/core/src/Core/User.php +++ b/framework/core/src/Core/User.php @@ -18,6 +18,7 @@ use Flarum\Core\Support\ScopeVisibilityTrait; use Flarum\Database\AbstractModel; use Flarum\Event\CheckUserPassword; use Flarum\Event\ConfigureUserPreferences; +use Flarum\Event\GetDisplayName; use Flarum\Event\PostWasDeleted; use Flarum\Event\PrepareUserGroups; use Flarum\Event\UserAvatarWasChanged; @@ -333,6 +334,16 @@ class User extends AbstractModel } } + /** + * Get the user's display name. + * + * @return string + */ + public function getDisplayNameAttribute() + { + return static::$dispatcher->until(new GetDisplayName($this)) ?: $this->username; + } + /** * Get the user's locale, falling back to the forum's default if they * haven't set one. diff --git a/framework/core/src/Event/GetDisplayName.php b/framework/core/src/Event/GetDisplayName.php new file mode 100644 index 000000000..71fbe6ba4 --- /dev/null +++ b/framework/core/src/Event/GetDisplayName.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Event; + +use Flarum\Core\User; + +class GetDisplayName +{ + /** + * @var User + */ + public $user; + + /** + * @param User $user + */ + public function __construct(User $user) + { + $this->user = $user; + } +} From 64cb0e866f3c7527da8630ab0b776561ab45e817 Mon Sep 17 00:00:00 2001 From: Tobias Weichart Date: Wed, 4 Oct 2017 08:38:36 +0200 Subject: [PATCH 11/72] minor change for getting the path * should be used via x_path() methods + parameter --- framework/core/src/Asset/LessCompiler.php | 2 +- framework/core/src/Database/MigrationCreator.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/core/src/Asset/LessCompiler.php b/framework/core/src/Asset/LessCompiler.php index 382a1b210..d498b08d9 100644 --- a/framework/core/src/Asset/LessCompiler.php +++ b/framework/core/src/Asset/LessCompiler.php @@ -49,7 +49,7 @@ class LessCompiler extends RevisionCompiler 'compress' => true, 'cache_dir' => $this->cachePath, 'import_dirs' => [ - base_path().'/vendor/components/font-awesome/less' => '', + base_path('vendor/components/font-awesome/less') => '', ], ]); diff --git a/framework/core/src/Database/MigrationCreator.php b/framework/core/src/Database/MigrationCreator.php index b3a912206..53fa22bff 100755 --- a/framework/core/src/Database/MigrationCreator.php +++ b/framework/core/src/Database/MigrationCreator.php @@ -107,7 +107,7 @@ class MigrationCreator */ protected function getMigrationPath($extension) { - $parent = $extension ? public_path().'/extensions/'.$extension : __DIR__.'/../..'; + $parent = $extension ? public_path('extensions/'.$extension) : __DIR__.'/../..'; return $parent.'/migrations'; } From 395cd3e53f765134ca7b043b95209aec7f0d175f Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Thu, 5 Oct 2017 11:43:12 +1030 Subject: [PATCH 12/72] Use display name as document title --- framework/core/js/forum/src/components/UserPage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/core/js/forum/src/components/UserPage.js b/framework/core/js/forum/src/components/UserPage.js index e7bebdd34..e8679ea58 100644 --- a/framework/core/js/forum/src/components/UserPage.js +++ b/framework/core/js/forum/src/components/UserPage.js @@ -72,7 +72,7 @@ export default class UserPage extends Page { show(user) { this.user = user; - app.setTitle(user.username()); + app.setTitle(user.displayName()); m.redraw(); } From 825997c857a6c22b5970d119c311e6ebc26d1465 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Thu, 5 Oct 2017 11:48:10 +1030 Subject: [PATCH 13/72] Fix post contentHtml sometimes breaking script parsing <\/script> tags in post content would sometimes be recognized as actually ending the script tag, even when escaped (not exactly sure why). This is fixed by encoding the < > characters in unicode. --- framework/core/views/app.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/core/views/app.blade.php b/framework/core/views/app.blade.php index 2f98cb715..198fde143 100644 --- a/framework/core/views/app.blade.php +++ b/framework/core/views/app.blade.php @@ -49,7 +49,7 @@ if (module.default) module.default(app); } - app.boot({!! json_encode($payload) !!}); + app.boot({!! json_encode($payload, JSON_HEX_TAG) !!}); @if (! $debug) } catch (e) { window.location += (window.location.search ? '&' : '?') + 'nojs=1'; From 09f98a5cbb1a0bf26b92001466daa6b859223b2c Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Thu, 5 Oct 2017 12:25:30 +1030 Subject: [PATCH 14/72] Use a constant instead of a property --- .../core/src/Http/Middleware/AuthenticateWithHeader.php | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/framework/core/src/Http/Middleware/AuthenticateWithHeader.php b/framework/core/src/Http/Middleware/AuthenticateWithHeader.php index 9b34b11dc..1bf1fd51b 100644 --- a/framework/core/src/Http/Middleware/AuthenticateWithHeader.php +++ b/framework/core/src/Http/Middleware/AuthenticateWithHeader.php @@ -20,10 +20,7 @@ use Zend\Stratigility\MiddlewareInterface; class AuthenticateWithHeader implements MiddlewareInterface { - /** - * @var string - */ - protected $prefix = 'Token '; + const TOKEN_PREFIX = 'Token '; /** * {@inheritdoc} @@ -34,8 +31,8 @@ class AuthenticateWithHeader implements MiddlewareInterface $parts = explode(';', $headerLine); - if (isset($parts[0]) && starts_with($parts[0], $this->prefix)) { - $id = substr($parts[0], strlen($this->prefix)); + if (isset($parts[0]) && starts_with($parts[0], self::TOKEN_PREFIX)) { + $id = substr($parts[0], strlen(self::TOKEN_PREFIX)); if (isset($parts[1])) { if (ApiKey::find($id)) { From 8c782a00e92bbe2e29130e71136b2eef6c291585 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Thu, 5 Oct 2017 12:26:05 +1030 Subject: [PATCH 15/72] Add the ApiKey model as a request attribute --- framework/core/src/Http/Middleware/AuthenticateWithHeader.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/core/src/Http/Middleware/AuthenticateWithHeader.php b/framework/core/src/Http/Middleware/AuthenticateWithHeader.php index 1bf1fd51b..dfb3f8e9c 100644 --- a/framework/core/src/Http/Middleware/AuthenticateWithHeader.php +++ b/framework/core/src/Http/Middleware/AuthenticateWithHeader.php @@ -35,9 +35,10 @@ class AuthenticateWithHeader implements MiddlewareInterface $id = substr($parts[0], strlen(self::TOKEN_PREFIX)); if (isset($parts[1])) { - if (ApiKey::find($id)) { + if ($key = ApiKey::find($id)) { $actor = $this->getUser($parts[1]); + $request = $request->withAttribute('apiKey', $key); $request = $request->withAttribute('bypassFloodgate', true); } } elseif ($token = AccessToken::find($id)) { From 40e82520bdb635ef35ce501d6fa0875e0e45d933 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Thu, 5 Oct 2017 12:39:40 +1030 Subject: [PATCH 16/72] Allow configuring cookie attributes I decided to put this in config.php because if cookie settings were to be stored in the database and configured via admin UI, entering incorrect settings could cause the admin session to be destroyed, requiring manual database intervention to fix. But it's a good prompt for discussion as to which kind of settings belong in config.php vs the database. Thoughts? --- framework/core/src/Http/CookieFactory.php | 35 +++++++++++++++++-- .../core/src/Http/Middleware/StartSession.php | 7 ++-- framework/core/src/Http/Rememberer.php | 9 +++-- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/framework/core/src/Http/CookieFactory.php b/framework/core/src/Http/CookieFactory.php index 868db278c..aacaff7f5 100644 --- a/framework/core/src/Http/CookieFactory.php +++ b/framework/core/src/Http/CookieFactory.php @@ -45,7 +45,7 @@ class CookieFactory // Parse the forum's base URL so that we can determine the optimal cookie settings $url = parse_url(rtrim($this->app->url(), '/')); - $cookie = SetCookie::create($name, $value); + $cookie = SetCookie::create($this->getName($name), $value); // Make sure we send both the MaxAge and Expires parameters (the former // is not supported by all browser versions) @@ -55,9 +55,38 @@ class CookieFactory ->withExpires(time() + $maxAge); } + if ($domain = $this->app->config('cookie.domain')) { + $cookie = $cookie->withDomain($domain); + } + + $path = $this->app->config('cookie.path', array_get($url, 'path') ?: '/'); + $secure = $this->app->config('cookie.secure', array_get($url, 'scheme') === 'https'); + return $cookie - ->withPath(array_get($url, 'path') ?: '/') - ->withSecure(array_get($url, 'scheme') === 'https') + ->withPath($path) + ->withSecure($secure) ->withHttpOnly(true); } + + /** + * Make an expired cookie instance. + * + * @param string $name + * @return \Dflydev\FigCookies\SetCookie + */ + public function expire($name) + { + return $this->make($name)->expire(); + } + + /** + * Get a cookie name. + * + * @param string $name + * @return string + */ + public function getName($name) + { + return $this->app->config('cookie.name', 'flarum').'_'.$name; + } } diff --git a/framework/core/src/Http/Middleware/StartSession.php b/framework/core/src/Http/Middleware/StartSession.php index 9c6060bfc..bf714c99e 100644 --- a/framework/core/src/Http/Middleware/StartSession.php +++ b/framework/core/src/Http/Middleware/StartSession.php @@ -22,13 +22,14 @@ use Zend\Stratigility\MiddlewareInterface; class StartSession implements MiddlewareInterface { + const COOKIE_NAME = 'session'; + /** * @var CookieFactory */ protected $cookie; /** - * Rememberer constructor. * @param CookieFactory $cookie */ public function __construct(CookieFactory $cookie) @@ -56,7 +57,7 @@ class StartSession implements MiddlewareInterface { $session = new Session; - $session->setName('flarum_session'); + $session->setName($this->cookie->getName(self::COOKIE_NAME)); $session->start(); if (! $session->has('csrf_token')) { @@ -79,7 +80,7 @@ class StartSession implements MiddlewareInterface { return FigResponseCookies::set( $response, - $this->cookie->make($session->getName(), $session->getId()) + $this->cookie->make(self::COOKIE_NAME, $session->getId()) ); } } diff --git a/framework/core/src/Http/Rememberer.php b/framework/core/src/Http/Rememberer.php index a014c99d9..d331d796c 100644 --- a/framework/core/src/Http/Rememberer.php +++ b/framework/core/src/Http/Rememberer.php @@ -16,7 +16,7 @@ use Psr\Http\Message\ResponseInterface; class Rememberer { - protected $cookieName = 'flarum_remember'; + const COOKIE_NAME = 'remember'; /** * @var CookieFactory @@ -43,7 +43,7 @@ class Rememberer return FigResponseCookies::set( $response, - $this->cookie->make($this->cookieName, $token->id, $lifetime) + $this->cookie->make(self::COOKIE_NAME, $token->id, $lifetime) ); } @@ -56,6 +56,9 @@ class Rememberer public function forget(ResponseInterface $response) { - return FigResponseCookies::expire($response, $this->cookieName); + return FigResponseCookies::set( + $response, + $this->cookie->expire(self::COOKIE_NAME) + ); } } From 659b25bbfde5e22332c0c38534bc9b1626ce5225 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sat, 7 Oct 2017 17:39:27 +1030 Subject: [PATCH 17/72] Consolidate avatar uploading, allow avatarUrl to be used when updating user --- framework/core/src/Core/AvatarUploader.php | 55 ++++++++++++++ .../src/Core/Command/DeleteAvatarHandler.php | 18 ++--- .../core/src/Core/Command/EditUserHandler.php | 37 +++++++++- .../src/Core/Command/RegisterUserHandler.php | 59 ++++----------- .../src/Core/Command/UploadAvatarHandler.php | 72 ++++++------------- .../core/src/Core/CoreServiceProvider.php | 10 +-- .../core/src/Event/AvatarWillBeSaved.php | 13 ++-- 7 files changed, 143 insertions(+), 121 deletions(-) create mode 100755 framework/core/src/Core/AvatarUploader.php diff --git a/framework/core/src/Core/AvatarUploader.php b/framework/core/src/Core/AvatarUploader.php new file mode 100755 index 000000000..3d7c018e7 --- /dev/null +++ b/framework/core/src/Core/AvatarUploader.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Flarum\Core; + +use Intervention\Image\Image; +use League\Flysystem\FilesystemInterface; +use Illuminate\Support\Str; + +class AvatarUploader +{ + protected $uploadDir; + + public function __construct(FilesystemInterface $uploadDir) + { + $this->uploadDir = $uploadDir; + } + + public function upload(User $user, Image $image) + { + if (extension_loaded('exif')) { + $image->orientate(); + } + + $encodedImage = $image->fit(100, 100)->encode('png'); + + $avatarPath = Str::quickRandom().'.png'; + + $this->remove($user); + $user->changeAvatarPath($avatarPath); + + $this->uploadDir->put($avatarPath, $encodedImage); + } + + public function remove(User $user) + { + $avatarPath = $user->avatar_path; + + $user->afterSave(function () use ($avatarPath) { + if ($this->uploadDir->has($avatarPath)) { + $this->uploadDir->delete($avatarPath); + } + }); + + $user->changeAvatarPath(null); + } +} diff --git a/framework/core/src/Core/Command/DeleteAvatarHandler.php b/framework/core/src/Core/Command/DeleteAvatarHandler.php index 8efd972c7..2a7cb1638 100644 --- a/framework/core/src/Core/Command/DeleteAvatarHandler.php +++ b/framework/core/src/Core/Command/DeleteAvatarHandler.php @@ -12,6 +12,7 @@ namespace Flarum\Core\Command; use Flarum\Core\Access\AssertPermissionTrait; +use Flarum\Core\AvatarUploader; use Flarum\Core\Exception\PermissionDeniedException; use Flarum\Core\Repository\UserRepository; use Flarum\Core\Support\DispatchEventsTrait; @@ -30,20 +31,20 @@ class DeleteAvatarHandler protected $users; /** - * @var FilesystemInterface + * @var AvatarUploader */ - protected $uploadDir; + protected $uploader; /** * @param Dispatcher $events * @param UserRepository $users - * @param FilesystemInterface $uploadDir + * @param AvatarUploader $uploader */ - public function __construct(Dispatcher $events, UserRepository $users, FilesystemInterface $uploadDir) + public function __construct(Dispatcher $events, UserRepository $users, AvatarUploader $uploader) { $this->events = $events; $this->users = $users; - $this->uploadDir = $uploadDir; + $this->uploader = $uploader; } /** @@ -61,8 +62,7 @@ class DeleteAvatarHandler $this->assertCan($actor, 'edit', $user); } - $avatarPath = $user->avatar_path; - $user->changeAvatarPath(null); + $this->uploader->remove($user); $this->events->fire( new AvatarWillBeDeleted($user, $actor) @@ -70,10 +70,6 @@ class DeleteAvatarHandler $user->save(); - if ($this->uploadDir->has($avatarPath)) { - $this->uploadDir->delete($avatarPath); - } - $this->dispatchEventsFor($user, $actor); return $user; diff --git a/framework/core/src/Core/Command/EditUserHandler.php b/framework/core/src/Core/Command/EditUserHandler.php index d42acdfc9..d5d4bae1b 100644 --- a/framework/core/src/Core/Command/EditUserHandler.php +++ b/framework/core/src/Core/Command/EditUserHandler.php @@ -11,7 +11,9 @@ namespace Flarum\Core\Command; +use Exception; use Flarum\Core\Access\AssertPermissionTrait; +use Flarum\Core\AvatarUploader; use Flarum\Core\Repository\UserRepository; use Flarum\Core\Support\DispatchEventsTrait; use Flarum\Core\User; @@ -19,6 +21,9 @@ use Flarum\Core\Validator\UserValidator; use Flarum\Event\UserGroupsWereChanged; use Flarum\Event\UserWillBeSaved; use Illuminate\Contracts\Events\Dispatcher; +use Illuminate\Contracts\Validation\Factory; +use Illuminate\Contracts\Validation\ValidationException; +use Intervention\Image\ImageManager; class EditUserHandler { @@ -35,16 +40,30 @@ class EditUserHandler */ protected $validator; + /** + * @var AvatarUploader + */ + protected $avatarUploader; + + /** + * @var Factory + */ + private $validatorFactory; + /** * @param Dispatcher $events * @param UserRepository $users * @param UserValidator $validator + * @param AvatarUploader $avatarUploader + * @param Factory $validatorFactory */ - public function __construct(Dispatcher $events, UserRepository $users, UserValidator $validator) + public function __construct(Dispatcher $events, UserRepository $users, UserValidator $validator, AvatarUploader $avatarUploader, Factory $validatorFactory) { $this->events = $events; $this->users = $users; $this->validator = $validator; + $this->avatarUploader = $avatarUploader; + $this->validatorFactory = $validatorFactory; } /** @@ -135,6 +154,22 @@ class EditUserHandler }); } + if ($avatarUrl = array_get($attributes, 'avatarUrl')) { + $validation = $this->validatorFactory->make(compact('avatarUrl'), ['avatarUrl' => 'url']); + + if ($validation->fails()) { + throw new ValidationException($validation); + } + + try { + $image = (new ImageManager)->make($avatarUrl); + + $this->avatarUploader->upload($user, $image); + } catch (Exception $e) { + // + } + } + $this->events->fire( new UserWillBeSaved($user, $actor, $data) ); diff --git a/framework/core/src/Core/Command/RegisterUserHandler.php b/framework/core/src/Core/Command/RegisterUserHandler.php index c7daf38fa..f57492548 100644 --- a/framework/core/src/Core/Command/RegisterUserHandler.php +++ b/framework/core/src/Core/Command/RegisterUserHandler.php @@ -14,22 +14,17 @@ namespace Flarum\Core\Command; use Exception; use Flarum\Core\Access\AssertPermissionTrait; use Flarum\Core\AuthToken; +use Flarum\Core\AvatarUploader; use Flarum\Core\Exception\PermissionDeniedException; use Flarum\Core\Support\DispatchEventsTrait; use Flarum\Core\User; use Flarum\Core\Validator\UserValidator; use Flarum\Event\UserWillBeSaved; -use Flarum\Foundation\Application; use Flarum\Settings\SettingsRepositoryInterface; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Contracts\Validation\Factory; use Illuminate\Contracts\Validation\ValidationException; -use Illuminate\Support\Str; use Intervention\Image\ImageManager; -use League\Flysystem\Adapter\Local; -use League\Flysystem\Filesystem; -use League\Flysystem\FilesystemInterface; -use League\Flysystem\MountManager; class RegisterUserHandler { @@ -47,14 +42,9 @@ class RegisterUserHandler protected $validator; /** - * @var Application + * @var AvatarUploader */ - protected $app; - - /** - * @var FilesystemInterface - */ - protected $uploadDir; + protected $avatarUploader; /** * @var Factory @@ -65,17 +55,15 @@ class RegisterUserHandler * @param Dispatcher $events * @param SettingsRepositoryInterface $settings * @param UserValidator $validator - * @param Application $app - * @param FilesystemInterface $uploadDir + * @param AvatarUploader $avatarUploader * @param Factory $validatorFactory */ - public function __construct(Dispatcher $events, SettingsRepositoryInterface $settings, UserValidator $validator, Application $app, FilesystemInterface $uploadDir, Factory $validatorFactory) + public function __construct(Dispatcher $events, SettingsRepositoryInterface $settings, UserValidator $validator, AvatarUploader $avatarUploader, Factory $validatorFactory) { $this->events = $events; $this->settings = $settings; $this->validator = $validator; - $this->app = $app; - $this->uploadDir = $uploadDir; + $this->avatarUploader = $avatarUploader; $this->validatorFactory = $validatorFactory; } @@ -130,12 +118,6 @@ class RegisterUserHandler $user->activate(); } - $this->events->fire( - new UserWillBeSaved($user, $actor, $data) - ); - - $this->validator->assertValid(array_merge($user->getAttributes(), compact('password'))); - if ($avatarUrl = array_get($data, 'attributes.avatarUrl')) { $validation = $this->validatorFactory->make(compact('avatarUrl'), ['avatarUrl' => 'url']); @@ -144,12 +126,20 @@ class RegisterUserHandler } try { - $this->saveAvatarFromUrl($user, $avatarUrl); + $image = (new ImageManager)->make($avatarUrl); + + $this->avatarUploader->upload($user, $avatarUrl); } catch (Exception $e) { // } } + $this->events->fire( + new UserWillBeSaved($user, $actor, $data) + ); + + $this->validator->assertValid(array_merge($user->getAttributes(), compact('password'))); + $user->save(); if (isset($token)) { @@ -160,23 +150,4 @@ class RegisterUserHandler return $user; } - - private function saveAvatarFromUrl(User $user, $url) - { - $tmpFile = tempnam($this->app->storagePath().'/tmp', 'avatar'); - - $manager = new ImageManager; - $manager->make($url)->fit(100, 100)->save($tmpFile); - - $mount = new MountManager([ - 'source' => new Filesystem(new Local(pathinfo($tmpFile, PATHINFO_DIRNAME))), - 'target' => $this->uploadDir, - ]); - - $uploadName = Str::lower(Str::quickRandom()).'.png'; - - $user->changeAvatarPath($uploadName); - - $mount->move('source://'.pathinfo($tmpFile, PATHINFO_BASENAME), "target://$uploadName"); - } } diff --git a/framework/core/src/Core/Command/UploadAvatarHandler.php b/framework/core/src/Core/Command/UploadAvatarHandler.php index de451a074..02e655bd6 100644 --- a/framework/core/src/Core/Command/UploadAvatarHandler.php +++ b/framework/core/src/Core/Command/UploadAvatarHandler.php @@ -13,18 +13,14 @@ namespace Flarum\Core\Command; use Exception; use Flarum\Core\Access\AssertPermissionTrait; +use Flarum\Core\AvatarUploader; use Flarum\Core\Repository\UserRepository; use Flarum\Core\Support\DispatchEventsTrait; use Flarum\Core\Validator\AvatarValidator; use Flarum\Event\AvatarWillBeSaved; use Flarum\Foundation\Application; use Illuminate\Contracts\Events\Dispatcher; -use Illuminate\Support\Str; use Intervention\Image\ImageManager; -use League\Flysystem\Adapter\Local; -use League\Flysystem\Filesystem; -use League\Flysystem\FilesystemInterface; -use League\Flysystem\MountManager; use Symfony\Component\HttpFoundation\File\UploadedFile; class UploadAvatarHandler @@ -37,16 +33,16 @@ class UploadAvatarHandler */ protected $users; - /** - * @var FilesystemInterface - */ - protected $uploadDir; - /** * @var Application */ protected $app; + /** + * @var AvatarUploader + */ + protected $uploader; + /** * @var AvatarValidator */ @@ -55,16 +51,16 @@ class UploadAvatarHandler /** * @param Dispatcher $events * @param UserRepository $users - * @param FilesystemInterface $uploadDir * @param Application $app + * @param AvatarUploader $uploader * @param AvatarValidator $validator */ - public function __construct(Dispatcher $events, UserRepository $users, FilesystemInterface $uploadDir, Application $app, AvatarValidator $validator) + public function __construct(Dispatcher $events, UserRepository $users, Application $app, AvatarUploader $uploader, AvatarValidator $validator) { $this->events = $events; $this->users = $users; - $this->uploadDir = $uploadDir; $this->app = $app; + $this->uploader = $uploader; $this->validator = $validator; } @@ -83,60 +79,36 @@ class UploadAvatarHandler $this->assertCan($actor, 'edit', $user); } + $file = $command->file; + $tmpFile = tempnam($this->app->storagePath().'/tmp', 'avatar'); - $command->file->moveTo($tmpFile); + $file->moveTo($tmpFile); try { $file = new UploadedFile( $tmpFile, - $command->file->getClientFilename(), - $command->file->getClientMediaType(), - $command->file->getSize(), - $command->file->getError(), + $file->getClientFilename(), + $file->getClientMediaType(), + $file->getSize(), + $file->getError(), true ); $this->validator->assertValid(['avatar' => $file]); - $manager = new ImageManager; - - // Explicitly tell Intervention to encode the image as PNG (instead of having to guess from the extension) - // Read exif data to orientate avatar only if EXIF extension is enabled - if (extension_loaded('exif')) { - $encodedImage = $manager->make($tmpFile)->orientate()->fit(100, 100)->encode('png', 100); - } else { - $encodedImage = $manager->make($tmpFile)->fit(100, 100)->encode('png', 100); - } - file_put_contents($tmpFile, $encodedImage); + $image = (new ImageManager)->make($tmpFile); $this->events->fire( - new AvatarWillBeSaved($user, $actor, $tmpFile) + new AvatarWillBeSaved($user, $actor, $image) ); - $mount = new MountManager([ - 'source' => new Filesystem(new Local(pathinfo($tmpFile, PATHINFO_DIRNAME))), - 'target' => $this->uploadDir, - ]); - - if ($user->avatar_path && $mount->has($file = "target://$user->avatar_path")) { - $mount->delete($file); - } - - $uploadName = Str::lower(Str::quickRandom()).'.png'; - - $user->changeAvatarPath($uploadName); - - $mount->move('source://'.pathinfo($tmpFile, PATHINFO_BASENAME), "target://$uploadName"); + $this->uploader->upload($user, $image); $user->save(); - - $this->dispatchEventsFor($user, $actor); - - return $user; - } catch (Exception $e) { + } finally { @unlink($tmpFile); - - throw $e; } + + return $user; } } diff --git a/framework/core/src/Core/CoreServiceProvider.php b/framework/core/src/Core/CoreServiceProvider.php index 551cdcfda..713e5758e 100644 --- a/framework/core/src/Core/CoreServiceProvider.php +++ b/framework/core/src/Core/CoreServiceProvider.php @@ -49,15 +49,7 @@ class CoreServiceProvider extends AbstractServiceProvider return $app->make('Illuminate\Contracts\Filesystem\Factory')->disk('flarum-avatars')->getDriver(); }; - $this->app->when('Flarum\Core\Command\UploadAvatarHandler') - ->needs('League\Flysystem\FilesystemInterface') - ->give($avatarsFilesystem); - - $this->app->when('Flarum\Core\Command\DeleteAvatarHandler') - ->needs('League\Flysystem\FilesystemInterface') - ->give($avatarsFilesystem); - - $this->app->when('Flarum\Core\Command\RegisterUserHandler') + $this->app->when('Flarum\Core\AvatarUploader') ->needs('League\Flysystem\FilesystemInterface') ->give($avatarsFilesystem); } diff --git a/framework/core/src/Event/AvatarWillBeSaved.php b/framework/core/src/Event/AvatarWillBeSaved.php index f4e52bb65..0c52399cc 100644 --- a/framework/core/src/Event/AvatarWillBeSaved.php +++ b/framework/core/src/Event/AvatarWillBeSaved.php @@ -12,6 +12,7 @@ namespace Flarum\Event; use Flarum\Core\User; +use Intervention\Image\Image; class AvatarWillBeSaved { @@ -30,21 +31,21 @@ class AvatarWillBeSaved public $actor; /** - * The path to the avatar that will be saved. + * The image that will be saved. * - * @var string + * @var Image */ - public $path; + public $image; /** * @param User $user The user whose avatar will be saved. * @param User $actor The user performing the action. - * @param string $path The path to the avatar that will be saved. + * @param Image $image The image that will be saved. */ - public function __construct(User $user, User $actor, $path) + public function __construct(User $user, User $actor, Image $image) { $this->user = $user; $this->actor = $actor; - $this->path = $path; + $this->image = $image; } } From 9a8433d02c257f71edfbb361018112b4dd8cc711 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sat, 7 Oct 2017 17:51:30 +1030 Subject: [PATCH 18/72] No need to set a remember cookie if only logging in for session --- .../core/src/Forum/Controller/LogInController.php | 4 +++- framework/core/src/Http/Rememberer.php | 11 ++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/framework/core/src/Forum/Controller/LogInController.php b/framework/core/src/Forum/Controller/LogInController.php index f131150f9..8fc2fd879 100644 --- a/framework/core/src/Forum/Controller/LogInController.php +++ b/framework/core/src/Forum/Controller/LogInController.php @@ -81,7 +81,9 @@ class LogInController implements ControllerInterface event(new UserLoggedIn($this->users->findOrFail($data->userId), $token)); - $response = $this->rememberer->remember($response, $token, ! array_get($body, 'remember')); + if (array_get($body, 'remember')) { + $response = $this->rememberer->remember($response, $token); + } } return $response; diff --git a/framework/core/src/Http/Rememberer.php b/framework/core/src/Http/Rememberer.php index d331d796c..697cfe243 100644 --- a/framework/core/src/Http/Rememberer.php +++ b/framework/core/src/Http/Rememberer.php @@ -24,7 +24,6 @@ class Rememberer protected $cookie; /** - * Rememberer constructor. * @param CookieFactory $cookie */ public function __construct(CookieFactory $cookie) @@ -32,18 +31,16 @@ class Rememberer $this->cookie = $cookie; } - public function remember(ResponseInterface $response, AccessToken $token, $session = false) + public function remember(ResponseInterface $response, AccessToken $token) { $lifetime = null; - if (! $session) { - $token->lifetime = $lifetime = 5 * 365 * 24 * 60 * 60; // 5 years - $token->save(); - } + $token->lifetime = 5 * 365 * 24 * 60 * 60; // 5 years + $token->save(); return FigResponseCookies::set( $response, - $this->cookie->make(self::COOKIE_NAME, $token->id, $lifetime) + $this->cookie->make(self::COOKIE_NAME, $token->id, $token->lifetime) ); } From ba3afb2d6fffe5d8af42bee6cd440bc83a916088 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sat, 7 Oct 2017 19:17:59 +1030 Subject: [PATCH 19/72] Fix variable --- framework/core/src/Core/Command/RegisterUserHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/core/src/Core/Command/RegisterUserHandler.php b/framework/core/src/Core/Command/RegisterUserHandler.php index f57492548..809b5b38e 100644 --- a/framework/core/src/Core/Command/RegisterUserHandler.php +++ b/framework/core/src/Core/Command/RegisterUserHandler.php @@ -128,7 +128,7 @@ class RegisterUserHandler try { $image = (new ImageManager)->make($avatarUrl); - $this->avatarUploader->upload($user, $avatarUrl); + $this->avatarUploader->upload($user, $image); } catch (Exception $e) { // } From 6ddb8e78e56c64ad761557353fc42f0ae5ea565b Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Sat, 7 Oct 2017 11:20:38 +0200 Subject: [PATCH 20/72] Fix namespace imports --- framework/core/src/Core/AvatarUploader.php | 2 +- framework/core/src/Core/Command/DeleteAvatarHandler.php | 1 - framework/core/src/Core/Command/UploadAvatarHandler.php | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/framework/core/src/Core/AvatarUploader.php b/framework/core/src/Core/AvatarUploader.php index 3d7c018e7..394a966e6 100755 --- a/framework/core/src/Core/AvatarUploader.php +++ b/framework/core/src/Core/AvatarUploader.php @@ -11,9 +11,9 @@ namespace Flarum\Core; +use Illuminate\Support\Str; use Intervention\Image\Image; use League\Flysystem\FilesystemInterface; -use Illuminate\Support\Str; class AvatarUploader { diff --git a/framework/core/src/Core/Command/DeleteAvatarHandler.php b/framework/core/src/Core/Command/DeleteAvatarHandler.php index 2a7cb1638..2e735582d 100644 --- a/framework/core/src/Core/Command/DeleteAvatarHandler.php +++ b/framework/core/src/Core/Command/DeleteAvatarHandler.php @@ -18,7 +18,6 @@ use Flarum\Core\Repository\UserRepository; use Flarum\Core\Support\DispatchEventsTrait; use Flarum\Event\AvatarWillBeDeleted; use Illuminate\Contracts\Events\Dispatcher; -use League\Flysystem\FilesystemInterface; class DeleteAvatarHandler { diff --git a/framework/core/src/Core/Command/UploadAvatarHandler.php b/framework/core/src/Core/Command/UploadAvatarHandler.php index 02e655bd6..0750ff0b7 100644 --- a/framework/core/src/Core/Command/UploadAvatarHandler.php +++ b/framework/core/src/Core/Command/UploadAvatarHandler.php @@ -11,7 +11,6 @@ namespace Flarum\Core\Command; -use Exception; use Flarum\Core\Access\AssertPermissionTrait; use Flarum\Core\AvatarUploader; use Flarum\Core\Repository\UserRepository; From 34d7b14d396dd880ded76af23e49fea848cab527 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sat, 7 Oct 2017 20:10:50 +1030 Subject: [PATCH 21/72] Use display names in avatars --- framework/core/js/lib/helpers/avatar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/core/js/lib/helpers/avatar.js b/framework/core/js/lib/helpers/avatar.js index c9174c7fb..ef47173b7 100644 --- a/framework/core/js/lib/helpers/avatar.js +++ b/framework/core/js/lib/helpers/avatar.js @@ -19,7 +19,7 @@ export default function avatar(user, attrs = {}) { // uploaded image, or the first letter of their username if they haven't // uploaded one. if (user) { - const username = user.username() || '?'; + const username = user.displayName() || '?'; const avatarUrl = user.avatarUrl(); if (hasTitle) attrs.title = attrs.title || username; From 8108c2f1eba7fd3220910ec1f6b681bb8ab4242a Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sat, 7 Oct 2017 20:12:15 +1030 Subject: [PATCH 22/72] Recompile JS --- framework/core/js/admin/dist/app.js | 5 +++-- framework/core/js/forum/dist/app.js | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/framework/core/js/admin/dist/app.js b/framework/core/js/admin/dist/app.js index 16a08b91b..b8f9c3745 100644 --- a/framework/core/js/admin/dist/app.js +++ b/framework/core/js/admin/dist/app.js @@ -21364,7 +21364,7 @@ System.register('flarum/helpers/avatar', [], function (_export, _context) { // uploaded image, or the first letter of their username if they haven't // uploaded one. if (user) { - var username = user.username() || '?'; + var username = user.displayName() || '?'; var avatarUrl = user.avatarUrl(); if (hasTitle) attrs.title = attrs.title || username; @@ -21614,7 +21614,7 @@ System.register("flarum/helpers/username", [], function (_export, _context) { "use strict"; function username(user) { - var name = user && user.username() || app.translator.trans('core.lib.username.deleted_text'); + var name = user && user.displayName() || app.translator.trans('core.lib.username.deleted_text'); return m( "span", @@ -22445,6 +22445,7 @@ System.register('flarum/models/User', ['flarum/Model', 'flarum/utils/stringToCol babelHelpers.extends(User.prototype, { username: Model.attribute('username'), + displayName: Model.attribute('displayName'), email: Model.attribute('email'), isActivated: Model.attribute('isActivated'), password: Model.attribute('password'), diff --git a/framework/core/js/forum/dist/app.js b/framework/core/js/forum/dist/app.js index fffd65333..8a51d8577 100644 --- a/framework/core/js/forum/dist/app.js +++ b/framework/core/js/forum/dist/app.js @@ -28828,7 +28828,7 @@ System.register('flarum/components/UserPage', ['flarum/components/Page', 'flarum value: function show(user) { this.user = user; - app.setTitle(user.username()); + app.setTitle(user.displayName()); m.redraw(); } @@ -29286,7 +29286,7 @@ System.register('flarum/helpers/avatar', [], function (_export, _context) { // uploaded image, or the first letter of their username if they haven't // uploaded one. if (user) { - var username = user.username() || '?'; + var username = user.displayName() || '?'; var avatarUrl = user.avatarUrl(); if (hasTitle) attrs.title = attrs.title || username; From 51510319c1e4cf56c3d7c42d07479c73c3275168 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sat, 7 Oct 2017 20:28:15 +1030 Subject: [PATCH 23/72] Let avatarUrl attribute delete the avatar as well --- framework/core/src/Core/Command/EditUserHandler.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/core/src/Core/Command/EditUserHandler.php b/framework/core/src/Core/Command/EditUserHandler.php index d5d4bae1b..0b366dcec 100644 --- a/framework/core/src/Core/Command/EditUserHandler.php +++ b/framework/core/src/Core/Command/EditUserHandler.php @@ -168,6 +168,8 @@ class EditUserHandler } catch (Exception $e) { // } + } elseif (array_key_exists('avatarUrl', $attributes)) { + $this->avatarUploader->remove($user); } $this->events->fire( From 1f4966bbf10895cac7bac66582db4c2e4eb80e4e Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sun, 8 Oct 2017 08:59:18 +1030 Subject: [PATCH 24/72] Make sure components receive all children properly --- framework/core/js/lib/utils/patchMithril.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/core/js/lib/utils/patchMithril.js b/framework/core/js/lib/utils/patchMithril.js index 672b2e39e..8935b2e79 100644 --- a/framework/core/js/lib/utils/patchMithril.js +++ b/framework/core/js/lib/utils/patchMithril.js @@ -5,7 +5,7 @@ export default function patchMithril(global) { const m = function(comp, ...args) { if (comp.prototype && comp.prototype instanceof Component) { - return comp.component(...args); + return comp.component(args[0], args.slice(1)); } const node = mo.apply(this, arguments); From 96be7c449d40484e191b53804ee4f9832b991f29 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Sun, 8 Oct 2017 08:59:54 +1030 Subject: [PATCH 25/72] Fix extractText breaking in some cases --- framework/core/js/lib/utils/extractText.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/core/js/lib/utils/extractText.js b/framework/core/js/lib/utils/extractText.js index faa6bd048..13d55862e 100644 --- a/framework/core/js/lib/utils/extractText.js +++ b/framework/core/js/lib/utils/extractText.js @@ -7,7 +7,7 @@ export default function extractText(vdom) { if (vdom instanceof Array) { return vdom.map(element => extractText(element)).join(''); - } else if (typeof vdom === 'object') { + } else if (typeof vdom === 'object' && vdom !== null) { return extractText(vdom.children); } else { return vdom; From b7800d2272e1889315650fa7c83f9bb98390a774 Mon Sep 17 00:00:00 2001 From: Joseph Date: Sun, 22 Oct 2017 13:37:08 -0700 Subject: [PATCH 26/72] Add drag and drop avatar uploading --- .../js/forum/src/components/AvatarEditor.js | 100 ++++++++++++++---- framework/core/less/forum/AvatarEditor.less | 5 +- 2 files changed, 83 insertions(+), 22 deletions(-) diff --git a/framework/core/js/forum/src/components/AvatarEditor.js b/framework/core/js/forum/src/components/AvatarEditor.js index 8d7f6c525..869904b33 100644 --- a/framework/core/js/forum/src/components/AvatarEditor.js +++ b/framework/core/js/forum/src/components/AvatarEditor.js @@ -23,6 +23,13 @@ export default class AvatarEditor extends Component { * @type {Boolean} */ this.loading = false; + + /** + * Whether or not an image has been dragged over the dropzone. + * + * @type {Boolean} + */ + this.isDraggedOver = false; } static initProps(props) { @@ -35,12 +42,17 @@ export default class AvatarEditor extends Component { const user = this.props.user; return ( -
+
{avatar(user)} + onclick={this.quickUpload.bind(this)} + ondragover={this.enableDragover.bind(this)} + ondragenter={this.enableDragover.bind(this)} + ondragleave={this.disableDragover.bind(this)} + ondragend={this.disableDragover.bind(this)} + ondrop={this.dropUpload.bind(this)}> {this.loading ? LoadingIndicator.component() : (user.avatarUrl() ? icon('pencil') : icon('plus-circle'))}
    @@ -62,7 +74,7 @@ export default class AvatarEditor extends Component { Button.component({ icon: 'upload', children: app.translator.trans('core.forum.user.avatar_upload_button'), - onclick: this.upload.bind(this) + onclick: this.openPicker.bind(this) }) ); @@ -77,6 +89,40 @@ export default class AvatarEditor extends Component { return items; } + /** + * Enable dragover style + * + * @param {Event} e + */ + enableDragover(e) { + e.preventDefault(); + e.stopPropagation(); + this.isDraggedOver = true; + } + + /** + * Disable dragover style + * + * @param {Event} e + */ + disableDragover(e) { + e.preventDefault(); + e.stopPropagation(); + this.isDraggedOver = false; + } + + /** + * Upload avatar when file is dropped into dropzone. + * + * @param {Event} e + */ + dropUpload(e) { + e.preventDefault(); + e.stopPropagation(); + this.isDraggedOver = false; + this.upload(e.dataTransfer.files[0]); + } + /** * If the user doesn't have an avatar, there's no point in showing the * controls dropdown, because only one option would be viable: uploading. @@ -89,14 +135,14 @@ export default class AvatarEditor extends Component { if (!this.props.user.avatarUrl()) { e.preventDefault(); e.stopPropagation(); - this.upload(); + this.openPicker(); } } /** - * Prompt the user to upload a new avatar. + * Upload avatar using file picker */ - upload() { + openPicker() { if (this.loading) return; // Create a hidden HTML input element and click on it so the user can select @@ -105,24 +151,36 @@ export default class AvatarEditor extends Component { const $input = $(''); $input.appendTo('body').hide().click().on('change', e => { - const data = new FormData(); - data.append('avatar', $(e.target)[0].files[0]); - - this.loading = true; - m.redraw(); - - app.request({ - method: 'POST', - url: app.forum.attribute('apiUrl') + '/users/' + user.id() + '/avatar', - serialize: raw => raw, - data - }).then( - this.success.bind(this), - this.failure.bind(this) - ); + this.upload($(e.target)[0].files[0]); }); } + /** + * Upload avatar + * + * @param {File} file + */ + upload(file) { + if (this.loading) return; + + const user = this.props.user; + const data = new FormData(); + data.append('avatar', file); + + this.loading = true; + m.redraw(); + + app.request({ + method: 'POST', + url: app.forum.attribute('apiUrl') + '/users/' + user.id() + '/avatar', + serialize: raw => raw, + data + }).then( + this.success.bind(this), + this.failure.bind(this) + ); + } + /** * Remove the user's avatar. */ diff --git a/framework/core/less/forum/AvatarEditor.less b/framework/core/less/forum/AvatarEditor.less index 72fc55eda..7f6dc7a9b 100644 --- a/framework/core/less/forum/AvatarEditor.less +++ b/framework/core/less/forum/AvatarEditor.less @@ -17,7 +17,10 @@ .AvatarEditor--noAvatar { opacity: 0.7; } - &:hover .Dropdown-toggle, &.open .Dropdown-toggle, &.loading .Dropdown-toggle { + &:hover .Dropdown-toggle, + &.open .Dropdown-toggle, + &.loading .Dropdown-toggle, + &.dragover .Dropdown-toggle { opacity: 1; } .LoadingIndicator { From c9a689229733614381ab6375d5560f569d4f9164 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Wed, 25 Oct 2017 12:36:09 +1030 Subject: [PATCH 27/72] Add LESS variable to configure expansion of sideNav dropdowns --- framework/core/less/lib/sideNav.less | 71 ++++++++++++++------------ framework/core/less/lib/variables.less | 2 + 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/framework/core/less/lib/sideNav.less b/framework/core/less/lib/sideNav.less index ebc1caee2..13383a41b 100755 --- a/framework/core/less/lib/sideNav.less +++ b/framework/core/less/lib/sideNav.less @@ -9,7 +9,7 @@ // control; the new discussion button is the primary-control. On anything // larger than a phone, however, we need to affix the sidebar and expand the // .dropdown-select into a plain list. -@media @tablet-up { +@media @expand-side-nav { .sideNav { // Expand the dropdown-select component into a normal nav list. & .Dropdown--select { @@ -48,45 +48,50 @@ } } -.sideNav--horizontal { - padding: 15px 0; - white-space: nowrap; - overflow: auto; - -webkit-overflow-scrolling: touch; +.sideNav--horizontal {} - &:after { - content: " "; - position: absolute; - left: 0; - right: 0; - margin-top: 15px; - border-bottom: 1px solid @control-bg; - } +@media @expand-side-nav { + .sideNav--horizontal { + padding: 15px 0; + white-space: nowrap; + overflow: auto; + -webkit-overflow-scrolling: touch; - > ul > li, .Dropdown-menu > li { - display: inline-block; - margin: 0 20px 0 0; - vertical-align: top; - } - .Dropdown-separator { - display: none; - } + &:after { + content: " "; + position: absolute; + left: 0; + right: 0; + margin-top: 15px; + border-bottom: 1px solid @control-bg; + } - .Dropdown--select .Dropdown-menu > li > a { - padding-left: 25px; + > ul > li, .Dropdown-menu > li { + display: inline-block; + margin: 0 20px 0 0; + vertical-align: top; + } + .Dropdown-separator { + display: none; + } - .icon { - margin-left: -25px; + .Dropdown--select .Dropdown-menu > li > a { + padding-left: 25px; + + .icon { + margin-left: -25px; + } + } + + .affix { + position: static; } } - .affix { - position: static; - } -} -@media @tablet { - .sideNav { - .sideNav--horizontal(); + @media @tablet { + .sideNav { + .sideNav--horizontal(); + } } } diff --git a/framework/core/less/lib/variables.less b/framework/core/less/lib/variables.less index 501e52372..bc2ba5f32 100755 --- a/framework/core/less/lib/variables.less +++ b/framework/core/less/lib/variables.less @@ -110,6 +110,8 @@ @zindex-alerts: 1060; @zindex-tooltip: 1070; +@expand-side-nav: @tablet-up; + // --------------------------------- // BREAKPOINTS From cf9a7f141a4ddb688fc1e756a7156cd167c218cf Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Wed, 25 Oct 2017 13:40:57 +1030 Subject: [PATCH 28/72] Only apply custom CSS and header HTML on forum, not admin --- framework/core/src/Forum/WebApp.php | 4 ++++ framework/core/src/Http/WebApp/AbstractWebApp.php | 4 ---- framework/core/views/admin.blade.php | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/framework/core/src/Forum/WebApp.php b/framework/core/src/Forum/WebApp.php index 310532922..eecfbee44 100644 --- a/framework/core/src/Forum/WebApp.php +++ b/framework/core/src/Forum/WebApp.php @@ -51,6 +51,10 @@ class WebApp extends AbstractWebApp return $this->formatter->getJs(); }); + $view->getCss()->addString(function () { + return $this->settings->get('custom_less'); + }); + return $view; } diff --git a/framework/core/src/Http/WebApp/AbstractWebApp.php b/framework/core/src/Http/WebApp/AbstractWebApp.php index 0e2bc24aa..e20ec3cac 100644 --- a/framework/core/src/Http/WebApp/AbstractWebApp.php +++ b/framework/core/src/Http/WebApp/AbstractWebApp.php @@ -131,10 +131,6 @@ abstract class AbstractWebApp $css->addString($lessVariables); $localeCss->addString($lessVariables); - - $css->addString(function () { - return $this->settings->get('custom_less'); - }); } /** diff --git a/framework/core/views/admin.blade.php b/framework/core/views/admin.blade.php index 80a0856a8..47275da32 100644 --- a/framework/core/views/admin.blade.php +++ b/framework/core/views/admin.blade.php @@ -1,5 +1,3 @@ -{!! array_get($forum, 'attributes.headerHtml') !!} -
    From 244280ae057425b7a4db713c47fab4bdeb0001b7 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 30 Oct 2017 02:54:02 +1030 Subject: [PATCH 29/72] Fix blurry chrome image rendering for Avatar / Logo - closes #1259 (#1276) * Fix blurry chrome image rendering for Avatar / Logo - closes #1259 * Add comments for Chrome css fix --- framework/core/less/lib/App.less | 2 ++ framework/core/less/lib/Avatar.less | 2 ++ 2 files changed, 4 insertions(+) diff --git a/framework/core/less/lib/App.less b/framework/core/less/lib/App.less index c8af74833..0ee490ba5 100755 --- a/framework/core/less/lib/App.less +++ b/framework/core/less/lib/App.less @@ -171,6 +171,8 @@ .Header-logo { max-height: 30px; vertical-align: middle; + // Prevent blurriness in Chrome + image-rendering: -webkit-optimize-contrast; } // On phones, the header is displayed inside of the drawer. We lay its diff --git a/framework/core/less/lib/Avatar.less b/framework/core/less/lib/Avatar.less index 5fe5054c8..b65e014c8 100755 --- a/framework/core/less/lib/Avatar.less +++ b/framework/core/less/lib/Avatar.less @@ -14,6 +14,8 @@ height: 100%; border-radius: 100%; vertical-align: top; + // Prevent blurriness in Chrome + image-rendering: -webkit-optimize-contrast; } } From a18088f38522138e635dd40f645ad2c58d60bf56 Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 2 Nov 2017 00:51:31 +0100 Subject: [PATCH 30/72] The CookieFactory now also works if no configuration exists (#1258) * Returning the $default value if there's no config This is especially important for the CookieFactory which accesses the configuration before the application is installed * Injecting the configuration values into the CookieFactory --- framework/core/src/Foundation/Application.php | 2 +- framework/core/src/Http/CookieFactory.php | 52 ++++++++++++++----- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/framework/core/src/Foundation/Application.php b/framework/core/src/Foundation/Application.php index a432bdbb8..b3edee9fe 100644 --- a/framework/core/src/Foundation/Application.php +++ b/framework/core/src/Foundation/Application.php @@ -143,7 +143,7 @@ class Application extends Container implements ApplicationContract */ public function config($key, $default = null) { - return array_get($this->make('flarum.config'), $key, $default); + return $this->isInstalled() ? array_get($this->make('flarum.config'), $key, $default) : $default; } /** diff --git a/framework/core/src/Http/CookieFactory.php b/framework/core/src/Http/CookieFactory.php index aacaff7f5..0ddb3de4c 100644 --- a/framework/core/src/Http/CookieFactory.php +++ b/framework/core/src/Http/CookieFactory.php @@ -17,16 +17,46 @@ use Flarum\Foundation\Application; class CookieFactory { /** - * @var Application + * The prefix for the cookie names. + * + * @var string */ - protected $app; + protected $prefix; + + /** + * A path scope for the cookies. + * + * @var string + */ + protected $path; + + /** + * A domain scope for the cookies. + * + * @var string + */ + protected $domain; + + /** + * Whether the cookie(s) can be requested only over HTTPS. + * + * @var bool + */ + protected $secure; /** * @param Application $app */ public function __construct(Application $app) { - $this->app = $app; + // Parse the forum's base URL so that we can determine the optimal cookie settings + $url = parse_url(rtrim($app->url(), '/')); + + // Get the cookie settings from the config or use the default values + $this->prefix = $app->config('cookie.name', 'flarum'); + $this->path = $app->config('cookie.path', array_get($url, 'path') ?: '/'); + $this->domain = $app->config('cookie.domain'); + $this->secure = $app->config('cookie.secure', array_get($url, 'scheme') === 'https'); } /** @@ -42,9 +72,6 @@ class CookieFactory */ public function make($name, $value = null, $maxAge = null) { - // Parse the forum's base URL so that we can determine the optimal cookie settings - $url = parse_url(rtrim($this->app->url(), '/')); - $cookie = SetCookie::create($this->getName($name), $value); // Make sure we send both the MaxAge and Expires parameters (the former @@ -55,16 +82,13 @@ class CookieFactory ->withExpires(time() + $maxAge); } - if ($domain = $this->app->config('cookie.domain')) { - $cookie = $cookie->withDomain($domain); + if ($this->domain != null) { + $cookie = $cookie->withDomain($this->domain); } - $path = $this->app->config('cookie.path', array_get($url, 'path') ?: '/'); - $secure = $this->app->config('cookie.secure', array_get($url, 'scheme') === 'https'); - return $cookie - ->withPath($path) - ->withSecure($secure) + ->withPath($this->path) + ->withSecure($this->secure) ->withHttpOnly(true); } @@ -87,6 +111,6 @@ class CookieFactory */ public function getName($name) { - return $this->app->config('cookie.name', 'flarum').'_'.$name; + return $this->prefix.'_'.$name; } } From 6614fddbcd93f68bd668f7a5d8d356a775dd4ac3 Mon Sep 17 00:00:00 2001 From: Franz Liedke Date: Thu, 2 Nov 2017 01:12:49 +0100 Subject: [PATCH 31/72] Remove user bio feature (#1214) The feature is very limited in scope, and we hope for community extensions to take over this feature and make it much better. --- framework/core/js/admin/dist/app.js | 4 - framework/core/js/forum/dist/app.js | 148 +----------------- .../core/js/forum/src/components/UserBio.js | 104 ------------ .../core/js/forum/src/components/UserCard.js | 8 - framework/core/js/lib/models/User.js | 2 - framework/core/less/forum/UserCard.less | 31 ---- .../src/Api/Serializer/UserSerializer.php | 1 - .../core/src/Core/Command/EditUserHandler.php | 8 - framework/core/src/Core/User.php | 17 -- .../core/src/Event/UserBioWasChanged.php | 37 ----- 10 files changed, 2 insertions(+), 358 deletions(-) delete mode 100644 framework/core/js/forum/src/components/UserBio.js delete mode 100644 framework/core/src/Event/UserBioWasChanged.php diff --git a/framework/core/js/admin/dist/app.js b/framework/core/js/admin/dist/app.js index b8f9c3745..4a38b31f5 100644 --- a/framework/core/js/admin/dist/app.js +++ b/framework/core/js/admin/dist/app.js @@ -22451,10 +22451,6 @@ System.register('flarum/models/User', ['flarum/Model', 'flarum/utils/stringToCol password: Model.attribute('password'), avatarUrl: Model.attribute('avatarUrl'), - bio: Model.attribute('bio'), - bioHtml: computed('bio', function (bio) { - return bio ? '

    ' + $('

    ').text(bio).html().replace(/\n/g, '
    ').autoLink({ rel: 'nofollow' }) + '

    ' : ''; - }), preferences: Model.attribute('preferences'), groups: Model.hasMany('groups'), diff --git a/framework/core/js/forum/dist/app.js b/framework/core/js/forum/dist/app.js index 8a51d8577..efc6992d7 100644 --- a/framework/core/js/forum/dist/app.js +++ b/framework/core/js/forum/dist/app.js @@ -28478,143 +28478,10 @@ System.register('flarum/components/TextEditor', ['flarum/Component', 'flarum/uti });; 'use strict'; -System.register('flarum/components/UserBio', ['flarum/Component', 'flarum/components/LoadingIndicator', 'flarum/utils/classList', 'flarum/utils/extractText'], function (_export, _context) { +System.register('flarum/components/UserCard', ['flarum/Component', 'flarum/utils/humanTime', 'flarum/utils/ItemList', 'flarum/utils/UserControls', 'flarum/helpers/avatar', 'flarum/helpers/username', 'flarum/helpers/icon', 'flarum/components/Dropdown', 'flarum/components/AvatarEditor', 'flarum/helpers/listItems'], function (_export, _context) { "use strict"; - var Component, LoadingIndicator, classList, extractText, UserBio; - return { - setters: [function (_flarumComponent) { - Component = _flarumComponent.default; - }, function (_flarumComponentsLoadingIndicator) { - LoadingIndicator = _flarumComponentsLoadingIndicator.default; - }, function (_flarumUtilsClassList) { - classList = _flarumUtilsClassList.default; - }, function (_flarumUtilsExtractText) { - extractText = _flarumUtilsExtractText.default; - }], - execute: function () { - UserBio = function (_Component) { - babelHelpers.inherits(UserBio, _Component); - - function UserBio() { - babelHelpers.classCallCheck(this, UserBio); - return babelHelpers.possibleConstructorReturn(this, (UserBio.__proto__ || Object.getPrototypeOf(UserBio)).apply(this, arguments)); - } - - babelHelpers.createClass(UserBio, [{ - key: 'init', - value: function init() { - /** - * Whether or not the bio is currently being edited. - * - * @type {Boolean} - */ - this.editing = false; - - /** - * Whether or not the bio is currently being saved. - * - * @type {Boolean} - */ - this.loading = false; - } - }, { - key: 'view', - value: function view() { - var user = this.props.user; - var content = void 0; - - if (this.editing) { - content = m('textarea', { className: 'FormControl', placeholder: extractText(app.translator.trans('core.forum.user.bio_placeholder')), rows: '3', value: user.bio() }); - } else { - var subContent = void 0; - - if (this.loading) { - subContent = m( - 'p', - { className: 'UserBio-placeholder' }, - LoadingIndicator.component({ size: 'tiny' }) - ); - } else { - var bioHtml = user.bioHtml(); - - if (bioHtml) { - subContent = m.trust(bioHtml); - } else if (this.props.editable) { - subContent = m( - 'p', - { className: 'UserBio-placeholder' }, - app.translator.trans('core.forum.user.bio_placeholder') - ); - } - } - - content = m( - 'div', - { className: 'UserBio-content', onclick: this.edit.bind(this) }, - subContent - ); - } - - return m( - 'div', - { className: 'UserBio ' + classList({ - editable: this.props.editable, - editing: this.editing - }) }, - content - ); - } - }, { - key: 'edit', - value: function edit() { - if (!this.props.editable) return; - - this.editing = true; - m.redraw(); - - var bio = this; - var save = function save(e) { - if (e.shiftKey) return; - e.preventDefault(); - bio.save($(this).val()); - }; - - this.$('textarea').focus().bind('blur', save).bind('keydown', 'return', save); - } - }, { - key: 'save', - value: function save(value) { - var _this2 = this; - - var user = this.props.user; - - if (user.bio() !== value) { - this.loading = true; - - user.save({ bio: value }).catch(function () {}).then(function () { - _this2.loading = false; - m.redraw(); - }); - } - - this.editing = false; - m.redraw(); - } - }]); - return UserBio; - }(Component); - - _export('default', UserBio); - } - }; -});; -'use strict'; - -System.register('flarum/components/UserCard', ['flarum/Component', 'flarum/utils/humanTime', 'flarum/utils/ItemList', 'flarum/utils/UserControls', 'flarum/helpers/avatar', 'flarum/helpers/username', 'flarum/helpers/icon', 'flarum/components/Dropdown', 'flarum/components/UserBio', 'flarum/components/AvatarEditor', 'flarum/helpers/listItems'], function (_export, _context) { - "use strict"; - - var Component, humanTime, ItemList, UserControls, avatar, username, icon, Dropdown, UserBio, AvatarEditor, listItems, UserCard; + var Component, humanTime, ItemList, UserControls, avatar, username, icon, Dropdown, AvatarEditor, listItems, UserCard; return { setters: [function (_flarumComponent) { Component = _flarumComponent.default; @@ -28632,8 +28499,6 @@ System.register('flarum/components/UserCard', ['flarum/Component', 'flarum/utils icon = _flarumHelpersIcon.default; }, function (_flarumComponentsDropdown) { Dropdown = _flarumComponentsDropdown.default; - }, function (_flarumComponentsUserBio) { - UserBio = _flarumComponentsUserBio.default; }, function (_flarumComponentsAvatarEditor) { AvatarEditor = _flarumComponentsAvatarEditor.default; }, function (_flarumHelpersListItems) { @@ -28713,11 +28578,6 @@ System.register('flarum/components/UserCard', ['flarum/Component', 'flarum/utils var user = this.props.user; var lastSeenTime = user.lastSeenTime(); - items.add('bio', UserBio.component({ - user: user, - editable: this.props.editable - })); - if (lastSeenTime) { var online = user.isOnline(); @@ -30541,10 +30401,6 @@ System.register('flarum/models/User', ['flarum/Model', 'flarum/utils/stringToCol password: Model.attribute('password'), avatarUrl: Model.attribute('avatarUrl'), - bio: Model.attribute('bio'), - bioHtml: computed('bio', function (bio) { - return bio ? '

    ' + $('

    ').text(bio).html().replace(/\n/g, '
    ').autoLink({ rel: 'nofollow' }) + '

    ' : ''; - }), preferences: Model.attribute('preferences'), groups: Model.hasMany('groups'), diff --git a/framework/core/js/forum/src/components/UserBio.js b/framework/core/js/forum/src/components/UserBio.js deleted file mode 100644 index 41795a05f..000000000 --- a/framework/core/js/forum/src/components/UserBio.js +++ /dev/null @@ -1,104 +0,0 @@ -import Component from 'flarum/Component'; -import LoadingIndicator from 'flarum/components/LoadingIndicator'; -import classList from 'flarum/utils/classList'; -import extractText from 'flarum/utils/extractText'; - -/** - * The `UserBio` component displays a user's bio, optionally letting the user - * edit it. - */ -export default class UserBio extends Component { - init() { - /** - * Whether or not the bio is currently being edited. - * - * @type {Boolean} - */ - this.editing = false; - - /** - * Whether or not the bio is currently being saved. - * - * @type {Boolean} - */ - this.loading = false; - } - - view() { - const user = this.props.user; - let content; - - if (this.editing) { - content =