388 lines
15 KiB
PHP
388 lines
15 KiB
PHP
<?php
|
|
class GlpiApi {
|
|
private string $url;
|
|
private string $appToken;
|
|
private string $username;
|
|
private string $password;
|
|
private ?string $sessionToken = null;
|
|
|
|
public function __construct(string $url, string $appToken, string $username, string $password) {
|
|
$this->url = rtrim($url, '/');
|
|
$this->appToken = $appToken;
|
|
$this->username = $username;
|
|
$this->password = $password;
|
|
}
|
|
|
|
private function log(string $message) {
|
|
error_log('[GLPI API] ' . $message);
|
|
}
|
|
|
|
public function getSessionToken(): ?string {
|
|
return $this->sessionToken;
|
|
}
|
|
|
|
public function initSession(): bool {
|
|
$endpoint = $this->url . '/initSession';
|
|
$ch = curl_init($endpoint);
|
|
$auth = base64_encode($this->username . ':' . $this->password);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Authorization: Basic ' . $auth,
|
|
'Content-Type: application/json',
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
$data = json_decode($response, true);
|
|
$this->sessionToken = $data['session_token'] ?? null;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public function getItems(string $itemType, array $params = []): array {
|
|
if (!$this->sessionToken && !$this->initSession()) return [];
|
|
$endpoint = $this->url . '/' . $itemType;
|
|
if (!empty($params)) {
|
|
$endpoint .= '?' . http_build_query($params);
|
|
}
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
return json_decode($response, true) ?? [];
|
|
}
|
|
$this->log("getItems($itemType) HTTP $httpCode");
|
|
return [];
|
|
}
|
|
|
|
public function getItem(string $itemType, int $id): ?array {
|
|
if (!$this->sessionToken && !$this->initSession()) return null;
|
|
$endpoint = $this->url . '/' . $itemType . '/' . $id;
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
return json_decode($response, true);
|
|
}
|
|
$this->log("getItem($itemType/$id) HTTP $httpCode");
|
|
return null;
|
|
}
|
|
|
|
public function createTicket(array $ticketData): ?array {
|
|
if (!$this->sessionToken && !$this->initSession()) return null;
|
|
$endpoint = $this->url . '/Ticket';
|
|
$ch = curl_init($endpoint);
|
|
$payload = json_encode(['input' => $ticketData]);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
'Content-Type: application/json',
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 201) {
|
|
return json_decode($response, true);
|
|
}
|
|
$this->log("createTicket HTTP $httpCode: " . substr($response, 0, 500));
|
|
return null;
|
|
}
|
|
|
|
public function getTicket(int $id): ?array {
|
|
if (!$this->sessionToken && !$this->initSession()) return null;
|
|
$endpoint = $this->url . '/Ticket/' . $id . '?expand_dropdowns=true';
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
return json_decode($response, true);
|
|
}
|
|
$this->log("getTicket($id) HTTP $httpCode");
|
|
return null;
|
|
}
|
|
|
|
public function getTicketFollowups(int $ticketId): array {
|
|
if (!$this->sessionToken && !$this->initSession()) return [];
|
|
$endpoint = $this->url . '/Ticket/' . $ticketId . '/ITILFollowup?expand_dropdowns=true&range=0-100';
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
return json_decode($response, true) ?? [];
|
|
}
|
|
$this->log("getTicketFollowups($ticketId) HTTP $httpCode");
|
|
return [];
|
|
}
|
|
|
|
public function getKnowbaseItems(int $start = 0, int $limit = 100): array {
|
|
if (!$this->sessionToken && !$this->initSession()) return [];
|
|
$params = ['range' => $start . '-' . ($start + $limit - 1), 'expand_dropdowns' => 'true'];
|
|
$endpoint = $this->url . '/KnowbaseItem?' . http_build_query($params);
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
return json_decode($response, true) ?? [];
|
|
}
|
|
return [];
|
|
}
|
|
|
|
public function getKnowbaseItem(int $id): ?array {
|
|
if (!$this->sessionToken && !$this->initSession()) return null;
|
|
$endpoint = $this->url . '/KnowbaseItem/' . $id . '?expand_dropdowns=true';
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
return json_decode($response, true);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function searchKnowbaseItems(string $query, int $limit = 30): array {
|
|
if (!$this->sessionToken && !$this->initSession()) return [];
|
|
$endpoint = $this->url . '/search/KnowbaseItem';
|
|
$criteria = [
|
|
'criteria' => [
|
|
['link' => 'OR', 'field' => 1, 'searchtype' => 'contains', 'value' => $query],
|
|
['link' => 'OR', 'field' => 2, 'searchtype' => 'contains', 'value' => $query]
|
|
],
|
|
'range' => "0-$limit",
|
|
'sort' => 2,
|
|
'order' => 'DESC'
|
|
];
|
|
$payload = json_encode($criteria);
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
'Content-Type: application/json',
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
$data = json_decode($response, true);
|
|
return $data['data'] ?? [];
|
|
}
|
|
$this->log("searchKnowbaseItems HTTP $httpCode");
|
|
return [];
|
|
}
|
|
|
|
public function searchKnowbaseItemsSince(string $since, int $limit = 1000): array {
|
|
if (!$this->sessionToken && !$this->initSession()) return [];
|
|
$endpoint = $this->url . '/search/KnowbaseItem';
|
|
$criteria = [
|
|
'criteria' => [
|
|
['field' => 15, 'searchtype' => 'morethan', 'value' => $since] // 15 = date_mod
|
|
],
|
|
'range' => "0-$limit",
|
|
'sort' => 15,
|
|
'order' => 'ASC'
|
|
];
|
|
$payload = json_encode($criteria);
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
'Content-Type: application/json',
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
$data = json_decode($response, true);
|
|
return $data['data'] ?? [];
|
|
}
|
|
$this->log("searchKnowbaseItemsSince HTTP $httpCode");
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Получение документов, привязанных к статье базы знаний (улучшенная версия)
|
|
*/
|
|
public function getKnowbaseItemDocuments(int $kbItemId): array {
|
|
if (!$this->sessionToken && !$this->initSession()) return [];
|
|
|
|
$documents = [];
|
|
|
|
// Способ 1: через прямой фильтр Document
|
|
$params = [
|
|
'filter[itemtype]' => 'KnowbaseItem',
|
|
'filter[items_id]' => $kbItemId,
|
|
'range' => '0-100'
|
|
];
|
|
$endpoint = $this->url . '/Document?' . http_build_query($params);
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
$docs = json_decode($response, true);
|
|
if (is_array($docs) && count($docs) > 0) {
|
|
$this->log("Got " . count($docs) . " documents for KB $kbItemId via Document filter");
|
|
return $docs;
|
|
}
|
|
}
|
|
|
|
// Способ 2: через подресурс KnowbaseItem/{id}/Document
|
|
$endpoint = $this->url . '/KnowbaseItem/' . $kbItemId . '/Document?expand_dropdowns=true&range=0-100';
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
$docs = json_decode($response, true);
|
|
if (is_array($docs) && count($docs) > 0) {
|
|
$this->log("Got " . count($docs) . " documents for KB $kbItemId via subresource");
|
|
return $docs;
|
|
}
|
|
}
|
|
|
|
// Способ 3: через Document_Item (если GLPI версии 10+)
|
|
$endpoint = $this->url . '/Document_Item?filter[itemtype]=KnowbaseItem&filter[items_id]=' . $kbItemId;
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
if ($httpCode === 200) {
|
|
$links = json_decode($response, true);
|
|
if (is_array($links) && count($links) > 0) {
|
|
// Извлекаем ID документов
|
|
$docIds = array_column($links, 'documents_id');
|
|
if (!empty($docIds)) {
|
|
$ids = implode(',', $docIds);
|
|
$endpoint2 = $this->url . '/Document?filter[id]=' . $ids . '&range=0-100';
|
|
$ch2 = curl_init($endpoint2);
|
|
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch2, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
]);
|
|
$resp2 = curl_exec($ch2);
|
|
if (curl_getinfo($ch2, CURLINFO_HTTP_CODE) === 200) {
|
|
$docs = json_decode($resp2, true);
|
|
$this->log("Got " . count($docs) . " documents for KB $kbItemId via Document_Item");
|
|
return $docs;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->log("No documents found for KB $kbItemId (tried all methods)");
|
|
return [];
|
|
}
|
|
|
|
public function getDocumentContent(int $documentId): ?string {
|
|
if (!$this->sessionToken && !$this->initSession()) return null;
|
|
|
|
$endpoint = $this->url . '/Document/' . $documentId . '?download=true';
|
|
$ch = curl_init($endpoint);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'App-Token: ' . $this->appToken,
|
|
'Session-Token: ' . $this->sessionToken,
|
|
]);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
|
|
if ($httpCode !== 200 || $response === false) {
|
|
$this->log("getDocumentContent($documentId) failed HTTP $httpCode");
|
|
return null;
|
|
}
|
|
|
|
$json = json_decode($response, true);
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
return $response;
|
|
}
|
|
|
|
if (isset($json['content'])) {
|
|
$data = base64_decode($json['content'], true);
|
|
if ($data !== false) {
|
|
return $data;
|
|
}
|
|
return $json['content'];
|
|
}
|
|
|
|
$this->log("getDocumentContent($documentId) returned unexpected JSON");
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Получить имя компьютера по ID
|
|
*/
|
|
public function getComputerName(int $computerId): string {
|
|
$computer = $this->getItem('Computer', $computerId);
|
|
if ($computer) {
|
|
$name = $computer['name'] ?? '';
|
|
$serial = $computer['serial'] ?? $computer['otherserial'] ?? '';
|
|
if ($serial) {
|
|
return "$name (Инв.№ $serial)";
|
|
}
|
|
return $name;
|
|
}
|
|
return "Компьютер #$computerId";
|
|
}
|
|
|
|
/**
|
|
* Получить полное имя категории ITIL по ID
|
|
*/
|
|
public function getCategoryName(int $categoryId): string {
|
|
$category = $this->getItem('ITILCategory', $categoryId);
|
|
if ($category) {
|
|
return $category['completename'] ?? $category['name'] ?? "Категория #$categoryId";
|
|
}
|
|
return "Категория #$categoryId";
|
|
}
|
|
} |