2014-04-05 14:02:53 +00:00
< ? php
2016-01-26 14:31:53 +00:00
namespace Lychee\Modules ;
2014-04-05 14:02:53 +00:00
2016-01-30 22:24:08 +00:00
use ZipArchive ;
2016-03-04 22:03:35 +00:00
use Imagick ;
2016-03-16 14:22:18 +00:00
use ImagickPixel ;
2016-01-30 22:24:08 +00:00
2016-01-30 20:33:31 +00:00
final class Photo {
2014-04-05 14:02:53 +00:00
2016-01-24 21:14:20 +00:00
private $photoIDs = null ;
2014-04-05 14:02:53 +00:00
2015-04-13 19:09:28 +00:00
public static $validTypes = array (
2014-04-25 08:13:43 +00:00
IMAGETYPE_JPEG ,
IMAGETYPE_GIF ,
IMAGETYPE_PNG
2014-05-23 13:47:48 +00:00
);
2016-01-19 10:03:28 +00:00
2015-04-13 19:09:28 +00:00
public static $validExtensions = array (
2014-04-25 08:13:43 +00:00
'.jpg' ,
'.jpeg' ,
'.png' ,
'.gif'
2014-05-23 13:47:48 +00:00
);
2014-04-25 08:13:43 +00:00
2016-02-13 16:32:44 +00:00
/**
* @ return boolean Returns true when successful .
*/
2016-01-24 21:14:20 +00:00
public function __construct ( $photoIDs ) {
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Init vars
$this -> photoIDs = $photoIDs ;
2014-04-05 14:02:53 +00:00
return true ;
}
2016-02-13 22:37:25 +00:00
/**
* Creats new photo ( s ) .
* Exits on error .
* Use $returnOnError if you want to handle errors by your own .
2016-03-24 17:40:58 +00:00
* @ return string | false ID of the added photo .
2016-02-13 22:37:25 +00:00
*/
public function add ( array $files , $albumID = 0 , $returnOnError = false ) {
2014-04-11 22:30:26 +00:00
2016-01-30 20:43:57 +00:00
// Check permissions
2014-11-06 21:25:27 +00:00
if ( hasPermissions ( LYCHEE_UPLOADS ) === false ||
hasPermissions ( LYCHEE_UPLOADS_BIG ) === false ||
2014-12-06 13:29:17 +00:00
hasPermissions ( LYCHEE_UPLOADS_THUMB ) === false ) {
2016-01-31 17:49:31 +00:00
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'An upload-folder is missing or not readable and writable' );
2016-02-13 22:37:25 +00:00
if ( $returnOnError === true ) return false ;
2016-02-06 23:16:48 +00:00
Response :: error ( 'An upload-folder is missing or not readable and writable!' );
2014-05-16 21:24:11 +00:00
}
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-11 22:30:26 +00:00
switch ( $albumID ) {
case 's' :
2016-01-30 20:43:57 +00:00
// s for public (share)
$public = 1 ;
$star = 0 ;
$albumID = 0 ;
2014-04-11 22:30:26 +00:00
break ;
case 'f' :
2016-01-30 20:43:57 +00:00
// f for starred (fav)
$star = 1 ;
$public = 0 ;
$albumID = 0 ;
2014-04-11 22:30:26 +00:00
break ;
2014-06-29 13:40:06 +00:00
case 'r' :
2016-01-30 20:43:57 +00:00
// r for recent
$public = 0 ;
$star = 0 ;
$albumID = 0 ;
2014-06-29 13:40:06 +00:00
break ;
2014-04-11 22:30:26 +00:00
default :
2016-01-30 20:43:57 +00:00
$star = 0 ;
$public = 0 ;
2014-04-11 22:30:26 +00:00
break ;
}
2016-02-13 22:36:34 +00:00
// Only process the first photo in the array
$file = $files [ 0 ];
// Check if file exceeds the upload_max_filesize directive
if ( $file [ 'error' ] === UPLOAD_ERR_INI_SIZE ) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'The uploaded file exceeds the upload_max_filesize directive in php.ini' );
if ( $returnOnError === true ) return false ;
Response :: error ( 'The uploaded file exceeds the upload_max_filesize directive in php.ini!' );
}
2015-08-29 19:55:32 +00:00
2016-02-13 22:36:34 +00:00
// Check if file was only partially uploaded
if ( $file [ 'error' ] === UPLOAD_ERR_PARTIAL ) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'The uploaded file was only partially uploaded' );
if ( $returnOnError === true ) return false ;
Response :: error ( 'The uploaded file was only partially uploaded!' );
}
2015-08-29 19:55:32 +00:00
2016-02-13 22:36:34 +00:00
// Check if writing file to disk failed
if ( $file [ 'error' ] === UPLOAD_ERR_CANT_WRITE ) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Failed to write photo to disk' );
if ( $returnOnError === true ) return false ;
Response :: error ( 'Failed to write photo to disk!' );
}
2014-04-25 08:13:43 +00:00
2016-02-13 22:36:34 +00:00
// Check if a extension stopped the file upload
if ( $file [ 'error' ] === UPLOAD_ERR_EXTENSION ) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'A PHP extension stopped the file upload' );
if ( $returnOnError === true ) return false ;
Response :: error ( 'A PHP extension stopped the file upload!' );
}
2014-04-11 22:30:26 +00:00
2016-02-13 22:36:34 +00:00
// Check if the upload was successful
if ( $file [ 'error' ] !== UPLOAD_ERR_OK ) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Upload contains an error (' . $file [ 'error' ] . ')' );
if ( $returnOnError === true ) return false ;
Response :: error ( 'Upload failed!' );
}
2014-04-11 22:30:26 +00:00
2016-02-13 22:36:34 +00:00
// Verify extension
2016-03-16 16:07:22 +00:00
$extension = getExtension ( $file [ 'name' ], false );
2016-02-13 22:36:34 +00:00
if ( ! in_array ( strtolower ( $extension ), self :: $validExtensions , true )) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Photo format not supported' );
if ( $returnOnError === true ) return false ;
Response :: error ( 'Photo format not supported!' );
}
2014-04-11 22:30:26 +00:00
2016-02-13 22:36:34 +00:00
// Verify image
$type = @ exif_imagetype ( $file [ 'tmp_name' ]);
if ( ! in_array ( $type , self :: $validTypes , true )) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Photo type not supported' );
if ( $returnOnError === true ) return false ;
Response :: error ( 'Photo type not supported!' );
}
2014-08-22 20:02:58 +00:00
2016-02-13 22:36:34 +00:00
// Generate id
2016-03-12 22:51:33 +00:00
$id = generateID ();
2016-02-13 22:36:34 +00:00
// Set paths
$tmp_name = $file [ 'tmp_name' ];
$photo_name = md5 ( $id ) . $extension ;
$path = LYCHEE_UPLOADS_BIG . $photo_name ;
// Calculate checksum
$checksum = sha1_file ( $tmp_name );
if ( $checksum === false ) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not calculate checksum for photo' );
if ( $returnOnError === true ) return false ;
Response :: error ( 'Could not calculate checksum for photo!' );
}
2014-08-22 20:02:58 +00:00
2016-02-13 22:36:34 +00:00
// Check if image exists based on checksum
if ( $checksum === false ) {
2014-08-22 20:02:58 +00:00
2016-02-13 22:36:34 +00:00
$checksum = '' ;
$exists = false ;
2014-08-22 20:02:58 +00:00
2016-02-13 22:36:34 +00:00
} else {
2014-08-22 20:14:50 +00:00
2016-02-13 22:36:34 +00:00
$exists = $this -> exists ( $checksum );
2014-08-22 20:02:58 +00:00
2016-02-13 22:36:34 +00:00
if ( $exists !== false ) {
$photo_name = $exists [ 'photo_name' ];
$path = $exists [ 'path' ];
$path_thumb = $exists [ 'path_thumb' ];
$medium = ( $exists [ 'medium' ] === '1' ? 1 : 0 );
$exists = true ;
2014-04-11 22:30:26 +00:00
}
2016-02-13 22:36:34 +00:00
}
2014-08-22 20:02:58 +00:00
2016-02-13 22:36:34 +00:00
if ( $exists === false ) {
2015-06-27 19:03:27 +00:00
2016-02-13 22:36:34 +00:00
// Import if not uploaded via web
if ( ! is_uploaded_file ( $tmp_name )) {
if ( !@ copy ( $tmp_name , $path )) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not copy photo to uploads' );
if ( $returnOnError === true ) return false ;
Response :: error ( 'Could not copy photo to uploads!' );
} else @ unlink ( $tmp_name );
} else {
if ( !@ move_uploaded_file ( $tmp_name , $path )) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not move photo to uploads' );
2015-06-28 21:09:41 +00:00
if ( $returnOnError === true ) return false ;
2016-02-13 22:36:34 +00:00
Response :: error ( 'Could not move photo to uploads!' );
2015-06-27 19:03:27 +00:00
}
2014-08-22 20:02:58 +00:00
}
2014-07-20 14:45:30 +00:00
2016-02-13 22:36:34 +00:00
} else {
2014-04-11 22:30:26 +00:00
2016-02-13 22:36:34 +00:00
// Photo already exists
// Check if the user wants to skip duplicates
if ( Settings :: get ()[ 'skipDuplicates' ] === '1' ) {
Log :: notice ( Database :: get (), __METHOD__ , __LINE__ , 'Skipped upload of existing photo because skipDuplicates is activated' );
if ( $returnOnError === true ) return false ;
Response :: warning ( 'This photo has been skipped because it\'s already in your library.' );
}
2014-08-22 20:02:58 +00:00
2016-02-13 22:36:34 +00:00
}
2014-08-22 20:02:58 +00:00
2016-02-13 22:36:34 +00:00
// Read infos
$info = $this -> getInfo ( $path );
2014-04-11 22:30:26 +00:00
2016-02-13 22:36:34 +00:00
// Use title of file if IPTC title missing
if ( $info [ 'title' ] === '' ) $info [ 'title' ] = substr ( basename ( $file [ 'name' ], $extension ), 0 , 30 );
2014-10-10 22:52:15 +00:00
2016-02-13 22:36:34 +00:00
if ( $exists === false ) {
2014-04-13 10:10:49 +00:00
2016-02-13 22:36:34 +00:00
// Set orientation based on EXIF data
if ( $file [ 'type' ] === 'image/jpeg' && isset ( $info [ 'orientation' ]) && $info [ 'orientation' ] !== '' ) {
$adjustFile = $this -> adjustFile ( $path , $info );
if ( $adjustFile !== false ) $info = $adjustFile ;
else Log :: notice ( Database :: get (), __METHOD__ , __LINE__ , 'Skipped adjustment of photo (' . $info [ 'title' ] . ')' );
2014-05-08 18:50:18 +00:00
}
2014-04-11 22:30:26 +00:00
2016-02-13 22:36:34 +00:00
// Set original date
if ( $info [ 'takestamp' ] !== '' && $info [ 'takestamp' ] !== 0 ) @ touch ( $path , $info [ 'takestamp' ]);
2014-04-11 22:30:26 +00:00
2016-02-13 22:36:34 +00:00
// Create Thumb
if ( ! $this -> createThumb ( $path , $photo_name , $info [ 'type' ], $info [ 'width' ], $info [ 'height' ])) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not create thumbnail for photo' );
2015-06-28 21:09:41 +00:00
if ( $returnOnError === true ) return false ;
2016-02-13 22:36:34 +00:00
Response :: error ( 'Could not create thumbnail for photo!' );
2014-05-08 18:50:18 +00:00
}
2014-04-11 22:30:26 +00:00
2016-02-13 22:36:34 +00:00
// Create Medium
if ( $this -> createMedium ( $path , $photo_name , $info [ 'width' ], $info [ 'height' ])) $medium = 1 ;
else $medium = 0 ;
// Set thumb url
$path_thumb = md5 ( $id ) . '.jpeg' ;
}
// Save to DB
2016-04-17 19:36:58 +00:00
$values = array ( LYCHEE_TABLE_PHOTOS , $id , $info [ 'title' ], $photo_name , $info [ 'description' ], $info [ 'tags' ], $info [ 'type' ], $info [ 'width' ], $info [ 'height' ], $info [ 'size' ], $info [ 'iso' ], $info [ 'aperture' ], $info [ 'make' ], $info [ 'model' ], $info [ 'shutter' ], $info [ 'focal' ], $info [ 'takestamp' ], $path_thumb , $albumID , $public , $star , $checksum , $medium );
2016-02-13 22:36:34 +00:00
$query = Database :: prepare ( Database :: get (), " INSERT INTO ? (id, title, url, description, tags, type, width, height, size, iso, aperture, make, model, shutter, focal, takestamp, thumbUrl, album, public, star, checksum, medium) VALUES ('?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?', '?') " , $values );
$result = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
if ( $result === false ) {
if ( $returnOnError === true ) return false ;
Response :: error ( 'Could not save photo in database!' );
2014-04-11 22:30:26 +00:00
}
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-11 22:30:26 +00:00
2016-03-24 17:40:58 +00:00
return $id ;
2014-04-11 22:30:26 +00:00
}
2016-02-13 16:32:44 +00:00
/**
* @ return array | false Returns a subset of a photo when same photo exists or returns false on failure .
*/
2014-08-22 20:54:33 +00:00
private function exists ( $checksum , $photoID = null ) {
2014-08-22 20:14:50 +00:00
2016-01-30 20:43:57 +00:00
// Exclude $photoID from select when $photoID is set
2016-01-24 21:14:20 +00:00
if ( isset ( $photoID )) $query = Database :: prepare ( Database :: get (), " SELECT id, url, thumbUrl, medium FROM ? WHERE checksum = '?' AND id <> '?' LIMIT 1 " , array ( LYCHEE_TABLE_PHOTOS , $checksum , $photoID ));
2016-02-13 16:32:44 +00:00
else $query = Database :: prepare ( Database :: get (), " SELECT id, url, thumbUrl, medium FROM ? WHERE checksum = '?' LIMIT 1 " , array ( LYCHEE_TABLE_PHOTOS , $checksum ));
2014-08-22 20:54:33 +00:00
2016-01-31 17:49:31 +00:00
$result = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2014-08-22 20:14:50 +00:00
2016-01-31 17:49:31 +00:00
if ( $result === false ) return false ;
2014-08-22 20:14:50 +00:00
if ( $result -> num_rows === 1 ) {
2014-08-22 20:34:59 +00:00
$result = $result -> fetch_object ();
2014-08-22 20:14:50 +00:00
$return = array (
2016-01-30 20:43:57 +00:00
'photo_name' => $result -> url ,
'path' => LYCHEE_UPLOADS_BIG . $result -> url ,
'path_thumb' => $result -> thumbUrl ,
'medium' => $result -> medium
2014-08-22 20:14:50 +00:00
);
return $return ;
}
return false ;
}
2016-02-13 16:32:44 +00:00
/**
* @ return boolean Returns true when successful .
*/
2014-10-18 14:57:09 +00:00
private function createThumb ( $url , $filename , $type , $width , $height ) {
2014-04-11 22:30:26 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-11 22:30:26 +00:00
2016-02-01 08:44:23 +00:00
// Quality of thumbnails
$thumbQuality = 90 ;
2016-01-30 20:43:57 +00:00
// Size of the thumbnail
$newWidth = 200 ;
$newHeight = 200 ;
2014-10-18 14:57:09 +00:00
2016-01-30 20:43:57 +00:00
$photoName = explode ( '.' , $filename );
$newUrl = LYCHEE_UPLOADS_THUMB . $photoName [ 0 ] . '.jpeg' ;
$newUrl2x = LYCHEE_UPLOADS_THUMB . $photoName [ 0 ] . '@2x.jpeg' ;
2014-04-11 22:30:26 +00:00
2016-01-30 20:43:57 +00:00
// Create thumbnails with Imagick
2016-04-17 19:06:25 +00:00
if ( Settings :: hasImagick ()) {
2014-04-11 22:30:26 +00:00
2016-01-30 20:43:57 +00:00
// Read image
2014-04-11 22:30:26 +00:00
$thumb = new Imagick ();
$thumb -> readImage ( $url );
2016-02-01 08:44:23 +00:00
$thumb -> setImageCompressionQuality ( $thumbQuality );
2014-04-11 22:30:26 +00:00
$thumb -> setImageFormat ( 'jpeg' );
2016-01-30 20:43:57 +00:00
// Copy image for 2nd thumb version
2014-04-11 22:30:26 +00:00
$thumb2x = clone $thumb ;
2016-01-30 20:43:57 +00:00
// Create 1st version
2014-10-18 14:57:09 +00:00
$thumb -> cropThumbnailImage ( $newWidth , $newHeight );
2014-04-11 22:30:26 +00:00
$thumb -> writeImage ( $newUrl );
2014-04-19 15:32:33 +00:00
$thumb -> clear ();
$thumb -> destroy ();
2014-04-11 22:30:26 +00:00
2016-01-30 20:43:57 +00:00
// Create 2nd version
2014-10-18 14:57:09 +00:00
$thumb2x -> cropThumbnailImage ( $newWidth * 2 , $newHeight * 2 );
2014-04-11 22:30:26 +00:00
$thumb2x -> writeImage ( $newUrl2x );
$thumb2x -> clear ();
$thumb2x -> destroy ();
} else {
2016-01-30 20:43:57 +00:00
// Create image
$thumb = imagecreatetruecolor ( $newWidth , $newHeight );
$thumb2x = imagecreatetruecolor ( $newWidth * 2 , $newHeight * 2 );
2014-10-10 22:52:15 +00:00
2016-01-30 20:43:57 +00:00
// Set position
2014-10-18 14:57:09 +00:00
if ( $width < $height ) {
2016-01-30 20:43:57 +00:00
$newSize = $width ;
$startWidth = 0 ;
$startHeight = $height / 2 - $width / 2 ;
2014-04-11 22:30:26 +00:00
} else {
2016-01-30 20:43:57 +00:00
$newSize = $height ;
$startWidth = $width / 2 - $height / 2 ;
$startHeight = 0 ;
2014-04-11 22:30:26 +00:00
}
2016-01-30 20:43:57 +00:00
// Create new image
2014-10-18 14:57:09 +00:00
switch ( $type ) {
2016-01-30 20:43:57 +00:00
case 'image/jpeg' : $sourceImg = imagecreatefromjpeg ( $url ); break ;
case 'image/png' : $sourceImg = imagecreatefrompng ( $url ); break ;
case 'image/gif' : $sourceImg = imagecreatefromgif ( $url ); break ;
2016-01-31 17:49:31 +00:00
default : Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Type of photo is not supported' );
2016-01-30 20:43:57 +00:00
return false ;
break ;
2014-04-11 22:30:26 +00:00
}
2016-01-30 20:43:57 +00:00
// Create thumb
2016-01-26 14:31:53 +00:00
fastImageCopyResampled ( $thumb , $sourceImg , 0 , 0 , $startWidth , $startHeight , $newWidth , $newHeight , $newSize , $newSize );
2016-02-01 08:44:23 +00:00
imagejpeg ( $thumb , $newUrl , $thumbQuality );
2014-04-19 15:32:33 +00:00
imagedestroy ( $thumb );
2016-01-30 20:43:57 +00:00
// Create retina thumb
2016-01-26 14:31:53 +00:00
fastImageCopyResampled ( $thumb2x , $sourceImg , 0 , 0 , $startWidth , $startHeight , $newWidth * 2 , $newHeight * 2 , $newSize , $newSize );
2016-02-01 08:44:23 +00:00
imagejpeg ( $thumb2x , $newUrl2x , $thumbQuality );
2014-04-19 15:32:33 +00:00
imagedestroy ( $thumb2x );
2016-01-30 20:43:57 +00:00
// Free memory
2014-04-19 15:32:33 +00:00
imagedestroy ( $sourceImg );
2014-04-11 22:30:26 +00:00
}
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-11 22:30:26 +00:00
return true ;
}
2016-02-13 16:32:44 +00:00
/**
* Creates a smaller version of a photo when its size is bigger than a preset size .
2016-02-14 13:09:49 +00:00
* Photo must be big enough and Imagick must be installed and activated .
2016-02-13 16:32:44 +00:00
* @ return boolean Returns true when successful .
*/
2014-10-11 14:09:10 +00:00
private function createMedium ( $url , $filename , $width , $height ) {
2014-10-10 22:52:15 +00:00
2016-01-30 20:43:57 +00:00
// Excepts the following:
// (string) $url = Path to the photo-file
// (string) $filename = Name of the photo-file
// (int) $width = Width of the photo
// (int) $height = Height of the photo
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-10-10 22:52:15 +00:00
2016-01-30 20:43:57 +00:00
// Set to true when creation of medium-photo failed
2014-12-06 13:29:17 +00:00
$error = false ;
2016-01-30 20:43:57 +00:00
// Size of the medium-photo
// When changing these values,
// also change the size detection in the front-end
$newWidth = 1920 ;
$newHeight = 1080 ;
2014-10-11 14:09:10 +00:00
2016-01-30 20:43:57 +00:00
// Check permissions
2014-12-06 13:29:17 +00:00
if ( hasPermissions ( LYCHEE_UPLOADS_MEDIUM ) === false ) {
2016-01-30 20:43:57 +00:00
// Permissions are missing
2016-01-31 17:49:31 +00:00
Log :: notice ( Database :: get (), __METHOD__ , __LINE__ , 'Skipped creation of medium-photo, because uploads/medium/ is missing or not readable and writable.' );
2014-12-06 13:29:17 +00:00
$error = true ;
}
2016-01-30 20:43:57 +00:00
// Is photo big enough?
// Is Imagick installed and activated?
2014-12-06 13:29:17 +00:00
if (( $error === false ) &&
( $width > $newWidth || $height > $newHeight ) &&
2016-01-24 21:14:20 +00:00
( extension_loaded ( 'imagick' ) && Settings :: get ()[ 'imagick' ] === '1' )) {
2014-10-10 22:52:15 +00:00
$newUrl = LYCHEE_UPLOADS_MEDIUM . $filename ;
2016-01-30 20:43:57 +00:00
// Read image
2014-10-10 22:52:15 +00:00
$medium = new Imagick ();
$medium -> readImage ( $url );
2014-12-06 13:29:17 +00:00
2016-01-30 20:43:57 +00:00
// Adjust image
2014-10-10 22:52:15 +00:00
$medium -> scaleImage ( $newWidth , $newHeight , true );
2014-12-06 13:29:17 +00:00
2016-01-30 20:43:57 +00:00
// Save image
2014-12-06 13:29:17 +00:00
try { $medium -> writeImage ( $newUrl ); }
catch ( ImagickException $err ) {
2016-02-06 22:23:10 +00:00
Log :: notice ( Database :: get (), __METHOD__ , __LINE__ , 'Could not save medium-photo (' . $err -> getMessage () . ')' );
2014-12-06 13:29:17 +00:00
$error = true ;
}
2014-10-10 22:52:15 +00:00
$medium -> clear ();
$medium -> destroy ();
} else {
2016-01-30 20:43:57 +00:00
// Photo too small or
// Medium is deactivated or
// Imagick not installed
2014-10-10 22:52:15 +00:00
$error = true ;
}
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-10-10 22:52:15 +00:00
if ( $error === true ) return false ;
return true ;
}
2016-02-13 16:32:44 +00:00
/**
* Rotates and flips a photo based on its EXIF orientation .
* @ return array | false Returns an array with the new orientation , width , height or false on failure .
*/
2016-01-30 00:06:21 +00:00
public function adjustFile ( $path , array $info ) {
2014-04-12 12:15:21 +00:00
2016-01-30 20:43:57 +00:00
// Excepts the following:
// (string) $path = Path to the photo-file
// (array) $info = ['orientation', 'width', 'height']
2015-04-06 17:08:58 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-12 12:15:21 +00:00
2014-10-18 14:57:09 +00:00
$swapSize = false ;
2016-01-24 21:14:20 +00:00
if ( extension_loaded ( 'imagick' ) && Settings :: get ()[ 'imagick' ] === '1' ) {
2014-04-12 12:15:21 +00:00
switch ( $info [ 'orientation' ]) {
case 3 :
$rotateImage = 180 ;
break ;
case 6 :
2016-01-30 20:43:57 +00:00
$rotateImage = 90 ;
$swapSize = true ;
2014-04-12 12:15:21 +00:00
break ;
case 8 :
2016-01-30 20:43:57 +00:00
$rotateImage = 270 ;
$swapSize = true ;
2014-10-18 14:57:09 +00:00
break ;
default :
return false ;
2014-04-12 12:15:21 +00:00
break ;
}
2014-04-19 15:32:33 +00:00
if ( $rotateImage !== 0 ) {
2014-04-12 12:15:21 +00:00
$image = new Imagick ();
$image -> readImage ( $path );
$image -> rotateImage ( new ImagickPixel (), $rotateImage );
2014-10-18 14:57:09 +00:00
$image -> setImageOrientation ( 1 );
2014-04-12 12:15:21 +00:00
$image -> writeImage ( $path );
$image -> clear ();
$image -> destroy ();
}
} else {
2016-01-30 20:43:57 +00:00
$newWidth = $info [ 'width' ];
$newHeight = $info [ 'height' ];
$sourceImg = imagecreatefromjpeg ( $path );
2014-04-12 12:15:21 +00:00
switch ( $info [ 'orientation' ]) {
case 2 :
2016-01-30 20:43:57 +00:00
// mirror
// not yet implemented
2014-10-18 14:57:09 +00:00
return false ;
2014-04-12 12:15:21 +00:00
break ;
case 3 :
2016-01-30 20:43:57 +00:00
$sourceImg = imagerotate ( $sourceImg , - 180 , 0 );
2014-04-12 12:15:21 +00:00
break ;
case 4 :
2016-01-30 20:43:57 +00:00
// rotate 180 and mirror
// not yet implemented
2014-10-18 14:57:09 +00:00
return false ;
2014-04-12 12:15:21 +00:00
break ;
case 5 :
2016-01-30 20:43:57 +00:00
// rotate 90 and mirror
// not yet implemented
2014-10-18 14:57:09 +00:00
return false ;
2014-04-12 12:15:21 +00:00
break ;
case 6 :
2016-01-30 20:43:57 +00:00
$sourceImg = imagerotate ( $sourceImg , - 90 , 0 );
$newWidth = $info [ 'height' ];
$newHeight = $info [ 'width' ];
$swapSize = true ;
2014-04-12 12:15:21 +00:00
break ;
case 7 :
2016-01-30 20:43:57 +00:00
// rotate -90 and mirror
// not yet implemented
2014-10-18 14:57:09 +00:00
return false ;
2014-04-12 12:15:21 +00:00
break ;
case 8 :
2016-01-30 20:43:57 +00:00
$sourceImg = imagerotate ( $sourceImg , 90 , 0 );
$newWidth = $info [ 'height' ];
$newHeight = $info [ 'width' ];
$swapSize = true ;
2014-04-12 12:15:21 +00:00
break ;
2014-10-18 14:57:09 +00:00
default :
return false ;
break ;
2014-04-12 12:15:21 +00:00
2014-10-18 14:57:09 +00:00
}
2014-04-19 15:32:33 +00:00
2016-01-30 20:43:57 +00:00
// Recreate photo
2014-10-18 14:57:09 +00:00
$newSourceImg = imagecreatetruecolor ( $newWidth , $newHeight );
imagecopyresampled ( $newSourceImg , $sourceImg , 0 , 0 , 0 , 0 , $newWidth , $newHeight , $newWidth , $newHeight );
imagejpeg ( $newSourceImg , $path , 100 );
2014-04-19 15:32:33 +00:00
2016-01-30 20:43:57 +00:00
// Free memory
2014-10-18 14:57:09 +00:00
imagedestroy ( $sourceImg );
imagedestroy ( $newSourceImg );
2014-04-12 12:15:21 +00:00
}
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-12 12:15:21 +00:00
2016-01-30 20:43:57 +00:00
// SwapSize should be true when the image has been rotated
// Return new dimensions in this case
2014-10-18 14:57:09 +00:00
if ( $swapSize === true ) {
2016-01-30 20:43:57 +00:00
$swapSize = $info [ 'width' ];
$info [ 'width' ] = $info [ 'height' ];
$info [ 'height' ] = $swapSize ;
2014-10-18 14:57:09 +00:00
}
return $info ;
2014-04-12 12:15:21 +00:00
}
2016-02-13 16:32:44 +00:00
/**
* Rurns photo - attributes into a front - end friendly format . Note that some attributes remain unchanged .
* @ return array Returns photo - attributes in a normalized structure .
*/
2016-01-30 00:06:21 +00:00
public static function prepareData ( array $data ) {
2015-03-11 23:11:16 +00:00
2016-01-30 20:43:57 +00:00
// Excepts the following:
// (array) $data = ['id', 'title', 'tags', 'public', 'star', 'album', 'thumbUrl', 'takestamp', 'url']
2015-03-11 23:11:16 +00:00
2016-01-30 20:43:57 +00:00
// Init
2015-03-11 23:11:16 +00:00
$photo = null ;
2016-01-30 20:43:57 +00:00
// Set unchanged attributes
$photo [ 'id' ] = $data [ 'id' ];
$photo [ 'title' ] = $data [ 'title' ];
$photo [ 'tags' ] = $data [ 'tags' ];
$photo [ 'public' ] = $data [ 'public' ];
$photo [ 'star' ] = $data [ 'star' ];
$photo [ 'album' ] = $data [ 'album' ];
2015-03-11 23:11:16 +00:00
2016-01-30 20:43:57 +00:00
// Parse urls
$photo [ 'thumbUrl' ] = LYCHEE_URL_UPLOADS_THUMB . $data [ 'thumbUrl' ];
$photo [ 'url' ] = LYCHEE_URL_UPLOADS_BIG . $data [ 'url' ];
2015-03-11 23:11:16 +00:00
2016-01-30 20:43:57 +00:00
// Use takestamp as sysdate when possible
2015-03-11 23:11:16 +00:00
if ( isset ( $data [ 'takestamp' ]) && $data [ 'takestamp' ] !== '0' ) {
2016-01-30 20:43:57 +00:00
// Use takestamp
$photo [ 'cameraDate' ] = '1' ;
2016-03-15 12:28:39 +00:00
$photo [ 'sysdate' ] = strftime ( '%d %B %Y' , $data [ 'takestamp' ]);
2015-03-11 23:11:16 +00:00
} else {
2016-01-30 20:43:57 +00:00
// Use sysstamp from the id
$photo [ 'cameraDate' ] = '0' ;
2016-03-15 12:28:39 +00:00
$photo [ 'sysdate' ] = strftime ( '%d %B %Y' , substr ( $data [ 'id' ], 0 , - 4 ));
2015-03-11 23:11:16 +00:00
}
return $photo ;
}
2016-02-13 16:32:44 +00:00
/**
* @ return array | false Returns an array with information about the photo or false on failure .
*/
2014-04-05 14:02:53 +00:00
public function get ( $albumID ) {
2016-01-30 20:43:57 +00:00
// Excepts the following:
// (string) $albumID = Album which is currently visible to the user
2015-04-06 16:48:52 +00:00
2016-01-30 20:43:57 +00:00
// Check dependencies
2016-01-30 20:33:31 +00:00
Validator :: required ( isset ( $this -> photoIDs ), __METHOD__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-30 20:43:57 +00:00
// Get photo
$query = Database :: prepare ( Database :: get (), " SELECT * FROM ? WHERE id = '?' LIMIT 1 " , array ( LYCHEE_TABLE_PHOTOS , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$photos = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2016-02-13 16:32:44 +00:00
if ( $photos === false ) return false ;
2016-01-31 17:49:31 +00:00
// Get photo object
$photo = $photos -> fetch_assoc ();
2014-04-05 14:02:53 +00:00
2016-02-13 22:37:25 +00:00
// Photo not found?
if ( $photo === null ) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not find specified photo' );
return false ;
}
2016-01-30 20:43:57 +00:00
// Parse photo
2016-03-15 12:28:39 +00:00
$photo [ 'sysdate' ] = strftime ( '%d %b. %Y' , substr ( $photo [ 'id' ], 0 , - 4 ));
if ( strlen ( $photo [ 'takestamp' ]) > 1 ) $photo [ 'takedate' ] = strftime ( '%d %b. %Y' , $photo [ 'takestamp' ]);
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Parse medium
if ( $photo [ 'medium' ] === '1' ) $photo [ 'medium' ] = LYCHEE_URL_UPLOADS_MEDIUM . $photo [ 'url' ];
else $photo [ 'medium' ] = '' ;
2014-10-11 14:09:10 +00:00
2016-01-30 20:43:57 +00:00
// Parse paths
$photo [ 'url' ] = LYCHEE_URL_UPLOADS_BIG . $photo [ 'url' ];
$photo [ 'thumbUrl' ] = LYCHEE_URL_UPLOADS_THUMB . $photo [ 'thumbUrl' ];
2014-07-12 18:12:38 +00:00
2014-04-05 14:02:53 +00:00
if ( $albumID != 'false' ) {
2016-01-30 20:43:57 +00:00
// Only show photo as public when parent album is public
// Check if parent album is not 'Unsorted'
2015-04-06 16:48:52 +00:00
if ( $photo [ 'album' ] !== '0' ) {
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Get album
$query = Database :: prepare ( Database :: get (), " SELECT public FROM ? WHERE id = '?' LIMIT 1 " , array ( LYCHEE_TABLE_ALBUMS , $photo [ 'album' ]));
2016-01-31 17:49:31 +00:00
$albums = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2016-02-13 16:32:44 +00:00
if ( $albums === false ) return false ;
2016-01-31 17:49:31 +00:00
// Get album object
$album = $albums -> fetch_assoc ();
2014-04-05 14:02:53 +00:00
2016-02-13 22:37:25 +00:00
// Photo not found?
if ( $photo === null ) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not find specified album' );
return false ;
}
2016-01-30 20:43:57 +00:00
// Parse album
2015-04-06 16:48:52 +00:00
$photo [ 'public' ] = ( $album [ 'public' ] === '1' ? '2' : $photo [ 'public' ]);
2014-04-05 14:02:53 +00:00
}
2016-01-30 20:43:57 +00:00
$photo [ 'original_album' ] = $photo [ 'album' ];
$photo [ 'album' ] = $albumID ;
2014-04-05 14:02:53 +00:00
}
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-05 14:25:49 +00:00
2014-04-05 14:02:53 +00:00
return $photo ;
}
2016-02-13 16:32:44 +00:00
/**
* Reads and parses information and metadata out of a photo .
* @ return array Returns an array of photo information and metadata .
*/
2014-08-26 18:26:14 +00:00
public function getInfo ( $url ) {
2014-04-11 22:30:26 +00:00
2016-01-30 20:43:57 +00:00
// Functions returns information and metadata of a photo
// Excepts the following:
// (string) $url = Path to photo-file
// Returns the following:
// (array) $return
2015-04-06 16:48:52 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-11 22:30:26 +00:00
2016-01-30 20:43:57 +00:00
$iptcArray = array ();
$info = getimagesize ( $url , $iptcArray );
2014-04-11 22:30:26 +00:00
2016-01-30 20:43:57 +00:00
// General information
2016-04-17 19:06:25 +00:00
$return [ 'type' ] = $info [ 'mime' ];
$return [ 'width' ] = $info [ 0 ];
$return [ 'height' ] = $info [ 1 ];
$return [ 'title' ] = '' ;
$return [ 'description' ] = '' ;
$return [ 'orientation' ] = '' ;
$return [ 'iso' ] = '' ;
$return [ 'aperture' ] = '' ;
$return [ 'make' ] = '' ;
$return [ 'model' ] = '' ;
$return [ 'shutter' ] = '' ;
$return [ 'focal' ] = '' ;
$return [ 'takestamp' ] = 0 ;
$return [ 'lens' ] = '' ;
2016-04-18 08:49:31 +00:00
$return [ 'tags' ] = '' ;
2016-04-17 19:06:25 +00:00
$return [ 'position' ] = '' ;
$return [ 'latitude' ] = '' ;
$return [ 'longitude' ] = '' ;
$return [ 'altitude' ] = '' ;
2014-04-11 22:30:26 +00:00
2016-01-30 20:43:57 +00:00
// Size
2014-04-11 22:30:26 +00:00
$size = filesize ( $url ) / 1024 ;
if ( $size >= 1024 ) $return [ 'size' ] = round ( $size / 1024 , 1 ) . ' MB' ;
else $return [ 'size' ] = round ( $size , 1 ) . ' KB' ;
2016-01-30 20:43:57 +00:00
// IPTC Metadata
2016-04-17 19:06:25 +00:00
// See https://www.iptc.org/std/IIM/4.2/specification/IIMV4.2.pdf for mapping
2014-04-11 22:30:26 +00:00
if ( isset ( $iptcArray [ 'APP13' ])) {
$iptcInfo = iptcparse ( $iptcArray [ 'APP13' ]);
if ( is_array ( $iptcInfo )) {
2016-04-17 19:06:25 +00:00
// Title
if ( ! empty ( $iptcInfo [ '2#105' ][ 0 ])) $return [ 'title' ] = $iptcInfo [ '2#105' ][ 0 ];
else if ( ! empty ( $iptcInfo [ '2#005' ][ 0 ])) $return [ 'title' ] = $iptcInfo [ '2#005' ][ 0 ];
// Description
if ( ! empty ( $iptcInfo [ '2#120' ][ 0 ])) $return [ 'description' ] = $iptcInfo [ '2#120' ][ 0 ];
2014-04-11 22:30:26 +00:00
2016-04-17 19:06:25 +00:00
// Tags
2016-04-17 19:45:51 +00:00
if ( ! empty ( $iptcInfo [ '2#025' ])) $return [ 'tags' ] = implode ( ',' , $iptcInfo [ '2#025' ]);
2014-04-11 22:30:26 +00:00
2016-04-17 19:06:25 +00:00
// Position
$fields = array ();
2016-04-17 20:23:05 +00:00
if ( ! empty ( $iptcInfo [ '2#090' ])) $fields [] = trim ( $iptcInfo [ '2#090' ][ 0 ]);
if ( ! empty ( $iptcInfo [ '2#092' ])) $fields [] = trim ( $iptcInfo [ '2#092' ][ 0 ]);
if ( ! empty ( $iptcInfo [ '2#095' ])) $fields [] = trim ( $iptcInfo [ '2#095' ][ 0 ]);
if ( ! empty ( $iptcInfo [ '2#101' ])) $fields [] = trim ( $iptcInfo [ '2#101' ][ 0 ]);
2016-04-17 19:06:25 +00:00
if ( ! empty ( $fields )) $return [ 'position' ] = implode ( ', ' , $fields );
2014-08-26 14:07:59 +00:00
2014-04-11 22:30:26 +00:00
}
}
2016-01-30 20:43:57 +00:00
// Read EXIF
2016-04-18 08:49:31 +00:00
if ( $info [ 'mime' ] == 'image/jpeg' ) $exif = @ exif_read_data ( $url , 'EXIF' , false , false );
2014-04-11 22:30:26 +00:00
else $exif = false ;
2016-01-30 20:43:57 +00:00
// EXIF Metadata
2014-04-11 22:30:26 +00:00
if ( $exif !== false ) {
2016-04-18 07:40:52 +00:00
// Orientation
2014-04-11 22:30:26 +00:00
if ( isset ( $exif [ 'Orientation' ])) $return [ 'orientation' ] = $exif [ 'Orientation' ];
else if ( isset ( $exif [ 'IFD0' ][ 'Orientation' ])) $return [ 'orientation' ] = $exif [ 'IFD0' ][ 'Orientation' ];
2016-04-18 07:40:52 +00:00
// ISO
2016-04-17 19:06:25 +00:00
if ( ! empty ( $exif [ 'ISOSpeedRatings' ])) $return [ 'iso' ] = $exif [ 'ISOSpeedRatings' ];
2014-04-11 22:30:26 +00:00
2016-04-18 07:40:52 +00:00
// Aperture
2016-04-17 19:06:25 +00:00
if ( ! empty ( $exif [ 'COMPUTED' ][ 'ApertureFNumber' ])) $return [ 'aperture' ] = $exif [ 'COMPUTED' ][ 'ApertureFNumber' ];
2014-04-11 22:30:26 +00:00
2016-04-18 07:40:52 +00:00
// Make
2016-04-17 19:06:25 +00:00
if ( ! empty ( $exif [ 'Make' ])) $return [ 'make' ] = trim ( $exif [ 'Make' ]);
2014-04-11 22:30:26 +00:00
2016-04-18 07:40:52 +00:00
// Model
2016-04-17 19:06:25 +00:00
if ( ! empty ( $exif [ 'Model' ])) $return [ 'model' ] = trim ( $exif [ 'Model' ]);
2014-04-11 22:30:26 +00:00
2016-04-18 07:40:52 +00:00
// Exposure
2016-04-17 19:06:25 +00:00
if ( ! empty ( $exif [ 'ExposureTime' ])) $return [ 'shutter' ] = $exif [ 'ExposureTime' ] . ' s' ;
2014-04-11 22:30:26 +00:00
2016-04-18 07:40:52 +00:00
// Focal Length
2016-04-17 20:11:08 +00:00
if ( ! empty ( $exif [ 'FocalLength' ])) {
if ( strpos ( $exif [ 'FocalLength' ], '/' ) !== false ) {
$temp = explode ( '/' , $exif [ 'FocalLength' ], 2 );
2014-12-21 21:50:56 +00:00
$temp = $temp [ 0 ] / $temp [ 1 ];
$temp = round ( $temp , 1 );
$return [ 'focal' ] = $temp . ' mm' ;
2016-04-18 07:40:52 +00:00
} else {
$return [ 'focal' ] = $exif [ 'FocalLength' ] . ' mm' ;
2014-12-21 21:50:56 +00:00
}
}
2014-04-11 22:30:26 +00:00
2016-04-18 07:40:52 +00:00
// Takestamp
2016-04-17 19:06:25 +00:00
if ( ! empty ( $exif [ 'DateTimeOriginal' ])) $return [ 'takestamp' ] = strtotime ( $exif [ 'DateTimeOriginal' ]);
// Lens field from Lightroom
if ( ! empty ( $exif [ 'UndefinedTag:0xA434' ])) $return [ 'lens' ] = trim ( $exif [ 'UndefinedTag:0xA434' ]);
2016-04-17 19:59:50 +00:00
// Deal with GPS coordinates
2016-04-18 07:40:52 +00:00
if ( ! empty ( $exif [ 'GPSLatitude' ]) && ! empty ( $exif [ 'GPSLatitudeRef' ])) $return [ 'latitude' ] = getGPSCoordinate ( $exif [ 'GPSLatitude' ], $exif [ 'GPSLatitudeRef' ]);
if ( ! empty ( $exif [ 'GPSLongitude' ]) && ! empty ( $exif [ 'GPSLongitudeRef' ])) $return [ 'longitude' ] = getGPSCoordinate ( $exif [ 'GPSLongitude' ], $exif [ 'GPSLongitudeRef' ]);
2014-04-11 22:30:26 +00:00
}
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-11 22:30:26 +00:00
return $return ;
}
2016-02-13 16:32:44 +00:00
/**
* Starts a download of a photo .
* @ return resource | boolean Sends a ZIP - file or returns false on failure .
*/
2014-04-05 14:02:53 +00:00
public function getArchive () {
2016-01-30 20:43:57 +00:00
// Check dependencies
2016-01-30 20:33:31 +00:00
Validator :: required ( isset ( $this -> photoIDs ), __METHOD__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-30 20:43:57 +00:00
// Get photo
$query = Database :: prepare ( Database :: get (), " SELECT title, url FROM ? WHERE id = '?' LIMIT 1 " , array ( LYCHEE_TABLE_PHOTOS , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$photos = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2014-04-05 14:02:53 +00:00
2016-02-13 16:32:44 +00:00
if ( $photos === false ) return false ;
2015-04-06 16:48:52 +00:00
2016-01-31 14:44:54 +00:00
// Get photo object
$photo = $photos -> fetch_object ();
2016-02-13 22:37:25 +00:00
// Photo not found?
2015-04-06 16:48:52 +00:00
if ( $photo === null ) {
2016-01-31 17:49:31 +00:00
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not find specified photo' );
2016-02-13 16:32:44 +00:00
return false ;
2015-04-06 16:48:52 +00:00
}
2016-01-30 20:43:57 +00:00
// Get extension
2016-03-17 18:21:59 +00:00
$extension = getExtension ( $photo -> url , false );
2016-03-17 08:35:12 +00:00
if ( empty ( $extension ) === true ) {
2016-01-31 17:49:31 +00:00
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Invalid photo extension' );
2014-05-08 18:50:18 +00:00
return false ;
}
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Illicit chars
$badChars = array_merge (
array_map ( 'chr' , range ( 0 , 31 )),
array ( " < " , " > " , " : " , '"' , " / " , " \\ " , " | " , " ? " , " * " )
);
2014-08-30 17:18:09 +00:00
2016-01-30 20:43:57 +00:00
// Parse title
2014-04-05 14:02:53 +00:00
if ( $photo -> title == '' ) $photo -> title = 'Untitled' ;
2016-01-30 20:43:57 +00:00
// Escape title
2014-08-30 18:03:38 +00:00
$photo -> title = str_replace ( $badChars , '' , $photo -> title );
2014-08-30 17:18:09 +00:00
2016-01-30 20:43:57 +00:00
// Set headers
2014-04-05 14:02:53 +00:00
header ( " Content-Type: application/octet-stream " );
2014-05-20 10:31:48 +00:00
header ( " Content-Disposition: attachment; filename= \" " . $photo -> title . $extension . " \" " );
2014-04-13 12:08:18 +00:00
header ( " Content-Length: " . filesize ( LYCHEE_UPLOADS_BIG . $photo -> url ));
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Send file
2014-04-13 12:08:18 +00:00
readfile ( LYCHEE_UPLOADS_BIG . $photo -> url );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-05 14:25:49 +00:00
2014-04-05 14:02:53 +00:00
return true ;
}
2016-02-13 16:32:44 +00:00
/**
* Sets the title of a photo .
* @ return boolean Returns true when successful .
*/
2016-03-06 15:50:56 +00:00
public function setTitle ( $title = 'Untitled' ) {
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Check dependencies
2016-01-30 20:33:31 +00:00
Validator :: required ( isset ( $this -> photoIDs ), __METHOD__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-30 20:43:57 +00:00
// Set title
$query = Database :: prepare ( Database :: get (), " UPDATE ? SET title = '?' WHERE id IN (?) " , array ( LYCHEE_TABLE_PHOTOS , $title , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$result = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-31 17:49:31 +00:00
if ( $result === false ) return false ;
2014-04-05 14:02:53 +00:00
return true ;
}
2016-02-13 16:32:44 +00:00
/**
* Sets the description of a photo .
* @ return boolean Returns true when successful .
*/
2014-04-25 08:13:43 +00:00
public function setDescription ( $description ) {
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Check dependencies
2016-01-30 20:33:31 +00:00
Validator :: required ( isset ( $this -> photoIDs ), __METHOD__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-30 20:43:57 +00:00
// Set description
$query = Database :: prepare ( Database :: get (), " UPDATE ? SET description = '?' WHERE id IN ('?') " , array ( LYCHEE_TABLE_PHOTOS , $description , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$result = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-31 17:49:31 +00:00
if ( $result === false ) return false ;
2014-04-05 14:02:53 +00:00
return true ;
}
2016-02-13 16:32:44 +00:00
/**
* Toggles the star property of a photo .
* @ return boolean Returns true when successful .
*/
2014-04-05 14:02:53 +00:00
public function setStar () {
2016-01-30 20:43:57 +00:00
// Check dependencies
2016-01-30 20:33:31 +00:00
Validator :: required ( isset ( $this -> photoIDs ), __METHOD__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-30 20:43:57 +00:00
// Init vars
$error = false ;
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Get photos
$query = Database :: prepare ( Database :: get (), " SELECT id, star FROM ? WHERE id IN (?) " , array ( LYCHEE_TABLE_PHOTOS , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$photos = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
if ( $photos === false ) return false ;
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// For each photo
2014-04-05 14:02:53 +00:00
while ( $photo = $photos -> fetch_object ()) {
2016-01-30 20:43:57 +00:00
// Invert star
2014-04-05 14:02:53 +00:00
$star = ( $photo -> star == 0 ? 1 : 0 );
2016-01-30 20:43:57 +00:00
// Set star
2016-01-31 14:44:54 +00:00
$query = Database :: prepare ( Database :: get (), " UPDATE ? SET star = '?' WHERE id = '?' " , array ( LYCHEE_TABLE_PHOTOS , $star , $photo -> id ));
2016-01-31 17:49:31 +00:00
$result = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2016-01-31 14:44:54 +00:00
if ( $result === false ) $error = true ;
2014-04-05 14:02:53 +00:00
}
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-31 17:49:31 +00:00
if ( $error === true ) return false ;
2014-04-05 14:02:53 +00:00
return true ;
}
2016-02-13 16:32:44 +00:00
/**
* Checks if photo or parent album is public .
* @ return integer 0 = Photo private and parent album private
* 1 = Album public , but password incorrect
* 2 = Photo public or album public and password correct
*/
2014-04-25 08:13:43 +00:00
public function getPublic ( $password ) {
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Check dependencies
2016-01-30 20:33:31 +00:00
Validator :: required ( isset ( $this -> photoIDs ), __METHOD__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-30 20:43:57 +00:00
// Get photo
$query = Database :: prepare ( Database :: get (), " SELECT public, album FROM ? WHERE id = '?' LIMIT 1 " , array ( LYCHEE_TABLE_PHOTOS , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$photos = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
if ( $photos === false ) return 0 ;
// Get photo object
$photo = $photos -> fetch_object ();
2014-04-05 14:02:53 +00:00
2016-02-13 22:37:25 +00:00
// Photo not found?
if ( $photo === null ) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not find specified photo' );
return false ;
}
2016-01-30 20:43:57 +00:00
// Check if public
2015-04-06 16:48:52 +00:00
if ( $photo -> public === '1' ) {
2016-01-30 20:43:57 +00:00
// Photo public
2015-04-06 16:48:52 +00:00
return 2 ;
} else {
2016-01-30 20:43:57 +00:00
// Check if album public
$album = new Album ( $photo -> album );
$agP = $album -> getPublic ();
$acP = $album -> checkPassword ( $password );
2015-04-06 16:48:52 +00:00
2016-01-30 20:43:57 +00:00
// Album public and password correct
2015-04-06 16:48:52 +00:00
if ( $agP === true && $acP === true ) return 2 ;
2016-01-30 20:43:57 +00:00
// Album public, but password incorrect
2015-04-06 16:48:52 +00:00
if ( $agP === true && $acP === false ) return 1 ;
2014-04-05 14:02:53 +00:00
}
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-30 20:43:57 +00:00
// Photo private
2015-04-06 16:48:52 +00:00
return 0 ;
2014-04-05 14:02:53 +00:00
}
2016-02-13 16:32:44 +00:00
/**
* Toggles the public property of a photo .
* @ return boolean Returns true when successful .
*/
2014-04-05 14:02:53 +00:00
public function setPublic () {
2016-01-30 20:43:57 +00:00
// Check dependencies
2016-01-30 20:33:31 +00:00
Validator :: required ( isset ( $this -> photoIDs ), __METHOD__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-30 20:43:57 +00:00
// Get public
$query = Database :: prepare ( Database :: get (), " SELECT public FROM ? WHERE id = '?' LIMIT 1 " , array ( LYCHEE_TABLE_PHOTOS , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$photos = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
if ( $photos === false ) return false ;
// Get photo object
$photo = $photos -> fetch_object ();
2014-04-05 14:02:53 +00:00
2016-02-13 22:37:25 +00:00
// Photo not found?
if ( $photo === null ) {
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not find specified photo' );
return false ;
}
2016-01-30 20:43:57 +00:00
// Invert public
2014-04-05 14:02:53 +00:00
$public = ( $photo -> public == 0 ? 1 : 0 );
2016-01-30 20:43:57 +00:00
// Set public
$query = Database :: prepare ( Database :: get (), " UPDATE ? SET public = '?' WHERE id = '?' " , array ( LYCHEE_TABLE_PHOTOS , $public , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$result = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-31 17:49:31 +00:00
if ( $result === false ) return false ;
2014-04-05 14:02:53 +00:00
return true ;
}
2016-02-13 16:32:44 +00:00
/**
* Sets the parent album of a photo .
* @ return boolean Returns true when successful .
*/
2014-04-05 14:02:53 +00:00
function setAlbum ( $albumID ) {
2016-01-30 20:43:57 +00:00
// Check dependencies
2016-01-30 20:33:31 +00:00
Validator :: required ( isset ( $this -> photoIDs ), __METHOD__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-30 20:43:57 +00:00
// Set album
$query = Database :: prepare ( Database :: get (), " UPDATE ? SET album = '?' WHERE id IN (?) " , array ( LYCHEE_TABLE_PHOTOS , $albumID , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$result = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-31 17:49:31 +00:00
if ( $result === false ) return false ;
2014-04-05 14:02:53 +00:00
return true ;
}
2016-02-13 16:32:44 +00:00
/**
* Sets the tags of a photo .
* @ return boolean Returns true when successful .
*/
2014-04-05 14:02:53 +00:00
public function setTags ( $tags ) {
2016-01-30 20:43:57 +00:00
// Excepts the following:
// (string) $tags = Comma separated list of tags with a maximum length of 1000 chars
2015-04-06 16:48:52 +00:00
2016-01-30 20:43:57 +00:00
// Check dependencies
2016-01-30 20:33:31 +00:00
Validator :: required ( isset ( $this -> photoIDs ), __METHOD__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-30 20:43:57 +00:00
// Parse tags
2014-04-05 14:02:53 +00:00
$tags = preg_replace ( '/(\ ,\ )|(\ ,)|(,\ )|(,{1,}\ {0,})|(,$|^,)/' , ',' , $tags );
2014-04-18 19:27:52 +00:00
$tags = preg_replace ( '/,$|^,|(\ ){0,}$/' , '' , $tags );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Set tags
$query = Database :: prepare ( Database :: get (), " UPDATE ? SET tags = '?' WHERE id IN (?) " , array ( LYCHEE_TABLE_PHOTOS , $tags , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$result = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-31 17:49:31 +00:00
if ( $result === false ) return false ;
2014-04-05 14:02:53 +00:00
return true ;
}
2016-02-13 16:32:44 +00:00
/**
* Duplicates a photo .
* @ return boolean Returns true when successful .
*/
2014-08-22 21:31:09 +00:00
public function duplicate () {
2016-01-30 20:43:57 +00:00
// Check dependencies
2016-01-30 20:33:31 +00:00
Validator :: required ( isset ( $this -> photoIDs ), __METHOD__ );
2014-08-22 21:31:09 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-08-22 21:31:09 +00:00
2016-01-31 17:49:31 +00:00
// Init vars
$error = false ;
2016-01-30 20:43:57 +00:00
// Get photos
$query = Database :: prepare ( Database :: get (), " SELECT id, checksum FROM ? WHERE id IN (?) " , array ( LYCHEE_TABLE_PHOTOS , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$photos = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2016-01-31 14:44:54 +00:00
2016-01-31 17:49:31 +00:00
if ( $photos === false ) return false ;
2014-08-22 21:31:09 +00:00
2016-01-30 20:43:57 +00:00
// For each photo
2014-08-22 21:31:09 +00:00
while ( $photo = $photos -> fetch_object ()) {
2016-01-30 20:43:57 +00:00
// Generate id
2016-03-12 22:51:33 +00:00
$id = generateID ();
2014-08-22 21:31:09 +00:00
2016-01-30 20:43:57 +00:00
// Duplicate entry
2016-01-31 14:44:54 +00:00
$values = array ( LYCHEE_TABLE_PHOTOS , $id , LYCHEE_TABLE_PHOTOS , $photo -> id );
$query = Database :: prepare ( Database :: get (), " INSERT INTO ? (id, title, url, description, tags, type, width, height, size, iso, aperture, make, model, shutter, focal, takestamp, thumbUrl, album, public, star, checksum) SELECT '?' AS id, title, url, description, tags, type, width, height, size, iso, aperture, make, model, shutter, focal, takestamp, thumbUrl, album, public, star, checksum FROM ? WHERE id = '?' " , $values );
2016-01-31 17:49:31 +00:00
$result = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2016-01-31 14:44:54 +00:00
2016-01-31 17:49:31 +00:00
if ( $result === false ) $error = true ;
2014-08-22 21:31:09 +00:00
}
2016-01-31 17:49:31 +00:00
if ( $error === true ) return false ;
2014-08-22 21:31:09 +00:00
return true ;
}
2016-02-13 16:32:44 +00:00
/**
* Deletes a photo with all its data and files .
* @ return boolean Returns true when successful .
*/
2014-04-05 14:02:53 +00:00
public function delete () {
2016-01-30 20:43:57 +00:00
// Check dependencies
2016-01-30 20:33:31 +00:00
Validator :: required ( isset ( $this -> photoIDs ), __METHOD__ );
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 0 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-31 17:49:31 +00:00
// Init vars
$error = false ;
2016-01-30 20:43:57 +00:00
// Get photos
$query = Database :: prepare ( Database :: get (), " SELECT id, url, thumbUrl, checksum FROM ? WHERE id IN (?) " , array ( LYCHEE_TABLE_PHOTOS , $this -> photoIDs ));
2016-01-31 17:49:31 +00:00
$photos = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2016-01-31 14:44:54 +00:00
2016-01-31 17:49:31 +00:00
if ( $photos === false ) return false ;
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// For each photo
2014-04-05 14:02:53 +00:00
while ( $photo = $photos -> fetch_object ()) {
2016-01-30 20:43:57 +00:00
// Check if other photos are referring to this images
// If so, only delete the db entry
2014-08-22 20:54:33 +00:00
if ( $this -> exists ( $photo -> checksum , $photo -> id ) === false ) {
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Get retina thumb url
2014-08-22 20:54:33 +00:00
$thumbUrl2x = explode ( " . " , $photo -> thumbUrl );
$thumbUrl2x = $thumbUrl2x [ 0 ] . '@2x.' . $thumbUrl2x [ 1 ];
2014-05-08 18:50:18 +00:00
2016-01-30 20:43:57 +00:00
// Delete big
2014-08-22 20:54:33 +00:00
if ( file_exists ( LYCHEE_UPLOADS_BIG . $photo -> url ) &&! unlink ( LYCHEE_UPLOADS_BIG . $photo -> url )) {
2016-01-31 17:49:31 +00:00
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not delete photo in uploads/big/' );
$error = true ;
2014-08-22 20:54:33 +00:00
}
2016-01-30 20:43:57 +00:00
// Delete medium
2014-10-11 14:09:10 +00:00
if ( file_exists ( LYCHEE_UPLOADS_MEDIUM . $photo -> url ) &&! unlink ( LYCHEE_UPLOADS_MEDIUM . $photo -> url )) {
2016-01-31 17:49:31 +00:00
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not delete photo in uploads/medium/' );
$error = true ;
2014-10-11 14:09:10 +00:00
}
2016-01-30 20:43:57 +00:00
// Delete thumb
2014-08-22 20:54:33 +00:00
if ( file_exists ( LYCHEE_UPLOADS_THUMB . $photo -> thumbUrl ) &&! unlink ( LYCHEE_UPLOADS_THUMB . $photo -> thumbUrl )) {
2016-01-31 17:49:31 +00:00
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not delete photo in uploads/thumb/' );
$error = true ;
2014-08-22 20:54:33 +00:00
}
2016-01-30 20:43:57 +00:00
// Delete thumb@2x
if ( file_exists ( LYCHEE_UPLOADS_THUMB . $thumbUrl2x ) &&! unlink ( LYCHEE_UPLOADS_THUMB . $thumbUrl2x )) {
2016-01-31 17:49:31 +00:00
Log :: error ( Database :: get (), __METHOD__ , __LINE__ , 'Could not delete high-res photo in uploads/thumb/' );
$error = true ;
2014-08-22 20:54:33 +00:00
}
2014-05-08 18:50:18 +00:00
}
2014-04-05 14:02:53 +00:00
2016-01-30 20:43:57 +00:00
// Delete db entry
$query = Database :: prepare ( Database :: get (), " DELETE FROM ? WHERE id = '?' " , array ( LYCHEE_TABLE_PHOTOS , $photo -> id ));
2016-01-31 17:49:31 +00:00
$result = Database :: execute ( Database :: get (), $query , __METHOD__ , __LINE__ );
2016-01-31 14:44:54 +00:00
2016-01-31 17:49:31 +00:00
if ( $result === false ) $error = true ;
2014-04-05 14:02:53 +00:00
}
2016-01-30 20:43:57 +00:00
// Call plugins
2016-01-29 23:27:50 +00:00
Plugins :: get () -> activate ( __METHOD__ , 1 , func_get_args ());
2014-04-05 14:25:49 +00:00
2016-01-31 17:49:31 +00:00
if ( $error === true ) return false ;
2014-04-05 14:02:53 +00:00
return true ;
}
}
2016-01-31 14:53:44 +00:00
?>