" in theme headers throws // an uncaught exception that the global handler renders as "Server Error" // mid-document. Guarantee the constant exists. if (!defined('THEME_VERSION')) define('THEME_VERSION', '1.0.0'); // Auto-seed default settings (runs once, cached) ensure_default_settings(); // Auto-create missing AI tables + seo_meta/users/contact_messages columns // (runs once per schema version bump, cached). Self-heals old installs. if (function_exists('ensure_required_tables')) { ensure_required_tables(); } // Integrity check — sparse (~2% of requests). The first call to // IntegrityGuard::fingerprint() also lazily persists the install fingerprint. // Call is silent on success, logs drift on failure. Never throws. if (class_exists('IntegrityGuard')) { try { IntegrityGuard::maybeVerify(); } catch (\Throwable $e) { /* non-fatal */ } } // License heartbeat — sparse (~1% of requests). Rotates the license token // when it's within 24h of expiry. Network failure is silent; cached token // continues to work until expiry, then degradation kicks in. if (class_exists('LicenseEnforcer') && rand(1, 100) === 42) { try { LicenseEnforcer::heartbeat(); } catch (\Throwable $e) {} } // AI bulk worker tick — 2% of requests drives one queue item. Cron is // the primary driver; this web-side tick ensures progress on hosts with // no cron (shared hosting) without flooding Gemini's rate limits. if (class_exists('AI_BulkJobManager') && rand(1, 50) === 11) { try { $mgr = new AI_BulkJobManager(); $mgr->resumePausedJobs(); $mgr->processBatch(1); } catch (\Throwable $e) {} } // Process scheduled posts if (rand(1, 100) === 1) { // 1% chance on each request processScheduledPosts(); publishDueContentQueueItems(); } /** * Process scheduled posts that are due */ function processScheduledPosts(): void { try { $db = db(); $now = date('Y-m-d H:i:s'); $scheduledPosts = $db->fetchAll( "SELECT p.id, p.title, p.excerpt, p.content FROM posts p WHERE p.status = 'scheduled' AND p.scheduled_at <= :now", [':now' => $now] ); foreach ($scheduledPosts as $post) { // Generate random view count between 400-3000 for new published posts $randomViewCount = rand(400, 3000); $db->update( 'posts', [ 'status' => 'published', 'published_at' => $now, 'view_count' => $randomViewCount, ], 'id = ?', [$post['id']] ); // Auto-fill SEO if empty autoFillPostSeo($post['id'], $post); log_message("Published scheduled post ID: {$post['id']}", 'info', 'scheduler.log'); } } catch (Exception $e) { log_message("Scheduler error: " . $e->getMessage(), 'error', 'scheduler.log'); } } /** * Publish due content queue items */ function publishDueContentQueueItems(): void { try { if (!class_exists('ContentQueue')) { return; } $queue = new ContentQueue(); $result = $queue->publishDueItems(); if ($result['published'] > 0) { log_message("Content queue: published {$result['published']} items", 'info', 'scheduler.log'); } } catch (Exception $e) { log_message("Content queue error: " . $e->getMessage(), 'error', 'scheduler.log'); } } /** * Auto-fill SEO meta for a post if empty */ function autoFillPostSeo(int $postId, array $postData): void { $db = db(); // Check if SEO already exists and has data $existing = $db->fetch( "SELECT * FROM seo_meta WHERE post_id = ?", [$postId] ); // If meta_title is already filled, skip auto-fill if ($existing && !empty($existing['meta_title'])) { return; } $title = $postData['title'] ?? ''; $excerpt = $postData['excerpt'] ?? ''; $content = $postData['content'] ?? ''; // Generate meta description from excerpt or content $metaDescription = ''; if (!empty($excerpt)) { $metaDescription = mb_substr(strip_tags($excerpt), 0, 160); } elseif (!empty($content)) { $metaDescription = mb_substr(strip_tags($content), 0, 160); } // Generate focus keyword from title $stopWords = ['ve', 'ile', 'bir', 'bu', 'da', 'de', 'için', 'mi', 'mı', 'mu', 'mü', 'ne', 'o', 'olan', 'olarak', 'ama', 'fakat', 'ancak', 'çünkü', 'gibi', 'kadar', 'ki', 'ya', 'veya', 'hem', 'nasıl', 'neden']; $words = preg_split('/\s+/', mb_strtolower(strip_tags($title))); $keywords = []; foreach ($words as $word) { $word = trim($word, '.,!?;:"\'-()[]{}'); if (mb_strlen($word) > 2 && !in_array($word, $stopWords)) { $keywords[] = $word; if (count($keywords) >= 3) break; } } $focusKeyword = implode(' ', $keywords); $seoData = [ 'post_id' => $postId, 'meta_title' => $title, 'meta_description' => $metaDescription, 'focus_keyword' => $focusKeyword, 'og_title' => $title, 'og_description' => $metaDescription ]; if ($existing) { $db->update('seo_meta', $seoData, 'post_id = ?', [$postId]); } else { $db->insert('seo_meta', $seoData); } }
Something went wrong. Please try again later.