This commit is contained in:
themohooks 2025-05-26 12:17:26 +03:00
parent f93696cb14
commit a10056228f
33 changed files with 1280 additions and 1917 deletions

View file

@ -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,
)
);
}
}

View file

@ -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()
{

View file

@ -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
)
);
}
}

View file

@ -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();
}
}
}

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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');
}
}

27
app/Models/Admin/News.php Normal file
View file

@ -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>';
}
}

View file

@ -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 '

98
app/Services/Captcha.php Normal file
View file

@ -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']}");
}
}
}

View file

@ -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');

View file

@ -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: ''

View file

@ -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; }
}

View file

@ -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">&times;</span>
<span data-close-modal-id="` +
modalid +
`" class="close">&times;</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>&ensp;&emsp;Ctrl + Enter
<button type="submit" onclick="editComment('` +
id +
`', document.getElementById('bodypost__commedit` +
id +
`').value, '` +
modalid +
`')" id="sbmt">Отредактировать</button>&ensp;&emsp;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">&times;</span>
<span data-close-modal-id="` +
modalid +
`" class="close">&times;</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();
},
});
};

View file

@ -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);

View file

@ -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()

View file

@ -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;

View file

@ -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>

View file

@ -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>

View file

@ -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) {

View file

@ -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>
</div>

View file

@ -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>

View file

@ -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>';
}
}

View file

@ -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>

View file

@ -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>

View file

@ -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);

View file

@ -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">

View file

@ -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&amp;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> &nbsp;<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> &nbsp;<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>

View file

@ -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">

View file

@ -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());

View file

@ -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>

View file

@ -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> &nbsp;·&nbsp; <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> &nbsp;·&nbsp; <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>&nbsp;
<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>&nbsp;
<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>&nbsp;</td>
</tr>
<tr>
<td class="lcol">Дата съёмки с</td>
<td>
<input type="text" name="date1" id="date1" size="10" maxlength="10" value="12.02.2025" disabled=""> &nbsp;по&nbsp;
<input type="text" name="date2" id="date2" size="10" maxlength="10" value="12.02.2025" disabled="">&nbsp;
<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=""> &nbsp;по&nbsp;
<input type="text" name="pub2" id="pub2" size="10" maxlength="10" value="12.02.2025" disabled="">&nbsp;
<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>&nbsp;</td>
</tr>
<tr>
<td></td><td>&nbsp;</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>&nbsp;</td>
</tr>
<tr>
<td align="right">Сортировать по&nbsp;</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="&nbsp; &nbsp; &nbsp; Искать &nbsp; &nbsp; &nbsp;"></td>
</tr-->
</tbody></table>
</form>
</tbody>
</table>
</table>
</body>

View file

@ -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="&nbsp; &nbsp; &nbsp; Отправить &nbsp; &nbsp; &nbsp;">
<input name="create" type="submit" value="&nbsp; &nbsp; &nbsp; Отправить &nbsp; &nbsp; &nbsp;">
</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&amp;type=2&amp;mid=632">Вернуться назад</a></div><br>
<div class="p20">
<h4>Правила заполнения формы</h4>
<ul class="straight">