[PLUGIN][TagBasedFiltering] Expand to allow filtering by actor tags
This commit is contained in:
parent
e29e1cc87c
commit
9f445632b2
@ -28,12 +28,14 @@ use App\Core\Controller;
|
|||||||
use App\Core\DB\DB;
|
use App\Core\DB\DB;
|
||||||
use App\Core\Form;
|
use App\Core\Form;
|
||||||
use function App\Core\I18n\_m;
|
use function App\Core\I18n\_m;
|
||||||
|
use App\Entity\Actor;
|
||||||
|
use App\Entity\ActorTag;
|
||||||
|
use App\Entity\ActorTagBlock;
|
||||||
use App\Entity\Language;
|
use App\Entity\Language;
|
||||||
use App\Entity\Note;
|
use App\Entity\Note;
|
||||||
use App\Entity\NoteTag;
|
use App\Entity\NoteTag;
|
||||||
use App\Entity\NoteTagBlock;
|
use App\Entity\NoteTagBlock;
|
||||||
use App\Util\Common;
|
use App\Util\Common;
|
||||||
use App\Util\Exception\NotImplementedException;
|
|
||||||
use App\Util\Exception\RedirectException;
|
use App\Util\Exception\RedirectException;
|
||||||
use Component\Tag\Tag;
|
use Component\Tag\Tag;
|
||||||
use Functional as F;
|
use Functional as F;
|
||||||
@ -46,23 +48,24 @@ use Symfony\Component\HttpFoundation\Request;
|
|||||||
|
|
||||||
class TagBasedFiltering extends Controller
|
class TagBasedFiltering extends Controller
|
||||||
{
|
{
|
||||||
/**
|
private function editBlocked(
|
||||||
* Edit the user's list of blocked note tags, with the option of adding the notes from the note $note_id, if given
|
Request $request,
|
||||||
*/
|
?int $id,
|
||||||
public function editBlockedNoteTags(Request $request, ?int $note_id)
|
string $type_name,
|
||||||
{
|
callable $calculate_target,
|
||||||
|
callable $calculate_blocks,
|
||||||
|
callable $calculate_tags,
|
||||||
|
string $new_label,
|
||||||
|
string $existing_label,
|
||||||
|
string $block_class,
|
||||||
|
) {
|
||||||
$user = Common::ensureLoggedIn();
|
$user = Common::ensureLoggedIn();
|
||||||
$note = !\is_null($note_id) ? Note::getById($note_id) : null;
|
$target = $calculate_target();
|
||||||
$note_tag_blocks = NoteTagBlock::getByActorId($user->getId());
|
$tag_blocks = $calculate_blocks($user);
|
||||||
$note_tags = !\is_null($note_id) ? NoteTag::getByNoteId($note_id) : [];
|
$blockable_tags = $calculate_tags($tag_blocks);
|
||||||
$blockable_note_tags = F\reject(
|
|
||||||
$note_tags,
|
|
||||||
fn (NoteTag $nt) => NoteTagBlock::checkBlocksNoteTag($nt, $note_tag_blocks),
|
|
||||||
);
|
|
||||||
|
|
||||||
$new_tags_form_definition = [];
|
$new_tags_form_definition = [];
|
||||||
|
foreach ($blockable_tags as $nt) {
|
||||||
foreach ($blockable_note_tags as $nt) {
|
|
||||||
$canon = $nt->getCanonical();
|
$canon = $nt->getCanonical();
|
||||||
$new_tags_form_definition[] = ["{$canon}:new-tag", TextType::class, ['data' => '#' . $nt->getTag(), 'label' => ' ']];
|
$new_tags_form_definition[] = ["{$canon}:new-tag", TextType::class, ['data' => '#' . $nt->getTag(), 'label' => ' ']];
|
||||||
$new_tags_form_definition[] = ["{$canon}:use-canon", CheckboxType::class, ['label' => _m('Use canonical'), 'help' => _m('Block all similar tags'), 'required' => false, 'data' => true]];
|
$new_tags_form_definition[] = ["{$canon}:use-canon", CheckboxType::class, ['label' => _m('Use canonical'), 'help' => _m('Block all similar tags'), 'required' => false, 'data' => true]];
|
||||||
@ -70,7 +73,7 @@ class TagBasedFiltering extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$existing_tags_form_definition = [];
|
$existing_tags_form_definition = [];
|
||||||
foreach ($note_tag_blocks as $ntb) {
|
foreach ($tag_blocks as $ntb) {
|
||||||
$canon = $ntb->getCanonical();
|
$canon = $ntb->getCanonical();
|
||||||
$existing_tags_form_definition[] = ["{$canon}:old-tag", TextType::class, ['data' => '#' . $ntb->getTag(), 'label' => ' ', 'disabled' => true]];
|
$existing_tags_form_definition[] = ["{$canon}:old-tag", TextType::class, ['data' => '#' . $ntb->getTag(), 'label' => ' ', 'disabled' => true]];
|
||||||
$existing_tags_form_definition[] = ["{$canon}:toggle-canon", SubmitType::class, ['label' => $ntb->getUseCanonical() ? _m('Set non-canonical') : _m('Set canonical')]];
|
$existing_tags_form_definition[] = ["{$canon}:toggle-canon", SubmitType::class, ['label' => $ntb->getUseCanonical() ? _m('Set non-canonical') : _m('Set canonical')]];
|
||||||
@ -78,7 +81,7 @@ class TagBasedFiltering extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$new_tags_form = null;
|
$new_tags_form = null;
|
||||||
if (!empty($new_tags_form_definition)) {
|
if (!empty($new_tags_form_definition) && $user->getId() !== $target->getActorId()) {
|
||||||
$new_tags_form = Form::create($new_tags_form_definition);
|
$new_tags_form = Form::create($new_tags_form_definition);
|
||||||
$new_tags_form->handleRequest($request);
|
$new_tags_form->handleRequest($request);
|
||||||
if ($new_tags_form->isSubmitted() && $new_tags_form->isValid()) {
|
if ($new_tags_form->isSubmitted() && $new_tags_form->isValid()) {
|
||||||
@ -89,11 +92,12 @@ class TagBasedFiltering extends Controller
|
|||||||
/** @var SubmitButton $button */
|
/** @var SubmitButton $button */
|
||||||
$button = $new_tags_form->get($id);
|
$button = $new_tags_form->get($id);
|
||||||
if ($button->isClicked()) {
|
if ($button->isClicked()) {
|
||||||
Cache::delete(NoteTagBlock::cacheKey($user->getId()));
|
Cache::delete($block_class::cacheKey($user->getId()));
|
||||||
Cache::delete(TagFilerPlugin::cacheKeys($user->getId())['note']);
|
Cache::delete(TagFilerPlugin::cacheKeys($user->getId())[$type_name]);
|
||||||
$new_tag = Tag::ensureValid($data[$canon . ':new-tag']);
|
$new_tag = Tag::ensureValid($data[$canon . ':new-tag']);
|
||||||
$canonical_tag = Tag::canonicalTag($new_tag, Language::getByNote($note)->getLocale());
|
$language = $target instanceof Note ? Language::getByNote($target)->getLocale() : $user->getActor()->getTopLanguage()->getLocale();
|
||||||
DB::persist(NoteTagBlock::create([
|
$canonical_tag = Tag::canonicalTag($new_tag, $language);
|
||||||
|
DB::persist($block_class::create([
|
||||||
'blocker' => $user->getId(),
|
'blocker' => $user->getId(),
|
||||||
'tag' => $new_tag,
|
'tag' => $new_tag,
|
||||||
'canonical' => $canonical_tag,
|
'canonical' => $canonical_tag,
|
||||||
@ -119,17 +123,17 @@ class TagBasedFiltering extends Controller
|
|||||||
/** @var SubmitButton $button */
|
/** @var SubmitButton $button */
|
||||||
$button = $existing_tags_form->get($id);
|
$button = $existing_tags_form->get($id);
|
||||||
if ($button->isClicked()) {
|
if ($button->isClicked()) {
|
||||||
Cache::delete(NoteTagBlock::cacheKey($user->getId()));
|
Cache::delete($block_class::cacheKey($user->getId()));
|
||||||
Cache::delete(TagFilerPlugin::cacheKeys($user->getId())['note']);
|
Cache::delete(TagFilerPlugin::cacheKeys($user->getId())[$type_name]);
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'toggle-canon':
|
case 'toggle-canon':
|
||||||
$ntb = DB::getReference('note_tag_block', ['blocker' => $user->getId(), 'canonical' => $canon]);
|
$ntb = DB::getReference($block_class, ['blocker' => $user->getId(), 'canonical' => $canon]);
|
||||||
$ntb->setUseCanonical(!$ntb->getUseCanonical());
|
$ntb->setUseCanonical(!$ntb->getUseCanonical());
|
||||||
DB::flush();
|
DB::flush();
|
||||||
throw new RedirectException;
|
throw new RedirectException;
|
||||||
case 'remove':
|
case 'remove':
|
||||||
$old_tag = $data[$canon . ':old-tag'];
|
$old_tag = $data[$canon . ':old-tag'];
|
||||||
DB::removeBy('note_tag_block', ['blocker' => $user->getId(), 'canonical' => $canon]);
|
DB::removeBy($block_class, ['blocker' => $user->getId(), 'canonical' => $canon]);
|
||||||
throw new RedirectException;
|
throw new RedirectException;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,14 +144,50 @@ class TagBasedFiltering extends Controller
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'_template' => 'tag-based-filtering/edit-tags.html.twig',
|
'_template' => 'tag-based-filtering/edit-tags.html.twig',
|
||||||
'note' => !\is_null($note_id) ? Note::getById($note_id) : null,
|
$type_name => $target,
|
||||||
'new_tags_form' => $new_tags_form?->createView(),
|
'new_tags_form' => $new_tags_form?->createView(),
|
||||||
'existing_tags_form' => $existing_tags_form?->createView(),
|
'existing_tags_form' => $existing_tags_form?->createView(),
|
||||||
|
'new_label' => $new_label,
|
||||||
|
'existing_label' => $existing_label,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function editBlockedActorTags(Request $request, ?int $note_id)
|
/**
|
||||||
|
* Edit the user's list of blocked note tags, with the option of adding the notes from the note $note_id, if given
|
||||||
|
*/
|
||||||
|
public function editBlockedNoteTags(Request $request, ?int $note_id)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException;
|
return $this->editBlocked(
|
||||||
|
request: $request,
|
||||||
|
id: $note_id,
|
||||||
|
type_name: 'note',
|
||||||
|
calculate_target: fn () => !\is_null($note_id) ? Note::getById($note_id) : null,
|
||||||
|
calculate_blocks: fn ($user) => NoteTagBlock::getByActorId($user->getId()),
|
||||||
|
calculate_tags: fn ($tag_blocks) => F\reject(
|
||||||
|
!\is_null($note_id) ? NoteTag::getByNoteId($note_id) : [],
|
||||||
|
fn (NoteTag $nt) => NoteTagBlock::checkBlocksNoteTag($nt, $tag_blocks),
|
||||||
|
),
|
||||||
|
new_label: _m('Tags in the note above:'),
|
||||||
|
existing_label: _m('Tags you already blocked:'),
|
||||||
|
block_class: NoteTagBlock::class,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function editBlockedActorTags(Request $request, ?int $actor_id)
|
||||||
|
{
|
||||||
|
return $this->editBlocked(
|
||||||
|
request: $request,
|
||||||
|
id: $actor_id,
|
||||||
|
type_name: 'actor',
|
||||||
|
calculate_target: fn () => !\is_null($actor_id) ? Actor::getById($actor_id) : null,
|
||||||
|
calculate_blocks: fn ($user) => ActorTagBlock::getByActorId($user->getId()),
|
||||||
|
calculate_tags: fn ($tag_blocks) => F\reject(
|
||||||
|
!\is_null($actor_id) ? ActorTag::getByActorId($actor_id) : [],
|
||||||
|
fn (ActorTag $nt) => ActorTagBlock::checkBlocksActorTag($nt, $tag_blocks),
|
||||||
|
),
|
||||||
|
new_label: _m('Tags of the account above:'),
|
||||||
|
existing_label: _m('Tags you already blocked:'),
|
||||||
|
block_class: ActorTagBlock::class,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,13 +48,13 @@ class TagBasedFiltering extends Plugin
|
|||||||
if (!\is_int($actor_id)) {
|
if (!\is_int($actor_id)) {
|
||||||
$actor_id = $actor_id->getId();
|
$actor_id = $actor_id->getId();
|
||||||
}
|
}
|
||||||
return ['note' => "filtered-tags-{$actor_id}"];
|
return ['note' => "blocked-note-tags-{$actor_id}", 'actor' => "blocked-actor-tags-{$actor_id}"];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onAddRoute(RouteLoader $r)
|
public function onAddRoute(RouteLoader $r)
|
||||||
{
|
{
|
||||||
$r->connect(id: self::NOTE_TAG_FILTER_ROUTE, uri_path: '/filter/edit-blocked-note-tags/{note_id<\d+>?}', target: [Controller\TagBasedFiltering::class, 'editBlockedNoteTags']);
|
$r->connect(id: self::NOTE_TAG_FILTER_ROUTE, uri_path: '/filter/edit-blocked-note-tags/{note_id<\d+>?}', target: [Controller\TagBasedFiltering::class, 'editBlockedNoteTags']);
|
||||||
$r->connect(id: self::ACTOR_TAG_FILTER_ROUTE, uri_path: '/filter/edit-blocked-actor-tags/{note_id<\d+>?}', target: [Controller\TagBasedFiltering::class, 'editBlockedActorTags']);
|
$r->connect(id: self::ACTOR_TAG_FILTER_ROUTE, uri_path: '/filter/edit-blocked-actor-tags/{actor_id<\d+>?}', target: [Controller\TagBasedFiltering::class, 'editBlockedActorTags']);
|
||||||
return Event::next;
|
return Event::next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ class TagBasedFiltering extends Plugin
|
|||||||
$actions[] = [
|
$actions[] = [
|
||||||
'title' => _m('Block people tags'),
|
'title' => _m('Block people tags'),
|
||||||
'classes' => '',
|
'classes' => '',
|
||||||
'url' => Router::url(self::ACTOR_TAG_FILTER_ROUTE, ['note_id' => $note->getId()]),
|
'url' => Router::url(self::ACTOR_TAG_FILTER_ROUTE, ['actor_id' => $note->getActor()->getId()]),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,15 +2,22 @@
|
|||||||
{% import '/cards/note/view.html.twig' as noteView %}
|
{% import '/cards/note/view.html.twig' as noteView %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% if new_tags_form is not null %}
|
{% if note is defined or actor is defined %}
|
||||||
<div class="section-widget">
|
<div class="section-widget-padded">
|
||||||
|
{% if note is defined %}
|
||||||
{{ noteView.macro_note(note, {}) }}
|
{{ noteView.macro_note(note, {}) }}
|
||||||
|
{% elseif actor is defined %}
|
||||||
|
{% include 'cards/profile/view.html.twig' with {'actor': actor} only %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<p>{% trans %}Tags in the note above:{% endtrans %}</p>
|
{% endif %}
|
||||||
|
{% if new_tags_form is not null %}
|
||||||
|
<p>{{ new_label }}</p>
|
||||||
{{ form(new_tags_form) }}
|
{{ form(new_tags_form) }}
|
||||||
|
<hr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if existing_tags_form is not null %}
|
{% if existing_tags_form is not null %}
|
||||||
<p>{% trans %}Tags you already blocked:{% endtrans %}</p>
|
<p>{{ existing_label }}</p>
|
||||||
{{ form(existing_tags_form) }}
|
{{ form(existing_tags_form) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -263,6 +263,7 @@ class UserPanel extends Controller
|
|||||||
$extra_step = function ($data, $extra_args) use ($user, $actor) {
|
$extra_step = function ($data, $extra_args) use ($user, $actor) {
|
||||||
$user->setNicknameSanitizedAndCached($data['nickname'], $actor->getId());
|
$user->setNicknameSanitizedAndCached($data['nickname'], $actor->getId());
|
||||||
};
|
};
|
||||||
|
|
||||||
return Form::handle($form_definition, $request, $actor, $extra, $extra_step, [['self_tags' => $extra['self_tags']]]);
|
return Form::handle($form_definition, $request, $actor, $extra, $extra_step, [['self_tags' => $extra['self_tags']]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,10 +263,15 @@ class DB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function removeBy(string $table, array $criteria)
|
public static function removeBy(string $table, array $criteria): void
|
||||||
{
|
{
|
||||||
|
$class = self::$table_map[$table];
|
||||||
|
if (empty(array_intersect(self::getPKForClass($class), array_keys($criteria)))) {
|
||||||
|
self::remove(self::findOneBy($class, $criteria));
|
||||||
|
} else {
|
||||||
self::remove(self::getReference($table, $criteria));
|
self::remove(self::getReference($table, $criteria));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function count(string $table, array $criteria)
|
public static function count(string $table, array $criteria)
|
||||||
{
|
{
|
||||||
|
@ -270,6 +270,14 @@ class Actor extends Entity
|
|||||||
return Cache::get('actor-fullname-id-' . $id, fn () => self::getById($id)->getFullname());
|
return Cache::get('actor-fullname-id-' . $id, fn () => self::getById($id)->getFullname());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For consistency with Note
|
||||||
|
*/
|
||||||
|
public function getActorId(): int
|
||||||
|
{
|
||||||
|
return $this->getId();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tags attributed to self, shortcut function for increased legibility
|
* Tags attributed to self, shortcut function for increased legibility
|
||||||
*
|
*
|
||||||
|
@ -21,6 +21,7 @@ declare(strict_types = 1);
|
|||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Core\Cache;
|
||||||
use App\Core\DB\DB;
|
use App\Core\DB\DB;
|
||||||
use App\Core\Entity;
|
use App\Core\Entity;
|
||||||
use App\Core\Router\Router;
|
use App\Core\Router\Router;
|
||||||
@ -109,6 +110,19 @@ class ActorTag extends Entity
|
|||||||
// @codeCoverageIgnoreEnd
|
// @codeCoverageIgnoreEnd
|
||||||
// }}} Autocode
|
// }}} Autocode
|
||||||
|
|
||||||
|
public static function cacheKey(int|Actor $actor_id)
|
||||||
|
{
|
||||||
|
if (!\is_int($actor_id)) {
|
||||||
|
$actor_id = $actor_id->getId();
|
||||||
|
}
|
||||||
|
return "actor-tags-{$actor_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getByActorId(int $actor_id): array
|
||||||
|
{
|
||||||
|
return Cache::getList(self::cacheKey($actor_id), fn () => DB::dql('select at from actor_tag at join actor a with a.id = at.tagger where a.id = :id', ['id' => $actor_id]));
|
||||||
|
}
|
||||||
|
|
||||||
public function getUrl(?Actor $actor = null): string
|
public function getUrl(?Actor $actor = null): string
|
||||||
{
|
{
|
||||||
$params = ['tag' => $this->getCanonical()];
|
$params = ['tag' => $this->getCanonical()];
|
||||||
|
@ -26,6 +26,7 @@ use App\Core\DB\DB;
|
|||||||
use App\Core\Entity;
|
use App\Core\Entity;
|
||||||
use Component\Tag\Tag;
|
use Component\Tag\Tag;
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
|
use Functional as F;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity for User's Note Tag block
|
* Entity for User's Note Tag block
|
||||||
|
Loading…
Reference in New Issue
Block a user