PluginProbe
Extendify / 3.1.5
Extendify v3.1.5
3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 0.7.0 All 126 releases
extendify / app / QuickEdit / Controllers / WPFormsController.php

WPFormsController.php in Extendify 3.1.5, at app/QuickEdit/Controllers/WPFormsController.php

168 lines 5.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Extendify\QuickEdit\Controllers;
4
5 defined('ABSPATH') || die('No direct access.');
6
7 use Extendify\Config;
8
9 // WPForms stores forms as a wpforms CPT with JSON-encoded post_content.
10 // Save is shallow-merged so we don't drop WPForms-internal keys (choices,
11 // conditional logic, validation) we don't surface.
12 class WPFormsController
13 {
14 public static function init()
15 {
16 add_action('rest_api_init', [self::class, 'registerRoutes']);
17 }
18
19 public static function registerRoutes()
20 {
21 register_rest_route('extendify/v1', '/quick-edit/wpforms', [
22 [
23 'methods' => 'GET',
24 'permission_callback' => [self::class, 'permissionCallback'],
25 'callback' => [self::class, 'handleGet'],
26 ],
27 [
28 'methods' => 'POST',
29 'permission_callback' => [self::class, 'permissionCallback'],
30 'callback' => [self::class, 'handlePost'],
31 ],
32 ]);
33 }
34
35 public static function permissionCallback(): bool
36 {
37 return current_user_can(Config::$requiredCapability);
38 }
39
40 public static function handleGet(\WP_REST_Request $req)
41 {
42 $formId = (int) $req->get_param('form_id');
43 $fieldId = (int) $req->get_param('field_id');
44 if ($formId <= 0 || $fieldId <= 0) {
45 return new \WP_REST_Response(['error' => 'form_id + field_id required'], 400);
46 }
47
48 $form = self::loadForm($formId);
49 if (is_wp_error($form)) {
50 return new \WP_REST_Response(['error' => $form->get_error_message()], 404);
51 }
52
53 $fields = is_array($form['fields'] ?? null) ? $form['fields'] : [];
54 $field = $fields[$fieldId] ?? null;
55 if (!$field) {
56 return new \WP_REST_Response(['error' => 'field not found'], 404);
57 }
58
59 return new \WP_REST_Response([
60 'id' => (int) ($field['id'] ?? $fieldId),
61 'type' => (string) ($field['type'] ?? ''),
62 'label' => (string) ($field['label'] ?? ''),
63 'placeholder' => (string) ($field['placeholder'] ?? ''),
64 'required' => self::truthy($field['required'] ?? false),
65 'description' => (string) ($field['description'] ?? ''),
66 ]);
67 }
68
69 public static function handlePost(\WP_REST_Request $req)
70 {
71 $body = $req->get_json_params() ?: [];
72 $formId = (int) ($body['form_id'] ?? 0);
73 $fieldId = (int) ($body['field_id'] ?? 0);
74 $changes = is_array($body['changes'] ?? null) ? $body['changes'] : [];
75 if ($formId <= 0 || $fieldId <= 0 || !$changes) {
76 return new \WP_REST_Response(
77 ['error' => 'form_id, field_id, changes required'],
78 400
79 );
80 }
81
82 $form = self::loadForm($formId);
83 if (is_wp_error($form)) {
84 return new \WP_REST_Response(['error' => $form->get_error_message()], 404);
85 }
86
87 $fields = is_array($form['fields'] ?? null) ? $form['fields'] : [];
88 if (!isset($fields[$fieldId])) {
89 return new \WP_REST_Response(['error' => 'field not found'], 404);
90 }
91
92 $field = $fields[$fieldId];
93
94 // Whitelisted fields only; anything else in `changes` is dropped silently.
95 if (array_key_exists('label', $changes)) {
96 $field['label'] = sanitize_text_field((string) $changes['label']);
97 }
98 if (array_key_exists('placeholder', $changes)) {
99 $field['placeholder'] = sanitize_text_field((string) $changes['placeholder']);
100 }
101 if (array_key_exists('description', $changes)) {
102 $field['description'] = wp_kses_post((string) $changes['description']);
103 }
104 if (array_key_exists('required', $changes)) {
105 // WPForms stores `required` as "1"/"" historically and as bool
106 // in newer versions; preserve the existing shape.
107 $existing = $field['required'] ?? null;
108 $next = self::truthy($changes['required']);
109 if (is_bool($existing)) {
110 $field['required'] = $next;
111 } else {
112 $field['required'] = $next ? '1' : '';
113 }
114 }
115
116 $fields[$fieldId] = $field;
117 $form['fields'] = $fields;
118
119 // wpforms wp_unslashes on load; wp_update_post expects slashed input.
120 $newContent = wp_json_encode($form);
121 if ($newContent === false) {
122 return new \WP_REST_Response(['error' => 'failed to encode form'], 500);
123 }
124
125 $update = wp_update_post([
126 'ID' => $formId,
127 'post_content' => wp_slash($newContent),
128 ], true);
129 if (is_wp_error($update)) {
130 return new \WP_REST_Response(['error' => $update->get_error_message()], 500);
131 }
132
133 return new \WP_REST_Response(['ok' => true]);
134 }
135
136 /**
137 * @return array|\WP_Error
138 */
139 private static function loadForm(int $formId)
140 {
141 $post = get_post($formId);
142 if (!$post || $post->post_type !== 'wpforms') {
143 return new \WP_Error('not_found', 'wpforms form not found');
144 }
145 $raw = (string) $post->post_content;
146 $decoded = json_decode($raw, true);
147 if (!is_array($decoded)) {
148 return new \WP_Error('bad_form', 'wpforms form content is not valid JSON');
149 }
150 return $decoded;
151 }
152
153 private static function truthy($v): bool
154 {
155 if (is_bool($v)) {
156 return $v;
157 }
158 if (is_int($v) || is_float($v)) {
159 return $v != 0;
160 }
161 if (is_string($v)) {
162 $v = strtolower(trim($v));
163 return $v === '1' || $v === 'true' || $v === 'yes' || $v === 'on';
164 }
165 return (bool) $v;
166 }
167 }
168