PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.8.2
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.8.2
2.12.6 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 All 96 releases
sureforms / inc / abilities / forms / create-form.php
create-form.php
390 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 * Create 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 * Create_Form ability class.
23 *
24 * Creates a new SureForms form using the existing Field_Mapping engine.
25 *
26 * @since 2.5.2
27 */
28 class Create_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/create-form';
39 $this->label = __( 'Create SureForms Form', 'sureforms' );
40 $description = __( 'Create a new SureForms form with specified title, fields, metadata, and status. Supports all standard field types (input, email, textarea, dropdown, checkbox, multi-choice, phone, number, url, address, gdpr, payment, inline-button).', 'sureforms' );
41
42 /**
43 * Filter the description for the create-form ability.
44 *
45 * Pro and third-party plugins can append their supported field types.
46 *
47 * @param string $description The ability description.
48 * @since 2.5.2
49 */
50 $this->description = apply_filters( 'srfm_ability_create_form_description', $description );
51 $this->capability = 'manage_options';
52 $this->gated = 'srfm_abilities_api_edit';
53 }
54
55 /**
56 * {@inheritDoc}
57 */
58 public function get_annotations() {
59 return [
60 'readonly' => false,
61 'destructive' => false,
62 'idempotent' => false,
63 'priority' => 2.0,
64 'openWorldHint' => false,
65 'instructions' => 'Confirm the form title, field list, and publish status with the user before creating.',
66 ];
67 }
68
69 /**
70 * {@inheritDoc}
71 */
72 public function get_input_schema() {
73 $field_properties = $this->get_form_field_schema();
74
75 return [
76 'type' => 'object',
77 'additionalProperties' => false,
78 'properties' => [
79 'formTitle' => [
80 'type' => 'string',
81 'description' => __( 'Title of the form in 5-10 words.', 'sureforms' ),
82 ],
83 'formFields' => [
84 'type' => 'array',
85 'description' => __( 'Array of form field definitions.', 'sureforms' ),
86 'items' => [
87 'type' => 'object',
88 'properties' => $field_properties,
89 'required' => [ 'label', 'fieldType' ],
90 ],
91 ],
92 'formMetaData' => [
93 'type' => 'object',
94 'description' => __( 'Optional form metadata including confirmation, compliance, and styling settings.', 'sureforms' ),
95 'properties' => [
96 'formConfirmation' => [
97 'type' => 'object',
98 'properties' => [
99 'confirmationMessage' => [
100 'type' => 'string',
101 'description' => __( 'Message displayed after successful submission.', 'sureforms' ),
102 ],
103 ],
104 ],
105 'compliance' => [
106 'type' => 'object',
107 'properties' => [
108 'enableCompliance' => [ 'type' => 'boolean' ],
109 'neverStoreEntries' => [ 'type' => 'boolean' ],
110 'autoDeleteEntries' => [ 'type' => 'boolean' ],
111 'autoDeleteEntriesDays' => [ 'type' => 'string' ],
112 ],
113 ],
114 'instantForm' => [
115 'type' => 'object',
116 'properties' => [
117 'instantForm' => [ 'type' => 'boolean' ],
118 'showTitle' => [ 'type' => 'boolean' ],
119 'formBackgroundColor' => [ 'type' => 'string' ],
120 'formWidth' => [ 'type' => 'integer' ],
121 ],
122 ],
123 'general' => [
124 'type' => 'object',
125 'properties' => [
126 'useLabelAsPlaceholder' => [ 'type' => 'boolean' ],
127 'submitText' => [ 'type' => 'string' ],
128 ],
129 ],
130 'styling' => [
131 'type' => 'object',
132 'properties' => [
133 'submitAlignment' => [ 'type' => 'string' ],
134 ],
135 ],
136 'formStyling' => [
137 'type' => 'object',
138 'description' => __( 'Form styling settings including colors and spacing.', 'sureforms' ),
139 'properties' => [
140 'primaryColor' => [
141 'type' => 'string',
142 'description' => __( 'Primary/accent color as hex (e.g. #111C44).', 'sureforms' ),
143 ],
144 'textColor' => [
145 'type' => 'string',
146 'description' => __( 'Text color as hex (e.g. #1E1E1E).', 'sureforms' ),
147 ],
148 'textColorOnPrimary' => [
149 'type' => 'string',
150 'description' => __( 'Text color on primary backgrounds as hex (e.g. #FFFFFF).', 'sureforms' ),
151 ],
152 'fieldSpacing' => [
153 'type' => 'string',
154 'description' => __( 'Spacing between fields.', 'sureforms' ),
155 'enum' => [ 'small', 'medium', 'large' ],
156 ],
157 ],
158 ],
159 'emailNotification' => [
160 'type' => 'object',
161 'description' => __( 'Email notification settings for the first notification. Merges with existing.', 'sureforms' ),
162 'properties' => [
163 'status' => [
164 'type' => 'boolean',
165 'description' => __( 'Enable or disable the notification.', 'sureforms' ),
166 ],
167 'name' => [
168 'type' => 'string',
169 'description' => __( 'Notification name.', 'sureforms' ),
170 ],
171 'emailTo' => [
172 'type' => 'string',
173 'description' => __( 'Recipient email. Supports smart tags like {admin_email}.', 'sureforms' ),
174 ],
175 'emailReplyTo' => [ 'type' => 'string' ],
176 'fromName' => [
177 'type' => 'string',
178 'description' => __( 'Sender name. Supports smart tags like {site_title}.', 'sureforms' ),
179 ],
180 'fromEmail' => [ 'type' => 'string' ],
181 'emailCc' => [ 'type' => 'string' ],
182 'emailBcc' => [ 'type' => 'string' ],
183 'subject' => [
184 'type' => 'string',
185 'description' => __( 'Email subject. Supports smart tags like {form_title}.', 'sureforms' ),
186 ],
187 'emailBody' => [
188 'type' => 'string',
189 'description' => __( 'Email body. Supports smart tags like {all_data}.', 'sureforms' ),
190 ],
191 ],
192 ],
193 'formRestriction' => [
194 'type' => 'object',
195 'description' => __( 'Form submission restrictions and scheduling.', 'sureforms' ),
196 'properties' => [
197 'status' => [
198 'type' => 'boolean',
199 'description' => __( 'Enable entry limit restriction.', 'sureforms' ),
200 ],
201 'maxEntries' => [
202 'type' => 'integer',
203 'description' => __( 'Maximum number of entries allowed (0 = unlimited).', 'sureforms' ),
204 ],
205 'date' => [
206 'type' => 'string',
207 'description' => __( 'Expiry date (YYYY-MM-DD).', 'sureforms' ),
208 ],
209 'hours' => [ 'type' => 'string' ],
210 'minutes' => [ 'type' => 'string' ],
211 'meridiem' => [
212 'type' => 'string',
213 'enum' => [ 'AM', 'PM' ],
214 ],
215 'message' => [
216 'type' => 'string',
217 'description' => __( 'Message shown when form is closed.', 'sureforms' ),
218 ],
219 'schedulingStatus' => [
220 'type' => 'boolean',
221 'description' => __( 'Enable form scheduling.', 'sureforms' ),
222 ],
223 'startDate' => [
224 'type' => 'string',
225 'description' => __( 'Schedule start date (YYYY-MM-DD).', 'sureforms' ),
226 ],
227 'startHours' => [ 'type' => 'string' ],
228 'startMinutes' => [ 'type' => 'string' ],
229 'startMeridiem' => [
230 'type' => 'string',
231 'enum' => [ 'AM', 'PM' ],
232 ],
233 'schedulingNotStartedMessage' => [ 'type' => 'string' ],
234 'schedulingEndedMessage' => [ 'type' => 'string' ],
235 ],
236 ],
237 'formCustomCss' => [
238 'type' => 'string',
239 'description' => __( 'Custom CSS for the form.', 'sureforms' ),
240 ],
241 ],
242 ],
243 'formStatus' => [
244 'type' => 'string',
245 'description' => __( 'Form publish status.', 'sureforms' ),
246 'enum' => [ 'publish', 'draft', 'private' ],
247 'default' => 'draft',
248 ],
249 ],
250 'required' => [ 'formTitle', 'formFields' ],
251 ];
252 }
253
254 /**
255 * {@inheritDoc}
256 */
257 public function get_output_schema() {
258 return [
259 'type' => 'object',
260 'properties' => [
261 'form_id' => [ 'type' => 'integer' ],
262 'title' => [ 'type' => 'string' ],
263 'status' => [ 'type' => 'string' ],
264 'edit_url' => [ 'type' => 'string' ],
265 'shortcode' => [ 'type' => 'string' ],
266 ],
267 ];
268 }
269
270 /**
271 * Execute the create-form ability.
272 *
273 * @param array<string,mixed> $input Validated input data.
274 * @return array<string,mixed>|\WP_Error
275 */
276 public function execute( $input ) {
277 $form_title = sanitize_text_field( Helper::get_string_value( $input['formTitle'] ?? '' ) );
278 $form_fields = $input['formFields'];
279 $form_status = ! empty( $input['formStatus'] ) ? sanitize_text_field( Helper::get_string_value( $input['formStatus'] ) ) : 'draft';
280 $meta_data = ! empty( $input['formMetaData'] ) ? Helper::get_array_value( $input['formMetaData'] ) : [];
281
282 $allowed_statuses = [ 'publish', 'draft', 'private' ];
283 if ( ! in_array( $form_status, $allowed_statuses, true ) ) {
284 $form_status = 'draft';
285 }
286
287 if ( empty( $form_title ) ) {
288 return new \WP_Error(
289 'srfm_missing_title',
290 __( 'Form title is required.', 'sureforms' ),
291 [ 'status' => 400 ]
292 );
293 }
294
295 if ( empty( $form_fields ) || ! is_array( $form_fields ) ) {
296 return new \WP_Error(
297 'srfm_missing_fields',
298 __( 'At least one form field is required.', 'sureforms' ),
299 [ 'status' => 400 ]
300 );
301 }
302
303 // Sanitize form fields before passing to Field_Mapping.
304 $form_fields = $this->sanitize_form_fields( $form_fields );
305
306 // Build a mock WP_REST_Request to reuse Field_Mapping.
307 $request = new WP_REST_Request( 'POST' );
308 $request->set_param(
309 'form_data',
310 [
311 'form' => [
312 'formFields' => $form_fields,
313 ],
314 ]
315 );
316
317 $post_content = Field_Mapping::generate_gutenberg_fields_from_questions( $request );
318
319 if ( is_wp_error( $post_content ) ) {
320 return $post_content;
321 }
322
323 if ( empty( $post_content ) ) {
324 return new \WP_Error(
325 'srfm_field_mapping_failed',
326 __( 'Failed to generate form fields from the provided data.', 'sureforms' ),
327 [ 'status' => 400 ]
328 );
329 }
330
331 // Get default post meta.
332 $post_metas = Create_New_Form::get_default_meta_keys();
333
334 // Add serialized meta defaults for metadata overrides.
335 $post_metas['_srfm_instant_form_settings'] = [
336 'bg_type' => 'color',
337 'bg_color' => '#ffffff',
338 'bg_image' => '',
339 'site_logo' => '',
340 'cover_type' => 'color',
341 'cover_color' => '#111C44',
342 'cover_image' => '',
343 'enable_instant_form' => false,
344 'form_container_width' => 620,
345 'single_page_form_title' => true,
346 'use_banner_as_page_background' => false,
347 ];
348 $post_metas['_srfm_form_confirmation'] = [];
349 $post_metas['_srfm_compliance'] = [];
350 $post_metas['_srfm_forms_styling'] = [];
351 $post_metas['_srfm_email_notification'] = [];
352 $post_metas['_srfm_form_restriction'] = '';
353 $post_metas['_srfm_form_custom_css'] = '';
354
355 // Apply metadata overrides from input.
356 $post_metas = $this->apply_metadata_overrides( $post_metas, $meta_data );
357
358 $post_id = wp_insert_post(
359 [
360 'post_title' => $form_title,
361 'post_content' => $post_content,
362 'post_status' => $form_status,
363 'post_type' => SRFM_FORMS_POST_TYPE,
364 'meta_input' => $post_metas,
365 ],
366 true
367 );
368
369 if ( is_wp_error( $post_id ) ) {
370 return $post_id;
371 }
372
373 if ( empty( $post_id ) ) {
374 return new \WP_Error(
375 'srfm_create_failed',
376 __( 'Failed to create the form.', 'sureforms' ),
377 [ 'status' => 500 ]
378 );
379 }
380
381 return [
382 'form_id' => $post_id,
383 'title' => $form_title,
384 'status' => $form_status,
385 'edit_url' => admin_url( 'post.php?post=' . $post_id . '&action=edit' ),
386 'shortcode' => sprintf( '[sureforms id="%d"]', $post_id ),
387 ];
388 }
389 }
390