gestione documentale avanzata 1 parte

This commit is contained in:
2026-05-28 09:34:28 +02:00
parent 3471befb1a
commit f2b0833b90
34482 changed files with 4312269 additions and 546 deletions
File diff suppressed because it is too large Load Diff
+535
View File
@@ -0,0 +1,535 @@
<?php
namespace As247\CloudStorages\Service;
use As247\CloudStorages\Exception\StorageException;
use As247\CloudStorages\Support\StorageAttributes;
use Google\Auth\HttpHandler\Guzzle5HttpHandler;
use Google\Auth\HttpHandler\HttpHandlerFactory;
use Google_Http_MediaFileUpload;
use Google_Service_Drive;
use Google_Service_Drive_Permission;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Psr7\Stream;
use As247\CloudStorages\Contracts\Storage\StorageContract;
use Psr\Http\Message\RequestInterface;
use Google_Service_Drive_FileList;
use Google_Service_Drive_DriveFile;
use Psr\Http\Message\StreamInterface;
class GoogleDrive
{
/**
* MIME type of directory
*
* @var string
*/
const DIR_MIME = 'application/vnd.google-apps.folder';
/**
* Default options
*
* @var array
*/
protected static $defaultOptions = [
'additionalFetchField' => '',
'publishPermission' => [
'type' => 'anyone',
'role' => 'reader',
'withLink' => true
],
'appsExportMap' => [
'application/vnd.google-apps.document' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.google-apps.spreadsheet' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.google-apps.drawing' => 'application/pdf',
'application/vnd.google-apps.presentation' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.google-apps.script' => 'application/vnd.google-apps.script+json',
'default' => 'application/pdf'
],
// Default parameters for each command
// see https://developers.google.com/drive/v3/reference/files
// ex. 'defaultParams' => ['files.list' => ['includeTeamDriveItems' => true]]
'defaultParams' => [
'files.list'=>[
'corpora'=>'user'// default is user
],
],
'teamDrive' => false,
'useTrash' => true,
];
protected $options;
protected $publishPermission;
/**
* Fetch fields setting for get
*
* @var string
*/
protected $fetchFieldsGet='id,name,mimeType,modifiedTime,parents,permissions,size,webContentLink,webViewLink';
protected $fetchFieldsList='files({{fieldsGet}}),nextPageToken';
protected $additionalFields;
protected $defaultParams;
protected $service;
//Add prefix / when original root has /
protected $rootPrefix='';
use HasLogger;
public function __construct(Google_Service_Drive $service,$options=[])
{
$this->service=$service;
$this->setupLogger($options);
$this->options = array_replace_recursive(static::$defaultOptions, $options);
$this->publishPermission = $this->options['publishPermission'];
if ($this->options['additionalFetchField']) {
$this->fetchFieldsGet .= ',' . $this->options['additionalFetchField'];
$this->additionalFields = explode(',', $this->options['additionalFetchField']);
}
$this->fetchFieldsList = str_replace('{{fieldsGet}}', $this->fetchFieldsGet, $this->fetchFieldsList);
if (isset($this->options['defaultParams'])) {
$this->defaultParams = $this->options['defaultParams'];
}
if ($this->options['teamDrive']) {
if(is_string($this->options['teamDrive'])){
$this->options['teamDrive']=[
'driveId'=>$this->options['teamDrive'],
'corpora'=>'drive',
'includeItemsFromAllDrives'=>true,
];
}
$this->enableTeamDriveSupport();
}
$root=$options['prefix']??'';
if($root){
$firstChar=substr($root,0,1);
if($firstChar==='/' || $firstChar==='\\'){
$this->rootPrefix=$firstChar;
}
}
}
public function isTeamDrive(){
return $this->options['teamDrive'];
}
public function getTeamDriveId(){
return $this->options['teamDrive']['driveId']??null;
}
public function getClient(){
return $this->service->getClient();
}
public function normalizeMetadata(Google_Service_Drive_DriveFile $object, $path)
{
$id = $object->getId();
$result = [
StorageAttributes::ATTRIBUTE_PATH => is_string($path) ? $this->rootPrefix.ltrim($path,'\/'):null,
StorageAttributes::ATTRIBUTE_TYPE => $object->mimeType === self::DIR_MIME ? StorageAttributes::TYPE_DIRECTORY : StorageAttributes::TYPE_FILE,
StorageAttributes::ATTRIBUTE_LAST_MODIFIED=>strtotime($object->getModifiedTime())
];
$result[StorageAttributes::ATTRIBUTE_MIME_TYPE] = $object->getMimeType();
$result[StorageAttributes::ATTRIBUTE_FILE_SIZE] = (int) $object->getSize();
$result[StorageAttributes::ATTRIBUTE_VISIBILITY]=$this->getVisibility($object);
// attach additional fields
if ($this->additionalFields) {
foreach($this->additionalFields as $field) {
if (property_exists($object, $field)) {
$result['@'.$field] = $object->$field;
}
}
}
$result['@id']=$id;
$result['@shareLink']=$result['@link']=$object->getWebViewLink();
$result['@downloadUrl']=$object->getWebContentLink();
return $result;
}
protected function getVisibility(Google_Service_Drive_DriveFile $object){
$permissions = $object->getPermissions();
$visibility = StorageContract::VISIBILITY_PRIVATE;
foreach ($permissions as $permission) {
if ($permission->type === $this->publishPermission['type'] && $permission->role === $this->publishPermission['role']) {
$visibility = StorageContract::VISIBILITY_PUBLIC;
break;
}
}
return $visibility;
}
/**
* Enables Team Drive support by changing default parameters
*
* @return void
*
* @see https://developers.google.com/drive/v3/reference/files
* @see \Google_Service_Drive_Resource_Files
*/
public function enableTeamDriveSupport()
{
$this->defaultParams = array_merge_recursive(
array_fill_keys([
'files.copy', 'files.create', 'files.delete',
'files.trash', 'files.get', 'files.list', 'files.update',
'files.watch',
'files.permission.create',
'files.permission.delete'
], ['supportsAllDrives' => true]),
$this->defaultParams
);
$this->mergeCommandDefaultParams('files.list',$this->options['teamDrive']);
}
protected function getParams($cmd, ...$params){
$default=$this->getDefaultParams($cmd);
return array_replace($default,...$params);
}
protected function getDefaultParams($cmd){
if(isset($this->defaultParams[$cmd]) && is_array($this->defaultParams[$cmd])){
return $this->defaultParams[$cmd];
}
return [];
}
protected function mergeCommandDefaultParams($cmd,$params){
if(!isset($this->defaultParams[$cmd])){
$this->defaultParams[$cmd]=[];
}
$this->defaultParams[$cmd]=array_replace_recursive($this->defaultParams[$cmd],$params);
return $this;
}
/**
* Create directory
* @param $name
* @param $parentId
* @return bool|Google_Service_Drive_DriveFile|RequestInterface
*/
public function dirCreate($name, $parentId){
$file = new Google_Service_Drive_DriveFile();
$file->setName($name);
$file->setParents([
$parentId
]);
$file->setMimeType(self::DIR_MIME);
return $this->filesCreate($file);
}
/**
* Find files by name in given directory
* @param $name
* @param $parent
* @param $mineType
* @return Google_Service_Drive_FileList|Google_Service_Drive_DriveFile[]
*/
public function filesFindByName($name,$parent, $mineType=null){
if($parent instanceof Google_Service_Drive_DriveFile){
$parent=$parent->getId();
}
$timerStart=microtime(true);
$client=$this->service->getClient();
$collectOthers=false;
$q='trashed = false and "%s" in parents and name = "%s"';
$argsMatchName = [
'pageSize' => 2,
'q' =>sprintf($q,$parent,$name,static::DIR_MIME),
];
if($collectOthers) {
$client->setUseBatch(true);
$batch = $this->service->createBatch();
$filesMatchedName=$this->filesListFiles($argsMatchName);
$q='trashed = false and "%s" in parents';
if($mineType){
$q.=" and mimeType ".$mineType;
}
$argsOthers = [
'pageSize' => 50,
'q' =>sprintf($q,$parent,$name,static::DIR_MIME),
];
$otherFiles=$this->filesListFiles($argsOthers);
$batch->add($filesMatchedName,'matched');
$batch->add($otherFiles,'others');
$results = $batch->execute();
$files=[];
$isFullResult=empty($mineType);//if limited to a mime type so it is not full result
if(!isset($results['response-others'])){
$isFullResult=false;
}
foreach ($results as $key => $result) {
if ($result instanceof Google_Service_Drive_FileList) {
if($key==='response-matched'){
if(count($result)>1){
throw new StorageException("Duplicated file ".$name.' in '.$parent);
}
}
foreach ($result as $file) {
if (!isset($files[$file->id])) {
$files[$file->id] = $file;
}
}
if ($key === 'response-others' && $result->nextPageToken) {
$isFullResult = false;
}
}
}
$client->setUseBatch(false);
}else{
$isFullResult=false;
$list=$this->filesListFiles($argsMatchName);
$files=$list->getFiles();
}
$this->logRequest('files.list.by_name',[
'query'=>'find for '.$name.' in '.$parent,
'duration'=>microtime(true)-$timerStart]);
$list=new Google_Service_Drive_FileList();
$list->setFiles($files);
return [$list,$isFullResult];
}
/**
* @param array $optParams
* @return Google_Service_Drive_FileList | RequestInterface
*/
public function filesListFiles($optParams = array()){
$timerStart=microtime(true);
$optParams=$this->getParams('files.list',['fields' => $this->fetchFieldsList],$optParams);
$result= $this->service->files->listFiles($optParams);
if(!$this->service->getClient()->shouldDefer()) {
$this->logRequest('files.list', [
'query'=>func_get_args(),
'duration'=>microtime(true)-$timerStart,
]);
}
return $result;
}
/**
* @param $fileId
* @param array $optParams
* @return Google_Service_Drive_DriveFile|RequestInterface
*/
public function filesGet($fileId, $optParams = array()){
if($fileId instanceof Google_Service_Drive_DriveFile){
$fileId=$fileId->getId();
}
$timerStart=microtime(true);
$optParams=$this->getParams('files.get',['fields' => $this->fetchFieldsGet],$optParams);
$result= $this->service->files->get($fileId,$optParams);
$this->logRequest('files.get', [
'query'=>func_get_args(),
'duration'=>microtime(true)-$timerStart,
]);
return $result;
}
/**
* @param Google_Service_Drive_DriveFile $postBody
* @param array $optParams
* @return Google_Service_Drive_DriveFile | RequestInterface
*/
public function filesCreate(Google_Service_Drive_DriveFile $postBody, $optParams = array()){
$timerStart=microtime(true);
$optParams=$this->getParams('files.create',['fields' => $this->fetchFieldsGet],$optParams);
$result= $this->service->files->create($postBody,$optParams);
$this->logRequest('files.create', [
'query'=>func_get_args(),
'duration'=>microtime(true)-$timerStart,
]);
return $result;
}
public function filesUpdate($fileId, Google_Service_Drive_DriveFile $postBody, $optParams = array()){
$timerStart=microtime(true);
if($fileId instanceof Google_Service_Drive_DriveFile){
$fileId=$fileId->getId();
}
$optParams=$this->getParams('files.update',['fields' => $this->fetchFieldsGet],$optParams);
$result= $this->service->files->update($fileId,$postBody,$optParams);
$this->logRequest('files.update', [
'query'=>func_get_args(),
'duration'=>microtime(true)-$timerStart,
]);
return $result;
}
public function filesCopy($fileId, Google_Service_Drive_DriveFile $postBody, $optParams = array()){
$timerStart=microtime(true);
if($fileId instanceof Google_Service_Drive_DriveFile){
$fileId=$fileId->getId();
}
$optParams=$this->getParams('files.copy',['fields' => $this->fetchFieldsGet],$optParams);
$result= $this->service->files->copy($fileId,$postBody,$optParams);
$this->logRequest('files.copy', [
'query'=>func_get_args(),
'duration'=>microtime(true)-$timerStart,
]);
return $result;
}
public function filesDelete($fileId, $optParams = array()){
$timerStart=microtime(true);
if($fileId instanceof Google_Service_Drive_DriveFile){
$fileId=$fileId->getId();
}
$optParams=$this->getParams('files.delete',$optParams);
if($this->options['useTrash']){
$fileUpdate=new Google_Service_Drive_DriveFile();
$fileUpdate->setTrashed(true);
$result=$this->service->files->update($fileId,$fileUpdate,$optParams);
}else {
$result = $this->service->files->delete($fileId, $optParams);
}
$this->logRequest('files.delete', [
'query'=>func_get_args(),
'duration'=>microtime(true)-$timerStart,
]);
return $result;
}
/**
* @param $fileId
* @return resource|null
* @throws GuzzleException
*/
public function filesRead($fileId){
$timerStart=microtime(true);
if($fileId instanceof Google_Service_Drive_DriveFile){
$fileId=$fileId->getId();
}
$this->service->getClient()->setUseBatch(true);
$request=$this->filesGet($fileId, ['alt' => 'media']);
$this->service->getClient()->setUseBatch(false);
$stream=null;
$client=$this->service->getClient()->authorize();
$handler=HttpHandlerFactory::build($client);
if($handler instanceof Guzzle5HttpHandler){
//Handler v5 still working but stream read all to buffer
//We use native method to read
$stream=$this->fileReadNative($request);
}else {
$response = $handler($request, ['stream' => true]);
if ($response->getBody() instanceof Stream) {
$stream = $response->getBody()->detach();
}
}
$this->logRequest('files.read', [
'query'=>func_get_args(),
'duration'=>microtime(true)-$timerStart,
]);
return $stream;
}
protected function fileReadNative(RequestInterface $request){
$token=$this->service->getClient()->getAccessToken();
$token=$token['access_token'];
$url=$request->getUri()->__toString();
$auth = "Authorization: Bearer $token";
$opts = array (
'http' => array (
'method' => "GET",
'header' => $auth,
'user_agent' => 'as247/cloud-storages',
)
);
$context = stream_context_create($opts);
$fp = fopen($url, 'rb', false, $context);
return $fp;
}
/**
* Publish file
* @param Google_Service_Drive_DriveFile $file
*/
public function publish(Google_Service_Drive_DriveFile $file)
{
if ($this->getVisibility($file) === StorageContract::VISIBILITY_PUBLIC) {//already published
return;
}
$timerStart=microtime(true);
$permission = new Google_Service_Drive_Permission($this->publishPermission);
$optParams=$this->getParams('files.permission.create');
if ($newPermission=$this->service->permissions->create($file->getId(), $permission, $optParams)) {
$permissions=$file->getPermissions();
$permissions=array_merge($permissions,[$newPermission]);
$file->setPermissions($permissions);
}
$this->logRequest('files.permission.create', [
'query'=>$file->getId(),
'duration'=>microtime(true)-$timerStart,
]);
}
/**
* Un-publish specified path item
* @param Google_Service_Drive_DriveFile $file
*/
public function unPublish(Google_Service_Drive_DriveFile $file)
{
$timerStart=microtime(true);
$permissions = $file->getPermissions();
$optParams=$this->getParams('files.permission.delete');
foreach ($permissions as $index=> $permission) {
if ($permission->type === 'anyone' && $permission->role === 'reader') {
$this->service->permissions->delete($file->getId(), $permission->getId(), $optParams);
unset($permissions[$index]);
}
}
$file->setPermissions($permissions);
$this->logRequest('files.permission.create', [
'query'=>$file->getId(),
'duration'=>microtime(true)-$timerStart,
]);
}
public function filesUploadChunk(Google_Service_Drive_DriveFile $file,StreamInterface $contents,$chunk){
$client = $this->service->getClient();
$client->setDefer(true);
if (!$file->getId()) {
$request = $this->filesCreate($file);
} else {
$update=new Google_Service_Drive_DriveFile();
$update->setMimeType($file->getMimeType());
$request = $this->filesUpdate($file->getId(), $update);
}
$mime=$file->getMimeType();
// Create a media file upload to represent our upload process.
$media = new Google_Http_MediaFileUpload($client, $request, $mime, null, true, $chunk);
$media->setFileSize($contents->getSize());
// Upload the various chunks. $status will be false until the process is
// complete.
$status = false;
$contents->rewind();
while (!$status && !$contents->eof()) {
$status = $media->nextChunk($contents->read($chunk));
}
$client->setDefer(false);
return $status;
}
/**
* @param Google_Service_Drive_DriveFile $file
* @param $contents
* @return Google_Service_Drive_DriveFile|RequestInterface
*/
public function filesUploadSimple(Google_Service_Drive_DriveFile $file,StreamInterface $contents){
$params = [
'data' => $contents->getContents(),
'uploadType' => 'media',
];
if (!$file->getId()) {
$obj = $this->filesCreate($file, $params);
} else {
$update=new Google_Service_Drive_DriveFile();
$update->setMimeType($file->getMimeType());
$obj = $this->filesUpdate($file->getId(), $update, $params);
}
return $obj;
}
protected function logRequest($cmd, $query){
$this->logger->request($cmd,$query);
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace As247\CloudStorages\Service;
trait HasLogger
{
protected $logger;
public function getLogger(){
return $this->logger;
}
public function setLogger($logger){
$this->logger=$logger;
return $this;
}
protected function setupLogger($options){
$dir=__DIR__.'/../../';
$log=$options['log']??false;
if(is_string($log)){
$dir=$log;
$log=true;
}
$dir=$options['log.dir']??$dir;
$this->logger=new Logger($dir);
$this->logger->enable($log);
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
namespace As247\CloudStorages\Service;
class Logger
{
protected $logDir;
protected $enabled=false;
protected $num_requests=0;
protected $withTrace=false;
public function __construct($logDir='')
{
$this->logDir=$logDir;
}
public function enable($flag=true){
$previous= $this->enabled;
$this->enabled=$flag;
return $previous;
}
public function withTrace($flag=true){
$this->withTrace=$flag;
return $this;
}
function log($message, $level='debug'){
if(!$this->enabled){
return $this;
}
if(is_array($message) || is_object($message)){
$message=json_encode($message,JSON_PRETTY_PRINT);
}
$this->write("[$level] $message",'debug');
return $this;
}
function request($cmd, $query){
if(!$this->enabled){
return $this;
}
if(is_array($query) || is_object($query)) {
$query = json_encode($query, JSON_PRETTY_PRINT);
}
$this->write("{$cmd}\n$query",'query');
$this->num_requests++;
return $this;
}
public function getNumRequests(){
return $this->num_requests;
}
protected function write($line,$file){
if(!$this->enabled || !$this->logDir){
return ;
}
$time=date('Y-m-d h:i:s');
$trace='';
if($this->withTrace){
$trace=PHP_EOL.
'Trace:'.
$this->debugBacktraceSummary(null,1);
}
file_put_contents($this->logDir."/$file.log",
$time.' '. $line.$trace
.PHP_EOL.PHP_EOL,FILE_APPEND);
}
protected function debugBacktraceSummary( $ignore_class = null, $skip_frames = 0, $pretty = true ) {
static $truncate_paths;
$trace = debug_backtrace( false );
$caller = array();
$check_class = ! is_null( $ignore_class );
$skip_frames++; // Skip this function.
if ( ! isset( $truncate_paths ) ) {
$truncate_paths = array(
);
}
foreach ( $trace as $call ) {
if ( $skip_frames > 0 ) {
$skip_frames--;
} elseif ( isset( $call['class'] ) ) {
if ( $check_class && $ignore_class == $call['class'] ) {
continue; // Filter out calls.
}
$caller[] = "{$call['class']}{$call['type']}{$call['function']}";
} else {
if ( in_array( $call['function'], array( 'do_action', 'apply_filters', 'do_action_ref_array', 'apply_filters_ref_array' ), true ) ) {
$caller[] = "{$call['function']}('{$call['args'][0]}')";
} elseif ( in_array( $call['function'], array( 'include', 'include_once', 'require', 'require_once' ), true ) ) {
$filename = isset( $call['args'][0] ) ? $call['args'][0] : '';
$caller[] = $call['function'] . "('" . str_replace( $truncate_paths, '', ( $filename ) ) . "')";
} else {
$caller[] = $call['function'];
}
}
}
if ( $pretty ) {
return join( ', ', array_reverse( $caller ) );
} else {
return $caller;
}
}
}
+328
View File
@@ -0,0 +1,328 @@
<?php
namespace As247\CloudStorages\Service;
use ArrayObject;
use As247\CloudStorages\Exception\StorageException;
use As247\CloudStorages\Support\Path;
use As247\CloudStorages\Support\StorageAttributes;
use Generator;
use As247\CloudStorages\Contracts\Storage\StorageContract;
use InvalidArgumentException;
use Microsoft\Graph\Exception\GraphException;
use Microsoft\Graph\Graph;
use Microsoft\Graph\Http\GraphRequest;
class OneDrive
{
protected $graph;
const ROOT = '/me/drive/root';
protected $publishPermission = [
'role' => 'read',
'scope' => 'anonymous',
'withLink' => true
];
protected $options;
//Add prefix / when original root has /
protected $rootPrefix='';
use HasLogger;
public function __construct(Graph $graph,$options=[])
{
$this->options=$options;
$root=$options['root']??'';
if($root){
$firstChar=substr($root,0,1);
if($firstChar==='/' || $firstChar==='\\'){
$this->rootPrefix=$firstChar;
}
}
$this->setupLogger($options);
$this->graph=$graph;
}
function normalizeMetadata(array $response, string $path): array
{
$permissions=$response['permissions']??[];
$visibility = StorageContract::VISIBILITY_PRIVATE;
$shareLink=null;
foreach ($permissions as $permission) {
if(!isset($permission['link']['scope']) || !isset($permission['roles'])){
continue;
}
if(in_array($this->publishPermission['role'],$permission['roles'])
&& $permission['link']['scope']==$this->publishPermission['scope']){
$visibility = StorageContract::VISIBILITY_PUBLIC;
$shareLink=$permission['link']['webUrl']??null;
break;
}
}
$meta= [
StorageAttributes::ATTRIBUTE_PATH => $this->rootPrefix.ltrim($path,'\/'),
StorageAttributes::ATTRIBUTE_LAST_MODIFIED => strtotime($response['lastModifiedDateTime']),
StorageAttributes::ATTRIBUTE_FILE_SIZE => $response['size'],
StorageAttributes::ATTRIBUTE_TYPE => isset($response['file']) ? 'file' : 'dir',
StorageAttributes::ATTRIBUTE_MIME_TYPE => $response['file']['mimeType'] ?? null,
StorageAttributes::ATTRIBUTE_VISIBILITY=>$visibility,
'@id'=>$response['id']??null,
'@link' => $response['webUrl'] ?? null,
'@shareLink'=>$shareLink,
'@downloadUrl' => $response['@microsoft.graph.downloadUrl']?? null,
];
return $meta;
}
function getEndpoint($path='',$action='',$params=[]){
$this->validatePath($path);
$path=Path::clean($path);
$path=trim($path,'\\/');
$path=static::ROOT.':/'.$path;
/**
* Path should not end with /
* /me/drive/root:/path/to/file
* /me/drive/root
*/
$path=rtrim($path,':/');
if($action===true){//path reference
if(strpos($path,':')===false) {
$path .= ':';//root path should end with :
}
}
if ($action && is_string($action)) {
/**
* Append action to path
* /me/drive/root:/path:/action
* trim : for root
* /me/drive/root/action
*/
$path= rtrim($path,':');
if(strpos($path,':')!==false) {
$path .=':/' . $action;//root:/path:/action
}else{
$path .= '/' . $action;//root/action
}
}
if($params){
$path.='?'.http_build_query($params);
}
return $path;
}
/**
* @param $path
* @param $newPath
* @return array|null
* @throws GraphException
*/
public function copy($path,$newPath){
$endpoint = $this->getEndpoint($path,'copy');
$name=basename($newPath);
$this->createDirectory(dirname($newPath));
$newPathParent=$this->getEndpoint(dirname($newPath),true);
$body=[
'name' => $name,
'parentReference' => [
'path' => $newPathParent,
],
];
return $this->createRequest('POST', $endpoint)
->attachBody($body)
->execute()->getBody();
}
/**
* @param $path
* @return array|null
* @throws GraphException
*/
public function createDirectory($path){
$path=Path::clean($path);
if($path==='/'){
return $this->getItem('/');
}
$endpoint=$this->getEndpoint($path);
return $this->createRequest('PATCH', $endpoint)
->attachBody([
'folder' => new ArrayObject(),
])->execute()->getBody();
}
/**
* @param $path
* @return array|null
* @throws GraphException
*/
public function delete($path){
$endpoint=$this->getEndpoint($path);
return $this->createRequest('DELETE', $endpoint)->execute()->getBody();
}
/**
* @param $path
* @param null $format
* @return resource|null
* @throws GraphException
*/
public function download($path,$format=null){
$args=[];
if($format){
if(is_string($format)){
$args=['format'=>$format];
}elseif(is_array($format)){
$args=$format;
}
}
$endpoint=$this->getEndpoint($path,'content',$args);
$response=$this->createRequest('GET',$endpoint)->setReturnType('GuzzleHttp\Psr7\Stream')->execute();
/**
* @var StreamWrapper $response
*/
return $response->detach();
}
/**
* @param $path
* @param array $args
* @return array|null
* @throws GraphException
*/
public function getItem($path,$args=[]){
$endpoint=$this->getEndpoint($path,'',$args);
$response = $this->createRequest('GET', $endpoint)->execute();
return $response->getBody();
}
/**
* @param $path
* @return Generator
* @throws GraphException
*/
public function listChildren($path){
$endpoint = $this->getEndpoint($path,'children');
$nextPage=null;
do {
if ($nextPage) {
$endpoint = $nextPage;
}
$response = $this->createRequest('GET', $endpoint)
->execute();
$nextPage = $response->getNextLink();
$items = $response->getBody()['value']??[];
if(!is_array($items)){
$items=[];
}
yield from $items;
}while($nextPage);
}
/**
* @param $path
* @param $newPath
* @return array|null
* @throws GraphException
*/
public function move($path,$newPath){
$endpoint = $this->getEndpoint($path);
$name=basename($newPath);
$this->createDirectory(dirname($newPath));
$newPathParent=$this->getEndpoint(dirname($newPath),true);
$body=[
'name' => $name,
'parentReference' => [
'path' => $newPathParent,
],
];
return $this->createRequest('PATCH', $endpoint)
->attachBody($body)
->execute()->getBody();
}
/**
* @param $path
* @param $contents
* @return array|null
* @throws GraphException
*/
public function upload($path,$contents){
$endpoint = $this->getEndpoint($path,'content');
try {
$stream = StreamWrapper::wrap($contents);
}catch (InvalidArgumentException $e){
throw new StorageException("Invalid contents. ".$e->getMessage());
}
$this->createDirectory(dirname($path));
return $this->createRequest('PUT', $endpoint)
->attachBody($stream)
->execute()->getBody();
}
/**
* @param $path
* @return array|mixed
* @throws GraphException
*/
public function getPermissions($path){
$endpoint=$this->getEndpoint($path,'permissions');
$response = $this->createRequest('GET', $endpoint)->execute();
return $response->getBody()['value']??[];
}
/**
* @param $path
* @return array
* @throws GraphException
*/
function publish($path){
$endpoint=$this->getEndpoint($path,'createLink');
$body=['type'=>'view','scope'=>'anonymous'];
$response = $this->createRequest('POST', $endpoint)
->attachBody($body)->execute();
return $response->getBody();
}
/**
* @param $path
* @throws GraphException
*/
function unPublish($path){
$permissions=$this->getPermissions($path);
$idToRemove='';
foreach ($permissions as $permission){
if(in_array($this->publishPermission['role'],$permission['roles'])
&& $permission['link']['scope']==$this->publishPermission['scope']){
$idToRemove=$permission['id'];
break;
}
}
if(!$idToRemove){
return ;
}
$endpoint=$this->getEndpoint($path,'permissions/'.$idToRemove);
$this->createRequest('DELETE', $endpoint)->execute();
}
/**
* @param $requestType
* @param $endpoint
* @return GraphRequest
* @throws GraphException
*/
protected function createRequest($requestType, $endpoint){
$this->logger->request($requestType,$endpoint);
return $this->graph->createRequest($requestType,$endpoint);
}
protected function validatePath($path){
$invalidChars=['"','*',':','<','>','?', '|'];
foreach ($invalidChars as $char){
if(strpos($path,$char)!==false){
throw new StorageException("Invalid character $char in path $path");
}
}
}
}
@@ -0,0 +1,22 @@
<?php
namespace As247\CloudStorages\Service;
use GuzzleHttp\Psr7\Utils;
/**
* Custom implement Psr7 Stream to fix AssemblyStream
* Google require min chunk size: 262144 but assembly stream may have smaller chunk size
* Eg NextCloud chunk size: 8192 and you will got following error
* Invalid request. The number of bytes uploaded is required to be equal or greater than 262144, except for the final request (it's recommended to be the exact multiple of 262144). The received request contained 8192 bytes, which does not meet this requirement.
* Class Stream
* @package As247\CloudStorages\Service
*/
class StreamWrapper
{
public static function wrap($stream,$options=[]){
return Utils::streamFor($stream,$options);
}
}