Replace enabled checkbox with account mode select
Identities can now be set as primary (default), alias of an existing separate account, or separate account. Alias records store parent_id and skip server validation. Password sentinel changed to a safe string that survives HTML form round-trips. Preconfig host composition fixed to include security scheme.
This commit is contained in:
@@ -20,7 +20,9 @@ var ident_switch_portDefaults = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
$("INPUT[name='_ident_switch.form.common.enabled']").change();
|
// Apply initial mode visibility
|
||||||
|
var initialMode = $("SELECT[name='_ident_switch.form.common.mode']").val() || 'primary';
|
||||||
|
plugin_switchIdent_mode_onChange(initialMode);
|
||||||
plugin_switchIdent_processPreconfig();
|
plugin_switchIdent_processPreconfig();
|
||||||
|
|
||||||
// Bind security change handlers
|
// Bind security change handlers
|
||||||
@@ -87,6 +89,42 @@ $(function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle mode select change: show/hide form sections based on selected mode.
|
||||||
|
* @param {string} mode - 'primary', 'alias:N', or 'separate'.
|
||||||
|
*/
|
||||||
|
function plugin_switchIdent_mode_onChange(mode) {
|
||||||
|
// Find the fieldsets/legends for each section
|
||||||
|
var $modeFld = $("SELECT[name='_ident_switch.form.common.mode']");
|
||||||
|
var $allFieldsets = $modeFld.closest('form').find('fieldset');
|
||||||
|
var $readonlyRow = $("INPUT[name='_ident_switch.form.common.readonly']").parentsUntil('FIELDSET', 'TR, .row');
|
||||||
|
|
||||||
|
// Separate account fieldsets (label, IMAP, SMTP, Sieve, Notify)
|
||||||
|
var separateFieldsets = [];
|
||||||
|
$allFieldsets.each(function() {
|
||||||
|
var $fs = $(this);
|
||||||
|
if ($fs.find("INPUT[name='_ident_switch.form.common.label']").length ||
|
||||||
|
$fs.find("INPUT[name='_ident_switch.form.imap.host']").length ||
|
||||||
|
$fs.find("INPUT[name='_ident_switch.form.smtp.host']").length ||
|
||||||
|
$fs.find("INPUT[name='_ident_switch.form.sieve.host']").length ||
|
||||||
|
$fs.find("INPUT[name='_ident_switch.form.notify.check']").length) {
|
||||||
|
separateFieldsets.push($fs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mode === 'separate') {
|
||||||
|
// Show all separate account fieldsets
|
||||||
|
$.each(separateFieldsets, function(_, $fs) { $fs.show(); });
|
||||||
|
plugin_switchIdent_processPreconfig();
|
||||||
|
} else {
|
||||||
|
// Hide all separate account fieldsets for primary and alias modes
|
||||||
|
$.each(separateFieldsets, function(_, $fs) { $fs.hide(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always hide readonly row
|
||||||
|
$readonlyRow.hide();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle security dropdown change: update port placeholder and show/hide warning.
|
* Handle security dropdown change: update port placeholder and show/hide warning.
|
||||||
* @param {string} proto - Protocol name (imap, smtp, sieve).
|
* @param {string} proto - Protocol name (imap, smtp, sieve).
|
||||||
@@ -187,15 +225,6 @@ function plugin_switchIdent_processPreconfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function plugin_switchIdent_enabled_onChange(e) {
|
|
||||||
var $enFld = $("INPUT[name='_ident_switch.form.common.enabled'], INPUT[name='_ident_switch.form.imap.host'], INPUT[name='_ident_switch.form.smtp.host']");
|
|
||||||
var $fieldset = $enFld.parents("FIELDSET");
|
|
||||||
var isEnabled = $enFld.is(":checked");
|
|
||||||
$("INPUT[name!='_ident_switch.form.common.enabled']", $fieldset).prop("disabled", !isEnabled);
|
|
||||||
$("SELECT", $fieldset).prop("disabled", !isEnabled);
|
|
||||||
plugin_switchIdent_processPreconfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle email field change: apply preconfig and manage domain restriction.
|
* Handle email field change: apply preconfig and manage domain restriction.
|
||||||
* @param {string} email - The email address entered by the user.
|
* @param {string} email - The email address entered by the user.
|
||||||
@@ -217,17 +246,22 @@ function plugin_switchIdent_onEmailChange(email) {
|
|||||||
// Update username placeholder to match current email
|
// Update username placeholder to match current email
|
||||||
$("INPUT[name='_ident_switch.form.imap.username']").attr('placeholder', email);
|
$("INPUT[name='_ident_switch.form.imap.username']").attr('placeholder', email);
|
||||||
|
|
||||||
// Show/hide domain warning
|
// Show/hide domain warning and restrict "separate" option
|
||||||
|
var $modeSelect = $("SELECT[name='_ident_switch.form.common.mode']");
|
||||||
if (preconfigOnly && !cfg) {
|
if (preconfigOnly && !cfg) {
|
||||||
var tpl = rcmail.env.ident_switch_warning_tpl || '';
|
var tpl = rcmail.env.ident_switch_warning_tpl || '';
|
||||||
$('#ident-switch-domain-warning').text(tpl.replace('%s', domain)).show();
|
$('#ident-switch-domain-warning').text(tpl.replace('%s', domain)).show();
|
||||||
$("INPUT[name='_ident_switch.form.common.enabled']").prop('checked', false).prop('disabled', true);
|
// Disable "separate" option but keep alias options available
|
||||||
plugin_switchIdent_enabled_onChange();
|
$modeSelect.find('option[value="separate"]').prop('disabled', true);
|
||||||
|
if ($modeSelect.val() === 'separate') {
|
||||||
|
$modeSelect.val('primary');
|
||||||
|
plugin_switchIdent_mode_onChange('primary');
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#ident-switch-domain-warning').hide();
|
$('#ident-switch-domain-warning').hide();
|
||||||
$("INPUT[name='_ident_switch.form.common.enabled']").prop('disabled', false);
|
$modeSelect.find('option[value="separate"]').prop('disabled', false);
|
||||||
|
|
||||||
// Only auto-fill for identities without an existing DB record
|
// Only auto-fill for identities without an existing DB record
|
||||||
if (!rcmail.env.ident_switch_has_record && cfg) {
|
if (!rcmail.env.ident_switch_has_record && cfg) {
|
||||||
@@ -241,6 +275,9 @@ function plugin_switchIdent_onEmailChange(email) {
|
|||||||
* @param {string} email - The full email address.
|
* @param {string} email - The full email address.
|
||||||
*/
|
*/
|
||||||
function plugin_switchIdent_applyJsPreconfig(cfg, email) {
|
function plugin_switchIdent_applyJsPreconfig(cfg, email) {
|
||||||
|
// Pre-fill server fields silently without changing mode.
|
||||||
|
// The user must explicitly select "Separate account" to see them.
|
||||||
|
|
||||||
// Apply protocol settings
|
// Apply protocol settings
|
||||||
$.each(['imap', 'smtp', 'sieve'], function(_, proto) {
|
$.each(['imap', 'smtp', 'sieve'], function(_, proto) {
|
||||||
if (!cfg[proto]) return;
|
if (!cfg[proto]) return;
|
||||||
@@ -282,6 +319,6 @@ function plugin_switchIdent_applyJsPreconfig(cfg, email) {
|
|||||||
}
|
}
|
||||||
$("INPUT[name='_ident_switch.form.common.readonly']").val(readonlyLevel);
|
$("INPUT[name='_ident_switch.form.common.readonly']").val(readonlyLevel);
|
||||||
|
|
||||||
// Re-apply enabled/disabled state (enables fields, then processPreconfig disables readonly ones)
|
// Re-apply preconfig readonly state
|
||||||
plugin_switchIdent_enabled_onChange();
|
plugin_switchIdent_processPreconfig();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
class IdentSwitchForm
|
class IdentSwitchForm
|
||||||
{
|
{
|
||||||
|
/** @var string Sentinel value placed in password fields to indicate an existing password. */
|
||||||
|
private const PASSWORD_SENTINEL = '__IDENT_SWITCH_UNCHANGED__';
|
||||||
|
|
||||||
private ident_switch $plugin;
|
private ident_switch $plugin;
|
||||||
|
|
||||||
public function __construct(ident_switch $plugin)
|
public function __construct(ident_switch $plugin)
|
||||||
@@ -27,9 +30,82 @@ class IdentSwitchForm
|
|||||||
* Build the common form fields for identity settings.
|
* Build the common form fields for identity settings.
|
||||||
*
|
*
|
||||||
* @param array $record Identity record data used for placeholders.
|
* @param array $record Identity record data used for placeholders.
|
||||||
* @return array Form field definitions for enabled, label, and readonly.
|
* @return array Form field definitions for mode select, label, and readonly.
|
||||||
*/
|
*/
|
||||||
public function get_common_fields(array &$record): array
|
public function get_common_fields(array &$record, bool $domainAllowed = true, string $warningHtml = ''): array
|
||||||
|
{
|
||||||
|
$prefix = 'ident_switch.form.common.';
|
||||||
|
$rc = rcmail::get_instance();
|
||||||
|
|
||||||
|
// Build mode select: Primary / Alias of X / Separate account
|
||||||
|
$modeSelect = new html_select([
|
||||||
|
'name' => "_{$prefix}mode",
|
||||||
|
'onchange' => 'plugin_switchIdent_mode_onChange(this.value);',
|
||||||
|
]);
|
||||||
|
$modeSelect->add($this->plugin->gettext('form.common.mode.primary'), 'primary');
|
||||||
|
|
||||||
|
// Add available accounts as alias targets
|
||||||
|
$accounts = $this->get_available_accounts($rc, $record['identity_id'] ?? null);
|
||||||
|
foreach ($accounts as $acc) {
|
||||||
|
$label = $acc['label'] ?: $acc['email'];
|
||||||
|
$modeSelect->add($label, 'alias:' . $acc['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only offer "Separate account" when domain is allowed
|
||||||
|
if ($domainAllowed) {
|
||||||
|
$modeSelect->add($this->plugin->gettext('form.common.mode.separate'), 'separate');
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentMode = $record["{$prefix}mode"] ?? 'primary';
|
||||||
|
$modeHtml = $modeSelect->show($currentMode)
|
||||||
|
. html::span(
|
||||||
|
['class' => 'form-text'],
|
||||||
|
rcube::Q($this->plugin->gettext('form.common.mode.hint'))
|
||||||
|
)
|
||||||
|
. $warningHtml;
|
||||||
|
|
||||||
|
return [
|
||||||
|
$prefix . 'mode' => ['value' => $modeHtml],
|
||||||
|
$prefix . 'readonly' => ['type' => 'hidden'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all separate accounts available as alias targets.
|
||||||
|
*
|
||||||
|
* @param rcmail $rc Roundcube instance.
|
||||||
|
* @param int|null $excludeIid Identity ID to exclude (the identity being edited).
|
||||||
|
* @return array List of account records with id, label, username, email.
|
||||||
|
*/
|
||||||
|
private function get_available_accounts(rcmail $rc, ?int $excludeIid): array
|
||||||
|
{
|
||||||
|
$sql = 'SELECT isw.id, isw.label, isw.username, ii.email'
|
||||||
|
. ' FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' isw'
|
||||||
|
. ' INNER JOIN ' . $rc->db->table_name('identities') . ' ii ON isw.iid = ii.identity_id'
|
||||||
|
. ' WHERE isw.user_id = ? AND isw.flags & ? > 0 AND isw.parent_id IS NULL';
|
||||||
|
|
||||||
|
$params = [$rc->user->ID, ident_switch::DB_ENABLED];
|
||||||
|
|
||||||
|
if ($excludeIid !== null) {
|
||||||
|
$sql .= ' AND isw.iid != ?';
|
||||||
|
$params[] = $excludeIid;
|
||||||
|
}
|
||||||
|
|
||||||
|
$q = $rc->db->query($sql, ...$params);
|
||||||
|
$accounts = [];
|
||||||
|
while ($r = $rc->db->fetch_assoc($q)) {
|
||||||
|
$accounts[] = $r;
|
||||||
|
}
|
||||||
|
return $accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the separate account header fields (label).
|
||||||
|
*
|
||||||
|
* @param array $record Identity record data.
|
||||||
|
* @return array Form field definitions for label.
|
||||||
|
*/
|
||||||
|
public function get_separate_fields(array &$record): array
|
||||||
{
|
{
|
||||||
$prefix = 'ident_switch.form.common.';
|
$prefix = 'ident_switch.form.common.';
|
||||||
|
|
||||||
@@ -46,9 +122,7 @@ class IdentSwitchForm
|
|||||||
);
|
);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
$prefix . 'enabled' => ['type' => 'checkbox', 'onchange' => 'plugin_switchIdent_enabled_onChange();'],
|
|
||||||
$prefix . 'label' => ['value' => $labelHtml],
|
$prefix . 'label' => ['value' => $labelHtml],
|
||||||
$prefix . 'readonly' => ['type' => 'hidden'],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +140,7 @@ class IdentSwitchForm
|
|||||||
$prefix . 'security' => ['value' => $this->build_security_select($prefix, $record, 'ssl')],
|
$prefix . 'security' => ['value' => $this->build_security_select($prefix, $record, 'ssl')],
|
||||||
$prefix . 'port' => ['type' => 'text', 'size' => 5, 'placeholder' => 993],
|
$prefix . 'port' => ['type' => 'text', 'size' => 5, 'placeholder' => 993],
|
||||||
$prefix . 'username' => ['type' => 'text', 'size' => 64, 'placeholder' => $record['email'] ?? ''],
|
$prefix . 'username' => ['type' => 'text', 'size' => 64, 'placeholder' => $record['email'] ?? ''],
|
||||||
$prefix . 'password' => ['type' => 'password', 'size' => 64, 'autocomplete' => 'new-password'],
|
$prefix . 'password' => ['type' => 'password', 'size' => 64, 'autocomplete' => 'off'],
|
||||||
$prefix . 'delimiter' => ['value' => $this->build_delimiter_field($prefix, $record)],
|
$prefix . 'delimiter' => ['value' => $this->build_delimiter_field($prefix, $record)],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -96,7 +170,7 @@ class IdentSwitchForm
|
|||||||
$prefix . 'port' => ['type' => 'text', 'size' => 5, 'placeholder' => 587],
|
$prefix . 'port' => ['type' => 'text', 'size' => 5, 'placeholder' => 587],
|
||||||
$prefix . 'auth' => ['value' => $authType->show($authVal !== null ? [$authVal] : [])],
|
$prefix . 'auth' => ['value' => $authType->show($authVal !== null ? [$authVal] : [])],
|
||||||
$prefix . 'username' => ['type' => 'text', 'size' => 64, 'autocomplete' => 'off'],
|
$prefix . 'username' => ['type' => 'text', 'size' => 64, 'autocomplete' => 'off'],
|
||||||
$prefix . 'password' => ['type' => 'password', 'size' => 64, 'autocomplete' => 'new-password'],
|
$prefix . 'password' => ['type' => 'password', 'size' => 64, 'autocomplete' => 'off'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +198,7 @@ class IdentSwitchForm
|
|||||||
$prefix . 'port' => ['type' => 'text', 'size' => 5, 'placeholder' => 4190],
|
$prefix . 'port' => ['type' => 'text', 'size' => 5, 'placeholder' => 4190],
|
||||||
$prefix . 'auth' => ['value' => $authType->show($authVal !== null ? [$authVal] : [])],
|
$prefix . 'auth' => ['value' => $authType->show($authVal !== null ? [$authVal] : [])],
|
||||||
$prefix . 'username' => ['type' => 'text', 'size' => 64, 'autocomplete' => 'off'],
|
$prefix . 'username' => ['type' => 'text', 'size' => 64, 'autocomplete' => 'off'],
|
||||||
$prefix . 'password' => ['type' => 'password', 'size' => 64, 'autocomplete' => 'new-password'],
|
$prefix . 'password' => ['type' => 'password', 'size' => 64, 'autocomplete' => 'off'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,8 +453,13 @@ class IdentSwitchForm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checkboxes: absent from POST means unchecked
|
// Mode select
|
||||||
$record['ident_switch.form.common.enabled'] = !empty(self::get_field_value('common', 'enabled', false));
|
$modeVal = self::get_field_value('common', 'mode', false);
|
||||||
|
if ($modeVal !== null) {
|
||||||
|
$record['ident_switch.form.common.mode'] = $modeVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checkbox: absent from POST means unchecked
|
||||||
$record['ident_switch.form.notify.check'] = !empty(self::get_field_value('notify', 'check', false));
|
$record['ident_switch.form.notify.check'] = !empty(self::get_field_value('notify', 'check', false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,42 +582,27 @@ class IdentSwitchForm
|
|||||||
|
|
||||||
$this->plugin->add_texts('localization');
|
$this->plugin->add_texts('localization');
|
||||||
|
|
||||||
// Build info section with description and domain warning
|
|
||||||
$preconfigOnly = $rc->config->get('ident_switch.preconfig_only', false);
|
$preconfigOnly = $rc->config->get('ident_switch.preconfig_only', false);
|
||||||
$domainAllowed = empty($args['record']['email']) || $this->is_domain_allowed($args['record']['email']);
|
$domainAllowed = empty($args['record']['email']) || $this->is_domain_allowed($args['record']['email']);
|
||||||
|
|
||||||
$warningVisible = $preconfigOnly && !$domainAllowed;
|
// Build domain warning HTML (included in mode field)
|
||||||
|
$warningHtml = '';
|
||||||
// Extract domain from email for warning message
|
if ($preconfigOnly) {
|
||||||
$email = $args['record']['email'] ?? '';
|
$email = $args['record']['email'] ?? '';
|
||||||
$domain = '';
|
$domain = '';
|
||||||
if (!empty($email) && str_contains($email, '@')) {
|
if (!empty($email) && str_contains($email, '@')) {
|
||||||
$domain = substr($email, strpos($email, '@') + 1);
|
$domain = substr($email, strpos($email, '@') + 1);
|
||||||
}
|
}
|
||||||
|
$warningHtml = html::div(
|
||||||
$infoContent = html::div(
|
|
||||||
['class' => 'boxinformation', 'id' => 'ident-switch-info'],
|
|
||||||
rcube::Q($this->plugin->gettext('form.description'))
|
|
||||||
);
|
|
||||||
$warningContent = html::div(
|
|
||||||
['class' => 'boxwarning', 'id' => 'ident-switch-domain-warning',
|
['class' => 'boxwarning', 'id' => 'ident-switch-domain-warning',
|
||||||
'style' => $warningVisible ? '' : 'display:none'],
|
'style' => $domainAllowed ? 'display:none' : ''],
|
||||||
rcube::Q(sprintf($this->plugin->gettext('form.preconfig_only_warning'), $domain))
|
rcube::Q(sprintf($this->plugin->gettext('form.preconfig_only_warning'), $domain))
|
||||||
);
|
);
|
||||||
|
}
|
||||||
$args['form']['ident_switch'] = [
|
|
||||||
'name' => $this->plugin->gettext('form.caption'),
|
|
||||||
'content' => $infoContent . $warningContent,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Pass preconfig data to JS for dynamic form updates
|
// Pass preconfig data to JS for dynamic form updates
|
||||||
$this->pass_preconfig_to_js($rc);
|
$this->pass_preconfig_to_js($rc);
|
||||||
|
|
||||||
// When preconfig_only is enabled, hide field sections for non-preconfigured domains
|
|
||||||
if (!$domainAllowed) {
|
|
||||||
return $args;
|
|
||||||
}
|
|
||||||
|
|
||||||
$row = null;
|
$row = null;
|
||||||
if (isset($args['record']['identity_id'])) {
|
if (isset($args['record']['identity_id'])) {
|
||||||
$sql = 'SELECT * FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?';
|
$sql = 'SELECT * FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?';
|
||||||
@@ -581,8 +645,22 @@ class IdentSwitchForm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse flags
|
// Replace encrypted passwords with sentinel (shows dots in form, detectable on save)
|
||||||
$record['ident_switch.form.common.enabled'] = (bool)($row['flags'] & ident_switch::DB_ENABLED);
|
foreach (['imap.password', 'smtp.password', 'sieve.password'] as $passKey) {
|
||||||
|
$fullKey = 'ident_switch.form.' . $passKey;
|
||||||
|
if (!empty($record[$fullKey])) {
|
||||||
|
$record[$fullKey] = self::PASSWORD_SENTINEL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine mode: alias (has parent_id) or separate account
|
||||||
|
if ($row['parent_id'] !== null) {
|
||||||
|
$record['ident_switch.form.common.mode'] = 'alias:' . $row['parent_id'];
|
||||||
|
} elseif ($row['flags'] & ident_switch::DB_ENABLED) {
|
||||||
|
$record['ident_switch.form.common.mode'] = 'separate';
|
||||||
|
} else {
|
||||||
|
$record['ident_switch.form.common.mode'] = 'primary';
|
||||||
|
}
|
||||||
|
|
||||||
// Parse host schemes into separate security fields
|
// Parse host schemes into separate security fields
|
||||||
foreach (['imap', 'smtp', 'sieve'] as $proto) {
|
foreach (['imap', 'smtp', 'sieve'] as $proto) {
|
||||||
@@ -621,7 +699,11 @@ class IdentSwitchForm
|
|||||||
|
|
||||||
$args['form']['ident_switch.common'] = [
|
$args['form']['ident_switch.common'] = [
|
||||||
'name' => $this->plugin->gettext('form.common.general'),
|
'name' => $this->plugin->gettext('form.common.general'),
|
||||||
'content' => $this->get_common_fields($record),
|
'content' => $this->get_common_fields($record, $domainAllowed, $warningHtml),
|
||||||
|
];
|
||||||
|
$args['form']['ident_switch.separate'] = [
|
||||||
|
'name' => $this->plugin->gettext('form.caption'),
|
||||||
|
'content' => $this->get_separate_fields($record),
|
||||||
];
|
];
|
||||||
$args['form']['ident_switch.imap'] = [
|
$args['form']['ident_switch.imap'] = [
|
||||||
'name' => $this->plugin->gettext('form.imap.caption'),
|
'name' => $this->plugin->gettext('form.imap.caption'),
|
||||||
@@ -670,32 +752,46 @@ class IdentSwitchForm
|
|||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block save for non-preconfigured domains when preconfig_only is enabled
|
$mode = self::get_field_value('common', 'mode', false) ?? 'primary';
|
||||||
if (!$this->is_domain_allowed($args['record']['email'])) {
|
|
||||||
|
// Block separate mode for non-preconfigured domains (alias and primary are always allowed)
|
||||||
|
if (!$this->is_domain_allowed($args['record']['email']) && $mode === 'separate') {
|
||||||
$this->disable($args['id']);
|
$this->disable($args['id']);
|
||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!self::get_field_value('common', 'enabled', false)) {
|
if ($mode === 'primary') {
|
||||||
$this->disable($args['id']);
|
$this->disable($args['id']);
|
||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (str_starts_with($mode, 'alias:')) {
|
||||||
|
$parentId = (int)substr($mode, 6);
|
||||||
|
$this->save_alias($args['id'], $parentId);
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mode === 'separate': full validation + save
|
||||||
$data = $this->validate();
|
$data = $this->validate();
|
||||||
if (!empty($data['err'])) {
|
if (!empty($data['err'])) {
|
||||||
$this->plugin->add_texts('localization');
|
$this->plugin->add_texts('localization');
|
||||||
$args['abort'] = true;
|
$args['abort'] = true;
|
||||||
|
$args['result'] = false;
|
||||||
$args['message'] = 'ident_switch.err.' . $data['err'];
|
$args['message'] = 'ident_switch.err.' . $data['err'];
|
||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->apply_readonly_preconfig($data, $args['record']['email']);
|
$this->apply_readonly_preconfig($data, $args['record']['email']);
|
||||||
|
|
||||||
|
// Resolve sentinel passwords to actual passwords for connection testing
|
||||||
|
$testPass = $this->resolve_password_for_test($rc, $args['id'], $data['imap.pass']);
|
||||||
|
|
||||||
// Test connections before saving
|
// Test connections before saving
|
||||||
$connErr = $this->test_connections($data, $args['record']['email'], $data['imap.pass']);
|
$connErr = $this->test_connections($data, $args['record']['email'], $testPass);
|
||||||
if ($connErr) {
|
if ($connErr) {
|
||||||
$this->plugin->add_texts('localization');
|
$this->plugin->add_texts('localization');
|
||||||
$args['abort'] = true;
|
$args['abort'] = true;
|
||||||
|
$args['result'] = false;
|
||||||
$args['message'] = 'ident_switch.err.' . $connErr;
|
$args['message'] = 'ident_switch.err.' . $connErr;
|
||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
@@ -723,30 +819,48 @@ class IdentSwitchForm
|
|||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Block creation for non-preconfigured domains when preconfig_only is enabled
|
$mode = self::get_field_value('common', 'mode', false) ?? 'primary';
|
||||||
if (!$this->is_domain_allowed($args['record']['email'])) {
|
|
||||||
|
// Block separate mode for non-preconfigured domains (alias and primary are always allowed)
|
||||||
|
if (!$this->is_domain_allowed($args['record']['email']) && $mode === 'separate') {
|
||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!self::get_field_value('common', 'enabled', false)) {
|
if ($mode === 'primary') {
|
||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (str_starts_with($mode, 'alias:')) {
|
||||||
|
$parentId = (int)substr($mode, 6);
|
||||||
|
$_SESSION['createData' . ident_switch::MY_POSTFIX] = [
|
||||||
|
'mode' => 'alias',
|
||||||
|
'parent_id' => $parentId,
|
||||||
|
'label' => self::get_field_value('common', 'label'),
|
||||||
|
];
|
||||||
|
return $args;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mode === 'separate': full validation
|
||||||
$data = $this->validate();
|
$data = $this->validate();
|
||||||
if (!empty($data['err'])) {
|
if (!empty($data['err'])) {
|
||||||
$this->plugin->add_texts('localization');
|
$this->plugin->add_texts('localization');
|
||||||
$args['abort'] = true;
|
$args['abort'] = true;
|
||||||
|
$args['result'] = false;
|
||||||
$args['message'] = 'ident_switch.err.' . $data['err'];
|
$args['message'] = 'ident_switch.err.' . $data['err'];
|
||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->apply_readonly_preconfig($data, $args['record']['email']);
|
$this->apply_readonly_preconfig($data, $args['record']['email']);
|
||||||
|
|
||||||
|
// For new identities, sentinel should not appear, but handle defensively
|
||||||
|
$testPass = ($data['imap.pass'] === self::PASSWORD_SENTINEL) ? '' : $data['imap.pass'];
|
||||||
|
|
||||||
// Test connections before saving
|
// Test connections before saving
|
||||||
$connErr = $this->test_connections($data, $args['record']['email'], $data['imap.pass']);
|
$connErr = $this->test_connections($data, $args['record']['email'], $testPass);
|
||||||
if ($connErr) {
|
if ($connErr) {
|
||||||
$this->plugin->add_texts('localization');
|
$this->plugin->add_texts('localization');
|
||||||
$args['abort'] = true;
|
$args['abort'] = true;
|
||||||
|
$args['result'] = false;
|
||||||
$args['message'] = 'ident_switch.err.' . $connErr;
|
$args['message'] = 'ident_switch.err.' . $connErr;
|
||||||
return $args;
|
return $args;
|
||||||
}
|
}
|
||||||
@@ -779,6 +893,8 @@ class IdentSwitchForm
|
|||||||
unset($_SESSION['createData' . ident_switch::MY_POSTFIX]);
|
unset($_SESSION['createData' . ident_switch::MY_POSTFIX]);
|
||||||
if (!$data || count($data) === 0) {
|
if (!$data || count($data) === 0) {
|
||||||
ident_switch::write_log("Object with ident_switch values not found in session for ID = {$args['id']}.");
|
ident_switch::write_log("Object with ident_switch values not found in session for ID = {$args['id']}.");
|
||||||
|
} elseif (($data['mode'] ?? '') === 'alias') {
|
||||||
|
$this->save_alias($args['id'], $data['parent_id']);
|
||||||
} else {
|
} else {
|
||||||
$data['id'] = $args['id'];
|
$data['id'] = $args['id'];
|
||||||
$this->save($rc, $data);
|
$this->save($rc, $data);
|
||||||
@@ -829,15 +945,12 @@ class IdentSwitchForm
|
|||||||
$record = ['email' => $email];
|
$record = ['email' => $email];
|
||||||
$preconfig->apply($record);
|
$preconfig->apply($record);
|
||||||
|
|
||||||
// Map preconfig record keys to validated data keys
|
// Map simple preconfig record keys to validated data keys
|
||||||
$map = [
|
$map = [
|
||||||
'ident_switch.form.imap.host' => 'imap.host',
|
|
||||||
'ident_switch.form.imap.port' => 'imap.port',
|
'ident_switch.form.imap.port' => 'imap.port',
|
||||||
'ident_switch.form.imap.delimiter' => 'imap.delimiter',
|
'ident_switch.form.imap.delimiter' => 'imap.delimiter',
|
||||||
'ident_switch.form.imap.username' => 'imap.user',
|
'ident_switch.form.imap.username' => 'imap.user',
|
||||||
'ident_switch.form.smtp.host' => 'smtp.host',
|
|
||||||
'ident_switch.form.smtp.port' => 'smtp.port',
|
'ident_switch.form.smtp.port' => 'smtp.port',
|
||||||
'ident_switch.form.sieve.host' => 'sieve.host',
|
|
||||||
'ident_switch.form.sieve.port' => 'sieve.port',
|
'ident_switch.form.sieve.port' => 'sieve.port',
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -846,6 +959,44 @@ class IdentSwitchForm
|
|||||||
$data[$dataKey] = $record[$recordKey];
|
$data[$dataKey] = $record[$recordKey];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compose hosts with security scheme (preconfig stores them separately)
|
||||||
|
foreach (['imap', 'smtp', 'sieve'] as $proto) {
|
||||||
|
$hostKey = "ident_switch.form.{$proto}.host";
|
||||||
|
$secKey = "ident_switch.form.{$proto}.security";
|
||||||
|
if (empty($data["{$proto}.host"]) && !empty($record[$hostKey])) {
|
||||||
|
$security = $record[$secKey] ?? '';
|
||||||
|
$data["{$proto}.host"] = self::compose_host_scheme($record[$hostKey], $security);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve IMAP password for connection testing.
|
||||||
|
*
|
||||||
|
* If the password is the sentinel (unchanged), decrypt from DB.
|
||||||
|
* Otherwise return the raw password as-is.
|
||||||
|
*
|
||||||
|
* @param rcmail $rc Roundcube instance.
|
||||||
|
* @param int $iid Identity ID.
|
||||||
|
* @param string|null $pass Raw password from POST.
|
||||||
|
* @return string Decrypted or raw password for testing.
|
||||||
|
*/
|
||||||
|
private function resolve_password_for_test(rcmail $rc, int $iid, ?string $pass): string
|
||||||
|
{
|
||||||
|
if ($pass !== self::PASSWORD_SENTINEL) {
|
||||||
|
return $pass ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = 'SELECT password FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?';
|
||||||
|
$q = $rc->db->query($sql, $iid, $rc->user->ID);
|
||||||
|
$r = $rc->db->fetch_assoc($q);
|
||||||
|
if (!$r || empty($r['password'])) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$decrypted = $rc->decrypt($r['password']);
|
||||||
|
return ($decrypted !== false) ? $decrypted : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -922,8 +1073,8 @@ class IdentSwitchForm
|
|||||||
return $retVal;
|
return $retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
$retVal['smtp.auth'] = self::get_field_value('smtp', 'auth');
|
$retVal['smtp.auth'] = self::get_field_value('smtp', 'auth') ?? (string)ident_switch::SMTP_AUTH_IMAP;
|
||||||
if (!ctype_digit($retVal['smtp.auth'] ?? '')) {
|
if (!ctype_digit($retVal['smtp.auth'])) {
|
||||||
$retVal['err'] = 'auth.num';
|
$retVal['err'] = 'auth.num';
|
||||||
return $retVal;
|
return $retVal;
|
||||||
}
|
}
|
||||||
@@ -960,8 +1111,8 @@ class IdentSwitchForm
|
|||||||
return $retVal;
|
return $retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
$retVal['sieve.auth'] = self::get_field_value('sieve', 'auth');
|
$retVal['sieve.auth'] = self::get_field_value('sieve', 'auth') ?? (string)ident_switch::SIEVE_AUTH_IMAP;
|
||||||
if (!ctype_digit($retVal['sieve.auth'] ?? '')) {
|
if (!ctype_digit($retVal['sieve.auth'])) {
|
||||||
$retVal['err'] = 'auth.num';
|
$retVal['err'] = 'auth.num';
|
||||||
return $retVal;
|
return $retVal;
|
||||||
}
|
}
|
||||||
@@ -1042,7 +1193,7 @@ class IdentSwitchForm
|
|||||||
// Record already exists, will update it
|
// Record already exists, will update it
|
||||||
$sql = 'UPDATE ' .
|
$sql = 'UPDATE ' .
|
||||||
$rc->db->table_name(ident_switch::TABLE) .
|
$rc->db->table_name(ident_switch::TABLE) .
|
||||||
' SET flags = ?, label = ?, imap_host = ?, imap_port = ?, imap_delimiter = ?, username = ?, password = ?,' .
|
' SET flags = ?, parent_id = ?, label = ?, imap_host = ?, imap_port = ?, imap_delimiter = ?, username = ?, password = ?,' .
|
||||||
' smtp_host = ?, smtp_port = ?, smtp_auth = ?, smtp_username = ?, smtp_password = ?,' .
|
' smtp_host = ?, smtp_port = ?, smtp_auth = ?, smtp_username = ?, smtp_password = ?,' .
|
||||||
' sieve_host = ?, sieve_port = ?, sieve_auth = ?, sieve_username = ?, sieve_password = ?,' .
|
' sieve_host = ?, sieve_port = ?, sieve_auth = ?, sieve_username = ?, sieve_password = ?,' .
|
||||||
' notify_check = ?, notify_basic = ?, notify_sound = ?, notify_desktop = ?,' .
|
' notify_check = ?, notify_basic = ?, notify_sound = ?, notify_desktop = ?,' .
|
||||||
@@ -1052,34 +1203,31 @@ class IdentSwitchForm
|
|||||||
// No record exists, create new one
|
// No record exists, create new one
|
||||||
$sql = 'INSERT INTO ' .
|
$sql = 'INSERT INTO ' .
|
||||||
$rc->db->table_name(ident_switch::TABLE) .
|
$rc->db->table_name(ident_switch::TABLE) .
|
||||||
'(flags, label, imap_host, imap_port, imap_delimiter, username, password,' .
|
'(flags, parent_id, label, imap_host, imap_port, imap_delimiter, username, password,' .
|
||||||
' smtp_host, smtp_port, smtp_auth, smtp_username, smtp_password,' .
|
' smtp_host, smtp_port, smtp_auth, smtp_username, smtp_password,' .
|
||||||
' sieve_host, sieve_port, sieve_auth, sieve_username, sieve_password,' .
|
' sieve_host, sieve_port, sieve_auth, sieve_username, sieve_password,' .
|
||||||
' notify_check, notify_basic, notify_sound, notify_desktop,' .
|
' notify_check, notify_basic, notify_sound, notify_desktop,' .
|
||||||
' user_id, iid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
' user_id, iid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt IMAP password (compare raw POST value with decrypted DB value)
|
// Handle IMAP password: sentinel means unchanged, empty means clear, else encrypt
|
||||||
$existingImapPass = !empty($r['password']) ? $rc->decrypt($r['password']) : null;
|
if ($data['imap.pass'] === self::PASSWORD_SENTINEL) {
|
||||||
if ($data['imap.pass'] === $existingImapPass && $existingImapPass !== false) {
|
$data['imap.pass'] = $r['password'] ?? null;
|
||||||
$data['imap.pass'] = $r['password'];
|
|
||||||
} else {
|
} else {
|
||||||
$data['imap.pass'] = $rc->encrypt($data['imap.pass']);
|
$data['imap.pass'] = $data['imap.pass'] ? $rc->encrypt($data['imap.pass']) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt SMTP password
|
// Handle SMTP password
|
||||||
$existingSmtpPass = !empty($r['smtp_password']) ? $rc->decrypt($r['smtp_password']) : null;
|
if ($data['smtp.pass'] === self::PASSWORD_SENTINEL) {
|
||||||
if ($data['smtp.pass'] === $existingSmtpPass && $existingSmtpPass !== false) {
|
|
||||||
$data['smtp.pass'] = $r['smtp_password'] ?? null;
|
$data['smtp.pass'] = $r['smtp_password'] ?? null;
|
||||||
} else {
|
} else {
|
||||||
$data['smtp.pass'] = $data['smtp.pass'] ? $rc->encrypt($data['smtp.pass']) : null;
|
$data['smtp.pass'] = $data['smtp.pass'] ? $rc->encrypt($data['smtp.pass']) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt Sieve password
|
// Handle Sieve password
|
||||||
$existingSievePass = !empty($r['sieve_password']) ? $rc->decrypt($r['sieve_password']) : null;
|
if ($data['sieve.pass'] === self::PASSWORD_SENTINEL) {
|
||||||
if ($data['sieve.pass'] === $existingSievePass && $existingSievePass !== false) {
|
|
||||||
$data['sieve.pass'] = $r['sieve_password'] ?? null;
|
$data['sieve.pass'] = $r['sieve_password'] ?? null;
|
||||||
} else {
|
} else {
|
||||||
$data['sieve.pass'] = $data['sieve.pass'] ? $rc->encrypt($data['sieve.pass']) : null;
|
$data['sieve.pass'] = $data['sieve.pass'] ? $rc->encrypt($data['sieve.pass']) : null;
|
||||||
@@ -1088,6 +1236,7 @@ class IdentSwitchForm
|
|||||||
$rc->db->query(
|
$rc->db->query(
|
||||||
$sql,
|
$sql,
|
||||||
$data['flags'],
|
$data['flags'],
|
||||||
|
null, // parent_id: NULL for separate accounts
|
||||||
$data['label'],
|
$data['label'],
|
||||||
$data['imap.host'],
|
$data['imap.host'],
|
||||||
$data['imap.port'],
|
$data['imap.port'],
|
||||||
@@ -1116,6 +1265,54 @@ class IdentSwitchForm
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save an alias link between an identity and a parent account.
|
||||||
|
*
|
||||||
|
* Creates or updates an ident_switch record with parent_id set and
|
||||||
|
* all server fields cleared (alias uses parent's config).
|
||||||
|
*
|
||||||
|
* @param int $iid Identity ID of the alias.
|
||||||
|
* @param int $parentId ident_switch.id of the parent account.
|
||||||
|
*/
|
||||||
|
private function save_alias(int $iid, int $parentId): void
|
||||||
|
{
|
||||||
|
$rc = rcmail::get_instance();
|
||||||
|
|
||||||
|
// Validate that parent_id exists and belongs to this user
|
||||||
|
$sql = 'SELECT id FROM ' . $rc->db->table_name(ident_switch::TABLE)
|
||||||
|
. ' WHERE id = ? AND user_id = ? AND parent_id IS NULL';
|
||||||
|
$q = $rc->db->query($sql, $parentId, $rc->user->ID);
|
||||||
|
if (!$rc->db->fetch_assoc($q)) {
|
||||||
|
ident_switch::write_log("Alias save: parent account with id={$parentId} not found for user.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$label = self::get_field_value('common', 'label');
|
||||||
|
|
||||||
|
// Check if record already exists
|
||||||
|
$sql = 'SELECT id FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?';
|
||||||
|
$q = $rc->db->query($sql, $iid, $rc->user->ID);
|
||||||
|
$r = $rc->db->fetch_assoc($q);
|
||||||
|
|
||||||
|
if ($r) {
|
||||||
|
$sql = 'UPDATE ' . $rc->db->table_name(ident_switch::TABLE)
|
||||||
|
. ' SET flags = ?, parent_id = ?, label = ?,'
|
||||||
|
. ' imap_host = NULL, imap_port = NULL, imap_delimiter = NULL,'
|
||||||
|
. ' username = NULL, password = NULL,'
|
||||||
|
. ' smtp_host = NULL, smtp_port = NULL, smtp_auth = 1, smtp_username = NULL, smtp_password = NULL,'
|
||||||
|
. ' sieve_host = NULL, sieve_port = NULL, sieve_auth = 1, sieve_username = NULL, sieve_password = NULL,'
|
||||||
|
. ' notify_check = 0, notify_basic = NULL, notify_sound = NULL, notify_desktop = NULL'
|
||||||
|
. ' WHERE id = ?';
|
||||||
|
$rc->db->query($sql, ident_switch::DB_ENABLED, $parentId, $label, $r['id']);
|
||||||
|
} else {
|
||||||
|
$sql = 'INSERT INTO ' . $rc->db->table_name(ident_switch::TABLE)
|
||||||
|
. ' (flags, parent_id, label, user_id, iid) VALUES (?, ?, ?, ?, ?)';
|
||||||
|
$rc->db->query($sql, ident_switch::DB_ENABLED, $parentId, $label, $rc->user->ID, $iid);
|
||||||
|
}
|
||||||
|
|
||||||
|
ident_switch::write_log("Saved alias link: identity {$iid} → parent account {$parentId}.");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable the ident_switch flag for a given identity.
|
* Disable the ident_switch flag for a given identity.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ $labels['form.preconfig_only_warning'] = 'Die Konfiguration als separates Konto
|
|||||||
// General
|
// General
|
||||||
$labels['form.common.general'] = 'Allgemein';
|
$labels['form.common.general'] = 'Allgemein';
|
||||||
|
|
||||||
// Enabled
|
// Account mode
|
||||||
$labels['form.common.enabled'] = 'Aktiviert';
|
$labels['form.common.mode'] = 'Kontomodus';
|
||||||
|
$labels['form.common.mode.primary'] = 'Hauptkonto';
|
||||||
|
$labels['form.common.mode.separate'] = 'Separates Konto';
|
||||||
|
$labels['form.common.mode.hint'] = 'Verknüpfen Sie diese Identität mit einem bestehenden Konto oder konfigurieren Sie sie als separates Konto mit eigenem Mailserver.';
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
$labels['form.common.label'] = 'Bezeichnung';
|
$labels['form.common.label'] = 'Bezeichnung';
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ $labels['form.preconfig_only_warning'] = 'Separate account configuration is not
|
|||||||
// General
|
// General
|
||||||
$labels['form.common.general'] = 'General';
|
$labels['form.common.general'] = 'General';
|
||||||
|
|
||||||
// Enabled
|
// Account mode
|
||||||
$labels['form.common.enabled'] = 'Enabled';
|
$labels['form.common.mode'] = 'Account mode';
|
||||||
|
$labels['form.common.mode.primary'] = 'Primary account';
|
||||||
|
$labels['form.common.mode.separate'] = 'Separate account';
|
||||||
|
$labels['form.common.mode.hint'] = 'Link this identity to an existing account, or configure it as a separate account with its own mail server.';
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
$labels['form.common.label'] = 'Label';
|
$labels['form.common.label'] = 'Label';
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ $labels['form.preconfig_only_warning'] = 'La configuration de compte séparé n\
|
|||||||
// General
|
// General
|
||||||
$labels['form.common.general'] = 'Général';
|
$labels['form.common.general'] = 'Général';
|
||||||
|
|
||||||
// Enabled
|
// Account mode
|
||||||
$labels['form.common.enabled'] = 'Activer';
|
$labels['form.common.mode'] = 'Mode de compte';
|
||||||
|
$labels['form.common.mode.primary'] = 'Compte principal';
|
||||||
|
$labels['form.common.mode.separate'] = 'Compte séparé';
|
||||||
|
$labels['form.common.mode.hint'] = 'Rattachez cette identité à un compte existant, ou configurez-la comme un compte séparé avec son propre serveur de messagerie.';
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
$labels['form.common.label'] = 'Nom à afficher';
|
$labels['form.common.label'] = 'Nom à afficher';
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ $labels['form.preconfig_only_warning'] = 'La configurazione come account separat
|
|||||||
// General
|
// General
|
||||||
$labels['form.common.general'] = 'Generale';
|
$labels['form.common.general'] = 'Generale';
|
||||||
|
|
||||||
// Enabled
|
// Account mode
|
||||||
$labels['form.common.enabled'] = 'Abilita';
|
$labels['form.common.mode'] = 'Modalità account';
|
||||||
|
$labels['form.common.mode.primary'] = 'Account principale';
|
||||||
|
$labels['form.common.mode.separate'] = 'Account separato';
|
||||||
|
$labels['form.common.mode.hint'] = 'Collega questa identità a un account esistente o configurala come un account separato con il proprio server di posta.';
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
$labels['form.common.label'] = 'Nome visualizzato';
|
$labels['form.common.label'] = 'Nome visualizzato';
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ $labels['form.preconfig_only_warning'] = 'Configuratie als afzonderlijk account
|
|||||||
// General
|
// General
|
||||||
$labels['form.common.general'] = 'Algemeen';
|
$labels['form.common.general'] = 'Algemeen';
|
||||||
|
|
||||||
// Enabled
|
// Account mode
|
||||||
$labels['form.common.enabled'] = 'Inschakelen';
|
$labels['form.common.mode'] = 'Accountmodus';
|
||||||
|
$labels['form.common.mode.primary'] = 'Hoofdaccount';
|
||||||
|
$labels['form.common.mode.separate'] = 'Afzonderlijk account';
|
||||||
|
$labels['form.common.mode.hint'] = 'Koppel deze identiteit aan een bestaand account of configureer het als een afzonderlijk account met een eigen mailserver.';
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
$labels['form.common.label'] = 'Label';
|
$labels['form.common.label'] = 'Label';
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ $labels['form.preconfig_only_warning'] = 'Настройка отдельног
|
|||||||
// General
|
// General
|
||||||
$labels['form.common.general'] = 'Общие';
|
$labels['form.common.general'] = 'Общие';
|
||||||
|
|
||||||
// Enabled
|
// Account mode
|
||||||
$labels['form.common.enabled'] = 'Включено';
|
$labels['form.common.mode'] = 'Режим аккаунта';
|
||||||
|
$labels['form.common.mode.primary'] = 'Основной аккаунт';
|
||||||
|
$labels['form.common.mode.separate'] = 'Отдельный аккаунт';
|
||||||
|
$labels['form.common.mode.hint'] = 'Привяжите эту учётную запись к существующему аккаунту или настройте её как отдельный аккаунт с собственным почтовым сервером.';
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
$labels['form.common.label'] = 'Название';
|
$labels['form.common.label'] = 'Название';
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ $labels['form.preconfig_only_warning'] = 'Konfiguracija ločenega računa za dom
|
|||||||
// General
|
// General
|
||||||
$labels['form.common.general'] = 'Splošno';
|
$labels['form.common.general'] = 'Splošno';
|
||||||
|
|
||||||
// Enabled
|
// Account mode
|
||||||
$labels['form.common.enabled'] = 'Omogočeno';
|
$labels['form.common.mode'] = 'Način računa';
|
||||||
|
$labels['form.common.mode.primary'] = 'Glavni račun';
|
||||||
|
$labels['form.common.mode.separate'] = 'Ločen račun';
|
||||||
|
$labels['form.common.mode.hint'] = 'Povežite to identiteto z obstoječim računom ali jo konfigurirajte kot ločen račun z lastnim poštnim strežnikom.';
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
$labels['form.common.label'] = 'Oznaka';
|
$labels['form.common.label'] = 'Oznaka';
|
||||||
|
|||||||
Reference in New Issue
Block a user