From bae941e155b70e76e08b2befe70ba501902d067b Mon Sep 17 00:00:00 2001 From: Maksym Date: Wed, 18 Jan 2023 12:43:45 +0100 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D1=83=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5.=20=D0=A0=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=BD=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DarksLight2/Training/StepFactory.php | 39 ++- .../DarksLight2/Training/TrainingManager.php | 283 +++++++++--------- api/index.php | 31 +- js/training/modal.js | 43 ++- modules_data/steps/step.php | 30 +- 5 files changed, 265 insertions(+), 161 deletions(-) diff --git a/_incl_data/class/DarksLight2/Training/StepFactory.php b/_incl_data/class/DarksLight2/Training/StepFactory.php index 992eca25..c30742e3 100644 --- a/_incl_data/class/DarksLight2/Training/StepFactory.php +++ b/_incl_data/class/DarksLight2/Training/StepFactory.php @@ -2,12 +2,47 @@ namespace DarksLight2\Training; +use DarksLight2\Helpers\Str; +use User; + abstract class StepFactory { - abstract public function getTitle(): string; abstract public function getMessage(): string; - abstract public function getShortName(): string; abstract public function getRewards(): array; + public function onLocations(): array + { + return ['all']; + } + + public function getShortName(): string + { + return Str::snakeCase(get_class($this)); + } + public function getAnswer() + { + return null; + } + + public function allowedToMove(): array + { + return []; + } + + public function isInfo(): bool + { + return true; + } + + public function databaseRecord() + { + return [ + 'completed' => false, + 'progress' => [ + 'current' => 0, + 'need' => 1, + ] + ]; + } } \ No newline at end of file diff --git a/_incl_data/class/DarksLight2/Training/TrainingManager.php b/_incl_data/class/DarksLight2/Training/TrainingManager.php index 05b56298..e61df599 100644 --- a/_incl_data/class/DarksLight2/Training/TrainingManager.php +++ b/_incl_data/class/DarksLight2/Training/TrainingManager.php @@ -3,24 +3,127 @@ namespace DarksLight2\Training; use Core\Db; +use DarksLight2\Training\Steps\ChatFirstStep; +use DarksLight2\Training\Steps\MyUserFirstQuest; +use DarksLight2\Training\Steps\MyUserFirstStep; +use DarksLight2\Training\Steps\MyUserFourthQuest; +use DarksLight2\Training\Steps\MyUserFourthStep; +use DarksLight2\Training\Steps\MyUserSecondQuest; +use DarksLight2\Training\Steps\MyUserSecondStep; +use DarksLight2\Training\Steps\MyUserThirdQuest; +use DarksLight2\Training\Steps\MyUserThirdStep; use DarksLight2\Traits\Singleton; +use PassGen; use PDO; use stdClass; +use User; class TrainingManager { - use Singleton; - - private $database_records = false; - private array $steps_data; private int $user_id; - private array $steps; + public string $current_step = ''; + private array $registered_steps; + private array $completed_steps; + private array $active_steps; - public function __construct(int $user_id) + private $database; + + public function __construct(int $user_id, $refresh_token = true) { + try { + $this->register([ + new ChatFirstStep(), + new MyUserFirstStep(), + new MyUserFirstQuest(), + new MyUserSecondStep(), + new MyUserSecondQuest(), + new MyUserThirdStep(), + new MyUserThirdQuest(), + new MyUserFourthStep(), + new MyUserFourthQuest(), + ]); + } catch (TrainingException $e) { + } + $this->user_id = $user_id; - $this->generateToken(); + + if($refresh_token) { + $this->generateToken(); + } + + $this->database = Db::getRow('SELECT * FROM user_training WHERE user_id = ?', [$user_id]); + + $this->createDatabaseRecord(); + + $this->database['data'] = json_decode($this->database['data'], true); + + $this->selectActiveSteps(); + } + + public function getCurrentStepName(): string + { + if(empty($this->current_step)) { + $this->current_step = array_key_first($this->active_steps) ?? ''; + } + + return $this->current_step; + } + public function getRegistered(): array + { + return $this->registered_steps; + } + + public function getDatabaseData() + { + return $this->database; + } + + public function addPoint($short_name, $closure = null) + { + if($short_name === $this->getCurrentStepName()) { + $this->database['data'][$short_name]['progress']['current']++; + if(isset($closure)) { + $closure($this); + } + } + } + + public function nextStep() + { + + if($this->database['data'][$this->getCurrentStepName()]['progress']['need'] <= $this->database['data'][$this->getCurrentStepName()]['progress']['current']) { + $this->database['data'][$this->getCurrentStepName()]['progress']['current'] = 0; + $this->database['data'][$this->getCurrentStepName()]['completed'] = true; + unset($this->active_steps[$this->getCurrentStepName()]); + $this->current_step = array_key_first($this->active_steps) ?? ''; + $this->database['current'] = $this->getCurrentStepName(); + } + } + + public function previousStep() + { + $this->current_step = end($this->completed_steps) ?? ''; + $this->database['current'] = $this->current_step; + $this->database['data'][$this->getCurrentStepName()]['completed'] = false; + } + + public function selectActiveSteps() + { + foreach ($this->database['data'] as $step_name => $data) { + if($data['completed'] === false) { + $this->active_steps[$step_name] = $data; + continue; + } + + $this->completed_steps[] = $step_name; + + } + } + + public function store() + { + Db::sql('UPDATE user_training SET data = ?, current = ? WHERE user_id = ?', [json_encode($this->database['data']), $this->database['current'], $this->user_id]); } /** @@ -37,77 +140,14 @@ class TrainingManager foreach ($steps as $step) { if($step instanceof StepFactory) { - $this->steps[$step->getShortName()] = new class ( - $step->getMessage(), - $step->getTitle(), - $step->getRewards(), - $this->stepData($step->getShortName())->complete, - $step->getShortName(), - $this->stepData($step->getShortName())->progress - ) { - public string $message; - public string $title; - public string $short_name; - public array $rewards; - public bool $isComplete; - - public stdClass $progress; - - public function __construct( - string $message, - string $title, - array $rewards, - bool $isComplete, - string $short_name, - stdClass $progress - ) { - $this->rewards = $rewards; - $this->title = $title; - $this->isComplete = $isComplete; - $this->message = $message; - $this->short_name = $short_name; - $this->progress = $progress; - } - - public function render() - { - if(!$this->isComplete) { - $path = $_SERVER['DOCUMENT_ROOT'] . '/modules_data/steps/' . $this->short_name . '.php'; - - if (file_exists($path)) { - require $path; - return; - } - - throw TrainingException::noRenderingFile(); - } - } - }; + $this->registered_steps[$step->getShortName()] = $step; } } } - - private function stepData(string $short_name): object - { - $data = $this->getDatabaseRecords()->data; - - if(!isset($data->$short_name)) { - $this->updateDatabaseRecord($short_name); - $data = $this->getDatabaseRecords()->data; - } - - return $data->$short_name; - } - private function generateToken($length = 16) { - $letters = 'abcdefgmikiHGJKLjkGASysj7603456'; - $token = ''; - - for ($i = 0; $i <= $length; $i++) { - $token .= $letters[rand(0, strlen($letters))]; - } + $token = PassGen::new($length); Db::run('UPDATE user_training SET api_token = ? WHERE user_id = ?', [ $token, @@ -115,87 +155,42 @@ class TrainingManager ]); } - public function getDatabaseRecords(): stdClass - { - if(!$this->database_records) { - $data = Db::run('SELECT * FROM user_training WHERE user_id = ?', [$this->user_id]) - ->fetch(PDO::FETCH_OBJ); - - $this->database_records = new stdClass(); - - $this->database_records->user_id = $data->user_id; - $this->database_records->api_token = $data->api_token; - $this->database_records->data = json_decode($data->data); - } - - return $this->database_records; - } - - public function updateDatabaseRecord(string $short_name) - { - if($this->getDatabaseRecords()) { - - $this->database_records->data->$short_name = $this->firstRecordData()->$short_name; - - Db::run('UPDATE user_training SET data = ? WHERE user_id = ?', [ - json_encode($this->database_records->data), - $this->user_id - ]); - } - } - public function createDatabaseRecord() { - if(!$this->getDatabaseRecords()) { - Db::run('INSERT INTO user_training (user_id, data) VALUES (?, ?)', [ - $this->user_id, - json_encode($this->firstRecordData()) - ]); - } - } + if(!$this->database) { - private function firstRecordData(): stdClass - { - $data = new stdClass(); - $short_names = [ - 'first_step', - 'second_step' - ]; + $data = []; - foreach ($short_names as $name) { - $data->$name->complete = false; - $data->$name->progress->current = 0; - $data->$name->progress->need = 1; - } - - return $data; - } - public function __get(string $name) - { - return $this->steps[$name]; - } - - public function addPoint(string $short_name) - { - $this->database_records->data->$short_name->progress->current++; - } - - public function nextStep() - { - foreach ($this->database_records->data as $step) { - if($step->complete === false && $step->progress->current >= $step->progress->need) { - $step->complete = true; + foreach ($this->registered_steps as $step) { + $data[$step->getShortName()] = $step->databaseRecord(); } + + Db::sql('INSERT INTO user_training (user_id, data, current) VALUES (?, ?, ?)', [ + $this->user_id, + json_encode($data, true), + array_key_first($data) + ]); + + $this->database = Db::getRow('SELECT * FROM user_training WHERE user_id = ?', [$this->user_id]); } } - public function store() + /** + * @throws \DarksLight2\Training\TrainingException + */ + public function render() { - $this->nextStep(); + if(in_array(User::start()->room['file'], $this->registered_steps[$this->getCurrentStepName()]->onLocations()) || in_array('all', $this->registered_steps[$this->getCurrentStepName()]->onLocations())) { + $path = $_SERVER['DOCUMENT_ROOT'] . '/modules_data/steps/step.php'; - Db::run('UPDATE user_training SET data = ? WHERE user_id = ?', [ - json_encode($this->database_records->data), - $this->user_id - ]); + if (file_exists($path)) { + $short_name = $this->getCurrentStepName(); + $answer = $this->registered_steps[$this->getCurrentStepName()]->getAnswer(); + require $path; + return; + } + + throw TrainingException::noRenderingFile(); + } } } \ No newline at end of file diff --git a/api/index.php b/api/index.php index 07462aa2..4f4d0702 100644 --- a/api/index.php +++ b/api/index.php @@ -1,5 +1,8 @@ info)) { - $training = TrainingManager::getInstance($user->info['id']); + $training = TrainingManager::getInstance($user->info['id'], false); + + try { + $training->createDatabaseRecord(); + $training->register([ + new MyUserFirstQuest(), + new ChatFirstStep() + ]); + } catch (TrainingException $e) { + } if (!isset($data->token) || !password_verify( - $training->getDatabaseRecords()->api_token . $data->time . $user->info['id'], + $training->getDatabaseData()->api_token . $data->time . $user->info['id'], $data->token )) { http_response_code(401); @@ -32,16 +44,27 @@ if(!empty($user->info)) { http_response_code(400); die; case 'complete': - if ($training->{$data->short_name}->isComplete) { + if ($training->getDatabaseData()['data'][$training->getCurrentStepName()]['completed']) { http_response_code(400); die; } - $training->addPoint($data->short_name); + if($training->getRegistered()[$data->short_name]->isInfo()) { + + $training->addPoint($training->getCurrentStepName()); + } + $training->nextStep(); + $training->store(); die(json_encode(['status' => 'ok'])); + + case 'go_back': + $training->previousStep(); + $training->store(); + die(json_encode(['status' => 'ok', 'message' => 'Вы указал не верный ответ!'])); + } } } \ No newline at end of file diff --git a/js/training/modal.js b/js/training/modal.js index b4be8069..11c3ea62 100644 --- a/js/training/modal.js +++ b/js/training/modal.js @@ -1,23 +1,44 @@ const request = new XMLHttpRequest(); const training_handler = () => { - request.open('POST', '/api/training/complete') + + const url = (condition) => { + return condition ? '/api/training/complete' : '/api/training/go_back'; + }; + + request.open('POST', url(training_data().answer === null || training_data().answer === get_user_answer())) request.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); - request.onreadystatechange = function() {//Call a function when the state changes. - if(request.readyState === 4 && request.status === 200) { - parent.frames['main'].location.reload(); + request.onreadystatechange = function () {//Call a function when the state changes. + if (request.readyState === 4) { + if(request.status === 200) { + parent.frames['main'].location.reload(); + } + + if(request.responseText !== '') { + let response = JSON.parse(request.responseText); + + if (response.message !== undefined) { + alert(response.message); + } + } } } request.send(JSON.stringify({ time: training_data().time, token: training_data().token, - short_name: training_data().short_name + short_name: training_data().short_name, + answer: get_user_answer(), })) - } -let active_training = null; +function get_user_answer() { + if(training_data().answer === '') { + return ''; + } + + return document.getElementsByName('user_answer')[0].value; +} (function () { if (typeof window.CustomEvent === "function") return false; @@ -44,20 +65,24 @@ $modal = function (options) { elemModal = document.createElement('div'), modalTemplate = '', modalFooterTemplate = '', + modalInputAnswer = '', modalButtonTemplate = '', modalHTML, modal_content, modalFooterHTML = ''; + if(training_data().answer !== '') { + modalInputAnswer = '

'; + } + elemModal.classList.add('modal'); modalHTML = modalTemplate.replace('{{title}}', options.title || 'Новое окно'); - //modalHTML = modalHTML.replace('{{content}}', options.content || ''); if (options.footerButtons) { for (var i = 0, length = options.footerButtons.length; i < length; i++) { var modalFooterButton = modalButtonTemplate.replace('{{button_class}}', options.footerButtons[i].class); modalFooterButton = modalFooterButton.replace('{{button_handler}}', options.footerButtons[i].handler); - modalFooterButton = modalFooterButton.replace('{{button_text}}', options.footerButtons[i].text); + modalFooterButton = modalFooterButton.replace('{{button_text}}', options.footerButtons[i].text) + modalInputAnswer; modalFooterHTML += modalFooterButton; } modalFooterHTML = modalFooterTemplate.replace('{{buttons}}', modalFooterHTML); diff --git a/modules_data/steps/step.php b/modules_data/steps/step.php index 15b8c9e2..70e68993 100644 --- a/modules_data/steps/step.php +++ b/modules_data/steps/step.php @@ -1,16 +1,42 @@ $short_name; +$step = $manager->getRegistered()[$short_name]; $button_text = 'Продолжить'; $time = time(); -$token = password_hash($manager->getDatabaseRecords()->api_token . $time . $user->info['id'], PASSWORD_DEFAULT); +$token = password_hash($manager->getDatabaseData()->api_token . $time . $user->info['id'], PASSWORD_DEFAULT); +?> + + + + +