2017-12-07 08:56:11 -05:00
< ? php
/**
2023-01-01 09:36:24 -05:00
* @ copyright Copyright ( C ) 2010 - 2023 , the Friendica project
2020-02-09 15:45:36 +01:00
*
* @ license GNU AGPL version 3 or any later version
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*
2017-12-07 08:56:11 -05:00
*/
2020-02-09 15:45:36 +01:00
2017-12-07 08:56:11 -05:00
namespace Friendica\Model ;
2021-10-23 10:49:27 +02:00
use Friendica\Core\Cache\Enum\Duration ;
2019-08-02 16:38:50 +00:00
use Friendica\Core\Logger ;
2019-02-22 23:51:13 +01:00
use Friendica\Core\System ;
2018-07-20 08:19:26 -04:00
use Friendica\Database\DBA ;
2019-12-15 22:34:11 +01:00
use Friendica\DI ;
2021-10-23 12:11:38 +02:00
use Friendica\Core\Storage\Type\ExternalResource ;
use Friendica\Core\Storage\Exception\InvalidClassStorageException ;
use Friendica\Core\Storage\Exception\ReferenceStorageException ;
use Friendica\Core\Storage\Exception\StorageException ;
use Friendica\Core\Storage\Type\SystemResource ;
2022-04-02 20:26:11 +02:00
use Friendica\Network\HTTPClient\Client\HttpClientAccept ;
2017-12-07 08:56:11 -05:00
use Friendica\Object\Image ;
2018-01-26 21:38:34 -05:00
use Friendica\Util\DateTimeFormat ;
2019-10-17 21:26:15 -04:00
use Friendica\Util\Images ;
2020-09-30 11:14:01 +02:00
use Friendica\Security\Security ;
2023-01-28 14:57:04 +00:00
use Friendica\Util\Network ;
2020-12-07 06:43:43 +00:00
use Friendica\Util\Proxy ;
2019-08-04 03:45:23 +00:00
use Friendica\Util\Strings ;
2017-12-07 08:56:11 -05:00
/**
2023-03-21 23:17:22 -04:00
* Class to handle photo database table
2017-12-07 08:56:11 -05:00
*/
2019-12-15 23:28:01 +01:00
class Photo
2017-12-07 08:56:11 -05:00
{
2020-08-18 22:18:48 +00:00
const CONTACT_PHOTOS = 'Contact Photos' ;
2021-10-14 04:12:00 +00:00
const PROFILE_PHOTOS = 'Profile Photos' ;
2022-01-06 07:34:16 +00:00
const BANNER_PHOTOS = 'Banner Photos' ;
2020-08-18 22:18:48 +00:00
2021-10-11 14:21:10 +00:00
const DEFAULT = 0 ;
const USER_AVATAR = 10 ;
const USER_BANNER = 11 ;
const CONTACT_AVATAR = 20 ;
const CONTACT_BANNER = 21 ;
2017-12-07 08:56:11 -05:00
/**
2020-01-19 06:05:23 +00:00
* Select rows from the photo table and returns them as array
2018-11-20 22:33:35 +01:00
*
2019-01-06 16:06:53 -05:00
* @ param array $fields Array of selected fields , empty for all
* @ param array $conditions Array of fields for conditions
* @ param array $params Array of several parameters
2018-11-20 22:33:35 +01:00
*
* @ return boolean | array
*
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2019-07-27 15:52:02 +00:00
* @ see \Friendica\Database\DBA :: selectToArray
2018-11-20 22:33:35 +01:00
*/
2019-07-27 15:52:02 +00:00
public static function selectToArray ( array $fields = [], array $conditions = [], array $params = [])
2018-11-20 22:33:35 +01:00
{
if ( empty ( $fields )) {
2019-01-07 13:26:54 -05:00
$fields = self :: getFields ();
2018-11-20 22:33:35 +01:00
}
2019-07-27 16:53:48 +00:00
return DBA :: selectToArray ( 'photo' , $fields , $conditions , $params );
2018-11-20 22:33:35 +01:00
}
/**
2020-01-19 06:05:23 +00:00
* Retrieve a single record from the photo table
2018-11-20 22:33:35 +01:00
*
2019-01-06 16:06:53 -05:00
* @ param array $fields Array of selected fields , empty for all
* @ param array $conditions Array of fields for conditions
* @ param array $params Array of several parameters
2018-11-20 22:33:35 +01:00
*
* @ return bool | array
*
2019-01-06 16:06:53 -05:00
* @ throws \Exception
* @ see \Friendica\Database\DBA :: select
2018-11-20 22:33:35 +01:00
*/
2018-11-21 16:26:56 +01:00
public static function selectFirst ( array $fields = [], array $conditions = [], array $params = [])
2018-11-20 22:33:35 +01:00
{
if ( empty ( $fields )) {
2018-11-20 23:15:03 +01:00
$fields = self :: getFields ();
2018-11-20 22:33:35 +01:00
}
2022-06-18 18:21:05 +02:00
return DBA :: selectFirst ( 'photo' , $fields , $conditions , $params );
2018-11-21 15:10:47 +01:00
}
2018-11-20 22:33:35 +01:00
2018-11-21 17:55:16 +01:00
/**
2020-01-19 06:05:23 +00:00
* Get photos for user id
2018-11-21 17:55:16 +01:00
*
2019-01-06 16:06:53 -05:00
* @ param integer $uid User id
2023-03-22 00:08:15 -04:00
* @ param string $resourceid Resource ID of the photo
2019-01-06 16:06:53 -05:00
* @ param array $conditions Array of fields for conditions
* @ param array $params Array of several parameters
2018-11-21 17:55:16 +01:00
*
* @ return bool | array
*
2019-01-06 16:06:53 -05:00
* @ throws \Exception
* @ see \Friendica\Database\DBA :: select
2018-11-21 17:55:16 +01:00
*/
2022-06-16 20:42:40 +02:00
public static function getPhotosForUser ( int $uid , string $resourceid , array $conditions = [], array $params = [])
2018-11-21 17:55:16 +01:00
{
2022-06-18 18:21:05 +02:00
$conditions [ 'resource-id' ] = $resourceid ;
$conditions [ 'uid' ] = $uid ;
2018-11-21 17:55:16 +01:00
2019-07-27 15:52:02 +00:00
return self :: selectToArray ([], $conditions , $params );
2018-11-21 17:55:16 +01:00
}
/**
2020-01-19 06:05:23 +00:00
* Get a photo for user id
2018-11-21 17:55:16 +01:00
*
2019-01-06 16:06:53 -05:00
* @ param integer $uid User id
2023-03-22 00:08:15 -04:00
* @ param string $resourceid Resource ID of the photo
2019-01-06 16:06:53 -05:00
* @ param integer $scale Scale of the photo . Defaults to 0
* @ param array $conditions Array of fields for conditions
* @ param array $params Array of several parameters
2018-11-21 17:55:16 +01:00
*
* @ return bool | array
*
2019-01-06 16:06:53 -05:00
* @ throws \Exception
* @ see \Friendica\Database\DBA :: select
2018-11-21 17:55:16 +01:00
*/
2022-06-18 18:21:05 +02:00
public static function getPhotoForUser ( int $uid , $resourceid , $scale = 0 , array $conditions = [], array $params = [])
2018-11-21 17:55:16 +01:00
{
2022-06-18 18:21:05 +02:00
$conditions [ 'resource-id' ] = $resourceid ;
$conditions [ 'uid' ] = $uid ;
$conditions [ 'scale' ] = $scale ;
2018-11-21 17:55:16 +01:00
return self :: selectFirst ([], $conditions , $params );
}
2018-11-20 22:33:35 +01:00
/**
2020-01-19 06:05:23 +00:00
* Get a single photo given resource id and scale
2018-11-20 22:33:35 +01:00
*
* This method checks for permissions . Returns associative array
2018-11-20 23:15:03 +01:00
* on success , " no sign " image info , if user has no permission ,
2018-11-20 22:33:35 +01:00
* false if photo does not exists
*
2023-03-22 00:08:15 -04:00
* @ param string $resourceid Resource ID of the photo
2023-02-08 20:16:19 +00:00
* @ param integer $scale Scale of the photo . Defaults to 0
* @ param integer $visitor_uid UID of the visitor
2018-11-20 22:33:35 +01:00
*
* @ return boolean | array
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2018-11-20 22:33:35 +01:00
*/
2023-02-08 20:16:19 +00:00
public static function getPhoto ( string $resourceid , int $scale = 0 , int $visitor_uid = 0 )
2018-11-20 22:33:35 +01:00
{
2022-06-18 18:21:05 +02:00
$r = self :: selectFirst ([ 'uid' ], [ 'resource-id' => $resourceid ]);
2019-09-28 05:37:24 +00:00
if ( ! DBA :: isResult ( $r )) {
2018-11-21 15:10:47 +01:00
return false ;
}
2018-11-20 22:33:35 +01:00
2022-06-18 18:21:05 +02:00
$uid = $r [ 'uid' ];
2019-06-22 12:24:30 -05:00
2020-03-09 08:59:56 +00:00
$accessible = $uid ? ( bool ) DI :: pConfig () -> get ( $uid , 'system' , 'accessible-photos' , false ) : false ;
2020-03-08 13:16:59 +00:00
2023-02-08 20:16:19 +00:00
if ( ! empty ( $visitor_uid ) && ( $uid == $visitor_uid )) {
$sql_acl = '' ;
} else {
$sql_acl = Security :: getPermissionsSQLByUserId ( $uid , $accessible );
}
2018-11-20 22:33:35 +01:00
2022-06-18 23:30:13 +02:00
$conditions = [ " `resource-id` = ? AND `scale` <= ? " . $sql_acl , $resourceid , $scale ];
2022-06-18 18:21:05 +02:00
$params = [ 'order' => [ 'scale' => true ]];
2018-11-21 16:26:56 +01:00
$photo = self :: selectFirst ([], $conditions , $params );
2018-12-14 08:31:08 +01:00
2018-11-20 22:33:35 +01:00
return $photo ;
}
2022-11-25 23:43:07 +01:00
/**
* Returns all browsable albums for a given user
*
* @ param int $uid The given user
*
* @ return array An array of albums
* @ throws \Exception
*/
public static function getBrowsableAlbumsForUser ( int $uid ) : array
{
$photos = DBA :: toArray (
DBA :: p (
2022-11-26 23:34:16 +01:00
" SELECT DISTINCT(`album`) AS `album` FROM `photo` WHERE `uid` = ? AND NOT `photo-type` IN (?, ?) " ,
2022-11-25 23:43:07 +01:00
$uid ,
static :: CONTACT_AVATAR ,
static :: CONTACT_BANNER
)
);
return array_column ( $photos , 'album' );
}
/**
* Returns browsable photos for a given user ( optional and a given album )
*
* @ param int $uid The given user id
* @ param string | null $album ( optional ) The given album
*
* @ return array All photos of the user / album
* @ throws \Exception
*/
public static function getBrowsablePhotosForUser ( int $uid , string $album = null ) : array
{
2022-11-27 01:52:49 +01:00
$values = [
$uid ,
Photo :: CONTACT_AVATAR ,
Photo :: CONTACT_BANNER
];
2022-11-25 23:43:07 +01:00
if ( ! empty ( $album )) {
2022-11-27 01:52:49 +01:00
$sqlExtra = " AND `album` = ? " ;
$values [] = $album ;
2022-11-25 23:43:07 +01:00
$sqlExtra2 = " " ;
} else {
$sqlExtra = '' ;
$sqlExtra2 = ' ORDER BY created DESC LIMIT 0, 10' ;
}
return DBA :: toArray (
DBA :: p (
" SELECT `resource-id`, ANY_VALUE(`id`) AS `id`, ANY_VALUE(`filename`) AS `filename`, ANY_VALUE(`type`) AS `type`,
min ( `scale` ) AS `hiq` , max ( `scale` ) AS `loq` , ANY_VALUE ( `desc` ) AS `desc` , ANY_VALUE ( `created` ) AS `created`
2023-02-08 20:16:19 +00:00
FROM `photo` WHERE `uid` = ? AND NOT `photo-type` IN ( ? , ? ) $sqlExtra
2022-11-25 23:43:07 +01:00
GROUP BY `resource-id` $sqlExtra2 " ,
2022-11-27 01:52:49 +01:00
$values
2022-11-25 23:43:07 +01:00
));
}
2018-11-20 22:33:35 +01:00
/**
2020-01-19 06:05:23 +00:00
* Check if photo with given conditions exists
2018-11-20 22:33:35 +01:00
*
2019-01-06 16:06:53 -05:00
* @ param array $conditions Array of extra conditions
2018-11-20 22:33:35 +01:00
*
* @ return boolean
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2018-11-20 22:33:35 +01:00
*/
2022-06-18 18:21:05 +02:00
public static function exists ( array $conditions ) : bool
2018-11-20 22:33:35 +01:00
{
2022-06-18 18:21:05 +02:00
return DBA :: exists ( 'photo' , $conditions );
2018-11-20 22:33:35 +01:00
}
2018-12-11 20:01:54 +01:00
2018-11-20 22:33:35 +01:00
/**
2020-12-26 19:31:39 +00:00
* Get Image data for given row id . null if row id does not exist
2018-11-20 22:33:35 +01:00
*
2019-01-06 16:06:53 -05:00
* @ param array $photo Photo data . Needs at least 'id' , 'type' , 'backend-class' , 'backend-ref'
2018-11-20 22:33:35 +01:00
*
2022-06-18 18:21:05 +02:00
* @ return \Friendica\Object\Image | null Image object or null on error
2018-11-20 22:33:35 +01:00
*/
2020-12-26 19:31:39 +00:00
public static function getImageDataForPhoto ( array $photo )
2018-11-20 22:33:35 +01:00
{
2021-04-01 04:51:55 +00:00
if ( ! empty ( $photo [ 'data' ])) {
return $photo [ 'data' ];
}
2021-08-10 23:56:30 +02:00
try {
$backendClass = DI :: storageManager () -> getByName ( $photo [ 'backend-class' ] ? ? '' );
2021-08-15 20:40:23 +02:00
/// @todo refactoring this returning, because the storage returns a "string" which is casted in different ways - a check "instanceof Image" will fail!
return $backendClass -> get ( $photo [ 'backend-ref' ] ? ? '' );
2021-08-10 23:56:30 +02:00
} catch ( InvalidClassStorageException $storageException ) {
2021-08-01 14:00:48 +02:00
try {
2021-08-10 23:56:30 +02:00
// legacy data storage in "data" column
$i = self :: selectFirst ([ 'data' ], [ 'id' => $photo [ 'id' ]]);
if ( $i !== false ) {
return $i [ 'data' ];
} else {
DI :: logger () -> info ( 'Stored legacy data is empty' , [ 'photo' => $photo ]);
}
} catch ( \Exception $exception ) {
DI :: logger () -> info ( 'Unexpected database exception' , [ 'photo' => $photo , 'exception' => $exception ]);
2021-08-01 14:00:48 +02:00
}
2021-08-10 23:56:30 +02:00
} catch ( ReferenceStorageException $referenceStorageException ) {
DI :: logger () -> debug ( 'Invalid reference for photo' , [ 'photo' => $photo , 'exception' => $referenceStorageException ]);
} catch ( StorageException $storageException ) {
DI :: logger () -> info ( 'Unexpected storage exception' , [ 'photo' => $photo , 'exception' => $storageException ]);
} catch ( \ImagickException $imagickException ) {
DI :: logger () -> info ( 'Unexpected imagick exception' , [ 'photo' => $photo , 'exception' => $imagickException ]);
2018-11-20 23:15:03 +01:00
}
2021-08-10 23:56:30 +02:00
return null ;
2020-12-26 19:31:39 +00:00
}
2018-11-20 23:15:03 +01:00
2020-12-26 19:31:39 +00:00
/**
* Get Image object for given row id . null if row id does not exist
*
* @ param array $photo Photo data . Needs at least 'id' , 'type' , 'backend-class' , 'backend-ref'
*
* @ return \Friendica\Object\Image
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
*/
2021-08-10 23:56:30 +02:00
public static function getImageForPhoto ( array $photo ) : Image
2020-12-26 19:31:39 +00:00
{
2021-08-10 23:56:30 +02:00
return new Image ( self :: getImageDataForPhoto ( $photo ), $photo [ 'type' ]);
2018-11-20 22:33:35 +01:00
}
/**
2020-01-19 06:05:23 +00:00
* Return a list of fields that are associated with the photo table
2018-11-20 22:33:35 +01:00
*
* @ return array field list
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2018-11-20 22:33:35 +01:00
*/
2022-06-18 18:21:05 +02:00
private static function getFields () : array
2018-11-20 22:33:35 +01:00
{
2022-07-12 23:21:16 +02:00
$allfields = DI :: dbaDefinition () -> getAll ();
2022-06-18 18:21:05 +02:00
$fields = array_keys ( $allfields [ 'photo' ][ 'fields' ]);
array_splice ( $fields , array_search ( 'data' , $fields ), 1 );
2018-11-20 22:33:35 +01:00
return $fields ;
}
2022-12-11 09:56:30 +00:00
/**
* Construct a photo array for a given image data string
*
* @ param string $image_data Image data
* @ param string $mimetype Image mime type . Is guessed by file name when empty .
*
* @ return array
* @ throws \Exception
*/
public static function createPhotoForImageData ( string $image_data , string $mimetype = '' ) : array
{
$fields = self :: getFields ();
$values = array_fill ( 0 , count ( $fields ), '' );
$photo = array_combine ( $fields , $values );
$photo [ 'data' ] = $image_data ;
$photo [ 'type' ] = $mimetype ? : Images :: getMimeTypeByData ( $image_data );
$photo [ 'cacheable' ] = false ;
return $photo ;
}
2018-11-20 23:15:03 +01:00
/**
2020-01-19 06:05:23 +00:00
* Construct a photo array for a system resource image
2018-11-20 23:15:03 +01:00
*
2019-01-06 16:06:53 -05:00
* @ param string $filename Image file name relative to code root
2021-06-30 04:28:03 +00:00
* @ param string $mimetype Image mime type . Is guessed by file name when empty .
2018-11-20 23:15:03 +01:00
*
* @ return array
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2018-11-20 23:15:03 +01:00
*/
2022-06-18 18:21:05 +02:00
public static function createPhotoForSystemResource ( string $filename , string $mimetype = '' ) : array
2018-11-20 23:15:03 +01:00
{
2021-06-30 04:28:03 +00:00
if ( empty ( $mimetype )) {
$mimetype = Images :: guessTypeByExtension ( $filename );
}
2018-11-20 23:15:03 +01:00
$fields = self :: getFields ();
2022-06-18 18:21:05 +02:00
$values = array_fill ( 0 , count ( $fields ), '' );
2019-02-19 01:56:41 +01:00
2020-01-06 22:07:23 +01:00
$photo = array_combine ( $fields , $values );
$photo [ 'backend-class' ] = SystemResource :: NAME ;
$photo [ 'backend-ref' ] = $filename ;
$photo [ 'type' ] = $mimetype ;
$photo [ 'cacheable' ] = false ;
2019-02-19 01:56:41 +01:00
2018-11-20 23:15:03 +01:00
return $photo ;
}
2018-11-20 22:33:35 +01:00
2021-06-24 17:30:22 +00:00
/**
* Construct a photo array for an external resource image
*
* @ param string $url Image URL
2021-06-28 10:08:51 +00:00
* @ param int $uid User ID of the requesting person
2021-06-30 04:28:03 +00:00
* @ param string $mimetype Image mime type . Is guessed by file name when empty .
2022-12-04 14:58:53 +00:00
* @ param string $blurhash The blurhash that will be used to generate a picture when the original picture can ' t be fetched
* @ param int $width Image width
* @ param int $height Image height
2021-06-24 17:30:22 +00:00
*
* @ return array
* @ throws \Exception
*/
2022-12-04 15:14:43 +00:00
public static function createPhotoForExternalResource ( string $url , int $uid = 0 , string $mimetype = '' , string $blurhash = null , int $width = null , int $height = null ) : array
2021-06-24 17:30:22 +00:00
{
2021-06-30 04:28:03 +00:00
if ( empty ( $mimetype )) {
$mimetype = Images :: guessTypeByExtension ( $url );
}
2021-06-24 17:30:22 +00:00
$fields = self :: getFields ();
2022-06-18 18:21:05 +02:00
$values = array_fill ( 0 , count ( $fields ), '' );
2021-06-24 17:30:22 +00:00
$photo = array_combine ( $fields , $values );
$photo [ 'backend-class' ] = ExternalResource :: NAME ;
2021-06-28 10:08:51 +00:00
$photo [ 'backend-ref' ] = json_encode ([ 'url' => $url , 'uid' => $uid ]);
2021-06-24 17:30:22 +00:00
$photo [ 'type' ] = $mimetype ;
2021-06-28 13:09:00 +00:00
$photo [ 'cacheable' ] = true ;
2022-12-04 14:58:53 +00:00
$photo [ 'blurhash' ] = $blurhash ;
$photo [ 'width' ] = $width ;
$photo [ 'height' ] = $height ;
2021-06-24 17:30:22 +00:00
return $photo ;
}
2018-11-20 22:33:35 +01:00
/**
2020-01-19 06:05:23 +00:00
* store photo metadata in db and binary in default backend
2018-11-20 22:33:35 +01:00
*
2022-06-18 18:21:05 +02:00
* @ param Image $image Image object with data
2018-11-22 17:25:43 +01:00
* @ param integer $uid User ID
* @ param integer $cid Contact ID
2022-06-18 18:41:07 +02:00
* @ param string $rid Resource ID
2018-11-22 17:25:43 +01:00
* @ param string $filename Filename
* @ param string $album Album name
* @ param integer $scale Scale
2022-06-18 18:30:03 +02:00
* @ param integer $type Photo type , optional , default : Photo :: DEFAULT
2018-11-22 17:25:43 +01:00
* @ param string $allow_cid Permissions , allowed contacts . optional , default = " "
* @ param string $allow_gid Permissions , allowed groups . optional , default = " "
* @ param string $deny_cid Permissions , denied contacts . optional , default = " "
2023-03-21 23:17:54 -04:00
* @ param string $deny_gid Permissions , denied group . optional , default = " "
2018-11-22 17:25:43 +01:00
* @ param string $desc Photo caption . optional , default = " "
2018-11-20 22:33:35 +01:00
*
* @ return boolean True on success
2019-01-06 16:06:53 -05:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2017-12-07 08:56:11 -05:00
*/
2022-06-18 18:41:07 +02:00
public static function store ( Image $image , int $uid , int $cid , string $rid , string $filename , string $album , int $scale , int $type = self :: DEFAULT , string $allow_cid = '' , string $allow_gid = '' , string $deny_cid = '' , string $deny_gid = '' , string $desc = '' ) : bool
2017-12-07 08:56:11 -05:00
{
2022-06-18 23:30:13 +02:00
$photo = self :: selectFirst ([ 'guid' ], [ " `resource-id` = ? AND `guid` != ? " , $rid , '' ]);
2018-07-21 08:46:04 -04:00
if ( DBA :: isResult ( $photo )) {
2022-06-18 18:21:05 +02:00
$guid = $photo [ 'guid' ];
2017-12-07 08:56:11 -05:00
} else {
2018-07-09 21:38:16 +02:00
$guid = System :: createGUID ();
2017-12-07 08:56:11 -05:00
}
2022-06-18 18:21:05 +02:00
$existing_photo = self :: selectFirst ([ 'id' , 'created' , 'backend-class' , 'backend-ref' ], [ 'resource-id' => $rid , 'uid' => $uid , 'contact-id' => $cid , 'scale' => $scale ]);
2018-11-22 17:25:43 +01:00
$created = DateTimeFormat :: utcNow ();
if ( DBA :: isResult ( $existing_photo )) {
2022-06-18 18:21:05 +02:00
$created = $existing_photo [ 'created' ];
2018-11-22 17:25:43 +01:00
}
2017-12-07 08:56:11 -05:00
2018-11-21 09:38:54 +01:00
// Get defined storage backend.
// if no storage backend, we use old "data" column in photo table.
2018-11-21 16:26:56 +01:00
// if is an existing photo, reuse same backend
2022-06-18 18:21:05 +02:00
$data = '' ;
$backend_ref = '' ;
$storage = '' ;
2021-08-10 23:56:30 +02:00
try {
if ( DBA :: isResult ( $existing_photo )) {
2022-06-18 18:21:05 +02:00
$backend_ref = ( string ) $existing_photo [ 'backend-ref' ];
$storage = DI :: storageManager () -> getWritableStorageByName ( $existing_photo [ 'backend-class' ] ? ? '' );
2021-08-10 23:56:30 +02:00
} else {
$storage = DI :: storage ();
}
2022-06-18 18:21:05 +02:00
$backend_ref = $storage -> put ( $image -> asString (), $backend_ref );
2021-08-10 23:56:30 +02:00
} catch ( InvalidClassStorageException $storageException ) {
2022-06-18 18:21:05 +02:00
$data = $image -> asString ();
2018-11-21 09:38:54 +01:00
}
2018-01-11 03:26:30 -05:00
$fields = [
2022-06-18 18:21:05 +02:00
'uid' => $uid ,
'contact-id' => $cid ,
'guid' => $guid ,
'resource-id' => $rid ,
'hash' => md5 ( $image -> asString ()),
'created' => $created ,
'edited' => DateTimeFormat :: utcNow (),
'filename' => basename ( $filename ),
'type' => $image -> getType (),
'album' => $album ,
'height' => $image -> getHeight (),
'width' => $image -> getWidth (),
'datasize' => strlen ( $image -> asString ()),
2022-12-04 13:29:21 +00:00
'blurhash' => $image -> getBlurHash (),
2022-06-18 18:21:05 +02:00
'data' => $data ,
'scale' => $scale ,
'photo-type' => $type ,
'profile' => false ,
'allow_cid' => $allow_cid ,
'allow_gid' => $allow_gid ,
'deny_cid' => $deny_cid ,
'deny_gid' => $deny_gid ,
'desc' => $desc ,
'backend-class' => ( string ) $storage ,
'backend-ref' => $backend_ref
2018-01-11 03:26:30 -05:00
];
2017-12-07 08:56:11 -05:00
2018-07-21 08:46:04 -04:00
if ( DBA :: isResult ( $existing_photo )) {
2022-06-18 18:21:05 +02:00
$r = DBA :: update ( 'photo' , $fields , [ 'id' => $existing_photo [ 'id' ]]);
2017-12-07 08:56:11 -05:00
} else {
2022-06-18 18:21:05 +02:00
$r = DBA :: insert ( 'photo' , $fields );
2017-12-07 08:56:11 -05:00
}
return $r ;
}
2019-01-02 16:18:40 +01:00
2018-12-11 20:01:54 +01:00
/**
2020-01-19 06:05:23 +00:00
* Delete info from table and data from storage
2018-12-11 20:01:54 +01:00
*
2019-01-06 16:06:53 -05:00
* @ param array $conditions Field condition ( s )
* @ param array $options Options array , Optional
2018-12-11 20:01:54 +01:00
*
* @ return boolean
*
2019-01-06 16:06:53 -05:00
* @ throws \Exception
* @ see \Friendica\Database\DBA :: delete
2018-12-11 20:01:54 +01:00
*/
2022-06-18 18:21:05 +02:00
public static function delete ( array $conditions , array $options = []) : bool
2018-11-21 16:26:56 +01:00
{
// get photo to delete data info
2021-03-19 11:42:29 +00:00
$photos = DBA :: select ( 'photo' , [ 'id' , 'backend-class' , 'backend-ref' ], $conditions );
2018-12-11 20:01:54 +01:00
2021-03-19 11:42:29 +00:00
while ( $photo = DBA :: fetch ( $photos )) {
2021-08-10 23:56:30 +02:00
try {
$backend_class = DI :: storageManager () -> getWritableStorageByName ( $photo [ 'backend-class' ] ? ? '' );
2021-09-05 19:05:19 +02:00
$backend_class -> delete ( $photo [ 'backend-ref' ] ? ? '' );
2021-08-10 23:56:30 +02:00
// Delete the photos after they had been deleted successfully
2022-06-18 18:21:05 +02:00
DBA :: delete ( 'photo' , [ 'id' => $photo [ 'id' ]]);
2021-08-10 23:56:30 +02:00
} catch ( InvalidClassStorageException $storageException ) {
DI :: logger () -> debug ( 'Storage class not found.' , [ 'conditions' => $conditions , 'exception' => $storageException ]);
} catch ( ReferenceStorageException $referenceStorageException ) {
DI :: logger () -> debug ( 'Photo doesn\'t exist.' , [ 'conditions' => $conditions , 'exception' => $referenceStorageException ]);
2018-11-21 16:26:56 +01:00
}
}
2021-03-19 11:42:29 +00:00
DBA :: close ( $photos );
2022-06-18 18:21:05 +02:00
return DBA :: delete ( 'photo' , $conditions , $options );
2018-11-21 16:26:56 +01:00
}
2018-11-21 17:55:16 +01:00
/**
2020-01-19 06:05:23 +00:00
* Update a photo
2018-11-21 17:55:16 +01:00
*
2022-06-18 18:21:05 +02:00
* @ param array $fields Contains the fields that are updated
* @ param array $conditions Condition array with the key values
* @ param Image $image Image to update . Optional , default null .
* @ param array $old_fields Array with the old field values that are about to be replaced ( true = update on duplicate )
2018-11-21 17:55:16 +01:00
*
2023-03-22 00:08:30 -04:00
* @ return boolean Was the update successful ?
2018-11-21 17:55:16 +01:00
*
2019-01-06 16:06:53 -05:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ see \Friendica\Database\DBA :: update
2018-11-21 17:55:16 +01:00
*/
2022-06-18 18:21:05 +02:00
public static function update ( array $fields , array $conditions , Image $image = null , array $old_fields = []) : bool
2018-11-21 17:55:16 +01:00
{
2022-06-18 18:21:05 +02:00
if ( ! is_null ( $image )) {
2018-11-21 17:55:16 +01:00
// get photo to update
2019-07-27 16:57:00 +00:00
$photos = self :: selectToArray ([ 'backend-class' , 'backend-ref' ], $conditions );
2018-11-21 17:55:16 +01:00
foreach ( $photos as $photo ) {
2021-08-10 23:56:30 +02:00
try {
$backend_class = DI :: storageManager () -> getWritableStorageByName ( $photo [ 'backend-class' ] ? ? '' );
2022-06-18 18:21:05 +02:00
$fields [ 'backend-ref' ] = $backend_class -> put ( $image -> asString (), $photo [ 'backend-ref' ]);
2021-08-10 23:56:30 +02:00
} catch ( InvalidClassStorageException $storageException ) {
2022-06-18 18:21:05 +02:00
$fields [ 'data' ] = $image -> asString ();
2018-11-21 17:55:16 +01:00
}
}
2018-12-11 20:01:54 +01:00
$fields [ 'updated' ] = DateTimeFormat :: utcNow ();
2018-11-21 17:55:16 +01:00
}
2018-12-11 20:01:54 +01:00
$fields [ 'edited' ] = DateTimeFormat :: utcNow ();
2018-11-21 18:08:23 +01:00
2022-06-18 18:21:05 +02:00
return DBA :: update ( 'photo' , $fields , $conditions , $old_fields );
2018-11-21 17:55:16 +01:00
}
2017-12-07 08:56:11 -05:00
/**
2018-01-11 03:26:30 -05:00
* @ param string $image_url Remote URL
2017-12-07 08:56:11 -05:00
* @ param integer $uid user id
* @ param integer $cid contact id
* @ param boolean $quit_on_error optional , default false
2022-06-18 18:41:07 +02:00
* @ return array | bool Array on success , false on error
2019-01-06 16:06:53 -05:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
2017-12-07 08:56:11 -05:00
*/
2022-06-18 18:21:05 +02:00
public static function importProfilePhoto ( string $image_url , int $uid , int $cid , bool $quit_on_error = false )
2017-12-07 08:56:11 -05:00
{
2022-06-18 18:21:05 +02:00
$thumb = '' ;
$micro = '' ;
2018-02-14 00:05:00 -05:00
2018-07-20 08:19:26 -04:00
$photo = DBA :: selectFirst (
2022-06-18 18:21:05 +02:00
'photo' , [ 'resource-id' ], [ 'uid' => $uid , 'contact-id' => $cid , 'scale' => 4 , 'photo-type' => self :: CONTACT_AVATAR ]
2017-12-07 08:56:11 -05:00
);
2018-11-30 09:06:22 -05:00
if ( ! empty ( $photo [ 'resource-id' ])) {
2022-06-18 18:21:05 +02:00
$resource_id = $photo [ 'resource-id' ];
2017-12-07 08:56:11 -05:00
} else {
2019-10-26 09:05:35 -04:00
$resource_id = self :: newResource ();
2017-12-07 08:56:11 -05:00
}
$photo_failure = false ;
2023-01-28 14:57:04 +00:00
if ( ! Network :: isValidHttpUrl ( $image_url )) {
Logger :: warning ( 'Invalid image url' , [ 'image_url' => $image_url , 'uid' => $uid , 'cid' => $cid , 'callstack' => System :: callstack ( 20 )]);
return false ;
}
2018-01-11 03:26:30 -05:00
$filename = basename ( $image_url );
2023-01-28 14:57:04 +00:00
if ( ! empty ( $image_url )) {
2022-04-02 21:16:22 +02:00
$ret = DI :: httpClient () -> get ( $image_url , HttpClientAccept :: IMAGE );
2022-03-29 06:24:20 +00:00
Logger :: debug ( 'Got picture' , [ 'Content-Type' => $ret -> getHeader ( 'Content-Type' ), 'url' => $image_url ]);
2019-06-20 20:09:33 +00:00
$img_str = $ret -> getBody ();
2021-08-20 20:03:42 +02:00
$type = $ret -> getContentType ();
2019-06-11 05:26:16 +00:00
} else {
$img_str = '' ;
2021-08-20 20:03:42 +02:00
$type = '' ;
2019-06-11 05:26:16 +00:00
}
2017-12-07 08:56:11 -05:00
2022-06-18 18:21:05 +02:00
if ( $quit_on_error && ( $img_str == '' )) {
2017-12-07 08:56:11 -05:00
return false ;
}
2021-08-20 20:03:42 +02:00
$type = Images :: getMimeTypeByData ( $img_str , $image_url , $type );
2019-06-20 20:09:33 +00:00
2022-06-18 18:21:05 +02:00
$image = new Image ( $img_str , $type );
if ( $image -> isValid ()) {
$image -> scaleToSquare ( 300 );
2017-12-07 08:56:11 -05:00
2022-06-18 18:21:05 +02:00
$filesize = strlen ( $image -> asString ());
2022-11-30 02:26:56 +01:00
$maximagesize = Strings :: getBytesFromShorthand ( DI :: config () -> get ( 'system' , 'maximagesize' ));
2022-11-30 02:44:48 +01:00
2022-11-30 05:17:48 +01:00
if ( $maximagesize && ( $filesize > $maximagesize )) {
2022-06-18 18:21:05 +02:00
Logger :: info ( 'Avatar exceeds image limit' , [ 'uid' => $uid , 'cid' => $cid , 'maximagesize' => $maximagesize , 'size' => $filesize , 'type' => $image -> getType ()]);
if ( $image -> getType () == 'image/gif' ) {
$image -> toStatic ();
$image = new Image ( $image -> asString (), 'image/png' );
2021-03-26 06:56:08 +00:00
2022-06-18 18:21:05 +02:00
$filesize = strlen ( $image -> asString ());
Logger :: info ( 'Converted gif to a static png' , [ 'uid' => $uid , 'cid' => $cid , 'size' => $filesize , 'type' => $image -> getType ()]);
2021-03-26 06:56:08 +00:00
}
if ( $filesize > $maximagesize ) {
foreach ([ 160 , 80 ] as $pixels ) {
if ( $filesize > $maximagesize ) {
2022-06-18 18:21:05 +02:00
Logger :: info ( 'Resize' , [ 'uid' => $uid , 'cid' => $cid , 'size' => $filesize , 'max' => $maximagesize , 'pixels' => $pixels , 'type' => $image -> getType ()]);
$image -> scaleDown ( $pixels );
$filesize = strlen ( $image -> asString ());
2021-03-26 06:56:08 +00:00
}
}
}
2022-06-18 18:21:05 +02:00
Logger :: info ( 'Avatar is resized' , [ 'uid' => $uid , 'cid' => $cid , 'size' => $filesize , 'type' => $image -> getType ()]);
2021-03-26 06:56:08 +00:00
}
2022-06-18 18:21:05 +02:00
$r = self :: store ( $image , $uid , $cid , $resource_id , $filename , self :: CONTACT_PHOTOS , 4 , self :: CONTACT_AVATAR );
2017-12-07 08:56:11 -05:00
if ( $r === false ) {
$photo_failure = true ;
}
2022-06-18 18:21:05 +02:00
$image -> scaleDown ( 80 );
2017-12-07 08:56:11 -05:00
2022-06-18 18:21:05 +02:00
$r = self :: store ( $image , $uid , $cid , $resource_id , $filename , self :: CONTACT_PHOTOS , 5 , self :: CONTACT_AVATAR );
2017-12-07 08:56:11 -05:00
if ( $r === false ) {
$photo_failure = true ;
}
2022-06-18 18:21:05 +02:00
$image -> scaleDown ( 48 );
2017-12-07 08:56:11 -05:00
2022-06-18 18:21:05 +02:00
$r = self :: store ( $image , $uid , $cid , $resource_id , $filename , self :: CONTACT_PHOTOS , 6 , self :: CONTACT_AVATAR );
2017-12-07 08:56:11 -05:00
if ( $r === false ) {
$photo_failure = true ;
}
2022-06-18 18:21:05 +02:00
$suffix = '?ts=' . time ();
2017-12-07 08:56:11 -05:00
2022-06-18 18:21:05 +02:00
$image_url = DI :: baseUrl () . '/photo/' . $resource_id . '-4.' . $image -> getExt () . $suffix ;
$thumb = DI :: baseUrl () . '/photo/' . $resource_id . '-5.' . $image -> getExt () . $suffix ;
$micro = DI :: baseUrl () . '/photo/' . $resource_id . '-6.' . $image -> getExt () . $suffix ;
2017-12-07 08:56:11 -05:00
} else {
$photo_failure = true ;
}
if ( $photo_failure && $quit_on_error ) {
return false ;
}
if ( $photo_failure ) {
2020-12-07 06:43:43 +00:00
$contact = Contact :: getById ( $cid ) ? : [];
$image_url = Contact :: getDefaultAvatar ( $contact , Proxy :: SIZE_SMALL );
$thumb = Contact :: getDefaultAvatar ( $contact , Proxy :: SIZE_THUMB );
$micro = Contact :: getDefaultAvatar ( $contact , Proxy :: SIZE_MICRO );
2017-12-07 08:56:11 -05:00
}
2022-12-11 09:56:30 +00:00
$photo = DBA :: selectFirst (
'photo' , [ 'blurhash' ], [ 'uid' => $uid , 'contact-id' => $cid , 'scale' => 4 , 'photo-type' => self :: CONTACT_AVATAR ]
);
return [ $image_url , $thumb , $micro , $photo [ 'blurhash' ]];
2017-12-07 08:56:11 -05:00
}
2018-01-03 21:01:41 -05:00
/**
2022-06-26 15:57:09 +02:00
* Returns a float that represents the GPS coordinate from EXIF data
*
2019-01-21 16:51:59 -05:00
* @ param array $exifCoord coordinate
2018-01-03 21:01:41 -05:00
* @ param string $hemi hemi
* @ return float
*/
2022-06-18 23:30:13 +02:00
public static function getGps ( array $exifCoord , string $hemi ) : float
2018-01-03 21:01:41 -05:00
{
$degrees = count ( $exifCoord ) > 0 ? self :: gps2Num ( $exifCoord [ 0 ]) : 0 ;
$minutes = count ( $exifCoord ) > 1 ? self :: gps2Num ( $exifCoord [ 1 ]) : 0 ;
$seconds = count ( $exifCoord ) > 2 ? self :: gps2Num ( $exifCoord [ 2 ]) : 0 ;
2018-01-15 08:05:12 -05:00
2022-06-18 18:21:05 +02:00
$flip = ( $hemi == 'W' || $hemi == 'S' ) ? - 1 : 1 ;
2018-01-15 08:05:12 -05:00
2018-01-03 21:01:41 -05:00
return floatval ( $flip * ( $degrees + ( $minutes / 60 ) + ( $seconds / 3600 )));
}
/**
2022-06-18 18:21:05 +02:00
* Change GPS to float number
*
2018-01-03 21:01:41 -05:00
* @ param string $coordPart coordPart
* @ return float
*/
2022-06-18 18:21:05 +02:00
private static function gps2Num ( string $coordPart ) : float
2018-01-03 21:01:41 -05:00
{
2022-06-18 18:21:05 +02:00
$parts = explode ( '/' , $coordPart );
2018-01-15 08:05:12 -05:00
2018-01-03 21:01:41 -05:00
if ( count ( $parts ) <= 0 ) {
return 0 ;
}
2018-01-15 08:05:12 -05:00
2018-01-03 21:01:41 -05:00
if ( count ( $parts ) == 1 ) {
2022-06-18 18:21:05 +02:00
return ( float ) $parts [ 0 ];
2018-01-03 21:01:41 -05:00
}
2018-01-15 08:05:12 -05:00
2018-01-03 21:01:41 -05:00
return floatval ( $parts [ 0 ]) / floatval ( $parts [ 1 ]);
}
/**
2020-01-19 06:05:23 +00:00
* Fetch the photo albums that are available for a viewer
2018-01-03 21:01:41 -05:00
*
* The query in this function is cost intensive , so it is cached .
*
* @ param int $uid User id of the photos
* @ param bool $update Update the cache
*
* @ return array Returns array of the photo albums
2019-01-06 16:06:53 -05:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2018-01-03 21:01:41 -05:00
*/
2022-06-18 18:21:05 +02:00
public static function getAlbums ( int $uid , bool $update = false ) : array
2018-01-03 21:01:41 -05:00
{
2018-10-17 21:30:41 +02:00
$sql_extra = Security :: getPermissionsSQLByUserId ( $uid );
2018-01-03 21:01:41 -05:00
2022-10-20 22:14:50 +02:00
$avatar_type = ( DI :: userSession () -> getLocalUserId () && ( DI :: userSession () -> getLocalUserId () == $uid )) ? self :: USER_AVATAR : self :: DEFAULT ;
$banner_type = ( DI :: userSession () -> getLocalUserId () && ( DI :: userSession () -> getLocalUserId () == $uid )) ? self :: USER_BANNER : self :: DEFAULT ;
2021-10-19 19:15:28 +00:00
2022-10-20 22:14:50 +02:00
$key = 'photo_albums:' . $uid . ':' . DI :: userSession () -> getLocalUserId () . ':' . DI :: userSession () -> getRemoteUserId ();
2020-01-07 00:45:49 +01:00
$albums = DI :: cache () -> get ( $key );
2022-06-18 18:21:05 +02:00
2018-01-03 21:01:41 -05:00
if ( is_null ( $albums ) || $update ) {
2022-06-18 18:21:05 +02:00
if ( ! DI :: config () -> get ( 'system' , 'no_count' , false )) {
2018-01-03 21:01:41 -05:00
/// @todo This query needs to be renewed. It is really slow
// At this time we just store the data in the cache
2021-10-08 04:10:45 +00:00
$albums = DBA :: toArray ( DBA :: p ( " SELECT COUNT(DISTINCT `resource-id`) AS `total`, `album`, ANY_VALUE(`created`) AS `created`
2018-01-03 21:01:41 -05:00
FROM `photo`
2022-01-08 22:43:11 +00:00
WHERE `uid` = ? AND `photo-type` IN ( ? , ? , ? ) $sql_extra
2018-01-03 21:01:41 -05:00
GROUP BY `album` ORDER BY `created` DESC " ,
2021-10-08 04:10:45 +00:00
$uid ,
2021-10-19 19:15:28 +00:00
self :: DEFAULT ,
2022-01-08 22:43:11 +00:00
$banner_type ,
2021-10-19 19:15:28 +00:00
$avatar_type
2021-10-08 04:10:45 +00:00
));
2018-01-03 21:01:41 -05:00
} else {
// This query doesn't do the count and is much faster
2021-10-08 04:10:45 +00:00
$albums = DBA :: toArray ( DBA :: p ( " SELECT DISTINCT(`album`), '' AS `total`
2018-01-03 21:01:41 -05:00
FROM `photo` USE INDEX ( `uid_album_scale_created` )
2022-01-08 22:43:11 +00:00
WHERE `uid` = ? AND `photo-type` IN ( ? , ? , ? ) $sql_extra " ,
2021-10-08 04:10:45 +00:00
$uid ,
2021-10-19 19:15:28 +00:00
self :: DEFAULT ,
2022-01-08 22:43:11 +00:00
$banner_type ,
2021-10-19 19:15:28 +00:00
$avatar_type
2021-10-08 04:10:45 +00:00
));
2018-01-03 21:01:41 -05:00
}
2020-01-18 15:41:19 +01:00
DI :: cache () -> set ( $key , $albums , Duration :: DAY );
2018-01-03 21:01:41 -05:00
}
return $albums ;
}
2018-01-03 22:36:15 -05:00
/**
* @ param int $uid User id of the photos
* @ return void
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2018-01-03 22:36:15 -05:00
*/
2022-06-18 18:21:05 +02:00
public static function clearAlbumCache ( int $uid )
2018-01-03 22:36:15 -05:00
{
2022-10-20 22:14:50 +02:00
$key = 'photo_albums:' . $uid . ':' . DI :: userSession () -> getLocalUserId () . ':' . DI :: userSession () -> getRemoteUserId ();
2020-01-18 15:41:19 +01:00
DI :: cache () -> set ( $key , null , Duration :: DAY );
2018-01-03 22:36:15 -05:00
}
2018-02-20 10:02:07 +00:00
/**
* Generate a unique photo ID .
*
2022-06-18 18:21:05 +02:00
* @ return string Resource GUID
2019-01-06 16:06:53 -05:00
* @ throws \Exception
2018-02-20 10:02:07 +00:00
*/
2022-06-18 18:21:05 +02:00
public static function newResource () : string
2018-02-20 11:20:28 +00:00
{
2018-11-22 16:57:41 +01:00
return System :: createGUID ( 32 , false );
2018-02-20 10:02:07 +00:00
}
2019-08-02 16:38:50 +00:00
2020-01-09 21:41:35 +01:00
/**
* Extracts the rid from a local photo URI
*
* @ param string $image_uri The URI of the photo
* @ return string The rid of the photo , or an empty string if the URI is not local
*/
2022-06-18 18:21:05 +02:00
public static function ridFromURI ( string $image_uri ) : string
2020-01-09 21:41:35 +01:00
{
if ( ! stristr ( $image_uri , DI :: baseUrl () . '/photo/' )) {
2020-01-11 16:00:02 +01:00
return '' ;
2020-01-09 21:41:35 +01:00
}
$image_uri = substr ( $image_uri , strrpos ( $image_uri , '/' ) + 1 );
$image_uri = substr ( $image_uri , 0 , strpos ( $image_uri , '-' ));
2022-05-24 17:39:02 +00:00
return trim ( $image_uri );
2020-01-09 21:41:35 +01:00
}
2022-05-09 04:26:00 +00:00
/**
* Checks if the given URL is a local photo .
* Since it is meant for time critical occasions , the check is done without any database requests .
*
* @ param string $url
* @ return boolean
*/
public static function isPhotoURI ( string $url ) : bool
{
return ! empty ( self :: ridFromURI ( $url ));
}
2019-08-02 16:38:50 +00:00
/**
2019-08-02 16:42:24 +00:00
* Changes photo permissions that had been embedded in a post
2019-08-02 16:38:50 +00:00
*
* @ todo This function currently does have some flaws :
2019-08-02 16:59:26 +00:00
* - Sharing a post with a forum will create a photo that only the forum can see .
* - Sharing a photo again that been shared non public before doesn ' t alter the permissions .
2019-08-02 16:38:50 +00:00
*
* @ return string
* @ throws \Exception
*/
public static function setPermissionFromBody ( $body , $uid , $original_contact_id , $str_contact_allow , $str_group_allow , $str_contact_deny , $str_group_deny )
{
// Simplify image codes
$img_body = preg_replace ( " / \ [img \ =([0-9]*)x([0-9]*) \ ](.*?) \ [ \ /img \ ]/ism " , '[img]$3[/img]' , $body );
$img_body = preg_replace ( " / \ [img \ =(.*?) \ ](.*?) \ [ \ /img \ ]/ism " , '[img]$1[/img]' , $img_body );
// Search for images
if ( ! preg_match_all ( " / \ [img \ ](.*?) \ [ \ /img \ ]/ " , $img_body , $match )) {
return false ;
}
$images = $match [ 1 ];
if ( empty ( $images )) {
return false ;
}
foreach ( $images as $image ) {
2020-01-10 20:29:15 +01:00
$image_rid = self :: ridFromURI ( $image );
2020-01-11 16:00:02 +01:00
if ( empty ( $image_rid )) {
2019-08-02 16:38:50 +00:00
continue ;
}
// Ensure to only modify photos that you own
$srch = '<' . intval ( $original_contact_id ) . '>' ;
2020-03-21 18:30:40 +00:00
$condition = [
'allow_cid' => $srch , 'allow_gid' => '' , 'deny_cid' => '' , 'deny_gid' => '' ,
'resource-id' => $image_rid , 'uid' => $uid
];
2022-01-06 20:13:32 +00:00
if ( ! self :: exists ( $condition )) {
2020-03-22 12:51:37 +00:00
$photo = self :: selectFirst ([ 'allow_cid' , 'allow_gid' , 'deny_cid' , 'deny_gid' , 'uid' ], [ 'resource-id' => $image_rid ]);
2020-03-22 09:57:46 +00:00
if ( ! DBA :: isResult ( $photo )) {
2020-03-22 12:51:37 +00:00
Logger :: info ( 'Image not found' , [ 'resource-id' => $image_rid ]);
2020-03-22 09:57:46 +00:00
} else {
Logger :: info ( 'Mismatching permissions' , [ 'condition' => $condition , 'photo' => $photo ]);
}
2019-08-02 16:38:50 +00:00
continue ;
}
2020-03-08 13:16:59 +00:00
/**
* @ todo Existing permissions need to be mixed with the new ones .
* Otherwise this creates problems with sharing the same picture multiple times
* Also check if $str_contact_allow does contain a public forum .
* Then set the permissions to public .
*/
2019-09-09 21:37:26 +00:00
2023-03-26 18:42:40 -04:00
self :: setPermissionForResource ( $image_rid , $uid , $str_contact_allow , $str_group_allow , $str_contact_deny , $str_group_deny );
2019-08-02 16:38:50 +00:00
}
return true ;
}
2019-08-04 03:45:23 +00:00
2021-05-01 15:48:19 +00:00
/**
2023-03-26 18:42:40 -04:00
* Add permissions to photo resource
2021-05-01 15:48:19 +00:00
* @ todo mix with previous photo permissions
2022-01-06 07:34:16 +00:00
*
2021-05-01 15:48:19 +00:00
* @ param string $image_rid
* @ param integer $uid
* @ param string $str_contact_allow
* @ param string $str_group_allow
* @ param string $str_contact_deny
* @ param string $str_group_deny
* @ return void
*/
2023-03-26 18:42:40 -04:00
public static function setPermissionForResource ( string $image_rid , int $uid , string $str_contact_allow , string $str_group_allow , string $str_contact_deny , string $str_group_deny )
2021-05-01 15:48:19 +00:00
{
$fields = [ 'allow_cid' => $str_contact_allow , 'allow_gid' => $str_group_allow ,
'deny_cid' => $str_contact_deny , 'deny_gid' => $str_group_deny ,
'accessible' => DI :: pConfig () -> get ( $uid , 'system' , 'accessible-photos' , false )];
$condition = [ 'resource-id' => $image_rid , 'uid' => $uid ];
Logger :: info ( 'Set permissions' , [ 'condition' => $condition , 'permissions' => $fields ]);
2022-01-06 20:13:32 +00:00
self :: update ( $fields , $condition );
2021-05-01 15:48:19 +00:00
}
2019-08-04 03:45:23 +00:00
/**
2021-07-19 06:14:14 +00:00
* Fetch the guid and scale from picture links
2019-08-04 03:45:23 +00:00
*
* @ param string $name Picture link
2021-07-19 06:14:14 +00:00
* @ return array
2019-08-04 03:45:23 +00:00
*/
2022-06-18 18:21:05 +02:00
public static function getResourceData ( string $name ) : array
2019-08-04 03:45:23 +00:00
{
2023-02-18 20:57:30 +01:00
$base = DI :: baseUrl ();
2019-08-04 03:45:23 +00:00
$guid = str_replace ([ Strings :: normaliseLink ( $base ), '/photo/' ], '' , Strings :: normaliseLink ( $name ));
2021-07-19 06:14:14 +00:00
if ( parse_url ( $guid , PHP_URL_SCHEME )) {
return [];
}
2021-10-02 17:28:29 -04:00
$guid = pathinfo ( $guid , PATHINFO_FILENAME );
2019-08-04 03:45:23 +00:00
if ( substr ( $guid , - 2 , 1 ) != " - " ) {
2021-07-19 06:14:14 +00:00
return [];
2019-08-04 03:45:23 +00:00
}
$scale = intval ( substr ( $guid , - 1 , 1 ));
2019-10-08 04:42:51 +00:00
if ( ! is_numeric ( $scale )) {
2021-07-19 06:14:14 +00:00
return [];
2019-08-04 03:45:23 +00:00
}
$guid = substr ( $guid , 0 , - 2 );
2021-07-19 06:14:14 +00:00
return [ 'guid' => $guid , 'scale' => $scale ];
2019-08-04 03:45:23 +00:00
}
2019-08-05 16:27:45 +00:00
/**
* Tests if the picture link points to a locally stored picture
*
* @ param string $name Picture link
* @ return boolean
* @ throws \Exception
*/
2022-06-18 18:21:05 +02:00
public static function isLocal ( string $name ) : bool
2021-07-30 13:22:06 +00:00
{
2022-06-18 18:21:05 +02:00
// @TODO Maybe a proper check here on true condition?
2021-07-30 13:22:06 +00:00
return ( bool ) self :: getIdForName ( $name );
}
/**
* Return the id of a local photo
*
* @ param string $name Picture link
* @ return int
*/
2022-06-18 18:21:05 +02:00
public static function getIdForName ( string $name ) : int
2019-08-05 16:27:45 +00:00
{
2021-07-19 06:14:14 +00:00
$data = self :: getResourceData ( $name );
if ( empty ( $data )) {
2021-07-30 13:22:06 +00:00
return 0 ;
2019-08-05 16:27:45 +00:00
}
2021-07-30 13:22:06 +00:00
$photo = DBA :: selectFirst ( 'photo' , [ 'id' ], [ 'resource-id' => $data [ 'guid' ], 'scale' => $data [ 'scale' ]]);
if ( ! empty ( $photo [ 'id' ])) {
return $photo [ 'id' ];
}
return 0 ;
2019-08-05 16:27:45 +00:00
}
2019-11-11 22:37:50 +00:00
/**
* Tests if the link points to a locally stored picture page
*
* @ param string $name Page link
* @ return boolean
* @ throws \Exception
*/
2022-06-18 18:21:05 +02:00
public static function isLocalPage ( string $name ) : bool
2019-11-11 22:37:50 +00:00
{
2023-02-18 20:57:30 +01:00
$base = DI :: baseUrl ();
2019-11-11 22:37:50 +00:00
$guid = str_replace ( Strings :: normaliseLink ( $base ), '' , Strings :: normaliseLink ( $name ));
$guid = preg_replace ( " =/photos/.*/image/(.*)=ism " , '$1' , $guid );
if ( empty ( $guid )) {
return false ;
}
return DBA :: exists ( 'photo' , [ 'resource-id' => $guid ]);
}
2021-05-16 09:56:02 +00:00
2022-06-18 18:21:05 +02:00
/**
* Tries to resize image to wanted maximum size
*
* @ param Image $image Image instance
* @ return Image | null Image instance on success , null on error
*/
private static function fitImageSize ( Image $image )
2022-01-08 22:43:11 +00:00
{
$max_length = DI :: config () -> get ( 'system' , 'max_image_length' );
if ( $max_length > 0 ) {
2022-06-18 18:21:05 +02:00
$image -> scaleDown ( $max_length );
2022-01-08 22:43:11 +00:00
Logger :: info ( 'File upload: Scaling picture to new size' , [ 'max-length' => $max_length ]);
}
2022-06-18 18:21:05 +02:00
$filesize = strlen ( $image -> asString ());
$width = $image -> getWidth ();
$height = $image -> getHeight ();
2022-01-08 22:43:11 +00:00
2022-11-30 02:26:56 +01:00
$maximagesize = Strings :: getBytesFromShorthand ( DI :: config () -> get ( 'system' , 'maximagesize' ));
2022-01-08 22:43:11 +00:00
2022-11-30 05:17:48 +01:00
if ( $maximagesize && ( $filesize > $maximagesize )) {
2022-01-08 22:43:11 +00:00
// Scale down to multiples of 640 until the maximum size isn't exceeded anymore
foreach ([ 5120 , 2560 , 1280 , 640 ] as $pixels ) {
if (( $filesize > $maximagesize ) && ( max ( $width , $height ) > $pixels )) {
Logger :: info ( 'Resize' , [ 'size' => $filesize , 'width' => $width , 'height' => $height , 'max' => $maximagesize , 'pixels' => $pixels ]);
2022-06-18 18:21:05 +02:00
$image -> scaleDown ( $pixels );
$filesize = strlen ( $image -> asString ());
$width = $image -> getWidth ();
$height = $image -> getHeight ();
2022-01-08 22:43:11 +00:00
}
}
if ( $filesize > $maximagesize ) {
Logger :: notice ( 'Image size is too big' , [ 'size' => $filesize , 'max' => $maximagesize ]);
return null ;
}
}
2022-06-18 18:21:05 +02:00
return $image ;
2022-01-08 22:43:11 +00:00
}
2022-06-18 18:21:05 +02:00
/**
* Fetches image from URL and returns an array with instance and local file name
*
* @ param string $image_url URL to image
* @ return array With : 'image' and 'filename' fields or empty array on error
*/
private static function loadImageFromURL ( string $image_url ) : array
2022-01-08 22:43:11 +00:00
{
$filename = basename ( $image_url );
if ( ! empty ( $image_url )) {
2022-04-02 21:16:22 +02:00
$ret = DI :: httpClient () -> get ( $image_url , HttpClientAccept :: IMAGE );
2022-03-29 06:24:20 +00:00
Logger :: debug ( 'Got picture' , [ 'Content-Type' => $ret -> getHeader ( 'Content-Type' ), 'url' => $image_url ]);
2022-01-08 22:43:11 +00:00
$img_str = $ret -> getBody ();
$type = $ret -> getContentType ();
} else {
$img_str = '' ;
$type = '' ;
}
if ( empty ( $img_str )) {
Logger :: notice ( 'Empty content' );
return [];
}
$type = Images :: getMimeTypeByData ( $img_str , $image_url , $type );
2022-06-18 18:21:05 +02:00
$image = new Image ( $img_str , $type );
2022-01-08 22:43:11 +00:00
2022-06-18 18:21:05 +02:00
$image = self :: fitImageSize ( $image );
if ( empty ( $image )) {
2022-01-08 22:43:11 +00:00
return [];
}
2022-06-18 18:21:05 +02:00
return [ 'image' => $image , 'filename' => $filename ];
2022-01-08 22:43:11 +00:00
}
2022-06-18 18:21:05 +02:00
/**
* Inserts uploaded image into database and removes local temporary file
*
* @ param array $files File array
* @ return array With 'image' for Image instance and 'filename' for local file name or empty array on error
*/
private static function uploadImage ( array $files ) : array
2021-05-16 09:56:02 +00:00
{
Logger :: info ( 'starting new upload' );
if ( empty ( $files )) {
Logger :: notice ( 'Empty upload file' );
return [];
}
if ( ! empty ( $files [ 'tmp_name' ])) {
if ( is_array ( $files [ 'tmp_name' ])) {
$src = $files [ 'tmp_name' ][ 0 ];
} else {
$src = $files [ 'tmp_name' ];
}
} else {
$src = '' ;
}
if ( ! empty ( $files [ 'name' ])) {
if ( is_array ( $files [ 'name' ])) {
$filename = basename ( $files [ 'name' ][ 0 ]);
} else {
$filename = basename ( $files [ 'name' ]);
}
} else {
$filename = '' ;
}
if ( ! empty ( $files [ 'size' ])) {
if ( is_array ( $files [ 'size' ])) {
$filesize = intval ( $files [ 'size' ][ 0 ]);
} else {
$filesize = intval ( $files [ 'size' ]);
}
} else {
$filesize = 0 ;
}
if ( ! empty ( $files [ 'type' ])) {
if ( is_array ( $files [ 'type' ])) {
$filetype = $files [ 'type' ][ 0 ];
} else {
$filetype = $files [ 'type' ];
}
} else {
$filetype = '' ;
}
if ( empty ( $src )) {
2022-01-06 07:34:16 +00:00
Logger :: notice ( 'No source file name' , [ 'files' => $files ]);
2021-05-16 09:56:02 +00:00
return [];
}
$filetype = Images :: getMimeTypeBySource ( $src , $filename , $filetype );
Logger :: info ( 'File upload' , [ 'src' => $src , 'filename' => $filename , 'size' => $filesize , 'type' => $filetype ]);
$imagedata = @ file_get_contents ( $src );
2022-06-18 18:21:05 +02:00
$image = new Image ( $imagedata , $filetype );
if ( ! $image -> isValid ()) {
2022-01-06 07:34:16 +00:00
Logger :: notice ( 'Image is unvalid' , [ 'files' => $files ]);
2021-05-16 09:56:02 +00:00
return [];
}
2022-06-18 18:21:05 +02:00
$image -> orient ( $src );
2021-05-16 09:56:02 +00:00
@ unlink ( $src );
2022-06-18 18:21:05 +02:00
$image = self :: fitImageSize ( $image );
if ( empty ( $image )) {
2022-01-08 22:43:11 +00:00
return [];
2021-05-16 09:56:02 +00:00
}
2023-05-09 05:29:05 +00:00
return [ 'image' => $image , 'filename' => $filename , 'size' => $filesize ];
2022-01-06 07:34:16 +00:00
}
/**
2022-06-18 18:21:05 +02:00
* Handles uploaded image and assigns it to given user id
*
2022-01-17 16:46:43 -05:00
* @ param int $uid User ID
* @ param array $files uploaded file array
2022-06-18 18:21:05 +02:00
* @ param string $album Album name ( optional )
2022-01-17 16:46:43 -05:00
* @ param string | null $allow_cid
* @ param string | null $allow_gid
* @ param string $deny_cid
* @ param string $deny_gid
2022-06-18 18:21:05 +02:00
* @ param string $desc Description ( optional )
* @ param string $resource_id GUID ( optional )
* @ return array photo record or empty array on error
2022-01-17 16:46:43 -05:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
2022-01-06 07:34:16 +00:00
*/
2022-01-06 20:13:32 +00:00
public static function upload ( int $uid , array $files , string $album = '' , string $allow_cid = null , string $allow_gid = null , string $deny_cid = '' , string $deny_gid = '' , string $desc = '' , string $resource_id = '' ) : array
2022-01-06 07:34:16 +00:00
{
$user = User :: getOwnerDataById ( $uid );
if ( empty ( $user )) {
Logger :: notice ( 'User not found' , [ 'uid' => $uid ]);
return [];
}
$data = self :: uploadImage ( $files );
if ( empty ( $data )) {
Logger :: info ( 'upload failed' );
return [];
}
2022-06-18 18:21:05 +02:00
$image = $data [ 'image' ];
2022-01-06 07:34:16 +00:00
$filename = $data [ 'filename' ];
2023-05-09 05:29:05 +00:00
$filesize = $data [ 'size' ];
2022-01-06 07:34:16 +00:00
2022-01-06 20:13:32 +00:00
$resource_id = $resource_id ? : self :: newResource ();
2022-01-06 07:34:16 +00:00
$album = $album ? : DI :: l10n () -> t ( 'Wall Photos' );
if ( is_null ( $allow_cid ) && is_null ( $allow_gid )) {
$allow_cid = '<' . $user [ 'id' ] . '>' ;
$allow_gid = '' ;
}
2021-05-16 09:56:02 +00:00
2023-05-09 05:29:05 +00:00
$smallest = self :: storeWithPreview ( $image , $user [ 'uid' ], $resource_id , $filename , $filesize , $album , $desc , $allow_cid , $allow_gid , $deny_cid , $deny_gid );
if ( $smallest < 0 ) {
2023-05-09 05:32:52 +00:00
Logger :: warning ( 'Photo could not be stored' , [ 'uid' => $user [ 'uid' ], 'resource_id' => $resource_id , 'filename' => $filename , 'album' => $album ]); Logger :: notice ( 'Photo not stored' , [ 'resource-id' => $resource_id ]);
2021-05-16 09:56:02 +00:00
return [];
}
$condition = [ 'resource-id' => $resource_id ];
$photo = self :: selectFirst ([ 'id' , 'datasize' , 'width' , 'height' , 'type' ], $condition , [ 'order' => [ 'width' => true ]]);
if ( empty ( $photo )) {
Logger :: notice ( 'Photo not found' , [ 'condition' => $condition ]);
return [];
}
$picture = [];
2022-01-06 07:34:16 +00:00
$picture [ 'id' ] = $photo [ 'id' ];
$picture [ 'resource_id' ] = $resource_id ;
$picture [ 'size' ] = $photo [ 'datasize' ];
$picture [ 'width' ] = $photo [ 'width' ];
$picture [ 'height' ] = $photo [ 'height' ];
$picture [ 'type' ] = $photo [ 'type' ];
$picture [ 'albumpage' ] = DI :: baseUrl () . '/photos/' . $user [ 'nickname' ] . '/image/' . $resource_id ;
2022-11-20 17:07:08 -05:00
$picture [ 'picture' ] = DI :: baseUrl () . '/photo/' . $resource_id . '-0.' . $image -> getExt ();
$picture [ 'preview' ] = DI :: baseUrl () . '/photo/' . $resource_id . '-' . $smallest . '.' . $image -> getExt ();
2022-01-06 07:34:16 +00:00
2021-05-16 09:56:02 +00:00
Logger :: info ( 'upload done' , [ 'picture' => $picture ]);
return $picture ;
}
2022-01-06 07:34:16 +00:00
2023-05-09 05:29:05 +00:00
/**
* store photo metadata in db and binary with preview photos in default backend
*
* @ param Image $image Image object with data
* @ param integer $uid User ID
* @ param string $resource_id Resource ID
* @ param string $filename Filename
* @ param integer $filesize Filesize
* @ param string $album Album name
* @ param string $description Photo caption
* @ param string $allow_cid Permissions , allowed contacts
* @ param string $allow_gid Permissions , allowed groups
* @ param string $deny_cid Permissions , denied contacts
* @ param string $deny_gid Permissions , denied group
*
* @ return boolean True on success
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function storeWithPreview ( Image $image , int $uid , string $resource_id , string $filename , int $filesize , string $album , string $description , string $allow_cid , string $allow_gid , string $deny_cid , string $deny_gid ) : int
{
$width = $image -> getWidth ();
$height = $image -> getHeight ();
$maximagesize = Strings :: getBytesFromShorthand ( DI :: config () -> get ( 'system' , 'maximagesize' ));
if ( $maximagesize && $filesize > $maximagesize ) {
// Scale down to multiples of 640 until the maximum size isn't exceeded anymore
foreach ([ 5120 , 2560 , 1280 , 640 , 320 ] as $pixels ) {
if ( $filesize > $maximagesize && max ( $width , $height ) > $pixels ) {
DI :: logger () -> info ( 'Resize' , [ 'size' => $filesize , 'width' => $width , 'height' => $height , 'max' => $maximagesize , 'pixels' => $pixels ]);
$image -> scaleDown ( $pixels );
$filesize = strlen ( $image -> asString ());
$width = $image -> getWidth ();
$height = $image -> getHeight ();
}
}
if ( $filesize > $maximagesize ) {
DI :: logger () -> notice ( 'Image size is too big' , [ 'size' => $filesize , 'max' => $maximagesize ]);
return - 1 ;
}
}
$width = $image -> getWidth ();
$height = $image -> getHeight ();
$smallest = 0 ;
$result = self :: store ( $image , $uid , 0 , $resource_id , $filename , $album , 0 , self :: DEFAULT , $allow_cid , $allow_gid , $deny_cid , $deny_gid , $description );
if ( ! $result ) {
Logger :: warning ( 'Photo could not be stored' , [ 'uid' => $uid , 'resource_id' => $resource_id , 'filename' => $filename , 'album' => $album ]);
return - 1 ;
}
if ( $width > 640 || $height > 640 ) {
$image -> scaleDown ( 640 );
}
if ( $width > 320 || $height > 320 ) {
$result = self :: store ( $image , $uid , 0 , $resource_id , $filename , $album , 1 , self :: DEFAULT , $allow_cid , $allow_gid , $deny_cid , $deny_gid , $description );
if ( $result ) {
$smallest = 1 ;
}
$image -> scaleDown ( 320 );
$result = self :: store ( $image , $uid , 0 , $resource_id , $filename , $album , 2 , self :: DEFAULT , $allow_cid , $allow_gid , $deny_cid , $deny_gid , $description );
if ( $result && ( $smallest == 0 )) {
$smallest = 2 ;
}
}
return $smallest ;
}
2022-01-06 07:34:16 +00:00
/**
2022-01-08 22:43:11 +00:00
* Upload a user avatar
2022-01-06 07:34:16 +00:00
*
2022-01-08 22:43:11 +00:00
* @ param int $uid User ID
* @ param array $files uploaded file array
* @ param string $url External image url
2022-01-06 07:34:16 +00:00
* @ return string avatar resource
*/
2022-01-08 22:43:11 +00:00
public static function uploadAvatar ( int $uid , array $files , string $url = '' ) : string
2022-01-06 07:34:16 +00:00
{
2022-01-08 22:43:11 +00:00
if ( ! empty ( $files )) {
$data = self :: uploadImage ( $files );
if ( empty ( $data )) {
Logger :: info ( 'upload failed' );
return '' ;
}
} elseif ( ! empty ( $url )) {
$data = self :: loadImageFromURL ( $url );
if ( empty ( $data )) {
Logger :: info ( 'loading from external url failed' );
return '' ;
}
} else {
Logger :: info ( 'Neither files nor url provided' );
2022-01-06 20:13:32 +00:00
return '' ;
2022-01-06 07:34:16 +00:00
}
2022-06-18 18:21:05 +02:00
$image = $data [ 'image' ];
2022-01-06 07:34:16 +00:00
$filename = $data [ 'filename' ];
2022-06-18 18:21:05 +02:00
$width = $image -> getWidth ();
$height = $image -> getHeight ();
2022-01-06 07:34:16 +00:00
2022-01-06 20:13:32 +00:00
$resource_id = self :: newResource ();
$album = DI :: l10n () -> t ( self :: PROFILE_PHOTOS );
2022-01-06 07:34:16 +00:00
// upload profile image (scales 4, 5, 6)
logger :: info ( 'starting new profile image upload' );
if ( $width > 300 || $height > 300 ) {
2022-06-18 18:21:05 +02:00
$image -> scaleDown ( 300 );
2022-01-06 07:34:16 +00:00
}
2022-06-18 18:21:05 +02:00
$r = self :: store ( $image , $uid , 0 , $resource_id , $filename , $album , 4 , self :: USER_AVATAR );
2022-01-06 07:34:16 +00:00
if ( ! $r ) {
2022-08-31 19:03:37 +00:00
logger :: warning ( 'profile image upload with scale 4 (300) failed' , [ 'uid' => $uid , 'resource_id' => $resource_id , 'filename' => $filename , 'album' => $album ]);
2022-01-06 07:34:16 +00:00
}
if ( $width > 80 || $height > 80 ) {
2022-06-18 18:21:05 +02:00
$image -> scaleDown ( 80 );
2022-01-06 07:34:16 +00:00
}
2022-06-18 18:21:05 +02:00
$r = self :: store ( $image , $uid , 0 , $resource_id , $filename , $album , 5 , self :: USER_AVATAR );
2022-01-06 07:34:16 +00:00
if ( ! $r ) {
2022-08-31 19:03:37 +00:00
logger :: warning ( 'profile image upload with scale 5 (80) failed' , [ 'uid' => $uid , 'resource_id' => $resource_id , 'filename' => $filename , 'album' => $album ]);
2022-01-06 07:34:16 +00:00
}
if ( $width > 48 || $height > 48 ) {
2022-06-18 18:21:05 +02:00
$image -> scaleDown ( 48 );
2022-01-06 07:34:16 +00:00
}
2022-06-18 18:21:05 +02:00
$r = self :: store ( $image , $uid , 0 , $resource_id , $filename , $album , 6 , self :: USER_AVATAR );
2022-01-06 07:34:16 +00:00
if ( ! $r ) {
2022-08-31 19:03:37 +00:00
logger :: warning ( 'profile image upload with scale 6 (48) failed' , [ 'uid' => $uid , 'resource_id' => $resource_id , 'filename' => $filename , 'album' => $album ]);
2022-01-06 07:34:16 +00:00
}
logger :: info ( 'new profile image upload ended' );
$condition = [ " `profile` AND `resource-id` != ? AND `uid` = ? " , $resource_id , $uid ];
2022-01-06 20:13:32 +00:00
self :: update ([ 'profile' => false , 'photo-type' => self :: DEFAULT ], $condition );
2022-01-06 07:34:16 +00:00
Contact :: updateSelfFromUserID ( $uid , true );
// Update global directory in background
Profile :: publishUpdate ( $uid );
return $resource_id ;
}
/**
2022-01-08 22:43:11 +00:00
* Upload a user banner
2022-01-06 07:34:16 +00:00
*
2022-01-08 22:43:11 +00:00
* @ param int $uid User ID
* @ param array $files uploaded file array
* @ param string $url External image url
2022-01-06 07:34:16 +00:00
* @ return string avatar resource
*/
2022-01-08 22:43:11 +00:00
public static function uploadBanner ( int $uid , array $files = [], string $url = '' ) : string
2022-01-06 07:34:16 +00:00
{
2022-01-08 22:43:11 +00:00
if ( ! empty ( $files )) {
$data = self :: uploadImage ( $files );
if ( empty ( $data )) {
Logger :: info ( 'upload failed' );
return '' ;
}
} elseif ( ! empty ( $url )) {
$data = self :: loadImageFromURL ( $url );
if ( empty ( $data )) {
Logger :: info ( 'loading from external url failed' );
return '' ;
}
} else {
Logger :: info ( 'Neither files nor url provided' );
2022-01-06 20:13:32 +00:00
return '' ;
2022-01-06 07:34:16 +00:00
}
2022-06-18 18:21:05 +02:00
$image = $data [ 'image' ];
2022-01-06 07:34:16 +00:00
$filename = $data [ 'filename' ];
2022-06-18 18:21:05 +02:00
$width = $image -> getWidth ();
$height = $image -> getHeight ();
2022-01-06 07:34:16 +00:00
2022-01-06 20:13:32 +00:00
$resource_id = self :: newResource ();
$album = DI :: l10n () -> t ( self :: BANNER_PHOTOS );
2022-01-06 07:34:16 +00:00
if ( $width > 960 ) {
2022-06-18 18:21:05 +02:00
$image -> scaleDown ( 960 );
2022-01-06 07:34:16 +00:00
}
2022-06-18 18:21:05 +02:00
$r = self :: store ( $image , $uid , 0 , $resource_id , $filename , $album , 3 , self :: USER_BANNER );
2022-01-06 07:34:16 +00:00
if ( ! $r ) {
2022-08-30 19:45:30 +00:00
logger :: warning ( 'profile banner upload with scale 3 (960) failed' );
2022-01-06 07:34:16 +00:00
}
2022-12-04 16:33:29 +00:00
logger :: info ( 'new profile banner upload ended' , [ 'uid' => $uid , 'resource_id' => $resource_id , 'filename' => $filename ]);
2022-01-06 07:34:16 +00:00
$condition = [ " `photo-type` = ? AND `resource-id` != ? AND `uid` = ? " , self :: USER_BANNER , $resource_id , $uid ];
2022-01-06 20:13:32 +00:00
self :: update ([ 'photo-type' => self :: DEFAULT ], $condition );
2022-01-06 07:34:16 +00:00
Contact :: updateSelfFromUserID ( $uid , true );
// Update global directory in background
Profile :: publishUpdate ( $uid );
return $resource_id ;
}
2017-12-07 08:56:11 -05:00
}