[tag_edit] separate responsibility for tags, source, owner, and lock
This commit is contained in:
parent
3d905b1055
commit
9c4e306b58
26 changed files with 545 additions and 322 deletions
|
@ -38,8 +38,8 @@ class Artists extends Extension
|
|||
public function onImageInfoSet(ImageInfoSetEvent $event): void
|
||||
{
|
||||
global $user;
|
||||
if ($user->can(Permissions::EDIT_IMAGE_ARTIST) && isset($event->params["tag_edit__author"])) {
|
||||
send_event(new AuthorSetEvent($event->image, $user, $event->params["tag_edit__author"]));
|
||||
if ($user->can(Permissions::EDIT_IMAGE_ARTIST) && isset($event->params["author"])) {
|
||||
send_event(new AuthorSetEvent($event->image, $user, $event->params["author"]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ class ArtistsTheme extends Themelet
|
|||
return SHM_POST_INFO(
|
||||
"Author",
|
||||
$author,
|
||||
INPUT(["type" => "text", "name" => "tag_edit__author", "value" => $author])
|
||||
INPUT(["type" => "text", "name" => "author", "value" => $author])
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,11 +29,12 @@ class MetadataInput
|
|||
public static function update_post_metadata(int $post_id, MetadataInput $metadata): Image
|
||||
{
|
||||
global $user;
|
||||
$_POST['tag_edit__tags'] = $metadata->tags;
|
||||
$_POST['tag_edit__source'] = $metadata->source;
|
||||
$image = Image::by_id($post_id);
|
||||
if (!$image->is_locked() || $user->can(Permissions::EDIT_IMAGE_LOCK)) {
|
||||
send_event(new ImageInfoSetEvent($image, $_POST));
|
||||
send_event(new ImageInfoSetEvent($image, [
|
||||
'tags' => $metadata->tags,
|
||||
'source' => $metadata->source,
|
||||
]));
|
||||
}
|
||||
return Image::by_id($post_id);
|
||||
}
|
||||
|
|
18
ext/post_lock/info.php
Normal file
18
ext/post_lock/info.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
class PostLockInfo extends ExtensionInfo
|
||||
{
|
||||
public const KEY = "post_lock";
|
||||
|
||||
public string $key = self::KEY;
|
||||
public string $name = "Lock Editor";
|
||||
public bool $core = true;
|
||||
public string $url = self::SHIMMIE_URL;
|
||||
public array $authors = self::SHISH_AUTHOR;
|
||||
public ExtensionCategory $category = ExtensionCategory::METADATA;
|
||||
public string $description = "Allow images to have metadata locked";
|
||||
}
|
56
ext/post_lock/main.php
Normal file
56
ext/post_lock/main.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
class LockSetEvent extends Event
|
||||
{
|
||||
public Image $image;
|
||||
public bool $locked;
|
||||
|
||||
public function __construct(Image $image, bool $locked)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->image = $image;
|
||||
$this->locked = $locked;
|
||||
}
|
||||
}
|
||||
|
||||
class PostLock extends Extension
|
||||
{
|
||||
/** @var PostLockTheme */
|
||||
protected Themelet $theme;
|
||||
|
||||
public function onImageAddition(ImageAdditionEvent $event): void
|
||||
{
|
||||
if (!empty($event->metadata['locked'])) {
|
||||
send_event(new LockSetEvent($event->image, $event->metadata['locked']));
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageInfoSet(ImageInfoSetEvent $event): void
|
||||
{
|
||||
global $page, $user;
|
||||
if ($event->image->is_locked() && !$user->can(Permissions::EDIT_IMAGE_LOCK)) {
|
||||
throw new PermissionDenied("Error: This image is locked and cannot be edited.");
|
||||
}
|
||||
if ($user->can(Permissions::EDIT_IMAGE_LOCK)) {
|
||||
$locked = isset($event->params['locked']) && $event->params['locked'] == "on";
|
||||
send_event(new LockSetEvent($event->image, $locked));
|
||||
}
|
||||
}
|
||||
|
||||
public function onLockSet(LockSetEvent $event): void
|
||||
{
|
||||
global $user;
|
||||
if ($user->can(Permissions::EDIT_IMAGE_LOCK)) {
|
||||
$event->image->set_locked($event->locked);
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event): void
|
||||
{
|
||||
$event->add_part($this->theme->get_lock_editor_html($event->image), 42);
|
||||
}
|
||||
}
|
37
ext/post_lock/test.php
Normal file
37
ext/post_lock/test.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
class PostLockTest extends ShimmiePHPUnitTestCase
|
||||
{
|
||||
public function testLockEdit(): void
|
||||
{
|
||||
// user can post
|
||||
$this->log_in_as_user();
|
||||
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
|
||||
|
||||
// admin can lock
|
||||
$this->log_in_as_admin();
|
||||
send_event(new ImageInfoSetEvent(Image::by_id($image_id), ["locked" => "on"]));
|
||||
|
||||
// user can't edit locked post
|
||||
$this->log_in_as_user();
|
||||
$this->assertException(PermissionDenied::class, function () use ($image_id) {
|
||||
send_event(new ImageInfoSetEvent(Image::by_id($image_id), ["source" => "http://example.com"]));
|
||||
});
|
||||
|
||||
// admin can edit locked post
|
||||
$this->log_in_as_admin();
|
||||
send_event(new ImageInfoSetEvent(Image::by_id($image_id), ["source" => "http://example.com"]));
|
||||
|
||||
// admin can unlock
|
||||
$this->log_in_as_admin();
|
||||
send_event(new ImageInfoSetEvent(Image::by_id($image_id), [])); // "locked" is not set
|
||||
|
||||
// user can edit un-locked post
|
||||
$this->log_in_as_user();
|
||||
send_event(new ImageInfoSetEvent(Image::by_id($image_id), ["source" => "http://example.com"]));
|
||||
}
|
||||
}
|
22
ext/post_lock/theme.php
Normal file
22
ext/post_lock/theme.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
use MicroHTML\HTMLElement;
|
||||
|
||||
use function MicroHTML\{INPUT};
|
||||
|
||||
class PostLockTheme extends Themelet
|
||||
{
|
||||
public function get_lock_editor_html(Image $image): HTMLElement
|
||||
{
|
||||
global $user;
|
||||
return SHM_POST_INFO(
|
||||
"Locked",
|
||||
$image->is_locked() ? "Yes (Only admins may edit these details)" : "No",
|
||||
$user->can(Permissions::EDIT_IMAGE_LOCK) ? INPUT(["type" => "checkbox", "name" => "locked", "checked" => $image->is_locked()]) : null
|
||||
);
|
||||
}
|
||||
}
|
18
ext/post_owner/info.php
Normal file
18
ext/post_owner/info.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
class PostOwnerInfo extends ExtensionInfo
|
||||
{
|
||||
public const KEY = "post_owner";
|
||||
|
||||
public string $key = self::KEY;
|
||||
public string $name = "Owner Editor";
|
||||
public bool $core = true;
|
||||
public string $url = self::SHIMMIE_URL;
|
||||
public array $authors = self::SHISH_AUTHOR;
|
||||
public ExtensionCategory $category = ExtensionCategory::METADATA;
|
||||
public string $description = "Allow images to have owners assigned to them";
|
||||
}
|
55
ext/post_owner/main.php
Normal file
55
ext/post_owner/main.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
class OwnerSetEvent extends Event
|
||||
{
|
||||
public Image $image;
|
||||
public User $owner;
|
||||
|
||||
public function __construct(Image $image, User $owner)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->image = $image;
|
||||
$this->owner = $owner;
|
||||
}
|
||||
}
|
||||
|
||||
class PostOwner extends Extension
|
||||
{
|
||||
/** @var PostOwnerTheme */
|
||||
protected Themelet $theme;
|
||||
|
||||
public function onImageAddition(ImageAdditionEvent $event): void
|
||||
{
|
||||
// FIXME: send an event instead of implicit default owner?
|
||||
}
|
||||
|
||||
public function onImageInfoSet(ImageInfoSetEvent $event): void
|
||||
{
|
||||
global $page, $user;
|
||||
if ($user->can(Permissions::EDIT_IMAGE_OWNER) && isset($event->params['owner'])) {
|
||||
$owner = User::by_name($event->params['owner']);
|
||||
if ($owner instanceof User) {
|
||||
send_event(new OwnerSetEvent($event->image, $owner));
|
||||
} else {
|
||||
throw new UserNotFound("Error: No user with that name was found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onOwnerSet(OwnerSetEvent $event): void
|
||||
{
|
||||
global $user;
|
||||
if ($user->can(Permissions::EDIT_IMAGE_OWNER)) {
|
||||
$event->image->set_owner($event->owner);
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event): void
|
||||
{
|
||||
$event->add_part($this->theme->get_owner_editor_html($event->image), 39);
|
||||
}
|
||||
}
|
22
ext/post_owner/test.php
Normal file
22
ext/post_owner/test.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
class PostOwnerTest extends ShimmiePHPUnitTestCase
|
||||
{
|
||||
public function testOwnerEdit(): void
|
||||
{
|
||||
$this->log_in_as_user();
|
||||
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
|
||||
$image = Image::by_id($image_id);
|
||||
|
||||
$this->log_in_as_admin();
|
||||
send_event(new ImageInfoSetEvent($image, ["owner" => self::$admin_name]));
|
||||
|
||||
$this->log_in_as_user();
|
||||
$this->get_page("post/view/$image_id");
|
||||
$this->assert_text(self::$admin_name);
|
||||
}
|
||||
}
|
34
ext/post_owner/theme.php
Normal file
34
ext/post_owner/theme.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
use MicroHTML\HTMLElement;
|
||||
|
||||
use function MicroHTML\{TR, TH, TD, emptyHTML, rawHTML, joinHTML, DIV, INPUT, A, TEXTAREA};
|
||||
|
||||
class PostOwnerTheme extends Themelet
|
||||
{
|
||||
public function get_owner_editor_html(Image $image): HTMLElement
|
||||
{
|
||||
global $user;
|
||||
$owner = $image->get_owner()->name;
|
||||
$date = rawHTML(autodate($image->posted));
|
||||
$ip = $user->can(Permissions::VIEW_IP) ? rawHTML(" (" . show_ip($image->owner_ip, "Post posted {$image->posted}") . ")") : "";
|
||||
$info = SHM_POST_INFO(
|
||||
"Uploader",
|
||||
emptyHTML(A(["class" => "username", "href" => make_link("user/$owner")], $owner), $ip, ", ", $date),
|
||||
$user->can(Permissions::EDIT_IMAGE_OWNER) ? INPUT(["type" => "text", "name" => "owner", "value" => $owner]) : null
|
||||
);
|
||||
// SHM_POST_INFO returns a TR, let's sneakily append
|
||||
// a TD with the avatar in it
|
||||
$info->appendChild(
|
||||
TD(
|
||||
["width" => "80px", "rowspan" => "4"],
|
||||
rawHTML($image->get_owner()->get_avatar_html())
|
||||
)
|
||||
);
|
||||
return $info;
|
||||
}
|
||||
}
|
18
ext/post_source/info.php
Normal file
18
ext/post_source/info.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
class PostSourceInfo extends ExtensionInfo
|
||||
{
|
||||
public const KEY = "post_source";
|
||||
|
||||
public string $key = self::KEY;
|
||||
public string $name = "Source Editor";
|
||||
public bool $core = true;
|
||||
public string $url = self::SHIMMIE_URL;
|
||||
public array $authors = self::SHISH_AUTHOR;
|
||||
public ExtensionCategory $category = ExtensionCategory::METADATA;
|
||||
public string $description = "Allow images to have sources assigned to them";
|
||||
}
|
105
ext/post_source/main.php
Normal file
105
ext/post_source/main.php
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
class SourceSetEvent extends Event
|
||||
{
|
||||
public Image $image;
|
||||
public ?string $source;
|
||||
|
||||
public function __construct(Image $image, string $source = null)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->image = $image;
|
||||
$this->source = trim($source);
|
||||
}
|
||||
}
|
||||
|
||||
class PostSource extends Extension
|
||||
{
|
||||
/** @var PostSourceTheme */
|
||||
protected Themelet $theme;
|
||||
|
||||
public function onPageRequest(PageRequestEvent $event): void
|
||||
{
|
||||
global $user, $page;
|
||||
if ($event->page_matches("tag_edit/mass_source_set", method: "POST", permission: Permissions::MASS_TAG_EDIT)) {
|
||||
$this->mass_source_edit($event->req_POST('tags'), $event->req_POST('source'));
|
||||
$page->set_mode(PageMode::REDIRECT);
|
||||
$page->set_redirect(search_link());
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageAddition(ImageAdditionEvent $event): void
|
||||
{
|
||||
if(!empty($event->metadata['source'])) {
|
||||
send_event(new SourceSetEvent($event->image, $event->metadata['source']));
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageInfoSet(ImageInfoSetEvent $event): void
|
||||
{
|
||||
global $page, $user;
|
||||
if ($user->can(Permissions::EDIT_IMAGE_SOURCE) && isset($event->params['source'])) {
|
||||
if (isset($event->params['tags']) ? !preg_match('/source[=|:]/', $event->params["tags"]) : true) {
|
||||
send_event(new SourceSetEvent($event->image, $event->params['source']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onSourceSet(SourceSetEvent $event): void
|
||||
{
|
||||
global $user;
|
||||
if ($user->can(Permissions::EDIT_IMAGE_SOURCE)) {
|
||||
$event->image->set_source($event->source);
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event): void
|
||||
{
|
||||
$event->add_part($this->theme->get_source_editor_html($event->image), 41);
|
||||
}
|
||||
|
||||
public function onTagTermCheck(TagTermCheckEvent $event): void
|
||||
{
|
||||
if (preg_match("/^source[=|:](.*)$/i", $event->term)) {
|
||||
$event->metatag = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function onTagTermParse(TagTermParseEvent $event): void
|
||||
{
|
||||
if (preg_match("/^source[=|:](.*)$/i", $event->term, $matches)) {
|
||||
$source = ($matches[1] !== "none" ? $matches[1] : null);
|
||||
send_event(new SourceSetEvent(Image::by_id($event->image_id), $source));
|
||||
}
|
||||
}
|
||||
|
||||
private function mass_source_edit(string $tags, string $source): void
|
||||
{
|
||||
$tags = Tag::explode($tags);
|
||||
|
||||
$last_id = -1;
|
||||
while (true) {
|
||||
// make sure we don't look at the same images twice.
|
||||
// search returns high-ids first, so we want to look
|
||||
// at images with lower IDs than the previous.
|
||||
$search_forward = $tags;
|
||||
if ($last_id >= 0) {
|
||||
$search_forward[] = "id<$last_id";
|
||||
}
|
||||
|
||||
$images = Search::find_images(limit: 100, tags: $search_forward);
|
||||
if (count($images) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($images as $image) {
|
||||
send_event(new SourceSetEvent($image, $source));
|
||||
$last_id = $image->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
ext/post_source/test.php
Normal file
21
ext/post_source/test.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
class PostSourceTest extends ShimmiePHPUnitTestCase
|
||||
{
|
||||
public function testSourceEdit(): void
|
||||
{
|
||||
$this->log_in_as_user();
|
||||
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
|
||||
$image = Image::by_id($image_id);
|
||||
|
||||
send_event(new ImageInfoSetEvent($image, ["source" => "example.com"]));
|
||||
send_event(new ImageInfoSetEvent($image, ["source" => "http://example.com"]));
|
||||
|
||||
$this->get_page("post/view/$image_id");
|
||||
$this->assert_text("example.com");
|
||||
}
|
||||
}
|
54
ext/post_source/theme.php
Normal file
54
ext/post_source/theme.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
use MicroHTML\HTMLElement;
|
||||
|
||||
use function MicroHTML\{TR, TH, TD, emptyHTML, rawHTML, joinHTML, DIV, INPUT, A, TEXTAREA};
|
||||
|
||||
class PostSourceTheme extends Themelet
|
||||
{
|
||||
public function mss_html(string $terms): string
|
||||
{
|
||||
$h_terms = html_escape($terms);
|
||||
$html = make_form(make_link("tag_edit/mass_source_set")) . "
|
||||
<input type='hidden' name='tags' value='$h_terms'>
|
||||
<input type='text' name='source' value=''>
|
||||
<input type='submit' value='Set Source For All' onclick='return confirm(\"This will mass-edit all sources on the page.\\nAre you sure you want to do this?\")'>
|
||||
</form>
|
||||
";
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function get_source_editor_html(Image $image): HTMLElement
|
||||
{
|
||||
global $user;
|
||||
return SHM_POST_INFO(
|
||||
"Source Link",
|
||||
DIV(
|
||||
["style" => "overflow: hidden; white-space: nowrap; max-width: 350px; text-overflow: ellipsis;"],
|
||||
$this->format_source($image->get_source())
|
||||
),
|
||||
$user->can(Permissions::EDIT_IMAGE_SOURCE) ? INPUT(["type" => "text", "name" => "source", "value" => $image->get_source()]) : null,
|
||||
link: Extension::is_enabled(SourceHistoryInfo::KEY) ? make_link("source_history/{$image->id}") : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function format_source(string $source = null): HTMLElement
|
||||
{
|
||||
if (!empty($source)) {
|
||||
if (!str_contains($source, "://")) {
|
||||
$source = "https://" . $source;
|
||||
}
|
||||
$proto_domain = explode("://", $source);
|
||||
$h_source = $proto_domain[1];
|
||||
if (str_ends_with($h_source, "/")) {
|
||||
$h_source = substr($h_source, 0, -1);
|
||||
}
|
||||
return A(["href" => $source], $h_source);
|
||||
}
|
||||
return rawHTML("Unknown");
|
||||
}
|
||||
}
|
|
@ -4,9 +4,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shimmie2;
|
||||
|
||||
class TagEditInfo extends ExtensionInfo
|
||||
class PostTagsInfo extends ExtensionInfo
|
||||
{
|
||||
public const KEY = "tag_edit";
|
||||
public const KEY = "post_tags";
|
||||
|
||||
public string $key = self::KEY;
|
||||
public string $name = "Tag Editor";
|
|
@ -8,40 +8,6 @@ use Symfony\Component\Console\Command\Command;
|
|||
use Symfony\Component\Console\Input\{InputInterface,InputArgument};
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/*
|
||||
* OwnerSetEvent:
|
||||
* $image_id
|
||||
* $source
|
||||
*
|
||||
*/
|
||||
class OwnerSetEvent extends Event
|
||||
{
|
||||
public Image $image;
|
||||
public User $owner;
|
||||
|
||||
public function __construct(Image $image, User $owner)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->image = $image;
|
||||
$this->owner = $owner;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SourceSetEvent extends Event
|
||||
{
|
||||
public Image $image;
|
||||
public ?string $source;
|
||||
|
||||
public function __construct(Image $image, string $source = null)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->image = $image;
|
||||
$this->source = trim($source);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TagSetException extends UserError
|
||||
{
|
||||
public ?string $redirect;
|
||||
|
@ -94,19 +60,6 @@ class TagSetEvent extends Event
|
|||
}
|
||||
}
|
||||
|
||||
class LockSetEvent extends Event
|
||||
{
|
||||
public Image $image;
|
||||
public bool $locked;
|
||||
|
||||
public function __construct(Image $image, bool $locked)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->image = $image;
|
||||
$this->locked = $locked;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether or not a tag is a meta-tag
|
||||
*/
|
||||
|
@ -138,9 +91,9 @@ class TagTermParseEvent extends Event
|
|||
}
|
||||
}
|
||||
|
||||
class TagEdit extends Extension
|
||||
class PostTags extends Extension
|
||||
{
|
||||
/** @var TagEditTheme */
|
||||
/** @var PostTagsTheme */
|
||||
protected Themelet $theme;
|
||||
|
||||
public function onPageRequest(PageRequestEvent $event): void
|
||||
|
@ -151,11 +104,6 @@ class TagEdit extends Extension
|
|||
$page->set_mode(PageMode::REDIRECT);
|
||||
$page->set_redirect(make_link("admin"));
|
||||
}
|
||||
if ($event->page_matches("tag_edit/mass_source_set", method: "POST", permission: Permissions::MASS_TAG_EDIT)) {
|
||||
$this->mass_source_edit($event->req_POST('tags'), $event->req_POST('source'));
|
||||
$page->set_mode(PageMode::REDIRECT);
|
||||
$page->set_redirect(search_link());
|
||||
}
|
||||
}
|
||||
|
||||
public function onCliGen(CliGenEvent $event): void
|
||||
|
@ -173,41 +121,19 @@ class TagEdit extends Extension
|
|||
});
|
||||
}
|
||||
|
||||
// public function onPostListBuilding(PostListBuildingEvent $event): void
|
||||
// {
|
||||
// global $user;
|
||||
// if ($user->can(UserAbilities::BULK_EDIT_IMAGE_SOURCE) && !empty($event->search_terms)) {
|
||||
// $event->add_control($this->theme->mss_html(Tag::implode($event->search_terms)));
|
||||
// }
|
||||
// }
|
||||
|
||||
public function onImageAddition(ImageAdditionEvent $event): void
|
||||
{
|
||||
if(!empty($event->metadata['tags'])) {
|
||||
send_event(new TagSetEvent($event->image, $event->metadata['tags']));
|
||||
}
|
||||
if(!empty($event->metadata['source'])) {
|
||||
send_event(new SourceSetEvent($event->image, $event->metadata['source']));
|
||||
}
|
||||
if (!empty($event->metadata['locked'])) {
|
||||
send_event(new LockSetEvent($event->image, $event->metadata['locked']));
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageInfoSet(ImageInfoSetEvent $event): void
|
||||
{
|
||||
global $page, $user;
|
||||
if ($user->can(Permissions::EDIT_IMAGE_OWNER) && isset($event->params['tag_edit__owner'])) {
|
||||
$owner = User::by_name($event->params['tag_edit__owner']);
|
||||
if ($owner instanceof User) {
|
||||
send_event(new OwnerSetEvent($event->image, $owner));
|
||||
} else {
|
||||
throw new UserNotFound("Error: No user with that name was found.");
|
||||
}
|
||||
}
|
||||
if ($user->can(Permissions::EDIT_IMAGE_TAG) && isset($event->params['tag_edit__tags'])) {
|
||||
if ($user->can(Permissions::EDIT_IMAGE_TAG) && isset($event->params['tags'])) {
|
||||
try {
|
||||
send_event(new TagSetEvent($event->image, Tag::explode($event->params['tag_edit__tags'])));
|
||||
send_event(new TagSetEvent($event->image, Tag::explode($event->params['tags'])));
|
||||
} catch (TagSetException $e) {
|
||||
if ($e->redirect) {
|
||||
$page->flash("{$e->getMessage()}, please see {$e->redirect}");
|
||||
|
@ -216,23 +142,6 @@ class TagEdit extends Extension
|
|||
}
|
||||
}
|
||||
}
|
||||
if ($user->can(Permissions::EDIT_IMAGE_SOURCE) && isset($event->params['tag_edit__source'])) {
|
||||
if (isset($event->params['tag_edit__tags']) ? !preg_match('/source[=|:]/', $event->params["tag_edit__tags"]) : true) {
|
||||
send_event(new SourceSetEvent($event->image, $event->params['tag_edit__source']));
|
||||
}
|
||||
}
|
||||
if ($user->can(Permissions::EDIT_IMAGE_LOCK)) {
|
||||
$locked = isset($event->params['tag_edit__locked']) && $event->params['tag_edit__locked'] == "on";
|
||||
send_event(new LockSetEvent($event->image, $locked));
|
||||
}
|
||||
}
|
||||
|
||||
public function onOwnerSet(OwnerSetEvent $event): void
|
||||
{
|
||||
global $user;
|
||||
if ($user->can(Permissions::EDIT_IMAGE_OWNER) && (!$event->image->is_locked() || $user->can(Permissions::EDIT_IMAGE_LOCK))) {
|
||||
$event->image->set_owner($event->owner);
|
||||
}
|
||||
}
|
||||
|
||||
public function onTagSet(TagSetEvent $event): void
|
||||
|
@ -245,23 +154,6 @@ class TagEdit extends Extension
|
|||
send_event(new TagTermParseEvent($tag, $event->image->id));
|
||||
}
|
||||
}
|
||||
|
||||
public function onSourceSet(SourceSetEvent $event): void
|
||||
{
|
||||
global $user;
|
||||
if ($user->can(Permissions::EDIT_IMAGE_SOURCE) && (!$event->image->is_locked() || $user->can(Permissions::EDIT_IMAGE_LOCK))) {
|
||||
$event->image->set_source($event->source);
|
||||
}
|
||||
}
|
||||
|
||||
public function onLockSet(LockSetEvent $event): void
|
||||
{
|
||||
global $user;
|
||||
if ($user->can(Permissions::EDIT_IMAGE_LOCK)) {
|
||||
$event->image->set_locked($event->locked);
|
||||
}
|
||||
}
|
||||
|
||||
public function onImageDeletion(ImageDeletionEvent $event): void
|
||||
{
|
||||
$event->image->delete_tags_from_image();
|
||||
|
@ -289,25 +181,7 @@ class TagEdit extends Extension
|
|||
|
||||
public function onImageInfoBoxBuilding(ImageInfoBoxBuildingEvent $event): void
|
||||
{
|
||||
$event->add_part($this->theme->get_user_editor_html($event->image), 39);
|
||||
$event->add_part($this->theme->get_tag_editor_html($event->image), 40);
|
||||
$event->add_part($this->theme->get_source_editor_html($event->image), 41);
|
||||
$event->add_part($this->theme->get_lock_editor_html($event->image), 42);
|
||||
}
|
||||
|
||||
public function onTagTermCheck(TagTermCheckEvent $event): void
|
||||
{
|
||||
if (preg_match("/^source[=|:](.*)$/i", $event->term)) {
|
||||
$event->metatag = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function onTagTermParse(TagTermParseEvent $event): void
|
||||
{
|
||||
if (preg_match("/^source[=|:](.*)$/i", $event->term, $matches)) {
|
||||
$source = ($matches[1] !== "none" ? $matches[1] : null);
|
||||
send_event(new SourceSetEvent(Image::by_id($event->image_id), $source));
|
||||
}
|
||||
}
|
||||
|
||||
public function onParseLinkTemplate(ParseLinkTemplateEvent $event): void
|
||||
|
@ -380,30 +254,4 @@ class TagEdit extends Extension
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function mass_source_edit(string $tags, string $source): void
|
||||
{
|
||||
$tags = Tag::explode($tags);
|
||||
|
||||
$last_id = -1;
|
||||
while (true) {
|
||||
// make sure we don't look at the same images twice.
|
||||
// search returns high-ids first, so we want to look
|
||||
// at images with lower IDs than the previous.
|
||||
$search_forward = $tags;
|
||||
if ($last_id >= 0) {
|
||||
$search_forward[] = "id<$last_id";
|
||||
}
|
||||
|
||||
$images = Search::find_images(limit: 100, tags: $search_forward);
|
||||
if (count($images) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($images as $image) {
|
||||
send_event(new SourceSetEvent($image, $source));
|
||||
$last_id = $image->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Shimmie2;
|
||||
|
||||
class TagEditTest extends ShimmiePHPUnitTestCase
|
||||
class PostTagsTest extends ShimmiePHPUnitTestCase
|
||||
{
|
||||
public function testValidChange(): void
|
||||
{
|
||||
|
@ -46,17 +46,4 @@ class TagEditTest extends ShimmiePHPUnitTestCase
|
|||
$this->get_page("post/view/$image_id");
|
||||
$this->assert_title("Post $image_id: tagme");
|
||||
}
|
||||
|
||||
public function testSourceEdit(): void
|
||||
{
|
||||
$this->log_in_as_user();
|
||||
$image_id = $this->post_image("tests/pbx_screenshot.jpg", "pbx");
|
||||
$image = Image::by_id($image_id);
|
||||
|
||||
send_event(new SourceSetEvent($image, "example.com"));
|
||||
send_event(new SourceSetEvent($image, "http://example.com"));
|
||||
|
||||
$this->get_page("post/view/$image_id");
|
||||
$this->assert_text("example.com");
|
||||
}
|
||||
}
|
56
ext/post_tags/theme.php
Normal file
56
ext/post_tags/theme.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
use MicroHTML\HTMLElement;
|
||||
|
||||
use function MicroHTML\{joinHTML, A, TEXTAREA};
|
||||
|
||||
class PostTagsTheme extends Themelet
|
||||
{
|
||||
public function display_mass_editor(): void
|
||||
{
|
||||
global $page;
|
||||
$html = "
|
||||
" . make_form(make_link("tag_edit/replace")) . "
|
||||
<table class='form'>
|
||||
<tr><th>Search</th><td><input type='text' name='search' class='autocomplete_tags'></tr>
|
||||
<tr><th>Replace</th><td><input type='text' name='replace' class='autocomplete_tags'></td></tr>
|
||||
<tr><td colspan='2'><input type='submit' value='Replace'></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
";
|
||||
$page->add_block(new Block("Mass Tag Edit", $html));
|
||||
}
|
||||
|
||||
public function get_tag_editor_html(Image $image): HTMLElement
|
||||
{
|
||||
global $user;
|
||||
|
||||
$tag_links = [];
|
||||
foreach ($image->get_tag_array() as $tag) {
|
||||
$tag_links[] = A([
|
||||
"href" => search_link([$tag]),
|
||||
"class" => "tag",
|
||||
"title" => "View all posts tagged $tag"
|
||||
], $tag);
|
||||
}
|
||||
|
||||
return SHM_POST_INFO(
|
||||
"Tags",
|
||||
joinHTML(", ", $tag_links),
|
||||
$user->can(Permissions::EDIT_IMAGE_TAG) ? TEXTAREA([
|
||||
"class" => "autocomplete_tags",
|
||||
"type" => "text",
|
||||
"name" => "tags",
|
||||
"id" => "tag_editor",
|
||||
"spellcheck" => "off",
|
||||
], $image->get_tag_list()) : null,
|
||||
link: Extension::is_enabled(TagHistoryInfo::KEY) ?
|
||||
make_link("tag_history/{$image->id}") :
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -56,8 +56,8 @@ class PostTitles extends Extension
|
|||
{
|
||||
global $user;
|
||||
|
||||
if ($user->can(Permissions::EDIT_IMAGE_TITLE) && isset($event->params["post_title"])) {
|
||||
send_event(new PostTitleSetEvent($event->image, $event->params["post_title"]));
|
||||
if ($user->can(Permissions::EDIT_IMAGE_TITLE) && isset($event->params["title"])) {
|
||||
send_event(new PostTitleSetEvent($event->image, $event->params["title"]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ class PostTitlesTheme extends Themelet
|
|||
return SHM_POST_INFO(
|
||||
"Title",
|
||||
$title,
|
||||
$can_set ? INPUT(["type" => "text", "name" => "post_title", "value" => $title]) : null
|
||||
$can_set ? INPUT(["type" => "text", "name" => "title", "value" => $title]) : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,9 +56,9 @@ class Relationships extends Extension
|
|||
{
|
||||
global $user;
|
||||
if ($user->can(Permissions::EDIT_IMAGE_RELATIONSHIPS)) {
|
||||
if (isset($event->params['tag_edit__tags']) ? !preg_match('/parent[=|:]/', $event->params["tag_edit__tags"]) : true) { //Ignore tag_edit__parent if tags contain parent metatag
|
||||
if (isset($event->params["tag_edit__parent"]) ? ctype_digit($event->params["tag_edit__parent"]) : false) {
|
||||
send_event(new ImageRelationshipSetEvent($event->image->id, (int) $event->params["tag_edit__parent"]));
|
||||
if (isset($event->params['tags']) ? !preg_match('/parent[=|:]/', $event->params["tags"]) : true) { //Ignore parent if tags contain parent metatag
|
||||
if (isset($event->params["parent"]) ? ctype_digit($event->params["parent"]) : false) {
|
||||
send_event(new ImageRelationshipSetEvent($event->image->id, (int) $event->params["parent"]));
|
||||
} else {
|
||||
$this->remove_parent($event->image->id);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ class RelationshipsTheme extends Themelet
|
|||
return SHM_POST_INFO(
|
||||
"Parent",
|
||||
strval($image['parent_id']) ?: "None",
|
||||
!$user->is_anonymous() ? INPUT(["type" => "number", "name" => "tag_edit__parent", "value" => $image['parent_id']]) : null
|
||||
!$user->is_anonymous() ? INPUT(["type" => "number", "name" => "parent", "value" => $image['parent_id']]) : null
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shimmie2;
|
||||
|
||||
use MicroHTML\HTMLElement;
|
||||
|
||||
use function MicroHTML\{TR, TH, TD, emptyHTML, rawHTML, joinHTML, DIV, INPUT, A, TEXTAREA};
|
||||
|
||||
class TagEditTheme extends Themelet
|
||||
{
|
||||
/*
|
||||
* Display a form which links to tag_edit/replace with POST[search]
|
||||
* and POST[replace] set appropriately
|
||||
*/
|
||||
public function display_mass_editor(): void
|
||||
{
|
||||
global $page;
|
||||
$html = "
|
||||
" . make_form(make_link("tag_edit/replace")) . "
|
||||
<table class='form'>
|
||||
<tr><th>Search</th><td><input type='text' name='search' class='autocomplete_tags'></tr>
|
||||
<tr><th>Replace</th><td><input type='text' name='replace' class='autocomplete_tags'></td></tr>
|
||||
<tr><td colspan='2'><input type='submit' value='Replace'></td></tr>
|
||||
</table>
|
||||
</form>
|
||||
";
|
||||
$page->add_block(new Block("Mass Tag Edit", $html));
|
||||
}
|
||||
|
||||
public function mss_html(string $terms): string
|
||||
{
|
||||
$h_terms = html_escape($terms);
|
||||
$html = make_form(make_link("tag_edit/mass_source_set")) . "
|
||||
<input type='hidden' name='tags' value='$h_terms'>
|
||||
<input type='text' name='source' value=''>
|
||||
<input type='submit' value='Set Source For All' onclick='return confirm(\"This will mass-edit all sources on the page.\\nAre you sure you want to do this?\")'>
|
||||
</form>
|
||||
";
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function get_tag_editor_html(Image $image): HTMLElement
|
||||
{
|
||||
global $user;
|
||||
|
||||
$tag_links = [];
|
||||
foreach ($image->get_tag_array() as $tag) {
|
||||
$tag_links[] = A([
|
||||
"href" => search_link([$tag]),
|
||||
"class" => "tag",
|
||||
"title" => "View all posts tagged $tag"
|
||||
], $tag);
|
||||
}
|
||||
|
||||
return SHM_POST_INFO(
|
||||
"Tags",
|
||||
joinHTML(", ", $tag_links),
|
||||
$user->can(Permissions::EDIT_IMAGE_TAG) ? TEXTAREA([
|
||||
"class" => "autocomplete_tags",
|
||||
"type" => "text",
|
||||
"name" => "tag_edit__tags",
|
||||
"id" => "tag_editor",
|
||||
"spellcheck" => "off",
|
||||
], $image->get_tag_list()) : null,
|
||||
link: Extension::is_enabled(TagHistoryInfo::KEY) ?
|
||||
make_link("tag_history/{$image->id}") :
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
public function get_user_editor_html(Image $image): HTMLElement
|
||||
{
|
||||
global $user;
|
||||
$owner = $image->get_owner()->name;
|
||||
$date = rawHTML(autodate($image->posted));
|
||||
$ip = $user->can(Permissions::VIEW_IP) ? rawHTML(" (" . show_ip($image->owner_ip, "Post posted {$image->posted}") . ")") : "";
|
||||
$info = SHM_POST_INFO(
|
||||
"Uploader",
|
||||
emptyHTML(A(["class" => "username", "href" => make_link("user/$owner")], $owner), $ip, ", ", $date),
|
||||
$user->can(Permissions::EDIT_IMAGE_OWNER) ? INPUT(["type" => "text", "name" => "tag_edit__owner", "value" => $owner]) : null
|
||||
);
|
||||
// SHM_POST_INFO returns a TR, let's sneakily append
|
||||
// a TD with the avatar in it
|
||||
$info->appendChild(
|
||||
TD(
|
||||
["width" => "80px", "rowspan" => "4"],
|
||||
rawHTML($image->get_owner()->get_avatar_html())
|
||||
)
|
||||
);
|
||||
return $info;
|
||||
}
|
||||
|
||||
public function get_source_editor_html(Image $image): HTMLElement
|
||||
{
|
||||
global $user;
|
||||
return SHM_POST_INFO(
|
||||
"Source Link",
|
||||
DIV(
|
||||
["style" => "overflow: hidden; white-space: nowrap; max-width: 350px; text-overflow: ellipsis;"],
|
||||
$this->format_source($image->get_source())
|
||||
),
|
||||
$user->can(Permissions::EDIT_IMAGE_SOURCE) ? INPUT(["type" => "text", "name" => "tag_edit__source", "value" => $image->get_source()]) : null,
|
||||
link: Extension::is_enabled(SourceHistoryInfo::KEY) ? make_link("source_history/{$image->id}") : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected function format_source(string $source = null): HTMLElement
|
||||
{
|
||||
if (!empty($source)) {
|
||||
if (!str_contains($source, "://")) {
|
||||
$source = "https://" . $source;
|
||||
}
|
||||
$proto_domain = explode("://", $source);
|
||||
$h_source = $proto_domain[1];
|
||||
if (str_ends_with($h_source, "/")) {
|
||||
$h_source = substr($h_source, 0, -1);
|
||||
}
|
||||
return A(["href" => $source], $h_source);
|
||||
}
|
||||
return rawHTML("Unknown");
|
||||
}
|
||||
|
||||
public function get_lock_editor_html(Image $image): HTMLElement
|
||||
{
|
||||
global $user;
|
||||
return SHM_POST_INFO(
|
||||
"Locked",
|
||||
$image->is_locked() ? "Yes (Only admins may edit these details)" : "No",
|
||||
$user->can(Permissions::EDIT_IMAGE_LOCK) ? INPUT(["type" => "checkbox", "name" => "tag_edit__locked", "checked" => $image->is_locked()]) : null
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,11 +7,11 @@ namespace Shimmie2;
|
|||
class ImageInfoSetEvent extends Event
|
||||
{
|
||||
public Image $image;
|
||||
/** @var array<string, mixed> */
|
||||
/** @var array<string, string> */
|
||||
public array $params;
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $params
|
||||
* @param array<string, string> $params
|
||||
*/
|
||||
public function __construct(Image $image, array $params)
|
||||
{
|
||||
|
|
|
@ -75,7 +75,12 @@ class ViewPost extends Extension
|
|||
$image_id = int_escape($event->req_POST('image_id'));
|
||||
$image = Image::by_id($image_id);
|
||||
if (!$image->is_locked() || $user->can(Permissions::EDIT_IMAGE_LOCK)) {
|
||||
send_event(new ImageInfoSetEvent($image, $event->POST));
|
||||
// currently all post metadata is string => string - in the future
|
||||
// we might want to have a more complex type system, but for now
|
||||
// we just filter out non-string keys
|
||||
/** @var array<string, string> */
|
||||
$kvs = array_filter($event->POST, 'is_string', ARRAY_FILTER_USE_KEY);
|
||||
send_event(new ImageInfoSetEvent($image, $kvs));
|
||||
$page->set_mode(PageMode::REDIRECT);
|
||||
|
||||
if ($event->get_GET('search')) {
|
||||
|
|
Reference in a new issue