Implement users avatars for Collabora
This commit is contained in:
parent
1999fdb4aa
commit
b7384648e8
166
lib/KD2/Graphics/SVG/Avatar.php
Normal file
166
lib/KD2/Graphics/SVG/Avatar.php
Normal file
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KD2\Graphics\SVG;
|
||||
|
||||
/**
|
||||
* PHP port of https://github.com/boringdesigners/boring-avatars/
|
||||
*/
|
||||
class Avatar
|
||||
{
|
||||
static protected function hashCode(string $name): int
|
||||
{
|
||||
return hexdec(substr(md5($name), 0, 8));
|
||||
}
|
||||
|
||||
static protected function getRandomColor(int $number, array $colors, int $range): string
|
||||
{
|
||||
return $colors[($number) % $range];
|
||||
}
|
||||
|
||||
static protected function getUnit(int $number, int $range, int $index = 0): int
|
||||
{
|
||||
$value = $number % $range;
|
||||
|
||||
if ($index && ((self::getDigit($number, $index) % 2) === 0)) {
|
||||
return -($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
static protected function getDigit(int $number, int $ntn): int
|
||||
{
|
||||
$a = intval($number / pow(10, $ntn));
|
||||
return (int)floor($a % 10);
|
||||
}
|
||||
|
||||
static protected function getBoolean(int $number, int $ntn): bool
|
||||
{
|
||||
return (!((self::getDigit($number, $ntn)) % 2));
|
||||
}
|
||||
|
||||
|
||||
static protected function getAngle(int $x, int $y): float
|
||||
{
|
||||
return atan2($y, $x) * 180 / PI;
|
||||
}
|
||||
|
||||
|
||||
static protected function getContrast(string $hexcolor): string
|
||||
{
|
||||
// If a leading # is provided, remove it
|
||||
if (substr($hexcolor, 0, 1) === '#') {
|
||||
$hexcolor = substr($hexcolor, 1);
|
||||
}
|
||||
|
||||
if (strlen($hexcolor) === 3) {
|
||||
$hexcolor = $hexcolor[0] . $hexcolor[0] . $hexcolor[1] . $hexcolor[1] . $hexcolor[2] . $hexcolor[2];
|
||||
}
|
||||
|
||||
// Convert to RGB value
|
||||
$r = hexdec(substr($hexcolor, 0, 2));
|
||||
$g = hexdec(substr($hexcolor, 2, 2));
|
||||
$b = hexdec(substr($hexcolor, 4, 2));
|
||||
|
||||
// Get YIQ ratio
|
||||
$yiq = (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
|
||||
|
||||
// Check contrast
|
||||
return ($yiq >= 128) ? '#000000' : '#FFFFFF';
|
||||
}
|
||||
|
||||
static public function beam(string $name, array $options = []): string
|
||||
{
|
||||
$colors = $options['colors'] ?? ['#0c9', '#9c0', '#6f0'];
|
||||
$w = intval($options['size'] ?? 36);
|
||||
$options['square'] ??= false;
|
||||
|
||||
$size = 36;
|
||||
$numFromName = self::hashCode($name);
|
||||
$range = count($colors);
|
||||
$wrapperColor = self::getRandomColor($numFromName, $colors, $range);
|
||||
$preTranslateX = self::getUnit($numFromName, 10, 1);
|
||||
$wrapperTranslateX = $preTranslateX < 5 ? $preTranslateX + $size / 9 : $preTranslateX;
|
||||
$preTranslateY = self::getUnit($numFromName, 10, 2);
|
||||
$wrapperTranslateY = $preTranslateY < 5 ? $preTranslateY + $size / 9 : $preTranslateY;
|
||||
|
||||
$faceColor = self::getContrast($wrapperColor);
|
||||
$backgroundColor = self::getRandomColor($numFromName + 13, $colors, $range);
|
||||
$wrapperRotate = self::getUnit($numFromName, 360);
|
||||
$wrapperScale = 1 + self::getUnit($numFromName, intval($size / 12)) / 10;
|
||||
$isMouthOpen = self::getBoolean($numFromName, 2);
|
||||
$isCircle = self::getBoolean($numFromName, 1);
|
||||
$eyeSpread = self::getUnit($numFromName, 5);
|
||||
$mouthSpread = self::getUnit($numFromName, 3);
|
||||
$faceRotate = self::getUnit($numFromName, 10, 3);
|
||||
$faceTranslateX = $wrapperTranslateX > $size / 6 ? $wrapperTranslateX / 2 : self::getUnit($numFromName, 6, 1);
|
||||
$faceTranslateY = $wrapperTranslateY > $size / 6 ? $wrapperTranslateY / 2 : self::getUnit($numFromName, 5, 2);
|
||||
|
||||
$maskID = 'mask-' . md5(random_bytes(8));
|
||||
|
||||
$rx1 = $options['square'] ? 0 : $size * 2;
|
||||
$rx2 = $isCircle ? $size : $size / 6;
|
||||
$rx3 = 1 + self::getUnit($numFromName, 6, 2);
|
||||
$half_size = $size / 2;
|
||||
$spread = 22 + $mouthSpread;
|
||||
|
||||
if (!$isMouthOpen) {
|
||||
$mouth = "<path d=\"M13 {$spread}c4 2 8 2 12 0\" stroke=\"{$faceColor}\" fill=\"none\" strokeLinecap=\"round\" />";
|
||||
}
|
||||
else {
|
||||
$mouth = "<path d=\"M12,{$spread} a2,1.5 0 0,0 14,0\" fill=\"{$faceColor}\" />";
|
||||
}
|
||||
|
||||
$x1 = 14 - $eyeSpread;
|
||||
$x2 = 20 + $eyeSpread;
|
||||
|
||||
return <<<EOF
|
||||
<svg
|
||||
viewBox="0 0 {$size} {$size}"
|
||||
fill="none"
|
||||
role="img"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="{$w}"
|
||||
height="{$w}"
|
||||
>
|
||||
<mask id="{$maskID}" maskUnits="userSpaceOnUse" x="0" y="0" width="{$size}" height="{$size}">
|
||||
<rect width="{$size}" height="{$size}" rx="{$rx1}" fill="#FFFFFF" />
|
||||
</mask>
|
||||
<g mask="url('#{$maskID}')">
|
||||
<rect width="{$size}" height="{$size}" fill="{$backgroundColor}" />
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="{$size}"
|
||||
height="{$size}"
|
||||
transform="translate({$wrapperTranslateX} {$wrapperTranslateY}) rotate({$wrapperRotate} {$half_size} {$half_size}) scale({$wrapperScale})"
|
||||
fill="{$wrapperColor}"
|
||||
rx="{$rx2}"
|
||||
/>
|
||||
<g transform="translate({$faceTranslateY} {$faceTranslateY}) rotate({$faceRotate} $half_size $half_size)">
|
||||
{$mouth}
|
||||
<rect
|
||||
x="{$x1}"
|
||||
y="14"
|
||||
width="4"
|
||||
height="6"
|
||||
rx="{$rx3}"
|
||||
stroke="none"
|
||||
fill="{$faceColor}"
|
||||
/>
|
||||
<rect
|
||||
x="{$x2}"
|
||||
y="14"
|
||||
width="4"
|
||||
height="6"
|
||||
rx="{$rx3}"
|
||||
stroke="none"
|
||||
fill="{$faceColor}"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
EOF;
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ namespace KaraDAV;
|
|||
|
||||
use KD2\WebDAV\NextCloud as WebDAV_NextCloud;
|
||||
use KD2\WebDAV\Exception as WebDAV_Exception;
|
||||
use KD2\Graphics\SVG\Avatar;
|
||||
|
||||
class NextCloud extends WebDAV_NextCloud
|
||||
{
|
||||
|
@ -187,4 +188,9 @@ class NextCloud extends WebDAV_NextCloud
|
|||
return ['created' => !$exists, 'etag' => md5(filemtime($target) . filesize($target))];
|
||||
}
|
||||
|
||||
protected function nc_avatar(): void
|
||||
{
|
||||
header('Content-Type: image/svg+xml; charset=utf-8');
|
||||
echo Avatar::beam($_SERVER['REQUEST_URI'] ?? '', ['colors' => ['#009', '#ccf', '#9cf']]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -649,7 +649,7 @@ class Storage extends AbstractStorage implements TrashInterface
|
|||
return null;
|
||||
}
|
||||
|
||||
$path = $this->users->current()->path . $uri;
|
||||
$path = $user->path . $uri;
|
||||
|
||||
if (!file_exists($path)) {
|
||||
return null;
|
||||
|
@ -662,7 +662,7 @@ class Storage extends AbstractStorage implements TrashInterface
|
|||
WOPI::PROP_READ_ONLY => $readonly,
|
||||
WOPI::PROP_USER_NAME => $user->login,
|
||||
WOPI::PROP_USER_ID => md5($user->login),
|
||||
WOPI::PROP_USER_AVATAR => null,
|
||||
WOPI::PROP_USER_AVATAR => $user->avatar_url,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ class Users
|
|||
$user->path = rtrim(realpath($user->path), '/') . '/';
|
||||
|
||||
$user->dav_url = WWW_URL . 'files/' . $user->login . '/';
|
||||
$user->avatar_url = WWW_URL . 'avatars/' . substr(md5($user->login), 0, 16);
|
||||
}
|
||||
|
||||
return $user;
|
||||
|
|
|
@ -46,7 +46,7 @@ html_head('My files');
|
|||
|
||||
echo <<<EOF
|
||||
<h2 class="myfiles"><a class="btn" href="{$user->dav_url}">Manage my files</a></h2>
|
||||
<h3>Hello, {$username} !</h3>
|
||||
<h3>Hello, {$username} ! <img src="{$user->avatar_url}" alt="" /></h3>
|
||||
<dl>
|
||||
<dd><h3>{$percent} used, {$free} free</h3></dd>
|
||||
<dd><progress max="{$quota->total}" value="{$quota->used}"></progress>
|
||||
|
|
|
@ -180,6 +180,11 @@ p.info tt {
|
|||
padding: .2em;
|
||||
}
|
||||
|
||||
h3 img {
|
||||
vertical-align: middle;
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
main {
|
||||
border-radius: 0;
|
||||
|
|
|
@ -142,6 +142,7 @@ else {
|
|||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>User</th>
|
||||
<td>Quota</td>
|
||||
<td>Admin</td>
|
||||
|
@ -154,11 +155,13 @@ else {
|
|||
$quota = $users->quota($user);
|
||||
|
||||
printf('<tr>
|
||||
<td><img src="%s" alt="" /></td>
|
||||
<th>%s</th>
|
||||
<td>%s used out of %s<br /><progress max="%d" value="%d"></progress></td>
|
||||
<td>%s</td>
|
||||
<td><a href="?edit=%d" class="btn sm">Edit</a> <a href="?delete=%d" class="btn sm">Delete</a></td>
|
||||
</tr>',
|
||||
$user->avatar_url,
|
||||
htmlspecialchars($user->login),
|
||||
format_bytes($quota->used),
|
||||
format_bytes($quota->total),
|
||||
|
|
Loading…
Reference in a new issue