You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
597 lines
22 KiB
597 lines
22 KiB
<?php
|
|
|
|
if (!defined('UPDRAFTPLUS_DIR')) die('No direct access allowed.');
|
|
|
|
if (!class_exists('UpdraftPlus_BackupModule')) require_once(UPDRAFTPLUS_DIR.'/methods/backup-module.php');
|
|
|
|
class UpdraftPlus_BackupModule_openstack_base extends UpdraftPlus_BackupModule {
|
|
|
|
protected $chunk_size;
|
|
|
|
protected $client;
|
|
|
|
protected $method;
|
|
|
|
protected $desc;
|
|
|
|
protected $long_desc;
|
|
|
|
protected $img_url;
|
|
|
|
public function __construct($method, $desc, $long_desc = null, $img_url = '') {
|
|
$this->method = $method;
|
|
$this->desc = $desc;
|
|
$this->long_desc = (is_string($long_desc)) ? $long_desc : $desc;
|
|
$this->img_url = $img_url;
|
|
}
|
|
|
|
public function backup($backup_array) {
|
|
|
|
global $updraftplus;
|
|
|
|
$default_chunk_size = (defined('UPDRAFTPLUS_UPLOAD_CHUNKSIZE') && UPDRAFTPLUS_UPLOAD_CHUNKSIZE > 0) ? max(UPDRAFTPLUS_UPLOAD_CHUNKSIZE, 1048576) : 5242880;
|
|
|
|
$this->chunk_size = $updraftplus->jobdata_get('openstack_chunk_size', $default_chunk_size);
|
|
|
|
$opts = $this->get_options();
|
|
|
|
$this->container = $opts['path'];
|
|
|
|
try {
|
|
$storage = $this->get_openstack_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
|
|
} catch (AuthenticationError $e) {
|
|
$updraftplus->log($this->desc.' authentication failed ('.$e->getMessage().')');
|
|
$updraftplus->log(sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
|
return false;
|
|
} catch (Exception $e) {
|
|
$updraftplus->log($this->desc.' error - failed to access the container ('.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
|
$updraftplus->log(sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
|
return false;
|
|
}
|
|
// Get the container
|
|
try {
|
|
$this->container_object = $storage->getContainer($this->container);
|
|
} catch (Exception $e) {
|
|
$updraftplus->log('Could not access '.$this->desc.' container ('.get_class($e).', '.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
|
$updraftplus->log(sprintf(__('Could not access %s container', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')', 'error');
|
|
return false;
|
|
}
|
|
|
|
foreach ($backup_array as $key => $file) {
|
|
|
|
$file_key = 'status_'.md5($file);
|
|
$file_status = $this->jobdata_get($file_key, null, 'openstack_'.$file_key);
|
|
if (is_array($file_status) && !empty($file_status['chunks']) && !empty($file_status['chunks'][1]['size'])) $this->chunk_size = $file_status['chunks'][1]['size'];
|
|
|
|
// First, see the object's existing size (if any)
|
|
$uploaded_size = $this->get_remote_size($file);
|
|
|
|
try {
|
|
if (1 === $updraftplus->chunked_upload($this, $file, $this->method."://".$this->container."/$file", $this->desc, $this->chunk_size, $uploaded_size)) {
|
|
try {
|
|
if (false !== ($data = fopen($updraftplus->backups_dir_location().'/'.$file, 'r+'))) {
|
|
$this->container_object->uploadObject($file, $data);
|
|
$updraftplus->log($this->desc." regular upload: success");
|
|
$updraftplus->uploaded_file($file);
|
|
} else {
|
|
throw new Exception('uploadObject failed: fopen failed');
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->log("$logname regular upload: failed ($file) (".$e->getMessage().")");
|
|
$this->log("$file: ".sprintf(__('%s Error: Failed to upload', 'updraftplus'), $logname), 'error');
|
|
}
|
|
}
|
|
} catch (Exception $e) {
|
|
$updraftplus->log($this->desc.' error - failed to upload file'.' ('.$e->getMessage().') (line: '.$e->getLine().', file: '.$e->getFile().')');
|
|
$updraftplus->log(sprintf(__('%s error - failed to upload file', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return array('object' => $this->container_object, 'orig_path' => $opts['path'], 'container' => $this->container);
|
|
|
|
}
|
|
|
|
private function get_remote_size($file) {
|
|
try {
|
|
$response = $this->container_object->getClient()->head($this->container_object->getUrl($file))->send();
|
|
$response_object = $this->container_object->dataObject()->populateFromResponse($response)->setName($file);
|
|
return $response_object->getContentLength();
|
|
} catch (Exception $e) {
|
|
// Allow caller to distinguish between zero-sized and not-found
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This function lists the files found in the configured storage location
|
|
*
|
|
* @param String $match a substring to require (tested via strpos() !== false)
|
|
*
|
|
* @return Array - each file is represented by an array with entries 'name' and (optional) 'size'
|
|
*/
|
|
public function listfiles($match = 'backup_') {
|
|
$opts = $this->get_options();
|
|
$container = $opts['path'];
|
|
$path = $container;
|
|
|
|
if (empty($opts['user']) || (empty($opts['apikey']) && empty($opts['password']))) return new WP_Error('no_settings', __('No settings were found', 'updraftplus'));
|
|
|
|
try {
|
|
$storage = $this->get_openstack_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
|
|
} catch (Exception $e) {
|
|
return new WP_Error('no_access', sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')');
|
|
}
|
|
|
|
// Get the container
|
|
try {
|
|
$this->container_object = $storage->getContainer($container);
|
|
} catch (Exception $e) {
|
|
return new WP_Error('no_access', sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')');
|
|
}
|
|
|
|
$results = array();
|
|
$marker = '';
|
|
$page_size = 1000;
|
|
try {
|
|
// http://php-opencloud.readthedocs.io/en/latest/services/object-store/objects.html#list-objects-in-a-container
|
|
while (null !== $marker) {
|
|
|
|
$params = array(
|
|
'prefix' => $match,
|
|
'limit' => $page_size,
|
|
'marker' => $marker
|
|
);
|
|
|
|
$objects = $this->container_object->objectList($params);
|
|
|
|
$total = $objects->count();
|
|
|
|
if (0 == $total) break;
|
|
|
|
$index = 0;
|
|
|
|
while (false !== ($file = $objects->offsetGet($index)) && !empty($file)) {
|
|
$index++;
|
|
try {
|
|
if ((is_object($file) && !empty($file->name))) {
|
|
$result = array('name' => $file->name);
|
|
// Rackspace returns the size of a manifested file properly; other OpenStack implementations may not
|
|
if (!empty($file->bytes)) {
|
|
$result['size'] = $file->bytes;
|
|
} else {
|
|
$size = $this->get_remote_size($file->name);
|
|
if (false !== $size && $size > 0) $result['size'] = $size;
|
|
}
|
|
$results[] = $result;
|
|
}
|
|
} catch (Exception $e) {
|
|
// Catch
|
|
}
|
|
$marker = (!empty($file->name) && $total >= $page_size) ? $file->name : null;
|
|
}
|
|
|
|
}
|
|
} catch (Exception $e) {
|
|
// Catch
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Called when all chunks have been uploaded, to allow any required finishing actions to be carried out
|
|
*
|
|
* @param String $file - the basename of the file being uploaded
|
|
*
|
|
* @return Boolean - success or failure state of any finishing actions
|
|
*/
|
|
public function chunked_upload_finish($file) {
|
|
|
|
$chunk_path = 'chunk-do-not-delete-'.$file;
|
|
try {
|
|
|
|
$headers = array(
|
|
'Content-Length' => 0,
|
|
'X-Object-Manifest' => sprintf('%s/%s', $this->container, $chunk_path.'_')
|
|
);
|
|
|
|
$url = $this->container_object->getUrl($file);
|
|
$this->container_object->getClient()->put($url, $headers)->send();
|
|
return true;
|
|
|
|
} catch (Exception $e) {
|
|
global $updraftplus;
|
|
$updraftplus->log("Error when sending manifest (".get_class($e)."): ".$e->getMessage());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* N.B. Since we use varying-size chunks, we must be careful as to what we do with $chunk_index
|
|
*
|
|
* @param String $file Full path for the file being uploaded
|
|
* @param Resource $fp File handle to read upload data from
|
|
* @param Integer $chunk_index Index of chunked upload
|
|
* @param Integer $upload_size Size of the upload, in bytes
|
|
* @param Integer $upload_start How many bytes into the file the upload process has got
|
|
* @param Integer $upload_end How many bytes into the file we will be after this chunk is uploaded
|
|
* @param Integer $total_file_size Total file size
|
|
*
|
|
* @return Boolean
|
|
*/
|
|
public function chunked_upload($file, $fp, $chunk_index, $upload_size, $upload_start, $upload_end, $total_file_size) {
|
|
|
|
global $updraftplus;
|
|
|
|
$file_key = 'status_'.md5($file);
|
|
$file_status = $this->jobdata_get($file_key, null, 'openstack_'.$file_key);
|
|
|
|
$next_chunk_size = $upload_size;
|
|
|
|
$bytes_already_uploaded = 0;
|
|
|
|
$last_uploaded_chunk_index = 0;
|
|
|
|
// Once a chunk is uploaded, its status is set, allowing the sequence to be reconstructed
|
|
if (is_array($file_status) && isset($file_status['chunks']) && !empty($file_status['chunks'])) {
|
|
foreach ($file_status['chunks'] as $c_id => $c_status) {
|
|
if ($c_id > $last_uploaded_chunk_index) $last_uploaded_chunk_index = $c_id;
|
|
if ($chunk_index + 1 == $c_id) {
|
|
$next_chunk_size = $c_status['size'];
|
|
}
|
|
$bytes_already_uploaded += $c_status['size'];
|
|
}
|
|
} else {
|
|
$file_status = array('chunks' => array());
|
|
}
|
|
|
|
$this->jobdata_set($file_key, $file_status);
|
|
|
|
if ($upload_start < $bytes_already_uploaded) {
|
|
if ($next_chunk_size != $upload_size) {
|
|
$response = new stdClass;
|
|
$response->new_chunk_size = $upload_size;
|
|
$response->log = false;
|
|
return $response;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Shouldn't be able to happen
|
|
if ($chunk_index <= $last_uploaded_chunk_index) {
|
|
$updraftplus->log($this->desc.": Chunk sequence error; chunk_index=$chunk_index, last_uploaded_chunk_index=$last_uploaded_chunk_index, upload_start=$upload_start, upload_end=$upload_end, file_status=".json_encode($file_status));
|
|
}
|
|
|
|
// Used to use $chunk_index here, before switching to variable chunk sizes
|
|
$upload_remotepath = 'chunk-do-not-delete-'.$file.'_'.sprintf("%016d", $chunk_index);
|
|
|
|
$remote_size = $this->get_remote_size($upload_remotepath);
|
|
|
|
// Without this, some versions of Curl add Expect: 100-continue, which results in Curl then giving this back: curl error: 55) select/poll returned error
|
|
// Didn't make the difference - instead we just check below for actual success even when Curl reports an error
|
|
// $chunk_object->headers = array('Expect' => '');
|
|
|
|
if ($remote_size >= $upload_size) {
|
|
$updraftplus->log($this->desc.": Chunk ($upload_start - $upload_end, $chunk_index): already uploaded");
|
|
} else {
|
|
$updraftplus->log($this->desc.": Chunk ($upload_start - $upload_end, $chunk_index): begin upload");
|
|
// Upload the chunk
|
|
try {
|
|
$data = fread($fp, $upload_size);
|
|
$time_start = microtime(true);
|
|
$this->container_object->uploadObject($upload_remotepath, $data);
|
|
$time_now = microtime(true);
|
|
$time_taken = $time_now - $time_start;
|
|
if ($next_chunk_size < 52428800 && $total_file_size > 0 && $upload_end + 1 < $total_file_size) {
|
|
$job_run_time = $time_now - $updraftplus->job_time_ms;
|
|
if ($time_taken < 10) {
|
|
$upload_rate = $upload_size / max($time_taken, 0.0001);
|
|
$upload_secs = min(floor($job_run_time), 10);
|
|
if ($job_run_time < 15) $upload_secs = max(6, $job_run_time*0.6);
|
|
|
|
// In megabytes
|
|
$memory_limit_mb = $updraftplus->memory_check_current();
|
|
$bytes_used = memory_get_usage();
|
|
$bytes_free = $memory_limit_mb * 1048576 - $bytes_used;
|
|
|
|
$new_chunk = max(min($upload_secs * $upload_rate * 0.9, 52428800, $bytes_free), 5242880);
|
|
$new_chunk = $new_chunk - ($new_chunk % 5242880);
|
|
$next_chunk_size = (int) $new_chunk;
|
|
$updraftplus->jobdata_set('openstack_chunk_size', $next_chunk_size);
|
|
}
|
|
}
|
|
|
|
} catch (Exception $e) {
|
|
$updraftplus->log($this->desc." chunk upload: error: ($file / $chunk_index) (".$e->getMessage().") (line: ".$e->getLine().', file: '.$e->getFile().')');
|
|
// Experience shows that Curl sometimes returns a select/poll error (curl error 55) even when everything succeeded. Google seems to indicate that this is a known bug.
|
|
|
|
$remote_size = $this->get_remote_size($upload_remotepath);
|
|
|
|
if ($remote_size >= $upload_size) {
|
|
$updraftplus->log("$file: Chunk now exists; ignoring error (presuming it was an apparently known curl bug)");
|
|
} else {
|
|
$updraftplus->log("$file: ".sprintf(__('%s Error: Failed to upload', 'updraftplus'), $this->desc), 'error');
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
$file_status['chunks'][$chunk_index]['size'] = $upload_size;
|
|
|
|
$this->jobdata_set($file_key, $file_status);
|
|
|
|
if ($next_chunk_size != $upload_size) {
|
|
$response = new stdClass;
|
|
$response->new_chunk_size = $next_chunk_size;
|
|
$response->log = true;
|
|
return $response;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function delete($files, $data = false, $sizeinfo = array()) {
|
|
|
|
global $updraftplus;
|
|
if (is_string($files)) $files = array($files);
|
|
|
|
if (is_array($data)) {
|
|
$container_object = $data['object'];
|
|
$container = $data['container'];
|
|
$path = $data['orig_path'];
|
|
} else {
|
|
$opts = $this->get_options();
|
|
$container = $opts['path'];
|
|
$path = $container;
|
|
try {
|
|
$storage = $this->get_openstack_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
|
|
} catch (AuthenticationError $e) {
|
|
$updraftplus->log($this->desc.' authentication failed ('.$e->getMessage().')');
|
|
$updraftplus->log(sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
|
return false;
|
|
} catch (Exception $e) {
|
|
$updraftplus->log($this->desc.' error - failed to access the container ('.$e->getMessage().')');
|
|
$updraftplus->log(sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
|
return false;
|
|
}
|
|
// Get the container
|
|
try {
|
|
$container_object = $storage->getContainer($container);
|
|
} catch (Exception $e) {
|
|
$updraftplus->log('Could not access '.$this->desc.' container ('.get_class($e).', '.$e->getMessage().')');
|
|
$updraftplus->log(sprintf(__('Could not access %s container', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')', 'error');
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
$ret = true;
|
|
foreach ($files as $file) {
|
|
|
|
$updraftplus->log($this->desc.": Delete remote: container=$container, path=$file");
|
|
|
|
// We need to search for chunks
|
|
$chunk_path = "chunk-do-not-delete-".$file;
|
|
|
|
try {
|
|
$objects = $container_object->objectList(array('prefix' => $chunk_path));
|
|
$index = 0;
|
|
while (false !== ($chunk = $objects->offsetGet($index)) && !empty($chunk)) {
|
|
try {
|
|
$name = $chunk->name;
|
|
$container_object->dataObject()->setName($name)->delete();
|
|
$updraftplus->log($this->desc.': Chunk deleted: '.$name);
|
|
} catch (Exception $e) {
|
|
$updraftplus->log($this->desc." chunk delete failed: $name: ".$e->getMessage());
|
|
}
|
|
$index++;
|
|
}
|
|
} catch (Exception $e) {
|
|
$updraftplus->log($this->desc.' chunk delete failed: '.$e->getMessage());
|
|
}
|
|
|
|
// Finally, delete the object itself
|
|
try {
|
|
$container_object->dataObject()->setName($file)->delete();
|
|
$updraftplus->log($this->desc.': Deleted: '.$file);
|
|
} catch (Exception $e) {
|
|
$updraftplus->log($this->desc.' delete failed: '.$e->getMessage());
|
|
$ret = false;
|
|
}
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
public function download($file) {
|
|
|
|
global $updraftplus;
|
|
|
|
$opts = $this->get_options();
|
|
|
|
try {
|
|
$storage = $this->get_openstack_service($opts, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts'), UpdraftPlus_Options::get_updraft_option('updraft_ssl_disableverify'));
|
|
} catch (AuthenticationError $e) {
|
|
$updraftplus->log($this->desc.' authentication failed ('.$e->getMessage().')');
|
|
$updraftplus->log(sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
|
return false;
|
|
} catch (Exception $e) {
|
|
$updraftplus->log($this->desc.' error - failed to access the container ('.$e->getMessage().')');
|
|
$updraftplus->log(sprintf(__('%s error - failed to access the container', 'updraftplus'), $this->desc).' ('.$e->getMessage().')', 'error');
|
|
return false;
|
|
}
|
|
|
|
$container = untrailingslashit($opts['path']);
|
|
$updraftplus->log($this->desc." download: ".$this->method."://$container/$file");
|
|
|
|
// Get the container
|
|
try {
|
|
$this->container_object = $storage->getContainer($container);
|
|
} catch (Exception $e) {
|
|
$updraftplus->log('Could not access '.$this->desc.' container ('.get_class($e).', '.$e->getMessage().')');
|
|
$updraftplus->log(sprintf(__('Could not access %s container', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')', 'error');
|
|
return false;
|
|
}
|
|
|
|
// Get information about the object within the container
|
|
$remote_size = $this->get_remote_size($file);
|
|
if (false === $remote_size) {
|
|
$updraftplus->log('Could not access '.$this->desc.' object');
|
|
$updraftplus->log(sprintf(__('The %s object was not found', 'updraftplus'), $this->desc), 'error');
|
|
return false;
|
|
}
|
|
|
|
return (!is_bool($remote_size)) ? $updraftplus->chunked_download($file, $this, $remote_size, true, $this->container_object) : false;
|
|
|
|
}
|
|
|
|
public function chunked_download($file, $headers, $container_object) {
|
|
try {
|
|
$dl = $container_object->getObject($file, $headers);
|
|
} catch (Exception $e) {
|
|
global $updraftplus;
|
|
$updraftplus->log("$file: Failed to download (".$e->getMessage().")");
|
|
$updraftplus->log("$file: ".sprintf(__("%s Error", 'updraftplus'), $this->desc).": ".__('Error downloading remote file: Failed to download', 'updraftplus').' ('.$e->getMessage().")", 'error');
|
|
return false;
|
|
}
|
|
return $dl->getContent();
|
|
}
|
|
|
|
public function credentials_test_go($opts, $path, $useservercerts, $disableverify) {
|
|
|
|
if (preg_match("#^([^/]+)/(.*)$#", $path, $bmatches)) {
|
|
$container = $bmatches[1];
|
|
$path = $bmatches[2];
|
|
} else {
|
|
$container = $path;
|
|
$path = '';
|
|
}
|
|
|
|
if (empty($container)) {
|
|
_e('Failure: No container details were given.', 'updraftplus');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$storage = $this->get_openstack_service($opts, $useservercerts, $disableverify);
|
|
// @codingStandardsIgnoreLine
|
|
} catch (Guzzle\Http\Exception\ClientErrorResponseException $e) {
|
|
$response = $e->getResponse();
|
|
$code = $response->getStatusCode();
|
|
$reason = $response->getReasonPhrase();
|
|
if (401 == $code && 'Unauthorized' == $reason) {
|
|
echo __('Authorisation failed (check your credentials)', 'updraftplus');
|
|
} else {
|
|
echo __('Authorisation failed (check your credentials)', 'updraftplus')." ($code:$reason)";
|
|
}
|
|
return;
|
|
} catch (AuthenticationError $e) {
|
|
echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.$e->getMessage().')';
|
|
return;
|
|
} catch (Exception $e) {
|
|
echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$container_object = $storage->getContainer($container);
|
|
// @codingStandardsIgnoreLine
|
|
} catch (Guzzle\Http\Exception\ClientErrorResponseException $e) {
|
|
$response = $e->getResponse();
|
|
$code = $response->getStatusCode();
|
|
$reason = $response->getReasonPhrase();
|
|
if (404 == $code) {
|
|
$container_object = $storage->createContainer($container);
|
|
} else {
|
|
echo __('Authorisation failed (check your credentials)', 'updraftplus')." ($code:$reason)";
|
|
return;
|
|
}
|
|
} catch (Exception $e) {
|
|
echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')';
|
|
return;
|
|
}
|
|
|
|
if (!is_a($container_object, 'OpenCloud\ObjectStore\Resource\Container') && !is_a($container_object, 'Container')) {
|
|
echo sprintf(__('%s authentication failed', 'updraftplus'), $this->desc).' ('.get_class($container_object).')';
|
|
return;
|
|
}
|
|
|
|
$try_file = md5(rand()).'.txt';
|
|
|
|
try {
|
|
$object = $container_object->uploadObject($try_file, 'UpdraftPlus test file', array('content-type' => 'text/plain'));
|
|
} catch (Exception $e) {
|
|
echo sprintf(__('%s error - we accessed the container, but failed to create a file within it', 'updraftplus'), $this->desc).' ('.get_class($e).', '.$e->getMessage().')';
|
|
if (!empty($this->region)) echo ' '.sprintf(__('Region: %s', 'updraftplus'), $this->region);
|
|
return;
|
|
}
|
|
|
|
echo __('Success', 'updraftplus').": ".__('We accessed the container, and were able to create files within it.', 'updraftplus');
|
|
if (!empty($this->region)) echo ' '.sprintf(__('Region: %s', 'updraftplus'), $this->region);
|
|
|
|
try {
|
|
if (!empty($object)) {
|
|
// One OpenStack server we tested on did not delete unless we slept... some kind of race condition at their end
|
|
sleep(1);
|
|
$object->delete();
|
|
}
|
|
} catch (Exception $e) {
|
|
// Catch
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Get the pre configuration template
|
|
*
|
|
* @return String - the template
|
|
*/
|
|
public function get_pre_configuration_template() {
|
|
|
|
global $updraftplus_admin;
|
|
|
|
$classes = $this->get_css_classes(false);
|
|
|
|
?>
|
|
<tr class="<?php echo $classes . ' ' . $this->method . '_pre_config_container';?>">
|
|
<td colspan="2">
|
|
<?php
|
|
if (!empty($this->img_url)) {
|
|
?>
|
|
<img alt="<?php echo $this->long_desc; ?>" src="<?php echo UPDRAFTPLUS_URL.$this->img_url; ?>">
|
|
<?php
|
|
}
|
|
?>
|
|
<br>
|
|
<?php
|
|
// Check requirements.
|
|
global $updraftplus_admin;
|
|
if (!function_exists('mb_substr')) {
|
|
$updraftplus_admin->show_double_warning('<strong>'.__('Warning', 'updraftplus').':</strong> '.sprintf(__('Your web server\'s PHP installation does not included a required module (%s). Please contact your web hosting provider\'s support.', 'updraftplus'), 'mbstring').' '.sprintf(__("UpdraftPlus's %s module <strong>requires</strong> %s. Please do not file any support requests; there is no alternative.", 'updraftplus'), $this->desc, 'mbstring'), $this->method);
|
|
}
|
|
$updraftplus_admin->curl_check($this->long_desc, false, $this->method);
|
|
echo '<br>';
|
|
$this->get_pre_configuration_middlesection_template();
|
|
?>
|
|
</td>
|
|
</tr>
|
|
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Get the configuration template
|
|
*
|
|
* @return String - the template, ready for substitutions to be carried out
|
|
*/
|
|
public function get_configuration_template() {
|
|
ob_start();
|
|
$classes = $this->get_css_classes();
|
|
$template_str = ob_get_clean();
|
|
$template_str .= $this->get_configuration_middlesection_template();
|
|
$template_str .= $this->get_test_button_html($this->desc);
|
|
return $template_str;
|
|
}
|
|
}
|