File: /home/slfopp7cb1df/www/conradinvestmentgroup.com/pm/app/Libraries/Google.php
<?php
namespace App\Libraries;
use Psr\Http\Message\RequestInterface;
use Google\Http\MediaFileUpload;
use GuzzleHttp\Psr7\Request;
class Google {
private $Settings_model;
public function __construct() {
$this->Settings_model = model("App\Models\Settings_model");
//load resources
require_once(APPPATH . "ThirdParty/Google/2-18-1/autoload.php");
}
//authorize connection
public function authorize() {
$client = $this->_get_client_credentials();
$this->_check_access_token($client, true);
}
//check access token
private function _check_access_token($client, $redirect_to_settings = false) {
//load previously authorized token from database, if it exists.
$accessToken = get_setting("google_drive_oauth_access_token");
if (get_setting("google_drive_authorized") && $accessToken && !$redirect_to_settings) {
$client->setAccessToken(json_decode($accessToken, true));
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
if ($redirect_to_settings) {
app_redirect("settings/integration/google_drive");
}
} else {
$authUrl = $client->createAuthUrl();
app_redirect($authUrl, true);
}
} else {
if ($redirect_to_settings) {
app_redirect("settings/integration/google_drive");
}
}
}
//fetch access token with auth code and save to database
public function save_access_token($auth_code) {
$client = $this->_get_client_credentials();
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($auth_code);
$error = get_array_value($accessToken, "error");
if ($error)
die($error);
$client->setAccessToken($accessToken);
// Save the token to database
$new_access_token = json_encode($client->getAccessToken());
if ($new_access_token) {
$this->Settings_model->save_setting("google_drive_oauth_access_token", $new_access_token);
//got the valid access token. store to setting that it's authorized
$this->Settings_model->save_setting("google_drive_authorized", "1");
}
//create parent folder
$this->_create_folder(get_setting('app_title'), "parent");
}
private function _does_folder_exist($service, $folder_id) {
try {
$file = $service->files->get($folder_id, array(
'fields' => 'id, name, mimeType, parents'
));
if ($file->getMimeType() === 'application/vnd.google-apps.folder') {
return true;
}
} catch (\Google_Service_Exception $e) {
// Handle API error
if ($e->getCode() == 404) {
return false; // Folder does not exist.
} else {
log_message('error', 'Google API Error: ' . $e->getMessage());
}
} catch (\Exception $e) {
log_message('error', 'Google Error: ' . $e->getMessage());
}
return null;
}
//check a folder if it exists or not
private function _is_folder_name_exists($service, $folder_name, $parent_folder_id = null) {
$exists = false;
$parent_folder_query = "";
if ($parent_folder_id) {
$parent_folder_query = " and '$parent_folder_id' in parents";
}
$parameters = array(
'fields' => 'files(id, name)',
'q' => "mimeType='application/vnd.google-apps.folder' and trashed=false $parent_folder_query",
'pageSize' => 1
);
$files = $service->files->listFiles($parameters);
$drive_folders = array_column((array) $files->files, 'name');
if (in_array($folder_name, $drive_folders)) {
$exists = true;
}
return $exists;
}
//save all the folders and temporary files ID into database as serialized data
private function _save_id($name = "", $id = "", $type = "folder", $path_type = "node") {
if ($path_type == "parent") {
//save parent folder id
//save it individually because app title might be change later
$this->Settings_model->save_setting("google_drive_parent_folder_id", $id);
} else {
$final_ids = array();
if ($type == "folder") {
$setting_name = "google_drive_folder_ids";
$ids = get_setting($setting_name);
} else {
$setting_name = "google_drive_temp_file_ids";
$ids = get_setting($setting_name);
}
if (!empty($ids) && is_array(@unserialize($ids))) {
$final_ids = unserialize($ids);
}
$final_ids[$name] = $id;
$this->Settings_model->save_setting($setting_name, serialize($final_ids));
}
}
//download file
public function download_file($file_id = "") {
$service = $this->_get_drive_service();
$response = $service->files->get($file_id, array(
'alt' => 'media'
));
return $response->getBody()->getContents();
}
//get file content
public function get_file_content($file_id = "") {
try {
$service = $this->_get_drive_service();
$response = $service->files->get($file_id, array(
'alt' => 'media'
));
$content_type_header = $response->getHeader('Content-Type');
$mime_type = "";
if ($content_type_header) {
$mime_type = $content_type_header[0];
}
return array("mime_type" => $mime_type, "contents" => $response->getBody()->getContents());
} catch (\Exception $e) {
return json_decode($e->getMessage(), true);
}
}
//get service
private function _get_drive_service() {
$client = $this->_get_client_credentials();
$this->_check_access_token($client);
return new \Google_Service_Drive($client);
}
//get folder and temp file ID
private function _get_saved_folder_id($path) {
$save_ids = get_setting("google_drive_folder_ids");
if ($save_ids && $path) {
$ids = unserialize($save_ids);
return get_array_value($ids, $path); //path could be folder name
}
}
private function _get_id($name = "", $type = "folder") {
if ($type == "folder") {
$stored_ids = get_setting("google_drive_folder_ids");
} else {
$stored_ids = get_setting("google_drive_temp_file_ids");
}
$ids = $stored_ids ? unserialize($stored_ids) : array();
$file_id = null;
//for temp file id, remove old one
if ($type == "file" && get_array_value($ids, $name)) {
$file_id = get_array_value($ids, $name);
$final_ids = $ids;
unset($final_ids[$name]);
$this->Settings_model->save_setting("google_drive_temp_file_ids", serialize($final_ids));
}
return $file_id;
}
//create folder
private function _create_folder($folder_name = "", $path_type = "node", $parent_folder_id = null) {
$service = $this->_get_drive_service();
if (!$this->_is_folder_name_exists($service, $folder_name, $parent_folder_id)) {
$file = new \stdClass();
if ($path_type == "parent") {
//create parent folder with app title
//in future, all uploads will be into this folder
$fileMetadata = new \Google_Service_Drive_DriveFile(
array(
'name' => $folder_name,
'mimeType' => 'application/vnd.google-apps.folder'
)
);
$file = $service->files->create($fileMetadata, array('fields' => 'id'));
} else {
//this are the node folders
if ($parent_folder_id) {
//check if the parent folder exists
$fileMetadata = new \Google_Service_Drive_DriveFile(array(
'name' => $folder_name,
'parents' => array($parent_folder_id),
'mimeType' => 'application/vnd.google-apps.folder',
));
$file = $service->files->create($fileMetadata, array(
'uploadType' => 'multipart',
'fields' => 'id'
));
} else {
$this->_create_folder(get_setting('app_title'), "parent");
}
}
//save folder id
if (isset($file->id)) {
$this->_save_id($folder_name, $file->id, "folder", $path_type);
return $file->id;
} else {
return "";
}
} else {
return $this->_get_id($folder_name);
}
}
//upload file to temp folder
public function upload_file($temp_file, $file_name, $folder_name = "", $file_content = "", $file_size = 0) {
$service = $this->_get_drive_service();
$parent_folder_id = get_setting("google_drive_parent_folder_id");
if (!$parent_folder_id) {
$parent_folder_id = $this->_create_folder(get_setting('app_title'), "parent");
}
$folder_id = $this->_get_saved_folder_id($folder_name);
if (!$folder_id) {
$folder_id = $this->_create_folder($folder_name, "node", $parent_folder_id);
}
$meta = array(
'name' => $file_name,
'parents' => array($folder_id)
);
$fileMetadata = new \Google_Service_Drive_DriveFile($meta);
$google_drive_file_id = "";
if ($file_content) {
$finfo = new \finfo(FILEINFO_MIME);
$mime_type = $finfo->buffer($file_content);
$file = $service->files->create($fileMetadata, array(
'data' => $file_content,
'mimeType' => $mime_type,
'uploadType' => 'multipart',
'fields' => 'id'
));
$google_drive_file_id = $file->id;
} else {
$google_drive_file_id = $this->_upload_file_chunk_wise($temp_file, $meta, $file_size);
if ($google_drive_file_id == "folder_does_not_exist") {
//can't upload file since the parent folder does not exist.
if ($this->_does_folder_exist($service, $folder_id) === false) {
//check if the root folder exist or not.
$new_parent_folder_id = null;
$parent_folder_exist = $this->_does_folder_exist($service, $parent_folder_id);
if ($parent_folder_exist === false) {
$new_parent_folder_id = $this->_create_folder(get_setting('app_title'), "parent");
} else if ($parent_folder_exist) {
$new_parent_folder_id = $parent_folder_id;
}
if ($new_parent_folder_id) {
$this->_create_folder($folder_name, "node", $new_parent_folder_id);
}
return false;
//we can try to upload the file here but it could lead to infinite loop
//so, we'll not do it.
//when user will try to upload the next file, everything should work fine
//also it requires to reload the settings to get the parent folder id
}
}
}
if (!$google_drive_file_id) {
return false;
}
$this->_make_file_as_public($service, $google_drive_file_id);
//save id's for temp files
if ($folder_name == "temp") {
$this->_save_id($file_name, $google_drive_file_id, "file");
} else {
return array("file_name" => $file_name, "file_id" => $google_drive_file_id, "service_type" => "google");
}
}
private function _upload_file_chunk_wise($temp_file, $meta, $file_size = 0) {
try {
$mime_type = mime_content_type($temp_file);
$client = $this->_get_client_credentials();
$this->_check_access_token($client);
$request_body = $meta;
$request_body["mimeType"] = $mime_type;
$access_token = get_array_value($client->getAccessToken(), "access_token");
//Note:
//The suggested url by google drive documentaion is https://www.googleapis.com/upload/drive/v3/files?uploadType=media.
//https://developers.google.com/drive/api/guides/manage-uploads
//But there might be any bug in the MediaFileUpload. So, use the following url:
//https://www.googleapis.com/drive/v3/files?uploadType=resumable
$request = new Request(
'POST',
'https://www.googleapis.com/drive/v3/files?uploadType=resumable',
array(
"Authorization" => 'Bearer ' . $access_token,
"Content-Type" => "application/json"
),
json_encode($request_body)
);
$chunk_size = 1024 * 1024 * 3; // 3MB
$media_file = new MediaFileUpload(
$client,
$request,
"application/json",
json_encode($request_body),
true,
$chunk_size
);
$media_file->setFileSize($file_size);
$temp_file_content = fopen($temp_file, 'r');
while (!feof($temp_file_content)) {
$chunk = fread($temp_file_content, $chunk_size);
$media_file->nextChunk($chunk);
}
fclose($temp_file_content);
// The upload is complete.
//find the file id
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $media_file->getResumeUri());
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, "file=1"); //could be any data.
$headers = array(
"Authorization" => "Bearer $access_token",
"Content-Type" => "applicaito/json",
"Content-length" => 6 //google drive api requires a content lenght for post re
);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
$result = curl_exec($curl);
curl_close($curl);
if ($result) {
$result = json_decode($result);
return $result->id;
}
} catch (\Google_Service_Exception $e) {
if ($e->getCode() == 404) {
return "folder_does_not_exist";
log_message('error', 'Can not upload file to google drive. Folder does not exist.');
} else {
log_message('error', 'Google API Error: ' . $e->getMessage());
}
} catch (\Exception $e) {
log_message('error', 'Google file uploading error: ' . $e->getMessage());
}
return null;
}
//make drive file as public
private function _make_file_as_public($service, $file_id = "") {
$permission = new \Google_Service_Drive_Permission(array(
'type' => 'anyone',
'role' => 'reader'
));
$service->permissions->create($file_id, $permission);
}
//move temp files to permanent directory
public function move_temp_file($file_name, $new_filename, $folder_name) {
$service = $this->_get_drive_service();
$fileId = $this->_get_id($file_name, "file");
if (!$fileId) {
log_message('error', 'Temp file not found. ' . $fileId);
exit();
}
$parent_folder_id = get_setting("google_drive_parent_folder_id");
$folder_id = $this->_get_saved_folder_id($folder_name);
$emptyFileMetadata = new \Google_Service_Drive_DriveFile();
// Retrieve the existing parents to remove
$file = $service->files->get($fileId, array('fields' => 'parents'));
$previousParents = join(',', $file->parents);
try {
// Move the file to the new folder
$file = $service->files->update($fileId, $emptyFileMetadata, array(
'addParents' => $folder_id,
'removeParents' => $previousParents,
'fields' => 'id, parents'
));
} catch (\Google_Service_Exception $e) {
if ($e->getCode() == 404) {
//the parent folder does not exist
//recreate the parent folder and try again.
if ($this->_does_folder_exist($service, $folder_id) === false) {
$folder_id = $this->_create_folder($folder_name, "node", $parent_folder_id);
if ($folder_id && $folder_id != "folder_does_not_exist") {
$file = $service->files->update($fileId, $emptyFileMetadata, array(
'addParents' => $folder_id,
'removeParents' => $previousParents,
'fields' => 'id, parents'
));
}
}
} else {
log_message('error', 'Google API Error: ' . $e->getMessage());
}
} catch (\Exception $e) {
log_message('error', 'Google file uploading error: ' . $e->getMessage());
}
//rename file with new name
$this->_rename_file($service, $fileId, $new_filename);
return array("file_name" => $new_filename, "file_id" => $fileId, "service_type" => "google");
}
//rename file
private function _rename_file($service, $file_id, $new_filename) {
$file = new \Google_Service_Drive_DriveFile();
$file->setName($new_filename);
$service->files->update($file_id, $file, array(
'fields' => 'name'
));
}
//delete file
public function delete_file($file_id) {
$service = $this->_get_drive_service();
$service->files->delete($file_id);
}
//get client credentials
private function _get_client_credentials() {
$url = get_uri("google_api/save_access_token");
$client = new \Google_Client();
$client->setApplicationName(get_setting('app_title'));
$client->setRedirectUri($url);
$client->setClientId(get_setting('google_drive_client_id'));
$client->setClientSecret(get_setting('google_drive_client_secret'));
$client->setScopes(\Google_Service_Drive::DRIVE);
$client->setAccessType("offline");
$client->setPrompt('select_account consent');
return $client;
}
}