667 lines
16 KiB
PHP
667 lines
16 KiB
PHP
<?php
|
|
/*****************************************************************************
|
|
* Asset.php
|
|
* Contains key class Asset.
|
|
*
|
|
* Kabuki CMS (C) 2013-2015, Aaron van Geffen
|
|
*****************************************************************************/
|
|
|
|
class Asset
|
|
{
|
|
protected $id_asset;
|
|
protected $id_user_uploaded;
|
|
protected $subdir;
|
|
protected $filename;
|
|
protected $title;
|
|
protected $slug;
|
|
protected $mimetype;
|
|
protected $image_width;
|
|
protected $image_height;
|
|
protected $date_captured;
|
|
protected $priority;
|
|
|
|
protected $meta;
|
|
protected $tags;
|
|
protected $thumbnails;
|
|
|
|
protected function __construct(array $data)
|
|
{
|
|
foreach ($data as $attribute => $value)
|
|
{
|
|
if (property_exists($this, $attribute))
|
|
$this->$attribute = $value;
|
|
}
|
|
|
|
if (!empty($data['date_captured']) && $data['date_captured'] !== 'NULL')
|
|
$this->date_captured = new DateTime($data['date_captured']);
|
|
}
|
|
|
|
public static function fromId($id_asset, $return_format = 'object')
|
|
{
|
|
$row = Registry::get('db')->queryAssoc('
|
|
SELECT *
|
|
FROM assets
|
|
WHERE id_asset = {int:id_asset}',
|
|
[
|
|
'id_asset' => $id_asset,
|
|
]);
|
|
|
|
return empty($row) ? false : self::byRow($row, $return_format);
|
|
}
|
|
|
|
public static function fromSlug($slug, $return_format = 'object')
|
|
{
|
|
$row = Registry::get('db')->queryAssoc('
|
|
SELECT *
|
|
FROM assets
|
|
WHERE slug = {string:slug}',
|
|
[
|
|
'slug' => $slug,
|
|
]);
|
|
|
|
return empty($row) ? false : self::byRow($row, $return_format);
|
|
}
|
|
|
|
public static function byRow(array $row, $return_format = 'object')
|
|
{
|
|
$db = Registry::get('db');
|
|
|
|
// Supplement with metadata.
|
|
$row['meta'] = $db->queryPair('
|
|
SELECT variable, value
|
|
FROM assets_meta
|
|
WHERE id_asset = {int:id_asset}',
|
|
[
|
|
'id_asset' => $row['id_asset'],
|
|
]);
|
|
|
|
// And thumbnails.
|
|
$row['thumbnails'] = $db->queryPair('
|
|
SELECT
|
|
CONCAT(
|
|
width,
|
|
{string:x},
|
|
height,
|
|
IF(mode != {string:empty}, CONCAT({string:_}, mode), {string:empty})
|
|
) AS selector, filename
|
|
FROM assets_thumbs
|
|
WHERE id_asset = {int:id_asset}',
|
|
[
|
|
'id_asset' => $row['id_asset'],
|
|
'empty' => '',
|
|
'x' => 'x',
|
|
'_' => '_',
|
|
]);
|
|
|
|
return $return_format == 'object' ? new Asset($row) : $row;
|
|
}
|
|
|
|
public static function fromIds(array $id_assets, $return_format = 'array')
|
|
{
|
|
if (empty($id_assets))
|
|
return [];
|
|
|
|
$db = Registry::get('db');
|
|
|
|
$res = $db->query('
|
|
SELECT *
|
|
FROM assets
|
|
WHERE id_asset IN ({array_int:id_assets})
|
|
ORDER BY id_asset',
|
|
[
|
|
'id_assets' => $id_assets,
|
|
]);
|
|
|
|
$assets = [];
|
|
while ($asset = $db->fetch_assoc($res))
|
|
{
|
|
$assets[$asset['id_asset']] = $asset;
|
|
$assets[$asset['id_asset']]['meta'] = [];
|
|
$assets[$asset['id_asset']]['thumbnails'] = [];
|
|
}
|
|
|
|
$metas = $db->queryRows('
|
|
SELECT id_asset, variable, value
|
|
FROM assets_meta
|
|
WHERE id_asset IN ({array_int:id_assets})
|
|
ORDER BY id_asset',
|
|
[
|
|
'id_assets' => $id_assets,
|
|
]);
|
|
|
|
foreach ($metas as $meta)
|
|
$assets[$meta[0]]['meta'][$meta[1]] = $meta[2];
|
|
|
|
$thumbnails = $db->queryRows('
|
|
SELECT id_asset,
|
|
CONCAT(
|
|
width,
|
|
{string:x},
|
|
height,
|
|
IF(mode != {string:empty}, CONCAT({string:_}, mode), {string:empty})
|
|
) AS selector, filename
|
|
FROM assets_thumbs
|
|
WHERE id_asset IN ({array_int:id_assets})
|
|
ORDER BY id_asset',
|
|
[
|
|
'id_assets' => $id_assets,
|
|
'empty' => '',
|
|
'x' => 'x',
|
|
'_' => '_',
|
|
]);
|
|
|
|
foreach ($thumbnails as $thumb)
|
|
$assets[$thumb[0]]['thumbnails'][$thumb[1]] = $thumb[2];
|
|
|
|
if ($return_format == 'array')
|
|
return $assets;
|
|
else
|
|
{
|
|
$objects = [];
|
|
foreach ($assets as $id => $asset)
|
|
$objects[$id] = new Asset($asset);
|
|
return $objects;
|
|
}
|
|
}
|
|
|
|
public static function createNew(array $data, $return_format = 'object')
|
|
{
|
|
// Extract the data array.
|
|
extract($data);
|
|
|
|
// No filename? Abort!
|
|
if (!isset($filename_to_copy) || !is_file($filename_to_copy))
|
|
return false;
|
|
|
|
// No subdir? Use YYYY/MM
|
|
if (!isset($preferred_subdir))
|
|
$preferred_subdir = date('Y') . '/' . date('m');
|
|
|
|
// Does this dir exist yet? If not, create it.
|
|
if (!is_dir(ASSETSDIR . '/' . $preferred_subdir))
|
|
mkdir(ASSETSDIR . '/' . $preferred_subdir, 0755, true);
|
|
|
|
// Construct the destination filename. Make sure we don't accidentally overwrite anything.
|
|
if (!isset($preferred_filename))
|
|
$preferred_filename = basename($filename_to_copy);
|
|
|
|
$new_filename = $preferred_filename;
|
|
$destination = ASSETSDIR . '/' . $preferred_subdir . '/' . $preferred_filename;
|
|
while (file_exists($destination))
|
|
{
|
|
$filename = pathinfo($preferred_filename, PATHINFO_FILENAME) . '_' . mt_rand(10, 99);
|
|
$extension = pathinfo($preferred_filename, PATHINFO_EXTENSION);
|
|
$new_filename = $filename . '.' . $extension;
|
|
$destination = dirname($destination) . '/' . $new_filename;
|
|
}
|
|
|
|
// Can we write to the target directory? Then copy the file.
|
|
if (is_writable(ASSETSDIR . '/' . $preferred_subdir))
|
|
copy($filename_to_copy, $destination);
|
|
else
|
|
return false;
|
|
|
|
// Figure out the mime type for the file.
|
|
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
|
$mimetype = finfo_file($finfo, $destination);
|
|
finfo_close($finfo);
|
|
|
|
// Do we have a title yet? Otherwise, use the filename.
|
|
$title = isset($data['title']) ? $data['title'] : pathinfo($preferred_filename, PATHINFO_FILENAME);
|
|
|
|
// Same with the slug.
|
|
$slug = isset($data['slug']) ? $data['slug'] : $preferred_subdir . '/' . pathinfo($preferred_filename, PATHINFO_FILENAME);
|
|
|
|
// Detected an image?
|
|
if (substr($mimetype, 0, 5) == 'image')
|
|
{
|
|
$image = new Imagick($destination);
|
|
$d = $image->getImageGeometry();
|
|
|
|
// Get image dimensions, bearing orientation in mind.
|
|
switch ($image->getImageOrientation())
|
|
{
|
|
case Imagick::ORIENTATION_LEFTBOTTOM:
|
|
case Imagick::ORIENTATION_RIGHTTOP:
|
|
$image_width = $d['height'];
|
|
$image_height = $d['width'];
|
|
break;
|
|
|
|
default:
|
|
$image_width = $d['width'];
|
|
$image_height = $d['height'];
|
|
}
|
|
|
|
unset($image);
|
|
|
|
$exif = EXIF::fromFile($destination);
|
|
$date_captured = $exif->created_timestamp > 0 ? $exif->created_timestamp : time();
|
|
}
|
|
|
|
$db = Registry::get('db');
|
|
$res = $db->query('
|
|
INSERT INTO assets
|
|
(id_user_uploaded, subdir, filename, title, slug, mimetype, image_width, image_height, date_captured, priority)
|
|
VALUES
|
|
({int:id_user_uploaded}, {string:subdir}, {string:filename}, {string:title}, {string:slug}, {string:mimetype},
|
|
{int:image_width}, {int:image_height},
|
|
IF({int:date_captured} > 0, FROM_UNIXTIME({int:date_captured}), NULL),
|
|
{int:priority})',
|
|
[
|
|
'id_user_uploaded' => isset($id_user) ? $id_user : Registry::get('user')->getUserId(),
|
|
'subdir' => $preferred_subdir,
|
|
'filename' => $new_filename,
|
|
'title' => $title,
|
|
'slug' => $slug,
|
|
'mimetype' => $mimetype,
|
|
'image_width' => isset($image_width) ? $image_width : 'NULL',
|
|
'image_height' => isset($image_height) ? $image_height : 'NULL',
|
|
'date_captured' => isset($date_captured) ? $date_captured : 'NULL',
|
|
'priority' => isset($priority) ? (int) $priority : 0,
|
|
]);
|
|
|
|
if (!$res)
|
|
{
|
|
unlink($destination);
|
|
return false;
|
|
}
|
|
|
|
$data['id_asset'] = $db->insert_id();
|
|
return $return_format === 'object' ? new self($data) : $data;
|
|
}
|
|
|
|
public function getId()
|
|
{
|
|
return $this->id_asset;
|
|
}
|
|
|
|
public function getAuthor()
|
|
{
|
|
return Member::fromId($this->id_user_uploaded);
|
|
}
|
|
|
|
public function getDateCaptured()
|
|
{
|
|
return $this->date_captured;
|
|
}
|
|
|
|
public function getFilename()
|
|
{
|
|
return $this->filename;
|
|
}
|
|
|
|
public function getLinkedPosts()
|
|
{
|
|
$posts = Registry::get('db')->queryValues('
|
|
SELECT id_post
|
|
FROM posts_assets
|
|
WHERE id_asset = {int:id_asset}',
|
|
['id_asset' => $this->id_asset]);
|
|
|
|
// TODO: fix empty post iterator.
|
|
if (empty($posts))
|
|
return [];
|
|
|
|
return PostIterator::getByOptions([
|
|
'ids' => $posts,
|
|
'type' => '',
|
|
]);
|
|
}
|
|
|
|
public function getMeta()
|
|
{
|
|
return $this->meta;
|
|
}
|
|
|
|
public function getFullPath()
|
|
{
|
|
return ASSETSDIR . '/' . $this->subdir . '/' . $this->filename;
|
|
}
|
|
|
|
public function getSlug()
|
|
{
|
|
return $this->slug;
|
|
}
|
|
|
|
public function getSubdir()
|
|
{
|
|
return $this->subdir;
|
|
}
|
|
|
|
public function getPriority()
|
|
{
|
|
return $this->priority;
|
|
}
|
|
|
|
public function getTags()
|
|
{
|
|
if (!isset($this->tags))
|
|
$this->tags = Tag::byAssetId($this->id_asset);
|
|
|
|
return $this->tags;
|
|
}
|
|
|
|
public function getTitle()
|
|
{
|
|
return $this->title;
|
|
}
|
|
|
|
public function getUrl()
|
|
{
|
|
return BASEURL . '/assets/' . $this->subdir . '/' . $this->filename;
|
|
}
|
|
|
|
public function getPageUrl()
|
|
{
|
|
return BASEURL . '/' . $this->slug . '/';
|
|
}
|
|
|
|
public function getType()
|
|
{
|
|
return substr($this->mimetype, 0, strpos($this->mimetype, '/'));
|
|
}
|
|
|
|
public function getDimensions($as_type = 'string')
|
|
{
|
|
return $as_type === 'string' ? $this->image_width . 'x' . $this->image_height : [$this->image_width, $this->image_height];
|
|
}
|
|
|
|
public function isImage()
|
|
{
|
|
return isset($this->mimetype) && substr($this->mimetype, 0, 5) === 'image';
|
|
}
|
|
|
|
public function getImage()
|
|
{
|
|
if (!$this->isImage())
|
|
throw new Exception('Trying to upgrade an Asset to an Image while the Asset is not an image!');
|
|
|
|
return new Image(get_object_vars($this));
|
|
}
|
|
|
|
public function replaceFile($filename)
|
|
{
|
|
// No filename? Abort!
|
|
if (!isset($filename) || !is_readable($filename))
|
|
return false;
|
|
|
|
// Can we write to the target file?
|
|
$destination = ASSETSDIR . '/' . $this->subdir . '/' . $this->filename;
|
|
if (!is_writable($destination))
|
|
return false;
|
|
|
|
copy($filename, $destination);
|
|
|
|
// Figure out the mime type for the file.
|
|
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
|
$this->mimetype = finfo_file($finfo, $destination);
|
|
finfo_close($finfo);
|
|
|
|
// Detected an image?
|
|
if (substr($this->mimetype, 0, 5) === 'image')
|
|
{
|
|
$image = new Imagick($destination);
|
|
$d = $image->getImageGeometry();
|
|
$this->image_width = $d['width'];
|
|
$this->image_height = $d['height'];
|
|
unset($image);
|
|
|
|
$exif = EXIF::fromFile($destination);
|
|
if (!empty($exif->created_timestamp))
|
|
$this->date_captured = new DateTime(date('r', $exif->created_timestamp));
|
|
else
|
|
$this->date_captured = null;
|
|
}
|
|
else
|
|
{
|
|
$this->image_width = null;
|
|
$this->image_height = null;
|
|
$this->date_captured = null;
|
|
}
|
|
|
|
return Registry::get('db')->query('
|
|
UPDATE assets
|
|
SET
|
|
mimetype = {string:mimetype},
|
|
image_width = {int:image_width},
|
|
image_height = {int:image_height},
|
|
date_captured = {datetime:date_captured},
|
|
priority = {int:priority}
|
|
WHERE id_asset = {int:id_asset}',
|
|
[
|
|
'id_asset' => $this->id_asset,
|
|
'mimetype' => $this->mimetype,
|
|
'image_width' => isset($this->image_width) ? $this->image_width : 'NULL',
|
|
'image_height' => isset($this->image_height) ? $this->image_height : 'NULL',
|
|
'date_captured' => isset($this->date_captured) ? $this->date_captured : 'NULL',
|
|
'priority' => $this->priority,
|
|
]);
|
|
}
|
|
|
|
protected function saveMetaData()
|
|
{
|
|
$this->setMetaData($this->meta);
|
|
}
|
|
|
|
public function setMetaData(array $new_meta, $mode = 'replace')
|
|
{
|
|
$db = Registry::get('db');
|
|
|
|
// If we're replacing, delete current data first.
|
|
if ($mode === 'replace')
|
|
{
|
|
$to_remove = array_diff_key($this->meta, $new_meta);
|
|
if (!empty($to_remove))
|
|
$db->query('
|
|
DELETE FROM assets_meta
|
|
WHERE id_asset = {int:id_asset} AND
|
|
variable IN({array_string:variables})',
|
|
[
|
|
'id_asset' => $this->id_asset,
|
|
'variables' => array_keys($to_remove),
|
|
]);
|
|
}
|
|
|
|
// Build rows
|
|
$to_insert = [];
|
|
foreach ($new_meta as $key => $value)
|
|
$to_insert[] = [
|
|
'id_asset' => $this->id_asset,
|
|
'variable' => $key,
|
|
'value' => $value,
|
|
];
|
|
|
|
// Do the insertion
|
|
$res = Registry::get('db')->insert('replace', 'assets_meta', [
|
|
'id_asset' => 'int',
|
|
'variable' => 'string',
|
|
'value' => 'string',
|
|
], $to_insert, ['id_asset', 'variable']);
|
|
|
|
if ($res)
|
|
$this->meta = $new_meta;
|
|
}
|
|
|
|
public function delete()
|
|
{
|
|
$db = Registry::get('db');
|
|
|
|
if (!unlink(ASSETSDIR . '/' . $this->subdir . '/' . $this->filename))
|
|
return false;
|
|
|
|
$db->query('
|
|
DELETE FROM assets_meta
|
|
WHERE id_asset = {int:id_asset}',
|
|
[
|
|
'id_asset' => $this->id_asset,
|
|
]);
|
|
|
|
$recount_tags = $db->queryValues('
|
|
SELECT id_tag
|
|
FROM assets_tags
|
|
WHERE id_asset = {int:id_asset}',
|
|
[
|
|
'id_asset' => $this->id_asset,
|
|
]);
|
|
|
|
$db->query('
|
|
DELETE FROM assets_tags
|
|
WHERE id_asset = {int:id_asset}',
|
|
[
|
|
'id_asset' => $this->id_asset,
|
|
]);
|
|
|
|
Tag::recount($recount_tags);
|
|
|
|
$return = $db->query('
|
|
DELETE FROM assets
|
|
WHERE id_asset = {int:id_asset}',
|
|
[
|
|
'id_asset' => $this->id_asset,
|
|
]);
|
|
|
|
$rows = $db->query('
|
|
SELECT id_tag
|
|
FROM tags
|
|
WHERE id_asset_thumb = {int:id_asset}',
|
|
[
|
|
'id_asset' => $this->id_asset,
|
|
]);
|
|
|
|
if (!empty($rows))
|
|
{
|
|
foreach ($rows as $row)
|
|
{
|
|
$tag = Tag::fromId($row['id_tag']);
|
|
$tag->resetIdAsset();
|
|
}
|
|
}
|
|
|
|
return $return;
|
|
}
|
|
|
|
public function linkTags(array $id_tags)
|
|
{
|
|
if (empty($id_tags))
|
|
return true;
|
|
|
|
$pairs = [];
|
|
foreach ($id_tags as $id_tag)
|
|
$pairs[] = ['id_asset' => $this->id_asset, 'id_tag' => $id_tag];
|
|
|
|
Registry::get('db')->insert('ignore', 'assets_tags', [
|
|
'id_asset' => 'int',
|
|
'id_tag' => 'int',
|
|
], $pairs, ['id_asset', 'id_tag']);
|
|
|
|
Tag::recount($id_tags);
|
|
}
|
|
|
|
public function unlinkTags(array $id_tags)
|
|
{
|
|
if (empty($id_tags))
|
|
return true;
|
|
|
|
Registry::get('db')->query('
|
|
DELETE FROM assets_tags
|
|
WHERE id_asset = {int:id_asset} AND id_tag IN ({array_int:id_tags})',
|
|
[
|
|
'id_asset' => $this->id_asset,
|
|
'id_tags' => $id_tags,
|
|
]);
|
|
|
|
Tag::recount($id_tags);
|
|
}
|
|
|
|
public static function getCount()
|
|
{
|
|
return Registry::get('db')->queryValue('
|
|
SELECT COUNT(*)
|
|
FROM assets');
|
|
}
|
|
|
|
public function setKeyData($title, $slug, DateTime $date_captured = null, $priority)
|
|
{
|
|
$params = [
|
|
'id_asset' => $this->id_asset,
|
|
'title' => $title,
|
|
'slug' => $slug,
|
|
'priority' => $priority,
|
|
];
|
|
|
|
if (isset($date_captured))
|
|
$params['date_captured'] = $date_captured->format('Y-m-d H:i:s');
|
|
|
|
return Registry::get('db')->query('
|
|
UPDATE assets
|
|
SET title = {string:title},
|
|
slug = {string:slug},' . (isset($date_captured) ? '
|
|
date_captured = {datetime:date_captured},' : '') . '
|
|
priority = {int:priority}
|
|
WHERE id_asset = {int:id_asset}',
|
|
$params);
|
|
}
|
|
|
|
public function getUrlForPreviousInSet($id_tag = null)
|
|
{
|
|
$row = Registry::get('db')->queryAssoc('
|
|
SELECT a.*
|
|
' . (isset($id_tag) ? '
|
|
FROM assets_tags AS t
|
|
INNER JOIN assets AS a ON a.id_asset = t.id_asset
|
|
WHERE t.id_tag = {int:id_tag} AND
|
|
(a.date_captured, a.id_asset) < ({datetime:date_captured}, {int:id_asset})
|
|
ORDER BY a.date_captured DESC, a.id_asset DESC'
|
|
: '
|
|
FROM assets AS a
|
|
WHERE (a.date_captured, a.id_asset) > ({datetime:date_captured}, {int:id_asset})
|
|
ORDER BY date_captured ASC, a.id_asset ASC')
|
|
. '
|
|
LIMIT 1',
|
|
[
|
|
'id_asset' => $this->id_asset,
|
|
'id_tag' => $id_tag,
|
|
'date_captured' => $this->date_captured,
|
|
]);
|
|
|
|
if ($row)
|
|
{
|
|
$obj = self::byRow($row, 'object');
|
|
return $obj->getPageUrl() . ($id_tag ? '?in=' . $id_tag : '');
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
public function getUrlForNextInSet($id_tag = null)
|
|
{
|
|
$row = Registry::get('db')->queryAssoc('
|
|
SELECT a.*
|
|
' . (isset($id_tag) ? '
|
|
FROM assets_tags AS t
|
|
INNER JOIN assets AS a ON a.id_asset = t.id_asset
|
|
WHERE t.id_tag = {int:id_tag} AND
|
|
(a.date_captured, a.id_asset) > ({datetime:date_captured}, {int:id_asset})
|
|
ORDER BY a.date_captured ASC, a.id_asset ASC'
|
|
: '
|
|
FROM assets AS a
|
|
WHERE (a.date_captured, a.id_asset) < ({datetime:date_captured}, {int:id_asset})
|
|
ORDER BY date_captured DESC, a.id_asset DESC')
|
|
. '
|
|
LIMIT 1',
|
|
[
|
|
'id_asset' => $this->id_asset,
|
|
'id_tag' => $id_tag,
|
|
'date_captured' => $this->date_captured,
|
|
]);
|
|
|
|
if ($row)
|
|
{
|
|
$obj = self::byRow($row, 'object');
|
|
return $obj->getPageUrl() . ($id_tag ? '?in=' . $id_tag : '');
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
}
|