diff --git a/classes/controllers/FrmAppController.php b/classes/controllers/FrmAppController.php index 5d87c178e4..fac66973b4 100644 --- a/classes/controllers/FrmAppController.php +++ b/classes/controllers/FrmAppController.php @@ -729,9 +729,20 @@ public static function admin_js() { wp_register_script( 'formidable_admin', $plugin_url . '/js/formidable_admin.js', self::get_admin_js_dependencies(), $version, true ); + wp_register_script( + 'formidable_forms_list', + $plugin_url . '/js/admin/forms-list.js', + array( + 'formidable_dom', + ), + $version, + true + ); + if ( FrmAppHelper::on_form_listing_page() ) { // For the existing page dropdown in the Form embed modal. wp_enqueue_script( 'jquery-ui-autocomplete' ); + wp_enqueue_script( 'formidable_forms_list' ); } wp_register_script( 'bootstrap-multiselect', $plugin_url . '/js/bootstrap-multiselect.js', array( 'jquery', 'bootstrap_tooltip', 'popper' ), '2.0', true ); diff --git a/classes/controllers/FrmFormsController.php b/classes/controllers/FrmFormsController.php index d8113005db..a60265f375 100644 --- a/classes/controllers/FrmFormsController.php +++ b/classes/controllers/FrmFormsController.php @@ -48,6 +48,13 @@ public static function maybe_load_listing_hooks() { add_filter( 'manage_toplevel_page_formidable_sortable_columns', 'FrmFormsController::get_sortable_columns' ); } + /** + * Runs on admin head of the formidable forms page. + * + * @since x.x This adds screen options. + * + * @return void + */ public static function head() { if ( wp_is_mobile() ) { wp_enqueue_script( 'jquery-touch-punch' ); @@ -1233,15 +1240,25 @@ public static function get_columns( $columns ) { $columns['cb'] = ''; $columns['name'] = esc_html__( 'Form Title', 'formidable' ); $columns['entries'] = esc_html__( 'Entries', 'formidable' ); + $columns['views'] = esc_html__( 'Views', 'formidable' ); $columns['id'] = 'ID'; $columns['form_key'] = esc_html__( 'Key', 'formidable' ); - if ( 'trash' !== FrmAppHelper::simple_get( 'form_type' ) ) { + if ( 'trash' !== FrmAppHelper::simple_get( 'form_type' ) && has_filter( 'frm_form_list_actions' ) ) { + // If there is any filters added, keep this column. $columns['shortcode'] = esc_html__( 'Actions', 'formidable' ); } $columns['created_at'] = esc_html__( 'Date', 'formidable' ); + if ( 'trash' !== FrmAppHelper::simple_get( 'form_type' ) ) { + $columns['embeds'] = esc_html__( 'Embeds', 'formidable' ); + $columns['settings'] = '
'; + $columns['settings'] .= FrmAppHelper::icon_by_class( 'frmfont frm_settings_icon', array( 'echo' => false ) ); + $columns['settings'] .= '' . esc_html__( 'List settings', 'formidable' ) . ''; + $columns['settings'] .= '
'; + } + add_screen_option( 'per_page', array( @@ -3696,6 +3713,37 @@ function () { ); } + /** + * Prints necessary templates for the forms list page. + * + * @since x.x + * + * @return void + */ + public static function print_forms_list_templates() { + if ( ! FrmAppHelper::on_form_listing_page() ) { + return; + } + + $screen = get_current_screen(); + + if ( ! $screen ) { + return; + } + + $columns = get_column_headers( $screen ); + $hidden = get_hidden_columns( $screen ); + $skip_cols = array( 'cb', 'name', 'settings' ); + $per_page = get_user_option( 'formidable_page_formidable_per_page' ); + $show_screen_options = $screen->show_screen_options(); + + if ( $per_page < 1 ) { + $per_page = 20; + } + + include FrmAppHelper::plugin_path() . '/classes/views/frm-forms/forms-list-settings.php'; + } + /** * @deprecated 4.0 * diff --git a/classes/controllers/FrmHooksController.php b/classes/controllers/FrmHooksController.php index d6a0183076..708dcac71d 100644 --- a/classes/controllers/FrmHooksController.php +++ b/classes/controllers/FrmHooksController.php @@ -85,6 +85,9 @@ public static function load_hooks() { add_action( 'wp_scheduled_delete', 'FrmForm::scheduled_delete' ); + // Clear embed posts transient when posts are updated. + add_action( 'wp_insert_post', 'FrmFormsListHelper::maybe_clear_embed_posts_transient', 10, 2 ); + // Form Shortcodes. add_shortcode( 'formidable', 'FrmFormsController::get_form_shortcode' ); @@ -159,6 +162,7 @@ public static function load_admin_hooks() { add_filter( 'set-screen-option', 'FrmFormsController::save_per_page', 10, 3 ); add_action( 'admin_footer', 'FrmFormsController::insert_form_popup' ); + add_action( 'admin_footer', 'FrmFormsController::print_forms_list_templates' ); // Elementor. add_action( 'elementor/editor/footer', 'FrmElementorController::admin_init' ); diff --git a/classes/helpers/FrmAppHelper.php b/classes/helpers/FrmAppHelper.php index f0a98d889c..57280adf8f 100644 --- a/classes/helpers/FrmAppHelper.php +++ b/classes/helpers/FrmAppHelper.php @@ -1536,6 +1536,8 @@ public static function array_to_html_params( $atts, $echo = false ) { * @param bool $echo * * @return string|null + * + * @psalm-return ($echo is true ? null : string) */ public static function clip( $echo_function, $echo = false ) { if ( ! $echo ) { diff --git a/classes/helpers/FrmEntriesListHelper.php b/classes/helpers/FrmEntriesListHelper.php index fc171a98f0..d14329bb3c 100644 --- a/classes/helpers/FrmEntriesListHelper.php +++ b/classes/helpers/FrmEntriesListHelper.php @@ -364,7 +364,7 @@ public function single_row( $item, $style = '' ) { } if ( in_array( $column_name, $hidden, true ) ) { - $class .= ' frm_hidden'; + $class .= ' hidden'; } elseif ( ! $action_col && ! in_array( $column_name, $action_columns, true ) ) { $action_col = $column_name; } diff --git a/classes/helpers/FrmFormsHelper.php b/classes/helpers/FrmFormsHelper.php index 4efd31452b..483050f386 100644 --- a/classes/helpers/FrmFormsHelper.php +++ b/classes/helpers/FrmFormsHelper.php @@ -1248,7 +1248,7 @@ public static function get_action_links( $form_id, $form ) { } else { $actions['duplicate'] = array( 'url' => wp_nonce_url( $duplicate_link ), - 'label' => __( 'Duplicate Form', 'formidable' ), + 'label' => __( 'Duplicate', 'formidable' ), 'icon' => 'frmfont frm_clone_icon', ); } diff --git a/classes/helpers/FrmFormsListHelper.php b/classes/helpers/FrmFormsListHelper.php index 6f0b8e8148..54297147f0 100644 --- a/classes/helpers/FrmFormsListHelper.php +++ b/classes/helpers/FrmFormsListHelper.php @@ -5,6 +5,15 @@ class FrmFormsListHelper extends FrmListHelper { + /** + * The transient name that stores data for which posts a form is embedded in. + * + * @since x.x + * + * @var string + */ + private static $embed_posts_transient_name = 'frm_posts_contain_form'; + /** * @var string */ @@ -35,12 +44,13 @@ public function prepare_items() { $page = $this->get_pagenum(); $per_page = $this->get_items_per_page( 'formidable_page_formidable_per_page' ); - $mode = self::get_param( + $mode = self::get_param( array( 'param' => 'mode', 'default' => 'list', ) ); + $orderby = self::get_param( array( 'param' => 'orderby', @@ -223,21 +233,6 @@ public function get_views() { return $links; } - /** - * @param string $which - * - * @return void - */ - public function pagination( $which ) { - global $mode; - - parent::pagination( $which ); - - if ( 'top' === $which ) { - $this->view_switcher( $mode ); - } - } - /** * @param stdClass $item * @param string $style @@ -254,17 +249,8 @@ public function single_row( $item, $style = '' ) { $this->get_actions( $actions, $item, $edit_link ); $action_links = $this->row_actions( $actions ); - - // Set up the checkbox ( because the user is editable, otherwise its empty ) - $checkbox = ''; - $checkbox_label_text = sprintf( - // translators: Form title - __( 'Select %s', 'formidable' ), - ! empty( $item->name ) ? $item->name : FrmFormsHelper::get_no_title_text() - ); - - $checkbox .= ''; - $r = ''; + $checkbox = $this->get_row_checkbox( $item ); + $r = ''; list( $columns, $hidden ) = $this->get_column_info(); @@ -279,16 +265,15 @@ public function single_row( $item, $style = '' ) { $style = ''; if ( in_array( $column_name, $hidden, true ) ) { - $class .= ' frm_hidden'; + $class .= ' hidden'; } if ( $column_name === 'name' ) { $class .= ' column-primary'; } - $class = 'class="' . esc_attr( $class ) . '"'; - $data_colname = ' data-colname="' . esc_attr( $column_display_name ) . '"'; - $attributes = $class . $style . $data_colname; + $class = 'class="' . esc_attr( $class ) . '"'; + $attributes = $class . $style . $this->get_column_data_attr( $column_name, $column_display_name ); switch ( $column_name ) { case 'cb': @@ -307,19 +292,7 @@ public function single_row( $item, $style = '' ) { $val = '' . $date . ''; break; case 'entries': - if ( ! empty( $item->options['no_save'] ) ) { - $val = FrmAppHelper::icon_by_class( - 'frmfont frm_forbid_icon frm_bstooltip', - array( - 'title' => __( 'Saving entries is disabled for this form', 'formidable' ), - 'echo' => false, - ) - ); - } else { - $text = FrmEntry::getRecordCount( $item->id ); - $val = current_user_can( 'frm_view_entries' ) ? '' . $text . '' : $text; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong - unset( $text ); - } + $val = $this->get_entries_column_value( $item ); break; default: if ( method_exists( $this, 'column_' . $column_name ) ) { @@ -338,11 +311,62 @@ public function single_row( $item, $style = '' ) { return $r . ''; } + /** + * @param string $column_name + * @param string $column_display_name + * + * @return string + */ + private function get_column_data_attr( $column_name, $column_display_name ) { + if ( 'settings' === $column_name ) { + return ' data-colname="' . esc_attr( trim( strip_tags( $column_display_name ) ) ) . '"'; + } + return ' data-colname="' . esc_attr( $column_display_name ) . '"'; + } + + /** + * @param object $item + * + * @return string + */ + private function get_entries_column_value( $item ) { + if ( ! empty( $item->options['no_save'] ) ) { + return (string) FrmAppHelper::icon_by_class( + 'frmfont frm_forbid_icon frm_bstooltip', + array( + 'title' => __( 'Saving entries is disabled for this form', 'formidable' ), + 'echo' => false, + ) + ); + } + + $text = intval( FrmEntry::getRecordCount( $item->id ) ); + return current_user_can( 'frm_view_entries' ) ? '' . $text . '' : (string) $text; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong + } + + /** + * @param object $item + * + * @return string + */ + private function get_row_checkbox( $item ) { + // Set up the checkbox (because the user is editable, otherwise it's empty). + $checkbox = ''; + $checkbox_label_text = sprintf( + // translators: Form title + __( 'Select %s', 'formidable' ), + ! empty( $item->name ) ? $item->name : FrmFormsHelper::get_no_title_text() + ); + + return $checkbox . ''; + } + /** * Get the HTML for the Actions column in the form list. * This includes multiple icons for triggering the embed modal, the visual styler, and an active landing page. * * @since 6.0 + * @deprecated x.x We moved these actions to other places. This column will show if there is a filter added to the hook. * * @param stdClass $form * @@ -404,13 +428,31 @@ protected function column_views( $form ) { 'target' => '_blank', ); + if ( class_exists( 'FrmViewsDisplay' ) ) { + $view_ids = FrmViewsDisplay::get_display_ids_by_form( $form->id ); + $view_count = $view_ids ? count( $view_ids ) : 0; + } else { + $view_count = 0; + } + // phpcs:disable Generic.WhiteSpace.ScopeIndent return ' - ' . FrmAppHelper::icon_by_class( 'frmfont frm_eye_icon', array( 'echo' => false ) ) . + ' . intval( $view_count ) . ''; // phpcs:enable Generic.WhiteSpace.ScopeIndent } + /** + * Get the HTML for the Settings column in the form list. + * + * @since x.x + * + * @return string + */ + protected function column_settings() { + return ' '; + } + /** * @param array $actions * @param object $item @@ -435,8 +477,9 @@ private function get_actions( &$actions, $item, $edit_link ) { $actions['frm_settings'] = '' . esc_html__( 'Settings', 'formidable' ) . ''; } - $actions = array_merge( $actions, $new_actions ); - $actions['view'] = '' . esc_html__( 'Preview', 'formidable' ) . ''; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong + $actions = array_merge( $actions, $new_actions ); + $actions['embed'] = '' . esc_html__( 'Embed', 'formidable' ) . ''; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong + $actions['view'] = '' . esc_html__( 'Preview', 'formidable' ) . ''; // phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong } /** @@ -508,4 +551,196 @@ private function add_form_description( $item, &$val ) { protected function confirm_bulk_delete() { return __( 'ALL selected forms and their entries will be permanently deleted. Want to proceed?', 'formidable' ); } + + /** + * @param stdClass $form + * + * @return string + */ + public function column_embeds( $form ) { + $posts = $this->get_posts_contain_form( $form ); + + if ( ! $posts ) { + return '0'; + } + + return FrmAppHelper::clip( + function () use ( $posts ) { + ?> + + + + id ] ) && is_array( $cached_posts[ $form->id ] ) ) { + return $cached_posts[ $form->id ]; + } + + $posts = $this->query_posts_contain_form( $form ); + + if ( ! is_array( $posts ) ) { + return array(); + } + + foreach ( $posts as $post ) { + if ( ! property_exists( $post, 'permalink' ) ) { + $post->permalink = get_permalink( $post->ID ); + } + + if ( ! property_exists( $post, 'edit_link' ) ) { + $post->edit_link = get_edit_post_link( $post->ID ); + } + + // Ensure post_name is not null or the string "null" + if ( ! isset( $post->post_name ) ) { + $post->post_name = ''; + } + + // Ensure post_title is not null or the string "null" + if ( ! isset( $post->post_title ) ) { + $post->post_title = ''; + } + + if ( '' === $post->post_title ) { + $post->post_title = __( '(no title)', 'formidable' ); + } + }//end foreach + + if ( ! is_array( $cached_posts ) ) { + $cached_posts = array(); + } + + $cached_posts[ $form->id ] = $posts; + set_transient( self::$embed_posts_transient_name, $cached_posts, DAY_IN_SECONDS ); + + return $posts; + } + + /** + * Gets search strings for a form inside a post. + * + * @since x.x + * + * @param int $form_id Form ID. + * + * @return string[] + */ + protected function get_search_strings_for_form( $form_id ) { + return $this->get_base_search_strings_for_form( $form_id ); + } + + /** + * Gets the base search strings for a form inside a post. + * + * @since x.x + * + * @param int $form_id Form ID. + * + * @return string[] + */ + protected function get_base_search_strings_for_form( $form_id ) { + return array( + '[formidable id=' . $form_id . ']', + '[formidable id=' . $form_id . ' ', + '[formidable id="' . $form_id . '"', + "[formidable id='" . $form_id . "'", + '