· 6 years ago · May 17, 2019, 11:48 AM
1<?php
2
3/*
4Widget Name: Contact Form
5Description: A light weight contact form builder.
6Author: SiteOrigin
7Author URI: https://siteorigin.com
8Documentation: https://siteorigin.com/widgets-bundle/contact-form-widget/
9*/
10
11class SiteOrigin_Widgets_ContactForm_Widget extends SiteOrigin_Widget {
12
13 function __construct() {
14
15 parent::__construct(
16 'sow-contact-form',
17 __( 'SiteOrigin Contact Form', 'so-widgets-bundle' ),
18 array(
19 'description' => __( 'Create a simple contact form for your users to get hold of you.', 'so-widgets-bundle' ),
20 ),
21 array(),
22 false,
23 plugin_dir_path( __FILE__ )
24 );
25 }
26
27 /**
28 * Initialize the contact form widget
29 */
30 function initialize() {
31 $this->register_frontend_scripts(
32 array(
33 array(
34 'sow-contact',
35 plugin_dir_url( __FILE__ ) . 'js/contact' . SOW_BUNDLE_JS_SUFFIX . '.js',
36 array( 'jquery' ),
37 SOW_BUNDLE_VERSION
38 )
39 )
40 );
41 add_filter( 'siteorigin_widgets_sanitize_field_multiple_emails', array( $this, 'sanitize_multiple_emails' ) );
42 }
43
44 function get_widget_form() {
45 return array(
46 'title' => array(
47 'type' => 'text',
48 'label' => __( 'Title', 'so-widgets-bundle' ),
49 'default' => __( 'Contact Us', 'so-widgets-bundle' ),
50 ),
51
52 'display_title' => array(
53 'type' => 'checkbox',
54 'label' => __( 'Display title', 'so-widgets-bundle' ),
55 ),
56
57 'settings' => array(
58 'type' => 'section',
59 'label' => __( 'Settings', 'so-widgets-bundle' ),
60 'hide' => true,
61 'fields' => array(
62 'to' => array(
63 'type' => 'text',
64 'label' => __( 'To email address', 'so-widgets-bundle' ),
65 'description' => __( 'Where contact emails will be delivered to. You can send to multiple emails by separating the emails with a comma (,)', 'so-widgets-bundle' ),
66 'sanitize' => 'multiple_emails',
67 ),
68 'from' => array(
69 'type' => 'text',
70 'label' => __( 'From email address', 'so-widgets-bundle' ),
71 'description' => __( 'It will appear as if emails are sent from this address. Ideally this should be in the same domain as this server to avoid spam filters.', 'so-widgets-bundle' ),
72 'sanitize' => 'email',
73 ),
74 'default_subject' => array(
75 'type' => 'text',
76 'label' => __( 'Default subject', 'so-widgets-bundle' ),
77 'description' => __( "Subject to use when there isn't one available.", 'so-widgets-bundle' ),
78 ),
79 'subject_prefix' => array(
80 'type' => 'text',
81 'label' => __( 'Subject prefix', 'so-widgets-bundle' ),
82 'description' => __( 'Prefix added to all incoming email subjects.', 'so-widgets-bundle' ),
83 ),
84 'success_message' => array(
85 'type' => 'tinymce',
86 'label' => __( 'Success message', 'so-widgets-bundle' ),
87 'description' => __( 'Message to display after message successfully sent.', 'so-widgets-bundle' ),
88 'default' => __( "Thanks for contacting us. We'll get back to you shortly.", 'so-widgets-bundle' )
89 ),
90 'submit_text' => array(
91 'type' => 'text',
92 'label' => __( 'Submit button text', 'so-widgets-bundle' ),
93 'default' => __( "Contact Us", 'so-widgets-bundle' )
94 ),
95 'submit_id' => array(
96 'type' => 'text',
97 'label' => __( 'Button ID', 'so-widgets-bundle' ),
98 'description' => __( 'An ID attribute allows you to target this button in JavaScript.', 'so-widgets-bundle' ),
99 ),
100 'onclick' => array(
101 'type' => 'text',
102 'label' => __( 'Onclick', 'so-widgets-bundle' ),
103 'description' => __( 'Run this JavaScript when the button is clicked. Ideal for tracking.', 'so-widgets-bundle' ),
104 ),
105 'required_field_indicator' => array(
106 'type' => 'checkbox',
107 'label' => __( 'Indicate required fields with asterisk (*)', 'so-widgets-bundle' ),
108 'state_emitter' => array(
109 'callback' => 'conditional',
110 'args' => array(
111 'required_fields[show]: val',
112 'required_fields[hide]: ! val'
113 ),
114 )
115 ),
116 'required_field_indicator_message' => array(
117 'type' => 'text',
118 'label' => __( 'Required field indicator message', 'so-widgets-bundle' ),
119 'default' => __( 'Fields marked with * are required', 'so-widgets-bundle' ),
120 'state_handler' => array(
121 'required_fields[show]' => array( 'show' ),
122 'required_fields[hide]' => array( 'hide' ),
123 )
124 ),
125 'log_ip_address' => array(
126 'type' => 'checkbox',
127 'label' => __( 'Log IP addresses.', 'so-widgets-bundle' ),
128 'default' => false,
129 ),
130 ),
131 ),
132
133 'fields' => array(
134
135 'type' => 'repeater',
136 'label' => __( 'Fields', 'so-widgets-bundle' ),
137 'item_name' => __( 'Field', 'so-widgets-bundle' ),
138 'item_label' => array(
139 'selector' => "[id*='label']",
140 ),
141 'fields' => array(
142
143 'type' => array(
144 'type' => 'select',
145 'label' => __( 'Field Type', 'so-widgets-bundle' ),
146 'prompt' => __( 'Select Field Type', 'so-widgets-bundle' ),
147 'options' => array(
148 'name' => __( 'Name', 'so-widgets-bundle' ),
149 'email' => __( 'Email', 'so-widgets-bundle' ),
150 'tel' => __( 'Phone Number', 'so-widgets-bundle' ),
151 'subject' => __( 'Subject', 'so-widgets-bundle' ),
152 'text' => __( 'Text', 'so-widgets-bundle' ),
153 'textarea' => __( 'Text Area', 'so-widgets-bundle' ),
154 'select' => __( 'Dropdown Select', 'so-widgets-bundle' ),
155 'checkboxes' => __( 'Checkboxes', 'so-widgets-bundle' ),
156 'radio' => __( 'Radio', 'so-widgets-bundle' ),
157 ),
158 'state_emitter' => array(
159 'callback' => 'select',
160 'args' => array( 'field_type_{$repeater}' ),
161 )
162 ),
163
164 'label' => array(
165 'type' => 'text',
166 'label' => __( 'Label', 'so-widgets-bundle' ),
167 ),
168
169 'description' => array(
170 'type' => 'text',
171 'label' => __( 'Description', 'so-widgets-bundle' ),
172 'description' => __( 'This text will appear small beneath the input field.', 'so-widgets-bundle' ),
173 ),
174
175 'required' => array(
176 'type' => 'section',
177 'label' => __( 'Required Field', 'so-widgets-bundle' ),
178 'fields' => array(
179 'required' => array(
180 'type' => 'checkbox',
181 'label' => __( 'Required field', 'so-widgets-bundle' ),
182 'description' => __( 'Is this field required?', 'so-widgets-bundle' ),
183 ),
184 'missing_message' => array(
185 'type' => 'text',
186 'label' => __( 'Missing message', 'so-widgets-bundle' ),
187 'description' => __( 'Error message to display if this field is missing.', 'so-widgets-bundle' ),
188 )
189 )
190 ),
191
192 // This are for select, radio, and checkboxes
193 'options' => array(
194 'type' => 'repeater',
195 'label' => __( 'Options', 'so-widgets-bundle' ),
196 'item_name' => __( 'Option', 'so-widgets-bundle' ),
197 'item_label' => array( 'selector' => "[id*='value']" ),
198 'fields' => array(
199 'value' => array(
200 'type' => 'text',
201 'label' => __( 'Value', 'so-widgets-bundle' ),
202 ),
203 ),
204
205 // These are only required for a few states
206 'state_handler' => array(
207 'field_type_{$repeater}[select,checkboxes,radio]' => array( 'show' ),
208 '_else[field_type_{$repeater}]' => array( 'hide' ),
209 ),
210 ),
211 ),
212 ),
213
214 'spam' => array(
215 'type' => 'section',
216 'label' => __( 'Spam Protection', 'so-widgets-bundle' ),
217 'hide' => true,
218 'fields' => array(
219
220 'recaptcha' => array(
221 'type' => 'section',
222 'label' => __( 'reCAPTCHA v2', 'so-widgets-bundle' ),
223 'fields' => array(
224 'use_captcha' => array(
225 'type' => 'checkbox',
226 'label' => __( 'Use reCAPTCHA v2', 'so-widgets-bundle' ),
227 'default' => false,
228 'description' => sprintf(
229 __( 'Please make sure you register a new reCAPTCHA v2 key %shere%s.', 'so-widgets-bundle' ),
230 '<a href="https://www.google.com/recaptcha/admin/create" target="_blank" rel="noopener noreferrer">',
231 '</a>'
232 ),
233 'state_emitter' => array(
234 'callback' => 'conditional',
235 'args' => array(
236 'use_captcha[show]: val',
237 'use_captcha[hide]: ! val',
238 ),
239 ),
240 ),
241 'site_key' => array(
242 'type' => 'text',
243 'label' => __( 'reCAPTCHA v2 Site Key', 'so-widgets-bundle' ),
244 'state_handler' => array(
245 'use_captcha[show]' => array( 'slideDown' ),
246 'use_captcha[hide]' => array( 'slideUp' ),
247 ),
248 ),
249 'secret_key' => array(
250 'type' => 'text',
251 'label' => __( 'reCAPTCHA v2 Secret Key', 'so-widgets-bundle' ),
252 'state_handler' => array(
253 'use_captcha[show]' => array( 'slideDown' ),
254 'use_captcha[hide]' => array( 'slideUp' ),
255 ),
256 ),
257 'theme' => array(
258 'type' => 'select',
259 'label' => __( 'Theme', 'so-widgets-bundle' ),
260 'default' => 'light',
261 'options' => array(
262 'light' => __( 'Light', 'so-widgets-bundle' ),
263 'dark' => __( 'Dark', 'so-widgets-bundle' ),
264 ),
265 'state_handler' => array(
266 'use_captcha[show]' => array( 'slideDown' ),
267 'use_captcha[hide]' => array( 'slideUp' ),
268 ),
269 ),
270 'type' => array(
271 'type' => 'select',
272 'label' => __( 'Challenge type', 'so-widgets-bundle' ),
273 'default' => 'image',
274 'options' => array(
275 'image' => __( 'Image', 'so-widgets-bundle' ),
276 'audio' => __( 'Audio', 'so-widgets-bundle' ),
277 ),
278 'state_handler' => array(
279 'use_captcha[show]' => array( 'slideDown' ),
280 'use_captcha[hide]' => array( 'slideUp' ),
281 ),
282 ),
283 'size' => array(
284 'type' => 'select',
285 'label' => __( 'Size', 'so-widgets-bundle' ),
286 'default' => 'normal',
287 'options' => array(
288 'normal' => __( 'Normal', 'so-widgets-bundle' ),
289 'compact' => __( 'Compact', 'so-widgets-bundle' ),
290 ),
291 'state_handler' => array(
292 'use_captcha[show]' => array( 'slideDown' ),
293 'use_captcha[hide]' => array( 'slideUp' ),
294 ),
295 ),
296 )
297 ),
298
299 'akismet' => array(
300 'type' => 'section',
301 'label' => __( 'Akismet', 'so-widgets-bundle' ),
302 'fields' => array(
303 'use_akismet' => array(
304 'type' => 'checkbox',
305 'label' => __( 'Use Akismet filtering', 'so-widgets-bundle' ),
306 'default' => true,
307 ),
308 'spam_action' => array(
309 'type' => 'select',
310 'label' => __( 'Spam action', 'so-widgets-bundle' ),
311 'options' => array(
312 'error' => __( 'Show error message', 'so-widgets-bundle' ),
313 'tag' => __( 'Tag as spam in subject', 'so-widgets-bundle' ),
314 ),
315 'description' => __( 'How to handle submissions that are identified as spam.', 'so-widgets-bundle' ),
316 'default' => 'error',
317 ),
318 )
319 ),
320 ),
321 ),
322
323 'design' => array(
324 'type' => 'section',
325 'label' => __( 'Design', 'so-widgets-bundle' ),
326 'hide' => true,
327 'fields' => array(
328
329 'container' => array(
330 'type' => 'section',
331 'label' => __( 'Container', 'so-widgets-bundle' ),
332 'fields' => array(
333 'background' => array(
334 'type' => 'color',
335 'label' => __( 'Background color', 'so-widgets-bundle' ),
336 'default' => '#f2f2f2',
337 ),
338 'padding' => array(
339 'type' => 'measurement',
340 'label' => __( 'Padding', 'so-widgets-bundle' ),
341 'default' => '10px',
342 ),
343 'border_color' => array(
344 'type' => 'color',
345 'label' => __( 'Border color', 'so-widgets-bundle' ),
346 'default' => '#c0c0c0',
347 ),
348 'border_width' => array(
349 'type' => 'measurement',
350 'label' => __( 'Border width', 'so-widgets-bundle' ),
351 'default' => '1px',
352 ),
353 'border_style' => array(
354 'type' => 'select',
355 'label' => __( 'Border style', 'so-widgets-bundle' ),
356 'default' => 'solid',
357 'options' => array(
358 'none' => __( 'None', 'so-widgets-bundle' ),
359 'hidden' => __( 'Hidden', 'so-widgets-bundle' ),
360 'dotted' => __( 'Dotted', 'so-widgets-bundle' ),
361 'dashed' => __( 'Dashed', 'so-widgets-bundle' ),
362 'solid' => __( 'Solid', 'so-widgets-bundle' ),
363 'double' => __( 'Double', 'so-widgets-bundle' ),
364 'groove' => __( 'Groove', 'so-widgets-bundle' ),
365 'ridge' => __( 'Ridge', 'so-widgets-bundle' ),
366 'inset' => __( 'Inset', 'so-widgets-bundle' ),
367 'outset' => __( 'Outset', 'so-widgets-bundle' ),
368 )
369 ),
370 )
371 ),
372
373 'labels' => array(
374 'type' => 'section',
375 'label' => __( 'Field labels', 'so-widgets-bundle' ),
376 'fields' => array(
377 'font' => array(
378 'type' => 'font',
379 'label' => __( 'Font', 'so-widgets-bundle' ),
380 'default' => 'default',
381 ),
382 'size' => array(
383 'type' => 'measurement',
384 'label' => __( 'Font size', 'so-widgets-bundle' ),
385 'default' => 'default',
386 ),
387 'color' => array(
388 'type' => 'color',
389 'label' => __( 'Color', 'so-widgets-bundle' ),
390 'default' => 'default',
391 ),
392 'position' => array(
393 'type' => 'select',
394 'label' => __( 'Position', 'so-widgets-bundle' ),
395 'default' => 'above',
396 'options' => array(
397 'above' => __( 'Above', 'so-widgets-bundle' ),
398 'below' => __( 'Below', 'so-widgets-bundle' ),
399 'left' => __( 'Left', 'so-widgets-bundle' ),
400 'right' => __( 'Right', 'so-widgets-bundle' ),
401 'inside' => __( 'Inside', 'so-widgets-bundle' ),
402 ),
403 ),
404 'width' => array(
405 'type' => 'measurement',
406 'label' => __( 'Width', 'so-widgets-bundle' ),
407 'default' => '',
408 ),
409 'align' => array(
410 'type' => 'select',
411 'label' => __( 'Align', 'so-widgets-bundle' ),
412 'default' => 'left',
413 'options' => array(
414 'left' => __( 'Left', 'so-widgets-bundle' ),
415 'right' => __( 'Right', 'so-widgets-bundle' ),
416 'center' => __( 'Center', 'so-widgets-bundle' ),
417 'justify' => __( 'Justify', 'so-widgets-bundle' ),
418 )
419 ),
420 ),
421 ),
422
423 'fields' => array(
424 'type' => 'section',
425 'label' => __( 'Fields', 'so-widgets-bundle' ),
426 'fields' => array(
427 'font' => array(
428 'type' => 'font',
429 'label' => __( 'Font', 'so-widgets-bundle' ),
430 'default' => 'default',
431 ),
432 'font_size' => array(
433 'type' => 'measurement',
434 'label' => __( 'Font Size', 'so-widgets-bundle' )
435 ),
436 'color' => array(
437 'type' => 'color',
438 'label' => __( 'Text Color', 'so-widgets-bundle' ),
439 ),
440 'margin' => array(
441 'type' => 'measurement',
442 'label' => __( 'Margin', 'so-widgets-bundle' )
443 ),
444 'padding' => array(
445 'type' => 'measurement',
446 'label' => __( 'Padding', 'so-widgets-bundle' )
447 ),
448 'height' => array(
449 'type' => 'measurement',
450 'label' => __( 'Height', 'so-widgets-bundle' )
451 ),
452 'height_textarea' => array(
453 'type' => 'measurement',
454 'label' => __( 'Text Area Height', 'so-widgets-bundle' )
455 ),
456 'background' => array(
457 'type' => 'color',
458 'label' => __( 'Background', 'so-widgets-bundle' ),
459 ),
460 'border_color' => array(
461 'type' => 'color',
462 'label' => __( 'Border color', 'so-widgets-bundle' ),
463 'default' => '#c0c0c0',
464 ),
465 'border_width' => array(
466 'type' => 'measurement',
467 'label' => __( 'Border width', 'so-widgets-bundle' ),
468 'default' => '1px',
469 ),
470 'border_style' => array(
471 'type' => 'select',
472 'label' => __( ' Border style', 'so-widgets-bundle' ),
473 'default' => 'solid',
474 'options' => array(
475 'none' => __( 'None', 'so-widgets-bundle' ),
476 'hidden' => __( 'Hidden', 'so-widgets-bundle' ),
477 'dotted' => __( 'Dotted', 'so-widgets-bundle' ),
478 'dashed' => __( 'Dashed', 'so-widgets-bundle' ),
479 'solid' => __( 'Solid', 'so-widgets-bundle' ),
480 'double' => __( 'Double', 'so-widgets-bundle' ),
481 'groove' => __( 'Groove', 'so-widgets-bundle' ),
482 'ridge' => __( 'Ridge', 'so-widgets-bundle' ),
483 'inset' => __( 'Inset', 'so-widgets-bundle' ),
484 'outset' => __( 'Outset', 'so-widgets-bundle' ),
485 )
486 ),
487 'border_radius' => array(
488 'type' => 'slider',
489 'label' => __( 'Border rounding', 'so-widgets-bundle' ),
490 'default' => 0,
491 'max' => 50,
492 'min' => 0
493 ),
494 )
495 ),
496
497 'descriptions' => array(
498 'type' => 'section',
499 'label' => __( 'Field descriptions', 'so-widgets-bundle' ),
500 'fields' => array(
501 'size' => array(
502 'type' => 'measurement',
503 'label' => __( 'Size', 'so-widgets-bundle' ),
504 'default' => '0.9em',
505 ),
506 'color' => array(
507 'type' => 'color',
508 'label' => __( 'Color', 'so-widgets-bundle' ),
509 'default' => '#999999',
510 ),
511 'style' => array(
512 'type' => 'select',
513 'label' => __( 'Style', 'so-widgets-bundle' ),
514 'default' => 'italic',
515 'options' => array(
516 'italic' => __( 'Italic', 'so-widgets-bundle' ),
517 'normal' => __( 'Normal', 'so-widgets-bundle' ),
518 )
519 ),
520 )
521 ),
522
523 'errors' => array(
524 'type' => 'section',
525 'label' => __( 'Error messages', 'so-widgets-bundle' ),
526 'fields' => array(
527 'background' => array(
528 'type' => 'color',
529 'label' => __( 'Error background color', 'so-widgets-bundle' ),
530 'default' => '#fce4e5',
531 ),
532 'border_color' => array(
533 'type' => 'color',
534 'label' => __( 'Error border color', 'so-widgets-bundle' ),
535 'default' => '#ec666a',
536 ),
537 'text_color' => array(
538 'type' => 'color',
539 'label' => __( 'Error text color', 'so-widgets-bundle' ),
540 'default' => '#ec666a',
541 ),
542 'padding' => array(
543 'type' => 'measurement',
544 'label' => __( 'Error padding', 'so-widgets-bundle' ),
545 'default' => '5px',
546 ),
547 'margin' => array(
548 'type' => 'measurement',
549 'label' => __( 'Error margin', 'so-widgets-bundle' ),
550 'default' => '10px',
551 ),
552 )
553 ),
554
555 'submit' => array(
556 'type' => 'section',
557 'label' => __( 'Submit button', 'so-widgets-bundle' ),
558 'fields' => array(
559 'styled' => array(
560 'type' => 'checkbox',
561 'label' => __( 'Style submit button', 'so-widgets-bundle' ),
562 'description' => __( 'Style the button or leave it with default theme styling.', 'so-widgets-bundle' ),
563 'default' => true,
564 ),
565
566 'background_color' => array(
567 'type' => 'color',
568 'label' => __( 'Background color', 'so-widgets-bundle' ),
569 'default' => '#eeeeee',
570 ),
571 'background_gradient' => array(
572 'type' => 'slider',
573 'label' => __( 'Gradient intensity', 'so-widgets-bundle' ),
574 'default' => 10,
575 ),
576 'border_color' => array(
577 'type' => 'color',
578 'label' => __( 'Border color', 'so-widgets-bundle' ),
579 'default' => '#989a9c',
580 ),
581 'border_style' => array(
582 'type' => 'select',
583 'label' => __( 'Border style', 'so-widgets-bundle' ),
584 'default' => 'solid',
585 'options' => array(
586 'none' => __( 'None', 'so-widgets-bundle' ),
587 'solid' => __( 'Solid', 'so-widgets-bundle' ),
588 'dotted' => __( 'Dotted', 'so-widgets-bundle' ),
589 'dashed' => __( 'Dashed', 'so-widgets-bundle' ),
590 )
591 ),
592 'border_width' => array(
593 'type' => 'measurement',
594 'label' => __( 'Border width', 'so-widgets-bundle' ),
595 'default' => '1px',
596 ),
597 'border_radius' => array(
598 'type' => 'slider',
599 'label' => __( 'Border rounding', 'so-widgets-bundle' ),
600 'default' => 3,
601 'max' => 50,
602 'min' => 0
603 ),
604 'text_color' => array(
605 'type' => 'color',
606 'label' => __( 'Text color', 'so-widgets-bundle' ),
607 'default' => '#5a5a5a',
608 ),
609 'font_size' => array(
610 'type' => 'measurement',
611 'label' => __( 'Font size', 'so-widgets-bundle' ),
612 'default' => 'default',
613 ),
614 'weight' => array(
615 'type' => 'select',
616 'label' => __( 'Font weight', 'so-widgets-bundle' ),
617 'default' => '500',
618 'options' => array(
619 'normal' => __( 'Normal', 'so-widgets-bundle' ),
620 '500' => __( 'Semi-bold', 'so-widgets-bundle' ),
621 'bold' => __( 'Bold', 'so-widgets-bundle' ),
622 )
623 ),
624 'padding' => array(
625 'type' => 'measurement',
626 'label' => __( 'Padding', 'so-widgets-bundle' ),
627 'default' => '10px',
628 ),
629 'width' => array(
630 'type' => 'measurement',
631 'label' => __( 'Width', 'so-widgets-bundle' ),
632 ),
633 'align' => array(
634 'type' => 'select',
635 'label' => __( 'Align', 'so-widgets-bundle' ),
636 'default' => 'left',
637 'options' => array(
638 'left' => __( 'Left', 'so-widgets-bundle' ),
639 'right' => __( 'Right', 'so-widgets-bundle' ),
640 'center' => __( 'Center', 'so-widgets-bundle' ),
641 )
642 ),
643 'inset_highlight' => array(
644 'type' => 'slider',
645 'label' => __( 'Inset highlight', 'so-widgets-bundle' ),
646 'description' => __( 'The white highlight at the bottom of the button', 'so-widgets-bundle' ),
647 'default' => 50,
648 'max' => 100,
649 'min' => 0
650 ),
651 )
652 ),
653
654 'focus' => array(
655 'type' => 'section',
656 'label' => __( 'Input focus', 'so-widgets-bundle' ),
657 'fields' => array(
658 'style' => array(
659 'type' => 'select',
660 'label' => __( 'Style', 'so-widgets-bundle' ),
661 'default' => 'solid',
662 'options' => array(
663 'dotted' => __( 'Dotted', 'so-widgets-bundle' ),
664 'dashed' => __( 'Dashed', 'so-widgets-bundle' ),
665 'solid' => __( 'Solid', 'so-widgets-bundle' ),
666 'double' => __( 'Double', 'so-widgets-bundle' ),
667 'groove' => __( 'Groove', 'so-widgets-bundle' ),
668 'ridge' => __( 'Ridge', 'so-widgets-bundle' ),
669 'inset' => __( 'Inset', 'so-widgets-bundle' ),
670 'outset' => __( 'Outset', 'so-widgets-bundle' ),
671 'none' => __( 'None', 'so-widgets-bundle' ),
672 'hidden' => __( 'Hidden', 'so-widgets-bundle' ),
673 )
674 ),
675 'color' => array(
676 'type' => 'color',
677 'label' => __( 'Color', 'so-widgets-bundle' ),
678 'default' => 'default',
679 ),
680 'width' => array(
681 'type' => 'measurement',
682 'label' => __( 'Width', 'so-widgets-bundle' ),
683 'default' => '1px',
684 ),
685 ),
686 ),
687 ),
688 ),
689 );
690 }
691
692 function get_form_teaser() {
693 if ( ! $this->display_siteorigin_premium_teaser() ) {
694 return false;
695 }
696
697 $url = add_query_arg( array(
698 'featured_addon' => 'plugin/contact-form-fields',
699 'featured_plugin' => 'widgets-bundle'
700 ), 'https://siteorigin.com/downloads/premium/' );
701
702 return sprintf(
703 __( 'Get more form fields for the Contact Form Widget in %s', 'so-widgets-bundle' ),
704 '<a href="' . esc_url( $url ) . '" target="_blank" rel="noopener noreferrer">' . __( 'SiteOrigin Premium', 'so-widgets-bundle' ) . '</a>'
705 );
706 }
707
708 function sanitize_multiple_emails( $value ) {
709 $values = explode( ',', $value );
710 foreach ( $values as $i => $email ) {
711 $values[ $i ] = sanitize_email( $email );
712 }
713
714 return implode( ',', $values );
715 }
716
717 function modify_instance( $instance ) {
718 // Use this to set up an initial version of the
719 if ( empty( $instance['settings']['to'] ) ) {
720 $current_user = wp_get_current_user();
721 $instance['settings']['to'] = $current_user->user_email;
722 }
723 if ( empty( $instance['settings']['from'] ) ) {
724 $instance['settings']['from'] = get_option( 'admin_email' );
725 }
726 if ( empty( $instance['fields'] ) ) {
727 $instance['fields'] = array(
728 array(
729 'type' => 'name',
730 'label' => __( 'Your Name', 'so-widgets-bundle' ),
731 'required' => array(
732 'required' => true,
733 'missing_message' => __( 'Please enter your name', 'so-widgets-bundle' ),
734 ),
735 ),
736 array(
737 'type' => 'email',
738 'label' => __( 'Your Email', 'so-widgets-bundle' ),
739 'required' => array(
740 'required' => true,
741 'missing_message' => __( 'Please enter a valid email address', 'so-widgets-bundle' ),
742 ),
743 ),
744 array(
745 'type' => 'subject',
746 'label' => __( 'Subject', 'so-widgets-bundle' ),
747 'required' => array(
748 'required' => true,
749 'missing_message' => __( 'Please enter a subject', 'so-widgets-bundle' ),
750 ),
751 ),
752 array(
753 'type' => 'textarea',
754 'label' => __( 'Message', 'so-widgets-bundle' ),
755 'required' => array(
756 'required' => true,
757 'missing_message' => __( 'Please write something', 'so-widgets-bundle' ),
758 ),
759 ),
760 );
761 }
762
763 return $instance;
764 }
765
766 function get_template_variables( $instance, $args ) {
767 unset( $instance['title'] );
768 unset( $instance['display_title'] );
769 unset( $instance['design'] );
770 unset( $instance['panels_info'] );
771
772 // Include '_sow_form_id' in generation of 'instance_hash' to allow multiple instances of the same form on a page.
773 $instance_hash = md5( serialize( $instance ) );
774 unset( $instance['_sow_form_id'] );
775
776 $submit_attributes = array();
777 if ( ! empty( $instance['settings']['submit_id'] ) ) {
778 $submit_attributes['id'] = $instance['settings']['submit_id'];
779 }
780
781 return array(
782 'instance_hash' => $instance_hash,
783 'submit_attributes' => $submit_attributes,
784 'onclick' => ! empty( $instance['settings']['onclick'] ) ? $instance['settings']['onclick'] : '',
785 );
786 }
787
788 function get_less_variables( $instance ) {
789 if ( empty( $instance['design'] ) ) {
790 return;
791 }
792 if ( empty( $instance['design']['labels']['font'] ) ) {
793 $instance['design']['labels'] = array( 'font' => '' );
794 }
795 $label_font = siteorigin_widget_get_font( $instance['design']['labels']['font'] );
796 $field_font = siteorigin_widget_get_font( $instance['design']['fields']['font'] );
797
798 $label_position = $instance['design']['labels']['position'];
799 if ( $label_position != 'left' && $label_position != 'right' ) {
800 $label_position = 'default';
801 }
802
803 $vars = array(
804 // All the container variables.
805 'container_background' => $instance['design']['container']['background'],
806 'container_padding' => $instance['design']['container']['padding'],
807 'container_border_color' => $instance['design']['container']['border_color'],
808 'container_border_width' => $instance['design']['container']['border_width'],
809 'container_border_style' => $instance['design']['container']['border_style'],
810
811 // Field labels
812 'label_font_family' => $label_font['family'],
813 'label_font_weight' => ! empty( $label_font['weight'] ) ? $label_font['weight'] : '',
814 'label_font_size' => $instance['design']['labels']['size'],
815 'label_font_color' => $instance['design']['labels']['color'],
816 'label_position' => $label_position,
817 'label_width' => $instance['design']['labels']['width'],
818 'label_align' => $instance['design']['labels']['align'],
819
820 // Fields
821 'field_font_family' => $field_font['family'],
822 'field_font_weight' => ! empty( $field_font['weight'] ) ? $field_font['weight'] : '',
823 'field_font_size' => $instance['design']['fields']['font_size'],
824 'field_font_color' => $instance['design']['fields']['color'],
825 'field_margin' => $instance['design']['fields']['margin'],
826 'field_padding' => $instance['design']['fields']['padding'],
827 'field_height' => $instance['design']['fields']['height'],
828 'field_height_textarea' => ! empty( $instance['design']['fields']['height_textarea'] ) ? $instance['design']['fields']['height_textarea'] : '',
829 'field_background' => $instance['design']['fields']['background'],
830 'field_border_color' => $instance['design']['fields']['border_color'],
831 'field_border_width' => $instance['design']['fields']['border_width'],
832 'field_border_style' => $instance['design']['fields']['border_style'],
833 'field_border_radius' => $instance['design']['fields']['border_radius'] . 'px',
834
835 // Field descriptions
836 'description_font_size' => $instance['design']['descriptions']['size'],
837 'description_font_color' => $instance['design']['descriptions']['color'],
838 'description_font_style' => $instance['design']['descriptions']['style'],
839
840 // The error message styles
841 'error_background' => $instance['design']['errors']['background'],
842 'error_border' => $instance['design']['errors']['border_color'],
843 'error_text' => $instance['design']['errors']['text_color'],
844 'error_padding' => $instance['design']['errors']['padding'],
845 'error_margin' => $instance['design']['errors']['margin'],
846
847 // The submit button
848 'submit_background_color' => $instance['design']['submit']['background_color'],
849 'submit_background_gradient' => $instance['design']['submit']['background_gradient'] . '%',
850 'submit_border_color' => $instance['design']['submit']['border_color'],
851 'submit_border_style' => $instance['design']['submit']['border_style'],
852 'submit_border_width' => $instance['design']['submit']['border_width'],
853 'submit_border_radius' => $instance['design']['submit']['border_radius'] . 'px',
854 'submit_text_color' => $instance['design']['submit']['text_color'],
855 'submit_font_size' => $instance['design']['submit']['font_size'],
856 'submit_weight' => $instance['design']['submit']['weight'],
857 'submit_padding' => $instance['design']['submit']['padding'],
858 'submit_width' => ! empty( $instance['design']['submit']['width'] ) ? $instance['design']['submit']['width'] : '',
859 'submit_align' => ! empty( $instance['design']['submit']['align'] ) ? $instance['design']['submit']['align'] : '',
860 'submit_inset_highlight' => $instance['design']['submit']['inset_highlight'] . '%',
861
862 // Input focus styles
863 'outline_style' => $instance['design']['focus']['style'],
864 'outline_color' => $instance['design']['focus']['color'],
865 'outline_width' => $instance['design']['focus']['width'],
866 );
867
868 return $vars;
869 }
870
871 function get_google_font_fields( $instance ) {
872 return array(
873 $instance['design']['labels']['font'],
874 $instance['design']['fields']['font'],
875 );
876 }
877
878 static function name_from_label( $label, & $ids ) {
879 $it = 0;
880
881 $label = str_replace( ' ', '-', strtolower( $label ) );
882 $label = sanitize_html_class( $label );
883 do {
884 $id = $label . ( $it > 0 ? '-' . $it : '' );
885 $it ++;
886 } while ( ! empty( $ids[ $id ] ) );
887 $ids[ $id ] = true;
888
889 return $id;
890 }
891
892 /**
893 * Render the form fields
894 *
895 * @param $fields
896 * @param array $errors
897 * @param $instance
898 */
899 function render_form_fields( $fields, $errors = array(), $instance ) {
900
901 $field_ids = array();
902 $label_position = $instance['design']['labels']['position'];
903
904 $indicate_required_fields = $instance['settings']['required_field_indicator'];
905
906 if ( ! empty( $indicate_required_fields ) ) {
907 ?>
908 <p><em><?php echo esc_html( $instance['settings']['required_field_indicator_message'] ) ?></em></p>
909 <?php
910 }
911
912 foreach ( $fields as $i => $field ) {
913 if ( empty( $field['type'] ) ) {
914 continue;
915 }
916 // Using `$instance['_sow_form_id']` to uniquely identify contact form fields across widgets.
917 // I.e. if there are many contact form widgets on a page this will prevent field name conflicts.
918 $field_name = $this->name_from_label( ! empty( $field['label'] ) ? $field['label'] : $i, $field_ids ) . '-' . $instance['_sow_form_id'];
919 $field_id = 'sow-contact-form-field-' . $field_name;
920
921 $value = '';
922 if ( ! empty( $_POST[ $field_name ] ) && wp_verify_nonce( $_POST['_wpnonce'], '_contact_form_submit' ) ) {
923 $value = stripslashes_deep( $_POST[ $field_name ] );
924 }
925
926 ?>
927 <div class="sow-form-field sow-form-field-<?php echo sanitize_html_class( $field['type'] ) ?>"><?php
928
929 $label = $field['label'];
930 if ( $indicate_required_fields && ! empty( $field['required']['required'] ) ) {
931 $label .= '*';
932 }
933 $is_text_input_field = ( $field['type'] != 'select' && $field['type'] != 'radio' && $field['type'] != 'checkboxes' );
934 // label should be rendered before the field, then CSS will do the exact positioning.
935 $render_label_before_field = ( $label_position != 'below' && $label_position != 'inside' ) || ( $label_position == 'inside' && ! $is_text_input_field );
936 if ( empty( $label_position ) || $render_label_before_field ) {
937 $this->render_form_label( $field_id, $label, $label_position );
938 }
939
940 $show_placeholder = $label_position == 'inside';
941
942 if ( ! empty( $errors[ $field_name ] ) ) {
943 ?>
944 <div class="sow-error">
945 <?php echo wp_kses_post( $errors[ $field_name ] ) ?>
946 </div>
947 <?php
948 }
949 ?><span class="sow-field-container"><?php
950 $class_name = empty( $field['type'] ) ? '' : 'SiteOrigin_Widget_ContactForm_Field_' . ucwords( $field['type'] );
951 // This does autoloading if required.
952 if ( class_exists( $class_name ) ) {
953 /**
954 * @var $contact_field SiteOrigin_Widget_ContactForm_Field_Base
955 */
956 $field_input_options = array(
957 'field' => $field,
958 'field_id' => $field_id,
959 'field_name' => $field_name,
960 'value' => $value,
961 'show_placeholder' => $show_placeholder,
962 'label' => $label,
963 );
964 $contact_field = new $class_name( $field_input_options );
965 $contact_field->render();
966 } else {
967 echo '<input type="text" name="' . esc_attr( $field_name ) . '" id="' . esc_attr( $field_id ) . '" value="' . esc_attr( $value ) . '" class="sow-text-field" ' . ( $show_placeholder ? 'placeholder="' . esc_attr( $label ) . '"' : '' ) . '/>';
968 }
969 ?></span><?php
970
971 if ( ! empty( $label_position ) && $label_position == 'below' ) {
972 $this->render_form_label( $field_id, $label, $instance );
973 }
974
975 if ( ! empty( $field['description'] ) ) {
976 ?>
977 <div class="sow-form-field-description">
978 <?php echo wp_kses_post( $field['description'] ) ?>
979 </div>
980 <?php
981 }
982
983 ?></div><?php
984 }
985 }
986
987 function render_form_label( $field_id, $label, $position ) {
988 if ( ! empty( $label ) ) {
989 $label_class = '';
990 if ( ! empty( $position ) ) {
991 $label_class = ' class="sow-form-field-label-' . $position . '"';
992 }
993 ?><label<?php if ( ! empty( $label_class ) ) {
994 echo $label_class;
995 } ?> for="<?php echo esc_attr( $field_id ) ?>"><strong><?php echo esc_html( $label ) ?></strong></label>
996 <?php
997 }
998 }
999
1000 /**
1001 * Ajax action handler to send the form
1002 */
1003 function contact_form_action( $instance, $storage_hash ) {
1004 if ( empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], '_contact_form_submit' ) ) {
1005 // Using `return false;` instead of `wp_die` because this function may sometimes be called as a side effect
1006 // of trying to enqueue scripts required for the front end or when previewing widgets e.g. in the block editor.
1007 // In those cases `$_POST['_wpnonce']` doesn't exist and calling `wp_die` will halt script execution and break things.
1008 return false;
1009 }
1010 if ( empty( $_POST['instance_hash'] ) || $_POST['instance_hash'] != $storage_hash ) {
1011 return false;
1012 }
1013 if ( empty( $instance['fields'] ) ) {
1014 array(
1015 'status' => null,
1016 );
1017 }
1018
1019 // Make sure that this action only runs once per instance
1020 static $send_cache = array();
1021 $send_cache_hash = md5( serialize( $instance ) . '::' . $storage_hash );
1022 if ( isset( $send_cache[ $send_cache_hash ] ) ) {
1023 return $send_cache[ $send_cache_hash ];
1024 }
1025
1026 $errors = array();
1027 $email_fields = array();
1028 $post_vars = stripslashes_deep( $_POST );
1029
1030 $field_ids = array();
1031 foreach ( $instance['fields'] as $i => $field ) {
1032 if ( empty( $field['type'] ) ) {
1033 continue;
1034 }
1035 $field_name = $this->name_from_label( ! empty( $field['label'] ) ? $field['label'] : $i, $field_ids ) . '-' . $instance['_sow_form_id'];
1036 $value = isset( $post_vars[ $field_name ] ) ? $post_vars[ $field_name ] : '';
1037
1038 // Can't just use `strlen` here as $value could be an array. E.g. for checkboxes field.
1039 if ( empty( $value ) && $value !== '0' ) {
1040 if ( $field['required']['required'] ) {
1041 // Add in the default subject
1042 if ( $field['type'] == 'subject' && ! empty( $instance['settings']['default_subject'] ) ) {
1043 $value = $instance['settings']['default_subject'];
1044 } else {
1045 $errors[ $field_name ] = ! empty( $field['required']['missing_message'] ) ? $field['required']['missing_message'] : __( 'Required field', 'so-widgets-bundle' );
1046 continue;
1047 }
1048 } else {
1049 continue; // Don't process an empty field that's not required
1050 }
1051 }
1052
1053 // Type Validation
1054 switch ( $field['type'] ) {
1055 case 'email':
1056 if ( $value != sanitize_email( $value ) ) {
1057 $errors[ $field_name ] = __( 'Invalid email address.', 'so-widgets-bundle' );
1058 }
1059 $email_fields[ $field['type'] ] = $value;
1060
1061 break;
1062
1063 case 'name':
1064 case 'subject':
1065 $email_fields[ $field['type'] ] = $value;
1066
1067 break;
1068
1069 case 'checkboxes':
1070 $email_fields['message'][] = array(
1071 'label' => $field['label'],
1072 'value' => implode( ', ', $value ),
1073 );
1074 break;
1075
1076 default:
1077 $email_fields['message'][] = array(
1078 'label' => $field['label'],
1079 'value' => $value,
1080 );
1081 break;
1082 }
1083 }
1084
1085 // Add in a default email address if no email field is defined in the form at all.
1086 if ( ! isset( $email_fields['email'] ) && ! empty( $instance['settings']['from'] ) ) {
1087 $email_fields['email'] = $instance['settings']['from'];
1088 }
1089
1090 // Add in the default subject if no subject field is defined in the form at all.
1091 if ( ! isset( $email_fields['subject'] ) && ! empty( $instance['settings']['default_subject'] ) ) {
1092 $email_fields['subject'] = $instance['settings']['default_subject'];
1093 }
1094
1095 // Add in the default subject prefix
1096 if ( ! empty( $email_fields['subject'] ) && ! empty( $instance['settings']['subject_prefix'] ) ) {
1097 $email_fields['subject'] = $instance['settings']['subject_prefix'] . ' ' . $email_fields['subject'];
1098 }
1099
1100 // Now we do some email message validation
1101 if ( empty( $errors ) ) {
1102 $email_errors = $this->validate_mail( $email_fields );
1103 // Missing subject input and no default subject set. Revert to using a generic default 'SiteName Contact Form'
1104 if ( ! isset( $email_fields['subject'] ) && ! empty( $email_errors['subject'] ) ) {
1105 unset( $email_errors['subject'] );
1106 $email_fields['subject'] = get_bloginfo() . ' ' . __( 'Contact Form', 'siteorigin-widgets' );
1107 }
1108 if ( ! empty( $email_errors ) ) {
1109 $errors['_general'] = $email_errors;
1110 }
1111 }
1112
1113 // And if we get this far, do some spam filtering and Captcha checking
1114 if ( empty( $errors ) ) {
1115 $spam_errors = $this->spam_check( $post_vars, $email_fields, $instance );
1116 if ( ! empty( $spam_errors ) ) {
1117 // Now we can decide how we want to handle this spam status
1118 if ( ! empty( $spam_errors['akismet'] ) && $instance['spam']['akismet']['spam_action'] == 'tag' ) {
1119 unset( $spam_errors['akismet'] );
1120 $email_fields['subject'] = '[spam] ' . $email_fields['subject'];
1121 }
1122 }
1123
1124 if ( ! empty( $spam_errors ) ) {
1125 $errors['_general'] = $spam_errors;
1126 }
1127 }
1128
1129 if ( empty( $errors ) ) {
1130 // We can send the email
1131 $success = $this->send_mail( $email_fields, $instance );
1132
1133 if ( is_wp_error( $success ) ) {
1134 $errors['_general'] = array( 'send' => $success->get_error_message() );
1135 } else if ( empty( $success ) ) {
1136 $errors['_general'] = array( 'send' => __( 'BÅÄ„D. W trakcie wysyÅ‚ania wiadomoÅ›ci napotkaliÅ›my problem. Prosimy spróbować ponownie.', 'so-widgets-bundle' ) );
1137 } else {
1138 // This action will allow other plugins to run code when contact form has successfully been sent
1139 do_action( 'siteorigin_widgets_contact_sent', $instance, $email_fields );
1140 }
1141 }
1142
1143 if ( ! empty( $errors ) ) {
1144 // This action will allow other plugins to run code when the contact form submission has resulted in error
1145 do_action( 'siteorigin_widgets_contact_error', $instance, $email_fields, $errors );
1146 }
1147
1148 $send_cache[ $send_cache_hash ] = array(
1149 'status' => empty( $errors ) ? 'success' : 'fail',
1150 'errors' => $errors
1151 );
1152
1153 return $send_cache[ $send_cache_hash ];
1154 }
1155
1156 /**
1157 * Validate fields of an email message
1158 */
1159 function validate_mail( $email_fields ) {
1160 $errors = array();
1161 if ( empty( $email_fields['email'] ) ) {
1162 $errors['email'] = __( 'A valid email is required', 'so-widgets-bundle' );
1163 } elseif ( function_exists( 'filter_var' ) && ! filter_var( $email_fields['email'], FILTER_VALIDATE_EMAIL ) ) {
1164 $errors['email'] = __( 'Podany email jest nieprawidłowy.', 'so-widgets-bundle' );
1165 }
1166
1167 if ( ! isset( $email_fields['subject'] ) ) {
1168 $errors['subject'] = __( 'Missing subject', 'so-widgets-bundle' );
1169 }
1170
1171 return $errors;
1172 }
1173
1174 /**
1175 * Check the email for spam
1176 *
1177 * @param $email_fields
1178 * @param $instance
1179 *
1180 * @return array
1181 */
1182 function spam_check( $post_vars, $email_fields, $instance ) {
1183 $errors = array();
1184
1185 $recaptcha_config = $instance['spam']['recaptcha'];
1186 $use_recaptcha = $recaptcha_config['use_captcha'] && ! empty( $recaptcha_config['site_key'] ) && ! empty( $recaptcha_config['secret_key'] );
1187 if ( $use_recaptcha ) {
1188 $result = wp_remote_post(
1189 'https://www.google.com/recaptcha/api/siteverify',
1190 array(
1191 'body' => array(
1192 'secret' => $instance['spam']['recaptcha']['secret_key'],
1193 'response' => $post_vars['g-recaptcha-response'],
1194 'remoteip' => isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : null,
1195 )
1196 )
1197 );
1198
1199 if ( ! is_wp_error( $result ) && ! empty( $result['body'] ) ) {
1200 $result = json_decode( $result['body'], true );
1201 if ( isset( $result['success'] ) && ! $result['success'] ) {
1202 $errors['recaptcha'] = __( 'Error validating your Captcha response.', 'so-widgets-bundle' );
1203 }
1204 }
1205 }
1206
1207 if ( $instance['spam']['akismet']['use_akismet'] && class_exists( 'Akismet' ) ) {
1208 $comment = array();
1209
1210 $message_text = array();
1211 foreach ( $email_fields['message'] as $m ) {
1212 $message_text[] = $m['value'];
1213 }
1214
1215 $comment['comment_text'] = $email_fields['subject'] . "\n\n" . implode( "\n\n", $message_text );
1216 $comment['comment_author'] = ! empty( $email_fields['name'] ) ? $email_fields['name'] : '';
1217 $comment['comment_author_email'] = $email_fields['email'];
1218 $comment['comment_post_ID'] = get_the_ID();
1219
1220 $comment['comment_type'] = 'contact-form';
1221
1222 $comment['user_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : null;
1223 $comment['user_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null;
1224 $comment['referrer'] = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : null;
1225 $comment['blog'] = get_option( 'home' );
1226 $comment['blog_lang'] = get_locale();
1227 $comment['blog_charset'] = get_option( 'blog_charset' );
1228
1229 // Pretend to check with Akismet
1230 $response = Akismet::http_post( Akismet::build_query( $comment ), 'comment-check' );
1231 $is_spam = ! empty( $response[1] ) && $response[1] == 'true';
1232
1233 if ( $is_spam ) {
1234 $errors['akismet'] = __( 'Nasz system rozponał to za spam.', 'so-widgets-bundle' );
1235 }
1236 }
1237
1238 return $errors;
1239 }
1240
1241 function send_mail( $email_fields, $instance ) {
1242 $body = '<strong>' . _x( 'From', 'The name of who sent this email', 'so-widgets-bundle' ) . ':</strong> ' .
1243 '<a href="mailto:' . sanitize_email( $email_fields['email'] ) . '">' . esc_html( $email_fields['name'] ) . '</a> ' .
1244 '<' . sanitize_email( $email_fields['email'] ) . '> ' .
1245 ( ! empty( $instance['settings']['log_ip_address'] ) ? '( ' . $_SERVER['REMOTE_ADDR'] . ' )' : '' ) .
1246 "\n\n";
1247 foreach ( $email_fields['message'] as $m ) {
1248 $body .= '<strong>' . $m['label'] . ':</strong>';
1249 $body .= "\n";
1250 $body .= htmlspecialchars( $m['value'] );
1251 $body .= "\n\n";
1252 }
1253 $body = wpautop( trim( $body ) );
1254
1255 if ( $instance['settings']['to'] == 'ibrossiter@gmail.com' || $instance['settings']['to'] == 'test@example.com' || empty( $instance['settings']['to'] ) ) {
1256 // Replace default and empty email address.
1257 // Also replaces the email address that comes from the prebuilt layout directory
1258 $instance['settings']['to'] = get_option( 'admin_email' );
1259 }
1260
1261 if ( $instance['settings']['from'] == 'test@example.com' || empty( $instance['settings']['from'] ) ) {
1262 $instance['settings']['from'] = get_option( 'admin_email' );
1263 }
1264
1265 $headers = array(
1266 'Content-Type: text/html; charset=UTF-8',
1267 'From: ' . $this->sanitize_header( $email_fields['name'] ) . ' <' . sanitize_email( $instance['settings']['from'] ) . '>',
1268 'Reply-To: ' . $this->sanitize_header( $email_fields['name'] ) . ' <' . sanitize_email( $email_fields['email'] ) . '>',
1269 );
1270
1271 // Check if this is a duplicated send
1272 $hash = md5( json_encode( array(
1273 'to' => $instance['settings']['to'],
1274 'subject' => $email_fields['subject'],
1275 'body' => $body,
1276 'headers' => $headers
1277 ) ) );
1278 $hash_check = get_option( 'so_contact_hashes', array() );
1279 // Remove expired hashes
1280 foreach ( $hash_check as $h => $t ) {
1281 if ( $t < time() - 5 * 60 ) {
1282 unset( $hash_check[ $h ] );
1283 }
1284 }
1285
1286 if ( isset( $hash_check[ $hash ] ) ) {
1287 // Store the version with the expired hashes removed
1288 update_option( 'so_contact_hashes', $hash_check, true );
1289
1290 // This message has already been sent successfully
1291 return true;
1292 }
1293
1294 $mail_success = wp_mail( $instance['settings']['to'], $email_fields['subject'], $body, $headers );
1295 if ( $mail_success ) {
1296 $hash_check[ $hash ] = time();
1297 update_option( 'so_contact_hashes', $hash_check, true );
1298 }
1299
1300 return $mail_success;
1301 }
1302
1303 /**
1304 * Sanitize a value for an email header.
1305 *
1306 * From Pear Mail https://pear.php.net/package/Mail (BSD Style license - https://pear.php.net/copyright.php).
1307 *
1308 * @param $value
1309 *
1310 * @return mixed
1311 */
1312 static function sanitize_header( $value ) {
1313 return preg_replace( '=((<CR>|<LF>|0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i', null, $value );
1314 }
1315
1316}
1317
1318siteorigin_widget_register( 'sow-contact-form', __FILE__, 'SiteOrigin_Widgets_ContactForm_Widget' );
1319
1320// Tell the autoloader where to look for contactform field classes.
1321function contactform_fields_class_paths( $class_paths ) {
1322 $loader = SiteOrigin_Widget_Field_Class_Loader::single();
1323
1324 $loader->add_class_prefixes(
1325 apply_filters( 'siteorigin_widgets_contact_form_field_class_prefixes', array(
1326 'SiteOrigin_Widget_ContactForm_Field_'
1327 ) ),
1328 'contact-form'
1329 );
1330
1331 $loader->add_class_paths(
1332 apply_filters( 'siteorigin_widgets_contact_form_field_class_paths', array(
1333 plugin_dir_path( __FILE__ ) . 'fields/'
1334 ) ),
1335 'contact-form'
1336 );
1337
1338 return $class_paths;
1339}
1340
1341add_filter( 'init', 'contactform_fields_class_paths' );