���� JFIF fdasasfas213sdaf
Server IP : 147.79.69.172 / Your IP : 216.73.216.222 Web Server : LiteSpeed System : Linux in-mum-web669.main-hosting.eu 5.14.0-503.23.2.el9_5.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Feb 12 05:52:18 EST 2025 x86_64 User : u479334040 ( 479334040) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /home/u479334040/domains/omjaindia.com/public_html/wp-content/plugins/dashify/ |
Upload File : |
<?php /* * Plugin Name: Dashify * Description: A modern design and UI for the WooCommerce admin. Manage, search, and navigate orders faster. Make the WordPress admin dashboard ecommerce-focused. * Version: 1.3.14 * Author: Dashify * License: GPLv2 or later * License URI: https://www.gnu.org/licenses/gpl-2.0.html * Requires Plugins: woocommerce */ if ( ! defined( 'ABSPATH' ) ) { exit; } define( 'DASHIFY_BASE_FILE', __FILE__ ); define( 'DASHIFY_BASENAME', plugin_basename( __FILE__ ) ); define( 'DASHIFY_PATH', plugin_dir_path( __FILE__ ) ); require_once DASHIFY_PATH . 'polyfill.php'; require_once DASHIFY_PATH . 'modules/plugin-action-links/class-plugin-action-links.php'; use Dashify\Modules; class Dashify_Base { const VERSION = '1.3.14'; // HPOS const SUBSCRIPTION_PAGE_ID = 'woocommerce_page_wc-orders--shop_subscription'; // Non-HPOS (Yes, the page IDs in WooCommerce are not intuitive and seem backwards.) const LEGACY_SUBSCRIPTION_LIST_PAGE_ID = 'edit-shop_subscription'; const LEGACY_SUBSCRIPTION_EDIT_PAGE_ID = 'shop_subscription'; public function __construct() { add_action( 'init', array( $this, 'init' ) ); } public function init() { // If not viewing the admin, or if on the frontend and cannot see the // admin bar, there’s no need to load Dashify. if ( ! is_admin() && ! is_admin_bar_showing() ) { return; } add_action( 'admin_enqueue_scripts', array( $this, 'dashify_admin_enqueue_scripts' ) ); add_filter( 'dashify_current_page_filter', array( $this, 'subscriptions_current_page' ), 10, 2 ); register_activation_hook( __FILE__, array( $this, 'move_subscription_edit_meta_boxes' ) ); register_deactivation_hook( __FILE__, array( $this, 'restore_subscription_edit_meta_box_layout' ) ); add_action( 'wp_ajax_save_dashify_on', array( $this, 'save_dashify_on' ) ); add_action( 'wp_ajax_save_dashify_option', array( $this, 'save_dashify_option' ) ); add_action( 'wp_ajax_mark_notice_dismissed', array( $this, 'mark_notice_dismissed' ) ); add_action( 'wp_ajax_dashify_order_list_analytics', array( $this, 'order_list_analytics' ) ); register_activation_hook( __FILE__, array( $this, 'move_order_edit_meta_boxes' ) ); register_deactivation_hook( __FILE__, array( $this, 'restore_order_edit_meta_box_layout' ) ); new Modules\Plugin_Action_Links(); add_filter( 'plugin_row_meta', array( $this, 'dashify_row_meta' ), 10, 2 ); add_action( 'admin_init', array( $this, 'migrate_order_edit_meta_box_layout_to_user_meta' ) ); add_action( 'admin_init', array( $this, 'migrate_subscription_edit_meta_box_layout_to_user_meta' ) ); require_once DASHIFY_PATH . 'settings.php'; $settings = Dashify_Settings::get_instance(); $settings->init(); add_filter( 'dashify_settings', array( $this, 'list_table_settings' ), 11 ); require_once DASHIFY_PATH . 'modules/navigation/navigation.php'; new Dashify_Navigation(); } /** * Updates value of dashify_on option as requested */ public function save_dashify_on() { if ( empty( $_POST['dashify_on'] ) ) { return; } check_ajax_referer( 'save_dashify_option_nonce' ); update_option( 'dashify_on', sanitize_key( $_POST['dashify_on'] ) ); if ( $this->dashify_on() ) { $this->move_order_edit_meta_boxes(); if ( is_plugin_active( 'woocommerce-subscriptions/woocommerce-subscriptions.php' ) ) { $this->move_subscription_edit_meta_boxes(); } } else { $this->restore_order_edit_meta_box_layout(); if ( is_plugin_active( 'woocommerce-subscriptions/woocommerce-subscriptions.php' ) ) { $this->restore_subscription_edit_meta_box_layout(); } } wp_die(); // this is required to terminate immediately and return a proper response } public function save_dashify_option() { if ( empty( $_POST['option_name'] ) || empty( $_POST['option_value'] ) ) { return; } $allowed_option_names = array( 'dashify_line_item_menu_order_column_enabled', 'dashify_line_item_menu_order_column_default_sort', ); if ( ! in_array( $_POST['option_name'], $allowed_option_names ) ) { return; } check_ajax_referer( 'save_dashify_option_nonce' ); $result = update_option( sanitize_key( $_POST['option_name'] ), sanitize_text_field( $_POST['option_value'] ) ); if ( ! $result ) { echo 'Something went wrong. The options were not saved.'; } wp_die(); } /** * Marks the release notes dismissed for the current version of Dashify. */ public function mark_notice_dismissed() { check_ajax_referer( 'mark_notice_dismissed_nonce' ); if ( isset( $_POST['option'] ) && 'forever' === $_POST['option'] ) { update_option( 'dashify_dismissed_release_notices_forever', 1 ); return; } update_option( 'dashify_dismissed_notices', array_merge( get_option( 'dashify_dismissed_notices', array() ), array( self::VERSION ) ) ); wp_die(); } public static function dashify_on() { return ( get_option( 'dashify_on', 'true' ) === 'true' ) ? 1 : 0; } public function order_list_analytics() { if ( ! isset( $_POST['days'] ) ) { return; } check_ajax_referer( 'dashify_order_list_analytics_nonce' ); update_option( 'dashify_analytics_range', intval( sanitize_key( $_POST['days'] ) ) ); // We can't use the class method here for determining this because when // doing AJAX it doesn't know which page it's on from the PHP side. $is_subscription_list = sanitize_key( $_POST['is_subscriptions'] ); try { $range = get_option( 'dashify_analytics_range' ); $analytics = $is_subscription_list ? $this->calculate_subscription_list_analytics( $range ) : $this->calculate_order_list_analytics( $range ); } catch ( Exception $exception ) { error_log( $exception ); } $sections = $is_subscription_list ? $this->create_subscription_list_analytics_sections( $analytics ) : $this->create_order_list_analytics_sections( $analytics ); wp_send_json_success( array( 'analytics_range' => $analytics['analytics_range'], 'sections' => $sections, 'intervalData' => $analytics['intervalData'], 'graphDivisions' => $analytics['graphDivisions'], 'divisionMaxima' => $analytics['divisionMaxima'], ) ); } /** * Restore the original positions of the order meta boxes. */ public function restore_order_edit_meta_box_layout() { $default = array( 'normal' => 'woocommerce-order-data,woocommerce-order-items,woocommerce-order-downloads', 'side' => 'woocommerce-order-actions,woocommerce-order-source-data,woocommerce-customer-history,woocommerce-order-notes', 'advanced' => 'order_custom', ); $page = $this->isHPOS() ? 'woocommerce_page_wc-orders' : 'shop_order'; update_user_meta( get_current_user_id(), "meta-box-order_$page", $default ); } public function move_order_edit_meta_boxes() { $page = $this->isHPOS() ? 'woocommerce_page_wc-orders' : 'shop_order'; $layout = $this->get_layout( $page ); // If they have never moved the meta boxes, $layout will be an empty string, // so we need to set a default before we move them around. if ( empty( $layout ) ) { $this->restore_order_edit_meta_box_layout(); $layout = $this->get_layout( $page ); } $layout = $this->move_meta_box( $layout, 'woocommerce-order-data', 'normal', 'side', 'prepend' ); $layout = $this->move_meta_box( $layout, 'woocommerce-order-downloads', 'normal', 'side', 'append' ); $layout = $this->move_meta_box( $layout, 'order_custom', 'advanced', 'side', 'append' ); $layout = $this->move_meta_box( $layout, 'woocommerce-order-notes', 'side', 'normal', 'append' ); $this->update_layout( $page, $layout ); } public function restore_subscription_edit_meta_box_layout() { $default = array( 'normal' => 'woocommerce-subscription-data,subscription_renewal_orders,woocommerce-order-items,woocommerce-order-downloads', 'side' => 'woocommerce-order-actions,woocommerce-order-source-data,woocommerce-customer-history,woocommerce-subscription-schedule,woocommerce-order-notes', 'advanced' => $this->isHPOS() ? 'order_custom' : 'postcustom', ); $page = $this->isHPOS() ? $this::SUBSCRIPTION_PAGE_ID : $this::LEGACY_SUBSCRIPTION_EDIT_PAGE_ID; update_user_meta( get_current_user_id(), "meta-box-order_$page", $default ); } public function move_subscription_edit_meta_boxes() { if ( ! is_plugin_active( 'woocommerce-subscriptions/woocommerce-subscriptions.php' ) ) { return; } $page = $this->isHPOS() ? $this::SUBSCRIPTION_PAGE_ID : $this::LEGACY_SUBSCRIPTION_EDIT_PAGE_ID; $layout = $this->get_layout( $page ); // If they have never moved the meta boxes, $layout will be an empty string, // so we need to set a default before we move them around. if ( empty( $layout ) ) { $this->restore_subscription_edit_meta_box_layout(); $layout = $this->get_layout( $page ); } $layout = $this->move_meta_box( $layout, 'woocommerce-subscription-data', 'normal', 'side', 'prepend' ); $layout = $this->move_meta_box( $layout, 'woocommerce-order-downloads', 'normal', 'side', 'append' ); $layout = $this->move_meta_box( $layout, $this->isHPOS() ? 'order_custom' : 'postcustom', 'advanced', 'side', 'append' ); $layout = $this->move_meta_box( $layout, 'subscription_renewal_orders', 'side', 'normal', 'append' ); $layout = $this->move_meta_box( $layout, 'woocommerce-subscription-schedule', 'side', 'normal', 'append' ); $layout = $this->move_meta_box( $layout, 'woocommerce-order-notes', 'side', 'normal', 'append' ); $this->update_layout( $page, $layout ); } private function get_layout( $page ) { return get_user_meta( get_current_user_id(), "meta-box-order_$page", true ); } private function update_layout( $page, $layout ) { update_user_meta( get_current_user_id(), "meta-box-order_$page", $layout ); } private function move_meta_box( $layout, $box_id, $from_section, $to_section, $position = 'append' ) { $layout[ $from_section ] = $this->remove_from_csv( $layout[ $from_section ], $box_id ); if ( ! str_contains( $layout[ $to_section ], $box_id ) ) { $layout[ $to_section ] = 'prepend' === $position ? $box_id . ',' . $layout[ $to_section ] : $layout[ $to_section ] . ',' . $box_id; } return $layout; } private function remove_from_csv( $csv, $to_remove ) { return implode( ',', array_diff( str_getcsv( $csv ), array( $to_remove ) ) ); } /** * Is WooCommerce High Performance Order Storage is enabled? */ private function isHPOS(): bool { return get_option( 'woocommerce_custom_orders_table_enabled' ) === 'yes'; } function dashify_row_meta( $plugin_meta, $plugin_file ) { if ( strpos( $plugin_file, 'dashify.php' ) === false ) { return $plugin_meta; } if ( ! is_plugin_active( 'dashify-pro/dashify-pro.php' ) ) { $new_links = array( 'upgrade' => '<a href="https://getdashify.com/#pro" target="_blank">Upgrade to Pro</a>', ); $plugin_meta = array_merge( $plugin_meta, $new_links ); } return $plugin_meta; } public function dashify_admin_enqueue_scripts() { $current_page = $this->dashify_current_page( get_current_screen()->id ); $pages_to_load_dashify = array( 'order_list', 'order_summary', ); if ( ! in_array( $current_page, $pages_to_load_dashify ) && ! $this->is_subscription_table_page() && ! $this->is_subscription_edit_page() ) { return; } // screen option must load regardless of dashify_on option self::dashify_enqueue_screen_options_files( $this->dashify_on() ); if ( ! $this->dashify_on() ) { return; } /* begin: for all pages with active dashify */ $this->dashify_enqueue_utils(); self::enqueue_dismissible(); /* end: for all pages with active dashify */ if ( 'order_summary' === $current_page ) { $this->dashify_enqueue_order_summary_files(); } elseif ( $this->is_subscription_edit_page() ) { $this->dashify_enqueue_order_summary_files(); $this->dashify_enqueue_subscription_edit_files(); } elseif ( 'order_list' === $current_page ) { $this->dashify_enqueue_table_files(); $this->dashify_enqueue_order_table_files(); } elseif ( $this->is_subscription_table_page() ) { $this->dashify_enqueue_table_files(); $this->dashify_enqueue_subscription_table_files(); } } private function is_subscription_table_page() { $screen_id = get_current_screen()->id; return $this->is_HPOS_subscription_table_page( $screen_id ) || $this->is_legacy_subscription_table_page( $screen_id ); } private function is_HPOS_subscription_table_page( $screen_id ) { return $this::SUBSCRIPTION_PAGE_ID === $screen_id && ( ! isset( $_GET['action'] ) || ( isset( $_GET['action'] ) && 'edit' !== $_GET['action'] && 'new' !== $_GET['action'] ) ); } private function is_legacy_subscription_table_page( $screen_id ) { return $this::LEGACY_SUBSCRIPTION_LIST_PAGE_ID === $screen_id; } private function is_subscription_edit_page() { $screen_id = get_current_screen()->id; return $this->is_HPOS_subscription_edit_page( $screen_id ) || $this->is_legacy_subscription_edit_page( $screen_id ); } /** * This function also considers the add new subscription page to be an edit * page, as it’s nearly identical. */ private function is_HPOS_subscription_edit_page( $screen_id ) { return $this::SUBSCRIPTION_PAGE_ID === $screen_id && ( isset( $_GET['action'] ) && ( 'edit' === $_GET['action'] || 'new' === $_GET['action'] ) ); } private function is_legacy_subscription_edit_page( $screen_id ) { return $this::LEGACY_SUBSCRIPTION_EDIT_PAGE_ID === $screen_id; } // This filter callback is used in the filter dashify_current_page_filter. public function subscriptions_current_page( $page, $screen_id ) { if ( $this::SUBSCRIPTION_PAGE_ID === $screen_id && ( ! isset( $_GET['action'] ) || ( isset( $_GET['action'] ) && 'edit' !== $_GET['action'] ) ) ) { $page = 'subscription_list'; } if ( $this::SUBSCRIPTION_PAGE_ID === $screen_id && isset( $_GET['action'] ) && 'edit' === $_GET['action'] ) { $page = 'subscription_edit'; } if ( $this::LEGACY_SUBSCRIPTION_LIST_PAGE_ID === $screen_id ) { $page = 'subscription_list'; } if ( $this::LEGACY_SUBSCRIPTION_EDIT_PAGE_ID === $screen_id ) { $page = 'subscription_edit'; } return $page; } public function dashify_enqueue_utils() { wp_enqueue_script( 'dashify_util_script', plugins_url( '/admin/js/util.js', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/admin/js/util.js' ) ); } static function dashify_enqueue_screen_options_files( $dashify_on ) { wp_enqueue_script( 'dashify_screen_options_script', plugins_url( '/admin/js/dashify-screen-options-script.js', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/admin/js/dashify-screen-options-script.js' ) ); $nonce = wp_create_nonce( 'save_dashify_option_nonce' ); // Returning the false and true as strings is needed for them to appear // as booleans in JavaScript. $dashify_pro_enabled = is_plugin_active( 'dashify-pro/dashify-pro.php' ) ? 'true' : 'false'; $line_item_menu_order_column_enabled = get_option( 'dashify_line_item_menu_order_column_enabled', 'false' ); $line_item_menu_order_column_default_sort = get_option( 'dashify_line_item_menu_order_column_default_sort', 'none' ); wp_add_inline_script( 'dashify_screen_options_script', "const dashifyScreenOptions = { nonce: '$nonce', dashifyOn: $dashify_on, dashifyProEnabled: $dashify_pro_enabled, lineItemMenuOrderColumnEnabled: $line_item_menu_order_column_enabled, lineItemMenuOrderColumnDefaultSort: '$line_item_menu_order_column_default_sort', };", 'before' ); } /** * Enqueues files for 'dismissibles', currently only supporting a single * item in the form of a flyout. */ public static function enqueue_dismissible() { if ( get_option( 'dashify_dismissed_release_notices_forever' ) ) { return; } $dismissedForCurrentVersion = in_array( self::VERSION, get_option( 'dashify_dismissed_notices', array() ) ); if ( $dismissedForCurrentVersion ) { return; } wp_enqueue_script( 'dashify_dismissibles_script', plugins_url( '/admin/js/dashify-dismissibles-script.js', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/admin/js/dashify-dismissibles-script.js' ) ); // data to display in the flyout $dashifyDismissible = json_encode( array( 'nonce' => wp_create_nonce( 'mark_notice_dismissed_nonce' ), 'heading' => 'Dashify ' . self::VERSION, 'description' => 'Check out what’s new in this release!', 'content' => array( array( 'heading' => 'Bug fixes', 'content' => array( 'The styling of a button in TrackShip has been fixed just a little bit more!' ), ), ), ) ); wp_add_inline_script( 'dashify_dismissibles_script', "const dashifyDismissible = $dashifyDismissible;", 'before' ); wp_enqueue_style( 'dashify_dismissibles_styles', plugins_url( '/admin/css/dashify-dismissibles-styles.css', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/admin/css/dashify-dismissibles-styles.css' ) ); } public static function dashify_current_page( $screen_id ) { $page = 'unknown'; switch ( $screen_id ) { // If WooCommerce HPOS setting is enabled (https://woo.com/document/high-performance-order-storage/) case 'woocommerce_page_wc-orders': if ( isset( $_GET['action'] ) && ( $_GET['action'] === 'edit' || $_GET['action'] === 'new' ) ) { $page = 'order_summary'; } else { $page = 'order_list'; } break; case 'shop_order': // Order summary (without WooCommerce HPOS setting enabled) $page = 'order_summary'; break; case 'edit-shop_order': // Orders list (without WooCommerce HPOS setting enabled) $page = 'order_list'; break; } return apply_filters( 'dashify_current_page_filter', $page, $screen_id ); } private function dashify_enqueue_order_summary_files() { wp_enqueue_script( 'dashify_order_script', plugins_url( '/admin/js/dashify-order-script.js', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/admin/js/dashify-order-script.js' ) ); $admin_url = admin_url(); $previous_and_next_order_ids = $this->prev_and_next_ids(); $orders = json_encode( $previous_and_next_order_ids ); $order_edit_page = 'admin.php?page=wc-orders&action=edit&id='; // WooCommerce Subscriptions if ( $this->is_subscription_edit() ) { $order_edit_page = $this->is_HPOS_subscription_edit_page( get_current_screen()->id ) ? 'admin.php?page=wc-orders--shop_subscription&action=edit&id=' : 'post.php?action=edit&post='; } $previous_order_url = admin_url( $order_edit_page . ( $previous_and_next_order_ids['prev'] ?? '' ) ); $next_order_url = admin_url( $order_edit_page . ( $previous_and_next_order_ids['next'] ?? '' ) ); $timeZone = wp_timezone_string(); $is_subscription_edit = $this->is_subscription_edit(); wp_add_inline_script( 'dashify_order_script', " const dashify = { adminURL: '$admin_url', orders: $orders, previousOrderURL: '$previous_order_url', nextOrderURL: '$next_order_url', timeZone: '$timeZone', }; const dashifyIsSubscriptionEdit = $is_subscription_edit; ", 'before' ); wp_enqueue_style( 'dashify_order_styles', plugins_url( '/admin/css/dashify-order-styles.css', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/admin/css/dashify-order-styles.css' ) ); } private function dashify_enqueue_subscription_edit_files() { wp_enqueue_style( 'dashify_subscription_edit_styles', plugins_url( '/modules/subscriptions/edit.css', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/modules/subscriptions/edit.css' ) ); wp_enqueue_script( 'dashify_subscription_edit_script', plugins_url( '/modules/subscriptions/edit.js', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/modules/subscriptions/edit.js' ) ); } private function dashify_enqueue_subscription_table_files() { wp_enqueue_style( 'dashify_subscription_list_styles', plugins_url( '/modules/subscriptions/list.css', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/modules/subscriptions/list.css' ) ); wp_enqueue_script( 'dashify_subscription_list_script', plugins_url( '/modules/subscriptions/list.js', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/modules/subscriptions/list.js' ) ); } /** * Returns previous and next order ids * * @return array array( 'prev' => [previous order id or -1 if n.a.], 'next' => [next order id or -1 if n.a.] ) */ private function prev_and_next_ids() { // If we're on the "Add new order" page, we don't need to do this, as we // don't have any `id` or `post`. if ( ! isset( $_GET['id'] ) && ! isset( $_GET['post'] ) ) { return array(); } $current_id = intval( $this->isHPOS() ? $_GET['id'] : $_GET['post'] ); $statusFilter = isset( $_GET['status'] ) ? $_GET['status'] : ( isset( $_GET['post_status'] ) ? $_GET['post_status'] : false ); // The order status in the WC_Order object does not contain the wc- // prefix, so we remove it here. $statusFilter = str_replace( 'wc-', '', $statusFilter ); $prev = -1; $next = -1; for ( $ptr = $current_id - 1; $ptr > 0; $ptr-- ) { $order = wc_get_order( $ptr ); // WooCommerce Subscriptions if ( $this->is_subscription_edit() ) { $order = wcs_get_subscription( $ptr ); } if ( ! $order ) { continue; } if ( ! $this->is_subscription_edit() && $order instanceof WC_Subscription ) { continue; } $status = $order->get_base_data()['status']; if ( 'trash' === $status ) { continue; } if ( 'completed' === $status && array_key_exists( 'refunded_by', $order->get_base_data() ) ) { continue; } if ( $statusFilter && $statusFilter !== 'all' && $status !== $statusFilter ) { continue; } $prev = $ptr; break; } for ( $ptr = $current_id + 1; $ptr <= $this->get_single_order( 'DESC' ); $ptr++ ) { $order = wc_get_order( $ptr ); // WooCommerce Subscriptions if ( $this->is_subscription_edit() ) { $order = wcs_get_subscription( $ptr ); } if ( ! $order ) { continue; } if ( ! $this->is_subscription_edit() && $order instanceof WC_Subscription ) { continue; } $status = $order->get_base_data()['status']; if ( 'trash' === $status ) { continue; } if ( 'completed' === $status && array_key_exists( 'refunded_by', $order->get_base_data() ) ) { continue; } if ( $statusFilter && $statusFilter !== 'all' && $status !== $statusFilter ) { continue; } $next = $ptr; break; } return array( 'prev' => $prev, 'next' => $next, ); } /** * @param string $direction DESC or ASC * * @return -1 if there are no orders */ private function get_single_order( string $direction ): int { // WooCommerce Subscriptions if ( $this->is_subscription_edit() ) { $subscriptions = wcs_get_subscriptions( array( 'subscriptions_per_page' => 1, 'order' => $direction, ) ); if ( empty( $subscriptions ) ) { return -1; } return reset( $subscriptions )->get_id(); } $query = new WC_Order_Query( array( 'limit' => 1, 'orderby' => 'date', 'order' => $direction, 'return' => 'ids', ) ); $orders = $query->get_orders(); if ( empty( $orders ) ) { return -1; } return $orders[0]; } /** * Enqueues JS and CSS files for restyling WooCommerce tables. * So far, works for the order list, subscription list (partially), and the product list pages. */ private function dashify_enqueue_table_files() { wp_enqueue_script( 'dashify_table_script', plugins_url( '/admin/js/dashify-table-script.js', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/admin/js/dashify-table-script.js' ) ); $nonce = wp_create_nonce( 'dashify_order_list_analytics_nonce' ); try { $range = get_option( 'dashify_analytics_range', 7 ); $analytics = $this->is_subscription_list() ? $this->calculate_subscription_list_analytics( $range ) : $this->calculate_order_list_analytics( $range ); } catch ( Exception $exception ) { error_log( $exception ); } $sections = $this->is_subscription_list() ? $this->create_subscription_list_analytics_sections( $analytics ) : $this->create_order_list_analytics_sections( $analytics ); $sections = json_encode( $sections ); $is_subscription_list = $this->is_subscription_list(); // Returning the false and true as strings is needed for them to appear // as booleans in JavaScript. $open_search_and_filter_by_default = get_option( 'dashify_list_table_open_search_and_filter_by_default', 'no' ) === 'yes' ? 'true' : 'false'; wp_add_inline_script( 'dashify_table_script', "const dashifyAnalyticsAJAX = { nonce: '$nonce', }; let dashifyAnalyticsData = { analytics_range: {$analytics['analytics_range']}, sections: $sections, intervalData: {$analytics['intervalData']}, graphDivisions: {$analytics['graphDivisions']}, divisionMaxima: {$analytics['divisionMaxima']}, }; const isDashifySubscriptionList = $is_subscription_list; const openSearchAndFilterByDefault = $open_search_and_filter_by_default; ", 'before' ); wp_enqueue_style( 'dashify_table_styles', plugins_url( '/admin/css/dashify-table-styles.css', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/admin/css/dashify-table-styles.css' ) ); } private function dashify_enqueue_order_table_files() { wp_enqueue_script( 'dashify_order_table_script', plugins_url( '/admin/js/dashify-order-table-script.js', __FILE__ ), array(), filemtime( plugin_dir_path( __FILE__ ) . '/admin/js/dashify-order-table-script.js' ) ); $has_subscriptions = json_encode( is_plugin_active( 'woocommerce-subscriptions/woocommerce-subscriptions.php' ) ); wp_add_inline_script( 'dashify_order_table_script', "const dashifyHasWooCommerceSubscriptions = $has_subscriptions;", 'before' ); } private function calculate_order_list_analytics( $days ) { $analytics_range = intval( $days ); $midnight = new DateTimeImmutable( 'midnight', wp_timezone() ); $now = new DateTimeImmutable( 'now', wp_timezone() ); $start_time = $analytics_range === 0 ? $midnight->getTimestamp() : ( $now->getTimestamp() - $analytics_range * 86400 ); $end_time = $now->getTimestamp(); $orders = wc_get_orders( array( 'date_created' => '' . $start_time . '...' . $end_time, 'limit' => -1, ) ); $num_orders = 0; $num_refunded = 0; $num_completed = 0; $graph_divisions = $analytics_range; if ( 1 === $analytics_range ) { $graph_divisions = 24; } if ( 0 === $analytics_range ) { $interval = $midnight->diff( $now ); $hours_since_midnight = $interval->h + ( $interval->i / 60 ) + ( $interval->s / 3600 ); $graph_divisions = $hours_since_midnight; } $s_per_interval = ( 0 === $analytics_range || 1 === $analytics_range ) ? 3600 : 86400; $interval_data = array(); $division_maxima = array( 'num_orders' => 0, 'num_refunded' => 0, 'num_completed' => 0, ); foreach ( $orders as $order ) { $order_data = $order->get_base_data(); if ( 'deleted' === $order_data['status'] ) { continue; } if ( 'completed' === $order_data['status'] && array_key_exists( 'refunded_by', $order_data ) ) { continue; } $date_created = $order_data['date_created']->getTimestamp(); $interval_number = intdiv( ( $date_created - $start_time ), $s_per_interval ); if ( ! array_key_exists( $interval_number, $interval_data ) ) { $interval_data[ $interval_number ] = array( 'num_orders' => 0, 'num_refunded' => 0, 'num_completed' => 0, ); } ++$num_orders; $interval_data[ $interval_number ]['num_orders'] += 1; $division_maxima['num_orders'] = max( $division_maxima['num_orders'], $interval_data[ $interval_number ]['num_orders'] ); if ( 'refunded' === $order_data['status'] ) { ++$num_refunded; $interval_data[ $interval_number ]['num_refunded'] += 1; $division_maxima['num_refunded'] = max( $division_maxima['num_refunded'], $interval_data[ $interval_number ]['num_refunded'] ); } elseif ( 'completed' === $order_data['status'] ) { ++$num_completed; $interval_data[ $interval_number ]['num_completed'] += 1; $division_maxima['num_completed'] = max( $division_maxima['num_completed'], $interval_data[ $interval_number ]['num_completed'] ); } } $interval_data = json_encode( $interval_data ); $division_maxima = json_encode( $division_maxima ); return array( 'analytics_range' => $analytics_range, 'num_orders' => $num_orders, 'num_refunded' => $num_refunded, 'num_completed' => $num_completed, 'intervalData' => $interval_data, 'graphDivisions' => $graph_divisions, 'divisionMaxima' => $division_maxima, ); } private function calculate_subscription_list_analytics( $days ) { $range = intval( $days ); $subscription_revenue = 0; $num_new_subscriptions = 0; $num_cancelled_subscriptions = 0; $midnight = new DateTimeImmutable( 'midnight', wp_timezone() ); $now = new DateTimeImmutable( 'now', wp_timezone() ); $graph_divisions = ( 1 === $range ) ? 24 : $range; if ( 0 === $range ) { $interval = $midnight->diff( $now ); $hours_since_midnight = $interval->h + ( $interval->i / 60 ) + ( $interval->s / 3600 ); $graph_divisions = $hours_since_midnight; } $seconds_per_interval = ( 0 === $range || 1 === $range ) ? 3600 : 86400; $interval_data = array(); $division_maxima = array( 'subscription_revenue' => 0, 'num_new_subscriptions' => 0, 'num_cancelled_subscriptions' => 0, ); $start_time = $range === 0 ? $midnight->getTimestamp() : ( $now->getTimestamp() - $range * 86400 ); $end_time = $now->getTimestamp(); // wcs_get_subscriptions() does not support passing in a date range // (https://github.com/riclain/woocommerce-subscriptions/blob/master/wcs-functions.php#L332) // so we have to get the posts directly so that we can pass in date_query. $subscription_post_ids = get_posts( array( 'numberposts' => -1, // Get all that match. 'post_type' => 'shop_subscription', 'post_status' => array( 'wc-active', 'wc-cancelled' ), 'orderby' => 'post_date', 'order' => 'ASC', 'date_query' => array( array( 'after' => date( 'c', $start_time ), 'before' => date( 'c', $end_time ), 'inclusive' => true, ), ), 'fields' => 'ids', // Return just IDs—we will create WC_Subscription object from these. ) ); $subscriptions = array(); foreach ( $subscription_post_ids as $post_id ) { $subscriptions[ $post_id ] = wcs_get_subscription( $post_id ); } foreach ( $subscriptions as $subscription ) { if ( 'deleted' === $subscription->get_status() ) { continue; } $subscription_data = $subscription->get_base_data(); $date_created = $subscription_data['date_created']->getTimestamp(); $interval_number = intdiv( ( $date_created - $start_time ), $seconds_per_interval ); if ( ! array_key_exists( $interval_number, $interval_data ) ) { $interval_data[ $interval_number ] = array( 'subscription_revenue' => 0, 'num_new_subscriptions' => 0, 'num_cancelled_subscriptions' => 0, ); } $related_orders = $subscription->get_related_orders( 'all' ); foreach ( $related_orders as $order ) { $amount = $order->get_total( 'edit' ); $subscription_revenue += $amount; $interval_data[ $interval_number ]['subscription_revenue'] += $amount; $division_maxima['subscription_revenue'] = max( $division_maxima['subscription_revenue'], $interval_data[ $interval_number ]['subscription_revenue'] ); } if ( 'cancelled' === $subscription->get_status() ) { ++$num_cancelled_subscriptions; $interval_data[ $interval_number ]['num_cancelled_subscriptions'] += 1; $division_maxima['num_cancelled_subscriptions'] = max( $division_maxima['num_cancelled_subscriptions'], $interval_data[ $interval_number ]['num_cancelled_subscriptions'] ); } else { ++$num_new_subscriptions; $interval_data[ $interval_number ]['num_new_subscriptions'] += 1; $division_maxima['num_new_subscriptions'] = max( $division_maxima['num_new_subscriptions'], $interval_data[ $interval_number ]['num_new_subscriptions'] ); } } $interval_data = json_encode( $interval_data ); $division_maxima = json_encode( $division_maxima ); return array( 'analytics_range' => $range, 'subscription_revenue' => $subscription_revenue, 'num_new_subscriptions' => $num_new_subscriptions, 'num_cancelled_subscriptions' => $num_cancelled_subscriptions, 'intervalData' => $interval_data, 'graphDivisions' => $graph_divisions, 'divisionMaxima' => $division_maxima, ); } private function create_order_list_analytics_sections( $analytics ) { return array( array( 'label' => 'Total orders', 'value' => $analytics['num_orders'], 'key' => 'num_orders', ), array( 'label' => 'Refunded', 'value' => $analytics['num_refunded'], 'key' => 'num_refunded', ), array( 'label' => 'Completed', 'value' => $analytics['num_completed'], 'key' => 'num_completed', ), ); } private function create_subscription_list_analytics_sections( $analytics ) { $revenue = numfmt_format_currency( numfmt_create( get_locale(), NumberFormatter::CURRENCY ), $analytics['subscription_revenue'], get_option( 'woocommerce_currency' ) ); return array( array( 'label' => 'Subscription revenue', 'value' => $revenue, 'key' => 'subscription_revenue', ), array( 'label' => 'New subscriptions', 'value' => $analytics['num_new_subscriptions'], 'key' => 'num_new_subscriptions', ), array( 'label' => 'Cancelled subscriptions', 'value' => $analytics['num_cancelled_subscriptions'], 'key' => 'num_cancelled_subscriptions', ), ); } private function is_subscription_list() { $current_page = $this->dashify_current_page( get_current_screen()->id ); // Ternary is needed because PHP will otherwise give empty string for false. return 'subscription_list' === $current_page ? 1 : 0; } private function is_subscription_edit() { $current_page = $this->dashify_current_page( get_current_screen()->id ); return 'subscription_edit' === $current_page ? 1 : 0; } /** * Set the positions of the meta boxes in the order edit view to those * preferred by Dashify upon first activation. * * This will also run a single time after anyone updates an older version * of Dashify to the version specified below. * * @since 1.2.8 */ public function migrate_order_edit_meta_box_layout_to_user_meta() { if ( get_option( 'dashify_migrated_order_edit_meta_box_layout_to_user_meta' ) ) { return; } // People might switch between the two, at least it seems like it based // on our blog post for turning off HPOS being so popular, so we’ll clear // the user_option for both HPOS and non-HPOS. delete_user_option( get_current_user_id(), 'meta-box-order_woocommerce_page_wc-orders' ); delete_user_option( get_current_user_id(), 'meta-box-order_shop_order' ); $this->move_order_edit_meta_boxes(); update_option( 'dashify_migrated_order_edit_meta_box_layout_to_user_meta', 1 ); } /** * @since 1.2.8 */ public function migrate_subscription_edit_meta_box_layout_to_user_meta() { if ( ! is_plugin_active( 'woocommerce-subscriptions/woocommerce-subscriptions.php' ) ) { return; } if ( get_option( 'dashify_migrated_subscription_edit_meta_box_layout_to_user_meta' ) ) { return; } $this->move_subscription_edit_meta_boxes(); update_option( 'dashify_migrated_subscription_edit_meta_box_layout_to_user_meta', 1 ); } public function list_table_settings( $fields ) { $fields[] = array( 'name' => 'List table search', 'type' => 'checkbox', 'default' => 'no', 'desc' => 'Expand the search and filter by default', 'desc_tip' => 'When checked, the search and filter on list tables (orders, products, etc.) will be open by default. This is helpful if you frequently use the search or filters.', 'id' => 'dashify_list_table_open_search_and_filter_by_default', ); return $fields; } } $dashify = new Dashify_Base();