bucket = $args[ 'bucket' ];
$this->key_json = json_decode($args['key_json'], 1);
// May be Loading Google SDK....
if( !class_exists( '\wpCloud\StatelessMedia\Google_Client\Google_Client' ) ) {
include_once( ud_get_stateless_media()->path('lib/Google/vendor/autoload.php', 'dir') );
}
/* Initialize our client */
$this->client = new \wpCloud\StatelessMedia\Google_Client\Google_Client();
// We're supporting Google SDK 1.X version since
// The plugins which also are using Google SDK may have its old version
// what may cause conflicts
//
if( version_compare( $this->client->getLibraryVersion(), '2.0', '<' ) ) {
// We should set the warning about potential issue
// If Google SDK has different version with already included
$this->_setWarning();
$wp_upload_dir = wp_upload_dir();
$dir = $wp_upload_dir[ 'path' ];
$filename = md5( wp_generate_password() ) . '.tmp';
$path = wp_normalize_path( $dir . '/' .$filename );
@file_put_contents( $path, json_encode( $this->key_json ) );
$cred = $this->client->loadServiceAccountJson( $path, ['https://www.googleapis.com/auth/devstorage.full_control'] );
$this->client->setAssertionCredentials($cred);
if ($this->client->getAuth()->isAccessTokenExpired()) {
$this->client->getAuth()->refreshTokenWithAssertion($cred);
}
@unlink( $path );
} else {
// May be delete warning transient if it was set
$this->_deleteWarning();
$this->client->setAuthConfig($this->key_json);
}
if( isset( $current_blog ) && isset( $current_blog->domain ) ) {
$this->client->setApplicationName( $current_blog->domain );
} else {
$this->client->setApplicationName( urlencode( str_replace( array( 'http://', 'https://' ), '', get_bloginfo( 'url' ) ) ) );
}
$this->client->setScopes(['https://www.googleapis.com/auth/devstorage.full_control']);
// May be Loading Google SDK. Because some bad plugins may load their Google SDK with not included Google_Service_Storage.
if( !class_exists( 'Google_Service_Storage' ) ) {
include_once( ud_get_stateless_media()->path('lib/Google/vendor/autoload.php', 'dir') );
}
/* Now, Initialize our Google Storage Service */
$this->service = new \wpCloud\StatelessMedia\Google_Client\Google_Service_Storage( $this->client );
}
/**
* Wrapper for listObjects()
*/
public function list_objects( $options = array() ) {
$options = wp_parse_args( $options, array(
'delimiter' => '',
'maxResults' => 1000,
'pageToken' => '',
'prefix' => '',
'projection' => 'noAcl',
'versions' => false
) );
return $this->service->objects->listObjects( $this->bucket, $options );
}
/**
* List all items page by page of maxResults
* @param $bucket
* @param array $options
* @return mixed
*/
public function list_all_objects( $options = array() ) {
$options = wp_parse_args( $options, array(
'delimiter' => '',
'maxResults' => 1000,
'pageToken' => '',
'prefix' => '',
'projection' => 'noAcl',
'versions' => false
) );
$response = $this->service->objects->listObjects( $this->bucket, $options );
$this->temp_objects = array_merge( $this->temp_objects, $response->getItems() );
if ( !empty( $response->nextPageToken ) ) {
$options['pageToken'] = $response->nextPageToken;
return $this->list_all_objects( $this->bucket, $options );
} else {
return $this->temp_objects;
}
}
/**
* Add/Update Media Object to Bucket
*
* @author peshkov@UD
* @param array $args
* @return bool
*/
public function add_media( $args = array() ) {
try {
@set_time_limit( -1 );
extract( $args = wp_parse_args( $args, array(
'force' => false,
'name' => false,
'absolutePath' => false,
'mimeType' => 'image/jpeg',
'metadata' => array(),
) ) );
/* Be sure file exists. */
if( !file_exists( $args['absolutePath'] ) ) {
return new \WP_Error( 'sm_error', __( 'Unable to locate file on disk', ud_get_stateless_media()->domain ) );
}
/* Set default name if parameter was not passed. */
if( empty( $name ) ) {
$name = basename( $args['name'] );
}
$name = apply_filters( 'wp_stateless_file_name', $name );
// If media exists we just return it
if ( !$args['force'] && $media = $this->media_exists( $name ) ) {
if($media->getCacheControl() != $args['cacheControl']){
$media->setCacheControl($args['cacheControl']);
$media = $this->service->objects->patch($this->bucket, $name, $media);
}
return get_object_vars( $media );
}
$media = new \wpCloud\StatelessMedia\Google_Client\Google_Service_Storage_StorageObject();
$media->setName($name);
$media->setMetadata($args['metadata']);
if (isset($args['cacheControl'])) {
$media->setCacheControl($args['cacheControl']);
}
if (isset($args['contentEncoding'])) {
$media->setContentEncoding($args['contentEncoding']);
}
if (isset($args['contentDisposition'])) {
$media->getContentDisposition($args['contentDisposition']);
}
/* Upload Media file to Google storage */
$media = $this->service->objects->insert($this->bucket, $media, array_filter(array(
'data' => file_get_contents($args['absolutePath']),
'uploadType' => 'media',
'mimeType' => $args['mimeType'],
'predefinedAcl' => 'bucketOwnerFullControl',
)));
/* Make Media Public READ for all on success */
if (is_object($media)) {
$acl = new \wpCloud\StatelessMedia\Google_Client\Google_Service_Storage_ObjectAccessControl();
$acl->setEntity('allUsers');
$acl->setRole('READER');
$this->service->objectAccessControls->insert($this->bucket, $name, $acl);
}
} catch( Exception $e ) {
return new WP_Error( 'sm_error', $e->getMessage() );
}
return get_object_vars( $media );
}
/**
* get or save media file
* @param $path
* @param bool $save
* @param bool $save_path
* @return bool|\Google_Service_Storage_StorageObject|int
*/
public function get_media( $path, $save = false, $save_path = false ) {
try {
$media = $this->service->objects->get($this->bucket, $path);
} catch ( \Exception $e ) {
return false;
}
if ( empty( $media->id ) ) return false;
if ( $save && $save_path ) {
if ( !file_exists( $_dir = dirname( $save_path ) ) ) {
wp_mkdir_p( $_dir );
}
return $this->client->getHttpClient()->get($media->getMediaLink(), ['save_to' => $save_path] )->getStatusCode();
}
return $media;
}
/**
* Check if media exists
* @param $path
* @return bool
*/
public function media_exists( $path ) {
try {
$media = $this->service->objects->get($this->bucket, $path);
// Here we wanted to check if access allowed, but noticed it actually sets this ACL... Leaving it as is. @author korotkov@ud
$this->service->objectAccessControls->get($this->bucket, $path, 'allUsers');
} catch ( \Exception $e ) {
return false;
}
if ( empty( $media->id ) ) return false;
return $media;
}
/**
* Fired for every file remove action
*
* @author peshkov@UD
* @param string $name
* @return bool
*/
public function remove_media( $name ) {
try {
$name = apply_filters( 'wp_stateless_file_name', $name );
$this->service->objects->delete( $this->bucket, $name );
} catch( Exception $e ) {
return new WP_Error( 'sm_error', $e->getMessage() );
}
return true;
}
/**
* Tests connection to Google Storage
* by trying to get passed bucket's data.
*
* @author peshkov@UD
*/
public function is_connected() {
try {
$this->service->buckets->get( $this->bucket );
} catch( Exception $e ) {
return $e;
}
return true;
}
/**
* Determine if instance already exists and Return Instance
*
* @param array $args
*
* $args
* @param string client_id
* @param string service_account_name
* @param string key_file_path
*
* @author peshkov@UD
* @return \wpCloud\StatelessMedia\GS_Client
*/
public static function get_instance( $args ) {
if( null === self::$instance ) {
try {
if( empty( $args[ 'bucket' ] ) ) {
throw new Exception( __( 'Bucket parameter must be provided.' ) );
}
$json = "{}";
if ( !empty( $args[ 'key_json' ] ) ) {
$json = json_decode($args['key_json']);
}
if( !$json || !property_exists($json, 'private_key') ){
throw new Exception( __( 'Service Account JSON is invalid.' ) );
}
self::$instance = new self( $args );
} catch( Exception $e ) {
return new WP_Error( 'sm_error', $e->getMessage() );
}
}
return self::$instance;
}
/**
* Set warning about potential conflict with Google SDK
*
* @since 2.0.1
*/
private function _setWarning() {
$reflector = new \ReflectionClass('Google_Client');
$pluginBasename = wp_normalize_path( plugin_basename( $reflector->getFileName() ) );
// Check if get_plugins() function exists. This is required on the front end of the
// site, since it is in a file that is normally only loaded in the admin.
if ( ! function_exists( 'get_plugins' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$pluginBasenameParts = explode( '/', $pluginBasename );
$pluginName = __( "UNDEFINED", ud_get_stateless_media()->domain );
foreach( get_plugins() as $path => $meta ) {
if( strpos( $path, trailingslashit( $pluginBasenameParts[0] ) ) === 0 ) {
$pluginName = $meta['Name'];
}
};
$error = sprintf( __( "%s plugin may have potential Google SDK version conflicts with %s plugin. %s is using Google SDK %s, when %s loads old Google SDK version %s.", ud_get_stateless_media()->domain ),
"" . 'WP-Stateless' . "",
"" . $pluginName . "",
'WP-Stateless',
"v2.0",
$pluginName,
"v" . \wpCloud\StatelessMedia\Google_Client\Google_Client::LIBVER . ""
);
set_transient( "wp_stateless_google_sdk_conflict", $error );
}
/**
* Removes Warning if it exists
*
*/
private function _deleteWarning() {
delete_transient( "wp_stateless_google_sdk_conflict" );
}
}
}
}