diff --git a/extensions/mentions/extend.php b/extensions/mentions/extend.php
index 345e47907..5f52d4798 100644
--- a/extensions/mentions/extend.php
+++ b/extensions/mentions/extend.php
@@ -31,10 +31,15 @@ return [
->js(__DIR__.'/js/dist/forum.js')
->css(__DIR__.'/less/forum.less'),
+ (new Extend\Frontend('admin'))
+ ->js(__DIR__.'/js/dist/admin.js'),
+
(new Extend\Formatter)
->configure(ConfigureMentions::class)
->render(Formatter\FormatPostMentions::class)
- ->render(Formatter\FormatUserMentions::class),
+ ->render(Formatter\FormatUserMentions::class)
+ ->unparse(Formatter\UnparsePostMentions::class)
+ ->unparse(Formatter\UnparseUserMentions::class),
(new Extend\Model(Post::class))
->belongsToMany('mentionedBy', Post::class, 'post_mentions_post', 'mentions_post_id', 'post_id')
@@ -70,6 +75,9 @@ return [
(new Extend\ApiController(Controller\AbstractSerializeController::class))
->prepareDataForSerialization(FilterVisiblePosts::class),
+ (new Extend\Settings)
+ ->serializeToForum('allowUsernameMentionFormat', 'flarum-mentions.allow_username_format', 'boolval'),
+
(new Extend\Event())
->listen(Posted::class, Listener\UpdateMentionsMetadataWhenVisible::class)
->listen(Restored::class, Listener\UpdateMentionsMetadataWhenVisible::class)
diff --git a/extensions/mentions/js/admin.js b/extensions/mentions/js/admin.js
new file mode 100644
index 000000000..3e69ff3b9
--- /dev/null
+++ b/extensions/mentions/js/admin.js
@@ -0,0 +1 @@
+export * from './src/admin';
diff --git a/extensions/mentions/js/src/admin/index.js b/extensions/mentions/js/src/admin/index.js
new file mode 100644
index 000000000..069ae009d
--- /dev/null
+++ b/extensions/mentions/js/src/admin/index.js
@@ -0,0 +1,10 @@
+app.initializers.add('flarum-mentions', function() {
+ app.extensionData
+ .for('flarum-mentions')
+ .registerSetting({
+ setting: 'flarum-mentions.allow_username_format',
+ type: 'boolean',
+ label: app.translator.trans('flarum-mentions.admin.settings.allow_username_format_label'),
+ help: app.translator.trans('flarum-mentions.admin.settings.allow_username_format_text')
+ });
+});
diff --git a/extensions/mentions/js/src/forum/addComposerAutocomplete.js b/extensions/mentions/js/src/forum/addComposerAutocomplete.js
index 27f4b6db0..e96298e94 100644
--- a/extensions/mentions/js/src/forum/addComposerAutocomplete.js
+++ b/extensions/mentions/js/src/forum/addComposerAutocomplete.js
@@ -10,6 +10,7 @@ import KeyboardNavigatable from 'flarum/utils/KeyboardNavigatable';
import { truncate } from 'flarum/utils/string';
import AutocompleteDropdown from './fragments/AutocompleteDropdown';
+import cleanDisplayName from './utils/cleanDisplayName';
export default function addComposerAutocomplete() {
const $container = $('
');
@@ -35,6 +36,7 @@ export default function addComposerAutocomplete() {
let relMentionStart;
let absMentionStart;
let typed;
+ let matchTyped;
let searchTimeout;
// We store users returned from an API here to preserve order in which they are returned
@@ -74,6 +76,8 @@ export default function addComposerAutocomplete() {
if (absMentionStart) {
typed = lastChunk.substring(relMentionStart).toLowerCase();
+ matchTyped = typed.match(/^"((?:(?!"#).)+)$/);
+ typed = (matchTyped && matchTyped[1]) || typed;
const makeSuggestion = function(user, replacement, content, className = '') {
const username = usernameHelper(user);
@@ -116,7 +120,7 @@ export default function addComposerAutocomplete() {
if (!userMatches(user)) return;
suggestions.push(
- makeSuggestion(user, '@' + user.username(), '', 'MentionsDropdown-user')
+ makeSuggestion(user, `@"${cleanDisplayName(user)}"#${user.id()}`, '', 'MentionsDropdown-user')
);
});
}
@@ -142,7 +146,7 @@ export default function addComposerAutocomplete() {
.forEach(post => {
const user = post.user();
suggestions.push(
- makeSuggestion(user, '@' + user.username() + '#' + post.id(), [
+ makeSuggestion(user, `@"${cleanDisplayName(user)}"#p${post.id()}`, [
app.translator.trans('flarum-mentions.forum.composer.reply_to_post_text', {number: post.number()}), ' — ',
truncate(post.contentPlain(), 200)
], 'MentionsDropdown-post')
diff --git a/extensions/mentions/js/src/forum/addPostMentionPreviews.js b/extensions/mentions/js/src/forum/addPostMentionPreviews.js
index 76e4400da..5707caa86 100644
--- a/extensions/mentions/js/src/forum/addPostMentionPreviews.js
+++ b/extensions/mentions/js/src/forum/addPostMentionPreviews.js
@@ -14,12 +14,12 @@ export default function addPostMentionPreviews() {
const parentPost = this.attrs.post;
const $parentPost = this.$();
- this.$().on('click', '.UserMention, .PostMention', function (e) {
+ this.$().on('click', '.UserMention:not(.UserMention--deleted), .PostMention:not(.PostMention--deleted)', function (e) {
m.route.set(this.getAttribute('href'));
e.preventDefault();
});
- this.$('.PostMention').each(function() {
+ this.$('.PostMention:not(.PostMention--deleted)').each(function() {
const $this = $(this);
const id = $this.data('id');
let timeout;
diff --git a/extensions/mentions/js/src/forum/utils/cleanDisplayName.js b/extensions/mentions/js/src/forum/utils/cleanDisplayName.js
new file mode 100644
index 000000000..b32c2282c
--- /dev/null
+++ b/extensions/mentions/js/src/forum/utils/cleanDisplayName.js
@@ -0,0 +1,3 @@
+export default function cleanDisplayName(user) {
+ return user.displayName().replace(/"#[a-z]{0,3}[0-9]+/, '_');
+};
diff --git a/extensions/mentions/js/src/forum/utils/reply.js b/extensions/mentions/js/src/forum/utils/reply.js
index e9fc5d3a2..2355641b5 100644
--- a/extensions/mentions/js/src/forum/utils/reply.js
+++ b/extensions/mentions/js/src/forum/utils/reply.js
@@ -1,9 +1,10 @@
import DiscussionControls from 'flarum/utils/DiscussionControls';
import EditPostComposer from 'flarum/components/EditPostComposer';
+import cleanDisplayName from './cleanDisplayName';
function insertMention(post, composer, quote) {
const user = post.user();
- const mention = '@' + (user ? user.username() : post.number()) + '#' + post.id() + ' ';
+ const mention = `@"${(user && cleanDisplayName(user)) || app.translator.trans('core.lib.username.deleted_text')}"#p${post.id()}`;
// If the composer is empty, then assume we're starting a new reply.
// In which case we don't want the user to have to confirm if they
diff --git a/extensions/mentions/js/src/forum/utils/textFormatter.js b/extensions/mentions/js/src/forum/utils/textFormatter.js
index 747a490a9..f1aa9aef5 100644
--- a/extensions/mentions/js/src/forum/utils/textFormatter.js
+++ b/extensions/mentions/js/src/forum/utils/textFormatter.js
@@ -2,14 +2,22 @@ import username from 'flarum/helpers/username';
import extractText from 'flarum/utils/extractText';
export function filterUserMentions(tag) {
- const user = app.store.getBy('users', 'username', tag.getAttribute('username'));
+ let user;
+
+ if (app.forum.attribute('allowUsernameMentionFormat') && tag.hasAttribute('username'))
+ user = app.store.getBy('users', 'username', tag.getAttribute('username'));
+ else if (tag.hasAttribute('id'))
+ user = app.store.getById('users', tag.getAttribute('id'));
if (user) {
tag.setAttribute('id', user.id());
+ tag.setAttribute('slug', user.slug());
tag.setAttribute('displayname', extractText(username(user)));
return true;
}
+
+ tag.invalidate();
}
export function filterPostMentions(tag) {
diff --git a/extensions/mentions/less/forum.less b/extensions/mentions/less/forum.less
index ee1bcb7fc..3292dd4cf 100644
--- a/extensions/mentions/less/forum.less
+++ b/extensions/mentions/less/forum.less
@@ -4,6 +4,7 @@
border-radius: @border-radius;
padding: 2px 5px;
border: 0 !important;
+ font-weight: 600;
blockquote & {
background: @body-bg;
@@ -13,6 +14,12 @@
color: @link-color;
}
}
+.UserMention, .PostMention {
+ &--deleted {
+ opacity: 0.8;
+ filter: grayscale(1);
+ }
+}
.PostMention {
margin: 0 3px;
diff --git a/extensions/mentions/locale/en.yml b/extensions/mentions/locale/en.yml
index a46a07950..962baf633 100644
--- a/extensions/mentions/locale/en.yml
+++ b/extensions/mentions/locale/en.yml
@@ -4,6 +4,16 @@ flarum-mentions:
# UNIQUE KEYS - The following keys are used in only one location each.
##
+ # Translations in this namespace are used by the admin interface.
+ admin:
+
+ # These translations are used in the mentions Settings page.
+ settings:
+ allow_username_format_label: Allow username mention format (@Username)
+ allow_username_format_text: |
+ The current format for user mentions is @"Display Name"#ID.
+ This setting allows using the old format of @Username, however it will still be converted to the new format upon saving.
+
# Translations in this namespace are used by the forum user interface.
forum:
@@ -36,6 +46,10 @@ flarum-mentions:
user:
mentions_link: Mentions
+ # These translations are used in the post mentions
+ post_mention:
+ deleted_text: "[unknown]"
+
# Translations in this namespace are used in emails sent by the forum.
email:
diff --git a/extensions/mentions/migrations/2021_04_19_000000_set_default_settings.php b/extensions/mentions/migrations/2021_04_19_000000_set_default_settings.php
new file mode 100644
index 000000000..5d6cb90d2
--- /dev/null
+++ b/extensions/mentions/migrations/2021_04_19_000000_set_default_settings.php
@@ -0,0 +1,14 @@
+ 1,
+]);
diff --git a/extensions/mentions/src/ConfigureMentions.php b/extensions/mentions/src/ConfigureMentions.php
index f942a1a31..f1d50d46e 100644
--- a/extensions/mentions/src/ConfigureMentions.php
+++ b/extensions/mentions/src/ConfigureMentions.php
@@ -11,6 +11,7 @@ namespace Flarum\Mentions;
use Flarum\Http\UrlGenerator;
use Flarum\Post\CommentPost;
+use Flarum\Settings\SettingsRepositoryInterface;
use Flarum\User\User;
use s9e\TextFormatter\Configurator;
@@ -42,14 +43,22 @@ class ConfigureMentions
$tagName = 'USERMENTION';
$tag = $config->tags->add($tagName);
- $tag->attributes->add('username');
$tag->attributes->add('displayname');
$tag->attributes->add('id')->filterChain->append('#uint');
- $tag->template = '@';
+ $tag->template = '
+
+
+ @
+
+
+ @
+
+ ';
$tag->filterChain->prepend([static::class, 'addUserId'])
->setJS('function(tag) { return flarum.extensions["flarum-mentions"].filterUserMentions(tag); }');
+ $config->Preg->match('/\B@"(?((?!"#[a-z]{0,3}[0-9]+).)+)"#(?[0-9]+)\b/', $tagName);
$config->Preg->match('/\B@(?[a-z0-9_-]+)(?!#)/i', $tagName);
}
@@ -60,12 +69,22 @@ class ConfigureMentions
*/
public static function addUserId($tag)
{
- if ($user = User::where('username', $tag->getAttribute('username'))->first()) {
+ $allow_username_format = (bool) resolve(SettingsRepositoryInterface::class)->get('flarum-mentions.allow_username_format');
+
+ if ($tag->hasAttribute('username') && $allow_username_format) {
+ $user = User::where('username', $tag->getAttribute('username'))->first();
+ } elseif ($tag->hasAttribute('id')) {
+ $user = User::find($tag->getAttribute('id'));
+ }
+
+ if (isset($user)) {
$tag->setAttribute('id', $user->id);
$tag->setAttribute('displayname', $user->display_name);
return true;
}
+
+ $tag->invalidate();
}
private function configurePostMentions(Configurator $config)
@@ -76,19 +95,26 @@ class ConfigureMentions
$tag = $config->tags->add($tagName);
- $tag->attributes->add('username');
$tag->attributes->add('displayname');
$tag->attributes->add('number')->filterChain->append('#uint');
$tag->attributes->add('discussionid')->filterChain->append('#uint');
$tag->attributes->add('id')->filterChain->append('#uint');
- $tag->template = '';
+ $tag->template = '
+
+
+
+
+
+
+
+ ';
$tag->filterChain
->prepend([static::class, 'addPostId'])
->setJS('function(tag) { return flarum.extensions["flarum-mentions"].filterPostMentions(tag); }');
- $config->Preg->match('/\B@(?[a-z0-9_-]+)#(?\d+)/i', $tagName);
+ $config->Preg->match('/\B@"(?((?!"#[a-z]{0,3}[0-9]+).)+)"#p(?[0-9]+)\b/', $tagName);
}
/**
diff --git a/extensions/mentions/src/Formatter/FormatPostMentions.php b/extensions/mentions/src/Formatter/FormatPostMentions.php
index c1a002f09..289376a7e 100644
--- a/extensions/mentions/src/Formatter/FormatPostMentions.php
+++ b/extensions/mentions/src/Formatter/FormatPostMentions.php
@@ -12,9 +12,20 @@ namespace Flarum\Mentions\Formatter;
use Psr\Http\Message\ServerRequestInterface as Request;
use s9e\TextFormatter\Renderer;
use s9e\TextFormatter\Utils;
+use Symfony\Contracts\Translation\TranslatorInterface;
class FormatPostMentions
{
+ /**
+ * @var TranslatorInterface
+ */
+ private $translator;
+
+ public function __construct(TranslatorInterface $translator)
+ {
+ $this->translator = $translator;
+ }
+
/**
* Configure rendering for post mentions.
*
@@ -34,6 +45,17 @@ class FormatPostMentions
$attributes['displayname'] = $post->user->display_name;
}
+ $attributes['deleted'] = false;
+
+ if (! $post) {
+ $attributes['displayname'] = $this->translator->trans('flarum-mentions.forum.post_mention.deleted_text');
+ $attributes['deleted'] = true;
+ }
+
+ if ($post && ! $post->user) {
+ $attributes['displayname'] = $this->translator->trans('core.lib.username.deleted_text');
+ }
+
return $attributes;
});
}
diff --git a/extensions/mentions/src/Formatter/FormatUserMentions.php b/extensions/mentions/src/Formatter/FormatUserMentions.php
index d97a2b80b..b52324195 100644
--- a/extensions/mentions/src/Formatter/FormatUserMentions.php
+++ b/extensions/mentions/src/Formatter/FormatUserMentions.php
@@ -9,29 +9,54 @@
namespace Flarum\Mentions\Formatter;
-use Psr\Http\Message\ServerRequestInterface as Request;
+use Flarum\Http\SlugManager;
+use Flarum\User\User;
use s9e\TextFormatter\Renderer;
use s9e\TextFormatter\Utils;
+use Symfony\Contracts\Translation\TranslatorInterface;
class FormatUserMentions
{
+ /**
+ * @var SlugManager
+ */
+ private $slugManager;
+
+ /**
+ * @var TranslatorInterface
+ */
+ private $translator;
+
+ public function __construct(SlugManager $slugManager, TranslatorInterface $translator)
+ {
+ $this->slugManager = $slugManager;
+ $this->translator = $translator;
+ }
+
/**
* Configure rendering for user mentions.
*
* @param s9e\TextFormatter\Renderer $renderer
* @param mixed $context
* @param string|null $xml
- * @param Psr\Http\Message\ServerRequestInterface $request
+ * @return string $xml to be rendered
*/
- public function __invoke(Renderer $renderer, $context, $xml, Request $request = null)
+ public function __invoke(Renderer $renderer, $context, string $xml)
{
$post = $context;
return Utils::replaceAttributes($xml, 'USERMENTION', function ($attributes) use ($post) {
$user = $post->mentionsUsers->find($attributes['id']);
+
+ $attributes['deleted'] = false;
+
if ($user) {
- $attributes['username'] = $user->username;
+ $attributes['slug'] = $this->slugManager->forResource(User::class)->toSlug($user);
$attributes['displayname'] = $user->display_name;
+ } else {
+ $attributes['deleted'] = true;
+ $attributes['slug'] = '';
+ $attributes['displayname'] = $this->translator->trans('core.lib.username.deleted_text');
}
return $attributes;
diff --git a/extensions/mentions/src/Formatter/UnparsePostMentions.php b/extensions/mentions/src/Formatter/UnparsePostMentions.php
new file mode 100644
index 000000000..4f8d01614
--- /dev/null
+++ b/extensions/mentions/src/Formatter/UnparsePostMentions.php
@@ -0,0 +1,95 @@
+translator = $translator;
+ }
+
+ /**
+ * Configure rendering for user mentions.
+ *
+ * @param string $xml
+ * @param mixed $context
+ * @return string $xml to be unparsed
+ */
+ public function __invoke($context, string $xml)
+ {
+ $xml = $this->updatePostMentionTags($context, $xml);
+ $xml = $this->unparsePostMentionTags($xml);
+
+ return $xml;
+ }
+
+ /**
+ * Updates XML post mention tags before unparsing so that unparsing uses new display names.
+ *
+ * @param mixed $context
+ * @param string $xml : Parsed text.
+ * @return string $xml : Updated XML tags;
+ */
+ protected function updatePostMentionTags($context, string $xml): string
+ {
+ $post = $context;
+
+ return Utils::replaceAttributes($xml, 'POSTMENTION', function ($attributes) use ($post) {
+ $post = $post->mentionsPosts->find($attributes['id']);
+ if ($post && $post->user) {
+ $attributes['displayname'] = $post->user->display_name;
+ }
+
+ if (! $post) {
+ $attributes['displayname'] = $this->translator->trans('flarum-mentions.forum.post_mention.deleted_text');
+ }
+
+ if ($post && ! $post->user) {
+ $attributes['displayname'] = $this->translator->trans('core.lib.username.deleted_text');
+ }
+
+ if (strpos($attributes['displayname'], '"#') !== false) {
+ $attributes['displayname'] = preg_replace('/"#[a-z]{0,3}[0-9]+/', '_', $attributes['displayname']);
+ }
+
+ return $attributes;
+ });
+ }
+
+ /**
+ * Transforms post mention tags from XML to raw unparsed content with updated format and display name.
+ *
+ * @param string $xml : Parsed text.
+ * @return string : Unparsed text.
+ */
+ protected function unparsePostMentionTags(string $xml): string
+ {
+ $tagName = 'POSTMENTION';
+
+ if (strpos($xml, $tagName) === false) {
+ return $xml;
+ }
+
+ return preg_replace(
+ '/<'.preg_quote($tagName).'\b[^>]*(?=\bdisplayname="(.*)")[^>]*(?=\bid="([0-9]+)")[^>]*>@[^<]+<\/'.preg_quote($tagName).'>/U',
+ '@"$1"#p$2',
+ $xml
+ );
+ }
+}
diff --git a/extensions/mentions/src/Formatter/UnparseUserMentions.php b/extensions/mentions/src/Formatter/UnparseUserMentions.php
new file mode 100644
index 000000000..694a2d5ed
--- /dev/null
+++ b/extensions/mentions/src/Formatter/UnparseUserMentions.php
@@ -0,0 +1,91 @@
+translator = $translator;
+ }
+
+ /**
+ * Configure rendering for user mentions.
+ *
+ * @param string $xml
+ * @param mixed $context
+ * @return string $xml to be unparsed
+ */
+ public function __invoke($context, string $xml)
+ {
+ $xml = $this->updateUserMentionTags($context, $xml);
+ $xml = $this->unparseUserMentionTags($xml);
+
+ return $xml;
+ }
+
+ /**
+ * Updates XML user mention tags before unparsing so that unparsing uses new display names.
+ *
+ * @param mixed $context
+ * @param string $xml : Parsed text.
+ * @return string $xml : Updated XML tags;
+ */
+ protected function updateUserMentionTags($context, string $xml): string
+ {
+ $post = $context;
+
+ return Utils::replaceAttributes($xml, 'USERMENTION', function ($attributes) use ($post) {
+ $user = $post->mentionsUsers->find($attributes['id']);
+
+ if ($user) {
+ $attributes['displayname'] = $user->display_name;
+ } else {
+ $attributes['displayname'] = $this->translator->trans('core.lib.username.deleted_text');
+ }
+
+ if (strpos($attributes['displayname'], '"#') !== false) {
+ $attributes['displayname'] = preg_replace('/"#[a-z]{0,3}[0-9]+/', '_', $attributes['displayname']);
+ }
+
+ return $attributes;
+ });
+ }
+
+ /**
+ * Transforms user mention tags from XML to raw unparsed content with updated format and display name.
+ *
+ * @param string $xml : Parsed text.
+ * @return string : Unparsed text.
+ */
+ protected function unparseUserMentionTags(string $xml): string
+ {
+ $tagName = 'USERMENTION';
+
+ if (strpos($xml, $tagName) === false) {
+ return $xml;
+ }
+
+ return preg_replace(
+ '/<'.preg_quote($tagName).'\b[^>]*(?=\bdisplayname="(.*)")[^>]*(?=\bid="([0-9]+)")[^>]*>@[^<]+<\/'.preg_quote($tagName).'>/U',
+ '@"$1"#$2',
+ $xml
+ );
+ }
+}
diff --git a/extensions/mentions/tests/integration/api/PostMentionsTest.php b/extensions/mentions/tests/integration/api/PostMentionsTest.php
index 5d88dc8c2..c975a3072 100644
--- a/extensions/mentions/tests/integration/api/PostMentionsTest.php
+++ b/extensions/mentions/tests/integration/api/PostMentionsTest.php
@@ -36,6 +36,7 @@ class PostMentionsTest extends TestCase
'users' => [
['id' => 3, 'username' => 'potato', 'email' => 'potato@machine.local', 'is_email_confirmed' => 1],
['id' => 4, 'username' => 'toby', 'email' => 'toby@machine.local', 'is_email_confirmed' => 1],
+ ['id' => 5, 'username' => 'bad_user', 'email' => 'bad_user@machine.local', 'is_email_confirmed' => 1],
],
'discussions' => [
['id' => 2, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 3, 'first_post_id' => 4, 'comment_count' => 2],
@@ -43,10 +44,17 @@ class PostMentionsTest extends TestCase
'posts' => [
['id' => 4, 'number' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now(), 'user_id' => 3, 'type' => 'comment', 'content' => '@tobyuuu#5'],
['id' => 5, 'number' => 3, 'discussion_id' => 2, 'created_at' => Carbon::now(), 'user_id' => 4, 'type' => 'comment', 'content' => '@potato#4'],
+ ['id' => 6, 'number' => 4, 'discussion_id' => 2, 'created_at' => Carbon::now(), 'user_id' => 3, 'type' => 'comment', 'content' => '@"i_am_a_deleted_user"#p7'],
+ ['id' => 7, 'number' => 5, 'discussion_id' => 2, 'created_at' => Carbon::now(), 'user_id' => 2021, 'type' => 'comment', 'content' => '@"POTATO$"#2010'],
+ ['id' => 8, 'number' => 6, 'discussion_id' => 2, 'created_at' => Carbon::now(), 'user_id' => 4, 'type' => 'comment', 'content' => '@"i_am_a_deleted_user"#p2020'],
+ ['id' => 9, 'number' => 10, 'discussion_id' => 2, 'created_at' => Carbon::now(), 'user_id' => 5, 'type' => 'comment', 'content' => 'I am bad
'],
+ ['id' => 10, 'number' => 11, 'discussion_id' => 2, 'created_at' => Carbon::now(), 'user_id' => 4, 'type' => 'comment', 'content' => '@"Bad "#p6 User"#p9'],
],
'post_mentions_post' => [
['post_id' => 4, 'mentions_post_id' => 5],
- ['post_id' => 5, 'mentions_post_id' => 4]
+ ['post_id' => 5, 'mentions_post_id' => 4],
+ ['post_id' => 6, 'mentions_post_id' => 7],
+ ['post_id' => 10, 'mentions_post_id' => 9]
],
'settings' => [
['key' => 'display_name_driver', 'value' => 'custom_display_name_driver'],
@@ -73,7 +81,7 @@ class PostMentionsTest extends TestCase
/**
* @test
*/
- public function mentioning_a_valid_post_works()
+ public function mentioning_a_valid_post_with_old_format_doesnt_work()
{
$this->app();
$this->recalculateDisplayNameDriver();
@@ -98,8 +106,42 @@ class PostMentionsTest extends TestCase
$response = json_decode($response->getBody(), true);
+ $this->assertStringNotContainsString('POTATO$', $response['data']['attributes']['contentHtml']);
+ $this->assertEquals('@potato#4', $response['data']['attributes']['content']);
+ $this->assertStringNotContainsString('PostMention', $response['data']['attributes']['contentHtml']);
+ $this->assertNull(CommentPost::find($response['data']['id'])->mentionsPosts->find(4));
+ }
+
+ /**
+ * @test
+ */
+ public function mentioning_a_valid_post_with_new_format_works()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('POST', '/api/posts', [
+ 'authenticatedAs' => 1,
+ 'json' => [
+ 'data' => [
+ 'attributes' => [
+ 'content' => '@"POTATO$"#p4',
+ ],
+ 'relationships' => [
+ 'discussion' => ['data' => ['id' => 2]],
+ ],
+ ],
+ ],
+ ])
+ );
+
+ $this->assertEquals(201, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
$this->assertStringContainsString('POTATO$', $response['data']['attributes']['contentHtml']);
- $this->assertStringContainsString('@potato#4', $response['data']['attributes']['content']);
+ $this->assertEquals('@"POTATO$"#p4', $response['data']['attributes']['content']);
$this->assertStringContainsString('PostMention', $response['data']['attributes']['contentHtml']);
$this->assertNotNull(CommentPost::find($response['data']['id'])->mentionsPosts->find(4));
}
@@ -118,7 +160,7 @@ class PostMentionsTest extends TestCase
'json' => [
'data' => [
'attributes' => [
- 'content' => '@franzofflarum#215',
+ 'content' => '@"franzofflarum"#p215',
],
'relationships' => [
'discussion' => ['data' => ['id' => 2]],
@@ -133,7 +175,7 @@ class PostMentionsTest extends TestCase
$response = json_decode($response->getBody(), true);
$this->assertStringNotContainsString('FRANZOFFLARUM$', $response['data']['attributes']['contentHtml']);
- $this->assertStringContainsString('@franzofflarum#215', $response['data']['attributes']['content']);
+ $this->assertEquals('@"franzofflarum"#p215', $response['data']['attributes']['content']);
$this->assertStringNotContainsString('PostMention', $response['data']['attributes']['contentHtml']);
$this->assertCount(0, CommentPost::find($response['data']['id'])->mentionsPosts);
}
@@ -152,7 +194,7 @@ class PostMentionsTest extends TestCase
'json' => [
'data' => [
'attributes' => [
- 'content' => '@toby#5 @flarum @franzofflarum#220 @potato @potato#4',
+ 'content' => '@"TOBY$"#p5 @"flarum"#2015 @"franzofflarum"#220 @"POTATO$"#3 @"POTATO$"#p4',
],
'relationships' => [
'discussion' => ['data' => ['id' => 2]],
@@ -169,7 +211,7 @@ class PostMentionsTest extends TestCase
$this->assertStringContainsString('TOBY$', $response['data']['attributes']['contentHtml']);
$this->assertStringNotContainsString('FRANZOFFLARUM$', $response['data']['attributes']['contentHtml']);
$this->assertStringContainsString('POTATO$', $response['data']['attributes']['contentHtml']);
- $this->assertEquals('@toby#5 @flarum @franzofflarum#220 @potato @potato#4', $response['data']['attributes']['content']);
+ $this->assertEquals('@"TOBY$"#p5 @"flarum"#2015 @"franzofflarum"#220 @"POTATO$"#3 @"POTATO$"#p4', $response['data']['attributes']['content']);
$this->assertStringContainsString('PostMention', $response['data']['attributes']['contentHtml']);
$this->assertCount(2, CommentPost::find($response['data']['id'])->mentionsPosts);
}
@@ -196,12 +238,211 @@ class PostMentionsTest extends TestCase
$this->assertStringContainsString('PostMention', $response['data']['attributes']['contentHtml']);
$this->assertCount(1, CommentPost::find($response['data']['id'])->mentionsPosts);
}
+
+ /**
+ * @test
+ */
+ public function post_mentions_unparse_with_fresh_data()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('GET', '/api/posts/4', [
+ 'authenticatedAs' => 1,
+ ])
+ );
+
+ $this->assertEquals(200, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString('@"TOBY$"#p5', $response['data']['attributes']['content']);
+ $this->assertCount(1, CommentPost::find($response['data']['id'])->mentionsPosts);
+ }
+
+ /**
+ * @test
+ */
+ public function deleted_post_mentions_s_user_unparse_and_render_without_user_data()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+ $deleted_text = $this->app()->getContainer()->make('translator')->trans('core.lib.username.deleted_text');
+
+ $response = $this->send(
+ $this->request('GET', '/api/posts/6', [
+ 'authenticatedAs' => 1,
+ ])
+ );
+
+ $this->assertEquals(200, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString($deleted_text, $response['data']['attributes']['contentHtml']);
+ $this->assertStringContainsString('@"'.$deleted_text.'"#p7', $response['data']['attributes']['content']);
+ $this->assertStringContainsString('PostMention', $response['data']['attributes']['contentHtml']);
+ $this->assertStringNotContainsString('i_am_a_deleted_user', $response['data']['attributes']['contentHtml']);
+ $this->assertStringNotContainsString('i_am_a_deleted_user', $response['data']['attributes']['content']);
+ $this->assertCount(1, CommentPost::find($response['data']['id'])->mentionsPosts);
+ }
+
+ /**
+ * @test
+ */
+ public function deleted_post_mentions_unparse_and_render_without_user_data()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+ $deleted_text = $this->app()->getContainer()->make('translator')->trans('flarum-mentions.forum.post_mention.deleted_text');
+
+ $response = $this->send(
+ $this->request('GET', '/api/posts/7', [
+ 'authenticatedAs' => 1,
+ ])
+ );
+
+ $this->assertEquals(200, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString($deleted_text, $response['data']['attributes']['contentHtml']);
+ $this->assertStringContainsString('@"'.$deleted_text.'"#p2010', $response['data']['attributes']['content']);
+ $this->assertStringContainsString('PostMention', $response['data']['attributes']['contentHtml']);
+ $this->assertStringNotContainsString('POTATO$', $response['data']['attributes']['contentHtml']);
+ $this->assertStringNotContainsString('POTATO$', $response['data']['attributes']['content']);
+ $this->assertCount(0, CommentPost::find($response['data']['id'])->mentionsPosts);
+ }
+
+ /**
+ * @test
+ */
+ public function deleted_post_mentions_and_deleted_user_unparse_and_render_without_user_data()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+ $deleted_text = $this->app()->getContainer()->make('translator')->trans('flarum-mentions.forum.post_mention.deleted_text');
+
+ $response = $this->send(
+ $this->request('GET', '/api/posts/8', [
+ 'authenticatedAs' => 1,
+ ])
+ );
+
+ $this->assertEquals(200, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString($deleted_text, $response['data']['attributes']['contentHtml']);
+ $this->assertStringContainsString('@"'.$deleted_text.'"#p2020', $response['data']['attributes']['content']);
+ $this->assertStringContainsString('PostMention', $response['data']['attributes']['contentHtml']);
+ $this->assertStringNotContainsString('POTATO$', $response['data']['attributes']['contentHtml']);
+ $this->assertStringNotContainsString('POTATO$', $response['data']['attributes']['content']);
+ $this->assertCount(0, CommentPost::find($response['data']['id'])->mentionsPosts);
+ }
+
+ /**
+ * @test
+ */
+ public function post_mentions_with_unremoved_bad_string_from_display_names_doesnt_work()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('POST', '/api/posts', [
+ 'authenticatedAs' => 1,
+ 'json' => [
+ 'data' => [
+ 'attributes' => [
+ 'content' => '@"Bad "#p6 User"#p9',
+ ],
+ 'relationships' => [
+ 'discussion' => ['data' => ['id' => 2]],
+ ],
+ ],
+ ],
+ ])
+ );
+
+ $this->assertEquals(201, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString('POTATO$', $response['data']['attributes']['contentHtml']);
+ $this->assertEquals('@"POTATO$"#p6 User"#p9', $response['data']['attributes']['content']);
+ $this->assertStringContainsString('PostMention', $response['data']['attributes']['contentHtml']);
+ $this->assertNotNull(CommentPost::find($response['data']['id'])->mentionsPosts->find(6));
+ }
+
+ /**
+ * @test
+ */
+ public function post_mentions_unparsing_removes_bad_display_name_string()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('GET', '/api/posts/10', [
+ 'authenticatedAs' => 1,
+ ])
+ );
+
+ $this->assertEquals(200, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString('Bad "#p6 User', $response['data']['attributes']['contentHtml']);
+ $this->assertStringContainsString('@"Bad _ User"#p9', $response['data']['attributes']['content']);
+ $this->assertStringContainsString('PostMention', $response['data']['attributes']['contentHtml']);
+ $this->assertNotNull(CommentPost::find($response['data']['id'])->mentionsPosts->find(9));
+ }
+
+ /**
+ * @test
+ */
+ public function post_mentions_with_removed_bad_string_from_display_names_works()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('POST', '/api/posts', [
+ 'authenticatedAs' => 1,
+ 'json' => [
+ 'data' => [
+ 'attributes' => [
+ 'content' => '@"Bad _ User"#p9',
+ ],
+ 'relationships' => [
+ 'discussion' => ['data' => ['id' => 2]],
+ ],
+ ],
+ ],
+ ])
+ );
+
+ $this->assertEquals(201, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString('Bad "#p6 User', $response['data']['attributes']['contentHtml']);
+ $this->assertEquals('@"Bad _ User"#p9', $response['data']['attributes']['content']);
+ $this->assertStringContainsString('PostMention', $response['data']['attributes']['contentHtml']);
+ $this->assertNotNull(CommentPost::find($response['data']['id'])->mentionsPosts->find(9));
+ }
}
class CustomOtherDisplayNameDriver implements DriverInterface
{
public function displayName(User $user): string
{
+ if ($user->username === 'bad_user') {
+ return 'Bad "#p6 User';
+ }
+
return strtoupper($user->username).'$';
}
}
diff --git a/extensions/mentions/tests/integration/api/UserMentionsTest.php b/extensions/mentions/tests/integration/api/UserMentionsTest.php
index a23de7498..2bb1614a7 100644
--- a/extensions/mentions/tests/integration/api/UserMentionsTest.php
+++ b/extensions/mentions/tests/integration/api/UserMentionsTest.php
@@ -37,15 +37,19 @@ class UserMentionsTest extends TestCase
$this->normalUser(),
['id' => 3, 'username' => 'potato', 'email' => 'potato@machine.local', 'is_email_confirmed' => 1],
['id' => 4, 'username' => 'toby', 'email' => 'toby@machine.local', 'is_email_confirmed' => 1],
+ ['id' => 5, 'username' => 'bad_user', 'email' => 'bad_user@machine.local', 'is_email_confirmed' => 1],
],
'discussions' => [
['id' => 2, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 3, 'first_post_id' => 4, 'comment_count' => 2],
],
'posts' => [
['id' => 4, 'number' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now(), 'user_id' => 3, 'type' => 'comment', 'content' => '@tobyuuu'],
+ ['id' => 6, 'number' => 3, 'discussion_id' => 2, 'created_at' => Carbon::now(), 'user_id' => 4, 'type' => 'comment', 'content' => '@"i_am_a_deleted_user"#2021'],
+ ['id' => 10, 'number' => 11, 'discussion_id' => 2, 'created_at' => Carbon::now(), 'user_id' => 5, 'type' => 'comment', 'content' => '@"Bad "#p6 User"#5'],
],
'post_mentions_user' => [
- ['post_id' => 4, 'mentions_user_id' => 4]
+ ['post_id' => 4, 'mentions_user_id' => 4],
+ ['post_id' => 10, 'mentions_user_id' => 5]
],
'settings' => [
['key' => 'display_name_driver', 'value' => 'custom_display_name_driver'],
@@ -72,14 +76,60 @@ class UserMentionsTest extends TestCase
/**
* @test
*/
- public function mentioning_a_valid_user_works()
+ public function mentioning_a_valid_user_with_old_format_doesnt_work_if_off()
{
+ $this->prepareDatabase([
+ 'settings' => [
+ ['key' => 'flarum-mentions.allow_username_format', 'value' => '0']
+ ]
+ ]);
+
$this->app();
$this->recalculateDisplayNameDriver();
$response = $this->send(
$this->request('POST', '/api/posts', [
- 'authenticatedAs' => 2,
+ 'authenticatedAs' => 1,
+ 'json' => [
+ 'data' => [
+ 'attributes' => [
+ 'content' => '@potato',
+ ],
+ 'relationships' => [
+ 'discussion' => ['data' => ['id' => 2]],
+ ],
+ ],
+ ],
+ ])
+ );
+
+ $this->assertEquals(201, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringNotContainsString('@POTATO$', $response['data']['attributes']['contentHtml']);
+ $this->assertStringContainsString('@potato', $response['data']['attributes']['content']);
+ $this->assertStringNotContainsString('UserMention', $response['data']['attributes']['contentHtml']);
+ $this->assertCount(0, CommentPost::find($response['data']['id'])->mentionsUsers);
+ }
+
+ /**
+ * @test
+ */
+ public function mentioning_a_valid_user_with_old_format_works_if_on()
+ {
+ $this->prepareDatabase([
+ 'settings' => [
+ ['key' => 'flarum-mentions.allow_username_format', 'value' => '1']
+ ]
+ ]);
+
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('POST', '/api/posts', [
+ 'authenticatedAs' => 1,
'json' => [
'data' => [
'attributes' => [
@@ -98,7 +148,41 @@ class UserMentionsTest extends TestCase
$response = json_decode($response->getBody(), true);
$this->assertStringContainsString('@POTATO$', $response['data']['attributes']['contentHtml']);
- $this->assertStringContainsString('@potato', $response['data']['attributes']['content']);
+ $this->assertEquals('@"POTATO$"#3', $response['data']['attributes']['content']);
+ $this->assertStringContainsString('UserMention', $response['data']['attributes']['contentHtml']);
+ $this->assertNotNull(CommentPost::find($response['data']['id'])->mentionsUsers->find(3));
+ }
+
+ /**
+ * @test
+ */
+ public function mentioning_a_valid_user_with_new_format_works()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('POST', '/api/posts', [
+ 'authenticatedAs' => 1,
+ 'json' => [
+ 'data' => [
+ 'attributes' => [
+ 'content' => '@"POTATO$"#3',
+ ],
+ 'relationships' => [
+ 'discussion' => ['data' => ['id' => 2]],
+ ],
+ ],
+ ],
+ ])
+ );
+
+ $this->assertEquals(201, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString('@POTATO$', $response['data']['attributes']['contentHtml']);
+ $this->assertStringContainsString('@"POTATO$"#3', $response['data']['attributes']['content']);
$this->assertStringContainsString('UserMention', $response['data']['attributes']['contentHtml']);
$this->assertNotNull(CommentPost::find($response['data']['id'])->mentionsUsers->find(3));
}
@@ -113,11 +197,11 @@ class UserMentionsTest extends TestCase
$response = $this->send(
$this->request('POST', '/api/posts', [
- 'authenticatedAs' => 2,
+ 'authenticatedAs' => 1,
'json' => [
'data' => [
'attributes' => [
- 'content' => '@franzofflarum',
+ 'content' => '@"franzofflarum"#82',
],
'relationships' => [
'discussion' => ['data' => ['id' => 2]],
@@ -132,7 +216,7 @@ class UserMentionsTest extends TestCase
$response = json_decode($response->getBody(), true);
$this->assertStringNotContainsString('@FRANZOFFLARUM$', $response['data']['attributes']['contentHtml']);
- $this->assertStringContainsString('@franzofflarum', $response['data']['attributes']['content']);
+ $this->assertStringContainsString('@"franzofflarum"#82', $response['data']['attributes']['content']);
$this->assertStringNotContainsString('UserMention', $response['data']['attributes']['contentHtml']);
$this->assertCount(0, CommentPost::find($response['data']['id'])->mentionsUsers);
}
@@ -147,11 +231,11 @@ class UserMentionsTest extends TestCase
$response = $this->send(
$this->request('POST', '/api/posts', [
- 'authenticatedAs' => 2,
+ 'authenticatedAs' => 1,
'json' => [
'data' => [
'attributes' => [
- 'content' => '@toby @potato#4 @franzofflarum @potato',
+ 'content' => '@"TOBY$"#4 @"POTATO$"#p4 @"franzofflarum"#82 @"POTATO$"#3',
],
'relationships' => [
'discussion' => ['data' => ['id' => 2]],
@@ -168,7 +252,7 @@ class UserMentionsTest extends TestCase
$this->assertStringContainsString('@TOBY$', $response['data']['attributes']['contentHtml']);
$this->assertStringNotContainsString('@FRANZOFFLARUM$', $response['data']['attributes']['contentHtml']);
$this->assertStringContainsString('@POTATO$', $response['data']['attributes']['contentHtml']);
- $this->assertEquals('@toby @potato#4 @franzofflarum @potato', $response['data']['attributes']['content']);
+ $this->assertEquals('@"TOBY$"#4 @"POTATO$"#p4 @"franzofflarum"#82 @"POTATO$"#3', $response['data']['attributes']['content']);
$this->assertStringContainsString('UserMention', $response['data']['attributes']['contentHtml']);
$this->assertCount(2, CommentPost::find($response['data']['id'])->mentionsUsers);
}
@@ -176,7 +260,7 @@ class UserMentionsTest extends TestCase
/**
* @test
*/
- public function user_mentions_render_with_fresh_data()
+ public function old_user_mentions_still_render()
{
$this->app();
$this->recalculateDisplayNameDriver();
@@ -195,12 +279,201 @@ class UserMentionsTest extends TestCase
$this->assertStringContainsString('UserMention', $response['data']['attributes']['contentHtml']);
$this->assertCount(1, CommentPost::find($response['data']['id'])->mentionsUsers);
}
+
+ /**
+ * @test
+ */
+ public function user_mentions_render_with_fresh_data()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('POST', '/api/posts', [
+ 'authenticatedAs' => 1,
+ 'json' => [
+ 'data' => [
+ 'attributes' => [
+ 'content' => '@"potato_"#3',
+ ],
+ 'relationships' => [
+ 'discussion' => ['data' => ['id' => 2]],
+ ],
+ ],
+ ],
+ ])
+ );
+
+ $this->assertEquals(201, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString('@POTATO$', $response['data']['attributes']['contentHtml']);
+ $this->assertStringContainsString('UserMention', $response['data']['attributes']['contentHtml']);
+ $this->assertNotNull(CommentPost::find($response['data']['id'])->mentionsUsers->find(3));
+ }
+
+ /**
+ * @test
+ */
+ public function user_mentions_unparse_with_fresh_data()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('POST', '/api/posts', [
+ 'authenticatedAs' => 1,
+ 'json' => [
+ 'data' => [
+ 'attributes' => [
+ 'content' => '@"potato_"#3',
+ ],
+ 'relationships' => [
+ 'discussion' => ['data' => ['id' => 2]],
+ ],
+ ],
+ ],
+ ])
+ );
+
+ $this->assertEquals(201, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString('@"POTATO$"#3', $response['data']['attributes']['content']);
+ $this->assertNotNull(CommentPost::find($response['data']['id'])->mentionsUsers->find(3));
+ }
+
+ /**
+ * @test
+ */
+ public function deleted_user_mentions_unparse_and_render_without_user_data()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+ $deleted_text = $this->app()->getContainer()->make('translator')->trans('core.lib.username.deleted_text');
+
+ $response = $this->send(
+ $this->request('GET', '/api/posts/6', [
+ 'authenticatedAs' => 1,
+ ])
+ );
+
+ $this->assertEquals(200, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString("@$deleted_text", $response['data']['attributes']['contentHtml']);
+ $this->assertStringContainsString('@"'.$deleted_text.'"#2021', $response['data']['attributes']['content']);
+ $this->assertStringContainsString('UserMention', $response['data']['attributes']['contentHtml']);
+ $this->assertStringContainsString('UserMention--deleted', $response['data']['attributes']['contentHtml']);
+ $this->assertStringNotContainsString('i_am_a_deleted_user', $response['data']['attributes']['contentHtml']);
+ $this->assertStringNotContainsString('i_am_a_deleted_user', $response['data']['attributes']['content']);
+ $this->assertCount(0, CommentPost::find($response['data']['id'])->mentionsUsers);
+ }
+
+ /**
+ * @test
+ */
+ public function user_mentions_with_unremoved_bad_string_from_display_names_doesnt_work()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('POST', '/api/posts', [
+ 'authenticatedAs' => 1,
+ 'json' => [
+ 'data' => [
+ 'attributes' => [
+ 'content' => '@"Bad "#p6 User"#5',
+ ],
+ 'relationships' => [
+ 'discussion' => ['data' => ['id' => 2]],
+ ],
+ ],
+ ],
+ ])
+ );
+
+ $this->assertEquals(201, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringNotContainsString('Bad "#p6 User', $response['data']['attributes']['contentHtml']);
+ $this->assertNotEquals('@"Bad "#p6 User"#5', $response['data']['attributes']['content']);
+ $this->assertStringNotContainsString('UserMention', $response['data']['attributes']['contentHtml']);
+ $this->assertNull(CommentPost::find($response['data']['id'])->mentionsUsers->find(5));
+ }
+
+ /**
+ * @test
+ */
+ public function user_mentions_unparsing_removes_bad_display_name_string()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('GET', '/api/posts/10', [
+ 'authenticatedAs' => 1,
+ ])
+ );
+
+ $this->assertEquals(200, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString('Bad "#p6 User', $response['data']['attributes']['contentHtml']);
+ $this->assertStringContainsString('@"Bad _ User"#5', $response['data']['attributes']['content']);
+ $this->assertStringContainsString('UserMention', $response['data']['attributes']['contentHtml']);
+ $this->assertNotNull(CommentPost::find($response['data']['id'])->mentionsUsers->find(5));
+ }
+
+ /**
+ * @test
+ */
+ public function user_mentions_with_removed_bad_string_from_display_names_works()
+ {
+ $this->app();
+ $this->recalculateDisplayNameDriver();
+
+ $response = $this->send(
+ $this->request('POST', '/api/posts', [
+ 'authenticatedAs' => 1,
+ 'json' => [
+ 'data' => [
+ 'attributes' => [
+ 'content' => '@"Bad _ User"#5',
+ ],
+ 'relationships' => [
+ 'discussion' => ['data' => ['id' => 2]],
+ ],
+ ],
+ ],
+ ])
+ );
+
+ $this->assertEquals(201, $response->getStatusCode());
+
+ $response = json_decode($response->getBody(), true);
+
+ $this->assertStringContainsString('Bad "#p6 User', $response['data']['attributes']['contentHtml']);
+ $this->assertEquals('@"Bad _ User"#5', $response['data']['attributes']['content']);
+ $this->assertStringContainsString('UserMention', $response['data']['attributes']['contentHtml']);
+ $this->assertNotNull(CommentPost::find($response['data']['id'])->mentionsUsers->find(5));
+ }
}
class CustomDisplayNameDriver implements DriverInterface
{
public function displayName(User $user): string
{
+ if ($user->username === 'bad_user') {
+ return 'Bad "#p6 User';
+ }
+
return strtoupper($user->username).'$';
}
}