Bug report: a mention followed by a period is not recognised
Product: FluentCommunity 2.9.1 (Pro 2.9.1), WordPress 7.1, PHP 8.3
What happens
A mention at the end of a sentence is not resolved:
Thanks @ pcl,renders as a link to the profile. Correct.Thanks @ pcl.renders as plain text. No link, no notification.
Every other punctuation mark works. Only the period fails.
It is not limited to the one mention: getMentions() returns null for the
whole post as soon as no candidate resolves, so a second, correctly written
mention in the same post is dropped as well.
Why
app/Services/FeedsHelper.php:
public static function getMentions($text, $spaceId = null, $withUsers = false)
{
// the mention may have . or _ or - in the username
preg_match_all('/@([a-zA-Z0-9_.-]+)/', $text, $matches);
The period is part of the character class, so @pcl. yields the candidate
pcl., and XProfile::whereIn('username', ['pcl.']) finds nobody.
The comment above the pattern does not hold. Every path that assigns a user
name goes through CustomSanitizer::sanitizeUserName():
$username = sanitize_user($username);
$username = preg_replace('/[^a-zA-Z0-9_]/', '', $username);
Neither a period nor a hyphen survives that. Callers are
ProfileHelper (2x), ProfileController (2x) and PortalHandler, which even
compares the stored name against the sanitised one. Measured on a live site
with 236 profiles: 0 contain a period, 0 contain a hyphen.
The affected mention paths are FeedsController::store,
FeedsController::update, FeedsHelper::createFeed, CommentsController::store
and CommentsController::update. Beyond the missing link, the post also loses
meta.mentioned_user_ids, and with it the bell notification
(NotificationEventHandler::maybeHandleMentionedUserIds), the mention e-mail
(EmailNotificationHandler) and the Pro FollowHandler.
Suggested fix
Drop the period and the hyphen from the character class, matching what
sanitizeUserName() allows:
preg_match_all('/@([a-zA-Z0-9_]+)/', $text, $matches);
If older installations may carry imported names with a period, a fallback that
retries the candidate with trailing periods trimmed would cover both.
Related, same function
strtr() replaces substrings anywhere in the text, so a mention that resolves
also rewrites an e-mail address that happens to contain the same name:
$t = 'Thanks @ anna, write to hello@ anna.de please.';
strtr($t, ['@ anna' => '<a>Anna</a>']);
// Thanks <a>Anna</a>, write to hello<a>Anna</a>.de please.
A replacement anchored on a word boundary before the @ would avoid it.
Peter Claus Lamprecht We will have a look into it. Thanks for the details report!