feat: update admin page
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
<?php
|
||||
|
||||
use Bastion\ApplicationFactory;
|
||||
use Cart\User;
|
||||
use Openguru\OpenCartFramework\Http\Response as HttpResponse;
|
||||
use Openguru\OpenCartFramework\Logger\LoggerInterface;
|
||||
use Openguru\OpenCartFramework\Logger\OpenCartLogAdapter;
|
||||
use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
|
||||
$sysLibPath = rtrim(DIR_SYSTEM, '/') . '/library/oc_telegram_shop';
|
||||
$basePath = rtrim(DIR_APPLICATION, '/') . '/..';
|
||||
@@ -90,16 +94,11 @@ class ControllerExtensionModuleTgshop extends Controller
|
||||
|
||||
public function index(): void
|
||||
{
|
||||
$hasConfig = $this->config->get('module_tgshop_app_name') !== null;
|
||||
|
||||
if ($hasConfig) {
|
||||
$this->updateConfigFromDefaults();
|
||||
$this->cleanUpOldAssets();
|
||||
$this->injectVueJs();
|
||||
$this->config();
|
||||
} else {
|
||||
$this->init();
|
||||
}
|
||||
$this->cleanUpOldAssets();
|
||||
$this->migrateFromOldSettings();
|
||||
$this->removeLegacyFiles();
|
||||
$this->injectVueJs();
|
||||
$this->config();
|
||||
}
|
||||
|
||||
private function config(): void
|
||||
@@ -107,142 +106,78 @@ class ControllerExtensionModuleTgshop extends Controller
|
||||
$data = [];
|
||||
$this->document->setTitle($this->language->get('heading_title'));
|
||||
|
||||
if (($this->request->server['REQUEST_METHOD'] === 'POST') && $this->validate()) {
|
||||
$postData = $this->request->post;
|
||||
$postData['module_tgshop_mainpage_slider'] = [];
|
||||
if (! empty($_POST['module_tgshop_mainpage_slider'])) {
|
||||
$postData['module_tgshop_mainpage_slider'] = $_POST['module_tgshop_mainpage_slider'];
|
||||
}
|
||||
$this->model_setting_setting->editSetting('module_tgshop', $postData);
|
||||
|
||||
$this->session->data['success'] = $this->language->get('text_success');
|
||||
|
||||
$this->response->redirect(
|
||||
$this->url->link(
|
||||
'extension/module/tgshop',
|
||||
'user_token=' . $this->session->data['user_token'] . '&type=module',
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->baseData($data);
|
||||
|
||||
$data['order_statuses'] = $this->getOrderStatuses();
|
||||
$data['customer_groups'] = $this->getCustomerGroups();
|
||||
$data['themes'] = self::$themes;
|
||||
|
||||
$data['action'] = $this->url->link(
|
||||
'extension/module/tgshop',
|
||||
'user_token=' . $this->session->data['user_token'],
|
||||
true
|
||||
);
|
||||
|
||||
$data['settings'] = $this->getSettingsConfig();
|
||||
|
||||
$data['mainpage_slider'] = [];
|
||||
$banners = $this->config->get('module_tgshop_mainpage_slider');
|
||||
if ($banners) {
|
||||
$banners = html_entity_decode($banners);
|
||||
$data['mainpage_slider'] = $banners;
|
||||
}
|
||||
|
||||
foreach ($data['settings'] as $configs) {
|
||||
foreach ($configs as $key => $config) {
|
||||
if ($config['type'] === 'image') {
|
||||
if (isset($this->request->post[$key]) && is_file(DIR_IMAGE . $this->request->post[$key])) {
|
||||
$data[$key] = $this->model_tool_image->resize($this->request->post[$key], 100, 100);
|
||||
} elseif ($this->config->get($key) && is_file(DIR_IMAGE . $this->config->get($key))) {
|
||||
$data[$key] = $this->model_tool_image->resize($this->config->get($key), 100, 100);
|
||||
} else {
|
||||
$data[$key] = $this->model_tool_image->resize('no_image.png', 100, 100);
|
||||
}
|
||||
} elseif ($config['type'] === 'products') {
|
||||
$products = $this->request->post[$key] ?? $this->config->get($key) ?? [];
|
||||
|
||||
$data[$key] = [];
|
||||
foreach ($products as $productId) {
|
||||
$productItem = $this->model_catalog_product->getProduct($productId);
|
||||
$data[$key][] = [
|
||||
'product_id' => $productId,
|
||||
'name' => $productItem['name'],
|
||||
];
|
||||
}
|
||||
} elseif ($config['type'] === 'categories') {
|
||||
$categories = $this->request->post[$key] ?? $this->config->get($key) ?? [];
|
||||
|
||||
$data[$key] = [];
|
||||
foreach ($categories as $categoryId) {
|
||||
$categoryItem = $this->model_catalog_category->getCategory($categoryId);
|
||||
$data[$key][] = [
|
||||
'category_id' => $categoryId,
|
||||
'name' => $categoryItem['name'],
|
||||
];
|
||||
}
|
||||
} elseif (isset($this->request->post[$key])) {
|
||||
$data[$key] = $this->request->post[$key];
|
||||
} else {
|
||||
$data[$key] = $this->config->get($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->setOutput($this->load->view('extension/module/tgshop', $data));
|
||||
}
|
||||
|
||||
public function init(): void
|
||||
{
|
||||
$data = [];
|
||||
$this->baseData($data);
|
||||
|
||||
$data['action'] = $this->url->link(
|
||||
'extension/module/tgshop/init',
|
||||
'user_token=' . $this->session->data['user_token'],
|
||||
true
|
||||
);
|
||||
|
||||
if ($this->request->server['REQUEST_METHOD'] === 'POST') {
|
||||
$defaults = $this->getDefaultConfig();
|
||||
$this->model_setting_setting->editSetting('module_tgshop', $defaults);
|
||||
$this->session->data['success'] = 'Инициализация модуля выполнена успешно.';
|
||||
$this->response->redirect(
|
||||
$this->url->link(
|
||||
'extension/module/tgshop',
|
||||
'user_token=' . $this->session->data['user_token'],
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->response->setOutput($this->load->view('extension/module/tgshop_init', $data));
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$app = ApplicationFactory::create([
|
||||
'base_url' => HTTPS_SERVER,
|
||||
'public_url' => HTTPS_CATALOG,
|
||||
'telegram' => [
|
||||
'bot_token' => $this->config->get('module_tgshop_bot_token'),
|
||||
'chat_id' => $this->config->get('module_tgshop_chat_id'),
|
||||
'owner_notification_template' => $this->config->get('module_tgshop_owner_notification_template'),
|
||||
'customer_notification_template' => $this->config->get('module_tgshop_customer_notification_template'),
|
||||
],
|
||||
'db' => [
|
||||
'host' => DB_HOSTNAME,
|
||||
'database' => DB_DATABASE,
|
||||
'username' => DB_USERNAME,
|
||||
'password' => DB_PASSWORD,
|
||||
'prefix' => DB_PREFIX,
|
||||
'port' => DB_PORT,
|
||||
],
|
||||
'logs' => [
|
||||
'path' => DIR_LOGS,
|
||||
],
|
||||
]);
|
||||
try {
|
||||
$json = $this->model_setting_setting->getSetting('module_telecart');
|
||||
if (! isset($json['module_telecart_settings'])) {
|
||||
$json['module_telecart_settings'] = [];
|
||||
}
|
||||
|
||||
$items = Arr::mergeArraysRecursively($json['module_telecart_settings'], [
|
||||
'app' => [
|
||||
'shop_base_url' => HTTPS_CATALOG, // for catalog: HTTPS_SERVER, for admin: HTTPS_CATALOG
|
||||
'language_id' => (int) $this->config->get('config_language_id'),
|
||||
],
|
||||
'logs' => [
|
||||
'path' => DIR_LOGS,
|
||||
],
|
||||
'database' => [
|
||||
'host' => DB_HOSTNAME,
|
||||
'database' => DB_DATABASE,
|
||||
'username' => DB_USERNAME,
|
||||
'password' => DB_PASSWORD,
|
||||
'prefix' => DB_PREFIX,
|
||||
'port' => (int) DB_PORT,
|
||||
],
|
||||
'store' => [
|
||||
'oc_store_id' => 0,
|
||||
'oc_default_currency' => $this->config->get('config_currency'),
|
||||
'oc_config_tax' => filter_var($this->config->get('config_tax'), FILTER_VALIDATE_BOOLEAN),
|
||||
],
|
||||
'orders' => [
|
||||
'oc_customer_group_id' => (int) $this->config->get('config_customer_group_id'),
|
||||
],
|
||||
'telegram' => [
|
||||
'mini_app_url' => rtrim(HTTPS_CATALOG, '/') . '/image/catalog/tgshopspa/#/',
|
||||
],
|
||||
]);
|
||||
|
||||
$app->bind(OcRegistryDecorator::class, fn() => new OcRegistryDecorator($this->registry));
|
||||
$app = ApplicationFactory::create($items);
|
||||
$app->bind(OcRegistryDecorator::class, fn() => new OcRegistryDecorator($this->registry));
|
||||
|
||||
$app
|
||||
->withLogger(fn() => new OpenCartLogAdapter($this->log, 'TeleCartAdmin'))
|
||||
->bootAndHandleRequest();
|
||||
$app
|
||||
->withLogger(fn() => new OpenCartLogAdapter(
|
||||
$this->log,
|
||||
'TeleCartAdmin',
|
||||
$app->getConfigValue('app.app_debug')
|
||||
? LoggerInterface::LEVEL_DEBUG
|
||||
: LoggerInterface::LEVEL_WARNING,
|
||||
))
|
||||
->bootAndHandleRequest();
|
||||
} catch (Exception $e) {
|
||||
$this->log->write('[TELECART] Error: ' . $e->getMessage());
|
||||
http_response_code(HttpResponse::HTTP_INTERNAL_SERVER_ERROR);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode([
|
||||
'error' => 'Ошибка сервера. Приносим свои извинения за неудобства.',
|
||||
], JSON_THROW_ON_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
protected function validate(): bool
|
||||
@@ -251,16 +186,6 @@ class ControllerExtensionModuleTgshop extends Controller
|
||||
$this->error['telecart_error_warning'] = $this->language->get('error_permission');
|
||||
}
|
||||
|
||||
foreach ($this->getSettingsConfig() as $configs) {
|
||||
foreach ($configs as $key => $config) {
|
||||
if (($config['required'] ?? false) === true && ! $this->request->post[$key]) {
|
||||
$this->error["error_$key"] = 'Поле "' . $this->language->get(
|
||||
"lbl_$key"
|
||||
) . '" обязательно для заполнения.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ! $this->error;
|
||||
}
|
||||
|
||||
@@ -316,286 +241,6 @@ class ControllerExtensionModuleTgshop extends Controller
|
||||
$data['user_token'] = $this->session->data['user_token'];
|
||||
}
|
||||
|
||||
private function getDefaultConfig(): array
|
||||
{
|
||||
return [
|
||||
'module_tgshop_status' => 1,
|
||||
'module_tgshop_debug' => 0,
|
||||
'module_tgshop_app_name' => $this->config->get('config_meta_title'),
|
||||
'module_tgshop_app_icon' => $this->config->get('config_image') ?: $this->model_tool_image->resize(
|
||||
'no_image.png',
|
||||
100,
|
||||
100
|
||||
),
|
||||
'module_tgshop_owner_notification_template' => <<<TEXT
|
||||
*Новый заказ \#{order_id}* в магазине *{store_name}*
|
||||
|
||||
*Покупатель:* {customer}
|
||||
*Email:* {email}
|
||||
*Телефон:* {phone}
|
||||
*IP:* {ip}
|
||||
|
||||
*Адрес доставки:*
|
||||
{address}
|
||||
|
||||
*Комментарий:*
|
||||
{comment}
|
||||
|
||||
*Сумма заказа:* {total}
|
||||
*Дата оформления:* {created_at}
|
||||
TEXT,
|
||||
'module_tgshop_customer_notification_template' => <<<TEXT
|
||||
Спасибо за Ваш заказ в магазине *{store_name}*
|
||||
|
||||
*Номер заказа* \#{order_id}
|
||||
*Сумма заказа:* {total}
|
||||
*Дата оформления:* {created_at}
|
||||
|
||||
Мы свяжемся с вами при необходимости\.
|
||||
Хорошего дня\!
|
||||
TEXT,
|
||||
'module_tgshop_theme_light' => 'light',
|
||||
'module_tgshop_theme_dark' => 'dark',
|
||||
'module_tgshop_mainpage_products' => 'most_viewed',
|
||||
'module_tgshop_order_customer_group_id' => 1,
|
||||
'module_tgshop_order_default_status_id' => 1,
|
||||
'module_tgshop_mini_app_url' => rtrim(HTTPS_CATALOG, '/') . '/image/catalog/tgshopspa/#/',
|
||||
'module_tgshop_mainpage_categories' => 'latest10',
|
||||
'module_tgshop_enable_store' => 1,
|
||||
'module_tgshop_feature_coupons' => 0,
|
||||
'module_tgshop_feature_vouchers' => 0,
|
||||
'module_tgshop_text_no_more_products' => 'Это всё по текущему запросу. Попробуйте уточнить фильтры или поиск.',
|
||||
'module_tgshop_text_empty_cart' => 'Ваша корзина пуста',
|
||||
'module_tgshop_text_order_created_success' => 'Ваш заказ успешно оформлен и будет обработан в ближайшее время.',
|
||||
'module_tgshop_mainpage_slider' => json_encode([
|
||||
'is_enabled' => false,
|
||||
'effect' => 'slide',
|
||||
'pagination' => true,
|
||||
'scrollbar' => false,
|
||||
'free_mode' => false,
|
||||
'space_between' => 30,
|
||||
'autoplay' => false,
|
||||
'loop' => false,
|
||||
'slides' => [],
|
||||
], JSON_THROW_ON_ERROR),
|
||||
'module_tgshop_yandex_metrika' => '',
|
||||
'module_tgshop_chat_id' => '',
|
||||
'module_tgshop_bot_token' => '',
|
||||
];
|
||||
}
|
||||
|
||||
private function getSettingsConfig(): array
|
||||
{
|
||||
$ocCouponsLink = $this->url->link(
|
||||
'marketing/coupon',
|
||||
'user_token=' . $this->session->data['user_token'],
|
||||
true
|
||||
);
|
||||
$ocVouchersLink = $this->url->link(
|
||||
'sale/voucher',
|
||||
'user_token=' . $this->session->data['user_token'],
|
||||
true
|
||||
);
|
||||
|
||||
return [
|
||||
'general' => [
|
||||
'module_tgshop_status' => [
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
0 => 'Выключено',
|
||||
1 => 'Включено',
|
||||
],
|
||||
'help' => 'Если выключено, покупатели в Telegram увидят сообщение, что магазин временно закрыт. Заказы и просмотр товаров будут недоступны.',
|
||||
],
|
||||
|
||||
'module_tgshop_app_name' => [
|
||||
'type' => 'text',
|
||||
'placeholder' => 'Без названия',
|
||||
'help' => <<<TEXT
|
||||
Отображается в заголовке Telegram Mini App при запуске, а также используется как подпись
|
||||
под иконкой, если пользователь добавит приложение на главный экран своего устройства.
|
||||
Рекомендуется короткое и понятное название (до 20 символов).
|
||||
Если оставить пустым, то название выводиться не будет.
|
||||
TEXT,
|
||||
],
|
||||
|
||||
'module_tgshop_app_icon' => [
|
||||
'type' => 'image',
|
||||
'help' => <<<TEXT
|
||||
Изображение, которое будет отображаться в Telegram Mini App и на рабочем столе устройства,
|
||||
если пользователь добавит приложение как ярлык. Рекомендуется использовать квадратное изображение PNG или SVG,
|
||||
размером 32×32 пикселей.
|
||||
TEXT,
|
||||
],
|
||||
|
||||
'module_tgshop_theme_light' => [
|
||||
'type' => 'select',
|
||||
'options' => static::$themes,
|
||||
'help' => 'Выберите стиль, который будет использоваться при отображении вашего магазина в Telegram для дневного режима. <a href="https://daisyui.com/docs/themes/#list-of-themes" target="_blank">Посмотреть как выглядят темы</a>',
|
||||
],
|
||||
|
||||
'module_tgshop_theme_dark' => [
|
||||
'type' => 'select',
|
||||
'options' => static::$themes,
|
||||
'help' => 'Выберите стиль, который будет использоваться при отображении вашего магазина в Telegram для ночного режима. <a href="https://daisyui.com/docs/themes/#list-of-themes" target="_blank">Посмотреть как выглядят темы</a>',
|
||||
],
|
||||
|
||||
'module_tgshop_debug' => [
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
0 => 'Выключено',
|
||||
1 => 'Включено',
|
||||
],
|
||||
'help' => 'Режим разработчика. Рекомендуется включать только по необходимости. В остальных случаях, для нормальной работы магазина, должен быть выключен.',
|
||||
],
|
||||
],
|
||||
'telegram' => [
|
||||
'module_tgshop_mini_app_url' => [
|
||||
'type' => 'text_readonly',
|
||||
'help' => <<<HTML
|
||||
Это прямая ссылка на ваш Telegram Mini App. Скопируйте её в точности, как указано — и добавьте в настройки Telegram-бота.
|
||||
<p class="text-warning">⚠️ Важно: ссылка обязательно должна заканчиваться на /#/ — иначе приложение не загрузится.</p>
|
||||
HTML,
|
||||
],
|
||||
|
||||
'module_tgshop_bot_token' => [
|
||||
'type' => 'bot_token',
|
||||
'placeholder' => 'Введите токен от телеграм бота',
|
||||
'help' => <<<TEXT
|
||||
Токен, полученный при создании бота через @BotFather.
|
||||
Он используется для взаимодействия модуля с Telegram API.
|
||||
TEXT,
|
||||
],
|
||||
'module_tgshop_chat_id' => [
|
||||
'type' => 'chatid',
|
||||
'placeholder' => 'Введите Chat ID',
|
||||
'help' => <<<TEXT
|
||||
Идентификатор Telegram-чата, куда будут отправляться уведомления о новых заказах.
|
||||
Если оставить поле пустым, уведомления отправляться не будут.
|
||||
TEXT,
|
||||
],
|
||||
'module_tgshop_owner_notification_template' => [
|
||||
'type' => 'tg_message_template',
|
||||
'placeholder' => 'Введите текст уведомления',
|
||||
'rows' => 15,
|
||||
'help' => 'Введите шаблон сообщения для Telegram-уведомлений о новом заказе владельцу магазина.',
|
||||
],
|
||||
'module_tgshop_customer_notification_template' => [
|
||||
'type' => 'tg_message_template',
|
||||
'placeholder' => 'Введите текст уведомления',
|
||||
'rows' => 15,
|
||||
'help' => 'Введите шаблон сообщения для Telegram-уведомлений о новом заказе покупателю.',
|
||||
],
|
||||
],
|
||||
'statistics' => [
|
||||
'module_tgshop_yandex_metrika' => [
|
||||
'type' => 'textarea',
|
||||
'placeholder' => 'Вставьте код счётчика Яндекс Метрики.',
|
||||
'rows' => 15,
|
||||
'help' => 'Для проверки интеграции через кнопку "Проверить" в интерфейсе Яндекс Метрики, необходимо сначала включить "Режим разработчика" на вкладке "Общие".'
|
||||
],
|
||||
],
|
||||
'shop' => [
|
||||
'module_tgshop_enable_store' => [
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
0 => 'Выключено',
|
||||
1 => 'Включено',
|
||||
],
|
||||
'help' => <<<HTML
|
||||
Если опция <strong>включена</strong> — пользователи смогут оформлять заказы прямо в Telegram-магазине. <br>
|
||||
Если <strong>выключена</strong> — оформление заказов будет недоступно. Вместо кнопки «Добавить в корзину» пользователи увидят кнопку «Перейти к товару», которая откроет страницу товара на вашем сайте. В этом режиме Telecart работает как каталог.
|
||||
HTML,
|
||||
],
|
||||
'module_tgshop_mainpage_products' => [
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'most_viewed' => 'Популярные товары',
|
||||
'latest' => 'Последние сверху',
|
||||
'featured' => 'Избранные товары (задать в поле ниже)',
|
||||
],
|
||||
'help' => 'Выберите, какие товары показывать на главной странице магазина в Telegram. Это влияет на первую видимую секцию каталога для пользователя.',
|
||||
],
|
||||
|
||||
'module_tgshop_featured_products' => [
|
||||
'type' => 'products',
|
||||
'help' => 'На главной странице будут отображаться избранные товары, если вы выберете этот вариант в настройке “Товары на главной”. Если товары не выбраны, то будут показаны популярные товары.',
|
||||
],
|
||||
|
||||
'module_tgshop_mainpage_categories' => [
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
'no_categories' => 'Отображать только кнопку "Каталог"',
|
||||
'latest10' => 'Последние 10 категорий',
|
||||
'featured' => 'Избранные категории (задать в поле ниже)',
|
||||
],
|
||||
'help' => 'Выберите, какие категории показывать на главной странице магазина в Telegram. Это влияет на первую видимую секцию каталога для пользователя.',
|
||||
],
|
||||
|
||||
'module_tgshop_featured_categories' => [
|
||||
'type' => 'categories',
|
||||
'help' => 'На главной странице будут отображаться эти категории, если вы выберете этот вариант в настройке “Категории на главной”.',
|
||||
],
|
||||
|
||||
'module_tgshop_feature_coupons' => [
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
0 => 'Выключено',
|
||||
1 => 'Включено',
|
||||
],
|
||||
'help' => <<<HTML
|
||||
Позволяет использовать стандартные <a href="{$ocCouponsLink}" target="_blank">купоны OpenCart</a> для предоставления скидок при оформлении заказа.
|
||||
HTML,
|
||||
],
|
||||
|
||||
'module_tgshop_feature_vouchers' => [
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
0 => 'Выключено',
|
||||
1 => 'Включено',
|
||||
],
|
||||
'help' => <<<HTML
|
||||
Позволяет покупателям использовать <a href="{$ocVouchersLink}" target="_blank">подарочные сертификаты OpenCart</a> при оформлении заказа.
|
||||
HTML,
|
||||
],
|
||||
],
|
||||
'orders' => [
|
||||
'module_tgshop_order_default_status_id' => [
|
||||
'type' => 'select',
|
||||
'options' => $this->getOrderStatuses(),
|
||||
'help' => 'Статус, с которым будут создаваться заказы через Telegram по умолчанию.',
|
||||
],
|
||||
|
||||
'module_tgshop_order_customer_group_id' => [
|
||||
'hidden' => true,
|
||||
'type' => 'select',
|
||||
'options' => $this->getCustomerGroups(),
|
||||
'help' => 'Группа покупателей, которая будет назначена для заказов, оформленных через Telegram-магазин.',
|
||||
],
|
||||
],
|
||||
|
||||
'texts' => [
|
||||
'module_tgshop_text_no_more_products' => [
|
||||
'type' => 'text',
|
||||
'placeholder' => 'Это всё по текущему запросу. Попробуйте уточнить фильтры или поиск.',
|
||||
'help' => 'Текст, отображаемый в конце списка, когда больше нет доступных товаров. Покупатель дошел до конца списка.',
|
||||
],
|
||||
|
||||
'module_tgshop_text_empty_cart' => [
|
||||
'type' => 'text',
|
||||
'placeholder' => 'Ваша корзина пуста',
|
||||
'help' => 'Текст, отображаемый на странице просмотра корзины, если в ней нет товаров.',
|
||||
],
|
||||
|
||||
'module_tgshop_text_order_created_success' => [
|
||||
'type' => 'text',
|
||||
'placeholder' => 'Ваш заказ успешно оформлен и будет обработан в ближайшее время.',
|
||||
'help' => 'Текст, отображаемый при успешном создании заказа.',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function getCustomerGroups(): array
|
||||
{
|
||||
$map = [];
|
||||
@@ -607,7 +252,7 @@ HTML,
|
||||
return $map;
|
||||
}
|
||||
|
||||
private function getOrderStatuses()
|
||||
private function getOrderStatuses(): array
|
||||
{
|
||||
$statuses = $this->model_localisation_order_status->getOrderStatuses();
|
||||
$map = [];
|
||||
@@ -619,45 +264,6 @@ HTML,
|
||||
return $map;
|
||||
}
|
||||
|
||||
private function updateConfigFromDefaults(): void
|
||||
{
|
||||
$defaults = $this->getDefaultConfig();
|
||||
$settings = $this->model_setting_setting->getSetting('module_tgshop');
|
||||
|
||||
$diff = [];
|
||||
foreach ($defaults as $key => $value) {
|
||||
if (! array_key_exists($key, $settings)) {
|
||||
$diff[$key] = $defaults[$key];
|
||||
}
|
||||
}
|
||||
|
||||
if ($diff) {
|
||||
$settings = array_merge($settings, $diff);
|
||||
$this->model_setting_setting->editSetting('module_tgshop', $settings);
|
||||
$this->log->write('[TELECART] Выполнено обновление настроек по умолчанию для модуля.');
|
||||
$this->session->data['success'] = 'Выполнено обновление настроек по умолчанию для модуля.';
|
||||
|
||||
foreach ($diff as $key => $value) {
|
||||
$this->config->set($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$diffToDelete = [];
|
||||
foreach ($settings as $key => $value) {
|
||||
if (! array_key_exists($key, $defaults)) {
|
||||
$diffToDelete[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if ($diffToDelete) {
|
||||
$keys = implode(', ', array_map(function ($key) {
|
||||
return "'{$key}'";
|
||||
}, $diffToDelete));
|
||||
$this->db->query("DELETE FROM " . DB_PREFIX . "setting WHERE `key` IN ($keys)");
|
||||
$this->log->write('[TELECART] Удалены старые конфиги: ' . $keys);
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanUpOldAssets(): void
|
||||
{
|
||||
$spaPath = rtrim(DIR_IMAGE, '/') . '/catalog/tgshopspa';
|
||||
@@ -733,4 +339,95 @@ HTML,
|
||||
throw new RuntimeException('Unable to load Vuejs frontend.');
|
||||
}
|
||||
}
|
||||
|
||||
private function migrateFromOldSettings(): void
|
||||
{
|
||||
$legacySettings = $this->model_setting_setting->getSetting('module_tgshop');
|
||||
if (! $legacySettings) {
|
||||
return;
|
||||
}
|
||||
|
||||
$newSettings = $this->model_setting_setting->getSetting('module_telecart');
|
||||
|
||||
static $mapLegacyToNewSettings = [
|
||||
'module_tgshop_app_icon' => 'app.app_icon',
|
||||
'module_tgshop_theme_light' => 'app.theme_light',
|
||||
'module_tgshop_bot_token' => 'telegram.bot_token',
|
||||
'module_tgshop_status' => 'app.app_enabled',
|
||||
'module_tgshop_app_name' => 'app.app_name',
|
||||
'module_tgshop_theme_dark' => 'app.theme_dark',
|
||||
'module_tgshop_debug' => 'app.app_debug',
|
||||
'module_tgshop_chat_id' => 'telegram.chat_id',
|
||||
'module_tgshop_owner_notification_template' => 'telegram.owner_notification_template',
|
||||
'module_tgshop_text_order_created_success' => 'texts.text_order_created_success',
|
||||
'module_tgshop_enable_store' => 'store.enable_store',
|
||||
'module_tgshop_mainpage_products' => 'store.mainpage_products',
|
||||
'module_tgshop_yandex_metrika' => 'metrics.yandex_metrika_counter',
|
||||
'module_tgshop_customer_notification_template' => 'telegram.customer_notification_template',
|
||||
'module_tgshop_feature_vouchers' => 'store.feature_vouchers',
|
||||
'module_tgshop_order_default_status_id' => 'orders.order_default_status_id',
|
||||
'module_tgshop_feature_coupons' => 'store.feature_coupons',
|
||||
'module_tgshop_mainpage_categories' => 'store.mainpage_categories',
|
||||
'module_tgshop_text_no_more_products' => 'texts.text_no_more_products',
|
||||
'module_tgshop_text_empty_cart' => 'texts.text_empty_cart',
|
||||
];
|
||||
|
||||
if (! $newSettings) {
|
||||
$data = [];
|
||||
Arr::set($data, 'app.app_icon', $legacySettings['module_tgshop_app_icon']);
|
||||
|
||||
foreach ($mapLegacyToNewSettings as $key => $value) {
|
||||
if (array_key_exists($key, $legacySettings)) {
|
||||
if ($key === 'module_tgshop_status') {
|
||||
$newValue = filter_var($legacySettings[$key], FILTER_VALIDATE_BOOLEAN);
|
||||
} elseif ($key === 'module_tgshop_debug') {
|
||||
$newValue = filter_var($legacySettings[$key], FILTER_VALIDATE_BOOLEAN);
|
||||
} elseif ($key === 'module_tgshop_chat_id') {
|
||||
$newValue = (int) $legacySettings[$key];
|
||||
} elseif ($key === 'module_tgshop_enable_store') {
|
||||
$newValue = filter_var($legacySettings[$key], FILTER_VALIDATE_BOOLEAN);
|
||||
} elseif ($key === 'module_tgshop_order_default_status_id') {
|
||||
$newValue = (int) $legacySettings[$key];
|
||||
} elseif ($key === 'module_tgshop_feature_vouchers') {
|
||||
$newValue = filter_var($legacySettings[$key], FILTER_VALIDATE_BOOLEAN);
|
||||
} elseif ($key === 'module_tgshop_feature_coupons') {
|
||||
$newValue = filter_var($legacySettings[$key], FILTER_VALIDATE_BOOLEAN);
|
||||
} else {
|
||||
$newValue = $legacySettings[$key];
|
||||
}
|
||||
|
||||
Arr::set($data, $value, $newValue);
|
||||
}
|
||||
}
|
||||
|
||||
Arr::set(
|
||||
$data,
|
||||
'metrics.yandex_metrika_enabled',
|
||||
! empty(trim($legacySettings['module_tgshop_yandex_metrika']))
|
||||
);
|
||||
|
||||
$this->model_setting_setting->editSetting('module_telecart', [
|
||||
'module_telecart_settings' => $data,
|
||||
]);
|
||||
|
||||
$this->log->write('[TELECART] Выполнено обновление настроек с 1й версии модуля.');
|
||||
$this->session->data['success'] = 'Выполнено обновление настроек с прошлой версии модуля.';
|
||||
}
|
||||
|
||||
$this->model_setting_setting->deleteSetting('module_tgshop');
|
||||
}
|
||||
|
||||
private function removeLegacyFiles(): void
|
||||
{
|
||||
$legacyFilesToRemove = [
|
||||
DIR_TEMPLATE . '/extension/module/tgshop_init.twig',
|
||||
];
|
||||
|
||||
foreach ($legacyFilesToRemove as $file) {
|
||||
if (file_exists($file)) {
|
||||
unlink($file);
|
||||
$this->log->write('[TELECART] Удалён старый файл: ' . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,6 @@
|
||||
<div id="content">
|
||||
<div class="page-header">
|
||||
<div class="container-fluid">
|
||||
<div class="pull-right">
|
||||
<button type="submit" form="form-module" data-toggle="tooltip" title="{{ button_save }}"
|
||||
class="btn btn-primary"><i class="fa fa-save"></i></button>
|
||||
<a href="{{ cancel }}" data-toggle="tooltip" title="{{ button_cancel }}" class="btn btn-default"><i
|
||||
class="fa fa-reply"></i></a></div>
|
||||
<h1>{{ heading_title }}</h1>
|
||||
<ul class="breadcrumb">
|
||||
{% for breadcrumb in breadcrumbs %}
|
||||
@@ -27,438 +22,18 @@
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-pencil"></i> {{ text_edit }}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form action="{{ action }}" method="post" enctype="multipart/form-data" id="form-module"
|
||||
class="form-horizontal">
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
{% for tabKey, tabItems in settings %}
|
||||
<li{% if tabKey == 'general' %} class="active" {% endif %}>
|
||||
<a href="#{{ tabKey }}" data-toggle="tab">
|
||||
{% if attribute(_context, 'tab_' ~ tabKey) %}
|
||||
{{ attribute(_context, 'tab_' ~ tabKey) }}
|
||||
{% else %}
|
||||
{{ 'tab_' ~ tabKey }}
|
||||
{% endif %}
|
||||
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
<li>
|
||||
<a href="#banners" data-toggle="tab">
|
||||
Баннеры
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
{% for tabKey, tabItems in settings %}
|
||||
<div class="tab-pane{%if tabKey == 'general' %} active{% endif %}" id="{{ tabKey }}">
|
||||
{% for settingKey, item in tabItems %}
|
||||
<div class="form-group{%if item['required'] %} required{% endif %}{% if item['hidden'] %} hidden{% endif %}">
|
||||
<label class="col-sm-2 control-label" for="{{ settingKey }}">
|
||||
{% if attribute(_context, 'lbl_' ~ settingKey) %}
|
||||
{{ attribute(_context, 'lbl_' ~ settingKey) }}
|
||||
{% else %}
|
||||
{{ 'lbl_' ~ settingKey }}
|
||||
{% endif %}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
{# Select #}
|
||||
{% if item['type'] == 'select' %}
|
||||
<select name="{{ settingKey }}" id="{{ settingKey }}" class="form-control">
|
||||
{% for key, value in item['options'] %}
|
||||
<option value="{{ key }}" {% if key == attribute(_context, settingKey) %}selected="selected"{% endif %}>
|
||||
{{ value }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
{# Text Input #}
|
||||
{% elseif item['type'] == 'text' %}
|
||||
<input type="text"
|
||||
name="{{ settingKey }}"
|
||||
value="{{ attribute(_context, settingKey) }}"
|
||||
placeholder="{{ item['placeholder'] }}"
|
||||
id="{{ settingKey }}"
|
||||
class="form-control"
|
||||
/>
|
||||
|
||||
{# Image #}
|
||||
{% elseif item['type'] == 'image' %}
|
||||
<a href="" id="thumb-image-{{ settingKey }}" data-toggle="image" class="img-thumbnail">
|
||||
<img src="{{ attribute(_context, settingKey) }}"
|
||||
data-placeholder="https://placehold.co/100x100?text=Удалено"
|
||||
/>
|
||||
</a>
|
||||
<input type="hidden"
|
||||
name="{{ settingKey }}"
|
||||
value="{{ attribute(_context, settingKey) }}"
|
||||
id="{{ settingKey }}"
|
||||
/>
|
||||
{# Textarea #}
|
||||
{% elseif item['type'] == 'textarea' %}
|
||||
<textarea name="{{ settingKey }}"
|
||||
rows="{{ item['rows'] }}"
|
||||
placeholder="{{ item['placeholder'] }}"
|
||||
id="{{ settingKey }}"
|
||||
class="form-control"
|
||||
>{{ attribute(_context, settingKey) }}</textarea>
|
||||
{# Products #}
|
||||
{% elseif item['type'] == 'products' %}
|
||||
<input type="text" value="" placeholder="Начните вводить название товара..." id="{{ settingKey }}-input" class="form-control"/>
|
||||
<div id="{{ settingKey }}-list" class="well well-sm" style="height: 150px; overflow: auto;">
|
||||
{% for product in attribute(_context, settingKey) %}
|
||||
<div id="{{ settingKey }}-{{ product.product_id }}">
|
||||
<i class="fa fa-minus-circle"></i> {{ product.name }}
|
||||
<input type="hidden" name="{{ settingKey }}[]" value="{{ product.product_id }}"/>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<script>
|
||||
$('#{{ settingKey }}-input').autocomplete({
|
||||
'source': function(request, response) {
|
||||
$.ajax({
|
||||
url: 'index.php?route=catalog/product/autocomplete&user_token={{ user_token }}&filter_name=' + encodeURIComponent(request),
|
||||
dataType: 'json',
|
||||
success: function(json) {
|
||||
response($.map(json, function(item) {
|
||||
return {
|
||||
label: item['name'],
|
||||
value: item['product_id']
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
},
|
||||
'select': function(item) {
|
||||
$('#{{ settingKey }}').val('');
|
||||
$('#{{ settingKey }}-' + item['value']).remove();
|
||||
$('#{{ settingKey }}-list').append('<div id="{{ settingKey }}-' + item['value'] + '"><i class="fa fa-minus-circle"></i> ' + item['label'] + '<input type="hidden" name="{{ settingKey }}[]" value="' + item['value'] + '" /></div>');
|
||||
}
|
||||
});
|
||||
|
||||
$('#{{ settingKey }}-list').delegate('.fa-minus-circle', 'click', function() {
|
||||
$(this).parent().remove();
|
||||
});
|
||||
</script>
|
||||
|
||||
{% elseif item['type'] == 'categories' %}
|
||||
<input type="text" value="" placeholder="Начните вводить название категории..." id="{{ settingKey }}-input" class="form-control"/>
|
||||
<div id="{{ settingKey }}-list" class="well well-sm" style="height: 150px; overflow: auto;">
|
||||
{% for category in attribute(_context, settingKey) %}
|
||||
<div id="{{ settingKey }}-{{ category.category_id }}">
|
||||
<i class="fa fa-minus-circle"></i> {{ category.name }}
|
||||
<input type="hidden" name="{{ settingKey }}[]" value="{{ category.category_id }}"/>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<script>
|
||||
$('#{{ settingKey }}-input').autocomplete({
|
||||
'source': function(request, response) {
|
||||
$.ajax({
|
||||
url: 'index.php?route=catalog/category/autocomplete&user_token={{ user_token }}&filter_name=' + encodeURIComponent(request),
|
||||
dataType: 'json',
|
||||
success: function(json) {
|
||||
response($.map(json, function(item) {
|
||||
return {
|
||||
label: item['name'],
|
||||
value: item['category_id']
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
},
|
||||
'select': function(item) {
|
||||
$('#{{ settingKey }}').val('');
|
||||
$('#{{ settingKey }}-' + item['value']).remove();
|
||||
$('#{{ settingKey }}-list').append('<div id="{{ settingKey }}-' + item['value'] + '"><i class="fa fa-minus-circle"></i> ' + item['label'] + '<input type="hidden" name="{{ settingKey }}[]" value="' + item['value'] + '" /></div>');
|
||||
}
|
||||
});
|
||||
|
||||
$('#{{ settingKey }}-list').delegate('.fa-minus-circle', 'click', function() {
|
||||
$(this).parent().remove();
|
||||
});
|
||||
</script>
|
||||
{# ChatID #}
|
||||
{% elseif item['type'] == 'chatid' %}
|
||||
{% if module_tgshop_bot_token %}
|
||||
<div class="input-group">
|
||||
<span class="input-group-btn">
|
||||
<button id="{{ settingKey }}-btn" class="btn btn-primary" type="button">
|
||||
<i class="fa fa-refresh"></i> Получить Chat ID
|
||||
</button>
|
||||
</span>
|
||||
<input type="text"
|
||||
name="{{ settingKey }}"
|
||||
value="{{ attribute(_context, settingKey) }}"
|
||||
placeholder="{{ item['placeholder'] }}"
|
||||
id="{{ settingKey }}"
|
||||
class="form-control"
|
||||
/>
|
||||
<script>
|
||||
$('#{{ settingKey }}-btn').click(function () {
|
||||
const $resultLabel = $('#{{ settingKey }}-result-label');
|
||||
const telegramToken = $('#module_tgshop_bot_token').val().trim(); // fetch from input
|
||||
if (! telegramToken) {
|
||||
alert('Сначала введите Telegram Bot Token!');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/admin/index.php?route=extension/module/tgshop/handle&api_action=getChatId&user_token={{ user_token }}')
|
||||
.then(async (res) => {
|
||||
const data = await res.json().catch(() => null);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Ошибка ${res.status}: ${data.message || res.statusText}`);
|
||||
}
|
||||
|
||||
$('#{{ settingKey }}').val(data.data.chat_id);
|
||||
$resultLabel
|
||||
.text('✅ ChatID успешно получен и подставлен в поле. Не забудьте сохранить настройки!')
|
||||
.css('color', 'green');
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
alert(err);
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</div>
|
||||
|
||||
<div id="{{ settingKey }}-result-label"></div>
|
||||
|
||||
<button class="btn btn-link btn-xs" type="button" data-toggle="collapse" data-target="#{{ settingKey }}-collapse" aria-expanded="false" aria-controls="collapseExample">
|
||||
Инструкция как получить ChatID.
|
||||
</button>
|
||||
<div class="collapse" id="{{ settingKey }}-collapse">
|
||||
<div class="well">
|
||||
<p class="text-primary">Как получить Chat ID</p>
|
||||
<ol>
|
||||
<li>Убедитесь, что Telegram Bot Token введён выше.</li>
|
||||
<li>Откройте вашего бота в Telegram и отправьте ему кодовое слово: `opencart_get_chatid`. Важно отправить именно такое сообщение, иначе не сработает.</li>
|
||||
<li>Вернитесь сюда и нажмите кнопку «Получить Chat ID» — скрипт автоматически подставит его в поле ниже.</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<strong>BotToken</strong> не указан. Пожалуйста, введите корректный BotToken и сохраните настройки. После этого здесь станет доступна настройка ChatID.
|
||||
</div>
|
||||
{% endif %}
|
||||
{% elseif item['type'] == 'tg_message_template' %}
|
||||
<div style="margin-bottom: 10px;">
|
||||
<textarea name="{{ settingKey }}"
|
||||
rows="{{ item['rows'] }}"
|
||||
placeholder="{{ item['placeholder'] }}"
|
||||
id="{{ settingKey }}"
|
||||
class="form-control"
|
||||
>{{ attribute(_context, settingKey) }}</textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button class="btn btn-link" type="button" data-toggle="collapse" data-target="#{{ settingKey }}-collapse">
|
||||
Документация
|
||||
</button>
|
||||
<button id="{{ settingKey }}-btn-test" type="button" class="btn btn-primary btn-sm">
|
||||
<i class="fa fa-envelope"></i>
|
||||
Отправить тестовое уведомление
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="collapse" id="{{ settingKey }}-collapse" style="margin-top: 15px">
|
||||
<div class="well">
|
||||
<p>Вы можете использовать переменные:</p>
|
||||
<ul>
|
||||
<li><code>{store_name}</code> — название магазина</li>
|
||||
<li><code>{order_id}</code> — номер заказа</li>
|
||||
<li><code>{customer}</code> — имя и фамилия покупателя</li>
|
||||
<li><code>{email}</code> — email покупателя</li>
|
||||
<li><code>{phone}</code> — телефон</li>
|
||||
<li><code>{comment}</code> — комментарий к заказу</li>
|
||||
<li><code>{address}</code> — адрес доставки</li>
|
||||
<li><code>{total}</code> — сумма заказа</li>
|
||||
<li><code>{ip}</code> — IP покупателя</li>
|
||||
<li><code>{created_at}</code> — дата и время создания заказа</li>
|
||||
</ul>
|
||||
<p>Форматирование: поддерживается <a href="https://core.telegram.org/bots/api#markdownv2-style" target="_blank">*MarkdownV2* <i class="fa fa-external-link"></i></a>.</p>
|
||||
<p>Символы, которые нужно экранировать в тексте:</p>
|
||||
<pre>_ * [ ] ( ) ~ ` > # + - = | { } . !</pre>
|
||||
<p>Каждый из них нужно экранировать обратным слэшем \, если он не используется для форматирования. Например вместо <code>Заказ #123</code> нужно писать <code>Заказ \#123</code>.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$('#{{ settingKey }}-btn-test').click(function () {
|
||||
const telegramToken = $('#module_tgshop_bot_token').val().trim();
|
||||
if (! telegramToken) {
|
||||
alert('Сначала введите Telegram Bot Token!');
|
||||
return;
|
||||
}
|
||||
|
||||
const chatId = $('#module_tgshop_chat_id').val().trim();
|
||||
if (! chatId) {
|
||||
alert('Сначала введите Chat ID!');
|
||||
return;
|
||||
}
|
||||
|
||||
const template = $('#{{ settingKey }}').val().trim();
|
||||
if (! template) {
|
||||
alert('Сначала задайте шаблон!');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/index.php?route=extension/tgshop/handle&api_action=testTgMessage', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
token: telegramToken,
|
||||
chat_id: chatId,
|
||||
template: template
|
||||
})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(response => {
|
||||
alert(response.message || 'Уведомление успешно отправлено');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
alert('Ошибка при отправке тестового сообщения');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{% elseif item['type'] == 'text_readonly' %}
|
||||
<input type="text"
|
||||
readonly
|
||||
name="{{ settingKey }}"
|
||||
value="{{ attribute(_context, settingKey) }}"
|
||||
id="{{ settingKey }}"
|
||||
class="form-control"
|
||||
onfocus="this.select()"
|
||||
/>
|
||||
|
||||
{# BOT TOKEN #}
|
||||
{% elseif item['type'] == 'bot_token' %}
|
||||
<div class="input-group">
|
||||
<span class="input-group-btn">
|
||||
<button id="{{ settingKey }}-btn" class="btn btn-primary" type="button" onclick="validateBotToken()">
|
||||
<i class="fa fa-refresh"></i> Проверить Bot Token
|
||||
</button>
|
||||
</span>
|
||||
<input type="text"
|
||||
name="{{ settingKey }}"
|
||||
value="{{ attribute(_context, settingKey) }}"
|
||||
placeholder="{{ item['placeholder'] }}"
|
||||
id="{{ settingKey }}"
|
||||
class="form-control"
|
||||
onfocusout="validateBotToken()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="{{ settingKey }}-result-label"></div>
|
||||
|
||||
<button class="btn btn-link btn-xs" type="button" data-toggle="collapse" data-target="#{{ settingKey }}-collapse" aria-expanded="false" aria-controls="collapseExample">
|
||||
Инструкция как создать Bot Token
|
||||
</button>
|
||||
<div class="collapse" id="{{ settingKey }}-collapse">
|
||||
<div class="well">
|
||||
<p>Подробная инструкция доступна в <a href="https://nikitakiselev.github.io/telecart-docs/#telegram" target="_blank">документации <i class="fa fa-external-link"></i></a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function validateBotToken() {
|
||||
const $input = $('#{{ settingKey }}');
|
||||
const $resultLabel = $('#{{ settingKey }}-result-label');
|
||||
const botToken = $input.val();
|
||||
const url = '/admin/index.php?route=extension/module/tgshop/handle&api_action=configureBotToken&user_token={{ user_token }}';
|
||||
|
||||
if (botToken.trim().length === 0) {
|
||||
$resultLabel
|
||||
.text(`❌ Введите Bot Token!`)
|
||||
.css('color', 'red');
|
||||
return;
|
||||
}
|
||||
|
||||
$input.attr('readonly', true);
|
||||
$resultLabel.text('Проверяю...');
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ botToken }),
|
||||
})
|
||||
.then(async (res) => {
|
||||
const response = await res.json().catch(() => null);
|
||||
|
||||
if (res.status === 422) {
|
||||
console.error(res, response);
|
||||
$resultLabel
|
||||
.text(`❌ Ошибка: ${response.error}`)
|
||||
.css('color', 'red');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Ошибка ${response.error || res.statusText}`);
|
||||
}
|
||||
|
||||
if (! response.id) {
|
||||
throw new Error(`bot token is not found in server response.`);
|
||||
}
|
||||
|
||||
$resultLabel
|
||||
.text(`✅ Бот: @${response.username} (id: ${response.id}) webhook: ${response.webhook_url}`)
|
||||
.css('color', 'green');
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
$resultLabel
|
||||
.text(`❌ Ошибка проверки BotToken.`)
|
||||
.css('color', 'red');
|
||||
})
|
||||
.finally(() => $input.attr('readonly', false))
|
||||
}
|
||||
|
||||
</script>
|
||||
{% else %}
|
||||
Unsupported {{ item|json_encode }}
|
||||
{% endif %}
|
||||
|
||||
{% if attribute(_context, 'error_' ~ settingKey) %}
|
||||
<div class="text-danger">{{ attribute(_context, 'error_' ~ settingKey) }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if item['help'] %}
|
||||
<p class="help-block">{{ item['help'] }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div id="banners" class="tab-pane">
|
||||
<script>
|
||||
window.TeleCart = {
|
||||
user_token: '{{ user_token }}',
|
||||
mainpage_slider: '{{ mainpage_slider }}',
|
||||
};
|
||||
</script>
|
||||
<div id="app">App Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<script>
|
||||
window.TeleCart = {
|
||||
user_token: '{{ user_token }}',
|
||||
mainpage_slider: '{{ mainpage_slider }}',
|
||||
themes: '{{ themes | json_encode }}',
|
||||
order_statuses: '{{ order_statuses | json_encode }}',
|
||||
};
|
||||
</script>
|
||||
<div id="app">App Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -467,7 +42,7 @@
|
||||
|
||||
<script>
|
||||
const $element = $('#thumb-image-module_tgshop_app_icon');
|
||||
$('#button-clear').on('click', function() {
|
||||
$('#button-clear').on('click', function () {
|
||||
$element.find('img').attr('src', $element.find('img').attr('data-placeholder'));
|
||||
$element.parent().find('input').val('');
|
||||
$element.popover('destroy');
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
{{ header }}{{ column_left }}
|
||||
<div id="content">
|
||||
<div class="page-header">
|
||||
<div class="container-fluid">
|
||||
<div class="pull-right">
|
||||
<a href="{{ cancel }}" data-toggle="tooltip" title="{{ button_cancel }}" class="btn btn-default"><i
|
||||
class="fa fa-reply"></i></a></div>
|
||||
<h1>{{ heading_title }}</h1>
|
||||
<ul class="breadcrumb">
|
||||
{% for breadcrumb in breadcrumbs %}
|
||||
<li><a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
{% if error_warning %}
|
||||
<div class="alert alert-danger alert-dismissible"><i
|
||||
class="fa fa-exclamation-circle"></i> {{ error_warning }}
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><i class="fa fa-pencil"></i> Инициализация модуля</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="col-md-push-3 col-md-6 text-center">
|
||||
<div style="font-size: 16px;">
|
||||
<h2 style="margin-top: 50px; margin-bottom: 30px;">🛠 Добро пожаловать в модуль Telegram-магазина</h2>
|
||||
<p>Этот модуль разработан с вниманием к деталям и заботой о стабильной работе вашего магазина в Telegram. Я старался сделать его максимально простым, понятным и гибким.</p>
|
||||
<p>Если у вас возникнут вопросы, пожелания или нужны доработки — вы всегда можете обратиться:</p>
|
||||
<ul style="list-style: none">
|
||||
<li>📬 Email: <a href="mailto:kiselev2008@gmail.com">kiselev2008@gmail.com</a></li>
|
||||
<li>💬 Telegram-группа: <a href="https://t.me/ocstore3" target="_blank">https://t.me/ocstore3 <i class="fa fa-external-link"></i></a></li>
|
||||
</ul>
|
||||
|
||||
<p>Заходите в Telegram-группу, там я анонсирую свежие версии своих модулей.</p>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<p>⚠️ Перед началом работы требуется инициализация модуля.</p>
|
||||
<p>Она создаст дефолтные настройки и подготовит систему к использованию.</p>
|
||||
<p>Нажмите кнопку ниже, чтобы выполнить первичную настройку. Всё выполнится автоматически.</p>
|
||||
</div>
|
||||
<form action="{{ action }}" method="post" enctype="multipart/form-data" class="form-horizontal">
|
||||
<button type="submit"
|
||||
data-toggle="tooltip"
|
||||
title="Нажмите чтобы выполнить начальную инициализацию модуля"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
Инициализация
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ footer }}
|
||||
Reference in New Issue
Block a user