mirror of
https://github.com/OPSnet/Gazelle.git
synced 2026-01-16 18:04:34 -05:00
add a manager to deal with rejected tags
This commit is contained in:
@@ -62,14 +62,54 @@ class Tag extends \Gazelle\BaseManager {
|
||||
|
||||
/**
|
||||
* Check whether this name is allowed. Some tags we never want to see again.
|
||||
* TODO: implement
|
||||
*/
|
||||
// phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
public function validName(string $name): bool {
|
||||
return true;
|
||||
return !$this->pg()->scalar("
|
||||
select 1 from tag_reject where name = ?
|
||||
", $name
|
||||
);
|
||||
}
|
||||
|
||||
// phpcs:enable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
/**
|
||||
* Add a rejected tag that will not be accepted on entry
|
||||
* Returns the table id or 0 on failure
|
||||
*/
|
||||
public function createRejected(string $name, \Gazelle\User $user): int {
|
||||
try {
|
||||
$id = $this->pg()->insert("
|
||||
insert into tag_reject
|
||||
(name, id_user)
|
||||
values (?, ?)
|
||||
", $this->sanitize($name), $user->id
|
||||
);
|
||||
return $id;
|
||||
} catch (\PDOException) {
|
||||
// most likely a duplicate key due to a name that already exists
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public function rejectedList(): array {
|
||||
return $this->pg()->all(<<<END_SQL
|
||||
select tr.id_tag_reject as id,
|
||||
tr.id_user,
|
||||
um."Username" as creator,
|
||||
tr.created,
|
||||
tr.name
|
||||
from tag_reject tr
|
||||
inner join relay.users_main um on (um."ID" = tr.id_user)
|
||||
order by tr.name
|
||||
END_SQL
|
||||
);
|
||||
}
|
||||
|
||||
public function removeRejectedList(array $idList): int {
|
||||
return $this->pg()->prepared_query("
|
||||
delete from tag_reject
|
||||
where id_tag_reject in (" . placeholders($idList) . ")",
|
||||
...$idList
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a tag ready for database input and display.
|
||||
|
||||
23
misc/pg-migrations/20250917000000_tag_reject.php
Normal file
23
misc/pg-migrations/20250917000000_tag_reject.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class TagReject extends AbstractMigration {
|
||||
public function up(): void {
|
||||
$this->table('tag_reject', ['id' => false, 'primary_key' => 'id_tag_reject'])
|
||||
->addColumn('id_tag_reject', 'integer', ['identity' => true])
|
||||
->addColumn('id_user', 'integer')
|
||||
->addColumn('created', 'timestamp', ['timezone' => true, 'default' => 'CURRENT_TIMESTAMP'])
|
||||
->addColumn('name', 'string', ['length' => 40])
|
||||
->save();
|
||||
$this->query("
|
||||
create unique index tr_name_uidx on tag_reject (name)
|
||||
");
|
||||
}
|
||||
|
||||
public function down(): void {
|
||||
$this->table('tag_reject')->drop()->save();
|
||||
}
|
||||
}
|
||||
@@ -937,6 +937,18 @@ hr.tbx {
|
||||
margin: 15px 6px 10px 6px;
|
||||
}
|
||||
|
||||
div.tbx-tag-reject {
|
||||
display: grid;
|
||||
gap: 6px 2px;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
div.tbx-tag-reject div.tbx-field {
|
||||
border: 1px solid #808080;
|
||||
width: 120px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.pager-link {
|
||||
margin: 0 2px;
|
||||
padding: 1px 3px;
|
||||
|
||||
@@ -22,19 +22,18 @@ if (is_null($tgroup)) {
|
||||
|
||||
//Delete cached tag used for undos
|
||||
if (isset($_REQUEST['undo'])) {
|
||||
$Cache->delete_value("deleted_tags_{$tgroup->id()}_{$Viewer->id()}");
|
||||
$Cache->delete_value("deleted_tags_{$tgroup->id}_{$Viewer->id}");
|
||||
}
|
||||
|
||||
$added = [];
|
||||
$rejected = [];
|
||||
$tagMan = new \Gazelle\Manager\Tag();
|
||||
$Tags = array_unique(explode(',', $_REQUEST['tagname']));
|
||||
$tagMan = new Manager\Tag();
|
||||
|
||||
foreach ($Tags as $tagName) {
|
||||
foreach (array_unique(explode(',', $_REQUEST['tagname'])) as $tagName) {
|
||||
$tagName = $tagMan->sanitize($tagName);
|
||||
$resolved = $tagMan->resolve($tagName);
|
||||
|
||||
if (empty($resolved)) {
|
||||
if (is_null($resolved)) {
|
||||
$rejected[] = $tagName;
|
||||
} else {
|
||||
$tag = $tagMan->softCreate($resolved, $Viewer);
|
||||
@@ -44,6 +43,7 @@ foreach ($Tags as $tagName) {
|
||||
json_error('This tag is not allowed');
|
||||
} else {
|
||||
header('Location: ' . $tgroup->location());
|
||||
exit;
|
||||
}
|
||||
}
|
||||
if ($tag->hasVoteTGroup($tgroup, $Viewer)) {
|
||||
@@ -52,8 +52,8 @@ foreach ($Tags as $tagName) {
|
||||
json_error('you have already voted on this tag');
|
||||
} else {
|
||||
header('Location: ' . $tgroup->location());
|
||||
exit;
|
||||
}
|
||||
exit;
|
||||
}
|
||||
$tag->addTGroup($tgroup, $Viewer, 3);
|
||||
$tag->voteTGroup($tgroup, $Viewer, 'up');
|
||||
@@ -64,7 +64,7 @@ foreach ($Tags as $tagName) {
|
||||
}
|
||||
|
||||
$tgroup->refresh();
|
||||
if (AJAX) {
|
||||
if (defined('AJAX')) {
|
||||
json_print('success', [
|
||||
'added' => $added,
|
||||
'rejected' => $rejected,
|
||||
|
||||
@@ -201,6 +201,9 @@ switch ($_REQUEST['action'] ?? '') {
|
||||
case 'tags_official':
|
||||
include_once 'managers/tags_official.php';
|
||||
break;
|
||||
case 'tag-reject':
|
||||
include_once 'managers/tag-reject.php';
|
||||
break;
|
||||
case 'tokens':
|
||||
include_once 'managers/tokens.php';
|
||||
break;
|
||||
|
||||
37
sections/tools/managers/tag-reject.php
Normal file
37
sections/tools/managers/tag-reject.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/** @phpstan-var \Gazelle\User $Viewer */
|
||||
/** @phpstan-var \Twig\Environment $Twig */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Gazelle;
|
||||
|
||||
if (!$Viewer->permitted('users_mod')) {
|
||||
Error403::error();
|
||||
}
|
||||
|
||||
$manager = new Manager\Tag();
|
||||
$message = [];
|
||||
|
||||
if (isset($_POST['create']) && $_POST['create'] !== '') {
|
||||
authorize();
|
||||
$name = $manager->sanitize($_POST['create']);
|
||||
if ($name !== '') {
|
||||
$message[] = $manager->createRejected($name, $Viewer)
|
||||
? "Created tag \"$name\""
|
||||
: "Did not create tag \"$name\", check if it already exists";
|
||||
}
|
||||
}
|
||||
if (isset($_POST['remove']) && $_POST['remove'] !== []) {
|
||||
authorize();
|
||||
$result = $manager->removeRejectedList(
|
||||
array_map('intval', $_POST['remove'])
|
||||
);
|
||||
$message[] = "$result tag" . plural($result) . " removed";
|
||||
}
|
||||
|
||||
echo $Twig->render('tag/reject.twig', [
|
||||
'list' => $manager->rejectedList(),
|
||||
'message' => $message,
|
||||
'viewer' => $Viewer,
|
||||
]);
|
||||
@@ -14,7 +14,7 @@ if (!$Viewer->permitted('users_mod')) {
|
||||
echo $Twig->render('admin/toolbox.twig', [
|
||||
'applicant_viewer' => (bool)array_filter(
|
||||
new Manager\ApplicantRole()->publishedList(),
|
||||
fn($r) => $r->isStaffViewer($Viewer)
|
||||
fn ($r) => $r->isStaffViewer($Viewer)
|
||||
),
|
||||
'viewer' => $Viewer,
|
||||
]);
|
||||
|
||||
@@ -6,6 +6,15 @@ namespace Gazelle;
|
||||
|
||||
if (!empty($_REQUEST['action'])) {
|
||||
switch ($_REQUEST['action']) {
|
||||
case 'add_alias':
|
||||
include_once 'add_alias.php';
|
||||
break;
|
||||
case 'add_tag':
|
||||
include_once __DIR__ . '/../ajax/torrent_tag_add.php';
|
||||
break;
|
||||
case 'delete_alias':
|
||||
include_once 'delete_alias.php';
|
||||
break;
|
||||
case 'edit':
|
||||
include_once 'edit.php';
|
||||
break;
|
||||
@@ -83,12 +92,6 @@ if (!empty($_REQUEST['action'])) {
|
||||
case 'tgroup-merge':
|
||||
include_once 'merge.php';
|
||||
break;
|
||||
case 'add_alias':
|
||||
include_once 'add_alias.php';
|
||||
break;
|
||||
case 'delete_alias':
|
||||
include_once 'delete_alias.php';
|
||||
break;
|
||||
case 'delete':
|
||||
include_once 'delete.php';
|
||||
break;
|
||||
@@ -158,7 +161,7 @@ if (!empty($_REQUEST['action'])) {
|
||||
$torrentId = (int)$_GET['torrentid'];
|
||||
$tgroup = $manager->findByTorrentId($torrentId);
|
||||
if ($tgroup) {
|
||||
header("Location: torrents.php?id={$tgroup->id()}&torrentid={$torrentId}#torrent{$torrentId}");
|
||||
header("Location: torrents.php?id={$tgroup->id}&torrentid={$torrentId}#torrent{$torrentId}");
|
||||
} else {
|
||||
header("Location: log.php?search=Torrent+$torrentId");
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
['Reports V1', 'reports.php', viewer.permittedAny('admin_reports', 'site_moderate_forums')],
|
||||
['Staff page group manager', 'tools.php?action=staff_groups', viewer.permitted('admin_manage_permissions')],
|
||||
['Torrent report configuration', 'tools.php?action=torrent_report_view', viewer.permitted('users_mod')],
|
||||
['Userclass manager', 'tools.php?action=userclass', viewer.permitted('admin_manage_permissions')],
|
||||
]) }}
|
||||
|
||||
{{ _self.category('Announcements', [
|
||||
@@ -48,6 +47,13 @@
|
||||
|
||||
]) }}
|
||||
|
||||
{{ _self.category('Tags', [
|
||||
['Batch tag editor', 'tools.php?action=tags', true],
|
||||
['Tag aliases', 'tools.php?action=tags_aliases', true],
|
||||
['Official tags manager', 'tools.php?action=tags_official', true],
|
||||
['Rejected tags manager', 'tools.php?action=tag-reject', true],
|
||||
]) }}
|
||||
|
||||
</div><div class="toolbox_container">
|
||||
|
||||
{{ _self.category('User management', [
|
||||
@@ -65,6 +71,7 @@
|
||||
['Registration log', 'tools.php?action=registration_log', viewer.permitted('users_view_email')],
|
||||
['Send custom PM', 'tools.php?action=custom_pm', viewer.permitted('admin_site_debug')],
|
||||
['User flow', 'tools.php?action=user_flow', viewer.permitted('site_view_flow')],
|
||||
['Userclass manager', 'tools.php?action=userclass', viewer.permitted('admin_manage_permissions')],
|
||||
]) }}
|
||||
|
||||
{{ _self.category('Community', [
|
||||
@@ -76,12 +83,6 @@
|
||||
['Stylesheet usage', 'tools.php?action=stylesheets', viewer.permitted('admin_manage_stylesheets')],
|
||||
]) }}
|
||||
|
||||
{{ _self.category('Tags', [
|
||||
['Batch tag editor', 'tools.php?action=tags', true],
|
||||
['Tag aliases', 'tools.php?action=tags_aliases', true],
|
||||
['Official tags manager', 'tools.php?action=tags_official', true],
|
||||
]) }}
|
||||
|
||||
</div><div class="toolbox_container">
|
||||
|
||||
{{ _self.category('Finances', [
|
||||
|
||||
57
templates/tag/reject.twig
Normal file
57
templates/tag/reject.twig
Normal file
@@ -0,0 +1,57 @@
|
||||
{{ header('Rejected Tags Manager') }}
|
||||
<div class="header">
|
||||
<div class="linkbox">
|
||||
<a href="?action=tags" class="brackets">Batch Tag Editor</a>
|
||||
<a href="?action=tags_aliases" class="brackets">Tag Aliases</a>
|
||||
<a href="?action=tags_official" class="brackets">Official Tags</a>
|
||||
</div>
|
||||
<h2>Rejected Tags Manager</h2>
|
||||
</div>
|
||||
<div class="thin box">
|
||||
{% for m in message %}
|
||||
{% if loop.first %}
|
||||
<ul class="nobullet">
|
||||
{% endif %}
|
||||
<li>{{ m }}</li>
|
||||
{% if loop.last %}
|
||||
</ul>
|
||||
<br />
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<form name="tag-reject" method="post">
|
||||
{% for tag in list %}
|
||||
{% if loop.first %}
|
||||
<div class="tbx-tag-reject">
|
||||
{% endif %}
|
||||
<div class="tbx-field">
|
||||
<span title="Created by {{ tag.creator }} on {{
|
||||
tag.created|date('Y-m-d H:i:s') }}">{{ tag.name }}</span>
|
||||
<div style="float: right">
|
||||
<label><small>❌</small>
|
||||
<input type="checkbox" name="remove[]" value="{{ tag.id }}" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{% if loop.last %}
|
||||
<div>Check any <small>❌</small> to remove</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<div class="pad"><b>No rejected tags have been defined.</b></div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="tbx tbx-tag-reject-edit">
|
||||
<div class="tbx-label">Create new entry</div>
|
||||
<div class="tbx-field">
|
||||
<input type="text" name="create" size="20" value="" placeholder="tag name" />
|
||||
</div>
|
||||
<div class="tbx-label"></div>
|
||||
<div class="tbx-field">
|
||||
<input type="hidden" name="action" value="tag-reject" />
|
||||
<input type="hidden" name="auth" value="{{ viewer.auth }}" />
|
||||
<input type="submit" value="Update" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{ footer() }}
|
||||
@@ -15,29 +15,29 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
{% for tag in tgroup.tagList %}
|
||||
{% if loop.first %}
|
||||
{% if loop.first %}
|
||||
<ul class="stats nobullet">
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<li>
|
||||
<a href="torrents.php?taglist={{ tag.name }}" style="float: left; display: block;">{{ tag.name }}</a>
|
||||
<div style="float: right; display: block; letter-spacing: -1px;" class="edit_tags_votes">
|
||||
<a href="torrents.php?action=vote_tag&way=up&groupid={{ tgroup_id }}&tagid={{ tag.id }}&auth={{ viewer.auth }}" title="Vote this tag up" class="tooltip vote_tag_up">▲</a>
|
||||
{{ tag.score }}
|
||||
<a href="torrents.php?action=vote_tag&way=down&groupid={{ tgroup_id }}&tagid={{ tag.id }}&auth={{ viewer.auth }}" title="Vote this tag down" class="tooltip vote_tag_down">▼</a>
|
||||
{% if viewer.permitted('users_warn') %}
|
||||
{% if viewer.permitted('users_warn') %}
|
||||
<a href="user.php?id={{ tag.userId }}" title="View the profile of the user that added this tag" class="brackets tooltip view_tag_user">U</a>
|
||||
{% endif %}
|
||||
{% if not viewer.disableTagging and viewer.permitted('site_delete_tag') %}
|
||||
{% endif %}
|
||||
{% if not viewer.disableTagging and viewer.permitted('site_delete_tag') %}
|
||||
<span class="remove remove_tag">
|
||||
<a href="ajax.php?action=delete_tag&groupid={{ tgroup_id }}&tagid={{ tag.id }}&auth={{ viewer.auth }}" class="brackets tooltip" title="Remove tag">X</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<br style="clear: both;" />
|
||||
</li>
|
||||
{% if loop.last %}
|
||||
{% if loop.last %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<ul><li>There are no tags to display.</li></ul>
|
||||
{% endfor %}
|
||||
@@ -47,7 +47,7 @@
|
||||
<div class="box box_addtag">
|
||||
<div class="head"><strong>Add tag</strong></div>
|
||||
<div class="body">
|
||||
<form class="add_form" name="tags" action="ajax.php" method="post">
|
||||
<form class="add_form" name="tags" action="torrents.php" method="post">
|
||||
<input type="hidden" name="action" value="add_tag" />
|
||||
<input type="hidden" name="auth" value="{{ viewer.auth }}" />
|
||||
<input type="hidden" name="groupid" value="{{ tgroup_id }}>" />
|
||||
|
||||
@@ -47,6 +47,7 @@ class TagTest extends TestCase {
|
||||
|
||||
public function testNormalize(): void {
|
||||
$manager = new Manager\Tag();
|
||||
$this->assertEquals('', $manager->normalize(' '), 'tag-normalize-space');
|
||||
$this->assertEquals('dub', $manager->normalize('Dub dub DUB! '), 'tag-normalize-dup');
|
||||
$this->assertEquals('neo.folk', $manager->normalize('neo...folk neo-folk'), 'tag-normalize-more');
|
||||
$this->assertEquals('pop rock', $manager->normalize(' pop rock rock pop Rock'), 'tag-normalize-two');
|
||||
@@ -144,6 +145,13 @@ class TagTest extends TestCase {
|
||||
),
|
||||
'tag-replace-alias'
|
||||
);
|
||||
$found = $manager->listAlias(false);
|
||||
$this->assertGreaterThan(0, count($found), 'tag-alias-total');
|
||||
$this->assertEquals(
|
||||
['id' => $aliasId, 'bad' => $bad->name(), 'alias' => $good->name()],
|
||||
$found[$aliasId],
|
||||
'tag-alias-list',
|
||||
);
|
||||
$tagList = [
|
||||
'include' => [$good->name()],
|
||||
'exclude' => ['!' . $exclude->name()],
|
||||
@@ -396,6 +404,10 @@ class TagTest extends TestCase {
|
||||
}
|
||||
|
||||
public function testTop10(): void {
|
||||
global $Cache;
|
||||
$Cache->delete_multi([
|
||||
"toptaguse_1", "toptagreq_1", "toptagvote_1",
|
||||
]);
|
||||
$manager = new Manager\Tag();
|
||||
$this->assertGreaterThanOrEqual(0, count($manager->topTGroupList(1)), 'tag-top10-tgroup');
|
||||
$this->assertGreaterThanOrEqual(0, count($manager->topRequestList(1)), 'tag-top10-request');
|
||||
@@ -414,4 +426,26 @@ class TagTest extends TestCase {
|
||||
|
||||
$this->assertCount(0, new Json\Top10\Tag('bogus', 1, $manager)->payload(), 'tag-top10-bogus-payload');
|
||||
}
|
||||
|
||||
public function testTagRejection(): void {
|
||||
$this->user = Helper::makeUser('tag.' . randomString(6), 'tag');
|
||||
$manager = new Manager\Tag();
|
||||
$name = 'reject.' . randomString(16);
|
||||
|
||||
$id = $manager->createRejected($name, $this->user);
|
||||
$this->assertGreaterThan(0, $id, 'tag-reject-create');
|
||||
$this->assertEquals(
|
||||
0,
|
||||
$manager->createRejected($name, $this->user),
|
||||
'tag-reject-duplicate',
|
||||
);
|
||||
$list = array_filter($manager->rejectedList(), fn ($t) => $t['name'] === $name);
|
||||
$this->assertCount(1, $list, 'tag-reject-list-count');
|
||||
$this->assertEquals($name, $list[0]['name'], 'tag-reject-list-name');
|
||||
$this->assertEquals(
|
||||
1,
|
||||
$manager->removeRejectedList([$id]),
|
||||
'tag-reject-remove',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user