2015-12-09 14:50:17 -05:00
< ? php namespace BookStack\Services ;
2016-01-17 10:20:07 -05:00
use BookStack\Exceptions\ImageUploadException ;
2015-12-09 14:50:17 -05:00
use BookStack\Image ;
2015-12-09 17:30:55 -05:00
use BookStack\User ;
2015-12-09 14:50:17 -05:00
use Intervention\Image\ImageManager ;
use Illuminate\Contracts\Filesystem\Factory as FileSystem ;
use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance ;
use Illuminate\Contracts\Cache\Repository as Cache ;
use Setting ;
use Symfony\Component\HttpFoundation\File\UploadedFile ;
class ImageService
{
protected $imageTool ;
protected $fileSystem ;
protected $cache ;
/**
* @ var FileSystemInstance
*/
protected $storageInstance ;
protected $storageUrl ;
/**
* ImageService constructor .
* @ param $imageTool
* @ param $fileSystem
* @ param $cache
*/
public function __construct ( ImageManager $imageTool , FileSystem $fileSystem , Cache $cache )
{
$this -> imageTool = $imageTool ;
$this -> fileSystem = $fileSystem ;
$this -> cache = $cache ;
}
2015-12-09 17:30:55 -05:00
/**
* Saves a new image from an upload .
* @ param UploadedFile $uploadedFile
* @ param string $type
* @ return mixed
*/
public function saveNewFromUpload ( UploadedFile $uploadedFile , $type )
{
$imageName = $uploadedFile -> getClientOriginalName ();
$imageData = file_get_contents ( $uploadedFile -> getRealPath ());
return $this -> saveNew ( $imageName , $imageData , $type );
}
/**
* Gets an image from url and saves it to the database .
* @ param $url
* @ param string $type
* @ param bool | string $imageName
* @ return mixed
* @ throws \Exception
*/
private function saveNewFromUrl ( $url , $type , $imageName = false )
{
$imageName = $imageName ? $imageName : basename ( $url );
$imageData = file_get_contents ( $url );
if ( $imageData === false ) throw new \Exception ( 'Cannot get image from ' . $url );
return $this -> saveNew ( $imageName , $imageData , $type );
}
/**
* Saves a new image
* @ param string $imageName
* @ param string $imageData
* @ param string $type
* @ return Image
2016-01-17 10:20:07 -05:00
* @ throws ImageUploadException
2015-12-09 17:30:55 -05:00
*/
private function saveNew ( $imageName , $imageData , $type )
2015-12-09 14:50:17 -05:00
{
$storage = $this -> getStorage ();
$secureUploads = Setting :: get ( 'app-secure-images' );
2015-12-09 17:30:55 -05:00
$imageName = str_replace ( ' ' , '-' , $imageName );
2015-12-09 14:50:17 -05:00
if ( $secureUploads ) $imageName = str_random ( 16 ) . '-' . $imageName ;
$imagePath = '/uploads/images/' . $type . '/' . Date ( 'Y-m-M' ) . '/' ;
while ( $storage -> exists ( $imagePath . $imageName )) {
$imageName = str_random ( 3 ) . $imageName ;
}
$fullPath = $imagePath . $imageName ;
2016-01-17 10:20:07 -05:00
if ( ! is_writable ( dirname ( public_path ( $fullPath )))) throw new ImageUploadException ( 'Image Directory ' . public_path ( $fullPath ) . ' is not writable by the server.' );
2015-12-09 17:30:55 -05:00
$storage -> put ( $fullPath , $imageData );
2015-12-09 14:50:17 -05:00
2016-01-17 10:20:07 -05:00
$imageDetails = [
2015-12-09 17:30:55 -05:00
'name' => $imageName ,
'path' => $fullPath ,
'url' => $this -> getPublicUrl ( $fullPath ),
2016-01-17 10:20:07 -05:00
'type' => $type
];
if ( auth () -> user () && auth () -> user () -> id !== 0 ) {
$userId = auth () -> user () -> id ;
$imageDetails [ 'created_by' ] = $userId ;
$imageDetails [ 'updated_by' ] = $userId ;
}
$image = Image :: forceCreate ( $imageDetails );
2015-12-09 14:50:17 -05:00
return $image ;
}
/**
* Get the thumbnail for an image .
* If $keepRatio is true only the width will be used .
* Checks the cache then storage to avoid creating / accessing the filesystem on every check .
*
* @ param Image $image
* @ param int $width
* @ param int $height
* @ param bool $keepRatio
* @ return string
*/
public function getThumbnail ( Image $image , $width = 220 , $height = 220 , $keepRatio = false )
{
$thumbDirName = '/' . ( $keepRatio ? 'scaled-' : 'thumbs-' ) . $width . '-' . $height . '/' ;
$thumbFilePath = dirname ( $image -> path ) . $thumbDirName . basename ( $image -> path );
if ( $this -> cache -> has ( 'images-' . $image -> id . '-' . $thumbFilePath ) && $this -> cache -> get ( 'images-' . $thumbFilePath )) {
return $this -> getPublicUrl ( $thumbFilePath );
}
$storage = $this -> getStorage ();
if ( $storage -> exists ( $thumbFilePath )) {
return $this -> getPublicUrl ( $thumbFilePath );
}
// Otherwise create the thumbnail
$thumb = $this -> imageTool -> make ( $storage -> get ( $image -> path ));
if ( $keepRatio ) {
$thumb -> resize ( $width , null , function ( $constraint ) {
$constraint -> aspectRatio ();
$constraint -> upsize ();
});
} else {
$thumb -> fit ( $width , $height );
}
$thumbData = ( string ) $thumb -> encode ();
$storage -> put ( $thumbFilePath , $thumbData );
$this -> cache -> put ( 'images-' . $image -> id . '-' . $thumbFilePath , $thumbFilePath , 60 * 72 );
return $this -> getPublicUrl ( $thumbFilePath );
}
/**
* Destroys an Image object along with its files and thumbnails .
* @ param Image $image
* @ return bool
*/
public function destroyImage ( Image $image )
{
$storage = $this -> getStorage ();
$imageFolder = dirname ( $image -> path );
$imageFileName = basename ( $image -> path );
$allImages = collect ( $storage -> allFiles ( $imageFolder ));
$imagesToDelete = $allImages -> filter ( function ( $imagePath ) use ( $imageFileName ) {
$expectedIndex = strlen ( $imagePath ) - strlen ( $imageFileName );
return strpos ( $imagePath , $imageFileName ) === $expectedIndex ;
});
$storage -> delete ( $imagesToDelete -> all ());
// Cleanup of empty folders
foreach ( $storage -> directories ( $imageFolder ) as $directory ) {
if ( $this -> isFolderEmpty ( $directory )) $storage -> deleteDirectory ( $directory );
}
if ( $this -> isFolderEmpty ( $imageFolder )) $storage -> deleteDirectory ( $imageFolder );
$image -> delete ();
return true ;
}
2015-12-09 17:30:55 -05:00
/**
* Save a gravatar image and set a the profile image for a user .
* @ param User $user
* @ param int $size
* @ return mixed
*/
public function saveUserGravatar ( User $user , $size = 500 )
{
$emailHash = md5 ( strtolower ( trim ( $user -> email )));
$url = 'http://www.gravatar.com/avatar/' . $emailHash . '?s=' . $size . '&d=identicon' ;
$imageName = str_replace ( ' ' , '-' , $user -> name . '-gravatar.png' );
$image = $this -> saveNewFromUrl ( $url , 'user' , $imageName );
$image -> created_by = $user -> id ;
2016-01-17 10:20:07 -05:00
$image -> updated_by = $user -> id ;
2015-12-09 17:30:55 -05:00
$image -> save ();
return $image ;
}
2015-12-09 14:50:17 -05:00
/**
* Get the storage that will be used for storing images .
* @ return FileSystemInstance
*/
private function getStorage ()
{
if ( $this -> storageInstance !== null ) return $this -> storageInstance ;
2016-01-09 14:23:35 -05:00
$storageType = config ( 'filesystems.default' );
2015-12-09 14:50:17 -05:00
$this -> storageInstance = $this -> fileSystem -> disk ( $storageType );
return $this -> storageInstance ;
}
/**
* Check whether or not a folder is empty .
* @ param $path
* @ return int
*/
private function isFolderEmpty ( $path )
{
$files = $this -> getStorage () -> files ( $path );
$folders = $this -> getStorage () -> directories ( $path );
return count ( $files ) === 0 && count ( $folders ) === 0 ;
}
/**
* Gets a public facing url for an image by checking relevant environment variables .
* @ param $filePath
* @ return string
*/
private function getPublicUrl ( $filePath )
{
if ( $this -> storageUrl === null ) {
2016-01-09 14:23:35 -05:00
$storageUrl = config ( 'filesystems.url' );
2015-12-09 14:50:17 -05:00
// Get the standard public s3 url if s3 is set as storage type
2016-01-09 14:23:35 -05:00
if ( $storageUrl == false && config ( 'filesystems.default' ) === 's3' ) {
2015-12-09 14:50:17 -05:00
$storageDetails = config ( 'filesystems.disks.s3' );
$storageUrl = 'https://s3-' . $storageDetails [ 'region' ] . '.amazonaws.com/' . $storageDetails [ 'bucket' ];
}
$this -> storageUrl = $storageUrl ;
}
return ( $this -> storageUrl == false ? '' : rtrim ( $this -> storageUrl , '/' )) . $filePath ;
}
}