瀏覽代碼

Admin translations and user management

svalavuo 6 天之前
父節點
當前提交
a3ea956fcc
共有 5 個文件被更改,包括 492 次插入8 次删除
  1. 162 8
      admin/users.php
  2. 219 0
      css/admin-users.css
  3. 15 0
      database_migrations/add_user_roles_status.sql
  4. 48 0
      languages/en.php
  5. 48 0
      languages/fi.php

+ 162 - 8
admin/users.php

@@ -33,18 +33,45 @@ $db = Database::getInstance();
 
 switch ($action) {
     case 'list':
-        // Get all users with pagination
+        // Get filter parameters
+        $search = trim($_GET['search'] ?? '');
+        $role_filter = $_GET['role'] ?? 'all';
+        $status_filter = $_GET['status'] ?? 'all';
         $page = max(1, (int)($_GET['page'] ?? 1));
         $limit = 20;
         $offset = ($page - 1) * $limit;
         
-        // Get total users count
-        $total_users = $db->fetch("SELECT COUNT(*) as count FROM users")['count'];
+        // Build WHERE clause for filtering
+        $where_conditions = [];
+        $params = [];
+        
+        if (!empty($search)) {
+            $where_conditions[] = "(username LIKE ? OR email LIKE ?)";
+            $params[] = "%$search%";
+            $params[] = "%$search%";
+        }
+        
+        if ($role_filter !== 'all') {
+            $where_conditions[] = "role = ?";
+            $params[] = $role_filter;
+        }
+        
+        if ($status_filter !== 'all') {
+            $where_conditions[] = "status = ?";
+            $params[] = $status_filter;
+        }
+        
+        $where_clause = !empty($where_conditions) ? "WHERE " . implode(" AND ", $where_conditions) : "";
+        
+        // Get total users count with filters
+        $count_sql = "SELECT COUNT(*) as count FROM users $where_clause";
+        $total_users = $db->fetch($count_sql, $params)['count'];
         $total_pages = ceil($total_users / $limit);
         
-        // Get users for current page
-        $sql = "SELECT id, username, email, auth_type, created_at, last_login FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?";
-        $users = $db->fetchAll($sql, [$limit, $offset]);
+        // Get users for current page with filters
+        $sql = "SELECT id, username, email, auth_type, role, status, created_at, last_login FROM users $where_clause ORDER BY created_at DESC LIMIT ? OFFSET ?";
+        $query_params = array_merge($params, [$limit, $offset]);
+        $users = $db->fetchAll($sql, $query_params);
         
         break;
         
@@ -54,6 +81,52 @@ switch ($action) {
             exit;
         }
         
+        // Handle form submission for user update
+        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+            $username = trim($_POST['username'] ?? '');
+            $email = trim($_POST['email'] ?? '');
+            $auth_type = trim($_POST['auth_type'] ?? 'local');
+            $role = trim($_POST['role'] ?? 'user');
+            $status = trim($_POST['status'] ?? 'active');
+            $password = trim($_POST['password'] ?? '');
+            
+            // Validation
+            if (empty($username)) $error = t('admin_username_required');
+            elseif (empty($email)) $error = t('admin_email_required');
+            elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) $error = t('admin_email_invalid');
+            
+            if (!$error) {
+                try {
+                    $update_data = [
+                        'username' => $username,
+                        'email' => $email,
+                        'auth_type' => $auth_type,
+                        'role' => $role,
+                        'status' => $status
+                    ];
+                    
+                    // Update password if provided
+                    if (!empty($password)) {
+                        $update_data['password'] = password_hash($password, PASSWORD_DEFAULT);
+                    }
+                    
+                    $db->update('users', $update_data, 'id = ?', [$user_id]);
+                    $message = t('admin_user_updated_success');
+                    
+                    // Redirect to avoid form resubmission
+                    header('Location: users.php?message=' . urlencode($message));
+                    exit;
+                    
+                } catch (Exception $e) {
+                    if (strpos($e->getMessage(), 'Duplicate') !== false) {
+                        $error = t('admin_username_exists');
+                    } else {
+                        $error = t('admin_user_update_error') . ' ' . $e->getMessage();
+                    }
+                }
+            }
+        }
+        
         // Get user details
         $user = $db->fetch("SELECT * FROM users WHERE id = ?", [$user_id]);
         if (!$user) {
@@ -97,6 +170,10 @@ switch ($action) {
             elseif (empty($password) && $auth_type === 'local') $error = t('admin_password_required_local');
             elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) $error = t('admin_email_invalid');
             
+            // Get role and status from form
+            $role = trim($_POST['role'] ?? 'user');
+            $status = trim($_POST['status'] ?? 'active');
+            
             if (!$error) {
                 // Check if username already exists
                 $existing_user = $db->fetch("SELECT id FROM users WHERE username = ?", [$username]);
@@ -105,8 +182,8 @@ switch ($action) {
                 } else {
                     // Create new user
                     $hashed_password = password_hash($password, PASSWORD_DEFAULT);
-                    $sql = "INSERT INTO users (username, email, password, auth_type, created_at) VALUES (?, ?, ?, ?, NOW())";
-                    $db->query($sql, [$username, $email, $hashed_password, $auth_type]);
+                    $sql = "INSERT INTO users (username, email, password, auth_type, role, status, created_at) VALUES (?, ?, ?, ?, ?, ?, NOW())";
+                    $db->query($sql, [$username, $email, $hashed_password, $auth_type, $role, $status]);
                     
                     $message = t('admin_user_created_success');
                     
@@ -132,6 +209,7 @@ switch ($action) {
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title><?php echo t('manage_users'); ?> - <?php echo SITE_TITLE; ?></title>
     <link rel="stylesheet" href="../css/style.css">
+    <link rel="stylesheet" href="../css/admin-users.css">
 </head>
 <body>
     <header class="admin-header">
@@ -174,6 +252,38 @@ switch ($action) {
                         </div>
                     </div>
                     
+                    <div class="filters">
+                        <form method="get" class="filter-form">
+                            <div class="filter-group">
+                                <label for="search"><?php echo t('search'); ?>:</label>
+                                <input type="text" id="search" name="search" value="<?php echo htmlspecialchars($search); ?>" placeholder="<?php echo t('search_users_placeholder'); ?>">
+                            </div>
+                            
+                            <div class="filter-group">
+                                <label for="role"><?php echo t('role'); ?>:</label>
+                                <select id="role" name="role">
+                                    <option value="all" <?php echo $role_filter === 'all' ? 'selected' : ''; ?>><?php echo t('filter_all'); ?></option>
+                                    <option value="admin" <?php echo $role_filter === 'admin' ? 'selected' : ''; ?>><?php echo t('role_admin'); ?></option>
+                                    <option value="user" <?php echo $role_filter === 'user' ? 'selected' : ''; ?>><?php echo t('role_user'); ?></option>
+                                </select>
+                            </div>
+                            
+                            <div class="filter-group">
+                                <label for="status"><?php echo t('status'); ?>:</label>
+                                <select id="status" name="status">
+                                    <option value="all" <?php echo $status_filter === 'all' ? 'selected' : ''; ?>><?php echo t('filter_all'); ?></option>
+                                    <option value="active" <?php echo $status_filter === 'active' ? 'selected' : ''; ?>><?php echo t('status_active'); ?></option>
+                                    <option value="inactive" <?php echo $status_filter === 'inactive' ? 'selected' : ''; ?>><?php echo t('status_inactive'); ?></option>
+                                </select>
+                            </div>
+                            
+                            <button type="submit" class="btn btn-sm"><?php echo t('filter'); ?></button>
+                            <?php if ($search || $role_filter !== 'all' || $status_filter !== 'all'): ?>
+                                <a href="users.php" class="btn btn-sm btn-secondary"><?php echo t('clear'); ?></a>
+                            <?php endif; ?>
+                        </form>
+                    </div>
+                    
                     <div class="users-table">
                         <table class="admin-table">
                             <thead>
@@ -181,6 +291,8 @@ switch ($action) {
                                     <th><?php echo t('username'); ?></th>
                                     <th><?php echo t('email'); ?></th>
                                     <th><?php echo t('auth_type'); ?></th>
+                                    <th><?php echo t('role'); ?></th>
+                                    <th><?php echo t('status'); ?></th>
                                     <th><?php echo t('created'); ?></th>
                                     <th><?php echo t('last_login'); ?></th>
                                     <th><?php echo t('actions'); ?></th>
@@ -196,6 +308,16 @@ switch ($action) {
                                                 <?php echo ucfirst($user['auth_type']); ?>
                                             </span>
                                         </td>
+                                        <td>
+                                            <span class="role-badge role-<?php echo $user['role']; ?>">
+                                                <?php echo t('role_' . $user['role']); ?>
+                                            </span>
+                                        </td>
+                                        <td>
+                                            <span class="status-badge status-<?php echo $user['status']; ?>">
+                                                <?php echo t('status_' . $user['status']); ?>
+                                            </span>
+                                        </td>
                                         <td><?php echo date('Y-m-d H:i', strtotime($user['created_at'])); ?></td>
                                         <td><?php echo $user['last_login'] ? date('Y-m-d H:i', strtotime($user['last_login'])) : 'Never'; ?></td>
                                         <td>
@@ -252,6 +374,22 @@ switch ($action) {
                             </select>
                         </div>
                         
+                        <div class="form-group">
+                            <label for="role"><?php echo t('role'); ?>:</label>
+                            <select id="role" name="role">
+                                <option value="user" <?php echo ($user['role'] ?? 'user') === 'user' ? 'selected' : ''; ?>><?php echo t('role_user'); ?></option>
+                                <option value="admin" <?php echo ($user['role'] ?? 'user') === 'admin' ? 'selected' : ''; ?>><?php echo t('role_admin'); ?></option>
+                            </select>
+                        </div>
+                        
+                        <div class="form-group">
+                            <label for="status"><?php echo t('status'); ?>:</label>
+                            <select id="status" name="status">
+                                <option value="active" <?php echo ($user['status'] ?? 'active') === 'active' ? 'selected' : ''; ?>><?php echo t('status_active'); ?></option>
+                                <option value="inactive" <?php echo ($user['status'] ?? 'active') === 'inactive' ? 'selected' : ''; ?>><?php echo t('status_inactive'); ?></option>
+                            </select>
+                        </div>
+                        
                         <div class="form-actions">
                             <button type="submit" class="btn btn-primary"><?php echo t('update'); ?></button>
                             <a href="users.php" class="btn"><?php echo t('cancel'); ?></a>
@@ -291,6 +429,22 @@ switch ($action) {
                             </select>
                         </div>
                         
+                        <div class="form-group">
+                            <label for="role"><?php echo t('role'); ?>:</label>
+                            <select id="role" name="role">
+                                <option value="user"><?php echo t('role_user'); ?></option>
+                                <option value="admin"><?php echo t('role_admin'); ?></option>
+                            </select>
+                        </div>
+                        
+                        <div class="form-group">
+                            <label for="status"><?php echo t('status'); ?>:</label>
+                            <select id="status" name="status">
+                                <option value="active"><?php echo t('status_active'); ?></option>
+                                <option value="inactive"><?php echo t('status_inactive'); ?></option>
+                            </select>
+                        </div>
+                        
                         <div class="form-actions">
                             <button type="submit" class="btn btn-primary"><?php echo t('create'); ?></button>
                             <a href="users.php" class="btn"><?php echo t('cancel'); ?></a>

+ 219 - 0
css/admin-users.css

@@ -0,0 +1,219 @@
+/* User Management Styles */
+
+.filters {
+    background-color: #f9fafb;
+    border: 1px solid #e5e7eb;
+    border-radius: 8px;
+    padding: 20px;
+    margin-bottom: 20px;
+}
+
+.filter-form {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 15px;
+    align-items: end;
+}
+
+.filter-group {
+    flex: 1;
+    min-width: 150px;
+}
+
+.filter-group label {
+    display: block;
+    margin-bottom: 5px;
+    font-weight: 600;
+    color: #374151;
+    font-size: 14px;
+}
+
+.filter-group input,
+.filter-group select {
+    width: 100%;
+    padding: 8px 12px;
+    border: 1px solid #d1d5db;
+    border-radius: 6px;
+    font-size: 14px;
+    transition: border-color 0.2s;
+}
+
+.filter-group input:focus,
+.filter-group select:focus {
+    outline: none;
+    border-color: #3b82f6;
+    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
+}
+
+.role-badge, .status-badge {
+    display: inline-block;
+    padding: 4px 8px;
+    border-radius: 12px;
+    font-size: 12px;
+    font-weight: 600;
+    text-transform: uppercase;
+    letter-spacing: 0.5px;
+}
+
+.role-badge {
+    background-color: #f3f4f6;
+    color: #374151;
+}
+
+.role-badge.role-admin {
+    background-color: #dc2626;
+    color: white;
+}
+
+.role-badge.role-user {
+    background-color: #3b82f6;
+    color: white;
+}
+
+.status-badge {
+    background-color: #f3f4f6;
+    color: #374151;
+}
+
+.status-badge.status-active {
+    background-color: #10b981;
+    color: white;
+}
+
+.status-badge.status-inactive {
+    background-color: #6b7280;
+    color: white;
+}
+
+.auth-type {
+    display: inline-block;
+    padding: 4px 8px;
+    border-radius: 12px;
+    font-size: 12px;
+    font-weight: 600;
+    text-transform: uppercase;
+    letter-spacing: 0.5px;
+}
+
+.auth-type.local {
+    background-color: #8b5cf6;
+    color: white;
+}
+
+.auth-type.ldap {
+    background-color: #f59e0b;
+    color: white;
+}
+
+.users-table .admin-table th:nth-child(4),
+.users-table .admin-table th:nth-child(5) {
+    width: 80px;
+    text-align: center;
+}
+
+.users-table .admin-table td:nth-child(4),
+.users-table .admin-table td:nth-child(5) {
+    text-align: center;
+}
+
+/* Form improvements */
+.admin-form .form-group {
+    margin-bottom: 20px;
+}
+
+.admin-form label {
+    display: block;
+    margin-bottom: 5px;
+    font-weight: 600;
+    color: #374151;
+}
+
+.admin-form input[type="text"],
+.admin-form input[type="email"],
+.admin-form input[type="password"],
+.admin-form select {
+    width: 100%;
+    padding: 10px;
+    border: 1px solid #d1d5db;
+    border-radius: 6px;
+    font-size: 14px;
+    transition: border-color 0.2s;
+}
+
+.admin-form input:focus,
+.admin-form select:focus {
+    outline: none;
+    border-color: #3b82f6;
+    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
+}
+
+.form-actions {
+    margin-top: 30px;
+    display: flex;
+    gap: 10px;
+}
+
+.btn {
+    padding: 10px 20px;
+    border: none;
+    border-radius: 6px;
+    font-size: 14px;
+    font-weight: 600;
+    cursor: pointer;
+    transition: all 0.2s;
+    text-decoration: none;
+    display: inline-block;
+}
+
+.btn-primary {
+    background-color: #3b82f6;
+    color: white;
+}
+
+.btn-primary:hover {
+    background-color: #2563eb;
+}
+
+.btn-secondary {
+    background-color: #6b7280;
+    color: white;
+}
+
+.btn-secondary:hover {
+    background-color: #4b5563;
+}
+
+.btn-danger {
+    background-color: #dc2626;
+    color: white;
+}
+
+.btn-danger:hover {
+    background-color: #b91c1c;
+}
+
+.btn-sm {
+    padding: 6px 12px;
+    font-size: 12px;
+}
+
+/* Responsive design */
+@media (max-width: 768px) {
+    .users-table .admin-table {
+        font-size: 12px;
+    }
+    
+    .role-badge, .status-badge, .auth-type {
+        font-size: 10px;
+        padding: 2px 6px;
+    }
+    
+    .form-actions {
+        flex-direction: column;
+    }
+    
+    .btn {
+        width: 100%;
+        text-align: center;
+    }
+}

+ 15 - 0
database_migrations/add_user_roles_status.sql

@@ -0,0 +1,15 @@
+-- Add user role and status management
+-- Run this migration to enhance the users table
+
+ALTER TABLE users 
+ADD COLUMN role ENUM('admin', 'user') NOT NULL DEFAULT 'user' AFTER auth_type,
+ADD COLUMN status ENUM('active', 'inactive') NOT NULL DEFAULT 'active' AFTER role,
+ADD COLUMN updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP AFTER created_at;
+
+-- Create index for better performance
+CREATE INDEX idx_users_role ON users(role);
+CREATE INDEX idx_users_status ON users(status);
+
+-- Update existing users to have default role and status
+UPDATE users SET role = 'admin' WHERE id = 1; -- Make first user admin
+UPDATE users SET status = 'active' WHERE status IS NULL;

+ 48 - 0
languages/en.php

@@ -268,4 +268,52 @@ return [
     'admin_category_created_success' => 'Category created successfully',
     'admin_category_name_exists' => 'Category name already exists',
     'admin_category_save_error' => 'Error saving category',
+    
+    // User Management
+    'manage_users' => 'Manage Users',
+    'create_user' => 'Create User',
+    'username' => 'Username',
+    'email' => 'Email',
+    'auth_type' => 'Auth Type',
+    'created' => 'Created',
+    'last_login' => 'Last Login',
+    'actions' => 'Actions',
+    'edit' => 'Edit',
+    'delete' => 'Delete',
+    'delete_user_confirm' => 'Are you sure you want to delete this user?',
+    'previous' => 'Previous',
+    'next' => 'Next',
+    'page' => 'Page',
+    'of' => 'of',
+    'edit_user' => 'Edit User',
+    'update' => 'Update',
+    'cancel' => 'Cancel',
+    'password' => 'Password',
+    'create' => 'Create',
+    'local' => 'Local',
+    'ldap' => 'LDAP',
+    'nav_dashboard' => 'Dashboard',
+    'manage_publications' => 'Manage Publications',
+    'manage_categories' => 'Manage Categories',
+    'nav_ldap_users' => 'LDAP Users',
+    'logout' => 'Logout',
+    'copyright' => '© :year :site',
+    
+    // User Roles and Status
+    'role' => 'Role',
+    'status' => 'Status',
+    'role_admin' => 'Admin',
+    'role_user' => 'User',
+    'status_active' => 'Active',
+    'status_inactive' => 'Inactive',
+    
+    // User Update
+    'admin_user_updated_success' => 'User updated successfully',
+    'admin_user_update_error' => 'Error updating user',
+    
+    // Search and Filter
+    'search_users_placeholder' => 'Search users by username or email',
+    'filter_all' => 'All',
+    'filter' => 'Filter',
+    'clear' => 'Clear',
 ];

+ 48 - 0
languages/fi.php

@@ -268,4 +268,52 @@ return [
     'admin_category_created_success' => 'Kategoria luotu onnistuneesti',
     'admin_category_name_exists' => 'Kategorian nimi on jo olemassa',
     'admin_category_save_error' => 'Virhe kategoriaa tallentaessa',
+    
+    // User Management
+    'manage_users' => 'Hallinnoi käyttäjiä',
+    'create_user' => 'Luo käyttäjä',
+    'username' => 'Käyttäjätunnus',
+    'email' => 'Sähköposti',
+    'auth_type' => 'Todennustyyppi',
+    'created' => 'Luotu',
+    'last_login' => 'Viimeisin kirjautuminen',
+    'actions' => 'Toiminnot',
+    'edit' => 'Muokkaa',
+    'delete' => 'Poista',
+    'delete_user_confirm' => 'Oletko varma, että haluat poistaa tämän käyttäjän?',
+    'previous' => 'Edellinen',
+    'next' => 'Seuraava',
+    'page' => 'Sivu',
+    'of' => '/',
+    'edit_user' => 'Muokkaa käyttäjää',
+    'update' => 'Päivitä',
+    'cancel' => 'Peruuta',
+    'password' => 'Salasana',
+    'create' => 'Luo',
+    'local' => 'Paikallinen',
+    'ldap' => 'LDAP',
+    'nav_dashboard' => 'Hallintapaneeli',
+    'manage_publications' => 'Hallinnoi julkaisuja',
+    'manage_categories' => 'Hallinnoi kategorioita',
+    'nav_ldap_users' => 'LDAP-käyttäjät',
+    'logout' => 'Kirjaudu ulos',
+    'copyright' => '© :year :site',
+    
+    // User Roles and Status
+    'role' => 'Rooli',
+    'status' => 'Tila',
+    'role_admin' => 'Ylläpitäjä',
+    'role_user' => 'Käyttäjä',
+    'status_active' => 'Aktiivinen',
+    'status_inactive' => 'Passiivinen',
+    
+    // User Update
+    'admin_user_updated_success' => 'Käyttäjä päivitetty onnistuneesti',
+    'admin_user_update_error' => 'Virhe käyttäjää päivitettäessä',
+    
+    // Search and Filter
+    'search_users_placeholder' => 'Etsi käyttäjiä käyttäjätunnuksen tai sähköpostin mukaan',
+    'filter_all' => 'Kaikki',
+    'filter' => 'Suodata',
+    'clear' => 'Tyhjennä',
 ];