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 / create-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
create-form.php
401 lines
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 'disableDefaultStyles' => [
158 'type' => 'boolean',
159 'description' => __( 'When true, SureForms does not enqueue its default frontend styles for this form, so the active theme styling applies instead.', 'sureforms' ),
160 ],
161 ],
162 ],
163 'emailNotification' => [
164 'type' => 'object',
165 'description' => __( 'Email notification settings for the first notification. Merges with existing.', 'sureforms' ),
166 'properties' => [
167 'status' => [
168 'type' => 'boolean',
169 'description' => __( 'Enable or disable the notification.', 'sureforms' ),
170 ],
171 'name' => [
172 'type' => 'string',
173 'description' => __( 'Notification name.', 'sureforms' ),
174 ],
175 'emailTo' => [
176 'type' => 'string',
177 'description' => __( 'Recipient email. Supports smart tags like {admin_email}.', 'sureforms' ),
178 ],
179 'emailReplyTo' => [ 'type' => 'string' ],
180 'fromName' => [
181 'type' => 'string',
182 'description' => __( 'Sender name. Supports smart tags like {site_title}.', 'sureforms' ),
183 ],
184 'fromEmail' => [ 'type' => 'string' ],
185 'emailCc' => [ 'type' => 'string' ],
186 'emailBcc' => [ 'type' => 'string' ],
187 'subject' => [
188 'type' => 'string',
189 'description' => __( 'Email subject. Supports smart tags like {form_title}.', 'sureforms' ),
190 ],
191 'emailBody' => [
192 'type' => 'string',
193 'description' => __( 'Email body. Supports smart tags like {all_data}.', 'sureforms' ),
194 ],
195 ],
196 ],
197 'formRestriction' => [
198 'type' => 'object',
199 'description' => __( 'Form submission restrictions and scheduling.', 'sureforms' ),
200 'properties' => [
201 'status' => [
202 'type' => 'boolean',
203 'description' => __( 'Enable entry limit restriction.', 'sureforms' ),
204 ],
205 'maxEntries' => [
206 'type' => 'integer',
207 'description' => __( 'Maximum number of entries allowed (0 = unlimited).', 'sureforms' ),
208 ],
209 'date' => [
210 'type' => 'string',
211 'description' => __( 'Expiry date (YYYY-MM-DD).', 'sureforms' ),
212 ],
213 'hours' => [ 'type' => 'string' ],
214 'minutes' => [ 'type' => 'string' ],
215 'meridiem' => [
216 'type' => 'string',
217 'enum' => [ 'AM', 'PM' ],
218 ],
219 'message' => [
220 'type' => 'string',
221 'description' => __( 'Message shown when form is closed.', 'sureforms' ),
222 ],
223 'schedulingStatus' => [
224 'type' => 'boolean',
225 'description' => __( 'Enable form scheduling.', 'sureforms' ),
226 ],
227 'startDate' => [
228 'type' => 'string',
229 'description' => __( 'Schedule start date (YYYY-MM-DD).', 'sureforms' ),
230 ],
231 'startHours' => [ 'type' => 'string' ],
232 'startMinutes' => [ 'type' => 'string' ],
233 'startMeridiem' => [
234 'type' => 'string',
235 'enum' => [ 'AM', 'PM' ],
236 ],
237 'schedulingNotStartedMessage' => [ 'type' => 'string' ],
238 'schedulingEndedMessage' => [ 'type' => 'string' ],
239 ],
240 ],
241 'formCustomCss' => [
242 'type' => 'string',
243 'description' => __( 'Custom CSS for the form.', 'sureforms' ),
244 ],
245 ],
246 ],
247 'formStatus' => [
248 'type' => 'string',
249 'description' => __( 'Form publish status.', 'sureforms' ),
250 'enum' => [ 'publish', 'draft', 'private' ],
251 'default' => 'draft',
252 ],
253 ],
254 'required' => [ 'formTitle', 'formFields' ],
255 ];
256 }
257
258 /**
259 * {@inheritDoc}
260 */
261 public function get_output_schema() {
262 return [
263 'type' => 'object',
264 'properties' => [
265 'form_id' => [ 'type' => 'integer' ],
266 'title' => [ 'type' => 'string' ],
267 'status' => [ 'type' => 'string' ],
268 'edit_url' => [ 'type' => 'string' ],
269 'shortcode' => [ 'type' => 'string' ],
270 ],
271 ];
272 }
273
274 /**
275 * Execute the create-form ability.
276 *
277 * @param array<string,mixed> $input Validated input data.
278 * @return array<string,mixed>|\WP_Error
279 */
280 public function execute( $input ) {
281 $form_title = sanitize_text_field( Helper::get_string_value( $input['formTitle'] ?? '' ) );
282 $form_fields = $input['formFields'];
283 $form_status = ! empty( $input['formStatus'] ) ? sanitize_text_field( Helper::get_string_value( $input['formStatus'] ) ) : 'draft';
284 $meta_data = ! empty( $input['formMetaData'] ) ? Helper::get_array_value( $input['formMetaData'] ) : [];
285
286 $allowed_statuses = [ 'publish', 'draft', 'private' ];
287 if ( ! in_array( $form_status, $allowed_statuses, true ) ) {
288 $form_status = 'draft';
289 }
290
291 if ( empty( $form_title ) ) {
292 return new \WP_Error(
293 'srfm_missing_title',
294 __( 'Form title is required.', 'sureforms' ),
295 [ 'status' => 400 ]
296 );
297 }
298
299 if ( empty( $form_fields ) || ! is_array( $form_fields ) ) {
300 return new \WP_Error(
301 'srfm_missing_fields',
302 __( 'At least one form field is required.', 'sureforms' ),
303 [ 'status' => 400 ]
304 );
305 }
306
307 // Sanitize form fields before passing to Field_Mapping.
308 $form_fields = $this->sanitize_form_fields( $form_fields );
309
310 // Build a mock WP_REST_Request to reuse Field_Mapping.
311 $request = new WP_REST_Request( 'POST' );
312 $request->set_param(
313 'form_data',
314 [
315 'form' => [
316 'formFields' => $form_fields,
317 ],
318 ]
319 );
320
321 $post_content = Field_Mapping::generate_gutenberg_fields_from_questions( $request );
322
323 if ( is_wp_error( $post_content ) ) {
324 return $post_content;
325 }
326
327 if ( empty( $post_content ) ) {
328 return new \WP_Error(
329 'srfm_field_mapping_failed',
330 __( 'Failed to generate form fields from the provided data.', 'sureforms' ),
331 [ 'status' => 400 ]
332 );
333 }
334
335 // Get default post meta.
336 $post_metas = Create_New_Form::get_default_meta_keys();
337
338 // Pull complex-meta defaults from the registered schemas so created
339 // forms are identical to hand-created ones. Storing empty arrays/strings
340 // would override the register_post_meta 'default' values (WordPress only
341 // uses those defaults when no value is stored at all), causing confirmation
342 // messages, email notifications, styling, and restriction settings to be
343 // blank for any form created through this code path.
344 $registered = get_registered_meta_keys( 'post', SRFM_FORMS_POST_TYPE );
345
346 $post_metas['_srfm_instant_form_settings'] = [
347 'bg_type' => 'color',
348 'bg_color' => '#ffffff',
349 'bg_image' => '',
350 'site_logo' => '',
351 'cover_type' => 'color',
352 'cover_color' => '#111C44',
353 'cover_image' => '',
354 'enable_instant_form' => false,
355 'form_container_width' => 620,
356 'single_page_form_title' => true,
357 'use_banner_as_page_background' => false,
358 ];
359 $post_metas['_srfm_form_confirmation'] = $registered['_srfm_form_confirmation']['default'] ?? [];
360 $post_metas['_srfm_compliance'] = $registered['_srfm_compliance']['default'] ?? [];
361 $post_metas['_srfm_forms_styling'] = $registered['_srfm_forms_styling']['default'] ?? [];
362 $post_metas['_srfm_email_notification'] = $registered['_srfm_email_notification']['default'] ?? [];
363 $post_metas['_srfm_form_restriction'] = $registered['_srfm_form_restriction']['default'] ?? '';
364 $post_metas['_srfm_form_custom_css'] = '';
365
366 // Apply metadata overrides from input.
367 $post_metas = $this->apply_metadata_overrides( $post_metas, $meta_data );
368
369 $post_id = wp_insert_post(
370 [
371 'post_title' => $form_title,
372 'post_content' => $post_content,
373 'post_status' => $form_status,
374 'post_type' => SRFM_FORMS_POST_TYPE,
375 'meta_input' => $post_metas,
376 ],
377 true
378 );
379
380 if ( is_wp_error( $post_id ) ) {
381 return $post_id;
382 }
383
384 if ( empty( $post_id ) ) {
385 return new \WP_Error(
386 'srfm_create_failed',
387 __( 'Failed to create the form.', 'sureforms' ),
388 [ 'status' => 500 ]
389 );
390 }
391
392 return [
393 'form_id' => $post_id,
394 'title' => $form_title,
395 'status' => $form_status,
396 'edit_url' => admin_url( 'post.php?post=' . $post_id . '&action=edit' ),
397 'shortcode' => sprintf( '[sureforms id="%d"]', $post_id ),
398 ];
399 }
400 }
401