1
0
Fork 0
mirror of synced 2024-05-20 12:42:39 +12:00

Merge branch 'feat-database-indexing' into feat-usage-daemon

This commit is contained in:
Damodar Lohani 2021-08-19 11:12:49 +05:45
commit 0d47dea97f
83 changed files with 1487 additions and 904 deletions

View file

@ -41,6 +41,7 @@ script:
- docker-compose exec appwrite doctor
- docker-compose exec appwrite vars
- docker-compose exec appwrite test --debug
- docker-compose logs appwrite
deploy:
- provider: script

View file

@ -282,6 +282,17 @@ docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7
The Runtimes for all supported cloud functions (multicore builds) can be found at the [appwrite/php-runtimes](https://github.com/appwrite/php-runtimes) repository.
## Generate SDK
For generating a new console SDK follow the next steps:
1. Update the console spec file located at `app/config/specs/0.10.x.console.json` from the dynamic version located at `https://localhost/specs/swagger2?platform=console`
2. Generate a new SDK using the command `php app/cli.php sdks`
3. Change your working dir using `cd app/sdks/console-web`
4. Build the new SDK `npm run build`
5. Copy `iife/sdk.js` to `appwrite.js`
6. Go back to the root of the project `run npm run build`
## Debug
Appwrite uses [yasd](https://github.com/swoole/yasd) debugger, which can be made available during build of Appwrite. You can connect to the debugger using VS Code [PHP Debug](https://marketplace.visualstudio.com/items?itemName=felixfbecker.php-debug) extension or if you are in PHP Storm you don't need any plugin. Below are the settings required for remote debugger connection.

View file

@ -32,10 +32,6 @@ $admins = [
'users.write',
'collections.read',
'collections.write',
'attributes.read',
'attributes.write',
'indexes.read',
'indexes.write',
'platforms.read',
'platforms.write',
'keys.read',

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -43,7 +43,7 @@ App::post('/v1/account')
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->label('abuse-limit', 10)
->param('userId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, and underscore. Can\'t start with a leading underscore. Max length is 36 chars.')
->param('userId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('email', '', new Email(), 'User email.')
->param('password', '', new Password(), 'User password. Must be between 6 to 32 chars.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)

View file

@ -1,17 +1,21 @@
<?php
use Appwrite\Utopia\Response;
use Appwrite\Database\Validator\CustomId;
use Utopia\App;
use Utopia\Exception;
use Utopia\Audit\Audit;
use Utopia\Validator\Boolean;
use Utopia\Validator\FloatValidator;
use Utopia\Validator\Integer;
use Utopia\Validator\Numeric;
use Utopia\Validator\Range;
use Utopia\Validator\WhiteList;
use Utopia\Validator\Text;
use Utopia\Validator\ArrayList;
use Utopia\Validator\JSON;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\QueryValidator;
@ -19,11 +23,9 @@ use Utopia\Database\Validator\Queries as QueriesValidator;
use Utopia\Database\Validator\Structure;
use Utopia\Database\Validator\UID;
use Utopia\Database\Exception\Authorization as AuthorizationException;
use Utopia\Database\Exception\Duplicate as DuplicateException;
use Utopia\Database\Exception\Structure as StructureException;
use Appwrite\Utopia\Response;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use DeviceDetector\DeviceDetector;
$attributesCallback = function ($attribute, $response, $dbForExternal, $database, $audits, $usage) {
/** @var Utopia\Database\Document $document*/
@ -84,12 +86,13 @@ $attributesCallback = function ($attribute, $response, $dbForExternal, $database
}
}
$success = $dbForExternal->addAttributeInQueue($collectionId, $attributeId, $type, $size, $required, $default, $signed, $array, $format, $filters);
$dbForExternal->addAttributeInQueue($collectionId, $attributeId, $type, $size, $required, $default, $signed, $array, $format, $filters);
// Database->addAttributeInQueue() does not return a document
// So we need to create one for the response
//
// TODO@kodumbeats should $signed and $filters be part of the response model?
$attribute = new Document([
'$collection' => $collectionId,
'$id' => $attributeId,
@ -114,7 +117,7 @@ $attributesCallback = function ($attribute, $response, $dbForExternal, $database
$audits
->setParam('event', 'database.attributes.create')
->setParam('resource', 'database/attributes/'.$attribute->getId())
->setParam('resource', 'database/collection/'.$collection->getId())
->setParam('data', $attribute)
;
@ -134,7 +137,7 @@ App::post('/v1/database/collections')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_COLLECTION)
->param('collectionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, and underscore. Can\'t start with a leading underscore. Max length is 36 chars.')
->param('collectionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.')
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('write', null, new Permissions(), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
@ -247,6 +250,102 @@ App::get('/v1/database/collections/:collectionId')
$response->dynamic($collection, Response::MODEL_COLLECTION);
});
App::get('/v1/database/collections/:collectionId/logs')
->desc('List Collection Logs')
->groups(['api', 'database'])
->label('scope', 'collections.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'database')
->label('sdk.method', 'listCollectionLogs')
->label('sdk.description', '/docs/references/database/get-collection-logs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('collectionId', '', new UID(), 'Collection unique ID.')
->inject('response')
->inject('dbForInternal')
->inject('dbForExternal')
->inject('locale')
->inject('geodb')
->action(function ($collectionId, $response, $dbForInternal, $dbForExternal, $locale, $geodb) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForInternal */
/** @var Utopia\Database\Database $dbForExternal */
/** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */
$collection = $dbForExternal->getCollection($collectionId);
if ($collection->isEmpty()) {
throw new Exception('Collection not found', 404);
}
$audit = new Audit($dbForInternal);
$logs = $audit->getLogsByResource('database/collection/'.$collection->getId());
$output = [];
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$dd = new DeviceDetector($log['userAgent']);
$dd->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$dd->parse();
$os = $dd->getOs();
$osCode = (isset($os['short_name'])) ? $os['short_name'] : '';
$osName = (isset($os['name'])) ? $os['name'] : '';
$osVersion = (isset($os['version'])) ? $os['version'] : '';
$client = $dd->getClient();
$clientType = (isset($client['type'])) ? $client['type'] : '';
$clientCode = (isset($client['short_name'])) ? $client['short_name'] : '';
$clientName = (isset($client['name'])) ? $client['name'] : '';
$clientVersion = (isset($client['version'])) ? $client['version'] : '';
$clientEngine = (isset($client['engine'])) ? $client['engine'] : '';
$clientEngineVersion = (isset($client['engine_version'])) ? $client['engine_version'] : '';
$output[$i] = new Document([
'event' => $log['event'],
'userId' => $log['userId'],
'userEmail' => $log['data']['userEmail'] ?? null,
'userName' => $log['data']['userName'] ?? null,
'mode' => $log['data']['mode'] ?? null,
'ip' => $log['ip'],
'time' => $log['time'],
'osCode' => $osCode,
'osName' => $osName,
'osVersion' => $osVersion,
'clientType' => $clientType,
'clientCode' => $clientCode,
'clientName' => $clientName,
'clientVersion' => $clientVersion,
'clientEngine' => $clientEngine,
'clientEngineVersion' => $clientEngineVersion,
'deviceName' => $dd->getDeviceName(),
'deviceBrand' => $dd->getBrandName(),
'deviceModel' => $dd->getModel(),
]);
$record = $geodb->get($log['ip']);
if ($record) {
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
} else {
$output[$i]['countryCode'] = '--';
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
}
}
$response->dynamic(new Document(['logs' => $output]), Response::MODEL_LOG_LIST);
});
App::put('/v1/database/collections/:collectionId')
->desc('Update Collection')
->groups(['api', 'database'])
@ -298,7 +397,7 @@ App::put('/v1/database/collections/:collectionId')
$audits
->setParam('event', 'database.collections.update')
->setParam('resource', 'database/collections/'.$collection->getId())
->setParam('resource', 'database/collection/'.$collection->getId())
->setParam('data', $collection->getArrayCopy())
;
@ -346,7 +445,7 @@ App::delete('/v1/database/collections/:collectionId')
$audits
->setParam('event', 'database.collections.delete')
->setParam('resource', 'database/collections/'.$collection->getId())
->setParam('resource', 'database/collection/'.$collection->getId())
->setParam('data', $collection->getArrayCopy())
;
@ -357,7 +456,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
->desc('Create String Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('scope', 'attributes.write')
->label('scope', 'collections.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
->label('sdk.method', 'createStringAttribute')
@ -398,7 +497,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
->desc('Create Email Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('scope', 'attributes.write')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'createEmailAttribute')
@ -439,7 +538,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
->desc('Create IP Address Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('scope', 'attributes.write')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'createIpAttribute')
@ -480,7 +579,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
->desc('Create IP Address Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('scope', 'attributes.write')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'createUrlAttribute')
@ -522,7 +621,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
->desc('Create Integer Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('scope', 'attributes.write')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'createIntegerAttribute')
@ -570,7 +669,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
->desc('Create Float Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('scope', 'attributes.write')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'createFloatAttribute')
@ -617,7 +716,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
->desc('Create Boolean Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('scope', 'attributes.write')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.method', 'createBooleanAttribute')
@ -656,7 +755,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
App::get('/v1/database/collections/:collectionId/attributes')
->desc('List Attributes')
->groups(['api', 'database'])
->label('scope', 'attributes.read')
->label('scope', 'collections.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
->label('sdk.method', 'listAttributes')
@ -698,7 +797,7 @@ App::get('/v1/database/collections/:collectionId/attributes')
App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
->desc('Get Attribute')
->groups(['api', 'database'])
->label('scope', 'attributes.read')
->label('scope', 'collections.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
->label('sdk.method', 'getAttribute')
@ -743,7 +842,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:attributeId')
App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
->desc('Delete Attribute')
->groups(['api', 'database'])
->label('scope', 'attributes.write')
->label('scope', 'collections.write')
->label('event', 'database.attributes.delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
@ -802,7 +901,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:attributeId')
$audits
->setParam('event', 'database.attributes.delete')
->setParam('resource', 'database/attributes/'.$attribute->getId())
->setParam('resource', 'database/collection/'.$collection->getId())
->setParam('data', $attribute->getArrayCopy())
;
@ -813,7 +912,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
->desc('Create Index')
->groups(['api', 'database'])
->label('event', 'database.indexes.create')
->label('scope', 'indexes.write')
->label('scope', 'collections.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
->label('sdk.method', 'createIndex')
@ -868,7 +967,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
$lengths[$key] = ($attributeType === Database::VAR_STRING) ? $attributeSize : null;
}
$success = $dbForExternal->addIndexInQueue($collectionId, $indexId, $type, $attributes, $lengths, $orders);
$dbForExternal->addIndexInQueue($collectionId, $indexId, $type, $attributes, $lengths, $orders);
// Database->createIndex() does not return a document
// So we need to create one for the response
@ -892,7 +991,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
$audits
->setParam('event', 'database.indexes.create')
->setParam('resource', 'database/indexes/'.$index->getId())
->setParam('resource', 'database/collection/'.$collection->getId())
->setParam('data', $index->getArrayCopy())
;
@ -904,7 +1003,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
App::get('/v1/database/collections/:collectionId/indexes')
->desc('List Indexes')
->groups(['api', 'database'])
->label('scope', 'indexes.read')
->label('scope', 'collections.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
->label('sdk.method', 'listIndexes')
@ -946,7 +1045,7 @@ App::get('/v1/database/collections/:collectionId/indexes')
App::get('/v1/database/collections/:collectionId/indexes/:indexId')
->desc('Get Index')
->groups(['api', 'database'])
->label('scope', 'indexes.read')
->label('scope', 'collections.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
->label('sdk.method', 'getIndex')
@ -991,7 +1090,7 @@ App::get('/v1/database/collections/:collectionId/indexes/:indexId')
App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
->desc('Delete Index')
->groups(['api', 'database'])
->label('scope', 'indexes.write')
->label('scope', 'collections.write')
->label('event', 'database.indexes.delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
@ -1050,7 +1149,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:indexId')
$audits
->setParam('event', 'database.indexes.delete')
->setParam('resource', 'database/indexes/'.$index->getId())
->setParam('resource', 'database/collection/'.$collection->getId())
->setParam('data', $index->getArrayCopy())
;
@ -1069,7 +1168,7 @@ App::post('/v1/database/collections/:collectionId/documents')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_DOCUMENT)
->param('documentId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, and underscore. Can\'t start with a leading underscore. Max length is 36 chars.')
->param('documentId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('collectionId', null, new UID(), 'Collection unique ID. You can create a new collection with validation rules using the Database service [server integration](/docs/server/database#createCollection).')
->param('data', [], new JSON(), 'Document data as JSON object.')
->param('read', null, new Permissions(), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
@ -1109,7 +1208,8 @@ App::post('/v1/database/collections/:collectionId/documents')
try {
$document = $dbForExternal->createDocument($collectionId, new Document($data));
} catch (StructureException $exception) {
}
catch (StructureException $exception) {
throw new Exception($exception->getMessage(), 400);
}
@ -1184,16 +1284,14 @@ App::get('/v1/database/collections/:collectionId/documents')
}
}
$documents = $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null);
$usage
->setParam('database.documents.read', 1)
->setParam('collectionId', $collectionId)
;
$response->dynamic(new Document([
'sum' => \count($documents),
'documents' => $documents,
'sum' => $dbForExternal->count($collectionId, $queries, APP_LIMIT_COUNT),
'documents' => $dbForExternal->find($collectionId, $queries, $limit, $offset, $orderAttributes, $orderTypes, $afterDocument ?? null),
]), Response::MODEL_DOCUMENT_LIST);
});
@ -1295,9 +1393,11 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
try {
$document = $dbForExternal->updateDocument($collection->getId(), $document->getId(), new Document($data));
} catch (AuthorizationException $exception) {
}
catch (AuthorizationException $exception) {
throw new Exception('Unauthorized permissions', 401);
} catch (StructureException $exception) {
}
catch (StructureException $exception) {
throw new Exception('Bad structure. '.$exception->getMessage(), 400);
}
@ -1352,7 +1452,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
throw new Exception('No document found', 404);
}
$success = $dbForExternal->deleteDocument($collectionId, $documentId);
$dbForExternal->deleteDocument($collectionId, $documentId);
$usage
->setParam('database.documents.delete', 1)

View file

@ -39,7 +39,7 @@ App::post('/v1/functions')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FUNCTION)
->param('functionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, and underscore. Can\'t start with a leading underscore. Max length is 36 chars.')
->param('functionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.')
->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.')

View file

@ -42,7 +42,7 @@ App::post('/v1/projects')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, and underscore. Can\'t start with a leading underscore. Max length is 36 chars.')
->param('projectId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
->param('teamId', '', new UID(), 'Team unique ID.')
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
@ -80,7 +80,6 @@ App::post('/v1/projects')
$project = $dbForConsole->createDocument('projects', new Document([
'$id' => $projectId == 'unique()' ? $dbForConsole->getId() : $projectId,
'$collection' => 'projects',
'$read' => ['team:' . $teamId],
'$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'],
'name' => $name,

View file

@ -38,7 +38,7 @@ App::post('/v1/storage/files')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_FILE)
->param('fileId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, and underscore. Can\'t start with a leading underscore. Max length is 36 chars.')
->param('fileId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('file', [], new File(), 'Binary file.', false)
->param('read', null, new ArrayList(new Text(64)), 'An array of strings with read permissions. By default only the current user is granted with read permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)
->param('write', null, new ArrayList(new Text(64)), 'An array of strings with write permissions. By default only the current user is granted with write permissions. [learn more about permissions](/docs/permissions) and get a full list of available permissions.', true)

View file

@ -33,7 +33,7 @@ App::post('/v1/teams')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEAM)
->param('teamId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, and underscore. Can\'t start with a leading underscore. Max length is 36 chars.')
->param('teamId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', null, new Text(128), 'Team name. Max length: 128 chars.')
->param('roles', ['owner'], new ArrayList(new Key()), 'Array of strings. Use this param to set the roles in the team for the user who created it. The default role is **owner**. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Max length for each role is 32 chars.', true)
->inject('response')

View file

@ -30,7 +30,7 @@ App::post('/v1/users')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_USER)
->param('userId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, and underscore. Can\'t start with a leading underscore. Max length is 36 chars.')
->param('userId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string `unique()` to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('email', '', new Email(), 'User email.')
->param('password', '', new Password(), 'User password. Must be between 6 to 32 chars.')
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)

View file

@ -9,7 +9,7 @@ use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Storage;
App::init(function ($utopia, $request, $response, $project, $user, $register, $events, $audits, $usage, $deletes, $database, $dbForInternal) {
App::init(function ($utopia, $request, $response, $project, $user, $events, $audits, $usage, $deletes, $database, $dbForInternal, $mode) {
/** @var Utopia\App $utopia */
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
@ -89,6 +89,9 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
$audits
->setParam('projectId', $project->getId())
->setParam('userId', $user->getId())
->setParam('userEmail', $user->getAttribute('email'))
->setParam('userName', $user->getAttribute('name'))
->setParam('mode', $mode)
->setParam('event', '')
->setParam('resource', '')
->setParam('userAgent', $request->getUserAgent(''))
@ -114,7 +117,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
$database
->setParam('projectId', $project->getId())
;
}, ['utopia', 'request', 'response', 'project', 'user', 'register', 'events', 'audits', 'usage', 'deletes', 'database', 'dbForInternal'], 'api');
}, ['utopia', 'request', 'response', 'project', 'user', 'events', 'audits', 'usage', 'deletes', 'database', 'dbForInternal', 'mode'], 'api');
App::init(function ($utopia, $request, $project) {
/** @var Utopia\App $utopia */

View file

@ -4,9 +4,7 @@ use Utopia\App;
use Utopia\View;
use Utopia\Config\Config;
use Utopia\Domains\Domain;
use Utopia\Database\Database;
use Appwrite\Database\Validator\Authorization;
use Appwrite\Database\Validator\UID;
use Utopia\Database\Validator\UID;
use Utopia\Storage\Storage;
App::init(function ($layout) {
@ -212,25 +210,19 @@ App::get('/console/database/collection')
->param('id', '', new UID(), 'Collection unique ID.')
->inject('response')
->inject('layout')
->inject('dbForExternal')
->action(function ($id, $response, $layout, $dbForExternal) {
->action(function ($id, $response, $layout) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\View $layout */
/** @var Utopia\Database\Database $dbForExternal */
Authorization::disable();
$collection = $dbForExternal->getCollection($id);
Authorization::reset();
$logs = new View(__DIR__.'/../../views/console/comps/logs.phtml');
if ($collection->isEmpty()) {
throw new Exception('Collection not found', 404);
}
$logs
->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0))
;
$page = new View(__DIR__.'/../../views/console/database/collection.phtml');
$page
->setParam('collection', $collection)
;
$page->setParam('logs', $logs);
$layout
->setParam('title', APP_NAME.' - Database Collection')
@ -250,25 +242,14 @@ App::get('/console/database/document')
->label('scope', 'console')
->param('collection', '', new UID(), 'Collection unique ID.')
->inject('layout')
->inject('dbForExternal')
->action(function ($collection, $layout, $dbForExternal) {
->action(function ($collection, $layout) {
/** @var Utopia\View $layout */
/** @var Utopia\Database\Database $dbForExternal */
Authorization::disable();
$collection = $dbForExternal->getCollection($collection);
Authorization::reset();
if ($collection->isEmpty()) {
throw new Exception('Collection not found', 404);
}
$page = new View(__DIR__.'/../../views/console/database/document.phtml');
$searchFiles = new View(__DIR__.'/../../views/console/database/search/files.phtml');
$searchDocuments = new View(__DIR__.'/../../views/console/database/search/documents.phtml');
$page
->setParam('db', $dbForExternal)
->setParam('collection', $collection)
->setParam('searchFiles', $searchFiles)
->setParam('searchDocuments', $searchDocuments)

View file

@ -56,12 +56,12 @@ App::get('/')
;
if ('console' === $project->getId() || $project->isEmpty()) {
$whitlistRoot = App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled');
$whitelistRoot = App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled');
if($whitlistRoot !== 'disabled') {
$sum = $dbForConsole->count('users', [], APP_LIMIT_USERS);
if($whitelistRoot !== 'disabled') {
$count = $dbForConsole->count('users', [], 1);
if($sum !== 0) {
if($count !== 0) {
return $response->redirect('/auth/signin');
}
}

View file

@ -33,15 +33,15 @@ $http
])
;
$http->on('WorkerStart', function($serv, $workerId) {
Console::success('Worker '.++$workerId.' started succefully');
$http->on('WorkerStart', function($server, $workerId) {
Console::success('Worker '.++$workerId.' started successfully');
});
$http->on('BeforeReload', function($serv, $workerId) {
$http->on('BeforeReload', function($server, $workerId) {
Console::success('Starting reload...');
});
$http->on('AfterReload', function($serv, $workerId) {
$http->on('AfterReload', function($server, $workerId) {
Console::success('Reload completed...');
});
@ -138,7 +138,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
}
});
Console::success('Server started succefully (max payload is '.number_format($payloadSize).' bytes)');
Console::success('Server started successfully (max payload is '.number_format($payloadSize).' bytes)');
Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}");

View file

@ -305,7 +305,7 @@
<table class="vertical small">
<thead>
<tr>
<th width="140">Date</th>
<th width="120">Date</th>
<th width="175">Event</th>
<th>Client</th>
<th width="90">Location</th>

View file

@ -0,0 +1,76 @@
<?php
$interval = floor((int)$this->getParam('interval', 0) / 86400);
?>
<div
data-service="database.listCollectionLogs"
data-param-collection-id="{{router.params.id}}"
data-scope="sdk"
data-event="load"
data-name="project-collection-logs">
<div class="box margin-bottom">
<div data-ls-if="0 == {{project-collection-logs.logs.length}}">
<h3 class="margin-bottom-small text-bold">No Logs Found</h3>
<p class="margin-bottom-no">Logs are retained for <?php echo $this->escape($interval); ?> days.</p>
</div>
<table class="vertical small" data-ls-if="0 != {{project-collection-logs.logs.length}}">
<thead>
<tr>
<th width="120">Date</th>
<th width="180">Initiator</th>
<th>Event</th>
<th width="90">Location</th>
<th width="90">IP</th>
</tr>
</thead>
<tbody data-ls-loop="project-collection-logs.logs" data-ls-as="log" class="text-size-small">
<tr>
<td data-title="Date: "><span class="text-fade" data-ls-bind="{{log.time|dateTime}}"></span></td>
<td data-title="Initiator: ">
<span data-ls-if="{{log.userName|escape}} !== '' && {{log.mode}} === ''"><i class="icon-user"></i>&nbsp; <a data-ls-attrs="href=/console/users/user?id={{log.userId}}&project={{router.params.project}}" data-ls-bind="{{log.userName}}"></a></span>
<span data-ls-if="{{log.userName|escape}} === '' && {{log.userEmail}} !== '' && {{log.mode}} !== 'key'"><i class="icon-user"></i>&nbsp; Unknown</span>
<span data-ls-if="{{log.userName|escape}} === '' && {{log.userEmail}} === '' && {{log.mode}} !== 'key'"><i class="icon-user"></i>&nbsp; Anonymous User</span>
<span data-ls-if="{{log.mode}} === 'admin'"><i class="icon-user"></i>&nbsp; <span data-ls-bind="{{log.userName}} (Admin)"></span></span>
<span data-ls-if="{{log.mode}} === 'key'"> <i class="icon-key"></i>&nbsp; API Key</span>
</td>
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
<td data-title="Location: ">
<img onerror="this.onerror=null;this.src='/images/unknown.svg'" data-ls-attrs="src={{env.API}}/avatars/flags/{{log.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs inline margin-end-small" />
<span data-ls-bind="{{log.geo.countryName}}"></span>
</td>
<td data-title="IP: "><span data-ls-bind="{{log.ip}}"></span></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="pull-end text-align-center paging">
<form
data-service="database.listCollectionLogs"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-collection-logs"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-back data-offset="{{router.params.offset}}" data-sum="{{project-collection-logs.sum}}" class="margin-end-small round small" aria-label="Back"><i class="icon-left-open"></i></button>
</form>
<form
data-service="database.listCollectionLogs"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-collection-logs"
data-success="state"
data-success-param-state-keys="search,offset">
<button name="offset" data-paging-next data-offset="{{router.params.offset}}" data-sum="{{project-collection-logs.sum}}" class="margin-start-small round small" aria-label="Next"><i class="icon-right-open"></i></button>
</form>
</div>

View file

@ -1,14 +1,13 @@
<?php
$collection = $this->getParam('collection', []);
$rules = $collection->getAttribute('rules', []);
$maxCells = 10;
?>
$logs = $this->getParam('logs', null);
?>
<div
data-service="database.getCollection"
data-param-collection-id="{{router.params.id}}"
data-scope="sdk"
data-event="load,database.updateCollection"
data-event="load,database.updateCollection,database.createAttribute,database.deleteAttribute,database.createIndex,database.deleteIndex"
data-name="project-collection">
<div class="cover">
@ -39,28 +38,6 @@ $maxCells = 10;
<h2>Documents</h2>
<form class="box padding-small margin-bottom search"
data-service="database.listDocuments"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset=""
data-param-order-type="DESC"
data-scope="sdk"
data-name="project-documents"
data-success="state"
data-success-param-state-keys="search,offset">
<div class="row thin responsive">
<div class="col span-10">
<input name="search" id="searchDocuments" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
</div>
<div class="col span-2 desktops-only">
<button class="fill" title="Search" aria-label="Search"><i class="icon-search"></i></button>
</div>
</div>
</form>
<div
data-service="database.listDocuments"
data-event="load,database.createDocument,database.updateDocument,database.deleteDocument"
@ -68,64 +45,38 @@ $maxCells = 10;
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-offset="{{router.params.offset}}"
data-param-order-type="DESC"
data-param-order-types="DESC"
data-param-order-types-cast-to="array"
data-scope="sdk"
data-name="project-documents">
<div data-ls-if="0 == {{project-documents.sum}}" class="box margin-bottom">
<h3 class="margin-bottom-small text-bold">No Documents Found</h3>
<p class="margin-bottom-no">Create your first document to get started</p>
<p class="margin-bottom-no">Add your first document to get started</p>
</div>
<div data-ls-if="({{project-documents.sum}})">
<div data-ls-if="({{project-documents.sum}})" class="margin-top-negative">
<div class="margin-bottom-small margin-end-small text-align-end text-size-small"><span data-ls-bind="{{project-documents.sum}}"></span> documents found</div>
<div class="box margin-bottom y-scroll">
<table class="vertical">
<div class="box margin-bottom y-scroll text-size-small">
<table class="vertical borders">
<thead>
<tr>
<?php foreach ($rules as $i => $rule):
if ($i > $maxCells) {
break;
}
$label = (isset($rule['label'])) ? $rule['label'] : '';
?>
<th width="120"><?php echo $this->escape($label); ?></th>
<?php endforeach;?>
<tr data-ls-loop="project-collection.attributes" data-ls-as="attribute" data-ls-prefix="template-attribute-title-first" data-ls-limit="20">
<td style="width: 170px">
<i data-ls-attrs="class=pull-end icon-{{attribute.type}} text-size-xs"></i>
<span class="text-bold" data-ls-bind="{{attribute.$id}}"></span>
<span class="text-size-small" data-ls-if="{{attribute.array}}">[]</span>
</td>
</tr>
</thead>
<tbody data-ls-loop="project-documents.documents" data-ls-as="node">
<tr>
<?php foreach ($rules as $i => $rule):
if ($i > $maxCells) {
break;
}
$label = $rule['label'] ?? '';
$key = $rule['key'] ?? '';
$type = $rule['type'] ?? '';
$array = $rule['array'] ?? '';
?>
<td data-title="<?php echo $this->escape($label); ?>: " class="text-size-small text-height-small">
<a data-ls-attrs="href=/console/database/document?id={{node.$id}}&collection={{router.params.id}}&project={{router.params.project}}&buster={{project-collection.dateUpdated}}">
<?php if (!$array): ?>
<?php switch ($type):
case 'fileId': ?>
<img data-ls-if="{{node.<?php echo $this->escape($key); ?>}} != ''" src="" data-ls-attrs="src={{env.ENDPOINT}}/v1/storage/files/{{node.<?php echo $this->escape($key); ?>}}/preview?width=65&height=65&project={{router.params.project}}&mode=admin" class="avatar" width="30" height="30" loading="lazy" />
<?php break;?>
<?php case 'document': ?>
{...}
<?php break;?>
<?php default: ?>
<span data-ls-bind="{{node.<?php echo $this->escape($key); ?>}}" data-ls-attrs="title={{node.<?php echo $this->escape($key); ?>}}"></span>
<?php break;?>
<?php endswitch;?>
<?php else: ?>
[...]
<?php endif;?>
</a>
</td>
<?php endforeach;?>
<tr data-ls-loop="project-collection.attributes" data-ls-as="attribute" data-ls-prefix="template-attribute-body-first" data-ls-limit="20">
<td data-ls-attrs="data-title={{attribute.$id}}: ">
<!-- <a href="" target="_blank" rel="noopener" class="pull-end margin-start"><i class="icon-link-ext"></i></a> -->
<a data-ls-attrs="href=/console/database/document?id={{node.$id}}&collection={{router.params.id}}&project={{router.params.project}}"><span data-ls-bind="{{node|documentAttribute}}"></span></a>
<span data-ls-if="!{{node|documentAttribute}}" class="text-fade">n/a</span>
</td>
</tr>
</tbody>
</table>
@ -139,7 +90,8 @@ $maxCells = 10;
data-param-collection-id="{{router.params.id}}"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-param-order-types="DESC"
data-param-order-types-cast-to="array"
data-scope="sdk"
data-name="project-documents"
data-success="state"
@ -155,7 +107,8 @@ $maxCells = 10;
data-param-collection-id="{{router.params.id}}"
data-param-search="{{router.params.search}}"
data-param-limit="<?php echo APP_PAGING_LIMIT; ?>"
data-param-order-type="DESC"
data-param-order-types="DESC"
data-param-order-types-cast-to="array"
data-scope="sdk"
data-name="project-documents"
data-success="state"
@ -164,11 +117,270 @@ $maxCells = 10;
</form>
</div>
<a data-ls-if="{{project-collection.rules.length}} > 0" data-ls-attrs="href=/console/database/document?collection={{router.params.id}}&project={{router.params.project}}&buster={{project-collection.dateUpdated}}" class="button">
<a data-ls-if="0 < {{project-collection.attributes.length}}" data-ls-attrs="href=/console/database/document?collection={{router.params.id}}&project={{router.params.project}}&buster={{project-collection.dateUpdated}}" class="button">
Add Document
</a>
<button disabled data-ls-if="!{{project-collection.attributes.length}}">
Add Document
</button >
</div>
</li>
<li data-state="/console/database/collection/attributes?id={{router.params.id}}&project={{router.params.project}}">
<h2>Attributes</h2>
<div class="clear box margin-bottom">
<div data-ls-if="0 == {{project-collection.attributes.length}} && 0 == {{project-collection.attributesInQueue.length}}">
<h3 class="margin-bottom-small text-bold">No Attributes Found</h3>
<p class="margin-bottom-no">Add your first attribute to get started</p>
</div>
<table class="vertical" data-ls-if="0 < {{project-collection.attributes.length}} || 0 < {{project-collection.attributesInQueue.length}}">
<thead>
<tr>
<th width="30"></th>
<th width="80"></th>
<th width="130">Attribute ID</th>
<th width="100">Type</th>
<th width="180">Default</th>
<th></th>
<th width="80"></th>
</tr>
</thead>
<tbody data-ls-loop="project-collection.attributes" data-ls-as="attribute">
<tr>
<td>
<i data-ls-attrs="class=icon-{{attribute.type}}"></i>
</td>
<td data-title="Status">
<span class="text-size-small text-success">available&nbsp;</span>
</td>
<td data-title="Attribute ID: ">
<span class="text-size-small" data-ls-bind="{{attribute.$id}}"></span><span class="text-size-small" data-ls-if="{{attribute.size}}" data-ls-bind=" ({{attribute.size}})"></span>
</td>
<td data-title="Type:">
<span class="text-size-small" data-ls-bind="{{attribute.type}}"></span>
<span class="text-size-small" data-ls-if="{{attribute.array}}">[]</span>
</td>
<td data-title="Default:">
<span class="text-size-small" data-ls-bind="{{attribute.default}}" data-ls-attr="title={{attribute.default}}"></span>
<span class="text-fade text-size-small" data-ls-if="!({{attribute.default}})">n/a</span>
</td>
<td data-title="">
<span class="text-size-small text-danger" data-ls-if="{{attribute.required}}">required</span>
</td>
<td data-title="">
<form class="pull-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Collection Attribute"
data-service="database.deleteAttribute"
data-scope="sdk"
data-event="submit"
data-confirm="Are you sure you want to delete this attribute?"
data-success="alert,trigger"
data-success-param-alert-text="Deleted attribute successfully"
data-success-param-trigger-events="database.deleteAttribute"
data-failure="alert"
data-failure-param-alert-text="Failed to delete attribute"
data-failure-param-alert-classname="error">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<input type="hidden" name="collectionId" data-ls-bind="{{project-collection.$id}}" />
<input type="hidden" name="attributeId" data-ls-bind="{{attribute.$id}}" />
<button class="danger small">Delete</button>
</form>
</td>
</tr>
</tbody>
</table>
<hr data-ls-if="0 < {{project-collection.attributesInQueue.length}}" />
<table class="vertical" data-ls-if="0 < {{project-collection.attributesInQueue.length}}">
<tbody data-ls-loop="project-collection.attributesInQueue" data-ls-as="attribute">
<tr>
<td width="30">
<i data-ls-attrs="class=icon-{{attribute.type}}"></i>
</td>
<td width="80" data-title="Status">
<span class="text-size-small text-info">creating&nbsp;</span>
</td>
<td width="130" data-title="Attribute ID: ">
<span class="text-size-small" data-ls-bind="{{attribute.$id}}"></span><span class="text-size-small" data-ls-if="{{attribute.size}}" data-ls-bind=" ({{attribute.size}})"></span>
</td>
<td width="100" data-title="Type:">
<span class="text-size-small" data-ls-bind="{{attribute.type}}"></span>
<span class="text-size-small" data-ls-if="{{attribute.array}}">[]</span>
</td>
<td width="180" data-title="Default:">
<span class="text-size-small" data-ls-bind="{{attribute.default}}" data-ls-attr="title={{attribute.default}}"></span>
<span class="text-fade text-size-small" data-ls-if="!({{attribute.default}})">n/a</span>
</td>
<td data-title="">
<span class="text-size-small text-danger" data-ls-if="{{attribute.required}}">required</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="drop-list pull-start" data-ls-ui-open="" data-button-aria="Choose Platform" data-button-text="Add Attribute" data-button-class="button" data-blur="1">
<ul>
<li>
<div class="link new-attribute-string"><img src="/images/clients/web.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="String Attribute Logo" class="avatar xxs margin-end-small" loading="lazy" /> New String Attribute</div>
</li>
<li>
<div class="link new-attribute-integer"><img src="/images/clients/web.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="String Attribute Logo" class="avatar xxs margin-end-small" loading="lazy" /> New Integer Attribute</div>
</li>
<li>
<div class="link new-attribute-float"><img src="/images/clients/web.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="String Attribute Logo" class="avatar xxs margin-end-small" loading="lazy" /> New Float Attribute</div>
</li>
<li>
<div class="link new-attribute-boolean"><img src="/images/clients/web.png?v=<?php echo APP_CACHE_BUSTER; ?>" alt="String Attribute Logo" class="avatar xxs margin-end-small" loading="lazy" /> New Boolean Attribute</div>
</li>
</ul>
</div>
</li>
<li data-state="/console/database/collection/indexes?id={{router.params.id}}&project={{router.params.project}}">
<h2>Indexes</h2>
<div class="clear box margin-bottom">
<div data-ls-if="0 == {{project-collection.indexes.length}} && 0 == {{project-collection.indexesInQueue.length}}">
<h3 class="margin-bottom-small text-bold">No Indexes Found</h3>
<p class="margin-bottom-no">Add your first index to get started</p>
</div>
<table class="vertical multi-line" data-ls-if="0 < {{project-collection.indexes.length}} || 0 < {{project-collection.indexesInQueue.length}}">
<thead>
<tr>
<th width="30"></th>
<th width="80"></th>
<th width="130">Index ID</th>
<th width="100">Type</th>
<th width="180">Attributes</th>
<th></th>
<th width="80"></th>
</tr>
</thead>
<tbody data-ls-loop="project-collection.indexes" data-ls-as="index">
<tr>
<td>
<i class="icon-key"></i>
</td>
<td data-title="Status">
<span class="text-size-small text-success">available</span>
</td>
<td data-title="Index ID: ">
<span class="text-size-small" data-ls-bind="{{index.$id}}"></span><span class="text-size-small" data-ls-if="{{index.size}}" data-ls-bind="({{index.size}})"></span>
</td>
<td data-title="Type:">
<span class="text-size-small" data-ls-bind="{{index.type}}"></span>
<span class="text-size-small" data-ls-if="{{index.array}}">[]</span>
</td>
<td data-title="Attributes:">
<span class="text-size-small" data-ls-bind="{{index|indexAttributes}}"></span>
</td>
<td data-title="">
<span class="text-size-small text-danger" data-ls-if="{{index.required}}">required</span>
</td>
<td data-title="">
<form class="pull-end"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Collection Index"
data-service="database.deleteIndex"
data-scope="sdk"
data-event="submit"
data-confirm="Are you sure you want to delete this index?"
data-success="alert,trigger"
data-success-param-alert-text="Deleted index successfully"
data-success-param-trigger-events="database.deleteIndex"
data-failure="alert"
data-failure-param-alert-text="Failed to delete index"
data-failure-param-alert-classname="error">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<input type="hidden" name="collectionId" data-ls-bind="{{project-collection.$id}}" />
<input type="hidden" name="indexId" data-ls-bind="{{index.$id}}" />
<button class="danger small">Delete</button>
</form>
</td>
</tr>
</tbody>
</table>
<hr data-ls-if="0 < {{project-collection.indexesInQueue.length}}" />
<table class="vertical multi-line" data-ls-if="0 < {{project-collection.indexesInQueue.length}}">
<tbody data-ls-loop="project-collection.indexesInQueue" data-ls-as="index">
<tr>
<td width="30">
<i class="icon-key"></i>
</td>
<td width="80" data-title="Status">
<span class="text-size-small text-info">creating</span>
</td>
<td width="130" data-title="Index ID: ">
<span class="text-size-small" data-ls-bind="{{index.$id}}"></span><span class="text-size-small" data-ls-if="{{index.size}}" data-ls-bind="({{index.size}})"></span>
</td>
<td width="100" data-title="Type:">
<span class="text-size-small" data-ls-bind="{{index.type}}"></span>
<span class="text-size-small" data-ls-if="{{index.array}}">[]</span>
</td>
<td width="180" data-title="Attributes:">
<span class="text-size-small" data-ls-bind="{{index|indexAttributes}}"></span>
</td>
<td data-title="">
<span class="text-size-small text-danger" data-ls-if="{{index.required}}">required</span>
</td>
<td width="80" data-title="">
</td>
</tr>
</tbody>
</table>
</div>
<button class="new-index">Add Index</button>
</li>
<li data-state="/console/database/collection/activity?id={{router.params.id}}&project={{router.params.project}}">
<h2>Activity <span class="badge" data-ls-bind="{{project-collection-logs.logs.length}}"></span></h2>
<?php echo $logs->render(); ?>
</li>
<li data-state="/console/database/collection/usage?id={{router.params.id}}&project={{router.params.project}}">
<h2>Usage</h2>
</li>
<li data-state="/console/database/collection/settings?id={{router.params.id}}&project={{router.params.project}}">
<h2>Settings</h2>
@ -184,10 +396,9 @@ $maxCells = 10;
data-scope="sdk"
data-event="submit"
data-param-collection-id="{{router.params.id}}"
data-success="alert,trigger,redirect"
data-success="alert,trigger"
data-success-param-alert-text="Updated collection successfully"
data-success-param-trigger-events="database.updateCollection"
data-success-param-redirect-url="/console/database/collection/settings?id={{serviceData.$id}}&project={{router.params.project}}"
data-failure="alert"
data-failure-param-alert-text="Failed to update collection"
data-failure-param-alert-classname="error">
@ -198,133 +409,38 @@ $maxCells = 10;
<label for="collection-name">Name</label>
<input name="name" id="collection-name" type="text" autocomplete="off" data-ls-bind="{{project-collection.name}}" data-forms-text-direction required placeholder="Collection Name" maxlength="128" />
<h3 class="margin-bottom-small">Rules</h3>
<div data-ls-if="(!{{project-collection.rules.length}})">
<hr class="margin-bottom-no margin-top-no" />
<div class="margin-bottom-xl margin-top-xl margin-end margin-start text-align-center">
<h4 class="text-fade text-size-small">No attribute rules added yet.</h4>
<label class="margin-bottom-small">Permissions</label>
<p class="text-fade text-size-small">Choose the permissions model for this collection.</p>
<hr class="margin-top-small" />
<div class="row">
<div class="col span-1"><input type="radio" class="margin-top-no" /></div>
<div class="col span-11">
<b>Document Level</b>
<p class="text-fade margin-top-tiny">Bla bla bla bla bla Bla blabla bla Bla blabla bla Bla blabla bla Bla blaBla bla bla Bla bla bla Bla bla bla Bla bla bla Bla bla bla Bla bla bla Bla bla bla Bla bla bla</p>
</div>
</div>
<input type="hidden" id="rulesInit" name="rules" data-cast-to="array-empty">
<div class="row">
<div class="col span-1"><input type="radio" class="margin-top-tiny" /></div>
<div class="col span-11">
<b>Collection Level</b>
<p class="text-fade margin-top-tiny">Bla bla bla Bla blabla bla Bla blabla bla Bla blabla bla Bla bla bla Bla bla bla Bla bla bla Bla bla bla Bla bla bla Bla bla bla Bla</p>
<div data-ls-if="({{project-collection.rules.length}})">
<ul data-ls-loop="project-collection.rules" data-ls-as="rule" class="sortable">
<li data-forms-remove data-forms-move-up data-forms-move-down>
<div class="toggle list sorts" data-ls-ui-open>
<i class="icon-up-open pull-end margin-top-tiny"></i>
<i class="icon-down-open pull-end margin-top-tiny"></i>
<h4 class="margin-bottom">
<div class="pull-start margin-end-large margin-bottom-small">
<button type="button" disabled class="margin-bottom strip round" data-move-down><i class="icon-down-dir"></i></button>
<button type="button" class="margin-bottom strip round" data-move-down><i class="icon-down-dir"></i></button>
<button type="button" class="margin-bottom strip round" data-move-up><i class="icon-up-dir"></i></button>
<button type="button" disabled class="margin-bottom strip round" data-move-up><i class="icon-up-dir"></i></button>
</div>
<span data-ls-bind="{{rule.label}}"></span>
<span data-ls-if="({{rule.array}}.toString() == 'false')">
<span class="text-fade text-size-small" data-ls-bind="&nbsp;{{rule.type}}&nbsp;"></span>
</span>
<span data-ls-if="({{rule.array}}.toString() == 'true')">
<span class="text-size-small text-fade" data-ls-bind="&nbsp;{{rule.type}}[]&nbsp;"></span>
</span>
<div data-ls-if="{{rule.required}}.toString() == 'true'">
<span class="text-size-xs text-danger text-fade">required</span>
</div>
<div data-ls-if="({{rule.required}}.toString() == 'false')">
<span class="text-size-xs text-fade">optional</span>
</div>
</h4>
<hr class="margin-top-no fade" />
<fieldset data-ls-attrs="name=rules" data-cast-to="array">
<input name="$id" type="hidden" data-ls-bind="{{rule.$id}}" />
<input name="$collection" type="hidden" data-ls-bind="{{rule.$collection}}" />
<div class="row thin">
<div class="col span-6">
<label data-ls-attrs="for=rule-label-{{rule.$id}}">Label
<span class="tooltip" data-tooltip="Attribute internal display name"><i class="icon-info-circled"></i></span>
</label>
<input name="label" type="text" data-ls-bind="{{rule.label}}" />
</div>
<div class="col span-6">
<label data-ls-attrs="for=rule-key-{{rule.$id}}">Key
<span class="tooltip small" data-tooltip="Attribute key name. Used as the document JSON key in the Database API"><i class="icon-info-circled"></i></span>
</label>
<div class="input-copy">
<input data-forms-copy name="key" type="text" data-ls-bind="{{rule.key}}" maxlength="32" pattern="^(\d|\w)+$" title="No spaces or special charts allowed" />
</div>
</div>
</div>
<label data-ls-attrs="for=rule-type-{{rule.$id}}">Rule Type</label>
<select data-ls-attrs="id=rule-type-{{rule.$id}}" name="type" required data-ls-bind="{{rule.type}}">
<optgroup label="General">
<option value="text">Text</option>
<option value="numeric">Numeric</option>
<option value="boolean">Boolean</option>
<option value="wildcard">Wildcard (*)</option>
</optgroup>
<!-- <optgroup label="Links">
<option value="fileId">File ID</option>
<option value="documentId">Document ID</option>
</optgroup> -->
<optgroup label="Advanced">
<option value="email">Email</option>
<option value="url">URL</option>
<option value="ip">IP</option>
<option value="markdown">Markdown</option>
<option value="document">Document (Embeded)</option>
</optgroup>
</select>
<div class="margin-bottom">
<input name="required" type="hidden" data-forms-switch data-ls-bind="{{rule.required}}" data-cast-to="boolean" /> &nbsp; Required <span class="tooltip" data-tooltip="Mark whether this is a required attribute"><i class="icon-info-circled"></i></span>
</div>
<div class="margin-bottom">
<input name="array" type="hidden" data-forms-switch data-ls-bind="{{rule.array}}" data-cast-to="boolean" /> &nbsp; Array <span class="tooltip" data-tooltip="Mark whether this attribute should act as an array"><i class="icon-info-circled"></i></span>
</div>
<div data-ls-template="template-validation-{{rule.type}}" data-type="script" class="margin-bottom"></div>
</fieldset>
<button type="button" data-ls-ui-trigger="splice-rule-{{$index}}" class="reverse danger margin-bottom">Remove</button>
<!-- <button type="button" data-remove="" class="reverse danger margin-bottom">Remove</button> -->
</div>
</li>
</ul>
<label for="collection-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
<input type="hidden" id="collection-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-collection.$permissions.read}}" placeholder="User ID, Team ID or Role" />
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add * for wildcard access</div>
<label for="collection-write">Write Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
<input type="hidden" id="collection-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-collection.$permissions.write}}" placeholder="User ID, Team ID or Role" />
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add * for wildcard access</div>
</div>
</div>
<hr class="margin-bottom-no margin-top-no" />
<button type="button" data-ls-ui-trigger="add-rule" class="reverse margin-top"><i class="icon-plus"></i>Add</button>
<div class="toggle margin-bottom margin-top" data-ls-ui-open data-button-aria="Open Permissions">
<i class="icon-plus pull-end margin-top-tiny"></i>
<i class="icon-minus pull-end margin-top-tiny"></i>
<h3 class="margin-bottom-large">Permissions</h3>
<label for="collection-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
<input type="hidden" id="collection-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-collection.$permissions.read}}" placeholder="User ID, Team ID or Role" />
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
<label for="collection-write">Write Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
<input type="hidden" id="collection-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-collection.$permissions.write}}" placeholder="User ID, Team ID or Role" />
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
</div>
<hr class="margin-top-no" />
<button>Update</button>
</form>
@ -364,172 +480,199 @@ $maxCells = 10;
</form>
</div>
</div>
<ul data-ls-loop="project-collection.rules" data-ls-as="rule" class="sortable">
<li data-forms-remove data-forms-move-up data-forms-move-down>
<form
data-analytics
data-analytics-event="splice-rule-{{$index}}"
data-analytics-category="console"
data-analytics-label="Spliced Collection Rule"
data-service="container.path"
data-event="splice-rule-{{$index}}"
data-scope="window.ls"
data-success="reset">
<input type="hidden" name="path" value="project-collection.rules" />
<input type="hidden" name="type" value="splice" />
<input type="hidden" name="value" data-ls-bind="{{$index}}" />
</form>
</li>
</ul>
<div data-ui-modal class="box modal close" data-button-alias="none" data-open-event="add-rule">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Add Rule</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Added Collection Rule"
data-service="container.path"
data-event="submit"
data-scope="window.ls"
data-success="reset">
<input type="hidden" name="path" value="project-collection.rules" />
<input type="hidden" name="type" value="append" />
<fieldset name="value" data-cast-to="object">
<input name="$id" type="hidden" value="" />
<input name="$collection" type="hidden" value="rules" />
<div class="row thin">
<div class="col span-6">
<label for="rule-label-new">Label
<span class="tooltip" data-tooltip="Attribute internal display name"><i class="icon-info-circled"></i></span>
</label>
<input name="label" type="text" required />
</div>
<div class="col span-6">
<label for="rule-key-new">Key
<span class="tooltip small" data-tooltip="Attribute key name. Used as the document JSON key in the Database API"><i class="icon-info-circled"></i></span>
</label>
<div class="input-copy">
<input data-forms-copy name="key" type="text" required maxlength="32" pattern="^(\d|\w)+$" title="No spaces or special charts allowed" />
</div>
</div>
</div>
<label for="rule-type-new">Rule Type</label>
<select name="type" required>
<optgroup label="General">
<option value="text">Text</option>
<option value="numeric">Numeric</option>
<option value="boolean">Boolean</option>
<option value="wildcard">Wildcard (*)</option>
</optgroup>
<!-- <optgroup label="Links">
<option value="fileId">File ID</option>
<option value="documentId">Document ID</option>
</optgroup> -->
<optgroup label="Advanced">
<option value="email">Email</option>
<option value="url">URL</option>
<option value="ip">IP</option>
<option value="markdown">Markdown</option>
<option value="document">Document (Embeded)</option>
</optgroup>
</select>
<input name="default" type="hidden" value="" />
<input name="required" type="hidden" value="false" data-cast-to="boolean" />
<input name="array" type="hidden" value="false" data-cast-to="boolean" />
</fieldset>
<hr class="margin-top-no" />
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>
</li>
</ul>
</div>
<div data-ui-modal class="modal box close sticky-footer" data-button-alias=".new-attribute-string">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Add String Attribute</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Collection Attribute (string)"
data-service="database.createStringAttribute"
data-scope="sdk"
data-event="submit"
data-success="alert,trigger,reset"
data-success-param-alert-text="Created new attribute successfully"
data-success-param-trigger-events="database.createAttribute"
data-failure="alert"
data-failure-param-alert-text="Failed to create attribute"
data-failure-param-alert-classname="error">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
<label for="string-attributeId">Attribute ID</label>
<input type="text" class="full-width" name="attributeId" required autocomplete="off" maxlength="128" />
<label for="string-length">Size</label>
<input id="string-length" name="size" type="number" class="margin-bottom" autocomplete="off" required value="255" data-cast-to="integer" />
<div class="margin-bottom">
<input name="required" type="hidden" data-forms-switch data-cast-to="boolean" /> &nbsp; Required <span class="tooltip" data-tooltip="Mark whether this is a required attribute"><i class="icon-info-circled"></i></span>
</div>
<div class="margin-bottom">
<input name="array" type="hidden" data-forms-switch data-cast-to="boolean" /> &nbsp; Array <span class="tooltip" data-tooltip="Mark whether this attribute should act as an array"><i class="icon-info-circled"></i></span>
</div>
<label for="string-default">Default Value</label>
<input id="string-default" name="default" type="text" class="margin-bottom-large" autocomplete="off">
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Back</button>
</footer>
</form>
</div>
<div data-ui-modal class="modal box close sticky-footer" data-button-alias=".new-attribute-integer">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Add Integer Attribute</h1>
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Collection Attribute (integer)"
data-service="database.createIntegerAttribute"
data-scope="sdk"
data-event="submit"
data-success="alert,trigger,reset"
data-success-param-alert-text="Created new attribute successfully"
data-success-param-trigger-events="database.createAttribute"
data-failure="alert"
data-failure-param-alert-text="Failed to create attribute"
data-failure-param-alert-classname="error">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
<label for="integer-attributeId">Attribute ID</label>
<input type="text" class="full-width" name="attributeId" required autocomplete="off" maxlength="128" />
<div class="margin-bottom">
<input name="required" type="hidden" data-forms-switch data-cast-to="boolean" /> &nbsp; Required <span class="tooltip" data-tooltip="Mark whether this is a required attribute"><i class="icon-info-circled"></i></span>
</div>
<div class="margin-bottom">
<input name="array" type="hidden" data-forms-switch data-cast-to="boolean" /> &nbsp; Array <span class="tooltip" data-tooltip="Mark whether this attribute should act as an array"><i class="icon-info-circled"></i></span>
</div>
<div class="row responsive thin">
<div class="col span-6 margin-bottom-small">
<label for="integer-min">Min</label>
<input type="text" class="full-width" name="number" value="0" required autocomplete="off" />
</div>
<div class="col span-6 margin-bottom-small">
<label for="integer-min">Max</label>
<input type="text" class="full-width" name="number" value="0" required autocomplete="off" />
</div>
</div>
<label for="integer-default">Default Value</label>
<input id="integer-default" name="default" type="number" value="0" class="margin-bottom-large" autocomplete="off">
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Back</button>
</footer>
</form>
</div>
<div data-ui-modal class="modal box close sticky-footer" data-button-alias=".new-index">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h1>Add Index</h1>
<div data-ls-if="0 == {{project-collection.attributes.length}}">
<div class="info margin-top margin-bottom">
<h2>No Attributes Found</h2>
<p>Please add your first attribute before adding your first index.</p>
</div>
<button data-ui-modal-close="" type="button" class="reverse">Back</button>
</div>
<form data-ls-if="0 < {{project-collection.attributes.length}}"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Create Collection Index"
data-service="database.createIndex"
data-scope="sdk"
data-event="submit"
data-success="alert,trigger,reset"
data-success-param-alert-text="Created new index successfully"
data-success-param-trigger-events="database.createIndex"
data-failure="alert"
data-failure-param-alert-text="Failed to create index"
data-failure-param-alert-classname="error">
<input type="hidden" name="projectId" data-ls-bind="{{router.params.project}}" />
<input type="hidden" name="collectionId" data-ls-bind="{{router.params.id}}" />
<label for="index-indexId">Index ID</label>
<input id="index-indexId" type="text" class="full-width" name="indexId" required autocomplete="off" maxlength="128" />
<label for="index-type">Type</label>
<select id="index-type" name="type">
<option value="key">Key</option>
<option value="unique">Unique</option>
<option value="fulltext">Fulltext</option>
</select>
<label>Attributes</label>
<div id="attributes-section"></div>
<div class="margin-bottom">
<div data-forms-clone="" data-label="Add Attribute" data-target="attributes-section" data-first="1">
<div class="row responsive thin margin-bottom-tiny">
<div class="col span-7 margin-bottom-small">
<select data-duplications data-ls-attrs="name=attributes" data-ls-loop="project-collection.attributes" data-ls-as="option" data-cast-to="array" class="margin-bottom-no">
<option data-ls-attrs="value={{option.$id}}" data-ls-bind="{{option.$id}}"></option>
</select>
</div>
<div class="col span-4 margin-bottom-small">
<select name="orders" data-cast-to="array" class="margin-bottom-no">
<option value="ASC">ASC</option>
<option value="DESC">DESC</option>
</select>
</div>
<div class="col span-1 margin-bottom-small">
<button type="button" data-remove class="dark danger small round pull-end" style="margin-top: 10px;"><i class="icon-cancel"></i></button>
</div>
</div>
</div>
</div>
<footer>
<button type="submit">Create</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Back</button>
</footer>
</form>
</div>
</div>
<script type="text/html" id="template-validation-text">
<div class="margin-bottom">
<label data-ls-attrs="for=rule-default-{{rule.$id}}">Default Value</label>
<input name="default" type="text" data-ls-bind="{{rule.default}}" data-forms-text-direction />
</div>
</script>
<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="numeric" placeholder="0" step="any" />
</div>
</script>
<script type="text/html" id="template-validation-boolean">
<div class="margin-bottom">
<label data-ls-attrs="for=rule-default-{{rule.$id}}">Default Value</label>
<input name="default" data-ls-bind="{{rule.default}}" type="hidden" data-forms-switch data-cast-to="boolean" />
</div>
</script>
<script type="text/html" id="template-validation-text">
<div class="margin-bottom">
<label data-ls-attrs="for=rule-default-{{rule.$id}}">Default Value</label>
<input name="default" type="text" data-ls-bind="{{rule.default}}" data-forms-text-direction />
</div>
</script>
<script type="text/html" id="template-validation-documentId">
<div data-ls-template="template-validation-document-array-false" data-type="script"></div>
<div class="margin-bottom text-align-center margin-top-xl margin-bottom-large" data-ls-if="{{project-collections.sum}} == 1">
No Collections Found.
</div>
</script>
<script type="text/html" id="template-validation-document">
<div data-ls-template="template-validation-document-array-{{rule.array}}" data-type="script"></div>
<div class="margin-bottom text-align-center margin-top-xl margin-bottom-large" data-ls-if="{{project-collections.sum}} == 1">
No Collections Found.
</div>
</script>
<script type="text/html" id="template-validation-document-array-true">
<label data-ls-attrs="for=rule-list-{{rule.$id}}" class="margin-bottom">Allowed Collections</label>
<div data-ls-loop="project-collections.collections" data-ls-as="project" data-ls-key="$index2" class="tiles cell-3 margin-bottom-negative">
<div class="margin-bottom" data-ls-if="{{project.$id}} != {{router.params.id}}">
<input type="checkbox" name="list" data-ls-attrs="value={{project.$id}},id={{project.$id}}" data-ls-bind="{{rule.list}}" /> <label data-ls-attrs="for={{project.$id}}" data-ls-bind="{{project.name}}"></label>
</div>
</div>
</script>
<script type="text/html" id="template-validation-document-array-false">
<label data-ls-attrs="for=rule-list-{{rule.$id}}" class="margin-bottom">Allowed Collection</label>
<div data-ls-loop="project-collections.collections" data-ls-as="project" data-ls-key="$index2" class="tiles cell-3 margin-bottom-negative">
<div class="margin-bottom" data-ls-if="{{project.$id}} != {{router.params.id}}">
<input type="radio" data-ls-attrs="value={{project.$id}},id=[{{rule.$id}}].{{project.$id}},name=[{{rule.$id}}].list" data-ls-bind="{{rule.list|firstElement}}" data-cast-to="array" required />
<label data-ls-attrs="for={{project.$id}}"data-ls-bind="{{project.name}}"></label>
</div>
</div>
</script>
<div class="margin-top"
data-service="database.listCollections"
data-event="load,database.createCollection,database.updateCollection,database.deleteCollection"
data-scope="sdk"
data-name="project-collections">
</div>
<script type="text/html" id="template-attribute-title-first">
<i data-ls-attrs="class=pull-end icon-key text-size-xs"></i>
<span class="text-bold">$id</span>
</script>
<script type="text/html" id="template-attribute-body-first">
<span class="text-fade" data-ls-bind="{{node.$id}}" data-general-copy data-class="icon-docs note copy text-fade pull-end"></span>
</script>

View file

@ -1,252 +1,84 @@
<?php
use Appwrite\Database\Database;
use Appwrite\Database\Validator\Authorization;
use Utopia\View;
$collection = $this->getParam('collection', null);
$searchFiles = $this->getParam('searchFiles', null);
$searchDocuments = $this->getParam('searchDocuments', null);
$name = $collection->getAttribute('name', '');
$db = $this->getParam('db', null);
$rules = $collection->getAttribute('rules', []);
$namespace = 'project-document';
$collections = [];
?>
<?php echo $searchFiles->render(); ?>
<?php foreach ($rules as $rule): // Form to append child document
$key = $rule['key'] ?? '';
$label = $rule['label'] ?? '';
$type = $rule['type'] ?? '';
$list = $rule['list'] ?? [];
?>
<?php foreach ($list as $item):
if ($item === $collection->getId()) {
continue; // Do not allow rec recrusion
}
if (!isset($collections[$item])) {
Authorization::disable(); //TODO Try and avoid calling the DB from the template
$collections[$item] = $db->getDocument($item, false);
Authorization::reset();
}
$childCollection = $collections[$item];
if (!$childCollection->getId() || $childCollection->getCollection() !== Database::SYSTEM_COLLECTION_COLLECTIONS) {
continue;
}
$searchDocuments
->setParam('collection', $childCollection)
;
echo $searchDocuments->render();?>
<?php endforeach;?>
<?php endforeach;?>
<?php foreach ($rules as $rule): // Form to append child document
$key = $rule['key'] ?? '';
$label = $rule['label'] ?? '';
$type = $rule['type'] ?? '';
$list = $rule['list'] ?? [];
$array = $rule['array'] ?? false;
?>
<?php if ($type !== 'document' && $array): ?>
<form class="margin-bottom-no"
data-service="container.path"
data-event="collection-child-<?php echo $this->escape($namespace . '.' . $key); ?>"
data-scope="window.ls"
data-success="reset">
<input type="hidden" name="path" value="<?php echo $this->escape($namespace . '.' . $key); ?>" />
<input type="hidden" name="type" value="append" />
<input type="hidden" name="value" value="" />
</form>
<?php endif;?>
<?php if ($type !== 'document'): ?>
<?php continue;?>
<?php endif;?>
<?php foreach ($list as $item):
if ($item === $collection->getId()) {
continue; // Do not allow rec recrusion
}
if (!isset($collections[$item])) {
Authorization::disable(); //TODO Try and avoid calling the DB from the template
$collections[$item] = $db->getDocument($item, false);
Authorization::reset();
}
$childCollection = $collections[$item];
if (!$childCollection->getId() || $childCollection->getCollection() !== Database::SYSTEM_COLLECTION_COLLECTIONS) {
continue;
}
?>
<form class="margin-bottom-no"
data-service="container.path"
data-event="collection-child-<?php echo $this->escape($namespace . '.' . $key); ?>-<?php echo $this->escape($childCollection->getId()); ?>"
data-scope="window.ls"
data-success="reset">
<input type="hidden" name="path" value="<?php echo $this->escape($namespace . '.' . $key); ?>" />
<input type="hidden" name="type" value="append" />
<fieldset name="value" data-cast-to="object">
<input name="$id" type="hidden" value="" />
<input name="$collection" type="hidden" value="<?php echo $this->escape($childCollection->getId()); ?>" />
</fieldset>
</form>
<script type="text/html" id="collection-<?php echo ($array) ? 'array-' : ''; ?><?php echo $this->escape($childCollection->getId()); ?>">
<?php
$comp = new View(__DIR__ . '/form.phtml');
$comp
->setParam('collection', $childCollection)
->setParam('namespace', ($array) ? 'node' : $namespace . '.' . $key)
->setParam('key', $key)
->setParam('array', $array)
->setParam('parent', 0)
;
echo $comp->render();
?>
</script>
<?php endforeach;?>
<?php endforeach;?>
<?php foreach ($rules as $rule): // Form to remove array $index key
$key = $rule['key'] ?? '';
$label = $rule['label'] ?? '';
$type = $rule['type'] ?? '';
$list = $rule['list'] ?? false;
$array = $rule['array'] ?? false;
if (!$array) {
continue;
}
?>
<ul data-ls-loop="<?php echo $this->escape($namespace . '.' . $key); ?>" data-ls-as="xxx">
<li>
<form
data-service="container.path"
data-event="splice-<?php echo $this->escape($namespace . '.' . $key); ?>-{{$index}}"
data-scope="window.ls"
data-success="reset">
<input type="hidden" name="path" value="<?php echo $this->escape($namespace . '.' . $key); ?>" />
<input type="hidden" name="type" value="splice" />
<input type="hidden" name="value" data-ls-bind="{{$index}}" />
</form>
</li>
</ul>
<?php endforeach;?>
<div
data-service="database.getDocument"
data-service="database.getCollection"
data-param-collection-id="{{router.params.collection}}"
data-param-document-id="{{router.params.id}}"
data-scope="sdk"
data-event="load,database.updateDocument"
data-name="project-document"
data-success="default">
data-name="project-collection">
<div
data-service="database.getDocument"
data-param-collection-id="{{router.params.collection}}"
data-param-document-id="{{router.params.id}}"
data-scope="sdk"
data-event="load"
data-name="project-document"
data-success="default">
<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 link-return-animation--start"><i class="icon-left-open"></i> <?php echo $this->escape($name); ?></a>
<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 link-return-animation--start"><i class="icon-left-open"></i> <span data-ls-bind="{{project-collection.name}}"></span></a>
<br />
<br />
<span data-ls-if="({{project-document.$id}})" data-ls-bind="Document">&nbsp;&nbsp;</span>
<span data-ls-if="(!{{project-document.$id}})" data-ls-bind="Document">&nbsp;&nbsp;</span>
</h1>
</div>
<div data-ui-modal class="modal width-large box close" data-button-hide="on" data-open-event="open-json">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h2>JSON View</h2>
<div class="margin-bottom">
<input type="hidden" data-ls-bind="{{project-document}}" data-forms-code />
<span data-ls-if="({{project-document.$id}})" data-ls-bind="Document">&nbsp;&nbsp;</span>
<span data-ls-if="(!{{project-document.$id}})" data-ls-bind="Document">&nbsp;&nbsp;</span>
</h1>
</div>
<button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</div>
<div data-ui-modal class="modal width-large box close" data-button-hide="on" data-open-event="open-json">
<button type="button" class="close pull-end" data-ui-modal-close=""><i class="icon-cancel"></i></button>
<h2>JSON View</h2>
<div class="zone xl margin-bottom-no">
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
<li data-state="/console/database/document?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
<h2>Update</h2>
<div class="margin-bottom">
<input type="hidden" data-ls-bind="{{project-document}}" data-forms-code />
</div>
<div class="row responsive margin-top-negative">
<div class="col span-8 margin-bottom">
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Database Document"
data-service="{{|documentAction}}"
data-name="project-document"
data-scope="sdk"
data-event="submit"
data-success="alert,trigger{{|documentSuccess}}"
data-success-param-alert-text="Updated document successfully"
data-success-param-trigger-events="database.updateDocument"
data-success-param-redirect-url="/console/database/document?id={{serviceData.$id}}&collection={{router.params.collection}}&project={{router.params.project}}"
data-failure="alert"
data-failure-param-alert-text="Failed to update document"
data-failure-param-alert-classname="error">
<button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</div>
<label>&nbsp;</label>
<div class="zone xl margin-bottom-no">
<ul class="phases clear" data-ui-phases data-selected="{{router.params.tab}}">
<li data-state="/console/database/document?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
<h2>Overview</h2>
<div class="box">
<div class="row responsive margin-top-negative">
<div class="col span-8 margin-bottom">
<form
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Database Document"
data-service="{{|documentAction}}"
data-name="project-document"
data-scope="sdk"
data-event="submit"
data-success="alert,trigger"
data-success-param-alert-text="Updated document successfully"
data-success-param-trigger-events="database.updateDocument"
data-failure="alert"
data-failure-param-alert-text="Failed to update document"
data-failure-param-alert-classname="error">
<input type="hidden" name="collectionId" data-ls-bind="{{project-collection.$id}}" />
<input type="hidden" name="documentId" data-ls-bind="{{project-document.$id}}" />
<!-- <div class="document-nav" data-forms-nav>
<ul class="text-align-end margin-end-small">
<?php foreach ($rules as $rule): // Form to append child document
$key = $rule['key'] ?? '';
$label = $rule['label'] ?? '';
$type = $rule['type'] ?? '';
$list = $rule['list'] ?? [];
?>
<li class="text-size-small">
<span class="link text-fade" data-forms-nav-link="<?php echo $this->escape($key); ?>"><?php echo $this->escape($label); ?></span>
</li>
<?php endforeach;?>
</ul>
</div> -->
<label>&nbsp;</label>
<?php if (empty($rules)): ?>
<div class="margin-bottom-xl margin-top-xl margin-end margin-start text-align-center">
<h4 class="text-fade text-size-small">No attribute rules added yet.<br /><br /><a data-ls-attrs="href=/console/database/collection/settings?id={{router.params.collection}}&project={{router.params.project}}">Update Collection</a></h4>
</div>
<?php else: ?>
<?php
$comp = new View(__DIR__ . '/form.phtml');
$comp
->setParam('collection', $collection)
->setParam('collections', $collections)
->setParam('namespace', $namespace)
->setParam('key', 'data')
->setParam('parent', 1)
;
echo $comp->render();
?>
<div class="box">
<fieldset name="data" data-cast-to="object">
<ul data-ls-loop="project-collection.attributes" data-ls-as="attribute">
<li>
<label>
<div data-ls-bind="{{attribute.$id}}" class="margin-bottom-tiny"></div>
<div data-ls-if="{{attribute.required}}" class="text-size-xs text-danger text-fade">required</div>
<div data-ls-if="!{{attribute.required}}" class="text-size-xs text-fade">optional</div>
</label>
<textarea data-ls-attrs="name={{attribute.$id}}" data-ls-bind="{{project-document|documentAttribute}}"></textarea>
</li>
</ul>
</fieldset>
<div class="toggle margin-bottom" data-ls-ui-open data-button-aria="Open Permissions">
<i class="icon-plus pull-end margin-top-tiny"></i>
@ -255,68 +87,68 @@ echo $comp->render();
<h3 class="margin-bottom-large">Permissions</h3>
<label for="collection-read">Read Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</span></label>
<input type="hidden" id="collection-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-document.$permissions.read}}" placeholder="User ID, Team ID or Role" />
<input type="hidden" id="collection-read" name="read" data-forms-tags data-cast-to="json" data-ls-bind="{{project-document.$read}}" placeholder="User ID, Team ID or Role" />
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
<label for="collection-write">Write Access <span class="text-size-small">(<a data-ls-attrs="href={{env.HOME}}/docs/permissions" target="_blank" rel="noopener">Learn more</a>)</label>
<input type="hidden" id="collection-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-document.$permissions.write}}" placeholder="User ID, Team ID or Role" />
<input type="hidden" id="collection-write" name="write" data-forms-tags data-cast-to="json" data-ls-bind="{{project-document.$write}}" placeholder="User ID, Team ID or Role" />
<div class="text-fade text-size-xs margin-top-negative-small margin-bottom">Add 'role:all' for wildcard access</div>
</div>
<button data-ls-if="({{project-document.$id}})">Update</button>
<button data-ls-if="(!{{project-document.$id}})">Create</button>
<?php endif;?>
</div>
</div>
</form>
</div>
<div class="col span-4 sticky-top">
<div data-ls-if="({{project-document.$id}})">
<label>Document ID</label>
<div class="input-copy margin-bottom">
<input type="text" autocomplete="off" placeholder="" data-ls-bind="{{project-document.$id}}" disabled data-forms-copy class="margin-bottom-no" />
</div>
</div>
<label>Collection ID</label>
<div class="input-copy margin-bottom">
<input type="text" autocomplete="off" placeholder="" data-ls-bind="{{router.params.collection}}" disabled data-forms-copy class="margin-bottom-no" />
</div>
<ul class="margin-bottom-large text-fade text-size-small" data-ls-if="({{project-document.$id}})">
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
</ul>
<div data-ls-if="({{project-document.$id}})">
<form name="database.deleteDocument" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Collection Document"
data-service="database.deleteDocument"
data-event="submit"
data-param-collection-id="{{router.params.collection}}"
data-param-document-id="{{project-document.$id}}"
data-confirm="Are you sure you want to delete this document?"
data-success="alert,trigger,redirect"
data-success-param-alert-text="Document deleted successfully"
data-success-param-trigger-events="database.deleteDocument"
data-success-param-redirect-url="/console/database/collection?id={{router.params.collection}}&project={{router.params.project}}"
data-failure="alert"
data-failure-param-alert-text="Failed to delete collection"
data-failure-param-alert-classname="error">
<button type="submit" class="danger fill">Delete Document</button>
</form>
</div>
<div class="col span-4 sticky-top">
<div data-ls-if="({{project-document.$id}})">
<label>Document ID</label>
<div class="input-copy margin-bottom">
<input type="text" autocomplete="off" placeholder="" data-ls-bind="{{project-document.$id}}" disabled data-forms-copy class="margin-bottom-no" />
</div>
</div>
<label>Collection ID</label>
<div class="input-copy margin-bottom">
<input type="text" autocomplete="off" placeholder="" data-ls-bind="{{router.params.collection}}" disabled data-forms-copy class="margin-bottom-no" />
</div>
<ul class="margin-bottom-large text-fade text-size-small" data-ls-if="({{project-document.$id}})">
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
</ul>
<div data-ls-if="({{project-document.$id}})">
<form name="database.deleteDocument" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Delete Collection Document"
data-service="database.deleteDocument"
data-event="submit"
data-param-collection-id="{{router.params.collection}}"
data-param-document-id="{{project-document.$id}}"
data-confirm="Are you sure you want to delete this document?"
data-success="alert,trigger,redirect"
data-success-param-alert-text="Document deleted successfully"
data-success-param-trigger-events="database.deleteDocument"
data-success-param-redirect-url="/console/database/collection?id={{router.params.collection}}&project={{router.params.project}}"
data-failure="alert"
data-failure-param-alert-text="Failed to delete collection"
data-failure-param-alert-classname="error">
<button type="submit" class="danger fill">Delete Document</button>
</form>
</div>
</div>
</div>
</div>
</li>
<!-- <li data-ls-if="{{project-document.$id}}" data-state="/console/database/document/activity?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
<h2>Activity</h2>
</li> -->
</ul>
</li>
<!-- <li data-ls-if="{{project-document.$id}}" data-state="/console/database/document/activity?id={{router.params.id}}&collection={{router.params.collection}}&project={{router.params.project}}">
<h2>Activity</h2>
</li> -->
</ul>
</div>
</div>
</div>
</div>

View file

@ -74,7 +74,7 @@ $scopes = $this->getParam('scopes', []);
<hr class="margin-top-no" />
<button type="submit">Save</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
<button type="submit">Update</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</form>
</div>

View file

@ -43,7 +43,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
data-event="submit"
data-param-project-id="{{router.params.project}}"
data-success="alert,trigger"
data-success-param-alert-text="Saved project successfully"
data-success-param-alert-text="Updated project successfully"
data-success-param-trigger-events="projects.update"
data-failure="alert"
data-failure-param-alert-text="Failed to update project"
@ -75,7 +75,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
</div> -->
<button class="" type="submit">Save</button>
<button class="" type="submit">Update</button>
</form>
</div>
@ -152,7 +152,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
data-event="submit"
data-param-project-id="{{router.params.project}}"
data-success="alert,trigger"
data-success-param-alert-text="Saved project successfully"
data-success-param-alert-text="Updated project successfully"
data-success-param-trigger-events="projects.update"
data-failure="alert"
data-failure-param-alert-text="Failed to update project"
@ -191,7 +191,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false);
<hr />
<button class="" type="submit">Save</button>
<button class="" type="submit">Update</button>
</div>
</form>
</li> -->

View file

@ -33,7 +33,7 @@ $fileLimitHuman = $this->getParam('fileLimitHuman', 0);
<input name="search" id="searchFiles" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
</div>
<div class="col span-2 desktops-only">
<button class="fill" title="Search" aria-label="Search"><i class="icon-search"></i></button>
<button class="fill" title="Search" aria-label="Search">Search</button>
</div>
</div>
</form>

View file

@ -34,7 +34,7 @@ $auth = $this->getParam('auth', []);
<input name="search" id="searchUsers" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
</div>
<div class="col span-2 desktops-only">
<button class="fill" title="Search" aria-label="Search"><i class="icon-search"></i></button>
<button class="fill" title="Search" aria-label="Search">Search</button>
</div>
</div>
</form>
@ -201,7 +201,7 @@ $auth = $this->getParam('auth', []);
<input name="search" id="searchTeams" type="search" autocomplete="off" placeholder="Search" class="margin-bottom-no" data-ls-bind="{{router.params.search}}">
</div>
<div class="col span-2 desktops-only">
<button class="fill" title="Search" aria-label="Search"><i class="icon-search"></i></button>
<button class="fill" title="Search" aria-label="Search">Search</button>
</div>
</div>
</form>

View file

@ -114,7 +114,7 @@ $events = array_keys($this->getParam('events', []));
</div>
<footer>
<button type="submit">Save</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
<button type="submit">Update</button> &nbsp; <button data-ui-modal-close="" type="button" class="reverse">Cancel</button>
</footer>
</form>
</div>

View file

@ -98,6 +98,12 @@ services:
- _APP_FUNCTIONS_MEMORY
- _APP_FUNCTIONS_MEMORY_SWAP
- _APP_FUNCTIONS_RUNTIMES
- _APP_STATSD_HOST
- _APP_STATSD_PORT
- _APP_MAINTENANCE_INTERVAL
- _APP_MAINTENANCE_RETENTION_EXECUTION
- _APP_MAINTENANCE_RETENTION_ABUSE
- _APP_MAINTENANCE_RETENTION_AUDIT
appwrite-worker-usage:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>

View file

@ -21,6 +21,9 @@ class AuditsV1 extends Worker
{
$projectId = $this->args['projectId'];
$userId = $this->args['userId'];
$userName = $this->args['userName'];
$userEmail = $this->args['userEmail'];
$mode = $this->args['mode'];
$event = $this->args['event'];
$resource = $this->args['resource'];
$userAgent = $this->args['userAgent'];
@ -30,7 +33,12 @@ class AuditsV1 extends Worker
$dbForInternal = $this->getInternalDB($projectId);
$audit = new Audit($dbForInternal);
$audit->log($userId, $event, $resource, $userAgent, $ip, '', $data);
$audit->log($userId, $event, $resource, $userAgent, $ip, '', [
'userName' => $userName,
'userEmail' => $userEmail,
'mode' => $mode,
'data' => $data,
]);
}
public function shutdown(): void

View file

@ -45,7 +45,7 @@
"utopia-php/cache": "0.4.*",
"utopia-php/cli": "0.11.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.7.*",
"utopia-php/database": "0.9.*",
"utopia-php/locale": "0.4.*",
"utopia-php/registry": "0.5.*",
"utopia-php/preloader": "0.2.*",
@ -53,6 +53,7 @@
"utopia-php/swoole": "0.2.*",
"utopia-php/storage": "0.5.*",
"utopia-php/image": "0.5.*",
"resque/php-resque": "1.3.6",
"matomo/device-detector": "4.2.3",
"dragonmantank/cron-expression": "3.1.0",

44
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "7de5dc8a9fe3cbc14696c685d1cdddee",
"content-hash": "6489948fde57f58412fdda5737e5a77e",
"packages": [
{
"name": "adhocore/jwt",
@ -1666,22 +1666,22 @@
},
{
"name": "utopia-php/abuse",
"version": "0.6.2",
"version": "0.6.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "4cd9c16610f7398d2e1737663ef682fa721ae736"
"reference": "d63e928c2c50b367495a499a85ba9806ee274c5e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/4cd9c16610f7398d2e1737663ef682fa721ae736",
"reference": "4cd9c16610f7398d2e1737663ef682fa721ae736",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/d63e928c2c50b367495a499a85ba9806ee274c5e",
"reference": "d63e928c2c50b367495a499a85ba9806ee274c5e",
"shasum": ""
},
"require": {
"ext-pdo": "*",
"php": ">=7.4",
"utopia-php/database": "0.7.*"
"utopia-php/database": ">=0.6 <1.0"
},
"require-dev": {
"phpunit/phpunit": "^9.4",
@ -1713,9 +1713,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/0.6.2"
"source": "https://github.com/utopia-php/abuse/tree/0.6.3"
},
"time": "2021-08-13T07:52:34+00:00"
"time": "2021-08-16T18:38:31+00:00"
},
{
"name": "utopia-php/analytics",
@ -1774,22 +1774,22 @@
},
{
"name": "utopia-php/audit",
"version": "0.6.2",
"version": "0.6.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "2ec39a53eb98a5f9d230550ad56c7c04de5d77df"
"reference": "d79b467fbc7d03e5e02f12cdeb08761507a60ca0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/2ec39a53eb98a5f9d230550ad56c7c04de5d77df",
"reference": "2ec39a53eb98a5f9d230550ad56c7c04de5d77df",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/d79b467fbc7d03e5e02f12cdeb08761507a60ca0",
"reference": "d79b467fbc7d03e5e02f12cdeb08761507a60ca0",
"shasum": ""
},
"require": {
"ext-pdo": "*",
"php": ">=7.4",
"utopia-php/database": "0.7.*"
"utopia-php/database": ">=0.6 <1.0"
},
"require-dev": {
"phpunit/phpunit": "^9.3",
@ -1821,9 +1821,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/0.6.2"
"source": "https://github.com/utopia-php/audit/tree/0.6.3"
},
"time": "2021-08-13T08:05:20+00:00"
"time": "2021-08-16T18:49:55+00:00"
},
{
"name": "utopia-php/cache",
@ -1984,16 +1984,16 @@
},
{
"name": "utopia-php/database",
"version": "0.7.0",
"version": "0.9.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "46c4a99347397e362a9429826e1888b0aefb2056"
"reference": "f9b1836621df7e14300f1622cb8b8d6fcfedfdaf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/46c4a99347397e362a9429826e1888b0aefb2056",
"reference": "46c4a99347397e362a9429826e1888b0aefb2056",
"url": "https://api.github.com/repos/utopia-php/database/zipball/f9b1836621df7e14300f1622cb8b8d6fcfedfdaf",
"reference": "f9b1836621df7e14300f1622cb8b8d6fcfedfdaf",
"shasum": ""
},
"require": {
@ -2041,9 +2041,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.7.0"
"source": "https://github.com/utopia-php/database/tree/0.9.0"
},
"time": "2021-08-10T19:09:58+00:00"
"time": "2021-08-18T19:08:47+00:00"
},
{
"name": "utopia-php/domains",
@ -6278,5 +6278,5 @@
"platform-overrides": {
"php": "8.0"
},
"plugin-api-version": "2.0.0"
"plugin-api-version": "2.1.0"
}

View file

@ -123,6 +123,10 @@ services:
- _APP_FUNCTIONS_RUNTIMES
- _APP_STATSD_HOST
- _APP_STATSD_PORT
- _APP_MAINTENANCE_INTERVAL
- _APP_MAINTENANCE_RETENTION_EXECUTION
- _APP_MAINTENANCE_RETENTION_ABUSE
- _APP_MAINTENANCE_RETENTION_AUDIT
appwrite-worker-audits:
entrypoint: worker-audits

View file

@ -0,0 +1,14 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.getMembership('[TEAM_ID]', '[MEMBERSHIP_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -5,7 +5,7 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.account.create('email@example.com', 'password');
let promise = sdk.account.create('', 'email@example.com', 'password');
promise.then(function (response) {
console.log(response); // Success

View file

@ -0,0 +1,14 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createBooleanAttribute('[COLLECTION_ID]', '', false);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -5,7 +5,7 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createCollection('[NAME]', '', '');
let promise = sdk.database.createCollection('', '[NAME]', '', '');
promise.then(function (response) {
console.log(response); // Success

View file

@ -5,7 +5,7 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createDocument('[COLLECTION_ID]', {});
let promise = sdk.database.createDocument('[COLLECTION_ID]', '', {});
promise.then(function (response) {
console.log(response); // Success

View file

@ -0,0 +1,14 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createEmailAttribute('[COLLECTION_ID]', '', false);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -0,0 +1,14 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createFloatAttribute('[COLLECTION_ID]', '', false);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -5,7 +5,7 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createIndex('[COLLECTION_ID]', '', 'text', []);
let promise = sdk.database.createIndex('[COLLECTION_ID]', '', 'key', []);
promise.then(function (response) {
console.log(response); // Success

View file

@ -0,0 +1,14 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createIntegerAttribute('[COLLECTION_ID]', '', false);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -0,0 +1,14 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createIpAttribute('[COLLECTION_ID]', '', false);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -0,0 +1,14 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createStringAttribute('[COLLECTION_ID]', '', null, false);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -0,0 +1,14 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.createUrlAttribute('[COLLECTION_ID]', '', null, false);
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -0,0 +1,14 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.database.listCollectionLogs('[COLLECTION_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -5,7 +5,7 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.functions.create('[NAME]', [], 'java-11.0');
let promise = sdk.functions.create('', '[NAME]', [], 'java-11.0');
promise.then(function (response) {
console.log(response); // Success

View file

@ -5,7 +5,7 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.projects.create('[NAME]', '[TEAM_ID]');
let promise = sdk.projects.create('', '[NAME]', '[TEAM_ID]');
promise.then(function (response) {
console.log(response); // Success

View file

@ -5,7 +5,7 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.projects.updateServiceStatus('[PROJECT_ID]', 'account');
let promise = sdk.projects.updateServiceStatus('[PROJECT_ID]', 'account', false);
promise.then(function (response) {
console.log(response); // Success

View file

@ -5,7 +5,7 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.storage.createFile(document.getElementById('uploader').files[0]);
let promise = sdk.storage.createFile('', document.getElementById('uploader').files[0]);
promise.then(function (response) {
console.log(response); // Success

View file

@ -5,7 +5,7 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.create('[NAME]');
let promise = sdk.teams.create('', '[NAME]');
promise.then(function (response) {
console.log(response); // Success

View file

@ -0,0 +1,14 @@
let sdk = new Appwrite();
sdk
.setEndpoint('https://[HOSTNAME_OR_IP]/v1') // Your API Endpoint
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.teams.getMembership('[TEAM_ID]', '[MEMBERSHIP_ID]');
promise.then(function (response) {
console.log(response); // Success
}, function (error) {
console.log(error); // Failure
});

View file

@ -5,7 +5,7 @@ sdk
.setProject('5df5acd0d48c2') // Your project ID
;
let promise = sdk.users.create('email@example.com', 'password');
let promise = sdk.users.create('', 'email@example.com', 'password');
promise.then(function (response) {
console.log(response); // Success

View file

@ -0,0 +1 @@
Get the collection activity logs list by its unique ID.

View file

@ -1 +1 @@
Get a user activity logs list by its unique ID.
Get the user activity logs list by its unique ID.

View file

@ -46,6 +46,7 @@ const configApp = {
'public/scripts/views/forms/copy.js',
'public/scripts/views/forms/custom-id.js',
'public/scripts/views/forms/document.js',
'public/scripts/views/forms/duplications.js',
'public/scripts/views/forms/document-preview.js',
'public/scripts/views/forms/filter.js',
'public/scripts/views/forms/headers.js',
@ -68,6 +69,7 @@ const configApp = {
'public/scripts/views/forms/upload.js',
'public/scripts/views/general/cookies.js',
'public/scripts/views/general/copy.js',
'public/scripts/views/general/page-title.js',
'public/scripts/views/general/scroll-to.js',
'public/scripts/views/general/scroll-direction.js',

View file

@ -193,19 +193,20 @@ if(typeof write!=='undefined'){payload['write']=write;}
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),deleteDocument:(collectionId,documentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
let path='/database/collections/{collectionId}/documents/{documentId}'.replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listIndexes:(collectionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
let path='/database/collections/{collectionId}/indexes'.replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createIndex:(collectionId,id,type,attributes,orders)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
if(typeof id==='undefined'){throw new AppwriteException('Missing required parameter: "id"');}
let path='/database/collections/{collectionId}/indexes'.replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createIndex:(collectionId,indexId,type,attributes,orders)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
if(typeof type==='undefined'){throw new AppwriteException('Missing required parameter: "type"');}
if(typeof attributes==='undefined'){throw new AppwriteException('Missing required parameter: "attributes"');}
let path='/database/collections/{collectionId}/indexes'.replace('{collectionId}',collectionId);let payload={};if(typeof id!=='undefined'){payload['id']=id;}
let path='/database/collections/{collectionId}/indexes'.replace('{collectionId}',collectionId);let payload={};if(typeof indexId!=='undefined'){payload['indexId']=indexId;}
if(typeof type!=='undefined'){payload['type']=type;}
if(typeof attributes!=='undefined'){payload['attributes']=attributes;}
if(typeof orders!=='undefined'){payload['orders']=orders;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getIndex:(collectionId,indexId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
console.log(collectionId,indexId,type,attributes,orders);const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getIndex:(collectionId,indexId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),deleteIndex:(collectionId,indexId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);})};this.functions={list:(search,limit,offset,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listCollectionLogs:(collectionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
let path='/database/collections/{collectionId}/logs'.replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.functions={list:(search,limit,offset,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}
@ -2127,13 +2128,15 @@ result+'")": '+
error);}
if(debug){console.info("debug-ls-if result:",result);}
paths=expression.getPaths();let prv=element.$lsSkip;element.$lsSkip=!result;if(!result){element.style.visibility="hidden";element.style.display="none";}else{element.style.removeProperty("display");element.style.removeProperty("visibility");}
if(prv===true&&element.$lsSkip===false){view.render(element);}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split(".");while(path.length){container.bind(element,path.join("."),check);path.pop();}}},});window.ls.container.get("view").add({selector:"data-ls-loop",template:false,nested:false,controller:function(element,view,container,window,expression){let expr=expression.parse(element.getAttribute("data-ls-loop"));let as=element.getAttribute("data-ls-as");let key=element.getAttribute("data-ls-key")||"$index";let limit=parseInt(expression.parse(element.getAttribute("data-limit")||"")||-1);let debug=element.getAttribute("data-debug")||false;let echo=function(){let array=container.path(expr);let counter=0;array=!array?[]:array;let watch=!!(array&&array.__proxy);while(element.hasChildNodes()){element.removeChild(element.lastChild);element.lastChild=null;}
if(prv===true&&element.$lsSkip===false){view.render(element);}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split(".");while(path.length){container.bind(element,path.join("."),check);path.pop();}}},});window.ls.container.get("view").add({selector:"data-ls-loop",template:false,nested:false,controller:function(element,view,container,filter,window,expression){let expr=expression.parse(element.getAttribute("data-ls-loop"));let as=element.getAttribute("data-ls-as");let filterName=element.getAttribute("data-ls-filter");let key=element.getAttribute("data-ls-key")||"$index";let prefix=element.getAttribute("data-ls-prefix")||null;let postfix=element.getAttribute("data-ls-postfix")||null;let limit=parseInt(expression.parse(element.getAttribute("data-ls-limit")||"")||-1);let debug=element.getAttribute("data-debug")||false;let echo=function(){let array=container.path(expr);let counter=0;array=!array?[]:array;if(filterName){array=filter.apply(filterName,array);}
let watch=!!(array&&array.__proxy);while(element.hasChildNodes()){element.removeChild(element.lastChild);element.lastChild=null;}
if(array instanceof Array&&typeof array!=="object"){throw new Error("Reference value must be array or object. "+typeof array+" given");}
let children=[];element.$lsSkip=true;element.style.visibility=0===array.length&&element.style.visibility==""?"hidden":"visible";for(let prop in array){if(counter==limit){break;}
let children=[];element.$lsSkip=true;element.style.visibility=0===array.length&&element.style.visibility==""?"hidden":"visible";if(prefix){prefixElement=document.getElementById(prefix);let html=prefixElement.innerHTML;prefixElement=template.cloneNode(true);if(prefixElement){prefixElement.innerHTML=html;element.appendChild(prefixElement);view.render(prefixElement);}}
for(let prop in array){if(counter==limit){break;}
counter++;if(!array.hasOwnProperty(prop)){continue;}
children[prop]=template.cloneNode(true);element.appendChild(children[prop]);((index)=>{let context=expr+"."+index;container.addNamespace(as,context);if(debug){console.info("debug-ls-loop","index",index);console.info("debug-ls-loop","context",context);console.info("debug-ls-loop","context-path",container.path(context).name);console.info("debug-ls-loop","namespaces",container.namespaces);}
container.set(as,container.path(context),true,watch);container.set(key,index,true,false);view.render(children[prop]);container.removeNamespace(as);})(prop);}
element.dispatchEvent(new Event("looped"));};let template=element.children.length===1?element.children[0]:window.document.createElement("li");echo();container.bind(element,expr+".length",echo);let path=(expr+".length").split(".");while(path.length){container.bind(element,path.join("."),echo);path.pop();}},});window.ls.container.get("view").add({selector:"data-ls-template",template:false,controller:function(element,view,http,expression,document,container){let template=element.getAttribute("data-ls-template")||"";let type=element.getAttribute("data-type")||"url";let debug=element.getAttribute("data-debug")||false;let paths=[];let check=function(init=false){let source=expression.parse(template);paths=expression.getPaths();element.innerHTML="";if("script"===type){let inlineTemplate=document.getElementById(source);if(inlineTemplate&&inlineTemplate.innerHTML){element.innerHTML=inlineTemplate.innerHTML;element.dispatchEvent(new CustomEvent("template-loaded",{bubbles:true,cancelable:false,}));}else{if(debug){console.error('Missing template "'+source+'"');}}
element.dispatchEvent(new Event("looped"));};let template=element.children.length>=1?element.children[0]:window.document.createElement("li");echo();container.bind(element,expr+".length",echo);let path=(expr+".length").split(".");while(path.length){container.bind(element,path.join("."),echo);path.pop();}},});window.ls.container.get("view").add({selector:"data-ls-template",template:false,controller:function(element,view,http,expression,document,container){let template=element.getAttribute("data-ls-template")||"";let type=element.getAttribute("data-type")||"url";let debug=element.getAttribute("data-debug")||false;let paths=[];let check=function(init=false){let source=expression.parse(template);paths=expression.getPaths();element.innerHTML="";if("script"===type){let inlineTemplate=document.getElementById(source);if(inlineTemplate&&inlineTemplate.innerHTML){element.innerHTML=inlineTemplate.innerHTML;element.dispatchEvent(new CustomEvent("template-loaded",{bubbles:true,cancelable:false,}));}else{if(debug){console.error('Missing template "'+source+'"');}}
if(!init){view.render(element);}
return;}
http.get(source).then((function(element){return function(data){element.innerHTML=data;view.render(element);element.dispatchEvent(new CustomEvent("template-loaded",{bubbles:true,cancelable:false,}));};})(element),function(){throw new Error("Failed loading template");});};check(true);for(let i=0;i<paths.length;i++){let path=paths[i].split(".");while(path.length){container.bind(element,path.join("."),check);path.pop();}}},});window.ls.error=function(){return function(error){window.console.error(error);if(window.location.pathname!=='/console'){window.location='/console';}};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){var subscribe=document.getElementById('newsletter').checked;if(subscribe){let alerts=container.get('alerts');let loaderId=alerts.add({text:'Loading...',class:""},0);fetch('https://appwrite.io/v1/newsletter/subscribe',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:form.name,email:form.email,}),}).finally(function(){alerts.remove(loaderId);window.location='/console';});}else{window.location='/console';}},function(error){window.location='/auth/signup?failure=1';});});(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;message.remove=function(){scope.remove(message.id);};scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
@ -2308,7 +2311,10 @@ return $value;}).add("platformsLimit",function($value){return $value;}).add("lim
return $value.join(", ").replace(/,\s([^,]+)$/,' and $1');}).add("runtimeName",function($value,env){if(env&&env.RUNTIMES&&env.RUNTIMES[$value]){return env.RUNTIMES[$value].name;}
return'';}).add("runtimeLogo",function($value,env){if(env&&env.RUNTIMES&&env.RUNTIMES[$value]){return env.RUNTIMES[$value].logo;}
return'';}).add("runtimeVersion",function($value,env){if(env&&env.RUNTIMES&&env.RUNTIMES[$value]){return env.RUNTIMES[$value].version;}
return'';});function abbreviate(number,maxPlaces,forcePlaces,forceLetter){number=Number(number);forceLetter=forceLetter||false;if(forceLetter!==false){return annotate(number,maxPlaces,forcePlaces,forceLetter);}
return'';}).add("indexAttributes",function($value){let output='';for(let i=0;i<$value.attributes.length;i++){output+=$value.attributes[i]+' ('+$value.orders[i]+'), '}
return output.slice(0,-2);}).add("collectionAttributes",function($value){if(!Array.isArray($value)){return[];}
$value.unshift({$id:'$id'});return $value;}).add("documentAttribute",function($value,attribute){if($value[attribute.$id]){return $value[attribute.$id];}
return null;});function abbreviate(number,maxPlaces,forcePlaces,forceLetter){number=Number(number);forceLetter=forceLetter||false;if(forceLetter!==false){return annotate(number,maxPlaces,forcePlaces,forceLetter);}
let abbr;if(number>=1e12){abbr="T";}else if(number>=1e9){abbr="B";}else if(number>=1e6){abbr="M";}else if(number>=1e3){abbr="K";}else{abbr="";}
return annotate(number,maxPlaces,forcePlaces,abbr);}
function annotate(number,maxPlaces,forcePlaces,abbr){let rounded=0;switch(abbr){case"T":rounded=number/1e12;break;case"B":rounded=number/1e9;break;case"M":rounded=number/1e6;break;case"K":rounded=number/1e3;break;case"":rounded=number;break;}
@ -2344,7 +2350,7 @@ running=false;element.style.backgroud='transparent';element.classList.add("load-
container.set("serviceData",data,true,true);container.set("serviceForm",formData,true,true);for(let i=0;i<parsedSuccess.length;i++){container.resolve(resolve(callbacks[parsedSuccess[i]],"successParam"+
parsedSuccess[i].charAt(0).toUpperCase()+
parsedSuccess[i].slice(1),{}));}
container.set("serviceData",null,true,true);container.set("serviceForm",null,true,true);element.$lsSkip=false;view.render(element);},function(exception){if(loaderId!==null){alerts.remove(loaderId);}
container.set("serviceData",null,true,true);container.set("serviceForm",null,true,true);element.$lsSkip=false;view.render(element);},function(exception){console.error(exception);if(loaderId!==null){alerts.remove(loaderId);}
if(!element){return;}
running=false;element.style.backgroud='transparent';element.classList.add("load-service-end");for(let i=0;i<parsedFailure.length;i++){container.resolve(resolve(callbacks[parsedFailure[i]],"failureParam"+
parsedFailure[i].charAt(0).toUpperCase()+
@ -2358,8 +2364,9 @@ let doNotTrack=window.navigator.doNotTrack;if(doNotTrack=='1'){return;}
let project=router.params["project"]||'None';ga("set","page",window.location.pathname);ga("set","dimension1",project);ga('set','dimension2',env.VERSION);ga('set','dimension3',env.SETUP);ga("send","pageview");}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-clone",controller:function(element,document,view){var template=element.innerHTML.toString();var label=element.dataset["label"]||"Add";var icon=element.dataset["icon"]||null;var target=element.dataset["target"]||null;var first=parseInt(element.dataset["first"]||1);var button=document.createElement("button");button.type="button";button.innerText=" "+label+" ";button.classList.add("margin-end");button.classList.add("margin-bottom-small");button.classList.add("reverse");if(icon){var iconElement=document.createElement("i");iconElement.className=icon;button.insertBefore(iconElement,button.firstChild);}
if(target){target=document.getElementById(target);}
button.addEventListener("click",function(){var clone=document.createElement(element.tagName);if(element.name){clone.name=element.name;}
clone.innerHTML=template;clone.className=element.className;view.render(clone);if(target){target.appendChild(clone);}else{button.parentNode.insertBefore(clone,button);}
clone.querySelector("input").focus();Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-add",repeat:false,controller:function(element,view,container,document){for(var i=0;i<element.children.length;i++){let button=document.createElement("button");let template=element.children[i].cloneNode(true);let as=element.getAttribute('data-ls-as');let counter=0;button.type="button";button.innerText="Add";button.classList.add("reverse");button.classList.add("margin-end-small");button.addEventListener('click',function(){container.addNamespace(as,'new-'+counter++);console.log(container.namespaces,container.get(as),as);container.set(as,null,true,true);let child=template.cloneNode(true);view.render(child);element.appendChild(child);element.style.visibility='visible';let inputs=child.querySelectorAll('input,textarea');for(let index=0;index<inputs.length;++index){if(inputs[index].type!=='hidden'){inputs[index].focus();break;}}});element.after(button);}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-chart",controller:function(element,container,date,document){let wrapper=document.createElement("div");let child=document.createElement("canvas");let sources=element.getAttribute('data-forms-chart');let width=element.getAttribute('data-width')||500;let height=element.getAttribute('data-height')||175;let colors=(element.getAttribute('data-colors')||'blue,green,orange,red').split(',');let themes={'blue':'#29b5d9','green':'#4eb55b','orange':'#fba233','red':'#dc3232',};let range={'24h':'H:i','7d':'d F Y','30d':'d F Y','90d':'d F Y'}
clone.innerHTML=template;clone.className=element.className;var input=clone.querySelector("input, select, textarea");view.render(clone);if(target){target.appendChild(clone);}else{button.parentNode.insertBefore(clone,button);}
if(input){input.focus();}
Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);button.form.addEventListener('reset',function(event){target.innerHTML='';if(first){button.click();}});if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-add",repeat:false,controller:function(element,view,container,document){for(var i=0;i<element.children.length;i++){let button=document.createElement("button");let template=element.children[i].cloneNode(true);let as=element.getAttribute('data-ls-as');let counter=0;button.type="button";button.innerText="Add";button.classList.add("reverse");button.classList.add("margin-end-small");button.addEventListener('click',function(){container.addNamespace(as,'new-'+counter++);console.log(container.namespaces,container.get(as),as);container.set(as,null,true,true);let child=template.cloneNode(true);view.render(child);element.appendChild(child);element.style.visibility='visible';let inputs=child.querySelectorAll('input,textarea');for(let index=0;index<inputs.length;++index){if(inputs[index].type!=='hidden'){inputs[index].focus();break;}}});element.after(button);}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-chart",controller:function(element,container,date,document){let wrapper=document.createElement("div");let child=document.createElement("canvas");let sources=element.getAttribute('data-forms-chart');let width=element.getAttribute('data-width')||500;let height=element.getAttribute('data-height')||175;let colors=(element.getAttribute('data-colors')||'blue,green,orange,red').split(',');let themes={'blue':'#29b5d9','green':'#4eb55b','orange':'#fba233','red':'#dc3232',};let range={'24h':'H:i','7d':'d F Y','30d':'d F Y','90d':'d F Y'}
element.parentNode.insertBefore(wrapper,element.nextSibling);wrapper.classList.add('content');child.width=width;child.height=height;sources=sources.split(',');wrapper.appendChild(child);let chart=null;let check=function(){let config={type:"line",data:{labels:[],datasets:[]},options:{responsive:true,title:{display:false,text:"Stats"},legend:{display:false},tooltips:{mode:"index",intersect:false,caretPadding:0},hover:{mode:"nearest",intersect:true},scales:{xAxes:[{display:false}],yAxes:[{display:false}]}}};for(let i=0;i<sources.length;i++){let label=sources[i].substring(0,sources[i].indexOf('='));let path=sources[i].substring(sources[i].indexOf('=')+1);let data=container.path(path);let value=JSON.parse(element.value);config.data.labels[i]=label;config.data.datasets[i]={};config.data.datasets[i].label=label;config.data.datasets[i].borderColor=themes[colors[i]];config.data.datasets[i].backgroundColor=themes[colors[i]]+'36';config.data.datasets[i].borderWidth=2;config.data.datasets[i].data=[0,0,0,0,0,0,0];config.data.datasets[i].fill=true;if(!data){return;}
let dateFormat=(value.range&&range[value.range])?range[value.range]:'d F Y';for(let x=0;x<data.length;x++){config.data.datasets[i].data[x]=data[x].value;config.data.labels[x]=date.format(dateFormat,data[x].date);}}
if(chart){chart.destroy();}
@ -2384,9 +2391,11 @@ element.setAttribute("data-id-type",idType);info.innerHTML="Appwrite will genera
button.className=idType=="custom"?"icon-shuffle copy":"icon-edit copy";}
const syncEditorWithID=function(event){if(element.value!=='unique()'||idType!='auto'){writer.value=element.value;}
if(idType=='auto'){element.value='unique()';}}
const keypress=function(e){const key=e.which||e.keyCode;const ZERO=48;const NINE=57;const SMALL_A=97;const SMALL_Z=122;const CAPITAL_A=65;const CAPITAL_Z=90;const UNDERSCORE=95;const isNotValidDigit=key<ZERO||key>NINE;const isNotValidSmallAlphabet=key<SMALL_A||key>SMALL_Z;const isNotValidCapitalAlphabet=key<CAPITAL_A||key>CAPITAL_Z;if(key==UNDERSCORE&&e.target.value.length==0){e.preventDefault();}
if(key!=UNDERSCORE&&isNotValidDigit&&isNotValidSmallAlphabet&&isNotValidCapitalAlphabet){e.preventDefault();}}
syncEditorWithID();setIdType(idType);writer.addEventListener("change",function(event){element.value=writer.value;});writer.form.addEventListener('reset',function(event){const resetEvent=new Event('reset');element.dispatchEvent(resetEvent);});element.addEventListener('reset',function(event){idType=element.getAttribute('data-id-type');setIdType(idType);});writer.addEventListener('keypress',keypress);button.addEventListener("click",switchType);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-document",controller:function(element,container,search){var formsDocument=(element.dataset["formsDocument"]||'');var searchButton=(element.dataset["search"]||0);let path=container.scope(searchButton);element.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent(formsDocument,{bubbles:false,cancelable:true}));});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-document-preview",controller:function(element,container,search){element.addEventListener('change',function(){console.log(element.value);});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-filter",controller:function(document,container,expression,element,form,di){let name=element.dataset["formsFilter"]||"";let events=element.dataset["event"]||"";let serialize=function(obj,prefix){let str=[],p;for(p in obj){if(obj.hasOwnProperty(p)){let k=prefix?prefix+"["+p+"]":p,v=obj[p];if(v===""){continue;}
const keypress=function(e){const key=e.which||e.keyCode;const ZERO=48;const NINE=57;const SMALL_A=97;const SMALL_Z=122;const CAPITAL_A=65;const CAPITAL_Z=90;const UNDERSCORE=95;const HYPHEN=45;const PERIOD=46;const isNotValidDigit=key<ZERO||key>NINE;const isNotValidSmallAlphabet=key<SMALL_A||key>SMALL_Z;const isNotValidCapitalAlphabet=key<CAPITAL_A||key>CAPITAL_Z;const isNotValidFirstChar=(key===UNDERSCORE||key===HYPHEN||key===PERIOD);if(isNotValidFirstChar&&e.target.value.length==0){e.preventDefault();}
if(key!=UNDERSCORE&&key!=HYPHEN&&key!=PERIOD&&isNotValidDigit&&isNotValidSmallAlphabet&&isNotValidCapitalAlphabet){e.preventDefault();}}
syncEditorWithID();setIdType(idType);writer.addEventListener("change",function(event){element.value=writer.value;});writer.form.addEventListener('reset',function(event){const resetEvent=new Event('reset');element.dispatchEvent(resetEvent);});element.addEventListener('reset',function(event){idType=element.getAttribute('data-id-type');setIdType(idType);});writer.addEventListener('keypress',keypress);button.addEventListener("click",switchType);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-document",controller:function(element,container,search){var formsDocument=(element.dataset["formsDocument"]||'');var searchButton=(element.dataset["search"]||0);let path=container.scope(searchButton);element.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent(formsDocument,{bubbles:false,cancelable:true}));});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-duplications",controller:function(element){let validate=function(element){let duplication=0;let form=element.form;for(let i=0;i<form.elements.length;i++){let field=form.elements[i];if(field.name===element.name&&field.value===element.value){duplication++;}}
if(duplication>1){element.setCustomValidity("Duplicated value");}
else{element.setCustomValidity("");}};element.addEventListener('change',function(event){validate(event.target)});element.addEventListener('focus',function(event){validate(event.target)});element.addEventListener('blur',function(event){validate(event.target)});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-document-preview",controller:function(element,container,search){element.addEventListener('change',function(){console.log(element.value);});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-filter",controller:function(document,container,expression,element,form,di){let name=element.dataset["formsFilter"]||"";let events=element.dataset["event"]||"";let serialize=function(obj,prefix){let str=[],p;for(p in obj){if(obj.hasOwnProperty(p)){let k=prefix?prefix+"["+p+"]":p,v=obj[p];if(v===""){continue;}
str.push(v!==null&&typeof v==="object"?serialize(v,k):encodeURIComponent(k)+"="+encodeURIComponent(v));}}
return str.join("&");};let parse=function(filter){if(filter===""){return null;}
let operatorsMap=["!=",">=","<=","=",">","<"];let operator=null;for(let key=0;key<operatorsMap.length;key++){if(filter.indexOf(operatorsMap[key])>-1){operator=operatorsMap[key];}}
@ -2421,8 +2430,9 @@ element.addEventListener("keyup",resize);element.addEventListener("change",resiz
var file=document.createElement("li");var image=document.createElement("img");image.src=image.src=env.API+"/storage/files/"+
result+"/preview?width="+
previewWidth+"&height="+
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile(files[0],read,write,1).then(function(response){onComplete(message);render(response.$id);},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
render(element.value);wrapper.scrollIntoView();});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);if(searchButton){let searchOpen=document.createElement("button");searchOpen.type='button';searchOpen.innerHTML='<i class="icon icon-search"></i> Search';searchOpen.classList.add('reverse');let path=container.scope(searchButton);searchOpen.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent("open-file-serach",{bubbles:false,cancelable:true}));});wrapper.appendChild(searchOpen);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-cookies",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookies"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",label:'Learn More',callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile('unique()',files[0],read,write,1).then(function(response){onComplete(message);render(response.$id);},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
render(element.value);wrapper.scrollIntoView();});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);if(searchButton){let searchOpen=document.createElement("button");searchOpen.type='button';searchOpen.innerHTML='<i class="icon icon-search"></i> Search';searchOpen.classList.add('reverse');let path=container.scope(searchButton);searchOpen.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent("open-file-serach",{bubbles:false,cancelable:true}));});wrapper.appendChild(searchOpen);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-cookies",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookies"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",label:'Learn More',callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-copy',repeat:false,controller:function(document,element,alerts){let button=document.createElement("i");button.type="button";button.title="Copy to Clipboard";button.className=element.getAttribute("data-class")||"icon-docs note copy";button.style.cursor="pointer";element.parentNode.insertBefore(button,element.nextSibling);let copy=function(event){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
window.getSelection().removeAllRanges();};button.addEventListener("click",copy);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
element.classList.add('scroll-to-bottom')}
else{element.classList.remove('scroll-to-bottom')
element.classList.add('scroll-to-top')}
@ -2458,12 +2468,12 @@ if(buttonSelector){let buttonElements=element.querySelectorAll(buttonSelector);b
element.addEventListener('click',function(event){let targetA=findParent('a',event.target);let targetB=findParent('button',event.target);if(!targetA&&!targetB){return false;}
if(targetA&&!targetA.href){return false;}
if(targetB&&!targetB.classList.contains('link')){return false;}
closeDelay();});element.insertBefore(button,element.firstChild);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-phases",controller:function(element,window,document,expression,router){var tabs=document.createElement("ul");var container=document.createElement("div");var titles=Array.prototype.slice.call(element.querySelectorAll("li > h1"));var next=Array.prototype.slice.call(element.querySelectorAll("[data-next]"));var previous=Array.prototype.slice.call(element.querySelectorAll("[data-previous]"));var position=0;var init=false;if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h2"));}
closeDelay();});element.insertBefore(button,element.firstChild);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-phases",controller:function(element,window,document,expression,router,view){var tabs=document.createElement("ul");var container=document.createElement("div");var titles=Array.prototype.slice.call(element.querySelectorAll("li > h1"));var next=Array.prototype.slice.call(element.querySelectorAll("[data-next]"));var previous=Array.prototype.slice.call(element.querySelectorAll("[data-previous]"));var position=0;var init=false;if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h2"));}
if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h3"));}
if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h4"));}
if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h5"));}
if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h6"));}
for(var i=0;i<element.children.length;i++){var tabState=expression.parse(element.children[i].dataset["state"]||"");if(tabState&&tabState===(window.location.pathname+window.location.search).substring(0,tabState.length)){position=i;}}
var setTab=function(index){var tabState=expression.parse(element.children[index].dataset["state"]||"");var url="";if(tabState!==""&&tabState!==window.location.pathname+window.location.search){var parser=document.createElement("a");parser.href=tabState;url=!init?parser.pathname+window.location.search:tabState;if(position!=index){window.history.pushState({},"",url);router.reset();}}
element.children[position].classList.remove("selected");element.children[index].classList.add("selected");tabs.children[position].classList.remove("selected");tabs.children[index].classList.add("selected");position=index;document.dispatchEvent(new CustomEvent("tab-changed"));init=true;};tabs.classList.add("tabs");container.classList.add("container");container.classList.add("close");container.dataset["lsUiOpen"]="";container.dataset["buttonClass"]="icon icon-down-dir";titles.map(function(obj,i){var title=document.createElement("li");title.innerHTML=obj.innerHTML;title.className=obj.className;title.tabIndex=0;tabs.appendChild(title);title.addEventListener("click",function(){setTab(i);});title.addEventListener("keyup",function(){if(event.which===13){setTab(i);}});});next.map(function(obj){obj.addEventListener("click",function(){setTab(position+1);});});previous.map(function(obj){obj.addEventListener("click",function(){setTab(position-1);});});setTab(position);container.appendChild(tabs);element.parentNode.insertBefore(container,element);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-trigger",controller:function(element,document,expression){let trigger=expression.parse(element.dataset["lsUiTrigger"]||'').trim().split(',');let event=expression.parse(element.dataset["event"]||'click');let debug=element.getAttribute('data-debug')||false;for(let index=0;index<trigger.length;index++){let name=trigger[index];element.addEventListener(event,function(){if(debug){console.log('Debug: event triggered: '+name);}
element.children[position].classList.remove("selected");element.children[index].classList.add("selected");tabs.children[position].classList.remove("selected");tabs.children[index].classList.add("selected");position=index;document.dispatchEvent(new CustomEvent("tab-changed"));init=true;};tabs.classList.add("tabs");container.classList.add("container");container.classList.add("close");container.dataset["lsUiOpen"]="";container.dataset["buttonClass"]="icon icon-down-dir";titles.map(function(obj,i){var title=document.createElement("li");title.innerHTML=obj.innerHTML;title.className=obj.className;title.tabIndex=0;tabs.appendChild(title);title.addEventListener("click",function(){setTab(i);});title.addEventListener("keyup",function(){if(event.which===13){setTab(i);}});view.render(title);});next.map(function(obj){obj.addEventListener("click",function(){setTab(position+1);});});previous.map(function(obj){obj.addEventListener("click",function(){setTab(position-1);});});setTab(position);container.appendChild(tabs);element.parentNode.insertBefore(container,element);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-trigger",controller:function(element,document,expression){let trigger=expression.parse(element.dataset["lsUiTrigger"]||'').trim().split(',');let event=expression.parse(element.dataset["event"]||'click');let debug=element.getAttribute('data-debug')||false;for(let index=0;index<trigger.length;index++){let name=trigger[index];element.addEventListener(event,function(){if(debug){console.log('Debug: event triggered: '+name);}
document.dispatchEvent(new CustomEvent(name));});}}});})(window);

View file

@ -193,19 +193,20 @@ if(typeof write!=='undefined'){payload['write']=write;}
const uri=new URL(this.config.endpoint+path);return yield this.call('patch',uri,{'content-type':'application/json',},payload);}),deleteDocument:(collectionId,documentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
if(typeof documentId==='undefined'){throw new AppwriteException('Missing required parameter: "documentId"');}
let path='/database/collections/{collectionId}/documents/{documentId}'.replace('{collectionId}',collectionId).replace('{documentId}',documentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listIndexes:(collectionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
let path='/database/collections/{collectionId}/indexes'.replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createIndex:(collectionId,id,type,attributes,orders)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
if(typeof id==='undefined'){throw new AppwriteException('Missing required parameter: "id"');}
let path='/database/collections/{collectionId}/indexes'.replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),createIndex:(collectionId,indexId,type,attributes,orders)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
if(typeof type==='undefined'){throw new AppwriteException('Missing required parameter: "type"');}
if(typeof attributes==='undefined'){throw new AppwriteException('Missing required parameter: "attributes"');}
let path='/database/collections/{collectionId}/indexes'.replace('{collectionId}',collectionId);let payload={};if(typeof id!=='undefined'){payload['id']=id;}
let path='/database/collections/{collectionId}/indexes'.replace('{collectionId}',collectionId);let payload={};if(typeof indexId!=='undefined'){payload['indexId']=indexId;}
if(typeof type!=='undefined'){payload['type']=type;}
if(typeof attributes!=='undefined'){payload['attributes']=attributes;}
if(typeof orders!=='undefined'){payload['orders']=orders;}
const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getIndex:(collectionId,indexId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
console.log(collectionId,indexId,type,attributes,orders);const uri=new URL(this.config.endpoint+path);return yield this.call('post',uri,{'content-type':'application/json',},payload);}),getIndex:(collectionId,indexId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),deleteIndex:(collectionId,indexId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
if(typeof indexId==='undefined'){throw new AppwriteException('Missing required parameter: "indexId"');}
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);})};this.functions={list:(search,limit,offset,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
let path='/database/collections/{collectionId}/indexes/{indexId}'.replace('{collectionId}',collectionId).replace('{indexId}',indexId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listCollectionLogs:(collectionId)=>__awaiter(this,void 0,void 0,function*(){if(typeof collectionId==='undefined'){throw new AppwriteException('Missing required parameter: "collectionId"');}
let path='/database/collections/{collectionId}/logs'.replace('{collectionId}',collectionId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);})};this.functions={list:(search,limit,offset,orderType)=>__awaiter(this,void 0,void 0,function*(){let path='/functions';let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
if(typeof orderType!=='undefined'){payload['orderType']=orderType;}

View file

@ -111,13 +111,15 @@ result+'")": '+
error);}
if(debug){console.info("debug-ls-if result:",result);}
paths=expression.getPaths();let prv=element.$lsSkip;element.$lsSkip=!result;if(!result){element.style.visibility="hidden";element.style.display="none";}else{element.style.removeProperty("display");element.style.removeProperty("visibility");}
if(prv===true&&element.$lsSkip===false){view.render(element);}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split(".");while(path.length){container.bind(element,path.join("."),check);path.pop();}}},});window.ls.container.get("view").add({selector:"data-ls-loop",template:false,nested:false,controller:function(element,view,container,window,expression){let expr=expression.parse(element.getAttribute("data-ls-loop"));let as=element.getAttribute("data-ls-as");let key=element.getAttribute("data-ls-key")||"$index";let limit=parseInt(expression.parse(element.getAttribute("data-limit")||"")||-1);let debug=element.getAttribute("data-debug")||false;let echo=function(){let array=container.path(expr);let counter=0;array=!array?[]:array;let watch=!!(array&&array.__proxy);while(element.hasChildNodes()){element.removeChild(element.lastChild);element.lastChild=null;}
if(prv===true&&element.$lsSkip===false){view.render(element);}};check();for(let i=0;i<paths.length;i++){let path=paths[i].split(".");while(path.length){container.bind(element,path.join("."),check);path.pop();}}},});window.ls.container.get("view").add({selector:"data-ls-loop",template:false,nested:false,controller:function(element,view,container,filter,window,expression){let expr=expression.parse(element.getAttribute("data-ls-loop"));let as=element.getAttribute("data-ls-as");let filterName=element.getAttribute("data-ls-filter");let key=element.getAttribute("data-ls-key")||"$index";let prefix=element.getAttribute("data-ls-prefix")||null;let postfix=element.getAttribute("data-ls-postfix")||null;let limit=parseInt(expression.parse(element.getAttribute("data-ls-limit")||"")||-1);let debug=element.getAttribute("data-debug")||false;let echo=function(){let array=container.path(expr);let counter=0;array=!array?[]:array;if(filterName){array=filter.apply(filterName,array);}
let watch=!!(array&&array.__proxy);while(element.hasChildNodes()){element.removeChild(element.lastChild);element.lastChild=null;}
if(array instanceof Array&&typeof array!=="object"){throw new Error("Reference value must be array or object. "+typeof array+" given");}
let children=[];element.$lsSkip=true;element.style.visibility=0===array.length&&element.style.visibility==""?"hidden":"visible";for(let prop in array){if(counter==limit){break;}
let children=[];element.$lsSkip=true;element.style.visibility=0===array.length&&element.style.visibility==""?"hidden":"visible";if(prefix){prefixElement=document.getElementById(prefix);let html=prefixElement.innerHTML;prefixElement=template.cloneNode(true);if(prefixElement){prefixElement.innerHTML=html;element.appendChild(prefixElement);view.render(prefixElement);}}
for(let prop in array){if(counter==limit){break;}
counter++;if(!array.hasOwnProperty(prop)){continue;}
children[prop]=template.cloneNode(true);element.appendChild(children[prop]);((index)=>{let context=expr+"."+index;container.addNamespace(as,context);if(debug){console.info("debug-ls-loop","index",index);console.info("debug-ls-loop","context",context);console.info("debug-ls-loop","context-path",container.path(context).name);console.info("debug-ls-loop","namespaces",container.namespaces);}
container.set(as,container.path(context),true,watch);container.set(key,index,true,false);view.render(children[prop]);container.removeNamespace(as);})(prop);}
element.dispatchEvent(new Event("looped"));};let template=element.children.length===1?element.children[0]:window.document.createElement("li");echo();container.bind(element,expr+".length",echo);let path=(expr+".length").split(".");while(path.length){container.bind(element,path.join("."),echo);path.pop();}},});window.ls.container.get("view").add({selector:"data-ls-template",template:false,controller:function(element,view,http,expression,document,container){let template=element.getAttribute("data-ls-template")||"";let type=element.getAttribute("data-type")||"url";let debug=element.getAttribute("data-debug")||false;let paths=[];let check=function(init=false){let source=expression.parse(template);paths=expression.getPaths();element.innerHTML="";if("script"===type){let inlineTemplate=document.getElementById(source);if(inlineTemplate&&inlineTemplate.innerHTML){element.innerHTML=inlineTemplate.innerHTML;element.dispatchEvent(new CustomEvent("template-loaded",{bubbles:true,cancelable:false,}));}else{if(debug){console.error('Missing template "'+source+'"');}}
element.dispatchEvent(new Event("looped"));};let template=element.children.length>=1?element.children[0]:window.document.createElement("li");echo();container.bind(element,expr+".length",echo);let path=(expr+".length").split(".");while(path.length){container.bind(element,path.join("."),echo);path.pop();}},});window.ls.container.get("view").add({selector:"data-ls-template",template:false,controller:function(element,view,http,expression,document,container){let template=element.getAttribute("data-ls-template")||"";let type=element.getAttribute("data-type")||"url";let debug=element.getAttribute("data-debug")||false;let paths=[];let check=function(init=false){let source=expression.parse(template);paths=expression.getPaths();element.innerHTML="";if("script"===type){let inlineTemplate=document.getElementById(source);if(inlineTemplate&&inlineTemplate.innerHTML){element.innerHTML=inlineTemplate.innerHTML;element.dispatchEvent(new CustomEvent("template-loaded",{bubbles:true,cancelable:false,}));}else{if(debug){console.error('Missing template "'+source+'"');}}
if(!init){view.render(element);}
return;}
http.get(source).then((function(element){return function(data){element.innerHTML=data;view.render(element);element.dispatchEvent(new CustomEvent("template-loaded",{bubbles:true,cancelable:false,}));};})(element),function(){throw new Error("Failed loading template");});};check(true);for(let i=0;i<paths.length;i++){let path=paths[i].split(".");while(path.length){container.bind(element,path.join("."),check);path.pop();}}},});window.ls.error=function(){return function(error){window.console.error(error);if(window.location.pathname!=='/console'){window.location='/console';}};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){var subscribe=document.getElementById('newsletter').checked;if(subscribe){let alerts=container.get('alerts');let loaderId=alerts.add({text:'Loading...',class:""},0);fetch('https://appwrite.io/v1/newsletter/subscribe',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:form.name,email:form.email,}),}).finally(function(){alerts.remove(loaderId);window.location='/console';});}else{window.location='/console';}},function(error){window.location='/auth/signup?failure=1';});});(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;message.remove=function(){scope.remove(message.id);};scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
@ -292,7 +294,10 @@ return $value;}).add("platformsLimit",function($value){return $value;}).add("lim
return $value.join(", ").replace(/,\s([^,]+)$/,' and $1');}).add("runtimeName",function($value,env){if(env&&env.RUNTIMES&&env.RUNTIMES[$value]){return env.RUNTIMES[$value].name;}
return'';}).add("runtimeLogo",function($value,env){if(env&&env.RUNTIMES&&env.RUNTIMES[$value]){return env.RUNTIMES[$value].logo;}
return'';}).add("runtimeVersion",function($value,env){if(env&&env.RUNTIMES&&env.RUNTIMES[$value]){return env.RUNTIMES[$value].version;}
return'';});function abbreviate(number,maxPlaces,forcePlaces,forceLetter){number=Number(number);forceLetter=forceLetter||false;if(forceLetter!==false){return annotate(number,maxPlaces,forcePlaces,forceLetter);}
return'';}).add("indexAttributes",function($value){let output='';for(let i=0;i<$value.attributes.length;i++){output+=$value.attributes[i]+' ('+$value.orders[i]+'), '}
return output.slice(0,-2);}).add("collectionAttributes",function($value){if(!Array.isArray($value)){return[];}
$value.unshift({$id:'$id'});return $value;}).add("documentAttribute",function($value,attribute){if($value[attribute.$id]){return $value[attribute.$id];}
return null;});function abbreviate(number,maxPlaces,forcePlaces,forceLetter){number=Number(number);forceLetter=forceLetter||false;if(forceLetter!==false){return annotate(number,maxPlaces,forcePlaces,forceLetter);}
let abbr;if(number>=1e12){abbr="T";}else if(number>=1e9){abbr="B";}else if(number>=1e6){abbr="M";}else if(number>=1e3){abbr="K";}else{abbr="";}
return annotate(number,maxPlaces,forcePlaces,abbr);}
function annotate(number,maxPlaces,forcePlaces,abbr){let rounded=0;switch(abbr){case"T":rounded=number/1e12;break;case"B":rounded=number/1e9;break;case"M":rounded=number/1e6;break;case"K":rounded=number/1e3;break;case"":rounded=number;break;}
@ -328,7 +333,7 @@ running=false;element.style.backgroud='transparent';element.classList.add("load-
container.set("serviceData",data,true,true);container.set("serviceForm",formData,true,true);for(let i=0;i<parsedSuccess.length;i++){container.resolve(resolve(callbacks[parsedSuccess[i]],"successParam"+
parsedSuccess[i].charAt(0).toUpperCase()+
parsedSuccess[i].slice(1),{}));}
container.set("serviceData",null,true,true);container.set("serviceForm",null,true,true);element.$lsSkip=false;view.render(element);},function(exception){if(loaderId!==null){alerts.remove(loaderId);}
container.set("serviceData",null,true,true);container.set("serviceForm",null,true,true);element.$lsSkip=false;view.render(element);},function(exception){console.error(exception);if(loaderId!==null){alerts.remove(loaderId);}
if(!element){return;}
running=false;element.style.backgroud='transparent';element.classList.add("load-service-end");for(let i=0;i<parsedFailure.length;i++){container.resolve(resolve(callbacks[parsedFailure[i]],"failureParam"+
parsedFailure[i].charAt(0).toUpperCase()+
@ -342,8 +347,9 @@ let doNotTrack=window.navigator.doNotTrack;if(doNotTrack=='1'){return;}
let project=router.params["project"]||'None';ga("set","page",window.location.pathname);ga("set","dimension1",project);ga('set','dimension2',env.VERSION);ga('set','dimension3',env.SETUP);ga("send","pageview");}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-clone",controller:function(element,document,view){var template=element.innerHTML.toString();var label=element.dataset["label"]||"Add";var icon=element.dataset["icon"]||null;var target=element.dataset["target"]||null;var first=parseInt(element.dataset["first"]||1);var button=document.createElement("button");button.type="button";button.innerText=" "+label+" ";button.classList.add("margin-end");button.classList.add("margin-bottom-small");button.classList.add("reverse");if(icon){var iconElement=document.createElement("i");iconElement.className=icon;button.insertBefore(iconElement,button.firstChild);}
if(target){target=document.getElementById(target);}
button.addEventListener("click",function(){var clone=document.createElement(element.tagName);if(element.name){clone.name=element.name;}
clone.innerHTML=template;clone.className=element.className;view.render(clone);if(target){target.appendChild(clone);}else{button.parentNode.insertBefore(clone,button);}
clone.querySelector("input").focus();Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-add",repeat:false,controller:function(element,view,container,document){for(var i=0;i<element.children.length;i++){let button=document.createElement("button");let template=element.children[i].cloneNode(true);let as=element.getAttribute('data-ls-as');let counter=0;button.type="button";button.innerText="Add";button.classList.add("reverse");button.classList.add("margin-end-small");button.addEventListener('click',function(){container.addNamespace(as,'new-'+counter++);console.log(container.namespaces,container.get(as),as);container.set(as,null,true,true);let child=template.cloneNode(true);view.render(child);element.appendChild(child);element.style.visibility='visible';let inputs=child.querySelectorAll('input,textarea');for(let index=0;index<inputs.length;++index){if(inputs[index].type!=='hidden'){inputs[index].focus();break;}}});element.after(button);}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-chart",controller:function(element,container,date,document){let wrapper=document.createElement("div");let child=document.createElement("canvas");let sources=element.getAttribute('data-forms-chart');let width=element.getAttribute('data-width')||500;let height=element.getAttribute('data-height')||175;let colors=(element.getAttribute('data-colors')||'blue,green,orange,red').split(',');let themes={'blue':'#29b5d9','green':'#4eb55b','orange':'#fba233','red':'#dc3232',};let range={'24h':'H:i','7d':'d F Y','30d':'d F Y','90d':'d F Y'}
clone.innerHTML=template;clone.className=element.className;var input=clone.querySelector("input, select, textarea");view.render(clone);if(target){target.appendChild(clone);}else{button.parentNode.insertBefore(clone,button);}
if(input){input.focus();}
Array.prototype.slice.call(clone.querySelectorAll("[data-remove]")).map(function(obj){obj.addEventListener("click",function(){clone.parentNode.removeChild(clone);obj.scrollIntoView({behavior:"smooth"});});});Array.prototype.slice.call(clone.querySelectorAll("[data-up]")).map(function(obj){obj.addEventListener("click",function(){if(clone.previousElementSibling){clone.parentNode.insertBefore(clone,clone.previousElementSibling);obj.scrollIntoView({behavior:"smooth"});}});});Array.prototype.slice.call(clone.querySelectorAll("[data-down]")).map(function(obj){obj.addEventListener("click",function(){if(clone.nextElementSibling){clone.parentNode.insertBefore(clone.nextElementSibling,clone);obj.scrollIntoView({behavior:"smooth"});}});});});element.parentNode.insertBefore(button,element.nextSibling);element.parentNode.removeChild(element);button.form.addEventListener('reset',function(event){target.innerHTML='';if(first){button.click();}});if(first){button.click();}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-add",repeat:false,controller:function(element,view,container,document){for(var i=0;i<element.children.length;i++){let button=document.createElement("button");let template=element.children[i].cloneNode(true);let as=element.getAttribute('data-ls-as');let counter=0;button.type="button";button.innerText="Add";button.classList.add("reverse");button.classList.add("margin-end-small");button.addEventListener('click',function(){container.addNamespace(as,'new-'+counter++);console.log(container.namespaces,container.get(as),as);container.set(as,null,true,true);let child=template.cloneNode(true);view.render(child);element.appendChild(child);element.style.visibility='visible';let inputs=child.querySelectorAll('input,textarea');for(let index=0;index<inputs.length;++index){if(inputs[index].type!=='hidden'){inputs[index].focus();break;}}});element.after(button);}}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-chart",controller:function(element,container,date,document){let wrapper=document.createElement("div");let child=document.createElement("canvas");let sources=element.getAttribute('data-forms-chart');let width=element.getAttribute('data-width')||500;let height=element.getAttribute('data-height')||175;let colors=(element.getAttribute('data-colors')||'blue,green,orange,red').split(',');let themes={'blue':'#29b5d9','green':'#4eb55b','orange':'#fba233','red':'#dc3232',};let range={'24h':'H:i','7d':'d F Y','30d':'d F Y','90d':'d F Y'}
element.parentNode.insertBefore(wrapper,element.nextSibling);wrapper.classList.add('content');child.width=width;child.height=height;sources=sources.split(',');wrapper.appendChild(child);let chart=null;let check=function(){let config={type:"line",data:{labels:[],datasets:[]},options:{responsive:true,title:{display:false,text:"Stats"},legend:{display:false},tooltips:{mode:"index",intersect:false,caretPadding:0},hover:{mode:"nearest",intersect:true},scales:{xAxes:[{display:false}],yAxes:[{display:false}]}}};for(let i=0;i<sources.length;i++){let label=sources[i].substring(0,sources[i].indexOf('='));let path=sources[i].substring(sources[i].indexOf('=')+1);let data=container.path(path);let value=JSON.parse(element.value);config.data.labels[i]=label;config.data.datasets[i]={};config.data.datasets[i].label=label;config.data.datasets[i].borderColor=themes[colors[i]];config.data.datasets[i].backgroundColor=themes[colors[i]]+'36';config.data.datasets[i].borderWidth=2;config.data.datasets[i].data=[0,0,0,0,0,0,0];config.data.datasets[i].fill=true;if(!data){return;}
let dateFormat=(value.range&&range[value.range])?range[value.range]:'d F Y';for(let x=0;x<data.length;x++){config.data.datasets[i].data[x]=data[x].value;config.data.labels[x]=date.format(dateFormat,data[x].date);}}
if(chart){chart.destroy();}
@ -368,9 +374,11 @@ element.setAttribute("data-id-type",idType);info.innerHTML="Appwrite will genera
button.className=idType=="custom"?"icon-shuffle copy":"icon-edit copy";}
const syncEditorWithID=function(event){if(element.value!=='unique()'||idType!='auto'){writer.value=element.value;}
if(idType=='auto'){element.value='unique()';}}
const keypress=function(e){const key=e.which||e.keyCode;const ZERO=48;const NINE=57;const SMALL_A=97;const SMALL_Z=122;const CAPITAL_A=65;const CAPITAL_Z=90;const UNDERSCORE=95;const isNotValidDigit=key<ZERO||key>NINE;const isNotValidSmallAlphabet=key<SMALL_A||key>SMALL_Z;const isNotValidCapitalAlphabet=key<CAPITAL_A||key>CAPITAL_Z;if(key==UNDERSCORE&&e.target.value.length==0){e.preventDefault();}
if(key!=UNDERSCORE&&isNotValidDigit&&isNotValidSmallAlphabet&&isNotValidCapitalAlphabet){e.preventDefault();}}
syncEditorWithID();setIdType(idType);writer.addEventListener("change",function(event){element.value=writer.value;});writer.form.addEventListener('reset',function(event){const resetEvent=new Event('reset');element.dispatchEvent(resetEvent);});element.addEventListener('reset',function(event){idType=element.getAttribute('data-id-type');setIdType(idType);});writer.addEventListener('keypress',keypress);button.addEventListener("click",switchType);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-document",controller:function(element,container,search){var formsDocument=(element.dataset["formsDocument"]||'');var searchButton=(element.dataset["search"]||0);let path=container.scope(searchButton);element.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent(formsDocument,{bubbles:false,cancelable:true}));});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-document-preview",controller:function(element,container,search){element.addEventListener('change',function(){console.log(element.value);});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-filter",controller:function(document,container,expression,element,form,di){let name=element.dataset["formsFilter"]||"";let events=element.dataset["event"]||"";let serialize=function(obj,prefix){let str=[],p;for(p in obj){if(obj.hasOwnProperty(p)){let k=prefix?prefix+"["+p+"]":p,v=obj[p];if(v===""){continue;}
const keypress=function(e){const key=e.which||e.keyCode;const ZERO=48;const NINE=57;const SMALL_A=97;const SMALL_Z=122;const CAPITAL_A=65;const CAPITAL_Z=90;const UNDERSCORE=95;const HYPHEN=45;const PERIOD=46;const isNotValidDigit=key<ZERO||key>NINE;const isNotValidSmallAlphabet=key<SMALL_A||key>SMALL_Z;const isNotValidCapitalAlphabet=key<CAPITAL_A||key>CAPITAL_Z;const isNotValidFirstChar=(key===UNDERSCORE||key===HYPHEN||key===PERIOD);if(isNotValidFirstChar&&e.target.value.length==0){e.preventDefault();}
if(key!=UNDERSCORE&&key!=HYPHEN&&key!=PERIOD&&isNotValidDigit&&isNotValidSmallAlphabet&&isNotValidCapitalAlphabet){e.preventDefault();}}
syncEditorWithID();setIdType(idType);writer.addEventListener("change",function(event){element.value=writer.value;});writer.form.addEventListener('reset',function(event){const resetEvent=new Event('reset');element.dispatchEvent(resetEvent);});element.addEventListener('reset',function(event){idType=element.getAttribute('data-id-type');setIdType(idType);});writer.addEventListener('keypress',keypress);button.addEventListener("click",switchType);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-document",controller:function(element,container,search){var formsDocument=(element.dataset["formsDocument"]||'');var searchButton=(element.dataset["search"]||0);let path=container.scope(searchButton);element.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent(formsDocument,{bubbles:false,cancelable:true}));});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-duplications",controller:function(element){let validate=function(element){let duplication=0;let form=element.form;for(let i=0;i<form.elements.length;i++){let field=form.elements[i];if(field.name===element.name&&field.value===element.value){duplication++;}}
if(duplication>1){element.setCustomValidity("Duplicated value");}
else{element.setCustomValidity("");}};element.addEventListener('change',function(event){validate(event.target)});element.addEventListener('focus',function(event){validate(event.target)});element.addEventListener('blur',function(event){validate(event.target)});}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-document-preview",controller:function(element,container,search){element.addEventListener('change',function(){console.log(element.value);});}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-forms-filter",controller:function(document,container,expression,element,form,di){let name=element.dataset["formsFilter"]||"";let events=element.dataset["event"]||"";let serialize=function(obj,prefix){let str=[],p;for(p in obj){if(obj.hasOwnProperty(p)){let k=prefix?prefix+"["+p+"]":p,v=obj[p];if(v===""){continue;}
str.push(v!==null&&typeof v==="object"?serialize(v,k):encodeURIComponent(k)+"="+encodeURIComponent(v));}}
return str.join("&");};let parse=function(filter){if(filter===""){return null;}
let operatorsMap=["!=",">=","<=","=",">","<"];let operator=null;for(let key=0;key<operatorsMap.length;key++){if(filter.indexOf(operatorsMap[key])>-1){operator=operatorsMap[key];}}
@ -405,8 +413,9 @@ element.addEventListener("keyup",resize);element.addEventListener("change",resiz
var file=document.createElement("li");var image=document.createElement("img");image.src=image.src=env.API+"/storage/files/"+
result+"/preview?width="+
previewWidth+"&height="+
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile(files[0],read,write,1).then(function(response){onComplete(message);render(response.$id);},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
render(element.value);wrapper.scrollIntoView();});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);if(searchButton){let searchOpen=document.createElement("button");searchOpen.type='button';searchOpen.innerHTML='<i class="icon icon-search"></i> Search';searchOpen.classList.add('reverse');let path=container.scope(searchButton);searchOpen.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent("open-file-serach",{bubbles:false,cancelable:true}));});wrapper.appendChild(searchOpen);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-cookies",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookies"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",label:'Learn More',callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
previewHeight+"&project="+project+"&mode=admin";image.alt=previewAlt;file.className="file avatar";file.tabIndex=0;file.appendChild(image);preview.appendChild(file);var remove=(function(result){return function(event){render(result.$id);element.value='';};})(result);file.addEventListener("click",remove);file.addEventListener("keypress",remove);element.value=result;};input.addEventListener("change",function(){var message=alerts.add({text:labelLoading,class:""},0);var files=input.files;var read=JSON.parse(expression.parse(element.dataset["read"]||"[]"));var write=JSON.parse(expression.parse(element.dataset["write"]||"[]"));sdk.storage.createFile('unique()',files[0],read,write,1).then(function(response){onComplete(message);render(response.$id);},function(error){alerts.add({text:"An error occurred!",class:""},3000);onComplete(message);});input.disabled=true;});element.addEventListener("change",function(){if(!element.value){return;}
render(element.value);wrapper.scrollIntoView();});upload.addEventListener("keypress",function(){input.click();});element.parentNode.insertBefore(wrapper,element);wrapper.appendChild(preview);wrapper.appendChild(progress);wrapper.appendChild(upload);upload.appendChild(input);render(output);if(searchButton){let searchOpen=document.createElement("button");searchOpen.type='button';searchOpen.innerHTML='<i class="icon icon-search"></i> Search';searchOpen.classList.add('reverse');let path=container.scope(searchButton);searchOpen.addEventListener('click',function(){search.selected=element.value;search.path=path;document.dispatchEvent(new CustomEvent("open-file-serach",{bubbles:false,cancelable:true}));});wrapper.appendChild(searchOpen);}}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-cookies",controller:function(element,alerts,cookie,env){if(!cookie.get("cookie-alert")){let text=element.dataset["cookies"]||"";alerts.add({text:text,class:"cookie-alert",link:env.HOME+"/policy/cookies",label:'Learn More',callback:function(){cookie.set("cookie-alert","true",365*10);}},0);}}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-copy',repeat:false,controller:function(document,element,alerts){let button=document.createElement("i");button.type="button";button.title="Copy to Clipboard";button.className=element.getAttribute("data-class")||"icon-docs note copy";button.style.cursor="pointer";element.parentNode.insertBefore(button,element.nextSibling);let copy=function(event){window.getSelection().removeAllRanges();let range=document.createRange();range.selectNode(element);window.getSelection().addRange(range);try{document.execCommand("copy");alerts.add({text:"Copied to clipboard",class:""},3000);}catch(err){alerts.add({text:"Failed to copy text ",class:"error"},3000);}
window.getSelection().removeAllRanges();};button.addEventListener("click",copy);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-page-title",repeat:true,controller:function(element,document,expression){document.title=expression.parse(element.getAttribute("data-page-title"))||document.title;}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-to',repeat:false,controller:function(element,window){let button=window.document.createElement('button');button.className='scroll-to icon-up-dir';button.alt='Back To Top';button.title='Back To Top';button.addEventListener('click',function(){element.scrollIntoView(true,{behavior:'smooth'});button.blur();},false);element.appendChild(button);}});})(window);(function(window){"use strict";window.ls.view.add({selector:'data-general-scroll-direction',repeat:false,controller:function(element,window){let position=0;let check=function(){let direction=window.document.documentElement.scrollTop;if(direction>position){element.classList.remove('scroll-to-top')
element.classList.add('scroll-to-bottom')}
else{element.classList.remove('scroll-to-bottom')
element.classList.add('scroll-to-top')}
@ -442,12 +451,12 @@ if(buttonSelector){let buttonElements=element.querySelectorAll(buttonSelector);b
element.addEventListener('click',function(event){let targetA=findParent('a',event.target);let targetB=findParent('button',event.target);if(!targetA&&!targetB){return false;}
if(targetA&&!targetA.href){return false;}
if(targetB&&!targetB.classList.contains('link')){return false;}
closeDelay();});element.insertBefore(button,element.firstChild);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-phases",controller:function(element,window,document,expression,router){var tabs=document.createElement("ul");var container=document.createElement("div");var titles=Array.prototype.slice.call(element.querySelectorAll("li > h1"));var next=Array.prototype.slice.call(element.querySelectorAll("[data-next]"));var previous=Array.prototype.slice.call(element.querySelectorAll("[data-previous]"));var position=0;var init=false;if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h2"));}
closeDelay();});element.insertBefore(button,element.firstChild);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ui-phases",controller:function(element,window,document,expression,router,view){var tabs=document.createElement("ul");var container=document.createElement("div");var titles=Array.prototype.slice.call(element.querySelectorAll("li > h1"));var next=Array.prototype.slice.call(element.querySelectorAll("[data-next]"));var previous=Array.prototype.slice.call(element.querySelectorAll("[data-previous]"));var position=0;var init=false;if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h2"));}
if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h3"));}
if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h4"));}
if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h5"));}
if(titles.length===0){titles=Array.prototype.slice.call(element.querySelectorAll("li > h6"));}
for(var i=0;i<element.children.length;i++){var tabState=expression.parse(element.children[i].dataset["state"]||"");if(tabState&&tabState===(window.location.pathname+window.location.search).substring(0,tabState.length)){position=i;}}
var setTab=function(index){var tabState=expression.parse(element.children[index].dataset["state"]||"");var url="";if(tabState!==""&&tabState!==window.location.pathname+window.location.search){var parser=document.createElement("a");parser.href=tabState;url=!init?parser.pathname+window.location.search:tabState;if(position!=index){window.history.pushState({},"",url);router.reset();}}
element.children[position].classList.remove("selected");element.children[index].classList.add("selected");tabs.children[position].classList.remove("selected");tabs.children[index].classList.add("selected");position=index;document.dispatchEvent(new CustomEvent("tab-changed"));init=true;};tabs.classList.add("tabs");container.classList.add("container");container.classList.add("close");container.dataset["lsUiOpen"]="";container.dataset["buttonClass"]="icon icon-down-dir";titles.map(function(obj,i){var title=document.createElement("li");title.innerHTML=obj.innerHTML;title.className=obj.className;title.tabIndex=0;tabs.appendChild(title);title.addEventListener("click",function(){setTab(i);});title.addEventListener("keyup",function(){if(event.which===13){setTab(i);}});});next.map(function(obj){obj.addEventListener("click",function(){setTab(position+1);});});previous.map(function(obj){obj.addEventListener("click",function(){setTab(position-1);});});setTab(position);container.appendChild(tabs);element.parentNode.insertBefore(container,element);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-trigger",controller:function(element,document,expression){let trigger=expression.parse(element.dataset["lsUiTrigger"]||'').trim().split(',');let event=expression.parse(element.dataset["event"]||'click');let debug=element.getAttribute('data-debug')||false;for(let index=0;index<trigger.length;index++){let name=trigger[index];element.addEventListener(event,function(){if(debug){console.log('Debug: event triggered: '+name);}
element.children[position].classList.remove("selected");element.children[index].classList.add("selected");tabs.children[position].classList.remove("selected");tabs.children[index].classList.add("selected");position=index;document.dispatchEvent(new CustomEvent("tab-changed"));init=true;};tabs.classList.add("tabs");container.classList.add("container");container.classList.add("close");container.dataset["lsUiOpen"]="";container.dataset["buttonClass"]="icon icon-down-dir";titles.map(function(obj,i){var title=document.createElement("li");title.innerHTML=obj.innerHTML;title.className=obj.className;title.tabIndex=0;tabs.appendChild(title);title.addEventListener("click",function(){setTab(i);});title.addEventListener("keyup",function(){if(event.which===13){setTab(i);}});view.render(title);});next.map(function(obj){obj.addEventListener("click",function(){setTab(position+1);});});previous.map(function(obj){obj.addEventListener("click",function(){setTab(position-1);});});setTab(position);container.appendChild(tabs);element.parentNode.insertBefore(container,element);}});})(window);(function(window){window.ls.container.get("view").add({selector:"data-ls-ui-trigger",controller:function(element,document,expression){let trigger=expression.parse(element.dataset["lsUiTrigger"]||'').trim().split(',');let event=expression.parse(element.dataset["event"]||'click');let debug=element.getAttribute('data-debug')||false;for(let index=0;index<trigger.length;index++){let name=trigger[index];element.addEventListener(event,function(){if(debug){console.log('Debug: event triggered: '+name);}
document.dispatchEvent(new CustomEvent(name));});}}});})(window);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1636,19 +1636,19 @@
*
*
* @param {string} collectionId
* @param {string} id
* @param {string} indexId
* @param {string} type
* @param {string[]} attributes
* @param {string[]} orders
* @throws {AppwriteException}
* @returns {Promise}
*/
createIndex: (collectionId, id, type, attributes, orders) => __awaiter(this, void 0, void 0, function* () {
createIndex: (collectionId, indexId, type, attributes, orders) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
if (typeof id === 'undefined') {
throw new AppwriteException('Missing required parameter: "id"');
if (typeof indexId === 'undefined') {
throw new AppwriteException('Missing required parameter: "indexId"');
}
if (typeof type === 'undefined') {
throw new AppwriteException('Missing required parameter: "type"');
@ -1658,8 +1658,8 @@
}
let path = '/database/collections/{collectionId}/indexes'.replace('{collectionId}', collectionId);
let payload = {};
if (typeof id !== 'undefined') {
payload['id'] = id;
if (typeof indexId !== 'undefined') {
payload['indexId'] = indexId;
}
if (typeof type !== 'undefined') {
payload['type'] = type;
@ -1670,6 +1670,8 @@
if (typeof orders !== 'undefined') {
payload['orders'] = orders;
}
console.log(collectionId, indexId, type, attributes, orders);
const uri = new URL(this.config.endpoint + path);
return yield this.call('post', uri, {
'content-type': 'application/json',
@ -1720,6 +1722,26 @@
return yield this.call('delete', uri, {
'content-type': 'application/json',
}, payload);
}),
/**
* Get Collection Logs
*
* Get the collection activity logs list by its unique ID.
*
* @param {string} collectionId
* @throws {AppwriteException}
* @returns {Promise}
*/
listCollectionLogs: (collectionId) => __awaiter(this, void 0, void 0, function* () {
if (typeof collectionId === 'undefined') {
throw new AppwriteException('Missing required parameter: "collectionId"');
}
let path = '/database/collections/{collectionId}/logs'.replace('{collectionId}', collectionId);
let payload = {};
const uri = new URL(this.config.endpoint + path);
return yield this.call('get', uri, {
'content-type': 'application/json',
}, payload);
})
};
this.functions = {
@ -4174,7 +4196,7 @@
/**
* Get User Logs
*
* Get a user activity logs list by its unique ID.
* Get the user activity logs list by its unique ID.
*
* @param {string} userId
* @throws {AppwriteException}
@ -4522,4 +4544,4 @@
Object.defineProperty(exports, '__esModule', { value: true });
}(this.window = this.window || {}, null, window));
}(this.window = this.window || {}, null, window));

View file

@ -1121,18 +1121,26 @@ window.ls.container.get("view").add({
selector: "data-ls-loop",
template: false,
nested: false,
controller: function (element, view, container, window, expression) {
controller: function (element, view, container, filter, window, expression) {
let expr = expression.parse(element.getAttribute("data-ls-loop"));
let as = element.getAttribute("data-ls-as");
let filterName = element.getAttribute("data-ls-filter");
let key = element.getAttribute("data-ls-key") || "$index";
let prefix = element.getAttribute("data-ls-prefix") || null;
let postfix = element.getAttribute("data-ls-postfix") || null;
let limit = parseInt(
expression.parse(element.getAttribute("data-limit") || "") || -1
expression.parse(element.getAttribute("data-ls-limit") || "") || -1
);
let debug = element.getAttribute("data-debug") || false;
let echo = function () {
let array = container.path(expr);
let counter = 0;
array = !array ? [] : array;
if(filterName) {
array = filter.apply(filterName, array);
}
let watch = !!(array && array.__proxy);
while (element.hasChildNodes()) {
element.removeChild(element.lastChild);
@ -1149,6 +1157,18 @@ window.ls.container.get("view").add({
0 === array.length && element.style.visibility == ""
? "hidden"
: "visible";
if(prefix) {
prefixElement = document.getElementById(prefix);
let html = prefixElement.innerHTML;
prefixElement = template.cloneNode(true);
if(prefixElement) {
prefixElement.innerHTML = html;
element.appendChild(prefixElement);
view.render(prefixElement);
}
}
for (let prop in array) {
if (counter == limit) {
break;
@ -1181,7 +1201,7 @@ window.ls.container.get("view").add({
element.dispatchEvent(new Event("looped"));
};
let template =
element.children.length === 1
element.children.length >= 1
? element.children[0]
: window.document.createElement("li");
echo();

View file

@ -255,6 +255,32 @@ window.ls.filter
return '';
})
.add("indexAttributes", function($value) {
let output = '';
for(let i = 0; i < $value.attributes.length; i++) {
output += $value.attributes[i] + ' (' + $value.orders[i] + '), '
}
return output.slice(0, -2);
})
.add("collectionAttributes", function($value) {
if(!Array.isArray($value)) {
return [];
}
$value.unshift({
$id: '$id'
});
return $value;
})
.add("documentAttribute", function($value, attribute) {
if($value[attribute.$id]) {
return $value[attribute.$id];
}
return null;
})
;
function abbreviate(number, maxPlaces, forcePlaces, forceLetter) {

View file

@ -38,6 +38,8 @@
clone.innerHTML = template;
clone.className = element.className;
var input = clone.querySelector("input, select, textarea");
view.render(clone);
if (target) {
@ -46,7 +48,9 @@
button.parentNode.insertBefore(clone, button);
}
clone.querySelector("input").focus();
if(input) {
input.focus();
}
Array.prototype.slice
.call(clone.querySelectorAll("[data-remove]"))
@ -87,6 +91,15 @@
element.parentNode.removeChild(element);
button.form.addEventListener('reset', function (event) {
target.innerHTML = '';
if (first) {
button.click();
}
});
if (first) {
button.click();
}

View file

@ -114,16 +114,19 @@
const CAPITAL_A = 65;
const CAPITAL_Z = 90;
const UNDERSCORE = 95;
const HYPHEN = 45;
const PERIOD = 46;
const isNotValidDigit = key < ZERO || key > NINE;
const isNotValidSmallAlphabet = key < SMALL_A || key > SMALL_Z;
const isNotValidCapitalAlphabet = key < CAPITAL_A || key > CAPITAL_Z;
const isNotValidFirstChar = (key === UNDERSCORE || key === HYPHEN || key === PERIOD);
//Leading underscore is prevented
if (key == UNDERSCORE && e.target.value.length == 0) {
if ( isNotValidFirstChar && e.target.value.length == 0) {
e.preventDefault();
}
if (key != UNDERSCORE && isNotValidDigit && isNotValidSmallAlphabet && isNotValidCapitalAlphabet) {
if (key != UNDERSCORE && key != HYPHEN && key != PERIOD && isNotValidDigit && isNotValidSmallAlphabet && isNotValidCapitalAlphabet) {
e.preventDefault();
}
}

View file

@ -0,0 +1,39 @@
(function (window) {
"use strict";
window.ls.container.get("view").add({
selector: "data-duplications",
controller: function (element) {
let validate = function (element) {
let duplication = 0;
let form = element.form;
for (let i = 0; i < form.elements.length; i++) {
let field = form.elements[i];
if(field.name === element.name && field.value === element.value) {
duplication++;
}
}
if(duplication > 1) { // self + another element with same name and value
element.setCustomValidity("Duplicated value");
}
else {
element.setCustomValidity("");
}
};
element.addEventListener('change', function(event) {
validate(event.target)
});
element.addEventListener('focus', function(event) {
validate(event.target)
});
element.addEventListener('blur', function(event) {
validate(event.target)
});
}
});
})(window);

View file

@ -108,7 +108,7 @@
expression.parse(element.dataset["write"] || "[]")
);
sdk.storage.createFile(files[0], read, write, 1).then(
sdk.storage.createFile('unique()', files[0], read, write, 1).then(
function(response) {
onComplete(message);

View file

@ -0,0 +1,44 @@
(function(window) {
"use strict";
window.ls.view.add({
selector: 'data-general-copy',
repeat: false,
controller: function(document, element, alerts) {
let button = document.createElement("i");
button.type = "button";
button.title = "Copy to Clipboard";
button.className = element.getAttribute("data-class") || "icon-docs note copy";
button.style.cursor = "pointer";
element.parentNode.insertBefore(button, element.nextSibling);
let copy = function(event) {
window.getSelection().removeAllRanges();
let range = document.createRange();
range.selectNode(element);
window.getSelection().addRange(range);
try {
document.execCommand("copy");
alerts.add({
text: "Copied to clipboard",
class: ""
}, 3000);
} catch (err) {
alerts.add({
text: "Failed to copy text ",
class: "error"
}, 3000);
}
window.getSelection().removeAllRanges();
};
button.addEventListener("click", copy);
}
});
})(window);

View file

@ -373,6 +373,7 @@
view.render(element);
},
function(exception) {
console.error(exception);
if (loaderId !== null) {
// Remove loader if needed
alerts.remove(loaderId);

View file

@ -1,7 +1,7 @@
(function(window) {
window.ls.container.get("view").add({
selector: "data-ui-phases",
controller: function(element, window, document, expression, router) {
controller: function(element, window, document, expression, router, view) {
var tabs = document.createElement("ul");
var container = document.createElement("div");
var titles = Array.prototype.slice.call(
@ -111,6 +111,8 @@
setTab(i);
}
});
view.render(title);
});
next.map(function(obj) {

View file

@ -8,6 +8,10 @@
li {
display: none;
.badge {
display: none;
}
li {
display: block;
}
@ -45,10 +49,31 @@
-ms-user-select: none;
user-select: none;
li {
position: relative;
}
.badge {
background: var(--config-color-focus);
color: var(--config-color-background-fade);
display: inline-block;
border-radius: 15px;
width: 15px;
height: 15px;
margin: 10px;
line-height: 15px;
padding: 3px;
text-align: center;
font-weight: 500!important;
position: absolute;
top: -5px;
right: -35px;
font-size: 12px;
}
.selected {
font-weight: 400;
color: var(--config-color-focus);
position: relative;
opacity: 1;
&:after {

View file

@ -707,6 +707,82 @@
"css": "reddit",
"code": 59473,
"src": "elusive"
},
{
"uid": "5b43525a8080befb3493998ea77195e0",
"css": "integer",
"code": 59475,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M381.7 825.7L279.7 825.7 313.1 625 202.7 625 202.7 533.6 327.9 533.6 347.8 409.3 240.4 409.3 240.4 316.8 363.3 316.8 397.6 113.3 498.5 113.3 465.2 316.8 541.3 316.8 574.6 113.3 676.5 113.3 642.2 316.8 757.6 316.8 757.6 409.3 626.3 409.3 605.9 533.6 720.3 533.6 720.3 625 591.5 625 558.2 825.7 457.3 825.7 490.6 625 415 625 381.7 825.7ZM449.8 409.3L429.4 533.6 505 533.6 525.3 409.3 449.8 409.3Z",
"width": 1000
},
"search": [
"integer"
]
},
{
"uid": "23359bf9000e958419520fc09f111a22",
"css": "float",
"code": 59476,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M381.7 825.7L279.7 825.7 313.1 625 202.7 625 202.7 533.6 327.9 533.6 347.8 409.3 240.4 409.3 240.4 316.8 363.3 316.8 397.6 113.3 498.5 113.3 465.2 316.8 541.3 316.8 574.6 113.3 676.5 113.3 642.2 316.8 757.6 316.8 757.6 409.3 626.3 409.3 605.9 533.6 720.3 533.6 720.3 625 591.5 625 558.2 825.7 457.3 825.7 490.6 625 415 625 381.7 825.7ZM449.8 409.3L429.4 533.6 505 533.6 525.3 409.3 449.8 409.3Z",
"width": 1000
},
"search": [
"float"
]
},
{
"uid": "bbbebc5835cb7e7dd8cecdc59a4dad47",
"css": "ip",
"code": 59477,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M378.7 683.7L147.5 683.7 147.5 606.5 196.1 594.4 196.1 365.8 138.3 353.7 138.3 276.2 330.4 276.2 330.4 594.4 378.7 606.5 378.7 683.7ZM330.4 198.9L192.3 198.9 192.3 96.1 330.4 96.1 330.4 198.9ZM640.9 840.3L409.3 840.3 409.3 762.8 460.6 750.7 460.6 365.8 402.4 353.7 402.4 276.2 586.5 276.2 591.1 322.1C602.3 305.1 615.9 291.9 631.7 282.6 647.5 273.3 666.9 268.6 689.9 268.6 722.5 268.6 750.6 277.5 774.1 295.2 797.6 312.9 815.6 337.7 828.3 369.6 840.9 401.4 847.2 438.5 847.2 480.7L847.2 488.6C847.2 529.3 840.9 564.8 828.3 595.4 815.6 625.9 797.4 649.5 773.7 666.3 750 683.2 721.4 691.6 688 691.6 667.6 691.6 649.6 687.8 634.2 680.3 618.7 672.7 605.4 661.7 594.2 647.1L594.2 750.7 640.9 762.8 640.9 840.3ZM648.2 591C672.4 591 689.3 582 698.7 564.1 708.1 546.1 712.9 521 712.9 488.6L712.9 480.7C712.9 458.3 710.7 438.9 706.4 422.3 702 405.7 695.1 392.9 685.5 383.7 675.9 374.5 663.2 369.9 647.4 369.9 635.9 369.9 625.5 372.5 616.2 377.5 606.9 382.5 599.6 390 594.2 400.1L594.2 566.2C599.6 574.4 607 580.7 616.4 584.8 625.8 589 636.4 591 648.2 591Z",
"width": 1000
},
"search": [
"ip"
]
},
{
"uid": "ffd1698833bf4ed2d4c04041051f336b",
"css": "string",
"code": 59474,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M659.6 835.5L339.5 835.5 339.5 736.9 413.8 721.5 413.8 266.2 303.8 266.2 299.4 341.6 169.9 341.6 169.9 135.4 831.2 135.4 831.2 341.6 700.2 341.6 696.2 266.2 585.3 266.2 585.3 721.5 659.6 736.9 659.6 835.5Z",
"width": 1000
},
"search": [
"string"
]
},
{
"uid": "d91a2d9df5ed8188f0eb0eaeeebd675d",
"css": "more",
"code": 59478,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M970 500C970 759.6 759.6 970 500 970 240.4 970 30 759.6 30 500 30 240.4 240.4 30 500 30 759.6 30 970 240.4 970 500ZM680.1 623.6C641.2 623.6 609.6 655.1 609.6 694 609.6 732.9 641.2 764.5 680.1 764.5 719 764.5 750.5 732.9 750.5 694 750.5 655.1 719 623.6 680.1 623.6ZM308.5 621.5C269.6 621.5 238.1 653 238.1 692 238.1 730.9 269.6 762.4 308.5 762.4 347.4 762.4 378.9 730.9 378.9 692 378.9 653 347.4 621.5 308.5 621.5ZM493.6 621.9C454.7 621.9 423.1 653.4 423.1 692.3 423.1 731.2 454.7 762.7 493.6 762.7 532.5 762.7 564 731.2 564 692.3 564 653.4 532.5 621.9 493.6 621.9ZM751.5 694C751.5 713.7 743.4 731.7 730.6 744.5 717.7 757.4 699.7 765.5 680.1 765.5 660.4 765.5 642.4 757.4 629.5 744.5 616.7 731.7 608.6 713.7 608.6 694 608.6 674.4 616.7 656.4 629.5 643.5 642.4 630.6 660.4 622.6 680.1 622.6 699.7 622.6 717.7 630.6 730.6 643.5 743.4 656.4 751.5 674.4 751.5 694ZM729.2 644.9C716.5 632.3 699.3 624.6 680.1 624.6 660.8 624.6 643.6 632.3 631 644.9 618.3 657.5 610.6 674.8 610.6 694 610.6 713.3 618.3 730.5 631 743.1 643.6 755.7 660.8 763.5 680.1 763.5 699.3 763.5 716.5 755.7 729.2 743.1 741.8 730.5 749.5 713.3 749.5 694 749.5 674.8 741.8 657.5 729.2 644.9ZM379.9 692C379.9 711.6 371.9 729.6 359 742.5 346.1 755.3 328.1 763.4 308.5 763.4 288.8 763.4 270.9 755.3 258 742.5 245.1 729.6 237.1 711.6 237.1 692 237.1 672.3 245.1 654.3 258 641.4 270.9 628.6 288.8 620.5 308.5 620.5 328.1 620.5 346.1 628.6 359 641.4 371.9 654.3 379.9 672.3 379.9 692ZM357.6 642.9C345 630.2 327.7 622.5 308.5 622.5 289.2 622.5 272 630.2 259.4 642.9 246.8 655.5 239.1 672.7 239.1 692 239.1 711.2 246.8 728.4 259.4 741.1 272 753.7 289.2 761.4 308.5 761.4 327.7 761.4 345 753.7 357.6 741.1 370.2 728.4 377.9 711.2 377.9 692 377.9 672.7 370.2 655.5 357.6 642.9ZM565 692.3C565 712 557 729.9 544.1 742.8 531.2 755.7 513.2 763.7 493.6 763.7 473.9 763.7 455.9 755.7 443.1 742.8 430.2 729.9 422.1 712 422.1 692.3 422.1 672.6 430.2 654.7 443.1 641.8 455.9 628.9 473.9 620.9 493.6 620.9 513.2 620.9 531.2 628.9 544.1 641.8 557 654.7 565 672.6 565 692.3ZM542.7 643.2C530.1 630.6 512.8 622.9 493.6 622.9 474.3 622.9 457.1 630.6 444.5 643.2 431.9 655.8 424.1 673.1 424.1 692.3 424.1 711.6 431.9 728.8 444.5 741.4 457.1 754 474.3 761.7 493.6 761.7 512.8 761.7 530.1 754 542.7 741.4 555.3 728.8 563 711.6 563 692.3 563 673.1 555.3 655.8 542.7 643.2Z",
"width": 1000
},
"search": [
"more"
]
},
{
"uid": "272e08e0e16226aadf94dcbf33aab2b2",
"css": "key",
"code": 59479,
"src": "elusive"
}
]
}

View file

@ -662,7 +662,7 @@ input[type=checkbox], input[type=radio] {
.paging {
color: var(--config-color-fade);
padding: 5px 15px;
padding: 0;
font-size: 12px;
form {

File diff suppressed because one or more lines are too long

View file

@ -7,11 +7,28 @@ table {
position: relative;
table-layout: fixed;
&.y-scroll {
overflow-y: auto;
}
&.multi-line {
thead th, tbody td {
line-height: inherit;
text-overflow:inherit;
white-space: inherit;
}
}
&.borders {
td, th {
.func-border-end(1px, var(--config-border-fade-super));
&:last-child{
border: none;
}
}
}
thead {
box-shadow: 0 0 2px rgba(0,0,0,.25);
border-bottom: solid 1px var(--config-color-fade-super);
@ -95,10 +112,6 @@ table {
&:first-child {
.func-padding-start(30px);
}
&:last-child {
.func-padding-end(30px);
}
}
td, th {

View file

@ -31,6 +31,7 @@
--config-color-info: #386fd2;
--config-border-color: #f3f3f3;
--config-border-fade: #e0e3e4;
--config-border-fade-super: #f7f7f7;
--config-border-radius: 10px;
--config-prism-background: #373738;
--config-prism-numbers: #39393c;
@ -113,6 +114,7 @@
--config-color-info: #386fd2;
--config-border-color: #262D50;
--config-border-fade: #19203a;
--config-border-fade-super: #262D50;
--config-prism-background: #1F253F;
--config-prism-numbers: #1F253F;
--config-note-background: #171e33;

View file

@ -40,13 +40,6 @@ class Attribute extends Model
'default' => false,
'example' => true,
])
->addRule('signed', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is attribute signed?',
'default' => true,
'example' => true,
'required' => false,
])
->addRule('array', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is attribute an array?',
@ -54,14 +47,6 @@ class Attribute extends Model
'example' => false,
'required' => false
])
->addRule('filters', [
'type' => self::TYPE_STRING,
'description' => 'Attribute filters.',
'default' => [],
'example' => [],
'array' => true,
'required' => false,
])
;
}

View file

@ -10,12 +10,6 @@ class Index extends Model
public function __construct()
{
$this
->addRule('$collection', [
'type' => self::TYPE_STRING,
'description' => 'Collection ID.',
'default' => '',
'example' => '5e5ea5c16d55',
])
->addRule('$id', [
'type' => self::TYPE_STRING,
'description' => 'Index ID.',
@ -35,14 +29,6 @@ class Index extends Model
'example' => [],
'array' => true,
])
->addRule('lengths', [
'type' => self::TYPE_STRING,
'description' => 'Index lengths.',
'default' => [],
'example' => [],
'array' => true,
'required' => false,
])
->addRule('orders', [
'type' => self::TYPE_STRING,
'description' => 'Index orders.',

View file

@ -16,6 +16,30 @@ class Log extends Model
'default' => '',
'example' => 'account.sessions.create',
])
->addRule('userId', [
'type' => self::TYPE_STRING,
'description' => 'User ID.',
'default' => '',
'example' => '610fc2f985ee0',
])
->addRule('userEmail', [
'type' => self::TYPE_STRING,
'description' => 'User Email.',
'default' => '',
'example' => 'john@appwrite.io',
])
->addRule('userName', [
'type' => self::TYPE_STRING,
'description' => 'User Name.',
'default' => '',
'example' => 'John Doe',
])
->addRule('mode', [
'type' => self::TYPE_STRING,
'description' => 'API mode when event triggered.',
'default' => '',
'example' => 'admin',
])
->addRule('ip', [
'type' => self::TYPE_STRING,
'description' => 'IP session in use when the session was created.',

View file

@ -70,10 +70,6 @@ trait ProjectCustom
'teams.write',
'collections.read',
'collections.write',
'attributes.read',
'attributes.write',
'indexes.read',
'indexes.write',
'documents.read',
'documents.write',
'files.read',

View file

@ -68,21 +68,18 @@ trait DatabaseBase
]);
$this->assertEquals($title['headers']['status-code'], 201);
$this->assertEquals($title['body']['$collection'], $data['moviesId']);
$this->assertEquals($title['body']['$id'], 'title');
$this->assertEquals($title['body']['type'], 'string');
$this->assertEquals($title['body']['size'], 256);
$this->assertEquals($title['body']['required'], true);
$this->assertEquals($releaseYear['headers']['status-code'], 201);
$this->assertEquals($releaseYear['body']['$collection'], $data['moviesId']);
$this->assertEquals($releaseYear['body']['$id'], 'releaseYear');
$this->assertEquals($releaseYear['body']['type'], 'integer');
$this->assertEquals($releaseYear['body']['size'], 0);
$this->assertEquals($releaseYear['body']['required'], true);
$this->assertEquals($actors['headers']['status-code'], 201);
$this->assertEquals($actors['body']['$collection'], $data['moviesId']);
$this->assertEquals($actors['body']['$id'], 'actors');
$this->assertEquals($actors['body']['type'], 'string');
$this->assertEquals($actors['body']['size'], 256);
@ -98,9 +95,6 @@ trait DatabaseBase
'x-appwrite-key' => $this->getProject()['apiKey']
]), []);
$this->assertEquals($movies['body']['$id'], $title['body']['$collection']);
$this->assertEquals($movies['body']['$id'], $releaseYear['body']['$collection']);
$this->assertEquals($movies['body']['$id'], $actors['body']['$collection']);
$this->assertIsArray($movies['body']['attributesInQueue']);
$this->assertCount(0, $movies['body']['attributesInQueue']);
$this->assertIsArray($movies['body']['attributes']);
@ -128,7 +122,6 @@ trait DatabaseBase
]);
$this->assertEquals($titleIndex['headers']['status-code'], 201);
$this->assertEquals($titleIndex['body']['$collection'], $data['moviesId']);
$this->assertEquals($titleIndex['body']['$id'], 'titleIndex');
$this->assertEquals($titleIndex['body']['type'], 'fulltext');
$this->assertCount(1, $titleIndex['body']['attributes']);
@ -143,7 +136,6 @@ trait DatabaseBase
'x-appwrite-key' => $this->getProject()['apiKey']
]), []);
$this->assertEquals($movies['body']['$id'], $titleIndex['body']['$collection']);
$this->assertIsArray($movies['body']['indexes']);
$this->assertCount(1, $movies['body']['indexes']);
$this->assertEquals($movies['body']['indexes'][0]['$id'], $titleIndex['body']['$id']);
@ -221,7 +213,6 @@ trait DatabaseBase
]);
$this->assertEquals($document1['headers']['status-code'], 201);
$this->assertEquals($document1['body']['$collection'], $data['moviesId']);
$this->assertEquals($document1['body']['title'], 'Captain America');
$this->assertEquals($document1['body']['releaseYear'], 1944);
$this->assertIsArray($document1['body']['$read']);
@ -233,7 +224,6 @@ trait DatabaseBase
$this->assertEquals($document1['body']['actors'][1], 'Samuel Jackson');
$this->assertEquals($document2['headers']['status-code'], 201);
$this->assertEquals($document2['body']['$collection'], $data['moviesId']);
$this->assertEquals($document2['body']['title'], 'Spider-Man: Far From Home');
$this->assertEquals($document2['body']['releaseYear'], 2019);
$this->assertIsArray($document2['body']['$read']);
@ -246,7 +236,6 @@ trait DatabaseBase
$this->assertEquals($document2['body']['actors'][2], 'Samuel Jackson');
$this->assertEquals($document3['headers']['status-code'], 201);
$this->assertEquals($document3['body']['$collection'], $data['moviesId']);
$this->assertEquals($document3['body']['title'], 'Spider-Man: Homecoming');
$this->assertEquals($document3['body']['releaseYear'], 2017);
$this->assertIsArray($document3['body']['$read']);
@ -545,7 +534,6 @@ trait DatabaseBase
]);
$id = $document['body']['$id'];
$collection = $document['body']['$collection'];
$this->assertEquals($document['headers']['status-code'], 201);
$this->assertEquals($document['body']['title'], 'Thor: Ragnaroc');
@ -553,7 +541,7 @@ trait DatabaseBase
$this->assertEquals($document['body']['$read'][1], 'user:testx');
$this->assertEquals($document['body']['$write'][1], 'user:testy');
$document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $collection . '/documents/' . $id, array_merge([
$document = $this->client->call(Client::METHOD_PATCH, '/database/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -566,13 +554,12 @@ trait DatabaseBase
$this->assertEquals($document['body']['title'], 'Thor: Ragnarok');
$this->assertEquals($document['body']['releaseYear'], 2017);
$document = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collection . '/documents/' . $id, array_merge([
$document = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$id = $document['body']['$id'];
$collection = $document['body']['$collection'];
$this->assertEquals($document['headers']['status-code'], 200);
$this->assertEquals($document['body']['title'], 'Thor: Ragnarok');
@ -601,25 +588,24 @@ trait DatabaseBase
]);
$id = $document['body']['$id'];
$collection = $document['body']['$collection'];
$this->assertEquals($document['headers']['status-code'], 201);
$document = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collection . '/documents/' . $id, array_merge([
$document = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($document['headers']['status-code'], 200);
$document = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $collection . '/documents/' . $id, array_merge([
$document = $this->client->call(Client::METHOD_DELETE, '/database/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($document['headers']['status-code'], 204);
$document = $this->client->call(Client::METHOD_GET, '/database/collections/' . $collection . '/documents/' . $id, array_merge([
$document = $this->client->call(Client::METHOD_GET, '/database/collections/' . $data['moviesId'] . '/documents/' . $id, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -988,7 +974,6 @@ trait DatabaseBase
$id = $document['body']['$id'];
$this->assertEquals($document['headers']['status-code'], 201);
$this->assertEquals($document['body']['$collection'], $data['moviesId']);
$this->assertEquals($document['body']['title'], 'Captain America');
$this->assertEquals($document['body']['releaseYear'], 1944);
$this->assertIsArray($document['body']['$read']);

View file

@ -170,8 +170,6 @@ class DatabaseCustomServerTest extends Scope
$unneededId = $unneeded['body']['$id'];
$this->assertEquals($collection['body']['$id'], $firstName['body']['$collection']);
$this->assertEquals($collection['body']['$id'], $lastName['body']['$collection']);
$this->assertIsArray($collection['body']['attributes']);
$this->assertCount(3, $collection['body']['attributes']);
$this->assertEquals($collection['body']['attributes'][0]['$id'], $firstName['body']['$id']);
@ -195,8 +193,6 @@ class DatabaseCustomServerTest extends Scope
'x-appwrite-key' => $this->getProject()['apiKey']
]), []);
$this->assertEquals($collection['body']['$id'], $firstName['body']['$collection']);
$this->assertEquals($collection['body']['$id'], $lastName['body']['$collection']);
$this->assertIsArray($collection['body']['attributes']);
$this->assertCount(2, $collection['body']['attributes']);
$this->assertEquals($collection['body']['attributes'][0]['$id'], $firstName['body']['$id']);
@ -267,7 +263,6 @@ class DatabaseCustomServerTest extends Scope
]);
$this->assertEquals($document1['headers']['status-code'], 201);
$this->assertEquals($document1['body']['$collection'], $collectionId);
$this->assertIsArray($document1['body']['$read']);
$this->assertIsArray($document1['body']['$write']);
$this->assertCount(1, $document1['body']['$read']);
@ -276,7 +271,6 @@ class DatabaseCustomServerTest extends Scope
$this->assertEquals($document1['body']['lastName'], 'Holland');
$this->assertEquals($document2['headers']['status-code'], 201);
$this->assertEquals($document2['body']['$collection'], $collectionId);
$this->assertIsArray($document2['body']['$read']);
$this->assertIsArray($document2['body']['$write']);
$this->assertCount(1, $document2['body']['$read']);

View file

@ -72,10 +72,8 @@ trait WebhooksBase
]);
$this->assertEquals($firstName['headers']['status-code'], 201);
$this->assertEquals($firstName['body']['$collection'], $data['actorsId']);
$this->assertEquals($firstName['body']['$id'], 'firstName');
$this->assertEquals($lastName['headers']['status-code'], 201);
$this->assertEquals($lastName['body']['$collection'], $data['actorsId']);
$this->assertEquals($lastName['body']['$id'], 'lastName');
// wait for database worker to kick in

View file

@ -70,7 +70,6 @@ class WebhooksCustomServerTest extends Scope
]);
$this->assertEquals($index['headers']['status-code'], 201);
$this->assertEquals($index['body']['$collection'], $data['actorsId']);
$this->assertEquals($index['body']['$id'], 'fullname');
// wait for database worker to create index