| Server IP : 146.59.209.152 / Your IP : 216.73.216.152 Web Server : Apache System : Linux webm009.cluster131.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64 User : monpetu ( 144298) PHP Version : 7.4.33 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/monpetu/www/ipprint/wp-content/plugins/cartflows/admin-core/api/ |
Upload File : |
<?php
/**
* CartFlows Flows Query.
*
* @package CartFlows
*/
namespace CartflowsAdmin\AdminCore\Api;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use CartflowsAdmin\AdminCore\Api\ApiBase;
/**
* Class Admin_Query.
*/
class Flows extends ApiBase {
/**
* Route base.
*
* @var string
*/
protected $rest_base = '/admin/flows/';
/**
* Instance
*
* @access private
* @var object Class object.
* @since 1.0.0
*/
private static $instance;
/**
* Initiator
*
* @since 1.0.0
* @return object initialized object of class.
*/
public static function get_instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Init Hooks.
*
* @since 1.0.0
* @return void
*/
public function register_routes() {
// Eg. http://example.local/wp-json/cartflows/v1/admin/flows/.
$namespace = $this->get_api_namespace();
register_rest_route(
$namespace,
$this->rest_base,
array(
array(
'methods' => 'POST', // WP_REST_Server::READABLE.
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => array(), // get_collection_params may use.
),
'schema' => array( $this, 'get_public_item_schema' ),
)
);
}
/**
* Get items
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response
*/
public function get_items( $request ) {
$post_status = 'any';
$post_count = 10;
if ( null !== $request->get_param( 'per_page' ) ) {
$post_count = min( absint( $request->get_param( 'per_page' ) ), 100 );
}
$args = array(
'post_type' => CARTFLOWS_FLOW_POST_TYPE,
'post_status' => $post_status,
'orderby' => 'ID',
);
// checking if store checkout is available and removing it from the list of flows.
$store_checkout_id = intval( \Cartflows_Helper::get_global_setting( '_cartflows_store_checkout' ) );
if ( 0 !== $store_checkout_id ) {
$args['post__not_in'] = array( $store_checkout_id );
}
if ( null !== $request->get_param( 'paged' ) ) {
$args['paged'] = absint( $request->get_param( 'paged' ) );
}
$mode = $request->get_param( 'mode' );
if ( null !== $mode ) {
if ( 'sandbox' === $mode ) {
$args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
array(
'key' => 'wcf-testing',
'value' => 'yes',
),
);
} else {
$args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
'relation' => 'OR',
array(
'key' => 'wcf-testing',
'value' => 'no',
),
array(
'key' => 'wcf-testing',
'compare' => 'NOT EXISTS',
),
);
}
}
if ( 'any' === $post_status ) {
if ( null !== $request->get_param( 's' ) ) {
$args['s'] = sanitize_text_field( $request->get_param( 's' ) );
}
if ( null !== $request->get_param( 'post_status' ) ) {
$status = $request->get_param( 'post_status' );
// Allowlist of valid post statuses for flows.
$allowed_statuses = array( 'active', 'inactive', 'publish', 'draft', 'pending', 'trash' );
if ( 'active' === $status ) {
$args['post_status'] = 'publish';
} elseif ( 'inactive' === $status ) {
$args['post_status'] = 'draft';
} elseif ( in_array( $status, $allowed_statuses, true ) ) {
$args['post_status'] = sanitize_text_field( $status );
}
// Invalid statuses are silently ignored, falling through to the default below.
}
if ( ! isset( $args['post_status'] ) || null === $request->get_param( 'post_status' ) ) {
$args['post_status'] = 'publish';
}
$start_date = $request->get_param( 'start_date' );
$end_date = $request->get_param( 'end_date' );
if ( ! empty( $start_date ) && ! empty( $end_date ) ) {
$args['date_query'] = array(
array(
'after' => sanitize_text_field( $start_date ) . ' 00:00:00',
'before' => sanitize_text_field( $end_date ) . ' 23:59:59',
'inclusive' => true,
'column' => 'post_date',
),
);
}
}
if ( ! empty( $post_count ) ) {
$args['posts_per_page'] = $post_count;
}
$result = new \WP_Query( $args );
$data = array(
'items' => array(),
'pagination' => array(),
);
// On free, compute revenue for the whole page in a single grouped query instead of one query per flow.
$is_free_revenue = ! _is_cartflows_pro() && ! is_wcf_pro_plan();
$revenue_map = array();
$zero_revenue = str_replace( ' ', '', wc_price( 0 ) );
if ( $is_free_revenue && ! empty( $result->posts ) ) {
$revenue_map = $this->get_flows_revenue( wp_list_pluck( $result->posts, 'ID' ) );
}
if ( $result->have_posts() ) {
while ( $result->have_posts() ) {
$result->the_post();
global $post;
$post_data = (array) $post;
// Modify the date Format just to display it.
$post_data['post_modified'] = date_format( date_create( $post_data['post_modified'] ), 'yy/m/d' );
$post_data['post_status'] = ucwords( $post_data['post_status'] );
$first_step_url = \Cartflows_Flow_Post_Type::get_instance()->get_first_step_url( $post );
$view = $first_step_url ? $first_step_url : '#';
$edit = admin_url( 'admin.php?page=cartflows&path=flows&action=wcf-edit-flow&flow_id=' . $post->ID );
$delete = '#';
$clone = '#';
$export = '#';
$post_data['flow_test_mode'] = ( 'yes' === wcf()->options->get_flow_meta_value( $post->ID, 'wcf-testing' ) ) ? true : false;
$flow_steps = get_post_meta( $post->ID, 'wcf-steps', true );
$post_data['step_count'] = is_array( $flow_steps ) ? count( $flow_steps ) : 0;
$post_data['actions'] = array(
'view' => array(
'action' => 'edit',
'class' => '',
'attr' => array( 'target' => '_blank' ),
'text' => __( 'View', 'cartflows' ),
'link' => $view,
),
'edit' => array(
'action' => 'edit',
'class' => '',
'attr' => array(),
'text' => __( 'Edit', 'cartflows' ),
'link' => $edit,
),
'duplicate' => array(
'action' => 'clone',
'attr' => array(),
'class' => '',
'text' => __( 'Duplicate', 'cartflows' ),
'link' => $clone,
),
'export' => array(
'action' => 'export',
'attr' => array(),
'class' => '',
'text' => __( 'Export', 'cartflows' ),
'link' => $export,
),
'delete' => array(
'action' => 'delete',
'attr' => array(),
'class' => '',
'text' => __( 'Delete', 'cartflows' ),
'link' => $delete,
),
);
// Fetch the revenue only for free version for the PRO it will be fetched and added by the filter later in the code.
if ( $is_free_revenue ) {
$post_data['revenue'] = isset( $revenue_map[ $post->ID ] ) ? $revenue_map[ $post->ID ] : $zero_revenue;
}
$data['items'][] = $post_data;
}
}
$data['found_posts'] = $result->found_posts;
$data['post_status'] = isset( $post_data['post_status'] ) ? $post_data['post_status'] : $args['post_status'];
$data['active_flows_count'] = intval( wp_count_posts( CARTFLOWS_FLOW_POST_TYPE )->publish );
$data['trash_flows_count'] = intval( wp_count_posts( CARTFLOWS_FLOW_POST_TYPE )->trash );
$data['draft_flows_count'] = intval( wp_count_posts( CARTFLOWS_FLOW_POST_TYPE )->draft );
$data['pagination'] = array(
'found_posts' => $result->found_posts,
'paged' => $result->query['paged'],
'max_pages' => $result->max_num_pages,
);
// Reducing count of active_flows_count if store checkout is set.
if ( 0 !== $store_checkout_id ) {
$data['active_flows_count']--;
}
wp_reset_postdata();
$data['status'] = true;
// Retrieve the revenue data from the PRO to display it on the flow listing page.
if ( _is_cartflows_pro() && is_wcf_pro_plan() ) {
$data = apply_filters( 'cartflows_admin_flows_page_data', $data );
}
if ( ! $result->have_posts() ) {
$data['status'] = false;
$response = new \WP_REST_Response( $data );
$response->set_status( 200 );
return $response;
}
$response = new \WP_REST_Response( $data );
$response->set_status( 200 );
return $response;
}
/**
* Check whether a given request has permission to read notes.
*
* @param WP_REST_Request $request Full details about the request.
* @return WP_Error|boolean
*/
public function get_items_permissions_check( $request ) {
if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
return new \WP_Error( 'cartflows_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'cartflows' ), array( 'status' => rest_authorization_required_code() ) );
}
return true;
}
/**
* Get revenue of flow.
*
* @param int $flow_id flow id.
* @return int
*/
public function get_per_flow_revenue( $flow_id ) {
$gross_sale = 0;
// Return if WooCommerce is not active.
if ( ! wcf()->is_woo_active ) {
return $gross_sale;
}
// Fetch primary orders: Checkout, Order Bumps.
$args = array(
'status' => array( 'completed', 'processing', 'cancelled' ), // Accepts a string: one of 'pending', 'processing', 'on-hold', 'completed', 'refunded, 'failed', 'cancelled', or a custom order status.
'meta_key' => '_wcf_flow_id', // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
'meta_value' => $flow_id, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value
'meta_compare' => '=', // Possible values are ‘=’, ‘!=’, ‘>’, ‘>=’, ‘<‘, ‘<=’, ‘LIKE’, ‘NOT LIKE’, ‘IN’, ‘NOT IN’, ‘BETWEEN’, ‘NOT BETWEEN’, ‘EXISTS’ (only in WP >= 3.5), and ‘NOT EXISTS’ (also only in WP >= 3.5). Values ‘REGEXP’, ‘NOT REGEXP’ and ‘RLIKE’ were added in WordPress 3.7. Default value is ‘=’.
'return' => 'ids', // Accepts a string: 'ids' or 'objects'. Default: 'objects'.
);
$orders = wc_get_orders( $args );
if ( ! empty( $orders ) && is_array( $orders ) ) {
foreach ( $orders as $order_id ) {
$order = wc_get_order( $order_id );
$user_id = $order->get_user_id();
// skip the orders which are placed by the user whose user role is Administrator.
if ( $user_id && user_can( $user_id, 'cartflows_manage_flows_steps' ) ) {
continue;
}
$order_total = $order->get_total();
if ( ! $order->has_status( 'cancelled' ) ) {
$gross_sale += (float) $order_total;
}
}
}
return str_replace( ' ', '', wc_price( $gross_sale ) );
}
/**
* Get gross revenue for many flows in a single grouped query.
*
* Replaces the per-flow N+1 lookup; sums completed/processing order totals
* grouped by `_wcf_flow_id`, excluding orders placed by flow managers.
*
* @since 3.1.3
*
* @param int[] $flow_ids Flow IDs to fetch revenue for.
* @return array<int,string> Map of flow ID => formatted revenue string.
*/
public function get_flows_revenue( $flow_ids ) {
$revenue = array();
// Return if WooCommerce is not active.
if ( ! function_exists( 'WC' ) ) {
return $revenue;
}
$flow_ids = array_filter( array_map( 'absint', (array) $flow_ids ) );
if ( empty( $flow_ids ) ) {
return $revenue;
}
global $wpdb;
// Exclude orders placed by users who can manage flows (e.g. internal test orders).
$excluded_user_ids = array_map(
'absint',
get_users(
array(
'capability' => 'cartflows_manage_flows_steps',
'fields' => 'ID',
)
)
);
$decimals = wc_get_price_decimals();
$flow_placeholder = implode( ',', array_fill( 0, count( $flow_ids ), '%d' ) );
$query_args = $flow_ids;
$is_hpos = class_exists( '\Automattic\WooCommerce\Utilities\OrderUtil' ) && \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled();
if ( $is_hpos ) {
$order_table = $wpdb->prefix . 'wc_orders';
$order_meta_table = $wpdb->prefix . 'wc_orders_meta';
$exclude_clause = '';
if ( ! empty( $excluded_user_ids ) ) {
$exclude_clause = ' AND o.customer_id NOT IN ( ' . implode( ',', array_fill( 0, count( $excluded_user_ids ), '%d' ) ) . ' )';
$query_args = array_merge( $query_args, $excluded_user_ids );
}
//phpcs:disable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
$rows = $wpdb->get_results(
$wpdb->prepare(
"SELECT om.meta_value AS flow_id, ROUND( SUM( o.total_amount ), $decimals ) AS revenue
FROM $order_table o
INNER JOIN $order_meta_table om ON o.id = om.order_id AND om.meta_key = '_wcf_flow_id'
WHERE o.type = 'shop_order'
AND o.status IN ( 'wc-completed', 'wc-processing' )
AND om.meta_value IN ( $flow_placeholder )
$exclude_clause
GROUP BY om.meta_value",
$query_args
)
);
//phpcs:enable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
} else {
$order_table = $wpdb->prefix . 'posts';
$order_meta_table = $wpdb->prefix . 'postmeta';
$exclude_clause = '';
if ( ! empty( $excluded_user_ids ) ) {
$exclude_clause = ' AND ( cu.meta_value IS NULL OR cu.meta_value NOT IN ( ' . implode( ',', array_fill( 0, count( $excluded_user_ids ), '%d' ) ) . ' ) )';
$query_args = array_merge( $query_args, $excluded_user_ids );
}
//phpcs:disable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
$rows = $wpdb->get_results(
$wpdb->prepare(
"SELECT om.meta_value AS flow_id, ROUND( SUM( m.meta_value ), $decimals ) AS revenue
FROM $order_table o
INNER JOIN $order_meta_table m ON o.ID = m.post_id AND m.meta_key = '_order_total'
INNER JOIN $order_meta_table om ON o.ID = om.post_id AND om.meta_key = '_wcf_flow_id'
LEFT JOIN $order_meta_table cu ON o.ID = cu.post_id AND cu.meta_key = '_customer_user'
WHERE o.post_type = 'shop_order'
AND o.post_status IN ( 'wc-completed', 'wc-processing' )
AND om.meta_value IN ( $flow_placeholder )
$exclude_clause
GROUP BY om.meta_value",
$query_args
)
);
//phpcs:enable WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
}
if ( ! empty( $rows ) && is_array( $rows ) ) {
foreach ( $rows as $row ) {
$revenue[ absint( $row->flow_id ) ] = str_replace( ' ', '', wc_price( (float) $row->revenue ) );
}
}
return $revenue;
}
}