mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2024-12-23 02:04:46 +02:00
Merge pull request #6045 from mailcow/feat/rename-mbox
[Web][DockerApi] Add Feature to Rename Email Addresses
This commit is contained in:
commit
2d76ffc88c
@ -342,6 +342,30 @@ class DockerApi:
|
||||
cmd = ["/bin/bash", "-c", cmd_vmail]
|
||||
maildir_cleanup = container.exec_run(cmd, user='vmail')
|
||||
return self.exec_run_handler('generic', maildir_cleanup)
|
||||
# api call: container_post - post_action: exec - cmd: maildir - task: move
|
||||
def container_post__exec__maildir__move(self, request_json, **kwargs):
|
||||
if 'container_id' in kwargs:
|
||||
filters = {"id": kwargs['container_id']}
|
||||
elif 'container_name' in kwargs:
|
||||
filters = {"name": kwargs['container_name']}
|
||||
|
||||
if 'old_maildir' in request_json and 'new_maildir' in request_json:
|
||||
for container in self.sync_docker_client.containers.list(filters=filters):
|
||||
vmail_name = request_json['old_maildir'].replace("'", "'\\''")
|
||||
new_vmail_name = request_json['new_maildir'].replace("'", "'\\''")
|
||||
cmd_vmail = f"if [[ -d '/var/vmail/{vmail_name}' ]]; then /bin/mv '/var/vmail/{vmail_name}' '/var/vmail/{new_vmail_name}'; fi"
|
||||
|
||||
index_name = request_json['old_maildir'].split("/")
|
||||
new_index_name = request_json['new_maildir'].split("/")
|
||||
if len(index_name) > 1 and len(new_index_name) > 1:
|
||||
index_name = index_name[1].replace("'", "'\\''") + "@" + index_name[0].replace("'", "'\\''")
|
||||
new_index_name = new_index_name[1].replace("'", "'\\''") + "@" + new_index_name[0].replace("'", "'\\''")
|
||||
cmd_vmail_index = f"if [[ -d '/var/vmail_index/{index_name}' ]]; then /bin/mv '/var/vmail_index/{index_name}' '/var/vmail_index/{new_index_name}_index'; fi"
|
||||
cmd = ["/bin/bash", "-c", cmd_vmail + " && " + cmd_vmail_index]
|
||||
else:
|
||||
cmd = ["/bin/bash", "-c", cmd_vmail]
|
||||
maildir_move = container.exec_run(cmd, user='vmail')
|
||||
return self.exec_run_handler('generic', maildir_move)
|
||||
# api call: container_post - post_action: exec - cmd: rspamd - task: worker_password
|
||||
def container_post__exec__rspamd__worker_password(self, request_json, **kwargs):
|
||||
if 'container_id' in kwargs:
|
||||
@ -374,6 +398,107 @@ class DockerApi:
|
||||
self.logger.error('failed changing Rspamd password')
|
||||
res = { 'type': 'danger', 'msg': 'command did not complete' }
|
||||
return Response(content=json.dumps(res, indent=4), media_type="application/json")
|
||||
# api call: container_post - post_action: exec - cmd: sogo - task: rename
|
||||
def container_post__exec__sogo__rename_user(self, request_json, **kwargs):
|
||||
if 'container_id' in kwargs:
|
||||
filters = {"id": kwargs['container_id']}
|
||||
elif 'container_name' in kwargs:
|
||||
filters = {"name": kwargs['container_name']}
|
||||
|
||||
if 'old_username' in request_json and 'new_username' in request_json:
|
||||
for container in self.sync_docker_client.containers.list(filters=filters):
|
||||
old_username = request_json['old_username'].replace("'", "'\\''")
|
||||
new_username = request_json['new_username'].replace("'", "'\\''")
|
||||
|
||||
sogo_return = container.exec_run(["/bin/bash", "-c", f"sogo-tool rename-user '{old_username}' '{new_username}'"], user='sogo')
|
||||
return self.exec_run_handler('generic', sogo_return)
|
||||
# api call: container_post - post_action: exec - cmd: doveadm - task: get_acl
|
||||
def container_post__exec__doveadm__get_acl(self, request_json, **kwargs):
|
||||
if 'container_id' in kwargs:
|
||||
filters = {"id": kwargs['container_id']}
|
||||
elif 'container_name' in kwargs:
|
||||
filters = {"name": kwargs['container_name']}
|
||||
|
||||
for container in self.sync_docker_client.containers.list(filters=filters):
|
||||
id = request_json['id'].replace("'", "'\\''")
|
||||
|
||||
shared_folders = container.exec_run(["/bin/bash", "-c", f"doveadm mailbox list -u '{id}'"])
|
||||
shared_folders = shared_folders.output.decode('utf-8')
|
||||
shared_folders = shared_folders.splitlines()
|
||||
|
||||
formatted_acls = []
|
||||
mailbox_seen = []
|
||||
for shared_folder in shared_folders:
|
||||
if "Shared" not in shared_folder and "/" not in shared_folder:
|
||||
continue
|
||||
shared_folder = shared_folder.split("/")
|
||||
if len(shared_folder) < 3:
|
||||
continue
|
||||
|
||||
user = shared_folder[1].replace("'", "'\\''")
|
||||
mailbox = '/'.join(shared_folder[2:]).replace("'", "'\\''")
|
||||
if mailbox in mailbox_seen:
|
||||
continue
|
||||
|
||||
acls = container.exec_run(["/bin/bash", "-c", f"doveadm acl get -u '{user}' '{mailbox}'"])
|
||||
acls = acls.output.decode('utf-8').strip().splitlines()
|
||||
if len(acls) >= 2:
|
||||
for acl in acls[1:]:
|
||||
_, rights = acls[1].split(maxsplit=1)
|
||||
mailbox_seen.append(mailbox)
|
||||
formatted_acls.append({ 'user': user, 'id': id, 'mailbox': mailbox, 'rights': rights.split() })
|
||||
|
||||
return Response(content=json.dumps(formatted_acls, indent=4), media_type="application/json")
|
||||
# api call: container_post - post_action: exec - cmd: doveadm - task: delete_acl
|
||||
def container_post__exec__doveadm__delete_acl(self, request_json, **kwargs):
|
||||
if 'container_id' in kwargs:
|
||||
filters = {"id": kwargs['container_id']}
|
||||
elif 'container_name' in kwargs:
|
||||
filters = {"name": kwargs['container_name']}
|
||||
|
||||
for container in self.sync_docker_client.containers.list(filters=filters):
|
||||
user = request_json['user'].replace("'", "'\\''")
|
||||
mailbox = request_json['mailbox'].replace("'", "'\\''")
|
||||
id = request_json['id'].replace("'", "'\\''")
|
||||
|
||||
if user and mailbox and id:
|
||||
acl_delete_return = container.exec_run(["/bin/bash", "-c", f"doveadm acl delete -u '{user}' '{mailbox}' 'user={id}'"])
|
||||
return self.exec_run_handler('generic', acl_delete_return)
|
||||
# api call: container_post - post_action: exec - cmd: doveadm - task: set_acl
|
||||
def container_post__exec__doveadm__set_acl(self, request_json, **kwargs):
|
||||
if 'container_id' in kwargs:
|
||||
filters = {"id": kwargs['container_id']}
|
||||
elif 'container_name' in kwargs:
|
||||
filters = {"name": kwargs['container_name']}
|
||||
|
||||
for container in self.sync_docker_client.containers.list(filters=filters):
|
||||
user = request_json['user'].replace("'", "'\\''")
|
||||
mailbox = request_json['mailbox'].replace("'", "'\\''")
|
||||
id = request_json['id'].replace("'", "'\\''")
|
||||
rights = ""
|
||||
|
||||
available_rights = [
|
||||
"admin",
|
||||
"create",
|
||||
"delete",
|
||||
"expunge",
|
||||
"insert",
|
||||
"lookup",
|
||||
"post",
|
||||
"read",
|
||||
"write",
|
||||
"write-deleted",
|
||||
"write-seen"
|
||||
]
|
||||
for right in request_json['rights']:
|
||||
right = right.replace("'", "'\\''").lower()
|
||||
if right in available_rights:
|
||||
rights += right + " "
|
||||
|
||||
if user and mailbox and id and rights:
|
||||
acl_set_return = container.exec_run(["/bin/bash", "-c", f"doveadm acl set -u '{user}' '{mailbox}' 'user={id}' {rights}"])
|
||||
return self.exec_run_handler('generic', acl_set_return)
|
||||
|
||||
|
||||
# Collect host stats
|
||||
async def get_host_stats(self, wait=5):
|
||||
|
@ -953,11 +953,6 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
||||
$_SESSION['pending_mailcow_cc_role'] = "user";
|
||||
$_SESSION['pending_tfa_methods'] = $authenticators['additional'];
|
||||
unset($_SESSION['ldelay']);
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $user, '*'),
|
||||
'msg' => array('logged_in_as', $user)
|
||||
);
|
||||
return "pending";
|
||||
} else if (!isset($authenticators['additional']) || !is_array($authenticators['additional']) || count($authenticators['additional']) == 0) {
|
||||
// no authenticators found, login successfull
|
||||
@ -966,6 +961,11 @@ function check_login($user, $pass, $app_passwd_data = false) {
|
||||
$stmt->execute(array(':user' => $user));
|
||||
|
||||
unset($_SESSION['ldelay']);
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $user, '*'),
|
||||
'msg' => array('logged_in_as', $user)
|
||||
);
|
||||
return "user";
|
||||
}
|
||||
} elseif ($app_passwd_data['eas'] === true || $app_passwd_data['dav'] === true) {
|
||||
|
@ -3203,6 +3203,195 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
|
||||
}
|
||||
return true;
|
||||
break;
|
||||
case 'mailbox_rename':
|
||||
$domain = $_data['domain'];
|
||||
$old_local_part = $_data['old_local_part'];
|
||||
$old_username = $old_local_part . "@" . $domain;
|
||||
$new_local_part = $_data['new_local_part'];
|
||||
$new_username = $new_local_part . "@" . $domain;
|
||||
$create_alias = intval($_data['create_alias']);
|
||||
|
||||
if (!filter_var($old_username, FILTER_VALIDATE_EMAIL)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('username_invalid', $old_username)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!filter_var($new_username, FILTER_VALIDATE_EMAIL)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('username_invalid', $new_username)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
$is_now = mailbox('get', 'mailbox_details', $old_username);
|
||||
if (empty($is_now)) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $is_now['domain'])) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => 'access_denied'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get imap acls
|
||||
try {
|
||||
$exec_fields = array(
|
||||
'cmd' => 'doveadm',
|
||||
'task' => 'get_acl',
|
||||
'id' => $old_username
|
||||
);
|
||||
$imap_acls = json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true);
|
||||
// delete imap acls
|
||||
foreach ($imap_acls as $imap_acl) {
|
||||
$exec_fields = array(
|
||||
'cmd' => 'doveadm',
|
||||
'task' => 'delete_acl',
|
||||
'user' => $imap_acl['user'],
|
||||
'mailbox' => $imap_acl['mailbox'],
|
||||
'id' => $imap_acl['id']
|
||||
);
|
||||
docker('post', 'dovecot-mailcow', 'exec', $exec_fields);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => $e->getMessage()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// rename username in sql
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
$pdo->exec('SET FOREIGN_KEY_CHECKS = 0');
|
||||
|
||||
// Update username in mailbox table
|
||||
$pdo->prepare('UPDATE mailbox SET username = :new_username, local_part = :new_local_part WHERE username = :old_username')
|
||||
->execute([
|
||||
':new_username' => $new_username,
|
||||
':new_local_part' => $new_local_part,
|
||||
':old_username' => $old_username
|
||||
]);
|
||||
|
||||
$pdo->prepare("UPDATE alias SET address = :new_username, goto = :new_username2 WHERE address = :old_username")
|
||||
->execute([
|
||||
':new_username' => $new_username,
|
||||
':new_username2' => $new_username,
|
||||
':old_username' => $old_username
|
||||
]);
|
||||
|
||||
// Update the username in all related tables
|
||||
$tables = [
|
||||
'tags_mailbox' => ['username'],
|
||||
'sieve_filters' => ['username'],
|
||||
'app_passwd' => ['mailbox'],
|
||||
'user_acl' => ['username'],
|
||||
'da_acl' => ['username'],
|
||||
'quota2' => ['username'],
|
||||
'quota2replica' => ['username'],
|
||||
'pushover' => ['username'],
|
||||
'alias' => ['goto'],
|
||||
"imapsync" => ['user2'],
|
||||
'bcc_maps' => ['local_dest', 'bcc_dest'],
|
||||
'recipient_maps' => ['old_dest', 'new_dest'],
|
||||
'sender_acl' => ['logged_in_as', 'send_as']
|
||||
];
|
||||
foreach ($tables as $table => $columns) {
|
||||
foreach ($columns as $column) {
|
||||
$stmt = $pdo->prepare("UPDATE $table SET $column = :new_username WHERE $column = :old_username")
|
||||
->execute([
|
||||
':new_username' => $new_username,
|
||||
':old_username' => $old_username
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Update c_uid, c_name and mail in _sogo_static_view table
|
||||
$pdo->prepare("UPDATE _sogo_static_view SET c_uid = :new_username, c_name = :new_username2, mail = :new_username3 WHERE c_uid = :old_username")
|
||||
->execute([
|
||||
':new_username' => $new_username,
|
||||
':new_username2' => $new_username,
|
||||
':new_username3' => $new_username,
|
||||
':old_username' => $old_username
|
||||
]);
|
||||
|
||||
// Re-enable foreign key checks
|
||||
$pdo->exec('SET FOREIGN_KEY_CHECKS = 1');
|
||||
$pdo->commit();
|
||||
} catch (PDOException $e) {
|
||||
// Rollback the transaction if something goes wrong
|
||||
$pdo->rollBack();
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'danger',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => $e->getMessage()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// move maildir
|
||||
$exec_fields = array(
|
||||
'cmd' => 'maildir',
|
||||
'task' => 'move',
|
||||
'old_maildir' => $domain . '/' . $old_local_part,
|
||||
'new_maildir' => $domain . '/' . $new_local_part
|
||||
);
|
||||
docker('post', 'dovecot-mailcow', 'exec', $exec_fields);
|
||||
|
||||
// rename username in sogo
|
||||
$exec_fields = array(
|
||||
'cmd' => 'sogo',
|
||||
'task' => 'rename_user',
|
||||
'old_username' => $old_username,
|
||||
'new_username' => $new_username
|
||||
);
|
||||
docker('post', 'sogo-mailcow', 'exec', $exec_fields);
|
||||
|
||||
// set imap acls
|
||||
foreach ($imap_acls as $imap_acl) {
|
||||
$exec_fields = array(
|
||||
'cmd' => 'doveadm',
|
||||
'task' => 'set_acl',
|
||||
'user' => $imap_acl['user'],
|
||||
'mailbox' => $imap_acl['mailbox'],
|
||||
'id' => $new_username,
|
||||
'rights' => $imap_acl['rights']
|
||||
);
|
||||
docker('post', 'dovecot-mailcow', 'exec', $exec_fields);
|
||||
}
|
||||
|
||||
// create alias
|
||||
if ($create_alias == 1) {
|
||||
mailbox("add", "alias", array(
|
||||
"address" => $old_username,
|
||||
"goto" => $new_username,
|
||||
"active" => 1,
|
||||
"sogo_visible" => 1,
|
||||
"private_comment" => sprintf($lang['success']['mailbox_renamed'], $old_username, $new_username)
|
||||
));
|
||||
}
|
||||
|
||||
$_SESSION['return'][] = array(
|
||||
'type' => 'success',
|
||||
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
|
||||
'msg' => array('mailbox_renamed', $old_username, $new_username)
|
||||
);
|
||||
break;
|
||||
case 'mailbox_templates':
|
||||
if ($_SESSION['mailcow_cc_role'] != "admin") {
|
||||
$_SESSION['return'][] = array(
|
||||
|
@ -89,12 +89,14 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
||||
if ($as == "admin") {
|
||||
$_SESSION['mailcow_cc_username'] = $login_user;
|
||||
$_SESSION['mailcow_cc_role'] = "admin";
|
||||
header("Location: /admin");
|
||||
header("Location: /debug");
|
||||
die();
|
||||
}
|
||||
elseif ($as == "domainadmin") {
|
||||
$_SESSION['mailcow_cc_username'] = $login_user;
|
||||
$_SESSION['mailcow_cc_role'] = "domainadmin";
|
||||
header("Location: /mailbox");
|
||||
die();
|
||||
}
|
||||
elseif ($as == "user") {
|
||||
$_SESSION['mailcow_cc_username'] = $login_user;
|
||||
@ -110,6 +112,7 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
|
||||
die();
|
||||
}
|
||||
header("Location: /user");
|
||||
die();
|
||||
}
|
||||
elseif ($as != "pending") {
|
||||
unset($_SESSION['pending_mailcow_cc_username']);
|
||||
|
@ -58,6 +58,11 @@ $(document).ready(function() {
|
||||
$('input[name=multiple_bookings]').val($("#multiple_bookings_custom").val());
|
||||
});
|
||||
|
||||
$("#show_mailbox_rename_form").click(function() {
|
||||
$("#rename_warning").hide();
|
||||
$("#rename_form").removeClass("d-none");
|
||||
});
|
||||
|
||||
// load tags
|
||||
if ($('#tags').length){
|
||||
var tagsEl = $('#tags').parent().find('.tag-values')[0];
|
||||
|
@ -2020,6 +2020,9 @@ if (isset($_GET['query'])) {
|
||||
case "rl-mbox":
|
||||
process_edit_return(ratelimit('edit', 'mailbox', array_merge(array('object' => $items), $attr)));
|
||||
break;
|
||||
case "rename-mbox":
|
||||
process_edit_return(mailbox('edit', 'mailbox_rename', array_merge(array('mailbox' => $items), $attr)));
|
||||
break;
|
||||
case "user-acl":
|
||||
process_edit_return(acl('edit', 'user', array_merge(array('username' => $items), $attr)));
|
||||
break;
|
||||
|
@ -641,6 +641,11 @@
|
||||
"mailbox": "Mailbox bearbeiten",
|
||||
"mailbox_quota_def": "Standard-Quota einer Mailbox",
|
||||
"mailbox_relayhost_info": "Wird auf eine Mailbox und direkte Alias-Adressen angewendet. Überschreibt die Einstellung einer Domain.",
|
||||
"mailbox_rename": "Mailbox umbenennen",
|
||||
"mailbox_rename_agree": "Ich habe ein Backup erstellt.",
|
||||
"mailbox_rename_warning": "WICHTIG! Vor dem Umbenennen der Mailbox ein Backup erstellen.",
|
||||
"mailbox_rename_alias": "Alias automatisch erstellen",
|
||||
"mailbox_rename_title": "Neuer Lokaler Mailbox Name",
|
||||
"max_aliases": "Max. Aliasse",
|
||||
"max_mailboxes": "Max. Mailboxanzahl",
|
||||
"max_quota": "Max. Größe per Mailbox (MiB)",
|
||||
@ -1084,6 +1089,7 @@
|
||||
"mailbox_added": "Mailbox %s wurde angelegt",
|
||||
"mailbox_modified": "Änderungen an Mailbox %s wurden gespeichert",
|
||||
"mailbox_removed": "Mailbox %s wurde entfernt",
|
||||
"mailbox_renamed": "Mailbox wurde von %s in %s umbenannt",
|
||||
"nginx_reloaded": "Nginx wurde neu geladen",
|
||||
"object_modified": "Änderungen an Objekt %s wurden gespeichert",
|
||||
"password_policy_saved": "Passwortrichtlinie wurde erfolgreich gespeichert",
|
||||
|
@ -641,6 +641,11 @@
|
||||
"mailbox": "Edit mailbox",
|
||||
"mailbox_quota_def": "Default mailbox quota",
|
||||
"mailbox_relayhost_info": "Applied to the mailbox and direct aliases only, does override a domain relayhost.",
|
||||
"mailbox_rename": "Rename mailbox",
|
||||
"mailbox_rename_agree": "I have created a backup.",
|
||||
"mailbox_rename_warning": "IMPORTANT! Create a backup before renaming the mailbox.",
|
||||
"mailbox_rename_alias": "Create alias automatically",
|
||||
"mailbox_rename_title": "New local mailbox name",
|
||||
"max_aliases": "Max. aliases",
|
||||
"max_mailboxes": "Max. possible mailboxes",
|
||||
"max_quota": "Max. quota per mailbox (MiB)",
|
||||
@ -1091,6 +1096,7 @@
|
||||
"mailbox_added": "Mailbox %s has been added",
|
||||
"mailbox_modified": "Changes to mailbox %s have been saved",
|
||||
"mailbox_removed": "Mailbox %s has been removed",
|
||||
"mailbox_renamed": "Mailbox was renamed from %s to %s",
|
||||
"nginx_reloaded": "Nginx was reloaded",
|
||||
"object_modified": "Changes to object %s have been saved",
|
||||
"password_policy_saved": "Password policy was saved successfully",
|
||||
|
@ -9,6 +9,7 @@
|
||||
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#mpushover">{{ lang.edit.pushover }}</button></li>
|
||||
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#macl">{{ lang.edit.acl }}</button></li>
|
||||
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#mrl">{{ lang.edit.ratelimit }}</button></li>
|
||||
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-toggle="tab" data-bs-target="#mrename">⚠️ {{ lang.edit.mailbox_rename }}</button></li>
|
||||
</ul>
|
||||
<hr class="d-none d-md-block">
|
||||
<div class="tab-content">
|
||||
@ -287,10 +288,10 @@
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex d-md-none fs-5">
|
||||
<button class="btn flex-grow-1 text-start" data-bs-target="#collapse-tab-mattr" data-bs-toggle="collapse" aria-controls="collapse-tab-mattr">
|
||||
{{ lang.edit.mailbox }} <span class="badge bg-info table-lines"></span>
|
||||
{{ lang.edit.custom_attributes }} <span class="badge bg-info table-lines"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="collapse-tab-mattr" class="card-body collapse show" data-bs-parent="#mailbox-content">
|
||||
<div id="collapse-tab-mattr" class="card-body collapse" data-bs-parent="#mailbox-content">
|
||||
<form class="form-inline" data-id="mbox_attr" role="form" method="post">
|
||||
<table class="table table-condensed" style="white-space: nowrap;" id="mbox_attr_table">
|
||||
<tr>
|
||||
@ -477,6 +478,58 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mrename" class="tab-pane fade" role="tabpanel" aria-labelledby="mailbox-rename">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex d-md-none fs-5">
|
||||
<button class="btn flex-grow-1 text-start" data-bs-target="#collapse-tab-mrename" data-bs-toggle="collapse" aria-controls="collapse-tab-mrename">
|
||||
⚠️ {{ lang.edit.mailbox_rename }}<span class="badge bg-info table-lines"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="collapse-tab-mrename" class="card-body collapse" data-bs-parent="#mailbox-content">
|
||||
<div class="well">
|
||||
<div id="rename_warning">
|
||||
<p>{{ lang.edit.mailbox_rename_warning }}</p>
|
||||
<div id="confirm_show_rspamd_global_filters">
|
||||
<div class="row">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<label>
|
||||
<input type="checkbox" class="form-check-input" id="show_mailbox_rename_form"> {{ lang.edit.mailbox_rename_agree }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="rename_form" class="d-none">
|
||||
<form data-id="mboxrename" method="post">
|
||||
<input name="domain" type="hidden" value="{{ result.domain }}">
|
||||
<input name="old_local_part" type="hidden" value="{{ result.local_part }}">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-12 col-md-2">
|
||||
<span>{{ lang.edit.mailbox_rename_title }}</span>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-10 col-xl-8">
|
||||
<div class="input-group mb-2">
|
||||
<input type="text" class="form-control" name="new_local_part" autocomplete="off" value="{{ result.local_part }}">
|
||||
<span class="input-group-text">@{{ result.domain }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="col-sm-12 offset-md-2 col-md-10 col-xl-8">
|
||||
<label><input type="checkbox" class="form-check-input" value="1" name="create_alias" checked> {{ lang.edit.mailbox_rename_alias }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-12 offset-md-2 col-md-10 col-xl-8">
|
||||
<button class="btn btn-xs-lg d-block d-sm-inline btn-secondary" data-action="edit_selected" data-id="mboxrename" data-item="{{ mailbox }}" data-api-url='edit/rename-mbox' data-api-attr='{}' data-api-reload-location="/mailbox" href="#">{{ lang.edit.save }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
|
@ -535,7 +535,7 @@ services:
|
||||
- watchdog
|
||||
|
||||
dockerapi-mailcow:
|
||||
image: mailcow/dockerapi:2.08
|
||||
image: mailcow/dockerapi:2.09
|
||||
security_opt:
|
||||
- label=disable
|
||||
restart: always
|
||||
|
Loading…
Reference in New Issue
Block a user