Procházet zdrojové kódy

Add wordpress import tool

svalavuo před 4 dny
rodič
revize
54a1d750ac
6 změnil soubory, kde provedl 1509 přidání a 1 odebrání
  1. 346 0
      admin/wordpress_import.php
  2. 549 0
      css/admin-import.css
  3. 517 0
      includes/wordpress_import.php
  4. 47 0
      languages/en.php
  5. 47 0
      languages/fi.php
  6. 3 1
      setup.sql

+ 346 - 0
admin/wordpress_import.php

@@ -0,0 +1,346 @@
+<?php
+// Start session for language preference
+if (session_status() === PHP_SESSION_NONE) {
+    session_start();
+}
+
+require_once '../includes/config.php';
+require_once '../includes/database.php';
+require_once '../includes/auth.php';
+require_once '../includes/wordpress_import.php';
+require_once '../includes/translation.php';
+
+// Translation system is auto-initialized when translation.php is included
+
+$auth = new Auth();
+$auth->requireAuth();
+
+$user = $auth->getUser();
+$message = '';
+$importResults = null;
+$connectionTest = null;
+
+// Handle form submission
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+    try {
+        // Get WordPress database configuration
+        $wpConfig = [
+            'host' => trim($_POST['wp_host']),
+            'database' => trim($_POST['wp_database']),
+            'username' => trim($_POST['wp_username']),
+            'password' => $_POST['wp_password']
+        ];
+        
+        // Validate required fields
+        if (empty($wpConfig['host']) || empty($wpConfig['database']) || empty($wpConfig['username'])) {
+            throw new Exception(t('wordpress_import_config_required'));
+        }
+        
+        // Create import instance
+        $importer = new WordPressImport($wpConfig);
+        
+        if (isset($_POST['test_connection'])) {
+            // Test connection only
+            $connectionTest = $importer->testConnection();
+            if ($connectionTest['success']) {
+                $message = t('wordpress_connection_success') . ': ' . 
+                          t('posts') . ': ' . $connectionTest['stats']['posts'] . ', ' .
+                          t('categories') . ': ' . $connectionTest['stats']['categories'] . ', ' .
+                          t('users') . ': ' . $connectionTest['stats']['users'] . ', ' .
+                          t('comments') . ': ' . $connectionTest['stats']['comments'];
+            } else {
+                throw new Exception($connectionTest['error']);
+            }
+        } elseif (isset($_POST['start_import'])) {
+            // Start import
+            $importOptions = [
+                'import_categories' => isset($_POST['import_categories']),
+                'import_users' => isset($_POST['import_users']),
+                'import_posts' => isset($_POST['import_posts']),
+                'import_comments' => isset($_POST['import_comments'])
+            ];
+            
+            $importResults = $importer->importAll($importOptions);
+            
+            if ($importResults['success']) {
+                $message = t('wordpress_import_success');
+            } else {
+                throw new Exception($importResults['error']);
+            }
+        }
+        
+    } catch (Exception $e) {
+        $message = $e->getMessage();
+    }
+}
+?>
+<!DOCTYPE html>
+<html lang="<?php echo Translation::getCurrentLang(); ?>">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title><?php echo t('wordpress_import'); ?> - <?php echo SITE_TITLE; ?></title>
+    <link rel="stylesheet" href="../css/style.css">
+    <link rel="stylesheet" href="../css/admin-import.css">
+</head>
+<body>
+    <div class="admin-layout">
+        <header class="admin-header">
+            <div class="header-content">
+                <h1><a href="/index.php"><?php echo SITE_TITLE; ?></a></h1>
+                <nav class="admin-nav">
+                    <a href="index.php" class="nav-link"><?php echo t('admin_nav_dashboard'); ?></a>
+                    <a href="publications.php" class="nav-link"><?php echo t('admin_nav_publications'); ?></a>
+                    <a href="categories.php" class="nav-link"><?php echo t('admin_nav_categories'); ?></a>
+                    <a href="comments.php" class="nav-link"><?php echo t('admin_nav_comments'); ?></a>
+                    <a href="users.php" class="nav-link"><?php echo t('manage_users'); ?></a>
+                    <a href="wordpress_import.php" class="nav-link active"><?php echo t('wordpress_import'); ?></a>
+                    <?php if (LDAP_ENABLED): ?>
+                        <a href="ldap-users.php" class="nav-link"><?php echo t('admin_nav_ldap_users'); ?></a>
+                    <?php endif; ?>
+                    <a href="logout.php" class="nav-link"><?php echo t('admin_nav_logout'); ?></a>
+                </nav>
+                <div class="user-info">
+                    <?php echo t('welcome'); ?>, <?php echo htmlspecialchars($user['username']); ?>
+                </div>
+                <?php echo Translation::getLanguageSwitcher('wordpress_import.php'); ?>
+            </div>
+        </header>
+
+        <main class="admin-main">
+            <div class="page-header">
+                <h2><?php echo t('wordpress_import'); ?></h2>
+                <p class="page-description"><?php echo t('wordpress_import_description'); ?></p>
+            </div>
+            
+            <?php if ($message): ?>
+                <div class="alert alert-<?php echo strpos($message, 'Error') === false && strpos($message, 'failed') === false ? 'success' : 'error'; ?>">
+                    <?php echo $message; ?>
+                </div>
+            <?php endif; ?>
+
+            <!-- Import Form -->
+            <div class="import-form-container">
+                <form method="post" class="import-form">
+                    <div class="form-section">
+                        <h3><?php echo t('wordpress_database_config'); ?></h3>
+                        
+                        <div class="form-row">
+                            <div class="form-group">
+                                <label for="wp_host"><?php echo t('database_host'); ?> *</label>
+                                <input type="text" id="wp_host" name="wp_host" 
+                                       value="<?php echo htmlspecialchars($_POST['wp_host'] ?? 'localhost'); ?>" 
+                                       placeholder="localhost" required>
+                            </div>
+                            
+                            <div class="form-group">
+                                <label for="wp_database"><?php echo t('database_name'); ?> *</label>
+                                <input type="text" id="wp_database" name="wp_database" 
+                                       value="<?php echo htmlspecialchars($_POST['wp_database'] ?? ''); ?>" 
+                                       placeholder="wordpress" required>
+                            </div>
+                        </div>
+                        
+                        <div class="form-row">
+                            <div class="form-group">
+                                <label for="wp_username"><?php echo t('database_username'); ?> *</label>
+                                <input type="text" id="wp_username" name="wp_username" 
+                                       value="<?php echo htmlspecialchars($_POST['wp_username'] ?? ''); ?>" 
+                                       placeholder="wp_user" required>
+                            </div>
+                            
+                            <div class="form-group">
+                                <label for="wp_password"><?php echo t('database_password'); ?></label>
+                                <input type="password" id="wp_password" name="wp_password" 
+                                       placeholder="<?php echo t('database_password_placeholder'); ?>">
+                            </div>
+                        </div>
+                    </div>
+                    
+                    <div class="form-section">
+                        <h3><?php echo t('import_options'); ?></h3>
+                        
+                        <div class="checkbox-group">
+                            <label class="checkbox-label">
+                                <input type="checkbox" name="import_categories" checked>
+                                <span class="checkmark"></span>
+                                <?php echo t('import_categories'); ?>
+                            </label>
+                            
+                            <label class="checkbox-label">
+                                <input type="checkbox" name="import_users" checked>
+                                <span class="checkmark"></span>
+                                <?php echo t('import_users'); ?>
+                            </label>
+                            
+                            <label class="checkbox-label">
+                                <input type="checkbox" name="import_posts" checked>
+                                <span class="checkmark"></span>
+                                <?php echo t('import_posts'); ?>
+                            </label>
+                            
+                            <label class="checkbox-label">
+                                <input type="checkbox" name="import_comments" checked>
+                                <span class="checkmark"></span>
+                                <?php echo t('import_comments'); ?>
+                            </label>
+                        </div>
+                    </div>
+                    
+                    <div class="form-actions">
+                        <button type="submit" name="test_connection" class="btn btn-secondary">
+                            <?php echo t('test_connection'); ?>
+                        </button>
+                        <button type="submit" name="start_import" class="btn btn-primary" 
+                                onclick="return confirm('<?php echo t('wordpress_import_confirm'); ?>')">
+                            <?php echo t('start_import'); ?>
+                        </button>
+                    </div>
+                </form>
+            </div>
+
+            <!-- Import Results -->
+            <?php if ($importResults): ?>
+                <div class="import-results">
+                    <h3><?php echo t('import_results'); ?></h3>
+                    
+                    <?php if ($importResults['success']): ?>
+                        <div class="success-summary">
+                            <p class="success-message"><?php echo t('wordpress_import_success'); ?></p>
+                        </div>
+                        
+                        <div class="results-details">
+                            <?php foreach ($importResults['results'] as $type => $result): ?>
+                                <div class="result-item">
+                                    <h4><?php echo t('import_' . $type); ?></h4>
+                                    <div class="result-stats">
+                                        <span class="stat success">
+                                            <?php echo t('imported'); ?>: <strong><?php echo $result['imported']; ?></strong>
+                                        </span>
+                                        <span class="stat warning">
+                                            <?php echo t('skipped'); ?>: <strong><?php echo $result['skipped']; ?></strong>
+                                        </span>
+                                    </div>
+                                </div>
+                            <?php endforeach; ?>
+                        </div>
+                        
+                        <?php if (!empty($importResults['log'])): ?>
+                            <div class="import-log">
+                                <h4><?php echo t('import_log'); ?></h4>
+                                <div class="log-container">
+                                    <?php foreach ($importResults['log'] as $logEntry): ?>
+                                        <div class="log-entry log-<?php echo $logEntry['level']; ?>">
+                                            <span class="log-time"><?php echo $logEntry['timestamp']; ?></span>
+                                            <span class="log-message"><?php echo htmlspecialchars($logEntry['message']); ?></span>
+                                        </div>
+                                    <?php endforeach; ?>
+                                </div>
+                            </div>
+                        <?php endif; ?>
+                        
+                        <?php if (!empty($importResults['errors'])): ?>
+                            <div class="import-errors">
+                                <h4><?php echo t('import_errors'); ?></h4>
+                                <div class="error-list">
+                                    <?php foreach ($importResults['errors'] as $error): ?>
+                                        <div class="error-item">
+                                            <?php echo htmlspecialchars($error); ?>
+                                        </div>
+                                    <?php endforeach; ?>
+                                </div>
+                            </div>
+                        <?php endif; ?>
+                        
+                    <?php else: ?>
+                        <div class="error-summary">
+                            <p class="error-message"><?php echo htmlspecialchars($importResults['error']); ?></p>
+                        </div>
+                    <?php endif; ?>
+                </div>
+            <?php endif; ?>
+
+            <!-- Information Section -->
+            <div class="info-section">
+                <h3><?php echo t('wordpress_import_info'); ?></h3>
+                <div class="info-content">
+                    <div class="info-item">
+                        <h4><?php echo t('what_gets_imported'); ?></h4>
+                        <ul>
+                            <li><?php echo t('import_posts_info'); ?></li>
+                            <li><?php echo t('import_categories_info'); ?></li>
+                            <li><?php echo t('import_users_info'); ?></li>
+                            <li><?php echo t('import_comments_info'); ?></li>
+                        </ul>
+                    </div>
+                    
+                    <div class="info-item">
+                        <h4><?php echo t('import_requirements'); ?></h4>
+                        <ul>
+                            <li><?php echo t('import_db_access'); ?></li>
+                            <li><?php echo t('import_wp_structure'); ?></li>
+                            <li><?php echo t('import_backup'); ?></li>
+                        </ul>
+                    </div>
+                    
+                    <div class="info-item">
+                        <h4><?php echo t('import_notes'); ?></h4>
+                        <ul>
+                            <li><?php echo t('import_existing_data'); ?></li>
+                            <li><?php echo t('import_content_processing'); ?></li>
+                            <li><?php echo t('import_user_mapping'); ?></li>
+                        </ul>
+                    </div>
+                </div>
+            </div>
+        </main>
+    </div>
+
+    <script>
+        // WordPress Import JavaScript
+        document.addEventListener('DOMContentLoaded', function() {
+            const form = document.querySelector('.import-form');
+            const testBtn = document.querySelector('button[name="test_connection"]');
+            const importBtn = document.querySelector('button[name="start_import"]');
+            
+            // Show loading state
+            function setLoading(button, loading) {
+                if (loading) {
+                    button.disabled = true;
+                    button.dataset.originalText = button.textContent;
+                    button.textContent = button.dataset.loadingText || '<?php echo t('loading'); ?>...';
+                } else {
+                    button.disabled = false;
+                    button.textContent = button.dataset.originalText;
+                }
+            }
+            
+            // Test connection loading
+            testBtn.addEventListener('click', function() {
+                setLoading(this, true);
+                this.dataset.loadingText = '<?php echo t('testing_connection'); ?>';
+            });
+            
+            // Import loading
+            importBtn.addEventListener('click', function() {
+                setLoading(this, true);
+                this.dataset.loadingText = '<?php echo t('importing'); ?>...';
+            });
+            
+            // Reset loading on form submit
+            form.addEventListener('submit', function() {
+                setTimeout(() => {
+                    setLoading(testBtn, false);
+                    setLoading(importBtn, false);
+                }, 1000);
+            });
+            
+            // Auto-focus first empty field
+            const firstEmpty = form.querySelector('input[value=""], input:not([value])');
+            if (firstEmpty) {
+                firstEmpty.focus();
+            }
+        });
+    </script>
+</body>
+</html>

+ 549 - 0
css/admin-import.css

@@ -0,0 +1,549 @@
+/* WordPress Import Interface Styles */
+
+.import-form-container {
+    background: white;
+    border: 1px solid #e9ecef;
+    border-radius: 8px;
+    padding: 2rem;
+    margin-bottom: 2rem;
+    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+.form-section {
+    margin-bottom: 2rem;
+}
+
+.form-section h3 {
+    margin-bottom: 1.5rem;
+    color: #2c3e50;
+    font-size: 1.25rem;
+    border-bottom: 2px solid #3498db;
+    padding-bottom: 0.5rem;
+}
+
+.form-row {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    gap: 1.5rem;
+    margin-bottom: 1rem;
+}
+
+.form-group {
+    margin-bottom: 1.5rem;
+}
+
+.form-group label {
+    display: block;
+    margin-bottom: 0.5rem;
+    font-weight: 600;
+    color: #495057;
+}
+
+.form-group input[type="text"],
+.form-group input[type="password"] {
+    width: 100%;
+    padding: 0.75rem;
+    border: 1px solid #ced4da;
+    border-radius: 4px;
+    font-size: 1rem;
+    transition: border-color 0.2s ease, box-shadow 0.2s ease;
+}
+
+.form-group input:focus {
+    outline: none;
+    border-color: #3498db;
+    box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
+}
+
+.form-group input::placeholder {
+    color: #6c757d;
+    font-style: italic;
+}
+
+/* Checkbox Styles */
+.checkbox-group {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+    gap: 1rem;
+    margin-bottom: 1rem;
+}
+
+.checkbox-label {
+    display: flex;
+    align-items: center;
+    cursor: pointer;
+    padding: 0.75rem;
+    border: 1px solid #e9ecef;
+    border-radius: 4px;
+    transition: background-color 0.2s ease, border-color 0.2s ease;
+}
+
+.checkbox-label:hover {
+    background-color: #f8f9fa;
+    border-color: #3498db;
+}
+
+.checkbox-label input[type="checkbox"] {
+    display: none;
+}
+
+.checkmark {
+    width: 20px;
+    height: 20px;
+    border: 2px solid #3498db;
+    border-radius: 4px;
+    margin-right: 0.75rem;
+    position: relative;
+    transition: background-color 0.2s ease, border-color 0.2s ease;
+}
+
+.checkbox-label input[type="checkbox"]:checked + .checkmark {
+    background-color: #3498db;
+    border-color: #3498db;
+}
+
+.checkbox-label input[type="checkbox"]:checked + .checkmark::after {
+    content: '✓';
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    color: white;
+    font-size: 14px;
+    font-weight: bold;
+}
+
+/* Form Actions */
+.form-actions {
+    display: flex;
+    gap: 1rem;
+    justify-content: flex-start;
+    padding-top: 1.5rem;
+    border-top: 1px solid #e9ecef;
+}
+
+.form-actions .btn {
+    padding: 0.75rem 2rem;
+    border: none;
+    border-radius: 4px;
+    font-size: 1rem;
+    font-weight: 600;
+    cursor: pointer;
+    transition: background-color 0.2s ease, transform 0.1s ease, box-shadow 0.2s ease;
+    text-decoration: none;
+    display: inline-block;
+}
+
+.form-actions .btn:hover:not(:disabled) {
+    transform: translateY(-2px);
+    box-shadow: 0 4px 8px rgba(0,0,0,0.2);
+}
+
+.form-actions .btn:disabled {
+    opacity: 0.6;
+    cursor: not-allowed;
+    transform: none;
+}
+
+.btn-primary {
+    background: linear-gradient(135deg, #3498db, #2980b9);
+    color: white;
+    border: 1px solid #2980b9;
+}
+
+.btn-primary:hover:not(:disabled) {
+    background: linear-gradient(135deg, #2980b9, #3498db);
+}
+
+.btn-secondary {
+    background: linear-gradient(135deg, #95a5a6, #7f8c8d);
+    color: white;
+    border: 1px solid #7f8c8d;
+}
+
+.btn-secondary:hover:not(:disabled) {
+    background: linear-gradient(135deg, #7f8c8d, #95a5a6);
+}
+
+/* Import Results */
+.import-results {
+    background: white;
+    border: 1px solid #e9ecef;
+    border-radius: 8px;
+    padding: 2rem;
+    margin-bottom: 2rem;
+    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+.import-results h3 {
+    margin-bottom: 1.5rem;
+    color: #2c3e50;
+    font-size: 1.25rem;
+}
+
+.success-summary {
+    background: #d4edda;
+    border: 1px solid #c3e6cb;
+    border-radius: 4px;
+    padding: 1rem;
+    margin-bottom: 1.5rem;
+}
+
+.success-message {
+    color: #155724;
+    font-weight: 600;
+    margin: 0;
+}
+
+.error-summary {
+    background: #f8d7da;
+    border: 1px solid #f5c6cb;
+    border-radius: 4px;
+    padding: 1rem;
+    margin-bottom: 1.5rem;
+}
+
+.error-message {
+    color: #721c24;
+    font-weight: 600;
+    margin: 0;
+}
+
+.results-details {
+    margin-bottom: 1.5rem;
+}
+
+.result-item {
+    background: #f8f9fa;
+    border: 1px solid #e9ecef;
+    border-radius: 4px;
+    padding: 1rem;
+    margin-bottom: 1rem;
+}
+
+.result-item h4 {
+    margin-bottom: 0.75rem;
+    color: #495057;
+    font-size: 1rem;
+}
+
+.result-stats {
+    display: flex;
+    gap: 2rem;
+    flex-wrap: wrap;
+}
+
+.stat {
+    display: flex;
+    align-items: center;
+    gap: 0.5rem;
+    font-size: 0.875rem;
+}
+
+.stat strong {
+    font-weight: 600;
+}
+
+.stat.success {
+    color: #28a745;
+}
+
+.stat.warning {
+    color: #ffc107;
+}
+
+.stat.error {
+    color: #dc3545;
+}
+
+/* Import Log */
+.import-log {
+    margin-bottom: 1.5rem;
+}
+
+.import-log h4 {
+    margin-bottom: 1rem;
+    color: #495057;
+}
+
+.log-container {
+    background: #f8f9fa;
+    border: 1px solid #e9ecef;
+    border-radius: 4px;
+    padding: 1rem;
+    max-height: 300px;
+    overflow-y: auto;
+}
+
+.log-entry {
+    display: flex;
+    gap: 1rem;
+    padding: 0.5rem 0;
+    border-bottom: 1px solid #e9ecef;
+    font-size: 0.875rem;
+}
+
+.log-entry:last-child {
+    border-bottom: none;
+}
+
+.log-time {
+    color: #6c757d;
+    font-family: monospace;
+    white-space: nowrap;
+}
+
+.log-message {
+    color: #495057;
+    flex: 1;
+}
+
+.log-entry.log-error .log-message {
+    color: #dc3545;
+}
+
+.log-entry.log-warning .log-message {
+    color: #ffc107;
+}
+
+.log-entry.log-info .log-message {
+    color: #17a2b8;
+}
+
+/* Import Errors */
+.import-errors {
+    margin-bottom: 1.5rem;
+}
+
+.import-errors h4 {
+    margin-bottom: 1rem;
+    color: #dc3545;
+}
+
+.error-list {
+    background: #f8d7da;
+    border: 1px solid #f5c6cb;
+    border-radius: 4px;
+    padding: 1rem;
+    max-height: 200px;
+    overflow-y: auto;
+}
+
+.error-item {
+    color: #721c24;
+    padding: 0.5rem 0;
+    border-bottom: 1px solid #f5c6cb;
+    font-size: 0.875rem;
+}
+
+.error-item:last-child {
+    border-bottom: none;
+}
+
+/* Information Section */
+.info-section {
+    background: white;
+    border: 1px solid #e9ecef;
+    border-radius: 8px;
+    padding: 2rem;
+    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
+}
+
+.info-section h3 {
+    margin-bottom: 1.5rem;
+    color: #2c3e50;
+    font-size: 1.25rem;
+    border-bottom: 2px solid #3498db;
+    padding-bottom: 0.5rem;
+}
+
+.info-content {
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+    gap: 2rem;
+}
+
+.info-item {
+    background: #f8f9fa;
+    border: 1px solid #e9ecef;
+    border-radius: 4px;
+    padding: 1.5rem;
+}
+
+.info-item h4 {
+    margin-bottom: 1rem;
+    color: #495057;
+    font-size: 1.1rem;
+}
+
+.info-item ul {
+    margin: 0;
+    padding-left: 1.5rem;
+}
+
+.info-item li {
+    margin-bottom: 0.5rem;
+    color: #6c757d;
+    line-height: 1.5;
+}
+
+.info-item li:last-child {
+    margin-bottom: 0;
+}
+
+/* Page Description */
+.page-description {
+    color: #6c757d;
+    font-size: 1rem;
+    margin-bottom: 2rem;
+    padding: 1rem;
+    background: #f8f9fa;
+    border-left: 4px solid #3498db;
+    border-radius: 4px;
+}
+
+/* Loading States */
+.btn.loading {
+    position: relative;
+    pointer-events: none;
+}
+
+.btn.loading::after {
+    content: '';
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    width: 16px;
+    height: 16px;
+    margin: -8px 0 0 -8px;
+    border: 2px solid transparent;
+    border-top: 2px solid currentColor;
+    border-radius: 50%;
+    animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+    0% { transform: rotate(0deg); }
+    100% { transform: rotate(360deg); }
+}
+
+/* Alert Styles */
+.alert {
+    padding: 1rem;
+    border-radius: 4px;
+    margin-bottom: 1.5rem;
+    border: 1px solid transparent;
+}
+
+.alert-success {
+    background: #d4edda;
+    color: #155724;
+    border-color: #c3e6cb;
+}
+
+.alert-error {
+    background: #f8d7da;
+    color: #721c24;
+    border-color: #f5c6cb;
+}
+
+.alert-warning {
+    background: #fff3cd;
+    color: #856404;
+    border-color: #ffeaa7;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+    .import-form-container,
+    .import-results,
+    .info-section {
+        padding: 1rem;
+    }
+    
+    .form-row {
+        grid-template-columns: 1fr;
+        gap: 1rem;
+    }
+    
+    .checkbox-group {
+        grid-template-columns: 1fr;
+    }
+    
+    .form-actions {
+        flex-direction: column;
+        align-items: stretch;
+    }
+    
+    .form-actions .btn {
+        width: 100%;
+        text-align: center;
+    }
+    
+    .result-stats {
+        flex-direction: column;
+        gap: 0.5rem;
+    }
+    
+    .info-content {
+        grid-template-columns: 1fr;
+        gap: 1rem;
+    }
+    
+    .log-entry {
+        flex-direction: column;
+        gap: 0.25rem;
+    }
+    
+    .log-time {
+        font-size: 0.75rem;
+    }
+}
+
+/* Progress Indicator */
+.progress-indicator {
+    width: 100%;
+    height: 8px;
+    background: #e9ecef;
+    border-radius: 4px;
+    overflow: hidden;
+    margin: 1rem 0;
+}
+
+.progress-bar {
+    height: 100%;
+    background: linear-gradient(90deg, #3498db, #2980b9);
+    border-radius: 4px;
+    transition: width 0.3s ease;
+    width: 0%;
+}
+
+/* Status Badges */
+.status-badge {
+    display: inline-block;
+    padding: 0.25rem 0.75rem;
+    border-radius: 12px;
+    font-size: 0.75rem;
+    font-weight: 600;
+    text-transform: uppercase;
+}
+
+.status-badge.success {
+    background: #d4edda;
+    color: #155724;
+}
+
+.status-badge.warning {
+    background: #fff3cd;
+    color: #856404;
+}
+
+.status-badge.error {
+    background: #f8d7da;
+    color: #721c24;
+}
+
+.status-badge.info {
+    background: #d1ecf1;
+    color: #0c5460;
+}

+ 517 - 0
includes/wordpress_import.php

@@ -0,0 +1,517 @@
+<?php
+/**
+ * WordPress Database Import Tool
+ * Imports posts, categories, users, and comments from WordPress to the publication system
+ */
+
+class WordPressImport {
+    private $wpDb;
+    private $targetDb;
+    private $wpConfig;
+    private $importLog = [];
+    private $errors = [];
+    
+    public function __construct($wpConfig) {
+        $this->wpConfig = $wpConfig;
+        $this->targetDb = Database::getInstance();
+        $this->connectWordPress();
+    }
+    
+    /**
+     * Connect to WordPress database
+     */
+    private function connectWordPress() {
+        try {
+            $dsn = "mysql:host={$this->wpConfig['host']};dbname={$this->wpConfig['database']};charset=utf8mb4";
+            $this->wpDb = new PDO($dsn, $this->wpConfig['username'], $this->wpConfig['password']);
+            $this->wpDb->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+            $this->wpDb->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
+            $this->log('Connected to WordPress database successfully');
+        } catch (Exception $e) {
+            throw new Exception("Failed to connect to WordPress database: " . $e->getMessage());
+        }
+    }
+    
+    /**
+     * Test WordPress connection and verify structure
+     */
+    public function testConnection() {
+        try {
+            // Check if WordPress tables exist
+            $tables = ['wp_posts', 'wp_users', 'wp_terms', 'wp_term_taxonomy', 'wp_term_relationships', 'wp_comments'];
+            $existingTables = [];
+            
+            foreach ($tables as $table) {
+                $stmt = $this->wpDb->query("SHOW TABLES LIKE '{$table}'");
+                if ($stmt->rowCount() > 0) {
+                    $existingTables[] = $table;
+                }
+            }
+            
+            if (count($existingTables) < 6) {
+                throw new Exception("WordPress database structure incomplete. Missing tables: " . implode(', ', array_diff($tables, $existingTables)));
+            }
+            
+            // Get basic stats
+            $stats = [
+                'posts' => $this->getPostCount(),
+                'pages' => $this->getPageCount(),
+                'categories' => $this->getCategoryCount(),
+                'users' => $this->getUserCount(),
+                'comments' => $this->getCommentCount()
+            ];
+            
+            return ['success' => true, 'stats' => $stats, 'tables' => $existingTables];
+            
+        } catch (Exception $e) {
+            return ['success' => false, 'error' => $e->getMessage()];
+        }
+    }
+    
+    /**
+     * Import all WordPress data
+     */
+    public function importAll($options = []) {
+        $results = [];
+        
+        try {
+            // Start transaction
+            $this->targetDb->beginTransaction();
+            
+            // Import categories first (posts depend on them)
+            if ($options['import_categories'] ?? true) {
+                $results['categories'] = $this->importCategories();
+            }
+            
+            // Import users
+            if ($options['import_users'] ?? true) {
+                $results['users'] = $this->importUsers();
+            }
+            
+            // Import posts
+            if ($options['import_posts'] ?? true) {
+                $results['posts'] = $this->importPosts();
+            }
+            
+            // Import comments
+            if ($options['import_comments'] ?? true) {
+                $results['comments'] = $this->importComments();
+            }
+            
+            // Commit transaction
+            $this->targetDb->commit();
+            
+            $this->log('Import completed successfully');
+            return ['success' => true, 'results' => $results, 'log' => $this->importLog];
+            
+        } catch (Exception $e) {
+            $this->targetDb->rollBack();
+            $this->errors[] = $e->getMessage();
+            $this->log('Import failed: ' . $e->getMessage(), 'error');
+            return ['success' => false, 'error' => $e->getMessage(), 'log' => $this->importLog, 'errors' => $this->errors];
+        }
+    }
+    
+    /**
+     * Import WordPress categories
+     */
+    public function importCategories() {
+        $this->log('Starting categories import');
+        $imported = 0;
+        $skipped = 0;
+        
+        // Get WordPress categories
+        $stmt = $this->wpDb->query("
+            SELECT t.name, tt.description, tt.term_id
+            FROM wp_terms t
+            JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
+            WHERE tt.taxonomy = 'category'
+            ORDER BY t.name
+        ");
+        
+        $categories = $stmt->fetchAll();
+        
+        foreach ($categories as $wpCategory) {
+            try {
+                // Check if category already exists
+                $existing = $this->targetDb->fetch(
+                    "SELECT id FROM categories WHERE name = ?",
+                    [$wpCategory['name']]
+                );
+                
+                if ($existing) {
+                    $skipped++;
+                    $this->log("Category '{$wpCategory['name']}' already exists, skipping");
+                    continue;
+                }
+                
+                // Insert new category
+                $this->targetDb->execute(
+                    "INSERT INTO categories (name, description, created_at) VALUES (?, ?, NOW())",
+                    [
+                        $wpCategory['name'],
+                        $wpCategory['description'] ?? ''
+                    ]
+                );
+                
+                $imported++;
+                $this->log("Imported category: {$wpCategory['name']}");
+                
+            } catch (Exception $e) {
+                $this->errors[] = "Error importing category '{$wpCategory['name']}': " . $e->getMessage();
+                $this->log("Error importing category '{$wpCategory['name']}': " . $e->getMessage(), 'error');
+            }
+        }
+        
+        $this->log("Categories import completed: {$imported} imported, {$skipped} skipped");
+        return ['imported' => $imported, 'skipped' => $skipped];
+    }
+    
+    /**
+     * Import WordPress users
+     */
+    public function importUsers() {
+        $this->log('Starting users import');
+        $imported = 0;
+        $skipped = 0;
+        
+        // Get WordPress users
+        $stmt = $this->wpDb->query("
+            SELECT ID, user_login, user_email, user_nicename, user_registered, display_name
+            FROM wp_users
+            WHERE user_status = 0
+            ORDER BY ID
+        ");
+        
+        $users = $stmt->fetchAll();
+        
+        foreach ($users as $wpUser) {
+            try {
+                // Check if user already exists
+                $existing = $this->targetDb->fetch(
+                    "SELECT id FROM users WHERE username = ?",
+                    [$wpUser['user_login']]
+                );
+                
+                if ($existing) {
+                    $skipped++;
+                    $this->log("User '{$wpUser['user_login']}' already exists, skipping");
+                    continue;
+                }
+                
+                // Determine user role (WordPress usermeta table)
+                $role = $this->getUserRole($wpUser['ID']);
+                
+                // Insert new user
+                $this->targetDb->execute(
+                    "INSERT INTO users (username, email, role, auth_type, created_at) VALUES (?, ?, ?, 'wordpress', ?)",
+                    [
+                        $wpUser['user_login'],
+                        $wpUser['user_email'],
+                        $role,
+                        $wpUser['user_registered']
+                    ]
+                );
+                
+                $imported++;
+                $this->log("Imported user: {$wpUser['user_login']} (role: {$role})");
+                
+            } catch (Exception $e) {
+                $this->errors[] = "Error importing user '{$wpUser['user_login']}': " . $e->getMessage();
+                $this->log("Error importing user '{$wpUser['user_login']}': " . $e->getMessage(), 'error');
+            }
+        }
+        
+        $this->log("Users import completed: {$imported} imported, {$skipped} skipped");
+        return ['imported' => $imported, 'skipped' => $skipped];
+    }
+    
+    /**
+     * Import WordPress posts
+     */
+    public function importPosts() {
+        $this->log('Starting posts import');
+        $imported = 0;
+        $skipped = 0;
+        
+        // Get WordPress posts (only published posts, not pages)
+        $stmt = $this->wpDb->query("
+            SELECT p.ID, p.post_title, p.post_content, p.post_excerpt, p.post_date, 
+                   p.post_modified, p.post_status, p.post_author, p.post_name
+            FROM wp_posts p
+            WHERE p.post_type = 'post' AND p.post_status IN ('publish', 'draft')
+            ORDER BY p.post_date
+        ");
+        
+        $posts = $stmt->fetchAll();
+        
+        foreach ($posts as $wpPost) {
+            try {
+                // Get author name
+                $author = $this->getAuthorName($wpPost['post_author']);
+                
+                // Get post categories
+                $categories = $this->getPostCategories($wpPost['ID']);
+                
+                // Generate slug from post_name or title
+                $slug = !empty($wpPost['post_name']) ? $wpPost['post_name'] : $this->generateSlug($wpPost['post_title']);
+                
+                // Map WordPress status to our status
+                $status = ($wpPost['post_status'] === 'publish') ? 'published' : 'draft';
+                
+                // Insert post
+                $this->targetDb->execute(
+                    "INSERT INTO publications (title, slug, content, summary, author, status, created_at, updated_at, published_at) 
+                     VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
+                    [
+                        $wpPost['post_title'],
+                        $slug,
+                        $this->processContent($wpPost['post_content']),
+                        $wpPost['post_excerpt'] ?? '',
+                        $author,
+                        $status,
+                        $wpPost['post_date'],
+                        $wpPost['post_modified'],
+                        ($status === 'published') ? $wpPost['post_date'] : null
+                    ]
+                );
+                
+                $publicationId = $this->targetDb->lastInsertId();
+                
+                // Link categories
+                if (!empty($categories)) {
+                    $this->linkPostCategories($publicationId, $categories);
+                }
+                
+                $imported++;
+                $this->log("Imported post: '{$wpPost['post_title']}'");
+                
+            } catch (Exception $e) {
+                $this->errors[] = "Error importing post '{$wpPost['post_title']}': " . $e->getMessage();
+                $this->log("Error importing post '{$wpPost['post_title']}': " . $e->getMessage(), 'error');
+            }
+        }
+        
+        $this->log("Posts import completed: {$imported} imported, {$skipped} skipped");
+        return ['imported' => $imported, 'skipped' => $skipped];
+    }
+    
+    /**
+     * Import WordPress comments
+     */
+    public function importComments() {
+        $this->log('Starting comments import');
+        $imported = 0;
+        $skipped = 0;
+        
+        // Get WordPress comments
+        $stmt = $this->wpDb->query("
+            SELECT c.comment_ID, c.comment_post_ID, c.comment_author, c.comment_author_email,
+                   c.comment_content, c.comment_date, c.comment_approved, c.comment_parent
+            FROM wp_comments c
+            JOIN wp_posts p ON c.comment_post_ID = p.ID
+            WHERE p.post_type = 'post'
+            ORDER BY c.comment_date
+        ");
+        
+        $comments = $stmt->fetchAll();
+        
+        foreach ($comments as $wpComment) {
+            try {
+                // Find corresponding publication
+                $publication = $this->targetDb->fetch(
+                    "SELECT id FROM publications WHERE slug = ? OR title = ? LIMIT 1",
+                    [$this->getPostSlugById($wpComment['comment_post_ID']), $this->getPostTitleById($wpComment['comment_post_ID'])]
+                );
+                
+                if (!$publication) {
+                    $skipped++;
+                    $this->log("Comment skipped - no matching publication found for post ID {$wpComment['comment_post_ID']}");
+                    continue;
+                }
+                
+                // Map comment status
+                $status = ($wpComment['comment_approved'] === '1') ? 'approved' : 'pending';
+                
+                // Handle parent comment
+                $parentId = null;
+                if ($wpComment['comment_parent'] > 0) {
+                    $parentComment = $this->targetDb->fetch(
+                        "SELECT id FROM comments WHERE wp_comment_id = ?",
+                        [$wpComment['comment_parent']]
+                    );
+                    if ($parentComment) {
+                        $parentId = $parentComment['id'];
+                    }
+                }
+                
+                // Insert comment
+                $this->targetDb->execute(
+                    "INSERT INTO comments (publication_id, parent_id, name, email, content, status, created_at, admin_reply) 
+                     VALUES (?, ?, ?, ?, ?, ?, ?, FALSE)",
+                    [
+                        $publication['id'],
+                        $parentId,
+                        $wpComment['comment_author'],
+                        $wpComment['comment_author_email'],
+                        $wpComment['comment_content'],
+                        $status,
+                        $wpComment['comment_date']
+                    ]
+                );
+                
+                $commentId = $this->targetDb->lastInsertId();
+                
+                // Store WordPress comment ID for parent mapping
+                $this->targetDb->execute(
+                    "UPDATE comments SET wp_comment_id = ? WHERE id = ?",
+                    [$wpComment['comment_ID'], $commentId]
+                );
+                
+                $imported++;
+                $this->log("Imported comment for post ID {$wpComment['comment_post_ID']}");
+                
+            } catch (Exception $e) {
+                $this->errors[] = "Error importing comment: " . $e->getMessage();
+                $this->log("Error importing comment: " . $e->getMessage(), 'error');
+            }
+        }
+        
+        $this->log("Comments import completed: {$imported} imported, {$skipped} skipped");
+        return ['imported' => $imported, 'skipped' => $skipped];
+    }
+    
+    /**
+     * Helper methods
+     */
+    private function getUserRole($userId) {
+        $stmt = $this->wpDb->prepare("
+            SELECT meta_value FROM wp_usermeta 
+            WHERE user_id = ? AND meta_key = 'wp_capabilities'
+        ");
+        $stmt->execute([$userId]);
+        $capabilities = $stmt->fetchColumn();
+        
+        if ($capabilities && strpos($capabilities, 'administrator') !== false) {
+            return 'admin';
+        }
+        
+        return 'editor'; // Default role
+    }
+    
+    private function getAuthorName($authorId) {
+        $stmt = $this->wpDb->prepare("SELECT display_name FROM wp_users WHERE ID = ?");
+        $stmt->execute([$authorId]);
+        $name = $stmt->fetchColumn();
+        return $name ?: 'Unknown Author';
+    }
+    
+    private function getPostCategories($postId) {
+        $stmt = $this->wpDb->prepare("
+            SELECT t.name FROM wp_terms t
+            JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
+            JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
+            WHERE tr.object_id = ? AND tt.taxonomy = 'category'
+        ");
+        $stmt->execute([$postId]);
+        return $stmt->fetchAll(PDO::FETCH_COLUMN);
+    }
+    
+    private function getPostSlugById($postId) {
+        $stmt = $this->wpDb->prepare("SELECT post_name FROM wp_posts WHERE ID = ?");
+        $stmt->execute([$postId]);
+        return $stmt->fetchColumn() ?: '';
+    }
+    
+    private function getPostTitleById($postId) {
+        $stmt = $this->wpDb->prepare("SELECT post_title FROM wp_posts WHERE ID = ?");
+        $stmt->execute([$postId]);
+        return $stmt->fetchColumn() ?: '';
+    }
+    
+    private function linkPostCategories($publicationId, $categories) {
+        foreach ($categories as $categoryName) {
+            $category = $this->targetDb->fetch(
+                "SELECT id FROM categories WHERE name = ?",
+                [$categoryName]
+            );
+            
+            if ($category) {
+                $this->targetDb->execute(
+                    "INSERT IGNORE INTO publication_categories (publication_id, category_id) VALUES (?, ?)",
+                    [$publicationId, $category['id']]
+                );
+            }
+        }
+    }
+    
+    private function processContent($content) {
+        // Basic WordPress content processing
+        // You can extend this to handle shortcodes, etc.
+        $content = str_replace('[caption]', '', $content);
+        $content = str_replace('[/caption]', '', $content);
+        $content = preg_replace('/\[gallery.*?\]/', '', $content);
+        
+        return $content;
+    }
+    
+    private function generateSlug($title) {
+        $slug = strtolower($title);
+        $slug = preg_replace('/[^a-z0-9]+/', '-', $slug);
+        $slug = trim($slug, '-');
+        return $slug;
+    }
+    
+    private function log($message, $level = 'info') {
+        $this->importLog[] = [
+            'timestamp' => date('Y-m-d H:i:s'),
+            'level' => $level,
+            'message' => $message
+        ];
+    }
+    
+    /**
+     * Get statistics methods
+     */
+    public function getPostCount() {
+        $stmt = $this->wpDb->query("SELECT COUNT(*) FROM wp_posts WHERE post_type = 'post'");
+        return $stmt->fetchColumn();
+    }
+    
+    public function getPageCount() {
+        $stmt = $this->wpDb->query("SELECT COUNT(*) FROM wp_posts WHERE post_type = 'page'");
+        return $stmt->fetchColumn();
+    }
+    
+    public function getCategoryCount() {
+        $stmt = $this->wpDb->query("
+            SELECT COUNT(*) FROM wp_term_taxonomy 
+            WHERE taxonomy = 'category'
+        ");
+        return $stmt->fetchColumn();
+    }
+    
+    public function getUserCount() {
+        $stmt = $this->wpDb->query("SELECT COUNT(*) FROM wp_users");
+        return $stmt->fetchColumn();
+    }
+    
+    public function getCommentCount() {
+        $stmt = $this->wpDb->query("SELECT COUNT(*) FROM wp_comments");
+        return $stmt->fetchColumn();
+    }
+    
+    /**
+     * Get import log
+     */
+    public function getLog() {
+        return $this->importLog;
+    }
+    
+    /**
+     * Get errors
+     */
+    public function getErrors() {
+        return $this->errors;
+    }
+}

+ 47 - 0
languages/en.php

@@ -117,6 +117,53 @@ return [
     'page' => 'Page',
     'of' => 'of',
     
+    // WordPress Import
+    'wordpress_import' => 'WordPress Import',
+    'wordpress_import_description' => 'Import posts, categories, users, and comments from a WordPress database to your publication system.',
+    'wordpress_database_config' => 'WordPress Database Configuration',
+    'database_host' => 'Database Host',
+    'database_name' => 'Database Name',
+    'database_username' => 'Database Username',
+    'database_password' => 'Database Password',
+    'database_password_placeholder' => 'Leave empty if no password',
+    'import_options' => 'Import Options',
+    'import_categories' => 'Import Categories',
+    'import_users' => 'Import Users',
+    'import_posts' => 'Import Posts',
+    'import_comments' => 'Import Comments',
+    'test_connection' => 'Test Connection',
+    'start_import' => 'Start Import',
+    'wordpress_connection_success' => 'Connection successful! Found',
+    'wordpress_import_config_required' => 'Database host, name, and username are required.',
+    'wordpress_import_confirm' => 'This will import data from WordPress. Make sure you have a backup. Continue?',
+    'wordpress_import_success' => 'WordPress import completed successfully!',
+    'import_results' => 'Import Results',
+    'imported' => 'Imported',
+    'skipped' => 'Skipped',
+    'import_log' => 'Import Log',
+    'import_errors' => 'Import Errors',
+    'wordpress_import_info' => 'WordPress Import Information',
+    'what_gets_imported' => 'What Gets Imported',
+    'import_posts_info' => 'Published and draft posts with titles, content, excerpts, authors, and publication dates',
+    'import_categories_info' => 'Post categories with names and descriptions',
+    'import_users_info' => 'WordPress users mapped to system users (admin/editor roles)',
+    'import_comments_info' => 'Approved and pending comments with threading support',
+    'import_requirements' => 'Requirements',
+    'import_db_access' => 'Database access credentials for WordPress database',
+    'import_wp_structure' => 'Standard WordPress database structure (wp_posts, wp_users, wp_categories, etc.)',
+    'import_backup' => 'Backup your current database before importing',
+    'import_notes' => 'Important Notes',
+    'import_existing_data' => 'Existing data with same names will be skipped (no overwriting)',
+    'import_content_processing' => 'WordPress shortcodes and special formatting will be processed',
+    'import_user_mapping' => 'WordPress administrators become system admins, others become editors',
+    'testing_connection' => 'Testing Connection...',
+    'importing' => 'Importing',
+    'loading' => 'Loading',
+    'posts' => 'Posts',
+    'categories' => 'Categories',
+    'users' => 'Users',
+    'comments' => 'Comments',
+    
     // Search
     'search_results' => 'Search Results',
     'search_query' => 'Search for',

+ 47 - 0
languages/fi.php

@@ -118,6 +118,53 @@ return [
     'page' => 'Sivu',
     'of' => '/',
     
+    // WordPress Import
+    'wordpress_import' => 'WordPress-tuonti',
+    'wordpress_import_description' => 'Tuo julkaisut, kategoriat, käyttäjät ja kommentit WordPress-tietokannasta julkaisujärjestelmääsi.',
+    'wordpress_database_config' => 'WordPress-tietokannan asetukset',
+    'database_host' => 'Tietokantapalvelin',
+    'database_name' => 'Tietokannan nimi',
+    'database_username' => 'Tietokannan käyttäjätunnus',
+    'database_password' => 'Tietokannan salasana',
+    'database_password_placeholder' => 'Jätä tyhjäksi jos ei salasanaa',
+    'import_options' => 'Tuontiasetukset',
+    'import_categories' => 'Tuo kategoriat',
+    'import_users' => 'Tuo käyttäjät',
+    'import_posts' => 'Tuo julkaisut',
+    'import_comments' => 'Tuo kommentit',
+    'test_connection' => 'Testaa yhteys',
+    'start_import' => 'Aloita tuonti',
+    'wordpress_connection_success' => 'Yhteys onnistui! Löytyi',
+    'wordpress_import_config_required' => 'Tietokantapalvelin, nimi ja käyttäjätunnus vaaditaan.',
+    'wordpress_import_confirm' => 'Tämä tuo dataa WordPressistä. Varmuuskopioi tietokantasi ennen jatkamista. Jatka?',
+    'wordpress_import_success' => 'WordPress-tuonti suoritettu onnistuneesti!',
+    'import_results' => 'Tuontitulokset',
+    'imported' => 'Tuotu',
+    'skipped' => 'Ohitettu',
+    'import_log' => 'Tuontiloki',
+    'import_errors' => 'Tuontivirheet',
+    'wordpress_import_info' => 'WordPress-tuonnin tiedot',
+    'what_gets_imported' => 'Mitä tuodaan',
+    'import_posts_info' => 'Julkaistut ja luonnosjulkaisut otsikoiden, sisällön, tiivistelmien, tekijöiden ja julkaisupäivien kanssa',
+    'import_categories_info' => 'Julkaisukategoriat nimien ja kuvausten kanssa',
+    'import_users_info' => 'WordPress-käyttäjät järjestelmän käyttäjiksi (ylläpitäjä/toimittaja-rooleilla)',
+    'import_comments_info' => 'Hyväksytyt ja odottavat kommentit ketjuutuksella',
+    'import_requirements' => 'Vaatimukset',
+    'import_db_access' => 'Tietokantayhteystiedot WordPress-tietokantaan',
+    'import_wp_structure' => 'Standardi WordPress-tietokantarakenne (wp_posts, wp_users, wp_categories, jne.)',
+    'import_backup' => 'Varmuuskopioi nykyinen tietokanta ennen tuontia',
+    'import_notes' => 'Tärkeitä huomioita',
+    'import_existing_data' => 'Olemassa oleva data samoilla nimillä ohitetaan (ei ylikirjoitusta)',
+    'import_content_processing' => 'WordPress-lyhytkoodit ja erityisformaatit käsitellään',
+    'import_user_mapping' => 'WordPress-ylläpitäjistä tulee järjestelmän ylläpitäjiä, muista toimittajia',
+    'testing_connection' => 'Testataan yhteyttä...',
+    'importing' => 'Tuodaan',
+    'loading' => 'Ladataan',
+    'posts' => 'Julkaisut',
+    'categories' => 'Kategoriat',
+    'users' => 'Käyttäjät',
+    'comments' => 'Kommentit',
+    
     // Search
     'search_results' => 'Hakutulokset',
     'search_query' => 'Hakusana',

+ 3 - 1
setup.sql

@@ -83,13 +83,15 @@ CREATE TABLE IF NOT EXISTS comments (
     updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
     admin_reply BOOLEAN DEFAULT FALSE,
     replied_by INT NULL,
+    wp_comment_id INT NULL,
     FOREIGN KEY (publication_id) REFERENCES publications(id) ON DELETE CASCADE,
     FOREIGN KEY (parent_id) REFERENCES comments(id) ON DELETE CASCADE,
     FOREIGN KEY (replied_by) REFERENCES users(id) ON DELETE SET NULL,
     INDEX idx_publication_id (publication_id),
     INDEX idx_parent_id (parent_id),
     INDEX idx_status (status),
-    INDEX idx_created_at (created_at)
+    INDEX idx_created_at (created_at),
+    INDEX idx_wp_comment_id (wp_comment_id)
 );
 
 -- Insert default admin user (password: admin123)