PluginProbe
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder / 51.1.86
King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder v51.1.86
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 / widgets / Woo_My_Account_Content / Woo_My_Account_Content.php

Woo_My_Account_Content.php in King Addons for Elementor – 100+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce Builder, Mega Menu, Popup Builder 51.1.86, at includes/widgets/Woo_My_Account_Content/Woo_My_Account_Content.php

406 lines 12.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Woo My Account Content widget.
4 *
5 * @package King_Addons
6 */
7
8 namespace King_Addons;
9
10 use Elementor\Controls_Manager;
11 use Elementor\Group_Control_Typography;
12 use King_Addons\Woo_Builder\Context as Woo_Context;
13
14 if (!defined('ABSPATH')) {
15 exit;
16 }
17
18 /**
19 * Renders my account content area.
20 */
21 class Woo_My_Account_Content extends Abstract_My_Account_Widget
22 {
23 /**
24 * Cached custom endpoints config.
25 *
26 * @var array<string,array<string,mixed>>
27 */
28 private static array $custom_endpoints = [];
29
30 /**
31 * Whether hooks were added.
32 *
33 * @var bool
34 */
35
36 /**
37 * Constructor.
38 *
39 * @param array<mixed> $data Data.
40 * @param array<mixed> $args Args.
41 */
42 public function __construct($data = [], $args = null)
43 {
44 parent::__construct($data, $args);
45
46 // No hooks here: the endpoint list is only known once render() runs,
47 // long after init, so registering from the constructor could never see
48 // it. Woo_Builder boots this widget's endpoints from the stored option
49 // instead - see boot().
50 // (nothing to do)
51 }
52
53 public function get_name(): string
54 {
55 return 'woo_my_account_content';
56 }
57
58 public function get_title(): string
59 {
60 return esc_html__('My Account Content', 'king-addons');
61 }
62
63 public function get_icon(): string
64 {
65 return 'king-addons-icon king-addons-woo-my-account-content';
66 }
67
68 public function get_categories(): array
69 {
70 return ['king-addons-woo-builder'];
71 }
72
73 public function get_style_depends(): array
74 {
75 return [KING_ADDONS_ASSETS_UNIQUE_KEY . '-woo-my-account-content-style'];
76 }
77
78 /**
79 * Register controls.
80 *
81 * @return void
82 */
83 protected function register_controls(): void
84 {
85 $this->start_controls_section(
86 'section_content',
87 [
88 'label' => esc_html__('Custom Endpoints (Pro)', 'king-addons'),
89 'tab' => Controls_Manager::TAB_CONTENT,
90 ]
91 );
92
93 $this->add_control(
94 'endpoints',
95 [
96 'label' => esc_html__('Endpoints', 'king-addons'),
97 'type' => Controls_Manager::REPEATER,
98 'fields' => [
99 [
100 'name' => 'slug',
101 'label' => esc_html__('Slug', 'king-addons'),
102 'type' => Controls_Manager::TEXT,
103 'placeholder' => 'my-endpoint',
104 ],
105 [
106 'name' => 'label',
107 'label' => esc_html__('Menu label', 'king-addons'),
108 'type' => Controls_Manager::TEXT,
109 'placeholder' => esc_html__('My Endpoint', 'king-addons'),
110 ],
111 [
112 'name' => 'position',
113 'label' => esc_html__('Menu position', 'king-addons'),
114 'type' => Controls_Manager::NUMBER,
115 'default' => 90,
116 ],
117 [
118 'name' => 'content',
119 'label' => esc_html__('Content', 'king-addons'),
120 'type' => Controls_Manager::WYSIWYG,
121 ],
122 ],
123 'title_field' => '{{ label }} ({{ slug }})',
124 ]
125 );
126
127 $this->end_controls_section();
128
129 $this->start_controls_section(
130 'section_style',
131 [
132 'label' => esc_html__('Style', 'king-addons'),
133 'tab' => Controls_Manager::TAB_STYLE,
134 ]
135 );
136
137 $this->add_group_control(
138 Group_Control_Typography::get_type(),
139 [
140 'name' => 'text_typography',
141 'selector' => '{{WRAPPER}} .ka-woo-my-account-content',
142 ]
143 );
144
145 $this->add_control(
146 'text_color',
147 [
148 'label' => esc_html__('Text Color', 'king-addons'),
149 'type' => Controls_Manager::COLOR,
150 'selectors' => [
151 '{{WRAPPER}} .ka-woo-my-account-content' => 'color: {{VALUE}};',
152 ],
153 ]
154 );
155
156 $this->end_controls_section();
157 }
158
159 protected function render(): void
160 {
161 if (!Woo_Context::maybe_render_context_notice('my_account')) {
162 return;
163 }
164
165 if (!$this->should_render()) {
166 $this->render_missing_account_notice();
167 return;
168 }
169
170 $settings = $this->get_settings_for_display();
171 $can_pro = king_addons_can_use_pro();
172
173 if ($can_pro && !empty($settings['endpoints']) && is_array($settings['endpoints'])) {
174 $collected = [];
175 foreach ($settings['endpoints'] as $ep) {
176 $slug = sanitize_title($ep['slug'] ?? '');
177 if (empty($slug)) {
178 continue;
179 }
180 $collected[$slug] = [
181 'label' => (string) ($ep['label'] ?? $slug),
182 'content' => (string) ($ep['content'] ?? ''),
183 'position' => isset($ep['position']) ? (int) $ep['position'] : 90,
184 ];
185 }
186 self::$custom_endpoints = array_merge(self::$custom_endpoints, $collected);
187 // The rewrite rule for an endpoint has to exist at init, before any
188 // widget renders - store the list so the next request can set it up.
189 self::remember_endpoints(self::$custom_endpoints);
190 }
191
192 if ($this->maybe_render_login_form()) {
193 return;
194 }
195
196 $skip_native = false;
197 if (!Woo_Context::is_editor() && function_exists('is_wc_endpoint_url')) {
198 $dedicated = [
199 'orders' => 'woo_my_account_orders',
200 'view-order' => 'woo_my_account_order_details',
201 'edit-address' => 'woo_my_account_address',
202 'edit-account' => 'woo_my_account_details',
203 'downloads' => 'woo_my_account_downloads',
204 ];
205 foreach ($dedicated as $endpoint => $widget_type) {
206 if (is_wc_endpoint_url($endpoint) && Woo_Context::template_has_widget($widget_type, 'my_account')) {
207 $skip_native = true;
208 break;
209 }
210 }
211 }
212
213 echo '<div class="ka-woo-my-account-content">';
214 woocommerce_output_all_notices();
215 if (!$skip_native) {
216 woocommerce_account_content();
217 }
218 echo '</div>';
219 }
220
221 /**
222 * Option holding the custom endpoints between requests.
223 */
224 private const ENDPOINTS_OPTION = 'king_addons_account_endpoints';
225
226 /**
227 * Option flag asking for a one-off rewrite flush.
228 */
229 private const FLUSH_OPTION = 'king_addons_account_endpoints_flush';
230
231 /**
232 * Persist the endpoint list, and ask for a rewrite flush when it changed.
233 *
234 * @param array<string,array<string,mixed>> $endpoints Endpoint definitions.
235 *
236 * @return void
237 */
238 private static function remember_endpoints(array $endpoints): void
239 {
240 $stored = get_option(self::ENDPOINTS_OPTION, []);
241 if (!is_array($stored)) {
242 $stored = [];
243 }
244
245 if ($stored === $endpoints) {
246 return;
247 }
248
249 update_option(self::ENDPOINTS_OPTION, $endpoints, false);
250
251 // Rewrite rules only change when the set of slugs changes.
252 if (array_keys($stored) !== array_keys($endpoints)) {
253 update_option(self::FLUSH_OPTION, 1, false);
254 }
255 }
256
257 /**
258 * Set up custom endpoints for this request.
259 *
260 * Called on init by Woo_Builder: rewrite endpoints and the account menu
261 * filter both have to be in place before anything renders.
262 *
263 * @return void
264 */
265 public static function boot(): void
266 {
267 $stored = get_option(self::ENDPOINTS_OPTION, []);
268 if (!is_array($stored) || empty($stored)) {
269 return;
270 }
271
272 self::$custom_endpoints = $stored;
273 self::register_endpoints();
274 add_filter('woocommerce_account_menu_items', [self::class, 'filter_menu_items'], 20);
275 add_filter('woocommerce_get_query_vars', [self::class, 'filter_wc_query_vars']);
276
277 if (get_option(self::FLUSH_OPTION)) {
278 delete_option(self::FLUSH_OPTION);
279 flush_rewrite_rules(false);
280 }
281 }
282
283 /**
284 * Whether this request is a King Addons custom My Account endpoint.
285 *
286 * Custom slugs are registered with add_rewrite_endpoint() but are not
287 * WooCommerce query vars unless filter_wc_query_vars() ran, so
288 * is_wc_endpoint_url() can miss them. Dashboard and other widgets use
289 * this to avoid stacking on those URLs.
290 *
291 * @return bool
292 */
293 public static function is_custom_endpoint_request(): bool
294 {
295 if (empty(self::$custom_endpoints)) {
296 $stored = get_option(self::ENDPOINTS_OPTION, []);
297 if (is_array($stored)) {
298 self::$custom_endpoints = $stored;
299 }
300 }
301
302 if (empty(self::$custom_endpoints)) {
303 return false;
304 }
305
306 global $wp;
307 if (!isset($wp->query_vars) || !is_array($wp->query_vars)) {
308 return false;
309 }
310
311 foreach (array_keys(self::$custom_endpoints) as $slug) {
312 if (array_key_exists((string) $slug, $wp->query_vars)) {
313 return true;
314 }
315 }
316
317 return false;
318 }
319
320 /**
321 * Expose custom account endpoints to WooCommerce's query-var map.
322 *
323 * @param array<string,string> $vars WooCommerce endpoint query vars.
324 *
325 * @return array<string,string>
326 */
327 public static function filter_wc_query_vars(array $vars): array
328 {
329 foreach (array_keys(self::$custom_endpoints) as $slug) {
330 $vars[(string) $slug] = (string) $slug;
331 }
332
333 return $vars;
334 }
335
336 /**
337 * Register rewrite endpoints for custom ones.
338 *
339 * @return void
340 */
341 public static function register_endpoints(): void
342 {
343 if (empty(self::$custom_endpoints)) {
344 return;
345 }
346 foreach (array_keys(self::$custom_endpoints) as $slug) {
347 add_rewrite_endpoint($slug, EP_ROOT | EP_PAGES);
348 $hook = 'woocommerce_account_' . $slug . '_endpoint';
349 add_action(
350 $hook,
351 static function () use ($slug): void {
352 $data = self::$custom_endpoints[$slug] ?? [];
353 $content = $data['content'] ?? '';
354 if ($content) {
355 echo '<div class="ka-woo-my-account-content__custom">' . do_shortcode(wp_kses_post($content)) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
356 } else {
357 echo '<div class="ka-woo-my-account-content__custom">' . esc_html__('Content not set.', 'king-addons') . '</div>';
358 }
359 }
360 );
361 }
362 }
363
364 /**
365 * Inject custom endpoints into account menu.
366 *
367 * @param array<string,string> $items Menu items.
368 *
369 * @return array<string,string>
370 */
371 public static function filter_menu_items(array $items): array
372 {
373 if (empty(self::$custom_endpoints)) {
374 return $items;
375 }
376 // Every standard item used to get the same position, so the natural
377 // sort below fell back to comparing slugs and reordered WooCommerce's
378 // menu alphabetically - Log out ended up first. Keep their given order
379 // by spacing them out, leaving room for custom endpoints in between.
380 $ordered = [];
381 $step = 10;
382 foreach ($items as $slug => $label) {
383 $ordered[ sprintf('%05d:%s', $step, $slug) ] = [$slug, $label];
384 $step += 10;
385 }
386 foreach (self::$custom_endpoints as $slug => $data) {
387 $pos = $data['position'] ?? 90;
388 $label = $data['label'] ?? $slug;
389 $ordered[ sprintf('%05d:%s', (int) $pos, $slug) ] = [$slug, $label];
390 }
391 ksort($ordered, SORT_NATURAL);
392 $result = [];
393 foreach ($ordered as $pack) {
394 [$slug, $label] = $pack;
395 $result[$slug] = $label;
396 }
397 return $result;
398 }
399 }
400
401
402
403
404
405
406