Skip to content

Commit

Permalink
Update to sub in "display names" from database when rendering
Browse files Browse the repository at this point in the history
This also improves the behaviour of mentions in the JS preview (no more
broken links, mention is only picked up if corresponding user/post is
found).

See flarum/framework#1246

Closes flarum/framework#315
  • Loading branch information
tobyzerner committed Sep 19, 2017
1 parent a0d33f8 commit 4aae02b
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 62 deletions.
69 changes: 61 additions & 8 deletions js/forum/dist/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,12 @@ System.register('flarum/mentions/addComposerAutocomplete', ['flarum/extend', 'fl

if (this.selectionEnd - cursor > 0) return;

// Search backwards from the cursor for an '@' symbol, without any
// intervening whitespace. If we find one, we will want to show the
// autocomplete dropdown!
// Search backwards from the cursor for an '@' symbol. If we find one,
// we will want to show the autocomplete dropdown!
var value = this.value;
mentionStart = 0;
for (var i = cursor - 1; i >= 0; i--) {
for (var i = cursor - 1; i >= cursor - 30; i--) {
var character = value.substr(i, 1);
if (/\s/.test(character)) break;
if (character === '@') {
mentionStart = i + 1;
break;
Expand Down Expand Up @@ -228,14 +226,22 @@ System.register('flarum/mentions/addComposerAutocomplete', ['flarum/extend', 'fl
);
};

var userMatches = function userMatches(user) {
var names = [user.username(), user.displayName()];

return names.some(function (value) {
return value.toLowerCase().substr(0, typed.length) === typed;
});
};

var buildSuggestions = function buildSuggestions() {
var suggestions = [];

// If the user has started to type a username, then suggest users
// matching that username.
if (typed) {
app.store.all('users').forEach(function (user) {
if (user.username().toLowerCase().substr(0, typed.length) !== typed) return;
if (!userMatches(user)) return;

suggestions.push(makeSuggestion(user, '@' + user.username(), '', 'MentionsDropdown-user'));
});
Expand All @@ -254,7 +260,7 @@ System.register('flarum/mentions/addComposerAutocomplete', ['flarum/extend', 'fl
return b.time() - a.time();
}).filter(function (post) {
var user = post.user();
return user && user.username().toLowerCase().substr(0, typed.length) === typed;
return user && userMatches(user);
}).splice(0, 5).forEach(function (post) {
var user = post.user();
suggestions.push(makeSuggestion(user, '@' + user.username() + '#' + post.id(), [app.translator.trans('flarum-mentions.forum.composer.reply_to_post_text', { number: post.number() }), ' — ', truncate(post.contentPlain(), 200)], 'MentionsDropdown-post'));
Expand All @@ -279,14 +285,18 @@ System.register('flarum/mentions/addComposerAutocomplete', ['flarum/extend', 'fl
left = parent.width() - width;
}
dropdown.show(left, top);
} else {
dropdown.active = false;
dropdown.hide();
}
};

dropdown.active = true;

buildSuggestions();

dropdown.setIndex(0);
dropdown.$().scrollTop(0);
dropdown.active = true;

clearTimeout(searchTimeout);
if (typed) {
Expand Down Expand Up @@ -1166,6 +1176,7 @@ System.register('flarum/mentions/main', ['flarum/extend', 'flarum/app', 'flarum/
}), 80);
});

// Remove post mentions when rendering post previews.
getPlainContent.removeSelectors.push('a.PostMention');
});
}
Expand Down Expand Up @@ -1258,4 +1269,46 @@ System.register('flarum/mentions/utils/selectedText', [], function (_export, _co
setters: [],
execute: function () {}
};
});;
'use strict';

System.register('flarum/mentions/utils/textFormatter', ['flarum/helpers/username', 'flarum/utils/extractText'], function (_export, _context) {
"use strict";

var username, extractText;
function filterUserMentions(tag) {
var user = app.store.getBy('users', 'username', tag.getAttribute('username'));

if (user) {
tag.setAttribute('id', user.id());
tag.setAttribute('displayname', extractText(username(user)));

return true;
}
}

_export('filterUserMentions', filterUserMentions);

function filterPostMentions(tag) {
var post = app.store.getById('posts', tag.getAttribute('id'));

if (post) {
tag.setAttribute('discussionid', post.discussion().id());
tag.setAttribute('number', post.number());
tag.setAttribute('displayname', extractText(username(post.user())));

return true;
}
}

_export('filterPostMentions', filterPostMentions);

return {
setters: [function (_flarumHelpersUsername) {
username = _flarumHelpersUsername.default;
}, function (_flarumUtilsExtractText) {
extractText = _flarumUtilsExtractText.default;
}],
execute: function () {}
};
});
21 changes: 14 additions & 7 deletions js/forum/src/addComposerAutocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,12 @@ export default function addComposerAutocomplete() {

if (this.selectionEnd - cursor > 0) return;

// Search backwards from the cursor for an '@' symbol, without any
// intervening whitespace. If we find one, we will want to show the
// autocomplete dropdown!
// Search backwards from the cursor for an '@' symbol. If we find one,
// we will want to show the autocomplete dropdown!
const value = this.value;
mentionStart = 0;
for (let i = cursor - 1; i >= 0; i--) {
for (let i = cursor - 1; i >= cursor - 30; i--) {
const character = value.substr(i, 1);
if (/\s/.test(character)) break;
if (character === '@') {
mentionStart = i + 1;
break;
Expand Down Expand Up @@ -95,14 +93,23 @@ export default function addComposerAutocomplete() {
);
};

const userMatches = function(user) {
const names = [
user.username(),
user.displayName()
];

return names.some(value => value.toLowerCase().substr(0, typed.length) === typed);
};

const buildSuggestions = () => {
const suggestions = [];

// If the user has started to type a username, then suggest users
// matching that username.
if (typed) {
app.store.all('users').forEach(user => {
if (user.username().toLowerCase().substr(0, typed.length) !== typed) return;
if (!userMatches(user)) return;

suggestions.push(
makeSuggestion(user, '@' + user.username(), '', 'MentionsDropdown-user')
Expand All @@ -122,7 +129,7 @@ export default function addComposerAutocomplete() {
.sort((a, b) => b.time() - a.time())
.filter(post => {
const user = post.user();
return user && user.username().toLowerCase().substr(0, typed.length) === typed;
return user && userMatches(user);
})
.splice(0, 5)
.forEach(post => {
Expand Down
1 change: 1 addition & 0 deletions js/forum/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,6 @@ app.initializers.add('flarum-mentions', function() {
);
});

// Remove post mentions when rendering post previews.
getPlainContent.removeSelectors.push('a.PostMention');
});
25 changes: 25 additions & 0 deletions js/forum/src/utils/textFormatter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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'));

if (user) {
tag.setAttribute('id', user.id());
tag.setAttribute('displayname', extractText(username(user)));

return true;
}
}

export function filterPostMentions(tag) {
const post = app.store.getById('posts', tag.getAttribute('id'));

if (post) {
tag.setAttribute('discussionid', post.discussion().id());
tag.setAttribute('number', post.number());
tag.setAttribute('displayname', extractText(username(post.user())));

return true;
}
}
42 changes: 26 additions & 16 deletions src/Listener/AddPostMentionedByRelationship.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,18 @@

namespace Flarum\Mentions\Listener;

use Flarum\Api\Controller\CreatePostController;
use Flarum\Api\Controller\ListPostsController;
use Flarum\Api\Controller\ShowDiscussionController;
use Flarum\Api\Controller\ShowPostController;
use Flarum\Api\Controller;
use Flarum\Api\Serializer\PostBasicSerializer;
use Flarum\Core\Post;
use Flarum\Core\Post\CommentPost;
use Flarum\Core\Repository\PostRepository;
use Flarum\Core\User;
use Flarum\Event\ConfigureApiController;
use Flarum\Event\GetApiRelationship;
use Flarum\Event\GetModelRelationship;
use Flarum\Event\PrepareApiData;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\Collection;

class AddPostMentionedByRelationship
{
Expand Down Expand Up @@ -94,24 +93,24 @@ public function getApiRelationship(GetApiRelationship $event)
*/
public function includeRelationships(ConfigureApiController $event)
{
if ($event->isController(ShowDiscussionController::class)) {
if ($event->isController(Controller\ShowDiscussionController::class)) {
$event->addInclude([
'posts.mentionedBy',
'posts.mentionedBy.user',
'posts.mentionedBy.discussion'
]);
}

if ($event->isController(ShowPostController::class)
|| $event->isController(ListPostsController::class)) {
if ($event->isController(Controller\ShowPostController::class)
|| $event->isController(Controller\ListPostsController::class)) {
$event->addInclude([
'mentionedBy',
'mentionedBy.user',
'mentionedBy.discussion'
]);
}

if ($event->isController(CreatePostController::class)) {
if ($event->isController(Controller\CreatePostController::class)) {
$event->addInclude([
'mentionsPosts',
'mentionsPosts.mentionedBy'
Expand All @@ -133,22 +132,33 @@ public function filterVisiblePosts(PrepareApiData $event)
{
// Firstly we gather a list of posts contained within the API document.
// This will vary according to the API endpoint that is being accessed.
if ($event->isController(ShowDiscussionController::class)) {
if ($event->isController(Controller\ShowDiscussionController::class)) {
$posts = $event->data->posts;
} elseif ($event->isController(ShowPostController::class)) {
} elseif ($event->isController(Controller\ShowPostController::class)
|| $event->isController(Controller\CreatePostController::class)
|| $event->isController(Controller\UpdatePostController::class)) {
$posts = [$event->data];
} elseif ($event->isController(ListPostsController::class)) {
} elseif ($event->isController(Controller\ListPostsController::class)) {
$posts = $event->data;
}

if (isset($posts)) {
$posts = array_filter((array) $posts, 'is_object');
$posts = new Collection($posts);

$posts = $posts->filter(function ($post) {
return $post instanceof CommentPost;
});

// Load all of the users that these posts mention. This way the data
// will be ready to go when we need to sub in current usernames
// during the rendering process.
$posts->load(['mentionsUsers', 'mentionsPosts.user']);

// Construct a list of the IDs of all of the posts that these posts
// have been mentioned in. We can then filter this list of IDs to
// weed out all of the ones which the user is not meant to see.
$ids = [];

// Once we have the posts, construct a list of the IDs of all of
// the posts that they have been mentioned in. We can then filter
// this list of IDs to weed out all of the ones which the user is
// not meant to see.
foreach ($posts as $post) {
$ids = array_merge($ids, $post->mentionedBy->pluck('id')->all());
}
Expand Down
21 changes: 16 additions & 5 deletions src/Listener/FormatPostMentions.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Flarum\Event\ConfigureFormatterRenderer;
use Flarum\Forum\UrlGenerator;
use Illuminate\Contracts\Events\Dispatcher;
use s9e\TextFormatter\Utils;

class FormatPostMentions
{
Expand Down Expand Up @@ -48,22 +49,23 @@ public function configure(ConfigureFormatter $event)
{
$configurator = $event->configurator;

$configurator->rendering->parameters['DISCUSSION_URL'] = $this->url->toRoute('discussion', ['id' => '']);

$tagName = 'POSTMENTION';

$tag = $configurator->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->attributes['number']->required = false;
$tag->attributes['discussionid']->required = false;

$tag->template = '<a href="{$DISCUSSION_URL}{@discussionid}/{@number}" class="PostMention" data-id="{@id}"><xsl:value-of select="@username"/></a>';
$tag->template = '<a href="{$DISCUSSION_URL}{@discussionid}/{@number}" class="PostMention" data-id="{@id}"><xsl:value-of select="@displayname"/></a>';

$tag->filterChain
->prepend([static::class, 'addId'])
->setJS('function() { return true; }');
->setJS('function(tag) { return System.get("flarum/mentions/utils/textFormatter").filterPostMentions(tag); }');

$configurator->Preg->match('/\B@(?<username>[a-z0-9_-]+)#(?<id>\d+)/i', $tagName);
}
Expand All @@ -73,7 +75,15 @@ public function configure(ConfigureFormatter $event)
*/
public function render(ConfigureFormatterRenderer $event)
{
$event->renderer->setParameter('DISCUSSION_URL', $this->url->toRoute('discussion', ['id' => '']));
$post = $event->context;

$event->xml = Utils::replaceAttributes($event->xml, 'POSTMENTION', function ($attributes) use ($post) {
$post = $post->mentionsPosts->find($attributes['id']);
if ($post && $post->user) {
$attributes['displayname'] = $post->user->display_name;
}
return $attributes;
});
}

/**
Expand All @@ -87,6 +97,7 @@ public static function addId($tag)
if ($post) {
$tag->setAttribute('discussionid', (int) $post->discussion_id);
$tag->setAttribute('number', (int) $post->number);
$tag->setAttribute('displayname', $post->user->display_name);

return true;
}
Expand Down
Loading

0 comments on commit 4aae02b

Please sign in to comment.