This commit is contained in:
Alexey Berezhok
2024-03-19 22:05:27 +03:00
commit 346a50856b
1572 changed files with 182163 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
namespace Hestia\Models;
class DnsDomain extends Model {
}

View File

@@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
namespace Hestia\Models;
class MailDomain extends Model {
}

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Hestia\Models;
class Model {
public function __construct() {
}
public static function all() {
}
}
/**
* Minimal list of models required
*
* User
*
* WebDomain
*
* MailDomain
* `-MailAccount
*
* DNSDomain
* `-DNSRecord
*
* Database
*
*/

View File

@@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
namespace Hestia\Models;
class User extends Model {
}

View File

@@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
namespace Hestia\Models;
class WebDomain extends Model {
}

View File

@@ -0,0 +1,353 @@
<?php
declare(strict_types=1);
namespace Hestia\System;
use function Hestiacp\quoteshellarg\quoteshellarg;
class HestiaApp {
/** @var string[] */
public $errors;
protected const TMPDIR_DOWNLOADS = "/tmp/hestia-webapp";
protected $phpsupport = false;
public function __construct() {
@mkdir(self::TMPDIR_DOWNLOADS);
}
public function run(string $cmd, $args, &$cmd_result = null): bool {
$cli_script = realpath(HESTIA_DIR_BIN . $cmd);
if (!str_starts_with((string) $cli_script, HESTIA_DIR_BIN)) {
$errstr = "$cmd is trying to traverse outside of " . HESTIA_DIR_BIN;
trigger_error($errstr);
throw new \Exception($errstr);
}
$cli_script = "/usr/bin/sudo " . quoteshellarg($cli_script);
$cli_arguments = "";
if (!empty($args) && is_array($args)) {
foreach ($args as $arg) {
$cli_arguments .= quoteshellarg((string) $arg) . " ";
}
} else {
$cli_arguments = quoteshellarg($args);
}
exec($cli_script . " " . $cli_arguments . " 2>&1", $output, $exit_code);
$result["code"] = $exit_code;
$result["args"] = $cli_arguments;
$result["raw"] = $output;
$result["text"] = implode(PHP_EOL, $output);
$result["json"] = json_decode($result["text"], true);
$cmd_result = (object) $result;
if ($exit_code > 0) {
//log error message in nginx-error.log
trigger_error($result["text"]);
//throw exception if command fails
throw new \Exception($result["text"]);
}
return $exit_code === 0;
}
public function runUser(string $cmd, $args, &$cmd_result = null): bool {
if (!empty($args) && is_array($args)) {
array_unshift($args, $this->user());
} else {
$args = [$this->user(), $args];
}
return $this->run($cmd, $args, $cmd_result);
}
public function installComposer($version) {
exec("curl https://composer.github.io/installer.sig", $output);
$signature = implode(PHP_EOL, $output);
if (empty($signature)) {
throw new \Exception("Error reading composer signature");
}
$composer_setup =
self::TMPDIR_DOWNLOADS . DIRECTORY_SEPARATOR . "composer-setup-" . $signature . ".php";
exec(
"wget https://getcomposer.org/installer --quiet -O " . quoteshellarg($composer_setup),
$output,
$return_code,
);
if ($return_code !== 0) {
throw new \Exception("Error downloading composer");
}
if ($signature !== hash_file("sha384", $composer_setup)) {
unlink($composer_setup);
throw new \Exception("Invalid composer signature");
}
$install_folder = $this->getUserHomeDir() . DIRECTORY_SEPARATOR . ".composer";
if (!file_exists($install_folder)) {
exec(HESTIA_CMD . "v-rebuild-user " . $this->user(), $output, $return_code);
if ($return_code !== 0) {
throw new \Exception("Unable to rebuild user");
}
}
$this->runUser(
"v-run-cli-cmd",
[
"/usr/bin/php",
$composer_setup,
"--quiet",
"--install-dir=" . $install_folder,
"--filename=composer",
"--$version",
],
$status,
);
unlink($composer_setup);
if ($status->code !== 0) {
throw new \Exception("Error installing composer");
}
}
public function updateComposer($version) {
$this->runUser("v-run-cli-cmd", ["composer", "selfupdate", "--$version"]);
}
public function runComposer($args, &$cmd_result = null, $data = []): bool {
$composer =
$this->getUserHomeDir() .
DIRECTORY_SEPARATOR .
".composer" .
DIRECTORY_SEPARATOR .
"composer";
if (!is_file($composer)) {
$this->installComposer($data["version"]);
} else {
$this->updateComposer($data["version"]);
}
if (empty($data["php_version"])) {
$data["php_version"] = "";
}
if (!empty($args) && is_array($args)) {
array_unshift($args, "php" . $data["php_version"], $composer);
} else {
$args = ["php" . $data["php_version"], $composer, $args];
}
return $this->runUser("v-run-cli-cmd", $args, $cmd_result);
}
public function runWp($args, &$cmd_result = null): bool {
$wp =
$this->getUserHomeDir() . DIRECTORY_SEPARATOR . ".wp-cli" . DIRECTORY_SEPARATOR . "wp";
if (!is_file($wp)) {
$this->runUser("v-add-user-wp-cli", []);
} else {
$this->runUser("v-run-cli-cmd", [$wp, "cli", "update"]);
}
array_unshift($args, $wp);
return $this->runUser("v-run-cli-cmd", $args, $cmd_result);
}
// Logged in user
public function realuser(): string {
return $_SESSION["user"];
}
// Effective user
public function user(): string {
$user = $this->realuser();
if ($_SESSION["userContext"] === "admin" && !empty($_SESSION["look"])) {
$user = $_SESSION["look"];
}
if (strpos($user, DIRECTORY_SEPARATOR) !== false) {
throw new \Exception("illegal characters in username");
}
return $user;
}
public function getUserHomeDir() {
$info = posix_getpwnam($this->user());
return $info["dir"];
}
public function userOwnsDomain(string $domain): bool {
return $this->runUser("v-list-web-domain", [$domain, "json"]);
}
public function checkDatabaseLimit() {
$status = $this->runUser("v-list-user", ["json"], $result);
$result->json[$this->user()];
if ($result->json[$this->user()]["DATABASES"] != "unlimited") {
if (
$result->json[$this->user()]["DATABASES"] -
$result->json[$this->user()]["U_DATABASES"] <
1
) {
return false;
}
}
return true;
}
public function databaseAdd(
string $dbname,
string $dbuser,
string $dbpass,
string $dbtype = "mysql",
string $charset = "utf8mb4",
) {
$v_password = tempnam("/tmp", "hst");
$fp = fopen($v_password, "w");
fwrite($fp, $dbpass . "\n");
fclose($fp);
$status = $this->runUser("v-add-database", [
$dbname,
$dbuser,
$v_password,
$dbtype,
"localhost",
$charset,
]);
if (!$status) {
$this->errors[] = _("Unable to add database!");
}
unlink($v_password);
return $status;
}
public function getCurrentBackendTemplate(string $domain) {
$status = $this->runUser("v-list-web-domain", [$domain, "json"], $return_message);
$version = $return_message->json[$domain]["BACKEND"];
if (!empty($version)) {
if ($version != "default") {
$test = preg_match("/^.*PHP-([0-9])\_([0-9])/", $version, $match);
return $match[1] . "." . $match[2];
} else {
$supported = $this->run("v-list-sys-php", "json", $result);
return $result->json[0];
}
} else {
$supported = $this->run("v-list-sys-php", "json", $result);
return $result->json[0];
}
}
public function changeWebTemplate(string $domain, string $template) {
$status = $this->runUser("v-change-web-domain-tpl", [$domain, $template]);
}
public function changeBackendTemplate(string $domain, string $template) {
$status = $this->runUser("v-change-web-domain-backend-tpl", [$domain, $template]);
}
public function listSuportedPHP() {
if (!$this->phpsupport) {
$status = $this->run("v-list-sys-php", "json", $result);
$this->phpsupport = $result->json;
}
return $this->phpsupport;
}
/*
Return highest available supported php version
Eg: Package requires: 7.3 or 7.4 and system has 8.0 and 7.4 it will return 7.4
Package requires: 8.0 or 8.1 and system has 8.0 and 7.4 it will return 8.0
Package requires: 7.4 or 8.0 and system has 8.0 and 7.4 it will return 8.0
If package isn't supported by the available php version false will returned
*/
public function getSupportedPHP($support) {
$versions = $this->listSuportedPHP();
$supported = false;
$supported_versions = [];
foreach ($versions as $version) {
if (in_array($version, $support)) {
$supported = true;
$supported_versions[] = $version;
}
}
if ($supported) {
return $supported_versions;
} else {
return false;
}
}
public function getWebDomainIp(string $domain) {
$this->runUser("v-list-web-domain", [$domain, "json"], $result);
$ip = $result->json[$domain]["IP"];
return filter_var($ip, FILTER_VALIDATE_IP);
}
public function getWebDomainPath(string $domain) {
return Util::join_paths($this->getUserHomeDir(), "web", $domain);
}
public function downloadUrl(string $src, $path = null, &$result = null) {
if (strpos($src, "http://") !== 0 && strpos($src, "https://") !== 0) {
return false;
}
exec(
"/usr/bin/wget --tries 3 --timeout=30 --no-dns-cache -nv " .
quoteshellarg($src) .
" -P " .
quoteshellarg(self::TMPDIR_DOWNLOADS) .
" 2>&1",
$output,
$return_var,
);
if ($return_var !== 0) {
return false;
}
if (
!preg_match(
'/URL:\s*(.+?)\s*\[(.+?)\]\s*->\s*"(.+?)"/',
implode(PHP_EOL, $output),
$matches,
)
) {
return false;
}
if (empty($matches) || count($matches) != 4) {
return false;
}
$status["url"] = $matches[1];
$status["file"] = $matches[3];
$result = (object) $status;
return true;
}
public function archiveExtract(string $src, string $path, $skip_components = null) {
if (empty($path)) {
throw new \Exception("Error extracting archive: missing target folder");
}
if (realpath($src)) {
$archive_file = $src;
} else {
if (!$this->downloadUrl($src, null, $download_result)) {
throw new \Exception("Error downloading archive");
}
$archive_file = $download_result->file;
}
$result = $this->runUser("v-extract-fs-archive", [
$archive_file,
$path,
null,
$skip_components,
]);
unlink($archive_file);
return $result;
}
}

View File

@@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
namespace Hestia\System;
class HestiaCLI {
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Hestia\System;
class Util {
/*
* Method from: https://stackoverflow.com/a/15575293
* https://stackoverflow.com/questions/1091107/how-to-join-filesystem-path-strings-in-php
*/
public static function join_paths() {
$paths = [];
foreach (func_get_args() as $arg) {
if ($arg !== "") {
$paths[] = $arg;
}
}
return preg_replace("#/+#", "/", join("/", $paths));
}
public static function generate_string(int $length = 16, $full = true) {
$chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
if ($full) {
$chars .= '~`!@|#[]$%^&*() _-=+{}:;<>?,./';
}
$random_string = "";
for ($i = 0; $i < $length; $i++) {
$random_string .= $chars[random_int(0, strlen($chars) - 1)];
}
return $random_string;
}
}

View File

@@ -0,0 +1,133 @@
<?php
declare(strict_types=1);
namespace Hestia\WebApp;
use Hestia\System\HestiaApp;
class AppWizard {
private $domain;
private $appsetup;
private $appcontext;
private $formNamespace = "webapp";
private $errors;
private $database_config = [
"database_create" => ["type" => "boolean", "value" => true],
"database_name" => ["type" => "text", "placeholder" => "auto"],
"database_user" => ["type" => "text", "placeholder" => "auto"],
"database_password" => ["type" => "password", "placeholder" => "auto"],
];
public function __construct(InstallerInterface $app, string $domain, HestiaApp $context) {
$this->domain = $domain;
$this->appcontext = $context;
if (!$this->appcontext->userOwnsDomain($domain)) {
throw new \Exception("User does not have access to domain [$domain]");
}
$this->appsetup = $app;
}
public function getStatus() {
return $this->errors;
}
public function isDomainRootClean() {
$this->appcontext->runUser("v-run-cli-cmd", ["ls", $this->appsetup->getDocRoot()], $status);
if ($status->code !== 0) {
throw new \Exception("Cannot list domain files");
}
$files = $status->raw;
if (count($files) > 2) {
return false;
}
foreach ($files as $file) {
if (!in_array($file, ["index.html", "robots.txt"])) {
return false;
}
}
return true;
}
public function formNs() {
return $this->formNamespace;
}
public function getOptions() {
$options = $this->appsetup->getOptions();
$config = $this->appsetup->getConfig();
$options = array_merge($options, [
"php_version" => [
"type" => "select",
"value" => $this->appcontext->getCurrentBackendTemplate($this->domain),
"options" => $this->appcontext->getSupportedPHP(
$config["server"]["php"]["supported"],
),
],
]);
if ($this->appsetup->withDatabase()) {
$options = array_merge($options, $this->database_config);
}
return $options;
}
public function info() {
return $this->appsetup->info();
}
public function filterOptions(array $options) {
$filteredoptions = [];
array_walk($options, function ($value, $key) use (&$filteredoptions) {
if (strpos($key, $this->formNs() . "_") === 0) {
$option = str_replace($this->formNs() . "_", "", $key);
$filteredoptions[$option] = $value;
}
});
return $filteredoptions;
}
public function execute(array $options) {
$options = $this->filterOptions($options);
$random_num = (string) random_int(10000, 99999);
if ($this->appsetup->withDatabase() && !empty($options["database_create"])) {
if (empty($options["database_name"])) {
$options["database_name"] = $random_num;
}
if (empty($options["database_user"])) {
$options["database_user"] = $random_num;
}
if (empty($options["database_password"])) {
$options["database_password"] = bin2hex(random_bytes(10));
}
if (!$this->appcontext->checkDatabaseLimit()) {
$this->errors[] = _("Unable to add database! Limit reached!");
return false;
}
if (
!$this->appcontext->databaseAdd(
$options["database_name"],
$options["database_user"],
$options["database_password"],
)
) {
$this->errors[] = "Error adding database";
return false;
}
}
if (empty($this->errors)) {
return $this->appsetup->install($options);
}
}
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Hestia\WebApp;
interface InstallerInterface {
public function install(array $options = null);
public function getDocRoot(string $append_relative_path = null): string;
public function withDatabase(): bool;
}

View File

@@ -0,0 +1,201 @@
<?php
namespace Hestia\WebApp\Installers;
use Hestia\System\Util;
use Hestia\System\HestiaApp;
use Hestia\WebApp\InstallerInterface;
use Hestia\Models\WebDomain;
use Hestia\WebApp\Installers\Resources\ComposerResource;
use Hestia\WebApp\Installers\Resources\WpResource;
abstract class BaseSetup implements InstallerInterface {
protected $appInfo;
protected $config;
protected $domain;
protected $extractsubdir;
protected $AppDirInstall;
protected $appcontext;
public function setAppDirInstall(string $appDir) {
if (!empty($appDir)) {
if (strpos(".", $appDir) !== false) {
throw new \Exception("Invalid install folder");
}
if (!is_dir($this->getDocRoot($appDir))) {
$this->appcontext->runUser(
"v-add-fs-directory",
[$this->getDocRoot($appDir)],
$result,
);
}
$this->AppDirInstall = $appDir;
}
}
public function getAppDirInstall() {
return $this->AppDirInstall;
}
public function info() {
$this->appInfo["enabled"] = true;
if (isset($this->config["server"]["php"]["supported"])) {
$this->appInfo["php_support"] = $this->config["server"]["php"]["supported"];
} else {
$this->appInfo["php_support"] = [
"5.6",
"7.0",
"7.1",
"7.2",
"7.3",
"7.4",
"8.0",
"8.1",
"8.2",
];
}
return $this->appInfo;
}
public function __construct($domain, HestiaApp $appcontext) {
if (filter_var($domain, FILTER_VALIDATE_DOMAIN) === false) {
throw new \Exception("Invalid domain name");
}
$this->domain = $domain;
$this->appcontext = $appcontext;
}
public function getConfig($section = null) {
return !empty($section) ? $this->config[$section] : $this->config;
}
public function getOptions() {
return $this->getConfig("form");
}
public function withDatabase(): bool {
return $this->getConfig("database") === true;
}
public function getDocRoot($append_relative_path = null): string {
$domain_path = $this->appcontext->getWebDomainPath($this->domain);
if (empty($domain_path) || !is_dir($domain_path)) {
throw new \Exception("Error finding domain folder ($domain_path)");
}
if (!$this->AppDirInstall) {
return Util::join_paths($domain_path, "public_html", $append_relative_path);
} else {
return Util::join_paths(
$domain_path,
"public_html",
$this->AppDirInstall,
$append_relative_path,
);
}
}
public function retrieveResources($options) {
foreach ($this->getConfig("resources") as $res_type => $res_data) {
if (!empty($res_data["dst"]) && is_string($res_data["dst"])) {
$resource_destination = $this->getDocRoot($res_data["dst"]);
} else {
$resource_destination = $this->getDocRoot($this->extractsubdir);
}
if ($res_type === "composer") {
$res_data["php_version"] = $options["php_version"];
new ComposerResource(
$this->appcontext,
$res_data,
$resource_destination,
$options["php_version"],
);
} elseif ($res_type === "wp") {
new WpResource(
$this->appcontext,
$res_data,
$resource_destination,
$options,
$this->info(),
);
} else {
$this->appcontext->archiveExtract($res_data["src"], $resource_destination, 1);
}
}
return true;
}
public function setup(array $options = null) {
if ($_SESSION["WEB_SYSTEM"] == "nginx") {
if (isset($this->config["server"]["nginx"]["template"])) {
$this->appcontext->changeWebTemplate(
$this->domain,
$this->config["server"]["nginx"]["template"],
);
} else {
$this->appcontext->changeWebTemplate($this->domain, "default");
}
} else {
if (isset($this->config["server"]["apache2"]["template"])) {
$this->appcontext->changeWebTemplate(
$this->domain,
$this->config["server"]["apache2"]["template"],
);
} else {
$this->appcontext->changeWebTemplate($this->domain, "default");
}
}
if ($_SESSION["WEB_BACKEND"] == "php-fpm") {
if (isset($this->config["server"]["php"]["supported"])) {
$php_version = $this->appcontext->getSupportedPHP(
$this->config["server"]["php"]["supported"],
);
if (!$php_version) {
throw new \Exception("Required PHP version is not supported");
}
//convert from x.x to PHP-x_x to accepted..
$this->appcontext->changeBackendTemplate(
$this->domain,
"PHP-" . str_replace(".", "_", $options["php_version"]),
);
}
}
}
public function install(array $options = null) {
$this->appcontext->runUser("v-delete-fs-file", [$this->getDocRoot("robots.txt")]);
$this->appcontext->runUser("v-delete-fs-file", [$this->getDocRoot("index.html")]);
return $this->retrieveResources($options);
}
public function cleanup() {
// Remove temporary folder
if (!empty($this->extractsubdir)) {
$this->appcontext->runUser(
"v-delete-fs-directory",
[$this->getDocRoot($this->extractsubdir)],
$result,
);
}
}
public function saveTempFile(string $data) {
$tmp_file = tempnam("/tmp", "hst.");
if (empty($tmp_file)) {
throw new \Exception("Error creating temp file");
}
if (file_put_contents($tmp_file, $data) > 0) {
chmod($tmp_file, 0644);
$user_tmp_file = Util::join_paths($this->appcontext->getUserHomeDir(), $tmp_file);
$this->appcontext->runUser("v-copy-fs-file", [$tmp_file, $user_tmp_file], $result);
unlink($tmp_file);
return $user_tmp_file;
}
if (file_exists($tmp_file)) {
unlink($tmp_file);
}
return false;
}
}

View File

@@ -0,0 +1,140 @@
<?php
namespace Hestia\WebApp\Installers\DokuWiki;
use Hestia\System\Util;
use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
class DokuWikiSetup extends BaseSetup {
protected $appInfo = [
"name" => "DokuWiki",
"group" => "wiki",
"enabled" => true,
"version" => "2023-04-04a",
"thumbnail" => "dokuwiki-logo.svg",
];
protected $appname = "dokuwiki";
protected $extractsubdir = "/tmp-dokuwiki";
protected $config = [
"form" => [
"wiki_name" => "text",
"superuser" => "text",
"real_name" => "text",
"email" => "text",
"password" => "password",
"initial_ACL_policy" => [
"type" => "select",
"options" => [
"0: Open Wiki (read, write, upload for everyone)", // 0
"1: Public Wiki (read for everyone, write and upload for registered users)", // 1
"2: Closed Wiki (read, write, upload for registered users only)", // 3
],
],
"content_license" => [
"type" => "select",
"options" => [
"cc-zero: CC0 1.0 Universal",
"publicdomain: Public Domain",
"cc-by: CC Attribution 4.0 International",
"cc-by-sa: CC Attribution-Share Alike 4.0 International",
"gnufdl: GNU Free Documentation License 1.3",
"cc-by-nc: CC Attribution-Noncommercial 4.0 International",
"cc-by-nc-sa: CC Attribution-Noncommercial-Share Alike 4.0 International",
"0: Do not show any license information",
],
],
],
"resources" => [
"archive" => [
"src" =>
"https://github.com/dokuwiki/dokuwiki/releases/download/release-2023-04-04a/dokuwiki-2023-04-04a.zip",
],
],
"server" => [
"nginx" => [
"template" => "default",
],
"php" => [
"supported" => ["7.3", "7.4", "8.0", "8.1"],
],
],
];
public function install(array $options = null, &$status = null) {
parent::install($options);
parent::setup($options);
//check if ssl is enabled
$this->appcontext->run(
"v-list-web-domain",
[$this->appcontext->user(), $this->domain, "json"],
$status,
);
$sslEnabled = $status->json[$this->domain]["SSL"] == "no" ? 0 : 1;
$webDomain = ($sslEnabled ? "https://" : "http://") . $this->domain . "/";
$this->appcontext->runUser(
"v-copy-fs-directory",
[
$this->getDocRoot($this->extractsubdir . "/dokuwiki-2023-04-04/."),
$this->getDocRoot(),
],
$status,
);
// enable htaccess
$this->appcontext->runUser(
"v-move-fs-file",
[$this->getDocRoot(".htaccess.dist"), $this->getDocRoot(".htaccess")],
$status,
);
$installUrl = $webDomain . "install.php";
$cmd =
"curl --request POST " .
($sslEnabled ? "" : "--insecure ") .
"--url $installUrl " .
"--header 'Content-Type: application/x-www-form-urlencoded' " .
"--data l=en " .
"--data 'd[title]=" .
rawurlencode($options["wiki_name"]) .
"' " .
"--data 'd[acl]=on' " .
"--data 'd[superuser]=" .
rawurlencode($options["superuser"]) .
"' " .
"--data 'd[fullname]=" .
rawurlencode($options["real_name"]) .
"' " .
"--data 'd[email]=" .
rawurlencode($options["email"]) .
"' " .
"--data 'd[password]=" .
rawurlencode($options["password"]) .
"' " .
"--data 'd[confirm]=" .
rawurlencode($options["password"]) .
"' " .
"--data 'd[policy]=" .
substr(rawurlencode($options["initial_ACL_policy"]), 0, 1) .
"' " .
"--data 'd[license]=" .
explode(":", rawurlencode($options["content_license"])[0]) .
"' " .
"--data submit=";
exec($cmd, $output, $return_var);
if ($return_var > 0) {
throw new \Exception(implode(PHP_EOL, $output));
}
// remove temp folder
$this->appcontext->runUser("v-delete-fs-file", [$this->getDocRoot("install.php")], $status);
$this->cleanup();
return $status->code === 0;
}
}

View File

@@ -0,0 +1,586 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="128.17094"
height="128.03864"
id="svg2"
sodipodi:version="0.32"
inkscape:version="0.48.1 "
sodipodi:docname="dokuwiki-logo.svg"
version="1.1">
<title
id="title3181">DokuWiki Logo</title>
<defs
id="defs4">
<linearGradient
id="linearGradient2624">
<stop
style="stop-color:#3a9030;stop-opacity:0.83673471;"
offset="0"
id="stop2626" />
<stop
style="stop-color:#3d9c32;stop-opacity:0.79591835;"
offset="1"
id="stop2628" />
</linearGradient>
<linearGradient
id="linearGradient2612">
<stop
style="stop-color:#25901b;stop-opacity:0.83673471;"
offset="0"
id="stop2614" />
<stop
style="stop-color:#25901b;stop-opacity:0.37755102;"
offset="1"
id="stop2616" />
</linearGradient>
<linearGradient
id="linearGradient2600">
<stop
style="stop-color:#e32525;stop-opacity:0.81632656;"
offset="0"
id="stop2602" />
<stop
style="stop-color:#e32525;stop-opacity:0.5714286;"
offset="1"
id="stop2604" />
</linearGradient>
<marker
inkscape:stockid="TriangleOutL"
orient="auto"
refY="0"
refX="0"
id="TriangleOutL"
style="overflow:visible">
<path
id="path2488"
d="m 5.77,0 -8.65,5 0,-10 8.65,5 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none"
transform="scale(0.8,0.8)"
inkscape:connector-curvature="0" />
</marker>
<marker
inkscape:stockid="Arrow2Lstart"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lstart"
style="overflow:visible">
<path
id="path2571"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(1.1,0,0,1.1,-5.5,0)"
inkscape:connector-curvature="0" />
</marker>
<linearGradient
id="linearGradient2408">
<stop
id="stop2410"
offset="0"
style="stop-color:#000000;stop-opacity:0.17346939;" />
<stop
id="stop2412"
offset="1"
style="stop-color:#c7cec2;stop-opacity:0;" />
</linearGradient>
<linearGradient
id="linearGradient2389">
<stop
style="stop-color:#000000;stop-opacity:0.17346939;"
offset="0"
id="stop2391" />
<stop
style="stop-color:#c7cec2;stop-opacity:0;"
offset="1"
id="stop2393" />
</linearGradient>
<linearGradient
id="linearGradient2370">
<stop
style="stop-color:#fbfaf9;stop-opacity:1;"
offset="0"
id="stop2372" />
<stop
style="stop-color:#e9dac7;stop-opacity:1;"
offset="1"
id="stop2374" />
</linearGradient>
<linearGradient
id="linearGradient2364">
<stop
id="stop2366"
offset="0"
style="stop-color:#fbf6f0;stop-opacity:1;" />
<stop
id="stop2368"
offset="1"
style="stop-color:#e9dac7;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient2348">
<stop
style="stop-color:#fbf6f0;stop-opacity:1;"
offset="0"
id="stop2350" />
<stop
style="stop-color:#e9dac7;stop-opacity:1;"
offset="1"
id="stop2352" />
</linearGradient>
<linearGradient
id="linearGradient2332">
<stop
style="stop-color:#ede1ae;stop-opacity:1;"
offset="0"
id="stop2334" />
<stop
style="stop-color:#fefdfa;stop-opacity:1;"
offset="1"
id="stop2336" />
</linearGradient>
<linearGradient
id="linearGradient2249">
<stop
style="stop-color:#00a423;stop-opacity:1;"
offset="0"
id="stop2251" />
<stop
style="stop-color:#00b427;stop-opacity:1;"
offset="1"
id="stop2253" />
</linearGradient>
<linearGradient
id="linearGradient2229">
<stop
id="stop2231"
offset="0"
style="stop-color:#00b62b;stop-opacity:1;" />
<stop
id="stop2233"
offset="1"
style="stop-color:#a1d784;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient2213">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop2215" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop2217" />
</linearGradient>
<linearGradient
id="linearGradient2360">
<stop
style="stop-color:#d69c00;stop-opacity:1;"
offset="0"
id="stop2362" />
<stop
style="stop-color:#ffe658;stop-opacity:1;"
offset="1"
id="stop2364" />
</linearGradient>
<linearGradient
id="linearGradient2352">
<stop
id="stop2354"
offset="0"
style="stop-color:#ce411e;stop-opacity:1;" />
<stop
id="stop2356"
offset="1"
style="stop-color:#ecad8d;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient2336">
<stop
style="stop-color:#8f2a15;stop-opacity:1;"
offset="0"
id="stop2338" />
<stop
style="stop-color:#c8381b;stop-opacity:1;"
offset="1"
id="stop2340" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2336"
id="linearGradient2342"
x1="219.21262"
y1="189.01556"
x2="286.22665"
y2="189.01556"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2352"
id="linearGradient2350"
x1="219.66267"
y1="192.73286"
x2="277.8761"
y2="192.73286"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2360"
id="radialGradient2366"
cx="224.41418"
cy="212.80016"
fx="224.41418"
fy="212.80016"
r="8.6813803"
gradientTransform="matrix(1,0,0,0.984179,0,3.366635)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2249"
id="linearGradient2227"
x1="192.03938"
y1="262.25757"
x2="263.67093"
y2="262.25757"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2229"
id="linearGradient2247"
x1="191.75092"
y1="258.91571"
x2="255.6561"
y2="258.91571"
gradientUnits="userSpaceOnUse" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2360"
id="radialGradient2317"
cx="257.41144"
cy="274.64203"
fx="257.41144"
fy="274.64203"
r="7.1440549"
gradientTransform="matrix(1,0,0,1.631384,0,-173.4045)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2360"
id="linearGradient2325"
x1="184.07063"
y1="246.35907"
x2="201.40646"
y2="246.35907"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2332"
id="linearGradient2346"
x1="162.76369"
y1="184.99277"
x2="240.84924"
y2="289.50323"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2348"
id="linearGradient2354"
x1="140.15784"
y1="303.78967"
x2="136.14151"
y2="195.87151"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2370"
id="linearGradient2362"
x1="286.15598"
y1="262.28729"
x2="185.81258"
y2="172.32423"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2389"
id="linearGradient2395"
x1="213.96568"
y1="220.07191"
x2="244.79126"
y2="265.40363"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2408"
id="linearGradient2406"
x1="184.30582"
y1="241.52789"
x2="224.67441"
y2="307.52844"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2600"
id="linearGradient2606"
x1="202.41772"
y1="222.05145"
x2="206.06017"
y2="210.3558"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2612"
id="linearGradient2618"
x1="248.62152"
y1="234.52202"
x2="251.64362"
y2="213.12164"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2624"
id="linearGradient2630"
x1="275.71765"
y1="251.56442"
x2="255.68353"
y2="217.94008"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2352"
id="linearGradient2640"
gradientUnits="userSpaceOnUse"
x1="219.66267"
y1="192.73286"
x2="277.8761"
y2="192.73286" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient2336"
id="linearGradient2643"
gradientUnits="userSpaceOnUse"
x1="219.21262"
y1="189.01556"
x2="286.22665"
y2="189.01556" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2360"
id="radialGradient2647"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.984179,0,3.366635)"
cx="224.41418"
cy="212.80016"
fx="224.41418"
fy="212.80016"
r="8.6813803" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.03"
inkscape:cx="35.144424"
inkscape:cy="83.160427"
inkscape:document-units="px"
inkscape:current-layer="layer3"
inkscape:window-width="1366"
inkscape:window-height="716"
inkscape:window-x="-8"
inkscape:window-y="-8"
showguides="true"
inkscape:guide-bbox="true"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-maximized="1"
inkscape:showpageshadow="false"
showborder="true"
borderlayer="false" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>DokuWiki Logo</dc:title>
<dc:creator>
<cc:Agent>
<dc:title>Esther Brunner</dc:title>
</cc:Agent>
</dc:creator>
<cc:license
rdf:resource="http://www.gnu.org/licenses/gpl-2.0.html" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="paper"
style="display:inline"
transform="translate(-158.10602,-158.67323)">
<g
id="g1419"
transform="matrix(0.99993322,0,0,0.9959778,0.01483419,0.8957919)">
<g
id="g2376">
<path
transform="matrix(0.989976,-0.141236,0.201069,0.979577,0,0)"
style="fill:url(#linearGradient2354);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.7216621px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline"
d="m 120.21543,196.43769 70.90655,-0.79226 -2.40261,109.05308 -71.71761,0.37344 3.21367,-108.63426 z"
id="rect1422"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient2362);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline"
d="m 179.20033,182.08731 79.84173,-19.51687 26.61391,101.72428 -82.50312,21.58684 -23.95252,-103.79425 z"
id="rect1425"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" />
<path
transform="matrix(0.995676,-0.09289891,0.08102261,0.996712,0,0)"
style="fill:url(#linearGradient2346);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00418305px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;display:inline"
d="m 159.01353,181.74387 85.58587,0.53396 0,110.47429 -84.53387,-2.5127 -1.052,-108.49555 z"
id="rect1419"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" />
</g>
<path
id="text2382"
d="m 167.55116,214.00773 0,-20.1846 5.34962,0 0,2.37403 -2.48145,0 0,15.43654 2.48145,0 0,2.37403 -5.34962,0 m 7.34767,0 0,-20.1846 5.34961,0 0,2.37403 -2.48144,0 0,15.43654 2.48144,0 0,2.37403 -5.34961,0 m 7.36915,-20.1846 5.81153,0 c 1.31054,2e-5 2.30956,0.10028 2.99707,0.30078 0.92382,0.27216 1.71516,0.75555 2.37403,1.4502 0.65884,0.69468 1.16014,1.54689 1.50391,2.55664 0.34373,1.00262 0.51561,2.24155 0.51562,3.71681 -10e-6,1.29623 -0.16115,2.41342 -0.4834,3.35156 -0.39389,1.14584 -0.95607,2.07325 -1.68652,2.78223 -0.55145,0.53711 -1.29624,0.95606 -2.23438,1.25684 -0.70183,0.222 -1.63999,0.33301 -2.81446,0.33301 l -5.9834,0 0,-15.74807 m 3.17969,2.66407 0,10.43067 2.37402,0 c 0.88802,1e-5 1.52897,-0.0501 1.92286,-0.15039 0.51561,-0.1289 0.94172,-0.34732 1.27832,-0.65527 0.34374,-0.30794 0.62304,-0.81282 0.83789,-1.51465 0.21483,-0.70898 0.32226,-1.6722 0.32227,-2.88965 -1e-5,-1.21744 -0.10744,-2.15201 -0.32227,-2.80372 -0.21485,-0.65168 -0.51563,-1.16014 -0.90234,-1.52539 -0.38673,-0.36522 -0.87729,-0.61229 -1.47168,-0.74121 -0.44402,-0.10025 -1.31414,-0.15038 -2.61036,-0.15039 l -1.42871,0 m 14.96388,13.084 -3.75977,-15.74807 3.25489,0 2.37403,10.8174 2.87891,-10.8174 3.78125,0 2.76074,11.00002 2.417,-11.00002 3.20118,0 -3.82423,15.74807 -3.37305,0 -3.13672,-11.77345 -3.12598,11.77345 -3.44825,0 m 22.76272,-15.74807 0,20.1846 -5.34961,0 0,-2.37403 2.48145,0 0,-15.45803 -2.48145,0 0,-2.35254 5.34961,0 m 7.34767,0 0,20.1846 -5.34962,0 0,-2.37403 2.48145,0 0,-15.45803 -2.48145,0 0,-2.35254 5.34962,0"
style="font-size:12.0000124px;font-style:normal;font-weight:normal;line-height:125%;fill:#6184a3;fill-opacity:1;stroke:none;display:inline;font-family:Bitstream Vera Sans"
transform="matrix(0.995433,-0.09546066,0.09546066,0.995433,0,0)"
inkscape:connector-curvature="0" />
<g
id="g2632"
style="display:inline">
<path
style="fill:url(#linearGradient2606);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;marker-end:none"
d="m 174.75585,201.60224 c -6.04576,2.46667 -10.16789,4.4194 -12.88454,6.35064 -2.71665,1.93124 -3.19257,4.60007 -3.24631,6.26587 -0.0269,0.8329 0.0809,1.77774 0.63189,2.44014 0.55103,0.6624 1.80769,1.87421 2.75794,2.38558 1.90049,1.02274 7.5417,2.42901 10.51899,3.07308 11.90917,2.57627 26.80568,1.68117 26.80568,1.68117 1.69307,1.2452 2.83283,2.82434 3.269,4.26902 4.5766,-1.88674 11.81084,-6.58439 13.15657,-8.57706 -5.45142,-4.19955 -10.79692,-6.33346 -16.51317,-8.30847 -1.59867,-0.71918 -2.87956,-1.22649 -0.71773,2.55635 0.98506,2.47275 0.85786,5.05143 0.57176,7.41825 0,0 -16.52749,0.40678 -28.23838,-2.1266 -2.92772,-0.63334 -5.46627,-0.95523 -7.21875,-1.89832 -0.87624,-0.47154 -1.48296,-0.8208 -1.91578,-1.3411 -0.43282,-0.5203 -0.2196,-1.29055 -0.20128,-1.85858 0.0366,-1.13607 0.25336,-1.67063 2.86177,-3.52492 2.60841,-1.85429 5.65407,-3.36195 11.65936,-5.81211 -0.0877,-1.29125 -0.29025,-2.5059 -1.29702,-2.99294 z"
id="path2414"
sodipodi:nodetypes="csssssccccccssssscc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient2618);fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 269.62539,220.7482 c -1.43576,-0.13963 -2.58044,0.30288 -2.56084,1.50218 0.94391,0.85652 1.34942,2.43518 1.48562,3.14008 0.1362,0.7049 0.0359,1.21914 -0.48562,1.89004 -1.043,1.3418 -3.12498,1.56875 -6.5006,2.72063 -6.75124,2.30377 -16.89306,2.52561 -27.90689,3.84639 -22.02767,2.64157 -39.03164,3.76107 -39.03164,3.76107 1.98346,-4.64758 6.32828,-4.41197 6.34903,-8.20969 0.27376,-0.89755 -3.14597,-1.31638 -5.09943,-0.10731 -4.26694,3.70137 -7.59152,6.75353 -10.69418,10.51311 l 1.88795,3.08438 c 0,0 26.13006,-2.88973 48.19776,-5.5361 11.03385,-1.32318 20.95601,-1.99856 27.80968,-4.33728 3.42683,-1.16936 5.95975,-1.49022 7.6409,-3.51958 0.63172,-0.76256 1.35238,-3.04699 1.06804,-4.73369 -0.21951,-1.30213 -1.14979,-3.09774 -2.15978,-4.01423 z"
id="path2608"
sodipodi:nodetypes="ccsssscccccssssc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient2630);fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 254.36185,220.33948 c -6.84997,3.24198 -7.15311,8.60912 -5.95953,12.79884 1.19358,4.18972 5.26293,8.75677 9.32121,12.40608 8.11656,7.29861 12.06046,9.33163 12.06046,9.33163 -3.71515,-0.10342 -7.89887,-1.41174 -8.13315,0.49304 -0.9483,2.97582 11.49137,3.47486 17.43787,2.70205 -1.39456,-7.57836 -3.79323,-13.21546 -7.73151,-14.90312 -1.68464,-0.14804 0.31242,4.72441 0.76985,9.39604 0,0 -3.62454,-1.73122 -11.60519,-8.90762 -3.99032,-3.5882 -7.37386,-7.3421 -8.47319,-11.20099 -1.09933,-3.85889 0.0776,-6.1205 4.95082,-9.53176 0.92816,-0.99528 -1.28985,-2.45913 -2.63764,-2.58419 z"
id="path2620"
sodipodi:nodetypes="csscccccsscc"
inkscape:connector-curvature="0" />
</g>
<path
sodipodi:nodetypes="cccccc"
id="rect2386"
d="m 213.96569,234.57806 2.18756,-14.42897 15.21982,6.08793 21.49387,29.94828 -20.40591,9.21832 -18.49534,-30.82556 z"
style="fill:url(#linearGradient2395);fill-opacity:1;stroke:none;display:inline"
inkscape:connector-curvature="0" />
<g
id="g2649"
style="display:inline">
<path
style="fill:url(#radialGradient2647);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 232.55816,219.5295 -15.92827,0.32199 3.08809,-15.15716 12.84018,14.83517 z"
id="path1443"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" />
<path
style="fill:#812310;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 221.60041,219.29315 -4.41205,0.0782 0.85429,-3.98263 3.55776,3.90445 z"
id="path1452"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient2643);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
d="m 269.44172,159.27421 0.098,8.91471 8.0581,8.72344 7.75906,0.7992 -52.80669,41.84092 -6.66532,-3.30696 -5.08243,-5.618 -1.08987,-5.91194 49.72911,-45.44137 z"
id="rect1437"
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient2640);fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 268.94766,168.32844 8.3426,8.82719 -51.1007,38.68262 -4.9197,-5.4436 47.6778,-42.06621 z"
id="rect1446"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" />
<path
style="fill:#ffe965;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;display:inline"
d="m 285.33776,177.73216 -8.16219,-0.86619 -7.7518,-8.67862 0.0132,-9.14293 8.36213,0.75209 7.18862,9.57682 0.35007,8.35883 z"
id="path1440"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" />
<path
style="fill:#cb391c;fill-opacity:1;fill-rule:evenodd;stroke:none"
d="m 280.72049,168.46367 0.1644,4.05654 -3.81335,-0.71676 -2.87504,-3.18901 -0.28089,-3.53393 3.85447,-0.16637 2.95041,3.54953 z"
id="path1449"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" />
</g>
<g
id="g2657"
style="display:inline">
<path
style="fill:url(#linearGradient2406);fill-opacity:1;stroke:none"
d="m 183.88617,256.82796 0.99991,-16.30721 17.2878,8.44012 26.05488,38.00946 -29.28095,-1.13363 -15.06164,-29.00874 z"
id="rect2397"
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient2325);fill-opacity:1;stroke:#000000;stroke-linejoin:round;stroke-opacity:1;display:inline"
d="m 200.90647,238.44836 -8.04601,15.77386 -7.05577,-13.57337 15.10178,-2.20049 z"
id="rect2207"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient2227);fill-opacity:1;stroke:#000000;stroke-linejoin:round;stroke-opacity:1"
d="m 201.05389,238.55401 62.11704,24.91912 -7.88689,3.21429 -4.35152,9.30976 1.1716,9.96396 -59.31453,-31.72759 -0.49402,-7.36382 3.09592,-5.82826 5.6624,-2.48746 z"
id="rect1328"
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#radialGradient2317);fill-opacity:1;stroke:#000000;stroke-linejoin:round;stroke-opacity:1;display:inline"
d="m 255.27801,266.53504 7.9241,-3.04772 0.85337,10.24037 -3.9011,8.28983 -8.04601,3.77919 -1.341,-9.63083 4.51064,-9.63084 z"
id="rect2204"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient2247);fill-opacity:1;stroke:none;display:inline"
d="m 195.7549,241.421 59.13059,24.7962 -4.5917,9.76614 -57.48995,-29.00967 2.95106,-5.55267 z"
id="rect2210"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" />
<path
style="fill:#00b527;fill-opacity:1;stroke:none"
d="m 255.02263,275.21029 2.08411,-4.1069 2.96459,-1.06995 0.69433,3.37197 -1.76759,3.85723 -3.15516,1.38315 -0.82028,-3.4355 z"
id="rect2308"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" />
<path
style="fill:#258209;fill-opacity:1;stroke:none;display:inline"
d="m 186.56849,241.00362 3.54963,-0.47312 -2.02297,3.53926 -1.52666,-3.06614 z"
id="rect2327"
sodipodi:nodetypes="cccc"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,89 @@
<?php
namespace Hestia\WebApp\Installers\Drupal;
use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
class DrupalSetup extends BaseSetup {
protected $appname = "drupal";
protected $appInfo = [
"name" => "Drupal",
"group" => "cms",
"enabled" => "yes",
"version" => "latest",
"thumbnail" => "drupal-thumb.png",
];
protected $config = [
"form" => [
"username" => ["type" => "text", "value" => "admin"],
"password" => "password",
"email" => "text",
],
"database" => true,
"resources" => [
"composer" => ["src" => "drupal/recommended-project", "dst" => "/"],
],
"server" => [
"nginx" => [
"template" => "drupal-composer",
],
"php" => [
"supported" => ["8.1", "8.2"],
],
],
];
public function install(array $options = null): bool {
parent::install($options);
parent::setup($options);
$this->appcontext->runComposer(
["require", "-d " . $this->getDocRoot(), "drush/drush"],
$status2,
["version" => 2, "php_version" => $options["php_version"]],
);
$htaccess_rewrite = '
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)$ web/$1 [L]
</IfModule>';
$tmp_configpath = $this->saveTempFile($htaccess_rewrite);
$this->appcontext->runUser(
"v-move-fs-file",
[$tmp_configpath, $this->getDocRoot(".htaccess")],
$result,
);
$this->appcontext->runUser(
"v-run-cli-cmd",
[
"/usr/bin/php" . $options["php_version"],
$this->getDocRoot("/vendor/drush/drush/drush"),
"site-install",
"standard",
"--db-url=mysql://" .
$this->appcontext->user() .
"_" .
$options["database_user"] .
":" .
$options["database_password"] .
"@localhost:3306/" .
$this->appcontext->user() .
"_" .
$options["database_name"] .
"",
"--account-name=" .
$options["username"] .
" --account-pass=" .
$options["password"],
"--site-name=Drupal",
"--site-mail=" . $options["email"],
],
$status,
);
return $status->code === 0;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,214 @@
<?php
namespace Hestia\WebApp\Installers\Flarum;
use Hestia\System\Util;
use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
class FlarumSetup extends BaseSetup {
protected $appInfo = [
"name" => "Flarum",
"group" => "forum",
"enabled" => true,
"version" => "latest",
"thumbnail" => "fl-thumb.png",
];
protected $appname = "flarum";
protected $config = [
"form" => [
"forum_title" => ["type" => "text", "value" => "Flarum Forum"],
"admin_username" => ["value" => "fladmin"],
"admin_email" => "text",
"admin_password" => "password",
"install_directory" => ["type" => "text", "value" => "", "placeholder" => "/"],
],
"database" => true,
"resources" => [
"composer" => ["src" => "flarum/flarum"],
],
"server" => [
"nginx" => [
"template" => "flarum",
],
"php" => [
"supported" => ["8.0", "8.1", "8.2"],
],
],
];
// Our updateFile routine done the 'Hestia way'
public function updateFile($file, $search, $replace) {
$result = null;
$this->appcontext->runUser("v-open-fs-file", [$file], $result);
foreach ($result->raw as $line_num => $line) {
if (strpos($line, $search) !== false) {
$result->raw[$line_num] = str_replace($search, $replace, $line);
}
}
$tmp = $this->saveTempFile(implode("\r\n", $result->raw));
if (!$this->appcontext->runUser("v-move-fs-file", [$tmp, $file], $result)) {
throw new \Exception("Error updating file in: " . $tmp . " " . $result->text);
}
return $result;
}
public function install(array $options = null): bool {
parent::setAppDirInstall($options["install_directory"]);
parent::install($options);
parent::setup($options);
$result = null;
// Move public folder content (https://docs.flarum.org/install/#customizing-paths)
if (
!$this->appcontext->runUser(
"v-list-fs-directory",
[$this->getDocRoot("public")],
$result,
)
) {
throw new \Exception(
"Error listing folder at: " . $this->getDocRoot("public") . $result->text,
);
}
foreach ($result->raw as $line_num => $line) {
$detail = explode("|", $line);
$type = $detail[0];
$name = end($detail);
if ($name != "") {
if ($type == "d") {
// Directory
if (
!$this->appcontext->runUser(
"v-move-fs-directory",
[
$this->getDocRoot("public") . "/" . $name,
$this->getDocRoot() . "/" . $name,
],
$result,
)
) {
throw new \Exception(
"Error moving folder at: " .
$this->getDocRoot("public") .
"/" .
$name .
$result->text,
);
}
} else {
if (
!$this->appcontext->runUser(
"v-move-fs-file",
[
$this->getDocRoot("public") . "/" . $name,
$this->getDocRoot() . "/" . $name,
],
$result,
)
) {
throw new \Exception(
"Error moving file at: " .
$this->getDocRoot("public") .
"/" .
$name .
$result->text,
);
}
}
}
}
if (
!$this->appcontext->runUser(
"v-delete-fs-directory",
[$this->getDocRoot("public")],
$result,
)
) {
throw new \Exception(
"Error deleting folder at: " . $this->getDocRoot("public") . $result->text,
);
}
// Not using 'public'; enable protection rewrite rules and update paths
$result = $this->updateFile(
$this->getDocRoot(".htaccess"),
"# RewriteRule ",
"RewriteRule ",
);
$result = $this->updateFile(
$this->getDocRoot("index.php"),
'$site = require \'../site.php\';',
'$site = require \'./site.php\';',
);
$result = $this->updateFile(
$this->getDocRoot("site.php"),
"'public' => __DIR__.'/public',",
"'public' => __DIR__,",
);
// POST install
$this->appcontext->run(
"v-list-web-domain",
[$this->appcontext->user(), $this->domain, "json"],
$status,
);
$sslEnabled = $status->json[$this->domain]["SSL"] == "no" ? 0 : 1;
$webDomain = ($sslEnabled ? "https://" : "http://") . $this->domain;
$webPort = $sslEnabled ? "443" : "80";
$mysql_host = "localhost";
$mysql_database = addcslashes(
$this->appcontext->user() . "_" . $options["database_name"],
"\\'",
);
$mysql_username = addcslashes(
$this->appcontext->user() . "_" . $options["database_user"],
"\\'",
);
$mysql_password = addcslashes($options["database_password"], "\\'");
$table_prefix = addcslashes(Util::generate_string(5, false) . "_", "\\'");
$subfolder = $options["install_directory"];
if (substr($subfolder, 0, 1) != "/") {
$subfolder = "/" . $subfolder;
}
$cmd =
"/usr/bin/curl --location --post301 --insecure --resolve " .
$this->domain .
":$webPort:" .
$this->appcontext->getWebDomainIp($this->domain) .
" " .
escapeshellarg($webDomain . $subfolder . "/index.php") .
" -d " .
escapeshellarg(
"forumTitle=" .
rawurlencode($options["forum_title"]) .
"&mysqlHost=" .
rawurlencode($mysql_host) .
"&mysqlDatabase=" .
rawurlencode($mysql_database) .
"&mysqlUsername=" .
rawurlencode($mysql_username) .
"&mysqlPassword=" .
rawurlencode($mysql_password) .
"&tablePrefix=" .
rawurlencode($table_prefix) .
"&adminUsername=" .
rawurlencode($options["admin_username"]) .
"&adminEmail=" .
rawurlencode($options["admin_email"]) .
"&adminPassword=" .
rawurlencode($options["admin_password"]) .
"&adminPasswordConfirmation=" .
rawurlencode($options["admin_password"]),
);
exec($cmd, $output, $return_var);
// Report any errors
if ($return_var > 0) {
throw new \Exception(implode(PHP_EOL, $output));
}
return $result->code === 0 && $return_var === 0;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -0,0 +1,76 @@
<?php
namespace Hestia\WebApp\Installers\Grav;
use Hestia\System\Util;
use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
class GravSetup extends BaseSetup {
protected $appInfo = [
"name" => "Grav",
"group" => "cms",
"enabled" => true,
"version" => "latest",
"thumbnail" => "grav-symbol.svg",
];
protected $appname = "grav";
protected $config = [
"form" => [
"admin" => ["type" => "boolean", "value" => false, "label" => "Create admin account"],
"username" => ["text" => "admin"],
"password" => "password",
"email" => "text",
],
"database" => false,
"resources" => [
"composer" => ["src" => "getgrav/grav", "dst" => "/"],
],
"server" => [
"nginx" => [
"template" => "grav",
],
"php" => [
"supported" => ["7.4", "8.0", "8.1"],
],
],
];
public function install(array $options = null) {
parent::install($options);
parent::setup($options);
if ($options["admin"] == true) {
chdir($this->getDocRoot());
$this->appcontext->runUser(
"v-run-cli-cmd",
[
"/usr/bin/php" . $options["php_version"],
$this->getDocRoot("/bin/gpm"),
"install admin",
],
$status,
);
$this->appcontext->runUser(
"v-run-cli-cmd",
[
"/usr/bin/php" . $options["php_version"],
$this->getDocRoot("/bin/plugin"),
"login new-user",
"-u " . $options["username"],
"-p " . $options["password"],
"-e " . $options["email"],
"-P a",
"-N " . $options["username"],
"-l en",
],
$status,
);
return $status->code === 0;
} else {
return true;
}
}
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Fireworks CS6, Export SVG Extension by Aaron Beall (http://fireworks.abeall.com) . Version: 0.6.1 -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg id="Grav-logo.fw-Page%201" viewBox="0 0 186 186" style="background-color:#ffffff00" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" x="0px" y="0px" width="186px" height="186px">
<g id="Layer_1_1">
<g>
<path d="M 98.4134 69.506 C 99.4991 68.4215 99.4991 66.6638 98.4134 65.5802 C 97.3273 64.4957 95.5692 64.4957 94.4835 65.5802 C 93.3973 66.6638 93.3973 68.4215 94.4835 69.506 C 95.5692 70.5895 97.3273 70.5895 98.4134 69.506 Z" fill="#221e1f"/>
<path d="M 88.6113 71.4462 C 87.5251 72.5297 87.5251 74.2875 88.6113 75.372 C 89.6955 76.4555 91.4551 76.4555 92.5412 75.372 C 93.6254 74.2875 93.6254 72.5297 92.5412 71.4462 C 91.4551 70.3617 89.6955 70.3617 88.6113 71.4462 Z" fill="#221e1f"/>
<path d="M 105.6614 72.8903 C 103.9628 71.193 101.2094 71.1945 99.5118 72.8903 L 95.8949 76.5033 C 94.1958 78.2006 94.1958 80.9511 95.8949 82.6464 L 99.8659 86.6146 C 101.5644 88.3119 104.3179 88.3104 106.0155 86.6146 L 109.6324 83.0016 C 111.331 81.3043 111.331 78.5538 109.6324 76.857 L 105.6614 72.8903 Z" fill="#221e1f"/>
<path d="M 140.0328 59.4342 C 144.622 50.7036 138.1301 45.604 133.6678 41.4182 C 126.5199 34.7063 119.3906 31.8046 111.2479 41.0982 C 103.1106 50.3923 108.2806 61.171 115.7239 66.4911 C 123.1706 71.8111 135.4371 68.1624 140.0328 59.4342 ZM 127.7032 55.8539 C 124.8355 52.4203 130.1964 46.1865 133.5598 49.9089 C 139.7549 56.7671 130.5759 59.2874 127.7032 55.8539 Z" fill="#221e1f"/>
<path d="M 92.7791 0.6418 C 41.5387 0.6418 0 42.1363 0 93.3177 C 0 144.503 41.5387 186 92.7791 186 C 144.0164 186 185.5547 144.503 185.5547 93.3177 C 185.5547 42.1363 144.0164 0.6418 92.7791 0.6418 ZM 142.9489 108.5071 C 133.2741 102.1201 127.7457 96.7234 122.9299 87.0473 C 119.6367 93.6768 111.9473 103.5349 98.9765 111.2898 C 92.5012 131.4129 61.6979 169.9242 51.7287 164.4651 C 48.7769 162.8503 48.2163 160.7232 48.8829 158.5289 C 49.5979 153.0278 60.9897 140.3846 60.9897 140.3846 C 60.9897 140.3846 61.2305 143.0375 64.8748 148.6264 C 60.0613 133.699 72.828 115.2805 76.21 109.0106 C 81.5244 107.3187 81.9093 100.5024 81.9093 100.5024 C 82.2492 89.9662 77.5378 82.3381 72.972 77.7761 C 76.2524 81.7756 77.3142 87.8196 77.47 93.3548 C 77.47 93.3631 77.47 93.3743 77.47 93.3856 C 77.4866 94.011 77.4866 94.6247 77.4841 95.2365 C 77.3283 99.8184 75.9409 106.1478 72.972 106.1478 L 72.9808 106.1878 C 69.9285 106.0571 66.1602 106.7079 62.7933 107.7597 L 55.35 109.5365 C 55.35 109.5365 59.3195 109.355 61.463 111.205 C 59.0758 115.0883 53.7701 119.9073 47.8524 122.6451 C 39.2424 126.6339 36.7678 118.6974 41.1393 113.5359 C 42.2127 112.2743 43.2969 111.2108 44.322 110.3395 C 43.6622 109.6575 43.2461 108.7442 43.1367 107.5851 C 43.1357 107.5792 43.1313 107.5734 43.1299 107.5675 C 42.5297 104.9341 42.8608 101.5152 46.591 96.0814 C 47.3275 94.9311 48.2104 93.7519 49.2682 92.5542 C 49.302 92.5132 49.3303 92.4752 49.3645 92.4352 C 49.4099 92.3801 49.4607 92.3279 49.5076 92.2742 C 49.5344 92.2444 49.5598 92.2147 49.5881 92.1835 C 49.8543 91.881 50.1376 91.5854 50.4404 91.3 C 54.8855 86.7517 61.9074 82.0215 72.972 77.7761 C 85.8759 58.8293 90.6014 55.2753 90.6014 55.2753 C 92.0299 53.8161 94.5269 52.0018 95.4446 51.5393 C 88.706 39.8425 87.3454 23.3534 89.1109 18.91 C 88.9678 19.1506 88.8364 19.3964 88.727 19.6511 C 89.3814 18.1295 89.82 17.6402 90.4998 16.8396 C 92.34 14.6647 98.5775 13.5251 100.8172 18.1324 C 102.0103 20.5887 102.2355 23.7705 102.1959 26.1562 C 97.2565 25.899 92.7761 31.5328 92.7761 31.5328 C 92.7761 31.5328 96.8672 29.6043 101.9498 29.5702 C 101.9498 29.5702 103.3045 30.7469 104.9948 32.9569 C 102.7126 37.2378 98.9609 46.3065 101.6777 55.827 C 102.128 57.7033 102.8088 59.3157 103.6542 60.6875 C 103.6864 60.7466 103.7133 60.8061 103.746 60.8671 C 103.8139 60.9559 103.8749 61.0295 103.9384 61.1115 C 108.4305 67.8673 116.664 68.6576 116.664 68.6576 C 112.7878 66.7257 109.6309 63.6488 107.5753 59.9182 C 106.5068 57.9229 105.8524 56.2383 105.4504 54.9094 C 103.2703 46.4978 106.4799 41.5875 108.3172 38.2955 C 112.5446 31.745 120.2458 27.7797 128.5232 28.7242 C 140.1402 30.0483 148.4841 40.5298 147.1562 52.1315 C 146.3465 59.2196 142.1137 65.0826 136.2982 68.2732 C 137.6969 72.0546 136.202 76.6394 136.202 76.6394 C 139.7241 81.0652 139.8794 83.6167 139.7578 85.9867 C 135.2814 85.2345 130.9324 88.2523 130.9324 88.2523 C 130.9324 88.2523 139.5683 86.1931 144.5854 90.6633 C 147.8482 94.169 150.0244 97.3372 151.3211 99.6769 C 153.1784 103.0139 161.8026 103.2519 160.8204 109.2535 C 159.8378 115.2463 153.2604 115.3029 142.9489 108.5071 Z" fill="#221e1f"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -0,0 +1,55 @@
<?php
namespace Hestia\WebApp\Installers\Laravel;
use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
class LaravelSetup extends BaseSetup {
protected $appname = "laravel";
protected $appInfo = [
"name" => "Laravel",
"group" => "framework",
"enabled" => true,
"version" => "10.x",
"thumbnail" => "laravel-thumb.png",
];
protected $config = [
"form" => [],
"database" => true,
"resources" => [
"composer" => ["src" => "laravel/laravel", "dst" => "/"],
],
"server" => [
"nginx" => [
"template" => "laravel",
],
"php" => [
"supported" => ["8.1", "8.2"],
],
],
];
public function install(array $options = null): bool {
parent::install($options);
parent::setup($options);
$result = null;
$htaccess_rewrite = '
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]
</IfModule>';
$tmp_configpath = $this->saveTempFile($htaccess_rewrite);
$this->appcontext->runUser(
"v-move-fs-file",
[$tmp_configpath, $this->getDocRoot(".htaccess")],
$result,
);
return $result->code === 0;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,53 @@
<svg xmlns="http://www.w3.org/2000/svg" width="270" height="300" viewBox="0 0 270 300">
<defs>
<linearGradient id="gradient" gradientUnits="userSpaceOnUse" x1="24" x2="246" y1="238" y2="16">
<stop offset="0%" stop-color="#0a00b2"/>
<stop offset="50%" stop-color="#ff0000"/>
<stop offset="100%" stop-color="#fffc00"/>
</linearGradient>
<style>
.wordmark {
fill: #000000;
}
.petal {
opacity: 0.65;
}
.petals {
fill: url(#gradient);
}
</style>
</defs>
<path id="wordmark" class="wordmark" d="M51.242,283.882l-0.046-35.7H46.274L33.716,271.845,20.928,248.178H16.006v35.7h5.7v-23.36L32.289,279.8h2.668l10.58-19.585,0.046,23.666h5.658Zm30.313-13.465a16.339,16.339,0,0,0-1.587-7.371,11.726,11.726,0,0,0-10.833-6.707A12.1,12.1,0,0,0,58.072,263.1a15.488,15.488,0,0,0-1.633,7.166,15.245,15.245,0,0,0,1.679,7.218,12.194,12.194,0,0,0,4.761,4.973,15.1,15.1,0,0,0,12.834.663A10.484,10.484,0,0,0,79.9,279.8l-3.082-3.927a8.521,8.521,0,0,1-6.67,2.958,8.086,8.086,0,0,1-5.313-1.759,7.558,7.558,0,0,1-2.645-4.769H81.463q0.092-1.224.092-1.887h0Zm-12.42-8.977a6.462,6.462,0,0,1,4.692,1.836,7.809,7.809,0,0,1,2.254,4.794H62.143a7.89,7.89,0,0,1,2.323-4.82,6.534,6.534,0,0,1,4.669-1.81h0Zm35.328-15.4v13.976a9.068,9.068,0,0,0-3.45-2.755,10.682,10.682,0,0,0-4.462-.918,11.775,11.775,0,0,0-10.81,6.631,17.384,17.384,0,0,0,0,14.613,11.74,11.74,0,0,0,10.81,6.656,10.891,10.891,0,0,0,4.623-.969,9.033,9.033,0,0,0,3.519-2.908v3.52h5.52V246.036h-5.75Zm-7.222,32.745a6.833,6.833,0,0,1-3.726-1.045,7.251,7.251,0,0,1-2.622-2.984,10.911,10.911,0,0,1,0-8.977,7.264,7.264,0,0,1,2.622-2.984,7.166,7.166,0,0,1,7.452,0,7.264,7.264,0,0,1,2.622,2.984,10.911,10.911,0,0,1,0,8.977,7.251,7.251,0,0,1-2.622,2.984,6.835,6.835,0,0,1-3.726,1.045h0Zm19.6,5.1h5.75V256.645h-5.75v27.237Zm21.758-27.543a18.782,18.782,0,0,0-5.75.867,13.457,13.457,0,0,0-4.646,2.5l2.254,4.641a9.726,9.726,0,0,1,3.381-1.989,12.032,12.032,0,0,1,4.071-.714,6.239,6.239,0,0,1,4.577,1.53,5.912,5.912,0,0,1,1.541,4.386v0.408h-6.348q-5.291,0-7.751,2.219a7.545,7.545,0,0,0-2.461,5.891,7.959,7.959,0,0,0,1.127,4.208,7.678,7.678,0,0,0,3.2,2.907,10.581,10.581,0,0,0,4.83,1.046,10.77,10.77,0,0,0,4.715-.944,6.892,6.892,0,0,0,3.013-2.729v3.316h5.428V267.917q0-5.814-2.875-8.7t-8.3-2.882h0ZM137.812,279.6a5.281,5.281,0,0,1-3.45-1.02,3.378,3.378,0,0,1-1.242-2.754q0-3.621,4.968-3.621h5.934v3.162a5.86,5.86,0,0,1-2.369,3.137,6.869,6.869,0,0,1-3.841,1.1h0Zm60.306-31.419-8.234,27.288-8.142-27.288h-5.52L167.9,275.313l-8-27.135h-6.21l10.672,35.7h6.394l8.1-26.421,7.958,26.421h6.348l10.672-35.7h-5.7Zm9.338,35.7h5.75V256.645h-5.75v27.237Zm30.911,0h6.992L234.135,268.07l10.258-11.425h-6.9l-11.914,12.241v-22.85h-5.75v37.846h5.75V276.69l4.278-4.488Zm9.568,0h5.75V256.645h-5.75v27.237Zm-128.2-30.857a3.567,3.567,0,0,0,2.645-1.043,3.513,3.513,0,0,0,1.035-2.574,3.073,3.073,0,0,0-1.058-2.388,3.781,3.781,0,0,0-2.622-.95,3.677,3.677,0,0,0-2.622,1,3.437,3.437,0,0,0,0,4.961,3.677,3.677,0,0,0,2.622,1h0Zm90.62,0A3.569,3.569,0,0,0,213,251.982a3.513,3.513,0,0,0,1.035-2.574,3.077,3.077,0,0,0-1.058-2.388,3.784,3.784,0,0,0-2.622-.95,3.674,3.674,0,0,0-2.622,1,3.437,3.437,0,0,0,0,4.961,3.674,3.674,0,0,0,2.622,1h0Zm40.479,0a3.569,3.569,0,0,0,2.645-1.043,3.513,3.513,0,0,0,1.035-2.574,3.077,3.077,0,0,0-1.058-2.388,3.784,3.784,0,0,0-2.622-.95,3.674,3.674,0,0,0-2.622,1,3.437,3.437,0,0,0,0,4.961,3.674,3.674,0,0,0,2.622,1h0Z"/>
<g id="petals" class="petals">
<path class="petal" d="M103.283,45.02c-0.647,8.246.645,16.05,5.16,23.1a23.041,23.041,0,0,0,13.04,9.63,5.672,5.672,0,0,0,4.719-1.153c6.093-5.8,9.031-12.89,8-21.625-1.829-15.522-11.06-26.108-22.808-36.2C106.826,27.538,104,35.917,103.283,45.02Z"/>
<path class="petal" d="M86.913,53.254c1.081,8.2,3.968,15.565,9.849,21.519a23.04,23.04,0,0,0,14.757,6.709,5.673,5.673,0,0,0,4.376-2.109c4.754-6.944,6.154-14.486,3.331-22.816-5.016-14.8-16.246-23.238-29.836-30.668C86.743,35.417,85.719,44.2,86.913,53.254Z"/>
<path class="petal" d="M72.611,64.711c2.762,7.8,7.117,14.4,14.108,19a23.04,23.04,0,0,0,15.83,3.494,5.673,5.673,0,0,0,3.842-2.973c3.206-7.78,3.007-15.449-1.486-23.01C96.921,47.786,84.183,41.87,69.345,37.429,68.737,47.3,69.561,56.1,72.611,64.711Z"/>
<path class="petal" d="M61,78.891c4.323,7.052,9.955,12.605,17.75,15.653a23.04,23.04,0,0,0,16.21.126A5.671,5.671,0,0,0,98.1,90.963c1.519-8.277-.27-15.737-6.237-22.2-10.6-11.483-24.293-14.621-39.73-15.881C53.595,62.666,56.232,71.107,61,78.891Z"/>
<path class="petal" d="M52.6,95.174c5.694,6,12.358,10.26,20.617,11.62A23.038,23.038,0,0,0,89.1,103.548a5.673,5.673,0,0,0,2.3-4.279c-0.235-8.412-3.536-15.337-10.716-20.416-12.759-9.028-26.8-9.251-42.164-7.274C41.979,80.844,46.313,88.552,52.6,95.174Z"/>
<path class="petal" d="M47.764,112.849c6.817,4.684,14.222,7.467,22.582,7.08a23.035,23.035,0,0,0,14.86-6.478,5.671,5.671,0,0,0,1.36-4.663c-1.979-8.179-6.647-14.266-14.727-17.742C57.483,84.868,43.7,87.57,29.086,92.7,34.4,101.041,40.238,107.679,47.764,112.849Z"/>
<path class="petal" d="M46.709,131.144c7.642,3.164,15.463,4.346,23.561,2.23a23.043,23.043,0,0,0,13.189-9.426,5.671,5.671,0,0,0,.361-4.844c-3.636-7.589-9.468-12.573-18.094-14.293-15.328-3.058-28.248,2.45-41.477,10.5C31.178,122.373,38.273,127.651,46.709,131.144Z"/>
<path class="petal" d="M49.481,149.257c8.133,1.506,16.029,1.037,23.509-2.717a23.041,23.041,0,0,0,10.941-11.962,5.67,5.67,0,0,0-.654-4.813c-5.135-6.667-11.876-10.33-20.67-10.219-15.629.2-27.121,8.27-38.386,18.9C32.465,143.907,40.5,147.6,49.481,149.257Z"/>
<path class="petal" d="M55.958,166.4c8.268-.218,15.894-2.319,22.431-7.546A23.037,23.037,0,0,0,86.6,144.878a5.67,5.67,0,0,0-1.64-4.572c-6.409-5.454-13.764-7.635-22.343-5.7C47.374,138.049,37.811,148.336,29,161.075,38.2,164.7,46.83,166.64,55.958,166.4Z"/>
<path class="petal" d="M65.857,181.82c8.042-1.933,15.065-5.573,20.372-12.045A23.039,23.039,0,0,0,91.358,154.4a5.672,5.672,0,0,0-2.555-4.131c-7.4-4-15.05-4.606-23.039-.928-14.2,6.536-21.413,18.586-27.381,32.878C48.137,183.853,56.979,183.953,65.857,181.82Z"/>
<path class="petal" d="M78.747,194.845c7.465-3.562,13.577-8.583,17.422-16.017a23.039,23.039,0,0,0,1.82-16.108,5.671,5.671,0,0,0-3.358-3.51c-8.073-2.375-15.679-1.376-22.728,3.883-12.529,9.344-17.08,22.631-19.947,37.852C61.836,200.518,70.506,198.778,78.747,194.845Z"/>
<path class="petal" d="M94.062,204.906c6.561-5.036,11.5-11.219,13.712-19.289a23.042,23.042,0,0,0-1.569-16.135,5.67,5.67,0,0,0-4.014-2.735c-8.391-.645-15.623,1.914-21.425,8.523-10.312,11.746-12,25.689-11.641,41.173C78.7,213.971,86.82,210.466,94.062,204.906Z"/>
<path class="petal" d="M111.135,211.562c5.371-6.29,8.912-13.363,9.4-21.718a23.035,23.035,0,0,0-4.89-15.456,5.666,5.666,0,0,0-4.5-1.84c-8.341,1.113-14.883,5.119-19.184,12.791-7.644,13.633-6.4,27.622-2.826,42.693C97.994,223.623,105.207,218.507,111.135,211.562Z"/>
<path class="petal" d="M129.219,214.524c3.945-7.27,5.939-14.924,4.68-23.2a23.038,23.038,0,0,0-8-14.1,5.674,5.674,0,0,0-4.78-.866c-7.927,2.823-13.494,8.1-16.106,16.5-4.643,14.924-.516,28.349,6.112,42.348C118.872,229.053,124.864,222.549,129.219,214.524Z"/>
<path class="petal" d="M147.523,213.661c2.348-7.931,2.706-15.833-.245-23.665a23.042,23.042,0,0,0-10.753-12.131,5.671,5.671,0,0,0-4.855.147c-7.167,4.41-11.514,10.731-12.323,19.489-1.439,15.563,5.389,27.837,14.783,40.152C140.424,230.024,144.932,222.416,147.523,213.661Z"/>
<path class="petal" d="M165.248,209.011c0.648-8.246-.645-16.049-5.16-23.1a23.038,23.038,0,0,0-13.04-9.631,5.67,5.67,0,0,0-4.719,1.154c-6.093,5.8-9.031,12.89-8,21.624,1.829,15.523,11.06,26.108,22.808,36.2C161.706,226.493,164.534,218.114,165.248,209.011Z"/>
<path class="petal" d="M181.619,200.778c-1.081-8.2-3.968-15.565-9.849-21.519a23.043,23.043,0,0,0-14.758-6.709,5.669,5.669,0,0,0-4.375,2.109c-4.754,6.944-6.155,14.486-3.331,22.816,5.015,14.8,16.245,23.238,29.835,30.668C181.789,218.614,182.813,209.83,181.619,200.778Z"/>
<path class="petal" d="M195.92,189.321c-2.762-7.8-7.117-14.4-14.108-19a23.04,23.04,0,0,0-15.83-3.494,5.668,5.668,0,0,0-3.841,2.972c-3.207,7.781-3.008,15.45,1.485,23.01,7.984,13.437,20.723,19.353,35.56,23.8C199.794,206.732,198.97,197.927,195.92,189.321Z"/>
<path class="petal" d="M207.527,175.14c-4.323-7.051-9.956-12.6-17.751-15.652a23.039,23.039,0,0,0-16.21-.127,5.672,5.672,0,0,0-3.139,3.707c-1.519,8.277.27,15.737,6.237,22.2,10.6,11.483,24.293,14.622,39.73,15.881C214.936,191.366,212.3,182.925,207.527,175.14Z"/>
<path class="petal" d="M215.931,158.857c-5.694-6-12.358-10.26-20.616-11.62a23.04,23.04,0,0,0-15.883,3.247,5.669,5.669,0,0,0-2.3,4.278c0.236,8.412,3.536,15.337,10.716,20.416,12.759,9.028,26.8,9.251,42.164,7.274C226.553,173.187,222.218,165.479,215.931,158.857Z"/>
<path class="petal" d="M220.767,141.182c-6.817-4.684-14.222-7.467-22.582-7.08a23.039,23.039,0,0,0-14.86,6.478,5.671,5.671,0,0,0-1.361,4.663c1.979,8.179,6.648,14.267,14.727,17.742,14.357,6.178,28.14,3.477,42.755-1.651C234.135,152.99,228.293,146.352,220.767,141.182Z"/>
<path class="petal" d="M221.822,122.888c-7.642-3.164-15.463-4.347-23.561-2.23a23.031,23.031,0,0,0-13.188,9.426,5.668,5.668,0,0,0-.361,4.844c3.636,7.588,9.468,12.573,18.093,14.292,15.328,3.058,28.248-2.45,41.477-10.5C237.354,131.659,230.259,126.38,221.822,122.888Z"/>
<path class="petal" d="M219.05,104.774c-8.132-1.506-16.028-1.037-23.509,2.717A23.042,23.042,0,0,0,184.6,119.453a5.673,5.673,0,0,0,.654,4.814c5.135,6.666,11.876,10.329,20.67,10.218,15.629-.2,27.121-8.27,38.387-18.9C236.066,110.124,228.029,106.436,219.05,104.774Z"/>
<path class="petal" d="M212.573,87.632c-8.268.218-15.894,2.319-22.43,7.546a23.038,23.038,0,0,0-8.215,13.975,5.672,5.672,0,0,0,1.64,4.572c6.409,5.454,13.764,7.635,22.343,5.7,15.246-3.441,24.809-13.728,33.619-26.467C230.33,89.328,221.7,87.391,212.573,87.632Z"/>
<path class="petal" d="M202.674,72.212c-8.042,1.932-15.065,5.573-20.372,12.045a23.036,23.036,0,0,0-5.129,15.378,5.67,5.67,0,0,0,2.555,4.131c7.4,4,15.05,4.606,23.039.928,14.2-6.536,21.413-18.586,27.381-32.878C220.4,70.178,211.552,70.078,202.674,72.212Z"/>
<path class="petal" d="M189.785,59.186c-7.465,3.562-13.577,8.583-17.423,16.017a23.039,23.039,0,0,0-1.82,16.108,5.672,5.672,0,0,0,3.358,3.51c8.073,2.376,15.679,1.376,22.729-3.883,12.528-9.345,17.08-22.632,19.947-37.853C206.7,53.513,198.025,55.254,189.785,59.186Z"/>
<path class="petal" d="M174.469,49.126c-6.561,5.036-11.5,11.218-13.712,19.289a23.046,23.046,0,0,0,1.569,16.135,5.673,5.673,0,0,0,4.015,2.735c8.39,0.645,15.622-1.913,21.424-8.523,10.312-11.745,12-25.688,11.641-41.173C189.831,40.06,181.712,43.565,174.469,49.126Z"/>
<path class="petal" d="M157.4,42.469c-5.371,6.29-8.912,13.363-9.4,21.718a23.041,23.041,0,0,0,4.889,15.456,5.671,5.671,0,0,0,4.5,1.84c8.341-1.113,14.883-5.12,19.185-12.791,7.644-13.633,6.4-27.622,2.826-42.693C170.537,30.408,163.325,35.524,157.4,42.469Z"/>
<path class="petal" d="M139.312,39.507c-3.945,7.27-5.939,14.924-4.68,23.2a23.035,23.035,0,0,0,8,14.1,5.67,5.67,0,0,0,4.779.865c7.928-2.823,13.494-8.1,16.106-16.5,4.643-14.924.516-28.349-6.112-42.348C149.659,24.978,143.667,31.482,139.312,39.507Z"/>
<path class="petal" d="M121.008,40.37c-2.348,7.931-2.706,15.833.245,23.665a23.042,23.042,0,0,0,10.753,12.131,5.671,5.671,0,0,0,4.855-.147c7.167-4.41,11.515-10.731,12.323-19.489,1.439-15.563-5.389-27.837-14.782-40.152C128.108,24.007,123.6,31.615,121.008,40.37Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,92 @@
<?php
namespace Hestia\WebApp\Installers\MediaWiki;
use Hestia\System\Util;
use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
class MediaWikiSetup extends BaseSetup {
protected $appInfo = [
"name" => "MediaWiki",
"group" => "cms",
"enabled" => true,
"version" => "1.40.0",
"thumbnail" => "MediaWiki-2020-logo.svg", //Max size is 300px by 300px
];
protected $appname = "mediawiki";
protected $extractsubdir = "/tmp-mediawiki";
protected $config = [
"form" => [
"admin_username" => ["type" => "text", "value" => "admin"],
"admin_password" => "password",
"language" => ["type" => "text", "value" => "en"],
],
"database" => true,
"resources" => [
"archive" => [
"src" => "https://releases.wikimedia.org/mediawiki/1.40/mediawiki-1.40.0.zip",
],
],
"server" => [
"nginx" => [
"template" => "default",
],
"php" => [
"supported" => ["7.4", "8.0"],
],
],
];
public function install(array $options = null) {
parent::install($options);
parent::setup($options);
//check if ssl is enabled
$this->appcontext->run(
"v-list-web-domain",
[$this->appcontext->user(), $this->domain, "json"],
$status,
);
if ($status->code !== 0) {
throw new \Exception("Cannot list domain");
}
$sslEnabled = $status->json[$this->domain]["SSL"] == "no" ? 0 : 1;
$webDomain = ($sslEnabled ? "https://" : "http://") . $this->domain;
$this->appcontext->runUser(
"v-copy-fs-directory",
[$this->getDocRoot($this->extractsubdir . "/mediawiki-1.39.2/."), $this->getDocRoot()],
$result,
);
$this->appcontext->runUser(
"v-run-cli-cmd",
[
"/usr/bin/php" . $options["php_version"],
$this->getDocRoot("maintenance/install.php"),
"--dbserver=localhost",
"--dbname=" . $this->appcontext->user() . "_" . $options["database_name"],
"--installdbuser=" . $this->appcontext->user() . "_" . $options["database_user"],
"--installdbpass=" . $options["database_password"],
"--dbuser=" . $this->appcontext->user() . "_" . $options["database_user"],
"--dbpass=" . $options["database_password"],
"--server=" . $webDomain,
"--scriptpath=", // must NOT be /
"--lang=" . $options["language"],
"--pass=" . $options["admin_password"],
"MediaWiki", // A Space here would trigger the next argument and preemptively set the admin username
$options["admin_username"],
],
$status,
);
$this->cleanup();
return $status->code === 0;
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace Hestia\WebApp\Installers\Nextcloud;
use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
class NextcloudSetup extends BaseSetup {
protected $appInfo = [
"name" => "Nextcloud",
"group" => "cloud",
"enabled" => true,
"version" => "latest",
"thumbnail" => "nextcloud-thumb.png",
];
protected $appname = "nextcloud";
protected $config = [
"form" => [
"username" => ["value" => "admin"],
"password" => "password",
],
"database" => true,
"resources" => [
"archive" => ["src" => "https://download.nextcloud.com/server/releases/latest.tar.bz2"],
],
"server" => [
"nginx" => [
"template" => "owncloud",
],
"php" => [
"supported" => ["8.0", "8.1", "8.2"],
],
],
];
public function install(array $options = null): bool {
parent::install($options);
parent::setup($options);
// install nextcloud
$php_version = $this->appcontext->getSupportedPHP(
$this->config["server"]["php"]["supported"],
);
$this->appcontext->runUser(
"v-run-cli-cmd",
[
"/usr/bin/php" . $options["php_version"],
$this->getDocRoot("occ"),
"maintenance:install",
"--database mysql",
"--database-name " . $this->appcontext->user() . "_" . $options["database_name"],
"--database-user " . $this->appcontext->user() . "_" . $options["database_user"],
"--database-pass " . $options["database_password"],
"--admin-user " . $options["username"],
"--admin-pass " . $options["password"],
],
$status,
);
$this->appcontext->runUser(
"v-run-cli-cmd",
[
"/usr/bin/php" . $options["php_version"],
$this->getDocRoot("occ"),
"config:system:set",
"trusted_domains 2 --value=" . $this->domain,
],
$status,
);
// Bump minimum memory limit to 512M
$result = null;
$file = $this->getDocRoot(".user.ini");
$this->appcontext->runUser("v-open-fs-file", [$file], $result);
array_push($result->raw, "memory_limit=512M");
$tmp = $this->saveTempFile(implode("\r\n", $result->raw));
if (!$this->appcontext->runUser("v-move-fs-file", [$tmp, $file], $result)) {
throw new \Exception("Error updating file in: " . $tmp . " " . $result->text);
}
return $status->code === 0;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,134 @@
<?php
namespace Hestia\WebApp\Installers\Opencart;
use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
class OpencartSetup extends BaseSetup {
protected $appInfo = [
"name" => "Opencart",
"group" => "ecommerce",
"enabled" => true,
"version" => "4.0.2.2",
"thumbnail" => "opencart-thumb.png",
];
protected $appname = "opencart";
protected $extractsubdir = "/tmp-opencart";
protected $config = [
"form" => [
"opencart_account_username" => ["value" => "ocadmin"],
"opencart_account_email" => "text",
"opencart_account_password" => "password",
],
"database" => true,
"resources" => [
"archive" => [
"src" =>
"https://github.com/opencart/opencart/releases/download/4.0.2.2/opencart-4.0.2.2.zip",
],
],
"server" => [
"nginx" => [
"template" => "opencart",
],
"php" => [
"supported" => ["7.4", "8.0", "8.1", "8.2"],
],
],
];
public function install(array $options = null): bool {
parent::install($options);
parent::setup($options);
$this->appcontext->runUser(
"v-copy-fs-directory",
[$this->getDocRoot($this->extractsubdir . "/upload/."), $this->getDocRoot()],
$result,
);
$this->appcontext->runUser("v-copy-fs-file", [
$this->getDocRoot("config-dist.php"),
$this->getDocRoot("config.php"),
]);
$this->appcontext->runUser("v-copy-fs-file", [
$this->getDocRoot("admin/config-dist.php"),
$this->getDocRoot("admin/config.php"),
]);
$this->appcontext->runUser("v-copy-fs-file", [
$this->getDocRoot(".htaccess.txt"),
$this->getDocRoot(".htaccess"),
]);
#Check if SSL is enabled
$this->appcontext->run(
"v-list-web-domain",
[$this->appcontext->user(), $this->domain, "json"],
$status,
);
if ($status->code !== 0) {
throw new \Exception("Cannot list domain");
}
if ($status->json[$this->domain]["SSL"] == "no") {
$protocol = "http://";
} else {
$protocol = "https://";
}
$this->appcontext->runUser(
"v-run-cli-cmd",
[
"/usr/bin/php" . $options["php_version"],
$this->getDocRoot("/install/cli_install.php"),
"install",
"--db_username " . $this->appcontext->user() . "_" . $options["database_user"],
"--db_password " . $options["database_password"],
"--db_database " . $this->appcontext->user() . "_" . $options["database_name"],
"--username " . $options["opencart_account_username"],
"--password " . $options["opencart_account_password"],
"--email " . $options["opencart_account_email"],
"--http_server " . $protocol . $this->domain . "/",
],
$status,
);
// After install, 'storage' folder must be moved to a location where the web server is not allowed to serve file
// - Opencart Nginx template and Apache ".htaccess" forbids acces to /storage folder
$this->appcontext->runUser(
"v-move-fs-directory",
[$this->getDocRoot("system/storage"), $this->getDocRoot()],
$result,
);
$this->appcontext->runUser(
"v-run-cli-cmd",
["sed", "-i", "s/'storage\//'..\/storage\// ", $this->getDocRoot("config.php")],
$status,
);
$this->appcontext->runUser(
"v-run-cli-cmd",
["sed", "-i", "s/'storage\//'..\/storage\// ", $this->getDocRoot("admin/config.php")],
$status,
);
$this->appcontext->runUser(
"v-run-cli-cmd",
["sed", "-i", "s/\^system\/storage\//^\/storage\// ", $this->getDocRoot(".htaccess")],
$status,
);
$this->appcontext->runUser("v-change-fs-file-permission", [
$this->getDocRoot("config.php"),
"640",
]);
$this->appcontext->runUser("v-change-fs-file-permission", [
$this->getDocRoot("admin/config.php"),
"640",
]);
// remove install folder
$this->appcontext->runUser("v-delete-fs-directory", [$this->getDocRoot("/install")]);
$this->cleanup();
return $status->code === 0;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -0,0 +1,93 @@
<?php
namespace Hestia\WebApp\Installers\Prestashop;
use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
class PrestashopSetup extends BaseSetup {
protected $appInfo = [
"name" => "Prestashop",
"group" => "ecommerce",
"enabled" => true,
"version" => "8.1.0",
"thumbnail" => "prestashop-thumb.png",
];
protected $appname = "prestashop";
protected $extractsubdir = "/tmp-prestashop";
protected $config = [
"form" => [
"prestashop_account_first_name" => ["value" => "John"],
"prestashop_account_last_name" => ["value" => "Doe"],
"prestashop_account_email" => "text",
"prestashop_account_password" => "password",
],
"database" => true,
"resources" => [
"archive" => [
"src" =>
"https://github.com/PrestaShop/PrestaShop/releases/download/8.1.0/prestashop_8.1.0.zip",
],
],
"server" => [
"nginx" => [
"template" => "prestashop",
],
"php" => [
"supported" => ["8.0", "8.1"],
],
],
];
public function install(array $options = null): bool {
parent::install($options);
parent::setup($options);
$this->appcontext->archiveExtract(
$this->getDocRoot($this->extractsubdir . "/prestashop.zip"),
$this->getDocRoot(),
);
//check if ssl is enabled
$this->appcontext->run(
"v-list-web-domain",
[$this->appcontext->user(), $this->domain, "json"],
$status,
);
if ($status->code !== 0) {
throw new \Exception("Cannot list domain");
}
if ($status->json[$this->domain]["SSL"] == "no") {
$ssl_enabled = 0;
} else {
$ssl_enabled = 1;
}
$php_version = $this->appcontext->getSupportedPHP(
$this->config["server"]["php"]["supported"],
);
$this->appcontext->runUser(
"v-run-cli-cmd",
[
"/usr/bin/php" . $options["php_version"],
$this->getDocRoot("/install/index_cli.php"),
"--db_user=" . $this->appcontext->user() . "_" . $options["database_user"],
"--db_password=" . $options["database_password"],
"--db_name=" . $this->appcontext->user() . "_" . $options["database_name"],
"--firstname=" . $options["prestashop_account_first_name"],
"--lastname=" . $options["prestashop_account_last_name"],
"--password=" . $options["prestashop_account_password"],
"--email=" . $options["prestashop_account_email"],
"--domain=" . $this->domain,
"--ssl=" . $ssl_enabled,
],
$status,
);
// remove install folder
$this->appcontext->runUser("v-delete-fs-directory", [$this->getDocRoot("/install")]);
$this->cleanup();
return $status->code === 0;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,37 @@
<?php
namespace Hestia\WebApp\Installers\Resources;
use Hestia\System\HestiaApp;
class ComposerResource {
private $project;
private $folder;
private $appcontext;
public function __construct(HestiaApp $appcontext, $data, $destination) {
$this->folder = dirname($destination);
$this->project = basename($destination);
$this->appcontext = $appcontext;
if (empty($data["version"])) {
$data["version"] = 2;
}
$this->appcontext->runComposer(
[
"create-project",
"--no-progress",
"--prefer-dist",
$data["src"],
"-d " . $this->folder,
$this->project,
],
$status,
$data,
);
if ($status->code !== 0) {
throw new \Exception("Error fetching Composer resource: " . $status->text);
}
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Hestia\WebApp\Installers\Resources;
use Hestia\System\HestiaApp;
class WpResource {
private $appcontext;
private $options;
public function __construct(HestiaApp $appcontext, $data, $destination, $options, $appinfo) {
$this->appcontext = $appcontext;
$this->appcontext->runWp(
[
"core",
"download",
"--locale=" . $options["language"],
"--version=" . $appinfo["version"],
"--path=" . $destination,
],
$status,
);
if ($status->code !== 0) {
throw new \Exception("Error fetching WP resource: " . $status->text);
}
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Hestia\WebApp\Installers\Symfony;
use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
class SymfonySetup extends BaseSetup {
protected $appInfo = [
"name" => "Symfony",
"group" => "framework",
"enabled" => true,
"version" => "latest",
"thumbnail" => "symfony-thumb.png",
];
protected $appname = "symfony";
protected $config = [
"form" => [],
"database" => true,
"resources" => [
"composer" => ["src" => "symfony/website-skeleton", "dst" => "/"],
],
"server" => [
"nginx" => [
"template" => "symfony4-5",
],
"php" => [
"supported" => ["8.1", "8.2"],
],
],
];
public function install(array $options = null): bool {
parent::install($options);
$result = null;
$htaccess_rewrite = '
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]
</IfModule>';
$this->appcontext->runComposer(
["config", "-d " . $this->getDocRoot(), "extra.symfony.allow-contrib", "true"],
$result,
);
$this->appcontext->runComposer(
["require", "-d " . $this->getDocRoot(), "symfony/apache-pack"],
$result,
);
$tmp_configpath = $this->saveTempFile($htaccess_rewrite);
$this->appcontext->runUser(
"v-move-fs-file",
[$tmp_configpath, $this->getDocRoot(".htaccess")],
$result,
);
return $result->code === 0;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,272 @@
<?php
namespace Hestia\WebApp\Installers\Wordpress;
use Hestia\System\Util;
use Hestia\WebApp\Installers\BaseSetup as BaseSetup;
use function Hestiacp\quoteshellarg\quoteshellarg;
class WordpressSetup extends BaseSetup {
protected $appInfo = [
"name" => "WordPress",
"group" => "cms",
"enabled" => true,
"version" => "latest",
"thumbnail" => "wp-thumb.png",
];
protected $appname = "wordpress";
protected $config = [
"form" => [
//'protocol' => [
// 'type' => 'select',
// 'options' => ['http','https'],
//],
"site_name" => ["type" => "text", "value" => "WordPress Blog"],
"wordpress_account_username" => ["value" => "wpadmin"],
"wordpress_account_email" => "text",
"wordpress_account_password" => "password",
"install_directory" => ["type" => "text", "value" => "", "placeholder" => "/"],
"language" => [
"type" => "select",
"value" => "en_US",
"options" => [
"cs_CZ" => "Czech",
"de_DE" => "German",
"es_ES" => "Spanish",
"en_US" => "English",
"fr_FR" => "French",
"hu_HU" => "Hungarian",
"it_IT" => "Italian",
"nl_NL" => "Dutch",
"pt_PT" => "Portuguese",
"pt_BR" => "Portuguese (Brazil)",
"sk_SK" => "Slovak",
"sr_RS" => "Serbian",
"tr_TR" => "Turkish",
"ru_RU" => "Russian",
"uk" => "Ukrainian",
"zh-CN" => "Simplified Chinese (China)",
"zh_TW" => "Traditional Chinese",
],
],
],
"database" => true,
"resources" => [
"wp" => ["src" => "https://wordpress.org/latest.tar.gz"],
],
"server" => [
"nginx" => [
"template" => "wordpress",
],
"php" => [
"supported" => ["7.4", "8.0", "8.1", "8.2"],
],
],
];
public function install(array $options = null) {
parent::setAppDirInstall($options["install_directory"]);
parent::install($options);
parent::setup($options);
$this->appcontext->runUser(
"v-open-fs-file",
[$this->getDocRoot("wp-config-sample.php")],
$result,
);
foreach ($result->raw as $line_num => $line) {
if ('$table_prefix =' === substr($line, 0, 15)) {
$result->raw[$line_num] =
'$table_prefix = \'' .
addcslashes(Util::generate_string(5, false) . "_", "\\'") .
"';\r\n";
continue;
}
if (!preg_match('/^define\(\s*\'([A-Z_]+)\',([ ]+)/', $line, $match)) {
continue;
}
$constant = $match[1];
$padding = $match[2];
switch ($constant) {
case "DB_NAME":
$result->raw[$line_num] =
"define( '" .
$constant .
"'," .
$padding .
"'" .
addcslashes(
$this->appcontext->user() . "_" . $options["database_name"],
"\\'",
) .
"' );";
break;
case "DB_USER":
$result->raw[$line_num] =
"define( '" .
$constant .
"'," .
$padding .
"'" .
addcslashes(
$this->appcontext->user() . "_" . $options["database_user"],
"\\'",
) .
"' );";
break;
case "DB_PASSWORD":
$result->raw[$line_num] =
"define( '" .
$constant .
"'," .
$padding .
"'" .
addcslashes($options["database_password"], "\\'") .
"' );";
break;
case "DB_HOST":
$result->raw[$line_num] =
"define( '" .
$constant .
"'," .
$padding .
"'" .
addcslashes("localhost", "\\'") .
"' );";
break;
case "DB_CHARSET":
$result->raw[$line_num] =
"define( '" .
$constant .
"'," .
$padding .
"'" .
addcslashes("utf8mb4", "\\'") .
"' );";
break;
case "AUTH_KEY":
case "SECURE_AUTH_KEY":
case "LOGGED_IN_KEY":
case "NONCE_KEY":
case "AUTH_SALT":
case "SECURE_AUTH_SALT":
case "LOGGED_IN_SALT":
case "NONCE_SALT":
$result->raw[$line_num] =
"define( '" .
$constant .
"'," .
$padding .
"'" .
Util::generate_string(64) .
"' );";
break;
}
}
$tmp_configpath = $this->saveTempFile(implode("\r\n", $result->raw));
if (
!$this->appcontext->runUser(
"v-move-fs-file",
[$tmp_configpath, $this->getDocRoot("wp-config.php")],
$result,
)
) {
throw new \Exception(
"Error installing config file in: " .
$tmp_configpath .
" to:" .
$this->getDocRoot("wp-config.php") .
$result->text,
);
}
$this->appcontext->downloadUrl(
"https://raw.githubusercontent.com/roots/wp-password-bcrypt/master/wp-password-bcrypt.php",
null,
$plugin_output,
);
$this->appcontext->runUser(
"v-add-fs-directory",
[$this->getDocRoot("wp-content/mu-plugins/")],
$result,
);
if (
!$this->appcontext->runUser(
"v-copy-fs-file",
[
$plugin_output->file,
$this->getDocRoot("wp-content/mu-plugins/wp-password-bcrypt.php"),
],
$result,
)
) {
throw new \Exception(
"Error installing wp-password-bcrypt file in: " .
$plugin_output->file .
" to:" .
$this->getDocRoot("wp-content/mu-plugins/wp-password-bcrypt.php") .
$result->text,
);
}
$this->appcontext->run(
"v-list-web-domain",
[$this->appcontext->user(), $this->domain, "json"],
$status,
);
$sslEnabled = $status->json[$this->domain]["SSL"] == "no" ? 0 : 1;
$webDomain = ($sslEnabled ? "https://" : "http://") . $this->domain . "/";
$webPort = $sslEnabled ? "443" : "80";
if (substr($options["install_directory"], 0, 1) == "/") {
$options["install_directory"] = substr($options["install_directory"], 1);
}
if (substr($options["install_directory"], -1, 1) == "/") {
$options["install_directory"] = substr(
$options["install_directory"],
0,
strlen($options["install_directory"]) - 1,
);
}
exec(
"/usr/bin/curl --location --post301 --insecure --resolve " .
$this->domain .
":$webPort:" .
$this->appcontext->getWebDomainIp($this->domain) .
" " .
quoteshellarg(
$webDomain . $options["install_directory"] . "/wp-admin/install.php?step=2",
) .
" -d " .
quoteshellarg(
"weblog_title=" .
rawurlencode($options["site_name"]) .
"&user_name=" .
rawurlencode($options["wordpress_account_username"]) .
"&admin_password=" .
rawurlencode($options["wordpress_account_password"]) .
"&admin_password2=" .
rawurlencode($options["wordpress_account_password"]) .
"&admin_email=" .
rawurlencode($options["wordpress_account_email"]),
),
$output,
$return_var,
);
if (
strpos(implode(PHP_EOL, $output), "Error establishing a database connection") !== false
) {
throw new \Exception("Error establishing a database connection");
}
if ($return_var > 0) {
throw new \Exception(implode(PHP_EOL, $output));
}
return $return_var === 0;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

10
web/src/composer.json Normal file
View File

@@ -0,0 +1,10 @@
{
"autoload": {
"psr-4": {
"Hestia\\": "app/"
}
},
"require-dev": {
"filp/whoops": "2.15.3"
}
}

140
web/src/composer.lock generated Normal file
View File

@@ -0,0 +1,140 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "6a75f12aba408723434633776955d2a1",
"packages": [],
"packages-dev": [
{
"name": "filp/whoops",
"version": "2.15.3",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
"reference": "c83e88a30524f9360b11f585f71e6b17313b7187"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/c83e88a30524f9360b11f585f71e6b17313b7187",
"reference": "c83e88a30524f9360b11f585f71e6b17313b7187",
"shasum": ""
},
"require": {
"php": "^5.5.9 || ^7.0 || ^8.0",
"psr/log": "^1.0.1 || ^2.0 || ^3.0"
},
"require-dev": {
"mockery/mockery": "^0.9 || ^1.0",
"phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3",
"symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0"
},
"suggest": {
"symfony/var-dumper": "Pretty print complex values better with var-dumper available",
"whoops/soap": "Formats errors as SOAP responses"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Whoops\\": "src/Whoops/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Filipe Dobreira",
"homepage": "https://github.com/filp",
"role": "Developer"
}
],
"description": "php error handling for cool kids",
"homepage": "https://filp.github.io/whoops/",
"keywords": [
"error",
"exception",
"handling",
"library",
"throwable",
"whoops"
],
"support": {
"issues": "https://github.com/filp/whoops/issues",
"source": "https://github.com/filp/whoops/tree/2.15.3"
},
"funding": [
{
"url": "https://github.com/denis-sokolov",
"type": "github"
}
],
"time": "2023-07-13T12:00:00+00:00"
},
{
"name": "psr/log",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
"shasum": ""
},
"require": {
"php": ">=8.0.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for logging libraries",
"homepage": "https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/3.0.0"
},
"time": "2021-07-14T16:46:02+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.3.0"
}

19
web/src/init.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
$loader = require_once __DIR__ .
DIRECTORY_SEPARATOR .
"vendor" .
DIRECTORY_SEPARATOR .
"autoload.php";
#
# Dev-debugging: Html error handler
# https://github.com/filp/whoops
# install:
# cd $HESTIA/web/src; composer require filp/whoops
#
# $whoops = new \Whoops\Run;
# $whoops->prependHandler(new \Whoops\Handler\PrettyPageHandler);
# $whoops->register();