2019-05-09 18:54:39 +12:00
< ? php
2019-10-09 16:53:37 +13:00
global $utopia , $request , $response , $version ;
2019-05-09 18:54:39 +12:00
use Utopia\Exception ;
use Utopia\Validator\Text ;
use Utopia\Validator\WhiteList ;
use Utopia\Validator\Range ;
use Utopia\Validator\URL ;
use Utopia\Cache\Cache ;
use Utopia\Cache\Adapter\Filesystem ;
use Resize\Resize ;
use BaconQrCode\Renderer\ImageRenderer ;
use BaconQrCode\Renderer\Image\ImagickImageBackEnd ;
use BaconQrCode\Renderer\RendererStyle\RendererStyle ;
use BaconQrCode\Writer ;
$types = [
2019-09-07 05:10:41 +12:00
'browsers' => include __DIR__ . '/../config/avatars/browsers.php' ,
'credit-cards' => include __DIR__ . '/../config/avatars/credit-cards.php' ,
'flags' => include __DIR__ . '/../config/avatars/flags.php' ,
2019-05-09 18:54:39 +12:00
];
2019-09-07 05:10:41 +12:00
$avatarCallback = function ( $type , $code , $width , $height , $quality ) use ( $types , $response , $request ) {
2019-05-09 18:54:39 +12:00
$code = strtolower ( $code );
$type = strtolower ( $type );
2019-09-07 05:10:41 +12:00
if ( ! array_key_exists ( $type , $types )) {
2019-05-09 18:54:39 +12:00
throw new Exception ( 'Avatar set not found' , 404 );
}
2019-09-07 05:10:41 +12:00
if ( ! array_key_exists ( $code , $types [ $type ])) {
2019-05-09 18:54:39 +12:00
throw new Exception ( 'Avatar not found' , 404 );
}
if ( ! extension_loaded ( 'imagick' )) {
throw new Exception ( 'Imagick extension is missing' , 500 );
}
2019-09-07 05:10:41 +12:00
$output = 'png' ;
$date = date ( 'D, d M Y H:i:s' , time () + ( 60 * 60 * 24 * 45 )) . ' GMT' ; // 45 days cache
$key = md5 ( '/v1/avatars/:type/:code-' . $code . $width . $height . $quality . $output );
$path = $types [ $type ][ $code ];
$type = 'png' ;
2019-05-09 18:54:39 +12:00
2019-11-13 11:39:07 +13:00
if ( ! is_readable ( $path )) {
2019-11-13 11:40:14 +13:00
throw new Exception ( 'File not readable in ' . $path , 500 );
2019-05-09 18:54:39 +12:00
}
2019-09-07 05:10:41 +12:00
$cache = new Cache ( new Filesystem ( '/storage/cache/app-0' )); // Limit file number or size
$data = $cache -> load ( $key , 60 * 60 * 24 * 30 * 3 /* 3 months */ );
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
if ( $data ) {
2019-05-09 18:54:39 +12:00
//$output = (empty($output)) ? $type : $output;
$response
-> setContentType ( 'image/png' )
-> addHeader ( 'Expires' , $date )
-> addHeader ( 'X-Appwrite-Cache' , 'hit' )
-> send ( $data , 0 )
;
}
$resize = new Resize ( file_get_contents ( $path ));
2019-09-07 05:10:41 +12:00
$resize -> crop (( int ) $width , ( int ) $height );
2019-05-09 18:54:39 +12:00
$output = ( empty ( $output )) ? $type : $output ;
$response
-> setContentType ( 'image/png' )
-> addHeader ( 'Expires' , $date )
-> addHeader ( 'X-Appwrite-Cache' , 'miss' )
-> send ( '' , null )
;
$data = $resize -> output ( $output , $quality );
2019-11-17 10:17:23 +13:00
$cache -> save ( $key , $data );
2019-05-09 18:54:39 +12:00
echo $data ;
unset ( $resize );
exit ( 0 );
};
$utopia -> get ( '/v1/avatars/credit-cards/:code' )
-> desc ( 'Get Credit Card Icon' )
2019-10-07 09:10:52 +13:00
-> param ( 'code' , '' , function () use ( $types ) { return new WhiteList ( array_keys ( $types [ 'credit-cards' ])); }, 'Credit Card Code. Possible values: ' . implode ( ', ' , array_keys ( $types [ 'credit-cards' ])) . '.' )
-> param ( 'width' , 100 , function () { return new Range ( 0 , 2000 ); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100' , true )
-> param ( 'height' , 100 , function () { return new Range ( 0 , 2000 ); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100' , true )
-> param ( 'quality' , 100 , function () { return new Range ( 0 , 100 ); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100' , true )
2019-05-09 18:54:39 +12:00
-> label ( 'scope' , 'avatars.read' )
-> label ( 'sdk.namespace' , 'avatars' )
-> label ( 'sdk.method' , 'getCreditCard' )
2019-10-09 21:31:51 +13:00
-> label ( 'sdk.description' , '/docs/references/avatars/get-credit-card.md' )
2019-10-07 09:10:52 +13:00
-> action ( function ( $code , $width , $height , $quality ) use ( $avatarCallback ) { return $avatarCallback ( 'credit-cards' , $code , $width , $height , $quality );
2019-09-30 19:13:40 +13:00
});
2019-05-09 18:54:39 +12:00
$utopia -> get ( '/v1/avatars/browsers/:code' )
-> desc ( 'Get Browser Icon' )
2019-10-07 09:10:52 +13:00
-> param ( 'code' , '' , function () use ( $types ) { return new WhiteList ( array_keys ( $types [ 'browsers' ])); }, 'Browser Code.' )
-> param ( 'width' , 100 , function () { return new Range ( 0 , 2000 ); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100' , true )
-> param ( 'height' , 100 , function () { return new Range ( 0 , 2000 ); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100' , true )
-> param ( 'quality' , 100 , function () { return new Range ( 0 , 100 ); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100' , true )
2019-05-09 18:54:39 +12:00
-> label ( 'scope' , 'avatars.read' )
-> label ( 'sdk.namespace' , 'avatars' )
-> label ( 'sdk.method' , 'getBrowser' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/avatars/get-browser.md' )
2019-10-07 09:10:52 +13:00
-> action ( function ( $code , $width , $height , $quality ) use ( $avatarCallback ) { return $avatarCallback ( 'browsers' , $code , $width , $height , $quality );
2019-09-30 19:13:40 +13:00
});
2019-05-09 18:54:39 +12:00
$utopia -> get ( '/v1/avatars/flags/:code' )
-> desc ( 'Get Country Flag' )
2019-10-07 09:10:52 +13:00
-> param ( 'code' , '' , function () use ( $types ) { return new WhiteList ( array_keys ( $types [ 'flags' ])); }, 'Country Code. ISO Alpha-2 country code format.' )
-> param ( 'width' , 100 , function () { return new Range ( 0 , 2000 ); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100' , true )
-> param ( 'height' , 100 , function () { return new Range ( 0 , 2000 ); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100' , true )
-> param ( 'quality' , 100 , function () { return new Range ( 0 , 100 ); }, 'Image quality. Pass an integer between 0 to 100. Defaults to 100' , true )
2019-05-09 18:54:39 +12:00
-> label ( 'scope' , 'avatars.read' )
-> label ( 'sdk.namespace' , 'avatars' )
-> label ( 'sdk.method' , 'getFlag' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/avatars/get-flag.md' )
2019-10-07 09:10:52 +13:00
-> action ( function ( $code , $width , $height , $quality ) use ( $avatarCallback ) { return $avatarCallback ( 'flags' , $code , $width , $height , $quality );
2019-09-30 19:13:40 +13:00
});
2019-05-09 18:54:39 +12:00
2019-07-10 08:20:39 +12:00
$utopia -> get ( '/v1/avatars/image' )
2019-08-24 02:16:25 +12:00
-> desc ( 'Get Image from URL' )
2019-10-07 09:10:52 +13:00
-> param ( 'url' , '' , function () { return new URL (); }, 'Image URL which you want to crop.' )
-> param ( 'width' , 400 , function () { return new Range ( 0 , 2000 ); }, 'Resize preview image width, Pass an integer between 0 to 4000' , true )
-> param ( 'height' , 400 , function () { return new Range ( 0 , 2000 ); }, 'Resize preview image height, Pass an integer between 0 to 4000' , true )
2019-07-10 08:20:39 +12:00
-> label ( 'scope' , 'avatars.read' )
-> label ( 'sdk.namespace' , 'avatars' )
-> label ( 'sdk.method' , 'getImage' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/avatars/get-image.md' )
2019-07-10 08:20:39 +12:00
-> action (
2019-10-09 16:53:37 +13:00
function ( $url , $width , $height ) use ( $response ) {
2019-09-07 05:10:41 +12:00
$quality = 80 ;
$output = 'png' ;
$date = date ( 'D, d M Y H:i:s' , time () + ( 60 * 60 * 24 * 45 )) . ' GMT' ; // 45 days cache
$key = md5 ( '/v2/avatars/images-' . $url . '-' . $width . '/' . $height . '/' . $quality );
$type = 'png' ;
$cache = new Cache ( new Filesystem ( '/storage/cache/app-0' )); // Limit file number or size
$data = $cache -> load ( $key , 60 * 60 * 24 * 7 /* 1 week */ );
if ( $data ) {
2019-07-10 08:20:39 +12:00
$response
-> setContentType ( 'image/png' )
-> addHeader ( 'Expires' , $date )
-> addHeader ( 'X-Appwrite-Cache' , 'hit' )
-> send ( $data , 0 )
;
}
if ( ! extension_loaded ( 'imagick' )) {
throw new Exception ( 'Imagick extension is missing' , 500 );
}
$fetch = @ file_get_contents ( $url , false );
2019-09-07 05:10:41 +12:00
if ( ! $fetch ) {
2019-07-10 08:20:39 +12:00
throw new Exception ( 'Image not found' , 404 );
}
try {
$resize = new Resize ( $fetch );
2019-09-07 05:10:41 +12:00
} catch ( \Exception $exception ) {
2019-07-10 08:20:39 +12:00
throw new Exception ( 'Unable to parse image' , 500 );
}
2019-09-07 05:10:41 +12:00
$resize -> crop (( int ) $width , ( int ) $height );
2019-07-10 08:20:39 +12:00
$output = ( empty ( $output )) ? $type : $output ;
$response
-> setContentType ( 'image/png' )
-> addHeader ( 'Expires' , $date )
-> addHeader ( 'X-Appwrite-Cache' , 'miss' )
-> send ( '' , null )
;
$data = $resize -> output ( $output , $quality );
$cache -> save ( $key , $data );
echo $data ;
unset ( $resize );
exit ( 0 );
}
);
2019-05-09 18:54:39 +12:00
$utopia -> get ( '/v1/avatars/favicon' )
-> desc ( 'Get Favicon' )
2019-10-07 09:10:52 +13:00
-> param ( 'url' , '' , function () { return new URL (); }, 'Website URL which you want to fetch the favicon from.' )
2019-05-09 18:54:39 +12:00
-> label ( 'scope' , 'avatars.read' )
-> label ( 'sdk.namespace' , 'avatars' )
-> label ( 'sdk.method' , 'getFavicon' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/avatars/get-favicon.md' )
2019-05-09 18:54:39 +12:00
-> action (
2019-10-09 16:53:37 +13:00
function ( $url ) use ( $response , $version ) {
2019-09-07 05:10:41 +12:00
$width = 56 ;
$height = 56 ;
$quality = 80 ;
$output = 'png' ;
$date = date ( 'D, d M Y H:i:s' , time () + ( 60 * 60 * 24 * 45 )) . ' GMT' ; // 45 days cache
$key = md5 ( '/v2/avatars/favicon-' . $url );
$type = 'png' ;
$cache = new Cache ( new Filesystem ( '/storage/cache/app-0' )); // Limit file number or size
$data = $cache -> load ( $key , 60 * 60 * 24 * 30 * 3 /* 3 months */ );
if ( $data ) {
2019-05-09 18:54:39 +12:00
$response
-> setContentType ( 'image/png' )
-> addHeader ( 'Expires' , $date )
-> addHeader ( 'X-Appwrite-Cache' , 'hit' )
-> send ( $data , 0 )
;
}
if ( ! extension_loaded ( 'imagick' )) {
throw new Exception ( 'Imagick extension is missing' , 500 );
}
$curl = curl_init ();
curl_setopt_array ( $curl , [
CURLOPT_RETURNTRANSFER => 1 ,
CURLOPT_FOLLOWLOCATION => true ,
CURLOPT_MAXREDIRS => 3 ,
CURLOPT_URL => $url ,
CURLOPT_USERAGENT => sprintf ( APP_USERAGENT , $version ),
]);
2019-09-07 05:10:41 +12:00
$html = curl_exec ( $curl );
2019-05-09 18:54:39 +12:00
curl_close ( $curl );
2019-09-07 05:10:41 +12:00
if ( ! $html ) {
2019-05-09 18:54:39 +12:00
throw new Exception ( 'Failed to fetch remote URL' , 404 );
}
$doc = new DOMDocument ();
$doc -> strictErrorChecking = false ;
@ $doc -> loadHTML ( $html );
2019-09-07 05:10:41 +12:00
$links = $doc -> getElementsByTagName ( 'link' );
$outputHref = '' ;
$outputExt = '' ;
$space = 0 ;
2019-05-09 18:54:39 +12:00
foreach ( $links as $link ) { /* @var $link DOMElement */
2019-09-07 05:10:41 +12:00
$href = $link -> getAttribute ( 'href' );
$rel = $link -> getAttribute ( 'rel' );
$sizes = $link -> getAttribute ( 'sizes' );
$absolute = unparse_url ( array_merge ( parse_url ( $url ), parse_url ( $href )));
2019-05-09 18:54:39 +12:00
switch ( strtolower ( $rel )) {
case 'icon' :
case 'shortcut icon' :
2019-07-10 08:20:39 +12:00
//case 'apple-touch-icon':
2019-07-07 07:43:45 +12:00
$ext = pathinfo ( parse_url ( $absolute , PHP_URL_PATH ), PATHINFO_EXTENSION );
2019-05-09 18:54:39 +12:00
switch ( $ext ) {
case 'ico' :
case 'png' :
case 'jpg' :
case 'jpeg' :
$size = explode ( 'x' , strtolower ( $sizes ));
2019-09-07 05:10:41 +12:00
$sizeWidth = ( isset ( $size [ 0 ])) ? ( int ) $size [ 0 ] : 0 ;
$sizeHeight = ( isset ( $size [ 1 ])) ? ( int ) $size [ 1 ] : 0 ;
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
if (( $sizeWidth * $sizeHeight ) >= $space ) {
$space = $sizeWidth * $sizeHeight ;
$outputHref = $absolute ;
$outputExt = $ext ;
2019-05-09 18:54:39 +12:00
}
2019-07-10 08:20:39 +12:00
break ;
2019-05-09 18:54:39 +12:00
}
2019-07-10 08:20:39 +12:00
break ;
2019-05-09 18:54:39 +12:00
}
}
2019-09-07 05:10:41 +12:00
if ( empty ( $outputHref ) || empty ( $outputExt )) {
2019-07-07 07:43:45 +12:00
$default = parse_url ( $url );
2019-09-07 05:10:41 +12:00
$outputHref = $default [ 'scheme' ] . '://' . $default [ 'host' ] . '/favicon.ico' ;
2019-07-07 07:43:45 +12:00
$outputExt = 'ico' ;
2019-05-09 18:54:39 +12:00
}
2019-09-07 05:10:41 +12:00
if ( 'ico' == $outputExt ) { // Skip crop, Imagick isn\'t supporting icon files
2019-07-07 07:43:45 +12:00
$data = @ file_get_contents ( $outputHref , false );
2019-09-07 05:10:41 +12:00
if ( empty ( $data ) || ( mb_substr ( $data , 0 , 5 ) === '<html' ) || mb_substr ( $data , 0 , 5 ) === '<!doc' ) {
2019-07-07 07:43:45 +12:00
throw new Exception ( 'Favicon not found' , 404 );
}
2019-05-09 18:54:39 +12:00
$cache -> save ( $key , $data );
$response
-> setContentType ( 'image/x-icon' )
-> addHeader ( 'Expires' , $date )
-> addHeader ( 'X-Appwrite-Cache' , 'miss' )
-> send ( $data , 0 )
;
}
$fetch = @ file_get_contents ( $outputHref , false );
2019-09-07 05:10:41 +12:00
if ( ! $fetch ) {
2019-05-09 18:54:39 +12:00
throw new Exception ( 'Icon not found' , 404 );
}
$resize = new Resize ( $fetch );
2019-09-07 05:10:41 +12:00
$resize -> crop (( int ) $width , ( int ) $height );
2019-05-09 18:54:39 +12:00
$output = ( empty ( $output )) ? $type : $output ;
$response
-> setContentType ( 'image/png' )
-> addHeader ( 'Expires' , $date )
-> addHeader ( 'X-Appwrite-Cache' , 'miss' )
-> send ( '' , null )
;
$data = $resize -> output ( $output , $quality );
$cache -> save ( $key , $data );
echo $data ;
unset ( $resize );
exit ( 0 );
}
);
$utopia -> get ( '/v1/avatars/qr' )
2019-11-28 06:24:36 +13:00
-> desc ( 'Get QR Code' )
2019-10-07 09:10:52 +13:00
-> param ( 'text' , '' , function () { return new Text ( 512 ); }, 'Plain text to be converted to QR code image' )
-> param ( 'size' , 400 , function () { return new Range ( 0 , 1000 ); }, 'QR code size. Pass an integer between 0 to 1000. Defaults to 400.' , true )
-> param ( 'margin' , 1 , function () { return new Range ( 0 , 10 ); }, 'Margin From Edge. Pass an integer between 0 to 10. Defaults to 1.' , true )
-> param ( 'download' , 0 , function () { return new Range ( 0 , 1 ); }, 'Return resulting image with \'Content-Disposition: attachment \' headers for the browser to start downloading it. Pass 0 for no header, or 1 for otherwise. Default value is set to 0.' , true )
2019-05-09 18:54:39 +12:00
-> label ( 'scope' , 'avatars.read' )
-> label ( 'sdk.namespace' , 'avatars' )
-> label ( 'sdk.method' , 'getQR' )
2019-10-08 20:09:35 +13:00
-> label ( 'sdk.description' , '/docs/references/avatars/get-qr.md' )
2019-05-09 18:54:39 +12:00
-> action (
2019-09-07 05:10:41 +12:00
function ( $text , $size , $margin , $download ) use ( $response ) {
2019-05-09 18:54:39 +12:00
$renderer = new ImageRenderer (
new RendererStyle ( $size , $margin ),
new ImagickImageBackEnd ( 'png' , 100 )
);
$writer = new Writer ( $renderer );
2019-09-07 05:10:41 +12:00
if ( $download ) {
2019-05-09 18:54:39 +12:00
$response -> addHeader ( 'Content-Disposition' , 'attachment; filename="qr.png"' );
}
$response
2019-09-07 05:10:41 +12:00
-> addHeader ( 'Expires' , date ( 'D, d M Y H:i:s' , time () + ( 60 * 60 * 24 * 45 )) . ' GMT' ) // 45 days cache
2019-05-09 18:54:39 +12:00
-> setContentType ( 'image/png' )
-> send ( '' , $writer -> writeString ( $text ))
;
}
);
2019-09-07 05:10:41 +12:00
function unparse_url ( $parsed_url , $ommit = array ())
2019-05-09 18:54:39 +12:00
{
2019-09-07 05:10:41 +12:00
if ( isset ( $parsed_url [ 'path' ]) && mb_substr ( $parsed_url [ 'path' ], 0 , 1 ) !== '/' ) {
$parsed_url [ 'path' ] = '/' . $parsed_url [ 'path' ];
2019-05-09 18:54:39 +12:00
}
2019-09-07 05:10:41 +12:00
$p = array ();
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
$p [ 'scheme' ] = isset ( $parsed_url [ 'scheme' ]) ? $parsed_url [ 'scheme' ] . '://' : '' ;
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
$p [ 'host' ] = isset ( $parsed_url [ 'host' ]) ? $parsed_url [ 'host' ] : '' ;
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
$p [ 'port' ] = isset ( $parsed_url [ 'port' ]) ? ':' . $parsed_url [ 'port' ] : '' ;
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
$p [ 'user' ] = isset ( $parsed_url [ 'user' ]) ? $parsed_url [ 'user' ] : '' ;
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
$p [ 'pass' ] = isset ( $parsed_url [ 'pass' ]) ? ':' . $parsed_url [ 'pass' ] : '' ;
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
$p [ 'pass' ] = ( $p [ 'user' ] || $p [ 'pass' ]) ? $p [ 'pass' ] . '@' : '' ;
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
$p [ 'path' ] = isset ( $parsed_url [ 'path' ]) ? $parsed_url [ 'path' ] : '' ;
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
$p [ 'query' ] = isset ( $parsed_url [ 'query' ]) ? '?' . $parsed_url [ 'query' ] : '' ;
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
$p [ 'fragment' ] = isset ( $parsed_url [ 'fragment' ]) ? '#' . $parsed_url [ 'fragment' ] : '' ;
2019-05-09 18:54:39 +12:00
2019-09-07 05:10:41 +12:00
if ( $ommit ) {
foreach ( $ommit as $key ) {
if ( isset ( $p [ $key ])) {
2019-05-09 18:54:39 +12:00
$p [ $key ] = '' ;
}
}
}
return $p [ 'scheme' ] . $p [ 'user' ] . $p [ 'pass' ] . $p [ 'host' ] . $p [ 'port' ] . $p [ 'path' ] . $p [ 'query' ] . $p [ 'fragment' ];
2019-09-07 05:10:41 +12:00
}