feat: admin reset password and set quota for existing mailboxes
- Add Reset PW and Set Quota action buttons to each mailbox row - Shared edit modal that switches between password / quota modes - New woocow_admin_mailbox_edit AJAX handler in PHP - Quota reloads mailbox list after save; password closes modal silently - Customer-facing Change Password was already implemented in account JS Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -312,10 +312,11 @@
|
||||
<th>Email</th><th>Name</th><th>Quota Used</th><th>Quota Max</th><th>Active</th><th>Actions</th>
|
||||
</tr></thead><tbody>`;
|
||||
boxes.forEach(m => {
|
||||
const pct = m.percent_in_use || 0;
|
||||
const used = formatMB(m.quota_used);
|
||||
const max = formatMB(m.quota);
|
||||
const bar = `<div class="woocow-quota-bar"><div style="width:${pct}%"></div></div>`;
|
||||
const pct = m.percent_in_use || 0;
|
||||
const used = formatMB(m.quota_used);
|
||||
const max = formatMB(m.quota);
|
||||
const bar = `<div class="woocow-quota-bar"><div style="width:${pct}%"></div></div>`;
|
||||
const quotaMB = Math.round((m.quota || 0) / 1024 / 1024);
|
||||
html += `<tr>
|
||||
<td><a href="${esc(webmail)}" target="_blank">${esc(m.username)}</a></td>
|
||||
<td>${esc(m.name)}</td>
|
||||
@@ -323,7 +324,12 @@
|
||||
<td>${max}</td>
|
||||
<td>${m.active == 1 ? '✓' : '–'}</td>
|
||||
<td class="woocow-actions">
|
||||
<button class="button button-small wc-mb-del" data-email="${esc(m.username)}" style="color:#a00">Delete</button>
|
||||
<button class="button button-small wc-mb-reset-pw"
|
||||
data-email="${esc(m.username)}">Reset PW</button>
|
||||
<button class="button button-small wc-mb-set-quota"
|
||||
data-email="${esc(m.username)}" data-quota="${quotaMB}">Set Quota</button>
|
||||
<button class="button button-small wc-mb-del"
|
||||
data-email="${esc(m.username)}" style="color:#a00">Delete</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
});
|
||||
@@ -379,6 +385,69 @@
|
||||
else notice($('#wc-mb-notices'), 'error', res.data);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Edit modal: Reset PW ──────────────────────────────────────────────
|
||||
|
||||
let editEmail = '';
|
||||
let editType = '';
|
||||
|
||||
const openEditModal = (email, type, currentQuota) => {
|
||||
editEmail = email;
|
||||
editType = type;
|
||||
$('#wc-mb-edit-subtitle').text(email);
|
||||
$('#wc-mb-edit-notice').text('');
|
||||
$('#wc-mb-edit-pw-section, #wc-mb-edit-quota-section').hide();
|
||||
|
||||
if (type === 'password') {
|
||||
$('#wc-mb-edit-title').text('Reset Password');
|
||||
$('#wc-mb-edit-pass, #wc-mb-edit-pass2').val('');
|
||||
$('#wc-mb-edit-pw-section').show();
|
||||
setTimeout(() => $('#wc-mb-edit-pass').trigger('focus'), 100);
|
||||
} else {
|
||||
$('#wc-mb-edit-title').text('Set Quota');
|
||||
$('#wc-mb-edit-quota').val(currentQuota || 1024);
|
||||
$('#wc-mb-edit-quota-section').show();
|
||||
setTimeout(() => $('#wc-mb-edit-quota').trigger('focus'), 100);
|
||||
}
|
||||
$('#wc-mb-edit-modal').show();
|
||||
};
|
||||
|
||||
$(document).on('click', '.wc-mb-reset-pw', function () {
|
||||
openEditModal($(this).data('email'), 'password', null);
|
||||
});
|
||||
|
||||
$(document).on('click', '.wc-mb-set-quota', function () {
|
||||
openEditModal($(this).data('email'), 'quota', $(this).data('quota'));
|
||||
});
|
||||
|
||||
$('#wc-mb-edit-cancel').on('click', () => $('#wc-mb-edit-modal').hide());
|
||||
$(document).on('keydown', e => { if (e.key === 'Escape') $('#wc-mb-edit-modal').hide(); });
|
||||
|
||||
$('#wc-mb-edit-save').on('click', () => {
|
||||
const $note = $('#wc-mb-edit-notice').text('Saving…');
|
||||
const data = { server_id: currentServerId, email: editEmail, type: editType };
|
||||
|
||||
if (editType === 'password') {
|
||||
data.password = $('#wc-mb-edit-pass').val();
|
||||
data.password2 = $('#wc-mb-edit-pass2').val();
|
||||
if (!data.password) { $note.html('<span style="color:red">Password cannot be empty.</span>'); return; }
|
||||
if (data.password !== data.password2) { $note.html('<span style="color:red">Passwords do not match.</span>'); return; }
|
||||
} else {
|
||||
data.quota = $('#wc-mb-edit-quota').val();
|
||||
if (!data.quota || data.quota < 1) { $note.html('<span style="color:red">Enter a valid quota.</span>'); return; }
|
||||
}
|
||||
|
||||
ajax('woocow_admin_mailbox_edit', data).done(res => {
|
||||
if (res.success) {
|
||||
$('#wc-mb-edit-modal').hide();
|
||||
const msg = editType === 'password' ? 'Password updated.' : 'Quota updated.';
|
||||
notice($('#wc-mb-notices'), 'success', `<strong>${esc(editEmail)}</strong> — ${msg}`);
|
||||
if (editType === 'quota') loadMailboxes(); // refresh to show new quota
|
||||
} else {
|
||||
$note.html(`<span style="color:red">${esc(res.data)}</span>`);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── Utilities ─────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -23,6 +23,7 @@ class WooCow_Admin {
|
||||
'woocow_admin_mailboxes',
|
||||
'woocow_admin_mailbox_create',
|
||||
'woocow_admin_mailbox_delete',
|
||||
'woocow_admin_mailbox_edit',
|
||||
];
|
||||
|
||||
foreach ( $ajax_actions as $action ) {
|
||||
@@ -218,6 +219,45 @@ class WooCow_Admin {
|
||||
<div id="wc-mb-notices"></div>
|
||||
<div id="wc-mb-table-wrap"></div>
|
||||
|
||||
<!-- Edit Mailbox Modal (password / quota) -->
|
||||
<div id="wc-mb-edit-modal" class="woocow-modal" style="display:none">
|
||||
<div class="woocow-modal-box">
|
||||
<h3 id="wc-mb-edit-title">Edit Mailbox</h3>
|
||||
<p id="wc-mb-edit-subtitle" class="woocow-modal-subtitle"></p>
|
||||
|
||||
<div id="wc-mb-edit-pw-section" style="display:none">
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th><label for="wc-mb-edit-pass">New Password</label></th>
|
||||
<td><input type="password" id="wc-mb-edit-pass" class="regular-text" autocomplete="new-password"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><label for="wc-mb-edit-pass2">Confirm Password</label></th>
|
||||
<td><input type="password" id="wc-mb-edit-pass2" class="regular-text" autocomplete="new-password"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="wc-mb-edit-quota-section" style="display:none">
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th><label for="wc-mb-edit-quota">Max Quota (MB)</label></th>
|
||||
<td>
|
||||
<input type="number" id="wc-mb-edit-quota" class="small-text" min="1" step="1">
|
||||
<p class="description">Current usage is shown in the mailbox list. 1024 MB = 1 GB.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="woocow-modal-actions">
|
||||
<button class="button button-primary" id="wc-mb-edit-save">Save</button>
|
||||
<button class="button" id="wc-mb-edit-cancel">Cancel</button>
|
||||
<span id="wc-mb-edit-notice"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create Mailbox Modal -->
|
||||
<div id="wc-mb-modal" class="woocow-modal" style="display:none">
|
||||
<div class="woocow-modal-box">
|
||||
@@ -542,4 +582,54 @@ class WooCow_Admin {
|
||||
|
||||
$this->json_ok();
|
||||
}
|
||||
|
||||
public function ajax_woocow_admin_mailbox_edit(): void {
|
||||
$this->verify();
|
||||
|
||||
$server_id = absint( $_POST['server_id'] ?? 0 );
|
||||
$email = sanitize_email( $_POST['email'] ?? '' );
|
||||
$type = sanitize_text_field( $_POST['type'] ?? '' ); // 'password' or 'quota'
|
||||
|
||||
if ( ! $email ) {
|
||||
$this->json_err( 'Email address is required.' );
|
||||
}
|
||||
|
||||
$server = $this->get_server( $server_id );
|
||||
if ( ! $server ) {
|
||||
$this->json_err( 'Server not found.' );
|
||||
}
|
||||
|
||||
$api = WooCow_API::from_server( $server );
|
||||
$attr = [];
|
||||
|
||||
if ( $type === 'password' ) {
|
||||
$pass = $_POST['password'] ?? '';
|
||||
$pass2 = $_POST['password2'] ?? '';
|
||||
if ( ! $pass ) {
|
||||
$this->json_err( 'Password cannot be empty.' );
|
||||
}
|
||||
if ( $pass !== $pass2 ) {
|
||||
$this->json_err( 'Passwords do not match.' );
|
||||
}
|
||||
$attr = [ 'password' => $pass, 'password2' => $pass2 ];
|
||||
|
||||
} elseif ( $type === 'quota' ) {
|
||||
$quota = absint( $_POST['quota'] ?? 0 );
|
||||
if ( $quota < 1 ) {
|
||||
$this->json_err( 'Quota must be at least 1 MB.' );
|
||||
}
|
||||
$attr = [ 'quota' => $quota ];
|
||||
|
||||
} else {
|
||||
$this->json_err( 'Unknown edit type.' );
|
||||
}
|
||||
|
||||
$result = $api->edit_mailbox( [ $email ], $attr );
|
||||
|
||||
if ( ! $result['success'] ) {
|
||||
$this->json_err( $result['error'] ?? 'Failed to update mailbox.' );
|
||||
}
|
||||
|
||||
$this->json_ok();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user