[upload] separate out DataUpload, ImageAddition, and TagSet/SourceSet/LockSet/etc

This commit is contained in:
Shish 2024-01-05 13:53:21 +00:00
parent aa494efe45
commit af309601f5
23 changed files with 140 additions and 169 deletions

View file

@ -321,31 +321,32 @@ abstract class DataHandlerExtension extends Extension
throw new UploadException("The uploaded post is the same as the one to replace.");
}
// even more hax..
$event->metadata['tags'] = $existing->get_tag_list();
$image = $this->create_image_from_data(warehouse_path(Image::IMAGE_DIR, $event->hash), $event->metadata);
$image->posted = $existing->posted;
send_event(new ImageReplaceEvent($event->replace_id, $image));
$_id = $event->replace_id;
assert(!is_null($_id));
$event->image_id = $_id;
$replacement = $this->create_image_from_data(warehouse_path(Image::IMAGE_DIR, $event->hash), $event->metadata);
send_event(new ImageReplaceEvent($existing, $replacement));
$event->images[] = $replacement;
if(!empty($event->metadata['source'])) {
send_event(new SourceSetEvent($existing, $event->metadata['source']));
}
} else {
$image = $this->create_image_from_data(warehouse_path(Image::IMAGE_DIR, $event->hash), $event->metadata);
$iae = send_event(new ImageAdditionEvent($image));
$event->image_id = $iae->image->id;
$event->images[] = $iae->image;
$event->merged = $iae->merged;
// Rating Stuff.
if (!empty($event->metadata['rating'])) {
$rating = $event->metadata['rating'];
send_event(new RatingSetEvent($image, $rating));
if(!empty($event->metadata['tags'])) {
if($iae->merged) {
$event->metadata['tags'] = array_merge($iae->image->get_tag_array(), $event->metadata['tags']);
}
send_event(new TagSetEvent($image, $event->metadata['tags']));
}
if(!empty($event->metadata['source'])) {
send_event(new SourceSetEvent($image, $event->metadata['source']));
}
if (!empty($event->metadata['rating'])) {
send_event(new RatingSetEvent($image, $event->metadata['rating']));
}
// Locked Stuff.
if (!empty($event->metadata['locked'])) {
$locked = $event->metadata['locked'];
send_event(new LockSetEvent($image, $locked));
send_event(new LockSetEvent($image, $event->metadata['locked']));
}
}
} elseif ($supported_mime && !$check_contents) {
@ -401,8 +402,6 @@ abstract class DataHandlerExtension extends Extension
$image->hash = md5_file($filename);
$image->filename = (($pos = strpos($metadata['filename'], '?')) !== false) ? substr($metadata['filename'], 0, $pos) : $metadata['filename'];
$image->set_mime(MimeType::get_for_file($filename, get_file_ext($metadata["filename"]) ?? null));
$image->tag_array = is_array($metadata['tags']) ? $metadata['tags'] : Tag::explode($metadata['tags']);
$image->source = $metadata['source'];
if (empty($image->get_mime())) {
throw new UploadException("Unable to determine MIME for $filename");

View file

@ -60,8 +60,8 @@ class ImageReplaceEvent extends Event
* the old image file and thumbnail from the disk.
*/
public function __construct(
public int $id,
public Image $image
public Image $original,
public Image $replacement
) {
parent::__construct();
}

View file

@ -46,7 +46,7 @@ class Image
#[Field]
public ?string $posted = null;
#[Field]
public ?string $source;
public ?string $source = null;
#[Field]
public bool $locked = false;
public ?bool $lossless = null;
@ -433,7 +433,7 @@ class Image
if (is_null($m)) {
$m = MimeMap::get_for_extension($this->ext)[0];
}
return $m;
return strtolower($m);
}
/**

View file

@ -12,7 +12,7 @@ namespace Shimmie2;
* Add a directory full of images
*
* @param string $base
* @return array
* @return UploadResult[]
*/
function add_dir(string $base, ?array $extra_tags = []): array
{
@ -23,14 +23,14 @@ function add_dir(string $base, ?array $extra_tags = []): array
$filename = basename($full_path);
$tags = array_merge(path_to_tags($short_path), $extra_tags);
$result = "$short_path (".implode(", ", $tags).")... ";
try {
add_image($full_path, $filename, $tags);
$result .= "ok";
$dae = add_image($full_path, $filename, $tags);
foreach($dae->images as $image) {
$results[] = new UploadSuccess($filename, $image->id);
}
} catch (UploadException $ex) {
$result .= "failed: ".$ex->getMessage();
$results[] = new UploadError($filename, $ex->getMessage());
}
$results[] = $result;
}
return $results;

View file

@ -224,8 +224,8 @@ if(class_exists("\\PHPUnit\\Framework\\TestCase")) {
"tags" => Tag::explode($tags),
"source" => null,
]));
// if($dae->image_id == -1) throw new \Exception("Upload failed :(");
return $dae->image_id;
if(count($dae->images) == 0) throw new \Exception("Upload failed :(");
return $dae->images[0]->id;
}
protected function delete_image(int $image_id): void

View file

@ -7,6 +7,7 @@ namespace Shimmie2;
class BulkAddEvent extends Event
{
public string $dir;
/** @var UploadResult[] */
public array $results;
public function __construct(string $dir)
@ -29,10 +30,7 @@ class BulkAdd extends Extension
if ($user->can(Permissions::BULK_ADD) && $user->check_auth_token() && isset($_POST['dir'])) {
shm_set_timeout(null);
$bae = send_event(new BulkAddEvent($_POST['dir']));
foreach ($bae->results as $result) {
$this->theme->add_status("Adding files", $result);
}
$this->theme->display_upload_results($page);
$this->theme->display_upload_results($page, $bae->results);
}
}
}
@ -46,6 +44,13 @@ class BulkAdd extends Extension
if ($event->cmd == "bulk-add") {
if (count($event->args) == 1) {
$bae = send_event(new BulkAddEvent($event->args[0]));
foreach ($bae->results as $r) {
if(is_a($r, UploadError::class)) {
print($r->name." failed: ".$r->error."\n");
} else {
print($r->name." ok\n");
}
}
print(implode("\n", $bae->results));
}
}
@ -61,8 +66,7 @@ class BulkAdd extends Extension
if (is_dir($event->dir) && is_readable($event->dir)) {
$event->results = add_dir($event->dir);
} else {
$h_dir = html_escape($event->dir);
$event->results[] = "Error, $h_dir is not a readable directory";
$event->results = [new UploadError($event->dir, "is not a readable directory")];
}
}
}

View file

@ -10,11 +10,7 @@ class BulkAddTest extends ShimmiePHPUnitTestCase
{
send_event(new UserLoginEvent(User::by_name(self::$admin_name)));
$bae = send_event(new BulkAddEvent('asdf'));
$this->assertContainsEquals(
"Error, asdf is not a readable directory",
$bae->results,
implode("\n", $bae->results)
);
$this->assertTrue(is_a($bae->results[0], UploadError::class));
}
public function testValidDir()

View file

@ -4,21 +4,27 @@ declare(strict_types=1);
namespace Shimmie2;
use function MicroHTML\{UL, LI};
class BulkAddTheme extends Themelet
{
private array $messages = [];
/*
/**
* Show a standard page for results to be put into
*
* @param UploadResult[] $results
*/
public function display_upload_results(Page $page)
public function display_upload_results(Page $page, array $results)
{
$page->set_title("Adding folder");
$page->set_heading("Adding folder");
$page->add_block(new NavBlock());
$html = "";
foreach ($this->messages as $block) {
$html .= "<br/>" . $block->body;
$html = UL();
foreach ($results as $r) {
if (is_a($r, UploadError::class)) {
$html->appendChild(LI("{$r->name} failed: {$r->error}"));
} else {
$html->appendChild(LI("{$r->name} ok"));
}
}
$page->add_block(new Block("Results", $html));
}
@ -46,9 +52,4 @@ class BulkAddTheme extends Themelet
";
$page->add_block(new Block("Bulk Add", $html));
}
public function add_status($title, $body)
{
$this->messages[] = new Block($title, $body);
}
}

View file

@ -51,11 +51,13 @@ class BulkAddCSV extends Extension
private function add_image(string $tmpname, string $filename, array $tags, string $source, string $rating, string $thumbfile)
{
$event = add_image($tmpname, $filename, $tags, $source);
if ($event->image_id == -1) {
if (count($event->images) == 0) {
throw new UploadException("File type not recognised");
} else {
if (class_exists("Shimmie2\RatingSetEvent") && in_array($rating, ["s", "q", "e"])) {
send_event(new RatingSetEvent(Image::by_id($event->image_id), $rating));
foreach($event->images as $image) {
send_event(new RatingSetEvent($image, $rating));
}
}
if (file_exists($thumbfile)) {
copy($thumbfile, warehouse_path(Image::THUMBNAIL_DIR, $event->hash));

View file

@ -52,23 +52,19 @@ class BulkImportExport extends DataHandlerExtension
file_put_contents($tmpfile, $stream);
$id = add_image($tmpfile, $item->filename, $item->tags)->image_id;
$images = add_image($tmpfile, $item->filename, $item->tags)->images;
if ($id == -1) {
if (count($images) == 0) {
throw new SCoreException("Unable to import file $item->hash");
}
$image = Image::by_id($id);
if ($image == null) {
throw new SCoreException("Unable to import file $item->hash");
foreach ($images as $image) {
$event->images[] = $image;
if ($item->source != null) {
$image->set_source($item->source);
}
send_event(new BulkImportEvent($image, $item));
}
if ($item->source != null) {
$image->set_source($item->source);
}
send_event(new BulkImportEvent($image, $item));
$database->commit();
$total++;
} catch (\Exception $ex) {
@ -86,7 +82,6 @@ class BulkImportExport extends DataHandlerExtension
}
}
}
$event->image_id = -2; // default -1 = upload wasn't handled
log_info(
BulkImportExportInfo::KEY,

View file

@ -469,12 +469,12 @@ class CronUploader extends Extension
$event = add_image($tmpname, $filename, $tags, null);
// Generate info message
if ($event->image_id == -1) {
if (count($event->images) == 0) {
throw new UploadException("File type not recognised (".$event->mime."). Filename: {$filename}");
} elseif ($event->merged === true) {
$infomsg = "Post merged. ID: {$event->image_id} - Filename: {$filename}";
$infomsg = "Post merged. ID: {$event->images[0]->id} - Filename: {$filename}";
} else {
$infomsg = "Post uploaded. ID: {$event->image_id} - Filename: {$filename}";
$infomsg = "Post uploaded. ID: {$event->images[0]->id} - Filename: {$filename}";
}
$this->log_message(SCORE_LOG_INFO, $infomsg);

View file

@ -170,7 +170,7 @@ class GraphQL extends Extension
'source' => $source,
]));
return ["image_id" => $event->image_id];
return ["image_ids" => array_map(fn($im) => $im->id, $event->images)];
}
public function onCommand(CommandEvent $event)

View file

@ -35,13 +35,17 @@ class ArchiveFileHandler extends DataHandlerExtension
if (file_exists($tmpdir)) {
try {
$results = add_dir($tmpdir, $event->metadata['tags']);
if (count($results) > 0) {
$page->flash("Adding files" . implode("\n", $results));
foreach ($results as $r) {
if(is_a($r, UploadError::class)) {
$page->flash($r->name." failed: ".$r->error);
}
if(is_a($r, UploadSuccess::class)) {
$event->images[] = Image::by_id($r->image_id);
}
}
} finally {
deltree($tmpdir);
}
$event->image_id = -2; // default -1 = upload wasn't handled
}
}
}

View file

@ -129,13 +129,6 @@ class ImageIO extends Extension
try {
$image = $event->image;
/*
* Validate things
*/
if (strlen(trim($image->source ?? '')) == 0) {
$image->source = null;
}
/*
* Check for an existing image
*/
@ -143,18 +136,8 @@ class ImageIO extends Extension
if (!is_null($existing)) {
$handler = $config->get_string(ImageConfig::UPLOAD_COLLISION_HANDLER);
if ($handler == ImageConfig::COLLISION_MERGE || isset($_GET['update'])) {
$merged = array_merge($image->get_tag_array(), $existing->get_tag_array());
send_event(new TagSetEvent($existing, $merged));
if (isset($_GET['rating']) && isset($_GET['update']) && Extension::is_enabled(RatingsInfo::KEY)) {
send_event(new RatingSetEvent($existing, $_GET['rating']));
}
if (isset($_GET['source']) && isset($_GET['update'])) {
send_event(new SourceSetEvent($existing, $_GET['source']));
}
$event->merged = true;
$im = Image::by_id($existing->id);
assert(!is_null($image));
$event->image = $im;
$event->image = $existing;
return;
} else {
throw new ImageAdditionException(">>{$existing->id} already has hash {$image->hash}");
@ -165,18 +148,6 @@ class ImageIO extends Extension
$image->save_to_db();
log_info("image", "Uploaded >>{$image->id} ({$image->hash})");
# at this point in time, the image's tags haven't really been set,
# and so, having $image->tag_array set to something is a lie (but
# a useful one, as we want to know what the tags are /supposed/ to
# be). Here we correct the lie, by first nullifying the wrong tags
# then using the standard mechanism to set them properly.
$tags_to_set = $image->get_tag_array();
$image->tag_array = [];
send_event(new TagSetEvent($image, $tags_to_set));
if ($image->source) {
send_event(new SourceSetEvent($image, $image->source));
}
} catch (ImageAdditionException $e) {
throw new UploadException($e->error);
}
@ -202,34 +173,27 @@ class ImageIO extends Extension
public function onImageReplace(ImageReplaceEvent $event)
{
try {
$id = $event->id;
$image = $event->image;
$original = $event->original;
$replacement = $event->replacement;
$image->set_mime(
MimeType::get_for_file($image->get_image_filename())
try {
$duplicate = Image::by_hash($replacement->hash);
if (!is_null($duplicate) && $duplicate->id != $original->id) {
throw new ImageReplaceException(">>{$duplicate->id} already has hash {$replacement->hash}");
}
$replacement->set_mime(
MimeType::get_for_file($replacement->get_image_filename())
);
/* Check to make sure the image exists. */
$existing = Image::by_id($id);
if (is_null($existing)) {
throw new ImageReplaceException("Post to replace does not exist!");
}
$duplicate = Image::by_hash($image->hash);
if (!is_null($duplicate) && $duplicate->id != $id) {
throw new ImageReplaceException(">>{$duplicate->id} already has hash {$image->hash}");
}
if (strlen(trim($image->source ?? '')) == 0) {
$image->source = $existing->get_source();
}
// Update the data in the database.
$image->id = $id;
send_event(new MediaCheckPropertiesEvent($image));
$image->save_to_db();
if (empty($replacement->source)) {
$replacement->source = $original->get_source();
}
$replacement->posted = $original->posted;
$replacement->id = $original->id;
send_event(new MediaCheckPropertiesEvent($replacement));
$replacement->save_to_db();
/*
This step could be optional, ie: perhaps move the image somewhere
@ -237,13 +201,13 @@ class ImageIO extends Extension
inspected later by an admin?
*/
log_debug("image", "Removing image with hash " . $existing->hash);
$existing->remove_image_only(); // Actually delete the old image file from disk
log_debug("image", "Removing image with hash " . $original->hash);
$original->remove_image_only(); // Actually delete the old image file from disk
/* Generate new thumbnail */
send_event(new ThumbnailGenerationEvent($image->hash, strtolower($image->get_mime())));
send_event(new ThumbnailGenerationEvent($replacement->hash, $replacement->get_mime()));
log_info("image", "Replaced >>{$id} with ({$image->hash})");
log_info("image", "Replaced >>{$original->id} with ({$replacement->hash})");
} catch (ImageReplaceException $e) {
throw new UploadException($e->error);
}

View file

@ -52,11 +52,6 @@ class NotATag extends Extension
}
}
public function onImageAddition(ImageAdditionEvent $event)
{
$this->scan($event->image->get_tag_array());
}
public function onTagSet(TagSetEvent $event)
{
global $user;

View file

@ -77,10 +77,9 @@ class ResizeImage extends Extension
{
global $config, $page;
$image_obj = Image::by_id($event->image_id);
if ($config->get_bool(ResizeConfig::UPLOAD) == true
&& $this->can_resize_mime($event->mime)) {
$image_obj = $event->images[0];
$width = $height = 0;
if ($config->get_int(ResizeConfig::DEFAULT_WIDTH) !== 0) {
@ -109,10 +108,10 @@ class ResizeImage extends Extension
//Need to generate thumbnail again...
//This only seems to be an issue if one of the sizes was set to 0.
$image_obj = Image::by_id($event->image_id); //Must be a better way to grab the new hash than setting this again..
$image_obj = Image::by_id($image_obj->id); //Must be a better way to grab the new hash than setting this again..
send_event(new ThumbnailGenerationEvent($image_obj->hash, $image_obj->get_mime(), true));
log_info("resize", ">>{$event->image_id} has been resized to: ".$width."x".$height);
log_info("resize", ">>{$image_obj->id} has been resized to: ".$width."x".$height);
//TODO: Notify user that image has been resized.
}
}
@ -280,7 +279,7 @@ class ResizeImage extends Extension
/* Remove temporary file */
@unlink($tmp_filename);
send_event(new ImageReplaceEvent($image_obj->id, $new_image));
send_event(new ImageReplaceEvent($image_obj, $new_image));
log_info("resize", "Resized >>{$image_obj->id} - New hash: {$new_image->hash}");
}

View file

@ -195,7 +195,7 @@ class RotateImage extends Extension
/* Remove temporary file */
@unlink($tmp_filename);
send_event(new ImageReplaceEvent($image_id, $new_image));
send_event(new ImageReplaceEvent($image_obj, $new_image));
log_info("rotate", "Rotated >>{$image_id} - New hash: {$new_image->hash}");
}

View file

@ -54,16 +54,13 @@ class S3 extends Extension
public function onImageAddition(ImageAdditionEvent $event)
{
$this->sync_post($event->image);
// Tags aren't set at this point, let's wait for the TagSetEvent
// $this->sync_post($event->image);
}
public function onTagSet(TagSetEvent $event)
{
// pretend that tags were set already so that sync works
$orig_tags = $event->image->tag_array;
$event->image->tag_array = $event->tags;
$this->sync_post($event->image);
$event->image->tag_array = $orig_tags;
$this->sync_post($event->image, $event->tags);
}
public function onImageDeletion(ImageDeletionEvent $event)
@ -73,9 +70,8 @@ class S3 extends Extension
public function onImageReplace(ImageReplaceEvent $event)
{
$existing = Image::by_id($event->id);
$this->remove_file($existing->hash);
$this->sync_post($event->image);
$this->remove_file($event->original->hash);
$this->sync_post($event->replacement, $event->original->get_tag_array());
}
// utils
@ -105,7 +101,7 @@ class S3 extends Extension
}
// underlying s3 interaction functions
private function sync_post(Image $image)
private function sync_post(Image $image, ?array $new_tags = null)
{
global $config;
@ -121,7 +117,17 @@ class S3 extends Extension
return;
}
$image_bucket = $config->get_string(S3Config::IMAGE_BUCKET);
$friendly = $image->parse_link_template('$id - $tags.$ext');
if(is_null($new_tags)) {
$friendly = $image->parse_link_template('$id - $tags.$ext');
}
else {
$_orig_tags = $image->get_tag_array();
$image->tag_array = $new_tags;
$friendly = $image->parse_link_template('$id - $tags.$ext');
$image->tag_array = $_orig_tags;
}
$client->putObject([
'Bucket' => $image_bucket,
'Key' => $this->hash_to_path($image->hash),

View file

@ -375,7 +375,7 @@ class TranscodeImage extends Extension
/* Remove temporary file */
@unlink($tmp_filename);
send_event(new ImageReplaceEvent($image_obj->id, $new_image));
send_event(new ImageReplaceEvent($image_obj, $new_image));
return $new_image;
}

View file

@ -233,7 +233,7 @@ class TranscodeVideo extends Extension
throw new VideoTranscodeException("Failed to copy new post file from temporary location ({$tmp_filename}) to archive ($target)");
}
send_event(new ImageReplaceEvent($image->id, $new_image));
send_event(new ImageReplaceEvent($image, $new_image));
return $new_image;
} finally {

View file

@ -15,7 +15,8 @@ class DataUploadEvent extends Event
public string $mime;
public int $size;
public int $image_id = -1;
/** @var Image[] */
public array $images = [];
public bool $handled = false;
public bool $merged = false;
@ -377,11 +378,12 @@ class Upload extends Extension
$event = new DataUploadEvent($tmp_name, $metadata, $replace_id);
send_event($event);
if ($event->image_id == -1) {
if (count($event->images) == 0) {
throw new UploadException("MIME type not supported: " . $event->mime);
}
$results[] = new UploadSuccess($name, $event->image_id);
$page->add_http_header("X-Shimmie-Post-ID: " . $event->image_id);
foreach($event->images as $image) {
$results[] = new UploadSuccess($name, $image->id);
}
} catch (UploadException $ex) {
$results[] = new UploadError($name, $ex->getMessage());
}
@ -431,10 +433,12 @@ class Upload extends Extension
// Upload file
$event = new DataUploadEvent($tmp_filename, $metadata, $replace_id);
send_event($event);
if ($event->image_id == -1) {
if (count($event->images) == 0) {
throw new UploadException("File type not supported: " . $event->mime);
}
$results[] = new UploadSuccess($url, $event->image_id);
foreach($event->images as $image) {
$results[] = new UploadSuccess($url, $image->id);
}
} catch (UploadException $ex) {
$results[] = new UploadError($url, $ex->getMessage());
} finally {

View file

@ -45,7 +45,7 @@ class UploadTest extends ShimmiePHPUnitTestCase
];
$page = $this->post_page("upload", ["tags0" => "foo bar"]);
$this->assert_response(302);
$this->assertStringStartsWith("/test/post/list/poster%3Dtest/1", $page->redirect);
$this->assertStringStartsWith("/test/post/list/id%3D4%2C3%2C2%2C1/1", $page->redirect);
$this->assertEquals(4, $database->get_one("SELECT COUNT(*) FROM images"));
}
@ -105,8 +105,8 @@ class UploadTest extends ShimmiePHPUnitTestCase
public function testRejectUnknownFiletype()
{
$image_id = $this->post_image("index.php", "test");
$this->assertEquals(-1, $image_id); // no file handler claimed this
$this->expectException(\Exception::class);
$this->post_image("index.php", "test");
}
public function testRejectHuge()

View file

@ -295,9 +295,11 @@ class UploadTheme extends Themelet
} elseif (count($successes) == 1) {
$page->set_mode(PageMode::REDIRECT);
$page->set_redirect(make_link("post/view/{$successes[0]->image_id}"));
$page->add_http_header("X-Shimmie-Post-ID: " . $successes[0]->image_id);
} else {
$ids = join(",", array_reverse(array_map(fn ($s) => $s->image_id, $successes)));
$page->set_mode(PageMode::REDIRECT);
$page->set_redirect(search_link(["poster={$user->name}"]));
$page->set_redirect(search_link(["id={$ids}"]));
}
}