начало работы над сессиями и там дохуя чего ещё
This commit is contained in:
		
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,3 @@ | ||||
| !.gitignore | ||||
| composer.phar | ||||
| vendor | ||||
| !.gitignore | ||||
| composer.phar | ||||
| vendor | ||||
|   | ||||
							
								
								
									
										22
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								LICENSE
									
									
									
									
									
								
							| @@ -1,11 +1,11 @@ | ||||
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||||
| Version 2, December 2004 | ||||
|  | ||||
| Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> | ||||
|  | ||||
| Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. | ||||
|  | ||||
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||||
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
|  | ||||
|   0. You just DO WHAT THE FUCK YOU WANT TO. | ||||
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||||
| Version 2, December 2004 | ||||
|  | ||||
| Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> | ||||
|  | ||||
| Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. | ||||
|  | ||||
| DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE | ||||
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
|  | ||||
|   0. You just DO WHAT THE FUCK YOU WANT TO. | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| # site-account | ||||
|  | ||||
| # site-account | ||||
|  | ||||
| Site for intersite authentication | ||||
							
								
								
									
										113
									
								
								composer.json
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								composer.json
									
									
									
									
									
								
							| @@ -1,57 +1,56 @@ | ||||
| { | ||||
|     "name": "mirzaev/site-account", | ||||
|     "description": "API for intersite authentication", | ||||
|     "readme": "README.md", | ||||
|     "keywords": [ | ||||
|         "site", | ||||
|         "api", | ||||
|         "authentication", | ||||
|         "auth" | ||||
|     ], | ||||
|     "type": "site", | ||||
|     "homepage": "https://git.mirzaev.sexy/mirzaev/site-account", | ||||
|     "license": "WTFPL", | ||||
|     "authors": [ | ||||
|         { | ||||
|             "name": "Arsen Mirzaev Tatyano-Muradovich", | ||||
|             "email": "arsen@mirzaev.sexy", | ||||
|             "homepage": "https://mirzaev.sexy", | ||||
|             "role": "Programmer" | ||||
|         } | ||||
|     ], | ||||
|     "support": { | ||||
|         "email": "arsen@mirzaev.sexy", | ||||
|         "wiki": "https://git.mirzaev.sexy/mirzaev/site-account/wiki", | ||||
|         "issues": "https://git.mirzaev.sexy/mirzaev/site-account/issues" | ||||
|     }, | ||||
|     "funding": [ | ||||
|         { | ||||
|             "type": "funding", | ||||
|             "url": "https://fund.mirzaev.sexy" | ||||
|         } | ||||
|     ], | ||||
|     "require": { | ||||
|         "php": "~8.1", | ||||
|         "ext-sodium": "~8.1", | ||||
|         "mirzaev/minimal": "^2.0.x-dev", | ||||
|         "mirzaev/accounts": "~1.2.x-dev", | ||||
|         "mirzaev/arangodb": "^1.0.0", | ||||
|         "mirzaev/vk": "^5.0", | ||||
|         "triagens/arangodb": "~3.9.x-dev", | ||||
|         "twig/twig": "^3.4", | ||||
|         "guzzlehttp/guzzle": "^7.5" | ||||
|     }, | ||||
|     "require-dev": { | ||||
|         "phpunit/phpunit": "~9.5" | ||||
|     }, | ||||
|     "autoload": { | ||||
|         "psr-4": { | ||||
|             "mirzaev\\site\\account\\": "mirzaev/site/account/system" | ||||
|         } | ||||
|     }, | ||||
|     "autoload-dev": { | ||||
|         "psr-4": { | ||||
|             "mirzaev\\site\\account\\tests\\": "mirzaev/site/account/tests" | ||||
|         } | ||||
|     } | ||||
| } | ||||
| { | ||||
|   "name": "mirzaev/site-account", | ||||
|   "description": "API for intersite authentication", | ||||
|   "readme": "README.md", | ||||
|   "keywords": [ | ||||
|     "site", | ||||
|     "api", | ||||
|     "authentication" | ||||
|   ], | ||||
|   "type": "site", | ||||
|   "homepage": "https://git.mirzaev.sexy/mirzaev/site-account", | ||||
|   "license": "WTFPL", | ||||
|   "authors": [ | ||||
|     { | ||||
|       "name": "Arsen Mirzaev Tatyano-Muradovich", | ||||
|       "email": "arsen@mirzaev.sexy", | ||||
|       "homepage": "https://mirzaev.sexy", | ||||
|       "role": "Programmer" | ||||
|     } | ||||
|   ], | ||||
|   "support": { | ||||
|     "email": "arsen@mirzaev.sexy", | ||||
|     "wiki": "https://git.mirzaev.sexy/mirzaev/site-account/wiki", | ||||
|     "issues": "https://git.mirzaev.sexy/mirzaev/site-account/issues" | ||||
|   }, | ||||
|   "funding": [ | ||||
|     { | ||||
|       "type": "funding", | ||||
|       "url": "https://fund.mirzaev.sexy" | ||||
|     } | ||||
|   ], | ||||
|   "require": { | ||||
|     "php": "~8.2", | ||||
|     "ext-sodium": "~8.2", | ||||
|     "mirzaev/minimal": "^2.0.x-dev", | ||||
|     "mirzaev/accounts": "~1.2.x-dev", | ||||
|     "mirzaev/arangodb": "^1.0.0", | ||||
|     "mirzaev/vk": "^5.0", | ||||
|     "triagens/arangodb": "~3.9.x-dev", | ||||
|     "twig/twig": "^3.4", | ||||
|     "guzzlehttp/guzzle": "^7.5" | ||||
|   }, | ||||
|   "require-dev": { | ||||
|     "phpunit/phpunit": "~9.5" | ||||
|   }, | ||||
|   "autoload": { | ||||
|     "psr-4": { | ||||
|       "mirzaev\\site\\account\\": "mirzaev/site/account/system" | ||||
|     } | ||||
|   }, | ||||
|   "autoload-dev": { | ||||
|     "psr-4": { | ||||
|       "mirzaev\\site\\account\\tests\\": "mirzaev/site/account/tests" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,146 +1,146 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\controllers; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\controllers\core; | ||||
| use mirzaev\site\account\models\account_model as account; | ||||
| use mirzaev\site\account\models\session_model as session; | ||||
| use mirzaev\site\account\models\vk_model as vk; | ||||
|  | ||||
| // Библиотека для ArangoDB | ||||
| use ArangoDBClient\Document as _document; | ||||
| use stdClass; | ||||
|  | ||||
| // Фреймворк для ВКонтакте | ||||
| use mirzaev\vk\core as api; | ||||
|  | ||||
| /** | ||||
|  * Контроллер аккаунтов | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class account_controller extends core | ||||
| { | ||||
|     /** | ||||
|      * Страница профиля | ||||
|      * | ||||
|      * @param array $parameters Параметры запроса | ||||
|      */ | ||||
|     public function index(array $parameters = []): ?string | ||||
|     { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Инициализация | ||||
|      * | ||||
|      * @param array $parameters Параметры запроса | ||||
|      */ | ||||
|     public function initialization(array $parameters = []): ?string | ||||
|     { | ||||
|         if ($this->variables['account'] instanceof _document) { | ||||
|             // Найден аккаунт | ||||
|  | ||||
|             if ($this->variables['vk'] instanceof _document) { | ||||
|                 // Найден аккаунт ВКонтакте | ||||
|  | ||||
|                 // Инициализация данных аккаунта ВКонтакте | ||||
|                 vk::parse($this->variables['vk'], $this->variables['errors']['vk']); | ||||
|             } | ||||
|  | ||||
|             // Запись кода ответа | ||||
|             http_response_code(200); | ||||
|  | ||||
|             return null; | ||||
|         } else { | ||||
|             // Не найден аккаунт | ||||
|  | ||||
|             // Запись кода ответа | ||||
|             http_response_code(401); | ||||
|  | ||||
|             // Запись заголовка ответа с ключом аккаунта | ||||
|             header('session: ' . $this->variables['session']->hash); | ||||
|  | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // Запись кода ответа | ||||
|         http_response_code(500); | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Связь аккаунта с аккаунтом ВКонтакте | ||||
|      * | ||||
|      * @param array $parameters Параметры запроса | ||||
|      */ | ||||
|     public function connect(array $parameters = []): ?string | ||||
|     { | ||||
|         if ($this->variables['session']->hash === $parameters['state']) { | ||||
|             // Совпадает хеш сессии с полученным хешем из ответа ВКонтакте | ||||
|  | ||||
|             if (!empty($response = vk::key($parameters['code'], $this->variables['errors']['vk']))) { | ||||
|                 // Получены данные аккаунта ВКонтакте | ||||
|  | ||||
|                 if (($this->variables['vk'] = vk::initialization($response, $this->variables['errors']['vk'])) instanceof _document) { | ||||
|                     // Инициализирован аккаунт ВКонтакте | ||||
|  | ||||
|                     if (($this->variables['account'] = vk::account($this->variables['vk'])) instanceof _document) { | ||||
|                         // Найден аккаунт (существующий) | ||||
|  | ||||
|                         if (session::connect($this->variables['session'], $this->variables['account'], $this->variables['errors']['session'])) { | ||||
|                             // Связана сессия с аккаунтом | ||||
|                         } | ||||
|                     } else if (($this->variables['account'] = account::create($this->variables['errors']['account'])) instanceof _document) { | ||||
|                         // Найден аккаунт (создан новый) | ||||
|  | ||||
|                         if (session::connect($this->variables['session'], $this->variables['account'], $this->variables['errors']['session'])) { | ||||
|                             // Связана сессия с аккаунтом | ||||
|  | ||||
|                             if (account::connect($this->variables['account'], $this->variables['vk'], $this->variables['errors']['account'])) { | ||||
|                                 // Связан аккаунт с аккаунтом ВКонтакте | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     // Инициализация робота для аккаунта ВКонтакте | ||||
|                     $this->vk = api::init()->user(key: $this->variables['vk']->access['key']); | ||||
|  | ||||
|                     if ($this->variables['vk'] instanceof _document) { | ||||
|                         // Инициализирован робот для аккаунта ВКонтакте | ||||
|  | ||||
|                         // Инициализация данных аккаунта ВКонтакте | ||||
|                         $data = vk::parse($this->vk, $this->variables['errors']['vk']); | ||||
|                         var_dump($data); die; | ||||
|  | ||||
|                         if ($data instanceof stdClass) { | ||||
|                             // Получены данные ВКонтакте | ||||
|  | ||||
|                             // Запись в базу данных | ||||
|                             vk::update($this->variables['vk'], $data, $this->variables['errors']['vk']); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Генерация представления | ||||
|         return $this->view->render(DIRECTORY_SEPARATOR . 'account' . DIRECTORY_SEPARATOR . 'vk.html', $this->variables); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Генерация панели аккаунта | ||||
|      * | ||||
|      * @param array $parameters Параметры запроса | ||||
|      */ | ||||
|     public function panel(array $parameters = []): ?string | ||||
|     { | ||||
|         // Генерация представления | ||||
|         return $this->view->render(DIRECTORY_SEPARATOR . 'account' . DIRECTORY_SEPARATOR . 'panel.html', $this->variables); | ||||
|     } | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\controllers; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\controllers\core; | ||||
| use mirzaev\site\account\models\account_model as account; | ||||
| use mirzaev\site\account\models\session_model as session; | ||||
| use mirzaev\site\account\models\vk_model as vk; | ||||
|  | ||||
| // Библиотека для ArangoDB | ||||
| use ArangoDBClient\Document as _document; | ||||
| use stdClass; | ||||
|  | ||||
| // Фреймворк для ВКонтакте | ||||
| use mirzaev\vk\core as api; | ||||
|  | ||||
| /** | ||||
|  * Контроллер аккаунтов | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class account_controller extends core | ||||
| { | ||||
|     /** | ||||
|      * Страница профиля | ||||
|      * | ||||
|      * @param array $parameters Параметры запроса | ||||
|      */ | ||||
|     public function index(array $parameters = []): ?string | ||||
|     { | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Инициализация | ||||
|      * | ||||
|      * @param array $parameters Параметры запроса | ||||
|      */ | ||||
|     public function initialization(array $parameters = []): ?string | ||||
|     { | ||||
|         if ($this->variables['account'] instanceof _document) { | ||||
|             // Найден аккаунт | ||||
|  | ||||
|             if ($this->variables['vk'] instanceof _document) { | ||||
|                 // Найден аккаунт ВКонтакте | ||||
|  | ||||
|                 // Инициализация данных аккаунта ВКонтакте | ||||
|                 vk::parse($this->variables['vk'], $this->variables['errors']['vk']); | ||||
|             } | ||||
|  | ||||
|             // Запись кода ответа | ||||
|             http_response_code(200); | ||||
|  | ||||
|             return null; | ||||
|         } else { | ||||
|             // Не найден аккаунт | ||||
|  | ||||
|             // Запись кода ответа | ||||
|             http_response_code(401); | ||||
|  | ||||
|             // Запись заголовка ответа с ключом аккаунта | ||||
|             header('session: ' . $this->variables['session']->hash); | ||||
|  | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         // Запись кода ответа | ||||
|         http_response_code(500); | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Связь аккаунта с аккаунтом ВКонтакте | ||||
|      * | ||||
|      * @param array $parameters Параметры запроса | ||||
|      */ | ||||
|     public function connect(array $parameters = []): ?string | ||||
|     { | ||||
|         if ($this->variables['session']->hash === $parameters['state']) { | ||||
|             // Совпадает хеш сессии с полученным хешем из ответа ВКонтакте | ||||
|  | ||||
|             if (!empty($response = vk::key($parameters['code'], $this->variables['errors']['vk']))) { | ||||
|                 // Получены данные аккаунта ВКонтакте | ||||
|  | ||||
|                 if (($this->variables['vk'] = vk::initialization($response, $this->variables['errors']['vk'])) instanceof _document) { | ||||
|                     // Инициализирован аккаунт ВКонтакте | ||||
|  | ||||
|                     if (($this->variables['account'] = vk::account($this->variables['vk'])) instanceof _document) { | ||||
|                         // Найден аккаунт (существующий) | ||||
|  | ||||
|                         if (session::connect($this->variables['session'], $this->variables['account'], $this->variables['errors']['session'])) { | ||||
|                             // Связана сессия с аккаунтом | ||||
|                         } | ||||
|                     } else if (($this->variables['account'] = account::create($this->variables['errors']['account'])) instanceof _document) { | ||||
|                         // Найден аккаунт (создан новый) | ||||
|  | ||||
|                         if (session::connect($this->variables['session'], $this->variables['account'], $this->variables['errors']['session'])) { | ||||
|                             // Связана сессия с аккаунтом | ||||
|  | ||||
|                             if (account::connect($this->variables['account'], $this->variables['vk'], $this->variables['errors']['account'])) { | ||||
|                                 // Связан аккаунт с аккаунтом ВКонтакте | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     // Инициализация робота для аккаунта ВКонтакте | ||||
|                     $this->vk = api::init()->user(key: $this->variables['vk']->access['key']); | ||||
|  | ||||
|                     if ($this->variables['vk'] instanceof _document) { | ||||
|                         // Инициализирован робот для аккаунта ВКонтакте | ||||
|  | ||||
|                         // Инициализация данных аккаунта ВКонтакте | ||||
|                         $data = vk::parse($this->vk, $this->variables['errors']['vk']); | ||||
|                         var_dump($data); die; | ||||
|  | ||||
|                         if ($data instanceof stdClass) { | ||||
|                             // Получены данные ВКонтакте | ||||
|  | ||||
|                             // Запись в базу данных | ||||
|                             vk::update($this->variables['vk'], $data, $this->variables['errors']['vk']); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Генерация представления | ||||
|         return $this->view->render(DIRECTORY_SEPARATOR . 'account' . DIRECTORY_SEPARATOR . 'vk.html', $this->variables); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Генерация панели аккаунта | ||||
|      * | ||||
|      * @param array $parameters Параметры запроса | ||||
|      */ | ||||
|     public function panel(array $parameters = []): ?string | ||||
|     { | ||||
|         // Генерация представления | ||||
|         return $this->view->render(DIRECTORY_SEPARATOR . 'account' . DIRECTORY_SEPARATOR . 'panel.html', $this->variables); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -61,7 +61,7 @@ class core extends controller | ||||
|         $expires = time() + 604800; | ||||
|  | ||||
|         // Инициализация сессии (без журналирования) | ||||
|         $this->variables['session'] = session::initialization($_COOKIE["session"] ?? null, $expires) ?? header('Location: https://mirzaev.sexy/error?code=500&text=Не+удалось+инициализировать+сессию'); | ||||
|         $this->variables['session'] = new session($_COOKIE["session"] ?? null, $expires) ?? header('Location: https://mirzaev.sexy/error?code=500&text=Не+удалось+инициализировать+сессию'); | ||||
|  | ||||
|         if ($_COOKIE["session"] ?? null !== $this->variables['session']->hash) { | ||||
|             // Изменился хеш сессии (подразумевается, что сессия устарела) | ||||
| @@ -78,7 +78,7 @@ class core extends controller | ||||
|         } | ||||
|  | ||||
|         // Инициализация аккаунта (без журналирования) | ||||
|         $this->variables['account'] = session::account($this->variables['session']); | ||||
|         $this->variables['account'] = $this->variables['session']->account(); | ||||
|  | ||||
|         if ($this->variables['account'] instanceof _document) { | ||||
|             // Инициализирован аккаунт | ||||
|   | ||||
| @@ -1,44 +1,44 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\controllers; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\controllers\core; | ||||
|  | ||||
| /** | ||||
|  * Контроллер ошибок | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class error_controller extends core | ||||
| { | ||||
|     /** | ||||
|      * Страница с ошибкой | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      */ | ||||
|     public function index(array $parameters = []): ?string | ||||
|     { | ||||
|         // Запись текста ошибки в переменную окружения | ||||
|         $this->variables['text'] = $parameters['text'] ?? null; | ||||
|  | ||||
|         if (isset($parameters['code'])) { | ||||
|             // Получен код ошибки | ||||
|  | ||||
|             // Запись кода ошибки в переменную окружения | ||||
|             $this->variables['code'] = $parameters['code']; | ||||
|  | ||||
|             // Запись кода ответа | ||||
|             http_response_code($parameters['code']); | ||||
|  | ||||
|             // Генерация представления | ||||
|             return $this->view->render(DIRECTORY_SEPARATOR . 'errors' . DIRECTORY_SEPARATOR . 'index.html', $this->variables); | ||||
|         } | ||||
|  | ||||
|         // Генерация представления | ||||
|         return $this->view->render(DIRECTORY_SEPARATOR . 'errors' . DIRECTORY_SEPARATOR . ($parameters['code'] ?? 'index') . '.html', $this->variables); | ||||
|     } | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\controllers; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\controllers\core; | ||||
|  | ||||
| /** | ||||
|  * Контроллер ошибок | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class error_controller extends core | ||||
| { | ||||
|     /** | ||||
|      * Страница с ошибкой | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      */ | ||||
|     public function index(array $parameters = []): ?string | ||||
|     { | ||||
|         // Запись текста ошибки в переменную окружения | ||||
|         $this->variables['text'] = $parameters['text'] ?? null; | ||||
|  | ||||
|         if (isset($parameters['code'])) { | ||||
|             // Получен код ошибки | ||||
|  | ||||
|             // Запись кода ошибки в переменную окружения | ||||
|             $this->variables['code'] = $parameters['code']; | ||||
|  | ||||
|             // Запись кода ответа | ||||
|             http_response_code($parameters['code']); | ||||
|  | ||||
|             // Генерация представления | ||||
|             return $this->view->render(DIRECTORY_SEPARATOR . 'errors' . DIRECTORY_SEPARATOR . 'index.html', $this->variables); | ||||
|         } | ||||
|  | ||||
|         // Генерация представления | ||||
|         return $this->view->render(DIRECTORY_SEPARATOR . 'errors' . DIRECTORY_SEPARATOR . ($parameters['code'] ?? 'index') . '.html', $this->variables); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,59 +1,59 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\controllers; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\controllers\core; | ||||
|  | ||||
| /** | ||||
|  * Контроллер графика | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class graph_controller extends core | ||||
| { | ||||
|     /** | ||||
|      * Страница с графиком | ||||
|      * | ||||
|      * Можно использовать совместно с элементом <iframe> для изоляции | ||||
|      * содержимого бегущей строки от поисковых роботов | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      */ | ||||
|     public function index(array $parameters = []): ?string | ||||
|     { | ||||
|         // Инициализация элементов для генерации в головном элементе | ||||
|         $this->variables['head'] = [ | ||||
|             'title' => 'Бегущая строка', | ||||
|             'metas' => [ | ||||
|                 [ | ||||
|                     'attributes' => [ | ||||
|                         'name' => 'robots', | ||||
|                         'content' => 'nofollow' | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация бегущей строки | ||||
|         $this->variables['graph'] = [ | ||||
|             'id' => $this->variables['request']['id'] ?? 'graph' | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация аттрибутов бегущей строки | ||||
|         $this->variables['graph']['attributes'] = [ | ||||
|  | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация элементов бегущей строки | ||||
|         $this->variables['graph']['elements'] = [ | ||||
|         ]; | ||||
|  | ||||
|         // Генерация представления | ||||
|         return $this->view->render(DIRECTORY_SEPARATOR . 'graph' . DIRECTORY_SEPARATOR . 'index.html', $this->variables); | ||||
|     } | ||||
|  | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\controllers; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\controllers\core; | ||||
|  | ||||
| /** | ||||
|  * Контроллер графика | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class graph_controller extends core | ||||
| { | ||||
|     /** | ||||
|      * Страница с графиком | ||||
|      * | ||||
|      * Можно использовать совместно с элементом <iframe> для изоляции | ||||
|      * содержимого бегущей строки от поисковых роботов | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      */ | ||||
|     public function index(array $parameters = []): ?string | ||||
|     { | ||||
|         // Инициализация элементов для генерации в головном элементе | ||||
|         $this->variables['head'] = [ | ||||
|             'title' => 'Бегущая строка', | ||||
|             'metas' => [ | ||||
|                 [ | ||||
|                     'attributes' => [ | ||||
|                         'name' => 'robots', | ||||
|                         'content' => 'nofollow' | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация бегущей строки | ||||
|         $this->variables['graph'] = [ | ||||
|             'id' => $this->variables['request']['id'] ?? 'graph' | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация аттрибутов бегущей строки | ||||
|         $this->variables['graph']['attributes'] = [ | ||||
|  | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация элементов бегущей строки | ||||
|         $this->variables['graph']['elements'] = [ | ||||
|         ]; | ||||
|  | ||||
|         // Генерация представления | ||||
|         return $this->view->render(DIRECTORY_SEPARATOR . 'graph' . DIRECTORY_SEPARATOR . 'index.html', $this->variables); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,82 +1,82 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\controllers; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\controllers\core; | ||||
|  | ||||
| /** | ||||
|  * Контроллер бегущей строки | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class hotline_controller extends core | ||||
| { | ||||
|     /** | ||||
|      * Страница с бегущей строкой | ||||
|      * | ||||
|      * Можно использовать совместно с элементом <iframe> для изоляции | ||||
|      * содержимого бегущей строки от поисковых роботов | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      */ | ||||
|     public function index(array $parameters = []): ?string | ||||
|     { | ||||
|         // Инициализация элементов для генерации в головном элементе | ||||
|         $this->variables['head'] = [ | ||||
|             'title' => 'Бегущая строка', | ||||
|             'metas' => [ | ||||
|                 [ | ||||
|                     'attributes' => [ | ||||
|                         'name' => 'robots', | ||||
|                         'content' => 'nofollow' | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация бегущей строки | ||||
|         $this->variables['hotline'] = [ | ||||
|             'id' => $this->variables['request']['id'] ?? 'hotline' | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация параметров бегущей строки | ||||
|         $this->variables['hotline']['parameters'] = [ | ||||
|             // 'step' => 2 | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация аттрибутов бегущей строки | ||||
|         $this->variables['hotline']['attributes'] = [ | ||||
|  | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация элементов бегущей строки | ||||
|         $this->variables['hotline']['elements'] = [ | ||||
|             ['content' => '1'], | ||||
|             [ | ||||
|                 'tag' => 'article', | ||||
|                 'content' => '2' | ||||
|             ], | ||||
|             ['content' => '3'], | ||||
|             ['content' => '4'], | ||||
|             ['content' => '5'], | ||||
|             ['content' => '6'], | ||||
|             ['content' => '7'], | ||||
|             ['content' => '8'], | ||||
|             ['content' => '9'], | ||||
|             ['content' => '10'], | ||||
|             ['content' => '11'], | ||||
|             ['content' => '12'], | ||||
|             ['content' => '13'], | ||||
|             ['content' => '14'], | ||||
|             ['content' => '15'] | ||||
|         ]; | ||||
|  | ||||
|         // Генерация представления | ||||
|         return $this->view->render(DIRECTORY_SEPARATOR . 'hotline' . DIRECTORY_SEPARATOR . 'index.html', $this->variables); | ||||
|     } | ||||
|  | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\controllers; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\controllers\core; | ||||
|  | ||||
| /** | ||||
|  * Контроллер бегущей строки | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class hotline_controller extends core | ||||
| { | ||||
|     /** | ||||
|      * Страница с бегущей строкой | ||||
|      * | ||||
|      * Можно использовать совместно с элементом <iframe> для изоляции | ||||
|      * содержимого бегущей строки от поисковых роботов | ||||
|      * | ||||
|      * @param array $parameters | ||||
|      */ | ||||
|     public function index(array $parameters = []): ?string | ||||
|     { | ||||
|         // Инициализация элементов для генерации в головном элементе | ||||
|         $this->variables['head'] = [ | ||||
|             'title' => 'Бегущая строка', | ||||
|             'metas' => [ | ||||
|                 [ | ||||
|                     'attributes' => [ | ||||
|                         'name' => 'robots', | ||||
|                         'content' => 'nofollow' | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация бегущей строки | ||||
|         $this->variables['hotline'] = [ | ||||
|             'id' => $this->variables['request']['id'] ?? 'hotline' | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация параметров бегущей строки | ||||
|         $this->variables['hotline']['parameters'] = [ | ||||
|             // 'step' => 2 | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация аттрибутов бегущей строки | ||||
|         $this->variables['hotline']['attributes'] = [ | ||||
|  | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация элементов бегущей строки | ||||
|         $this->variables['hotline']['elements'] = [ | ||||
|             ['content' => '1'], | ||||
|             [ | ||||
|                 'tag' => 'article', | ||||
|                 'content' => '2' | ||||
|             ], | ||||
|             ['content' => '3'], | ||||
|             ['content' => '4'], | ||||
|             ['content' => '5'], | ||||
|             ['content' => '6'], | ||||
|             ['content' => '7'], | ||||
|             ['content' => '8'], | ||||
|             ['content' => '9'], | ||||
|             ['content' => '10'], | ||||
|             ['content' => '11'], | ||||
|             ['content' => '12'], | ||||
|             ['content' => '13'], | ||||
|             ['content' => '14'], | ||||
|             ['content' => '15'] | ||||
|         ]; | ||||
|  | ||||
|         // Генерация представления | ||||
|         return $this->view->render(DIRECTORY_SEPARATOR . 'hotline' . DIRECTORY_SEPARATOR . 'index.html', $this->variables); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,85 +1,80 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\controllers; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\controllers\core; | ||||
|  | ||||
| /** | ||||
|  * Контроллер основной страницы | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class index_controller extends core | ||||
| { | ||||
|     /** | ||||
|      * Главная страница | ||||
|      * | ||||
|      * @param array $parameters Параметры запроса | ||||
|      */ | ||||
|     public function index(array $parameters = []): ?string | ||||
|     { | ||||
|         // Инициализация загружаемых категорий | ||||
|         $this->variables['include'] = [ | ||||
|             'head' => ['self'], | ||||
|             'body' => ['self'] | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация бегущей строки | ||||
|         $this->variables['hotline'] = [ | ||||
|             'id' => $this->variables['request']['id'] ?? 'hotline' | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация параметров бегущей строки | ||||
|         $this->variables['hotline']['parameters'] = [ | ||||
|             // 'step' => 2 | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация аттрибутов бегущей строки | ||||
|         $this->variables['hotline']['attributes'] = [ | ||||
|  | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация элементов бегущей строки | ||||
|         $this->variables['hotline']['elements'] = [ | ||||
|             ['content' => '1'], | ||||
|             [ | ||||
|                 'tag' => 'article', | ||||
|                 'content' => '2' | ||||
|             ], | ||||
|             ['content' => '3'], | ||||
|             ['content' => '4'], | ||||
|             ['content' => '5'], | ||||
|             ['content' => '6'], | ||||
|             ['content' => '7'], | ||||
|             ['content' => '8'], | ||||
|             ['content' => '9'], | ||||
|             ['content' => '10'], | ||||
|             ['content' => '11'], | ||||
|             ['content' => '12'], | ||||
|             ['content' => '13'], | ||||
|             ['content' => '14'], | ||||
|             ['content' => '15'] | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация бегущей строки | ||||
|         $this->variables['graph'] = [ | ||||
|             'id' => $this->variables['request']['id'] ?? 'graph' | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация аттрибутов бегущей строки | ||||
|         $this->variables['graph']['attributes'] = [ | ||||
|  | ||||
|         ]; | ||||
|  | ||||
|         // Инициализация элементов бегущей строки | ||||
|         $this->variables['graph']['elements'] = [ | ||||
|         ]; | ||||
|  | ||||
|         // Генерация представления | ||||
|         return $this->view->render(DIRECTORY_SEPARATOR . 'index.html', $this->variables); | ||||
|     } | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\controllers; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\controllers\core; | ||||
|  | ||||
| /** | ||||
|  * Контроллер основной страницы | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class index_controller extends core | ||||
| { | ||||
|   /** | ||||
|    * Главная страница | ||||
|    * | ||||
|    * @param array $parameters Параметры запроса | ||||
|    */ | ||||
|   public function index(array $parameters = []): ?string | ||||
|   { | ||||
|     // Инициализация загружаемых категорий | ||||
|     $this->variables['include'] = [ | ||||
|       'head' => ['self'], | ||||
|       'body' => ['self'] | ||||
|     ]; | ||||
|  | ||||
|     // Инициализация бегущей строки | ||||
|     $this->variables['hotline'] = [ | ||||
|       'id' => $this->variables['request']['id'] ?? 'hotline' | ||||
|     ]; | ||||
|  | ||||
|     // Инициализация параметров бегущей строки | ||||
|     $this->variables['hotline']['parameters'] = [ | ||||
|       // 'step' => 2 | ||||
|     ]; | ||||
|  | ||||
|     // Инициализация аттрибутов бегущей строки | ||||
|     $this->variables['hotline']['attributes'] = []; | ||||
|  | ||||
|     // Инициализация элементов бегущей строки | ||||
|     $this->variables['hotline']['elements'] = [ | ||||
|       ['content' => '1'], | ||||
|       [ | ||||
|         'tag' => 'article', | ||||
|         'content' => '2' | ||||
|       ], | ||||
|       ['content' => '3'], | ||||
|       ['content' => '4'], | ||||
|       ['content' => '5'], | ||||
|       ['content' => '6'], | ||||
|       ['content' => '7'], | ||||
|       ['content' => '8'], | ||||
|       ['content' => '9'], | ||||
|       ['content' => '10'], | ||||
|       ['content' => '11'], | ||||
|       ['content' => '12'], | ||||
|       ['content' => '13'], | ||||
|       ['content' => '14'], | ||||
|       ['content' => '15'] | ||||
|     ]; | ||||
|  | ||||
|     // Инициализация бегущей строки | ||||
|     $this->variables['graph'] = [ | ||||
|       'id' => $this->variables['request']['id'] ?? 'graph' | ||||
|     ]; | ||||
|  | ||||
|     // Инициализация аттрибутов бегущей строки | ||||
|     $this->variables['graph']['attributes'] = []; | ||||
|  | ||||
|     // Инициализация элементов бегущей строки | ||||
|     $this->variables['graph']['elements'] = []; | ||||
|  | ||||
|     // Генерация представления | ||||
|     return $this->view->render(DIRECTORY_SEPARATOR . 'index.html', $this->variables); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,169 +1,169 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\models\vk_model as vk; | ||||
|  | ||||
| // Фреймворк ArangoDB | ||||
| use mirzaev\arangodb\collection, | ||||
|     mirzaev\arangodb\document; | ||||
|  | ||||
| // Библиотека для ArangoDB | ||||
| use ArangoDBClient\Document as _document; | ||||
|  | ||||
| // Встроенные библиотеки | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Модель регистрации, аутентификации и авторизации | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class account_model extends core | ||||
| { | ||||
|     /** | ||||
|      * Коллекция | ||||
|      */ | ||||
|     public const COLLECTION = 'account'; | ||||
|  | ||||
|     /** | ||||
|      * Создать | ||||
|      * | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция аккаунта, если удалось создать | ||||
|      */ | ||||
|     public static function create(array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if (collection::init(static::$db->session, self::COLLECTION)) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 // Запись аккаунта в базу данных | ||||
|                 $_id = document::write(static::$db->session, self::COLLECTION); | ||||
|  | ||||
|                 if ($account = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                             FOR d IN %s | ||||
|                             FILTER d._id == '$_id' | ||||
|                             RETURN d | ||||
|                         AQL, | ||||
|                     self::COLLECTION | ||||
|                 ))) { | ||||
|                     // Найден созданный аккаунт | ||||
|  | ||||
|                     return $account; | ||||
|                 } else throw new exception('Не удалось создать аккаунт'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Связь аккаунта с аккаунтом ВКонтакте | ||||
|      * | ||||
|      * @param _document $account Инстанция аккаунта | ||||
|      * @param _document $vk Инстанция аккаунта ВКонтакте | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return bool Статус выполнения | ||||
|      */ | ||||
|     public static function connect(_document $account, _document $vk, array &$errors = []): bool | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, vk::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализированы коллекции | ||||
|  | ||||
|                 if (document::write(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, [ | ||||
|                     '_from' => $account->getId(), | ||||
|                     '_to' => $vk->getId() | ||||
|                 ])) { | ||||
|                     // Создано ребро: account -> vk | ||||
|  | ||||
|                     return true; | ||||
|                 } else throw new exception('Не удалось создать ребро: account -> vk'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Поиск связанного аккаунта ВКонтакте | ||||
|      * | ||||
|      * @param _document $account Инстанция аккаунта | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция аккаунта, если удалось найти | ||||
|      */ | ||||
|     public static function vk(_document $account, array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, vk::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 if ($vk = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                         FOR document IN %s | ||||
|                         LET edge = ( | ||||
|                             FOR edge IN %s | ||||
|                             FILTER edge._from == '%s' | ||||
|                             SORT edge._key DESC | ||||
|                             LIMIT 1 | ||||
|                             RETURN edge | ||||
|                         ) | ||||
|                         FILTER document._id == edge[0]._to | ||||
|                         LIMIT 1 | ||||
|                         RETURN document | ||||
|                     AQL, | ||||
|                     vk::COLLECTION, | ||||
|                     self::COLLECTION . '_edge_' . vk::COLLECTION, | ||||
|                     $account->getId() | ||||
|                 ))) { | ||||
|                     // Найден аккаунт ВКонтакте | ||||
|  | ||||
|                     return $vk; | ||||
|                 } else throw new exception('Не удалось найти аккаунт ВКонтакте'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\models\vk_model as vk; | ||||
|  | ||||
| // Фреймворк ArangoDB | ||||
| use mirzaev\arangodb\collection, | ||||
|     mirzaev\arangodb\document; | ||||
|  | ||||
| // Библиотека для ArangoDB | ||||
| use ArangoDBClient\Document as _document; | ||||
|  | ||||
| // Встроенные библиотеки | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Модель регистрации, аутентификации и авторизации | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class account_model extends core | ||||
| { | ||||
|     /** | ||||
|      * Коллекция | ||||
|      */ | ||||
|     public const COLLECTION = 'account'; | ||||
|  | ||||
|     /** | ||||
|      * Создать | ||||
|      * | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция аккаунта, если удалось создать | ||||
|      */ | ||||
|     public static function create(array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if (collection::init(static::$db->session, self::COLLECTION)) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 // Запись аккаунта в базу данных | ||||
|                 $_id = document::write(static::$db->session, self::COLLECTION); | ||||
|  | ||||
|                 if ($account = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                             FOR d IN %s | ||||
|                             FILTER d._id == '$_id' | ||||
|                             RETURN d | ||||
|                         AQL, | ||||
|                     self::COLLECTION | ||||
|                 ))) { | ||||
|                     // Найден созданный аккаунт | ||||
|  | ||||
|                     return $account; | ||||
|                 } else throw new exception('Не удалось создать аккаунт'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Связь аккаунта с аккаунтом ВКонтакте | ||||
|      * | ||||
|      * @param _document $account Инстанция аккаунта | ||||
|      * @param _document $vk Инстанция аккаунта ВКонтакте | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return bool Статус выполнения | ||||
|      */ | ||||
|     public static function connect(_document $account, _document $vk, array &$errors = []): bool | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, vk::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализированы коллекции | ||||
|  | ||||
|                 if (document::write(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, [ | ||||
|                     '_from' => $account->getId(), | ||||
|                     '_to' => $vk->getId() | ||||
|                 ])) { | ||||
|                     // Создано ребро: account -> vk | ||||
|  | ||||
|                     return true; | ||||
|                 } else throw new exception('Не удалось создать ребро: account -> vk'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Поиск связанного аккаунта ВКонтакте | ||||
|      * | ||||
|      * @param _document $account Инстанция аккаунта | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция аккаунта, если удалось найти | ||||
|      */ | ||||
|     public static function vk(_document $account, array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, vk::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . vk::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 if ($vk = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                         FOR document IN %s | ||||
|                         LET edge = ( | ||||
|                             FOR edge IN %s | ||||
|                             FILTER edge._from == '%s' | ||||
|                             SORT edge._key DESC | ||||
|                             LIMIT 1 | ||||
|                             RETURN edge | ||||
|                         ) | ||||
|                         FILTER document._id == edge[0]._to | ||||
|                         LIMIT 1 | ||||
|                         RETURN document | ||||
|                     AQL, | ||||
|                     vk::COLLECTION, | ||||
|                     self::COLLECTION . '_edge_' . vk::COLLECTION, | ||||
|                     $account->getId() | ||||
|                 ))) { | ||||
|                     // Найден аккаунт ВКонтакте | ||||
|  | ||||
|                     return $vk; | ||||
|                 } else throw new exception('Не удалось найти аккаунт ВКонтакте'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,143 +1,143 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| use mirzaev\minimal\model; | ||||
|  | ||||
| use mirzaev\arangodb\connection; | ||||
|  | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Ядро моделей | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| class core extends model | ||||
| { | ||||
|     /** | ||||
|      * Коллекция в которой хранятся аккаунты | ||||
|      */ | ||||
|     public const SETTINGS = '../settings/arangodb.php'; | ||||
|  | ||||
|     /** | ||||
|      * Соединение с базой данных | ||||
|      */ | ||||
|     protected static connection $db; | ||||
|  | ||||
|     public function __construct(connection $db = null) | ||||
|     { | ||||
|         if (isset($db)) { | ||||
|             // Получена инстанция соединения с базой данных | ||||
|  | ||||
|             // Запись и инициализация соединения с базой данных | ||||
|             $this->__set('db', $db); | ||||
|         } else { | ||||
|             // Не получена инстанция соединения с базой данных | ||||
|  | ||||
|             // Инициализация соединения с базой данных по умолчанию | ||||
|             $this->__get('db'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Записать свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * @param mixed $value Значение | ||||
|      */ | ||||
|     public function __set(string $name, mixed $value = null): void | ||||
|     { | ||||
|         match ($name) { | ||||
|             'db' => (function () use ($value) { | ||||
|                 if ($this->__isset('db')) { | ||||
|                     // Свойство уже было инициализировано | ||||
|  | ||||
|                     // Выброс исключения (неудача) | ||||
|                     throw new exception('Запрещено реинициализировать соединение с базой данных ($this->db)', 500); | ||||
|                 } else { | ||||
|                     // Свойство ещё не было инициализировано | ||||
|  | ||||
|                     if ($value instanceof connection) { | ||||
|                         // Передано подходящее значение | ||||
|  | ||||
|                         // Запись свойства (успех) | ||||
|                         self::$db = $value; | ||||
|                     } else { | ||||
|                         // Передано неподходящее значение | ||||
|  | ||||
|                         // Выброс исключения (неудача) | ||||
|                         throw new exception('Соединение с базой данных ($this->db) должен быть инстанцией mirzaev\arangodb\connection', 500); | ||||
|                     } | ||||
|                 } | ||||
|             })(), | ||||
|             default => parent::__set($name, $value) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Прочитать свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * | ||||
|      * @return mixed Содержимое | ||||
|      */ | ||||
|     public function __get(string $name): mixed | ||||
|     { | ||||
|         return match ($name) { | ||||
|             'db' => (function () { | ||||
|                 if (!$this->__isset('db')) { | ||||
|                     // Свойство не инициализировано | ||||
|  | ||||
|                     // Инициализация значения по умолчанию исходя из настроек | ||||
|                     $this->__set('db', new connection(require static::SETTINGS)); | ||||
|                 } | ||||
|  | ||||
|                 return self::$db; | ||||
|             })(), | ||||
|             default => parent::__get($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Проверить свойство на инициализированность | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      */ | ||||
|     public function __isset(string $name): bool | ||||
|     { | ||||
|         return match ($name) { | ||||
|             default => parent::__isset($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Удалить свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      */ | ||||
|     public function __unset(string $name): void | ||||
|     { | ||||
|         match ($name) { | ||||
|             default => parent::__isset($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Статический вызов | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * @param array $arguments Параметры | ||||
|      */ | ||||
|     public static function __callStatic(string $name, array $arguments): mixed | ||||
|     { | ||||
|         match ($name) { | ||||
|             'db' => (new static)->__get('db'), | ||||
|             default => throw new exception("Не найдено свойство или функция: $name", 500) | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| use mirzaev\minimal\model; | ||||
|  | ||||
| use mirzaev\arangodb\connection; | ||||
|  | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Ядро моделей | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| class core extends model | ||||
| { | ||||
|     /** | ||||
|      * Коллекция в которой хранятся аккаунты | ||||
|      */ | ||||
|     public const SETTINGS = '../settings/arangodb.php'; | ||||
|  | ||||
|     /** | ||||
|      * Соединение с базой данных | ||||
|      */ | ||||
|     protected static connection $db; | ||||
|  | ||||
|     public function __construct(connection $db = null) | ||||
|     { | ||||
|         if (isset($db)) { | ||||
|             // Получена инстанция соединения с базой данных | ||||
|  | ||||
|             // Запись и инициализация соединения с базой данных | ||||
|             $this->__set('db', $db); | ||||
|         } else { | ||||
|             // Не получена инстанция соединения с базой данных | ||||
|  | ||||
|             // Инициализация соединения с базой данных по умолчанию | ||||
|             $this->__get('db'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Записать свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * @param mixed $value Значение | ||||
|      */ | ||||
|     public function __set(string $name, mixed $value = null): void | ||||
|     { | ||||
|         match ($name) { | ||||
|             'db' => (function () use ($value) { | ||||
|                 if ($this->__isset('db')) { | ||||
|                     // Свойство уже было инициализировано | ||||
|  | ||||
|                     // Выброс исключения (неудача) | ||||
|                     throw new exception('Запрещено реинициализировать соединение с базой данных ($this->db)', 500); | ||||
|                 } else { | ||||
|                     // Свойство ещё не было инициализировано | ||||
|  | ||||
|                     if ($value instanceof connection) { | ||||
|                         // Передано подходящее значение | ||||
|  | ||||
|                         // Запись свойства (успех) | ||||
|                         self::$db = $value; | ||||
|                     } else { | ||||
|                         // Передано неподходящее значение | ||||
|  | ||||
|                         // Выброс исключения (неудача) | ||||
|                         throw new exception('Соединение с базой данных ($this->db) должен быть инстанцией mirzaev\arangodb\connection', 500); | ||||
|                     } | ||||
|                 } | ||||
|             })(), | ||||
|             default => parent::__set($name, $value) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Прочитать свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * | ||||
|      * @return mixed Содержимое | ||||
|      */ | ||||
|     public function __get(string $name): mixed | ||||
|     { | ||||
|         return match ($name) { | ||||
|             'db' => (function () { | ||||
|                 if (!$this->__isset('db')) { | ||||
|                     // Свойство не инициализировано | ||||
|  | ||||
|                     // Инициализация значения по умолчанию исходя из настроек | ||||
|                     $this->__set('db', new connection(require static::SETTINGS)); | ||||
|                 } | ||||
|  | ||||
|                 return self::$db; | ||||
|             })(), | ||||
|             default => parent::__get($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Проверить свойство на инициализированность | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      */ | ||||
|     public function __isset(string $name): bool | ||||
|     { | ||||
|         return match ($name) { | ||||
|             default => parent::__isset($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Удалить свойство | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      */ | ||||
|     public function __unset(string $name): void | ||||
|     { | ||||
|         match ($name) { | ||||
|             default => parent::__isset($name) | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Статический вызов | ||||
|      * | ||||
|      * @param string $name Название | ||||
|      * @param array $arguments Параметры | ||||
|      */ | ||||
|     public static function __callStatic(string $name, array $arguments): mixed | ||||
|     { | ||||
|         match ($name) { | ||||
|             'db' => (new static)->__get('db'), | ||||
|             default => throw new exception("Не найдено свойство или функция: $name", 500) | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,213 +1,249 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\models\account_model as account; | ||||
|  | ||||
| // Фреймворк ArangoDB | ||||
| use mirzaev\arangodb\collection, | ||||
|     mirzaev\arangodb\document; | ||||
|  | ||||
| // Библиотека для ArangoDB | ||||
| use ArangoDBClient\Document as _document; | ||||
|  | ||||
| // Встроенные библиотеки | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Модель сессий | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class session_model extends core | ||||
| { | ||||
|     /** | ||||
|      * Коллекция | ||||
|      */ | ||||
|     public const COLLECTION = 'session'; | ||||
|  | ||||
|     /** | ||||
|      * Инициализация | ||||
|      * | ||||
|      * @param ?string $hash Хеш сессии в базе данных | ||||
|      * @param ?int $expires Дата окончания работы сессии (используется при создании новой сессии) | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция сессии, если удалось найти или создать | ||||
|      */ | ||||
|     public static function initialization(?string $hash = null, ?int $expires = null, array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if (collection::init(static::$db->session, self::COLLECTION)) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 if (isset($hash) && $session = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                         FOR d IN %s | ||||
|                         FILTER d.hash == '$hash' && d.expires > %d | ||||
|                         RETURN d | ||||
|                     AQL, | ||||
|                     self::COLLECTION, | ||||
|                     time() | ||||
|                 ))) { | ||||
|                     // Найдена сессия по хешу | ||||
|  | ||||
|                     // Возврат сессии | ||||
|                     return $session; | ||||
|                 } else if ($session = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                         FOR d IN %s | ||||
|                         FILTER d.ip == '%s' && d.expires > %d | ||||
|                         RETURN d | ||||
|                     AQL, | ||||
|                     self::COLLECTION, | ||||
|                     $_SERVER['REMOTE_ADDR'], | ||||
|                     time() | ||||
|                 ))) { | ||||
|                     // Найдена сессия по данным пользователя | ||||
|  | ||||
|                     // Возврат сессии | ||||
|                     return $session; | ||||
|                 } else { | ||||
|                     // Не найдена сессия | ||||
|  | ||||
|                     // Запись сессии в базу данных | ||||
|                     $_id = document::write(static::$db->session, self::COLLECTION, [ | ||||
|                         'ip' => $_SERVER['REMOTE_ADDR'], | ||||
|                         'expires' => $expires ?? time() + 604800 | ||||
|                     ]); | ||||
|  | ||||
|                     if ($session = collection::search(static::$db->session, sprintf( | ||||
|                         <<<AQL | ||||
|                             FOR d IN %s | ||||
|                             FILTER d._id == '$_id' && d.expires > %d | ||||
|                             RETURN d | ||||
|                         AQL, | ||||
|                         self::COLLECTION, | ||||
|                         time() | ||||
|                     ))) { | ||||
|                         // Найдена созданная сессия | ||||
|  | ||||
|                         // Запись хеша | ||||
|                         $session->hash = sodium_bin2hex(sodium_crypto_generichash($_id)); | ||||
|  | ||||
|                         if (document::update(static::$db->session, $session)) { | ||||
|                             // Записано обновление | ||||
|  | ||||
|                             return $session; | ||||
|                         } else throw new exception('Не удалось записать данные сессии'); | ||||
|                     } else throw new exception('Не удалось создать или найти созданную сессию'); | ||||
|                 } | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Связь сессии с аккаунтом | ||||
|      * | ||||
|      * @param _document $session Инстанция сессии | ||||
|      * @param _document $account Инстанция аккаунта | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return bool Статус выполнения | ||||
|      */ | ||||
|     public static function connect(_document $session, _document $account, array &$errors = []): bool | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, account::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализирована коллекция | ||||
|  | ||||
|                 if (document::write(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, [ | ||||
|                     '_from' => $session->getId(), | ||||
|                     '_to' => $account->getId() | ||||
|                 ])) { | ||||
|                     // Создано ребро: 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 _document $session Инстанция сессии | ||||
|      * @param array &$errors Журнал ошибок | ||||
|      * | ||||
|      * @return ?_document Инстанция аккаунта, если удалось найти | ||||
|      */ | ||||
|     public static function account(_document $session, array &$errors = []): ?_document | ||||
|     { | ||||
|         try { | ||||
|             if ( | ||||
|                 collection::init(static::$db->session, self::COLLECTION) | ||||
|                 && collection::init(static::$db->session, account::COLLECTION) | ||||
|                 && collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true) | ||||
|             ) { | ||||
|                 // Инициализированы коллекции | ||||
|  | ||||
|                 if ($account = collection::search(static::$db->session, sprintf( | ||||
|                     <<<AQL | ||||
|                         FOR document IN %s | ||||
|                         LET edge = ( | ||||
|                             FOR edge IN %s | ||||
|                             FILTER edge._from == '%s' | ||||
|                             SORT edge._key DESC | ||||
|                             LIMIT 1 | ||||
|                             RETURN edge | ||||
|                         ) | ||||
|                         FILTER document._id == edge[0]._to | ||||
|                         LIMIT 1 | ||||
|                         RETURN document | ||||
|                     AQL, | ||||
|                     account::COLLECTION, | ||||
|                     self::COLLECTION . '_edge_' . account::COLLECTION, | ||||
|                     $session->getId() | ||||
|                 ))) { | ||||
|                     // Найден аккаунт | ||||
|  | ||||
|                     return $account; | ||||
|                 } else throw new exception('Не удалось найти аккаунт'); | ||||
|             } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|         } catch (exception $e) { | ||||
|             // Запись в журнал ошибок | ||||
|             $errors[] = [ | ||||
|                 'text' => $e->getMessage(), | ||||
|                 'file' => $e->getFile(), | ||||
|                 'line' => $e->getLine(), | ||||
|                 'stack' => $e->getTrace() | ||||
|             ]; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\models; | ||||
|  | ||||
| // Файлы проекта | ||||
| use mirzaev\site\account\models\account_model as account; | ||||
|  | ||||
| // Фреймворк ArangoDB | ||||
| use mirzaev\arangodb\collection, | ||||
|   mirzaev\arangodb\document; | ||||
|  | ||||
| // Библиотека для ArangoDB | ||||
| use ArangoDBClient\Document as _document; | ||||
|  | ||||
| // Встроенные библиотеки | ||||
| use exception; | ||||
|  | ||||
| /** | ||||
|  * Модель сессий | ||||
|  * | ||||
|  * @package mirzaev\site\account\models | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class session_model extends core | ||||
| { | ||||
|   /** | ||||
|    * Коллекция | ||||
|    */ | ||||
|   public const COLLECTION = 'session'; | ||||
|  | ||||
|   /** | ||||
|    * Данные сессии из базы данных  | ||||
|    */ | ||||
|   public _document $document; | ||||
|  | ||||
|   /** | ||||
|    * Конструктор  | ||||
|    * | ||||
|    * Инициализация сессии и запись в свойство $this->document | ||||
|    * | ||||
|    * @param ?string $hash Хеш сессии в базе данных | ||||
|    * @param ?int $expires Дата окончания работы сессии (используется при создании новой сессии) | ||||
|    * @param array &$errors Журнал ошибок | ||||
|    * | ||||
|    * @return static Инстанция сессии | ||||
|    */ | ||||
|   public function __construct(?string $hash = null, ?int $expires = null, array &$errors = []) | ||||
|   { | ||||
|     try { | ||||
|       if (collection::init(static::$db->session, self::COLLECTION)) { | ||||
|         // Инициализирована коллекция | ||||
|  | ||||
|         if (isset($hash) && $session = collection::search(static::$db->session, sprintf( | ||||
|           <<<AQL | ||||
|             FOR d IN %s | ||||
|             FILTER d.hash == '$hash' && d.expires > %d | ||||
|             RETURN d | ||||
|           AQL, | ||||
|           self::COLLECTION, | ||||
|           time() | ||||
|         ))) { | ||||
|           // Найдена сессия по хешу | ||||
|  | ||||
|           // Запись в свойство | ||||
|           $this->document = $session; | ||||
|         } else if ($session = collection::search(static::$db->session, sprintf( | ||||
|           <<<AQL | ||||
|             FOR d IN %s | ||||
|             FILTER d.ip == '%s' && d.expires > %d | ||||
|             RETURN d | ||||
|           AQL, | ||||
|           self::COLLECTION, | ||||
|           $_SERVER['REMOTE_ADDR'], | ||||
|           time() | ||||
|         ))) { | ||||
|           // Найдена сессия по данным пользователя | ||||
|  | ||||
|           // Запись в свойство | ||||
|           $this->document = $session; | ||||
|         } else { | ||||
|           // Не найдена сессия | ||||
|  | ||||
|           // Запись сессии в базу данных | ||||
|           $_id = document::write(static::$db->session, self::COLLECTION, [ | ||||
|             'ip' => $_SERVER['REMOTE_ADDR'], | ||||
|             'expires' => $expires ?? time() + 604800 | ||||
|           ]); | ||||
|  | ||||
|           if ($session = collection::search(static::$db->session, sprintf( | ||||
|             <<<AQL | ||||
|               FOR d IN %s | ||||
|               FILTER d._id == '$_id' && d.expires > %d | ||||
|               RETURN d | ||||
|             AQL, | ||||
|             self::COLLECTION, | ||||
|             time() | ||||
|           ))) { | ||||
|             // Найдена созданная сессия | ||||
|  | ||||
|             // Запись хеша | ||||
|             $session->hash = sodium_bin2hex(sodium_crypto_generichash($_id)); | ||||
|  | ||||
|             if (document::update(static::$db->session, $session)) { | ||||
|               // Записано обновление | ||||
|  | ||||
|               // Запись в свойство | ||||
|               $this->document = $session; | ||||
|             } else throw new exception('Не удалось записать данные сессии'); | ||||
|           } else throw new exception('Не удалось создать или найти созданную сессию'); | ||||
|         } | ||||
|       } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|     } catch (exception $e) { | ||||
|       // Запись в журнал ошибок | ||||
|       $errors[] = [ | ||||
|         'text' => $e->getMessage(), | ||||
|         'file' => $e->getFile(), | ||||
|         'line' => $e->getLine(), | ||||
|         'stack' => $e->getTrace() | ||||
|       ]; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Связь сессии с аккаунтом | ||||
|    * | ||||
|    * @param _document $account Инстанция аккаунта | ||||
|    * @param array &$errors Журнал ошибок | ||||
|    * | ||||
|    * @return bool Статус выполнения | ||||
|    */ | ||||
|   public function connect(_document $account, array &$errors = []): bool | ||||
|   { | ||||
|     try { | ||||
|       if ( | ||||
|         collection::init(static::$db->session, self::COLLECTION) | ||||
|         && collection::init(static::$db->session, account::COLLECTION) | ||||
|         && collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true) | ||||
|       ) { | ||||
|         // Инициализирована коллекция | ||||
|  | ||||
|         if (document::write(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, [ | ||||
|           '_from' => $this->document->getId(), | ||||
|           '_to' => $account->getId() | ||||
|         ])) { | ||||
|           // Создано ребро: 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 ?_document Инстанция аккаунта, если удалось найти | ||||
|    */ | ||||
|   public function account(array &$errors = []): ?_document | ||||
|   { | ||||
|     try { | ||||
|       if ( | ||||
|         collection::init(static::$db->session, self::COLLECTION) | ||||
|         && collection::init(static::$db->session, account::COLLECTION) | ||||
|         && collection::init(static::$db->session, self::COLLECTION . '_edge_' . account::COLLECTION, true) | ||||
|       ) { | ||||
|         // Инициализированы коллекции | ||||
|  | ||||
|         if ($account = collection::search(static::$db->session, sprintf( | ||||
|           <<<AQL | ||||
|             FOR document IN %s | ||||
|             LET edge = ( | ||||
|               FOR edge IN %s | ||||
|               FILTER edge._from == '%s' | ||||
|               SORT edge._key DESC | ||||
|               LIMIT 1 | ||||
|               RETURN edge | ||||
|             ) | ||||
|             FILTER document._id == edge[0]._to | ||||
|             LIMIT 1 | ||||
|             RETURN document | ||||
|           AQL, | ||||
|           account::COLLECTION, | ||||
|           self::COLLECTION . '_edge_' . account::COLLECTION, | ||||
|           $this->document->getId() | ||||
|         ))) { | ||||
|           // Найден аккаунт | ||||
|  | ||||
|           return $account; | ||||
|         } else throw new exception('Не удалось найти аккаунт'); | ||||
|       } else throw new exception('Не удалось инициализировать коллекцию'); | ||||
|     } catch (exception $e) { | ||||
|       // Запись в журнал ошибок | ||||
|       $errors[] = [ | ||||
|         'text' => $e->getMessage(), | ||||
|         'file' => $e->getFile(), | ||||
|         'line' => $e->getLine(), | ||||
|         'stack' => $e->getTrace() | ||||
|       ]; | ||||
|     } | ||||
|  | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Записать | ||||
|    * | ||||
|    * Ищет свойство, если не находит, то ищет его в инстанции документа сессии из базы данных, | ||||
|    * затем записывает в него переданные данные. Инициализация новых свойств происходит в инстанции | ||||
|    * документа сессии из базы данных  | ||||
|    * | ||||
|    * @param string $name Название | ||||
|    * @param mixed $value Содержимое | ||||
|    * | ||||
|    * @return void | ||||
|    */ | ||||
|   public function __set(string $name, mixed $value = null): void | ||||
|   { | ||||
|     if (isset($this->{$name})) $this->{$name} = $value; | ||||
|     else $this->document->{$name} = $value; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Прочитать | ||||
|    * | ||||
|    * Ищет свойство, если не находит, то ищет его в инстанции документа сессии из базы данных | ||||
|    * | ||||
|    * @param string $name Название | ||||
|    * | ||||
|    * @return mixed Данные свойства инстанции сессии или инстанции документа сессии из базы данных | ||||
|    */ | ||||
|   public function __get(string $name): mixed | ||||
|   { | ||||
|     return $this->{$name} ?? $this->document->{$name}; | ||||
|   } | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,182 +1,182 @@ | ||||
| @keyframes glare { | ||||
|  | ||||
|     2%, | ||||
|     100% { | ||||
|         left   : 130%; | ||||
|         bottom : -200%; | ||||
|         width  : 120px; | ||||
|         opacity: 0.7; | ||||
|     } | ||||
| } | ||||
|  | ||||
| section#authentication { | ||||
|     z-index       : 1000; | ||||
|     width         : 400px; | ||||
|     position      : relative; | ||||
|     margin        : 25vh auto; | ||||
|     display       : flex; | ||||
|     flex-direction: column; | ||||
| } | ||||
|  | ||||
| section#authentication>section.header { | ||||
|     z-index           : 1000; | ||||
|     height            : 100px; | ||||
|     margin-left       : -50px; | ||||
|     padding           : 30px 0; | ||||
|     display           : flex; | ||||
|     clip-path         : url(#authentication-header-mask); | ||||
|     border-radius     : 3px 3px 0 0; | ||||
|     animation-duration: 120s; | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>img.avatar { | ||||
|     z-index           : 1500; | ||||
|     left              : 6px; | ||||
|     top               : 36px; | ||||
|     width             : 88px; | ||||
|     height            : 88px; | ||||
|     position          : absolute; | ||||
|     margin            : auto; | ||||
|     object-fit        : cover; | ||||
|     border-radius     : 100%; | ||||
|     cursor            : pointer; | ||||
|     image-rendering   : smooth; | ||||
|     box-shadow        : 0px 0px 12px 0px rgba(0, 0, 0, 0.5); | ||||
|     -webkit-box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.5); | ||||
|     -moz-box-shadow   : 0px 0px 12px 0px rgba(0, 0, 0, 0.5); | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>img.avatar:hover { | ||||
|     left              : 0; | ||||
|     top               : 30px; | ||||
|     width             : 100px; | ||||
|     height            : 100px; | ||||
|     box-shadow        : 0px 0px 8px 0px rgba(0, 0, 0, 0.3); | ||||
|     -webkit-box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.3); | ||||
|     -moz-box-shadow   : 0px 0px 8px 0px rgba(0, 0, 0, 0.3); | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>img.cover { | ||||
|     z-index        : -5000; | ||||
|     left           : -50px; | ||||
|     top            : 0; | ||||
|     position       : absolute; | ||||
|     width          : calc(100% + 100px); | ||||
|     height         : 100%; | ||||
|     object-position: 0px 30%; | ||||
|     object-fit     : cover; | ||||
|     clip-path      : polygon(50px 0, calc(100% - 50px) 0, calc(100% - 50px) 100%, 50px 100%); | ||||
|     border-radius  : 0 0 3px 3px; | ||||
|     background     : var(--background-above); | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>div.glare { | ||||
|     z-index                  : 3000; | ||||
|     left                     : -30px; | ||||
|     top                      : -300px; | ||||
|     width                    : 30px; | ||||
|     height                   : 400%; | ||||
|     position                 : absolute; | ||||
|     rotate                   : 25deg; | ||||
|     opacity                  : 0.2; | ||||
|     filter                   : unset; | ||||
|     pointer-events           : none; | ||||
|     animation-name           : glare; | ||||
|     animation-duration       : 32s; | ||||
|     animation-delay          : 2s; | ||||
|     animation-fill-mode      : forwards; | ||||
|     animation-timing-function: linear; | ||||
|     background-color         : #fff; | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>div { | ||||
|     animation-duration: 80s; | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>a { | ||||
|     margin        : auto; | ||||
|     width         : 100%; | ||||
|     margin-left   : 110px; | ||||
|     padding-bottom: 0.5ex; | ||||
|     white-space   : nowrap; | ||||
|     overflow-x    : hidden; | ||||
|     text-overflow : ellipsis; | ||||
|     font-size     : 1.3em; | ||||
|     font-weight   : bold; | ||||
|     color         : var(--text-inverse); | ||||
|     text-shadow   : 0 0 8px #00000080; | ||||
| } | ||||
|  | ||||
|  | ||||
| section#authentication>section.body { | ||||
|     margin-top           : -160px; | ||||
|     padding              : 180px 30px 20px 30px; | ||||
|     gap                  : 3ex; | ||||
|     display              : flex; | ||||
|     flex-direction       : column; | ||||
|     border-radius        : 3px; | ||||
|     background-color     : var(--background-above); | ||||
|     /* background-image  : radial-gradient(circle at 70% 20%, #000000A0 0%, var(--background-above) 75%); */ | ||||
|     box-shadow           : 0px 0px 8px 0px rgba(0, 0, 0, 0.2); | ||||
|     -webkit-box-shadow   : 0px 0px 8px 0px rgba(0, 0, 0, 0.2); | ||||
|     -moz-box-shadow      : 0px 0px 8px 0px rgba(0, 0, 0, 0.2); | ||||
| } | ||||
|  | ||||
| section#authentication>section.body>ul { | ||||
|     margin       : unset; | ||||
|     margin-left  : 10%; | ||||
|     margin-bottom: 1ex; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body ul ul { | ||||
|     padding-top: 1ex; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body ul li:not(:last-child) { | ||||
|     margin-bottom: 1ex; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons { | ||||
|     display: flex; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button { | ||||
|     padding         : 1ex 2ex; | ||||
|     cursor          : pointer; | ||||
|     border-radius   : 3px; | ||||
|     font-size       : 0.9em; | ||||
|     background-color: unset; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button:hover { | ||||
|     color: var(--text-hover); | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button:active { | ||||
|     color     : var(--text-active); | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button:first-of-type { | ||||
|     margin-left : auto; | ||||
|     margin-right: 5%; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button:last-of-type { | ||||
|     margin-right: auto; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button.accept { | ||||
|     padding         : 1ex 5ex; | ||||
|     color           : var(--text-inverse); | ||||
|     background-color: #63954d; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button.accept:hover { | ||||
|     color           : var(--text-inverse-above); | ||||
|     background-color: #6fa259; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button.accept:active { | ||||
|     background-color: #63954d; | ||||
| } | ||||
| @keyframes glare { | ||||
|  | ||||
|     2%, | ||||
|     100% { | ||||
|         left   : 130%; | ||||
|         bottom : -200%; | ||||
|         width  : 120px; | ||||
|         opacity: 0.7; | ||||
|     } | ||||
| } | ||||
|  | ||||
| section#authentication { | ||||
|     z-index       : 1000; | ||||
|     width         : 400px; | ||||
|     position      : relative; | ||||
|     margin        : 25vh auto; | ||||
|     display       : flex; | ||||
|     flex-direction: column; | ||||
| } | ||||
|  | ||||
| section#authentication>section.header { | ||||
|     z-index           : 1000; | ||||
|     height            : 100px; | ||||
|     margin-left       : -50px; | ||||
|     padding           : 30px 0; | ||||
|     display           : flex; | ||||
|     clip-path         : url(#authentication-header-mask); | ||||
|     border-radius     : 3px 3px 0 0; | ||||
|     animation-duration: 120s; | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>img.avatar { | ||||
|     z-index           : 1500; | ||||
|     left              : 6px; | ||||
|     top               : 36px; | ||||
|     width             : 88px; | ||||
|     height            : 88px; | ||||
|     position          : absolute; | ||||
|     margin            : auto; | ||||
|     object-fit        : cover; | ||||
|     border-radius     : 100%; | ||||
|     cursor            : pointer; | ||||
|     image-rendering   : smooth; | ||||
|     box-shadow        : 0px 0px 12px 0px rgba(0, 0, 0, 0.5); | ||||
|     -webkit-box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.5); | ||||
|     -moz-box-shadow   : 0px 0px 12px 0px rgba(0, 0, 0, 0.5); | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>img.avatar:hover { | ||||
|     left              : 0; | ||||
|     top               : 30px; | ||||
|     width             : 100px; | ||||
|     height            : 100px; | ||||
|     box-shadow        : 0px 0px 8px 0px rgba(0, 0, 0, 0.3); | ||||
|     -webkit-box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.3); | ||||
|     -moz-box-shadow   : 0px 0px 8px 0px rgba(0, 0, 0, 0.3); | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>img.cover { | ||||
|     z-index        : -5000; | ||||
|     left           : -50px; | ||||
|     top            : 0; | ||||
|     position       : absolute; | ||||
|     width          : calc(100% + 100px); | ||||
|     height         : 100%; | ||||
|     object-position: 0px 30%; | ||||
|     object-fit     : cover; | ||||
|     clip-path      : polygon(50px 0, calc(100% - 50px) 0, calc(100% - 50px) 100%, 50px 100%); | ||||
|     border-radius  : 0 0 3px 3px; | ||||
|     background     : var(--background-above); | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>div.glare { | ||||
|     z-index                  : 3000; | ||||
|     left                     : -30px; | ||||
|     top                      : -300px; | ||||
|     width                    : 30px; | ||||
|     height                   : 400%; | ||||
|     position                 : absolute; | ||||
|     rotate                   : 25deg; | ||||
|     opacity                  : 0.2; | ||||
|     filter                   : unset; | ||||
|     pointer-events           : none; | ||||
|     animation-name           : glare; | ||||
|     animation-duration       : 32s; | ||||
|     animation-delay          : 2s; | ||||
|     animation-fill-mode      : forwards; | ||||
|     animation-timing-function: linear; | ||||
|     background-color         : #fff; | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>div { | ||||
|     animation-duration: 80s; | ||||
| } | ||||
|  | ||||
| section#authentication>section.header>a { | ||||
|     margin        : auto; | ||||
|     width         : 100%; | ||||
|     margin-left   : 110px; | ||||
|     padding-bottom: 0.5ex; | ||||
|     white-space   : nowrap; | ||||
|     overflow-x    : hidden; | ||||
|     text-overflow : ellipsis; | ||||
|     font-size     : 1.3em; | ||||
|     font-weight   : bold; | ||||
|     color         : var(--text-inverse); | ||||
|     text-shadow   : 0 0 8px #00000080; | ||||
| } | ||||
|  | ||||
|  | ||||
| section#authentication>section.body { | ||||
|     margin-top           : -160px; | ||||
|     padding              : 180px 30px 20px 30px; | ||||
|     gap                  : 3ex; | ||||
|     display              : flex; | ||||
|     flex-direction       : column; | ||||
|     border-radius        : 3px; | ||||
|     background-color     : var(--background-above); | ||||
|     /* background-image  : radial-gradient(circle at 70% 20%, #000000A0 0%, var(--background-above) 75%); */ | ||||
|     box-shadow           : 0px 0px 8px 0px rgba(0, 0, 0, 0.2); | ||||
|     -webkit-box-shadow   : 0px 0px 8px 0px rgba(0, 0, 0, 0.2); | ||||
|     -moz-box-shadow      : 0px 0px 8px 0px rgba(0, 0, 0, 0.2); | ||||
| } | ||||
|  | ||||
| section#authentication>section.body>ul { | ||||
|     margin       : unset; | ||||
|     margin-left  : 10%; | ||||
|     margin-bottom: 1ex; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body ul ul { | ||||
|     padding-top: 1ex; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body ul li:not(:last-child) { | ||||
|     margin-bottom: 1ex; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons { | ||||
|     display: flex; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button { | ||||
|     padding         : 1ex 2ex; | ||||
|     cursor          : pointer; | ||||
|     border-radius   : 3px; | ||||
|     font-size       : 0.9em; | ||||
|     background-color: unset; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button:hover { | ||||
|     color: var(--text-hover); | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button:active { | ||||
|     color     : var(--text-active); | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button:first-of-type { | ||||
|     margin-left : auto; | ||||
|     margin-right: 5%; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button:last-of-type { | ||||
|     margin-right: auto; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button.accept { | ||||
|     padding         : 1ex 5ex; | ||||
|     color           : var(--text-inverse); | ||||
|     background-color: #63954d; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button.accept:hover { | ||||
|     color           : var(--text-inverse-above); | ||||
|     background-color: #6fa259; | ||||
| } | ||||
|  | ||||
| section#authentication>section.body div.buttons>button.accept:active { | ||||
|     background-color: #63954d; | ||||
| } | ||||
|   | ||||
| @@ -1,134 +1,134 @@ | ||||
| @keyframes red { | ||||
|     25% { | ||||
|         left: -240%; | ||||
|         top : -130%; | ||||
|     } | ||||
|  | ||||
|     50% { | ||||
|         left: -100%; | ||||
|         top : -120%; | ||||
|     } | ||||
|  | ||||
|     75% { | ||||
|         left: -160%; | ||||
|         top : -230%; | ||||
|     } | ||||
|  | ||||
|     100% { | ||||
|         left: -250%; | ||||
|         top : -300%; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes blue { | ||||
|     25% { | ||||
|         left: -180%; | ||||
|         top : -100%; | ||||
|     } | ||||
|  | ||||
|     50% { | ||||
|         left: -120%; | ||||
|         top : -250%; | ||||
|     } | ||||
|  | ||||
|     75% { | ||||
|         left: -250%; | ||||
|         top : -300%; | ||||
|     } | ||||
|  | ||||
|     100% { | ||||
|         left: -280%; | ||||
|         top : -80%; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes green { | ||||
|     25% { | ||||
|         left: -120%; | ||||
|         top : -250%; | ||||
|     } | ||||
|  | ||||
|     50% { | ||||
|         left: -250%; | ||||
|         top : -300%; | ||||
|     } | ||||
|  | ||||
|     75% { | ||||
|         left: -280%; | ||||
|         top : -80%; | ||||
|     } | ||||
|  | ||||
|     100% { | ||||
|         left: -180%; | ||||
|         top : -100%; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes wrap-background { | ||||
|     25% { | ||||
|         background-color: #9395ff; | ||||
|     } | ||||
|  | ||||
|     50% { | ||||
|         background-color: #fff393; | ||||
|     } | ||||
|  | ||||
|     75% { | ||||
|         background-color: #534eff; | ||||
|     } | ||||
|  | ||||
|     100% { | ||||
|         background-color: #ff5c5c; | ||||
|     } | ||||
| } | ||||
|  | ||||
| :is(div, section).gradient { | ||||
|     position                 : relative; | ||||
|     overflow                 : hidden; | ||||
|     animation-duration       : 30s; | ||||
|     animation-name           : wrap-background; | ||||
|     animation-iteration-count: infinite; | ||||
|     background-repeat        : no-repeat; | ||||
|     animation-timing-function: ease-in-out; | ||||
|     background-color         : #ff5c5c; | ||||
| } | ||||
|  | ||||
| :is(div, section).gradient>div:not(.gradient) { | ||||
|     z-index                  : -1000; | ||||
|     width                    : 500%; | ||||
|     height                   : 500%; | ||||
|     position                 : absolute; | ||||
|     pointer-events           : none; | ||||
|     filter                   : blur(200px); | ||||
|     animation-duration       : 12s; | ||||
|     background-repeat        : no-repeat; | ||||
|     animation-timing-function: ease-in-out; | ||||
|     animation-iteration-count: infinite; | ||||
| } | ||||
|  | ||||
| :is(div, section).gradient>div.red { | ||||
|     left          : -250%; | ||||
|     top           : -300%; | ||||
|     animation-name: red; | ||||
|     background-image: radial-gradient(circle, | ||||
|             rgba(255, 25, 25, 1) 0%, | ||||
|             rgba(0, 0, 0, 0) 35%); | ||||
| } | ||||
|  | ||||
| :is(div, section).gradient>div.blue { | ||||
|     left          : -280%; | ||||
|     top           : -80%; | ||||
|     animation-name: blue; | ||||
|     background-image: radial-gradient(circle, | ||||
|             rgba(25, 25, 255, 0.6) 0%, | ||||
|             rgba(0, 0, 0, 0) 35%); | ||||
| } | ||||
|  | ||||
| :is(div, section).gradient>div.green { | ||||
|     left          : -180%; | ||||
|     top           : -100%; | ||||
|     animation-name: green; | ||||
|     background-image: radial-gradient(circle, | ||||
|             rgba(25, 255, 25, 1) 0%, | ||||
|             rgba(0, 0, 0, 0) 35%); | ||||
| } | ||||
| @keyframes red { | ||||
|     25% { | ||||
|         left: -240%; | ||||
|         top : -130%; | ||||
|     } | ||||
|  | ||||
|     50% { | ||||
|         left: -100%; | ||||
|         top : -120%; | ||||
|     } | ||||
|  | ||||
|     75% { | ||||
|         left: -160%; | ||||
|         top : -230%; | ||||
|     } | ||||
|  | ||||
|     100% { | ||||
|         left: -250%; | ||||
|         top : -300%; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes blue { | ||||
|     25% { | ||||
|         left: -180%; | ||||
|         top : -100%; | ||||
|     } | ||||
|  | ||||
|     50% { | ||||
|         left: -120%; | ||||
|         top : -250%; | ||||
|     } | ||||
|  | ||||
|     75% { | ||||
|         left: -250%; | ||||
|         top : -300%; | ||||
|     } | ||||
|  | ||||
|     100% { | ||||
|         left: -280%; | ||||
|         top : -80%; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes green { | ||||
|     25% { | ||||
|         left: -120%; | ||||
|         top : -250%; | ||||
|     } | ||||
|  | ||||
|     50% { | ||||
|         left: -250%; | ||||
|         top : -300%; | ||||
|     } | ||||
|  | ||||
|     75% { | ||||
|         left: -280%; | ||||
|         top : -80%; | ||||
|     } | ||||
|  | ||||
|     100% { | ||||
|         left: -180%; | ||||
|         top : -100%; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes wrap-background { | ||||
|     25% { | ||||
|         background-color: #9395ff; | ||||
|     } | ||||
|  | ||||
|     50% { | ||||
|         background-color: #fff393; | ||||
|     } | ||||
|  | ||||
|     75% { | ||||
|         background-color: #534eff; | ||||
|     } | ||||
|  | ||||
|     100% { | ||||
|         background-color: #ff5c5c; | ||||
|     } | ||||
| } | ||||
|  | ||||
| :is(div, section).gradient { | ||||
|     position                 : relative; | ||||
|     overflow                 : hidden; | ||||
|     animation-duration       : 30s; | ||||
|     animation-name           : wrap-background; | ||||
|     animation-iteration-count: infinite; | ||||
|     background-repeat        : no-repeat; | ||||
|     animation-timing-function: ease-in-out; | ||||
|     background-color         : #ff5c5c; | ||||
| } | ||||
|  | ||||
| :is(div, section).gradient>div:not(.gradient) { | ||||
|     z-index                  : -1000; | ||||
|     width                    : 500%; | ||||
|     height                   : 500%; | ||||
|     position                 : absolute; | ||||
|     pointer-events           : none; | ||||
|     filter                   : blur(200px); | ||||
|     animation-duration       : 12s; | ||||
|     background-repeat        : no-repeat; | ||||
|     animation-timing-function: ease-in-out; | ||||
|     animation-iteration-count: infinite; | ||||
| } | ||||
|  | ||||
| :is(div, section).gradient>div.red { | ||||
|     left          : -250%; | ||||
|     top           : -300%; | ||||
|     animation-name: red; | ||||
|     background-image: radial-gradient(circle, | ||||
|             rgba(255, 25, 25, 1) 0%, | ||||
|             rgba(0, 0, 0, 0) 35%); | ||||
| } | ||||
|  | ||||
| :is(div, section).gradient>div.blue { | ||||
|     left          : -280%; | ||||
|     top           : -80%; | ||||
|     animation-name: blue; | ||||
|     background-image: radial-gradient(circle, | ||||
|             rgba(25, 25, 255, 0.6) 0%, | ||||
|             rgba(0, 0, 0, 0) 35%); | ||||
| } | ||||
|  | ||||
| :is(div, section).gradient>div.green { | ||||
|     left          : -180%; | ||||
|     top           : -100%; | ||||
|     animation-name: green; | ||||
|     background-image: radial-gradient(circle, | ||||
|             rgba(25, 255, 25, 1) 0%, | ||||
|             rgba(0, 0, 0, 0) 35%); | ||||
| } | ||||
|   | ||||
| @@ -1,245 +1,245 @@ | ||||
| @keyframes node-select { | ||||
|     from { | ||||
|         outline-offset: 0px; | ||||
|     } | ||||
|  | ||||
|     to { | ||||
|         outline       : 2px solid var(--grey-light); | ||||
|         outline-offset: 10px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes node-select-revert { | ||||
|     from { | ||||
|         outline       : 2px solid var(--grey-light); | ||||
|         outline-offset: 10px; | ||||
|     } | ||||
|  | ||||
|     to { | ||||
|         outline-offset: 0px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes description-select { | ||||
|     from { | ||||
|         outline-offset: 0px; | ||||
|     } | ||||
|  | ||||
|     to { | ||||
|         outline       : 2px solid var(--grey); | ||||
|         outline-offset: 10px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes description-select-revert { | ||||
|     from { | ||||
|         outline       : 2px solid var(--grey); | ||||
|         outline-offset: 10px; | ||||
|     } | ||||
|  | ||||
|     to { | ||||
|         outline-offset: 0px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| section.graph { | ||||
|     z-index   : 0; | ||||
|     width     : 100vw; | ||||
|     height    : 100vh; | ||||
|     position  : absolute; | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| section.graph * { | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| section.graph:active { | ||||
|     cursor: move; | ||||
| } | ||||
|  | ||||
| section.graph article.node { | ||||
|     z-index                  : 500; | ||||
|     position                 : absolute; | ||||
|     display                  : flex; | ||||
|     cursor                   : grab; | ||||
|     border-radius            : 100%; | ||||
|     background-color         : var(--node-background); | ||||
| } | ||||
|  | ||||
| section.graph article.node.animated { | ||||
|     animation-duration       : 0.1s; | ||||
|     animation-name           : node-select-revert; | ||||
|     animation-fill-mode      : forwards; | ||||
| } | ||||
|  | ||||
| section.graph article.node.animated:hover { | ||||
|     animation-duration       : 0.1s; | ||||
|     animation-name           : node-select; | ||||
|     animation-fill-mode      : forwards; | ||||
| } | ||||
|  | ||||
| section.graph article.node:active { | ||||
|     cursor          : grabbing; | ||||
|     background-color: var(--node-background-important); | ||||
| } | ||||
|  | ||||
| section.graph article.node>h4.title { | ||||
|     margin    : auto; | ||||
|     text-align: center; | ||||
|     cursor    : pointer; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description { | ||||
|     z-index         : 1000; | ||||
|     position        : absolute; | ||||
|     text-align      : justify; | ||||
|     text-align-last : center; | ||||
|     border-radius   : 100%; | ||||
|     overflow        : hidden; | ||||
|     background-color: var(--node-background-completed); | ||||
| } | ||||
|  | ||||
| /* section.graph article.node>div.description.animated { | ||||
|     animation-duration       : 0.1s; | ||||
|     animation-name           : description-select-revert; | ||||
|     animation-fill-mode      : forwards; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description.animated:hover { | ||||
|     animation-duration       : 0.1s; | ||||
|     animation-name           : description-select; | ||||
|     animation-fill-mode      : forwards; | ||||
| } */ | ||||
|  | ||||
| section.graph article.node * { | ||||
|     transition: 0.1s ease-in; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>span.wrapper { | ||||
|     width       : 50%; | ||||
|     height      : 100%; | ||||
|     shape-margin: 15px; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>span.left.wrapper { | ||||
|     float        : left; | ||||
|     shape-outside: polygon(100% 0%, 0% 0%, 0% 100%, 100% 100%, 68% 98%, 38% 90%, 10% 72%, 0% 50%, 10% 28%, 20% 20%, 100% 20%); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>span.right.wrapper { | ||||
|     float        : right; | ||||
|     shape-outside: polygon(0% 100%, 100% 100%, 100% 0%, 0% 0%, 0% 20%, 82% 20%, 90% 28%, 100% 50%, 90% 72%, 60% 90%, 32% 98%); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>p { | ||||
|     z-index   : 1500; | ||||
|     position  : relative; | ||||
|     margin    : 0; | ||||
|     opacity   : 0; | ||||
|     word-break: break-all; | ||||
|     color     : var(--text-inverse); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description:hover>p { | ||||
|     opacity: 1; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>a.source { | ||||
|     z-index    : 2000; | ||||
|     top        : calc(20% - 1.3em + (1em - 1.3ex)); | ||||
|     left       : 0; | ||||
|     position   : absolute; | ||||
|     font-weight: bold; | ||||
|     font-size  : 1.3em; | ||||
|     opacity    : 0; | ||||
| } | ||||
|  | ||||
| section.graph article.node.white>div.description>a.source { | ||||
|     color: var(--text-inverse); | ||||
| } | ||||
|  | ||||
| section.graph article.node.white>div.description>a.source:active { | ||||
|     color: var(--text-inverse-active); | ||||
| } | ||||
|  | ||||
| section.graph article.node.white>div.description>a.source:hover { | ||||
|     color: var(--text-inverse-hover); | ||||
| } | ||||
|  | ||||
| section.graph article.node.red>div.description>a.source { | ||||
|     color: var(--text-red); | ||||
| } | ||||
|  | ||||
| section.graph article.node.red>div.description>a.source:active { | ||||
|     color: var(--text-red-active); | ||||
| } | ||||
|  | ||||
| section.graph article.node.red>div.description>a.source:hover { | ||||
|     color: var(--text-red-hover); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>a.source.red:active { | ||||
|     color: var(--text-red-active); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description:hover>a.source { | ||||
|     opacity: 1; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>a.source:visited ::after { | ||||
|     content         : ''; | ||||
|     width           : 100%; | ||||
|     height          : 100%; | ||||
|     background-color: var(--node-background-completed); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>img.cover { | ||||
|     left          : 0; | ||||
|     top           : 0; | ||||
|     position      : absolute; | ||||
|     width         : 100%; | ||||
|     height        : 100%; | ||||
|     object-fit    : cover; | ||||
|     pointer-events: none; | ||||
|     filter        : unset; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description:hover>img.cover { | ||||
|     filter: blur(5px) brightness(0.5); | ||||
| } | ||||
|  | ||||
| section.graph article.node>i.close { | ||||
|     z-index   : -2000; | ||||
|     top       : -10%; | ||||
|     right     : -10%; | ||||
|     position  : absolute; | ||||
|     scale     : 0; | ||||
|     opacity   : 0; | ||||
|     cursor    : pointer; | ||||
|     color     : var(--text); | ||||
|     transition: 0.2s ease-out; | ||||
| } | ||||
|  | ||||
| section.graph article.node>i.close:hover { | ||||
|     scale     : 1.4 !important; | ||||
|     color     : var(--text-hover); | ||||
|     transition: 0.1s ease-in; | ||||
| } | ||||
|  | ||||
| section.graph article.node>i.close:active { | ||||
|     scale     : 1.2 !important; | ||||
|     color     : var(--text-active); | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| section.graph svg.connection { | ||||
|     z-index : -500; | ||||
|     position: absolute; | ||||
|     width   : 100%; | ||||
|     height  : 100%; | ||||
| } | ||||
|  | ||||
| section.graph svg.connection>line { | ||||
|     stroke: var(--connection); | ||||
| } | ||||
| @keyframes node-select { | ||||
|     from { | ||||
|         outline-offset: 0px; | ||||
|     } | ||||
|  | ||||
|     to { | ||||
|         outline       : 2px solid var(--grey-light); | ||||
|         outline-offset: 10px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes node-select-revert { | ||||
|     from { | ||||
|         outline       : 2px solid var(--grey-light); | ||||
|         outline-offset: 10px; | ||||
|     } | ||||
|  | ||||
|     to { | ||||
|         outline-offset: 0px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes description-select { | ||||
|     from { | ||||
|         outline-offset: 0px; | ||||
|     } | ||||
|  | ||||
|     to { | ||||
|         outline       : 2px solid var(--grey); | ||||
|         outline-offset: 10px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes description-select-revert { | ||||
|     from { | ||||
|         outline       : 2px solid var(--grey); | ||||
|         outline-offset: 10px; | ||||
|     } | ||||
|  | ||||
|     to { | ||||
|         outline-offset: 0px; | ||||
|     } | ||||
| } | ||||
|  | ||||
| section.graph { | ||||
|     z-index   : 0; | ||||
|     width     : 100vw; | ||||
|     height    : 100vh; | ||||
|     position  : absolute; | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| section.graph * { | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| section.graph:active { | ||||
|     cursor: move; | ||||
| } | ||||
|  | ||||
| section.graph article.node { | ||||
|     z-index                  : 500; | ||||
|     position                 : absolute; | ||||
|     display                  : flex; | ||||
|     cursor                   : grab; | ||||
|     border-radius            : 100%; | ||||
|     background-color         : var(--node-background); | ||||
| } | ||||
|  | ||||
| section.graph article.node.animated { | ||||
|     animation-duration       : 0.1s; | ||||
|     animation-name           : node-select-revert; | ||||
|     animation-fill-mode      : forwards; | ||||
| } | ||||
|  | ||||
| section.graph article.node.animated:hover { | ||||
|     animation-duration       : 0.1s; | ||||
|     animation-name           : node-select; | ||||
|     animation-fill-mode      : forwards; | ||||
| } | ||||
|  | ||||
| section.graph article.node:active { | ||||
|     cursor          : grabbing; | ||||
|     background-color: var(--node-background-important); | ||||
| } | ||||
|  | ||||
| section.graph article.node>h4.title { | ||||
|     margin    : auto; | ||||
|     text-align: center; | ||||
|     cursor    : pointer; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description { | ||||
|     z-index         : 1000; | ||||
|     position        : absolute; | ||||
|     text-align      : justify; | ||||
|     text-align-last : center; | ||||
|     border-radius   : 100%; | ||||
|     overflow        : hidden; | ||||
|     background-color: var(--node-background-completed); | ||||
| } | ||||
|  | ||||
| /* section.graph article.node>div.description.animated { | ||||
|     animation-duration       : 0.1s; | ||||
|     animation-name           : description-select-revert; | ||||
|     animation-fill-mode      : forwards; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description.animated:hover { | ||||
|     animation-duration       : 0.1s; | ||||
|     animation-name           : description-select; | ||||
|     animation-fill-mode      : forwards; | ||||
| } */ | ||||
|  | ||||
| section.graph article.node * { | ||||
|     transition: 0.1s ease-in; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>span.wrapper { | ||||
|     width       : 50%; | ||||
|     height      : 100%; | ||||
|     shape-margin: 15px; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>span.left.wrapper { | ||||
|     float        : left; | ||||
|     shape-outside: polygon(100% 0%, 0% 0%, 0% 100%, 100% 100%, 68% 98%, 38% 90%, 10% 72%, 0% 50%, 10% 28%, 20% 20%, 100% 20%); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>span.right.wrapper { | ||||
|     float        : right; | ||||
|     shape-outside: polygon(0% 100%, 100% 100%, 100% 0%, 0% 0%, 0% 20%, 82% 20%, 90% 28%, 100% 50%, 90% 72%, 60% 90%, 32% 98%); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>p { | ||||
|     z-index   : 1500; | ||||
|     position  : relative; | ||||
|     margin    : 0; | ||||
|     opacity   : 0; | ||||
|     word-break: break-all; | ||||
|     color     : var(--text-inverse); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description:hover>p { | ||||
|     opacity: 1; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>a.source { | ||||
|     z-index    : 2000; | ||||
|     top        : calc(20% - 1.3em + (1em - 1.3ex)); | ||||
|     left       : 0; | ||||
|     position   : absolute; | ||||
|     font-weight: bold; | ||||
|     font-size  : 1.3em; | ||||
|     opacity    : 0; | ||||
| } | ||||
|  | ||||
| section.graph article.node.white>div.description>a.source { | ||||
|     color: var(--text-inverse); | ||||
| } | ||||
|  | ||||
| section.graph article.node.white>div.description>a.source:active { | ||||
|     color: var(--text-inverse-active); | ||||
| } | ||||
|  | ||||
| section.graph article.node.white>div.description>a.source:hover { | ||||
|     color: var(--text-inverse-hover); | ||||
| } | ||||
|  | ||||
| section.graph article.node.red>div.description>a.source { | ||||
|     color: var(--text-red); | ||||
| } | ||||
|  | ||||
| section.graph article.node.red>div.description>a.source:active { | ||||
|     color: var(--text-red-active); | ||||
| } | ||||
|  | ||||
| section.graph article.node.red>div.description>a.source:hover { | ||||
|     color: var(--text-red-hover); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>a.source.red:active { | ||||
|     color: var(--text-red-active); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description:hover>a.source { | ||||
|     opacity: 1; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>a.source:visited ::after { | ||||
|     content         : ''; | ||||
|     width           : 100%; | ||||
|     height          : 100%; | ||||
|     background-color: var(--node-background-completed); | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description>img.cover { | ||||
|     left          : 0; | ||||
|     top           : 0; | ||||
|     position      : absolute; | ||||
|     width         : 100%; | ||||
|     height        : 100%; | ||||
|     object-fit    : cover; | ||||
|     pointer-events: none; | ||||
|     filter        : unset; | ||||
| } | ||||
|  | ||||
| section.graph article.node>div.description:hover>img.cover { | ||||
|     filter: blur(5px) brightness(0.5); | ||||
| } | ||||
|  | ||||
| section.graph article.node>i.close { | ||||
|     z-index   : -2000; | ||||
|     top       : -10%; | ||||
|     right     : -10%; | ||||
|     position  : absolute; | ||||
|     scale     : 0; | ||||
|     opacity   : 0; | ||||
|     cursor    : pointer; | ||||
|     color     : var(--text); | ||||
|     transition: 0.2s ease-out; | ||||
| } | ||||
|  | ||||
| section.graph article.node>i.close:hover { | ||||
|     scale     : 1.4 !important; | ||||
|     color     : var(--text-hover); | ||||
|     transition: 0.1s ease-in; | ||||
| } | ||||
|  | ||||
| section.graph article.node>i.close:active { | ||||
|     scale     : 1.2 !important; | ||||
|     color     : var(--text-active); | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| section.graph svg.connection { | ||||
|     z-index : -500; | ||||
|     position: absolute; | ||||
|     width   : 100%; | ||||
|     height  : 100%; | ||||
| } | ||||
|  | ||||
| section.graph svg.connection>line { | ||||
|     stroke: var(--connection); | ||||
| } | ||||
|   | ||||
| @@ -1,31 +1,31 @@ | ||||
| section.hotline { | ||||
|     display: inline-flex; | ||||
|     height : 100%; | ||||
| } | ||||
|  | ||||
| section.hotline * { | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| section.hotline:last-child { | ||||
|     margin-bottom: unset; | ||||
| } | ||||
|  | ||||
| section.hotline>article { | ||||
|     margin-right    : 18px; | ||||
|     width           : 140px; | ||||
|     height          : 190px; | ||||
|     display         : flex; | ||||
|     align-self      : flex-end; | ||||
|     border-radius   : 3px; | ||||
|     background-color: var(--background-light-1); | ||||
|     box-shadow      : 0px -6px 6px rgba(0, 0, 0, 0.3); | ||||
| } | ||||
|  | ||||
| section.hotline>article:last-child { | ||||
|     margin-right: unset; | ||||
| } | ||||
|  | ||||
| section.hotline>article>* { | ||||
|     margin: auto; | ||||
| } | ||||
| section.hotline { | ||||
|     display: inline-flex; | ||||
|     height : 100%; | ||||
| } | ||||
|  | ||||
| section.hotline * { | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| section.hotline:last-child { | ||||
|     margin-bottom: unset; | ||||
| } | ||||
|  | ||||
| section.hotline>article { | ||||
|     margin-right    : 18px; | ||||
|     width           : 140px; | ||||
|     height          : 190px; | ||||
|     display         : flex; | ||||
|     align-self      : flex-end; | ||||
|     border-radius   : 3px; | ||||
|     background-color: var(--background-light-1); | ||||
|     box-shadow      : 0px -6px 6px rgba(0, 0, 0, 0.3); | ||||
| } | ||||
|  | ||||
| section.hotline>article:last-child { | ||||
|     margin-right: unset; | ||||
| } | ||||
|  | ||||
| section.hotline>article>* { | ||||
|     margin: auto; | ||||
| } | ||||
|   | ||||
| @@ -1,29 +1,29 @@ | ||||
| .icon.close { | ||||
|     box-sizing   : border-box; | ||||
|     position     : relative; | ||||
|     display      : block; | ||||
|     transform    : scale(var(--ggs, 1)); | ||||
|     width        : 22px; | ||||
|     height       : 22px; | ||||
|     border       : 2px solid transparent; | ||||
|     border-radius: 40px | ||||
| } | ||||
|  | ||||
| .icon.close::after, | ||||
| .icon.close::before { | ||||
|     content      : ""; | ||||
|     display      : block; | ||||
|     box-sizing   : border-box; | ||||
|     position     : absolute; | ||||
|     width        : 16px; | ||||
|     height       : 2px; | ||||
|     background   : currentColor; | ||||
|     transform    : rotate(45deg); | ||||
|     border-radius: 5px; | ||||
|     top          : 8px; | ||||
|     left         : 1px | ||||
| } | ||||
|  | ||||
| .icon.close::after { | ||||
|     transform: rotate(-45deg) | ||||
| } | ||||
| .icon.close { | ||||
|     box-sizing   : border-box; | ||||
|     position     : relative; | ||||
|     display      : block; | ||||
|     transform    : scale(var(--ggs, 1)); | ||||
|     width        : 22px; | ||||
|     height       : 22px; | ||||
|     border       : 2px solid transparent; | ||||
|     border-radius: 40px | ||||
| } | ||||
|  | ||||
| .icon.close::after, | ||||
| .icon.close::before { | ||||
|     content      : ""; | ||||
|     display      : block; | ||||
|     box-sizing   : border-box; | ||||
|     position     : absolute; | ||||
|     width        : 16px; | ||||
|     height       : 2px; | ||||
|     background   : currentColor; | ||||
|     transform    : rotate(45deg); | ||||
|     border-radius: 5px; | ||||
|     top          : 8px; | ||||
|     left         : 1px | ||||
| } | ||||
|  | ||||
| .icon.close::after { | ||||
|     transform: rotate(-45deg) | ||||
| } | ||||
|   | ||||
| @@ -1,225 +1,225 @@ | ||||
| @import url('/fonts/comissioner.ttf'); | ||||
|  | ||||
| @media (prefers-color-scheme: light) { | ||||
|     :root { | ||||
|         --background-above-1       : #fff; | ||||
|         --background-above         : #fff6f6; | ||||
|         --background               : #e8dada; | ||||
|         --background-below         : #d7c5c5; | ||||
|         --background-inverse       : #221e1e; | ||||
|         --background-inverse-dark  : #120f0f; | ||||
|         --node-background-important: #c3eac3; | ||||
|         --node-background-completed: #b0c0b0; | ||||
|         --node-background          : #bdb; | ||||
|         --connection               : #b2b7b2; | ||||
|         --connection-completed     : #d1d1d1; | ||||
|         --text                     : #151313; | ||||
|         --text-hover               : #463e3e; | ||||
|         --text-active              : #0e0e0e; | ||||
|         --text-inverse-above       : #fff; | ||||
|         --text-inverse             : #efefef; | ||||
|         --text-inverse-below       : #d0d0d0; | ||||
|         --text-red                 : #f8a2a2; | ||||
|         --text-red-hover           : #ffbcbc; | ||||
|         --text-red-active          : #e69191; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @media (prefers-color-scheme: dark) { | ||||
|     :root { | ||||
|         --background-above-1: #322d2d; | ||||
|         --background-above  : #2b2525; | ||||
|         --background        : #221e1e; | ||||
|         --background-below  : #121010; | ||||
|         --node-background   : #221e1e; | ||||
|         --text              : #e6e6e6; | ||||
|         --text-hover        : #fff; | ||||
|         --text-active       : #d0d0d0; | ||||
|         --text-inverse      : 'dark'; | ||||
|         --red-light-1       : #dc4343; | ||||
|         --red-light         : #bf3737; | ||||
|         --red               : #a43333; | ||||
|         --red-dark          : #8d2a2a; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes page-background-gradient { | ||||
|     25% { | ||||
|         left: -350%; | ||||
|         top : 0%; | ||||
|     } | ||||
|  | ||||
|     50% { | ||||
|         left: 0%; | ||||
|         top : 0%; | ||||
|     } | ||||
|  | ||||
|     75% { | ||||
|         left: 0%; | ||||
|         top : -350%; | ||||
|     } | ||||
|  | ||||
|     to { | ||||
|         left: -350%; | ||||
|         top : -350%; | ||||
|     } | ||||
| } | ||||
|  | ||||
| :root { | ||||
|     --link       : #3c76ff; | ||||
|     --link-hover : #6594ff; | ||||
|     --link-active: #3064dd; | ||||
| } | ||||
|  | ||||
| * { | ||||
|     text-decoration: none; | ||||
|     outline        : none; | ||||
|     border         : none; | ||||
|     color          : var(--text); | ||||
|     font-family    : 'Commissioner', sans-serif; | ||||
|     transition     : 0.1s ease-out; | ||||
| } | ||||
|  | ||||
| .unselectable { | ||||
|     -webkit-touch-callout: none; | ||||
|     -webkit-user-select  : none; | ||||
|     -khtml-user-select   : none; | ||||
|     -moz-user-select     : none; | ||||
|     -ms-user-select      : none; | ||||
|     user-select          : none; | ||||
| } | ||||
|  | ||||
| a { | ||||
|     color: var(--link); | ||||
| } | ||||
|  | ||||
| a:hover { | ||||
|     color: var(--link-hover); | ||||
| } | ||||
|  | ||||
| a:active { | ||||
|     color     : var(--link-active); | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| body { | ||||
|     height          : 100vh; | ||||
|     margin          : 0; | ||||
|     overflow        : hidden; | ||||
|     background-color: var(--background); | ||||
| } | ||||
|  | ||||
| body>div.background { | ||||
|     z-index                  : -50000; | ||||
|     left                     : -350%; | ||||
|     top                      : -350%; | ||||
|     width                    : 500%; | ||||
|     height                   : 500%; | ||||
|     position                 : absolute; | ||||
|     filter                   : blur(200px); | ||||
|     animation-duration       : 15s; | ||||
|     animation-name           : page-background-gradient; | ||||
|     animation-iteration-count: infinite; | ||||
|     background-repeat        : no-repeat; | ||||
|     animation-timing-function: linear; | ||||
|     background-image         : radial-gradient(circle, var(--background-above) 0%, rgba(0, 0, 0, 0) 100%); | ||||
| } | ||||
|  | ||||
| aside { | ||||
|     z-index    : 500; | ||||
|     grid-column: 1/ 4; | ||||
|     grid-row   : 2; | ||||
|     overflow   : hidden; | ||||
| } | ||||
|  | ||||
| header { | ||||
|     z-index       : 5000; | ||||
|     position      : absolute; | ||||
|     display       : flex; | ||||
|     flex-direction: column; | ||||
|     box-shadow    : 2px 0 5px rgba(0, 0, 0, 0.3); | ||||
| } | ||||
|  | ||||
| header>menu { | ||||
|     margin          : unset; | ||||
|     padding         : 20px; | ||||
|     display         : flex; | ||||
|     flex-direction  : column; | ||||
|     flex-grow       : 1; | ||||
|     background-color: var(--background-light-1); | ||||
| } | ||||
|  | ||||
| header>#account>button#login { | ||||
|     z-index: 1500; | ||||
| } | ||||
|  | ||||
| header>menu a { | ||||
|     margin-bottom: 8px; | ||||
|     display      : flex; | ||||
|     align-items  : center; | ||||
| } | ||||
|  | ||||
| header>menu a:last-child { | ||||
|     margin-bottom: unset; | ||||
| } | ||||
|  | ||||
| header>menu a svg { | ||||
|     margin-right: 8px; | ||||
|     height      : 1.2rem; | ||||
|     position    : relative; | ||||
| } | ||||
|  | ||||
| header>menu a:hover svg { | ||||
|     margin-left : -5px; | ||||
|     margin-right: 13px; | ||||
| } | ||||
|  | ||||
| header>menu a svg path { | ||||
|     fill: var(--text); | ||||
| } | ||||
|  | ||||
| header>section { | ||||
|     background-color: var(--background-light-1); | ||||
| } | ||||
|  | ||||
| header :is(button, a[type="button"]) { | ||||
|     width           : 100%; | ||||
|     height          : 40px; | ||||
|     display         : flex; | ||||
|     justify-content : center; | ||||
|     align-items     : center; | ||||
|     cursor          : pointer; | ||||
|     background-color: var(--red); | ||||
|     transition      : unset; | ||||
| } | ||||
|  | ||||
| header button { | ||||
|     font-weight   : bold; | ||||
|     text-transform: uppercase; | ||||
| } | ||||
|  | ||||
| header :is(button, a[type="button"]):hover { | ||||
|     background-color: var(--red-light); | ||||
| } | ||||
|  | ||||
| header :is(button, a[type="button"]):active { | ||||
|     background-color: var(--red-dark); | ||||
| } | ||||
|  | ||||
| header>nav { | ||||
|     margin-top    : auto; | ||||
|     display       : flex; | ||||
|     flex-direction: column; | ||||
| } | ||||
|  | ||||
| main { | ||||
|     z-index       : 1000; | ||||
|     height        : 100%; | ||||
|     display       : flex; | ||||
|     flex-direction: column; | ||||
| } | ||||
|  | ||||
| footer { | ||||
|     z-index : 3000; | ||||
|     position: absolute; | ||||
| } | ||||
| @import url('/fonts/commissioner.ttf'); | ||||
|  | ||||
| @media (prefers-color-scheme: light) { | ||||
|     :root { | ||||
|         --background-above-1       : #fff; | ||||
|         --background-above         : #fff6f6; | ||||
|         --background               : #e8dada; | ||||
|         --background-below         : #d7c5c5; | ||||
|         --background-inverse       : #221e1e; | ||||
|         --background-inverse-dark  : #120f0f; | ||||
|         --node-background-important: #c3eac3; | ||||
|         --node-background-completed: #b0c0b0; | ||||
|         --node-background          : #bdb; | ||||
|         --connection               : #b2b7b2; | ||||
|         --connection-completed     : #d1d1d1; | ||||
|         --text                     : #151313; | ||||
|         --text-hover               : #463e3e; | ||||
|         --text-active              : #0e0e0e; | ||||
|         --text-inverse-above       : #fff; | ||||
|         --text-inverse             : #efefef; | ||||
|         --text-inverse-below       : #d0d0d0; | ||||
|         --text-red                 : #f8a2a2; | ||||
|         --text-red-hover           : #ffbcbc; | ||||
|         --text-red-active          : #e69191; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @media (prefers-color-scheme: dark) { | ||||
|     :root { | ||||
|         --background-above-1: #322d2d; | ||||
|         --background-above  : #2b2525; | ||||
|         --background        : #221e1e; | ||||
|         --background-below  : #121010; | ||||
|         --node-background   : #221e1e; | ||||
|         --text              : #e6e6e6; | ||||
|         --text-hover        : #fff; | ||||
|         --text-active       : #d0d0d0; | ||||
|         --text-inverse      : 'dark'; | ||||
|         --red-light-1       : #dc4343; | ||||
|         --red-light         : #bf3737; | ||||
|         --red               : #a43333; | ||||
|         --red-dark          : #8d2a2a; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @keyframes page-background-gradient { | ||||
|     25% { | ||||
|         left: -350%; | ||||
|         top : 0%; | ||||
|     } | ||||
|  | ||||
|     50% { | ||||
|         left: 0%; | ||||
|         top : 0%; | ||||
|     } | ||||
|  | ||||
|     75% { | ||||
|         left: 0%; | ||||
|         top : -350%; | ||||
|     } | ||||
|  | ||||
|     to { | ||||
|         left: -350%; | ||||
|         top : -350%; | ||||
|     } | ||||
| } | ||||
|  | ||||
| :root { | ||||
|     --link       : #3c76ff; | ||||
|     --link-hover : #6594ff; | ||||
|     --link-active: #3064dd; | ||||
| } | ||||
|  | ||||
| * { | ||||
|     text-decoration: none; | ||||
|     outline        : none; | ||||
|     border         : none; | ||||
|     color          : var(--text); | ||||
|     font-family    : 'Commissioner', sans-serif; | ||||
|     transition     : 0.1s ease-out; | ||||
| } | ||||
|  | ||||
| .unselectable { | ||||
|     -webkit-touch-callout: none; | ||||
|     -webkit-user-select  : none; | ||||
|     -khtml-user-select   : none; | ||||
|     -moz-user-select     : none; | ||||
|     -ms-user-select      : none; | ||||
|     user-select          : none; | ||||
| } | ||||
|  | ||||
| a { | ||||
|     color: var(--link); | ||||
| } | ||||
|  | ||||
| a:hover { | ||||
|     color: var(--link-hover); | ||||
| } | ||||
|  | ||||
| a:active { | ||||
|     color     : var(--link-active); | ||||
|     transition: unset; | ||||
| } | ||||
|  | ||||
| body { | ||||
|     height          : 100vh; | ||||
|     margin          : 0; | ||||
|     overflow        : hidden; | ||||
|     background-color: var(--background); | ||||
| } | ||||
|  | ||||
| body>div.background { | ||||
|     z-index                  : -50000; | ||||
|     left                     : -350%; | ||||
|     top                      : -350%; | ||||
|     width                    : 500%; | ||||
|     height                   : 500%; | ||||
|     position                 : absolute; | ||||
|     filter                   : blur(200px); | ||||
|     animation-duration       : 15s; | ||||
|     animation-name           : page-background-gradient; | ||||
|     animation-iteration-count: infinite; | ||||
|     background-repeat        : no-repeat; | ||||
|     animation-timing-function: linear; | ||||
|     background-image         : radial-gradient(circle, var(--background-above) 0%, rgba(0, 0, 0, 0) 100%); | ||||
| } | ||||
|  | ||||
| aside { | ||||
|     z-index    : 500; | ||||
|     grid-column: 1/ 4; | ||||
|     grid-row   : 2; | ||||
|     overflow   : hidden; | ||||
| } | ||||
|  | ||||
| header { | ||||
|     z-index       : 5000; | ||||
|     position      : absolute; | ||||
|     display       : flex; | ||||
|     flex-direction: column; | ||||
|     box-shadow    : 2px 0 5px rgba(0, 0, 0, 0.3); | ||||
| } | ||||
|  | ||||
| header>menu { | ||||
|     margin          : unset; | ||||
|     padding         : 20px; | ||||
|     display         : flex; | ||||
|     flex-direction  : column; | ||||
|     flex-grow       : 1; | ||||
|     background-color: var(--background-light-1); | ||||
| } | ||||
|  | ||||
| header>#account>button#login { | ||||
|     z-index: 1500; | ||||
| } | ||||
|  | ||||
| header>menu a { | ||||
|     margin-bottom: 8px; | ||||
|     display      : flex; | ||||
|     align-items  : center; | ||||
| } | ||||
|  | ||||
| header>menu a:last-child { | ||||
|     margin-bottom: unset; | ||||
| } | ||||
|  | ||||
| header>menu a svg { | ||||
|     margin-right: 8px; | ||||
|     height      : 1.2rem; | ||||
|     position    : relative; | ||||
| } | ||||
|  | ||||
| header>menu a:hover svg { | ||||
|     margin-left : -5px; | ||||
|     margin-right: 13px; | ||||
| } | ||||
|  | ||||
| header>menu a svg path { | ||||
|     fill: var(--text); | ||||
| } | ||||
|  | ||||
| header>section { | ||||
|     background-color: var(--background-light-1); | ||||
| } | ||||
|  | ||||
| header :is(button, a[type="button"]) { | ||||
|     width           : 100%; | ||||
|     height          : 40px; | ||||
|     display         : flex; | ||||
|     justify-content : center; | ||||
|     align-items     : center; | ||||
|     cursor          : pointer; | ||||
|     background-color: var(--red); | ||||
|     transition      : unset; | ||||
| } | ||||
|  | ||||
| header button { | ||||
|     font-weight   : bold; | ||||
|     text-transform: uppercase; | ||||
| } | ||||
|  | ||||
| header :is(button, a[type="button"]):hover { | ||||
|     background-color: var(--red-light); | ||||
| } | ||||
|  | ||||
| header :is(button, a[type="button"]):active { | ||||
|     background-color: var(--red-dark); | ||||
| } | ||||
|  | ||||
| header>nav { | ||||
|     margin-top    : auto; | ||||
|     display       : flex; | ||||
|     flex-direction: column; | ||||
| } | ||||
|  | ||||
| main { | ||||
|     z-index       : 1000; | ||||
|     height        : 100%; | ||||
|     display       : flex; | ||||
|     flex-direction: column; | ||||
| } | ||||
|  | ||||
| footer { | ||||
|     z-index : 3000; | ||||
|     position: absolute; | ||||
| } | ||||
|   | ||||
| @@ -1,26 +1,26 @@ | ||||
| "use strict"; | ||||
|  | ||||
| class account { | ||||
|     static async initialization() { | ||||
|         // Запрос | ||||
|         return fetch('https://auth.mirzaev.sexy/account/initialization', { | ||||
|             method: 'GET' | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     static authentication() { | ||||
|         // Инициализация аккаунта | ||||
|         alert(1); | ||||
|         this.initialization() | ||||
|             .then( | ||||
|                 (response) => { | ||||
|                     alert(2); | ||||
|                 } | ||||
|             ); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     static deauthentication() { | ||||
|     } | ||||
| } | ||||
| "use strict"; | ||||
|  | ||||
| class account { | ||||
|     static async initialization() { | ||||
|         // Запрос | ||||
|         return fetch('https://auth.mirzaev.sexy/account/initialization', { | ||||
|             method: 'GET' | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     static authentication() { | ||||
|         // Инициализация аккаунта | ||||
|         alert(1); | ||||
|         this.initialization() | ||||
|             .then( | ||||
|                 (response) => { | ||||
|                     alert(2); | ||||
|                 } | ||||
|             ); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     static deauthentication() { | ||||
|     } | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,127 +1,127 @@ | ||||
| "use strict"; | ||||
|  | ||||
| class troller { | ||||
|     static what = { | ||||
|         enable() { | ||||
|             document.body.onmouseleave = function () { | ||||
|                 // if (Math.random() > 0.90) { | ||||
|                 // 10% | ||||
|  | ||||
|                 troller.what.start(); | ||||
|                 // } | ||||
|             }; | ||||
|  | ||||
|             document.body.onmouseenter = function () { | ||||
|                 troller.what.end(); | ||||
|             }; | ||||
|         }, | ||||
|         disable() { | ||||
|             document.body.onmouseleave = document.body.onmouseenter = undefined; | ||||
|         }, | ||||
|         start() { | ||||
|             // Отображение изображения | ||||
|             document.getElementById('what_image').classList.add('active'); | ||||
|  | ||||
|             // Инициализация элемента со звуком | ||||
|             const what_sound = document.getElementById('what_sound'); | ||||
|  | ||||
|             // Воспроизведение звука | ||||
|             what_sound.currentTime = 0; | ||||
|             what_sound.play(); | ||||
|         }, | ||||
|         end() { | ||||
|             // Сокрытие изображения | ||||
|             document.getElementById('what_image').classList.remove('active'); | ||||
|  | ||||
|             // Остановка звука | ||||
|             document.getElementById('what_sound').pause(); | ||||
|         }, | ||||
|         single(event = 'onmouseleave') { | ||||
|             if (typeof event === 'string') { | ||||
|                 // Получены обязательные входные параметры | ||||
|                 // Отображение изображения | ||||
|                 document.getElementById('what_image').classList.add('active'); | ||||
|  | ||||
|                 // Инициализация элемента со звуком | ||||
|                 const what_sound = document.getElementById('what_sound'); | ||||
|  | ||||
|                 // Воспроизведение звука | ||||
|                 what_sound.currentTime = 0; | ||||
|                 what_sound.play(); | ||||
|  | ||||
|                 document.body[event] = function () { | ||||
|                     troller.what.end(); | ||||
|  | ||||
|                     document.body[event] = undefined; | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static vk() { | ||||
|         setInterval(function () { | ||||
|             const sound = document.getElementById('sound_vk'); | ||||
|  | ||||
|             if (Math.random() > 0.95) { | ||||
|                 // 5% | ||||
|  | ||||
|                 // Воспроизведение звука | ||||
|                 sound.currentTime = 0; | ||||
|                 sound.play(); | ||||
|             } | ||||
|         }, 85000); | ||||
|     } | ||||
|  | ||||
|     static whatsapp() { | ||||
|         setInterval(function () { | ||||
|             const sound = document.getElementById('sound_whatsup'); | ||||
|  | ||||
|             if (Math.random() > 0.97) { | ||||
|                 // 3% | ||||
|  | ||||
|                 // Воспроизведение звука | ||||
|                 sound.currentTime = 0; | ||||
|                 sound.play(); | ||||
|             } | ||||
|         }, 125000); | ||||
|     } | ||||
|  | ||||
|     static iphone() { | ||||
|         setInterval(function () { | ||||
|             const sound = document.getElementById('sound_iphone'); | ||||
|  | ||||
|             if (Math.random() > 0.98) { | ||||
|                 // 2% | ||||
|  | ||||
|                 // Воспроизведение звука | ||||
|                 sound.currentTime = 0; | ||||
|                 sound.play(); | ||||
|             } | ||||
|         }, 265000); | ||||
|     } | ||||
| } | ||||
|  | ||||
| if (Math.random() > 0.90) { | ||||
|     // 10% | ||||
|  | ||||
|     troller.what.enable(); | ||||
| } | ||||
|  | ||||
| if (Math.random() > 0.90) { | ||||
|     // 10% | ||||
|  | ||||
|     troller.vk(); | ||||
| } | ||||
|  | ||||
|  | ||||
| if (Math.random() > 0.90) { | ||||
|     // 10% | ||||
|  | ||||
|     troller.whatsapp(); | ||||
| } | ||||
|  | ||||
| if (Math.random() > 0.90) { | ||||
|     // 10% | ||||
|  | ||||
|     troller.iphone(); | ||||
| } | ||||
| "use strict"; | ||||
|  | ||||
| class troller { | ||||
|     static what = { | ||||
|         enable() { | ||||
|             document.body.onmouseleave = function () { | ||||
|                 // if (Math.random() > 0.90) { | ||||
|                 // 10% | ||||
|  | ||||
|                 troller.what.start(); | ||||
|                 // } | ||||
|             }; | ||||
|  | ||||
|             document.body.onmouseenter = function () { | ||||
|                 troller.what.end(); | ||||
|             }; | ||||
|         }, | ||||
|         disable() { | ||||
|             document.body.onmouseleave = document.body.onmouseenter = undefined; | ||||
|         }, | ||||
|         start() { | ||||
|             // Отображение изображения | ||||
|             document.getElementById('what_image').classList.add('active'); | ||||
|  | ||||
|             // Инициализация элемента со звуком | ||||
|             const what_sound = document.getElementById('what_sound'); | ||||
|  | ||||
|             // Воспроизведение звука | ||||
|             what_sound.currentTime = 0; | ||||
|             what_sound.play(); | ||||
|         }, | ||||
|         end() { | ||||
|             // Сокрытие изображения | ||||
|             document.getElementById('what_image').classList.remove('active'); | ||||
|  | ||||
|             // Остановка звука | ||||
|             document.getElementById('what_sound').pause(); | ||||
|         }, | ||||
|         single(event = 'onmouseleave') { | ||||
|             if (typeof event === 'string') { | ||||
|                 // Получены обязательные входные параметры | ||||
|                 // Отображение изображения | ||||
|                 document.getElementById('what_image').classList.add('active'); | ||||
|  | ||||
|                 // Инициализация элемента со звуком | ||||
|                 const what_sound = document.getElementById('what_sound'); | ||||
|  | ||||
|                 // Воспроизведение звука | ||||
|                 what_sound.currentTime = 0; | ||||
|                 what_sound.play(); | ||||
|  | ||||
|                 document.body[event] = function () { | ||||
|                     troller.what.end(); | ||||
|  | ||||
|                     document.body[event] = undefined; | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static vk() { | ||||
|         setInterval(function () { | ||||
|             const sound = document.getElementById('sound_vk'); | ||||
|  | ||||
|             if (Math.random() > 0.95) { | ||||
|                 // 5% | ||||
|  | ||||
|                 // Воспроизведение звука | ||||
|                 sound.currentTime = 0; | ||||
|                 sound.play(); | ||||
|             } | ||||
|         }, 85000); | ||||
|     } | ||||
|  | ||||
|     static whatsapp() { | ||||
|         setInterval(function () { | ||||
|             const sound = document.getElementById('sound_whatsup'); | ||||
|  | ||||
|             if (Math.random() > 0.97) { | ||||
|                 // 3% | ||||
|  | ||||
|                 // Воспроизведение звука | ||||
|                 sound.currentTime = 0; | ||||
|                 sound.play(); | ||||
|             } | ||||
|         }, 125000); | ||||
|     } | ||||
|  | ||||
|     static iphone() { | ||||
|         setInterval(function () { | ||||
|             const sound = document.getElementById('sound_iphone'); | ||||
|  | ||||
|             if (Math.random() > 0.98) { | ||||
|                 // 2% | ||||
|  | ||||
|                 // Воспроизведение звука | ||||
|                 sound.currentTime = 0; | ||||
|                 sound.play(); | ||||
|             } | ||||
|         }, 265000); | ||||
|     } | ||||
| } | ||||
|  | ||||
| if (Math.random() > 0.90) { | ||||
|     // 10% | ||||
|  | ||||
|     troller.what.enable(); | ||||
| } | ||||
|  | ||||
| if (Math.random() > 0.90) { | ||||
|     // 10% | ||||
|  | ||||
|     troller.vk(); | ||||
| } | ||||
|  | ||||
|  | ||||
| if (Math.random() > 0.90) { | ||||
|     // 10% | ||||
|  | ||||
|     troller.whatsapp(); | ||||
| } | ||||
|  | ||||
| if (Math.random() > 0.90) { | ||||
|     // 10% | ||||
|  | ||||
|     troller.iphone(); | ||||
| } | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| arangodb.php | ||||
| arangodb.php | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| <?php | ||||
|  | ||||
| return [ | ||||
|     'endpoint' => 'unix:///var/run/arangodb3/arango.sock', | ||||
|     'database' => '', | ||||
|     'name' => '', | ||||
|     'password' => '' | ||||
| ]; | ||||
| <?php | ||||
|  | ||||
| return [ | ||||
|     'endpoint' => 'unix:///var/run/arangodb3/arango.sock', | ||||
|     'database' => '', | ||||
|     'name' => '', | ||||
|     'password' => '' | ||||
| ]; | ||||
|   | ||||
| @@ -1,53 +0,0 @@ | ||||
| {% block css %} | ||||
| <link type="text/css" rel="stylesheet" href="/css/account.css"> | ||||
| <link type="text/css" rel="stylesheet" href="/css/gradient.css"> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| <section id="authentication"> | ||||
|     {% if account %} | ||||
|     {{ account.getKey() }} | ||||
|     {% if vk %} | ||||
|     {{ vk.mail }} | ||||
|     {% endif %} | ||||
|     {% else %} | ||||
|     <section class="header gradient unselectable"> | ||||
|         <div class="glare"></div> | ||||
|         <img class="avatar unselectable" src="/images/what.png" alt="Пользователь" draggable="false"> | ||||
|         <a href="https://mirzaev.sexy">Нейрожурнал Мирзаева</a> | ||||
|         <div class="red"></div> | ||||
|         <div class="green"></div> | ||||
|         <div class="blue"></div> | ||||
|         <img class="cover unselectable" src="/images/heh.gif" alt="Нейрожурнал Мирзаева" draggable="false"></img> | ||||
|     </section> | ||||
|     <section class="body"> | ||||
|         <ul> | ||||
|             <li>Подпункт 2.1.</li> | ||||
|             <li>Подпункт 2.2. | ||||
|                 <ul> | ||||
|                     <li>Подпункт 2.2.1.</li> | ||||
|                     <li>Подпункт 2.2.2.</li> | ||||
|                 </ul> | ||||
|             </li> | ||||
|             <li>Подпункт 2.3.</li> | ||||
|         </ul> | ||||
|         <div class="buttons"> | ||||
|             <button class="accept">Разрешить</button> | ||||
|             <button>Запретить</button> | ||||
|         </div> | ||||
|     </section> | ||||
|     {% endif %} | ||||
|     <svg width="0" height="0"> | ||||
|         <defs> | ||||
|             <clipPath id="authentication-header-mask"> | ||||
|                 <path | ||||
|                     d="M50,160 L50,130 C22,130 0,107.612 0,80 C0,52 22,30 50,30 L50,3 C50,1.3 51.3,0 53,0 L447,0 C448,0 450,1.5 450,3 L450,160 L50,160 Z" /> | ||||
|             </clipPath> | ||||
|         </defs> | ||||
|     </svg> | ||||
| </section> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| <script type="text/javascript" src="/js/account.js"></script> | ||||
| {% endblock %} | ||||
| @@ -1,14 +1,14 @@ | ||||
|  | ||||
| {% block css %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| <aside> | ||||
| </aside> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_init %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block css %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| <aside> | ||||
| </aside> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_init %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,33 +1,33 @@ | ||||
| <!doctype html> | ||||
|  | ||||
| <html lang="ru"> | ||||
|  | ||||
| <head> | ||||
| 	{% use 'head.html' with title as head_title, meta as head_meta, css as head_css %} | ||||
|  | ||||
| 	{% block title %} | ||||
| 	{{ block('head_title') }} | ||||
| 	{% endblock %} | ||||
|  | ||||
| 	{% block meta %} | ||||
| 	{{ block('head_meta') }} | ||||
| 	{% endblock %} | ||||
|  | ||||
| 	{% block css %} | ||||
| 	{{ block('head_css') }} | ||||
| 	{% endblock %} | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
| 	{% block body %} | ||||
| 	{% endblock %} | ||||
|  | ||||
| 	{% block js %} | ||||
| 	{% include 'js.html' %} | ||||
| 	{% endblock %} | ||||
|  | ||||
| 	{% block js_init %} | ||||
| 	{% endblock %} | ||||
| </body> | ||||
|  | ||||
| </html> | ||||
| <!doctype html> | ||||
|  | ||||
| <html lang="ru"> | ||||
|  | ||||
| <head> | ||||
| 	{% use 'head.html' with title as head_title, meta as head_meta, css as head_css %} | ||||
|  | ||||
| 	{% block title %} | ||||
| 	{{ block('head_title') }} | ||||
| 	{% endblock %} | ||||
|  | ||||
| 	{% block meta %} | ||||
| 	{{ block('head_meta') }} | ||||
| 	{% endblock %} | ||||
|  | ||||
| 	{% block css %} | ||||
| 	{{ block('head_css') }} | ||||
| 	{% endblock %} | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
| 	{% block body %} | ||||
| 	{% endblock %} | ||||
|  | ||||
| 	{% block js %} | ||||
| 	{% include 'js.html' %} | ||||
| 	{% endblock %} | ||||
|  | ||||
| 	{% block js_init %} | ||||
| 	{% endblock %} | ||||
| </body> | ||||
|  | ||||
| </html> | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| <footer> | ||||
|     <!-- <p><a href="http://www.anybrowser.org/campaign/"><img src="/img/logos/any_browser.gif" width="278" height="44" alt="Доступно на любом браузере" /></a></p> --> | ||||
|     <!-- <p><a href="/browsers"><img src="/img/logos/any_browser.gif" width="278" height="44" alt="Доступно на любом браузере" /></a></p> --> | ||||
| </footer> | ||||
| <footer> | ||||
|     <!-- <p><a href="http://www.anybrowser.org/campaign/"><img src="/img/logos/any_browser.gif" width="278" height="44" alt="Доступно на любом браузере" /></a></p> --> | ||||
|     <!-- <p><a href="/browsers"><img src="/img/logos/any_browser.gif" width="278" height="44" alt="Доступно на любом браузере" /></a></p> --> | ||||
| </footer> | ||||
|   | ||||
| @@ -1,86 +1,86 @@ | ||||
| {% block css %} | ||||
| <link type="text/css" rel="stylesheet" href="/css/graph.css"> | ||||
| <link type="text/css" rel="stylesheet" href="/css/icon_close.css" /> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| {% if graph.id != empty %} | ||||
| <section id="{{ graph.id }}" class="graph unselectable" {% for name, value in graph.attributes %} {{ name }}="{{value}}" | ||||
|     {% endfor %}> | ||||
|     {% for element in graph.elements %} | ||||
|     <{{element.tag??'article'}}>{{ element.content }}</{{element.tag??'article'}}> | ||||
|     {% endfor %} | ||||
| </section> | ||||
| {% endif %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| <script type="module" src="/js/victor.js" defer></script> | ||||
| <script type="module" src="/js/graph.js" defer></script> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_init %} | ||||
| <script> | ||||
|     document.addEventListener('graph.loaded', function (e) { | ||||
|         // Инициализация графика | ||||
|         {% if graph.id != empty %} | ||||
|         const core = new e.detail.graph(document.getElementById('{{ graph.id }}')); | ||||
|  | ||||
|         core.write({ | ||||
|             title: 'бебра' | ||||
|         }); | ||||
|         const mirzaev = core.write({ | ||||
|             title: 'Арсен Мирзаев', | ||||
|             description: ' абабабаба абабабабаабабабабаабабабабаабабабаба абабабаба абабабаба абабабабаабабабаба абабабаба абабабаба абабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабаба', | ||||
|             cover: 'https://sun9-east.userapi.com/sun9-27/s/v1/ig2/qOBvWvsDwPmMjOTqQbl0TuGHaoMwWQhhd81nxD847v32dT-pyYa9kxw2MY7moQBVBoN4iVLnUZx6WmE4x4HnIwAu.jpg?size=810x1080&quality=95&type=album', | ||||
|             link: { | ||||
|                 name: 'Арсен Мирзаев', | ||||
|                 title: 'Читать статью полностью', | ||||
|                 href: 'https://google.com', | ||||
|                 class: ['source'] | ||||
|             }, | ||||
|             popup: 'Для подробной информации читайте статью полностью', | ||||
|             color: 'red' | ||||
|         }); | ||||
|         const berbi = core.write({ | ||||
|             title: 'берби' | ||||
|         }); | ||||
|         const anarchy = core.write({ | ||||
|             title: 'анархия' | ||||
|         }); | ||||
|         core.connect( | ||||
|             berbi, | ||||
|             mirzaev); | ||||
|         core.connect( | ||||
|             anarchy, | ||||
|             mirzaev); | ||||
|         core.connect( | ||||
|             core.write({ | ||||
|                 title: 'бабы' | ||||
|             }), | ||||
|             mirzaev); | ||||
|         core.connect( | ||||
|             core.write({ | ||||
|                 title: 'Ксения Велькович', | ||||
|                 description: 'А меня вписать в кружочек?', | ||||
|                 cover: 'https://storage.mirzaev.sexy/2022/mirzaev.sexy/nodes/ksenia_velkovich.jpg', | ||||
|                 link: { | ||||
|                     name: 'Ксения Велькович', | ||||
|                     title: 'Страница ВКонтакте', | ||||
|                     href: 'https://vk.com/id720261644', | ||||
|                     class: ['source'] | ||||
|                 } | ||||
|             }), | ||||
|             mirzaev); | ||||
|         core.connect( | ||||
|             core.write({ | ||||
|                 title: 'чокопайки' | ||||
|             }), | ||||
|             mirzaev); | ||||
|         core.connect( | ||||
|             anarchy, | ||||
|             berbi); | ||||
|         {% endif %} | ||||
|     }); | ||||
| </script> | ||||
| {% endblock %} | ||||
| {% block css %} | ||||
| <link type="text/css" rel="stylesheet" href="/css/graph.css"> | ||||
| <link type="text/css" rel="stylesheet" href="/css/icon_close.css" /> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| {% if graph.id != empty %} | ||||
| <section id="{{ graph.id }}" class="graph unselectable" {% for name, value in graph.attributes %} {{ name }}="{{value}}" | ||||
|     {% endfor %}> | ||||
|     {% for element in graph.elements %} | ||||
|     <{{element.tag??'article'}}>{{ element.content }}</{{element.tag??'article'}}> | ||||
|     {% endfor %} | ||||
| </section> | ||||
| {% endif %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| <script type="module" src="/js/victor.js" defer></script> | ||||
| <script type="module" src="/js/graph.js" defer></script> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_init %} | ||||
| <script> | ||||
|     document.addEventListener('graph.loaded', function (e) { | ||||
|         // Инициализация графика | ||||
|         {% if graph.id != empty %} | ||||
|         const core = new e.detail.graph(document.getElementById('{{ graph.id }}')); | ||||
|  | ||||
|         core.write({ | ||||
|             title: 'бебра' | ||||
|         }); | ||||
|         const mirzaev = core.write({ | ||||
|             title: 'Арсен Мирзаев', | ||||
|             description: ' абабабаба абабабабаабабабабаабабабабаабабабаба абабабаба абабабаба абабабабаабабабаба абабабаба абабабаба абабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабабабабаба абабабабаабабаба', | ||||
|             cover: 'https://sun9-east.userapi.com/sun9-27/s/v1/ig2/qOBvWvsDwPmMjOTqQbl0TuGHaoMwWQhhd81nxD847v32dT-pyYa9kxw2MY7moQBVBoN4iVLnUZx6WmE4x4HnIwAu.jpg?size=810x1080&quality=95&type=album', | ||||
|             link: { | ||||
|                 name: 'Арсен Мирзаев', | ||||
|                 title: 'Читать статью полностью', | ||||
|                 href: 'https://google.com', | ||||
|                 class: ['source'] | ||||
|             }, | ||||
|             popup: 'Для подробной информации читайте статью полностью', | ||||
|             color: 'red' | ||||
|         }); | ||||
|         const berbi = core.write({ | ||||
|             title: 'берби' | ||||
|         }); | ||||
|         const anarchy = core.write({ | ||||
|             title: 'анархия' | ||||
|         }); | ||||
|         core.connect( | ||||
|             berbi, | ||||
|             mirzaev); | ||||
|         core.connect( | ||||
|             anarchy, | ||||
|             mirzaev); | ||||
|         core.connect( | ||||
|             core.write({ | ||||
|                 title: 'бабы' | ||||
|             }), | ||||
|             mirzaev); | ||||
|         core.connect( | ||||
|             core.write({ | ||||
|                 title: 'Ксения Велькович', | ||||
|                 description: 'А меня вписать в кружочек?', | ||||
|                 cover: 'https://storage.mirzaev.sexy/2022/mirzaev.sexy/nodes/ksenia_velkovich.jpg', | ||||
|                 link: { | ||||
|                     name: 'Ксения Велькович', | ||||
|                     title: 'Страница ВКонтакте', | ||||
|                     href: 'https://vk.com/id720261644', | ||||
|                     class: ['source'] | ||||
|                 } | ||||
|             }), | ||||
|             mirzaev); | ||||
|         core.connect( | ||||
|             core.write({ | ||||
|                 title: 'чокопайки' | ||||
|             }), | ||||
|             mirzaev); | ||||
|         core.connect( | ||||
|             anarchy, | ||||
|             berbi); | ||||
|         {% endif %} | ||||
|     }); | ||||
| </script> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| {% block title %} | ||||
| <title>{% if head.title != empty %}{{head.title}}{% else %}Управление аккаунтом{% endif %}</title> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block meta %} | ||||
| <meta charset="utf-8"> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | ||||
| {% for meta in head.metas %} | ||||
| <meta {% for name, value in meta.attributes %}{{name}}="{{value}}" {% endfor %}> | ||||
| {% endfor %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block css %} | ||||
| <link type="text/css" rel="stylesheet" href="/css/main.css" /> | ||||
| {% endblock %} | ||||
| {% block title %} | ||||
| <title>{% if head.title != empty %}{{head.title}}{% else %}Управление аккаунтом{% endif %}</title> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block meta %} | ||||
| <meta charset="utf-8"> | ||||
| <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | ||||
| {% for meta in head.metas %} | ||||
| <meta {% for name, value in meta.attributes %}{{name}}="{{value}}" {% endfor %}> | ||||
| {% endfor %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block css %} | ||||
| <link type="text/css" rel="stylesheet" href="/css/main.css" /> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,13 +1,13 @@ | ||||
| {% block css %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| <header> | ||||
| </header> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_init %} | ||||
| {% endblock %} | ||||
| {% block css %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| <header> | ||||
| </header> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_init %} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,28 +1,28 @@ | ||||
| {% block css %} | ||||
| <link type="text/css" rel="stylesheet" href="/css/hotline.css"> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| {% if hotline.id != empty %} | ||||
| <section id="{{ hotline.id }}" class="hotline unselectable" data-hotline="true" {% for name, value in hotline.parameters | ||||
|     %} data-hotline-{{ name }}="{{value}}" {% endfor %} {% for name, value in hotline.attributes %} {{ name | ||||
|     }}="{{value}}" {% endfor %}> | ||||
|     {% for element in hotline.elements %} | ||||
|     <{{element.tag??'article'}}>{{ element.content }}</{{element.tag??'article'}}> | ||||
|     {% endfor %} | ||||
| </section> | ||||
| {% endif %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| <script type="text/javascript" src="/js/hotline.js" defer></script> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_init %} | ||||
| <script> | ||||
|     document.addEventListener('hotline.loaded', function (e) { | ||||
|         // Запуск препроцессора бегущих строк | ||||
|         e.detail.hotline.preprocessing(); | ||||
|     }); | ||||
| </script> | ||||
| {% endblock %} | ||||
| {% block css %} | ||||
| <link type="text/css" rel="stylesheet" href="/css/hotline.css"> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| {% if hotline.id != empty %} | ||||
| <section id="{{ hotline.id }}" class="hotline unselectable" data-hotline="true" {% for name, value in hotline.parameters | ||||
|     %} data-hotline-{{ name }}="{{value}}" {% endfor %} {% for name, value in hotline.attributes %} {{ name | ||||
|     }}="{{value}}" {% endfor %}> | ||||
|     {% for element in hotline.elements %} | ||||
|     <{{element.tag??'article'}}>{{ element.content }}</{{element.tag??'article'}}> | ||||
|     {% endfor %} | ||||
| </section> | ||||
| {% endif %} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| <script type="text/javascript" src="/js/hotline.js" defer></script> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_init %} | ||||
| <script> | ||||
|     document.addEventListener('hotline.loaded', function (e) { | ||||
|         // Запуск препроцессора бегущих строк | ||||
|         e.detail.hotline.preprocessing(); | ||||
|     }); | ||||
| </script> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,47 +1,47 @@ | ||||
| {% extends "core.html" %} | ||||
|  | ||||
| {% use 'account/element.html' with css as account_css, body as account_body, js as account_js %} | ||||
| {% use "core.html" with css as core_css, body as core_body, js as core_js, js_init as core_js_init %} | ||||
| {% use "header.html" with css as header_css, body as header_body, js as header_js, js_init as header_js_init %} | ||||
| {% use "aside.html" with css as aside_css, body as aside_body, js as aside_js, js_init as aside_js_init %} | ||||
| {% use 'graph/index.html' with css as graph_css, body as graph_body, js as graph_js, js_init as graph_js_init %} | ||||
|  | ||||
| {% block css %} | ||||
| {{ block('core_css') }} | ||||
| {{ block('header_css') }} | ||||
| {{ block('aside_css') }} | ||||
| {{ block('graph_css') }} | ||||
| {{ block('account_css') }} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| {{ block('core_body') }} | ||||
| {{ block('aside_body') }} | ||||
| {{ block('header_body') }} | ||||
|  | ||||
| <main> | ||||
| 	<noscript>К сожалению мой сайт ещё пока не готов для работы без javascript</noscript> | ||||
| 	{% block main %} | ||||
| 	{{ block('account_body') }} | ||||
| 	{% endblock %} | ||||
| 	{{ block('graph_body') }} | ||||
| </main> | ||||
|  | ||||
| {# {% include 'footer.html' %} #} | ||||
| {# <div class="background"></div> #} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| {{ block('core_js') }} | ||||
| {{ block('header_js') }} | ||||
| {{ block('aside_js') }} | ||||
| {{ block('graph_js') }} | ||||
| {{ block('account_js') }} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_init %} | ||||
| {{ block('core_js_init') }} | ||||
| {{ block('header_js_init') }} | ||||
| {{ block('aside_js_init') }} | ||||
| {{ block('graph_js_init') }} | ||||
| {% endblock %} | ||||
| {% extends "core.html" %} | ||||
|  | ||||
| {% use 'nodes/account.html' with css as account_css, body as account_body, js as account_js %} | ||||
| {% use "core.html" with css as core_css, body as core_body, js as core_js, js_init as core_js_init %} | ||||
| {% use "header.html" with css as header_css, body as header_body, js as header_js, js_init as header_js_init %} | ||||
| {% use "aside.html" with css as aside_css, body as aside_body, js as aside_js, js_init as aside_js_init %} | ||||
| {% use 'graph/index.html' with css as graph_css, body as graph_body, js as graph_js, js_init as graph_js_init %} | ||||
|  | ||||
| {% block css %} | ||||
| {{ block('core_css') }} | ||||
| {{ block('header_css') }} | ||||
| {{ block('aside_css') }} | ||||
| {{ block('graph_css') }} | ||||
| {{ block('account_css') }} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| {{ block('core_body') }} | ||||
| {{ block('aside_body') }} | ||||
| {{ block('header_body') }} | ||||
|  | ||||
| <main> | ||||
| 	<noscript>К сожалению мой сайт ещё пока не готов для работы без javascript</noscript> | ||||
| 	{% block main %} | ||||
| 	{{ block('account_body') }} | ||||
| 	{% endblock %} | ||||
| 	{{ block('graph_body') }} | ||||
| </main> | ||||
|  | ||||
| {# {% include 'footer.html' %} #} | ||||
| {# <div class="background"></div> #} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| {{ block('core_js') }} | ||||
| {{ block('header_js') }} | ||||
| {{ block('aside_js') }} | ||||
| {{ block('graph_js') }} | ||||
| {{ block('account_js') }} | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js_init %} | ||||
| {{ block('core_js_init') }} | ||||
| {{ block('header_js_init') }} | ||||
| {{ block('aside_js_init') }} | ||||
| {{ block('graph_js_init') }} | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| {% block js %} | ||||
| <script type="text/javascript" src="/js/js.cookie.min.js" defer></script> | ||||
| {% endblock %} | ||||
| {% block js %} | ||||
| <script type="text/javascript" src="/js/js.cookie.min.js" defer></script> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,25 +1,25 @@ | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\views; | ||||
|  | ||||
| use mirzaev\minimal\controller; | ||||
|  | ||||
| use Twig\Loader\FilesystemLoader; | ||||
| use Twig\Environment as view; | ||||
|  | ||||
| /** | ||||
|  * Менеджер представлений | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class manager extends controller | ||||
| { | ||||
|     public function render(string $file, array $vars = []): ?string | ||||
|     { | ||||
|         // Генерация представления | ||||
|         return (new view(new FilesystemLoader(VIEWS)))->render($file, $vars); | ||||
|     } | ||||
| } | ||||
| <?php | ||||
|  | ||||
| declare(strict_types=1); | ||||
|  | ||||
| namespace mirzaev\site\account\views; | ||||
|  | ||||
| use mirzaev\minimal\controller; | ||||
|  | ||||
| use Twig\Loader\FilesystemLoader; | ||||
| use Twig\Environment as view; | ||||
|  | ||||
| /** | ||||
|  * Менеджер представлений | ||||
|  * | ||||
|  * @package mirzaev\site\account\controllers | ||||
|  * @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy> | ||||
|  */ | ||||
| final class manager extends controller | ||||
| { | ||||
|     public function render(string $file, array $vars = []): ?string | ||||
|     { | ||||
|         // Генерация представления | ||||
|         return (new view(new FilesystemLoader(VIEWS)))->render($file, $vars); | ||||
|     } | ||||
| } | ||||
|   | ||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										53
									
								
								mirzaev/site/account/system/views/nodes/account.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								mirzaev/site/account/system/views/nodes/account.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| {% block css %} | ||||
| <link type="text/css" rel="stylesheet" href="/css/account.css"> | ||||
| <link type="text/css" rel="stylesheet" href="/css/gradient.css"> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| <section id="authentication"> | ||||
|   {% if account %} | ||||
|   {{ account.getKey() }} | ||||
|   {% if vk %} | ||||
|   {{ vk.mail }} | ||||
|   {% endif %} | ||||
|   {% else %} | ||||
|   <section class="header gradient unselectable"> | ||||
|     <div class="glare"></div> | ||||
|     <img class="avatar unselectable" src="/images/what.png" alt="Пользователь" draggable="false"> | ||||
|     <a href="https://mirzaev.sexy">{{ name ?? session.ip ?? session.hash ?? 'Ты кто?'}}</a> | ||||
|     <div class="red"></div> | ||||
|     <div class="green"></div> | ||||
|     <div class="blue"></div> | ||||
|     <img class="cover unselectable" src="/images/heh.gif" alt="Нейрожурнал Мирзаева" draggable="false"></img> | ||||
|   </section> | ||||
|   <section class="body"> | ||||
|     <ul> | ||||
|       <li>Подпункт 2.1.</li> | ||||
|       <li>Подпункт 2.2. | ||||
|         <ul> | ||||
|           <li>Подпункт 2.2.1.</li> | ||||
|           <li>Подпункт 2.2.2.</li> | ||||
|         </ul> | ||||
|       </li> | ||||
|       <li>Подпункт 2.3.</li> | ||||
|     </ul> | ||||
|     <div class="buttons"> | ||||
|       <button class="accept">Разрешить</button> | ||||
|       <button>Запретить</button> | ||||
|     </div> | ||||
|   </section> | ||||
|   {% endif %} | ||||
|   <svg width="0" height="0"> | ||||
|     <defs> | ||||
|       <clipPath id="authentication-header-mask"> | ||||
|         <path | ||||
|           d="M50,160 L50,130 C22,130 0,107.612 0,80 C0,52 22,30 50,30 L50,3 C50,1.3 51.3,0 53,0 L447,0 C448,0 450,1.5 450,3 L450,160 L50,160 Z" /> | ||||
|       </clipPath> | ||||
|     </defs> | ||||
|   </svg> | ||||
| </section> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| <script type="text/javascript" src="/js/account.js"></script> | ||||
| {% endblock %} | ||||
							
								
								
									
										53
									
								
								mirzaev/site/account/system/views/nodes/connect.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								mirzaev/site/account/system/views/nodes/connect.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| {% block css %} | ||||
| <link type="text/css" rel="stylesheet" href="/css/account.css"> | ||||
| <link type="text/css" rel="stylesheet" href="/css/gradient.css"> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block body %} | ||||
| <section id="authentication"> | ||||
|   {% if account %} | ||||
|   {{ account.getKey() }} | ||||
|   {% if vk %} | ||||
|   {{ vk.mail }} | ||||
|   {% endif %} | ||||
|   {% else %} | ||||
|   <section class="header gradient unselectable"> | ||||
|     <div class="glare"></div> | ||||
|     <img class="avatar unselectable" src="/images/what.png" alt="Пользователь" draggable="false"> | ||||
|     <a href="https://mirzaev.sexy">Нейрожурнал Мирзаева</a> | ||||
|     <div class="red"></div> | ||||
|     <div class="green"></div> | ||||
|     <div class="blue"></div> | ||||
|     <img class="cover unselectable" src="/images/heh.gif" alt="Нейрожурнал Мирзаева" draggable="false"></img> | ||||
|   </section> | ||||
|   <section class="body"> | ||||
|     <ul> | ||||
|       <li>Подпункт 2.1.</li> | ||||
|       <li>Подпункт 2.2. | ||||
|         <ul> | ||||
|           <li>Подпункт 2.2.1.</li> | ||||
|           <li>Подпункт 2.2.2.</li> | ||||
|         </ul> | ||||
|       </li> | ||||
|       <li>Подпункт 2.3.</li> | ||||
|     </ul> | ||||
|     <div class="buttons"> | ||||
|       <button class="accept">Разрешить</button> | ||||
|       <button>Запретить</button> | ||||
|     </div> | ||||
|   </section> | ||||
|   {% endif %} | ||||
|   <svg width="0" height="0"> | ||||
|     <defs> | ||||
|       <clipPath id="authentication-header-mask"> | ||||
|         <path | ||||
|           d="M50,160 L50,130 C22,130 0,107.612 0,80 C0,52 22,30 50,30 L50,3 C50,1.3 51.3,0 53,0 L447,0 C448,0 450,1.5 450,3 L450,160 L50,160 Z" /> | ||||
|       </clipPath> | ||||
|     </defs> | ||||
|   </svg> | ||||
| </section> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block js %} | ||||
| <script type="text/javascript" src="/js/account.js"></script> | ||||
| {% endblock %} | ||||
		Reference in New Issue
	
	Block a user