From ee0715c3b209331b22a6c249ed618d14cef42a04 Mon Sep 17 00:00:00 2001 From: Andrew nuark G Date: Mon, 16 May 2022 01:28:21 +0700 Subject: [PATCH] W12 done --- w12/.gitignore | 2 + w12/.htaccess | 5 + w12/composer.json | 21 ++ w12/composer.lock | 323 ++++++++++++++++++++ w12/index.php | 436 +++++++++++++++++++++++++++ w12/src/database.php | 222 ++++++++++++++ w12/src/helpers.php | 13 + w12/views/edit-menu.twig | 99 ++++++ w12/views/gallery.twig | 20 ++ w12/views/image.twig | 26 ++ w12/views/import.twig | 51 ++++ w12/views/index.twig | 108 +++++++ w12/views/login.twig | 27 ++ w12/views/lookup-tables.twig | 50 +++ w12/views/propose-auth.twig | 12 + w12/views/recover-password-mail.twig | 4 + w12/views/recover-password.twig | 32 ++ w12/views/register.twig | 24 ++ w12/views/search.twig | 50 +++ 19 files changed, 1525 insertions(+) create mode 100644 w12/.gitignore create mode 100644 w12/.htaccess create mode 100644 w12/composer.json create mode 100644 w12/composer.lock create mode 100644 w12/index.php create mode 100644 w12/src/database.php create mode 100644 w12/src/helpers.php create mode 100644 w12/views/edit-menu.twig create mode 100644 w12/views/gallery.twig create mode 100644 w12/views/image.twig create mode 100644 w12/views/import.twig create mode 100644 w12/views/index.twig create mode 100644 w12/views/login.twig create mode 100644 w12/views/lookup-tables.twig create mode 100644 w12/views/propose-auth.twig create mode 100644 w12/views/recover-password-mail.twig create mode 100644 w12/views/recover-password.twig create mode 100644 w12/views/register.twig create mode 100644 w12/views/search.twig diff --git a/w12/.gitignore b/w12/.gitignore new file mode 100644 index 0000000..a496ede --- /dev/null +++ b/w12/.gitignore @@ -0,0 +1,2 @@ +*.jpg +/vendor \ No newline at end of file diff --git a/w12/.htaccess b/w12/.htaccess new file mode 100644 index 0000000..aa7a5f6 --- /dev/null +++ b/w12/.htaccess @@ -0,0 +1,5 @@ +RewriteEngine on +RewriteCond %{SCRIPT_FILENAME} !-f +RewriteCond %{SCRIPT_FILENAME} !-d +RewriteCond %{SCRIPT_FILENAME} !-l +RewriteRule ^(.*)$ index.php/$1 \ No newline at end of file diff --git a/w12/composer.json b/w12/composer.json new file mode 100644 index 0000000..29fb0ae --- /dev/null +++ b/w12/composer.json @@ -0,0 +1,21 @@ +{ + "name": "nuark/w12", + "description": "Work 12 app", + "type": "project", + "license": "MIT", + "autoload": { + "psr-4": { + "Nuark\\W12\\": "src/" + } + }, + "authors": [ + { + "name": "Andrew", + "email": "me@nuark.xyz" + } + ], + "require": { + "pecee/simple-router": "4.3.7.2", + "twig/twig": "^3.3" + } +} diff --git a/w12/composer.lock b/w12/composer.lock new file mode 100644 index 0000000..8285b02 --- /dev/null +++ b/w12/composer.lock @@ -0,0 +1,323 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "28d991162beec76e49d4e126da6f453e", + "packages": [ + { + "name": "pecee/simple-router", + "version": "4.3.7.2", + "source": { + "type": "git", + "url": "https://github.com/skipperbent/simple-php-router.git", + "reference": "032a2ae7e0e2d876599758f85b61bc965a63ea7c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/skipperbent/simple-php-router/zipball/032a2ae7e0e2d876599758f85b61bc965a63ea7c", + "reference": "032a2ae7e0e2d876599758f85b61bc965a63ea7c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=7.1" + }, + "require-dev": { + "mockery/mockery": "^1", + "phpstan/phpstan": "^0", + "phpstan/phpstan-deprecation-rules": "^0", + "phpstan/phpstan-phpunit": "^0", + "phpstan/phpstan-strict-rules": "^0", + "phpunit/phpunit": "^7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Pecee\\": "src/Pecee/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Simon Sessingø", + "email": "simon.sessingoe@gmail.com" + } + ], + "description": "Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.", + "keywords": [ + "framework", + "input-handler", + "laravel", + "pecee", + "php", + "request-handler", + "route", + "router", + "routing", + "routing-engine", + "simple-php-router", + "url-handling" + ], + "support": { + "issues": "https://github.com/skipperbent/simple-php-router/issues", + "source": "https://github.com/skipperbent/simple-php-router/issues" + }, + "time": "2021-07-17T23:45:36+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.25.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "30885182c981ab175d4d034db0f6f469898070ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", + "reference": "30885182c981ab175d4d034db0f6f469898070ab", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-10-20T20:35:02+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.25.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-11-30T18:21:41+00:00" + }, + { + "name": "twig/twig", + "version": "v3.3.10", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "8442df056c51b706793adf80a9fd363406dd3674" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/8442df056c51b706793adf80a9fd363406dd3674", + "reference": "8442df056c51b706793adf80a9fd363406dd3674", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.3-dev" + } + }, + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.3.10" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2022-04-06T06:47:41+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/w12/index.php b/w12/index.php new file mode 100644 index 0000000..48b2cb0 --- /dev/null +++ b/w12/index.php @@ -0,0 +1,436 @@ + false, +]); + +// start session +session_start(); + +// create favicon route +SimpleRouter::get('/favicon.ico', function() { + return; +}); + +// create main page route +SimpleRouter::get('/', function() use($twig) { + if (getSessionVariable('user') !== null) { + $user = getSessionVariable('user'); + echo $twig->render('index.twig', [ + 'user' => $user, + 'menu' => json_decode(Database::getUserMenu($user)["data"]), + 'gallery' => Database::getPublishedImages() + ]); + } else { + echo $twig->render('propose-auth.twig'); + } +}); + +// create register route +SimpleRouter::get('/register', function() use($twig) { + if (getSessionVariable('user') !== null) { + return response()->redirect('/'); + } + // get error from get parameters + $error = input()->get('error', ''); + echo $twig->render('register.twig', [ + 'error' => htmlspecialchars($error), + ]); +}); + +// create register POST route +SimpleRouter::post('/register', function() { + $login = trim(input()->post('login', '')); + $password = trim(input()->post('password', '')); + + // if login or password is empty, redirect to register page with error + if (empty($login) || empty($password)) { + return response()->redirect('/register?error=Empty login or password given!'); + } + + // try create user if ok - redirect to login page with message + try { + Database::createUser($login, $password); + $user = Database::getUser($login); + Database::createEmptyMenuForUser($user); + response()->redirect('/login?message=User created'); + } catch (PDOException $e) { + response()->redirect('/register?error=User already exists!'); + } +}); + +// create login route +SimpleRouter::get('/login', function() use($twig) { + if (getSessionVariable('user') !== null) { + return response()->redirect('/'); + } + // get message from get parameters + $message = input()->get('message', ''); + // get error from get parameters + $error = input()->get('error', ''); + echo $twig->render('login.twig', [ + 'message' => htmlspecialchars($message), + 'error' => htmlspecialchars($error), + ]); +}); + +// create login POST route +SimpleRouter::post('/login', function() { + $login = trim(input()->post('login', '')); + $password = trim(input()->post('password', '')); + + // if login or password is empty, redirect to register page with error + if (empty($login) || empty($password)) { + return response()->redirect('/login?error=Empty login or password given!'); + } + if (!Database::userExists($login)) { + return response()->redirect('/login?error=User does not exist!'); + } + if (!Database::verifyUser($login, $password)) { + return response()->redirect('/login?error=Wrong password!'); + } + + $user = Database::getUser($login); + // set session variables + setSessionVariable('user', $user); + // redirect to index page + response()->redirect('/'); +}); + +// create logout route +SimpleRouter::get('/logout', function() { + // unset session variables + unsetSessionVariable('user'); + // redirect to index page + response()->redirect('/'); +}); + +// create menu editing route +SimpleRouter::get('/editMenu', function() use($twig) { + if (getSessionVariable('user') === null) { + return response()->redirect('/'); + } + $user = getSessionVariable('user'); + echo $twig->render('edit-menu.twig', [ + 'user' => $user, + 'menu' => json_decode(Database::getUserMenu($user)["data"]), + ]); +}); + +// create menu save POST route +SimpleRouter::post('/editMenu', function() { + if (getSessionVariable('user') === null) { + return response()->json([ + 'error' => "Not authorized", + 'code' => 403, + ]); + } + try { + $user = getSessionVariable('user'); + $menuData = []; + $menu = input()->post('menu', []); + for ($i = 0; $i < count($menu->value); $i++) { + array_push($menuData, [ + 'url' => $menu->value[$i]->value['url']->value, + 'name' => $menu->value[$i]->value['name']->value, + ]); + } + Database::setUserMenu($user, json_encode($menuData)); + response()->json([ + 'data' => json_encode($menuData), + 'code' => 200, + ]); + } catch (PDOException $e) { + response()->json([ + 'error' => json_encode($e->getMessage()), + 'code' => 500, + ]); + } +}); + +// create tables lookup route +SimpleRouter::get('/lookupTables', function() use($twig) { + if (getSessionVariable('user') === null) { + return response()->redirect('/'); + } + $tables = []; + $tableData = []; + + $user = getSessionVariable('user'); + $table = input()->get('table', ''); + $sort = input()->get('sort', 'DESC'); + $col = input()->get('col', ''); + if ($table !== '') { + $cols = Database::getTableColumns($table); + if ($col === '') { + $col = $cols[0]['column_name']; + } else { + $col = $col->value; + } + $tableData = [ + "header" => $cols, + "data" => Database::getAnyTableSorted($table, $col, $sort), + ]; + } else { + $tables = array_map(function($tbl) { + return $tbl["table_name"]; + }, Database::getAvailableTables()); + } + echo $twig->render('lookup-tables.twig', [ + 'user' => $user, + 'table' => $table, + 'tables' => $tables, + 'tableData' => $tableData + ]); +}); + +// create POST router for image uploading +SimpleRouter::post('/uploadImage', function() { + if (getSessionVariable('user') === null) { + return response()->json([ + 'error' => "Not authorized", + 'code' => 403, + ]); + } + $user = getSessionVariable('user'); + $file = input()->file('file'); + $fileType = $file->type; + if ($fileType === 'image/jpeg') { + $filename = $user['login'] . '_' . time() . '.jpg'; + $fullpath = 'data/full/' . $filename; + $file->move($fullpath); + + + $size = getimagesize($fullpath); + $ratio = $size[0]/$size[1]; // width/height + if( $ratio > 1) { + $width = 200; + $height = 200/$ratio; + } + else { + $width = 200*$ratio; + $height = 200; + } + $thumb = imagecreatetruecolor($width, $height); + $source = imagecreatefromjpeg($fullpath); + + imagecopyresized($thumb, $source, 0, 0, 0, 0, $width, $height, $size[0], $size[1]); + $color = imagecolorallocate($thumb, 255, 0, 0); + imagestring($thumb, 2, 2, 2, "Watermark text :)", $color); + imagejpeg($thumb, 'data/thumb/' . $filename); + + try { + Database::addImageToGallery($filename, $user); + } catch (PDOException $e) { + return response()->json([ + 'error' => json_encode($e->getMessage()), + 'code' => 500, + ]); + } + + return response()->json([ + 'code' => 200, + ]); + } else { + return response()->json([ + 'error' => "Wrong file type", + 'code' => 400, + ]); + } +}); + +// create gallery route +SimpleRouter::get('/gallery', function() use($twig) { + if (getSessionVariable('user') === null) { + return response()->redirect('/'); + } + $user = getSessionVariable('user'); + $images = Database::getUsersImages($user); + echo $twig->render('gallery.twig', [ + 'user' => $user, + 'images' => $images, + ]); +}); + +// create image route +SimpleRouter::get('/image/{id}', function($id) use($twig) { + if (getSessionVariable('user') === null) { + return response()->redirect('/'); + } + $user = getSessionVariable('user'); + $image = Database::getImage($id); + if ($image['user_id'] !== $user['id']) { + return response()->httpCode(403); + } + + echo $twig->render('image.twig', [ + 'user' => $user, + 'image' => $image, + ]); +}); + +// create image info save POST route +SimpleRouter::post('/image/{id}', function($id) { + if (getSessionVariable('user') === null) { + return response()->redirect('/'); + } + $user = getSessionVariable('user'); + $image = Database::getImage($id); + if ($image['user_id'] !== $user['id']) { + return response()->httpCode(403); + } + + $description = input()->post('description', '')->value; + $published = boolval(input()->post('published', 0)->value)?"true":"false"; + var_dump($published); + try { + Database::updateImage($id, $description, $published); + } catch (PDOException $e) { + return response()->json([ + 'error' => json_encode($e->getMessage()), + 'code' => 500, + ]); + } + + response()->redirect('/'); +}); + +// create image DELETE route +SimpleRouter::get('/image/{id}/delete', function($id) { + if (getSessionVariable('user') === null) { + return response()->redirect('/'); + } + $user = getSessionVariable('user'); + $image = Database::getImage($id); + if ($image['user_id'] !== $user['id']) { + return response()->httpCode(403); + } + unlink('data/full/' . $image['filename']); + unlink('data/thumb/' . $image['filename']); + Database::deleteImage($id); + response()->redirect('/gallery'); +}); + +// create image serving route +SimpleRouter::all('/images', function() { + $type = input()->get('type', 'thumb'); + $filename = input()->get('filename', ''); + if (file_exists("data/$type/$filename")) { + header("Content-type: image/jpeg"); + return file_get_contents("data/$type/$filename"); + } else { + response()->httpCode(404); + } +}); + +// create export route +SimpleRouter::get('/export', function() { + if (getSessionVariable('user') === null) { + return response()->redirect('/'); + } + header("Content-type: application/xml"); + echo Database::exportUsersXML(); +}); + +// create import route + +// create gallery route +SimpleRouter::get('/import', function() use($twig) { + if (getSessionVariable('user') === null) { + return response()->redirect('/'); + } + $user = getSessionVariable('user'); + $message = input()->get('message', ''); + $error = input()->get('error', ''); + echo $twig->render('import.twig', [ + 'user' => $user, + 'message' => htmlspecialchars($message), + 'error' => htmlspecialchars($error), + ]); +}); + +// create import POST route +SimpleRouter::post('/import', function() { + if (getSessionVariable('user') === null) { + return response()->redirect('/'); + } + $xml = input()->post('xml')->value; + if ($xml) { + try { + list($ok, $fail) = Database::importUsersXML($xml); + response()->redirect("/import?message=OK: $ok, FAIL: $fail"); + } catch (Exception $e) { + response()->redirect("/import?error=" . $e->getMessage()); + } + } else { + response()->redirect('/import?error=Error happened '); + } +}); + +// create image search route +SimpleRouter::get('/search', function() use($twig) { + if (getSessionVariable('user') === null) { + return response()->redirect('/'); + } + $user = getSessionVariable('user'); + $search = input()->get('search', '')->value; + $author = input()->get('author', '')->value; + $authors = Database::getUsersList(); + $images = []; + if ($search and $author) { + $images = Database::searchImages("%".$search."%", $author); + } + echo $twig->render('search.twig', [ + 'user' => $user, + 'images' => $images, + 'search' => $search, + 'author' => $author, + 'authors' => $authors, + ]); +}); + +// create recover password route +SimpleRouter::get('/recover-password', function() use($twig) { + $message = input()->get('message', ''); + $error = input()->get('error', ''); + $mail = input()->get('mail', ''); + echo $twig->render('recover-password.twig', [ + 'message' => htmlspecialchars($message), + 'error' => htmlspecialchars($error), + 'mail' => $mail, + ]); +}); + +// create recover password POST route +SimpleRouter::post('/recover-password', function() use ($twig) { + $login = input()->post('login', '')->value; + if ($login) { + try { + $user = Database::getUser($login); + if ($user) { + $newPassword = Database::generateUserNewPassword($login); + $sentMail = $twig->render('recover-password-mail.twig', [ + 'login' => $login, + 'password' => $newPassword, + ]); + response()->redirect('/recover-password?message=Success! We sent you an email with your new password&mail=' . urlencode($sentMail)); + } else { + response()->redirect('/recover-password?error=User not found'); + } + } catch (PDOException $e) { + response()->redirect('/recover-password?error=' . $e->getMessage()); + } + } else { + response()->redirect('/recover-password?error=Error happened'); + } +}); + +SimpleRouter::start(); diff --git a/w12/src/database.php b/w12/src/database.php new file mode 100644 index 0000000..17bd92d --- /dev/null +++ b/w12/src/database.php @@ -0,0 +1,222 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false, + ]; + + public static function getDB() { + if (!isset(self::$db)) { + try { + self::$db = new PDO(self::$dsn, self::$username, self::$password, self::$options); + } catch (PDOException $e) { + throw new PDOException($e->getMessage(), (int)$e->getCode()); + } + } + return self::$db; + } + + public static function userExists($login) { + $db = self::getDB(); + $query = $db->prepare('SELECT * FROM users WHERE login = :login'); + $query->bindParam(':login', $login); + $query->execute(); + return $query->rowCount() > 0; + } + + public static function createUser($login, $password) { + $db = self::getDB(); + $query = $db->prepare('INSERT INTO users (login, password) VALUES (:login, :password)'); + $query->bindParam(':login', $login); + $query->bindParam(':password', $password); + $query->execute(); + } + + public static function verifyUser($login, $password) { + $db = self::getDB(); + $query = $db->prepare('SELECT * FROM users WHERE login = :login AND password = :password'); + $query->bindParam(':login', $login); + $query->bindParam(':password', $password); + $query->execute(); + return $query->rowCount() > 0; + } + + public static function getUser($login) { + $db = self::getDB(); + $query = $db->prepare('SELECT * FROM users WHERE login = :login'); + $query->bindParam(':login', $login); + $query->execute(); + return $query->fetch(); + } + + public static function generateUserNewPassword($login) { + $db = self::getDB(); + $query = $db->prepare('UPDATE users SET password = :password WHERE login = :login'); + $query->bindParam(':login', $login); + $query->bindParam(':password', $password); + $password = bin2hex(random_bytes(8)); + $query->execute(); + return $password; + } + + public static function getUsersList() { + $db = self::getDB(); + $query = $db->prepare('SELECT id, login FROM users'); + $query->execute(); + return $query->fetchAll(); + } + + public static function createEmptyMenuForUser($user) { + $db = self::getDB(); + $query = $db->prepare('INSERT INTO menu (user_id) VALUES (:user_id)'); + $query->bindParam(':user_id', $user["id"]); + $query->execute(); + } + + public static function getUserMenu($user) { + $db = self::getDB(); + $query = $db->prepare('SELECT data FROM menu WHERE user_id = :user_id'); + $query->bindParam(':user_id', $user["id"]); + $query->execute(); + return $query->fetch(); + } + + public static function setUserMenu($user, $data) { + $db = self::getDB(); + $query = $db->prepare('UPDATE menu SET data = :data WHERE user_id = :user_id'); + $query->bindParam(':data', $data); + $query->bindParam(':user_id', $user["id"]); + $query->execute(); + } + + public static function addImageToGallery($filename, $user) { + $db = self::getDB(); + $query = $db->prepare('INSERT INTO images (user_id, filename) VALUES (:user_id, :filename)'); + $query->bindParam(':user_id', $user["id"]); + $query->bindParam(':filename', $filename); + $query->execute(); + } + + public static function getUsersImages($user) { + $db = self::getDB(); + $query = $db->prepare('SELECT id, filename FROM images WHERE user_id = :user_id'); + $query->bindParam(':user_id', $user["id"]); + $query->execute(); + return $query->fetchAll(); + } + + public static function getPublishedImages() { + $db = self::getDB(); + $query = $db->prepare('SELECT id, filename FROM images WHERE published = TRUE'); + $query->execute(); + return $query->fetchAll(); + } + + public static function getImage($id) { + $db = self::getDB(); + $query = $db->prepare('SELECT * FROM images WHERE id = :id'); + $query->bindParam(':id', $id); + $query->execute(); + return $query->fetch(); + } + + public static function searchImages($search, $author) { + $db = self::getDB(); + if (intval($author) !== -1) { + $query = $db->prepare('SELECT * FROM images WHERE description LIKE :search AND user_id = :author'); + $query->bindParam(':search', $search); + $query->bindParam(':author', $author); + } else { + $query = $db->prepare('SELECT * FROM images WHERE description LIKE :search'); + $query->bindParam(':search', $search); + } + $query->execute(); + return $query->fetchAll(); + } + + public static function updateImage($id, $description, $published) { + $db = self::getDB(); + $query = $db->prepare('UPDATE images SET published = :published, description = :description WHERE id = :id'); + $query->bindParam(':id', $id); + $query->bindParam(':published', $published); + $query->bindParam(':description', $description); + $query->execute(); + } + + public static function deleteImage($id) { + $db = self::getDB(); + $query = $db->prepare('DELETE FROM images WHERE id = :id'); + $query->bindParam(':id', $id); + $query->execute(); + } + + public static function getAnyTableSorted($table, $sort_by, $sort_order) { + $db = self::getDB(); + $query = $db->prepare('SELECT * FROM ' . $table . ' ORDER BY ' . $sort_by . ' ' . $sort_order); + $query->execute(); + return $query->fetchAll(); + } + + public static function getTableColumns($table) { + $db = self::getDB(); + $query = $db->prepare('SELECT column_name FROM information_schema.columns WHERE table_schema = \'public\' AND table_name = :table_name'); + $query->bindParam(':table_name', $table); + $query->execute(); + return $query->fetchAll(); + } + + public static function getAvailableTables() { + $db = self::getDB(); + $query = $db->prepare('SELECT table_name FROM information_schema.tables WHERE table_schema = \'public\' ORDER BY table_name'); + $query->execute(); + return $query->fetchAll(); + } + + public static function exportUsersXML() { + $db = self::getDB(); + $query = $db->prepare('SELECT * FROM users'); + $query->execute(); + $users = $query->fetchAll(); + $xml = new DOMDocument('1.0', 'UTF-8'); + $xml->formatOutput = true; + $root = $xml->createElement('users'); + $xml->appendChild($root); + foreach ($users as $user) { + $user_node = $xml->createElement('user'); + $root->appendChild($user_node); + $user_node->setAttribute('exported_id', $user['id']); + $user_node->setAttribute('login', $user['login']); + $user_node->setAttribute('password', $user['password']); + } + return $xml->saveXML(); + } + + public static function importUsersXML($xmlData) { + $xml = new DOMDocument(); + $xml->loadXML($xmlData); + $users = $xml->getElementsByTagName('user'); + + $ok = 0; + $fail = 0; + foreach ($users as $user) { + try { + $login = $user->getAttribute('login'); + $password = $user->getAttribute('password'); + self::createUser($login, $password); + $user = self::getUser($login); + self::createEmptyMenuForUser($user); + $ok += 1; + } catch (PDOException $e) { + $fail += 1; + } + } + return [$ok, $fail]; + } +} +?> \ No newline at end of file diff --git a/w12/src/helpers.php b/w12/src/helpers.php new file mode 100644 index 0000000..1d4e187 --- /dev/null +++ b/w12/src/helpers.php @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/w12/views/edit-menu.twig b/w12/views/edit-menu.twig new file mode 100644 index 0000000..04e6087 --- /dev/null +++ b/w12/views/edit-menu.twig @@ -0,0 +1,99 @@ + + + + + + + Document + + + {{user.login}}, вы редактируете меню. На главную +
+ Меню: + + +
+
+ + + +
+ + + + \ No newline at end of file diff --git a/w12/views/gallery.twig b/w12/views/gallery.twig new file mode 100644 index 0000000..721df04 --- /dev/null +++ b/w12/views/gallery.twig @@ -0,0 +1,20 @@ + + + + + + + Document + + + {{user.login}}, вы смотрите свою галерею. На главную +
+ {% for image in images %} + + {{image.id}} + + {% else %} +

Нет изображений

+ {% endfor %} + + \ No newline at end of file diff --git a/w12/views/image.twig b/w12/views/image.twig new file mode 100644 index 0000000..6d36452 --- /dev/null +++ b/w12/views/image.twig @@ -0,0 +1,26 @@ + + + + + + + Document + + + {{user.login}} осматривает картинку... На главную +
+ {{image.filename}} +
+ Миниатюра +
+
+ +
+ Опубликовано? + +
+ +
+ Удалить + + \ No newline at end of file diff --git a/w12/views/import.twig b/w12/views/import.twig new file mode 100644 index 0000000..0263252 --- /dev/null +++ b/w12/views/import.twig @@ -0,0 +1,51 @@ + + + + + + + Document + + + {% if message %} +
{{message}}
+ {% endif %} + {% if error %} +
{{error}}
+ {% endif %} +

Import page

+

Drop XML file on page or enter content down here

+
+ +
+ +
+ + + \ No newline at end of file diff --git a/w12/views/index.twig b/w12/views/index.twig new file mode 100644 index 0000000..d18661f --- /dev/null +++ b/w12/views/index.twig @@ -0,0 +1,108 @@ + + + + + + + Document + + + + + Привет, {{user.login}}! Выйти +
+ Меню: + {% if menu %} + + {% else %} +

Пусто

+ {% endif %} +
+
+ Загрузить изображение +
+
+ Действия: + +
+ Общая галерея: + + + + \ No newline at end of file diff --git a/w12/views/login.twig b/w12/views/login.twig new file mode 100644 index 0000000..a18f6d3 --- /dev/null +++ b/w12/views/login.twig @@ -0,0 +1,27 @@ + + + + + + + Document + + + {% if message %} +
{{message}}
+ {% endif %} + {% if error %} +
{{error}}
+ {% endif %} +

Login page

+
+ + + +
+
+ Registration +
+ Recover password + + \ No newline at end of file diff --git a/w12/views/lookup-tables.twig b/w12/views/lookup-tables.twig new file mode 100644 index 0000000..2cc0185 --- /dev/null +++ b/w12/views/lookup-tables.twig @@ -0,0 +1,50 @@ + + + + + + + Document + + + {% if table %} + {{user.login}}, вы смотрите таблицу {{table}}. Отмена + {% else %} + {{user.login}}, вы смотрите таблицы. На главную + {% endif %} +
+ {% if table %} + + + + {% for item in tableData.header %} + + {% endfor %} + + + + {% for row in tableData.data %} + + {% for col in row %} + + {% endfor %} + + {% endfor %} + +
+ [ASC] + {{item.column_name}} + [DESC] +
{{col}}
+ {% else %} +
+ + +
+ {% endif %} + + \ No newline at end of file diff --git a/w12/views/propose-auth.twig b/w12/views/propose-auth.twig new file mode 100644 index 0000000..cf2dbd2 --- /dev/null +++ b/w12/views/propose-auth.twig @@ -0,0 +1,12 @@ + + + + + + + Document + + + Вам нужно войти или зарегистрироваться для работы с системой. + + \ No newline at end of file diff --git a/w12/views/recover-password-mail.twig b/w12/views/recover-password-mail.twig new file mode 100644 index 0000000..009baad --- /dev/null +++ b/w12/views/recover-password-mail.twig @@ -0,0 +1,4 @@ +Hello, {{login}}! +You have requested password change, so we generated you a new one (without backtics): `{{password}}`. +
+Please be sure to delete this message after you have logged in. For now you cannot change your password by yourself. \ No newline at end of file diff --git a/w12/views/recover-password.twig b/w12/views/recover-password.twig new file mode 100644 index 0000000..141bde8 --- /dev/null +++ b/w12/views/recover-password.twig @@ -0,0 +1,32 @@ + + + + + + + Document + + + {% if mail %} +
{{mail | raw}}
+
+ {% endif %} + {% if message %} +
{{message}}
+
+ {% endif %} + {% if error %} +
{{error}}
+
+ {% endif %} +

Password recovery page

+
+ + +
+
+ Registration +
+ Login + + \ No newline at end of file diff --git a/w12/views/register.twig b/w12/views/register.twig new file mode 100644 index 0000000..bdfa483 --- /dev/null +++ b/w12/views/register.twig @@ -0,0 +1,24 @@ + + + + + + + Document + + + {% if error %} +
{{error}}
+ {% endif %} +

Registration page

+
+ + + +
+
+ Login +
+ Recover password + + \ No newline at end of file diff --git a/w12/views/search.twig b/w12/views/search.twig new file mode 100644 index 0000000..83196dc --- /dev/null +++ b/w12/views/search.twig @@ -0,0 +1,50 @@ + + + + + + + Document + + + + + {{user.login}} что-то ищет 🤔 На главную +
+
+ + + +
+ {% if images %} + Результаты поиска: +
+ {% for image in images %} + + {{image.filename}} + + {% else %} +

Пусто

+ {% endfor %} + {% else %} +

Пусто

+ {% endif %} +
+ + \ No newline at end of file