Compare commits
32 Commits
2.0.22
...
upgrade-2.
| Author | SHA1 | Date | |
|---|---|---|---|
| d7a3568506 | |||
| 10c13bc5b1 | |||
| dcb3f6f568 | |||
| 7566def0d1 | |||
| 3630f82817 | |||
| 9ddd2e9154 | |||
| 53afe4ebd1 | |||
| 4f69e8ee0e | |||
| 32f5b0d5e9 | |||
| 53a6485a6e | |||
| f2643ac887 | |||
| e37687a85d | |||
| ccbc3286a5 | |||
| 929a562324 | |||
| 3704cf6621 | |||
| 10238a1466 | |||
| 9002ddf4a2 | |||
| 8a2de5a44a | |||
| 96c0af18dd | |||
| 5bb228ce78 | |||
| 804128280c | |||
| 5b8e918f75 | |||
| 0e3e83d184 | |||
| 8ced61c6aa | |||
| 29a2ab7567 | |||
|
|
166ec0575b | ||
|
|
215e749ba8 | ||
|
|
506cccd7c8 | ||
|
|
6d9014c29b | ||
|
|
10555bff76 | ||
|
|
37aa7af4da | ||
|
|
4b75369597 |
53
.drone.yml
Normal file
53
.drone.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
kind: pipeline
|
||||
name: deploy-froxlor
|
||||
type: docker
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
- upgrade-2.0
|
||||
event:
|
||||
include:
|
||||
- push
|
||||
|
||||
environment:
|
||||
DEPLOY_HOST: rechner.maketank.net
|
||||
DEPLOY_DIR: ~/froxlor-test
|
||||
|
||||
steps:
|
||||
- name: deploy
|
||||
image: cr.wks/drone/drone-rsync:latest
|
||||
settings:
|
||||
hosts: ["rechner02.maketank.net"]
|
||||
source: ./
|
||||
target: ~/froxlor-test
|
||||
user: www-data
|
||||
exclude: ['vendor', '.git*', '*drone.yml', '.settings', '.buildpath', '.editorconfig', '.project', '.travis.yml', 'node_modules']
|
||||
args: '-v --delete'
|
||||
log_level: quiet
|
||||
key:
|
||||
from_secret: ssh-www-data-maketank-rsa
|
||||
command_timeout: 10m
|
||||
- name: compose
|
||||
image: appleboy/drone-ssh
|
||||
settings:
|
||||
host:
|
||||
- rechner02.maketank.net
|
||||
username: www-data
|
||||
key:
|
||||
from_secret: ssh-www-data-maketank-rsa
|
||||
script:
|
||||
- cd ~/froxlor-test && composer install --no-dev
|
||||
- name: npm
|
||||
image: appleboy/drone-ssh
|
||||
settings:
|
||||
host:
|
||||
- rechner02.maketank.net
|
||||
username: www-data
|
||||
key:
|
||||
from_secret: ssh-www-data-maketank-rsa
|
||||
script:
|
||||
- cd ~/froxlor-test && npm install && npm run build
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -18,7 +18,6 @@ img/
|
||||
vendor/
|
||||
node_modules/
|
||||
fonts/
|
||||
templates/*
|
||||
!templates/index.html
|
||||
!templates/Froxlor/
|
||||
templates/Froxlor/assets/mix-manifest.json
|
||||
|
||||
@@ -299,6 +299,30 @@ if ($page == 'email_domain') {
|
||||
'action' => 'edit',
|
||||
'id' => $id,
|
||||
]);
|
||||
} elseif ($action == 'togglegreylist' && $id != 0) {
|
||||
try {
|
||||
$json_result = Emails::getLocal($userinfo, [
|
||||
'id' => $id
|
||||
])->get();
|
||||
} catch (Exception $e) {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
$result = json_decode($json_result, true)['data'];
|
||||
|
||||
try {
|
||||
Emails::getLocal($userinfo, [
|
||||
'id' => $id,
|
||||
'disablegreylist' => ($result['disablegreylist'] == '1' ? 0 : 1)
|
||||
])->updateGreylist();
|
||||
} catch (Exception $e) {
|
||||
Response::dynamicError($e->getMessage());
|
||||
}
|
||||
Response::redirectTo($filename, [
|
||||
'page' => $page,
|
||||
'domainid' => $email_domainid,
|
||||
'action' => 'edit',
|
||||
'id' => $id,
|
||||
]);
|
||||
}
|
||||
} elseif ($page == 'accounts') {
|
||||
$email_domainid = Request::any('domainid', 0);
|
||||
|
||||
@@ -697,7 +697,7 @@ opcache.validate_timestamps'),
|
||||
('system', 'distribution', ''),
|
||||
('system', 'update_channel', 'stable'),
|
||||
('system', 'updatecheck_data', ''),
|
||||
('system', 'update_notify_last', '2.0.22'),
|
||||
('system', 'update_notify_last', '2.0.24'),
|
||||
('system', 'traffictool', 'goaccess'),
|
||||
('system', 'req_limit_per_interval', 60),
|
||||
('system', 'req_limit_interval', 60),
|
||||
@@ -744,7 +744,7 @@ opcache.validate_timestamps'),
|
||||
('panel', 'logo_overridetheme', '0'),
|
||||
('panel', 'logo_overridecustom', '0'),
|
||||
('panel', 'settings_mode', '0'),
|
||||
('panel', 'version', '2.0.22'),
|
||||
('panel', 'version', '2.0.24'),
|
||||
('panel', 'db_version', '202304260');
|
||||
|
||||
|
||||
|
||||
@@ -507,3 +507,13 @@ if (Froxlor::isFroxlorVersion('2.0.21')) {
|
||||
Update::showUpdateStep("Updating from 2.0.21 to 2.0.22", false);
|
||||
Froxlor::updateToVersion('2.0.22');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.22')) {
|
||||
Update::showUpdateStep("Updating from 2.0.22 to 2.0.23", false);
|
||||
Froxlor::updateToVersion('2.0.23');
|
||||
}
|
||||
|
||||
if (Froxlor::isFroxlorVersion('2.0.23')) {
|
||||
Update::showUpdateStep("Updating from 2.0.23 to 2.0.24", false);
|
||||
Froxlor::updateToVersion('2.0.24');
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
|
||||
// parameters
|
||||
$iscatchall = $this->getBoolParam('iscatchall', true, 0);
|
||||
$disablegreylist = $this->getBoolParam('disablegreylist', true, 0);
|
||||
$description = $this->getParam('description', true, '');
|
||||
|
||||
// validation
|
||||
@@ -118,7 +119,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
|
||||
// duplicate check
|
||||
$stmt = Database::prepare("
|
||||
SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid` FROM `" . TABLE_MAIL_VIRTUAL . "`
|
||||
SELECT `id`, `email`, `email_full`, `iscatchall`, `destination`, `customerid`, `disablegreylist` FROM `" . TABLE_MAIL_VIRTUAL . "`
|
||||
WHERE (`email` = :email OR `email_full` = :emailfull )
|
||||
AND `customerid`= :cid
|
||||
");
|
||||
@@ -144,7 +145,8 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
`email_full` = :email_full,
|
||||
`iscatchall` = :iscatchall,
|
||||
`domainid` = :domainid,
|
||||
`description` = :description
|
||||
`description` = :description,
|
||||
`disablegreylist` = :disablegreylist
|
||||
");
|
||||
$params = [
|
||||
"cid" => $customer['customerid'],
|
||||
@@ -152,7 +154,8 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
"email_full" => $email_full,
|
||||
"iscatchall" => $iscatchall,
|
||||
"domainid" => $domain_check['id'],
|
||||
"description" => $description
|
||||
"description" => $description,
|
||||
"disablegreylist" => $disablegreylist
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
|
||||
@@ -191,7 +194,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
$customer_ids = $this->getAllowedCustomerIds('email');
|
||||
$params['idea'] = ($id <= 0 ? $emailaddr : $id);
|
||||
|
||||
$result_stmt = Database::prepare("SELECT v.`id`, v.`email`, v.`email_full`, v.`iscatchall`, v.`destination`, v.`customerid`, v.`popaccountid`, v.`domainid`, v.`description`, u.`quota`, u.`imap`, u.`pop3`, u.`postfix`, u.`mboxsize`
|
||||
$result_stmt = Database::prepare("SELECT v.`id`, v.`email`, v.`email_full`, v.`iscatchall`, v.`disablegreylist`, v.`destination`, v.`customerid`, v.`popaccountid`, v.`domainid`, v.`description`, u.`quota`, u.`imap`, u.`pop3`, u.`postfix`, u.`mboxsize`
|
||||
FROM `" . TABLE_MAIL_VIRTUAL . "` v
|
||||
LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON v.`popaccountid` = u.`id`
|
||||
WHERE v.`customerid` IN (" . implode(", ", $customer_ids) . ")
|
||||
@@ -302,6 +305,81 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
return $this->response($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle greylist flag of given email address either by id or email-address
|
||||
*
|
||||
* @param int $id
|
||||
* optional, the email-address-id
|
||||
* @param string $emailaddr
|
||||
* optional, the email-address
|
||||
* @param int $customerid
|
||||
* optional, required when called as admin (if $loginname is not specified)
|
||||
* @param string $loginname
|
||||
* optional, required when called as admin (if $customerid is not specified)
|
||||
* @param boolean $greylist
|
||||
* optional
|
||||
* @param string $description
|
||||
* optional custom description (currently not used/shown in the frontend), default empty
|
||||
*
|
||||
* @access admin, customer
|
||||
* @return string json-encoded array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function updateGreylist()
|
||||
{
|
||||
if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'email')) {
|
||||
throw new Exception("You cannot access this resource", 405);
|
||||
}
|
||||
|
||||
// if enabling catchall is not allowed by settings, we do not need
|
||||
// to run update()
|
||||
/** if (Settings::Get('catchall.catchall_enabled') != '1') {
|
||||
Response::standardError([
|
||||
'operationnotpermitted',
|
||||
'featureisdisabled'
|
||||
], 'catchall', true);
|
||||
} */
|
||||
|
||||
$id = $this->getParam('id', true, 0);
|
||||
$ea_optional = $id > 0;
|
||||
$emailaddr = $this->getParam('emailaddr', $ea_optional, '');
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'id' => $id,
|
||||
'emailaddr' => $emailaddr
|
||||
]);
|
||||
$id = $result['id'];
|
||||
$email = $result['email'];
|
||||
|
||||
|
||||
// parameters
|
||||
$disablegreylist = $this->getBoolParam('disablegreylist', true, $result['disablegreylist']);
|
||||
$description = $this->getParam('description', true, $result['description']);
|
||||
|
||||
// get needed customer info to reduce the email-address-counter by one
|
||||
$customer = $this->getCustomerData();
|
||||
|
||||
// check for catchall-flag
|
||||
$stmt = Database::prepare("
|
||||
UPDATE `" . TABLE_MAIL_VIRTUAL . "`
|
||||
SET `email` = :email , `disablegreylist` = :grflag, `description` = :description
|
||||
WHERE `customerid`= :cid AND `id`= :id
|
||||
");
|
||||
$params = [
|
||||
"email" => $email,
|
||||
"grflag" => $disablegreylist,
|
||||
"description" => $description,
|
||||
"cid" => $customer['customerid'],
|
||||
"id" => $id
|
||||
];
|
||||
Database::pexecute($stmt, $params, true, true);
|
||||
$this->logger()->logAction($this->isAdmin() ? FroxlorLogger::ADM_ACTION : FroxlorLogger::USR_ACTION, LOG_NOTICE, "[API] toggled greylist-flag for email address '" . $result['email_full'] . "'");
|
||||
|
||||
$result = $this->apiCall('Emails.get', [
|
||||
'emailaddr' => $result['email_full']
|
||||
]);
|
||||
return $this->response($result);
|
||||
}
|
||||
/**
|
||||
* list all email addresses, if called from an admin, list all email addresses of all customers you are allowed to
|
||||
* view, or specify id or loginname for one specific customer
|
||||
@@ -331,7 +409,7 @@ class Emails extends ApiCommand implements ResourceEntity
|
||||
$result = [];
|
||||
$query_fields = [];
|
||||
$result_stmt = Database::prepare("
|
||||
SELECT m.`id`, m.`domainid`, m.`email`, m.`email_full`, m.`iscatchall`, m.`destination`, m.`popaccountid`, d.`domain`, u.`quota`, u.`imap`, u.`pop3`, u.`postfix`, u.`mboxsize`
|
||||
SELECT m.`id`, m.`domainid`, m.`email`, m.`email_full`, m.`iscatchall`, m.`disablegreylist`, m.`destination`, m.`popaccountid`, d.`domain`, u.`quota`, u.`imap`, u.`pop3`, u.`postfix`, u.`mboxsize`
|
||||
FROM `" . TABLE_MAIL_VIRTUAL . "` m
|
||||
LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON (m.`domainid` = d.`id`)
|
||||
LEFT JOIN `" . TABLE_MAIL_USERS . "` u ON (m.`popaccountid` = u.`id`)
|
||||
|
||||
@@ -132,18 +132,16 @@ abstract class DnsBase
|
||||
");
|
||||
|
||||
while ($domain = $result_domains_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$privkey_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim' . $domain['dkim_id'] . Settings::Get('dkim.privkeysuffix'));
|
||||
$pubkey_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim' . $domain['dkim_id'] . '.public');
|
||||
$privkey_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/mx.' . $domain['domain'] . '.' . Settings::Get('dkim.privkeysuffix'));
|
||||
$pubkey_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/mx.' . $domain['domain'] . '.public');
|
||||
|
||||
if ($domain['dkim_privkey'] == '' || $domain['dkim_pubkey'] == '') {
|
||||
$max_dkim_id_stmt = Database::query("SELECT MAX(`dkim_id`) as `max_dkim_id` FROM `" . TABLE_PANEL_DOMAINS . "`");
|
||||
$max_dkim_id = $max_dkim_id_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$domain['dkim_id'] = (int)$max_dkim_id['max_dkim_id'] + 1;
|
||||
$privkey_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim' . $domain['dkim_id'] . Settings::Get('dkim.privkeysuffix'));
|
||||
FileDir::safe_exec('openssl genrsa -out ' . escapeshellarg($privkey_filename) . ' ' . Settings::Get('dkim.dkim_keylength'));
|
||||
$domain['dkim_privkey'] = file_get_contents($privkey_filename);
|
||||
FileDir::safe_exec("chmod 0640 " . escapeshellarg($privkey_filename));
|
||||
$pubkey_filename = FileDir::makeCorrectFile(Settings::Get('dkim.dkim_prefix') . '/dkim' . $domain['dkim_id'] . '.public');
|
||||
FileDir::safe_exec('openssl rsa -in ' . escapeshellarg($privkey_filename) . ' -pubout -outform pem -out ' . escapeshellarg($pubkey_filename));
|
||||
$domain['dkim_pubkey'] = file_get_contents($pubkey_filename);
|
||||
FileDir::safe_exec("chmod 0664 " . escapeshellarg($pubkey_filename));
|
||||
@@ -217,7 +215,7 @@ abstract class DnsBase
|
||||
`" . TABLE_PANEL_DOMAINS . "` `d`
|
||||
LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` `c` USING(`customerid`)
|
||||
WHERE
|
||||
`d`.`isbinddomain` = '1'
|
||||
`d`.`isbinddomain` = '1' aND `d`.`deactivated` = '0'
|
||||
ORDER BY
|
||||
`d`.`domain` ASC
|
||||
");
|
||||
|
||||
@@ -883,7 +883,7 @@ class Nginx extends HttpConfigBase
|
||||
// remove comments
|
||||
$vhost = implode("\n", preg_replace('/^(\s+)?#(.*)$/', '', explode("\n", $vhost)));
|
||||
// Break blocks into lines
|
||||
$vhost = preg_replace("/^(\s+)location(.+)\{(.+)\}$/misU", "location $2 {\n $3 \n}", $vhost);
|
||||
$vhost = preg_replace("/^(\s+)?location(.+)\{(.+)\}$/misU", "location $2 {\n $3 \n}", $vhost);
|
||||
// Break into array items
|
||||
$vhost = explode("\n", preg_replace('/[ \t]+/', ' ', trim(preg_replace('/\t+/', '', $vhost))));
|
||||
// Remove empty lines
|
||||
|
||||
@@ -151,9 +151,13 @@ class CurrentUser
|
||||
]);
|
||||
$addition = $result['emaildomains'] != 0;
|
||||
} elseif ($resource == 'subdomains') {
|
||||
$parentDomainCollection = (new Collection(SubDomains::class, $_SESSION['userinfo'],
|
||||
['sql_search' => ['d.parentdomainid' => 0]]));
|
||||
$addition = $parentDomainCollection->count() != 0;
|
||||
if (Settings::IsInList('panel.customer_hide_options', 'domains')) {
|
||||
$addition = false;
|
||||
} else {
|
||||
$parentDomainCollection = (new Collection(SubDomains::class, $_SESSION['userinfo'],
|
||||
['sql_search' => ['d.parentdomainid' => 0]]));
|
||||
$addition = $parentDomainCollection->count() != 0;
|
||||
}
|
||||
} elseif ($resource == 'domains') {
|
||||
$customerCollection = (new Collection(Customers::class, $_SESSION['userinfo']));
|
||||
$addition = $customerCollection != 0;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
* @author Froxlor team <team@froxlor.org>
|
||||
* @license https://files.froxlor.org/misc/COPYING.txt GPLv2
|
||||
*/
|
||||
|
||||
namespace Froxlor\Dns;
|
||||
|
||||
use Froxlor\Database\Database;
|
||||
@@ -183,7 +182,10 @@ class Dns
|
||||
}
|
||||
if (Settings::Get('dkim.use_dkim') == '1') {
|
||||
// check for DKIM content later
|
||||
self::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey.' . $sub_record, 'TXT', $required_entries);
|
||||
//self::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey.' . $sub_record, 'TXT', $required_entries);
|
||||
self::addRequiredEntry('mx._domainkey.' . $sub_record, 'TXT', $required_entries);
|
||||
//Also add dmarc
|
||||
self::addRequiredEntry('_dmarc' . $sub_record, 'TXT', $required_entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,7 +222,10 @@ class Dns
|
||||
}
|
||||
if (Settings::Get('dkim.use_dkim') == '1') {
|
||||
// check for DKIM content later
|
||||
self::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey', 'TXT', $required_entries);
|
||||
//self::addRequiredEntry('dkim' . $domain['dkim_id'] . '._domainkey', 'TXT', $required_entries);
|
||||
self::addRequiredEntry('mx._domainkey', 'TXT', $required_entries);
|
||||
//Also add dmarc
|
||||
self::addRequiredEntry('_dmarc', 'TXT', $required_entries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,10 +383,13 @@ class Dns
|
||||
if (array_key_exists("TXT", $required_entries)) {
|
||||
if (Settings::Get('dkim.use_dkim') == '1') {
|
||||
$dkim_entries = self::generateDkimEntries($domain);
|
||||
$dmarc_entries = self::generateDmarcEntries($domain);
|
||||
}
|
||||
|
||||
foreach ($required_entries as $type => $records) {
|
||||
if ($type == 'TXT') {
|
||||
//$dkim_record = 'dkim' . $domain['dkim_id'] . '._domainkey';
|
||||
$dkim_record = 'mx._domainkey';
|
||||
foreach ($records as $record) {
|
||||
if ($record == '@SPF@') {
|
||||
// spf for main-domain
|
||||
@@ -392,9 +400,8 @@ class Dns
|
||||
$txt_content = Settings::Get('spf.spf_entry');
|
||||
$sub_record = substr($record, 6);
|
||||
$zonerecords[] = new DnsEntry($sub_record, 'TXT', self::encloseTXTContent($txt_content));
|
||||
} elseif (!empty($dkim_entries)) {
|
||||
} elseif (!empty($dkim_entries) && $record == $dkim_record ) {
|
||||
// DKIM entries
|
||||
$dkim_record = 'dkim' . $domain['dkim_id'] . '._domainkey';
|
||||
if ($record == $dkim_record) {
|
||||
// dkim for main-domain
|
||||
// check for multiline entry
|
||||
@@ -412,7 +419,10 @@ class Dns
|
||||
}
|
||||
$zonerecords[] = new DnsEntry($record, 'TXT', self::encloseTXTContent($dkim_entries[0], $multiline));
|
||||
}
|
||||
} elseif ($record == '_dmarc' && !empty($dmarc_entries) && $domain['isemaildomain'] == '1') {
|
||||
$zonerecords[] = new DnsEntry($record, 'TXT', self::encloseTXTContent($dmarc_entries[0]));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -523,7 +533,7 @@ class Dns
|
||||
* @param array $domain
|
||||
* @return array
|
||||
*/
|
||||
private static function generateDkimEntries(array $domain): array
|
||||
/** private static function generateDkimEntries(array $domain): array
|
||||
{
|
||||
$zone_dkim = [];
|
||||
|
||||
@@ -569,43 +579,61 @@ class Dns
|
||||
}
|
||||
|
||||
return $zone_dkim;
|
||||
}
|
||||
|
||||
} */
|
||||
private static function generateDkimEntries(array $domain): array
|
||||
{
|
||||
$zone_dkim = [];
|
||||
if (Settings::Get('dkim.use_dkim') == '1' && $domain['dkim'] == '1' && $domain['dkim_pubkey'] != '') {
|
||||
// start
|
||||
$dkim_txt = 'v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAosq0CmLqEzJJxIHkQwG1Xwk6CSyHHWSDXL9BHCKzY9lJXH7a23PogVlLvUBYaAgBtFOpsKuUCBl+/g6rOqgVXKg0OpYdpgTxZyz1i4NcubGFLifQGnF8ZKpIEDqIzmLI6SbH+9DKwYA319sXAR6feZI4g5bWqF07t/kzA5LN+2V5QnDQ3th++GPRl5rmWF6uoidIRD85UZVEX4s3J1hce0k6tRb2aEozCJaSXHUwyarmbbX/5rky467QQ+45Uy0q9CNaMMu1IX5eybhLRxYXK1k0TfIRJv4FH1UFLlq2QoGC7d+KvLrUabhzQ5wbdZkWuVgLFZ7CL2NegfzO6YeEcQIDAQAB';
|
||||
$zone_dkim[] = $dkim_txt;
|
||||
}
|
||||
return $zone_dkim;
|
||||
}
|
||||
private static function generateDmarcEntries(array $domain): array
|
||||
{
|
||||
$zone_dmarc = [];
|
||||
if (Settings::Get('dkim.use_dkim') == '1' && $domain['dkim'] == '1' ){
|
||||
$dmarc_txt = 'v=DMARC1; p=reject; ruf=mailto:dmarc@'. $domain['domain'] . '; rua=mailto:dmarc@'. $domain['domain'] . '; fo=1; adkim=r; aspf=r; pct=100; rf=afrf; ri=345600;';
|
||||
$zone_dmarc[] = $dmarc_txt;
|
||||
}
|
||||
return $zone_dmarc;
|
||||
}
|
||||
/**
|
||||
* @param string $txt_content
|
||||
* @param bool $isMultiLine
|
||||
* @return string
|
||||
*/
|
||||
public static function encloseTXTContent(string $txt_content, bool $isMultiLine = false): string
|
||||
{
|
||||
// check that TXT content is enclosed in " "
|
||||
if (!$isMultiLine && Settings::Get('system.dns_server') != 'PowerDNS') {
|
||||
if (substr($txt_content, 0, 1) != '"') {
|
||||
$txt_content = '"' . $txt_content;
|
||||
}
|
||||
if (substr($txt_content, -1) != '"') {
|
||||
$txt_content .= '"';
|
||||
}
|
||||
}
|
||||
if (Settings::Get('system.dns_server') == 'PowerDNS') {
|
||||
// no quotation for PowerDNS
|
||||
if (substr($txt_content, 0, 1) == '"') {
|
||||
$txt_content = substr($txt_content, 1);
|
||||
}
|
||||
if (substr($txt_content, -1) == '"') {
|
||||
$txt_content = substr($txt_content, 0, -1);
|
||||
}
|
||||
}
|
||||
return $txt_content;
|
||||
}
|
||||
{
|
||||
// check that TXT content is enclosed in " "
|
||||
if (! $isMultiLine && Settings::Get('system.dns_server') != 'PowerDNS') {
|
||||
if (substr($txt_content, 0, 1) != '"') {
|
||||
$txt_content = '"' . $txt_content;
|
||||
}
|
||||
if (substr($txt_content, - 1) != '"') {
|
||||
$txt_content .= '"';
|
||||
}
|
||||
}
|
||||
if (Settings::Get('system.dns_server') == 'PowerDNS') {
|
||||
// no quotation for PowerDNS
|
||||
if (substr($txt_content, 0, 1) == '"') {
|
||||
$txt_content = substr($txt_content, 1);
|
||||
}
|
||||
if (substr($txt_content, - 1) == '"') {
|
||||
$txt_content = substr($txt_content, 0, - 1);
|
||||
}
|
||||
}
|
||||
return $txt_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
* @return string
|
||||
*/
|
||||
private static function escapeSoaAdminMail(string $email): string
|
||||
{
|
||||
$mail_parts = explode("@", $email);
|
||||
return str_replace(".", "\.", $mail_parts[0]) . "." . $mail_parts[1] . ".";
|
||||
}
|
||||
{
|
||||
$mail_parts = explode("@", $email);
|
||||
return str_replace(".", "\.", $mail_parts[0]) . "." . $mail_parts[1] . ".";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ class IpAddr
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getSslIpPortCombinations(): array
|
||||
{
|
||||
@@ -75,7 +76,7 @@ class IpAddr
|
||||
$additional_conditions_params = [];
|
||||
$additional_conditions_array = [];
|
||||
|
||||
if ($userinfo['ip'] != '-1') {
|
||||
if (!empty($userinfo) && $userinfo['ip'] != '-1') {
|
||||
$admin_ip_stmt = Database::prepare("
|
||||
SELECT `id`, `ip`, `port` FROM `" . TABLE_PANEL_IPSANDPORTS . "` WHERE `id` = IN (:ipid)
|
||||
");
|
||||
|
||||
@@ -31,7 +31,7 @@ final class Froxlor
|
||||
{
|
||||
|
||||
// Main version variable
|
||||
const VERSION = '2.0.22';
|
||||
const VERSION = '2.0.24';
|
||||
|
||||
// Database version (YYYYMMDDC where C is a daily counter)
|
||||
const DBVERSION = '202304260';
|
||||
|
||||
@@ -52,7 +52,13 @@ return [
|
||||
'type' => 'checkbox',
|
||||
'value' => '1',
|
||||
'checked' => false
|
||||
]
|
||||
],
|
||||
'disablegreylist' => [
|
||||
'label' => lng('emails.disablegreylist'),
|
||||
'type' => 'checkbox',
|
||||
'value' => '1',
|
||||
'checked' => false
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
@@ -102,6 +102,19 @@ return [
|
||||
]
|
||||
]
|
||||
],
|
||||
'mail_disablegreylist' => [
|
||||
'label' => lng('emails.greylist'),
|
||||
'type' => 'label',
|
||||
'value' => ((int)$result['disablegreylist'] == 0 ? lng('panel.no') : lng('panel.yes')),
|
||||
'next_to' => [
|
||||
'add_link' => [
|
||||
'type' => 'link',
|
||||
'href' => $filename . '?page=' . $page . '&domainid=' . $result['domainid'] . '&action=togglegreylist&id=' . $result['id'],
|
||||
'label' => '<i class="fa-solid fa-arrow-right-arrow-left"></i> ' . lng('panel.toggle'),
|
||||
'classes' => 'btn btn-sm btn-secondary'
|
||||
]
|
||||
]
|
||||
],
|
||||
'mail_fwds' => [
|
||||
'label' => lng('emails.forwarders') . ' (' . $forwarders_count . ')',
|
||||
'type' => 'itemlist',
|
||||
|
||||
@@ -38,7 +38,7 @@ return [
|
||||
'url' => 'customer_email.php?page=emails',
|
||||
'label' => lng('menue.email.emails'),
|
||||
'required_resources' => 'emails',
|
||||
'add_shortlink' => CurrentUser::canAddResource('emails') ? 'customer_email.php?page=email_domain&action=add' : null,
|
||||
'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('emails') ? 'customer_email.php?page=email_domain&action=add' : null,
|
||||
],
|
||||
[
|
||||
'url' => Settings::Get('panel.webmail_url'),
|
||||
@@ -60,7 +60,7 @@ return [
|
||||
'url' => 'customer_mysql.php?page=mysqls',
|
||||
'label' => lng('menue.mysql.databases'),
|
||||
'required_resources' => 'mysqls',
|
||||
'add_shortlink' => CurrentUser::canAddResource('mysqls')? 'customer_mysql.php?page=mysqls&action=add' : null,
|
||||
'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('mysqls')? 'customer_mysql.php?page=mysqls&action=add' : null,
|
||||
],
|
||||
[
|
||||
'url' => Settings::Get('panel.phpmyadmin_url'),
|
||||
@@ -81,7 +81,7 @@ return [
|
||||
[
|
||||
'url' => 'customer_domains.php?page=domains',
|
||||
'label' => lng('menue.domains.settings'),
|
||||
'add_shortlink' => CurrentUser::canAddResource('subdomains') ? 'customer_domains.php?page=domains&action=add' : null,
|
||||
'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('subdomains') ? 'customer_domains.php?page=domains&action=add' : null,
|
||||
],
|
||||
[
|
||||
'url' => 'customer_domains.php?page=sslcertificates',
|
||||
@@ -98,7 +98,7 @@ return [
|
||||
[
|
||||
'url' => 'customer_ftp.php?page=accounts',
|
||||
'label' => lng('menue.ftp.accounts'),
|
||||
'add_shortlink' => CurrentUser::canAddResource('ftps') ? 'customer_ftp.php?page=accounts&action=add' : null,
|
||||
'add_shortlink' => !CurrentUser::isAdmin() && CurrentUser::canAddResource('ftps') ? 'customer_ftp.php?page=accounts&action=add' : null,
|
||||
],
|
||||
[
|
||||
'url' => Settings::Get('panel.webftp_url'),
|
||||
|
||||
@@ -55,6 +55,12 @@ return [
|
||||
'callback' => [Text::class, 'boolean'],
|
||||
'visible' => Settings::Get('catchall.catchall_enabled') == '1'
|
||||
],
|
||||
'm.disablegreylist' => [
|
||||
'label' => lng('emails.greylist'),
|
||||
'field' => 'disablegreylist',
|
||||
'callback' => [Text::class, 'boolean'],
|
||||
'#visible' => Settings::Get('greylist.greylist_enabled') == '1'
|
||||
],
|
||||
'u.quota' => [
|
||||
'label' => lng('emails.quota'),
|
||||
'field' => 'quota',
|
||||
@@ -66,6 +72,7 @@ return [
|
||||
'm.destination',
|
||||
'm.popaccountid',
|
||||
'm.iscatchall',
|
||||
'm.disablegreylist',
|
||||
'u.quota'
|
||||
]),
|
||||
'actions' => [
|
||||
|
||||
2406
lng/ca.lng.php
Normal file
2406
lng/ca.lng.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -724,6 +724,8 @@ return [
|
||||
'back_to_overview' => 'Zurück zur Domain-Übersicht',
|
||||
'accounts' => 'Konten',
|
||||
'emails' => 'Adressen',
|
||||
'disablegreylist' => 'Greylisting deaktivieren?',
|
||||
'greylist' => 'Greylisting aus?'
|
||||
],
|
||||
'error' => [
|
||||
'error' => 'Fehlermeldung',
|
||||
|
||||
@@ -34,6 +34,7 @@ return [
|
||||
'pt' => 'Portuguese',
|
||||
'se' => 'Swedish',
|
||||
'es' => 'Spanish',
|
||||
'ca' => 'Catalan',
|
||||
],
|
||||
'2fa' => [
|
||||
'2fa' => '2FA options',
|
||||
|
||||
55
notice.html
Normal file
55
notice.html
Normal file
@@ -0,0 +1,55 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>froxlor - Domain not configured</title>
|
||||
<style>
|
||||
:root{--primary:#1872a2;--fonts:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"}
|
||||
body{display:flex;flex-direction:column;background:#f8f9fa;color:#4b5563;align-items:center;justify-content:center;font-family:var(--fonts)}
|
||||
main{background:#fff;margin:10% auto 12px;max-width:540px;padding:2rem;box-shadow:4px 8px 16px 0 rgba(0,0,0,.07);border-radius:.375rem}
|
||||
main h2{margin:0}
|
||||
main p{margin-bottom:1.5rem}
|
||||
main p:last-child{margin-bottom:0}
|
||||
main ul{list-style:none;margin-left:-40px}
|
||||
main li{display:flex;align-items:center;margin-bottom:1rem}
|
||||
main .icon{min-width:24px;width:24px;stroke:var(--primary);margin-right:.75rem}
|
||||
code{background:#eee;padding:.1rem .25rem;border-radius:4px;color:rgba(0,0,0,.75)}
|
||||
hr{margin:2rem 0;border:none;border-bottom:solid 1px rgba(0,0,0,.1)}
|
||||
a,a:active,a:visited{color:var(--primary);text-decoration:none}
|
||||
a:hover{text-decoration:underline}
|
||||
footer{display:flex;align-items:center;margin-top:.5rem}
|
||||
footer .logo{margin-right:.35rem}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root{--primary:#29a2d6}
|
||||
body{background:#212529;color:#f8f9fa}
|
||||
main{background:#343a40}
|
||||
hr{border-color:rgba(0,0,0,.2)}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<h2>Domain not configured</h2>
|
||||
<p>
|
||||
This domain requires configuration via the froxlor server management panel, as it is currently not assigned to any customer.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="icon">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
|
||||
</svg>
|
||||
<span>Please ask your provider/hoster if you have any questions.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</main>
|
||||
<footer>
|
||||
<img class="logo" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAAAQCAYAAAC1MDndAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAy1JREFUeNrs1muIVlUUBuBnvqZkoswudKErZFOG0kWt6WpN0wWCDMOsgX4UmVS/KovKhIKYLhRBJvV1o/5VlJCmYEmUFYijaBYhSRQVBVE0ZnRBJ/vzfnA4nDN+wTh/pgWHc/baa6+z97v3et/d0Ww24SQ8ij5MNL7tN6zBvdjWiZOxDpP8b3JA5qAXPQ0MtAnOm9jZBvqbsQHbsGsMFrQLv+KPUc47CQONlFXZPsNfJd/buBrDFfHDWIDDcAZmohsPjwFAD+EQnL8Xcl/WqOGc+/FJyXcxVuHJivjX8Hx2cwGeweO4fIxOkL10Wg9o1HSsxUcl30V5P1JxnFuxp+M53I67cQ5mYwZexj2YlnarJBdl3BE4FfPxdfofSOwNhX+uwgXxf1Uz/024DpNxFHrwRIEiPs74XnyIq3A8Xion6qxIvj0TX1vyb8x7CJ/jrELfjrx/zgmS0p2bCWzHzejAQTgmO35J+OpInIcv8GLKeROuzcI24lAsRH/yzcGJFfMfDIB/B/ipeD8bNojX8W1hPR+gCxPwTzsAdaGRgTeGrAZLJXd4zc59l1Jr2dzC91S8g+PSXhFwGgGxO4s6Bd9gaQRkAHdhSQHso/FCzRweS56ezLmB1bgCb+DBCg67rwaLSud+uDAAvVLRfw1OqCO18JEKbusvgNMqA/F153sCpgegT+O7Iwt8NwrZwKsh5irbUuDMFoXMquhv2eKRSKiOg54OUFXSt3SEfPvi4Dz77IEAW/8uH+vdpf6dkXGF+F9GyNtRkXe4or8tqwNoWrigbDeFTEfDZhbKckuBy9blu0XkC1Pi++PK+Objy5q80/NeXSDl9wrgnDkaAIlyzCj5zh1FCb00JL07Jd2LKfgBx+LWXE6XJP6pcMiUiMg8/FmRd3GEYHNiZ+H6wgZP/i+T7MzPJo5wkjYU2t01cWfjp6hH2WZn0adVbM7KALAS3weYftwZIVgTIHtxS8YtD6kOYX2Uqi8iIKCsD6CDhXnNi+iI+vXhwD3g83tHs9l8K5JZZbfh2UL7x0jyeLFljVzUhtoc0DWOwBnCoga25s6wrHDhG8+2I1j0YOu/AwBUU7aBHvM/ZwAAAABJRU5ErkJggg==" alt="froxlor"/>
|
||||
<small>© 2009-<span id="year"></span> by <a href="https://froxlor.org" rel="external">the froxlor team</a></small>
|
||||
</footer>
|
||||
<script>
|
||||
document.getElementById("year").innerHTML = new Date().getFullYear();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
10455
package-lock.json
generated
10455
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
templates/Froxlor/assets/css/dark.css
Normal file
17
templates/Froxlor/assets/css/dark.css
Normal file
File diff suppressed because one or more lines are too long
17
templates/Froxlor/assets/css/main.css
Normal file
17
templates/Froxlor/assets/css/main.css
Normal file
File diff suppressed because one or more lines are too long
2
templates/Froxlor/assets/js/main.js
Normal file
2
templates/Froxlor/assets/js/main.js
Normal file
File diff suppressed because one or more lines are too long
45
templates/Froxlor/assets/js/main.js.LICENSE.txt
Normal file
45
templates/Froxlor/assets/js/main.js.LICENSE.txt
Normal file
@@ -0,0 +1,45 @@
|
||||
/*!
|
||||
* Bootstrap v5.3.2 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @kurkle/color v0.2.1
|
||||
* https://github.com/kurkle/color#readme
|
||||
* (c) 2022 Jukka Kurkela
|
||||
* Released under the MIT License
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Chart.js v3.9.1
|
||||
* https://www.chartjs.org
|
||||
* (c) 2022 Chart.js Contributors
|
||||
* Released under the MIT License
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2023 Fonticons, Inc.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* jQuery JavaScript Library v3.7.1
|
||||
* https://jquery.com/
|
||||
*
|
||||
* Copyright OpenJS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://jquery.org/license
|
||||
*
|
||||
* Date: 2023-08-28T13:37Z
|
||||
*/
|
||||
|
||||
/*!
|
||||
* jQuery Validation Plugin v1.20.0
|
||||
*
|
||||
* https://jqueryvalidation.org/
|
||||
*
|
||||
* Copyright (c) 2023 Jörn Zaefferer
|
||||
* Released under the MIT license
|
||||
*/
|
||||
13
templates/Froxlor/assets/mix-manifest.json
Normal file
13
templates/Froxlor/assets/mix-manifest.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"/js/main.js": "/js/main.js?id=29451cc889431e973473738b93e6a626",
|
||||
"/css/dark.css": "/css/dark.css?id=f965da97d900604705c3762bb9cd645a",
|
||||
"/css/main.css": "/css/main.css?id=cbda89c88526530a66fe1e0d46a63a22",
|
||||
"/webfonts/fa-brands-400.ttf": "/webfonts/fa-brands-400.ttf?id=69e5d8e4e818f05fd882cceb758d1eba",
|
||||
"/webfonts/fa-brands-400.woff2": "/webfonts/fa-brands-400.woff2?id=189b85e9c72c6f75e464c3f58a6707cf",
|
||||
"/webfonts/fa-regular-400.ttf": "/webfonts/fa-regular-400.ttf?id=ed4c23399d1013809882e90bfe396d1b",
|
||||
"/webfonts/fa-regular-400.woff2": "/webfonts/fa-regular-400.woff2?id=be75b1958ae0da55e1eed562d9b7713d",
|
||||
"/webfonts/fa-solid-900.ttf": "/webfonts/fa-solid-900.ttf?id=dfdc7801582dd0d20ea75faa3b96c296",
|
||||
"/webfonts/fa-solid-900.woff2": "/webfonts/fa-solid-900.woff2?id=a0feb384c3c6071947a49708f2b0bc85",
|
||||
"/webfonts/fa-v4compatibility.ttf": "/webfonts/fa-v4compatibility.ttf?id=e24ec0b8661f7fa333b29444df39e399",
|
||||
"/webfonts/fa-v4compatibility.woff2": "/webfonts/fa-v4compatibility.woff2?id=e11465c0eff0549edd4e8ea6bbcf242f"
|
||||
}
|
||||
BIN
templates/Froxlor/assets/webfonts/fa-brands-400.ttf
Normal file
BIN
templates/Froxlor/assets/webfonts/fa-brands-400.ttf
Normal file
Binary file not shown.
BIN
templates/Froxlor/assets/webfonts/fa-brands-400.woff2
Normal file
BIN
templates/Froxlor/assets/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
templates/Froxlor/assets/webfonts/fa-regular-400.ttf
Normal file
BIN
templates/Froxlor/assets/webfonts/fa-regular-400.ttf
Normal file
Binary file not shown.
BIN
templates/Froxlor/assets/webfonts/fa-regular-400.woff2
Normal file
BIN
templates/Froxlor/assets/webfonts/fa-regular-400.woff2
Normal file
Binary file not shown.
BIN
templates/Froxlor/assets/webfonts/fa-solid-900.ttf
Normal file
BIN
templates/Froxlor/assets/webfonts/fa-solid-900.ttf
Normal file
Binary file not shown.
BIN
templates/Froxlor/assets/webfonts/fa-solid-900.woff2
Normal file
BIN
templates/Froxlor/assets/webfonts/fa-solid-900.woff2
Normal file
Binary file not shown.
BIN
templates/Froxlor/assets/webfonts/fa-v4compatibility.ttf
Normal file
BIN
templates/Froxlor/assets/webfonts/fa-v4compatibility.ttf
Normal file
Binary file not shown.
BIN
templates/Froxlor/assets/webfonts/fa-v4compatibility.woff2
Normal file
BIN
templates/Froxlor/assets/webfonts/fa-v4compatibility.woff2
Normal file
Binary file not shown.
213
templates/Maketank/assets/css/custom.css
Normal file
213
templates/Maketank/assets/css/custom.css
Normal file
@@ -0,0 +1,213 @@
|
||||
:root,[data-bs-theme=light] {
|
||||
--bs-blue: #0d6efd;
|
||||
--bs-indigo: #6610f2;
|
||||
--bs-purple: #6f42c1;
|
||||
--bs-pink: #d63384;
|
||||
--bs-red: #dc3545;
|
||||
--bs-orange: #fd7e14;
|
||||
--bs-yellow: #ffc107;
|
||||
--bs-green: #198754;
|
||||
--bs-teal: #20c997;
|
||||
--bs-cyan: #0dcaf0;
|
||||
--bs-black: #000;
|
||||
--bs-white: #fff;
|
||||
--bs-gray: #6c757d;
|
||||
/* --bs-gray-dark:#343a40; */
|
||||
--bs-gray-100: #f8f9fa;
|
||||
--bs-gray-200: #e9ecef;
|
||||
--bs-gray-300: #dee2e6;
|
||||
--bs-gray-400: #ced4da;
|
||||
--bs-gray-500: #adb5bd;
|
||||
--bs-gray-600: #6c757d;
|
||||
--bs-gray-700: #495057;
|
||||
--bs-gray-800: #343a40;
|
||||
--bs-gray-900: #212529;
|
||||
--bs-primary: #1872a2;
|
||||
--bs-secondary: #6c757d;
|
||||
--bs-success: #059669;
|
||||
--bs-info: #0e5380;
|
||||
--bs-warning: #fbbf24;
|
||||
--bs-danger: #be123c;
|
||||
--bs-light: #f8f9fa;
|
||||
--bs-dark: #212529;
|
||||
--bs-primary-rgb: 180,170,160;
|
||||
--bs-secondary-rgb: 218,212,208;
|
||||
--bs-success-rgb: 5,150,105;
|
||||
--bs-info-rgb: 14,83,128;
|
||||
--bs-warning-rgb: 251,191,36;
|
||||
--bs-danger-rgb: 190,18,60;
|
||||
--bs-light-rgb: 248,249,250;
|
||||
--bs-dark-rgb: 218,212,208;
|
||||
--bs-primary-text-emphasis: #0a2e41;
|
||||
--bs-secondary-text-emphasis: #2b2f32;
|
||||
--bs-success-text-emphasis: #023c2a;
|
||||
--bs-info-text-emphasis: #062133;
|
||||
--bs-warning-text-emphasis: #644c0e;
|
||||
--bs-danger-text-emphasis: #4c0718;
|
||||
--bs-light-text-emphasis: #495057;
|
||||
--bs-dark-text-emphasis: #495057;
|
||||
--bs-primary-bg-subtle: #d1e3ec;
|
||||
--bs-secondary-bg-subtle: #e2e3e5;
|
||||
--bs-success-bg-subtle: #cdeae1;
|
||||
--bs-info-bg-subtle: #cfdde6;
|
||||
--bs-warning-bg-subtle: #fef2d3;
|
||||
--bs-danger-bg-subtle: #f2d0d8;
|
||||
--bs-light-bg-subtle: #fcfcfd;
|
||||
--bs-dark-bg-subtle: #ced4da;
|
||||
--bs-primary-border-subtle: #a3c7da;
|
||||
--bs-secondary-border-subtle: #c4c8cb;
|
||||
--bs-success-border-subtle: #9bd5c3;
|
||||
--bs-info-border-subtle: #9fbacc;
|
||||
--bs-warning-border-subtle: #fde5a7;
|
||||
--bs-danger-border-subtle: #e5a0b1;
|
||||
--bs-light-border-subtle: #e9ecef;
|
||||
--bs-dark-border-subtle: #adb5bd;
|
||||
--bs-white-rgb: 255,255,255;
|
||||
--bs-black-rgb: 0,0,0;
|
||||
--bs-font-sans-serif: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
|
||||
--bs-font-monospace: SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;
|
||||
--bs-gradient: linear-gradient(180deg,hsla(0,0%,100%,.15),hsla(0,0%,100%,0));
|
||||
--bs-root-font-size: 16px;
|
||||
--bs-body-font-family: var(--bs-font-sans-serif);
|
||||
--bs-body-font-size: 1rem;
|
||||
--bs-body-font-weight: 400;
|
||||
--bs-body-line-height: 1.5;
|
||||
--bs-body-color: #343a40;
|
||||
--bs-body-color-rgb: 52,58,64;
|
||||
--bs-body-bg: #f8f9fa;
|
||||
--bs-body-bg-rgb: 248,249,250;
|
||||
--bs-emphasis-color: #000;
|
||||
--bs-emphasis-color-rgb: 0,0,0;
|
||||
--bs-secondary-color: rgba(52,58,64,.75);
|
||||
--bs-secondary-color-rgb: 52,58,64;
|
||||
--bs-secondary-bg: #e9ecef;
|
||||
--bs-secondary-bg-rgb: 233,236,239;
|
||||
--bs-tertiary-color: rgba(52,58,64,.5);
|
||||
--bs-tertiary-color-rgb: 52,58,64;
|
||||
--bs-tertiary-bg: #f8f9fa;
|
||||
--bs-tertiary-bg-rgb: 248,249,250;
|
||||
--bs-heading-color: inherit;
|
||||
--bs-link-color: #1872a2;
|
||||
--bs-link-color-rgb: 24,114,162;
|
||||
--bs-link-decoration: underline;
|
||||
--bs-link-hover-color: #135b82;
|
||||
--bs-link-hover-color-rgb: 19,91,130;
|
||||
--bs-code-color: #d63384;
|
||||
--bs-highlight-color: #343a40;
|
||||
--bs-highlight-bg: #fff3cd;
|
||||
--bs-border-width: 1px;
|
||||
--bs-border-style: solid;
|
||||
--bs-border-color: #A08C78;
|
||||
--bs-border-color-translucent: rgba(0,0,0,.175);
|
||||
--bs-border-radius: 0.375rem;
|
||||
--bs-border-radius-sm: 0.25rem;
|
||||
--bs-border-radius-lg: 0.5rem;
|
||||
--bs-border-radius-xl: 1rem;
|
||||
--bs-border-radius-xxl: 2rem;
|
||||
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
|
||||
--bs-border-radius-pill: 50rem;
|
||||
--bs-box-shadow: 0 0.5rem 1rem rgba(0,0,0,.15);
|
||||
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0,0,0,.075);
|
||||
--bs-box-shadow-lg: 0 1rem 3rem rgba(0,0,0,.175);
|
||||
--bs-box-shadow-inset: inset 0 1px 2px rgba(0,0,0,.075);
|
||||
--bs-focus-ring-width: 0.25rem;
|
||||
--bs-focus-ring-opacity: 0.25;
|
||||
--bs-focus-ring-color: rgba(24,114,162,.25);
|
||||
--bs-form-valid-color: #059669;
|
||||
--bs-form-valid-border-color: #059669;
|
||||
--bs-form-invalid-color: #be123c;
|
||||
--bs-form-invalid-border-color: #be123c
|
||||
}
|
||||
|
||||
.navbar {
|
||||
background: rgb(var(--bs-primary-rgb));
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
background-color: #fff;
|
||||
border-color: rgb(var(--bs-primary-rgb));
|
||||
box-shadow: 0 0 0 .25rem rgba(24,114,162,.25);
|
||||
color: var(--bs-body-color);
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
|
||||
.navbar .navbar-brand {
|
||||
background: rgb(180,170,160);
|
||||
flex-shrink: 0;
|
||||
margin-right: 0;
|
||||
width: 256px;
|
||||
}
|
||||
|
||||
.sidebar>.nav>.nav-item>.nav-link:not(.collapsed) {
|
||||
background: rgb(var(--bs-primaryu-rgb));
|
||||
border-left: 3px solid #1872a2;
|
||||
padding-left: calc(1rem - 3px);
|
||||
}
|
||||
|
||||
.text-light {
|
||||
--bs-text-opacity: 1;
|
||||
color: rgba(var(--bs-gray-900),var(--bs-text-opacity))!important;
|
||||
}
|
||||
|
||||
.sidebar>.nav>.nav-item>.collapse, .sidebar>.nav>.nav-item>.collapsing {
|
||||
background: rgb(160,140,120);
|
||||
color: rgba(var(--bs-light),var(--bs-text-opacity))!important;
|
||||
}
|
||||
|
||||
img.header-logo {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
--bs-btn-color: #fff;
|
||||
--bs-btn-bg: rgb(180,170,160);
|
||||
--bs-btn-border-color: rgb(160,140,120);
|
||||
--bs-btn-hover-color: #000;
|
||||
--bs-btn-hover-bg: #fff;
|
||||
--bs-btn-hover-border-color: rgb(160,140,120);
|
||||
--bs-btn-focus-shadow-rgb: 59,135,176;
|
||||
--bs-btn-active-color: #fff;
|
||||
--bs-btn-active-bg: #135b82;
|
||||
--bs-btn-active-border-color: #12567a;
|
||||
--bs-btn-active-shadow: inset 0 3px 5px rgba(0,0,0,.125);
|
||||
--bs-btn-disabled-color: #fff;
|
||||
--bs-btn-disabled-bg: #1872a2;
|
||||
--bs-btn-disabled-border-color: #1872a2;
|
||||
}
|
||||
|
||||
|
||||
.btn-outline-primary {
|
||||
--bs-btn-color: #000;
|
||||
--bs-btn-border-color: rgb(160,140,120);
|
||||
--bs-btn-hover-color: #fff;
|
||||
--bs-btn-hover-bg: rgb(180,170,160);
|
||||
--bs-btn-hover-border-color: rgb(160,140,120);
|
||||
--bs-btn-focus-shadow-rgb: 24,114,162;
|
||||
--bs-btn-active-color: #fff;
|
||||
--bs-btn-active-bg: #1872a2;
|
||||
--bs-btn-active-border-color: #1872a2;
|
||||
--bs-btn-active-shadow: inset 0 3px 5px rgba(0,0,0,.125);
|
||||
--bs-btn-disabled-color: #1872a2;
|
||||
--bs-btn-disabled-bg: transparent;
|
||||
--bs-btn-disabled-border-color: #1872a2;
|
||||
--bs-gradient: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.alert, .card, .shadow-sm, .sub-sidebar {
|
||||
box-shadow: var(--bs-box-shadow)!important;
|
||||
}
|
||||
|
||||
|
||||
body.d-flex {
|
||||
display: flex!important;
|
||||
background-color: rgb(var(--bs-secondary-rgb))!important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
17
templates/Maketank/assets/css/dark.css
Normal file
17
templates/Maketank/assets/css/dark.css
Normal file
File diff suppressed because one or more lines are too long
17
templates/Maketank/assets/css/main.css
Normal file
17
templates/Maketank/assets/css/main.css
Normal file
File diff suppressed because one or more lines are too long
BIN
templates/Maketank/assets/img/logo.png
Normal file
BIN
templates/Maketank/assets/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.4 KiB |
3
templates/Maketank/assets/img/logo.svg
Normal file
3
templates/Maketank/assets/img/logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 6.0 KiB |
BIN
templates/Maketank/assets/img/logo_grey.png
Normal file
BIN
templates/Maketank/assets/img/logo_grey.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
0
templates/Maketank/assets/index.html
Normal file
0
templates/Maketank/assets/index.html
Normal file
2
templates/Maketank/assets/js/main.js
Normal file
2
templates/Maketank/assets/js/main.js
Normal file
File diff suppressed because one or more lines are too long
45
templates/Maketank/assets/js/main.js.LICENSE.txt
Normal file
45
templates/Maketank/assets/js/main.js.LICENSE.txt
Normal file
@@ -0,0 +1,45 @@
|
||||
/*!
|
||||
* Bootstrap v5.3.2 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
*/
|
||||
|
||||
/*!
|
||||
* @kurkle/color v0.2.1
|
||||
* https://github.com/kurkle/color#readme
|
||||
* (c) 2022 Jukka Kurkela
|
||||
* Released under the MIT License
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Chart.js v3.9.1
|
||||
* https://www.chartjs.org
|
||||
* (c) 2022 Chart.js Contributors
|
||||
* Released under the MIT License
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2023 Fonticons, Inc.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* jQuery JavaScript Library v3.7.1
|
||||
* https://jquery.com/
|
||||
*
|
||||
* Copyright OpenJS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://jquery.org/license
|
||||
*
|
||||
* Date: 2023-08-28T13:37Z
|
||||
*/
|
||||
|
||||
/*!
|
||||
* jQuery Validation Plugin v1.20.0
|
||||
*
|
||||
* https://jqueryvalidation.org/
|
||||
*
|
||||
* Copyright (c) 2023 Jörn Zaefferer
|
||||
* Released under the MIT license
|
||||
*/
|
||||
13
templates/Maketank/assets/mix-manifest.json
Normal file
13
templates/Maketank/assets/mix-manifest.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"/js/main.js": "/js/main.js?id=29451cc889431e973473738b93e6a626",
|
||||
"/css/dark.css": "/css/dark.css?id=f965da97d900604705c3762bb9cd645a",
|
||||
"/css/main.css": "/css/main.css?id=cbda89c88526530a66fe1e0d46a63a22",
|
||||
"/webfonts/fa-brands-400.ttf": "/webfonts/fa-brands-400.ttf?id=69e5d8e4e818f05fd882cceb758d1eba",
|
||||
"/webfonts/fa-brands-400.woff2": "/webfonts/fa-brands-400.woff2?id=189b85e9c72c6f75e464c3f58a6707cf",
|
||||
"/webfonts/fa-regular-400.ttf": "/webfonts/fa-regular-400.ttf?id=ed4c23399d1013809882e90bfe396d1b",
|
||||
"/webfonts/fa-regular-400.woff2": "/webfonts/fa-regular-400.woff2?id=be75b1958ae0da55e1eed562d9b7713d",
|
||||
"/webfonts/fa-solid-900.ttf": "/webfonts/fa-solid-900.ttf?id=dfdc7801582dd0d20ea75faa3b96c296",
|
||||
"/webfonts/fa-solid-900.woff2": "/webfonts/fa-solid-900.woff2?id=a0feb384c3c6071947a49708f2b0bc85",
|
||||
"/webfonts/fa-v4compatibility.ttf": "/webfonts/fa-v4compatibility.ttf?id=e24ec0b8661f7fa333b29444df39e399",
|
||||
"/webfonts/fa-v4compatibility.woff2": "/webfonts/fa-v4compatibility.woff2?id=e11465c0eff0549edd4e8ea6bbcf242f"
|
||||
}
|
||||
BIN
templates/Maketank/assets/webfonts/fa-brands-400.ttf
Normal file
BIN
templates/Maketank/assets/webfonts/fa-brands-400.ttf
Normal file
Binary file not shown.
BIN
templates/Maketank/assets/webfonts/fa-brands-400.woff2
Normal file
BIN
templates/Maketank/assets/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
templates/Maketank/assets/webfonts/fa-regular-400.ttf
Normal file
BIN
templates/Maketank/assets/webfonts/fa-regular-400.ttf
Normal file
Binary file not shown.
BIN
templates/Maketank/assets/webfonts/fa-regular-400.woff2
Normal file
BIN
templates/Maketank/assets/webfonts/fa-regular-400.woff2
Normal file
Binary file not shown.
BIN
templates/Maketank/assets/webfonts/fa-solid-900.ttf
Normal file
BIN
templates/Maketank/assets/webfonts/fa-solid-900.ttf
Normal file
Binary file not shown.
BIN
templates/Maketank/assets/webfonts/fa-solid-900.woff2
Normal file
BIN
templates/Maketank/assets/webfonts/fa-solid-900.woff2
Normal file
Binary file not shown.
BIN
templates/Maketank/assets/webfonts/fa-v4compatibility.ttf
Normal file
BIN
templates/Maketank/assets/webfonts/fa-v4compatibility.ttf
Normal file
Binary file not shown.
BIN
templates/Maketank/assets/webfonts/fa-v4compatibility.woff2
Normal file
BIN
templates/Maketank/assets/webfonts/fa-v4compatibility.woff2
Normal file
Binary file not shown.
39
templates/Maketank/base.html.twig
Normal file
39
templates/Maketank/base.html.twig
Normal file
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<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"/>
|
||||
<link rel="icon" type="image/x-icon" href="{{ basehref|default('') }}templates/Froxlor/assets/img/icon.png">
|
||||
{% if csrf_token %}<meta name="csrf-token" content="{{ csrf_token }}" />{% endif %}
|
||||
|
||||
<!-- CSS -->
|
||||
{% if theme_css is empty %}
|
||||
<link href="{{ basehref|default('') }}{{ mix('templates/Froxlor/assets/css/main.css') }}" rel="stylesheet" type="text/css" />
|
||||
{% else %}
|
||||
{{ theme_css|raw }}
|
||||
{% endif %}
|
||||
{% block custom_css %}{% endblock %}
|
||||
|
||||
<!-- Scripts -->
|
||||
{% if theme_js is empty %}
|
||||
<script type="text/javascript" src="{{ basehref|default('') }}{{ mix('templates/Froxlor/assets/js/main.js') }}"></script>
|
||||
{% else %}
|
||||
{{ theme_js|raw }}
|
||||
{% endif %}
|
||||
{% block custom_js %}{% endblock %}
|
||||
<title>Froxlor{% if page_title %} | {{ page_title }}{% endif %}</title>
|
||||
</head>
|
||||
<body class="min-vh-100 d-flex flex-column">
|
||||
{% block navigation %}{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="container-fluid">
|
||||
{% block content %}{% endblock %}
|
||||
{{ include('Froxlor/footer.html.twig') }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
35
templates/Maketank/config.json
Normal file
35
templates/Maketank/config.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"variants": {
|
||||
"default": {
|
||||
"img": {
|
||||
"login": "logo.svg",
|
||||
"ui": "logo_white.png"
|
||||
},
|
||||
"css": [
|
||||
"main.css",
|
||||
"custom.css"
|
||||
],
|
||||
"js": [
|
||||
"main.js",
|
||||
"apikey.js"
|
||||
],
|
||||
"description": "Default"
|
||||
},
|
||||
"dark": {
|
||||
"img": {
|
||||
"login": "logo.svg",
|
||||
"ui": "logo_white.png"
|
||||
},
|
||||
"css": [
|
||||
"dark.css",
|
||||
"custom.css"
|
||||
],
|
||||
"js": [
|
||||
"main.js",
|
||||
"apikey.js"
|
||||
],
|
||||
"description": "Darkmode"
|
||||
}
|
||||
},
|
||||
"author": "Maketank"
|
||||
}
|
||||
23
templates/Maketank/footer.html.twig
Normal file
23
templates/Maketank/footer.html.twig
Normal file
@@ -0,0 +1,23 @@
|
||||
<footer class="text-center mb-3">
|
||||
<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 area == 'login') or (area != 'login'
|
||||
and get_setting('admin.show_version_footer') == '1') %}
|
||||
{{ call_static('\\Froxlor\\Froxlor', 'getFullVersion') }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
© 2009-{{ "now"|date("Y") }} by <a href="https://www.froxlor.org/" rel="external" target="_blank">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/>
|
||||
<small class="mt-3">{{ lng('panel.translator') }}: {{ lng('translator') }}</small>
|
||||
{% endif %}
|
||||
</footer>
|
||||
56
templates/Maketank/form/form.html.twig
Normal file
56
templates/Maketank/form/form.html.twig
Normal file
@@ -0,0 +1,56 @@
|
||||
{% macro form(form_data, formaction, title = "", hiddenid = "", nosubmit = false, idprefix = "") %}
|
||||
|
||||
{% import "Froxlor/form/formfields.html.twig" as formfields %}
|
||||
|
||||
<form action="{{ formaction|default("") }}" {% if form_data.id is defined %}id="{{ form_data.id }}"{% endif %} method="post" enctype="multipart/form-data" class="form">
|
||||
{% for sid,section in form_data.sections %}
|
||||
{% if section.visible is not defined or (section.visible is defined and section.visible == true) %}
|
||||
<div class="card mb-3" id="{{ idprefix }}{{ sid }}">
|
||||
{% if section.title is not empty %}
|
||||
<div class="card-header">
|
||||
{% if section.image is not empty %}
|
||||
<i class="{{ section.image }}"></i>
|
||||
{% endif %}
|
||||
{{ section.title }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="formfields">
|
||||
{% for id,field in section.fields %}
|
||||
{{ formfields.fieldrow(id, field) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if nosubmit == false %}
|
||||
<!-- submit buttons -->
|
||||
<div>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"/>
|
||||
{% if hiddenid is not empty %}
|
||||
<input type="hidden" name="id" value="{{ hiddenid }}"/>
|
||||
{% endif %}
|
||||
<input type="hidden" name="page" value="{{ page }}"/>
|
||||
<input type="hidden" name="action" value="{{ action }}"/>
|
||||
<input type="hidden" name="send" value="send"/>
|
||||
|
||||
<div class="col-12 text-center mb-2 d-grid gap-2 d-md-block">
|
||||
{% if form_data.buttons is defined and form_data.buttons is iterable %}
|
||||
{% for btn in form_data.buttons %}
|
||||
<button type="{{ btn.type|default("submit") }}" class="btn btn-lg {{ btn.class|default(" btn-primary") }}">{{ btn.label }}</button>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<button type="reset" class="btn btn-lg btn-outline-secondary me-md-3">{{ lng('panel.reset') }}</button>
|
||||
<button type="submit" class="btn btn-lg btn-primary">{{ lng('panel.save') }}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<span class="text-danger">*</span> {{ lng('panel.mandatoryfield') }}
|
||||
</form>
|
||||
|
||||
{# add translation for custom validations #}
|
||||
{% if form_data.id is defined and form_data.id in ['customer_add', 'customer_edit', 'domain_add', 'domain_edit'] %}
|
||||
<script>$(function() { $.extend($.validator.messages, {required: "{{ lng('error.requiredfield') }}"}) });</script>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
250
templates/Maketank/form/formfields.html.twig
Normal file
250
templates/Maketank/form/formfields.html.twig
Normal file
@@ -0,0 +1,250 @@
|
||||
{% macro fieldrow(id, field, norow = false, nohide = false, em = false) %}
|
||||
{% if field.visible is not defined or (field.visible is defined and field.visible) or nohide == true %}
|
||||
{% if norow == false and (field.type != 'hidden' or (field.type == 'hidden' and field.display is defined and field.display is not empty)) %}
|
||||
<div class="row g-0 formfield d-flex align-items-center">
|
||||
{% if field.prior_infotext is defined and field.prior_infotext|length > 0 %}
|
||||
<h5>{{ field.prior_infotext }}</h5>
|
||||
{% endif %}
|
||||
{% if field.label is iterable %}
|
||||
<label for="{{ id }}" class="col-sm-6 col-form-label pe-3">
|
||||
{% if em %}
|
||||
<mark>
|
||||
{% endif %}
|
||||
{{ field.label.title|raw }}
|
||||
{% if field.mandatory is defined and field.mandatory %}
|
||||
<span class="text-danger">*</span>
|
||||
{% endif %}
|
||||
{% if em %}
|
||||
</mark>
|
||||
{% endif %}
|
||||
{% if field.label.description is defined and field.label.description is not empty %}<br><small>{{ field.label.description|raw }}</small>
|
||||
{% endif %}
|
||||
{% if field.requires_reconf is defined and field.requires_reconf is not empty %}
|
||||
<div class="bg-info bg-opacity-25 rounded p-2 mt-2 d-flex align-items-center" role="alert">
|
||||
<i class="fa-solid fa-circle-exclamation me-2"></i><p class="mb-0">{{ lng('serversettings.requires_reconfiguration', [field.requires_reconf|join(', ')])|raw }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</label>
|
||||
{% else %}
|
||||
<label for="{{ id }}" class="col-sm-6 col-form-label pe-3">
|
||||
{% if em %}
|
||||
<mark>
|
||||
{% endif %}
|
||||
{{ field.label|raw }}
|
||||
{% if field.mandatory is defined and field.mandatory %}
|
||||
<span class="text-danger">*</span>
|
||||
{% endif %}
|
||||
{% if em %}
|
||||
</mark>
|
||||
{% endif %}
|
||||
{% if field.desc is defined and field.desc is not empty %}<br><small>{{ field.desc|raw }}</small>
|
||||
{% endif %}
|
||||
{% if field.requires_reconf is defined and field.requires_reconf is not empty %}
|
||||
<div class="bg-info bg-opacity-25 rounded p-2 mt-2 d-flex align-items-center" role="alert">
|
||||
<i class="fa-solid fa-circle-exclamation me-2"></i><p class="mb-0">{{ lng('serversettings.requires_reconfiguration', [field.requires_reconf|join(', ')])|raw }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</label>
|
||||
{% endif %}
|
||||
<div class="col-sm-6">
|
||||
{% endif %}
|
||||
{% if field.type == 'text' or field.type == 'password' or field.type == 'number' or field.type == 'file' or field.type == 'email' or field.type == 'url' or field.type == 'hidden' or field.type == 'date' or field.type == 'datetime-local' %}
|
||||
{{ _self.input(id, field) }}
|
||||
{% elseif field.type == 'textul' %}
|
||||
{{ _self.input_ul(id, field) }}
|
||||
{% elseif field.type == 'checkbox' %}
|
||||
{{ _self.bool(id, field) }}
|
||||
{% elseif field.type == 'checkrequired' %}
|
||||
{{ _self.chk_required(id, field) }}
|
||||
{% elseif field.type == 'select' %}
|
||||
{{ _self.select(id, field) }}
|
||||
{% elseif field.type == 'textarea' %}
|
||||
{{ _self.textarea(id, field) }}
|
||||
{% elseif field.type == 'label' %}
|
||||
{{ _self.plain(id, field) }}
|
||||
{% elseif field.type == 'link' %}
|
||||
{{ _self.link(id, field) }}
|
||||
{% elseif field.type == 'itemlist' %}
|
||||
{{ _self.itemlist(id, field) }}
|
||||
{% elseif field.type == 'infotext' %}
|
||||
{{ _self.infotext(id, field) }}
|
||||
{% elseif field.type == 'image' %}
|
||||
{{ _self.image(id, field) }}
|
||||
{% else %}
|
||||
<div class="alert alert-warning" role="alert">Unknown field-type
|
||||
{{ field.type }}</div>
|
||||
{% endif %}
|
||||
{% if field.note is defined and field.note is not empty %}
|
||||
<small class="text-info">{{ field.note|raw }}</small>
|
||||
{% endif %}
|
||||
|
||||
{% if norow == false and (field.type != 'hidden' or (field.type == 'hidden' and field.display is defined and field.display is not empty)) %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{# installation specific format #}
|
||||
{% macro field(id, field, norow = true, nohide = false, em = false) %}
|
||||
{% if field.type == 'checkbox' %}
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input type="hidden" value="0" name="{{ id }}"/>
|
||||
<input type="checkbox" {% if (field.visible is defined and field.visible == false) or (field.disabled is defined and field.disabled == true) %} disabled {% endif %} value="{{ field.value }}" id="{{ id }}" name="{{ id }}" class="form-check-input {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" {% if field.checked is defined and field.checked == 1 %} checked="checked" {% endif %}>
|
||||
<label for="{{ id }}" class="form-check-label">{{ field.label|raw }}</label>
|
||||
</div>
|
||||
{% elseif field.type == 'hidden' %}
|
||||
{{ _self.fieldrow(id, field, norow, nohide, em) }}
|
||||
{% else %}
|
||||
<div class="form-floating mb-3">
|
||||
{{ _self.fieldrow(id, field, norow, nohide, em) }}
|
||||
<label for="{{ id }}" class="form-label">{{ field.label|raw }}</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro bool(id, field) %}
|
||||
{% if field.is_array is defined and field.is_array == 1 and field.values is not empty %}
|
||||
{% for subfield in field.values %}
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" {% if (field.visible is defined and field.visible == false) or (field.disabled is defined and field.disabled == true) %} disabled {% endif %} value="{{ subfield.value }}" name="{{ id }}[]" class="form-check-input" {% if field.value is defined and subfield.value in field.value %} checked="checked" {% endif %} {% if field.mandatory is defined and field.mandatory %} required {% endif %}>
|
||||
<label class="form-check-label">
|
||||
{{ subfield.label|raw }}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="form-check form-switch">
|
||||
<input type="hidden" value="0" name="{{ id }}"/>
|
||||
<input type="checkbox" {% if (field.visible is defined and field.visible == false) or (field.disabled is defined and field.disabled == true) %} disabled {% endif %} value="{{ field.value }}" id="{{ id }}" name="{{ id }}" class="form-check-input {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" {% if field.checked is defined and field.checked == 1 %} checked="checked" {% endif %}>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro chk_required(id, field) %}
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" value="{{ field.value }}" id="{{ id }}" name="{{ id }}" class="form-check-input" {% if field.mandatory is defined and field.mandatory == 1 %} required {% endif %} />
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro infotext(id, field) %}
|
||||
{% if field.next_to is defined %}
|
||||
<div class="input-group">
|
||||
{% endif %}
|
||||
<span {% if field.classes is defined %} class="{{ field.classes }}" {% endif %}>{{ field.value|raw }}</span>
|
||||
{% if field.next_to is defined %}
|
||||
{% for nid, nfield in field.next_to %}
|
||||
{% if nfield.next_to_prefix is defined %}
|
||||
<span class="input-group-text">{{ nfield.next_to_prefix }}</span>
|
||||
{% endif %}
|
||||
{{ _self.fieldrow(nid, nfield, true) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro plain(id, field) %}
|
||||
<input type="text" readonly class="form-control-plaintext" id="{{ id }}" name="{{ id }}" value="{{ field.value|raw }}">
|
||||
{% if field.next_to is defined %}
|
||||
{% for nid, nfield in field.next_to %}
|
||||
{% if nfield.next_to_prefix is defined %}
|
||||
<span class="input-group-text">{{ nfield.next_to_prefix }}</span>
|
||||
{% endif %}
|
||||
{{ _self.fieldrow(nid, nfield, true) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro input(id, field) %}
|
||||
{% if field.next_to is defined %}
|
||||
<div class="input-group">
|
||||
{% endif %}
|
||||
<input type="{{ field.type }}" {% if field.visible is defined and field.visible == false %} disabled {% endif %} {% if field.type == 'number' and field.min is defined %} min="{{ field.min }}" {% endif %} {% if field.type == 'number' and field.max is defined %} max="{{ field.max }}" {% endif %} {% if field.type != 'number' and field.maxlength is defined %} maxlength="{{ field.maxlength }}" {% endif %} id="{{ id }}" name="{{ id }}" value="{{ field.value|raw }}" class="form-control {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" {% if field.mandatory is defined and field.mandatory %} required {% endif %} {% if field.readonly is defined and field.readonly %} readonly {% endif %} {% if field.autocomplete is defined %} autocomplete="{{ field.autocomplete }}" {% endif %} {% if field.placeholder is defined %} placeholder="{{ field.placeholder }}" {% endif %} {% if field.type == 'file' and field.accept is defined %} accept="{{ field.accept }}" {% endif %} {% if field.pattern is defined %} pattern="{{ field.pattern }}" {% endif %}/>
|
||||
{% if field.type == 'hidden' and field.display is defined %}
|
||||
<input type="text" readonly class="form-control-plaintext" value="{{ field.display|raw }}">
|
||||
{% endif %}
|
||||
{% if field.next_to is defined %}
|
||||
{% for nid, nfield in field.next_to %}
|
||||
{% if nfield.next_to_prefix is defined %}
|
||||
<span class="input-group-text">{{ nfield.next_to_prefix }}</span>
|
||||
{% endif %}
|
||||
{{ _self.fieldrow(nid, nfield, true) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro image(id, field) %}
|
||||
{% if field.value is not empty %}
|
||||
<img src="{{ field.value }}" alt="Current Image" class="field-image-preview"><br>
|
||||
<div class="form-check form-switch mb-2">
|
||||
<input type="checkbox" value="1" name="{{ id }}_delete" class="form-check-input">
|
||||
<label class="form-check-label">
|
||||
{{ lng('panel.image_field_delete') }}
|
||||
</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% set field = field|merge({'type':'file'}) %}
|
||||
{{ _self.input(id, field) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro input_ul(id, field) %}
|
||||
{% set max = "" %}
|
||||
{% if field.maxlength is defined %}
|
||||
{% for i in 1..field.maxlength %}
|
||||
{% set max = max ~ "9" %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<div class="input-group">
|
||||
<input type="number" min="0" {% if max is not empty %} max="{{ max }}" {% endif %} id="{{ id }}" name="{{ id }}" value="{% if field.value >= 0 %}{{ field.value }}{% endif %}" class="form-control {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" {% if field.mandatory is defined and field.mandatory %} required {% endif %} {% if field.readonly is defined and field.readonly %} readonly {% endif %} {% if field.autocomplete is defined %} autocomplete="{{ field.autocomplete }}" {% endif %} {% if field.placeholder is defined %} placeholder="{{ field.placeholder }}" {% endif %}/>
|
||||
<div class="input-group-text">
|
||||
<input class="form-check-input mt-0" type="checkbox" name="{{ id }}_ul" value="1" {% if field.value == -1 %} checked="checked" {% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro select(id, field) %}
|
||||
{% if field.next_to is defined %}
|
||||
<div class="input-group">
|
||||
{% endif %}
|
||||
<select {% if field.visible is defined and field.visible == false %} disabled {% endif %} class="form-select {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" name="{{ id }}{% if field.select_mode is defined and field.select_mode == 'multiple' %}[]{% endif %}" id="{{ id }}" {% if field.mandatory is defined and field.mandatory %} required {% endif %} {% if field.select_mode is defined and field.select_mode == 'multiple' %} multiple="multiple" {% endif %}{% if field.readonly is defined and field.readonly %} readonly {% endif %}>
|
||||
{% for val,txt in field.select_var %}
|
||||
<option value="{{ val }}" {% if field.selected is defined and ((field.selected is not iterable and field.selected == val) or (field.selected is iterable and val in field.selected|keys)) %} selected="selected" {% endif %}>{{ txt|raw }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if field.next_to is defined %}
|
||||
{% for nid, nfield in field.next_to %}
|
||||
{% if nfield.next_to_prefix is defined %}
|
||||
<span class="input-group-text">{{ nfield.next_to_prefix }}</span>
|
||||
{% endif %}
|
||||
{{ _self.fieldrow(nid, nfield, true) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro textarea(id, field) %}
|
||||
<textarea {% if field.visible is defined and field.visible == false %} disabled {% endif %} rows="{{ field.rows|default('12') }}" cols="{{ field.cols|default('60') }}" id="{{ id }}" name="{{ id }}" class="form-control {% if field.valid is defined and field.valid == false %}is-invalid{% endif %}" {% if field.mandatory is defined and field.mandatory %} required {% endif %} {% if field.readonly is defined and field.readonly %} readonly {% endif %} {% if field.placeholder is defined %} placeholder="{{ field.placeholder }}" {% endif %} {% if field.style is defined %} style="{{ field.style }}" {% endif %}>{{ field.value|raw }}</textarea>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro link(id, field) %}
|
||||
<a href="{{ field.href|raw }}" class="{{ field.classes }}">{{ field.label|raw }}</a>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro itemlist(id, field) %}
|
||||
{% if field.values is not empty %}
|
||||
{% for value in field.values %}
|
||||
<p>{{ value.item|raw }}
|
||||
{% if value.href is defined and value.href is not empty %}
|
||||
{{ _self.link(id, value) }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if field.next_to is defined %}
|
||||
{% for nid, nfield in field.next_to %}
|
||||
{{ _self.fieldrow(nid, nfield, true) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
0
templates/Maketank/form/index.html
Normal file
0
templates/Maketank/form/index.html
Normal file
37
templates/Maketank/form/yesnoquestion.html.twig
Normal file
37
templates/Maketank/form/yesnoquestion.html.twig
Normal file
@@ -0,0 +1,37 @@
|
||||
{% extends "Froxlor/userarea.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<form action="{{ action|default("") }}" method="post" enctype="application/x-www-form-urlencoded" class="form">
|
||||
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<h4 class="alert-heading">{{ lng('panel.security_question') }}</h4>
|
||||
<p>{{ question|raw }}</p>
|
||||
{% if with_checkbox is defined and with_checkbox is iterable %}
|
||||
{% if with_checkbox.show %}
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="delete_userfiles" name="delete_userfiles" value="1">
|
||||
<label class="form-check-label" for="delete_userfiles">{{ with_checkbox.chk_text|raw }}</label>
|
||||
</div>
|
||||
{% else %}
|
||||
<input type="hidden" name="delete_userfiles" value="0"/>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<p>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"/>
|
||||
<input type="hidden" name="send" value="send"/>
|
||||
{% for id,field in url_params %}
|
||||
<input type="hidden" name="{{ id }}" value="{{ field }}"/>
|
||||
{% endfor %}
|
||||
<button class="btn btn-danger" type="submit" name="submitbutton">{{ lng('panel.yes') }}</button>
|
||||
{% if back_link is defined and back_link is iterable and back_link|length > 0 %}
|
||||
<a href="{{ linker(back_link) }}" class="btn btn-secondary">{{ lng('panel.no') }}</a>
|
||||
{% else %}
|
||||
<a href="javascript:history.back(-1)" class="btn btn-secondary">{{ lng('panel.no') }}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
0
templates/Maketank/index.html
Normal file
0
templates/Maketank/index.html
Normal file
89
templates/Maketank/install/form.html.twig
Normal file
89
templates/Maketank/install/form.html.twig
Normal file
@@ -0,0 +1,89 @@
|
||||
<!-- 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') }} »
|
||||
</button>
|
||||
</aside>
|
||||
</form>
|
||||
0
templates/Maketank/install/index.html
Normal file
0
templates/Maketank/install/index.html
Normal file
126
templates/Maketank/install/index.html.twig
Normal file
126
templates/Maketank/install/index.html.twig
Normal file
@@ -0,0 +1,126 @@
|
||||
{% extends "Froxlor/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container max-w-lg flex align-content-center mt-5">
|
||||
<img src="{{ basehref|default('') }}templates/Froxlor/assets/img/logo.png" class="mb-5" alt="{{ lng('install.slogan') }}"/>
|
||||
|
||||
{% if error is not null %}
|
||||
<div class="alert alert-danger mb-4">{{ error }}</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row text-center gx-0">
|
||||
<div class="col p-3{{ setup.step == 0 ? ' bg-white shadow rounded-top' : '' }}">
|
||||
<i class="far fa-circle{{ setup.step > 0 ? '-check' : '' }}"></i>
|
||||
{% if setup.step > 0 %}<a href="?step=0" class="text-decoration-none">{{ lng('install.preflight') }}</a>{% else %}{{ lng('install.preflight') }}{% endif %}
|
||||
</div>
|
||||
<div class="col p-3{{ setup.step == 1 ? ' bg-white shadow rounded-top' : '' }}">
|
||||
<i class="far fa-circle{{ setup.step > 1 ? '-check' : '' }}"></i>
|
||||
{% if setup.step > 1 %}<a href="?step=1" class="text-decoration-none">{{ lng('install.database.top') }}</a>{% else %}{{ lng('install.database.top') }}{% endif %}
|
||||
</div>
|
||||
<div class="col p-3{{ setup.step == 2 ? ' bg-white shadow rounded-top' : '' }}">
|
||||
<i class="far fa-circle{{ setup.step > 2 ? '-check' : '' }}"></i>
|
||||
{% if setup.step > 2 %}<a href="?step=2" class="text-decoration-none">{{ lng('install.admin.top') }}</a>{% else %}{{ lng('install.admin.top') }}{% endif %}
|
||||
</div>
|
||||
<div class="col p-3{{ setup.step == 3 ? ' bg-white shadow rounded-top' : '' }}">
|
||||
<i class="far fa-circle{{ setup.step > 3 ? '-check' : '' }}"></i>
|
||||
{% if setup.step > 3 %}<a href="?step=3" class="text-decoration-none">{{ lng('install.system.top') }}</a>{% else %}{{ lng('install.system.top') }}{% endif %}
|
||||
</div>
|
||||
<div class="col p-3{{ setup.step == 4 ? ' bg-white shadow rounded-top' : '' }}">
|
||||
<i class="far fa-circle{{ setup.step > 4 ? '-check' : '' }}"></i>
|
||||
{% if setup.step > 4 %}<a href="?step=4" class="text-decoration-none">{{ lng('install.install.top') }}</a>{% else %}{{ lng('install.install.top') }}{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card border-0 shadow">
|
||||
<div class="card-body p-5">
|
||||
<form method="post" action="?step={{ setup.step }}">
|
||||
{% if setup.step > 0 %}
|
||||
<div class="d-block d-lg-flex justify-content-between align-items-center mb-3">
|
||||
<h4 class="mb-3 mb-lg-0">{{ section.title }}</h4>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="switchInstallMode" {% if extended is defined and extended %}checked{% endif %}>
|
||||
<label class="form-check-label" for="switchInstallMode">{% if extended is defined and extended %}{{ lng('install.switchmode_basic') }}{% else %}{{ lng('install.switchmode_advanced') }}{% endif %}</label>
|
||||
</div>
|
||||
</div>
|
||||
<p class="lead">{{ section.description|raw }}</p>
|
||||
<hr />
|
||||
|
||||
{% import "Froxlor/form/formfields.html.twig" as formfields %}
|
||||
|
||||
{% for id, field in section.fields %}
|
||||
{% if field.advanced is defined and field.advanced == true and extended == false %}
|
||||
{# hide advanced fields #}
|
||||
{% set field = field|merge({'type': 'hidden'}) %}
|
||||
{% endif %}
|
||||
{{ formfields.field(id, field) }}
|
||||
{% endfor %}
|
||||
|
||||
<div class="d-flex {% if setup.step < setup.max_steps %}justify-content-between{% else %}justify-content-end{% endif %} mt-4">
|
||||
{% if setup.step < setup.max_steps %}
|
||||
<a href="?step={{ setup.step - 1 }}" class="btn btn-secondary">« {{ lng('panel.back') }}</a>
|
||||
<button type="submit" name="submit" class="btn btn-primary">{{ lng('panel.next') }} »</button>
|
||||
{% else %}
|
||||
<span id="submitAuto"><i class="fas fa-spinner fa-pulse"></i> {{ lng('install.install.waitforconfig') }}</span>
|
||||
<button id="submitManual" type="submit" name="submit" class="btn btn-success d-none">{{ lng('install.install.top') }} »</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<h4 class="mb-3">{{ lng('install.dependency_check.title') }}</h4>
|
||||
<p class="lead">{{ lng('install.dependency_check.description') }}</p>
|
||||
|
||||
<p class="lead {{ preflight.criticals ? 'text-danger' : preflight.suggestions ? 'text-warning' : 'text-success'}}">
|
||||
<i class="{{ preflight.criticals ? 'fa-solid fa-triangle-exclamation' : preflight.suggestions ? 'fa-solid fa-circle-info' : 'far fa-circle-check' }}"></i>
|
||||
{{ preflight.text }}
|
||||
</p>
|
||||
|
||||
{% if preflight.criticals %}
|
||||
<p class="text-muted">{{ lng('install.critical_error') }}</p>
|
||||
<ul>
|
||||
{% for ctype, critical in preflight.criticals %}
|
||||
{% if ctype == 'wrong_ownership' %}
|
||||
<li>{{ lng('install.errors.' ~ ctype, [critical.user, critical.group]) }}</li>
|
||||
{% elseif ctype == 'missing_extensions' %}
|
||||
<li>{{ lng('install.errors.' ~ ctype) }}<ul>
|
||||
{% for missext in critical %}
|
||||
<li>{{ missext }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% else %}
|
||||
<li>{{ critical|raw }}</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if preflight.suggestions %}
|
||||
<p class="text-muted">{{ lng('install.suggestions') }}</p>
|
||||
<ul>
|
||||
{% for ctype, suggestion in preflight.suggestions %}
|
||||
{% if ctype == 'missing_extensions' %}
|
||||
<li>{{ lng('install.errors.suggestedextensions') }}<ul>
|
||||
{% for missext in suggestion %}
|
||||
<li>{{ missext }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% else %}
|
||||
<li>{{ suggestion|raw }}</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
<div class="d-flex justify-content-end mt-4">
|
||||
{% if preflight.criticals %}
|
||||
<a href="" class="btn btn-secondary"><i class="fa-solid fa-arrow-rotate-left"></i> {{ lng('install.check_again') }}</a>
|
||||
{% else %}
|
||||
<a href="?step=1" class="btn btn-primary">{{ lng('install.start_installation') }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
48
templates/Maketank/install/update.html.twig
Normal file
48
templates/Maketank/install/update.html.twig
Normal file
@@ -0,0 +1,48 @@
|
||||
{% extends "Froxlor/userarea.html.twig" %}
|
||||
|
||||
{% block heading %}
|
||||
<div>
|
||||
<h5 class="mb-1">
|
||||
<i class="fa-solid fa-download me-1"></i>
|
||||
{{ lng('update.update') }}
|
||||
</h5>
|
||||
<span class="text-muted">{{ lng('update.description') }}</span>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card table-responsive">
|
||||
<div class="card-body">
|
||||
<table class="table table-borderless align-middle mb-0 px-3">
|
||||
<tbody>
|
||||
{% 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 %} <i class="fa-solid fa-check-circle" {% elseif check.result == 2 %}<span class="d-md-none"> ???</span>{% elseif check.result == 1 %}<span class="d-md-none"> !!!</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if check.result_desc is not empty %}
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<span>{{ check.result_desc|raw }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="row pt-md-3">
|
||||
<div class="col-12 text-end mt-4 mt-md-0">
|
||||
<a class="btn btn-lg btn-block btn-primary" href="admin_index.php">
|
||||
{{ lng('success.clickheretocontinue') }}
|
||||
»
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
36
templates/Maketank/login/enter2fa.html.twig
Normal file
36
templates/Maketank/login/enter2fa.html.twig
Normal file
@@ -0,0 +1,36 @@
|
||||
{% extends "Froxlor/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<form action="index.php" class="col-12 max-w-420 d-flex flex-column" method="post" enctype="application/x-www-form-urlencoded">
|
||||
<img class="align-self-center my-5" src="{{ header_logo_login }}" alt="Froxlor Server Management Panel"/>
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ pagetitle }}</h5>
|
||||
|
||||
{% if message is not empty %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h4 class="alert-heading">{{ lng('error.error') }}</h4>
|
||||
<p>{{ message|raw }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="2fa_code" class="col-form-label">{{ lng('login.2facode') }}</label>
|
||||
<input class="form-control" type="text" name="2fa_code" id="2fa_code" value="" autocomplete="off" autofocus required/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-body d-grid gap-2">
|
||||
<input type="hidden" name="action" value="2fa_verify"/>
|
||||
<input type="hidden" name="send" value="send"/>
|
||||
<button class="btn btn-primary rounded-top-0" type="submit" name="2faverify">{{ lng('2fa.2fa_verify') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
53
templates/Maketank/login/fpwd.html.twig
Normal file
53
templates/Maketank/login/fpwd.html.twig
Normal file
@@ -0,0 +1,53 @@
|
||||
{% extends "Froxlor/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<form action="{{ formaction }}" class="col-12 max-w-420 d-flex flex-column" method="post" enctype="application/x-www-form-urlencoded">
|
||||
<img class="align-self-center my-5" src="{{ header_logo_login }}" alt="Froxlor Server Management Panel"/>
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ pagetitle }}</h5>
|
||||
|
||||
{% if upd_in_progress %}
|
||||
<div class="alert alert-warning" role="alert">
|
||||
{{ lng('update.updateinprogress_onlyadmincanlogin')|raw }}
|
||||
</div>
|
||||
{% elseif successmsg is not empty %}
|
||||
<div class="alert alert-success" role="alert">
|
||||
<h4 class="alert-heading">{{ lng('success.success') }}</h4>
|
||||
<p>{{ successmsg|raw }}</p>
|
||||
</div>
|
||||
{% elseif message is not empty %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h4 class="alert-heading">{{ lng('error.error') }}</h4>
|
||||
<p>{{ message|raw }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="loginname" class="col-form-label">{{ lng('login.username') }}</label>
|
||||
<input class="form-control" type="text" name="loginname" id="loginname" value="" required autofocus/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="loginemail" class="col-form-label">{{ lng('login.email') }}</label>
|
||||
<input class="form-control" type="email" name="loginemail" id="loginemail" value="" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body d-grid gap-2">
|
||||
<button class="btn btn-primary rounded-top-0" type="submit" name="doremind">{{ lng('login.remind') }}</button>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<a class="card-link text-muted" href="index.php">
|
||||
<i class="fa-solid fa-angles-left"></i>
|
||||
{{ lng('login.backtologin') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
0
templates/Maketank/login/index.html
Normal file
0
templates/Maketank/login/index.html
Normal file
54
templates/Maketank/login/login.html.twig
Normal file
54
templates/Maketank/login/login.html.twig
Normal file
@@ -0,0 +1,54 @@
|
||||
{% extends "Froxlor/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<form class="col-12 max-w-420 d-flex flex-column" method="post" enctype="application/x-www-form-urlencoded">
|
||||
<img class="align-self-center my-5" src="{{ header_logo_login }}" alt="Froxlor Server Management Panel"/>
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ pagetitle }}</h5>
|
||||
<p>{{ lng('login.welcomemsg') }}</p>
|
||||
|
||||
{% if upd_in_progress %}
|
||||
<div class="alert alert-warning" role="alert">
|
||||
{{ lng('update.updateinprogress_onlyadmincanlogin')|raw }}
|
||||
</div>
|
||||
{% elseif successmsg is not empty %}
|
||||
<div class="alert alert-success" role="alert">
|
||||
<h4 class="alert-heading">{{ lng('success.success') }}</h4>
|
||||
<p>{{ successmsg|raw }}</p>
|
||||
</div>
|
||||
{% elseif message is not empty %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h4 class="alert-heading">{{ lng('error.error') }}</h4>
|
||||
<p>{{ message|raw }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="loginname" class="col-form-label">{{ lng('login.username') }}</label>
|
||||
<input class="form-control" type="text" name="loginname" id="loginname" value="" required autofocus/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="col-form-label">{{ lng('login.password') }}</label>
|
||||
<input class="form-control" type="password" name="password" id="password" value="" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body d-grid gap-2">
|
||||
<button class="btn btn-primary rounded-top-0" type="submit" name="dologin">{{ lng('login.login') }}</button>
|
||||
</div>
|
||||
|
||||
{% if get_setting('panel.allow_preset') == '1' %}
|
||||
<div class="card-footer">
|
||||
<a class="card-link text-muted" href="index.php?action=forgotpwd">{{ lng('login.forgotpwd') }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
45
templates/Maketank/login/rpwd.html.twig
Normal file
45
templates/Maketank/login/rpwd.html.twig
Normal file
@@ -0,0 +1,45 @@
|
||||
{% extends "Froxlor/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<form action="{{ formaction }}" class="col-12 max-w-420 d-flex flex-column" method="post" enctype="application/x-www-form-urlencoded">
|
||||
<img class="align-self-center my-5" src="{{ header_logo_login }}" alt="Froxlor Server Management Panel"/>
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ pagetitle }}</h5>
|
||||
<p>{{ lng('login.presend') }}</p>
|
||||
|
||||
{% if message is not empty %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<h4 class="alert-heading">{{ lng('error.error') }}</h4>
|
||||
<p>{{ message|raw }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="new_password" class="col-form-label">{{ lng('changepassword.new_password') }}</label>
|
||||
<input class="form-control" type="password" name="new_password" id="new_password" value="" required autofocus/>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="new_password_confirm" class="col-form-label">{{ lng('changepassword.new_password_confirm') }}</label>
|
||||
<input class="form-control" type="password" name="new_password_confirm" id="new_password_confirm" value="" required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body d-grid gap-2">
|
||||
<button class="btn btn-primary rounded-top-0" type="submit" name="doremind">{{ lng('login.remind') }}</button>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<a class="card-link text-muted" href="index.php">
|
||||
<i class="fa-solid fa-angles-left"></i>
|
||||
{{ lng('login.backtologin') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
5
templates/Maketank/misc/alert.html.twig
Normal file
5
templates/Maketank/misc/alert.html.twig
Normal file
@@ -0,0 +1,5 @@
|
||||
{% extends "Froxlor/userarea.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'Froxlor/misc/alertbox.html.twig' %}
|
||||
{% endblock %}
|
||||
5
templates/Maketank/misc/alert_nosession.html.twig
Normal file
5
templates/Maketank/misc/alert_nosession.html.twig
Normal file
@@ -0,0 +1,5 @@
|
||||
{% extends "Froxlor/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
{% include 'Froxlor/misc/alertbox.html.twig' %}
|
||||
{% endblock %}
|
||||
27
templates/Maketank/misc/alertbox.html.twig
Normal file
27
templates/Maketank/misc/alertbox.html.twig
Normal file
@@ -0,0 +1,27 @@
|
||||
<div class="alert alert-{{ type|default("info") }} fade show" role="alert">
|
||||
{% if heading is defined and heading is not empty %}
|
||||
<h4 class="alert-heading">
|
||||
{{ heading }}
|
||||
</h4>
|
||||
{% endif %}
|
||||
<p>
|
||||
{{ alert_msg|raw }}
|
||||
</p>
|
||||
{% if alert_info %}
|
||||
<hr>
|
||||
<p class="mb-0">
|
||||
<pre>{{ alert_info|raw }}</pre>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if redirect_link %}
|
||||
<p>
|
||||
<a href="{{ redirect_link|raw }}" class="btn btn-{{ btntype }}">
|
||||
{% if type == 'danger' %}
|
||||
{{ lng('panel.back') }}
|
||||
{% else %}
|
||||
{{ lng('success.clickheretocontinue') }}
|
||||
{% endif %}
|
||||
</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
22
templates/Maketank/misc/configurehint.html.twig
Normal file
22
templates/Maketank/misc/configurehint.html.twig
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends "Froxlor/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container max-w-lg flex align-content-center mt-5">
|
||||
<img src="templates/Froxlor/assets/img/logo.png" alt="Froxlor Server Management Panel" />
|
||||
|
||||
<div class="row gx-0 rounded shadow bg-primary text-white mt-5">
|
||||
<div class="col p-5 rounded-start">
|
||||
<h2 class="card-title">Welcome to Froxlor</h2>
|
||||
<p class="lead mt-5">It seems that Froxlor has not been installed yet.</p>
|
||||
<p class="lead">Click on the button below to start the installation.</p>
|
||||
</div>
|
||||
|
||||
<div class="col text-white position-relative">
|
||||
<img class="h-75 position-absolute bottom-0 end-0 rounded-tl-bl" src="{{ basehref }}templates/Froxlor/assets/img/preview.jpg">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 text-end">
|
||||
<a class="btn btn-lg btn-primary" href="./install/install.php" title="Click to start the install process">Start install</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
26
templates/Maketank/misc/dberrornice.html.twig
Normal file
26
templates/Maketank/misc/dberrornice.html.twig
Normal file
@@ -0,0 +1,26 @@
|
||||
{% extends "Froxlor/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-auto">
|
||||
<div class="alert alert-danger fade show" role="alert">
|
||||
<h4 class="alert-heading">
|
||||
A database error occurred
|
||||
</h4>
|
||||
<p>
|
||||
{{ message }}
|
||||
</p>
|
||||
{% if debug is not empty %}
|
||||
<hr>
|
||||
<p class="mb-0">
|
||||
<pre>{{ debug }}</pre>
|
||||
</p>
|
||||
{% endif %}
|
||||
<p class="mt-1 text-center">
|
||||
<a href="#" class="btn btn-primary" title="Click here to go back" id="historyback">Go back</a>
|
||||
{% if report is not empty %}
|
||||
<a href="{{ report|raw }}" class="btn btn-warning" title="Click here to report error">Report error</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
0
templates/Maketank/misc/index.html
Normal file
0
templates/Maketank/misc/index.html
Normal file
18
templates/Maketank/misc/ownershiphint.html.twig
Normal file
18
templates/Maketank/misc/ownershiphint.html.twig
Normal file
@@ -0,0 +1,18 @@
|
||||
{% extends "Froxlor/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<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>
|
||||
<pre>chown -R {{ user }}:{{ group }} {{ installdir }}</pre>
|
||||
<hr>
|
||||
<p class="mt-1 text-center">
|
||||
<a href="" class="btn btn-primary" title="Reload page">Reload</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
43
templates/Maketank/misc/phprequirementfailed.html.twig
Normal file
43
templates/Maketank/misc/phprequirementfailed.html.twig
Normal file
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<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" />
|
||||
|
||||
<!-- CSS -->
|
||||
<link href="{{ basehref }}templates/Froxlor/assets/css/main.css" rel="stylesheet">
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="{{ basehref }}templates/Froxlor/assets/js/main.js"></script>
|
||||
|
||||
<title>Froxlor - Error</title>
|
||||
</head>
|
||||
<body class="min-vh-100 d-flex align-items-center">
|
||||
<div class="container-fluid">
|
||||
<div class="container max-w-lg">
|
||||
<div class="card bg-danger text-white">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">
|
||||
Whoops!
|
||||
</h4>
|
||||
<p>It seems you are using an older version of PHP</p>
|
||||
<p>Froxlor requires at least PHP version {{ froxlor_min_version }}</p>
|
||||
<p>The installed version is: {{ current_version }}</p>
|
||||
</div>
|
||||
<div class="card-footer text-end">
|
||||
<a href="" class="btn btn-primary" title="Click to refresh">Refresh</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="pý-5 text-center">
|
||||
<span>
|
||||
<img src="{{ basehref }}templates/Froxlor/assets/img/logo_grey.png" alt="Froxlor" />
|
||||
© 2009-{{ current_year }} by <a href="https://www.froxlor.org/" rel="external">the Froxlor Team</a>
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
17
templates/Maketank/misc/ratelimithint.html.twig
Normal file
17
templates/Maketank/misc/ratelimithint.html.twig
Normal file
@@ -0,0 +1,17 @@
|
||||
{% extends "Froxlor/base.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container my-auto">
|
||||
<div class="alert alert-warning fade show" role="alert">
|
||||
<h4 class="alert-heading">
|
||||
Whoops!
|
||||
</h4>
|
||||
<p>It seems like you've hit the rate limit.</p>
|
||||
<p>Please slow down your requests and retry after {{ retry|date('d.m.Y H:i:s') }}</p>
|
||||
<hr>
|
||||
<p class="mt-1 text-center">
|
||||
<a href="" class="btn btn-primary" title="Reload page">Reload</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
43
templates/Maketank/misc/vendormissinghint.html.twig
Normal file
43
templates/Maketank/misc/vendormissinghint.html.twig
Normal file
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<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" />
|
||||
|
||||
<!-- CSS -->
|
||||
<link href="{{ basehref }}templates/Froxlor/assets/css/main.css" rel="stylesheet">
|
||||
|
||||
<!-- Scripts -->
|
||||
<script src="{{ basehref }}templates/Froxlor/assets/js/main.js"></script>
|
||||
|
||||
<title>Froxlor - Error</title>
|
||||
</head>
|
||||
<body class="min-vh-100 d-flex align-items-center">
|
||||
<div class="container-fluid">
|
||||
<div class="container max-w-lg">
|
||||
<div class="card bg-danger text-white">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">
|
||||
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>
|
||||
<pre>cd {{ froxlor_install_dir }} && composer install --no-dev</pre>
|
||||
</div>
|
||||
<div class="card-footer text-end">
|
||||
<a href="" class="btn btn-primary" title="Click to refresh">Refresh</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="py-5 text-center">
|
||||
<span>
|
||||
<img src="{{ basehref }}templates/Froxlor/assets/img/logo_grey.png" alt="Froxlor" />
|
||||
© 2009-{{ current_year }} by <a href="https://www.froxlor.org/" rel="external">the Froxlor Team</a>
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
26
templates/Maketank/misc/version_popover.html.twig
Normal file
26
templates/Maketank/misc/version_popover.html.twig
Normal file
@@ -0,0 +1,26 @@
|
||||
{% macro vpopover(isnewerversion, additional_info, full_version, dbversion, channel, last_update_check, message) %}
|
||||
{% if isnewerversion == 0 %}
|
||||
<p>{{ additional_info }}</p>
|
||||
<div class='d-flex justify-content-between'>
|
||||
<div class='fw-bold'>Version:</div>
|
||||
<div>{{ full_version }}</div>
|
||||
</div>
|
||||
<div class='d-flex justify-content-between'>
|
||||
<div class='fw-bold'>Database version:</div>
|
||||
<div>{{ dbversion }}</div>
|
||||
</div>
|
||||
<div class='d-flex justify-content-between'>
|
||||
<div class='fw-bold'>Channel:</div>
|
||||
<div>{{ channel }}</div>
|
||||
</div>
|
||||
<div class='d-flex justify-content-between'>
|
||||
<div class='fw-bold'>Last checked:</div>
|
||||
<div>{{ last_update_check|date('d.m.Y H:i') }}</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>{{ message }}</p>
|
||||
{% if get_config('enable_webupdate') %}
|
||||
<a class='btn d-block btn-outline-warning' href='admin_autoupdate.php?page=overview'>Open updater</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
12
templates/Maketank/misc/version_top.html.twig
Normal file
12
templates/Maketank/misc/version_top.html.twig
Normal file
@@ -0,0 +1,12 @@
|
||||
{% import "Froxlor/misc/version_popover.html.twig" as vc %}
|
||||
<span id="ucheck" class="nav-link {% if isnewerversion == 0 and aucheck < 0 %}text-muted{% elseif isnewerversion == 0 %}text-success{% else %}text-warning{% endif %}"
|
||||
data-bs-container="body" data-bs-toggle="popover" data-bs-placement="bottom" data-bs-trigger="hover focus click" data-bs-html="true"
|
||||
data-bs-content="{{ vc.vpopover(isnewerversion, additional_info, full_version, dbversion, channel, last_update_check, message) }}"
|
||||
>
|
||||
{% if isnewerversion == 0 and aucheck == 0 %}
|
||||
<i class="fa-solid fa-circle-check me-1"></i>
|
||||
{% else %}
|
||||
<i class="fa-solid fa-circle-exclamation me-1"></i>
|
||||
{% endif %}
|
||||
<span class="d-none d-xl-inline">{{ version }}</span>
|
||||
</span>
|
||||
176
templates/Maketank/settings/apcuinfo.html.twig
Normal file
176
templates/Maketank/settings/apcuinfo.html.twig
Normal file
@@ -0,0 +1,176 @@
|
||||
{% extends "Froxlor/userarea.html.twig" %}
|
||||
|
||||
{% block heading %}
|
||||
<h5>
|
||||
<i class="fa-solid fa-hard-drive me-1"></i>
|
||||
{{ lng('admin.apcuinfo') }}
|
||||
</h5>
|
||||
{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
<a class="btn btn-warning" href="{{ linker({'section':'apcuinfo','page':'showinfo','action':'delete'}) }}">
|
||||
<i class="fa-solid fa-trash-can me-1"></i>
|
||||
{{ lng('apcuinfo.clearcache') }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 mb-4">
|
||||
<div class="col">
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('apcuinfo.memnote') }}</h5>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ apcuinfo.mem_used_percentage }}%" aria-valuenow="{{ apcuinfo.mem_used }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.mem_avail }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ apcuinfo.mem_used_percentage }}%</small>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('apcuinfo.total') }}
|
||||
<span class="badge bg-secondary">{{ apcuinfo.readable.mem_size }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('apcuinfo.used') }}
|
||||
<span class="badge bg-secondary">{{ apcuinfo.readable.mem_used }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('apcuinfo.free') }}
|
||||
<span class="badge bg-secondary">{{ apcuinfo.readable.mem_avail }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('apcuinfo.hitmiss') }}</h5>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ apcuinfo.num_hits_percentage }}%" aria-valuenow="{{ apcuinfo.num_hits }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.num_hits_and_misses }}"></div>
|
||||
<div class="progress-bar bg-danger" role="progressbar" style="width: {{ 100 - apcuinfo.num_misses_percentage }}%" aria-valuenow="{{ apcuinfo.num_misses }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.num_hits_and_misses }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ apcuinfo.num_hits_percentage }}%</small>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('apcuinfo.hit') }}
|
||||
<span class="badge bg-secondary">{{ apcuinfo.readable.num_hits }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('apcuinfo.miss') }}
|
||||
<span class="badge bg-secondary">{{ apcuinfo.readable.num_misses }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('apcuinfo.cachetitle') }}</h5>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('apcuinfo.cvar') }}
|
||||
<span class="badge bg-secondary">{{ apcuinfo.readable.number_vars }}
|
||||
({{ apcuinfo.size_vars }})</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('apcuinfo.reqrate') }}
|
||||
<span class="badge bg-secondary">{{ apcuinfo.req_rate_user }}
|
||||
{{ lng('apcuinfo.creqsec') }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('apcuinfo.hitrate') }}
|
||||
<span class="badge bg-secondary">{{ apcuinfo.hit_rate_user }}
|
||||
{{ lng('apcuinfo.creqsec') }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('apcuinfo.detailmem') }}</h5>
|
||||
{% if apcuinfo.fragmentation is not iterable %}
|
||||
{{ lng('apcuinfo.nofragment') }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if apcuinfo.fragmentation is iterable %}
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ apcuinfo.fragmentation.used_percentage }}%" aria-valuenow="{{ apcuinfo.fragmentation.used_bytes }}" aria-valuemin="0" aria-valuemax="{{ apcuinfo.fragmentation.total_bytes }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ apcuinfo.fragmentation.used_percentage }}%</small>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('apcuinfo.total') }}
|
||||
<span class="badge bg-secondary">{{ apcuinfo.fragmentation.readable.total_bytes }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('apcuinfo.used') }}
|
||||
<span class="badge bg-secondary">{{ apcuinfo.fragmentation.readable.used_bytes }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('apcuinfo.fragments') }}
|
||||
<span class="badge bg-secondary">{{ apcuinfo.fragmentation.readable.num_frags }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2">
|
||||
<div class="col">
|
||||
<div class="card table-responsive mb-3">
|
||||
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('apcuinfo.generaltitle') }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.version') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.apcversion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.phpversion') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.phpversion }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.hostname') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.host }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.serversoftware') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.server }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.start') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.start_time|date('d.m.Y H:i:s') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('apcuinfo.uptime') }}</th>
|
||||
<td class="text-end">{{ apcuinfo.uptime }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card table-responsive">
|
||||
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('apcuinfo.runtime') }}</th>
|
||||
</tr>
|
||||
{% for k,v in apcuinfo.runtimelines %}
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ k|raw }}</th>
|
||||
<td class="text-end">{{ v|raw }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
1
templates/Maketank/settings/conf/command.html.twig
Normal file
1
templates/Maketank/settings/conf/command.html.twig
Normal file
@@ -0,0 +1 @@
|
||||
<textarea class="form-control bg-secondary text-light mb-2" rows="{{ numbrows }}" readonly>{{ commands|raw }}</textarea>
|
||||
2
templates/Maketank/settings/conf/file.html.twig
Normal file
2
templates/Maketank/settings/conf/file.html.twig
Normal file
@@ -0,0 +1,2 @@
|
||||
<textarea class="form-control bg-secondary text-light mb-2" rows="1" readonly>{{ distro_editor }} {{ realname }}</textarea>
|
||||
<textarea class="form-control mb-2" rows="{% if numbrows <= 20 %}{{ numbrows }}{% else %}21{% endif %}" readonly>{{ file_content|raw }}</textarea>
|
||||
6
templates/Maketank/settings/conf/fileblock.html.twig
Normal file
6
templates/Maketank/settings/conf/fileblock.html.twig
Normal file
@@ -0,0 +1,6 @@
|
||||
<fieldset class="file">
|
||||
{# <legend>{{ realname }}</legend> #}
|
||||
{{ commands_pre|raw }}
|
||||
{{ commands_file|raw }}
|
||||
{{ commands_post|raw }}
|
||||
</fieldset>
|
||||
12
templates/Maketank/settings/configuration-final.html.twig
Normal file
12
templates/Maketank/settings/configuration-final.html.twig
Normal file
@@ -0,0 +1,12 @@
|
||||
{% extends "Froxlor/settings/configuration.html.twig" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-2">
|
||||
{% include 'Froxlor/misc/alertbox.html.twig' %}
|
||||
</div>
|
||||
|
||||
<div class="row mb-2">
|
||||
<textarea cols="12" rows="4" readonly class="form-control w-100">{{ basedir }}bin/froxlor-cli froxlor:config-services --apply={{ params_filename }}
|
||||
rm {{ params_filename }}</textarea>
|
||||
</div>
|
||||
{% endblock %}
|
||||
138
templates/Maketank/settings/configuration.html.twig
Normal file
138
templates/Maketank/settings/configuration.html.twig
Normal file
@@ -0,0 +1,138 @@
|
||||
{% extends "Froxlor/userarea.html.twig" %}
|
||||
|
||||
{% block heading %}
|
||||
<h5>
|
||||
<i class="fa-solid fa-wrench"></i>
|
||||
{{ lng('admin.configfiles.serverconfiguration') }}
|
||||
</h5>
|
||||
<span class="text-muted">{{ lng('admin.configfiles.description') }}</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
<a class="btn btn-outline-primary" href="{{ linker({'section':'configfiles','reselect':1}) }}">
|
||||
<i class="fa-solid fa-grip me-1"></i>
|
||||
{{ lng('admin.configfiles.distribution') }}:
|
||||
{{ distribution }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="pb-2">
|
||||
<div class="alert alert-info fade show" role="alert">
|
||||
<p>{{ lng('admin.configfiles.minihowto')|raw }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="{{ action|default(filename) }}" method="post" enctype="application/x-www-form-urlencoded" class="form">
|
||||
{% block settings %}
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-4 g-3">
|
||||
{% for stype,field in fields %}
|
||||
<div class="col">
|
||||
<div class="card h-100 position-relative">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ stype|upper }}</h5>
|
||||
{% if stype != 'system' %}
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="{{ stype }}" id="{{ stype }}none" value="x" checked>
|
||||
<label class="form-check-label" for="{{ stype }}none">
|
||||
{{ lng('admin.configfiles.skipconfig') }}
|
||||
</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% set daemons = field.getDaemons %}
|
||||
{% for dtype,daemon in daemons %}
|
||||
{% if stype == 'system' %}
|
||||
<div class="form-check">
|
||||
{% set recommended = false %}
|
||||
{% if
|
||||
(dtype == get_setting('system.traffictool')) or
|
||||
(dtype == 'libnssextrausers' and (get_setting('system.mod_fcgid') == '1' or get_setting('phpfpm.enabled') == '1' or get_setting('system.apacheitksupport') == '1')) or
|
||||
(dtype == 'logrotate') or
|
||||
(dtype == 'fcgid' and get_setting('system.mod_fcgid') == '1') or
|
||||
(dtype == 'php-fpm' and get_setting('phpfpm.enabled') == '1') or
|
||||
(dtype == 'cron')
|
||||
%}
|
||||
{% set recommended = true %}
|
||||
{% endif %}
|
||||
<input class="form-check-input" type="checkbox" name="system[{{ dtype }}]" id="{{ dtype }}" value="{{ dtype }}" data-recommended="{{ recommended }}">
|
||||
<label class="form-check-label" for="{{ dtype }}">
|
||||
{% if recommended %}
|
||||
<strong>{{ daemon.title }}<span class="text-danger">*</span>
|
||||
</strong>
|
||||
{% else %}
|
||||
{{ daemon.title }}
|
||||
{% endif %}
|
||||
</label>
|
||||
<a class="show-config text-secondary opacity-50 float-end" role="button" data-dist="{{ distribution }}" data-section="{{ stype }}" data-daemon="{{ dtype }}" title="show config">
|
||||
<i class="fa-regular fa-file-code"></i>
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="form-check">
|
||||
{% set recommended = false %}
|
||||
{% if
|
||||
(dtype == 'apache22' and get_setting('system.webserver') == 'apache2' and get_setting('system.apache24') == '0') or
|
||||
(dtype == 'apache24' and get_setting('system.webserver') == 'apache2' and get_setting('system.apache24') == '1') or
|
||||
(dtype == 'lighttpd' and get_setting('system.webserver') == 'lighttpd') or
|
||||
(dtype == 'nginx' and get_setting('system.webserver') == 'nginx') or
|
||||
(dtype == 'bind' and get_setting('system.bind_enable') == '1' and get_setting('system.dns_server') == 'Bind') or
|
||||
(dtype == 'powerdns' and get_setting('system.bind_enable') == '1' and get_setting('system.dns_server') == 'PowerDNS') or
|
||||
(dtype == 'proftpd' and get_setting('system.ftpserver') == 'proftpd') or
|
||||
(dtype == 'pureftpd' and get_setting('system.ftpserver') == 'pureftpd')
|
||||
%}
|
||||
{% set recommended = true %}
|
||||
{% endif %}
|
||||
<input class="form-check-input" type="radio" name="{{ stype }}" id="{{ dtype }}" value="{{ dtype }}" data-recommended="{{ recommended }}">
|
||||
<label class="form-check-label" for="{{ dtype }}">
|
||||
{% if recommended %}
|
||||
<strong>{{ daemon.title }}<span class="text-danger">*</span>
|
||||
</strong>
|
||||
{% else %}
|
||||
{{ daemon.title }}
|
||||
{% endif %}
|
||||
</label>
|
||||
<a class="show-config text-secondary opacity-50 float-end" role="button" data-dist="{{ distribution }}" data-section="{{ stype }}" data-daemon="{{ dtype }}" title="show config">
|
||||
<i class="fa-regular fa-file-code"></i>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
<div class="row mt-3">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"/>
|
||||
<input type="hidden" name="finish" value="1"/>
|
||||
<div class="col-12 col-md-6">
|
||||
<span class="text-danger">*</span>
|
||||
{{ lng('admin.configfiles.recommendednote') }}
|
||||
</div>
|
||||
<div class="col-12 col-md-6 text-end">
|
||||
<button type="button" class="btn btn-outline-secondary" id="selectRecommendedConfig">{{ lng('admin.configfiles.selectrecommended') }}</button>
|
||||
<button type="button" class="btn btn-outline-secondary" id="downloadSelectionAsJson">
|
||||
<i class="fa-solid fa-download"></i>
|
||||
{{ lng('admin.configfiles.downloadselected') }}</button>
|
||||
<button type="submit" class="btn btn-primary">{{ lng('update.proceed') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="modal fade" id="configTplShow" aria-hidden="true" aria-labelledby="configTplShowLabel" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="configTplShowLabel"></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{ lng('panel.modalclose') }}"></button>
|
||||
</div>
|
||||
<div class="modal-body text-start"></div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-bs-dismiss="modal">{{ lng('panel.modalclose') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
40
templates/Maketank/settings/detailpart.html.twig
Normal file
40
templates/Maketank/settings/detailpart.html.twig
Normal file
@@ -0,0 +1,40 @@
|
||||
{% extends "Froxlor/settings/index.html.twig" %}
|
||||
|
||||
{% block actions %}
|
||||
<a class="btn btn-outline-primary" href="{{ linker({'section':'settings','page':'overview','part':'all'}) }}">
|
||||
<i class="fa-solid fa-grip me-1"></i>
|
||||
{{ lng('admin.configfiles.overview') }}
|
||||
</a>
|
||||
<a class="btn btn-outline-secondary" href="{{ linker({'section':'settings','page':'importexport'}) }}">
|
||||
<i class="fa-solid fa-file-import me-1"></i>
|
||||
{{ lng('admin.configfiles.importexport') }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block settings %}
|
||||
|
||||
{% import "Froxlor/form/formfields.html.twig" as formfields %}
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="formfields">
|
||||
{% for id,setting in fields %}
|
||||
{% if id != '_group' %}
|
||||
{% set isEm = em is defined and em == id %}
|
||||
{{ formfields.fieldrow(id, setting, false, (get_setting('system.hide_incompatible_settings') == '0'), isEm) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}"/>
|
||||
<input type="hidden" name="page" value="{{ page }}"/>
|
||||
<input type="hidden" name="action" value="{{ action }}"/>
|
||||
<input type="hidden" name="send" value="send"/>
|
||||
|
||||
<div class="col-12 text-center mb-2 d-grid gap-2 d-md-block">
|
||||
<button type="reset" class="btn btn-lg btn-outline-secondary me-md-3">{{ lng('panel.reset') }}</button>
|
||||
<button type="submit" class="btn btn-lg btn-primary">{{ lng('panel.save') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
0
templates/Maketank/settings/index.html
Normal file
0
templates/Maketank/settings/index.html
Normal file
60
templates/Maketank/settings/index.html.twig
Normal file
60
templates/Maketank/settings/index.html.twig
Normal file
@@ -0,0 +1,60 @@
|
||||
{% extends "Froxlor/userarea.html.twig" %}
|
||||
|
||||
{% block heading %}
|
||||
<h5>
|
||||
<i class="fa-solid fa-gears"></i>
|
||||
{{ lng('admin.serversettings') }}
|
||||
{% if fields._group is defined %} » {{ fields._group.title|raw }}
|
||||
{% endif %}
|
||||
</h5>
|
||||
<span class="text-muted">{{ lng('admin.serversettings_desc') }}</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
<a class="btn btn-outline-secondary" href="{{ linker({'section':'settings','page':'toggleSettingsMode'}) }}" title="{{ lng('panel.settingsmodetoggle') }}">
|
||||
{% if get_setting('panel.settings_mode') == 0 %}
|
||||
<i class="fa-solid fa-maximize me-1"></i>
|
||||
{{ lng('panel.settingsmode') }}: {{ lng('panel.settingsmodebasic') }}
|
||||
{% else %}
|
||||
<i class="fa-solid fa-minimize me-1"></i>
|
||||
{{ lng('panel.settingsmode') }}: {{ lng('panel.settingsmodeadvanced') }}
|
||||
{% endif %}
|
||||
</a>
|
||||
<a class="btn btn-outline-secondary" href="{{ linker({'section':'settings','page':'importexport'}) }}">
|
||||
<i class="fa-solid fa-file-import me-1"></i>
|
||||
{{ lng('admin.configfiles.importexport') }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form action="{{ action|default(filename) }}" method="post" enctype="multipart/form-data" class="form">
|
||||
{% block settings %}
|
||||
<div class="row row-cols-2 row-cols-md-2 row-cols-xl-4 g-3">
|
||||
{% for field in fields %}
|
||||
{% if get_setting('system.hide_incompatible_settings') == 0 or (get_setting('system.hide_incompatible_settings') == 1 and (field.visible is not defined or (field.visible is defined and field.visible))) %}
|
||||
<div class="col">
|
||||
<div class="card h-100 position-relative {% if not field.activated %}{% endif %}">
|
||||
<div class="card-body d-flex overflow-hidden align-items-center">
|
||||
<a href="{{ linker({'section':'settings','page':'overview','part':field.part}) }}" class="stretched-link">
|
||||
<i class="{{ field.icon }} fa-2x me-4" style="width: 1em;"></i>
|
||||
</a>
|
||||
<div>
|
||||
{{ field.title|raw }}
|
||||
{% if field.info is defined and field.info is not empty %}
|
||||
{{ field.info|raw }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if not field.activated %}
|
||||
<div class="position-absolute top-0 end-0 p-1">
|
||||
<span class="badge text-muted" style="background: #eee">{{ lng('panel.not_activated') }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
214
templates/Maketank/settings/opcacheinfo.html.twig
Normal file
214
templates/Maketank/settings/opcacheinfo.html.twig
Normal file
@@ -0,0 +1,214 @@
|
||||
{% extends "Froxlor/userarea.html.twig" %}
|
||||
|
||||
{% block heading %}
|
||||
<h5>
|
||||
<i class="fa-solid fa-hard-drive me-1"></i>
|
||||
{{ lng('admin.opcacheinfo') }}
|
||||
</h5>
|
||||
{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
<a class="btn btn-warning" href="{{ linker({'section':'opcacheinfo','page':'showinfo','action':'reset'}) }}">
|
||||
<i class="fa-solid fa-trash-can me-1"></i>
|
||||
{{ lng('opcacheinfo.resetcache') }}
|
||||
</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 mb-4">
|
||||
<div class="col">
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('opcacheinfo.memusage') }}</h5>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ opcacheinfo.overview.used_memory_percentage }}%" aria-valuenow="{{ opcacheinfo.overview.used_memory }}" aria-valuemin="0" aria-valuemax="{{ opcacheinfo.overview.total_memory }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ opcacheinfo.overview.used_memory_percentage }}%</small>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.totalmem') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.total_memory }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.used') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.used_memory }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.free') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.free_memory }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.wastedmem') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.wasted_memory }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('opcacheinfo.hitsc') }}</h5>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ opcacheinfo.overview.hit_rate_percentage }}%" aria-valuenow="{{ opcacheinfo.overview.hits }}" aria-valuemin="0" aria-valuemax="{{ opcacheinfo.overview.hits + opcacheinfo.overview.misses }}"></div>
|
||||
<div class="progress-bar bg-danger" role="progressbar" style="width: {{ 100 - opcacheinfo.overview.hit_rate_percentage }}%" aria-valuenow="{{ opcacheinfo.overview.misses }}" aria-valuemin="0" aria-valuemax="{{ opcacheinfo.overview.hits + opcacheinfo.overview.misses }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ opcacheinfo.overview.hit_rate_percentage }}%</small>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.cachedscripts') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.num_cached_scripts }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.hitsc') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.hits }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.missc') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.misses }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.blmissc') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.blacklist_miss }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('opcacheinfo.usedkey') }}</h5>
|
||||
<div class="progress position-relative">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ opcacheinfo.overview.used_key_percentage }}%" aria-valuenow="{{ opcacheinfo.overview.num_cached_keys }}" aria-valuemin="0" aria-valuemax="{{ opcacheinfo.overview.max_cached_keys }}"></div>
|
||||
<small class="justify-content-center d-flex position-absolute w-100 text-dark">{{ opcacheinfo.overview.used_key_percentage }}%</small>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.maxkey') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.max_cached_keys }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.usedkey') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.num_cached_keys }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ lng('opcacheinfo.strinterning') }}</h5>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.totalmem') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.interned.buffer_size }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.used') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.interned.strings_used_memory }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.free') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.interned.strings_free_memory }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
{{ lng('opcacheinfo.strcount') }}
|
||||
<span class="badge bg-secondary">{{ opcacheinfo.overview.readable.interned.number_of_strings }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row row-cols-1 row-cols-md-2">
|
||||
<div class="col">
|
||||
<div class="card table-responsive mb-3">
|
||||
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('opcacheinfo.generaltitle') }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.version') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.version }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.phpversion') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.php }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.hostname') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.host }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('admin.serversoftware') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.version.server }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.start') }}</th>
|
||||
<td class="text-end">{{ opcacheinfo.overview.start_time|date('d.m.Y H:i:s') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ lng('opcacheinfo.lastreset') }}</th>
|
||||
<td class="text-end">
|
||||
{% if opcacheinfo.overview.last_restart_time > 0 %}
|
||||
{{ opcacheinfo.overview.last_restart_time|date('d.m.Y H:i:s') }}
|
||||
{% else %}
|
||||
{{ lng('panel.never') }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card table-responsive">
|
||||
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-center" scope="row">{{ lng('opcacheinfo.funcsavail') }}</th>
|
||||
</tr>
|
||||
{% for funcs in opcacheinfo.functions %}
|
||||
<tr>
|
||||
<td>{{ funcs }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="card table-responsive">
|
||||
<table class="table table-borderless table-striped align-middle mb-0 px-3">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="text-center" colspan="2" scope="row">{{ lng('opcacheinfo.runtimeconf') }}</th>
|
||||
</tr>
|
||||
{% for directive in opcacheinfo.directives %}
|
||||
<tr>
|
||||
<th class="fw-bold" scope="row">{{ directive.k }}</th>
|
||||
<td class="text-end">
|
||||
{% if directive.v is iterable %}
|
||||
{% for vval in directive.v %}
|
||||
{% if vval is iterable %}
|
||||
{% for val2 in vval %}
|
||||
{{ val2|raw }}<br>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ vval|raw }}<br>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{{ directive.v|raw }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
42
templates/Maketank/settings/phpinfo.html.twig
Normal file
42
templates/Maketank/settings/phpinfo.html.twig
Normal file
@@ -0,0 +1,42 @@
|
||||
{% extends "Froxlor/userarea.html.twig" %}
|
||||
|
||||
{% block heading %}
|
||||
<h5>
|
||||
<i class="fa-solid fa-gears me-1"></i>
|
||||
{{ lng('admin.phpinfo') }}
|
||||
</h5>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card table-responsive">
|
||||
<table class="table table-borderless table-striped align-middle mb-0 px-3" id="phpinfotable">
|
||||
<tbody>
|
||||
{% for name,section in phpinfo %}
|
||||
{% if name|lower == 'phpinfo' %}
|
||||
{% set name = 'PHP ' ~ phpversion %}
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th colspan="3">{{ name|raw }}</th>
|
||||
</tr>
|
||||
{% for key,val in section %}
|
||||
{% if key != 'Directive' %}
|
||||
<tr>
|
||||
{% if val is iterable %}
|
||||
<td width="180">{{ key|raw }}</td>
|
||||
<td colspan="2">{{ val[0]|raw }}<br/><small>(Master:
|
||||
{{ val[1]|raw }})</small>
|
||||
</td>
|
||||
{% elseif key matches '/^\\d+$/' %}
|
||||
<td colspan="3" align="center">{{ val|raw }}</td>
|
||||
{% else %}
|
||||
<td width="180">{{ key|raw }}</td>
|
||||
<td colspan="2">{{ val|raw }}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
42
templates/Maketank/sidebar.html.twig
Normal file
42
templates/Maketank/sidebar.html.twig
Normal file
@@ -0,0 +1,42 @@
|
||||
<nav id="sidebar" class="sidebar collapse d-md-flex flex-shrink-0 flex-column bg-dark overflow-auto max-h-before-header">
|
||||
<ul class="nav d-flex flex-fill flex-column py-3">
|
||||
{% for idx,mitems in nav_entries %}
|
||||
{% if mitems.items is not empty %}
|
||||
<li class="nav-item" {% if mitems.active == 1 %}aria-current="page"{% endif %}>
|
||||
<a class="nav-link text-light {% if mitems.active == 0 %}collapsed{% endif %}" href="#sub{{ idx }}" data-bs-toggle="collapse" data-bs-target="#sub{{ idx }}">
|
||||
{% if mitems.icon is not empty %}
|
||||
<i class="{{ mitems.icon }}"></i>
|
||||
{% endif %}
|
||||
{{ mitems.label }}
|
||||
</a>
|
||||
<div class="collapse {% if mitems.active == 1 %}show{% endif %}" id="sub{{ idx }}" aria-expanded="{% if mitems.active == 1 %}true{% else %}false{% endif %}">
|
||||
<ul class="flex-column ps-3 nav">
|
||||
{% for item in mitems.items %}
|
||||
<li class="nav-item d-flex justify-content-between align-items-center" {% if item.active == 1 %}aria-current="page"{% endif %}>
|
||||
<div class="me-auto">
|
||||
<a class="nav-link text-light {% if item.active == 1 %}active fw-bold{% endif %}" href="{{ item.url|raw }}" {% if item.is_external is defined and item.is_external %}target="_blank"{% endif %}>{{ item.label|raw }}</a>
|
||||
</div>
|
||||
{% if item.add_shortlink is defined and item.add_shortlink is not empty %}
|
||||
<a href="{{ item.add_shortlink|raw }}" class="text-secondary me-2"><i class="fa-solid fa-plus-circle"></i></a>
|
||||
{% endif %}
|
||||
{% if item.is_external is defined and item.is_external %}
|
||||
<span class="me-2"><i class="fa-solid fa-arrow-up-right-from-square"></i></span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item" {% if mitems.active == 1 %}aria-current="page"{% endif %}>
|
||||
<a class="nav-link text-light {% if mitems.active == 1 %}active{% endif %}" href="{% if mitems.url is not empty %}{{ mitems.url|raw }}{% else %}#{% endif %}" {% if mitems.target is not empty %} target="{{ mitems.target }}" {% endif %}>
|
||||
{% if mitems.icon is not empty %}
|
||||
<i class="{{ mitems.icon }}"></i>
|
||||
{% endif %}
|
||||
{{ mitems.label|upper }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
0
templates/Maketank/src/index.html
Normal file
0
templates/Maketank/src/index.html
Normal file
60
templates/Maketank/src/js/components/apikeys.js
Normal file
60
templates/Maketank/src/js/components/apikeys.js
Normal file
@@ -0,0 +1,60 @@
|
||||
$(function () {
|
||||
|
||||
var timer, delay = 500;
|
||||
$('div[data-action="apikeys"] #allowed_from').on('keyup change', function () {
|
||||
var _this = $(this);
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function () {
|
||||
var akid = _this.closest('div[data-action="apikeys"]').data('entry');
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=editapikey",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: { id: akid, allowed_from: _this.val(), valid_until: $('div[data-entry="' + akid + '"] #valid_until').val() },
|
||||
success: function (data) {
|
||||
if (data.message) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
} else {
|
||||
_this.removeClass('is-invalid');
|
||||
_this.addClass('is-valid');
|
||||
_this.val(data.allowed_from);
|
||||
}
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
}
|
||||
});
|
||||
}, delay);
|
||||
});
|
||||
|
||||
$('div[data-action="apikeys"] #valid_until').on('keyup change', function () {
|
||||
var _this = $(this);
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function () {
|
||||
var akid = _this.closest('div[data-action="apikeys"]').data('entry');
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=editapikey",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: { id: akid, valid_until: _this.val(), allowed_from: $('div[data-entry="' + akid + '"] #allowed_from').val() },
|
||||
success: function (data) {
|
||||
if (data.message) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
} else {
|
||||
_this.removeClass('is-invalid');
|
||||
_this.addClass('is-valid');
|
||||
_this.val(data.valid_until);
|
||||
}
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
_this.removeClass('is-valid');
|
||||
_this.addClass('is-invalid');
|
||||
}
|
||||
});
|
||||
}, delay);
|
||||
});
|
||||
|
||||
});
|
||||
52
templates/Maketank/src/js/components/configfiles.js
Normal file
52
templates/Maketank/src/js/components/configfiles.js
Normal file
@@ -0,0 +1,52 @@
|
||||
$(function () {
|
||||
/*
|
||||
* config files - select all recommended
|
||||
*/
|
||||
$('#selectRecommendedConfig').on('click', function () {
|
||||
$('input[data-recommended]').each(function () {
|
||||
if ($(this).data('recommended') == 1) {
|
||||
$(this).prop('checked', true);
|
||||
} else {
|
||||
$(this).prop('checked', false);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/*
|
||||
* export/download JSON file (e.g. for usage with config-services)
|
||||
*/
|
||||
$('#downloadSelectionAsJson').on('click', function () {
|
||||
var formData = $(this).closest('form').serialize();
|
||||
window.location = "lib/ajax.php?action=getConfigJsonExport&" + formData;
|
||||
});
|
||||
|
||||
/*
|
||||
* open modal window to show selected config-commands/files
|
||||
* for selected daemon
|
||||
*/
|
||||
$('.show-config').on('click', function () {
|
||||
var distro = $(this).data('dist');
|
||||
var section = $(this).data('section');
|
||||
var daemon = $(this).data('daemon');
|
||||
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=getConfigDetails",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: { distro: distro, section: section, daemon: daemon },
|
||||
success: function (data) {
|
||||
$('#configTplShowLabel').html(data.title);
|
||||
$('#configTplShow .modal-body').html(data.content);
|
||||
var myModal = new bootstrap.Modal(document.getElementById('configTplShow'));
|
||||
myModal.show();
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
$('#configTplShowLabel').html('Error');
|
||||
$('#configTplShow .modal-body').html('<div class="alert alert-danger" role="alert">' + request.responseJSON.message + '</div>');
|
||||
var myModal = new bootstrap.Modal(document.getElementById('configTplShow'));
|
||||
myModal.show();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
76
templates/Maketank/src/js/components/customer.js
Normal file
76
templates/Maketank/src/js/components/customer.js
Normal file
@@ -0,0 +1,76 @@
|
||||
$(function() {
|
||||
|
||||
// Make inputs with enabled unlimited checked disabled
|
||||
$("input[name$='_ul']").each(function () {
|
||||
var fieldname = $(this).attr("name").substring(0, $(this).attr("name").length - 3);
|
||||
$("input[name='" + fieldname + "']").prop({
|
||||
readonly: $(this).is(":checked"),
|
||||
required: !$(this).is(":checked")
|
||||
});
|
||||
});
|
||||
// change state when unlimited checkboxes are clicked
|
||||
$("input[name$='_ul']").on('change', function () {
|
||||
var fieldname = $(this).attr("name").substring(0, $(this).attr("name").length - 3);
|
||||
$("input[name='" + fieldname + "']").prop({
|
||||
readonly: $(this).is(":checked"),
|
||||
required: !$(this).is(":checked")
|
||||
});
|
||||
if (!$(this).is(":checked")) {
|
||||
$("input[name='" + fieldname + "']").focus()
|
||||
}
|
||||
});
|
||||
|
||||
// set values from hosting plan when adding/editing a customer according to the plan's values
|
||||
$('#use_plan').on('change', function () {
|
||||
var pid = $(this).val();
|
||||
if (pid > 0) {
|
||||
$.ajax({
|
||||
url: "admin_plans.php?page=overview&action=jqGetPlanValues",
|
||||
type: "POST",
|
||||
data: {
|
||||
planid: pid
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
for (var i in json) {
|
||||
if (i == 'email_imap' || i == 'email_pop3' || i == 'perlenabled' || i == 'phpenabled' || i == 'dnsenabled' || i == 'logviewenabled') {
|
||||
/** handle checkboxes **/
|
||||
if (json[i] == 1) {
|
||||
$("input[name='" + i + "']").prop('checked', true);
|
||||
} else {
|
||||
$("input[name='" + i + "']").prop('checked', false);
|
||||
}
|
||||
} else if (i == 'allowed_phpconfigs') {
|
||||
/** handle array of values **/
|
||||
$("input[name='allowed_phpconfigs[]']").each(function (index) {
|
||||
$(this).prop('checked', false);
|
||||
for (var j in json[i]) {
|
||||
if ($(this).val() == json[i][j]) {
|
||||
$(this).prop('checked', true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (json[i] == -1) {
|
||||
/** handle unlimited checkboxes **/
|
||||
$("input[name='" + i + "_ul']").attr('checked', 'checked');
|
||||
$("input[name='" + i + "']").prop({
|
||||
readonly: true
|
||||
});
|
||||
} else {
|
||||
/** handle normal value **/
|
||||
$("input[name='" + i + "']").val(json[i]);
|
||||
$("input[name='" + i + "']").prop({
|
||||
readonly: false
|
||||
});
|
||||
$("input[name='" + i + "_ul']").prop('checked', false);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
19
templates/Maketank/src/js/components/dnseditor.js
Normal file
19
templates/Maketank/src/js/components/dnseditor.js
Normal file
@@ -0,0 +1,19 @@
|
||||
$(function () {
|
||||
|
||||
// Display helptext to content box according to dns-record type selected
|
||||
$("select[name='dns_type']").on('change', function () {
|
||||
var selVal = $(this).val();
|
||||
$.ajax({
|
||||
url: "lib/ajax.php?action=loadLanguageString",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: { langid: 'dnseditor.notes.' + selVal },
|
||||
success: function (data) {
|
||||
$("#dns_content").next().html(data);
|
||||
},
|
||||
error: function (request, status, error) {
|
||||
console.log(request, status, error)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
87
templates/Maketank/src/js/components/domains.js
Normal file
87
templates/Maketank/src/js/components/domains.js
Normal file
@@ -0,0 +1,87 @@
|
||||
$(function() {
|
||||
|
||||
// disable unusable php-configuration by customer settings
|
||||
$('#customerid').on('change', function () {
|
||||
var cid = $(this).val();
|
||||
$.ajax({
|
||||
url: "admin_domains.php?page=domains&action=jqGetCustomerPHPConfigs",
|
||||
type: "POST",
|
||||
data: {
|
||||
customerid: cid
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
if (json.length > 0) {
|
||||
$('#phpsettingid option').each(function () {
|
||||
var pid = $(this).val();
|
||||
$(this).attr("disabled", "disabled");
|
||||
for (var i in json) {
|
||||
if (pid == json[i]) {
|
||||
$(this).removeAttr("disabled");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// show warning if speciallogfile option is toggled
|
||||
if ($('input[name=speciallogverified]')) {
|
||||
$('input[name=speciallogfile]').on('click', function () {
|
||||
$('#speciallogfilenote').remove();
|
||||
$('#speciallogfile').removeClass('is-invalid');
|
||||
$('#speciallogverified').val(0);
|
||||
$.ajax({
|
||||
url: "admin_domains.php?page=overview&action=jqSpeciallogfileNote",
|
||||
type: "POST",
|
||||
data: {
|
||||
id: $('input[name=id]').val(), newval: +$('#speciallogfile').is(':checked')
|
||||
},
|
||||
dataType: "json",
|
||||
success: function (json) {
|
||||
if (json.changed) {
|
||||
$('#speciallogfile').addClass('is-invalid');
|
||||
$('#speciallogfile').parent().append(json.info);
|
||||
$('#speciallogverified').val(1);
|
||||
}
|
||||
},
|
||||
error: function (a, b) {
|
||||
console.log(a, b);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* email only domain - hide unnecessary/unused sections
|
||||
*/
|
||||
if ($('#id') && $('#email_only').is(':checked')) {
|
||||
$('#section_b').hide();
|
||||
$('#section_bssl').hide();
|
||||
$('#section_c').hide();
|
||||
$('#section_d').hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* toggle show/hide of sections in case of email only flag
|
||||
*/
|
||||
$('#email_only').on('click', function () {
|
||||
if ($(this).is(':checked')) {
|
||||
// hide unnecessary sections
|
||||
$('#section_b').hide();
|
||||
$('#section_bssl').hide();
|
||||
$('#section_c').hide();
|
||||
$('#section_d').hide();
|
||||
} else {
|
||||
// show sections
|
||||
$('#section_b').show();
|
||||
$('#section_bssl').show();
|
||||
$('#section_c').show();
|
||||
$('#section_d').show();
|
||||
}
|
||||
})
|
||||
});
|
||||
12
templates/Maketank/src/js/components/global.js
Normal file
12
templates/Maketank/src/js/components/global.js
Normal file
@@ -0,0 +1,12 @@
|
||||
$(function () {
|
||||
|
||||
$('#historyback').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
history.back(1);
|
||||
})
|
||||
|
||||
$('#copySysInfo').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText($('#ccSysInfo').text().trim());
|
||||
})
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user