
Получите клиентов в любой нише!
Делегируйте запуск рекламы нам — бесплатно
Подробнее
6.0

PHP задачи с собеседований
5.0
17
Поделиться
В избранное
Канал временно
не принимает заявки
не принимает заявки
Выберите другой из рекомендаций или получите готовую корзину под ваш бюджет с помощью AI
Персональный AI-подбор
Последние посты канала
Авторизация действий над определенными полями на основе ролей и разрешений
Использую пакет spatie-permission, Laravel 11.У юзера есть permission формата entity.action.value, например - order.edit.description(редактировать описание заказа) или order.see.Accepted (видеть заказы со статусом Accepted). У меня появились две идеи как это авторизовать: первая - написать кастомное правило валидации и применить к каждому полю в FormRequset:
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (!Gate::allows($this->action,[$attribute,auth()->user()])){
$fail('You do not have permission to interact with this field.');
}
}
Gate:
Gate::define('order-edit-field', function (User $user,string $attribute): bool {
return (in_array($attribute,$user->convertPermissionToValue('order.edit')) || $user->isAdmin());
});
Идея в чем - все разрешения обрезаются с помощью convertPermissionToValue() до value , и это value сравнивается с пришедшем с реквеста полем.
Вторая мысль - через политики:
public function update(User $user, Order $order,array $data): bool
{
if ($user->isAdmin()){
return true;
}
$fields = $user->convertPermissionToValue('order.edit');
$statuses = $user->convertPermissionToValue('order.view');
return in_array($order->status,$statuses)
&& array_reduce(array_keys($data),function ($accum, $item ) use ($fields){
return in_array($item,$fields) && $accum;
},true);
}
Сравнивается .value и fields реквеста, если какого то field нет в value - 403. Таким же образом проверяются любые действия над сущностями - permissions обрезаются до value, value сравнивается с полями ,со статусами, и вообще с чем угодно ,над чем нужно будет совершить action.
В чем собственно говоря вопрос: хоть один из этих вариантов - так вообще норм делать ? Сколько бы не гуглил, я не смог найти как именно организовать авторизацию действий над конкретными полями. Есть какие примеры реализации такого, как бы это вы сделали, где разумно было бы делать подобные проверки, хорошая ли вообще идея с convert разрешений, рабочая ли идея с такими составными permission. Буду ооочень благодарен за любую помощь или подсказку, ибо я новичок и даже те два примера выше выдались огромными нервозатратами)
Использую пакет spatie-permission, Laravel 11.У юзера есть permission формата entity.action.value, например - order.edit.description(редактировать описание заказа) или order.see.Accepted (видеть заказы со статусом Accepted). У меня появились две идеи как это авторизовать: первая - написать кастомное правило валидации и применить к каждому полю в FormRequset:
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (!Gate::allows($this->action,[$attribute,auth()->user()])){
$fail('You do not have permission to interact with this field.');
}
}
Gate:
Gate::define('order-edit-field', function (User $user,string $attribute): bool {
return (in_array($attribute,$user->convertPermissionToValue('order.edit')) || $user->isAdmin());
});
Идея в чем - все разрешения обрезаются с помощью convertPermissionToValue() до value , и это value сравнивается с пришедшем с реквеста полем.
Вторая мысль - через политики:
public function update(User $user, Order $order,array $data): bool
{
if ($user->isAdmin()){
return true;
}
$fields = $user->convertPermissionToValue('order.edit');
$statuses = $user->convertPermissionToValue('order.view');
return in_array($order->status,$statuses)
&& array_reduce(array_keys($data),function ($accum, $item ) use ($fields){
return in_array($item,$fields) && $accum;
},true);
}
Сравнивается .value и fields реквеста, если какого то field нет в value - 403. Таким же образом проверяются любые действия над сущностями - permissions обрезаются до value, value сравнивается с полями ,со статусами, и вообще с чем угодно ,над чем нужно будет совершить action.
В чем собственно говоря вопрос: хоть один из этих вариантов - так вообще норм делать ? Сколько бы не гуглил, я не смог найти как именно организовать авторизацию действий над конкретными полями. Есть какие примеры реализации такого, как бы это вы сделали, где разумно было бы делать подобные проверки, хорошая ли вообще идея с convert разрешений, рабочая ли идея с такими составными permission. Буду ооочень благодарен за любую помощь или подсказку, ибо я новичок и даже те два примера выше выдались огромными нервозатратами)
142
05:47
18.09.2024
Ошибка Call to undefined function mysqli_connect()
Не могу подключить базу данных к сайту. При попытке подключить БД выдаётся такая ошибка:
Fatal error: Uncaught Error: Call to undefined function mysqli_connect() in D:\Проекты\Новая папка\index.php:11 Stack trace: #0 {main} thrown in D:\Проекты\Новая папка\index.php on line 11
У меня OC Windows 11
Вот мой код:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>Не работает php</p>
<?php
$connection=mysqli_connect('localhost', 'root', '', 'data');
$sql = "SELECT Name FROM tablical";
$massive=mysqli_query($connection, $sql);
$data=mysqli_fetch_assoc($massive);
foreach ($data as $key => $value) {
print_r($value);
}
?>
</body>
</html>
Не могу подключить базу данных к сайту. При попытке подключить БД выдаётся такая ошибка:
Fatal error: Uncaught Error: Call to undefined function mysqli_connect() in D:\Проекты\Новая папка\index.php:11 Stack trace: #0 {main} thrown in D:\Проекты\Новая папка\index.php on line 11
У меня OC Windows 11
Вот мой код:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>Не работает php</p>
<?php
$connection=mysqli_connect('localhost', 'root', '', 'data');
$sql = "SELECT Name FROM tablical";
$massive=mysqli_query($connection, $sql);
$data=mysqli_fetch_assoc($massive);
foreach ($data as $key => $value) {
print_r($value);
}
?>
</body>
</html>
459
09:17
16.09.2024
Подключение скриптов и стилей на уровень выше корневой директории
Пытаюсь реализовать собственную cms для своего сайта в качестве практики php, но столкнулся со следующей проблемой (Картинка)
Имеется иерархия проекта описанная выше. В папке core основные важные скрипты, по типу базового контроллера, подключения к БД и др, В папке develop находятся пользовательские контроллеры, представления, модели и шаблоны В папке public только .htaccess, index.php который вызывает bootstrap.php из core и иконка сайта
При нужном url я вызываю требуемый контроллер и подключаю:
шапку сайта
представление
подвал сайта
но мне очень нравится идея с шаблонами, и понравилась bitrix структура шаблонов, поэтому я хотел бы сделать это и у себя. Шаблоны, как я и писал выше, хранятся в /develop/templates. Корневая директория сайта /public/
Пробовал относительные пути по совету ChatGPT и людей с форумов, не помогает
(../../develop/...)
Пробовал реализовать перенаправление в htaccess
RewriteCond %{REQUEST_URI} ^/templates/admin/(.*)$
RewriteCond %{REQUEST_URI} !/develop/templates/(.*)$
RewriteRule ^(.*)$ http://%{HTTP_HOST}/../templates/$1 [L,R=301]
но пока что ничего не помогает, а хранить шаблоны в public мне не очень нравится...
Собственно, вопрос: Можно ли реализовать подключение стилей, скриптов, картинок через php и htaccess, не затрагивая конфиг файл httpd? Пока что видел только такое решение, но хотел бы его избежать, чтобы сайт был легк переносим.
P.S. Забыл написать, что также нашел решение
echo '<style>';
include <путь к стилю>
echo '</style>';
Насколько такое решение будет... Неправильным, при создании шаблонов?
Пытаюсь реализовать собственную cms для своего сайта в качестве практики php, но столкнулся со следующей проблемой (Картинка)
Имеется иерархия проекта описанная выше. В папке core основные важные скрипты, по типу базового контроллера, подключения к БД и др, В папке develop находятся пользовательские контроллеры, представления, модели и шаблоны В папке public только .htaccess, index.php который вызывает bootstrap.php из core и иконка сайта
При нужном url я вызываю требуемый контроллер и подключаю:
шапку сайта
представление
подвал сайта
но мне очень нравится идея с шаблонами, и понравилась bitrix структура шаблонов, поэтому я хотел бы сделать это и у себя. Шаблоны, как я и писал выше, хранятся в /develop/templates. Корневая директория сайта /public/
Пробовал относительные пути по совету ChatGPT и людей с форумов, не помогает
(../../develop/...)
Пробовал реализовать перенаправление в htaccess
RewriteCond %{REQUEST_URI} ^/templates/admin/(.*)$
RewriteCond %{REQUEST_URI} !/develop/templates/(.*)$
RewriteRule ^(.*)$ http://%{HTTP_HOST}/../templates/$1 [L,R=301]
но пока что ничего не помогает, а хранить шаблоны в public мне не очень нравится...
Собственно, вопрос: Можно ли реализовать подключение стилей, скриптов, картинок через php и htaccess, не затрагивая конфиг файл httpd? Пока что видел только такое решение, но хотел бы его избежать, чтобы сайт был легк переносим.
P.S. Забыл написать, что также нашел решение
echo '<style>';
include <путь к стилю>
echo '</style>';
Насколько такое решение будет... Неправильным, при создании шаблонов?
609
09:14
13.09.2024
$places_ids = []; // Храню артикулы всех товаров, по которым будет забирать отзывы
$arSelect = ["NAME","PROPERTY_ART_WB"];
$arFilter = ["IBLOCK_ID" => $PRODUCT_IBLOCK,"ACTIVE" => "Y",];
$resp = CIBlockElement::GetList(Array(), $arFilter, false, false, $arSelect);
while($ob = $resp->GetNextElement()) {
$arFields = $ob->GetFields(); // Получаю значения полей элемента
$places_ids[] = $arFields['PROPERTY_ART_WB_VALUE']; // Заполняю массив с артикулами
}
foreach ($places_ids as $arrwb) {
if (!empty($arrwb)) {
parseAndAdd($arrwb, 7);
}
}
?>
$arSelect = ["NAME","PROPERTY_ART_WB"];
$arFilter = ["IBLOCK_ID" => $PRODUCT_IBLOCK,"ACTIVE" => "Y",];
$resp = CIBlockElement::GetList(Array(), $arFilter, false, false, $arSelect);
while($ob = $resp->GetNextElement()) {
$arFields = $ob->GetFields(); // Получаю значения полей элемента
$places_ids[] = $arFields['PROPERTY_ART_WB_VALUE']; // Заполняю массив с артикулами
}
foreach ($places_ids as $arrwb) {
if (!empty($arrwb)) {
parseAndAdd($arrwb, 7);
}
}
?>
687
09:12
11.09.2024
Проверка на наличие существующего элемента в инфоблоке по свойству (Битрикс)
Друзья заранее прошу, отнестись к вопросу не очень строго, пишу скрипт первый раз.
Есть 2 инфоблока:
Инфоблок (ID=2) с товарами, там собираю массив с артикулами на WB;
Инфоблок (ID=7) туда добавляю сами отзывы (которые забираю по API).
Функция скрипта: По API получаю отзывы с WB по определенным товарам (по артикулу WB, которые указаны в инфоблоке с товарами) и добавляет элементы в инфоблок с отзывами.
Подскажите пожалуйста как сделать, чтобы при выполнении функции проверялось, на наличие этого отзыва (то есть при повторном запуске скрипта, те что уже есть не добавлялись), у каждого отзыва с WB есть уникальный ID (пример: kzgXM4MB65w5GC7j5vjK), вот по этому свойству элемента инфоблока с отзывами (ID 7) и проверять на существования отзыва.
Заранее всем благодарен за помощь!
<?php
// подключение функций пролога
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");
set_time_limit(60);
CModule::IncludeModule("main"); // CFile
CModule::IncludeModule("iblock"); // CIBlockElement
$PRODUCT_IBLOCK = '2';
$REVIEWS_IBLOCK = '7';
function getRequestResult($request)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$headers = array();
$headers[] = 'Content-Type: application/json';
$headers[] = 'Authorization: ........';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
}
curl_close($ch);
return $result;
}
function parseAndAdd($artnum, $PRODUCT_IBLOCK) {
$org_url = 'https://feedbacks-api.wildberries.ru/api/v1/feedbacks?isAnswered=true&nmId='. $artnum .'&take=5000&skip=0';
$html = getRequestResult($org_url);
$arr = json_decode($html, true);
$counter = 0;
foreach($arr['data']['feedbacks'] as $value){
//Свойства
$PROP = array();
$i = 0;
if (!empty($value['photoLinks'])) {
foreach ($value['photoLinks'] as $morePhoto) {
$arMorePhoto['n'.$i] = array("VALUE"=>CFile::MakeFileArray($morePhoto['fullSize']));
$i++;
}
}
$PROP['ART_WB'] = $value['productDetails']['nmId'];
$PROP['RAITING'] = $value['productValuation'];
$PROP['ISTOCHNIK'] = $value['state'];
$PROP['PHOTO'] = $arMorePhoto;
$PROP['REVIEWSID'] = $value['id'];
//$photolinks[] = $value['photoLinks'];
/*if (!empty($photolinks)) {
foreach($photolinks as $photo){
$photo = $photo['fullSize'];
}
}*/
$el = new CIBlockElement;
$fields = [
'ACTIVE' => "Y",
'IBLOCK_ID' => 7,
"IBLOCK_SECTION_ID" => false,
"NAME" => $value['userName'],
"PREVIEW_TEXT" => $value['text'],
"PREVIEW_PICTURE" => $imgUrl ? CFile::MakeFileArray($imgUrl) : CFile::MakeFileArray($_SERVER["DOCUMENT_ROOT"]."/local/api/wb/avatar.png"),
"PROPERTY_VALUES" => $PROP,
//'ACTIVE_FROM' => $date, // Начало активности
//'DATE_CREATE' => $date, // Дата создания
];
if ($PRODUCT_ID = $el->Add($fields)) {
echo 'Добавлен отзыв, ID: ' . $PRODUCT_ID . '<br />';
$counter++;
} else {
echo "Error[" . $PRODUCT_ID . "]: " . $el->LAST_ERROR . '<br />';
}
}
echo 'Отзывы по товару с артикулом '.$artnum . ' добавлены. Количество '. $counter. '<br>';
}
//print_r($photo);
//print_r(parseAndAdd(113509876, 6));
Друзья заранее прошу, отнестись к вопросу не очень строго, пишу скрипт первый раз.
Есть 2 инфоблока:
Инфоблок (ID=2) с товарами, там собираю массив с артикулами на WB;
Инфоблок (ID=7) туда добавляю сами отзывы (которые забираю по API).
Функция скрипта: По API получаю отзывы с WB по определенным товарам (по артикулу WB, которые указаны в инфоблоке с товарами) и добавляет элементы в инфоблок с отзывами.
Подскажите пожалуйста как сделать, чтобы при выполнении функции проверялось, на наличие этого отзыва (то есть при повторном запуске скрипта, те что уже есть не добавлялись), у каждого отзыва с WB есть уникальный ID (пример: kzgXM4MB65w5GC7j5vjK), вот по этому свойству элемента инфоблока с отзывами (ID 7) и проверять на существования отзыва.
Заранее всем благодарен за помощь!
<?php
// подключение функций пролога
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");
set_time_limit(60);
CModule::IncludeModule("main"); // CFile
CModule::IncludeModule("iblock"); // CIBlockElement
$PRODUCT_IBLOCK = '2';
$REVIEWS_IBLOCK = '7';
function getRequestResult($request)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $request);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$headers = array();
$headers[] = 'Content-Type: application/json';
$headers[] = 'Authorization: ........';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($ch);
if (curl_errno($ch)) {
echo 'Error:' . curl_error($ch);
}
curl_close($ch);
return $result;
}
function parseAndAdd($artnum, $PRODUCT_IBLOCK) {
$org_url = 'https://feedbacks-api.wildberries.ru/api/v1/feedbacks?isAnswered=true&nmId='. $artnum .'&take=5000&skip=0';
$html = getRequestResult($org_url);
$arr = json_decode($html, true);
$counter = 0;
foreach($arr['data']['feedbacks'] as $value){
//Свойства
$PROP = array();
$i = 0;
if (!empty($value['photoLinks'])) {
foreach ($value['photoLinks'] as $morePhoto) {
$arMorePhoto['n'.$i] = array("VALUE"=>CFile::MakeFileArray($morePhoto['fullSize']));
$i++;
}
}
$PROP['ART_WB'] = $value['productDetails']['nmId'];
$PROP['RAITING'] = $value['productValuation'];
$PROP['ISTOCHNIK'] = $value['state'];
$PROP['PHOTO'] = $arMorePhoto;
$PROP['REVIEWSID'] = $value['id'];
//$photolinks[] = $value['photoLinks'];
/*if (!empty($photolinks)) {
foreach($photolinks as $photo){
$photo = $photo['fullSize'];
}
}*/
$el = new CIBlockElement;
$fields = [
'ACTIVE' => "Y",
'IBLOCK_ID' => 7,
"IBLOCK_SECTION_ID" => false,
"NAME" => $value['userName'],
"PREVIEW_TEXT" => $value['text'],
"PREVIEW_PICTURE" => $imgUrl ? CFile::MakeFileArray($imgUrl) : CFile::MakeFileArray($_SERVER["DOCUMENT_ROOT"]."/local/api/wb/avatar.png"),
"PROPERTY_VALUES" => $PROP,
//'ACTIVE_FROM' => $date, // Начало активности
//'DATE_CREATE' => $date, // Дата создания
];
if ($PRODUCT_ID = $el->Add($fields)) {
echo 'Добавлен отзыв, ID: ' . $PRODUCT_ID . '<br />';
$counter++;
} else {
echo "Error[" . $PRODUCT_ID . "]: " . $el->LAST_ERROR . '<br />';
}
}
echo 'Отзывы по товару с артикулом '.$artnum . ' добавлены. Количество '. $counter. '<br>';
}
//print_r($photo);
//print_r(parseAndAdd(113509876, 6));
660
09:12
09.09.2024
Взаимодействие из JS с функцией в PHP через JSON запрос
Задача. Нужно без обновления страницы вывести данные из базы данных в модальное окно, которое открывается при нажатии на кнопку. Для этого в JS я вызываю функцию PHP и передаю несколько параметров через JSON, затем также через JSON возвращаю массив с данными из таблицы базы данных.
let params = {
userfunc: 'get_preview',
modul: 'home',
id: 1
};
let response = await fetch('./functions/functions.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(params),
});
if (response.ok) {
let result = await response.json();
console.log(result);
};
в PHP пишу следующий код
function get_preview($module, $id) {
global $db;
$result = $db -> prepare("SELECT * FROM $module WHERE
$result->execute(array($id));
$result = $result->fetch(PDO::FETCH_LAZY);
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_NUMERIC_CHECK);
}
header('Content-Type: application/json');
$param = json_decode($_POST['params']);
$userfunc = $param['userfunc'];
$module = $param['modul'];
$id = $param['id'];
if ($userfunc == 'get_preview') {
get_preview($module, $id);
}
Постоянно появляется ошибка JSON, помогите пожалуйста сделать код рабочим.
Задача. Нужно без обновления страницы вывести данные из базы данных в модальное окно, которое открывается при нажатии на кнопку. Для этого в JS я вызываю функцию PHP и передаю несколько параметров через JSON, затем также через JSON возвращаю массив с данными из таблицы базы данных.
let params = {
userfunc: 'get_preview',
modul: 'home',
id: 1
};
let response = await fetch('./functions/functions.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(params),
});
if (response.ok) {
let result = await response.json();
console.log(result);
};
в PHP пишу следующий код
function get_preview($module, $id) {
global $db;
$result = $db -> prepare("SELECT * FROM $module WHERE
status
!= '0'");$result->execute(array($id));
$result = $result->fetch(PDO::FETCH_LAZY);
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_NUMERIC_CHECK);
}
header('Content-Type: application/json');
$param = json_decode($_POST['params']);
$userfunc = $param['userfunc'];
$module = $param['modul'];
$id = $param['id'];
if ($userfunc == 'get_preview') {
get_preview($module, $id);
}
Постоянно появляется ошибка JSON, помогите пожалуйста сделать код рабочим.
762
09:11
06.09.2024
imageИзображение не доступно для предпросмотра
Как отображать имена внутри события fullcalendar?
Видел на других сайтах возможность в fullcalendar отображать имена пользователей, к примеру кто забронировал комнату. (прикрепил скрин) Подскажите пожалуйста как это реализовать?
Видел на других сайтах возможность в fullcalendar отображать имена пользователей, к примеру кто забронировал комнату. (прикрепил скрин) Подскажите пожалуйста как это реализовать?
812
09:10
04.09.2024
Bitrix и его файл init.php
сем привет! Подскажите пожалуйста проблема заключается в том что на локальном проекте под битрикс в папке php_interface создал файл init.php и просто хочу вывести текст - только вот выводит текст или что либо в самом конце сайта. Знаю то что все содержимое файла init должно подгружаться в начале сайта. Кто ни будь сталкивался с такой проблемой?
сем привет! Подскажите пожалуйста проблема заключается в том что на локальном проекте под битрикс в папке php_interface создал файл init.php и просто хочу вывести текст - только вот выводит текст или что либо в самом конце сайта. Знаю то что все содержимое файла init должно подгружаться в начале сайта. Кто ни будь сталкивался с такой проблемой?
944
09:09
02.09.2024
imageИзображение не доступно для предпросмотра
Хотите узнать, как концепция дилеммы заключенного применяется в разработке архитектурных решений?
Всего за пару часов вы разберете основы теории игр, научитесь анализировать преимущества и риски различных архитектурных подходов, сможете эффективно управлять компромиссами между компонентами системы.
Присоединяйтесь к открытому вебинару 3 сентября в 20:00 мск! Урок создан для архитекторов ПО, руководителей IT-проектов, Fullstack- и Backend-разработчиков.
Спикер Евгений Тюменцев — директор компании по разработке ПО, 20 лет преподает компьютерные дисциплины в ВУЗе: C++, Kotlin, C#, ООП.
Встречаемся в преддверии старта курса «Архитектура и шаблоны проектирования». Все участники вебинара получат специальную цену на обучение!
Регистрируйтесь прямо сейчас, чтобы не пропустить мероприятие: https://clck.ru/3Cy6gG
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
Всего за пару часов вы разберете основы теории игр, научитесь анализировать преимущества и риски различных архитектурных подходов, сможете эффективно управлять компромиссами между компонентами системы.
Присоединяйтесь к открытому вебинару 3 сентября в 20:00 мск! Урок создан для архитекторов ПО, руководителей IT-проектов, Fullstack- и Backend-разработчиков.
Спикер Евгений Тюменцев — директор компании по разработке ПО, 20 лет преподает компьютерные дисциплины в ВУЗе: C++, Kotlin, C#, ООП.
Встречаемся в преддверии старта курса «Архитектура и шаблоны проектирования». Все участники вебинара получат специальную цену на обучение!
Регистрируйтесь прямо сейчас, чтобы не пропустить мероприятие: https://clck.ru/3Cy6gG
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
915
07:50
02.09.2024
close
С этим каналом часто покупают
Лучшие в тематике
Новинки в тематике
Выбрано
0
каналов на сумму:0.00₽
Подписчики:
0
Просмотры:
lock_outline
Перейти в корзинуКупить за:0.00₽
Комментарий