From 0d739257a9d164a3a97260e095aa45b79c04a84e Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Tue, 3 Oct 2017 00:01:04 -0500 Subject: [PATCH] First pass at XML exporter for services --- .../Options/OptionShareController.php | 45 +++++++ .../Services/Exporter/XMLExporterService.php | 118 ++++++++++++++++++ composer.json | 1 + composer.lock | 116 ++++++++++++++++- .../admin/services/options/view.blade.php | 3 +- .../pterodactyl/admin/services/view.blade.php | 2 +- routes/admin.php | 1 + 7 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 app/Http/Controllers/Admin/Services/Options/OptionShareController.php create mode 100644 app/Services/Services/Exporter/XMLExporterService.php diff --git a/app/Http/Controllers/Admin/Services/Options/OptionShareController.php b/app/Http/Controllers/Admin/Services/Options/OptionShareController.php new file mode 100644 index 000000000..bdf8c8c41 --- /dev/null +++ b/app/Http/Controllers/Admin/Services/Options/OptionShareController.php @@ -0,0 +1,45 @@ +. + * + * This software is licensed under the terms of the MIT license. + * https://opensource.org/licenses/MIT + */ + +namespace Pterodactyl\Http\Controllers\Admin\Services\Options; + +use Pterodactyl\Models\ServiceOption; +use Pterodactyl\Http\Controllers\Controller; +use Pterodactyl\Services\Services\Exporter\XMLExporterService; + +class OptionShareController extends Controller +{ + /** + * @var \Pterodactyl\Services\Services\Exporter\XMLExporterService + */ + protected $exporterService; + + /** + * OptionShareController constructor. + * + * @param \Pterodactyl\Services\Services\Exporter\XMLExporterService $exporterService + */ + public function __construct(XMLExporterService $exporterService) + { + $this->exporterService = $exporterService; + } + + /** + * @param \Pterodactyl\Models\ServiceOption $option + * @return $this + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function export(ServiceOption $option) + { + return response($this->exporterService->handle($option), 200, [ + 'Content-Type' => 'application/xml', + ]); + } +} diff --git a/app/Services/Services/Exporter/XMLExporterService.php b/app/Services/Services/Exporter/XMLExporterService.php new file mode 100644 index 000000000..5570a45c5 --- /dev/null +++ b/app/Services/Services/Exporter/XMLExporterService.php @@ -0,0 +1,118 @@ +. + * + * This software is licensed under the terms of the MIT license. + * https://opensource.org/licenses/MIT + */ + +namespace Pterodactyl\Services\Services\Exporter; + +use Carbon\Carbon; +use Sabre\Xml\Writer; +use Sabre\Xml\Service; +use Pterodactyl\Models\ServiceOption; +use Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface; + +class XMLExporterService +{ + const XML_OPTION_NAMESPACE = '{https://pterodactyl.io/exporter/option/}'; + + /** + * @var \Carbon\Carbon + */ + protected $carbon; + + /** + * @var \Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface + */ + protected $repository; + + /** + * @var \Sabre\Xml\Service + */ + protected $xml; + + /** + * XMLExporterService constructor. + * + * @param \Carbon\Carbon $carbon + * @param \Sabre\Xml\Service $xml + * @param \Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface $repository + */ + public function __construct( + Carbon $carbon, + Service $xml, + ServiceOptionRepositoryInterface $repository + ) { + $this->carbon = $carbon; + $this->repository = $repository; + $this->xml = $xml; + + $this->xml->namespaceMap = [ + str_replace(['{', '}'], '', self::XML_OPTION_NAMESPACE) => 'p', + ]; + } + + /** + * Return an XML structure to represent this service option. + * + * @param int|\Pterodactyl\Models\ServiceOption $option + * @return string + * + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function handle($option): string + { + if (! $option instanceof ServiceOption) { + $option = $this->repository->find($option); + } + + $struct = [ + 'exported_at' => $this->carbon->now()->toIso8601String(), + 'name' => $option->name, + 'author' => array_get(explode(':', $option->tag), 0), + 'tag' => $option->tag, + 'description' => $option->description, + 'image' => $option->docker_image, + 'config' => [ + 'files' => $option->config_files, + 'startup' => $option->config_startup, + 'logs' => $option->config_logs, + 'stop' => $option->config_stop, + ], + 'scripts' => [ + 'installation' => [ + 'script' => function (Writer $writer) use ($option) { + return $writer->writeCData($option->copy_script_install); + }, + ], + ], + ]; + + return $this->xml->write(self::XML_OPTION_NAMESPACE . 'root', $this->recursiveArrayKeyPrepend($struct)); + } + + /** + * @param array $array + * @param string $prepend + * + * @return array + */ + protected function recursiveArrayKeyPrepend(array $array, $prepend = self::XML_OPTION_NAMESPACE): array + { + $parsed = []; + foreach ($array as $k => &$v) { + $k = $prepend . $k; + + if (is_array($v)) { + $v = $this->recursiveArrayKeyPrepend($v); + } + + $parsed[$k] = $v; + } + + return $parsed; + } +} diff --git a/composer.json b/composer.json index db04ade72..d0aa38e74 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "prologue/alerts": "^0.4", "ramsey/uuid": "^3.7", "s1lentium/iptools": "^1.1", + "sabre/xml": "^2.0", "sofa/eloquence": "~5.4.1", "spatie/laravel-fractal": "^4.0", "watson/validating": "^3.0", diff --git a/composer.lock b/composer.lock index 4c112e5f4..34bb845a4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "46a0a06ec8f3af50ed6ec05c2bb3b9a3", + "content-hash": "bc8c88f86ea043406bce2f8fddf704b3", "packages": [ { "name": "aws/aws-sdk-php", @@ -2510,6 +2510,120 @@ ], "time": "2016-08-21T15:57:09+00:00" }, + { + "name": "sabre/uri", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/fruux/sabre-uri.git", + "reference": "a42126042c7dcb53e2978dadb6d22574d1359b4c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruux/sabre-uri/zipball/a42126042c7dcb53e2978dadb6d22574d1359b4c", + "reference": "a42126042c7dcb53e2978dadb6d22574d1359b4c", + "shasum": "" + }, + "require": { + "php": ">=7" + }, + "require-dev": { + "phpunit/phpunit": "^6.0", + "sabre/cs": "~1.0.0" + }, + "type": "library", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Sabre\\Uri\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + } + ], + "description": "Functions for making sense out of URIs.", + "homepage": "http://sabre.io/uri/", + "keywords": [ + "rfc3986", + "uri", + "url" + ], + "time": "2017-02-20T20:02:35+00:00" + }, + { + "name": "sabre/xml", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/fruux/sabre-xml.git", + "reference": "054292959a1f2b64c10c9c7a03a816ba1872b8a3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruux/sabre-xml/zipball/054292959a1f2b64c10c9c7a03a816ba1872b8a3", + "reference": "054292959a1f2b64c10c9c7a03a816ba1872b8a3", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "lib-libxml": ">=2.6.20", + "php": ">=7.0", + "sabre/uri": ">=1.0,<3.0.0" + }, + "require-dev": { + "phpunit/phpunit": "*", + "sabre/cs": "~1.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Sabre\\Xml\\": "lib/" + }, + "files": [ + "lib/Deserializer/functions.php", + "lib/Serializer/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + }, + { + "name": "Markus Staab", + "email": "markus.staab@redaxo.de", + "role": "Developer" + } + ], + "description": "sabre/xml is an XML library that you may not hate.", + "homepage": "https://sabre.io/xml/", + "keywords": [ + "XMLReader", + "XMLWriter", + "dom", + "xml" + ], + "time": "2016-11-16T00:41:01+00:00" + }, { "name": "sofa/eloquence", "version": "5.4.1", diff --git a/resources/themes/pterodactyl/admin/services/options/view.blade.php b/resources/themes/pterodactyl/admin/services/options/view.blade.php index 1f6564d81..641a28be7 100644 --- a/resources/themes/pterodactyl/admin/services/options/view.blade.php +++ b/resources/themes/pterodactyl/admin/services/options/view.blade.php @@ -131,7 +131,8 @@ - + + Export Option Configuration diff --git a/resources/themes/pterodactyl/admin/services/view.blade.php b/resources/themes/pterodactyl/admin/services/view.blade.php index 2a6886f01..f93e75a73 100644 --- a/resources/themes/pterodactyl/admin/services/view.blade.php +++ b/resources/themes/pterodactyl/admin/services/view.blade.php @@ -101,7 +101,7 @@ @foreach($service->options as $option) {{ $option->name }} - {!! $option->description !!} + {!! $option->description !!} {{ $option->tag }} {{ $option->servers->count() }} diff --git a/routes/admin.php b/routes/admin.php index d5f16e813..6e1e93703 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -155,6 +155,7 @@ Route::group(['prefix' => 'services'], function () { Route::get('/view/{service}/functions', 'ServiceController@viewFunctions')->name('admin.services.view.functions'); Route::get('/option/new', 'OptionController@create')->name('admin.services.option.new'); Route::get('/option/{option}', 'OptionController@viewConfiguration')->name('admin.services.option.view'); + Route::get('/option/{option}/export', 'Services\Options\OptionShareController@export')->name('admin.services.option.export'); Route::get('/option/{option}/variables', 'VariableController@view')->name('admin.services.option.variables'); Route::get('/option/{option}/scripts', 'OptionController@viewScripts')->name('admin.services.option.scripts');