662 lines
14 KiB
PHP
662 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* @package akeebabackupwp
|
|
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
|
|
* @license GNU GPL version 3 or later
|
|
*/
|
|
|
|
namespace Akeeba\WPCLI\Command;
|
|
|
|
use Akeeba\Engine\Factory;
|
|
use Akeeba\Engine\Platform;
|
|
use Akeeba\Engine\Util\ParseIni;
|
|
use Awf\Mvc\Model;
|
|
use Exception;
|
|
use Solo\Application;
|
|
use WP_CLI;
|
|
use WP_CLI\Utils as CliUtils;
|
|
|
|
/**
|
|
* View and change the configuration options of your Akeeba Backup profiles.
|
|
*
|
|
* @package Akeeba\WPCLI\Command
|
|
*
|
|
* @since 3.0.0
|
|
*/
|
|
class Option
|
|
{
|
|
/**
|
|
* Lists the configuration options for an Akeeba Backup profile, including their titles
|
|
*
|
|
* ## OPTIONS
|
|
*
|
|
* [--profile=<profile>]
|
|
* : The backup profile to use. Default: 1.
|
|
*
|
|
* [--filter=<filter>]
|
|
* : Only return records whose keys begin with the given filter
|
|
*
|
|
* [--sort-by=<column>]
|
|
* : Sort the output by the given column.
|
|
* ---
|
|
* default: none
|
|
* options:
|
|
* - none
|
|
* - key
|
|
* - value
|
|
* - type
|
|
* - default
|
|
* - title
|
|
* - description
|
|
* - section
|
|
* ---
|
|
*
|
|
* [--sort-order=<sortOrder>]
|
|
* : Sort order
|
|
* ---
|
|
* default: asc
|
|
* options:
|
|
* - asc
|
|
* - desc
|
|
* ---
|
|
*
|
|
* [--format=<format>]
|
|
* : The format for the returned list
|
|
* ---
|
|
* default: table
|
|
* options:
|
|
* - table
|
|
* - json
|
|
* - csv
|
|
* - yaml
|
|
* - count
|
|
* ---
|
|
*
|
|
* ## EXAMPLES
|
|
*
|
|
* wp akeeba option list
|
|
*
|
|
* wp akeeba option list --profile=2 --format=json
|
|
*
|
|
* wp akeeba option list --profile=2 --filter="core." --sort-by=key --sort-order=desc
|
|
*
|
|
* @when after_wp_load
|
|
* @subcommand list
|
|
*
|
|
* @param array $args Positional arguments (literal arguments)
|
|
* @param array $assoc_args Associative arguments (--flag, --no-flag, --key=value)
|
|
*
|
|
* @return void
|
|
*
|
|
* @throws WP_CLI\ExitException
|
|
*
|
|
* @since 3.0.0
|
|
*/
|
|
public function _list($args, $assoc_args)
|
|
{
|
|
/** @var Application $akeebaBackupApplication */
|
|
global $akeebaBackupApplication;
|
|
$container = $akeebaBackupApplication->getContainer();
|
|
|
|
$format = isset($assoc_args['format']) ? $assoc_args['format'] : 'table';
|
|
$profileID = isset($assoc_args['profile']) ? (int) $assoc_args['profile'] : 1;
|
|
$profileID = max(1, $profileID);
|
|
|
|
/** @var \Solo\Model\Profiles $model */
|
|
$model = Model::getTmpInstance($container->application_name, 'Profiles', $container);
|
|
|
|
try
|
|
{
|
|
$model->findOrFail($profileID);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
WP_CLI::error("Could not find profile #$profileID.");
|
|
}
|
|
|
|
unset($model);
|
|
|
|
// Get the profile's configuration
|
|
Platform::getInstance()->load_configuration($profileID);
|
|
$config = Factory::getConfiguration();
|
|
$rawJson = $config->exportAsJSON();
|
|
|
|
unset($config);
|
|
|
|
// Get the key information from the GUI data
|
|
$info = $this->parseJsonGuiData();
|
|
|
|
// Convert the INI data we got into an array we can print
|
|
$rawValues = json_decode($rawJson, true);
|
|
|
|
unset($rawJson);
|
|
|
|
$output = [];
|
|
|
|
$rawValues = $this->flattenOptions($rawValues);
|
|
|
|
foreach ($rawValues as $key => $v)
|
|
{
|
|
$output[$key] = array_merge([
|
|
'key' => $key,
|
|
'value' => $v,
|
|
'title' => '',
|
|
'description' => '',
|
|
'type' => '',
|
|
'default' => '',
|
|
'section' => '',
|
|
'options' => [],
|
|
'optionTitles' => [],
|
|
'limits' => [],
|
|
], $this->getOptionInfo($key, $info));
|
|
}
|
|
|
|
// Filter the returned options
|
|
$filter = isset($assoc_args['filter']) ? $assoc_args['filter'] : '';
|
|
|
|
$output = array_filter($output, function ($item) use ($filter) {
|
|
if (!empty($filter) && strpos($item['key'], $filter) !== 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return $item['type'] != 'hidden';
|
|
});
|
|
|
|
// Sort the results
|
|
$sort = isset($assoc_args['sort-by']) ? $assoc_args['sort-by'] : 'none';
|
|
$order = isset($assoc_args['sort-order']) ? $assoc_args['sort-order'] : 'asc';
|
|
|
|
if ($sort != 'none')
|
|
{
|
|
usort($output, function ($a, $b) use ($sort, $order) {
|
|
if ($a[$sort] == $b[$sort])
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
$signChange = ($order == 'asc') ? 1 : -1;
|
|
$isGreater = $a[$sort] > $b[$sort] ? 1 : -1;
|
|
|
|
return $signChange * $isGreater;
|
|
});
|
|
}
|
|
|
|
// Output the list
|
|
if (empty($output))
|
|
{
|
|
WP_CLI::error("No options found matching your criteria.");
|
|
}
|
|
|
|
$keys = array_keys($output);
|
|
$firstKey = array_shift($keys);
|
|
CliUtils\format_items($format, $output, array_keys($output[$firstKey]));
|
|
}
|
|
|
|
/**
|
|
* Gets the value of a configuration option for an Akeeba Backup profile
|
|
*
|
|
* ## OPTIONS
|
|
*
|
|
* <key>
|
|
* : The option key to retrieve
|
|
*
|
|
* [--profile=<profile>]
|
|
* : The backup profile to use. Default: 1.
|
|
*
|
|
* [--format=<format>]
|
|
* : The format for the returned value
|
|
* ---
|
|
* default: text
|
|
* options:
|
|
* - text
|
|
* - json
|
|
* - print_r
|
|
* - var_dump
|
|
* - var_export
|
|
* ---
|
|
*
|
|
* ## EXAMPLES
|
|
*
|
|
* wp akeeba option get akeeba.basic.archive_name
|
|
*
|
|
* wp akeeba option get akeeba.basic.archive_name --profile=2
|
|
*
|
|
* wp akeeba option get akeeba.basic.archive_name --profile=2 --format=var_export
|
|
*
|
|
* @when after_wp_load
|
|
*
|
|
* @param array $args Positional arguments (literal arguments)
|
|
* @param array $assoc_args Associative arguments (--flag, --no-flag, --key=value)
|
|
*
|
|
* @return void
|
|
*
|
|
* @throws WP_CLI\ExitException
|
|
*
|
|
* @since 3.0.0
|
|
*/
|
|
public function get($args, $assoc_args)
|
|
{
|
|
$key = isset($args[0]) ? $args[0] : null;
|
|
|
|
if (empty($key))
|
|
{
|
|
WP_CLI::error("You must specify the option key to retrieve.");
|
|
}
|
|
|
|
/** @var Application $akeebaBackupApplication */
|
|
global $akeebaBackupApplication;
|
|
$container = $akeebaBackupApplication->getContainer();
|
|
|
|
$profileID = isset($assoc_args['profile']) ? (int) $assoc_args['profile'] : 1;
|
|
$profileID = max(1, $profileID);
|
|
|
|
/** @var \Solo\Model\Profiles $model */
|
|
$model = Model::getTmpInstance($container->application_name, 'Profiles', $container);
|
|
|
|
try
|
|
{
|
|
$model->findOrFail($profileID);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
WP_CLI::error("Could not find profile #$profileID.");
|
|
}
|
|
|
|
unset($model);
|
|
|
|
// Get the profile's configuration
|
|
Platform::getInstance()->load_configuration($profileID);
|
|
$config = Factory::getConfiguration();
|
|
|
|
$value = $config->get($key, '', false);
|
|
|
|
switch ($assoc_args['format'])
|
|
{
|
|
case 'text':
|
|
default:
|
|
echo $value;
|
|
break;
|
|
|
|
case 'json':
|
|
echo json_encode($value);
|
|
break;
|
|
|
|
case 'print_r':
|
|
print_r($value);
|
|
break;
|
|
|
|
case 'var_dump':
|
|
var_dump($value);
|
|
break;
|
|
|
|
case 'var_export':
|
|
var_export($value);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the value of a configuration option for an Akeeba Backup profile
|
|
*
|
|
* ## OPTIONS
|
|
*
|
|
* <key>
|
|
* : The option key to set
|
|
*
|
|
* <value>
|
|
* : The value to set
|
|
*
|
|
* [--profile=<profile>]
|
|
* : The backup profile to use. Default: 1.
|
|
*
|
|
* [--force]
|
|
* : Allow setting of protected options.
|
|
*
|
|
* ## EXAMPLES
|
|
*
|
|
* wp akeeba option set akeeba.basic.archive_name "site-[HOST]-[DATE]-[TIME]"
|
|
*
|
|
* wp akeeba option set akeeba.basic.archive_name "site-[HOST]-[DATE]-[TIME]" --profile=2
|
|
*
|
|
* @when after_wp_load
|
|
*
|
|
* @param array $args Positional arguments (literal arguments)
|
|
* @param array $assoc_args Associative arguments (--flag, --no-flag, --key=value)
|
|
*
|
|
* @return void
|
|
*
|
|
* @throws WP_CLI\ExitException
|
|
*
|
|
* @since 3.0.0
|
|
*/
|
|
public function set($args, $assoc_args)
|
|
{
|
|
$key = isset($args[0]) ? $args[0] : null;
|
|
$value = isset($args[1]) ? $args[1] : null;
|
|
|
|
if (empty($key))
|
|
{
|
|
WP_CLI::error("You must specify the option key to set.");
|
|
}
|
|
|
|
if (is_null($value))
|
|
{
|
|
WP_CLI::error("You must specify the option value to set.");
|
|
}
|
|
|
|
/** @var Application $akeebaBackupApplication */
|
|
global $akeebaBackupApplication;
|
|
$container = $akeebaBackupApplication->getContainer();
|
|
|
|
$profileID = isset($assoc_args['profile']) ? (int) $assoc_args['profile'] : 1;
|
|
$profileID = max(1, $profileID);
|
|
|
|
/** @var \Solo\Model\Profiles $model */
|
|
$model = Model::getTmpInstance($container->application_name, 'Profiles', $container);
|
|
|
|
try
|
|
{
|
|
$model->findOrFail($profileID);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
WP_CLI::error("Could not find profile #$profileID.");
|
|
}
|
|
|
|
unset($model);
|
|
|
|
// Get the profile's configuration
|
|
Platform::getInstance()->load_configuration($profileID);
|
|
$config = Factory::getConfiguration();
|
|
|
|
// Get the key information from the GUI data
|
|
$info = $this->parseJsonGuiData();
|
|
|
|
// Does the key exist?
|
|
if (!array_key_exists($key, $info['options']))
|
|
{
|
|
WP_CLI::error("Invalid option key '$key'.");
|
|
}
|
|
|
|
// Validate / sanitize the value
|
|
$optionInfo = $this->getOptionInfo($key, $info);
|
|
|
|
switch ($optionInfo['type'])
|
|
{
|
|
case 'integer':
|
|
$value = (int) $value;
|
|
|
|
if (($value < $optionInfo['limits']['min']) || ($value > $optionInfo['limits']['max']))
|
|
{
|
|
WP_CLI::error("Invalid value '$value': out of bounds.");
|
|
}
|
|
break;
|
|
|
|
case 'bool':
|
|
if (is_numeric($value))
|
|
{
|
|
$value = (int) $value;
|
|
}
|
|
elseif (is_string($value))
|
|
{
|
|
$value = strtolower($value);
|
|
}
|
|
|
|
if (in_array($value, [false, 0, '0', 'false', 'no', 'off'], true))
|
|
{
|
|
$value = 0;
|
|
}
|
|
elseif (in_array($value, [true, 1, '1', 'true', 'yes', 'on'], true))
|
|
{
|
|
$value = 1;
|
|
}
|
|
else
|
|
{
|
|
WP_CLI::error("Invalid boolean value '$value': use one of 0, false, no, off, 1, true, yes or on.'");
|
|
}
|
|
|
|
break;
|
|
|
|
case 'enum':
|
|
if (!in_array($value, $optionInfo['options']))
|
|
{
|
|
$options = array_map(function ($v) {
|
|
return "'$v'";
|
|
}, $optionInfo['options']);
|
|
$options = implode(', ', $options);
|
|
|
|
WP_CLI::error("Invalid enumerated value '$value'. Must be one of $options.");
|
|
}
|
|
|
|
break;
|
|
|
|
case 'hidden':
|
|
WP_CLI::error("Setting hidden option '$key' is not allowed.");
|
|
break;
|
|
|
|
case 'string':
|
|
break;
|
|
|
|
default:
|
|
WP_CLI::error("Unknown type {$optionInfo['type']} for option '$key'. Have you manually tampered with the option INI files?");
|
|
break;
|
|
}
|
|
|
|
$protected = $config->getProtectedKeys();
|
|
$force = isset($assoc_args['force']) && $assoc_args['force'];
|
|
|
|
if (in_array($key, $protected) && !$force)
|
|
{
|
|
WP_CLI::error("Cannot set protected option '$key'. Please use the --force option to override the protection.");
|
|
}
|
|
|
|
if (in_array($key, $protected) && $force)
|
|
{
|
|
$config->setKeyProtection($key, false);
|
|
}
|
|
|
|
$result = $config->set($key, $value, false);
|
|
|
|
if ($result === false)
|
|
{
|
|
WP_CLI::error("Could not set option '$key'.");
|
|
}
|
|
|
|
Platform::getInstance()->save_configuration($profileID);
|
|
|
|
WP_CLI::success("Successfully set option '$key' to '$value'");
|
|
}
|
|
|
|
/**
|
|
* Parse the JSON GUI definition returned by Akeeba Engine into something I can use to provide information about
|
|
* the options.
|
|
*
|
|
* @return array
|
|
*
|
|
* @since 3.0.0
|
|
*/
|
|
private function parseJsonGuiData()
|
|
{
|
|
$jsonGUIData = Factory::getEngineParamsProvider()->getJsonGuiDefinition();
|
|
$guiData = json_decode($jsonGUIData, true);
|
|
|
|
$ret = [
|
|
'engines' => [],
|
|
'installers' => [],
|
|
'options' => [],
|
|
];
|
|
|
|
// Parse engines
|
|
foreach ($guiData['engines'] as $engineType => $engineRecords)
|
|
{
|
|
if (!isset($ret['engines'][$engineType]))
|
|
{
|
|
$ret['engines'][$engineType] = [];
|
|
}
|
|
|
|
foreach ($engineRecords as $engineName => $record)
|
|
{
|
|
$ret['engines'][$engineType][$engineName] = [
|
|
'title' => $record['information']['title'],
|
|
'description' => $record['information']['description'],
|
|
];
|
|
|
|
foreach ($record['parameters'] as $key => $optionRecord)
|
|
{
|
|
$ret['options'][$key] = array_merge($optionRecord, [
|
|
'section' => $record['information']['title'],
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse installers
|
|
foreach ($guiData['installers'] as $installerName => $installerInfo)
|
|
{
|
|
$ret['installers'][$installerName] = $installerInfo['name'];
|
|
}
|
|
|
|
// Parse GUI sections
|
|
foreach ($guiData['gui'] as $section => $options)
|
|
{
|
|
foreach ($options as $key => $optionRecord)
|
|
{
|
|
$ret['options'][$key] = array_merge($optionRecord, [
|
|
'section' => $section,
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* Get the information for an option record.
|
|
*
|
|
* @param string $key The option key
|
|
* @param array $info The array returned by parseJsonGuiData
|
|
*
|
|
* @return array
|
|
*
|
|
* @since 3.0.0
|
|
*/
|
|
private function getOptionInfo($key, &$info)
|
|
{
|
|
$ret = [];
|
|
|
|
if (!isset($info['options'][$key]))
|
|
{
|
|
return $ret;
|
|
}
|
|
|
|
$keyInfo = $info['options'][$key];
|
|
|
|
$ret = [
|
|
'title' => $keyInfo['title'],
|
|
'description' => $keyInfo['description'],
|
|
'section' => $keyInfo['section'],
|
|
'type' => $keyInfo['type'],
|
|
'default' => $keyInfo['default'],
|
|
'options' => [],
|
|
'optionTitles' => [],
|
|
'limits' => [],
|
|
];
|
|
|
|
switch ($keyInfo['type'])
|
|
{
|
|
case 'integer':
|
|
if (isset($keyInfo['shortcuts']))
|
|
{
|
|
$ret['options'] = explode('|', $keyInfo['shortcuts']);
|
|
}
|
|
|
|
$ret['limits'] = [
|
|
'min' => $keyInfo['min'],
|
|
'max' => $keyInfo['max'],
|
|
];
|
|
break;
|
|
|
|
case 'bool':
|
|
$ret['type'] = 'integer';
|
|
$ret['options'] = [0, 1];
|
|
$ret['limits'] = [
|
|
'min' => 0,
|
|
'max' => 1,
|
|
];
|
|
break;
|
|
|
|
case 'engine':
|
|
$ret['type'] = 'enum';
|
|
$ret['type'] = 'string';
|
|
$ret['options'] = array_keys($info['engines'][$keyInfo['subtype']]);
|
|
$ret['optionTitles'] = [];
|
|
|
|
foreach ($info['engines'][$keyInfo['subtype']] as $k => $details)
|
|
{
|
|
$ret['optionTitles'][$k] = $details['title'];
|
|
}
|
|
|
|
break;
|
|
|
|
case 'installer':
|
|
$ret['type'] = 'enum';
|
|
$ret['type'] = 'string';
|
|
$ret['options'] = array_keys($info['installers']);
|
|
$ret['optionTitles'] = $info['installers'];
|
|
|
|
break;
|
|
|
|
case 'enum':
|
|
$ret['type'] = 'string';
|
|
$ret['options'] = explode('|', $keyInfo['enumvalues']);
|
|
$ret['optionTitles'] = explode('|', $keyInfo['enumkeys']);
|
|
|
|
break;
|
|
|
|
case 'hidden':
|
|
case 'button':
|
|
case 'separator':
|
|
$ret['type'] = 'hidden';
|
|
break;
|
|
|
|
case 'string':
|
|
case 'browsedir':
|
|
case 'password':
|
|
default:
|
|
$ret['type'] = 'string';
|
|
break;
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* Flattens the option tree returned by exportToJson into an array with dotted notation for each option.
|
|
*
|
|
* @param array $rawOptions The option tree
|
|
* @param string $prefix Current prefix, used for recursion
|
|
*
|
|
* @return array
|
|
*/
|
|
private function flattenOptions(array $rawOptions, $prefix = '')
|
|
{
|
|
$ret = [];
|
|
|
|
foreach ($rawOptions as $k => $v)
|
|
{
|
|
if (is_array($v))
|
|
{
|
|
$ret = array_merge($ret, $this->flattenOptions($v, $prefix . $k . '.'));
|
|
|
|
continue;
|
|
}
|
|
|
|
$ret[$prefix . $k] = $v;
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
}
|