' . esc_html($msg) . '

'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- class-mom-admin.php::admin_notices: ausschließlich Anzeige/Filter/fester Navigationspfad; kein Speichern oder Erteilen einer Berechtigung. Mutierende Handler besitzen eigene Prüfung. $mom_saved = isset($_GET['mom_saved']) && $_GET['mom_saved'] === '1'; if ($mom_saved) { echo '

' . esc_html__('Einstellungen gespeichert.', 'restocommerce') . '

'; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- class-mom-admin.php::admin_notices: ausschließlich Anzeige/Filter/fester Navigationspfad; kein Speichern oder Erteilen einer Berechtigung. Mutierende Handler besitzen eigene Prüfung. $mom_err = isset($_GET['mom_error']) ? sanitize_text_field(wp_unslash($_GET['mom_error'])) : ''; if ($mom_err !== '') { echo '

' . esc_html($mom_err) . '

'; } } /** * Admin-Post Fallback: Speichern wenn JS deaktiviert (kein options.php) */ public function handle_admin_post_save(): void { try { if (!current_user_can('manage_options')) { if (function_exists('mom_log_req')) { mom_log_req('ADMIN_SAVE_ERR', ['status_code' => 403, 'error' => 'unauthorized', 'user_id' => get_current_user_id()]); } wp_die(esc_html__('Unauthorized', 'restocommerce'), 403); } check_admin_referer('mom_save_settings', 'mom_nonce'); if (function_exists('mom_log_req')) { mom_log_req('ADMIN_SAVE_START', [ 'endpoint' => 'admin-post:mom_save_settings', 'user_id' => get_current_user_id(), ]); } $raw = isset($_POST['mom_gastrokomplete_settings']) && is_array($_POST['mom_gastrokomplete_settings']) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- wp_unslash am Eingang; sanitize_settings prüft die einzelnen Einstellungen, Lieferzonen zusätzlich validate_zones; die REST-Einstellungs-API prüft erneut. Keine pauschale Textsanitisierung strukturierter Arrays. ? wp_unslash($_POST['mom_gastrokomplete_settings']) : []; $raw = GC_Setup_Wizard::normalize_admin_hours($raw); if (is_wp_error($raw)) { wp_die(esc_html($raw->get_error_message()), '', ['response' => 400, 'back_link' => true]); } $merged = $this->sanitize_settings($raw); $payload = [ 'settings' => [ 'mom_gastrokomplete' => [ 'online_orders' => $merged['online_orders'] ?? [], 'timeslots' => $merged['timeslots'] ?? [], 'delivery' => $merged['delivery'] ?? [], 'inventory' => $merged['inventory'] ?? [], 'debug' => $merged['debug'] ?? [], 'website' => $merged['website'] ?? [], 'mail' => $merged['mail'] ?? [], ], ], ]; $request = new WP_REST_Request('POST', '/mom/v1/admin/settings'); $request->set_header('Content-Type', 'application/json'); $request->set_body(wp_json_encode($payload)); $api = MoM_Admin_Settings_API::get_instance(); $response = $api->update_settings($request); if (is_wp_error($response)) { $msg = $response->get_error_message(); if (function_exists('mom_log_req')) { mom_log_req('ADMIN_SAVE_ERR', ['status_code' => $response->get_error_data()['status'] ?? 500, 'message' => $msg]); } $tab = isset($_POST['_tab']) ? sanitize_text_field(wp_unslash($_POST['_tab'])) : 'general'; wp_safe_redirect(self::settings_redirect_url($tab, ['mom_error' => $msg])); exit; } if (function_exists('mom_log_req')) { mom_log_req('ADMIN_SAVE_OK', ['endpoint' => 'admin-post:mom_save_settings', 'user_id' => get_current_user_id()]); } $tab = isset($_POST['_tab']) ? sanitize_text_field(wp_unslash($_POST['_tab'])) : 'general'; wp_safe_redirect(self::settings_redirect_url($tab, ['mom_saved' => '1'])); exit; } catch (Throwable $e) { if (function_exists('mom_log_req')) { mom_log_req('ADMIN_SAVE_ERR', [ 'status_code' => 500, 'error' => 'handle_admin_post_save_fatal', 'file' => $e->getFile(), 'line' => $e->getLine(), 'message' => $e->getMessage(), 'user_id' => get_current_user_id(), ]); } (function_exists('restocommerce_debug_event') && restocommerce_debug_event('RCDBG0052')); set_transient('mom_settings_save_failed', __('Speichern fehlgeschlagen:', 'restocommerce') . ' ' . $e->getMessage(), 60); wp_safe_redirect(admin_url('admin.php?page=mom-gastrokomplete')); exit; } } public function add_menu(): void { add_menu_page( 'RestoCommerce', 'RestoCommerce', 'manage_woocommerce', 'mom-gastrokomplete', [$this, 'render_page'], '', // Empty - we'll use CSS for the icon 56 ); add_submenu_page( 'mom-gastrokomplete', __('Zahlungsarten', 'restocommerce'), __('Zahlungsarten', 'restocommerce'), 'manage_woocommerce', 'mom-payment-methods', [$this, 'render_payment_methods_page'] ); if (class_exists('GC_Setup_Wizard') && class_exists('GC_Setup_Presets')) { $gc_state = GC_Setup_Presets::get_state(); $done = !empty($gc_state['completed']); add_submenu_page( 'mom-gastrokomplete', $done ? __('Installationswizard öffnen', 'restocommerce') : __('Einrichtung fortsetzen', 'restocommerce'), $done ? __('Installationswizard öffnen', 'restocommerce') : __('Einrichtung fortsetzen', 'restocommerce'), 'manage_options', 'gc-setup', ['GC_Setup_Wizard', 'render'] ); } } public function add_menu_icon_style(): void { $icon_url = MOM_GASTROKOMPLETE_URL . 'assets/admin/images/gastrocomplete-icon.svg'; ?> #toplevel_page_mom-gastrokomplete .wp-menu-image { background-image: url('') !important; background-size: 22px 22px; background-position: center; background-repeat: no-repeat; width: 22px; height: 22px; } #toplevel_page_mom-gastrokomplete .wp-menu-image:before { content: '' !important; } #toplevel_page_mom-gastrokomplete .wp-menu-image img { display: none !important; } #toplevel_page_mom-gastrokomplete:hover .wp-menu-image, #toplevel_page_mom-gastrokomplete.current .wp-menu-image { background-image: url('') !important; opacity: 1; filter: brightness(1.1); } admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('gc_setup'), ]); } wp_enqueue_style('mom-admin', MOM_GASTROKOMPLETE_URL . 'assets/admin/css/admin.css', ['gc-moms-tokens', 'gc-moms-fonts'], MOM_GASTROKOMPLETE_VERSION); wp_enqueue_script('rc-hours-format', MOM_GASTROKOMPLETE_URL . 'assets/admin/js/hours-format.js', ['jquery'], MOM_GASTROKOMPLETE_VERSION, true); wp_enqueue_script('mom-admin', MOM_GASTROKOMPLETE_URL . 'assets/admin/js/admin.js', ['jquery'], MOM_GASTROKOMPLETE_VERSION, true); // REST-Save Script auf allen MoM-Seiten mit Formular (damit Speichern-Button immer reagiert) if (strpos($hook, 'mom-gastrokomplete') !== false) { wp_enqueue_script( 'mom-admin-settings', MOM_GASTROKOMPLETE_URL . 'assets/admin/js/mom-admin-settings.js', ['jquery'], MOM_GASTROKOMPLETE_VERSION, true ); wp_localize_script('mom-admin-settings', 'momAdminSettings', [ 'restUrl' => rest_url('mom/v1/admin/settings'), 'nonce' => wp_create_nonce('wp_rest'), 'i18n' => [ 'saving' => __('Speichern…', 'restocommerce'), 'saved' => __('Einstellungen gespeichert.', 'restocommerce'), 'error' => __('Speichern fehlgeschlagen.', 'restocommerce'), 'errorDefault' => __('Ein unbekannter Fehler ist aufgetreten.', 'restocommerce'), ], ]); } if ($is_payment_methods_page) { wp_enqueue_style( 'mom-payment-methods', MOM_GASTROKOMPLETE_URL . 'assets/admin/css/mom-payment-methods.css', [], filemtime(MOM_GASTROKOMPLETE_PATH . 'assets/admin/css/mom-payment-methods.css') ?: MOM_GASTROKOMPLETE_VERSION ); wp_enqueue_script( 'mom-payment-methods', MOM_GASTROKOMPLETE_URL . 'assets/admin/js/mom-payment-methods.js', ['jquery', 'jquery-ui-sortable'], filemtime(MOM_GASTROKOMPLETE_PATH . 'assets/admin/js/mom-payment-methods.js') ?: MOM_GASTROKOMPLETE_VERSION, true ); wp_localize_script('mom-payment-methods', 'momPaymentMethods', [ 'restUrl' => rest_url(), 'nonce' => wp_create_nonce('wp_rest'), ]); } // Enqueue WooCommerce product search scripts for product selection if (class_exists('WooCommerce')) { // WooCommerce's enhanced select scripts handle product search automatically // We need to enqueue these to get the proper nonce handling wp_enqueue_script('wc-enhanced-select'); wp_enqueue_style('woocommerce_admin_styles'); // Use selectWoo (WooCommerce 3.0+) or select2 as fallback if (wp_script_is('selectWoo', 'registered')) { wp_enqueue_script('selectWoo'); wp_enqueue_style('selectWoo'); } else { wp_enqueue_script('select2'); wp_enqueue_style('select2'); } // Localize script with AJAX URL wp_localize_script('mom-admin', 'momAdmin', [ 'ajaxUrl' => admin_url('admin-ajax.php'), ]); // WooCommerce's wc-enhanced-select script should provide wc_product_search_params // But we ensure it's available as fallback $search_params = [ 'ajax_url' => admin_url('admin-ajax.php'), 'search_products_nonce' => wp_create_nonce('woocommerce_json_search_products'), ]; $json_flags = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT; wp_add_inline_script('mom-admin', 'if (typeof wc_product_search_params === "undefined") { var wc_product_search_params = ' . wp_json_encode($search_params, $json_flags) . '; }' . 'if (typeof ajaxurl === "undefined") { var ajaxurl = ' . wp_json_encode(admin_url('admin-ajax.php'), $json_flags) . '; }' ); } } public function render_page(): void { if (!current_user_can('manage_options')) { GC_Merchant_Settings::admin_page(); return; } if (!current_user_can('manage_woocommerce')) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- class-mom-admin.php::render_page: ausschließlich Anzeige/Filter/fester Navigationspfad; kein Speichern oder Erteilen einer Berechtigung. Mutierende Handler besitzen eigene Prüfung. $tab = isset($_GET['tab']) ? sanitize_text_field(wp_unslash($_GET['tab'])) : 'general'; if (in_array($tab, ['tse', 'pos', 'deposit', 'promotions', 'reservations'], true)) { wp_safe_redirect(admin_url('admin.php?page=mom-gastrokomplete&tab=general')); exit; } $settings = MoM_GastroComplete_Core::get_settings(); ?>

render_tab_extras($settings); ?> render_tab_menu($settings); ?>
render_tab_general($settings); break; case 'ci': $this->render_tab_ci($settings); break; case 'seo': $this->render_tab_seo($settings); break; case 'opening-hours': case 'online-orders': $this->render_tab_opening_hours($settings); break; case 'timeslots': $this->render_tab_timeslots($settings); break; case 'delivery': $this->render_tab_delivery($settings); break; case 'inventory': $this->render_tab_inventory($settings); break; case 'email': $this->render_tab_email($settings); break; case 'debug': $this->render_tab_debug($settings); break; } ?>

— · · ·

class_exists('GC_Frontend') ? GC_Frontend::sanitize_appearance($w['appearance'] ?? 'light') : ((($w['appearance'] ?? 'light') === 'dark') ? 'dark' : 'light'), 'banner_id' => (int) ($w['banner_id'] ?? 0), 'logo_id' => (int) ($w['logo_id'] ?? 0), 'favicon_id' => (int) ($w['favicon_id'] ?? 0), 'show_product_images' => (string) ($w['show_product_images'] ?? '0') === '1' ? 1 : 0, 'landing_enabled' => !empty($w['landing_enabled']) ? 1 : 0, 'color' => (string) ($w['color'] ?? '#5a6b4e'), 'color_contrast' => (string) ($w['color_contrast'] ?? '#ffffff'), ]; $appearance = $ci['appearance']; $ci_color = $ci['color']; $ci_contrast = $ci['color_contrast']; $media = [ 'logo' => [ 'label' => __('Logo', 'restocommerce'), 'help' => __('Sitzt im Header und auf dem Banner. Wird auch für den Laufzettel genutzt.', 'restocommerce'), 'id' => (int) $ci['logo_id'], 'pick' => __('Logo wählen', 'restocommerce'), 'size' => 'medium', ], 'favicon' => [ 'label' => __('Favicon', 'restocommerce'), 'help' => __('Kleines Symbol im Browser-Tab. Quadratisch, mindestens 512×512 px.', 'restocommerce'), 'id' => (int) $ci['favicon_id'], 'pick' => __('Favicon wählen', 'restocommerce'), 'size' => 'thumbnail', ], 'banner' => [ 'label' => __('Bannerbild', 'restocommerce'), 'help' => __('Querformat für die Startseite (Ladenfront oder Speise). Ohne eigenes Bild wird ein Kategorie-Motiv genutzt.', 'restocommerce'), 'id' => (int) $ci['banner_id'], 'pick' => __('Banner wählen', 'restocommerce'), 'size' => 'medium', ], ]; ?>

$row): ?>

Hinzufügen 12,90 € +

Automatisch folgt der Hell-/Dunkel-Einstellung des Geräts. Hell und Dunkel bleiben fest.

Ohne Bilder: Karten wie bei Lieferando mit Name, Preis und Plus-Button.

Startseite mit Text und „Online bestellen“. Die Speisekarte bleibt unter /speisekarte. Texte unter .

Social Media

Kompakte Icons im Footer. Nur ausgefüllte Links erscheinen — Header und Warenkorb bleiben frei.

$meta): $value = (string) ($ci['social_' . $key] ?? ''); ?>

jQuery(function ($) { if ($.fn.wpColorPicker) { $('.gc-admin-color').each(function () { var $input = $(this); $input.wpColorPicker({ change: function (event, ui) { if (!ui || !ui.color) return; var hex = ui.color.toString(); var isContrast = ($input.attr('name') || '').indexOf('color_contrast') !== -1; if (isContrast) { $('.gc-admin-ci-chip, .gc-admin-ci-plus').css('color', hex); } else { $('.gc-admin-ci-chip, .gc-admin-ci-plus').css('background-color', hex); } } }); }); } if (!wp.media) return; $('.gc-admin-media-pick').on('click', function (e) { e.preventDefault(); var $btn = $(this); var frame = wp.media({ title: $btn.data('title') || , multiple: false, library: { type: 'image' } }); frame.on('select', function () { var att = frame.state().get('selection').first().toJSON(); $('#' + $btn.data('target')).val(att.id); $('#' + $btn.data('preview')).html(''); }); frame.open(); }); $('.gc-admin-media-clear').on('click', function (e) { e.preventDefault(); var $btn = $(this); $('#' + $btn.data('target')).val('0'); $('#' + $btn.data('preview')).empty(); }); });

.

.

__('Mittag von', 'restocommerce'), 'l_to' => __('Mittag bis', 'restocommerce'), 'e_from' => __('Abend von', 'restocommerce'), 'e_to' => __('Abend bis', 'restocommerce')]; $days = [ 'mon' => __('Montag', 'restocommerce'), 'tue' => __('Dienstag', 'restocommerce'), 'wed' => __('Mittwoch', 'restocommerce'), 'thu' => __('Donnerstag', 'restocommerce'), 'fri' => __('Freitag', 'restocommerce'), 'sat' => __('Samstag', 'restocommerce'), 'sun' => __('Sonntag', 'restocommerce'), ]; ?>

>

$label): $h = $s['hours'][$key] ?? ['open' => 0, 'l_from' => '11:00', 'l_to' => '14:00', 'e_from' => '17:00', 'e_to' => '22:00']; ?> $time_label): $time = (string) ($h[$key_time] ?? ''); $period = 'AM'; if ($format === '12' && preg_match('/^([01][0-9]|2[0-3]):([0-5][0-9])$/', $time, $parts)) { $hour = (int) $parts[1]; $period = $hour >= 12 ? 'PM' : 'AM'; $time = sprintf('%02d:%s', $hour % 12 ?: 12, $parts[2]); } $field_label = $label . ' · ' . $time_label; ?>
/>

'product_cat', 'hide_empty' => false]); if (!is_wp_error($terms) && !empty($terms)): ?> term_id; $lim = (int)($category_limits[$tid] ?? 0); ?>
name); ?>

'mom-gastrokomplete', 'tab' => $tab], $args), admin_url('admin.php')); } private function render_tab_extras(array $settings): void { if (!class_exists('GastroComplete_Extras_Plugin')) { echo '

' . esc_html__('Das Extras-Modul ist nicht verfügbar.', 'restocommerce') . '

'; return; } // Get the instance - GastroComplete_Extras_Plugin is instantiated at the bottom of the file global $restocommerce_extras_plugin_instance; $instance = null; if (isset($restocommerce_extras_plugin_instance) && method_exists($restocommerce_extras_plugin_instance, 'admin_page')) { $instance = $restocommerce_extras_plugin_instance; } else { // Fallback: create a temporary instance just to render the page $instance = new GastroComplete_Extras_Plugin(); } if ($instance && method_exists($instance, 'admin_page')) { // Capture the output ob_start(); $instance->admin_page(); $output = ob_get_clean(); // Remove the outer wrap div and h1 (we're already inside a wrap) $output = preg_replace('/
\s*/i', '', $output, 1); $output = preg_replace('/

Extras & Zutaten<\/h1>\s*/i', '

Extras & Zutaten

', $output, 1); // Remove the closing wrap div at the end (but keep other content) $output = preg_replace('/<\/div>\s*$/', '', $output); echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Captured admin_page renderer escapes fields; removing wrappers must preserve forms, nonces and inline UI script. } else { echo '

' . esc_html__('Die Extras-Seite konnte nicht geladen werden.', 'restocommerce') . '

'; } } private function render_tab_inventory(array $settings): void { $s = $settings['inventory'] ?? []; $enabled = !empty($s['enabled']); $show_in_shop = array_key_exists('show_in_shop', $s) ? !empty($s['show_in_shop']) : true; $rows = class_exists('MoM_GastroComplete_Inventory') ? MoM_GastroComplete_Inventory::get_categories_snapshot() : []; ?>

>

/>
'rot', 'label' => '', 'last_test' => '', 'last_error' => '', 'hint' => '']; $conflicts = class_exists('GC_Mail_Settings') ? GC_Mail_Settings::conflicting_smtp_plugins() : []; $err = class_exists('GC_Mail_Log') ? GC_Mail_Log::last_error() : []; ?>

—

·

.

$label): ?>

jQuery(function ($) { $('#gc-admin-mail-test-btn').on('click', function () { var $b = $(this); $b.prop('disabled', true); $.post(ajaxurl, { action: 'gc_mail_test', nonce: '', to: $('#gc-admin-mail-test-to').val() }).done(function (res) { $('#gc-admin-mail-test-result').text(res && res.success ? (res.data && res.data.message) : ((res && res.data && res.data.message) || )); }).always(function () { $b.prop('disabled', false); }); }); });

reset, null->'', always string. */ private static function mom_safe_scalar($v) { if (is_array($v)) { $v = reset($v); } return $v === null ? '' : (string)$v; } private static function sanitize_preorder_flag($raw, int $fallback): int { if ($raw === null) { return $fallback ? 1 : 0; } if (is_array($raw)) { $raw = end($raw); } return !empty($raw) ? 1 : 0; } public function sanitize_settings($input): array { $current = MoM_GastroComplete_Core::get_settings(); if (!is_array($input)) { return $current; } /* MoM speichert NICHT über options.php – nur admin-post/REST. Kein option_page-Check. */ $output = $current; // Sanitize each section - preserve existing values if not in input if (isset($input['online_orders']) && is_array($input['online_orders'])) { $output['online_orders'] = array_merge($output['online_orders'] ?? [], [ 'hours_format' => in_array($input['online_orders']['hours_format'] ?? '', ['12', '24'], true) ? $input['online_orders']['hours_format'] : ($output['online_orders']['hours_format'] ?? ''), 'allergens_url' => isset($input['online_orders']['allergens_url']) ? esc_url_raw($input['online_orders']['allergens_url']) : ($output['online_orders']['allergens_url'] ?? ''), 'checkout_labeling_enabled' => isset($input['online_orders']['checkout_labeling_enabled']) ? (!empty($input['online_orders']['checkout_labeling_enabled']) ? 1 : 0) : (int) ($output['online_orders']['checkout_labeling_enabled'] ?? 1), 'pickup_enabled' => isset($input['online_orders']['pickup_enabled']) ? (!empty($input['online_orders']['pickup_enabled']) ? 1 : 0) : ($output['online_orders']['pickup_enabled'] ?? 0), 'slot_minutes' => isset($input['online_orders']['slot_minutes']) ? max(5, min(120, (int)($input['online_orders']['slot_minutes'] ?? 15))) : ($output['online_orders']['slot_minutes'] ?? 15), 'category_limits' => isset($input['online_orders']['category_limits']) && is_array($input['online_orders']['category_limits']) ? array_map('intval', $input['online_orders']['category_limits']) : ($output['online_orders']['category_limits'] ?? []), 'closed_dates' => isset($input['online_orders']['closed_dates']) ? sanitize_textarea_field($input['online_orders']['closed_dates']) : ($output['online_orders']['closed_dates'] ?? ''), 'special_dates' => isset($input['online_orders']['special_dates']) ? sanitize_textarea_field($input['online_orders']['special_dates']) : ($output['online_orders']['special_dates'] ?? ''), 'preorder_enabled' => self::sanitize_preorder_flag( $input['online_orders']['preorder_enabled'] ?? null, (int)($output['online_orders']['preorder_enabled'] ?? 0) ), 'preorder_min_days' => isset($input['online_orders']['preorder_min_days']) ? max(1, min(14, (int)$input['online_orders']['preorder_min_days'])) : max(1, min(14, (int)($output['online_orders']['preorder_min_days'] ?? 1))), ]); // Hours if (isset($input['online_orders']['hours']) && is_array($input['online_orders']['hours'])) { foreach (['mon','tue','wed','thu','fri','sat','sun'] as $day) { if (isset($input['online_orders']['hours'][$day])) { $output['online_orders']['hours'][$day] = [ 'open' => !empty($input['online_orders']['hours'][$day]['open']) ? 1 : 0, 'l_from' => sanitize_text_field($input['online_orders']['hours'][$day]['l_from'] ?? ''), 'l_to' => sanitize_text_field($input['online_orders']['hours'][$day]['l_to'] ?? ''), 'e_from' => sanitize_text_field($input['online_orders']['hours'][$day]['e_from'] ?? ''), 'e_to' => sanitize_text_field($input['online_orders']['hours'][$day]['e_to'] ?? ''), ]; } } } } if (isset($input['website']) && is_array($input['website'])) { if (class_exists('GC_Frontend')) { $output['website'] = GC_Frontend::sanitize_website($input['website']); GC_Frontend::apply_website($output['website']); // receipt logo sync to TSE removed in Basic (Pro/Kasse only) } else { $output['website'] = array_merge($output['website'] ?? [], $input['website']); } } if (class_exists('GC_Frontend') && (isset($input['seo']) || isset($input['seo_json']))) { GC_Frontend::save_seo([ 'seo' => is_array($input['seo'] ?? null) ? $input['seo'] : [], 'seo_json' => is_string($input['seo_json'] ?? null) ? $input['seo_json'] : '', ]); $fresh = MoM_GastroComplete_Core::get_settings(); if (isset($fresh['website']) && is_array($fresh['website'])) { $output['website'] = $fresh['website']; } } if (isset($input['timeslots']) && is_array($input['timeslots'])) { $time_mode = isset($input['timeslots']['time_mode']) ? (class_exists('MoM_GastroComplete_Time_Confirmation') ? MoM_GastroComplete_Time_Confirmation::normalize_mode($input['timeslots']['time_mode']) : sanitize_text_field((string)$input['timeslots']['time_mode'])) : ($output['timeslots']['time_mode'] ?? 'slots'); $output['timeslots'] = array_merge($output['timeslots'] ?? [], [ 'category_limits_enabled' => isset($input['timeslots']['category_limits_enabled']) ? (!empty($input['timeslots']['category_limits_enabled']) ? 1 : 0) : ($output['timeslots']['category_limits_enabled'] ?? 1), 'slot_minutes' => isset($input['timeslots']['slot_minutes']) ? max(5, min(120, (int)($input['timeslots']['slot_minutes'] ?? 15))) : ($output['timeslots']['slot_minutes'] ?? 15), 'time_mode' => $time_mode, ]); // Also handle category_limits if moved to timeslots if (isset($input['timeslots']['category_limits']) && is_array($input['timeslots']['category_limits'])) { $output['timeslots']['category_limits'] = array_map('intval', $input['timeslots']['category_limits']); } elseif (isset($output['online_orders']['category_limits'])) { // Preserve from online_orders if exists $output['timeslots']['category_limits'] = $output['online_orders']['category_limits']; } } if (isset($input['reservations']) && is_array($input['reservations'])) { $output['reservations']['enabled'] = !empty($input['reservations']['enabled']) ? 1 : 0; } if (isset($input['delivery']) && is_array($input['delivery'])) { $excluded_products = []; if (isset($input['delivery']['min_order_excluded_products']) && is_array($input['delivery']['min_order_excluded_products'])) { $excluded_products = array_map('intval', array_filter($input['delivery']['min_order_excluded_products'])); } $d = $input['delivery']; $default_service = sanitize_key((string) self::mom_safe_scalar($d['default_service'] ?? ($output['delivery']['default_service'] ?? 'pickup'))); if (!in_array($default_service, ['pickup', 'delivery'], true)) { $default_service = 'pickup'; } $output['delivery'] = array_merge($output['delivery'], [ 'enabled' => !empty($d['enabled']) ? 1 : 0, 'default_service' => $default_service, 'checkout_page_id' => max(0, (int)(self::mom_safe_scalar($d['checkout_page_id'] ?? 0))), 'min_order_amount' => !empty($d['min_order_amount']) ? (float)self::mom_safe_scalar($d['min_order_amount']) : '', 'min_order_excluded_products' => $excluded_products, 'google_places_api_key' => sanitize_text_field(self::mom_safe_scalar($d['google_places_api_key'] ?? '')), 'google_places_enabled' => !empty($d['google_places_enabled']) ? 1 : 0, 'fee_mode' => in_array(($d['fee_mode'] ?? 'none'), ['none', 'free', 'flat', 'zones'], true) ? $d['fee_mode'] : 'none', 'fee_amount' => sanitize_text_field(self::mom_safe_scalar($d['fee_amount'] ?? '')), 'free_above' => sanitize_text_field(self::mom_safe_scalar($d['free_above'] ?? '')), 'fee_configured' => 1, 'postcode_whitelist' => sanitize_textarea_field(self::mom_safe_scalar($d['postcode_whitelist'] ?? '')), 'postcode_ranges' => sanitize_textarea_field(self::mom_safe_scalar($d['postcode_ranges'] ?? '')), 'postcode_prefixes' => sanitize_textarea_field(self::mom_safe_scalar($d['postcode_prefixes'] ?? '')), 'checkout_info_text' => wp_kses_post(self::mom_safe_scalar($d['checkout_info_text'] ?? '')), ]); } if(isset($input['delivery'])) { $zones=GC_Delivery_Fees::validate_zones($input['delivery']['fee_zones']??($output['delivery']['fee_zones']??[])); if(is_wp_error($zones)||(($output['delivery']['fee_mode']??'')==='zones'&&!$zones)) { // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Fehler beim Speichern wird abgefangen; Redirect-Parameter wird beim Anzeigen mit sanitize_text_field und esc_html behandelt. throw new InvalidArgumentException(is_wp_error($zones)?$zones->get_error_message():__('Bitte mindestens ein Liefergebiet anlegen.', 'restocommerce')); } else $output['delivery']['fee_zones']=$zones; } if (isset($input['mail']) && is_array($input['mail']) && class_exists('GC_Mail_Settings')) { $output['mail'] = GC_Mail_Settings::sanitize($input['mail'], $output['mail'] ?? []); } if (isset($input['inventory']) && is_array($input['inventory'])) { $inv_in = $input['inventory']; $output['inventory'] = class_exists('MoM_GastroComplete_Inventory') ? MoM_GastroComplete_Inventory::sanitize_settings($inv_in) : [ 'enabled' => !empty($inv_in['enabled']) ? 1 : 0, 'show_in_shop' => !empty($inv_in['show_in_shop']) ? 1 : 0, ]; if (class_exists('MoM_GastroComplete_Inventory')) { MoM_GastroComplete_Inventory::apply_category_input($inv_in); $sync = MoM_GastroComplete_Inventory::current_master_map(); $output['online_orders']['category_master_products'] = $sync['masters']; } } if (isset($input['debug']) && is_array($input['debug'])) { $output['debug']['enabled'] = !empty($input['debug']['enabled']) ? 1 : 0; } // Update settings version $output['settings_version'] = MOM_GASTROKOMPLETE_VERSION; // Sync back to legacy options for backward compatibility $this->sync_to_legacy_options($output); return $output; } /** * Sync settings back to legacy options for backward compatibility */ private function sync_to_legacy_options(array $settings): void { // Sync to fimui_settings (for backward compatibility) if (isset($settings['online_orders'])) { // Get slot_minutes from timeslots or fallback to online_orders $slot_minutes = $settings['timeslots']['slot_minutes'] ?? $settings['online_orders']['slot_minutes'] ?? 15; update_option('fimui_settings', [ 'allergens_url' => $settings['online_orders']['allergens_url'] ?? '', 'checkout_labeling_enabled' => array_key_exists('checkout_labeling_enabled', $settings['online_orders']) ? (!empty($settings['online_orders']['checkout_labeling_enabled']) ? 1 : 0) : 1, 'pickup_enabled' => $settings['online_orders']['pickup_enabled'] ?? 0, 'slot_minutes' => $slot_minutes, 'category_limits' => $settings['online_orders']['category_limits'] ?? [], 'category_master_products' => $settings['online_orders']['category_master_products'] ?? [], 'hours' => $settings['online_orders']['hours'] ?? [], 'closed_dates' => $settings['online_orders']['closed_dates'] ?? '', 'special_dates' => $settings['online_orders']['special_dates'] ?? '', 'preorder_enabled' => array_key_exists('preorder_enabled', $settings['online_orders']) ? (!empty($settings['online_orders']['preorder_enabled']) ? 1 : 0) : 0, 'preorder_min_days' => max(1, min(14, (int)($settings['online_orders']['preorder_min_days'] ?? 1))), ]); } } /** * Render Zahlungsarten page */ public function render_payment_methods_page(): void { if (!current_user_can('manage_woocommerce')) { wp_die('Unauthorized'); } ?>

RestoCommerce Pro' ), ['a' => ['href' => true]] ); ?>