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
f8d4fe4
feat(order): add prepaid status
syedgalib Aug 5, 2026
c614611
fix: include directory slug in author profile links
syedgalib Aug 6, 2026
6630660
Merge branch 'development' of https://github.com/sovware/directorist …
syedgalib Aug 15, 2026
236c0fa
Merge branch 'development' into fix/author-profile-link
RabbiIslamRony Aug 16, 2026
3c87cea
Merge branch 'development' of https://github.com/sovware/directorist …
syedgalib Aug 16, 2026
e98ff53
Merge branch 'feat/order-system' of https://github.com/syedgalib/dire…
syedgalib Aug 16, 2026
2cb30a0
fix(order): preserve status column constraints
syedgalib Aug 17, 2026
9eec0b0
fix: include directory slug in author avatar link
syedgalib Aug 17, 2026
9b769c7
Merge pull request #2955 from syedgalib/feat/order-system
Armanul46 Aug 17, 2026
221764a
Merge pull request #2957 from syedgalib/fix/author-profile-link
Armanul46 Aug 17, 2026
1bc3a55
readme update
Armanul46 Aug 17, 2026
8e0a671
Merge pull request #2967 from Armanul46/readme-v894
Armanul46 Aug 17, 2026
d92efd5
Merge branch 'trunk' into development
RabbiIslamRony Aug 18, 2026
093af8b
fix(featured-listing): sync expiration after order update
syedgalib Aug 19, 2026
9c4b142
fix(featured-listing): honor force-disabled state
syedgalib Aug 19, 2026
eb53299
refactor(featured-listing): use new orders in expiration cron
syedgalib Aug 19, 2026
5af64bf
fix(featured-listing): handle provider and order edge cases
syedgalib Aug 19, 2026
6c4a2a4
fix(featured-listing): set expiration on partial paid updates
syedgalib Aug 19, 2026
7bf7b67
fix(featured-listing): refresh expiration on paid updates
syedgalib Aug 20, 2026
4dda2b3
fix(featured-listing): persist expiration after listing update
syedgalib Aug 20, 2026
a7bc3d6
Merge pull request #2969 from syedgalib/fix/featured-listing-expiration
Armanul46 Aug 27, 2026
8255e7d
readme update
Armanul46 Aug 27, 2026
e5f9c6b
Merge pull request #2978 from Armanul46/readme-v894
Armanul46 Aug 27, 2026
34f369a
fix(featured-listing): persist order expiration before early return
syedgalib Aug 27, 2026
33f0865
fix(payment): resolve prepaid and featured expiry handling
syedgalib Aug 27, 2026
0e9bbf1
fix(payment): preserve prepaid order lifecycle
syedgalib Aug 27, 2026
3bc8e8a
fix(payment): preserve entitlements and inserted payments
syedgalib Aug 27, 2026
496d25e
fix(featured-listing): preserve entitlement expiration
syedgalib Aug 27, 2026
46e6422
fix(featured-listing): initialize missing paid expiry
syedgalib Aug 27, 2026
b35b669
Merge pull request #2980 from syedgalib/fix/generic-issues-2
Armanul46 Aug 27, 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
2 changes: 1 addition & 1 deletion config.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php
// Plugin version.
if ( ! defined( 'ATBDP_VERSION' ) ) {
define( 'ATBDP_VERSION', '8.9.3' );
define( 'ATBDP_VERSION', '8.9.4' );
}
// Plugin Folder Path.
if ( ! defined( 'ATBDP_DIR' ) ) {
Expand Down
2 changes: 1 addition & 1 deletion directorist-base.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Plugin Name: Directorist - Business Directory Solution
* Plugin URI: https://wpwax.com
* Description: A comprehensive solution to create professional looking directory site of any kind. Like Yelp, Foursquare, etc.
* Version: 8.9.3
* Version: 8.9.4
* Requires PHP: 7.4
* Author: wpWax
* Author URI: https://wpwax.com
Expand Down
48 changes: 13 additions & 35 deletions includes/classes/class-cron.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,12 @@ public function atbdp_custom_schedule_cron() {
*/

private function featured_listing_followup() {
if ( directorist_is_force_disabled_featured_listings() ) {
return;
}

if ( directorist_is_monetization_enabled() && directorist_is_featured_listing_enabled() ) {
$featured_days = get_directorist_option( 'featured_listing_time', 30 );
$orders = directorist_order_repository();
// Define the query
$args = [
'post_type' => ATBDP_POST_TYPE,
Expand All @@ -120,46 +124,20 @@ private function featured_listing_followup() {
// Start the Loop
if ( $listings->found_posts ) {
foreach ( $listings->posts as $listing ) {
$order = $this->get_order_by_listing( $listing->ID );
if ( $order ) {
$days = round( abs( strtotime( current_time( 'mysql' ) ) - strtotime( $order[0]->post_date ) ) / 86400 );
if ( $days > $featured_days ) {
do_action( 'atbdp_listing_featured_to_general', $listing->ID );
update_post_meta( $listing->ID, '_featured', '' );
}
$has_paid_order = $orders->listing_has_paid_featured_order( $listing->ID );
$has_active_entitlement = $orders->listing_has_active_featured_entitlement( $listing->ID );

if ( ! $has_paid_order || $has_active_entitlement ) {
continue;
}

do_action( 'atbdp_listing_featured_to_general', $listing->ID );
update_post_meta( $listing->ID, '_featured', '' );
}
}
}
}

private function get_order_by_listing( $listing_id ) {
$args = [
'post_type' => ATBDP_ORDER_POST_TYPE,
'posts_per_page' => 1,
'post_status' => 'publish',
'meta_query' => [
'relation' => 'AND',
[
'key' => '_listing_id',
'value' => $listing_id,
],
[
'key' => '_payment_status',
'value' => 'completed',
],
],
];

$listings = new WP_Query( $args );

// Start the Loop
if ( $listings->found_posts ) {
return $listings->posts;
}
return '';
}

/**
* Move listings to renewal status (only if applicable).
*
Expand Down
91 changes: 66 additions & 25 deletions includes/classes/class-featured-listing-checkout.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,24 @@
class FeaturedListingCheckout {
const CHECKOUT_TYPE = 'featured_listing';

public function __construct() {
add_filter( 'directorist_checkout_types', [$this, 'add_checkout_type'] );
public function __construct() {
add_filter( 'directorist_order_data', [ $this, 'handle_order_data' ] );
add_action( 'directorist_after_order_update', [$this, 'handle_after_order_update'] );
add_filter( 'directorist_payment_receipt_order_items', [$this, 'handle_payment_receipt_order_items'], 10, 2 );

add_action( 'plugins_loaded', [ $this, 'register_hooks' ], 20 );
}

public function register_hooks() {
if ( directorist_is_force_disabled_featured_listings() ) {
return;
}

add_filter( 'directorist_checkout_types', [$this, 'add_checkout_type'] );
add_action( 'directorist_after_order_update', [$this, 'handle_after_order_update'], 10, 2 );
add_filter( 'directorist_checkout_validation', [$this, 'validate_checkout'], 10, 2 );
add_action( 'directorist_checkout_table', [$this, 'handle_checkout_table'], 10, 4 );
add_filter( 'directorist_checkout_subtotal', [$this, 'handle_checkout_subtotal'], 10, 3 );
add_action( 'directorist_checkout_create_order', [$this, 'handle_checkout_create_order'], 10, 3 );
add_action( 'directorist_before_order_update', [$this, 'handle_before_order_update'] );
add_filter( 'directorist_checkout_product_name', [$this, 'handle_checkout_product_name'], 10, 2 );
add_filter( 'directorist_ajax_listing_submission_response', [ $this, 'handle_listing_submission_response_data' ], 10, 2 );
add_filter( 'directorist_listing_update_args_after_preview', [ $this, 'handle_listing_status_after_preview' ], 10, 2 );
Expand Down Expand Up @@ -125,24 +133,7 @@ public function handle_checkout_create_order( OrderDTO $dto, string $checkout_ty
$dto->set_listing_id( $request->get_param( 'listing_id' ) )->set_is_featured_listing( 1 )->set_ref_type( self::CHECKOUT_TYPE )->set_amount( $amount )->set_sub_total( $amount );
}

public function handle_before_order_update( OrderDTO $dto ) {
if ( ! $this->is_featured_order( $dto ) ) {
return;
}

if ( ! $dto->is_initialized( 'status' ) || $dto->get_status() !== Status::PAID ) {
return;
}

$order = directorist_get_order_by_id( $dto->get_id() );

if ( $order->is_featured_listing && ! $order->expires_at ) {
$featured_days = get_directorist_option( 'featured_listing_time', 30 );
$dto->set_expires_at( directorist_now()->add_days( $featured_days ) );
}
}

public function handle_after_order_update( OrderDTO $dto ) {
public function handle_after_order_update( OrderDTO $dto, $old_order = null ) {
if ( ! $this->is_featured_order( $dto ) ) {
return;
}
Expand All @@ -152,19 +143,69 @@ public function handle_after_order_update( OrderDTO $dto ) {
}

if ( Status::PAID === $dto->get_status() ) {
$order_expiration = $this->should_refresh_order_expiration( $dto, $old_order )
? $this->refresh_order_expiration( $dto )
: null;

directorist_set_listing_featured( $dto->get_listing_id(), true );

// Publish the listing if it's pending
$listing = get_post( $dto->get_listing_id() );


if ( $listing && $order_expiration ) {
$this->update_listing_expiration( $listing, $order_expiration );
}

if ( $listing && 'publish' !== $listing->post_status ) {
directorist_set_listing_status( $dto->get_listing_id(), 'publish' );
}
} else {
} elseif ( ! directorist_order_repository()->listing_has_active_featured_entitlement( $dto->get_listing_id() ) ) {
directorist_set_listing_featured( $dto->get_listing_id(), false );
}
}

private function is_paid_transition( $old_order ): bool {
return ! $old_order || ! isset( $old_order->status ) || Status::PAID !== $old_order->status;
}

private function should_refresh_order_expiration( OrderDTO $order, $old_order ): bool {
return $this->is_paid_transition( $old_order ) || ! $order->is_initialized( 'expires_at' );
}

private function refresh_order_expiration( OrderDTO $order ) {
$featured_days = (int) get_directorist_option( 'featured_listing_time', 30 );
$order_expiration = directorist_now()->add_days( $featured_days );

$order->set_expires_at( $order_expiration );
$expiration_update = ( new OrderDTO() )
->set_id( $order->get_id() )
->set_expires_at( $order_expiration );

directorist_order_repository()->silent_update( $expiration_update );

return $order_expiration;
}

private function update_listing_expiration( $listing, $order_expiration ) {
if ( get_post_meta( $listing->ID, '_never_expire', true ) ) {
return;
}

$listing_expiration = get_post_meta( $listing->ID, '_expiry_date', true );
$listing_expiration_date = $listing_expiration
? date_create_from_format( 'Y-m-d H:i:s', $listing_expiration, wp_timezone() )
: false;

if ( $listing_expiration_date
&& $listing_expiration === $listing_expiration_date->format( 'Y-m-d H:i:s' )
&& $listing_expiration_date->getTimestamp() >= $order_expiration->getTimestamp()
) {
return;
}

update_post_meta( $listing->ID, '_expiry_date', $order_expiration->format( 'Y-m-d H:i:s' ) );
}

public function handle_payment_receipt_order_items( array $order_items, OrderDTO $order ) {
if ( ! $this->is_featured_order( $order ) ) {
return $order_items;
Expand Down Expand Up @@ -206,7 +247,7 @@ public function handle_checkout_product_name( string $product_name, OrderDTO $dt
}

public function is_featured_order( OrderDTO $order_dto ): bool {
if ( ! $order_dto->is_initialized( 'ref_type' ) || ! $order_dto->is_initialized( 'ref' ) || ! $order_dto->is_initialized( 'listing_id' ) ) {
if ( ! $order_dto->is_initialized( 'ref_type' ) || ! $order_dto->is_initialized( 'listing_id' ) ) {
return false;
}

Expand Down
20 changes: 16 additions & 4 deletions includes/classes/class-permalink.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,26 @@ public static function get_listing_permalink( $post_id = 0, $permalink = '' ) {
$permalink = get_the_permalink( $post_id );
}

$directory_slug = self::get_listing_directory_type_slug( $post_id );

$permalink = str_replace( '%' . ATBDP_DIRECTORY_TYPE . '%', $directory_slug, $permalink );

return $permalink;
}

/**
* Get the directory type slug for a listing.
*
* @param int $post_id Listing ID.
* @return string
*/
public static function get_listing_directory_type_slug( $post_id = 0 ) {
$directory_slug = '';
$directory_id = directorist_get_listing_directory( $post_id );

if ( $directory_id ) {
$directory_term = get_term( $directory_id, ATBDP_DIRECTORY_TYPE );
$directory_slug = $directory_term ? $directory_term->slug : '';
$directory_slug = ( $directory_term && ! is_wp_error( $directory_term ) ) ? $directory_term->slug : '';
}

if ( empty( $directory_slug ) ) {
Expand All @@ -49,9 +63,7 @@ public static function get_listing_permalink( $post_id = 0, $permalink = '' ) {
}
}

$permalink = str_replace( '%' . ATBDP_DIRECTORY_TYPE . '%', $directory_slug, $permalink );

return $permalink;
return $directory_slug;
}

public static function get_listing_slug() {
Expand Down
7 changes: 7 additions & 0 deletions includes/directorist-core-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ function directorist_is_guest_submission_enabled() {
return (bool) get_directorist_option( 'guest_listings', false );
}

/**
* @return bool
*/
function directorist_is_force_disabled_featured_listings() {
return (bool) apply_filters( 'directorist_is_force_disabled_featured_listings', false );
}

function directorist_is_featured_listing_enabled( array $context = [] ) {
return (bool) apply_filters( 'directorist_is_featured_listing_enabled', get_directorist_option( 'enable_featured_listing' ), $context );
}
Expand Down
4 changes: 3 additions & 1 deletion includes/enums/order/status.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

class Status {
const PENDING = 'pending';
const PREPAID = 'prepaid';
const PAID = 'paid';
const FAILED = 'failed';
const CANCELLED = 'cancelled';
Expand All @@ -16,6 +17,7 @@ class Status {
public static function all() {
return [
self::PENDING,
self::PREPAID,
self::PAID,
self::FAILED,
self::CANCELLED,
Expand All @@ -24,4 +26,4 @@ public static function all() {
self::UNPAID,
];
}
}
}
3 changes: 2 additions & 1 deletion includes/model/SingleListing.php
Original file line number Diff line number Diff line change
Expand Up @@ -1590,12 +1590,13 @@ public function user_avatar() {
$user_pro_pic = get_user_meta( $this->author_id, 'pro_pic', true );
$u_pro_pic = ! empty( $u_pro_pic ) ? wp_get_attachment_image_src( $u_pro_pic, 'thumbnail' ) : '';
$author_data = get_userdata( $this->author_id );
$directory_type = ATBDP_Permalink::get_listing_directory_type_slug( $this->id );

$author_first_name = ! empty( $author_data ) ? $author_data->first_name : '';
$author_last_name = ! empty( $author_data ) ? $author_data->last_name : '';

$args = [
'author_link' => ATBDP_Permalink::get_user_profile_page_link( $this->author_id ),
'author_link' => ATBDP_Permalink::get_user_profile_page_link( $this->author_id, $directory_type ),
'u_pro_pic' => $u_pro_pic,
'avatar_img' => get_avatar( $this->author_id, apply_filters( 'atbdp_avatar_size', 32 ) ),
'author_full_name' => $author_first_name . ' ' . $author_last_name,
Expand Down
1 change: 1 addition & 0 deletions includes/payments/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ function atbdp_get_payment_statuses() {
$statuses = [
'created' => __( "Created", 'directorist' ),
'pending' => __( "Pending", 'directorist' ),
'prepaid' => __( "Prepaid", 'directorist' ),
'completed' => __( "Completed", 'directorist' ),
'failed' => __( "Failed", 'directorist' ),
'cancelled' => __( "Cancelled", 'directorist' ),
Expand Down
43 changes: 42 additions & 1 deletion includes/repositories/order-repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Directorist\Helpers\DateTime;
use Directorist\DTO\Order\DTO;
use Directorist\Enums\Order\Status as OrderStatus;
use Directorist\Enums\Payment\Status as PaymentStatus;
use Directorist\Enums\Order\TaxType as OrderTaxType;
use Directorist\Enums\Order\DiscountType as OrderDiscountType;
use Directorist\DTO\Order\Read;
Expand Down Expand Up @@ -88,6 +89,46 @@ public function listing_has_paid_featured_order( int $listing_id ): bool {
return $order ? true : false;
}

public function listing_has_active_featured_entitlement( int $listing_id ): bool {
$orders = $this->get_query_builder()
->select( 'd_order.ref_type', 'd_order.created_at', 'd_order.expires_at' )
->where( 'd_order.listing_id', $listing_id )
->where( 'd_order.is_featured_listing', 1 )
->where( 'd_order.status', OrderStatus::PAID )
->get();

$current_time = directorist_now()->getTimestamp();
$featured_days = (int) get_directorist_option( 'featured_listing_time', 30 );
$is_active = false;

foreach ( $orders as $order ) {
if ( ! empty( $order->expires_at ) ) {
$is_active = ( new DateTime( $order->expires_at ) )->getTimestamp() > $current_time;
} elseif ( 'featured_listing' === $order->ref_type ) {
$is_active = ( new DateTime( $order->created_at ) )->add_days( $featured_days )->getTimestamp() > $current_time;
} else {
$is_active = true;
}

if ( $is_active ) {
break;
}
}

return (bool) apply_filters( 'directorist_listing_has_active_featured_entitlement', $is_active, $listing_id, $orders );
}

public function get_latest_paid_featured_order_by_listing_id( int $listing_id ) {
return $this->get_query_builder()
->select( 'd_order.created_at', 'd_order.expires_at' )
->where( 'd_order.listing_id', $listing_id )
->where( 'd_order.is_featured_listing', 1 )
->where( 'd_order.ref_type', 'featured_listing' )
->where( 'd_order.status', OrderStatus::PAID )
->order_by_desc( 'd_order.id' )
->first();
}

protected function get_orders( Builder $query, Read $dto ) {
$query = apply_filters( 'directorist_order_list_query', $query, $dto );

Expand Down Expand Up @@ -169,7 +210,7 @@ public function update( \Directorist\Utils\DTO $dto ) {

do_action( 'directorist_after_order_update', $this->to_dto( $updated_order ), $old_order );

if ( $dto->is_initialized( 'status' ) ) {
if ( $dto->is_initialized( 'status' ) && in_array( $dto->get_status(), PaymentStatus::all(), true ) ) {
( new PaymentRepository )->update_status_by_order_id( $dto->get_id(), $dto->get_status() );
}

Expand Down
Loading
Loading