1
0
mirror of https://github.com/flarum/core.git synced 2025-08-05 16:07:34 +02:00

Merge branch 'master' into next-back

# Conflicts:
#	src/Listener/AddPostMentionedByRelationship.php
#	src/Listener/FormatPostMentions.php
#	src/Listener/FormatUserMentions.php
#	src/Listener/UpdatePostMentionsMetadata.php
#	src/Listener/UpdateUserMentionsMetadata.php
This commit is contained in:
Toby Zerner
2017-12-26 20:20:31 +10:30
13 changed files with 204 additions and 80 deletions

View File

@@ -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;
@@ -228,6 +226,14 @@ 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 = [];
@@ -235,7 +241,7 @@ System.register('flarum/mentions/addComposerAutocomplete', ['flarum/extend', 'fl
// 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'));
});
@@ -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'));
@@ -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) {
@@ -1000,19 +1010,27 @@ System.register('flarum/mentions/components/PostQuoteButton', ['flarum/component
$(document).on('mousedown', this.hide.bind(this));
}
}, {
key: 'show',
value: function show(left, top) {
var $this = this.$().show();
var parentOffset = $this.offsetParent().offset();
$this.css('left', left - parentOffset.left).css('top', top - parentOffset.top);
}
}, {
key: 'showStart',
value: function showStart(left, top) {
var $this = this.$();
$this.show().css('left', left).css('top', $(window).scrollTop() + top - $this.outerHeight() - 5);
this.show(left, $(window).scrollTop() + top - $this.outerHeight() - 5);
}
}, {
key: 'showEnd',
value: function showEnd(right, bottom) {
var $this = this.$();
$this.show().css('left', right - $this.outerWidth()).css('top', $(window).scrollTop() + bottom + 5);
this.show(right - $this.outerWidth(), $(window).scrollTop() + bottom + 5);
}
}, {
key: 'hide',
@@ -1166,6 +1184,7 @@ System.register('flarum/mentions/main', ['flarum/extend', 'flarum/app', 'flarum/
}), 80);
});
// Remove post mentions when rendering post previews.
getPlainContent.removeSelectors.push('a.PostMention');
});
}
@@ -1258,4 +1277,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 () {}
};
});

View File

@@ -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;
@@ -95,6 +93,15 @@ 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 = [];
@@ -102,7 +109,7 @@ export default function addComposerAutocomplete() {
// 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')
@@ -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 => {
@@ -154,14 +161,18 @@ export default function addComposerAutocomplete() {
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) {

View File

@@ -26,20 +26,25 @@ export default class PostQuoteButton extends Button {
$(document).on('mousedown', this.hide.bind(this));
}
show(left, top) {
const $this = this.$().show();
const parentOffset = $this.offsetParent().offset();
$this
.css('left', left - parentOffset.left)
.css('top', top - parentOffset.top);
}
showStart(left, top) {
const $this = this.$();
$this.show()
.css('left', left)
.css('top', $(window).scrollTop() + top - $this.outerHeight() - 5);
this.show(left, $(window).scrollTop() + top - $this.outerHeight() - 5);
}
showEnd(right, bottom) {
const $this = this.$();
$this.show()
.css('left', right - $this.outerWidth())
.css('top', $(window).scrollTop() + bottom + 5)
this.show(right - $this.outerWidth(), $(window).scrollTop() + bottom + 5);
}
hide() {

View File

@@ -67,5 +67,6 @@ app.initializers.add('flarum-mentions', function() {
);
});
// Remove post mentions when rendering post previews.
getPlainContent.removeSelectors.push('a.PostMention');
});

View File

@@ -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;
}
}

View File

@@ -31,7 +31,7 @@
}
.MentionsDropdown {
max-width: 500px;
max-height: 200px;
max-height: 150px;
overflow: auto;
position: absolute;

View File

@@ -24,6 +24,7 @@ use Flarum\Post\Post;
use Flarum\Post\PostRepository;
use Flarum\User\User;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Database\Eloquent\Collection;
class AddPostMentionedByRelationship
{
@@ -94,7 +95,7 @@ class AddPostMentionedByRelationship
*/
public function includeRelationships(WillGetData $event)
{
if ($event->isController(ShowDiscussionController::class)) {
if ($event->isController(Controller\ShowDiscussionController::class)) {
$event->addInclude([
'posts.mentionedBy',
'posts.mentionedBy.user',
@@ -102,8 +103,8 @@ class AddPostMentionedByRelationship
]);
}
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',
@@ -111,7 +112,7 @@ class AddPostMentionedByRelationship
]);
}
if ($event->isController(CreatePostController::class)) {
if ($event->isController(Controller\CreatePostController::class)) {
$event->addInclude([
'mentionsPosts',
'mentionsPosts.mentionedBy'
@@ -133,22 +134,33 @@ class AddPostMentionedByRelationship
{
// 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());
}

View File

@@ -16,6 +16,7 @@ use Flarum\Formatter\Event\Rendering;
use Flarum\Http\UrlGenerator;
use Flarum\Post\CommentPost;
use Illuminate\Contracts\Events\Dispatcher;
use s9e\TextFormatter\Utils;
class FormatPostMentions
{
@@ -48,22 +49,23 @@ class FormatPostMentions
{
$configurator = $event->configurator;
$configurator->rendering->parameters['DISCUSSION_URL'] = $this->url->to('forum')->route('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);
}
@@ -73,7 +75,16 @@ class FormatPostMentions
*/
public function render(Rendering $event)
{
$event->renderer->setParameter('DISCUSSION_URL', $this->url->to('forum')->route('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;
});
}
/**
@@ -87,6 +98,7 @@ class FormatPostMentions
if ($post) {
$tag->setAttribute('discussionid', (int) $post->discussion_id);
$tag->setAttribute('number', (int) $post->number);
$tag->setAttribute('displayname', $post->user->display_name);
return true;
}

View File

@@ -15,28 +15,22 @@ use Flarum\Formatter\Event\Configuring;
use Flarum\Formatter\Event\Parsing;
use Flarum\Formatter\Event\Rendering;
use Flarum\Http\UrlGenerator;
use Flarum\User\UserRepository;
use Flarum\User\User;
use Illuminate\Contracts\Events\Dispatcher;
use s9e\TextFormatter\Utils;
class FormatUserMentions
{
/**
* @var UserRepository
*/
protected $users;
/**
* @var UrlGenerator
*/
protected $url;
/**
* @param UserRepository $users
* @param UrlGenerator $url
*/
public function __construct(UserRepository $users, UrlGenerator $url)
public function __construct(UrlGenerator $url)
{
$this->users = $users;
$this->url = $url;
}
@@ -46,7 +40,6 @@ class FormatUserMentions
public function subscribe(Dispatcher $events)
{
$events->listen(Configuring::class, [$this, 'configure']);
$events->listen(Parsing::class, [$this, 'parse']);
$events->listen(Rendering::class, [$this, 'render']);
}
@@ -57,46 +50,50 @@ class FormatUserMentions
{
$configurator = $event->configurator;
$configurator->rendering->parameters['PROFILE_URL'] = $this->url->to('forum')->route('user', ['username' => '']);
$tagName = 'USERMENTION';
$tag = $configurator->tags->add($tagName);
$tag->attributes->add('username');
$tag->attributes->add('displayname');
$tag->attributes->add('id')->filterChain->append('#uint');
$tag->attributes['id']->required = false;
$tag->template = '<a href="{$PROFILE_URL}{@username}" class="UserMention">@<xsl:value-of select="@username"/></a>';
$tag->template = '<a href="{$PROFILE_URL}{@username}" class="UserMention">@<xsl:value-of select="@displayname"/></a>';
$tag->filterChain->prepend([static::class, 'addId'])
->addParameterByName('userRepository')
->setJS('function() { return true; }');
->setJS('function(tag) { return System.get("flarum/mentions/utils/textFormatter").filterUserMentions(tag); }');
$configurator->Preg->match('/\B@(?<username>[a-z0-9_-]+)(?!#)/i', $tagName);
}
/**
* @param Parsing $event
*/
public function parse(Parsing $event)
{
$event->parser->registeredVars['userRepository'] = $this->users;
}
/**
* @param Rendering $event
*/
public function render(Rendering $event)
{
$event->renderer->setParameter('PROFILE_URL', $this->url->to('forum')->route('user', ['username' => '']));
$post = $event->context;
$event->xml = Utils::replaceAttributes($event->xml, 'USERMENTION', function ($attributes) use ($post) {
$user = $post->mentionsUsers->find($attributes['id']);
if ($user) {
$attributes['displayname'] = $user->display_name;
}
return $attributes;
});
}
/**
* @param $tag
* @param UserRepository $users
*
* @return bool
*/
public static function addId($tag, UserRepository $users)
public static function addId($tag)
{
if ($id = $users->getIdForUsername($tag->getAttribute('username'))) {
$tag->setAttribute('id', $id);
if ($user = User::where('username', 'like', $tag->getAttribute('username'))->first()) {
$tag->setAttribute('id', $user->id);
$tag->setAttribute('displayname', $user->display_name);
return true;
}

View File

@@ -11,7 +11,7 @@
namespace Flarum\Mentions\Listener;
use Flarum\Api\Serializer\BasicPostSerializer;
use Flarum\Api\Serializer\PostSerializer;
use Flarum\Event\ConfigureNotificationTypes;
use Flarum\Mentions\Notification\PostMentionedBlueprint;
use Flarum\Notification\NotificationSyncer;
@@ -57,7 +57,7 @@ class UpdatePostMentionsMetadata
*/
public function addNotificationType(ConfigureNotificationTypes $event)
{
$event->add(PostMentionedBlueprint::class, BasicPostSerializer::class, ['alert']);
$event->add(PostMentionedBlueprint::class, PostSerializer::class, ['alert']);
}
/**

View File

@@ -11,7 +11,7 @@
namespace Flarum\Mentions\Listener;
use Flarum\Api\Serializer\BasicPostSerializer;
use Flarum\Api\Serializer\PostSerializer;
use Flarum\Event\ConfigureNotificationTypes;
use Flarum\Mentions\Notification\UserMentionedBlueprint;
use Flarum\Notification\NotificationSyncer;
@@ -58,7 +58,7 @@ class UpdateUserMentionsMetadata
*/
public function addNotificationType(ConfigureNotificationTypes $event)
{
$event->add(UserMentionedBlueprint::class, BasicPostSerializer::class, ['alert']);
$event->add(UserMentionedBlueprint::class, PostSerializer::class, ['alert']);
}
/**

View File

@@ -1,9 +1,9 @@
Hey {{ $user->username }}!
Hey {!! $user->username !!}!
{{ $blueprint->reply->user->username }} replied to your post (#{{ $blueprint->post->number }}) in {{ $blueprint->post->discussion->title }}.
{!! $blueprint->reply->user->username !!} replied to your post (#{!! $blueprint->post->number !!}) in {!! $blueprint->post->discussion->title !!}.
{{ app()->url() }}/d/{{ $blueprint->reply->discussion_id }}/{{ $blueprint->reply->number }}
{!! app()->url() !!}/d/{!! $blueprint->reply->discussion_id !!}/{!! $blueprint->reply->number !!}
---
{{ strip_tags($blueprint->reply->contentHtml) }}
{!! strip_tags($blueprint->reply->contentHtml) !!}

View File

@@ -1,9 +1,9 @@
Hey {{ $user->username }}!
Hey {!! $user->username !!}!
{{ $blueprint->post->user->username }} mentioned you in a post in {{ $blueprint->post->discussion->title }}.
{!! $blueprint->post->user->username !!} mentioned you in a post in {!! $blueprint->post->discussion->title !!}.
{{ app()->url() }}/d/{{ $blueprint->post->discussion_id }}/{{ $blueprint->post->number }}
{!! app()->url() !!}/d/{!! $blueprint->post->discussion_id !!}/{!! $blueprint->post->number !!}
---
{{ strip_tags($blueprint->post->contentHtml) }}
{!! strip_tags($blueprint->post->contentHtml) !!}