pics/controllers/EditAsset.php
Aaron van Geffen e28fcd8b03 Move photo deletion from ViewPhoto to EditAsset
Removes the intermediate confirmation page, instead using JavaScript for confirmation.

Fixes an XSS issue, in that the previous method was not passing or checking the session (!)
2023-11-11 15:29:32 +01:00

182 lines
5.7 KiB
PHP

<?php
/*****************************************************************************
* EditAsset.php
* Contains the asset management controller
*
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
*****************************************************************************/
class EditAsset extends HTMLController
{
public function __construct()
{
if (empty($_GET['id']))
throw new Exception('Invalid request.');
$asset = Asset::fromId($_GET['id']);
if (empty($asset))
throw new NotFoundException('Asset not found');
// Can we edit this asset?
$user = Registry::get('user');
if (!($user->isAdmin() || $asset->isOwnedBy($user)))
throw new NotAllowedException();
if (isset($_REQUEST['delete']) && Session::validateSession('get'))
{
$redirectUrl = BASEURL . '/' . $asset->getSubdir();
$asset->delete();
header('Location: ' . $redirectUrl);
exit;
}
if (!empty($_POST))
{
if (isset($_GET['updatethumb']))
{
$image = $asset->getImage();
return $this->updateThumb($image);
}
// Key info
if (isset($_POST['title'], $_POST['slug'], $_POST['date_captured'], $_POST['priority']))
{
$date_captured = !empty($_POST['date_captured']) ? new DateTime($_POST['date_captured']) : null;
$slug = strtr($_POST['slug'], [' ' => '-', '--' => '-', '&' => 'and', '=>' => '', "'" => "", ":"=> "", '\\' => '-']);
$asset->setKeyData(htmlspecialchars($_POST['title']), $slug, $date_captured, intval($_POST['priority']));
}
// Handle tags
$new_tags = [];
if (isset($_POST['tag']) && is_array($_POST['tag']))
foreach ($_POST['tag'] as $id_tag => $bool)
if (is_numeric($id_tag))
$new_tags[] = $id_tag;
$current_tags = array_keys($asset->getTags());
$tags_to_unlink = array_diff($current_tags, $new_tags);
$asset->unlinkTags($tags_to_unlink);
$tags_to_link = array_diff($new_tags, $current_tags);
$asset->linkTags($tags_to_link);
// Meta data
if (isset($_POST['meta_key'], $_POST['meta_value']))
{
$new_meta = array_filter(array_combine($_POST['meta_key'], $_POST['meta_value']), function($e) {
return !empty($e);
});
$asset->setMetaData($new_meta);
}
// A replacement file?
if (isset($_FILES['replacement'], $_POST['replacement_target']) && !empty($_FILES['replacement']['tmp_name']))
{
if ($_POST['replacement_target'] === 'full')
{
$asset->replaceFile($_FILES['replacement']['tmp_name']);
if ($asset->isImage())
{
$image = $asset->getImage();
$image->removeAllThumbnails();
}
}
elseif (preg_match('~^thumb_(\d+x\d+(?:_c[best]?)?)$~', $_POST['replacement_target'], $match))
{
$image = $asset->getImage();
if (($replace_result = $image->replaceThumbnail($match[1], $_FILES['replacement']['tmp_name'])) !== 0)
throw new Exception('Could not replace thumbnail \'' . $match[1] . '\' with the uploaded file. Error code: ' . $replace_result);
}
}
header('Location: ' . BASEURL . '/editasset/?id=' . $asset->getId());
}
// Get list of thumbnails
$thumbs = $this->getThumbs($asset);
$page = new EditAssetForm($asset, $thumbs);
parent::__construct('Edit asset \'' . $asset->getTitle() . '\' (' . $asset->getFilename() . ') - ' . SITE_TITLE);
$this->page->adopt($page);
}
private function getThumbs(Asset $asset)
{
if (!$asset->isImage())
return [];
$image = $asset->getImage();
$subdir = $image->getSubdir();
$metadata = $image->getMeta();
$thumb_selectors = $image->getThumbnails();
$thumbs = [];
foreach ($thumb_selectors as $selector => $filename)
{
if (!preg_match('~^(?<width>\d+)x(?<height>\d+)(?<suffix>_c(?<method>[best]?))?$~', $selector, $thumb))
continue;
$dimensions = $thumb['width'] . 'x' . $thumb['height'];
// Does the thumbnail exist on disk? If not, use an url to generate it.
if (!$filename || !file_exists(THUMBSDIR . '/' . $subdir . '/' . $filename))
$thumb_url = BASEURL . '/thumbnail/' . $image->getId() . '/' . $dimensions . ($thumb['suffix'] ?? '') . '/';
else
$thumb_url = THUMBSURL . '/' . $subdir . '/' . $filename;
$has_crop_boundary = isset($metadata['crop_' . $dimensions]);
$has_custom_image = isset($metadata['custom_' . $dimensions]);
$thumbs[] = [
'dimensions' => [(int) $thumb['width'], (int) $thumb['height']],
'cropped' => !$has_custom_image && (!empty($thumb['suffix']) || $has_crop_boundary),
'crop_method' => !$has_custom_image && !empty($thumb['method']) ? $thumb['method'] : (!empty($thumb['suffix']) ? 'c' : null),
'crop_region' => $has_crop_boundary ? $metadata['crop_' . $dimensions] : null,
'custom_image' => $has_custom_image,
'filename' => $filename,
'url' => $thumb_url,
];
}
return $thumbs;
}
private function updateThumb(Image $image)
{
$data = json_decode($_POST['data']);
$meta = $image->getMeta();
// Set new crop boundary.
$crop_key = 'crop_' . $data->thumb_width . 'x' . $data->thumb_height;
$crop_value = $data->crop_width . ',' . $data->crop_height . ',' . $data->source_x . ',' . $data->source_y;
$meta[$crop_key] = $crop_value;
// If we previously uploaded a custom thumbnail, stop considering it such.
$custom_key = 'custom_' . $data->thumb_width . 'x' . $data->thumb_height;
if (isset($meta[$custom_key]))
{
// TODO: delete from disk
unset($meta[$custom_key]);
}
// Save meta changes so far.
$image->setMetaData($meta);
// Force a rebuild of related thumbnails.
$image->removeThumbnailsOfSize($data->thumb_width, $data->thumb_height);
$payload = [
'key' => $crop_key,
'value' => $crop_value,
'url' => $image->getThumbnailUrl($data->thumb_width, $data->thumb_height, 'exact'),
];
header('Content-Type: text/json; charset=utf-8');
echo json_encode($payload);
exit;
}
}