This commit is contained in:
2026-05-27 11:44:10 -04:00
commit 414f85ad05
31452 changed files with 3580409 additions and 0 deletions

View File

@ -0,0 +1,224 @@
<?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
]
]
]]);
}
}