Overview

Namespaces

  • None
  • Quform
    • Traduttore_Registry

Classes

  • Quform
  • Quform_Admin_InsertForm
  • Quform_Admin_Page
  • Quform_Admin_Page_Controller
  • Quform_Admin_Page_Dashboard
  • Quform_Admin_Page_Entries
  • Quform_Admin_Page_Entries_Edit
  • Quform_Admin_Page_Entries_List
  • Quform_Admin_Page_Entries_View
  • Quform_Admin_Page_Factory
  • Quform_Admin_Page_Forms_Add
  • Quform_Admin_Page_Forms_Edit
  • Quform_Admin_Page_Forms_List
  • Quform_Admin_Page_Help
  • Quform_Admin_Page_Preview
  • Quform_Admin_Page_Settings
  • Quform_Admin_Page_Tools
  • Quform_Admin_Page_Tools_ExportEntries
  • Quform_Admin_Page_Tools_ExportForm
  • Quform_Admin_Page_Tools_Home
  • Quform_Admin_Page_Tools_ImportForm
  • Quform_Admin_Page_Tools_Migrate
  • Quform_Admin_Page_Tools_Uninstall
  • Quform_Api
  • Quform_Block
  • Quform_Builder
  • Quform_Captcha
  • Quform_ClassLoader
  • Quform_Confirmation
  • Quform_Container
  • Quform_Dashboard_Widget
  • Quform_Dispatcher
  • Quform_Element
  • Quform_Element_Captcha
  • Quform_Element_Checkbox
  • Quform_Element_Column
  • Quform_Element_Container
  • Quform_Element_Container_Iterator
  • Quform_Element_Date
  • Quform_Element_Email
  • Quform_Element_Factory
  • Quform_Element_Field
  • Quform_Element_File
  • Quform_Element_Group
  • Quform_Element_Hidden
  • Quform_Element_Honeypot
  • Quform_Element_Html
  • Quform_Element_Multi
  • Quform_Element_Multiselect
  • Quform_Element_Name
  • Quform_Element_Page
  • Quform_Element_Password
  • Quform_Element_Radio
  • Quform_Element_Recaptcha
  • Quform_Element_Row
  • Quform_Element_Select
  • Quform_Element_Submit
  • Quform_Element_Text
  • Quform_Element_Textarea
  • Quform_Element_Time
  • Quform_Entry_Controller
  • Quform_Entry_Exporter
  • Quform_Entry_List_Settings
  • Quform_Entry_List_Table
  • Quform_Entry_Processor
  • Quform_Entry_UserSearcher
  • Quform_Filter_Abstract
  • Quform_Filter_Alpha
  • Quform_Filter_AlphaNumeric
  • Quform_Filter_Digits
  • Quform_Filter_Regex
  • Quform_Filter_Static
  • Quform_Filter_StripTags
  • Quform_Filter_Trim
  • Quform_Form
  • Quform_Form_Controller
  • Quform_Form_Exporter
  • Quform_Form_Factory
  • Quform_Form_Importer
  • Quform_Form_Iterator
  • Quform_Form_List_Settings
  • Quform_Form_List_Table
  • Quform_Form_Processor
  • Quform_License
  • Quform_Migrator
  • Quform_NonceRefresher
  • Quform_Notification
  • Quform_Notification_Resender
  • Quform_Options
  • Quform_Permissions
  • Quform_Repository
  • Quform_ScriptLoader
  • Quform_Session
  • Quform_Settings
  • Quform_Shortcode
  • Quform_Themes
  • Quform_TokenReplacer
  • Quform_Toolbar
  • Quform_Translations
  • Quform_Updater
  • Quform_Upgrader
  • Quform_Uploader
  • Quform_Validator_Abstract
  • Quform_Validator_Alpha
  • Quform_Validator_AlphaNumeric
  • Quform_Validator_Array
  • Quform_Validator_Captcha
  • Quform_Validator_Date
  • Quform_Validator_Digits
  • Quform_Validator_Duplicate
  • Quform_Validator_Email
  • Quform_Validator_FileUpload
  • Quform_Validator_GreaterThan
  • Quform_Validator_Honeypot
  • Quform_Validator_Identical
  • Quform_Validator_InArray
  • Quform_Validator_Length
  • Quform_Validator_LessThan
  • Quform_Validator_Recaptcha
  • Quform_Validator_Regex
  • Quform_Validator_Required
  • Quform_Validator_Static
  • Quform_Validator_Time
  • Quform_View
  • Quform_ViewFactory
  • Quform_Widget_Form
  • Quform_Widget_Popup

Interfaces

  • Quform_Attachable
  • Quform_Element_Editable
  • Quform_Filter_Interface
  • Quform_Validator_Interface

Constants

  • Quform\Traduttore_Registry\TRANSIENT_KEY_PLUGIN
  • Quform\Traduttore_Registry\TRANSIENT_KEY_THEME

Functions

  • Quform\Traduttore_Registry\add_project
  • Quform\Traduttore_Registry\clean_translations_cache
  • Quform\Traduttore_Registry\get_available_locales
  • Quform\Traduttore_Registry\get_installed_translations
  • Quform\Traduttore_Registry\get_translations
  • Quform\Traduttore_Registry\register_clean_translations_cache
  • Quform\Traduttore_Registry\sanitize_date
  • Overview
  • Namespace
  • Class
  1: <?php
  2: 
  3: /**
  4:  * @copyright Copyright (c) 2009-2022 ThemeCatcher (https://www.themecatcher.net)
  5:  */
  6: class Quform_Form_Processor
  7: {
  8:     /**
  9:      * @var Quform_Repository
 10:      */
 11:     protected $repository;
 12: 
 13:     /**
 14:      * @var Quform_Session
 15:      */
 16:     protected $session;
 17: 
 18:     /**
 19:      * @var Quform_Uploader
 20:      */
 21:     protected $uploader;
 22: 
 23:     /**
 24:      * @var Quform_Options
 25:      */
 26:     protected $options;
 27: 
 28:     /**
 29:      * @param  Quform_Repository $repository
 30:      * @param  Quform_Session    $session
 31:      * @param  Quform_Uploader   $uploader
 32:      * @param  Quform_Options    $options
 33:      */
 34:     public function __construct(
 35:         Quform_Repository $repository,
 36:         Quform_Session $session,
 37:         Quform_Uploader $uploader,
 38:         Quform_Options $options
 39:     ) {
 40:         $this->repository = $repository;
 41:         $this->session = $session;
 42:         $this->uploader = $uploader;
 43:         $this->options = $options;
 44:     }
 45: 
 46:     /**
 47:      * Process the given form
 48:      *
 49:      * @param   Quform_Form  $form
 50:      * @return  array
 51:      */
 52:     public function process(Quform_Form $form)
 53:     {
 54:         // Strip slashes from the submitted data (WP adds them automatically)
 55:         $_POST = wp_unslash($_POST);
 56: 
 57:         // CSRF check
 58:         if ($this->options->get('csrfProtection') && ( ! isset($_POST['quform_csrf_token']) || $this->session->getToken() != $_POST['quform_csrf_token'])) {
 59:             return apply_filters(
 60:                 'quform_csrf_failure_response',
 61:                 array(
 62:                     'type' => 'error',
 63:                     'error' => array(
 64:                         'enabled' => true,
 65:                         'title' => __('An error occurred', 'quform'),
 66:                         'content' => __('Refresh the page and try again.', 'quform')
 67:                     )
 68:                 ),
 69:                 $form
 70:             );
 71:         }
 72: 
 73:         // Time-based spam protection check
 74:         if ($this->options->get('timeBasedProtection') && isset($_POST['quform_loaded'])) {
 75:             $parts = explode('|', sanitize_text_field($_POST['quform_loaded']), 2);
 76: 
 77:             // Verify format and hash
 78:             if (count($parts) === 2) {
 79:                 list($timestamp, $hash) = $parts;
 80: 
 81:                 // Verify hash to prevent tampering
 82:                 if (hash_equals(wp_hash($timestamp . '|'), $hash)) {
 83:                     $elapsed = time() - (int) $timestamp;
 84: 
 85:                     // Check minimum submission time to detect bots
 86:                     $minTime = apply_filters('quform_time_based_min_time', 2);
 87:                     $minTime = apply_filters('quform_time_based_min_time_' . $form->getId(), $minTime, $form);
 88: 
 89:                     if ($elapsed < $minTime) {
 90:                         return apply_filters(
 91:                             'quform_time_based_failure_response',
 92:                             array(
 93:                                 'type' => 'error',
 94:                                 'error' => array(
 95:                                     'enabled' => true,
 96:                                     'title' => __('An error occurred', 'quform'),
 97:                                     'content' => __('Please wait a moment before submitting.', 'quform')
 98:                                 )
 99:                             ),
100:                             $form
101:                         );
102:                     }
103:                 } else {
104:                     // Hash verification failed - possible tampering, treat as spam
105:                     return apply_filters(
106:                         'quform_time_based_failure_response',
107:                         array(
108:                             'type' => 'error',
109:                             'error' => array(
110:                                 'enabled' => true,
111:                                 'title' => __('An error occurred', 'quform'),
112:                                 'content' => __('Refresh the page and try again.', 'quform')
113:                             )
114:                         ),
115:                         $form
116:                     );
117:                 }
118:             } else {
119:                 // Invalid format - possible tampering, treat as spam
120:                 return apply_filters(
121:                     'quform_time_based_failure_response',
122:                     array(
123:                         'type' => 'error',
124:                         'error' => array(
125:                             'enabled' => true,
126:                             'title' => __('An error occurred', 'quform'),
127:                             'content' => __('Refresh the page and try again.', 'quform')
128:                         )
129:                     ),
130:                     $form
131:                 );
132:             }
133:         }
134: 
135:         // Pre-process hooks
136:         $result = apply_filters('quform_pre_process', array(), $form);
137:         $result = apply_filters('quform_pre_process_' . $form->getId(), $result, $form);
138: 
139:         if (is_array($result) && ! empty($result)) {
140:             return $result;
141:         }
142: 
143:         $result = $this->checkEntryLimits( $form);
144: 
145:         if (is_array($result) && ! empty($result)) {
146:             return $result;
147:         }
148: 
149:         $result = $this->checkFormSchedule( $form);
150: 
151:         if (is_array($result) && ! empty($result)) {
152:             return $result;
153:         }
154: 
155:         $this->uploader->mergeSessionFiles($form);
156: 
157:         // Set the form element values
158:         $form->setValues($_POST, true);
159: 
160:         $result = apply_filters('quform_post_set_form_values', array(), $form);
161:         $result = apply_filters('quform_post_set_form_values_' . $form->getId(), $result, $form);
162: 
163:         if (is_array($result) && ! empty($result)) {
164:             return $result;
165:         }
166: 
167:         // Calculate which elements are hidden by conditional logic and which groups are empty
168:         $form->calculateElementVisibility();
169: 
170:         $form->setCurrentPageById((int) $_POST['quform_current_page_id']);
171: 
172:         // Pre-validate action hooks
173:         $result = apply_filters('quform_pre_validate', array(), $form);
174:         $result = apply_filters('quform_pre_validate_' . $form->getId(), $result, $form);
175: 
176:         if (is_array($result) && ! empty($result)) {
177:             return $result;
178:         }
179: 
180:         // Moving between pages
181:         if ($form->hasPages()) {
182:             if (isset($_POST['quform_submit']) && $_POST['quform_submit'] === 'back') {
183:                 // We want to go to a previous page, so don't validate just find the closest page that isn't hidden by CL
184:                 return array('type' => 'page', 'page' => $form->getNextPageId(true));
185:             } else {
186:                 // If the current page is valid and there is a next page not hidden by CL, return the next page ID
187:                 if (($nextPageId = $form->getNextPageId()) !== null) {
188:                     if ($form->getCurrentPage()->isValid()) {
189:                         return array('type' => 'page', 'page' => $nextPageId);
190:                     } else {
191:                         // Return current page errors
192:                         return array('type' => 'error', 'error' => $form->getGlobalError(), 'errors' => $form->getCurrentPage()->getErrors(), 'page' => $form->getCurrentPage()->getId());
193:                     }
194:                 }
195:             }
196:         }
197: 
198:         // This is the last page so validate the entire form
199:         list($valid, $firstErrorPage) = $form->isValid();
200: 
201:         if ($valid) {
202:             // Post-validate action hooks
203:             $result = apply_filters('quform_post_validate', array(), $form);
204:             $result = apply_filters('quform_post_validate_' . $form->getId(), $result, $form);
205: 
206:             if (is_array($result) && ! empty($result)) {
207:                 return $result;
208:             }
209: 
210:             // Save the entry
211:             $entryId = $this->saveEntry($form);
212:             $form->setEntryId($entryId);
213: 
214:             $result = apply_filters('quform_post_set_entry_id', array(), $form);
215:             $result = apply_filters('quform_post_set_entry_id_' . $form->getId(), $result, $form);
216: 
217:             if (is_array($result) && ! empty($result)) {
218:                 return $result;
219:             }
220: 
221:             // Process any uploads
222:             $this->uploader->process($form);
223: 
224:             // Save the entry data
225:             $this->saveEntryData($entryId, $form);
226: 
227:             $result = apply_filters('quform_post_save_entry_data', array(), $form);
228:             $result = apply_filters('quform_post_save_entry_data_' . $form->getId(), $result, $form);
229: 
230:             if (is_array($result) && ! empty($result)) {
231:                 return $result;
232:             }
233: 
234:             // Send notification emails
235:             $this->sendNotifications($form);
236: 
237:             // Set the confirmation to use for this submission
238:             $form->setConfirmation();
239: 
240:             // Save to custom database table
241:             $this->saveToCustomDatabase($form);
242: 
243:             // Clear session data for this form
244:             if ($this->session->has($form->getSessionKey())) {
245:                 $this->session->forget($form->getSessionKey());
246:             }
247: 
248:             // Post-process action hooks
249:             $result = apply_filters('quform_post_process', array(), $form);
250:             $result = apply_filters('quform_post_process_' . $form->getId(), $result, $form);
251: 
252:             if (is_array($result) && ! empty($result)) {
253:                 return $result;
254:             }
255: 
256:             $result = array(
257:                 'type' => 'success',
258:                 'confirmation' => $form->getConfirmation()->getData()
259:             );
260:         } else {
261:             $result = array(
262:                 'type' => 'error',
263:                 'error' => $form->getGlobalError(),
264:                 'errors' => $form->getErrors(),
265:                 'page' => $firstErrorPage->getId()
266:             );
267:         }
268: 
269:         return $result;
270:     }
271: 
272:     /**
273:      * Check if the current submission exceeds any entry limit
274:      *
275:      * @param   Quform_Form  $form  The current form instance
276:      * @return  true|array          Return true to proceed with form processing, or a response array to show an error
277:      */
278:     protected function checkEntryLimits(Quform_Form $form) {
279:         if ( ! $this->options->get('saveEntries') || ! $form->config('saveEntry')) {
280:             return true;
281:         }
282: 
283:         if (apply_filters('quform_bypass_entry_limits', current_user_can('quform_edit_forms'), $form)) {
284:             return true;
285:         }
286: 
287:         if ($form->config('oneEntryPerUser')) {
288:             $entryExists = false;
289: 
290:             if ($form->config('oneEntryPer') == 'logged-in-user') {
291:                 if (is_user_logged_in()) {
292:                     $entryExists = $this->repository->entryExistsByFormIdAndCreatedBy(
293:                         $form->getId(),
294:                         get_current_user_id()
295:                     );
296:                 }
297:             } elseif ($form->config('oneEntryPer') == 'ip-address') {
298:                 $entryExists = $this->repository->entryExistsByFormIdAndIpAddress(
299:                     $form->getId(),
300:                     Quform::getClientIp()
301:                 );
302:             }
303: 
304:             $entryExists = apply_filters('quform_one_entry_per_user_entry_exists', $entryExists, $form);
305: 
306:             if ($entryExists) {
307:                 return apply_filters('quform_one_entry_per_user_error', array(
308:                     'type' => 'error',
309:                     'error' => array(
310:                         'enabled' => true,
311:                         'title' => '',
312:                         'content' => $form->getTranslation(
313:                             'onlyOneSubmissionAllowed',
314:                             __('Only one submission is allowed.', 'quform')
315:                         )
316:                     )
317:                 ), $form);
318:             }
319:         }
320: 
321:         if ($form->config('limitEntries') && is_numeric($form->config('entryLimit')) && $form->config('entryLimit') > 0) {
322:             $entryCount = $this->repository->getEntryCount($form->getId());
323: 
324:             $entryCount = apply_filters('quform_entry_count', $entryCount, $form);
325:             $entryCount = apply_filters('quform_entry_count_' . $form->getId(), $entryCount, $form);
326: 
327:             if ($entryCount >= $form->config('entryLimit')) {
328:                 return apply_filters('quform_entry_limit_reached_error', array(
329:                     'type' => 'error',
330:                     'error' => array(
331:                         'enabled' => true,
332:                         'title' => '',
333:                         'content' => $form->getTranslation(
334:                             'thisFormIsCurrentlyClosed',
335:                             __('This form is currently closed for submissions.', 'quform')
336:                         )
337:                     )
338:                 ), $form);
339:             }
340:         }
341: 
342:         return true;
343:     }
344: 
345:     /**
346:      * Check if the current submission is within the schedule
347:      *
348:      * @param   Quform_Form  $form  The current form instance
349:      * @return  true|array          Return true to proceed with form processing, or a response array to show an error
350:      */
351:     public function checkFormSchedule(Quform_Form $form)
352:     {
353:         if (
354:             ! $form->config('enableSchedule') ||
355:             (
356:                 ! Quform::isNonEmptyString('scheduleStart') &&
357:                 ! Quform::isNonEmptyString('scheduleEnd')
358:             )
359:         ) {
360:             return true;
361:         }
362: 
363:         if (apply_filters('quform_bypass_form_schedule', current_user_can('quform_edit_forms'), $form)) {
364:             return true;
365:         }
366: 
367:         try {
368:             $now = new DateTime('now', new DateTimeZone('UTC'));
369: 
370:             if (Quform::isNonEmptyString($form->config('scheduleStart'))) {
371:                 $start = DateTime::createFromFormat('Y-m-d H:i:s', $form->config('scheduleStart'));
372: 
373:                 if ($start instanceof DateTime && $now < $start) {
374:                     return apply_filters('quform_schedule_start_error', array(
375:                         'type' => 'error',
376:                         'error' => array(
377:                             'enabled' => true,
378:                             'title' => '',
379:                             'content' => $form->getTranslation(
380:                                 'formIsNotYetOpenForSubmissions',
381:                                 __('This form is not yet open for submissions.', 'quform')
382:                             )
383:                         )
384:                     ), $form);
385:                 }
386:             }
387: 
388:             if (Quform::isNonEmptyString($form->config('scheduleEnd'))) {
389:                 $end = DateTime::createFromFormat('Y-m-d H:i:s', $form->config('scheduleEnd'));
390: 
391:                 if ($end instanceof DateTime && $now > $end) {
392:                     return apply_filters('quform_schedule_end_error', array(
393:                         'type' => 'error',
394:                         'error' => array(
395:                             'enabled' => true,
396:                             'title' => '',
397:                             'content' => $form->getTranslation(
398:                                 'formIsNoLongerOpenForSubmissions',
399:                                 __('This form is no longer open for submissions.', 'quform')
400:                             )
401:                         )
402:                     ), $form);
403:                 }
404:             }
405:         } catch (Exception $e) {
406:             // Ignore exception, continue processing
407:         }
408: 
409:         return true;
410:     }
411: 
412:     /**
413:      * @param   Quform_Form  $form
414:      * @return  $this
415:      */
416:     protected function sendNotifications(Quform_Form $form)
417:     {
418:         foreach ($form->getNotifications() as $notification) {
419:             if ( ! $notification->config('enabled')) {
420:                 continue;
421:             }
422: 
423:             if ($notification->config('logicEnabled') && count($notification->config('logicRules'))) {
424:                 if ($form->checkLogicAction($notification->config('logicAction'), $notification->config('logicMatch'), $notification->config('logicRules'))) {
425:                     $notification->send();
426:                 }
427:             } else {
428:                 $notification->send();
429:             }
430:         }
431: 
432:         return $this;
433:     }
434: 
435:     /**
436:      * Create a new entry and return the new entry ID
437:      *
438:      * @param   Quform_Form  $form
439:      * @return  int|null
440:      */
441:     protected function saveEntry(Quform_Form $form)
442:     {
443:         if ( ! $this->options->get('saveEntries') || ! $form->config('saveEntry')) {
444:             return null;
445:         }
446: 
447:         $currentTime = Quform::date('Y-m-d H:i:s', null, new DateTimeZone('UTC'));
448: 
449:         $entry = array(
450:             'form_id'           => $form->getId(),
451:             'ip'                => $this->options->get('saveIpAddresses') ? Quform::substr(Quform::getClientIp(), 0, 45) : '',
452:             'form_url'          => isset($_POST['form_url']) ? Quform::substr($_POST['form_url'], 0, 512) : '',
453:             'referring_url'     => isset($_POST['referring_url']) ? Quform::substr($_POST['referring_url'], 0, 512) : '',
454:             'post_id'           => is_numeric($postId = Quform::get($_POST, 'post_id')) && $postId > 0 ? (int) $postId : null,
455:             'created_by'        => is_user_logged_in() ? (int) Quform::getUserProperty('ID') : null,
456:             'created_at'        => $currentTime,
457:             'updated_at'        => $currentTime,
458:         );
459: 
460:         $entry = $this->repository->saveEntry($entry);
461: 
462:         return $entry['id'];
463:     }
464: 
465:     /**
466:      * Save the entry data
467:      *
468:      * @param  int          $entryId
469:      * @param  Quform_Form  $form
470:      */
471:     protected function saveEntryData($entryId, Quform_Form $form)
472:     {
473:         if ( ! ($entryId > 0) || ! $this->options->get('saveEntries') || ! $form->config('saveEntry')) {
474:             return;
475:         }
476: 
477:         $data = array();
478: 
479:         foreach ($form->getRecursiveIterator() as $element) {
480:             if ($element->config('saveToDatabase') && ! $element->isConditionallyHidden()) {
481:                 if ( ! $element->isEmpty()) {
482:                     $data[$element->getId()] = $element->getValueForStorage();
483:                 }
484:             }
485:         }
486: 
487:         if (count($data)) {
488:             $this->repository->saveEntryData($entryId, $data);
489:         }
490:     }
491: 
492:     /**
493:      * Save to the custom database table if configured
494:      *
495:      * @param Quform_Form $form
496:      */
497:     protected function saveToCustomDatabase(Quform_Form $form)
498:     {
499:         if ( ! $form->config('databaseEnabled') ||
500:              ! count($columns = $form->config('databaseColumns')) ||
501:              ! Quform::isNonEmptyString($table = $form->config('databaseTable'))
502:         ) {
503:             return;
504:         }
505: 
506:         $data = array();
507:         foreach ($columns as $column) {
508:             if ( ! Quform::isNonEmptyString($column['name'])) {
509:                 continue;
510:             }
511: 
512:             $data[$column['name']] = $form->replaceVariables($column['value']);
513:         }
514: 
515:         if ($form->config('databaseWordpress')) {
516:             global $wpdb;
517:             $wpdb->insert($table, $data);
518:         } else {
519:             $customWpdb = new wpdb(
520:                 $form->config('databaseUsername'),
521:                 $form->config('databasePassword'),
522:                 $form->config('databaseDatabase'),
523:                 $form->config('databaseHost')
524:             );
525: 
526:             $customWpdb->insert($table, $data);
527:         }
528:     }
529: }
530: 
API documentation generated by ApiGen