first commit

This commit is contained in:
2024-07-15 11:28:08 +02:00
commit f52d538ea5
21891 changed files with 6161164 additions and 0 deletions

View File

@@ -0,0 +1,263 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json;
/**
* Handles data encapsulation
*/
class Encapsulation
{
/**
* Known encapsulation handlers
*
* @var EncapsulationInterface[]
*/
protected $handlers = array();
/**
* List of encapsulation types
*
* @var array
*/
protected $encapsulations = array();
/**
* The server key used to decrypt / encrypt data and check the authorisation
*
* @var string
*/
protected $serverKey;
/**
* Public constructor
*
* @param string $serverKey The server key used for data encyrption/decryption and authorisation checks
*/
public function __construct($serverKey)
{
$this->serverKey = $serverKey;
// Populate the list of encapsulation handlers
$this->initialiseHandlers();
}
/**
* Returns the encapsulation ID given its code. For example given $code == 'ENCAPSULATION_AESCTR256' it will return
* the ID integer 3.
*
* @param string $code The encapsulation code, e.g. ENCAPSULATION_AESCTR256
*
* @return int The numeric ID, e.g. 3
*/
public function getEncapsulationByCode($code)
{
$info = $this->getEncapsulationInfoByCode($code);
return $info['id'];
}
/**
* Returns the encapsulation information array given its code. For example given $code == 'ENCAPSULATION_AESCTR256'
* it will return the information for the data in AES-256 stream (CTR) mode encrypted JSON type.
*
* @param string $code The encapsulation code, e.g. ENCAPSULATION_AESCTR256
*
* @return array The information of the encapsulation handler
*/
public function getEncapsulationInfoByCode($code)
{
// Normalise the code
$code = strtoupper($code);
// If we have no idea what the encapsulation should be revert to raw (plain text)
if (!isset($this->encapsulations[$code]))
{
return $this->encapsulations['ENCAPSULATION_RAW'];
}
return $this->encapsulations[$code];
}
/**
* Decodes the data. For encrypted encapsulations this means base64-decoding the data, decrypting it and then JSON-
* decoding the result. If any error occurs along the way the appropriate exception is thrown.
*
* The data being decoded corresponds to the Request Body described in the API documentation
*
* @param int $encapsulation The encapsulation type
* @param string $data Encoded data
*
* @return array The decoded data.
*
* @throw \RuntimeException When the server capabilities don't match the requested encapsulation
* @throw \InvalidArgumentException When $data cannot be decoded successfully
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02.html
*/
public function decode($encapsulation, $data)
{
$body = null;
// Find the suitable handler and encode the data
foreach ($this->handlers as $handler)
{
if ($handler->isSupported($encapsulation))
{
$body = $handler->decode($this->serverKey, $data);
break;
}
}
// If the data cannot be encoded throw an exception
if (!isset($handler) || is_null($body))
{
throw new \RuntimeException('The requested encapsulation type is not supported', 503);
}
$authorised = true;
$body = rtrim($body, chr(0));
// Make sure it looks like a valid JSON string and is at least 12 characters (minimum valid message length)
if ((strlen($body) < 12) || (substr($body, 0, 1) != '{') || (substr($body, -1) != '}'))
{
$authorised = false;
}
// Try to JSON decode the body
if ($authorised)
{
$body = json_decode($body, true);
if (is_null($body))
{
$authorised = false;
}
elseif (!is_array($body))
{
$authorised = false;
}
}
// Make sure there is a requested method
if ($authorised)
{
if (!isset($body['method']) || empty($body['method']))
{
$authorised = false;
}
}
if ($authorised)
{
$authorised = $handler->isAuthorised($this->serverKey, $body);
}
if (!$authorised)
{
throw new \InvalidArgumentException('Authentication failed', 401);
}
return (array)$body;
}
/**
* Encodes the data. The data is JSON encoded by this method before encapsulation takes place. Encrypted
* encapsulations will then encrypt the data and base64-encode it before returning it.
*
* The data being encoded correspond to the body > data structure described in the API documentation
*
* @param int $encapsulation The encapsulation type
* @param mixed $data The data to encode, typically a string, array or object
* @param string $key Key to use for encoding. If not provided we revert to $this->serverKey
*
* @return string The encapsulated data
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02s02.html
*
* @throw \RuntimeException When the server capabilities don't match the requested encapsulation
* @throw \InvalidArgumentException When $data cannot be converted to JSON
*/
public function encode($encapsulation, $data, $key = null)
{
// Try to JSON-encode the data
$data = json_encode($data);
// If the data cannot be JSON-encoded throw an exception
if ($data === false)
{
throw new \InvalidArgumentException('Data cannot be encapsulated in the requested format', 500);
}
// Make sure we have a valid key
if (empty($key))
{
$key = $this->serverKey;
}
// Find the suitable handler and encode the data
foreach ($this->handlers as $handler)
{
if ($handler->isSupported($encapsulation))
{
return $handler->encode($key, $data);
}
}
// If the data cannot be encoded throw an exception
throw new \RuntimeException('Data cannot be encapsulated in the requested format', 500);
}
/**
* Initialises the encapsulation handlers
*
* @return void
*/
protected function initialiseHandlers()
{
// Reset the arrays
$this->handlers = array();
$this->encapsulations = array();
// Look all files in the Encapsulation handlers' directory
$dh = new \DirectoryIterator(__DIR__ . '/Encapsulation');
/** @var \DirectoryIterator $entry */
foreach ($dh as $entry)
{
$fileName = $entry->getFilename();
// Ignore non-PHP files
if (substr($fileName, -4) != '.php')
{
continue;
}
// Ignore the Base class
if ($fileName == 'Base.php')
{
continue;
}
// Get the class name
$className = '\\Solo\\Model\\Json\\Encapsulation\\' . substr($fileName, 0, -4);
// Check if the class really exists
if (!class_exists($className, true))
{
continue;
}
/** @var EncapsulationInterface $o */
$o = new $className;
$info = $o->getInformation();
$this->encapsulations[$info['code']] = $info;
$this->handlers[] = $o;
}
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Encapsulation;
use Akeeba\Engine\Factory;
/**
* AES CBC 128 encapsulation
*/
class AesCbc128 extends Base
{
/**
* Constructs the encapsulation handler object
*/
function __construct()
{
parent::__construct(4, 'ENCAPSULATION_AESCBC128', ' Data in AES-128 standard (CBC) mode encrypted JSON');
}
/**
* Decodes the data. For encrypted encapsulations this means base64-decoding the data, decrypting it and then JSON-
* decoding the result. If any error occurs along the way the appropriate exception is thrown.
*
* The data being decoded corresponds to the Request Body described in the API documentation
*
* @param string $serverKey The server key we need to decode data
* @param string $data Encoded data
*
* @return string The decoded data.
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be decoded successfully
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02.html
*/
public function decode($serverKey, $data)
{
$data = base64_decode($data);
return $this->getEncryption()->AESDecryptCBC($data, $serverKey, 128);
}
/**
* Encodes the data. The data is JSON encoded by this method before encapsulation takes place. Encrypted
* encapsulations will then encrypt the data and base64-encode it before returning it.
*
* The data being encoded correspond to the body > data structure described in the API documentation
*
* @param string $serverKey The server key we need to encode data
* @param mixed $data The data to encode, typically a string, array or object
*
* @return string The encapsulated data
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02s02.html
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be converted to JSON
*/
public function encode($serverKey, $data)
{
return base64_encode($this->getEncryption()->AESEncryptCBC($data, $serverKey, 128));
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Encapsulation;
use Akeeba\Engine\Factory;
/**
* AES CBC 256 encapsulation
*/
class AesCbc256 extends Base
{
/**
* Constructs the encapsulation handler object
*/
function __construct()
{
parent::__construct(5, 'ENCAPSULATION_AESCBC256', ' Data in AES-256 standard (CBC) mode encrypted JSON');
}
/**
* Decodes the data. For encrypted encapsulations this means base64-decoding the data, decrypting it and then JSON-
* decoding the result. If any error occurs along the way the appropriate exception is thrown.
*
* The data being decoded corresponds to the Request Body described in the API documentation
*
* @param string $serverKey The server key we need to decode data
* @param string $data Encoded data
*
* @return string The decoded data.
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be decoded successfully
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02.html
*/
public function decode($serverKey, $data)
{
$data = base64_decode($data);
return $this->getEncryption()->AESDecryptCBC($data, $serverKey, 256);
}
/**
* Encodes the data. The data is JSON encoded by this method before encapsulation takes place. Encrypted
* encapsulations will then encrypt the data and base64-encode it before returning it.
*
* The data being encoded correspond to the body > data structure described in the API documentation
*
* @param string $serverKey The server key we need to encode data
* @param mixed $data The data to encode, typically a string, array or object
*
* @return string The encapsulated data
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02s02.html
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be converted to JSON
*/
public function encode($serverKey, $data)
{
return base64_encode($this->getEncryption()->AESEncryptCBC($data, $serverKey, 256));
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Encapsulation;
use Akeeba\Engine\Factory;
/**
* AES CTR 128 encapsulation
*/
class AesCtr128 extends Base
{
/**
* Constructs the encapsulation handler object
*/
function __construct()
{
parent::__construct(2, 'ENCAPSULATION_AESCTR128', 'Data in AES-128 stream (CTR) mode encrypted JSON');
}
/**
* Decodes the data. For encrypted encapsulations this means base64-decoding the data, decrypting it and then JSON-
* decoding the result. If any error occurs along the way the appropriate exception is thrown.
*
* The data being decoded corresponds to the Request Body described in the API documentation
*
* @param string $serverKey The server key we need to decode data
* @param string $data Encoded data
*
* @return string The decoded data.
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be decoded successfully
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02.html
*/
public function decode($serverKey, $data)
{
return $this->getEncryption()->AESDecryptCtr($data, $serverKey, 128);
}
/**
* Encodes the data. The data is JSON encoded by this method before encapsulation takes place. Encrypted
* encapsulations will then encrypt the data and base64-encode it before returning it.
*
* The data being encoded correspond to the body > data structure described in the API documentation
*
* @param string $serverKey The server key we need to encode data
* @param mixed $data The data to encode, typically a string, array or object
*
* @return string The encapsulated data
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02s02.html
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be converted to JSON
*/
public function encode($serverKey, $data)
{
return $this->getEncryption()->AESEncryptCtr($data, $serverKey, 128);
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Encapsulation;
use Akeeba\Engine\Factory;
/**
* AES CTR 256 encapsulation
*/
class AesCtr256 extends Base
{
/**
* Constructs the encapsulation handler object
*/
function __construct()
{
parent::__construct(3, 'ENCAPSULATION_AESCTR256', 'Data in AES-256 stream (CTR) mode encrypted JSON');
}
/**
* Decodes the data. For encrypted encapsulations this means base64-decoding the data, decrypting it and then JSON-
* decoding the result. If any error occurs along the way the appropriate exception is thrown.
*
* The data being decoded corresponds to the Request Body described in the API documentation
*
* @param string $serverKey The server key we need to decode data
* @param string $data Encoded data
*
* @return string The decoded data.
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be decoded successfully
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02.html
*/
public function decode($serverKey, $data)
{
return $this->getEncryption()->AESDecryptCtr($data, $serverKey, 256);
}
/**
* Encodes the data. The data is JSON encoded by this method before encapsulation takes place. Encrypted
* encapsulations will then encrypt the data and base64-encode it before returning it.
*
* The data being encoded correspond to the body > data structure described in the API documentation
*
* @param string $serverKey The server key we need to encode data
* @param mixed $data The data to encode, typically a string, array or object
*
* @return string The encapsulated data
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02s02.html
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be converted to JSON
*/
public function encode($serverKey, $data)
{
return $this->getEncryption()->AESEncryptCtr($data, $serverKey, 256);
}
}

View File

@@ -0,0 +1,163 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Encapsulation;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Util\Encrypt;
use Solo\Model\Json\EncapsulationInterface;
abstract class Base implements EncapsulationInterface
{
/**
* The numeric ID of this encapsulation
*
* @var int
*/
protected $id = 0;
/**
* The code of this encapsulation
*
* @var string
*/
protected $code = 'ENCAPSULATION_VOID';
/**
* The description of this encapsulation
*
* @var string
*/
protected $description = 'Invalid encapsulation';
/**
* The encryption object which is set up for use with the JSON API
*
* @var Encrypt
*/
private $encryption;
/**
* Public constructor. Called by children to customise the encapsulation handler object
*
* @param int $id Numeric ID
* @param string $code Code
* @param string $description Human readable description
*/
function __construct($id, $code, $description)
{
$this->id = $id;
$this->code = strtoupper($code);
$this->description = $description;
}
/**
* Returns information about the encapsulation supported by this class. The return array has the following keys:
* id: The numeric ID of the encapsulation, e.g. 3
* code: The short code of the encapsulation, e.g. ENCAPSULATION_AESCTR256
* description: A human readable descriptions, e.g. "Data in AES-256 stream (CTR) mode encrypted JSON"
*
* @return array See above
*/
public function getInformation()
{
return array(
'id' => $this->id,
'code' => $this->code,
'description' => $this->description,
);
}
/**
* Checks if the request body authorises the user to use the API. Each encapsulation can implement its own
* authorisation method. This method is only called after the request body has been successfully decoded, therefore
* encrypted encapsulations can simply return true.
*
* @param string $serverKey The server key we need to check the authorisation
* @param array $body The decoded body (as returned by the decode() method)
*
* @return bool True if authorised
*/
public function isAuthorised($serverKey, $body)
{
return true;
}
/**
* Is the provided encapsulation type supported by this class?
*
* @param int $encapsulation Encapsulation type
*
* @return bool True if supported
*/
public function isSupported($encapsulation)
{
return $encapsulation == $this->id;
}
/**
* Decodes the data. For encrypted encapsulations this means base64-decoding the data, decrypting it and then JSON-
* decoding the result. If any error occurs along the way the appropriate exception is thrown.
*
* The data being decoded corresponds to the Request Body described in the API documentation
*
* @param string $serverKey The server key we need to decode data
* @param string $data Encoded data
*
* @return string The decoded data.
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be decoded successfully
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02.html
*/
public function decode($serverKey, $data)
{
}
/**
* Encodes the data. The data is JSON encoded by this method before encapsulation takes place. Encrypted
* encapsulations will then encrypt the data and base64-encode it before returning it.
*
* The data being encoded correspond to the body > data structure described in the API documentation
*
* @param string $serverKey The server key we need to encode data
* @param mixed $data The data to encode, typically a string, array or object
*
* @return string The encapsulated data
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02s02.html
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be converted to JSON
*/
public function encode($serverKey, $data)
{
}
/**
* Returns an encryption object normalized for use in the JSON API: PBKDF2 uses a dynamic salt with SHA1 algorithm.
* This is necessary when we are running a backup against a profile which uses a static salt. In this case the
* static salt is not included in the ciphertext, making it impossible for the remote side to decipher our message,
* leading to backup failure.
*
* @return Encrypt
*/
protected function getEncryption()
{
if (is_null($this->encryption))
{
$encryption = Factory::getEncryption();
$this->encryption = clone $encryption;
$this->encryption->setPbkdf2UseStaticSalt(false);
$this->encryption->setPbkdf2Algorithm('sha1');
}
return $this->encryption;
}
}

View File

@@ -0,0 +1,89 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Encapsulation;
/**
* Raw (plain text) encapsulation
*/
class Raw extends Base
{
/**
* Constructs the encapsulation handler object
*/
function __construct()
{
parent::__construct(1, 'ENCAPSULATION_RAW', 'Data in plain-text JSON');
}
/**
* Decodes the data. For encrypted encapsulations this means base64-decoding the data, decrypting it and then JSON-
* decoding the result. If any error occurs along the way the appropriate exception is thrown.
*
* The data being decoded corresponds to the Request Body described in the API documentation
*
* @param string $serverKey The server key we need to decode data
* @param string $data Encoded data
*
* @return string The decoded data.
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be decoded successfully
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02.html
*/
public function decode($serverKey, $data)
{
return $data;
}
/**
* Encodes the data. The data is JSON encoded by this method before encapsulation takes place. Encrypted
* encapsulations will then encrypt the data and base64-encode it before returning it.
*
* The data being encoded correspond to the body > data structure described in the API documentation
*
* @param string $serverKey The server key we need to encode data
* @param mixed $data The data to encode, typically a string, array or object
*
* @return string The encapsulated data
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02s02.html
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be converted to JSON
*/
public function encode($serverKey, $data)
{
return $data;
}
/**
* Checks if the request body authorises the user to use the API. Each encapsulation can implement its own
* authorisation method. This method is only called after the request body has been successfully decoded, therefore
* encrypted encapsulations can simply return true.
*
* @param string $serverKey The server key we need to check the authorisation
* @param array $body The decoded body (as returned by the decode() method)
*
* @return bool True if authorised
*/
public function isAuthorised($serverKey, $body)
{
$authenticated = false;
if (isset($body['challenge']) && (strpos($body['challenge'], ':') >= 2) && (strlen($body['challenge']) >= 3))
{
list ($challengeData, $providedHash) = explode(':', $body['challenge']);
$computedHash = strtolower(md5($challengeData . $serverKey));
$authenticated = ($computedHash == $providedHash);
}
return $authenticated;
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json;
/**
* Interface for Encapsulation data handlers
*/
interface EncapsulationInterface
{
/**
* Is the provided encapsulation type supported by this class?
*
* @param int $encapsulation Encapsulation type
*
* @return bool True if supported
*/
public function isSupported($encapsulation);
/**
* Returns information about the encapsulation supported by this class. The return array has the following keys:
* id: The numeric ID of the encapsulation, e.g. 3
* code: The short code of the encapsulation, e.g. ENCAPSULATION_AESCTR256
* description: A human readable descriptions, e.g. "Data in AES-256 stream (CTR) mode encrypted JSON"
*
* @return array See above
*/
public function getInformation();
/**
* Decodes the data. For encrypted encapsulations this means base64-decoding the data, decrypting it but *NOT* JSON-
* decoding the result. If any error occurs along the way the appropriate exception is thrown.
*
* The data being decoded corresponds to the Request Body described in the API documentation
*
* @param string $serverKey The server key we need to decode data
* @param string $data Encoded data
*
* @return string The decoded data.
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be decoded successfully
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02.html
*/
public function decode($serverKey, $data);
/**
* Encodes the data. The data is JSON encoded by this method before encapsulation takes place. Encrypted
* encapsulations will then encrypt the data and base64-encode it before returning it.
*
* The data being encoded correspond to the body > data structure described in the API documentation
*
* @param string $serverKey The server key we need to encode data
* @param mixed $data The data to encode, typically a string, array or object
*
* @return string The encapsulated data
*
* @see https://www.akeebabackup.com/documentation/json-api/ar01s02s02.html
*
* @throws \RuntimeException When the server capabilities don't match the requested encapsulation
* @throws \InvalidArgumentException When $data cannot be converted to JSON
*/
public function encode($serverKey, $data);
/**
* Checks if the request body authorises the user to use the API. Each encapsulation can implement its own
* authorisation method. This method is only called after the request body has been successfully decoded, therefore
* encrypted encapsulations can simply return true.
*
* @param string $serverKey The server key we need to check the authorisation
* @param array $body The decoded body (as returned by the decode() method)
*
* @return bool True if authorised
*/
public function isAuthorised($serverKey, $body);
}

View File

@@ -0,0 +1,110 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json;
/**
* Handles task execution
*/
class Task
{
/** @var TaskInterface[] The task handlers known to us */
protected $handlers = array();
/**
* Public constructor. Populates the list of task handlers.
*/
public function __construct()
{
// Populate the list of task handlers
$this->initialiseHandlers();
}
/**
* Do I have a specific task handling method?
*
* @param string $method The method to check for
*
* @return bool
*/
public function hasMethod($method)
{
$method = strtolower($method);
return isset($this->handlers[$method]);
}
/**
* Execute a JSON API method
*
* @param string $method The method's name
* @param array $parameters The parameters to the method (optional)
*
* @return mixed
*
* @throws \RuntimeException When the method requested is not known to us
*/
public function execute($method, $parameters = array())
{
if (!$this->hasMethod($method))
{
throw new \RuntimeException("Invalid method $method", 405);
}
$method = strtolower($method);
return $this->handlers[$method]->execute($parameters);
}
/**
* Initialises the encapsulation handlers
*
* @return void
*/
protected function initialiseHandlers()
{
// Reset the array
$this->handlers = array();
// Look all files in the Task handlers' directory
$dh = new \DirectoryIterator(__DIR__ . '/Task');
/** @var \DirectoryIterator $entry */
foreach ($dh as $entry)
{
$fileName = $entry->getFilename();
// Ignore non-PHP files
if (substr($fileName, -4) != '.php')
{
continue;
}
// Ignore the Base class
if ($fileName == 'Base.php')
{
continue;
}
// Get the class name
$className = '\\Solo\\Model\\Json\\Task\\' . substr($fileName, 0, -4);
// Check if the class really exists
if (!class_exists($className, true))
{
continue;
}
/** @var TaskInterface $o */
$o = new $className;
$name = $o->getMethodName();
$name = strtolower($name);
$this->handlers[$name] = $o;
}
}
}

View File

@@ -0,0 +1,72 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Browser;
use Solo\Model\Json\TaskInterface;
/**
* Return folder browser results
*/
class Browse implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'browse';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'folder' => '',
'processfolder' => 0
);
$defConfig = array_merge($defConfig, $parameters);
$folder = $filter->clean($defConfig['folder'], 'string');
$processFolder = $filter->clean($defConfig['processfolder'], 'bool');
/** @var \Solo\Model\Browser $model */
$model = new Browser();
$model->setState('folder', $folder);
$model->setState('processfolder', $processFolder);
$model->makeListing();
$ret = array(
'folder' => $model->getState('folder'),
'folder_raw' => $model->getState('folder_raw'),
'parent' => $model->getState('parent'),
'exists' => $model->getState('exists'),
'inRoot' => $model->getState('inRoot'),
'openbasedirRestricted' => $model->getState('openbasedirRestricted'),
'writable' => $model->getState('writable'),
'subfolders' => $model->getState('subfolders'),
'breadcrumbs' => $model->getState('breadcrumbs'),
);
return $ret;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Manage;
/**
* Delete a backup record
*/
class Delete implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'delete';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'backup_id' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$backup_id = (int)$defConfig['backup_id'];
$model = new Manage();
$model->setState('id', $backup_id);
try
{
$model->delete();
}
catch (\Exception $e)
{
throw new \RuntimeException($e->getMessage(), 500);
}
return true;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Manage;
/**
* Delete the backup archives of a backup record
*/
class DeleteFiles implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'deleteFiles';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'backup_id' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$backup_id = (int)$defConfig['backup_id'];
$model = new Manage();
$model->setState('id', $backup_id);
try
{
$model->deleteFile();
}
catch (\Exception $e)
{
throw new \RuntimeException($e->getMessage(), 500);
}
return true;
}
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Profiles;
/**
* Delete a backup profile
*/
class DeleteProfile implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'deleteProfile';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$profile = (int)$defConfig['profile'];
// You need to specify the profile
if (empty($profile))
{
throw new \RuntimeException('Invalid profile ID', 404);
}
if ($profile == 1)
{
throw new \RuntimeException('You cannot delete the default backup profile', 404);
}
// Get a profile model
$profileModel = new Profiles();
$profileModel->findOrFail($profile);
$profileModel->delete();
return true;
}
}

View File

@@ -0,0 +1,109 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use Solo\Model\Json\TaskInterface;
/**
* Download a chunk of a backup archive over HTTP
*/
class Download implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'download';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'backup_id' => 0,
'part_id' => 1,
'segment' => 1,
'chunk_size' => 1
);
$defConfig = array_merge($defConfig, $parameters);
$backup_id = (int)$defConfig['backup_id'];
$part_id = (int)$defConfig['part_id'];
$segment = (int)$defConfig['segment'];
$chunk_size = (int)$defConfig['chunk_size'];
$backup_stats = Platform::getInstance()->get_statistics($backup_id);
if (empty($backup_stats))
{
// Backup record doesn't exist
throw new \RuntimeException('Invalid backup record identifier', 404);
}
$files = Factory::getStatistics()->get_all_filenames($backup_stats);
if ((count($files) < $part_id) || ($part_id <= 0))
{
// Invalid part
throw new \RuntimeException('Invalid backup part', 404);
}
$file = $files[ $part_id - 1 ];
$filesize = @filesize($file);
$seekPos = $chunk_size * 1048576 * ($segment - 1);
if ($seekPos > $filesize)
{
// Trying to seek past end of file
throw new \RuntimeException('Invalid segment', 404);
}
$fp = fopen($file, 'rb');
if ($fp === false)
{
// Could not read file
throw new \RuntimeException('Error reading backup archive', 500);
}
rewind($fp);
if (fseek($fp, $seekPos, SEEK_SET) === -1)
{
// Could not seek to position
throw new \RuntimeException('Error reading specified segment', 500);
}
$buffer = fread($fp, 1048576);
if ($buffer === false)
{
throw new \RuntimeException('Error reading specified segment', 500);
}
fclose($fp);
return base64_encode($buffer);
}
}

View File

@@ -0,0 +1,172 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use Awf\Application\Application;
use Solo\Model\Json\TaskInterface;
/**
* Download an entire backup archive directly over HTTP
*/
class DownloadDirect implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'downloadDirect';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'backup_id' => 0,
'part_id' => 1,
);
$defConfig = array_merge($defConfig, $parameters);
$backup_id = (int)$defConfig['backup_id'];
$part_id = (int)$defConfig['part_id'];
$container = Application::getInstance()->getContainer();
$backup_stats = Platform::getInstance()->get_statistics($backup_id);
if (empty($backup_stats))
{
// Backup record doesn't exist
@ob_end_clean();
header('HTTP/1.1 500 Invalid backup record identifier');
flush();
$container->application->close();
}
$files = Factory::getStatistics()->get_all_filenames($backup_stats);
if ((count($files) < $part_id) || ($part_id <= 0))
{
// Invalid part
@ob_end_clean();
header('HTTP/1.1 500 Invalid backup part');
flush();
$container->application->close();
}
$filename = $files[ $part_id - 1 ];
@clearstatcache();
// For a certain unmentionable browser
if (function_exists('ini_get') && function_exists('ini_set'))
{
if (ini_get('zlib.output_compression'))
{
ini_set('zlib.output_compression', 'Off');
}
}
// Remove php's time limit
if (function_exists('ini_get') && function_exists('set_time_limit'))
{
if (!ini_get('safe_mode'))
{
@set_time_limit(0);
}
}
$basename = @basename($filename);
$fileSize = @filesize($filename);
$extension = strtolower(str_replace(".", "", strrchr($filename, ".")));
while (@ob_end_clean())
{
;
}
@clearstatcache();
// Send MIME headers
header('MIME-Version: 1.0');
header('Content-Disposition: attachment; filename="' . $basename . '"');
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
switch ($extension)
{
case 'zip':
// ZIP MIME type
header('Content-Type: application/zip');
break;
default:
// Generic binary data MIME type
header('Content-Type: application/octet-stream');
break;
}
// Notify of file size, if this info is available
if ($fileSize > 0)
{
header('Content-Length: ' . @filesize($filename));
}
// Disable caching
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Expires: 0");
header('Pragma: no-cache');
flush();
if ($fileSize > 0)
{
// If the filesize is reported, use 1M chunks for echoing the data to the browser
$blockSize = 1048576; //1M chunks
$handle = @fopen($filename, "r");
// Now we need to loop through the file and echo out chunks of file data
if ($handle !== false)
{
while (!@feof($handle))
{
echo @fread($handle, $blockSize);
@ob_flush();
flush();
}
}
if ($handle !== false)
{
@fclose($handle);
}
}
else
{
// If the filesize is not reported, hope that readfile works
@readfile($filename);
}
flush();
$container->application->close();
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Factory;
use Solo\Application;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Profiles;
/**
* Export the profile's configuration
*/
class ExportConfiguration implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'exportConfiguration';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$profile_id = (int)$defConfig['profile'];
if ($profile_id <= 0)
{
$profile_id = 1;
}
/** @var Profiles $profile */
$profile = new Profiles();
$data = $profile->findOrFail($profile_id)->getData();
if (substr($data['configuration'], 0, 12) == '###AES128###')
{
// Load the server key file if necessary
if (!defined('AKEEBA_SERVERKEY'))
{
$filename = \Awf\Application\Application::getInstance()->getContainer()->basePath . '/engine/secretkey.php';
include_once $filename;
}
$key = Factory::getSecureSettings()->getKey();
$data['configuration'] = Factory::getSecureSettings()->decryptSettings($data['configuration'], $key);
}
return array(
'description' => $data['description'],
'configuration' => $data['configuration'],
'filters' => $data['filters'],
);
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use Solo\Model\Json\TaskInterface;
/**
* Get information for a given backup record
*/
class GetBackupInfo implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getBackupInfo';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'backup_id' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$backup_id = (int)$defConfig['backup_id'];
// Get the basic statistics
$record = Platform::getInstance()->get_statistics($backup_id);
// Get a list of filenames
$backup_stats = Platform::getInstance()->get_statistics($backup_id);
// Backup record doesn't exist
if (empty($backup_stats))
{
throw new \RuntimeException('Invalid backup record identifier', 404);
}
$filenames = Factory::getStatistics()->get_all_filenames($record);
if (empty($filenames))
{
// Archives are not stored on the server or no files produced
$record['filenames'] = array();
}
else
{
$filedata = array();
$i = 0;
// Get file sizes per part
foreach ($filenames as $file)
{
$i++;
$size = @filesize($file);
$size = is_numeric($size) ? $size : 0;
$filedata[] = array(
'part' => $i,
'name' => basename($file),
'size' => $size
);
}
// Add the file info to $record['filenames']
$record['filenames'] = $filedata;
}
return $record;
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Dbfilters;
use Solo\Model\Json\TaskInterface;
/**
* Get the database entities along with their filtering status (typically for rendering a GUI)
*/
class GetDBEntities implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getDBEntities';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'root' => '[SITEDB]',
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$root = $filter->clean($defConfig['root'], 'string');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a root
if (empty($root))
{
throw new \RuntimeException('Unknown database root', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Dbfilters $model */
$model = new Dbfilters();
return $model->make_listing($root);
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Dbfilters;
use Solo\Model\Json\TaskInterface;
/**
* Get the database filters
*/
class GetDBFilters implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getDBFilters';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'root' => '[SITEDB]',
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$root = $filter->clean($defConfig['root'], 'string');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a root
if (empty($root))
{
throw new \RuntimeException('Unknown database root', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Dbfilters $model */
$model = new Dbfilters();
return $model->get_filters($root);
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Dbfilters;
use Solo\Model\Json\TaskInterface;
/**
* Get the database roots (database definitions)
*/
class GetDBRoots implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getDBRoots';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$profile = (int)$defConfig['profile'];
if ($profile <= 0)
{
$profile = 1;
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Dbfilters $model */
$model = new Dbfilters();
return $model->get_roots();
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Fsfilters;
use Solo\Model\Json\TaskInterface;
/**
* Get the filesystem entities along with their filtering status (typically for rendering a GUI)
*/
class GetFSEntities implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getFSEntities';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'root' => '[SITEROOT]',
'subdirectory' => '',
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$root = $filter->clean($defConfig['root'], 'string');
$subdirectory = $filter->clean($defConfig['subdirectory'], 'path');
$crumbs = array();
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a root
if (empty($root))
{
throw new \RuntimeException('Unknown filesystem root', 500);
}
// Get the subdirectory and explode it to its parts
if (!empty($subdirectory))
{
$subdirectory = trim($subdirectory, '/');
}
if (!empty($subdirectory))
{
$crumbs = explode('/', $subdirectory);
}
// Set the active profile
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Fsfilters $model */
$model = new Fsfilters();
return $model->make_listing($root, $crumbs);
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Fsfilters;
use Solo\Model\Json\TaskInterface;
/**
* Get the filesystem filters
*/
class GetFSFilters implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getFSFilters';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'root' => '[SITEROOT]',
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$root = $filter->clean($defConfig['root'], 'string');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a root
if (empty($root))
{
throw new \RuntimeException('Unknown filesystem root', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Fsfilters $model */
$model = new Fsfilters();
return $model->get_filters($root);
}
}

View File

@@ -0,0 +1,65 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Fsfilters;
use Solo\Model\Json\TaskInterface;
/**
* Get the filesystem roots (site root and extra included directories)
*/
class GetFSRoots implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getFSRoots';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$profile = (int)$defConfig['profile'];
if ($profile <= 0)
{
$profile = 1;
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Fsfilters $model */
$model = new Fsfilters();
return $model->get_roots();
}
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Json\TaskInterface;
/**
* Get the GUI definitions for the configuration page
*/
class GetGUIConfiguration implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getGUIConfiguration';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$profile = (int)$defConfig['profile'];
if ($profile <= 0)
{
$profile = 1;
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
return Factory::getEngineParamsProvider()->getJsonGuiDefinition();
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Multidb;
/**
* Get the extra included databases
*/
class GetIncludedDBs implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getIncludedDBs';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$profile = (int)$defConfig['profile'];
if ($profile <= 0)
{
$profile = 1;
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Multidb $model */
$model = new Multidb();
return $model->get_databases();
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Extradirs;
use Solo\Model\Json\TaskInterface;
/**
* Get the extra included directories
*/
class GetIncludedDirectories implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getIncludedDirectories';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$profile = (int)$defConfig['profile'];
if ($profile <= 0)
{
$profile = 1;
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Extradirs $model */
$model = new Extradirs();
return $model->get_directories();
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Profiles;
/**
* Get a list of known backup profiles
*/
class GetProfiles implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getProfiles';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$model = new Profiles();
$profiles = $model->get(true);
$ret = array();
if (count($profiles))
{
foreach ($profiles as $profile)
{
$temp = new \stdClass();
$temp->id = $profile->id;
$temp->name = $profile->description;
$ret[] = $temp;
}
}
return $ret;
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Regexdbfilters;
/**
* Get the regex database filters
*/
class GetRegexDBFilters implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getRegexDBFilters';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'root' => '[SITEDB]',
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$root = $filter->clean($defConfig['root'], 'string');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a root
if (empty($root))
{
throw new \RuntimeException('Unknown database root', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Regexdbfilters $model */
$model = new Regexdbfilters();
return $model->get_regex_filters($root);
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Regexfsfilters;
/**
* Get the regex filesystem filters
*/
class GetRegexFSFilters implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getRegexFSFilters';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'root' => '[SITEROOT]',
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$root = $filter->clean($defConfig['root'], 'string');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a root
if (empty($root))
{
throw new \RuntimeException('Unknown database root', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Regexfsfilters $model */
$model = new Regexfsfilters();
return $model->get_regex_filters($root);
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Awf\Registry\Registry;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Update;
/**
* Get the version information of Akeeba Solo
*/
class GetVersion implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'getVersion';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$update = new Update();
$updateInformation = $update->getUpdateInformation();
if (is_object($updateInformation) && ($updateInformation instanceof Registry))
{
$updateInformation = $updateInformation->toArray();
}
if (is_array($updateInformation) && array_key_exists('releasenotes', $updateInformation))
{
unset ($updateInformation['releasenotes']);
}
$edition = AKEEBABACKUP_PRO ? 'pro' : 'core';
return (object)array(
'api' => AKEEBA_JSON_API_VERSION,
'component' => AKEEBABACKUP_VERSION,
'date' => AKEEBABACKUP_DATE,
'edition' => $edition,
'updateinfo' => $updateInformation,
);
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Profiles;
/**
* Import the profile's configuration
*/
class ImportConfiguration implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'importConfiguration';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'data' => null,
);
$defConfig = array_merge($defConfig, $parameters);
$profile_id = (int)$defConfig['profile'];
$data = $defConfig['data'];
if ($profile_id <= 0)
{
$profile_id = 0;
}
/** @var Profiles $profile */
$profile = new Profiles();
if ($profile_id)
{
$profile->find($profile_id);
}
$profile->import($data);
return true;
}
}

View File

@@ -0,0 +1,56 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Manage;
/**
* List the backup records
*/
class ListBackups implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'listBackups';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'from' => 0,
'limit' => 50
);
$defConfig = array_merge($defConfig, $parameters);
$from = (int)$defConfig['from'];
$limit = (int)$defConfig['limit'];
$model = new Manage();
$model->setState('limitstart', $from);
$model->setState('limit', $limit);
return $model->getStatisticsListWithMeta(false);
}
}

View File

@@ -0,0 +1,52 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Factory;
use Solo\Model\Json\TaskInterface;
/**
* Get the log contents
*/
class Log implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'log';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'tag' => 'remote'
);
$defConfig = array_merge($defConfig, $parameters);
$tag = (int)$defConfig['tag'];
$filename = Factory::getLog()->getLogFilename($tag);
return file_get_contents($filename);
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Multidb;
/**
* Remove an extra database definition
*/
class RemoveIncludedDB implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'removeIncludedDB';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'name' => '',
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$name = $filter->clean($defConfig['name'], 'string');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a uuid
if (empty($name))
{
throw new \RuntimeException('The database name is required', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Multidb $model */
$model = new Multidb();
return $model->remove($name);
}
}

View File

@@ -0,0 +1,77 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Extradirs;
use Solo\Model\Json\TaskInterface;
/**
* Remove an extra directory definition
*/
class RemoveIncludedDirectory implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'removeIncludedDirectory';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'uuid' => '',
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$uuid = $filter->clean($defConfig['uuid'], 'string');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a uuid
if (empty($uuid))
{
throw new \RuntimeException('UUID is required', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Extradirs $model */
$model = new Extradirs();
return $model->remove($uuid);
}
}

View File

@@ -0,0 +1,80 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use Solo\Model\Json\TaskInterface;
/**
* Save the configuration for a given profile
*/
class SaveConfiguration implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'saveConfiguration';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'profile' => -1,
'engineconfig' => array()
);
$defConfig = array_merge($defConfig, $parameters);
$profile = (int)$defConfig['profile'];
$data = $defConfig['engineconfig'];
if (empty($profile))
{
throw new \RuntimeException('Invalid profile ID', 404);
}
// Forbid stupidly selecting the site's root as the output or temporary directory
if (array_key_exists('akeeba.basic.output_directory', $data))
{
$folder = $data['akeeba.basic.output_directory'];
$folder = Factory::getFilesystemTools()->translateStockDirs($folder, true, true);
$check = Factory::getFilesystemTools()->translateStockDirs('[SITEROOT]', true, true);
if ($check == $folder)
{
$data['akeeba.basic.output_directory'] = '[DEFAULT_OUTPUT]';
}
}
// Merge it
$config = Factory::getConfiguration();
$protectedKeys = $config->getProtectedKeys();
$config->resetProtectedKeys();
$config->mergeArray($data, false, false);
$config->setProtectedKeys($protectedKeys);
// Save configuration
return Platform::getInstance()->save_configuration($profile);
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Profiles;
/**
* Saves a backup profile
*/
class SaveProfile implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'saveProfile';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'description' => null,
'quickicon' => null,
'source' => 0,
);
$defConfig = array_merge($defConfig, $parameters);
$profile = (int)$defConfig['profile'];
$description = $defConfig['description'];
$quickicon = $defConfig['quickicon'];
$source = (int)$defConfig['source'];
if ($profile <= 0)
{
$profile = null;
}
// At least one of these parameters is required
if (empty($profile) && empty($source) && empty($description))
{
throw new \RuntimeException('Invalid profile ID', 404);
}
// Get a profile model
$profileModel = new Profiles();
// Load the profile
$sourceId = empty($profile) ? $source : $profile;
if (!empty($sourceId))
{
$profileModel->findOrFail($sourceId);
}
else
{
$profileModel->reset(true);
}
$profileModel->setFieldValue('id', $profile);
if ($description)
{
$profileModel->setFieldValue('description', $description);
}
if (!is_null($quickicon))
{
$profileModel->setFieldValue('quickicon', (int)$quickicon);
}
$profileModel->save();
return true;
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Dbfilters;
use Solo\Model\Json\TaskInterface;
/**
* Set or unset a database filter
*/
class SetDBFilter implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'setDBFilter';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'root' => '[SITEDB]',
'table' => '',
'type' => 'tables',
'status' => 1
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$root = $filter->clean($defConfig['root'], 'string');
$table = $filter->clean($defConfig['table'], 'string');
$type = $filter->clean($defConfig['type'], 'cmd');
$status = $filter->clean($defConfig['status'], 'bool');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a root
if (empty($root))
{
throw new \RuntimeException('Unknown database root', 500);
}
// We need a table name
if (empty($table))
{
throw new \RuntimeException('Table name is mandatory', 500);
}
// We need a table name
if (empty($type))
{
throw new \RuntimeException('Filter type is mandatory', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Dbfilters $model */
$model = new Dbfilters();
if ($status)
{
$ret = $model->setFilter($root, $table, $type);
}
else
{
$ret = $model->remove($root, $table, $type);
}
return $ret;
}
}

View File

@@ -0,0 +1,121 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Fsfilters;
use Solo\Model\Json\TaskInterface;
/**
* Set or unset a filesystem filter
*/
class SetFSFilter implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'setFSFilter';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'root' => '[SITEROOT]',
'path' => '',
'type' => '',
'status' => 1
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$root = $filter->clean($defConfig['root'], 'string');
$path = $filter->clean($defConfig['path'], 'path');
$type = $filter->clean($defConfig['type'], 'cmd');
$status = $filter->clean($defConfig['status'], 'bool');
$crumbs = array();
$node = '';
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a root
if (empty($root))
{
throw new \RuntimeException('Unknown filesystem root', 500);
}
// We need a path
if (empty($path))
{
throw new \RuntimeException('Unknown path', 500);
}
// Get the subdirectory and explode it to its parts
$path = trim($path, '/');
if (!empty($path))
{
$crumbs = explode('/', $root);
$node = array_pop($crumbs);
}
if (empty($node))
{
throw new \RuntimeException('Unknown path', 500);
}
// We need a table name
if (empty($type))
{
throw new \RuntimeException('Filter type is mandatory', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Fsfilters $model */
$model = new Fsfilters();
if ($status)
{
$ret = $model->setFilter($root, $crumbs, $node, $type);
}
else
{
$ret = $model->remove($root, $crumbs, $node, $type);
}
return $ret;
}
}

View File

@@ -0,0 +1,94 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Multidb;
/**
* Set up or edit an extra database definition
*/
class SetIncludedDB implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'setIncludedDB';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'name' => '',
'connection' => array(),
'test' => true,
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$name = $filter->clean($defConfig['name'], 'string');
$connection = $filter->clean($defConfig['connection'], 'array');
$test = $filter->clean($defConfig['test'], 'bool');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
if (
empty($connection) || !isset($connection['host']) || !isset($connection['driver'])
|| !isset($connection['database']) || !isset($connection['user'])
|| !isset($connection['password'])
)
{
throw new \RuntimeException('Connection information missing or incomplete', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Multidb $model */
$model = new Multidb();
if ($test)
{
$result = $model->test($connection);
if (!$result['status'])
{
throw new \RuntimeException('Connection test failed: ' . $result['message'], 500);
}
}
return $model->setFilter($name, $connection);
}
}

View File

@@ -0,0 +1,114 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Awf\Session\Randval;
use Awf\Utils\Phpfunc;
use Solo\Application;
use Solo\Model\Extradirs;
use Solo\Model\Json\TaskInterface;
/**
* Set up or edit an extra directory definition
*/
class SetIncludedDirectory implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'setIncludedDirectory';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'uuid' => '',
'path' => '',
'virtualFolder' => '',
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$path = $filter->clean($defConfig['path'], 'path');
$uuid = $filter->clean($defConfig['uuid'], 'string');
$virtualFolder = $filter->clean($defConfig['virtualFolder'], 'string');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a path
if (empty($path))
{
throw new \RuntimeException('Path is required', 500);
}
// We need a uuid
if (empty($uuid))
{
$uuid = $this->uuid_v4();
}
// We need a vf
if (empty($virtualFolder))
{
$virtualFolder = basename($path);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Extradirs $model */
$model = new Extradirs();
$data = array($path, $virtualFolder);
return $model->setFilter($uuid, $data);
}
/**
* Generate a UUID v4
*
* @return string
*/
private function uuid_v4()
{
$phpFunc = new Phpfunc();
$randval = new Randval($phpFunc);
$data = $randval->generate(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Regexdbfilters;
/**
* Set or unset a Regex database filter
*/
class SetRegexDBFilter implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'setRegexDBFilter';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'root' => '[SITEDB]',
'regex' => '',
'type' => 'tables',
'status' => 1
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$root = $filter->clean($defConfig['root'], 'string');
$regex = $filter->clean($defConfig['regex'], 'string');
$type = $filter->clean($defConfig['type'], 'cmd');
$status = $filter->clean($defConfig['status'], 'bool');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a root
if (empty($root))
{
throw new \RuntimeException('Unknown database root', 500);
}
// We need a regex name
if (empty($regex))
{
throw new \RuntimeException('Regex is mandatory', 500);
}
// We need a regex name
if (empty($type))
{
throw new \RuntimeException('Filter type is mandatory', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Regexdbfilters $model */
$model = new Regexdbfilters();
if ($status)
{
$ret = $model->setFilter($type, $root, $regex);
}
else
{
$ret = $model->remove($type, $root, $regex);
}
return $ret;
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Platform;
use Solo\Application;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Regexfsfilters;
/**
* Set or unset a Regex filesystem filter
*/
class SetRegexFSFilter implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'setRegexFSFilter';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 0,
'root' => '[SITEDB]',
'regex' => '',
'type' => 'directories',
'status' => 1
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$root = $filter->clean($defConfig['root'], 'string');
$regex = $filter->clean($defConfig['regex'], 'string');
$type = $filter->clean($defConfig['type'], 'cmd');
$status = $filter->clean($defConfig['status'], 'bool');
// We need a valid profile ID
if ($profile <= 0)
{
$profile = 1;
}
// We need a root
if (empty($root))
{
throw new \RuntimeException('Unknown database root', 500);
}
// We need a regex name
if (empty($regex))
{
throw new \RuntimeException('Regex is mandatory', 500);
}
// We need a regex name
if (empty($type))
{
throw new \RuntimeException('Filter type is mandatory', 500);
}
$session = Application::getInstance()->getContainer()->segment;
$session->set('profile', $profile);
// Load the configuration
Platform::getInstance()->load_configuration($profile);
/** @var \Solo\Model\Regexfsfilters $model */
$model = new Regexfsfilters();
if ($status)
{
$ret = $model->setFilter($type, $root, $regex);
}
else
{
$ret = $model->remove($type, $root, $regex);
}
return $ret;
}
}

View File

@@ -0,0 +1,104 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Factory;
use Akeeba\Engine\Platform;
use Awf\Application\Application;
use Awf\Mvc\Model;
use Solo\Model\Backup;
use Solo\Model\Json\TaskInterface;
/**
* Start a backup job
*/
class StartBackup implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'startBackup';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => 1,
'description' => '',
'comment' => '',
'backupid' => null,
'overrides' => array(),
);
$defConfig = array_merge($defConfig, $parameters);
$profile = (int) $defConfig['profile'];
$profile = max(1, $profile); // Make sure $profile is a positive integer >= 1
$description = $filter->clean($defConfig['description'], 'string');
$comment = $filter->clean($defConfig['comment'], 'string');
$backupid = $filter->clean($defConfig['backupid'], 'cmd');
$backupid = empty($backupid) ? null : $backupid; // Otherwise the Engine doesn't set a backup ID
$overrides = $filter->clean($defConfig['overrides'], 'array');
$container = Application::getInstance()->getContainer();
$session = $container->segment;
$session->set('profile', $profile);
define('AKEEBA_PROFILE', $profile);
/**
* DO NOT REMOVE!
*
* The Model will only try to load the configuration after nuking the factory. This causes Profile 1 to be
* loaded first. Then it figures out it needs to load a different profile and it does but the protected keys
* are NOT replaced, meaning that certain configuration parameters are not replaced. Most notably, the chain.
* This causes backups to behave weirdly. So, DON'T REMOVE THIS UNLESS WE REFACTOR THE MODEL.
*/
Platform::getInstance()->load_configuration($profile);
/** @var Backup $model */
$model = Model::getTmpInstance($container->application_name, 'Backup', $container);
$model->setState('tag', AKEEBA_BACKUP_ORIGIN);
$model->setState('backupid', $backupid);
$model->setState('description', $description);
$model->setState('comment', $comment);
$array = $model->startBackup($overrides);
if ($array['Error'] != '')
{
throw new \RuntimeException('A backup error has occurred: ' . $array['Error'], 500);
}
// BackupID contains the numeric backup record ID. backupid contains the backup id (usually in the form id123)
$statistics = Factory::getStatistics();
$array['BackupID'] = $statistics->getId();
// Remote clients expect a boolean, not an integer.
$array['HasRun'] = ($array['HasRun'] === 0);
return $array;
}
}

View File

@@ -0,0 +1,98 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Akeeba\Engine\Factory;
use Awf\Application\Application;
use Awf\Mvc\Model;
use Solo\Model\Backup;
use Solo\Model\Json\TaskInterface;
/**
* Step through a backup job
*/
class StepBackup implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'stepBackup';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'profile' => null,
'tag' => AKEEBA_BACKUP_ORIGIN,
'backupid' => null,
);
$defConfig = array_merge($defConfig, $parameters);
$profile = $filter->clean($defConfig['profile'], 'int');
$tag = $filter->clean($defConfig['tag'], 'cmd');
$backupid = $filter->clean($defConfig['backupid'], 'cmd');
if (is_null($backupid) && defined('AKEEBA_BACKUP_ID'))
{
$tag = AKEEBA_BACKUP_ID;
}
if (empty($backupid))
{
throw new \RuntimeException("JSON API :: stepBackup -- You have not provided the required backupid parameter. This parameter is MANDATORY since May 2016. Please update your client software to include this parameter.");
}
$container = Application::getInstance()->getContainer();
$session = $container->segment;
if (!empty($profile))
{
$profile = max(1, $profile); // Make sure $profile is a positive integer >= 1
$session->set('profile', $profile);
define('AKEEBA_PROFILE', $profile);
}
/** @var Backup $model */
$model = Model::getTmpInstance($container->application_name, 'Backup', $container);
$model->setState('tag', $tag);
$model->setState('backupid', $backupid);
$array = $model->stepBackup(false);
if ($array['Error'] != '')
{
throw new \RuntimeException('A backup error has occurred: ' . $array['Error'], 500);
}
// BackupID contains the numeric backup record ID. backupid contains the backup id (usually in the form id123)
$statistics = Factory::getStatistics();
$array['BackupID'] = $statistics->getId();
// Remote clients expect a boolean, not an integer.
$array['HasRun'] = ($array['HasRun'] === 0);
return $array;
}
}

View File

@@ -0,0 +1,64 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Multidb;
/**
* Test an extra database definition
*/
class TestDBConnection implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'testDBConnection';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'connection' => array(),
);
$defConfig = array_merge($defConfig, $parameters);
$connection = $filter->clean($defConfig['connection'], 'array');
if (
empty($connection) || !isset($connection['host']) || !isset($connection['driver'])
|| !isset($connection['database']) || !isset($connection['user'])
|| !isset($connection['password'])
)
{
throw new \RuntimeException('Connection information missing or incomplete', 500);
}
/** @var \Solo\Model\Multidb $model */
$model = new Multidb();
return $model->test($connection);
}
}

View File

@@ -0,0 +1,52 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Update;
/**
* Download the update package
*/
class UpdateDownload implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'updateDownload';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$update = new Update();
$update->prepareDownload();
$ret = $update->stepDownload(false);
if (!$ret['status'])
{
throw new \RuntimeException($ret['errorCode'] . ': ' . $ret['error'], 500);
}
return true;
}
}

View File

@@ -0,0 +1,183 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Update;
define('KICKSTART', 1);
require_once APATH_ROOT . '/restore.php';
/**
* Extract the update package
*/
class UpdateExtract implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'updateExtract';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$update = new Update();
$update->createRestorationINI();
$ini_data = null;
$setupFile = APATH_ROOT . '/restoration.php';
if (!file_exists($setupFile))
{
throw new \RuntimeException("Could not create restoration.php for extracting the update file", 500);
}
// Load restoration.php. It creates a global variable named $restoration_setup
$restoration_setup = array();
require_once $setupFile;
$ini_data = $restoration_setup;
if (empty($ini_data))
{
// No parameters fetched. Darn, how am I supposed to work like that?!
throw new \RuntimeException("Could not read restoration.php for extracting the update file", 500);
}
foreach ($ini_data as $key => $value)
{
\AKFactory::set($key, $value);
}
\AKFactory::set('kickstart.enabled', true);
// Reinitialize $ini_data
$ini_data = null;
\AKFactory::nuke();
/** @var \AKAbstractUnarchiver $engine */
$engine = \AKFactory::getUnarchiver(); // Get the engine
$observer = new RestorationObserver(); // Create a new observer
$engine->attach($observer); // Attach the observer
do
{
$engine->tick();
$ret = $engine->getStatusArray();
}
while ($ret['HasRun'] && empty($ret['Error']));
if ($ret['Error'])
{
throw new \RuntimeException("Extraction error: " . $ret['Error'], 500);
}
// Remove the installation directory
$root = \AKFactory::get('kickstart.setup.destdir');
recursive_remove_directory($root . '/installation');
/** @var \AKAbstractPostproc $postproc */
$postproc = \AKFactory::getPostProc();
// Rename htaccess.bak to .htaccess
if (file_exists($root . '/htaccess.bak'))
{
if (file_exists($root . '/.htaccess'))
{
$postproc->unlink($root . '/.htaccess');
}
$postproc->rename($root . '/htaccess.bak', $root . '/.htaccess');
}
// Rename web.config.bak to web.config
if (file_exists($root . '/web.config.bak'))
{
if (file_exists($root . '/web.config'))
{
$postproc->unlink($root . '/web.config');
}
$postproc->rename($root . '/web.config.bak', $root . '/web.config');
}
// Remove restoration.php
$basepath = KSROOTDIR;
$basepath = rtrim(str_replace('\\', '/', $basepath), '/');
if (!empty($basepath))
{
$basepath .= '/';
}
$postproc->unlink($basepath . 'restoration.php');
// Import a custom finalisation file
if (file_exists(APATH_ROOT . '/restore_finalisation.php'))
{
include_once APATH_ROOT . '/restore_finalisation.php';
}
// Run a custom finalisation script
if (function_exists('finalizeRestore'))
{
finalizeRestore($root, $basepath);
}
return true;
}
}
// The observer class, used to report number of files and bytes processed
class RestorationObserver extends \AKAbstractPartObserver
{
public $compressedTotal = 0;
public $uncompressedTotal = 0;
public $filesProcessed = 0;
public function update($object, $message)
{
if (!is_object($message))
{
return;
}
if (!array_key_exists('type', get_object_vars($message)))
{
return;
}
if ($message->type == 'startfile')
{
$this->filesProcessed++;
$this->compressedTotal += $message->content->compressed;
$this->uncompressedTotal += $message->content->uncompressed;
}
}
public function __toString()
{
return __CLASS__;
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
/**
* Get the update information
*/
class UpdateGetInformation implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'updateGetInformation';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$filter = \Awf\Input\Filter::getInstance();
// Get the passed configuration values
$defConfig = array(
'force' => 0
);
$defConfig = array_merge($defConfig, $parameters);
$force = $filter->clean($defConfig['force'], 'bool');
$update = new \Solo\Model\Update;
$updateInformation = $update->getUpdateInformation($force);
return (object)$updateInformation;
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json\Task;
use Solo\Model\Json\TaskInterface;
use Solo\Model\Main;
/**
* Performs the necessary post-update actions
*/
class UpdateInstall implements TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName()
{
return 'updateInstall';
}
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array())
{
$model = new Main();
$model->postUpgradeActions();
return true;
}
}

View File

@@ -0,0 +1,32 @@
<?php
/**
* @package solo
* @copyright Copyright (c)2014-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 3 or later
*/
namespace Solo\Model\Json;
/**
* Interface for JSON API tasks
*/
interface TaskInterface
{
/**
* Return the JSON API task's name ("method" name). Remote clients will use it to call us.
*
* @return string
*/
public function getMethodName();
/**
* Execute the JSON API task
*
* @param array $parameters The parameters to this task
*
* @return mixed
*
* @throws \RuntimeException In case of an error
*/
public function execute(array $parameters = array());
}