assets_url = WPMCS_ASSETS_URL; $this->version = WPMCS_VERSION; $this->token = WPMCS_TOKEN; // Initialize setup $this->init(); } /** * Initialise Client */ public function init() { $this->settings = Utils::get_settings(); $this->credentials = Utils::get_credentials(); $this->config = isset($this->credentials['config']) && !empty($this->credentials['config']) ? $this->credentials['config'] : []; $this->bucketConfig = isset($this->credentials['bucketConfig']) && !empty($this->credentials['bucketConfig']) ? $this->credentials['bucketConfig'] : []; $this->bucket_name = isset($this->bucketConfig['bucket_name']) && !empty($this->bucketConfig['bucket_name']) ? $this->bucketConfig['bucket_name'] : ''; $this->cdnConfig = isset($this->credentials['cdn']) && !empty($this->credentials['cdn']) ? $this->credentials['cdn'] : []; if ( isset($this->config['config_json']) && !empty($this->config['config_json']) && isset($this->config['config_json']['path']) && !empty($this->config['config_json']['path']) && isset($this->bucket_name) && !empty($this->bucket_name) ) { if(file_exists($this->config['config_json']['path'])){ // Set google client $this->gcloudClient = new StorageClient([ 'keyFilePath' => $this->config['config_json']['path'], ]); // Set bucket object $this->bucket = $this->gcloudClient->bucket($this->bucket_name); } else { add_action('admin_notices', function (){ echo wp_kses_post(sprintf( "

%s:
Google Cloud Storage configuration file missing from the directory. It may break the media url's as well as media uploads.
Re-configure plugin to fix the issue.

", esc_html__('Media Cloud Sync', 'media-cloud-sync'), admin_url('admin.php?page='.$this->token . '-admin-ui#/configure') )); }); } } } /** * Verify Credentials * @since 1.0.0 * @return boolean */ public function verifyCredentials( $config_file ){ if ( isset($config_file) && !empty($config_file) && isset($config_file['path']) && !empty($config_file['path']) && file_exists($config_file['path']) ) { try { $googleClient = new StorageClient([ 'keyFilePath' => $config_file['path'], ]); $result = [ 'success' => false, 'code' => 200, 'message' => esc_html__('Please check the authorization details', 'media-cloud-sync'), ]; try { $bucket = $googleClient->bucket($this->token . '_dummy-bucket-for-auth-check'); $exists = $bucket->exists(); // Triggers the API call // If we reach here, the credentials are valid $result = [ 'success' => true, 'code' => 200, 'message' => esc_html__('Credentials are valid', 'media-cloud-sync'), ]; } catch (ServiceException $e) { $statusCode = $e->getCode(); $validErrors = [200, 403, 404]; if (in_array($statusCode, $validErrors)) { // If we reach here, the credentials are valid $result = [ 'success' => true, 'code' => 200, 'message' => esc_html__('Credentials are valid', 'media-cloud-sync'), ]; } } if($result['success'] == false) { return $result; } try { $buckets = $googleClient->buckets(); $newBucketFormat = []; if(isset($buckets) && !empty($buckets)){ foreach($buckets as $bucket) { $name = $bucket->name(); if(!empty($name)) { // Fetch the bucket's metadata $bucketInfo = $bucket->info(); $newBucketFormat[] = ['Name' => $name, 'CreationDate' => $bucketInfo['timeCreated']]; } } } $result['buckets_data']['buckets'] = $newBucketFormat; $result['buckets_data']['message'] = esc_html__('Buckets listed successfully', 'media-cloud-sync'); $result['buckets_data']['status'] = true; } catch (Exception $e) { $result ['buckets_data']['buckets'] = []; $result ['buckets_data']['message'] = esc_html__('Unable to list buckets, Please check the bucket listing permission', 'media-cloud-sync'); $result ['buckets_data']['status'] = false; } return $result; } catch (Exception $ex) { return array('message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false); } } return array('message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false); } /** * Verify Bucket Exists * @since 1.0.0 * @return boolean */ public function verifyBucketExist( $config_file, $bucket_name ){ if ( !( isset($config_file['path']) && !empty($config_file['path']) && file_exists($config_file['path']) && !empty($bucket_name) ) ) { return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false]; } try { $googleClient = new StorageClient([ 'keyFilePath' => $config_file['path'], ]); try { $bucket = $googleClient->bucket($bucket_name); if ($bucket->exists()) { return [ 'message' => esc_html__('Bucket exists', 'media-cloud-sync'), 'code' => 200, 'success' => true, ]; } else { return [ 'message' => esc_html__('Bucket does not exist', 'media-cloud-sync'), 'code' => 200, 'success' => false, ]; } } catch (ServiceException $e) { return [ 'message' => esc_html__('Bucket does not exist or credentials are invalid: ', 'media-cloud-sync') . $e->getMessage(), 'code' => 200, 'success' => false, ]; } if($bucket_found) { return array('message' => esc_html__('Bucket exist', 'media-cloud-sync'), 'code' => 200, 'success' => true); } else { return array('message' => esc_html__("Bucket choosen does not exist / does not have read permission", 'media-cloud-sync'), 'code' => 200, 'success' => false); } } catch (Exception $ex) { return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false]; } } /** * Create Bucket * @since 1.0.0 * @return boolean */ public function createBucket( $config_file, $region, $bucket_name ){ if ( !( isset($config_file['path']) && !empty($config_file['path']) && file_exists($config_file['path']) && !empty($region) && !empty($bucket_name) ) ) { return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false]; } try { $googleClient = new StorageClient([ 'keyFilePath' => $config_file['path'], ]); // Create Bucket $bucket = $googleClient->createBucket($bucket_name, [ 'location' => $region, 'iamConfiguration' => [ 'uniformBucketLevelAccess' => [ 'enabled' => true ] ] ]); // Fetch the bucket's IAM try { $iam = $bucket->iam(); $policy = $iam->policy(); // Add allUsers as a Storage Object Viewer $policy['bindings'][] = [ 'role' => 'roles/storage.objectViewer', 'members' => ['allUsers'], ]; // Set the updated policy $iam->setPolicy($policy); } catch (ServiceException $e) { return [ 'message' => esc_html__('Bucket created successfully. But failed to set IAM policy.', 'media-cloud-sync'), 'data' => [ 'Name' => $bucket_name, 'CreationDate' => date('Y-m-d\TH:i:s\Z'), ], 'code' => 200, 'success' => true, ]; } catch (Exception $e) { return [ 'message' => esc_html__('Bucket created successfully. But failed to set IAM policy.', 'media-cloud-sync'), 'data' => [ 'Name' => $bucket_name, 'CreationDate' => date('Y-m-d\TH:i:s\Z'), ], 'code' => 200, 'success' => true, ]; } return [ 'message' => esc_html__('Bucket created successfully.', 'media-cloud-sync'), 'data' => [ 'Name' => $bucket_name, 'CreationDate' => date('Y-m-d\TH:i:s\Z'), ], 'code' => 200, 'success' => true, ]; } catch (Exception $ex) { return ['message' => $e->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false]; } } /** * Check Bucket Write Permission * @since 1.0.0 */ public function verifyObjectWritePermission($config_file, $bucket_name){ if ( !( isset($config_file['path']) && !empty($config_file['path']) && file_exists($config_file['path']) && !empty($bucket_name) ) ) { return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false]; } try { $googleClient = new StorageClient([ 'keyFilePath' => $config_file['path'], ]); $bucket = $googleClient->bucket($bucket_name); if ($bucket->exists()) { $bucket_found = true; } else { return ['message' => esc_html__('No Buckets found', 'media-cloud-sync'), 'code' => 200, 'success' => false]; } if ($bucket_found) { $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', ''); // Prepare a temporary file with content to check write permission $stream = fopen('php://temp', 'r+'); fwrite($stream, 'This is a test object to check write permission.'); rewind($stream); // Upload the object to the bucket $object = $bucket->upload( $stream, [ 'name' => $object_key, ] ); if(is_resource($stream)) { fclose($stream); } if ($object->exists()) { return ['message' => esc_html__('Bucket write permission verified successfully', 'media-cloud-sync'), 'code' => 200, 'success' => true]; } else { return ['message' => esc_html__('Bucket write permission not verified', 'media-cloud-sync'), 'code' => 200, 'success' => false]; } } } catch (Exception $ex) { return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false]; } } /** * Check Bucket Delete Permission * @since 1.0.0 */ public function verifyObjectDeletePermission($config_file, $bucket_name){ if ( !( isset($config_file['path']) && !empty($config_file['path']) && file_exists($config_file['path']) && !empty($bucket_name) ) ) { return ['message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), 'code' => 200, 'success' => false]; } try { $googleClient = new StorageClient([ 'keyFilePath' => $config_file['path'], ]); $bucket = $googleClient->bucket($bucket_name); try { $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', ''); // Try fetching a dummy object to test access $object = $bucket->object($object_key); if ($object->exists()) { $object->delete(); if (!$object->exists()) { return [ 'message' => esc_html__('Bucket exists', 'media-cloud-sync'), 'code' => 200, 'success' => true, ]; } else { return [ 'message' => esc_html__('You do not have permission to delete object', 'media-cloud-sync'), 'code' => 200, 'success' => false, ]; } } else { return [ 'message' => esc_html__('Object does not exist', 'media-cloud-sync'), 'code' => 200, 'success' => false, ]; } } catch (Exception $ex) { return [ 'message' => esc_html__('Object does not exist or credentials are invalid: ', 'media-cloud-sync') . $e->getMessage(), 'code' => 200, 'success' => false, ]; } } catch (Exception $ex) { return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false]; } } /** * Check Bucket Read Permission * @since 1.2.4 */ public function verifyObjectReadPermission() { $result = [ 'status' => false, 'message' => '', 'lastChecked' => time(), ]; if (empty($this->gcloudClient) || empty($this->bucket_name)) { $result['message'] = esc_html__('Please check the authorization details', 'media-cloud-sync'); Utils::set_status('cdnRead', $result); return [ 'message' => $result['message'], 'code' => 200, 'success' => false, 'lastChecked' => $result['lastChecked'], ]; } try { $object_key = Utils::generate_object_key($this->token . '_dummy-object-for-bucket-permission-check', ''); // Check if the object was created successfully if (!$this->bucket->object($object_key)->exists()) { // Create a dummy object to check write permission $stream = fopen('php://temp', 'r+'); fwrite($stream, 'This is a test object to check read permission.'); rewind($stream); $this->bucket->upload( $stream, [ 'name' => $object_key, ] ); if (is_resource($stream)) { fclose($stream); } // Re-check if the object was created successfully if (!$this->bucket->object($object_key)->exists()) { $result['status'] = false; $result['message'] = esc_html__('Failed to create an object for read permission check, please check service configuration', 'media-cloud-sync'); Utils::set_status('cdnRead', $result); return [ 'message' => $result['message'], 'code' => 200, 'success' => false, 'lastChecked' => $result['lastChecked'], ]; } } $url = $this->generate_file_url($object_key); $cdn_url = Cdn::may_generate_cdn_url($url, $object_key); $headers = @get_headers($cdn_url); if (strpos($headers[0], '200') !== false) { $result['status'] = true; $result['message'] = esc_html__('Objects are accessible to Read', 'media-cloud-sync'); } else if (strpos($headers[0], '403') !== false) { $result['status'] = false; if($this->cdnConfig['service'] == $this->service) { $result['message'] = esc_html__('Access Denied. Please check your bucket policy. Public Read Access is required.', 'media-cloud-sync'); } else { $result['message'] = esc_html__('Access Denied. Please check your bucket policy', 'media-cloud-sync'); } $result['message'] = esc_html__('Access Denied. Please check your bucket policy', 'media-cloud-sync'); } else if (strpos($headers[0], '404') !== false) { $result['status'] = false; $result['message'] = esc_html__('Object not found. Please check your bucket policy', 'media-cloud-sync'); } else if (strpos($headers[0], '500') !== false) { $result['status'] = false; $result['message'] = esc_html__('Internal Server error. Please check your bucket policy', 'media-cloud-sync'); } else { $result['status'] = false; $result['message'] = esc_html__('Objects are not accessible to read', 'media-cloud-sync'); } Utils::set_status('cdnRead', $result); $this->deleteSingle($object_key); return [ 'message' => $result['message'], 'code' => 200, 'success' => $result['status'], 'lastChecked' => $result['lastChecked'], ]; } catch (ServiceException $ex) { return ['message' => $ex->getMessage(), 'code' => 200, 'success' => false]; } catch (Exception $ex) { return ['message' => $ex->getMessage() ?? esc_html__('Please check the authorization details', 'media-cloud-sync'), 'code' => 200, 'success' => false]; } } /** * isConfigured Function To Identify the congfigurations are correct * @since 1.0.0 */ public function isConfigured(){ if ($this->gcloudClient) { try { $bucket = $this->gcloudClient->bucket($this->token . '_dummy-bucket-for-auth-check'); $exists = $bucket->exists(); // Triggers the API call return false; } catch (ServiceException $e) { $statusCode = $e->getCode(); $validErrors = [200, 403, 404]; if (in_array($statusCode, $validErrors)) { // If we reach here, the credentials are valid return true; } else { // If we reach here, the credentials are not valid return false; } } } return false; } /** * Make Object Private * @since 1.0.0 * */ public function toPrivate($key) { if(!$key) return false; try { $object = $this->bucket->object($key); if ($object->exists()) { $object->update(['acl' => []], ['predefinedAcl' => 'private']); return true; } } catch (ServiceException $e) { // Handle exception if needed return false; } catch (Exception $e) { // Handle other exceptions if needed return false; } return false; } /** * Make Object Public * @since 1.0.0 * */ public function toPublic($key) { if(!$key) return false; try { $object = $this->bucket->object($key); if ($object->exists()) { $object->update(['acl' => []], ['predefinedAcl' => 'publicRead']); return true; } return false; } catch (ServiceException $e) { // Handle exception if needed return false; } catch (Exception $e) { // Handle other exceptions if needed return false; } } /** * Check the object exist * @since 1.1.8 */ public function exists($key) { if(!$key) return false; try { $object = $this->bucket->object($key); if ($object->exists()) { return true; } else { return false; } } catch (ServiceException $e) { // Handle exception if needed return false; } catch (Exception $e) { // Handle other exceptions if needed return false; } return false; } /** * Upload Single * @since 1.0.0 * @return boolean */ public function uploadSingle($media_absolute_path, $media_path, $prefix=''){ $result = array(); if ( isset($media_absolute_path) && !empty($media_absolute_path) && isset($media_path) && !empty($media_path) ) { $file_name = wp_basename( $media_path ); if ($file_name) { $upload_path = Utils::generate_object_key($media_path, $prefix); // Decide Multipart upload or normal put object if (filesize($media_absolute_path) <= Schema::getConstant('GCLOUD_MULTIPART_MIN_FILE_SIZE')) { // Upload a publicly accessible file. The file size and type are determined by the SDK. try { $upload = $this->bucket->upload( fopen($media_absolute_path, 'r'), [ 'name' => $upload_path, ] ); $object = $this->bucket->object($upload_path); if ($object->exists()) { $result = array( 'success' => true, 'code' => 200, 'file_url' => $this->generate_file_url($upload_path), 'key' => $upload_path, 'message' => esc_html__('File Uploaded Successfully', 'media-cloud-sync'), ); } else { $result = array( 'success' => false, 'code' => 200, 'message' => esc_html__('Object not found at server.', 'media-cloud-sync'), ); } } catch (Exception $e) { $result = array( 'success' => false, 'code' => 200, 'message' => $e->getMessage(), ); } } else { try { $upload = $this->bucket->upload( fopen($media_absolute_path, 'r'), [ 'name' => $upload_path, 'chunkSize' => 262144 * 2, ] ); $object = $this->bucket->object($upload_path); if ($object->exists()) { $result = array( 'success' => true, 'code' => 200, 'file_url' => $this->generate_file_url($upload_path), 'key' => $upload_path, 'message' => esc_html__('File Uploaded Successfully', 'media-cloud-sync'), ); } else { $result = array( 'success' => false, 'code' => 200, 'message' => esc_html__('Something happened while uploading to server', 'media-cloud-sync'), ); } } catch (Exception $e) { $result = array( 'success' => false, 'code' => 200, 'message' => $e->getMessage(), ); } } } else { $result = array( 'success' => false, 'code' => 200, 'message' => esc_html__('Check the file you are trying to upload. Please try again', 'media-cloud-sync'), ); } } else { $result = array( 'success' => false, 'code' => 200, 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), ); } return $result; } /** * Save object to server * @since 1.0.0 */ public function object_to_server($key, $save_path){ try { $object = $this->bucket->object($key); if ($object->exists()) { $object->downloadToFile($save_path); if (file_exists($save_path)) { return true; } } } catch (Exception $e) { return false; } return false; } /** * Delete Single * @since 1.0.0 * @return boolean */ public function deleteSingle($key){ $result = array(); if (isset($key) && !empty($key)) { try { $object = $this->bucket->object($key); $object->delete(); if (!$object->exists()) { $result = array( 'success' => true, 'code' => 200, 'message' => esc_html__('Deleted Successfully', 'media-cloud-sync'), ); } else { $result = array( 'success' => false, 'code' => 200, 'message' => esc_html__('File not deleted', 'media-cloud-sync'), ); } } catch (Exception $e) { $result = array( 'success' => false, 'code' => 200, 'message' => $e->getMessage(), ); } } else { $result = array( 'success' => false, 'code' => 200, 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), ); } return $result; } /** * get presigned URL * @since 1.0.0 * @return boolean */ public function get_presigned_url($key) { $result = array(); if (isset($key) && !empty($key)) { try { $object = $this->bucket->object($key); $expires = isset($this->settings['presigned_expire']) ? $this->settings['presigned_expire'] : 20; $presignedUrl = $object->signedUrl(new \DateTime(sprintf('+%s minutes', $expires))); if ($presignedUrl) { $result = array( 'success' => true, 'code' => 200, 'file_url' => $presignedUrl, 'message' => esc_html__('Got Presigned URL Successfully', 'media-cloud-sync'), ); } else { $result = array( 'success' => false, 'code' => 200, 'message' => esc_html__('Error getting presigned URL', 'media-cloud-sync'), ); } } catch (Exception $e) { $result = array( 'success' => false, 'code' => 200, 'message' => $e->getMessage(), ); } } else { $result = array( 'success' => false, 'code' => 200, 'message' => esc_html__('Insufficient Data. Please try again', 'media-cloud-sync'), ); } return $result; } /** * Generate file URL */ private function generate_file_url($key){ $domain = $this->get_domain(); return apply_filters('wpmcs_generate_google_file_url', $domain . '/' . $this->bucket_name . '/' . $key, $domain, $key, $this->bucket_name ); } /** * Get domain URL */ public function get_domain() { $url_base = 'https://storage.googleapis.com'; return $url_base; } }