PluginProbe ʕ •ᴥ•ʔ
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.3
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.3
2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8 0.0.9 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.1.0 1.1.1 1.1.2 1.10.0 1.10.1 1.11.0 1.12.0 1.12.1 1.12.2 1.12.3 1.13.0 1.13.1 1.13.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.8.0 1.9.0 1.9.1 2.0.0 2.0.1 2.0.2 2.1.0 2.1.1 2.2.0 2.2.1 2.2.2 2.3.0 2.4.0 2.5.0 2.5.2 2.6.0
sureforms / inc / abilities / forms / update-form.php
sureforms / inc / abilities / forms Last commit date
create-form.php 1 month ago delete-form.php 5 months ago duplicate-form.php 5 months ago form-field-schema.php 2 months ago form-metadata.php 1 month ago get-form-stats.php 5 months ago get-form.php 5 months ago get-shortcode.php 5 months ago list-forms.php 2 months ago update-form.php 1 month ago
update-form.php
427 lines
1 <?php
2 /**
3 * Update Form Ability.
4 *
5 * @package sureforms
6 * @since 2.5.2
7 */
8
9 namespace SRFM\Inc\Abilities\Forms;
10
11 use SRFM\Inc\Abilities\Abstract_Ability;
12 use SRFM\Inc\AI_Form_Builder\Field_Mapping;
13 use SRFM\Inc\Create_New_Form;
14 use SRFM\Inc\Helper;
15 use WP_REST_Request;
16
17 if ( ! defined( 'ABSPATH' ) ) {
18 exit; // Exit if accessed directly.
19 }
20
21 /**
22 * Update_Form ability class.
23 *
24 * Updates an existing SureForms form's title, status, fields, and/or metadata.
25 *
26 * @since 2.5.2
27 */
28 class Update_Form extends Abstract_Ability {
29 use Form_Field_Schema;
30 use Form_Metadata;
31
32 /**
33 * Constructor.
34 *
35 * @since 2.5.2
36 */
37 public function __construct() {
38 $this->id = 'sureforms/update-form';
39 $this->label = __( 'Update SureForms Form', 'sureforms' );
40 $this->description = __( 'Update an existing SureForms form title, status (publish/draft/private/trash), fields, and/or metadata settings. Use status "trash" to trash a form, or change from "trash" to another status to restore it. Providing formFields replaces all existing fields.', 'sureforms' );
41 $this->capability = 'manage_options';
42 $this->gated = 'srfm_abilities_api_edit';
43 }
44
45 /**
46 * {@inheritDoc}
47 *
48 * @since 2.5.2
49 */
50 public function get_annotations() {
51 return [
52 'readonly' => false,
53 'destructive' => true,
54 'idempotent' => true,
55 'priority' => 2.0,
56 'openWorldHint' => false,
57 'instructions' => 'Confirm the changes with the user before executing. Setting status to trash moves the form out of production. Providing formFields replaces ALL existing fields.',
58 ];
59 }
60
61 /**
62 * {@inheritDoc}
63 *
64 * @since 2.5.2
65 */
66 public function get_input_schema() {
67 $field_properties = $this->get_form_field_schema();
68
69 return [
70 'type' => 'object',
71 'additionalProperties' => false,
72 'properties' => [
73 'form_id' => [
74 'type' => 'integer',
75 'description' => __( 'The ID of the form to update.', 'sureforms' ),
76 ],
77 'title' => [
78 'type' => 'string',
79 'description' => __( 'New title for the form.', 'sureforms' ),
80 ],
81 'status' => [
82 'type' => 'string',
83 'description' => __( 'New status for the form.', 'sureforms' ),
84 'enum' => [ 'publish', 'draft', 'private', 'trash' ],
85 ],
86 'formFields' => [
87 'type' => 'array',
88 'description' => __( 'Array of form field definitions. Providing this replaces all existing fields.', 'sureforms' ),
89 'items' => [
90 'type' => 'object',
91 'properties' => $field_properties,
92 'required' => [ 'label', 'fieldType' ],
93 ],
94 ],
95 'formMetaData' => [
96 'type' => 'object',
97 'description' => __( 'Optional form metadata including confirmation, compliance, and styling settings. Same schema as create-form.', 'sureforms' ),
98 'properties' => [
99 'formConfirmation' => [
100 'type' => 'object',
101 'properties' => [
102 'confirmationMessage' => [
103 'type' => 'string',
104 'description' => __( 'Message displayed after successful submission.', 'sureforms' ),
105 ],
106 ],
107 ],
108 'compliance' => [
109 'type' => 'object',
110 'properties' => [
111 'enableCompliance' => [ 'type' => 'boolean' ],
112 'neverStoreEntries' => [ 'type' => 'boolean' ],
113 'autoDeleteEntries' => [ 'type' => 'boolean' ],
114 'autoDeleteEntriesDays' => [ 'type' => 'string' ],
115 ],
116 ],
117 'instantForm' => [
118 'type' => 'object',
119 'properties' => [
120 'instantForm' => [ 'type' => 'boolean' ],
121 'showTitle' => [ 'type' => 'boolean' ],
122 'formBackgroundColor' => [ 'type' => 'string' ],
123 'formWidth' => [ 'type' => 'integer' ],
124 ],
125 ],
126 'general' => [
127 'type' => 'object',
128 'properties' => [
129 'useLabelAsPlaceholder' => [ 'type' => 'boolean' ],
130 'submitText' => [ 'type' => 'string' ],
131 ],
132 ],
133 'styling' => [
134 'type' => 'object',
135 'properties' => [
136 'submitAlignment' => [ 'type' => 'string' ],
137 ],
138 ],
139 'formStyling' => [
140 'type' => 'object',
141 'description' => __( 'Form styling settings including colors and spacing.', 'sureforms' ),
142 'properties' => [
143 'primaryColor' => [
144 'type' => 'string',
145 'description' => __( 'Primary/accent color as hex (e.g. #111C44).', 'sureforms' ),
146 ],
147 'textColor' => [
148 'type' => 'string',
149 'description' => __( 'Text color as hex (e.g. #1E1E1E).', 'sureforms' ),
150 ],
151 'textColorOnPrimary' => [
152 'type' => 'string',
153 'description' => __( 'Text color on primary backgrounds as hex (e.g. #FFFFFF).', 'sureforms' ),
154 ],
155 'fieldSpacing' => [
156 'type' => 'string',
157 'description' => __( 'Spacing between fields.', 'sureforms' ),
158 'enum' => [ 'small', 'medium', 'large' ],
159 ],
160 'disableDefaultStyles' => [
161 'type' => 'boolean',
162 'description' => __( 'When true, SureForms does not enqueue its default frontend styles for this form, so the active theme styling applies instead.', 'sureforms' ),
163 ],
164 ],
165 ],
166 'emailNotification' => [
167 'type' => 'object',
168 'description' => __( 'Email notification settings for the first notification. Merges with existing.', 'sureforms' ),
169 'properties' => [
170 'status' => [
171 'type' => 'boolean',
172 'description' => __( 'Enable or disable the notification.', 'sureforms' ),
173 ],
174 'name' => [
175 'type' => 'string',
176 'description' => __( 'Notification name.', 'sureforms' ),
177 ],
178 'emailTo' => [
179 'type' => 'string',
180 'description' => __( 'Recipient email. Supports smart tags like {admin_email}.', 'sureforms' ),
181 ],
182 'emailReplyTo' => [ 'type' => 'string' ],
183 'fromName' => [
184 'type' => 'string',
185 'description' => __( 'Sender name. Supports smart tags like {site_title}.', 'sureforms' ),
186 ],
187 'fromEmail' => [ 'type' => 'string' ],
188 'emailCc' => [ 'type' => 'string' ],
189 'emailBcc' => [ 'type' => 'string' ],
190 'subject' => [
191 'type' => 'string',
192 'description' => __( 'Email subject. Supports smart tags like {form_title}.', 'sureforms' ),
193 ],
194 'emailBody' => [
195 'type' => 'string',
196 'description' => __( 'Email body. Supports smart tags like {all_data}.', 'sureforms' ),
197 ],
198 ],
199 ],
200 'formRestriction' => [
201 'type' => 'object',
202 'description' => __( 'Form submission restrictions and scheduling.', 'sureforms' ),
203 'properties' => [
204 'status' => [
205 'type' => 'boolean',
206 'description' => __( 'Enable entry limit restriction.', 'sureforms' ),
207 ],
208 'maxEntries' => [
209 'type' => 'integer',
210 'description' => __( 'Maximum number of entries allowed (0 = unlimited).', 'sureforms' ),
211 ],
212 'date' => [
213 'type' => 'string',
214 'description' => __( 'Expiry date (YYYY-MM-DD).', 'sureforms' ),
215 ],
216 'hours' => [ 'type' => 'string' ],
217 'minutes' => [ 'type' => 'string' ],
218 'meridiem' => [
219 'type' => 'string',
220 'enum' => [ 'AM', 'PM' ],
221 ],
222 'message' => [
223 'type' => 'string',
224 'description' => __( 'Message shown when form is closed.', 'sureforms' ),
225 ],
226 'schedulingStatus' => [
227 'type' => 'boolean',
228 'description' => __( 'Enable form scheduling.', 'sureforms' ),
229 ],
230 'startDate' => [
231 'type' => 'string',
232 'description' => __( 'Schedule start date (YYYY-MM-DD).', 'sureforms' ),
233 ],
234 'startHours' => [ 'type' => 'string' ],
235 'startMinutes' => [ 'type' => 'string' ],
236 'startMeridiem' => [
237 'type' => 'string',
238 'enum' => [ 'AM', 'PM' ],
239 ],
240 'schedulingNotStartedMessage' => [ 'type' => 'string' ],
241 'schedulingEndedMessage' => [ 'type' => 'string' ],
242 ],
243 ],
244 'formCustomCss' => [
245 'type' => 'string',
246 'description' => __( 'Custom CSS for the form.', 'sureforms' ),
247 ],
248 ],
249 ],
250 ],
251 'required' => [ 'form_id' ],
252 ];
253 }
254
255 /**
256 * {@inheritDoc}
257 *
258 * @since 2.5.2
259 */
260 public function get_output_schema() {
261 return [
262 'type' => 'object',
263 'properties' => [
264 'form_id' => [ 'type' => 'integer' ],
265 'title' => [ 'type' => 'string' ],
266 'status' => [ 'type' => 'string' ],
267 'previous_status' => [ 'type' => 'string' ],
268 'edit_url' => [ 'type' => 'string' ],
269 'updated_fields' => [
270 'type' => 'array',
271 'items' => [ 'type' => 'string' ],
272 ],
273 ],
274 ];
275 }
276
277 /**
278 * Execute the update-form ability.
279 *
280 * @param array<string,mixed> $input Validated input data.
281 * @since 2.5.2
282 * @return array<string,mixed>|\WP_Error
283 */
284 public function execute( $input ) {
285 $form_id = Helper::get_integer_value( $input['form_id'] ?? 0 );
286 $post = get_post( $form_id );
287
288 if ( ! $post || SRFM_FORMS_POST_TYPE !== $post->post_type ) {
289 return new \WP_Error(
290 'srfm_form_not_found',
291 __( 'Form not found.', 'sureforms' ),
292 [ 'status' => 404 ]
293 );
294 }
295
296 $previous_status = $post->post_status;
297 $updated_fields = [];
298
299 // Handle status changes.
300 if ( ! empty( $input['status'] ) ) {
301 $new_status = sanitize_text_field( Helper::get_string_value( $input['status'] ) );
302 $allowed_status = [ 'publish', 'draft', 'private', 'trash' ];
303
304 if ( in_array( $new_status, $allowed_status, true ) ) {
305 if ( 'trash' === $new_status && 'trash' !== $previous_status ) {
306 wp_trash_post( $form_id );
307 $updated_fields[] = 'status';
308 } elseif ( 'trash' !== $new_status && 'trash' === $previous_status ) {
309 wp_untrash_post( $form_id );
310 // After untrash, set the desired status.
311 wp_update_post(
312 [
313 'ID' => $form_id,
314 'post_status' => $new_status,
315 ]
316 );
317 $updated_fields[] = 'status';
318 } elseif ( $new_status !== $previous_status ) {
319 wp_update_post(
320 [
321 'ID' => $form_id,
322 'post_status' => $new_status,
323 ]
324 );
325 $updated_fields[] = 'status';
326 }
327 }
328 }
329
330 // Handle title changes.
331 if ( ! empty( $input['title'] ) ) {
332 $new_title = sanitize_text_field( Helper::get_string_value( $input['title'] ) );
333
334 if ( $new_title !== $post->post_title ) {
335 wp_update_post(
336 [
337 'ID' => $form_id,
338 'post_title' => $new_title,
339 ]
340 );
341 $updated_fields[] = 'title';
342 }
343 }
344
345 // Handle metadata changes.
346 if ( ! empty( $input['formMetaData'] ) && is_array( $input['formMetaData'] ) ) {
347 $current_metas = [];
348 $meta_keys = Create_New_Form::get_default_meta_keys();
349
350 foreach ( array_keys( $meta_keys ) as $meta_key ) {
351 $current_metas[ $meta_key ] = get_post_meta( $form_id, $meta_key, true );
352 }
353
354 // Load serialized object metas needed for metadata overrides.
355 $instant_raw = get_post_meta( $form_id, '_srfm_instant_form_settings', true );
356 $confirmation_raw = get_post_meta( $form_id, '_srfm_form_confirmation', true );
357 $compliance_raw = get_post_meta( $form_id, '_srfm_compliance', true );
358
359 $current_metas['_srfm_instant_form_settings'] = Helper::get_array_value( is_array( $instant_raw ) ? $instant_raw : [] );
360 $current_metas['_srfm_form_confirmation'] = Helper::get_array_value( is_array( $confirmation_raw ) ? $confirmation_raw : [] );
361 $current_metas['_srfm_compliance'] = Helper::get_array_value( is_array( $compliance_raw ) ? $compliance_raw : [] );
362
363 // Load additional serialized metas.
364 $styling_raw = get_post_meta( $form_id, '_srfm_forms_styling', true );
365 $notif_raw = get_post_meta( $form_id, '_srfm_email_notification', true );
366
367 $current_metas['_srfm_forms_styling'] = Helper::get_array_value( is_array( $styling_raw ) ? $styling_raw : [] );
368 $current_metas['_srfm_email_notification'] = Helper::get_array_value( is_array( $notif_raw ) ? $notif_raw : [] );
369 $current_metas['_srfm_form_restriction'] = Helper::get_string_value( get_post_meta( $form_id, '_srfm_form_restriction', true ) );
370 $current_metas['_srfm_form_custom_css'] = Helper::get_string_value( get_post_meta( $form_id, '_srfm_form_custom_css', true ) );
371
372 $updated_metas = $this->apply_metadata_overrides( $current_metas, $input['formMetaData'] );
373
374 foreach ( $updated_metas as $meta_key => $meta_value ) {
375 if ( isset( $current_metas[ $meta_key ] ) && $current_metas[ $meta_key ] === $meta_value ) {
376 continue;
377 }
378 update_post_meta( $form_id, $meta_key, $meta_value );
379 }
380
381 $updated_fields[] = 'metadata';
382 }
383
384 // Handle field changes.
385 if ( ! empty( $input['formFields'] ) && is_array( $input['formFields'] ) ) {
386 // Sanitize form fields before passing to Field_Mapping.
387 $form_fields = $this->sanitize_form_fields( $input['formFields'] );
388
389 $request = new WP_REST_Request( 'POST' );
390 $request->set_param(
391 'form_data',
392 [
393 'form' => [ 'formFields' => $form_fields ],
394 ]
395 );
396
397 $post_content = Field_Mapping::generate_gutenberg_fields_from_questions( $request );
398
399 if ( is_wp_error( $post_content ) ) {
400 return $post_content;
401 }
402
403 if ( ! empty( $post_content ) ) {
404 wp_update_post(
405 [
406 'ID' => $form_id,
407 'post_content' => $post_content,
408 ]
409 );
410 $updated_fields[] = 'fields';
411 }
412 }
413
414 // Re-fetch post to get the current state.
415 $updated_post = get_post( $form_id );
416
417 return [
418 'form_id' => $form_id,
419 'title' => $updated_post ? $updated_post->post_title : $post->post_title,
420 'status' => $updated_post ? $updated_post->post_status : $post->post_status,
421 'previous_status' => $previous_status,
422 'edit_url' => admin_url( 'post.php?post=' . $form_id . '&action=edit' ),
423 'updated_fields' => $updated_fields,
424 ];
425 }
426 }
427