diff --git a/assets/css/elementor-user-directory.css b/assets/css/elementor-user-directory.css new file mode 100644 index 000000000..57c92b659 --- /dev/null +++ b/assets/css/elementor-user-directory.css @@ -0,0 +1,69 @@ +/** + * DESCRIPTION: Elementor-specific CSS overrides for the User Directory widget. + */ + +/* Wrapper */ +.wpuf-elementor-user-directory-wrapper { + width: 100%; +} + +/* Empty state for Elementor editor */ +.wpuf-elementor-ud-empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 20px; + text-align: center; + background: #f9fafb; + border: 2px dashed #d1d5db; + border-radius: 8px; +} + +.wpuf-elementor-ud-empty-icon { + color: #9ca3af; + margin-bottom: 16px; +} + +.wpuf-elementor-ud-empty-text { + font-size: 16px; + font-weight: 600; + color: #374151; + margin: 0 0 8px; +} + +.wpuf-elementor-ud-empty-hint { + font-size: 14px; + color: #6b7280; + margin: 0; +} + +.wpuf-elementor-ud-empty-hint a { + color: #7c3aed; + text-decoration: underline; +} + +/* Profile preview separator in Elementor editor */ +.wpuf-elementor-profile-separator { + display: flex; + align-items: center; + margin: 40px 0 30px; + gap: 16px; +} + +.wpuf-elementor-profile-separator::before, +.wpuf-elementor-profile-separator::after { + content: ''; + flex: 1; + height: 1px; + background: #d1d5db; +} + +.wpuf-elementor-profile-separator span { + font-size: 13px; + font-weight: 600; + color: #6b7280; + text-transform: uppercase; + letter-spacing: 0.05em; + white-space: nowrap; +} diff --git a/includes/Assets.php b/includes/Assets.php index 278972d3f..67f9a2ba8 100644 --- a/includes/Assets.php +++ b/includes/Assets.php @@ -194,6 +194,9 @@ public function get_styles() { 'account' => [ 'src' => WPUF_ASSET_URI . '/css/frontend/account.css', ], + 'elementor-user-directory' => [ + 'src' => WPUF_ASSET_URI . '/css/elementor-user-directory.css', + ], ]; return apply_filters( 'wpuf_styles_to_register', $styles ); diff --git a/includes/Integrations/Elementor/Elementor.php b/includes/Integrations/Elementor/Elementor.php index d15ea6d8b..47818e86d 100644 --- a/includes/Integrations/Elementor/Elementor.php +++ b/includes/Integrations/Elementor/Elementor.php @@ -1,4 +1,9 @@ post_content) which is always false for Elementor + // (widget data lives in _elementor_data meta). Enqueue Pro's UD styles here instead. + wp_enqueue_style( 'wpuf-ud-styles' ); + wp_enqueue_style( 'wpuf-ud-shortcode-styles' ); } /** @@ -79,10 +119,42 @@ public function enqueue_scripts() { // Enqueue all required WPUF form assets $this->enqueue_wpuf_form_assets(); + // User Directory scripts — register if the free module hasn't done it + if ( ! wp_script_is( 'wpuf-user-directory-frontend', 'registered' ) ) { + wp_register_script( + 'wpuf-user-directory-frontend', + WPUF_ASSET_URI . '/js/wpuf-user-directory-frontend.js', + [ 'jquery' ], + WPUF_VERSION, + true + ); + } + if ( ! wp_script_is( 'wpuf-ud-search-shortcode', 'registered' ) ) { + wp_register_script( + 'wpuf-ud-search-shortcode', + WPUF_ASSET_URI . '/js/ud-search-shortcode.js', + [], + WPUF_VERSION, + true + ); + } + wp_enqueue_script( 'wpuf-user-directory-frontend' ); + wp_enqueue_script( 'wpuf-ud-search-shortcode' ); + + // Localize search script + wp_localize_script( + 'wpuf-ud-search-shortcode', + 'wpufUserDirectorySearch', + [ + 'restUrl' => rest_url( 'wpuf/v1/user_directory/search' ), + 'nonce' => wp_create_nonce( 'wp_rest' ), + ] + ); + // Ensure editor scripts are loaded for TinyMCE in Elementor preview if ( function_exists( 'wp_enqueue_editor' ) ) { wp_enqueue_editor(); - + // Also explicitly enqueue TinyMCE scripts if available if ( function_exists( 'wp_enqueue_script' ) ) { // Check if these scripts exist and enqueue them @@ -231,7 +303,9 @@ private function enqueue_wpuf_form_assets() { /** * Register Elementor Widget Category * - * @param \Elementor\Elements_Manager $elements_manager + * @since 4.3.1 + * + * @param \Elementor\Elements_Manager $elements_manager Elementor elements manager. * * @return void */ @@ -248,7 +322,9 @@ public function register_category( $elements_manager ) { /** * Register Elementor Widgets * - * @param \Elementor\Widgets_Manager $widgets_manager + * @since 4.3.1 + * + * @param \Elementor\Widgets_Manager $widgets_manager Elementor widgets manager. * * @return void */ @@ -256,9 +332,136 @@ public function register_widgets( $widgets_manager ) { require_once __DIR__ . '/Widget.php'; require_once __DIR__ . '/Subscription_Plans_Widget.php'; require_once __DIR__ . '/Account_Widget.php'; + require_once __DIR__ . '/User_Directory_Widget.php'; $widgets_manager->register( new Widget() ); $widgets_manager->register( new Subscription_Plans_Widget() ); $widgets_manager->register( new Account_Widget() ); + $widgets_manager->register( new User_Directory_Widget() ); + } + + /** + * Flush rewrite rules when Elementor saves a page containing the User Directory widget + * + * @since 4.3.1 + * + * @param int $post_id The post ID. + * @param array $data The Elementor data. + * + * @return void + */ + public function maybe_flush_rules_on_elementor_save( $post_id, $data ) { + $post = get_post( $post_id ); + + if ( ! $post || 'page' !== $post->post_type ) { + return; + } + + $elementor_data = get_post_meta( $post_id, '_elementor_data', true ); + + $has_widget = ! empty( $elementor_data ) && is_string( $elementor_data ) && strpos( $elementor_data, '"widgetType":"wpuf-user-directory"' ) !== false; + + // Keep a queryable meta flag so register_elementor_page_rewrite_rules() can find + // these pages efficiently without scanning all _elementor_data values on every init. + if ( $has_widget ) { + update_post_meta( $post_id, '_wpuf_has_ud_elementor_widget', '1' ); + flush_rewrite_rules(); + } else { + delete_post_meta( $post_id, '_wpuf_has_ud_elementor_widget' ); + } + } + + /** + * Register pretty URL rewrite rules for pages using the User Directory Elementor widget + * + * This runs directly on init (priority 5) so the rules are registered regardless of + * whether the free or Pro module is handling PrettyUrls. Pages are found via a post + * meta flag (_wpuf_has_ud_elementor_widget) set in maybe_flush_rules_on_elementor_save(). + * + * @since 4.3.1 + * + * @return void + */ + public function register_elementor_page_rewrite_rules() { + // Fast path: query only pages that have been flagged via the meta key. + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query + $pages = get_posts( [ + 'post_type' => 'page', + 'post_status' => 'publish', + 'posts_per_page' => -1, + 'meta_query' => [ + [ + 'key' => '_wpuf_has_ud_elementor_widget', + 'value' => '1', + 'compare' => '=', + ], + ], + ] ); + + // Fallback: if no flagged pages exist (e.g. first deploy, meta not yet written), + // scan _elementor_data for existing pages and back-fill the meta flag so future + // requests use the fast path. This only runs once until a page is found. + if ( empty( $pages ) ) { + $all_pages = get_posts( [ + 'post_type' => 'page', + 'post_status' => 'publish', + 'posts_per_page' => -1, + ] ); + + foreach ( $all_pages as $page ) { + $elementor_data = get_post_meta( $page->ID, '_elementor_data', true ); + if ( ! empty( $elementor_data ) && is_string( $elementor_data ) && strpos( $elementor_data, '"widgetType":"wpuf-user-directory"' ) !== false ) { + update_post_meta( $page->ID, '_wpuf_has_ud_elementor_widget', '1' ); + $pages[] = $page; + } + } + + // If we found pages via the fallback scan, flush so the new rules take effect. + if ( ! empty( $pages ) ) { + flush_rewrite_rules(); + } + } + + foreach ( $pages as $page ) { + $page_slug = $page->post_name; + add_rewrite_rule( + '^' . $page_slug . '/([^/]+)/?$', + 'index.php?pagename=' . $page_slug . '&wpuf_user_profile=$matches[1]', + 'top' + ); + } + } + + /** + * Add Elementor pages that use the User Directory widget to the pretty URL rewrite rules + * + * Elementor stores widget data in post meta, not in post_content, so pages + * using the User Directory widget via Elementor won't be detected by the + * default shortcode-based check in PrettyUrls::get_directory_pages(). + * + * @since 4.3.1 + * + * @param array $directory_pages Pages already detected via shortcode. + * @param array $all_pages All published pages. + * + * @return array + */ + public function add_elementor_directory_pages( $directory_pages, $all_pages ) { + $existing_ids = wp_list_pluck( $directory_pages, 'ID' ); + + foreach ( $all_pages as $page ) { + // Skip pages already detected via shortcode + if ( in_array( $page->ID, $existing_ids, true ) ) { + continue; + } + + $elementor_data = get_post_meta( $page->ID, '_elementor_data', true ); + + if ( ! empty( $elementor_data ) && is_string( $elementor_data ) && strpos( $elementor_data, '"widgetType":"wpuf-user-directory"' ) !== false ) { + $directory_pages[] = $page; + } + } + + return $directory_pages; } } diff --git a/includes/Integrations/Elementor/User_Directory_Widget.php b/includes/Integrations/Elementor/User_Directory_Widget.php new file mode 100644 index 000000000..691509fb9 --- /dev/null +++ b/includes/Integrations/Elementor/User_Directory_Widget.php @@ -0,0 +1,1405 @@ +register_content_controls(); + // Directory Style Controls + $this->register_search_bar_style_controls(); + $this->register_filter_controls_style_controls(); + $this->register_user_card_style_controls(); + $this->register_user_name_style_controls(); + $this->register_user_info_style_controls(); + $this->register_view_profile_button_style_controls(); + $this->register_pagination_style_controls(); + // Profile Style Controls + $this->register_profile_name_style_controls(); + $this->register_profile_contact_style_controls(); + $this->register_profile_tab_style_controls(); + $this->register_profile_bio_style_controls(); + $this->register_profile_edit_button_style_controls(); + } + + /** + * Register content controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_content_controls() { + $this->start_controls_section( + 'section_content', + [ + 'label' => __( 'Content', 'wp-user-frontend' ), + ] + ); + $directories = $this->get_directories(); + if ( empty( $directories ) ) { + $this->add_control( + 'no_directory_notice', + [ + 'type' => Controls_Manager::RAW_HTML, + 'raw' => sprintf( + /* translators: %s: Admin URL to create a directory */ + __( 'No user directory found. Create one first.', 'wp-user-frontend' ), + admin_url( 'admin.php?page=wpuf_userlisting' ) + ), + 'content_classes' => 'elementor-panel-alert elementor-panel-alert-info', + ] + ); + } + $this->add_control( + 'directory_id', + [ + 'label' => __( 'Directory', 'wp-user-frontend' ), + 'type' => Controls_Manager::SELECT, + 'default' => '', + 'options' => $this->get_directory_options(), + 'description' => __( 'Select a user directory to display.', 'wp-user-frontend' ), + ] + ); + $this->end_controls_section(); + } + + /** + * Register search bar style controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_search_bar_style_controls() { + $this->start_controls_section( + 'section_search_bar_style', + [ + 'label' => __( 'Search Bar', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + $this->add_control( + 'search_bg_color', + [ + 'label' => __( 'Background Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-search-wrapper' => 'background-color: {{VALUE}} !important;', + '{{WRAPPER}} .wpuf-ud-search-by' => 'background-color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'search_text_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-search-input' => 'color: {{VALUE}} !important;', + '{{WRAPPER}} .wpuf-ud-search-by' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'search_placeholder_color', + [ + 'label' => __( 'Placeholder Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-search-input::placeholder' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'search_border', + 'selector' => '{{WRAPPER}} .wpuf-ud-search-wrapper, {{WRAPPER}} .wpuf-ud-search-by', + 'fields_options' => [ + 'border' => [ + 'default' => 'solid', + ], + 'width' => [ + 'default' => [ + 'top' => '1', + 'right' => '1', + 'bottom' => '1', + 'left' => '1', + 'unit' => 'px', + 'isLinked' => true, + ], + 'selectors' => [ + '{{SELECTOR}}' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ], + 'color' => [ + 'default' => '#D1D5DB', + 'selectors' => [ + '{{SELECTOR}}' => 'border-color: {{VALUE}} !important;', + ], + ], + ], + ] + ); + $this->add_control( + 'search_border_radius', + [ + 'label' => __( 'Border Radius', 'wp-user-frontend' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%' ], + 'default' => [ + 'top' => '6', + 'right' => '6', + 'bottom' => '6', + 'left' => '6', + 'unit' => 'px', + 'isLinked' => true, + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-search-wrapper' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + '{{WRAPPER}} .wpuf-ud-search-by' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ] + ); + $this->add_responsive_control( + 'search_padding', + [ + 'label' => __( 'Padding', 'wp-user-frontend' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', 'em' ], + 'default' => [ + 'top' => '9', + 'right' => '15', + 'bottom' => '9', + 'left' => '17', + 'unit' => 'px', + 'isLinked' => false, + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-search-wrapper' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ] + ); + $this->end_controls_section(); + } + + /** + * Register filter controls style controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_filter_controls_style_controls() { + $this->start_controls_section( + 'section_filter_controls_style', + [ + 'label' => __( 'Filter Controls', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + // Dropdowns heading + $this->add_control( + 'filter_dropdowns_heading', + [ + 'label' => __( 'Dropdowns', 'wp-user-frontend' ), + 'type' => Controls_Manager::HEADING, + ] + ); + $this->add_control( + 'filter_dropdown_bg_color', + [ + 'label' => __( 'Background Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-sort-by' => 'background-color: {{VALUE}} !important;', + '{{WRAPPER}} .wpuf-ud-sort-order' => 'background-color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'filter_dropdown_text_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-sort-by' => 'color: {{VALUE}} !important;', + '{{WRAPPER}} .wpuf-ud-sort-order' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'filter_dropdown_border', + 'selector' => '{{WRAPPER}} .wpuf-ud-sort-by, {{WRAPPER}} .wpuf-ud-sort-order', + 'fields_options' => [ + 'border' => [ + 'default' => 'solid', + ], + 'width' => [ + 'default' => [ + 'top' => '1', + 'right' => '1', + 'bottom' => '1', + 'left' => '1', + 'unit' => 'px', + 'isLinked' => true, + ], + 'selectors' => [ + '{{SELECTOR}}' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ], + 'color' => [ + 'default' => '#D1D5DB', + 'selectors' => [ + '{{SELECTOR}}' => 'border-color: {{VALUE}} !important;', + ], + ], + ], + ] + ); + $this->add_control( + 'filter_dropdown_border_radius', + [ + 'label' => __( 'Border Radius', 'wp-user-frontend' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%' ], + 'default' => [ + 'top' => '6', + 'right' => '6', + 'bottom' => '6', + 'left' => '6', + 'unit' => 'px', + 'isLinked' => true, + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-sort-by' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + '{{WRAPPER}} .wpuf-ud-sort-order' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ] + ); + // Reset Button heading + $this->add_control( + 'filter_reset_btn_heading', + [ + 'label' => __( 'Reset Button', 'wp-user-frontend' ), + 'type' => Controls_Manager::HEADING, + 'separator' => 'before', + ] + ); + $this->add_control( + 'filter_reset_btn_text_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-reset-filters' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'filter_reset_btn_bg_color', + [ + 'label' => __( 'Background Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#059669', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-reset-filters' => 'background-color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'filter_reset_btn_border_radius', + [ + 'label' => __( 'Border Radius', 'wp-user-frontend' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%' ], + 'default' => [ + 'top' => '6', + 'right' => '6', + 'bottom' => '6', + 'left' => '6', + 'unit' => 'px', + 'isLinked' => true, + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-reset-filters' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ] + ); + $this->add_responsive_control( + 'filter_reset_btn_padding', + [ + 'label' => __( 'Padding', 'wp-user-frontend' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', 'em' ], + 'default' => [ + 'top' => '8', + 'right' => '24', + 'bottom' => '8', + 'left' => '24', + 'unit' => 'px', + 'isLinked' => false, + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-reset-filters' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ] + ); + $this->end_controls_section(); + } + + /** + * Register user card style controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_user_card_style_controls() { + $this->start_controls_section( + 'section_user_card_style', + [ + 'label' => __( 'User Cards', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + $this->add_control( + 'card_bg_color', + [ + 'label' => __( 'Background Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li' => 'background-color: {{VALUE}} !important;', + ], + ] + ); + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'card_border', + 'selector' => '{{WRAPPER}} .wpuf-ud-list-layout-3 li', + 'fields_options' => [ + 'border' => [ + 'default' => 'solid', + ], + 'width' => [ + 'default' => [ + 'top' => '1', + 'right' => '1', + 'bottom' => '1', + 'left' => '1', + 'unit' => 'px', + 'isLinked' => true, + ], + 'selectors' => [ + '{{SELECTOR}}' => 'border-width: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ], + 'color' => [ + 'default' => '#D1D5DB', + 'selectors' => [ + '{{SELECTOR}}' => 'border-color: {{VALUE}} !important;', + ], + ], + ], + ] + ); + $this->add_control( + 'card_border_radius', + [ + 'label' => __( 'Border Radius', 'wp-user-frontend' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%' ], + 'default' => [ + 'top' => '8', + 'right' => '8', + 'bottom' => '8', + 'left' => '8', + 'unit' => 'px', + 'isLinked' => true, + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ] + ); + $this->add_group_control( + Group_Control_Box_Shadow::get_type(), + [ + 'name' => 'card_box_shadow', + 'selector' => '{{WRAPPER}} .wpuf-ud-list-layout-3 li', + ] + ); + $this->add_responsive_control( + 'card_padding', + [ + 'label' => __( 'Padding', 'wp-user-frontend' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', 'em' ], + 'default' => [ + 'top' => '24', + 'right' => '24', + 'bottom' => '24', + 'left' => '24', + 'unit' => 'px', + 'isLinked' => true, + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ] + ); + $this->add_responsive_control( + 'cards_gap', + [ + 'label' => __( 'Gap Between Cards', 'wp-user-frontend' ), + 'type' => Controls_Manager::SLIDER, + 'size_units' => [ 'px' ], + 'range' => [ 'px' => [ 'min' => 0, 'max' => 100 ] ], + 'default' => [ 'size' => 24, 'unit' => 'px' ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 ul' => 'gap: {{SIZE}}{{UNIT}} !important;', + ], + ] + ); + $this->end_controls_section(); + } + + /** + * Register user name style controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_user_name_style_controls() { + $this->start_controls_section( + 'section_user_name_style', + [ + 'label' => __( 'User Name', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + $this->add_control( + 'user_name_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#111827', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li h3' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'user_name_typography', + 'selector' => '{{WRAPPER}} .wpuf-ud-list-layout-3 li h3', + ] + ); + $this->add_responsive_control( + 'user_name_align', + [ + 'label' => __( 'Alignment', 'wp-user-frontend' ), + 'type' => Controls_Manager::CHOOSE, + 'options' => [ + 'left' => [ 'title' => __( 'Left', 'wp-user-frontend' ), 'icon' => 'eicon-text-align-left' ], + 'center' => [ 'title' => __( 'Center', 'wp-user-frontend' ), 'icon' => 'eicon-text-align-center' ], + 'right' => [ 'title' => __( 'Right', 'wp-user-frontend' ), 'icon' => 'eicon-text-align-right' ], + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li h3' => 'text-align: {{VALUE}} !important; width: 100% !important;', + ], + ] + ); + $this->end_controls_section(); + } + + /** + * Register user info style controls (email, website, phone) + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_user_info_style_controls() { + $this->start_controls_section( + 'section_user_info_style', + [ + 'label' => __( 'User Info', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + $this->add_control( + 'user_info_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#4B5563', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li p' => 'color: {{VALUE}} !important;', + '{{WRAPPER}} .wpuf-ud-list-layout-3 li p a' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'user_info_hover_color', + [ + 'label' => __( 'Link Hover Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#111827', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li p a:hover' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'user_info_typography', + 'selector' => '{{WRAPPER}} .wpuf-ud-list-layout-3 li p', + ] + ); + $this->end_controls_section(); + } + + /** + * Register View Profile button style controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_view_profile_button_style_controls() { + $this->start_controls_section( + 'section_view_profile_btn_style', + [ + 'label' => __( 'View Profile Button', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'view_profile_btn_typography', + 'selector' => '{{WRAPPER}} .wpuf-ud-list-layout-3 li a.wpuf-bg-purple-600', + ] + ); + $this->start_controls_tabs( 'tabs_view_profile_btn' ); + $this->start_controls_tab( + 'tab_view_profile_btn_normal', + [ 'label' => __( 'Normal', 'wp-user-frontend' ) ] + ); + $this->add_control( + 'view_profile_btn_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li a.wpuf-bg-purple-600' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'view_profile_btn_bg_color', + [ + 'label' => __( 'Background Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#9333EA', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li a.wpuf-bg-purple-600' => 'background-color: {{VALUE}} !important;', + ], + ] + ); + $this->add_group_control( + Group_Control_Border::get_type(), + [ + 'name' => 'view_profile_btn_border', + 'selector' => '{{WRAPPER}} .wpuf-ud-list-layout-3 li a.wpuf-bg-purple-600', + ] + ); + $this->end_controls_tab(); + $this->start_controls_tab( + 'tab_view_profile_btn_hover', + [ 'label' => __( 'Hover', 'wp-user-frontend' ) ] + ); + $this->add_control( + 'view_profile_btn_hover_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li a.wpuf-bg-purple-600:hover' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'view_profile_btn_hover_bg_color', + [ + 'label' => __( 'Background Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li a.wpuf-bg-purple-600:hover' => 'background-color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'view_profile_btn_hover_border_color', + [ + 'label' => __( 'Border Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li a.wpuf-bg-purple-600:hover' => 'border-color: {{VALUE}} !important;', + ], + ] + ); + $this->end_controls_tab(); + $this->end_controls_tabs(); + $this->add_control( + 'view_profile_btn_border_radius', + [ + 'label' => __( 'Border Radius', 'wp-user-frontend' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%' ], + 'separator' => 'before', + 'default' => [ + 'top' => '6', + 'right' => '6', + 'bottom' => '6', + 'left' => '6', + 'unit' => 'px', + 'isLinked' => true, + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li a.wpuf-bg-purple-600' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ] + ); + $this->add_responsive_control( + 'view_profile_btn_padding', + [ + 'label' => __( 'Padding', 'wp-user-frontend' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', 'em' ], + 'default' => [ + 'top' => '8', + 'right' => '16', + 'bottom' => '8', + 'left' => '16', + 'unit' => 'px', + 'isLinked' => false, + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-list-layout-3 li a.wpuf-bg-purple-600' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ] + ); + $this->end_controls_section(); + } + + /** + * Register pagination style controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_pagination_style_controls() { + $this->start_controls_section( + 'section_pagination_style', + [ + 'label' => __( 'Pagination', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + $this->add_control( + 'pagination_text_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#374151', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-pagination-shortcode a' => 'color: {{VALUE}} !important;', + '{{WRAPPER}} .wpuf-ud-pagination-shortcode span' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'pagination_active_color', + [ + 'label' => __( 'Active Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#059669', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-pagination-shortcode span[aria-current="page"]' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'pagination_active_border_color', + [ + 'label' => __( 'Active Border Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#059669', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-pagination-shortcode span[aria-current="page"]' => 'border-top-color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'pagination_hover_color', + [ + 'label' => __( 'Hover Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#059669', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-pagination-shortcode a:hover' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'pagination_hover_border_color', + [ + 'label' => __( 'Hover Border Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#059669', + 'selectors' => [ + '{{WRAPPER}} .wpuf-ud-pagination-shortcode a:hover' => 'border-top-color: {{VALUE}} !important;', + ], + ] + ); + $this->end_controls_section(); + } + + /** + * Register profile name style controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_profile_name_style_controls() { + $this->start_controls_section( + 'section_profile_name_style', + [ + 'label' => __( 'Profile - Name', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + $this->add_control( + 'profile_name_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#111827', + 'selectors' => [ + '{{WRAPPER}} .wpuf-profile-layout-2 h1' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'profile_name_typography', + 'selector' => '{{WRAPPER}} .wpuf-profile-layout-2 h1', + ] + ); + $this->end_controls_section(); + } + + /** + * Register profile contact info style controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_profile_contact_style_controls() { + $this->start_controls_section( + 'section_profile_contact_style', + [ + 'label' => __( 'Profile - Contact Info', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + $this->add_control( + 'profile_contact_icon_color', + [ + 'label' => __( 'Icon Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#059669', + 'selectors' => [ + '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card svg path' => 'stroke: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'profile_contact_text_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#111827', + 'selectors' => [ + '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card span' => 'color: {{VALUE}} !important;', + '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card a:not(.wpuf-edit-profile-btn)' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'profile_contact_typography', + 'selector' => '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card span', + ] + ); + $this->end_controls_section(); + } + + /** + * Register profile tab navigation style controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_profile_tab_style_controls() { + $this->start_controls_section( + 'section_profile_tab_style', + [ + 'label' => __( 'Profile - Tab Navigation', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + $this->add_control( + 'profile_tab_text_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#6B7280', + 'selectors' => [ + '{{WRAPPER}} .wpuf-tab-button-2' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'profile_tab_active_color', + [ + 'label' => __( 'Active Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#059669', + 'selectors' => [ + '{{WRAPPER}} .wpuf-tab-button-2.active' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'profile_tab_active_border_color', + [ + 'label' => __( 'Active Border Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#059669', + 'selectors' => [ + '{{WRAPPER}} .wpuf-tab-button-2.active::after' => 'background-color: {{VALUE}} !important;', + ], + ] + ); + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'profile_tab_typography', + 'selector' => '{{WRAPPER}} .wpuf-tab-button-2', + ] + ); + $this->add_control( + 'profile_tab_bg_color', + [ + 'label' => __( 'Background Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => 'transparent', + 'selectors' => [ + '{{WRAPPER}} .wpuf-tab-button-2' => 'background-color: {{VALUE}} !important;', + ], + ] + ); + $this->end_controls_section(); + } + + /** + * Register profile bio text style controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_profile_bio_style_controls() { + $this->start_controls_section( + 'section_profile_bio_style', + [ + 'label' => __( 'Profile - Bio', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + $this->add_control( + 'profile_bio_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#9CA3AF', + 'selectors' => [ + '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card p' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'profile_bio_typography', + 'selector' => '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card p', + ] + ); + $this->end_controls_section(); + } + + /** + * Register Edit Profile button style controls + * + * @since WPUF_SINCE + * + * @return void + */ + protected function register_profile_edit_button_style_controls() { + $this->start_controls_section( + 'section_profile_edit_btn_style', + [ + 'label' => __( 'Profile - Edit Profile Button', 'wp-user-frontend' ), + 'tab' => Controls_Manager::TAB_STYLE, + ] + ); + $this->add_group_control( + Group_Control_Typography::get_type(), + [ + 'name' => 'profile_edit_btn_typography', + 'selector' => '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card a.wpuf-edit-profile-btn', + ] + ); + $this->start_controls_tabs( 'tabs_profile_edit_btn' ); + $this->start_controls_tab( + 'tab_profile_edit_btn_normal', + [ 'label' => __( 'Normal', 'wp-user-frontend' ) ] + ); + $this->add_control( + 'profile_edit_btn_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#ffffff', + 'selectors' => [ + '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card a.wpuf-edit-profile-btn' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'profile_edit_btn_bg_color', + [ + 'label' => __( 'Background Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'default' => '#059669', + 'selectors' => [ + '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card a.wpuf-edit-profile-btn' => 'background-color: {{VALUE}} !important;', + ], + ] + ); + $this->end_controls_tab(); + $this->start_controls_tab( + 'tab_profile_edit_btn_hover', + [ 'label' => __( 'Hover', 'wp-user-frontend' ) ] + ); + $this->add_control( + 'profile_edit_btn_hover_color', + [ + 'label' => __( 'Text Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card a.wpuf-edit-profile-btn:hover' => 'color: {{VALUE}} !important;', + ], + ] + ); + $this->add_control( + 'profile_edit_btn_hover_bg_color', + [ + 'label' => __( 'Background Color', 'wp-user-frontend' ), + 'type' => Controls_Manager::COLOR, + 'selectors' => [ + '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card a.wpuf-edit-profile-btn:hover' => 'background-color: {{VALUE}} !important;', + ], + ] + ); + $this->end_controls_tab(); + $this->end_controls_tabs(); + $this->add_control( + 'profile_edit_btn_border_radius', + [ + 'label' => __( 'Border Radius', 'wp-user-frontend' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', '%' ], + 'separator' => 'before', + 'default' => [ + 'top' => '8', + 'right' => '8', + 'bottom' => '8', + 'left' => '8', + 'unit' => 'px', + 'isLinked' => true, + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card a.wpuf-edit-profile-btn' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ] + ); + $this->add_responsive_control( + 'profile_edit_btn_padding', + [ + 'label' => __( 'Padding', 'wp-user-frontend' ), + 'type' => Controls_Manager::DIMENSIONS, + 'size_units' => [ 'px', 'em' ], + 'default' => [ + 'top' => '10', + 'right' => '30', + 'bottom' => '10', + 'left' => '30', + 'unit' => 'px', + 'isLinked' => false, + ], + 'selectors' => [ + '{{WRAPPER}} .wpuf-profile-layout-2 .wpuf-profile-header-card a.wpuf-edit-profile-btn' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}} !important;', + ], + ] + ); + $this->end_controls_section(); + } + + /** + * Render the widget output on the frontend + * + * @since WPUF_SINCE + * + * @return void + */ + protected function render() { + $settings = $this->get_settings_for_display(); + $directory_id = ! empty( $settings['directory_id'] ) ? absint( $settings['directory_id'] ) : 0; + $is_editor = $this->is_editor_render(); + if ( empty( $directory_id ) ) { + if ( $is_editor ) { + $this->render_empty_state(); + } + return; + } + // Verify directory exists + $post = get_post( $directory_id ); + if ( ! $post || 'wpuf_user_listing' !== $post->post_type ) { + if ( $is_editor ) { + $this->render_empty_state( __( 'Selected directory no longer exists.', 'wp-user-frontend' ) ); + } + return; + } + // Localize search script + wp_localize_script( + 'wpuf-ud-search-shortcode', + 'wpufUserDirectorySearch', + [ + 'restUrl' => rest_url( 'wpuf/v1/user_directory/search' ), + 'nonce' => wp_create_nonce( 'wp_rest' ), + ] + ); + echo '
' . esc_html( $message ) . '
'; + echo ''; + echo wp_kses_post( + sprintf( + /* translators: %s: Admin URL to create a directory */ + __( 'Create a user directory in WP User Frontend settings.', 'wp-user-frontend' ), + admin_url( 'admin.php?page=wpuf_userlisting' ) + ) + ); + echo '
'; + echo '