diff --git a/composer.json b/composer.json index c147da7..7611b74 100755 --- a/composer.json +++ b/composer.json @@ -30,16 +30,17 @@ } ], "require": { - "php": "~8.2", - "ext-sodium": "~8.2.4", + "php": "~8.3", + "ext-sodium": "~8.3", "mirzaev/minimal": "^2.0.x-dev", "mirzaev/accounts": "~1.2.x-dev", "mirzaev/arangodb": "^1.0.0", "triagens/arangodb": "~3.9.x-dev", - "twig/twig": "^3.4", + "twig/twig": "^3.10", "twig/extra-bundle": "^3.7", - "twig/intl-extra": "^3.7", - "phpoffice/phpspreadsheet": "^1.29" + "twig/intl-extra": "^3.10", + "phpoffice/phpspreadsheet": "^2.1", + "openswoole/core": "^22.1" }, "require-dev": { "phpunit/phpunit": "~9.5" diff --git a/composer.lock b/composer.lock index 01f84c0..575d7d8 100755 --- a/composer.lock +++ b/composer.lock @@ -4,69 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "588f1020cbf90d4c8ed02b057592c14f", + "content-hash": "c0be2095032e176b9fb16a24e7a4d1a1", "packages": [ - { - "name": "ezyang/htmlpurifier", - "version": "v4.16.0", - "source": { - "type": "git", - "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8", - "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8", - "shasum": "" - }, - "require": { - "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0" - }, - "require-dev": { - "cerdic/css-tidy": "^1.7 || ^2.0", - "simpletest/simpletest": "dev-master" - }, - "suggest": { - "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", - "ext-bcmath": "Used for unit conversion and imagecrash protection", - "ext-iconv": "Converts text to and from non-UTF-8 encodings", - "ext-tidy": "Used for pretty-printing HTML" - }, - "type": "library", - "autoload": { - "files": [ - "library/HTMLPurifier.composer.php" - ], - "psr-0": { - "HTMLPurifier": "library/" - }, - "exclude-from-classmap": [ - "/library/HTMLPurifier/Language/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "LGPL-2.1-or-later" - ], - "authors": [ - { - "name": "Edward Z. Yang", - "email": "admin@htmlpurifier.org", - "homepage": "http://ezyang.com" - } - ], - "description": "Standards compliant HTML filter written in PHP", - "homepage": "http://htmlpurifier.org/", - "keywords": [ - "html" - ], - "support": { - "issues": "https://github.com/ezyang/htmlpurifier/issues", - "source": "https://github.com/ezyang/htmlpurifier/tree/v4.16.0" - }, - "time": "2022-09-18T07:06:19+00:00" - }, { "name": "guzzlehttp/guzzle", "version": "7.7.0", @@ -728,17 +667,88 @@ "time": "2023-03-20T11:46:41+00:00" }, { - "name": "phpoffice/phpspreadsheet", - "version": "1.29.0", + "name": "openswoole/core", + "version": "22.1.5", "source": { "type": "git", - "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0" + "url": "https://github.com/openswoole/core.git", + "reference": "06dae68fdac73341ccf565ecef388434bd893141" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/fde2ccf55eaef7e86021ff1acce26479160a0fa0", - "reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0", + "url": "https://api.github.com/repos/openswoole/core/zipball/06dae68fdac73341ccf565ecef388434bd893141", + "reference": "06dae68fdac73341ccf565ecef388434bd893141", + "shasum": "" + }, + "require": { + "ext-openswoole": ">=22.0", + "php": ">=7.4", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-server-middleware": "^1.0.0" + }, + "require-dev": { + "ext-curl": "*", + "ext-sockets": "*", + "friendsofphp/php-cs-fixer": "^3.6", + "openswoole/ide-helper": "^22.0", + "php-http/psr7-integration-tests": "^1.1", + "phpunit/phpunit": "^9.5" + }, + "suggest": { + "ext-mysqli": "*", + "ext-pdo": "*", + "ext-redis": "Required to use redis database, and the required version is greater than or equal to 3.1.3" + }, + "type": "library", + "autoload": { + "files": [ + "src/Coroutine/functions.php" + ], + "psr-4": { + "OpenSwoole\\Core\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "OpenSwoole Group", + "email": "hello@openswoole.com" + } + ], + "description": "Openswoole core library", + "homepage": "https://openswoole.com", + "keywords": [ + "http", + "http2", + "mqtt", + "openswoole", + "php", + "tcp", + "websocket" + ], + "support": { + "docs": "https://openswoole.com/docs", + "issues": "https://github.com/openswoole/openswoole/issues", + "pull-request": "https://github.com/openswoole/openswoole/pulls", + "source": "https://github.com/openswoole/openswoole" + }, + "time": "2023-12-10T19:02:13+00:00" + }, + { + "name": "phpoffice/phpspreadsheet", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "dbed77bd3a0f68f96c0dd68ad4499d5674fecc3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/dbed77bd3a0f68f96c0dd68ad4499d5674fecc3e", + "reference": "dbed77bd3a0f68f96c0dd68ad4499d5674fecc3e", "shasum": "" }, "require": { @@ -755,25 +765,24 @@ "ext-xmlwriter": "*", "ext-zip": "*", "ext-zlib": "*", - "ezyang/htmlpurifier": "^4.15", "maennchen/zipstream-php": "^2.1 || ^3.0", "markbaker/complex": "^3.0", "markbaker/matrix": "^3.0", - "php": "^7.4 || ^8.0", + "php": "^8.0", "psr/http-client": "^1.0", "psr/http-factory": "^1.0", "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "dev-main", - "dompdf/dompdf": "^1.0 || ^2.0", + "dompdf/dompdf": "^2.0", "friendsofphp/php-cs-fixer": "^3.2", "mitoteam/jpgraph": "^10.3", "mpdf/mpdf": "^8.1.1", "phpcompatibility/php-compatibility": "^9.3", "phpstan/phpstan": "^1.1", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^8.5 || ^9.0 || ^10.0", + "phpunit/phpunit": "^9.6", "squizlabs/php_codesniffer": "^3.7", "tecnickcom/tcpdf": "^6.5" }, @@ -828,9 +837,9 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.0" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/2.1.0" }, - "time": "2023-06-14T22:48:31+00:00" + "time": "2024-05-11T04:17:56+00:00" }, { "name": "psr/cache", @@ -1144,6 +1153,119 @@ }, "time": "2023-04-04T09:54:51+00:00" }, + { + "name": "psr/http-server-handler", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-handler.git", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4", + "reference": "84c4fb66179be4caaf8e97bd239203245302e7d4", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side request handler", + "keywords": [ + "handler", + "http", + "http-interop", + "psr", + "psr-15", + "psr-7", + "request", + "response", + "server" + ], + "support": { + "source": "https://github.com/php-fig/http-server-handler/tree/1.0.2" + }, + "time": "2023-04-10T20:06:20+00:00" + }, + { + "name": "psr/http-server-middleware", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-server-middleware.git", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "reference": "c1481f747daaa6a0782775cd6a8c26a1bf4a3829", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/http-server-handler": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP server-side middleware", + "keywords": [ + "http", + "http-interop", + "middleware", + "psr", + "psr-15", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-server-middleware/issues", + "source": "https://github.com/php-fig/http-server-middleware/tree/1.0.2" + }, + "time": "2023-04-11T06:14:47+00:00" + }, { "name": "psr/log", "version": "3.0.0", @@ -1619,16 +1741,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.3.0", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", - "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", "shasum": "" }, "require": { @@ -1637,7 +1759,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.4-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1666,7 +1788,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -1682,7 +1804,7 @@ "type": "tidelift" } ], - "time": "2023-05-23T14:45:45+00:00" + "time": "2024-04-18T09:32:20+00:00" }, { "name": "symfony/error-handler", @@ -2377,25 +2499,25 @@ }, { "name": "symfony/intl", - "version": "v6.3.2", + "version": "v7.0.7", "source": { "type": "git", "url": "https://github.com/symfony/intl.git", - "reference": "1f8cb145c869ed089a8531c51a6a4b31ed0b3c69" + "reference": "dd12042707110995e2e7d80103f8d9928bea8621" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/intl/zipball/1f8cb145c869ed089a8531c51a6a4b31ed0b3c69", - "reference": "1f8cb145c869ed089a8531c51a6a4b31ed0b3c69", + "url": "https://api.github.com/repos/symfony/intl/zipball/dd12042707110995e2e7d80103f8d9928bea8621", + "reference": "dd12042707110995e2e7d80103f8d9928bea8621", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^5.4|^6.0", - "symfony/finder": "^5.4|^6.0", - "symfony/var-exporter": "^5.4|^6.0" + "symfony/filesystem": "^6.4|^7.0", + "symfony/finder": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -2403,7 +2525,8 @@ "Symfony\\Component\\Intl\\": "" }, "exclude-from-classmap": [ - "/Tests/" + "/Tests/", + "/Resources/data/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2439,7 +2562,7 @@ "localization" ], "support": { - "source": "https://github.com/symfony/intl/tree/v6.3.2" + "source": "https://github.com/symfony/intl/tree/v7.0.7" }, "funding": [ { @@ -2455,20 +2578,20 @@ "type": "tidelift" } ], - "time": "2023-07-20T07:43:09+00:00" + "time": "2024-04-18T09:29:19+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", "shasum": "" }, "require": { @@ -2482,9 +2605,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -2521,7 +2641,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" }, "funding": [ { @@ -2537,20 +2657,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", "shasum": "" }, "require": { @@ -2564,9 +2684,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -2604,7 +2721,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" }, "funding": [ { @@ -2620,20 +2737,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", - "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", "shasum": "" }, "require": { @@ -2641,9 +2758,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -2687,7 +2801,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" }, "funding": [ { @@ -2703,7 +2817,7 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-php83", @@ -3516,25 +3630,25 @@ }, { "name": "twig/intl-extra", - "version": "v3.7.1", + "version": "v3.10.0", "source": { "type": "git", "url": "https://github.com/twigphp/intl-extra.git", - "reference": "4f4fe572f635534649cc069e1dafe4a8ad63774d" + "reference": "693f6beb8ca91fc6323e01b3addf983812f65c93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/4f4fe572f635534649cc069e1dafe4a8ad63774d", - "reference": "4f4fe572f635534649cc069e1dafe4a8ad63774d", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/693f6beb8ca91fc6323e01b3addf983812f65c93", + "reference": "693f6beb8ca91fc6323e01b3addf983812f65c93", "shasum": "" }, "require": { - "php": ">=7.1.3", - "symfony/intl": "^5.4|^6.0", - "twig/twig": "^2.7|^3.0" + "php": ">=7.2.5", + "symfony/intl": "^5.4|^6.4|^7.0", + "twig/twig": "^3.10" }, "require-dev": { - "symfony/phpunit-bridge": "^5.4|^6.3" + "symfony/phpunit-bridge": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -3564,7 +3678,7 @@ "twig" ], "support": { - "source": "https://github.com/twigphp/intl-extra/tree/v3.7.1" + "source": "https://github.com/twigphp/intl-extra/tree/v3.10.0" }, "funding": [ { @@ -3576,33 +3690,41 @@ "type": "tidelift" } ], - "time": "2023-07-29T15:34:56+00:00" + "time": "2024-05-11T07:35:57+00:00" }, { "name": "twig/twig", - "version": "v3.6.1", + "version": "v3.10.3", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd" + "reference": "67f29781ffafa520b0bbfbd8384674b42db04572" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd", - "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/67f29781ffafa520b0bbfbd8384674b42db04572", + "reference": "67f29781ffafa520b0bbfbd8384674b42db04572", "shasum": "" }, "require": { "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" }, "require-dev": { "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -3635,7 +3757,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.6.1" + "source": "https://github.com/twigphp/Twig/tree/v3.10.3" }, "funding": [ { @@ -3647,7 +3769,7 @@ "type": "tidelift" } ], - "time": "2023-06-08T12:52:13+00:00" + "time": "2024-05-16T10:04:27+00:00" } ], "packages-dev": [ @@ -5393,8 +5515,8 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": "~8.2", - "ext-sodium": "~8.2.4" + "php": "~8.3", + "ext-sodium": "~8.3" }, "platform-dev": [], "plugin-api-version": "2.2.0" diff --git a/ebala-socket.service b/ebala-socket.service new file mode 100755 index 0000000..3772832 --- /dev/null +++ b/ebala-socket.service @@ -0,0 +1,17 @@ +[Unit] +Description=Ebala-socket + +Wants=network.target +After=syslog.target network-online.target + +[Service] +ExecStart=sudo -u www-data /usr/bin/php /var/www/ebala/mirzaev/ebala/system/public/socket.php +PIDFile=/var/run/php/ebala-socket.pid +RemainAfterExit=no +RuntimeMaxSec=3600s +Restart=always +RestartSec=30s + +[Install] +WantedBy=multi-user.target + diff --git a/mirzaev/ebala/system/controllers/account.php b/mirzaev/ebala/system/controllers/account.php index d488299..ad7ebae 100755 --- a/mirzaev/ebala/system/controllers/account.php +++ b/mirzaev/ebala/system/controllers/account.php @@ -106,123 +106,158 @@ final class account extends core */ public function update(array $parameters = []): ?string { - if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { - // Авторизован аккаунт администратора или оператора + try { + if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { + // Авторизован аккаунт администратора или оператора - // Инициализация данных аккаунта - $account = model::read('d._key == "' . $parameters['id'] . '"'); + // Инициализация данных аккаунта + $account = model::read('d._key == "' . $parameters['id'] . '"'); - if (!empty($account)) { - // Найден аккаунт + if (!empty($account)) { + // Найден аккаунт - // Инициализация буфера изменённости пароля - $password = false; + // Инициализация буфера изменённости пароля + $password = false; - // Инициализация параметров (перезапись переданными значениями) - if ($parameters['name_first'] !== $account->name['first']) $account->name = ['first' => $parameters['name_first']] + $account->name; - if ($parameters['name_second'] !== $account->name['second']) $account->name = ['second' => $parameters['name_second']] + $account->name; - if ($parameters['name_last'] !== $account->name['last']) $account->name = ['last' => $parameters['name_last']] + $account->name; - if ($parameters['number'] !== $account->number) - if (mb_strlen($parameters['number']) === 11) $account->number = $parameters['number']; - else throw new exception('Номер должен состоять из 11 символов'); - if ($parameters['mail'] !== $account->mail) $account->mail = $parameters['mail']; - if (!empty($parameters['password']) && !@sodium_crypto_pwhash_str_verify($parameters['password'], $account->password ?? '') && $password = true) - if (mb_strlen($parameters['password']) > 6) $account->password = sodium_crypto_pwhash_str( - $parameters['password'], - SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE, - SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE - ); - else throw new exception('Пароль должен быть длиннее 6 символов'); - if ($parameters['commentary'] !== $account->commentary) $account->commentary = $parameters['commentary']; - if ($parameters['transactions'] !== $account->transactions) - $account->transactions = match ($parameters['transactions']) { - 'true' => true, - 'false' => false, - default => false - }; + // Инициализация параметров (перезапись переданными значениями) + if ($parameters['name_first'] !== $account->name['first']) $account->name = ['first' => $parameters['name_first']] + $account->name; + if ($parameters['name_second'] !== $account->name['second']) $account->name = ['second' => $parameters['name_second']] + $account->name; + if ($parameters['name_last'] !== $account->name['last']) $account->name = ['last' => $parameters['name_last']] + $account->name; + if ($parameters['number'] !== $account->number) + if (mb_strlen($parameters['number']) === 11) $account->number = $parameters['number']; + else throw new exception('Номер должен состоять из 11 символов'); + if ($parameters['mail'] !== $account->mail) $account->mail = $parameters['mail']; + if (!empty($parameters['password']) && !@sodium_crypto_pwhash_str_verify($parameters['password'], $account->password ?? '') && $password = true) + if (mb_strlen($parameters['password']) > 6) $account->password = sodium_crypto_pwhash_str( + $parameters['password'], + SODIUM_CRYPTO_PWHASH_OPSLIMIT_SENSITIVE, + SODIUM_CRYPTO_PWHASH_MEMLIMIT_SENSITIVE + ); + else throw new exception('Пароль должен быть длиннее 6 символов'); + if ($parameters['commentary'] !== $account->commentary) $account->commentary = $parameters['commentary']; + if (!empty($parameters['transactions']) && $parameters['transactions'] !== $account->transactions) + $account->transactions = match ($parameters['transactions']) { + 'true' => true, + 'false' => false, + default => false + }; - if (_core::update($account)) { - // Записаны данные аккаунта + if (_core::update($account)) { + // Записаны данные аккаунта - if ($account->type === 'worker') { - // Сотрудник + if ($account->type === 'worker') { + // Сотрудник - // Инициализация строки в глобальную переменную шаблонизатора - $this->view->rows = registry::workers( - before: sprintf( - "FILTER a._id == '%s' && a.deleted != true", - $account->getId() - ), - after: <<view->rows = registry::workers( + before: sprintf( + "FILTER a._id == '%s' && a.deleted != true", + $account->getId() + ), + after: <<type === 'market') { - // Магазин + amount: 1 + ); + } else if ($account->type === 'market') { + // Магазин - // Инициализация строки в глобальную переменную шаблонизатора - $this->view->rows = registry::markets( - before: sprintf( - "FILTER a._id == '%s' && a.deleted != true", - $account->getId() - ), - after: <<view->rows = registry::markets( + before: sprintf( + "FILTER a._id == '%s' && a.deleted != true", + $account->getId() + ), + after: <<view->rows = [['account' => $account->getAll()]]; - } + // Инициализация строки в глобальную переменную шаблонизатора + $this->view->rows = [['account' => $account->getAll()]]; + } - // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) - $this->view->page = null; + // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) + $this->view->page = null; - // Запись заголовков ответа - header('Content-Type: application/json'); - header('Content-Encoding: none'); - header('X-Accel-Buffering: no'); + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); - // Инициализация буфера вывода - ob_start(); + // Инициализация буфера вывода + ob_start(); - // Инициализация буфера ответа - $return = [ - 'updated' => true, - 'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . $account->type . 's.html'), - 'errors' => self::parse_only_text($this->errors) - ]; + // Инициализация буфера ответа + $return = [ + 'updated' => true, + 'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . $account->type . 's.html'), + 'errors' => self::parse_only_text($this->errors) + ]; - if ($password) $return['clipboard'] = match ($account->type) { - 'worker' => 'Номер: ' . model::worker($account->getId())?->number, - 'market' => 'Идентификатор: ' . model::market($account->getId())?->id, - 'operator' => "Идентификатор: {$account->getKey()}", - 'administrator' => "Идентификатор: {$account->getKey()}", - default => "Идентификатор: {$account->getKey()}" - } - . "\nПароль: {$parameters['password']}"; + if ($password) $return['clipboard'] = match ($account->type) { + 'worker' => 'Номер: ' . model::worker($account->getId())?->number, + 'market' => 'Идентификатор: ' . model::market($account->getId())?->id, + 'operator' => "Идентификатор: {$account->getKey()}", + 'administrator' => "Идентификатор: {$account->getKey()}", + default => "Идентификатор: {$account->getKey()}" + } + . "\nПароль: {$parameters['password']}"; - // Генерация ответа - echo json_encode($return); + // Генерация ответа + echo json_encode($return); - // Запись заголовков ответа - header('Content-Length: ' . ob_get_length()); + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); - // Отправка и деинициализация буфера вывода - ob_end_flush(); - flush(); - } else throw new exception('Не удалось записать изменения в базу данных'); - } else throw new exception('Не удалось найти аккаунт'); + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } else throw new exception('Не удалось записать изменения в базу данных'); + } else throw new exception('Не удалось найти аккаунт'); + } + } catch (exception $e) { + // Write to the errors registry + $this->errors['account'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Инициализация буфера ответа + $return = [ + 'updated' => false, + 'errors' => self::parse_only_text($this->errors) + ]; + + // Генерация ответа + echo json_encode($return); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); } + // Возврат (провал) return null; } @@ -234,75 +269,110 @@ final class account extends core */ public function delete(array $parameters = []): ?string { - if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { - // Авторизован аккаунт администратора или оператора + try { + if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { + // Авторизован аккаунт администратора или оператора - // Инициализация данных аккаунта - $account = model::read('d._key == "' . $parameters['id'] . '"'); + // Инициализация данных аккаунта + $account = model::read('d._key == "' . $parameters['id'] . '"'); - if (!empty($account)) { - // Найден аккаунт - - // Удаление - $account->active = false; - $account->deleted = true; - - if ($account->type === 'worker') { - // Сотрудник - - // Инициализация сотрудника - if (empty($worker = model::worker($account->getId()))) throw new exception('Не удалось инициализировать сотрудника'); + if (!empty($account)) { + // Найден аккаунт // Удаление - $worker->active = false; - $worker->deleted = true; + $account->active = false; + $account->deleted = true; - // Запись в ArangoDB - if (!_core::update($worker)) throw throw new exception('Не удалось записать изменения в базу данных'); - } else if ($account->type === 'market') { - // Магазин + if ($account->type === 'worker') { + // Сотрудник - // Инициализация магазина - if (empty($market = model::market($account->getId()))) throw new exception('Не удалось инициализировать магазин'); + // Инициализация сотрудника + if (empty($worker = model::worker($account->getId()))) throw new exception('Не удалось инициализировать сотрудника'); - // Удаление - $market->active = false; - $market->deleted = true; + // Удаление + $worker->active = false; + $worker->deleted = true; - // Запись в ArangoDB - if (!_core::update($market)) throw throw new exception('Не удалось записать изменения в базу данных'); - } + // Запись в ArangoDB + if (!_core::update($worker)) throw throw new exception('Не удалось записать изменения в базу данных'); + } else if ($account->type === 'market') { + // Магазин - if (_core::update($account)) { - // Записаны данные аккаунта + // Инициализация магазина + if (empty($market = model::market($account->getId()))) throw new exception('Не удалось инициализировать магазин'); - // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) - $this->view->page = null; + // Удаление + $market->active = false; + $market->deleted = true; - // Запись заголовков ответа - header('Content-Type: application/json'); - header('Content-Encoding: none'); - header('X-Accel-Buffering: no'); + // Запись в ArangoDB + if (!_core::update($market)) throw throw new exception('Не удалось записать изменения в базу данных'); + } - // Инициализация буфера вывода - ob_start(); + if (_core::update($account)) { + // Записаны данные аккаунта - // Генерация ответа - echo json_encode([ - 'deleted' => true, - 'errors' => self::parse_only_text($this->errors) - ]); + // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) + $this->view->page = null; - // Запись заголовков ответа - header('Content-Length: ' . ob_get_length()); + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); - // Отправка и деинициализация буфера вывода - ob_end_flush(); - flush(); - } else throw new exception('Не удалось записать изменения в базу данных'); - } else throw new exception('Не удалось найти аккаунт'); + // Инициализация буфера вывода + ob_start(); + + // Генерация ответа + echo json_encode([ + 'deleted' => true, + 'errors' => self::parse_only_text($this->errors) + ]); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } else throw new exception('Не удалось записать изменения в базу данных'); + } else throw new exception('Не удалось найти аккаунт'); + } + } catch (exception $e) { + // Write to the errors registry + $this->errors['account'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Инициализация буфера ответа + $return = [ + 'deleted' => false, + 'errors' => self::parse_only_text($this->errors) + ]; + + // Генерация ответа + echo json_encode($return); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); } + // Возврат (провал) return null; } @@ -314,46 +384,81 @@ final class account extends core */ public function ban(array $parameters = []): ?string { - if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { - // Авторизован аккаунт администратора или оператора + try { + if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { + // Авторизован аккаунт администратора или оператора - // Инициализация данных аккаунта - $account = model::read('d._key == "' . $parameters['id'] . '"'); + // Инициализация данных аккаунта + $account = model::read('d._key == "' . $parameters['id'] . '"'); - if (!empty($account)) { - // Найден аккаунт + if (!empty($account)) { + // Найден аккаунт - // Блокирование - $account->active = false; - $account->banned = true; + // Блокирование + $account->active = false; + $account->banned = true; - if (_core::update($account)) { - // Записаны данные аккаунта + if (_core::update($account)) { + // Записаны данные аккаунта - // Запись заголовков ответа - header('Content-Type: application/json'); - header('Content-Encoding: none'); - header('X-Accel-Buffering: no'); + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); - // Инициализация буфера вывода - ob_start(); + // Инициализация буфера вывода + ob_start(); - // Генерация ответа - echo json_encode([ - 'banned' => true, - 'errors' => self::parse_only_text($this->errors) - ]); + // Генерация ответа + echo json_encode([ + 'banned' => true, + 'errors' => self::parse_only_text($this->errors) + ]); - // Запись заголовков ответа - header('Content-Length: ' . ob_get_length()); + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); - // Отправка и деинициализация буфера вывода - ob_end_flush(); - flush(); - } else throw new exception('Не удалось записать изменения в базу данных'); - } else throw new exception('Не удалось найти аккаунт'); + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } else throw new exception('Не удалось записать изменения в базу данных'); + } else throw new exception('Не удалось найти аккаунт'); + } + } catch (exception $e) { + // Write to the errors registry + $this->errors['account'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Инициализация буфера ответа + $return = [ + 'banned' => false, + 'errors' => self::parse_only_text($this->errors) + ]; + + // Генерация ответа + echo json_encode($return); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); } + // Возврат (провал) return null; } @@ -365,46 +470,81 @@ final class account extends core */ public function unban(array $parameters = []): ?string { - if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { - // Авторизован аккаунт администратора или оператора + try { + if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { + // Авторизован аккаунт администратора или оператора - // Инициализация данных аккаунта - $account = model::read('d._key == "' . $parameters['id'] . '"'); + // Инициализация данных аккаунта + $account = model::read('d._key == "' . $parameters['id'] . '"'); - if (!empty($account)) { - // Найден аккаунт + if (!empty($account)) { + // Найден аккаунт - // Блокирование - $account->active = true; - $account->banned = false; + // Блокирование + $account->active = true; + $account->banned = false; - if (_core::update($account)) { - // Записаны данные аккаунта + if (_core::update($account)) { + // Записаны данные аккаунта - // Запись заголовков ответа - header('Content-Type: application/json'); - header('Content-Encoding: none'); - header('X-Accel-Buffering: no'); + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); - // Инициализация буфера вывода - ob_start(); + // Инициализация буфера вывода + ob_start(); - // Генерация ответа - echo json_encode([ - 'unbanned' => true, - 'errors' => self::parse_only_text($this->errors) - ]); + // Генерация ответа + echo json_encode([ + 'unbanned' => true, + 'errors' => self::parse_only_text($this->errors) + ]); - // Запись заголовков ответа - header('Content-Length: ' . ob_get_length()); + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); - // Отправка и деинициализация буфера вывода - ob_end_flush(); - flush(); - } else throw new exception('Не удалось записать изменения в базу данных'); - } else throw new exception('Не удалось найти аккаунт'); + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } else throw new exception('Не удалось записать изменения в базу данных'); + } else throw new exception('Не удалось найти аккаунт'); + } + } catch (exception $e) { + // Write to the errors registry + $this->errors['account'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Инициализация буфера ответа + $return = [ + 'unbanned' => false, + 'errors' => self::parse_only_text($this->errors) + ]; + + // Генерация ответа + echo json_encode($return); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); } + // Возврат (провал) return null; } diff --git a/mirzaev/ebala/system/controllers/administrator.php b/mirzaev/ebala/system/controllers/administrator.php index db5b95e..045ade6 100755 --- a/mirzaev/ebala/system/controllers/administrator.php +++ b/mirzaev/ebala/system/controllers/administrator.php @@ -234,12 +234,35 @@ final class administrator extends core if (empty($account)) throw new exception('Не удалось создать аккаунт'); } catch (exception $e) { // Write to the errors registry - $this->errors['account'][] = [ + $this->errors['administrator'][] = [ 'text' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'stack' => $e->getTrace() ]; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Инициализация буфера ответа + $return = [ + 'errors' => self::parse_only_text($this->errors) + ]; + + // Генерация ответа + echo json_encode($return); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); } // Инициализация идентификатора аккаунта (ключ документа инстанции аккаунта в базе данных) diff --git a/mirzaev/ebala/system/controllers/core.php b/mirzaev/ebala/system/controllers/core.php index 7619563..728d5f9 100755 --- a/mirzaev/ebala/system/controllers/core.php +++ b/mirzaev/ebala/system/controllers/core.php @@ -56,7 +56,7 @@ class core extends controller public function __construct(bool $initialize = true) { // Блокировка запросов от CloudFlare - if ($_SERVER['HTTP_USER_AGENT'] === 'nginx-ssl early hints') return; + if (!empty($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] === 'nginx-ssl early hints') return; parent::__construct($initialize); diff --git a/mirzaev/ebala/system/controllers/market.php b/mirzaev/ebala/system/controllers/market.php index b529668..65c35c1 100755 --- a/mirzaev/ebala/system/controllers/market.php +++ b/mirzaev/ebala/system/controllers/market.php @@ -341,7 +341,7 @@ final class market extends core throw new exception('Не инициализирован аккаунт'); } catch (exception $e) { // Write to the errors registry - $this->errors['account'][] = [ + $this->errors['market'][] = [ 'text' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), @@ -398,49 +398,83 @@ final class market extends core */ public function ban(array $parameters = []): ?string { - if ($this->account->status() && $this->account->type === 'market') { - // Авторизован аккаунт магазина + try { + if ($this->account->status() && $this->account->type === 'market') { + // Авторизован аккаунт магазина - // Инициализация данных магазина - $market = account::market($this->account->getId()); + // Инициализация данных магазина + $market = account::market($this->account->getId()); - if ($market instanceof _document) { - // Найден магазин + if ($market instanceof _document) { + // Найден магазин - // Блокировка сотрудника - if (!in_array($parameters['worker'], $market->bans ??= [], true)) $market->bans = $market->bans + [$parameters['worker']]; + // Блокировка сотрудника + if (!in_array($parameters['worker'], $market->bans ??= [], true)) $market->bans = $market->bans + [$parameters['worker']]; - if (_core::update($market)) { - // Записаны данные магазина + if (_core::update($market)) { + // Записаны данные магазина - // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) - $this->view->page = null; + // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) + $this->view->page = null; - // Запись заголовков ответа - header('Content-Type: application/json'); - header('Content-Encoding: none'); - header('X-Accel-Buffering: no'); + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); - // Инициализация буфера вывода - ob_start(); + // Инициализация буфера вывода + ob_start(); - // Инициализация буфера ответа - $return = [ - 'banned' => true, - 'errors' => self::parse_only_text($this->errors) - ]; + // Инициализация буфера ответа + $return = [ + 'banned' => true, + 'errors' => self::parse_only_text($this->errors) + ]; - // Генерация ответа - echo json_encode($return); + // Генерация ответа + echo json_encode($return); - // Запись заголовков ответа - header('Content-Length: ' . ob_get_length()); + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); - // Отправка и деинициализация буфера вывода - ob_end_flush(); - flush(); - } else throw new exception('Не удалось записать изменения в базу данных'); - } else throw new exception('Не удалось найти магазин'); + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } else throw new exception('Не удалось записать изменения в базу данных'); + } else throw new exception('Не удалось найти магазин'); + } + } catch (exception $e) { + // Write to the errors registry + $this->errors['market'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Инициализация буфера ответа + $return = [ + 'banned' => false, + 'errors' => self::parse_only_text($this->errors) + ]; + + // Генерация ответа + echo json_encode($return); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); } // Возврат (провал) @@ -454,49 +488,83 @@ final class market extends core */ public function unban(array $parameters = []): ?string { - if ($this->account->status() && $this->account->type === 'market') { - // Авторизован аккаунт магазина + try { + if ($this->account->status() && $this->account->type === 'market') { + // Авторизован аккаунт магазина - // Инициализация данных магазина - $market = account::market($this->account->getId()); + // Инициализация данных магазина + $market = account::market($this->account->getId()); - if ($market instanceof _document) { - // Найден магазин + if ($market instanceof _document) { + // Найден магазин - // Разблокировка сотрудника - if (in_array($parameters['worker'], $market->bans ??= [], true)) $market->bans = array_diff($market->bans ??= [], [$parameters['worker']]); + // Разблокировка сотрудника + if (in_array($parameters['worker'], $market->bans ??= [], true)) $market->bans = array_diff($market->bans ??= [], [$parameters['worker']]); - if (_core::update($market)) { - // Записаны данные магазина + if (_core::update($market)) { + // Записаны данные магазина - // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) - $this->view->page = null; + // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) + $this->view->page = null; - // Запись заголовков ответа - header('Content-Type: application/json'); - header('Content-Encoding: none'); - header('X-Accel-Buffering: no'); + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); - // Инициализация буфера вывода - ob_start(); + // Инициализация буфера вывода + ob_start(); - // Инициализация буфера ответа - $return = [ - 'unbanned' => true, - 'errors' => self::parse_only_text($this->errors) - ]; + // Инициализация буфера ответа + $return = [ + 'unbanned' => true, + 'errors' => self::parse_only_text($this->errors) + ]; - // Генерация ответа - echo json_encode($return); + // Генерация ответа + echo json_encode($return); - // Запись заголовков ответа - header('Content-Length: ' . ob_get_length()); + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); - // Отправка и деинициализация буфера вывода - ob_end_flush(); - flush(); - } else throw new exception('Не удалось записать изменения в базу данных'); - } else throw new exception('Не удалось найти магазин'); + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } else throw new exception('Не удалось записать изменения в базу данных'); + } else throw new exception('Не удалось найти магазин'); + } + } catch (exception $e) { + // Write to the errors registry + $this->errors['market'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Инициализация буфера ответа + $return = [ + 'unbanned' => false, + 'errors' => self::parse_only_text($this->errors) + ]; + + // Генерация ответа + echo json_encode($return); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); } // Возврат (провал) @@ -510,94 +578,15 @@ final class market extends core */ public function banned(array $parameters = []): ?string { - if ($this->account->status() && $this->account->type === 'market') { - // Авторизован аккаунт магазина + try { + if ($this->account->status() && $this->account->type === 'market') { + // Авторизован аккаунт магазина - // Инициализация данных магазина - $market = account::market($this->account->getId()); + // Инициализация данных магазина + $market = account::market($this->account->getId()); - if ($market instanceof _document) { - // Найден магазин - - // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) - $this->view->page = null; - - // Запись заголовков ответа - header('Content-Type: application/json'); - header('Content-Encoding: none'); - header('X-Accel-Buffering: no'); - - // Инициализация буфера вывода - ob_start(); - - // Инициализация буфера ответа - $return = [ - 'banned' => in_array($parameters['worker'], $market->bans ?? [], true), - 'errors' => self::parse_only_text($this->errors) - ]; - - // Генерация ответа - echo json_encode($return); - - // Запись заголовков ответа - header('Content-Length: ' . ob_get_length()); - - // Отправка и деинициализация буфера вывода - ob_end_flush(); - flush(); - } else throw new exception('Не удалось найти магазин'); - } - - return null; - } - - /** - * Обновить данные - * - * @param array $parameters Параметры запроса - */ - public function update(array $parameters = []): ?string - { - if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { - // Авторизован аккаунт администратора или оператора - - // Инициализация данных магазина - $market = model::read('d.id == "' . urldecode($parameters['id']) . '"'); - - if (!empty($market)) { - // Найден магазин - - // Инициализация параметров (перезапись переданными значениями) - if ($parameters['name_first'] !== $market->name['first']) $market->name = ['first' => $parameters['name_first']] + $market->name; - if ($parameters['name_second'] !== $market->name['second']) $market->name = ['second' => $parameters['name_second']] + $market->name; - if ($parameters['name_last'] !== $market->name['last']) $market->name = ['last' => $parameters['name_last']] + $market->name; - if ($parameters['number'] !== $market->number) - if (mb_strlen($parameters['number']) === 11) $market->number = $parameters['number']; - else throw new exception('Номер должен состоять из 11 символов'); - if ($parameters['mail'] !== $market->mail) $market->mail = $parameters['mail']; - if ($parameters['type'] !== $market->type) $market->type = $parameters['type']; - if ($parameters['city'] !== $market->city) $market->city = $parameters['city']; - if ($parameters['district'] !== $market->district) $market->district = $parameters['district']; - if ($parameters['address'] !== $market->address) $market->address = $parameters['address']; - if (!in_array($parameters['ban'], $market->bans, true)) $market->bans[] = $parameters['ban']; - - if (_core::update($market)) { - // Записаны данные магазина - - // Инициализация строки в глобальную переменную шаблонизатора - $this->view->rows = registry::markets( - before: sprintf( - "FILTER a._id == '%s' && a.deleted != true", - $market->getId() - ), - after: <<view->page = null; @@ -612,8 +601,7 @@ final class market extends core // Инициализация буфера ответа $return = [ - 'updated' => true, - 'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'markets.html'), + 'banned' => in_array($parameters['worker'], $market->bans ?? [], true), 'errors' => self::parse_only_text($this->errors) ]; @@ -626,8 +614,155 @@ final class market extends core // Отправка и деинициализация буфера вывода ob_end_flush(); flush(); - } else throw new exception('Не удалось записать изменения в базу данных'); - } else throw new exception('Не удалось найти аккаунт'); + } else throw new exception('Не удалось найти магазин'); + } + } catch (exception $e) { + // Write to the errors registry + $this->errors['market'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Инициализация буфера ответа + $return = [ + 'errors' => self::parse_only_text($this->errors) + ]; + + // Генерация ответа + echo json_encode($return); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } + + return null; + } + + /** + * Обновить данные + * + * @param array $parameters Параметры запроса + */ + public function update(array $parameters = []): ?string + { + try { + if ($this->account->status() && ($this->account->type === 'administrator' || $this->account->type === 'operator')) { + // Авторизован аккаунт администратора или оператора + + // Инициализация данных магазина + $market = model::read('d.id == "' . urldecode($parameters['id']) . '"'); + + if (!empty($market)) { + // Найден магазин + + // Инициализация параметров (перезапись переданными значениями) + if ($parameters['name_first'] !== $market->name['first']) $market->name = ['first' => $parameters['name_first']] + $market->name; + if ($parameters['name_second'] !== $market->name['second']) $market->name = ['second' => $parameters['name_second']] + $market->name; + if ($parameters['name_last'] !== $market->name['last']) $market->name = ['last' => $parameters['name_last']] + $market->name; + if ($parameters['number'] !== $market->number) + if (mb_strlen($parameters['number']) === 11) $market->number = $parameters['number']; + else throw new exception('Номер должен состоять из 11 символов'); + if ($parameters['mail'] !== $market->mail) $market->mail = $parameters['mail']; + if ($parameters['type'] !== $market->type) $market->type = $parameters['type']; + if ($parameters['city'] !== $market->city) $market->city = $parameters['city']; + if ($parameters['district'] !== $market->district) $market->district = $parameters['district']; + if ($parameters['address'] !== $market->address) $market->address = $parameters['address']; + if (!in_array($parameters['ban'], $market->bans, true)) $market->bans[] = $parameters['ban']; + + if (_core::update($market)) { + // Записаны данные магазина + + // Инициализация строки в глобальную переменную шаблонизатора + $this->view->rows = registry::markets( + before: sprintf( + "FILTER a._id == '%s' && a.deleted != true", + $market->getId() + ), + after: <<view->page = null; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Инициализация буфера ответа + $return = [ + 'updated' => true, + 'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'markets.html'), + 'errors' => self::parse_only_text($this->errors) + ]; + + // Генерация ответа + echo json_encode($return); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } else throw new exception('Не удалось записать изменения в базу данных'); + } else throw new exception('Не удалось найти аккаунт'); + } + } catch (exception $e) { + // Write to the errors registry + $this->errors['market'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Инициализация буфера ответа + $return = [ + 'updated' => false, + 'errors' => self::parse_only_text($this->errors) + ]; + + // Генерация ответа + echo json_encode($return); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); } // Возврат (провал) diff --git a/mirzaev/ebala/system/controllers/operator.php b/mirzaev/ebala/system/controllers/operator.php index 0b9c1d4..0fce30f 100755 --- a/mirzaev/ebala/system/controllers/operator.php +++ b/mirzaev/ebala/system/controllers/operator.php @@ -233,12 +233,35 @@ final class operator extends core if (empty($account)) throw new exception('Не удалось создать аккаунт'); } catch (exception $e) { // Write to the errors registry - $this->errors['account'][] = [ + $this->errors['operator'][] = [ 'text' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'stack' => $e->getTrace() ]; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Инициализация буфера ответа + $return = [ + 'errors' => self::parse_only_text($this->errors) + ]; + + // Генерация ответа + echo json_encode($return); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); } // Инициализация идентификатора аккаунта (ключ документа инстанции аккаунта в базе данных) diff --git a/mirzaev/ebala/system/controllers/socket.php b/mirzaev/ebala/system/controllers/socket.php new file mode 100755 index 0000000..93b1e84 --- /dev/null +++ b/mirzaev/ebala/system/controllers/socket.php @@ -0,0 +1,62 @@ + + */ +final class socket extends core +{ + use errors; + + /** + * Регистрация + * + * @param array $parameters Параметры запроса + */ + public function registration(array $parameters = []): ?string + { + try { + if (model::session($this->session->getId(), $parameters['key'], $this->errors)) { + // Инициализировано соединение + + // @todo сделать например возврат того что соединение удалось и на клиенте что-то оптимизировать (или не удалось чтобы повторил) + return null; + } else throw new exception('Не удалось зарегистрировать сокет'); + } catch (exception $e) { + // Write to the errors registry + $this->errors['socket'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Возврат (провал) + return null; + } +} diff --git a/mirzaev/ebala/system/controllers/task.php b/mirzaev/ebala/system/controllers/task.php index 9719eac..bff20e3 100755 --- a/mirzaev/ebala/system/controllers/task.php +++ b/mirzaev/ebala/system/controllers/task.php @@ -274,6 +274,7 @@ final class task extends core ), after: 'COLLECT x = worker, y = market, z = task OPTIONS { method: "sorted" }', page: (int) $this->session->buffer['worker']['tasks']['page'], + sort: 'z.date DESC, z.created DESC, z._key DESC, x.created DESC, y.created DESC, x._key DESC, y._key DESC', target: empty($search) ? model::COLLECTION : 'registry_tasks', return: '{task: z, worker: x, market: y}', binds: ['worker' => account::worker($this->account->getId())?->id] + (empty($search) ? [] : ['search' => $search]) @@ -292,6 +293,7 @@ final class task extends core ), after: 'COLLECT x = worker, y = market, z = task OPTIONS { method: "sorted" }', page: (int) $this->session->buffer['market']['tasks']['page'], + sort: 'z.date DESC, z.created DESC, z._key DESC, x.created DESC, y.created DESC, x._key DESC, y._key DESC', target: empty($search) ? model::COLLECTION : 'registry_tasks', return: '{task: z, worker: x, market: y}', binds: ['market' => account::market($this->account->getId())?->id] + (empty($search) ? [] : ['search' => $search]) @@ -310,6 +312,7 @@ final class task extends core ), after: 'COLLECT x = worker, y = market, z = task OPTIONS { method: "sorted" }', page: (int) $this->session->buffer['operator']['tasks']['page'], + sort: 'z.date DESC, z.created DESC, z._key DESC, x.created DESC, y.created DESC, x._key DESC, y._key DESC', target: empty($search) ? model::COLLECTION : 'registry_tasks', return: '{task: z, worker: x, market: y}', binds: empty($search) ? [] : [ @@ -448,7 +451,6 @@ final class task extends core // Перенос времени в дату $start = $date->setTime((int) $start->format('H'), (int) $start->format('i')); - // Запись в буфер $link->task = ['start' => $start] + $link->task; } @@ -469,6 +471,11 @@ final class task extends core $link->task = ['end' => $end] + $link->task; } + // Инициализация данных заблокировавшего аккаунта + if (isset($link->task['block']) && $link->task['block']['expires'] > time() && $link->task['block']['account'] !== (int) $account->getKey()) + $link->task = ['block' => ['_account' => account::read('d._key == "' . $link->task['block']['account'] . '"')] + $link->task['block']] + $link->task; + else $link->task = ['block' => null] + $link->task; + // Инициализация буфера сгенерированных данных работы для шаблонизатора $generated = []; @@ -642,49 +649,59 @@ final class task extends core // Найден сотрудник (запрашиваемый для записи сотрудник существует в базе данных) if (!$worker->fired) { - if ($task->worker !== $parameters['worker']) { - // Идентификатор запрашиваемого сотрудника не равен актуальному + // Не уволен сотрудник - // Запись сотрудника - $task->worker = $worker->id; + if (in_array($task->work, $worker->works, true)) { + // Работа соответствует подходящим сотруднику - // Снятие с публикации - $task->published = false; + if ((($age = (new DateTime)->diff(DateTime::createFromFormat('d.m.Y', $worker->birth))->y) > 15 && ($task->work === 'Выкладчик' || $task->work === 'Гастроном')) || $age > 17) { + // Подходит по возрасту сотрудник - if (_core::update($task)) { - // Записано изменение в базу данных + if ($task->worker !== $parameters['worker']) { + // Идентификатор запрашиваемого сотрудника не равен актуальному - // Инициализация строки в глобальную переменную шаблонизатора - $this->view->rows = static::preprocessing($this->account, model::list(before: 'FILTER task._key == "' . $parameters['task'] . '"', amount: 1)); + // Запись сотрудника + $task->worker = $worker->id; - // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) - $this->view->page = null; + // Снятие с публикации + $task->published = false; - // Запись заголовков ответа - header('Content-Type: application/json'); - header('Content-Encoding: none'); - header('X-Accel-Buffering: no'); + if (_core::update($task)) { + // Записано изменение в базу данных - // Инициализация буфера вывода - ob_start(); + // Инициализация строки в глобальную переменную шаблонизатора + $this->view->rows = static::preprocessing($this->account, model::list(before: 'FILTER task._key == "' . $parameters['task'] . '"', amount: 1)); - // Генерация ответа - echo json_encode( - [ - 'updated' => true, - 'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'tasks.html'), - 'errors' => self::parse_only_text($this->errors) - ] - ); + // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) + $this->view->page = null; - // Запись заголовков ответа - header('Content-Length: ' . ob_get_length()); + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); - // Отправка и деинициализация буфера вывода - ob_end_flush(); - flush(); - } else throw new exception('Не удалось записать изменения в базу данных'); - } else throw new exception('Сотрудник уже назначен'); + // Инициализация буфера вывода + ob_start(); + + // Генерация ответа + echo json_encode( + [ + 'updated' => true, + 'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'tasks.html'), + 'errors' => self::parse_only_text($this->errors) + ] + ); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } else throw new exception('Не удалось записать изменения в базу данных'); + } else throw new exception('Сотрудник уже назначен'); + } else throw new exception('Сотрудник не подходит по возрасту'); + } else throw new exception('Заявка не подходит по типу работы для сотрудника'); } else throw new exception('Нельзя назначить уволенного сотрудника'); } else throw new exception('Не найден сотрудник'); } @@ -834,9 +851,11 @@ final class task extends core // Инициализация данных сотрудника $this->view->task = model::read( 'd._key == "' . $parameters['task'] . '"', - return: $this->account->type === 'market' - ? '{_key: d._key, created: d.created, updated: d.updated, date: d.date, start: d.start, end: d.end, market: d.market, confirmed: d.confirmed, completed: d.completed }' - : '{_key: d._key, created: d.created, updated: d.updated, date: d.date, start: d.start, end: d.end, market: d.market, confirmed: d.confirmed, completed: d.completed, hided: d.hided, updates: d.updates }' + return: match ($this->account->type) { + 'market' => '{_key: d._key, created: d.created, updated: d.updated, date: d.date, start: d.start, end: d.end, market: d.market, confirmed: d.confirmed, completed: d.completed }', + 'administrator', 'operator' => '{_key: d._key, created: d.created, updated: d.updated, date: d.date, start: d.start, end: d.end, market: d.market, confirmed: d.confirmed, completed: d.completed, hided: d.hided, updates: d.updates }', + default => 'd' + } )->getAll(); // Заявка не принадлежит запросившему магазину? @@ -1020,7 +1039,7 @@ final class task extends core 'tax' => 'ИНН', 'city' => 'Город', 'payment' => 'Форма оплаты', - 'works' => 'Формы работ', + 'works' => 'Типы работ', default => $key }, 'value' => $value @@ -1128,6 +1147,7 @@ final class task extends core 'number' => 'Номер', 'mail' => 'Почта', 'commentary' => 'Комментарий', + 'bans' => 'Заблокированные', default => $key }, 'value' => $value @@ -1694,6 +1714,94 @@ final class task extends core } } + /** + * Сгенерировать строку + * + * Используется тогда, когда нужно получить HTML-код строки заявки + * + * @param array $parameters Параметры запроса + * + * @return void В буфер вывода JSON-документ с запрашиваемыми параметрами + */ + public function row(array $parameters = []): void + { + try { + if ($this->account->status()) { + // Авторизован аккаунт + + // Инициализация строки в глобальную переменную шаблонизатора + if ($this->account->type === 'worker') + $this->view->rows = model::list(before: 'FILTER task._key == "' . $parameters['task'] . '" && task.worker == "' . account::worker($this->account->getId())?->id . '"'); + else if ($this->account->type === 'operator') + $this->view->rows = model::list(before: 'FILTER task._key == "' . $parameters['task'] . '"'); + else if ($this->account->type === 'market') + $this->view->rows = model::list(before: 'FILTER task._key == "' . $parameters['task'] . '" && task.market == "' . account::market($this->account->getId())?->id . '"'); + else if ($this->account->type === 'administrator') + $this->view->rows = model::list(before: 'FILTER task._key == "' . $parameters['task'] . '"'); + else $this->view->rows = []; + + // Генерация данных для генерации строки + $this->view->rows = static::preprocessing($this->account, $this->view->rows); + + // Запись в глобальную переменную шаблонизатора обрабатываемой страницы (отключение) + $this->view->page = null; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Генерация ответа + echo json_encode( + [ + 'row' => $this->view->render(DIRECTORY_SEPARATOR . 'elements' . DIRECTORY_SEPARATOR . 'tasks.html'), + 'errors' => self::parse_only_text($this->errors) + ] + ); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } else throw new exception('Вы не авторизованы'); + } catch (exception $e) { + // Запись в реестр ошибок + $this->errors[] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + + // Запись заголовков ответа + header('Content-Type: application/json'); + header('Content-Encoding: none'); + header('X-Accel-Buffering: no'); + + // Инициализация буфера вывода + ob_start(); + + // Генерация ответа + echo json_encode( + [ + 'errors' => self::parse_only_text($this->errors) + ] + ); + + // Запись заголовков ответа + header('Content-Length: ' . ob_get_length()); + + // Отправка и деинициализация буфера вывода + ob_end_flush(); + flush(); + } + } + /** * Удалить * diff --git a/mirzaev/ebala/system/models/account.php b/mirzaev/ebala/system/models/account.php index 9338aac..4b38bb2 100755 --- a/mirzaev/ebala/system/models/account.php +++ b/mirzaev/ebala/system/models/account.php @@ -60,7 +60,7 @@ final class account extends core $this->document = $account->document; // Связь сессии с аккаунтом - session::connect($session->getId(), $this->document->getId(), $errors); + self::session($session->getId(), $this->document->getId(), $errors); // Блокировка доступа if ($account?->active !== true) throw new exception('Свяжитесь с оператором'); @@ -96,7 +96,7 @@ final class account extends core $this->document = $account; // Связь сессии с аккаунтом - session::connect($session->getId(), $this->document->getId(), $errors); + self::session($session->getId(), $this->document->getId(), $errors); // Удаление использованных данных из буфера сессии $session->write(['entry' => ['number' => null, 'password' => null]]); @@ -129,7 +129,7 @@ final class account extends core $this->document = $account; // Связь сессии с аккаунтом - session::connect($session->getId(), $this->document->getId(), $errors); + self::session($session->getId(), $this->document->getId(), $errors); // Удаление использованных данных из буфера сессии $session->write(['entry' => ['_key' => null, 'password' => null]]); @@ -155,7 +155,7 @@ final class account extends core $this->document = $account; // Связь сессии с аккаунтом - session::connect($session->getId(), $this->document->getId(), $errors); + self::session($session->getId(), $this->document->getId(), $errors); // Удаление использованных данных из буфера сессии $session->write(['entry' => ['_key' => null, 'password' => null]]); @@ -181,7 +181,7 @@ final class account extends core $this->document = $account; // Связь сессии с аккаунтом - session::connect($session->getId(), $this->document->getId(), $errors); + self::session($session->getId(), $this->document->getId(), $errors); // Удаление использованных данных из буфера сессии $session->write(['entry' => ['_key' => null, 'password' => null]]); @@ -318,7 +318,7 @@ final class account extends core } /** - * Инициализировать связь аккаунта с сотрудником + * Подключить к сотруднику * * Ищет связь аккаунта с сотрудником, если не находит, то создаёт её * @@ -328,6 +328,9 @@ final class account extends core * @param array &$errors Реестр ошибок * * @return bool Связан аккаунт с сотрудником? + * + * @todo + * 1. Переделать на подобие account::session и перенести в mirzaev/ebala/models/worker */ public static function connect(string $account, string $target, string $type = 'worker', array &$errors = []): bool { @@ -403,6 +406,65 @@ final class account extends core return null; } + /** + * Подключить к сессии + * + * Ищет связь сессии с аккаунтом, если не находит, то создаёт её + * + * @param string $session Идентификатор сессии + * @param string $account Идентификатор аккаунта + * @param array &$errors Реестр ошибок + * + * @return bool Аккаунт подключен к сессии? + */ + public static function session(string $session, string $account, array &$errors = []): bool + { + try { + if ( + collection::init(static::$arangodb->session, self::COLLECTION) + && collection::init(static::$arangodb->session, session::COLLECTION) + && collection::init(static::$arangodb->session, session::COLLECTION . '_edge_' . self::COLLECTION, true) + ) { + // Инициализированы коллекции + + if ( + collection::search(static::$arangodb->session, sprintf( + <<session, session::COLLECTION . '_edge_' . self::COLLECTION, [ + '_from' => $session, + '_to' => $account + ]) + ) { + // Найдено, либо создано ребро: session -> account + + // Возврат (успех) + return true; + } else throw new exception('Не удалось создать ребро: session -> account'); + } else throw new exception('Не удалось инициализировать коллекции'); + } catch (exception $e) { + // Запись в реестр ошибок + $errors['account'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Возврат (провал) + return false; + } + + /** * Записать * diff --git a/mirzaev/ebala/system/models/core.php b/mirzaev/ebala/system/models/core.php index 658cebc..f45ea72 100755 --- a/mirzaev/ebala/system/models/core.php +++ b/mirzaev/ebala/system/models/core.php @@ -35,7 +35,7 @@ class core extends model /** * Путь до файла с настройками подключения к базе данных ArangoDB */ - final public const ARANGODB = '../settings/arangodb.php'; + final public const ARANGODB = __DIR__ . '/../settings/arangodb.php'; /** * Соединение с базой данных ArangoDB @@ -203,7 +203,8 @@ class core extends model * * @return int|null Количество документов в базе данных, если найдены */ - public static function count(?string $collection = null, array &$errors = []): int|null { + public static function count(?string $collection = null, array &$errors = []): int|null + { try { if (collection::init(static::$arangodb->session, static::COLLECTION)) { // Инициализирована коллекция diff --git a/mirzaev/ebala/system/models/session.php b/mirzaev/ebala/system/models/session.php index 0e7ac22..d02ff18 100755 --- a/mirzaev/ebala/system/models/session.php +++ b/mirzaev/ebala/system/models/session.php @@ -164,67 +164,12 @@ final class session extends core // Закрыть сессию } - /** - * Инициализировать связь сессии с аккаунтом - * - * Ищет связь сессии с аккаунтом, если не находит, то создаёт её - * - * @param account $account Инстанция аккаунта - * @param array &$errors Реестр ошибок - * - * @return bool Связана сессия с аккаунтом? - */ - public static function connect(string $session, string $account, array &$errors = []): bool - { - try { - if ( - collection::init(static::$arangodb->session, self::COLLECTION) - && collection::init(static::$arangodb->session, account::COLLECTION) - && collection::init(static::$arangodb->session, self::COLLECTION . '_edge_' . account::COLLECTION, true) - ) { - // Инициализированы коллекции - - if ( - collection::search(static::$arangodb->session, sprintf( - <<session, self::COLLECTION . '_edge_' . account::COLLECTION, [ - '_from' => $session, - '_to' => $account - ]) - ) { - // Найдено, либо создано ребро: session -> account - - return true; - } else throw new exception('Не удалось создать ребро: session -> account'); - } else throw new exception('Не удалось инициализировать коллекции'); - } catch (exception $e) { - // Запись в реестр ошибок - $errors[] = [ - 'text' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine(), - 'stack' => $e->getTrace() - ]; - } - - return false; - } - /** * Найти связанный аккаунт * * @param array &$errors Реестр ошибок * - * @return ?account Инстанция аккаунта, если удалось найти + * @return account|null Инстанция аккаунта, если удалось найти */ public function account(array &$errors = []): ?account { @@ -272,7 +217,8 @@ final class session extends core 'stack' => $e->getTrace() ]; } - + + // Возврат (провал) return null; } diff --git a/mirzaev/ebala/system/models/socket.php b/mirzaev/ebala/system/models/socket.php new file mode 100755 index 0000000..91b24f1 --- /dev/null +++ b/mirzaev/ebala/system/models/socket.php @@ -0,0 +1,164 @@ + + */ +final class socket extends core +{ + use instance, status; + + /** + * Collection name in ArangoDB + */ + final public const COLLECTION = 'socket'; + + /** + * Инстанция документа в базе данных + */ + protected readonly _document $document; + + /** + * Подключение к сессии + * + * @param string $session Идентификатор сессии + * @param string $key Ключ для регистрации (из документа в static::COLLECTION) + * @param array &$errors Реестр ошибок + * + * @return bool Сокет подключен к сессии? + */ + public static function session(string $session, string $key, array &$errors = []): bool + { + try { + if ( + collection::init(static::$arangodb->session, self::COLLECTION) + && collection::init(static::$arangodb->session, session::COLLECTION) + && collection::init(static::$arangodb->session, $edge = session::COLLECTION . '_edge_' . self::COLLECTION, true) + ) { + // Инициализирована коллекция + + // Чтение сокета + $socket = self::read( + filter: "d.key == \"$key\" && d.expires > DATE_NOW() / 1000", + sort: 'd.created desc, d.expires desc', + amount: 1, + ); + + if ($socket instanceof _document) { + // Найден сокет + + if (document::write(static::$arangodb->session, $edge, [ + '_from' => $session, + '_to' => $socket->getId() + ])) { + // Записано ребро: СЕССИЯ -> СОКЕТ + + // Возврат (успех) + return true; + } else throw new exception('Не удалось записать изменения в базу данных'); + } else throw new exception('Не удалось найти сокет'); + } else throw new exception('Не удалось инициализировать коллекции'); + } catch (exception $e) { + // Write to the errors registry + $errors['socket'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Возврат (провал) + return false; + } + + /** + * Найти аккаунт + * + * Ищет аккаунт по идентификатору подключения к сокету + * + * @param int $socket Идентификатор подключения к сокету (во внутренней базе данных OpenSwoole) + * @param array &$errors Реестр ошибок + * + * @returnaccount|null Аккаунт, если найден + */ + public static function account(int $socket, array &$errors = []): ?account + { + try { + if ( + collection::init(static::$arangodb->session, self::COLLECTION) + && collection::init(static::$arangodb->session, session::COLLECTION) + && collection::init(static::$arangodb->session, account::COLLECTION) + && collection::init(static::$arangodb->session, session::COLLECTION . '_edge_' . self::COLLECTION, true) + && collection::init(static::$arangodb->session, session::COLLECTION . '_edge_' . account::COLLECTION, true) + ) { + // Инициализированы коллекции + + // Инициализация инстанции аккаунта + $account = new account; + + // Поиск инстанции аккаунта в базе данных + $instance = $account->instance(collection::search(static::$arangodb->session, sprintf( + << DATE_NOW() / 1000 + SORT s.created desc, s.expires desc + LIMIT 1 + RETURN s + )[0]._id + %s + RETURN s + )[0]._id + %s + RETURN a + AQL, + socket::COLLECTION, + $socket, + session::COLLECTION . '_edge_' . socket::COLLECTION, + session::COLLECTION . '_edge_' . account::COLLECTION, + ))); + + // Возврат (успех) + return $instance instanceof _document ? $account : throw new exception('Не удалось найти инстанцию аккаунта в базе данных'); + } else throw new exception('Не удалось инициализировать коллекцию'); + } catch (exception $e) { + // Запись в реестр ошибок + $errors['socket'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Возврат (провал) + return null; + } +} diff --git a/mirzaev/ebala/system/models/task.php b/mirzaev/ebala/system/models/task.php index 3717620..6ba6e1d 100755 --- a/mirzaev/ebala/system/models/task.php +++ b/mirzaev/ebala/system/models/task.php @@ -294,4 +294,100 @@ final class task extends core // Exit (fail) return null; } + + /** + * Block by account + * + * @param int $task (_key) + * @param int $account (_key) + * @param array $errors + * + * @return bool Task is blocked? + */ + public static function block( + int $task, + int $account, + array &$errors = [] + ): bool { + try { + if (collection::init(static::$arangodb->session, self::COLLECTION)) { + // Инициализирована коллекция + + if (($task = static::read('d._key == "' . $task . '"')) instanceof _document) { + // Найдена заявка + + if ($task->block === null || $task->block['expires'] < time()) { + // Не заблокирована заявка + + // Блокировка + $task->block = ['account' => $account, 'expires' => (int) strtotime('+1 minute')]; + + // Запись обновления в базу данных и возврат (успех) + return core::update($task); + } else throw new exception('Заявка уже заблокирована: ' . $task->block['account']); + } else throw new exception('Не удалось инициализировать коллекции'); + } else throw new exception('Не удалось инициализировать коллекции'); + } catch (exception $e) { + // Write to the errors registry + $errors['task'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Exit (fail) + return false; + } + + /** + * Unblock by account + * + * @param int $task (_key) + * @param int $account (_key) + * @param array $errors + * + * @return bool Task is blocked? + */ + public static function unblock( + int $task, + int $account, + array &$errors = [] + ): bool { + try { + if (collection::init(static::$arangodb->session, self::COLLECTION)) { + // Инициализирована коллекция + + if (($task = static::read('d._key == "' . $task . '"')) instanceof _document) { + // Найдена заявка + + if ($task->block !== null) { + // Заблокирована заявка + + if ($task->block['account'] === $account || $task->block['expires'] > time()) { + // Аккаунт отменяет свою блокировку или истекло время блокировки + + // Разблокировка + $task->block = null; + + // Запись обновления в базу данных и возврат (успех) + return core::update($task); + } + } else throw new exception('Заявка не заблокирована'); + } else throw new exception('Не удалось инициализировать коллекции'); + } else throw new exception('Не удалось инициализировать коллекции'); + } catch (exception $e) { + // Write to the errors registry + $errors['task'][] = [ + 'text' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'stack' => $e->getTrace() + ]; + } + + // Exit (fail) + return false; + } } diff --git a/mirzaev/ebala/system/public/css/connection.css b/mirzaev/ebala/system/public/css/connection.css new file mode 100755 index 0000000..43ab17e --- /dev/null +++ b/mirzaev/ebala/system/public/css/connection.css @@ -0,0 +1,35 @@ +@charset "UTF-8"; + +#connection { + z-index: 999999; + position: fixed; + bottom: 20px; + left: 20px; + height: 16px; + display: flex; +} + +#connection > i#indicator { + width: 16px; + height: 16px; + display: block; + cursor: help; + border-radius: 100%; +} + +#connection > small { + margin-left: 7px; + height: 16px; + display: flex; + align-items: center; + font-weight: bold; + color: var(--socket-text); +} + +#connection > i#indicator.disconnected:not(.connected) { + background-color: var(--socket-disconnected); +} + +#connection > i#indicator.connected:not(.disconnected) { + background-color: var(--socket-connected); +} diff --git a/mirzaev/ebala/system/public/css/fonts/dejavu.css b/mirzaev/ebala/system/public/css/fonts/dejavu.css old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/css/fonts/fira.css b/mirzaev/ebala/system/public/css/fonts/fira.css old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/css/fonts/hack.css b/mirzaev/ebala/system/public/css/fonts/hack.css old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/css/icons/home.css b/mirzaev/ebala/system/public/css/icons/home.css old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/css/icons/shopping_cart.css b/mirzaev/ebala/system/public/css/icons/shopping_cart.css old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/css/icons/smartphone.css b/mirzaev/ebala/system/public/css/icons/smartphone.css old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/css/icons/timer.css b/mirzaev/ebala/system/public/css/icons/timer.css old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/css/icons/user.css b/mirzaev/ebala/system/public/css/icons/user.css old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/css/icons/work_alt.css b/mirzaev/ebala/system/public/css/icons/work_alt.css old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/css/list.css b/mirzaev/ebala/system/public/css/list.css index 14df95e..517e7f5 100755 --- a/mirzaev/ebala/system/public/css/list.css +++ b/mirzaev/ebala/system/public/css/list.css @@ -46,7 +46,7 @@ div#popup>section.list>div.row.endless { section.panel.list> :is(form, search).row.menu>label>div:has(>button) { position: relative; - display: flex; + display: flex; height: 30px; } @@ -112,13 +112,14 @@ section.panel.list>div#title>span { } section.panel.list>div.row { + --width: calc(100% - 24px); --gap: 12px; --background: var(--cloud); position: relative; left: 0px; - width: calc(100% - 24px); height: 35px; display: flex; + width: var(--width, calc(100% - 24px)); gap: var(--gap, 12px); padding: 0 var(--gap, 12px); border-radius: 0px; @@ -138,7 +139,7 @@ section.panel.list>div.row:not(:nth-of-type(1))>span { -moz-box-shadow: var(--box-shadow); } -section.panel.list>div.row:not(:nth-of-type(1)):is(:hover, :focus) { +section.panel.list>div.row:not(:nth-of-type(1)):not([data-blocked]):is(:hover, :focus) { --padding-left: 24px; --padding-right: 24px; left: -12px; @@ -281,7 +282,7 @@ section.panel.list>div.row>span.field { cursor: text; } -section.panel.list>div.row:not(:nth-of-type(1))>span:is(.important, .interactive:is(:hover, :focus)) { +section.panel.list>div.row:not(:nth-of-type(1)):not([data-blocked])>span:is(.important, .interactive:is(:hover, :focus)) { --margin: calc(var(--gap) / 2); --border-left: calc(var(--padding-left, var(--margin, 0px)) * -1); --border-right: var(--padding-right, var(--margin, 0px)); @@ -482,3 +483,29 @@ section.panel.list>div.row[data-row="worker"]:not(:nth-of-type(1)).fired>span:is section.panel.list>div.row[data-row="worker"]:not(:nth-of-type(1)):nth-child(2n + 1).fired>span:is(.important, .interactive:is(:hover, :focus)) { --background: var(--magma-important-above); } + +section.panel.list>div.row[data-blocked] { + margin-left: 8px; + width: calc(var(--width) - 16px); + cursor: progress; + opacity: 70% !important; +} + +section.panel.list>div.row[data-blocked]:before { + content: attr(data-blocked) !important; + color: var(--earth-text-important-below); +} + +section.panel.list>div.row[data-blocked] * { + pointer-events: none; +} + +section.panel.list>div.row:not([data-blocked]):has(+ div.row[data-blocked]) { + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; +} + +section.panel.list>div.row[data-blocked]+div.row:not([data-blocked]) { + border-top-left-radius: 10px; + border-top-right-radius: 10px; +} diff --git a/mirzaev/ebala/system/public/css/popup.css b/mirzaev/ebala/system/public/css/popup.css old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/css/themes/harmony/earth.css b/mirzaev/ebala/system/public/css/themes/harmony/earth.css old mode 100644 new mode 100755 index bb48b41..fcbff27 --- a/mirzaev/ebala/system/public/css/themes/harmony/earth.css +++ b/mirzaev/ebala/system/public/css/themes/harmony/earth.css @@ -167,6 +167,10 @@ --link: #3c76ff; --link-hover: #6594ff; --link-active: #3064dd; + + --socket-connected: #2be851; + --socket-disconnected: #8e8181; + --socket-text: #b09999; } body { diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSans-Bold.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSans-Bold.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSans-BoldOblique.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSans-BoldOblique.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSans-ExtraLight.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSans-ExtraLight.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSans-Oblique.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSans-Oblique.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSans.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSans.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansCondensed-Bold.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansCondensed-Bold.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansCondensed-BoldOblique.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansCondensed-BoldOblique.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansCondensed-Oblique.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansCondensed-Oblique.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansCondensed.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansCondensed.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansMono-Bold.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansMono-Bold.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansMono-BoldOblique.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansMono-BoldOblique.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansMono-Oblique.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansMono-Oblique.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansMono.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSansMono.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerif-Bold.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerif-Bold.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerif-BoldItalic.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerif-BoldItalic.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerif-Italic.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerif-Italic.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerif.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerif.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerifCondensed-Bold.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerifCondensed-Bold.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerifCondensed-BoldItalic.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerifCondensed-BoldItalic.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerifCondensed-Italic.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerifCondensed-Italic.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerifCondensed.ttf b/mirzaev/ebala/system/public/fonts/dejavu/DejaVuLGCSerifCondensed.ttf old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraMono-Bold.woff b/mirzaev/ebala/system/public/fonts/fira/FiraMono-Bold.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraMono-Bold.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraMono-Bold.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraMono-Medium.woff b/mirzaev/ebala/system/public/fonts/fira/FiraMono-Medium.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraMono-Medium.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraMono-Medium.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraMono-Regular.woff b/mirzaev/ebala/system/public/fonts/fira/FiraMono-Regular.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraMono-Regular.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraMono-Regular.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Bold.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Bold.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Bold.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Bold.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-BoldItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-BoldItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-BoldItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-BoldItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Book.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Book.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Book.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Book.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-BookItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-BookItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-BookItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-BookItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Eight.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Eight.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Eight.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Eight.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-EightItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-EightItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-EightItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-EightItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraBold.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraBold.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraBold.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraBold.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraBoldItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraBoldItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraBoldItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraBoldItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraLight.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraLight.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraLight.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraLight.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraLightItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraLightItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraLightItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-ExtraLightItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Four.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Four.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Four.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Four.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-FourItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-FourItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-FourItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-FourItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Hair.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Hair.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Hair.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Hair.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-HairItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-HairItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-HairItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-HairItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Heavy.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Heavy.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Heavy.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Heavy.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-HeavyItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-HeavyItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-HeavyItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-HeavyItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Italic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Italic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Italic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Italic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Light.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Light.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Light.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Light.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-LightItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-LightItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-LightItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-LightItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Medium.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Medium.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Medium.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Medium.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-MediumItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-MediumItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-MediumItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-MediumItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Regular.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Regular.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Regular.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Regular.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-SemiBold.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-SemiBold.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-SemiBold.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-SemiBold.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-SemiBoldItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-SemiBoldItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-SemiBoldItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-SemiBoldItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Thin.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Thin.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Thin.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Thin.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-ThinItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-ThinItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-ThinItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-ThinItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Two.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Two.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Two.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Two.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-TwoItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-TwoItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-TwoItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-TwoItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Ultra.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Ultra.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-Ultra.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-Ultra.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraLight.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraLight.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraLight.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraLight.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraLightItalic.woff b/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraLightItalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraLightItalic.woff2 b/mirzaev/ebala/system/public/fonts/fira/FiraSans-UltraLightItalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-bold-subset.woff b/mirzaev/ebala/system/public/fonts/hack/hack-bold-subset.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-bold-subset.woff2 b/mirzaev/ebala/system/public/fonts/hack/hack-bold-subset.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-bold.woff b/mirzaev/ebala/system/public/fonts/hack/hack-bold.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-bold.woff2 b/mirzaev/ebala/system/public/fonts/hack/hack-bold.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-bolditalic-subset.woff b/mirzaev/ebala/system/public/fonts/hack/hack-bolditalic-subset.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-bolditalic-subset.woff2 b/mirzaev/ebala/system/public/fonts/hack/hack-bolditalic-subset.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-bolditalic.woff b/mirzaev/ebala/system/public/fonts/hack/hack-bolditalic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-bolditalic.woff2 b/mirzaev/ebala/system/public/fonts/hack/hack-bolditalic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-italic-subset.woff b/mirzaev/ebala/system/public/fonts/hack/hack-italic-subset.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-italic-subset.woff2 b/mirzaev/ebala/system/public/fonts/hack/hack-italic-subset.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-italic.woff b/mirzaev/ebala/system/public/fonts/hack/hack-italic.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-italic.woff2 b/mirzaev/ebala/system/public/fonts/hack/hack-italic.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-regular-subset.woff b/mirzaev/ebala/system/public/fonts/hack/hack-regular-subset.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-regular-subset.woff2 b/mirzaev/ebala/system/public/fonts/hack/hack-regular-subset.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-regular.woff b/mirzaev/ebala/system/public/fonts/hack/hack-regular.woff old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/fonts/hack/hack-regular.woff2 b/mirzaev/ebala/system/public/fonts/hack/hack-regular.woff2 old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/index.php b/mirzaev/ebala/system/public/index.php index 6a11e40..63979e7 100755 --- a/mirzaev/ebala/system/public/index.php +++ b/mirzaev/ebala/system/public/index.php @@ -85,6 +85,7 @@ $router->write('/tasks/read', 'task', 'read', 'POST'); $router->write('/works/list', 'work', 'datalist', 'POST'); $router->write('/tasks/works', 'task', 'works', 'POST'); $router->write('/task/$task/read', 'task', 'task', 'POST'); +$router->write('/task/$task/row', 'task', 'row', 'POST'); $router->write('/task/$task/value', 'task', 'value', 'POST'); $router->write('/task/$task/confirm', 'task', 'confirm', 'POST'); $router->write('/task/$task/problem', 'task', 'problem', 'POST'); @@ -108,10 +109,10 @@ $router->write('/payments/confirm/$type', 'payments', 'confirm', 'POST'); $router->write('/settings', 'settings', 'index', 'GET'); $router->write('/settings', 'settings', 'index', 'POST'); $router->write('/settings/$id/write', 'settings', 'write', 'POST'); +$router->write('/socket/registration', 'socket', 'registration', 'POST'); // @todo PUT // Инициализация ядра $core = new core(namespace: __NAMESPACE__, router: $router, controller: new controller(false), model: new model(false)); - // Обработка запроса echo $core->start(); diff --git a/mirzaev/ebala/system/public/js/account.js b/mirzaev/ebala/system/public/js/account.js new file mode 100755 index 0000000..97bc0af --- /dev/null +++ b/mirzaev/ebala/system/public/js/account.js @@ -0,0 +1,74 @@ +"use strict"; + +if (typeof window.connection !== "function") { + // Not initialized + + // Initialize of the class in global namespace + window.connection = class connection { + // Wrap of indicator of the connection + static wrap = document.getElementById("connection"); + + // Indicator of the connection + static indicator = this.wrap.getElementsByTagName("i")[0]; + + // Description of the connection + static description = this.wrap.getElementsByTagName("small")[0]; + + // Statuc of the connection + static connected = false; + + // Duration of the disconnected status + static timeout = 0; + + // Instance of the time counter in disconnected status + static counter; + + /** + * Initialize status of the connection to socket + * + * @param {bool} connected Connected? + * + * @return {void} + */ + static status(connected = false) { + if (this.indicator instanceof HTMLElement) { + // Инициализирован индикатор + + if (this.connected = connected) { + // Connected + + this.wrap.setAttribute("title", "Вы подключены к серверу"); + + this.indicator.classList.remove("disconnected"); + this.indicator.classList.add("connected"); + + clearInterval(this.counter); + this.description.innerText = ""; + this.counter = undefined; + this.timeout = 0; + } else { + // Disconnected + + this.wrap.setAttribute("title", "Вы отключены от сервера"); + + this.indicator.classList.remove("connected"); + this.indicator.classList.add("disconnected"); + + if (typeof this.counter === "undefined") { + this.counter = setInterval(() => { + this.timeout += 0.01; + this.description.innerText = this.timeout.toFixed(2); + }, 10); + } + } + } + } + }; +} + +// Вызов события: "инициализировано" +document.dispatchEvent( + new CustomEvent("connection.initialized", { + detail: { connection: window.connection }, + }), +); diff --git a/mirzaev/ebala/system/public/js/administrators.js b/mirzaev/ebala/system/public/js/administrators.js old mode 100644 new mode 100755 index 2c8f467..0578483 --- a/mirzaev/ebala/system/public/js/administrators.js +++ b/mirzaev/ebala/system/public/js/administrators.js @@ -275,17 +275,17 @@ if (typeof window.administrators !== "function") { navigator.clipboard.writeText(data.clipboard); } - if (this.body.wrap instanceof HTMLElement) { + if (core.popup_body.wrap instanceof HTMLElement) { // Найдено активное окно // Деинициализация быстрых действий по кнопкам document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Деинициализация активного окна - this.body.wrap.remove(); + core.popup_body.wrap.remove(); // Удаление статуса активной строки row.removeAttribute("data-selected"); @@ -333,8 +333,8 @@ if (typeof window.administrators !== "function") { row.setAttribute("data-selected", "true"); // Инициализация оболочки всплывающего окна - this.body.wrap = document.createElement("div"); - this.body.wrap.setAttribute("id", "popup"); + core.popup_body.wrap = document.createElement("div"); + core.popup_body.wrap.setAttribute("id", "popup"); // Инициализация оболочки всплывающего окна const popup = document.createElement("section"); @@ -597,15 +597,15 @@ if (typeof window.administrators !== "function") { ); // Инициализация окна с ошибками - this.body.errors = document.createElement("section"); - this.body.errors.classList.add( + core.popup_body.errors = document.createElement("section"); + core.popup_body.errors.classList.add( "errors", "window", "list", "small", "hidden", ); - this.body.errors.setAttribute("data-errors", true); + core.popup_body.errors.setAttribute("data-errors", true); // Инициализация элемента-оболочки с ошибками для всплывающего окна const errors = document.createElement("section"); @@ -624,7 +624,7 @@ if (typeof window.administrators !== "function") { document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Удаление активного окна old.remove(); @@ -673,13 +673,13 @@ if (typeof window.administrators !== "function") { main.appendChild(account); popup.appendChild(main); - this.body.wrap.appendChild(popup); + core.popup_body.wrap.appendChild(popup); errors.appendChild(dl); - this.body.errors.appendChild(errors); - this.body.wrap.appendChild(this.body.errors); + core.popup_body.errors.appendChild(errors); + core.popup_body.wrap.appendChild(core.popup_body.errors); - document.body.appendChild(this.body.wrap); + document.body.appendChild(core.popup_body.wrap); // Инициалиация масок account_number_input.mask = IMask(account_number_input, { @@ -701,9 +701,9 @@ if (typeof window.administrators !== "function") { 100, ); } - top(this.body.errors); - const resize = new ResizeObserver(() => top(this.body.errors)); - resize.observe(this.body.wrap); + top(core.popup_body.errors); + const resize = new ResizeObserver(() => top(core.popup_body.errors)); + resize.observe(core.popup_body.wrap); // Инициализация функции генерации пароля и записи в поле ввода и буфер обмена const generate = (e) => { @@ -777,10 +777,10 @@ if (typeof window.administrators !== "function") { // Инициализация функции закрытия всплывающего окна const click = () => { // Блокировка - if (this.freeze) return; + if (core.popup_freeze) return; // Удаление всплывающего окна - this.body.wrap.remove(); + core.popup_body.wrap.remove(); // Инициализация буфера выбранных строк const buffer = [ @@ -796,16 +796,16 @@ if (typeof window.administrators !== "function") { document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; }; // Инициализация функции добавления функции закрытия всплывающего окна const enable = () => - this.body.wrap.addEventListener("click", click); + core.popup_body.wrap.addEventListener("click", click); // Инициализация функции удаления функции закрытия всплывающего окна const disable = () => - this.body.wrap.removeEventListener("click", click); + core.popup_body.wrap.removeEventListener("click", click); // Первичная активация функции удаления всплывающего окна enable(); @@ -817,10 +817,10 @@ if (typeof window.administrators !== "function") { // Инициализация функции блокировки удаления окна по событию select.addEventListener("click", () => { // Блокировка удаления окна - this.freeze = true; + core.popup_freeze = true; // Разблокировка удаления окна - setTimeout(() => this.freeze = false, 100); + setTimeout(() => core.popup_freeze = false, 100); }); for (const option of select.getElementsByTagName("option")) { @@ -829,10 +829,10 @@ if (typeof window.administrators !== "function") { // Инициализация функции блокировки удаления окна по событию option.addEventListener("click", () => { // Блокировка удаления окна - this.freeze = true; + core.popup_freeze = true; // Разблокировка удаления окна - setTimeout(() => this.freeze = false, 100); + setTimeout(() => core.popup_freeze = false, 100); }); } } @@ -842,7 +842,7 @@ if (typeof window.administrators !== "function") { // Перебор всех textarea-элементов // Обновлять позицию окна с ошибками при изменении размера textarea (там position: absolute) - new MutationObserver(() => top(this.body.errors)).observe( + new MutationObserver(() => top(core.popup_body.errors)).observe( textarea, { attributes: true, @@ -866,14 +866,14 @@ if (typeof window.administrators !== "function") { }); // Добавление функции блокировки удаления окна и клавиш по событиям - textarea.addEventListener("focus", () => this.freeze = true); - textarea.addEventListener("focusout", () => this.freeze = false); + textarea.addEventListener("focus", () => core.popup_freeze = true); + textarea.addEventListener("focusout", () => core.popup_freeze = false); } // Инициализация функции управления кнопками this.buttons = (e, force = false) => { // Блокировка - if (!force && this.freeze) return; + if (!force && core.popup_freeze) return; if (e.keyCode === 27) { // Нажата кнопка: "escape" @@ -884,16 +884,16 @@ if (typeof window.administrators !== "function") { // Нажата кнопка: "enter" // Инициализация буфера с текущим статусом блокировки закрытия окна - const freeze = this.freeze; + const freeze = core.popup_freeze; // Блокировка закрытия окна (чтобы не вызвался click() через событие onclick) - this.freeze = true; + core.popup_freeze = true; // Активация виртуальной кнопки "сохранить" account_update_button.click(); // Возвращение статуса блокировки закрытия окна - this.freeze = freeze; + core.popup_freeze = freeze; } }; @@ -1034,17 +1034,17 @@ if (typeof window.administrators !== "function") { navigator.clipboard.writeText(data.clipboard); } - if (this.body.wrap instanceof HTMLElement) { + if (core.popup_body.wrap instanceof HTMLElement) { // Найдено активное окно // Деинициализация быстрых действий по кнопкам document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Деинициализация активного окна - this.body.wrap.remove(); + core.popup_body.wrap.remove(); } // Реинициализация строк @@ -1064,8 +1064,8 @@ if (typeof window.administrators !== "function") { */ static create = damper(() => { // Инициализация оболочки всплывающего окна - this.body.wrap = document.createElement("div"); - this.body.wrap.setAttribute("id", "popup"); + core.popup_body.wrap = document.createElement("div"); + core.popup_body.wrap.setAttribute("id", "popup"); // Инициализация оболочки всплывающего окна const popup = document.createElement("section"); @@ -1281,15 +1281,15 @@ if (typeof window.administrators !== "function") { ); // Инициализация окна с ошибками - this.body.errors = document.createElement("section"); - this.body.errors.classList.add( + core.popup_body.errors = document.createElement("section"); + core.popup_body.errors.classList.add( "errors", "window", "list", "small", "hidden", ); - this.body.errors.setAttribute("data-errors", true); + core.popup_body.errors.setAttribute("data-errors", true); // Инициализация элемента-оболочки с ошибками для всплывающего окна const errors = document.createElement("section"); @@ -1308,7 +1308,7 @@ if (typeof window.administrators !== "function") { document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Удаление активного окна old.remove(); @@ -1356,13 +1356,13 @@ if (typeof window.administrators !== "function") { main.appendChild(account); popup.appendChild(main); - this.body.wrap.appendChild(popup); + core.popup_body.wrap.appendChild(popup); errors.appendChild(dl); - this.body.errors.appendChild(errors); - this.body.wrap.appendChild(this.body.errors); + core.popup_body.errors.appendChild(errors); + core.popup_body.wrap.appendChild(core.popup_body.errors); - document.body.appendChild(this.body.wrap); + document.body.appendChild(core.popup_body.wrap); // Инициалиация масок account_number_input.mask = IMask(account_number_input, { @@ -1384,9 +1384,9 @@ if (typeof window.administrators !== "function") { 100, ); } - top(this.body.errors); - const resize = new ResizeObserver(() => top(this.body.errors)); - resize.observe(this.body.wrap); + top(core.popup_body.errors); + const resize = new ResizeObserver(() => top(core.popup_body.errors)); + resize.observe(core.popup_body.wrap); // Инициализация функции блокировки кнопки const block = () => { @@ -1474,10 +1474,10 @@ if (typeof window.administrators !== "function") { // Инициализация функции закрытия всплывающего окна const click = () => { // Блокировка - if (this.freeze) return; + if (core.popup_freeze) return; // Удаление всплывающего окна - this.body.wrap.remove(); + core.popup_body.wrap.remove(); // Удаление статуса активной строки row.removeAttribute("data-selected"); @@ -1486,14 +1486,14 @@ if (typeof window.administrators !== "function") { document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; }; // Инициализация функции добавления функции закрытия всплывающего окна - const enable = () => this.body.wrap.addEventListener("click", click); + const enable = () => core.popup_body.wrap.addEventListener("click", click); // Инициализация функции удаления функции закрытия всплывающего окна - const disable = () => this.body.wrap.removeEventListener("click", click); + const disable = () => core.popup_body.wrap.removeEventListener("click", click); // Первичная активация функции удаления всплывающего окна enable(); @@ -1505,10 +1505,10 @@ if (typeof window.administrators !== "function") { // Инициализация функции блокировки удаления окна по событию select.addEventListener("click", () => { // Блокировка удаления окна - this.freeze = true; + core.popup_freeze = true; // Разблокировка удаления окна - setTimeout(() => this.freeze = false, 100); + setTimeout(() => core.popup_freeze = false, 100); }); for (const option of select.getElementsByTagName("option")) { @@ -1517,10 +1517,10 @@ if (typeof window.administrators !== "function") { // Инициализация функции блокировки удаления окна по событию option.addEventListener("click", () => { // Блокировка удаления окна - this.freeze = true; + core.popup_freeze = true; // Разблокировка удаления окна - setTimeout(() => this.freeze = false, 100); + setTimeout(() => core.popup_freeze = false, 100); }); } } @@ -1530,7 +1530,7 @@ if (typeof window.administrators !== "function") { // Перебор всех textarea-элементов // Обновлять позицию окна с ошибками при изменении размера textarea (там position: absolute) - new MutationObserver(() => top(this.body.errors)).observe(textarea, { + new MutationObserver(() => top(core.popup_body.errors)).observe(textarea, { attributes: true, attributeFilter: ["style"], }); @@ -1551,14 +1551,14 @@ if (typeof window.administrators !== "function") { }); // Добавление функции блокировки удаления окна и клавиш по событиям - textarea.addEventListener("focus", () => this.freeze = true); - textarea.addEventListener("focusout", () => this.freeze = false); + textarea.addEventListener("focus", () => core.popup_freeze = true); + textarea.addEventListener("focusout", () => core.popup_freeze = false); } // Инициализация функции управления кнопками this.buttons = (e, force = false) => { // Блокировка - if (!force && this.freeze) return; + if (!force && core.popup_freeze) return; if (e.keyCode === 27) { // Нажата кнопка: "escape" @@ -1569,16 +1569,16 @@ if (typeof window.administrators !== "function") { // Нажата кнопка: "enter" // Инициализация буфера с текущим статусом блокировки закрытия окна - const freeze = this.freeze; + const freeze = core.popup_freeze; // Блокировка закрытия окна (чтобы не вызвался click() через событие onclick) - this.freeze = true; + core.popup_freeze = true; // Активация виртуальной кнопки "создать" account_create_button.click(); // Возвращение статуса блокировки закрытия окна - this.freeze = freeze; + core.popup_freeze = freeze; } }; @@ -1723,17 +1723,17 @@ if (typeof window.administrators !== "function") { row.remove(); } - if (this.body.wrap instanceof HTMLElement) { + if (core.popup_body.wrap instanceof HTMLElement) { // Найдено активное окно // Деинициализация быстрых действий по кнопкам document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Деинициализация активного окна - this.body.wrap.remove(); + core.popup_body.wrap.remove(); // Удаление статуса активной строки row.removeAttribute("data-selected"); @@ -1990,8 +1990,8 @@ if (typeof window.administrators !== "function") { */ static errors(registry, render = true, clean = true) { // Инициализация ссылки на HTML-элемент с ошибками - const wrap = document.body.contains(this.body.errors) - ? this.body.errors + const wrap = document.body.contains(core.popup_body.errors) + ? core.popup_body.errors : document.querySelector('[data-errors="true"]'); if (wrap instanceof HTMLElement && document.body.contains(wrap)) { diff --git a/mirzaev/ebala/system/public/js/buffer.js b/mirzaev/ebala/system/public/js/buffer.js old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/js/chat.js b/mirzaev/ebala/system/public/js/chat.js old mode 100644 new mode 100755 index 9fd1276..27ef64e --- a/mirzaev/ebala/system/public/js/chat.js +++ b/mirzaev/ebala/system/public/js/chat.js @@ -145,8 +145,8 @@ if (typeof window.chat !== "function") { row.setAttribute("data-selected", "true"); // Инициализация оболочки всплывающего окна - this.body.wrap = document.createElement("div"); - this.body.wrap.setAttribute("id", "popup"); + core.popup_body.wrap = document.createElement("div"); + core.popup_body.wrap.setAttribute("id", "popup"); // Инициализация оболочки всплывающего окна const popup = document.createElement("section"); @@ -251,7 +251,7 @@ if (typeof window.chat !== "function") { market.appendChild(market_message); // Обновлять позицию окна с ошибками при изменении размера textarea (там position: absolute) - new MutationObserver(() => top(this.body.errors)).observe( + new MutationObserver(() => top(core.popup_body.errors)).observe( market_textarea, { attributes: true, @@ -270,24 +270,24 @@ if (typeof window.chat !== "function") { // Нажаты кнопки: "control", "enter" // Инициализация буфера с текущим статусом блокировки закрытия окна - const freeze = this.freeze; + const freeze = core.popup_freeze; // Блокировка закрытия окна (чтобы не вызвался click() через событие onclick) - this.freeze = true; + core.popup_freeze = true; // Активация виртуальной кнопки "отправить сообщение" market_send.click(); // Возвращение статуса блокировки закрытия окна - this.freeze = freeze; + core.popup_freeze = freeze; } }); // Добавление функции блокировки удаления окна и клавиш по событиям - market_textarea.addEventListener("focus", () => this.freeze = true); + market_textarea.addEventListener("focus", () => core.popup_freeze = true); market_textarea.addEventListener( "focusout", - () => this.freeze = false, + () => core.popup_freeze = false, ); // Инициализация колонки чата: СОТРУДНИК <-> ОПЕРАТОР @@ -341,7 +341,7 @@ if (typeof window.chat !== "function") { worker.appendChild(worker_message); // Обновлять позицию окна с ошибками при изменении размера textarea (там position: absolute) - new MutationObserver(() => top(this.body.errors)).observe( + new MutationObserver(() => top(core.popup_body.errors)).observe( worker_textarea, { attributes: true, @@ -360,36 +360,36 @@ if (typeof window.chat !== "function") { // Нажаты кнопки: "control", "enter" // Инициализация буфера с текущим статусом блокировки закрытия окна - const freeze = this.freeze; + const freeze = core.popup_freeze; // Блокировка закрытия окна (чтобы не вызвался click() через событие onclick) - this.freeze = true; + core.popup_freeze = true; // Активация виртуальной кнопки "отправить сообщение" worker_send.click(); // Возвращение статуса блокировки закрытия окна - this.freeze = freeze; + core.popup_freeze = freeze; } }); // Добавление функции блокировки удаления окна и клавиш по событиям - worker_textarea.addEventListener("focus", () => this.freeze = true); + worker_textarea.addEventListener("focus", () => core.popup_freeze = true); worker_textarea.addEventListener( "focusout", - () => this.freeze = false, + () => core.popup_freeze = false, ); // Инициализация окна с ошибками - this.body.errors = document.createElement("section"); - this.body.errors.classList.add( + core.popup_body.errors = document.createElement("section"); + core.popup_body.errors.classList.add( "errors", "window", "list", "calculated", "hidden", ); - this.body.errors.setAttribute("data-errors", true); + core.popup_body.errors.setAttribute("data-errors", true); // Инициализация элемента-оболочки с ошибками для всплывающего окна const errors = document.createElement("section"); @@ -408,7 +408,7 @@ if (typeof window.chat !== "function") { document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Удаление активного окна old.remove(); @@ -449,13 +449,13 @@ if (typeof window.chat !== "function") { } popup.appendChild(main); - this.body.wrap.appendChild(popup); + core.popup_body.wrap.appendChild(popup); errors.appendChild(dl); - this.body.errors.appendChild(errors); - this.body.wrap.appendChild(this.body.errors); + core.popup_body.errors.appendChild(errors); + core.popup_body.wrap.appendChild(core.popup_body.errors); - document.body.appendChild(this.body.wrap); + document.body.appendChild(core.popup_body.wrap); if (typeof core === "function") { // Найдено ядро @@ -481,7 +481,7 @@ if (typeof window.chat !== "function") { } // Инициализация переменных для окна с ошибками (12 - это значение gap из div#popup) - this.body.errors.calculate = (errors) => { + core.popup_body.errors.calculate = (errors) => { errors.style.setProperty( "transition", "0s", @@ -496,24 +496,24 @@ if (typeof window.chat !== "function") { ); // Инициализация ширины окна с ошибками - this.body.errors.style.setProperty( + core.popup_body.errors.style.setProperty( "--calculated-width", popup.offsetWidth + "px", ); }; - this.body.errors.calculate(this.body.errors); + core.popup_body.errors.calculate(core.popup_body.errors); const resize = new ResizeObserver(() => - this.body.errors.calculate(this.body.errors) + core.popup_body.errors.calculate(core.popup_body.errors) ); - resize.observe(this.body.wrap); + resize.observe(core.popup_body.wrap); // Инициализация функции закрытия всплывающего окна const click = () => { // Блокировка - if (this.freeze) return; + if (core.popup_freeze) return; // Удаление всплывающего окна - this.body.wrap.remove(); + core.popup_body.wrap.remove(); // Инициализация буфера выбранных строк const buffer = [ @@ -534,15 +534,15 @@ if (typeof window.chat !== "function") { } // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; }; // Инициализация функции добавления функции закрытия всплывающего окна - const enable = () => this.body.wrap.addEventListener("click", click); + const enable = () => core.popup_body.wrap.addEventListener("click", click); // Инициализация функции удаления функции закрытия всплывающего окна const disable = () => - this.body.wrap.removeEventListener("click", click); + core.popup_body.wrap.removeEventListener("click", click); // Первичная активация функции удаления всплывающего окна enable(); @@ -550,7 +550,7 @@ if (typeof window.chat !== "function") { // Инициализация функции управления кнопками this.buttons = (e, force = false) => { // Блокировка - if (!force && this.freeze) return; + if (!force && core.popup_freeze) return; if (e.keyCode === 27) { // Нажата кнопка: "escape" @@ -669,7 +669,7 @@ if (typeof window.chat !== "function") { } // Перерасчёт положения окна ошибок - this.body.errors.calculate(this.body.errors); + core.popup_body.errors.calculate(core.popup_body.errors); // Прокрутка до актуального значения прокрутки wrap.scrollTop = scrollTop; @@ -793,7 +793,7 @@ if (typeof window.chat !== "function") { document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Реинициализация активного окна (чат) this.popup(row); @@ -876,8 +876,8 @@ if (typeof window.chat !== "function") { */ static errors(registry, render = true, clean = true) { // Инициализация ссылки на HTML-элемент с ошибками - const wrap = document.body.contains(this.body.errors) - ? this.body.errors + const wrap = document.body.contains(core.popup_body.errors) + ? core.popup_body.errors : document.querySelector('[data-errors="true"]'); if (wrap instanceof HTMLElement && document.body.contains(wrap)) { diff --git a/mirzaev/ebala/system/public/js/connection.js b/mirzaev/ebala/system/public/js/connection.js new file mode 100755 index 0000000..97bc0af --- /dev/null +++ b/mirzaev/ebala/system/public/js/connection.js @@ -0,0 +1,74 @@ +"use strict"; + +if (typeof window.connection !== "function") { + // Not initialized + + // Initialize of the class in global namespace + window.connection = class connection { + // Wrap of indicator of the connection + static wrap = document.getElementById("connection"); + + // Indicator of the connection + static indicator = this.wrap.getElementsByTagName("i")[0]; + + // Description of the connection + static description = this.wrap.getElementsByTagName("small")[0]; + + // Statuc of the connection + static connected = false; + + // Duration of the disconnected status + static timeout = 0; + + // Instance of the time counter in disconnected status + static counter; + + /** + * Initialize status of the connection to socket + * + * @param {bool} connected Connected? + * + * @return {void} + */ + static status(connected = false) { + if (this.indicator instanceof HTMLElement) { + // Инициализирован индикатор + + if (this.connected = connected) { + // Connected + + this.wrap.setAttribute("title", "Вы подключены к серверу"); + + this.indicator.classList.remove("disconnected"); + this.indicator.classList.add("connected"); + + clearInterval(this.counter); + this.description.innerText = ""; + this.counter = undefined; + this.timeout = 0; + } else { + // Disconnected + + this.wrap.setAttribute("title", "Вы отключены от сервера"); + + this.indicator.classList.remove("connected"); + this.indicator.classList.add("disconnected"); + + if (typeof this.counter === "undefined") { + this.counter = setInterval(() => { + this.timeout += 0.01; + this.description.innerText = this.timeout.toFixed(2); + }, 10); + } + } + } + } + }; +} + +// Вызов события: "инициализировано" +document.dispatchEvent( + new CustomEvent("connection.initialized", { + detail: { connection: window.connection }, + }), +); diff --git a/mirzaev/ebala/system/public/js/core.js b/mirzaev/ebala/system/public/js/core.js old mode 100644 new mode 100755 index e208cdf..f895530 --- a/mirzaev/ebala/system/public/js/core.js +++ b/mirzaev/ebala/system/public/js/core.js @@ -5,9 +5,30 @@ if (typeof window.core !== "function") { // Initialize of the class in global namespace window.core = class core { + // Zone + static zone = window.location.host.split(".")[2]; + + // Domain + static domain = window.location.host.split(".")[1]; + // Subdomain static subdomain = window.location.host.split(".")[0]; + // Socket address (xn--e1ajlli это сокет) + static socket = "wss://xn--e1ajlli.xn--24-mlca2chbdebu6a.xn--p1ai:9502"; + + // Instance of connection to the socket + static connection; + + // Iterval for reconnect + static interval; + + // Attempts to connect (when core.connection.readyState === 0) + static attempts = 0; + + // Interval for block + static block; + // Page static page = window.location.pathname === "/" ? "tasks" @@ -24,10 +45,308 @@ if (typeof window.core !== "function") { ? "administrator" : "worker"))); + /** + * Заблокировать функцию закрытия всплывающего окна? + */ + static popup_freeze = false; + + /** + * Тело всплывающего окна (массив) + */ + static popup_body = {}; + + /** + * Фильтр: "только цифры, точки и запятые" + * + * @param {object} e Event (onkeypress) + * + * @return {string} + */ static numbers(e) { const c = (e.which) ? e.which : e.keyCode; return !(c !== 46 && c !== 44 && c > 31 && (c < 48 || c > 57)); } + + /** + * Сгенерировать окно выбора действия + * + * @param {string} title Верхний колонтинул + * @param {string} text Основное содержимое окна + * @param {string} left Содержимое левой кнопки + * @param {object} left_css Перечисление CSS-классов левой кнопки (массив) + * @param {function} left_click Действие после нажатия на левую кнопку + * @param {string} right Содержимое правой кнопки + * @param {object} right_css Перечисление CSS-классов правой кнопки (массив) + * @param {function} right_click Действие после нажатия на правую кнопку + * + * @return {void} + */ + static choose = damper( + ( + title = "Выбор действия", + text = "", + left = "Да", + left_css = ["grass"], + left_click = () => {}, + right = "Нет", + right_css = ["clay"], + right_click = () => {}, + ) => { + // Инициализация оболочки всплывающего окна + this.popup_body.wrap = document.createElement("div"); + this.popup_body.wrap.setAttribute("id", "popup"); + + // Инициализация всплывающего окна + const popup = document.createElement("section"); + popup.classList.add("list", "small"); + + // Инициализация заголовка всплывающего окна + const title_h3 = document.createElement("h3"); + title_h3.classList.add("unselectable"); + title_h3.innerText = title; + + // Инициализация оболочки с основной информацией + const main = document.createElement("section"); + main.classList.add("main"); + + // Инициализация колонки + const column = document.createElement("div"); + column.classList.add("column"); + + // Инициализация текста + const text_p = document.createElement("p"); + text_p.innerText = text; + + // Инициализация строки + const row = document.createElement("div"); + row.classList.add("row", "buttons"); + + // Инициализация левой кнопки + const left_button = document.createElement("button"); + left_button.classList.add(...left_css); + left_button.innerText = left; + left_button.addEventListener("click", left_click); + + // Инициализация правой кнопки + const right_button = document.createElement("button"); + right_button.classList.add(...right_css); + right_button.innerText = right; + right_button.addEventListener("click", right_click); + + // Инициализация окна с ошибками + this.popup_body.errors = document.createElement("section"); + this.popup_body.errors.classList.add( + "errors", + "window", + "list", + "calculated", + "hidden", + ); + this.popup_body.errors.setAttribute("data-errors", true); + + // Инициализация элемента-тела (оболочки) окна с ошибками + const errors = document.createElement("section"); + errors.classList.add("body"); + + // Инициализация элемента-списка ошибок + const dl = document.createElement("dl"); + + // Инициализация активного всплывающего окна + const old = document.getElementById("popup"); + + if (old instanceof HTMLElement) { + // Найдено активное окно + + // Деинициализация быстрых действий по кнопкам + document.removeEventListener("keydown", this.buttons); + + // Сброс блокировки + this.freeze = false; + + // Удаление активного окна + old.remove(); + } + + // Запись в документ + popup.appendChild(title_h3); + + column.appendChild(text_p); + + row.appendChild(left_button); + row.appendChild(right_button); + column.appendChild(row); + + main.appendChild(column); + popup.appendChild(main); + + this.popup_body.wrap.appendChild(popup); + document.body.appendChild(this.popup_body.wrap); + + errors.appendChild(dl); + this.popup_body.errors.appendChild(errors); + this.popup_body.wrap.appendChild(this.popup_body.errors); + + // Инициализация ширины окна с ошибками + this.popup_body.errors.style.setProperty( + "--calculated-width", + popup.offsetWidth + "px", + ); + + // Инициализация переменных для окна с ошибками (12 - это значение gap из div#popup) + function top(errors) { + errors.style.setProperty("transition", "0s"); + errors.style.setProperty( + "--top", + popup.offsetTop + popup.offsetHeight + 12 + "px", + ); + setTimeout(() => errors.style.removeProperty("transition"), 100); + } + top(this.popup_body.errors); + const resize = new ResizeObserver(() => top(this.popup_body.errors)); + resize.observe(this.popup_body.wrap); + + // Инициализация функции закрытия всплывающего окна + const click = () => { + // Блокировка + if (this.freeze) return; + + // Удаление всплывающего окна + this.popup_body.wrap.remove(); + + // Удаление статуса активной строки + row.removeAttribute("data-selected"); + + // Деинициализация быстрых действий по кнопкам + document.removeEventListener("keydown", this.buttons); + + // Сброс блокировки + this.freeze = false; + }; + + // Инициализация функции добавления функции закрытия всплывающего окна + const enable = () => + this.popup_body.wrap.addEventListener("click", click); + + // Инициализация функции удаления функции закрытия всплывающего окна + const disable = () => + this.popup_body.wrap.removeEventListener("click", click); + + // Первичная активация функции удаления всплывающего окна + enable(); + + // Добавление функции удаления всплывающего окна по событиям + popup.addEventListener("mouseenter", disable); + popup.addEventListener("mouseleave", enable); + + // Добавление функции удаления всплывающего окна по кнопкам + left_button.addEventListener("click", click); + right_button.addEventListener("click", click); + + // Фокусировка + right_button.focus(); + }, + 300, + ); + + /** + * Connect to the socket + * + * @param {bool|number} interval Connection check interval (ms) + * @param {function} preprocessing Will be executed every cycle + * @param {function} onmessage New message + * @param {function} onopen Connection opened + * @param {function} onclose Connection closed + * @param {function} onerror An error has occurred + * + * @return {Promise} + */ + static connect( + interval = false, + preprocessing, + onmessage, + onopen, + onclose, + onerror, + ) { + return new Promise((resolve, reject) => { + try { + if (typeof interval === "number" && interval > 0) { + // Connect with automatic reconnect + + if (typeof this.interval === "undefined") { + this.interval = setInterval(() => { + preprocessing(); + + if ( + !(this.connection instanceof WebSocket) || + (this.connection.readyState === 3 || + this.connection.readyState === 4) || + (this.connection.readyState === 0 && ++this.attempts > 10) + ) { + this.attempts = 0; + + if (this.connection instanceof WebSocket) { + this.connection.close(); + } + + this.connection = new WebSocket(this.socket); + this.connection.addEventListener("message", (e) => { + try { + const json = JSON.parse(e.data); + + if (json.type === "registration") { + // Подключение сокета к сессии + + fetch("/socket/registration", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: `key=${json.key}`, + }); + } + } catch (_e) {} + }); + this.connection.addEventListener("message", onmessage); + this.connection.addEventListener("open", onopen); + this.connection.addEventListener("close", onclose); + this.connection.addEventListener("error", onerror); + + resolve(this.connection); + } else resolve(this.connection); + }, interval); + } + } else { + // Connect without reconnecting + + if ( + !(this.connection instanceof WebSocket) || + (this.connection.readyState === 3 || + this.connection.readyState === 4) + ) { + if (this.connection instanceof WebSocket) this.connection.close(); + + this.connection = new WebSocket(this.socket); + this.connection.addEventListener("message", onmessage); + this.connection.addEventListener("open", onopen); + this.connection.addEventListener("close", onclose); + this.connection.addEventListener("error", onerror); + + resolve(this.connection); + } else resolve(this.connection); + } + } catch (_e) {} + }); + } + + /** + * Core is connected to the socket? + * + * @return {bool} + */ + static connected() { + return this.connection instanceof WebSocket && this.connection.readyState === 1; + } }; } diff --git a/mirzaev/ebala/system/public/js/imask-7.1.0-alpha.js b/mirzaev/ebala/system/public/js/imask-7.1.0-alpha.js old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/public/js/markets.js b/mirzaev/ebala/system/public/js/markets.js old mode 100644 new mode 100755 index 048318f..3a95044 --- a/mirzaev/ebala/system/public/js/markets.js +++ b/mirzaev/ebala/system/public/js/markets.js @@ -5,16 +5,6 @@ if (typeof window.markets !== "function") { // Initialize of the class in global namespace window.markets = class markets { - /** - * Заблокировать функцию закрытия всплывающего окна? - */ - static freeze = false; - - /** - * Тело всплывающего окна (массив) - */ - static body = {}; - /** * Инициализирован класс? */ @@ -303,17 +293,17 @@ if (typeof window.markets !== "function") { navigator.clipboard.writeText(data.clipboard); } - if (this.body.wrap instanceof HTMLElement) { + if (core.popup_body.wrap instanceof HTMLElement) { // Найдено активное окно // Деинициализация быстрых действий по кнопкам document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Деинициализация активного окна - this.body.wrap.remove(); + core.popup_body.wrap.remove(); } // Реинициализация строк @@ -333,8 +323,8 @@ if (typeof window.markets !== "function") { */ static create = damper(() => { // Инициализация оболочки всплывающего окна - this.body.wrap = document.createElement("div"); - this.body.wrap.setAttribute("id", "popup"); + core.popup_body.wrap = document.createElement("div"); + core.popup_body.wrap.setAttribute("id", "popup"); // Инициализация всплывающего окна const popup = document.createElement("section"); @@ -606,26 +596,26 @@ if (typeof window.markets !== "function") { const market_city_option_2 = document.createElement("option"); market_city_option_2.classList.add("value", "Железногорск"); market_city_option_2.innerText = "Железногорск"; - /* - // Инициализация варианта для выбора - const market_city_option_3 = document.createElement("option"); - market_city_option_3.classList.add("value", ""); - market_city_option_3.innerText = ""; - // Инициализация варианта для выбора - const market_city_option_4 = document.createElement("option"); - market_city_option_4.classList.add("value", ""); - market_city_option_4.innerText = ""; + // Инициализация варианта для выбора + const market_city_option_3 = document.createElement("option"); + market_city_option_3.classList.add("value", "Сосновоборск"); + market_city_option_3.innerText = "Сосновоборск"; - // Инициализация варианта для выбора - const market_city_option_5 = document.createElement("option"); - market_city_option_5.classList.add("value", ""); - market_city_option_5.innerText = ""; + // Инициализация варианта для выбора + const market_city_option_4 = document.createElement("option"); + market_city_option_4.classList.add("value", "Иркутск"); + market_city_option_4.innerText = "Иркутск"; - // Инициализация варианта для выбора - const market_city_option_ = document.createElement("option"); - market_city_option_.classList.add("value", ""); - market_city_option_.innerText = ""; */ + // Инициализация варианта для выбора + const market_city_option_5 = document.createElement("option"); + market_city_option_5.classList.add("value", "Тыва"); + market_city_option_5.innerText = "Тыва"; + + // Инициализация варианта для выбора + const market_city_option_6 = document.createElement("option"); + market_city_option_6.classList.add("value", "Хакасия"); + market_city_option_6.innerText = "Хакасия"; // Инициализация строки const market_district = document.createElement("div"); @@ -923,15 +913,15 @@ if (typeof window.markets !== "function") { ); // Инициализация окна с ошибками - this.body.errors = document.createElement("section"); - this.body.errors.classList.add( + core.popup_body.errors = document.createElement("section"); + core.popup_body.errors.classList.add( "errors", "window", "list", "calculated", "hidden" ); - this.body.errors.setAttribute("data-errors", true); + core.popup_body.errors.setAttribute("data-errors", true); // Инициализация элемента-тела (оболочки) окна с ошибками const errors = document.createElement("section"); @@ -950,7 +940,7 @@ if (typeof window.markets !== "function") { document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Удаление активного окна old.remove(); @@ -1000,6 +990,10 @@ if (typeof window.markets !== "function") { market_city_label.appendChild(market_city_title); market_city_select.appendChild(market_city_option_1); market_city_select.appendChild(market_city_option_2); + market_city_select.appendChild(market_city_option_3); + market_city_select.appendChild(market_city_option_4); + market_city_select.appendChild(market_city_option_5); + market_city_select.appendChild(market_city_option_6); market_city_label.appendChild(market_city_select); market_city.appendChild(market_city_label); market.appendChild(market_city); @@ -1057,16 +1051,16 @@ if (typeof window.markets !== "function") { main.appendChild(account); popup.appendChild(main); - this.body.wrap.appendChild(popup); + core.popup_body.wrap.appendChild(popup); errors.appendChild(dl); - this.body.errors.appendChild(errors); - this.body.wrap.appendChild(this.body.errors); + core.popup_body.errors.appendChild(errors); + core.popup_body.wrap.appendChild(core.popup_body.errors); - document.body.appendChild(this.body.wrap); + document.body.appendChild(core.popup_body.wrap); // Инициализация ширины окна с ошибками - this.body.errors.style.setProperty( + core.popup_body.errors.style.setProperty( "--calculated-width", popup.offsetWidth + "px" ); @@ -1088,9 +1082,9 @@ if (typeof window.markets !== "function") { ); setTimeout(() => errors.style.removeProperty("transition"), 100); } - top(this.body.errors); - const resize = new ResizeObserver(() => top(this.body.errors)); - resize.observe(this.body.wrap); + top(core.popup_body.errors); + const resize = new ResizeObserver(() => top(core.popup_body.errors)); + resize.observe(core.popup_body.wrap); // Инициализация функции блокировки кнопки const block = () => { @@ -1177,10 +1171,10 @@ if (typeof window.markets !== "function") { // Инициализация функции закрытия всплывающего окна const click = () => { // Блокировка - if (this.freeze) return; + if (core.popup_freeze) return; // Удаление всплывающего окна - this.body.wrap.remove(); + core.popup_body.wrap.remove(); // Удаление статуса активной строки // row.removeAttribute("data-selected"); // Это окно создания строки, её ещё не существует @@ -1189,14 +1183,16 @@ if (typeof window.markets !== "function") { document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; }; // Инициализация функции добавления функции закрытия всплывающего окна - const enable = () => this.body.wrap.addEventListener("click", click); + const enable = () => + core.popup_body.wrap.addEventListener("click", click); // Инициализация функции удаления функции закрытия всплывающего окна - const disable = () => this.body.wrap.removeEventListener("click", click); + const disable = () => + core.popup_body.wrap.removeEventListener("click", click); // Первичная активация функции удаления всплывающего окна enable(); @@ -1208,10 +1204,10 @@ if (typeof window.markets !== "function") { // Инициализация функции блокировки удаления окна по событию select.addEventListener("click", () => { // Блокировка удаления окна - this.freeze = true; + core.popup_freeze = true; // Разблокировка удаления окна - setTimeout(() => (this.freeze = false), 100); + setTimeout(() => (core.popup_freeze = false), 100); }); for (const option of select.getElementsByTagName("option")) { @@ -1220,10 +1216,10 @@ if (typeof window.markets !== "function") { // Инициализация функции блокировки удаления окна по событию option.addEventListener("click", () => { // Блокировка удаления окна - this.freeze = true; + core.popup_freeze = true; // Разблокировка удаления окна - setTimeout(() => (this.freeze = false), 100); + setTimeout(() => (core.popup_freeze = false), 100); }); } } @@ -1233,10 +1229,13 @@ if (typeof window.markets !== "function") { // Перебор всех textarea-элементов // Обновлять позицию окна с ошибками при изменении размера textarea (там position: absolute) - new MutationObserver(() => top(this.body.errors)).observe(textarea, { - attributes: true, - attributeFilter: ["style"], - }); + new MutationObserver(() => top(core.popup_body.errors)).observe( + textarea, + { + attributes: true, + attributeFilter: ["style"], + } + ); // Инициализация функции игнорирования блокировки для выбранных кнопок textarea.addEventListener("keydown", (e) => { @@ -1254,14 +1253,17 @@ if (typeof window.markets !== "function") { }); // Добавление функции блокировки удаления окна и клавиш по событиям - textarea.addEventListener("focus", () => (this.freeze = true)); - textarea.addEventListener("focusout", () => (this.freeze = false)); + textarea.addEventListener("focus", () => (core.popup_freeze = true)); + textarea.addEventListener( + "focusout", + () => (core.popup_freeze = false) + ); } // Инициализация функции управления кнопками this.buttons = (e, force = false) => { // Блокировка - if (!force && this.freeze) return; + if (!force && core.popup_freeze) return; if (e.keyCode === 27) { // Нажата кнопка: "escape" @@ -1272,16 +1274,16 @@ if (typeof window.markets !== "function") { // Нажата кнопка: "enter" // Инициализация буфера с текущим статусом блокировки закрытия окна - const freeze = this.freeze; + const freeze = core.popup_freeze; // Блокировка закрытия окна (чтобы не вызвался click() через событие onclick) - this.freeze = true; + core.popup_freeze = true; // Активация виртуальной кнопки "создать" account_create_button.click(); // Возвращение статуса блокировки закрытия окна - this.freeze = freeze; + core.popup_freeze = freeze; } }; @@ -1700,17 +1702,17 @@ if (typeof window.markets !== "function") { } } - if (this.body.wrap instanceof HTMLElement) { + if (core.popup_body.wrap instanceof HTMLElement) { // Найдено активное окно // Деинициализация быстрых действий по кнопкам document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Деинициализация активного окна - this.body.wrap.remove(); + core.popup_body.wrap.remove(); // Удаление статуса активной строки row.removeAttribute("data-selected"); @@ -1758,8 +1760,8 @@ if (typeof window.markets !== "function") { row.setAttribute("data-selected", "true"); // Инициализация оболочки всплывающего окна - this.body.wrap = document.createElement("div"); - this.body.wrap.setAttribute("id", "popup"); + core.popup_body.wrap = document.createElement("div"); + core.popup_body.wrap.setAttribute("id", "popup"); // Инициализация всплывающего окна const popup = document.createElement("section"); @@ -2015,25 +2017,25 @@ if (typeof window.markets !== "function") { market_city_option_2.classList.add("value", "Железногорск"); market_city_option_2.innerText = "Железногорск"; - /* // Инициализация варианта для выбора - const market_city_option_3 = document.createElement("option"); - market_city_option_3.classList.add("value", ""); - market_city_option_3.innerText = ""; + // Инициализация варианта для выбора + const market_city_option_3 = document.createElement("option"); + market_city_option_3.classList.add("value", "Сосновоборск"); + market_city_option_3.innerText = "Сосновоборск"; - // Инициализация варианта для выбора - const market_city_option_4 = document.createElement("option"); - market_city_option_4.classList.add("value", ""); - market_city_option_4.innerText = ""; + // Инициализация варианта для выбора + const market_city_option_4 = document.createElement("option"); + market_city_option_4.classList.add("value", "Иркутск"); + market_city_option_4.innerText = "Иркутск"; - // Инициализация варианта для выбора - const market_city_option_5 = document.createElement("option"); - market_city_option_5.classList.add("value", ""); - market_city_option_5.innerText = ""; + // Инициализация варианта для выбора + const market_city_option_5 = document.createElement("option"); + market_city_option_5.classList.add("value", "Тыва"); + market_city_option_5.innerText = "Тыва"; - // Инициализация варианта для выбора - const market_city_option_ = document.createElement("option"); - market_city_option_.classList.add("value", ""); - market_city_option_.innerText = ""; */ + // Инициализация варианта для выбора + const market_city_option_6 = document.createElement("option"); + market_city_option_6.classList.add("value", "Хакасия"); + market_city_option_6.innerText = "Хакасия"; // Инициализация строки const market_district = document.createElement("div"); @@ -2104,15 +2106,15 @@ if (typeof window.markets !== "function") { ); // Инициализация окна с ошибками - this.body.errors = document.createElement("section"); - this.body.errors.classList.add( + core.popup_body.errors = document.createElement("section"); + core.popup_body.errors.classList.add( "errors", "window", "list", "calculated", "hidden" ); - this.body.errors.setAttribute("data-errors", true); + core.popup_body.errors.setAttribute("data-errors", true); // Инициализация элемента-тела (оболочки) окна с ошибками const errors = document.createElement("section"); @@ -2131,7 +2133,7 @@ if (typeof window.markets !== "function") { document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Удаление активного окна old.remove(); @@ -2176,6 +2178,10 @@ if (typeof window.markets !== "function") { market_city_label.appendChild(market_city_title); market_city_select.appendChild(market_city_option_1); market_city_select.appendChild(market_city_option_2); + market_city_select.appendChild(market_city_option_3); + market_city_select.appendChild(market_city_option_4); + market_city_select.appendChild(market_city_option_5); + market_city_select.appendChild(market_city_option_6); market_city_label.appendChild(market_city_select); market_city.appendChild(market_city_label); market.appendChild(market_city); @@ -2196,16 +2202,16 @@ if (typeof window.markets !== "function") { main.appendChild(market); popup.appendChild(main); - this.body.wrap.appendChild(popup); + core.popup_body.wrap.appendChild(popup); errors.appendChild(dl); - this.body.errors.appendChild(errors); - this.body.wrap.appendChild(this.body.errors); + core.popup_body.errors.appendChild(errors); + core.popup_body.wrap.appendChild(core.popup_body.errors); - document.body.appendChild(this.body.wrap); + document.body.appendChild(core.popup_body.wrap); // Инициализация ширины окна с ошибками - this.body.errors.style.setProperty( + core.popup_body.errors.style.setProperty( "--calculated-width", popup.offsetWidth + "px" ); @@ -2224,17 +2230,19 @@ if (typeof window.markets !== "function") { ); setTimeout(() => errors.style.removeProperty("transition"), 100); } - top(this.body.errors); - const resize = new ResizeObserver(() => top(this.body.errors)); - resize.observe(this.body.wrap); + top(core.popup_body.errors); + const resize = new ResizeObserver(() => + top(core.popup_body.errors) + ); + resize.observe(core.popup_body.wrap); // Инициализация функции закрытия всплывающего окна const click = () => { // Блокировка - if (this.freeze) return; + if (core.popup_freeze) return; // Удаление всплывающего окна - this.body.wrap.remove(); + core.popup_body.wrap.remove(); // Инициализация буфера выбранных строк const buffer = [ @@ -2250,16 +2258,16 @@ if (typeof window.markets !== "function") { document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; }; // Инициализация функции добавления функции закрытия всплывающего окна const enable = () => - this.body.wrap.addEventListener("click", click); + core.popup_body.wrap.addEventListener("click", click); // Инициализация функции удаления функции закрытия всплывающего окна const disable = () => - this.body.wrap.removeEventListener("click", click); + core.popup_body.wrap.removeEventListener("click", click); // Первичная активация функции удаления всплывающего окна enable(); @@ -2271,10 +2279,10 @@ if (typeof window.markets !== "function") { // Инициализация функции блокировки удаления окна по событию select.addEventListener("click", () => { // Блокировка удаления окна - this.freeze = true; + core.popup_freeze = true; // Разблокировка удаления окна - setTimeout(() => (this.freeze = false), 100); + setTimeout(() => (core.popup_freeze = false), 100); }); for (const option of select.getElementsByTagName("option")) { @@ -2283,10 +2291,10 @@ if (typeof window.markets !== "function") { // Инициализация функции блокировки удаления окна по событию option.addEventListener("click", () => { // Блокировка удаления окна - this.freeze = true; + core.popup_freeze = true; // Разблокировка удаления окна - setTimeout(() => (this.freeze = false), 100); + setTimeout(() => (core.popup_freeze = false), 100); }); } } @@ -2296,7 +2304,7 @@ if (typeof window.markets !== "function") { // Перебор всех textarea-элементов // Обновлять позицию окна с ошибками при изменении размера textarea (там position: absolute) - new MutationObserver(() => top(this.body.errors)).observe( + new MutationObserver(() => top(core.popup_body.errors)).observe( textarea, { attributes: true, @@ -2320,17 +2328,20 @@ if (typeof window.markets !== "function") { }); // Добавление функции блокировки удаления окна и клавиш по событиям - textarea.addEventListener("focus", () => (this.freeze = true)); + textarea.addEventListener( + "focus", + () => (core.popup_freeze = true) + ); textarea.addEventListener( "focusout", - () => (this.freeze = false) + () => (core.popup_freeze = false) ); } // Инициализация функции управления кнопками this.buttons = (e, force = false) => { // Блокировка - if (!force && this.freeze) return; + if (!force && core.popup_freeze) return; if (e.keyCode === 27) { // Нажата кнопка: "escape" @@ -2341,16 +2352,16 @@ if (typeof window.markets !== "function") { // Нажата кнопка: "enter" // Инициализация буфера с текущим статусом блокировки закрытия окна - const freeze = this.freeze; + const freeze = core.popup_freeze; // Блокировка закрытия окна (чтобы не вызвался click() через событие onclick) - this.freeze = true; + core.popup_freeze = true; // Активация виртуальной кнопки "сохранить" market_update_button.click(); // Возвращение статуса блокировки закрытия окна - this.freeze = freeze; + core.popup_freeze = freeze; } }; @@ -2538,17 +2549,17 @@ if (typeof window.markets !== "function") { navigator.clipboard.writeText(data.clipboard); } - if (this.core.body.wrap instanceof HTMLElement) { + if (core.popup_body.wrap instanceof HTMLElement) { // Найдено активное окно // Деинициализация быстрых действий по кнопкам document.removeEventListener("keydown", this.core.buttons); // Сброс блокировки - this.core.freeze = false; + core.popup_freeze = false; // Деинициализация активного окна - this.core.body.wrap.remove(); + core.popup_body.wrap.remove(); // Удаление статуса активной строки row.removeAttribute("data-selected"); @@ -2596,8 +2607,8 @@ if (typeof window.markets !== "function") { row.setAttribute("data-selected", "true"); // Инициализация оболочки всплывающего окна - this.core.body.wrap = document.createElement("div"); - this.core.body.wrap.setAttribute("id", "popup"); + core.popup_body.wrap = document.createElement("div"); + core.popup_body.wrap.setAttribute("id", "popup"); // Инициализация оболочки всплывающего окна const popup = document.createElement("section"); @@ -2859,15 +2870,15 @@ if (typeof window.markets !== "function") { ); // Инициализация окна с ошибками - this.core.body.errors = document.createElement("section"); - this.core.body.errors.classList.add( + core.popup_body.errors = document.createElement("section"); + core.popup_body.errors.classList.add( "errors", "window", "list", "small", "hidden" ); - this.core.body.errors.setAttribute("data-errors", true); + core.popup_body.errors.setAttribute("data-errors", true); // Инициализация элемента-оболочки с ошибками для всплывающего окна const errors = document.createElement("section"); @@ -2886,7 +2897,7 @@ if (typeof window.markets !== "function") { document.removeEventListener("keydown", this.buttons); // Сброс блокировки - this.freeze = false; + core.popup_freeze = false; // Удаление активного окна old.remove(); @@ -2935,13 +2946,13 @@ if (typeof window.markets !== "function") { main.appendChild(account); popup.appendChild(main); - this.core.body.wrap.appendChild(popup); + core.popup_body.wrap.appendChild(popup); errors.appendChild(dl); - this.core.body.errors.appendChild(errors); - this.core.body.wrap.appendChild(this.core.body.errors); + core.popup_body.errors.appendChild(errors); + core.popup_body.wrap.appendChild(core.popup_body.errors); - document.body.appendChild(this.core.body.wrap); + document.body.appendChild(core.popup_body.wrap); // Инициалиация масок account_number_input.mask = IMask(account_number_input, { @@ -2960,11 +2971,11 @@ if (typeof window.markets !== "function") { 100 ); } - top(this.core.body.errors); + top(core.popup_body.errors); const resize = new ResizeObserver(() => - top(this.core.body.errors) + top(core.popup_body.errors) ); - resize.observe(this.core.body.wrap); + resize.observe(core.popup_body.wrap); // Инициализация функции генерации пароля и записи в поле ввода и буфер обмена const generate = (e) => { @@ -3038,10 +3049,10 @@ if (typeof window.markets !== "function") { // Инициализация функции закрытия всплывающего окна const click = () => { // Блокировка - if (this.core.freeze) return; + if (core.popup_freeze) return; // Удаление всплывающего окна - this.core.body.wrap.remove(); + core.popup_body.wrap.remove(); // Инициализация буфера выбранных строк const buffer = [ @@ -3057,16 +3068,16 @@ if (typeof window.markets !== "function") { document.removeEventListener("keydown", this.core.buttons); // Сброс блокировки - this.core.freeze = false; + core.popup_freeze = false; }; // Инициализация функции добавления функции закрытия всплывающего окна const enable = () => - this.core.body.wrap.addEventListener("click", click); + core.popup_body.wrap.addEventListener("click", click); // Инициализация функции удаления функции закрытия всплывающего окна const disable = () => - this.core.body.wrap.removeEventListener("click", click); + core.popup_body.wrap.removeEventListener("click", click); // Первичная активация функции удаления всплывающего окна enable(); @@ -3078,10 +3089,10 @@ if (typeof window.markets !== "function") { // Инициализация функции блокировки удаления окна по событию select.addEventListener("click", () => { // Блокировка удаления окна - this.core.freeze = true; + core.popup_freeze = true; // Разблокировка удаления окна - setTimeout(() => (this.core.freeze = false), 100); + setTimeout(() => (core.popup_freeze = false), 100); }); for (const option of select.getElementsByTagName("option")) { @@ -3090,10 +3101,10 @@ if (typeof window.markets !== "function") { // Инициализация функции блокировки удаления окна по событию option.addEventListener("click", () => { // Блокировка удаления окна - this.core.freeze = true; + core.popup_freeze = true; // Разблокировка удаления окна - setTimeout(() => (this.core.freeze = false), 100); + setTimeout(() => (core.popup_freeze = false), 100); }); } } @@ -3103,7 +3114,7 @@ if (typeof window.markets !== "function") { // Перебор всех textarea-элементов // Обновлять позицию окна с ошибками при изменении размера textarea (там position: absolute) - new MutationObserver(() => top(this.core.body.errors)).observe( + new MutationObserver(() => top(core.popup_body.errors)).observe( textarea, { attributes: true, @@ -3129,18 +3140,18 @@ if (typeof window.markets !== "function") { // Добавление функции блокировки удаления окна и клавиш по событиям textarea.addEventListener( "focus", - () => (this.core.freeze = true) + () => (core.popup_freeze = true) ); textarea.addEventListener( "focusout", - () => (this.core.freeze = false) + () => (core.popup_freeze = false) ); } // Инициализация функции управления кнопками this.core.buttons = (e, force = false) => { // Блокировка - if (!force && this.core.freeze) return; + if (!force && core.popup_freeze) return; if (e.keyCode === 27) { // Нажата кнопка: "escape" @@ -3151,16 +3162,16 @@ if (typeof window.markets !== "function") { // Нажата кнопка: "enter" // Инициализация буфера с текущим статусом блокировки закрытия окна - const freeze = this.core.freeze; + const freeze = core.popup_freeze; // Блокировка закрытия окна (чтобы не вызвался click() через событие onclick) - this.core.freeze = true; + core.popup_freeze = true; // Активация виртуальной кнопки "сохранить" account_update_button.click(); // Возвращение статуса блокировки закрытия окна - this.core.freeze = freeze; + core.popup_freeze = freeze; } }; @@ -3307,17 +3318,17 @@ if (typeof window.markets !== "function") { row.remove(); } - if (this.core.body.wrap instanceof HTMLElement) { + if (core.popup_body.wrap instanceof HTMLElement) { // Найдено активное окно // Деинициализация быстрых действий по кнопкам document.removeEventListener("keydown", this.core.buttons); // Сброс блокировки - this.core.freeze = false; + core.popup_freeze = false; // Деинициализация активного окна - this.core.body.wrap.remove(); + core.popup_body.wrap.remove(); // Удаление статуса активной строки row.removeAttribute("data-selected"); @@ -3337,7 +3348,10 @@ if (typeof window.markets !== "function") { */ static async ban(worker, button) { console.log(button); - if ((typeof worker === "string" || typeof worker === 'number') && button instanceof HTMLElement) { + if ( + (typeof worker === "string" || typeof worker === "number") && + button instanceof HTMLElement + ) { // Получены идентификатор сотрудника и кнопка // Запрос к серверу @@ -3371,7 +3385,10 @@ if (typeof window.markets !== "function") { * @param {HTMLElement} button Кнопка - + + + {% endif %} + {% if account.type == 'administrator' or (account.type == 'operator' and account.transactions) or account.type == + 'market' %}
- - +
{% endif %} diff --git a/mirzaev/ebala/system/views/pages/workers.html b/mirzaev/ebala/system/views/pages/workers.html old mode 100644 new mode 100755 diff --git a/mirzaev/ebala/system/views/templater.php b/mirzaev/ebala/system/views/templater.php index cf70cad..4d1cf24 100755 --- a/mirzaev/ebala/system/views/templater.php +++ b/mirzaev/ebala/system/views/templater.php @@ -15,7 +15,8 @@ use mirzaev\minimal\controller; use Twig\Loader\FilesystemLoader, Twig\Environment as twig, Twig\Extra\Intl\IntlExtension as intl, - Twig\TwigFilter; + Twig\TwigFilter, + Twig\TwigFunction; // Встроенные библиотеки @@ -122,12 +123,32 @@ final class templater extends controller implements ArrayAccess 'settings_by_city', function (array|null $settings = []) { $result = []; - foreach($settings ?? [] as $setting) $result[$setting['city']][] = $setting; + foreach ($settings ?? [] as $setting) $result[$setting['city']][] = $setting; return $result; } ) ); + // Инициализация функции + $this->twig->addFunction( + new TwigFunction( + 'format_name', + function (string|null $first, string|null $second, string|null $last, bool $full = false) { + if ($full) { + // Полное имя (Иван Иванов Иванович) + + // Генерация и возврат (успех) + return "$first $second $last"; + } else { + // Сокращённое имя (И. И. Иванов) + + // Генерация и возврат (успех) + return trim((!empty($first) ? mb_strtoupper(mb_substr($first, 0, 1)) . '.' : '') . (!empty($last) ? ' ' . mb_strtoupper(mb_substr($last, 0, 1)) . '.' : '') . (!empty($second) ? ' ' . $second : ''), ' '); + } + } + ) + ); + // Инициализация расширений $this->twig->addExtension(new intl()); }