285 lines
7.7 KiB
PHP
285 lines
7.7 KiB
PHP
<?php namespace App\Libraries;
|
|
|
|
use Google_Client;
|
|
use Google_Service_Sheets;
|
|
use Google_Service_Drive;
|
|
use Google_Service_Sheets_ValueRange;
|
|
use Google_Service_Sheets_BatchUpdateSpreadsheetRequest;
|
|
|
|
class GoogleSheetLib
|
|
{
|
|
protected Google_Client $client;
|
|
protected Google_Service_Sheets $sheets;
|
|
protected Google_Service_Drive $drive;
|
|
|
|
|
|
public function __construct()
|
|
{
|
|
$this->client = new Google_Client();
|
|
|
|
// Service account JSON
|
|
$this->client->setAuthConfig(
|
|
ROOTPATH . 'nhance-ee8d1-e3c5269b1ec7.json'
|
|
);
|
|
|
|
// IMPORTANT for service account
|
|
$this->client->useApplicationDefaultCredentials();
|
|
|
|
// Required scopes
|
|
$this->client->addScope([
|
|
Google_Service_Drive::DRIVE,
|
|
Google_Service_Sheets::SPREADSHEETS
|
|
]);
|
|
|
|
// Init services
|
|
$this->sheets = new Google_Service_Sheets($this->client);
|
|
$this->drive = new Google_Service_Drive($this->client);
|
|
|
|
}
|
|
|
|
/* ===================== READ ===================== */
|
|
|
|
public function read(string $spreadsheetId, string $range = 'RFQ Page')
|
|
{
|
|
$response = $this->sheets
|
|
->spreadsheets_values
|
|
->get($spreadsheetId, $range);
|
|
|
|
return $response->getValues() ?? [];
|
|
}
|
|
|
|
/* ===================== WRITE ===================== */
|
|
|
|
public function write(string $spreadsheetId, array $values, string $range = 'Sheet1')
|
|
{
|
|
$body = new Google_Service_Sheets_ValueRange([
|
|
'values' => $values
|
|
]);
|
|
|
|
$this->sheets
|
|
->spreadsheets_values
|
|
->update(
|
|
$spreadsheetId,
|
|
$range,
|
|
$body,
|
|
['valueInputOption' => 'RAW']
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* ===================== DOWNLOAD ===================== */
|
|
|
|
public function downloadExcel(string $spreadsheetId)
|
|
{
|
|
$response = $this->drive->files->export(
|
|
$spreadsheetId,
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
['alt' => 'media']
|
|
);
|
|
|
|
return $response->getBody()->getContents();
|
|
}
|
|
|
|
|
|
/* ================= COPY TEMPLATE ================= */
|
|
|
|
public function copyTemplate(string $templateId, string $name, string $folderId): string
|
|
{
|
|
$file = $this->drive->files->copy(
|
|
$templateId,
|
|
new \Google_Service_Drive_DriveFile([
|
|
'name' => $name,
|
|
'parents' => [$folderId],
|
|
|
|
]),[
|
|
'supportsAllDrives' => true,
|
|
'fields' => 'id, name, parents'
|
|
]
|
|
);
|
|
|
|
return $file->id;
|
|
}
|
|
|
|
/* ================= PERMISSIONS ================= */
|
|
|
|
public function applyPermissions(string $fileId, array $permissions)
|
|
{
|
|
foreach ($permissions['editors'] as $email) {
|
|
$this->createPermission($fileId, $email, 'writer');
|
|
}
|
|
|
|
foreach ($permissions['viewers'] as $email) {
|
|
$this->createPermission($fileId, $email, 'reader');
|
|
}
|
|
}
|
|
|
|
private function createPermission(string $fileId, string $email, string $role)
|
|
{
|
|
$type = str_starts_with($email, 'group:') ? 'group' : 'user';
|
|
$email = str_replace('group:', '', $email);
|
|
|
|
$this->drive->permissions->create(
|
|
$fileId,
|
|
new \Google_Service_Drive_Permission([
|
|
'type' => $type,
|
|
'role' => $role,
|
|
'emailAddress' => $email
|
|
]),
|
|
['sendNotificationEmail' => false,'supportsAllDrives' => true]
|
|
);
|
|
}
|
|
|
|
/* ================= PROTECTIONS ================= */
|
|
|
|
public function applyProtectionsold(string $spreadsheetId, array $ranges)
|
|
{
|
|
$spreadsheet = $this->sheets->spreadsheets->get($spreadsheetId);
|
|
$sheetId = $spreadsheet->getSheets()[0]->getProperties()->getSheetId();
|
|
|
|
$requests = [];
|
|
|
|
foreach ($ranges as $range) {
|
|
[$sheetName, $a1] = explode('!', $range);
|
|
|
|
$requests[] = [
|
|
'addProtectedRange' => [
|
|
'protectedRange' => [
|
|
'range' => [
|
|
'sheetId' => $sheetId
|
|
],
|
|
'warningOnly' => false
|
|
]
|
|
]
|
|
];
|
|
}
|
|
|
|
$this->sheets->spreadsheets->batchUpdate(
|
|
$spreadsheetId,
|
|
new Google_Service_Sheets_BatchUpdateSpreadsheetRequest([
|
|
'requests' => $requests
|
|
])
|
|
);
|
|
}
|
|
|
|
|
|
public function applyProtections(string $spreadsheetId, array $protections)
|
|
{
|
|
// Fetch spreadsheet metadata
|
|
$spreadsheet = $this->sheets->spreadsheets->get(
|
|
$spreadsheetId,
|
|
['fields' => 'sheets(properties(sheetId,title,gridProperties))']
|
|
);
|
|
|
|
// Map sheet names
|
|
$sheetMap = [];
|
|
foreach ($spreadsheet->getSheets() as $sheet) {
|
|
$props = $sheet->getProperties();
|
|
$sheetMap[$props->getTitle()] = [
|
|
'sheetId' => $props->getSheetId(),
|
|
'rowCount' => $props->getGridProperties()->getRowCount(),
|
|
'colCount' => $props->getGridProperties()->getColumnCount(),
|
|
];
|
|
}
|
|
|
|
$requests = [];
|
|
|
|
foreach ($protections as $protection) {
|
|
|
|
$rangeStr = $protection['range'];
|
|
|
|
if (!str_contains($rangeStr, '!')) {
|
|
throw new \Exception("Invalid range format: {$rangeStr}");
|
|
}
|
|
|
|
[$sheetName, $a1] = explode('!', $rangeStr, 2);
|
|
|
|
if (!isset($sheetMap[$sheetName])) {
|
|
throw new \Exception("Sheet not found: {$sheetName}");
|
|
}
|
|
|
|
$sheetMeta = $sheetMap[$sheetName];
|
|
|
|
$gridRange = $this->convertA1ToGridRange(
|
|
$a1,
|
|
$sheetMeta['sheetId'],
|
|
$sheetMeta['rowCount'],
|
|
$sheetMeta['colCount']
|
|
);
|
|
|
|
$protectedRange = [
|
|
'range' => $gridRange,
|
|
'description' => 'RFQ Protected Area',
|
|
'warningOnly' => false,
|
|
'editors' => [
|
|
'users' => $protection['users'] ?? [],
|
|
'groups' => $protection['groups'] ?? []
|
|
]
|
|
];
|
|
|
|
$requests[] = [
|
|
'addProtectedRange' => [
|
|
'protectedRange' => $protectedRange
|
|
]
|
|
];
|
|
}
|
|
|
|
if (!empty($requests)) {
|
|
$batch = new \Google_Service_Sheets_BatchUpdateSpreadsheetRequest([
|
|
'requests' => $requests
|
|
]);
|
|
|
|
$this->sheets->spreadsheets->batchUpdate($spreadsheetId, $batch);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/* ================= URL ================= */
|
|
|
|
public function sheetUrl(string $sheetId): string
|
|
{
|
|
return "https://docs.google.com/spreadsheets/d/{$sheetId}/edit";
|
|
}
|
|
|
|
|
|
private function convertA1ToGridRange($a1, $sheetId, $maxRows, $maxCols)
|
|
{
|
|
if (preg_match('/^([A-Z]+)(\d+)(?::([A-Z]+)(\d+))?$/i', $a1, $m)) {
|
|
|
|
$startCol = $this->colToIndex($m[1]);
|
|
$startRow = intval($m[2]) - 1;
|
|
|
|
if (!empty($m[3])) {
|
|
$endCol = $this->colToIndex($m[3]) + 1;
|
|
$endRow = intval($m[4]);
|
|
} else {
|
|
$endCol = $startCol + 1;
|
|
$endRow = $startRow + 1;
|
|
}
|
|
|
|
return [
|
|
'sheetId' => $sheetId,
|
|
'startRowIndex' => $startRow,
|
|
'endRowIndex' => $endRow,
|
|
'startColumnIndex' => $startCol,
|
|
'endColumnIndex' => $endCol
|
|
];
|
|
}
|
|
|
|
throw new \Exception("Unsupported A1 format: {$a1}");
|
|
}
|
|
|
|
private function colToIndex($letters)
|
|
{
|
|
$letters = strtoupper($letters);
|
|
$index = 0;
|
|
for ($i = 0; $i < strlen($letters); $i++) {
|
|
$index = $index * 26 + (ord($letters[$i]) - 64);
|
|
}
|
|
return $index - 1;
|
|
}
|
|
|
|
|
|
}
|