- Fix: globalna wyszukiwarka - Content-Type, Cache-Control, POST, FETCH_ASSOC, try/catch wrapper - New: document.title w szczegółach zamówienia = numer zamówienia Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
564 lines
19 KiB
PHP
564 lines
19 KiB
PHP
<?php
|
|
namespace admin\Controllers;
|
|
|
|
use Domain\Languages\LanguagesRepository;
|
|
use Domain\Settings\SettingsRepository;
|
|
use admin\ViewModels\Forms\FormEditViewModel;
|
|
use admin\ViewModels\Forms\FormField;
|
|
use admin\ViewModels\Forms\FormTab;
|
|
use admin\ViewModels\Forms\FormAction;
|
|
use admin\Support\Forms\FormRequestHandler;
|
|
|
|
/**
|
|
* Kontroler ustawien w panelu administratora.
|
|
*/
|
|
class SettingsController
|
|
{
|
|
private SettingsRepository $settingsRepository;
|
|
private LanguagesRepository $languagesRepository;
|
|
private FormRequestHandler $formHandler;
|
|
|
|
public function __construct(SettingsRepository $settingsRepository, LanguagesRepository $languagesRepository)
|
|
{
|
|
$this->settingsRepository = $settingsRepository;
|
|
$this->languagesRepository = $languagesRepository;
|
|
$this->formHandler = new FormRequestHandler();
|
|
}
|
|
|
|
/**
|
|
* Czyszczenie cache.
|
|
*/
|
|
public function clearCache(): void
|
|
{
|
|
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
|
\Shared\Helpers\Helpers::delete_dir('../thumbs/');
|
|
|
|
$redis = \Shared\Cache\RedisConnection::getInstance()->getConnection();
|
|
if ($redis) {
|
|
$redis->flushAll();
|
|
}
|
|
|
|
\Shared\Helpers\Helpers::alert('Cache został wyczyszczony.');
|
|
\Shared\Helpers\Helpers::htacces();
|
|
|
|
header('Location: /admin/dashboard/main_view/');
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Czyszczenie cache (AJAX).
|
|
*/
|
|
public function clearCacheAjax(): void
|
|
{
|
|
try {
|
|
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
|
\Shared\Helpers\Helpers::delete_dir('../thumbs/');
|
|
|
|
$redis = \Shared\Cache\RedisConnection::getInstance()->getConnection();
|
|
if ($redis) {
|
|
$redis->flushAll();
|
|
}
|
|
|
|
\Shared\Helpers\Helpers::htacces();
|
|
|
|
echo json_encode(['status' => 'success', 'message' => 'Cache został wyczyszczony.']);
|
|
} catch (\Exception $e) {
|
|
echo json_encode(['status' => 'error', 'message' => 'Błąd podczas czyszczenia cache: ' . $e->getMessage()]);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Globalna wyszukiwarka admin (produkty + zamowienia) - AJAX.
|
|
*/
|
|
public function globalSearchAjax(): void
|
|
{
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
header('Cache-Control: no-store');
|
|
|
|
try {
|
|
$this->executeGlobalSearch();
|
|
} catch (\Throwable $e) {
|
|
echo json_encode([
|
|
'status' => 'error',
|
|
'items' => [],
|
|
]);
|
|
}
|
|
exit;
|
|
}
|
|
|
|
private function executeGlobalSearch(): void
|
|
{
|
|
global $mdb;
|
|
|
|
$phrase = isset($_REQUEST['q']) ? trim((string)$_REQUEST['q']) : '';
|
|
if ($phrase === '' || mb_strlen($phrase) < 2) {
|
|
echo json_encode(['status' => 'ok', 'items' => []]);
|
|
return;
|
|
}
|
|
|
|
$phrase = mb_substr($phrase, 0, 120);
|
|
$phraseNormalized = trim((string)preg_replace('/\s+/', ' ', $phrase));
|
|
$like = '%' . $phrase . '%';
|
|
$likeNormalized = '%' . $phraseNormalized . '%';
|
|
|
|
$items = [];
|
|
|
|
$defaultLang = '1';
|
|
try {
|
|
$defaultLang = (string)$this->languagesRepository->defaultLanguage();
|
|
} catch (\Throwable $e) {
|
|
// fallback to '1'
|
|
}
|
|
|
|
// --- Produkty ---
|
|
try {
|
|
$productStmt = $mdb->query(
|
|
'SELECT '
|
|
. 'p.id, p.ean, p.sku, p.parent_id, psl.name '
|
|
. 'FROM pp_shop_products AS p '
|
|
. 'LEFT JOIN pp_shop_products_langs AS psl ON psl.product_id = p.id AND psl.lang_id = :lang_id '
|
|
. 'WHERE '
|
|
. '(p.ean LIKE :q1 OR p.sku LIKE :q2 OR psl.name LIKE :q3) '
|
|
. 'AND p.archive != 1 '
|
|
. 'ORDER BY p.id DESC '
|
|
. 'LIMIT 15',
|
|
[
|
|
':lang_id' => $defaultLang,
|
|
':q1' => $like,
|
|
':q2' => $like,
|
|
':q3' => $like,
|
|
]
|
|
);
|
|
} catch (\Throwable $e) {
|
|
$productStmt = false;
|
|
}
|
|
|
|
$productRows = ($productStmt && method_exists($productStmt, 'fetchAll'))
|
|
? $productStmt->fetchAll(\PDO::FETCH_ASSOC)
|
|
: [];
|
|
|
|
if (is_array($productRows)) {
|
|
foreach ($productRows as $row) {
|
|
$productId = (int)($row['id'] ?? 0);
|
|
if ($productId <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$name = trim((string)($row['name'] ?? ''));
|
|
if ($name === '') {
|
|
$name = 'Produkt #' . $productId;
|
|
}
|
|
|
|
$meta = [];
|
|
$sku = trim((string)($row['sku'] ?? ''));
|
|
$ean = trim((string)($row['ean'] ?? ''));
|
|
if ($sku !== '') {
|
|
$meta[] = 'SKU: ' . $sku;
|
|
}
|
|
if ($ean !== '') {
|
|
$meta[] = 'EAN: ' . $ean;
|
|
}
|
|
|
|
$items[] = [
|
|
'type' => 'product',
|
|
'title' => $name,
|
|
'subtitle' => implode(' | ', $meta),
|
|
'url' => '/admin/shop_product/product_edit/id=' . $productId,
|
|
];
|
|
}
|
|
}
|
|
|
|
// --- Zamowienia ---
|
|
try {
|
|
$orderStmt = $mdb->query(
|
|
'SELECT '
|
|
. 'id, number, client_name, client_surname, client_email, client_phone '
|
|
. 'FROM pp_shop_orders '
|
|
. 'WHERE '
|
|
. '('
|
|
. 'number LIKE :q1 '
|
|
. 'OR client_email LIKE :q2 '
|
|
. 'OR client_name LIKE :q3 '
|
|
. 'OR client_surname LIKE :q4 '
|
|
. 'OR client_phone LIKE :q5 '
|
|
. "OR CONCAT_WS(' ', TRIM(client_name), TRIM(client_surname)) LIKE :q6 "
|
|
. "OR CONCAT_WS(' ', TRIM(client_surname), TRIM(client_name)) LIKE :q7 "
|
|
. ') '
|
|
. 'ORDER BY id DESC '
|
|
. 'LIMIT 15',
|
|
[
|
|
':q1' => $like,
|
|
':q2' => $like,
|
|
':q3' => $like,
|
|
':q4' => $like,
|
|
':q5' => $like,
|
|
':q6' => $likeNormalized,
|
|
':q7' => $likeNormalized,
|
|
]
|
|
);
|
|
} catch (\Throwable $e) {
|
|
$orderStmt = false;
|
|
}
|
|
|
|
$orderRows = ($orderStmt && method_exists($orderStmt, 'fetchAll'))
|
|
? $orderStmt->fetchAll(\PDO::FETCH_ASSOC)
|
|
: [];
|
|
|
|
if (is_array($orderRows)) {
|
|
foreach ($orderRows as $row) {
|
|
$orderId = (int)($row['id'] ?? 0);
|
|
if ($orderId <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$orderNumber = trim((string)($row['number'] ?? ''));
|
|
$clientName = trim((string)($row['client_name'] ?? ''));
|
|
$clientSurname = trim((string)($row['client_surname'] ?? ''));
|
|
$clientEmail = trim((string)($row['client_email'] ?? ''));
|
|
$clientPhone = trim((string)($row['client_phone'] ?? ''));
|
|
|
|
$title = $orderNumber !== '' ? 'Zamówienie ' . $orderNumber : 'Zamówienie #' . $orderId;
|
|
$subtitleParts = [];
|
|
$fullName = trim($clientName . ' ' . $clientSurname);
|
|
if ($fullName !== '') {
|
|
$subtitleParts[] = $fullName;
|
|
}
|
|
if ($clientEmail !== '') {
|
|
$subtitleParts[] = $clientEmail;
|
|
}
|
|
if ($clientPhone !== '') {
|
|
$subtitleParts[] = $clientPhone;
|
|
}
|
|
|
|
$items[] = [
|
|
'type' => 'order',
|
|
'title' => $title,
|
|
'subtitle' => implode(' | ', $subtitleParts),
|
|
'url' => '/admin/shop_order/order_details/order_id=' . $orderId,
|
|
];
|
|
}
|
|
}
|
|
|
|
$json = json_encode(['status' => 'ok', 'items' => array_slice($items, 0, 20)]);
|
|
if ($json === false) {
|
|
echo json_encode(['status' => 'ok', 'items' => []], JSON_UNESCAPED_UNICODE);
|
|
return;
|
|
}
|
|
echo $json;
|
|
}
|
|
|
|
/**
|
|
* Zapis ustawien (AJAX).
|
|
*/
|
|
public function save(): void
|
|
{
|
|
// Kompatybilnosc wsteczna dla legacy gridEdit (values jako JSON).
|
|
$legacyValues = \Shared\Helpers\Helpers::get('values');
|
|
if ($legacyValues) {
|
|
$values = json_decode($legacyValues, true);
|
|
$result = $this->settingsRepository->saveSettings(is_array($values) ? $values : []);
|
|
|
|
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
|
\Shared\Helpers\Helpers::htacces();
|
|
|
|
echo json_encode($result);
|
|
exit;
|
|
}
|
|
|
|
$languages = $this->languagesRepository->languagesList();
|
|
$settings = $this->settingsRepository->getSettings();
|
|
$viewModel = $this->buildFormViewModel($settings, $languages);
|
|
|
|
$result = $this->formHandler->handleSubmit($viewModel, $_POST);
|
|
if (!$result['success']) {
|
|
$_SESSION['form_errors'][$this->getFormId()] = $result['errors'];
|
|
echo json_encode(['success' => false, 'errors' => $result['errors']]);
|
|
exit;
|
|
}
|
|
|
|
$values = $this->transformFormDataToSettings($result['data']);
|
|
$saveResult = $this->settingsRepository->saveSettings($values);
|
|
|
|
\Shared\Helpers\Helpers::delete_dir('../temp/');
|
|
\Shared\Helpers\Helpers::htacces();
|
|
|
|
echo json_encode([
|
|
'success' => ($saveResult['status'] ?? '') === 'ok',
|
|
'message' => $saveResult['msg'] ?? 'Ustawienia zostały zapisane.',
|
|
'errors' => (($saveResult['status'] ?? '') === 'ok') ? [] : ['general' => ($saveResult['msg'] ?? 'Błąd zapisu.')],
|
|
]);
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Widok ustawien.
|
|
*/
|
|
public function view(): string
|
|
{
|
|
$languages = $this->languagesRepository->languagesList();
|
|
$settings = $this->settingsRepository->getSettings();
|
|
|
|
$validationErrors = $_SESSION['form_errors'][$this->getFormId()] ?? null;
|
|
if ($validationErrors) {
|
|
unset($_SESSION['form_errors'][$this->getFormId()]);
|
|
}
|
|
|
|
$viewModel = $this->buildFormViewModel($settings, $languages, $validationErrors);
|
|
|
|
return \Shared\Tpl\Tpl::view('components/form-edit', ['form' => $viewModel]);
|
|
}
|
|
|
|
private function buildFormViewModel(array $settings, array $languages, ?array $errors = null): FormEditViewModel
|
|
{
|
|
$data = $this->transformSettingsToFormData($settings, $languages);
|
|
|
|
$tabs = [
|
|
new FormTab('contact', 'Dane kontaktowe', 'fa-paper-plane'),
|
|
new FormTab('shop', 'Sklep', 'fa-dollar'),
|
|
new FormTab('products', 'Produkty', 'fa-shopping-cart'),
|
|
new FormTab('mail', 'Poczta', 'fa-envelope'),
|
|
new FormTab('other', 'Pozostałe', 'fa-bars'),
|
|
new FormTab('system', 'System', 'fa-cog'),
|
|
new FormTab('conversions', 'Konwersje', 'fa-line-chart'),
|
|
];
|
|
|
|
$fields = [
|
|
FormField::text('firm_name', [
|
|
'label' => 'Nazwa firmy',
|
|
'tab' => 'contact',
|
|
]),
|
|
FormField::editor('additional_info', [
|
|
'label' => 'Dodatkowe informacje',
|
|
'tab' => 'contact',
|
|
'height' => 150,
|
|
]),
|
|
FormField::switch('google_maps', [
|
|
'label' => 'Mapa',
|
|
'tab' => 'contact',
|
|
]),
|
|
FormField::textarea('firm_adress', [
|
|
'label' => 'Mapa - adres',
|
|
'tab' => 'contact',
|
|
]),
|
|
|
|
FormField::editor('shop_bank_account_info', [
|
|
'label' => 'Dane do przelewu',
|
|
'tab' => 'shop',
|
|
'height' => 200,
|
|
]),
|
|
FormField::text('hotpay_api', [
|
|
'label' => 'Klucz API HotPay',
|
|
'tab' => 'shop',
|
|
]),
|
|
FormField::switch('tpay_sandbox', [
|
|
'label' => 'Tpay.com - tryb sandbox',
|
|
'tab' => 'shop',
|
|
]),
|
|
FormField::text('tpay_id', [
|
|
'label' => 'Tpay.com ID',
|
|
'tab' => 'shop',
|
|
]),
|
|
FormField::text('tpay_security_code', [
|
|
'label' => 'Tpay.com - kod bezpieczeństwa',
|
|
'tab' => 'shop',
|
|
]),
|
|
FormField::switch('przelewy24_sandbox', [
|
|
'label' => 'Przelewy24.pl - tryb sandbox',
|
|
'tab' => 'shop',
|
|
]),
|
|
FormField::text('przelewy24_merchant_id', [
|
|
'label' => 'Przelewy24.pl - merchant ID',
|
|
'tab' => 'shop',
|
|
]),
|
|
FormField::text('przelewy24_crc_key', [
|
|
'label' => 'Przelewy24.pl - klucz CRC',
|
|
'tab' => 'shop',
|
|
]),
|
|
FormField::text('free_delivery', [
|
|
'label' => 'Darmowa dostawa od',
|
|
'tab' => 'shop',
|
|
'attributes' => ['class' => 'number-format'],
|
|
]),
|
|
FormField::text('orlen_paczka_map_token', [
|
|
'label' => 'Orlen Paczka map token',
|
|
'tab' => 'shop',
|
|
]),
|
|
|
|
FormField::langSection('warehouse_messages', 'products', [
|
|
FormField::text('warehouse_message_zero', [
|
|
'label' => 'Komunikat gdy stan magazynowy równy 0',
|
|
]),
|
|
FormField::text('warehouse_message_nonzero', [
|
|
'label' => 'Komunikat gdy stan magazynowy większy niż 0',
|
|
]),
|
|
]),
|
|
|
|
FormField::switch('contact_form', [
|
|
'label' => 'Formularz kontaktowy',
|
|
'tab' => 'mail',
|
|
]),
|
|
FormField::text('contact_email', [
|
|
'label' => 'Email kontaktowy',
|
|
'tab' => 'mail',
|
|
]),
|
|
FormField::text('email_host', [
|
|
'label' => 'Email - host',
|
|
'tab' => 'mail',
|
|
]),
|
|
FormField::text('email_port', [
|
|
'label' => 'Email - port',
|
|
'tab' => 'mail',
|
|
]),
|
|
FormField::text('email_login', [
|
|
'label' => 'Email - login',
|
|
'tab' => 'mail',
|
|
]),
|
|
FormField::text('email_password', [
|
|
'label' => 'Email - hasło',
|
|
'tab' => 'mail',
|
|
]),
|
|
|
|
FormField::text('facebook_link', [
|
|
'label' => 'Facebook link',
|
|
'tab' => 'other',
|
|
]),
|
|
FormField::text('piksel', [
|
|
'label' => 'Piksel Facebook',
|
|
'tab' => 'other',
|
|
]),
|
|
FormField::textarea('statistic_code', [
|
|
'label' => 'Kod statystyk',
|
|
'tab' => 'other',
|
|
'rows' => 10,
|
|
]),
|
|
FormField::textarea('htaccess', [
|
|
'label' => 'Własne reguły htacess',
|
|
'tab' => 'other',
|
|
'rows' => 10,
|
|
]),
|
|
FormField::textarea('robots', [
|
|
'label' => 'Własne reguły robots.txt',
|
|
'tab' => 'other',
|
|
'rows' => 10,
|
|
]),
|
|
|
|
FormField::switch('update', [
|
|
'label' => 'Aktualizacja',
|
|
'tab' => 'system',
|
|
]),
|
|
FormField::text('update_key', [
|
|
'label' => 'Numer licencji',
|
|
'tab' => 'system',
|
|
]),
|
|
FormField::switch('devel', [
|
|
'label' => 'Strona konstrukcyjna',
|
|
'tab' => 'system',
|
|
]),
|
|
FormField::switch('lazy_loading', [
|
|
'label' => 'Lazy loading obrazów',
|
|
'tab' => 'system',
|
|
]),
|
|
FormField::switch('generate_webp', [
|
|
'label' => 'Generowanie obrazków WEBP',
|
|
'tab' => 'system',
|
|
]),
|
|
FormField::switch('infinitescroll', [
|
|
'label' => 'Infinitescroll',
|
|
'tab' => 'system',
|
|
]),
|
|
FormField::switch('htaccess_cache', [
|
|
'label' => 'Htaccess cache',
|
|
'tab' => 'system',
|
|
]),
|
|
FormField::text('api_key', [
|
|
'label' => 'Klucz API (ordersPRO)',
|
|
'tab' => 'system',
|
|
]),
|
|
|
|
FormField::text('google_tag_manager_id', [
|
|
'label' => 'Google Tag Manager - ID',
|
|
'tab' => 'conversions',
|
|
]),
|
|
FormField::textarea('own_gtm_js', [
|
|
'label' => 'Własny kod GTM JS (bez tagu script)',
|
|
'tab' => 'conversions',
|
|
'rows' => 10,
|
|
]),
|
|
FormField::textarea('own_gtm_html', [
|
|
'label' => 'Własny kod GTM HTML',
|
|
'tab' => 'conversions',
|
|
'rows' => 10,
|
|
]),
|
|
];
|
|
|
|
$actions = [
|
|
FormAction::save('/admin/settings/save/', ''),
|
|
];
|
|
|
|
return new FormEditViewModel(
|
|
$this->getFormId(),
|
|
'Edycja ustawień',
|
|
$data,
|
|
$fields,
|
|
$tabs,
|
|
$actions,
|
|
'POST',
|
|
'/admin/settings/save/',
|
|
null,
|
|
false,
|
|
[],
|
|
$languages,
|
|
$errors
|
|
);
|
|
}
|
|
|
|
private function getFormId(): string
|
|
{
|
|
return 'settings-edit';
|
|
}
|
|
|
|
private function transformSettingsToFormData(array $settings, array $languages): array
|
|
{
|
|
$data = $settings;
|
|
$data['languages'] = [];
|
|
|
|
foreach ($languages as $lang) {
|
|
if (!($lang['status'] ?? false)) {
|
|
continue;
|
|
}
|
|
|
|
$langId = (string)$lang['id'];
|
|
$data['languages'][$langId] = [
|
|
'warehouse_message_zero' => $settings['warehouse_message_zero_' . $langId] ?? '',
|
|
'warehouse_message_nonzero' => $settings['warehouse_message_nonzero_' . $langId] ?? '',
|
|
];
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
private function transformFormDataToSettings(array $data): array
|
|
{
|
|
if (!isset($data['warehouse_messages']) || !is_array($data['warehouse_messages'])) {
|
|
return $data;
|
|
}
|
|
|
|
$data['warehouse_message_zero'] = [];
|
|
$data['warehouse_message_nonzero'] = [];
|
|
|
|
foreach ($data['warehouse_messages'] as $langId => $langValues) {
|
|
if (!is_array($langValues)) {
|
|
continue;
|
|
}
|
|
|
|
$data['warehouse_message_zero'][$langId] = $langValues['warehouse_message_zero'] ?? '';
|
|
$data['warehouse_message_nonzero'][$langId] = $langValues['warehouse_message_nonzero'] ?? '';
|
|
}
|
|
|
|
unset($data['warehouse_messages']);
|
|
|
|
return $data;
|
|
}
|
|
}
|