[PLUGINS][RelatedTags] Add related tags plugin and needed infrastructure. Initial work on pinned content

This commit is contained in:
Hugo Sales 2021-11-28 18:58:56 +00:00 committed by Diogo Peralta Cordeiro
parent 3227018963
commit 3477ad5efc
Signed by: diogo
GPG Key ID: 18D2D35001FBFAB0
11 changed files with 117 additions and 5 deletions

View File

@ -68,7 +68,7 @@ class Tag extends Component
$processed_tags = false;
preg_match_all(self::TAG_REGEX, $content, $matched_tags, \PREG_SET_ORDER);
foreach ($matched_tags as $match) {
$tag = self::ensureLength($match[2]);
$tag = str_replace('#', '', self::ensureLength($match[2]));
$canonical_tag = self::canonicalTag($tag, Language::getFromId($note->getLanguageId())->getLocale());
DB::persist(NoteTag::create(['tag' => $tag, 'canonical' => $canonical_tag, 'note_id' => $note->getId()]));
Cache::pushList("tag-{$canonical_tag}", $note);

View File

@ -1,6 +1,10 @@
{% extends 'base.html.twig' %}
{% block body %}
{% for pinned in handle_event('AddPinnedFeedContent', app.request) %}
{% include pinned['template'] with { 'actor_tags': pinned['vars']} only %}
{% endfor %}
{% for actor in results %}
{% block profile_view %}{% include 'cards/profile/view.html.twig' %}{% endblock profile_view %}
{% endfor %}

View File

@ -2,9 +2,15 @@
{% import '/cards/note/view.html.twig' as noteView %}
{% block body %}
{% for pinned in handle_event('AddPinnedFeedContent', app.request) %}
{% include pinned['template'] with { 'note_tags': pinned['vars']} only %}
{% endfor %}
{% for note in results %}
{% block current_note %}
{{ noteView.macro_note(note) }}
<div class="section-widget">
{{ noteView.macro_note(note) }}
</div>
{% endblock current_note %}
{% endfor %}

View File

@ -0,0 +1,77 @@
<?php
declare(strict_types = 1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
namespace Plugin\RelatedTags;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\Event;
use App\Core\Modules\Plugin;
use App\Entity\NoteTag;
use Symfony\Component\HttpFoundation\Request;
class RelatedTags extends Plugin
{
public function onAddPinnedFeedContent(Request $request, array &$pinned)
{
$tags = $request->attributes->get('tags');
$tags = !\is_null($tags) ? explode('-', $tags) : [$request->attributes->get('tag')];
switch ($request->attributes->get('_route')) {
case 'single_note_tag':
// fall-through
case 'multi_note_tag':
$related = Cache::getList(
'related-note-tags-' . implode('-', $tags),
fn () => DB::sql(
<<<'EOQ'
select {select} from note_tag nt
where nt.note_id in (select n.id from note n join note_tag nt on n.id = nt.note_id where nt.canonical in (:tags))
and not nt.canonical in (:tags)
limit 5
EOQ,
['tags' => $tags],
entities: ['nt' => NoteTag::class],
),
);
$pinned[] = ['template' => 'related_tags/note_tags.html.twig', 'vars' => $related];
break;
case 'single_actor_tag':
// fall-through
case 'multi_actor_tag':
$related = Cache::getList(
'related-actor-tags-' . implode('-', $tags),
fn () => DB::sql(
<<<'EOQ'
select {select} from actor_tag at
where at.tagged in (select at.tagged from actor_tag at where at.canonical in (:tags))
and not at.canonical in (:tags)
limit 5
EOQ,
['tags' => $tags],
),
);
$pinned[] = ['template' => 'related_tags/actor_tags.html.twig', 'vars' => $related];
break;
}
return Event::next;
}
}

View File

@ -0,0 +1,7 @@
<div class="section-widget">
<p>Related tags:</p>
{% for at in actor_tags %}
{% include 'cards/tag/actor_tag.html.twig' with { 'tag': at, 'actor': null } %}
{% endfor %}
</div>

View File

@ -0,0 +1,6 @@
<div class="section-widget">
<p>Related tags:</p>
{% for nt in note_tags %}
{% include 'cards/tag/note_tag.html.twig' with { 'tag': nt } %}
{% endfor %}
</div>

View File

@ -487,7 +487,7 @@ class Actor extends Entity
return array_merge(...F\map($langs, fn ($l) => $l->toChoiceFormat()));
}
public function isVisibleTo(self $other): bool
public function isVisibleTo(null|LocalUser|self $other): bool
{
return true; // TODO
}

View File

@ -22,6 +22,7 @@ declare(strict_types = 1);
namespace App\Entity;
use App\Core\Entity;
use App\Core\Router\Router;
use Component\Tag\Tag;
use DateTimeInterface;
@ -95,6 +96,15 @@ class NoteTag extends Entity
// @codeCoverageIgnoreEnd
// }}} Autocode
public function getUrl(?Actor $actor = null): string
{
$params = ['tag' => $this->getCanonical()];
if (!\is_null($actor)) {
$params['lang'] = $actor->getTopLanguage()->getLocale();
}
return Router::url('single_note_tag', $params);
}
public static function schemaDef(): array
{
return [

View File

@ -28,8 +28,8 @@
<nav class="profile-info-tags">
{% if actor_tags %}
{% for tag in actor_tags %}
<a href="{{ tag.getActorTag().getUrl(actor) }}"><em>#{{ tag.getTag() }}</em></a>
{% for tag in actor_tags %} {# Actually a list of actor_circle #}
{% include 'cards/tag/actor_tag.html.twig' with { 'tag': tag.getActorTag(), 'actor': actor } %}
{% endfor %}
{% else %}
{{ '(No tags)' | trans }}

View File

@ -0,0 +1 @@
<a href="{{ tag.getUrl(actor) }}" title="{{ tag.getTag() }}" rel="tag"><em>#{{ tag.getTag() }}</em></a>

View File

@ -0,0 +1 @@
<a href="{{ tag.getUrl() }}" title="{{ tag.getTag() }}" rel="tag">#{{ tag.getTag() }}</a>