Sindbad~EG File Manager

Current Path : /home/copmadinaarea/thecopmadinaarea.org/newsfeed/classes/
Upload File :
Current File : /home/copmadinaarea/thecopmadinaarea.org/newsfeed/classes/Editorial.php

<?php
/**
 * Editorial Management Class for COP News Portal Proofreading System
 */

class Editorial {
    private $conn;
    
    public function __construct($db) {
        $this->conn = $db;
    }

    /**
     * Submit article for review
     */
    public function submitForReview($news_id, $user_id) {
        try {
            // Get article details
            $article = $this->getArticleById($news_id);
            if (!$article || $article['user_id'] != $user_id) {
                return ['success' => false, 'error' => 'Article not found or access denied'];
            }

            // Check if article is in draft status
            if ($article['status'] !== 'draft') {
                return ['success' => false, 'error' => 'Only draft articles can be submitted for review'];
            }

            // Auto-assign editor based on location
            $assigned_editor = $this->autoAssignEditor($article['location_id']);
            
            // Update article status
            $update_query = "UPDATE news SET 
                            status = 'pending_review', 
                            assigned_editor_id = ?, 
                            submitted_for_review_at = NOW() 
                            WHERE id = ?";
            
            $stmt = $this->conn->prepare($update_query);
            $result = $stmt->execute([$assigned_editor['id'] ?? null, $news_id]);

            if ($result) {
                // Log review request
                $this->logReviewAction($news_id, $user_id, 'draft', 'pending_review', 'Article submitted for review');
                
                // Send notification to assigned editor
                if ($assigned_editor) {
                    $this->createNotification($news_id, $assigned_editor['id'], 'review_requested');
                }
                
                log_audit('SUBMIT_FOR_REVIEW', 'news', $news_id, null, ['assigned_editor_id' => $assigned_editor['id'] ?? null]);
                
                return ['success' => true, 'assigned_editor' => $assigned_editor];
            }
            
            return ['success' => false, 'error' => 'Failed to submit article for review'];
        } catch (Exception $e) {
            error_log("Submit for review error: " . $e->getMessage());
            return ['success' => false, 'error' => 'System error occurred'];
        }
    }

    /**
     * Review article (approve/reject)
     */
    public function reviewArticle($news_id, $reviewer_id, $action, $comments = '', $internal_notes = '') {
        try {
            // Validate reviewer permissions
            if (!$this->canReviewArticle($news_id, $reviewer_id)) {
                return ['success' => false, 'error' => 'You do not have permission to review this article'];
            }

            $article = $this->getArticleById($news_id);
            if (!$article) {
                return ['success' => false, 'error' => 'Article not found'];
            }

            $new_status = ($action === 'approve') ? 'approved' : 'rejected';
            
            // Update article
            $update_query = "UPDATE news SET 
                            status = ?, 
                            reviewed_at = NOW(), 
                            review_comments = ? 
                            WHERE id = ?";
            
            $stmt = $this->conn->prepare($update_query);
            $result = $stmt->execute([$new_status, $comments, $news_id]);

            if ($result) {
                // Log review action
                $this->logReviewAction($news_id, $reviewer_id, $article['status'], $new_status, $comments, $internal_notes);
                
                // Create notifications
                $notification_type = ($action === 'approve') ? 'article_approved' : 'article_rejected';
                $this->createNotification($news_id, $article['user_id'], $notification_type);
                
                log_audit('REVIEW_ARTICLE', 'news', $news_id, null, [
                    'action' => $action, 
                    'reviewer_id' => $reviewer_id,
                    'comments' => $comments
                ]);
                
                return ['success' => true, 'new_status' => $new_status];
            }
            
            return ['success' => false, 'error' => 'Failed to update article status'];
        } catch (Exception $e) {
            error_log("Review article error: " . $e->getMessage());
            return ['success' => false, 'error' => 'System error occurred'];
        }
    }

    /**
     * Publish approved article
     */
    public function publishArticle($news_id, $user_id) {
        try {
            $article = $this->getArticleById($news_id);
            if (!$article) {
                return ['success' => false, 'error' => 'Article not found'];
            }

            // Check if user can publish (author, editor, or admin)
            if (!$this->canPublishArticle($news_id, $user_id)) {
                return ['success' => false, 'error' => 'You do not have permission to publish this article'];
            }

            // Check if article is approved
            if ($article['status'] !== 'approved') {
                return ['success' => false, 'error' => 'Only approved articles can be published'];
            }

            // Update to published
            $update_query = "UPDATE news SET status = 'published' WHERE id = ?";
            $stmt = $this->conn->prepare($update_query);
            $result = $stmt->execute([$news_id]);

            if ($result) {
                $this->logReviewAction($news_id, $user_id, 'approved', 'published', 'Article published');
                log_audit('PUBLISH_ARTICLE', 'news', $news_id);
                return ['success' => true];
            }
            
            return ['success' => false, 'error' => 'Failed to publish article'];
        } catch (Exception $e) {
            error_log("Publish article error: " . $e->getMessage());
            return ['success' => false, 'error' => 'System error occurred'];
        }
    }

    /**
     * Get review queue for editor
     */
    public function getReviewQueue($editor_id, $filters = []) {
        try {
            $sql = "SELECT n.*, u.name as author_name, l.name as location_name, l.type as location_type,
                           c.name as category_name
                    FROM news n
                    LEFT JOIN users u ON n.user_id = u.id
                    LEFT JOIN locations l ON n.location_id = l.id
                    LEFT JOIN categories c ON n.category_id = c.id
                    WHERE n.status IN ('pending_review', 'draft')";
            
            $params = [];
            
            // Check user role - admins and superusers see all articles
            $user_query = "SELECT account_type FROM users WHERE id = ?";
            $user_stmt = $this->conn->prepare($user_query);
            $user_stmt->execute([$editor_id]);
            $user = $user_stmt->fetch(PDO::FETCH_ASSOC);
            
            if ($user && !in_array($user['account_type'], ['admin', 'superuser'])) {
                // For regular editors, filter by assigned editor or location-based access
                $editor_locations = $this->getEditorLocations($editor_id);
                if (!empty($editor_locations)) {
                    $placeholders = str_repeat('?,', count($editor_locations) - 1) . '?';
                    $sql .= " AND (n.assigned_editor_id = ? OR n.location_id IN ($placeholders))";
                    $params[] = $editor_id;
                    $params = array_merge($params, $editor_locations);
                } else {
                    $sql .= " AND n.assigned_editor_id = ?";
                    $params[] = $editor_id;
                }
            }
            
            if (!empty($filters['location_id'])) {
                $sql .= " AND n.location_id = ?";
                $params[] = $filters['location_id'];
            }
            
            $sql .= " ORDER BY 
                        CASE 
                            WHEN n.status = 'pending_review' THEN 1 
                            WHEN n.status = 'draft' THEN 2 
                        END,
                        COALESCE(n.submitted_for_review_at, n.created_at) ASC";
            
            if (!empty($filters['limit'])) {
                $sql .= " LIMIT ?";
                $params[] = $filters['limit'];
            }
            
            $stmt = $this->conn->prepare($sql);
            $stmt->execute($params);
            
            return $stmt->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            error_log("Get review queue error: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Get review history for article
     */
    public function getReviewHistory($news_id) {
        try {
            $query = "SELECT nr.*, u.name as reviewer_name
                      FROM news_reviews nr
                      LEFT JOIN users u ON nr.reviewer_id = u.id
                      WHERE nr.news_id = ?
                      ORDER BY nr.reviewed_at DESC";
            
            $stmt = $this->conn->prepare($query);
            $stmt->execute([$news_id]);
            
            return $stmt->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            error_log("Get review history error: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Auto-assign editor based on location hierarchy
     */
    private function autoAssignEditor($location_id) {
        try {
            // Get location details
            $location_query = "SELECT * FROM locations WHERE id = ?";
            $stmt = $this->conn->prepare($location_query);
            $stmt->execute([$location_id]);
            $location = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$location) return null;

            // Find assigned editor for this location
            $editor_query = "SELECT u.* FROM users u
                            JOIN editor_assignments ea ON u.id = ea.editor_id
                            WHERE ea.location_id = ? AND ea.is_active = 1 AND u.status = 'active'
                            ORDER BY ea.assignment_type = 'primary' DESC
                            LIMIT 1";
            
            $stmt = $this->conn->prepare($editor_query);
            $stmt->execute([$location_id]);
            $editor = $stmt->fetch(PDO::FETCH_ASSOC);
            
            // If no direct assignment, try parent location (for assemblies -> district, district -> area)
            if (!$editor && $location['parent_id']) {
                return $this->autoAssignEditor($location['parent_id']);
            }
            
            return $editor;
        } catch (Exception $e) {
            error_log("Auto assign editor error: " . $e->getMessage());
            return null;
        }
    }

    /**
     * Check if user can review specific article
     */
    private function canReviewArticle($news_id, $user_id) {
        try {
            // Get user details
            $user_query = "SELECT account_type, editorial_scope FROM users WHERE id = ?";
            $stmt = $this->conn->prepare($user_query);
            $stmt->execute([$user_id]);
            $user = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$user) return false;
            
            // Admins and superusers can review any article
            if (in_array($user['account_type'], ['admin', 'superuser'])) {
                return true;
            }
            
            // Editors can review articles assigned to them or in their scope
            if ($user['account_type'] === 'editor') {
                $article_query = "SELECT n.*, l.type as location_type 
                                 FROM news n 
                                 LEFT JOIN locations l ON n.location_id = l.id 
                                 WHERE n.id = ?";
                $stmt = $this->conn->prepare($article_query);
                $stmt->execute([$news_id]);
                $article = $stmt->fetch(PDO::FETCH_ASSOC);
                
                if ($article) {
                    // Check if assigned to this editor
                    if ($article['assigned_editor_id'] == $user_id) {
                        return true;
                    }
                    
                    // Check editorial scope
                    $editor_locations = $this->getEditorLocations($user_id);
                    return in_array($article['location_id'], $editor_locations);
                }
            }
            
            return false;
        } catch (Exception $e) {
            error_log("Can review article error: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Check if user can publish article
     */
    private function canPublishArticle($news_id, $user_id) {
        try {
            $user_query = "SELECT account_type FROM users WHERE id = ?";
            $stmt = $this->conn->prepare($user_query);
            $stmt->execute([$user_id]);
            $user = $stmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$user) return false;
            
            // Admins and superusers can publish any approved article
            if (in_array($user['account_type'], ['admin', 'superuser'])) {
                return true;
            }
            
            // Editors can publish articles in their scope
            if ($user['account_type'] === 'editor') {
                return $this->canReviewArticle($news_id, $user_id);
            }
            
            // Authors can publish their own approved articles
            $article_query = "SELECT user_id FROM news WHERE id = ?";
            $stmt = $this->conn->prepare($article_query);
            $stmt->execute([$news_id]);
            $article = $stmt->fetch(PDO::FETCH_ASSOC);
            
            return $article && $article['user_id'] == $user_id;
        } catch (Exception $e) {
            error_log("Can publish article error: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Get locations assigned to editor
     */
    private function getEditorLocations($editor_id) {
        try {
            $query = "SELECT location_id FROM editor_assignments 
                     WHERE editor_id = ? AND is_active = 1";
            $stmt = $this->conn->prepare($query);
            $stmt->execute([$editor_id]);
            
            return $stmt->fetchAll(PDO::FETCH_COLUMN);
        } catch (Exception $e) {
            error_log("Get editor locations error: " . $e->getMessage());
            return [];
        }
    }

    /**
     * Log review action
     */
    private function logReviewAction($news_id, $reviewer_id, $previous_status, $new_status, $comments = '', $internal_notes = '') {
        try {
            $query = "INSERT INTO news_reviews 
                     (news_id, reviewer_id, previous_status, new_status, review_comments, internal_notes) 
                     VALUES (?, ?, ?, ?, ?, ?)";
            
            $stmt = $this->conn->prepare($query);
            return $stmt->execute([$news_id, $reviewer_id, $previous_status, $new_status, $comments, $internal_notes]);
        } catch (Exception $e) {
            error_log("Log review action error: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Create notification
     */
    private function createNotification($news_id, $recipient_id, $type) {
        try {
            $query = "INSERT INTO review_notifications (news_id, recipient_id, notification_type) 
                     VALUES (?, ?, ?)";
            
            $stmt = $this->conn->prepare($query);
            return $stmt->execute([$news_id, $recipient_id, $type]);
        } catch (Exception $e) {
            error_log("Create notification error: " . $e->getMessage());
            return false;
        }
    }

    /**
     * Get article by ID
     */
    private function getArticleById($id) {
        try {
            $query = "SELECT * FROM news WHERE id = ?";
            $stmt = $this->conn->prepare($query);
            $stmt->execute([$id]);
            return $stmt->fetch(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            error_log("Get article by ID error: " . $e->getMessage());
            return null;
        }
    }

    /**
     * Get editorial statistics
     */
    public function getEditorialStats($editor_id = null) {
        try {
            $stats = [];
            
            // Base query conditions
            $where_conditions = [];
            $params = [];
            
            if ($editor_id && $this->isEditor($editor_id)) {
                // For editors, only show stats for their assigned locations
                $assigned_locations = $this->getEditorAssignedLocations($editor_id);
                if (!empty($assigned_locations)) {
                    $placeholders = str_repeat('?,', count($assigned_locations) - 1) . '?';
                    $where_conditions[] = "n.location_id IN ($placeholders)";
                    $params = array_merge($params, $assigned_locations);
                }
            }
            
            $where_clause = !empty($where_conditions) ? 'WHERE ' . implode(' AND ', $where_conditions) : '';
            
            // Get counts by status
            $query = "SELECT 
                        status,
                        COUNT(*) as count
                      FROM news n
                      $where_clause
                      GROUP BY status";
            
            $stmt = $this->conn->prepare($query);
            $stmt->execute($params);
            $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
            
            // Initialize all statuses with 0
            $stats = [
                'draft' => 0,
                'pending_review' => 0,
                'approved' => 0,
                'published' => 0,
                'rejected' => 0,
                'archived' => 0,
                'total_for_review' => 0
            ];
            
            // Fill in actual counts
            foreach ($results as $result) {
                $stats[$result['status']] = (int)$result['count'];
            }
            
            // Calculate total articles available for review (draft + pending_review)
            $stats['total_for_review'] = $stats['draft'] + $stats['pending_review'];
            
            return $stats;
            
        } catch (Exception $e) {
            error_log("Editorial::getEditorialStats() - Error: " . $e->getMessage());
            return [];
        }
    }







/**
 * Check if user can review articles
 */
public function canUserReview($user_id) {
    try {
        $query = "SELECT account_type, can_edit_others FROM users WHERE id = ?";
        $stmt = $this->conn->prepare($query);
        $stmt->execute([$user_id]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$user) {
            return false;
        }
        
        // Editors, admins, and superusers can review
        return in_array($user['account_type'], ['editor', 'admin', 'superuser']) || 
               $user['can_edit_others'];
        
    } catch (Exception $e) {
        error_log("Editorial::canUserReview() - Error: " . $e->getMessage());
        return false;
    }
}

/**
 * Check if user can publish articles
 */
public function canUserPublish($user_id) {
    try {
        $query = "SELECT account_type, can_edit_others FROM users WHERE id = ?";
        $stmt = $this->conn->prepare($query);
        $stmt->execute([$user_id]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$user) {
            return false;
        }
        
        // Editors, admins, and superusers can publish
        return in_array($user['account_type'], ['editor', 'admin', 'superuser']) || 
               $user['can_edit_others'];
        
    } catch (Exception $e) {
        error_log("Editorial::canUserPublish() - Error: " . $e->getMessage());
        return false;
    }
}

/**
 * Get approved articles for publishing
 */
public function getApprovedArticles($editor_id, $options = []) {
    try {
        $sql = "SELECT n.*, u.name as author_name, l.name as location_name,
                       c.name as category_name
                FROM news n
                LEFT JOIN users u ON n.user_id = u.id
                LEFT JOIN locations l ON n.location_id = l.id
                LEFT JOIN categories c ON n.category_id = c.id
                WHERE n.status = 'approved'";
        
        $params = [];
        
        // Filter by editor's assigned locations if editor
        if ($this->isEditor($editor_id)) {
            $assigned_locations = $this->getEditorAssignedLocations($editor_id);
            if (!empty($assigned_locations)) {
                $placeholders = str_repeat('?,', count($assigned_locations) - 1) . '?';
                $sql .= " AND n.location_id IN ($placeholders)";
                $params = array_merge($params, $assigned_locations);
            }
        }
        
        $sql .= " ORDER BY n.approved_at DESC";
        
        // Add limit if specified
        if (isset($options['limit'])) {
            $sql .= " LIMIT " . (int)$options['limit'];
        }
        
        $stmt = $this->conn->prepare($sql);
        $stmt->execute($params);
        
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
        
    } catch (Exception $e) {
        error_log("Editorial::getApprovedArticles() - Error: " . $e->getMessage());
        return [];
    }
}

/**
 * Find assigned editor for a location
 */
public function findAssignedEditor($location_id) {
    try {
        // First try to find direct assignment
        $query = "SELECT u.* FROM users u
                 INNER JOIN editor_assignments ea ON u.id = ea.editor_id
                 WHERE ea.location_id = ? AND ea.is_active = 1 AND u.account_type = 'editor'
                 LIMIT 1";
        
        $stmt = $this->conn->prepare($query);
        $stmt->execute([$location_id]);
        $editor = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($editor) {
            return $editor;
        }
        
        // If no direct assignment, try parent locations
        $location_query = "SELECT parent_id FROM locations WHERE id = ?";
        $stmt = $this->conn->prepare($location_query);
        $stmt->execute([$location_id]);
        $location = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if ($location && $location['parent_id']) {
            return $this->findAssignedEditor($location['parent_id']);
        }
        
        return null;
        
    } catch (Exception $e) {
        error_log("Editorial::findAssignedEditor() - Error: " . $e->getMessage());
        return null;
    }
}

/**
 * Get editor assigned locations
 */
private function getEditorAssignedLocations($editor_id) {
    try {
        $query = "SELECT location_id FROM editor_assignments 
                 WHERE editor_id = ? AND is_active = 1";
        $stmt = $this->conn->prepare($query);
        $stmt->execute([$editor_id]);
        
        return $stmt->fetchAll(PDO::FETCH_COLUMN);
    } catch (Exception $e) {
        error_log("Editorial::getEditorAssignedLocations() - Error: " . $e->getMessage());
        return [];
    }
}

/**
 * Check if user is an editor
 */
private function isEditor($user_id) {
    try {
        $query = "SELECT account_type FROM users WHERE id = ?";
        $stmt = $this->conn->prepare($query);
        $stmt->execute([$user_id]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        
        return $user && $user['account_type'] === 'editor';
    } catch (Exception $e) {
        error_log("Editorial::isEditor() - Error: " . $e->getMessage());
        return false;
    }
}

}
?>

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists