Files
crm-ms1/application/libraries/GoogleDriveDocs.php
2026-05-27 11:44:10 -04:00

225 lines
8.4 KiB
PHP
Raw 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
defined('BASEPATH') or exit('No direct script access allowed');
class GoogleDriveDocs
{
protected $CI;
// Scopes
const SCOPE_DRIVE = 'https://www.googleapis.com/auth/drive';
const SCOPE_DOCS = 'https://www.googleapis.com/auth/documents';
public function __construct()
{
$this->CI =& get_instance();
// Doit être chargé dans le contrôleur ou le modèle avant usage,
// mais on le fait “au cas où”
if (!isset($this->CI->googleservicejwt)) {
$this->CI->load->library('GoogleServiceJWT');
}
}
/* -----------------------------
* HTTP helpers (Bearer token)
* ----------------------------- */
private function requestJson(string $method, string $url, string $token, ?string $body = null, array $extraHeaders = [])
{
$ch = curl_init($url);
$headers = array_merge([
'Authorization: Bearer ' . $token,
'Accept: application/json',
], $extraHeaders);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_TIMEOUT => 30,
CURLOPT_CUSTOMREQUEST => strtoupper($method),
CURLOPT_HTTPHEADER => $headers,
]);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
$out = curl_exec($ch);
$err = curl_error($ch);
$code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($err) {
log_message('error', '[GoogleDriveDocs] CURL error '.$err.' for '.$url);
throw new Exception('HTTP error: ' . $err);
}
$json = json_decode($out, true);
if ($code >= 400) {
$msg = is_array($json) ? json_encode($json) : $out;
log_message('error', "[GoogleDriveDocs] HTTP $code: $msg");
throw new Exception("HTTP $code: $msg");
}
return is_array($json) ? $json : [];
}
public function getJson(string $url, string $token): array
{
return $this->requestJson('GET', $url, $token, null);
}
public function postJson(string $url, string $token, string $jsonBody): array
{
return $this->requestJson('POST', $url, $token, $jsonBody, ['Content-Type: application/json']);
}
/* ------------------------------------------------
* DRIVE (Shared drives OK) — Service Account JWT
* ------------------------------------------------ */
/** Liste les sous-dossiers directs dun parent (uniquement folders). */
/** Liste les sous-dossiers directs dun parent (uniquement folders), avec pagination complète. */
public function listChildrenFolders(string $parentId, ?string $pageToken = null, int $pageSize = 1000): array
{
$token = $this->CI->googleservicejwt->getAccessToken(self::SCOPE_DRIVE);
$files = [];
$q = sprintf(
"'%s' in parents and mimeType='application/vnd.google-apps.folder' and trashed=false",
$parentId
);
do {
$params = [
'supportsAllDrives' => 'true',
'includeItemsFromAllDrives' => 'true',
'corpora' => 'allDrives',
'q' => $q,
'orderBy' => 'name',
'pageSize' => $pageSize, // 1000 max par appel
'fields' => 'nextPageToken, files(id,name)'
];
if ($pageToken) {
$params['pageToken'] = $pageToken;
}
$url = 'https://www.googleapis.com/drive/v3/files?' . http_build_query($params);
$resp = $this->getJson($url, $token);
foreach (($resp['files'] ?? []) as $f) {
if (!empty($f['id']) && !empty($f['name'])) {
$files[] = $f; // on garde le format attendu par GoogleDrive::list_folders()
}
}
$pageToken = $resp['nextPageToken'] ?? null;
} while ($pageToken);
// IMPORTANT : GoogleDrive::list_folders() lit $res['files']
return ['files' => $files];
}
/** Métadonnées (id, name, parents, driveId, ownedByMe) dun fichier/dossier. */
public function getFileMeta(string $fileId): array
{
$token = $this->CI->googleservicejwt->getAccessToken(self::SCOPE_DRIVE);
$url = 'https://www.googleapis.com/drive/v3/files/' . rawurlencode($fileId) . '?' . http_build_query([
'supportsAllDrives' => 'true',
'fields' => 'id,name,parents,driveId,ownedByMe'
]);
return $this->getJson($url, $token) ?: [];
}
/**
* Récupère (si existe) ou crée un Google Docs par NOM dans un dossier donné.
* Retourne le fileId. Création directe dans le dossier => propriété = Drive/Shared drive, pas un utilisateur.
*/
public function ensureDocInFolderByName(string $folderId, string $name): string
{
$token = $this->CI->googleservicejwt->getAccessToken(self::SCOPE_DRIVE);
// 1) Cherche un doc (Google Docs) strictement par nom + parent
$qName = addcslashes($name, "'");
$listUrl = 'https://www.googleapis.com/drive/v3/files?' . http_build_query([
'supportsAllDrives' => 'true',
'includeItemsFromAllDrives' => 'true',
'q' => "name='{$qName}' and '{$folderId}' in parents and mimeType='application/vnd.google-apps.document' and trashed=false",
'fields' => 'files(id,name,driveId)'
]);
$found = $this->getJson($listUrl, $token);
if (!empty($found['files'][0]['id'])) {
return $found['files'][0]['id'];
}
// 2) Crée le doc dans ce dossier (donc sous le drive partagé sil sagit dun Shared drive)
$createUrl = 'https://www.googleapis.com/drive/v3/files?supportsAllDrives=true';
$body = json_encode([
'name' => $name,
'mimeType' => 'application/vnd.google-apps.document',
'parents' => [$folderId],
], JSON_UNESCAPED_UNICODE);
$created = $this->postJson($createUrl, $token, $body);
if (empty($created['id'])) {
throw new Exception('Impossible de créer le document Google Docs.');
}
return $created['id'];
}
/* -----------------------
* DOCS helpers (JWT)
* ----------------------- */
/** Lit un document Docs (pour récupérer endIndex, etc.). */
public function getDocument(string $docId): array
{
$token = $this->CI->googleservicejwt->getAccessToken(self::SCOPE_DOCS);
$url = 'https://docs.googleapis.com/v1/documents/' . rawurlencode($docId);
return $this->getJson($url, $token) ?: [];
}
/** Envoie un batchUpdate à Docs API. */
public function batchUpdateDocument(string $docId, array $requests): array
{
$token = $this->CI->googleservicejwt->getAccessToken(self::SCOPE_DOCS);
$url = 'https://docs.googleapis.com/v1/documents/' . rawurlencode($docId) . ':batchUpdate';
$body = json_encode(['requests' => $requests], JSON_UNESCAPED_UNICODE);
return $this->postJson($url, $token, $body);
}
/** Efface le contenu (sauf le dernier caractère) pour “réécrire proprement”. */
public function clearDocument(string $docId): void
{
// On lit le document pour connaître lindex de fin
$doc = $this->getDocument($docId);
$content = $doc['body']['content'] ?? [];
if (!$content) {
return; // rien à effacer
}
// Google Docs garde un newline terminal non-supprimable
$last = $content[count($content) - 1] ?? [];
$end = isset($last['endIndex']) ? (int)$last['endIndex'] : null;
if (!$end || $end <= 2) {
// 1 = tout début, 2 = juste le newline terminal: rien à effacer
return;
}
// ⚠️ Ne jamais inclure le newline terminal dans la suppression
$safeEnd = max(2, $end - 1);
$this->batchUpdateDocument($docId, [[
'deleteContentRange' => [
'range' => [
'startIndex' => 1,
'endIndex' => $safeEnd
]
]
]]);
}
}