PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.74
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.74
51.1.86 51.1.84 51.1.85 51.1.83 51.1.82 51.1.81 51.1.79 51.1.78 51.1.77 51.1.76 51.1.74 51.1.75 51.1.65 51.1.64 51.1.63 trunk 51.1.14 51.1.2 51.1.35 51.1.36 51.1.37 51.1.38 51.1.39 51.1.44 51.1.45 All 40 releases
king-addons / includes / helpers / Woo_Builder / My_Account_Manager.php

My_Account_Manager.php in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.74, at includes/helpers/Woo_Builder/My_Account_Manager.php

371 lines 11.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * My Account endpoints manager.
4 *
5 * @package King_Addons
6 */
7
8 namespace King_Addons\Woo_Builder;
9
10 use Elementor\Plugin as Elementor_Plugin;
11
12 if (!defined('ABSPATH')) {
13 exit;
14 }
15
16 /**
17 * Handles endpoint registration and template routing for My Account.
18 */
19 class My_Account_Manager
20 {
21 /**
22 * Option name for endpoints config.
23 */
24 private const OPTION_NAME = 'ka_woo_account_endpoints';
25
26 /**
27 * Cached endpoints.
28 *
29 * @var array<string,array<string,mixed>>
30 */
31 private array $endpoints = [];
32
33 /**
34 * Constructor.
35 */
36 public function __construct()
37 {
38 add_action('init', [$this, 'register_custom_endpoints']);
39 add_filter('woocommerce_account_menu_items', [$this, 'filter_menu_items'], 99);
40 add_filter('woocommerce_get_endpoint_url', [$this, 'filter_logout_url'], 10, 4);
41 add_action('template_redirect', [$this, 'maybe_render_endpoint_template'], 1);
42 // Use priority 15 to ensure the parent menu exists before adding this submenu
43 add_action('admin_menu', [$this, 'register_admin_page'], 15);
44 add_action('admin_init', [$this, 'register_settings']);
45 }
46
47 /**
48 * Register custom endpoints (rewrite).
49 *
50 * @return void
51 */
52 public function register_custom_endpoints(): void
53 {
54 foreach ($this->get_endpoints() as $slug => $data) {
55 if (!empty($data['is_custom']) && !empty($data['enabled'])) {
56 add_rewrite_endpoint($slug, EP_ROOT | EP_PAGES);
57 }
58 }
59 }
60
61 /**
62 * Filter My Account menu items based on config.
63 *
64 * @param array<string,string> $items Default menu.
65 *
66 * @return array<string,string>
67 */
68 public function filter_menu_items(array $items): array
69 {
70 $endpoints = $this->get_endpoints();
71 $output = [];
72
73 foreach ($endpoints as $slug => $data) {
74 if (empty($data['enabled'])) {
75 continue;
76 }
77
78 if (!$this->is_allowed_for_user($data)) {
79 continue;
80 }
81
82 $label = $data['label'] ?? ($items[$slug] ?? ucfirst(str_replace('-', ' ', $slug)));
83 $position = isset($data['position']) ? (int) $data['position'] : 20;
84
85 $output[$position . ':' . $slug] = [$slug, $label];
86 }
87
88 ksort($output, SORT_NATURAL);
89
90 $sorted = [];
91 foreach ($output as $item) {
92 [$slug, $label] = $item;
93 $sorted[$slug] = $label;
94 }
95
96 return $sorted;
97 }
98
99 /**
100 * Render endpoint template if mapped; otherwise fall back.
101 *
102 * @return void
103 */
104 public function maybe_render_endpoint_template(): void
105 {
106 if (!function_exists('is_account_page') || !is_account_page()) {
107 return;
108 }
109 if (!class_exists(Elementor_Plugin::class)) {
110 return;
111 }
112
113 if (isset($_GET['ka_logout_confirm'])) {
114 $this->render_logout_confirm();
115 return;
116 }
117
118 $endpoints = $this->get_endpoints();
119 $current = $this->detect_current_endpoint(array_keys($endpoints));
120 if (!$current) {
121 return;
122 }
123
124 $config = $endpoints[$current] ?? [];
125 $template_id = isset($config['template_id']) ? (int) $config['template_id'] : 0;
126
127 if (!$this->is_allowed_for_user($config)) {
128 return;
129 }
130
131 if (!$template_id || !king_addons_can_use_pro()) {
132 return;
133 }
134
135 status_header(200);
136 nocache_headers();
137 echo '<div class="king-addons-woo-builder king-addons-woo-builder--my-account king-addons-woo-builder--endpoint-' . esc_attr($current) . '">';
138 echo Elementor_Plugin::$instance->frontend->get_builder_content_for_display($template_id); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
139 echo '</div>';
140 exit;
141 }
142
143 /**
144 * Add confirm flag to logout URL if configured.
145 *
146 * @param string $url URL.
147 * @param string $endpoint Endpoint.
148 * @param string $value Value.
149 * @param string $permalink Permalink.
150 *
151 * @return string
152 */
153 public function filter_logout_url(string $url, string $endpoint, string $value, string $permalink): string
154 {
155 $endpoints = $this->get_endpoints();
156 if ('customer-logout' === $endpoint && !empty($endpoints['customer-logout']['confirm'])) {
157 $url = add_query_arg('ka_logout_confirm', '1', $url);
158 }
159 return $url;
160 }
161
162 /**
163 * Get endpoints config with defaults and filters.
164 *
165 * @return array<string,array<string,mixed>>
166 */
167 public function get_endpoints(): array
168 {
169 if (!empty($this->endpoints)) {
170 return $this->endpoints;
171 }
172
173 $defaults = wc_get_account_menu_items();
174 $config = get_option(self::OPTION_NAME, []);
175
176 $endpoints = [];
177 foreach ($defaults as $slug => $label) {
178 $endpoints[$slug] = [
179 'label' => $label,
180 'enabled' => true,
181 'position' => 20,
182 'template_id' => 0,
183 'is_custom' => false,
184 'confirm' => false,
185 ];
186 }
187
188 // Merge saved config.
189 if (is_array($config)) {
190 foreach ($config as $slug => $data) {
191 $endpoints[$slug] = array_merge($endpoints[$slug] ?? [], $data);
192 }
193 }
194
195 /**
196 * Filter endpoints (add custom, adjust existing).
197 *
198 * @param array<string,array<string,mixed>> $endpoints Endpoints config.
199 */
200 $endpoints = apply_filters('king_addons/my_account/endpoints', $endpoints);
201
202 $this->endpoints = $endpoints;
203 return $this->endpoints;
204 }
205
206 /**
207 * Detect current endpoint slug.
208 *
209 * @param array<int,string> $endpoints Endpoint slugs.
210 *
211 * @return string|null
212 */
213 private function detect_current_endpoint(array $endpoints): ?string
214 {
215 global $wp;
216 foreach ($endpoints as $endpoint) {
217 if (isset($wp->query_vars[$endpoint])) {
218 return $endpoint;
219 }
220 }
221 return null;
222 }
223
224 /**
225 * Register admin settings page.
226 *
227 * @return void
228 */
229 public function register_admin_page(): void
230 {
231 add_submenu_page(
232 'king-addons',
233 esc_html__('My Account Endpoints', 'king-addons'),
234 esc_html__('My Account Endpoints', 'king-addons'),
235 'manage_options',
236 'king-addons-myaccount-endpoints',
237 [$this, 'render_admin_page']
238 );
239 }
240
241 /**
242 * Register settings.
243 *
244 * @return void
245 */
246 public function register_settings(): void
247 {
248 register_setting(
249 'king_addons_myaccount_endpoints',
250 self::OPTION_NAME,
251 [
252 'type' => 'array',
253 'description' => 'King Addons My Account endpoints config',
254 'sanitize_callback' => [$this, 'sanitize_endpoints_option'],
255 'default' => [],
256 ]
257 );
258 }
259
260 /**
261 * Sanitize endpoints option (expects JSON string or array).
262 *
263 * @param mixed $value Raw value.
264 *
265 * @return array<string,mixed>
266 */
267 public function sanitize_endpoints_option($value): array
268 {
269 if (is_string($value)) {
270 $decoded = json_decode($value, true);
271 if (is_array($decoded)) {
272 return $decoded;
273 }
274 return [];
275 }
276 return is_array($value) ? $value : [];
277 }
278
279 /**
280 * Render admin page.
281 *
282 * @return void
283 */
284 public function render_admin_page(): void
285 {
286 if (!current_user_can('manage_options')) {
287 return;
288 }
289 $option = get_option(self::OPTION_NAME, []);
290 $json = wp_json_encode($option, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
291 ?>
292 <div class="wrap">
293 <h1><?php esc_html_e('My Account Endpoints', 'king-addons'); ?></h1>
294 <p><?php esc_html_e('Define endpoints as JSON: slug => {label, enabled, position, template_id, is_custom, confirm, roles:[...]}. Custom endpoints require Pro.', 'king-addons'); ?></p>
295 <form method="post" action="options.php">
296 <?php settings_fields('king_addons_myaccount_endpoints'); ?>
297 <textarea name="<?php echo esc_attr(self::OPTION_NAME); ?>" rows="16" style="width:100%;font-family:monospace;"><?php echo esc_textarea($json ?: ''); ?></textarea>
298 <?php submit_button(); ?>
299 </form>
300 </div>
301 <?php
302 }
303
304 /**
305 * Render logout confirmation page.
306 *
307 * @return void
308 */
309 private function render_logout_confirm(): void
310 {
311 if (!is_user_logged_in()) {
312 return;
313 }
314
315 $logout_url = wp_logout_url(wc_get_page_permalink('myaccount'));
316 $cancel_url = remove_query_arg('ka_logout_confirm');
317
318 status_header(200);
319 nocache_headers();
320
321 echo '<style>';
322 echo '.ka-logout-confirm{min-height:100vh;display:flex;align-items:center;justify-content:center;background:#f8fafc;padding:24px;box-sizing:border-box;}';
323 echo '.ka-logout-confirm__card{max-width:420px;width:100%;background:#fff;border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 10px 30px rgba(15,23,42,0.08);padding:28px;text-align:center;}';
324 echo '.ka-logout-confirm__title{margin:0 0 12px;font-size:24px;font-weight:700;color:#0f172a;}';
325 echo '.ka-logout-confirm__desc{margin:0 0 20px;font-size:15px;color:#334155;}';
326 echo '.ka-logout-confirm__actions{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;}';
327 echo '.ka-logout-confirm__btn{display:inline-flex;align-items:center;justify-content:center;padding:10px 18px;border-radius:8px;border:1px solid transparent;font-weight:600;text-decoration:none;transition:all .15s ease;min-width:120px;}';
328 echo '.ka-logout-confirm__btn--primary{background:#2563eb;border-color:#2563eb;color:#fff;}';
329 echo '.ka-logout-confirm__btn--primary:hover{background:#1d4ed8;border-color:#1d4ed8;}';
330 echo '.ka-logout-confirm__btn--ghost{background:#fff;border-color:#cbd5e1;color:#0f172a;}';
331 echo '.ka-logout-confirm__btn--ghost:hover{border-color:#94a3b8;}';
332 echo '</style>';
333
334 echo '<div class="ka-logout-confirm">';
335 echo '<div class="ka-logout-confirm__card">';
336 echo '<h2 class="ka-logout-confirm__title">' . esc_html__('Confirm logout', 'king-addons') . '</h2>';
337 echo '<p class="ka-logout-confirm__desc">' . esc_html__('Are you sure you want to log out?', 'king-addons') . '</p>';
338 echo '<div class="ka-logout-confirm__actions">';
339 echo '<a class="ka-logout-confirm__btn ka-logout-confirm__btn--primary" href="' . esc_url($logout_url) . '">' . esc_html__('Yes, log out', 'king-addons') . '</a>';
340 echo '<a class="ka-logout-confirm__btn ka-logout-confirm__btn--ghost" href="' . esc_url($cancel_url) . '">' . esc_html__('Cancel', 'king-addons') . '</a>';
341 echo '</div>';
342 echo '</div>';
343 echo '</div>';
344 exit;
345 }
346
347 /**
348 * Check role-based access.
349 *
350 * @param array<string,mixed> $data Endpoint data.
351 *
352 * @return bool
353 */
354 private function is_allowed_for_user(array $data): bool
355 {
356 if (empty($data['roles']) || !is_array($data['roles'])) {
357 return true;
358 }
359 $user = wp_get_current_user();
360 if (!$user || empty($user->roles)) {
361 return false;
362 }
363 return (bool) array_intersect($user->roles, $data['roles']);
364 }
365 }
366
367
368
369
370
371