1
0
Fork 0
mirror of synced 2024-06-01 18:39:57 +12:00
appwrite/app/controllers/api/avatars.php

1248 lines
54 KiB
PHP
Raw Normal View History

2019-05-09 18:54:39 +12:00
<?php
2022-05-03 06:06:12 +12:00
use Appwrite\Extend\Exception;
2021-02-20 02:59:46 +13:00
use Appwrite\URL\URL as URLParse;
use Appwrite\Utopia\Response;
2024-03-07 06:34:21 +13:00
use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
2020-06-29 05:31:21 +12:00
use Utopia\App;
use Utopia\CLI\Console;
2021-02-20 02:59:46 +13:00
use Utopia\Config\Config;
2023-04-19 19:25:05 +12:00
use Utopia\Database\Database;
use Utopia\Database\DateTime;
2022-05-03 06:06:12 +12:00
use Utopia\Database\Document;
2023-04-26 20:21:10 +12:00
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\UID;
use Utopia\Domains\Domain;
2021-02-20 02:59:46 +13:00
use Utopia\Image\Image;
use Utopia\Logger\Log;
use Utopia\Logger\Logger;
2020-06-22 00:18:00 +12:00
use Utopia\Validator\Boolean;
2021-02-20 02:59:46 +13:00
use Utopia\Validator\HexColor;
2019-05-09 18:54:39 +12:00
use Utopia\Validator\Range;
2021-02-20 02:59:46 +13:00
use Utopia\Validator\Text;
use Utopia\Validator\URL;
2021-02-20 02:59:46 +13:00
use Utopia\Validator\WhiteList;
2019-05-09 18:54:39 +12:00
2022-07-08 02:51:22 +12:00
$avatarCallback = function (string $type, string $code, int $width, int $height, int $quality, Response $response) {
2020-06-30 09:43:34 +12:00
2020-06-20 23:20:49 +12:00
$code = \strtolower($code);
$type = \strtolower($type);
2021-02-20 02:59:46 +13:00
$set = Config::getParam('avatar-' . $type, []);
2019-05-09 18:54:39 +12:00
2020-06-27 17:36:22 +12:00
if (empty($set)) {
throw new Exception(Exception::AVATAR_SET_NOT_FOUND);
2019-05-09 18:54:39 +12:00
}
2020-06-27 17:36:22 +12:00
if (!\array_key_exists($code, $set)) {
throw new Exception(Exception::AVATAR_NOT_FOUND);
2019-05-09 18:54:39 +12:00
}
2020-06-20 23:20:49 +12:00
if (!\extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
2019-05-09 18:54:39 +12:00
}
$output = 'png';
$path = $set[$code]['path'];
$type = 'png';
2019-05-09 18:54:39 +12:00
2020-06-20 23:20:49 +12:00
if (!\is_readable($path)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'File not readable in ' . $path);
2019-05-09 18:54:39 +12:00
}
2021-02-20 02:59:46 +13:00
$image = new Image(\file_get_contents($path));
$image->crop((int) $width, (int) $height);
2019-05-09 18:54:39 +12:00
$output = (empty($output)) ? $type : $output;
2021-02-20 02:59:46 +13:00
$data = $image->output($output, $quality);
2022-07-24 21:49:51 +12:00
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
2022-07-24 21:49:51 +12:00
->setContentType('image/png')
2023-04-19 19:25:05 +12:00
->file($data);
2021-02-20 02:59:46 +13:00
unset($image);
2019-05-09 18:54:39 +12:00
};
$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForConsole, ?Logger $logger) {
2023-04-26 20:21:10 +12:00
try {
2023-04-28 19:30:30 +12:00
$user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
2023-04-26 20:21:10 +12:00
$sessions = $user->getAttribute('sessions', []);
$gitHubSession = null;
foreach ($sessions as $session) {
if ($session->getAttribute('provider', '') === 'github') {
$gitHubSession = $session;
break;
}
}
if (empty($gitHubSession)) {
throw new Exception(Exception::USER_SESSION_NOT_FOUND, 'GitHub session not found.');
}
$provider = $gitHubSession->getAttribute('provider', '');
$accessToken = $gitHubSession->getAttribute('providerAccessToken');
$accessTokenExpiry = $gitHubSession->getAttribute('providerAccessTokenExpiry');
$refreshToken = $gitHubSession->getAttribute('providerRefreshToken');
2023-04-26 20:21:10 +12:00
2023-10-26 06:33:23 +13:00
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
2023-04-26 20:21:10 +12:00
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
if (!\class_exists($className)) {
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
$oauth2 = new $className($appId, $appSecret, '', [], []);
2023-04-28 19:30:30 +12:00
$isExpired = new \DateTime($accessTokenExpiry) < new \DateTime('now');
if ($isExpired) {
try {
$oauth2->refreshTokens($refreshToken);
$accessToken = $oauth2->getAccessToken('');
$refreshToken = $oauth2->getRefreshToken('');
2023-04-28 21:07:26 +12:00
$verificationId = $oauth2->getUserID($accessToken);
if (empty($verificationId)) {
throw new \Exception("Locked tokens."); // Race codition, handeled in catch
2023-04-28 19:30:30 +12:00
}
2023-04-26 20:21:10 +12:00
$gitHubSession
2023-04-28 19:30:30 +12:00
->setAttribute('providerAccessToken', $accessToken)
->setAttribute('providerRefreshToken', $refreshToken)
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry('')));
2023-04-26 20:21:10 +12:00
Authorization::skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession));
2023-04-26 20:21:10 +12:00
2023-12-15 02:32:06 +13:00
$dbForProject->purgeCachedDocument('users', $user->getId());
2023-04-28 19:30:30 +12:00
} catch (Throwable $err) {
$index = 0;
do {
$previousAccessToken = $gitHubSession->getAttribute('providerAccessToken');
2023-04-26 20:21:10 +12:00
2023-04-28 19:30:30 +12:00
$user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
$sessions = $user->getAttribute('sessions', []);
$gitHubSession = new Document();
foreach ($sessions as $session) {
if ($session->getAttribute('provider', '') === 'github') {
$gitHubSession = $session;
break;
}
}
$accessToken = $gitHubSession->getAttribute('providerAccessToken');
2023-04-28 19:30:30 +12:00
2023-04-28 21:07:26 +12:00
if ($accessToken !== $previousAccessToken) {
2023-04-28 19:30:30 +12:00
break;
}
$index++;
2023-04-28 21:07:26 +12:00
\usleep(500000);
2023-04-28 19:30:30 +12:00
} while ($index < 10);
}
2023-04-26 23:51:19 +12:00
}
2023-04-26 20:21:10 +12:00
2023-04-28 21:07:26 +12:00
$oauth2 = new $className($appId, $appSecret, '', [], []);
2023-04-26 20:21:10 +12:00
$githubUser = $oauth2->getUserSlug($accessToken);
$githubId = $oauth2->getUserID($accessToken);
return [
'name' => $githubUser,
'id' => $githubId
];
} catch (Exception $error) {
if ($logger) {
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
$log = new Log();
$log->setNamespace('console');
$log->setServer(\gethostname());
$log->setVersion($version);
$log->setType(Log::TYPE_ERROR);
$log->setMessage($error->getMessage());
$log->addTag('code', $error->getCode());
$log->addTag('verboseType', get_class($error));
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('detailedTrace', $error->getTrace());
$log->setAction('avatarsGetGitHub');
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
$responseCode = $logger->addLog($log);
Console::info('GitHub error log pushed with status code: ' . $responseCode);
}
Console::warning("Failed: {$error->getMessage()}");
Console::warning($error->getTraceAsString());
2023-04-26 20:21:10 +12:00
return [];
}
return [];
2023-04-26 20:21:10 +12:00
};
2020-06-29 05:31:21 +12:00
App::get('/v1/avatars/credit-cards/:code')
2023-08-02 03:26:48 +12:00
->desc('Get credit card icon')
2022-07-24 19:05:24 +12:00
->groups(['api', 'avatars'])
2019-05-09 18:54:39 +12:00
->label('scope', 'avatars.read')
2022-08-17 21:25:47 +12:00
->label('cache', true)
2022-08-16 01:55:11 +12:00
->label('cache.resource', 'avatar/credit-card')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
2019-05-09 18:54:39 +12:00
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getCreditCard')
2020-05-18 16:26:28 +12:00
->label('sdk.methodType', 'location')
2019-10-09 21:31:51 +13:00
->label('sdk.description', '/docs/references/avatars/get-credit-card.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
2020-11-12 11:02:02 +13:00
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
2021-02-20 02:59:46 +13:00
->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-credit-cards'))), 'Credit Card Code. Possible values: ' . \implode(', ', \array_keys(Config::getParam('avatar-credit-cards'))) . '.')
2022-04-20 20:27:28 +12:00
->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
2020-09-11 02:40:14 +12:00
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
2020-12-27 03:59:15 +13:00
->inject('response')
2022-07-08 02:51:22 +12:00
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response));
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App::get('/v1/avatars/browsers/:code')
2023-08-02 03:26:48 +12:00
->desc('Get browser icon')
2022-07-24 19:05:24 +12:00
->groups(['api', 'avatars'])
2019-05-09 18:54:39 +12:00
->label('scope', 'avatars.read')
2022-08-17 21:25:47 +12:00
->label('cache', true)
2022-08-16 01:55:11 +12:00
->label('cache.resource', 'avatar/browser')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
2019-05-09 18:54:39 +12:00
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getBrowser')
->label('sdk.methodType', 'location')
2019-10-08 20:09:35 +13:00
->label('sdk.description', '/docs/references/avatars/get-browser.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
2020-11-12 11:02:02 +13:00
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
2020-09-11 02:40:14 +12:00
->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-browsers'))), 'Browser Code.')
2022-04-20 20:27:28 +12:00
->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
2020-09-11 02:40:14 +12:00
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
2020-12-27 03:59:15 +13:00
->inject('response')
2022-07-08 02:51:22 +12:00
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response));
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App::get('/v1/avatars/flags/:code')
2023-08-02 03:26:48 +12:00
->desc('Get country flag')
2022-07-24 19:05:24 +12:00
->groups(['api', 'avatars'])
2019-05-09 18:54:39 +12:00
->label('scope', 'avatars.read')
2022-08-17 21:25:47 +12:00
->label('cache', true)
2022-08-16 01:55:11 +12:00
->label('cache.resource', 'avatar/flag')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
2019-05-09 18:54:39 +12:00
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getFlag')
2020-05-18 16:26:28 +12:00
->label('sdk.methodType', 'location')
2019-10-08 20:09:35 +13:00
->label('sdk.description', '/docs/references/avatars/get-flag.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
2020-11-12 11:02:02 +13:00
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
2020-09-11 02:40:14 +12:00
->param('code', '', new WhiteList(\array_keys(Config::getParam('avatar-flags'))), 'Country Code. ISO Alpha-2 country code format.')
2022-04-20 20:31:12 +12:00
->param('width', 100, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
2020-09-11 02:40:14 +12:00
->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true)
2020-12-27 03:59:15 +13:00
->inject('response')
2022-07-08 02:51:22 +12:00
->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response));
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App::get('/v1/avatars/image')
2023-08-02 03:26:48 +12:00
->desc('Get image from URL')
2020-06-26 06:32:12 +12:00
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
2022-08-17 21:25:47 +12:00
->label('cache', true)
2022-08-16 01:55:11 +12:00
->label('cache.resource', 'avatar/image')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getImage')
2020-05-18 16:26:28 +12:00
->label('sdk.methodType', 'location')
2019-10-08 20:09:35 +13:00
->label('sdk.description', '/docs/references/avatars/get-image.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
2020-11-12 11:02:02 +13:00
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
2022-02-17 04:16:37 +13:00
->param('url', '', new URL(['http', 'https']), 'Image URL which you want to crop.')
2022-04-20 20:31:12 +12:00
->param('width', 400, new Range(0, 2000), 'Resize preview image width, Pass an integer between 0 to 2000. Defaults to 400.', true)
->param('height', 400, new Range(0, 2000), 'Resize preview image height, Pass an integer between 0 to 2000. Defaults to 400.', true)
2020-12-27 03:59:15 +13:00
->inject('response')
2022-07-08 02:51:22 +12:00
->action(function (string $url, int $width, int $height, Response $response) {
2020-06-30 09:43:34 +12:00
$quality = 80;
$output = 'png';
$type = 'png';
2020-06-30 09:43:34 +12:00
if (!\extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
2020-06-30 09:43:34 +12:00
}
$domain = new Domain(\parse_url($url, PHP_URL_HOST));
if (!$domain->isKnown()) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
$fetch = @\file_get_contents($url);
2020-06-30 09:43:34 +12:00
if (!$fetch) {
throw new Exception(Exception::AVATAR_IMAGE_NOT_FOUND);
2020-06-30 09:43:34 +12:00
}
2020-06-30 09:43:34 +12:00
try {
2021-02-20 02:59:46 +13:00
$image = new Image($fetch);
} catch (\Throwable $exception) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unable to parse image');
2020-06-30 09:43:34 +12:00
}
2021-02-20 02:59:46 +13:00
$image->crop((int) $width, (int) $height);
2020-06-30 09:43:34 +12:00
$output = (empty($output)) ? $type : $output;
2021-02-20 02:59:46 +13:00
$data = $image->output($output, $quality);
2022-07-24 21:49:51 +12:00
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
2022-07-24 21:49:51 +12:00
->setContentType('image/png')
2023-04-19 19:25:05 +12:00
->file($data);
2021-02-20 02:59:46 +13:00
unset($image);
2020-12-27 03:59:15 +13:00
});
2020-06-29 05:31:21 +12:00
App::get('/v1/avatars/favicon')
2023-08-02 03:26:48 +12:00
->desc('Get favicon')
2022-07-24 19:05:24 +12:00
->groups(['api', 'avatars'])
2019-05-09 18:54:39 +12:00
->label('scope', 'avatars.read')
2022-08-17 21:25:47 +12:00
->label('cache', true)
2022-08-16 01:55:11 +12:00
->label('cache.resource', 'avatar/favicon')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
2019-05-09 18:54:39 +12:00
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getFavicon')
2020-05-18 16:26:28 +12:00
->label('sdk.methodType', 'location')
2019-10-08 20:09:35 +13:00
->label('sdk.description', '/docs/references/avatars/get-favicon.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
2020-11-12 11:02:02 +13:00
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
2022-02-17 04:16:37 +13:00
->param('url', '', new URL(['http', 'https']), 'Website URL which you want to fetch the favicon from.')
2020-12-27 03:59:15 +13:00
->inject('response')
2022-07-08 02:51:22 +12:00
->action(function (string $url, Response $response) {
2020-06-30 09:43:34 +12:00
$width = 56;
$height = 56;
$quality = 80;
$output = 'png';
$type = 'png';
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if (!\extension_loaded('imagick')) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
2020-06-30 09:43:34 +12:00
}
2019-05-09 18:54:39 +12:00
$domain = new Domain(\parse_url($url, PHP_URL_HOST));
if (!$domain->isKnown()) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
2020-06-30 09:43:34 +12:00
$curl = \curl_init();
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
\curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_URL => $url,
2022-05-09 03:10:56 +12:00
CURLOPT_USERAGENT => \sprintf(
APP_USERAGENT,
2020-06-30 23:09:28 +12:00
App::getEnv('_APP_VERSION', 'UNKNOWN'),
2020-06-30 09:43:34 +12:00
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
),
]);
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$html = \curl_exec($curl);
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
\curl_close($curl);
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if (!$html) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
2020-06-30 09:43:34 +12:00
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$doc = new DOMDocument();
$doc->strictErrorChecking = false;
@$doc->loadHTML($html);
$links = $doc->getElementsByTagName('link');
$outputHref = '';
$outputExt = '';
$space = 0;
foreach ($links as $link) { /* @var $link DOMElement */
$href = $link->getAttribute('href');
$rel = $link->getAttribute('rel');
$sizes = $link->getAttribute('sizes');
$absolute = URLParse::unparse(\array_merge(\parse_url($url), \parse_url($href)));
switch (\strtolower($rel)) {
case 'icon':
case 'shortcut icon':
//case 'apple-touch-icon':
$ext = \pathinfo(\parse_url($absolute, PHP_URL_PATH), PATHINFO_EXTENSION);
switch ($ext) {
case 'ico':
case 'png':
case 'jpg':
case 'jpeg':
$size = \explode('x', \strtolower($sizes));
$sizeWidth = (int) ($size[0] ?? 0);
$sizeHeight = (int) ($size[1] ?? 0);
2020-06-30 09:43:34 +12:00
if (($sizeWidth * $sizeHeight) >= $space) {
$space = $sizeWidth * $sizeHeight;
$outputHref = $absolute;
$outputExt = $ext;
}
break;
}
2020-06-30 09:43:34 +12:00
break;
2019-05-09 18:54:39 +12:00
}
2020-06-30 09:43:34 +12:00
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if (empty($outputHref) || empty($outputExt)) {
$default = \parse_url($url);
2019-05-09 18:54:39 +12:00
2021-02-20 02:59:46 +13:00
$outputHref = $default['scheme'] . '://' . $default['host'] . '/favicon.ico';
2020-06-30 09:43:34 +12:00
$outputExt = 'ico';
}
2019-05-09 18:54:39 +12:00
2023-08-30 18:14:19 +12:00
$domain = new Domain(\parse_url($outputHref, PHP_URL_HOST));
if (!$domain->isKnown()) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
2020-06-30 09:43:34 +12:00
if ('ico' == $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
$data = @\file_get_contents($outputHref, false);
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if (empty($data) || (\mb_substr($data, 0, 5) === '<html') || \mb_substr($data, 0, 5) === '<!doc') {
2022-08-14 18:56:12 +12:00
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND, 'Favicon not found');
2019-05-09 18:54:39 +12:00
}
2022-07-27 00:50:33 +12:00
$response
2022-07-31 06:55:36 +12:00
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
2022-07-27 00:50:33 +12:00
->setContentType('image/x-icon')
2023-04-19 19:25:05 +12:00
->file($data);
2020-06-30 09:43:34 +12:00
}
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
$fetch = @\file_get_contents($outputHref, false);
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if (!$fetch) {
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND);
2020-06-30 09:43:34 +12:00
}
2019-05-09 18:54:39 +12:00
2021-02-20 02:59:46 +13:00
$image = new Image($fetch);
$image->crop((int) $width, (int) $height);
2020-06-30 09:43:34 +12:00
$output = (empty($output)) ? $type : $output;
2021-02-20 02:59:46 +13:00
$data = $image->output($output, $quality);
2020-06-30 09:43:34 +12:00
2022-07-24 21:49:51 +12:00
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
2022-07-24 21:49:51 +12:00
->setContentType('image/png')
2023-04-19 19:25:05 +12:00
->file($data);
2021-02-20 02:59:46 +13:00
unset($image);
2020-12-27 03:59:15 +13:00
});
2019-05-09 18:54:39 +12:00
2020-06-29 05:31:21 +12:00
App::get('/v1/avatars/qr')
2023-08-02 03:26:48 +12:00
->desc('Get QR code')
2020-06-26 06:32:12 +12:00
->groups(['api', 'avatars'])
2019-05-09 18:54:39 +12:00
->label('scope', 'avatars.read')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
2019-05-09 18:54:39 +12:00
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getQR')
2020-05-18 16:26:28 +12:00
->label('sdk.methodType', 'location')
2019-10-08 20:09:35 +13:00
->label('sdk.description', '/docs/references/avatars/get-qr.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
2020-11-12 11:02:02 +13:00
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
2020-09-11 02:40:14 +12:00
->param('text', '', new Text(512), 'Plain text to be converted to QR code image.')
2022-04-12 23:36:03 +12:00
->param('size', 400, new Range(1, 1000), 'QR code size. Pass an integer between 1 to 1000. Defaults to 400.', true)
2020-09-11 02:40:14 +12:00
->param('margin', 1, new Range(0, 10), 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true)
->param('download', false, new Boolean(true), '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)
2020-12-27 03:59:15 +13:00
->inject('response')
2022-05-09 03:10:56 +12:00
->action(function (string $text, int $size, int $margin, bool $download, Response $response) {
2020-06-30 09:43:34 +12:00
$download = ($download === '1' || $download === 'true' || $download === 1 || $download === true);
2020-10-27 13:12:35 +13:00
$options = new QROptions([
'addQuietzone' => true,
'quietzoneSize' => $margin,
'outputType' => QRCode::OUTPUT_IMAGICK,
2020-10-23 07:50:57 +13:00
]);
2020-10-27 13:12:35 +13:00
$qrcode = new QRCode($options);
2019-05-09 18:54:39 +12:00
2020-06-30 09:43:34 +12:00
if ($download) {
$response->addHeader('Content-Disposition', 'attachment; filename="qr.png"');
2020-06-10 07:48:26 +12:00
}
2020-06-30 09:43:34 +12:00
2021-02-20 02:59:46 +13:00
$image = new Image($qrcode->render($text));
$image->crop((int) $size, (int) $size);
2021-01-10 13:17:44 +13:00
2020-06-30 09:43:34 +12:00
$response
2022-07-27 00:50:33 +12:00
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
2020-06-30 09:43:34 +12:00
->setContentType('image/png')
2023-04-19 19:25:05 +12:00
->send($image->output('png', 9));
2020-12-27 03:59:15 +13:00
});
2020-06-10 07:48:26 +12:00
2020-06-29 05:31:21 +12:00
App::get('/v1/avatars/initials')
2023-08-02 03:26:48 +12:00
->desc('Get user initials')
2020-06-26 06:32:12 +12:00
->groups(['api', 'avatars'])
2020-06-10 07:48:26 +12:00
->label('scope', 'avatars.read')
2022-08-16 01:55:11 +12:00
->label('cache.resource', 'avatar/initials')
2021-04-16 19:22:17 +12:00
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
2020-06-10 07:48:26 +12:00
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getInitials')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-initials.md')
2020-11-12 10:02:24 +13:00
->label('sdk.response.code', Response::STATUS_CODE_OK)
2020-11-12 11:02:02 +13:00
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
2020-09-11 02:40:14 +12:00
->param('name', '', new Text(128), 'Full Name. When empty, current user name or email will be used. Max length: 128 chars.', true)
2022-04-20 20:27:28 +12:00
->param('width', 500, new Range(0, 2000), 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 500, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
2020-09-11 02:40:14 +12:00
->param('background', '', new HexColor(), 'Changes background color. By default a random color will be picked and stay will persistent to the given name.', true)
2020-12-27 03:59:15 +13:00
->inject('response')
->inject('user')
2022-09-05 21:36:01 +12:00
->action(function (string $name, int $width, int $height, string $background, Response $response, Document $user) {
2020-06-30 09:43:34 +12:00
$themes = [
2023-09-19 10:34:37 +12:00
['background' => '#FD366E'], // Default (Pink)
['background' => '#FE9567'], // Orange
['background' => '#7C67FE'], // Purple
['background' => '#68A3FE'], // Blue
['background' => '#85DBD8'], // Mint
2020-06-30 09:43:34 +12:00
];
$name = (!empty($name)) ? $name : $user->getAttribute('name', $user->getAttribute('email', ''));
$words = \explode(' ', \strtoupper($name));
2021-07-26 00:13:49 +12:00
// if there is no space, try to split by `_` underscore
2022-05-09 03:10:56 +12:00
$words = (count($words) == 1) ? \explode('_', \strtoupper($name)) : $words;
2024-01-23 06:24:12 +13:00
$initials = '';
2020-06-30 09:43:34 +12:00
$code = 0;
foreach ($words as $key => $w) {
2023-09-06 20:54:59 +12:00
if (ctype_alnum($w[0] ?? '')) {
$initials .= $w[0];
$code += ord($w[0]);
2020-06-30 09:43:34 +12:00
if ($key == 1) {
break;
}
2020-06-10 07:48:26 +12:00
}
2020-06-30 09:43:34 +12:00
}
2020-06-10 07:48:26 +12:00
2021-02-20 02:59:46 +13:00
$rand = \substr($code, -1);
2022-09-05 21:36:01 +12:00
// Wrap rand value to avoid out of range
$rand = ($rand > \count($themes) - 1) ? $rand % \count($themes) : $rand;
2021-02-20 02:59:46 +13:00
$background = (!empty($background)) ? '#' . $background : $themes[$rand]['background'];
2020-06-30 09:43:34 +12:00
$image = new \Imagick();
$punch = new \Imagick();
2020-06-30 09:43:34 +12:00
$draw = new \ImagickDraw();
$fontSize = \min($width, $height) / 2;
2021-02-20 02:59:46 +13:00
$punch->newImage($width, $height, 'transparent');
2023-09-19 10:34:37 +12:00
$draw->setFont(__DIR__ . "/../../assets/fonts/inter-v8-latin-regular.woff2");
$image->setFont(__DIR__ . "/../../assets/fonts/inter-v8-latin-regular.woff2");
2020-06-30 09:43:34 +12:00
$draw->setFillColor(new ImagickPixel('black'));
2020-06-30 09:43:34 +12:00
$draw->setFontSize($fontSize);
2021-02-20 02:59:46 +13:00
2020-06-30 09:43:34 +12:00
$draw->setTextAlignment(\Imagick::ALIGN_CENTER);
$draw->annotation($width / 1.97, ($height / 2) + ($fontSize / 3), $initials);
2021-02-20 02:59:46 +13:00
$punch->drawImage($draw);
$punch->negateImage(true, Imagick::CHANNEL_ALPHA);
2020-06-30 09:43:34 +12:00
$image->newImage($width, $height, $background);
$image->setImageFormat("png");
$image->compositeImage($punch, Imagick::COMPOSITE_COPYOPACITY, 0, 0);
2020-06-30 09:43:34 +12:00
$response
2021-02-20 02:59:46 +13:00
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
2020-06-30 09:43:34 +12:00
->setContentType('image/png')
2023-04-19 19:25:05 +12:00
->file($image->getImageBlob());
});
2023-04-26 23:06:11 +12:00
App::get('/v1/cards/cloud')
2023-04-26 20:21:10 +12:00
->desc('Get Front Of Cloud Card')
2023-04-19 19:25:05 +12:00
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
2023-04-29 08:03:44 +12:00
->label('cache', true)
->label('cache.resourceType', 'cards/cloud')
->label('cache.resource', 'card/{request.userId}')
2023-04-19 19:25:05 +12:00
->label('docs', false)
->label('origin', '*')
2023-04-26 20:21:10 +12:00
->param('userId', '', new UID(), 'User ID.', true)
2023-04-28 00:34:50 +12:00
->param('mock', '', new WhiteList(['employee', 'employee-2digit', 'hero', 'contributor', 'normal', 'platinum', 'normal-no-github', 'normal-long']), 'Mocking behaviour.', true)
2023-05-01 23:54:41 +12:00
->param('width', 0, new Range(0, 512), 'Resize image width, Pass an integer between 0 to 512.', true)
->param('height', 0, new Range(0, 320), 'Resize image height, Pass an integer between 0 to 320.', true)
2023-04-19 19:25:05 +12:00
->inject('user')
->inject('project')
->inject('dbForProject')
2023-04-26 20:21:10 +12:00
->inject('dbForConsole')
2023-04-19 19:25:05 +12:00
->inject('response')
2023-04-26 20:21:10 +12:00
->inject('heroes')
->inject('contributors')
->inject('employees')
->inject('logger')
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
2023-04-26 21:10:06 +12:00
$user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
2023-04-19 19:25:05 +12:00
2023-04-26 20:21:10 +12:00
if ($user->isEmpty() && empty($mock)) {
2023-04-26 21:10:06 +12:00
throw new Exception(Exception::USER_NOT_FOUND);
2023-04-26 20:21:10 +12:00
}
2023-04-19 19:25:05 +12:00
2023-04-26 23:51:19 +12:00
if (!$mock) {
2023-04-26 20:21:10 +12:00
$name = $user->getAttribute('name', 'Anonymous');
$email = $user->getAttribute('email', '');
$createdAt = new \DateTime($user->getCreatedAt());
2023-04-26 23:51:19 +12:00
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger);
2023-04-26 20:21:10 +12:00
$githubName = $gitHub['name'] ?? '';
$githubId = $gitHub['id'] ?? '';
2023-04-26 23:51:19 +12:00
2023-04-27 20:24:53 +12:00
$isHero = \array_key_exists($email, $heroes);
2023-04-26 20:21:10 +12:00
$isContributor = \in_array($githubId, $contributors);
2023-04-27 20:24:53 +12:00
$isEmployee = \array_key_exists($email, $employees);
$employeeNumber = $isEmployee ? $employees[$email]['spot'] : '';
if ($isHero) {
$createdAt = new \DateTime($heroes[$email]['memberSince'] ?? '');
2023-04-29 08:04:27 +12:00
} elseif ($isEmployee) {
2023-04-27 20:24:53 +12:00
$createdAt = new \DateTime($employees[$email]['memberSince'] ?? '');
}
2023-04-28 19:30:30 +12:00
if (!$isEmployee && !empty($githubName)) {
2023-04-27 20:24:53 +12:00
$employeeGitHub = \array_search(\strtolower($githubName), \array_map(fn ($employee) => \strtolower($employee['gitHub']) ?? '', $employees));
if (!empty($employeeGitHub)) {
$isEmployee = true;
$employeeNumber = $isEmployee ? $employees[$employeeGitHub]['spot'] : '';
$createdAt = new \DateTime($employees[$employeeGitHub]['memberSince'] ?? '');
}
}
2023-04-26 23:51:19 +12:00
2023-04-26 23:06:11 +12:00
$isPlatinum = $user->getInternalId() % 100 === 0;
2023-04-26 20:21:10 +12:00
} else {
$name = $mock === 'normal-long' ? 'Sir First Walter O\'Brian Junior' : 'Walter O\'Brian';
$createdAt = new \DateTime('now');
$githubName = $mock === 'normal-no-github' ? '' : ($mock === 'normal-long' ? 'sir-first-walterobrian-junior' : 'walterobrian');
$isHero = $mock === 'hero';
$isContributor = $mock === 'contributor';
$isEmployee = \str_starts_with($mock, 'employee');
$employeeNumber = match ($mock) {
'employee' => '1',
'employee-2digit' => '18',
default => ''
};
$isPlatinum = $mock === 'platinum';
}
2023-04-19 19:25:05 +12:00
2023-04-27 20:24:53 +12:00
if ($isEmployee) {
$isContributor = false;
$isHero = false;
}
if ($isHero) {
$isContributor = false;
$isEmployee = false;
}
if ($isContributor) {
$isHero = false;
$isEmployee = false;
}
2023-04-26 20:21:10 +12:00
$isGolden = $isEmployee || $isHero || $isContributor;
$isPlatinum = $isGolden ? false : $isPlatinum;
$memberSince = \strtoupper('Member since ' . $createdAt->format('M') . ' ' . $createdAt->format('d') . ', ' . $createdAt->format('o'));
$imagePath = $isGolden ? 'front-golden.png' : ($isPlatinum ? 'front-platinum.png' : 'front.png');
2023-04-29 20:40:22 +12:00
$baseImage = new \Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $imagePath);
2023-04-26 20:21:10 +12:00
2023-04-26 23:51:19 +12:00
if ($isEmployee) {
2023-04-29 20:40:22 +12:00
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/employee.png');
2023-04-26 20:21:10 +12:00
$image->setGravity(Imagick::GRAVITY_CENTER);
2023-05-01 23:54:41 +12:00
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 793, 35);
2023-04-26 20:21:10 +12:00
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_CENTER);
2023-04-29 20:40:22 +12:00
$text->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
2023-04-26 20:21:10 +12:00
$text->setFillColor(new \ImagickPixel('#FFFADF'));
2023-05-01 23:54:41 +12:00
$text->setFontSize(\strlen($employeeNumber) <= 2 ? 54 : 48);
2023-04-26 20:21:10 +12:00
$text->setFontWeight(700);
$metricsText = $baseImage->queryFontMetrics($text, $employeeNumber);
$hashtag = new \ImagickDraw();
$hashtag->setTextAlignment(Imagick::ALIGN_CENTER);
2023-04-29 20:40:22 +12:00
$hashtag->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
2023-04-26 20:21:10 +12:00
$hashtag->setFillColor(new \ImagickPixel('#FFFADF'));
2023-05-01 23:54:41 +12:00
$hashtag->setFontSize(28);
2023-04-26 20:21:10 +12:00
$hashtag->setFontWeight(700);
$metricsHashtag = $baseImage->queryFontMetrics($hashtag, '#');
2023-05-01 23:54:41 +12:00
$startX = 898;
$totalWidth = $metricsHashtag['textWidth'] + 12 + $metricsText['textWidth'];
2023-04-26 20:21:10 +12:00
2023-04-26 23:51:19 +12:00
$hashtagX = ($metricsHashtag['textWidth'] / 2);
2023-05-01 23:54:41 +12:00
$textX = $hashtagX + 12 + ($metricsText['textWidth'] / 2);
2023-04-26 23:51:19 +12:00
$hashtagX -= $totalWidth / 2;
$textX -= $totalWidth / 2;
2023-04-26 20:21:10 +12:00
$hashtagX += $startX;
$textX += $startX;
2023-05-01 23:54:41 +12:00
$baseImage->annotateImage($hashtag, $hashtagX, 150, 0, '#');
$baseImage->annotateImage($text, $textX, 150, 0, $employeeNumber);
2023-04-26 20:21:10 +12:00
}
2023-04-19 19:25:05 +12:00
2023-04-26 23:51:19 +12:00
if ($isContributor) {
2023-04-29 20:40:22 +12:00
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/contributor.png');
2023-04-26 20:21:10 +12:00
$image->setGravity(Imagick::GRAVITY_CENTER);
2023-05-01 23:54:41 +12:00
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 793, 34);
2023-04-26 20:21:10 +12:00
}
2023-04-19 19:25:05 +12:00
2023-04-26 23:51:19 +12:00
if ($isHero) {
2023-04-29 20:40:22 +12:00
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/hero.png');
2023-04-26 20:21:10 +12:00
$image->setGravity(Imagick::GRAVITY_CENTER);
2023-05-01 23:54:41 +12:00
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 793, 34);
2023-04-26 20:21:10 +12:00
}
2023-04-19 19:25:05 +12:00
2023-04-26 20:21:10 +12:00
setlocale(LC_ALL, "en_US.utf8");
2023-05-02 20:28:14 +12:00
// $name = \iconv("utf-8", "ascii//TRANSLIT", $name);
// $memberSince = \iconv("utf-8", "ascii//TRANSLIT", $memberSince);
// $githubName = \iconv("utf-8", "ascii//TRANSLIT", $githubName);
2023-04-19 19:25:05 +12:00
2023-04-26 20:21:10 +12:00
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_CENTER);
2023-09-19 10:34:37 +12:00
$text->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
2023-04-26 20:21:10 +12:00
$text->setFillColor(new \ImagickPixel('#FFFFFF'));
2023-04-19 19:25:05 +12:00
2023-05-02 20:28:14 +12:00
if (\strlen($name) > 32) {
$name = \substr($name, 0, 32);
2023-04-26 20:21:10 +12:00
}
2023-04-19 19:25:05 +12:00
2023-04-26 23:51:19 +12:00
if (\strlen($name) <= 23) {
2023-05-01 23:54:41 +12:00
$text->setFontSize(80);
2023-04-29 05:46:24 +12:00
$scalingDown = false;
2023-04-26 20:21:10 +12:00
} else {
2023-05-01 23:54:41 +12:00
$text->setFontSize(54);
2023-04-29 05:46:24 +12:00
$scalingDown = true;
2023-04-26 20:21:10 +12:00
}
$text->setFontWeight(700);
2023-05-01 23:54:41 +12:00
$baseImage->annotateImage($text, 512, 477, 0, $name);
2023-04-19 19:25:05 +12:00
2023-04-26 20:21:10 +12:00
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_CENTER);
2023-04-29 20:40:22 +12:00
$text->setFont(__DIR__ . '/../../../public/fonts/Inter-SemiBold.ttf');
2023-04-26 20:21:10 +12:00
$text->setFillColor(new \ImagickPixel($isGolden || $isPlatinum ? '#FFFFFF' : '#FFB9CC'));
2023-05-01 23:54:41 +12:00
$text->setFontSize(27);
2023-04-26 20:21:10 +12:00
$text->setFontWeight(600);
2023-04-28 00:34:50 +12:00
$text->setTextKerning(1.08);
2023-05-01 23:54:41 +12:00
$baseImage->annotateImage($text, 512, 541, 0, \strtoupper($memberSince));
2023-04-26 20:21:10 +12:00
if (!empty($githubName)) {
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_CENTER);
2023-04-29 20:40:22 +12:00
$text->setFont(__DIR__ . '/../../../public/fonts/Inter-Regular.ttf');
2023-04-26 20:21:10 +12:00
$text->setFillColor(new \ImagickPixel('#FFFFFF'));
2023-05-01 23:54:41 +12:00
$text->setFontSize($scalingDown ? 28 : 32);
2023-04-26 20:21:10 +12:00
$text->setFontWeight(400);
$metrics = $baseImage->queryFontMetrics($text, $githubName);
2023-05-01 23:54:41 +12:00
$baseImage->annotateImage($text, 512 + 20 + 4, 373 + ($scalingDown ? 2 : 0), 0, $githubName);
2023-04-26 20:21:10 +12:00
2023-04-29 20:40:22 +12:00
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github.png');
2023-04-26 20:21:10 +12:00
$image->setGravity(Imagick::GRAVITY_CENTER);
2023-05-01 23:54:41 +12:00
$precisionFix = 5;
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 512 - ($metrics['textWidth'] / 2) - 20 - 4, 373 - ($metrics['textHeight'] - $precisionFix));
2023-04-26 20:21:10 +12:00
}
2023-04-19 19:25:05 +12:00
2023-04-26 20:21:10 +12:00
if (!empty($width) || !empty($height)) {
$baseImage->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1);
}
2023-04-19 19:25:05 +12:00
2023-04-26 20:21:10 +12:00
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->setContentType('image/png')
->file($baseImage->getImageBlob());
});
2023-04-19 19:25:05 +12:00
2023-04-26 23:06:11 +12:00
App::get('/v1/cards/cloud-back')
2023-04-26 20:21:10 +12:00
->desc('Get Back Of Cloud Card')
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
2023-04-29 08:03:44 +12:00
->label('cache', true)
->label('cache.resourceType', 'cards/cloud-back')
->label('cache.resource', 'card-back/{request.userId}')
2023-04-26 20:21:10 +12:00
->label('docs', false)
->label('origin', '*')
->param('userId', '', new UID(), 'User ID.', true)
->param('mock', '', new WhiteList(['golden', 'normal', 'platinum']), 'Mocking behaviour.', true)
2023-05-01 23:54:41 +12:00
->param('width', 0, new Range(0, 512), 'Resize image width, Pass an integer between 0 to 512.', true)
->param('height', 0, new Range(0, 320), 'Resize image height, Pass an integer between 0 to 320.', true)
2023-04-26 20:21:10 +12:00
->inject('user')
->inject('project')
->inject('dbForProject')
->inject('dbForConsole')
->inject('response')
->inject('heroes')
->inject('contributors')
->inject('employees')
->inject('logger')
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
2023-04-26 21:10:06 +12:00
$user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
2023-04-19 19:25:05 +12:00
2023-04-26 20:21:10 +12:00
if ($user->isEmpty() && empty($mock)) {
2023-04-26 21:10:06 +12:00
throw new Exception(Exception::USER_NOT_FOUND);
2023-04-26 20:21:10 +12:00
}
2023-04-25 19:35:48 +12:00
2023-04-26 23:51:19 +12:00
if (!$mock) {
2023-04-26 20:21:10 +12:00
$userId = $user->getId();
$email = $user->getAttribute('email', '');
2023-04-26 23:51:19 +12:00
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger);
2023-04-26 20:21:10 +12:00
$githubId = $gitHub['id'] ?? '';
2023-04-26 23:51:19 +12:00
2023-04-27 20:24:53 +12:00
$isHero = \array_key_exists($email, $heroes);
2023-04-26 20:21:10 +12:00
$isContributor = \in_array($githubId, $contributors);
2023-04-27 20:24:53 +12:00
$isEmployee = \array_key_exists($email, $employees);
2023-04-26 20:21:10 +12:00
$isGolden = $isEmployee || $isHero || $isContributor;
2023-04-26 23:06:11 +12:00
$isPlatinum = $user->getInternalId() % 100 === 0;
2023-04-26 20:21:10 +12:00
} else {
$userId = '63e0bcf3c3eb803ba530';
$isGolden = $mock === 'golden';
$isPlatinum = $mock === 'platinum';
}
2023-04-19 19:25:05 +12:00
2023-04-26 20:21:10 +12:00
$userId = 'UID ' . $userId;
2023-04-25 19:35:48 +12:00
2023-04-26 20:21:10 +12:00
$isPlatinum = $isGolden ? false : $isPlatinum;
2023-04-25 19:35:48 +12:00
2023-04-26 20:21:10 +12:00
$imagePath = $isGolden ? 'back-golden.png' : ($isPlatinum ? 'back-platinum.png' : 'back.png');
2023-04-25 19:35:48 +12:00
2023-04-29 20:40:22 +12:00
$baseImage = new \Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $imagePath);
2023-04-25 19:35:48 +12:00
2023-04-26 20:21:10 +12:00
setlocale(LC_ALL, "en_US.utf8");
2023-05-02 20:28:14 +12:00
// $userId = \iconv("utf-8", "ascii//TRANSLIT", $userId);
2023-04-19 19:25:05 +12:00
2023-04-26 20:21:10 +12:00
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_CENTER);
2023-04-29 20:40:22 +12:00
$text->setFont(__DIR__ . '/../../../public/fonts/SourceCodePro-Regular.ttf');
2023-04-26 20:21:10 +12:00
$text->setFillColor(new \ImagickPixel($isGolden ? '#664A1E' : ($isPlatinum ? '#555555' : '#E8E9F0')));
2023-05-01 23:54:41 +12:00
$text->setFontSize(28);
2023-04-26 20:21:10 +12:00
$text->setFontWeight(400);
2023-05-01 23:54:41 +12:00
$baseImage->annotateImage($text, 512, 596, 0, $userId);
2023-04-19 19:25:05 +12:00
if (!empty($width) || !empty($height)) {
$baseImage->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1);
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->setContentType('image/png')
->file($baseImage->getImageBlob());
2020-12-27 03:59:15 +13:00
});
2023-04-26 20:21:10 +12:00
2023-04-26 23:06:11 +12:00
App::get('/v1/cards/cloud-og')
->desc('Get OG Image From Cloud Card')
->groups(['api', 'avatars'])
->label('scope', 'avatars.read')
2023-04-29 08:03:44 +12:00
->label('cache', true)
->label('cache.resourceType', 'cards/cloud-og')
->label('cache.resource', 'card-og/{request.userId}')
2023-04-26 23:06:11 +12:00
->label('docs', false)
->label('origin', '*')
->param('userId', '', new UID(), 'User ID.', true)
2023-04-29 05:46:24 +12:00
->param('mock', '', new WhiteList(['employee', 'employee-2digit', 'hero', 'contributor', 'normal', 'platinum', 'normal-no-github', 'normal-long', 'normal-long-right', 'normal-long-middle', 'normal-bg2', 'normal-bg3', 'normal-right', 'normal-middle', 'platinum-right', 'platinum-middle', 'hero-middle', 'hero-right', 'contributor-right', 'employee-right', 'contributor-middle', 'employee-middle', 'employee-2digit-middle', 'employee-2digit-right']), 'Mocking behaviour.', true)
2023-04-26 23:06:11 +12:00
->param('width', 0, new Range(0, 1024), 'Resize image card width, Pass an integer between 0 to 1024.', true)
->param('height', 0, new Range(0, 1024), 'Resize image card height, Pass an integer between 0 to 1024.', true)
->inject('user')
->inject('project')
->inject('dbForProject')
->inject('dbForConsole')
->inject('response')
->inject('heroes')
->inject('contributors')
->inject('employees')
->inject('logger')
->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
2023-04-26 23:06:11 +12:00
$user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
if ($user->isEmpty() && empty($mock)) {
throw new Exception(Exception::USER_NOT_FOUND);
}
2023-04-26 23:51:19 +12:00
if (!$mock) {
2023-04-26 23:06:11 +12:00
$internalId = $user->getInternalId();
$bgVariation = $internalId % 3 === 0 ? '1' : ($internalId % 3 === 1 ? '2' : '3');
2023-04-29 05:46:24 +12:00
$cardVariation = $internalId % 3 === 0 ? '1' : ($internalId % 3 === 1 ? '2' : '3');
2023-04-26 23:06:11 +12:00
$name = $user->getAttribute('name', 'Anonymous');
$email = $user->getAttribute('email', '');
$createdAt = new \DateTime($user->getCreatedAt());
2023-04-26 23:51:19 +12:00
$gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger);
2023-04-26 23:06:11 +12:00
$githubName = $gitHub['name'] ?? '';
$githubId = $gitHub['id'] ?? '';
2023-04-26 23:51:19 +12:00
2023-04-27 20:24:53 +12:00
$isHero = \array_key_exists($email, $heroes);
2023-04-26 23:06:11 +12:00
$isContributor = \in_array($githubId, $contributors);
2023-04-27 20:24:53 +12:00
$isEmployee = \array_key_exists($email, $employees);
$employeeNumber = $isEmployee ? $employees[$email]['spot'] : '';
if ($isHero) {
$createdAt = new \DateTime($heroes[$email]['memberSince'] ?? '');
2023-04-29 08:04:27 +12:00
} elseif ($isEmployee) {
2023-04-27 20:24:53 +12:00
$createdAt = new \DateTime($employees[$email]['memberSince'] ?? '');
}
2023-04-28 19:30:30 +12:00
if (!$isEmployee && !empty($githubName)) {
2023-04-27 20:24:53 +12:00
$employeeGitHub = \array_search(\strtolower($githubName), \array_map(fn ($employee) => \strtolower($employee['gitHub']) ?? '', $employees));
if (!empty($employeeGitHub)) {
$isEmployee = true;
$employeeNumber = $isEmployee ? $employees[$employeeGitHub]['spot'] : '';
$createdAt = new \DateTime($employees[$employeeGitHub]['memberSince'] ?? '');
}
}
2023-04-26 23:51:19 +12:00
2023-04-26 23:06:11 +12:00
$isPlatinum = $user->getInternalId() % 100 === 0;
} else {
2023-04-26 23:51:19 +12:00
$bgVariation = \str_ends_with($mock, '-bg2') ? '2' : (\str_ends_with($mock, '-bg3') ? '3' : '1');
2023-04-29 05:46:24 +12:00
$cardVariation = \str_ends_with($mock, '-right') ? '2' : (\str_ends_with($mock, '-middle') ? '3' : '1');
2023-04-28 00:34:50 +12:00
$name = \str_starts_with($mock, 'normal-long') ? 'Sir First Walter O\'Brian Junior' : 'Walter O\'Brian';
2023-04-26 23:06:11 +12:00
$createdAt = new \DateTime('now');
2023-04-28 00:34:50 +12:00
$githubName = $mock === 'normal-no-github' ? '' : (\str_starts_with($mock, 'normal-long') ? 'sir-first-walterobrian-junior' : 'walterobrian');
2023-04-27 20:24:53 +12:00
$isHero = \str_starts_with($mock, 'hero');
$isContributor = \str_starts_with($mock, 'contributor');
2023-04-26 23:06:11 +12:00
$isEmployee = \str_starts_with($mock, 'employee');
$employeeNumber = match ($mock) {
'employee' => '1',
2023-04-27 20:24:53 +12:00
'employee-right' => '1',
2023-04-29 05:46:24 +12:00
'employee-middle' => '1',
2023-04-26 23:06:11 +12:00
'employee-2digit' => '18',
2023-04-28 00:34:50 +12:00
'employee-2digit-right' => '18',
2023-04-29 05:46:24 +12:00
'employee-2digit-middle' => '18',
2023-04-26 23:06:11 +12:00
default => ''
};
2023-04-28 00:34:50 +12:00
$isPlatinum = \str_starts_with($mock, 'platinum');
2023-04-26 23:06:11 +12:00
}
2023-04-27 20:24:53 +12:00
if ($isEmployee) {
$isContributor = false;
$isHero = false;
}
if ($isHero) {
$isContributor = false;
$isEmployee = false;
}
if ($isContributor) {
$isHero = false;
$isEmployee = false;
}
2023-04-26 23:06:11 +12:00
$isGolden = $isEmployee || $isHero || $isContributor;
$isPlatinum = $isGolden ? false : $isPlatinum;
$memberSince = \strtoupper('Member since ' . $createdAt->format('M') . ' ' . $createdAt->format('d') . ', ' . $createdAt->format('o'));
2023-04-29 20:50:04 +12:00
$baseImage = new \Imagick(__DIR__ . "/../../../public/images/cards/cloud/og-background{$bgVariation}.png");
2023-04-26 23:39:50 +12:00
2023-04-26 23:06:11 +12:00
$cardType = $isGolden ? '-golden' : ($isPlatinum ? '-platinum' : '');
2023-04-26 23:39:50 +12:00
2023-04-29 20:50:04 +12:00
$image = new Imagick(__DIR__ . "/../../../public/images/cards/cloud/og-card{$cardType}{$cardVariation}.png");
2023-04-26 23:51:19 +12:00
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 1008 / 2 - $image->getImageWidth() / 2, 1008 / 2 - $image->getImageHeight() / 2);
2023-04-26 23:39:50 +12:00
2023-04-29 20:40:22 +12:00
$imageLogo = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/og-background-logo.png');
2023-04-29 20:50:04 +12:00
$imageShadow = new Imagick(__DIR__ . "/../../../public/images/cards/cloud/og-shadow{$cardType}.png");
2023-04-26 23:51:19 +12:00
if ($cardVariation === '1') {
2023-04-29 05:46:24 +12:00
$baseImage->compositeImage($imageLogo, Imagick::COMPOSITE_OVER, 32, 1008 - $imageLogo->getImageHeight() - 32);
$baseImage->compositeImage($imageShadow, Imagick::COMPOSITE_OVER, -450, 700);
2023-04-29 08:04:27 +12:00
} elseif ($cardVariation === '2') {
2023-04-29 05:46:24 +12:00
$baseImage->compositeImage($imageLogo, Imagick::COMPOSITE_OVER, 1008 - $imageLogo->getImageWidth() - 32, 1008 - $imageLogo->getImageHeight() - 32);
$baseImage->compositeImage($imageShadow, Imagick::COMPOSITE_OVER, -20, 710);
2023-04-26 23:39:50 +12:00
} else {
2023-04-29 05:46:24 +12:00
$baseImage->compositeImage($imageLogo, Imagick::COMPOSITE_OVER, 1008 - $imageLogo->getImageWidth() - 32, 1008 - $imageLogo->getImageHeight() - 32);
$baseImage->compositeImage($imageShadow, Imagick::COMPOSITE_OVER, -135, 710);
2023-04-26 23:39:50 +12:00
}
2023-04-26 23:06:11 +12:00
2023-04-27 20:24:53 +12:00
if ($isEmployee) {
2023-04-29 05:46:24 +12:00
$file = $cardVariation === '3' ? 'employee-skew.png' : 'employee.png';
2023-04-29 20:40:22 +12:00
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $file);
2023-04-27 20:24:53 +12:00
$image->setGravity(Imagick::GRAVITY_CENTER);
$hashtag = new \ImagickDraw();
$hashtag->setTextAlignment(Imagick::ALIGN_LEFT);
2023-04-29 20:40:22 +12:00
$hashtag->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
2023-04-27 20:24:53 +12:00
$hashtag->setFillColor(new \ImagickPixel('#FFFADF'));
$hashtag->setFontSize(20);
$hashtag->setFontWeight(700);
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_LEFT);
2023-04-29 20:40:22 +12:00
$text->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
2023-04-27 20:24:53 +12:00
$text->setFillColor(new \ImagickPixel('#FFFADF'));
$text->setFontSize(\strlen($employeeNumber) <= 1 ? 36 : 28);
$text->setFontWeight(700);
2023-04-29 05:46:24 +12:00
if ($cardVariation === '3') {
2023-05-02 02:45:39 +12:00
$hashtag->setFontSize(16);
$text->setFontSize(\strlen($employeeNumber) <= 1 ? 30 : 26);
2023-04-29 05:46:24 +12:00
$hashtag->skewY(20);
$hashtag->skewX(20);
$text->skewY(20);
$text->skewX(20);
}
$metricsHashtag = $baseImage->queryFontMetrics($hashtag, '#');
2023-04-27 20:24:53 +12:00
$metricsText = $baseImage->queryFontMetrics($text, $employeeNumber);
$group = new Imagick();
$groupWidth = $metricsHashtag['textWidth'] + 6 + $metricsText['textWidth'];
if ($cardVariation === '1') {
2023-04-29 05:46:24 +12:00
$group->newImage($groupWidth, $metricsText['textHeight'], '#00000000');
$group->annotateImage($hashtag, 0, $metricsText['textHeight'], 0, '#');
$group->annotateImage($text, $metricsHashtag['textWidth'] + 6, $metricsText['textHeight'], 0, $employeeNumber);
$image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
$image->rotateImage(new ImagickPixel('#00000000'), -20);
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 612, 203);
2023-04-27 20:24:53 +12:00
$group->rotateImage(new ImagickPixel('#00000000'), -22);
2023-04-28 19:30:30 +12:00
if (\strlen($employeeNumber) <= 1) {
2023-04-27 20:24:53 +12:00
$baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 660, 245);
} else {
$baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 655, 247);
}
2023-04-29 08:04:27 +12:00
} elseif ($cardVariation === '2') {
2023-04-29 05:46:24 +12:00
$group->newImage($groupWidth, $metricsText['textHeight'], '#00000000');
$group->annotateImage($hashtag, 0, $metricsText['textHeight'], 0, '#');
$group->annotateImage($text, $metricsHashtag['textWidth'] + 6, $metricsText['textHeight'], 0, $employeeNumber);
$image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
$image->rotateImage(new ImagickPixel('#00000000'), 30);
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 715, 425);
2023-04-27 20:24:53 +12:00
$group->rotateImage(new ImagickPixel('#00000000'), 32);
2023-04-28 19:30:30 +12:00
if (\strlen($employeeNumber) <= 1) {
2023-04-27 20:24:53 +12:00
$baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 775, 465);
} else {
$baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 767, 470);
}
2023-04-29 05:46:24 +12:00
} else {
$group->newImage(300, 300, '#00000000');
$hashtag->annotation(0, $metricsText['textHeight'], '#');
2023-05-02 02:45:39 +12:00
$text->annotation($metricsHashtag['textWidth'] + 2, $metricsText['textHeight'], $employeeNumber);
2023-04-29 05:46:24 +12:00
$group->drawImage($hashtag);
$group->drawImage($text);
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 640, 293);
if (\strlen($employeeNumber) <= 1) {
2023-05-02 02:45:39 +12:00
$baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 670, 317);
2023-04-29 05:46:24 +12:00
} else {
2023-05-02 02:45:39 +12:00
$baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 663, 322);
2023-04-29 05:46:24 +12:00
}
2023-04-27 20:24:53 +12:00
}
}
if ($isContributor) {
2023-04-29 05:46:24 +12:00
$file = $cardVariation === '3' ? 'contributor-skew.png' : 'contributor.png';
2023-04-29 20:40:22 +12:00
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $file);
2023-04-27 20:24:53 +12:00
$image->setGravity(Imagick::GRAVITY_CENTER);
2023-04-29 05:46:24 +12:00
if ($cardVariation === '1') {
$image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
$image->rotateImage(new ImagickPixel('#00000000'), -20);
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 612, 203);
2023-04-29 08:04:27 +12:00
} elseif ($cardVariation === '2') {
2023-04-29 05:46:24 +12:00
$image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
$image->rotateImage(new ImagickPixel('#00000000'), 30);
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 715, 425);
} else {
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 640, 293);
}
2023-04-27 20:24:53 +12:00
}
if ($isHero) {
2023-04-29 05:46:24 +12:00
$file = $cardVariation === '3' ? 'hero-skew.png' : 'hero.png';
2023-04-29 20:40:22 +12:00
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $file);
2023-04-27 20:24:53 +12:00
$image->setGravity(Imagick::GRAVITY_CENTER);
2023-04-29 05:46:24 +12:00
if ($cardVariation === '1') {
$image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
$image->rotateImage(new ImagickPixel('#00000000'), -20);
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 612, 203);
2023-04-29 08:04:27 +12:00
} elseif ($cardVariation === '2') {
2023-04-29 05:46:24 +12:00
$image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
$image->rotateImage(new ImagickPixel('#00000000'), 30);
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 715, 425);
} else {
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 640, 293);
}
2023-04-27 20:24:53 +12:00
}
2023-04-26 23:06:11 +12:00
setlocale(LC_ALL, "en_US.utf8");
2023-05-02 20:28:14 +12:00
// $name = \iconv("utf-8", "ascii//TRANSLIT", $name);
// $memberSince = \iconv("utf-8", "ascii//TRANSLIT", $memberSince);
// $githubName = \iconv("utf-8", "ascii//TRANSLIT", $githubName);
2023-04-26 23:06:11 +12:00
2023-04-29 05:46:24 +12:00
$textName = new \ImagickDraw();
$textName->setTextAlignment(Imagick::ALIGN_CENTER);
2023-09-19 10:34:37 +12:00
$textName->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
2023-04-29 05:46:24 +12:00
$textName->setFillColor(new \ImagickPixel('#FFFFFF'));
2023-04-26 23:06:11 +12:00
2023-05-02 20:28:14 +12:00
if (\strlen($name) > 32) {
$name = \substr($name, 0, 32);
2023-04-27 20:24:53 +12:00
}
2023-04-28 19:30:30 +12:00
if ($cardVariation === '1') {
2023-04-28 00:34:50 +12:00
if (\strlen($name) <= 23) {
2023-04-29 05:46:24 +12:00
$scalingDown = false;
$textName->setFontSize(54);
2023-04-28 00:34:50 +12:00
} else {
2023-04-29 05:46:24 +12:00
$scalingDown = true;
$textName->setFontSize(36);
}
2023-04-29 08:04:27 +12:00
} elseif ($cardVariation === '2') {
2023-04-29 05:46:24 +12:00
if (\strlen($name) <= 23) {
$scalingDown = false;
$textName->setFontSize(50);
} else {
$scalingDown = true;
$textName->setFontSize(34);
2023-04-28 00:34:50 +12:00
}
2023-04-27 20:24:53 +12:00
} else {
2023-04-28 00:34:50 +12:00
if (\strlen($name) <= 23) {
2023-04-29 05:46:24 +12:00
$scalingDown = false;
$textName->setFontSize(44);
2023-04-28 00:34:50 +12:00
} else {
2023-04-29 05:46:24 +12:00
$scalingDown = true;
$textName->setFontSize(32);
2023-04-28 00:34:50 +12:00
}
2023-04-27 20:24:53 +12:00
}
2023-04-28 19:30:30 +12:00
2023-04-29 05:46:24 +12:00
$textName->setFontWeight(700);
$textMember = new \ImagickDraw();
$textMember->setTextAlignment(Imagick::ALIGN_CENTER);
2023-04-29 20:40:22 +12:00
$textMember->setFont(__DIR__ . '/../../../public/fonts/Inter-Medium.ttf');
2023-04-29 05:46:24 +12:00
$textMember->setFillColor(new \ImagickPixel($isGolden || $isPlatinum ? '#FFFFFF' : '#FFB9CC'));
$textMember->setFontWeight(500);
$textMember->setTextKerning(1.12);
2023-04-27 20:24:53 +12:00
if ($cardVariation === '1') {
2023-04-29 05:46:24 +12:00
$textMember->setFontSize(21);
2023-04-27 20:24:53 +12:00
2023-04-29 08:04:27 +12:00
$baseImage->annotateImage($textName, 550, 600, -22, $name);
$baseImage->annotateImage($textMember, 585, 635, -22, $memberSince);
} elseif ($cardVariation === '2') {
2023-04-29 05:46:24 +12:00
$textMember->setFontSize(20);
2023-04-27 20:24:53 +12:00
2023-04-29 05:46:24 +12:00
$baseImage->annotateImage($textName, 435, 590, 31.37, $name);
$baseImage->annotateImage($textMember, 412, 628, 31.37, $memberSince);
2023-04-27 20:24:53 +12:00
} else {
2023-04-29 05:46:24 +12:00
$textMember->setFontSize(16);
$textName->skewY(20);
$textName->skewX(20);
2023-05-02 02:45:39 +12:00
$textName->annotation(320, 700, $name);
2023-04-29 05:46:24 +12:00
$textMember->skewY(20);
$textMember->skewX(20);
$textMember->annotation(330, 735, $memberSince);
$baseImage->drawImage($textName);
$baseImage->drawImage($textMember);
2023-04-27 20:24:53 +12:00
}
if (!empty($githubName)) {
$text = new \ImagickDraw();
$text->setTextAlignment(Imagick::ALIGN_LEFT);
2023-04-29 20:40:22 +12:00
$text->setFont(__DIR__ . '/../../../public/fonts/Inter-Regular.ttf');
2023-04-27 20:24:53 +12:00
$text->setFillColor(new \ImagickPixel('#FFFFFF'));
2023-05-02 02:45:39 +12:00
$text->setFontSize($scalingDown ? 16 : 20);
2023-04-27 20:24:53 +12:00
$text->setFontWeight(400);
if ($cardVariation === '1') {
2023-04-29 05:46:24 +12:00
$metrics = $baseImage->queryFontMetrics($text, $githubName);
$group = new Imagick();
$groupWidth = $metrics['textWidth'] + 32 + 4;
2023-05-02 20:28:14 +12:00
$group->newImage($groupWidth, $metrics['textHeight'] + 10, '#00000000');
2023-04-29 20:40:22 +12:00
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github.png');
2023-04-29 05:46:24 +12:00
$image->setGravity(Imagick::GRAVITY_CENTER);
$image->resizeImage(32, 32, Imagick::FILTER_LANCZOS, 1);
2023-05-02 20:28:14 +12:00
$precisionFix = -1;
2023-04-29 08:04:27 +12:00
2023-04-29 05:46:24 +12:00
$group->compositeImage($image, Imagick::COMPOSITE_OVER, 0, 0);
$group->annotateImage($text, 32 + 4, $metrics['textHeight'] - $precisionFix, 0, $githubName);
2023-04-27 20:24:53 +12:00
$group->rotateImage(new ImagickPixel('#00000000'), -22);
2023-04-28 00:34:50 +12:00
$x = 510 - $group->getImageWidth() / 2;
$y = 530 - $group->getImageHeight() / 2;
$baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, $x, $y);
2023-04-29 08:04:27 +12:00
} elseif ($cardVariation === '2') {
2023-04-29 05:46:24 +12:00
$metrics = $baseImage->queryFontMetrics($text, $githubName);
2023-04-29 08:04:27 +12:00
2023-04-29 05:46:24 +12:00
$group = new Imagick();
$groupWidth = $metrics['textWidth'] + 32 + 4;
2023-05-02 20:28:14 +12:00
$group->newImage($groupWidth, $metrics['textHeight'] + 10, '#00000000');
2023-04-29 20:40:22 +12:00
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github.png');
2023-04-29 05:46:24 +12:00
$image->setGravity(Imagick::GRAVITY_CENTER);
$image->resizeImage(32, 32, Imagick::FILTER_LANCZOS, 1);
2023-05-02 20:28:14 +12:00
$precisionFix = -1;
2023-04-29 08:04:27 +12:00
2023-04-29 05:46:24 +12:00
$group->compositeImage($image, Imagick::COMPOSITE_OVER, 0, 0);
$group->annotateImage($text, 32 + 4, $metrics['textHeight'] - $precisionFix, 0, $githubName);
2023-04-28 00:34:50 +12:00
$group->rotateImage(new ImagickPixel('#00000000'), 31.11);
$x = 485 - $group->getImageWidth() / 2;
$y = 530 - $group->getImageHeight() / 2;
$baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, $x, $y);
2023-04-29 05:46:24 +12:00
} else {
$text->skewY(20);
$text->skewX(20);
$text->setTextAlignment(\Imagick::ALIGN_CENTER);
2023-05-02 02:45:39 +12:00
$text->annotation(320 + 15 + 2, 640, $githubName);
2023-04-29 05:46:24 +12:00
$metrics = $baseImage->queryFontMetrics($text, $githubName);
2023-04-29 20:40:22 +12:00
$image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github-skew.png');
2023-04-29 05:46:24 +12:00
$image->setGravity(Imagick::GRAVITY_CENTER);
2023-05-02 02:45:39 +12:00
$baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 512 - ($metrics['textWidth'] / 2), 518 + \strlen($githubName) * 1.3);
2023-04-29 05:46:24 +12:00
$baseImage->drawImage($text);
2023-04-27 20:24:53 +12:00
}
}
2023-04-26 23:06:11 +12:00
if (!empty($width) || !empty($height)) {
$baseImage->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1);
}
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->setContentType('image/png')
->file($baseImage->getImageBlob());
2020-12-27 03:59:15 +13:00
});