Sindbad~EG File Manager
<?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