Form builder to get in touch with visitors, grow your email list and collect payments — Happyforms - Version 1.13.0

Version Description

  • New feature: Long Answer field now supports rich-text editing.
  • New feature: Alphabetical choices, "other" choice, randomization, submission limiting support for Single Choice field.
  • New feature: Alphabetical choices, "other" choice, randomization, submission limiting support for Multiple Choice field.
  • New feature: "Other" choice, randomization, submission limiting support for Dropdown field.
  • Bugfix: Widget was misbehaving with new block-based Widgets screen.
  • Bugfix: Number field custom separators weren't working correctly.
  • Bugfix: Prefilled Long Answer field had styling glitches.
  • Bugfix: Long Answer field Limit words/characters controls had validation issues.
Download this release

Release Info

Developer happyforms
Plugin Icon 128x128 Form builder to get in touch with visitors, grow your email list and collect payments — Happyforms
Version 1.13.0
Comparing to
See all releases

Code changes from version 1.12.12 to 1.13.0

Files changed (45) hide show
  1. core/assets/css/color.css +191 -2
  2. core/assets/css/customize.css +29 -1
  3. core/assets/css/layout.css +348 -1
  4. core/assets/css/rich-text-editor.css +63 -0
  5. core/assets/js/admin/block.js +6 -4
  6. core/assets/js/frontend/checkbox.js +78 -8
  7. core/assets/js/frontend/long-text.js +319 -8
  8. core/assets/js/frontend/number.js +4 -2
  9. core/assets/js/frontend/radio.js +87 -0
  10. core/assets/js/frontend/select.js +41 -0
  11. core/assets/js/parts/part-checkbox.js +161 -1
  12. core/assets/js/parts/part-multi-line-text.js +43 -0
  13. core/assets/js/parts/part-radio.js +119 -0
  14. core/assets/js/parts/part-select.js +144 -0
  15. core/classes/class-block.php +3 -2
  16. core/classes/class-form-controller.php +23 -0
  17. core/classes/class-form-messages.php +27 -0
  18. core/classes/class-form-option-limiter.php +200 -0
  19. core/classes/class-form-setup.php +2 -2
  20. core/classes/class-form-shuffle.php +127 -0
  21. core/classes/class-happyforms-core.php +7 -0
  22. core/classes/parts/class-part-checkbox.php +240 -7
  23. core/classes/parts/class-part-multi-line-text.php +196 -25
  24. core/classes/parts/class-part-radio.php +153 -11
  25. core/classes/parts/class-part-select.php +149 -12
  26. core/helpers/helper-form-templates.php +18 -0
  27. core/helpers/helper-misc.php +11 -1
  28. core/templates/partials/frontend-rich-text-toolbar-icons.php +2 -2
  29. core/templates/parts/block-rich-text.php +23 -0
  30. core/templates/parts/customize-checkbox.php +60 -14
  31. core/templates/parts/customize-multi-line-text.php +8 -2
  32. core/templates/parts/customize-radio.php +37 -0
  33. core/templates/parts/customize-select.php +36 -2
  34. core/templates/parts/frontend-checkbox-other-option.php +35 -0
  35. core/templates/parts/frontend-checkbox.php +12 -4
  36. core/templates/parts/frontend-multi-line-text.php +8 -0
  37. core/templates/parts/frontend-radio-other-option.php +30 -0
  38. core/templates/parts/frontend-radio.php +8 -4
  39. core/templates/parts/frontend-select-other-option.php +22 -0
  40. core/templates/parts/frontend-select.php +26 -5
  41. happyforms.php +2 -2
  42. inc/assets/js/customize.js +37 -0
  43. inc/classes/class-message-controller.php +122 -4
  44. languages/happyforms.pot +177 -90
  45. readme.txt +15 -2
core/assets/css/color.css CHANGED
@@ -259,7 +259,8 @@
259
  margin-bottom: 10px;
260
  }
261
 
262
- .happyforms-styles .happyforms-part .option-label .label {
 
263
  font-weight: normal;
264
  font-size: 16px;
265
  font-size: var(--happyforms-part-value-font-size);
@@ -267,6 +268,12 @@
267
  color: var(--happyforms-color-part-description);
268
  }
269
 
 
 
 
 
 
 
270
  .happyforms-styles .happyforms-part-option__description {
271
  font-size: 12px;
272
  color: #000;
@@ -366,8 +373,21 @@
366
  background-color: var(--happyforms-color-choice-checkmark-color);
367
  }
368
 
369
- .happyforms-styles .happyforms-part--radio input:checked+.checkmark .happyforms-radio-circle {
 
 
 
 
 
 
370
  display: block;
 
 
 
 
 
 
 
371
  }
372
 
373
  /* parts - select */
@@ -880,3 +900,172 @@
880
  border-top-right-radius: 25px;
881
  border-bottom-right-radius: 25px;
882
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  margin-bottom: 10px;
260
  }
261
 
262
+ .happyforms-styles .happyforms-part .option-label .label,
263
+ .happyforms-part .option-label .happyforms-remaining-choice {
264
  font-weight: normal;
265
  font-size: 16px;
266
  font-size: var(--happyforms-part-value-font-size);
268
  color: var(--happyforms-color-part-description);
269
  }
270
 
271
+ .happyforms-styles .happyforms-part .option-label input:checked+.checkmark,
272
+ .happyforms-part.happyforms-part-choice--display-as-letters input:checked+.checkmark {
273
+ background-color: #000;
274
+ background-color: var(--happyforms-color-choice-checkmark-bg-focus);
275
+ }
276
+
277
  .happyforms-styles .happyforms-part-option__description {
278
  font-size: 12px;
279
  color: #000;
373
  background-color: var(--happyforms-color-choice-checkmark-color);
374
  }
375
 
376
+ .happyforms-styles .happyforms-part--radio input:checked+.checkmark .happyforms-radio-circle,
377
+ .happyforms-styles .happyforms-part--poll input:checked+.checkmark .happyforms-radio-circle,
378
+ .happyforms-styles .happyforms-part--table:not(.happyforms-selection--multiple) input:checked+.checkmark .happyforms-radio-circle {
379
+ display: block;
380
+ }
381
+
382
+ .happyforms-styles .happyforms-part input:checked+.checkmark svg {
383
  display: block;
384
+ color: #fff;
385
+ color: var(--happyforms-color-choice-checkmark-color);
386
+ }
387
+
388
+ .happyforms-part.happyforms-part-choice--display-as-letters input:checked + .checkmark {
389
+ color: #fff;
390
+ color: var(--happyforms-color-choice-checkmark-color);
391
  }
392
 
393
  /* parts - select */
900
  border-top-right-radius: 25px;
901
  border-bottom-right-radius: 25px;
902
  }
903
+
904
+ .happyforms-part__char-counter {
905
+ font-size: 14px;
906
+ font-size: var(--happyforms-part-description-font-size);
907
+ color: #454545;
908
+ color: var(--happyforms-color-part-description);
909
+ }
910
+
911
+ /*
912
+ * Rich Text
913
+ */
914
+ .happyforms-styles .happyforms-part--rich_text div.mce-toolbar-grp {
915
+ border-radius: 6px;
916
+ border-bottom-left-radius: 0;
917
+ border-bottom-right-radius: 0;
918
+ background-color: white;
919
+ border-color: #dbdbdb !important;
920
+ border-color: var(--happyforms-color-part-border) !important;
921
+ }
922
+
923
+ .happyforms-styles.happyforms-form--part-border-radius-square .happyforms-part--rich_text div.mce-toolbar-grp {
924
+ border-radius: 0;
925
+ }
926
+
927
+ .happyforms-styles.happyforms-form--part-border-radius-pill .happyforms-part--rich_text div.mce-toolbar-grp {
928
+ border-radius: 25px;
929
+ border-bottom-left-radius: 0;
930
+ border-bottom-right-radius: 0;
931
+ }
932
+
933
+ .happyforms-styles div.mce-inline-toolbar-grp.happyforms-editor-toolbar-link {
934
+ border-color: #7aa4ff !important;
935
+ border-color: var(--happyforms-color-part-border-focus) !important;
936
+ }
937
+
938
+ .happyforms-styles .happyforms-editor-toolbar-link.mce-inline-toolbar-grp.mce-arrow-down:before {
939
+ border-top-color: #7aa4ff;
940
+ border-top-color: var(--happyforms-color-part-border-focus);
941
+ }
942
+
943
+ .happyforms-styles div.mce-inline-toolbar-grp.happyforms-editor-toolbar-link div.wp-link-input input::placeholder {
944
+ color: #888888;
945
+ color: var(--happyforms-color-part-placeholder) !important;
946
+ }
947
+
948
+ .happyforms-styles .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn,
949
+ .happyforms-styles .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn,
950
+ .happyforms-styles .happyforms-part--rich_text .qt-dfw,
951
+ .happyforms-styles .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn,
952
+ .happyforms-styles .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn.mce-primary {
953
+ padding: 2px;
954
+ background-color: white;
955
+ border-color: white;
956
+ }
957
+
958
+ .happyforms-styles .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn:hover,
959
+ .happyforms-styles .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn:focus,
960
+ .happyforms-styles .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn:hover,
961
+ .happyforms-styles .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn.mce-primary:hover,
962
+ .happyforms-styles .happyforms-part--rich_text .qt-dfw:hover,
963
+ .happyforms-styles .happyforms-part--rich_text .qt-dfw:focus,
964
+ .happyforms-styles .happyforms-part--rich_text .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn:hover,
965
+ .happyforms-styles .happyforms-part--rich_text .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn:focus,
966
+ .happyforms-styles .happyforms-part--rich_text .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn.mce-primary:hover,
967
+ .happyforms-styles .happyforms-part--rich_text .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn.mce-primary:focus,
968
+ .happyforms-styles .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn.mce-active,
969
+ .happyforms-styles .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn:active,
970
+ .happyforms-styles .happyforms-part--rich_text .qt-dfw.active,
971
+ .happyforms-styles .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn.mce-active:hover,
972
+ .happyforms-styles .happyforms-part--rich_text .qt-dfw.active:hover {
973
+ border-color: transparent;
974
+ border-radius: 6px;
975
+ background-color: #efefef;
976
+ }
977
+
978
+ .happyforms-styles .happyforms-part--rich_text .mce-btn svg,
979
+ .happyforms-styles .happyforms-editor-toolbar-link svg {
980
+ fill: #000;
981
+ }
982
+
983
+ .happyforms-styles .happyforms-part--rich_text .happyforms-visual-editor {
984
+ padding-bottom: 1.5px;
985
+ border: 0;
986
+ }
987
+
988
+ .happyforms-styles .happyforms-part--rich_text .mce-edit-area {
989
+ border-bottom-width: 1.5px;
990
+ border: 1px solid #dbdbdb !important;
991
+ border-radius: 6px;
992
+ border-top-left-radius: 0;
993
+ border-top-right-radius: 0;
994
+ transition: border-color 0.25s;
995
+ transition: border-color var(--happyforms-transition-duration);
996
+ }
997
+
998
+ .happyforms-styles.happyforms-form--part-border-radius-square .happyforms-part--rich_text .mce-edit-area {
999
+ border-radius: 0;
1000
+ }
1001
+
1002
+ .happyforms-styles .happyforms-part--rich_text.focus .mce-edit-area {
1003
+ border-color: #7aa4ff !important;
1004
+ border-color: var(--happyforms-color-part-border-focus) !important;
1005
+ box-shadow: inset 0 0 0 1px #7aa4ff;
1006
+ box-shadow: inset 0 0 0 1px var(--happyforms-color-part-border-focus);
1007
+ }
1008
+
1009
+ .happyforms-styles .happyforms-part--rich_text .mce-edit-area {
1010
+ background-color: #fff !important;
1011
+ background-color: var(--happyforms-color-part-background) !important;
1012
+ }
1013
+
1014
+ .happyforms-styles.happyforms-form--part-border-radius-pill .happyforms-part--rich_text .mce-edit-area {
1015
+ border-bottom-left-radius: 25px;
1016
+ border-bottom-right-radius: 25px;
1017
+ }
1018
+
1019
+ .happyforms-styles .happyforms-part--rich_text.focus .mce-edit-area {
1020
+ background-color: #fff !important;
1021
+ background-color: var(--happyforms-color-part-background-focus) !important;
1022
+ }
1023
+
1024
+ .happyforms-styles .happyforms-part--rich_text .mce-statusbar {
1025
+ border-bottom-left-radius: 6px;
1026
+ border-bottom-right-radius: 6px;
1027
+ }
1028
+
1029
+ .happyforms-styles .happyforms-part--rich_text .mce-statusbar .mce-path:nth-child(1) {
1030
+ display: none;
1031
+ }
1032
+
1033
+ .happyforms-styles.happyforms-form--part-border-radius-square .happyforms-part--rich_text .mce-statusbar {
1034
+ border-radius: 0;
1035
+ }
1036
+
1037
+ .happyforms-styles.happyforms-form--part-border-radius-pill .happyforms-part--rich_text .mce-statusbar {
1038
+ border-bottom-left-radius: 25px;
1039
+ border-bottom-right-radius: 25px;
1040
+ }
1041
+
1042
+ .happyforms-styles .happyforms-part--rich_text.happyforms-part--label-below.happyforms-part--has-tooltip .happyforms-visual-editor,
1043
+ .happyforms-styles .happyforms-part--rich_text.happyforms-part--label-left.happyforms-part--has-tooltip .happyforms-visual-editor {
1044
+ margin-top: 35px;
1045
+ }
1046
+
1047
+ .happyforms-styles .happyforms-part--rich_text.happyforms-part--label-hidden.happyforms-part--has-tooltip .happyforms-part__el {
1048
+ margin-top: 35px;
1049
+ }
1050
+
1051
+ .happyforms-styles .happyforms-part--rich_text.happyforms-part--label-hidden.happyforms-part--has-tooltip .happyforms-tooltip {
1052
+ top: -30px;
1053
+ }
1054
+
1055
+ .happyforms-styles.happyforms-form--part-border-off .happyforms-part--rich_text .happyforms-visual-editor {
1056
+ padding-bottom: 1px;
1057
+ }
1058
+
1059
+ .happyforms-part--legal .option-label .label p,
1060
+ .happyforms-part--mailchimp .option-label .label p,
1061
+ .happyforms-part--email_integration .option-label .label p,
1062
+ .happyforms-part--signature .option-label .label p {
1063
+ margin-top: 0;
1064
+ }
1065
+
1066
+ .happyforms-part--legal .option-label .label p:last-child,
1067
+ .happyforms-part--mailchimp .option-label .label p:last-child,
1068
+ .happyforms-part--email_integration .option-label .label p:last-child,
1069
+ .happyforms-part--signature .option-label .label p:last-child {
1070
+ margin-bottom: 0;
1071
+ }
core/assets/css/customize.css CHANGED
@@ -626,7 +626,8 @@ a.happyforms-form-part-remove:hover {
626
  .has-max-words-label #customize-control-message_too_long,
627
  .has-select #customize-control-no_results_label,
628
  .has-number #customize-control-number_min_invalid,
629
- .has-number #customize-control-number_max_invalid {
 
630
  display: block;
631
  }
632
 
@@ -1649,6 +1650,24 @@ ul.happyforms-parts-list li.happyforms-parts-list-item--group .happyforms-parts-
1649
  padding-right: 0;
1650
  }
1651
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1652
  .happyforms-widget-form .happyforms-widget-row {
1653
  display: flex;
1654
  flex-wrap: wrap;
@@ -1687,3 +1706,12 @@ ul.happyforms-parts-list li.happyforms-parts-list-item--group .happyforms-parts-
1687
  p.label-field-group {
1688
  margin-bottom: 0;
1689
  }
 
 
 
 
 
 
 
 
 
626
  .has-max-words-label #customize-control-message_too_long,
627
  .has-select #customize-control-no_results_label,
628
  .has-number #customize-control-number_min_invalid,
629
+ .has-number #customize-control-number_max_invalid,
630
+ .has-show-submissions-left #customize-control-submissions_left_label {
631
  display: block;
632
  }
633
 
1650
  padding-right: 0;
1651
  }
1652
 
1653
+ .number-options--numeric {
1654
+ display: flex;
1655
+ }
1656
+
1657
+ .number-options--numeric > * {
1658
+ width: 50%;
1659
+ margin: 0;
1660
+ padding: 0 5px;
1661
+ }
1662
+
1663
+ .number-options--numeric > *:first-child {
1664
+ padding-left: 0;
1665
+ }
1666
+
1667
+ .number-options--numeric > *:last-child {
1668
+ padding-right: 0;
1669
+ }
1670
+
1671
  .happyforms-widget-form .happyforms-widget-row {
1672
  display: flex;
1673
  flex-wrap: wrap;
1706
  p.label-field-group {
1707
  margin-bottom: 0;
1708
  }
1709
+
1710
+ /**
1711
+ *
1712
+ * Rich text part
1713
+ *
1714
+ */
1715
+ ul.happyforms-parts-list li[data-part-type="rich_text"] {
1716
+ display: none;
1717
+ }
core/assets/css/layout.css CHANGED
@@ -1147,10 +1147,27 @@ input[type=submit][disabled].happyforms-button--submit {
1147
  padding: 45px 1% 10px;
1148
  }
1149
 
1150
- .happyforms-part__char-counter .counter-label {
 
 
 
 
 
 
 
1151
  text-transform: lowercase;
1152
  }
1153
 
 
 
 
 
 
 
 
 
 
 
1154
  .screen-reader-text {
1155
  border: 0;
1156
  clip: rect(1px, 1px, 1px, 1px);
@@ -1297,3 +1314,333 @@ input[type=submit][disabled].happyforms-button--submit {
1297
  background: #ffffff;
1298
  color: #000;
1299
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1147
  padding: 45px 1% 10px;
1148
  }
1149
 
1150
+ .happyforms-part__char-counter {
1151
+ display: none;
1152
+ margin-top: 5px;
1153
+ pointer-events: none;
1154
+ }
1155
+
1156
+ .happyforms-part__char-counter .counter-label,
1157
+ .happyforms-part .happyforms-remaining-choice {
1158
  text-transform: lowercase;
1159
  }
1160
 
1161
+ .happyforms-part--checkbox .option-label .label,
1162
+ .happyforms-part--radio .option-label .label {
1163
+ padding-right: 5px;
1164
+ }
1165
+
1166
+
1167
+ .happyforms-custom-select-dropdown .happyforms-remaining-choice {
1168
+ padding-left: 5px;
1169
+ }
1170
+
1171
  .screen-reader-text {
1172
  border: 0;
1173
  clip: rect(1px, 1px, 1px, 1px);
1314
  background: #ffffff;
1315
  color: #000;
1316
  }
1317
+
1318
+ .happyforms-part--choice .happyforms-part-option--other input[type=text] {
1319
+ display: none;
1320
+ margin-top: 10px !important;
1321
+ }
1322
+
1323
+ .happyforms-part--choice .happyforms-part-option--other input[type=text].hf-show {
1324
+ display: block;
1325
+ }
1326
+
1327
+ .happyforms-part--select .happyforms-part-option--other input[type=text] {
1328
+ display: none;
1329
+ margin-top: 10px;
1330
+ }
1331
+
1332
+ .happyforms-part--select .happyforms-part-option--other input[type=text].hf-show {
1333
+ display:block;
1334
+ }
1335
+
1336
+ /*
1337
+ * Rich Text
1338
+ */
1339
+
1340
+ .happyforms-part--rich_text div.mce-toolbar-grp {
1341
+ position: relative;
1342
+ left: -1px;
1343
+ width: calc(100% - 2px);
1344
+ border: 1px solid #dbdbdb !important;
1345
+ border-bottom: 0 !important;
1346
+ }
1347
+
1348
+ .happyforms-part--rich_text div.mce-panel {
1349
+ background: transparent !important;
1350
+ }
1351
+
1352
+ .happyforms-part--rich_text.focus div.mce-toolbar-grp {
1353
+ left: 0;
1354
+ }
1355
+
1356
+ .happyforms-part--rich_text div.mce-toolbar-grp>div {
1357
+ padding: 0 2px;
1358
+ }
1359
+
1360
+ .happyforms-part--rich_text.focus div.mce-toolbar-grp>div {
1361
+ padding: 0 1px;
1362
+ }
1363
+
1364
+ .happyforms-part--rich_text .mce-top-part::before {
1365
+ box-shadow: none;
1366
+ }
1367
+
1368
+ div.mce-inline-toolbar-grp.happyforms-editor-toolbar-link {
1369
+ background-color: white;
1370
+ border-width: 1px;
1371
+ box-shadow: none;
1372
+ border-radius: 6px;
1373
+ }
1374
+
1375
+ .happyforms-editor-toolbar-link.mce-inline-toolbar-grp.mce-arrow-up:before {
1376
+ top: -11px;
1377
+ left: 50%;
1378
+ border-width: 0 11px 11px;
1379
+ }
1380
+
1381
+ .happyforms-editor-toolbar-link.mce-inline-toolbar-grp.mce-arrow-down:before {
1382
+ bottom: -10px;
1383
+ border-width: 9px 9px 0;
1384
+ }
1385
+
1386
+ .happyforms-editor-toolbar-link.mce-inline-toolbar-grp.mce-arrow-down:after {
1387
+ bottom: -8px;
1388
+ left: 50%;
1389
+ border-top-color: #fff;
1390
+ }
1391
+
1392
+ .happyforms-editor-toolbar-link.mce-inline-toolbar-grp.mce-arrow-left:before {
1393
+ left: 18px;
1394
+ }
1395
+
1396
+ .happyforms-editor-toolbar-link.mce-inline-toolbar-grp.mce-arrow-left.mce-arrow-down:after {
1397
+ left: 19px;
1398
+ }
1399
+
1400
+ .happyforms-editor-toolbar-link.mce-inline-toolbar-grp.mce-arrow-up.mce-arrow-right:before {
1401
+ left: auto;
1402
+ right: 18px;
1403
+ }
1404
+
1405
+ .happyforms-editor-toolbar-link.mce-inline-toolbar-grp.mce-arrow-up:after {
1406
+ left: calc(50% + 0px);
1407
+ top: -10px;
1408
+ border-bottom-color: #fff;
1409
+ border-width: 0px 10px 10px 10px;
1410
+ }
1411
+
1412
+ .happyforms-editor-toolbar-link.mce-inline-toolbar-grp.mce-arrow-up.mce-arrow-left:after {
1413
+ left: 19px;
1414
+ }
1415
+
1416
+ .happyforms-editor-toolbar-link.mce-inline-toolbar-grp.mce-arrow-right:after {
1417
+ left: auto;
1418
+ right: 19px;
1419
+ }
1420
+
1421
+ .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn button,
1422
+ .happyforms-editor-toolbar-link .mce-toolbar .mce-btn-group .mce-btn button {
1423
+ cursor: pointer !important;
1424
+ padding: 0 !important;
1425
+ background-color: transparent;
1426
+ }
1427
+
1428
+ div.mce-inline-toolbar-grp.happyforms-editor-toolbar-link div.wp-link-input input {
1429
+ margin: 0;
1430
+ padding: 5px;
1431
+ border: 0;
1432
+ background-color: white;
1433
+ }
1434
+
1435
+ div.mce-inline-toolbar-grp.happyforms-editor-toolbar-link div.wp-link-preview {
1436
+ margin: 7px 6px;
1437
+ }
1438
+
1439
+ div.mce-inline-toolbar-grp.happyforms-editor-toolbar-link div.wp-link-input input::placeholder {
1440
+ color: #888888;
1441
+ }
1442
+
1443
+ div.mce-inline-toolbar-grp.happyforms-editor-toolbar-link .mce-btn,
1444
+ div.mce-inline-toolbar-grp.happyforms-editor-toolbar-link .mce-btn.mce-primary {
1445
+ border-color: transparent;
1446
+ }
1447
+
1448
+ div.mce-inline-toolbar-grp.happyforms-editor-toolbar-link .mce-btn:hover,
1449
+ div.mce-inline-toolbar-grp.happyforms-editor-toolbar-link .mce-btn:active {
1450
+ border-color: transparent !important;
1451
+ border-radius: 6px;
1452
+ background-color: #efefef !important;
1453
+ }
1454
+
1455
+ .happyforms-part--rich_text .mce-panel {
1456
+ z-index: 1 !important;
1457
+ }
1458
+
1459
+ .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn,
1460
+ .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn,
1461
+ .happyforms-part--rich_text .qt-dfw,
1462
+ .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn,
1463
+ .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn.mce-primary {
1464
+ padding: 2px;
1465
+ background-color: transparent;
1466
+ }
1467
+
1468
+ .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn:hover,
1469
+ .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn:focus,
1470
+ .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn:hover,
1471
+ .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn.mce-primary:hover,
1472
+ .happyforms-part--rich_text .qt-dfw:hover,
1473
+ .happyforms-part--rich_text .qt-dfw:focus,
1474
+ .happyforms-part--rich_text .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn:hover,
1475
+ .happyforms-part--rich_text .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn:focus,
1476
+ .happyforms-part--rich_text .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn.mce-primary:hover,
1477
+ .happyforms-part--rich_text .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn.mce-primary:focus,
1478
+ .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn.mce-active,
1479
+ .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn:active,
1480
+ .happyforms-part--rich_text .qt-dfw.active,
1481
+ .happyforms-part--rich_text .mce-toolbar .mce-btn-group .mce-btn.mce-active:hover,
1482
+ .happyforms-part--rich_text .qt-dfw.active:hover {
1483
+ background: transparent;
1484
+ box-shadow: none;
1485
+ color: inherit;
1486
+ }
1487
+
1488
+ .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn.mce-primary,
1489
+ .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn.mce-primary:hover,
1490
+ .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn.mce-primary:focus,
1491
+ .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn,
1492
+ .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn:hover,
1493
+ .mce-inline-toolbar-grp .mce-toolbar .mce-btn-group .mce-btn:focus {
1494
+ box-shadow: none;
1495
+ }
1496
+
1497
+ .happyforms-part--rich_text .mce-btn svg {
1498
+ fill: currentColor;
1499
+ }
1500
+
1501
+ .happyforms-editor-toolbar-link svg {
1502
+ fill: #000;
1503
+ }
1504
+
1505
+ .happyforms-part--rich_text .mce-edit-area,
1506
+ .happyforms-part--rich_text .mce-statusbar .mce-container-body,
1507
+ .happyforms-part--rich_text div.mce-statusbar {
1508
+ border-width: 0 !important;
1509
+ }
1510
+
1511
+ .happyforms-part--rich_text div.mce-toolbar-grp {
1512
+ background-color: #fff !important;
1513
+ }
1514
+
1515
+ .happyforms-part--rich_text .mce-tinymce.mce-container {
1516
+ border: 0px;
1517
+ border-width: 0px !important;
1518
+ box-shadow: none;
1519
+ }
1520
+
1521
+ .happyforms-part--rich_text.focus .mce-tinymce.mce-container {
1522
+ overflow: hidden;
1523
+ }
1524
+
1525
+ .happyforms-part--rich_text .mce-statusbar {
1526
+ position: absolute;
1527
+ left: 2px;
1528
+ bottom: 2px;
1529
+ width: calc(100% - 4px);
1530
+ background-color: transparent !important;
1531
+ }
1532
+
1533
+ .happyforms-part--rich_text .happyforms-visual-editor {
1534
+ position: relative;
1535
+ overflow: hidden;
1536
+ padding: 1px 1px 0;
1537
+ border: 1px solid;
1538
+ }
1539
+
1540
+ .happyforms-part--rich_text.focus .happyforms-visual-editor {
1541
+ outline: 0;
1542
+ }
1543
+
1544
+ .happyforms-form--part-border-off .happyforms-part--rich_text .happyforms-visual-editor {
1545
+ border-color: transparent;
1546
+ }
1547
+
1548
+ .happyforms-form--part-borders-bottom-only .happyforms-part--rich_text.focus .happyforms-visual-editor {
1549
+ border-bottom-width: 1px;
1550
+ }
1551
+
1552
+ .happyforms-form--part-border-off .happyforms-part--rich_text div.mce-toolbar-grp,
1553
+ .happyforms-form--part-border-off .happyforms-part--rich_text .mce-statusbar .mce-container-body {
1554
+ border: 0;
1555
+ }
1556
+
1557
+ .happyforms-form--part-borders-bottom-only .happyforms-part--rich_text .happyforms-visual-editor {
1558
+ box-shadow: none;
1559
+ border-top-color: transparent;
1560
+ border-left-color: transparent;
1561
+ border-right-color: transparent;
1562
+ }
1563
+
1564
+ .happyforms-form--part-borders-bottom-only .happyforms-part--rich_text .mce-statusbar .mce-container-body {
1565
+ border-left: 0;
1566
+ border-right: 0;
1567
+ }
1568
+
1569
+ .happyforms-form--part-border-radius-square .happyforms-part--rich_text .happyforms-visual-editor,
1570
+ .happyforms-form--part-border-radius-square .happyforms-part--rich_text .mce-tinymce.mce-container {
1571
+ border-radius: 0 !important;
1572
+ }
1573
+
1574
+ .happyforms-form--part-border-radius-pill .happyforms-part--rich_text .happyforms-visual-editor,
1575
+ .happyforms-form--part-border-radius-pill .happyforms-part--rich_text .mce-tinymce.mce-container {
1576
+ border-radius: 25px !important;
1577
+ }
1578
+
1579
+ .happyforms-form--part-border-radius-pill .happyforms-part--rich_text .mce-toolbar .mce-btn-group.mce-first {
1580
+ margin-left: 10px;
1581
+ }
1582
+
1583
+ .happyforms-form--part-border-radius-pill .happyforms-part--rich_text .mce-container-body .mce-resizehandle {
1584
+ right: 10px;
1585
+ bottom: 3px;
1586
+ }
1587
+
1588
+ .happyforms-form--part-inner-padding-narrow .happyforms-part--rich_text .mce-content-body {
1589
+ margin-top: 0;
1590
+ margin-bottom: 0;
1591
+ }
1592
+
1593
+ .happyforms-part--rich_text .mce-edit-area {
1594
+ position: relative;
1595
+ left: -1px;
1596
+ padding: 0;
1597
+ overflow: hidden;
1598
+ }
1599
+
1600
+ .happyforms-part--rich_text.focus .mce-edit-area {
1601
+ left: 0;
1602
+ }
1603
+
1604
+ .happyforms-form--part-inner-padding-wide .happyforms-part--rich_text.focus .mce-edit-area {
1605
+ width: calc(100% - 10px);
1606
+ }
1607
+
1608
+ .happyforms-part--rich_text.focus .mce-edit-area iframe {
1609
+ margin-left: -1px;
1610
+ }
1611
+
1612
+ .happyforms-part--rich_text .mce-edit-area iframe {
1613
+ position: relative;
1614
+ top: -5px;
1615
+ }
1616
+
1617
+ .happyforms-form--part-inner-padding-wide .happyforms-part--rich_text .mce-edit-area {
1618
+ padding: 0 5px 5px;
1619
+ }
1620
+
1621
+ .happyforms-form--part-inner-padding-narrow .happyforms-part--rich_text .mce-edit-area iframe {
1622
+ top: -10px;
1623
+ left: -5px;
1624
+ }
1625
+
1626
+ .happyforms-part--rich_text .mce-container-body .mce-resizehandle {
1627
+ right: -1px;
1628
+ bottom: -1px;
1629
+ overflow-y: hidden;
1630
+ resize: horizontal;
1631
+ }
1632
+
1633
+ .happyforms-part--rich_text .mce-container-body .mce-resizehandle .mce-i-resize:before {
1634
+ resize: horizontal;
1635
+ overflow-y: scroll;
1636
+ color: transparent;
1637
+ }
1638
+
1639
+ .happyforms-part--rich_text.happyforms-part--label-below.happyforms-part--has-tooltip .happyforms-visual-editor,
1640
+ .happyforms-part--rich_text.happyforms-part--label-left.happyforms-part--has-tooltip .happyforms-visual-editor {
1641
+ margin-top: 35px;
1642
+ }
1643
+
1644
+ .happyforms-part--rich_text.happyforms-part--label-hidden.happyforms-part--has-tooltip .happyforms-part__el {
1645
+ margin-top: 35px;
1646
+ }
core/assets/css/rich-text-editor.css ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ html.happyforms-form--part-value-text-align-center body {
2
+ text-align: center;
3
+ }
4
+
5
+ html.happyforms-form--part-value-text-align-right body {
6
+ text-align: right;
7
+ }
8
+
9
+ body {
10
+ padding: 10px;
11
+ font-size: 16px;
12
+ font-size: var(--happyforms-part-value-font-size);
13
+ background: transparent !important;
14
+ color: #000;
15
+ color: var(--happyforms-color-part-value);
16
+ }
17
+
18
+ html.happyforms-form--part-inner-padding-wide body {
19
+ padding-top: 15px;
20
+ }
21
+
22
+ p, strong, em, ul, ol, li, blockquote, pre {
23
+ margin: 0;
24
+ padding: 0;
25
+ border: 0;
26
+ background: transparent !important;
27
+ color: #000;
28
+ color: var(--happyforms-color-part-value);
29
+ }
30
+
31
+ ol, ul {
32
+ list-style: none;
33
+ }
34
+
35
+ ul {
36
+ list-style-type: disc;
37
+ }
38
+
39
+ ol {
40
+ list-style-type: decimal;
41
+ }
42
+
43
+ ul:before, ul:after, li:before, li:after {
44
+ content: '';
45
+ width: 0;
46
+ height: 0;
47
+ background: transparent;
48
+ }
49
+
50
+ strong {
51
+ font-weight: bold !important;
52
+ }
53
+
54
+ em {
55
+ font-style: italic !important;
56
+ }
57
+
58
+ ul, ol, blockquote {
59
+ margin: 20px 0;
60
+ padding-left: 40px;
61
+ font-size: 16px;
62
+ font-size: var(--happyforms-part-value-font-size) !important;
63
+ }
core/assets/js/admin/block.js CHANGED
@@ -2,14 +2,14 @@
2
 
3
  var el = wp.element.createElement;
4
  var registerBlockType = wp.blocks.registerBlockType;
5
- var ServerSideRender = wp.components.ServerSideRender;
6
  var PanelBody = wp.components.PanelBody;
7
  var SelectControl = wp.components.SelectControl;
8
  var Placeholder = wp.components.Placeholder;
9
  var Notice = wp.components.Notice;
10
  var Button = wp.components.Button;
11
  var Icon = wp.components.Icon;
12
- var InspectorControls = wp.editor.InspectorControls;
13
 
14
  var blockID = 'thethemefoundry/happyforms';
15
  var options = Object
@@ -25,6 +25,7 @@
25
  icon: settings.icon,
26
  label: settings.i18n.placeholder_text,
27
  className: settings.i18n.placeholder_class,
 
28
  },
29
  el( SelectControl, {
30
  value: '',
@@ -39,9 +40,10 @@
39
  };
40
 
41
  var ComponentForm = function( props ) {
42
- var shortcode = el( ServerSideRender, {
43
  block: blockID,
44
  attributes: props.attributes,
 
45
  }
46
  );
47
 
@@ -52,7 +54,7 @@
52
 
53
  var ComponentInspector = function( props ) {
54
  var component =
55
- el( InspectorControls, {},
56
  el( PanelBody, { title: settings.i18n.settings_title },
57
  el( SelectControl, {
58
  value: props.attributes.id,
2
 
3
  var el = wp.element.createElement;
4
  var registerBlockType = wp.blocks.registerBlockType;
5
+ var ServerSideRender = wp.serverSideRender;
6
  var PanelBody = wp.components.PanelBody;
7
  var SelectControl = wp.components.SelectControl;
8
  var Placeholder = wp.components.Placeholder;
9
  var Notice = wp.components.Notice;
10
  var Button = wp.components.Button;
11
  var Icon = wp.components.Icon;
12
+ var InspectorControls = wp.blockEditor.InspectorControls;
13
 
14
  var blockID = 'thethemefoundry/happyforms';
15
  var options = Object
25
  icon: settings.icon,
26
  label: settings.i18n.placeholder_text,
27
  className: settings.i18n.placeholder_class,
28
+ key: 'happyforms-component-placeholder',
29
  },
30
  el( SelectControl, {
31
  value: '',
40
  };
41
 
42
  var ComponentForm = function( props ) {
43
+ var shortcode = el( wp.serverSideRender, {
44
  block: blockID,
45
  attributes: props.attributes,
46
+ key: 'happyforms-component-form',
47
  }
48
  );
49
 
54
 
55
  var ComponentInspector = function( props ) {
56
  var component =
57
+ el( InspectorControls, { key: 'happyforms-inspector-controls' },
58
  el( PanelBody, { title: settings.i18n.settings_title },
59
  el( SelectControl, {
60
  value: props.attributes.id,
core/assets/js/frontend/checkbox.js CHANGED
@@ -5,7 +5,7 @@
5
  HappyForms.parts.checkbox = {
6
  init: function() {
7
  this.type = this.$el.data( 'happyforms-type' );
8
- this.$input = $( 'input[data-serialize]', this.$el );
9
  this.$selectAllCheckbox = $( 'input.happyforms-select-all', this.$el );
10
 
11
  if ( this.$selectAllCheckbox.length ) {
@@ -14,24 +14,94 @@
14
  }
15
 
16
  this.$input.on( 'change', this.triggerChange.bind( this ) );
 
 
 
17
  this.initTooltip();
18
  },
19
 
20
  onSelectAllChange: function() {
21
  if ( this.$selectAllCheckbox.is( ':checked' ) ) {
22
- this.$input.prop( 'checked', true );
 
 
 
 
23
  } else {
24
- this.$input.prop( 'checked', false );
 
 
 
 
25
  }
26
  },
27
 
28
  onCheckboxChange: function( e ) {
29
- var $checkbox = $( e.target );
 
 
 
 
 
 
 
 
 
 
30
 
31
- if ( ! $checkbox.is( ':checked' ) ) {
32
- this.$selectAllCheckbox.prop( 'checked', false );
 
 
 
33
  }
34
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  };
36
 
37
- } )( jQuery );
5
  HappyForms.parts.checkbox = {
6
  init: function() {
7
  this.type = this.$el.data( 'happyforms-type' );
8
+ this.$input = $( 'input', this.$el );
9
  this.$selectAllCheckbox = $( 'input.happyforms-select-all', this.$el );
10
 
11
  if ( this.$selectAllCheckbox.length ) {
14
  }
15
 
16
  this.$input.on( 'change', this.triggerChange.bind( this ) );
17
+ this.$input.on( 'change', this.onCheckboxChange.bind( this ) );
18
+ this.$input.on( 'focus', this.onFocus.bind( this ) );
19
+
20
  this.initTooltip();
21
  },
22
 
23
  onSelectAllChange: function() {
24
  if ( this.$selectAllCheckbox.is( ':checked' ) ) {
25
+ this.$input.each( function( i, input ) {
26
+ if ( $(input).hasClass( 'happyforms-checkbox' ) ) {
27
+ $(input).prop( 'checked', true );
28
+ }
29
+ } );
30
  } else {
31
+ this.$input.each( function( i, input ) {
32
+ if ( $(input).hasClass( 'happyforms-checkbox' ) ) {
33
+ $(input).prop( 'checked', false );
34
+ }
35
+ } );
36
  }
37
  },
38
 
39
  onCheckboxChange: function( e ) {
40
+ var $otherCheckbox = $( '.happyforms-part-option--other input[type=checkbox]', this.$el );
41
+ var $label = $otherCheckbox.parent();
42
+ var $otherInput = $label.nextAll( 'input' );
43
+
44
+ var $generalInput = $( 'input[type=text]', this.$el );
45
+ $generalInput.prop( 'required', false );
46
+
47
+ if ( $otherCheckbox.length && $otherCheckbox.is( ':checked' ) ) {
48
+ if ( $( e.target )[0] === $otherCheckbox[0] ) {
49
+ $otherInput.addClass( 'hf-show' ).trigger( 'focus' );
50
+ }
51
 
52
+ if ( '' === this.$el.data( 'happyforms-required' ) ) {
53
+ $otherInput.prop( 'required', true );
54
+ }
55
+ } else {
56
+ $generalInput.removeClass( 'hf-show' );
57
  }
58
+ },
59
+
60
+ serialize: function() {
61
+ var self = this;
62
+
63
+ var serialized = this.$input.map( function( i, input ) {
64
+ var $input = $( input );
65
+ var $customInput = 0;
66
+
67
+ if ( 'text' === $input.attr( 'type' ) ) {
68
+ return;
69
+ }
70
+
71
+ if ( 'text' === $( self.$input[i+1] ).attr( 'type' ) ) {
72
+ $customInput = $( self.$input[i+1] );
73
+ }
74
+
75
+ var keyValue = {
76
+ name: $input.attr( 'name' ),
77
+ value: $input.val()
78
+ };
79
+
80
+ if ( $customInput.length ) {
81
+ var otherValue = $customInput.val();
82
+
83
+ keyValue['value'] = [ $input.val(), otherValue ];
84
+
85
+ keyValue['value'] = JSON.stringify( keyValue['value'] );
86
+ }
87
+
88
+ if ( ! $input.is( ':checked' ) ) {
89
+ return;
90
+ }
91
+
92
+ return keyValue;
93
+ } ).toArray();
94
+
95
+ return serialized;
96
+ },
97
+
98
+ onFocus: function( e ) {
99
+ var $input = $( e.target );
100
+
101
+ if ( 'text' === $input.attr( 'type' ) ) {
102
+ $input.prevAll( 'input[type=checkbox]' ).prop( 'checked', true );
103
+ }
104
+ },
105
  };
106
 
107
+ } )( jQuery );
core/assets/js/frontend/long-text.js CHANGED
@@ -1,31 +1,156 @@
1
- ( function( $ ) {
2
 
3
  HappyForms.parts = HappyForms.parts || {};
4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  HappyForms.parts.multi_line_text = {
6
- init: function() {
 
 
7
  this.type = this.$el.data( 'happyforms-type' );
8
  this.$input = $( 'textarea', this.$el );
9
  this.$counter = $( '.happyforms-part__char-counter span.counter', this.$el );
10
 
11
- this.$input.on( 'blur', this.onBlur.bind( this ) );
12
- this.$input.on( 'keyup', this.triggerChange.bind( this ) );
13
- this.$input.on( 'change', this.triggerChange.bind( this ) );
 
 
 
 
 
 
 
14
 
15
- this.$el.on( 'keyup', this.refreshCounter.bind( this ) );
16
- this.refreshCounter();
 
 
17
 
 
 
18
  this.initTooltip();
19
  },
20
 
21
  getValueLength: function() {
22
  var mode = this.$input.attr( 'data-length-mode' );
23
  var value = this.$input.val();
 
 
 
 
 
 
 
 
24
  var length = value.length;
25
 
26
  if ( 'word' === mode ) {
27
  var matches = value.match( /\w+/g );
28
  length = matches ? matches.length : 0;
 
 
 
 
29
  }
30
 
31
  return length;
@@ -41,6 +166,192 @@
41
  var length = this.getValueLength();
42
  this.$counter.text( length );
43
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  };
45
 
46
- } )( jQuery );
1
+ ( function( $, editorSettings ) {
2
 
3
  HappyForms.parts = HappyForms.parts || {};
4
 
5
+ window._wpLink = {};
6
+
7
+ var defaultOnPostRender = function( editor, settings, e ) {
8
+ e.target.$el.html( $( '#' + settings.icon ).html() );
9
+ };
10
+
11
+ var GetButton = function( editor, settings ) {
12
+ var onPostRender = settings.onPostRender || defaultOnPostRender;
13
+
14
+ var button = Object.assign( {}, settings, {
15
+ onClick: settings.onClick.bind( this, editor ),
16
+ onPostRender: onPostRender.bind( this, editor, settings ),
17
+ } );
18
+
19
+ return button;
20
+ };
21
+
22
+ var buttons = {
23
+ hfbold: {
24
+ icon: 'happyforms-rich-text-icon-bold',
25
+ stateSelector: 'strong',
26
+
27
+ onClick: function ( editor ) {
28
+ editor.execCommand( 'mceToggleFormat', false, 'bold' );
29
+ },
30
+ },
31
+ hfitalic: {
32
+ icon: 'happyforms-rich-text-icon-italic',
33
+ stateSelector: 'em',
34
+
35
+ onClick: function ( editor ) {
36
+ editor.execCommand( 'mceToggleFormat', false, 'italic' );
37
+ },
38
+ },
39
+ hful: {
40
+ icon: 'happyforms-rich-text-icon-ul',
41
+ stateSelector: 'ul',
42
+
43
+ onClick: function ( editor ) {
44
+ editor.execCommand( 'InsertUnorderedList' );
45
+ },
46
+ },
47
+ hfol: {
48
+ icon: 'happyforms-rich-text-icon-ol',
49
+ stateSelector: 'ol',
50
+
51
+ onClick: function ( editor ) {
52
+ editor.execCommand( 'InsertOrderedList' );
53
+ },
54
+ },
55
+ hfquote: {
56
+ icon: 'happyforms-rich-text-icon-quote',
57
+ stateSelector: 'blockquote',
58
+
59
+ onClick: function ( editor ) {
60
+ editor.execCommand( 'mceToggleFormat', false, 'blockquote' );
61
+ },
62
+ },
63
+ hfcode: {
64
+ icon: 'happyforms-rich-text-icon-code',
65
+ stateSelector: 'pre',
66
+
67
+ onClick: function ( editor ) {
68
+ editor.execCommand( 'mceToggleFormat', false, 'pre' );
69
+ },
70
+ },
71
+ hfstrike: {
72
+ icon: 'happyforms-rich-text-icon-strike',
73
+ stateSelector: 'del',
74
+
75
+ onClick: function ( editor ) {
76
+ editor.execCommand( 'mceToggleFormat', false, 'strikethrough' );
77
+ },
78
+ },
79
+ hfunderline: {
80
+ icon: 'happyforms-rich-text-icon-underline',
81
+
82
+ onClick: function ( editor ) {
83
+ editor.execCommand( 'mceToggleFormat', false, 'underline' );
84
+ },
85
+
86
+ onPostRender: function( editor, settings, e ) {
87
+ defaultOnPostRender.apply( this, arguments );
88
+
89
+ var self = e.target;
90
+
91
+ editor.on( 'NodeChange', function( e ) {
92
+ self.active( 'underline' === e.element.style.textDecoration );
93
+ } );
94
+ },
95
+ },
96
+ hfhr: {
97
+ icon: 'happyforms-rich-text-icon-hr',
98
+
99
+ onClick: function ( editor ) {
100
+ editor.execCommand( 'InsertHorizontalRule' );
101
+ },
102
+ },
103
+ };
104
+
105
  HappyForms.parts.multi_line_text = {
106
+ init: function( options ) {
107
+ this.$form = options.form;
108
+ this.rich = this.$el.hasClass( 'happyforms-part--rich_text' );
109
  this.type = this.$el.data( 'happyforms-type' );
110
  this.$input = $( 'textarea', this.$el );
111
  this.$counter = $( '.happyforms-part__char-counter span.counter', this.$el );
112
 
113
+ if ( ! this.rich ) {
114
+ this.$input.on( 'blur', this.onBlur.bind( this ) );
115
+ this.$input.on( 'keyup', this.triggerChange.bind( this ) );
116
+ this.$input.on( 'change', this.triggerChange.bind( this ) );
117
+ this.$el.on( 'keyup', this.refreshCounter.bind( this ) );
118
+ this.refreshCounter();
119
+ $( '.happyforms-part__char-counter' ).show();
120
+ } else {
121
+ this.editorId = this.$input.attr( 'id' );
122
+ this.editor = null;
123
 
124
+ this.$el.on( 'happyforms.attach', this.editorCreate.bind( this ) );
125
+ this.$el.on( 'happyforms.detach', this.editorDestroy.bind( this ) );
126
+ this.$el.on( 'happyforms.formclass', this.onFormClassChange.bind( this ) );
127
+ this.$el.on( 'happyforms.cssvar', this.onCSSVarChange.bind( this ) );
128
 
129
+ this.editorCreate();
130
+ }
131
  this.initTooltip();
132
  },
133
 
134
  getValueLength: function() {
135
  var mode = this.$input.attr( 'data-length-mode' );
136
  var value = this.$input.val();
137
+
138
+ if ( this.rich ) {
139
+ value = this.editor.getContent( { format: 'text' } );
140
+ value = value.replace( /[\r\n]+/gm, ' ' );
141
+ value = value.replace(/\s+/g,' ');
142
+ value = value.trim();
143
+ }
144
+
145
  var length = value.length;
146
 
147
  if ( 'word' === mode ) {
148
  var matches = value.match( /\w+/g );
149
  length = matches ? matches.length : 0;
150
+ } else {
151
+ if ( this.rich && '\n' === value ) {
152
+ length --;
153
+ }
154
  }
155
 
156
  return length;
166
  var length = this.getValueLength();
167
  this.$counter.text( length );
168
  },
169
+
170
+ editorCreate: function() {
171
+ var self = this;
172
+
173
+ tinymce.init( {
174
+ selector: '#' + this.editorId,
175
+ branding: false,
176
+ menubar: false,
177
+ toolbar1: 'hfbold,hfitalic,hful,hfol,hfquote,link,unlink,hfcode,hfstrike,hfunderline,hfhr',
178
+ statusbar: true,
179
+ resize: true,
180
+ plugins: 'hr,paste',
181
+ content_css: editorSettings.contentCSS,
182
+ valid_elements: editorSettings.validElements,
183
+ paste_as_text: true,
184
+ inline_styles: false,
185
+ min_height: 50,
186
+ formats: {
187
+ underline: { inline: 'u', exact : true },
188
+ strikethrough: { inline: 'del', exact : true },
189
+ },
190
+
191
+ setup: function( editor ) {
192
+ var wpAddButton = editor.addButton;
193
+
194
+ // Remove tooltips
195
+ editor.addButton = function( id, options ) {
196
+ switch ( id ) {
197
+ case 'link':
198
+ case 'unlink':
199
+ case 'wp_link_apply':
200
+ case 'wp_link_edit':
201
+ case 'wp_link_remove':
202
+ delete options.tooltip;
203
+ break;
204
+ default:
205
+ break;
206
+ }
207
+
208
+ return wpAddButton.apply( this, arguments );
209
+ }
210
+
211
+ // Add custom buttons
212
+ for ( var button in buttons ) {
213
+ editor.addButton( button, GetButton( editor, buttons[button] ) );
214
+ }
215
+
216
+ // Inherit form class, fonts and css-vars
217
+ editor.on( 'init', function() {
218
+ self.editor = editor;
219
+
220
+ self.inheritFormClass();
221
+ self.inheritFonts();
222
+
223
+ var target = self.editor.dom.doc.querySelector( 'body' );
224
+ self.inheritCSSVars( editorSettings.cssVars, target );
225
+ self.refreshCounter();
226
+ $( '.happyforms-part__char-counter' ).show();
227
+ } );
228
+
229
+ // Custom main toolbar icons
230
+ editor.on( 'postrender', function() {
231
+ var $el = $( editor.container );
232
+
233
+ $( 'i.mce-i-link', $el ).replaceWith(
234
+ $( '#happyforms-rich-text-icon-link' ).html()
235
+ );
236
+
237
+ $( 'i.mce-i-unlink', $el ).replaceWith(
238
+ $( '#happyforms-rich-text-icon-unlink' ).html()
239
+ );
240
+
241
+ self.editor = editor;
242
+
243
+ var target = self.editor.dom.doc.querySelector( 'body' );
244
+ target.style.color = $( 'body' ).css( 'color' );
245
+ } );
246
+
247
+ editor.on( 'change', self.onEditorChange.bind( self ) );
248
+ editor.on( 'focus', self.onFocus.bind( self ) );
249
+ editor.on( 'blur', self.onBlur.bind( self ) );
250
+ editor.on( 'keyup', self.refreshCounter.bind( self ) );
251
+ },
252
+ } );
253
+ },
254
+
255
+ editorDestroy: function() {
256
+ var editor = tinymce.get( this.editorId );
257
+
258
+ if ( editor ) {
259
+ editor.remove();
260
+ }
261
+
262
+ self.editor = null;
263
+ },
264
+
265
+ onEditorChange: function() {
266
+ this.triggerChange();
267
+ },
268
+
269
+ onFocus: function() {
270
+ this.$el.addClass( 'focus' );
271
+ },
272
+
273
+ onBlur: function() {
274
+ this.$el.removeClass( 'focus' );
275
+ },
276
+
277
+ inheritFormClass: function() {
278
+ this.editor.dom.doc.documentElement.className = this.$form.attr( 'class' );
279
+ },
280
+
281
+ inheritCSSVars: function( vars, target ) {
282
+ var styles = getComputedStyle( this.$form[0] );
283
+
284
+ if ( 'object' !== typeof vars ) {
285
+ return;
286
+ }
287
+
288
+ vars.forEach( function( variable ) {
289
+ var value = styles.getPropertyValue( variable );
290
+ target.style.setProperty( variable, value );
291
+ } );
292
+ },
293
+
294
+ inheritFonts: function() {
295
+ var formStyle = getComputedStyle( this.$form[0] );
296
+ var stylesheets = [].slice.call( document.styleSheets );
297
+ var editorDocument = this.editor.dom.doc;
298
+
299
+ // Append parent stylesheets to editor
300
+ stylesheets
301
+ .map( function( stylesheet ) {
302
+ if ( ! stylesheet.ownerNode ) {
303
+ return null;
304
+ }
305
+
306
+ return stylesheet.ownerNode.cloneNode();
307
+ } )
308
+ .forEach( function( node ) {
309
+ if ( node ) {
310
+ editorDocument.head.insertBefore( node, editorDocument.head.firstChild );
311
+ }
312
+ } );
313
+
314
+ // Apply font-family
315
+ editorDocument.body.style.setProperty( 'font-family', formStyle.getPropertyValue( 'font-family' ) );
316
+ },
317
+
318
+ onFormClassChange: function( e, classes ) {
319
+ if ( this.editor ) {
320
+ this.editor.dom.doc.documentElement.className = classes;
321
+ }
322
+ },
323
+
324
+ onCSSVarChange: function( e, variable ) {
325
+ if ( this.editor ) {
326
+ this.editor.dom.doc.querySelector( 'body' ).style.setProperty( variable.name, variable.value );
327
+
328
+ $( '.happyforms-editor-toolbar-link' ).each( function() {
329
+ this.style.setProperty( variable.name, variable.value );
330
+ } );
331
+ }
332
+ },
333
+
334
+ isFilled: function() {
335
+ if ( this.rich ) {
336
+ var content = tinymce.get( this.editorId ).getContent();
337
+ return '' !== content;
338
+ }
339
+
340
+ return '' !== this.$input.val();
341
+ },
342
+
343
+ serialize: function() {
344
+ var serialized = HappyForms.parts.base.serialize.apply( this, arguments );
345
+
346
+ if ( this.rich ) {
347
+ serialized = [ {
348
+ name: this.$input.attr( 'name' ),
349
+ value: tinymce.get( this.editorId ).getContent(),
350
+ } ];
351
+ }
352
+
353
+ return serialized;
354
+ }
355
  };
356
 
357
+ } )( jQuery, _happyFormsRichTextSettings );
core/assets/js/frontend/number.js CHANGED
@@ -22,14 +22,16 @@
22
 
23
  if ( masked ) {
24
  var self = this;
 
 
25
 
26
  this.$input.each( function() {
27
  var $input = $( this );
28
 
29
  var cleave = new Cleave( $input, {
30
  numeral: true,
31
- numeralDecimalMark: this.numeralDecimalMark,
32
- delimiter: this.delimiter,
33
  } );
34
 
35
  self.cleaveInstances.push( cleave );
22
 
23
  if ( masked ) {
24
  var self = this;
25
+ var decimalMark = this.numeralDecimalMark;
26
+ var delimiterMark = this.delimiter;
27
 
28
  this.$input.each( function() {
29
  var $input = $( this );
30
 
31
  var cleave = new Cleave( $input, {
32
  numeral: true,
33
+ numeralDecimalMark: decimalMark,
34
+ delimiter: delimiterMark,
35
  } );
36
 
37
  self.cleaveInstances.push( cleave );
core/assets/js/frontend/radio.js ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ( function( $ ) {
2
+
3
+ HappyForms.parts = HappyForms.parts || {};
4
+
5
+ HappyForms.parts.radio = {
6
+ init: function() {
7
+ this.type = this.$el.data( 'happyforms-type' );
8
+ this.$input = $( 'input', this.$el );
9
+ this.$visualInput = $( 'input[type=email]', this.$el );
10
+
11
+ this.$input.on( 'keyup', this.triggerChange.bind( this ) );
12
+ this.$input.on( 'change', this.triggerChange.bind( this ) );
13
+ this.$input.on( 'change', this.onRadioChange.bind( this ) );
14
+ this.$input.on( 'focus', this.onFocus.bind( this ) );
15
+
16
+ this.initTooltip();
17
+ },
18
+
19
+ onRadioChange: function( e ) {
20
+ var $otherRadio = $( '.happyforms-part-option--other input[type=radio]', this.$el );
21
+ var $label = $otherRadio.parent();
22
+ var $otherInput = $label.nextAll( 'input' );
23
+
24
+ var $generalInput = $( 'input[type=text]', this.$el );
25
+ $generalInput.prop( 'required', false );
26
+
27
+ if ( $otherRadio.length && $otherRadio.is( ':checked' ) ) {
28
+ if ( $( e.target )[0] === $otherRadio[0] ) {
29
+ $otherInput.addClass( 'hf-show' ).trigger( 'focus' );
30
+ }
31
+
32
+ if ( '' === this.$el.data( 'happyforms-required' ) ) {
33
+ $otherInput.prop( 'required', true );
34
+ }
35
+ } else {
36
+ $generalInput.removeClass( 'hf-show' );
37
+ }
38
+ },
39
+
40
+ onFocus: function( e ) {
41
+ var $input = $( e.target );
42
+
43
+ if ( 'text' === $input.attr( 'type' ) ) {
44
+ $input.prevAll( 'input[type=radio]' ).prop( 'checked', true );
45
+ }
46
+ },
47
+
48
+ serialize: function() {
49
+ var self = this;
50
+
51
+ var serialized = this.$input.map( function( i, input ) {
52
+ var $input = $( input );
53
+ var $customInput = 0;
54
+
55
+ if ( 'text' === $input.attr( 'type' ) ) {
56
+ return;
57
+ }
58
+
59
+ if ( 'text' === $( self.$input[i+1] ).attr( 'type' ) ) {
60
+ $customInput = $( self.$input[i+1] );
61
+ }
62
+
63
+ var keyValue = {
64
+ name: $input.attr( 'name' ),
65
+ value: $input.val()
66
+ };
67
+
68
+ if ( $customInput.length ) {
69
+ var otherValue = $customInput.val();
70
+
71
+ keyValue['value'] = [ $input.val(), otherValue ];
72
+
73
+ keyValue['value'] = JSON.stringify( keyValue['value'] );
74
+ }
75
+
76
+ if ( ! $input.is( ':checked' ) ) {
77
+ return;
78
+ }
79
+
80
+ return keyValue;
81
+ } ).toArray();
82
+
83
+ return serialized;
84
+ }
85
+ };
86
+
87
+ } )( jQuery );
core/assets/js/frontend/select.js CHANGED
@@ -20,6 +20,47 @@
20
  this.$input.on( 'blur', this.onBlur.bind(this) );
21
  this.initTooltip();
22
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  };
24
 
25
  } )( jQuery );
20
  this.$input.on( 'blur', this.onBlur.bind(this) );
21
  this.initTooltip();
22
  },
23
+
24
+ onBlur: function() {
25
+ var $otherinput = $( '.happyforms-part-option--other input[type=text]', this.$el );
26
+ if ( '999' === this.$input.val() ) {
27
+ $otherinput.addClass( 'hf-show' );
28
+ $otherinput.focus();
29
+ } else {
30
+ $otherinput.removeClass( 'hf-show' );
31
+ }
32
+ },
33
+
34
+ serialize: function() {
35
+ var self = this;
36
+
37
+ var serialized = this.$input.map( function( i, input ) {
38
+ var $input = $( input, self.$el );
39
+ var $customInput = 0;
40
+
41
+ if ( 999 == $input.val() ) {
42
+ $customInput = $( ' .happyforms-part-option--other input[type=text]', self.$el );
43
+ }
44
+
45
+ var keyValue = {
46
+ name: $input.attr( 'name' ),
47
+ value: $input.val()
48
+ };
49
+
50
+ if ( $customInput.length ) {
51
+ var otherValue = $customInput.val();
52
+
53
+ keyValue['value'] = [ $input.val(), otherValue ];
54
+
55
+ keyValue['value'] = JSON.stringify( keyValue['value'] );
56
+ }
57
+
58
+ return keyValue;
59
+ } ).toArray();
60
+
61
+ return serialized;
62
+
63
+ }
64
  };
65
 
66
  } )( jQuery );
core/assets/js/parts/part-checkbox.js CHANGED
@@ -44,15 +44,86 @@
44
  'change [name=label]': 'onItemLabelChange',
45
  'keyup [name=description]': 'onItemDescriptionChange',
46
  'change [name=is_default]': 'onItemDefaultChange',
 
 
 
 
 
47
  },
48
 
49
  initialize: function( options ) {
50
  this.template = _.template( $( this.template ).text() );
51
  this.part = options.part;
52
 
 
 
53
  this.listenTo( this, 'ready', this.onReady );
54
  },
55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  render: function() {
57
  this.setElement( this.template( this.model.toJSON() ) );
58
 
@@ -144,6 +215,9 @@
144
  'change [name=display_type]': 'onDisplayTypeChange',
145
  'keyup [name=label]': 'onEnterKey',
146
  'keyup [name=description]': 'onEnterKey',
 
 
 
147
  } ),
148
 
149
  initialize: function() {
@@ -163,6 +237,13 @@
163
  this.listenTo( this.model, 'change:show_select_all', this.onSelectAllChange );
164
  this.listenTo( this.model, 'change:select_all_label', this.onSelectAllLabelChange );
165
  this.listenTo( this.model, 'change:options_width', this.onOptionsWidthChange );
 
 
 
 
 
 
 
166
  },
167
 
168
  onReady: function() {
@@ -429,7 +510,70 @@
429
  };
430
 
431
  happyForms.previewSend( 'happyforms-part-dom-update', data );
432
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  } );
434
 
435
  happyForms.previewer = _.extend( happyForms.previewer, {
@@ -498,6 +642,22 @@
498
 
499
  this.$( '.happyforms-part-option--select-all .label', $part ).text( part.get( 'select_all_label' ) );
500
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  } );
502
 
503
  } ) ( jQuery, _, Backbone, wp.customize, _happyFormsSettings );
44
  'change [name=label]': 'onItemLabelChange',
45
  'keyup [name=description]': 'onItemDescriptionChange',
46
  'change [name=is_default]': 'onItemDefaultChange',
47
+
48
+ 'change [name=limit_submissions]': 'onItemLimitSubmissionsChange',
49
+ 'keyup [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
50
+ 'change [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
51
+ 'change [name=show_submissions_amount]': 'onItemLimitShowSubmissionsAmountChange',
52
  },
53
 
54
  initialize: function( options ) {
55
  this.template = _.template( $( this.template ).text() );
56
  this.part = options.part;
57
 
58
+ this.listenTo( this.model, 'change:show_submissions_amount', this.onChangeShowSubmissions );
59
+ this.listenTo( this.model, 'change:limit_submissions_amount', this.onChangeMaxSubmissionsAmount );
60
  this.listenTo( this, 'ready', this.onReady );
61
  },
62
 
63
+ onItemLimitSubmissionsChange: function( e ) {
64
+ var isChecked = $( e.target ).is( ':checked' );
65
+
66
+ if ( ! isChecked ) {
67
+ this.model.set( 'show_submissions_amount', 0 );
68
+ $( "input[name='show_submissions_amount']", this.$el ).prop('checked',false);;
69
+ }
70
+
71
+ this.model.set( 'limit_submissions', isChecked ? 1 : 0 );
72
+ $( '.happyforms-part-item-limit-submission-settings', this.$el ).toggle();
73
+ },
74
+
75
+ onChangeShowSubmissions: function( e ) {
76
+
77
+ var model = this.part;
78
+
79
+ this.part.fetchHtml( function( response ) {
80
+ var data = {
81
+ id: model.get( 'id' ),
82
+ html: response,
83
+ };
84
+
85
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
86
+
87
+ } );
88
+ },
89
+
90
+ onChangeMaxSubmissionsAmount: function( e ) {
91
+
92
+ var model = this.part;
93
+
94
+ if ( 1 != this.model.get('show_submissions_amount') ) {
95
+ return;
96
+ }
97
+
98
+ this.part.fetchHtml( function( response ) {
99
+ var data = {
100
+ id: model.get( 'id' ),
101
+ html: response,
102
+ };
103
+
104
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
105
+
106
+ } );
107
+ },
108
+
109
+ onItemLimitSubmissionsAmountChange: function( e ) {
110
+ this.model.set( 'limit_submissions_amount', $( e.target ).val() );
111
+ this.part.trigger( 'change' );
112
+ },
113
+
114
+ onItemLimitSubmissionsAmountChange: function( e ) {
115
+ this.model.set( 'limit_submissions_amount', $( e.target ).val() );
116
+ this.part.trigger( 'change' );
117
+ },
118
+
119
+ onItemLimitShowSubmissionsAmountChange: function( e ) {
120
+ var isChecked = $( e.target ).is( ':checked' );
121
+
122
+ this.model.set( 'show_submissions_amount', isChecked ? "1" : 0 );
123
+ this.part.trigger( 'change' );
124
+ },
125
+
126
+
127
  render: function() {
128
  this.setElement( this.template( this.model.toJSON() ) );
129
 
215
  'change [name=display_type]': 'onDisplayTypeChange',
216
  'keyup [name=label]': 'onEnterKey',
217
  'keyup [name=description]': 'onEnterKey',
218
+
219
+ 'change [data-bind=limit_choices_min]': 'refreshMinMaxChoices',
220
+ 'change [data-bind=limit_choices_max]': 'refreshMinMaxChoices',
221
  } ),
222
 
223
  initialize: function() {
237
  this.listenTo( this.model, 'change:show_select_all', this.onSelectAllChange );
238
  this.listenTo( this.model, 'change:select_all_label', this.onSelectAllLabelChange );
239
  this.listenTo( this.model, 'change:options_width', this.onOptionsWidthChange );
240
+
241
+ this.listenTo( this.model, 'change:other_option', this.onAddOtherOption );
242
+ this.listenTo( this.model, 'change:other_option_label', this.onOtherOptionLabelChange );
243
+ this.listenTo( this.model, 'change:other_option_placeholder', this.onOtherOptionPlaceholderChange );
244
+ this.listenTo( this.model, 'change:display_as_letters', this.refreshPart );
245
+ this.listenTo( this.model, 'change:limit_choices', this.onLimitChoices );
246
+ this.listenTo( this.model.get( 'options' ), 'add remove', this.refreshMinMaxChoices );
247
  },
248
 
249
  onReady: function() {
510
  };
511
 
512
  happyForms.previewSend( 'happyforms-part-dom-update', data );
513
+ },
514
+
515
+ onAddOtherOption: function( model, value ) {
516
+ var $otherOptionOptions = $( '.happyforms-nested-settings[data-trigger="other_option"]', this.$el );
517
+
518
+ if ( 1 == value ) {
519
+ $otherOptionOptions.show();
520
+ } else {
521
+ $otherOptionOptions.hide();
522
+ }
523
+
524
+ this.refreshPart();
525
+ },
526
+
527
+ onOtherOptionLabelChange: function( model, value ) {
528
+ var data = {
529
+ id: this.model.get( 'id' ),
530
+ callback: 'onCheckboxOtherOptionLabelChangeCallback'
531
+ };
532
+
533
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
534
+ },
535
+
536
+ onOtherOptionPlaceholderChange: function( model, value ) {
537
+ var data = {
538
+ id: this.model.get( 'id' ),
539
+ callback: 'onCheckboxOtherOptionPlaceholderChangeCallback'
540
+ };
541
+
542
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
543
+ },
544
+
545
+ onLimitChoices: function( model, value ) {
546
+ var $limitChoicesOptions = $( '.happyforms-nested-settings[data-trigger="limit_choices"]', this.$el );
547
+
548
+ if ( 1 == value ) {
549
+ $limitChoicesOptions.show();
550
+ } else {
551
+ $limitChoicesOptions.hide();
552
+ }
553
+ },
554
+
555
+ refreshMinMaxChoices: function() {
556
+ var minChoices = this.model.get( 'limit_choices_min' );
557
+ var maxChoices = this.model.get( 'limit_choices_max' );
558
+ var numChoices = this.model.get( 'options' ).length;
559
+
560
+ var clamp = function( v, min, max ) {
561
+ return Math.min( Math.max( v, min ), max );
562
+ };
563
+
564
+ minChoices = clamp( minChoices, numChoices > 1 ? 2 : 1, minChoices );
565
+ minChoices = clamp( minChoices, minChoices, numChoices );
566
+ maxChoices = clamp( maxChoices, minChoices, numChoices );
567
+
568
+ this.model.set( 'limit_choices_min', minChoices );
569
+ this.model.set( 'limit_choices_max', maxChoices );
570
+
571
+ var $limitMinChoice = $( '[data-trigger="limit_choices_min"]', this.$el );
572
+ var $limitMaxChoice = $( '[data-trigger="limit_choices_max"]', this.$el );
573
+
574
+ $limitMinChoice.val( minChoices );
575
+ $limitMaxChoice.val( maxChoices );
576
+ },
577
  } );
578
 
579
  happyForms.previewer = _.extend( happyForms.previewer, {
642
 
643
  this.$( '.happyforms-part-option--select-all .label', $part ).text( part.get( 'select_all_label' ) );
644
  },
645
+
646
+ onCheckboxOtherOptionLabelChangeCallback: function( id, html, options ) {
647
+ var part = this.getPartModel( id );
648
+ var $part = this.getPartElement( html );
649
+ var $otherOptionLabel = $( '.happyforms-part-option--other .label', $part );
650
+
651
+ $otherOptionLabel.text( part.get( 'other_option_label' ) );
652
+ },
653
+
654
+ onCheckboxOtherOptionPlaceholderChangeCallback: function( id, html, options ) {
655
+ var part = this.getPartModel( id );
656
+ var $part = this.getPartElement( html );
657
+ var $otherOptionInput = $( '.happyforms-part-option--other input[type=text]', $part );
658
+
659
+ $otherOptionInput.attr( 'placeholder', part.get( 'other_option_placeholder' ) );
660
+ },
661
  } );
662
 
663
  } ) ( jQuery, _, Backbone, wp.customize, _happyFormsSettings );
core/assets/js/parts/part-multi-line-text.js CHANGED
@@ -23,6 +23,9 @@
23
  this.listenTo( this.model, 'change:characters_label', this.onLimitWordsCharactersLabelChange );
24
  this.listenTo( this.model, 'change:words_label', this.onLimitWordsCharactersLabelChange );
25
  this.listenTo( this.model, 'change:rows', this.onRowsChange );
 
 
 
26
  },
27
 
28
  /**
@@ -133,6 +136,46 @@
133
  };
134
 
135
  happyForms.previewSend( 'happyforms-part-dom-update', data );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  }
137
  } );
138
 
23
  this.listenTo( this.model, 'change:characters_label', this.onLimitWordsCharactersLabelChange );
24
  this.listenTo( this.model, 'change:words_label', this.onLimitWordsCharactersLabelChange );
25
  this.listenTo( this.model, 'change:rows', this.onRowsChange );
26
+
27
+ this.listenTo( this.model, 'change:rich_text', this.onRichTextChange );
28
+ this.listenTo( this.model, 'change:rows', this.onRowsChange );
29
  },
30
 
31
  /**
136
  };
137
 
138
  happyForms.previewSend( 'happyforms-part-dom-update', data );
139
+ },
140
+
141
+ onRichTextChange: function( model, value ) {
142
+ if ( 1 == value ) {
143
+ $( '.happyforms-placeholder-option', this.$el ).hide();
144
+ } else {
145
+ $( '.happyforms-placeholder-option', this.$el ).show();
146
+ }
147
+
148
+ model.fetchHtml( function( response ) {
149
+ var data = {
150
+ id: model.get( 'id' ),
151
+ html: response,
152
+ };
153
+
154
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
155
+ } );
156
+ },
157
+
158
+ onRowsChange: function( model, value ) {
159
+ if ( model.get( 'rich_text' ) ) {
160
+ model.fetchHtml( function( response ) {
161
+ var data = {
162
+ id: model.get( 'id' ),
163
+ html: response,
164
+ };
165
+
166
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
167
+ } );
168
+ } else {
169
+ var data = {
170
+ id: this.model.id,
171
+ callback: 'onMultiLineTextRowsChange',
172
+ options: {
173
+ value: value
174
+ }
175
+ };
176
+
177
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
178
+ }
179
  }
180
  } );
181
 
core/assets/js/parts/part-radio.js CHANGED
@@ -45,6 +45,11 @@
45
  'change [name=label]': 'onItemLabelChange',
46
  'keyup [name=description]': 'onItemDescriptionChange',
47
  'change [name=is_default]': 'onItemDefaultChange',
 
 
 
 
 
48
  },
49
 
50
  initialize: function( options ) {
@@ -52,6 +57,9 @@
52
  this.part = options.part;
53
 
54
  this.listenTo( this, 'ready', this.onReady );
 
 
 
55
  },
56
 
57
  render: function() {
@@ -130,6 +138,65 @@
130
 
131
  happyForms.previewSend( 'happyforms-part-dom-update', data );
132
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  } );
134
 
135
  happyForms.classes.views.parts.radio = happyForms.classes.views.Part.extend( {
@@ -160,6 +227,11 @@
160
  this.listenTo( this, 'sort-stop', this.onOptionSortStop );
161
  this.listenTo( this, 'ready', this.onReady );
162
  this.listenTo( this.model, 'change:options_width', this.onOptionsWidthChange );
 
 
 
 
 
163
  },
164
 
165
  onReady: function() {
@@ -394,6 +466,37 @@
394
  $textarea.val( '' );
395
  $( '.add-options', this.$el ).trigger( 'click' );
396
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  } );
398
 
399
  happyForms.previewer = _.extend( happyForms.previewer, {
@@ -454,6 +557,22 @@
454
  $part.removeClass( 'happyforms-part-options-width--full' );
455
 
456
  $part.addClass( 'happyforms-part-options-width--' + part.get( 'options_width' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
457
  }
458
  } );
459
 
45
  'change [name=label]': 'onItemLabelChange',
46
  'keyup [name=description]': 'onItemDescriptionChange',
47
  'change [name=is_default]': 'onItemDefaultChange',
48
+
49
+ 'change [name=limit_submissions]': 'onItemLimitSubmissionsChange',
50
+ 'keyup [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
51
+ 'change [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
52
+ 'change [name=show_submissions_amount]': 'onItemLimitShowSubmissionsAmountChange',
53
  },
54
 
55
  initialize: function( options ) {
57
  this.part = options.part;
58
 
59
  this.listenTo( this, 'ready', this.onReady );
60
+
61
+ this.listenTo( this.model, 'change:show_submissions_amount', this.onChangeShowSubmissions );
62
+ this.listenTo( this.model, 'change:limit_submissions_amount', this.onChangeMaxSubmissionsAmount );
63
  },
64
 
65
  render: function() {
138
 
139
  happyForms.previewSend( 'happyforms-part-dom-update', data );
140
  },
141
+
142
+ onItemLimitSubmissionsChange: function( e ) {
143
+ var isChecked = $( e.target ).is( ':checked' );
144
+
145
+ if ( ! isChecked ) {
146
+ this.model.set( 'show_submissions_amount', 0 );
147
+ $( "input[name='show_submissions_amount']", this.$el ).prop('checked',false);;
148
+ }
149
+
150
+ this.model.set( 'limit_submissions', isChecked ? 1 : 0 );
151
+ $( '.happyforms-part-item-limit-submission-settings', this.$el ).toggle();
152
+ },
153
+
154
+ onChangeShowSubmissions: function( e ) {
155
+
156
+ var model = this.part;
157
+
158
+ this.part.fetchHtml( function( response ) {
159
+ var data = {
160
+ id: model.get( 'id' ),
161
+ html: response,
162
+ };
163
+
164
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
165
+
166
+ } );
167
+ },
168
+
169
+ onChangeMaxSubmissionsAmount: function( e ) {
170
+
171
+ var model = this.part;
172
+
173
+ if ( 1 != this.model.get('show_submissions_amount') ) {
174
+ return;
175
+ }
176
+
177
+ this.part.fetchHtml( function( response ) {
178
+ var data = {
179
+ id: model.get( 'id' ),
180
+ html: response,
181
+ };
182
+
183
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
184
+
185
+ } );
186
+ },
187
+
188
+ onItemLimitSubmissionsAmountChange: function( e ) {
189
+ this.model.set( 'limit_submissions_amount', $( e.target ).val() );
190
+ this.part.trigger( 'change' );
191
+ },
192
+
193
+ onItemLimitShowSubmissionsAmountChange: function( e ) {
194
+ var isChecked = $( e.target ).is( ':checked' );
195
+
196
+ this.model.set( 'show_submissions_amount', isChecked ? "1" : 0 );
197
+ this.part.trigger( 'change' );
198
+ },
199
+
200
  } );
201
 
202
  happyForms.classes.views.parts.radio = happyForms.classes.views.Part.extend( {
227
  this.listenTo( this, 'sort-stop', this.onOptionSortStop );
228
  this.listenTo( this, 'ready', this.onReady );
229
  this.listenTo( this.model, 'change:options_width', this.onOptionsWidthChange );
230
+
231
+ this.listenTo( this.model, 'change:other_option', this.onAddOtherOption );
232
+ this.listenTo( this.model, 'change:other_option_label', this.onOtherOptionLabelChange );
233
+ this.listenTo( this.model, 'change:other_option_placeholder', this.onOtherOptionPlaceholderChange );
234
+ this.listenTo( this.model, 'change:display_as_letters', this.refreshPart );
235
  },
236
 
237
  onReady: function() {
466
  $textarea.val( '' );
467
  $( '.add-options', this.$el ).trigger( 'click' );
468
  },
469
+
470
+ onAddOtherOption: function( model, value ) {
471
+ var $otherOptionOptions = $( '.happyforms-nested-settings[data-trigger="other_option"]', this.$el );
472
+
473
+ if ( 1 == value ) {
474
+ $otherOptionOptions.show();
475
+ } else {
476
+ $otherOptionOptions.hide();
477
+ }
478
+
479
+ this.refreshPart();
480
+ },
481
+
482
+ onOtherOptionLabelChange: function() {
483
+ var data = {
484
+ id: this.model.get( 'id' ),
485
+ callback: 'onRadioOtherOptionLabelChangeCallback'
486
+ };
487
+
488
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
489
+ },
490
+
491
+ onOtherOptionPlaceholderChange: function() {
492
+ var data = {
493
+ id: this.model.get( 'id' ),
494
+ callback: 'onRadioOtherOptionPlaceholderChangeCallback'
495
+ };
496
+
497
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
498
+ },
499
+
500
  } );
501
 
502
  happyForms.previewer = _.extend( happyForms.previewer, {
557
  $part.removeClass( 'happyforms-part-options-width--full' );
558
 
559
  $part.addClass( 'happyforms-part-options-width--' + part.get( 'options_width' ) );
560
+ },
561
+
562
+ onRadioOtherOptionLabelChangeCallback: function( id, html, options ) {
563
+ var part = this.getPartModel( id );
564
+ var $part = this.getPartElement( html );
565
+ var $otherOptionLabel = $( '.happyforms-part-option--other .label', $part );
566
+
567
+ $otherOptionLabel.text( part.get( 'other_option_label' ) );
568
+ },
569
+
570
+ onRadioOtherOptionPlaceholderChangeCallback: function( id, html, options ) {
571
+ var part = this.getPartModel( id );
572
+ var $part = this.getPartElement( html );
573
+ var $otherOptionInput = $( '.happyforms-part-option--other input[type=text]', $part );
574
+
575
+ $otherOptionInput.attr( 'placeholder', part.get( 'other_option_placeholder' ) );
576
  }
577
  } );
578
 
core/assets/js/parts/part-select.js CHANGED
@@ -43,6 +43,12 @@
43
  'keyup [name=label]': 'onItemLabelKeyup',
44
  'change [name=label]': 'onItemLabelChange',
45
  'change [name=is_default]': 'onItemDefaultChange',
 
 
 
 
 
 
46
  },
47
 
48
  initialize: function( options ) {
@@ -50,6 +56,9 @@
50
  this.part = options.part;
51
 
52
  this.listenTo( this, 'ready', this.onReady );
 
 
 
53
  },
54
 
55
  render: function() {
@@ -121,6 +130,64 @@
121
 
122
  happyForms.previewSend( 'happyforms-part-dom-update', data );
123
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  } );
125
 
126
  happyForms.classes.views.parts.select = happyForms.classes.views.Part.extend( {
@@ -135,6 +202,7 @@
135
  'keyup [name=label]': 'onEnterKey',
136
  'keyup [name=description]': 'onEnterKey',
137
 
 
138
  } ),
139
 
140
  initialize: function() {
@@ -153,6 +221,10 @@
153
  this.listenTo( this.optionViews, 'reset', this.onOptionViewsSorted );
154
  this.listenTo( this, 'sort-stop', this.onOptionSortStop );
155
  this.listenTo( this, 'ready', this.onReady );
 
 
 
 
156
  },
157
 
158
  onReady: function() {
@@ -395,6 +467,61 @@
395
  $( '.add-options', this.$el ).trigger( 'click' );
396
  },
397
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
398
  } );
399
 
400
  happyForms.previewer = _.extend( happyForms.previewer, {
@@ -443,6 +570,23 @@
443
  $( '.happyforms-custom-select-dropdown__placeholder', $part ).removeClass('preview-hidden').text( options.label );
444
  },
445
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
  } );
447
 
448
  } ) ( jQuery, _, Backbone, wp.customize, _happyFormsSettings );
43
  'keyup [name=label]': 'onItemLabelKeyup',
44
  'change [name=label]': 'onItemLabelChange',
45
  'change [name=is_default]': 'onItemDefaultChange',
46
+
47
+
48
+ 'change [name=limit_submissions]': 'onItemLimitSubmissionsChange',
49
+ 'keyup [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
50
+ 'change [name=limit_submissions_amount]': 'onItemLimitSubmissionsAmountChange',
51
+ 'change [name=show_submissions_amount]': 'onItemLimitShowSubmissionsAmountChange',
52
  },
53
 
54
  initialize: function( options ) {
56
  this.part = options.part;
57
 
58
  this.listenTo( this, 'ready', this.onReady );
59
+
60
+ this.listenTo( this.model, 'change:show_submissions_amount', this.onChangeShowSubmissions );
61
+ this.listenTo( this.model, 'change:limit_submissions_amount', this.onChangeMaxSubmissionsAmount );
62
  },
63
 
64
  render: function() {
130
 
131
  happyForms.previewSend( 'happyforms-part-dom-update', data );
132
  },
133
+
134
+ onItemLimitSubmissionsChange: function( e ) {
135
+ var isChecked = $( e.target ).is( ':checked' );
136
+
137
+ if ( ! isChecked ) {
138
+ this.model.set( 'show_submissions_amount', 0 );
139
+ $( "input[name='show_submissions_amount']", this.$el ).prop('checked',false);;
140
+ }
141
+
142
+ this.model.set( 'limit_submissions', isChecked ? 1 : 0 );
143
+ $( '.happyforms-part-item-limit-submission-settings', this.$el ).toggle();
144
+ },
145
+
146
+ onItemLimitSubmissionsAmountChange: function( e ) {
147
+ this.model.set( 'limit_submissions_amount', $( e.target ).val() );
148
+ this.part.trigger( 'change' );
149
+ },
150
+
151
+ onItemLimitShowSubmissionsAmountChange: function( e ) {
152
+ var isChecked = $( e.target ).is( ':checked' );
153
+
154
+ this.model.set( 'show_submissions_amount', isChecked ? "1" : 0 );
155
+ this.part.trigger( 'change' );
156
+ },
157
+
158
+ onChangeShowSubmissions: function( e ) {
159
+
160
+ var model = this.part;
161
+
162
+ this.part.fetchHtml( function( response ) {
163
+ var data = {
164
+ id: model.get( 'id' ),
165
+ html: response,
166
+ };
167
+
168
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
169
+
170
+ } );
171
+ },
172
+
173
+ onChangeMaxSubmissionsAmount: function( e ) {
174
+
175
+ var model = this.part;
176
+
177
+ if ( 1 != this.model.get('show_submissions_amount') ) {
178
+ return;
179
+ }
180
+
181
+ this.part.fetchHtml( function( response ) {
182
+ var data = {
183
+ id: model.get( 'id' ),
184
+ html: response,
185
+ };
186
+
187
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
188
+
189
+ } );
190
+ },
191
  } );
192
 
193
  happyForms.classes.views.parts.select = happyForms.classes.views.Part.extend( {
202
  'keyup [name=label]': 'onEnterKey',
203
  'keyup [name=description]': 'onEnterKey',
204
 
205
+ 'change [data-bind=other_option_label]': 'onOtherSelectOptionLabelChange',
206
  } ),
207
 
208
  initialize: function() {
221
  this.listenTo( this.optionViews, 'reset', this.onOptionViewsSorted );
222
  this.listenTo( this, 'sort-stop', this.onOptionSortStop );
223
  this.listenTo( this, 'ready', this.onReady );
224
+
225
+ this.listenTo( this.model, 'change:other_option', this.onAddOtherOption );
226
+ this.listenTo( this.model, 'change:other_option_label', this.onOtherOptionLabelChange );
227
+ this.listenTo( this.model, 'change:other_option_placeholder', this.onOtherOptionPlaceholderChange );
228
  },
229
 
230
  onReady: function() {
467
  $( '.add-options', this.$el ).trigger( 'click' );
468
  },
469
 
470
+ onAddOtherOption: function( model, value ) {
471
+ var $otherOptionOptions = $( '.happyforms-nested-settings[data-trigger="other_option"]', this.$el );
472
+ var model = this.model;
473
+
474
+ if ( 1 == value ) {
475
+ $otherOptionOptions.show();
476
+ } else {
477
+ $otherOptionOptions.hide();
478
+ }
479
+
480
+ this.model.trigger( 'change' );
481
+
482
+ this.model.fetchHtml( function( response ) {
483
+ var data = {
484
+ id: model.get( 'id' ),
485
+ html: response,
486
+ };
487
+
488
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
489
+ } );
490
+ },
491
+
492
+ onOtherSelectOptionLabelChange: function( model, value ) {
493
+ this.model.trigger( 'change' );
494
+
495
+ var model = this.model;
496
+
497
+ this.model.fetchHtml( function( response ) {
498
+ var data = {
499
+ id: model.get( 'id' ),
500
+ html: response,
501
+ };
502
+
503
+ happyForms.previewSend( 'happyforms-form-part-refresh', data );
504
+ } );
505
+ },
506
+
507
+ onOtherOptionLabelChange: function() {
508
+ var data = {
509
+ id: this.model.get( 'id' ),
510
+ callback: 'onSelectOtherOptionLabelChangeCallback'
511
+ };
512
+
513
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
514
+ },
515
+
516
+ onOtherOptionPlaceholderChange: function() {
517
+ var data = {
518
+ id: this.model.get( 'id' ),
519
+ callback: 'onSelectOtherOptionPlaceholderChangeCallback'
520
+ };
521
+
522
+ happyForms.previewSend( 'happyforms-part-dom-update', data );
523
+ },
524
+
525
  } );
526
 
527
  happyForms.previewer = _.extend( happyForms.previewer, {
570
  $( '.happyforms-custom-select-dropdown__placeholder', $part ).removeClass('preview-hidden').text( options.label );
571
  },
572
 
573
+ onSelectOtherOptionLabelChangeCallback: function( id, html, options ) {
574
+ var part = this.getPartModel( id );
575
+ var $part = this.getPartElement( html );
576
+ var $otherOptionLabel = $( '.happyforms-custom-select-dropdown [data-value="999"]', $part );
577
+
578
+ $otherOptionLabel.text( part.get( 'other_option_label' ) );
579
+ $otherOptionLabel.attr( 'data-label', part.get( 'other_option_label' ) );
580
+ },
581
+
582
+ onSelectOtherOptionPlaceholderChangeCallback: function( id, html, options ) {
583
+ var part = this.getPartModel( id );
584
+ var $part = this.getPartElement( html );
585
+ var $otherOptionInput = $( '.happyforms-part-option--other input[type=text]', $part );
586
+
587
+ $otherOptionInput.attr( 'placeholder', part.get( 'other_option_placeholder' ) );
588
+ }
589
+
590
  } );
591
 
592
  } ) ( jQuery, _, Backbone, wp.customize, _happyFormsSettings );
core/classes/class-block.php CHANGED
@@ -82,10 +82,12 @@ class HappyForms_Block {
82
  }
83
 
84
  public function enqueue_scripts() {
 
 
85
  wp_enqueue_script(
86
  'happyforms-block',
87
  happyforms_get_plugin_url() . 'core/assets/js/admin/block.js',
88
- array( 'wp-blocks', 'wp-element', 'wp-components', 'wp-editor', 'jquery' )
89
  );
90
 
91
  $user_id = get_current_user_id();
@@ -94,7 +96,6 @@ class HappyForms_Block {
94
  $forms = array_values( wp_list_filter( $forms, array( 'post_status' => 'publish' ) ) );
95
  $forms = wp_list_pluck( $forms, 'post_title', 'ID' );
96
 
97
-
98
  $block_properties = $this->get_properties();
99
  $data = array(
100
  'ajaxurl' => admin_url( 'admin-ajax.php' ),
82
  }
83
 
84
  public function enqueue_scripts() {
85
+ $screen = get_current_screen();
86
+
87
  wp_enqueue_script(
88
  'happyforms-block',
89
  happyforms_get_plugin_url() . 'core/assets/js/admin/block.js',
90
+ array( 'wp-blocks', 'wp-element', 'wp-components', 'wp-block-editor', 'jquery' )
91
  );
92
 
93
  $user_id = get_current_user_id();
96
  $forms = array_values( wp_list_filter( $forms, array( 'post_status' => 'publish' ) ) );
97
  $forms = wp_list_pluck( $forms, 'post_title', 'ID' );
98
 
 
99
  $block_properties = $this->get_properties();
100
  $data = array(
101
  'ajaxurl' => admin_url( 'admin-ajax.php' ),
core/classes/class-form-controller.php CHANGED
@@ -96,6 +96,7 @@ class HappyForms_Form_Controller {
96
  'capability_type' => 'page',
97
  'has_archive' => false,
98
  'hierarchical' => false,
 
99
  'supports' => array( 'author', 'custom-fields' ),
100
  );
101
 
@@ -834,6 +835,28 @@ class HappyForms_Form_Controller {
834
  return $steps;
835
  }
836
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
837
  public function part_error_class( $classes, $part, $form ) {
838
  $part_name = happyforms_get_part_name( $part, $form );
839
  $notices = happyforms_get_session()->get_messages( $part_name );
96
  'capability_type' => 'page',
97
  'has_archive' => false,
98
  'hierarchical' => false,
99
+ 'can_export' => false,
100
  'supports' => array( 'author', 'custom-fields' ),
101
  );
102
 
835
  return $steps;
836
  }
837
 
838
+ public function count_by_option( $form_id, $option_id, $limit ) {
839
+ global $wpdb;
840
+
841
+ $meta_key = happyforms_get_option_counter_meta_key( $form_id, $option_id );
842
+
843
+ $query = $wpdb->prepare( "
844
+ SELECT COUNT(*) FROM (
845
+ SELECT p.ID FROM $wpdb->posts p
846
+ JOIN $wpdb->postmeta s ON p.ID = s.post_id AND s.meta_key = '_happyforms_read' AND s.meta_value != 2
847
+ JOIN $wpdb->postmeta c ON p.ID = c.post_id AND c.meta_key = %s
848
+ WHERE p.post_type = 'happyforms-message'
849
+ AND p.post_status = 'publish'
850
+ LIMIT 0, %d
851
+ ) AS r;
852
+ ", $meta_key, $limit );
853
+
854
+ $count = $wpdb->get_col( $query );
855
+ $count = empty( $count ) ? 0 : intval( $count[0] );
856
+
857
+ return $count;
858
+ }
859
+
860
  public function part_error_class( $classes, $part, $form ) {
861
  $part_name = happyforms_get_part_name( $part, $form );
862
  $notices = happyforms_get_session()->get_messages( $part_name );
core/classes/class-form-messages.php CHANGED
@@ -64,6 +64,18 @@ class HappyForms_Form_Messages {
64
  'default' => __( '', 'happyforms' ),
65
  'sanitize' => 'sanitize_text_field'
66
  ),
 
 
 
 
 
 
 
 
 
 
 
 
67
  );
68
 
69
  $fields = apply_filters( 'happyforms_messages_fields', $fields );
@@ -115,6 +127,16 @@ class HappyForms_Form_Messages {
115
  8000 => array (
116
  'type' => 'group_end',
117
  ),
 
 
 
 
 
 
 
 
 
 
118
 
119
  // individual controls
120
  4300 => array(
@@ -162,6 +184,11 @@ class HappyForms_Form_Messages {
162
  'label' => __( 'Question is required', 'happyforms' ),
163
  'field' => 'required_field_label',
164
  ),
 
 
 
 
 
165
  );
166
 
167
  $controls = happyforms_safe_array_merge( array(), $controls );
64
  'default' => __( '', 'happyforms' ),
65
  'sanitize' => 'sanitize_text_field'
66
  ),
67
+ 'select_less_choices' => array(
68
+ 'default' => __( 'Oops. Too many choices are selected.', 'happyforms' ),
69
+ 'sanitize' => 'sanitize_text_field',
70
+ ),
71
+ 'select_more_choices' => array(
72
+ 'default' => __( 'Oops. Not enough choices are selected.', 'happyforms' ),
73
+ 'sanitize' => 'sanitize_text_field',
74
+ ),
75
+ 'submissions_left_label' => array(
76
+ 'default' => __( 'Remaining', 'happyforms' ),
77
+ 'sanitize' => 'sanitize_text_field',
78
+ )
79
  );
80
 
81
  $fields = apply_filters( 'happyforms_messages_fields', $fields );
127
  8000 => array (
128
  'type' => 'group_end',
129
  ),
130
+ 4260 => array(
131
+ 'type' => 'text',
132
+ 'label' => __( "Too many choices are selected", 'happyforms' ),
133
+ 'field' => 'select_less_choices',
134
+ ),
135
+ 4280 => array(
136
+ 'type' => 'text',
137
+ 'label' => __( "Not enough choices are selected", 'happyforms' ),
138
+ 'field' => 'select_more_choices',
139
+ ),
140
 
141
  // individual controls
142
  4300 => array(
184
  'label' => __( 'Question is required', 'happyforms' ),
185
  'field' => 'required_field_label',
186
  ),
187
+ 6222 => array(
188
+ 'type' => 'text',
189
+ 'label' => __( 'Remaining submissions', 'happyforms' ),
190
+ 'field' => 'submissions_left_label',
191
+ )
192
  );
193
 
194
  $controls = happyforms_safe_array_merge( array(), $controls );
core/classes/class-form-option-limiter.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class HappyForms_Form_Option_Limiter {
3
+
4
+ private static $instance;
5
+
6
+ private $part_types;
7
+
8
+ public static function instance() {
9
+ if ( is_null( self::$instance ) ) {
10
+ self::$instance = new self();
11
+ }
12
+
13
+ self::$instance->hook();
14
+
15
+ return self::$instance;
16
+ }
17
+
18
+ public function __construct() {
19
+ $this->part_types = array(
20
+ 'radio',
21
+ 'checkbox',
22
+ 'select',
23
+ 'table',
24
+ );
25
+ }
26
+
27
+ public function hook() {
28
+ add_filter( 'happyforms_part_options', array( $this, 'get_part_options' ), 10, 3 );
29
+ add_filter( 'happyforms_get_form_parts', array( $this, 'get_form_parts' ), 10, 2 );
30
+ add_action( 'happyforms_submission_success', array( $this, 'submission_success' ), 10, 3 );
31
+ }
32
+
33
+ public function get_option_fields() {
34
+ $defaults = array(
35
+ 'limit_submissions' => 0,
36
+ 'limit_submissions_amount' => 1,
37
+ 'show_submissions_amount' => 0,
38
+ 'submissions_left' => 0,
39
+ 'submissions_left_label' => '',
40
+ );
41
+
42
+ return $defaults;
43
+ }
44
+
45
+ public function get_part_options( $options, $part, $form ) {
46
+ $option_defaults = $this->get_option_fields();
47
+
48
+ // Calculate remaining choices
49
+ $options = array_map( function( $option ) use( $option_defaults, $form ) {
50
+ $option = wp_parse_args( $option, $option_defaults );
51
+
52
+ if ( intval( $option['limit_submissions'] ) ) {
53
+ $limit = intval( $option['limit_submissions_amount'] );
54
+ $count = happyforms_get_form_controller()->count_by_option( $form['ID'], $option['id'], $limit );
55
+ $option['submissions_left'] = $limit - $count;
56
+ }
57
+
58
+ return $option;
59
+ }, $options );
60
+
61
+ // Remove exhausted choices
62
+ $options = array_filter( $options, function( $option ) {
63
+ if ( intval( $option['limit_submissions'] ) ) {
64
+ return $option['submissions_left'] > 0;
65
+ }
66
+
67
+ return $option;
68
+ } );
69
+
70
+ // Add remaining choices labels
71
+ $options = array_map( function( $option ) use( $form ) {
72
+ if ( 1 == $option['show_submissions_amount'] ) {
73
+ $submissions_left_label = $form['submissions_left_label'];
74
+
75
+ if ( happyforms_is_preview() ) {
76
+ $submissions_left_label = sprintf(
77
+ '<span class="happyforms-submissions-left">%s</span>',
78
+ $submissions_left_label
79
+ );
80
+ }
81
+
82
+ $submissions_left = sprintf(
83
+ ' <span class="happyforms-remaining-choice">(%1$s %2$s)</span>',
84
+ $option['submissions_left'], $submissions_left_label
85
+ );
86
+
87
+ $option['submissions_left_label'] = $submissions_left;
88
+ }
89
+
90
+ return $option;
91
+ }, $options );
92
+
93
+ return $options;
94
+ }
95
+
96
+ public function submission_success( $submission, $form, $message ) {
97
+ $parts = array();
98
+
99
+ foreach( $form['parts'] as $part ) {
100
+ if ( ! in_array( $part['type'], $this->part_types ) ) {
101
+ continue;
102
+ }
103
+
104
+ $options_key = 'options';
105
+
106
+ if ( 'table' === $part['type'] ) {
107
+ $options_key = 'columns';
108
+ }
109
+
110
+ $options = $part[$options_key];
111
+
112
+ foreach( $part[$options_key] as $o => $option ) {
113
+ $option = wp_parse_args( $option, $this->get_option_fields() );
114
+
115
+ if ( ! $option['limit_submissions'] ) {
116
+ continue;
117
+ }
118
+
119
+ $part_name = happyforms_get_part_name( $part, $form );
120
+
121
+ if ( ! isset( $_REQUEST[$part_name] ) ) {
122
+ continue;
123
+ }
124
+
125
+ $request_value = $_REQUEST[$part_name];
126
+
127
+ if ( ! is_array( $request_value ) ) {
128
+ $request_value = array( $request_value );
129
+ }
130
+
131
+ foreach( $request_value as $submitted_value ) {
132
+ if ( ! is_array( $submitted_value ) ) {
133
+ $submitted_value = array( $submitted_value );
134
+ }
135
+
136
+ // Filter out "other" options
137
+ $submitted_value = array_filter( $submitted_value, 'is_numeric' );
138
+ $submitted_value = array_map( 'intval', $submitted_value );
139
+
140
+ if ( ! in_array( $o, $submitted_value ) ) {
141
+ continue;
142
+ }
143
+
144
+ $meta_key = happyforms_get_option_counter_meta_key( $form['ID'], $option['id'] );
145
+ update_post_meta( $message['ID'], $meta_key, 1 );
146
+ }
147
+ }
148
+ }
149
+ }
150
+
151
+ public function get_form_parts( $parts, $form ) {
152
+ foreach( $parts as $p => $part ) {
153
+ if ( ! in_array( $part['type'], $this->part_types ) ) {
154
+ continue;
155
+ }
156
+
157
+ $options_key = 'options';
158
+
159
+ if ( 'table' === $part['type'] ) {
160
+ $options_key = 'columns';
161
+ }
162
+
163
+ $options = $part[$options_key];
164
+
165
+ foreach( $options as $o => $option ) {
166
+ $option = wp_parse_args( $option, $this->get_option_fields() );
167
+
168
+ if ( ! $option['limit_submissions'] ) {
169
+ continue;
170
+ }
171
+
172
+ $limit = intval( $option['limit_submissions_amount'] );
173
+ $form_id = $form['ID'];
174
+ $option_id = $option['id'];
175
+ $count = happyforms_get_form_controller()->count_by_option( $form_id, $option_id, $limit );
176
+
177
+ if ( $limit <= $count ) {
178
+ unset( $part[$options_key][$o] );
179
+ }
180
+
181
+ if ( 0 === count( $part[$options_key] ) ) {
182
+ unset( $parts[$p] );
183
+ }
184
+ }
185
+ }
186
+
187
+ return $parts;
188
+ }
189
+
190
+ }
191
+
192
+ if ( ! function_exists( 'happyforms_upgrade_get_option_limiter' ) ) :
193
+
194
+ function happyforms_upgrade_get_option_limiter() {
195
+ return HappyForms_Form_Option_Limiter::instance();
196
+ }
197
+
198
+ endif;
199
+
200
+ happyforms_upgrade_get_option_limiter();
core/classes/class-form-setup.php CHANGED
@@ -46,7 +46,7 @@ class HappyForms_Form_Setup {
46
  add_filter( 'happyforms_messages_controls', array( $this, 'messages_controls' ) );
47
 
48
  // Server-side hide-on-submit
49
- add_action( 'happyforms_submission_success', array( $this, 'submission_success' ), 10, 2 );
50
  }
51
 
52
  public function get_fields() {
@@ -335,7 +335,7 @@ class HappyForms_Form_Setup {
335
  return $update_data;
336
  }
337
 
338
- public function submission_success( $submission, $form ) {
339
  if ( 'success_message_hide_form' !== happyforms_get_form_property( $form, 'confirm_submission' ) && happyforms_is_falsy( happyforms_get_form_property( $form, 'form_hide_on_submit' ) ) ) {
340
  return;
341
  }
46
  add_filter( 'happyforms_messages_controls', array( $this, 'messages_controls' ) );
47
 
48
  // Server-side hide-on-submit
49
+ add_action( 'happyforms_submission_success', array( $this, 'submission_success' ), 10, 3 );
50
  }
51
 
52
  public function get_fields() {
335
  return $update_data;
336
  }
337
 
338
+ public function submission_success( $submission, $form, $message ) {
339
  if ( 'success_message_hide_form' !== happyforms_get_form_property( $form, 'confirm_submission' ) && happyforms_is_falsy( happyforms_get_form_property( $form, 'form_hide_on_submit' ) ) ) {
340
  return;
341
  }
core/classes/class-form-shuffle.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class HappyForms_Form_Shuffle_Parts {
3
+ private static $instance;
4
+
5
+ public static function instance() {
6
+ if ( is_null( self::$instance ) ) {
7
+ self::$instance = new self();
8
+ }
9
+
10
+ self::$instance->hook();
11
+
12
+ return self::$instance;
13
+ }
14
+
15
+ public function hook() {
16
+ add_filter( 'happyforms_meta_fields', array( $this, 'add_fields' ), 10, 1 );
17
+ add_filter( 'happyforms_setup_controls', array( $this, 'add_setup_controls' ), 10, 1 );
18
+
19
+ add_filter( 'happyforms_part_customize_fields_select', array( $this, 'add_shuffle_part_options_field' ), 10, 1 );
20
+ add_filter( 'happyforms_part_customize_fields_checkbox', array( $this, 'add_shuffle_part_options_field' ), 10, 1 );
21
+ add_filter( 'happyforms_part_customize_fields_radio', array( $this, 'add_shuffle_part_options_field' ), 10, 1 );
22
+ add_filter( 'happyforms_part_customize_fields_table', array( $this, 'add_shuffle_part_options_field' ), 10, 1 );
23
+ add_filter( 'happyforms_part_customize_fields_poll', array( $this, 'add_shuffle_part_options_field' ), 10, 1 );
24
+ add_filter( 'happyforms_part_customize_fields_rank_order', array( $this, 'add_shuffle_part_options_field' ), 10, 1 );
25
+
26
+ add_filter( 'happyforms_get_form_parts', array( $this, 'get_form_parts' ), 10, 2 );
27
+ add_filter( 'happyforms_part_options', array( $this, 'shuffle_part_options' ), 10, 3 );
28
+ }
29
+
30
+ public function add_fields( $fields ) {
31
+ $fields['shuffle_parts'] = array(
32
+ 'default' => 0,
33
+ 'sanitize' => 'happyforms_sanitize_checkbox'
34
+ );
35
+
36
+ return $fields;
37
+ }
38
+
39
+ public function add_setup_controls( $controls ) {
40
+ $setup_controls = array(
41
+ 1450 => array(
42
+ 'field' => 'shuffle_parts',
43
+ 'label' => __( 'Randomize fields to prevent bias', 'happyforms' ),
44
+ 'type' => 'checkbox'
45
+ ),
46
+ );
47
+
48
+ $controls = happyforms_safe_array_merge( $controls, $setup_controls );
49
+
50
+ return $controls;
51
+ }
52
+
53
+ public function get_form_parts( $parts, $form ) {
54
+ if ( is_customize_preview() ) {
55
+ return $parts;
56
+ }
57
+
58
+ if ( ! happyforms_get_form_property( $form, 'shuffle_parts' ) ) {
59
+ return $parts;
60
+ }
61
+
62
+ if ( happyforms_get_stepper()->is_multistep( $form ) ) {
63
+ return $parts;
64
+ }
65
+
66
+ $parts = $this->shuffle_form_parts( $parts );
67
+
68
+ return $parts;
69
+ }
70
+
71
+ public function shuffle_form_parts( $parts ) {
72
+ $shuffled = $parts;
73
+ $index = 0;
74
+
75
+ shuffle( $shuffled );
76
+
77
+ foreach ( $shuffled as $key => $part ) {
78
+ $shuffled[$key]['width'] = $parts[$index]['width'];
79
+ $index++;
80
+ }
81
+
82
+ $parts = $shuffled;
83
+
84
+ return $parts;
85
+ }
86
+
87
+ public function add_shuffle_part_options_field( $fields ) {
88
+ $fields['shuffle_options'] = array(
89
+ 'default' => 0,
90
+ 'sanitize' => 'happyforms_sanitize_checkbox'
91
+ );
92
+
93
+ return $fields;
94
+ }
95
+
96
+ public function shuffle_part_options( $options, $part, $form ) {
97
+ if ( ! is_customize_preview() && isset( $part['shuffle_options'] ) && 1 === intval( $part['shuffle_options'] ) ) {
98
+ // only shuffle rows in Table part
99
+ if ( 'table' === $part['type'] && $options[0]['type'] === 'column' ) {
100
+ return $options;
101
+ }
102
+
103
+ $options_keys = array_keys( $options );
104
+ shuffle( $options_keys );
105
+ $shuffled = array();
106
+
107
+ foreach ( $options_keys as $key ) {
108
+ $shuffled[$key] = $options[$key];
109
+ }
110
+
111
+ $options = $shuffled;
112
+ }
113
+
114
+ return $options;
115
+ }
116
+
117
+ }
118
+
119
+ if ( ! function_exists( 'happyforms_upgrade_get_shuffle_parts' ) ) :
120
+
121
+ function happyforms_upgrade_get_shuffle_parts() {
122
+ return HappyForms_Form_Shuffle_Parts::instance();
123
+ }
124
+
125
+ endif;
126
+
127
+ happyforms_upgrade_get_shuffle_parts();
core/classes/class-happyforms-core.php CHANGED
@@ -75,8 +75,15 @@ class HappyForms_Core {
75
  require_once( happyforms_get_core_folder() . '/classes/class-session.php' );
76
  require_once( happyforms_get_core_folder() . '/classes/class-happyforms-widget.php' );
77
  require_once( happyforms_get_core_folder() . '/helpers/helper-form-templates.php' );
 
78
  require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
79
 
 
 
 
 
 
 
80
  // Gutenberg block
81
  if ( happyforms_is_gutenberg() ) {
82
  require_once( happyforms_get_core_folder() . '/classes/class-block.php' );
75
  require_once( happyforms_get_core_folder() . '/classes/class-session.php' );
76
  require_once( happyforms_get_core_folder() . '/classes/class-happyforms-widget.php' );
77
  require_once( happyforms_get_core_folder() . '/helpers/helper-form-templates.php' );
78
+
79
  require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
80
 
81
+ // Option limiting
82
+ require_once( happyforms_get_core_folder() . '/classes/class-form-option-limiter.php' );
83
+
84
+ // Shuffle
85
+ require_once( happyforms_get_core_folder() . '/classes/class-form-shuffle.php' );
86
+
87
  // Gutenberg block
88
  if ( happyforms_is_gutenberg() ) {
89
  require_once( happyforms_get_core_folder() . '/classes/class-block.php' );
core/classes/parts/class-part-checkbox.php CHANGED
@@ -16,10 +16,12 @@ class HappyForms_Part_Checkbox extends HappyForms_Form_Part {
16
  public function hook() {
17
  self::$parent = $this;
18
 
 
19
  add_filter( 'happyforms_part_value', array( $this, 'get_part_value' ), 10, 3 );
20
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
21
  add_filter( 'happyforms_stringify_part_value', array( $this, 'stringify_value' ), 10, 3 );
22
  add_filter( 'happyforms_frontend_dependencies', array( $this, 'script_dependencies' ), 10, 2 );
 
23
  }
24
 
25
  /**
@@ -82,12 +84,50 @@ class HappyForms_Part_Checkbox extends HappyForms_Form_Part {
82
  'select_all_label' => array(
83
  'default' => __( 'Select all', 'happyforms' ),
84
  'sanitize' => 'sanitize_text_field',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  )
86
  );
87
 
88
  return happyforms_get_part_customize_fields( $fields, $this->type );
89
  }
90
 
 
 
 
 
 
 
 
 
 
 
91
  protected function get_option_defaults() {
92
  return array(
93
  'is_default' => 0,
@@ -171,8 +211,28 @@ class HappyForms_Part_Checkbox extends HappyForms_Form_Part {
171
  if ( isset( $request[$part_name] ) ) {
172
  $requested_data = $request[$part_name];
173
 
174
- if ( is_array( $requested_data ) ) {
175
- $sanitized_value = array_map( 'intval', $requested_data );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  }
177
  }
178
 
@@ -197,13 +257,82 @@ class HappyForms_Part_Checkbox extends HappyForms_Form_Part {
197
  return $validated_value;
198
  }
199
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  $options = range( 0, count( $part['options'] ) - 1 );
201
- $intersection = array_intersect( $options, $validated_value );
202
 
203
- if ( count( $validated_value ) !== count( $intersection ) ) {
204
  return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
205
  }
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  return $validated_value;
208
  }
209
 
@@ -221,14 +350,104 @@ class HappyForms_Part_Checkbox extends HappyForms_Form_Part {
221
 
222
  public function stringify_value( $value, $part, $form, $force = false ) {
223
  if ( $this->type === $part['type'] || $force ) {
 
 
 
 
224
  $options = happyforms_get_part_options( $part['options'], $part, $form );
225
  $labels = wp_list_pluck( $options, 'label' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
 
227
- foreach ( $value as $i => $index ) {
228
- $value[$i] = $labels[$index];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  }
230
 
231
- $value = implode( ', ', $value );
 
 
 
 
 
 
 
232
  }
233
 
234
  return $value;
@@ -247,9 +466,23 @@ class HappyForms_Part_Checkbox extends HappyForms_Form_Part {
247
  $class[] = "happyforms-part-options-width--{$part_options_width}";
248
  }
249
 
 
 
 
 
 
 
250
  return $class;
251
  }
252
 
 
 
 
 
 
 
 
 
253
  public function script_dependencies( $deps, $forms ) {
254
  $contains_checkbox = false;
255
  $form_controller = happyforms_get_form_controller();
16
  public function hook() {
17
  self::$parent = $this;
18
 
19
+ add_filter( 'happyforms_part_input_after', array( $this, 'append_input' ), 10, 2 );
20
  add_filter( 'happyforms_part_value', array( $this, 'get_part_value' ), 10, 3 );
21
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
22
  add_filter( 'happyforms_stringify_part_value', array( $this, 'stringify_value' ), 10, 3 );
23
  add_filter( 'happyforms_frontend_dependencies', array( $this, 'script_dependencies' ), 10, 2 );
24
+ add_filter( 'happyforms_validate_part', array( $this, 'validate_part' ) );
25
  }
26
 
27
  /**
84
  'select_all_label' => array(
85
  'default' => __( 'Select all', 'happyforms' ),
86
  'sanitize' => 'sanitize_text_field',
87
+ ),
88
+ 'other_option' => array(
89
+ 'default' => 0,
90
+ 'sanitize' => 'happyforms_sanitize_checkbox',
91
+ ),
92
+ 'other_option_label' => array(
93
+ 'default' => __( 'Other', 'happyforms' ),
94
+ 'sanitize' => 'sanitize_text_field',
95
+ ),
96
+ 'other_option_placeholder' => array(
97
+ 'default' => '',
98
+ 'sanitize' => 'sanitize_text_field',
99
+ ),
100
+ 'display_as_letters' => array(
101
+ 'default' => 0,
102
+ 'sanitize' => 'happyforms_sanitize_checkbox',
103
+ ),
104
+ 'limit_choices' => array(
105
+ 'default' => 0,
106
+ 'sanitize' => 'happyforms_sanitize_checkbox',
107
+ ),
108
+ 'limit_choices_min' => array(
109
+ 'default' => 1,
110
+ 'sanitize' => 'intval',
111
+ ),
112
+ 'limit_choices_max' => array(
113
+ 'default' => 1,
114
+ 'sanitize' => 'intval',
115
  )
116
  );
117
 
118
  return happyforms_get_part_customize_fields( $fields, $this->type );
119
  }
120
 
121
+ public function append_input( $part, $form ) {
122
+ if ( $this->type !== $part['type'] ) {
123
+ return;
124
+ }
125
+
126
+ if ( 1 == $part['other_option'] ) {
127
+ require( happyforms_get_core_folder() . '/templates/parts/frontend-checkbox-other-option.php' );
128
+ }
129
+ }
130
+
131
  protected function get_option_defaults() {
132
  return array(
133
  'is_default' => 0,
211
  if ( isset( $request[$part_name] ) ) {
212
  $requested_data = $request[$part_name];
213
 
214
+ $filtered_request = array_map( 'json_decode', array_map( 'stripslashes', $requested_data ) );
215
+ $contains_array = array_map( 'is_array', $filtered_request );
216
+
217
+ if ( ! in_array( true, $contains_array ) ) {
218
+ if ( isset( $request[$part_name] ) ) {
219
+ $requested_data = $request[$part_name];
220
+
221
+ if ( is_array( $requested_data ) ) {
222
+ $sanitized_value = array_map( 'intval', $requested_data );
223
+ }
224
+ }
225
+ } else {
226
+ foreach ( $filtered_request as $index => $request ) {
227
+ if ( is_array( $request ) ) {
228
+ $filtered_request[$index][0] = intval( $filtered_request[$index][0] );
229
+ $filtered_request[$index][1] = sanitize_text_field( $filtered_request[$index][1] );
230
+ } else {
231
+ $filtered_request[$index] = intval( $filtered_request[$index] );
232
+ }
233
+ }
234
+
235
+ $sanitized_value = $filtered_request;
236
  }
237
  }
238
 
257
  return $validated_value;
258
  }
259
 
260
+ if ( 1 === $part['limit_choices'] ) {
261
+ if ( count( $validated_value ) < $part['limit_choices_min'] ) {
262
+ return new WP_Error( 'error', happyforms_get_validation_message( 'select_more_choices' ) );
263
+ }
264
+
265
+ if ( count( $validated_value ) > $part['limit_choices_max'] ) {
266
+ return new WP_Error( 'error', happyforms_get_validation_message( 'select_less_choices' ) );
267
+ }
268
+ }
269
+
270
+ if ( ! is_array( $validated_value ) && 1 !== $part['required'] ) {
271
+ return $validated_value;
272
+ }
273
+
274
+ $contains_array = array_map( 'is_array', $validated_value );
275
+
276
+ if ( ! in_array( true, $contains_array ) ) {
277
+ if ( 1 === $part['required'] && empty( $validated_value ) ) {
278
+ $validated_value = new WP_Error( 'error', happyforms_get_validation_message( 'no_selection' ) );
279
+ return $validated_value;
280
+ }
281
+
282
+ $options = range( 0, count( $part['options'] ) - 1 );
283
+ $intersection = array_intersect( $options, $validated_value );
284
+
285
+ if ( count( $validated_value ) !== count( $intersection ) ) {
286
+ return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
287
+ }
288
+
289
+ if ( is_wp_error( $validated_value ) ) {
290
+ return $validated_value;
291
+ }
292
+
293
+ foreach ( $validated_value as $value ) {
294
+ $value = $this->validate_option_limits( $value, $part, $form );
295
+
296
+ if ( is_wp_error( $value ) ) {
297
+ return $value;
298
+ }
299
+ }
300
+ }
301
+
302
+ $numeric_values = array_filter( $validated_value, 'is_int' );
303
+ $array_values = array_filter( $validated_value, 'is_array' );
304
  $options = range( 0, count( $part['options'] ) - 1 );
305
+ $intersection = array_intersect( $options, $numeric_values );
306
 
307
+ if ( count( $numeric_values ) !== count( $intersection ) ) {
308
  return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
309
  }
310
 
311
+ foreach ( $numeric_values as $numeric_value ) {
312
+ $validated_numeric_value = $this->validate_option_limits( $numeric_value, $part, $form );
313
+
314
+ if ( is_wp_error( $validated_numeric_value ) ) {
315
+ return $validated_numeric_value;
316
+ }
317
+ }
318
+
319
+ foreach ( $array_values as $array_value ) {
320
+ if ( 1 === $part['required'] && ! isset( $array_value[1] ) ) {
321
+ $validated_value = new WP_Error( 'error', happyforms_get_validation_message( 'field_empty' ) );
322
+ return $validated_value;
323
+ }
324
+ }
325
+
326
+ foreach ( $validated_value as $key => $value ) {
327
+ if ( is_array( $validated_value[ $key ] ) ) {
328
+ foreach ( $validated_value[$key] as $opt_key => $opt_val ) {
329
+ if ( '' == $validated_value[$key][$opt_key] ) {
330
+ return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
331
+ }
332
+ }
333
+ }
334
+ }
335
+
336
  return $validated_value;
337
  }
338
 
350
 
351
  public function stringify_value( $value, $part, $form, $force = false ) {
352
  if ( $this->type === $part['type'] || $force ) {
353
+ if ( empty( $value ) ) {
354
+ return $value;
355
+ }
356
+ $original_value = $value;
357
  $options = happyforms_get_part_options( $part['options'], $part, $form );
358
  $labels = wp_list_pluck( $options, 'label' );
359
+ $contains_array = array_map( 'is_array', $value );
360
+
361
+ if ( ! in_array( true, $contains_array ) ) {
362
+ foreach ( $value as $i => $index ) {
363
+ $value[$i] = $labels[$index];
364
+ }
365
+
366
+ $value = implode( ', ', $value );
367
+ if ( $this->display_choices_as_letters( $part ) ) {
368
+ $values = array();
369
+ foreach ( $original_value as $option_index ) {
370
+ if ( isset( $part['options'][(int) $option_index] ) ) {
371
+ $option = $part['options'][(int) $option_index];
372
+ $letter = happyforms_get_part_choice_letter( $option, $part );
373
+ $values[] = "{$letter}) {$option['label']}";
374
+ }
375
+ }
376
+
377
+ $value = implode( ', ', $values );
378
+ }
379
+ } else {
380
+ foreach ( $value as $i => $index ) {
381
+ $label = '';
382
+
383
+ if ( is_array( $index ) && ! empty( $index[1] ) ) { // other option
384
+ $value[$i] = $index[1];
385
+
386
+ if ( $this->display_choices_as_letters( $part ) ) {
387
+ $letter = happyforms_get_part_choice_letter( '', $part );
388
+ $label = $value[$i];
389
+ }
390
+ } else { // standard option
391
+ $value[$i] = $options[$index]['label'];
392
+
393
+ if ( $this->display_choices_as_letters( $part ) ) {
394
+ if ( isset( $part['options'][$index] ) ) {
395
+ $option = $part['options'][$index];
396
+ $letter = happyforms_get_part_choice_letter( $option, $part );
397
+ $label = $option['label'];
398
+ }
399
+ }
400
+ }
401
+
402
+ if ( $this->display_choices_as_letters( $part ) ) {
403
+ $value[$i] = "{$letter}) {$label}";
404
+ }
405
+ }
406
+ }
407
+ }
408
+
409
+ return $value;
410
+ }
411
+
412
+ private function clamp( $v, $min, $max ) {
413
+ return min( max( $v, $min ), $max );
414
+ }
415
+
416
+ public function validate_part( $part_data ) {
417
+ if ( $this->type !== $part_data['type'] ) {
418
+ return $part_data;
419
+ }
420
 
421
+ $min_choices = intval( $part_data['limit_choices_min'] );
422
+ $max_choices = intval( $part_data['limit_choices_max'] );
423
+ $num_choices = count( $part_data['options'] );
424
+
425
+ $min_choices = $this->clamp( $min_choices, $num_choices > 1 ? 2 : 1, $min_choices );
426
+ $min_choices = $this->clamp( $min_choices, $min_choices, $num_choices );
427
+ $max_choices = $this->clamp( $max_choices, $min_choices, $num_choices );
428
+
429
+ $part_data['limit_choices_min'] = $min_choices;
430
+ $part_data['limit_choices_max'] = $max_choices;
431
+
432
+ return $part_data;
433
+ }
434
+
435
+ private function validate_option_limits( $value, $part, $form ) {
436
+ foreach( $part['options'] as $o => $option ) {
437
+ $option = wp_parse_args( $option, happyforms_upgrade_get_option_limiter()->get_option_fields() );
438
+
439
+ if ( ! $option['limit_submissions'] || $o !== intval( $value ) ) {
440
+ continue;
441
  }
442
 
443
+ $limit = intval( $option['limit_submissions_amount'] );
444
+ $form_id = $form['ID'];
445
+ $option_id = $option['id'];
446
+ $count = happyforms_get_form_controller()->count_by_option( $form_id, $option_id, $limit );
447
+
448
+ if ( $count === $limit ) {
449
+ return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
450
+ }
451
  }
452
 
453
  return $value;
466
  $class[] = "happyforms-part-options-width--{$part_options_width}";
467
  }
468
 
469
+ if ( $this->type === $part['type'] ) {
470
+ if ( $this->display_choices_as_letters( $part ) ) {
471
+ $class[] = 'happyforms-part-choice--display-as-letters';
472
+ }
473
+ }
474
+
475
  return $class;
476
  }
477
 
478
+ public function display_choices_as_letters( $part ) {
479
+ if ( 1 == $part['display_as_letters'] ) {
480
+ return true;
481
+ }
482
+
483
+ return false;
484
+ }
485
+
486
  public function script_dependencies( $deps, $forms ) {
487
  $contains_checkbox = false;
488
  $form_controller = happyforms_get_form_controller();
core/classes/parts/class-part-multi-line-text.php CHANGED
@@ -16,6 +16,8 @@ class HappyForms_Part_MultiLineText extends HappyForms_Form_Part {
16
  add_action( 'happyforms_part_input_after', array( $this, 'part_input_after' ), 10, 2 );
17
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
18
  add_filter( 'happyforms_frontend_dependencies', array( $this, 'script_dependencies' ), 10, 2 );
 
 
19
  }
20
 
21
  /**
@@ -93,6 +95,10 @@ class HappyForms_Part_MultiLineText extends HappyForms_Form_Part {
93
  'default_value' => array(
94
  'default' => '',
95
  'sanitize' => 'sanitize_text_field'
 
 
 
 
96
  )
97
  );
98
 
@@ -127,7 +133,13 @@ class HappyForms_Part_MultiLineText extends HappyForms_Form_Part {
127
  $part = wp_parse_args( $part_data, $this->get_customize_defaults() );
128
  $form = $form_data;
129
 
130
- include( happyforms_get_core_folder() . '/templates/parts/frontend-multi-line-text.php' );
 
 
 
 
 
 
131
  }
132
 
133
  /**
@@ -161,7 +173,12 @@ class HappyForms_Part_MultiLineText extends HappyForms_Form_Part {
161
  $part_name = happyforms_get_part_name( $part_data, $form_data );
162
 
163
  if ( isset( $request[$part_name] ) ) {
164
- $sanitized_value = sanitize_textarea_field( $request[$part_name] );
 
 
 
 
 
165
  }
166
 
167
  return $sanitized_value;
@@ -193,8 +210,9 @@ class HappyForms_Part_MultiLineText extends HappyForms_Form_Part {
193
 
194
  if ( $character_limit > 0 ) {
195
  $character_limit_mode = $part['character_limit_mode'];
196
- $character_count = strlen( $validated_value );
197
- $word_count = str_word_count( $validated_value );
 
198
 
199
  if ( 'character_max' === $character_limit_mode && $character_count > $character_limit ) {
200
  return new WP_Error( 'error', happyforms_get_validation_message( 'message_too_long' ) );
@@ -234,6 +252,10 @@ class HappyForms_Part_MultiLineText extends HappyForms_Form_Part {
234
  if ( 'focus-reveal' === $part['description_mode'] ) {
235
  $class[] = 'happyforms-part--focus-reveal-description';
236
  }
 
 
 
 
237
  }
238
 
239
  return $class;
@@ -295,25 +317,6 @@ class HappyForms_Part_MultiLineText extends HappyForms_Form_Part {
295
  return $attrs;
296
  }
297
 
298
- public function part_input_after( $part, $form ) {
299
- if ( $this->type !== $part['type'] ) {
300
- return;
301
- }
302
-
303
- $limit_input = intval( $part['limit_input'] );
304
- $character_limit = intval( $part['character_limit'] );
305
- $character_limit = $limit_input ? $character_limit : 0;
306
-
307
- if ( $character_limit || happyforms_is_preview() ) {
308
- $label = $this->get_limit_label( $part, $form );
309
- ?>
310
- <div class="happyforms-part__char-counter <?php echo $part['character_limit_mode']; ?>" <?php if ( happyforms_is_preview() && ! $character_limit ) : ?>style="display: none;"<?php endif; ?>>
311
- <span class="counter">0</span>/<?php echo $character_limit; ?> <?php echo $label; ?>
312
- </div>
313
- <?php
314
- }
315
- }
316
-
317
  public function email_part_value( $value, $message, $part, $form ) {
318
  if ( $this->type === $part['type'] ) {
319
  $value = str_replace( "\n", '<br />', $value );
@@ -336,11 +339,22 @@ class HappyForms_Part_MultiLineText extends HappyForms_Form_Part {
336
 
337
  public function script_dependencies( $deps, $forms ) {
338
  $contains_long_text = false;
 
339
  $form_controller = happyforms_get_form_controller();
340
 
341
  foreach ( $forms as $form ) {
342
- if ( $form_controller->get_first_part_by_type( $form, $this->type ) ) {
 
 
343
  $contains_long_text = true;
 
 
 
 
 
 
 
 
344
  break;
345
  }
346
  }
@@ -349,10 +363,39 @@ class HappyForms_Part_MultiLineText extends HappyForms_Form_Part {
349
  return $deps;
350
  }
351
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  wp_register_script(
353
  'happyforms-part-long-text',
354
  happyforms_get_plugin_url() . 'core/assets/js/frontend/long-text.js',
355
- array(), HAPPYFORMS_VERSION, true
 
 
 
 
 
 
356
  );
357
 
358
  $deps[] = 'happyforms-part-long-text';
@@ -360,4 +403,132 @@ class HappyForms_Part_MultiLineText extends HappyForms_Form_Part {
360
  return $deps;
361
  }
362
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  }
16
  add_action( 'happyforms_part_input_after', array( $this, 'part_input_after' ), 10, 2 );
17
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
18
  add_filter( 'happyforms_frontend_dependencies', array( $this, 'script_dependencies' ), 10, 2 );
19
+ add_action( 'happyforms_print_scripts', array( $this, 'footer_templates' ) );
20
+ add_filter( 'happyforms_style_dependencies', array( $this, 'style_dependencies' ), 10, 2 );
21
  }
22
 
23
  /**
95
  'default_value' => array(
96
  'default' => '',
97
  'sanitize' => 'sanitize_text_field'
98
+ ),
99
+ 'rich_text' => array(
100
+ 'default' => 0,
101
+ 'sanitize' => 'happyforms_sanitize_checkbox'
102
  )
103
  );
104
 
133
  $part = wp_parse_args( $part_data, $this->get_customize_defaults() );
134
  $form = $form_data;
135
 
136
+ $template_path = happyforms_get_core_folder() . '/templates/parts/frontend-multi-line-text.php';
137
+
138
+ if ( 1 == $part['rich_text'] && happyforms_is_block_context() ) {
139
+ $template_path = happyforms_get_core_folder() . '/templates/parts/block-rich-text.php';
140
+ }
141
+
142
+ include( $template_path );
143
  }
144
 
145
  /**
173
  $part_name = happyforms_get_part_name( $part_data, $form_data );
174
 
175
  if ( isset( $request[$part_name] ) ) {
176
+ if ( 1 == $part_data['rich_text'] ) {
177
+ $allowed_html = $this->get_allowed_html();
178
+ $sanitized_value = wp_kses( $request[$part_name], $allowed_html );
179
+ } else {
180
+ $sanitized_value = sanitize_textarea_field( $request[$part_name] );
181
+ }
182
  }
183
 
184
  return $sanitized_value;
210
 
211
  if ( $character_limit > 0 ) {
212
  $character_limit_mode = $part['character_limit_mode'];
213
+ $character_limit_input_value = trim( preg_replace('/\s+/', ' ', str_replace( '&nbsp;', ' ', strip_tags( $validated_value ) ) ) );
214
+ $character_count = strlen( $character_limit_input_value );
215
+ $word_count = str_word_count( $character_limit_input_value );
216
 
217
  if ( 'character_max' === $character_limit_mode && $character_count > $character_limit ) {
218
  return new WP_Error( 'error', happyforms_get_validation_message( 'message_too_long' ) );
252
  if ( 'focus-reveal' === $part['description_mode'] ) {
253
  $class[] = 'happyforms-part--focus-reveal-description';
254
  }
255
+
256
+ if ( 1 == $part['rich_text'] ) {
257
+ $class[] = 'happyforms-part--rich_text';
258
+ }
259
  }
260
 
261
  return $class;
317
  return $attrs;
318
  }
319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  public function email_part_value( $value, $message, $part, $form ) {
321
  if ( $this->type === $part['type'] ) {
322
  $value = str_replace( "\n", '<br />', $value );
339
 
340
  public function script_dependencies( $deps, $forms ) {
341
  $contains_long_text = false;
342
+ $contains_editor = false;
343
  $form_controller = happyforms_get_form_controller();
344
 
345
  foreach ( $forms as $form ) {
346
+ $parts = $form_controller->get_parts_by_type( $form, $this->type );
347
+
348
+ if ( ! empty( $parts ) ) {
349
  $contains_long_text = true;
350
+ }
351
+
352
+ $editors = array_filter( $parts, function( $part ) {
353
+ return 1 == $part['rich_text'];
354
+ } );
355
+
356
+ if ( ! empty( $editors ) ) {
357
+ $contains_editor = true;
358
  break;
359
  }
360
  }
363
  return $deps;
364
  }
365
 
366
+ $contains_editor = $contains_editor || happyforms_is_preview();
367
+ $editor_settings = array();
368
+
369
+ if ( $contains_editor ) {
370
+ $content_css = array( happyforms_get_plugin_url() . 'core/assets/css/rich-text-editor.css' );
371
+ /**
372
+ * Filters the list of CSS files to load
373
+ * in the editor document.
374
+ *
375
+ * @param array $content_css Current list of files.
376
+ *
377
+ * @return array
378
+ */
379
+ $content_css = apply_filters( 'happyforms_text_editor_content_css', $content_css );
380
+ $css_vars = array_values( happyforms_get_styles()->form_css_vars() );
381
+ $allowed_elements = $this->get_editor_valid_elements();
382
+ $editor_settings = array(
383
+ 'contentCSS' => $content_css,
384
+ 'cssVars' => $css_vars,
385
+ 'allowedElements' => $allowed_elements,
386
+ );
387
+ }
388
+
389
  wp_register_script(
390
  'happyforms-part-long-text',
391
  happyforms_get_plugin_url() . 'core/assets/js/frontend/long-text.js',
392
+ ( $contains_editor ? array( 'wp-tinymce' ) : array() ), HAPPYFORMS_VERSION, true
393
+ );
394
+
395
+ wp_localize_script(
396
+ 'happyforms-part-long-text',
397
+ '_happyFormsRichTextSettings',
398
+ $editor_settings
399
  );
400
 
401
  $deps[] = 'happyforms-part-long-text';
403
  return $deps;
404
  }
405
 
406
+ public function style_dependencies( $deps, $forms ) {
407
+ $contains_long_text = false;
408
+ $contains_editor = false;
409
+
410
+ $form_controller = happyforms_get_form_controller();
411
+
412
+ foreach ( $forms as $form ) {
413
+ $parts = $form_controller->get_parts_by_type( $form, $this->type );
414
+
415
+ if ( ! empty( $parts ) ) {
416
+ $contains_long_text = true;
417
+ }
418
+
419
+ $editors = array_filter( $parts, function( $part ) {
420
+ return 1 == $part['rich_text'];
421
+ } );
422
+
423
+ if ( ! empty( $editors ) ) {
424
+ $contains_editor = true;
425
+ break;
426
+ }
427
+ }
428
+
429
+ if ( ! happyforms_is_preview() && ! $contains_long_text ) {
430
+ return $deps;
431
+ }
432
+
433
+ if( happyforms_is_preview() ) {
434
+ $contains_editor = true;
435
+ }
436
+
437
+ if ( $contains_editor ) {
438
+ $deps[] = 'editor-buttons';
439
+ }
440
+
441
+ return $deps;
442
+ }
443
+
444
+ public function footer_templates( $forms ) {
445
+ $contains_editor = false;
446
+ $form_controller = happyforms_get_form_controller();
447
+
448
+ foreach ( $forms as $form ) {
449
+ $parts = $form_controller->get_parts_by_type( $form, $this->type );
450
+
451
+ $editors = array_filter( $parts, function( $part ) {
452
+ return 1 == $part['rich_text'];
453
+ } );
454
+
455
+ if ( ! empty( $editors ) ) {
456
+ $contains_editor = true;
457
+ break;
458
+ }
459
+ }
460
+
461
+ if( happyforms_is_preview() ) {
462
+ $contains_editor = true;
463
+ }
464
+
465
+ if ( ! $contains_editor ) {
466
+ return;
467
+ }
468
+
469
+ require_once( happyforms_get_core_folder() . '/templates/partials/frontend-rich-text-toolbar-icons.php' );
470
+ }
471
+
472
+ private function get_allowed_html() {
473
+ $allowed = array(
474
+ 'br' => array(),
475
+ 'b' => array(),
476
+ 'strong' => array(),
477
+ 'i' => array(),
478
+ 'em' => array(),
479
+ 'ul' => array(),
480
+ 'ol' => array(),
481
+ 'li' => array(),
482
+ 'p' => array(),
483
+ 'a' => array( 'href' => array() ),
484
+ 'pre' => array(),
485
+ 'hr' => array(),
486
+ 'u' => array(),
487
+ 'strike' => array(),
488
+ 'del' => array(),
489
+ 'blockquote' => array(),
490
+ );
491
+
492
+ return $allowed;
493
+ }
494
+
495
+ private function get_editor_valid_elements() {
496
+ $tags = $this->get_allowed_html();
497
+ $elements = array();
498
+
499
+ foreach( $tags as $tag => $attributes ) {
500
+ $element = $tag;
501
+ $element_attributes = implode( '|', array_keys( $attributes ) );
502
+
503
+ if ( ! empty( $element_attributes ) ) {
504
+ $element = "{$element}[{$element_attributes}]";
505
+ }
506
+
507
+ $elements[] = $element;
508
+ }
509
+
510
+ $elements = implode( ',', $elements );
511
+
512
+ return $elements;
513
+ }
514
+
515
+ public function part_input_after( $part, $form ) {
516
+ if ( $this->type !== $part['type'] ) {
517
+ return;
518
+ }
519
+
520
+ $limit_input = intval( $part['limit_input'] );
521
+ $character_limit = intval( $part['character_limit'] );
522
+ $character_limit = $limit_input ? $character_limit : 0;
523
+
524
+ if ( $character_limit ) {
525
+ $label = $this->get_limit_label( $part, $form );
526
+ ?>
527
+ <div class="happyforms-part__char-counter <?php echo $part['character_limit_mode']; ?>">
528
+ <span class="counter">0</span>/<?php echo $character_limit; ?> <?php echo $label; ?>
529
+ </div>
530
+ <?php
531
+ }
532
+ }
533
+
534
  }
core/classes/parts/class-part-radio.php CHANGED
@@ -18,9 +18,11 @@ class HappyForms_Part_Radio extends HappyForms_Form_Part {
18
  public function hook() {
19
  self::$parent = $this;
20
 
 
21
  add_filter( 'happyforms_part_value', array( $this, 'get_part_value' ), 10, 3 );
22
  add_filter( 'happyforms_stringify_part_value', array( $this, 'stringify_value' ), 10, 3 );
23
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
 
24
  }
25
 
26
  /**
@@ -75,7 +77,23 @@ class HappyForms_Part_Radio extends HappyForms_Form_Part {
75
  'options_width' => array(
76
  'default' => 'auto',
77
  'sanitize' => 'sanitize_text_field'
78
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  );
80
 
81
  return happyforms_get_part_customize_fields( $fields, $this->type );
@@ -165,7 +183,19 @@ class HappyForms_Part_Radio extends HappyForms_Form_Part {
165
  $part_name = happyforms_get_part_name( $part_data, $form_data );
166
 
167
  if ( isset( $request[$part_name] ) ) {
168
- $sanitized_value = sanitize_text_field( $request[$part_name] );
 
 
 
 
 
 
 
 
 
 
 
 
169
  }
170
 
171
  return $sanitized_value;
@@ -184,23 +214,76 @@ class HappyForms_Part_Radio extends HappyForms_Form_Part {
184
  public function validate_value( $value, $part = array(), $form = array() ) {
185
  $validated_value = $value;
186
 
187
- if ( 1 === $part['required'] && '' === $validated_value ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  return new WP_Error( 'error', happyforms_get_validation_message( 'no_selection' ) );
189
  }
190
 
191
- if ( '' !== $validated_value ) {
192
- if ( ! is_numeric( $validated_value ) ) {
193
  return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
194
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
- $options = range( 0, count( $part['options'] ) - 1 );
 
 
 
197
 
198
- if ( ! in_array( $validated_value, $options ) ) {
199
  return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
200
  }
201
  }
202
 
203
- return $validated_value;
 
 
 
 
 
 
 
 
 
 
204
  }
205
 
206
  public function get_part_value( $value, $part, $form ) {
@@ -215,11 +298,40 @@ class HappyForms_Part_Radio extends HappyForms_Form_Part {
215
  return $value;
216
  }
217
 
 
 
 
 
 
 
 
 
218
  public function stringify_value( $value, $part, $form, $force = false ) {
219
  if ( $this->type === $part['type'] || $force ) {
220
- if ( '' !== $value ) {
221
- $options = happyforms_get_part_options( $part['options'], $part, $form );
222
- $value = $options[$value]['label'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  }
224
  }
225
 
@@ -234,6 +346,10 @@ class HappyForms_Part_Radio extends HappyForms_Form_Part {
234
  $class[] = 'display-type--block';
235
  }
236
 
 
 
 
 
237
  $part_options_width = $part['options_width'];
238
 
239
  $class[] = "happyforms-part-options-width--{$part_options_width}";
@@ -242,4 +358,30 @@ class HappyForms_Part_Radio extends HappyForms_Form_Part {
242
  return $class;
243
  }
244
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  }
18
  public function hook() {
19
  self::$parent = $this;
20
 
21
+ add_filter( 'happyforms_part_input_after', array( $this, 'append_input' ), 10, 2 );
22
  add_filter( 'happyforms_part_value', array( $this, 'get_part_value' ), 10, 3 );
23
  add_filter( 'happyforms_stringify_part_value', array( $this, 'stringify_value' ), 10, 3 );
24
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
25
+ add_filter( 'happyforms_frontend_dependencies', array( $this, 'script_dependencies' ), 10, 2 );
26
  }
27
 
28
  /**
77
  'options_width' => array(
78
  'default' => 'auto',
79
  'sanitize' => 'sanitize_text_field'
80
+ ),
81
+ 'display_as_letters' => array(
82
+ 'default' => 0,
83
+ 'sanitize' => 'happyforms_sanitize_checkbox'
84
+ ),
85
+ 'other_option' => array(
86
+ 'default' => 0,
87
+ 'sanitize' => 'happyforms_sanitize_checkbox'
88
+ ),
89
+ 'other_option_label' => array(
90
+ 'default' => __( 'Other', 'happyforms' ),
91
+ 'sanitize' => 'sanitize_text_field'
92
+ ),
93
+ 'other_option_placeholder' => array(
94
+ 'default' => '',
95
+ 'sanitize' => 'sanitize_text_field'
96
+ ),
97
  );
98
 
99
  return happyforms_get_part_customize_fields( $fields, $this->type );
183
  $part_name = happyforms_get_part_name( $part_data, $form_data );
184
 
185
  if ( isset( $request[$part_name] ) ) {
186
+ $value_array = json_decode( stripslashes( $request[$part_name] ) );
187
+
188
+ if ( ! is_array( $value_array ) ) {
189
+ if ( isset( $request[$part_name] ) ) {
190
+ $sanitized_value = sanitize_text_field( $request[$part_name] );
191
+ }
192
+
193
+ return $sanitized_value;
194
+ }
195
+
196
+ $sanitized_value = array();
197
+ $sanitized_value[0] = intval( $value_array[0] );
198
+ $sanitized_value[1] = sanitize_text_field( $value_array[1] );
199
  }
200
 
201
  return $sanitized_value;
214
  public function validate_value( $value, $part = array(), $form = array() ) {
215
  $validated_value = $value;
216
 
217
+ if ( ! is_array( $validated_value ) ) {
218
+ if ( 1 === $part['required'] && '' === $validated_value ) {
219
+ return new WP_Error( 'error', happyforms_get_validation_message( 'no_selection' ) );
220
+ }
221
+
222
+ if ( '' !== $validated_value ) {
223
+ if ( ! is_numeric( $validated_value ) ) {
224
+ return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
225
+ }
226
+
227
+ $options = range( 0, count( $part['options'] ) - 1 );
228
+
229
+ if ( ! in_array( $validated_value, $options ) ) {
230
+ return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
231
+ }
232
+ }
233
+
234
+ if ( is_wp_error( $validated_value ) ) {
235
+ return $validated_value;
236
+ }
237
+
238
+ return $this->validate_option_limits( $validated_value, $part, $form );
239
+ }
240
+
241
+ if ( 1 === $part['required'] && empty( $validated_value[0] ) ) {
242
  return new WP_Error( 'error', happyforms_get_validation_message( 'no_selection' ) );
243
  }
244
 
245
+ if ( ! empty( $validated_value[0] ) ) {
246
+ if ( ! is_numeric( $validated_value[0] ) ) {
247
  return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
248
  }
249
+ }
250
+
251
+ if ( '' === $validated_value[1] ) {
252
+ return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
253
+ }
254
+
255
+ return $validated_value;
256
+ }
257
+
258
+ private function validate_option_limits( $value, $part, $form ) {
259
+ foreach( $part['options'] as $o => $option ) {
260
+ $option = wp_parse_args( $option, happyforms_upgrade_get_option_limiter()->get_option_fields() );
261
+
262
+ if ( ! $option['limit_submissions'] || $o !== intval( $value ) ) {
263
+ continue;
264
+ }
265
 
266
+ $limit = intval( $option['limit_submissions_amount'] );
267
+ $form_id = $form['ID'];
268
+ $option_id = $option['id'];
269
+ $count = happyforms_get_form_controller()->count_by_option( $form_id, $option_id, $limit );
270
 
271
+ if ( $count === $limit ) {
272
  return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
273
  }
274
  }
275
 
276
+ return $value;
277
+ }
278
+
279
+ public function append_input( $part, $form ) {
280
+ if ( $this->type !== $part['type'] ) {
281
+ return;
282
+ }
283
+
284
+ if ( 1 == $part['other_option'] ) {
285
+ require( happyforms_get_core_folder() . '/templates/parts/frontend-radio-other-option.php' );
286
+ }
287
  }
288
 
289
  public function get_part_value( $value, $part, $form ) {
298
  return $value;
299
  }
300
 
301
+ public function display_choices_as_letters( $part ) {
302
+ if ( 1 == $part['display_as_letters'] ) {
303
+ return true;
304
+ }
305
+
306
+ return false;
307
+ }
308
+
309
  public function stringify_value( $value, $part, $form, $force = false ) {
310
  if ( $this->type === $part['type'] || $force ) {
311
+ $original_value = $value;
312
+
313
+ if ( ! is_array( $value ) ) { // standard options
314
+ if ( '' !== $value ) {
315
+ $options = happyforms_get_part_options( $part['options'], $part, $form );
316
+ $value = $options[$value]['label'];
317
+ }
318
+
319
+ if ( $this->display_choices_as_letters( $part ) ) {
320
+ $original_value = intval( $original_value );
321
+
322
+ if ( isset( $part['options'][$original_value] ) ) {
323
+ $option = $part['options'][$original_value];
324
+ $letter = happyforms_get_part_choice_letter( $option, $part );
325
+ $value = "{$letter}) {$value}";
326
+ }
327
+ }
328
+ } else { // other option
329
+ $value = ( ! empty( $value[1] ) ) ? $value[1] : '';
330
+
331
+ if ( $this->display_choices_as_letters( $part ) ) {
332
+ $letter = happyforms_get_part_choice_letter( '', $part );
333
+ $value = "{$letter}) {$value}";
334
+ }
335
  }
336
  }
337
 
346
  $class[] = 'display-type--block';
347
  }
348
 
349
+ if ( 1 == $part['display_as_letters'] ) {
350
+ $class[] = 'happyforms-part-choice--display-as-letters';
351
+ }
352
+
353
  $part_options_width = $part['options_width'];
354
 
355
  $class[] = "happyforms-part-options-width--{$part_options_width}";
358
  return $class;
359
  }
360
 
361
+ public function script_dependencies( $deps, $forms ) {
362
+ $contains_radio = false;
363
+ $form_controller = happyforms_get_form_controller();
364
+
365
+ foreach ( $forms as $form ) {
366
+ if ( $form_controller->get_first_part_by_type( $form, $this->type ) ) {
367
+ $contains_radio = true;
368
+ break;
369
+ }
370
+ }
371
+
372
+ if ( ! happyforms_is_preview() && ! $contains_radio ) {
373
+ return $deps;
374
+ }
375
+
376
+ wp_register_script(
377
+ 'happyforms-radio-ext',
378
+ happyforms_get_plugin_url() . 'core/assets/js/frontend/radio.js',
379
+ array(), HAPPYFORMS_VERSION, true
380
+ );
381
+
382
+ $deps[] = 'happyforms-radio-ext';
383
+
384
+ return $deps;
385
+ }
386
+
387
  }
core/classes/parts/class-part-select.php CHANGED
@@ -16,6 +16,7 @@ class HappyForms_Part_Select extends HappyForms_Form_Part {
16
  public function hook() {
17
  self::$parent = $this;
18
 
 
19
  add_filter( 'happyforms_stringify_part_value', array( $this, 'stringify_value' ), 10, 3 );
20
  add_filter( 'happyforms_frontend_dependencies', array( $this, 'script_dependencies' ), 10, 2 );
21
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
@@ -71,6 +72,18 @@ class HappyForms_Part_Select extends HappyForms_Form_Part {
71
  'default' => array(),
72
  'sanitize' => 'happyforms_sanitize_array'
73
  ),
 
 
 
 
 
 
 
 
 
 
 
 
74
  );
75
 
76
  return happyforms_get_part_customize_fields( $fields, $this->type );
@@ -159,7 +172,18 @@ class HappyForms_Part_Select extends HappyForms_Form_Part {
159
  $part_name = happyforms_get_part_name( $part_data, $form_data );
160
 
161
  if ( isset( $request[$part_name] ) ) {
162
- $sanitized_value = sanitize_text_field( $request[$part_name] );
 
 
 
 
 
 
 
 
 
 
 
163
  }
164
 
165
  return $sanitized_value;
@@ -178,30 +202,79 @@ class HappyForms_Part_Select extends HappyForms_Form_Part {
178
  public function validate_value( $value, $part = array(), $form = array() ) {
179
  $validated_value = $value;
180
 
181
- if ( 1 === $part['required'] && '' === $validated_value ) {
182
- return new WP_Error( 'error', happyforms_get_validation_message( 'no_selection' ) );
183
- }
 
184
 
185
- if ( '' !== $validated_value ) {
186
- if ( ! is_numeric( $validated_value ) ) {
187
- return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
 
 
 
 
 
 
 
188
  }
189
 
190
- $options = range( 0, count( $part['options'] ) - 1 );
 
 
 
 
 
191
 
192
- if ( ! in_array( intval( $validated_value ), $options ) ) {
 
 
 
 
 
193
  return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
194
  }
195
  }
196
 
 
 
 
 
197
  return $validated_value;
198
  }
199
 
200
  public function stringify_value( $value, $part, $form ) {
201
  if ( $this->type === $part['type'] ) {
202
- if ( '' !== $value ) {
203
- $options = happyforms_get_part_options( $part['options'], $part, $form );
204
- $value = $options[$value]['label'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  }
206
  }
207
 
@@ -222,6 +295,70 @@ class HappyForms_Part_Select extends HappyForms_Form_Part {
222
  return $value;
223
  }
224
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  public function html_part_class( $class, $part, $form ) {
226
  if ( $this->type === $part['type'] ) {
227
  if ( happyforms_get_part_value( $part, $form ) ) {
16
  public function hook() {
17
  self::$parent = $this;
18
 
19
+ add_filter( 'happyforms_part_input_after', array( $this, 'append_input' ), 10, 2 );
20
  add_filter( 'happyforms_stringify_part_value', array( $this, 'stringify_value' ), 10, 3 );
21
  add_filter( 'happyforms_frontend_dependencies', array( $this, 'script_dependencies' ), 10, 2 );
22
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
72
  'default' => array(),
73
  'sanitize' => 'happyforms_sanitize_array'
74
  ),
75
+ 'other_option' => array(
76
+ 'default' => 0,
77
+ 'sanitize' => 'happyforms_sanitize_checkbox'
78
+ ),
79
+ 'other_option_label' => array(
80
+ 'default' => __( 'Other', 'happyforms' ),
81
+ 'sanitize' => 'sanitize_text_field'
82
+ ),
83
+ 'other_option_placeholder' => array(
84
+ 'default' => '',
85
+ 'sanitize' => 'sanitize_text_field'
86
+ ),
87
  );
88
 
89
  return happyforms_get_part_customize_fields( $fields, $this->type );
172
  $part_name = happyforms_get_part_name( $part_data, $form_data );
173
 
174
  if ( isset( $request[$part_name] ) ) {
175
+ $value_array = json_decode( stripslashes( $request[$part_name] ) );
176
+
177
+ if ( ! is_array( $value_array ) ) {
178
+ if ( isset( $request[$part_name] ) ) {
179
+ $sanitized_value = sanitize_text_field( $request[$part_name] );
180
+ }
181
+ return $sanitized_value;
182
+ }
183
+
184
+ $sanitized_value = array();
185
+ $sanitized_value[0] = intval( $value_array[0] );
186
+ $sanitized_value[1] = sanitize_text_field( $value_array[1] );
187
  }
188
 
189
  return $sanitized_value;
202
  public function validate_value( $value, $part = array(), $form = array() ) {
203
  $validated_value = $value;
204
 
205
+ if ( ! is_array( $validated_value ) ) {
206
+ if ( 1 === $part['required'] && '' === $validated_value ) {
207
+ return new WP_Error( 'error', happyforms_get_validation_message( 'no_selection' ) );
208
+ }
209
 
210
+ if ( '' !== $validated_value ) {
211
+ if ( ! is_numeric( $validated_value ) ) {
212
+ return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
213
+ }
214
+
215
+ $options = range( 0, count( $part['options'] ) - 1 );
216
+
217
+ if ( ! in_array( intval( $validated_value ), $options ) ) {
218
+ return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
219
+ }
220
  }
221
 
222
+ if ( is_wp_error( $validated_value ) ) {
223
+ return $validated_value;
224
+ }
225
+
226
+ return $this->validate_option_limits( $validated_value, $part, $form );
227
+ }
228
 
229
+ if ( 1 === $part['required'] && empty( $validated_value[0] ) ) {
230
+ return new WP_Error( 'error', happyforms_get_validation_message( 'no_selection' ) );
231
+ }
232
+
233
+ if ( ! empty( $validated_value[0] ) ) {
234
+ if ( ! is_numeric( $validated_value[0] ) ) {
235
  return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
236
  }
237
  }
238
 
239
+ if ( '' === $validated_value[1] ) {
240
+ return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
241
+ }
242
+
243
  return $validated_value;
244
  }
245
 
246
  public function stringify_value( $value, $part, $form ) {
247
  if ( $this->type === $part['type'] ) {
248
+ $original_value = $value;
249
+
250
+ if ( ! is_array( $value ) ) { // standard options
251
+ if ( '' !== $value ) {
252
+ $options = happyforms_get_part_options( $part['options'], $part, $form );
253
+ $value = $options[$value]['label'];
254
+ }
255
+ } else { // other option
256
+ $value = ( ! empty( $value[1] ) ) ? $value[1] : '';
257
+ }
258
+ }
259
+
260
+ return $value;
261
+ }
262
+
263
+ private function validate_option_limits( $value, $part, $form ) {
264
+ foreach( $part['options'] as $o => $option ) {
265
+ $option = wp_parse_args( $option, happyforms_upgrade_get_option_limiter()->get_option_fields() );
266
+
267
+ if ( ! $option['limit_submissions'] || $o !== intval( $value ) ) {
268
+ continue;
269
+ }
270
+
271
+ $limit = intval( $option['limit_submissions_amount'] );
272
+ $form_id = $form['ID'];
273
+ $option_id = $option['id'];
274
+ $count = happyforms_get_form_controller()->count_by_option( $form_id, $option_id, $limit );
275
+
276
+ if ( $count === $limit ) {
277
+ return new WP_Error( 'error', happyforms_get_validation_message( 'field_invalid' ) );
278
  }
279
  }
280
 
295
  return $value;
296
  }
297
 
298
+ public function get_part_options( $options, $part, $form ) {
299
+ if ( is_customize_preview() ) {
300
+ return $options;
301
+ }
302
+
303
+ if ( $this->type !== $part['type'] ) {
304
+ return $options;
305
+ }
306
+
307
+ $options = array_filter( $options, function( $option ) use( $part, $form ) {
308
+ $option = wp_parse_args( $option, $this->get_option_defaults() );
309
+
310
+ if ( ! intval( $option['limit_submissions'] ) ) {
311
+ return true;
312
+ }
313
+
314
+ $limit = intval( $option['limit_submissions_amount'] );
315
+ $form_id = $form['ID'];
316
+ $option_id = $option['id'];
317
+ $count = happyforms_get_form_controller()->count_by_option( $form_id, $option_id, $limit );
318
+
319
+ return $limit > $count;
320
+ } );
321
+
322
+ return $options;
323
+ }
324
+
325
+ public function submission_success( $submission, $form, $message ) {
326
+ $parts = array();
327
+
328
+ foreach( $form['parts'] as $part ) {
329
+ if ( $this->type !== $part['type'] ) {
330
+ continue;
331
+ }
332
+
333
+ foreach( $part['options'] as $o => $option ) {
334
+ $option = wp_parse_args( $option, $this->get_option_defaults() );
335
+
336
+ if ( ! $option['limit_submissions'] ) {
337
+ continue;
338
+ }
339
+
340
+ $part_name = happyforms_get_part_name( $part, $form );
341
+
342
+ if ( ! isset( $_REQUEST[$part_name] ) || ( $o !== intval( $_REQUEST[$part_name] ) ) ) {
343
+ continue;
344
+ }
345
+
346
+ $meta_key = happyforms_get_option_counter_meta_key( $form['ID'], $option['id'] );
347
+ update_post_meta( $message['ID'], $meta_key, 1 );
348
+ }
349
+ }
350
+ }
351
+
352
+ public function append_input( $part, $form ) {
353
+ if ( $this->type !== $part['type'] ) {
354
+ return;
355
+ }
356
+
357
+ if ( 1 == $part['other_option'] ) {
358
+ require( happyforms_get_core_folder() . '/templates/parts/frontend-select-other-option.php' );
359
+ }
360
+ }
361
+
362
  public function html_part_class( $class, $part, $form ) {
363
  if ( $this->type === $part['type'] ) {
364
  if ( happyforms_get_part_value( $part, $form ) ) {
core/helpers/helper-form-templates.php CHANGED
@@ -1612,6 +1612,24 @@ function happyforms_get_part_states( $location = '' ) {
1612
 
1613
  endif;
1614
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1615
  if ( ! function_exists( 'happyforms_get_prefixed_css' ) ):
1616
  /**
1617
  * Prefix CSS selectors with specified prefix.
1612
 
1613
  endif;
1614
 
1615
+ if ( ! function_exists( 'happyforms_get_part_choice_letter' ) ) :
1616
+
1617
+ function happyforms_get_part_choice_letter( $option, $part ) {
1618
+ $letters = range( 'A', 'Z' );
1619
+
1620
+ if ( ! empty( $option ) ) {
1621
+ $option_index = array_search( $option['id'], array_column( $part['options'], 'id' ) );
1622
+ $letter = $letters[$option_index];
1623
+ } else {
1624
+ $options_count = count( $part['options'] );
1625
+ $letter = $letters[$options_count];
1626
+ }
1627
+
1628
+ return $letter;
1629
+ }
1630
+
1631
+ endif;
1632
+
1633
  if ( ! function_exists( 'happyforms_get_prefixed_css' ) ):
1634
  /**
1635
  * Prefix CSS selectors with specified prefix.
core/helpers/helper-misc.php CHANGED
@@ -1002,6 +1002,16 @@ function happyforms_is_admin_screen( $id ) {
1002
 
1003
  endif;
1004
 
 
 
 
 
 
 
 
 
 
 
1005
  if ( ! function_exists( 'happyforms_safe_array_merge' ) ):
1006
 
1007
  function happyforms_safe_array_merge( $a, $b ) {
@@ -1016,4 +1026,4 @@ function happyforms_safe_array_merge( $a, $b ) {
1016
  return $a;
1017
  }
1018
 
1019
- endif;
1002
 
1003
  endif;
1004
 
1005
+ if ( ! function_exists( 'happyforms_get_option_counter_meta_key' ) ):
1006
+
1007
+ function happyforms_get_option_counter_meta_key( $form_id, $option_id ) {
1008
+ $meta_key = "_happyforms_option_counter_{$form_id}_{$option_id}";
1009
+
1010
+ return $meta_key;
1011
+ }
1012
+
1013
+ endif;
1014
+
1015
  if ( ! function_exists( 'happyforms_safe_array_merge' ) ):
1016
 
1017
  function happyforms_safe_array_merge( $a, $b ) {
1026
  return $a;
1027
  }
1028
 
1029
+ endif;
core/templates/partials/frontend-rich-text-toolbar-icons.php CHANGED
@@ -32,8 +32,8 @@
32
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19 13H5v-2h14v2z"/></svg>
33
  </script>
34
  <script type="text/template" id="happyforms-rich-text-icon-return">
35
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M11 9l1.42 1.42L8.83 14H18V4h2v12H8.83l3.59 3.58L11 21l-6-6 6-6z"/></svg>
36
  </script>
37
  <script type="text/template" id="happyforms-rich-text-icon-pencil">
38
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM5.92 19H5v-.92l9.06-9.06.92.92L5.92 19zM20.71 5.63l-2.34-2.34c-.2-.2-.45-.29-.71-.29s-.51.1-.7.29l-1.83 1.83 3.75 3.75 1.83-1.83c.39-.39.39-1.02 0-1.41z"/></svg>
39
- </script>
32
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19 13H5v-2h14v2z"/></svg>
33
  </script>
34
  <script type="text/template" id="happyforms-rich-text-icon-return">
35
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 12"><path d="m-2-6h24v24h-24z" fill="none"/><path d="m17 1v4h-13.2l3.6-3.6-1.4-1.4-6 6 6 6 1.4-1.4-3.6-3.6h15.2v-6z"/></svg>
36
  </script>
37
  <script type="text/template" id="happyforms-rich-text-icon-pencil">
38
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM5.92 19H5v-.92l9.06-9.06.92.92L5.92 19zM20.71 5.63l-2.34-2.34c-.2-.2-.45-.29-.71-.29s-.51.1-.7.29l-1.83 1.83 3.75 3.75 1.83-1.83c.39-.39.39-1.02 0-1.41z"/></svg>
39
+ </script>
core/templates/parts/block-rich-text.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="<?php happyforms_the_part_class( $part, $form ); ?>" id="<?php happyforms_the_part_id( $part, $form ); ?>-part" <?php happyforms_the_part_data_attributes( $part, $form ); ?>>
2
+ <div class="happyforms-part-wrap">
3
+ <?php if ( 'as_placeholder' !== $part['label_placement'] ) : ?>
4
+ <?php happyforms_the_part_label( $part, $form ); ?>
5
+ <?php endif; ?>
6
+
7
+ <div class="happyforms-part__el">
8
+ <?php do_action( 'happyforms_part_input_before', $part, $form ); ?>
9
+
10
+ <textarea id="<?php happyforms_the_part_id( $part, $form ); ?>" name="<?php happyforms_the_part_name( $part, $form ); ?>" rows="5" <?php happyforms_the_part_attributes( $part, $form ); ?>><?php happyforms_the_part_value( $part, $form ); ?></textarea>
11
+
12
+ <?php if ( 'as_placeholder' === $part['label_placement'] ) : ?>
13
+ <?php happyforms_the_part_label( $part, $form ); ?>
14
+ <?php endif; ?>
15
+
16
+ <?php do_action( 'happyforms_part_input_after', $part, $form ); ?>
17
+
18
+ <?php happyforms_print_part_description( $part ); ?>
19
+
20
+ <?php happyforms_part_error_message( happyforms_get_part_name( $part, $form ) ); ?>
21
+ </div>
22
+ </div>
23
+ </div>
core/templates/parts/customize-checkbox.php CHANGED
@@ -54,11 +54,57 @@
54
 
55
  <?php do_action( 'happyforms_part_customize_checkbox_before_advanced_options' ); ?>
56
 
 
 
 
 
 
57
  <p>
58
  <label>
59
  <input type="checkbox" class="checkbox" value="1" <% if ( instance.show_select_all ) { %>checked="checked"<% } %> data-bind="show_select_all" /> <?php _e( 'Add \'select all\' choice', 'happyforms' ); ?>
60
  </label>
61
  </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  <p>
63
  <label for="<%= instance.id %>_display_type"><?php _e( 'Choices display', 'happyforms' ); ?></label>
64
  <select id="<%= instance.id %>_display_type" name="display_type" data-bind="display_type" class="widefat">
@@ -76,20 +122,8 @@
76
  <option class="display-type--inline display-type--block" value="auto"<%= (instance.options_width == 'auto') ? ' selected' : '' %>><?php _e( 'Auto', 'happyforms' ); ?></option>
77
  </select>
78
  </p>
79
- <p>
80
- <label for="<%= instance.id %>_width"><?php _e( 'Width', 'happyforms' ); ?></label>
81
- <select id="<%= instance.id %>_width" name="width" data-bind="width" class="widefat">
82
- <option value="full"<%= (instance.width == 'full') ? ' selected' : '' %>><?php _e( 'Full', 'happyforms' ); ?></option>
83
- <option value="half"<%= (instance.width == 'half') ? ' selected' : '' %>><?php _e( 'Half', 'happyforms' ); ?></option>
84
- <option value="third"<%= (instance.width == 'third') ? ' selected' : '' %>><?php _e( 'Third', 'happyforms' ); ?></option>
85
- <option value="auto"<%= (instance.width == 'auto') ? ' selected' : '' %>><?php _e( 'Auto', 'happyforms' ); ?></option>
86
- </select>
87
- </p>
88
- <p class="width-options" style="display: none">
89
- <label>
90
- <input type="checkbox" class="checkbox apply-all-check" value="" /> <?php _e( 'Apply to all fields', 'happyforms' ); ?>
91
- </label>
92
- </p>
93
 
94
  <?php do_action( 'happyforms_part_customize_checkbox_after_advanced_options' ); ?>
95
 
@@ -117,7 +151,19 @@
117
  <div class="happyforms-part-item-advanced">
118
  <label>
119
  <input type="checkbox" name="is_default" value="1" <% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
 
 
 
120
  </label>
 
 
 
 
 
 
 
 
 
121
  </div>
122
  <div class="option-actions">
123
  <a href="#" class="delete-option"><?php _e( 'Delete', 'happyforms' ); ?></a> |
54
 
55
  <?php do_action( 'happyforms_part_customize_checkbox_before_advanced_options' ); ?>
56
 
57
+ <p>
58
+ <label>
59
+ <input type="checkbox" class="checkbox" value="1" <% if ( instance.display_as_letters ) { %>checked="checked"<% } %> data-bind="display_as_letters" /> <?php _e( 'Show as alphabetical choice', 'happyforms' ); ?>
60
+ </label>
61
+ </p>
62
  <p>
63
  <label>
64
  <input type="checkbox" class="checkbox" value="1" <% if ( instance.show_select_all ) { %>checked="checked"<% } %> data-bind="show_select_all" /> <?php _e( 'Add \'select all\' choice', 'happyforms' ); ?>
65
  </label>
66
  </p>
67
+ <div class="happyforms-nested-settings" data-trigger="show_select_all" style="display: <%= ( instance.show_select_all ) ? 'block' : 'none' %>">
68
+ <p>
69
+ <label for="<%= instance.id %>_select_all_label"><?php _e( '\'Select all\' label', 'happyforms' ); ?></label>
70
+ <input type="text" id="<%= instance.id %>_select_all_label" maxlength="30" class="widefat title" value="<%= instance.select_all_label %>" data-bind="select_all_label" />
71
+ </p>
72
+ </div>
73
+ <p>
74
+ <label>
75
+ <input type="checkbox" class="checkbox" value="1" <% if ( instance.other_option ) { %>checked="checked"<% } %> data-bind="other_option" /> <?php _e( 'Add \'other\' choice', 'happyforms' ); ?>
76
+ </label>
77
+ </p>
78
+ <div class="happyforms-nested-settings" data-trigger="other_option" style="display: <%= ( instance.other_option ) ? 'block' : 'none' %>">
79
+ <p>
80
+ <label for="<%= instance.id %>_other_option_label"><?php _e( '\'Other\' label', 'happyforms' ); ?></label>
81
+ <input type="text" id="<%= instance.id %>_other_option_label" maxlength="30" class="widefat title" value="<%= instance.other_option_label %>" data-bind="other_option_label" />
82
+ </p>
83
+ <p>
84
+ <label for="<%= instance.id %>_other_option_placeholder"><?php _e( '\'Other\' placeholder', 'happyforms' ); ?></label>
85
+ <input type="text" id="<%= instance.id %>_other_option_placeholder" maxlength="50" class="widefat title" value="<%= instance.other_option_placeholder %>" data-bind="other_option_placeholder" />
86
+ </p>
87
+ </div>
88
+ <p>
89
+ <label>
90
+ <input type="checkbox" class="checkbox" value="1" <% if ( instance.shuffle_options ) { %>checked="checked"<% } %> data-bind="shuffle_options" /> <?php _e( 'Randomize choices to prevent bias', 'happyforms' ); ?>
91
+ </label>
92
+ </p>
93
+ <p>
94
+ <label>
95
+ <input type="checkbox" class="checkbox" value="1" <% if ( instance.limit_choices ) { %>checked="checked"<% } %> data-bind="limit_choices" /> <?php _e( 'Limit choices', 'happyforms' ); ?>
96
+ </label>
97
+ </p>
98
+ <div class="happyforms-nested-settings" data-trigger="limit_choices" style="display: <%= ( instance.limit_choices ) ? 'block' : 'none' %>">
99
+ <p>
100
+ <label for="<%= instance.id %>_limit_choices_min"><?php _e( 'Min choices', 'happyforms' ); ?></label>
101
+ <input type="number" id="<%= instance.id %>_limit_choices_min" class="widefat title happyforms-checkbox-limit-min" min="1" value="<%= instance.limit_choices_min %>" data-trigger="limit_choices_min" data-bind="limit_choices_min" />
102
+ </p>
103
+ <p>
104
+ <label for="<%= instance.id %>_limit_choices_max"><?php _e( 'Max choices', 'happyforms' ); ?></label>
105
+ <input type="number" id="<%= instance.id %>_limit_choices_max" class="widefat title happyforms-checkbox-limit-max" min="1" value="<%= instance.limit_choices_max %>" data-trigger="limit_choices_max" data-bind="limit_choices_max" />
106
+ </p>
107
+ </div>
108
  <p>
109
  <label for="<%= instance.id %>_display_type"><?php _e( 'Choices display', 'happyforms' ); ?></label>
110
  <select id="<%= instance.id %>_display_type" name="display_type" data-bind="display_type" class="widefat">
122
  <option class="display-type--inline display-type--block" value="auto"<%= (instance.options_width == 'auto') ? ' selected' : '' %>><?php _e( 'Auto', 'happyforms' ); ?></option>
123
  </select>
124
  </p>
125
+
126
+ <?php happyforms_customize_part_width_control(); ?>
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
  <?php do_action( 'happyforms_part_customize_checkbox_after_advanced_options' ); ?>
129
 
151
  <div class="happyforms-part-item-advanced">
152
  <label>
153
  <input type="checkbox" name="is_default" value="1" <% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
154
+ </label><br>
155
+ <label>
156
+ <input type="checkbox" name="limit_submissions" value="1" class="default-option-switch"<% if ( typeof limit_submissions !== 'undefined' && limit_submissions == 1) { %> checked="checked"<% } %>> <?php _e( 'Limit submissions', 'happyforms' ); ?>
157
  </label>
158
+ <div class="happyforms-nested-settings happyforms-part-item-limit-submission-settings" <% if ( typeof limit_submissions === 'undefined' || limit_submissions != 1 ) { %>style="display: none;"<% } %>>
159
+ <label>
160
+ <?php _e( 'Max submissions', 'happyforms' ); ?>:
161
+ <input type="number" class="widefat" name="limit_submissions_amount" min="1" value="<%= typeof limit_submissions_amount !== 'undefined' ? limit_submissions_amount : '1' %>">
162
+ </label>
163
+ <label>
164
+ <input type="checkbox" name="show_submissions_amount" class="" <% if ( typeof show_submissions_amount !== 'undefined' && show_submissions_amount == 1) { %> checked="checked"<% } %>> <?php _e( 'Show remaining submissions', 'happyforms' ); ?>
165
+ </label><br>
166
+ </div>
167
  </div>
168
  <div class="option-actions">
169
  <a href="#" class="delete-option"><?php _e( 'Delete', 'happyforms' ); ?></a> |
core/templates/parts/customize-multi-line-text.php CHANGED
@@ -21,8 +21,8 @@
21
  <input type="text" id="<%= instance.id %>_placeholder" class="widefat title" value="<%= instance.placeholder %>" data-bind="placeholder" />
22
  </p>
23
  <p class="happyforms-default-value-option">
24
- <label for="<%= instance.id %>_default_value"><?php _e( 'Prefill', 'happyforms' ); ?></label>
25
- <input type="text" id="<%= instance.id %>_default_value" class="widefat title default_value" value="<%= instance.default_value %>" data-bind="default_value" />
26
  </p>
27
  <p>
28
  <label for="<%= instance.id %>_description"><?php _e( 'Hint', 'happyforms' ); ?></label>
@@ -63,6 +63,12 @@
63
  </p>
64
  </div>
65
 
 
 
 
 
 
 
66
  <p>
67
  <label for="<%= instance.id %>_rows"><?php _e( 'Rows', 'happyforms' ); ?></label>
68
  <input type="number" id="<%= instance.id %>_rows" min="2" max="20" class="widefat title" value="<%= instance.rows %>" data-bind="rows" />
21
  <input type="text" id="<%= instance.id %>_placeholder" class="widefat title" value="<%= instance.placeholder %>" data-bind="placeholder" />
22
  </p>
23
  <p class="happyforms-default-value-option">
24
+ <label for="<%= instance.id %>_default_value"><?php _e( 'Prefill', 'happyforms' ); ?></label>
25
+ <input type="text" id="<%= instance.id %>_default_value" class="widefat title default_value" value="<%= instance.default_value %>" data-bind="default_value" />
26
  </p>
27
  <p>
28
  <label for="<%= instance.id %>_description"><?php _e( 'Hint', 'happyforms' ); ?></label>
63
  </p>
64
  </div>
65
 
66
+ <p>
67
+ <label>
68
+ <input type="checkbox" class="checkbox" value="1" <% if ( instance.rich_text ) { %>checked="checked"<% } %> data-bind="rich_text" /> <?php _e( 'Add rich-text editor toolbar', 'happyforms' ); ?>
69
+ </label>
70
+ </p>
71
+
72
  <p>
73
  <label for="<%= instance.id %>_rows"><?php _e( 'Rows', 'happyforms' ); ?></label>
74
  <input type="number" id="<%= instance.id %>_rows" min="2" max="20" class="widefat title" value="<%= instance.rows %>" data-bind="rows" />
core/templates/parts/customize-radio.php CHANGED
@@ -54,6 +54,31 @@
54
 
55
  <?php do_action( 'happyforms_part_customize_radio_before_advanced_options' ); ?>
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  <p>
58
  <label for="<%= instance.id %>_display_type"><?php _e( 'Choices display', 'happyforms' ); ?></label>
59
  <select id="<%= instance.id %>_display_type" name="display_type" data-bind="display_type" class="widefat">
@@ -100,7 +125,19 @@
100
  <div class="happyforms-part-item-advanced">
101
  <label>
102
  <input type="checkbox" name="is_default" value="1" class="default-option-switch"<% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
 
 
 
103
  </label>
 
 
 
 
 
 
 
 
 
104
  </div>
105
  <div class="option-actions">
106
  <a href="#" class="delete-option"><?php _e( 'Delete', 'happyforms' ); ?></a> |
54
 
55
  <?php do_action( 'happyforms_part_customize_radio_before_advanced_options' ); ?>
56
 
57
+ <p>
58
+ <label>
59
+ <input type="checkbox" class="checkbox" value="1" <% if ( instance.display_as_letters ) { %>checked="checked"<% } %> data-bind="display_as_letters" /> <?php _e( 'Show as alphabetical choice', 'happyforms' ); ?>
60
+ </label>
61
+ </p>
62
+ <p>
63
+ <label>
64
+ <input type="checkbox" class="checkbox" value="1" <% if ( instance.other_option ) { %>checked="checked"<% } %> data-bind="other_option" /> <?php _e( 'Add \'other\' choice', 'happyforms' ); ?>
65
+ </label>
66
+ </p>
67
+ <div class="happyforms-nested-settings" data-trigger="other_option" style="display: <%= ( instance.other_option ) ? 'block' : 'none' %>">
68
+ <p>
69
+ <label for="<%= instance.id %>_other_option_label"><?php _e( '\'Other\' label', 'happyforms' ); ?></label>
70
+ <input type="text" id="<%= instance.id %>_other_option_label" maxlength="30" class="widefat title" value="<%= instance.other_option_label %>" data-bind="other_option_label" />
71
+ </p>
72
+ <p>
73
+ <label for="<%= instance.id %>_other_option_placeholder"><?php _e( '\'Other\' placeholder', 'happyforms' ); ?></label>
74
+ <input type="text" id="<%= instance.id %>_other_option_placeholder" maxlength="50" class="widefat title" value="<%= instance.other_option_placeholder %>" data-bind="other_option_placeholder" />
75
+ </p>
76
+ </div>
77
+ <p>
78
+ <label>
79
+ <input type="checkbox" class="checkbox" value="1" <% if ( instance.shuffle_options ) { %>checked="checked"<% } %> data-bind="shuffle_options" /> <?php _e( 'Randomize choices to prevent bias', 'happyforms' ); ?>
80
+ </label>
81
+ </p>
82
  <p>
83
  <label for="<%= instance.id %>_display_type"><?php _e( 'Choices display', 'happyforms' ); ?></label>
84
  <select id="<%= instance.id %>_display_type" name="display_type" data-bind="display_type" class="widefat">
125
  <div class="happyforms-part-item-advanced">
126
  <label>
127
  <input type="checkbox" name="is_default" value="1" class="default-option-switch"<% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
128
+ </label><br>
129
+ <label>
130
+ <input type="checkbox" name="limit_submissions" value="1" class="default-option-switch"<% if ( typeof limit_submissions !== 'undefined' && limit_submissions == 1) { %> checked="checked"<% } %>> <?php _e( 'Limit submissions', 'happyforms' ); ?>
131
  </label>
132
+ <div class="happyforms-nested-settings happyforms-part-item-limit-submission-settings" <% if ( typeof limit_submissions === 'undefined' || limit_submissions != 1 ) { %>style="display: none;"<% } %>>
133
+ <label>
134
+ <?php _e( 'Max submissions', 'happyforms' ); ?>:
135
+ <input type="number" class="widefat" name="limit_submissions_amount" min="1" value="<%= typeof limit_submissions_amount !== 'undefined' ? limit_submissions_amount : '1' %>">
136
+ </label>
137
+ <label>
138
+ <input type="checkbox" name="show_submissions_amount" class="" <% if ( typeof show_submissions_amount !== 'undefined' && show_submissions_amount == 1) { %> checked="checked"<% } %>> <?php _e( 'Show remaining submissions', 'happyforms' ); ?>
139
+ </label><br>
140
+ </div>
141
  </div>
142
  <div class="option-actions">
143
  <a href="#" class="delete-option"><?php _e( 'Delete', 'happyforms' ); ?></a> |
core/templates/parts/customize-select.php CHANGED
@@ -54,19 +54,41 @@
54
  </label>
55
  </p>
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  <?php do_action( 'happyforms_part_customize_select_after_options' ); ?>
58
 
59
  <?php do_action( 'happyforms_part_customize_select_before_advanced_options' ); ?>
60
 
61
- <?php happyforms_customize_part_width_control(); ?>
 
 
 
 
62
 
63
- <?php do_action( 'happyforms_part_customize_select_after_advanced_options' ); ?>
64
 
65
  <p>
66
  <label for="<%= instance.id %>_css_class"><?php _e( 'CSS classes', 'happyforms' ); ?></label>
67
  <input type="text" id="<%= instance.id %>_css_class" class="widefat title" value="<%= instance.css_class %>" data-bind="css_class" />
68
  </p>
69
 
 
 
70
  <div class="happyforms-part-logic-wrap">
71
  <div class="happyforms-logic-view">
72
  <?php happyforms_customize_part_logic(); ?>
@@ -86,7 +108,19 @@
86
  <div class="happyforms-part-item-advanced">
87
  <label>
88
  <input type="checkbox" name="is_default" value="1" class="default-option-switch"<% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
 
 
 
89
  </label>
 
 
 
 
 
 
 
 
 
90
  </div>
91
  <div class="option-actions">
92
  <a href="#" class="delete-option"><?php _e( 'Delete', 'happyforms' ); ?></a> |
54
  </label>
55
  </p>
56
 
57
+ <p>
58
+ <label>
59
+ <input type="checkbox" class="checkbox" value="1" <% if ( instance.other_option ) { %>checked="checked"<% } %> data-bind="other_option" /> <?php _e( 'Add \'other\' choice', 'happyforms' ); ?>
60
+ </label>
61
+ </p>
62
+ <div class="happyforms-nested-settings" data-trigger="other_option" style="display: <%= ( instance.other_option ) ? 'block' : 'none' %>">
63
+ <p>
64
+ <label for="<%= instance.id %>_other_option_label"><?php _e( '\'Other\' label', 'happyforms' ); ?></label>
65
+ <input type="text" id="<%= instance.id %>_other_option_label" maxlength="30" class="widefat title" value="<%= instance.other_option_label %>" data-bind="other_option_label" />
66
+ </p>
67
+ <p>
68
+ <label for="<%= instance.id %>_other_option_placeholder"><?php _e( '\'Other\' placeholder', 'happyforms' ); ?></label>
69
+ <input type="text" id="<%= instance.id %>_other_option_placeholder" maxlength="50" class="widefat title" value="<%= instance.other_option_placeholder %>" data-bind="other_option_placeholder" />
70
+ </p>
71
+ </div>
72
+
73
  <?php do_action( 'happyforms_part_customize_select_after_options' ); ?>
74
 
75
  <?php do_action( 'happyforms_part_customize_select_before_advanced_options' ); ?>
76
 
77
+ <p>
78
+ <label>
79
+ <input type="checkbox" class="checkbox" value="1" <% if ( instance.shuffle_options ) { %>checked="checked"<% } %> data-bind="shuffle_options" /> <?php _e( 'Randomize choices to prevent bias', 'happyforms' ); ?>
80
+ </label>
81
+ </p>
82
 
83
+ <?php happyforms_customize_part_width_control(); ?>
84
 
85
  <p>
86
  <label for="<%= instance.id %>_css_class"><?php _e( 'CSS classes', 'happyforms' ); ?></label>
87
  <input type="text" id="<%= instance.id %>_css_class" class="widefat title" value="<%= instance.css_class %>" data-bind="css_class" />
88
  </p>
89
 
90
+ <?php do_action( 'happyforms_part_customize_select_after_advanced_options' ); ?>
91
+
92
  <div class="happyforms-part-logic-wrap">
93
  <div class="happyforms-logic-view">
94
  <?php happyforms_customize_part_logic(); ?>
108
  <div class="happyforms-part-item-advanced">
109
  <label>
110
  <input type="checkbox" name="is_default" value="1" class="default-option-switch"<% if (is_default == 1) { %> checked="checked"<% } %>> <?php _e( 'Make this choice default', 'happyforms' ); ?>
111
+ </label><br>
112
+ <label>
113
+ <input type="checkbox" name="limit_submissions" value="1" class="default-option-switch"<% if ( typeof limit_submissions !== 'undefined' && limit_submissions == 1) { %> checked="checked"<% } %>> <?php _e( 'Limit submissions', 'happyforms' ); ?>
114
  </label>
115
+ <div class="happyforms-nested-settings happyforms-part-item-limit-submission-settings" <% if ( typeof limit_submissions === 'undefined' || limit_submissions != 1 ) { %>style="display: none;"<% } %>>
116
+ <label>
117
+ <?php _e( 'Max submissions', 'happyforms' ); ?>:
118
+ <input type="number" class="widefat" name="limit_submissions_amount" min="1" value="<%= typeof limit_submissions_amount !== 'undefined' ? limit_submissions_amount : '1' %>">
119
+ </label>
120
+ <label>
121
+ <input type="checkbox" name="show_submissions_amount" class="" <% if ( typeof show_submissions_amount !== 'undefined' && show_submissions_amount == 1) { %> checked="checked"<% } %>> <?php _e( 'Show remaining submissions', 'happyforms' ); ?>
122
+ </label><br>
123
+ </div>
124
  </div>
125
  <div class="option-actions">
126
  <a href="#" class="delete-option"><?php _e( 'Delete', 'happyforms' ); ?></a> |
core/templates/parts/frontend-checkbox-other-option.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $value = happyforms_get_part_value( $part, $form );
3
+ $array_values = array();
4
+ $text_value = '';
5
+ $checked = false;
6
+
7
+ if ( is_array( $value ) ) {
8
+ $array_values = array_filter( $value, 'is_array' );
9
+ }
10
+
11
+ foreach ( $array_values as $index => $array ) {
12
+ if ( 999 === $array[0] ) {
13
+ $checked = true;
14
+
15
+ if ( isset( $array[1] ) ) {
16
+ $text_value = $array[1];
17
+ }
18
+ }
19
+ }
20
+
21
+ $checkmark_content = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path fill="currentColor" d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/></svg>';
22
+
23
+ if ( 1 == $part['display_as_letters'] ) {
24
+ $letter = happyforms_get_part_choice_letter( '', $part );
25
+ $checkmark_content = $letter;
26
+ }
27
+ ?>
28
+ <div class="happyforms-part__option happyforms-part-option happyforms-part-option--other" id="<?php echo $part['id']; ?>_other">
29
+ <label class="option-label">
30
+ <input type="checkbox" class="happyforms-visuallyhidden" name="<?php happyforms_the_part_name( $part, $form ); ?>[]" <?php echo ( $checked ) ? 'checked="checked"' : ''; ?> value="999" data-serialize <?php happyforms_the_part_attributes( $part, $form ); ?>>
31
+ <span class="checkmark"><?php echo $checkmark_content; ?></span>
32
+ <span class="label" id="hf-label-<?php happyforms_the_part_name( $part, $form ); ?>"><?php echo $part['other_option_label']; ?></span>
33
+ </label>
34
+ <input <?php echo ( $checked ) ? 'class="hf-show"' : ''; ?> type="text" aria-labelledby="hf-label-<?php happyforms_the_part_name( $part, $form ); ?>" placeholder="<?php echo $part['other_option_placeholder']; ?>" value="<?php echo $text_value; ?>" <?php happyforms_the_part_attributes( $part, $form ); ?>>
35
+ </div>
core/templates/parts/frontend-checkbox.php CHANGED
@@ -6,15 +6,19 @@
6
  <?php happyforms_print_part_description( $part ); ?>
7
  <?php endif; ?>
8
 
 
 
 
 
9
  <div class="happyforms-part__el">
10
  <?php do_action( 'happyforms_part_input_before', $part, $form ); ?>
11
 
12
  <?php if ( 1 == $part['show_select_all'] ) : ?>
13
- <div class="happyforms-part__option happyforms-part-option">
14
  <label class="option-label">
15
  <input type="checkbox" class="happyforms-visuallyhidden happyforms-select-all">
16
  <span class="checkmark"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path fill="currentColor" d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/></svg></span>
17
- <span class="label"><?php _e( 'Select all', 'happyforms' ); ?></span>
18
  </label>
19
  </div>
20
  <?php endif; ?>
@@ -33,9 +37,13 @@
33
  }
34
  ?>
35
  <label class="option-label">
 
 
 
 
36
  <input type="checkbox" class="happyforms-visuallyhidden happyforms-checkbox" name="<?php happyforms_the_part_name( $part, $form ); ?>[]" value="<?php echo $o; ?>" data-serialize <?php echo $checked; ?> <?php happyforms_the_part_attributes( $part, $form ); ?>>
37
- <span class="checkmark"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path fill="currentColor" d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/></svg></span>
38
- <span class="label"><?php echo esc_attr( $option['label'] ); ?></span>
39
  </label>
40
  <span class="happyforms-part-option__description"><?php echo esc_attr( $option['description'] ); ?></span>
41
  </div>
6
  <?php happyforms_print_part_description( $part ); ?>
7
  <?php endif; ?>
8
 
9
+ <?php
10
+ $checkmark_content = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path fill="currentColor" d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/></svg>';
11
+ ?>
12
+
13
  <div class="happyforms-part__el">
14
  <?php do_action( 'happyforms_part_input_before', $part, $form ); ?>
15
 
16
  <?php if ( 1 == $part['show_select_all'] ) : ?>
17
+ <div class="happyforms-part__option happyforms-part-option happyforms-part-option--select-all">
18
  <label class="option-label">
19
  <input type="checkbox" class="happyforms-visuallyhidden happyforms-select-all">
20
  <span class="checkmark"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path fill="currentColor" d="M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"/></svg></span>
21
+ <span class="label"><?php echo $part['select_all_label']; ?></span>
22
  </label>
23
  </div>
24
  <?php endif; ?>
37
  }
38
  ?>
39
  <label class="option-label">
40
+ <?php if ( 1 == $part['display_as_letters'] ) {
41
+ $letter = happyforms_get_part_choice_letter( $option, $part );
42
+ $checkmark_content = $letter;
43
+ } ?>
44
  <input type="checkbox" class="happyforms-visuallyhidden happyforms-checkbox" name="<?php happyforms_the_part_name( $part, $form ); ?>[]" value="<?php echo $o; ?>" data-serialize <?php echo $checked; ?> <?php happyforms_the_part_attributes( $part, $form ); ?>>
45
+ <span class="checkmark"><?php echo $checkmark_content; ?></span>
46
+ <span class="label"><?php echo esc_attr( $option['label'] ); ?></span><?php echo $option['submissions_left_label']; ?>
47
  </label>
48
  <span class="happyforms-part-option__description"><?php echo esc_attr( $option['description'] ); ?></span>
49
  </div>
core/templates/parts/frontend-multi-line-text.php CHANGED
@@ -11,11 +11,19 @@
11
  <div class="happyforms-part__el">
12
  <?php do_action( 'happyforms_part_input_before', $part, $form ); ?>
13
 
 
 
 
 
14
  <textarea id="<?php happyforms_the_part_id( $part, $form ); ?>" name="<?php happyforms_the_part_name( $part, $form ); ?>" rows="<?php echo $part['rows']; ?>" placeholder="<?php echo esc_attr( $part['placeholder'] ); ?>" <?php happyforms_the_part_attributes( $part, $form ); ?>><?php happyforms_the_part_value( $part, $form ); ?></textarea>
15
  <?php if ( 'as_placeholder' === $part['label_placement'] ) : ?>
16
  <?php happyforms_the_part_label( $part, $form ); ?>
17
  <?php endif; ?>
18
 
 
 
 
 
19
  <?php do_action( 'happyforms_part_input_after', $part, $form ); ?>
20
 
21
  <?php happyforms_part_error_message( happyforms_get_part_name( $part, $form ) ); ?>
11
  <div class="happyforms-part__el">
12
  <?php do_action( 'happyforms_part_input_before', $part, $form ); ?>
13
 
14
+ <?php if ( 1 == $part['rich_text'] ) : ?>
15
+ <div class="happyforms-visual-editor">
16
+ <?php endif; ?>
17
+
18
  <textarea id="<?php happyforms_the_part_id( $part, $form ); ?>" name="<?php happyforms_the_part_name( $part, $form ); ?>" rows="<?php echo $part['rows']; ?>" placeholder="<?php echo esc_attr( $part['placeholder'] ); ?>" <?php happyforms_the_part_attributes( $part, $form ); ?>><?php happyforms_the_part_value( $part, $form ); ?></textarea>
19
  <?php if ( 'as_placeholder' === $part['label_placement'] ) : ?>
20
  <?php happyforms_the_part_label( $part, $form ); ?>
21
  <?php endif; ?>
22
 
23
+ <?php if ( 1 == $part['rich_text'] ) : ?>
24
+ </div>
25
+ <?php endif; ?>
26
+
27
  <?php do_action( 'happyforms_part_input_after', $part, $form ); ?>
28
 
29
  <?php happyforms_part_error_message( happyforms_get_part_name( $part, $form ) ); ?>
core/templates/parts/frontend-radio-other-option.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $value = happyforms_get_part_value( $part, $form );
3
+ $checked = false;
4
+ $text_value = '';
5
+
6
+ if ( is_array( $value ) ) {
7
+ if ( 999 === $value[0] ) {
8
+ $checked = true;
9
+
10
+ if ( isset( $value[1] ) ) {
11
+ $text_value = $value[1];
12
+ }
13
+ }
14
+ }
15
+
16
+ $checkmark_content = '<span class="happyforms-radio-circle"></span>';
17
+
18
+ if ( 1 == $part['display_as_letters'] ) {
19
+ $letter = happyforms_get_part_choice_letter( '', $part );
20
+ $checkmark_content = $letter;
21
+ }
22
+ ?>
23
+ <div class="happyforms-part__option happyforms-part-option happyforms-part-option--other" id="<?php echo $part['id']; ?>_other">
24
+ <label class="option-label">
25
+ <input type="radio" class="happyforms-visuallyhidden" name="<?php happyforms_the_part_name( $part, $form ); ?>" value="999" <?php echo ( $checked ) ? 'checked="checked"' : ''; ?> <?php happyforms_the_part_attributes( $part, $form ); ?>>
26
+ <span class="checkmark"><?php echo $checkmark_content; ?></span>
27
+ <span class="label" id="hf-label-<?php happyforms_the_part_name( $part, $form ); ?>"><?php echo $part['other_option_label']; ?></span>
28
+ </label>
29
+ <input type="text" name="<?php happyforms_the_part_name( $part, $form ); ?>" placeholder="<?php echo $part['other_option_placeholder']; ?>" aria-labelledby="hf-label-<?php happyforms_the_part_name( $part, $form ); ?>" value="<?php echo $text_value; ?>" class="<?php echo ( $checked ) ? 'hf-show' : ''; ?>" <?php happyforms_the_part_attributes( $part, $form ); ?>>
30
+ </div>
core/templates/parts/frontend-radio.php CHANGED
@@ -12,6 +12,7 @@
12
  <?php
13
  $options = happyforms_get_part_options( $part['options'], $part, $form );
14
  $value = happyforms_get_part_value( $part, $form );
 
15
 
16
  foreach( $options as $o => $option ) : ?>
17
  <?php
@@ -24,14 +25,17 @@
24
  if ( false === $checked ) {
25
  $checked = checked( 1, $option['is_default'], false );
26
  }
 
 
 
 
 
27
  ?>
28
  <div class="happyforms-part__option happyforms-part-option" id="<?php echo esc_attr( $option['id'] ); ?>">
29
  <label class="option-label">
30
  <input type="radio" class="happyforms-visuallyhidden" name="<?php happyforms_the_part_name( $part, $form ); ?>" value="<?php echo $o; ?>" <?php echo $checked; ?> <?php happyforms_the_part_attributes( $part, $form ); ?>>
31
- <span class="checkmark">
32
- <span class="happyforms-radio-circle"></span>
33
- </span>
34
- <span class="label"><?php echo esc_attr( $option['label'] ); ?></span>
35
  </label>
36
  <span class="happyforms-part-option__description"><?php echo esc_attr( $option['description'] ); ?></span>
37
  </div>
12
  <?php
13
  $options = happyforms_get_part_options( $part['options'], $part, $form );
14
  $value = happyforms_get_part_value( $part, $form );
15
+ $checkmark_content = '<span class="happyforms-radio-circle"></span>';
16
 
17
  foreach( $options as $o => $option ) : ?>
18
  <?php
25
  if ( false === $checked ) {
26
  $checked = checked( 1, $option['is_default'], false );
27
  }
28
+
29
+ if ( 1 == $part['display_as_letters'] ) {
30
+ $letter = happyforms_get_part_choice_letter( $option, $part );
31
+ $checkmark_content = $letter;
32
+ }
33
  ?>
34
  <div class="happyforms-part__option happyforms-part-option" id="<?php echo esc_attr( $option['id'] ); ?>">
35
  <label class="option-label">
36
  <input type="radio" class="happyforms-visuallyhidden" name="<?php happyforms_the_part_name( $part, $form ); ?>" value="<?php echo $o; ?>" <?php echo $checked; ?> <?php happyforms_the_part_attributes( $part, $form ); ?>>
37
+ <span class="checkmark"><?php echo $checkmark_content; ?></span>
38
+ <span class="label"><?php echo esc_attr( $option['label'] ); ?></span><?php echo $option['submissions_left_label']; ?>
 
 
39
  </label>
40
  <span class="happyforms-part-option__description"><?php echo esc_attr( $option['description'] ); ?></span>
41
  </div>
core/templates/parts/frontend-select-other-option.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ $selected = false;
4
+ $text_value = '';
5
+ $value = happyforms_get_part_value( $part, $form );
6
+
7
+ if ( is_array( $value ) ) {
8
+ if ( 999 === $value[0] ) {
9
+ $selected = true;
10
+
11
+ if ( isset( $value[1] ) ) {
12
+ $text_value = $value[1];
13
+ }
14
+ }
15
+ }
16
+
17
+
18
+ ?>
19
+
20
+ <div class="happyforms-part__option happyforms-part-option happyforms-part-option--other" id="<?php echo $part['id']; ?>_other">
21
+ <input type="text" name="<?php happyforms_the_part_name( $part, $form ); ?>" placeholder="<?php echo $part['other_option_placeholder']; ?>" aria-labelledby="hf-label-<?php happyforms_the_part_name( $part, $form ); ?>" value="<?php echo $text_value; ?>" class="happyforms-select-dropdown-other <?php echo ( $selected ) ? 'hf-show' : ''; ?>" <?php happyforms_the_part_attributes( $part, $form ); ?>>
22
+ </div>
core/templates/parts/frontend-select.php CHANGED
@@ -8,10 +8,20 @@
8
 
9
  <?php
10
  $options = happyforms_get_part_options( $part['options'], $part, $form );
11
- $is_searchable = count( $options ) > 5;
12
- $is_searchable = apply_filters( 'happyforms_is_dropdown_searchable', $is_searchable, $part, $form );
13
  $value = happyforms_get_part_value( $part, $form );
14
- $default_label = ( '' !== $value ) ? $options[$value]['label'] : '';
 
 
 
 
 
 
 
 
 
 
 
 
15
  $placeholder_text = $part['placeholder'];
16
  ?>
17
 
@@ -21,15 +31,26 @@
21
  <div class="happyforms-part__select-wrap">
22
  <input type="hidden" name="<?php happyforms_the_part_name( $part, $form ); ?>" value="<?php echo $value; ?>" data-serialize />
23
 
24
- <input id="<?php happyforms_the_part_id( $part, $form ); ?>" type="text" value="<?php echo $default_label; ?>" placeholder="<?php echo $placeholder_text; ?>" data-searchable="<?php echo $is_searchable ? 'true' : 'false'; ?>" autocomplete="off" <?php happyforms_the_part_attributes( $part, $form ); ?> />
 
 
 
25
 
 
 
 
 
 
 
 
26
  <?php happyforms_select( $options, $part, $form ); ?>
27
  </div>
28
  </div>
29
 
 
 
30
  <?php do_action( 'happyforms_part_input_after', $part, $form ); ?>
31
 
32
- <?php happyforms_part_error_message( happyforms_get_part_name( $part, $form ) ); ?>
33
  </div>
34
  </div>
35
  </div>
8
 
9
  <?php
10
  $options = happyforms_get_part_options( $part['options'], $part, $form );
 
 
11
  $value = happyforms_get_part_value( $part, $form );
12
+ $default_label = '';
13
+
14
+ if ( is_array( $value ) ) {
15
+ $default_label = $part['other_option_label'];
16
+ $value = $value[0];
17
+ } else {
18
+ if ( '' !== $value ) {
19
+ if ( array_key_exists( $value, $options ) ) {
20
+ $default_label = $options[$value]['label'];
21
+ }
22
+ }
23
+ }
24
+
25
  $placeholder_text = $part['placeholder'];
26
  ?>
27
 
31
  <div class="happyforms-part__select-wrap">
32
  <input type="hidden" name="<?php happyforms_the_part_name( $part, $form ); ?>" value="<?php echo $value; ?>" data-serialize />
33
 
34
+ <input id="<?php happyforms_the_part_id( $part, $form ); ?>" type="text" value="<?php echo $default_label; ?>" placeholder="<?php echo $placeholder_text; ?>" data-searchable="<?php echo ( count( $part['options'] ) > 5 ) ? 'true' : 'false'; ?>" autocomplete="off" <?php happyforms_the_part_attributes( $part, $form ); ?> />
35
+
36
+ <?php
37
+ $other_select = ( !empty( $part['other_option'] ) ) ? $part['other_option_label'] : '';
38
 
39
+ if ( !empty( $other_select ) ) {
40
+ $options[] = array(
41
+ 'value' => 999,
42
+ 'label' => $other_select,
43
+ );
44
+ }
45
+ ?>
46
  <?php happyforms_select( $options, $part, $form ); ?>
47
  </div>
48
  </div>
49
 
50
+ <?php happyforms_part_error_message( happyforms_get_part_name( $part, $form ) ); ?>
51
+
52
  <?php do_action( 'happyforms_part_input_after', $part, $form ); ?>
53
 
 
54
  </div>
55
  </div>
56
  </div>
happyforms.php CHANGED
@@ -5,7 +5,7 @@
5
  * Plugin URI: https://happyforms.io
6
  * Description: We're changin' WordPress forms.
7
  * Author: Happyforms
8
- * Version: 1.12.12
9
  * Author URI: https://happyforms.io
10
  * Upgrade URI: https://happyforms.io/upgrade
11
  */
@@ -13,7 +13,7 @@
13
  /**
14
  * The current version of the plugin.
15
  */
16
- define( 'HAPPYFORMS_VERSION', '1.12.12' );
17
 
18
  if ( ! function_exists( 'happyforms_plugin_file' ) ):
19
  /**
5
  * Plugin URI: https://happyforms.io
6
  * Description: We're changin' WordPress forms.
7
  * Author: Happyforms
8
+ * Version: 1.13.0
9
  * Author URI: https://happyforms.io
10
  * Upgrade URI: https://happyforms.io/upgrade
11
  */
13
  /**
14
  * The current version of the plugin.
15
  */
16
+ define( 'HAPPYFORMS_VERSION', '1.13.0' );
17
 
18
  if ( ! function_exists( 'happyforms_plugin_file' ) ):
19
  /**
inc/assets/js/customize.js CHANGED
@@ -1735,6 +1735,8 @@
1735
  'change [data-attribute="characters_label_max"]': 'onCharLimitMaxCharsChange',
1736
  'keyup [data-attribute="no_results_label"]': 'onNoResultsLabelChange',
1737
  'change [data-attribute="no_results_label"]': 'onNoResultsLabelChange',
 
 
1738
  } ),
1739
 
1740
  editors: {
@@ -1898,6 +1900,25 @@
1898
  self.$el.addClass( 'has-searchable-dropdown' );
1899
  }
1900
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1901
  },
1902
 
1903
  ready: function() {
@@ -1969,7 +1990,13 @@
1969
 
1970
  happyForms.previewSend( 'happyforms-form-dom-update', data );
1971
  },
 
 
 
 
1972
 
 
 
1973
  } );
1974
 
1975
  classes.views.FormStyle = classes.views.Base.extend( {
@@ -2518,6 +2545,12 @@
2518
  var default_value = part.get( 'default_value' );
2519
 
2520
  $part.find( ':input' ).val( default_value );
 
 
 
 
 
 
2521
  },
2522
 
2523
  onDescriptionModeChangeCallback: function( id, html ) {
@@ -2620,6 +2653,10 @@
2620
 
2621
  $suffix.text( part.get( 'suffix' ) );
2622
  },
 
 
 
 
2623
  };
2624
 
2625
  happyForms = window.happyForms = new HappyForms();
1735
  'change [data-attribute="characters_label_max"]': 'onCharLimitMaxCharsChange',
1736
  'keyup [data-attribute="no_results_label"]': 'onNoResultsLabelChange',
1737
  'change [data-attribute="no_results_label"]': 'onNoResultsLabelChange',
1738
+
1739
+ 'keyup [data-attribute="submissions_left_label"]': 'onSubmissionsLeftLabelChange',
1740
  } ),
1741
 
1742
  editors: {
1900
  self.$el.addClass( 'has-searchable-dropdown' );
1901
  }
1902
 
1903
+
1904
+ var partsWithLimitedOptions = happyForms.form.get( 'parts' ).filter( function( part ) {
1905
+ var hasOptions = [ 'radio', 'checkbox', 'select' ].includes( part.get( 'type' ) );
1906
+ if ( ! hasOptions ) {
1907
+ return false;
1908
+ }
1909
+ var hasLimitedOptions = part.get( 'options' ).findWhere( {
1910
+ show_submissions_amount: "1"
1911
+ } );
1912
+ if ( hasLimitedOptions ) {
1913
+ return hasLimitedOptions;
1914
+ }
1915
+ return false;
1916
+ } );
1917
+
1918
+ if ( partsWithLimitedOptions.length ) {
1919
+ self.$el.addClass( 'has-show-submissions-left' );
1920
+ }
1921
+
1922
  },
1923
 
1924
  ready: function() {
1990
 
1991
  happyForms.previewSend( 'happyforms-form-dom-update', data );
1992
  },
1993
+ onSubmissionsLeftLabelChange: function( e ) {
1994
+ var data = {
1995
+ callback: 'onSubmissionsLeftLabelChangeCallback',
1996
+ };
1997
 
1998
+ happyForms.previewSend( 'happyforms-form-dom-update', data );
1999
+ },
2000
  } );
2001
 
2002
  classes.views.FormStyle = classes.views.Base.extend( {
2545
  var default_value = part.get( 'default_value' );
2546
 
2547
  $part.find( ':input' ).val( default_value );
2548
+
2549
+ if ( part.get( 'rich_text' ) ) {
2550
+ var instance = $part.data( 'HappyFormPart' );
2551
+
2552
+ instance.editor.setContent( default_value );
2553
+ }
2554
  },
2555
 
2556
  onDescriptionModeChangeCallback: function( id, html ) {
2653
 
2654
  $suffix.text( part.get( 'suffix' ) );
2655
  },
2656
+ onSubmissionsLeftLabelChangeCallback: function( $form ) {
2657
+ var submissionsLeftLabel = happyForms.form.get( 'submissions_left_label' );
2658
+ $( '.happyforms-submissions-left', $form ).text( submissionsLeftLabel );
2659
+ },
2660
  };
2661
 
2662
  happyForms = window.happyForms = new HappyForms();
inc/classes/class-message-controller.php CHANGED
@@ -83,6 +83,118 @@ class HappyForms_Message_Controller {
83
  return self::$instance;
84
  }
85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  /**
87
  * Register hooks.
88
  *
@@ -164,10 +276,12 @@ class HappyForms_Message_Controller {
164
  $form_id = intval( $_REQUEST[$this->form_parameter] );
165
 
166
  // Validate nonce
167
- if ( ! isset( $_REQUEST[$this->nonce_name] )
168
- || ! $this->verify_nonce( $_REQUEST[$this->nonce_name], $form_id ) ) {
 
169
 
170
- wp_send_json_error();
 
171
  }
172
 
173
  $form_controller = happyforms_get_form_controller();
@@ -248,6 +362,10 @@ class HappyForms_Message_Controller {
248
  $this->email_user_confirmation( $form, $submission );
249
  }
250
 
 
 
 
 
251
  /**
252
  * This action fires once a message is succesfully submitted.
253
  *
@@ -258,7 +376,7 @@ class HappyForms_Message_Controller {
258
  *
259
  * @return void
260
  */
261
- do_action( 'happyforms_submission_success', $submission, $form, array() );
262
 
263
  // Render the form
264
  $response['html'] = $form_controller->render( $form );
83
  return self::$instance;
84
  }
85
 
86
+ public function get_post_fields() {
87
+ $fields = array(
88
+ 'post_title' => '',
89
+ 'post_type' => $this->post_type,
90
+ 'post_status' => 'publish',
91
+ );
92
+
93
+ return $fields;
94
+ }
95
+
96
+ /**
97
+ * Create a new message post object.
98
+ *
99
+ * @since 1.0
100
+ *
101
+ * @param array $form The message form data.
102
+ * @param array $submission The message form data.
103
+ *
104
+ * @return int|boolean
105
+ */
106
+ public function create( $form, $submission ) {
107
+ $post_data = $this->get_insert_post_data( $form, $submission );
108
+ $message_id = wp_insert_post( wp_slash( $post_data ), true );
109
+
110
+ wp_update_post( array(
111
+ 'ID' => $message_id,
112
+ 'post_title' => happyforms_get_message_title( $message_id ),
113
+ ) );
114
+
115
+ do_action( 'happyforms_response_created', $message_id, $form );
116
+
117
+ return $message_id;
118
+ }
119
+
120
+ public function get( $post_ids, $force = false ) {
121
+ $args = md5( serialize( func_get_args() ) );
122
+ $key = "_happyforms_cache_responses_get_{$args}";
123
+ $found = false;
124
+ $result = happyforms_cache_get( $key, $found );
125
+
126
+ if ( false === $found || $force ) {
127
+ $result = $this->do_get( $post_ids );
128
+ happyforms_cache_set( $key, $result );
129
+ }
130
+
131
+ return $result;
132
+ }
133
+
134
+ public function do_get( $post_ids = '' ) {
135
+ $query_params = array(
136
+ 'post_type' => $this->post_type,
137
+ 'post_status' => array( 'publish', 'draft', 'trash' ),
138
+ 'posts_per_page' => -1,
139
+ );
140
+
141
+ if ( ! empty( $post_ids ) ) {
142
+ if ( is_numeric( $post_ids ) ) {
143
+ $query_params['p'] = $post_ids;
144
+ } else if ( is_array( $post_ids ) ) {
145
+ $query_params['post__in'] = $post_ids;
146
+ }
147
+ }
148
+
149
+ $messages = get_posts( $query_params );
150
+ $message_entries = array_map( array( $this, 'to_array'), $messages );
151
+
152
+ if ( is_numeric( $post_ids ) ) {
153
+ if ( count( $message_entries ) > 0 ) {
154
+ return $message_entries[0];
155
+ } else {
156
+ return false;
157
+ }
158
+ }
159
+
160
+ return $message_entries;
161
+ }
162
+
163
+ public function get_insert_post_data( $form, $submission ) {
164
+ $defaults = $this->get_post_fields();
165
+ $defaults_meta = $this->get_meta_fields();
166
+ $raw_request = $this->get_raw_request( $form, $submission );
167
+ $message_meta = wp_parse_args( array(
168
+ 'form_id' => $form['ID'],
169
+ 'request' => $raw_request,
170
+ ), $defaults_meta );
171
+ $message_meta = array_merge( $message_meta, $submission );
172
+ $message_meta = happyforms_prefix_meta( $message_meta );
173
+ $post_data = array_merge( $defaults, array(
174
+ 'meta_input' => $message_meta
175
+ ) );
176
+
177
+ return $post_data;
178
+ }
179
+
180
+ public function get_raw_request( $form, $submission ) {
181
+ $request = array();
182
+
183
+ foreach( $form['parts'] as $part_id => $part ) {
184
+ $part_name = happyforms_get_part_name( $part, $form );
185
+
186
+ if ( ! isset( $_REQUEST[$part_name] ) ) {
187
+ continue;
188
+ }
189
+
190
+ $part_class = happyforms_get_part_library()->get_part( $part['type'] );
191
+ $value = $part_class->sanitize_value( $part, $form, $_REQUEST );
192
+ $request[$part_name] = $value;
193
+ }
194
+
195
+ return $request;
196
+ }
197
+
198
  /**
199
  * Register hooks.
200
  *
276
  $form_id = intval( $_REQUEST[$this->form_parameter] );
277
 
278
  // Validate nonce
279
+ if ( apply_filters( 'happyforms_verify_nonce', true ) ) {
280
+ if ( ! isset( $_REQUEST[$this->nonce_name] )
281
+ || ! $this->verify_nonce( $_REQUEST[$this->nonce_name], $form_id ) ) {
282
 
283
+ wp_send_json_error();
284
+ }
285
  }
286
 
287
  $form_controller = happyforms_get_form_controller();
362
  $this->email_user_confirmation( $form, $submission );
363
  }
364
 
365
+ // Create message post
366
+ $message_id = $this->create( $form, $submission );
367
+ $message = $this->get( $message_id );
368
+
369
  /**
370
  * This action fires once a message is succesfully submitted.
371
  *
376
  *
377
  * @return void
378
  */
379
+ do_action( 'happyforms_submission_success', $submission, $form, $message );
380
 
381
  // Render the form
382
  $response['html'] = $form_controller->render( $form );
languages/happyforms.pot CHANGED
@@ -2,9 +2,9 @@
2
  # This file is distributed under the same license as the Happyforms (free) package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Happyforms (free) 1.12.12\n"
6
  "Report-Msgid-Bugs-To: https://thethemefoundry.com/support/\n"
7
- "POT-Creation-Date: 2021-07-20 14:50:30+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=utf-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
@@ -14,8 +14,8 @@ msgstr ""
14
  "X-Generator: grunt-wp-i18n 0.4.9\n"
15
 
16
  #: core/classes/class-block.php:49 core/classes/class-form-controller.php:72
17
- #: core/classes/class-happyforms-core.php:166
18
- #: core/classes/class-happyforms-core.php:167
19
  #: core/classes/class-happyforms-widget.php:13 core/helpers/helper-misc.php:997
20
  msgid "Forms"
21
  msgstr ""
@@ -24,15 +24,15 @@ msgstr ""
24
  msgid "Displays a form."
25
  msgstr ""
26
 
27
- #: core/classes/class-block.php:104 core/classes/parts/class-part-select.php:55
28
  msgid "Choose"
29
  msgstr ""
30
 
31
- #: core/classes/class-block.php:105
32
  msgid "Which form would you like to add here?"
33
  msgstr ""
34
 
35
- #: core/classes/class-block.php:106
36
  msgid "HappyForms Settings"
37
  msgstr ""
38
 
@@ -179,8 +179,8 @@ msgid "Form"
179
  msgstr ""
180
 
181
  #: core/classes/class-form-controller.php:74
182
- #: core/classes/class-happyforms-core.php:182
183
- #: core/classes/class-happyforms-core.php:183
184
  msgid "Add New"
185
  msgstr ""
186
 
@@ -211,16 +211,16 @@ msgstr ""
211
 
212
  #: core/classes/class-form-controller.php:83
213
  #: core/classes/class-form-controller.php:84
214
- #: core/classes/class-happyforms-core.php:174
215
- #: core/classes/class-happyforms-core.php:175
216
  msgid "All Forms"
217
  msgstr ""
218
 
219
- #: core/classes/class-form-controller.php:530
220
  msgid "(no title)"
221
  msgstr ""
222
 
223
- #: core/classes/class-form-controller.php:625
224
  msgid " Copy"
225
  msgstr ""
226
 
@@ -285,13 +285,13 @@ msgid "Min words"
285
  msgstr ""
286
 
287
  #: core/classes/class-form-messages.php:36
288
- #: core/classes/parts/class-part-multi-line-text.php:74
289
  #: core/templates/parts/customize-multi-line-text.php:58
290
  msgid "Max words"
291
  msgstr ""
292
 
293
  #: core/classes/class-form-messages.php:40
294
- #: core/classes/parts/class-part-multi-line-text.php:70
295
  #: core/templates/parts/customize-multi-line-text.php:61
296
  msgid "Min characters"
297
  msgstr ""
@@ -317,82 +317,106 @@ msgstr ""
317
  msgid "(optional)"
318
  msgstr ""
319
 
320
- #: core/classes/class-form-messages.php:81
 
 
 
 
 
 
 
 
 
 
 
 
321
  msgid "Alerts"
322
  msgstr ""
323
 
324
- #: core/classes/class-form-messages.php:82
325
  msgid ""
326
  "These messages are shown to respondents at the very top of the form to "
327
  "communicate the form’s status."
328
  msgstr ""
329
 
330
- #: core/classes/class-form-messages.php:92
331
  msgid "Buttons"
332
  msgstr ""
333
 
334
- #: core/classes/class-form-messages.php:93
335
  msgid ""
336
  "The messages are shown to respondents as they fill out the form to help "
337
  "them trigger an action."
338
  msgstr ""
339
 
340
- #: core/classes/class-form-messages.php:102
341
  msgid "Errors"
342
  msgstr ""
343
 
344
- #: core/classes/class-form-messages.php:103
345
  msgid ""
346
  "These messages are shown to respondents when they try to submit their reply "
347
  "but one or more fields has a mistake."
348
  msgstr ""
349
 
350
- #: core/classes/class-form-messages.php:112
351
  msgid "Hints"
352
  msgstr ""
353
 
354
- #: core/classes/class-form-messages.php:113
355
  msgid ""
356
  "These messages are shown to respondents as they fill out the form to help "
357
  "them avoid mistakes."
358
  msgstr ""
359
 
360
- #: core/classes/class-form-messages.php:122
 
 
 
 
 
 
 
 
361
  msgid "Number too small"
362
  msgstr ""
363
 
364
- #: core/classes/class-form-messages.php:127
365
  msgid "Number too big"
366
  msgstr ""
367
 
368
- #: core/classes/class-form-messages.php:132
369
  msgid "Search couldn't find anything"
370
  msgstr ""
371
 
372
- #: core/classes/class-form-messages.php:137
373
  msgid "Minimum characters"
374
  msgstr ""
375
 
376
- #: core/classes/class-form-messages.php:142
377
  msgid "Maximum characters"
378
  msgstr ""
379
 
380
- #: core/classes/class-form-messages.php:147
381
  msgid "Minimum words"
382
  msgstr ""
383
 
384
- #: core/classes/class-form-messages.php:152
385
  msgid "Maximum words"
386
  msgstr ""
387
 
388
- #: core/classes/class-form-messages.php:157
389
  msgid "Question is optional"
390
  msgstr ""
391
 
392
- #: core/classes/class-form-messages.php:162
393
  msgid "Question is required"
394
  msgstr ""
395
 
 
 
 
 
396
  #: core/classes/class-form-part-library.php:189
397
  msgid "Invalid data"
398
  msgstr ""
@@ -465,6 +489,10 @@ msgstr ""
465
  msgid "Submit form"
466
  msgstr ""
467
 
 
 
 
 
468
  #: core/classes/class-form-styles.php:55
469
  msgid "Left-to-right"
470
  msgstr ""
@@ -627,7 +655,6 @@ msgstr ""
627
  #: core/classes/class-form-styles.php:560
628
  #: core/classes/class-form-styles.php:880
629
  #: core/templates/partials/customize-field-width.php:2
630
- #: core/templates/parts/customize-checkbox.php:80
631
  msgid "Width"
632
  msgstr ""
633
 
@@ -783,14 +810,14 @@ msgstr ""
783
 
784
  #: core/classes/class-form-styles.php:745
785
  #: core/templates/parts/customize-checkbox.php:4
786
- #: core/templates/parts/customize-checkbox.php:114
787
  #: core/templates/parts/customize-email.php:4
788
  #: core/templates/parts/customize-multi-line-text.php:4
789
  #: core/templates/parts/customize-number.php:4
790
  #: core/templates/parts/customize-radio.php:4
791
- #: core/templates/parts/customize-radio.php:97
792
  #: core/templates/parts/customize-select.php:4
793
- #: core/templates/parts/customize-select.php:83
794
  #: core/templates/parts/customize-single-line-text.php:4
795
  msgid "Label"
796
  msgstr ""
@@ -923,22 +950,22 @@ msgstr ""
923
  msgid "Additional CSS"
924
  msgstr ""
925
 
926
- #: core/classes/class-happyforms-core.php:190
927
- #: core/classes/class-happyforms-core.php:191
928
  msgid "Activity"
929
  msgstr ""
930
 
931
- #: core/classes/class-happyforms-core.php:199
932
- #: core/classes/class-happyforms-core.php:200
933
  msgid "Integrations"
934
  msgstr ""
935
 
936
- #: core/classes/class-happyforms-core.php:208
937
- #: core/classes/class-happyforms-core.php:209
938
  msgid "Settings"
939
  msgstr ""
940
 
941
- #: core/classes/class-happyforms-core.php:452
942
  msgid "Add Form"
943
  msgstr ""
944
 
@@ -1086,11 +1113,16 @@ msgstr ""
1086
  msgid "For checkboxes allowing multiple selections."
1087
  msgstr ""
1088
 
1089
- #: core/classes/parts/class-part-checkbox.php:83
1090
- #: core/templates/parts/frontend-checkbox.php:17
1091
  msgid "Select all"
1092
  msgstr ""
1093
 
 
 
 
 
 
 
1094
  #: core/classes/parts/class-part-email.php:8
1095
  msgid "Email Address"
1096
  msgstr ""
@@ -2940,9 +2972,9 @@ msgstr ""
2940
 
2941
  #: core/templates/customize-form-item.php:33
2942
  #: core/templates/customize-form-part-footer.php:3
2943
- #: core/templates/parts/customize-checkbox.php:123
2944
- #: core/templates/parts/customize-radio.php:106
2945
- #: core/templates/parts/customize-select.php:92
2946
  msgid "Delete"
2947
  msgstr ""
2948
 
@@ -3016,41 +3048,36 @@ msgid "Close"
3016
  msgstr ""
3017
 
3018
  #: core/templates/partials/customize-field-width.php:4
3019
- #: core/templates/parts/customize-checkbox.php:72
3020
- #: core/templates/parts/customize-checkbox.php:82
3021
- #: core/templates/parts/customize-radio.php:67
3022
  msgid "Full"
3023
  msgstr ""
3024
 
3025
  #: core/templates/partials/customize-field-width.php:5
3026
- #: core/templates/parts/customize-checkbox.php:73
3027
- #: core/templates/parts/customize-checkbox.php:83
3028
- #: core/templates/parts/customize-radio.php:68
3029
  msgid "Half"
3030
  msgstr ""
3031
 
3032
  #: core/templates/partials/customize-field-width.php:6
3033
- #: core/templates/parts/customize-checkbox.php:74
3034
- #: core/templates/parts/customize-checkbox.php:84
3035
- #: core/templates/parts/customize-radio.php:69
3036
  msgid "Third"
3037
  msgstr ""
3038
 
3039
  #: core/templates/partials/customize-field-width.php:7
3040
- #: core/templates/parts/customize-checkbox.php:75
3041
- #: core/templates/parts/customize-radio.php:70
3042
  msgid "Quarter"
3043
  msgstr ""
3044
 
3045
  #: core/templates/partials/customize-field-width.php:8
3046
- #: core/templates/parts/customize-checkbox.php:76
3047
- #: core/templates/parts/customize-checkbox.php:85
3048
- #: core/templates/parts/customize-radio.php:71
3049
  msgid "Auto"
3050
  msgstr ""
3051
 
3052
  #: core/templates/partials/customize-field-width.php:13
3053
- #: core/templates/parts/customize-checkbox.php:90
3054
  msgid "Apply to all fields"
3055
  msgstr ""
3056
 
@@ -3110,48 +3137,112 @@ msgid "Require an answer"
3110
  msgstr ""
3111
 
3112
  #: core/templates/parts/customize-checkbox.php:59
 
 
 
 
 
3113
  msgid "Add 'select all' choice"
3114
  msgstr ""
3115
 
3116
- #: core/templates/parts/customize-checkbox.php:63
3117
- #: core/templates/parts/customize-radio.php:58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3118
  msgid "Choices display"
3119
  msgstr ""
3120
 
3121
- #: core/templates/parts/customize-checkbox.php:65
3122
- #: core/templates/parts/customize-radio.php:60
3123
  msgid "Horizontal"
3124
  msgstr ""
3125
 
3126
- #: core/templates/parts/customize-checkbox.php:66
3127
- #: core/templates/parts/customize-radio.php:61
3128
  msgid "Vertical"
3129
  msgstr ""
3130
 
3131
- #: core/templates/parts/customize-checkbox.php:70
3132
- #: core/templates/parts/customize-radio.php:65
3133
  msgid "Choices width"
3134
  msgstr ""
3135
 
3136
- #: core/templates/parts/customize-checkbox.php:97
3137
  #: core/templates/parts/customize-email.php:58
3138
- #: core/templates/parts/customize-multi-line-text.php:76
3139
  #: core/templates/parts/customize-number.php:81
3140
- #: core/templates/parts/customize-radio.php:78
3141
- #: core/templates/parts/customize-select.php:66
3142
  #: core/templates/parts/customize-single-line-text.php:57
3143
  msgid "CSS classes"
3144
  msgstr ""
3145
 
3146
- #: core/templates/parts/customize-checkbox.php:119
3147
- #: core/templates/parts/customize-radio.php:102
3148
- #: core/templates/parts/customize-select.php:88
3149
  msgid "Make this choice default"
3150
  msgstr ""
3151
 
3152
- #: core/templates/parts/customize-checkbox.php:124
3153
- #: core/templates/parts/customize-radio.php:107
3154
- #: core/templates/parts/customize-select.php:93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3155
  msgid "More"
3156
  msgstr ""
3157
 
@@ -3186,7 +3277,11 @@ msgstr ""
3186
  msgid "Count"
3187
  msgstr ""
3188
 
3189
- #: core/templates/parts/customize-multi-line-text.php:67
 
 
 
 
3190
  msgid "Rows"
3191
  msgstr ""
3192
 
@@ -3214,10 +3309,6 @@ msgstr ""
3214
  msgid "Click to edit this part."
3215
  msgstr ""
3216
 
3217
- #: inc/classes/class-happyforms.php:119
3218
- msgid "Randomize fields to prevent bias"
3219
- msgstr ""
3220
-
3221
  #: inc/classes/class-happyforms.php:125
3222
  msgid "Use reCAPTCHA"
3223
  msgstr ""
@@ -3238,10 +3329,6 @@ msgstr ""
3238
  msgid "Disable buttons until required fields are answered"
3239
  msgstr ""
3240
 
3241
- #: inc/classes/class-happyforms.php:155
3242
- msgid "Limit submissions"
3243
- msgstr ""
3244
-
3245
  #: inc/classes/class-happyforms.php:161
3246
  msgid "Schedule visibility"
3247
  msgstr ""
2
  # This file is distributed under the same license as the Happyforms (free) package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Happyforms (free) 1.13.0\n"
6
  "Report-Msgid-Bugs-To: https://thethemefoundry.com/support/\n"
7
+ "POT-Creation-Date: 2021-08-11 14:01:53+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=utf-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
14
  "X-Generator: grunt-wp-i18n 0.4.9\n"
15
 
16
  #: core/classes/class-block.php:49 core/classes/class-form-controller.php:72
17
+ #: core/classes/class-happyforms-core.php:173
18
+ #: core/classes/class-happyforms-core.php:174
19
  #: core/classes/class-happyforms-widget.php:13 core/helpers/helper-misc.php:997
20
  msgid "Forms"
21
  msgstr ""
24
  msgid "Displays a form."
25
  msgstr ""
26
 
27
+ #: core/classes/class-block.php:105 core/classes/parts/class-part-select.php:56
28
  msgid "Choose"
29
  msgstr ""
30
 
31
+ #: core/classes/class-block.php:106
32
  msgid "Which form would you like to add here?"
33
  msgstr ""
34
 
35
+ #: core/classes/class-block.php:107
36
  msgid "HappyForms Settings"
37
  msgstr ""
38
 
179
  msgstr ""
180
 
181
  #: core/classes/class-form-controller.php:74
182
+ #: core/classes/class-happyforms-core.php:189
183
+ #: core/classes/class-happyforms-core.php:190
184
  msgid "Add New"
185
  msgstr ""
186
 
211
 
212
  #: core/classes/class-form-controller.php:83
213
  #: core/classes/class-form-controller.php:84
214
+ #: core/classes/class-happyforms-core.php:181
215
+ #: core/classes/class-happyforms-core.php:182
216
  msgid "All Forms"
217
  msgstr ""
218
 
219
+ #: core/classes/class-form-controller.php:531
220
  msgid "(no title)"
221
  msgstr ""
222
 
223
+ #: core/classes/class-form-controller.php:626
224
  msgid " Copy"
225
  msgstr ""
226
 
285
  msgstr ""
286
 
287
  #: core/classes/class-form-messages.php:36
288
+ #: core/classes/parts/class-part-multi-line-text.php:76
289
  #: core/templates/parts/customize-multi-line-text.php:58
290
  msgid "Max words"
291
  msgstr ""
292
 
293
  #: core/classes/class-form-messages.php:40
294
+ #: core/classes/parts/class-part-multi-line-text.php:72
295
  #: core/templates/parts/customize-multi-line-text.php:61
296
  msgid "Min characters"
297
  msgstr ""
317
  msgid "(optional)"
318
  msgstr ""
319
 
320
+ #: core/classes/class-form-messages.php:68
321
+ msgid "Oops. Too many choices are selected."
322
+ msgstr ""
323
+
324
+ #: core/classes/class-form-messages.php:72
325
+ msgid "Oops. Not enough choices are selected."
326
+ msgstr ""
327
+
328
+ #: core/classes/class-form-messages.php:76
329
+ msgid "Remaining"
330
+ msgstr ""
331
+
332
+ #: core/classes/class-form-messages.php:93
333
  msgid "Alerts"
334
  msgstr ""
335
 
336
+ #: core/classes/class-form-messages.php:94
337
  msgid ""
338
  "These messages are shown to respondents at the very top of the form to "
339
  "communicate the form’s status."
340
  msgstr ""
341
 
342
+ #: core/classes/class-form-messages.php:104
343
  msgid "Buttons"
344
  msgstr ""
345
 
346
+ #: core/classes/class-form-messages.php:105
347
  msgid ""
348
  "The messages are shown to respondents as they fill out the form to help "
349
  "them trigger an action."
350
  msgstr ""
351
 
352
+ #: core/classes/class-form-messages.php:114
353
  msgid "Errors"
354
  msgstr ""
355
 
356
+ #: core/classes/class-form-messages.php:115
357
  msgid ""
358
  "These messages are shown to respondents when they try to submit their reply "
359
  "but one or more fields has a mistake."
360
  msgstr ""
361
 
362
+ #: core/classes/class-form-messages.php:124
363
  msgid "Hints"
364
  msgstr ""
365
 
366
+ #: core/classes/class-form-messages.php:125
367
  msgid ""
368
  "These messages are shown to respondents as they fill out the form to help "
369
  "them avoid mistakes."
370
  msgstr ""
371
 
372
+ #: core/classes/class-form-messages.php:132
373
+ msgid "Too many choices are selected"
374
+ msgstr ""
375
+
376
+ #: core/classes/class-form-messages.php:137
377
+ msgid "Not enough choices are selected"
378
+ msgstr ""
379
+
380
+ #: core/classes/class-form-messages.php:144
381
  msgid "Number too small"
382
  msgstr ""
383
 
384
+ #: core/classes/class-form-messages.php:149
385
  msgid "Number too big"
386
  msgstr ""
387
 
388
+ #: core/classes/class-form-messages.php:154
389
  msgid "Search couldn't find anything"
390
  msgstr ""
391
 
392
+ #: core/classes/class-form-messages.php:159
393
  msgid "Minimum characters"
394
  msgstr ""
395
 
396
+ #: core/classes/class-form-messages.php:164
397
  msgid "Maximum characters"
398
  msgstr ""
399
 
400
+ #: core/classes/class-form-messages.php:169
401
  msgid "Minimum words"
402
  msgstr ""
403
 
404
+ #: core/classes/class-form-messages.php:174
405
  msgid "Maximum words"
406
  msgstr ""
407
 
408
+ #: core/classes/class-form-messages.php:179
409
  msgid "Question is optional"
410
  msgstr ""
411
 
412
+ #: core/classes/class-form-messages.php:184
413
  msgid "Question is required"
414
  msgstr ""
415
 
416
+ #: core/classes/class-form-messages.php:189
417
+ msgid "Remaining submissions"
418
+ msgstr ""
419
+
420
  #: core/classes/class-form-part-library.php:189
421
  msgid "Invalid data"
422
  msgstr ""
489
  msgid "Submit form"
490
  msgstr ""
491
 
492
+ #: core/classes/class-form-shuffle.php:43 inc/classes/class-happyforms.php:119
493
+ msgid "Randomize fields to prevent bias"
494
+ msgstr ""
495
+
496
  #: core/classes/class-form-styles.php:55
497
  msgid "Left-to-right"
498
  msgstr ""
655
  #: core/classes/class-form-styles.php:560
656
  #: core/classes/class-form-styles.php:880
657
  #: core/templates/partials/customize-field-width.php:2
 
658
  msgid "Width"
659
  msgstr ""
660
 
810
 
811
  #: core/classes/class-form-styles.php:745
812
  #: core/templates/parts/customize-checkbox.php:4
813
+ #: core/templates/parts/customize-checkbox.php:148
814
  #: core/templates/parts/customize-email.php:4
815
  #: core/templates/parts/customize-multi-line-text.php:4
816
  #: core/templates/parts/customize-number.php:4
817
  #: core/templates/parts/customize-radio.php:4
818
+ #: core/templates/parts/customize-radio.php:122
819
  #: core/templates/parts/customize-select.php:4
820
+ #: core/templates/parts/customize-select.php:105
821
  #: core/templates/parts/customize-single-line-text.php:4
822
  msgid "Label"
823
  msgstr ""
950
  msgid "Additional CSS"
951
  msgstr ""
952
 
953
+ #: core/classes/class-happyforms-core.php:197
954
+ #: core/classes/class-happyforms-core.php:198
955
  msgid "Activity"
956
  msgstr ""
957
 
958
+ #: core/classes/class-happyforms-core.php:206
959
+ #: core/classes/class-happyforms-core.php:207
960
  msgid "Integrations"
961
  msgstr ""
962
 
963
+ #: core/classes/class-happyforms-core.php:215
964
+ #: core/classes/class-happyforms-core.php:216
965
  msgid "Settings"
966
  msgstr ""
967
 
968
+ #: core/classes/class-happyforms-core.php:459
969
  msgid "Add Form"
970
  msgstr ""
971
 
1113
  msgid "For checkboxes allowing multiple selections."
1114
  msgstr ""
1115
 
1116
+ #: core/classes/parts/class-part-checkbox.php:85
 
1117
  msgid "Select all"
1118
  msgstr ""
1119
 
1120
+ #: core/classes/parts/class-part-checkbox.php:93
1121
+ #: core/classes/parts/class-part-radio.php:90
1122
+ #: core/classes/parts/class-part-select.php:80
1123
+ msgid "Other"
1124
+ msgstr ""
1125
+
1126
  #: core/classes/parts/class-part-email.php:8
1127
  msgid "Email Address"
1128
  msgstr ""
2972
 
2973
  #: core/templates/customize-form-item.php:33
2974
  #: core/templates/customize-form-part-footer.php:3
2975
+ #: core/templates/parts/customize-checkbox.php:169
2976
+ #: core/templates/parts/customize-radio.php:143
2977
+ #: core/templates/parts/customize-select.php:126
2978
  msgid "Delete"
2979
  msgstr ""
2980
 
3048
  msgstr ""
3049
 
3050
  #: core/templates/partials/customize-field-width.php:4
3051
+ #: core/templates/parts/customize-checkbox.php:118
3052
+ #: core/templates/parts/customize-radio.php:92
 
3053
  msgid "Full"
3054
  msgstr ""
3055
 
3056
  #: core/templates/partials/customize-field-width.php:5
3057
+ #: core/templates/parts/customize-checkbox.php:119
3058
+ #: core/templates/parts/customize-radio.php:93
 
3059
  msgid "Half"
3060
  msgstr ""
3061
 
3062
  #: core/templates/partials/customize-field-width.php:6
3063
+ #: core/templates/parts/customize-checkbox.php:120
3064
+ #: core/templates/parts/customize-radio.php:94
 
3065
  msgid "Third"
3066
  msgstr ""
3067
 
3068
  #: core/templates/partials/customize-field-width.php:7
3069
+ #: core/templates/parts/customize-checkbox.php:121
3070
+ #: core/templates/parts/customize-radio.php:95
3071
  msgid "Quarter"
3072
  msgstr ""
3073
 
3074
  #: core/templates/partials/customize-field-width.php:8
3075
+ #: core/templates/parts/customize-checkbox.php:122
3076
+ #: core/templates/parts/customize-radio.php:96
 
3077
  msgid "Auto"
3078
  msgstr ""
3079
 
3080
  #: core/templates/partials/customize-field-width.php:13
 
3081
  msgid "Apply to all fields"
3082
  msgstr ""
3083
 
3137
  msgstr ""
3138
 
3139
  #: core/templates/parts/customize-checkbox.php:59
3140
+ #: core/templates/parts/customize-radio.php:59
3141
+ msgid "Show as alphabetical choice"
3142
+ msgstr ""
3143
+
3144
+ #: core/templates/parts/customize-checkbox.php:64
3145
  msgid "Add 'select all' choice"
3146
  msgstr ""
3147
 
3148
+ #: core/templates/parts/customize-checkbox.php:69
3149
+ msgid "'Select all' label"
3150
+ msgstr ""
3151
+
3152
+ #: core/templates/parts/customize-checkbox.php:75
3153
+ #: core/templates/parts/customize-radio.php:64
3154
+ #: core/templates/parts/customize-select.php:59
3155
+ msgid "Add 'other' choice"
3156
+ msgstr ""
3157
+
3158
+ #: core/templates/parts/customize-checkbox.php:80
3159
+ #: core/templates/parts/customize-radio.php:69
3160
+ #: core/templates/parts/customize-select.php:64
3161
+ msgid "'Other' label"
3162
+ msgstr ""
3163
+
3164
+ #: core/templates/parts/customize-checkbox.php:84
3165
+ #: core/templates/parts/customize-radio.php:73
3166
+ #: core/templates/parts/customize-select.php:68
3167
+ msgid "'Other' placeholder"
3168
+ msgstr ""
3169
+
3170
+ #: core/templates/parts/customize-checkbox.php:90
3171
+ #: core/templates/parts/customize-radio.php:79
3172
+ #: core/templates/parts/customize-select.php:79
3173
+ msgid "Randomize choices to prevent bias"
3174
+ msgstr ""
3175
+
3176
+ #: core/templates/parts/customize-checkbox.php:95
3177
+ msgid "Limit choices"
3178
+ msgstr ""
3179
+
3180
+ #: core/templates/parts/customize-checkbox.php:100
3181
+ msgid "Min choices"
3182
+ msgstr ""
3183
+
3184
+ #: core/templates/parts/customize-checkbox.php:104
3185
+ msgid "Max choices"
3186
+ msgstr ""
3187
+
3188
+ #: core/templates/parts/customize-checkbox.php:109
3189
+ #: core/templates/parts/customize-radio.php:83
3190
  msgid "Choices display"
3191
  msgstr ""
3192
 
3193
+ #: core/templates/parts/customize-checkbox.php:111
3194
+ #: core/templates/parts/customize-radio.php:85
3195
  msgid "Horizontal"
3196
  msgstr ""
3197
 
3198
+ #: core/templates/parts/customize-checkbox.php:112
3199
+ #: core/templates/parts/customize-radio.php:86
3200
  msgid "Vertical"
3201
  msgstr ""
3202
 
3203
+ #: core/templates/parts/customize-checkbox.php:116
3204
+ #: core/templates/parts/customize-radio.php:90
3205
  msgid "Choices width"
3206
  msgstr ""
3207
 
3208
+ #: core/templates/parts/customize-checkbox.php:131
3209
  #: core/templates/parts/customize-email.php:58
3210
+ #: core/templates/parts/customize-multi-line-text.php:82
3211
  #: core/templates/parts/customize-number.php:81
3212
+ #: core/templates/parts/customize-radio.php:103
3213
+ #: core/templates/parts/customize-select.php:86
3214
  #: core/templates/parts/customize-single-line-text.php:57
3215
  msgid "CSS classes"
3216
  msgstr ""
3217
 
3218
+ #: core/templates/parts/customize-checkbox.php:153
3219
+ #: core/templates/parts/customize-radio.php:127
3220
+ #: core/templates/parts/customize-select.php:110
3221
  msgid "Make this choice default"
3222
  msgstr ""
3223
 
3224
+ #: core/templates/parts/customize-checkbox.php:156
3225
+ #: core/templates/parts/customize-radio.php:130
3226
+ #: core/templates/parts/customize-select.php:113
3227
+ #: inc/classes/class-happyforms.php:155
3228
+ msgid "Limit submissions"
3229
+ msgstr ""
3230
+
3231
+ #: core/templates/parts/customize-checkbox.php:160
3232
+ #: core/templates/parts/customize-radio.php:134
3233
+ #: core/templates/parts/customize-select.php:117
3234
+ msgid "Max submissions"
3235
+ msgstr ""
3236
+
3237
+ #: core/templates/parts/customize-checkbox.php:164
3238
+ #: core/templates/parts/customize-radio.php:138
3239
+ #: core/templates/parts/customize-select.php:121
3240
+ msgid "Show remaining submissions"
3241
+ msgstr ""
3242
+
3243
+ #: core/templates/parts/customize-checkbox.php:170
3244
+ #: core/templates/parts/customize-radio.php:144
3245
+ #: core/templates/parts/customize-select.php:127
3246
  msgid "More"
3247
  msgstr ""
3248
 
3277
  msgid "Count"
3278
  msgstr ""
3279
 
3280
+ #: core/templates/parts/customize-multi-line-text.php:68
3281
+ msgid "Add rich-text editor toolbar"
3282
+ msgstr ""
3283
+
3284
+ #: core/templates/parts/customize-multi-line-text.php:73
3285
  msgid "Rows"
3286
  msgstr ""
3287
 
3309
  msgid "Click to edit this part."
3310
  msgstr ""
3311
 
 
 
 
 
3312
  #: inc/classes/class-happyforms.php:125
3313
  msgid "Use reCAPTCHA"
3314
  msgstr ""
3329
  msgid "Disable buttons until required fields are answered"
3330
  msgstr ""
3331
 
 
 
 
 
3332
  #: inc/classes/class-happyforms.php:161
3333
  msgid "Schedule visibility"
3334
  msgstr ""
readme.txt CHANGED
@@ -3,9 +3,9 @@
3
  Contributors: happyforms
4
  Tags: contact form, contact form plugin, forms, form builder, custom form, intake form, registration form, survey form, quiz form, payment form, newsletter form, giveaway form, fundraising form, donation form, nomination form, order form, questionnaire form, onboarding form, contract form, estimate form, research form, qualitative form, quantitative form, membership form, mobile form, feedback form, release form, inquiry form, request form, evaluation form, RSVP form, reservation form, sales lead form, popup form, quote form, event form, booking form, application form, agreement form, safety form, compliance form, assessment form, sponsorship form, report form, file upload form, satisfaction form
5
  Requires at least: 4.8
6
- Tested up to: 5.7.2
7
  Requires PHP: 5.3
8
- Stable tag: 1.12.12
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -151,6 +151,16 @@ Aw, honestly, the thought that you're writing about our contact form builder is
151
 
152
  == Changelog ==
153
 
 
 
 
 
 
 
 
 
 
 
154
  = 1.12.12 =
155
  * Improvement: Fields with validation errors are now marked with a CSS class for easier styling.
156
  * Improvement: Browser autocompletion has been disabled everywhere for more consistent behavior.
@@ -833,6 +843,9 @@ Aw, honestly, the thought that you're writing about our contact form builder is
833
 
834
  == Upgrade Notice ==
835
 
 
 
 
836
  = 1.12.12 =
837
  * Miscellaneous improvements and bugfixes.
838
 
3
  Contributors: happyforms
4
  Tags: contact form, contact form plugin, forms, form builder, custom form, intake form, registration form, survey form, quiz form, payment form, newsletter form, giveaway form, fundraising form, donation form, nomination form, order form, questionnaire form, onboarding form, contract form, estimate form, research form, qualitative form, quantitative form, membership form, mobile form, feedback form, release form, inquiry form, request form, evaluation form, RSVP form, reservation form, sales lead form, popup form, quote form, event form, booking form, application form, agreement form, safety form, compliance form, assessment form, sponsorship form, report form, file upload form, satisfaction form
5
  Requires at least: 4.8
6
+ Tested up to: 5.8
7
  Requires PHP: 5.3
8
+ Stable tag: 1.13.0
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
151
 
152
  == Changelog ==
153
 
154
+ = 1.13.0 =
155
+ * New feature: Long Answer field now supports rich-text editing.
156
+ * New feature: Alphabetical choices, "other" choice, randomization, submission limiting support for Single Choice field.
157
+ * New feature: Alphabetical choices, "other" choice, randomization, submission limiting support for Multiple Choice field.
158
+ * New feature: "Other" choice, randomization, submission limiting support for Dropdown field.
159
+ * Bugfix: Widget was misbehaving with new block-based Widgets screen.
160
+ * Bugfix: Number field custom separators weren't working correctly.
161
+ * Bugfix: Prefilled Long Answer field had styling glitches.
162
+ * Bugfix: Long Answer field Limit words/characters controls had validation issues.
163
+
164
  = 1.12.12 =
165
  * Improvement: Fields with validation errors are now marked with a CSS class for easier styling.
166
  * Improvement: Browser autocompletion has been disabled everywhere for more consistent behavior.
843
 
844
  == Upgrade Notice ==
845
 
846
+ = 1.13.0 =
847
+ * Advanced controls for Single Choice, Multiple Choice and Dropdown fields, bugfixes.
848
+
849
  = 1.12.12 =
850
  * Miscellaneous improvements and bugfixes.
851