base_object = $base_object; $this->layout_object = $layout_object; } function run() { $debug_file = './admin2000_debug.log'; file_put_contents($debug_file, "\n=== RUN() START " . date('Y-m-d H:i:s') . " ===\n", FILE_APPEND); file_put_contents($debug_file, "REQUEST_METHOD: " . $_SERVER['REQUEST_METHOD'] . "\n", FILE_APPEND); file_put_contents($debug_file, "GET: " . print_r($_GET, true) . "\n", FILE_APPEND); file_put_contents($debug_file, "POST keys: " . print_r(array_keys($_POST), true) . "\n", FILE_APPEND); // Get section and page from URL $section = isset($_GET['section']) ? $_GET['section'] : 'stammdaten'; $page = isset($_GET['page']) ? $_GET['page'] : 'kunden'; $action = isset($_GET['action']) ? $_GET['action'] : ''; file_put_contents($debug_file, "Section: $section, Page: $page, Action: $action\n", FILE_APPEND); file_put_contents($debug_file, "POST action: " . (isset($_POST['action']) ? $_POST['action'] : 'NONE') . "\n", FILE_APPEND); // Handle POST save action if (isset($_POST['action']) && $_POST['action'] == 'save' && $page == 'kunden_editor') { file_put_contents($debug_file, "Routing to save_customer()\n", FILE_APPEND); return $this->save_customer(); } // Handle AJAX requests (return partial HTML only) if ($action == 'get_list_items') { return $this->get_list_items($section, $page); } // Build navigation data $navigation = $this->build_navigation($section, $page); $this->layout_object->assign('admin2000_nav', $navigation); $this->layout_object->assign('admin2000_section', $section); $this->layout_object->assign('admin2000_page', $page); // Pass admin user data to template $this->layout_object->assign('admin_user', $this->base_object->customer); // Load page content $content = $this->load_page_content($section, $page); $this->layout_object->assign('admin2000_content', $content); // Load FULL standalone HTML layout (not wrapped in main.tpl) return $this->layout_object->fetch('admin2000_layout.tpl'); } /** * Build navigation structure */ private function build_navigation($active_section, $active_page) { $nav = array( 'stammdaten' => array( 'title' => 'Stammdaten', 'icon' => '📊', 'pages' => array( 'kunden' => array( 'title' => 'Kunden', 'url' => './index.php?admin_modul=admin2000§ion=stammdaten&page=kunden', 'icon' => '👥', 'active' => ($active_section == 'stammdaten' && $active_page == 'kunden') ), 'artikel' => array( 'title' => 'Artikel', 'url' => './index.php?admin_modul=admin2000§ion=stammdaten&page=artikel', 'icon' => '📦', 'active' => ($active_section == 'stammdaten' && $active_page == 'artikel') ), 'lieferanten' => array( 'title' => 'Lieferanten', 'url' => './index.php?admin_modul=admin2000§ion=stammdaten&page=lieferanten', 'icon' => '🚚', 'active' => ($active_section == 'stammdaten' && $active_page == 'lieferanten') ) ) ) ); return $nav; } /** * Load page content based on section and page */ private function load_page_content($section, $page) { // Load data for specific pages if ($section == 'stammdaten' && $page == 'kunden') { $this->load_customer_data(); } elseif ($section == 'stammdaten' && $page == 'kunden_editor') { $this->load_customer_editor_data(); } // Build template path $template_file = "admin2000/{$section}_{$page}.tpl"; // Check if template exists $template_path = ROOT_DIR . "/themes/admin/templates/{$template_file}"; if (!file_exists($template_path)) { // Return placeholder if template doesn't exist yet return $this->get_placeholder_content($section, $page); } // Load and return template return $this->layout_object->fetch($template_file); } /** * Get placeholder content for pages that don't exist yet */ private function get_placeholder_content($section, $page) { return "

🚧 In Entwicklung

Diese Seite wird gerade entwickelt.

Section: {$section}
Page: {$page}
Template: admin2000/{$section}_{$page}.tpl
"; } /** * Load customer data for the list */ private function load_customer_data() { include_once './core/customer.class.php'; $customer = new Customer($this->base_object); // Pass table config to template $table_data = $customer->list_table_config; $table_data['list_filter'] = $customer->get_filter(); // Get customer statistics $stats = $this->get_customer_stats(); $this->layout_object->assign('table_data', $table_data); $this->layout_object->assign('customer_stats', $stats); } /** * Get customer statistics for dashboard cards */ private function get_customer_stats() { $db = $this->base_object->db; $stats = array(); // Total customers $sql = "SELECT COUNT(*) as total FROM customers"; $result = $db->query($sql); $row = $result->fetch_assoc(); $stats['total'] = $row['total']; // Active customers (locked = 0 means active) $sql = "SELECT COUNT(*) as active FROM customers WHERE locked = 0"; $result = $db->query($sql); $row = $result->fetch_assoc(); $stats['active'] = $row['active']; // Locked/Disabled customers $sql = "SELECT COUNT(*) as locked FROM customers WHERE locked = 1"; $result = $db->query($sql); $row = $result->fetch_assoc(); $stats['locked'] = $row['locked']; // New customers in last 30 days $sql = "SELECT COUNT(*) as new_30days FROM customers WHERE registration_date >= DATE_SUB(NOW(), INTERVAL 30 DAY)"; $result = $db->query($sql); $row = $result->fetch_assoc(); $stats['new_30days'] = $row['new_30days']; return $stats; } /** * Load customer editor data - WITH INJECTION PROTECTION! */ private function load_customer_editor_data() { include_once './core/customer.class.php'; include_once './core/customeraddress.class.php'; include_once './core/order.class.php'; // SECURITY: Validate and sanitize customer ID $customer_id = isset($_GET['id']) ? intval($_GET['id']) : 0; if ($customer_id <= 0) { $this->layout_object->assign('error', 'Ungültige Kunden-ID'); return; } $customer = new Customer($this->base_object); // Check if customer exists if (!Customer::has_id($customer_id)) { $this->layout_object->assign('error', 'Kunde nicht gefunden'); return; } // Load customer data - using get_data() for FULL legacy compatibility // (includes group_name, show_tax via LEFT JOIN customer_groups) $customer_data = $customer->get_data($customer_id); // Convert OBJECT to ARRAY for Smarty template compatibility $customer_data = (array) $customer_data; // Load customer addresses $customer_address_object = new CustomerAddress($this->base_object); $customer_addresses = $customer_address_object->get_data_by_customer_id($customer_id); // Convert addresses to array if needed if ($customer_addresses && is_array($customer_addresses)) { $addresses_array = array(); foreach ($customer_addresses as $addr) { $addresses_array[] = (array) $addr; } $customer_addresses = $addresses_array; } // Load customer orders (Bestellungen/Rechnungen) $orders = OrderHelper::get_all_customer_orders($customer_id); // Convert orders to array if needed if ($orders && is_array($orders)) { $orders_array = array(); foreach ($orders as $order) { $orders_array[] = (array) $order; } $orders = $orders_array; } // Get form field configuration from customer class $form_config = $customer->list_table_config['edit_fields']; // Load select options for dropdowns $select_values = array(); foreach ($form_config as $field) { if (isset($field['values']) && is_string($field['values'])) { // Call the function to get values (e.g. customer_group_values()) $select_values[$field['db_field']] = $customer->{$field['values']}(); } } // Check if save was successful $save_success = isset($_GET['saved']) && $_GET['saved'] == '1'; // Pass to template $this->layout_object->assign('customer_id', $customer_id); $this->layout_object->assign('customer_data', $customer_data); $this->layout_object->assign('customer_addresses', $customer_addresses); $this->layout_object->assign('orders', $orders); $this->layout_object->assign('form_config', $form_config); $this->layout_object->assign('select_values', $select_values); $this->layout_object->assign('save_success', $save_success); } /** * Save customer data - WITH INJECTION PROTECTION! */ private function save_customer() { // DEBUG: Write to file $debug_file = './admin2000_debug.log'; file_put_contents($debug_file, "=== SAVE_CUSTOMER START " . date('Y-m-d H:i:s') . " ===\n", FILE_APPEND); file_put_contents($debug_file, "POST data: " . print_r($_POST, true) . "\n", FILE_APPEND); include_once './core/customer.class.php'; // SECURITY: Validate customer ID $customer_id = isset($_POST['customer_id']) ? intval($_POST['customer_id']) : 0; file_put_contents($debug_file, "Customer ID: " . $customer_id . "\n", FILE_APPEND); if ($customer_id <= 0) { file_put_contents($debug_file, "ERROR: Invalid customer ID\n", FILE_APPEND); die('Ungültige Kunden-ID'); } $customer = new Customer($this->base_object); file_put_contents($debug_file, "Customer object created\n", FILE_APPEND); // Check if customer exists if (!Customer::has_id($customer_id)) { file_put_contents($debug_file, "ERROR: Customer not found\n", FILE_APPEND); die('Kunde nicht gefunden'); } file_put_contents($debug_file, "Customer exists check passed\n", FILE_APPEND); // SECURITY: Sanitize all input data // Legacy uses 'customer_field' not 'customer_data'! $customer_data = isset($_POST['customer_field']) ? $_POST['customer_field'] : array(); file_put_contents($debug_file, "customer_field data: " . print_r($customer_data, true) . "\n", FILE_APPEND); $sanitized_data = array(); foreach ($customer_data as $key => $value) { // Only allow alphanumeric field names (prevent injection) if (preg_match('/^[a-zA-Z0-9_]+$/', $key)) { // CRITICAL: Don't send empty password (would create md5('') hash!) if ($key === 'pass' && trim($value) === '') { continue; } // Sanitize value based on type if (is_array($value)) { $sanitized_data[$key] = array_map('strip_tags', $value); } else { $sanitized_data[$key] = strip_tags($value); } } } // Add customer ID to data $sanitized_data['id'] = $customer_id; file_put_contents($debug_file, "Sanitized data: " . print_r($sanitized_data, true) . "\n", FILE_APPEND); // Save using legacy customer class (has its own security) file_put_contents($debug_file, "Calling customer->save()\n", FILE_APPEND); $result = $customer->save($sanitized_data, $customer_id); file_put_contents($debug_file, "Save result: " . ($result ? 'SUCCESS' : 'FAILED') . "\n", FILE_APPEND); if ($result) { // Redirect back to editor with success message file_put_contents($debug_file, "Redirecting to editor\n", FILE_APPEND); header('Location: ./index.php?admin_modul=admin2000§ion=stammdaten&page=kunden_editor&id=' . $customer_id . '&saved=1'); exit(); } else { file_put_contents($debug_file, "ERROR: Save failed\n", FILE_APPEND); die('Fehler beim Speichern der Daten'); } } /** * AJAX handler to get list items */ private function get_list_items($section, $page) { if ($section == 'stammdaten' && $page == 'kunden') { include_once './core/customer.class.php'; try { $customer = new Customer($this->base_object); // Get settings from request $max_list_items = isset($_GET['max_list_items']) ? (int)$_GET['max_list_items'] : 20; $actual_page = isset($_GET['actual_page']) ? (int)$_GET['actual_page'] : 1; $sort_item = isset($_GET['sort_item']) ? $_GET['sort_item'] : 'number'; $sort_direction = isset($_GET['sort_direction']) ? $_GET['sort_direction'] : 'up'; $search_string = isset($_GET['search_string']) ? $_GET['search_string'] : ''; $list_filter = isset($_GET['list_filter']) && is_array($_GET['list_filter']) ? $_GET['list_filter'] : array(); $setting = array( 'max_list_items' => $max_list_items, 'actual_page' => $actual_page, 'sort_item' => $sort_item, 'sort_direction' => $sort_direction, 'search_string' => $search_string, 'list_filter' => $list_filter, 'data_format' => 1 ); $list_data = $customer->get_list_items($setting); $list_data['list_table_config'] = $customer->list_table_config; $this->layout_object->assign('list_data', $list_data); return $this->layout_object->fetch('admin2000_list_items.tpl'); } catch (Exception $e) { // Error handling $error_html = ''; $error_html .= 'Fehler beim Laden der Daten
'; $error_html .= htmlspecialchars($e->getMessage()); $error_html .= ''; return $error_html; } } return json_encode(array('error' => 'Unknown page')); } }