diff --git a/components/Tag/Tag.php b/components/Tag/Tag.php index 666fc6c26a..a0366548fb 100644 --- a/components/Tag/Tag.php +++ b/components/Tag/Tag.php @@ -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); diff --git a/components/Tag/templates/actor_tag_feed.html.twig b/components/Tag/templates/actor_tag_feed.html.twig index 292d2125d4..8aa818f48e 100644 --- a/components/Tag/templates/actor_tag_feed.html.twig +++ b/components/Tag/templates/actor_tag_feed.html.twig @@ -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 %} diff --git a/components/Tag/templates/note_tag_feed.html.twig b/components/Tag/templates/note_tag_feed.html.twig index c0173a97dc..9bf82e7955 100644 --- a/components/Tag/templates/note_tag_feed.html.twig +++ b/components/Tag/templates/note_tag_feed.html.twig @@ -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) }} +
+ {{ noteView.macro_note(note) }} +
{% endblock current_note %} {% endfor %} diff --git a/plugins/RelatedTags/RelatedTags.php b/plugins/RelatedTags/RelatedTags.php new file mode 100644 index 0000000000..30f5788486 --- /dev/null +++ b/plugins/RelatedTags/RelatedTags.php @@ -0,0 +1,77 @@ +. +// }}} + +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; + } +} diff --git a/plugins/RelatedTags/templates/related_tags/actor_tags.html.twig b/plugins/RelatedTags/templates/related_tags/actor_tags.html.twig new file mode 100644 index 0000000000..069a966d54 --- /dev/null +++ b/plugins/RelatedTags/templates/related_tags/actor_tags.html.twig @@ -0,0 +1,7 @@ +
+

Related tags:

+ {% for at in actor_tags %} + {% include 'cards/tag/actor_tag.html.twig' with { 'tag': at, 'actor': null } %} + {% endfor %} +
+ diff --git a/plugins/RelatedTags/templates/related_tags/note_tags.html.twig b/plugins/RelatedTags/templates/related_tags/note_tags.html.twig new file mode 100644 index 0000000000..afa2a6e6c7 --- /dev/null +++ b/plugins/RelatedTags/templates/related_tags/note_tags.html.twig @@ -0,0 +1,6 @@ +
+

Related tags:

+ {% for nt in note_tags %} + {% include 'cards/tag/note_tag.html.twig' with { 'tag': nt } %} + {% endfor %} +
diff --git a/src/Entity/Actor.php b/src/Entity/Actor.php index 1edfe4cebb..07577d0fef 100644 --- a/src/Entity/Actor.php +++ b/src/Entity/Actor.php @@ -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 } diff --git a/src/Entity/NoteTag.php b/src/Entity/NoteTag.php index c4fe4e02ea..71853bbdfe 100644 --- a/src/Entity/NoteTag.php +++ b/src/Entity/NoteTag.php @@ -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 [ diff --git a/templates/cards/profile/view.html.twig b/templates/cards/profile/view.html.twig index 6ede012193..973c6dc5a1 100644 --- a/templates/cards/profile/view.html.twig +++ b/templates/cards/profile/view.html.twig @@ -28,8 +28,8 @@