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::custom('api_key', $this->renderApiKeyField($data['api_key'] ?? ''), [ '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; } private function renderApiKeyField(string $value): string { $escaped = htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); $js = "var c='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'," . "k='';for(var i=0;i<32;i++){k+=c.charAt(Math.floor(Math.random()*c.length));}" . "document.getElementById('api_key').value=k;"; return '