php/googledriveOld.php
<?php
/* Google Drive Interface
docs + links
api: https://developers.google.com/drive/api/guides/about-sdk
file fields: https://developers.google.com/drive/api/reference/rest/v3/files
guide https://developers.google.com/drive/api/guides/manage-uploads examples in php http etc.
----------- installation
download latest release, select zip with appropriate release and expand
https://github.com/googleapis/google-api-php-client/releases
expand and remove in vendor/google/apiclient-services/src everything except Drive and Drive.php
https://github.com/googleapis/google-api-php-client-services/releases
*/
require_once 'net.php';
require_once 'googleClientLib/google-api-small/vendor/autoload.php';
GoogleDrive::ini();
class GoogleDrive {
/*
docs: https://docs.nextcloud.com/server/latest/developer_manual/client_apis/WebDAV/index.html
*/
public const TYPEMAP = ['application/vnd.google-apps.folder' => CLOUD::DIR];
public static $cfg = [
# 0=>google field, 1=>row key,2=>fmtMethBegin, 3=>fmtMethEnd
[ ['nextPageToken', '', 'aPpl', 0, 0]
, ['name', 'name', 'aText']
, ['mimeType', 'type','aType']
, ['id', 'id', 'aText']
, ['modifiedTime', 'modTst', 'aTst']
, ['size', 'size', 'aText']
, ['owners', 'owner', 'ownerB', '', 'owners(emailAddress)']
# , ['d:href', 'path', '', 'hrefE', '']
, ['parents', 'parent', 'parentB']
, ['createdTime', 'creTst', ' aTst ']
], ['path' => 1, 'creTst' => 1]];
public static function ini() {
self::$cfg = Pipeline::cfgBase(...self::$cfg);
foreach(Cloud::ROWKYS as $nm => $flds)
self::cfgAdd($nm, $flds);
}
public static function cfgAdd($nm, $flds) {
$c = Pipeline::cfg(self::$cfg, $flds);
$c['gdFlds'] = implode(',', $c['flds']);
self::$cfg[$nm] = isset(self::$cfg[$nm]) ? err("googledrive cfg key $nm already defined") : $c;
}
public function pipeline($cfg=null, $trg = null) {
if (! is_array($cfg)) {
if (! $trg)
list($cfg, $trg) = explode(',', "$cfg,");
$cfg or $cfg = 'file';
$cfg = self::$cfg[$cfg] ?? err("cfg $cfg not in", array_keys(self::$cfg));
}
$trg or $trg = 'metaRowOut';
$p = new Pipeline($this, $cfg);
$p->req = new Net2Str;
$p->httpChk = 'HTTP/1.1 200 OK';
if ($trg === 'string')
return $p;
$p->json = $trg !== 'jsonObj';
if (str_starts_with($trg, 'json'))
return $p;
$p->walk = new WalkJson($p); #, allB: 'outB', allE: 'outE');
$p->walk->typeMap = self::TYPEMAP;
return $p->makeWalk($trg);
}
public $cloudId, $user, $meta;
private $srv, $fiFi = 'name, id, mimeType, modifiedTime, owners(emailAddress), size, parents';
public function __construct($gc, $id, $ty, $cliN, $us) {
$this->gc = $gc;
$this->srv = new Google\Service\Drive($gc);
$this->cloudId = $id;
$this->user = $us;
$this->accessTk = $gc->getAccessToken();
$this->hAuth = "Authorization: Bearer " . $this->accessTk['access_token'];
dbg1("auth $this->hAuth, accessToken", $this->accessTk);
$r = $this->get('root');
if ($r[5] !== $this->user)
err("mismatch GoogleDrive(user=$this->user) but root user $r[5]:", $r);
}
function meta($fun, $dir) {
$now = new DateTime();
return ['timestamp' => toLocalTst($now) , 'cloudId' => $this->cloudId, 'fun' => $fun, 'dir' => $dir
, 'user'=> $this->user, 'timeZone' => $now->format('e P')];
}
public function getHTTP($ppl, $gid, $gp=false) {
is_object($ppl) or $ppl = $this->pipeline($ppl);
$ppl -> meta = $this->meta('getHTTP', '');
# "Authorization: Bearer " . $this->accessTk['access_token']ACCESS_TOKEN"
$ctx = stream_context_create(['http' => ['header' => $this->hAuth
#. "\nAccept: application/json" # . ' realm="https://www.googleapis.com/auth/documents.readonly"' # . $this->accessTk['scope'] . '"'
# , 'method' => 'PUT'
]]);
return $ppl->request("https://www.googleapis.com/drive/v3/files/$gid?fields=" . rawurlencode($ppl->cfg['gdFlds']), $ctx);
if(is_null($cnt) or $http_response_header[0] !== 'HTTP/1.1 200 OK') {
if ($http_response_header[0] === 'HTTP/1.1 404 Not Found')
return null;
err(__METHOD__ . "get($gid)", error_get_last(), ", response", $http_response_header);
};
return json_decode($cnt);
$g = $f = $this->srv->files->get($gid, ['fields' => $this->fiFi]);
if (! ($id = $g->getParents()[0] ?? false)) {
$p = './';
} elseif ($gp) {
$p = $g->getName() . substr('/', 0, 'application/vnd.google-apps.folder' === $f->getMimeType());
while(true) {
$g = $this->srv->files->get($id, ['fields' => 'name, parents']);
if ( ! ($id = $g->getParents()[0] ?? false))
break;
$p = $g->getName() . "/$p";
}
}
return [$f->getName(), $f->getId(), $f->getMimeType(), $p ?? ''
, toLocalTst($f->getModifiedTime()), $f->getOwners()[0]->getEmailAddress(), $f->getSize()
, $f->getParents()[0] ?? null, 0];
}
public function ffHTTP($ppl, $dirId='root', $whr=false) {
is_object($ppl) or $ppl = $this->pipeline($ppl);
# $dirNd = $this->get($dirId, true);
$ppl->meta = $this->meta('ffHTTP', '???');
$ppl->request(0, 0, 1);
$ctx = stream_context_create(['http' => ['header' => $this->hAuth]]);
$qp = 'pageSize=5'
. "&fields=" . rawurlencode('nextPageToken,files(' . $ppl->cfg['gdFlds']) . ')'
. "&q=" . rawurlencode("\"$dirId\" in parents and not trashed" . ($whr ? " and ($whr)" : ''))
. '&orderBy=name';
$ppl->nextPageToken = false;
$r = $ppl->request("https://www.googleapis.com/drive/v3/files?$qp", $ctx, 2);
while ($pt = $ppl->nextPageToken) {
$ppl->nextPageToken = false;
$r = $ppl->request("https://www.googleapis.com/drive/v3/files?$qp&pageToken=$pt", $ctx, 2);
}
$ppl->request(0, 0, 4);
return $r;
$this->ffFi = $this->ffFo = $this->ffSz = 0;
$dirNd[3] = '';
($this->ffHn)($dirNd);
$this->ff1(1, $dirId, '');
}
public function get($gid, $gp=false) {
$g = $f = $this->srv->files->get($gid, ['fields' => $this->fiFi]);
if (! ($id = $g->getParents()[0] ?? false)) {
$p = './';
} elseif ($gp) {
$p = $g->getName() . substr('/', 0, 'application/vnd.google-apps.folder' === $f->getMimeType());
while(true) {
$g = $this->srv->files->get($id, ['fields' => 'name, parents']);
if ( ! ($id = $g->getParents()[0] ?? false))
break;
$p = $g->getName() . "/$p";
}
}
dbg1("drive client token", $this->gc->getAccessToken());
return [$f->getName(), $f->getiD(), $f->getMimeType(), $p ?? ''
, toLocalTst($f->getModifiedTime()), $f->getOwners()[0]->getEmailAddress(), $f->getSize()
, $f->getParents()[0] ?? null, 0];
}
public function ffSim($q, $hn=null) {
$opt = [ 'pageSize' => 100
, 'fields' => "nextPageToken, files($this->fiFi)"
, 'q' => 'not trashed'. ($q ? " and ($q)" : '')
, 'orderBy' => 'name'
];
$this->ffFi = $this->ffFo = $this->ffSz = 0;
if ($hn)
return ffOpt($opt, $hn);
$res = [];
$this->ffOpt($opt, function ($n) use(&$res) {$res[] = $n; });
return $res;
}
public function ffOpt($opt, $hn, $pPa = '') {
out("ffOpt", $opt);
do {
$results = $this->srv->files->listFiles($opt);
foreach ($files=$results->getFiles() as $f) {
$fo = 'application/vnd.google-apps.folder' === $ty = $f->getMimeType();
if ($fo) {
$this->ffFo++;
} else {
$this->ffFi++;
$this->ffSz += $f->getSize();
}
$p = "$pPa{$f->getName()}" . substr('/', 0, $fo);
$hn([$f->getName(), $f->getiD(), $f->getMimeType(), $p
, toLocalTst($f->getModifiedTime()), $f->getOwners()[0]->getEmailAddress(), $f->getSize()
, $f->getParents()[0]]);
}
} while ($opt['pageToken'] = $results->getNextPageToken());
}
public function ff($dirId, $hn, $hMeta=null, $whr=null) {
$dirId or $dirId='root';
$dirNd = $this->get($dirId, true);
$this->meta = $this->meta('ff', $dirNd[3]);
if ($hMeta)
$hMeta($this->meta);
$ix = 0;
$this->ffHn = $hn;
$this->ffOpt = [ 'pageSize' => 100
, 'fields' => "nextPageToken, files($this->fiFi)"
, 'q' => ' in parents and not trashed' . ($whr ? " and ($whr)" : '')
, 'orderBy' => 'name'
];
$this->ffFi = $this->ffFo = $this->ffSz = 0;
$dirNd[3] = '';
($this->ffHn)($dirNd);
$this->ff1(1, $dirId, '');
}
function ff1($lv, $pId, $pPa) {
$opt = $this->ffOpt;
$opt['q'] = "\"$pId\" $opt[q]";
do {
$results = $this->srv->files->listFiles($opt);
foreach ($files=$results->getFiles() as $f) {
$fo = 'application/vnd.google-apps.folder' === $ty = $f->getMimeType();
if ($fo) {
$this->ffFo++;
} else {
$this->ffFi++;
$this->ffSz += $f->getSize();
}
$p = "$pPa{$f->getName()}" . substr('/', 0, $fo);
($this->ffHn)([$f->getName(), $f->getiD(), $ty, $p
, toLocalTst($f->getModifiedTime()), $f->getOwners()[0]->getEmailAddress(), $f->getSize()
, $f->getParents()[0], $lv]);
if ($fo)
$this->ff1($lv+1, $f->getiD(), $p);
}
} while ($opt['pageToken'] = $results->getNextPageToken());
}
public function create($pa, $fn, $ty, $cnt) {
$fileMetadata = new Google\Service\Drive\DriveFile(['name' => $fn, 'parents' => [$pa]]);
$file = $this->srv->files->create($fileMetadata
, [ 'data' => $cnt . "\n" . toLocalTst('now') . "\n und so weiter"
, 'mimeType' => $ty
, 'uploadType' => 'multipart'
, 'fields' => 'id'
, 'keepRevisionForever' => true
]);
out("File ID: $file->id");
return $file->id;
}
public function uploadversion($gid, $ty, $cnt) {
$fileMetadata = new Google\Service\Drive\DriveFile();
$f = $this->srv->files->update($gid, $fileMetadata
, [ 'data' => $cnt . "]]," . toLocalTst('now') . ",upload time"
# , 'mimeType' => $ty
, 'uploadType' => 'multipart'
, 'fields' => $this->fiFi
, 'keepRevisionForever' => true
]);
out('uploadVers file ID:', [$f->getName(), $f->getiD(), $f->getMimeType(), $p ?? ''
, toLocalTst($f->getModifiedTime()), $f->getOwners()[0]->getEmailAddress(), $f->getSize()
, $f->getParents()[0] ?? null]);
return $f->id;
}
public function upload($fn, $ty, $cnt) {
$ff = $this->ffSim("\"root\" in parents and name =\"$fn\"" );
if (count($ff) === 0)
$this->create('root', $fn, $ty, $cnt);
elseif (count($ff) === 1)
$this->uploadversion($ff[0][1], $ty, $cnt);
else
err("upload $fn has " . count($ff) . " nodes", $ff);
}
public function mv($fP, $tP) {
/* move file/directory $fr to new path/name $to
*/
$fr =implode('/', array_map('rawurlencode', is_array($fP) ? $fP : explode('/', $fP)));
$to =implode('/', array_map('rawurlencode', is_array($tP) ? $tP :explode('/', $tP)));
$ctx = stream_context_create(['http' => [
'header' => "$this->hAuth\nDestination: $this->fUri$to\nOverwrite: F"
, 'method' => 'MOVE'
]]);
$r = file_get_contents("$this->fUri$fr", 0, $ctx);
if ($http_response_header[0] !== 'HTTP/1.1 201 Created')
err("mv $fr to $to failed: http_response_header", $http_response_header);
}
public function putFi($fn, $cnt, $ty='text/csv') {
// upload to host $hst a file named $fn and contents $cnt, if the file already existst, create new version of the same id
$ctx = stream_context_create(['http' => [
'header' => "Content-Type: $ty\n$this->hAuth"
, 'method' => 'PUT'
, 'content' => $cnt
]]);
$r = file_get_contents("$this->fUri$fn", 0, $ctx);
echo "putFi sent " .strlen($cnt) ." chars to $this->cloudId $this->host $fn, response $http_response_header[0]\n";
if ($http_response_header[0] !== "HTTP/1.1 201 Created" and $http_response_header[0] !== "HTTP/1.1 204 No Content")
err("putFi $fn failed, response:>n ", $http_response_header);
}
public function byId($id, $dir='') {
$ctx = stream_context_create($c = ['http' => [
'header' => "Content-Type: text/xml\n$this->hAuth"
, 'method' => 'SEARCH'
, 'content' => <<<data
<?xml version="1.0" encoding="UTF-8"?>
<d:searchrequest xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
<d:basicsearch>
<d:select>
<d:prop>
<oc:fileid/>
<d:displayname/>
<d:getcontenttype/>
<d:getetag/>
<oc:size/>
</d:prop>
</d:select>
<d:from>
<d:scope>
<d:href>/files/$this->rootDir/</d:href>
<d:depth>infinity</d:depth>
</d:scope>
</d:from>
<d:where>
<d:eq>
<d:prop>
<oc:fileid/>
</d:prop>
<d:literal>$id</d:literal>
</d:eq>
</d:where>
</d:basicsearch>
</d:searchrequest>
data ]]);
$r = file_get_contents("$this->sUri", 0, $ctx);
echo "search $this->sUri response $http_response_header[0]\n";
var_dump($http_response_header);
return $r;
}
public function rename($r, $f2v, $hi) {
/* check all resources in xmmResult $r, if they need to be renamed
$f2v: callback: rename last level of path
$hi($nx, $n, $fP, $fM, $tP): callback if last level should be rename:
$nx=count, $n array for resource, $fP from path old, $fM from path for mv, $tp to path for mv
*/
$nx = -1;
$fP = $tP = [];
$hR = function ($n) use (&$nx, &$fP, &$tP, $f2v, $hi) {
$nx++;
$aS = $n[0];
if (($fo = $n[2] === 'd:collection') !== str_ends_with($aS, '/'))
err("path $aS mismatches type $n[2]");
if ($aS === './')
return;
$aP = explode('/', $fo ? substr($aS, 0, -1) : $aS);
$aV = $f2v($aL = $aP[$aY = count($aP) - 1]);
if ($aL === $aV)
return;
$y = min($aY, count($fP));
for ($x=0; $x < $y and $aP[$x] === $fP[$x]; $x++) ; # index of first difference
out("a) $x $aY fP", $fP, ", tP", $tP);
for ( ; $x < $aY ; $x++) # copy new levels
$fP[$x] = $tP[$x] = $aP[$x];
out("b) $x $aY fP", $fP, ", tP", $tP);
while (array_key_last($fP) > $aY)
array_pop($fP);
while (array_key_last($tP) > $aY)
array_pop($tP);
$fM = $tP;
$fP[$aY] = $fM[$aY] = $aL;
$tP[$aY] = $aV;
$hi($nx, $n, $fP, $fM, $tP);
};
$this->ffRsrc($r, $hR);
echo "$nx nodes in $this->cloudId\n";
}
} # end class GoogleDrive