Formidable Forms – Form Builder for WordPress - Version 5.0

Version Description

  • Increased WP version requirement to 5.0.
  • New: Fields can now be dragged beside each other and grid layouts will be automatically applied.
  • New: Added the option to quickly set layouts for a full row of fields at once.
  • New: Fields can now be multiselected using the command, control, and shift keys. Selected field groups can be merged into rows and deleted in batches.
  • New: Added additional formatting to calculations to avoid issues with comparisons getting stripped when spaces are left out.
  • New: Permissions are now updated right away when a formidable license is activated to avoid issues with certain pro features not being available right away.
  • Fix: Updated styling rules for repeater button icons to avoid issues with file upload icons getting styled as well.
  • Fix: The confirmation pop up title was displaying as inline when trying to delete a section.
  • Fix: Updated styling for new fields that are about to be dropped into a form.
Download this release

Release Info

Developer formidableforms
Plugin Icon 128x128 Formidable Forms – Form Builder for WordPress
Version 5.0
Comparing to
See all releases

Code changes from version 4.11.05 to 5.0

classes/controllers/FrmAddonsController.php CHANGED
@@ -11,16 +11,15 @@ class FrmAddonsController {
11
  }
12
 
13
  $label = __( 'Add-Ons', 'formidable' );
14
- if ( FrmAppHelper::pro_is_installed() ) {
15
- $label = '<span style="color:#fe5a1d">' . $label . '</span>';
16
- }
17
  add_submenu_page( 'formidable', 'Formidable | ' . __( 'Add-Ons', 'formidable' ), $label, 'frm_view_forms', 'formidable-addons', 'FrmAddonsController::list_addons' );
18
 
19
  if ( ! FrmAppHelper::pro_is_installed() ) {
20
  add_submenu_page(
21
  'formidable',
22
  'Formidable | ' . __( 'Upgrade', 'formidable' ),
23
- '<span style="color:#fe5a1d">' . __( 'Upgrade', 'formidable' ) . '</span>',
24
  'frm_view_forms',
25
  'formidable-pro-upgrade',
26
  'FrmAddonsController::upgrade_to_pro'
11
  }
12
 
13
  $label = __( 'Add-Ons', 'formidable' );
14
+ $label = '<span style="color:#fe5a1d">' . $label . '</span>';
15
+
 
16
  add_submenu_page( 'formidable', 'Formidable | ' . __( 'Add-Ons', 'formidable' ), $label, 'frm_view_forms', 'formidable-addons', 'FrmAddonsController::list_addons' );
17
 
18
  if ( ! FrmAppHelper::pro_is_installed() ) {
19
  add_submenu_page(
20
  'formidable',
21
  'Formidable | ' . __( 'Upgrade', 'formidable' ),
22
+ '<span class="frm-upgrade-submenu">' . __( 'Upgrade', 'formidable' ) . '</span>',
23
  'frm_view_forms',
24
  'formidable-pro-upgrade',
25
  'FrmAddonsController::upgrade_to_pro'
classes/controllers/FrmAppController.php CHANGED
@@ -440,6 +440,7 @@ class FrmAppController {
440
  'jquery-ui-sortable',
441
  'bootstrap_tooltip',
442
  'bootstrap-multiselect',
 
443
  );
444
 
445
  if ( FrmAppHelper::is_admin_page( 'formidable-styles' ) || FrmAppHelper::is_admin_page( 'formidable-styles2' ) ) {
440
  'jquery-ui-sortable',
441
  'bootstrap_tooltip',
442
  'bootstrap-multiselect',
443
+ 'wp-i18n',
444
  );
445
 
446
  if ( FrmAppHelper::is_admin_page( 'formidable-styles' ) || FrmAppHelper::is_admin_page( 'formidable-styles2' ) ) {
classes/controllers/FrmFieldsController.php CHANGED
@@ -196,13 +196,22 @@ class FrmFieldsController {
196
  $classes = isset( $field['classes'] ) ? $field['classes'] : '';
197
 
198
  // Exclude alignright for now since we aren't using widths.
199
- $classes = str_replace( ' frm_alignright ', ' ', $classes );
200
- if ( trim( $classes ) === 'frm_alignright' ) {
 
 
201
  $classes = '';
202
  }
203
 
204
- $li_classes .= ' frm_form_field frmstart ' . $classes . ' frmend';
205
- if ( ! empty( $field ) ) {
 
 
 
 
 
 
 
206
  $li_classes = apply_filters( 'frm_build_field_class', $li_classes, $field );
207
  }
208
 
196
  $classes = isset( $field['classes'] ) ? $field['classes'] : '';
197
 
198
  // Exclude alignright for now since we aren't using widths.
199
+ $classes = str_replace( ' frm_alignright ', ' ', $classes );
200
+ $classes = trim( $classes );
201
+
202
+ if ( 'frm_alignright' === $classes ) {
203
  $classes = '';
204
  }
205
 
206
+ $li_classes .= ' frm_form_field frmstart ';
207
+
208
+ if ( $classes ) {
209
+ $li_classes .= $classes . ' ';
210
+ }
211
+
212
+ $li_classes .= 'frmend';
213
+
214
+ if ( $field ) {
215
  $li_classes = apply_filters( 'frm_build_field_class', $li_classes, $field );
216
  }
217
 
classes/controllers/FrmHooksController.php CHANGED
@@ -157,6 +157,7 @@ class FrmHooksController {
157
 
158
  FrmSMTPController::load_hooks();
159
  FrmWelcomeController::load_hooks();
 
160
  }
161
 
162
  public static function load_ajax_hooks() {
157
 
158
  FrmSMTPController::load_hooks();
159
  FrmWelcomeController::load_hooks();
160
+ new FrmPluginSearch();
161
  }
162
 
163
  public static function load_ajax_hooks() {
classes/helpers/FrmAppHelper.php CHANGED
@@ -11,7 +11,7 @@ class FrmAppHelper {
11
  /**
12
  * @since 2.0
13
  */
14
- public static $plug_version = '4.11.05';
15
 
16
  /**
17
  * @since 1.07.02
@@ -1224,6 +1224,14 @@ class FrmAppHelper {
1224
  }
1225
  }
1226
 
 
 
 
 
 
 
 
 
1227
  public static function frm_capabilities( $type = 'auto' ) {
1228
  $cap = array(
1229
  'frm_view_forms' => __( 'View Forms', 'formidable' ),
@@ -1234,16 +1242,22 @@ class FrmAppHelper {
1234
  'frm_delete_entries' => __( 'Delete Entries from Admin Area', 'formidable' ),
1235
  );
1236
 
1237
- if ( ! self::pro_is_installed() && 'pro' != $type ) {
1238
  return $cap;
1239
  }
1240
 
1241
- $cap['frm_create_entries'] = __( 'Add Entries from Admin Area', 'formidable' );
1242
- $cap['frm_edit_entries'] = __( 'Edit Entries from Admin Area', 'formidable' );
1243
- $cap['frm_view_reports'] = __( 'View Reports', 'formidable' );
1244
- $cap['frm_edit_displays'] = __( 'Add/Edit Views', 'formidable' );
 
 
 
 
 
 
1245
 
1246
- return $cap;
1247
  }
1248
 
1249
  /**
@@ -1530,7 +1544,7 @@ class FrmAppHelper {
1530
  }
1531
 
1532
  public static function get_user_id_param( $user_id ) {
1533
- if ( ! $user_id || empty( $user_id ) || is_numeric( $user_id ) ) {
1534
  return $user_id;
1535
  }
1536
 
11
  /**
12
  * @since 2.0
13
  */
14
+ public static $plug_version = '5.0';
15
 
16
  /**
17
  * @since 1.07.02
1224
  }
1225
  }
1226
 
1227
+ /**
1228
+ * Gets the list of capabilities.
1229
+ *
1230
+ * @since 5.0 Parameter `$type` supports `pro_only` value.
1231
+ *
1232
+ * @param string $type Supports `auto`, `pro`, or `pro_only`.
1233
+ * @return array
1234
+ */
1235
  public static function frm_capabilities( $type = 'auto' ) {
1236
  $cap = array(
1237
  'frm_view_forms' => __( 'View Forms', 'formidable' ),
1242
  'frm_delete_entries' => __( 'Delete Entries from Admin Area', 'formidable' ),
1243
  );
1244
 
1245
+ if ( ! self::pro_is_installed() && 'pro' !== $type && 'pro_only' !== $type ) {
1246
  return $cap;
1247
  }
1248
 
1249
+ $pro_cap = array(
1250
+ 'frm_create_entries' => __( 'Add Entries from Admin Area', 'formidable' ),
1251
+ 'frm_edit_entries' => __( 'Edit Entries from Admin Area', 'formidable' ),
1252
+ 'frm_view_reports' => __( 'View Reports', 'formidable' ),
1253
+ 'frm_edit_displays' => __( 'Add/Edit Views', 'formidable' ),
1254
+ );
1255
+
1256
+ if ( 'pro_only' === $type ) {
1257
+ return $pro_cap;
1258
+ }
1259
 
1260
+ return $cap + $pro_cap;
1261
  }
1262
 
1263
  /**
1544
  }
1545
 
1546
  public static function get_user_id_param( $user_id ) {
1547
+ if ( ! $user_id || is_numeric( $user_id ) ) {
1548
  return $user_id;
1549
  }
1550
 
classes/helpers/FrmEntriesHelper.php CHANGED
@@ -385,7 +385,7 @@ class FrmEntriesHelper {
385
  */
386
  public static function maybe_set_other_validation( $field, &$value, &$args ) {
387
  $args['other'] = false;
388
- if ( ! $value || empty( $value ) || ! FrmAppHelper::pro_is_installed() ) {
389
  return;
390
  }
391
 
385
  */
386
  public static function maybe_set_other_validation( $field, &$value, &$args ) {
387
  $args['other'] = false;
388
+ if ( ! $value || ! FrmAppHelper::pro_is_installed() ) {
389
  return;
390
  }
391
 
classes/helpers/FrmFieldGridHelper.php ADDED
@@ -0,0 +1,274 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ die( 'You are not allowed to call this page directly.' );
4
+ }
5
+
6
+ class FrmFieldGridHelper {
7
+
8
+ private $parent_li;
9
+
10
+ /**
11
+ * @var int
12
+ */
13
+ private $current_list_size;
14
+
15
+ /**
16
+ * @var int
17
+ */
18
+ private $current_field_count;
19
+
20
+ /**
21
+ * @var string
22
+ */
23
+ private $field_layout_class;
24
+
25
+ /**
26
+ * @var int
27
+ */
28
+ private $active_field_size;
29
+
30
+ /**
31
+ * @var bool $is_frm_first flagged while calling get_field_layout_class, true if classes contain frm_first class.
32
+ */
33
+ private $is_frm_first;
34
+
35
+ /**
36
+ * @var stdClass
37
+ */
38
+ private $field;
39
+
40
+ /**
41
+ * @var FrmFieldGridHelper $section_helper
42
+ */
43
+ private $section_helper;
44
+
45
+ /**
46
+ * @var bool $nested
47
+ */
48
+ private $nested;
49
+
50
+ /**
51
+ * @var int $section_size
52
+ */
53
+ private $section_size;
54
+
55
+ private $section_is_open = false;
56
+
57
+ public function __construct( $nested = false ) {
58
+ $this->parent_li = false;
59
+ $this->current_list_size = 0;
60
+ $this->current_field_count = 0;
61
+ $this->nested = $nested;
62
+ }
63
+
64
+ /**
65
+ * @param stdClass $field
66
+ */
67
+ public function set_field( $field ) {
68
+ $this->field = $field;
69
+
70
+ if ( ! empty( $this->section_helper ) && 'end_divider' !== $field->type ) {
71
+ $this->section_helper->set_field( $field );
72
+ return;
73
+ }
74
+
75
+ if ( 'end_divider' === $field->type ) {
76
+ $this->field_layout_class = '';
77
+ $this->active_field_size = $this->section_size;
78
+ $this->section_is_open = false;
79
+ $this->maybe_close_section_helper();
80
+ } else {
81
+ $this->field_layout_class = $this->get_field_layout_class();
82
+ $this->active_field_size = $this->get_size_of_class( $this->field_layout_class );
83
+ }
84
+
85
+ if ( 'divider' === $field->type && empty( $this->nested ) ) {
86
+ $this->section_size = $this->active_field_size;
87
+ $this->active_field_size = 0;
88
+ $this->section_helper = new self( true );
89
+ }
90
+ }
91
+
92
+ private function maybe_close_section_helper() {
93
+ if ( empty( $this->section_helper ) ) {
94
+ return;
95
+ }
96
+ $this->section_helper->force_close_field_wrapper();
97
+ $this->section_helper = null;
98
+ }
99
+
100
+ /**
101
+ * @return string
102
+ */
103
+ public function get_field_layout_class() {
104
+ $field = FrmFieldsHelper::setup_edit_vars( $this->field );
105
+
106
+ if ( empty( $field['classes'] ) ) {
107
+ return '';
108
+ }
109
+
110
+ $split = explode( ' ', $field['classes'] );
111
+ $this->is_frm_first = in_array( 'frm_first', $split, true );
112
+ $classes = self::get_grid_classes();
113
+
114
+ foreach ( $classes as $class ) {
115
+ if ( in_array( $class, $split, true ) ) {
116
+ return $class;
117
+ }
118
+ }
119
+
120
+ return '';
121
+ }
122
+
123
+ public function maybe_begin_field_wrapper() {
124
+ if ( $this->should_first_close_the_active_field_wrapper() ) {
125
+ $this->close_field_wrapper();
126
+ }
127
+
128
+ if ( false === $this->parent_li && 'end_divider' !== $this->field->type ) {
129
+ $this->begin_field_wrapper();
130
+ }
131
+
132
+ if ( ! empty( $this->section_helper ) && $this->section_is_open ) {
133
+ $this->section_helper->maybe_begin_field_wrapper();
134
+ }
135
+ }
136
+
137
+ /**
138
+ * @return bool
139
+ */
140
+ private function should_first_close_the_active_field_wrapper() {
141
+ if ( false === $this->parent_li || ! empty( $this->section_helper ) ) {
142
+ return false;
143
+ }
144
+ if ( 'end_divider' === $this->field->type ) {
145
+ return false;
146
+ }
147
+ return ! $this->can_support_current_layout() || $this->is_frm_first;
148
+ }
149
+
150
+ private function begin_field_wrapper() {
151
+ echo '<li class="frm_field_box"><ul class="frm_grid_container frm_sorting">';
152
+ $this->parent_li = true;
153
+ $this->current_list_size = 0;
154
+ $this->current_field_count = 0;
155
+ }
156
+
157
+ /**
158
+ * @param string $class
159
+ * @return int
160
+ */
161
+ private static function get_size_of_class( $class ) {
162
+ switch ( $class ) {
163
+ case 'frm_half':
164
+ return 6;
165
+ case 'frm_third':
166
+ return 4;
167
+ case 'frm_two_thirds':
168
+ return 8;
169
+ case 'frm_fourth':
170
+ return 3;
171
+ case 'frm_three_fourths':
172
+ return 9;
173
+ case 'frm_sixth':
174
+ return 2;
175
+ }
176
+
177
+ if ( 0 === strpos( $class, 'frm' ) ) {
178
+ $substr = substr( $class, 3 );
179
+ if ( is_numeric( $substr ) ) {
180
+ return (int) $substr;
181
+ }
182
+ }
183
+
184
+ // Anything missing a layout class should be a full width row.
185
+ return 12;
186
+ }
187
+
188
+ public function sync_list_size() {
189
+ if ( ! isset( $this->field ) ) {
190
+ return;
191
+ }
192
+
193
+ if ( 'divider' === $this->field->type ) {
194
+ $this->section_is_open = true;
195
+ }
196
+
197
+ if ( ! empty( $this->section_helper ) ) {
198
+ $this->section_helper->sync_list_size();
199
+ if ( 'end_divider' === $this->field->type ) {
200
+ $this->maybe_close_section_helper();
201
+ }
202
+ return;
203
+ }
204
+
205
+ if ( false !== $this->parent_li ) {
206
+ $this->current_field_count ++;
207
+ $this->current_list_size += $this->active_field_size;
208
+ if ( 12 === $this->current_list_size ) {
209
+ $this->close_field_wrapper();
210
+ }
211
+ }
212
+ }
213
+
214
+ /**
215
+ * It is possible that there was still space for another field so the wrapper could still be open after looping the fields.
216
+ * If it is, make sure it's closed now.
217
+ */
218
+ public function force_close_field_wrapper() {
219
+ if ( false !== $this->parent_li ) {
220
+ $this->close_field_wrapper();
221
+ }
222
+ }
223
+
224
+ private function close_field_wrapper() {
225
+ $this->maybe_close_section_helper();
226
+ echo '</ul></li>';
227
+ $this->parent_li = false;
228
+ $this->current_list_size = 0;
229
+ $this->current_field_count = 0;
230
+ }
231
+
232
+ private function can_support_current_layout() {
233
+ if ( 'end_divider' === $this->field->type ) {
234
+ return true;
235
+ }
236
+ return $this->can_support_an_additional_layout( $this->field_layout_class );
237
+ }
238
+
239
+ /**
240
+ * @param string $class
241
+ * @return bool
242
+ */
243
+ private function can_support_an_additional_layout( $class ) {
244
+ $size = $this->get_size_of_class( $class );
245
+ return $this->current_list_size + $size <= 12;
246
+ }
247
+
248
+ /**
249
+ * @return array<string>
250
+ */
251
+ private static function get_grid_classes() {
252
+ return array(
253
+ 'frm_full',
254
+ 'frm_half',
255
+ 'frm_third',
256
+ 'frm_two_thirds',
257
+ 'frm_fourth',
258
+ 'frm_three_fourths',
259
+ 'frm_sixth',
260
+ 'frm1',
261
+ 'frm2',
262
+ 'frm3',
263
+ 'frm4',
264
+ 'frm5',
265
+ 'frm6',
266
+ 'frm7',
267
+ 'frm8',
268
+ 'frm9',
269
+ 'frm10',
270
+ 'frm11',
271
+ 'frm12',
272
+ );
273
+ }
274
+ }
classes/helpers/FrmFormsHelper.php CHANGED
@@ -75,7 +75,7 @@ class FrmFormsHelper {
75
  }
76
 
77
  /**
78
- * @param string|object $selected - The label for the placeholder, or the form object.
79
  */
80
  public static function form_switcher( $selected = false ) {
81
  $where = apply_filters( 'frm_forms_dropdown', array(), '' );
@@ -751,7 +751,7 @@ BEFORE_HTML;
751
  }
752
 
753
  public static function submit_button_label( $submit ) {
754
- if ( ! $submit || empty( $submit ) ) {
755
  $frm_settings = FrmAppHelper::get_settings();
756
  $submit = $frm_settings->submit_value;
757
  }
75
  }
76
 
77
  /**
78
+ * @param string|object|false $selected - The label for the placeholder, or the form object.
79
  */
80
  public static function form_switcher( $selected = false ) {
81
  $where = apply_filters( 'frm_forms_dropdown', array(), '' );
751
  }
752
 
753
  public static function submit_button_label( $submit ) {
754
+ if ( ! $submit ) {
755
  $frm_settings = FrmAppHelper::get_settings();
756
  $submit = $frm_settings->submit_value;
757
  }
classes/models/FrmAddon.php CHANGED
@@ -238,6 +238,36 @@ class FrmAddon {
238
  update_option( $this->option_name . 'active', $is_active );
239
  $this->delete_cache();
240
  FrmAppHelper::save_combined_js();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  }
242
 
243
  /**
238
  update_option( $this->option_name . 'active', $is_active );
239
  $this->delete_cache();
240
  FrmAppHelper::save_combined_js();
241
+ $this->update_pro_capabilities();
242
+ }
243
+
244
+ /**
245
+ * Updates roles capabilities after pro license is active.
246
+ *
247
+ * @since 5.0
248
+ */
249
+ protected function update_pro_capabilities() {
250
+ global $wp_roles;
251
+
252
+ $caps = FrmAppHelper::frm_capabilities( 'pro_only' );
253
+ $roles = get_editable_roles();
254
+ $settings = new FrmSettings();
255
+ foreach ( $caps as $cap => $cap_desc ) {
256
+ $cap_roles = (array) ( isset( $settings->$cap ) ? $settings->$cap : 'administrator' );
257
+
258
+ // Make sure administrators always have permissions.
259
+ if ( ! in_array( 'administrator', $cap_roles ) ) {
260
+ array_push( $cap_roles, 'administrator' );
261
+ }
262
+
263
+ foreach ( $roles as $role => $details ) {
264
+ if ( in_array( $role, $cap_roles ) ) {
265
+ $wp_roles->add_cap( $role, $cap );
266
+ } else {
267
+ $wp_roles->remove_cap( $role, $cap );
268
+ }
269
+ }
270
+ }
271
  }
272
 
273
  /**
classes/models/FrmEntry.php CHANGED
@@ -65,7 +65,7 @@ class FrmEntry {
65
  global $wpdb;
66
  $entry_exists = FrmDb::get_col( $wpdb->prefix . 'frm_items', $check_val, 'id', array( 'order_by' => 'created_at DESC' ) );
67
 
68
- if ( ! $entry_exists || empty( $entry_exists ) || ! isset( $values['item_meta'] ) ) {
69
  return false;
70
  }
71
 
65
  global $wpdb;
66
  $entry_exists = FrmDb::get_col( $wpdb->prefix . 'frm_items', $check_val, 'id', array( 'order_by' => 'created_at DESC' ) );
67
 
68
+ if ( ! $entry_exists || ! isset( $values['item_meta'] ) ) {
69
  return false;
70
  }
71
 
classes/models/FrmForm.php CHANGED
@@ -267,11 +267,7 @@ class FrmForm {
267
  private static function sanitize_field_opt( $opt, &$value ) {
268
  if ( is_string( $value ) ) {
269
  if ( $opt === 'calc' ) {
270
- $allow = array( '<= ', ' >=' ); // Allow <= and >=
271
- $temp = array( '< = ', ' > =' );
272
- $value = str_replace( $allow, $temp, $value );
273
- $value = strip_tags( $value );
274
- $value = str_replace( $temp, $allow, $value );
275
  } else {
276
  $value = FrmAppHelper::kses( $value, 'all' );
277
  }
@@ -279,6 +275,39 @@ class FrmForm {
279
  }
280
  }
281
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  /**
283
  * Updating the settings page
284
  */
267
  private static function sanitize_field_opt( $opt, &$value ) {
268
  if ( is_string( $value ) ) {
269
  if ( $opt === 'calc' ) {
270
+ $value = self::sanitize_calc( $value );
 
 
 
 
271
  } else {
272
  $value = FrmAppHelper::kses( $value, 'all' );
273
  }
275
  }
276
  }
277
 
278
+ /**
279
+ * @param string $value
280
+ * @return string
281
+ */
282
+ private static function sanitize_calc( $value ) {
283
+ if ( false !== strpos( $value, '<' ) ) {
284
+ $value = self::normalize_calc_spaces( $value );
285
+ }
286
+ $allow = array( '<= ', ' >=' ); // Allow <= and >=
287
+ $temp = array( '< = ', ' > =' );
288
+ $value = str_replace( $allow, $temp, $value );
289
+ $value = strip_tags( $value );
290
+ $value = str_replace( $temp, $allow, $value );
291
+ return $value;
292
+ }
293
+
294
+ /**
295
+ * Format a comparison like 5<10 to 5 < 10. Also works on 5< 10, 5 <10, 5<=10 variations.
296
+ * This is to avoid an issue with unspaced calculations being recognized as HTML that gets removed when strip_tags is called.
297
+ *
298
+ * @param string $calc
299
+ * @return string
300
+ */
301
+ private static function normalize_calc_spaces( $calc ) {
302
+ // Check for a pattern with 5 parts
303
+ // $1 \d the first comparison digit.
304
+ // $2 a space (optional).
305
+ // $3 an equals sign (optional) that follows the < operator for <= comparisons.
306
+ // $4 another space (optional).
307
+ // $5 \d the second comparison digit.
308
+ return preg_replace( '/(\d)( ){0,1}<(=){0,1}( ){0,1}(\d)/', '$1 <$3 $5', $calc );
309
+ }
310
+
311
  /**
312
  * Updating the settings page
313
  */
classes/models/FrmPluginSearch.php ADDED
@@ -0,0 +1,363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ die( 'You are not allowed to call this page directly.' );
4
+ }
5
+
6
+ class FrmPluginSearch {
7
+
8
+ /**
9
+ * PSH slug name.
10
+ *
11
+ * @var string
12
+ */
13
+ public static $slug = 'frm-plugin-search';
14
+
15
+ /**
16
+ * Option name that holds dismissed suggestions.
17
+ *
18
+ * @var string
19
+ */
20
+ protected static $dismissed_opt = 'frm_dismissed_hints';
21
+
22
+ public function __construct() {
23
+ add_action( 'current_screen', array( $this, 'start' ) );
24
+ }
25
+
26
+ /**
27
+ * Add actions and filters only if this is the plugin installation screen and it's the first page.
28
+ *
29
+ * @param object $screen WP Screen object.
30
+ *
31
+ * @since 4.12
32
+ */
33
+ public function start( $screen ) {
34
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
35
+ if ( 'plugin-install' === $screen->base && ( ! isset( $_GET['paged'] ) || 1 === intval( $_GET['paged'] ) ) ) {
36
+ add_filter( 'plugins_api_result', array( $this, 'inject_suggestion' ), 10, 3 );
37
+ add_filter( 'self_admin_url', array( $this, 'plugin_details' ) );
38
+ add_filter( 'plugin_install_action_links', array( $this, 'insert_related_links' ), 10, 2 );
39
+ add_action( 'admin_enqueue_scripts', array( $this, 'load_plugins_search_script' ) );
40
+ $this->maybe_dismiss();
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Intercept the plugins API response and add in an appropriate card.
46
+ *
47
+ * @param object $result Plugin search results.
48
+ * @param string $action unused.
49
+ * @param object $args Search args.
50
+ */
51
+ public function inject_suggestion( $result, $action, $args ) {
52
+ // Looks like a search query; it's matching time.
53
+ if ( empty( $args->search ) ) {
54
+ return $result;
55
+ }
56
+
57
+ $addon_list = $this->get_addons();
58
+
59
+ // Lowercase, trim, remove punctuation/special chars, decode url, remove 'formidable'.
60
+ $normalized_term = $this->search_to_array( $args->search );
61
+ if ( empty( $normalized_term ) ) {
62
+ // Don't add anything extra.
63
+ return $result;
64
+ }
65
+
66
+ $matching_addon = $this->matching_addon( $addon_list, $normalized_term );
67
+
68
+ if ( empty( $matching_addon ) || ! $this->should_display_hint( $matching_addon ) ) {
69
+ return $result;
70
+ }
71
+
72
+ $inject = (array) $this->get_plugin_data();
73
+ $overrides = array(
74
+ 'plugin-search' => true, // Helps to determine if that an injected card.
75
+ 'name' => sprintf(
76
+ /* translators: Formidable addon name */
77
+ esc_html_x( 'Formidable %s', 'Formidable Addon Name', 'formidable' ),
78
+ $addon_list[ $matching_addon ]['name']
79
+ ),
80
+ 'addon' => $addon_list[ $matching_addon ]['slug'],
81
+ 'short_description' => $addon_list[ $matching_addon ]['excerpt'],
82
+ 'slug' => self::$slug,
83
+ 'version' => $addon_list[ $matching_addon ]['version'],
84
+ );
85
+
86
+ // Splice in the base addon data.
87
+ $inject = array_merge( $inject, $addon_list[ $matching_addon ], $overrides );
88
+
89
+ // Add it to the top of the list.
90
+ array_unshift( $result->plugins, $inject );
91
+
92
+ return $result;
93
+ }
94
+
95
+ /**
96
+ * Search for any addons that match the searched terms.
97
+ *
98
+ * @since 4.12
99
+ *
100
+ * @return int
101
+ */
102
+ private function matching_addon( $addon_list, $normalized_term ) {
103
+ $matching_addon = null;
104
+
105
+ // Try to match a passed search term with addon's search terms.
106
+ foreach ( $addon_list as $addon_id => $addon_opts ) {
107
+ if ( ! is_array( $addon_opts ) || empty( $addon_opts['excerpt'] ) ) {
108
+ continue;
109
+ }
110
+
111
+ /*
112
+ * Does the site's current plan support the feature?
113
+ */
114
+ $is_supported_by_plan = ! empty( $addon_opts['url'] );
115
+
116
+ if ( ! isset( $addon_opts['search_terms'] ) ) {
117
+ $addon_opts['search_terms'] = '';
118
+ }
119
+
120
+ $addon_terms = $this->search_to_array( $addon_opts['search_terms'] . ', ' . $addon_opts['name'] );
121
+
122
+ $matches = ! empty( array_intersect( $addon_terms, $normalized_term ) );
123
+
124
+ if ( $matches && $is_supported_by_plan ) {
125
+ $matching_addon = $addon_id;
126
+ break;
127
+ }
128
+ }
129
+
130
+ return $matching_addon;
131
+ }
132
+
133
+ /**
134
+ * @since 4.12
135
+ *
136
+ * @return array
137
+ */
138
+ private function get_addons() {
139
+ $api = new FrmFormApi();
140
+ return $api->get_api_info();
141
+ }
142
+
143
+ /**
144
+ * Get the plugin repo's data to populate the fields with.
145
+ *
146
+ * @return array|mixed|object|WP_Error
147
+ */
148
+ private function get_plugin_data() {
149
+ $data = get_transient( 'formidable_plugin_data' );
150
+
151
+ if ( false !== $data && ! is_wp_error( $data ) ) {
152
+ return $data;
153
+ }
154
+
155
+ include_once ABSPATH . 'wp-admin/includes/plugin-install.php';
156
+ $data = plugins_api(
157
+ 'plugin_information',
158
+ array(
159
+ 'slug' => 'formidable',
160
+ 'is_ssl' => is_ssl(),
161
+ 'fields' => array(
162
+ 'banners' => true,
163
+ 'reviews' => true,
164
+ 'active_installs' => true,
165
+ 'versions' => false,
166
+ 'sections' => false,
167
+ ),
168
+ )
169
+ );
170
+ set_transient( 'formidable_plugin_data', $data, DAY_IN_SECONDS );
171
+
172
+ return $data;
173
+ }
174
+
175
+ /**
176
+ * Modify URL used to fetch to plugin information so it pulls Formidable plugin page.
177
+ *
178
+ * @param string $url URL to load in dialog pulling the plugin page from wporg.
179
+ *
180
+ * @since 4.12
181
+ *
182
+ * @return string The URL with 'formidable' instead of 'frm-plugin-search'.
183
+ */
184
+ public function plugin_details( $url ) {
185
+ return false !== stripos( $url, 'tab=plugin-information&amp;plugin=' . self::$slug )
186
+ ? 'plugin-install.php?tab=plugin-information&amp;plugin=formidable&amp;TB_iframe=true&amp;width=600&amp;height=550'
187
+ : $url;
188
+ }
189
+
190
+ /**
191
+ * @since 4.12
192
+ */
193
+ private function maybe_dismiss() {
194
+ $addon = FrmAppHelper::get_param( 'frm-dismiss', '', 'get', 'absint' );
195
+ if ( ! empty( $addon ) ) {
196
+ $this->add_to_dismissed_hints( $addon );
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Returns a list of previously dismissed hints.
202
+ *
203
+ * @since 4.12
204
+ *
205
+ * @return array List of dismissed hints.
206
+ */
207
+ protected function get_dismissed_hints() {
208
+ $dismissed_hints = get_option( self::$dismissed_opt );
209
+ return ! empty( $dismissed_hints ) && is_array( $dismissed_hints ) ? $dismissed_hints : array();
210
+ }
211
+
212
+ /**
213
+ * Save the hint in the list of dismissed hints.
214
+ *
215
+ * @since 4.12
216
+ *
217
+ * @param string $hint The hint id, which is a Formidable addon slug.
218
+ *
219
+ * @return bool Whether the card was added to the list and hence dismissed.
220
+ */
221
+ protected function add_to_dismissed_hints( $hint ) {
222
+ $hints = array_merge( $this->get_dismissed_hints(), array( $hint ) );
223
+ return update_option( self::$dismissed_opt, $hints, 'no' );
224
+ }
225
+
226
+ /**
227
+ * Checks that the addon slug passed should be displayed.
228
+ *
229
+ * A feature hint will be displayed if it has not been dismissed before or if 2 or fewer other hints have been dismissed.
230
+ *
231
+ * @since 7.2.1
232
+ *
233
+ * @param string $hint The hint id, which is a Formidable addon slug.
234
+ *
235
+ * @return bool True if $hint should be displayed.
236
+ */
237
+ protected function should_display_hint( $hint ) {
238
+ $dismissed_hints = $this->get_dismissed_hints();
239
+
240
+ // If more than 3 hints have been dismissed, then show no more.
241
+ if ( 3 < count( $dismissed_hints ) ) {
242
+ return false;
243
+ }
244
+
245
+ return ! in_array( $hint, $dismissed_hints, true );
246
+ }
247
+
248
+ /**
249
+ * Take a raw search query and return something a bit more standardized and
250
+ * easy to work with.
251
+ *
252
+ * @param string $term The raw search term.
253
+ * @return string A simplified/sanitized version.
254
+ */
255
+ private function sanitize_search_term( $term ) {
256
+ $term = strtolower( urldecode( $term ) );
257
+
258
+ // remove non-alpha/space chars.
259
+ $term = preg_replace( '/[^a-z ]/', '', $term );
260
+
261
+ // remove strings that don't help matches.
262
+ $term = trim( str_replace( array( 'formidable', 'free', 'wordpress', 'wp ', 'plugin' ), '', $term ) );
263
+
264
+ return $term;
265
+ }
266
+
267
+ /**
268
+ * @since 4.12
269
+ *
270
+ * @return array
271
+ */
272
+ private function search_to_array( $terms ) {
273
+ $terms = $this->sanitize_search_term( $terms );
274
+ return array_filter( explode( ',', $terms ) );
275
+ }
276
+
277
+ /**
278
+ * Put some more appropriate links on our custom result cards.
279
+ *
280
+ * @param array $links Related links.
281
+ * @param array $plugin Plugin result information.
282
+ */
283
+ public function insert_related_links( $links, $plugin ) {
284
+ if ( self::$slug !== $plugin['slug'] ) {
285
+ return $links;
286
+ }
287
+
288
+ // By the time this filter is applied, self_admin_url was already applied and we don't need it anymore.
289
+ remove_filter( 'self_admin_url', array( $this, 'plugin_details' ) );
290
+
291
+ $links = array();
292
+ $is_installed = $this->is_installed( $plugin['plugin'] );
293
+ $is_active = is_plugin_active( $plugin['plugin'] );
294
+ $has_access = ! empty( $plugin['url'] );
295
+
296
+ // Plugin installed, active, feature not enabled; prompt to enable.
297
+ if ( ! $is_active && $is_installed ) {
298
+ if ( current_user_can( 'activate_plugins' ) ) {
299
+ $activate_url = add_query_arg(
300
+ array(
301
+ 'action' => 'activate',
302
+ '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $plugin['plugin'] ),
303
+ 'plugin' => $plugin['plugin'],
304
+ ),
305
+ admin_url( 'plugins.php' )
306
+ );
307
+ $links['frm_get_started'] = '<a href="' . esc_url( $activate_url ) . '" class="button activate-now" aria-label="Activate ' . esc_attr( $plugin['name'] ) . '">' . __( 'Activate', 'formidable' ) . '</a>';
308
+ }
309
+ } elseif ( ! $is_active && isset( $plugin['url'] ) ) {
310
+ // Go to the add-ons page to install.
311
+ $links[] = '<a
312
+ class="button-secondary"
313
+ href="' . esc_url( admin_url( 'admin.php?page=formidable-addons' ) ) . '"
314
+ >' . __( 'Install Now', 'formidable' ) . '</a>';
315
+
316
+ } elseif ( ! empty( $plugin['link'] ) ) {
317
+ // Add link pointing to a relevant doc page in formidable.com.
318
+ $links[] = '<a
319
+ class="button-primary frm-plugin-search__learn-more"
320
+ href="' . esc_url( FrmAppHelper::admin_upgrade_link( 'plugin-learn-more', $plugin['link'] ) ) . '"
321
+ target="_blank"
322
+ data-addon="' . esc_attr( $plugin['addon'] ) . '"
323
+ >' . esc_html__( 'Learn more', 'formidable' ) . '</a>';
324
+ }
325
+
326
+ // Dismiss link.
327
+ $dismiss = add_query_arg( array( 'frm-dismiss' => $plugin['id'] ) );
328
+ $links[] = '<a
329
+ href="' . $dismiss . '"
330
+ class="frm-plugin-search__dismiss"
331
+ data-addon="' . esc_attr( $plugin['addon'] ) . '"
332
+ >' . esc_html__( 'Hide this suggestion', 'formidable' ) . '</a>';
333
+
334
+ return $links;
335
+ }
336
+
337
+ protected function is_installed( $plugin ) {
338
+ if ( ! function_exists( 'get_plugins' ) ) {
339
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
340
+ }
341
+
342
+ $all_plugins = get_plugins();
343
+
344
+ return isset( $all_plugins[ $plugin ] );
345
+ }
346
+
347
+ /**
348
+ * Load the search scripts and CSS for PSH.
349
+ */
350
+ public function load_plugins_search_script() {
351
+ wp_enqueue_script( self::$slug, FrmAppHelper::plugin_url() . '/js/plugin-search.js', array(), FrmAppHelper::plugin_version(), true );
352
+ wp_localize_script(
353
+ self::$slug,
354
+ 'frmPlugSearch',
355
+ array(
356
+ 'legend' => esc_html__(
357
+ 'This suggestion was made by Formidable Forms, the form builder and application plugin already installed on your site.',
358
+ 'formidable'
359
+ ),
360
+ )
361
+ );
362
+ }
363
+ }
classes/views/frm-forms/add_field.php CHANGED
@@ -13,24 +13,36 @@ if ( ! defined( 'ABSPATH' ) ) {
13
  <div id="field_<?php echo esc_attr( $field['id'] ); ?>_inner_container" class="frm_inner_field_container">
14
  <div class="frm-field-action-icons frm-show-hover">
15
 
 
 
 
 
16
  <?php if ( $field['type'] === 'divider' ) { ?>
17
  <a href="#" class="frm-collapse-section frm-hover-icon frm_icon_font frm_arrowdown6_icon" title="<?php esc_attr_e( 'Expand/Collapse Section', 'formidable' ); ?>"></a>
18
  <?php } ?>
19
 
20
- <a href="#" class="frm_bstooltip frm-move frm-hover-icon" title="<?php esc_attr_e( 'Move Field', 'formidable' ); ?>">
21
- <?php FrmAppHelper::icon_by_class( 'frm_icon_font frm_move_icon' ); ?>
22
- </a>
23
- <a href="#" class="frm_bstooltip frm-hover-icon frm_delete_field" title="<?php esc_attr_e( 'Delete Field', 'formidable' ); ?>">
24
- <?php FrmAppHelper::icon_by_class( 'frm_icon_font frm_delete_icon' ); ?>
25
- </a>
26
-
27
- <a href="#" class="frm_bstooltip frm-hover-icon frm_clone_field" title="<?php ( $field['type'] === 'divider' ) ? esc_attr_e( 'Duplicate Section', 'formidable' ) : esc_attr_e( 'Duplicate Field', 'formidable' ); ?>">
28
- <?php FrmAppHelper::icon_by_class( 'frm_icon_font frm_clone_icon' ); ?>
29
  </a>
30
 
31
- <div class="frm-sub-label alignright">
32
- (ID <?php echo esc_html( $field['id'] ); ?>)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  </div>
 
34
  </div>
35
 
36
  <label class="frm_primary_label" id="field_label_<?php echo esc_attr( $field['id'] ); ?>">
@@ -75,10 +87,9 @@ if ( 'divider' === $field['type'] ) {
75
  ?>
76
  </div>
77
  <div class="frm_no_section_fields">
78
- <?php FrmAppHelper::icon_by_class( 'frmfont frm_plus2_icon frm-large-plus', array( 'aria-hidden' => 'true' ) ); ?>
79
- <p class="howto"><?php esc_html_e( 'Your section has no fields. Drag fields here to add them to this section.', 'formidable' ); ?></p>
80
  </div>
81
- <ul class="start_divider frm_sorting">
82
  <?php
83
  } elseif ( 'end_divider' === $field['type'] ) {
84
  ?>
13
  <div id="field_<?php echo esc_attr( $field['id'] ); ?>_inner_container" class="frm_inner_field_container">
14
  <div class="frm-field-action-icons frm-show-hover">
15
 
16
+ <div class="frm-sub-label frm-field-id">
17
+ (ID <?php echo esc_html( $field['id'] ); ?>)
18
+ </div>
19
+
20
  <?php if ( $field['type'] === 'divider' ) { ?>
21
  <a href="#" class="frm-collapse-section frm-hover-icon frm_icon_font frm_arrowdown6_icon" title="<?php esc_attr_e( 'Expand/Collapse Section', 'formidable' ); ?>"></a>
22
  <?php } ?>
23
 
24
+ <a href="#" class="frm_bstooltip frm-move frm-hover-icon" title="<?php esc_attr_e( 'Move Field', 'formidable' ); ?>" data-container="body">
25
+ <?php FrmAppHelper::icon_by_class( 'frm_icon_font frm_thick_move_icon' ); ?>
 
 
 
 
 
 
 
26
  </a>
27
 
28
+ <div class="dropdown">
29
+ <a href="#" class="frm_bstooltip frm-hover-icon frm-dropdown-toggle dropdown-toggle" title="<?php esc_attr_e( 'More Options', 'formidable' ); ?>" data-toggle="dropdown" data-container="body">
30
+ <?php FrmAppHelper::icon_by_class( 'frm_icon_font frm_thick_more_vert_icon' ); ?>
31
+ </a>
32
+ <ul class="frm-dropdown-menu" role="menu">
33
+ <li class="frm_dropdown_li frm_delete_field" href="#">
34
+ <?php FrmAppHelper::icon_by_class( 'frm_icon_font frm_delete_icon' ); ?> <?php esc_html_e( 'Delete', 'formidable' ); ?>
35
+ </li>
36
+ <li class="frm_dropdown_li frm_clone_field" href="#">
37
+ <?php FrmAppHelper::icon_by_class( 'frm_icon_font frm_clone_icon' ); ?> <?php esc_html_e( 'Duplicate', 'formidable' ); ?>
38
+ </li>
39
+ <hr>
40
+ <li class="frm_dropdown_li frm_select_field">
41
+ <?php FrmAppHelper::icon_by_class( 'frm_icon_font frm_settings_icon' ); ?> <?php esc_html_e( 'Field settings', 'formidable' ); ?>
42
+ </li>
43
+ </ul>
44
  </div>
45
+
46
  </div>
47
 
48
  <label class="frm_primary_label" id="field_label_<?php echo esc_attr( $field['id'] ); ?>">
87
  ?>
88
  </div>
89
  <div class="frm_no_section_fields">
90
+ <p class="howto"><?php esc_html_e( 'Add Fields Here', 'formidable' ); ?></p>
 
91
  </div>
92
+ <ul class="start_divider frm_sorting frm_grid_container">
93
  <?php
94
  } elseif ( 'end_divider' === $field['type'] ) {
95
  ?>
classes/views/frm-forms/edit.php CHANGED
@@ -39,3 +39,14 @@ if ( ! defined( 'ABSPATH' ) ) {
39
  </div>
40
  </div>
41
  </div>
 
 
 
 
 
 
 
 
 
 
 
39
  </div>
40
  </div>
41
  </div>
42
+ <div class="frm_hidden">
43
+ <svg id="frm_break_field_group_svg" class="frmsvg">
44
+ <use xlink:href="#frm_break_field_group_icon"></use>
45
+ </svg>
46
+ <svg id="frm_gear_svg" class="frmsvg">
47
+ <use xlink:href="#frm_gear_icon"></use>
48
+ </svg>
49
+ <svg id="frm_trash_svg" class="frmsvg">
50
+ <use xlink:href="#frm_trash_icon"></use>
51
+ </svg>
52
+ </div>
classes/views/frm-forms/form.php CHANGED
@@ -26,13 +26,19 @@ if ( ! defined( 'ABSPATH' ) ) {
26
 
27
  <ul id="frm-show-fields" class="frm_sorting inside">
28
  <?php
29
- if ( isset( $values['fields'] ) && ! empty( $values['fields'] ) ) {
 
30
  $values['count'] = 0;
31
  foreach ( $values['fields'] as $field ) {
32
  $values['count']++;
 
 
33
  FrmFieldsController::load_single_field( $field, $values );
 
34
  unset( $field );
35
  }
 
 
36
  }
37
  ?>
38
  </ul>
26
 
27
  <ul id="frm-show-fields" class="frm_sorting inside">
28
  <?php
29
+ if ( ! empty( $values['fields'] ) ) {
30
+ $grid_helper = new FrmFieldGridHelper();
31
  $values['count'] = 0;
32
  foreach ( $values['fields'] as $field ) {
33
  $values['count']++;
34
+ $grid_helper->set_field( $field );
35
+ $grid_helper->maybe_begin_field_wrapper();
36
  FrmFieldsController::load_single_field( $field, $values );
37
+ $grid_helper->sync_list_size();
38
  unset( $field );
39
  }
40
+ $grid_helper->force_close_field_wrapper();
41
+ unset( $grid_helper );
42
  }
43
  ?>
44
  </ul>
css/custom_theme.css.php CHANGED
@@ -1432,7 +1432,7 @@ select.frm_loading_lookup{
1432
  display:block;
1433
  }
1434
 
1435
- .with_frm_style .frm_repeat_sec .frm_form_field .frm_icon_font::before {
1436
  color:<?php echo esc_html( $defaults['repeat_icon_color'] . $important ); ?>;
1437
  color:var(--repeat-icon-color)<?php echo esc_html( $important ); ?>;
1438
  }
1432
  display:block;
1433
  }
1434
 
1435
+ .with_frm_style .frm_repeat_sec .frm_form_field.frm_repeat_buttons .frm_icon_font::before {
1436
  color:<?php echo esc_html( $defaults['repeat_icon_color'] . $important ); ?>;
1437
  color:var(--repeat-icon-color)<?php echo esc_html( $important ); ?>;
1438
  }
css/font_icons.css CHANGED
@@ -7,6 +7,16 @@
7
  font-style: normal;
8
  }
9
 
 
 
 
 
 
 
 
 
 
 
10
  .frmfont,
11
  .frm_icon_font,
12
  .frm_dashicon_font{
7
  font-style: normal;
8
  }
9
 
10
+ .frm-submenu-highlight {
11
+ background: #F15A24;
12
+ }
13
+
14
+ .frm-submenu-highlight a span {
15
+ color: #fff;
16
+ font-weight: 600;
17
+ }
18
+
19
+
20
  .frmfont,
21
  .frm_icon_font,
22
  .frm_dashicon_font{
css/frm_admin.css CHANGED
@@ -3201,6 +3201,18 @@ input[type="checkbox"] {
3201
  clear: both;
3202
  }
3203
 
 
 
 
 
 
 
 
 
 
 
 
 
3204
  .frm_form_settings #op-popup, /* 1Password */
3205
  .frm_form_settings com-1password-op-button,
3206
  .frm_conf_below .frm_conf_field_container .frm_primary_label,
@@ -3620,6 +3632,7 @@ label input[type="checkbox"], label input[type="radio"] {
3620
  .frm_field_box {
3621
  padding: 5px;
3622
  min-height: 20px;
 
3623
  }
3624
 
3625
  .frm_indent_opt, td.frm_indent_opt {
@@ -3636,12 +3649,28 @@ label input[type="checkbox"], label input[type="radio"] {
3636
 
3637
  .frm_single_option.ui-sortable-placeholder,
3638
  li.sortable-placeholder {
3639
- width: calc(100% - 5px) !important;
3640
- height: 0 !important;
3641
  border: none;
3642
- box-shadow: 2px -10px 0 1px var(--primary-color);
3643
  margin: 0;
3644
  visibility: visible !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3645
  }
3646
 
3647
  .edit_field_type_divider + li.sortable-placeholder {
@@ -3698,6 +3727,10 @@ li.ui-state-default.edit_field_type_divider .frm-show-hover {
3698
  transition: opacity .15s ease-in-out;
3699
  }
3700
 
 
 
 
 
3701
  .frm_sortable_field_opts {
3702
  margin-top: 5px;
3703
  }
@@ -3762,6 +3795,10 @@ li.ui-state-default.selected.edit_field_type_divider .divider_section_only .frm-
3762
  opacity: 100;
3763
  }
3764
 
 
 
 
 
3765
  .frm_sorting span.ui-icon {
3766
  width: 16px;
3767
  }
@@ -5200,15 +5237,40 @@ a.frm_action_icon:hover {
5200
  content: '\e606';
5201
  }
5202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5203
  .frm-field-action-icons a {
5204
- margin-left: 15px;
5205
- float: right;
5206
  font-size: 16px;
5207
  width: 17px;
5208
  }
5209
 
 
 
 
 
 
 
 
 
5210
  .frm-field-action-icons a .frmsvg {
5211
- color: var(--primary-hover) !important;
5212
  font-size: 17px;
5213
  width: 17px;
5214
  height: 17px;
@@ -5429,12 +5491,52 @@ span.howto {
5429
  min-height: 350px;
5430
  padding: 10px 0 35px;
5431
  margin: 0;
 
5432
  }
5433
 
5434
  .frm-has-fields #frm-show-fields {
5435
  min-height: 50px;
5436
  }
5437
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5438
  .frm_sorting .no_repeat_section li.ui-state-default.edit_field_type_end_divider {
5439
  display: none !important;
5440
  }
@@ -5449,7 +5551,7 @@ span.howto {
5449
  background-color: transparent;
5450
  background-image: none;
5451
  padding: 5px;
5452
- margin: 10px 0;
5453
  box-shadow: none;
5454
  display: block;
5455
  text-align: inherit !important;
@@ -5470,10 +5572,6 @@ span.howto {
5470
  font-style: normal;
5471
  }
5472
 
5473
- .repeat_section .start_divider li:only-child {
5474
- margin-top: 50px;
5475
- }
5476
-
5477
  .show_repeat_sec {
5478
  display: none;
5479
  }
@@ -5855,7 +5953,6 @@ tr.frm_options_heading td {
5855
  padding: 20px 0 0;
5856
  width: calc( 100% - 20px );
5857
  height: 121px;
5858
- border-color: var(--grey-border);
5859
  transition: all 0.4s linear;
5860
  }
5861
 
@@ -6635,6 +6732,16 @@ ul .frm_col_two {
6635
  font-size: 15px;
6636
  }
6637
 
 
 
 
 
 
 
 
 
 
 
6638
  .frm_code_list.frm-full-hover a span {
6639
  color: var(--grey);
6640
  float: right;
@@ -6836,6 +6943,10 @@ input[disabled],
6836
  color: var(--dark-grey);
6837
  }
6838
 
 
 
 
 
6839
  /* Icon while dragging */
6840
  .frmbutton.ui-draggable-dragging,
6841
  .frmbutton.ui-draggable-dragging a {
@@ -6847,12 +6958,13 @@ input[disabled],
6847
  .frmbutton.ui-draggable-dragging a {
6848
  text-decoration: none;
6849
  height: 25px !important;
6850
- max-width: 180px;
6851
  text-align: center;
6852
  display: block !important;
6853
  background-color: var(--primary-hover) !important;
6854
- border-radius: var(--border-radius) !important;
6855
  padding: 5px 20px !important;
 
6856
  }
6857
 
6858
  .frmbutton.ui-draggable-dragging:not(.ui-sortable-helper) a {
@@ -6868,11 +6980,13 @@ input[disabled],
6868
 
6869
  #frm-show-fields .frmbutton.ui-sortable-helper i,
6870
  .frmbutton.ui-sortable-helper span,
6871
- .frmbutton.ui-draggable-dragging i {
 
6872
  color: #fff !important;
6873
  }
6874
 
6875
- .frmbutton.ui-draggable-dragging:not(.ui-sortable-helper) i {
 
6876
  color: var(--dark-grey) !important;
6877
  }
6878
 
@@ -7495,10 +7609,9 @@ button.frm_choose_image_box,
7495
  margin-right: 46%;
7496
  }
7497
 
7498
- .rtl .frm-field-action-icons a {
7499
- float: left;
7500
- margin-right: 15px;
7501
- margin-left: 0;
7502
  }
7503
 
7504
  .rtl #frm_upgrade_modal .dismiss i,
@@ -8069,3 +8182,152 @@ Responsive Design
8069
  max-width: 843px;
8070
  border-radius: 10px;
8071
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3201
  clear: both;
3202
  }
3203
 
3204
+ .frm_inner_field_container > label {
3205
+ text-overflow: ellipsis;
3206
+ overflow: hidden;
3207
+ white-space: nowrap;
3208
+ }
3209
+
3210
+ .frm_field_box:not(.edit_field_type_divider):hover > .frm_inner_field_container > label,
3211
+ .frm_field_box .divider_section_only:hover > .frm_inner_field_container > label,
3212
+ li.ui-state-default.selected > .frm_inner_field_container > label {
3213
+ max-width: calc(100% - 100px);
3214
+ }
3215
+
3216
  .frm_form_settings #op-popup, /* 1Password */
3217
  .frm_form_settings com-1password-op-button,
3218
  .frm_conf_below .frm_conf_field_container .frm_primary_label,
3632
  .frm_field_box {
3633
  padding: 5px;
3634
  min-height: 20px;
3635
+ margin: 10px 0;
3636
  }
3637
 
3638
  .frm_indent_opt, td.frm_indent_opt {
3649
 
3650
  .frm_single_option.ui-sortable-placeholder,
3651
  li.sortable-placeholder {
 
 
3652
  border: none;
 
3653
  margin: 0;
3654
  visibility: visible !important;
3655
+ box-shadow: 2px -10px 0 1px var(--primary-color);
3656
+ }
3657
+
3658
+ #frm_form_editor_container > ul > .frm_single_option.ui-sortable-placeholder,
3659
+ #frm_form_editor_container > ul > li.sortable-placeholder {
3660
+ width: calc(100% - 5px) !important;
3661
+ height: 0 !important;
3662
+ }
3663
+
3664
+ #frm_form_editor_container > ul > li > ul {
3665
+ position: relative;
3666
+ }
3667
+
3668
+ #frm_form_editor_container > ul > li > ul > .frm_single_option.ui-sortable-placeholder,
3669
+ #frm_form_editor_container > ul > li > ul > li.sortable-placeholder,
3670
+ #frm_form_editor_container ul.start_divider > li > ul > li.sortable-placeholder {
3671
+ position: absolute;
3672
+ top: 20px;
3673
+ bottom: 0;
3674
  }
3675
 
3676
  .edit_field_type_divider + li.sortable-placeholder {
3727
  transition: opacity .15s ease-in-out;
3728
  }
3729
 
3730
+ #frm-show-fields .frm-field-settings-open .frm-show-hover {
3731
+ opacity: 1;
3732
+ }
3733
+
3734
  .frm_sortable_field_opts {
3735
  margin-top: 5px;
3736
  }
3795
  opacity: 100;
3796
  }
3797
 
3798
+ li.frm_field_box > ul.frm_grid_container {
3799
+ position: relative;
3800
+ }
3801
+
3802
  .frm_sorting span.ui-icon {
3803
  width: 16px;
3804
  }
5237
  content: '\e606';
5238
  }
5239
 
5240
+ .frm-field-action-icons {
5241
+ text-align: right;
5242
+ position: absolute;
5243
+ top: 2px;
5244
+ right: 2px;
5245
+ max-width: 100%;
5246
+ }
5247
+
5248
+ .frm-field-action-icons .frm-field-id {
5249
+ display: inline-block;
5250
+ overflow: hidden;
5251
+ text-overflow: ellipsis;
5252
+ white-space: nowrap;
5253
+ width: 65px;
5254
+ max-width: calc( 100% - 50px );
5255
+ position: relative;
5256
+ top: 2px;
5257
+ }
5258
+
5259
  .frm-field-action-icons a {
 
 
5260
  font-size: 16px;
5261
  width: 17px;
5262
  }
5263
 
5264
+ .frm-field-action-icons a:hover {
5265
+ text-decoration: none;
5266
+ }
5267
+
5268
+ .frm-field-action-icons .dropdown {
5269
+ display: inline-block;
5270
+ }
5271
+
5272
  .frm-field-action-icons a .frmsvg {
5273
+ color: #9EA9B8 !important;
5274
  font-size: 17px;
5275
  width: 17px;
5276
  height: 17px;
5491
  min-height: 350px;
5492
  padding: 10px 0 35px;
5493
  margin: 0;
5494
+ position: relative;
5495
  }
5496
 
5497
  .frm-has-fields #frm-show-fields {
5498
  min-height: 50px;
5499
  }
5500
 
5501
+ #frm-show-fields .frm6 .frm-dropdown-menu {
5502
+ max-width: unset;
5503
+ min-width: 160px;
5504
+ }
5505
+
5506
+ #frm_builder_page #frm_field_group_controls {
5507
+ position: absolute;
5508
+ background: rgba(65, 153, 253, 0.07);
5509
+ padding: 5px;
5510
+ border-radius: 4px 4px 0 0;
5511
+ text-align: right;
5512
+ box-sizing: border-box;
5513
+ right: 0;
5514
+ top: -28px;
5515
+ height: 28px;
5516
+ z-index: 3; /* section dividers use z-index: 2; position above that to avoid it taking over hover control. */
5517
+ }
5518
+
5519
+ #frm_builder_page #frm_field_group_controls > span:first-child {
5520
+ margin-right: 5px;
5521
+ }
5522
+
5523
+ #frm_builder_page #frm_field_group_controls svg {
5524
+ pointer-events: none;
5525
+ }
5526
+
5527
+ #frm_field_group_controls {
5528
+ visibility: hidden;
5529
+ }
5530
+
5531
+ .frm-field-group-hover-target #frm_field_group_controls {
5532
+ visibility: visible;
5533
+ }
5534
+
5535
+ .frm-field-group-hover-target,
5536
+ .frm-has-open-field-group-popup {
5537
+ background: rgba(65, 153, 253, 0.07);
5538
+ }
5539
+
5540
  .frm_sorting .no_repeat_section li.ui-state-default.edit_field_type_end_divider {
5541
  display: none !important;
5542
  }
5551
  background-color: transparent;
5552
  background-image: none;
5553
  padding: 5px;
5554
+ margin: 0;
5555
  box-shadow: none;
5556
  display: block;
5557
  text-align: inherit !important;
5572
  font-style: normal;
5573
  }
5574
 
 
 
 
 
5575
  .show_repeat_sec {
5576
  display: none;
5577
  }
5953
  padding: 20px 0 0;
5954
  width: calc( 100% - 20px );
5955
  height: 121px;
 
5956
  transition: all 0.4s linear;
5957
  }
5958
 
6732
  font-size: 15px;
6733
  }
6734
 
6735
+ .frm-field-action-icons .frm-dropdown-menu .frm_dropdown_li {
6736
+ padding: 5px 10px;
6737
+ color: rgba(40, 47, 54, 0.85);
6738
+ cursor: pointer;
6739
+ }
6740
+
6741
+ .frm-field-action-icons .frm-dropdown-menu .frm_dropdown_li:hover {
6742
+ background: #F6F7FB;
6743
+ }
6744
+
6745
  .frm_code_list.frm-full-hover a span {
6746
  color: var(--grey);
6747
  float: right;
6943
  color: var(--dark-grey);
6944
  }
6945
 
6946
+ .frmbutton.ui-draggable-dragging {
6947
+ width: auto !important;
6948
+ }
6949
+
6950
  /* Icon while dragging */
6951
  .frmbutton.ui-draggable-dragging,
6952
  .frmbutton.ui-draggable-dragging a {
6958
  .frmbutton.ui-draggable-dragging a {
6959
  text-decoration: none;
6960
  height: 25px !important;
6961
+ width: 180px;
6962
  text-align: center;
6963
  display: block !important;
6964
  background-color: var(--primary-hover) !important;
6965
+ border-radius: 35px !important;
6966
  padding: 5px 20px !important;
6967
+ color: #fff !important;
6968
  }
6969
 
6970
  .frmbutton.ui-draggable-dragging:not(.ui-sortable-helper) a {
6980
 
6981
  #frm-show-fields .frmbutton.ui-sortable-helper i,
6982
  .frmbutton.ui-sortable-helper span,
6983
+ .frmbutton.ui-draggable-dragging i,
6984
+ .frmbutton.ui-draggable-dragging .frmsvg {
6985
  color: #fff !important;
6986
  }
6987
 
6988
+ .frmbutton.ui-draggable-dragging:not(.ui-sortable-helper) i,
6989
+ .frmbutton.ui-draggable-dragging:not(.ui-sortable-helper) .frmsvg {
6990
  color: var(--dark-grey) !important;
6991
  }
6992
 
7609
  margin-right: 46%;
7610
  }
7611
 
7612
+ .rtl .frm-field-action-icons {
7613
+ right: unset;
7614
+ left: 2px;
 
7615
  }
7616
 
7617
  .rtl #frm_upgrade_modal .dismiss i,
8182
  max-width: 843px;
8183
  border-radius: 10px;
8184
  }
8185
+
8186
+ #frm_field_group_popup {
8187
+ position: absolute;
8188
+ top: 0;
8189
+ right: 0;
8190
+ width: 366px;
8191
+ height: 231px;
8192
+ border-radius: 8px;
8193
+ background: #fff;
8194
+ box-shadow: 0px 20px 48px rgba(40, 47, 54, 0.15);
8195
+ z-index: 3;
8196
+ box-sizing: border-box;
8197
+ padding: 24px 0;
8198
+ text-align: left;
8199
+ }
8200
+
8201
+ .rtl #frm_field_group_popup {
8202
+ text-align: right;
8203
+ }
8204
+
8205
+ .frm-merge-fields-into-row #frm_field_group_popup {
8206
+ top: unset;
8207
+ bottom: 50px;
8208
+ height: 196px;
8209
+ }
8210
+
8211
+ .frm-merge-fields-into-row #frm_field_group_popup .frm-break-field-group {
8212
+ display: none;
8213
+ }
8214
+
8215
+ .frm-merge-fields-into-row > a {
8216
+ color: rgba(40, 47, 54, 0.85);
8217
+ position: relative;
8218
+ top: 2px;
8219
+ }
8220
+
8221
+ .frm-row-layout-title {
8222
+ font-weight: 600;
8223
+ font-size: 11px;
8224
+ line-height: 15px;
8225
+ text-transform: uppercase;
8226
+ color: rgba(40, 47, 54, 0.45);
8227
+ margin-bottom: 40px;
8228
+ }
8229
+
8230
+ .frm-row-layout-option {
8231
+ background: #F6F7FB;
8232
+ box-sizing: border-box;
8233
+ border-radius: 2px;
8234
+ padding: 18px 11px;
8235
+ }
8236
+
8237
+ #frm_field_group_popup .frm-row-layout-option .frm_grid_container {
8238
+ grid-gap: 0 5%;
8239
+ grid-template-columns: repeat(12, auto);
8240
+ }
8241
+
8242
+ #frm_field_group_popup .frm-row-layout-option {
8243
+ cursor: pointer;
8244
+ }
8245
+
8246
+ #frm_field_group_popup .frm-row-layout-option.frm-active-row-layout,
8247
+ #frm_field_group_popup .frm-row-layout-option:hover {
8248
+ outline: 1px solid #4199FD;
8249
+ }
8250
+
8251
+ .frm-custom-field-group-layout,
8252
+ .frm-break-field-group {
8253
+ padding: 5px 15px;
8254
+ cursor: pointer;
8255
+ font-size: 14px;
8256
+ line-height: 19px;
8257
+ color: rgba(40, 47, 54, 0.85);
8258
+ }
8259
+
8260
+ .frm-custom-field-group-layout:hover,
8261
+ .frm-break-field-group:hover {
8262
+ background-color: #f5f5f5f5;
8263
+ }
8264
+
8265
+ .frm-custom-field-group-layout svg,
8266
+ .frm-break-field-group svg {
8267
+ margin-right: 5px;
8268
+ }
8269
+
8270
+ .frm-selected-field-group {
8271
+ background: rgba(65, 153, 253, 0.07);
8272
+ position: relative;
8273
+ }
8274
+
8275
+ #frm_field_multiselect_popup {