2016-08-18 20:39:31 +00:00
< ? php
/* --------------------------------------------------------------------
Chevereto
http :// chevereto . com /
@ author Rodolfo Berrios A . < http :// rodolfoberrios . com />
2018-08-16 18:51:52 +00:00
< inbox @ rodolfoberrios . com >
2016-08-18 20:39:31 +00:00
Copyright ( C ) Rodolfo Berrios A . All rights reserved .
2018-04-17 21:25:26 +00:00
2016-08-18 20:39:31 +00:00
BY USING THIS SOFTWARE YOU DECLARE TO ACCEPT THE CHEVERETO EULA
http :// chevereto . com / license
--------------------------------------------------------------------- */
namespace CHV ;
2018-08-16 18:51:52 +00:00
use G ;
use Exception ;
2020-06-24 18:35:15 +00:00
use SEOURLify ;
2018-08-16 18:51:52 +00:00
class Image
{
public static $table_chv_image = [
2020-06-24 18:35:15 +00:00
'name' ,
'extension' ,
'album_id' ,
'size' ,
'width' ,
'height' ,
'date' ,
'date_gmt' ,
'nsfw' ,
'user_id' ,
'uploader_ip' ,
'storage_mode' ,
'storage_id' ,
'md5' ,
'source_md5' ,
'original_filename' ,
'original_exifdata' ,
'category_id' ,
'description' ,
'chain' ,
'thumb_size' ,
'medium_size' ,
'title' ,
'expiration_date_gmt' ,
'likes' ,
'is_animated' ,
];
2018-04-17 21:25:26 +00:00
2018-08-16 18:51:52 +00:00
public static $chain_sizes = [ 'original' , 'image' , 'medium' , 'thumb' ];
2020-06-24 18:35:15 +00:00
public static function getSingle ( $id , $sumview = false , $pretty = false , $requester = null )
2018-08-16 18:51:52 +00:00
{
$tables = DB :: getTables ();
2020-06-24 18:35:15 +00:00
$query = 'SELECT * FROM ' . $tables [ 'images' ] . " \n " ;
2018-08-16 18:51:52 +00:00
$joins = [
2020-06-24 18:35:15 +00:00
'LEFT JOIN ' . $tables [ 'storages' ] . ' ON ' . $tables [ 'images' ] . '.image_storage_id = ' . $tables [ 'storages' ] . '.storage_id' ,
'LEFT JOIN ' . $tables [ 'storage_apis' ] . ' ON ' . $tables [ 'storages' ] . '.storage_api_id = ' . $tables [ 'storage_apis' ] . '.storage_api_id' ,
'LEFT JOIN ' . $tables [ 'users' ] . ' ON ' . $tables [ 'images' ] . '.image_user_id = ' . $tables [ 'users' ] . '.user_id' ,
'LEFT JOIN ' . $tables [ 'albums' ] . ' ON ' . $tables [ 'images' ] . '.image_album_id = ' . $tables [ 'albums' ] . '.album_id'
2018-08-16 18:51:52 +00:00
];
if ( $requester ) {
if ( ! is_array ( $requester )) {
$requester = User :: getSingle ( $requester , 'id' );
}
2020-09-02 16:39:23 +00:00
if ( ! defined ( 'G_APP_GITHUB_REPO' ) && version_compare ( Settings :: get ( 'chevereto_version_installed' ), '3.7.0' , '>=' )) {
2020-06-24 18:35:15 +00:00
$joins [] = 'LEFT JOIN ' . $tables [ 'likes' ] . ' ON ' . $tables [ 'likes' ] . '.like_content_type = "image" AND ' . $tables [ 'images' ] . '.image_id = ' . $tables [ 'likes' ] . '.like_content_id AND ' . $tables [ 'likes' ] . '.like_user_id = ' . $requester [ 'id' ];
}
2018-08-16 18:51:52 +00:00
}
$query .= implode ( " \n " , $joins ) . " \n " ;
2020-06-24 18:35:15 +00:00
$query .= 'WHERE image_id=:image_id;' . " \n " ;
2018-08-16 18:51:52 +00:00
if ( $sumview ) {
2020-06-24 18:35:15 +00:00
$query .= 'UPDATE ' . $tables [ 'images' ] . ' SET image_views = image_views + 1 WHERE image_id=:image_id' ;
2018-08-16 18:51:52 +00:00
}
try {
$db = DB :: getInstance ();
$db -> query ( $query );
$db -> bind ( ':image_id' , $id );
$image_db = $db -> fetchSingle ();
if ( $image_db ) {
if ( $sumview ) {
$image_db [ 'image_views' ] += 1 ;
// Track stats
Stat :: track ([
2020-06-24 18:35:15 +00:00
'action' => 'update' ,
'table' => 'images' ,
'value' => '+1' ,
'user_id' => $image_db [ 'image_user_id' ],
2018-08-16 18:51:52 +00:00
]);
}
2020-06-24 18:35:15 +00:00
if ( $requester ) {
$image_db [ 'image_liked' ] = ( bool ) $image_db [ 'like_user_id' ];
}
2018-08-16 18:51:52 +00:00
$return = $image_db ;
$return = $pretty ? self :: formatArray ( $return ) : $return ;
if ( ! $return [ 'file_resource' ]) {
$return [ 'file_resource' ] = self :: getSrcTargetSingle ( $image_db , true );
}
return $return ;
} else {
return $image_db ;
}
} catch ( Exception $e ) {
throw new ImageException ( $e -> getMessage (), 400 );
}
}
2020-06-24 18:35:15 +00:00
public static function getMultiple ( $ids , $pretty = false )
2018-08-16 18:51:52 +00:00
{
if ( ! is_array ( $ids )) {
throw new ImageException ( 'Expecting $ids array in Image::get_multiple' , 100 );
}
if ( count ( $ids ) == 0 ) {
throw new ImageException ( 'Null $ids provided in Image::get_multiple' , 100 );
}
$tables = DB :: getTables ();
2020-06-24 18:35:15 +00:00
$query = 'SELECT * FROM ' . $tables [ 'images' ] . " \n " ;
2018-08-16 18:51:52 +00:00
$joins = array (
2020-06-24 18:35:15 +00:00
'LEFT JOIN ' . $tables [ 'users' ] . ' ON ' . $tables [ 'images' ] . '.image_user_id = ' . $tables [ 'users' ] . '.user_id' ,
'LEFT JOIN ' . $tables [ 'albums' ] . ' ON ' . $tables [ 'images' ] . '.image_album_id = ' . $tables [ 'albums' ] . '.album_id'
2018-08-16 18:51:52 +00:00
);
$query .= implode ( " \n " , $joins ) . " \n " ;
2020-06-24 18:35:15 +00:00
$query .= 'WHERE image_id IN (' . join ( ',' , $ids ) . ')' . " \n " ;
2018-08-16 18:51:52 +00:00
try {
$db = DB :: getInstance ();
$db -> query ( $query );
$images_db = $db -> fetchAll ();
if ( $images_db ) {
foreach ( $images_db as $k => $v ) {
$images_db [ $k ] = array_merge ( $v , self :: getSrcTargetSingle ( $v , true )); // todo
}
}
if ( $pretty ) {
$return = [];
foreach ( $images_db as $k => $v ) {
$return [] = self :: formatArray ( $v );
}
return $return ;
}
return $images_db ;
} catch ( Exception $e ) {
throw new ImageException ( $e -> getMessage (), 400 );
}
}
2020-06-24 18:35:15 +00:00
public static function getAlbumSlice ( $image_id , $album_id = null , $padding = 2 )
2018-08-16 18:51:52 +00:00
{
$tables = DB :: getTables ();
if ( $image_id == null ) {
throw new ImageException ( " Image id can't be NULL " , 100 );
}
if ( $album_id == null ) {
try {
$db = DB :: getInstance ();
2020-06-24 18:35:15 +00:00
$db -> query ( 'SELECT image_album_id FROM ' . $tables [ 'images' ] . ' WHERE image_id=:image_id' );
2018-08-16 18:51:52 +00:00
$db -> bind ( ':image_id' , $image_id );
$image_album_db = $db -> fetchSingle ();
$album_id = $image_album_db [ 'image_album_id' ];
2020-06-24 18:35:15 +00:00
} catch ( Exception $e ) {
2018-08-16 18:51:52 +00:00
throw new ImageException ( $e -> getMessage (), 400 );
}
if ( $album_id == null ) {
return ;
}
}
if ( ! is_numeric ( $padding )) {
$padding = 2 ;
}
//$where_album = $album_id !== NULL ? "image_album_id=:image_album_id" : "image_album_id IS NULL";
try {
2020-06-24 18:35:15 +00:00
$prevs = new Listing ;
$prevs -> setType ( 'images' );
$prevs -> setLimit (( $padding * 2 ) + 1 );
$prevs -> setSortType ( 'date' );
$prevs -> setSortOrder ( 'desc' );
$prevs -> setRequester ( Login :: getUser ());
$prevs -> setWhere ( 'WHERE image_album_id=' . $album_id . ' AND image_id <= ' . $image_id );
$prevs -> exec ();
$nexts = new Listing ;
$nexts -> setType ( 'images' );
$nexts -> setLimit ( $padding * 2 );
$nexts -> setSortType ( 'date' );
$nexts -> setSortOrder ( 'asc' );
$nexts -> setRequester ( Login :: getUser ());
$nexts -> setWhere ( 'WHERE image_album_id=' . $album_id . ' AND image_id > ' . $image_id );
$nexts -> exec ();
if ( is_array ( $prevs -> output )) {
$prevs -> output = array_reverse ( $prevs -> output );
}
$list = array_merge ( $prevs -> output , $nexts -> output );
$album_offset = array (
'top' => $prevs -> count - 1 ,
'bottom' => $nexts -> count
);
2018-08-16 18:51:52 +00:00
2020-06-24 18:35:15 +00:00
$album_chop_count = count ( $list );
$album_iteration_times = $album_chop_count - ( $padding * 2 + 1 );
2018-08-16 18:51:52 +00:00
2020-06-24 18:35:15 +00:00
if ( $album_chop_count > ( $padding * 2 + 1 )) {
2018-08-16 18:51:52 +00:00
if ( $album_offset [ 'top' ] > $padding && $album_offset [ 'bottom' ] > $padding ) {
// Cut on top
2020-06-24 18:35:15 +00:00
for ( $i = 0 ; $i < $album_offset [ 'top' ] - $padding ; $i ++ ) {
unset ( $list [ $i ]);
2018-08-16 18:51:52 +00:00
}
// Cut on bottom
2020-06-24 18:35:15 +00:00
for ( $i = 1 ; $i <= $album_offset [ 'bottom' ] - $padding ; $i ++ ) {
unset ( $list [ $album_chop_count - $i ]);
2018-08-16 18:51:52 +00:00
}
} elseif ( $album_offset [ 'top' ] <= $padding ) {
// Cut bottom
2020-06-24 18:35:15 +00:00
for ( $i = 0 ; $i < $album_iteration_times ; $i ++ ) {
unset ( $list [ $album_chop_count - 1 - $i ]);
2018-08-16 18:51:52 +00:00
}
} elseif ( $album_offset [ 'bottom' ] <= $padding ) {
// Cut top
2020-06-24 18:35:15 +00:00
for ( $i = 0 ; $i < $album_iteration_times ; $i ++ ) {
unset ( $list [ $i ]);
2018-08-16 18:51:52 +00:00
}
}
2020-06-24 18:35:15 +00:00
$list = array_values ( $list );
2018-08-16 18:51:52 +00:00
}
2020-06-24 18:35:15 +00:00
$images = array ();
foreach ( $list as $k => $v ) {
$format = self :: formatArray ( $v );
$images [ $format [ 'id' ]] = $format ;
2018-08-16 18:51:52 +00:00
}
2020-06-24 18:35:15 +00:00
if ( is_array ( $prevs -> output ) && $prevs -> count > 1 ) {
$prevLastKey = $prevs -> count - 2 ;
$prevLastId = $prevs -> output [ $prevLastKey ][ 'image_id' ];
$slice [ 'prev' ] = $images [ $prevLastId ];
2018-08-16 18:51:52 +00:00
}
2020-06-24 18:35:15 +00:00
if ( $nexts -> output ) {
$slice [ 'next' ] = $images [ $nexts -> output [ 0 ][ 'image_id' ]];
2018-08-16 18:51:52 +00:00
}
2020-06-24 18:35:15 +00:00
$slice [ 'images' ] = $images ;
2018-08-16 18:51:52 +00:00
2020-06-24 18:35:15 +00:00
return $slice ;
2018-08-16 18:51:52 +00:00
} catch ( Exception $e ) {
throw new ImageException ( $e -> getMessage (), 400 );
}
2016-08-18 20:39:31 +00:00
}
2018-04-17 21:25:26 +00:00
2020-06-24 18:35:15 +00:00
public static function getSrcTargetSingle ( $filearray , $prefix = true )
2018-08-16 18:51:52 +00:00
{
$prefix = $prefix ? 'image_' : null ;
$folder = CHV_PATH_IMAGES ;
$pretty = ! isset ( $filearray [ 'image_id' ]);
2020-06-24 18:35:15 +00:00
$mode = $filearray [ $prefix . 'storage_mode' ];
2018-08-16 18:51:52 +00:00
$chain_mask = str_split (( string ) str_pad ( decbin ( $filearray [ $pretty ? 'chain' : 'image_chain' ]), 4 , '0' , STR_PAD_LEFT ));
$chain_to_sufix = [
//'original' => '.original.',
2020-06-24 18:35:15 +00:00
'image' => '.' ,
'thumb' => '.th.' ,
'medium' => '.md.'
2018-08-16 18:51:52 +00:00
];
2020-06-24 18:35:15 +00:00
if ( $pretty ) {
$type = $filearray [ 'storage' ][ 'id' ] ? 'url' : 'path' ;
} else {
$type = $filearray [ 'storage_id' ] ? 'url' : 'path' ;
}
if ( $type == 'url' ) { // URL resource folder
$folder = G\add_ending_slash ( $pretty ? $filearray [ 'storage' ][ 'url' ] : $filearray [ 'storage_url' ]);
}
2018-08-16 18:51:52 +00:00
2020-06-24 18:35:15 +00:00
switch ( $mode ) {
2018-08-16 18:51:52 +00:00
case 'datefolder' :
2020-06-24 18:35:15 +00:00
$datetime = $filearray [ $prefix . 'date' ];
2018-08-16 18:51:52 +00:00
$datefolder = preg_replace ( '/(.*)(\s.*)/' , '$1' , str_replace ( '-' , '/' , $datetime ));
$folder .= G\add_ending_slash ( $datefolder ); // Y/m/d/
2020-06-24 18:35:15 +00:00
break ;
2018-08-16 18:51:52 +00:00
case 'old' :
$folder .= 'old/' ;
2020-06-24 18:35:15 +00:00
break ;
2018-08-16 18:51:52 +00:00
case 'direct' :
// use direct $folder
2020-06-24 18:35:15 +00:00
break ;
case 'path' :
$folder = G\add_ending_slash ( $filearray [ 'path' ]);
break ;
2018-08-16 18:51:52 +00:00
}
$targets = [
'type' => $type ,
2020-06-24 18:35:15 +00:00
'chain' => [
2018-08-16 18:51:52 +00:00
//'original' => NULL,
2020-06-24 18:35:15 +00:00
'image' => null ,
'thumb' => null ,
'medium' => null
2018-08-16 18:51:52 +00:00
]
];
foreach ( $targets [ 'chain' ] as $k => $v ) {
2020-06-24 18:35:15 +00:00
$targets [ 'chain' ][ $k ] = $folder . $filearray [ $prefix . 'name' ] . $chain_to_sufix [ $k ] . $filearray [ $prefix . 'extension' ];
2018-04-17 21:25:26 +00:00
}
2018-08-16 18:51:52 +00:00
if ( $type == 'path' ) {
foreach ( $targets [ 'chain' ] as $k => $v ) {
if ( ! file_exists ( $v )) {
unset ( $targets [ 'chain' ][ $k ]);
};
}
} else {
foreach ( $chain_mask as $k => $v ) {
if ( ! ( bool ) $v ) {
unset ( $targets [ 'chain' ][ self :: $chain_sizes [ $k ]]);
}
}
}
2020-06-24 18:35:15 +00:00
2018-08-16 18:51:52 +00:00
return $targets ;
}
2020-06-24 18:35:15 +00:00
public static function getUrlViewer ( $id_encoded , $title = null )
2018-08-16 18:51:52 +00:00
{
2020-06-24 18:35:15 +00:00
if ( $title == null ) {
$seo = null ;
} else {
$seo = SEOURLify :: filter ( $title );
}
$url = $seo ? ( $seo . '.' . $id_encoded ) : $id_encoded ;
return G\get_base_url ( getSetting ( 'route_image' ) . '/' . $url );
2018-08-16 18:51:52 +00:00
}
public static function getAvailableExpirations ()
{
$string = _s ( 'After %n %t' );
$translate = [ // Just for gettext parsing
2020-06-24 18:35:15 +00:00
'minute' => _n ( 'minute' , 'minutes' , 1 ),
'hour' => _n ( 'hour' , 'hours' , 1 ),
'day' => _n ( 'day' , 'days' , 1 ),
'week' => _n ( 'week' , 'weeks' , 1 ),
'month' => _n ( 'month' , 'months' , 1 ),
'year' => _n ( 'year' , 'years' , 1 ),
];
2018-08-16 18:51:52 +00:00
$return = [
2020-06-24 18:35:15 +00:00
null => _s ( " Don't autodelete " ),
];
2018-08-16 18:51:52 +00:00
$table = [
2020-06-24 18:35:15 +00:00
[ 'minute' , 5 ],
[ 'minute' , 15 ],
[ 'minute' , 30 ],
[ 'hour' , 1 ],
[ 'hour' , 2 ],
[ 'hour' , 6 ],
[ 'hour' , 12 ],
[ 'day' , 1 ],
[ 'day' , 2 ],
[ 'day' , 3 ],
[ 'week' , 1 ],
[ 'week' , 2 ],
[ 'month' , 1 ],
];
2018-08-16 18:51:52 +00:00
foreach ( $table as $expire ) {
$unit = $expire [ 0 ];
$interval_spec = 'P' . ( in_array ( $unit , [ 'second' , 'minute' , 'hour' ]) ? 'T' : null ) . $expire [ 1 ] . strtoupper ( $unit [ 0 ]);
$return [ $interval_spec ] = strtr ( $string , [ '%n' => $expire [ 1 ], '%t' => _n ( $unit , $unit . 's' , $expire [ 1 ])]);
}
return $return ;
}
2020-06-24 18:35:15 +00:00
public static function watermark ( $image_path , $options = [])
2018-08-16 18:51:52 +00:00
{
// Watermark options
$options = array_merge ([
2020-06-24 18:35:15 +00:00
'ratio' => getSetting ( 'watermark_percentage' ) / 100 ,
'position' => explode ( ' ' , getSetting ( 'watermark_position' )),
'file' => CHV_PATH_CONTENT_IMAGES_SYSTEM . getSetting ( 'watermark_image' )
2018-08-16 18:51:52 +00:00
], $options );
if ( ! is_readable ( $options [ 'file' ])) {
throw new Exception ( " Can't read watermark file " , 100 );
}
// Fail-safe ratio
$options [ 'ratio' ] = min ( 1 , ( ! is_numeric ( $options [ 'ratio' ]) ? 0.01 : max ( 0.01 , $options [ 'ratio' ])));
// Fail-safe positioning
if ( ! in_array ( $options [ 'position' ][ 0 ], [ 'left' , 'center' , 'right' ])) {
$options [ 'position' ][ 0 ] = 'right' ;
}
if ( ! in_array ( $options [ 'position' ][ 1 ], [ 'top' , 'center' , 'bottom' ])) {
$options [ 'position' ][ 0 ] = 'bottom' ;
}
// Get source fileinfo
$image_fileinfo = G\get_image_fileinfo ( $image_path );
// Create working source image
2020-06-24 18:35:15 +00:00
$ext = $image_fileinfo [ 'extension' ] == 'jpg' ? 'jpeg' : $image_fileinfo [ 'extension' ];
$imagecreatefrom = 'imagecreatefrom' . $ext ;
$src = $imagecreatefrom ( $image_path );
2018-08-16 18:51:52 +00:00
$src_width = imagesx ( $src );
$src_height = imagesy ( $src );
// Create working watermark image
$watermark_fileinfo = G\get_image_fileinfo ( $options [ 'file' ]);
$watermark_width = $watermark_fileinfo [ 'width' ];
$watermark_height = $watermark_fileinfo [ 'height' ];
$watermark_max_upscale = 1.2 ;
$watermark_max_width = $watermark_fileinfo [ 'width' ] * $watermark_max_upscale ;
$watermark_max_height = $watermark_fileinfo [ 'height' ] * $watermark_max_upscale ;
$watermark_area = $image_fileinfo [ 'width' ] * $image_fileinfo [ 'height' ] * $options [ 'ratio' ];
$watermark_image_ratio = $watermark_fileinfo [ 'ratio' ];
2020-06-24 18:35:15 +00:00
$watermark_new_height = round ( sqrt ( $watermark_area / $watermark_image_ratio ), 0 );
2018-08-16 18:51:52 +00:00
if ( $watermark_new_height > $watermark_max_height ) { // Set a max cap
$watermark_new_height = $watermark_max_height ;
}
// To legit to quit
if ( $watermark_new_height > $src_height ) {
$watermark_new_height = $src_height ;
}
// Fix watermark margin issues on height
if ( getSetting ( 'watermark_margin' ) and $options [ 'position' ][ 1 ] !== 'center' and $watermark_new_height + getSetting ( 'watermark_margin' ) > $src_height ) {
2020-06-24 18:35:15 +00:00
$watermark_new_height -= $watermark_new_height + 2 * getSetting ( 'watermark_margin' ) - $src_height ;
2018-08-16 18:51:52 +00:00
}
$watermark_new_width = round ( $watermark_image_ratio * $watermark_new_height , 0 );
// To legit to quit yo
if ( $watermark_new_width > $src_width ) {
$watermark_new_width = $src_width ;
}
// Fix watermark margin issues on width
if ( getSetting ( 'watermark_margin' ) and $options [ 'position' ][ 0 ] !== 'center' and $watermark_new_width + getSetting ( 'watermark_margin' ) > $src_width ) {
2020-06-24 18:35:15 +00:00
$watermark_new_width -= $watermark_new_width + 2 * getSetting ( 'watermark_margin' ) - $src_width ;
2018-08-16 18:51:52 +00:00
$watermark_new_height = $watermark_new_width / $watermark_image_ratio ;
}
2020-06-24 18:35:15 +00:00
if ( $watermark_new_height <= $watermark_max_height or $watermark_new_width <= $watermark_fileinfo [ 'width' ] * 2 ) {
2018-08-16 18:51:52 +00:00
// Resizable watermark image
try {
$watermark_tempnam = @ tempnam ( sys_get_temp_dir (), 'chvtemp' );
if ( ! $watermark_tempnam ) {
$watermark_tempnam = @ tempnam ( dirname ( $image_path ), 'chvtemp' );
}
if ( ! $watermark_tempnam ) {
throw new Exception ( " Can't tempnam a watermak file " , 400 );
}
self :: resize ( $options [ 'file' ], dirname ( $watermark_tempnam ), basename ( $watermark_tempnam ), [ 'width' => $watermark_new_width ]);
$watermark_temp = $watermark_tempnam . '.png' ;
$watermark_src = imagecreatefrompng ( $watermark_temp );
$watermark_width = imagesx ( $watermark_src );
$watermark_height = imagesy ( $watermark_src );
@ unlink ( $watermark_tempnam );
} catch ( Exception $e ) {
error_log ( $e );
} // Silence
} else {
// Watermark "as is"
$watermark_src = imagecreatefrompng ( $options [ 'file' ]);
}
// Calculate the watermark position
switch ( $options [ 'position' ][ 0 ]) {
case 'left' :
$watermark_x = getSetting ( 'watermark_margin' );
2020-06-24 18:35:15 +00:00
break ;
2018-08-16 18:51:52 +00:00
case 'center' :
2020-06-24 18:35:15 +00:00
$watermark_x = $src_width / 2 - $watermark_width / 2 ;
break ;
2018-08-16 18:51:52 +00:00
case 'right' :
$watermark_x = $src_width - $watermark_width - getSetting ( 'watermark_margin' );
2020-06-24 18:35:15 +00:00
break ;
2018-08-16 18:51:52 +00:00
}
switch ( $options [ 'position' ][ 1 ]) {
case 'top' :
$watermark_y = getSetting ( 'watermark_margin' );
2020-06-24 18:35:15 +00:00
break ;
2018-08-16 18:51:52 +00:00
case 'center' :
2020-06-24 18:35:15 +00:00
$watermark_y = $src_height / 2 - $watermark_height / 2 ;
break ;
2018-08-16 18:51:52 +00:00
case 'bottom' :
$watermark_y = $src_height - $watermark_height - getSetting ( 'watermark_margin' );
2020-06-24 18:35:15 +00:00
break ;
2018-08-16 18:51:52 +00:00
}
// Apply and save the watermark
G\imagecopymerge_alpha ( $src , $watermark_src , $watermark_x , $watermark_y , 0 , 0 , $watermark_width , $watermark_height , getSetting ( 'watermark_opacity' ), $image_fileinfo [ 'extension' ]);
switch ( $image_fileinfo [ 'extension' ]) {
2020-06-24 18:35:15 +00:00
case 'gif' :
$imageRes = imagegif ( $src , $image_path );
// no break, cast gif as png (gif has very poor quality)
2018-08-16 18:51:52 +00:00
case 'png' :
2020-06-24 18:35:15 +00:00
$imageRes = imagepng ( $src , $image_path );
break ;
2018-08-16 18:51:52 +00:00
case 'jpg' :
2020-06-24 18:35:15 +00:00
$imageRes = imagejpeg ( $src , $image_path , 90 );
break ;
case 'webp' :
$imageRes = imagewebp ( $src , $image_path , 90 );
break ;
2018-08-16 18:51:52 +00:00
}
2020-06-24 18:35:15 +00:00
2018-08-16 18:51:52 +00:00
imagedestroy ( $src );
@ unlink ( $watermark_temp );
return true ;
}
2020-06-24 18:35:15 +00:00
public static function upload ( $source , $destination , $filename = null , $options = [], $storage_id = null , $guestSessionHandle = true )
2018-08-16 18:51:52 +00:00
{
$default_options = Upload :: getDefaultOptions ();
$options = array_merge ( $default_options , $options );
if ( ! is_null ( $filename ) && ! $options [ 'filenaming' ]) {
$options [ 'filenaming' ] = 'original' ;
}
2018-04-17 21:25:26 +00:00
try {
2018-08-16 18:51:52 +00:00
$upload = new Upload ;
$upload -> setSource ( $source );
$upload -> setDestination ( $destination );
$upload -> setOptions ( $options );
2020-06-24 18:35:15 +00:00
if ( ! is_null ( $storage_id )) {
$upload -> setStorageId ( $storage_id );
}
2018-08-16 18:51:52 +00:00
if ( ! is_null ( $filename )) {
$upload -> setFilename ( $filename );
}
2020-06-24 18:35:15 +00:00
if ( $guestSessionHandle == false ) {
// G\debug('Dont detect flood');
$upload -> detectFlood = false ;
} else {
// G\debug('Detect flood');
}
2018-08-16 18:51:52 +00:00
$upload -> exec ();
$original_md5 = $upload -> uploaded [ 'fileinfo' ][ 'md5' ];
2020-06-24 18:35:15 +00:00
$is_animated_image = G\is_animated_image ( $upload -> uploaded [ 'file' ]);
2018-08-16 18:51:52 +00:00
$apply_watermark = ( $options [ 'watermark' ] and ! $is_animated_image );
// Disable animated image watermark
if ( $is_animated_image ) {
$apply_watermark = false ;
}
if ( $apply_watermark ) {
// Detect watermark min image requirement
foreach ([ 'width' , 'height' ] as $k ) {
$min_value = getSetting ( 'watermark_target_min_' . $k );
if ( $min_value == 0 ) { // Skip on zero
continue ;
}
$apply_watermark = $upload -> uploaded [ 'fileinfo' ][ $k ] >= $min_value ;
}
// Disable on GIF image?
if ( $apply_watermark and $upload -> uploaded [ 'fileinfo' ][ 'extension' ] == 'gif' and ! $options [ 'watermark_gif' ]) {
$apply_watermark = false ;
}
2018-04-17 21:25:26 +00:00
}
2018-08-16 18:51:52 +00:00
if ( $apply_watermark && self :: watermark ( $upload -> uploaded [ 'file' ])) {
$upload -> uploaded [ 'fileinfo' ] = G\get_image_fileinfo ( $upload -> uploaded [ 'file' ]); // Remake the fileinfo array, new full array file info (todo: faster!)
$upload -> uploaded [ 'fileinfo' ][ 'md5' ] = $original_md5 ; // Preserve original MD5 for watermarked images
}
return [
2020-06-24 18:35:15 +00:00
'uploaded' => $upload -> uploaded ,
'source' => $upload -> source
2018-08-16 18:51:52 +00:00
];
} catch ( Exception $e ) {
throw new UploadException ( $e -> getMessage (), $e -> getCode ());
}
}
// Mostly for people uploading two times the same image to test or just bug you
// $mixed => $_FILES or md5 string
2020-06-24 18:35:15 +00:00
public static function isDuplicatedUpload ( $mixed , $time_frame = 'P1D' )
2018-08-16 18:51:52 +00:00
{
2020-06-24 18:35:15 +00:00
if ( is_array ( $mixed ) && isset ( $mixed [ 'tmp_name' ])) {
$filename = $mixed [ 'tmp_name' ];
if ( stream_resolve_include_path ( $filename ) == false ) {
throw new Exception ( " Concurrency: $filename is gone " , 666 );
}
$md5_file = @ md5_file ( $filename );
} else {
$filename = $mixed ;
$md5_file = $filename ;
}
2018-08-16 18:51:52 +00:00
if ( ! isset ( $md5_file )) {
throw new Exception ( 'Unable to process md5_file' , 100 );
}
$db = DB :: getInstance ();
$db -> query ( 'SELECT * FROM ' . DB :: getTable ( 'images' ) . ' WHERE (image_md5=:md5 OR image_source_md5=:md5) AND image_uploader_ip=:ip AND image_date_gmt > :date_gmt' );
$db -> bind ( ':md5' , $md5_file );
$db -> bind ( ':ip' , G\get_client_ip ());
$db -> bind ( ':date_gmt' , G\datetime_sub ( G\datetimegmt (), $time_frame ));
$db -> exec ();
return $db -> fetchColumn ();
}
2020-06-24 18:35:15 +00:00
public static function uploadToWebsite ( $source , $user = null , $params = [], $guestSessionHandle = true , $ip = null )
2018-08-16 18:51:52 +00:00
{
try {
// Get user
if ( $user ) {
2020-06-24 18:35:15 +00:00
// Not explicit $user array
if ( is_array ( $user ) == false ) {
if ( is_numeric ( $user )) { // ...$param unpack doesn't preserve type and turns everything into string
2018-08-16 18:51:52 +00:00
$user = User :: getSingle ( $user );
2020-06-24 18:35:15 +00:00
} else {
$user = User :: getSingle ( $user , 'username' );
}
2018-08-16 18:51:52 +00:00
}
if ( $user !== null && getSetting ( 'upload_max_filesize_mb_bak' ) !== null && getSetting ( 'upload_max_filesize_mb' ) == getSetting ( 'upload_max_filesize_mb_guest' )) {
Settings :: setValue ( 'upload_max_filesize_mb' , getSetting ( 'upload_max_filesize_mb_bak' ));
}
}
$do_dupe_check = ! getSetting ( 'enable_duplicate_uploads' ) && ! $user [ 'is_admin' ];
2020-06-24 18:35:15 +00:00
// if($guestSessionHandle == false) {
// $do_dupe_check = false;
// }
2018-08-16 18:51:52 +00:00
// Detect duplicated uploads ($_FILES) by IP + MD5 (first layer)
if ( $do_dupe_check && self :: isDuplicatedUpload ( $source )) {
throw new Exception ( _s ( 'Duplicated upload' ), 101 );
}
$storage_mode = getSetting ( 'upload_storage_mode' );
switch ( $storage_mode ) {
case 'direct' :
$upload_path = CHV_PATH_IMAGES ;
2020-06-24 18:35:15 +00:00
break ;
2018-08-16 18:51:52 +00:00
case 'datefolder' :
// Stock 'now' datetime
$datefolder_stock = [
'date' => G\datetime (),
'date_gmt' => G\datetimegmt (),
];
$datefolder = date ( 'Y/m/d/' , strtotime ( $datefolder_stock [ 'date' ]));
$upload_path = CHV_PATH_IMAGES . $datefolder ;
2020-06-24 18:35:15 +00:00
break ;
2018-08-16 18:51:52 +00:00
}
$filenaming = getSetting ( 'upload_filenaming' );
if ( $filenaming !== 'id' and in_array ( $params [ 'privacy' ], [ 'password' , 'private' , 'private_but_link' ])) {
$filenaming = 'random' ;
}
$upload_options = [
2020-06-24 18:35:15 +00:00
'max_size' => G\get_bytes ( getSetting ( 'upload_max_filesize_mb' ) . ' MB' ),
'watermark' => getSetting ( 'watermark_enable' ),
'exif' => ( getSetting ( 'upload_image_exif_user_setting' ) && $user ) ? $user [ 'image_keep_exif' ] : getSetting ( 'upload_image_exif' ),
2018-08-16 18:51:52 +00:00
];
// Workaround watermark by user group
if ( $upload_options [ 'watermark' ]) {
$watermark_user = $user ? ( $user [ 'is_admin' ] ? 'admin' : 'user' ) : 'guest' ;
$upload_options [ 'watermark' ] = getSetting ( 'watermark_enable_' . $watermark_user );
}
// Watermark by filetype
$upload_options [ 'watermark_gif' ] = ( bool ) getSetting ( 'watermark_enable_file_gif' );
// Reserve this ID
if ( $filenaming == 'id' ) {
try {
$dummy = [
'name' => '' ,
'extension' => '' ,
'size' => 0 ,
'width' => 0 ,
'height' => 0 ,
'date' => '0000-01-01 00:00:00' ,
'date_gmt' => '0000-01-01 00:00:00' ,
'nsfw' => 0 ,
'uploader_ip' => '' ,
'md5' => '' ,
'original_filename' => '' ,
'chain' => 0 ,
'thumb_size' => 0 ,
'medium_size' => 0 ,
];
$dummy_insert = DB :: insert ( 'images' , $dummy );
DB :: delete ( 'images' , [ 'id' => $dummy_insert ]);
$target_id = $dummy_insert ;
} catch ( Exception $e ) {
error_log ( $e );
// Fallback
$filenaming = 'original' ;
}
}
// Filenaming
$upload_options [ 'filenaming' ] = $filenaming ;
// Allowed extensions
$upload_options [ 'allowed_formats' ] = self :: getEnabledImageFormats ();
2020-06-24 18:35:15 +00:00
$image_upload = self :: upload ( $source , $upload_path , $filenaming == 'id' ? encodeID ( $target_id ) : null , $upload_options , $storage_id , $guestSessionHandle );
2018-08-16 18:51:52 +00:00
$chain_mask = [ 0 , 1 , 0 , 1 ]; // original image medium thumb
$chain_array = [];
// Detect duplicated uploads (all) by IP + MD5 (second layer)
if ( $do_dupe_check && self :: isDuplicatedUpload ( $image_upload [ 'uploaded' ][ 'fileinfo' ][ 'md5' ])) {
throw new Exception ( _s ( 'Duplicated upload' ), 102 );
}
// Handle resizing KEEP 'source', change 'uploaded'
$image_ratio = $image_upload [ 'uploaded' ][ 'fileinfo' ][ 'width' ] / $image_upload [ 'uploaded' ][ 'fileinfo' ][ 'height' ];
$must_resize = false ;
// This dumb step is to have a failover for 0 values
$image_max_size_cfg = [
2020-06-24 18:35:15 +00:00
'width' => Settings :: get ( 'upload_max_image_width' ) ? : $image_upload [ 'uploaded' ][ 'fileinfo' ][ 'width' ],
'height' => Settings :: get ( 'upload_max_image_height' ) ? : $image_upload [ 'uploaded' ][ 'fileinfo' ][ 'height' ],
2018-08-16 18:51:52 +00:00
];
// Is too large? (like my big fat dick...)
if ( $image_max_size_cfg [ 'width' ] < $image_upload [ 'uploaded' ][ 'fileinfo' ][ 'width' ] || $image_max_size_cfg [ 'height' ] < $image_upload [ 'uploaded' ][ 'fileinfo' ][ 'height' ]) {
// Adjust image_max to max_size_cfg (setting value)
$image_max = $image_max_size_cfg ;
$image_max [ 'width' ] = ( int ) round ( $image_max_size_cfg [ 'height' ] * $image_ratio );
$image_max [ 'height' ] = ( int ) round ( $image_max_size_cfg [ 'width' ] / $image_ratio );
if ( $image_max [ 'height' ] > $image_max_size_cfg [ 'height' ]) {
$image_max [ 'height' ] = $image_max_size_cfg [ 'height' ];
$image_max [ 'width' ] = ( int ) round ( $image_max [ 'height' ] * $image_ratio );
}
if ( $image_max [ 'width' ] > $image_max_size_cfg [ 'width' ]) {
$image_max [ 'width' ] = $image_max_size_cfg [ 'width' ];
$image_max [ 'height' ] = ( int ) round ( $image_max [ 'width' ] / $image_ratio );
}
// Detect if resize is needed, if so, use $image_max values
if ( $image_max != [ 'width' => $image_upload [ 'uploaded' ][ 'fileinfo' ][ 'width' ], 'height' => $image_max_size_cfg [ 'height' ]]) { // loose just in case..
$must_resize = true ;
$params [ 'width' ] = $image_max [ 'width' ];
$params [ 'height' ] = $image_max [ 'height' ];
}
}
foreach ([ 'width' , 'height' ] as $k ) {
if ( ! isset ( $params [ $k ]) || ! is_numeric ( $params [ $k ])) {
continue ;
}
if ( $params [ $k ] != $image_upload [ 'uploaded' ][ 'fileinfo' ][ $k ]) {
$must_resize = true ;
}
}
// Disable resize for animated images (for now)
if ( G\is_animated_image ( $image_upload [ 'uploaded' ][ 'file' ])) {
$must_resize = false ;
}
if ( $must_resize ) {
$source_md5 = $image_upload [ 'uploaded' ][ 'fileinfo' ][ 'md5' ];
// Detect duplicated uploads (all + resized) by IP + MD5(source not resized) (third layer)
// The idea is to reject these before the actual final resize (avoids potential ddos)
if ( $do_dupe_check && self :: isDuplicatedUpload ( $source_md5 )) {
throw new Exception ( _s ( 'Duplicated upload' ), 103 );
}
2020-06-24 18:35:15 +00:00
if ( $image_ratio == $params [ 'width' ] / $params [ 'height' ]) {
2018-08-16 18:51:52 +00:00
$image_resize_options = [
2020-06-24 18:35:15 +00:00
'width' => $params [ 'width' ],
'height' => $params [ 'height' ]
2018-08-16 18:51:52 +00:00
];
} else {
$image_resize_options = [ 'width' => $params [ 'width' ]];
}
$image_upload [ 'uploaded' ] = self :: resize ( $image_upload [ 'uploaded' ][ 'file' ], dirname ( $image_upload [ 'uploaded' ][ 'file' ]), null , $image_resize_options );
}
// Try to generate the thumb
$image_thumb_options = [
2020-06-24 18:35:15 +00:00
'width' => getSetting ( 'upload_thumb_width' ),
'height' => getSetting ( 'upload_thumb_height' )
2018-08-16 18:51:52 +00:00
];
// Try to generate the medium
$medium_size = getSetting ( 'upload_medium_size' );
$medium_fixed_dimension = getSetting ( 'upload_medium_fixed_dimension' );
2020-06-24 18:35:15 +00:00
$is_animated_image = G\is_animated_image ( $image_upload [ 'uploaded' ][ 'file' ]);
2018-08-16 18:51:52 +00:00
// Medium sized image
if ( $image_upload [ 'uploaded' ][ 'fileinfo' ][ $medium_fixed_dimension ] > $medium_size or $is_animated_image ) {
$image_medium_options = [];
$image_medium_options [ $medium_fixed_dimension ] = $medium_size ;
if ( $is_animated_image ) {
$image_medium_options [ 'forced' ] = true ;
$image_medium_options [ $medium_fixed_dimension ] = min ( $image_medium_options [ $medium_fixed_dimension ], $image_upload [ 'uploaded' ][ 'fileinfo' ][ $medium_fixed_dimension ]);
}
$image_medium = self :: resize ( $image_upload [ 'uploaded' ][ 'file' ], dirname ( $image_upload [ 'uploaded' ][ 'file' ]), $image_upload [ 'uploaded' ][ 'name' ] . '.md' , $image_medium_options );
$chain_mask [ 2 ] = 1 ;
}
// Thumb sized image
$image_thumb = self :: resize ( $image_upload [ 'uploaded' ][ 'file' ], dirname ( $image_upload [ 'uploaded' ][ 'file' ]), $image_upload [ 'uploaded' ][ 'name' ] . '.th' , $image_thumb_options );
2020-06-24 18:35:15 +00:00
$chain_value = bindec (( int ) implode ( '' , $chain_mask ));
2018-08-16 18:51:52 +00:00
$disk_space_needed = $image_upload [ 'uploaded' ][ 'fileinfo' ][ 'size' ] + $image_thumb [ 'fileinfo' ][ 'size' ] + ( $image_medium [ 'fileinfo' ][ 'size' ] ? : 0 );
$image_insert_values = [
2020-06-24 18:35:15 +00:00
'storage_mode' => $storage_mode ,
'storage_id' => $storage_id ,
'user_id' => $user [ 'id' ],
'album_id' => $params [ 'album_id' ] ? : null ,
'nsfw' => $params [ 'nsfw' ],
'category_id' => $params [ 'category_id' ] ? : null ,
'title' => $params [ 'title' ],
'description' => $params [ 'description' ] ? : null ,
'chain' => $chain_value ,
'thumb_size' => $image_thumb [ 'fileinfo' ][ 'size' ],
'medium_size' => $image_medium [ 'fileinfo' ][ 'size' ] ? : 0 ,
'is_animated' => $is_animated_image ,
2018-08-16 18:51:52 +00:00
'source_md5' => $source_md5 ? : null ,
]; // Note: NULL is strongly needed here to avoid integer 0 issues on sql
if ( isset ( $datefolder_stock )) {
$image_insert_values = array_merge ( $image_insert_values , $datefolder_stock );
}
// Expirable upload
if ( getSetting ( 'enable_expirable_uploads' )) {
// Inject guest forced auto delete
if ( ! $user && getSetting ( 'auto_delete_guest_uploads' ) !== null ) {
$params [ 'expiration' ] = getSetting ( 'auto_delete_guest_uploads' );
}
// Inject user's default expiration date
if ( ! isset ( $params [ 'expiration' ]) && ! is_null ( $user [ 'image_expiration' ])) {
$params [ 'expiration' ] = $user [ 'image_expiration' ];
}
try {
// Handle image expire time (source comes as DateInterval string)
if ( ! empty ( $params [ 'expiration' ]) && array_key_exists ( $params [ 'expiration' ], self :: getAvailableExpirations ())) {
$params [ 'expiration_date_gmt' ] = G\datetime_add ( G\datetimegmt (), strtoupper ( $params [ 'expiration' ]));
}
// Image expirable handling
if ( ! empty ( $params [ 'expiration_date_gmt' ])) {
$expirable_diff = G\datetime_diff ( G\datetimegmt (), $params [ 'expiration_date_gmt' ], 'm' );
// 5 minutes minimum
$image_insert_values [ 'expiration_date_gmt' ] = $expirable_diff < 5 ? G\datetime_modify ( G\datetimegmt (), '+5 minutes' ) : $params [ 'expiration_date_gmt' ];
}
} catch ( Exception $e ) {
} // Silence
2016-08-18 20:39:31 +00:00
}
// Inject image title
2018-08-16 18:51:52 +00:00
if ( ! array_key_exists ( 'title' , $params )) {
// From Exif
$title_from_exif = $image_upload [ 'source' ][ 'image_exif' ][ 'ImageDescription' ] ? trim ( $image_upload [ 'source' ][ 'image_exif' ][ 'ImageDescription' ]) : null ;
if ( $title_from_exif ) {
// Get rid of any unicode stuff
$title_from_exif = preg_replace ( '/[\x00-\x1F\x80-\xFF]/' , '' , $title_from_exif );
$image_title = $title_from_exif ;
} else {
// From filename
$title_from_filename = preg_replace ( '/[-_\s]+/' , ' ' , trim ( $image_upload [ 'source' ][ 'name' ]));
$image_title = $title_from_filename ;
}
$image_insert_values [ 'title' ] = $image_title ;
}
if ( $filenaming == 'id' && $target_id ) { // Insert as a reserved ID
$image_insert_values [ 'id' ] = $target_id ;
}
// Trim image_title to the actual DB limit
$image_insert_values [ 'title' ] = mb_substr ( $image_insert_values [ 'title' ], 0 , 100 , 'UTF-8' );
if ( $user && $image_insert_values [ 'album_id' ]) {
$album = Album :: getSingle ( $image_insert_values [ 'album_id' ]);
// Check album ownership
if ( $album [ 'user' ][ 'id' ] != $user [ 'id' ]) {
unset ( $image_insert_values [ 'album_id' ], $album );
}
}
2020-06-24 18:35:15 +00:00
if ( isset ( $ip )) {
$image_insert_values [ 'uploader_ip' ] = $ip ;
}
2018-08-16 18:51:52 +00:00
$uploaded_id = self :: insert ( $image_upload , $image_insert_values );
if ( $filenaming == 'id' ) {
unset ( $reserved_id );
}
// Private upload? Create a private album then (if needed)
if ( in_array ( $params [ 'privacy' ], [ 'private' , 'private_but_link' ])) {
if ( is_null ( $album ) or ! in_array ( $album [ 'privacy' ], [ 'private' , 'private_but_link' ])) {
$upload_timestamp = $params [ 'timestamp' ] ? : time ();
2020-06-24 18:35:15 +00:00
$session_handle = 'upload_' . $upload_timestamp ;
2018-08-16 18:51:52 +00:00
// Get timestamp based album
2020-06-24 18:35:15 +00:00
@ session_start ();
2018-08-16 18:51:52 +00:00
if ( isset ( $_SESSION [ $session_handle ])) {
$album = Album :: getSingle ( decodeID ( $_SESSION [ $session_handle ]));
} else {
$album = null ;
}
// Test that...
if ( ! $album or ! in_array ( $album [ 'privacy' ], [ 'private' , 'private_but_link' ])) {
2020-06-24 18:35:15 +00:00
$inserted_album = Album :: insert ([
'name' => _s ( 'Private upload' ) . ' ' . G\datetime ( 'Y-m-d' ),
'user_id' => $user [ 'id' ],
'privacy' => $params [ 'privacy' ]
]);
2018-08-16 18:51:52 +00:00
$_SESSION [ $session_handle ] = encodeID ( $inserted_album );
$image_insert_values [ 'album_id' ] = $inserted_album ;
} else {
$image_insert_values [ 'album_id' ] = $album [ 'id' ];
}
}
}
// Update album (if any)
if ( isset ( $image_insert_values [ 'album_id' ])) {
Album :: addImage ( $image_insert_values [ 'album_id' ], $uploaded_id );
}
// Update user (if any)
if ( $user ) {
DB :: increment ( 'users' , [ 'image_count' => '+1' ], [ 'id' => $user [ 'id' ]]);
2020-06-24 18:35:15 +00:00
} elseif ( $guestSessionHandle == true ) {
@ session_start ();
if ( ! isset ( $_SESSION [ 'guest_images' ])) {
$_SESSION [ 'guest_images' ] = [];
2018-08-16 18:51:52 +00:00
}
2020-06-24 18:35:15 +00:00
$_SESSION [ 'guest_images' ][] = $uploaded_id ;
2018-08-16 18:51:52 +00:00
}
return $uploaded_id ;
} catch ( Exception $e ) {
@ unlink ( $image_upload [ 'uploaded' ][ 'file' ]);
@ unlink ( $image_medium [ 'file' ]);
@ unlink ( $image_thumb [ 'file' ]);
throw $e ;
}
}
public static function getEnabledImageFormats ()
{
$formats = explode ( ',' , Settings :: get ( 'upload_enabled_image_formats' ));
if ( in_array ( 'jpg' , $formats )) {
$formats [] = 'jpeg' ;
}
return $formats ;
}
2020-06-24 18:35:15 +00:00
public static function resize ( $source , $destination , $filename = null , $options = [])
2018-08-16 18:51:52 +00:00
{
try {
$resize = new Imageresize ;
$resize -> setSource ( $source );
$resize -> setDestination ( $destination );
if ( $filename ) {
$resize -> setFilename ( $filename );
}
$resize -> setOptions ( $options );
if ( $options [ 'width' ]) {
$resize -> set_width ( $options [ 'width' ]);
}
if ( $options [ 'height' ]) {
$resize -> set_height ( $options [ 'height' ]);
}
if ( $options [ 'width' ] == $options [ 'height' ]) {
$resize -> set_fixed ();
}
if ( $options [ 'forced' ]) {
$resize -> setOption ( 'forced' , true );
}
$resize -> exec ();
return $resize -> resized ;
} catch ( Exception $e ) {
throw new ImageException ( $e -> getMessage (), $e -> getCode ());
}
}
2020-06-24 18:35:15 +00:00
public static function insert ( $image_upload , $values = [])
2018-08-16 18:51:52 +00:00
{
try {
$table_chv_image = self :: $table_chv_image ;
foreach ( $table_chv_image as $k => $v ) {
$table_chv_image [ $k ] = 'image_' . $v ;
}
2020-06-24 18:35:15 +00:00
if ( empty ( $values [ 'uploader_ip' ])) {
$values [ 'uploader_ip' ] = G\get_client_ip ();
}
2018-08-16 18:51:52 +00:00
// Remove eternal/useless Exif MakerNote
if ( $image_upload [ 'source' ][ 'image_exif' ][ 'MakerNote' ]) {
unset ( $image_upload [ 'source' ][ 'image_exif' ][ 'MakerNote' ]);
}
$original_exifdata = $image_upload [ 'source' ][ 'image_exif' ] ? json_encode ( G\array_utf8encode ( $image_upload [ 'source' ][ 'image_exif' ])) : null ;
// Fix some values
2020-06-24 18:35:15 +00:00
$values [ 'nsfw' ] = in_array ( strval ( $values [ 'nsfw' ]), [ '0' , '1' ]) ? $values [ 'nsfw' ] : 0 ;
2018-08-16 18:51:52 +00:00
$populate_values = [
2020-06-24 18:35:15 +00:00
'uploader_ip' => $values [ 'uploader_ip' ],
2018-08-16 18:51:52 +00:00
'md5' => $image_upload [ 'uploaded' ][ 'fileinfo' ][ 'md5' ],
'original_filename' => $image_upload [ 'source' ][ 'filename' ],
'original_exifdata' => $original_exifdata
];
if ( ! isset ( $values [ 'date' ])) {
$populate_values = array_merge ( $populate_values , [
'date' => G\datetime (),
'date_gmt' => G\datetimegmt (),
]);
}
// Populate values with fileinfo + populate_values
$values = array_merge ( $image_upload [ 'uploaded' ][ 'fileinfo' ], $populate_values , $values );
// This doesn't work all the time...
foreach ([ 'title' , 'description' , 'category_id' , 'album_id' ] as $v ) {
G\nullify_string ( $values [ $v ]);
}
// Now use only the values accepted by the table
foreach ( $values as $k => $v ) {
if ( ! in_array ( 'image_' . $k , $table_chv_image ) && $k !== 'id' ) {
unset ( $values [ $k ]);
}
}
// Insert image
$insert = DB :: insert ( 'images' , $values );
$disk_space_used = $values [ 'size' ] + $values [ 'thumb_size' ] + $values [ 'medium_size' ];
// Track stats
Stat :: track ([
2020-06-24 18:35:15 +00:00
'action' => 'insert' ,
'table' => 'images' ,
'value' => '+1' ,
'date_gmt' => $values [ 'date_gmt' ],
'disk_sum' => $disk_space_used ,
2018-08-16 18:51:52 +00:00
]);
// Update album count
if ( ! is_null ( $values [ 'album_id' ]) and $insert ) {
Album :: updateImageCount ( $values [ 'album_id' ], 1 );
}
return $insert ;
} catch ( Exception $e ) {
throw new ImageException ( $e -> getMessage (), $e -> getCode ());
}
}
public static function update ( $id , $values )
{
try {
$values = G\array_filter_array ( $values , self :: $table_chv_image , 'exclusion' );
foreach ([ 'title' , 'description' , 'category_id' , 'album_id' ] as $v ) {
if ( ! array_key_exists ( $v , $values )) {
continue ;
}
G\nullify_string ( $values [ $v ]);
}
if ( isset ( $values [ 'album_id' ])) {
$image_db = self :: getSingle ( $id , false , false );
$old_album = $image_db [ 'image_album_id' ];
$new_album = $values [ 'album_id' ];
$update = DB :: update ( 'images' , $values , [ 'id' => $id ]);
if ( $update and $old_album !== $new_album ) {
if ( ! is_null ( $old_album )) { // Update the old album
Album :: updateImageCount ( $old_album , 1 , '-' );
}
if ( ! is_null ( $new_album )) { // Update the new album
Album :: updateImageCount ( $new_album , 1 );
}
}
return $update ;
} else {
return DB :: update ( 'images' , $values , [ 'id' => $id ]);
}
2020-06-24 18:35:15 +00:00
} catch ( Exception $e ) {
2018-08-16 18:51:52 +00:00
throw new ImageException ( $e -> getMessage (), 400 );
}
}
2020-06-24 18:35:15 +00:00
public static function delete ( $id , $update_user = true )
2018-08-16 18:51:52 +00:00
{
try {
$image = self :: getSingle ( $id , false , true );
$disk_space_used = $image [ 'size' ] + $image [ 'thumb' ][ 'size' ] + $image [ 'medium' ][ 'size' ];
if ( $image [ 'file_resource' ][ 'type' ] == 'path' ) {
foreach ( $image [ 'file_resource' ][ 'chain' ] as $file_delete ) {
if ( file_exists ( $file_delete ) and !@ unlink ( $file_delete )) {
throw new ImageException ( " Can't delete file " , 200 );
}
}
}
if ( $update_user and isset ( $image [ 'user' ][ 'id' ])) {
DB :: increment ( 'users' , [ 'image_count' => '-1' ], [ 'id' => $image [ 'user' ][ 'id' ]]);
}
// Update album count
if ( $image [ 'album' ][ 'id' ] > 0 ) {
Album :: updateImageCount ( $image [ 'album' ][ 'id' ], 1 , '-' );
}
// Track stats
Stat :: track ([
2020-06-24 18:35:15 +00:00
'action' => 'delete' ,
'table' => 'images' ,
'value' => '-1' ,
'date_gmt' => $image [ 'date_gmt' ],
'disk_sum' => $disk_space_used ,
'likes' => $image [ 'likes' ],
2018-08-16 18:51:52 +00:00
]);
// Remove "liked" counter for each user who liked this image
DB :: queryExec ( 'UPDATE ' . DB :: getTable ( 'users' ) . ' INNER JOIN ' . DB :: getTable ( 'likes' ) . ' ON user_id = like_user_id AND like_content_type = "image" AND like_content_id = ' . $image [ 'id' ] . ' SET user_liked = GREATEST(cast(user_liked AS SIGNED) - 1, 0);' );
// Log image deletion
DB :: insert ( 'deletions' , [
2020-06-24 18:35:15 +00:00
'date_gmt' => G\datetimegmt (),
'content_id' => $image [ 'id' ],
'content_date_gmt' => $image [ 'date_gmt' ],
'content_user_id' => $image [ 'user' ][ 'id' ],
'content_ip' => $image [ 'uploader_ip' ],
'content_views' => $image [ 'views' ],
'content_md5' => $image [ 'md5' ],
'content_likes' => $image [ 'likes' ],
'content_original_filename' => $image [ 'original_filename' ],
2018-08-16 18:51:52 +00:00
]);
return DB :: delete ( 'images' , [ 'id' => $id ]);
} catch ( Exception $e ) {
2020-06-24 18:35:15 +00:00
throw new ImageException ( $e -> getMessage () . ' (LINE:' . $e -> getLine () . ')' , 400 );
2018-08-16 18:51:52 +00:00
}
}
public static function deleteMultiple ( $ids )
{
if ( ! is_array ( $ids )) {
2020-06-24 18:35:15 +00:00
throw new ImageException ( 'Expecting array argument, ' . gettype ( $ids ) . ' given in ' . __METHOD__ , 100 );
2018-08-16 18:51:52 +00:00
}
try {
$affected = 0 ;
foreach ( $ids as $id ) {
if ( self :: delete ( $id )) {
$affected += 1 ;
}
}
return $affected ;
2020-06-24 18:35:15 +00:00
} catch ( Exception $e ) {
2018-08-16 18:51:52 +00:00
throw new ImageException ( $e -> getMessage (), 400 );
}
}
2020-06-24 18:35:15 +00:00
public static function deleteExpired ( $limit = 50 )
2018-08-16 18:51:52 +00:00
{
try {
if ( ! $limit || ! is_numeric ( $limit )) {
$limit = 50 ;
}
$db = DB :: getInstance ();
$db -> query ( 'SELECT image_id FROM ' . DB :: getTable ( 'images' ) . ' WHERE image_expiration_date_gmt IS NOT NULL AND image_expiration_date_gmt < :datetimegmt ORDER BY image_expiration_date_gmt DESC LIMIT ' . $limit . ';' ); // Just 50 files per request to prevent CPU meltdown or something like that
$db -> bind ( ':datetimegmt' , G\datetimegmt ());
$expired_db = $db -> fetchAll ();
if ( $expired_db ) {
$expired = [];
foreach ( $expired_db as $k => $v ) {
$expired [] = $v [ 'image_id' ];
}
self :: deleteMultiple ( $expired );
} else {
return null ;
}
return $return ? $return [ 'image_id' ] : false ;
} catch ( Exception $e ) {
throw new ImageException ( $e -> getMessage (), 400 );
}
}
public static function fill ( & $image )
{
$image [ 'id_encoded' ] = encodeID ( $image [ 'id' ]);
$targets = self :: getSrcTargetSingle ( $image , false );
if ( $targets [ 'type' ] == 'path' ) {
// Re-create missing stuff
if ( $image [ 'size' ] == 0 ) {
$get_image_fileinfo = G\get_image_fileinfo ( $targets [ 'chain' ][ 'image' ]);
$update_missing_values = [
2020-06-24 18:35:15 +00:00
'width' => $get_image_fileinfo [ 'width' ],
'height' => $get_image_fileinfo [ 'height' ],
'size' => $get_image_fileinfo [ 'size' ],
2018-08-16 18:51:52 +00:00
];
foreach ([ 'thumb' , 'medium' ] as $k ) {
if ( ! array_key_exists ( $k , $targets [ 'chain' ])) {
continue ;
}
if ( $image [ $k . '_size' ] == 0 ) {
$update_missing_values [ $k . '_size' ] = intval ( filesize ( G\get_image_fileinfo ( $targets [ 'chain' ][ $k ])));
}
}
self :: update ( $image [ 'id' ], $update_missing_values );
$image = array_merge ( $image , $update_missing_values );
}
2020-06-24 18:35:15 +00:00
$is_animated = G\is_animated_image ( $targets [ 'chain' ][ 'image' ]);
2018-08-16 18:51:52 +00:00
// Recreate thumb
if ( count ( $targets [ 'chain' ]) > 0 && ! $targets [ 'chain' ][ 'thumb' ]) {
try {
$thumb_options = [
2020-06-24 18:35:15 +00:00
'width' => getSetting ( 'upload_thumb_width' ),
'height' => getSetting ( 'upload_thumb_height' ),
'forced' => $image [ 'extension' ] == 'gif' && $is_animated
2018-08-16 18:51:52 +00:00
];
$targets [ 'chain' ][ 'thumb' ] = self :: resize ( $targets [ 'chain' ][ 'image' ], pathinfo ( $targets [ 'chain' ][ 'image' ], PATHINFO_DIRNAME ), $image [ 'name' ] . '.th' , $thumb_options )[ 'file' ];
} catch ( Exception $e ) {
}
}
// Recreate medium
$medium_size = getSetting ( 'upload_medium_size' );
$medium_fixed_dimension = getSetting ( 'upload_medium_fixed_dimension' );
if ( $image [ $medium_fixed_dimension ] > $medium_size && count ( $targets [ 'chain' ]) > 0 && ! $targets [ 'chain' ][ 'medium' ]) {
try {
$medium_options = [
2020-06-24 18:35:15 +00:00
$medium_fixed_dimension => $medium_size ,
'forced' => $image [ 'extension' ] == 'gif' && $is_animated
2018-08-16 18:51:52 +00:00
];
$targets [ 'chain' ][ 'medium' ] = self :: resize ( $targets [ 'chain' ][ 'image' ], pathinfo ( $targets [ 'chain' ][ 'image' ], PATHINFO_DIRNAME ), $image [ 'name' ] . '.md' , $medium_options )[ 'file' ];
} catch ( Exception $e ) {
}
}
if ( count ( $targets [ 'chain' ]) > 0 ) {
$original_md5 = $image [ 'md5' ];
$image = array_merge ( $image , ( array ) @ get_image_fileinfo ( $targets [ 'chain' ][ 'image' ])); // Never do an array merge over an empty thing!
$image [ 'md5' ] = $original_md5 ;
}
// Update is_animated flag
if ( $is_animated && ! $image [ 'is_animated' ]) {
self :: update ( $image [ 'id' ], [ 'is_animated' => 1 ]);
$image [ 'is_animated' ] = 1 ;
}
2018-04-17 21:25:26 +00:00
} else {
2018-08-16 18:51:52 +00:00
$image_fileinfo = [
2020-06-24 18:35:15 +00:00
'ratio' => $image [ 'width' ] / $image [ 'height' ],
'size' => intval ( $image [ 'size' ]),
'size_formatted' => G\format_bytes ( $image [ 'size' ])
2018-08-16 18:51:52 +00:00
];
$image = array_merge ( $image , get_image_fileinfo ( $targets [ 'chain' ][ 'image' ]), $image_fileinfo );
2018-04-17 21:25:26 +00:00
}
2018-08-16 18:51:52 +00:00
$image [ 'file_resource' ] = $targets ;
2020-06-24 18:35:15 +00:00
$image [ 'url_viewer' ] = self :: getUrlViewer ( $image [ 'id_encoded' ], getSetting ( 'seo_image_urls' ) ? $image [ 'title' ] : null );
$image [ 'url_short' ] = self :: getUrlViewer ( $image [ 'id_encoded' ]);
2018-08-16 18:51:52 +00:00
foreach ( $targets [ 'chain' ] as $k => $v ) {
if ( $targets [ 'type' ] == 'path' ) {
$image [ $k ] = file_exists ( $v ) ? get_image_fileinfo ( $v ) : null ;
} else {
$image [ $k ] = get_image_fileinfo ( $v );
}
$image [ $k ][ 'size' ] = $image [( $k == 'image' ? '' : $k . '_' ) . 'size' ];
}
2020-06-24 18:35:15 +00:00
$display = $image [ 'medium' ] !== null ? $image [ 'medium' ] : ( $image [ 'size' ] < G\get_bytes ( '200 KB' ) ? $image : $image [ 'thumb' ]);
2018-08-16 18:51:52 +00:00
$display_thumb = $display == $image [ 'thumb' ];
$image [ 'size_formatted' ] = G\format_bytes ( $image [ 'size' ]);
$image [ 'display_url' ] = $display [ 'url' ];
$image [ 'display_width' ] = $display_thumb ? getSetting ( 'upload_thumb_width' ) : $image [ 'width' ];
$image [ 'display_height' ] = $display_thumb ? getSetting ( 'upload_thumb_height' ) : $image [ 'height' ];
$image [ 'views_label' ] = _n ( 'view' , 'views' , $image [ 'views' ]);
$image [ 'likes_label' ] = _n ( 'like' , 'likes' , $image [ 'likes' ]);
$image [ 'how_long_ago' ] = time_elapsed_string ( $image [ 'date_gmt' ]);
$image [ 'date_fixed_peer' ] = Login :: getUser () ? G\datetimegmt_convert_tz ( $image [ 'date_gmt' ], Login :: getUser ()[ 'timezone' ]) : $image [ 'date_gmt' ];
$image [ 'title_truncated' ] = G\truncate ( $image [ 'title' ], 28 );
$image [ 'title_truncated_html' ] = G\safe_html ( $image [ 'title_truncated' ]);
$image [ 'is_use_loader' ] = getSetting ( 'image_load_max_filesize_mb' ) !== '' ? ( $image [ 'size' ] > G\get_bytes ( getSetting ( 'image_load_max_filesize_mb' ) . 'MB' )) : false ;
}
2020-06-24 18:35:15 +00:00
public static function formatArray ( $dbrow , $safe = false )
2018-08-16 18:51:52 +00:00
{
try {
$output = DB :: formatRow ( $dbrow );
if ( ! is_null ( $output [ 'user' ][ 'id' ])) {
User :: fill ( $output [ 'user' ]);
} else {
unset ( $output [ 'user' ]);
}
if ( ! is_null ( $output [ 'album' ][ 'id' ]) or ! is_null ( $output [ 'user' ][ 'id' ])) {
Album :: fill ( $output [ 'album' ], $output [ 'user' ]);
} else {
unset ( $output [ 'album' ]);
}
self :: fill ( $output );
if ( $safe ) {
unset ( $output [ 'storage' ]);
unset ( $output [ 'id' ], $output [ 'path' ], $output [ 'uploader_ip' ]);
unset ( $output [ 'album' ][ 'id' ], $output [ 'album' ][ 'privacy_extra' ], $output [ 'album' ][ 'user_id' ]);
unset ( $output [ 'user' ][ 'id' ]);
unset ( $output [ 'file_resource' ]);
}
return $output ;
2020-06-24 18:35:15 +00:00
} catch ( Exception $e ) {
2018-08-16 18:51:52 +00:00
throw new ImageException ( $e -> getMessage (), 400 );
}
}
2016-08-18 20:39:31 +00:00
}
2018-08-16 18:51:52 +00:00
class ImageException extends Exception
{
}