400 lines
16 KiB
PHP
400 lines
16 KiB
PHP
<?php
|
|
/**
|
|
* Admin2000 - Modernes Admin-System
|
|
*
|
|
* Komplett neues, paralleles Admin-System mit:
|
|
* - Modernem CSS Layout
|
|
* - Responsive Design
|
|
* - Sauberer Code-Struktur
|
|
* - Keine Inline-Styles
|
|
*
|
|
* @package Admin2000
|
|
* @author Claude & Team
|
|
* @version 1.0.0
|
|
*/
|
|
|
|
class admin2000 {
|
|
private $base_object;
|
|
private $layout_object;
|
|
|
|
function __construct($base_object, $layout_object) {
|
|
$this->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 "
|
|
<div style='padding: 40px; text-align: center; background: #f8f9fa; border-radius: 8px; margin: 20px;'>
|
|
<h2 style='color: #333; margin-bottom: 20px;'>🚧 In Entwicklung</h2>
|
|
<p style='color: #666; font-size: 16px; margin-bottom: 30px;'>
|
|
Diese Seite wird gerade entwickelt.
|
|
</p>
|
|
<div style='background: white; padding: 20px; border-radius: 6px; display: inline-block;'>
|
|
<strong>Section:</strong> {$section}<br>
|
|
<strong>Page:</strong> {$page}<br>
|
|
<strong>Template:</strong> admin2000/{$section}_{$page}.tpl
|
|
</div>
|
|
</div>
|
|
";
|
|
}
|
|
|
|
/**
|
|
* 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 = '<tr><td colspan="100" style="text-align: center; padding: 40px; color: #e74c3c;">';
|
|
$error_html .= '<strong>Fehler beim Laden der Daten</strong><br>';
|
|
$error_html .= htmlspecialchars($e->getMessage());
|
|
$error_html .= '</td></tr>';
|
|
return $error_html;
|
|
}
|
|
}
|
|
|
|
return json_encode(array('error' => 'Unknown page'));
|
|
}
|
|
}
|