feat(admin): redesign VIP membership page with modal editing

- Replace per-row inline <details> forms and the separate bulk form with a
  single <dialog> shared by single-user and bulk adjustments
- Add stats row, styled tabs, date range filter with reset, sortable expiry
- Show fallback tier, remaining time, and disable unavailable operations
- History tab: labelled operation/status badges, avatars, local time,
  batch marker and error message
- Move inline script/onclick to assets/js/admin-vip.js; add vip.css
- Surface the service error message when every user fails

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Fable 5 committed 2026-10-30 16:12:12 +08:00
1 parent 18951b01d6
commit 1be52d75a0
5 files changed
+789 -126

No files matched your search

+137 -126
View File
@@ -1,6 +1,10 @@
<?php
/**
* Native WordPress VIP membership operations.
* 用户会员后台页(users.php → aksrt_user_vip):页面编排、写操作与查询。
*
* 列表展示由 UsersMenusVipView 输出;所有会员变更都通过同一个弹窗表单提交到
* admin_post_aksrt_users_mutate_vip(单人调整与批量调整共用),交互脚本见
* assets/js/admin-vip.js。
*
* @package AKSRT
*/
@@ -11,28 +15,52 @@ defined('ABSPATH') || exit;
trait UsersMenusVip {
use UsersMenusVipView;
private const VIP_PAGE_SLUG = 'aksrt_user_vip';
private const VIP_PER_PAGE = 20;
private const VIP_FILTER_KEYS = ['user_id', 'level', 'state', 'date_from', 'date_to', 'orderby', 'order'];
public function enqueue_vip_assets(string $hook): void {
if ($hook !== 'users_page_' . self::VIP_PAGE_SLUG) {
return;
}
$css = '/assets/css/admin/vip.css';
$js = '/assets/js/admin-vip.js';
if (is_file(AKSRT_DIR . $css)) {
wp_enqueue_style('aksrt-admin-vip', AKSRT_URI . $css, [NativeUi::STYLE_HANDLE], (string) filemtime(AKSRT_DIR . $css));
}
if (is_file(AKSRT_DIR . $js)) {
wp_enqueue_script('aksrt-admin-vip', AKSRT_URI . $js, [], (string) filemtime(AKSRT_DIR . $js), true);
}
}
public function render_vip_page(): void {
$this->ensure_cap();
$tab = $this->vip_tab();
$page = max(1, absint($_GET['paged'] ?? 1));
NativeUi::page_open();
NativeUi::header(__('用户会员', 'aksrt'), '', 'dashicons-awards');
NativeUi::page_open('aksrt-vip');
NativeUi::header(
__('用户会员', 'aksrt'),
__('查看会员档位与有效期,单人或批量调整会员;每次变更都会记录原因与结果。', 'aksrt'),
'dashicons-awards',
[[
'label' => __('会员配置', 'aksrt'),
'url' => admin_url('admin.php?page=aksrt-settings#/payment/vip'),
'icon' => 'dashicons-admin-generic',
]]
);
$this->flash();
$this->render_vip_stats();
$this->render_vip_tabs($tab);
if ($tab === 'history') {
$this->render_vip_history($page);
} else {
$filters = $this->vip_filters();
$result = $this->query_vip_users($filters, $page, 20);
NativeUi::card_open('', '', false, 'aksrt-native-toolbar');
$result = $this->query_vip_users($filters, $page, self::VIP_PER_PAGE);
$this->render_vip_filters($filters);
NativeUi::card_close();
$this->render_vip_bulk_form($filters);
NativeUi::table_card_open(__('会员列表', 'aksrt'));
$this->render_vip_table($result['items'], $filters);
NativeUi::table_wrap_close();
$this->pagination($result['pages'], $page);
NativeUi::card_close();
$this->render_vip_table($result['items'], $filters, $result['pages'], $page);
$this->render_vip_dialog($filters);
}
NativeUi::page_close();
}
@@ -55,6 +83,7 @@ trait UsersMenusVip {
$batch = count($ids) > 1 ? wp_generate_uuid4() : '';
$ok = $failed = $skipped = 0;
$last_error = '';
foreach ($ids as $user_id) {
if (!get_userdata($user_id) || !current_user_can('edit_user', $user_id)) {
$skipped++;
@@ -68,10 +97,17 @@ trait UsersMenusVip {
'batch_uuid' => $batch,
])
: new \WP_Error('aksrt_vip_unavailable', __('会员服务不可用。', 'aksrt'));
is_wp_error($result) ? $failed++ : $ok++;
if (is_wp_error($result)) {
$failed++;
$last_error = $result->get_error_message();
} else {
$ok++;
}
}
$message = $ok > 0 && ($failed > 0 || $skipped > 0) ? 'vip_partial' : ($ok > 0 ? 'vip_updated' : 'vip_failed');
$this->redirect_vip_result($message, [
if ($ok === 0) {
$this->redirect_vip_result('vip_failed', $last_error !== '' ? ['aksrt_vip_error' => $last_error] : []);
}
$this->redirect_vip_result($failed > 0 || $skipped > 0 ? 'vip_partial' : 'vip_updated', [
'aksrt_vip_ok' => $ok,
'aksrt_vip_failed_count' => $failed,
'aksrt_vip_skipped' => $skipped,
@@ -97,14 +133,35 @@ trait UsersMenusVip {
];
}
private function vip_filters_active(array $filters): bool {
return $filters['user_id'] > 0 || $filters['level'] !== 'all' || $filters['state'] !== 'all'
|| $filters['date_from'] !== '' || $filters['date_to'] !== '';
}
/** 当前筛选 + 覆盖项组成的列表 URL(只带非默认值)。 */
private function vip_list_url(array $filters, array $override = []): string {
$args = [];
foreach (array_merge($filters, $override) as $key => $value) {
$default = ['user_id' => 0, 'level' => 'all', 'state' => 'all', 'date_from' => '', 'date_to' => '', 'orderby' => 'id', 'order' => 'DESC'][$key] ?? null;
if ((string) $value !== (string) $default) {
$args[$key] = $value;
}
}
return add_query_arg($args, admin_url('users.php?page=' . self::VIP_PAGE_SLUG));
}
private function vip_date($value): string {
$value = sanitize_text_field((string) wp_unslash($value));
$date = \DateTimeImmutable::createFromFormat('!Y-m-d', $value, wp_timezone());
return $date && $date->format('Y-m-d') === $value ? $value : '';
}
/** @return array{items:array<int,array<string,mixed>>,pages:int} */
private function query_vip_users(array $filters, int $page, int $per_page): array {
/**
* 有效档位/到期时间的 SQL 片段:限时档位过期且存在基础档位时按基础档位计。
*
* @return array{join:string,raw_level:string,raw_expires:string,base_level:string,base_expires:string,expired_level:string,level:string,expires:string,is_expired:string,now:int}
*/
private function vip_sql_parts(): array {
global $wpdb;
$meta = "(SELECT user_id,
MAX(CASE WHEN meta_key = 'aksrt_vip_level' THEN meta_value END) level,
@@ -121,9 +178,59 @@ trait UsersMenusVip {
$base_expires = 'CAST(COALESCE(m.base_expires, 0) AS UNSIGNED)';
$expired_level = 'CAST(COALESCE(m.expired_level, 0) AS UNSIGNED)';
$now = time();
$level = "CASE WHEN {$raw_level} > 0 AND {$raw_expires} > 0 AND {$raw_expires} < {$now} AND {$base_level} > 0 THEN {$base_level} ELSE {$raw_level} END";
$expires = "CASE WHEN {$raw_level} > 0 AND {$raw_expires} > 0 AND {$raw_expires} < {$now} AND {$base_level} > 0 THEN {$base_expires} ELSE {$raw_expires} END";
$is_expired = "({$raw_level} > 0 AND {$raw_expires} > 0 AND {$raw_expires} < {$now} AND {$base_level} = 0) OR ({$raw_level} = 0 AND {$expired_level} > 0)";
$lapsed = "{$raw_level} > 0 AND {$raw_expires} > 0 AND {$raw_expires} < {$now}";
return [
'join' => "LEFT JOIN {$meta} ON m.user_id = u.ID",
'raw_level' => $raw_level,
'raw_expires' => $raw_expires,
'base_level' => $base_level,
'base_expires' => $base_expires,
'expired_level' => $expired_level,
'level' => "CASE WHEN {$lapsed} AND {$base_level} > 0 THEN {$base_level} ELSE {$raw_level} END",
'expires' => "CASE WHEN {$lapsed} AND {$base_level} > 0 THEN {$base_expires} ELSE {$raw_expires} END",
'is_expired' => "(({$lapsed} AND {$base_level} = 0) OR ({$raw_level} = 0 AND {$expired_level} > 0))",
'now' => $now,
];
}
/** 「7 天内到期」按自然日:今天 00:00 至第 7 天 23:59:59,与到期日期筛选一致。 */
private function vip_expiring_window(): array {
$today = new \DateTimeImmutable('today', wp_timezone());
$end = $today->modify('+7 days');
return ['from' => $today->format('Y-m-d'), 'to' => $end->format('Y-m-d'), 'until' => $end->setTime(23, 59, 59)->getTimestamp()];
}
/** @return array{active:int,permanent:int,expired:int,expiring:int,level_1:int,level_2:int} */
private function query_vip_stats(): array {
global $wpdb;
$p = $this->vip_sql_parts();
$valid = "{$p['level']} > 0 AND ({$p['expires']} = 0 OR {$p['expires']} >= {$p['now']})";
$row = $wpdb->get_row($wpdb->prepare(
"SELECT
SUM(CASE WHEN {$p['level']} > 0 AND {$p['expires']} >= {$p['now']} THEN 1 ELSE 0 END) active,
SUM(CASE WHEN {$p['level']} > 0 AND {$p['expires']} = 0 THEN 1 ELSE 0 END) permanent,
SUM(CASE WHEN {$p['is_expired']} THEN 1 ELSE 0 END) expired,
SUM(CASE WHEN {$p['level']} > 0 AND {$p['expires']} >= {$p['now']} AND {$p['expires']} <= %d THEN 1 ELSE 0 END) expiring,
SUM(CASE WHEN {$valid} AND {$p['level']} = %d THEN 1 ELSE 0 END) level_1,
SUM(CASE WHEN {$valid} AND {$p['level']} = %d THEN 1 ELSE 0 END) level_2
FROM {$wpdb->users} u {$p['join']}",
$this->vip_expiring_window()['until'],
1,
2
), ARRAY_A);
$stats = [];
foreach (['active', 'permanent', 'expired', 'expiring', 'level_1', 'level_2'] as $key) {
$stats[$key] = (int) ($row[$key] ?? 0);
}
return $stats;
}
/** @return array{items:array<int,array<string,mixed>>,pages:int} */
private function query_vip_users(array $filters, int $page, int $per_page): array {
global $wpdb;
$p = $this->vip_sql_parts();
$level = $p['level'];
$expires = $p['expires'];
$where = ['1=1'];
$params = [];
if ($filters['user_id']) {
@@ -136,13 +243,13 @@ trait UsersMenusVip {
}
if ($filters['state'] === 'active') {
$where[] = "{$level} > 0 AND {$expires} >= %d";
$params[] = $now;
$params[] = $p['now'];
} elseif ($filters['state'] === 'permanent') {
$where[] = "{$level} > 0 AND {$expires} = 0";
} elseif ($filters['state'] === 'expired') {
$where[] = $is_expired;
$where[] = $p['is_expired'];
} elseif ($filters['state'] === 'none') {
$where[] = "{$level} = 0 AND NOT {$is_expired}";
$where[] = "{$level} = 0 AND NOT {$p['is_expired']}";
}
foreach (['date_from' => '00:00:00', 'date_to' => '23:59:59'] as $key => $time_of_day) {
if ($filters[$key] !== '') {
@@ -151,104 +258,25 @@ trait UsersMenusVip {
$params[] = $date->getTimestamp();
}
}
$from = "FROM {$wpdb->users} u LEFT JOIN {$meta} ON m.user_id = u.ID WHERE " . implode(' AND ', $where);
$from = "FROM {$wpdb->users} u {$p['join']} WHERE " . implode(' AND ', $where);
$count_sql = "SELECT COUNT(*) {$from}";
$total = $params ? (int) $wpdb->get_var($wpdb->prepare($count_sql, $params)) : (int) $wpdb->get_var($count_sql);
$sort = $filters['orderby'] === 'expires'
? "CASE WHEN {$expires} = 0 THEN 1 ELSE 0 END {$filters['order']}, {$expires} {$filters['order']}, u.ID DESC"
: "u.ID {$filters['order']}";
$query = "SELECT u.ID, u.display_name, u.user_login, {$level} level, {$expires} expires,
{$raw_level} raw_level, {$raw_expires} raw_expires, {$base_level} base_level, {$base_expires} base_expires,
{$expired_level} expired_level, {$is_expired} is_expired {$from} ORDER BY {$sort} LIMIT %d OFFSET %d";
{$p['raw_level']} raw_level, {$p['raw_expires']} raw_expires, {$p['base_level']} base_level, {$p['base_expires']} base_expires,
{$p['expired_level']} expired_level, {$p['is_expired']} is_expired {$from} ORDER BY {$sort} LIMIT %d OFFSET %d";
$items = $wpdb->get_results($wpdb->prepare($query, array_merge($params, [$per_page, ($page - 1) * $per_page])), ARRAY_A);
return ['items' => is_array($items) ? $items : [], 'pages' => max(1, (int) ceil(max(1, $total) / $per_page))];
}
private function render_vip_tabs(string $tab): void {
$base = admin_url('users.php?page=aksrt_user_vip');
echo '<h2 class="nav-tab-wrapper"><a class="nav-tab ' . ($tab === 'members' ? 'nav-tab-active' : '') . '" href="' . esc_url($base) . '">' . esc_html__('会员列表', 'aksrt') . '</a><a class="nav-tab ' . ($tab === 'history' ? 'nav-tab-active' : '') . '" href="' . esc_url(add_query_arg('tab', 'history', $base)) . '">' . esc_html__('变更记录', 'aksrt') . '</a></h2>';
}
private function render_vip_filters(array $filters): void {
$names = $this->vip_level_names();
echo '<form method="get" class="aksrt-native-filters"><input type="hidden" name="page" value="aksrt_user_vip">';
echo '<label>UID<input type="number" min="1" name="user_id" value="' . esc_attr((string) $filters['user_id']) . '"></label><label>' . esc_html__('档位', 'aksrt') . '<select name="level">';
foreach (['all' => __('全部', 'aksrt'), '0' => __('普通用户', 'aksrt'), '1' => $names[1], '2' => $names[2]] as $key => $label) {
printf('<option value="%s"%s>%s</option>', esc_attr($key), selected($filters['level'], $key, false), esc_html($label));
}
echo '</select></label><label>' . esc_html__('状态', 'aksrt') . '<select name="state">';
foreach (['all' => __('全部', 'aksrt'), 'active' => __('有效', 'aksrt'), 'permanent' => __('永久', 'aksrt'), 'expired' => __('已过期', 'aksrt'), 'none' => __('无会员', 'aksrt')] as $key => $label) {
printf('<option value="%s"%s>%s</option>', esc_attr($key), selected($filters['state'], $key, false), esc_html($label));
}
echo '</select></label><label>' . esc_html__('到期从', 'aksrt') . '<input type="date" name="date_from" value="' . esc_attr($filters['date_from']) . '"></label><label>' . esc_html__('到期至', 'aksrt') . '<input type="date" name="date_to" value="' . esc_attr($filters['date_to']) . '"></label>';
submit_button(__('筛选', 'aksrt'), 'secondary', '', false);
echo '</form>';
}
private function render_vip_bulk_form(array $filters): void {
$names = $this->vip_level_names();
NativeUi::card_open(__('批量操作', 'aksrt'));
echo '<form id="aksrt-vip-bulk" method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-native-inline">';
echo '<input type="hidden" name="action" value="aksrt_users_mutate_vip">';
wp_nonce_field('aksrt_users_mutate_vip');
$this->render_vip_return_fields($filters);
$this->render_vip_operation_fields($names);
echo ' <label>' . esc_html__('原因', 'aksrt') . ' <input required name="reason" class="regular-text"></label> ';
submit_button(__('应用到选中用户', 'aksrt'), 'primary', '', false);
echo '</form>';
NativeUi::card_close();
}
private function render_vip_table(array $items, array $filters): void {
echo '<table class="wp-list-table widefat fixed striped"><thead><tr><td class="manage-column column-cb check-column"><input type="checkbox" aria-label="' . esc_attr__('选择全部用户', 'aksrt') . '" onclick="document.querySelectorAll(\'.aksrt-vip-check\').forEach((element) => element.checked = this.checked)"></td><th>UID</th><th>' . esc_html__('用户', 'aksrt') . '</th><th>' . esc_html__('当前会员', 'aksrt') . '</th><th>' . esc_html__('到期时间', 'aksrt') . '</th><th>' . esc_html__('操作', 'aksrt') . '</th></tr></thead><tbody>';
if ($items === []) {
echo NativeUi::empty_row(6, __('暂无用户', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
}
foreach ($items as $row) {
$this->render_vip_row($row, $filters);
}
echo '</tbody></table>';
}
private function render_vip_row(array $row, array $filters): void {
$id = (int) $row['ID'];
$names = $this->vip_level_names();
$level = (int) $row['level'];
$expires = (int) $row['expires'];
$label = $level > 0 ? ($names[$level] ?? ('VIP' . $level)) : __('普通用户', 'aksrt');
$expiry = !empty($row['is_expired'])
? __('已过期', 'aksrt')
: ($level > 0 ? ($expires === 0 ? __('永久有效', 'aksrt') : wp_date('Y-m-d H:i', $expires, wp_timezone())) : '—');
if ((int) $row['raw_level'] !== $level || (int) $row['raw_expires'] !== $expires) {
$label .= ' (' . __('基础档位恢复后', 'aksrt') . ')';
}
$tone = $level > 0 ? ((int) $row['raw_level'] !== $level || (int) $row['raw_expires'] !== $expires ? 'warning' : 'success') : 'neutral';
echo '<tr><td class="check-column"><input class="aksrt-vip-check" form="aksrt-vip-bulk" type="checkbox" name="user_ids[]" value="' . $id . '"></td><td>' . $id . '</td><td>' . NativeUi::user_cell($id, (string) $row['display_name']) . '<div class="aksrt-native-muted">@' . esc_html((string) $row['user_login']) . '</div></td><td>' . NativeUi::badge($label, $tone) . '</td><td>' . esc_html($expiry) . '</td><td><details><summary class="button button-small">' . esc_html__('调整', 'aksrt') . '</summary><form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="aksrt-native-form-foot"><input type="hidden" name="action" value="aksrt_users_mutate_vip"><input type="hidden" name="user_ids[]" value="' . $id . '">'; // phpcs:ignore WordPress.Security.EscapeOutput -- builders escape.
wp_nonce_field('aksrt_users_mutate_vip');
$this->render_vip_return_fields($filters);
$this->render_vip_operation_fields($names);
echo '<label class="aksrt-native-field is-wide">' . esc_html__('原因', 'aksrt') . '<input required name="reason" class="regular-text"></label>';
submit_button(__('保存', 'aksrt'), 'secondary', '', false);
echo '</form></details></td></tr>';
}
private function render_vip_operation_fields(array $names): void {
echo '<select class="aksrt-vip-operation" name="vip_operation"><option value="set">' . esc_html__('设置会员', 'aksrt') . '</option><option value="extend">' . esc_html__('延长有效期', 'aksrt') . '</option><option value="restore">' . esc_html__('取消限时升级/恢复回退会员', 'aksrt') . '</option><option value="disable">' . esc_html__('停用会员', 'aksrt') . '</option></select> ';
echo '<span class="aksrt-vip-set-fields"><label>' . esc_html__('档位', 'aksrt') . ' <select name="level"><option value="1">' . esc_html($names[1]) . '</option><option value="2">' . esc_html($names[2]) . '</option></select></label> ';
echo '<label><input type="checkbox" name="permanent" value="1">' . esc_html__('永久', 'aksrt') . '</label> <label>' . esc_html__('绝对到期日', 'aksrt') . ' <input type="date" name="expires_date"></label></span> ';
echo '<span class="aksrt-vip-extend-fields"><label>' . esc_html__('延长', 'aksrt') . ' <input class="aksrt-vip-amount small-text" type="number" name="amount" min="1" max="3650" value="1"><select name="unit"><option value="day">' . esc_html__('天', 'aksrt') . '</option><option value="month">' . esc_html__('月', 'aksrt') . '</option></select></label></span>';
echo '<script>document.querySelectorAll(".aksrt-vip-operation").forEach(function(select){function sync(){var form=select.closest("form");if(!form)return;var set=form.querySelector(".aksrt-vip-set-fields"),extend=form.querySelector(".aksrt-vip-extend-fields"),danger=select.value==="disable"||select.value==="restore";if(set)set.hidden=select.value!=="set";if(extend)extend.hidden=select.value!=="extend";if(danger&&!form.dataset.confirmBound){form.addEventListener("submit",function(e){if((select.value==="disable"&&!window.confirm("停用后将立即撤销该用户的前端会员权益,并清理恢复状态,确定继续吗?"))||(select.value==="restore"&&!window.confirm("将恢复回退会员并取消当前限时升级,确定继续吗?")))e.preventDefault();});form.dataset.confirmBound="1";}}sync();select.addEventListener("change",sync);});</script>';
}
/** @return array<string,mixed>|\WP_Error */
private function vip_mutation_payload(string $operation): array|\WP_Error {
if (!in_array($operation, ['set', 'extend', 'disable', 'restore'], true)) {
return new \WP_Error('aksrt_vip_operation_invalid', __('会员操作无效。', 'aksrt'));
}
if ($operation === 'disable') {
return [];
}
if ($operation === 'restore') {
if ($operation === 'disable' || $operation === 'restore') {
return [];
}
if ($operation === 'extend') {
@@ -268,23 +296,6 @@ trait UsersMenusVip {
return ['level' => absint($_POST['level'] ?? 0), 'expires' => $expires];
}
private function render_vip_history(int $page): void {
$result = (new \AKSRT\Content\VipMembershipEventRepository())->query($page, 20);
NativeUi::table_card_open(__('变更记录', 'aksrt'));
echo '<table class="wp-list-table widefat fixed striped"><thead><tr><th>' . esc_html__('时间', 'aksrt') . '</th><th>' . esc_html__('用户', 'aksrt') . '</th><th>' . esc_html__('操作', 'aksrt') . '</th><th>' . esc_html__('操作人', 'aksrt') . '</th><th>' . esc_html__('原因', 'aksrt') . '</th><th>' . esc_html__('结果', 'aksrt') . '</th></tr></thead><tbody>';
if ($result['items'] === []) {
echo NativeUi::empty_row(6, __('暂无变更记录', 'aksrt')); // phpcs:ignore WordPress.Security.EscapeOutput -- builder escapes.
}
foreach ($result['items'] as $event) {
$user = (string) ($event['display_name'] ?: $event['user_login']);
echo '<tr><td>' . esc_html((string) $event['created_at']) . '</td><td>' . esc_html($user) . ' (#' . (int) $event['user_id'] . ')</td><td>' . esc_html((string) $event['operation']) . '</td><td>' . esc_html((string) ($event['actor_name'] ?: '—')) . '</td><td>' . esc_html((string) $event['reason']) . '</td><td>' . esc_html((string) $event['status']) . '</td></tr>';
}
echo '</tbody></table>';
NativeUi::table_wrap_close();
$this->pagination($result['pages'], $page);
NativeUi::card_close();
}
/** @return array<int,string> */
private function vip_level_names(): array {
$levels = class_exists('\AKSRT\Settings\Settings') ? (array) (\AKSRT\Settings\Settings::get_payment_vip_settings()['levels'] ?? []) : [];
@@ -295,18 +306,18 @@ trait UsersMenusVip {
}
private function render_vip_return_fields(array $filters): void {
foreach (['user_id', 'level', 'state', 'date_from', 'date_to', 'orderby', 'order'] as $key) {
foreach (self::VIP_FILTER_KEYS as $key) {
echo '<input type="hidden" name="return_' . esc_attr($key) . '" value="' . esc_attr((string) $filters[$key]) . '">';
}
}
private function redirect_vip_result(string $message, array $extra = []): void {
$filters = [];
foreach (['user_id', 'level', 'state', 'date_from', 'date_to', 'orderby', 'order'] as $key) {
foreach (self::VIP_FILTER_KEYS as $key) {
if (isset($_POST['return_' . $key])) {
$filters[$key] = sanitize_text_field((string) wp_unslash($_POST['return_' . $key]));
}
}
$this->redirect('aksrt_user_vip', $message, array_merge($filters, $extra));
$this->redirect(self::VIP_PAGE_SLUG, $message, array_merge($filters, $extra));
}
}