1
0
Fork 0
mirror of synced 2024-06-24 01:00:35 +12:00

Merge branch 'master' of github.com:appwrite/appwrite into team-features

This commit is contained in:
Eldad Fux 2020-06-10 22:08:00 +03:00
commit f3c8e57e70
39 changed files with 389 additions and 163 deletions

0
.gitattributes vendored Normal file
View file

View file

@ -2,8 +2,10 @@
## Features
- Added a new route in Locale API to fetch a list of languages
- New route in Locale API to fetch a list of languages
- Added option to force HTTPS connection to the Appwrite server (_APP_OPTIONS_FORCE_HTTPS)
- Added Google Fonts to Appwrite for offline availability
- Added a new route in the Avatars API to get user initials avatar
- Added option to delete team from the console
- Added option to view team members from the console
- Added option to join a user to any team from the console
@ -12,6 +14,8 @@
- Fixed output of /v1/health/queue/certificates returning wrong data
- Fixed bug where team members count was wrong in some cases
- Fixed network calculation for uploaded files
- Fixed a UI bug preventing float values in numeric fields
## Security

View file

@ -51,6 +51,7 @@ ENV TZ=Asia/Tel_Aviv \
_APP_HOME=https://appwrite.io \
_APP_EDITION=community \
_APP_OPTIONS_ABUSE=enabled \
_APP_OPTIONS_FORCE_HTTPS=disabled \
_APP_OPENSSL_KEY_V1=your-secret-key \
_APP_STORAGE_LIMIT=104857600 \
_APP_STORAGE_ANTIVIRUS=enabled \

View file

@ -82,6 +82,14 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $
* As recommended at:
* @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
*/
if ($request->getServer('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
if(Config::getParam('protocol') !== 'https') {
return $response->redirect('https://' . Config::getParam('domain').$request->getServer('REQUEST_URI'));
}
$response->addHeader('Strict-Transport-Security', 'max-age='.(60 * 60 * 24 * 126)); // 126 days
}
$response
->addHeader('Server', 'Appwrite')
->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url='.urlencode($request->getServer('REQUEST_URI')))
@ -236,12 +244,12 @@ $utopia->shutdown(function () use ($response, $request, $webhook, $audit, $usage
}
$route = $utopia->match($request);
if($project->getId()
&& $mode !== APP_MODE_ADMIN
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage and admin mode
$usage
->setParam('request', $request->getSize())
->setParam('request', $request->getSize() + $usage->getParam('storage'))
->setParam('response', $response->getSize())
->trigger()
;

View file

@ -16,6 +16,7 @@ use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use Utopia\Config\Config;
use Utopia\Validator\HexColor;
include_once __DIR__ . '/../shared/api.php';
@ -385,7 +386,83 @@ $utopia->get('/v1/avatars/qr')
$response
->addHeader('Expires', date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->send('', $writer->writeString($text))
->send($writer->writeString($text))
;
}
);
$utopia->get('/v1/avatars/initials')
->desc('Get User Initials')
->param('name', '', function () { return new Text(512); }, 'Full Name. When empty, current user name or email will be used.', true)
->param('width', 500, function () { return new Range(0, 2000); }, 'Image width. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('height', 500, function () { return new Range(0, 2000); }, 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true)
->param('color', '', function () { return new HexColor(); }, 'Changes text color. By default a random color will be picked and stay will persistent to the given name.', true)
->param('background', '', function () { return new HexColor(); }, 'Changes background color. By default a random color will be picked and stay will persistent to the given name.', true)
->label('scope', 'avatars.read')
->label('sdk.platform', [APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER])
->label('sdk.namespace', 'avatars')
->label('sdk.method', 'getInitials')
->label('sdk.methodType', 'location')
->label('sdk.description', '/docs/references/avatars/get-initials.md')
->action(
function ($name, $width, $height, $color, $background) use ($response, $user) {
$themes = [
['color' => '#27005e', 'background' => '#e1d2f6'], // VIOLET
['color' => '#5e2700', 'background' => '#f3d9c6'], // ORANGE
['color' => '#006128', 'background' => '#c9f3c6'], // GREEN
['color' => '#580061', 'background' => '#f2d1f5'], // FUSCHIA
['color' => '#00365d', 'background' => '#c6e1f3'], // BLUE
['color' => '#00075c', 'background' => '#d2d5f6'], // INDIGO
['color' => '#610038', 'background' => '#f5d1e6'], // PINK
['color' => '#386100', 'background' => '#dcf1bd'], // LIME
['color' => '#615800', 'background' => '#f1ecba'], // YELLOW
['color' => '#610008', 'background' => '#f6d2d5'] // RED
];
$rand = rand(0, count($themes)-1);
$name = (!empty($name)) ? $name : $user->getAttribute('name', $user->getAttribute('email', ''));
$words = explode(' ', strtoupper($name));
$initials = null;
$code = 0;
foreach ($words as $key => $w) {
$initials .= (isset($w[0])) ? $w[0] : '';
$code += (isset($w[0])) ? ord($w[0]) : 0;
if($key == 1) {
break;
}
}
$length = count($words);
$rand = substr($code,-1);
$background = (!empty($background)) ? '#'.$background : $themes[$rand]['background'];
$color = (!empty($color)) ? '#'.$color : $themes[$rand]['color'];
$image = new \Imagick();
$draw = new \ImagickDraw();
$fontSize = min($width, $height) / 2;
$draw->setFont(__DIR__."/../../../public/fonts/poppins-v9-latin-600.ttf");
$image->setFont(__DIR__."/../../../public/fonts/poppins-v9-latin-600.ttf");
$draw->setFillColor(new \ImagickPixel($color));
$draw->setFontSize($fontSize);
$draw->setTextAlignment(\Imagick::ALIGN_CENTER);
$draw->annotation($width / 1.97, ($height / 2) + ($fontSize / 3), $initials);
$image->newImage($width, $height, $background);
$image->setImageFormat("png");
$image->drawImage($draw);
//$image->setImageCompressionQuality(9 - round(($quality / 100) * 9));
$response
->addHeader('Expires', date('D, d M Y H:i:s', time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
->setContentType('image/png')
->send($image->getImageBlob())
;
}
);

View file

@ -424,6 +424,10 @@ $utopia->delete('/v1/projects/:projectId')
}
}
}
if (!$consoleDB->deleteDocument($project->getAttribute('teamId', null))) {
throw new Exception('Failed to remove project team from DB', 500);
}
if (!$consoleDB->deleteDocument($projectId)) {
throw new Exception('Failed to remove project from DB', 500);

View file

@ -1,6 +1,6 @@
<div class="cover">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console" class="back text-size-small"><i class="icon-left-open"></i> Home</a>
<a data-ls-attrs="href=/console" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Home</a>
<br />
<span>Your Account</span>
@ -26,7 +26,7 @@
<br />
<a href="https://en.gravatar.com/gravatars/new/" rel="noopener" class="button margin-bottom-small" target="_blank"><i class="icon-upload"></i> Upload</a>
<a href="https://en.gravatar.com/gravatars/new/" rel="noopener" class="button margin-bottom-small link-animation-disabled" target="_blank"><i class="icon-upload"></i> Upload</a>
<br />
@ -317,4 +317,4 @@
</div>
</li>
</ul>
</div>
</div>

View file

@ -1,5 +1,5 @@
<header class="clear">
<a href="/console" class="logo pull-start">
<a href="/console" class="logo pull-start link-animation-disabled">
<img src="/images/appwrite.svg" alt="Appwrite Light Logo" class="force-light" />
<img src="/images/appwrite-footer-dark.svg" alt="Appwrite Dark Logo" class="force-dark" />
</a>
@ -34,10 +34,10 @@
data-failure-param-trigger-events="account.deleteSession">
<div class="console-back">
<a href="/console">Back to Console &nbsp;<i class="icon-right-open"></i></a>
<a href="/console" class="link-return-animation--end">Back to Console &nbsp;<i class="icon-right-open"></i></a>
</div>
<div class="account link pull-end clear">
<div class="account link link-animation-disabled pull-end clear">
<img src="" data-ls-attrs="src={{account|gravatar}}" alt="User Avatar" class="avatar margin-start pull-end" />
<span class="name pull-end desktops-only" data-ls-bind="{{account.name}}"></span>
</div>
@ -45,10 +45,10 @@
<div class="console-index drop-list bottom end" data-ls-ui-open="" data-button-text="" data-button-icon="" data-button-selector="[data-toggler]" data-button-class="account-button" data-blur="1" tabindex="1">
<ul class="margin-top-large arrow-end">
<li>
<a href="/console/account"><i class="icon-user"></i> &nbsp; Your Account</a>
<a href="/console/account" class="link-animation-disabled"><i class="icon-user"></i> &nbsp; Your Account</a>
</li>
<li>
<span class="link"><i class="icon-sun-inv force-dark pull-start"></i><i class="icon-moon-inv force-light pull-start"></i> &nbsp; Change Theme
<span class="link link-animation-disabled"><i class="icon-sun-inv force-dark pull-start"></i><i class="icon-moon-inv force-light pull-start"></i> &nbsp; Change Theme
<div class="pull-end switch-theme">
<button data-general-theme
data-analytics-event="click"
@ -66,7 +66,7 @@
</div>
<nav class="project-only" data-ls-ui-open="" data-button-class="round icon-btn phones-only tablets-only" data-button-icon="icon-dot-3">
<a class="logo" href="/console"
<a class="logo link-animation-disabled" href="/console"
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Logo Link">
@ -83,7 +83,8 @@
<a data-ls-attrs="href=/console/home?project={{router.params.project}}"
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Home Link">
data-analytics-label="Home Link"
class="link-animation-disabled">
<i class="icon-home"></i>
Home
</a>
@ -97,7 +98,8 @@
<a data-ls-attrs="href=/console/database?project={{router.params.project}}"
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Database Link">
data-analytics-label="Database Link"
class="link-animation-disabled">
<i class="icon-database"></i>
Database
</a>
@ -106,7 +108,8 @@
<a data-ls-attrs="href=/console/storage?project={{router.params.project}}"
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Storage Link">
data-analytics-label="Storage Link"
class="link-animation-disabled">
<i class="icon-folder"></i>
Storage
</a>
@ -115,7 +118,8 @@
<a data-ls-attrs="href=/console/users?project={{router.params.project}}"
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Users Link">
data-analytics-label="Users Link"
class="link-animation-disabled">
<i class="icon-users"></i>
Users
</a>
@ -129,7 +133,8 @@
<a data-ls-attrs="href=/console/tasks?project={{router.params.project}}"
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Tasks Link">
data-analytics-label="Tasks Link"
class="link-animation-disabled">
<i class="icon-clock"></i>
Tasks
</a>
@ -138,7 +143,8 @@
<a data-ls-attrs="href=/console/webhooks?project={{router.params.project}}"
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Webhooks Links">
data-analytics-label="Webhooks Links"
class="link-animation-disabled">
<i class="icon-link"></i>
Webhooks
</a>
@ -147,7 +153,8 @@
<a data-ls-attrs="href=/console/keys?project={{router.params.project}}"
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="API Keys Link">
data-analytics-label="API Keys Link"
class="link-animation-disabled">
<i class="icon-key-inv"></i>
API Keys
</a>
@ -160,7 +167,8 @@
<a data-ls-attrs="href=/console/settings?project={{router.params.project}}"
data-analytics-event="click"
data-analytics-category="console/navigation"
data-analytics-label="Settings Link">
data-analytics-label="Settings Link"
class="link-animation-disabled">
<i class="icon-cog"></i> Settings</a>
</li>
</ul>
@ -214,4 +222,4 @@
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</footer>
</form>
</div>
</div>

View file

@ -12,7 +12,7 @@ $rules = $collection->getAttribute('rules', []);
<div class="cover">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/database?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Database</a>
<a data-ls-attrs="href=/console/database?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Database</a>
<br />
@ -457,7 +457,7 @@ $rules = $collection->getAttribute('rules', []);
<script type="text/html" id="template-validation-numeric">
<div class="margin-bottom">
<label data-ls-attrs="for=rule-default-{{rule.$id}}">Default Value</label>
<input name="default" type="number" data-ls-bind="{{rule.default}}" data-cast-to="integer" placeholder="0" />
<input name="default" type="number" data-ls-bind="{{rule.default}}" data-cast-to="numeric" placeholder="0" step="any" />
</div>
</script>
@ -516,4 +516,4 @@ $rules = $collection->getAttribute('rules', []);
data-event="load,database.createCollection,database.updateCollection,database.deleteCollection"
data-scope="sdk"
data-name="project-collections">
</div>
</div>

View file

@ -20,7 +20,7 @@ $collections = [];
$key = (isset($rule['key'])) ? $rule['key'] : '';
$label = (isset($rule['label'])) ? $rule['label'] : '';
$type = (isset($rule['type'])) ? $rule['type'] : '';
$list = (isset($rule['list'])) ? $rule['list'] : [];
$list = (isset($rule['list']) && !empty($list)) ? $rule['list'] : [];
?>
<?php foreach($list as $item):
if($item === $collection->getId()) {
@ -51,7 +51,7 @@ $collections = [];
$key = (isset($rule['key'])) ? $rule['key'] : '';
$label = (isset($rule['label'])) ? $rule['label'] : '';
$type = (isset($rule['type'])) ? $rule['type'] : '';
$list = (isset($rule['list'])) ? $rule['list'] : false;
$list = (isset($rule['list'])) ? $rule['list'] : [];
$array = (isset($rule['array'])) ? $rule['array'] : false;
?>
@ -163,7 +163,7 @@ $collections = [];
<div class="cover">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/database/collection?id={{router.params.collection}}&project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> <?php echo $this->escape($name); ?></a>
<a data-ls-attrs="href=/console/database/collection?id={{router.params.collection}}&project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> <?php echo $this->escape($name); ?></a>
<br />
@ -315,4 +315,4 @@ $collections = [];
</li> -->
</ul>
</div>
</div>
</div>

View file

@ -1,6 +1,6 @@
<div class="cover">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Home</a>
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Home</a>
<br />
<span>Database</span>
@ -65,7 +65,7 @@
<div data-ls-if="0 != {{project-collections.sum}}">
<ul data-ls-loop="project-collections.collections" data-ls-as="collection" data-ls-append="" class="tiles cell-3 margin-bottom-small" style="visibility: hidden">
<li class="margin-bottom">
<a data-ls-attrs="href=/console/database/collection?id={{collection.$id}}&project={{router.params.project}}" class="box">
<a data-ls-attrs="href=/console/database/collection?id={{collection.$id}}&project={{router.params.project}}" class="box link-animation-disabled">
<div data-ls-bind="{{collection.name}}" class="text-one-liner margin-bottom text-bold">&nbsp;</div>
<i class="icon-right-open"></i>
@ -109,4 +109,4 @@
</li> -->
</ul>
</div>
</div>
</div>

View file

@ -4,4 +4,4 @@ $required = $this->getParam('required', '');
$namespace = $this->getParam('namespace', '');
?>
<input name="<?php echo $this->escape($key); ?>" type="number" autocomplete="off" data-ls-bind="{{<?php echo $this->escape($namespace); ?>}}" data-cast-to="integer"<?php if($required): ?> required<?php endif; ?> class="margin-bottom-no" />
<input name="<?php echo $this->escape($key); ?>" type="number" autocomplete="off" data-ls-bind="{{<?php echo $this->escape($namespace); ?>}}" data-cast-to="numeric"<?php if($required): ?> required<?php endif; ?> class="margin-bottom-no" step=any />

View file

@ -33,7 +33,7 @@ $home = $this->getParam('home', '');
<ul data-ls-loop="console-projects" data-ls-as="project" data-ls-append="" class="tiles cell-3" style="visibility: hidden">
<li class="margin-bottom">
<a data-ls-attrs="href=/console/home?project={{project.$id}}" class="box">
<a data-ls-attrs="href=/console/home?project={{project.$id}}" class="box link-animation-disabled">
<div data-ls-bind="{{project.name}}" class="text-one-liner margin-bottom-tiny text-bold">&nbsp;</div>
<p data-ls-if="({{project.platforms.length}})" class="text-fade text-size-small" data-ls-bind="{{project.platforms.length}} apps"></p>
@ -102,4 +102,4 @@ $home = $this->getParam('home', '');
</div>
</div>
</div>
</section>
</section>

View file

@ -3,7 +3,7 @@ $scopes = $this->getParam('scopes', []);
?>
<div class="cover margin-bottom-large">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Home</a>
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Home</a>
<br />
<span>API Keys</span>
@ -167,4 +167,4 @@ $scopes = $this->getParam('scopes', []);
</form>
</div>
</div>
</div>
</div>

View file

@ -6,7 +6,7 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
?>
<div class="cover">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Home</a>
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Home</a>
<br />
<span>Settings</span>
@ -27,8 +27,10 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
<li data-state="/console/settings?project={{router.params.project}}">
<h2>Overview</h2>
<div class="row responsive">
<div class="row responsive margin-top-negative">
<div class="col span-8 margin-bottom">
<label>&nbsp;</label>
<div class="box margin-bottom-large">
<form
data-analytics-event="submit"
@ -500,4 +502,4 @@ $customDomainsTarget = $this->getParam('customDomainsTarget', false);
</li>
</ul>
</div>
</div>
</div>

View file

@ -5,7 +5,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
?>
<div class="cover">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Home</a>
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Home</a>
<br />
<span>Storage</span>
@ -36,7 +36,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
data-failure="alert"
data-failure-param-alert-text="Failed to upload file"
data-failure-param-alert-classname="error">
<input type="hidden" name="folderId" id="files-folderId" data-cast-to="int" value="1">
<input type="hidden" name="folderId" id="files-folderId" data-cast-to="integer" value="1">
<label for="file-read">File</label>
<input type="file" name="file" id="file-file" size="1" required>
@ -141,7 +141,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
<div class="input-copy">
<input data-forms-copy type="text" data-ls-attrs="id=file-id-{{file.$id}}" name="fileId" disabled data-ls-bind="{{file.$id}}" />
</div>
<input type="hidden" data-ls-attrs="id=file-folderId-{{file.$id}}" name="folderId" data-cast-to="int" value="1">
<input type="hidden" data-ls-attrs="id=file-folderId-{{file.$id}}" name="folderId" data-cast-to="integer" value="1">
<label for="file-read">Read Access (<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank">Learn more</a>)</label>
<input type="hidden" data-ls-attrs="id=file-read-{{file.$id}}" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{file.$permissions.read}}" placeholder="User ID, Team ID or Role" />
@ -240,4 +240,4 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
</div>
</li>
</ul>
</div>
</div>

View file

@ -1,6 +1,6 @@
<div class="cover margin-bottom-large">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Home</a>
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Home</a>
<br />
<span>Tasks</span>
@ -346,4 +346,4 @@
</form>
</div>
</div>
</div>
</div>

View file

@ -4,7 +4,7 @@ $providers = $this->getParam('providers', []);
<div class="cover">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Home</a>
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Home</a>
<br />
<span>Users</span>
@ -387,4 +387,4 @@ $providers = $this->getParam('providers', []);
</div>
</li>
</ul>
</div>
</div>

View file

@ -8,7 +8,7 @@
<div class="cover">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/users?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Users</a>
<a data-ls-attrs="href=/console/users?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Users</a>
<br />
<span data-ls-bind="{{user.name}}">&nbsp;</span>
@ -255,4 +255,4 @@
</li>
</ul>
</div>
</div>
</div>

View file

@ -5,7 +5,7 @@ $events = array_keys($this->getParam('events', []));
?>
<div class="cover margin-bottom-large">
<h1 class="zone xl margin-bottom-large">
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small"><i class="icon-left-open"></i> Home</a>
<a data-ls-attrs="href=/console/home?project={{router.params.project}}" class="back text-size-small link-return-animation--start"><i class="icon-left-open"></i> Home</a>
<br />
<span>Webhooks</span>
@ -227,4 +227,4 @@ $events = array_keys($this->getParam('events', []));
</form>
</div>
</div>
</div>
</div>

View file

@ -1,6 +1,6 @@
<div class="logo text-align-center margin-bottom-xl">
<a href="/">
<a href="/" class="link-animation-disabled">
<img src="/images/appwrite.svg" alt="Appwrite Light Logo" class="force-light" />
<img src="/images/appwrite-footer-dark.svg" alt="Appwrite Dark Logo" class="force-dark" />
</a>
</div>
</div>

View file

@ -1,6 +1,3 @@
#!/bin/bash
export PHP_VERSION=$PHP_VERSION
# Init server settings
php /usr/share/nginx/html/app/tasks/migrate.php run

View file

@ -66,6 +66,7 @@ services:
#- _APP_ENV=production
- _APP_ENV=development
- _APP_OPTIONS_ABUSE=disabled
- _APP_OPTIONS_FORCE_HTTPS=disabled
- _APP_OPENSSL_KEY_V1=your-secret-key
- _APP_DOMAIN=demo.appwrite.io
- _APP_DOMAIN_TARGET=demo.appwrite.io

View file

@ -0,0 +1,3 @@
Use this endpoint to show your user initials avatar icon on your website or app. By default, this route will try to print your logged-in user name or email initials. You can also overwrite the user name if you pass the 'name' parameter. If no name is given and no user is logged, an empty avatar will be returned.
You can use the color and background params to change the avatar colors. By default, a random theme will be selected. The random theme will persist for the user's initials when reloading the same theme will always return for the same initials.

View file

@ -10,7 +10,11 @@ Set your server running environment. By default, the var is set to 'development'
### _APP_OPTIONS_ABUSE
Allows you to disable abuse checks and API rate limiting. By default, set to 'enabled'. To cancel the abuse checking, set to 'disabled'. It is not recommended to disable this check-in a production environment.
Allows you to disable abuse checks and API rate limiting. By default, set to 'enabled'. To cancel the abuse checking, set to 'disabled'. It is not recommended to disable this feature in a production environment.
### _APP_OPTIONS_FORCE_HTTPS
Allows you to force HTTPS connection to your API. This feature redirects any HTTP call to HTTPS and adds the 'Strict-Transport-Security' header to all HTTP responses. By default, set to 'disabled'. To enable, set to 'enabled'. This feature will work only when your ports are set to default 80 and 443.
### _APP_OPENSSL_KEY_V1

View file

@ -2511,7 +2511,7 @@ match=text.match(new RegExp(regex,'gi'))
if(!match){return fail}
for(i=0,len=match.length;i<len;i++){if(!process(match[i])){return fail}}
return(date.getTime()/1000)}
return{format:format,strtotime:strtotime}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,to){switch(to){case'int':case'integer':value=parseInt(value);break;case'string':value=value.toString();break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value&&value.constructor&&value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
return{format:format,strtotime:strtotime}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,to){switch(to){case'int':case'integer':value=parseInt(value);break;case'numeric':value=Number(value);break;case'string':value=value.toString();break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value&&value.constructor&&value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
return value;}
function toJson(element,json){json=json||{};let name=element.getAttribute('name');let type=element.getAttribute('type');let castTo=element.getAttribute('data-cast-to');let ref=json;if(name&&'FORM'!==element.tagName){if('FIELDSET'===element.tagName){if(castTo==='object'){if(json[name]===undefined){json[name]={};}
ref=json[name];}
@ -2553,15 +2553,10 @@ return k;}
function J(k){k=k.replace(/rn/g,"n");let d="";for(let F=0;F<k.length;F++){let x=k.charCodeAt(F);if(x<128){d+=String.fromCharCode(x);}else{if(x>127&&x<2048){d+=String.fromCharCode((x>>6)|192);d+=String.fromCharCode((x&63)|128);}else{d+=String.fromCharCode((x>>12)|224);d+=String.fromCharCode(((x>>6)&63)|128);d+=String.fromCharCode((x&63)|128);}}}
return d;}
let C=Array();let P,h,E,v,g,Y,X,W,V;let S=7,Q=12,N=17,M=22;let A=5,z=9,y=14,w=20;let o=4,m=11,l=16,j=23;let U=6,T=10,R=15,O=21;s=J(s);C=e(s);Y=1732584193;X=4023233417;W=2562383102;V=271733878;for(P=0;P<C.length;P+=16){h=Y;E=X;v=W;g=V;Y=u(Y,X,W,V,C[P+0],S,3614090360);V=u(V,Y,X,W,C[P+1],Q,3905402710);W=u(W,V,Y,X,C[P+2],N,606105819);X=u(X,W,V,Y,C[P+3],M,3250441966);Y=u(Y,X,W,V,C[P+4],S,4118548399);V=u(V,Y,X,W,C[P+5],Q,1200080426);W=u(W,V,Y,X,C[P+6],N,2821735955);X=u(X,W,V,Y,C[P+7],M,4249261313);Y=u(Y,X,W,V,C[P+8],S,1770035416);V=u(V,Y,X,W,C[P+9],Q,2336552879);W=u(W,V,Y,X,C[P+10],N,4294925233);X=u(X,W,V,Y,C[P+11],M,2304563134);Y=u(Y,X,W,V,C[P+12],S,1804603682);V=u(V,Y,X,W,C[P+13],Q,4254626195);W=u(W,V,Y,X,C[P+14],N,2792965006);X=u(X,W,V,Y,C[P+15],M,1236535329);Y=f(Y,X,W,V,C[P+1],A,4129170786);V=f(V,Y,X,W,C[P+6],z,3225465664);W=f(W,V,Y,X,C[P+11],y,643717713);X=f(X,W,V,Y,C[P+0],w,3921069994);Y=f(Y,X,W,V,C[P+5],A,3593408605);V=f(V,Y,X,W,C[P+10],z,38016083);W=f(W,V,Y,X,C[P+15],y,3634488961);X=f(X,W,V,Y,C[P+4],w,3889429448);Y=f(Y,X,W,V,C[P+9],A,568446438);V=f(V,Y,X,W,C[P+14],z,3275163606);W=f(W,V,Y,X,C[P+3],y,4107603335);X=f(X,W,V,Y,C[P+8],w,1163531501);Y=f(Y,X,W,V,C[P+13],A,2850285829);V=f(V,Y,X,W,C[P+2],z,4243563512);W=f(W,V,Y,X,C[P+7],y,1735328473);X=f(X,W,V,Y,C[P+12],w,2368359562);Y=D(Y,X,W,V,C[P+5],o,4294588738);V=D(V,Y,X,W,C[P+8],m,2272392833);W=D(W,V,Y,X,C[P+11],l,1839030562);X=D(X,W,V,Y,C[P+14],j,4259657740);Y=D(Y,X,W,V,C[P+1],o,2763975236);V=D(V,Y,X,W,C[P+4],m,1272893353);W=D(W,V,Y,X,C[P+7],l,4139469664);X=D(X,W,V,Y,C[P+10],j,3200236656);Y=D(Y,X,W,V,C[P+13],o,681279174);V=D(V,Y,X,W,C[P+0],m,3936430074);W=D(W,V,Y,X,C[P+3],l,3572445317);X=D(X,W,V,Y,C[P+6],j,76029189);Y=D(Y,X,W,V,C[P+9],o,3654602809);V=D(V,Y,X,W,C[P+12],m,3873151461);W=D(W,V,Y,X,C[P+15],l,530742520);X=D(X,W,V,Y,C[P+2],j,3299628645);Y=t(Y,X,W,V,C[P+0],U,4096336452);V=t(V,Y,X,W,C[P+7],T,1126891415);W=t(W,V,Y,X,C[P+14],R,2878612391);X=t(X,W,V,Y,C[P+5],O,4237533241);Y=t(Y,X,W,V,C[P+12],U,1700485571);V=t(V,Y,X,W,C[P+3],T,2399980690);W=t(W,V,Y,X,C[P+10],R,4293915773);X=t(X,W,V,Y,C[P+1],O,2240044497);Y=t(Y,X,W,V,C[P+8],U,1873313359);V=t(V,Y,X,W,C[P+15],T,4264355552);W=t(W,V,Y,X,C[P+6],R,2734768916);X=t(X,W,V,Y,C[P+13],O,1309151649);Y=t(Y,X,W,V,C[P+4],U,4149444226);V=t(V,Y,X,W,C[P+11],T,3174756917);W=t(W,V,Y,X,C[P+2],R,718787259);X=t(X,W,V,Y,C[P+9],O,3951481745);Y=K(Y,h);X=K(X,E);W=K(W,v);V=K(V,g);}
let i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase();};let size=element.dataset["size"]||80;let email=$value.email||$value||"";let name=$value.name||$value||"";name=(typeof name!=='string')?'':name;let theme=name.split("").map(char=>char.charCodeAt(0)).reduce((a,b)=>a+b,0).toString();let themes=[{color:"27005e",background:"e1d2f6"},{color:"5e2700",background:"f3d9c6"},{color:"006128",background:"c9f3c6"},{color:"580061",background:"f2d1f5"},{color:"00365d",background:"c6e1f3"},{color:"00075c",background:"d2d5f6"},{color:"610038",background:"f5d1e6"},{color:"386100",background:"dcf1bd"},{color:"615800",background:"f1ecba"},{color:"610008",background:"f6d2d5"}];name=name.split(" ").map(function(n){if(!isNaN(parseFloat(n))&&isFinite(n)){return"";}
return n[0];}).join("")||"--";let background=themes[theme[theme.length-1]]["background"];let color=themes[theme[theme.length-1]]["color"];let def="https://ui-avatars.com/api/"+
encodeURIComponent(name)+"/"+
size+"/"+
encodeURIComponent(background)+"/"+
encodeURIComponent(color);return("//www.gravatar.com/avatar/"+
MD5(email)+".jpg?s="+
size+"&d="+
encodeURIComponent(def));}).add("selectedCollection",function($value,router){return $value===router.params.collectionId?"selected":"";}).add("selectedDocument",function($value,router){return $value===router.params.documentId?"selected":"";}).add("localeString",function($value){$value=parseInt($value);return!Number.isNaN($value)?$value.toLocaleString():"";}).add("date",function($value,date){return date.format("Y-m-d",$value);}).add("date-time",function($value,date){return date.format("Y-m-d H:i",$value);}).add("date-text",function($value,date){return date.format("d M Y",$value);}).add("ms2hum",function($value){let temp=$value;const years=Math.floor(temp/31536000),days=Math.floor((temp%=31536000)/86400),hours=Math.floor((temp%=86400)/3600),minutes=Math.floor((temp%=3600)/60),seconds=temp%60;if(days||hours||seconds||minutes){return((years?years+"y ":"")+
let i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase();};let size=element.dataset["size"]||80;let email=$value.email||$value||"";let name=$value.name||$value||"";name=(typeof name!=='string')?'--':name;let def="/v1/avatars/initials?project=console"+"&name="+
encodeURIComponent(name)+"&width="+
size+"&height="+
size;return def;}).add("selectedCollection",function($value,router){return $value===router.params.collectionId?"selected":"";}).add("selectedDocument",function($value,router){return $value===router.params.documentId?"selected":"";}).add("localeString",function($value){$value=parseInt($value);return!Number.isNaN($value)?$value.toLocaleString():"";}).add("date",function($value,date){return date.format("Y-m-d",$value);}).add("date-time",function($value,date){return date.format("Y-m-d H:i",$value);}).add("date-text",function($value,date){return date.format("d M Y",$value);}).add("ms2hum",function($value){let temp=$value;const years=Math.floor(temp/31536000),days=Math.floor((temp%=31536000)/86400),hours=Math.floor((temp%=86400)/3600),minutes=Math.floor((temp%=3600)/60),seconds=temp%60;if(days||hours||seconds||minutes){return((years?years+"y ":"")+
(days?days+"d ":"")+
(hours?hours+"h ":"")+
(minutes?minutes+"m ":"")+

View file

@ -225,7 +225,7 @@ match=text.match(new RegExp(regex,'gi'))
if(!match){return fail}
for(i=0,len=match.length;i<len;i++){if(!process(match[i])){return fail}}
return(date.getTime()/1000)}
return{format:format,strtotime:strtotime}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,to){switch(to){case'int':case'integer':value=parseInt(value);break;case'string':value=value.toString();break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value&&value.constructor&&value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
return{format:format,strtotime:strtotime}}(),true);})(window);(function(window){"use strict";window.ls.container.set('env',function(){return APP_ENV;},true);})(window);(function(window){"use strict";window.ls.container.set('form',function(){function cast(value,to){switch(to){case'int':case'integer':value=parseInt(value);break;case'numeric':value=Number(value);break;case'string':value=value.toString();break;case'json':value=(value)?JSON.parse(value):[];break;case'array':value=(value&&value.constructor&&value.constructor===Array)?value:[value];break;case'array-empty':value=[];break;case'bool':case'boolean':value=(value==='false')?false:value;value=!!value;break;}
return value;}
function toJson(element,json){json=json||{};let name=element.getAttribute('name');let type=element.getAttribute('type');let castTo=element.getAttribute('data-cast-to');let ref=json;if(name&&'FORM'!==element.tagName){if('FIELDSET'===element.tagName){if(castTo==='object'){if(json[name]===undefined){json[name]={};}
ref=json[name];}
@ -267,15 +267,10 @@ return k;}
function J(k){k=k.replace(/rn/g,"n");let d="";for(let F=0;F<k.length;F++){let x=k.charCodeAt(F);if(x<128){d+=String.fromCharCode(x);}else{if(x>127&&x<2048){d+=String.fromCharCode((x>>6)|192);d+=String.fromCharCode((x&63)|128);}else{d+=String.fromCharCode((x>>12)|224);d+=String.fromCharCode(((x>>6)&63)|128);d+=String.fromCharCode((x&63)|128);}}}
return d;}
let C=Array();let P,h,E,v,g,Y,X,W,V;let S=7,Q=12,N=17,M=22;let A=5,z=9,y=14,w=20;let o=4,m=11,l=16,j=23;let U=6,T=10,R=15,O=21;s=J(s);C=e(s);Y=1732584193;X=4023233417;W=2562383102;V=271733878;for(P=0;P<C.length;P+=16){h=Y;E=X;v=W;g=V;Y=u(Y,X,W,V,C[P+0],S,3614090360);V=u(V,Y,X,W,C[P+1],Q,3905402710);W=u(W,V,Y,X,C[P+2],N,606105819);X=u(X,W,V,Y,C[P+3],M,3250441966);Y=u(Y,X,W,V,C[P+4],S,4118548399);V=u(V,Y,X,W,C[P+5],Q,1200080426);W=u(W,V,Y,X,C[P+6],N,2821735955);X=u(X,W,V,Y,C[P+7],M,4249261313);Y=u(Y,X,W,V,C[P+8],S,1770035416);V=u(V,Y,X,W,C[P+9],Q,2336552879);W=u(W,V,Y,X,C[P+10],N,4294925233);X=u(X,W,V,Y,C[P+11],M,2304563134);Y=u(Y,X,W,V,C[P+12],S,1804603682);V=u(V,Y,X,W,C[P+13],Q,4254626195);W=u(W,V,Y,X,C[P+14],N,2792965006);X=u(X,W,V,Y,C[P+15],M,1236535329);Y=f(Y,X,W,V,C[P+1],A,4129170786);V=f(V,Y,X,W,C[P+6],z,3225465664);W=f(W,V,Y,X,C[P+11],y,643717713);X=f(X,W,V,Y,C[P+0],w,3921069994);Y=f(Y,X,W,V,C[P+5],A,3593408605);V=f(V,Y,X,W,C[P+10],z,38016083);W=f(W,V,Y,X,C[P+15],y,3634488961);X=f(X,W,V,Y,C[P+4],w,3889429448);Y=f(Y,X,W,V,C[P+9],A,568446438);V=f(V,Y,X,W,C[P+14],z,3275163606);W=f(W,V,Y,X,C[P+3],y,4107603335);X=f(X,W,V,Y,C[P+8],w,1163531501);Y=f(Y,X,W,V,C[P+13],A,2850285829);V=f(V,Y,X,W,C[P+2],z,4243563512);W=f(W,V,Y,X,C[P+7],y,1735328473);X=f(X,W,V,Y,C[P+12],w,2368359562);Y=D(Y,X,W,V,C[P+5],o,4294588738);V=D(V,Y,X,W,C[P+8],m,2272392833);W=D(W,V,Y,X,C[P+11],l,1839030562);X=D(X,W,V,Y,C[P+14],j,4259657740);Y=D(Y,X,W,V,C[P+1],o,2763975236);V=D(V,Y,X,W,C[P+4],m,1272893353);W=D(W,V,Y,X,C[P+7],l,4139469664);X=D(X,W,V,Y,C[P+10],j,3200236656);Y=D(Y,X,W,V,C[P+13],o,681279174);V=D(V,Y,X,W,C[P+0],m,3936430074);W=D(W,V,Y,X,C[P+3],l,3572445317);X=D(X,W,V,Y,C[P+6],j,76029189);Y=D(Y,X,W,V,C[P+9],o,3654602809);V=D(V,Y,X,W,C[P+12],m,3873151461);W=D(W,V,Y,X,C[P+15],l,530742520);X=D(X,W,V,Y,C[P+2],j,3299628645);Y=t(Y,X,W,V,C[P+0],U,4096336452);V=t(V,Y,X,W,C[P+7],T,1126891415);W=t(W,V,Y,X,C[P+14],R,2878612391);X=t(X,W,V,Y,C[P+5],O,4237533241);Y=t(Y,X,W,V,C[P+12],U,1700485571);V=t(V,Y,X,W,C[P+3],T,2399980690);W=t(W,V,Y,X,C[P+10],R,4293915773);X=t(X,W,V,Y,C[P+1],O,2240044497);Y=t(Y,X,W,V,C[P+8],U,1873313359);V=t(V,Y,X,W,C[P+15],T,4264355552);W=t(W,V,Y,X,C[P+6],R,2734768916);X=t(X,W,V,Y,C[P+13],O,1309151649);Y=t(Y,X,W,V,C[P+4],U,4149444226);V=t(V,Y,X,W,C[P+11],T,3174756917);W=t(W,V,Y,X,C[P+2],R,718787259);X=t(X,W,V,Y,C[P+9],O,3951481745);Y=K(Y,h);X=K(X,E);W=K(W,v);V=K(V,g);}
let i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase();};let size=element.dataset["size"]||80;let email=$value.email||$value||"";let name=$value.name||$value||"";name=(typeof name!=='string')?'':name;let theme=name.split("").map(char=>char.charCodeAt(0)).reduce((a,b)=>a+b,0).toString();let themes=[{color:"27005e",background:"e1d2f6"},{color:"5e2700",background:"f3d9c6"},{color:"006128",background:"c9f3c6"},{color:"580061",background:"f2d1f5"},{color:"00365d",background:"c6e1f3"},{color:"00075c",background:"d2d5f6"},{color:"610038",background:"f5d1e6"},{color:"386100",background:"dcf1bd"},{color:"615800",background:"f1ecba"},{color:"610008",background:"f6d2d5"}];name=name.split(" ").map(function(n){if(!isNaN(parseFloat(n))&&isFinite(n)){return"";}
return n[0];}).join("")||"--";let background=themes[theme[theme.length-1]]["background"];let color=themes[theme[theme.length-1]]["color"];let def="https://ui-avatars.com/api/"+
encodeURIComponent(name)+"/"+
size+"/"+
encodeURIComponent(background)+"/"+
encodeURIComponent(color);return("//www.gravatar.com/avatar/"+
MD5(email)+".jpg?s="+
size+"&d="+
encodeURIComponent(def));}).add("selectedCollection",function($value,router){return $value===router.params.collectionId?"selected":"";}).add("selectedDocument",function($value,router){return $value===router.params.documentId?"selected":"";}).add("localeString",function($value){$value=parseInt($value);return!Number.isNaN($value)?$value.toLocaleString():"";}).add("date",function($value,date){return date.format("Y-m-d",$value);}).add("date-time",function($value,date){return date.format("Y-m-d H:i",$value);}).add("date-text",function($value,date){return date.format("d M Y",$value);}).add("ms2hum",function($value){let temp=$value;const years=Math.floor(temp/31536000),days=Math.floor((temp%=31536000)/86400),hours=Math.floor((temp%=86400)/3600),minutes=Math.floor((temp%=3600)/60),seconds=temp%60;if(days||hours||seconds||minutes){return((years?years+"y ":"")+
let i=B(Y)+B(X)+B(W)+B(V);return i.toLowerCase();};let size=element.dataset["size"]||80;let email=$value.email||$value||"";let name=$value.name||$value||"";name=(typeof name!=='string')?'--':name;let def="/v1/avatars/initials?project=console"+"&name="+
encodeURIComponent(name)+"&width="+
size+"&height="+
size;return def;}).add("selectedCollection",function($value,router){return $value===router.params.collectionId?"selected":"";}).add("selectedDocument",function($value,router){return $value===router.params.documentId?"selected":"";}).add("localeString",function($value){$value=parseInt($value);return!Number.isNaN($value)?$value.toLocaleString():"";}).add("date",function($value,date){return date.format("Y-m-d",$value);}).add("date-time",function($value,date){return date.format("Y-m-d H:i",$value);}).add("date-text",function($value,date){return date.format("d M Y",$value);}).add("ms2hum",function($value){let temp=$value;const years=Math.floor(temp/31536000),days=Math.floor((temp%=31536000)/86400),hours=Math.floor((temp%=86400)/3600),minutes=Math.floor((temp%=3600)/60),seconds=temp%60;if(days||hours||seconds||minutes){return((years?years+"y ":"")+
(days?days+"d ":"")+
(hours?hours+"h ":"")+
(minutes?minutes+"m ":"")+

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -3,7 +3,7 @@ window.ls.filter
if (!$value) {
return "";
}
// MD5 (Message-Digest Algorithm) by WebToolkit
let MD5 = function(s) {
function L(k, d) {
@ -216,59 +216,68 @@ window.ls.filter
let email = $value.email || $value || "";
let name = $value.name || $value || "";
name = (typeof name !== 'string') ? '' : name;
name = (typeof name !== 'string') ? '--' : name;
let theme = name
.split("")
.map(char => char.charCodeAt(0))
.reduce((a, b) => a + b, 0)
.toString();
let themes = [
{ color: "27005e", background: "e1d2f6" }, // VIOLET
{ color: "5e2700", background: "f3d9c6" }, // ORANGE
{ color: "006128", background: "c9f3c6" }, // GREEN
{ color: "580061", background: "f2d1f5" }, // FUSCHIA
{ color: "00365d", background: "c6e1f3" }, // BLUE
{ color: "00075c", background: "d2d5f6" }, // INDIGO
{ color: "610038", background: "f5d1e6" }, // PINK
{ color: "386100", background: "dcf1bd" }, // LIME
{ color: "615800", background: "f1ecba" }, // YELLOW
{ color: "610008", background: "f6d2d5" } // RED
];
// let theme = name
// .split("")
// .map(char => char.charCodeAt(0))
// .reduce((a, b) => a + b, 0)
// .toString();
// let themes = [
// { color: "27005e", background: "e1d2f6" }, // VIOLET
// { color: "5e2700", background: "f3d9c6" }, // ORANGE
// { color: "006128", background: "c9f3c6" }, // GREEN
// { color: "580061", background: "f2d1f5" }, // FUSCHIA
// { color: "00365d", background: "c6e1f3" }, // BLUE
// { color: "00075c", background: "d2d5f6" }, // INDIGO
// { color: "610038", background: "f5d1e6" }, // PINK
// { color: "386100", background: "dcf1bd" }, // LIME
// { color: "615800", background: "f1ecba" }, // YELLOW
// { color: "610008", background: "f6d2d5" } // RED
// ];
name =
name
.split(" ")
.map(function(n) {
if (!isNaN(parseFloat(n)) && isFinite(n)) {
return "";
}
// name =
// name
// .split(" ")
// .map(function(n) {
// if (!isNaN(parseFloat(n)) && isFinite(n)) {
// return "";
// }
return n[0];
})
.join("") || "--";
// return n[0];
// })
// .join("") || "--";
let background = themes[theme[theme.length - 1]]["background"];
let color = themes[theme[theme.length - 1]]["color"];
// let background = themes[theme[theme.length - 1]]["background"];
// let color = themes[theme[theme.length - 1]]["color"];
let def =
"https://ui-avatars.com/api/" +
"/v1/avatars/initials?project=console"+
"&name=" +
encodeURIComponent(name) +
"/" +
"&width=" +
size +
"/" +
encodeURIComponent(background) +
"/" +
encodeURIComponent(color);
"&height=" +
size;
return (
"//www.gravatar.com/avatar/" +
MD5(email) +
".jpg?s=" +
size +
"&d=" +
encodeURIComponent(def)
);
return def;
// let def =
// "https://ui-avatars.com/api/" +
// encodeURIComponent(name) +
// "/" +
// size +
// "/" +
// encodeURIComponent(background) +
// "/" +
// encodeURIComponent(color);
// return (
// "//www.gravatar.com/avatar/" +
// MD5(email) +
// ".jpg?s=" +
// size +
// "&d=" +
// encodeURIComponent(def)
// );
})
.add("selectedCollection", function($value, router) {
return $value === router.params.collectionId ? "selected" : "";

View file

@ -9,6 +9,9 @@
case 'integer':
value = parseInt(value);
break;
case 'numeric':
value = Number(value);
break;
case 'string':
value = value.toString();
break;

View file

@ -334,9 +334,6 @@ a.box {
}
a.box:hover {
border-bottom: none;
border-right: none;
border-left: none;
box-shadow: 0 0 1px rgba(0, 0, 0, .2);
opacity: .7;
}
@ -362,4 +359,4 @@ a.box:hover {
bottom: 0;
.func-start(-6px);
}
}
}

View file

@ -16,10 +16,6 @@ header {
margin: 7px 0;
display: block;
&:hover {
border-bottom: none;
}
img {
display: block;
}
@ -35,10 +31,6 @@ header {
.account {
.func-margin-start(25px);
&:hover {
border-bottom: none;
}
}
.avatar {
@ -113,4 +105,4 @@ header {
}
}
}
}
}

View file

@ -14,6 +14,8 @@ https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+css+clik
box-shadow: 0 2px 4px 0 rgba(50,50,93,.3);
border-radius: 10px;
margin-bottom: 30px;
&:extend(.force-left);
&:extend(.force-ltr);
* {
font-family: 'Source Code Pro', monospace;

View file

@ -36,7 +36,6 @@ button,
&:hover,
&:focus {
background: var(--config-color-focus-hover);
border-bottom: none;
}
&.fly {
@ -175,10 +174,6 @@ button,
padding: 0;
.func-padding-end(0) !important;
&:hover {
border-bottom: dotted 1px var(--config-color-link);
}
&:focus {
box-shadow: inherit;
}
@ -1300,4 +1295,4 @@ ol {
.pull-start;
}
}
}
}

View file

@ -130,10 +130,6 @@
.account {
.func-margin-start(25px);
.text-one-liner;
&:hover {
border-bottom: none;
}
}
.switch-theme {
@ -189,11 +185,6 @@
height: 39px;
padding: 15px 20px;
display: block;
border-bottom: none;
&:hover {
border-bottom: none;
}
img {
display: inline-block;
@ -364,7 +355,6 @@
&:hover,
&.selected {
color: #e4e4e4;
font-weight: 500;
}
}
}
@ -548,6 +538,7 @@
@media @tablets, @phones {
width: 100%;
padding-bottom: 32%;
float: none;
margin-bottom: 20px;
}

View file

@ -7,11 +7,9 @@ html.home {
.logo {
a {
display: block;
border-bottom: none;
&:hover {
opacity: .8;
border-bottom: none;
}
}
@ -30,4 +28,4 @@ html.home {
main {
min-height: 400px;
}
}
}

View file

@ -17,11 +17,12 @@ a, .link {
text-decoration: none;
border-left: 2px solid transparent;
border-right: 2px solid transparent;
border-bottom: solid 1px transparent;
transition: 200ms;
display: inline-block;
cursor: pointer;
&:hover {
border-bottom: dotted 1px var(--config-color-link);
cursor: pointer;
transform: translateY(-2px);
}
&.disabled {
@ -29,11 +30,11 @@ a, .link {
}
&.disabled:hover {
border-bottom: none;
.link.link-animation-disabled();
}
&.tag:hover {
border-bottom: none;
.link.link-animation-disabled();
opacity: .9;
}
@ -42,6 +43,46 @@ a, .link {
}
}
a.link-animation-disabled,
.link.link-animation-disabled {
&:hover {
transform: translateY(0);
}
}
.func-link-return-animation(@pos) {
.link.link-animation-disabled();
& > i {
display: inline-block;
transition: 200ms;
}
&:hover > i {
transform: translateX(@pos);
}
}
@link-return-animation-offset: 2px;
.link-return-animation--start {
.func-link-return-animation(
if((@config-start = right),
@link-return-animation-offset,
-@link-return-animation-offset
)
);
}
.link-return-animation--end {
.func-link-return-animation(
if((@config-start = right),
-@link-return-animation-offset,
@link-return-animation-offset
)
);
}
b, strong {
font-weight: 500;
}
@ -196,4 +237,12 @@ small {
.icon-dot-3:before {
.func-rotate(90deg);
}
}
// fix icons vertical alignment
i[class^='icon-'], i[class*=' icon-']{
&:before {
display: inline;
line-height: unset;
}
}

View file

@ -411,4 +411,95 @@ trait AvatarsBase
return [];
}
public function testGetInitials()
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/avatars/initials', [
'x-appwrite-project' => $this->getProject()['$id'],
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('image/png; charset=UTF-8', $response['headers']['content-type']);
$this->assertNotEmpty($response['body']);
$response = $this->client->call(Client::METHOD_GET, '/avatars/initials', [
'x-appwrite-project' => $this->getProject()['$id'],
], [
'width' => 200,
'height' => 200,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('image/png; charset=UTF-8', $response['headers']['content-type']);
$this->assertNotEmpty($response['body']);
$response = $this->client->call(Client::METHOD_GET, '/avatars/initials', [
'x-appwrite-project' => $this->getProject()['$id'],
], [
'name' => 'W W',
'width' => 200,
'height' => 200,
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('image/png; charset=UTF-8', $response['headers']['content-type']);
$this->assertNotEmpty($response['body']);
$response = $this->client->call(Client::METHOD_GET, '/avatars/initials', [
'x-appwrite-project' => $this->getProject()['$id'],
], [
'name' => 'W W',
'width' => 200,
'height' => 200,
'color' => 'ffffff',
'background' => '000000',
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('image/png; charset=UTF-8', $response['headers']['content-type']);
$this->assertNotEmpty($response['body']);
/**
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_GET, '/avatars/initials', [
'x-appwrite-project' => $this->getProject()['$id'],
], [
'name' => 'W W',
'width' => 200000,
'height' => 200,
'color' => 'ffffff',
'background' => '000000',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/avatars/initials', [
'x-appwrite-project' => $this->getProject()['$id'],
], [
'name' => 'W W',
'width' => 200,
'height' => 200,
'color' => 'white',
'background' => '000000',
]);
$this->assertEquals(400, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/avatars/initials', [
'x-appwrite-project' => $this->getProject()['$id'],
], [
'name' => 'W W',
'width' => 200,
'height' => 200,
'color' => 'ffffff',
'background' => 'black',
]);
$this->assertEquals(400, $response['headers']['status-code']);
}
}