Make badge style customizable (#8)

* Add Poser badge generator

* Make badge styles configurable

* Make flat style as default
This commit is contained in:
Anton Komarev
2020-07-20 22:17:33 +03:00
committed by GitHub
parent 3e0fb65678
commit 5c81fbf0d6
7 changed files with 377 additions and 68 deletions

View File

@@ -36,6 +36,24 @@ You need to add counter in README.md file in your profile repository via Markdow
> **NOTE**: Don't forget to replace example `username` parameter with real value.
### Customization
#### Styles
The following styles are available (`flat` is the default):
| style | demo |
| ----- | ---- |
| `flat` | ![](https://img.shields.io/static/v1?label=Profile+views&message=1234567890&color=007ec6&style=flat) |
| `flat-square` | ![](https://img.shields.io/static/v1?label=Profile+views&message=1234567890&color=007ec6&style=flat-square) |
| `plastic` | ![](https://img.shields.io/static/v1?label=Profile+views&message=1234567890&color=007ec6&style=plastic) |
```markdown
![](https://komarev.com/ghpvc/?username=your-github-username&style=flat-square)
```
For example
## Is it safe to place tracking image?
GitHub passes all images URLs through their proxy _camo_ service. URLs are look like [https://camo.githubusercontent.com/a9aa69b588bef281647d53091b4faa01ac126121/68747470733a2f2f6b6f6d617265762e636f6d2f67687076632f3f757365726e616d653d616e746f6e6b6f6d6172657626](https://camo.githubusercontent.com/a9aa69b588bef281647d53091b4faa01ac126121/68747470733a2f2f6b6f6d617265762e636f6d2f67687076632f3f757365726e616d653d616e746f6e6b6f6d6172657626).

View File

@@ -27,6 +27,7 @@
},
"require": {
"php": "^7.4",
"badges/poser": "^1.4",
"vlucas/phpdotenv": "^5.0",
"webmozart/assert": "^1.9"
},

319
composer.lock generated
View File

@@ -4,8 +4,75 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "1199b144b9fb9e5178e80602812dd8b2",
"content-hash": "268e668398fa52181cf35d8920a04ab9",
"packages": [
{
"name": "badges/poser",
"version": "v1.4.1",
"source": {
"type": "git",
"url": "https://github.com/badges/poser.git",
"reference": "4281890cbd58e0ea29c6550c4a4aaa16a15b3ce0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/badges/poser/zipball/4281890cbd58e0ea29c6550c4a4aaa16a15b3ce0",
"reference": "4281890cbd58e0ea29c6550c4a4aaa16a15b3ce0",
"shasum": ""
},
"require": {
"ext-gd": "*",
"php": ">=5.3",
"symfony/console": "~2.0|~3.0|~4.0"
},
"require-dev": {
"behat/behat": "^3.4",
"coduo/phpspec-data-provider-extension": "^1.0",
"friendsofphp/php-cs-fixer": "@stable",
"phpspec/phpspec": "^2.5"
},
"bin": [
"bin/poser"
],
"type": "lib",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
},
"autoload": {
"psr-4": {
"PUGX\\Poser\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Giulio De Donato",
"email": "liuggio@gmail.com"
},
{
"name": "PUGX Community",
"homepage": "https://github.com/PUGX/badge-poser/contributors"
},
{
"name": "Shields Community",
"homepage": "https://github.com/badges/poser/contributors"
}
],
"description": "Poser, add badges on your readme..",
"homepage": "https://github.com/PUGX",
"keywords": [
"badge",
"composer",
"packagist",
"pin"
],
"time": "2020-06-11T16:17:31+00:00"
},
{
"name": "graham-campbell/result-type",
"version": "v1.0.1",
@@ -113,6 +180,132 @@
],
"time": "2020-06-07T10:40:07+00:00"
},
{
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "symfony/console",
"version": "v4.4.10",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "326b064d804043005526f5a0494cfb49edb59bb0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/326b064d804043005526f5a0494cfb49edb59bb0",
"reference": "326b064d804043005526f5a0494cfb49edb59bb0",
"shasum": ""
},
"require": {
"php": ">=7.1.3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php73": "^1.8",
"symfony/polyfill-php80": "^1.15",
"symfony/service-contracts": "^1.1|^2"
},
"conflict": {
"symfony/dependency-injection": "<3.4",
"symfony/event-dispatcher": "<4.3|>=5",
"symfony/lock": "<4.4",
"symfony/process": "<3.3"
},
"provide": {
"psr/log-implementation": "1.0"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "^3.4|^4.0|^5.0",
"symfony/dependency-injection": "^3.4|^4.0|^5.0",
"symfony/event-dispatcher": "^4.3",
"symfony/lock": "^4.4|^5.0",
"symfony/process": "^3.4|^4.0|^5.0",
"symfony/var-dumper": "^4.3|^5.0"
},
"suggest": {
"psr/log": "For using the console logger",
"symfony/event-dispatcher": "",
"symfony/lock": "",
"symfony/process": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.4-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Console\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2020-05-30T20:06:45+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.17.1",
@@ -238,6 +431,68 @@
],
"time": "2020-06-06T08:46:27+00:00"
},
{
"name": "symfony/polyfill-php73",
"version": "v1.18.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php73.git",
"reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fffa1a52a023e782cdcc221d781fe1ec8f87fcca",
"reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php73\\": ""
},
"files": [
"bootstrap.php"
],
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"time": "2020-07-14T12:35:20+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.17.1",
@@ -304,6 +559,68 @@
],
"time": "2020-06-06T08:46:27+00:00"
},
{
"name": "symfony/service-contracts",
"version": "v2.1.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
"reference": "58c7475e5457c5492c26cc740cc0ad7464be9442"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/58c7475e5457c5492c26cc740cc0ad7464be9442",
"reference": "58c7475e5457c5492c26cc740cc0ad7464be9442",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"psr/container": "^1.0"
},
"suggest": {
"symfony/service-implementation": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"psr-4": {
"Symfony\\Contracts\\Service\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Generic abstractions related to writing services",
"homepage": "https://symfony.com",
"keywords": [
"abstractions",
"contracts",
"decoupling",
"interfaces",
"interoperability",
"standards"
],
"time": "2020-07-06T13:23:11+00:00"
},
{
"name": "vlucas/phpdotenv",
"version": "v5.0.0",

View File

@@ -30,8 +30,6 @@ try {
$dotEnv = Dotenv::createImmutable($basePath);
$dotEnv->safeLoad();
$badgeImagePath = $basePath . '/resources/badge.svg';
$httpUserAgent = $_SERVER['HTTP_USER_AGENT'];
if (!isset($_ENV['FILE_STORAGE_PATH']) || $_ENV['FILE_STORAGE_PATH'] === null) {
@@ -40,12 +38,15 @@ try {
$storagePath = $_ENV['FILE_STORAGE_PATH'];
}
$style = $_GET['style'] ?? null;
$badgeStyle = $_GET['style'] ?? 'flat';
if (!in_array($badgeStyle, ['flat', 'flat-square', 'plastic'])) {
$badgeStyle = 'flat';
}
$username = $_GET['username'] ?? '';
$username = trim($username);
if ($username === '') {
echo $badgeImageRenderer->renderBadgeWithError($badgeImagePath, 'Invalid query parameter: username');
echo $badgeImageRenderer->renderBadgeWithError($badgeStyle, 'Invalid query parameter: username');
exit;
}
@@ -57,16 +58,16 @@ try {
$counterRepository->addViewByUsername($username);
}
if ($style === 'pixel') {
echo '<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>';
if ($badgeStyle === 'pixel') {
echo $badgeImageRenderer->renderPixel();
exit;
}
$count = $counterRepository->getViewsCountByUsername($username);
echo $badgeImageRenderer->renderBadgeWithCount($badgeImagePath, $count);
echo $badgeImageRenderer->renderBadgeWithCount($badgeStyle, $count);
exit;
} catch (Exception $exception) {
echo $badgeImageRenderer->renderBadgeWithError($badgeImagePath, $exception->getMessage());
echo $badgeImageRenderer->renderBadgeWithError($badgeStyle, $exception->getMessage());
exit;
}

View File

@@ -40,16 +40,17 @@ try {
'DB_NAME',
]);
$badgeImagePath = $basePath . '/resources/badge.svg';
$httpUserAgent = $_SERVER['HTTP_USER_AGENT'];
$style = $_GET['style'] ?? null;
$badgeStyle = $_GET['style'] ?? 'flat';
if (!in_array($badgeStyle, ['flat', 'flat-square', 'plastic'])) {
$badgeStyle = 'flat';
}
$username = $_GET['username'] ?? '';
$username = trim($username);
if ($username === '') {
echo $badgeImageRenderer->renderBadgeWithError($badgeImagePath, 'Invalid query parameter: username');
echo $badgeImageRenderer->renderBadgeWithError($badgeStyle, 'Invalid query parameter: username');
exit;
}
@@ -70,19 +71,19 @@ try {
$counterRepository->addViewByUsername($username);
}
if ($style === 'pixel') {
echo '<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>';
if ($badgeStyle === 'pixel') {
echo $badgeImageRenderer->renderPixel();
exit;
}
$count = $counterRepository->getViewsCountByUsername($username);
echo $badgeImageRenderer->renderBadgeWithCount($badgeImagePath, $count);
echo $badgeImageRenderer->renderBadgeWithCount($badgeStyle, $count);
exit;
} catch (InvalidPathException $exception) {
echo $badgeImageRenderer->renderBadgeWithError($badgeImagePath, 'Application environment file is missing');
echo $badgeImageRenderer->renderBadgeWithError($badgeStyle, 'Application environment file is missing');
exit;
} catch (Exception $exception) {
echo $badgeImageRenderer->renderBadgeWithError($badgeImagePath, $exception->getMessage());
echo $badgeImageRenderer->renderBadgeWithError($badgeStyle, $exception->getMessage());
exit;
}

View File

@@ -1,11 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="%IMAGE_WIDTH%" height="20">
<g shape-rendering="crispEdges">
<rect width="79" height="20" fill="#555"/>
<rect x="79" width="%MESSAGE_BACKGROUND_WIDTH%" height="20" fill="%MESSAGE_BACKGROUND_FILL%"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision" font-size="110">
<text x="405" y="140" transform="scale(.1)" textLength="690">%LABEL%</text>
<text x="%MESSAGE_TEXT_MARGIN_LEFT%" y="140" transform="scale(.1)" textLength="%MESSAGE_TEXT_LENGTH%">%MESSAGE%</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 697 B

View File

@@ -13,61 +13,43 @@ declare(strict_types=1);
namespace Komarev\GitHubProfileViewsCounter;
use Contracts\Komarev\GitHubProfileViewsCounter\InvalidPathException;
use PUGX\Poser\Poser;
use PUGX\Poser\Render\SvgFlatRender;
use PUGX\Poser\Render\SvgFlatSquareRender;
use PUGX\Poser\Render\SvgRender;
final class BadgeImageRendererService
{
public function renderBadgeWithCount(string $imagePath, int $count): string
private Poser $poser;
public function __construct()
{
$this->poser = new Poser([new SvgRender(), new SvgFlatRender(), new SvgFlatSquareRender()]);
}
public function renderBadgeWithCount($badgeStyle, int $count): string
{
$message = (string) $count;
$messageBackgroundFill = '#007ec6';
$messageBackgroundFill = '007ec6';
return $this->renderBadge($imagePath, $message, $messageBackgroundFill);
return $this->renderBadge($badgeStyle, $message, $messageBackgroundFill);
}
public function renderBadgeWithError(string $imagePath, string $message): string
public function renderBadgeWithError($badgeStyle, string $message): string
{
$messageBackgroundFill = '#e05d44';
$messageBackgroundFill = 'e05d44';
return $this->renderBadge($imagePath, $message, $messageBackgroundFill);
return $this->renderBadge($badgeStyle, $message, $messageBackgroundFill);
}
private function renderBadge(string $imagePath, string $message, string $messageBackgroundFill): string
private function renderBadge($badgeStyle, string $message, string $messageBackgroundFill): string
{
if (!file_exists($imagePath)) {
throw new InvalidPathException('Badge image not found');
}
$image = file_get_contents($imagePath);
return $this->replaceImagePlaceholders($image, $message, $messageBackgroundFill);
return (string) $this->poser->generate('Page views', $message, $messageBackgroundFill, $badgeStyle);
}
private function replaceImagePlaceholders(string $image, string $message, string $messageBackgroundFill): string
public function renderPixel(): string
{
$messageLength = strlen($message);
$minImageWidth = 98;
$minMessageBackgroundWidth = 17;
$minMessageTextLength = 70;
$minMessageTextMarginLeft = 885;
$label = 'Profile views';
$imageWidth = $minImageWidth + (8 * $messageLength);
$messageBackgroundWidth = $minMessageBackgroundWidth + (8 * $messageLength);
$messageTextLength = $minMessageTextLength + (80 * $messageLength);
$messageTextMarginLeft = $minMessageTextMarginLeft + (40 * $messageLength);
$image = str_replace('%IMAGE_WIDTH%', $imageWidth, $image);
$image = str_replace('%LABEL%', $label, $image);
$image = str_replace('%MESSAGE%', $message, $image);
$image = str_replace('%MESSAGE_BACKGROUND_WIDTH%', $messageBackgroundWidth, $image);
$image = str_replace('%MESSAGE_BACKGROUND_FILL%', $messageBackgroundFill, $image);
$image = str_replace('%MESSAGE_TEXT_LENGTH%', $messageTextLength, $image);
$image = str_replace('%MESSAGE_TEXT_MARGIN_LEFT%', $messageTextMarginLeft, $image);
return $image;
return '<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>';
}
}