Skip to content

Commit

Permalink
add date_modified_gmt to audit response
Browse files Browse the repository at this point in the history
  • Loading branch information
kilbot committed Apr 8, 2024
1 parent d5853a2 commit fa82092
Show file tree
Hide file tree
Showing 11 changed files with 343 additions and 129 deletions.
103 changes: 70 additions & 33 deletions includes/API/Customers_Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use WP_REST_Response;
use WP_User;
use WP_User_Query;
use WP_Error;

/**
* Product Tgas controller class.
Expand Down Expand Up @@ -172,7 +173,7 @@ public function wcpos_dispatch_request( $dispatch_result, WP_REST_Request $reque
$this->wcpos_register_wc_rest_api_hooks();
$params = $request->get_params();

// Optimised query for getting all product IDs
// Optimised query for getting all user IDs.
if ( isset( $params['posts_per_page'] ) && -1 == $params['posts_per_page'] && isset( $params['fields'] ) ) {
$dispatch_result = $this->wcpos_get_all_posts( $params['fields'] );
}
Expand All @@ -198,7 +199,7 @@ public function wcpos_register_wc_rest_api_hooks(): void {
public function wcpos_customer_response( WP_REST_Response $response, WP_User $user_data, WP_REST_Request $request ): WP_REST_Response {
$data = $response->get_data();

// Add the uuid to the response
// Add the uuid to the response.
$this->maybe_add_user_uuid( $user_data );

/*
Expand All @@ -221,7 +222,7 @@ function ( $meta ) {
}
);

// Convert to WC REST API expected format
// Convert to WC REST API expected format.
$data['meta_data'] = array_map(
function ( $meta ) {
return array(
Expand All @@ -236,7 +237,7 @@ function ( $meta ) {
Logger::log( 'Error getting customer meta data: ' . $e->getMessage() );
}

// Set any changes to the response data
// Set any changes to the response data.
$response->set_data( $data );
// $this->log_large_rest_response( $response, $user_data->ID );

Expand All @@ -250,38 +251,74 @@ function ( $meta ) {
* multisite would return all users from all sites, not just the current site.
* Also, querying by role is not as simple as querying by post type.
*
* @param array $fields
* @param array $fields Fields to return. Default is ID.
*
* @return array|WP_Error
*/
public function wcpos_get_all_posts( array $fields = array() ): array {
$args = array(
'fields' => 'ID', // Only return user IDs
);
$roles = 'all'; // @TODO: could be an array of roles, like ['customer', 'cashier']
global $wpdb;

if ( 'all' !== $roles ) {
$args['role__in'] = $roles;
}
$id_with_modified_date = array( 'id', 'date_modified_gmt' ) === $fields;

$user_query = new WP_User_Query( $args );
$args = array(
'fields' => array( 'ID', 'registered' ), // Return only the ID and registered date.
// 'role__in' => 'all', // @TODO: could be an array of roles, like ['customer', 'cashier']
);

/**
* The user query is too complex to do a direct sql query, eg: multisite would return all users from all sites,
* not just the current site. Also, querying by role is not as simple as querying by post type.
*
* For now we get all user ids and all 'last_update' meta values, then combine them into an array of objects.
*/
try {
$user_ids = $user_query->get_results();
$user_query = new WP_User_Query( $args );
$users = $user_query->get_results();
$last_updates = array();

if ( $id_with_modified_date ) {
$last_update_results = $wpdb->get_results(
"
SELECT user_id, meta_value
FROM $wpdb->usermeta
WHERE meta_key = 'last_update'",
OBJECT_K
);

foreach ( $last_update_results as $user_id => $meta ) {
$last_updates[ $user_id ] = $meta->meta_value;
}
}

return array_map( array( $this, 'wcpos_format_id' ), $user_ids );
// Merge user IDs with their corresponding 'last_updated' values or fallback to user_registered.
$user_data = array_map(
function ( $user ) use ( $last_updates, $id_with_modified_date ) {
$user_info = array( 'id' => (int) $user->ID );
if ( $id_with_modified_date ) {
if ( isset( $last_updates[ $user->ID ] ) ) {
$user_info['date_modified_gmt'] = wc_rest_prepare_date_response( $last_updates[ $user->ID ] );
} else {
$user_info['date_modified_gmt'] = wc_rest_prepare_date_response( $user->user_registered );
}
}
return $user_info;
},
$users
);

return $user_data;
} catch ( Exception $e ) {
Logger::log( 'Error fetching order IDs: ' . $e->getMessage() );

return new \WP_Error(
return new WP_Error(
'woocommerce_pos_rest_cannot_fetch',
'Error fetching customer IDs.',
array( 'status' => 500 )
);
}
}

/*
/**
* Filter arguments, before passing to WP_User_Query, when querying users via the REST API.
*
* @param array $prepared_args Array of arguments for WP_User_Query.
Expand All @@ -292,7 +329,7 @@ public function wcpos_get_all_posts( array $fields = array() ): array {
public function wcpos_customer_query( array $prepared_args, WP_REST_Request $request ): array {
$query_params = $request->get_query_params();

// add modified_after date_modified_gmt
// add modified_after date_modified_gmt.
if ( isset( $query_params['modified_after'] ) && '' !== $query_params['modified_after'] ) {
$timestamp = strtotime( $query_params['modified_after'] );
$prepared_args['meta_query'] = $this->wcpos_combine_meta_queries(
Expand All @@ -307,7 +344,7 @@ public function wcpos_customer_query( array $prepared_args, WP_REST_Request $req
);
}

// Handle orderby cases
// Handle orderby cases.
if ( isset( $query_params['orderby'] ) ) {
switch ( $query_params['orderby'] ) {
case 'first_name':
Expand Down Expand Up @@ -343,7 +380,7 @@ public function wcpos_customer_query( array $prepared_args, WP_REST_Request $req
}
}

// Handle search
// Handle search.
if ( isset( $query_params['search'] ) && ! empty( $query_params['search'] ) ) {
$search_keyword = $query_params['search'];

Expand All @@ -368,7 +405,7 @@ public function wcpos_customer_query( array $prepared_args, WP_REST_Request $req
'value' => $search_keyword,
'compare' => 'LIKE',
),
// WooCommerce billing fields
// WooCommerce billing fields.
array(
'key' => 'billing_first_name',
'value' => $search_keyword,
Expand Down Expand Up @@ -396,11 +433,11 @@ public function wcpos_customer_query( array $prepared_args, WP_REST_Request $req
),
);

// Combine the search meta_query with the existing meta_query
// Combine the search meta_query with the existing meta_query.
$prepared_args['meta_query'] = $this->wcpos_combine_meta_queries( $search_meta_query, $prepared_args['meta_query'] );
}

// Handle include/exclude
// Handle include/exclude.
if ( isset( $request['wcpos_include'] ) || isset( $request['wcpos_exclude'] ) ) {
add_action( 'pre_user_query', array( $this, 'wcpos_include_exclude_users_by_id' ) );
}
Expand All @@ -411,19 +448,19 @@ public function wcpos_customer_query( array $prepared_args, WP_REST_Request $req
/**
* Add user_email and user_login to the user query.
*
* @param WP_User_Query $query
* @param WP_User_Query $query The WP_User_Query instance (passed by reference).
*/
public function wcpos_search_user_table( $query ): void {
global $wpdb;

// Remove the hook
// Remove the hook.
remove_action( 'pre_user_query', array( $this, 'wcpos_search_user_table' ) );

// Get the search keyword
// Get the search keyword.
$query_params = $query->query_vars;
$search_keyword = $query_params['_wcpos_search'];

// Prepare the LIKE statement
// Prepare the LIKE statement.
$like_email = '%' . $wpdb->esc_like( $search_keyword ) . '%';
$like_login = '%' . $wpdb->esc_like( $search_keyword ) . '%';

Expand All @@ -435,10 +472,10 @@ public function wcpos_search_user_table( $query ): void {

$pattern = "/\(\s*\w+\.meta_key\s*=\s*'[^']+'\s*AND\s*\w+\.meta_value\s*LIKE\s*'[^']+'\s*\)(\s*OR\s*\(\s*\w+\.meta_key\s*=\s*'[^']+'\s*AND\s*\w+\.meta_value\s*LIKE\s*'[^']+'\s*\))*\s*/";

// Add the search keyword to the query
// Add the search keyword to the query.
$modified_where = preg_replace( $pattern, "$insertion$0", $query->query_where );
// Check if the replacement was successful and assign it back to query_where
// Check if the replacement was successful and assign it back to query_where.
if ( $modified_where !== $query->query_where ) {
$query->query_where = $modified_where;
}
Expand All @@ -447,22 +484,22 @@ public function wcpos_search_user_table( $query ): void {
/**
* Include or exclude users by ID.
*
* @param WP_User_Query $query
* @param WP_User_Query $query The WP_User_Query instance (passed by reference).
*/
public function wcpos_include_exclude_users_by_id( $query ) {
global $wpdb;
// Remove the hook
// Remove the hook.
remove_action( 'pre_user_query', array( $this, 'wcpos_include_exclude_users_by_id' ) );
// Handle 'wcpos_include'
// Handle 'wcpos_include'.
if ( ! empty( $this->wcpos_request['wcpos_include'] ) ) {
$include_ids = array_map( 'intval', (array) $this->wcpos_request['wcpos_include'] );
$ids_format = implode( ',', array_fill( 0, count( $include_ids ), '%d' ) );
$query->query_where .= $wpdb->prepare( " AND {$wpdb->users}.ID IN ($ids_format) ", $include_ids );
}
// Handle 'wcpos_exclude'
// Handle 'wcpos_exclude'.
if ( ! empty( $this->wcpos_request['wcpos_exclude'] ) ) {
$exclude_ids = array_map( 'intval', (array) $this->wcpos_request['wcpos_exclude'] );
$ids_format = implode( ',', array_fill( 0, count( $exclude_ids ), '%d' ) );
Expand Down
51 changes: 35 additions & 16 deletions includes/API/Orders_Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use WP_REST_Response;
use WP_REST_Server;
use Automattic\WooCommerce\Utilities\OrderUtil;
use WP_Error;

use const WCPOS\WooCommercePOS\PLUGIN_NAME;

Expand Down Expand Up @@ -595,34 +596,52 @@ public function wcpos_posts_where_order_include_exclude( string $where, $query )
/**
* Returns array of all order ids.
*
* @param array $fields
* @param array $fields Fields to return.
*
* @return array|WP_Error
*/
public function wcpos_get_all_posts( array $fields = array() ): array {
$args = array(
'limit' => -1,
'return' => 'ids',
'status' => array_keys( wc_get_order_statuses() ), // Get valid order statuses
global $wpdb;

$id_with_modified_date = array( 'id', 'date_modified_gmt' ) === $fields;

// Check if HPOS is enabled and custom orders table is used.
$hpos_enabled = class_exists( OrderUtil::class ) && OrderUtil::custom_orders_table_usage_is_enabled();
$sql = '';

$statuses = array_map(
function ( $status ) {
return "'$status'";
},
array_keys( wc_get_order_statuses() )
);

$order_query = new WC_Order_Query( $args );
if ( $hpos_enabled ) {
$select_fields = $id_with_modified_date ? 'id, date_updated_gmt as date_modified_gmt' : 'id';
$sql .= "SELECT DISTINCT {$select_fields} FROM {$wpdb->prefix}wc_orders WHERE type = 'shop_order'";
$sql .= ' AND status IN (' . implode( ',', $statuses ) . ')';

try {
$order_ids = $order_query->get_orders();
// Order by date_created_gmt DESC to maintain order consistency.
$sql .= " ORDER BY {$wpdb->prefix}wc_orders.date_created_gmt DESC";
} else {
$select_fields = $id_with_modified_date ? 'ID as id, post_modified_gmt as date_modified_gmt' : 'ID as id';
$sql .= "SELECT DISTINCT {$select_fields} FROM {$wpdb->posts} WHERE post_type = 'shop_order'";
$sql .= ' AND post_status IN (' . implode( ',', $statuses ) . ')';

return array_map( array( $this, 'wcpos_format_id' ), $order_ids );
} catch ( Exception $e ) {
Logger::log( 'Error fetching order IDs: ' . $e->getMessage() );
// Order by post_date DESC to maintain order consistency.
$sql .= " ORDER BY {$wpdb->posts}.post_date DESC";
}

return new \WP_Error(
'woocommerce_pos_rest_cannot_fetch',
'Error fetching order IDs.',
array( 'status' => 500 )
);
try {
$results = $wpdb->get_results( $sql );
return $this->wcpos_format_all_posts_response( $results );
} catch ( Exception $e ) {
Logger::log( 'Error fetching order data: ' . $e->getMessage() );
return new WP_Error( 'woocommerce_pos_rest_cannot_fetch', 'Error fetching order data.', array( 'status' => 500 ) );
}
}


/**
* Filters all query clauses at once.
* Covers the fields (SELECT), JOIN, WHERE, GROUP BY, ORDER BY, and LIMIT clauses.
Expand Down
12 changes: 9 additions & 3 deletions includes/API/Product_Categories_Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,15 @@ public function wcpos_get_all_posts( array $fields = array() ): array {
);

try {
$product_category_ids = get_terms( $args );

return array_map( array( $this, 'wcpos_format_id' ), $product_category_ids );
$results = get_terms( $args );

// Format the response.
return array_map(
function ( $item ) {
return array( 'id' => (int) $item );
},
$results
);
} catch ( Exception $e ) {
Logger::log( 'Error fetching product category IDs: ' . $e->getMessage() );

Expand Down
12 changes: 9 additions & 3 deletions includes/API/Product_Tags_Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,15 @@ public function wcpos_get_all_posts( array $fields = array() ): array {
);

try {
$product_tag_ids = get_terms( $args );

return array_map( array( $this, 'wcpos_format_id' ), $product_tag_ids );
$results = get_terms( $args );

// Format the response.
return array_map(
function ( $item ) {
return array( 'id' => (int) $item );
},
$results
);
} catch ( Exception $e ) {
Logger::log( 'Error fetching product tags IDs: ' . $e->getMessage() );

Expand Down
Loading

0 comments on commit fa82092

Please sign in to comment.