diff --git a/app/Controllers/Api/Admin/Models/RequestHandler.php b/app/Controllers/Api/Admin/Models/RequestHandler.php new file mode 100644 index 0000000..d4fc54b --- /dev/null +++ b/app/Controllers/Api/Admin/Models/RequestHandler.php @@ -0,0 +1,34 @@ +<?php + +namespace App\Controllers\Api\Admin\Models; + + + +use App\Services\{Auth, Router, GenerateRandomStr, DB, Json, EXIF}; +use App\Models\{User, Vote, Photo}; + + +class RequestHandler +{ + public function __construct() + { + + $id = explode('/', $_SERVER['REQUEST_URI'])[5]; + $type = explode('/', $_SERVER['REQUEST_URI'])[6]; + $modelrequest = DB::query('SELECT * FROM entities_requests WHERE id=:id', array(':id' => $id))[0]; + if ($modelrequest) { + if ($type === 'accept') { + DB::query('INSERT INTO entities_data VALUES (\'0\', :title, :createdate, :entityid, NULL, :content)', array(':title' => $modelrequest['title'], ':createdate' => time(), ':entityid' => $modelrequest['entityid'], ':content' => $modelrequest['data'])); + DB::query('UPDATE entities_requests SET status=1 WHERE id=:id', array(':id' => $id)); + } else if ($type === 'decline') { + DB::query('UPDATE entities_requests SET status=2 WHERE id=:id', array(':id' => $id)); + } + } + echo json_encode( + array( + 'errorcode' => '0', + 'error' => 0, + ) + ); + } +} diff --git a/app/Controllers/Api/Admin/CreateNews.php b/app/Controllers/Api/Admin/News/Create.php similarity index 88% rename from app/Controllers/Api/Admin/CreateNews.php rename to app/Controllers/Api/Admin/News/Create.php index b44e5d1..d81b9d9 100644 --- a/app/Controllers/Api/Admin/CreateNews.php +++ b/app/Controllers/Api/Admin/News/Create.php @@ -1,6 +1,6 @@ <?php -namespace App\Controllers\Api\Admin; +namespace App\Controllers\Api\Admin\News; @@ -8,7 +8,7 @@ use App\Services\{Auth, Router, GenerateRandomStr, DB, Json, EXIF}; use App\Models\{User, Vote, Photo}; -class CreateNews +class Create { public function __construct() { diff --git a/app/Controllers/Api/Admin/News/Delete.php b/app/Controllers/Api/Admin/News/Delete.php new file mode 100644 index 0000000..6ae7715 --- /dev/null +++ b/app/Controllers/Api/Admin/News/Delete.php @@ -0,0 +1,24 @@ +<?php + +namespace App\Controllers\Api\Admin\News; + + + +use App\Services\{Auth, Router, GenerateRandomStr, DB, Json, EXIF}; +use App\Models\{User, Vote, Photo}; + + +class Delete +{ + public function __construct() + { + $postId = explode('/', $_SERVER['REQUEST_URI'])[4]; + DB::query('DELETE FROM news WHERE id=:id', array(':id' => $postId)); + echo json_encode( + array( + 'errorcode' => 0, + 'error' => 0 + ) + ); + } +} diff --git a/app/Controllers/Api/Admin/LoadNews.php b/app/Controllers/Api/Admin/News/Load.php similarity index 59% rename from app/Controllers/Api/Admin/LoadNews.php rename to app/Controllers/Api/Admin/News/Load.php index 5f00b95..01d20ef 100644 --- a/app/Controllers/Api/Admin/LoadNews.php +++ b/app/Controllers/Api/Admin/News/Load.php @@ -1,6 +1,6 @@ <?php -namespace App\Controllers\Api\Admin; +namespace App\Controllers\Api\Admin\News; @@ -8,13 +8,14 @@ use App\Services\{Auth, Router, GenerateRandomStr, DB, Json, EXIF, Date}; use App\Models\{User, Vote, Photo}; -class LoadNews +class Load { public function __construct() { $news = DB::query('SELECT * FROM news ORDER BY id'); foreach ($news as $n) { - echo '<div class="card mb-3"><div class="card-body">' . Date::zmdate($n['time']) . '<br>' . $n['body'] . '</div></div>'; + $nn = new \App\Models\Admin\News($n['id']); + $nn->view(); } } } diff --git a/app/Controllers/Api/Login.php b/app/Controllers/Api/Login.php index a734807..3d3f889 100644 --- a/app/Controllers/Api/Login.php +++ b/app/Controllers/Api/Login.php @@ -15,10 +15,9 @@ class Login { $username = $_POST['username']; $password = $_POST['password']; - if (DB::query('SELECT email FROM users WHERE email=:username OR username=:username', array(':username' => $username))) { - $email = DB::query('SELECT email FROM users WHERE email=:username OR username=:username', array(':username' => $username))[0]['email']; + if (DB::query('SELECT email FROM users WHERE (LOWER(username) LIKE :username1) OR (LOWER(email) LIKE :username2)', array(':username1' => '%'.$username.'%', ':username2' => '%'.$username.'%'))) { + $email = DB::query('SELECT email FROM users WHERE (LOWER(username) LIKE :username1) OR (LOWER(email) LIKE :username2)', array(':username1' => '%'.$username.'%', ':username2' => '%'.$username.'%'))[0]['email']; if (password_verify($password, DB::query('SELECT password FROM users WHERE email=:username', array(':username' => $email))[0]['password'])) { - $cstrong = True; $token = GenerateRandomStr::gen_uuid(); $user_id = DB::query('SELECT id FROM users WHERE email=:username', array(':username' => $email))[0]['id']; @@ -50,7 +49,7 @@ class Login $iv = openssl_random_pseudo_bytes(16); $encryptedIp = openssl_encrypt($ip, 'AES-256-CBC', $encryptionKey, 0, $iv); $encryptedLoc = openssl_encrypt($loc, 'AES-256-CBC', $encryptionKey, 0, $iv); - DB::query('INSERT INTO login_tokens VALUES (\'0\', :token, :user_id, :device, :os, :ip, :loc, :la, :crd)', array( + DB::query('INSERT INTO login_tokens VALUES (\'0\', :token, :user_id, :device, :os, :ip, :loc, :la, :crd, :iv)', array( ':token' => $token, ':user_id' => $user_id, ':device' => $device, @@ -58,7 +57,8 @@ class Login ':ip' => $encryptedIp, ':loc' => $encryptedLoc, ':la' => time(), - ':crd' => time() + ':crd' => time(), + ':iv' => $iv )); setcookie("NGALLERYSESS", $token, time() + 50 * 50 * 54 * 72, '/', NULL, NULL, TRUE); diff --git a/app/Controllers/ApiController.php b/app/Controllers/ApiController.php index fe64043..ae7c0ca 100644 --- a/app/Controllers/ApiController.php +++ b/app/Controllers/ApiController.php @@ -30,9 +30,11 @@ use \App\Controllers\Api\Users\LoadUser as UserLoad; use \App\Controllers\Api\Users\EmailVerify as EmailVerify; use \App\Controllers\Api\Users\Search as UsersSearch; use \App\Controllers\Api\Admin\Images\SetVisibility as AdminPhotoSetVisibility; -use \App\Controllers\Api\Admin\CreateNews as AdminCreateNews; -use \App\Controllers\Api\Admin\LoadNews as AdminLoadNews; +use \App\Controllers\Api\Admin\News\Create as AdminCreateNews; +use \App\Controllers\Api\Admin\News\Load as AdminLoadNews; +use \App\Controllers\Api\Admin\News\Delete as AdminDeleteNews; use \App\Controllers\Api\Admin\GetVehicleInputs as AdminGetVehicleInputs; +use \App\Controllers\Api\Admin\Models\RequestHandler as AdminModelsRequestHandler; use \App\Controllers\Api\Admin\GeoDB\Create as AdminGeoDBCreate; use \App\Controllers\Api\Admin\GeoDB\Load as AdminGeoDBLoad; use \App\Controllers\Api\Admin\GeoDB\Delete as AdminGeoDBDelete; @@ -121,6 +123,9 @@ class ApiController public static function admincreatenews() { return new AdminCreateNews(); } + public static function admindeletenews() { + return new AdminDeleteNews(); + } public static function adminloadnews() { return new AdminLoadNews(); } @@ -172,6 +177,9 @@ class ApiController public static function photoloadmap() { return new PhotoLoadMap(); } + public static function adminmodelsrequesthandler() { + return new AdminModelsRequestHandler(); + } } \ No newline at end of file diff --git a/app/Core/Routes.php b/app/Core/Routes.php index e97cea8..467f276 100644 --- a/app/Core/Routes.php +++ b/app/Core/Routes.php @@ -63,7 +63,8 @@ class Routes Router::get('/voting/sendpretend', 'ContestsController@sendpretend'); Router::get('/vehicle/edit', 'VehicleController@iedit'); - Router::get('/vehicle/dbedit', 'VehicleController@dbedit'); + Router::any('/vehicle/dbedit', 'VehicleController@dbedit'); + Router::any('/vehicle/$id', 'VehicleController@i'); Router::post('/api/upload', 'ApiController@upload'); Router::post('/api/profile/update', 'ApiController@updateprofile'); Router::post('/api/photo/comment', 'ApiController@photocomment'); @@ -89,9 +90,11 @@ class Routes if ($user->i('admin') > 0) { Router::any('/admin', 'AdminController@index'); Router::any('/api/admin/images/setvisibility', 'ApiController@adminsetvis'); - Router::any('/api/admin/createnews', 'ApiController@admincreatenews'); + Router::any('/api/admin/news/create', 'ApiController@admincreatenews'); + Router::any('/api/admin/news/$id/delete', 'ApiController@admindeletenews'); Router::any('/api/admin/loadnews', 'ApiController@adminloadnews'); Router::any('/api/admin/getvehicleinputs/$id', 'ApiController@admingetvehicleinputs'); + Router::any('/api/admin/models/requests/$id/$type', 'ApiController@adminmodelsrequesthandler'); Router::any('/api/admin/geodb/create', 'ApiController@admingeodbcreate'); Router::any('/api/admin/geodb/load', 'ApiController@admingeodbload'); Router::any('/api/admin/contests/createtheme', 'ApiController@admincontestscreatetheme'); @@ -103,6 +106,5 @@ class Routes } else { Router::redirect('/login?return='.$_SERVER['HTTP_REFERER']); } - Router::get('/vehicle/$id', 'VehicleController@i'); } } \ No newline at end of file diff --git a/app/Models/Admin/News.php b/app/Models/Admin/News.php new file mode 100644 index 0000000..eb8b9bd --- /dev/null +++ b/app/Models/Admin/News.php @@ -0,0 +1,27 @@ +<?php +namespace App\Models\Admin; +use \App\Services\{DB, Date}; + +class News { + + public $id; + public $table; + function __construct(int $id) { + $this->id = $id; + $result = DB::query("SELECT * FROM news WHERE id=:id", [':id' => $this->id]); + if (!empty($result)) { + $this->table = (object) $result[0]; + } else { + $this->table = (object) []; + } + } + public function i($key) { + return $this->table->$key ?? null; + } + public function view() { + echo '<div class="card mb-3"><div class="card-body"><i>' + . Date::zmdate($this->table->time) . '</i><br>' + . $this->table->body + . '<br><a class="btn btn-danger mt-3" href="#" onclick="deleteNews('.$this->id.'); return false;">Удалить</a></div></div>'; + } +} \ No newline at end of file diff --git a/app/Models/Comment.php b/app/Models/Comment.php index 40e9c96..6c4b4b2 100644 --- a/app/Models/Comment.php +++ b/app/Models/Comment.php @@ -150,7 +150,7 @@ class Comment $pinc = 'Закрепить'; echo '<div class="' . $this->class . ' comment" wid="' . $this->c['id'] . '">'; if ($photo->i('pinnedcomment_id') === $this->c['id']) { - echo '<i style="padding-bottom: 15px;">Комментарий закреплён</i>'; + echo '<i style="margin-bottom: 1px;">Комментарий закреплён</i><br>'; $pinc = 'Открепить'; } echo ' diff --git a/app/Services/Captcha.php b/app/Services/Captcha.php new file mode 100644 index 0000000..9c2cbae --- /dev/null +++ b/app/Services/Captcha.php @@ -0,0 +1,98 @@ +<?php +namespace App\Services; +use Exception; +class Captcha +{ + private $secretKey; + private $token; + private $remoteIp; + private $expectedAction; + private $expectedCdata; + + public function __construct(string $secretKey) + { + $this->secretKey = $secretKey; + } + + public function setToken(string $token): void + { + $this->token = $token; + } + + public function setRemoteIp(string $ip): void + { + $this->remoteIp = $ip; + } + + public function setExpectedAction(string $action): void + { + $this->expectedAction = $action; + } + + public function setExpectedCdata(string $cdata): void + { + $this->expectedCdata = $cdata; + } + + public function verify(): array + { + if (empty($this->token)) { + throw new Exception('Turnstile token is missing'); + } + + if (empty($this->secretKey)) { + throw new Exception('Secret key is not configured'); + } + + $data = [ + 'secret' => $this->secretKey, + 'response' => $this->token + ]; + + if ($this->remoteIp) { + $data['remoteip'] = $this->remoteIp; + } + + $url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify'; + $options = [ + 'http' => [ + 'header' => "Content-type: application/x-www-form-urlencoded\r\n", + 'method' => 'POST', + 'content' => http_build_query($data) + ] + ]; + + $context = stream_context_create($options); + $response = file_get_contents($url, false, $context); + + if ($response === false) { + throw new Exception('Failed to verify Turnstile token'); + } + + $result = json_decode($response, true); + + if (!is_array($result)) { + throw new Exception('Invalid response from Turnstile server'); + } + + if (!$result['success']) { + $errorCodes = $result['error-codes'] ?? ['unknown-error']; + throw new Exception('Turnstile verification failed: ' . implode(', ', $errorCodes)); + } + + $this->validateAdditionalParameters($result); + + return $result; + } + + private function validateAdditionalParameters(array $response): void + { + if ($this->expectedAction && ($response['action'] ?? '') !== $this->expectedAction) { + throw new Exception("Action mismatch. Expected: {$this->expectedAction}, Received: {$response['action']}"); + } + + if ($this->expectedCdata && ($response['cdata'] ?? '') !== $this->expectedCdata) { + throw new Exception("Cdata mismatch. Expected: {$this->expectedCdata}, Received: {$response['cdata']}"); + } + } +} \ No newline at end of file diff --git a/index.php b/index.php index 3c9b3a7..7a04f6f 100644 --- a/index.php +++ b/index.php @@ -11,7 +11,9 @@ class App { public static function start() { - error_reporting(E_ALL & ~E_WARNING); + ini_set('display_errors', 0); + ini_set('display_startup_errors', 0); + error_reporting(E_ALL); if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/ngallery.yaml')) { define("NGALLERY", Yaml::parse(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/ngallery.yaml'))['ngallery']); @@ -35,9 +37,8 @@ class App } } catch (PDOException $ex) { echo '<details><summary class="p20 s5" style="border:none; margin:0 -20px"><b>Произошла ошибка MySQL</b></summary>'.nl2br($ex).'</details>'; - } catch (Exception $ex) { - echo '<details><summary class="p20 s5" style="border:none; margin:0 -20px"><b>Произошла скриптовая ошибка PHP</b></summary>'.nl2br($ex).'</details>'; + echo '<pre><b>Произошла скриптовая ошибка PHP</b><br><br>'.nl2br($ex).'</pre>'; } } else { Page::set('Errors/Problems'); diff --git a/ngallery-example.yaml b/ngallery-example.yaml index 9107e4a..b40d367 100644 --- a/ngallery-example.yaml +++ b/ngallery-example.yaml @@ -8,8 +8,11 @@ ngallery: keywords: "" maintenance: false debug: true - alloweddomains: ["nativegallery.loc", "pub-f05d2c8192d549e4b52535d646e5909a.r2.dev"] + alloweddomains: ["example.com"] botkey: '' + logslocation: '/logs' + encryptionkey: '' + footerslogan: 'Aloha, Hawaii!' access: type: 'allow' countries: '' diff --git a/static/css/style.css b/static/css/style.css index 67232fe..cc12ba1 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -915,4 +915,17 @@ input.ml-button { height:22px; } bottom: 0; left: 0; right: 0; +} +.ix-photos.shine { + background-image: linear-gradient(270deg, rgba(100, 181, 239, 0) 48.44%, #adadad 75.52%, rgba(100, 181, 239, 0) 100%); + background-repeat: no-repeat; + animation: bg-move linear 2s infinite; +} +@-webkit-keyframes bg-move { + 0% { background-position: -500px 0; } + 100% { background-position: 1000px 0; } +} +@keyframes bg-move { + 0% { background-position: -500px 0; } + 100% { background-position: 1000px 0; } } \ No newline at end of file diff --git a/static/js/act.js b/static/js/act.js index 582bf9d..a64e69c 100644 --- a/static/js/act.js +++ b/static/js/act.js @@ -1,200 +1,272 @@ function createModal(id, type, value, modalid) { - if (type === 'EDIT_COMMENT') { - var modal = ` - <div id="`+modalid+`" class="modal" style="display: block;"> + if (type === "EDIT_COMMENT") { + var modal = + ` + <div id="` + + modalid + + `" class="modal" style="display: block;"> <div class="modal-content"> - <span data-close-modal-id="`+modalid+`" class="close">×</span> + <span data-close-modal-id="` + + modalid + + `" class="close">×</span> <h3><b>Отредактировать комментарий</b></h3> <div style="padding:0 11px 11px"> <textarea style=" width: 100%; - height: 200px;" name="wtext" id="bodypost__commedit`+id+`">`+value+`</textarea><br> + height: 200px;" name="wtext" id="bodypost__commedit` + + id + + `">` + + value + + `</textarea><br> <div class="cmt-submit"> - <button type="submit" onclick="editComment('` + id + `', document.getElementById('bodypost__commedit` + id + `').value, '`+modalid+`')" id="sbmt">Отредактировать</button>  Ctrl + Enter + <button type="submit" onclick="editComment('` + + id + + `', document.getElementById('bodypost__commedit` + + id + + `').value, '` + + modalid + + `')" id="sbmt">Отредактировать</button>  Ctrl + Enter </div> </div> </div> </div>`; } - if (type === 'DELETE_COMMENT') { - var modal = ` - <div id="`+modalid+`" class="modal" style="display: block;"> + if (type === "DELETE_COMMENT") { + var modal = + ` + <div id="` + + modalid + + `" class="modal" style="display: block;"> <div class="modal-content"> - <span data-close-modal-id="`+modalid+`" class="close">×</span> + <span data-close-modal-id="` + + modalid + + `" class="close">×</span> <h3><b>Удалить комментарий</b></h3> Вы действительно хотите удалить комментарий? Действие необратимо. <div style="margin-top: 35px;"> <div class="cmt-submit"> - <button type="submit" onclick="deleteComment('` + id + `', '`+modalid+`')" id="sbmt">Удалить</button> - <button data-close-modal-id="`+modalid+`" type="submit" id="sbmt">Отмена</button> + <button type="submit" onclick="deleteComment('` + + id + + `', '` + + modalid + + `')" id="sbmt">Удалить</button> + <button data-close-modal-id="` + + modalid + + `" type="submit" id="sbmt">Отмена</button> </div> </div> </div> </div>`; - } + } document.body.innerHTML += modal; } +document.addEventListener("click", function (event) { + // Получаем ID модального окна, которое нужно закрыть + var modalId = event.target.getAttribute("data-close-modal-id"); -document.addEventListener("click", function(event) { - // Получаем ID модального окна, которое нужно закрыть - var modalId = event.target.getAttribute("data-close-modal-id"); - - // Удаляем модальное окно по его ID - if (modalId) { - var modalToClose = document.getElementById(modalId); - if (modalToClose) { - modalToClose.remove(); // Удаляем модальное окно из DOM - } + // Удаляем модальное окно по его ID + if (modalId) { + var modalToClose = document.getElementById(modalId); + if (modalToClose) { + modalToClose.remove(); // Удаляем модальное окно из DOM } + } - // Проверяем, кликнули ли мы на само модальное окно - var modals = document.querySelectorAll(".modal"); - modals.forEach(function(modal) { - if (event.target === modal) { - modal.remove(); // Удаляем модальное окно из DOM - } - }); + // Проверяем, кликнули ли мы на само модальное окно + var modals = document.querySelectorAll(".modal"); + modals.forEach(function (modal) { + if (event.target === modal) { + modal.remove(); // Удаляем модальное окно из DOM + } + }); }); - - const pinComment = (postId) => { - $(document).ready(function() { - $.ajax({ - type: "POST", - url: '/api/photo/comment/'+postId+'/pin', - success: function(response) { - var jsonData = JSON.parse(response); - - console.log(response); - if (jsonData.errorcode == "1") { - Notify.noty('danger', JSON.stringify(response)); - } else { - if (jsonData.action == "pin") { - Notify.noty('success', 'Успешно закреплено!'); - } else { - Notify.noty('success', 'Успешно откреплено!'); - } - - const url = window.location.pathname; - const segments = url.split('/'); - const id = segments[2]; - console.log(segments); - $.ajax({ - - - type: "POST", - url: "/api/photo/getcomments/"+id, - processData: false, - async: true, - success: function(r) { - $('#posts').html(r) - - - }, - error: function(r) { - console.log(r) - } - - }); - } - } - }); - }); - } + $(document).ready(function () { + $.ajax({ + type: "POST", + url: "/api/photo/comment/" + postId + "/pin", + success: function (response) { + var jsonData = JSON.parse(response); + + console.log(response); + if (jsonData.errorcode == "1") { + Notify.noty("danger", JSON.stringify(response)); + } else { + if (jsonData.action == "pin") { + Notify.noty("success", "Успешно закреплено!"); + } else { + Notify.noty("success", "Успешно откреплено!"); + } + + const url = window.location.pathname; + const segments = url.split("/"); + const id = segments[2]; + console.log(segments); + $.ajax({ + type: "POST", + url: "/api/photo/getcomments/" + id, + processData: false, + async: true, + success: function (r) { + $("#posts").html(r); + }, + error: function (r) { + console.log(r); + }, + }); + } + }, + }); + }); +}; const editComment = (postId, body, modalid) => { - $(document).ready(function() { + $(document).ready(function () { + $.ajax({ + type: "POST", + url: "/api/photo/comment/" + postId + "/edit", + data: JSON.stringify({ value: body }), + success: function (response) { + var jsonData = JSON.parse(response); + + console.log(response); + if (jsonData.errorcode == "1") { + Notify.noty("danger", JSON.stringify(response)); + } else { + document.getElementById(modalid).style.display = "none"; + + Notify.noty("success", "Успешно отредактировано!"); + const url = window.location.pathname; + const segments = url.split("/"); + const id = segments[2]; $.ajax({ - type: "POST", - url: '/api/photo/comment/'+postId+'/edit', - data: JSON.stringify({ "value": body }), - success: function(response) { - var jsonData = JSON.parse(response); - - console.log(response); - if (jsonData.errorcode == "1") { - Notify.noty('danger', JSON.stringify(response)); - } else { - document.getElementById(modalid).style.display = "none"; - - Notify.noty('success', 'Успешно отредактировано!'); - const url = window.location.pathname; - const segments = url.split('/'); - const id = segments[2]; - $.ajax({ - - - type: "POST", - url: "/api/photo/getcomments/"+id, - processData: false, - async: true, - success: function(r) { - $('#posts').html(r) - - - }, - error: function(r) { - console.log(r) - } - - }); - } - } + type: "POST", + url: "/api/photo/getcomments/" + id, + processData: false, + async: true, + success: function (r) { + $("#posts").html(r); + }, + error: function (r) { + console.log(r); + }, }); - }); -} - - + } + }, + }); + }); +}; const deleteComment = (postId, modalid) => { - $(document).ready(function() { - $.ajax({ - type: "POST", - url: '/api/photo/comment/'+postId+'/delete', - success: function(response) { - var jsonData = JSON.parse(response); - - console.log(response); - if (jsonData.errorcode == "1") { - Notify.noty('danger', JSON.stringify(response)); - } else { - document.getElementById(modalid).style.display = "none"; - - Notify.noty('success', 'Успешно удалено!'); - const url = window.location.pathname; - const segments = url.split('/'); - const id = segments[segments.length - 1]; - const commcountElem = document.getElementById('commcount'); - let innerHTML = commcountElem.innerHTML; - let match = innerHTML.match(/>(\d+)</); - console.log(match); - if (match) { - let count = parseInt(match[1], 10) - 1; - console.log(count); - let newHTML = innerHTML.replace(match[1], count); - commcountElem.innerHTML = newHTML; - } - $.ajax({ - - - type: "POST", - url: "/api/photo/getcomments/"+id, - processData: false, - async: true, - success: function(r) { - $('#posts').html(r) - - - }, - error: function(r) { - console.log(r) - } - - }); - } - } - }); - }); - } + $(document).ready(function () { + $.ajax({ + type: "POST", + url: "/api/photo/comment/" + postId + "/delete", + success: function (response) { + var jsonData = JSON.parse(response); + + console.log(response); + if (jsonData.errorcode == "1") { + Notify.noty("danger", JSON.stringify(response)); + } else { + document.getElementById(modalid).style.display = "none"; + + Notify.noty("success", "Успешно удалено!"); + const url = window.location.pathname; + const segments = url.split("/"); + const id = segments[segments.length - 1]; + const commcountElem = document.getElementById("commcount"); + let innerHTML = commcountElem.innerHTML; + let match = innerHTML.match(/>(\d+)</); + console.log(match); + if (match) { + let count = parseInt(match[1], 10) - 1; + console.log(count); + let newHTML = innerHTML.replace(match[1], count); + commcountElem.innerHTML = newHTML; + } + $.ajax({ + type: "POST", + url: "/api/photo/getcomments/" + id, + processData: false, + async: true, + success: function (r) { + $("#posts").html(r); + }, + error: function (r) { + console.log(r); + }, + }); + } + }, + }); + }); +}; +const loadNews = () => { + $.ajax({ + type: "GET", + url: "/api/admin/loadnews", + + success: function (response) { + $("#news").html(response); + }, + }); +}; +const deleteNews = (postId) => { + $.ajax({ + type: "POST", + url: "/api/admin/news/" + postId + "/delete", + success: function (response) { + var jsonData = JSON.parse(response); + + if (jsonData.errorcode == "1") { + Notify.noty("danger", JSON.stringify(response)); + } else { + Notify.noty("success", "Успешно удалено!"); + + loadNews(); + } + }, + }); +}; +const createNews = () => { + $.ajax({ + type: "POST", + url: "/api/admin/news/create", + data: { + body: $("#body").val(), + }, + success: function (response) { + Notify.noty("success", "OK!"); + loadNews(); + }, + }); +}; + +const handleModelRequest = (modelId, type) => { + $.ajax({ + type: "POST", + url: "/api/admin/models/requests/" + modelId + "/" + type, + data: { + body: $("#body").val(), + }, + success: function (response) { + Notify.noty("success", "OK!"); + const mdlNumElement = document.getElementById("mdlnum"); + + if (mdlNumElement) { + let currentNumber = parseInt(mdlNumElement.textContent, 10); + if (!isNaN(currentNumber)) { + currentNumber--; + mdlNumElement.textContent = currentNumber.toString(); + if (currentNumber === 0) { + mdlNumElement.remove(); + } + } + } + $("#mdl" + modelId).remove(); + }, + }); +}; diff --git a/static/js/comments.js b/static/js/comments.js index b04a9c7..0657b4f 100644 --- a/static/js/comments.js +++ b/static/js/comments.js @@ -215,12 +215,12 @@ if ((e.which == 10 || e.which == 13) && e.ctrlKey) $("#f1").submit(); }) .on("focus", function () { - navLock = true; - saveSelection(); - }) - .on("blur", function () { - navLock = false; - }) + navLock = true; + saveSelection(); + }) + .on("blur", function () { + navLock = false; + }) .on("blur", function () { navLock = false; }); @@ -278,7 +278,7 @@ // Вставляем в сохраненную позицию if (lastSelection) { lastSelection.insertNode(img); - + // Обновляем позицию курсора const newRange = document.createRange(); newRange.setStartAfter(img); @@ -290,14 +290,13 @@ } // Форсируем обновление состояния - editor.dispatchEvent(new Event('input', { bubbles: true })); + editor.dispatchEvent(new Event("input", { bubbles: true })); } - // Обработчики событий - const showPickerElement = document.getElementById("showPicker"); + const showPickerElement = document.getElementById("showPicker"); - if (showPickerElement) { + if (showPickerElement) { showPickerElement.addEventListener("click", function (e) { e.stopPropagation(); const picker = document.getElementById("picker"); @@ -309,24 +308,25 @@ }); } - document.querySelectorAll(".emoji-option").forEach((emoji) => { - emoji.addEventListener("mousedown", function(e) { // Используем mousedown вместо click + document.querySelectorAll(".emoji-option").forEach((emoji) => { + emoji.addEventListener("mousedown", function (e) { + // Используем mousedown вместо click e.preventDefault(); e.stopPropagation(); - + // Фокусируем редактор перед вставкой const editor = document.getElementById("wtext"); if (editor) editor.focus(); - + insertEmoji(emoji); document.getElementById("picker").classList.remove("active"); }); }); - document.addEventListener('selectionchange', () => { + document.addEventListener("selectionchange", () => { const editor = document.getElementById("wtext"); const sel = window.getSelection(); - + if (editor && sel.rangeCount > 0) { const range = sel.getRangeAt(0); if (editor.contains(range.commonAncestorContainer)) { @@ -335,19 +335,19 @@ } }); -const picker = document.getElementById("picker"); -const showPicker = document.getElementById("showPicker"); + const picker = document.getElementById("picker"); + const showPicker = document.getElementById("showPicker"); -if (picker) { - document.addEventListener("click", function(e) { - const isClickInsidePicker = picker.contains(e.target); - const isClickOnShowButton = showPicker?.contains(e.target); - - if (!isClickInsidePicker && !isClickOnShowButton) { - picker.classList.remove("active"); - } - }); -} + if (picker) { + document.addEventListener("click", function (e) { + const isClickInsidePicker = picker.contains(e.target); + const isClickOnShowButton = showPicker?.contains(e.target); + + if (!isClickInsidePicker && !isClickOnShowButton) { + picker.classList.remove("active"); + } + }); + } // Обновленный JavaScript для работы с новым форматом document.addEventListener("click", function (e) { @@ -415,10 +415,6 @@ if (picker) { }; }); - console.log( - "Processed codes:", - allSmileys.map((s) => s.code) - ); return allSmileys; } catch (error) { console.error("Error loading smileys:", error); @@ -490,350 +486,156 @@ if (picker) { // Автодополнение // Автодополнение - function setupAutocomplete() { - const editor = document.getElementById("wtext"); - let currentWord = ""; - let startPos = 0; - let selectedIndex = -1; - let isAutocompleteOpen = false; +function setupAutocomplete() { + const editor = document.getElementById("wtext"); + if (!editor) return; - const acContainer = document.createElement("div"); - acContainer.className = "autocomplete"; - document.body.appendChild(acContainer); + let autocompleteType = null; + let isAutocompleteOpen = false; + let startPos = 0; + let currentWord = ""; + let allSmileys = []; + + // Загрузка смайлов при инициализации + loadSmileys().then(data => allSmileys = data); - // Обработчик клавиш - function handleKeyDown(e) { - if (!isAutocompleteOpen) return; + const acContainer = document.createElement("div"); + acContainer.className = "autocomplete"; + document.body.appendChild(acContainer); - const items = acContainer.querySelectorAll(".autocomplete-item"); - if (items.length === 0) return; - - switch (e.key) { - case "ArrowDown": - e.preventDefault(); - selectedIndex = Math.min(selectedIndex + 1, items.length - 1); - updateSelection(); - break; - case "ArrowUp": - e.preventDefault(); - selectedIndex = Math.max(selectedIndex - 1, -1); - updateSelection(); - break; - case "Enter": - e.preventDefault(); - if (selectedIndex >= 0) { - insertSuggestion(items[selectedIndex]); - } - break; - case "Escape": - e.preventDefault(); - closeAutocomplete(); - break; - } + // Обработчик клавиш для активации + editor.addEventListener("keydown", function(e) { + // Активация при вводе первого символа + if ((e.key === ":" || e.key === "@") && !isAutocompleteOpen) { + e.preventDefault(); + autocompleteType = e.key === ":" ? "emoji" : "mention"; + isAutocompleteOpen = true; + const range = window.getSelection().getRangeAt(0); + startPos = range.startOffset; + showAutocomplete("", range); // Показываем сразу } + }); - function updateSelection() { - const items = acContainer.querySelectorAll(".autocomplete-item"); - items.forEach((item, index) => { - item.classList.toggle("selected", index === selectedIndex); - }); - } + // Обработчик ввода + editor.addEventListener("input", function() { + if (!isAutocompleteOpen) return; - function insertSuggestion(item) { - const code = item.dataset.code; - const sel = window.getSelection(); - const range = sel.getRangeAt(0); + const sel = window.getSelection(); + const range = sel.getRangeAt(0); + const text = range.startContainer.textContent || ""; + const pos = range.startOffset; - range.setStart(range.startContainer, startPos); - range.deleteContents(); + // Определяем границы текущего слова + let i = pos - 1; + while (i >= 0 && !/[:\s@]/.test(text[i])) i--; - const textNode = document.createTextNode(`[${code}]`); - range.insertNode(textNode); + startPos = i + 1; + currentWord = text.substring(startPos, pos); - range.setStartAfter(textNode); - range.collapse(true); + positionAutocomplete(range); // Обновляем позицию + showAutocomplete(currentWord, range); + }); + function positionAutocomplete(range) { + const rect = range.getBoundingClientRect(); + const scrollY = window.scrollY || window.pageYOffset; + + acContainer.style.top = `${rect.bottom + scrollY + 5}px`; + acContainer.style.left = `${rect.left + window.scrollX}px`; + } - closeAutocomplete(); - editor.focus(); - } - function closeAutocomplete() { - acContainer.style.display = "none"; - acContainer.style.top = "-9999px"; - acContainer.style.left = "-9999px"; - acContainer.innerHTML = ""; + // Показать подсказки (исправленная версия) + function showAutocomplete(query, range) { + positionAutocomplete(range); - // Сброс состояний - selectedIndex = -1; - isAutocompleteOpen = false; - autocompleteType = null; - } + if (!isAutocompleteOpen) return; - // Предполагаем, что editor получен через getElementById + // Позиционирование относительно редактора + const editorRect = editor.getBoundingClientRect(); + const rangeRect = range.getBoundingClientRect(); + + acContainer.style.top = `${rangeRect.top - editorRect.top + 30}px`; + acContainer.style.left = `${rangeRect.left - editorRect.left}px`; - if (editor) { - let isAutocompleteOpen = false; // Добавляем инициализацию переменных - let autocompleteType = ""; - let startPos = 0; - let currentWord = ""; + if (autocompleteType === "emoji") { + const suggestions = allSmileys.filter(smiley => + query ? smiley.code.toLowerCase().includes(query) : true + ).slice(0, 8); - // Объявляем функции для безопасного использования - const handleKeyDown = (e) => { - /* ... */ - }; - const showAutocomplete = (word, range) => { - /* ... */ - }; - const closeAutocomplete = () => { - /* ... */ - }; - - // Обработчик нажатия клавиш - editor.addEventListener("keydown", function (e) { - const isAtSymbol = - e.key === "@" || - (e.key === "2" && e.shiftKey) || - (e.key === "Quote" && e.shiftKey); - - if (e.key === ":" && !isAutocompleteOpen) { - autocompleteType = "emoji"; - isAutocompleteOpen = true; - editor.addEventListener("keydown", handleKeyDown); - } else if (isAtSymbol && !isAutocompleteOpen) { - autocompleteType = "mention"; - isAutocompleteOpen = true; - editor.addEventListener("keydown", handleKeyDown); - } - }); - - // Обработчик ввода - editor.addEventListener("input", function (e) { - if (!isAutocompleteOpen) return; - - const sel = window.getSelection(); - if (!sel || sel.rangeCount === 0) return; - - const range = sel.getRangeAt(0); - const text = range.startContainer?.textContent || ""; - const pos = range.startOffset; - - let i = pos - 1; - const stopChar = autocompleteType === "emoji" ? ":" : "@"; - - while (i >= 0 && text[i] !== stopChar && text[i] !== " ") i--; - - if (text[i] === stopChar) { - startPos = i; - currentWord = text.slice(i + 1, pos).toLowerCase(); - showAutocomplete(currentWord, range); - } else { - closeAutocomplete(); - } - }); - } - - // Показать подсказки - function showAutocomplete(query, range) { - if (autocompleteType === "emoji") { - const suggestions = allSmileys - .filter((smiley) => - smiley.code.toLowerCase().includes(query.toLowerCase()) - ) - .slice(0, 10); - - if (suggestions.length === 0) { - closeAutocomplete(); - return; - } - - const rect = range.getBoundingClientRect(); - acContainer.style.top = `${rect.bottom + window.scrollY}px`; - acContainer.style.left = `${rect.left + window.scrollX}px`; - - acContainer.innerHTML = suggestions - .map( - (smiley, index) => ` - <div class="autocomplete-item" - data-code="${smiley.code}"> - <img src="${smiley.url}" - style="width:24px;height:24px"> - <span>${smiley.code}</span> - </div> - ` - ) - .join(""); - - acContainer.style.display = "block"; - selectedIndex = 0; - updateSelection(); - } else if (autocompleteType === "mention") { - fetch(`/api/users/search?q=${encodeURIComponent(query)}`) - .then((response) => response.json()) - .then((users) => { - currentMentions = users; - const rect = range.getBoundingClientRect(); - acContainer.style.top = `${rect.bottom + window.scrollY}px`; - acContainer.style.left = `${rect.left + window.scrollX}px`; - - acContainer.innerHTML = users - .map( - (user, index) => ` - <div class="autocomplete-item" - data-user-id="${user.id}" - data-username="${user.username}"> - <img src="${user.photourl}" - style="width:24px;height:24px;border-radius:50%"> - <span>${user.username}</span> - </div> - ` - ) - .join(""); - - acContainer.style.display = "block"; - selectedIndex = 0; - updateSelection(); - }); - } - } - - // Обновленный обработчик клика - acContainer.addEventListener("mousedown", function (e) { - // Используем mousedown вместо click - const item = e.target.closest(".autocomplete-item"); - if (item) { - insertSuggestion(item); - e.preventDefault(); - } - }); - - // Скрыть при клике вне - document.addEventListener("click", function (e) { - if (!e.target.closest(".autocomplete")) { - acContainer.style.display = "none"; - } - }); - - function insertSuggestion(item) { - if (autocompleteType === "emoji") { - const code = item.dataset.code; - const sel = window.getSelection(); - const range = sel.getRangeAt(0); - - range.setStart(range.startContainer, startPos); - range.deleteContents(); - - // Создаем элемент изображения вместо текстового узла - const img = document.createElement("img"); - img.className = "editor-emoji"; - img.src = item.querySelector("img").src; - img.dataset.code = `[${code}]`; - img.contentEditable = "false"; - - range.insertNode(img); - - // Сдвигаем курсор после изображения - const newRange = document.createRange(); - newRange.setStartAfter(img); - newRange.collapse(true); - sel.removeAllRanges(); - sel.addRange(newRange); - - closeAutocomplete(); - editor.focus(); - } else if (autocompleteType === "mention") { - const userId = item.dataset.userId; - const username = item.dataset.username; - const sel = window.getSelection(); - - if (sel.rangeCount === 0) return; - - const range = sel.getRangeAt(0).cloneRange(); - range.setStart(range.startContainer, startPos); - range.deleteContents(); - - // Создаем основной элемент упоминания - const mentionSpan = document.createElement("span"); - mentionSpan.className = "user-mention"; - mentionSpan.dataset.userId = userId; - mentionSpan.textContent = `@${username}`; - mentionSpan.contentEditable = "false"; - - // Создаем пробел после упоминания - const spaceSpan = document.createTextNode("\u00A0"); - - // Вставляем элементы - const fragment = document.createDocumentFragment(); - fragment.appendChild(mentionSpan); - fragment.appendChild(spaceSpan); - range.insertNode(fragment); - - // Обновляем позицию курсора - const newRange = document.createRange(); - newRange.setStartAfter(spaceSpan); - newRange.collapse(true); - - sel.removeAllRanges(); - sel.addRange(newRange); - - // Обновляем редактор - closeAutocomplete(); - editor.focus(); - } - closeAutocomplete(); - editor.focus(); - window.addEventListener("scroll", () => closeAutocomplete(), true); - } - - function closeAutocomplete(immediate = false) { - if (immediate) { - // Немедленное удаление из DOM - acContainer.style.display = "none"; - acContainer.innerHTML = ""; - acContainer.style.top = "-9999px"; // Убираем за пределы видимости - selectedIndex = -1; - isAutocompleteOpen = false; - autocompleteType = null; + acContainer.innerHTML = suggestions.map(smiley => ` + <div class="autocomplete-item" data-code="${smiley.code}"> + <img src="${smiley.url}" class="emoji-preview"> + <span>${smiley.code}</span> + </div> + `).join(""); + + } else if (autocompleteType === "mention") { + if (!query) { + acContainer.innerHTML = getPopularContacts(); return; } - - // Анимация исчезновения (опционально) - acContainer.classList.add("closing"); - setTimeout(() => { - acContainer.style.display = "none"; - acContainer.innerHTML = ""; - acContainer.classList.remove("closing"); - selectedIndex = -1; - isAutocompleteOpen = false; - autocompleteType = null; - }, 200); - } - - document.addEventListener("mouseover", async function (e) { - const mention = e.target.closest(".user-mention"); - if (mention && !mention.dataset.loaded) { - const userId = mention.dataset.userId; - - const response = await fetch(`/api/users/load/${userId}`); - const userInfo = await response.json(); - } - }); - - function updateSelection() { - const items = acContainer.querySelectorAll(".autocomplete-item"); - items.forEach((item, index) => { - item.classList.toggle("selected", index === selectedIndex); - }); - - // Прокрутка к выбранному элементу - if (selectedIndex >= 0 && items[selectedIndex]) { - items[selectedIndex].scrollIntoView({ - behavior: "auto", - block: "nearest", + fetch(`/api/users/search?q=${encodeURIComponent(query)}`) + .then(response => response.json()) + .then(users => { + acContainer.innerHTML = users.map(user => ` + <div class="autocomplete-item" + data-user-id="${user.id}" + data-username="${user.username}"> + <img src="${user.avatar}" class="user-avatar"> + ${user.username} + </div> + `).join(""); }); - } } + + acContainer.style.display = "block"; } + // Обработчик выбора элемента + acContainer.addEventListener("mousedown", (e) => { + const item = e.target.closest(".autocomplete-item"); + if (!item) return; + + const sel = window.getSelection(); + const range = sel.getRangeAt(0); + + // Удаляем триггер и текущее слово + range.setStart(range.startContainer, startPos); + range.setEnd(range.startContainer, startPos + currentWord.length); + range.deleteContents(); + + // Вставка эмодзи + if (autocompleteType === "emoji") { + const img = document.createElement("img"); + img.className = "editor-emoji"; + img.src = item.querySelector("img").src; + img.dataset.code = `[:${item.dataset.code}:]`; + range.insertNode(img); + } + // Вставка упоминания + else if (autocompleteType === "mention") { + const mention = document.createElement("span"); + mention.className = "user-mention"; + mention.textContent = `@${item.dataset.username}`; + mention.dataset.userId = item.dataset.userId; + range.insertNode(mention); + } + + // Сброс состояния + acContainer.style.display = "none"; + isAutocompleteOpen = false; + editor.focus(); + }); + + // Закрытие при клике вне + document.addEventListener("click", (e) => { + if (!e.target.closest(".autocomplete")) { + acContainer.style.display = "none"; + isAutocompleteOpen = false; + } + }); +} + // Инициализация после загрузки window.addEventListener("DOMContentLoaded", setupAutocomplete); diff --git a/static/js/index.js b/static/js/index.js index 2ae4ec3..4314d64 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -70,24 +70,6 @@ $(document).ready(function() }); - $('#loginbtn').on('click', function() - { - var username = $('#username').val().trim(); - var password = $('#password').val().trim(); - - if (username != '' && username != '') - { - $('#loginbtn').prop('disabled', true).val(_text['IX_LOGGING']); - - $.post('/api.php?action=check-login', { username: username, password: password, remember: $('#remember').is('checked') }, function(r) - { - if (r == 0) - $('#loginform').submit(); - else window.location.reload(); - }) - .fail(function(jx) { if (jx.responseText != '') alert(jx.responseText); }); - } - }); $('#mobile-menu').on('click', function() diff --git a/static/themepacks/nativegallery-dark/root.css b/static/themepacks/nativegallery-dark/root.css index dea1763..ef6239c 100644 --- a/static/themepacks/nativegallery-dark/root.css +++ b/static/themepacks/nativegallery-dark/root.css @@ -267,7 +267,7 @@ a.underphoto:hover { background-color:#222; } } .tab-nav { - border-bottom: 1px solid #005499; + border-bottom: 1px solid #3d3d3d; padding: 4px 6px 0; display: flex; gap: 2px; @@ -294,7 +294,7 @@ a.underphoto:hover { background-color:#222; } .tab-item.active { background: #1e1e1e; - border-color: #005499; + border-color: #3d3d3d; color: #ffffff; padding-bottom: 5px; margin-bottom: -1px; @@ -308,7 +308,7 @@ a.underphoto:hover { background-color:#222; } } .tab-content { - border: 1px solid #005499; + border: 1px solid #3d3d3d; border-top: 0; padding: 12px; background: #1e1e1e; diff --git a/views/components/AdminSidebar.php b/views/components/AdminSidebar.php index 63522ca..0fd2937 100644 --- a/views/components/AdminSidebar.php +++ b/views/components/AdminSidebar.php @@ -2,50 +2,52 @@ use \App\Core\Page; use \App\Services\DB; + $nonreviewedimgs = DB::query('SELECT COUNT(*) FROM photos WHERE moderated=0')[0]['COUNT(*)']; - if ($nonreviewedimgs > 0) { - $nonr = '<span class="badge text-bg-danger">'.$nonreviewedimgs.'</span>'; - } +if ($nonreviewedimgs > 0) { + $nonr = '<span class="badge text-bg-danger">' . $nonreviewedimgs . '</span>'; +} + ?> <link rel="stylesheet" href="https://rsms.me/inter/inter.css"> <style> @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap"); -body { - font-family: Inter !important; -} + body { + font-family: Inter !important; + } </style> <script src="/static/js/changeTab.js" defer></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/boxicons@latest/css/boxicons.min.css"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous"> - <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.5/dist/umd/popper.min.js" integrity="sha384-Xe+8cL9oJa6tN/veChSP7q+mnSPaj5Bcu9mPX5F5xIGE0DVittaqT5lorf0EI7Vk" crossorigin="anonymous"></script> +<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.5/dist/umd/popper.min.js" integrity="sha384-Xe+8cL9oJa6tN/veChSP7q+mnSPaj5Bcu9mPX5F5xIGE0DVittaqT5lorf0EI7Vk" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.min.js" integrity="sha384-ODmDIVzN+pFdexxHEHFBQH3/9/vQ9uori45z4JjnFsRydbmQbmL5t1tQ0culUzyK" crossorigin="anonymous"></script> <link rel="stylesheet" href="/static/css/header.admin.css"> <link rel="stylesheet" href="/static/css/tabs.css"> <div class="layout__left-column layout__sticky"> - <header style="background-color: #0d1012;" class="header"> - <div class="header__container"> + <header style="background-color: #0d1012;" class="header"> + <div class="header__container"> - - - <div class="header__toggle"> - <i class='bx bx-menu' id="header-toggle"></i> - </div> + + + <div class="header__toggle"> + <i class='bx bx-menu' id="header-toggle"></i> </div> - </header> + </div> + </header> <div class="nav" id="navbar"> <nav class="nav__container"> <div> <a href="/admin" class="nav__link nav__logo"> - <h5><b><?=NGALLERY['root']['title']?></b></h5> + <h5><b><?= NGALLERY['root']['title'] ?></b></h5> </a> - + <div class="nav__list"> @@ -58,7 +60,7 @@ body { </a> <a href="/admin?type=Photo" class="nav__link"> <i class="bx bx-camera nav__icon"></i> - <span class="nav__name">Фотографии<?=$nonr?></span> + <span class="nav__name">Фотографии <?= $nonr ?></span> </a> <a href="/admin?type=Galleries" class="nav__link"> <i class="bx bx-images nav__icon"></i> @@ -78,7 +80,7 @@ body { </a> <a href="/admin?type=Models" class="nav__link"> <i class="bx bx-data nav__icon"></i> - <span class="nav__name">База моделей</span> + <span class="nav__name">База моделей <?= $nonr_e ?></span> </a> <a href="/admin?type=GeoDB" class="nav__link"> <i class="bx bx-world nav__icon"></i> @@ -92,41 +94,41 @@ body { <i class="bx bx-cog nav__icon"></i> <span class="nav__name">Настройки<span class="badge text-bg-warning">BETA</span></span> </a> - + </div> - + </div> </div> </nav> </div> </div> - <script> - /*==================== SHOW NAVBAR ====================*/ -const showMenu = (headerToggle, navbarId) =>{ - const toggleBtn = document.getElementById(headerToggle), - nav = document.getElementById(navbarId) - - // Validate that variables exist - if(headerToggle && navbarId){ - toggleBtn.addEventListener('click', ()=>{ - // We add the show-menu class to the div tag with the nav__menu class - nav.classList.toggle('show-menu') - // change icon - toggleBtn.classList.toggle('bx-x') - }) +<script> + /*==================== SHOW NAVBAR ====================*/ + const showMenu = (headerToggle, navbarId) => { + const toggleBtn = document.getElementById(headerToggle), + nav = document.getElementById(navbarId) + + // Validate that variables exist + if (headerToggle && navbarId) { + toggleBtn.addEventListener('click', () => { + // We add the show-menu class to the div tag with the nav__menu class + nav.classList.toggle('show-menu') + // change icon + toggleBtn.classList.toggle('bx-x') + }) + } } -} -showMenu('header-toggle','navbar') + showMenu('header-toggle', 'navbar') -/*==================== LINK ACTIVE ====================*/ -const linkColor = document.querySelectorAll('.nav__link') + /*==================== LINK ACTIVE ====================*/ + const linkColor = document.querySelectorAll('.nav__link') -function colorLink(){ - linkColor.forEach(l => l.classList.remove('active')) - this.classList.add('active') -} + function colorLink() { + linkColor.forEach(l => l.classList.remove('active')) + this.classList.add('active') + } -linkColor.forEach(l => l.addEventListener('click', colorLink)) + linkColor.forEach(l => l.addEventListener('click', colorLink)) </script> \ No newline at end of file diff --git a/views/pages/Admin/Index.php b/views/pages/Admin/Index.php index 00b590a..cd8d129 100644 --- a/views/pages/Admin/Index.php +++ b/views/pages/Admin/Index.php @@ -13,6 +13,7 @@ if (!isset($_GET['type']) || $_GET['type'] != 'Photo') { <!DOCTYPE html> <html lang="ru"> <script src="https://code.jquery.com/jquery-3.7.1.js"></script> +<script src="/static/js/act.js" defer></script> <link rel="stylesheet" href="/static/css/notie.css<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>"> <script src="/static/js/notie.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>"></script> diff --git a/views/pages/Admin/Models.php b/views/pages/Admin/Models.php index 4caa1cd..5d77e4e 100644 --- a/views/pages/Admin/Models.php +++ b/views/pages/Admin/Models.php @@ -12,6 +12,11 @@ if (!isset($_GET['type']) || $_GET['type'] != 'Photo') { } } +$nonreviewedentities = DB::query('SELECT COUNT(*) FROM entities_requests WHERE status=0')[0]['COUNT(*)']; +if ($nonreviewedentities > 0) { + $nonr_e = '<span id="mdlnum" class="badge text-bg-danger">' . $nonreviewedentities . '</span>'; +} + ?> <!DOCTYPE html> <html lang="ru"> @@ -46,32 +51,49 @@ if (!isset($_GET['type']) || $_GET['type'] != 'Photo') { <?= \App\Controllers\AdminController::loadMenu(); ?> <?= \App\Controllers\AdminController::loadContent(); ?> <h1><b>Модели</b></h1> - <a href="?type=ModelsCreate" class="btn btn-primary">Создать</a> - <div class="p20w" style="display:block"> - <table class="table"> - <tbody> - <tr> - <th width="100">ID</th> - <th width="20%">Название</th> - <th width="50%">Сущность</th> - <th>Действия</th> - </tr> + <div class="v-header__tabs"> + <div class="v-tabs"> + <div class="v-tabs__scroll"> + <div class="v-tabs__content"><a href="#" onclick="changeTab('list')" id="list" class="v-tab v-tab-b v-tab--active"><span class="v-tab__label"> + Список - <?php - $photos = DB::query('SELECT * FROM entities_data ORDER BY id DESC'); - foreach ($photos as $p) { - $entity = new Vehicle($p['entityid']); + </span></a><a href="#" onclick="changeTab('moderate')" id="moderate" class="v-tab v-tab-b"><span class="v-tab__label"> + Заявки на добавление <?= $nonr_e ?> - echo ' <tr id="pht' . $p['id'] . '" class="' . $color . '"> + </span></a> + + + </div> + </div> + </div> + </div> + <div id="list__block" class="active__block"> + <a href="?type=ModelsCreate" class="btn btn-primary mt-3">Создать</a> + <div class="p20w" style="display:block"> + <table class="table"> + <tbody> + <tr> + <th width="100">ID</th> + <th width="20%">Название</th> + <th width="50%">Сущность</th> + <th>Действия</th> + </tr> + + <?php + $photos = DB::query('SELECT * FROM entities_data ORDER BY id DESC'); + foreach ($photos as $p) { + $entity = new Vehicle($p['entityid']); + + echo ' <tr id="pht' . $p['id'] . '" class="' . $color . '"> <td> - '.$p['id'].' + ' . $p['id'] . ' </td> <td> - '.$p['title'].' + ' . $p['title'] . ' </td> <td> - '.$entity->i('title').' + ' . $entity->i('title') . ' </td> <td class="c"> @@ -81,20 +103,84 @@ if (!isset($_GET['type']) || $_GET['type'] != 'Photo') { <a data-bs-toggle="modal" data-bs-target="#declinePhotoModal' . $p['id'] . '" href="#" class="btn btn-danger">Удалить</a> </td>'; - - echo ' + + echo ' </tr> '; - } - ?> + } + ?> - </tbody> - </table> - </div><br> + </tbody> + </table> + </div><br> + </div> + <div style="display: none;" id="moderate__block"> + <div class="p20w" style="display:block"> + <table class="table"> + <tbody> + <tr> + <th width="100">ID</th> + <th width="20%">Название</th> + <th width="35%">Сущность</th> + <th width="50%">Данные</th> + <th width="50%">Действия</th> + </tr> + + <?php + $photos = DB::query('SELECT * FROM entities_requests WHERE status=0 ORDER BY id DESC'); + foreach ($photos as $p) { + $entity = new Vehicle($p['entityid']); + $vehiclevariables = json_decode($entity->i('sampledata'), true); + $vehicledatavariables = json_decode($p['data'], true); + $vhhtml = ''; + $num = 1; + foreach ($vehiclevariables as $vb) { + $vhhtml .= '<b>' . $vb['name'] . ': </b>' . $vehicledatavariables[$num]['value'] . '<br>'; + $num++; + } + echo '<tr id="mdl' . $p['id'] . '"> + <td> + ' . $p['id'] . ' + </td> + <td> + ' . $p['title'] . ' + + </td> + <td> + ' . $entity->i('title') . ' + + </td> + <td> + '.$vhhtml.' + + + </td> + <td class="c"> + '; + + echo '<a onclick="handleModelRequest('.$p['id'].', `accept`); return false;" class="btn btn-primary">Принять</a> + <a onclick="handleModelRequest('.$p['id'].', `decline`); return false;" class="btn btn-danger">Отклонить</a> + + </td>'; + + echo ' + </tr> + + + + '; + } + ?> + + + </tbody> + </table> + </div><br> + </div> <?php $entities = DB::query('SELECT * FROM entities'); foreach ($entities as $e) { diff --git a/views/pages/Admin/News.php b/views/pages/Admin/News.php index dca4463..de215de 100644 --- a/views/pages/Admin/News.php +++ b/views/pages/Admin/News.php @@ -36,36 +36,8 @@ use \App\Models\User; <?php $news = DB::query('SELECT * FROM news ORDER BY id'); foreach ($news as $n) { - echo '<div class="card mb-3"><div class="card-body">' . Date::zmdate($n['time']) . '<br>' . $n['body'] . '</div></div>'; + $nn = new \App\Models\Admin\News($n['id']); + $nn->view(); } ?> -</div> - -<script> - function createNews() { - $.ajax({ - type: "POST", - url: '/api/admin/createnews', - data: { - body: $('#body').val() - }, - success: function(response) { - Notify.noty('success', 'OK!'); - $.ajax({ - type: "GET", - url: '/api/admin/loadnews', - - success: function(response) { - $('#news').html(response); - - - } - - }); - - - } - - }); - } -</script> \ No newline at end of file +</div> \ No newline at end of file diff --git a/views/pages/Admin/Photo.php b/views/pages/Admin/Photo.php index 0923e24..f594123 100644 --- a/views/pages/Admin/Photo.php +++ b/views/pages/Admin/Photo.php @@ -39,10 +39,10 @@ use \App\Models\User; <div class="v-header__tabs"> <div class="v-tabs"> <div class="v-tabs__scroll"> - <div class="v-tabs__content"><!--a href="#" onclick="changeTab('full')" id="full" class="v-tab v-tab-b"><span class="v-tab__label"> + <div class="v-tabs__content"><a href="#" onclick="changeTab('full')" id="full" class="v-tab v-tab-b"><span class="v-tab__label"> Полный список - </span></!--a--><a href="#" onclick="changeTab('moderate')" id="moderate" class="v-tab v-tab-b v-tab--active"><span class="v-tab__label"> + </span></a><a href="#" onclick="changeTab('moderate')" id="moderate" class="v-tab v-tab-b v-tab--active"><span class="v-tab__label"> Ожидают модерации </span></a> diff --git a/views/pages/Admin/Settings.php b/views/pages/Admin/Settings.php index 06502bf..b6d6d2d 100644 --- a/views/pages/Admin/Settings.php +++ b/views/pages/Admin/Settings.php @@ -130,7 +130,8 @@ function renderInputs($data, $prefix = '') <?php foreach (NGALLERY_TASKS as $nt) { - $nt = $nt; + if ($nt['type'] === 'cron') { + $nt = $nt; echo '<tr><td> '.$nt['id'].' @@ -139,6 +140,8 @@ function renderInputs($data, $prefix = '') </td><td class="c"> <a onclick="taskManager(`'.$nt['id'].'`, 1)" class="btn btn-sm btn-primary">Запустить</a> <a onclick="taskManager(`'.$nt['id'].'`, 0)" class="btn btn-sm btn-danger">Остановить</a> </td> <tr>'; + } + } diff --git a/views/pages/Admin/UserEdit.php b/views/pages/Admin/UserEdit.php index 50b970b..3b957d6 100644 --- a/views/pages/Admin/UserEdit.php +++ b/views/pages/Admin/UserEdit.php @@ -1,12 +1,15 @@ <?php + use \App\Services\{Date, DB, Auth}; + $user = new \App\Models\User($_GET['user_id']); if ($user->i('id') === null) { die('Пользователь не найден'); } -function updateJson($data, $key, $value) { +function updateJson($data, $key, $value) +{ $data[$key] = $value; return $data; } @@ -25,65 +28,75 @@ if (isset($_POST['subbtn'])) { $updatedJson = updateJson($currentJson, 'premoderation', $premoderation); $updatedJsonString = json_encode($updatedJson, JSON_PRETTY_PRINT); - if (($admin === 1 && Auth::userid() === 1) || ($admin != 1 && Auth::userid() != 1)) { - DB::query("UPDATE users SET status = ?, admin = ?, content = ? WHERE id = ?", [ - $accountstatus, - $admin, - $updatedJsonString, - $userId - ]); - echo "Данные успешно обновлены."; - } else { - echo 'Не удалось обновить данные'; - } - + DB::query("UPDATE users SET status = ?, admin = ?, content = ? WHERE id = ?", [ + $accountstatus, + $admin, + $updatedJsonString, + $userId + ]); - + $alert = [ + 'type' => 'success', + 'code' => 'Данные успешно обновлены' + ]; } else { echo "Ошибка: JSON данные не найдены."; } } +if ($alert != null) { + echo '<div class="alert alert-' . $alert['type'] . '" role="alert">' . $alert['code'] . '</div>'; +} ?> -<form action="/admin?type=UserEdit&user_id=<?=$_GET['user_id']?>" method="post" name="form" id="form" enctype="multipart/form-data" style="display:inline-block; min-width:500px;"> +<form action="/admin?type=UserEdit&user_id=<?= $_GET['user_id'] ?>" method="post" name="form" id="form" enctype="multipart/form-data" style="display:inline-block; min-width:500px;"> - <p><img src="<?=$user->i('photourl')?>" width="50"> <?=$user->i('username')?></p> + <p><img src="<?= $user->i('photourl') ?>" width="50"> <?= $user->i('username') ?></p> <p>Был в сети: <b><?= Date::zmdate($user->i('online')) ?> <?php if (time() - 300 <= $user->i('online')) { ?><i>(online)</i><?php } ?></b></p> - <p>Ссылка на профиль: <b><a href="/author/<?=$_GET['user_id']?>">https://<?= $_SERVER['SERVER_NAME'] ?>/author/<?= $_GET['user_id'] ?></a></b></p> + <p>Ссылка на профиль: <b><a href="/author/<?= $_GET['user_id'] ?>">https://<?= $_SERVER['SERVER_NAME'] ?>/author/<?= $_GET['user_id'] ?></a></b></p> <div class="p20" style="text-align:left; margin-bottom:15px"> - <h4>Настройки</h4> + <h4 class="mt-3"><b>Настройки</b></h4> <div style="margin-bottom:3px; margin-top:5px">Прямая загрузка</div> - <select name="premoderation" style="width:100%"> - <option value="true" <?php if ($user->content('premoderation') === 'true') { echo 'selected'; } ?>>Да</option> - <option value="false" <?php if ($user->content('premoderation') === 'false' || $user->content('premoderation') === null) { echo 'selected'; } ?>>Нет</option> - </select> - <div style="margin-bottom:3px; margin-top:5px">Статус аккаунта</div> - <select name="accountstatus" style="width:100%"> - <option value="0" <?php if ($user->i('status') === 0) { echo 'selected'; } ?>>Без ограничений</option> - <option value="1" <?php if ($user->i('status') === 1) { echo 'selected'; } ?>>Заблокирован</option> - </select> - <div style="margin-bottom:3px; margin-top:5px">Статус аккаунта</div> - <select name="admin" style="width:100%"> - <option value="0" <?php if ((int)$user->i('admin') === 0) { echo 'selected'; } ?>>Пользователь</option> - <option value="1" <?php if ((int)$user->i('admin') === 1) { echo 'selected'; } if (Auth::userid() === 1) { echo 'disabled'; } ?>>Администратор</option> - <option value="2" <?php if ((int)$user->i('admin') === 2) { echo 'selected'; } ?>>Фотомодератор</option> - <option value="3" <?php if ((int)$user->i('admin') === 3) { echo 'selected'; } ?>>Модератор</option> - </select> - + <select class="form-select" name="premoderation" style="width:100%"> + <option value="true" <?php if ($user->content('premoderation') === 'true') { + echo 'selected'; + } ?>>Да</option> + <option value="false" <?php if ($user->content('premoderation') === 'false' || $user->content('premoderation') === null) { + echo 'selected'; + } ?>>Нет</option> + </select> + <div style="margin-bottom:3px; margin-top:5px">Статус аккаунта</div> + <select class="form-select" name="accountstatus" style="width:100%"> + <option value="0" <?php if ($user->i('status') === 0) { + echo 'selected'; + } ?>>Без ограничений</option> + <option value="1" <?php if ($user->i('status') === 1) { + echo 'selected'; + } ?>>Заблокирован</option> + </select> + <div style="margin-bottom:3px; margin-top:5px">Статус аккаунта</div> + <select class="form-select" name="admin" style="width:100%"> + <option value="0" <?php if ((int)$user->i('admin') === 0) { + echo 'selected'; + } ?>>Пользователь</option> + <option value="1" <?php if ((int)$user->i('admin') === 1) { + echo 'selected'; + } + if (Auth::userid() === 1) { + echo 'disabled'; + } ?>>Администратор</option> + <option value="2" <?php if ((int)$user->i('admin') === 2) { + echo 'selected'; + } ?>>Фотомодератор</option> + <option value="3" <?php if ((int)$user->i('admin') === 3) { + echo 'selected'; + } ?>>Модератор</option> + </select> </div> - <div class="p20" style="text-align:left; margin-bottom:15px"> -<h4>Операции</h4> - - -<div class="cmt-submit"><a href="/admin?type=UserEdit&user_id='.$u['id'].'">Сбросить аватар</a></div> - -</div> - -<div class="cmt-submit"><input name="subbtn" type="submit" value="Применить"></div> + <div class="cmt-submit"><input class="btn btn-primary" name="subbtn" type="submit" value="Применить"></div> </form> \ No newline at end of file diff --git a/views/pages/Main.php b/views/pages/Main.php index 2c8480a..9c03ab0 100644 --- a/views/pages/Main.php +++ b/views/pages/Main.php @@ -214,7 +214,7 @@ LIMIT 10;'); $first_id = $photos[0]['id']; $last_id = end($photos)['id']; ?> - <div id="recent-photos" class="ix-photos ix-photos-multiline" lastpid="<?= $first_id + 1 ?>" firstpid="<?= $last_id ?>"> + <div id="recent-photos" class="ix-photos ix-photos-multiline shine" lastpid="<?= $first_id + 1 ?>" firstpid="<?= $last_id ?>"> </div> </div> diff --git a/views/pages/MapMedia.php b/views/pages/MapMedia.php index b9f16ff..676db9b 100644 --- a/views/pages/MapMedia.php +++ b/views/pages/MapMedia.php @@ -108,7 +108,7 @@ use \App\Models\{Vehicle, User}; </tr> <script> - const map = L.map('mapd').setView([51.505, -0.09], 13); + const map = L.map('mapd').setView([55.751244, 37.618423], 13); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map); diff --git a/views/pages/Photo.php b/views/pages/Photo.php index 44ab83a..1402dd3 100644 --- a/views/pages/Photo.php +++ b/views/pages/Photo.php @@ -58,7 +58,9 @@ if ($photo->i('id') !== null) { <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" /> <script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.umd.js"></script> - <script src="/static/js/comments.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { echo '?'.time(); } ?>" defer></script> + <script src="/static/js/comments.js<?php if (NGALLERY['root']['cloudflare-caching'] === true) { + echo '?' . time(); + } ?>" defer></script> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.css" /> @@ -110,7 +112,7 @@ if ($photo->i('id') !== null) { <div id="err"></div> <script defer> $(document).ready(function() { - Fancybox.bind('[data-fancybox="gallery"]'); + Fancybox.bind('[data-fancybox="gallery"]'); }); </script> <?php @@ -703,12 +705,14 @@ if ($photo->i('id') !== null) { .autocomplete { position: absolute; - background: white; + background: #fff; border: 1px solid #ddd; - max-height: 200px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + min-width: 200px; + max-height: 300px; overflow-y: auto; - z-index: 1001; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + z-index: 10000; + transform: translateY(2px);/ } .autocomplete-item { @@ -861,67 +865,67 @@ if ($photo->i('id') !== null) { </div> <script data-restart> - $(document).ready(function() { - $('#f1').submit(function(e) { - e.preventDefault(); + $(document).ready(function() { + $('#f1').submit(function(e) { + e.preventDefault(); - var formData = new FormData(this); // Собираем данные из формы, включая filebody + var formData = new FormData(this); // Собираем данные из формы, включая filebody - $.ajax({ - type: "POST", - url: "/api/photo/comment", - data: formData, - processData: false, // Не обрабатывать данные (важно для файлов) - contentType: false, // Не устанавливать заголовок Content-Type (браузер сделает сам) - success: function(response) { - var jsonData = JSON.parse(response); + $.ajax({ + type: "POST", + url: "/api/photo/comment", + data: formData, + processData: false, // Не обрабатывать данные (важно для файлов) + contentType: false, // Не устанавливать заголовок Content-Type (браузер сделает сам) + success: function(response) { + var jsonData = JSON.parse(response); - if (jsonData.errorcode == "1") { - $('#statusSend').css({ - display: 'block', - color: 'red' - }).text('Комментарий некорректен'); - } else if (jsonData.errorcode == "2") { - $('#statusSend').css({ - display: 'block', - color: 'yellow' - }).text('Пожалуйста, подождите...'); - setTimeout(function() { - window.location.replace(jsonData.twofaurl); - }, 1000); - } else if (jsonData.errorcode == "0") { - $('#wtext').val(''); - $('#statusSend').css({ - display: 'block', - color: 'green' - }).text('Комментарий отправлен!'); + if (jsonData.errorcode == "1") { + $('#statusSend').css({ + display: 'block', + color: 'red' + }).text('Комментарий некорректен'); + } else if (jsonData.errorcode == "2") { + $('#statusSend').css({ + display: 'block', + color: 'yellow' + }).text('Пожалуйста, подождите...'); + setTimeout(function() { + window.location.replace(jsonData.twofaurl); + }, 1000); + } else if (jsonData.errorcode == "0") { + $('#wtext').val(''); + $('#statusSend').css({ + display: 'block', + color: 'green' + }).text('Комментарий отправлен!'); - $.ajax({ - type: "POST", - url: "/api/photo/getcomments/<?= $id ?>", - processData: false, - async: true, - success: function(r) { - $('#posts').html(r); - }, - error: function(r) { - console.log(r); - } + $.ajax({ + type: "POST", + url: "/api/photo/getcomments/<?= $id ?>", + processData: false, + async: true, + success: function(r) { + $('#posts').html(r); + }, + error: function(r) { + console.log(r); + } + }); + } else { + alert('Неизвестная ошибка'); + } + }, + error: function(err) { + console.error("Ошибка при отправке формы", err); + } + }); }); - } else { - alert('Неизвестная ошибка'); - } - }, - error: function(err) { - console.error("Ошибка при отправке формы", err); - } - }); - }); - }); + }); - function errimg() { - const content = `<center> + function errimg() { + const content = `<center> <div class="p20 s5" style="border:none; margin:0 -20px; display:none;"> <b>Фото потеряно при крахе винчестера</b> <div class="sm" style="margin-top:5px"> @@ -930,10 +934,10 @@ if ($photo->i('id') !== null) { </div> </div> </center>`; - $('#err').html(content); - $('#err .p20').slideDown(500); - } -</script> + $('#err').html(content); + $('#err .p20').slideDown(500); + } + </script> <?php } else { ?> <div class="p0" id="pp-item-comments"> diff --git a/views/pages/Profile/Index.php b/views/pages/Profile/Index.php index 8889a11..3a51c7d 100644 --- a/views/pages/Profile/Index.php +++ b/views/pages/Profile/Index.php @@ -30,259 +30,269 @@ $birthdate = json_decode($userprofile->i('content'), true)['aboutbirthday']['val <?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Navbar.php'); ?> <tr> <td class="main"> - + <?php if (((int)$userprofile->i('id') === (int)explode('/', $_SERVER['REQUEST_URI'])[2]) || $usercttc === True) { ?> - <h1><?= htmlspecialchars($userprofile->i('username')) ?><?php if ($userprofile->i('admin') === 1) { echo '<img width="32" src="/static/img/star.png">'; } ?></h1> - - <?php - if ($usercttc === True) { - echo '<div style="float:left; border:solid 1px #3b7dc1; padding:6px 10px 7px; margin-bottom:13px; background-color:#0199ff44"><b>Профиль на transphoto.org</b><br>Пользователь не зарегистрирован на сервере '.NGALLERY['root']['title'].'. Информация может быть неполной.<br><a href="https://transphoto.org/author/'.(int)explode('/', explode('@', $_SERVER['REQUEST_URI'])[0])[2].'" target="_blank">Открыть на transphoto.org</a></div>'; - } - if ($userprofile->i('admin') === 1) { - echo 'Администратор сервера'; - } else if ($userprofile->i('admin') === 2) { - echo 'Фотомодератор'; - } - - if ($userprofile->i('id') === Auth::userid()) { ?> - <p><b><a href="/lk/profile">Редактировать мой профиль</a></b></p> - <?php } ?> - <div class="p20" style="padding-right:12px;"> - <table width="100%"> - <tr> - <?php if ($userprofile->content('badge') !== null) { ?> - <div style="float:left; border:solid 1px #3b7dc1; padding:6px 10px 7px; margin-bottom:13px; background-color:#0199ff44"><b><?=nl2br($userprofile->content('badge'))?></div><br> + <h1><?= htmlspecialchars($userprofile->i('username')) ?><?php if ($userprofile->i('admin') === 1) { + echo '<img width="32" src="/static/img/star.png">'; + } ?></h1> + + <?php + if ($usercttc === True) { + echo '<div style="float:left; border:solid 1px #3b7dc1; padding:6px 10px 7px; margin-bottom:13px; background-color:#0199ff44"><b>Профиль на transphoto.org</b><br>Пользователь не зарегистрирован на сервере ' . NGALLERY['root']['title'] . '. Информация может быть неполной.<br><a href="https://transphoto.org/author/' . (int)explode('/', explode('@', $_SERVER['REQUEST_URI'])[0])[2] . '" target="_blank">Открыть на transphoto.org</a></div>'; + } + if ($userprofile->i('admin') === 1) { + echo 'Администратор сервера'; + } else if ($userprofile->i('admin') === 2) { + echo 'Фотомодератор'; + } + + if ($userprofile->i('id') === Auth::userid()) { ?> + <p><b><a href="/lk/profile">Редактировать мой профиль</a></b></p> <?php } ?> - <td style="vertical-align:top; width:100%" valign="top" width="100%"> - <table style="margin-bottom: 15px;"> - <colgroup><col width="170px"> - </colgroup> - <col width="170px"> - - <?php - if ($city != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">Откуда:</td> - <td><?= $city ?></td> - </tr> - <?php } ?> - - <?php - if ($birthdate != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">День рождения:</td> - <td><?= $birthdate ?></td> - </tr> - <?php } ?> - </col></table> + <div class="p20" style="padding-right:12px;"> + <table width="100%"> + <tr> + <?php if ($userprofile->content('badge') !== null) { ?> + <div style="float:left; border:solid 1px #3b7dc1; padding:6px 10px 7px; margin-bottom:13px; background-color:#0199ff44"><b><?= nl2br($userprofile->content('badge')) ?></div><br> + <?php } ?> + <td style="vertical-align:top; width:100%" valign="top" width="100%"> <table style="margin-bottom: 15px;"> - <colgroup><col width="170px"> - </colgroup> - <?php - if (json_decode($userprofile->i('content'), true)['aboutlangs']['value'] != null) { ?> + + <col width="250px"> <tr> - <td class="sm" style="padding:3px 10px 3px 0">Владение языками:</td> - <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutlangs']['value']) ?></td> + <td class="sm" style="padding:3px 10px 3px 0"><b>Дата регистрации:</b></td> + <td><span class="sm"><?= $regdate ?></span></td> </tr> - <?php } ?> - <?php - if (json_decode($userprofile->i('content'), true)['abouttelegram']['value'] != null) { ?> <tr> - <td class="sm" style="padding:3px 10px 3px 0">Telegram:</td> - <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['abouttelegram']['value']) ?></td> + <td class="sm" style="padding:3px 10px 3px 0"><b>Был на сайте:</b></td> + <td><span class="sm"><?= Date::zmdate($userprofile->i('online')) ?> <?php if (time() - 300 <= $userprofile->i('online')) { ?>(<b>online</b>)<?php } ?></span></td> </tr> - <?php } ?> - <?php - if (json_decode($userprofile->i('content'), true)['aboutvk']['value'] != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">ВКонтакте:</td> - <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutvk']['value']) ?></td> - </tr> - <?php } ?> - <?php - if (json_decode($userprofile->i('content'), true)['abouttwitter']['value'] != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">Twitter/X:</td> - <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['abouttwitter']['value']) ?></td> - </tr> - <?php } ?> - <?php - if (json_decode($userprofile->i('content'), true)['aboutyoutube']['value'] != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">YouTube:</td> - <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutyoutube']['value']) ?></td> - </tr> - <?php } ?> - <?php - if (json_decode($userprofile->i('content'), true)['aboutemail']['value'] != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">Почта:</td> - <td><a href="emailto:<?=htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutemail']['value'])?>"><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutemail']['value']) ?></a></td> - </tr> - <?php } ?> - <?php - if (json_decode($userprofile->i('content'), true)['aboutinstagram']['value'] != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">Instagram:</td> - <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutinstagram']['value']) ?></td> - </tr> - <?php } ?> - <?php - if (json_decode($userprofile->i('content'), true)['abouttransphoto']['value'] != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">TransPhoto:</td> - <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['abouttransphoto']['value']) ?></td> - </tr> - <?php } ?> - <?php - if (json_decode($userprofile->i('content'), true)['aboutwebsite']['value'] != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">Личный сайт:</td> - <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutwebsite']['value']) ?></td> - </tr> - <?php } ?> + </col> </table> + <?php + if ($city != null || $birthdate != null || json_decode($userprofile->i('content'), true)['aboutlangs']['value'] != null) { ?> <table style="margin-bottom: 15px;"> - <colgroup><col width="170px"> - </colgroup> - <?php - if (json_decode($userprofile->i('content'), true)['aboutfavs_trains']['value'] != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">Любимые модели поездов:</td> - <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutfavs_trains']['value']) ?></td> - </tr> - <?php } ?> - <?php - if (json_decode($userprofile->i('content'), true)['aboutfavs_countries']['value'] != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">Любимые страны:</td> - <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutfavs_countries']['value']) ?></td> - </tr> - <?php } ?> - <?php - if (json_decode($userprofile->i('content'), true)['aboutfavs_cities']['value'] != null) { ?> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">Любимые города:</td> - <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutfavs_cities']['value']) ?></td> - </tr> + <colgroup> + <col width="250px"> + </colgroup> + <?php + if ($city != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>Откуда:</b></td> + <td><?= $city ?></td> + </tr> + <?php } ?> + + <?php + if ($birthdate != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>День рождения:</b></td> + <td><?= $birthdate ?></td> + </tr> + <?php } ?> + <?php + if (json_decode($userprofile->i('content'), true)['aboutlangs']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0">Владение языками:</b></td> + <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutlangs']['value']) ?></td> + </tr> + <?php } ?> + </col> + </table> <?php } ?> + <table style="margin-bottom: 15px;"> + <colgroup> + <col width="250px"> + </colgroup> + <?php + if (json_decode($userprofile->i('content'), true)['abouttelegram']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>Telegram:</b></td> + <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['abouttelegram']['value']) ?></td> + </tr> + <?php } ?> + <?php + if (json_decode($userprofile->i('content'), true)['aboutvk']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>ВКонтакте:</b></td> + <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutvk']['value']) ?></td> + </tr> + <?php } ?> + <?php + if (json_decode($userprofile->i('content'), true)['abouttwitter']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>Twitter/X:</b></td> + <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['abouttwitter']['value']) ?></td> + </tr> + <?php } ?> + <?php + if (json_decode($userprofile->i('content'), true)['aboutyoutube']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>YouTube:</b></td> + <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutyoutube']['value']) ?></td> + </tr> + <?php } ?> + <?php + if (json_decode($userprofile->i('content'), true)['aboutemail']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>Почта:</b></td> + <td><a href="emailto:<?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutemail']['value']) ?>"><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutemail']['value']) ?></a></td> + </tr> + <?php } ?> + <?php + if (json_decode($userprofile->i('content'), true)['aboutinstagram']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>Instagram:</b></td> + <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutinstagram']['value']) ?></td> + </tr> + <?php } ?> + <?php + if (json_decode($userprofile->i('content'), true)['abouttransphoto']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>TransPhoto:</b></td> + <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['abouttransphoto']['value']) ?></td> + </tr> + <?php } ?> + <?php + if (json_decode($userprofile->i('content'), true)['aboutwebsite']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>Личный сайт:</b></td> + <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutwebsite']['value']) ?></td> + </tr> + <?php } ?> + </table> + <table > + <colgroup> + <col width="250px"> + </colgroup> + <?php + if (json_decode($userprofile->i('content'), true)['aboutfavs_trains']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>Любимые модели поездов:</b></td> + <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutfavs_trains']['value']) ?></td> + </tr> + <?php } ?> + <?php + if (json_decode($userprofile->i('content'), true)['aboutfavs_countries']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>Любимые страны:</b></td> + <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutfavs_countries']['value']) ?></td> + </tr> + <?php } ?> + <?php + if (json_decode($userprofile->i('content'), true)['aboutfavs_cities']['value'] != null) { ?> + <tr> + <td class="sm" style="padding:3px 10px 3px 0"><b>Любимые города:</b></td> + <td><?= htmlspecialchars(json_decode($userprofile->i('content'), true)['aboutfavs_cities']['value']) ?></td> + </tr> + <?php } ?> </table> <table> - <colgroup><col width="170px"> - </colgroup> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">Дата регистрации:</td> - <td><span class="sm"><?=$regdate?></span></td> - </tr> - <tr> - <td class="sm" style="padding:3px 10px 3px 0">Был на сайте:</td> - <td><span class="sm"><?= Date::zmdate($userprofile->i('online')) ?> <?php if (time() - 300 <= $userprofile->i('online')) { ?>(<b>online</b>)<?php } ?></span></td> - </tr> - </table> - <br /> - <div class="sm" style="float:right"><a href="/lk/ticket.php?action=add&aid=140"></a></div> - </td> - - <td valign="top" align="right"> - <script> - function getBodyScrollTop() { - return self.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop); - } - function showUserPhoto() { - _getID('userphoto_big_img').src = '<?= $userprofile->i('photourl') ?>'; - _getID('userphoto_big_div').style.top = '' + (getBodyScrollTop() + 10) + 'px'; - _getID('userphoto_big_div').style.display = 'block'; - } + </table> + </td> - function hideUserPhoto() { - _getID('userphoto_big_div').style.display = 'none'; - } + <td valign="top" align="right"> + <script> + function getBodyScrollTop() { + return self.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop); + } - document.onclick = function(e) { - e = e || window.event; - E = e.target || e.srcElement; - if (E.id != 'userphoto_big_div' && E.id != 'userphoto_img') { - _getID('userphoto_big_div').style.display = 'none'; - } - } - </script> + function showUserPhoto() { + _getID('userphoto_big_img').src = '<?= $userprofile->i('photourl') ?>'; + _getID('userphoto_big_div').style.top = '' + (getBodyScrollTop() + 10) + 'px'; + _getID('userphoto_big_div').style.display = 'block'; + } - <div id="userphoto_big_div" style="position:absolute; display:none; border:1px solid gray; padding:10px; background-color:white; margin:auto; text-align:center; right:100px; z-index:3000" class="p5 shadow"><a href="#" onclick="hideUserPhoto(); return false"><img src="" id="userphoto_big_img" border="0" style="vertical-align:middle"></a> - <div style="margin-top:8px"><a class="dot" href="#" onclick="hideUserPhoto(); return false">закрыть</a></div> - </div> + function hideUserPhoto() { + _getID('userphoto_big_div').style.display = 'none'; + } - <a href="<?=$photourl?>" onclick="showUserPhoto(); return false;"><img onerror="this.src = '/static/img/avatar.png'; this.onerror = null;" src="<?= $photourl ?>" alt="" id="userphoto_img" class="f" style="width:auto; max-width:100px"></a> - </td> - </tr> - </table> - </div> - <?php + document.onclick = function(e) { + e = e || window.event; + E = e.target || e.srcElement; + if (E.id != 'userphoto_big_div' && E.id != 'userphoto_img') { + _getID('userphoto_big_div').style.display = 'none'; + } + } + </script> + + <div id="userphoto_big_div" style="position:absolute; display:none; border:1px solid gray; padding:10px; background-color:white; margin:auto; text-align:center; right:100px; z-index:3000" class="p5 shadow"><a href="#" onclick="hideUserPhoto(); return false"><img src="" id="userphoto_big_img" border="0" style="vertical-align:middle"></a> + <div style="margin-top:8px"><a class="dot" href="#" onclick="hideUserPhoto(); return false">закрыть</a></div> + </div> + + <a href="<?= $photourl ?>" onclick="showUserPhoto(); return false;"><img onerror="this.src = '/static/img/avatar.png'; this.onerror = null;" src="<?= $photourl ?>" alt="" id="userphoto_img" class="f" style="width:auto; max-width:100px"></a> + </td> + </tr> + </table> + </div> + <?php if (($about != null) && $usercttc === False) { ?> - <div class="p20" style="margin-top: 8px; background-color: white !important;"> - <h4>О себе</h4> - <?= - nl2br(htmlspecialchars($about)) - ?> - </div> - <?php } else if ($usercttc === True) { - echo $about; + <div class="p20" style="margin-top: 8px;"> + <h4>О себе</h4> + <?= + nl2br(htmlspecialchars($about)) + ?> + </div> + <?php } else if ($usercttc === True) { + echo $about; } ?> - <div style="margin-top: 25px;"><b><a href="/search?id=<?=$userprofile->i('id')?>">Найти все фотографии, сделанные этим пользователем</a></b></div> - - <?php - if ($userprofile->i('id') != Auth::userid()) { ?> - <script> + <div style="margin-top: 25px;"><b><a href="/search?id=<?= $userprofile->i('id') ?>">Найти все фотографии, сделанные этим пользователем</a></b></div> + <?php + if ($userprofile->i('id') != Auth::userid()) { ?> + <script> + $(document).ready(function() { + $('.toggle, .toggle-label').on('click', function() { + var toggle = $('.toggle').toggleClass('on'); -$(document).ready(function() -{ - $('.toggle, .toggle-label').on('click', function() - { - var toggle = $('.toggle').toggleClass('on'); + var subscr_cnt = $('#subscr_cnt'); + var cnt = parseInt(subscr_cnt.text()); + subscr_cnt.html(toggle.is('.on') ? cnt + 1 : cnt - 1); - var subscr_cnt = $('#subscr_cnt'); - var cnt = parseInt(subscr_cnt.text()); - subscr_cnt.html(toggle.is('.on') ? cnt+1 : cnt-1); - - $.get('/api/subscribe', { action: 'subscribe', id: <?=$userprofile->i('id')?>, subj: 'a' }, function (r) - { - if (r != 0 && r != 1) - { - toggle.toggleClass('on'); - alert(r); - } - else toggle.attr('class', (r == 1) ? 'toggle on' : 'toggle'); - }); - }); -}); - - -</script> -<?php -if (DB::query('SELECT follower_id FROM followers WHERE user_id=:userid AND follower_id=:followerid', array(':userid' => $userprofile->i('id'), ':followerid' => Auth::userid()))) { -$class = 'on'; - } - ?> - <div style="margin-top: 8px;"><div class="toggle <?=$class?>"><div class="handle"></div></div> <label class="toggle-label"><b>Подписка на новые фотографии этого автора</b> (подписаны: <b id="subscr_cnt"><?=DB::query('SELECT COUNT(*) FROM followers WHERE user_id=:uid', array(':uid'=>$userprofile->i('id')))[0]['COUNT(*)'];?></b>)</label></div> + $.get('/api/subscribe', { + action: 'subscribe', + id: <?= $userprofile->i('id') ?>, + subj: 'a' + }, function(r) { + if (r != 0 && r != 1) { + toggle.toggleClass('on'); + alert(r); + } else toggle.attr('class', (r == 1) ? 'toggle on' : 'toggle'); + }); + }); + }); + </script> + <?php + if (DB::query('SELECT follower_id FROM followers WHERE user_id=:userid AND follower_id=:followerid', array(':userid' => $userprofile->i('id'), ':followerid' => Auth::userid()))) { + $class = 'on'; + } + ?> + <div style="margin-top: 8px;"> + <div class="toggle <?= $class ?>"> + <div class="handle"></div> + </div> <label class="toggle-label"><b>Подписка на новые фотографии этого автора</b> (подписаны: <b id="subscr_cnt"><?= DB::query('SELECT COUNT(*) FROM followers WHERE user_id=:uid', array(':uid' => $userprofile->i('id')))[0]['COUNT(*)']; ?></b>)</label> + </div> + <?php } else { ?> + <div style="margin-top: 8px;">Пользователей, подписанных на мои фотографии: <b><?= DB::query('SELECT COUNT(*) FROM followers WHERE user_id=:uid', array(':uid' => $userprofile->i('id')))[0]['COUNT(*)']; ?></b></div> + <?php } ?> <?php } else { ?> - <div style="margin-top: 8px;">Пользователей, подписанных на мои фотографии: <b><?=DB::query('SELECT COUNT(*) FROM followers WHERE user_id=:uid', array(':uid'=>$userprofile->i('id')))[0]['COUNT(*)'];?></b></div> - <?php } ?> - <?php } else { ?> - <center><h1>Пользователь не найден</h1></center> + <center> + <h1>Пользователь не найден</h1> + </center> <center><img src="/static/img/404.jpg"></center> <?php } ?> </td> </tr> <tr> - <?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Footer.php'); ?> - </tr> + <?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Footer.php'); ?> + </tr> </table> diff --git a/views/pages/Profile/LK/Profile.php b/views/pages/Profile/LK/Profile.php index 0d90242..89d0cba 100644 --- a/views/pages/Profile/LK/Profile.php +++ b/views/pages/Profile/LK/Profile.php @@ -24,7 +24,7 @@ $user = new User(Auth::userid()); <div class="tabs"> <nav class="tab-nav"> <a href="/lk/profile" class="tab-item <?php if ($_GET['type'] === null) { ?> active <?php } ?>">Профиль</a> - <a href="?type=Security" class="tab-item <?php if ($_GET['type'] === 'Security') { ?> active <?php } ?>">Безопасность</a> + <!--a href="?type=Security" class="tab-item <?php if ($_GET['type'] === 'Security') { ?> active <?php } ?>">Безопасность</!--a--> <a href="?type=Personalization" class="tab-item <?php if ($_GET['type'] === 'Personalization') { ?> active <?php } ?>">Внешний вид</a> </nav> <div class="tab-content"> diff --git a/views/pages/Profile/LK/Profile/Personalization.php b/views/pages/Profile/LK/Profile/Personalization.php index ad307c8..d684e62 100644 --- a/views/pages/Profile/LK/Profile/Personalization.php +++ b/views/pages/Profile/LK/Profile/Personalization.php @@ -8,11 +8,12 @@ $themeManager = new ThemeManager(); $themeManager->loadThemes(); $themesList = $themeManager->getAllThemes(); -var_dump($_SESSION); +$selectedTheme = $_SESSION['selected_theme']; + if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['theme_id'])) { try { $themeManager->saveThemeToProfile($_POST['theme_id']); - header('Location: ' . $_SERVER['REQUEST_URI']); + die('<script>window.location.href = "/lk/profile?type=Personalization"</script>'); exit; } catch (Exception $e) { error_log($e->getMessage()); diff --git a/views/pages/Rules.php b/views/pages/Rules.php index b9a0c05..c170dd4 100644 --- a/views/pages/Rules.php +++ b/views/pages/Rules.php @@ -1,288 +1,44 @@ <?php -include($_SERVER['DOCUMENT_ROOT'] . "/classes/DB.php"); -include($_SERVER['DOCUMENT_ROOT'] . "/classes/Auth.php"); -if (!isLoggedIn()) { - header('Location: /'); -} -function getFullUrl() { - $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https://" : "http://"; - return $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; -} +use \App\Services\{Auth, DB, Date}; +use \App\Models\User; -$fullUrl = getFullUrl(); -$parsedUrl = parse_url($fullUrl); -$baseUrl = $parsedUrl['scheme'] . '://' . $parsedUrl['host']; -$pathAndQuery = str_replace($baseUrl, '', $fullUrl); - -function saveTasks() { - $filteredIds = []; - foreach ($_POST as $key => $value) { - if (strpos($key, 'rsn__') === 0) { - $id = substr($key, 5); - $filteredIds[] = $id; - } - } - - if (!empty($filteredIds)) { - foreach ($filteredIds as $id) { - $val = ((int)$_POST['rsn__' . $id] === 0) ? 1 : 2; - DB::query('UPDATE tasks SET checked=:c WHERE id=:id', [':id' => $id, ':c' => $val]); - } - } -} - -if (isset($_POST['approve'])) { - saveTasks(); - DB::query('UPDATE applications_details SET checked=:ch WHERE category_id=:id AND user_id=:uid', - [':ch' => 1, ':id' => $_GET['id'], ':uid' => $_GET['uid']]); -} elseif (isset($_POST['decline'])) { - saveTasks(); - DB::query('UPDATE applications_details SET checked=:ch WHERE category_id=:id AND user_id=:uid', - [':ch' => 2, ':id' => $_GET['id'], ':uid' => $_GET['uid']]); -} - -$categoryTitle = htmlspecialchars(DB::query('SELECT title FROM categories_sub WHERE id=:id', [':id' => $_GET['id']])[0]['title']); -$isAdmin = (int)($_GET['adm'] ?? 0) === 1; -$userId = $isAdmin ? $_GET['uid'] : isLoggedIn(); -$subs = DB::query('SELECT * FROM tasks WHERE category_id=:id AND user_id=:uid', [':id' => $_GET['id'], ':uid' => $userId]); - -$appDetails = DB::query('SELECT * FROM applications_details WHERE user_id = :uid AND category_id = :cid', - [':uid' => $userId, ':cid' => $_GET['id']]); -$formData = $appDetails[0] ?? [ - 'experience' => '', - 'work_days' => '[]', - 'work_time_from' => '', - 'work_time_to' => '', - 'description' => '', - 'myself' => 0 -]; -$workDays = json_decode($formData['work_days'], true); ?> <!DOCTYPE html> -<html lang="en"> +<html lang="ru"> + <head> -<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"> - <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"> + <?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/LoadHead.php'); ?> + + </head> + + <body> - <?php include($_SERVER['DOCUMENT_ROOT'] . '/classes/Navbar.php'); ?> - - <div class="container mt-5"> - <div class="row mt-3"> - <div class="col-md-3"> - <?php if (!$isAdmin) : ?> - <?php include($_SERVER['DOCUMENT_ROOT'] . '/classes/MySidebar.php'); ?> - <?php endif; ?> - </div> - - <div class="col-md-9"> - <form<?= $isAdmin ? ' action="' . htmlspecialchars($pathAndQuery) . '" method="post"' : ' id="subFormd"' ?>> - <div id="tsksa"> - <div class="list-group services-list"> - <h1><?= $categoryTitle ?></h1> - - <?php foreach ($subs as $s) : - $statusIcon = match((int)$s['checked']) { - 0 => '/static/svg/clock.svg', - 1 => '/static/svg/success-ic.svg', - 2 => '/static/svg/warning-ic.svg', - }; - $sdata = DB::query('SELECT * FROM tasks WHERE id=:id', [':id' => $s['id']])[0]; - $imgsd = json_decode($sdata['images']); - ?> - <!-- Task Item --> - <div class="list-group-item d-flex justify-content-between align-items-center"> - <div> - <h5><b><?= htmlspecialchars($s['title']) ?> (€<?= htmlspecialchars($s['cost']) ?>)</b></h5> - <p><?= htmlspecialchars($s['description']) ?></p> - <div class="mb-3"> - <?php if (!$isAdmin) : ?> - <a href="#" data-bs-toggle="modal" data-bs-target="#delete<?= $s['id'] ?>Modal" class="text-danger me-3">Supprimer</a> - <a href="#" data-bs-toggle="modal" data-bs-target="#edit<?= $s['id'] ?>Modal">Editer</a> - <?php else : ?> - <select required name="rsn__<?= $s['id'] ?>"> - <option value="">Select</option> - <option value="0">Accept</option> - <option value="1">Decline</option> - </select> - <?php endif; ?> - </div> - </div> - <img width="20" src="<?= $statusIcon ?>"> - </div> - - <!-- Delete Modal --> - <div class="modal fade" id="delete<?= $s['id'] ?>Modal" tabindex="-1"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <h5 class="modal-title">Confirmation</h5> - <button type="button" class="btn-close" data-bs-dismiss="modal"></button> - </div> - <div class="modal-body"> - Êtes-vous sûr de vouloir supprimer? - </div> - <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Non</button> - <a href="/api/deletetask.php?id=<?= $s['id'] ?>" class="btn btn-danger">Oui</a> - </div> - </div> - </div> - </div> - - <!-- Модальное окно редактирования задачи --> -<?php foreach ($subs as $s) : - $sdata = DB::query('SELECT * FROM tasks WHERE id=:id', [':id' => $s['id']])[0]; - $imgsd = json_decode($sdata['images']); -?> -<div class="modal fade" id="edit<?= $s['id'] ?>Modal" tabindex="-1"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <h5 class="modal-title">Modifier le service</h5> - <button type="button" class="btn-close" data-bs-dismiss="modal"></button> - </div> - <form id="subFormED<?= $s['id'] ?>"> - <div class="modal-body"> - <input type="hidden" name="taskid" value="<?= $s['id'] ?>"> - - <div class="mb-3"> - <label class="form-label">Titre</label> - <input type="text" class="form-control" name="serviceName" - value="<?= htmlspecialchars($sdata['title']) ?>"> - </div> - - <div class="mb-3"> - <label class="form-label">Coût</label> - <div class="input-group"> - <input type="number" class="form-control" name="serviceCost" - value="<?= htmlspecialchars($sdata['cost']) ?>"> - <span class="input-group-text">€</span> - <select name="serviceCostUnit" class="form-select"> - <option value="за услугу" <?= $sdata['unit'] === 'за услугу' ? 'selected' : '' ?>>Par service</option> - <option value="за час" <?= $sdata['unit'] === 'за час' ? 'selected' : '' ?>>Par heure</option> - </select> - </div> - <div class="form-check mt-2"> - <input class="form-check-input" type="checkbox" - id="negotiable<?= $s['id'] ?>" <?= $sdata['negotiable'] ? 'checked' : '' ?>> - <label class="form-check-label" for="negotiable<?= $s['id'] ?>"> - Négociable - </label> - </div> - </div> - - <div class="mb-3"> - <label class="form-label">Description</label> - <textarea class="form-control" name="serviceDescription" rows="3" - ><?= htmlspecialchars($sdata['description']) ?></textarea> - </div> - - <div class="mb-3"> - <label class="form-label">Photos</label> - <div class="row row-cols-4"> - <?php for ($i = 0; $i < 4; $i++) : ?> - <div class="col"> - <label class="upload-btn" - style="<?= !empty($imgsd[$i]) ? "background-image: url('{$imgsd[$i]}')" : '' ?>"> - <input type="file" class="upload-input" - name="serviceImages[]" - onchange="previewImage(event, this)"> - <i class="bi bi-plus-lg"></i> - </label> - </div> - <?php endfor; ?> - </div> - </div> + <div id="backgr"></div> + <table class="tmain"> + <?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Navbar.php'); ?> + <td class="main"> + <h1>Правила сайта</h1> + + <div class="p20" style="padding:20px"> + <?php + $myfile = fopen($_SERVER['DOCUMENT_ROOT'].'/config/rules.html', "r") or die("Unable to open file!"); + echo fread($myfile,filesize($_SERVER['DOCUMENT_ROOT'].'/config/rules.html')); + fclose($myfile); + ?> </div> - <div class="modal-footer"> - <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button> - <button type="submit" class="btn btn-primary">Sauvegarder</button> - </div> - </form> - </div> - </div> -</div> + </td> + </tr> + + <tr> - <script> -// JavaScript обработчик для формы редактирования -document.getElementById('subFormED<?= $s['id'] ?>').addEventListener('submit', function(e) { - e.preventDefault(); - const formData = new FormData(this); - - fetch('/api/edittask.php', { - method: 'POST', - body: formData - }) - .then(response => response.json()) - .then(data => { - if(data.success) { - bootstrap.Modal.getInstance(document.getElementById('edit<?= $s['id'] ?>Modal')).hide(); - window.location.reload(); - } - }) - .catch(error => console.error('Error:', error)); -}); -</script> -<?php endforeach; ?> - </div> - </div> - </form> + <?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Footer.php'); ?> - <!-- Вторая форма (About Specialization) --> - <form id="subFormdd" method="post"> - <h4 class="mt-5">A propos de la spécialisation</h4> - - <?php if ($appDetails && $appDetails[0]['checked'] === 0 && !$isAdmin) : ?> - <div class="alert alert-warning"> - Ваши данные на модерации - </div> - <?php endif; ?> - <div class="mb-3"> - <label class="form-label">Expérience</label> - <select class="form-select" name="experience"> - <?php for ($i = 0; $i <= 11; $i++) : ?> - <option value="<?= $i ?>" <?= $i == $formData['experience'] ? 'selected' : '' ?>> - <?= $i === 11 ? '10+ années' : ($i === 0 ? '—' : "$i année") ?> - </option> - <?php endfor; ?> - </select> - </div> + </tr> + </table> - <!-- Полное содержимое второй формы --> - - <?php if ($isAdmin) : ?> - <div class="mb-3"> - <button type="submit" name="approve" class="btn btn-primary">Approve</button> - <button type="submit" name="decline" class="btn btn-danger">Decline</button> - </div> - <?php else : ?> - <button type="submit" class="btn btn-primary">Économiser</button> - <?php endif; ?> - </form> - </div> - </div> - - <!-- New Service Modal --> - <div class="modal fade" id="newServiceModal" tabindex="-1"> - <div class="modal-dialog"> - <div class="modal-content"> - <!-- Полное содержимое модального окна нового сервиса --> - </div> - </div> - </div> - </div> - - <!-- Все скрипты из оригинала --> - <script> - $(document).ready(function() { - $('#multiple-select-field').select2({ - theme: "bootstrap-5", - closeOnSelect: false - }); - }); - </script> </body> + </html> \ No newline at end of file diff --git a/views/pages/Search/Index.php b/views/pages/Search/Index.php index 8c9ddea..e85b326 100644 --- a/views/pages/Search/Index.php +++ b/views/pages/Search/Index.php @@ -6,508 +6,52 @@ use App\Services\{Router, Auth, DB, Date}; <head> - <?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/LoadHead.php'); ?> + <?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/LoadHead.php'); ?> </head> <body> - <div id="backgr"></div> - <table class="tmain"> - <?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Navbar.php'); ?> - <tr> - <td class="main"> - <h1>Результаты поиска</h1> - <div>Найдено изображений: <b><?=DB::query('SELECT COUNT(*) FROM photos WHERE user_id=:uid AND moderated=1 ORDER BY id DESC', array(':uid'=>$_GET['id']))[0]['COUNT(*)']?></b> · <a href="#sf">Новый поиск</a></div><br> - <?php - $photos = DB::query('SELECT * FROM photos WHERE user_id=:uid AND moderated=1 ORDER BY id DESC', array(':uid'=>$_GET['id'])); - foreach ($photos as $p) { - echo '<div class="p20p"> + <div id="backgr"></div> + <table class="tmain"> + <?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/Navbar.php'); ?> + <tr> + <td class="main"> + <h1>Результаты поиска</h1> + <div>Найдено изображений: <b><?= DB::query('SELECT COUNT(*) FROM photos WHERE user_id=:uid AND moderated=1 ORDER BY id DESC', array(':uid' => $_GET['id']))[0]['COUNT(*)'] ?></b> · <a href="#sf">Новый поиск</a></div><br> + <?php + $photos = DB::query('SELECT * FROM photos WHERE user_id=:uid AND moderated=1 ORDER BY id DESC', array(':uid' => $_GET['id'])); + foreach ($photos as $p) { + echo '<div class="p20p"> <table> <tbody> <tr> - <td class="pb_photo" id="p1936120"><a href="/photo/'.$p['id'].'" target="_blank" class="prw"><img class="f" src="'.$p['photourl'].'"> + <td class="pb_photo" id="p1936120"><a href="/photo/' . $p['id'] . '" target="_blank" class="prw"><img class="f" src="' . $p['photourl'] . '"> <div class="hpshade"> '; - if (DB::query('SELECT COUNT(*) FROM photos_comments WHERE photo_id=:id', array(':id'=>$p['id']))[0]['COUNT(*)'] >= 1) { - echo '<div class="com-icon">'.DB::query('SELECT COUNT(*) FROM photos_comments WHERE photo_id=:id', array(':id'=>$p['id']))[0]['COUNT(*)'].'</div>'; - } - echo ' - <div class="eye-icon">'.DB::query('SELECT COUNT(*) FROM photos_views WHERE photo_id=:id', array(':id'=>$p['id']))[0]['COUNT(*)'].'</div></div> + if (DB::query('SELECT COUNT(*) FROM photos_comments WHERE photo_id=:id', array(':id' => $p['id']))[0]['COUNT(*)'] >= 1) { + echo '<div class="com-icon">' . DB::query('SELECT COUNT(*) FROM photos_comments WHERE photo_id=:id', array(':id' => $p['id']))[0]['COUNT(*)'] . '</div>'; + } + echo ' + <div class="eye-icon">' . DB::query('SELECT COUNT(*) FROM photos_views WHERE photo_id=:id', array(':id' => $p['id']))[0]['COUNT(*)'] . '</div></div> </a></td> <td class="pb_descr"> - <p><b class="pw-place">'.htmlspecialchars($p['place']).'</b></p> - <span class="pw-descr">'.htmlspecialchars($p['postbody']).'</span> - <p class="sm"><b>'.Date::zmdate($p['timeupload']).'</b><br>Автор: <a href="/author/'.$p['user_id'].'/">'.htmlspecialchars($p['username']).'</a></p> + <p><b class="pw-place">' . htmlspecialchars($p['place']) . '</b></p> + <span class="pw-descr">' . htmlspecialchars($p['postbody']) . '</span> + <p class="sm"><b>' . Date::zmdate($p['timeupload']) . '</b><br>Автор: <a href="/author/' . $p['user_id'] . '/">' . htmlspecialchars($p['username']) . '</a></p> </td> </tr> </tbody> </table> </div>'; - } - ?> -<form method="get" id="mform" class="p20w" style="padding:10px 20px 10px 10px"> -<table> -<tbody><tr> - <td colspan="2"><h4 style="margin:-5px 0 10px">Условия, относящиеся к ТС:</h4></td> -</tr> -<tr> - <td class="lcol">Страна:</td> - <td> - <input type="hidden" name="vrid" id="vrid" value="0"> - <div class="ac-loader"></div><span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" id="vrname" style="width: 250px; padding-left: 3px; padding-right: 3px;" value="Не имеет значения" class="ui-autocomplete-input" autocomplete="off"><div class="xsign" style="display: none;"></div> - </td> -</tr> -<tr> - <td class="lcol">Город ТС:</td> - <td> - <input type="hidden" name="vcid" id="vcid" value="-1" data-vrid="0"> - <div class="ac-loader"></div><span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" id="vcname" style="width: 250px; padding-left: 3px; padding-right: 3px;" value="Не имеет значения" class="ui-autocomplete-input" autocomplete="off"><div class="xsign" style="display: none;"></div> - </td> -</tr> -<tr> - <td class="lcol">Вид транспорта:</td> - <td> - <select name="vtype" id="vtype" class=""> - <option value="-1" class="" selected="">Не имеет значения</option> - <option value="1" class="s5">Трамвай</option> - <option value="2" class="s8">Троллейбус</option> - <option value="3" class="s7">Метрополитен</option> - <option value="4" class="s9">Монорельс</option> - <option value="5" class="s2">Фуникулёр</option> - <option value="6" class="s6">Транслор</option> - <option value="7" class="s9">Мувер (АТН)</option> - <option value="8" class="s9">Маглев</option> - <option value="9" class="s3">Электробус</option> -</select> </td> -</tr> -<tr> - <td class="lcol">Локация:</td> - <td> - <select name="loid" id="loid" style="width:400px" data-vcid="-1" disabled=""> - <option value="0">Не имеет значения</option> - </select> - </td> -</tr> -<tr> - <td class="lcol">Депо/Парк:</td> - <td> - <select name="did" id="did" style="width:400px" data-vcid="-1" data-vtype="-1" data-loid="0" disabled=""> - <option value="0">Не имеет значения</option> - </select> - </td> -</tr> -<tr> - <td class="lcol">Система:</td> - <td> - <input type="hidden" name="vgrid" id="vgrid" value="0"> - <div class="ac-loader"></div><span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" id="vgrname" style="width: 400px; padding-left: 3px; padding-right: 3px;" value="Не имеет значения" class="ui-autocomplete-input" autocomplete="off"><div class="xsign" style="display: none;"></div> - </td> -</tr> -<tr> - <td class="lcol">Назначение:</td> - <td style="padding-bottom:17px"> - <select name="serv"> - <option value="-1" selected="">Не имеет значения</option> - <option value="0">Пассажирский</option> - <option value="1">Служебный</option> - <option value="2">Музейный</option> -</select> </td> -</tr> -<tr> - <td class="lcol">Номер:</td> - <td><input type="text" name="num" style="width:100px" value=""></td> -</tr> -<tr> - <td class="lcol">Госномер:</td> - <td style="padding-bottom:17px"><input type="text" name="gos" style="width:100px" value=""></td> -</tr> -<tr> - <td class="lcol">Модель:</td> - <td> - <input type="hidden" name="mid" id="mid" value="-1" data-vtype="-1"> - <div class="ac-loader"></div><span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" id="mname" style="width: 250px; padding-left: 3px; padding-right: 3px;" value="Не имеет значения" disabled="" class="ui-autocomplete-input" autocomplete="off"><div class="xsign" style="display: none;"></div> - <input type="checkbox" name="sub" id="sub" value="1"> <label for="sub">Учесть подмодели</label> - </td> -</tr> -<tr> - <td class="lcol">Шасси:</td> - <td style="padding-bottom:17px"> - <input type="hidden" name="chid" id="chid" value="-1" data-vtype="-1"> - <div class="ac-loader"></div><span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" id="chname" style="width: 250px; padding-left: 3px; padding-right: 3px;" value="Не имеет значения" disabled="" class="ui-autocomplete-input" autocomplete="off"><div class="xsign" style="display: none;"></div> - <input type="checkbox" name="sub2" id="sub2" value="1"> <label for="sub2">Учесть подмодели</label> - </td> -</tr> -<tr> - <td colspan="2"><h4 style="margin:0 0 10px">Условия, относящиеся к галереям:</h4></td> -</tr> -<tr> - <td class="lcol">Город галереи:</td> - <td> - <input type="hidden" name="gcid" id="gcid" value="-1"> - <div class="ac-loader"></div><span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" id="gcname" style="width: 250px; padding-left: 3px; padding-right: 3px;" value="Не имеет значения" class="ui-autocomplete-input" autocomplete="off"><div class="xsign" style="display: none;"></div> - </td> -</tr> -<tr> - <td class="lcol">Вид транспорта:</td> - <td> - <select name="gtype" id="gtype" class=""> - <option value="-1" class="" selected="">Не имеет значения</option> - <option value="0" class="s0">Без вида транспорта</option> - <option value="1" class="s5">Трамвай</option> - <option value="2" class="s8">Троллейбус</option> - <option value="3" class="s7">Метрополитен</option> - <option value="4" class="s9">Монорельс</option> - <option value="5" class="s2">Фуникулёр</option> - <option value="6" class="s6">Транслор</option> - <option value="7" class="s9">Мувер (АТН)</option> - <option value="8" class="s9">Маглев</option> - <option value="9" class="s3">Электробус</option> -</select> </td> -</tr> -<tr> - <td class="lcol">Раздел:</td> - <td> - <select name="sid" id="sid" style="width:400px"> - <option value="0" selected="">Не имеет значения</option> - <option value="1">События ГЭТ</option> - <option value="2">Фотогалереи ГЭТ</option> - <option value="6">Транспортное сообщество</option> - <option value="7">Выставки</option> - <option value="8">Обзоры</option> - <option value="9">Строительство и реконструкция</option> - <option value="13">Железная дорога</option> - <option value="14">Оборудование электротранспорта</option> - <option value="15">Творчество</option> - <option value="20">Метрополитены</option> - <option value="21">Монорельсы</option> - <option value="22">Фуникулёры</option> - <option value="112">События метрополитена</option> - <option value="113">Карты и схемы</option> - <option value="114">Временный раздел</option> -</select> </td> -</tr> -<tr> - <td class="lcol">Галерея:</td> - <td style="padding-bottom:17px"> - <select name="gid" id="gid" style="width:400px" data-gcid="-1" data-gtype="-1" data-sid="0" disabled=""> - <option value="0">Не имеет значения</option> - </select> - </td> -</tr> -<tr> - <td colspan="2"><h4 style="margin:0 0 10px">Условия, относящиеся к фото:</h4></td> -</tr> -<tr> - <td class="lcol">Вид транспорта:</td> - <td><select name="ptype" id="ptype" class=""> - <option value="-1" class="" selected="">Не имеет значения</option> - <option value="0" class="s0">Без вида транспорта</option> - <option value="1" class="s5">Трамвай</option> - <option value="2" class="s8">Троллейбус</option> - <option value="3" class="s7">Метрополитен</option> - <option value="4" class="s9">Монорельс</option> - <option value="5" class="s2">Фуникулёр</option> - <option value="6" class="s6">Транслор</option> - <option value="7" class="s9">Мувер (АТН)</option> - <option value="8" class="s9">Маглев</option> - <option value="9" class="s3">Электробус</option> -</select></td> -</tr> -<tr> - <td class="lcol">Страна:</td> - <td> - <input type="hidden" name="prid" id="prid" value="0"> - <div class="ac-loader"></div><span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" id="prname" style="width: 250px; padding-left: 3px; padding-right: 3px;" value="Не имеет значения" class="ui-autocomplete-input" autocomplete="off"><div class="xsign" style="display: none;"></div> - </td> -</tr> -<tr> - <td class="lcol">Система:</td> - <td> - <input type="hidden" name="pgrid" id="pgrid" value="0"> - <div class="ac-loader"></div><span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" id="pgrname" style="width: 400px; padding-left: 3px; padding-right: 3px;" value="Не имеет значения" class="ui-autocomplete-input" autocomplete="off"><div class="xsign" style="display: none;"></div> - </td> -</tr> -<tr> - <td class="lcol">Город съёмки:</td> - <td> - <input type="hidden" name="pcid" id="pcid" value="-1" data-prid="0"> - <div class="ac-loader"></div><span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" id="pcname" style="width: 400px; padding-left: 3px; padding-right: 3px;" value="Не имеет значения" class="ui-autocomplete-input" autocomplete="off"><div class="xsign" style="display: none;"></div> - </td> -</tr> -<tr> - <td class="lcol">Место съёмки:</td> - <td style="padding-bottom:15px"> - <div class="ac-loader"></div><span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" name="place" id="place" style="width:400px; margin-bottom:3px" value="" class="ui-autocomplete-input" autocomplete="off"><br> - <input type="checkbox" name="strict" id="strict" value="1"> <label for="strict">Строгое соответствие места съёмки</label><br> - <input type="checkbox" name="no_ren" id="no_ren" value="1" disabled=""> <label for="no_ren" style="color:#888">Без учёта переименований</label> - </td> -</tr> -<tr> - <td class="lcol">Маршрут:</td> - <td><input type="text" name="route" style="width:60px" value=""></td> -</tr> -<tr> - <td class="lcol">Примечание:</td> - <td><input type="text" name="notes" style="width:200px" value=""></td> -</tr> -<tr> - <td class="lcol">Описание:</td> - <td style="padding-bottom:17px"><input type="text" name="descr" style="width:400px" value=""></td> -</tr> -<tr> - <td class="lcol">Конкурсное:</td> - <td><select name="konk"> - <option value="0" selected="">Не имеет значения</option> - <option value="10">Все участники</option> - <option value="9">3 место и выше</option> - <option value="8">2 место и выше</option> - <option value="7">1 место и выше</option> - <option value="5">Пятёрка лучших за месяц</option> - <option value="4">Фото месяца</option> - <option value="2">Тройка лучших за год</option> - <option value="1">Фото года</option> -</select></td> -</tr> -<tr> - <td class="lcol">Ракурс:</td> - <td> - <input type="hidden" name="view" id="view" value="-1"> - <input type="text" id="view_txt" value="Не имеет значения" style="width:300px; cursor:pointer" readonly=""> - <div id="views-selector" style="position:absolute; padding:5px; z-index:20; display:none" class="p20 shadow"> - <table id="views"> - <tbody><tr> - <td colspan="3" style="text-align:center"><input type="checkbox" name="view_top" value="20" id="v20"> <label for="v20">Вид сверху</label></td> - <td></td> - </tr> - <tr> - <td><input type="radio" name="view_s" value="4" title="Сзади-слева (окна)" class="views-radio-single" style="position:relative; top:7px; left:7px"></td> - <td style="text-align:center"> - <input type="radio" name="view_s" value="8" title="Левый борт" class="views-radio-single"> - </td> - <td><input type="radio" name="view_s" value="2" title="Спереди-слева (окна)" style="position:relative; top:7px; left:-7px"></td> - <td style="padding:0 35px; line-height:23px" rowspan="3"> - <div><input type="radio" name="view_s" value="12" id="v12"> <label for="v12">Заводская табличка</label></div> - <div><input type="radio" name="view_s" value="13" id="v13"> <label for="v13">Отдельные элементы ТС</label></div> - <div class="twoside-old"><input type="radio" name="view_s" value="14" id="v14"> <label for="v14">Не определяется (двухстороннее ТС)</label></div> - <div><input type="radio" name="view_s" value="0" id="v0"> <label for="v0">Не указан</label></div> - <div><input type="radio" name="view_s" value="-1" id="vnone"> <label for="vnone">Не имеет значения</label></div> - <div class="sm" style="margin-top:15px"><a href="#" class="views-toggle-link dot">Переключить на: <span class="twoside-single">Одностороннее ТС</span><span class="twoside-twoside">Двухстороннее ТС</span></a></div> - </td> - </tr> - <tr> - <td style="padding:0 2px"><input type="radio" name="view_s" value="7" title="Вид строго сзади" class="views-radio-single"></td> - <td class="views-image"> - <table style="width:138px; height:82px"> - <tbody><tr> - <td style="text-align:left; padding-left:25px"> - <input type="radio" name="view_s" value="9" title="Салон, вид вперёд"> - </td> - <td style="text-align:right; padding:0"> - <input type="radio" name="view_s" value="10" title="Салон, вид назад" class="views-radio-single"> - <input type="radio" name="view_s" value="11" title="Кабина" style="position:relative; top:-7px"> - </td> - </tr> - </tbody></table> - </td> - <td style="padding:0 2px"><input type="radio" name="view_s" value="5" title="Вид строго спереди"></td> - </tr> - <tr> - <td><input type="radio" name="view_s" value="3" title="Сзади-справа (двери)" class="views-radio-single" style="position:relative; top:-7px; left:7px"></td> - <td style="text-align:center"> - <input type="radio" name="view_s" value="6" title="Правый борт"> - </td> - <td><input type="radio" name="view_s" value="1" title="Спереди-справа (двери)" style="position:relative; top:-7px; left:-7px"></td> - </tr> - <tr> - <td colspan="3" style="text-align:center"><input type="checkbox" name="view_bottom" value="40" id="v40"> <label for="v40">Вид снизу</label></td> - <td></td> - </tr> -</tbody></table> -<script> - -function openViewSelector(val, el, twoside) -{ - var selector = $('#views-selector'); - - var view = val % 20; - var modifier = val - view; - - $('input[value="' + view + '"]', selector).prop('checked', true); - $('#v20').prop('checked', modifier == 20); - $('#v40').prop('checked', modifier == 40); - - if (view != 14) - { - selector.attr('data-twoside', twoside); - $('.twoside-old').hide(); - } - else - { - selector.attr('data-twoside', 1); - $('.twoside-old').show(); - } + } + ?> + </tbody> - var p = el.offset(); - selector.css('left', p.left + 'px').css('top', (p.top + el.height() + 3) + 'px').show(); -} - - -function setView(e, func) -{ - var selector = $('#views-selector'); - - var view = parseInt($('input[type="radio"]:checked', selector).val()); - var modifier = parseInt($('input[type="checkbox"]:checked', selector).val()); - if (isNaN(modifier)) modifier = 0; - - var label = view || !modifier ? views[view] : ''; - if (label != '' && modifier) label += ' + '; - if (modifier) label += views[modifier]; - - func(e, view, modifier, label); - - selector.hide(); -} - - -function setViewSelectorCallback(func) -{ - var selector = $('#views-selector'); - - $('input[type="radio"]', selector).on('click', function(e) - { - setView(e, func); - }); - - $('input[type="checkbox"]', selector).on('click', function() - { - if ($(this).is('#v20:checked')) $('#v40').prop('checked', false); else - if ($(this).is('#v40:checked')) $('#v20').prop('checked', false); - }); - - $(document).on('click', function(e) - { - if ($(e.target).closest('#views-selector').length == 0 && $('#views-selector').is(':visible')) - { - setView(e, func); - } - }) - .on('keydown', function(e) - { - // Закрытие селектора ракурса по Esc или Backspace - if ((e.which == 27 || e.which == 8) && $('#views-selector').is(':visible')) - { - e.preventDefault(); - setView(e, func); - } - }); - -} - - -$(document).ready(function() -{ - $('.views-toggle-link').on('click', function() - { - var selector = $('#views-selector'); - var twoside = selector.attr('data-twoside'); - - selector.attr('data-twoside', twoside == 1 ? 0 : 1); - return false; - }); -}); - -</script> - </div> - </td> -</tr> -<!--tr> - <td class="lcol">Модель камеры:</td> - <td style="padding-bottom:17px"><input type="text" name="cammod" style="width:300px" value=""></td> -</!--tr> -<tr> - <td class="lcol">Пользователь:</td> - <td> - <input type="hidden" name="aid" id="aid" value="0"> - <div class="ac-loader"></div><span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span><input type="text" id="aname" style="width: 200px; padding-left: 4px; padding-right: 3px;" value="Не имеет значения" class="ui-autocomplete-input" autocomplete="off"><div class="xsign" style="display: none;"></div> - </td> -</tr> -<tr> - <td class="lcol">Авторство:</td> - <td><select name="auth"> - <option value="0" selected="">Не имеет значения</option> - <option value="1">только авторские</option> - <option value="2">присланные этим пользователем</option> -</select> </td> -</tr> -<tr> - <td></td> - <td><input type="checkbox" name="fav" id="fav" value="1"> <label for="fav">Поиск в Избранном</label></td> -</tr> -<tr> - <td></td> - <td><input type="checkbox" name="lost" id="lost" value="1"> <label for="lost">Потерянные фотографии</label><br> </td> -</tr> -<tr> - <td class="lcol">Дата съёмки с</td> - <td> - <input type="text" name="date1" id="date1" size="10" maxlength="10" value="12.02.2025" disabled=""> по - <input type="text" name="date2" id="date2" size="10" maxlength="10" value="12.02.2025" disabled=""> - <input type="checkbox" name="anydate" id="anydate" value="1" checked="checked"> <label for="anydate">Не имеет значения</label> - </td> -</tr> -<tr> - <td class="lcol">Опубликовано с</td> - <td> - <input type="text" name="pub1" id="pub1" size="10" maxlength="10" value="12.02.2025" disabled=""> по - <input type="text" name="pub2" id="pub2" size="10" maxlength="10" value="12.02.2025" disabled=""> - <input type="checkbox" name="anypub" id="anypub" value="1" checked="checked"> <label for="anypub">Не имеет значения</label> - </td> -</tr> -<tr> - <td></td> - <td class="sm" style="color:#888">Даты в формате ДД.ММ.ГГГГ<br> </td> -</tr> -<tr> - <td></td><td> </td> -</tr> -<tr> - <td class="lcol">Лицензии:</td> - <td> - <input type="checkbox" id="license_cc1" value="1" name="license_cc"> <label for="license_cc1">Выбрать только с свободными лицензиями</label><br> - <input type="checkbox" id="license_cc2" value="1" name="license_cc_commerce" disabled=""> <label for="license_cc2">Материалы для коммерческого использования</label><br> - <input type="checkbox" id="license_cc3" value="1" name="license_cc_derivatives" disabled=""> <label for="license_cc3">Материалы, которые можно изменять, адаптировать или использовать как основу</label> - </td> -</tr> -<tr> - <td></td><td> </td> -</tr> -<tr> - <td align="right">Сортировать по </td> - <td><select name="order"> - <option value="0">городу, бортовому номеру, дате съёмки</option> - <option value="1">дате съёмки, городу, бортовому номеру</option> - <option value="2">времени публикации (сверху старые)</option> - <option value="3" selected="">времени публикации (сверху новые)</option> - <option value="4">числу просмотров</option> - <option value="5">рейтингу</option> - <option value="6">числу комментариев</option> -</select> </td> -</tr> -<tr> - <td></td> - <td><br><input type="submit" value=" Искать "></td> -</tr--> -</tbody></table> -</form> - </tbody> - - - </table> + </table> </body> diff --git a/views/pages/Vehicle/DBEdit.php b/views/pages/Vehicle/DBEdit.php index fa4218c..50023c5 100644 --- a/views/pages/Vehicle/DBEdit.php +++ b/views/pages/Vehicle/DBEdit.php @@ -1,13 +1,57 @@ <?php -use \App\Services\{Auth, DB, Date}; +use \App\Services\{Auth, DB, Date, Captcha}; use \App\Models\{Vehicle, User}; +$vehicle = DB::query('SELECT * FROM entities WHERE id=:id', array(':id' => $_GET['type']))[0]; +$lastRequestUnix = DB::query('SELECT created_at FROM entities_requests WHERE user_id=:id ORDER BY id DESC LIMIT 1', array(':id' => Auth::userid()))[0]['created_at']; +$secondsDifference = time() - $lastRequestUnix; +$hoursDifference = floor($secondsDifference / 3600); +if (isset($_POST['create'])) { + if ($hoursDifference >= 23) { + try { + if (NGALLERY['root']['security']['captcha'] === true) { + $turnstile = new Captcha(NGALLERY['root']['security']['cloudflareturnstile-keys']['server']); + $turnstile->setToken($_POST['cf-turnstile-response']); + $turnstile->setRemoteIp($_SERVER['REMOTE_ADDR']); + $result = $turnstile->verify(); + } + $inputs = $_POST; + + $filteredInputs = []; + foreach ($inputs as $key => $value) { + if (strpos($key, 'modelinput_') === 0) { + $filteredInputs[$key] = $value; + } + } + ksort($filteredInputs); + $result = []; + + $counter = 1; + + foreach ($filteredInputs as $key => $value) { + $result[$counter] = [ + 'value' => $value + ]; + $counter++; + } + $jsonResult = json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + + DB::query('INSERT INTO entities_requests VALUES (\'0\', :user_id, :createdate, :entityid, :content, 0)', array(':user_id' => Auth::userid(), ':createdate' => time(), ':entityid' => $_GET['type'], ':content' => $jsonResult)); + $success = 1; + } catch (Exception $e) { + die("Error: " . $e->getMessage()); + } + } +} ?> <!DOCTYPE html> <html lang="ru"> +<script + src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" + defer></script> <head> <?php include($_SERVER['DOCUMENT_ROOT'] . '/views/components/LoadHead.php'); ?> @@ -23,104 +67,10 @@ use \App\Models\{Vehicle, User}; <tr> <td class="main"> <h1>Внесение изменений в БД</h1> - <script src="/js/jquery-ui.js?1633005526"></script> - <script src="/js/selector.js?1730197663"></script> - <script> - addTexts({ - 'VF_MAXLENGTH': 'Буфер обмена содержит %s знаков, но значение в этом поле не может быть длиннее %s знаков!\nВероятно, вы пытаетесь вставить некорректные данные' - }); + <?php if ($success) { ?> <div style="border:solid 1px rgb(0, 140, 86); padding:6px 10px 7px; margin-bottom:13px; background-color:rgb(137, 216, 150);">Заявка на рассмотрение успешно отправлена</div> <?php } ?> + <?php if ($hoursDifference >= 23) { ?> + <form method="post" id="mform" action="<?= $_SERVER['REQUEST_URI'] ?>"> - $(document).ready(function() { - $('#mname').autocompleteSelector('mid', '/api.php?action=get-models&type=2', { - minLength: 1, - defaultLabel: '(модель неизвестна)', - defaultValue: 632 - }); - $('#chname').autocompleteSelector('chid', '/api.php?action=get-chassis&type=2', { - minLength: 1, - defaultLabel: '(нет)' - }); - - $('#state, #service').change(function() { - $(this).attr('class', $('option:selected', this).attr('class')); - }).change(); - - $('#mform').on('submit', function() { - var built_y_len = $('#built_y').val().length; - var scrap_y_len = $('#scrap_y').val().length; - - if (built_y_len > 1 && built_y_len < 4 || - scrap_y_len > 1 && scrap_y_len < 4) { - alert('Неверное значение в поле «год» (0, 1 либо 4 символа).'); - return false; - } - - var source = $('#source'); - - if (source.val().trim().length < 4) { - alert('Не указан источник сведений. Пожалуйста, заполните соответствующее поле..'); - source[0].focus(); - return false; - } - - $('input[type="submit"]', this).prop('disabled', true); - - return true; - }); - - // Фильтрация вставляемых из буфера данных - $('#num, #gos, #zn, #vin, #cn, #start_y, #leave_y, #built_y, #scrap_y').on('paste', function(e) { - var field = $(this); - var text = e.originalEvent.clipboardData.getData('Text').trim(); - - var maxlength = parseInt(field.attr('maxlength')); - if (maxlength && text.length > maxlength) - alert(_text['VF_MAXLENGTH'].replace('%s', text.length).replace('%s', maxlength) + '.'); - else field.insertAtCaret(text); - - return false; - }); - - // Опции даты - $('.approx-aprx').css('font-weight', 'normal').on('change', function() { - $(this).attr('class', 'approx-aprx ' + $('option:selected', this).attr('class')) - }).change(); - }); - - - $.fn.insertAtCaret = function(myValue) { - return this.each(function() { - if (document.selection) { - // For browsers like Internet Explorer - this.focus(); - var sel = document.selection.createRange(); - sel.text = myValue; - this.focus(); - } else - if (this.selectionStart || this.selectionStart == '0') { - // For browsers like Firefox and Webkit based - var startPos = this.selectionStart; - var endPos = this.selectionEnd; - var scrollTop = this.scrollTop; - this.value = this.value.substring(0, startPos) + myValue + this.value.substring(endPos, this.value.length); - this.focus(); - this.selectionStart = startPos + myValue.length; - this.selectionEnd = startPos + myValue.length; - this.scrollTop = scrollTop; - } else { - this.value += myValue; - this.focus(); - } - }) - }; - </script> - - <form method="post" id="mform" action="?action=write"> - <input type="hidden" name="cid" value="14"> - <input type="hidden" name="type" value="2"> - <input type="hidden" name="link_gos" value="0"> - - <input type="hidden" name="num" id="num" value=""> <h4>Какую запись вы хотите уточнить?</h4> <div class="p20w"> @@ -136,17 +86,17 @@ use \App\Models\{Vehicle, User}; <td class="d" colspan="7">Никакую, я хочу добавить новое ТС</td> </tr> <?php - $entities = DB::query('SELECT * FROM entities_data WHERE entityid=:id AND (LOWER(title) LIKE :title) OR id=:title', array(':title'=>$_GET['num'], ':id'=>$_GET['type'])); + $entities = DB::query('SELECT * FROM entities_data WHERE entityid=:id AND (LOWER(title) LIKE :title)', array(':title' => $_GET['num'], ':id' => $_GET['type'])); foreach ($entities as $e) { echo '<tr> - <td class="ds"><input type="radio" name="base_nid" id="n'.$e['id'].'" value="'.$e['id'].'" onclick="fillFields('.$e['id'].')"></td> - <td class="n"><a href="/vehicle/'.$e['id'].'" target="_blank">'.$e['id'].'</a></td> - <td class="ds">'.$e['title'].'</td> + <td class="ds"><input type="radio" name="base_nid" id="n' . $e['id'] . '" value="' . $e['id'] . '" onclick="fillFields(' . $e['id'] . ')"></td> + <td class="n"><a href="/vehicle/' . $e['id'] . '" target="_blank">' . $e['id'] . '</a></td> + <td class="ds">' . $e['title'] . '</td> </tr>'; } ?> - + </tbody> </table> </div> @@ -161,95 +111,49 @@ use \App\Models\{Vehicle, User}; $data = json_decode($vehicle['sampledata'], true); $count = 1; foreach ($data as $d) { - + if ($d['important'] === "1") { $imp = 'required'; } echo ' <tr> <td class="lcol">' . $d['name'] . '</td> - <td style="padding-bottom:15px"><input type="text" name="modelinput_'.$count.'" id="num" style="width:80px" maxlength="21" value=""></td> + <td style="padding-bottom:15px"><input type="text" name="modelinput_' . $count . '" id="num" style="width:80px" maxlength="21" required></td> </tr>'; - $count++; + $count++; } ?> <tr> <td style="width: 10%"></td> - - <script> - var vdata = {}; - - vdata[0] = [0, '', '', '', 632, '(модель неизвестна)', 0, '(нет)', 0, 1, '', '', '', 0, '', 0, 0, '', 0, '', 0, '', 0, '', 0, '', 0, '', '']; - vdata[594939] = [27, '48', '', '', 135, 'ПТЗ-5283', 0, '(нет)', 0, 5, '14', '', '', 11, '2002', 10, 0, '', 10, '2018-11-30', 10, '0000-00-00', 0, '0000-00-00', 0, '2022-00-00', 0, '', '']; - - function setDateByYM(field, y, m, approx) { - $('#' + field + '_m').val(m == 0 ? '' : m); - $('#' + field + '_y').val(y == '0000' ? '' : y); - $('#' + field + '_approx_aprx').val(approx).change(); - } - function setDateByDate(field, date, approx) { - var d = date.substring(8, 10); - var m = date.substring(5, 7); - var y = date.substring(0, 4); - - $('#' + field + '_d').val(d == 0 ? '' : d); - $('#' + field + '_m').val(m == 0 ? '' : m); - $('#' + field + '_y').val(y == 0 ? '' : y); - $('#' + field + '_approx_aprx').val(approx).change(); - } - - - function fillFields(nid) { - var i = 0; - - $('#did').val(vdata[nid][i++]); - $('#num').val(vdata[nid][i++]); - $('#gos').val(vdata[nid][i++]); - $('#nu2').val(vdata[nid][i++]); - $('#mid').val(vdata[nid][i++]); - $('#mname').val(vdata[nid][i++]); - $('#chid').val(vdata[nid][i++]); - $('#chname').val(vdata[nid][i++]); - $('#service').val(vdata[nid][i++]).change(); - $('#state').val(vdata[nid][i++]).change(); - $('#zn').val(vdata[nid][i++]); - $('#cn').val(vdata[nid][i++]); - $('#vin').val(vdata[nid][i++]); - - setDateByYM('built', vdata[nid][i + 1], vdata[nid][i], vdata[nid][i + 2]); - i += 3; - setDateByYM('scrap', vdata[nid][i + 1], vdata[nid][i], vdata[nid][i + 2]); - i += 3; - - setDateByDate('start', vdata[nid][i], vdata[nid][i + 1]); - i += 2; - setDateByDate('launc', vdata[nid][i], vdata[nid][i + 1]); - i += 2; - setDateByDate('haltd', vdata[nid][i], vdata[nid][i + 1]); - i += 2; - setDateByDate('leave', vdata[nid][i], vdata[nid][i + 1]); - i += 2; - - $('#note').val(vdata[nid][i++]); - $('#history').val(vdata[nid][i++]); - } - </script> </tr> + <?php + if (NGALLERY['root']['security']['captcha'] === true) { ?> + <tr> + <td></td> + <td> + <br> + <div class="cf-turnstile" data-sitekey="<?= NGALLERY['root']['security']['cloudflareturnstile-keys']['client'] ?>"></div> + </td> + </tr> + <?php } ?> <tr> <td></td> <td> <br> - <input type="submit" value=" Отправить "> + <input name="create" type="submit" value=" Отправить "> </td> </tr> </tbody> </table> </div> </form> + <?php } else if (!$success) { + echo ' <div style="border:solid 1px rgb(140, 0, 0); padding:6px 10px 7px; margin-bottom:13px; background-color:rgb(216, 137, 137);">Заявки можно отправлять раз в 24 часа</div>'; + } + ?> - <div><a href="vehicles.php?cid=14&type=2&mid=632">Вернуться назад</a></div><br> <div class="p20"> <h4>Правила заполнения формы</h4> <ul class="straight">