File: /home/zeusxp5/lrliberia.com/wp-content/plugins/boldgrid-backup-premium/admin/remote/amazon_s3.php
<?php // phpcs:ignore
/**
* Amazon S3 class.
*
* @link https://www.boldgrid.com
* @since 1.0.0
*
* @package Boldgrid_Backup
* @subpackage Boldgrid_Backup/admin/remote
* @copyright BoldGrid
* @author BoldGrid <support@boldgrid.com>
*/
use Aws\S3\S3Client;
use Aws\S3\Model\MultipartUpload\UploadBuilder;
use Aws\Common\Exception\MultipartUploadException;
use Aws\Common\Model\MultipartUpload\AbstractTransfer;
/**
* Amazon S3 class.
*
* @since 1.0.0
*/
class Boldgrid_Backup_Premium_Admin_Remote_Amazon_S3 {
/**
* Backups page.
*
* @since 1.0.0
* @var Boldgrid_Backup_Premium_Admin_Remote_Amazon_S3_Backups_Page.
*/
public $backups_page;
/**
* Bucket object.
*
* @since 1.0.0
* @var Boldgrid_Backup_Premium_Admin_Remote_Amazon_S3_Bucket.
*/
public $bucket;
/**
* Bucket id.
*
* @since 1.0.0
* @var string $bucket_id
*/
public $bucket_id = null;
/**
* Our S3 client.
*
* @since 1.0.0
* @var object $client
*/
public $client = null;
/**
* Errors.
*
* @since 1.5.4
* @var array
*/
public $errors = array();
/**
* Nickname.
*
* @since 1.0.0
* @var string
*/
public $nickname;
/**
* Title.
*
* @since 1.0.0
* @var string
*/
public $title = 'Amazon S3';
/**
* Title attribute.
*
* Used as a title tag attribute.
*
* @since 1.0.0
* @var string
*/
public $title_attr;
/**
* The core class object.
*
* @since 1.0.0
* @access private
* @var Boldgrid_Backup_Admin_Core
*/
private $core;
/**
* An instance of Boldgrid_Backup_Premium_Admin_Core.
*
* @since 1.0.0
* @access private
* @var Boldgrid_Backup_Premium_Admin_Core
*/
private $premium_core;
/**
* Retention count.
*
* @since 1.0.0
* @access private
* @var int $retention_count
*/
private $retention_count = 5;
/**
* Key.
*
* @since 1.0.0
* @access private
* @var string $key
*/
private $key = null;
/**
* Secret, I'm not telling.
*
* @since 1.0.0
* @access private
* @var string $secret
*/
private $secret = null;
/**
* Constructor.
*
* @since 1.0.0
*
* @param Boldgrid_Backup_Admin_Core $core Boldgrid_Backup_Admin_Core object.
* @param Boldgrid_Backup_Premium_Admin_Core $premium_core Boldgrid_Backup_Premium_Admin_Core object.
*/
public function __construct( Boldgrid_Backup_Admin_Core $core, Boldgrid_Backup_Premium_Admin_Core $premium_core ) {
$this->core = $core;
$this->premium_core = $premium_core;
$settings = $this->core->settings->get_settings();
$this->key = ! empty( $settings['remote']['amazon_s3']['key'] ) ?
$settings['remote']['amazon_s3']['key'] : $this->key;
$this->secret = ! empty( $settings['remote']['amazon_s3']['secret'] ) ?
$settings['remote']['amazon_s3']['secret'] : $this->secret;
$this->bucket_id = ! empty( $settings['remote']['amazon_s3']['bucket_id'] ) ?
$settings['remote']['amazon_s3']['bucket_id'] : $this->create_unique_bucket();
$this->retention_count = ! empty( $settings['remote']['amazon_s3']['retention_count'] ) ?
$settings['remote']['amazon_s3']['retention_count'] : $this->retention_count;
$this->nickname = ! empty( $settings['remote']['amazon_s3']['nickname'] ) ?
$settings['remote']['amazon_s3']['nickname'] : $this->nickname;
$this->title_attr = $this->title . ': ' . $this->bucket_id;
if ( ! empty( $this->nickname ) ) {
$this->title = $this->nickname;
}
$this->backups_page = new Boldgrid_Backup_Premium_Admin_Remote_Amazon_S3_Backups_Page( $this->core, $this->premium_core );
$this->bucket = new Boldgrid_Backup_Premium_Admin_Remote_Amazon_S3_Bucket( $this->core, $this->premium_core );
}
/**
* Add menu items.
*
* @since 1.0
*/
public function add_menu_items() {
$capability = 'administrator';
add_submenu_page(
'boldgrid-backup-settings',
__( 'Amazon S3 Settings', 'boldgrid-backup' ),
__( 'Amazon S3 Settings', 'boldgrid-backup' ),
$capability,
'boldgrid-backup-amazon-s3',
array(
$this,
'submenu_page',
)
);
}
/**
* Upload a backup via an ajax request.
*
* This is done via the archive details of a single archive.
*
* @since 1.0.0
*/
public function ajax_upload() {
if ( ! current_user_can( 'update_plugins' ) ) {
wp_send_json_error( __( 'Permission denied.', 'boldgrid-backup' ) );
}
if ( ! $this->core->archive_details->validate_nonce() ) {
wp_send_json_error( __( 'Invalid nonce.', 'boldgrid-backup' ) );
}
$filename = ! empty( $_POST['filename'] ) ? $_POST['filename'] : false; // phpcs:ignore
$filepath = $this->core->backup_dir->get_path_to( $filename );
if ( empty( $filename ) || ! $this->core->wp_filesystem->exists( $filepath ) ) {
wp_send_json_error( __( 'Invalid archive filename.', 'boldgrid-backup' ) );
}
// @todo Temp code to get more details about any errors.
add_action(
'shutdown',
function() {
$last_error = error_get_last();
// If there's no error or this is not fatal, abort.
if ( empty( $last_error ) || 1 !== $last_error['type'] ) {
return;
}
$message = sprintf(
'<strong>%1$s</strong>: %2$s in %3$s on line %4$s',
__( 'Fatal error', 'boldgrid-backup' ),
$last_error['message'],
$last_error['file'],
$last_error['line']
);
wp_send_json_error( $message );
}
);
$upload_result = $this->upload( $filepath );
if ( true === $upload_result ) {
wp_send_json_success( $upload_result );
} else {
wp_send_json_error( $upload_result );
}
}
/**
* Create a unique bucket id.
*
* When you delete a bucket, Amazon gives you the following message:
* Amazon S3 buckets are unique. If you delete this bucket, you may lose the
* bucket name to another AWS user.
*
* @since 1.0.0
*/
public function create_unique_bucket() {
$url = parse_url( get_site_url() ); // phpcs:ignore WordPress.WP.AlternativeFunctions
$bucket_parts[] = 'boldgrid-backup';
$bucket_parts[] = $url['host'];
$bucket_id = implode( '-', $bucket_parts );
return $bucket_id;
}
/**
* Download a backup from Amazon S3 and put it in backups folder.
*
* @since 1.5.4
*
* @param string $key Key.
* @param string $bucket_id Bucket id.
* @return bool
*/
public function download( $key, $bucket_id = null ) {
$bucket_id = is_null( $bucket_id ) ? $this->bucket_id : $bucket_id;
if ( empty( $bucket_id ) || empty( $key ) ) {
return false;
}
$this->set_client();
$path = $this->core->backup_dir->get_path_to( $key );
// Example $result: https://pastebin.com/thyw9jrh .
$result = $this->client->getObject(
array(
'Bucket' => $bucket_id,
'Key' => $key,
'SaveAs' => $path,
)
);
if ( empty( $result['ContentLength'] ) ) {
return false;
}
// Change the timestamp of the backup file.
$last_modified = ! empty( $result['Metadata']['last_modified'] ) ? $result['Metadata']['last_modified'] : null;
if ( ! empty( $last_modified ) ) {
$this->core->wp_filesystem->touch( $path, $last_modified );
}
// If the backup file is not valid, delete it.
$valid = $this->bucket->validate_backup( $key );
if ( ! $valid && ! empty( $this->bucket->errors ) ) {
$this->errors = array_merge( $this->errors, $this->bucket->errors );
$this->core->wp_filesystem->delete( $path );
}
$this->core->remote->post_download( $path );
return $valid;
}
/**
* Enforce retention.
*
* @since 1.0.0
*/
public function enforce_retention() {
if ( empty( $this->retention_count ) ) {
return;
}
$bucket_contents = $this->bucket->get( $this->bucket_id, true, false );
if ( empty( $bucket_contents ) ) {
return;
}
// Remove files from bucket list that are not our backups.
foreach ( $bucket_contents as $key => $item ) {
if ( empty( $item['Headers']['Metadata']['is_boldgrid_backup'] ) || 'true' !== $item['Headers']['Metadata']['is_boldgrid_backup'] ) {
unset( $bucket_contents[ $key ] );
}
}
// Sort by timestamp desc.
usort(
$bucket_contents,
function( $a, $b ) {
return $a['Headers']['Metadata']['last_modified'] < $b['Headers']['Metadata']['last_modified'] ? 1 : -1;
}
);
// Do the deleting.
$count = 0;
foreach ( $bucket_contents as $item ) {
$count++;
if ( $count <= $this->retention_count ) {
continue;
}
$this->client->deleteObject(
array(
'Bucket' => $this->bucket_id,
'Key' => $item['Key'],
)
);
/**
* Remote file deleted due to remote retention settings.
*
* @since 1.5.3
*/
do_action(
'boldgrid_backup_remote_retention_deleted',
'Amazon S3',
// Translators: 1: Bucket id, 2: Key.
sprintf( __( 'Bucket: %1$s, Key: %2$s', 'boldgrid-backup' ), $this->bucket_id, $item['Key'] )
);
}
$this->bucket->delete_transients();
}
/**
* Add Amazon S3 backups to the list of all backups.
*
* @since 1.0.0
*/
public function filter_get_all() {
$bucket_contents = $this->bucket->get( $this->bucket_id, true );
if ( empty( $bucket_contents ) ) {
return;
}
foreach ( $bucket_contents as $item ) {
$filename = $item['Key'];
$is_this_site = false !== strpos( $item['Key'], get_site_option( 'boldgrid_backup_id' ) );
$is_backup = ! empty( $item['Headers']['Metadata']['is_boldgrid_backup'] ) && 'true' === $item['Headers']['Metadata']['is_boldgrid_backup'];
if ( ! $is_backup || ! $is_this_site ) {
continue;
}
$backup = array(
'filename' => $filename,
'last_modified' => $item['Headers']['Metadata']['last_modified'],
'size' => $item['Size'],
'locations' => array(
array(
'title' => $this->title,
'title_attr' => $this->title_attr,
'on_remote_server' => true,
),
),
);
$this->core->archives_all->add( $backup );
}
}
/**
* Get the contents of a bucket.
*
* @since 1.0.0
*
* @param string $bucket_id Bucket id.
* @param bool $include_headers Include headers.
* @return array https://pastebin.com/uVkx8t5A
*/
public function get_bucket( $bucket_id, $include_headers = false ) {
$this->set_client();
$this->set_bucket_id( $bucket_id );
$bucket_contents = array();
$transient_name = sprintf(
'boldgrid_backup_s3_bucket_%1$s%2$s',
$include_headers ? 'w_headers_' : '',
$this->bucket_id
);
$bucket_contents = get_transient( $transient_name );
if ( false !== $bucket_contents ) {
return $bucket_contents;
}
// If the bucket does not exist, return an empty bucket.
try {
$iterator = $this->client->getIterator(
'ListObjects',
array(
'Bucket' => $this->bucket_id,
)
);
foreach ( $iterator as $object ) {
if ( $include_headers ) {
$object['Headers'] = $this->get_headers( $object['Key'] );
}
$bucket_contents[] = $object;
}
} catch ( Aws\S3\Exception\NoSuchBucketException $e ) {
return array();
}
set_transient( $transient_name, $bucket_contents, 5 * MINUTE_IN_SECONDS );
return $bucket_contents;
}
/**
* Get settings.
*
* @since 1.0.0
*/
public function get_details() {
$settings = $this->core->settings->get_settings();
$details = array(
'title' => __( 'Amazon S3', 'boldgrid-backup' ),
'key' => 'amazon_s3',
'configure' => 'admin.php?page=boldgrid-backup-amazon-s3',
'is_setup' => $this->is_setup(),
'enabled' => ! empty( $settings['remote']['amazon_s3']['enabled'] ) && $settings['remote']['amazon_s3']['enabled'] && $this->is_setup(),
);
return $details;
}
/**
* Get the headers of an object.
*
* @since 1.0.0
*
* @param string $key Key.
*/
public function get_headers( $key ) {
$params = array(
'Bucket' => $this->bucket_id,
'Key' => $key,
);
$headers = $this->client->headObject( $params );
return $headers->toArray();
}
/**
* Determine if a file exists in a bucket.
*
* @since 1.0.0
*
* @param string $bucket_id Bucket id.
* @param string $filepath File path.
* @return bool
*/
public function in_bucket( $bucket_id, $filepath ) {
$this->set_client();
if ( empty( $this->client ) ) {
return false;
}
$bucket_contents = $this->bucket->get( $bucket_id );
$filename = basename( $filepath );
foreach ( $bucket_contents as $item ) {
if ( $item['Key'] === $filename ) {
return true;
}
}
return false;
}
/**
* Set our client.
*
* @since 1.0.0
*
* @param string $key Key.
* @param string $secret Secret.
*/
public function set_client( $key = null, $secret = null ) {
$key = empty( $key ) ? $this->key : $key;
$secret = empty( $secret ) ? $this->secret : $secret;
if ( empty( $key ) || empty( $secret ) ) {
return false;
}
$credentials = new \Aws\Credentials\Credentials( $key, $secret );
/*
* Define our region.
*
* When using v2 of the aws-sdk-php, no region was defined. As such, all backups should have
* defaulted to us-east-1.
* @link https://github.com/aws/aws-sdk-php/blob/2.8.31/src/Aws/Common/HostNameUtils.php#L26
*
* @todo Give users the option to choose.
*/
$region = 'us-east-1';
$this->client = new \Aws\S3\S3Client(
array(
/*
* A "version" configuration value is required. Specifying a version constraint ensures
* that your code will not be affected by a breaking change made to the service.
* @link https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_configuration.html#cfg-version
*/
'version' => '2006-03-01',
'region' => $region,
'credentials' => $credentials,
)
);
}
/**
* Return data about a particular archive in Amazon S3.
*
* For example, if you're looking at a single backup, we need to know if it
* already exists in our Amazon S3 account.
*
* This method will return an array of information useful to the single
* archive page.
*
* @since 1.0.0
*
* @param string $filepath File path.
*/
public function single_archive_remote_option( $filepath ) {
$allow_upload = $this->is_setup();
$uploaded = $allow_upload && $this->in_bucket( null, $filepath );
$storage = array(
'id' => 'amazon_s3',
'title' => $this->title,
'title_attr' => $this->title_attr,
'uploaded' => $uploaded,
'allow_upload' => $allow_upload,
'is_setup' => $allow_upload,
);
$this->core->archive_details->remote_storage_li[] = $storage;
}
/**
* Generate the submenu page for our Amazon S3 Settings page.
*
* @since 1.0.0
*/
public function submenu_page() {
wp_enqueue_style( 'boldgrid-backup-admin-hide-all' );
$this->submenu_page_save();
$settings = $this->core->settings->get_settings();
$key = ! empty( $settings['remote']['amazon_s3']['key'] ) ?
$settings['remote']['amazon_s3']['key'] : null;
$secret = ! empty( $settings['remote']['amazon_s3']['secret'] ) ?
$settings['remote']['amazon_s3']['secret'] : null;
$bucket_id = ! empty( $settings['remote']['amazon_s3']['bucket_id'] ) ?
$settings['remote']['amazon_s3']['bucket_id'] : $this->bucket_id;
$retention_count = ! empty( $settings['remote']['amazon_s3']['retention_count'] ) ?
$settings['remote']['amazon_s3']['retention_count'] : $this->retention_count;
$nickname = ! empty( $settings['remote']['amazon_s3']['nickname'] ) ?
$settings['remote']['amazon_s3']['nickname'] : '';
include BOLDGRID_BACKUP_PREMIUM_PATH . '/admin/partials/remote/amazon-s3.php';
}
/**
* Process the user's request to update their Amazon S3 settings.
*
* @since 1.0.0
*/
public function submenu_page_save() {
if ( ! current_user_can( 'update_plugins' ) ) {
return false;
}
if ( empty( $_POST ) ) { // phpcs:ignore
return false;
}
$this->bucket->delete_transients();
$settings = $this->core->settings->get_settings();
if ( ! isset( $settings['remote']['amazon_s3'] ) || ! is_array( $settings['remote']['amazon_s3'] ) ) {
$settings['remote']['amazon_s3'] = array();
}
/*
* If the user has requested to delete all their settings, do that now
* and return.
*/
if ( __( 'Delete settings', 'boldgrid-backup' ) === $_POST['submit'] ) { // phpcs:ignore
$settings['remote']['amazon_s3'] = array();
update_site_option( 'boldgrid_backup_settings', $settings );
$this->key = null;
$this->secret = null;
$this->bucket_id = null;
$this->retention_count = null;
$this->nickname = null;
do_action( 'boldgrid_backup_notice', __( 'Settings saved.', 'boldgrid-backup' ), 'notice updated is-dismissible' );
return;
}
$key = ! empty( $_POST['key'] ) ? $_POST['key'] : null; // phpcs:ignore
$secret = ! empty( $_POST['secret'] ) ? $_POST['secret'] : null; // phpcs:ignore
// If no bucket_id submitted, we had a valid bucket_id created in the constructor.
$bucket_id = ! empty( $_POST['bucket_id'] ) ? $_POST['bucket_id'] : $this->bucket_id; // phpcs:ignore
$retention_count = ! empty( $_POST['retention_count'] ) && is_numeric( $_POST['retention_count'] ) ? $_POST['retention_count'] : $this->retention_count; // phpcs:ignore
$nickname = ! empty( $_POST['nickname'] ) ? stripslashes( $_POST['nickname'] ) : null; // phpcs:ignore
echo $this->core->elements['long_checking_creds']; // phpcs:ignore
if ( ob_get_level() > 0 ) {
ob_flush();
}
flush();
$valid_credentials = $this->is_valid_credentials( $key, $secret );
if ( $valid_credentials ) {
$settings['remote']['amazon_s3']['key'] = $key;
$settings['remote']['amazon_s3']['secret'] = $secret;
$this->key = $key;
$this->secret = $secret;
} else {
$this->errors[] = __( 'Invalid Access Key Id and / or Secret Access Key.', 'boldgrid-backup' );
}
if ( $this->bucket->create( $bucket_id ) ) {
$settings['remote']['amazon_s3']['bucket_id'] = $bucket_id;
$this->bucket_id = $bucket_id;
}
$settings['remote']['amazon_s3']['retention_count'] = $retention_count;
$settings['remote']['amazon_s3']['nickname'] = $nickname;
if ( ! empty( $this->errors ) ) {
do_action( 'boldgrid_backup_notice', implode( '<br /><br />', $this->errors ) );
} else {
update_site_option( 'boldgrid_backup_settings', $settings );
do_action( 'boldgrid_backup_notice', __( 'Settings saved.', 'boldgrid-backup' ), 'notice updated is-dismissible' );
}
}
/**
* Set our bucket id.
*
* @since 1.0.0
*
* @param string $bucket_id Bucket id.
*/
public function set_bucket_id( $bucket_id ) {
if ( empty( $bucket_id ) ) {
return;
}
$this->bucket_id = $bucket_id;
}
/**
* Upload a backup file.
*
* @since 1.0.0
*
* @param string $filepath File path.
* @return mixed True on success, error message on failure.
*/
public function upload( $filepath ) {
$this->set_client();
if ( ! $this->core->wp_filesystem->exists( $filepath ) ) {
// Translators: 1: File path.
return sprintf( __( 'Failed to upload, filepath does not exist: %1$s', 'boldgrid-backup' ), $filepath );
}
$key = basename( $filepath );
/*
* When files are uploaded to Amazon S3, the LastModified is the time
* the file was uploaded, not the time the file was last modified. When
* we enforce retention, we'll need to know when the backup archive was
* created, not when it was uploaded to Amazon S3.
*/
$archive_data = $this->core->archive_log->get_by_zip( $filepath );
$last_modified = ! empty( $archive_data['lastmodunix'] ) ? $archive_data['lastmodunix'] : $this->core->wp_filesystem->mtime( $filepath );
// Make sure our bucket is created.
if ( ! $this->bucket->create( $this->bucket_id ) ) {
return sprintf(
// Translators: 1: URL.
__( 'Unable to create bucket! Please go to your <a href="%1$s">settings page</a> to configure Amazon S3.', 'boldgrid-backup' ),
'admin.php?page=boldgrid-backup-settings'
);
}
try {
$uploader = new \Aws\S3\MultipartUploader(
$this->client,
fopen( $filepath, 'rb' ), // phpcs:ignore WordPress.WP.AlternativeFunctions
array(
'bucket' => $this->bucket_id,
'key' => $key,
/*
* Before Initiate.
*
* Set our custom metadata.
*
* @link https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-s3-2006-03-01.html#createmultipartupload
* @link https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-multipart-upload.html
*
* @var $command Aws\Command A CreateMultipartUpload operation.
*/
'before_initiate' => function( $command ) use ( $last_modified ) {
$command['Metadata'] = array(
'is_boldgrid_backup' => 'true',
'last_modified' => $last_modified,
);
},
)
);
} catch ( Exception $e ) {
return __( 'Failed to initialize', 'boldgrid-backup' );
}
try {
$uploader->upload();
$this->enforce_retention();
$this->bucket->delete_transients();
} catch ( MultipartUploadException $e ) {
$uploader->abort();
return __( 'Failed to upload.', 'boldgrid-inspirations' );
}
/**
* File uploaded to remote storage location.
*
* @since 1.5.3
*
* @param string Amazon S3
* @param string $filepath
*/
do_action( 'boldgrid_backup_remote_uploaded', 'Amazon S3', $filepath );
return true;
}
/**
* Upload a file.
*
* The jobs queue will call this method to upload a file.
*
* @since 1.0.0
*
* @param string $filepath File path.
*/
public function upload_post_archiving( $filepath ) {
$success = $this->upload( $filepath );
return $success;
}
/**
* Determine if Amazon S3 is setup properly.
*
* Hook into "boldgrid_backup_is_setup_amazon_s3".
*/
public function is_setup() {
return $this->is_valid_credentials( $this->key, $this->secret ) && $this->client->isBucketDnsCompatible( $this->bucket_id );
}
/**
* Determine if Amazon S3 is setup properly.
*
* This method is ran within an ajax request.
*/
public function is_setup_ajax() {
if ( ! current_user_can( 'update_plugins' ) ) {
wp_send_json_error( __( 'Permission denied.', 'boldgrid-backup' ) );
}
if ( ! check_ajax_referer( 'boldgrid_backup_settings', 'security', false ) ) {
wp_send_json_error( __( 'Invalid nonce.', 'boldgrid-backup' ) );
}
$settings = $this->core->settings->get_settings();
$location = $this->get_details();
$tr = include BOLDGRID_BACKUP_PATH . '/admin/partials/settings/storage-location.php';
if ( $this->is_setup() ) {
wp_send_json_success( $tr );
} else {
wp_send_json_error( $tr );
}
}
/**
* Determine if credentials are valid.
*
* @since 1.0.0
*
* @param string $key Key.
* @param string $secret Secret.
* @return bool
*/
public function is_valid_credentials( $key, $secret ) {
if ( empty( $key ) || empty( $secret ) ) {
return false;
}
$this->set_client( $key, $secret );
try {
$this->client->listBuckets();
return true;
} catch ( Exception $e ) {
return false;
}
}
/**
* Actions to take after a backup file has been generated.
*
* @since 1.0.0
*
* @param array $info Info.
*/
public function post_archive_files( array $info ) {
/*
* We only want to add this to the jobs queue if we're in the middle of
* an automatic backup. If the user simply clicked on "Backup site now",
* we don't want to automatically send the backup to Amazon, there's a
* button for that.
*/
if ( ! $this->core->doing_cron ) {
return;
}
if ( ! $this->core->remote->is_enabled( 'amazon_s3' ) || $info['dryrun'] || ! $info['save'] ) {
return;
}
$args = array(
'filepath' => $info['filepath'],
'action' => 'boldgrid_backup_amazon_s3_upload_post_archive',
'action_data' => $info['filepath'],
'action_title' => __( 'Upload backup file to Amazon S3', 'boldgrid-backup' ),
);
$this->core->jobs->add( $args );
}
/**
* Register Amazon S3 as a storage location.
*
* When you go to the settings page and see a list of storage providers, each of those storage providers needs to
* hook into the "boldgrid_backup_register_storage_location" filter and add themselves.
*
* @since 1.0.0
*
* @param array $storage_locations Storage locations.
* @return array
*/
public function register_storage_location( array $storage_locations ) {
$storage_locations[] = $this->get_details();
return $storage_locations;
}
}