Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f531687
Add free User Directory feature with setup wizard
arifulhoque7 Nov 28, 2025
c1a5ef3
Update color classes to use ! prefix for importance
arifulhoque7 Nov 28, 2025
547c530
Enhance user directory UI and member count logic
arifulhoque7 Nov 28, 2025
8442e2d
Add file tab to user profile and update build tasks
arifulhoque7 Nov 28, 2025
4f0a05e
Add new utility classes for WPUF admin styles
arifulhoque7 Nov 29, 2025
f31990d
Merge branch 'weDevsOfficial:develop' into feat/user-directory-free
arifulhoque7 Dec 22, 2025
a063095
Refactor user directory module structure and build config
arifulhoque7 Dec 22, 2025
bd9dd30
Refactor advanced step options and update sort values
arifulhoque7 Dec 22, 2025
44b75a6
Update profile layout to use purple theme and improve UI
arifulhoque7 Dec 22, 2025
e13870a
Update primary color scheme to emerald green
arifulhoque7 Dec 22, 2025
83ca178
Refactor user avatar logic for consistency and fallback
arifulhoque7 Dec 22, 2025
ac0a385
Improve pagination shortcode handling and styling
arifulhoque7 Dec 22, 2025
f490092
Refactor user directory module and update styles
arifulhoque7 Dec 22, 2025
a9ce975
Merge remote-tracking branch 'upstream/develop' into FEAT/user-direct…
arifulhoque7 Dec 24, 2025
f3c8911
Skip role filter when 'all' is selected in user directory
arifulhoque7 Dec 26, 2025
2f23a67
Enhance profile tabs and settings compatibility
arifulhoque7 Dec 26, 2025
4bc03df
Merge branch 'develop' into FEAT/user-directory-free-pro-functionality
arifulhoque7 Jan 28, 2026
8e6014e
Merge branch 'develop' into FEAT/user-directory-free-pro-functionality
arifulhoque7 Feb 6, 2026
a593ab0
User Directory: add SingleSelect & UI polish
arifulhoque7 Feb 6, 2026
888be6f
Use SingleSelect for profile base field
arifulhoque7 Feb 6, 2026
f1c3079
Add button/focus classes and update wizard UI
arifulhoque7 Feb 9, 2026
4be5933
Merge remote-tracking branch 'arifulhoque7/FEAT/user-directory-free-p…
sapayth Mar 3, 2026
0f7e0c5
feature: user directory elementor widget
sapayth Mar 4, 2026
a812454
pro initial support
sapayth Mar 4, 2026
fe97092
add pro support
sapayth Mar 4, 2026
2210250
Merge remote-tracking branch 'upstream/develop' into feature/user_dir…
sapayth Apr 6, 2026
e3dbeae
Merge remote-tracking branch 'upstream/develop' into feature/user_dir…
sapayth Apr 27, 2026
7b153fe
Merge remote-tracking branch 'upstream/develop' into recover/pr-1825
arifulhoque7 Jun 16, 2026
0c3c63f
perf: cache Elementor UD back-fill scan instead of running on every i…
arifulhoque7 Jun 16, 2026
4fd8160
refactor: drop redundant transient-clear on Elementor page save
arifulhoque7 Jun 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions assets/css/elementor-user-directory.css
Original file line number Diff line number Diff line change
@@ -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;
}
3 changes: 3 additions & 0 deletions includes/Assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
228 changes: 219 additions & 9 deletions includes/Integrations/Elementor/Elementor.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<?php
/**
* DESCRIPTION: Elementor integration orchestrator. Registers widgets, categories, and handles asset enqueuing.
*
* @package WPUF\Elementor
*/

namespace WeDevs\Wpuf\Integrations\Elementor;

Expand All @@ -9,6 +14,11 @@
*/
class Elementor {

/**
* Constructor
*
* @since 4.0.0
*/
public function __construct() {
add_action( 'elementor/elements/categories_registered', [ $this, 'register_category' ] );
add_action( 'elementor/widgets/register', [ $this, 'register_widgets' ] );
Expand All @@ -19,6 +29,17 @@
// Ensure editor scripts are enqueued for TinyMCE in Elementor preview
add_action( 'elementor/frontend/after_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
add_action( 'elementor/editor/after_enqueue_scripts', [ $this, 'enqueue_scripts' ] );

// Include Elementor pages with the User Directory widget in pretty URL rewrite rules
// (used by the free module's PrettyUrls class when Pro is not active)
add_filter( 'wpuf_ud_directory_pages', [ $this, 'add_elementor_directory_pages' ], 10, 2 );

// Register pretty URL rewrite rules directly for Elementor pages — this runs regardless
// of which module is active, ensuring the rules are always registered.
add_action( 'init', [ $this, 'register_elementor_page_rewrite_rules' ], 5 );

// Flush rewrite rules when Elementor saves a page with our widget
add_action( 'elementor/editor/after_save', [ $this, 'maybe_flush_rules_on_elementor_save' ], 10, 2 );
}

/**
Expand All @@ -30,12 +51,12 @@
*/
public function enqueue_styles() {
// Dequeue all WPUF hardcoded styles so Elementor styles can work properly
wp_dequeue_style('wpuf-frontend-forms');
wp_dequeue_style('wpuf-layout1');
wp_dequeue_style('wpuf-layout2');
wp_dequeue_style('wpuf-layout3');
wp_dequeue_style('wpuf-layout4');
wp_dequeue_style('wpuf-layout5');
wp_dequeue_style( 'wpuf-frontend-forms' );
wp_dequeue_style( 'wpuf-layout1' );
wp_dequeue_style( 'wpuf-layout2' );
wp_dequeue_style( 'wpuf-layout3' );
wp_dequeue_style( 'wpuf-layout4' );
wp_dequeue_style( 'wpuf-layout5' );

$style_handles = [ 'wpuf-elementor-frontend-forms', 'wpuf-account' ];

Expand All @@ -52,9 +73,28 @@
wp_enqueue_style( $handle );
}

// User Directory assets — register if the free module hasn't done it
// (e.g. when Pro is active, Free_Loader is skipped so Shortcode::register_assets() never runs)
if ( ! wp_style_is( 'wpuf-user-directory-frontend', 'registered' ) ) {
wp_register_style(
'wpuf-user-directory-frontend',
WPUF_ASSET_URI . '/css/wpuf-user-directory-free.css',
[],
WPUF_VERSION
);
}
wp_enqueue_style( 'wpuf-user-directory-frontend' );
wp_enqueue_style( 'wpuf-elementor-user-directory' );

if ( wpuf_is_pro_active() ) {
wp_enqueue_script( 'wpuf-conditional-logic' );
wp_enqueue_script( 'wpuf-frontend-form' );

// Pro's Assets::enqueue_shortcode_assets() bails on Elementor pages because
// it checks has_shortcode($post->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' );
}

/**
Expand All @@ -79,10 +119,42 @@
// 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
Expand Down Expand Up @@ -180,7 +252,7 @@
'char_min_title' => __( 'Minimum character required.', 'wp-user-frontend' ),
'char_min_details' => __( 'This field requires minimum %number% characters. Please add some more character.', 'wp-user-frontend' ),
'protected_shortcodes' => wpuf_get_protected_shortcodes(),
'protected_shortcodes_message' => __( 'Using %shortcode% is restricted', 'wp-user-frontend' ),

Check failure on line 255 in includes/Integrations/Elementor/Elementor.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

A function call to __() with texts containing placeholders was found, but was not accompanied by a "translators:" comment on the line above to clarify the meaning of the placeholders.
'password_warning_weak' => __( 'Your password should be at least weak in strength', 'wp-user-frontend' ),
'password_warning_medium' => __( 'Your password needs to be medium strength for better protection', 'wp-user-frontend' ),
'password_warning_strong' => __( 'Create a strong password for maximum security', 'wp-user-frontend' ),
Expand Down Expand Up @@ -224,14 +296,16 @@
$api_key = wpuf_get_option( 'gmap_api_key', 'wpuf_general' );
if ( ! empty( $api_key ) ) {
$scheme = is_ssl() ? 'https' : 'http';
wp_enqueue_script( 'wpuf-google-maps', $scheme . '://maps.google.com/maps/api/js?libraries=places&key=' . $api_key, [], null, true );

Check warning on line 299 in includes/Integrations/Elementor/Elementor.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Resource version not set in call to wp_enqueue_script(). This means new versions of the script may not always be loaded due to browser caching.
}
}

/**
* 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
*/
Expand All @@ -248,17 +322,153 @@
/**
* Register Elementor Widgets
*
* @param \Elementor\Widgets_Manager $widgets_manager
* @since 4.3.1
*
* @param \Elementor\Widgets_Manager $widgets_manager Elementor widgets manager.
*
* @return void
*/
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 ) {

Check warning on line 353 in includes/Integrations/Elementor/Elementor.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

The method parameter $data is never used
$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 ) {
// The meta flag puts this page on the fast meta_query path, so no back-fill re-scan is needed.
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( [

Check failure on line 389 in includes/Integrations/Elementor/Elementor.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Opening parenthesis of a multi-line function call must be the last content on the line
'post_type' => 'page',
'post_status' => 'publish',
'posts_per_page' => -1,
'meta_query' => [

Check warning on line 393 in includes/Integrations/Elementor/Elementor.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Detected usage of meta_query, possible slow query.
[
'key' => '_wpuf_has_ud_elementor_widget',
'value' => '1',
'compare' => '=',
],
],
] );

Check failure on line 400 in includes/Integrations/Elementor/Elementor.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Closing parenthesis of a multi-line function call must be on a line by itself

// 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.
// Run the expensive all-pages back-fill scan at most once per cache window
// instead of on every front-end init. The meta flag is written on page save,
// so the fast path above handles new widget pages; this scan only migrates
// pages that pre-date the flag.
if ( empty( $pages ) && false === get_transient( 'wpuf_ud_elementor_backfill_done' ) ) {
$all_pages = get_posts( [

Check failure on line 410 in includes/Integrations/Elementor/Elementor.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Opening parenthesis of a multi-line function call must be the last content on the line
'post_type' => 'page',
'post_status' => 'publish',
'posts_per_page' => -1,
] );

Check failure on line 414 in includes/Integrations/Elementor/Elementor.php

View workflow job for this annotation

GitHub Actions / Run PHPCS inspection

Closing parenthesis of a multi-line function call must be on a line by itself

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;
}
}

set_transient( 'wpuf_ud_elementor_backfill_done', 1, DAY_IN_SECONDS );

// 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;
}
}
Loading
Loading