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 +384 -55 6.27 → trunk View file →
@@ -11,8 +11,11 @@
11 11 public $status = '';
12 12
13 13 public $total_items = 0;
14 14
15 + /**
16 + * @param array $args
17 + */
15 18 public function __construct( $args ) {
16 19 $this->status = self::get_param( array( 'param' => 'form_type' ) );
17 20
18 21 parent::__construct( $args );
@@ -31,14 +34,15 @@
31 34
32 35 $page = $this->get_pagenum();
33 36 $per_page = $this->get_items_per_page( 'formidable_page_formidable_per_page' );
34 37
35 - $mode = self::get_param(
38 + $mode = self::get_param(
36 39 array(
37 40 'param' => 'mode',
38 41 'default' => 'list',
39 42 )
40 43 );
44 +
41 45 $orderby = self::get_param(
42 46 array(
43 47 'param' => 'orderby',
44 48 'default' => 'name',
@@ -120,16 +124,42 @@
120 124 /**
121 125 * @return void
122 126 */
123 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 +
124 152 if ( $this->status === 'trash' ) {
125 153 echo '<p>';
126 154 esc_html_e( 'No forms found in the trash.', 'formidable' );
155 + // phpcs:disable Generic.WhiteSpace.ScopeIndent
127 156 ?>
128 157 <a href="<?php echo esc_url( admin_url( 'admin.php?page=formidable' ) ); ?>">
129 158 <?php esc_html_e( 'See all forms.', 'formidable' ); ?>
130 159 </a>
131 160 <?php
161 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
132 162 echo '</p>';
133 163 } else {
134 164 $title = __( 'No Forms Found', 'formidable' );
135 165 include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/_no_forms.php';
@@ -165,15 +195,19 @@
165 195 if ( 'top' !== $which ) {
166 196 return;
167 197 }
168 198
169 - if ( 'trash' === $this->status && current_user_can( 'frm_delete_forms' ) ) {
170 - ?>
171 - <div class="alignleft actions frm_visible_overflow">
172 - <?php submit_button( __( 'Empty Trash', 'formidable' ), 'apply', 'delete_all', false ); ?>
173 - </div>
174 - <?php
199 + if ( 'trash' !== $this->status || ! current_user_can( 'frm_delete_forms' ) ) {
200 + return;
175 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
176 210 }
177 211
178 212 /**
179 213 * @return array
@@ -214,23 +248,8 @@
214 248 return $links;
215 249 }
216 250
217 251 /**
218 - * @param string $which
219 - *
220 - * @return void
221 - */
222 - public function pagination( $which ) {
223 - global $mode;
224 -
225 - parent::pagination( $which );
226 -
227 - if ( 'top' === $which ) {
228 - $this->view_switcher( $mode );
229 - }
230 - }
231 -
232 - /**
233 252 * @param stdClass $item
234 253 * @param string $style
235 254 *
236 255 * @return string
@@ -244,20 +263,11 @@
244 263
245 264 $this->get_actions( $actions, $item, $edit_link );
246 265
247 266 $action_links = $this->row_actions( $actions );
267 + $checkbox = $this->get_row_checkbox( $item );
268 + $r = '<tr id="item-action-' . absint( $item->id ) . '"' . $style . '>';
248 269
249 - // Set up the checkbox ( because the user is editable, otherwise its empty )
250 - $checkbox = '<input type="checkbox" name="item-action[]" id="cb-item-action-' . absint( $item->id ) . '" value="' . esc_attr( $item->id ) . '" />';
251 - $checkbox_label_text = sprintf(
252 - // translators: Form title
253 - __( 'Select %s', 'formidable' ),
254 - ! empty( $item->name ) ? $item->name : FrmFormsHelper::get_no_title_text()
255 - );
256 -
257 - $checkbox .= '<label for="cb-item-action-' . absint( $item->id ) . '"><span class="screen-reader-text">' . esc_html( $checkbox_label_text ) . '</span></label>';
258 - $r = '<tr id="item-action-' . absint( $item->id ) . '"' . $style . '>';
259 -
260 270 list( $columns, $hidden ) = $this->get_column_info();
261 271
262 272 $format = 'Y/m/d';
263 273
@@ -269,9 +279,9 @@
269 279 $class = $column_name . ' column-' . $column_name . ( 'name' === $column_name ? ' post-title page-title column-title' : '' );
270 280 $style = '';
271 281
272 282 if ( in_array( $column_name, $hidden, true ) ) {
273 - $class .= ' frm_hidden';
283 + $class .= ' hidden';
274 284 }
275 285
276 286 if ( $column_name === 'name' ) {
277 287 $class .= ' column-primary';
@@ -276,11 +286,10 @@
276 286 if ( $column_name === 'name' ) {
277 287 $class .= ' column-primary';
278 288 }
279 289
280 - $class = 'class="' . esc_attr( $class ) . '"';
281 - $data_colname = ' data-colname="' . esc_attr( $column_display_name ) . '"';
282 - $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 );
283 292
284 293 switch ( $column_name ) {
285 294 case 'cb':
286 295 $r .= '<th scope="row" class="check-column">' . $checkbox . '</th>';
@@ -297,21 +306,9 @@
297 306 $date = gmdate( $format, strtotime( $item->created_at ) );
298 307 $val = '<abbr title="' . esc_attr( gmdate( 'Y/m/d g:i:s A', strtotime( $item->created_at ) ) ) . '">' . $date . '</abbr>';
299 308 break;
300 309 case 'entries':
301 - if ( ! empty( $item->options['no_save'] ) ) {
302 - $val = FrmAppHelper::icon_by_class(
303 - 'frmfont frm_forbid_icon frm_bstooltip',
304 - array(
305 - 'title' => __( 'Saving entries is disabled for this form', 'formidable' ),
306 - 'echo' => false,
307 - )
308 - );
309 - } else {
310 - $text = FrmEntry::getRecordCount( $item->id );
311 - $val = current_user_can( 'frm_view_entries' ) ? '<a href="' . esc_url( admin_url( 'admin.php?page=formidable-entries&form=' . $item->id ) ) . '">' . $text . '</a>' : $text; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
312 - unset( $text );
313 - }
310 + $val = $this->get_entries_column_value( $item );
314 311 break;
315 312 default:
316 313 if ( method_exists( $this, 'column_' . $column_name ) ) {
317 314 $val = call_user_func( array( $this, 'column_' . $column_name ), $item ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -329,12 +326,63 @@
329 326 return $r . '</tr>';
330 327 }
331 328
332 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 ) ) ) . '"';
338 + }
339 + return ' data-colname="' . esc_attr( $column_display_name ) . '"';
340 + }
341 +
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
360 + }
361 +
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 + /**
333 380 * Get the HTML for the Actions column in the form list.
334 381 * This includes multiple icons for triggering the embed modal, the visual styler, and an active landing page.
335 382 *
336 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.
337 385 *
338 386 * @param stdClass $form
339 387 *
340 388 * @return string
@@ -394,14 +442,34 @@
394 442 'title' => __( 'Link to list of all views for this form.', 'formidable' ),
395 443 'target' => '_blank',
396 444 );
397 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
398 454 return '<a ' . FrmAppHelper::array_to_html_params( $attributes ) . '>
399 - ' . FrmAppHelper::icon_by_class( 'frmfont frm_eye_icon', array( 'echo' => false ) ) .
455 + ' . intval( $view_count ) .
400 456 '</a>';
457 + // phpcs:enable Generic.WhiteSpace.ScopeIndent
401 458 }
402 459
403 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 + /**
404 472 * @param array $actions
405 473 * @param object $item
406 474 * @param string $edit_link
407 475 *
@@ -419,14 +487,15 @@
419 487 return;
420 488 }
421 489
422 490 if ( current_user_can( 'frm_edit_forms' ) ) {
423 - $actions['frm_edit'] = '<a href="' . esc_url( $edit_link ) . '">' . __( 'Edit', 'formidable' ) . '</a>';
424 - $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>';
425 493 }
426 494
427 - $actions = array_merge( $actions, $new_actions );
428 - $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
429 498 }
430 499
431 500 /**
432 501 * @param object $item
@@ -495,6 +564,266 @@
495 564 * @return string
496 565 */
497 566 protected function confirm_bulk_delete() {
498 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 );
499 828 }
500 829 }