<?php
/**
* SeekQuarry/Yioop --
* Open Source Pure PHP Search Engine, Crawler, and Indexer
*
* Copyright (C) 2009 - 2026 Chris Pollett chris@pollett.org
*
* LICENSE:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* END LICENSE
*
* @author Chris Pollett chris@pollett.org
* @license https://www.gnu.org/licenses/ GPL3
* @link https://www.seekquarry.com/
* @copyright 2009 - 2026
* @filesource
*/
namespace seekquarry\yioop\views\elements;
use seekquarry\yioop as B;
use seekquarry\yioop\configs as C;
/**
* Element used to draw forms to set up the various external servers
* that might be connected with a Yioop installation
*
* @author Chris Pollett
*/
class ServersElement extends Element
{
/**
* Method that draw forms to set up the various external servers
* that might be connected with a Yioop installation
*
* @param array $data holds data on the profile elements which have been
* filled in as well as data about which form fields to display
*/
public function render($data)
{
$admin_url = htmlentities(B\controllerUrl('admin', true));
$token_string = C\p('CSRF_TOKEN')."=".$data[C\p('CSRF_TOKEN')];
$pre_base_url = $admin_url . $token_string;
$base_url = $pre_base_url . "&a=servers";
?>
<div class="current-activity">
<form id="serversForm" method="post">
<input type="hidden" name="c" value="admin" >
<input type="hidden" name="<?= C\p('CSRF_TOKEN') ?>" value="<?=
$data[C\p('CSRF_TOKEN')] ?>" >
<input type="hidden" name="a" value="servers" >
<input type="hidden" name="arg" value="update" >
<h2><?= tl('servers_element_server_settings')?></h2>
<div class="bold">
<div class="top-margin">
<fieldset><legend><?=tl('servers_element_name_server') .
" " . $this->view->helper("helpbutton")->render(
"Name Server Setup", $data[C\p('CSRF_TOKEN')]) ?></legend><?php
if (!empty($this->view->controller_object->web_site) &&
$this->view->controller_object->web_site->isCli()) {
?>
[<a href="<?=$base_url . '&arg=restart' ?>"><?=
tl('servers_element_restart');?></a>]
<?php
}
?><div ><b><label for="queue-fetcher-salt"><?=
tl('servers_element_name_server_key')
?></label></b>
<input type="text" id="queue-fetcher-salt" name="AUTH_KEY"
value="<?= $data['AUTH_KEY'] ?>"
class="wide-field" >
</div>
<div class="top-margin"><b><label for="name-server-url"><?=
tl('servers_element_name_server_url')
?></label></b>
<input type="url" id="name-server-url" name="NAME_SERVER"
value="<?= $data['NAME_SERVER'] ?>"
class="extra-wide-field" >
</div>
<div id="filecache">
<div class="top-margin"><label for="use-filecache"><b><?=
tl('servers_element_use_filecache')?></b></label>
<input type="checkbox" id="use-filecache"
name="USE_FILECACHE" value="true" <?=
$data['USE_FILECACHE'] ? "checked='checked'" :
"" ?> >
[<a href="<?= $base_url . '&arg=clearcache'
?>"><?=tl('servers_element_clear_cache')
?></a>]
</div>
</div>
</fieldset>
</div>
<div class="top-margin">
<fieldset><legend><?=tl('servers_element_database_setup').
" " .$this->view->helper("helpbutton")->render(
"Database Setup", $data[C\p('CSRF_TOKEN')]) ?>
</legend>
<div ><label for="database-system"><b><?=
tl('servers_element_database_system')
?></b></label>
<?php $this->view->helper("options")->render(
"database-system", "DBMS",
$data['DBMSS'], $data['DBMS']);
?>
</div>
<div class="top-margin"><b><label for="database-name"><?=
tl('servers_element_databasename') ?></label></b>
<input type="text" id="database-name" name="DB_NAME"
value="<?= $data['DB_NAME'] ?>"
class="wide-field" >
</div>
<div id="login-dbms">
<div class="top-margin"><b><label for="database-host"><?=
tl('servers_element_databasehost')
?></label></b>
<input type="text" id="database-host" name="DB_HOST"
value="<?= $data['DB_HOST'] ?>"
class="wide-field" >
</div>
<div class="top-margin"><b><label for="database-user"><?=
tl('servers_element_databaseuser')
?></label></b>
<input type="text" id="database-user" name="DB_USER"
value="<?= $data['DB_USER'] ?>"
class="wide-field" >
</div>
<div class="top-margin"><b><label
for="database-password"><?=
tl('servers_element_databasepassword')
?></label></b>
<input type="password" id="database-password"
name="DB_PASSWORD" value="<?=
$data['DB_PASSWORD'] ?>" class="wide-field" >
</div>
</div>
</fieldset>
</div>
<div class = "top-margin">
</div>
<div class="top-margin">
<fieldset>
<legend><label for="acme-on"><?=
tl('servers_element_web_server')
?></label>
<?= $this->view->helper("helpbutton")->render(
"Web Server", $data[C\p('CSRF_TOKEN')])
?></legend>
<div class="top-margin"><b><label for="acme-on"><?=
tl('servers_element_acme_on')
?></label></b>
<input type="checkbox" id="acme-on"
name="ACME_ON" value="true" <?=
(!empty($data['ACME_ON']) &&
$data['ACME_ON'] == true) ?
"checked='checked'" : "" ?> >
</div>
<div class="top-margin" id="secure-domains-row"><b><label
for="secure-domain-add"><?=
tl('servers_element_secure_domains')
?></label></b>
<ul class="mail-domain-list"
id="secure-domain-list"
data-route-url="<?= $pre_base_url .
'&a=servers&arg=updateroute' ?>"
data-routing-label="<?= tl(
'servers_element_domain_routing'
) ?>"
data-default-label="<?= tl(
'servers_element_routing_default'
) ?>"
data-custom-label="<?= tl(
'servers_element_routing_custom'
) ?>"
data-group-label="<?= tl(
'servers_element_routing_group'
) ?>"
data-edit-label="<?= tl(
'servers_element_domain_edit'
) ?>"
data-apply-label="<?= tl(
'servers_element_route_apply'
) ?>"><?php
$secure_csv = trim((string)
($data['SECURE_DOMAINS'] ?? ''));
$secure_entries = $secure_csv === '' ?
[] : array_filter(array_map('trim',
explode(',', $secure_csv)));
foreach ($secure_entries as $secure_domain) {
$route_group = $data['DOMAIN_ROUTES'][
strtolower($secure_domain)] ?? '';
$is_custom = ($route_group !== '');
?><li class="mail-domain-item"><span
class="mail-domain-text"><?=
$secure_domain
?></span><button type="button"
class="secure-domain-edit"
data-domain="<?= $secure_domain
?>" title="<?= tl(
'servers_element_domain_edit'
) ?>">✎</button><button
type="button"
class="secure-domain-remove mail-domain-remove"
data-domain="<?= $secure_domain
?>" title="<?= tl(
'servers_element_mail_domain_remove'
) ?>">×</button>
<div class="domain-route-panel"
data-domain="<?=
strtolower($secure_domain) ?>"
style="display:none;">
<label><?= tl(
'servers_element_domain_routing'
) ?> <select
class="domain-route-mode">
<option value="default"<?=
$is_custom ? "" : " selected"
?>><?= tl(
'servers_element_routing_default'
) ?></option>
<option value="custom"<?=
$is_custom ? " selected" : ""
?>><?= tl(
'servers_element_routing_custom'
) ?></option>
</select></label>
<label class="domain-route-group-label"
<?= $is_custom ? "" :
"style=\"display:none;\"" ?>><?=
tl(
'servers_element_routing_group'
) ?> <input type="text"
class="domain-route-group"
value="<?= e($route_group) ?>">
</label>
<button type="button"
class="domain-route-apply"
title="<?= tl(
'servers_element_route_apply'
) ?>">→</button>
<div class="robots-config-link"><a href="<?=
$pre_base_url .
'&a=servers&arg=robots'.
'&domain=' . urlencode(strtolower(
$secure_domain)) ?>"><?= tl(
'servers_element_configure_robots'
) ?></a></div>
</div></li><?php
}
?></ul>
<div class="mail-domain-add-row">
<input type="text" id="secure-domain-add"
class="wide-field"
placeholder="<?= tl(
'servers_element_secure_domain_placeholder'
) ?>" >
<button type="button" id="secure-domain-add-button"
class="button-box"
data-remove-label="<?= tl(
'servers_element_mail_domain_remove'
) ?>"><?= tl(
'servers_element_mail_domain_add'
) ?></button>
</div>
<input type="hidden" id="secure-domains"
name="SECURE_DOMAINS"
value="<?= $secure_csv ?>" >
</div>
<div id="web-server-atto-config">
<div class="top-margin"><b><label for="www-user"><?=
tl('servers_element_www_user')
?></label></b>
<input type="text" id="www-user" name="WWW_USER"
class="wide-field"
value="<?= $data['WWW_USER'] ?? '' ?>" >
</div>
<div class="top-margin"><b><label><?=
tl('servers_element_alpn_protocols')
?></label></b><?php
$alpn_csv = trim((string)
($data['ALPN_PROTOCOLS'] ?? ''));
$alpn_selected = $alpn_csv === '' ? [] :
array_filter(array_map('trim',
explode(',', $alpn_csv)));
foreach ($data['ALPN_PROTOCOL_OPTIONS'] as
$alpn_option) {
$alpn_id = "alpn-" . str_replace(["/", "."],
["-", "-"], $alpn_option);
?><span class="alpn-option"><input
type="checkbox" class="alpn-protocol-box"
id="<?= $alpn_id ?>"
value="<?= $alpn_option ?>" <?=
in_array($alpn_option, $alpn_selected) ?
"checked='checked'" : "" ?>
onchange="updateAlpnProtocols();" ><label
for="<?= $alpn_id ?>"><?= $alpn_option
?></label></span><?php
}
?><input type="hidden" id="alpn-protocols"
name="ALPN_PROTOCOLS"
value="<?= $alpn_csv ?>" >
</div>
</div>
</fieldset>
</div>
<div class="top-margin">
<fieldset >
<legend><label for="mail-mode"><?=
tl('servers_element_mail_services') ?>
</label>
<?= $this->view->helper("helpbutton")->render(
"Mail Services", $data[C\p('CSRF_TOKEN')])
?></legend>
<?php $this->view->helper("options")->render(
"mail-mode", "MAIL_MODE",
$data['MAIL_MODES'],
$data['MAIL_MODE']);
?>
<div class="top-margin"><b><label for="mail-host-name"><?=
tl('servers_element_mail_banner')
?></label></b>
<input type="text" id="mail-host-name"
name="MAIL_HOST_NAME" class="wide-field"
value="<?= $data['MAIL_HOST_NAME'] ?? '' ?>"
placeholder="<?= $data['MAIL_HOST_NAME_DEFAULT']
?? '' ?>" >
</div>
<div id="external-config">
<div class="top-margin"><b><label for="mail-log-enabled"><?=
tl('servers_element_mail_log_enabled')
?></label></b>
<input type="checkbox" id="mail-log-enabled"
name="MAIL_LOG_ENABLED"
value="true" <?php if (
$data['MAIL_LOG_ENABLED'] == true) {
e("checked='checked'");
} ?> >
[<a href="<?=
$admin_url . "a=manageMachines&" .
$token_string . "&arg=log&" .
"name=NAME_SERVER&type=Mail&" .
"id=0" ?>"><?=
tl('servers_element_mail_log_view')
?></a>]
</div>
</div>
<div id="mailsite-config">
<div class="top-margin"><b><label for="mail-domain-add"><?=
tl('servers_element_mail_domains')
?></label></b>
<ul class="mail-domain-list" id="mail-domain-list"><?php
$domain_csv = trim((string)
$data['MAIL_DOMAINS']);
$domain_entries = $domain_csv === '' ?
[] : array_filter(array_map('trim',
explode(',', $domain_csv)));
foreach ($domain_entries as $domain) {
?><li class="mail-domain-item"><span
class="mail-domain-text"><?=
e($domain)
?></span><button type="button"
class="mail-domain-remove"
data-domain="<?= e(
$domain) ?>" title="<?= tl(
'servers_element_mail_domain_remove'
) ?>">×</button></li><?php
}
?></ul>
<div class="mail-domain-add-row">
<input type="text" id="mail-domain-add"
class="wide-field"
placeholder="<?= tl(
'servers_element_mail_domain_placeholder'
) ?>" >
<button type="button" id="mail-domain-add-button"
class="button-box"
data-remove-label="<?= tl(
'servers_element_mail_domain_remove'
) ?>"><?= tl(
'servers_element_mail_domain_add'
) ?></button>
</div>
<input type="hidden" id="mail-domains"
name="MAIL_DOMAINS"
value="<?= e(
$domain_csv) ?>" >
</div>
<div class="top-margin" id="mail-smtp-port-row"><b><label
for="mail-smtp-port"><?=
tl('servers_element_mail_smtp_port')
?></label></b>
<input type="number" id="mail-smtp-port"
name="MAIL_SMTP_PORT" min="1" max="65535"
value="<?= $data['MAIL_SMTP_PORT'] ?>"
class="narrow-field" >
</div>
<div class="top-margin" id="mail-submission-port-row"><b><label
for="mail-submission-port"><?=
tl('servers_element_mail_submission_port')
?></label></b>
<input type="number" id="mail-submission-port"
name="MAIL_SUBMISSION_PORT" min="1" max="65535"
value="<?= $data['MAIL_SUBMISSION_PORT'] ?>"
class="narrow-field" >
</div>
<?php
if (!empty($data['MAIL_TLS_AVAILABLE'])) { ?>
<div class="top-margin"><b><label for="mail-smtps-port"><?=
tl('servers_element_mail_smtps_port')
?></label></b>
<input type="number" id="mail-smtps-port"
name="MAIL_SMTPS_PORT" min="1" max="65535"
value="<?= $data['MAIL_SMTPS_PORT'] ?>"
class="narrow-field" >
</div><?php
} ?>
<div class="top-margin" id="mail-imap-port-row"><b><label
for="mail-imap-port"><?=
tl('servers_element_mail_imap_port')
?></label></b>
<input type="number" id="mail-imap-port"
name="MAIL_IMAP_PORT" min="1" max="65535"
value="<?= $data['MAIL_IMAP_PORT'] ?>"
class="narrow-field" >
</div>
<?php
if (!empty($data['MAIL_TLS_AVAILABLE'])) { ?>
<div class="top-margin"><b><label for="mail-imaps-port"><?=
tl('servers_element_mail_imaps_port')
?></label></b>
<input type="number" id="mail-imaps-port"
name="MAIL_IMAPS_PORT" min="1" max="65535"
value="<?= $data['MAIL_IMAPS_PORT'] ?>"
class="narrow-field" >
</div><?php
} ?>
<ul class="mail-port-links"><li><a
href="javascript:setMailDefaultPorts()"><?=
tl('servers_element_mail_use_default_ports')
?></a></li><?php
if (!empty($data['MAIL_DNS_RECORDS'])) { ?>
<li><a
href="javascript:toggleDisplay('mail-dns-records')"><?=
tl('servers_element_mail_dns_records')
?></a></li><?php
} ?>
</ul><?php
if (!empty($data['MAIL_DNS_RECORDS'])) { ?>
<pre class="mail-dns-records" id="mail-dns-records"><?php
e(tl('servers_element_mail_dns_help'));
foreach ($data['MAIL_DNS_RECORDS'] as
$dns_domain => $dns_rows) {
e("\n\n" . $dns_domain . "\n");
e(tl('servers_element_mail_dns_host') .
"\t" .
tl('servers_element_mail_dns_type') .
"\t" .
tl('servers_element_mail_dns_value'));
foreach ($dns_rows as $dns_row) {
e("\n" . $dns_row['host'] . "\t" .
$dns_row['type'] . "\t" .
$dns_row['value']);
}
} ?></pre><?php
} ?>
<?php
if (empty($data['MAIL_TLS_AVAILABLE'])) { ?>
<div class="top-margin red"><?=
tl('servers_element_mail_no_certificate')
?></div><?php
} ?>
<?php
if (!empty($data['MAIL_TLS_AVAILABLE'])) { ?>
<div class="top-margin"><b><label
for="mail-use-starttls"><?=
tl('servers_element_mail_use_starttls')
?></label></b>
<input type="checkbox"
id="mail-use-starttls"
name="MAIL_USE_STARTTLS"
value="true" <?php if (
$data['MAIL_USE_STARTTLS'] == true) {
e("checked='checked'");
} ?> >
</div><?php
} ?>
<div class="top-margin"><b><label
for="mail-dmarc-enforce"><?=
tl('servers_element_mail_dmarc_enforce')
?></label></b>
<input type="checkbox"
id="mail-dmarc-enforce"
name="MAIL_DMARC_ENFORCE"
value="true" <?php if (!empty(
$data['MAIL_DMARC_ENFORCE']) &&
$data['MAIL_DMARC_ENFORCE'] == true) {
e("checked='checked'");
} ?> >
</div>
<div class="top-margin"><b><label
for="mail-delivery-security"><?=
tl('servers_element_mail_delivery_security')
?></label></b><?php
if (empty($data['MAIL_SECURITY_AVAILABLE'])) {
/* Without a certificate and clean URLs the
secure postures cannot operate, so the
choice is forced to "insecure" and locked.
A disabled control is not submitted, so a
hidden field carries the value. */
?>
<input type="hidden" name="MAIL_DELIVERY_SECURITY"
value="insecure">
<select id="mail-delivery-security"
disabled="disabled">
<option value="insecure"
selected="selected"><?=
tl('system_component_mail_security_insecure')
?></option>
</select><?php
} else {
?>
<select id="mail-delivery-security"
name="MAIL_DELIVERY_SECURITY"><?php
foreach ($data['MAIL_SECURITY_OPTIONS'] as
$option_value => $option_label) {
$selected =
($data['MAIL_DELIVERY_SECURITY'] ==
$option_value) ?
' selected="selected"' : '';
e('<option value="' . $option_value .
'"' . $selected . '>' .
$option_label . '</option>');
} ?>
</select><?php
} ?>
</div>
<div class="top-margin"><b><label for="mail-external"><?=
tl('servers_element_mail_external')
?></label></b>
<input type="checkbox" id="mail-external"
name="MAIL_EXTERNAL"
value="true" <?php if (
$data['MAIL_EXTERNAL'] == true) {
e("checked='checked'");
} ?> >
</div>
<div class="top-margin"><b><label
for="mail-test-mode"><?=
tl('servers_element_mail_test_mode')
?></label></b>
<input type="checkbox"
id="mail-test-mode"
name="MAIL_TEST_MODE"
value="true" <?php if (
$data['MAIL_TEST_MODE'] == true) {
e("checked='checked'");
} ?> >
</div>
<div class="top-margin"
id="mail-test-mode-port-row"><b><label
for="mail-test-mode-fallback-port"><?=
tl(
'servers_element_mail_test_mode_fallback_port'
) ?></label></b>
<input type="number"
id="mail-test-mode-fallback-port"
name="MAIL_TEST_MODE_FALLBACK_PORT"
min="0" max="65535"
value="<?= (int)
$data['MAIL_TEST_MODE_FALLBACK_PORT']
?>" class="narrow-field" >
</div>
</div>
</fieldset>
</div>
<div class="top-margin">
<fieldset><legend><?=tl('servers_element_proxy_title') .
" " .$this->view->helper("helpbutton")->render(
"Proxy Server", $data[C\p('CSRF_TOKEN')]) ?></legend>
<div ><b><label for="tor-proxies"><?=
tl('servers_element_tor_proxy')?></label></b>
<input type="text" id="tor-proxies" name="TOR_PROXY"
value="<?=$data['TOR_PROXY'] ?>"
class="wide-field" >
</div>
<div class="top-margin"><label for="use-proxy"><b><?=
tl('servers_element_use_proxy_servers')
?></b></label>
<input type="checkbox" id="use-proxy"
name="USE_PROXY" value="true" <?=
$data['USE_PROXY'] ? "checked='checked'" :
"" ?> >
</div>
<div id="proxy">
<div class="top-margin"><label for="proxy-servers" ><b><?=
tl('servers_element_proxy_servers') ?></b></label>
</div>
<textarea class="short-text-area" id="proxy-servers"
name="PROXY_SERVERS"><?=$data['PROXY_SERVERS'] ?></textarea>
</div>
</fieldset>
</div>
<div class="top-margin center">
<button class="button-box" type="submit"><?=
tl('servers_element_save') ?></button>
</div>
</div>
</form>
</div>
<script>
window.onload = function()
{
initMailDomainList();
initMailInsecureToggle();
initSecureDomainList();
showHideWebServer();
}
/*
* Wires the mail-domain Add and Remove buttons. The Add button
* reads the input field, trims whitespace, skips duplicates,
* appends a new list item with its own Remove button, clears
* the input, and rebuilds the hidden CSV. The Remove buttons
* (including those rendered server-side for already-saved
* domains) remove their list item and rebuild the CSV. The
* hidden input feeds the form submit; pressing Save persists
* whatever is in the list at that moment.
*/
function initMailDomainList()
{
var add_button = elt('mail-domain-add-button');
var add_input = elt('mail-domain-add');
var list = elt('mail-domain-list');
if (!add_button || !add_input || !list) {
return;
}
listen(add_button, 'click', function () {
var value = add_input.value.trim();
if (value === '' || value.indexOf(',') !== -1) {
return;
}
var spans = list.querySelectorAll(
'.mail-domain-text');
for (var i = 0; i < spans.length; i++) {
if (spans[i].textContent === value) {
add_input.value = '';
return;
}
}
var item = ce('li');
item.className = 'mail-domain-item';
var text = ce('span');
text.className = 'mail-domain-text';
text.textContent = value;
var remove = ce('button');
remove.type = 'button';
remove.className = 'mail-domain-remove';
remove.setAttribute('data-domain', value);
remove.textContent = '\u00D7';
remove.title = add_button.getAttribute(
'data-remove-label') || 'Remove';
wireMailDomainRemove(remove);
item.appendChild(text);
item.appendChild(remove);
list.appendChild(item);
add_input.value = '';
syncMailDomainsHidden();
});
listen(add_input, 'keydown', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
add_button.click();
}
});
var removes = list.querySelectorAll(
'.mail-domain-remove');
for (var i = 0; i < removes.length; i++) {
wireMailDomainRemove(removes[i]);
}
var settings_form = add_input.form;
if (settings_form) {
listen(settings_form, 'submit', function () {
if (add_input.value.trim() !== '') {
add_button.click();
}
});
}
}
/*
* Attaches the click handler to one .mail-domain-remove button.
* Factored out so both server-rendered and dynamically-added
* buttons share the same wiring path.
*/
function wireMailDomainRemove(button)
{
listen(button, 'click', function () {
var item = button.closest('.mail-domain-item');
if (item && item.parentNode) {
item.parentNode.removeChild(item);
syncMailDomainsHidden();
}
});
}
/*
* Rebuilds the comma-separated value of the hidden
* MAIL_DOMAINS input from the current set of list items so a
* form submit reflects the latest UI state.
*/
function syncMailDomainsHidden()
{
var hidden = elt('mail-domains');
if (!hidden) {
return;
}
var spans = sel(
'#mail-domain-list .mail-domain-text');
var parts = [];
for (var i = 0; i < spans.length; i++) {
parts.push(spans[i].textContent);
}
hidden.value = parts.join(',');
}
/*
* Shows or hides the plaintext SMTP and IMAP port rows
* based on the "allow insecure connections" checkbox. When
* insecure connections are not allowed those listeners are
* never bound, so their port fields would only mislead;
* they are hidden until the box is checked. Runs once on
* load to set the initial state and again whenever the box
* is toggled.
*/
/*
* Shows the plaintext SMTP and IMAP port rows only when
* the delivery-security choice is "insecure"; the other
* postures refuse cleartext connections, so those port
* fields would only mislead. Runs once on load and again
* whenever the dropdown changes. When the dropdown is the
* locked single-option control (no certificate), its value
* is "insecure" so the rows show.
*/
function initMailInsecureToggle()
{
var picker = elt('mail-delivery-security');
if (!picker) {
return;
}
var apply = function () {
var insecure = (picker.value == 'insecure');
setDisplay('mail-smtp-port-row', insecure);
setDisplay('mail-imap-port-row', insecure);
};
listen(picker, 'change', apply);
apply();
}
/*
* Wires the secure-domain Add and Remove buttons, mirroring
* the mail-domain list: Add appends a trimmed, de-duplicated
* entry with its own Remove button and rebuilds the hidden
* SECURE_DOMAINS comma-separated value; Remove drops an entry
* and rebuilds. The hidden input feeds the form submit.
*/
function initSecureDomainList()
{
var add_button = elt('secure-domain-add-button');
var add_input = elt('secure-domain-add');
var list = elt('secure-domain-list');
if (!add_button || !add_input || !list) {
return;
}
listen(add_button, 'click', function () {
var value = add_input.value.trim();
if (value === '' || value.indexOf(',') !== -1) {
return;
}
var spans = list.querySelectorAll(
'.mail-domain-text');
for (var i = 0; i < spans.length; i++) {
if (spans[i].textContent === value) {
add_input.value = '';
return;
}
}
var item = ce('li');
item.className = 'mail-domain-item';
var text = ce('span');
text.className = 'mail-domain-text';
text.textContent = value;
var remove = ce('button');
remove.type = 'button';
remove.className = 'secure-domain-remove mail-domain-remove';
remove.setAttribute('data-domain', value);
remove.textContent = '\u00D7';
remove.title = add_button.getAttribute(
'data-remove-label') || 'Remove';
wireSecureDomainRemove(remove);
var edit = ce('button');
edit.type = 'button';
edit.className = 'secure-domain-edit';
edit.setAttribute('data-domain', value);
edit.textContent = '\u270E';
edit.title = list.getAttribute('data-edit-label') || '';
wireSecureDomainEdit(edit);
var panel = buildDomainRoutePanel(list,
value.toLowerCase());
item.appendChild(text);
item.appendChild(edit);
item.appendChild(remove);
item.appendChild(panel);
list.appendChild(item);
add_input.value = '';
syncSecureDomainsHidden();
});
listen(add_input, 'keydown', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
add_button.click();
}
});
var removes = list.querySelectorAll(
'.secure-domain-remove');
for (var i = 0; i < removes.length; i++) {
wireSecureDomainRemove(removes[i]);
}
var edits = list.querySelectorAll('.secure-domain-edit');
for (var j = 0; j < edits.length; j++) {
wireSecureDomainEdit(edits[j]);
}
var panels = list.querySelectorAll('.domain-route-panel');
for (var k = 0; k < panels.length; k++) {
wireDomainRoutePanel(panels[k]);
}
var settings_form = add_input.form;
if (settings_form) {
listen(settings_form, 'submit', function () {
if (add_input.value.trim() !== '') {
add_button.click();
}
});
}
}
/*
* Attaches the click handler to one .secure-domain-remove
* button, shared by server-rendered and added buttons.
*/
function wireSecureDomainRemove(button)
{
listen(button, 'click', function () {
var item = button.closest('.mail-domain-item');
if (item && item.parentNode) {
item.parentNode.removeChild(item);
syncSecureDomainsHidden();
}
});
}
/*
* Rebuilds the comma-separated value of the hidden
* SECURE_DOMAINS input from the current list items.
*/
function syncSecureDomainsHidden()
{
var hidden = elt('secure-domains');
if (!hidden) {
return;
}
var spans = sel(
'#secure-domain-list .mail-domain-text');
var parts = [];
for (var i = 0; i < spans.length; i++) {
parts.push(spans[i].textContent);
}
hidden.value = parts.join(',');
}
/*
* Builds the routing panel for one secure-domain row. The
* panel holds a Default/Custom routing select, a group-name
* field shown for Custom, and an apply button that requests
* the route change. Labels come from data- attributes on the
* list element so they stay localized.
*/
function buildDomainRoutePanel(list, domain)
{
var panel = ce('div');
panel.className = 'domain-route-panel';
panel.setAttribute('data-domain', domain);
panel.style.display = 'none';
var mode_label = ce('label');
mode_label.textContent =
(list.getAttribute('data-routing-label') || '') + ' ';
var mode = ce('select');
mode.className = 'domain-route-mode';
var default_option = ce('option');
default_option.value = 'default';
default_option.textContent =
list.getAttribute('data-default-label') || 'Default';
var custom_option = ce('option');
custom_option.value = 'custom';
custom_option.textContent =
list.getAttribute('data-custom-label') || 'Custom';
mode.appendChild(default_option);
mode.appendChild(custom_option);
mode_label.appendChild(mode);
var group_label = ce('label');
group_label.className = 'domain-route-group-label';
group_label.style.display = 'none';
group_label.textContent =
(list.getAttribute('data-group-label') || '') + ' ';
var group_input = ce('input');
group_input.type = 'text';
group_input.className = 'domain-route-group';
group_label.appendChild(group_input);
var apply = ce('button');
apply.type = 'button';
apply.className = 'domain-route-apply';
apply.textContent = '\u2192';
apply.title = list.getAttribute('data-apply-label') || '';
panel.appendChild(mode_label);
panel.appendChild(group_label);
panel.appendChild(apply);
wireDomainRoutePanel(panel);
return panel;
}
/*
* Wires one routing panel: switching the select between
* Default and Custom shows or hides the group-name field,
* and the apply button requests the route change for the
* panel's domain (an empty or Default selection clears the
* route). The request goes to the servers updateroute
* action, which stores the route and redirects back with a
* status message.
*/
function wireDomainRoutePanel(panel)
{
var mode = panel.querySelector('.domain-route-mode');
var group_label = panel.querySelector(
'.domain-route-group-label');
var group_input = panel.querySelector(
'.domain-route-group');
var apply = panel.querySelector('.domain-route-apply');
if (!mode || !group_label || !group_input || !apply) {
return;
}
listen(mode, 'change', function () {
group_label.style.display =
(mode.value === 'custom') ? '' : 'none';
});
listen(apply, 'click', function () {
var list = elt('secure-domain-list');
var route_url = list ?
list.getAttribute('data-route-url') : '';
var domain = panel.getAttribute('data-domain') || '';
var route_group = (mode.value === 'custom') ?
group_input.value.trim() : '';
if (route_url === '' || domain === '') {
return;
}
window.location = route_url + '&route_domain=' +
encodeURIComponent(domain) + '&route_group=' +
encodeURIComponent(route_group);
});
}
/*
* Attaches the click handler to one .secure-domain-edit
* (pencil) button: it toggles visibility of the routing panel
* in the button's list item.
*/
function wireSecureDomainEdit(button)
{
listen(button, 'click', function () {
var item = button.closest('.mail-domain-item');
var panel = item ?
item.querySelector('.domain-route-panel') : null;
if (panel) {
panel.style.display =
(panel.style.display === 'none') ? '' : 'none';
}
});
}
/*
* Shows the Run As User and ALPN fields only when Yioop
* serves over its own WebSite HTTP server, where those
* apply; under Apache or nginx the front server owns the
* ports and protocol so they are hidden. The under-WebSite
* flag is fixed server-side. The Domains list is always
* shown: it feeds certificates when Auto SSL Certs is on
* and per-domain landing routes either way.
*/
function showHideWebServer()
{
setDisplay('web-server-atto-config',
<?= $data['show_web_server_atto'] ?>);
}
/*
* Rebuilds the comma-separated value of the hidden
* ALPN_PROTOCOLS input from the checked protocol boxes, in
* the order they appear, so the saved list reflects the
* current selection.
*/
function updateAlpnProtocols()
{
var hidden = elt('alpn-protocols');
if (!hidden) {
return;
}
/* The checkboxes are shown in descending version order
(h3, h2, http/1.1), which is also preference order
(most modern first), so collecting in document order
gives the right advertise order. */
var boxes = sel(
'.alpn-protocol-box');
var parts = [];
for (var i = 0; i < boxes.length; i++) {
if (boxes[i].checked) {
parts.push(boxes[i].value);
}
}
hidden.value = parts.join(',');
}
/*
* Fills the mail port fields with their conventional
* values: SMTP 25 (MX and STARTTLS), submission 587
* (STARTTLS submission), IMAP 143 (STARTTLS), and the
* wrapped implicit-TLS ports SMTPS 465 and IMAPS 993. All
* five open together when a certificate is present, so the
* defaults no longer depend on the "use STARTTLS" choice.
* Invoked from the "Use Default Ports" link; the operator
* can still edit any field afterward.
*/
function setMailDefaultPorts()
{
var smtp = elt('mail-smtp-port');
var submission = elt('mail-submission-port');
var smtps = elt('mail-smtps-port');
var imap = elt('mail-imap-port');
var imaps = elt('mail-imaps-port');
if (smtp) {
smtp.value = 25;
}
if (submission) {
submission.value = 587;
}
if (imap) {
imap.value = 143;
}
if (smtps) {
smtps.value = 465;
}
if (imaps) {
imaps.value = 993;
}
}
</script>
<?php
}
}