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

Version Description

  • Improvement: Date part is renamed to Date & Time and offers a lot more options, including minimum and maximum value for year and hour inputs, and an option to set inputs to current date and time by default.
  • Improvement: Added option to specify the subject of email alerts sent to site owners.
  • Improvement: Email alerts now support From, Reply To and multiple CC email addresses.
  • Improvement: Address part now lets users grab their current location automatically.
  • Bug fix: Responsive behavior of Address and Date & Time form parts.
  • Bug fix: Updated code to support versions of PHP down to 5.2.4.
Download this release

Release Info

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

Code changes from version 1.4.2 to 1.4.3

assets/css/customize.css CHANGED
@@ -664,6 +664,15 @@ ul.happyforms-style-controls .customize-control {
664
  box-sizing: border-box;
665
  }
666
 
 
 
 
 
 
 
 
 
 
667
  /* Style view */
668
  .happyforms-stack-view.happyforms-style-view {
669
  padding: 0;
@@ -767,6 +776,7 @@ ul.happyforms-style-controls .customize-control.happyforms-divider-control h3 {
767
  .customize-control .mce-tinymce {
768
  border: 1px solid #ddd;
769
  box-sizing: border-box;
 
770
  }
771
 
772
  /* Range control styles */
664
  box-sizing: border-box;
665
  }
666
 
667
+ .happyforms-customize-controls-wrap--side-by-side {
668
+ display: flex;
669
+ justify-content: space-between;
670
+ }
671
+
672
+ .happyforms-customize-controls-wrap--side-by-side > * {
673
+ width: 47%;
674
+ }
675
+
676
  /* Style view */
677
  .happyforms-stack-view.happyforms-style-view {
678
  padding: 0;
776
  .customize-control .mce-tinymce {
777
  border: 1px solid #ddd;
778
  box-sizing: border-box;
779
+ box-shadow: none;
780
  }
781
 
782
  /* Range control styles */
assets/css/frontend.css CHANGED
@@ -917,35 +917,72 @@ h3.happyforms-form__title {
917
  border-color: transparent;
918
  }
919
 
920
- .happyforms-part-date--date .happyforms-part-date__time-input {
921
- display: none;
922
- }
923
-
924
- .happyforms-part-date--time .happyforms-part-date__date-input {
925
- display: none;
926
- }
927
-
928
  .happyforms-part-date--datetime.happyforms-part--width-half .happyforms-part__el,
929
  .happyforms-part-date--datetime.happyforms-part--width-third .happyforms-part__el {
930
  flex-wrap: wrap;
931
  }
932
 
933
- .happyforms-part-date--datetime.happyforms-part--width-half .happyforms-part__select-wrap,
934
- .happyforms-part-date--datetime.happyforms-part--width-third .happyforms-part__select-wrap {
935
  min-width: 33.333%;
936
  margin-bottom: 15px;
937
  }
938
 
939
- .happyforms-part__select-wrap {
 
940
  position: relative;
941
  flex: 1;
942
  padding: 0 0 0 2%;
943
  }
944
 
945
- .happyforms-part__select-wrap:first-of-type {
 
946
  padding-left: 0 !important;
947
  }
948
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
949
  .happyforms-part__select-wrap:after {
950
  content: '';
951
  position: absolute;
@@ -981,12 +1018,33 @@ h3.happyforms-form__title {
981
 
982
  .happyforms-part--date .happyforms-part__el {
983
  display: flex;
 
984
  }
985
 
986
  .happyforms-part__select-wrap:last-child {
987
  padding-right: 0;
988
  }
989
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
990
  /* parts - rating */
991
 
992
  .happyforms-part--rating .happyforms-rating-item-wrap {
@@ -1071,6 +1129,8 @@ h3.happyforms-form__title {
1071
 
1072
  .happyforms-part--address.happyforms-part--address-country-city .happyforms-part-el-wrap {
1073
  display: flex;
 
 
1074
  }
1075
 
1076
  .happyforms-part--address .happyforms-part__el {
@@ -1078,11 +1138,25 @@ h3.happyforms-form__title {
1078
  }
1079
 
1080
  .happyforms-part--address.happyforms-part--address-country-city .happyforms-part__el {
1081
- flex: 1;
 
 
 
 
 
 
 
 
 
 
 
 
1082
  }
1083
 
1084
- .happyforms-part--address.happyforms-part--address-country-city .happyforms-part__el:first-child {
1085
- padding-right: 2%;
 
 
1086
  }
1087
 
1088
  .happyforms-form--part-outer-padding-narrow .happyforms-part--address.happyforms-part--address-country-city .happyforms-part__el:first-child {
@@ -1093,7 +1167,7 @@ h3.happyforms-form__title {
1093
  padding-right: 6%;
1094
  }
1095
 
1096
- .happyforms-part__el--address--results {
1097
  position: absolute;
1098
  z-index: 9999;
1099
  width: 100%;
@@ -1102,7 +1176,7 @@ h3.happyforms-form__title {
1102
  border-radius: 6px;
1103
  }
1104
 
1105
- .happyforms-part__el--address--results ul {
1106
  list-style-type: none;
1107
  padding: 0;
1108
  border: 1px solid;
@@ -1113,7 +1187,7 @@ h3.happyforms-form__title {
1113
  background-color: var(--happyforms-color-part-background);
1114
  }
1115
 
1116
- .happyforms-part__el--address--results ul li {
1117
  cursor: pointer;
1118
  list-style: none;
1119
  padding: 3px 10px;
@@ -1126,12 +1200,12 @@ h3.happyforms-form__title {
1126
  color: var(--happyforms-color-part-value);
1127
  }
1128
 
1129
- .happyforms-part__el--address--results ul li:hover {
1130
  border-color: #000;
1131
  border-color: var(--happyforms-color-part-border-focus);
1132
  }
1133
 
1134
- .happyforms-part__el--address--results-google ul:after {
1135
  content: "";
1136
  padding: 5px 10px 0;
1137
  height: 16px;
@@ -1144,6 +1218,122 @@ h3.happyforms-form__title {
1144
  background-size: 120px 14px;
1145
  }
1146
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1147
  /* submit button */
1148
 
1149
  .happyforms-part--submit {
@@ -1279,7 +1469,7 @@ input[type=submit].happyforms-button--submit:focus {
1279
  .happyforms-form--part-border-radius-square .happyforms-part input[type=range],
1280
  .happyforms-form--part-border-radius-square .happyforms-part textarea,
1281
  .happyforms-form--part-border-radius-square .happyforms-part select,
1282
- .happyforms-form--part-border-radius-square .happyforms-part__el--address--results ul {
1283
  border-radius: 0px;
1284
  }
1285
 
@@ -1299,7 +1489,7 @@ input[type=submit].happyforms-button--submit:focus {
1299
  padding-right: 25px;
1300
  }
1301
 
1302
- .happyforms-form--part-border-radius-pill .happyforms-part__el--address--results ul {
1303
  border-radius: 25px;
1304
  }
1305
 
@@ -1546,8 +1736,7 @@ input[type=submit].happyforms-button--submit:focus {
1546
  font-weight: normal;
1547
  }
1548
 
1549
- /* helpers */
1550
- .happyforms-visuallyhidden {
1551
  position: absolute;
1552
  border: 0;
1553
  clip: rect(0 0 0 0);
917
  border-color: transparent;
918
  }
919
 
 
 
 
 
 
 
 
 
920
  .happyforms-part-date--datetime.happyforms-part--width-half .happyforms-part__el,
921
  .happyforms-part-date--datetime.happyforms-part--width-third .happyforms-part__el {
922
  flex-wrap: wrap;
923
  }
924
 
925
+ .happyforms-part-date--datetime.happyforms-part--width-half .happyforms-part--date__input-wrap,
926
+ .happyforms-part-date--datetime.happyforms-part--width-third .happyforms-part--date__input-wrap {
927
  min-width: 33.333%;
928
  margin-bottom: 15px;
929
  }
930
 
931
+ .happyforms-part__select-wrap,
932
+ .happyforms-part--date__input-wrap {
933
  position: relative;
934
  flex: 1;
935
  padding: 0 0 0 2%;
936
  }
937
 
938
+ .happyforms-part__select-wrap:first-of-type,
939
+ .happyforms-part--date__input-wrap:first-of-type {
940
  padding-left: 0 !important;
941
  }
942
 
943
+ .happyforms-part--date input[type=text] {
944
+ padding-top: 8px;
945
+ padding-bottom: 9px;
946
+ }
947
+
948
+ .happyforms-part-date__time-input--minutes {
949
+ padding-left: 0;
950
+ }
951
+
952
+ .happyforms-part--date__time-separator {
953
+ width: 2%;
954
+ text-align: center;
955
+ }
956
+
957
+ .happyforms-part--date__time-separator span {
958
+ position: relative;
959
+ top: 7px;
960
+ }
961
+
962
+ @media screen and (max-width: 800px) {
963
+ .happyforms-part-date__date-input {
964
+ flex: auto;
965
+ width: 33%;
966
+ }
967
+
968
+ .happyforms-part-date--datetime .happyforms-part-date__time-input {
969
+ flex: auto;
970
+ width: 33%;
971
+ }
972
+
973
+ .happyforms-part-date__time-input {
974
+ margin-top: 20px;
975
+ }
976
+
977
+ .happyforms-part-date__time-input--hours {
978
+ padding-left: 0;
979
+ }
980
+
981
+ .happyforms-part--date__time-separator span {
982
+ top: 28px;
983
+ }
984
+ }
985
+
986
  .happyforms-part__select-wrap:after {
987
  content: '';
988
  position: absolute;
1018
 
1019
  .happyforms-part--date .happyforms-part__el {
1020
  display: flex;
1021
+ flex-flow: row wrap;
1022
  }
1023
 
1024
  .happyforms-part__select-wrap:last-child {
1025
  padding-right: 0;
1026
  }
1027
 
1028
+ .happyforms-part-date__time-input .happyforms-spinner-arrow {
1029
+ cursor: pointer;
1030
+ content: '';
1031
+ display: block;
1032
+ position: absolute;
1033
+ bottom: 3px;
1034
+ right: 0;
1035
+ width: 22px;
1036
+ height: 22px;
1037
+ background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgdmlld0JveD0iMCAwIDE4IDE4Ij48cGF0aCBkPSJNNSA4bDQgNCA0LTR6Ii8+PC9zdmc+);
1038
+ background-position: left top;
1039
+ background-size: cover;
1040
+ }
1041
+
1042
+ .happyforms-part-date__time-input .happyforms-spinner-arrow--up {
1043
+ top: 3px;
1044
+ bottom: auto;
1045
+ transform: rotateX(-180deg);
1046
+ }
1047
+
1048
  /* parts - rating */
1049
 
1050
  .happyforms-part--rating .happyforms-rating-item-wrap {
1129
 
1130
  .happyforms-part--address.happyforms-part--address-country-city .happyforms-part-el-wrap {
1131
  display: flex;
1132
+ flex-direction: row;
1133
+ flex-wrap: wrap;
1134
  }
1135
 
1136
  .happyforms-part--address .happyforms-part__el {
1138
  }
1139
 
1140
  .happyforms-part--address.happyforms-part--address-country-city .happyforms-part__el {
1141
+ width: 50%;
1142
+ }
1143
+
1144
+ @media screen and (max-width: 800px) {
1145
+ .happyforms-part--address.happyforms-part--address-country-city .happyforms-part__el {
1146
+ width: 100%;
1147
+ margin-bottom: 20px;
1148
+ }
1149
+
1150
+ .happyforms-part--address.happyforms-part--address-country-city .happyforms-part__el:last-child {
1151
+ width: 100%;
1152
+ margin-bottom: 0;
1153
+ }
1154
  }
1155
 
1156
+ @media screen and (min-width: 800px) {
1157
+ .happyforms-part--address.happyforms-part--address-country-city .happyforms-part__el:first-child {
1158
+ padding-right: 2%;
1159
+ }
1160
  }
1161
 
1162
  .happyforms-form--part-outer-padding-narrow .happyforms-part--address.happyforms-part--address-country-city .happyforms-part__el:first-child {
1167
  padding-right: 6%;
1168
  }
1169
 
1170
+ .happyforms-part--address__results {
1171
  position: absolute;
1172
  z-index: 9999;
1173
  width: 100%;
1176
  border-radius: 6px;
1177
  }
1178
 
1179
+ .happyforms-part--address__results ul {
1180
  list-style-type: none;
1181
  padding: 0;
1182
  border: 1px solid;
1187
  background-color: var(--happyforms-color-part-background);
1188
  }
1189
 
1190
+ .happyforms-part--address__results ul li {
1191
  cursor: pointer;
1192
  list-style: none;
1193
  padding: 3px 10px;
1200
  color: var(--happyforms-color-part-value);
1201
  }
1202
 
1203
+ .happyforms-part--address__results ul li:hover {
1204
  border-color: #000;
1205
  border-color: var(--happyforms-color-part-border-focus);
1206
  }
1207
 
1208
+ .happyforms-part--address__results--google ul:after {
1209
  content: "";
1210
  padding: 5px 10px 0;
1211
  height: 16px;
1218
  background-size: 120px 14px;
1219
  }
1220
 
1221
+ .happyforms-part--address-has-geolocation .happyforms-part__dummy-input {
1222
+ display: flex;
1223
+ }
1224
+
1225
+ .happyforms-part--address-has-geolocation .happyforms-part__dummy-input > input {
1226
+ flex: 1 0;
1227
+ }
1228
+
1229
+ .happyforms-part--address-has-geolocation .happyforms-part__dummy-input > input[type=text],
1230
+ .happyforms-part--address-has-geolocation .happyforms-part__dummy-input > input[type=text]:focus {
1231
+ border-right: none !important;
1232
+ border-top-right-radius: 0 !important;
1233
+ border-bottom-right-radius: 0 !important;
1234
+ }
1235
+
1236
+ a.address-geolocate {
1237
+ flex: 0 0 auto;
1238
+ order: 2;
1239
+ height: auto !important;
1240
+ padding: 10px;
1241
+ border-width: 1px;
1242
+ border-style: solid;
1243
+ border-color: #dbdbdb;
1244
+ border-color: var(--happyforms-color-part-border);
1245
+ border-radius: 6px;
1246
+ border-left: none !important;
1247
+ border-top-left-radius: 0 !important;
1248
+ border-bottom-left-radius: 0 !important;
1249
+ box-shadow: none;
1250
+ font-style: normal;
1251
+ font-size: 16px;
1252
+ font-size: var(--happyforms-part-value-font-size);
1253
+ font-weight: normal;
1254
+ text-decoration: underline;
1255
+ background-color: #fff;
1256
+ background-color: var(--happyforms-color-part-background);
1257
+ color: #000;
1258
+ color: var(--happyforms-color-part-value);
1259
+ transition: border-color 0.4s;
1260
+ transition: border-color var(--happyforms-transition-duration);
1261
+ }
1262
+
1263
+ a.address-geolocate svg {
1264
+ display: inline-block;
1265
+ vertical-align: middle;
1266
+ width: calc(var(--happyforms-part-value-font-size) * (96 / 100) + 3px);
1267
+ height: calc(var(--happyforms-part-value-font-size) * (96 / 100));
1268
+ fill: #000;
1269
+ fill: var(--happyforms-color-part-value);
1270
+ }
1271
+
1272
+ a.address-geolocate.disabled {
1273
+ pointer-events: none;
1274
+ color: black;
1275
+ }
1276
+
1277
+ a.address-geolocate span {
1278
+ display: none;
1279
+ }
1280
+
1281
+ @media screen and (min-width: 800px) {
1282
+ a.address-geolocate span {
1283
+ display: inline;
1284
+ }
1285
+ }
1286
+
1287
+ .happyforms-part input[type=text]:focus + a.address-geolocate {
1288
+ border-width: 1px;
1289
+ border-style: solid;
1290
+ border-color: #000;
1291
+ border-color: var(--happyforms-color-part-border-focus);
1292
+ border-left: none;
1293
+ border-top-left-radius: 0;
1294
+ border-bottom-left-radius: 0;
1295
+ background-color: #fff;
1296
+ background-color: var(--happyforms-color-part-background-focus);
1297
+ }
1298
+
1299
+ .happyforms-form--part-border-radius-square a.address-geolocate {
1300
+ border-radius: 0px;
1301
+ }
1302
+
1303
+ .happyforms-form--part-border-radius-pill a.address-geolocate {
1304
+ border-radius: 25px;
1305
+ padding-left: 25px;
1306
+ padding-right: 25px;
1307
+ }
1308
+
1309
+ .happyforms-form--part-borders-bottom-only a.address-geolocate {
1310
+ border-top-color: transparent;
1311
+ border-left-color: transparent;
1312
+ border-right-color: transparent;
1313
+ }
1314
+
1315
+ .happyforms-form--part-disable-transitions a.address-geolocate {
1316
+ transition-duration: 0s;
1317
+ }
1318
+
1319
+ .happyforms-form--part-border-off a.address-geolocate {
1320
+ border-color: transparent !important;
1321
+ }
1322
+
1323
+ .happyforms-form--part-borders-bottom-only a.address-geolocate {
1324
+ border-top-color: transparent !important;
1325
+ border-left-color: transparent !important;
1326
+ border-right-color: transparent !important;
1327
+ }
1328
+
1329
+ .happyforms-form--part-inner-padding-narrow a.address-geolocate {
1330
+ padding: 5px;
1331
+ }
1332
+
1333
+ .happyforms-form--part-inner-padding-wide a.address-geolocate {
1334
+ padding: 15px;
1335
+ }
1336
+
1337
  /* submit button */
1338
 
1339
  .happyforms-part--submit {
1469
  .happyforms-form--part-border-radius-square .happyforms-part input[type=range],
1470
  .happyforms-form--part-border-radius-square .happyforms-part textarea,
1471
  .happyforms-form--part-border-radius-square .happyforms-part select,
1472
+ .happyforms-form--part-border-radius-square .happyforms-part--address__results ul {
1473
  border-radius: 0px;
1474
  }
1475
 
1489
  padding-right: 25px;
1490
  }
1491
 
1492
+ .happyforms-form--part-border-radius-pill .happyforms-part--address__results ul {
1493
  border-radius: 25px;
1494
  }
1495
 
1736
  font-weight: normal;
1737
  }
1738
 
1739
+ .happyforms-visuallyhidden, .ui-helper-hidden-accessible {
 
1740
  position: absolute;
1741
  border: 0;
1742
  clip: rect(0 0 0 0);
assets/css/preview.css CHANGED
@@ -47,23 +47,19 @@ body::before, body::after {
47
  color: #00a0d2;
48
  }
49
 
50
- /*.happyforms-flex {
51
- padding: 2.5%;
52
- overflow: visible;
53
- }*/
54
-
55
- /*.happyforms-form h3.happyforms-form__title {
56
- display: flex !important;
57
- align-items: center;
58
- padding: 0;
59
- }*/
60
-
61
  .happyforms-preview-badge {
62
  padding-left: 10px;
63
  font-weight: 400;
64
  font-size: 16px;
65
  }
66
 
 
 
 
 
 
 
 
67
  .happyforms-part.highlighted {
68
  outline: 1px solid lightblue;
69
  }
47
  color: #00a0d2;
48
  }
49
 
 
 
 
 
 
 
 
 
 
 
 
50
  .happyforms-preview-badge {
51
  padding-left: 10px;
52
  font-weight: 400;
53
  font-size: 16px;
54
  }
55
 
56
+ .happyforms-part.unloading {
57
+ opacity: 0.25;
58
+ cursor: progress !important;
59
+ -webkit-transition: opacity 0.5s;
60
+ transition: opacity 0.5s;
61
+ }
62
+
63
  .happyforms-part.highlighted {
64
  outline: 1px solid lightblue;
65
  }
assets/js/customize.js CHANGED
@@ -92,6 +92,10 @@
92
  data: data
93
  } );
94
 
 
 
 
 
95
  request.done( success );
96
  }
97
  } );
92
  data: data
93
  } );
94
 
95
+ api.previewer.send( 'happyforms-form-part-disable', {
96
+ id: this.get( 'id' ),
97
+ } );
98
+
99
  request.done( success );
100
  }
101
  } );
assets/js/frontend/address.js CHANGED
@@ -7,7 +7,7 @@
7
  var apiKey = this.$el.attr( 'data-google-apikey' );
8
 
9
  $.get( settings.url, {
10
- action: settings.action,
11
  key: apiKey,
12
  term: request.term,
13
  }, function( data ) {
@@ -27,24 +27,103 @@
27
  HappyForms.parts.address = {
28
  init: function() {
29
  this.$input = $( 'input', this.$el );
 
 
 
 
30
 
 
31
  this.$input.on( 'keyup', this.triggerChange.bind( this ) );
32
  this.$input.on( 'change', this.triggerChange.bind( this ) );
33
 
34
- var $input = $( '.happyforms-part__el--address-autocomplete', this.$el );
35
- var mode = this.$el.attr( 'data-mode' );
 
36
 
37
- if ( 'simple' === mode ) {
38
- return;
 
 
39
  }
40
 
41
- var containerSelector = $( '.happyforms-part__el--address--results', this.$el );
 
42
 
43
- $input.autocomplete( {
44
- appendTo: containerSelector,
45
- source: sources.get( mode, this ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  } );
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
 
50
  } )( jQuery, _happyFormsAddressSettings );
7
  var apiKey = this.$el.attr( 'data-google-apikey' );
8
 
9
  $.get( settings.url, {
10
+ action: settings.actionAutocomplete,
11
  key: apiKey,
12
  term: request.term,
13
  }, function( data ) {
27
  HappyForms.parts.address = {
28
  init: function() {
29
  this.$input = $( 'input', this.$el );
30
+ this.$fullAddress = $( '.address-full', this.$el );
31
+ this.$country = $( '.address-country', this.$el );
32
+ this.$city = $( '.address-city', this.$el );
33
+ this.mode = this.$el.attr( 'data-mode' );
34
 
35
+ // Validation
36
  this.$input.on( 'keyup', this.triggerChange.bind( this ) );
37
  this.$input.on( 'change', this.triggerChange.bind( this ) );
38
 
39
+ if ( 'simple' !== this.mode ) {
40
+ var $input = $( '.happyforms-part--address__autocomplete', this.$el );
41
+ var containerSelector = $( '.happyforms-part--address__results', this.$el );
42
 
43
+ $input.autocomplete( {
44
+ appendTo: containerSelector,
45
+ source: sources.get( this.mode, this ),
46
+ } );
47
  }
48
 
49
+ // Geolocation
50
+ this.$geolocation = $( '.address-geolocate', this.$el );
51
 
52
+ if ( this.$geolocation.length ) {
53
+ if ( navigator.geolocation ) {
54
+ $( 'span', this.$geolocation ).text( this.$geolocation.data( 'idle' ) );
55
+ this.$geolocation.click( this.geolocate.bind( this ) );
56
+ } else {
57
+ this.$geolocation.hide();
58
+ }
59
+ }
60
+ },
61
+
62
+ geolocate: function( e ) {
63
+ e.preventDefault();
64
+
65
+ this.$geolocation.addClass( 'disabled' );
66
+ $( 'span', this.$geolocation ).text( this.$geolocation.data( 'fetching' ) );
67
+ navigator.geolocation.getCurrentPosition( this.geolocationCallback.bind( this ) );
68
+ },
69
+
70
+ geolocationCallback: function( position ) {
71
+ var apiKey = this.$el.attr( 'data-google-apikey' );
72
+ var $input = this.$input;
73
+ var $geolocation = this.$geolocation;
74
+
75
+ $.get( settings.url, {
76
+ action: settings.actionGeocode,
77
+ key: apiKey,
78
+ latitude: position.coords.latitude,
79
+ longitude: position.coords.longitude,
80
+ }, this.applyGeolocationResults.bind( this ) );
81
+ },
82
+
83
+ getFullAddress: function( results ) {
84
+ return results.formatted_address;
85
+ },
86
+
87
+ getCountry: function( results ) {
88
+ if ( ! results.address_components ) {
89
+ return '';
90
+ }
91
+
92
+ var country = results.address_components.filter( function( component ) {
93
+ return component.types.indexOf( 'country' ) >= 0;
94
  } );
95
+
96
+ country = country.length > 0 ? country[0].long_name : '';
97
+
98
+ return country;
99
+ },
100
+
101
+ getCity: function( results ) {
102
+ if ( ! results.address_components ) {
103
+ return '';
104
+ }
105
+
106
+ var city = results.address_components.filter( function( component ) {
107
+ return component.types.indexOf( 'locality' ) >= 0;
108
+ } );
109
+
110
+ city = city.length > 0 ? city[0].long_name : '';
111
+
112
+ return city;
113
+ },
114
+
115
+ applyGeolocationResults: function( results ) {
116
+ var fullAddress = this.getFullAddress( results );
117
+ var country = this.getCountry( results );
118
+ var city = this.getCity( results );
119
+
120
+ this.$fullAddress.val( fullAddress );
121
+ this.$country.val( country );
122
+ this.$city.val( city );
123
+
124
+ $( 'span', this.$geolocation ).text( this.$geolocation.data( 'idle' ) );
125
+ this.$geolocation.removeClass( 'disabled' );
126
+ },
127
  }
128
 
129
  } )( jQuery, _happyFormsAddressSettings );
assets/js/frontend/date.js ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ($) {
2
+
3
+ HappyForms.parts = HappyForms.parts || {};
4
+
5
+ HappyForms.parts.date = {
6
+ init: function () {
7
+ this.spinnerInterval = '';
8
+ this.spinnerIntervalLength = 50;
9
+ this.$input = $('input', this.$el);
10
+ this.$spinnerUpArrow = $('.happyforms-spinner-arrow--up', this.$el);
11
+ this.$spinnerDownArrow = $('.happyforms-spinner-arrow--down', this.$el);
12
+
13
+ this.$input.on('keydown', this.onKeyDown.bind(this));
14
+ this.$input.on('change', this.onChange.bind(this));
15
+
16
+ this.$spinnerUpArrow.on('mousedown touchstart', this.onSpinnerUpClick.bind(this));
17
+ this.$spinnerUpArrow.on('mouseup mouseleave touchend', this.clearSpinnerInterval.bind(this));
18
+ this.$spinnerDownArrow.on('mousedown touchstart', this.onSpinnerDownClick.bind(this));
19
+ this.$spinnerDownArrow.on('mouseup mouseleave touchend', this.clearSpinnerInterval.bind(this));
20
+ },
21
+
22
+ clearSpinnerInterval: function() {
23
+ clearInterval(this.spinnerInterval);
24
+ },
25
+
26
+ onSpinnerUpClick: function(e) {
27
+ var self = this;
28
+
29
+ this.clearSpinnerInterval();
30
+
31
+ this.spinnerInterval = setInterval(function() {
32
+ var $input = $(e.target).prev('input');
33
+ var step = ($input.attr('step')) ? parseInt($input.attr('step'), 10) : 1;
34
+
35
+ var value = self.getIncreasedValue(parseInt($input.val(), 10), step);
36
+
37
+ self.setValue($input, value);
38
+ }, this.spinnerIntervalLength);
39
+ },
40
+
41
+ onSpinnerDownClick: function (e) {
42
+ var self = this;
43
+
44
+ this.clearSpinnerInterval();
45
+
46
+ this.spinnerInterval = setInterval(function() {
47
+ var $input = $(e.target).prevAll('input');
48
+ var step = ($input.attr('step')) ? parseInt($input.attr('step'), 10) : 1;
49
+
50
+ var value = self.getDecreasedValue(parseInt($input.val(), 10), step);
51
+
52
+ self.setValue($input, value);
53
+ }, this.spinnerIntervalLength);
54
+ },
55
+
56
+ setValue: function($input, value) {
57
+ if ( 'undefined' === typeof $input ) {
58
+ return;
59
+ }
60
+
61
+ var pattern = $input.attr('pattern');
62
+ var intNewValue = parseInt(value, 10);
63
+ var intMin = parseInt($input.attr('min'), 10);
64
+ var intMax = parseInt($input.attr('max'), 10);
65
+ var step = ($input.attr('step')) ? parseInt( $input.attr('step'), 10 ) : 1;
66
+
67
+ if (intNewValue > intMax) {
68
+ intNewValue = intMin;
69
+ }
70
+
71
+ if (intNewValue < intMin) {
72
+ if (step) {
73
+ intNewValue = (intMax + 1 - step);
74
+ } else {
75
+ intNewValue = intMax;
76
+ }
77
+ }
78
+
79
+ if (intNewValue !== 0 && intNewValue % step !== 0) {
80
+ intNewValue = step;
81
+ }
82
+
83
+ var validatedValue = this.validateNumber(intNewValue, pattern);
84
+
85
+ if (validatedValue) {
86
+ $input.val(validatedValue);
87
+ }
88
+ },
89
+
90
+ validateNumber: function (number, pattern) {
91
+ if (isNaN(number) || !number) {
92
+ return '00';
93
+ }
94
+
95
+ if (number.toString().match(pattern)) {
96
+ return number.toString();
97
+ } else {
98
+ var leadingZeroValue = '0' + number;
99
+
100
+ if (2 === leadingZeroValue.length && leadingZeroValue.match(pattern)) {
101
+ return leadingZeroValue;
102
+ }
103
+ }
104
+ },
105
+
106
+ getIncreasedValue: function(value, step) {
107
+ if ( 'undefined' === typeof value || !step ) {
108
+ return;
109
+ }
110
+
111
+ var newValue = (value + step).toString();
112
+
113
+ return newValue;
114
+ },
115
+
116
+ getDecreasedValue: function(value, step) {
117
+ if ( 'undefined' === typeof value || !step ) {
118
+ return;
119
+ }
120
+
121
+ var newValue = (value - step).toString();
122
+
123
+ return newValue;
124
+ },
125
+
126
+ onKeyDown: function(e) {
127
+ var $input = $(e.target);
128
+ var intValue = (parseInt($input.val(), 10)) ? parseInt($input.val(), 10) : parseInt($input.attr('min'), 10);
129
+ var newValue = '0';
130
+ var max = $input.attr('max');
131
+ var min = $input.attr('min');
132
+ var step = parseInt($input.attr('step'), 10) ? parseInt($input.attr('step'), 10) : 1;
133
+
134
+ if ( isNaN(intValue) && isNaN(parseInt(e.key, 10)) ) {
135
+ return false;
136
+ }
137
+
138
+ if ( e.keyCode !== 38 && e.keyCode !== 40 ) {
139
+ return;
140
+ }
141
+
142
+ if ( 38 === e.keyCode ) {
143
+ newValue = this.getIncreasedValue(intValue, step);
144
+
145
+ if ('undefined' === typeof newValue) {
146
+ newValue = min;
147
+ }
148
+ }
149
+
150
+ if (40 === e.keyCode) {
151
+ newValue = this.getDecreasedValue(intValue, step);
152
+
153
+ if ('undefined' === typeof newValue) {
154
+ newValue = max;
155
+ }
156
+ }
157
+
158
+ this.setValue($input, newValue);
159
+ },
160
+
161
+ onChange: function (e) {
162
+ var $input = $(e.target);
163
+ var value = $input.val();
164
+
165
+ this.setValue($input, value);
166
+ },
167
+ };
168
+
169
+ })(jQuery);
assets/js/parts/part-address.js CHANGED
@@ -22,10 +22,12 @@
22
 
23
  this.listenTo( this, 'ready', this.toggleApiKey );
24
  this.listenTo( this.model, 'change:mode', this.onModeChange );
 
25
  },
26
 
27
  toggleApiKey: function() {
28
- if ( 'autocomplete' === this.model.get( 'mode' ) ) {
 
29
  $( '.address-apikey', this.$el ).show();
30
  } else {
31
  $( '.address-apikey', this.$el ).hide();
@@ -43,37 +45,33 @@
43
 
44
  onModeChange: function( e ) {
45
  var self = this;
46
- var part = this.model;
47
 
48
  this.toggleApiKey();
49
 
50
- var data = {
51
- action: 'happyforms-form-part-add',
52
- 'happyforms-nonce': api.settings.nonce.happyforms,
53
- happyforms: 1,
54
- wp_customize: 'on',
55
- form_id: happyForms.form.id,
56
- part: this.model.toJSON(),
57
- };
58
 
59
- var request = $.ajax( ajaxurl, {
60
- type: 'post',
61
- dataType: 'html',
62
- data: data
63
  } );
 
 
 
 
 
 
 
64
 
65
- request.done( function( response ) {
66
  var data = {
67
- id: part.get( 'id' ),
68
  html: response,
69
- callback: 'onAddressModeChangeCallback',
70
  };
71
 
72
- if ( happyForms.previewer.ready ) {
73
- api.previewer.send( 'happyforms-form-part-refresh', data );
74
- } else {
75
- happyForms.buffer.push( response );
76
- }
77
  } );
78
  }
79
  } );
22
 
23
  this.listenTo( this, 'ready', this.toggleApiKey );
24
  this.listenTo( this.model, 'change:mode', this.onModeChange );
25
+ this.listenTo( this.model, 'change:has_geolocation', this.onGeolocationChange );
26
  },
27
 
28
  toggleApiKey: function() {
29
+ if ( 'autocomplete' === this.model.get( 'mode' )
30
+ || this.model.get( 'has_geolocation' ) ) {
31
  $( '.address-apikey', this.$el ).show();
32
  } else {
33
  $( '.address-apikey', this.$el ).hide();
45
 
46
  onModeChange: function( e ) {
47
  var self = this;
48
+ var model = this.model;
49
 
50
  this.toggleApiKey();
51
 
52
+ this.model.fetchHtml( function( response ) {
53
+ var data = {
54
+ id: model.get( 'id' ),
55
+ html: response,
56
+ };
 
 
 
57
 
58
+ api.previewer.send( 'happyforms-form-part-refresh', data );
 
 
 
59
  } );
60
+ },
61
+
62
+ onGeolocationChange: function( e ) {
63
+ var self = this;
64
+ var model = this.model;
65
+
66
+ this.toggleApiKey();
67
 
68
+ this.model.fetchHtml( function( response ) {
69
  var data = {
70
+ id: model.get( 'id' ),
71
  html: response,
 
72
  };
73
 
74
+ api.previewer.send( 'happyforms-form-part-refresh', data );
 
 
 
 
75
  } );
76
  }
77
  } );
assets/js/parts/part-date.js CHANGED
@@ -18,49 +18,79 @@
18
 
19
  this.listenTo( this.model, 'change:date_type', this.onDateTypeChange );
20
  this.listenTo( this.model, 'change:time_format', this.onTimeFormatChange );
 
 
 
 
 
21
  },
22
 
23
  onDateTypeChange: function() {
24
  var $timeOptions = $( '.time-options', this.$el );
 
25
 
26
  switch ( this.model.get( 'date_type' ) ) {
27
  case 'date':
28
  $timeOptions.hide();
 
29
  break;
30
 
31
  case 'datetime':
32
  $timeOptions.show();
 
33
  break;
34
 
35
  case 'time':
36
  $timeOptions.show();
 
37
  break;
38
  }
39
 
40
- var model = this.model;
 
41
 
42
- model.fetchHtml( function( response ) {
43
- var data = {
44
- id: model.get( 'id' ),
45
- html: response,
46
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
- api.previewer.send( 'happyforms-form-part-refresh', data );
49
- } );
 
 
 
 
 
 
50
  },
51
 
52
- onTimeFormatChange: function() {
53
  var model = this.model;
54
 
55
- model.fetchHtml( function( response ) {
56
  var data = {
57
- id: model.get( 'id' ),
58
- html: response,
59
  };
60
 
61
- api.previewer.send( 'happyforms-form-part-refresh', data );
62
- } );
63
- }
64
  } );
65
 
66
  happyForms.previewer = _.extend( happyForms.previewer, {
18
 
19
  this.listenTo( this.model, 'change:date_type', this.onDateTypeChange );
20
  this.listenTo( this.model, 'change:time_format', this.onTimeFormatChange );
21
+ this.listenTo( this.model, 'change:min_year', this.refreshDatePart );
22
+ this.listenTo( this.model, 'change:max_year', this.refreshDatePart );
23
+ this.listenTo( this.model, 'change:min_hour', this.refreshDatePart );
24
+ this.listenTo( this.model, 'change:max_hour', this.refreshDatePart );
25
+ this.listenTo( this.model, 'change:default_datetime', this.refreshDatePart );
26
  },
27
 
28
  onDateTypeChange: function() {
29
  var $timeOptions = $( '.time-options', this.$el );
30
+ var $dateOptions = $( '.date-options', this.$el );
31
 
32
  switch ( this.model.get( 'date_type' ) ) {
33
  case 'date':
34
  $timeOptions.hide();
35
+ $dateOptions.show();
36
  break;
37
 
38
  case 'datetime':
39
  $timeOptions.show();
40
+ $dateOptions.show();
41
  break;
42
 
43
  case 'time':
44
  $timeOptions.show();
45
+ $dateOptions.hide();
46
  break;
47
  }
48
 
49
+ this.refreshDatePart();
50
+ },
51
 
52
+ onTimeFormatChange: function( model, value ) {
53
+ if ( 12 === parseInt( value, 10 ) ) {
54
+ model.set('min_hour', 1, {
55
+ silent: true
56
+ });
57
+
58
+ this.$el.find('input[data-bind=min_hour]').val(1);
59
+
60
+ model.set('max_hour', 12, {
61
+ silent: true
62
+ });
63
+
64
+ this.$el.find('input[data-bind=max_hour]').val(12);
65
+ } else {
66
+ model.set('min_hour', 0, {
67
+ silent: true
68
+ });
69
+
70
+ this.$el.find('input[data-bind=min_hour]').val(0);
71
 
72
+ model.set('max_hour', 23, {
73
+ silent: true
74
+ });
75
+
76
+ this.$el.find('input[data-bind=max_hour]').val(23);
77
+ }
78
+
79
+ this.refreshDatePart();
80
  },
81
 
82
+ refreshDatePart: function() {
83
  var model = this.model;
84
 
85
+ model.fetchHtml(function (response) {
86
  var data = {
87
+ id: model.get('id'),
88
+ html: response
89
  };
90
 
91
+ api.previewer.send('happyforms-form-part-refresh', data);
92
+ });
93
+ },
94
  } );
95
 
96
  happyForms.previewer = _.extend( happyForms.previewer, {
assets/js/preview.js CHANGED
@@ -25,7 +25,6 @@
25
  }
26
 
27
  $part.prepend( $( $pencil ) );
28
-
29
  HappyForms.wrapPart( $part, $form );
30
  }
31
 
@@ -36,10 +35,16 @@
36
 
37
  $part.remove();
38
  $next.before( $refreshedPart );
39
-
40
  HappyForms.wrapPart( $refreshedPart, $form );
41
  }
42
 
 
 
 
 
 
 
43
  handlers.formPartRemove = function( id ) {
44
  handlers.getPart( id ).remove();
45
  }
@@ -129,6 +134,7 @@
129
  api.preview.bind( 'happyforms-form-part-remove', handlers.formPartRemove );
130
  api.preview.bind( 'happyforms-form-parts-sort', handlers.formPartsSort );
131
  api.preview.bind( 'happyforms-form-part-refresh', handlers.formPartRefresh );
 
132
  api.preview.bind( 'happyforms-part-dom-update', handlers.partDomUpdate );
133
  api.preview.bind( 'happyforms-css-variable-update', handlers.cssVariableUpdate );
134
  api.preview.bind( 'happyforms-form-class-update', handlers.formClassUpdate );
25
  }
26
 
27
  $part.prepend( $( $pencil ) );
 
28
  HappyForms.wrapPart( $part, $form );
29
  }
30
 
35
 
36
  $part.remove();
37
  $next.before( $refreshedPart );
38
+ $refreshedPart.prepend( $( $pencil ) );
39
  HappyForms.wrapPart( $refreshedPart, $form );
40
  }
41
 
42
+ handlers.formPartDisable = function( e ) {
43
+ var $part = handlers.getPart( e.id );
44
+
45
+ $part.addClass( 'unloading' );
46
+ }
47
+
48
  handlers.formPartRemove = function( id ) {
49
  handlers.getPart( id ).remove();
50
  }
134
  api.preview.bind( 'happyforms-form-part-remove', handlers.formPartRemove );
135
  api.preview.bind( 'happyforms-form-parts-sort', handlers.formPartsSort );
136
  api.preview.bind( 'happyforms-form-part-refresh', handlers.formPartRefresh );
137
+ api.preview.bind( 'happyforms-form-part-disable', handlers.formPartDisable );
138
  api.preview.bind( 'happyforms-part-dom-update', handlers.partDomUpdate );
139
  api.preview.bind( 'happyforms-css-variable-update', handlers.cssVariableUpdate );
140
  api.preview.bind( 'happyforms-form-class-update', handlers.formClassUpdate );
assets/svg/font-awesome/map-marker-alt.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M172.268 501.67C26.97 291.031 0 269.413 0 192 0 85.961 85.961 0 192 0s192 85.961 192 192c0 77.413-26.97 99.031-172.268 309.67-9.535 13.774-29.93 13.773-39.464 0zM192 272c44.183 0 80-35.817 80-80s-35.817-80-80-80-80 35.817-80 80 35.817 80 80 80z"/></svg>
happyforms.php CHANGED
@@ -5,7 +5,7 @@
5
  * Plugin URI: https://happyforms.me
6
  * Description: Your friendly drag and drop contact form builder for creating contact forms, lead generation forms, feedback forms, quote forms, survey forms and more!
7
  * Author: The Theme Foundry
8
- * Version: 1.4.2
9
  * Author URI: https://thethemefoundry.com
10
  * Upgrade URI: https://thethemefoundry.com
11
  */
@@ -13,7 +13,7 @@
13
  /**
14
  * The current version of the plugin.
15
  */
16
- define( 'HAPPYFORMS_VERSION', '1.4.2' );
17
 
18
  if ( ! function_exists( 'happyforms_plugin_file' ) ):
19
  /**
5
  * Plugin URI: https://happyforms.me
6
  * Description: Your friendly drag and drop contact form builder for creating contact forms, lead generation forms, feedback forms, quote forms, survey forms and more!
7
  * Author: The Theme Foundry
8
+ * Version: 1.4.3
9
  * Author URI: https://thethemefoundry.com
10
  * Upgrade URI: https://thethemefoundry.com
11
  */
13
  /**
14
  * The current version of the plugin.
15
  */
16
+ define( 'HAPPYFORMS_VERSION', '1.4.3' );
17
 
18
  if ( ! function_exists( 'happyforms_plugin_file' ) ):
19
  /**
inc/classes/class-email-message.php ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class HappyForms_Email_Message {
4
+
5
+ /**
6
+ * The sender address.
7
+ *
8
+ * @since 1.4.3
9
+ *
10
+ * @var string
11
+ */
12
+ private $from;
13
+
14
+ /**
15
+ * The reply-to address.
16
+ *
17
+ * @since 1.4.3
18
+ *
19
+ * @var string
20
+ */
21
+ private $reply_to;
22
+
23
+ /**
24
+ * The list of recipients.
25
+ *
26
+ * @since 1.4.3
27
+ *
28
+ * @var array
29
+ */
30
+ private $recipients;
31
+
32
+ /**
33
+ * The list of cc addresses.
34
+ *
35
+ * @since 1.4.3
36
+ *
37
+ * @var array
38
+ */
39
+ private $ccs;
40
+
41
+ /**
42
+ * The email subject.
43
+ *
44
+ * @since 1.4.3
45
+ *
46
+ * @var string
47
+ */
48
+ private $subject;
49
+
50
+ /**
51
+ * The email content type.
52
+ *
53
+ * @since 1.4.3
54
+ *
55
+ * @var string
56
+ */
57
+ private $content_type = 'text/html';
58
+
59
+ /**
60
+ * The email message content.
61
+ *
62
+ * @since 1.4.3
63
+ *
64
+ * @var string
65
+ */
66
+ private $content;
67
+
68
+ /**
69
+ * The submission message this email is linked to.
70
+ *
71
+ * @since 1.4.3
72
+ *
73
+ * @var array
74
+ */
75
+ public $message;
76
+
77
+ public function __construct( $message = array() ) {
78
+ $this->from = '';
79
+ $this->to = '';
80
+ $this->ccs = array();
81
+ $this->subject = '';
82
+ $this->content = '';
83
+ $this->message = $message;
84
+ }
85
+
86
+ public function setSenders( $senders = array() ) {
87
+ if ( is_string( $senders ) ) {
88
+ $senders = array( $senders );
89
+ }
90
+
91
+ /**
92
+ * Filter the list of senders for this email message.
93
+ *
94
+ * @since 1.4.3
95
+ *
96
+ * @param array $senders Current list of senders.
97
+ * @param array $message The submission this email was triggered from.
98
+ *
99
+ * @return array
100
+ */
101
+ $senders = apply_filters( 'happyforms_email_senders', $senders, $this->message );
102
+
103
+ if ( count( $senders ) ) {
104
+ $senders = array_map( 'trim', $senders );
105
+
106
+ $this->from = $senders[0];
107
+ $this->reply_to = $senders[0];
108
+ $this->ccs = array_slice( $senders, 1 );
109
+ }
110
+ }
111
+
112
+ public function setRecipients( $recipients = array() ) {
113
+ if ( is_string( $recipients ) ) {
114
+ $recipients = array( $recipients );
115
+ }
116
+
117
+ /**
118
+ * Filter the list of recipients for this email message.
119
+ *
120
+ * @since 1.4.3
121
+ *
122
+ * @param array $recipients Current list of recipients.
123
+ * @param array $message The submission this email was triggered from.
124
+ *
125
+ * @return array
126
+ */
127
+ $recipients = apply_filters( 'happyforms_email_recipients', $recipients, $this->message );
128
+ $this->recipients = array_map( 'trim', $recipients );
129
+ }
130
+
131
+ public function setSubject( $subject = '' ) {
132
+ $subject = trim( $subject );
133
+
134
+ /**
135
+ * Filter the subject for this email message.
136
+ *
137
+ * @since 1.4.3
138
+ *
139
+ * @param string $subject Current subject.
140
+ *
141
+ * @return string
142
+ */
143
+ $subject = apply_filters( 'happyforms_email_subject', $subject, $this->message );
144
+ $this->subject = $subject;
145
+ }
146
+
147
+ public function setContent( $content = '' ) {
148
+ $content = trim( $content );
149
+
150
+ /**
151
+ * Filter the content for this email message.
152
+ *
153
+ * @since 1.4.3
154
+ *
155
+ * @param string $content Current content.
156
+ * @param array $message The submission this email was triggered from.
157
+ *
158
+ * @return string
159
+ */
160
+ $content = apply_filters( 'happyforms_email_content', $content, $this->message );
161
+ $this->content = $content;
162
+ }
163
+
164
+ private function getHeaders() {
165
+ $headers = array();
166
+
167
+ array_push( $headers, 'From: ' . $this->from );
168
+ array_push( $headers, 'Reply-To: ' . $this->reply_to );
169
+
170
+ foreach ( $this->ccs as $cc ) {
171
+ array_push( $headers, 'Cc: ' . $cc );
172
+ }
173
+
174
+ return $headers;
175
+ }
176
+
177
+ public function get_content_type() {
178
+ /**
179
+ * Filter the content type for this email message.
180
+ *
181
+ * @since 1.4.3
182
+ *
183
+ * @param string $content_type Current content type.
184
+ * @param array $message The submission this email was triggered from.
185
+ *
186
+ * @return string
187
+ */
188
+ $content_type = apply_filters( 'happyforms_email_content_type', $this->content_type, $this->message );
189
+
190
+ return $content_type;
191
+ }
192
+
193
+ public function send() {
194
+ add_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );
195
+
196
+ $headers = $this->getHeaders();
197
+
198
+ foreach ( $this->recipients as $recipient ) {
199
+ $result = wp_mail( $recipient, $this->subject, $this->content, $headers );
200
+ }
201
+
202
+ remove_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );
203
+
204
+ return $result;
205
+ }
206
+
207
+ }
inc/classes/class-form-controller.php CHANGED
@@ -182,6 +182,10 @@ class HappyForms_Form_Controller {
182
  'default' => '',
183
  'sanitize' => 'happyforms_sanitize_emails',
184
  ),
 
 
 
 
185
  'confirmation_message' => array(
186
  'default' => __( 'Your message has been successfully sent. We appreciate you contacting us and we’ll be in touch soon.', 'happyforms' ),
187
  'sanitize' => 'sanitize_text_field',
182
  'default' => '',
183
  'sanitize' => 'happyforms_sanitize_emails',
184
  ),
185
+ 'alert_email_subject' => array(
186
+ 'default' => __( 'You received a new message', 'happyforms' ),
187
+ 'sanitize' => 'sanitize_text_field',
188
+ ),
189
  'confirmation_message' => array(
190
  'default' => __( 'Your message has been successfully sent. We appreciate you contacting us and we’ll be in touch soon.', 'happyforms' ),
191
  'sanitize' => 'sanitize_text_field',
inc/classes/class-happyforms-plugin.php CHANGED
@@ -37,7 +37,7 @@ class HappyForms_Plugin {
37
  *
38
  * @var array
39
  */
40
- private $current_forms = array();
41
 
42
  /**
43
  * Action: initialize admin and frontend logic.
@@ -59,6 +59,7 @@ class HappyForms_Plugin {
59
 
60
  require_once( happyforms_get_include_folder() . '/classes/class-form-controller.php' );
61
  require_once( happyforms_get_include_folder() . '/classes/class-message-controller.php' );
 
62
  require_once( happyforms_get_include_folder() . '/classes/class-form-part-library.php' );
63
  require_once( happyforms_get_include_folder() . '/classes/class-form-styles.php' );
64
  require_once( happyforms_get_include_folder() . '/classes/class-message-notices.php' );
@@ -340,6 +341,7 @@ class HappyForms_Plugin {
340
  public function wp() {
341
  global $post;
342
 
 
343
  if ( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, $this->shortcode ) ) {
344
  $pattern = get_shortcode_regex();
345
 
@@ -369,6 +371,7 @@ class HappyForms_Plugin {
369
  }
370
  }
371
 
 
372
  if ( happyforms_is_preview() ) {
373
  $form_controller = happyforms_get_form_controller();
374
  $form = $form_controller->get( get_the_ID() );
@@ -459,6 +462,10 @@ class HappyForms_Plugin {
459
  * @return void
460
  */
461
  public function wp_enqueue_scripts() {
 
 
 
 
462
  $dependencies = array( 'jquery' );
463
  $dependencies = apply_filters(
464
  'happyforms_frontend_dependencies',
37
  *
38
  * @var array
39
  */
40
+ public $current_forms = array();
41
 
42
  /**
43
  * Action: initialize admin and frontend logic.
59
 
60
  require_once( happyforms_get_include_folder() . '/classes/class-form-controller.php' );
61
  require_once( happyforms_get_include_folder() . '/classes/class-message-controller.php' );
62
+ require_once( happyforms_get_include_folder() . '/classes/class-email-message.php' );
63
  require_once( happyforms_get_include_folder() . '/classes/class-form-part-library.php' );
64
  require_once( happyforms_get_include_folder() . '/classes/class-form-styles.php' );
65
  require_once( happyforms_get_include_folder() . '/classes/class-message-notices.php' );
341
  public function wp() {
342
  global $post;
343
 
344
+ // Shortcode
345
  if ( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, $this->shortcode ) ) {
346
  $pattern = get_shortcode_regex();
347
 
371
  }
372
  }
373
 
374
+ // Preview
375
  if ( happyforms_is_preview() ) {
376
  $form_controller = happyforms_get_form_controller();
377
  $form = $form_controller->get( get_the_ID() );
462
  * @return void
463
  */
464
  public function wp_enqueue_scripts() {
465
+ // if ( empty( $this->current_forms ) ) {
466
+ // return;
467
+ // }
468
+
469
  $dependencies = array( 'jquery' );
470
  $dependencies = apply_filters(
471
  'happyforms_frontend_dependencies',
inc/classes/class-message-controller.php CHANGED
@@ -68,7 +68,22 @@ class HappyForms_Message_Controller {
68
  */
69
  public $unread_transient = 'happyforms_unread_messages';
70
 
 
 
 
 
 
 
 
71
  public $captcha_verify_url = 'https://www.google.com/recaptcha/api/siteverify';
 
 
 
 
 
 
 
 
72
  public $captcha_field = 'g-recaptcha-response';
73
 
74
  /**
@@ -124,10 +139,6 @@ class HappyForms_Message_Controller {
124
  return $fields;
125
  }
126
 
127
- private function get_public_fields() {
128
- return array( 'ID', 'post_date', 'parts', 'form_id', 'read' );
129
- }
130
-
131
  /**
132
  * Action: register the message custom post type.
133
  *
@@ -241,18 +252,6 @@ class HappyForms_Message_Controller {
241
 
242
  // Add a general error notice at the top
243
  $message_notices->add_message( 'form_error', $form_id );
244
-
245
- /**
246
- * This action fires upon an invalid submission.
247
- *
248
- * @since 1.4
249
- *
250
- * @param WP_Error $result Error data.
251
- * @param array $form Current form data.
252
- *
253
- * @return void
254
- */
255
- do_action( 'happyforms_submission_error', $result, $form );
256
  } else {
257
  // Add a general success notice at the top
258
  $message_notices->add_message( 'form_success', $form_id );
@@ -263,18 +262,6 @@ class HappyForms_Message_Controller {
263
  if ( ! empty( $form['redirect_url'] ) ) {
264
  $redirect_url = $form['redirect_url'];
265
  }
266
-
267
- /**
268
- * This action fires once a message is succesfully submitted.
269
- *
270
- * @since 1.4
271
- *
272
- * @param array $result Submission data.
273
- * @param array $form Current form data.
274
- *
275
- * @return void
276
- */
277
- do_action( 'happyforms_submission_success', $result, $form );
278
  }
279
 
280
  // Write the session cookie
@@ -416,19 +403,15 @@ class HappyForms_Message_Controller {
416
  $attrs = $defaults['post'] + array( 'meta_input' => $message_meta );
417
  $message_id = wp_insert_post( wp_slash( $attrs ), true );
418
 
419
- if ( is_wp_error( $message_id ) ) {
420
- return $message_id;
421
- }
422
-
423
- $message = $this->get( $message_id );
424
- $this->email_owner_confirmation( $form, $message );
425
- $this->email_user_confirmation( $form, $message );
426
 
427
- if ( ! $form['save_entries'] ) {
428
- wp_delete_post( $message_id, true);
 
429
  }
430
-
431
- return $message;
432
  }
433
 
434
  /**
@@ -436,12 +419,11 @@ class HappyForms_Message_Controller {
436
  *
437
  * @since 1.0
438
  *
439
- * @param string $post_ids The IDs of the messages to retrieve.
440
- * @param boolean $pretty Wether to return data in pretty format or not.
441
  *
442
  * @return array
443
  */
444
- public function get( $post_ids = '', $pretty = false ) {
445
  $query_params = array(
446
  'post_type' => $this->post_type,
447
  'post_status' => 'any',
@@ -506,7 +488,6 @@ class HappyForms_Message_Controller {
506
  $meta_entries = get_post_meta( $message->ID, '', true );
507
  $form_id = $meta_entries['form_id'][0];
508
  $form = happyforms_get_form_controller()->get( $form_id );
509
-
510
  $message_array['parts'] = array();
511
  $defaults = $this->get_defaults();
512
 
@@ -521,17 +502,10 @@ class HappyForms_Message_Controller {
521
  $part = happyforms_get_part_library()->get_part( $part_data['type'] );
522
  $part_id = $part_data['id'];
523
  $part_value = isset( $meta_entries[$part_id] ) ? $meta_entries[$part_id][0] : $part->get_default_value();
524
- $part_info = array(
525
- 'label' => happyforms_get_part_label( $part_data ),
526
- 'value' => happyforms_get_message_part_value( $part_value ),
527
- );
528
-
529
- $message_array['parts'][$part_id] = $part_info;
530
  }
531
  }
532
 
533
- $message_array = array_intersect_key( $message_array, array_flip ( $this->get_public_fields() ) );
534
-
535
  return $message_array;
536
  }
537
 
@@ -549,35 +523,35 @@ class HappyForms_Message_Controller {
549
  if ( isset( $form['email_recipient'] )
550
  && ! empty( $form['email_recipient'] ) ) {
551
 
552
- $email_recipients = $form['email_recipient'];
553
- $email_recipients = explode( ',', $email_recipients );
554
- $email_recipients = array_map( 'trim', $email_recipients );
555
- $email_subject = __( 'New submission', 'happyforms' );
556
- $email_content_lines = array();
 
 
 
557
 
558
- foreach ( $message['parts'] as $part_id => $part_data ) {
559
- $label = $part_data['label'];
560
- $value = $part_data['value'];
561
- $email_content_lines[] = "$label: $value";
 
562
  }
563
 
564
- $email_content = implode( '<br>', $email_content_lines );
565
- $email_content = sprintf(
566
- '<p>%s</p>',
567
- $email_content
568
- );
569
 
570
- $from = '';
571
  $email_part = happyforms_get_form_controller()->get_first_part_by_type( $form, 'email' );
572
 
573
  if ( false !== $email_part ) {
574
  $part_id = $email_part['id'];
575
- $from = $message['parts'][$part_id]['value'];
 
576
  }
577
 
578
- foreach ( $email_recipients as $recipient ) {
579
- $result = $this->send_mail( $recipient, $email_subject, $email_content, $from );
580
- }
581
  }
582
  }
583
 
@@ -598,55 +572,18 @@ class HappyForms_Message_Controller {
598
  && ! empty( $form['confirmation_email_subject'] )
599
  && ! empty( $form['confirmation_email_content'] ) ) {
600
 
 
 
 
 
 
601
  $part_id = $email_part['id'];
602
- $email_recipient = $message['parts'][$part_id]['value'];
603
- $email_subject = $form['confirmation_email_subject'];
604
- $email_content = sprintf(
605
- '%s',
606
- $form['confirmation_email_content']
607
- );
608
-
609
- $result = $this->send_mail( $email_recipient, $email_subject, $email_content );
610
- }
611
- }
612
-
613
- /**
614
- * Send an email to the specified recipient.
615
- *
616
- * @since 1.0
617
- *
618
- * @param string $recipient The recipient email address.
619
- * @param array $subject The email subject.
620
- * @param string $message The email content.
621
- *
622
- * @return boolean
623
- */
624
- private function send_mail( $recipient, $subject, $content, $from = '' ) {
625
- add_filter( 'wp_mail_content_type', array( $this, 'get_mail_content_type' ) );
626
-
627
- $headers = '';
628
 
629
- if ( ! empty( $from ) ) {
630
- $headers .= "From: {$from} \r\n";
631
  }
632
-
633
- $result = wp_mail( $recipient, $subject, $content, $headers );
634
-
635
- remove_filter( 'wp_mail_content_type', array( $this, 'get_mail_content_type' ) );
636
-
637
- return $result;
638
- }
639
-
640
- /**
641
- * Return the default content type used when
642
- * sending email messages.
643
- *
644
- * @since 1.0
645
- *
646
- * @return string
647
- */
648
- public function get_mail_content_type() {
649
- return 'text/html';
650
  }
651
 
652
  /**
68
  */
69
  public $unread_transient = 'happyforms_unread_messages';
70
 
71
+ /**
72
+ * The url used to verify a Google ReCaptcha request.
73
+ *
74
+ * @since 1.1
75
+ *
76
+ * @var string
77
+ */
78
  public $captcha_verify_url = 'https://www.google.com/recaptcha/api/siteverify';
79
+
80
+ /**
81
+ * The name of the field containing Google ReCaptcha data.
82
+ *
83
+ * @since 1.1
84
+ *
85
+ * @var string
86
+ */
87
  public $captcha_field = 'g-recaptcha-response';
88
 
89
  /**
139
  return $fields;
140
  }
141
 
 
 
 
 
142
  /**
143
  * Action: register the message custom post type.
144
  *
252
 
253
  // Add a general error notice at the top
254
  $message_notices->add_message( 'form_error', $form_id );
 
 
 
 
 
 
 
 
 
 
 
 
255
  } else {
256
  // Add a general success notice at the top
257
  $message_notices->add_message( 'form_success', $form_id );
262
  if ( ! empty( $form['redirect_url'] ) ) {
263
  $redirect_url = $form['redirect_url'];
264
  }
 
 
 
 
 
 
 
 
 
 
 
 
265
  }
266
 
267
  // Write the session cookie
403
  $attrs = $defaults['post'] + array( 'meta_input' => $message_meta );
404
  $message_id = wp_insert_post( wp_slash( $attrs ), true );
405
 
406
+ if ( ! is_wp_error( $message_id ) ) {
407
+ $message = $this->get( $message_id );
408
+ $this->email_owner_confirmation( $form, $message );
409
+ $this->email_user_confirmation( $form, $message );
 
 
 
410
 
411
+ if ( ! $form['save_entries'] ) {
412
+ wp_delete_post( $message_id, true);
413
+ }
414
  }
 
 
415
  }
416
 
417
  /**
419
  *
420
  * @since 1.0
421
  *
422
+ * @param string $post_ids The IDs of the messages to retrieve.
 
423
  *
424
  * @return array
425
  */
426
+ public function get( $post_ids = '' ) {
427
  $query_params = array(
428
  'post_type' => $this->post_type,
429
  'post_status' => 'any',
488
  $meta_entries = get_post_meta( $message->ID, '', true );
489
  $form_id = $meta_entries['form_id'][0];
490
  $form = happyforms_get_form_controller()->get( $form_id );
 
491
  $message_array['parts'] = array();
492
  $defaults = $this->get_defaults();
493
 
502
  $part = happyforms_get_part_library()->get_part( $part_data['type'] );
503
  $part_id = $part_data['id'];
504
  $part_value = isset( $meta_entries[$part_id] ) ? $meta_entries[$part_id][0] : $part->get_default_value();
505
+ $message_array['parts'][$part_id] = $part_value;
 
 
 
 
 
506
  }
507
  }
508
 
 
 
509
  return $message_array;
510
  }
511
 
523
  if ( isset( $form['email_recipient'] )
524
  && ! empty( $form['email_recipient'] ) ) {
525
 
526
+ // Compose an email message
527
+ $email_message = new HappyForms_Email_Message( $message );
528
+ $recipients = explode( ',', $form['email_recipient'] );
529
+ $email_message->setRecipients( $recipients );
530
+ $email_message->setSubject( $form['alert_email_subject'] );
531
+
532
+ // Compose content with submit data
533
+ $content_lines = array();
534
 
535
+ foreach ( $form['parts'] as $part_data ) {
536
+ $part_id = $part_data['id'];
537
+ $label = happyforms_get_part_label( $part_data );
538
+ $value = happyforms_get_message_part_value( $message['parts'][$part_id] );
539
+ $content_lines[] = "$label: $value";
540
  }
541
 
542
+ $content = implode( '<br>', $content_lines );
543
+ $email_message->setContent( $content );
 
 
 
544
 
545
+ // Add a From header it the form includes an email part
546
  $email_part = happyforms_get_form_controller()->get_first_part_by_type( $form, 'email' );
547
 
548
  if ( false !== $email_part ) {
549
  $part_id = $email_part['id'];
550
+ $sender = happyforms_get_message_part_value( $message['parts'][$part_id] );
551
+ $email_message->setSenders( $sender );
552
  }
553
 
554
+ $email_message->send();
 
 
555
  }
556
  }
557
 
572
  && ! empty( $form['confirmation_email_subject'] )
573
  && ! empty( $form['confirmation_email_content'] ) ) {
574
 
575
+ // Compose an email message
576
+ $email_message = new HappyForms_Email_Message( $message );
577
+ $senders = explode( ',', $form['email_recipient'] );
578
+ $email_message->setSenders( $senders );
579
+ $email_message->setSubject( $form['confirmation_email_subject'] );
580
  $part_id = $email_part['id'];
581
+ $recipient = happyforms_get_message_part_value( $message['parts'][$part_id] );
582
+ $email_message->setRecipients( $recipient );
583
+ $email_message->setContent( $form['confirmation_email_content'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
584
 
585
+ $email_message->send();
 
586
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
587
  }
588
 
589
  /**
inc/classes/parts/class-part-address.php CHANGED
@@ -2,8 +2,10 @@
2
 
3
  class HappyForms_Part_Address extends HappyForms_Form_Part {
4
 
5
- private $ajax_action = 'happyforms_address_autocomplete';
6
- private $api_url = 'https://maps.googleapis.com/maps/api/place/autocomplete/json';
 
 
7
 
8
  public $type = 'address';
9
 
@@ -14,8 +16,10 @@ class HappyForms_Part_Address extends HappyForms_Form_Part {
14
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
15
  add_filter( 'happyforms_part_data_attributes', array( $this, 'html_part_data_attributes' ), 10, 3 );
16
  add_filter( 'happyforms_frontend_dependencies', array( $this, 'script_dependencies' ), 10, 2 );
17
- add_action( 'wp_ajax_' . $this->ajax_action, array( $this, 'ajax_autocomplete' ) );
18
- add_action( 'wp_ajax_nopriv_' . $this->ajax_action, array( $this, 'ajax_autocomplete' ) );
 
 
19
  }
20
 
21
  /**
@@ -74,6 +78,10 @@ class HappyForms_Part_Address extends HappyForms_Form_Part {
74
  array( 'simple', 'autocomplete', 'country', 'country-city' ),
75
  ),
76
  ),
 
 
 
 
77
  );
78
  }
79
 
@@ -205,7 +213,7 @@ class HappyForms_Part_Address extends HappyForms_Form_Part {
205
  $results = array();
206
 
207
  if ( isset( $_GET['term'] ) && isset( $_GET['key'] ) ) {
208
- $url = $this->api_url;
209
  $key = sanitize_text_field( $_GET['key'] );
210
  $term = sanitize_text_field( $_GET['term'] );
211
  $args = array(
@@ -226,11 +234,45 @@ class HappyForms_Part_Address extends HappyForms_Form_Part {
226
  wp_send_json( $results );
227
  }
228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  public function html_part_class( $class, $part_data, $form_data ) {
230
  if ( isset( $part_data['mode'] ) && 'country-city' === $part_data['mode'] ) {
231
  $class[] = 'happyforms-part--address-country-city';
232
  }
233
 
 
 
 
 
234
  return $class;
235
  }
236
 
@@ -301,7 +343,8 @@ class HappyForms_Part_Address extends HappyForms_Form_Part {
301
 
302
  $settings = array(
303
  'url' => admin_url( 'admin-ajax.php' ),
304
- 'action' => $this->ajax_action,
 
305
  'countries' => happyforms_get_countries()
306
  );
307
 
2
 
3
  class HappyForms_Part_Address extends HappyForms_Form_Part {
4
 
5
+ private $ajax_action_autocomplete = 'happyforms_address_autocomplete';
6
+ private $ajax_action_geocode = 'happyforms_address_geocode';
7
+ private $api_url_autocomplete = 'https://maps.googleapis.com/maps/api/place/autocomplete/json';
8
+ private $api_url_geocode = 'https://maps.googleapis.com/maps/api/geocode/json';
9
 
10
  public $type = 'address';
11
 
16
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
17
  add_filter( 'happyforms_part_data_attributes', array( $this, 'html_part_data_attributes' ), 10, 3 );
18
  add_filter( 'happyforms_frontend_dependencies', array( $this, 'script_dependencies' ), 10, 2 );
19
+ add_action( 'wp_ajax_' . $this->ajax_action_autocomplete, array( $this, 'ajax_autocomplete' ) );
20
+ add_action( 'wp_ajax_nopriv_' . $this->ajax_action_autocomplete, array( $this, 'ajax_autocomplete' ) );
21
+ add_action( 'wp_ajax_' . $this->ajax_action_geocode, array( $this, 'ajax_geocode' ) );
22
+ add_action( 'wp_ajax_nopriv_' . $this->ajax_action_geocode, array( $this, 'ajax_geocode' ) );
23
  }
24
 
25
  /**
78
  array( 'simple', 'autocomplete', 'country', 'country-city' ),
79
  ),
80
  ),
81
+ 'has_geolocation' => array(
82
+ 'default' => 0,
83
+ 'sanitize' => 'happyforms_sanitize_checkbox',
84
+ ),
85
  );
86
  }
87
 
213
  $results = array();
214
 
215
  if ( isset( $_GET['term'] ) && isset( $_GET['key'] ) ) {
216
+ $url = $this->api_url_autocomplete;
217
  $key = sanitize_text_field( $_GET['key'] );
218
  $term = sanitize_text_field( $_GET['term'] );
219
  $args = array(
234
  wp_send_json( $results );
235
  }
236
 
237
+ public function ajax_geocode() {
238
+ $results = array();
239
+
240
+ if ( isset( $_GET['key'] ) && isset( $_GET['latitude'] ) && isset( $_GET['longitude'] ) ) {
241
+ $url = $this->api_url_geocode;
242
+ $key = sanitize_text_field( $_GET['key'] );
243
+ $latitude = sanitize_text_field( $_GET['latitude'] );
244
+ $longitude = sanitize_text_field( $_GET['longitude'] );
245
+ $latlng = "{$latitude},{$longitude}";
246
+
247
+ $args = array(
248
+ 'key' => $key,
249
+ 'result_type' => 'street_address',
250
+ 'latlng' => $latlng,
251
+ );
252
+ $query = http_build_query( $args );
253
+ $response = wp_remote_get( "{$url}?{$query}" );
254
+ $response = wp_remote_retrieve_body( $response );
255
+ $response = json_decode( $response, true );
256
+
257
+ if ( isset( $response['status'] ) && 'OK' === $response['status'] ) {
258
+ if ( count( $response['results'] ) ) {
259
+ $results = $response['results'][0];
260
+ }
261
+ }
262
+ }
263
+
264
+ wp_send_json( $results );
265
+ }
266
+
267
  public function html_part_class( $class, $part_data, $form_data ) {
268
  if ( isset( $part_data['mode'] ) && 'country-city' === $part_data['mode'] ) {
269
  $class[] = 'happyforms-part--address-country-city';
270
  }
271
 
272
+ if ( isset( $part_data['has_geolocation'] ) && $part_data['has_geolocation'] ) {
273
+ $class[] = 'happyforms-part--address-has-geolocation';
274
+ }
275
+
276
  return $class;
277
  }
278
 
343
 
344
  $settings = array(
345
  'url' => admin_url( 'admin-ajax.php' ),
346
+ 'actionAutocomplete' => $this->ajax_action_autocomplete,
347
+ 'actionGeocode' => $this->ajax_action_geocode,
348
  'countries' => happyforms_get_countries()
349
  );
350
 
inc/classes/parts/class-part-date.php CHANGED
@@ -5,10 +5,11 @@ class HappyForms_Part_Date extends HappyForms_Form_Part {
5
  public $type = 'date';
6
 
7
  public function __construct() {
8
- $this->label = __( 'Date', 'happyforms' );
9
  $this->description = __( 'For formatted day, month, year and or time fields.', 'happyforms' );
10
 
11
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
 
12
  }
13
 
14
  /**
@@ -48,6 +49,30 @@ class HappyForms_Part_Date extends HappyForms_Form_Part {
48
  'default' => 12,
49
  'sanitize' => 'intval'
50
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  'width' => array(
52
  'default' => 'full',
53
  'sanitize' => 'sanitize_key'
@@ -107,6 +132,14 @@ class HappyForms_Part_Date extends HappyForms_Form_Part {
107
  'not_valid_date' => array(
108
  'type' => 'error',
109
  'message' => __( 'Not a valid date.', 'happyforms' )
 
 
 
 
 
 
 
 
110
  )
111
  );
112
  }
@@ -200,7 +233,30 @@ class HappyForms_Part_Date extends HappyForms_Form_Part {
200
  }
201
 
202
  if ( !date_create_from_format( $date_format, $validated_value ) ) {
203
- $validated_value = new WP_Error( $part_id, 'not_valid_date' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  }
205
 
206
  return $validated_value;
@@ -211,6 +267,10 @@ class HappyForms_Part_Date extends HappyForms_Form_Part {
211
  $class[] = 'happyforms-part-date--' . $part_data['date_type'];
212
  }
213
 
 
 
 
 
214
  return $class;
215
  }
216
 
@@ -233,4 +293,30 @@ class HappyForms_Part_Date extends HappyForms_Form_Part {
233
  );
234
  }
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  }
5
  public $type = 'date';
6
 
7
  public function __construct() {
8
+ $this->label = __( 'Date & Time', 'happyforms' );
9
  $this->description = __( 'For formatted day, month, year and or time fields.', 'happyforms' );
10
 
11
  add_filter( 'happyforms_part_class', array( $this, 'html_part_class' ), 10, 3 );
12
+ add_filter( 'happyforms_frontend_dependencies', array( $this, 'script_dependencies' ), 10, 2 );
13
  }
14
 
15
  /**
49
  'default' => 12,
50
  'sanitize' => 'intval'
51
  ),
52
+ 'default_datetime' => array(
53
+ 'default' => '',
54
+ 'sanitize' => 'sanitize_text_field'
55
+ ),
56
+ 'min_year' => array(
57
+ 'default' => 1910,
58
+ 'sanitize' => 'intval'
59
+ ),
60
+ 'max_year' => array(
61
+ 'default' => date( 'Y' ),
62
+ 'sanitize' => 'intval'
63
+ ),
64
+ 'min_hour' => array(
65
+ 'default' => 1,
66
+ 'sanitize' => 'sanitize_text_field'
67
+ ),
68
+ 'max_hour' => array(
69
+ 'default' => 12,
70
+ 'sanitize' => 'intval'
71
+ ),
72
+ 'minute_step' => array(
73
+ 'default' => 1,
74
+ 'sanitize' => 'intval'
75
+ ),
76
  'width' => array(
77
  'default' => 'full',
78
  'sanitize' => 'sanitize_key'
132
  'not_valid_date' => array(
133
  'type' => 'error',
134
  'message' => __( 'Not a valid date.', 'happyforms' )
135
+ ),
136
+ 'invalid_hour' => array(
137
+ 'type' => 'error',
138
+ 'message' => __( 'Hour input does not match minimum and maximum value allowed.', 'happyforms' )
139
+ ),
140
+ 'invalid_year' => array(
141
+ 'type' => 'error',
142
+ 'message' => __( 'Year input does not match maximum value allowed.', 'happyforms' )
143
  )
144
  );
145
  }
233
  }
234
 
235
  if ( !date_create_from_format( $date_format, $validated_value ) ) {
236
+ $validated_value = new WP_Error( 'not_valid_date' );
237
+ } else {
238
+ $date = strtotime( $validated_value );
239
+
240
+ if ( 'date' !== $part_data['date_type'] && isset( $part_data['min_hour'] ) && isset( $part_data['max_hour'] ) ) {
241
+ $hour = (12 === $part_data['time_format']) ? intval( date( 'g', $date ) ) : intval( date( 'G', $date ) );
242
+
243
+ $min_hour = intval( $part_data['min_hour'] );
244
+ $max_hour = intval( $part_data['max_hour'] );
245
+
246
+ if ( $hour > $max_hour || $hour < $min_hour ) {
247
+ $validated_value = new WP_Error( 'invalid_hour' );
248
+ }
249
+ }
250
+
251
+ if ( 'time' !== $part_data['date_type'] && isset( $part_data['min_year'] ) && isset( $part_data['max_year'] ) ) {
252
+ $year = intval( date( 'Y', $date ) );
253
+ $min_year = intval( $part_data['min_year'] );
254
+ $max_year = intval( $part_data['max_year'] );
255
+
256
+ if ( $year > $max_year || $year < $min_year ) {
257
+ $validated_value = new WP_Error( 'invalid_year' );
258
+ }
259
+ }
260
  }
261
 
262
  return $validated_value;
267
  $class[] = 'happyforms-part-date--' . $part_data['date_type'];
268
  }
269
 
270
+ if ( isset( $part_data['time_format'] ) ) {
271
+ $class[] = 'happyforms-part-date--' . $part_data['time_format'];
272
+ }
273
+
274
  return $class;
275
  }
276
 
293
  );
294
  }
295
 
296
+ public function script_dependencies( $deps, $forms ) {
297
+ $contains_date = false;
298
+ $form_controller = happyforms_get_form_controller();
299
+
300
+ foreach ( $forms as $form ) {
301
+ if ( $form_controller->get_first_part_by_type( $form, $this->type ) ) {
302
+ $contains_date = true;
303
+ break;
304
+ }
305
+ }
306
+
307
+ if ( ! happyforms_is_preview() && ! $contains_date ) {
308
+ return $deps;
309
+ }
310
+
311
+ wp_register_script(
312
+ 'happyforms-part-date',
313
+ happyforms_get_plugin_url() . 'assets/js/frontend/date.js',
314
+ array(), false, true
315
+ );
316
+
317
+ $deps[] = 'happyforms-part-date';
318
+
319
+ return $deps;
320
+ }
321
+
322
  }
inc/helpers/helper-form-templates.php CHANGED
@@ -647,4 +647,14 @@ function happyforms_recaptcha( $form ) {
647
  <?php endif;
648
  }
649
 
650
- endif;
 
 
 
 
 
 
 
 
 
 
647
  <?php endif;
648
  }
649
 
650
+ endif;
651
+
652
+ if ( ! function_exists( 'happyforms_geolocation_link' ) ) :
653
+
654
+ function happyforms_geolocation_link( $part ) {
655
+ if ( 1 === intval( $part['has_geolocation'] ) ) : ?>
656
+ <a class="address-geolocate" href="#" data-idle="<?php _e( 'Get my location', 'happyforms' ); ?>" data-fetching="<?php _e( 'Fetching location…', 'happyforms' ); ?>"><?php require( happyforms_get_plugin_path() . 'assets/svg/font-awesome/map-marker-alt.svg' ) ; ?><span></span></a>
657
+ <?php endif;
658
+ }
659
+
660
+ endif;
inc/helpers/helper-misc.php CHANGED
@@ -131,29 +131,6 @@ function happyforms_get_all_form_link() {
131
 
132
  endif;
133
 
134
- if ( ! function_exists( 'happyforms_get_byline' ) ):
135
- /**
136
- * Get the byline text for forms and transitional email messages.
137
- *
138
- * @since 1.0
139
- *
140
- * @return string
141
- */
142
- function happyforms_get_byline() {
143
- $url = 'https://wordpress.org/plugins/happyforms/';
144
- $title = __( 'Powered by HappyForms', 'happyforms' );
145
- $text = __( 'Powered by', 'happyforms' );
146
-
147
- $link = sprintf(
148
- '%s <a href="%s" title="%s" target="_blank">HappyForms</a>',
149
- $text, $url, $title
150
- );
151
-
152
- return $link;
153
- }
154
-
155
- endif;
156
-
157
  if ( ! function_exists( 'happyforms_admin_footer' ) ):
158
  /**
159
  * Output the Happyforms rating admin footer.
131
 
132
  endif;
133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  if ( ! function_exists( 'happyforms_admin_footer' ) ):
135
  /**
136
  * Output the Happyforms rating admin footer.
inc/templates/customize-form-setup.php CHANGED
@@ -4,6 +4,10 @@
4
  <label for="" class="customize-control-title"><?php _e( 'Your email address', 'happyforms' ); ?> <i class="fa fa-question-circle" aria-hidden="true" data-pointer="email_recipient"></i></label>
5
  <input type="text" value="<%= email_recipient %>" data-attribute="email_recipient" data-pointer-target />
6
  </div>
 
 
 
 
7
  <div class="customize-control">
8
  <label for="" class="customize-control-title"><?php _e( 'Confirmation message', 'happyforms' ); ?> <i class="fa fa-question-circle" aria-hidden="true" data-pointer="confirmation_message"></i></label>
9
  <div data-pointer-target>
@@ -61,6 +65,9 @@
61
  <script type="text/template" id="happyforms-pointer-email_recipient">
62
  <?php _e( 'Add your email address here to receive a confirmation email for each form response. You can add multiple email addresses by separating each address with a comma.', 'happyforms' ); ?>
63
  </script>
 
 
 
64
  <script type="text/template" id="happyforms-pointer-confirmation_message">
65
  <?php _e( 'This is the message recipients will see after succesfully submitting your form.', 'happyforms' ); ?>
66
  </script>
4
  <label for="" class="customize-control-title"><?php _e( 'Your email address', 'happyforms' ); ?> <i class="fa fa-question-circle" aria-hidden="true" data-pointer="email_recipient"></i></label>
5
  <input type="text" value="<%= email_recipient %>" data-attribute="email_recipient" data-pointer-target />
6
  </div>
7
+ <div class="customize-control">
8
+ <label for="" class="customize-control-title"><?php _e( 'Message alert email subject', 'happyforms' ); ?> <i class="fa fa-question-circle" aria-hidden="true" data-pointer="alert_email_subject"></i></label>
9
+ <input type="text" value="<%= alert_email_subject %>" data-attribute="alert_email_subject" data-pointer-target />
10
+ </div>
11
  <div class="customize-control">
12
  <label for="" class="customize-control-title"><?php _e( 'Confirmation message', 'happyforms' ); ?> <i class="fa fa-question-circle" aria-hidden="true" data-pointer="confirmation_message"></i></label>
13
  <div data-pointer-target>
65
  <script type="text/template" id="happyforms-pointer-email_recipient">
66
  <?php _e( 'Add your email address here to receive a confirmation email for each form response. You can add multiple email addresses by separating each address with a comma.', 'happyforms' ); ?>
67
  </script>
68
+ <script type="text/template" id="happyforms-pointer-alert_email_subject">
69
+ <?php _e( 'Each time a user submits a message, you\'ll receive an email with this subject.', 'happyforms' ); ?>
70
+ </script>
71
  <script type="text/template" id="happyforms-pointer-confirmation_message">
72
  <?php _e( 'This is the message recipients will see after succesfully submitting your form.', 'happyforms' ); ?>
73
  </script>
inc/templates/parts/customize-address.php CHANGED
@@ -49,13 +49,27 @@
49
  <option value="country"<%= (instance.mode == 'country') ? ' selected' : '' %>><?php _e( 'Country only', 'happyforms' ); ?></option>
50
  </select>
51
  </p>
 
 
 
 
 
52
  <div class="address-apikey">
53
  <p>
54
  <label for="<%= instance.id %>_apikey"><?php _e( 'Google API Key', 'happyforms' ); ?></label>
55
  <input type="text" id="<%= instance.id %>_apikey class="widefat title" value="<%= instance.apikey %>" data-bind="apikey" />
56
  </p>
57
  <p class="description">
58
- Autocomplete mode uses the Google Places API to populate results. <a href="https://developers.google.com/places/web-service/get-api-key" target="_blank"><?php _e( 'Get a key for Google Places API Web Service', 'happyforms' ); ?></a>
 
 
 
 
 
 
 
 
 
59
  </p>
60
  </div>
61
  <p>
49
  <option value="country"<%= (instance.mode == 'country') ? ' selected' : '' %>><?php _e( 'Country only', 'happyforms' ); ?></option>
50
  </select>
51
  </p>
52
+ <p>
53
+ <label>
54
+ <input type="checkbox" name="has_geolocation" class="checkbox" value="1" <% if ( instance.has_geolocation ) { %>checked="checked"<% } %> data-bind="has_geolocation" /> <?php _e( 'Allow geolocation', 'happyforms' ); ?>
55
+ </label>
56
+ </p>
57
  <div class="address-apikey">
58
  <p>
59
  <label for="<%= instance.id %>_apikey"><?php _e( 'Google API Key', 'happyforms' ); ?></label>
60
  <input type="text" id="<%= instance.id %>_apikey class="widefat title" value="<%= instance.apikey %>" data-bind="apikey" />
61
  </p>
62
  <p class="description">
63
+ <?php printf(
64
+ '%s <a href="https://developers.google.com/places/web-service/get-api-key" target="_blank">%s</a>.',
65
+ __( 'Address autocompletion requires a', 'happyforms' ),
66
+ __( 'Google Places API key', 'happyforms' )
67
+ ); ?>
68
+ <?php printf(
69
+ '%s <a href="https://developers.google.com/maps/documentation/geocoding/start" target="_blank">%s</a>.',
70
+ __( 'Geolocation requires a', 'happyforms' ),
71
+ __( 'Google Geocoding API key', 'happyforms' )
72
+ ); ?>
73
  </p>
74
  </div>
75
  <p>
inc/templates/parts/customize-date.php CHANGED
@@ -34,6 +34,37 @@
34
  <option value="time"<%= (instance.date_type == 'time') ? ' selected' : '' %>><?php _e( 'Time', 'happyforms' ); ?></option>
35
  </select>
36
  </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  <p class="time-options"<%= (instance.date_type == 'date') ? ' style="display: none"' : '' %>>
38
  <label for="<%= instance.id %>_time_format"><?php _e( 'Time format', 'happyforms' ); ?></label>
39
  <select id="<%= instance.id %>_time_format" name="time_format" data-bind="time_format" class="widefat">
34
  <option value="time"<%= (instance.date_type == 'time') ? ' selected' : '' %>><?php _e( 'Time', 'happyforms' ); ?></option>
35
  </select>
36
  </p>
37
+ <p>
38
+ <label for="<%= instance.id %>_default_datetime"><?php _e( 'Default value', 'happyforms' ); ?></label>
39
+ <select id="<%= instance.id %>_default_datetime" name="default_datetime" data-bind="default_datetime" class="widefat">
40
+ <option value=""<%= (instance.default_datetime == 'blank') ? ' selected' : '' %>><?php _e( 'Blank', 'happyforms' ); ?></option>
41
+ <option value="current"<%= (instance.default_datetime == 'current') ? ' selected' : '' %>><?php _e( 'Current date and time', 'happyforms' ); ?></option>
42
+ </select>
43
+ </p>
44
+ <div class="date-options happyforms-customize-controls-wrap--side-by-side"<%= (instance.date_type == 'time') ? ' style="display: none"' : '' %>>
45
+ <p>
46
+ <label for="<%= instance.id %>min_year"><?php _e( 'Min year', 'happyforms' ); ?></label>
47
+ <input type="number" id="<%= instance.id %>min_year" data-bind="min_year" value="<%= instance.min_year %>">
48
+ </p>
49
+ <p>
50
+ <label for="<%= instance.id %>_max_year"><?php _e( 'Max year', 'happyforms' ); ?></label>
51
+ <input type="number" id="<%= instance.id %>_max_year" data-bind="max_year" min="<%= instance.min_year %>" max="<?php echo date( 'Y' ) + 2; ?>" value="<%= instance.max_year %>">
52
+ </p>
53
+ </div>
54
+ <div class="time-options happyforms-customize-controls-wrap--side-by-side"<%= (instance.date_type == 'date') ? ' style="display: none"' : '' %>>
55
+ <p>
56
+ <label for="<%= instance.id %>_min_hour"><?php _e( 'Min hour', 'happyforms' ); ?></label>
57
+ <input type="number" id="<%= instance.id %>_min_hour" data-bind="min_hour" value="<%= instance.min_hour %>">
58
+ </p>
59
+ <p>
60
+ <label for="<%= instance.id %>_max_hour"><?php _e( 'Max hour', 'happyforms' ); ?></label>
61
+ <input type="number" id="<%= instance.id %>_max_hour" data-bind="max_hour" min="<%= instance.max_hour %>" max="<?php echo date( 'Y' ) + 2; ?>" value="<%= instance.max_hour %>">
62
+ </p>
63
+ </div>
64
+ <p class="time-options">
65
+ <label for="<%= instance.id %>_minute_step"><?php _e( 'Minute step', 'happyforms' ); ?></label>
66
+ <input type="number" id="<%= instance.id %>_minute_step" min="0" max="30" step="15" data-bind="minute_step" value="<%= instance.minute_step %>">
67
+ </p>
68
  <p class="time-options"<%= (instance.date_type == 'date') ? ' style="display: none"' : '' %>>
69
  <label for="<%= instance.id %>_time_format"><?php _e( 'Time format', 'happyforms' ); ?></label>
70
  <select id="<%= instance.id %>_time_format" name="time_format" data-bind="time_format" class="widefat">
inc/templates/parts/frontend-address.php CHANGED
@@ -9,31 +9,43 @@
9
  <?php if ( 'simple' === $part['mode'] ) : ?>
10
  <div class="happyforms-part-el-wrap">
11
  <div class="happyforms-part__el">
12
- <input name="<?php happyforms_the_part_name( $part, $form ); ?>[full]" class="happyforms-part__el--text-input happyforms-part__el--address" type="text" value="<?php happyforms_the_part_value( $part, $form ); ?>" placeholder="<?php echo esc_attr( $part['placeholder'] ); ?>" <?php if ( 1 === $part['required'] ) : ?>required aria-required="true"<?php endif; ?> />
 
 
 
13
  </div>
14
  </div>
15
  <?php elseif ( 'autocomplete' === $part['mode'] ) : ?>
16
  <div class="happyforms-part-el-wrap">
17
  <div class="happyforms-part__el">
18
- <input name="<?php happyforms_the_part_name( $part, $form ); ?>[full]" class="happyforms-part__el--text-input happyforms-part__el--address-autocomplete" type="text" value="<?php happyforms_the_part_value( $part, $form ); ?>" placeholder="<?php echo esc_attr( $part['placeholder'] ); ?>" <?php if ( 1 === $part['required'] ) : ?>required aria-required="true"<?php endif; ?> autocomplete="off" />
19
- <div class="happyforms-part__el--address--results happyforms-part__el--address--results-google"></div>
 
 
 
20
  </div>
21
  </div>
22
  <?php elseif ( 'country' === $part['mode'] ) : ?>
23
  <div class="happyforms-part-el-wrap">
24
  <div class="happyforms-part__el">
25
- <input name="<?php happyforms_the_part_name( $part, $form ); ?>[country]" class="happyforms-part__el--text-input happyforms-part__el--address-autocomplete" type="text" value="<?php happyforms_the_part_value( $part, $form, 'country' ); ?>" placeholder="<?php _e( 'Country', 'happyforms' ); ?>" <?php if ( 1 === $part['required'] ) : ?>required aria-required="true"<?php endif; ?> autocomplete="off" />
26
- <div class="happyforms-part__el--address--results happyforms-part__el--address--results-country"></div>
 
 
 
27
  </div>
28
  </div>
29
  <?php else: ?>
30
  <div class="happyforms-part-el-wrap">
31
  <div class="happyforms-part__el">
32
- <input name="<?php happyforms_the_part_name( $part, $form ); ?>[country]" class="happyforms-part__el--text-input happyforms-part__el--address-autocomplete" type="text" value="<?php happyforms_the_part_value( $part, $form, 'country' ); ?>" placeholder="<?php _e( 'Country', 'happyforms' ); ?>" <?php if ( 1 === $part['required'] ) : ?>required aria-required="true"<?php endif; ?> autocomplete="off" />
33
- <div class="happyforms-part__el--address--results happyforms-part__el--address--results-country"></div>
 
 
 
34
  </div>
35
  <div class="happyforms-part__el">
36
- <input name="<?php happyforms_the_part_name( $part, $form ); ?>[city]"class="happyforms-part__el happyforms-part__el--text-input happyforms-part__el--address-city" type="text" value="<?php happyforms_the_part_value( $part, $form, 'city' ); ?>" placeholder="<?php _e( 'City', 'happyforms' ); ?>" <?php if ( 1 === $part['required'] ) : ?>required aria-required="true"<?php endif; ?> />
37
  </div>
38
  </div>
39
  <?php endif; ?>
@@ -41,7 +53,7 @@
41
  <?php happyforms_the_part_label( $part, $form ); ?>
42
  <?php endif; ?>
43
  <?php happyforms_print_part_description( $part ); ?>
44
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
45
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
46
  <?php endif; ?>
47
  </div>
9
  <?php if ( 'simple' === $part['mode'] ) : ?>
10
  <div class="happyforms-part-el-wrap">
11
  <div class="happyforms-part__el">
12
+ <div class="happyforms-part__dummy-input">
13
+ <input id="<?php happyforms_the_part_id( $part, $form ); ?>" name="<?php happyforms_the_part_name( $part, $form ); ?>[full]" class="address-full" type="text" value="<?php happyforms_the_part_value( $part, $form ); ?>" placeholder="<?php echo esc_attr( $part['placeholder'] ); ?>" <?php if ( 1 === $part['required'] ) : ?>required aria-required="true"<?php endif; ?> />
14
+ <?php happyforms_geolocation_link( $part ); ?>
15
+ </div>
16
  </div>
17
  </div>
18
  <?php elseif ( 'autocomplete' === $part['mode'] ) : ?>
19
  <div class="happyforms-part-el-wrap">
20
  <div class="happyforms-part__el">
21
+ <div class="happyforms-part__dummy-input">
22
+ <input id="<?php happyforms_the_part_id( $part, $form ); ?>" name="<?php happyforms_the_part_name( $part, $form ); ?>[full]" class="happyforms-part--address__autocomplete address-full" type="text" value="<?php happyforms_the_part_value( $part, $form ); ?>" placeholder="<?php echo esc_attr( $part['placeholder'] ); ?>" <?php if ( 1 === $part['required'] ) : ?>required aria-required="true"<?php endif; ?> autocomplete="off" />
23
+ <?php happyforms_geolocation_link( $part ); ?>
24
+ </div>
25
+ <div class="happyforms-part--address__results happyforms-part--address__results--google"></div>
26
  </div>
27
  </div>
28
  <?php elseif ( 'country' === $part['mode'] ) : ?>
29
  <div class="happyforms-part-el-wrap">
30
  <div class="happyforms-part__el">
31
+ <div class="happyforms-part__dummy-input">
32
+ <input id="<?php happyforms_the_part_id( $part, $form ); ?>" name="<?php happyforms_the_part_name( $part, $form ); ?>[country]" class="happyforms-part--address__autocomplete address-country" type="text" value="<?php happyforms_the_part_value( $part, $form, 'country' ); ?>" placeholder="<?php _e( 'Country', 'happyforms' ); ?>" <?php if ( 1 === $part['required'] ) : ?>required aria-required="true"<?php endif; ?> autocomplete="off" />
33
+ <?php happyforms_geolocation_link( $part ); ?>
34
+ </div>
35
+ <div class="happyforms-part--address__results"></div>
36
  </div>
37
  </div>
38
  <?php else: ?>
39
  <div class="happyforms-part-el-wrap">
40
  <div class="happyforms-part__el">
41
+ <div class="happyforms-part__dummy-input">
42
+ <input id ="<?php happyforms_the_part_id( $part, $form ); ?>" name="<?php happyforms_the_part_name( $part, $form ); ?>[country]" class="happyforms-part--address__autocomplete address-country" type="text" value="<?php happyforms_the_part_value( $part, $form, 'country' ); ?>" placeholder="<?php _e( 'Country', 'happyforms' ); ?>" <?php if ( 1 === $part['required'] ) : ?>required aria-required="true"<?php endif; ?> autocomplete="off" />
43
+ <?php happyforms_geolocation_link( $part ); ?>
44
+ </div>
45
+ <div class="happyforms-part--address__results"></div>
46
  </div>
47
  <div class="happyforms-part__el">
48
+ <input name="<?php happyforms_the_part_name( $part, $form ); ?>[city]" class="address-city" type="text" value="<?php happyforms_the_part_value( $part, $form, 'city' ); ?>" placeholder="<?php _e( 'City', 'happyforms' ); ?>" <?php if ( 1 === $part['required'] ) : ?>required aria-required="true"<?php endif; ?> />
49
  </div>
50
  </div>
51
  <?php endif; ?>
53
  <?php happyforms_the_part_label( $part, $form ); ?>
54
  <?php endif; ?>
55
  <?php happyforms_print_part_description( $part ); ?>
56
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
57
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
58
  <?php endif; ?>
59
  </div>
inc/templates/parts/frontend-checkbox.php CHANGED
@@ -18,7 +18,7 @@
18
  <?php endforeach; ?>
19
  </div>
20
  <?php happyforms_print_part_description( $part ); ?>
21
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
22
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
23
  <?php endif; ?>
24
  </div>
18
  <?php endforeach; ?>
19
  </div>
20
  <?php happyforms_print_part_description( $part ); ?>
21
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
22
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
23
  <?php endif; ?>
24
  </div>
inc/templates/parts/frontend-date.php CHANGED
@@ -1,5 +1,6 @@
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 ( 'inside' !== $part['label_placement'] ) : ?>
4
  <?php happyforms_the_part_label( $part, $form ); ?>
5
  <?php endif; ?>
@@ -8,67 +9,72 @@
8
  <?php endif; ?>
9
  <div class="happyforms-part__el">
10
  <?php if ( $part['date_type'] === 'datetime' || $part['date_type'] === 'date' ) : ?>
11
- <div class="happyforms-part__select-wrap happyforms-part-date__date-input">
 
12
  <select name="<?php happyforms_the_part_name( $part, $form ); ?>[month]"<?php if ( 1 === $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
13
  <option value=""><?php _e( 'Month', 'happyforms' ); ?></option>
14
  <?php for ( $i = 1; $i <= 12; $i++ ) : ?>
15
- <option value="<?php echo $i; ?>" <?php selected( happyforms_get_part_value( $part, $form, 'month' ), $i ); ?>><?php echo date( 'F', mktime( 0, 0, 0, $i) ); ?></option>
16
  <?php endfor; ?>
17
  </select>
18
  </div>
19
- <div class="happyforms-part__select-wrap happyforms-part-date__date-input">
 
20
  <select name="<?php happyforms_the_part_name( $part, $form ); ?>[day]"<?php if ( 1 === $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
21
  <option value=""><?php _e( 'Day', 'happyforms' ); ?></option>
22
  <?php for ( $i = 1; $i <= 31; $i++ ) : ?>
23
- <option value="<?php echo $i; ?>" <?php selected( happyforms_get_part_value( $part, $form, 'day' ), $i ); ?>><?php echo $i; ?></option>
24
  <?php endfor; ?>
25
  </select>
26
  </div>
27
- <div class="happyforms-part__select-wrap happyforms-part-date__date-input">
 
28
  <select name="<?php happyforms_the_part_name( $part, $form ); ?>[year]"<?php if ( 1 === $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
29
  <option value=""><?php _e( 'Year', 'happyforms' ); ?></option>
30
  <?php
31
- $year_cutoff = 1910;
32
- $year_current = date('Y');
33
 
34
- for ( $i = $year_current; $i >= $year_cutoff; $i-- ) : ?>
35
- <option value="<?php echo $i; ?>" <?php selected( happyforms_get_part_value( $part, $form, 'year' ), $i ); ?>><?php echo $i; ?></option>
36
  <?php endfor; ?>
37
  </select>
38
  </div>
39
  <?php endif; ?>
40
  <?php if ( $part['date_type'] === 'datetime' || $part['date_type'] === 'time' ) : ?>
41
- <div class="happyforms-part__select-wrap happyforms-part-date__time-input">
42
- <select name="<?php happyforms_the_part_name( $part, $form ); ?>[hour]"<?php if ( 1 === $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
43
- <option value=""><?php _e( 'Hour', 'happyforms' ); ?></option>
44
- <?php
45
- if ( 24 === intval( $part['time_format'] ) ) {
46
- $i_min = 0;
47
- $i_max = 24;
48
- } else {
49
- $i_min = 1;
50
- $i_max = 13;
51
- }
52
 
53
- for ( $i = $i_min; $i < $i_max; $i++ ) : ?>
54
- <option value="<?php echo $i; ?>" <?php selected( happyforms_get_part_value( $part, $form, 'hour' ), $i ); ?>><?php echo $i; ?></option>
55
- <?php endfor; ?>
56
- </select>
 
 
57
  </div>
58
- <div class="happyforms-part__select-wrap happyforms-part-date__time-input">
59
- <select name="<?php happyforms_the_part_name( $part, $form ); ?>[minute]"<?php if ( 1 === $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
60
- <option value=""><?php _e( 'Minute', 'happyforms' ); ?></option>
61
- <?php for ( $i = 0; $i < 60; $i++ ) : ?>
62
- <option value="<?php echo $i; ?>" <?php selected( happyforms_get_part_value( $part, $form, 'minute' ), $i ); ?>><?php echo $i; ?></option>
63
- <?php endfor; ?>
64
- </select>
 
 
 
65
  </div>
66
  <?php if ( 12 == intval( $part['time_format'] ) ) : ?>
67
- <div class="happyforms-part__select-wrap happyforms-part-date__time-input happyforms-part-date__time-input--period">
 
68
  <select name="<?php happyforms_the_part_name( $part, $form ); ?>[period]"<?php if ( 1 === $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
69
- <option value=""><?php _e( 'Period', 'happyforms' ); ?></option>
70
- <option value="AM" <?php selected( happyforms_get_part_value( $part, $form, 'period' ), 'AM' ); ?>><?php _e( 'AM', 'happyforms' ); ?></option>
71
- <option value="PM" <?php selected( happyforms_get_part_value( $part, $form, 'period' ), 'PM' ); ?>><?php _e( 'PM', 'happyforms' ); ?></option>
72
  </select>
73
  </div>
74
  <?php endif; ?>
@@ -78,7 +84,7 @@
78
  <?php happyforms_the_part_label( $part, $form ); ?>
79
  <?php endif; ?>
80
  <?php happyforms_print_part_description( $part ); ?>
81
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
82
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
83
  <?php endif; ?>
84
  </div>
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 $current_timestamp = current_time( 'timestamp', false ); ?>
4
  <?php if ( 'inside' !== $part['label_placement'] ) : ?>
5
  <?php happyforms_the_part_label( $part, $form ); ?>
6
  <?php endif; ?>
9
  <?php endif; ?>
10
  <div class="happyforms-part__el">
11
  <?php if ( $part['date_type'] === 'datetime' || $part['date_type'] === 'date' ) : ?>
12
+ <?php $month_value = ( 'current' === $part['default_datetime'] ) ? date( 'n' ) : happyforms_get_part_value( $part, $form, 'month' ); ?>
13
+ <div class="happyforms-part-date__date-input happyforms-part__select-wrap">
14
  <select name="<?php happyforms_the_part_name( $part, $form ); ?>[month]"<?php if ( 1 === $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
15
  <option value=""><?php _e( 'Month', 'happyforms' ); ?></option>
16
  <?php for ( $i = 1; $i <= 12; $i++ ) : ?>
17
+ <option value="<?php echo $i; ?>" <?php selected( $month_value, $i ); ?>><?php echo date( 'F', mktime( 0, 0, 0, $i) ); ?></option>
18
  <?php endfor; ?>
19
  </select>
20
  </div>
21
+ <?php $day_value = ( 'current' === $part['default_datetime'] ) ? date( 'j' ) : happyforms_get_part_value( $part, $form, 'day' ); ?>
22
+ <div class="happyforms-part-date__date-input happyforms-part__select-wrap">
23
  <select name="<?php happyforms_the_part_name( $part, $form ); ?>[day]"<?php if ( 1 === $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
24
  <option value=""><?php _e( 'Day', 'happyforms' ); ?></option>
25
  <?php for ( $i = 1; $i <= 31; $i++ ) : ?>
26
+ <option value="<?php echo $i; ?>" <?php selected( $day_value, $i ); ?>><?php echo $i; ?></option>
27
  <?php endfor; ?>
28
  </select>
29
  </div>
30
+ <?php $year_value = ( 'current' === $part['default_datetime'] ) ? date( 'Y', $current_timestamp ) : happyforms_get_part_value( $part, $form, 'year' ); ?>
31
+ <div class="happyforms-part-date__date-input happyforms-part__select-wrap">
32
  <select name="<?php happyforms_the_part_name( $part, $form ); ?>[year]"<?php if ( 1 === $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
33
  <option value=""><?php _e( 'Year', 'happyforms' ); ?></option>
34
  <?php
35
+ $min_year = $part['min_year'];
36
+ $max_year = ( $part['max_year'] > $min_year ) ? $part['max_year'] : date('Y');
37
 
38
+ for ( $i = $max_year; $i >= $min_year; $i-- ) : ?>
39
+ <option value="<?php echo $i; ?>" <?php selected( $year_value, $i ); ?>><?php echo $i; ?></option>
40
  <?php endfor; ?>
41
  </select>
42
  </div>
43
  <?php endif; ?>
44
  <?php if ( $part['date_type'] === 'datetime' || $part['date_type'] === 'time' ) : ?>
45
+ <?php
46
+ if ( 12 === $part['time_format'] ) {
47
+ $hour_pattern = '(0[0-9]|1[0-2])';
48
+ $hour_date_string = 'h';
49
+ } else {
50
+ $hour_pattern = '(0[0-9]|1[0-9]|2[0-3])';
51
+ $hour_date_string = 'H';
52
+ }
 
 
 
53
 
54
+ $hour_value = ( 'current' === $part['default_datetime'] ) ? date( $hour_date_string, $current_timestamp ) : '';
55
+ ?>
56
+ <div class="happyforms-part--date__input-wrap happyforms-part-date__time-input happyforms-part-date__time-input--hours">
57
+ <input type="text" name="<?php happyforms_the_part_name( $part, $form ); ?>[hour]" min="<?php echo $part['min_hour']; ?>" max="<?php echo $part['max_hour']; ?>" maxlength="2" pattern="<?php echo $hour_pattern; ?>" autocomplete="off" value="<?php echo $hour_value; ?>"<?php if ( 1 == $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
58
+ <span class="happyforms-spinner-arrow happyforms-spinner-arrow--up"></span>
59
+ <span class="happyforms-spinner-arrow happyforms-spinner-arrow--down"></span>
60
  </div>
61
+ <div class="happyforms-part--date__time-separator">
62
+ <span>:</span>
63
+ </div>
64
+ <?php
65
+ $minute_value = ( 'current' === $part['default_datetime'] ) ? date( 'i', $current_timestamp ) : '';
66
+ ?>
67
+ <div class="happyforms-part--date__input-wrap happyforms-part-date__time-input happyforms-part-date__time-input--minutes">
68
+ <input type="text" name="<?php happyforms_the_part_name( $part, $form ); ?>[minute]" min="0" max="59" step="<?php echo $part['minute_step']; ?>" maxlength="2" pattern="([0-5][0-9])" autocomplete="off" value="<?php echo $minute_value; ?>"<?php if ( 1 == $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
69
+ <span class="happyforms-spinner-arrow happyforms-spinner-arrow--up"></span>
70
+ <span class="happyforms-spinner-arrow happyforms-spinner-arrow--down"></span>
71
  </div>
72
  <?php if ( 12 == intval( $part['time_format'] ) ) : ?>
73
+ <?php $period_value = ( 'current' === $part['default_datetime'] ) ? date( 'A', $current_timestamp ) : happyforms_get_part_value( $part, $form, 'period' ); ?>
74
+ <div class="happyforms-part--date__input-wrap happyforms-part-date__time-input happyforms-part-date__time-input--period happyforms-part__select-wrap">
75
  <select name="<?php happyforms_the_part_name( $part, $form ); ?>[period]"<?php if ( 1 === $part['required'] ) : ?> required aria-required="true"<?php endif; ?>>
76
+ <option value="AM" <?php selected( $period_value, 'AM' ); ?>><?php _e( 'AM', 'happyforms' ); ?></option>
77
+ <option value="PM" <?php selected( $period_value, 'PM' ); ?>><?php _e( 'PM', 'happyforms' ); ?></option>
 
78
  </select>
79
  </div>
80
  <?php endif; ?>
84
  <?php happyforms_the_part_label( $part, $form ); ?>
85
  <?php endif; ?>
86
  <?php happyforms_print_part_description( $part ); ?>
87
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
88
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
89
  <?php endif; ?>
90
  </div>
inc/templates/parts/frontend-email.php CHANGED
@@ -11,7 +11,7 @@
11
  <?php happyforms_the_part_label( $part, $form ); ?>
12
  <?php endif; ?>
13
  <?php happyforms_print_part_description( $part ); ?>
14
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
15
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
16
  <?php endif; ?>
17
  </div>
11
  <?php happyforms_the_part_label( $part, $form ); ?>
12
  <?php endif; ?>
13
  <?php happyforms_print_part_description( $part ); ?>
14
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
15
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
16
  <?php endif; ?>
17
  </div>
inc/templates/parts/frontend-multi-line-text.php CHANGED
@@ -11,7 +11,7 @@
11
  <?php happyforms_the_part_label( $part, $form ); ?>
12
  <?php endif; ?>
13
  <?php happyforms_print_part_description( $part ); ?>
14
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
15
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
16
  <?php endif; ?>
17
  </div>
11
  <?php happyforms_the_part_label( $part, $form ); ?>
12
  <?php endif; ?>
13
  <?php happyforms_print_part_description( $part ); ?>
14
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
15
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
16
  <?php endif; ?>
17
  </div>
inc/templates/parts/frontend-number.php CHANGED
@@ -15,7 +15,7 @@
15
  <?php happyforms_the_part_label( $part, $form ); ?>
16
  <?php endif; ?>
17
  <?php happyforms_print_part_description( $part ); ?>
18
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
19
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
20
  <?php endif; ?>
21
  </div>
15
  <?php happyforms_the_part_label( $part, $form ); ?>
16
  <?php endif; ?>
17
  <?php happyforms_print_part_description( $part ); ?>
18
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
19
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
20
  <?php endif; ?>
21
  </div>
inc/templates/parts/frontend-phone.php CHANGED
@@ -11,7 +11,7 @@
11
  <?php happyforms_the_part_label( $part, $form ); ?>
12
  <?php endif; ?>
13
  <?php happyforms_print_part_description( $part ); ?>
14
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
15
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
16
  <?php endif; ?>
17
  </div>
11
  <?php happyforms_the_part_label( $part, $form ); ?>
12
  <?php endif; ?>
13
  <?php happyforms_print_part_description( $part ); ?>
14
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
15
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
16
  <?php endif; ?>
17
  </div>
inc/templates/parts/frontend-radio.php CHANGED
@@ -22,7 +22,7 @@
22
  <?php endforeach; ?>
23
  </div>
24
  <?php happyforms_print_part_description( $part ); ?>
25
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
26
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
27
  <?php endif; ?>
28
  </div>
22
  <?php endforeach; ?>
23
  </div>
24
  <?php happyforms_print_part_description( $part ); ?>
25
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
26
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
27
  <?php endif; ?>
28
  </div>
inc/templates/parts/frontend-scale.php CHANGED
@@ -13,7 +13,7 @@
13
  min="<?php echo esc_attr( $part['min_value'] ); ?>" max="<?php echo esc_attr( $part['max_value'] ); ?>" value="<?php happyforms_the_part_value( $part, $form ); ?>" />
14
  <?php happyforms_print_part_description( $part ); ?>
15
  <output for="<?php happyforms_the_part_id( $part, $form ); ?>"><?php echo $part['default_value']; ?></output>
16
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
17
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
18
  <?php endif; ?>
19
  </div>
13
  min="<?php echo esc_attr( $part['min_value'] ); ?>" max="<?php echo esc_attr( $part['max_value'] ); ?>" value="<?php happyforms_the_part_value( $part, $form ); ?>" />
14
  <?php happyforms_print_part_description( $part ); ?>
15
  <output for="<?php happyforms_the_part_id( $part, $form ); ?>"><?php echo $part['default_value']; ?></output>
16
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
17
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
18
  <?php endif; ?>
19
  </div>
inc/templates/parts/frontend-select.php CHANGED
@@ -16,7 +16,7 @@
16
  </select>
17
  </div>
18
  <?php happyforms_print_part_description( $part ); ?>
19
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
20
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
21
  <?php endif; ?>
22
  </div>
16
  </select>
17
  </div>
18
  <?php happyforms_print_part_description( $part ); ?>
19
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
20
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
21
  <?php endif; ?>
22
  </div>
inc/templates/parts/frontend-single-line-text.php CHANGED
@@ -11,7 +11,7 @@
11
  <?php happyforms_the_part_label( $part, $form ); ?>
12
  <?php endif; ?>
13
  <?php happyforms_print_part_description( $part ); ?>
14
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
15
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
16
  <?php endif; ?>
17
  </div>
11
  <?php happyforms_the_part_label( $part, $form ); ?>
12
  <?php endif; ?>
13
  <?php happyforms_print_part_description( $part ); ?>
14
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
15
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
16
  <?php endif; ?>
17
  </div>
inc/templates/parts/frontend-website-url.php CHANGED
@@ -11,7 +11,7 @@
11
  <?php happyforms_the_part_label( $part, $form ); ?>
12
  <?php endif; ?>
13
  <?php happyforms_print_part_description( $part ); ?>
14
- <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || empty( happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
15
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
16
  <?php endif; ?>
17
  </div>
11
  <?php happyforms_the_part_label( $part, $form ); ?>
12
  <?php endif; ?>
13
  <?php happyforms_print_part_description( $part ); ?>
14
+ <?php if ( 'happyforms-form--part-error-notices-below' === happyforms_get_form_property( $form, 'part_error_notices_location' ) || ( '' === happyforms_get_form_property( $form, 'part_error_notices_location' ) ) ) : ?>
15
  <?php happyforms_message_notices( $form, happyforms_get_part_name( $part, $form ) ); ?>
16
  <?php endif; ?>
17
  </div>
readme.txt CHANGED
@@ -3,9 +3,9 @@
3
  Contributors: thethemefoundry
4
  Tags: contact, contact form, email, feedback form, form, form builder, custom form, lead generation, survey form, quote form
5
  Requires at least: 4.8
6
- Tested up to: 4.9.5
7
  Requires PHP: 5.2.4
8
- Stable tag: 1.4.2
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -74,6 +74,14 @@ Absolutely! HappyForms gets out of the way and is designed to work with any them
74
 
75
  == Changelog ==
76
 
 
 
 
 
 
 
 
 
77
  = 1.4.2 =
78
  * Bug fix: Part widget toggle buttons didn't work on Firefox.
79
  * Bug fix: Part column widths were misbehaving on some themes.
@@ -123,6 +131,9 @@ Absolutely! HappyForms gets out of the way and is designed to work with any them
123
 
124
  == Upgrade Notice ==
125
 
 
 
 
126
  = 1.4.2 =
127
  * Minor bug fixes.
128
 
3
  Contributors: thethemefoundry
4
  Tags: contact, contact form, email, feedback form, form, form builder, custom form, lead generation, survey form, quote form
5
  Requires at least: 4.8
6
+ Tested up to: 4.9.6
7
  Requires PHP: 5.2.4
8
+ Stable tag: 1.4.3
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
74
 
75
  == Changelog ==
76
 
77
+ = 1.4.3 =
78
+ * Improvement: Date part is renamed to Date & Time and offers a lot more options, including minimum and maximum value for year and hour inputs, and an option to set inputs to current date and time by default.
79
+ * Improvement: Added option to specify the subject of email alerts sent to site owners.
80
+ * Improvement: Email alerts now support From, Reply To and multiple CC email addresses.
81
+ * Improvement: Address part now lets users grab their current location automatically.
82
+ * Bug fix: Responsive behavior of Address and Date & Time form parts.
83
+ * Bug fix: Updated code to support versions of PHP down to 5.2.4.
84
+
85
  = 1.4.2 =
86
  * Bug fix: Part widget toggle buttons didn't work on Firefox.
87
  * Bug fix: Part column widths were misbehaving on some themes.
131
 
132
  == Upgrade Notice ==
133
 
134
+ = 1.4.3 =
135
+ * Address, email and visual improvements and bug fixes.
136
+
137
  = 1.4.2 =
138
  * Minor bug fixes.
139