1: <?php
  2: 
  3:   4:   5: 
  6: class Quform_Form_Processor
  7: {
  8:       9:  10: 
 11:     protected $repository;
 12: 
 13:      14:  15: 
 16:     protected $session;
 17: 
 18:      19:  20: 
 21:     protected $uploader;
 22: 
 23:      24:  25: 
 26:     protected $options;
 27: 
 28:      29:  30:  31:  32:  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:  48:  49:  50:  51: 
 52:     public function process(Quform_Form $form)
 53:     {
 54:         
 55:         $_POST = wp_unslash($_POST);
 56: 
 57:         
 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:         
 74:         $result = apply_filters('quform_pre_process', array(), $form);
 75:         $result = apply_filters('quform_pre_process_' . $form->getId(), $result, $form);
 76: 
 77:         if (is_array($result) && ! empty($result)) {
 78:             return $result;
 79:         }
 80: 
 81:         $result = $this->checkEntryLimits( $form);
 82: 
 83:         if (is_array($result) && ! empty($result)) {
 84:             return $result;
 85:         }
 86: 
 87:         $result = $this->checkFormSchedule( $form);
 88: 
 89:         if (is_array($result) && ! empty($result)) {
 90:             return $result;
 91:         }
 92: 
 93:         $this->uploader->mergeSessionFiles($form);
 94: 
 95:         
 96:         $form->setValues($_POST, true);
 97: 
 98:         $result = apply_filters('quform_post_set_form_values', array(), $form);
 99:         $result = apply_filters('quform_post_set_form_values_' . $form->getId(), $result, $form);
100: 
101:         if (is_array($result) && ! empty($result)) {
102:             return $result;
103:         }
104: 
105:         
106:         $form->calculateElementVisibility();
107: 
108:         $form->setCurrentPageById((int) $_POST['quform_current_page_id']);
109: 
110:         
111:         $result = apply_filters('quform_pre_validate', array(), $form);
112:         $result = apply_filters('quform_pre_validate_' . $form->getId(), $result, $form);
113: 
114:         if (is_array($result) && ! empty($result)) {
115:             return $result;
116:         }
117: 
118:         
119:         if ($form->hasPages()) {
120:             if (isset($_POST['quform_submit']) && $_POST['quform_submit'] === 'back') {
121:                 
122:                 return array('type' => 'page', 'page' => $form->getNextPageId(true));
123:             } else {
124:                 
125:                 if (($nextPageId = $form->getNextPageId()) !== null) {
126:                     if ($form->getCurrentPage()->isValid()) {
127:                         return array('type' => 'page', 'page' => $nextPageId);
128:                     } else {
129:                         
130:                         return array('type' => 'error', 'error' => $form->getGlobalError(), 'errors' => $form->getCurrentPage()->getErrors(), 'page' => $form->getCurrentPage()->getId());
131:                     }
132:                 }
133:             }
134:         }
135: 
136:         
137:         list($valid, $firstErrorPage) = $form->isValid();
138: 
139:         if ($valid) {
140:             
141:             $result = apply_filters('quform_post_validate', array(), $form);
142:             $result = apply_filters('quform_post_validate_' . $form->getId(), $result, $form);
143: 
144:             if (is_array($result) && ! empty($result)) {
145:                 return $result;
146:             }
147: 
148:             
149:             $entryId = $this->saveEntry($form);
150:             $form->setEntryId($entryId);
151: 
152:             $result = apply_filters('quform_post_set_entry_id', array(), $form);
153:             $result = apply_filters('quform_post_set_entry_id_' . $form->getId(), $result, $form);
154: 
155:             if (is_array($result) && ! empty($result)) {
156:                 return $result;
157:             }
158: 
159:             
160:             $this->uploader->process($form);
161: 
162:             
163:             $this->saveEntryData($entryId, $form);
164: 
165:             $result = apply_filters('quform_post_save_entry_data', array(), $form);
166:             $result = apply_filters('quform_post_save_entry_data_' . $form->getId(), $result, $form);
167: 
168:             if (is_array($result) && ! empty($result)) {
169:                 return $result;
170:             }
171: 
172:             
173:             $this->sendNotifications($form);
174: 
175:             
176:             $form->setConfirmation();
177: 
178:             
179:             $this->saveToCustomDatabase($form);
180: 
181:             
182:             if ($this->session->has($form->getSessionKey())) {
183:                 $this->session->forget($form->getSessionKey());
184:             }
185: 
186:             
187:             $result = apply_filters('quform_post_process', array(), $form);
188:             $result = apply_filters('quform_post_process_' . $form->getId(), $result, $form);
189: 
190:             if (is_array($result) && ! empty($result)) {
191:                 return $result;
192:             }
193: 
194:             $result = array(
195:                 'type' => 'success',
196:                 'confirmation' => $form->getConfirmation()->getData()
197:             );
198:         } else {
199:             $result = array(
200:                 'type' => 'error',
201:                 'error' => $form->getGlobalError(),
202:                 'errors' => $form->getErrors(),
203:                 'page' => $firstErrorPage->getId()
204:             );
205:         }
206: 
207:         return $result;
208:     }
209: 
210:     211: 212: 213: 214: 215: 
216:     protected function checkEntryLimits(Quform_Form $form) {
217:         if ( ! $this->options->get('saveEntries') || ! $form->config('saveEntry')) {
218:             return true;
219:         }
220: 
221:         if (apply_filters('quform_bypass_entry_limits', current_user_can('quform_edit_forms'), $form)) {
222:             return true;
223:         }
224: 
225:         if ($form->config('oneEntryPerUser')) {
226:             $entryExists = false;
227: 
228:             if ($form->config('oneEntryPer') == 'logged-in-user') {
229:                 if (is_user_logged_in()) {
230:                     $entryExists = $this->repository->entryExistsByFormIdAndCreatedBy(
231:                         $form->getId(),
232:                         get_current_user_id()
233:                     );
234:                 }
235:             } elseif ($form->config('oneEntryPer') == 'ip-address') {
236:                 $entryExists = $this->repository->entryExistsByFormIdAndIpAddress(
237:                     $form->getId(),
238:                     Quform::getClientIp()
239:                 );
240:             }
241: 
242:             $entryExists = apply_filters('quform_one_entry_per_user_entry_exists', $entryExists, $form);
243: 
244:             if ($entryExists) {
245:                 return apply_filters('quform_one_entry_per_user_error', array(
246:                     'type' => 'error',
247:                     'error' => array(
248:                         'enabled' => true,
249:                         'title' => '',
250:                         'content' => $form->getTranslation(
251:                             'onlyOneSubmissionAllowed',
252:                             __('Only one submission is allowed.', 'quform')
253:                         )
254:                     )
255:                 ), $form);
256:             }
257:         }
258: 
259:         if ($form->config('limitEntries') && is_numeric($form->config('entryLimit')) && $form->config('entryLimit') > 0) {
260:             $entryCount = $this->repository->getEntryCount($form->getId());
261: 
262:             $entryCount = apply_filters('quform_entry_count', $entryCount, $form);
263:             $entryCount = apply_filters('quform_entry_count_' . $form->getId(), $entryCount, $form);
264: 
265:             if ($entryCount >= $form->config('entryLimit')) {
266:                 return apply_filters('quform_entry_limit_reached_error', array(
267:                     'type' => 'error',
268:                     'error' => array(
269:                         'enabled' => true,
270:                         'title' => '',
271:                         'content' => $form->getTranslation(
272:                             'thisFormIsCurrentlyClosed',
273:                             __('This form is currently closed for submissions.', 'quform')
274:                         )
275:                     )
276:                 ), $form);
277:             }
278:         }
279: 
280:         return true;
281:     }
282: 
283:     284: 285: 286: 287: 288: 
289:     public function checkFormSchedule(Quform_Form $form)
290:     {
291:         if (
292:             ! $form->config('enableSchedule') ||
293:             (
294:                 ! Quform::isNonEmptyString('scheduleStart') &&
295:                 ! Quform::isNonEmptyString('scheduleEnd')
296:             )
297:         ) {
298:             return true;
299:         }
300: 
301:         if (apply_filters('quform_bypass_form_schedule', current_user_can('quform_edit_forms'), $form)) {
302:             return true;
303:         }
304: 
305:         try {
306:             $now = new DateTime('now', new DateTimeZone('UTC'));
307: 
308:             if (Quform::isNonEmptyString($form->config('scheduleStart'))) {
309:                 $start = DateTime::createFromFormat('Y-m-d H:i:s', $form->config('scheduleStart'));
310: 
311:                 if ($start instanceof DateTime && $now < $start) {
312:                     return apply_filters('quform_schedule_start_error', array(
313:                         'type' => 'error',
314:                         'error' => array(
315:                             'enabled' => true,
316:                             'title' => '',
317:                             'content' => $form->getTranslation(
318:                                 'formIsNotYetOpenForSubmissions',
319:                                 __('This form is not yet open for submissions.', 'quform')
320:                             )
321:                         )
322:                     ), $form);
323:                 }
324:             }
325: 
326:             if (Quform::isNonEmptyString($form->config('scheduleEnd'))) {
327:                 $end = DateTime::createFromFormat('Y-m-d H:i:s', $form->config('scheduleEnd'));
328: 
329:                 if ($end instanceof DateTime && $now > $end) {
330:                     return apply_filters('quform_schedule_end_error', array(
331:                         'type' => 'error',
332:                         'error' => array(
333:                             'enabled' => true,
334:                             'title' => '',
335:                             'content' => $form->getTranslation(
336:                                 'formIsNoLongerOpenForSubmissions',
337:                                 __('This form is no longer open for submissions.', 'quform')
338:                             )
339:                         )
340:                     ), $form);
341:                 }
342:             }
343:         } catch (Exception $e) {
344:             
345:         }
346: 
347:         return true;
348:     }
349: 
350:     351: 352: 353: 
354:     protected function sendNotifications(Quform_Form $form)
355:     {
356:         foreach ($form->getNotifications() as $notification) {
357:             if ( ! $notification->config('enabled')) {
358:                 continue;
359:             }
360: 
361:             if ($notification->config('logicEnabled') && count($notification->config('logicRules'))) {
362:                 if ($form->checkLogicAction($notification->config('logicAction'), $notification->config('logicMatch'), $notification->config('logicRules'))) {
363:                     $notification->send();
364:                 }
365:             } else {
366:                 $notification->send();
367:             }
368:         }
369: 
370:         return $this;
371:     }
372: 
373:     374: 375: 376: 377: 378: 
379:     protected function saveEntry(Quform_Form $form)
380:     {
381:         if ( ! $this->options->get('saveEntries') || ! $form->config('saveEntry')) {
382:             return null;
383:         }
384: 
385:         $currentTime = Quform::date('Y-m-d H:i:s', null, new DateTimeZone('UTC'));
386: 
387:         $entry = array(
388:             'form_id'           => $form->getId(),
389:             'ip'                => $this->options->get('saveIpAddresses') ? Quform::substr(Quform::getClientIp(), 0, 45) : '',
390:             'form_url'          => isset($_POST['form_url']) ? Quform::substr($_POST['form_url'], 0, 512) : '',
391:             'referring_url'     => isset($_POST['referring_url']) ? Quform::substr($_POST['referring_url'], 0, 512) : '',
392:             'post_id'           => is_numeric($postId = Quform::get($_POST, 'post_id')) && $postId > 0 ? (int) $postId : null,
393:             'created_by'        => is_user_logged_in() ? (int) Quform::getUserProperty('ID') : null,
394:             'created_at'        => $currentTime,
395:             'updated_at'        => $currentTime,
396:         );
397: 
398:         $entry = $this->repository->saveEntry($entry);
399: 
400:         return $entry['id'];
401:     }
402: 
403:     404: 405: 406: 407: 408: 
409:     protected function saveEntryData($entryId, Quform_Form $form)
410:     {
411:         if ( ! ($entryId > 0) || ! $this->options->get('saveEntries') || ! $form->config('saveEntry')) {
412:             return;
413:         }
414: 
415:         $data = array();
416: 
417:         foreach ($form->getRecursiveIterator() as $element) {
418:             if ($element->config('saveToDatabase') && ! $element->isConditionallyHidden()) {
419:                 if ( ! $element->isEmpty()) {
420:                     $data[$element->getId()] = $element->getValueForStorage();
421:                 }
422:             }
423:         }
424: 
425:         if (count($data)) {
426:             $this->repository->saveEntryData($entryId, $data);
427:         }
428:     }
429: 
430:     431: 432: 433: 434: 
435:     protected function saveToCustomDatabase(Quform_Form $form)
436:     {
437:         if ( ! $form->config('databaseEnabled') ||
438:              ! count($columns = $form->config('databaseColumns')) ||
439:              ! Quform::isNonEmptyString($table = $form->config('databaseTable'))
440:         ) {
441:             return;
442:         }
443: 
444:         $data = array();
445:         foreach ($columns as $column) {
446:             if ( ! Quform::isNonEmptyString($column['name'])) {
447:                 continue;
448:             }
449: 
450:             $data[$column['name']] = $form->replaceVariables($column['value']);
451:         }
452: 
453:         if ($form->config('databaseWordpress')) {
454:             global $wpdb;
455:             $wpdb->insert($table, $data);
456:         } else {
457:             $customWpdb = new wpdb(
458:                 $form->config('databaseUsername'),
459:                 $form->config('databasePassword'),
460:                 $form->config('databaseDatabase'),
461:                 $form->config('databaseHost')
462:             );
463: 
464:             $customWpdb->insert($table, $data);
465:         }
466:     }
467: }
468: