preparing for re-design using new template-engine for future 0.11.x releases

Signed-off-by: Michael Kaufmann <d00p@froxlor.org>
This commit is contained in:
Michael Kaufmann 2022-02-15 11:18:19 +01:00
parent 1d7d32130a
commit 56a9a71248
No known key found for this signature in database
GPG Key ID: 08A83830520FCECB
39 changed files with 1685 additions and 1397 deletions

1
.gitignore vendored
View File

@ -16,6 +16,7 @@ img/
!templates/Froxlor/
!templates/Sparkle/
!templates/Sparkl2/
!templates/misc/
templates/Sparkle/assets/css/custom.css
vendor/

View File

@ -48,7 +48,11 @@
"monolog/monolog": "^1.24",
"robthree/twofactorauth": "^1.6",
"froxlor/idna-convert-legacy": "^2.1",
"voku/anti-xss": "^4.1"
"voku/anti-xss": "^4.1",
"twig/twig": "^3.3",
"twbs/bootstrap": "^5.1",
"components/jquery": "^3.6",
"components/font-awesome": "^5.15"
},
"require-dev": {
"phpunit/phpunit": "^9",

382
composer.lock generated
View File

@ -4,8 +4,96 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "85144894a3d797cd463442147120549f",
"content-hash": "723dd2a8563bc4727619df8792ab2cb5",
"packages": [
{
"name": "components/font-awesome",
"version": "5.15.4",
"source": {
"type": "git",
"url": "https://github.com/components/font-awesome.git",
"reference": "e6fd09f30f578915cc0cf186b0dd0da54385b6be"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/components/font-awesome/zipball/e6fd09f30f578915cc0cf186b0dd0da54385b6be",
"reference": "e6fd09f30f578915cc0cf186b0dd0da54385b6be",
"shasum": ""
},
"type": "component",
"extra": {
"component": {
"styles": [
"css/all.css"
],
"files": [
"css/all.min.css",
"webfonts/*"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"CC-BY-4.0",
"MIT",
"OFL-1.1"
],
"description": "Font Awesome, the iconic SVG, font, and CSS framework.",
"support": {
"issues": "https://github.com/components/font-awesome/issues",
"source": "https://github.com/components/font-awesome/tree/5.15.4"
},
"time": "2021-08-15T10:31:24+00:00"
},
{
"name": "components/jquery",
"version": "3.6.0",
"source": {
"type": "git",
"url": "https://github.com/components/jquery.git",
"reference": "6cf38ee1fd04b6adf8e7dda161283aa35be818c3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/components/jquery/zipball/6cf38ee1fd04b6adf8e7dda161283aa35be818c3",
"reference": "6cf38ee1fd04b6adf8e7dda161283aa35be818c3",
"shasum": ""
},
"type": "component",
"extra": {
"component": {
"scripts": [
"jquery.js"
],
"files": [
"jquery.min.js",
"jquery.min.map",
"jquery.slim.js",
"jquery.slim.min.js",
"jquery.slim.min.map"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "JS Foundation and other contributors"
}
],
"description": "jQuery JavaScript Library",
"homepage": "http://jquery.com",
"support": {
"forum": "http://forum.jquery.com",
"irc": "irc://irc.freenode.org/jquery",
"issues": "https://github.com/jquery/jquery/issues",
"source": "https://github.com/jquery/jquery",
"wiki": "http://docs.jquery.com/"
},
"time": "2021-03-20T19:13:42+00:00"
},
{
"name": "froxlor/idna-convert-legacy",
"version": "v2.1.2",
@ -346,6 +434,88 @@
],
"time": "2021-10-20T12:19:55+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-10-20T20:35:02+00:00"
},
{
"name": "symfony/polyfill-iconv",
"version": "v1.24.0",
@ -753,6 +923,132 @@
],
"time": "2021-05-27T09:17:38+00:00"
},
{
"name": "twbs/bootstrap",
"version": "v5.1.3",
"source": {
"type": "git",
"url": "https://github.com/twbs/bootstrap.git",
"reference": "1a6fdfae6be09b09eaced8f0e442ca6f7680a61e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twbs/bootstrap/zipball/1a6fdfae6be09b09eaced8f0e442ca6f7680a61e",
"reference": "1a6fdfae6be09b09eaced8f0e442ca6f7680a61e",
"shasum": ""
},
"replace": {
"twitter/bootstrap": "self.version"
},
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mark Otto",
"email": "markdotto@gmail.com"
},
{
"name": "Jacob Thornton",
"email": "jacobthornton@gmail.com"
}
],
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
"homepage": "https://getbootstrap.com/",
"keywords": [
"JS",
"css",
"framework",
"front-end",
"mobile-first",
"responsive",
"sass",
"web"
],
"support": {
"issues": "https://github.com/twbs/bootstrap/issues",
"source": "https://github.com/twbs/bootstrap/tree/v5.1.3"
},
"time": "2021-10-09T06:43:19+00:00"
},
{
"name": "twig/twig",
"version": "v3.3.8",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3"
},
"require-dev": {
"psr/container": "^1.0",
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3-dev"
}
},
"autoload": {
"psr-4": {
"Twig\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Twig Team",
"role": "Contributors"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "https://twig.symfony.com",
"keywords": [
"templating"
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
},
"funding": [
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/twig/twig",
"type": "tidelift"
}
],
"time": "2022-02-04T06:59:48+00:00"
},
{
"name": "voku/anti-xss",
"version": "4.1.35",
@ -3778,88 +4074,6 @@
],
"time": "2021-10-28T13:39:27+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-10-20T20:35:02+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.24.0",
@ -4238,5 +4452,5 @@
"platform-dev": {
"ext-pcntl": "*"
},
"plugin-api-version": "2.2.0"
"plugin-api-version": "2.0.0"
}

View File

@ -15,16 +15,39 @@
* @package Install
*
*/
if (! file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
// define default theme for configurehint, etc.
$_deftheme = 'Froxlor';
// validate correct php version
if (version_compare("7.1.0", PHP_VERSION, ">=")) {
// get hint-template
$vendor_hint = file_get_contents(dirname(__DIR__) . '/templates/Sparkle/misc/vendormissinghint.tpl');
$wrongphp_hint = file_get_contents(dirname(__DIR__) . '/templates/' . $_deftheme . '/misc/phprequirementfailed.html.twig');
// replace values
$wrongphp_hint = str_replace("<FROXLOR_PHPMIN>", "7.1.0", $wrongphp_hint);
$wrongphp_hint = str_replace("<CURRENT_VERSION>", PHP_VERSION, $wrongphp_hint);
$wrongphp_hint = str_replace("<CURRENT_YEAR>", date('Y', time()), $wrongphp_hint);
die($wrongphp_hint);
}
if (!file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
// get hint-template
$vendor_hint = file_get_contents(dirname(__DIR__) . '/templates/' . $_deftheme . '/misc/vendormissinghint.html.twig');
// replace values
$vendor_hint = str_replace("<FROXLOR_INSTALL_DIR>", dirname(__DIR__), $vendor_hint);
$vendor_hint = str_replace("<CURRENT_YEAR>", date('Y', time()), $vendor_hint);
die($vendor_hint);
}
require dirname(__DIR__) . '/vendor/autoload.php';
require __DIR__ . '/lib/class.FroxlorInstall.php';
use Froxlor\UI\Panel\UI;
UI::initTwig(true);
UI::Twig()->addGlobal('install_mode', '1');
UI::Twig()->addGlobal('basehref', '../');
$frxinstall = new FroxlorInstall();
$frxinstall->run();
UI::TwigOutputBuffer();

File diff suppressed because it is too large Load Diff

View File

@ -58,7 +58,7 @@ $lng['install']['mysql_unpriv_user'] = 'Username for the unprivileged MySQL-acco
$lng['install']['mysql_unpriv_pass'] = 'Password for the unprivileged MySQL-account';
$lng['install']['mysql_root_user'] = 'Username for the MySQL-root-account';
$lng['install']['mysql_root_pass'] = 'Password for the MySQL-root-account';
$lng['install']['mysql_ssl_ca_file'] = 'MySQL server certificate file path';
$lng['install']['mysql_ssl_ca_file'] = 'MySQL server certificate file path<br><small>(optional)</small>';
$lng['install']['mysql_ssl_verify_server_certificate'] = 'Verify MySQL TLS certificate';
$lng['install']['admin_account'] = 'Administrator Account';
$lng['install']['admin_user'] = 'Administrator Username';
@ -76,6 +76,7 @@ $lng['install']['lighttpd'] = 'LigHTTPd';
$lng['install']['nginx'] = 'NGINX';
$lng['install']['httpuser'] = 'HTTP username';
$lng['install']['httpgroup'] = 'HTTP groupname';
$lng['install']['use_ssl'] = 'Enable SSL via port 443';
$lng['install']['testing_mysql'] = 'Checking MySQL-root access...';
$lng['install']['testing_mysql_fail'] = 'There seems to be a problem with the database-connection. Cannot continue. Please go back and check your credentials.';
@ -95,6 +96,7 @@ $lng['install']['creating_configfile'] = 'Creating configfile...';
$lng['install']['creating_configfile_temp'] = 'File was saved in %s, please move to ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php';
$lng['install']['creating_configfile_failed'] = 'Could not create ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php, please create it manually with the following content:';
$lng['install']['froxlor_succ_installed'] = 'Froxlor was installed successfully.';
$lng['install']['failed'] = 'failed';
$lng['click_here_to_refresh'] = 'Click here to check again';
$lng['click_here_to_goback'] = 'Click here to go back';

View File

@ -58,7 +58,7 @@ $lng['install']['mysql_unpriv_user'] = 'Benutzername für den unprivilegierten M
$lng['install']['mysql_unpriv_pass'] = 'Passwort für den unprivilegierten MySQL-Account';
$lng['install']['mysql_root_user'] = 'Benutzername für den MySQL-Root-Account';
$lng['install']['mysql_root_pass'] = 'Passwort für den MySQL-Root-Account';
$lng['install']['mysql_ssl_ca_file'] = 'MySQL-Server Zertifikatspfad';
$lng['install']['mysql_ssl_ca_file'] = 'MySQL-Server Zertifikatspfad<br><small>(optional)</small>';
$lng['install']['mysql_ssl_verify_server_certificate'] = 'Validieren des MySQL-Server Zertifikats';
$lng['install']['admin_account'] = 'Admin-Zugang';
$lng['install']['admin_user'] = 'Administrator-Benutzername';
@ -76,6 +76,7 @@ $lng['install']['lighttpd'] = 'LigHTTPd';
$lng['install']['nginx'] = 'NGINX';
$lng['install']['httpuser'] = 'HTTP Username';
$lng['install']['httpgroup'] = 'HTTP Gruppenname';
$lng['install']['use_ssl'] = 'Aktiviere SSL via Port 443';
$lng['install']['testing_mysql'] = 'Teste MySQL-Root Zugang...';
$lng['install']['testing_mysql_fail'] = 'Bei der Verwendung der Datenbank gibt es scheinbar Probleme. Installation kann nicht fortgesetzt werden. Bitte Zugangsdaten prüfen und erneut versuchen.';
@ -95,6 +96,7 @@ $lng['install']['creating_configfile'] = 'Erstelle Konfigurationsdatei...';
$lng['install']['creating_configfile_temp'] = 'Datei wurde in %s gespeichert, bitte nach ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php verschieben.';
$lng['install']['creating_configfile_failed'] = 'Konnte ' . dirname(dirname(__DIR__)) . '/lib/userdata.inc.php nicht erstellen, bitte manuell mit folgendem Inhalt anlegen:';
$lng['install']['froxlor_succ_installed'] = 'Froxlor wurde erfolgreich installiert.';
$lng['install']['failed'] = 'fehlgeschlagen';
$lng['click_here_to_refresh'] = 'Hier klicken, um erneut zu prüfen';
$lng['click_here_to_goback'] = 'Einen Schritt zurück';

View File

@ -1,637 +0,0 @@
@charset "UTF-8";
/* RESET */
html, body, div, ul, ol, li, dl, dt, dd, h1, h2, h3, h4, h5, h6, pre,
form, p, blockquote, fieldset, input {
margin: 0;
padding: 0;
}
h1, h2, h3, h4, h5, h6, pre, code, address, caption, cite, code, em,
strong, th {
font-size: 1em;
font-weight: 400;
font-style: normal;
}
ul, ol {
list-style: none;
}
fieldset, img {
border: none;
}
caption, th {
text-align: left;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
article, aside, details, figcaption, figure, footer, header, hgroup,
menu, nav, section {
display: block;
}
/* TYPE */
html, body {
font: 12px/18px 'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial,
Verdana, sans-serif;
background-color: #f5f5f5;
color: #444;
-webkit-font-smoothing: subpixel-antialiased;
}
body {
margin: 0;
padding: 0;
}
.dark {
background-color: #e9edf0;
border-bottom: 1px solid #d1d5d8;
}
header img {
padding: 10px 0 10px 10px;
}
h1 {
display: none;
}
h2, h3 {
margin: 0 0 1em 0;
padding: 0;
font-weight: bold;
}
h2 {
font-size: 17px;
}
h3 {
font-size: 15px;
}
img {
border: 0;
vertical-align: middle;
}
td a {
text-decoration: none;
}
.bradius {
border-radius: 5px 5px 5px 5px;
box-shadow: rgba(0, 0, 0, 0.34902) 0 1px 3px 0;
}
/* FOOTER */
footer {
clear: both;
text-align: center;
color: #888;
font-size: 10px !important;
margin: 10px 0;
}
footer a, footer a:active, footer a:visited {
color: #888;
}
.install {
background-color: #fff;
margin: 20px auto 12px;
width: 800px;
}
p {
margin: 0 10px !important;
}
.installsec {
margin-top: 10px;
padding: 0;
text-align: left;
}
.installsec table {
width: 100%;
padding: 0 10px;
margin: 15px 0 15px 0;
}
.installsec h2 {
display: block;
border-bottom: 1px solid #d1d5d8;
margin: 0;
padding: 5px 15px 15px 15px;
}
.installsec form {
width: 800px;
margin: 0 auto;
padding: 10px 0 0;
text-align: left;
}
.installsec fieldset {
border: 0;
float: left;
clear: left;
width: 600px;
margin: 0 100px 10px;
padding: 0;
}
.installsec fieldset p, .installsec fieldset h3 {
clear: both;
}
.installsec legend {
display: none;
}
.installsec label {
float: left;
margin-right: 0;
margin-top: 8px;
text-align: left;
}
p.submit {
text-align: right;
padding-right: 46px;
}
.installsec aside {
border-top: 1px solid #d1d5d8;
clear: both;
float: none;
width: auto;
text-align: right;
padding: 10px;
}
.line {
border: 0;
width: 800px;
border-bottom: 1px solid #d1d5d8;
}
.messagewrapper {
width: 650px;
margin: 0 auto;
padding: 120px 0 0;
overflow: hidden;
}
.messagewrapperfull {
width: 100%;
margin: 0 auto;
padding: 0;
overflow: hidden;
}
.overviewsearch {
position: absolute;
top: 155px;
right: 36px;
font-size: 80%;
}
.overviewadd {
padding: 10px;
font-weight: 700;
}
/*
* error message display
*/
.errorcontainer {
background: url(../img/icons/error_big.png) 10px center no-repeat
#ffedef;
border: 1px solid #ffc2ca;
padding: 10px 10px 10px 68px !important;
margin: 10px 0 10px 0 !important;
text-align: left !important;
overflow: hidden;
box-shadow: 0 0 0 black;
}
.errortitle {
font-weight: 700;
color: #c00 !important;
}
.error {
font-weight: 400 !important;
color: #c00 !important;
}
/*
* warning message display
*/
.warningcontainer, .ui-dialog {
background: url(../img/icons/warning_big.png) 10px center no-repeat
#fffecc;
border: 1px solid #f3c37e;
padding: 10px 10px 10px 68px !important;
margin: 10px 0 10px 0 !important;
text-align: left !important;
overflow: hidden;
box-shadow: 0 0 0 black;
}
.ui-dialog {
padding: 10px !important;
}
.warningtitle, .ui-dialog-titlebar {
font-weight: 700;
color: #D57D00;
}
.warning, .ui-dialog-content {
color: #D57D00 !important;
}
/*
* success message display
*/
.successcontainer {
background: url(../img/icons/ok_big.png) 10px center no-repeat #E2F9E3;
border: 1px solid #9C9;
padding: 10px 10px 10px 68px !important;
margin: 10px 0 10px 0 !important;
text-align: left !important;
overflow: hidden;
box-shadow: 0 0 0 black;
}
.successtitle {
font-weight: 700;
color: #060 !important;
}
.success {
font-weight: 400 !important;
}
/*
* neutral/info message display
*/
.neutralcontainer {
background: url(../img/icons/info_big.png) 10px center no-repeat #d2eaf6;
border: 1px solid #b7d8ed;
padding: 10px 10px 10px 68px !important;
margin: 10px 0 10px 0 !important;
text-align: left !important;
overflow: hidden;
box-shadow: 0 0 0 black;
}
.neutraltitle {
font-weight: 700;
color: #3188c1 !important;
}
.neutral {
font-weight: 400 !important;
color: #3188c1 !important;
}
/* std hyperlink */
a, a:active, a:visited {
color: #176fa1;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.infotext {
font-size: 11px;
}
/*
* main container
*/
.main {
margin: 105px 10px 0 240px;
background-color: #fff;
padding: 30px 30px 30px 30px;
min-height: 400px;
}
.noborder {
width: 100%;
border-spacing: 0;
border-collapse: separate;
border: 0;
}
.noborder td {
border: 0;
}
table {
width: 100%;
border-spacing: 0;
border: 1px solid #d1d5d8;
border-collapse: separate;
box-shadow: 0 0 0 black !important;
}
table thead th, table th {
border-top: 1px solid #d1d5d8;
border-bottom: 1px solid #d1d5d8;
height: 25px !important;
padding: 5px 0 5px 8px;
background-color: #e9edf0;
font-weight: bold;
}
table thead:first-child th, table:first-child th {
border-top: none !important;
}
table th {
border-top: 0;
}
th a:hover {
text-decoration: none;
}
th a img {
}
th a:nth-child(odd) img {
position: relative;
top: -5px;
left: 4px;
}
th a:nth-child(even) img {
position: relative;
top: 3px;
left: -7px;
}
table thead:first-child th {
border-top: 0;
}
.disabled td, .disabled td a {
color: #cfcfcf;
}
table tbody td {
border-bottom: 1px dotted #ccc;
}
table tbody tr:last-child td {
border-bottom: 0;
}
.formtable {
width: 100%;
border-spacing: 0;
border: 0;
border-collapse: separate;
margin: 0 0 0;
}
.formtable tbody td {
border: 0;
border-bottom: 1px dotted #ccc;
min-height: 20px;
}
.formtable label {
float: none;
display: block;
padding: 0;
margin: 0;
width: 100%;
text-align: left;
}
td {
padding: 5px 10px;
min-height: 20px;
}
table tfoot td {
height: 25px;
border-top: 1px solid #d1d5d8;
background-color: #f2f8fa;
}
.tfootleft {
text-align: left;
}
.maintitle {
padding-top: 20px;
}
/* input elements */
input {
background: #fff url(../img/text_align_left.png) no-repeat 5px 4px;
color: #333;
padding: 2px 4px 2px 24px;
height: 22px;
border: 1px solid #d9d9d9;
margin-bottom: 5px;
border-radius: 3px;
}
textarea {
background: #fff url(../img/text_align_left.png) no-repeat 5px 4px;
color: #333;
padding: 4px 4px 2px 24px;
border: 1px solid #d9d9d9;
margin-bottom: 5px;
border-radius: 3px;
}
input[type="password"] {
background: #fff url(../img/password.png) no-repeat 5px 4px;
}
/*
* BUTTONS
*/
input[type="button"], input[type="submit"], input[type="reset"] {
margin: 0 5px;
padding: 5px 14px;
outline: 0;
border: 0;
background-color: #eee;
min-width: 80px;
height: 26px;
background-image: none;
}
.loginsec input[type="button"], .loginsec input[type="submit"],
.loginsec input[type="reset"] {
margin: 0 1px;
}
input[type="button"]:hover, input[type="submit"]:hover, input[type="reset"]:hover
{
color: #333;
background-color: #dcdcdc;
}
input[type="button"]:active, input[type="submit"]:active, input[type="reset"]:active
{
-webkit-box-shadow: inset 0 1px 8px rgba(0, 0, 0, 0.25);
-moz-box-shadow: inset 0 1px 8px rgba(0, 0, 0, 0.25);
box-shadow: inset 0 1px 8px rgba(0, 0, 0, 0.25);
color: white !important;
}
input[type="submit"], input[class="yesbutton"] {
color: white;
background-color: #35aa47;
}
input[type="submit"]:hover, input[class="yesbutton"]:hover {
color: white;
background-color: #1d943b;
}
input[class="submit"]:active, input[class="yesbutton"]:active {
background-color: #35aa47;
}
input[class="nobutton"], input[type="reset"] {
color: white;
background-color: #d84a38;
}
input[class="nobutton"]:hover, input[type="reset"]:hover {
color: white;
background-color: #c53727;
}
input[class="nobutton"]:active, input[type="reset"]:active {
background-color: #dd4b39;
}
input[type="checkbox"] {
background: #dae7ee;
padding: 0;
margin: 0 5px 0 0;
vertical-align: middle;
height: 26px;
}
input[type="radio"] {
vertical-align: middle;
margin: 0 10px 0 10px;
height: 22px;
}
select {
background: #fff;
padding: 4px;
color: #333;
border: 1px solid #d9d9d9;
margin-bottom: 5px;
min-width: 100px;
}
select.dropdown {
padding: 2px 4px 2px 24px;
height: 26px;
border: 1px solid #d9d9d9;
margin-bottom: 5px;
border-radius: 3px;
background: url(../../../../templates/Sparkle/assets/img/icons/down.png)
no-repeat 9px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
.maintable {
width: 90%;
}
.update_progess {
padding: 2em;
text-align: left;
}
.preconfig {
text-align: left;
margin-top: 20px;
margin-bottom: 5px;
margin-right: 15px;
margin-left: 15px;
}
.preconfigitem {
padding: .15em;
border-bottom: 1px solid #ccc;
}
.preconfdesc {
display: block;
margin-bottom: .5em;
font-size: 120%;
}
.installprogress {
width: 100%;
background-color: #e4e4e4;
height: 5px;
border-bottom: 1px solid #d1d5d8;
}
.installprogress .bar {
background-color: #35aa47;
height: 5px;
}
.red {
color: #ff0000;
}
.green {
color: green;
}
.orange {
color: orange;
}
.blue {
color: blue;
}
.install-block {
width: 65%;
}
.install-step {
width: 250px;
}
.install-h3 {
text-align: center;
}
.install-text {
margin: 20px 20px 0 !important;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 B

View File

@ -1,10 +0,0 @@
<p class="install-text">{$this->_lng['install']['title']}</p>
<form action="{$formaction}" method="get">
<fieldset>
{$formdata}
<p class="submit">
<input type="hidden" name="check" value="1" />
<input type="submit" name="chooselang" value="{$this->_lng['install']['btn_go']}" />
</p>
</fieldset>
</form>

View File

@ -1,13 +0,0 @@
<p class="install-text">{$this->_lng['install']['welcometext']}</p>
<form action="{$formaction}" method="post">
<hr class="line">
<fieldset>
{$formdata}
</fieldset>
<aside>
<input type="hidden" name="check" value="1" />
<input type="hidden" name="language" value="{$language}" />
<input type="hidden" name="installstep" value="1" />
<input class="bottom" type="submit" name="submitbutton" value="{$this->_lng['click_here_to_continue']}" />
</aside>
</form>

View File

@ -1,4 +0,0 @@
<p>
<label for="{$fieldname}" class="install-block {$style}">{$fieldlabel}:</label>
<input type="{$type}" name="{$fieldname}" id="{$fieldname}" value="{$fieldvalue}" {$required} />
</p>

View File

@ -1,4 +0,0 @@
<p>
<label for="{$fieldname}" class="install-block {$style}">{$groupname} {$fieldlabel}:</label>
<input type="radio" name="{$groupname}" id="{$fieldname}" value="{$fieldname}" {$checked} /><span>{$fieldlabel}</span>
</p>

View File

@ -1,6 +0,0 @@
<p>
<label for="{$fieldname}" class="install-block {$style}">{$fieldlabel}:</label>
<select name="{$fieldname}" id="{$fieldname}" class="dropdown">
{$options}
</select>
</p>

View File

@ -1,4 +0,0 @@
<p>
<label for="{$fieldname}" class="install-block {$style}">{$fieldlabel}:</label>
<input type="checkbox" name="{$fieldname}" id="{$fieldname}" value="1" {$checked} />
</p>

View File

@ -1,2 +0,0 @@
<br />
<h3>{$section}</h3>

View File

@ -1,7 +0,0 @@
</div>
<footer>
<span> Froxlor &copy; 2009-{$current_year} by <a href="http://www.froxlor.org/" rel="external">the Froxlor Team</a>
</span>
</footer>
</body>
</html>

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="Default-Style" content="text/css" />
<!--[if lt IE 9]><script src="../js/html5shiv.min.js"></script><![endif]-->
<link href="templates/assets/css/install.css" rel="stylesheet" type="text/css" />
<!--[if IE]><link rel="stylesheet" href="../templates/{$theme}/css/main_ie.css" type="text/css" /><![endif]-->
<link href="templates/assets/img/favicon.ico" rel="icon" type="image/x-icon" />
<title>Froxlor Server Management Panel - Installation</title>
</head>
<body>
<div class="installsec">

View File

@ -1,20 +0,0 @@
<form action="{$formaction}" method="get">
<fieldset>
<legend>{$this->_lng['install']['lngtitle']}</legend>
<table class="noborder">
<tr>
<td>
<label for="language">{$this->_lng['install']['language']}:</label>
</td>
<td align="right">
<select name="language" id="language" class="dropdown">
{$language_options}
</select>
<input type="hidden" name="check" value="1" />
<input type="submit" name="chooselang" value="{$this->_lng['install']['lngbtn_go']}" />
</td>
</tr>
</table>
</fieldset>
</form>
<hr class="line">

View File

@ -1,11 +0,0 @@
<article class="install bradius">
<header class="dark">
<img src="../templates/{$theme}/assets/img/logo.png" alt="Froxlor Server Management Panel" />
</header>
<section class="installsec">
<h2>{$pagetitle}</h2>
{$pagecontent}
{$pagenavigation}
</section>
</article>

View File

@ -1,4 +0,0 @@
<h3 class="install-h3 {$msgcolor}">{$message}</h3>
<aside>
<a href="{$link}">{$linktext}</a>
</aside>

View File

@ -1,5 +0,0 @@
<tr>
<td class="main_field_name">
<p>{$escpduserdata}</p>
</td>
</tr>

View File

@ -47,7 +47,7 @@ class PhpHelper
* @param string $charset
* See php documentation about this
*
* @return array The array with htmlentitie'd strings
* @return array|string The string or an array with htmlentities converted strings
* @author Florian Lippert <flo@syscp.org>
*/
public static function htmlentitiesArray($subject, $fields = '', $quote_style = ENT_QUOTES, $charset = 'UTF-8')
@ -64,7 +64,7 @@ class PhpHelper
}
}
} else {
$subject = htmlentities($subject, $quote_style, $charset);
$subject = empty($subject) ? "" : htmlentities($subject, $quote_style, $charset);
}
return $subject;
@ -120,22 +120,30 @@ class PhpHelper
}
if (! isset($_SERVER['SHELL']) || (isset($_SERVER['SHELL']) && $_SERVER['SHELL'] == '')) {
global $theme;
// fallback
if (empty($theme)) {
$theme = "Sparkle";
}
// prevent possible file-path-disclosure
$errfile = str_replace(\Froxlor\Froxlor::getInstallDir(), "", $errfile);
// if we're not on the shell, output a nicer error-message
$err_hint = file_get_contents(\Froxlor\Froxlor::getInstallDir() . '/templates/' . $theme . '/misc/phperrornice.tpl');
// replace values
$err_hint = str_replace("<TEXT>", '#' . $errno . ' ' . $errstr, $err_hint);
$err_hint = str_replace("<DEBUG>", $errfile . ':' . $errline, $err_hint);
// show
echo $err_hint;
// build alert
$type = 'danger';
if ($errno == E_NOTICE || $errno == E_DEPRECATED || $errno == E_STRICT) {
$type = 'info';
} elseif ($errno = E_WARNING) {
$type = 'warning';
}
$err_display = '<div class="alert alert-'.$type.' my-1" role="alert">';
$err_display .= '<strong>#' . $errno . ' ' . $errstr . '</strong><br>';
$err_display .= $errfile . ':' . $errline;
// later depended on whether to show or now
$err_display .= '<br><p><pre>';
$debug = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
foreach ($debug as $dline) {
$err_display .= $dline['function'] . '() called at [' . str_replace(\Froxlor\Froxlor::getInstallDir(), '', $dline['file']) . ':' . $dline['line'] . ']<br>';
}
$err_display .= '</pre></p>';
// end later
$err_display .= '</div>';
// check for more existing errors
$errors = isset(\Froxlor\UI\Panel\UI::Twig()->getGlobals()['global_errors']) ? \Froxlor\UI\Panel\UI::Twig()->getGlobals()['global_errors'] : "";
\Froxlor\UI\Panel\UI::Twig()->addGlobal('global_errors', $errors . $err_display);
// return true to ignore php standard error-handler
return true;
}
@ -144,6 +152,28 @@ class PhpHelper
return false;
}
public static function phpExceptionHandler(\Exception $exception)
{
if (! isset($_SERVER['SHELL']) || (isset($_SERVER['SHELL']) && $_SERVER['SHELL'] == '')) {
$err_display = '<div class="alert alert-danger my-1" role="alert">';
$err_display .= '<strong>#' . $exception->getCode() . ' ' . $exception->getMessage() . '</strong><br>';
// later depended on whether to show or now
$err_display .= '<br><p><pre>';
$debug = $exception->getTrace();
foreach ($debug as $dline) {
$err_display .= $dline['function'] . '() called at [' . str_replace(\Froxlor\Froxlor::getInstallDir(), '', $dline['file']) . ':' . $dline['line'] . ']<br>';
}
$err_display .= '</pre></p>';
// end later
$err_display .= '</div>';
// check for more existing errors
$errors = isset(\Froxlor\UI\Panel\UI::Twig()->getGlobals()['global_errors']) ? \Froxlor\UI\Panel\UI::Twig()->getGlobals()['global_errors'] : "";
\Froxlor\UI\Panel\UI::Twig()->addGlobal('global_errors', $errors . $err_display);
// return true to ignore php standard error-handler
return true;
}
}
public static function loadConfigArrayDir()
{
// Workaround until we use gettext

View File

@ -0,0 +1,54 @@
<?php
namespace Froxlor\UI\Panel;
class CustomReflection extends \Twig\Extension\AbstractExtension
{
/**
*
* {@inheritdoc}
*/
public function getFunctions()
{
return array(
new \Twig\TwigFunction('call_static', [
$this,
'callStaticMethod'
]),
new \Twig\TwigFunction('get_static', [
$this,
'getStaticProperty'
])
);
}
public function callStaticMethod($class, $method, array $args = [])
{
$refl = new \reflectionClass($class);
// Check that method is static AND public
if ($refl->hasMethod($method) && $refl->getMethod($method)->isStatic() && $refl->getMethod($method)->isPublic()) {
return call_user_func_array($class . '::' . $method, $args);
}
throw new \RuntimeException(sprintf('Invalid static method call for class %s and method %s', $class, $method));
}
public function getStaticProperty($class, $property)
{
$refl = new \reflectionClass($class);
// Check that property is static AND public
if ($refl->hasProperty($property) && $refl->getProperty($property)->isStatic() && $refl->getProperty($property)->isPublic()) {
return $refl->getProperty($property)->getValue();
}
throw new \RuntimeException(sprintf('Invalid static property get for class %s and property %s', $class, $property));
}
/**
*
* {@inheritdoc}
*/
public function getName()
{
return 'customreflection';
}
}

View File

@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace Froxlor\UI\Panel;
class FroxlorTwig extends \Twig\Extension\AbstractExtension
{
public function getFilters()
{
return array(
new \Twig\TwigFilter('formatBytes', array(
$this,
'formatBytesFilter'
)),
new \Twig\TwigFilter('formatIP', array(
$this,
'formatIPFilter'
)),
new \Twig\TwigFilter('idnDecode', array(
$this,
'idnDecodeFilter'
))
);
}
public function getTests()
{
return array(
new \Twig\TwigTest('numeric', function ($value) {
return is_numeric($value);
})
);
}
public function getFunctions()
{
return array(
new \Twig\TwigFunction('get_setting', [
$this,
'getSetting'
]),
new \Twig\TwigFunction('lng', [
$this,
'getLang'
])
);
}
public function formatBytesFilter($size, $suffix = "B", $factor = 1)
{
$size = $size * $factor;
$units = array(
'',
'K',
'M',
'G',
'T',
'P',
'E',
'Z',
'Y'
);
$power = $size > 0 ? floor(log($size, 1024)) : 0;
if ($power < 0) {
$size = 0.00;
$power = 0;
}
return number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $units[$power] . $suffix;
}
public function formatIPFilter($addr)
{
return inet_ntop(inet_pton($addr));
}
public function idnDecodeFilter($entity)
{
$idna_convert = new \Froxlor\Idna\IdnaWrapper();
return $idna_convert->decode($entity);
}
public function getSetting($setting = null)
{
return \Froxlor\Settings::Get($setting);
}
public function getLang($identifier = null)
{
return \Froxlor\UI\Panel\UI::getLng($identifier);
}
/**
*
* {@inheritdoc}
*/
public function getName()
{
return 'froxlortwig';
}
}

274
lib/Froxlor/UI/Panel/UI.php Normal file
View File

@ -0,0 +1,274 @@
<?php
declare(strict_types=1);
namespace Froxlor\UI\Panel;
class UI
{
/**
* twig object
*
* @var \Twig\Environment
*/
private static $twig = null;
/**
* twig buffer
*
* @var array
*/
private static $twigbuf = array();
/**
* language strigs array
*
* @var array
*/
private static $lng = array();
/**
* default fallback theme
*
* @var string
*/
private static $default_theme = 'Sparkle2';
private static $install_mode = false;
/**
* send various security related headers
*/
public static function sendHeaders()
{
header("Content-Type: text/html; charset=UTF-8");
// prevent Froxlor pages from being cached
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Pragma: no-cache");
header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', time()));
header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time()));
// Prevent inline - JS to be executed (i.e. XSS) in browsers which support this,
// Inline-JS is no longer allowed and used
// See: http://people.mozilla.org/~bsterne/content-security-policy/index.html
// New stuff see: https://www.owasp.org/index.php/List_of_useful_HTTP_headers and https://www.owasp.org/index.php/Content_Security_Policy
$csp_content = "default-src 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline';";
header("Content-Security-Policy: " . $csp_content);
header("X-Content-Security-Policy: " . $csp_content);
header("X-WebKit-CSP: " . $csp_content);
header("X-XSS-Protection: 1; mode=block");
// Don't allow to load Froxlor in an iframe to prevent i.e. clickjacking
header("X-Frame-Options: DENY");
// Internet Explorer shall not guess the Content-Type, see:
// http://blogs.msdn.com/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx
header("X-Content-Type-Options: nosniff");
// ensure that default timezone is set
if (function_exists("date_default_timezone_set") && function_exists("date_default_timezone_get")) {
@date_default_timezone_set(@date_default_timezone_get());
}
self::sendSslHeaders();
}
private static function sendSslHeaders()
{
/**
* If Froxlor was called via HTTPS -> enforce it for the next time by settings HSTS header according to settings
*/
if (isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off')) {
$maxage = \Froxlor\Settings::Get('system.hsts_maxage');
if (empty($maxage)) {
$maxage = 0;
}
$hsts_header = "Strict-Transport-Security: max-age=" . $maxage;
if (\Froxlor\Settings::Get('system.hsts_incsub') == '1') {
$hsts_header .= "; includeSubDomains";
}
if (\Froxlor\Settings::Get('system.hsts_preload') == '1') {
$hsts_header .= "; preload";
}
header($hsts_header);
}
}
/**
* initialize Twig template engine
*/
public static function initTwig(bool $install_mode = false)
{
self::$install_mode = $install_mode;
// init twig template engine
$loader = new \Twig\Loader\FilesystemLoader(\Froxlor\Froxlor::getInstallDir() . '/templates/');
self::$twig = new \Twig\Environment($loader, array(
'debug' => true,
'cache' => \Froxlor\Froxlor::getInstallDir() . '/cache',
'auto_reload' => true
));
self::$twig->addExtension(new \Twig\Extension\DebugExtension());
self::$twig->addExtension(new CustomReflection());
self::$twig->addExtension(new FroxlorTwig());
// empty buffer
self::$twigbuf = [];
}
/**
* twig wrapper
*
* @return \Twig\Environment
*/
public static function Twig()
{
return self::$twig;
}
/**
* wrapper for twig's "render" function to buffer the output
*
* @see \Twig\Environment::render()
*/
public static function TwigBuffer($name, array $context = [])
{
self::$twigbuf[] = [
self::getTheme() . '/' . $name => $context
];
}
public static function getTheme()
{
// fallback
$theme = self::$default_theme;
if (!self::$install_mode) {
// system default
if (\Froxlor\Froxlor::DBVERSION <= 201909150) {
\Froxlor\Settings::Set('panel.default_theme', 'Sparkle2');
}
$theme = (\Froxlor\Settings::Get('panel.default_theme') !== null) ? \Froxlor\Settings::Get('panel.default_theme') : $theme;
// customer theme
/*
if (\Froxlor\CurrentUser::hasSession() && \Froxlor\CurrentUser::getField('theme') != $theme) {
$theme = \Froxlor\CurrentUser::getField('theme');
}
*/
}
if (!file_exists(\Froxlor\Froxlor::getInstallDir() . '/templates/' . $theme)) {
\Froxlor\PhpHelper::phpErrHandler(E_USER_WARNING, "Theme '" . $theme . "' could not be found.", __FILE__, __LINE__, null);
$theme = self::$default_theme;
}
return $theme;
}
/**
* echo output buffer and empty buffer-content
*/
public static function TwigOutputBuffer()
{
$output = "";
foreach (self::$twigbuf as $buf) {
foreach ($buf as $name => $context) {
try {
$output .= self::$twig->render($name, $context);
} catch (\Exception $e) {
// whoops, template error
$errtpl = 'alert_nosession.html.twig';
/*
if (\Froxlor\CurrentUser::hasSession()) {
$errtpl = 'alert.html.twig';
}
*/
$edata = array(
'type' => "danger",
'heading' => "Template error",
'alert_msg' => $e->getMessage(),
'alert_info' => $e->getTraceAsString()
);
try {
// try with user theme if set
$output .= self::$twig->render(self::getTheme() . '/misc/' . $errtpl, $edata);
} catch (\Exception $e) {
// try with default theme if different from user theme
if (self::getTheme() != self::$default_theme) {
$output .= self::$twig->render(self::$default_theme . '/misc/' . $errtpl, $edata);
} else {
throw $e;
}
}
}
}
}
echo $output;
// empty buffer
self::$twigbuf = [];
}
public static function setLng($lng = array())
{
self::$lng = $lng;
}
public static function getLng($identifier, $context = null)
{
$id = explode(".", $identifier);
if (is_null($context)) {
$id_first = array_shift($id);
if (!isset(self::$lng[$id_first])) {
return null;
}
if (empty($id)) {
return self::$lng[$id_first];
} else {
return self::getLng(implode(".", $id), self::$lng[$id_first]);
}
} else {
$id_first = array_shift($id);
if (empty($id)) {
return isset($context[$id_first]) ? $context[$id_first] : null;
} else {
return self::getLng(implode(".", $id), $context[$id_first]);
}
}
}
/**
* returns an array of available themes
*
* @return array
*/
public static function getThemes()
{
$themespath = \Froxlor\FileDir::makeCorrectDir(\Froxlor\Froxlor::getInstallDir() . '/templates/');
$themes_available = array();
if (is_dir($themespath)) {
$its = new \DirectoryIterator($themespath);
foreach ($its as $it) {
if ($it->isDir() && $it->getFilename() != '.' && $it->getFilename() != '..' && $it->getFilename() != 'misc') {
$theme = $themespath . $it->getFilename();
if (file_exists($theme . '/config.json')) {
$themeconfig = json_decode(file_get_contents($theme . '/config.json'), true);
if (array_key_exists('variants', $themeconfig) && is_array($themeconfig['variants'])) {
foreach ($themeconfig['variants'] as $variant => $data) {
if ($variant == "default") {
$themes_available[$it->getFilename()] = $it->getFilename();
} elseif (array_key_exists('description', $data)) {
$themes_available[$it->getFilename() . '_' . $variant] = $data['description'];
} else {
$themes_available[$it->getFilename() . '_' . $variant] = $it->getFilename() . ' (' . $variant . ')';
}
}
} else {
$themes_available[$it->getFilename()] = $it->getFilename();
}
}
}
}
}
return $themes_available;
}
}

View File

@ -18,22 +18,22 @@
*/
// define default theme for configurehint, etc.
$_deftheme = 'Sparkle';
$_deftheme = 'Froxlor';
// validate correct php version
if (version_compare("7.1.0", PHP_VERSION, ">=")) {
// get hint-template
$vendor_hint = file_get_contents(dirname(__DIR__) . '/templates/' . $_deftheme . '/misc/phprequirementfailed.tpl');
$wrongphp_hint = file_get_contents(dirname(__DIR__) . '/templates/' . $_deftheme . '/misc/phprequirementfailed.html.twig');
// replace values
$vendor_hint = str_replace("<FROXLOR_PHPMIN>", "7.1.0", $vendor_hint);
$vendor_hint = str_replace("<CURRENT_VERSION>", PHP_VERSION, $vendor_hint);
$vendor_hint = str_replace("<CURRENT_YEAR>", date('Y', time()), $vendor_hint);
die($vendor_hint);
$wrongphp_hint = str_replace("<FROXLOR_PHPMIN>", "7.1.0", $wrongphp_hint);
$wrongphp_hint = str_replace("<CURRENT_VERSION>", PHP_VERSION, $wrongphp_hint);
$wrongphp_hint = str_replace("<CURRENT_YEAR>", date('Y', time()), $wrongphp_hint);
die($wrongphp_hint);
}
if (! file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
if (!file_exists(dirname(__DIR__) . '/vendor/autoload.php')) {
// get hint-template
$vendor_hint = file_get_contents(dirname(__DIR__) . '/templates/' . $_deftheme . '/misc/vendormissinghint.tpl');
$vendor_hint = file_get_contents(dirname(__DIR__) . '/templates/' . $_deftheme . '/misc/vendormissinghint.html.twig');
// replace values
$vendor_hint = str_replace("<FROXLOR_INSTALL_DIR>", dirname(__DIR__), $vendor_hint);
$vendor_hint = str_replace("<CURRENT_YEAR>", date('Y', time()), $vendor_hint);
@ -46,37 +46,11 @@ use Froxlor\Database\Database;
use Froxlor\Settings;
use voku\helper\AntiXSS;
use Froxlor\PhpHelper;
use Froxlor\UI\Panel\UI;
header("Content-Type: text/html; charset=UTF-8");
UI::sendHeaders();
UI::initTwig();
// prevent Froxlor pages from being cached
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Pragma: no-cache");
header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', time()));
header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time()));
// Prevent inline - JS to be executed (i.e. XSS) in browsers which support this,
// Inline-JS is no longer allowed and used
// See: http://people.mozilla.org/~bsterne/content-security-policy/index.html
// New stuff see: https://www.owasp.org/index.php/List_of_useful_HTTP_headers and https://www.owasp.org/index.php/Content_Security_Policy
$csp_content = "default-src 'self'; script-src 'self'; connect-src 'self'; img-src 'self' data:; style-src 'self';";
header("Content-Security-Policy: " . $csp_content);
header("X-Content-Security-Policy: " . $csp_content);
header("X-WebKit-CSP: " . $csp_content);
header("X-XSS-Protection: 1; mode=block");
// Don't allow to load Froxlor in an iframe to prevent i.e. clickjacking
header("X-Frame-Options: DENY");
// Internet Explorer shall not guess the Content-Type, see:
// http://blogs.msdn.com/ie/archive/2008/07/02/ie8-security-part-v-comprehensive-protection.aspx
header("X-Content-Type-Options: nosniff");
// ensure that default timezone is set
if (function_exists("date_default_timezone_set") && function_exists("date_default_timezone_get")) {
@date_default_timezone_set(@date_default_timezone_get());
}
/**
* Register Globals Security Fix
@ -106,81 +80,51 @@ unset($key);
$filename = htmlentities(basename($_SERVER['SCRIPT_NAME']));
// check whether the userdata file exists
if (! file_exists(\Froxlor\Froxlor::getInstallDir() . '/lib/userdata.inc.php')) {
$config_hint = file_get_contents(\Froxlor\Froxlor::getInstallDir() . '/templates/' . $_deftheme . '/misc/configurehint.tpl');
$config_hint = str_replace("<CURRENT_YEAR>", date('Y', time()), $config_hint);
die($config_hint);
if (!file_exists(\Froxlor\Froxlor::getInstallDir() . '/lib/userdata.inc.php')) {
UI::Twig()->addGlobal('install_mode', '1');
echo UI::Twig()->render($_deftheme . '/misc/configurehint.html.twig');
die();
}
// check whether we can read the userdata file
if (! is_readable(\Froxlor\Froxlor::getInstallDir() . '/lib/userdata.inc.php')) {
if (!is_readable(\Froxlor\Froxlor::getInstallDir() . '/lib/userdata.inc.php')) {
// get possible owner
$posixusername = posix_getpwuid(posix_getuid());
$posixgroup = posix_getgrgid(posix_getgid());
// get hint-template
$owner_hint = file_get_contents(\Froxlor\Froxlor::getInstallDir() . '/templates/' . $_deftheme . '/misc/ownershiphint.tpl');
// replace values
$owner_hint = str_replace("<USER>", $posixusername['name'], $owner_hint);
$owner_hint = str_replace("<GROUP>", $posixgroup['name'], $owner_hint);
$owner_hint = str_replace("<FROXLOR_INSTALL_DIR>", \Froxlor\Froxlor::getInstallDir(), $owner_hint);
$owner_hint = str_replace("<CURRENT_YEAR>", date('Y', time()), $owner_hint);
// show
die($owner_hint);
UI::Twig()->addGlobal('install_mode', '1');
echo UI::Twig()->render($_deftheme . '/misc/ownershiphint.html.twig', [
'user' => $posixusername['name'],
'group' => $posixgroup['name'],
'installdir' => \Froxlor\Froxlor::getInstallDir()
]);
die();
}
/**
* Includes the Usersettings eg.
* MySQL-Username/Passwort etc.
*/
// include MySQL-Username/Passwort etc.
require \Froxlor\Froxlor::getInstallDir() . '/lib/userdata.inc.php';
if (! isset($sql) || ! is_array($sql)) {
$config_hint = file_get_contents(\Froxlor\Froxlor::getInstallDir() . '/templates/' . $_deftheme . '/misc/configurehint.tpl');
$config_hint = str_replace("<CURRENT_YEAR>", date('Y', time()), $config_hint);
die($config_hint);
if (!isset($sql) || !is_array($sql)) {
UI::Twig()->addGlobal('install_mode', '1');
echo UI::Twig()->render($_deftheme . '/misc/configurehint.html.twig');
die();
}
/**
* Includes the Functions
*/
@set_error_handler(array(
// set error-handler
@set_error_handler([
'\\Froxlor\\PhpHelper',
'phpErrHandler'
));
]);
@set_exception_handler([
'\\Froxlor\\PhpHelper',
'phpExceptionHandler'
]);
/**
* Includes the MySQL-Tabledefinitions etc.
*/
// include MySQL-tabledefinitions
require \Froxlor\Froxlor::getInstallDir() . '/lib/tables.inc.php';
/**
* Create a new idna converter
*/
// create a new idna converter
$idna_convert = new \Froxlor\Idna\IdnaWrapper();
/**
* If Froxlor was called via HTTPS -> enforce it for the next time by settings HSTS header according to settings
*/
$is_ssl = false;
if (isset($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off')) {
$is_ssl = true;
$maxage = Settings::Get('system.hsts_maxage');
if (empty($maxage)) {
$maxage = 0;
}
$hsts_header = "Strict-Transport-Security: max-age=" . $maxage;
if (Settings::Get('system.hsts_incsub') == '1') {
$hsts_header .= "; includeSubDomains";
}
if (Settings::Get('system.hsts_preload') == '1') {
$hsts_header .= "; preload";
}
header($hsts_header);
}
/**
* SESSION MANAGEMENT
*/
// SESSION MANAGEMENT
$remote_addr = $_SERVER['REMOTE_ADDR'];
if (empty($_SERVER['HTTP_USER_AGENT'])) {
@ -248,7 +192,7 @@ if (isset($s) && $s != "" && $nosession != 1) {
$userinfo_stmt = Database::prepare($query);
$userinfo = Database::pexecute_first($userinfo_stmt, $userinfo_data);
if ($userinfo && (($userinfo['adminsession'] == '1' && AREA == 'admin' && isset($userinfo['adminid'])) || ($userinfo['adminsession'] == '0' && (AREA == 'customer' || AREA == 'login') && isset($userinfo['customerid']))) && (! isset($userinfo['deactivated']) || $userinfo['deactivated'] != '1')) {
if ($userinfo && (($userinfo['adminsession'] == '1' && AREA == 'admin' && isset($userinfo['adminid'])) || ($userinfo['adminsession'] == '0' && (AREA == 'customer' || AREA == 'login') && isset($userinfo['customerid']))) && (!isset($userinfo['deactivated']) || $userinfo['deactivated'] != '1')) {
$upd_stmt = Database::prepare("
UPDATE `" . TABLE_PANEL_SESSIONS . "` SET
`lastactivity` = :lastactive
@ -303,14 +247,14 @@ if (isset($userinfo['language']) && isset($languages[$userinfo['language']])) {
// default: use language from session, #277
$language = $userinfo['language'];
} else {
if (! isset($userinfo['def_language']) || ! isset($languages[$userinfo['def_language']])) // this will always evaluat true, since it is the above statement inverted. @todo remove
if (!isset($userinfo['def_language']) || !isset($languages[$userinfo['def_language']])) // this will always evaluat true, since it is the above statement inverted. @todo remove
{
if (isset($_GET['language']) && isset($languages[$_GET['language']])) {
$language = $_GET['language'];
} else {
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
for ($i = 0; $i < count($accept_langs); $i ++) {
for ($i = 0; $i < count($accept_langs); $i++) {
// this only works for most common languages. some (uncommon) languages have a 3 letter iso-code.
// to be able to use these also, we would have to depend on the intl extension for php (using Locale::lookup or similar)
// as long as froxlor does not support any of these languages, we can leave it like that.
@ -322,7 +266,7 @@ if (isset($userinfo['language']) && isset($languages[$userinfo['language']])) {
unset($iso);
// if HTTP_ACCEPT_LANGUAGES has no valid langs, use default (very unlikely)
if (! strlen($language) > 0) {
if (!strlen($language) > 0) {
$language = Settings::Get('panel.standardlanguage');
}
}
@ -370,7 +314,7 @@ if (preg_match("/([a-z0-9\.\-]+)_([a-z0-9\.\-]+)/i", $theme, $matches)) {
}
// check for existence of the theme
if (! file_exists('templates/' . $theme . '/config.json')) {
if (!file_exists('templates/' . $theme . '/config.json')) {
// Fallback
$theme = $_deftheme;
}
@ -378,7 +322,7 @@ if (! file_exists('templates/' . $theme . '/config.json')) {
$_themeoptions = json_decode(file_get_contents('templates/' . $theme . '/config.json'), true);
// check for existence of variant in theme
if (! array_key_exists('variants', $_themeoptions) || ! array_key_exists($themevariant, $_themeoptions['variants'])) {
if (!array_key_exists('variants', $_themeoptions) || !array_key_exists($themevariant, $_themeoptions['variants'])) {
$themevariant = "default";
}
@ -494,25 +438,28 @@ if (array_key_exists('css', $_themeoptions['variants'][$themevariant]) && is_arr
}
}
}
eval("\$header = \"" . \Froxlor\UI\Template::getTemplate('header', '1') . "\";");
$current_year = date('Y', time());
UI::Twig()->addGlobal('theme_js', $js);
UI::Twig()->addGlobal('theme_css', $css);
unset($js);
unset($css);
/**
* @TODO
*
$panel_imprint_url = Settings::Get('panel.imprint_url');
if (!empty($panel_imprint_url) && strtolower(substr($panel_imprint_url, 0, 4)) != 'http') {
$panel_imprint_url = 'https://'.$panel_imprint_url;
$panel_imprint_url = 'https://' . $panel_imprint_url;
}
$panel_terms_url = Settings::Get('panel.terms_url');
if (!empty($panel_terms_url) && strtolower(substr($panel_terms_url, 0, 4)) != 'http') {
$panel_terms_url = 'https://'.$panel_terms_url;
$panel_terms_url = 'https://' . $panel_terms_url;
}
$panel_privacy_url = Settings::Get('panel.privacy_url');
if (!empty($panel_privacy_url) && strtolower(substr($panel_privacy_url, 0, 4)) != 'http') {
$panel_privacy_url = 'https://'.$panel_privacy_url;
$panel_privacy_url = 'https://' . $panel_privacy_url;
}
eval("\$footer = \"" . \Froxlor\UI\Template::getTemplate('footer', '1') . "\";");
unset($js);
unset($css);
*/
if (isset($_POST['action'])) {
$action = trim(strip_tags($_POST['action']));

View File

@ -0,0 +1,12 @@
@charset "UTF-8";
footer {
color: #888;
font-size: 0.75rem !important;
}
footer a,
footer a:active,
footer a:visited {
color: #888;
}

View File

@ -0,0 +1,77 @@
<!DOCTYPE html>
<html>
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="noindex, nofollow, noarchive"/>
<meta
name="GOOGLEBOT" content="nosnippet"/>
<!-- Bootstrap CSS -->
<link
rel="stylesheet" href="{{ basehref|default("") }}vendor/twbs/bootstrap/dist/css/bootstrap.min.css">
<!-- FontAwesome CSS -->
<link
rel="stylesheet" href="{{ basehref|default("") }}vendor/components/font-awesome/css/all.min.css">
<!-- our css -->
<link href="{{ basehref|default("") }}templates/Froxlor/assets/css/main.css" rel="stylesheet">
{{ theme_css|raw }}
{% block custom_css %}{% endblock %}
<!-- Bootstrap and necessary plugins -->
<script src="{{ basehref|default("") }}vendor/components/jquery/jquery.min.js"></script>
<script src="{{ basehref|default("") }}vendor/twbs/bootstrap/dist/js/bootstrap.min.js"></script>
{{ theme_js|raw }}
{% block custom_js %}{% endblock %}
<title>Froxlor
{% if page_title %}
|
{{ page_title }}
{% endif %}
</title>
</head>
<body>
<div class="container-fluid"> {% block navigation %}{% endblock %}
{{ global_errors|raw }}
{% block body %}{% endblock %}
<footer class="pt-5 pb-5 text-center">
<span><img src="{{ basehref|default("") }}templates/Froxlor/assets/img/logo_grey.png" alt="Froxlor"/>
{% if install_mode is not defined %}
{% if (get_setting('admin.show_version_login') ==
'1' and module == 'Login') or (module != 'Login'
and get_setting('admin.show_version_footer') == '1') %}{% endif %}
{{ call_static('\\Froxlor\\Froxlor', 'getFullVersion') }}
{% endif %}
&copy; 2009-{{ "now"|date("Y") }}
by
<a href="http://www.froxlor.org/" rel="external">the Froxlor Team</a><br/>
{% if install_mode is not defined %}
{% if (get_setting('panel.imprint_url') != '') %}
<a href="{{ get_setting('panel.imprint_url') }}" target="_blank" class="footer-link">{{ lng('imprint') }}</a>
{% endif %}
{% if (get_setting('panel.terms_url') != '') %}
<a href="{{ get_setting('panel.terms_url') }}" target="_blank" class="footer-link">{{ lng('terms') }}</a>
{% endif %}
{% if (get_setting('panel.privacy_url') != '') %}
<a href="{{ get_setting('panel.privacy_url') }}" target="_blank" class="footer-link">{{ lng('privacy') }}</a>
{% endif %}
{% endif %}
</span>
{% if lng('translator') %}
<br/>
<span>{{ lng('panel.translator') }}:
{{ lng('translator') }}
{% endif %}
</footer>
</div>
</body>
</body>
</html>

View File

@ -0,0 +1,88 @@
<!-- language select -->
<form action="{{ pagecontent.form.formaction }}" method="get">
<div class="row mb-3">
<label for="language" class="col-sm-4 col-form-label">{{ lng('install.language') }}</label>
<div class="col-sm-8">
<select class="form-select" id="language" name="language">
{% for lngfile,lngname in pagecontent.form.languages %}
<option value="{{ lngfile }}" {% if lngfile == pagecontent.form.activelang %} selected="selected" {% endif %}>{{ lngname }}</option>
{% endfor %}
</select>
</div>
</div>
<aside class="text-end">
<input type="hidden" name="check" value="1"/>
<button class="btn btn-sm btn-primary" type="submit" name="chooselang">{{ lng('install.lngbtn_go') }}</button>
</aside>
</form>
<!-- main install form -->
<div class="alert alert-primary mt-md-3" role="alert">{{ lng('install.welcometext')|raw }}</div>
{% if pagecontent.form.result is not empty %}
<div class="alert alert-warning" role="alert">
{% for emsg in pagecontent.form.result %}
<p>{{ emsg }}</p>
{% endfor %}
</div>
{% endif %}
<form action="{{ pagecontent.form.formaction }}" method="post">
{% for fdata in pagecontent.form.data %}
<fieldset>
<legend>{{ fdata.title }}</legend>
{% for field in fdata.fields %}
{% if field is iterable %}
{% if field.type is defined %}
{% if field.type == 'text' or field.type == 'password' %}
<div class="row mb-3">
<label for="{{ field.id }}" class="col-sm-4 col-form-label">{{ field.label|raw }}</label>
<div class="col-sm-8">
<input type="{{ field.type }}" class="form-control {% if field.style == 'red' %}is-invalid{% endif %}" id="{{ field.id }}" name="{{ field.name }}" value="{{ field.value }}" {% if field.required %} required {% endif %}/>
</div>
</div>
{% elseif field.type == 'select' %}
<div class="row mb-3">
<label for="{{ field.id }}" class="col-sm-4 col-form-label">{{ field.label|raw }}</label>
<div class="col-sm-8">
<select class="form-select {% if field.style == 'red' %}is-invalid{% endif %}" id="{{ field.id }}" name="{{ field.name }}" {% if field.required %} required {% endif %}>
{% for opts in field.options %}
<option value="{{ opts.value }}" {% if opts.selected %} selected="selected" {% endif %}>{{ opts.label }}</option>
{% endfor %}
</select>
</div>
</div>
{% elseif field.type == 'checkbox' %}
<div class="row mb-3">
<label for="{{ field.id }}" class="col-sm-4 col-form-label">{{ field.label|raw }}</label>
<div class="col-sm-8">
<div class="form-check form-switch">
<input class="form-check-input {% if field.style == 'red' %}is-invalid{% endif %}" type="checkbox" value="{{ field.value }}" id="{{ field.id }}" name="{{ field.name }}" {% if field.checked %} checked="checked" {% endif %}>
</div>
</div>
</div>
{% endif %}
{% else %}
<div class="row mb-3">
<label class="col-sm-4 col-form-label">{{ field.label|raw }}</label>
<div class="col-sm-8">
{% for radios in field.fields %}
<div class="form-check">
<input class="form-check-input {% if field.style == 'red' %}is-invalid{% endif %}" type="radio" name="{{ radios.name }}" id="{{ radios.id }}" value="{{ radios.value }}" {% if radios.checked %}checked="checked"{% endif %}>
<label class="form-check-label" for="{{ radios.id }}">
{{ radios.label }}
</label>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% endif %}
{% endfor %}
</fieldset>
{% endfor %}
<aside class="text-end mt-3">
<input type="hidden" name="check" value="1"/>
<input type="hidden" name="language" value="{{ pagecontent.form.activelang }}"/>
<input type="hidden" name="installstep" value="1"/>
<button class="btn btn-lg btn-success" type="submit" name="submitbutton">{{ lng('click_here_to_continue') }}
&raquo;</button>
</aside>
</form>

View File

@ -0,0 +1,53 @@
{% extends "Froxlor/base.html.twig" %}
{% block body %}
<div class="container p-3 bg-light border border-top-0">
<img src="{{ basehref|default('') }}templates/Froxlor/assets/img/logo.png" alt="Froxlor Server Management Panel"/>
<section class="mt-4">
<h3 class="text-center mb-3">{{ pagetitle }}</h3>
{% if pagecontent.checks is defined or pagecontent.installprocess is defined %}
<table class="table table-borderless table-sm">
{% if pagecontent.checks is defined %}
{% set checks = pagecontent.checks %}
{% else %}
{% set checks = pagecontent.installprocess %}
{% endif %}
{% for check in checks %}
<tr class="{% if check.result == 1 %}table-danger{% elseif check.result == 2 %}table-warning{% endif %}">
<td class="w-75" scope="row">{{ check.title }}</td>
<td class="col-auto text-end{% if check.result == 0 %} text-success{% endif %}">
<span class="d-none d-md-inline">{{ check.result_txt }}</span>
{% if check.result == 0 %}&nbsp;&check;{% elseif check.result == 2 %}<span class="d-md-none">&nbsp;???</span>{% elseif check.result == 1 %}<span class="d-md-none">&nbsp;!!!</span>
{% endif %}
</td>
</tr>
{% if check.result_desc is not empty %}
<tr>
<td colspan="2" class="text-end">
<small>{{ check.result_desc|raw }}</span>
</td>
</tr>
</small>
</td>
</tr>
{% endif %}
{% endfor %}
</table>
{% elseif pagecontent.form is defined %}
{% include "Froxlor/install/form.html.twig" %}
{% else %}
<div class="alert alert-warning" role="alert">
{{ pagecontent }}
</div>
{% endif %}
{% if pagenavigation is not empty %}
<div class="row pt-md-3">
<div class="col-12 col-md-8 d-flex align-items-center">
<h4 class="p-0 m-0 text-{% if pagenavigation.bad %}danger{% else %}success{% endif %}">{{ pagenavigation.message }}</h4>
</div>
<div class="col-12 col-md-4 text-end mt-4 mt-md-0">
<a class="btn btn-lg btn-block btn-{% if pagenavigation.bad %}warning{% else %}success{% endif %}" href="{{ pagenavigation.link}}">{{ pagenavigation.linktext }}
&raquo;</a>
</div>
</div>
{% endif %}</section></div>{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "Froxlor/base.html.twig" %}
{% block body %}
<div class="alert alert-{{ type }} fade show" role="alert">
<h4 class="alert-heading">
{{ heading }}
</h4>
<p>
{{ alert_msg|raw }}
</p>
{% if alert_info %}
<hr>
<p class="mb-0">
<pre>{{ alert_info|raw }}</pre>
</p>
{% endif %}
{% if redirect_link %}
<p class="mt-1 text-center">
<a href="{{ redirect_link }}" class="btn btn-{{ type }}">{% if type == 'danger' %}{{ lng('panel.back') }}{% else %}{{ lng('success.clickheretocontinue') }}{% endif %}</a>
</p>
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "Froxlor/base.html.twig" %}
{% block body %}
<div class="container my-auto">
<div class="alert alert-primary fade show" role="alert">
<h4 class="alert-heading">
Welcome to froxlor
</h4>
<p>It seems that Froxlor has not been installed yet.</p>
<p>Click on the link below to start the installation.</p>
<hr>
<p class="mt-1 text-center">
<a href="./install/install.php" class="btn btn-primary" title="Click to start the install process">Start install</a>
</p>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,20 @@
{% extends "Froxlor/base.html.twig" %}
{% block body %}
<div class="container my-auto">
<div class="alert alert-warning fade show" role="alert">
<h4 class="alert-heading">
Whoops!
</h4>
<p>The configuration file <b>lib/userdata.inc.php</b> cannot be read from the webserver.</p>
<p>This mostly happens due to wrong ownership.<br />Try the following command to correct the ownership:</p>
<p class="mb-0">
<pre>chown -R {{ user }}:{{ group }} {{ installdir }}</pre>
</p>
<hr>
<p class="mt-1 text-center">
<a href="./install/install.php" class="btn btn-primary" title="Click to start the install process">Start install</a>
</p>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="noindex, nofollow, noarchive" />
<meta name="GOOGLEBOT" content="nosnippet" />
<!-- Bootstrap CSS (external) -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>Froxlor - Error</title>
</head>
<body>
<div class="container-fluid">
<div class="container my-auto">
<div class="alert alert-danger fade show" role="alert">
<h4 class="alert-heading">
Whoops!
</h4>
<p>It seems you are using an older version of PHP</p>
<p>Froxlor requires at least PHP version <FROXLOR_PHPMIN><br />The installed version is:
<CURRENT_VERSION>
</p>
<hr>
<p class="mt-1 text-center">
<a href="index.php" class="btn btn-primary" title="Click to refresh">Refresh</a>
</p>
</div>
</div>
<footer class="pt-5 pb-5 text-center">
<span><img src="templates/Froxlor/assets/img/logo_grey.png" alt="Froxlor" />
&copy; 2009-<CURRENT_YEAR> by <a href="http://www.froxlor.org/" rel="external">the Froxlor
Team</a><br />
</span>
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js"
integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF"
crossorigin="anonymous"></script>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="noindex, nofollow, noarchive" />
<meta name="GOOGLEBOT" content="nosnippet" />
<!-- Bootstrap CSS (external) -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>Froxlor - Error</title>
</head>
<body>
<div class="container-fluid">
<div class="container my-auto">
<div class="alert alert-danger fade show" role="alert">
<h4 class="alert-heading">
Whoops!
</h4>
<p>It seems you are missing some required files.</p>
<p>Froxlor uses composer for its external requirements. Try the following command to install them:</p>
<p class="mb-0">
cd <FROXLOR_INSTALL_DIR> && composer install --no-dev</pre>
</p>
<hr>
<p class="mt-1 text-center">
<a href="index.php" class="btn btn-primary" title="Click to refresh">Refresh</a>
</p>
</div>
</div>
<footer class="pt-5 pb-5 text-center">
<span><img src="templates/Froxlor/assets/img/logo_grey.png" alt="Froxlor" />
&copy; 2009-<CURRENT_YEAR> by <a href="http://www.froxlor.org/" rel="external">the Froxlor
Team</a><br />
</span>
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js"
integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF"
crossorigin="anonymous"></script>
</body>
</html>