280 lines
8.8 KiB
PHP
280 lines
8.8 KiB
PHP
<?php
|
|
/*
|
|
* Name: Bulk Actions
|
|
* Author: Matthew Barbour
|
|
* License: WTFPL
|
|
* Description: Provides query and selection-based bulk action support
|
|
* Documentation: Provides bulk action section in list view. Allows performing actions against a set of images based on query or manual selection.
|
|
* Based on Mass Tagger by Christian Walde <walde.christian@googlemail.com>, contributions by Shish and Agasa.
|
|
*/
|
|
|
|
|
|
class BulkActionBlockBuildingEvent extends Event
|
|
{
|
|
/** @var array */
|
|
public $actions = [];
|
|
|
|
public $search_terms = [];
|
|
|
|
public function add_action(String $action, string $button_text, string $access_key = null, String $confirmation_message = "", String $block = "", int $position = 40)
|
|
{
|
|
if ($block == null) {
|
|
$block = "";
|
|
}
|
|
|
|
if(!empty($access_key)) {
|
|
assert(strlen($access_key)==1);
|
|
foreach ($this->actions as $existing) {
|
|
if($existing["access_key"]==$access_key) {
|
|
throw new SCoreException("Access key $access_key is already in use");
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->actions[] =[
|
|
"block" => $block,
|
|
"access_key" => $access_key,
|
|
"confirmation_message" => $confirmation_message,
|
|
"action" => $action,
|
|
"button_text" => $button_text,
|
|
"position" => $position
|
|
];
|
|
}
|
|
}
|
|
|
|
class BulkActionEvent extends Event
|
|
{
|
|
/** @var string */
|
|
public $action;
|
|
/** @var array */
|
|
public $items;
|
|
/** @var PageRequestEvent */
|
|
public $page_request;
|
|
|
|
public function __construct(String $action, PageRequestEvent $pageRequestEvent, Generator $items)
|
|
{
|
|
$this->action = $action;
|
|
$this->page_request = $pageRequestEvent;
|
|
$this->items = $items;
|
|
}
|
|
}
|
|
|
|
class BulkActions extends Extension
|
|
{
|
|
public function onPostListBuilding(PostListBuildingEvent $event)
|
|
{
|
|
global $config, $page, $user;
|
|
|
|
if ($user->is_logged_in()) {
|
|
$babbe = new BulkActionBlockBuildingEvent();
|
|
$babbe->search_terms = $event->search_terms;
|
|
|
|
send_event($babbe);
|
|
|
|
if (sizeof($babbe->actions) == 0) {
|
|
return;
|
|
}
|
|
|
|
usort($babbe->actions, [$this, "sort_blocks"]);
|
|
|
|
$this->theme->display_selector($page, $babbe->actions, Tag::implode($event->search_terms));
|
|
}
|
|
}
|
|
|
|
public function onBulkActionBlockBuilding(BulkActionBlockBuildingEvent $event)
|
|
{
|
|
global $user;
|
|
|
|
if ($user->can(Permissions::DELETE_IMAGE)) {
|
|
$event->add_action("bulk_delete", "(D)elete", "d", "Delete selected images?", $this->theme->render_ban_reason_input(), 10);
|
|
}
|
|
|
|
if ($user->can(Permissions::BULK_EDIT_IMAGE_TAG)) {
|
|
|
|
$event->add_action(
|
|
"bulk_tag",
|
|
"Tag",
|
|
"t",
|
|
"",
|
|
$this->theme->render_tag_input(),
|
|
10);
|
|
}
|
|
|
|
if ($user->can(Permissions::BULK_EDIT_IMAGE_SOURCE)) {
|
|
$event->add_action("bulk_source", "Set (S)ource", "s","", $this->theme->render_source_input(), 10);
|
|
}
|
|
}
|
|
|
|
public function onBulkAction(BulkActionEvent $event)
|
|
{
|
|
global $user;
|
|
|
|
switch ($event->action) {
|
|
case "bulk_delete":
|
|
if ($user->can(Permissions::DELETE_IMAGE)) {
|
|
$i = $this->delete_items($event->items);
|
|
flash_message("Deleted $i items");
|
|
}
|
|
break;
|
|
case "bulk_tag":
|
|
if (!isset($_POST['bulk_tags'])) {
|
|
return;
|
|
}
|
|
if ($user->can(Permissions::BULK_EDIT_IMAGE_TAG)) {
|
|
$tags = $_POST['bulk_tags'];
|
|
$replace = false;
|
|
if (isset($_POST['bulk_tags_replace']) && $_POST['bulk_tags_replace'] == "true") {
|
|
$replace = true;
|
|
}
|
|
|
|
$i= $this->tag_items($event->items, $tags, $replace);
|
|
flash_message("Tagged $i items");
|
|
}
|
|
break;
|
|
case "bulk_source":
|
|
if (!isset($_POST['bulk_source'])) {
|
|
return;
|
|
}
|
|
if ($user->can(Permissions::BULK_EDIT_IMAGE_SOURCE)) {
|
|
$source = $_POST['bulk_source'];
|
|
$i = $this->set_source($event->items, $source);
|
|
flash_message("Set source for $i items");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
public function onPageRequest(PageRequestEvent $event)
|
|
{
|
|
global $page, $user;
|
|
if ($event->page_matches("bulk_action") && $user->can(Permissions::PERFORM_BULK_ACTIONS)) {
|
|
if (!isset($_POST['bulk_action'])) {
|
|
return;
|
|
}
|
|
|
|
$action = $_POST['bulk_action'];
|
|
|
|
$items = null;
|
|
if (isset($_POST['bulk_selected_ids']) && $_POST['bulk_selected_ids'] != "") {
|
|
$data = json_decode($_POST['bulk_selected_ids']);
|
|
if (is_array($data)&&!empty($data)) {
|
|
$items = $this->yield_items($data);
|
|
}
|
|
} elseif (isset($_POST['bulk_query']) && $_POST['bulk_query'] != "") {
|
|
$query = $_POST['bulk_query'];
|
|
if ($query != null && $query != "") {
|
|
$items = $this->yield_search_results($query);
|
|
}
|
|
}
|
|
|
|
if (is_iterable($items)) {
|
|
$newEvent = new BulkActionEvent($action, $event, $items);
|
|
send_event($newEvent);
|
|
}
|
|
|
|
|
|
$page->set_mode(PageMode::REDIRECT);
|
|
if (!isset($_SERVER['HTTP_REFERER'])) {
|
|
$_SERVER['HTTP_REFERER'] = make_link();
|
|
}
|
|
$page->set_redirect($_SERVER['HTTP_REFERER']);
|
|
}
|
|
}
|
|
|
|
private function yield_items(array $data): Generator
|
|
{
|
|
foreach ($data as $id) {
|
|
if (is_numeric($id)) {
|
|
$image = Image::by_id($id);
|
|
if($image!=null) {
|
|
yield $image;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private function yield_search_results(string $query): Generator
|
|
{
|
|
$tags = Tag::explode($query);
|
|
return Image::find_images_iterable(0, null, $tags);
|
|
}
|
|
|
|
private function sort_blocks($a, $b)
|
|
{
|
|
return $a["position"] - $b["position"];
|
|
}
|
|
|
|
private function delete_items(iterable $items): int
|
|
{
|
|
$total = 0;
|
|
foreach ($items as $image) {
|
|
try {
|
|
if (class_exists("ImageBan") && isset($_POST['bulk_ban_reason'])) {
|
|
$reason = $_POST['bulk_ban_reason'];
|
|
if ($reason) {
|
|
send_event(new AddImageHashBanEvent($image->hash, $reason));
|
|
}
|
|
}
|
|
send_event(new ImageDeletionEvent($image));
|
|
$total++;
|
|
} catch (Exception $e) {
|
|
flash_message("Error while removing {$image->id}: " . $e->getMessage(), "error");
|
|
}
|
|
}
|
|
return $total;
|
|
}
|
|
|
|
private function tag_items(iterable $items, string $tags, bool $replace): int
|
|
{
|
|
$tags = Tag::explode($tags);
|
|
|
|
$pos_tag_array = [];
|
|
$neg_tag_array = [];
|
|
foreach ($tags as $new_tag) {
|
|
if (strpos($new_tag, '-') === 0) {
|
|
$neg_tag_array[] = substr($new_tag, 1);
|
|
} else {
|
|
$pos_tag_array[] = $new_tag;
|
|
}
|
|
}
|
|
|
|
$total = 0;
|
|
if ($replace) {
|
|
foreach ($items as $image) {
|
|
send_event(new TagSetEvent($image, $tags));
|
|
$total++;
|
|
}
|
|
} else {
|
|
foreach ($items as $image) {
|
|
$img_tags = array_map("strtolower",$image->get_tag_array());
|
|
|
|
if (!empty($neg_tag_array)) {
|
|
$neg_tag_array = array_map("strtolower",$neg_tag_array);
|
|
|
|
$img_tags = array_merge($pos_tag_array, $img_tags);
|
|
$img_tags = array_diff($img_tags, $neg_tag_array);
|
|
} else {
|
|
$img_tags = array_merge($tags, $img_tags);
|
|
}
|
|
send_event(new TagSetEvent($image, $img_tags));
|
|
$total++;
|
|
}
|
|
}
|
|
|
|
return $total;
|
|
}
|
|
|
|
private function set_source(iterable $items, String $source): int
|
|
{
|
|
$total = 0;
|
|
foreach ($items as $image) {
|
|
try {
|
|
send_event(new SourceSetEvent($image, $source));
|
|
$total++;
|
|
} catch (Exception $e) {
|
|
flash_message("Error while setting source for {$image->id}: " . $e->getMessage(), "error");
|
|
}
|
|
}
|
|
return $total;
|
|
}
|
|
}
|