PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / trunk
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More vtrunk
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
← All changes | classes/helpers/FrmFormsListHelper.php +474 -90 6.5.2 → trunk View file →
@@ -3,16 +3,28 @@
3 3 die( 'You are not allowed to call this page directly.' );
4 4 }
5 5
6 6 class FrmFormsListHelper extends FrmListHelper {
7 +
8 + /**
9 + * @var string
10 + */
7 11 public $status = '';
8 12
9 13 public $total_items = 0;
10 14
15 + /**
16 + * @param array $args
17 + */
11 18 public function __construct( $args ) {
12 19 $this->status = self::get_param( array( 'param' => 'form_type' ) );
13 20
14 21 parent::__construct( $args );
22 + $this->screen->set_screen_reader_content(
23 + array(
24 + 'heading_list' => esc_html__( 'Forms list', 'formidable' ),
25 + )
26 + );
15 27 }
16 28
17 29 /**
18 30 * @return void
@@ -17,19 +29,20 @@
17 29 /**
18 30 * @return void
19 31 */
20 32 public function prepare_items() {
21 - global $wpdb, $per_page, $mode;
33 + global $per_page, $mode;
22 34
23 35 $page = $this->get_pagenum();
24 36 $per_page = $this->get_items_per_page( 'formidable_page_formidable_per_page' );
25 37
26 - $mode = self::get_param(
38 + $mode = self::get_param(
27 39 array(
28 40 'param' => 'mode',
29 41 'default' => 'list',
30 42 )
31 43 );
44 +
32 45 $orderby = self::get_param(
33 46 array(
34 47 'param' => 'orderby',
35 48 'default' => 'name',
@@ -40,9 +53,12 @@
40 53 'param' => 'order',
41 54 'default' => 'ASC',
42 55 )
43 56 );
44 - $start = self::get_param(
57 +
58 + FrmAppController::apply_saved_sort_preference( $orderby, $order );
59 +
60 + $start = self::get_param(
45 61 array(
46 62 'param' => 'start',
47 63 'default' => ( $page - 1 ) * $per_page,
48 64 )
@@ -74,12 +90,14 @@
74 90 'param' => 's',
75 91 'sanitize' => 'sanitize_text_field',
76 92 )
77 93 );
78 - if ( $s != '' ) {
94 +
95 + if ( $s !== '' ) {
79 96 preg_match_all( '/".*?("|$)|((?<=[\\s",+])|^)[^\\s",+]+/', $s, $matches );
80 97 $search_terms = array_map( 'trim', $matches[0] );
81 - foreach ( (array) $search_terms as $term ) {
98 +
99 + foreach ( $search_terms as $term ) {
82 100 $s_query[] = array(
83 101 'or' => true,
84 102 'name LIKE' => $term,
85 103 'description LIKE' => $term,
@@ -90,10 +108,10 @@
90 108 unset( $term );
91 109 }
92 110 }
93 111
94 - $this->items = FrmForm::getAll( $s_query, $orderby . ' ' . $order, $start . ',' . $per_page );
95 - $total_items = FrmDb::get_count( 'frm_forms', $s_query );
112 + $this->items = FrmForm::getAll( $s_query, $orderby . ' ' . $order, $start . ',' . $per_page );
113 + $total_items = FrmDb::get_count( 'frm_forms', $s_query );
96 114 $this->total_items = $total_items;
97 115
98 116 $this->set_pagination_args(
99 117 array(
@@ -106,16 +124,42 @@
106 124 /**
107 125 * @return void
108 126 */
109 127 public function no_items() {
128 + $s = self::get_param(
129 + array(
130 + 'param' => 's',
131 + 'sanitize' => 'sanitize_text_field',
132 + )
133 + );
134 +
135 + if ( $s !== '' ) {
136 + $current_url = set_url_scheme(
137 + 'http://' . FrmAppHelper::get_server_value( 'HTTP_HOST' ) . FrmAppHelper::get_server_value( 'REQUEST_URI' )
138 + );
139 + $clear_url = remove_query_arg( 's', $current_url );
140 +
141 + echo '<p>';
142 + printf(
143 + /* translators: %1$s: Start link HTML, %2$s: End link HTML */
144 + esc_html__( 'No forms match your search. %1$sClear search%2$s', 'formidable' ),
145 + '<a href="' . esc_url( $clear_url ) . '">',
146 + '</a>'
147 + );
148 + echo '</p>';
149 + return;
150 + }
151 +
110 152 if ( $this->status === 'trash' ) {
111 153 echo '<p>';
112 154 esc_html_e( 'No forms found in the trash.', 'formidable' );
155 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
113 156 ?>
114 157 <a href="<?php echo esc_url( admin_url( 'admin.php?page=formidable' ) ); ?>">
115 158 <?php esc_html_e( 'See all forms.', 'formidable' ); ?>
116 159 </a>
117 160 <?php
161 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
118 162 echo '</p>';
119 163 } else {
120 164 $title = __( 'No Forms Found', 'formidable' );
121 165 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/_no_forms.php';
@@ -124,9 +168,9 @@
124 168
125 169 public function get_bulk_actions() {
126 170 $actions = array();
127 171
128 - if ( 'trash' == $this->status ) {
172 + if ( 'trash' === $this->status ) {
129 173 if ( current_user_can( 'frm_edit_forms' ) ) {
130 174 $actions['bulk_untrash'] = __( 'Restore', 'formidable' );
131 175 }
132 176
@@ -142,26 +186,34 @@
142 186 return $actions;
143 187 }
144 188
145 189 /**
190 + * @param string $which
191 + *
146 192 * @return void
147 193 */
148 194 public function extra_tablenav( $which ) {
149 - if ( 'top' != $which ) {
195 + if ( 'top' !== $which ) {
150 196 return;
151 197 }
152 198
153 - if ( 'trash' == $this->status && current_user_can( 'frm_delete_forms' ) ) {
154 - ?>
155 - <div class="alignleft actions frm_visible_overflow">
156 - <?php submit_button( __( 'Empty Trash', 'formidable' ), 'apply', 'delete_all', false ); ?>
157 - </div>
158 - <?php
199 + if ( 'trash' !== $this->status || ! current_user_can( 'frm_delete_forms' ) ) {
200 + return;
159 201 }
202 +
203 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
204 + ?>
205 + <div class="alignleft actions frm_visible_overflow">
206 + <?php submit_button( __( 'Empty Trash', 'formidable' ), 'apply', 'delete_all', false ); ?>
207 + </div>
208 + <?php
209 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
160 210 }
161 211
212 + /**
213 + * @return array
214 + */
162 215 public function get_views() {
163 -
164 216 $statuses = array(
165 217 'published' => __( 'My Forms', 'formidable' ),
166 218 'draft' => __( 'Drafts', 'formidable' ),
167 219 'trash' => __( 'Trash', 'formidable' ),
@@ -182,18 +234,13 @@
182 234 )
183 235 );
184 236
185 237 foreach ( $statuses as $status => $name ) {
238 + $class = $status == $form_type ? ' class="current"' : ''; // phpcs:ignore Universal.Operators.StrictComparisons
186 239
187 - if ( $status == $form_type ) {
188 - $class = ' class="current"';
189 - } else {
190 - $class = '';
191 - }
192 -
193 240 if ( $counts->{$status} || 'draft' !== $status ) {
194 241 /* translators: %1$s: Status, %2$s: Number of items */
195 - $links[ $status ] = '<a href="' . esc_url( '?page=formidable&form_type=' . $status ) . '" ' . $class . '>' . sprintf( __( '%1$s <span class="count">(%2$s)</span>', 'formidable' ), $name, number_format_i18n( $counts->{$status} ) ) . '</a>';
242 + $links[ $status ] = '<a href="' . esc_url( '?page=formidable&form_type=' . $status ) . '" ' . $class . '>' . sprintf( __( '%1$s <span class="count">(%2$s)</span>', 'formidable' ), $name, number_format_i18n( $counts->{$status} ) ) . '</a>'; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
196 243 }
197 244
198 245 unset( $status, $name );
199 246 }
@@ -201,27 +248,15 @@
201 248 return $links;
202 249 }
203 250
204 251 /**
205 - * @return void
206 - */
207 - public function pagination( $which ) {
208 - global $mode;
209 -
210 - parent::pagination( $which );
211 -
212 - if ( 'top' === $which ) {
213 - $this->view_switcher( $mode );
214 - }
215 - }
216 -
217 - /**
218 252 * @param stdClass $item
219 253 * @param string $style
254 + *
220 255 * @return string
221 256 */
222 257 public function single_row( $item, $style = '' ) {
223 - global $frm_vars, $mode;
258 + global $mode;
224 259
225 260 // Set up the hover actions for this user
226 261 $actions = array();
227 262 $edit_link = FrmForm::get_edit_link( $item->id );
@@ -228,27 +263,25 @@
228 263
229 264 $this->get_actions( $actions, $item, $edit_link );
230 265
231 266 $action_links = $this->row_actions( $actions );
267 + $checkbox = $this->get_row_checkbox( $item );
268 + $r = '<tr id="item-action-' . absint( $item->id ) . '"' . $style . '>';
232 269
233 - // Set up the checkbox ( because the user is editable, otherwise its empty )
234 - $checkbox = '<input type="checkbox" name="item-action[]" id="cb-item-action-' . absint( $item->id ) . '" value="' . esc_attr( $item->id ) . '" />';
235 -
236 - $r = '<tr id="item-action-' . absint( $item->id ) . '"' . $style . '>';
237 -
238 270 list( $columns, $hidden ) = $this->get_column_info();
239 271
240 272 $format = 'Y/m/d';
241 - if ( 'list' != $mode ) {
273 +
274 + if ( 'list' !== $mode ) {
242 275 $format .= ' \<\b\r \/\> g:i:s a';
243 276 }
244 277
245 278 foreach ( $columns as $column_name => $column_display_name ) {
246 - $class = $column_name . ' column-' . $column_name . ( 'name' == $column_name ? ' post-title page-title column-title' : '' );
279 + $class = $column_name . ' column-' . $column_name . ( 'name' === $column_name ? ' post-title page-title column-title' : '' );
280 + $style = '';
247 281
248 - $style = '';
249 - if ( in_array( $column_name, $hidden ) ) {
250 - $class .= ' frm_hidden';
282 + if ( in_array( $column_name, $hidden, true ) ) {
283 + $class .= ' hidden';
251 284 }
252 285
253 286 if ( $column_name === 'name' ) {
254 287 $class .= ' column-primary';
@@ -253,11 +286,10 @@
253 286 if ( $column_name === 'name' ) {
254 287 $class .= ' column-primary';
255 288 }
256 289
257 - $class = 'class="' . esc_attr( $class ) . '"';
258 - $data_colname = ' data-colname="' . esc_attr( $column_display_name ) . '"';
259 - $attributes = $class . $style . $data_colname;
290 + $class = 'class="' . esc_attr( $class ) . '"';
291 + $attributes = $class . $style . $this->get_column_data_attr( $column_name, $column_display_name );
260 292
261 293 switch ( $column_name ) {
262 294 case 'cb':
263 295 $r .= '<th scope="row" class="check-column">' . $checkbox . '</th>';
@@ -266,9 +298,9 @@
266 298 case 'form_key':
267 299 $val = $item->{$column_name};
268 300 break;
269 301 case 'name':
270 - $val = $this->get_form_name( $item, $actions, $edit_link, $mode );
302 + $val = $this->get_form_name( $item, $actions, $edit_link, $mode );
271 303 $val .= $action_links;
272 304 break;
273 305 case 'created_at':
274 306 $date = gmdate( $format, strtotime( $item->created_at ) );
@@ -274,21 +306,9 @@
274 306 $date = gmdate( $format, strtotime( $item->created_at ) );
275 307 $val = '<abbr title="' . esc_attr( gmdate( 'Y/m/d g:i:s A', strtotime( $item->created_at ) ) ) . '">' . $date . '</abbr>';
276 308 break;
277 309 case 'entries':
278 - if ( isset( $item->options['no_save'] ) && $item->options['no_save'] ) {
279 - $val = FrmAppHelper::icon_by_class(
280 - 'frmfont frm_forbid_icon frm_bstooltip',
281 - array(
282 - 'title' => __( 'Saving entries is disabled for this form', 'formidable' ),
283 - 'echo' => false,
284 - )
285 - );
286 - } else {
287 - $text = FrmEntry::getRecordCount( $item->id );
288 - $val = current_user_can( 'frm_view_entries' ) ? '<a href="' . esc_url( admin_url( 'admin.php?page=formidable-entries&form=' . $item->id ) ) . '">' . $text . '</a>' : $text;
289 - unset( $text );
290 - }
310 + $val = $this->get_entries_column_value( $item );
291 311 break;
292 312 default:
293 313 if ( method_exists( $this, 'column_' . $column_name ) ) {
294 314 $val = call_user_func( array( $this, 'column_' . $column_name ), $item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -293,9 +313,9 @@
293 313 if ( method_exists( $this, 'column_' . $column_name ) ) {
294 314 $val = call_user_func( array( $this, 'column_' . $column_name ), $item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
295 315 }
296 316 break;
297 - }
317 + }//end switch
298 318
299 319 if ( isset( $val ) ) {
300 320 $r .= "<td $attributes>";
301 321 $r .= $val;
@@ -301,30 +321,81 @@
301 321 $r .= $val;
302 322 $r .= '</td>';
303 323 }
304 324 unset( $val );
325 + }//end foreach
326 + return $r . '</tr>';
327 + }
328 +
329 + /**
330 + * @param string $column_name
331 + * @param string $column_display_name
332 + *
333 + * @return string
334 + */
335 + private function get_column_data_attr( $column_name, $column_display_name ) {
336 + if ( 'settings' === $column_name ) {
337 + return ' data-colname="' . esc_attr( trim( strip_tags( $column_display_name ) ) ) . '"';
305 338 }
306 - $r .= '</tr>';
339 + return ' data-colname="' . esc_attr( $column_display_name ) . '"';
340 + }
307 341
308 - return $r;
342 + /**
343 + * @param object $item
344 + *
345 + * @return string
346 + */
347 + private function get_entries_column_value( $item ) {
348 + if ( ! empty( $item->options['no_save'] ) ) {
349 + return (string) FrmAppHelper::icon_by_class(
350 + 'frmfont frm_forbid_icon frm_bstooltip',
351 + array(
352 + 'title' => __( 'Saving entries is disabled for this form', 'formidable' ),
353 + 'echo' => false,
354 + )
355 + );
356 + }
357 +
358 + $text = intval( FrmEntry::getRecordCount( $item->id ) );
359 + return current_user_can( 'frm_view_entries' ) ? '<a href="' . esc_url( admin_url( 'admin.php?page=formidable-entries&form=' . $item->id ) ) . '">' . $text . '</a>' : (string) $text; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
309 360 }
310 361
311 362 /**
363 + * @param object $item
364 + *
365 + * @return string
366 + */
367 + private function get_row_checkbox( $item ) {
368 + // Set up the checkbox (because the user is editable, otherwise it's empty).
369 + $checkbox = '<input type="checkbox" name="item-action[]" id="cb-item-action-' . absint( $item->id ) . '" value="' . esc_attr( $item->id ) . '" />';
370 + $checkbox_label_text = sprintf(
371 + // translators: Form title
372 + __( 'Select %s', 'formidable' ),
373 + ! empty( $item->name ) ? $item->name : FrmFormsHelper::get_no_title_text()
374 + );
375 +
376 + return $checkbox . '<label for="cb-item-action-' . absint( $item->id ) . '"><span class="screen-reader-text">' . esc_html( $checkbox_label_text ) . '</span></label>';
377 + }
378 +
379 + /**
312 380 * Get the HTML for the Actions column in the form list.
313 381 * This includes multiple icons for triggering the embed modal, the visual styler, and an active landing page.
314 382 *
315 383 * @since 6.0
384 + * @deprecated 6.32 We moved these actions to other places. This column will show if there is a filter added to the hook.
316 385 *
317 386 * @param stdClass $form
387 + *
318 388 * @return string
319 389 */
320 390 protected function column_shortcode( $form ) {
321 - $val = '<a href="#" class="frm-embed-form" role="button" aria-label="' . esc_html__( 'Embed Form', 'formidable' ) . '">' . FrmAppHelper::icon_by_class( 'frmfont frm_code_icon', array( 'echo' => false ) ) . '</a>';
391 + $val = '<a href="#" class="frm-embed-form" role="button" aria-label="' . esc_attr__( 'Embed Form', 'formidable' ) . '">' . FrmAppHelper::icon_by_class( 'frmfont frm_code_icon', array( 'echo' => false ) ) . '</a>'; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
322 392 $val .= $this->column_style( $form );
393 + $val .= $this->column_views( $form );
323 394 $val = apply_filters( 'frm_form_list_actions', $val, array( 'form' => $form ) );
324 - $val = str_replace( '&nbsp;', '', $val ); // Remove the space hard coded in Landing pages.
325 - $val = '<div>' . $val . '</div>';
326 - return $val;
395 + // Remove the space hard coded in Landing pages.
396 + $val = str_replace( '&nbsp;', '', $val );
397 + return '<div>' . $val . '</div>';
327 398 }
328 399
329 400 /**
330 401 * Get the HTML for the Style column in the form list.
@@ -331,12 +402,13 @@
331 402 *
332 403 * @since 6.0
333 404 *
334 405 * @param stdClass $form
406 + *
335 407 * @return string
336 408 */
337 409 protected function column_style( $form ) {
338 - $style_setting = isset( $form->options['custom_style'] ) ? $form->options['custom_style'] : '';
410 + $style_setting = $form->options['custom_style'] ?? '';
339 411 $frm_settings = FrmAppHelper::get_settings();
340 412
341 413 if ( $style_setting === '0' || 'none' === $frm_settings->load_style ) {
342 414 // Don't show a link if styling is off.
@@ -343,8 +415,9 @@
343 415 return '';
344 416 }
345 417
346 418 $style = FrmStylesController::get_form_style( $form );
419 +
347 420 if ( ! $style ) {
348 421 // Do a second pass to avoid null values.
349 422 $frm_style = new FrmStyle( 'default' );
350 423 $style = $frm_style->get_one();
@@ -350,60 +423,108 @@
350 423 $style = $frm_style->get_one();
351 424 }
352 425
353 426 $href = FrmStylesHelper::get_edit_url( $style, $form->id );
354 - return '<a href="' . esc_url( $href ) . '" title="' . esc_attr( $style->post_title ) . '">' . FrmAppHelper::icon_by_class( 'frmfont frm_pallet_icon', array( 'echo' => false ) ) . '</a>';
427 + return '<a href="' . esc_url( $href ) . '" title="' . esc_attr( $style->post_title ) . '">' . FrmAppHelper::icon_by_class( 'frmfont frm_pallet_icon', array( 'echo' => false ) ) . '</a>'; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
355 428 }
356 429
357 430 /**
431 + * Generate the HTML for the form Views page.
432 + *
433 + * @since 6.19
434 + *
435 + * @param stdClass $form Form object.
436 + *
437 + * @return string
438 + */
439 + protected function column_views( $form ) {
440 + $attributes = array(
441 + 'href' => admin_url( 'admin.php?page=formidable-views&form=' . absint( $form->id ) . '&show_nav=1' ),
442 + 'title' => __( 'Link to list of all views for this form.', 'formidable' ),
443 + 'target' => '_blank',
444 + );
445 +
446 + if ( class_exists( 'FrmViewsDisplay' ) ) {
447 + $view_ids = FrmViewsDisplay::get_display_ids_by_form( $form->id );
448 + $view_count = $view_ids ? count( $view_ids ) : 0;
449 + } else {
450 + $view_count = 0;
451 + }
452 +
453 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
454 + return '<a ' . FrmAppHelper::array_to_html_params( $attributes ) . '>
455 + ' . intval( $view_count ) .
456 + '</a>';
457 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
458 + }
459 +
460 + /**
461 + * Get the HTML for the Settings column in the form list.
462 + *
463 + * @since 6.32
464 + *
465 + * @return string
466 + */
467 + protected function column_settings() {
468 + return '&nbsp;';
469 + }
470 +
471 + /**
472 + * @param array $actions
473 + * @param object $item
358 474 * @param string $edit_link
359 - * @param array $actions
360 - * @param stdClass $item
361 475 *
362 476 * @return void
363 477 */
364 478 private function get_actions( &$actions, $item, $edit_link ) {
365 479 $new_actions = FrmFormsHelper::get_action_links( $item->id, $item );
480 +
366 481 foreach ( $new_actions as $link => $action ) {
367 482 $new_actions[ $link ] = FrmFormsHelper::format_link_html( $action, 'short' );
368 483 }
369 484
370 - if ( 'trash' == $this->status ) {
485 + if ( 'trash' === $this->status ) {
371 486 $actions = $new_actions;
372 -
373 487 return;
374 488 }
375 489
376 490 if ( current_user_can( 'frm_edit_forms' ) ) {
377 - $actions['frm_edit'] = '<a href="' . esc_url( $edit_link ) . '">' . __( 'Edit', 'formidable' ) . '</a>';
378 - $actions['frm_settings'] = '<a href="' . esc_url( '?page=formidable&frm_action=settings&id=' . $item->id ) . '">' . __( 'Settings', 'formidable' ) . '</a>';
491 + $actions['frm_edit'] = '<a href="' . esc_url( $edit_link ) . '">' . esc_html__( 'Edit', 'formidable' ) . '</a>';
492 + $actions['frm_settings'] = '<a href="' . esc_url( '?page=formidable&frm_action=settings&id=' . $item->id ) . '">' . esc_html__( 'Settings', 'formidable' ) . '</a>';
379 493 }
380 494
381 - $actions = array_merge( $actions, $new_actions );
382 - $actions['view'] = '<a href="' . esc_url( FrmFormsHelper::get_direct_link( $item->form_key, $item ) ) . '" target="_blank">' . __( 'Preview', 'formidable' ) . '</a>';
495 + $actions = array_merge( $actions, $new_actions );
496 + $actions['embed'] = '<a href="#" class="frm-embed-form" role="button" aria-label="' . esc_attr__( 'Embed Form', 'formidable' ) . '">' . esc_html__( 'Embed', 'formidable' ) . '</a>'; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
497 + $actions['view'] = '<a href="' . esc_url( FrmFormsHelper::get_direct_link( $item->form_key, $item ) ) . '" target="_blank">' . esc_html__( 'Preview', 'formidable' ) . '</a>'; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
383 498 }
384 499
385 500 /**
501 + * @param object $item
502 + * @param array $actions
386 503 * @param string $edit_link
387 - * @param stdClass $item
504 + * @param string $mode
388 505 *
389 506 * @return string
390 507 */
391 508 private function get_form_name( $item, $actions, $edit_link, $mode = 'list' ) {
392 509 $form_name = $item->name;
393 - if ( trim( $form_name ) == '' ) {
394 - $form_name = __( '(no title)', 'formidable' );
510 +
511 + if ( is_null( $form_name ) || trim( $form_name ) === '' ) {
512 + $form_name = FrmFormsHelper::get_no_title_text();
395 513 }
514 +
396 515 $form_name = FrmAppHelper::kses( $form_name );
397 - if ( 'excerpt' != $mode ) {
516 +
517 + if ( 'excerpt' !== $mode ) {
398 518 $form_name = FrmAppHelper::truncate( $form_name, 50 );
399 519 }
400 520
401 521 $val = '<strong>';
402 - if ( 'trash' == $this->status ) {
522 +
523 + if ( 'trash' === $this->status ) {
403 524 $val .= $form_name;
404 525 } else {
405 - $val .= '<a href="' . esc_url( isset( $actions['frm_edit'] ) ? $edit_link : FrmFormsHelper::get_direct_link( $item->form_key, $item ) ) . '" class="row-title">' . FrmAppHelper::kses( $form_name ) . '</a> ';
526 + $val .= '<a href="' . esc_url( isset( $actions['frm_edit'] ) ? $edit_link : FrmFormsHelper::get_direct_link( $item->form_key, $item ) ) . '" class="row-title">' . FrmAppHelper::kses( $form_name ) . '</a> '; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
406 527 }
407 528
408 529 $this->add_draft_label( $item, $val );
409 530 $val .= '</strong>';
@@ -413,19 +534,21 @@
413 534 return $val;
414 535 }
415 536
416 537 /**
538 + * @param object $item
417 539 * @param string $val
418 540 *
419 541 * @return void
420 542 */
421 543 private function add_draft_label( $item, &$val ) {
422 - if ( 'draft' == $item->status && 'draft' != $this->status ) {
423 - $val .= ' - <span class="post-state">' . __( 'Draft', 'formidable' ) . '</span>';
544 + if ( 'draft' === $item->status && 'draft' !== $this->status ) {
545 + $val .= ' - <span class="post-state">' . esc_html__( 'Draft', 'formidable' ) . '</span>';
424 546 }
425 547 }
426 548
427 549 /**
550 + * @param object $item
428 551 * @param string $val
429 552 *
430 553 * @return void
431 554 */
@@ -430,9 +553,10 @@
430 553 * @return void
431 554 */
432 555 private function add_form_description( $item, &$val ) {
433 556 global $mode;
434 - if ( 'excerpt' == $mode ) {
557 +
558 + if ( 'excerpt' === $mode && ! is_null( $item->description ) ) {
435 559 $val .= FrmAppHelper::truncate( strip_tags( $item->description ), 50 );
436 560 }
437 561 }
438 562
@@ -440,6 +564,266 @@
440 564 * @return string
441 565 */
442 566 protected function confirm_bulk_delete() {
443 567 return __( 'ALL selected forms and their entries will be permanently deleted. Want to proceed?', 'formidable' );
568 + }
569 +
570 + /**
571 + * @param stdClass $form
572 + *
573 + * @return string
574 + */
575 + public function column_embeds( $form ) {
576 + if ( $this->column_is_hidden( 'embeds' ) ) {
577 + // Locating embeds means scanning post_content, so skip it when the column is hidden.
578 + return '';
579 + }
580 +
581 + $posts = $this->get_posts_contain_form( $form );
582 +
583 + if ( ! $posts ) {
584 + return '<span class="frm-forms-list-embeds-zero">0</span>';
585 + }
586 +
587 + return FrmAppHelper::clip(
588 + function () use ( $posts ) {
589 + ?>
590 + <a href="#" class="frm-forms-list-embeds-btn" data-posts="<?php echo esc_attr( wp_json_encode( $posts ) ); ?>">
591 + <?php
592 + FrmAppHelper::icon_by_class( 'frmfont frm_arrowdown6_icon' );
593 + echo intval( count( $posts ) );
594 + ?>
595 + </a>
596 + <?php
597 + }
598 + );
599 + }
600 +
601 + /**
602 + * Checks if a column is hidden with Screen Options.
603 + *
604 + * @since 6.35
605 + *
606 + * @param string $column_name Column name.
607 + *
608 + * @return bool
609 + */
610 + private function column_is_hidden( $column_name ) {
611 + $column_info = $this->get_column_info();
612 +
613 + if ( ! isset( $column_info[1] ) || ! is_array( $column_info[1] ) ) {
614 + return false;
615 + }
616 +
617 + return in_array( $column_name, $column_info[1], true );
618 + }
619 +
620 + /**
621 + * Gets posts or pages that contain the form shortcode.
622 + *
623 + * @since 6.32
624 + *
625 + * @param stdClass $form Form object.
626 + *
627 + * @return array
628 + */
629 + private function get_posts_contain_form( $form ) {
630 + $cached_posts = FrmFormEmbedsHelper::get_cached_posts();
631 +
632 + if ( ! isset( $cached_posts[ $form->id ] ) || ! is_array( $cached_posts[ $form->id ] ) ) {
633 + // A single scan covers every form listed on this page, not just this one.
634 + $cached_posts = $this->fill_embed_posts_cache( $form );
635 + }
636 +
637 + if ( ! isset( $cached_posts[ $form->id ] ) || ! is_array( $cached_posts[ $form->id ] ) ) {
638 + return array();
639 + }
640 +
641 + // Links and title fallbacks are derived at render time, not stored. get_edit_post_link()
642 + // depends on the current user, so caching it in a shared transient would hand one user's
643 + // edit link to another, and a permalink cached now goes stale on any slug change.
644 + return FrmFormEmbedsHelper::prepare_posts( $cached_posts[ $form->id ] );
645 + }
646 +
647 + /**
648 + * Scans for embeds once and caches the result for every form listed on the current page.
649 + *
650 + * @since 6.35
651 + *
652 + * @param stdClass $form The form whose column is currently rendering.
653 + *
654 + * @return array Embed posts keyed by form ID.
655 + */
656 + private function fill_embed_posts_cache( $form ) {
657 + $cached_posts = FrmFormEmbedsHelper::get_cached_posts();
658 + $search_map = array();
659 + $forms = array();
660 +
661 + foreach ( $this->get_forms_to_scan( $form ) as $form_id => $form_to_scan ) {
662 + if ( isset( $cached_posts[ $form_id ] ) && is_array( $cached_posts[ $form_id ] ) ) {
663 + continue;
664 + }
665 +
666 + $search_map[ $form_id ] = $this->get_search_strings_for_form( $form_id );
667 + $forms[ $form_id ] = $form_to_scan;
668 + }
669 +
670 + if ( ! $forms ) {
671 + return $cached_posts;
672 + }
673 +
674 + $matched = FrmFormEmbedsHelper::match_candidate_posts( $search_map );
675 +
676 + foreach ( $forms as $form_id => $form_to_scan ) {
677 + $posts = $this->filter_embed_posts( $matched[ $form_id ], $form_to_scan );
678 + $cached_posts[ $form_id ] = FrmFormEmbedsHelper::slim_posts( $posts );
679 + }
680 +
681 + FrmFormEmbedsHelper::save_cached_posts( $cached_posts );
682 +
683 + return $cached_posts;
684 + }
685 +
686 + /**
687 + * Gets every form rendered on the current list page, keyed by form ID.
688 + *
689 + * @since 6.35
690 + *
691 + * @param stdClass $form The form whose column is currently rendering.
692 + *
693 + * @return array
694 + */
695 + private function get_forms_to_scan( $form ) {
696 + $forms = array( $form->id => $form );
697 +
698 + if ( ! is_array( $this->items ) ) {
699 + return $forms;
700 + }
701 +
702 + foreach ( $this->items as $item ) {
703 + if ( is_object( $item ) && isset( $item->id ) ) {
704 + $forms[ $item->id ] = $item;
705 + }
706 + }
707 +
708 + return $forms;
709 + }
710 +
711 + /**
712 + * Applies the frm_get_posts_contain_form filter to a form's embed posts.
713 + *
714 + * @since 6.35
715 + *
716 + * @param array $posts Posts that embed the form.
717 + * @param stdClass $form Form object.
718 + *
719 + * @return array
720 + */
721 + private function filter_embed_posts( $posts, $form ) {
722 + /**
723 + * @since 6.32
724 + *
725 + * @param stdClass[] $posts
726 + * @param array $args
727 + */
728 + $filtered_posts = apply_filters( 'frm_get_posts_contain_form', $posts, compact( 'form' ) );
729 +
730 + if ( ! is_array( $filtered_posts ) ) {
731 + _doing_it_wrong( 'frm_get_posts_contain_form', 'Filter should return an array.', '6.32' );
732 + return $posts;
733 + }
734 +
735 + return $filtered_posts;
736 + }
737 +
738 + /**
739 + * Gets search strings for a form inside a post.
740 + *
741 + * @since 6.32
742 + *
743 + * @param int $form_id Form ID.
744 + *
745 + * @return string[]
746 + */
747 + protected function get_search_strings_for_form( $form_id ) {
748 + return $this->get_base_search_strings_for_form( $form_id );
749 + }
750 +
751 + /**
752 + * Gets the base search strings for a form inside a post.
753 + *
754 + * @since 6.32
755 + *
756 + * @param int $form_id Form ID.
757 + *
758 + * @return string[]
759 + */
760 + protected function get_base_search_strings_for_form( $form_id ) {
761 + $strings = array(
762 + '<!-- wp:formidable/simple-form {"formId":"' . $form_id . '"',
763 + );
764 +
765 + // The shortcode renders a form from its key just as happily as from its ID, in either
766 + // attribute, so all four spellings have to be searched or those pages never count.
767 + $identifiers = array( $form_id );
768 + $form_key = FrmForm::get_key_by_id( $form_id );
769 +
770 + if ( $form_key && $form_key !== (string) $form_id ) {
771 + $identifiers[] = $form_key;
772 + }
773 +
774 + foreach ( $identifiers as $identifier ) {
775 + foreach ( array( 'id', 'key' ) as $att ) {
776 + $strings[] = '[formidable ' . $att . '=' . $identifier . ']';
777 + $strings[] = '[formidable ' . $att . '=' . $identifier . ' ';
778 + $strings[] = '[formidable ' . $att . '="' . $identifier . '"';
779 + $strings[] = '[formidable ' . $att . "='" . $identifier . "'";
780 + }
781 + }
782 +
783 + return $strings;
784 + }
785 +
786 + /**
787 + * Maybe clear the embed posts transient when a post is inserted.
788 + *
789 + * @since 6.32
790 + *
791 + * @param int $post_id Post ID.
792 + * @param WP_Post $post Post object.
793 + * @param bool $update True when an existing post was updated rather than created.
794 + *
795 + * @return void
796 + */
797 + public static function maybe_clear_embed_posts_transient( $post_id, $post, $update = false ) {
798 + FrmFormEmbedsHelper::maybe_clear_on_insert( $post_id, $post, $update );
799 + }
800 +
801 + /**
802 + * Maybe clear the embed posts transient when a post is updated.
803 + *
804 + * @since 6.35
805 + *
806 + * @param int $post_id Post ID.
807 + * @param WP_Post $post_after Post object after the update.
808 + * @param WP_Post $post_before Post object before the update.
809 + *
810 + * @return void
811 + */
812 + public static function maybe_clear_embed_posts_transient_on_update( $post_id, $post_after, $post_before ) {
813 + FrmFormEmbedsHelper::maybe_clear_on_update( $post_id, $post_after, $post_before );
814 + }
815 +
816 + /**
817 + * Maybe clear the embed posts transient when a post is trashed, untrashed or deleted.
818 + *
819 + * @since 6.35
820 + *
821 + * @param int $post_id Post ID.
822 + * @param WP_Post|null $post Post object, when the hook provides one.
823 + *
824 + * @return void
825 + */
826 + public static function clear_embed_posts_transient_for_post( $post_id, $post = null ) {
827 + FrmFormEmbedsHelper::maybe_clear_for_post( $post_id, $post );
444 828 }
445 829 }