PluginProbe
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder / 51.1.83
King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder v51.1.83
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 51.1.46 51.1.47 51.1.49 All 37 releases
king-addons / includes / widgets / Form_Builder / helpers / Create_Submission.php

Create_Submission.php in King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder 51.1.83, at includes/widgets/Form_Builder/helpers/Create_Submission.php

306 lines 10.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace King_Addons;
4
5 if (!defined('ABSPATH')) {
6 exit;
7 }
8
9 /**
10 * Stores Form Builder submissions from the public AJAX endpoint.
11 */
12 class Create_Submission
13 {
14 /**
15 * HMAC of the one-time access secret for this submission.
16 */
17 public const META_ACCESS_SECRET = 'king_addons_fb_access_secret';
18
19 /**
20 * Plain secrets issued in this request, keyed by submission ID.
21 *
22 * @var array<int,string>
23 */
24 private static array $issued_secrets = [];
25
26 /**
27 * Registers submission AJAX hooks and admin meta updates.
28 */
29 public function __construct()
30 {
31 add_action('wp_ajax_king_addons_form_builder_submissions', [$this, 'add_to_submissions']);
32 add_action('wp_ajax_nopriv_king_addons_form_builder_submissions', [$this, 'add_to_submissions']);
33 add_action('save_post', [$this, 'update_submissions_post_meta']);
34 }
35
36 /**
37 * Creates a submission post from a public form request.
38 *
39 * Guests are allowed when the nonce is valid and the submitted page ID
40 * belongs to a published Form Builder source page.
41 *
42 * @return void
43 */
44 public function add_to_submissions()
45 {
46
47 $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
48
49 if (!wp_verify_nonce($nonce, 'king-addons-js')) {
50 wp_send_json_error(array(
51 'message' => esc_html__('Security check failed.', 'king-addons'),
52 ));
53 }
54
55 Form_Builder_Security::guard_spam();
56
57 $sanitized_form_page_id = absint($_POST['form_page_id'] ?? 0);
58 if (!Form_Builder_Security::is_valid_submission_page($sanitized_form_page_id)) {
59 wp_send_json_error(array(
60 'message' => esc_html__('Insufficient permissions.', 'king-addons'),
61 ));
62 }
63
64 $sanitized_form_name = sanitize_text_field(wp_unslash($_POST['form_name'] ?? ''));
65 $form_content = isset($_POST['form_content']) && is_array($_POST['form_content']) ? wp_unslash($_POST['form_content']) : [];
66 $sanitized_form_id = sanitize_key(wp_unslash($_POST['form_id'] ?? ''));
67 $sanitized_form_page = sanitize_text_field(wp_unslash($_POST['form_page'] ?? ''));
68
69 $post_id = self::insert_submission([
70 'form_name' => $sanitized_form_name,
71 'form_id' => $sanitized_form_id,
72 'form_page' => $sanitized_form_page,
73 'form_page_id' => $sanitized_form_page_id,
74 'form_content' => $form_content,
75 ]);
76
77 if ($post_id) {
78 wp_send_json_success(array(
79 'action' => 'king_addons_form_builder_submissions',
80 'post_id' => $post_id,
81 'access_secret' => self::issued_access_secret($post_id),
82 'message' => esc_html__('Submission created successfully', 'king-addons'),
83 'status' => 'success'
84 ));
85 } else {
86 wp_send_json_success(array(
87 'action' => 'king_addons_form_builder_submissions',
88 'post_id' => $post_id,
89 'message' => esc_html__('Submit action failed', 'king-addons'),
90 'status' => 'error'
91 ));
92 }
93 }
94
95 /**
96 * Insert a Form Builder submission from already-sanitized request pieces.
97 *
98 * @param array<string,mixed> $args {
99 * @type string $form_name
100 * @type string $form_id
101 * @type string $form_page
102 * @type int $form_page_id
103 * @type array $form_content
104 * }
105 *
106 * @return int Submission post ID, or 0.
107 */
108 public static function insert_submission(array $args): int
109 {
110 $form_name = sanitize_text_field((string) ($args['form_name'] ?? ''));
111 $form_id = sanitize_key((string) ($args['form_id'] ?? ''));
112 $form_page = sanitize_text_field((string) ($args['form_page'] ?? ''));
113 $form_page_id = absint($args['form_page_id'] ?? 0);
114 $form_content = isset($args['form_content']) && is_array($args['form_content']) ? $args['form_content'] : [];
115
116 $post_id = wp_insert_post([
117 'post_status' => 'publish',
118 'post_type' => 'king-addons-fb-sub',
119 'post_title' => $form_name
120 ? $form_name . ' - ' . current_time('mysql')
121 : current_time('mysql'),
122 ]);
123
124 if (!$post_id || is_wp_error($post_id)) {
125 return 0;
126 }
127
128 $post_id = (int) $post_id;
129
130 foreach ($form_content as $key => $value) {
131 if (!is_array($value) || count($value) < 3) {
132 continue;
133 }
134
135 $sanitized_key = sanitize_key((string) $key);
136 if ('' === $sanitized_key) {
137 continue;
138 }
139
140 update_post_meta($post_id, $sanitized_key, [
141 sanitize_text_field((string) $value[0]),
142 self::sanitize_field_value($value[1]),
143 sanitize_text_field((string) $value[2]),
144 ]);
145 }
146
147 update_post_meta($post_id, 'king_addons_form_name', $form_name);
148 update_post_meta($post_id, 'king_addons_form_id', $form_id);
149 update_post_meta($post_id, 'king_addons_form_page', $form_page);
150 update_post_meta($post_id, 'king_addons_form_page_id', $form_page_id);
151 $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_textarea_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
152 update_post_meta($post_id, 'king_addons_user_agent', $user_agent);
153 update_post_meta($post_id, 'king_addons_user_ip', Core::getClientIP());
154 self::issue_access_secret($post_id);
155
156 return $post_id;
157 }
158
159 /**
160 * Create a secret that later public requests must present to touch this submission.
161 *
162 * Only the HMAC is stored. The plaintext is kept for this request so the
163 * creator can send it with the payment call.
164 *
165 * @param int $submission_id Submission post ID.
166 * @return string Plaintext secret.
167 */
168 public static function issue_access_secret(int $submission_id): string
169 {
170 if ($submission_id < 1) {
171 return '';
172 }
173
174 try {
175 $secret = bin2hex(random_bytes(32));
176 } catch (\Exception $e) {
177 $secret = wp_generate_password(64, false, false);
178 }
179
180 update_post_meta($submission_id, self::META_ACCESS_SECRET, hash_hmac('sha256', $secret, self::access_secret_key()));
181 self::$issued_secrets[$submission_id] = $secret;
182
183 return $secret;
184 }
185
186 /**
187 * Plaintext secret issued for this submission in the current request.
188 *
189 * @param int $submission_id Submission post ID.
190 * @return string
191 */
192 public static function issued_access_secret(int $submission_id): string
193 {
194 return self::$issued_secrets[$submission_id] ?? '';
195 }
196
197 /**
198 * Whether the posted secret matches the one stored for this submission.
199 *
200 * @param int $submission_id Submission post ID.
201 * @param string $secret Plaintext from the request.
202 * @return bool
203 */
204 public static function verify_access_secret(int $submission_id, string $secret): bool
205 {
206 if ($submission_id < 1 || '' === $secret) {
207 return false;
208 }
209
210 $stored = (string) get_post_meta($submission_id, self::META_ACCESS_SECRET, true);
211 if ('' === $stored) {
212 return false;
213 }
214
215 return hash_equals($stored, hash_hmac('sha256', $secret, self::access_secret_key()));
216 }
217
218 /**
219 * Key used to HMAC submission access secrets.
220 *
221 * @return string
222 */
223 private static function access_secret_key(): string
224 {
225 return 'king-addons-fb-sub|' . wp_salt('auth');
226 }
227
228 /**
229 * Saves admin edits to an existing submission.
230 *
231 * @param int $post_id Submission post ID.
232 * @return void
233 */
234 public function update_submissions_post_meta($post_id)
235 {
236 // Security fix: Validate nonce and capabilities
237 if (!current_user_can('edit_post', $post_id)) {
238 return;
239 }
240
241 if (isset($_POST['king_addons_submission_changes']) && !empty($_POST['king_addons_submission_changes'])) {
242 // Security fix: Sanitize JSON input and validate structure
243 $raw_changes = sanitize_textarea_field(stripslashes($_POST['king_addons_submission_changes']));
244 $changes = json_decode($raw_changes, true);
245
246 if (!is_array($changes)) {
247 return; // Invalid JSON structure
248 }
249
250 foreach ($changes as $key => $value) {
251 // Security fix: Validate and sanitize keys and values
252 $sanitized_key = sanitize_key($key);
253 if (empty($sanitized_key)) {
254 continue; // Skip invalid keys
255 }
256
257 // Sanitize values based on type
258 if (is_array($value)) {
259 $sanitized_value = array_map('sanitize_text_field', $value);
260 } else {
261 $sanitized_value = sanitize_text_field($value);
262 }
263
264 update_post_meta($post_id, $sanitized_key, $sanitized_value);
265 }
266 }
267 }
268
269 /**
270 * Sanitize a submitted field value.
271 *
272 * Radio and checkbox groups arrive as rows of [value, checked, name, id].
273 * A flat array_map(sanitize_text_field) turns each row into an empty
274 * string, so the saved submission lost what the visitor actually picked.
275 *
276 * @param mixed $raw Raw value from form_content.
277 *
278 * @return string|array<int,string>
279 */
280 private static function sanitize_field_value($raw)
281 {
282 if (!is_array($raw)) {
283 return sanitize_text_field((string) $raw);
284 }
285
286 if (isset($raw[0]) && is_array($raw[0])) {
287 $picked = [];
288 foreach ($raw as $row) {
289 if (!is_array($row)) {
290 continue;
291 }
292 $option = sanitize_text_field((string) ($row[0] ?? ''));
293 $checked = !empty($row[1]) && 'false' !== (string) $row[1] && '0' !== (string) $row[1];
294 if ($checked && '' !== $option) {
295 $picked[] = $option;
296 }
297 }
298
299 return implode(', ', $picked);
300 }
301
302 return array_values(array_map('sanitize_text_field', $raw));
303 }
304 }
305
306 new Create_Submission();