PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / trunk
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! vtrunk
3.8.0 3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 All 112 releases
templately / modules / post-import-feedback / Ajax / FeedbackController.php

FeedbackController.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! trunk, at modules/post-import-feedback/Ajax/FeedbackController.php

111 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Templately\Modules\PostImportFeedback\Ajax;
4
5 use Templately\Modules\PostImportFeedback\Widget;
6 use Templately\Utils\Helper;
7 use Templately\Utils\Response\AjaxResponder;
8 use Templately\Utils\Response\ErrorCode;
9 use Templately\Utils\Response\ResponseNormalizer;
10
11 /**
12 * FeedbackController — independent AJAX endpoint handler, owned by
13 * modules/post-import-feedback and supplied to full-site-import through its
14 * `templately_fsi_ajax_handlers` filter seam (module.php::provide_ajax_handlers);
15 * FSI executes it through its shared nonce/capability wrapper (action names
16 * unchanged — see this spec's own Ownership note). Dropped 6 unused imports
17 * (Elementor\Plugin, Exception, and 4 FullSiteImport\Utils\* classes) carried over
18 * from the original monolith file, confirmed dead before removing (grep showed zero
19 * actual usage in the method bodies below).
20 */
21 class FeedbackController {
22
23 public function feedback_form() {
24 // Get data from $_POST
25 $review_description = isset($_POST['review-description']) ? sanitize_textarea_field($_POST['review-description']) : '';
26 $review_email = isset($_POST['review-email']) ? sanitize_email($_POST['review-email']) : '';
27 $rating = isset($_POST['rating']) ? sanitize_text_field($_POST['rating']) : '';
28 $pack_id = get_user_meta(get_current_user_id(), 'templately_fsi_pack_id', true);
29
30 // Prepare the body of the request
31 $body = json_encode([
32 'description' => $review_description,
33 'email' => $review_email,
34 'rating' => (int) $rating,
35 'pack_id' => (int) $pack_id,
36 ]);
37
38 // Send the request to the API
39 $response = Helper::make_api_post_request('v2/feedback/store', json_decode($body, true), [], 30);
40
41 // 043 FR-003 — classification via the central normalizer, replacing
42 // `extract_error_from_response()`'s `mixed` return (which could surface a
43 // raw upstream body as the user's message). Behaviour is kept identical to
44 // the REST twin in REST\Feedback::submit_feedback() — this shim exists only
45 // for already-cached bundles, so the two must not drift.
46 $normalized = ResponseNormalizer::normalize($response);
47
48 if ($normalized->is_error()) {
49 $error = $normalized->error();
50
51 // A repeat submission is not a failure — see the REST twin for the
52 // live-captured `{hasFeedback:true}` 400 this handles.
53 if (ErrorCode::ALREADY_SUBMITTED === $error->code()) {
54 update_user_meta(get_current_user_id(), Widget::LAST_SHOWN_META, time());
55 AjaxResponder::success(__('Your feedback has already been submitted. Thank you!', 'templately'));
56 return;
57 }
58
59 // The whole error, not just its text — the envelope carries the code, so the
60 // client can branch on it instead of reading prose.
61 AjaxResponder::error($error);
62 return;
63 }
64
65 $payload = $normalized->payload();
66 $result = is_array($payload) && isset($payload['message']) ? $payload['message'] : '';
67
68 if ('' === $result) {
69 AjaxResponder::error(ErrorCode::EMPTY_RESPONSE, __('The feedback service returned an unexpected response.', 'templately'));
70 return;
71 }
72
73 // FR-005: a submission starts the 30-day cooldown, same as a skip/close.
74 update_user_meta(get_current_user_id(), Widget::LAST_SHOWN_META, time());
75
76 AjaxResponder::success($result);
77 }
78 public function import_close_feedback_modal() {
79 $return = null;
80
81 // The dismiss reason is raw user input that gets FORWARDED TO THE CLOUD, so
82 // it is sanitized once here rather than read twice unsanitized. The nonce is
83 // verified by FullSiteImport's shared ajax wrapper before this handler runs.
84 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
85 $close_action = isset($_GET['closeAction']) ? sanitize_text_field(wp_unslash($_GET['closeAction'])) : '';
86
87 if ($close_action) {
88 $review_email = isset($_POST['review-email']) ? sanitize_email(wp_unslash($_POST['review-email'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
89 $pack_id = get_user_meta(get_current_user_id(), 'templately_fsi_pack_id', true);
90
91 // Prepare the body of the request
92 $body = json_encode([
93 'action' => $close_action,
94 'email' => $review_email,
95 'pack_id' => (int) $pack_id,
96 ]);
97
98 // Send the request to the API
99 $response = Helper::make_api_post_request('v2/feedback/close', json_decode($body, true), [], 30);
100 $body = wp_remote_retrieve_body($response);
101 $return = json_decode($body, true);
102 }
103 update_user_meta(get_current_user_id(), 'templately_fsi_complete', 'done');
104 // FR-005: record when the widget was dismissed so the 30-day cooldown
105 // (evaluated by Widget::is_eligible()) can expire on a rolling basis
106 // instead of suppressing forever.
107 update_user_meta(get_current_user_id(), Widget::LAST_SHOWN_META, time());
108 AjaxResponder::success($return);
109 }
110 }
111