/** * WooCommerce Customer Functions * * Functions for customers. * * @package WooCommerce\Functions * @version 2.2.0 */ use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore; use Automattic\WooCommerce\Utilities\OrderUtil; defined( 'ABSPATH' ) || exit; /** * Prevent any user who cannot 'edit_posts' (subscribers, customers etc) from seeing the admin bar. * * Note: get_option( 'woocommerce_lock_down_admin', true ) is a deprecated option here for backwards compatibility. Defaults to true. * * @param bool $show_admin_bar If should display admin bar. * @return bool */ function wc_disable_admin_bar( $show_admin_bar ) { /** * Controls whether the WooCommerce admin bar should be disabled. * * @since 3.0.0 * * @param bool $enabled */ if ( apply_filters( 'woocommerce_disable_admin_bar', true ) && ! ( current_user_can( 'edit_posts' ) || current_user_can( 'manage_woocommerce' ) ) ) { $show_admin_bar = false; } return $show_admin_bar; } add_filter( 'show_admin_bar', 'wc_disable_admin_bar', 10, 1 ); // phpcs:ignore WordPress.VIP.AdminBarRemoval.RemovalDetected if ( ! function_exists( 'wc_create_new_customer' ) ) { /** * Create a new customer. * * @param string $email Customer email. * @param string $username Customer username. * @param string $password Customer password. * @param array $args List of arguments to pass to `wp_insert_user()`. * @return int|WP_Error Returns WP_Error on failure, Int (user ID) on success. */ function wc_create_new_customer( $email, $username = '', $password = '', $args = array() ) { if ( empty( $email ) || ! is_email( $email ) ) { return new WP_Error( 'registration-error-invalid-email', __( 'Please provide a valid email address.', 'woocommerce' ) ); } if ( email_exists( $email ) ) { return new WP_Error( 'registration-error-email-exists', apply_filters( 'woocommerce_registration_error_email_exists', __( 'An account is already registered with your email address. Please log in.', 'woocommerce' ), $email ) ); } if ( 'yes' === get_option( 'woocommerce_registration_generate_username', 'yes' ) && empty( $username ) ) { $username = wc_create_new_customer_username( $email, $args ); } $username = sanitize_user( $username ); if ( empty( $username ) || ! validate_username( $username ) ) { return new WP_Error( 'registration-error-invalid-username', __( 'Please enter a valid account username.', 'woocommerce' ) ); } if ( username_exists( $username ) ) { return new WP_Error( 'registration-error-username-exists', __( 'An account is already registered with that username. Please choose another.', 'woocommerce' ) ); } // Handle password creation. $password_generated = false; if ( 'yes' === get_option( 'woocommerce_registration_generate_password' ) && empty( $password ) ) { $password = wp_generate_password(); $password_generated = true; } if ( empty( $password ) ) { return new WP_Error( 'registration-error-missing-password', __( 'Please enter an account password.', 'woocommerce' ) ); } // Use WP_Error to handle registration errors. $errors = new WP_Error(); do_action( 'woocommerce_register_post', $username, $email, $errors ); $errors = apply_filters( 'woocommerce_registration_errors', $errors, $username, $email ); if ( $errors->get_error_code() ) { return $errors; } $new_customer_data = apply_filters( 'woocommerce_new_customer_data', array_merge( $args, array( 'user_login' => $username, 'user_pass' => $password, 'user_email' => $email, 'role' => 'customer', ) ) ); $customer_id = wp_insert_user( $new_customer_data ); if ( is_wp_error( $customer_id ) ) { return $customer_id; } do_action( 'woocommerce_created_customer', $customer_id, $new_customer_data, $password_generated ); return $customer_id; } } /** * Create a unique username for a new customer. * * @since 3.6.0 * @param string $email New customer email address. * @param array $new_user_args Array of new user args, maybe including first and last names. * @param string $suffix Append string to username to make it unique. * @return string Generated username. */ function wc_create_new_customer_username( $email, $new_user_args = array(), $suffix = '' ) { $username_parts = array(); if ( isset( $new_user_args['first_name'] ) ) { $username_parts[] = sanitize_user( $new_user_args['first_name'], true ); } if ( isset( $new_user_args['last_name'] ) ) { $username_parts[] = sanitize_user( $new_user_args['last_name'], true ); } // Remove empty parts. $username_parts = array_filter( $username_parts ); // If there are no parts, e.g. name had unicode chars, or was not provided, fallback to email. if ( empty( $username_parts ) ) { $email_parts = explode( '@', $email ); $email_username = $email_parts[0]; // Exclude common prefixes. if ( in_array( $email_username, array( 'sales', 'hello', 'mail', 'contact', 'info', ), true ) ) { // Get the domain part. $email_username = $email_parts[1]; } $username_parts[] = sanitize_user( $email_username, true ); } $username = wc_strtolower( implode( '.', $username_parts ) ); if ( $suffix ) { $username .= $suffix; } /** * WordPress 4.4 - filters the list of blocked usernames. * * @since 3.7.0 * @param array $usernames Array of blocked usernames. */ $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() ); // Stop illegal logins and generate a new random username. if ( in_array( strtolower( $username ), array_map( 'strtolower', $illegal_logins ), true ) ) { $new_args = array(); /** * Filter generated customer username. * * @since 3.7.0 * @param string $username Generated username. * @param string $email New customer email address. * @param array $new_user_args Array of new user args, maybe including first and last names. * @param string $suffix Append string to username to make it unique. */ $new_args['first_name'] = apply_filters( 'woocommerce_generated_customer_username', 'woo_user_' . zeroise( wp_rand( 0, 9999 ), 4 ), $email, $new_user_args, $suffix ); return wc_create_new_customer_username( $email, $new_args, $suffix ); } if ( username_exists( $username ) ) { // Generate something unique to append to the username in case of a conflict with another user. $suffix = '-' . zeroise( wp_rand( 0, 9999 ), 4 ); return wc_create_new_customer_username( $email, $new_user_args, $suffix ); } /** * Filter new customer username. * * @since 3.7.0 * @param string $username Customer username. * @param string $email New customer email address. * @param array $new_user_args Array of new user args, maybe including first and last names. * @param string $suffix Append string to username to make it unique. */ return apply_filters( 'woocommerce_new_customer_username', $username, $email, $new_user_args, $suffix ); } /** * Login a customer (set auth cookie and set global user object). * * @param int $customer_id Customer ID. */ function wc_set_customer_auth_cookie( $customer_id ) { wp_set_current_user( $customer_id ); wp_set_auth_cookie( $customer_id, true ); // Update session. WC()->session->init_session_cookie(); } /** * Get past orders (by email) and update them. * * @param int $customer_id Customer ID. * @return int */ function wc_update_new_customer_past_orders( $customer_id ) { $linked = 0; $complete = 0; $customer = get_user_by( 'id', absint( $customer_id ) ); $customer_orders = wc_get_orders( array( 'limit' => -1, 'customer' => array( array( 0, $customer->user_email ) ), 'return' => 'ids', ) ); if ( ! empty( $customer_orders ) ) { foreach ( $customer_orders as $order_id ) { $order = wc_get_order( $order_id ); if ( ! $order ) { continue; } $order->set_customer_id( $customer->ID ); $order->save(); if ( $order->has_downloadable_item() ) { $data_store = WC_Data_Store::load( 'customer-download' ); $data_store->delete_by_order_id( $order->get_id() ); wc_downloadable_product_permissions( $order->get_id(), true ); } do_action( 'woocommerce_update_new_customer_past_order', $order_id, $customer ); if ( $order->get_status() === 'wc-completed' ) { $complete++; } $linked++; } } if ( $complete ) { update_user_meta( $customer_id, 'paying_customer', 1 ); update_user_meta( $customer_id, '_order_count', '' ); update_user_meta( $customer_id, '_money_spent', '' ); delete_user_meta( $customer_id, '_last_order' ); } return $linked; } /** * Order payment completed - This is a paying customer. * * @param int $order_id Order ID. */ function wc_paying_customer( $order_id ) { $order = wc_get_order( $order_id ); $customer_id = $order->get_customer_id(); if ( $customer_id > 0 && 'shop_order_refund' !== $order->get_type() ) { $customer = new WC_Customer( $customer_id ); if ( ! $customer->get_is_paying_customer() ) { $customer->set_is_paying_customer( true ); $customer->save(); } } } add_action( 'woocommerce_payment_complete', 'wc_paying_customer' ); add_action( 'woocommerce_order_status_completed', 'wc_paying_customer' ); /** * Checks if a user (by email or ID or both) has bought an item. * * @param string $customer_email Customer email to check. * @param int $user_id User ID to check. * @param int $product_id Product ID to check. * @return bool */ function wc_customer_bought_product( $customer_email, $user_id, $product_id ) { global $wpdb; $result = apply_filters( 'woocommerce_pre_customer_bought_product', null, $customer_email, $user_id, $product_id ); if ( null !== $result ) { return $result; } $transient_name = 'wc_customer_bought_product_' . md5( $customer_email . $user_id ); $transient_version = WC_Cache_Helper::get_transient_version( 'orders' ); $transient_value = get_transient( $transient_name ); if ( isset( $transient_value['value'], $transient_value['version'] ) && $transient_value['version'] === $transient_version ) { $result = $transient_value['value']; } else { $customer_data = array( $user_id ); if ( $user_id ) { $user = get_user_by( 'id', $user_id ); if ( isset( $user->user_email ) ) { $customer_data[] = $user->user_email; } } if ( is_email( $customer_email ) ) { $customer_data[] = $customer_email; } $customer_data = array_map( 'esc_sql', array_filter( array_unique( $customer_data ) ) ); $statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() ); if ( count( $customer_data ) === 0 ) { return false; } if ( OrderUtil::custom_orders_table_usage_is_enabled() ) { $statuses = array_map( function ( $status ) { return "wc-$status"; }, $statuses ); $order_table = OrdersTableDataStore::get_orders_table_name(); $user_id_clause = ''; if ( $user_id ) { $user_id_clause = 'OR o.customer_id = ' . absint( $user_id ); } $sql = " SELECT im.meta_value FROM $order_table AS o INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON o.id = i.order_id INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id WHERE o.status IN ('" . implode( "','", $statuses ) . "') AND im.meta_key IN ('_product_id', '_variation_id' ) AND im.meta_value != 0 AND ( o.billing_email IN ('" . implode( "','", $customer_data ) . "') $user_id_clause ) "; $result = $wpdb->get_col( $sql ); } else { $result = $wpdb->get_col( " SELECT im.meta_value FROM {$wpdb->posts} AS p INNER JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON p.ID = i.order_id INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' ) AND pm.meta_key IN ( '_billing_email', '_customer_user' ) AND im.meta_key IN ( '_product_id', '_variation_id' ) AND im.meta_value != 0 AND pm.meta_value IN ( '" . implode( "','", $customer_data ) . "' ) " ); // WPCS: unprepared SQL ok. } $result = array_map( 'absint', $result ); $transient_value = array( 'version' => $transient_version, 'value' => $result, ); set_transient( $transient_name, $transient_value, DAY_IN_SECONDS * 30 ); } return in_array( absint( $product_id ), $result, true ); } /** * Checks if the current user has a role. * * @param string $role The role. * @return bool */ function wc_current_user_has_role( $role ) { return wc_user_has_role( wp_get_current_user(), $role ); } /** * Checks if a user has a role. * * @param int|\WP_User $user The user. * @param string $role The role. * @return bool */ function wc_user_has_role( $user, $role ) { if ( ! is_object( $user ) ) { $user = get_userdata( $user ); } if ( ! $user || ! $user->exists() ) { return false; } return in_array( $role, $user->roles, true ); } /** * Checks if a user has a certain capability. * * @param array $allcaps All capabilities. * @param array $caps Capabilities. * @param array $args Arguments. * * @return array The filtered array of all capabilities. */ function wc_customer_has_capability( $allcaps, $caps, $args ) { if ( isset( $caps[0] ) ) { switch ( $caps[0] ) { case 'view_order': $user_id = intval( $args[1] ); $order = wc_get_order( $args[2] ); if ( $order && $user_id === $order->get_user_id() ) { $allcaps['view_order'] = true; } break; case 'pay_for_order': $user_id = intval( $args[1] ); $order_id = isset( $args[2] ) ? $args[2] : null; // When no order ID, we assume it's a new order // and thus, customer can pay for it. if ( ! $order_id ) { $allcaps['pay_for_order'] = true; break; } $order = wc_get_order( $order_id ); if ( $order && ( $user_id === $order->get_user_id() || ! $order->get_user_id() ) ) { $allcaps['pay_for_order'] = true; } break; case 'order_again': $user_id = intval( $args[1] ); $order = wc_get_order( $args[2] ); if ( $order && $user_id === $order->get_user_id() ) { $allcaps['order_again'] = true; } break; case 'cancel_order': $user_id = intval( $args[1] ); $order = wc_get_order( $args[2] ); if ( $order && $user_id === $order->get_user_id() ) { $allcaps['cancel_order'] = true; } break; case 'download_file': $user_id = intval( $args[1] ); $download = $args[2]; if ( $download && $user_id === $download->get_user_id() ) { $allcaps['download_file'] = true; } break; } } return $allcaps; } add_filter( 'user_has_cap', 'wc_customer_has_capability', 10, 3 ); /** * Safe way of allowing shop managers restricted capabilities that will remove * access to the capabilities if WooCommerce is deactivated. * * @since 3.5.4 * @param bool[] $allcaps Array of key/value pairs where keys represent a capability name and boolean values * represent whether the user has that capability. * @param string[] $caps Required primitive capabilities for the requested capability. * @param array $args Arguments that accompany the requested capability check. * @param WP_User $user The user object. * @return bool[] */ function wc_shop_manager_has_capability( $allcaps, $caps, $args, $user ) { if ( wc_user_has_role( $user, 'shop_manager' ) ) { // @see wc_modify_map_meta_cap, which limits editing to customers. $allcaps['edit_users'] = true; } return $allcaps; } add_filter( 'user_has_cap', 'wc_shop_manager_has_capability', 10, 4 ); /** * Modify the list of editable roles to prevent non-admin adding admin users. * * @param array $roles Roles. * @return array */ function wc_modify_editable_roles( $roles ) { if ( is_multisite() && is_super_admin() ) { return $roles; } if ( ! wc_current_user_has_role( 'administrator' ) ) { unset( $roles['administrator'] ); if ( wc_current_user_has_role( 'shop_manager' ) ) { $shop_manager_editable_roles = apply_filters( 'woocommerce_shop_manager_editable_roles', array( 'customer' ) ); return array_intersect_key( $roles, array_flip( $shop_manager_editable_roles ) ); } } return $roles; } add_filter( 'editable_roles', 'wc_modify_editable_roles' ); /** * Modify capabilities to prevent non-admin users editing admin users. * * $args[0] will be the user being edited in this case. * * @param array $caps Array of caps. * @param string $cap Name of the cap we are checking. * @param int $user_id ID of the user being checked against. * @param array $args Arguments. * @return array */ function wc_modify_map_meta_cap( $caps, $cap, $user_id, $args ) { if ( is_multisite() && is_super_admin() ) { return $caps; } switch ( $cap ) { case 'edit_user': case 'remove_user': case 'promote_user': case 'delete_user': if ( ! isset( $args[0] ) || $args[0] === $user_id ) { break; } else { if ( ! wc_current_user_has_role( 'administrator' ) ) { if ( wc_user_has_role( $args[0], 'administrator' ) ) { $caps[] = 'do_not_allow'; } elseif ( wc_current_user_has_role( 'shop_manager' ) ) { // Shop managers can only edit customer info. $userdata = get_userdata( $args[0] ); $shop_manager_editable_roles = apply_filters( 'woocommerce_shop_manager_editable_roles', array( 'customer' ) ); if ( property_exists( $userdata, 'roles' ) && ! empty( $userdata->roles ) && ! array_intersect( $userdata->roles, $shop_manager_editable_roles ) ) { $caps[] = 'do_not_allow'; } } } } break; } return $caps; } add_filter( 'map_meta_cap', 'wc_modify_map_meta_cap', 10, 4 ); /** * Get customer download permissions from the database. * * @param int $customer_id Customer/User ID. * @return array */ function wc_get_customer_download_permissions( $customer_id ) { $data_store = WC_Data_Store::load( 'customer-download' ); return apply_filters( 'woocommerce_permission_list', $data_store->get_downloads_for_customer( $customer_id ), $customer_id ); } /** * Get customer available downloads. * * @param int $customer_id Customer/User ID. * @return array */ function wc_get_customer_available_downloads( $customer_id ) { $downloads = array(); $_product = null; $order = null; $file_number = 0; // Get results from valid orders only. $results = wc_get_customer_download_permissions( $customer_id ); if ( $results ) { foreach ( $results as $result ) { $order_id = intval( $result->order_id ); if ( ! $order || $order->get_id() !== $order_id ) { // New order. $order = wc_get_order( $order_id ); $_product = null; } // Make sure the order exists for this download. if ( ! $order ) { continue; } // Check if downloads are permitted. if ( ! $order->is_download_permitted() ) { continue; } $product_id = intval( $result->product_id ); if ( ! $_product || $_product->get_id() !== $product_id ) { // New product. $file_number = 0; $_product = wc_get_product( $product_id ); } // Check product exists and has the file. if ( ! $_product || ! $_product->exists() || ! $_product->has_file( $result->download_id ) ) { continue; } $download_file = $_product->get_file( $result->download_id ); // If the downloadable file has been disabled (it may be located in an untrusted location) then do not return it. if ( ! $download_file->get_enabled() ) { continue; } // Download name will be 'Product Name' for products with a single downloadable file, and 'Product Name - File X' for products with multiple files. $download_name = apply_filters( 'woocommerce_downloadable_product_name', $download_file['name'], $_product, $result->download_id, $file_number ); $downloads[] = array( 'download_url' => add_query_arg( array( 'download_file' => $product_id, 'order' => $result->order_key, 'email' => rawurlencode( $result->user_email ), 'key' => $result->download_id, ), home_url( '/' ) ), 'download_id' => $result->download_id, 'product_id' => $_product->get_id(), 'product_name' => $_product->get_name(), 'product_url' => $_product->is_visible() ? $_product->get_permalink() : '', // Since 3.3.0. 'download_name' => $download_name, 'order_id' => $order->get_id(), 'order_key' => $order->get_order_key(), 'downloads_remaining' => $result->downloads_remaining, 'access_expires' => $result->access_expires, 'file' => array( 'name' => $download_file->get_name(), 'file' => $download_file->get_file(), ), ); $file_number++; } } return apply_filters( 'woocommerce_customer_available_downloads', $downloads, $customer_id ); } /** * Get total spent by customer. * * @param int $user_id User ID. * @return string */ function wc_get_customer_total_spent( $user_id ) { $customer = new WC_Customer( $user_id ); return $customer->get_total_spent(); } /** * Get total orders by customer. * * @param int $user_id User ID. * @return int */ function wc_get_customer_order_count( $user_id ) { $customer = new WC_Customer( $user_id ); return $customer->get_order_count(); } /** * Reset _customer_user on orders when a user is deleted. * * @param int $user_id User ID. */ function wc_reset_order_customer_id_on_deleted_user( $user_id ) { global $wpdb; if ( OrderUtil::custom_orders_table_usage_is_enabled() ) { $order_table_ds = wc_get_container()->get( OrdersTableDataStore::class ); $order_table = $order_table_ds::get_orders_table_name(); $wpdb->update( $order_table, array( 'customer_id' => 0, 'date_updated_gmt' => current_time( 'mysql', true ), ), array( 'customer_id' => $user_id, ), array( '%d', '%s', ), array( '%d', ) ); } if ( ! OrderUtil::custom_orders_table_usage_is_enabled() || OrderUtil::is_custom_order_tables_in_sync() ) { $wpdb->update( $wpdb->postmeta, array( 'meta_value' => 0, //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value ), array( 'meta_key' => '_customer_user', //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key 'meta_value' => $user_id, //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value ) ); } } add_action( 'deleted_user', 'wc_reset_order_customer_id_on_deleted_user' ); /** * Get review verification status. * * @param int $comment_id Comment ID. * @return bool */ function wc_review_is_from_verified_owner( $comment_id ) { $verified = get_comment_meta( $comment_id, 'verified', true ); return '' === $verified ? WC_Comments::add_comment_purchase_verification( $comment_id ) : (bool) $verified; } /** * Disable author archives for customers. * * @since 2.5.0 */ function wc_disable_author_archives_for_customers() { global $author; if ( is_author() ) { $user = get_user_by( 'id', $author ); if ( user_can( $user, 'customer' ) && ! user_can( $user, 'edit_posts' ) ) { wp_safe_redirect( wc_get_page_permalink( 'shop' ) ); exit; } } } add_action( 'template_redirect', 'wc_disable_author_archives_for_customers' ); /** * Hooks into the `profile_update` hook to set the user last updated timestamp. * * @since 2.6.0 * @param int $user_id The user that was updated. * @param array $old The profile fields pre-change. */ function wc_update_profile_last_update_time( $user_id, $old ) { wc_set_user_last_update_time( $user_id ); } add_action( 'profile_update', 'wc_update_profile_last_update_time', 10, 2 ); /** * Hooks into the update user meta function to set the user last updated timestamp. * * @since 2.6.0 * @param int $meta_id ID of the meta object that was changed. * @param int $user_id The user that was updated. * @param string $meta_key Name of the meta key that was changed. * @param mixed $_meta_value Value of the meta that was changed. */ function wc_meta_update_last_update_time( $meta_id, $user_id, $meta_key, $_meta_value ) { $keys_to_track = apply_filters( 'woocommerce_user_last_update_fields', array( 'first_name', 'last_name' ) ); $update_time = in_array( $meta_key, $keys_to_track, true ) ? true : false; $update_time = 'billing_' === substr( $meta_key, 0, 8 ) ? true : $update_time; $update_time = 'shipping_' === substr( $meta_key, 0, 9 ) ? true : $update_time; if ( $update_time ) { wc_set_user_last_update_time( $user_id ); } } add_action( 'update_user_meta', 'wc_meta_update_last_update_time', 10, 4 ); /** * Sets a user's "last update" time to the current timestamp. * * @since 2.6.0 * @param int $user_id The user to set a timestamp for. */ function wc_set_user_last_update_time( $user_id ) { update_user_meta( $user_id, 'last_update', gmdate( 'U' ) ); } /** * Get customer saved payment methods list. * * @since 2.6.0 * @param int $customer_id Customer ID. * @return array */ function wc_get_customer_saved_methods_list( $customer_id ) { return apply_filters( 'woocommerce_saved_payment_methods_list', array(), $customer_id ); } /** * Get info about customer's last order. * * @since 2.6.0 * @param int $customer_id Customer ID. * @return WC_Order|bool Order object if successful or false. */ function wc_get_customer_last_order( $customer_id ) { $customer = new WC_Customer( $customer_id ); return $customer->get_last_order(); } /** * Add support for searching by display_name. * * @since 3.2.0 * @param array $search_columns Column names. * @return array */ function wc_user_search_columns( $search_columns ) { $search_columns[] = 'display_name'; return $search_columns; } add_filter( 'user_search_columns', 'wc_user_search_columns' ); /** * When a user is deleted in WordPress, delete corresponding WooCommerce data. * * @param int $user_id User ID being deleted. */ function wc_delete_user_data( $user_id ) { global $wpdb; // Clean up sessions. $wpdb->delete( $wpdb->prefix . 'woocommerce_sessions', array( 'session_key' => $user_id, ) ); // Revoke API keys. $wpdb->delete( $wpdb->prefix . 'woocommerce_api_keys', array( 'user_id' => $user_id, ) ); // Clean up payment tokens. $payment_tokens = WC_Payment_Tokens::get_customer_tokens( $user_id ); foreach ( $payment_tokens as $payment_token ) { $payment_token->delete(); } } add_action( 'delete_user', 'wc_delete_user_data' ); /** * Store user agents. Used for tracker. * * @since 3.0.0 * @param string $user_login User login. * @param int|object $user User. */ function wc_maybe_store_user_agent( $user_login, $user ) { if ( 'yes' === get_option( 'woocommerce_allow_tracking', 'no' ) && user_can( $user, 'manage_woocommerce' ) ) { $admin_user_agents = array_filter( (array) get_option( 'woocommerce_tracker_ua', array() ) ); $admin_user_agents[] = wc_get_user_agent(); update_option( 'woocommerce_tracker_ua', array_unique( $admin_user_agents ), false ); } } add_action( 'wp_login', 'wc_maybe_store_user_agent', 10, 2 ); /** * Update logic triggered on login. * * @since 3.4.0 * @param string $user_login User login. * @param object $user User. */ function wc_user_logged_in( $user_login, $user ) { wc_update_user_last_active( $user->ID ); update_user_meta( $user->ID, '_woocommerce_load_saved_cart_after_login', 1 ); } add_action( 'wp_login', 'wc_user_logged_in', 10, 2 ); /** * Update when the user was last active. * * @since 3.4.0 */ function wc_current_user_is_active() { if ( ! is_user_logged_in() ) { return; } wc_update_user_last_active( get_current_user_id() ); } add_action( 'wp', 'wc_current_user_is_active', 10 ); /** * Set the user last active timestamp to now. * * @since 3.4.0 * @param int $user_id User ID to mark active. */ function wc_update_user_last_active( $user_id ) { if ( ! $user_id ) { return; } update_user_meta( $user_id, 'wc_last_active', (string) strtotime( gmdate( 'Y-m-d', time() ) ) ); } /** * Translate WC roles using the woocommerce textdomain. * * @since 3.7.0 * @param string $translation Translated text. * @param string $text Text to translate. * @param string $context Context information for the translators. * @param string $domain Text domain. Unique identifier for retrieving translated strings. * @return string */ function wc_translate_user_roles( $translation, $text, $context, $domain ) { // translate_user_role() only accepts a second parameter starting in WP 5.2. if ( version_compare( get_bloginfo( 'version' ), '5.2', '<' ) ) { return $translation; } if ( 'User role' === $context && 'default' === $domain && in_array( $text, array( 'Shop manager', 'Customer' ), true ) ) { return translate_user_role( $text, 'woocommerce' ); } return $translation; } add_filter( 'gettext_with_context', 'wc_translate_user_roles', 10, 4 ); /** * Deprecated functions * * Where functions come to die. * * @author Automattic * @category Core * @package WooCommerce\Functions * @version 3.3.0 */ use Automattic\Jetpack\Constants; use Automattic\WooCommerce\Internal\Admin\Logging\Settings; use Automattic\WooCommerce\Utilities\LoggingUtil; if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Runs a deprecated action with notice only if used. * * @since 3.0.0 * @param string $tag The name of the action hook. * @param array $args Array of additional function arguments to be passed to do_action(). * @param string $version The version of WooCommerce that deprecated the hook. * @param string $replacement The hook that should have been used. * @param string $message A message regarding the change. */ function wc_do_deprecated_action( $tag, $args, $version, $replacement = null, $message = null ) { if ( ! has_action( $tag ) ) { return; } wc_deprecated_hook( $tag, $version, $replacement, $message ); do_action_ref_array( $tag, $args ); } /** * Wrapper for deprecated functions so we can apply some extra logic. * * @since 3.0.0 * @param string $function Function used. * @param string $version Version the message was added in. * @param string $replacement Replacement for the called function. */ function wc_deprecated_function( $function, $version, $replacement = null ) { // @codingStandardsIgnoreStart if ( wp_doing_ajax() || WC()->is_rest_api_request() ) { do_action( 'deprecated_function_run', $function, $replacement, $version ); $log_string = "The {$function} function is deprecated since version {$version}."; $log_string .= $replacement ? " Replace with {$replacement}." : ''; error_log( $log_string ); } else { _deprecated_function( $function, $version, $replacement ); } // @codingStandardsIgnoreEnd } /** * Wrapper for deprecated hook so we can apply some extra logic. * * @since 3.3.0 * @param string $hook The hook that was used. * @param string $version The version of WordPress that deprecated the hook. * @param string $replacement The hook that should have been used. * @param string $message A message regarding the change. */ function wc_deprecated_hook( $hook, $version, $replacement = null, $message = null ) { // @codingStandardsIgnoreStart if ( wp_doing_ajax() || WC()->is_rest_api_request() ) { do_action( 'deprecated_hook_run', $hook, $replacement, $version, $message ); $message = empty( $message ) ? '' : ' ' . $message; $log_string = "{$hook} is deprecated since version {$version}"; $log_string .= $replacement ? "! Use {$replacement} instead." : ' with no alternative available.'; error_log( $log_string . $message ); } else { _deprecated_hook( $hook, $version, $replacement, $message ); } // @codingStandardsIgnoreEnd } /** * When catching an exception, this allows us to log it if unexpected. * * @since 3.3.0 * @param Exception $exception_object The exception object. * @param string $function The function which threw exception. * @param array $args The args passed to the function. */ function wc_caught_exception( $exception_object, $function = '', $args = array() ) { // @codingStandardsIgnoreStart $message = $exception_object->getMessage(); $message .= '. Args: ' . print_r( $args, true ) . '.'; do_action( 'woocommerce_caught_exception', $exception_object, $function, $args ); error_log( "Exception caught in {$function}. {$message}." ); // @codingStandardsIgnoreEnd } /** * Wrapper for _doing_it_wrong(). * * @since 3.0.0 * @param string $function Function used. * @param string $message Message to log. * @param string $version Version the message was added in. */ function wc_doing_it_wrong( $function, $message, $version ) { // @codingStandardsIgnoreStart $message .= ' Backtrace: ' . wp_debug_backtrace_summary(); if ( wp_doing_ajax() || WC()->is_rest_api_request() ) { do_action( 'doing_it_wrong_run', $function, $message, $version ); error_log( "{$function} was called incorrectly. {$message}. This message was added in version {$version}." ); } else { _doing_it_wrong( $function, $message, $version ); } // @codingStandardsIgnoreEnd } /** * Wrapper for deprecated arguments so we can apply some extra logic. * * @since 3.0.0 * @param string $argument * @param string $version * @param string $replacement */ function wc_deprecated_argument( $argument, $version, $message = null ) { if ( wp_doing_ajax() || WC()->is_rest_api_request() ) { do_action( 'deprecated_argument_run', $argument, $message, $version ); error_log( "The {$argument} argument is deprecated since version {$version}. {$message}" ); } else { _deprecated_argument( $argument, $version, $message ); } } /** * @deprecated 2.1 */ function woocommerce_show_messages() { wc_deprecated_function( 'woocommerce_show_messages', '2.1', 'wc_print_notices' ); wc_print_notices(); } /** * @deprecated 2.1 */ function woocommerce_weekend_area_js() { wc_deprecated_function( 'woocommerce_weekend_area_js', '2.1' ); } /** * @deprecated 2.1 */ function woocommerce_tooltip_js() { wc_deprecated_function( 'woocommerce_tooltip_js', '2.1' ); } /** * @deprecated 2.1 */ function woocommerce_datepicker_js() { wc_deprecated_function( 'woocommerce_datepicker_js', '2.1' ); } /** * @deprecated 2.1 */ function woocommerce_admin_scripts() { wc_deprecated_function( 'woocommerce_admin_scripts', '2.1' ); } /** * @deprecated 2.1 */ function woocommerce_create_page( $slug, $option = '', $page_title = '', $page_content = '', $post_parent = 0 ) { wc_deprecated_function( 'woocommerce_create_page', '2.1', 'wc_create_page' ); return wc_create_page( $slug, $option, $page_title, $page_content, $post_parent ); } /** * @deprecated 2.1 */ function woocommerce_readfile_chunked( $file, $retbytes = true ) { wc_deprecated_function( 'woocommerce_readfile_chunked', '2.1', 'WC_Download_Handler::readfile_chunked()' ); return WC_Download_Handler::readfile_chunked( $file ); } /** * Formal total costs - format to the number of decimal places for the base currency. * * @access public * @param mixed $number * @deprecated 2.1 * @return string */ function woocommerce_format_total( $number ) { wc_deprecated_function( __FUNCTION__, '2.1', 'wc_format_decimal()' ); return wc_format_decimal( $number, wc_get_price_decimals(), false ); } /** * Get product name with extra details such as SKU price and attributes. Used within admin. * * @access public * @param WC_Product $product * @deprecated 2.1 * @return string */ function woocommerce_get_formatted_product_name( $product ) { wc_deprecated_function( __FUNCTION__, '2.1', 'WC_Product::get_formatted_name()' ); return $product->get_formatted_name(); } /** * Handle IPN requests for the legacy paypal gateway by calling gateways manually if needed. * * @access public */ function woocommerce_legacy_paypal_ipn() { if ( ! empty( $_GET['paypalListener'] ) && 'paypal_standard_IPN' === $_GET['paypalListener'] ) { WC()->payment_gateways(); do_action( 'woocommerce_api_wc_gateway_paypal' ); } } add_action( 'init', 'woocommerce_legacy_paypal_ipn' ); /** * @deprecated 3.0 */ function get_product( $the_product = false, $args = array() ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_product' ); return wc_get_product( $the_product, $args ); } /** * @deprecated 3.0 */ function woocommerce_protected_product_add_to_cart( $passed, $product_id ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_protected_product_add_to_cart' ); return wc_protected_product_add_to_cart( $passed, $product_id ); } /** * @deprecated 3.0 */ function woocommerce_empty_cart() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_empty_cart' ); wc_empty_cart(); } /** * @deprecated 3.0 */ function woocommerce_load_persistent_cart( $user_login, $user = 0 ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_load_persistent_cart' ); return wc_load_persistent_cart( $user_login, $user ); } /** * @deprecated 3.0 */ function woocommerce_add_to_cart_message( $product_id ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_add_to_cart_message' ); wc_add_to_cart_message( $product_id ); } /** * @deprecated 3.0 */ function woocommerce_clear_cart_after_payment() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_clear_cart_after_payment' ); wc_clear_cart_after_payment(); } /** * @deprecated 3.0 */ function woocommerce_cart_totals_subtotal_html() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_cart_totals_subtotal_html' ); wc_cart_totals_subtotal_html(); } /** * @deprecated 3.0 */ function woocommerce_cart_totals_shipping_html() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_cart_totals_shipping_html' ); wc_cart_totals_shipping_html(); } /** * @deprecated 3.0 */ function woocommerce_cart_totals_coupon_html( $coupon ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_cart_totals_coupon_html' ); wc_cart_totals_coupon_html( $coupon ); } /** * @deprecated 3.0 */ function woocommerce_cart_totals_order_total_html() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_cart_totals_order_total_html' ); wc_cart_totals_order_total_html(); } /** * @deprecated 3.0 */ function woocommerce_cart_totals_fee_html( $fee ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_cart_totals_fee_html' ); wc_cart_totals_fee_html( $fee ); } /** * @deprecated 3.0 */ function woocommerce_cart_totals_shipping_method_label( $method ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_cart_totals_shipping_method_label' ); return wc_cart_totals_shipping_method_label( $method ); } /** * @deprecated 3.0 */ function woocommerce_get_template_part( $slug, $name = '' ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_template_part' ); wc_get_template_part( $slug, $name ); } /** * @deprecated 3.0 */ function woocommerce_get_template( $template_name, $args = array(), $template_path = '', $default_path = '' ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_template' ); wc_get_template( $template_name, $args, $template_path, $default_path ); } /** * @deprecated 3.0 */ function woocommerce_locate_template( $template_name, $template_path = '', $default_path = '' ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_locate_template' ); return wc_locate_template( $template_name, $template_path, $default_path ); } /** * @deprecated 3.0 */ function woocommerce_mail( $to, $subject, $message, $headers = "Content-Type: text/html\r\n", $attachments = "" ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_mail' ); wc_mail( $to, $subject, $message, $headers, $attachments ); } /** * @deprecated 3.0 */ function woocommerce_disable_admin_bar( $show_admin_bar ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_disable_admin_bar' ); return wc_disable_admin_bar( $show_admin_bar ); } /** * @deprecated 3.0 */ function woocommerce_create_new_customer( $email, $username = '', $password = '' ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_create_new_customer' ); return wc_create_new_customer( $email, $username, $password ); } /** * @deprecated 3.0 */ function woocommerce_set_customer_auth_cookie( $customer_id ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_set_customer_auth_cookie' ); wc_set_customer_auth_cookie( $customer_id ); } /** * @deprecated 3.0 */ function woocommerce_update_new_customer_past_orders( $customer_id ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_update_new_customer_past_orders' ); return wc_update_new_customer_past_orders( $customer_id ); } /** * @deprecated 3.0 */ function woocommerce_paying_customer( $order_id ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_paying_customer' ); wc_paying_customer( $order_id ); } /** * @deprecated 3.0 */ function woocommerce_customer_bought_product( $customer_email, $user_id, $product_id ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_customer_bought_product' ); return wc_customer_bought_product( $customer_email, $user_id, $product_id ); } /** * @deprecated 3.0 */ function woocommerce_customer_has_capability( $allcaps, $caps, $args ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_customer_has_capability' ); return wc_customer_has_capability( $allcaps, $caps, $args ); } /** * @deprecated 3.0 */ function woocommerce_sanitize_taxonomy_name( $taxonomy ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_sanitize_taxonomy_name' ); return wc_sanitize_taxonomy_name( $taxonomy ); } /** * @deprecated 3.0 */ function woocommerce_get_filename_from_url( $file_url ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_filename_from_url' ); return wc_get_filename_from_url( $file_url ); } /** * @deprecated 3.0 */ function woocommerce_get_dimension( $dim, $to_unit ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_dimension' ); return wc_get_dimension( $dim, $to_unit ); } /** * @deprecated 3.0 */ function woocommerce_get_weight( $weight, $to_unit ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_weight' ); return wc_get_weight( $weight, $to_unit ); } /** * @deprecated 3.0 */ function woocommerce_trim_zeros( $price ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_trim_zeros' ); return wc_trim_zeros( $price ); } /** * @deprecated 3.0 */ function woocommerce_round_tax_total( $tax ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_round_tax_total' ); return wc_round_tax_total( $tax ); } /** * @deprecated 3.0 */ function woocommerce_format_decimal( $number, $dp = false, $trim_zeros = false ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_format_decimal' ); return wc_format_decimal( $number, $dp, $trim_zeros ); } /** * @deprecated 3.0 */ function woocommerce_clean( $var ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_clean' ); return wc_clean( $var ); } /** * @deprecated 3.0 */ function woocommerce_array_overlay( $a1, $a2 ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_array_overlay' ); return wc_array_overlay( $a1, $a2 ); } /** * @deprecated 3.0 */ function woocommerce_price( $price, $args = array() ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_price' ); return wc_price( $price, $args ); } /** * @deprecated 3.0 */ function woocommerce_let_to_num( $size ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_let_to_num' ); return wc_let_to_num( $size ); } /** * @deprecated 3.0 */ function woocommerce_date_format() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_date_format' ); return wc_date_format(); } /** * @deprecated 3.0 */ function woocommerce_time_format() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_time_format' ); return wc_time_format(); } /** * @deprecated 3.0 */ function woocommerce_timezone_string() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_timezone_string' ); return wc_timezone_string(); } if ( ! function_exists( 'woocommerce_rgb_from_hex' ) ) { /** * @deprecated 3.0 */ function woocommerce_rgb_from_hex( $color ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_rgb_from_hex' ); return wc_rgb_from_hex( $color ); } } if ( ! function_exists( 'woocommerce_hex_darker' ) ) { /** * @deprecated 3.0 */ function woocommerce_hex_darker( $color, $factor = 30 ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_hex_darker' ); return wc_hex_darker( $color, $factor ); } } if ( ! function_exists( 'woocommerce_hex_lighter' ) ) { /** * @deprecated 3.0 */ function woocommerce_hex_lighter( $color, $factor = 30 ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_hex_lighter' ); return wc_hex_lighter( $color, $factor ); } } if ( ! function_exists( 'woocommerce_light_or_dark' ) ) { /** * @deprecated 3.0 */ function woocommerce_light_or_dark( $color, $dark = '#000000', $light = '#FFFFFF' ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_light_or_dark' ); return wc_light_or_dark( $color, $dark, $light ); } } if ( ! function_exists( 'woocommerce_format_hex' ) ) { /** * @deprecated 3.0 */ function woocommerce_format_hex( $hex ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_format_hex' ); return wc_format_hex( $hex ); } } /** * @deprecated 3.0 */ function woocommerce_get_order_id_by_order_key( $order_key ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_order_id_by_order_key' ); return wc_get_order_id_by_order_key( $order_key ); } /** * @deprecated 3.0 */ function woocommerce_downloadable_file_permission( $download_id, $product_id, $order ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_downloadable_file_permission' ); return wc_downloadable_file_permission( $download_id, $product_id, $order ); } /** * @deprecated 3.0 */ function woocommerce_downloadable_product_permissions( $order_id ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_downloadable_product_permissions' ); wc_downloadable_product_permissions( $order_id ); } /** * @deprecated 3.0 */ function woocommerce_add_order_item( $order_id, $item ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_add_order_item' ); return wc_add_order_item( $order_id, $item ); } /** * @deprecated 3.0 */ function woocommerce_delete_order_item( $item_id ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_delete_order_item' ); return wc_delete_order_item( $item_id ); } /** * @deprecated 3.0 */ function woocommerce_update_order_item_meta( $item_id, $meta_key, $meta_value, $prev_value = '' ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_update_order_item_meta' ); return wc_update_order_item_meta( $item_id, $meta_key, $meta_value, $prev_value ); } /** * @deprecated 3.0 */ function woocommerce_add_order_item_meta( $item_id, $meta_key, $meta_value, $unique = false ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_add_order_item_meta' ); return wc_add_order_item_meta( $item_id, $meta_key, $meta_value, $unique ); } /** * @deprecated 3.0 */ function woocommerce_delete_order_item_meta( $item_id, $meta_key, $meta_value = '', $delete_all = false ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_delete_order_item_meta' ); return wc_delete_order_item_meta( $item_id, $meta_key, $meta_value, $delete_all ); } /** * @deprecated 3.0 */ function woocommerce_get_order_item_meta( $item_id, $key, $single = true ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_order_item_meta' ); return wc_get_order_item_meta( $item_id, $key, $single ); } /** * @deprecated 3.0 */ function woocommerce_cancel_unpaid_orders() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_cancel_unpaid_orders' ); wc_cancel_unpaid_orders(); } /** * @deprecated 3.0 */ function woocommerce_processing_order_count() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_processing_order_count' ); return wc_processing_order_count(); } /** * @deprecated 3.0 */ function woocommerce_get_page_id( $page ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_page_id' ); return wc_get_page_id( $page ); } /** * @deprecated 3.0 */ function woocommerce_get_endpoint_url( $endpoint, $value = '', $permalink = '' ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_endpoint_url' ); return wc_get_endpoint_url( $endpoint, $value, $permalink ); } /** * @deprecated 3.0 */ function woocommerce_lostpassword_url( $url ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_lostpassword_url' ); return wc_lostpassword_url( $url ); } /** * @deprecated 3.0 */ function woocommerce_customer_edit_account_url() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_customer_edit_account_url' ); return wc_customer_edit_account_url(); } /** * @deprecated 3.0 */ function woocommerce_nav_menu_items( $items, $args ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_nav_menu_items' ); return wc_nav_menu_items( $items ); } /** * @deprecated 3.0 */ function woocommerce_nav_menu_item_classes( $menu_items, $args ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_nav_menu_item_classes' ); return wc_nav_menu_item_classes( $menu_items ); } /** * @deprecated 3.0 */ function woocommerce_list_pages( $pages ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_list_pages' ); return wc_list_pages( $pages ); } /** * @deprecated 3.0 */ function woocommerce_product_dropdown_categories( $args = array(), $deprecated_hierarchical = 1, $deprecated_show_uncategorized = 1, $deprecated_orderby = '' ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_product_dropdown_categories' ); return wc_product_dropdown_categories( $args, $deprecated_hierarchical, $deprecated_show_uncategorized, $deprecated_orderby ); } /** * @deprecated 3.0 */ function woocommerce_walk_category_dropdown_tree( $a1 = '', $a2 = '', $a3 = '' ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_walk_category_dropdown_tree' ); return wc_walk_category_dropdown_tree( $a1, $a2, $a3 ); } /** * @deprecated 3.0 */ function woocommerce_taxonomy_metadata_wpdbfix() { wc_deprecated_function( __FUNCTION__, '3.0' ); } /** * @deprecated 3.0 */ function wc_taxonomy_metadata_wpdbfix() { wc_deprecated_function( __FUNCTION__, '3.0' ); } /** * @deprecated 3.0 */ function woocommerce_order_terms( $the_term, $next_id, $taxonomy, $index = 0, $terms = null ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_reorder_terms' ); return wc_reorder_terms( $the_term, $next_id, $taxonomy, $index, $terms ); } /** * @deprecated 3.0 */ function woocommerce_set_term_order( $term_id, $index, $taxonomy, $recursive = false ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_set_term_order' ); return wc_set_term_order( $term_id, $index, $taxonomy, $recursive ); } /** * @deprecated 3.0 */ function woocommerce_terms_clauses( $clauses, $taxonomies, $args ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_terms_clauses' ); return wc_terms_clauses( $clauses, $taxonomies, $args ); } /** * @deprecated 3.0 */ function _woocommerce_term_recount( $terms, $taxonomy, $callback, $terms_are_term_taxonomy_ids ) { wc_deprecated_function( __FUNCTION__, '3.0', '_wc_term_recount' ); return _wc_term_recount( $terms, $taxonomy, $callback, $terms_are_term_taxonomy_ids ); } /** * @deprecated 3.0 */ function woocommerce_recount_after_stock_change( $product_id ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_recount_after_stock_change' ); return wc_recount_after_stock_change( $product_id ); } /** * @deprecated 3.0 */ function woocommerce_change_term_counts( $terms, $taxonomies, $args ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_change_term_counts' ); return wc_change_term_counts( $terms, $taxonomies ); } /** * @deprecated 3.0 */ function woocommerce_get_product_ids_on_sale() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_product_ids_on_sale' ); return wc_get_product_ids_on_sale(); } /** * @deprecated 3.0 */ function woocommerce_get_featured_product_ids() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_featured_product_ids' ); return wc_get_featured_product_ids(); } /** * @deprecated 3.0 */ function woocommerce_get_product_terms( $object_id, $taxonomy, $fields = 'all' ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_product_terms' ); return wc_get_product_terms( $object_id, $taxonomy, array( 'fields' => $fields ) ); } /** * @deprecated 3.0 */ function woocommerce_product_post_type_link( $permalink, $post ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_product_post_type_link' ); return wc_product_post_type_link( $permalink, $post ); } /** * @deprecated 3.0 */ function woocommerce_placeholder_img_src() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_placeholder_img_src' ); return wc_placeholder_img_src(); } /** * @deprecated 3.0 */ function woocommerce_placeholder_img( $size = 'woocommerce_thumbnail' ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_placeholder_img' ); return wc_placeholder_img( $size ); } /** * @deprecated 3.0 */ function woocommerce_get_formatted_variation( $variation = '', $flat = false ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_formatted_variation' ); return wc_get_formatted_variation( $variation, $flat ); } /** * @deprecated 3.0 */ function woocommerce_scheduled_sales() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_scheduled_sales' ); return wc_scheduled_sales(); } /** * @deprecated 3.0 */ function woocommerce_get_attachment_image_attributes( $attr ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_get_attachment_image_attributes' ); return wc_get_attachment_image_attributes( $attr ); } /** * @deprecated 3.0 */ function woocommerce_prepare_attachment_for_js( $response ) { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_prepare_attachment_for_js' ); return wc_prepare_attachment_for_js( $response ); } /** * @deprecated 3.0 */ function woocommerce_track_product_view() { wc_deprecated_function( __FUNCTION__, '3.0', 'wc_track_product_view' ); return wc_track_product_view(); } /** * @deprecated 2.3 has no replacement */ function woocommerce_compile_less_styles() { wc_deprecated_function( 'woocommerce_compile_less_styles', '2.3' ); } /** * woocommerce_calc_shipping was an option used to determine if shipping was enabled prior to version 2.6.0. This has since been replaced with wc_shipping_enabled() function and * the woocommerce_ship_to_countries setting. * @deprecated 2.6.0 * @return string */ function woocommerce_calc_shipping_backwards_compatibility( $value ) { if ( Constants::is_defined( 'WC_UPDATING' ) ) { return $value; } return 'disabled' === get_option( 'woocommerce_ship_to_countries' ) ? 'no' : 'yes'; } add_filter( 'pre_option_woocommerce_calc_shipping', 'woocommerce_calc_shipping_backwards_compatibility' ); /** * @deprecated 3.0.0 * @see WC_Structured_Data class * * @return string */ function woocommerce_get_product_schema() { wc_deprecated_function( 'woocommerce_get_product_schema', '3.0' ); global $product; $schema = "Product"; // Downloadable product schema handling if ( $product->is_downloadable() ) { switch ( $product->download_type ) { case 'application' : $schema = "SoftwareApplication"; break; case 'music' : $schema = "MusicAlbum"; break; default : $schema = "Product"; break; } } return 'http://schema.org/' . $schema; } /** * Save product price. * * This is a private function (internal use ONLY) used until a data manipulation api is built. * * @deprecated 3.0.0 * @param int $product_id * @param float $regular_price * @param float $sale_price * @param string $date_from * @param string $date_to */ function _wc_save_product_price( $product_id, $regular_price, $sale_price = '', $date_from = '', $date_to = '' ) { wc_doing_it_wrong( '_wc_save_product_price()', 'This function is not for developer use and is deprecated.', '3.0' ); $product_id = absint( $product_id ); $regular_price = wc_format_decimal( $regular_price ); $sale_price = '' === $sale_price ? '' : wc_format_decimal( $sale_price ); $date_from = wc_clean( $date_from ); $date_to = wc_clean( $date_to ); update_post_meta( $product_id, '_regular_price', $regular_price ); update_post_meta( $product_id, '_sale_price', $sale_price ); // Save Dates update_post_meta( $product_id, '_sale_price_dates_from', $date_from ? strtotime( $date_from ) : '' ); update_post_meta( $product_id, '_sale_price_dates_to', $date_to ? strtotime( $date_to ) : '' ); if ( $date_to && ! $date_from ) { $date_from = strtotime( 'NOW', current_time( 'timestamp' ) ); update_post_meta( $product_id, '_sale_price_dates_from', $date_from ); } // Update price if on sale if ( '' !== $sale_price && '' === $date_to && '' === $date_from ) { update_post_meta( $product_id, '_price', $sale_price ); } else { update_post_meta( $product_id, '_price', $regular_price ); } if ( '' !== $sale_price && $date_from && strtotime( $date_from ) < strtotime( 'NOW', current_time( 'timestamp' ) ) ) { update_post_meta( $product_id, '_price', $sale_price ); } if ( $date_to && strtotime( $date_to ) < strtotime( 'NOW', current_time( 'timestamp' ) ) ) { update_post_meta( $product_id, '_price', $regular_price ); update_post_meta( $product_id, '_sale_price_dates_from', '' ); update_post_meta( $product_id, '_sale_price_dates_to', '' ); } } /** * Return customer avatar URL. * * @deprecated 3.1.0 * @since 2.6.0 * @param string $email the customer's email. * @return string the URL to the customer's avatar. */ function wc_get_customer_avatar_url( $email ) { // Deprecated in favor of WordPress get_avatar_url() function. wc_deprecated_function( 'wc_get_customer_avatar_url()', '3.1', 'get_avatar_url()' ); return get_avatar_url( $email ); } /** * WooCommerce Core Supported Themes. * * @deprecated 3.3.0 * @since 2.2 * @return string[] */ function wc_get_core_supported_themes() { wc_deprecated_function( 'wc_get_core_supported_themes()', '3.3' ); return array( 'twentyseventeen', 'twentysixteen', 'twentyfifteen', 'twentyfourteen', 'twentythirteen', 'twentyeleven', 'twentytwelve', 'twentyten' ); } /** * Get min/max price meta query args. * * @deprecated 3.6.0 * @since 3.0.0 * @param array $args Min price and max price arguments. * @return array */ function wc_get_min_max_price_meta_query( $args ) { wc_deprecated_function( 'wc_get_min_max_price_meta_query()', '3.6' ); $current_min_price = isset( $args['min_price'] ) ? floatval( $args['min_price'] ) : 0; $current_max_price = isset( $args['max_price'] ) ? floatval( $args['max_price'] ) : PHP_INT_MAX; return apply_filters( 'woocommerce_get_min_max_price_meta_query', array( 'key' => '_price', 'value' => array( $current_min_price, $current_max_price ), 'compare' => 'BETWEEN', 'type' => 'DECIMAL(10,' . wc_get_price_decimals() . ')', ), $args ); } /** * When a term is split, ensure meta data maintained. * * @deprecated 3.6.0 * @param int $old_term_id Old term ID. * @param int $new_term_id New term ID. * @param string $term_taxonomy_id Term taxonomy ID. * @param string $taxonomy Taxonomy. */ function wc_taxonomy_metadata_update_content_for_split_terms( $old_term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) { wc_deprecated_function( 'wc_taxonomy_metadata_update_content_for_split_terms', '3.6' ); } /** * WooCommerce Term Meta API. * * WC tables for storing term meta are deprecated from WordPress 4.4 since 4.4 has its own table. * This function serves as a wrapper, using the new table if present, or falling back to the WC table. * * @deprecated 3.6.0 * @param int $term_id Term ID. * @param string $meta_key Meta key. * @param mixed $meta_value Meta value. * @param string $prev_value Previous value. (default: ''). * @return bool */ function update_woocommerce_term_meta( $term_id, $meta_key, $meta_value, $prev_value = '' ) { wc_deprecated_function( 'update_woocommerce_term_meta', '3.6', 'update_term_meta' ); return function_exists( 'update_term_meta' ) ? update_term_meta( $term_id, $meta_key, $meta_value, $prev_value ) : update_metadata( 'woocommerce_term', $term_id, $meta_key, $meta_value, $prev_value ); } /** * WooCommerce Term Meta API. * * WC tables for storing term meta are deprecated from WordPress 4.4 since 4.4 has its own table. * This function serves as a wrapper, using the new table if present, or falling back to the WC table. * * @deprecated 3.6.0 * @param int $term_id Term ID. * @param string $meta_key Meta key. * @param mixed $meta_value Meta value. * @param bool $unique Make meta key unique. (default: false). * @return bool */ function add_woocommerce_term_meta( $term_id, $meta_key, $meta_value, $unique = false ) { wc_deprecated_function( 'add_woocommerce_term_meta', '3.6', 'add_term_meta' ); return function_exists( 'add_term_meta' ) ? add_term_meta( $term_id, $meta_key, $meta_value, $unique ) : add_metadata( 'woocommerce_term', $term_id, $meta_key, $meta_value, $unique ); } /** * WooCommerce Term Meta API * * WC tables for storing term meta are deprecated from WordPress 4.4 since 4.4 has its own table. * This function serves as a wrapper, using the new table if present, or falling back to the WC table. * * @deprecated 3.6.0 * @param int $term_id Term ID. * @param string $meta_key Meta key. * @param mixed $meta_value Meta value (default: ''). * @param bool $deprecated Deprecated param (default: false). * @return bool */ function delete_woocommerce_term_meta( $term_id, $meta_key, $meta_value = '', $deprecated = false ) { wc_deprecated_function( 'delete_woocommerce_term_meta', '3.6', 'delete_term_meta' ); return function_exists( 'delete_term_meta' ) ? delete_term_meta( $term_id, $meta_key, $meta_value ) : delete_metadata( 'woocommerce_term', $term_id, $meta_key, $meta_value ); } /** * WooCommerce Term Meta API * * WC tables for storing term meta are deprecated from WordPress 4.4 since 4.4 has its own table. * This function serves as a wrapper, using the new table if present, or falling back to the WC table. * * @deprecated 3.6.0 * @param int $term_id Term ID. * @param string $key Meta key. * @param bool $single Whether to return a single value. (default: true). * @return mixed */ function get_woocommerce_term_meta( $term_id, $key, $single = true ) { wc_deprecated_function( 'get_woocommerce_term_meta', '3.6', 'get_term_meta' ); return function_exists( 'get_term_meta' ) ? get_term_meta( $term_id, $key, $single ) : get_metadata( 'woocommerce_term', $term_id, $key, $single ); } /** * Registers the default log handler. * * @deprecated 8.6.0 * @since 3.0 * @param array $handlers Handlers. * @return array */ function wc_register_default_log_handler( $handlers = array() ) { wc_deprecated_function( 'wc_register_default_log_handler', '8.6.0' ); $default_handler = wc_get_container()->get( Settings::class )->get_default_handler(); array_push( $handlers, new $default_handler() ); return $handlers; } /** * Get a log file path. * * @deprecated 8.6.0 * @since 2.2 * * @param string $handle name. * @return string the log file path. */ function wc_get_log_file_path( $handle ) { wc_deprecated_function( 'wc_get_log_file_path', '8.6.0' ); $directory = LoggingUtil::get_log_directory(); $file_id = LoggingUtil::generate_log_file_id( $handle, null, time() ); $hash = LoggingUtil::generate_log_file_hash( $file_id ); return "{$directory}{$file_id}-{$hash}.log"; } /** * Get a log file name. * * @since 3.3 * * @param string $handle Name. * @return string The log file name. */ function wc_get_log_file_name( $handle ) { wc_deprecated_function( 'wc_get_log_file_name', '8.6.0' ); $file_id = LoggingUtil::generate_log_file_id( $handle, null, time() ); $hash = LoggingUtil::generate_log_file_hash( $file_id ); return "{$file_id}-{$hash}"; } /** * WooCommerce Order Item Functions * * Functions for order specific things. * * @package WooCommerce\Functions * @version 3.4.0 */ defined( 'ABSPATH' ) || exit; /** * Add a item to an order (for example a line item). * * @param int $order_id Order ID. * @param array $item_array Items list. * * @throws Exception When `WC_Data_Store::load` validation fails. * @return int|bool Item ID or false */ function wc_add_order_item( $order_id, $item_array ) { $order_id = absint( $order_id ); if ( ! $order_id ) { return false; } $defaults = array( 'order_item_name' => '', 'order_item_type' => 'line_item', ); $item_array = wp_parse_args( $item_array, $defaults ); $data_store = WC_Data_Store::load( 'order-item' ); $item_id = $data_store->add_order_item( $order_id, $item_array ); $item = WC_Order_Factory::get_order_item( $item_id ); do_action( 'woocommerce_new_order_item', $item_id, $item, $order_id ); return $item_id; } /** * Update an item for an order. * * @since 2.2 * @param int $item_id Item ID. * @param array $args Either `order_item_type` or `order_item_name`. * * @throws Exception When `WC_Data_Store::load` validation fails. * @return bool True if successfully updated, false otherwise. */ function wc_update_order_item( $item_id, $args ) { $data_store = WC_Data_Store::load( 'order-item' ); $update = $data_store->update_order_item( $item_id, $args ); if ( false === $update ) { return false; } do_action( 'woocommerce_update_order_item', $item_id, $args ); return true; } /** * Delete an item from the order it belongs to based on item id. * * @param int $item_id Item ID. * * @throws Exception When `WC_Data_Store::load` validation fails. * @return bool */ function wc_delete_order_item( $item_id ) { $item_id = absint( $item_id ); if ( ! $item_id ) { return false; } $data_store = WC_Data_Store::load( 'order-item' ); do_action( 'woocommerce_before_delete_order_item', $item_id ); $data_store->delete_order_item( $item_id ); do_action( 'woocommerce_delete_order_item', $item_id ); return true; } /** * WooCommerce Order Item Meta API - Update term meta. * * @param int $item_id Item ID. * @param string $meta_key Meta key. * @param mixed $meta_value Meta value. * @param string $prev_value Previous value (default: ''). * * @throws Exception When `WC_Data_Store::load` validation fails. * @return bool */ function wc_update_order_item_meta( $item_id, $meta_key, $meta_value, $prev_value = '' ) { $data_store = WC_Data_Store::load( 'order-item' ); if ( $data_store->update_metadata( $item_id, $meta_key, $meta_value, $prev_value ) ) { WC_Cache_Helper::invalidate_cache_group( 'object_' . $item_id ); // Invalidate cache. return true; } return false; } /** * WooCommerce Order Item Meta API - Add term meta. * * @param int $item_id Item ID. * @param string $meta_key Meta key. * @param mixed $meta_value Meta value. * @param bool $unique If meta data should be unique (default: false). * * @throws Exception When `WC_Data_Store::load` validation fails. * @return int New row ID or 0. */ function wc_add_order_item_meta( $item_id, $meta_key, $meta_value, $unique = false ) { $data_store = WC_Data_Store::load( 'order-item' ); $meta_id = $data_store->add_metadata( $item_id, $meta_key, $meta_value, $unique ); if ( $meta_id ) { WC_Cache_Helper::invalidate_cache_group( 'object_' . $item_id ); // Invalidate cache. return $meta_id; } return 0; } /** * WooCommerce Order Item Meta API - Delete term meta. * * @param int $item_id Item ID. * @param string $meta_key Meta key. * @param mixed $meta_value Meta value (default: ''). * @param bool $delete_all Delete all meta data, defaults to `false`. * * @throws Exception When `WC_Data_Store::load` validation fails. * @return bool */ function wc_delete_order_item_meta( $item_id, $meta_key, $meta_value = '', $delete_all = false ) { $data_store = WC_Data_Store::load( 'order-item' ); if ( $data_store->delete_metadata( $item_id, $meta_key, $meta_value, $delete_all ) ) { WC_Cache_Helper::invalidate_cache_group( 'object_' . $item_id ); // Invalidate cache. return true; } return false; } /** * WooCommerce Order Item Meta API - Get term meta. * * @param int $item_id Item ID. * @param string $key Meta key. * @param bool $single Whether to return a single value. (default: true). * * @throws Exception When `WC_Data_Store::load` validation fails. * @return mixed */ function wc_get_order_item_meta( $item_id, $key, $single = true ) { $data_store = WC_Data_Store::load( 'order-item' ); return $data_store->get_metadata( $item_id, $key, $single ); } /** * Get order ID by order item ID. * * @param int $item_id Item ID. * * @throws Exception When `WC_Data_Store::load` validation fails. * @return int */ function wc_get_order_id_by_order_item_id( $item_id ) { $data_store = WC_Data_Store::load( 'order-item' ); return $data_store->get_order_id_by_order_item_id( $item_id ); } /** * WooCommerce Terms * * Functions for handling terms/term meta. * * @package WooCommerce\Functions * @version 2.1.0 */ defined( 'ABSPATH' ) || exit; /** * Change get terms defaults for attributes to order by the sorting setting, or default to menu_order for sortable taxonomies. * * @since 3.6.0 Sorting options are now set as the default automatically, so you no longer have to request to orderby menu_order. * * @param array $defaults An array of default get_terms() arguments. * @param array $taxonomies An array of taxonomies. * @return array */ function wc_change_get_terms_defaults( $defaults, $taxonomies ) { if ( is_array( $taxonomies ) && 1 < count( $taxonomies ) ) { return $defaults; } $taxonomy = is_array( $taxonomies ) ? (string) current( $taxonomies ) : $taxonomies; $orderby = 'name'; if ( taxonomy_is_product_attribute( $taxonomy ) ) { $orderby = wc_attribute_orderby( $taxonomy ); } elseif ( in_array( $taxonomy, apply_filters( 'woocommerce_sortable_taxonomies', array( 'product_cat' ) ), true ) ) { $orderby = 'menu_order'; } // Change defaults. Invalid values will be changed later @see wc_change_pre_get_terms. // These are in place so we know if a specific order was requested. switch ( $orderby ) { case 'menu_order': case 'name_num': case 'parent': $defaults['orderby'] = $orderby; break; } return $defaults; } add_filter( 'get_terms_defaults', 'wc_change_get_terms_defaults', 10, 2 ); /** * Adds support to get_terms for menu_order argument. * * @since 3.6.0 * @param WP_Term_Query $terms_query Instance of WP_Term_Query. */ function wc_change_pre_get_terms( $terms_query ) { $args = &$terms_query->query_vars; // Put back valid orderby values. if ( 'menu_order' === $args['orderby'] ) { $args['orderby'] = 'name'; $args['force_menu_order_sort'] = true; } if ( 'name_num' === $args['orderby'] ) { $args['orderby'] = 'name'; $args['force_numeric_name'] = true; } // When COUNTING, disable custom sorting. if ( 'count' === $args['fields'] ) { return; } // Support menu_order arg used in previous versions. if ( ! empty( $args['menu_order'] ) ) { $args['order'] = 'DESC' === strtoupper( $args['menu_order'] ) ? 'DESC' : 'ASC'; $args['force_menu_order_sort'] = true; } if ( ! empty( $args['force_menu_order_sort'] ) ) { $args['orderby'] = 'meta_value_num'; $args['meta_key'] = 'order'; // phpcs:ignore $terms_query->meta_query->parse_query_vars( $args ); } } add_action( 'pre_get_terms', 'wc_change_pre_get_terms', 10, 1 ); /** * Adjust term query to handle custom sorting parameters. * * @param array $clauses Clauses. * @param array $taxonomies Taxonomies. * @param array $args Arguments. * @return array */ function wc_terms_clauses( $clauses, $taxonomies, $args ) { global $wpdb; // No need to filter when counting. if ( strpos( $clauses['fields'], 'COUNT(*)' ) !== false ) { return $clauses; } // Force numeric sort if using name_num custom sorting param. if ( ! empty( $args['force_numeric_name'] ) ) { $clauses['orderby'] = str_replace( 'ORDER BY t.name', 'ORDER BY t.name+0', $clauses['orderby'] ); } // For sorting, force left join in case order meta is missing. if ( ! empty( $args['force_menu_order_sort'] ) ) { $clauses['join'] = str_replace( "INNER JOIN {$wpdb->termmeta} ON ( t.term_id = {$wpdb->termmeta}.term_id )", "LEFT JOIN {$wpdb->termmeta} ON ( t.term_id = {$wpdb->termmeta}.term_id AND {$wpdb->termmeta}.meta_key='order')", $clauses['join'] ); $clauses['where'] = str_replace( "{$wpdb->termmeta}.meta_key = 'order'", "( {$wpdb->termmeta}.meta_key = 'order' OR {$wpdb->termmeta}.meta_key IS NULL )", $clauses['where'] ); $clauses['orderby'] = 'DESC' === $args['order'] ? str_replace( 'meta_value+0', 'meta_value+0 DESC, t.name', $clauses['orderby'] ) : str_replace( 'meta_value+0', 'meta_value+0 ASC, t.name', $clauses['orderby'] ); } return $clauses; } add_filter( 'terms_clauses', 'wc_terms_clauses', 99, 3 ); /** * Helper to get cached object terms and filter by field using wp_list_pluck(). * Works as a cached alternative for wp_get_post_terms() and wp_get_object_terms(). * * @since 3.0.0 * @param int $object_id Object ID. * @param string $taxonomy Taxonomy slug. * @param string $field Field name. * @param string $index_key Index key name. * @return array */ function wc_get_object_terms( $object_id, $taxonomy, $field = null, $index_key = null ) { // Test if terms exists. get_the_terms() return false when it finds no terms. $terms = get_the_terms( $object_id, $taxonomy ); if ( ! $terms || is_wp_error( $terms ) ) { return array(); } return is_null( $field ) ? $terms : wp_list_pluck( $terms, $field, $index_key ); } /** * Cached version of wp_get_post_terms(). * This is a private function (internal use ONLY). * * @since 3.0.0 * @param int $product_id Product ID. * @param string $taxonomy Taxonomy slug. * @param array $args Query arguments. * @return array */ function _wc_get_cached_product_terms( $product_id, $taxonomy, $args = array() ) { $cache_key = 'wc_' . $taxonomy . md5( wp_json_encode( $args ) ); $cache_group = WC_Cache_Helper::get_cache_prefix( 'product_' . $product_id ) . $product_id; $terms = wp_cache_get( $cache_key, $cache_group ); if ( false !== $terms ) { return $terms; } $terms = wp_get_post_terms( $product_id, $taxonomy, $args ); wp_cache_add( $cache_key, $terms, $cache_group ); return $terms; } /** * Wrapper used to get terms for a product. * * @param int $product_id Product ID. * @param string $taxonomy Taxonomy slug. * @param array $args Query arguments. * @return array */ function wc_get_product_terms( $product_id, $taxonomy, $args = array() ) { if ( ! taxonomy_exists( $taxonomy ) ) { return array(); } return apply_filters( 'woocommerce_get_product_terms', _wc_get_cached_product_terms( $product_id, $taxonomy, $args ), $product_id, $taxonomy, $args ); } /** * Sort by name (numeric). * * @param WP_Post $a First item to compare. * @param WP_Post $b Second item to compare. * @return int */ function _wc_get_product_terms_name_num_usort_callback( $a, $b ) { $a_name = (float) $a->name; $b_name = (float) $b->name; if ( abs( $a_name - $b_name ) < 0.001 ) { return 0; } return ( $a_name < $b_name ) ? -1 : 1; } /** * Sort by parent. * * @param WP_Post $a First item to compare. * @param WP_Post $b Second item to compare. * @return int */ function _wc_get_product_terms_parent_usort_callback( $a, $b ) { if ( $a->parent === $b->parent ) { return 0; } return ( $a->parent < $b->parent ) ? 1 : -1; } /** * WooCommerce Dropdown categories. * * @param array $args Args to control display of dropdown. */ function wc_product_dropdown_categories( $args = array() ) { global $wp_query; $args = wp_parse_args( $args, array( 'pad_counts' => 1, 'show_count' => 1, 'hierarchical' => 1, 'hide_empty' => 1, 'show_uncategorized' => 1, 'orderby' => 'name', 'selected' => isset( $wp_query->query_vars['product_cat'] ) ? $wp_query->query_vars['product_cat'] : '', 'show_option_none' => __( 'Select a category', 'woocommerce' ), 'option_none_value' => '', 'value_field' => 'slug', 'taxonomy' => 'product_cat', 'name' => 'product_cat', 'class' => 'dropdown_product_cat', ) ); if ( 'order' === $args['orderby'] ) { $args['orderby'] = 'meta_value_num'; $args['meta_key'] = 'order'; // phpcs:ignore } wp_dropdown_categories( $args ); } /** * Custom walker for Product Categories. * * Previously used by wc_product_dropdown_categories, but wp_dropdown_categories has been fixed in core. * * @param mixed ...$args Variable number of parameters to be passed to the walker. * @return mixed */ function wc_walk_category_dropdown_tree( ...$args ) { if ( ! class_exists( 'WC_Product_Cat_Dropdown_Walker', false ) ) { include_once WC()->plugin_path() . '/includes/walkers/class-wc-product-cat-dropdown-walker.php'; } // The user's options are the third parameter. if ( empty( $args[2]['walker'] ) || ! is_a( $args[2]['walker'], 'Walker' ) ) { $walker = new WC_Product_Cat_Dropdown_Walker(); } else { $walker = $args[2]['walker']; } return $walker->walk( ...$args ); } /** * Migrate data from WC term meta to WP term meta. * * When the database is updated to support term meta, migrate WC term meta data across. * We do this when the new version is >= 34370, and the old version is < 34370 (34370 is when term meta table was added). * * @param string $wp_db_version The new $wp_db_version. * @param string $wp_current_db_version The old (current) $wp_db_version. */ function wc_taxonomy_metadata_migrate_data( $wp_db_version, $wp_current_db_version ) { if ( $wp_db_version >= 34370 && $wp_current_db_version < 34370 ) { global $wpdb; if ( $wpdb->query( "INSERT INTO {$wpdb->termmeta} ( term_id, meta_key, meta_value ) SELECT woocommerce_term_id, meta_key, meta_value FROM {$wpdb->prefix}woocommerce_termmeta;" ) ) { $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}woocommerce_termmeta" ); } } } add_action( 'wp_upgrade', 'wc_taxonomy_metadata_migrate_data', 10, 2 ); /** * Move a term before the a given element of its hierarchy level. * * @param int $the_term Term ID. * @param int $next_id The id of the next sibling element in save hierarchy level. * @param string $taxonomy Taxonomy. * @param int $index Term index (default: 0). * @param mixed $terms List of terms. (default: null). * @return int */ function wc_reorder_terms( $the_term, $next_id, $taxonomy, $index = 0, $terms = null ) { if ( ! $terms ) { $terms = get_terms( $taxonomy, 'hide_empty=0&parent=0&menu_order=ASC' ); } if ( empty( $terms ) ) { return $index; } $id = intval( $the_term->term_id ); $term_in_level = false; // Flag: is our term to order in this level of terms. foreach ( $terms as $term ) { $term_id = intval( $term->term_id ); if ( $term_id === $id ) { // Our term to order, we skip. $term_in_level = true; continue; // Our term to order, we skip. } // the nextid of our term to order, lets move our term here. if ( null !== $next_id && $term_id === $next_id ) { $index++; $index = wc_set_term_order( $id, $index, $taxonomy, true ); } // Set order. $index++; $index = wc_set_term_order( $term_id, $index, $taxonomy ); /** * After a term has had it's order set. */ do_action( 'woocommerce_after_set_term_order', $term, $index, $taxonomy ); // If that term has children we walk through them. $children = get_terms( $taxonomy, "parent={$term_id}&hide_empty=0&menu_order=ASC" ); if ( ! empty( $children ) ) { $index = wc_reorder_terms( $the_term, $next_id, $taxonomy, $index, $children ); } } // No nextid meaning our term is in last position. if ( $term_in_level && null === $next_id ) { $index = wc_set_term_order( $id, $index + 1, $taxonomy, true ); } return $index; } /** * Set the sort order of a term. * * @param int $term_id Term ID. * @param int $index Index. * @param string $taxonomy Taxonomy. * @param bool $recursive Recursive (default: false). * @return int */ function wc_set_term_order( $term_id, $index, $taxonomy, $recursive = false ) { $term_id = (int) $term_id; $index = (int) $index; update_term_meta( $term_id, 'order', $index ); if ( ! $recursive ) { return $index; } $children = get_terms( $taxonomy, "parent=$term_id&hide_empty=0&menu_order=ASC" ); foreach ( $children as $term ) { $index++; $index = wc_set_term_order( $term->term_id, $index, $taxonomy, true ); } clean_term_cache( $term_id, $taxonomy ); return $index; } /** * Function for recounting product terms, ignoring hidden products. * * @param array $terms List of terms. * @param object $taxonomy Taxonomy. * @param bool $callback Callback. * @param bool $terms_are_term_taxonomy_ids If terms are from term_taxonomy_id column. */ function _wc_term_recount( $terms, $taxonomy, $callback = true, $terms_are_term_taxonomy_ids = true ) { global $wpdb; /** * Filter to allow/prevent recounting of terms as it could be expensive. * A likely scenario for this is when bulk importing products. We could * then prevent it from recounting per product but instead recount it once * when import is done. Of course this means the import logic has to support this. * * @since 5.2 * @param bool */ if ( ! apply_filters( 'woocommerce_product_recount_terms', true ) ) { return; } // Standard callback. if ( $callback ) { _update_post_term_count( $terms, $taxonomy ); } $exclude_term_ids = array(); $product_visibility_term_ids = wc_get_product_visibility_term_ids(); if ( $product_visibility_term_ids['exclude-from-catalog'] ) { $exclude_term_ids[] = $product_visibility_term_ids['exclude-from-catalog']; } if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && $product_visibility_term_ids['outofstock'] ) { $exclude_term_ids[] = $product_visibility_term_ids['outofstock']; } $query = array( 'fields' => " SELECT COUNT( DISTINCT ID ) FROM {$wpdb->posts} p ", 'join' => '', 'where' => " WHERE 1=1 AND p.post_status = 'publish' AND p.post_type = 'product' ", ); if ( count( $exclude_term_ids ) ) { $query['join'] .= " LEFT JOIN ( SELECT object_id FROM {$wpdb->term_relationships} WHERE term_taxonomy_id IN ( " . implode( ',', array_map( 'absint', $exclude_term_ids ) ) . ' ) ) AS exclude_join ON exclude_join.object_id = p.ID'; $query['where'] .= ' AND exclude_join.object_id IS NULL'; } // Pre-process term taxonomy ids. if ( ! $terms_are_term_taxonomy_ids ) { // We passed in an array of TERMS in format id=>parent. $terms = array_filter( (array) array_keys( $terms ) ); } else { // If we have term taxonomy IDs we need to get the term ID. $term_taxonomy_ids = $terms; $terms = array(); foreach ( $term_taxonomy_ids as $term_taxonomy_id ) { $term = get_term_by( 'term_taxonomy_id', $term_taxonomy_id, $taxonomy->name ); $terms[] = $term->term_id; } } // Exit if we have no terms to count. if ( empty( $terms ) ) { return; } // Ancestors need counting. if ( is_taxonomy_hierarchical( $taxonomy->name ) ) { foreach ( $terms as $term_id ) { $terms = array_merge( $terms, get_ancestors( $term_id, $taxonomy->name ) ); } } // Unique terms only. $terms = array_unique( $terms ); // Count the terms. foreach ( $terms as $term_id ) { $terms_to_count = array( absint( $term_id ) ); if ( is_taxonomy_hierarchical( $taxonomy->name ) ) { // We need to get the $term's hierarchy so we can count its children too. $children = get_term_children( $term_id, $taxonomy->name ); if ( $children && ! is_wp_error( $children ) ) { $terms_to_count = array_unique( array_map( 'absint', array_merge( $terms_to_count, $children ) ) ); } } // Generate term query. $term_query = $query; $term_query['join'] .= " INNER JOIN ( SELECT object_id FROM {$wpdb->term_relationships} INNER JOIN {$wpdb->term_taxonomy} using( term_taxonomy_id ) WHERE term_id IN ( " . implode( ',', array_map( 'absint', $terms_to_count ) ) . ' ) ) AS include_join ON include_join.object_id = p.ID'; // Get the count. // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $count = $wpdb->get_var( implode( ' ', $term_query ) ); // Update the count. update_term_meta( $term_id, 'product_count_' . $taxonomy->name, absint( $count ) ); } delete_transient( 'wc_term_counts' ); } /** * Recount terms after the stock amount changes. * * @param int $product_id Product ID. */ function wc_recount_after_stock_change( $product_id ) { if ( 'yes' !== get_option( 'woocommerce_hide_out_of_stock_items' ) ) { return; } _wc_recount_terms_by_product( $product_id ); } add_action( 'woocommerce_product_set_stock_status', 'wc_recount_after_stock_change' ); /** * Overrides the original term count for product categories and tags with the product count. * that takes catalog visibility into account. * * @param array $terms List of terms. * @param string|array $taxonomies Single taxonomy or list of taxonomies. * @return array */ function wc_change_term_counts( $terms, $taxonomies ) { if ( is_admin() || wp_doing_ajax() ) { return $terms; } if ( ! isset( $taxonomies[0] ) || ! in_array( $taxonomies[0], apply_filters( 'woocommerce_change_term_counts', array( 'product_cat', 'product_tag' ) ), true ) ) { return $terms; } $o_term_counts = get_transient( 'wc_term_counts' ); $term_counts = false === $o_term_counts ? array() : $o_term_counts; foreach ( $terms as &$term ) { if ( is_object( $term ) ) { $term_counts[ $term->term_id ] = isset( $term_counts[ $term->term_id ] ) ? $term_counts[ $term->term_id ] : get_term_meta( $term->term_id, 'product_count_' . $taxonomies[0], true ); if ( '' !== $term_counts[ $term->term_id ] ) { $term->count = absint( $term_counts[ $term->term_id ] ); } } } // Update transient. if ( $term_counts !== $o_term_counts ) { set_transient( 'wc_term_counts', $term_counts, DAY_IN_SECONDS * 30 ); } return $terms; } add_filter( 'get_terms', 'wc_change_term_counts', 10, 2 ); /** * Return products in a given term, and cache value. * * To keep in sync, product_count will be cleared on "set_object_terms". * * @param int $term_id Term ID. * @param string $taxonomy Taxonomy. * @return array */ function wc_get_term_product_ids( $term_id, $taxonomy ) { $product_ids = get_term_meta( $term_id, 'product_ids', true ); if ( false === $product_ids || ! is_array( $product_ids ) ) { $product_ids = get_objects_in_term( $term_id, $taxonomy ); update_term_meta( $term_id, 'product_ids', $product_ids ); } return $product_ids; } /** * When a post is updated and terms recounted (called by _update_post_term_count), clear the ids. * * @param int $object_id Object ID. * @param array $terms An array of object terms. * @param array $tt_ids An array of term taxonomy IDs. * @param string $taxonomy Taxonomy slug. * @param bool $append Whether to append new terms to the old terms. * @param array $old_tt_ids Old array of term taxonomy IDs. */ function wc_clear_term_product_ids( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) { foreach ( $old_tt_ids as $term_id ) { delete_term_meta( $term_id, 'product_ids' ); } foreach ( $tt_ids as $term_id ) { delete_term_meta( $term_id, 'product_ids' ); } } add_action( 'set_object_terms', 'wc_clear_term_product_ids', 10, 6 ); /** * Get full list of product visibility term ids. * * @since 3.0.0 * @return int[] */ function wc_get_product_visibility_term_ids() { if ( ! taxonomy_exists( 'product_visibility' ) ) { wc_doing_it_wrong( __FUNCTION__, 'wc_get_product_visibility_term_ids should not be called before taxonomies are registered (woocommerce_after_register_post_type action).', '3.1' ); return array(); } return array_map( 'absint', wp_parse_args( wp_list_pluck( get_terms( array( 'taxonomy' => 'product_visibility', 'hide_empty' => false, ) ), 'term_taxonomy_id', 'name' ), array( 'exclude-from-catalog' => 0, 'exclude-from-search' => 0, 'featured' => 0, 'outofstock' => 0, 'rated-1' => 0, 'rated-2' => 0, 'rated-3' => 0, 'rated-4' => 0, 'rated-5' => 0, ) ) ); } /** * Recounts all terms. * * @since 5.2 * @return void */ function wc_recount_all_terms() { $product_cats = get_terms( 'product_cat', array( 'hide_empty' => false, 'fields' => 'id=>parent', ) ); _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), true, false ); $product_tags = get_terms( 'product_tag', array( 'hide_empty' => false, 'fields' => 'id=>parent', ) ); _wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), true, false ); } /** * Recounts terms by product. * * @since 5.2 * @param int $product_id The ID of the product. * @return void */ function _wc_recount_terms_by_product( $product_id = '' ) { if ( empty( $product_id ) ) { return; } $product_terms = get_the_terms( $product_id, 'product_cat' ); if ( $product_terms ) { $product_cats = array(); foreach ( $product_terms as $term ) { $product_cats[ $term->term_id ] = $term->parent; } _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), false, false ); } $product_terms = get_the_terms( $product_id, 'product_tag' ); if ( $product_terms ) { $product_tags = array(); foreach ( $product_terms as $term ) { $product_tags[ $term->term_id ] = $term->parent; } _wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), false, false ); } } /** * WooCommerce REST Functions * * Functions for REST specific things. * * @package WooCommerce\Functions * @version 2.6.0 */ defined( 'ABSPATH' ) || exit; /** * Parses and formats a date for ISO8601/RFC3339. * * Required WP 4.4 or later. * See https://developer.wordpress.org/reference/functions/mysql_to_rfc3339/ * * @since 2.6.0 * @param string|null|WC_DateTime $date Date. * @param bool $utc Send false to get local/offset time. * @return string|null ISO8601/RFC3339 formatted datetime. */ function wc_rest_prepare_date_response( $date, $utc = true ) { if ( is_numeric( $date ) ) { $date = new WC_DateTime( "@$date", new DateTimeZone( 'UTC' ) ); $date->setTimezone( new DateTimeZone( wc_timezone_string() ) ); } elseif ( is_string( $date ) ) { $date = new WC_DateTime( $date, new DateTimeZone( 'UTC' ) ); $date->setTimezone( new DateTimeZone( wc_timezone_string() ) ); } if ( ! is_a( $date, 'WC_DateTime' ) ) { return null; } // Get timestamp before changing timezone to UTC. return gmdate( 'Y-m-d\TH:i:s', $utc ? $date->getTimestamp() : $date->getOffsetTimestamp() ); } /** * Returns image mime types users are allowed to upload via the API. * * @since 2.6.4 * @return array */ function wc_rest_allowed_image_mime_types() { return apply_filters( 'woocommerce_rest_allowed_image_mime_types', array( 'jpg|jpeg|jpe' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', 'bmp' => 'image/bmp', 'tiff|tif' => 'image/tiff', 'ico' => 'image/x-icon', 'webp' => 'image/webp', ) ); } /** * Upload image from URL. * * @since 2.6.0 * @param string $image_url Image URL. * @return array|WP_Error Attachment data or error message. */ function wc_rest_upload_image_from_url( $image_url ) { $parsed_url = wp_parse_url( $image_url ); // Check parsed URL. if ( ! $parsed_url || ! is_array( $parsed_url ) ) { /* translators: %s: image URL */ return new WP_Error( 'woocommerce_rest_invalid_image_url', sprintf( __( 'Invalid URL %s.', 'woocommerce' ), $image_url ), array( 'status' => 400 ) ); } // Ensure url is valid. $image_url = esc_url_raw( $image_url ); // download_url function is part of wp-admin. if ( ! function_exists( 'download_url' ) ) { include_once ABSPATH . 'wp-admin/includes/file.php'; } $file_array = array(); $file_array['name'] = basename( current( explode( '?', $image_url ) ) ); // Download file to temp location. $file_array['tmp_name'] = download_url( $image_url ); // If error storing temporarily, return the error. if ( is_wp_error( $file_array['tmp_name'] ) ) { return new WP_Error( 'woocommerce_rest_invalid_remote_image_url', /* translators: %s: image URL */ sprintf( __( 'Error getting remote image %s.', 'woocommerce' ), $image_url ) . ' ' /* translators: %s: error message */ . sprintf( __( 'Error: %s', 'woocommerce' ), $file_array['tmp_name']->get_error_message() ), array( 'status' => 400 ) ); } // Do the validation and storage stuff. $file = wp_handle_sideload( $file_array, array( 'test_form' => false, 'mimes' => wc_rest_allowed_image_mime_types(), ), current_time( 'Y/m' ) ); if ( isset( $file['error'] ) ) { @unlink( $file_array['tmp_name'] ); // @codingStandardsIgnoreLine. /* translators: %s: error message */ return new WP_Error( 'woocommerce_rest_invalid_image', sprintf( __( 'Invalid image: %s', 'woocommerce' ), $file['error'] ), array( 'status' => 400 ) ); } do_action( 'woocommerce_rest_api_uploaded_image_from_url', $file, $image_url ); return $file; } /** * Set uploaded image as attachment. * * @since 2.6.0 * @param array $upload Upload information from wp_upload_bits. * @param int $id Post ID. Default to 0. * @return int Attachment ID */ function wc_rest_set_uploaded_image_as_attachment( $upload, $id = 0 ) { $info = wp_check_filetype( $upload['file'] ); $title = ''; $content = ''; if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) { include_once ABSPATH . 'wp-admin/includes/image.php'; } $image_meta = @wp_read_image_metadata( $upload['file'] ); if ( $image_meta ) { if ( trim( $image_meta['title'] ) && ! is_numeric( sanitize_title( $image_meta['title'] ) ) ) { $title = wc_clean( $image_meta['title'] ); } if ( trim( $image_meta['caption'] ) ) { $content = wc_clean( $image_meta['caption'] ); } } $attachment = array( 'post_mime_type' => $info['type'], 'guid' => $upload['url'], 'post_parent' => $id, 'post_title' => $title ? $title : basename( $upload['file'] ), 'post_content' => $content, ); $attachment_id = wp_insert_attachment( $attachment, $upload['file'], $id ); if ( ! is_wp_error( $attachment_id ) ) { @wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $upload['file'] ) ); } return $attachment_id; } /** * Validate reports request arguments. * * @since 2.6.0 * @param mixed $value Value to validate. * @param WP_REST_Request $request Request instance. * @param string $param Param to validate. * @return WP_Error|boolean */ function wc_rest_validate_reports_request_arg( $value, $request, $param ) { $attributes = $request->get_attributes(); if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) { return true; } $args = $attributes['args'][ $param ]; if ( 'string' === $args['type'] && ! is_string( $value ) ) { /* translators: 1: param 2: type */ return new WP_Error( 'woocommerce_rest_invalid_param', sprintf( __( '%1$s is not of type %2$s', 'woocommerce' ), $param, 'string' ) ); } if ( 'date' === $args['format'] ) { $regex = '#^\d{4}-\d{2}-\d{2}$#'; if ( ! preg_match( $regex, $value, $matches ) ) { return new WP_Error( 'woocommerce_rest_invalid_date', __( 'The date you provided is invalid.', 'woocommerce' ) ); } } return true; } /** * Encodes a value according to RFC 3986. * Supports multidimensional arrays. * * @since 2.6.0 * @param string|array $value The value to encode. * @return string|array Encoded values. */ function wc_rest_urlencode_rfc3986( $value ) { if ( is_array( $value ) ) { return array_map( 'wc_rest_urlencode_rfc3986', $value ); } return str_replace( array( '+', '%7E' ), array( ' ', '~' ), rawurlencode( $value ) ); } /** * Check permissions of posts on REST API. * * @since 2.6.0 * @param string $post_type Post type. * @param string $context Request context. * @param int $object_id Post ID. * @return bool */ function wc_rest_check_post_permissions( $post_type, $context = 'read', $object_id = 0 ) { $contexts = array( 'read' => 'read_private_posts', 'create' => 'publish_posts', 'edit' => 'edit_post', 'delete' => 'delete_post', 'batch' => 'edit_others_posts', ); if ( 'revision' === $post_type ) { $permission = false; } else { $cap = $contexts[ $context ]; $post_type_object = get_post_type_object( $post_type ); $permission = current_user_can( $post_type_object->cap->$cap, $object_id ); } return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, $post_type ); } /** * Check permissions of users on REST API. * * @since 2.6.0 * @param string $context Request context. * @param int $object_id Post ID. * @return bool */ function wc_rest_check_user_permissions( $context = 'read', $object_id = 0 ) { $contexts = array( 'read' => 'list_users', 'create' => 'promote_users', // Check if current user can create users, shop managers are not allowed to create users. 'edit' => 'edit_users', 'delete' => 'delete_users', 'batch' => 'promote_users', ); // Check to allow shop_managers to manage only customers. if ( in_array( $context, array( 'edit', 'delete' ), true ) && wc_current_user_has_role( 'shop_manager' ) ) { $permission = false; $user_data = get_userdata( $object_id ); $shop_manager_editable_roles = apply_filters( 'woocommerce_shop_manager_editable_roles', array( 'customer' ) ); if ( isset( $user_data->roles ) ) { $can_manage_users = array_intersect( $user_data->roles, array_unique( $shop_manager_editable_roles ) ); // Check if Shop Manager can edit customer or with the is same shop manager. if ( 0 < count( $can_manage_users ) || intval( $object_id ) === intval( get_current_user_id() ) ) { $permission = current_user_can( $contexts[ $context ], $object_id ); } } } else { $permission = current_user_can( $contexts[ $context ], $object_id ); } return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, 'user' ); } /** * Check permissions of product terms on REST API. * * @since 2.6.0 * @param string $taxonomy Taxonomy. * @param string $context Request context. * @param int $object_id Post ID. * @return bool */ function wc_rest_check_product_term_permissions( $taxonomy, $context = 'read', $object_id = 0 ) { $contexts = array( 'read' => 'manage_terms', 'create' => 'edit_terms', 'edit' => 'edit_terms', 'delete' => 'delete_terms', 'batch' => 'edit_terms', ); $cap = $contexts[ $context ]; $taxonomy_object = get_taxonomy( $taxonomy ); $permission = current_user_can( $taxonomy_object->cap->$cap, $object_id ); return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, $taxonomy ); } /** * Check manager permissions on REST API. * * @since 2.6.0 * @param string $object Object. * @param string $context Request context. * @return bool */ function wc_rest_check_manager_permissions( $object, $context = 'read' ) { $objects = array( 'reports' => 'view_woocommerce_reports', 'settings' => 'manage_woocommerce', 'system_status' => 'manage_woocommerce', 'attributes' => 'manage_product_terms', 'shipping_methods' => 'manage_woocommerce', 'payment_gateways' => 'manage_woocommerce', 'webhooks' => 'manage_woocommerce', ); $permission = current_user_can( $objects[ $object ] ); return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, 0, $object ); } /** * Check product reviews permissions on REST API. * * @since 3.5.0 * @param string $context Request context. * @param string $object_id Object ID. * @return bool */ function wc_rest_check_product_reviews_permissions( $context = 'read', $object_id = 0 ) { $permission = false; $contexts = array( 'read' => 'moderate_comments', 'create' => 'edit_products', 'edit' => 'edit_products', 'delete' => 'edit_products', 'batch' => 'edit_products', ); if ( $object_id > 0 ) { $object = get_comment( $object_id ); if ( ! is_a( $object, 'WP_Comment' ) || get_comment_type( $object ) !== 'review' ) { return false; } } if ( isset( $contexts[ $context ] ) ) { $permission = current_user_can( $contexts[ $context ], $object_id ); } return apply_filters( 'woocommerce_rest_check_permissions', $permission, $context, $object_id, 'product_review' ); } /** * Returns true if the current REST request is from the product editor. * * @since 8.9.0 * @return bool */ function wc_rest_is_from_product_editor() { return isset( $_SERVER['HTTP_X_WC_FROM_PRODUCT_EDITOR'] ) && '1' === $_SERVER['HTTP_X_WC_FROM_PRODUCT_EDITOR']; } /** * WooCommerce Message Functions * * Functions for error/message handling and display. * * @package WooCommerce\Functions * @version 2.1.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; } /** * Get the count of notices added, either for all notices (default) or for one. * particular notice type specified by $notice_type. * * @since 2.1 * @param string $notice_type Optional. The name of the notice type - either error, success or notice. * @return int */ function wc_notice_count( $notice_type = '' ) { if ( ! did_action( 'woocommerce_init' ) ) { wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' ); return; } $notice_count = 0; $all_notices = WC()->session->get( 'wc_notices', array() ); if ( isset( $all_notices[ $notice_type ] ) && is_array( $all_notices[ $notice_type ] ) ) { $notice_count = count( $all_notices[ $notice_type ] ); } elseif ( empty( $notice_type ) ) { foreach ( $all_notices as $notices ) { if ( is_countable( $notices ) ) { $notice_count += count( $notices ); } } } return $notice_count; } /** * Check if a notice has already been added. * * @since 2.1 * @param string $message The text to display in the notice. * @param string $notice_type Optional. The name of the notice type - either error, success or notice. * @return bool */ function wc_has_notice( $message, $notice_type = 'success' ) { if ( ! did_action( 'woocommerce_init' ) ) { wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' ); return false; } $notices = WC()->session->get( 'wc_notices', array() ); $notices = isset( $notices[ $notice_type ] ) ? $notices[ $notice_type ] : array(); return array_search( $message, wp_list_pluck( $notices, 'notice' ), true ) !== false; } /** * Add and store a notice. * * @since 2.1 * @version 3.9.0 * @param string $message The text to display in the notice. * @param string $notice_type Optional. The name of the notice type - either error, success or notice. * @param array $data Optional notice data. */ function wc_add_notice( $message, $notice_type = 'success', $data = array() ) { if ( ! did_action( 'woocommerce_init' ) ) { wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' ); return; } $notices = WC()->session->get( 'wc_notices', array() ); // Backward compatibility. if ( 'success' === $notice_type ) { $message = apply_filters( 'woocommerce_add_message', $message ); } $message = apply_filters( 'woocommerce_add_' . $notice_type, $message ); if ( ! empty( $message ) ) { $notices[ $notice_type ][] = array( 'notice' => $message, 'data' => $data, ); } WC()->session->set( 'wc_notices', $notices ); } /** * Set all notices at once. * * @since 2.6.0 * @param array[] $notices Array of notices. */ function wc_set_notices( $notices ) { if ( ! did_action( 'woocommerce_init' ) ) { wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.6' ); return; } WC()->session->set( 'wc_notices', $notices ); } /** * Unset all notices. * * @since 2.1 */ function wc_clear_notices() { if ( ! did_action( 'woocommerce_init' ) ) { wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' ); return; } WC()->session->set( 'wc_notices', null ); } /** * Prints messages and errors which are stored in the session, then clears them. * * @since 2.1 * @param bool $return true to return rather than echo. @since 3.5.0. * @return string|void */ function wc_print_notices( $return = false ) { if ( ! did_action( 'woocommerce_init' ) ) { wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' ); return; } $session = WC()->session; // If the session handler has not initialized, there will be no notices for us to read. if ( null === $session ) { return; } $all_notices = $session->get( 'wc_notices', array() ); $notice_types = apply_filters( 'woocommerce_notice_types', array( 'error', 'success', 'notice' ) ); // Buffer output. ob_start(); foreach ( $notice_types as $notice_type ) { if ( wc_notice_count( $notice_type ) > 0 ) { $messages = array(); foreach ( $all_notices[ $notice_type ] as $notice ) { $messages[] = isset( $notice['notice'] ) ? $notice['notice'] : $notice; } wc_get_template( "notices/{$notice_type}.php", array( 'messages' => array_filter( $messages ), // @deprecated 3.9.0 'notices' => array_filter( $all_notices[ $notice_type ] ), ) ); } } wc_clear_notices(); $notices = wc_kses_notice( ob_get_clean() ); if ( $return ) { return $notices; } echo $notices; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** * Print a single notice immediately. * * @since 2.1 * @version 3.9.0 * @param string $message The text to display in the notice. * @param string $notice_type Optional. The singular name of the notice type - either error, success or notice. * @param array $data Optional notice data. @since 3.9.0. * @param bool $return true to return rather than echo. @since 7.7.0. */ function wc_print_notice( $message, $notice_type = 'success', $data = array(), $return = false ) { if ( 'success' === $notice_type ) { $message = apply_filters( 'woocommerce_add_message', $message ); } $message = apply_filters( 'woocommerce_add_' . $notice_type, $message ); // Buffer output. ob_start(); wc_get_template( "notices/{$notice_type}.php", array( 'messages' => array( $message ), // @deprecated 3.9.0 'notices' => array( array( 'notice' => $message, 'data' => $data, ), ), ) ); $notice = wc_kses_notice( ob_get_clean() ); if ( $return ) { return $notice; } echo $notice; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** * Returns all queued notices, optionally filtered by a notice type. * * @since 2.1 * @version 3.9.0 * @param string $notice_type Optional. The singular name of the notice type - either error, success or notice. * @return array[] */ function wc_get_notices( $notice_type = '' ) { if ( ! did_action( 'woocommerce_init' ) ) { wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' ); return; } $all_notices = WC()->session->get( 'wc_notices', array() ); if ( empty( $notice_type ) ) { $notices = $all_notices; } elseif ( isset( $all_notices[ $notice_type ] ) ) { $notices = $all_notices[ $notice_type ]; } else { $notices = array(); } return $notices; } /** * Add notices for WP Errors. * * @param WP_Error $errors Errors. */ function wc_add_wp_error_notices( $errors ) { if ( is_wp_error( $errors ) && $errors->get_error_messages() ) { foreach ( $errors->get_error_messages() as $error ) { wc_add_notice( $error, 'error' ); } } } /** * Filters out the same tags as wp_kses_post, but allows tabindex for element. * * @since 3.5.0 * @param string $message Content to filter through kses. * @return string */ function wc_kses_notice( $message ) { $allowed_tags = array_replace_recursive( wp_kses_allowed_html( 'post' ), array( 'a' => array( 'tabindex' => true, ), ) ); /** * Kses notice allowed tags. * * @since 3.9.0 * @param array[]|string $allowed_tags An array of allowed HTML elements and attributes, or a context name such as 'post'. */ return wp_kses( $message, apply_filters( 'woocommerce_kses_notice_allowed_tags', $allowed_tags ) ); } /** * Get notice data attribute. * * @since 3.9.0 * @param array $notice Notice data. * @return string */ function wc_get_notice_data_attr( $notice ) { if ( empty( $notice['data'] ) ) { return; } $attr = ''; foreach ( $notice['data'] as $key => $value ) { $attr .= sprintf( ' data-%1$s="%2$s"', sanitize_title( $key ), esc_attr( $value ) ); } return $attr; } @icomoon-font-family: "vcpb-plugin-icons"; @icomoon-font-path: "../fonts/vc_icons_v3/fonts"; @vc-c-icon-seo: "\e927"; @vc-c-icon-clone: "\e926"; @vc-c-icon-paste: "\e924"; @vc-c-icon-copy: "\e925"; @vc-c-icon-layout_portrait-smartphones: "\e917"; @vc-c-icon-layout_landscape-smartphones: "\e918"; @vc-c-icon-layout_portrait-tablets: "\e919"; @vc-c-icon-layout_landscape-tablets: "\e91a"; @vc-c-icon-layout_default: "\e91b"; @vc-c-icon-cog: "\e91f"; @vc-c-icon-add_template: "\e920"; @vc-c-icon-more: "\e923"; @vc-c-icon-text-block: "\e903"; @vc-c-icon-fullscreen_exit: "\e90d"; @vc-c-icon-fullscreen: "\e910"; @vc-c-icon-close: "\e914"; @vc-c-icon-add_element: "\e915"; @vc-c-icon-redo: "\e921"; @vc-c-icon-undo: "\e922"; @vc-c-icon-minimize: "\e91c"; @vc-c-icon-1-6_4-6_1-6: "\e90e"; @vc-c-icon-arrow_back: "\e5c4"; @vc-c-icon-arrow_drop_down: "\e5c5"; @vc-c-icon-arrow_drop_up: "\e5c7"; @vc-c-icon-arrow_forward: "\e5c8"; @vc-c-icon-check: "\e5ca"; @vc-c-icon-arrow_upward: "\e5d8"; @vc-c-icon-arrow_downward: "\e5db"; @vc-c-icon-sync: "\e627"; @vc-c-icon-search: "\e8b6"; @vc-c-icon-1-1: "\e900"; @vc-c-icon-1-2_1-2: "\e901"; @vc-c-icon-1-3_1-3_1-3: "\e902"; @vc-c-icon-1-4_1-2_1-4: "\e904"; @vc-c-icon-1-4_1-4_1-4_1-4: "\e905"; @vc-c-icon-1-4_3-4: "\e906"; @vc-c-icon-1-6_1-6_1-6_1-2: "\e907"; @vc-c-icon-1-6_1-6_1-6_1-6_1-6_1-6: "\e908"; @vc-c-icon-l_15_15_15_15_15: "\e909"; @vc-c-icon-1-6_2-3_1-6: "\e90a"; @vc-c-icon-2-3_1-3: "\e90b"; @vc-c-icon-5-6_1-6: "\e90c"; @vc-c-icon-add: "\e90f"; @vc-c-icon-content_copy: "\e911"; @vc-c-icon-delete_empty: "\e912"; @vc-c-icon-dragndrop: "\e913"; @vc-c-icon-arrow_drop_right: "\e916"; @vc-c-icon-mode_edit: "\e91d"; @vc-c-icon-row_default_fe: "\e91e"; /** * WooCommerce Admin Updates * * Functions for updating data, used by the background updater. * * @package WooCommerce\Admin */ use Automattic\WooCommerce\Admin\Features\OnboardingTasks\TaskLists; use Automattic\WooCommerce\Admin\Notes\Notes; use Automattic\WooCommerce\Internal\Admin\Notes\UnsecuredReportFiles; use Automattic\WooCommerce\Admin\ReportExporter; /** * Update order stats `status` index length. * See: https://github.com/woocommerce/woocommerce-admin/issues/2969. */ function wc_admin_update_0201_order_status_index() { global $wpdb; // Max DB index length. See wp_get_db_schema(). $max_index_length = 191; $index = $wpdb->get_row( "SHOW INDEX FROM {$wpdb->prefix}wc_order_stats WHERE key_name = 'status'" ); if ( property_exists( $index, 'Sub_part' ) ) { // The index was created with the right length. Time to bail. if ( $max_index_length === $index->Sub_part ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName return; } // We need to drop the index so it can be recreated. $wpdb->query( "DROP INDEX `status` ON {$wpdb->prefix}wc_order_stats" ); } // Recreate the status index with a max length. $wpdb->query( $wpdb->prepare( "ALTER TABLE {$wpdb->prefix}wc_order_stats ADD INDEX status (status(%d))", $max_index_length ) ); } /** * Rename "gross_total" to "total_sales". * See: https://github.com/woocommerce/woocommerce-admin/issues/3175 */ function wc_admin_update_0230_rename_gross_total() { global $wpdb; // We first need to drop the new `total_sales` column, since dbDelta() will have created it. $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_order_stats DROP COLUMN `total_sales`" ); // Then we can rename the existing `gross_total` column. $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_order_stats CHANGE COLUMN `gross_total` `total_sales` double DEFAULT 0 NOT NULL" ); } /** * Remove the note unsnoozing scheduled action. */ function wc_admin_update_0251_remove_unsnooze_action() { as_unschedule_action( Notes::UNSNOOZE_HOOK, null, 'wc-admin-data' ); as_unschedule_action( Notes::UNSNOOZE_HOOK, null, 'wc-admin-notes' ); } /** * Remove Facebook Extension note. */ function wc_admin_update_110_remove_facebook_note() { Notes::delete_notes_with_name( 'wc-admin-facebook-extension' ); } /** * Remove Dismiss action from tracking opt-in admin note. */ function wc_admin_update_130_remove_dismiss_action_from_tracking_opt_in_note() { global $wpdb; $wpdb->query( "DELETE actions FROM {$wpdb->prefix}wc_admin_note_actions actions INNER JOIN {$wpdb->prefix}wc_admin_notes notes USING (note_id) WHERE actions.name = 'tracking-dismiss' AND notes.name = 'wc-admin-usage-tracking-opt-in'" ); } /** * Update DB Version. */ function wc_admin_update_130_db_version() { Installer::update_db_version( '1.3.0' ); } /** * Update DB Version. */ function wc_admin_update_140_db_version() { Installer::update_db_version( '1.4.0' ); } /** * Remove Facebook Experts note. */ function wc_admin_update_160_remove_facebook_note() { Notes::delete_notes_with_name( 'wc-admin-facebook-marketing-expert' ); } /** * Set "two column" homescreen layout as default for existing stores. */ function wc_admin_update_170_homescreen_layout() { add_option( 'woocommerce_default_homepage_layout', 'two_columns', '', 'no' ); } /** * Delete the preexisting export files. */ function wc_admin_update_270_delete_report_downloads() { $upload_dir = wp_upload_dir(); $base_dir = trailingslashit( $upload_dir['basedir'] ); $failed_files = array(); $exports_status = get_option( ReportExporter::EXPORT_STATUS_OPTION, array() ); $has_failure = false; if ( ! is_array( $exports_status ) ) { // This is essentially the same path as files failing deletion. Handle as such. return; } // Delete all export files based on the status option values. foreach ( $exports_status as $key => $progress ) { list( $report_type, $export_id ) = explode( ':', $key ); if ( ! $export_id ) { continue; } $file = "{$base_dir}wc-{$report_type}-report-export-{$export_id}.csv"; $header = $file . '.headers'; // phpcs:ignore if ( @file_exists( $file ) && false === @unlink( $file ) ) { array_push( $failed_files, $file ); } // phpcs:ignore if ( @file_exists( $header ) && false === @unlink( $header ) ) { array_push( $failed_files, $header ); } } // If the status option was missing or corrupt, there will be files left over. $potential_exports = glob( $base_dir . 'wc-*-report-export-*.csv' ); $reports_pattern = '(revenue|products|variations|orders|categories|coupons|taxes|stock|customers|downloads)'; /** * Look for files we can be reasonably sure were created by the report export. * * Export files we created will match the 'wc-*-report-export-*.csv' glob, with * the first wildcard being one of the exportable report slugs, and the second * being an integer with 11-14 digits (from microtime()'s output) that represents * a time in the past. */ foreach ( $potential_exports as $potential_export ) { $matches = array(); // See if the filename matches an unfiltered export pattern. if ( ! preg_match( "/wc-{$reports_pattern}-report-export-(?P\d{11,14})\.csv\$/", $potential_export, $matches ) ) { $has_failure = true; continue; } // Validate the timestamp (anything in the past). $timestamp = (int) substr( $matches['export_id'], 0, 10 ); if ( ! $timestamp || $timestamp > time() ) { $has_failure = true; continue; } // phpcs:ignore if ( false === @unlink( $potential_export ) ) { array_push( $failed_files, $potential_export ); } } // Try deleting failed files once more. foreach ( $failed_files as $failed_file ) { // phpcs:ignore if ( false === @unlink( $failed_file ) ) { $has_failure = true; } } if ( $has_failure ) { UnsecuredReportFiles::possibly_add_note(); } } /** * Update the old task list options. */ function wc_admin_update_271_update_task_list_options() { $hidden_lists = get_option( 'woocommerce_task_list_hidden_lists', array() ); $setup_list_hidden = get_option( 'woocommerce_task_list_hidden', 'no' ); $extended_list_hidden = get_option( 'woocommerce_extended_task_list_hidden', 'no' ); if ( 'yes' === $setup_list_hidden ) { $hidden_lists[] = 'setup'; } if ( 'yes' === $extended_list_hidden ) { $hidden_lists[] = 'extended'; } update_option( 'woocommerce_task_list_hidden_lists', array_unique( $hidden_lists ) ); delete_option( 'woocommerce_task_list_hidden' ); delete_option( 'woocommerce_extended_task_list_hidden' ); } /** * Update order stats `status`. */ function wc_admin_update_280_order_status() { global $wpdb; $wpdb->query( "UPDATE {$wpdb->prefix}wc_order_stats refunds INNER JOIN {$wpdb->prefix}wc_order_stats orders ON orders.order_id = refunds.parent_id SET refunds.status = orders.status WHERE refunds.parent_id != 0" ); } /** * Update the old task list options. */ function wc_admin_update_290_update_apperance_task_option() { $is_actioned = get_option( 'woocommerce_task_list_appearance_complete', false ); $task = TaskLists::get_task( 'appearance' ); if ( $task && $is_actioned ) { $task->mark_actioned(); } delete_option( 'woocommerce_task_list_appearance_complete' ); } /** * Delete the old woocommerce_default_homepage_layout option. */ function wc_admin_update_290_delete_default_homepage_layout_option() { delete_option( 'woocommerce_default_homepage_layout' ); } /** * Use woocommerce_admin_activity_panel_inbox_last_read from the user meta to set wc_admin_notes.is_read col. */ function wc_admin_update_300_update_is_read_from_last_read() { global $wpdb; $meta_key = 'woocommerce_admin_activity_panel_inbox_last_read'; // phpcs:ignore $users = get_users( "meta_key={$meta_key}&orderby={$meta_key}&fields=all_with_meta&number=1" ); if ( count( $users ) ) { $last_read = current( $users )->{$meta_key}; $date_in_utc = gmdate( 'Y-m-d H:i:s', intval( $last_read ) / 1000 ); $wpdb->query( $wpdb->prepare( " update {$wpdb->prefix}wc_admin_notes set is_read = 1 where date_created <= %s", $date_in_utc ) ); $wpdb->query( $wpdb->prepare( "delete from {$wpdb->usermeta} where meta_key=%s", $meta_key ) ); } } /** * Delete "is_primary" column from the wc_admin_notes table. */ function wc_admin_update_340_remove_is_primary_from_note_action() { global $wpdb; $wpdb->query( "ALTER TABLE {$wpdb->prefix}wc_admin_note_actions DROP COLUMN `is_primary`" ); } /** * Delete the deprecated remote inbox notifications option since transients are now used. */ function wc_update_670_delete_deprecated_remote_inbox_notifications_option() { delete_option( 'wc_remote_inbox_notifications_specs' ); }