Files
site_for_glpi/functions.php

264 lines
11 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
function checkAuth(PDO $pdo): ?array {
if (!isset($_SESSION['user_id'])) return null;
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
return $user ?: null;
}
function redirect(string $url): void {
header("Location: $url");
exit;
}
function getUserForms(PDO $pdo, int $userId): array {
$stmt = $pdo->prepare("SELECT * FROM forms WHERE user_id = ? ORDER BY created_at DESC");
$stmt->execute([$userId]);
return $stmt->fetchAll();
}
function createLocalForm(PDO $pdo, ?int $userId, string $type, array $data, string $status = 'pending', ?string $priority = null, ?int $locationId = null): int {
$jsonData = json_encode($data, JSON_UNESCAPED_UNICODE);
$stmt = $pdo->prepare("INSERT INTO forms (user_id, type, data, status, priority, location_id) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$userId, $type, $jsonData, $status, $priority, $locationId]);
return (int)$pdo->lastInsertId();
}
function uploadFile(array $file): string|false {
if ($file['error'] !== UPLOAD_ERR_OK) return false;
if ($file['size'] > MAX_FILE_SIZE) return false;
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
if (!in_array($file['type'], $allowedTypes)) return false;
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$basename = bin2hex(random_bytes(16)) . '.' . $ext;
$destination = UPLOAD_DIR . $basename;
if (move_uploaded_file($file['tmp_name'], $destination)) {
return 'uploads/' . $basename;
}
return false;
}
function syncFormWithGlpi(PDO $pdo, array &$form, GlpiApi $glpi): bool {
$data = json_decode($form['data'], true);
$glpiId = $data['glpi_ticket_id'] ?? null;
if (!$glpiId) return true;
$ticket = $glpi->getTicket($glpiId);
if (!$ticket) {
$stmt = $pdo->prepare("DELETE FROM forms WHERE id = ?");
$stmt->execute([$form['id']]);
return false;
}
$data['glpi_status'] = $ticket['status'] ?? null;
$data['glpi_priority'] = $ticket['priority'] ?? null;
$data['glpi_name'] = $ticket['name'] ?? '';
$newData = json_encode($data, JSON_UNESCAPED_UNICODE);
// ----- МАППИНГ СТАТУСОВ GLPI В ДОПУСТИМЫЕ ЗНАЧЕНИЯ -----
$statusGlpi = strtolower($ticket['status_name'] ?? $ticket['status'] ?? 'unknown');
$statusMap = [
'new' => 'pending',
'incoming' => 'pending',
'waiting' => 'pending',
'processing' => 'processing',
'assigned' => 'processing',
'solved' => 'completed',
'closed' => 'completed',
'rejected' => 'rejected',
'pending' => 'pending'
];
$newStatus = $statusMap[$statusGlpi] ?? 'pending';
// Дополнительная проверка на допустимость
$allowedStatuses = ['pending', 'processing', 'completed', 'rejected'];
if (!in_array($newStatus, $allowedStatuses)) {
$newStatus = 'pending';
}
// ----- МАППИНГ ПРИОРИТЕТОВ (ЧИСЛО -> 'low','medium','high') -----
$priorityGlpi = $ticket['priority'] ?? 3; // число 1..5
$priorityMap = [
1 => 'low',
2 => 'low',
3 => 'medium',
4 => 'high',
5 => 'high'
];
$newPriority = $priorityMap[$priorityGlpi] ?? 'medium';
// Обновляем запись в БД
$stmt = $pdo->prepare("UPDATE forms SET data = ?, status = ?, priority = ? WHERE id = ?");
$stmt->execute([$newData, $newStatus, $newPriority, $form['id']]);
$form['data'] = $newData;
$form['status'] = $newStatus;
$form['priority'] = $newPriority;
return true;
}
/* ========== БАЗА ЗНАНИЙ (исправленная) ========== */
// Получение всех статей
function getAllLocalKnowledgeArticles(PDO $pdo): array {
return $pdo->query("SELECT * FROM knowledge_base ORDER BY updated_at DESC")->fetchAll();
}
// Поиск с FULLTEXT или LIKE
function searchLocalKnowledgeFulltext(PDO $pdo, string $query, int $limit = 30): array {
if (strlen($query) < 3) {
$stmt = $pdo->prepare("SELECT id, title, SUBSTRING(content, 1, 200) AS preview, updated_at FROM knowledge_base WHERE title LIKE :q1 OR content LIKE :q2 ORDER BY updated_at DESC LIMIT $limit");
$like = '%' . $query . '%';
$stmt->execute(['q1' => $like, 'q2' => $like]);
return $stmt->fetchAll();
}
$stmt = $pdo->prepare("SELECT id, title, SUBSTRING(content, 1, 200) AS preview, updated_at, MATCH(title, content) AGAINST(:query IN NATURAL LANGUAGE MODE) AS relevance FROM knowledge_base WHERE MATCH(title, content) AGAINST(:query IN NATURAL LANGUAGE MODE) ORDER BY relevance DESC LIMIT $limit");
$stmt->execute(['query' => $query]);
return $stmt->fetchAll();
}
// Инкрементальная синхронизация (с проверкой существования колонки)
function syncKnowledgeBaseIncremental(PDO $pdo, GlpiApi $glpi, array &$log = []): void {
$log[] = "=== Инкрементальная синхронизация ===";
// Проверяем наличие колонки glpi_updated_at
try {
$pdo->query("SELECT glpi_updated_at FROM knowledge_base LIMIT 1");
} catch (PDOException $e) {
$log[] = "Ошибка: колонка glpi_updated_at отсутствует. Выполните ALTER TABLE knowledge_base ADD COLUMN glpi_updated_at DATETIME DEFAULT NULL;";
return;
}
$stmt = $pdo->query("SELECT MAX(glpi_updated_at) as last FROM knowledge_base");
$lastSync = $stmt->fetch()['last'];
$since = $lastSync ? $lastSync : '1970-01-01 00:00:00';
$glpiItems = $glpi->searchKnowbaseItemsSince($since);
$log[] = "Найдено изменённых статей в GLPI: " . count($glpiItems);
foreach ($glpiItems as $glpiArticle) {
$glpiId = (int)$glpiArticle['id'];
$fullArticle = $glpi->getKnowbaseItem($glpiId);
if (!$fullArticle) continue;
$title = $fullArticle['name'];
$content = $fullArticle['answer'] ?? '';
$content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$content = strip_tags($content, '<p><a><img><ul><li><ol><h1><h2><h3><h4><strong><em><span><div><br>');
$glpiUpdatedAt = $fullArticle['date_mod'] ?? date('Y-m-d H:i:s');
// Получаем документы (улучшенным методом)
$docs = $glpi->getKnowbaseItemDocuments($glpiId);
$log[] = "Статья GLPI #$glpiId: найдено документов: " . count($docs);
$documentsMeta = [];
foreach ($docs as $doc) {
$documentsMeta[] = [
'id' => $doc['id'],
'name' => $doc['name'] ?? $doc['filename'] ?? "document_{$doc['id']}",
'mime' => $doc['mime'] ?? '',
'path' => null // путь будет установлен при первом скачивании
];
}
$existing = findLocalKnowledgeByGlpiId($pdo, $glpiId);
if ($existing) {
updateLocalKnowledgeArticleMeta($pdo, $existing['id'], $title, $content, $documentsMeta, $glpiUpdatedAt);
$log[] = "Обновлена статья GLPI #$glpiId";
} else {
insertLocalKnowledgeArticle($pdo, $glpiId, $title, $content, $documentsMeta, $glpiUpdatedAt);
$log[] = "Добавлена новая статья GLPI #$glpiId";
}
}
$log[] = "=== Синхронизация завершена ===";
}
function findLocalKnowledgeByGlpiId(PDO $pdo, int $glpiId): ?array {
$stmt = $pdo->prepare("SELECT * FROM knowledge_base WHERE glpi_id = ?");
$stmt->execute([$glpiId]);
return $stmt->fetch() ?: null;
}
function insertLocalKnowledgeArticle(PDO $pdo, int $glpiId, string $title, string $content, array $documents, string $glpiUpdatedAt): int {
$stmt = $pdo->prepare("INSERT INTO knowledge_base (glpi_id, title, content, documents, glpi_updated_at) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$glpiId, $title, $content, json_encode($documents, JSON_UNESCAPED_UNICODE), $glpiUpdatedAt]);
return (int)$pdo->lastInsertId();
}
function updateLocalKnowledgeArticleMeta(PDO $pdo, int $localId, string $title, string $content, array $documents, string $glpiUpdatedAt): void {
$stmt = $pdo->prepare("SELECT documents FROM knowledge_base WHERE id = ?");
$stmt->execute([$localId]);
$old = $stmt->fetch();
$oldDocs = json_decode($old['documents'], true);
// Сохраняем уже скачанные пути
foreach ($documents as &$newDoc) {
foreach ($oldDocs as $oldDoc) {
if ($oldDoc['id'] == $newDoc['id'] && isset($oldDoc['path'])) {
$newDoc['path'] = $oldDoc['path'];
break;
}
}
}
$update = $pdo->prepare("UPDATE knowledge_base SET title = ?, content = ?, documents = ?, glpi_updated_at = ?, updated_at = NOW() WHERE id = ?");
$update->execute([$title, $content, json_encode($documents, JSON_UNESCAPED_UNICODE), $glpiUpdatedAt, $localId]);
}
function getLocalKnowledgeArticle(PDO $pdo, int $id): ?array {
$stmt = $pdo->prepare("SELECT * FROM knowledge_base WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetch() ?: null;
}
function lazyLoadDocument(PDO $pdo, int $knowledgeId, int $docId, string $docName, string $docMime, GlpiApi $glpi): ?string {
$safeName = $docId . '_' . sanitizeFileName($docName);
$cachePath = KB_CACHE_DIR . $safeName;
if (file_exists($cachePath)) {
return $cachePath;
}
$content = $glpi->getDocumentContent($docId);
if ($content === null) return null;
if (file_put_contents($cachePath, $content)) {
// Обновляем путь в БД
$stmt = $pdo->prepare("SELECT documents FROM knowledge_base WHERE id = ?");
$stmt->execute([$knowledgeId]);
$row = $stmt->fetch();
$docs = json_decode($row['documents'], true);
foreach ($docs as &$d) {
if ($d['id'] == $docId) {
$d['path'] = $safeName;
break;
}
}
$update = $pdo->prepare("UPDATE knowledge_base SET documents = ? WHERE id = ?");
$update->execute([json_encode($docs), $knowledgeId]);
return $cachePath;
}
return null;
}
function sanitizeFileName(string $name): string {
$name = basename($name);
return preg_replace('/[^a-zA-Z0-9_\-\.]/u', '_', $name);
}
function getDocumentUrl(PDO $pdo, int $knowledgeId, int $docId): ?string {
$stmt = $pdo->prepare("SELECT documents FROM knowledge_base WHERE id = ?");
$stmt->execute([$knowledgeId]);
$row = $stmt->fetch();
if (!$row) return null;
$docs = json_decode($row['documents'], true);
foreach ($docs as $doc) {
if ($doc['id'] == $docId) {
if (!empty($doc['path']) && file_exists(KB_CACHE_DIR . $doc['path'])) {
return 'get_document.php?kid=' . $knowledgeId . '&doc=' . $docId;
} else {
// Файл ещё не скачан, но ссылка та же - будет скачан при запросе
return 'get_document.php?kid=' . $knowledgeId . '&doc=' . $docId;
}
}
}
return null;
}