Files
wp-multisite-waas/inc/admin-pages/class-dashboard-admin-page.php
David Stone a815fdf179 Prep Plugin for release on WordPress.org
Escape everything that should be escaped.
Add nonce checks where needed.
Sanitize all inputs.
Apply Code style changes across the codebase.
Correct many deprecation notices.
Optimize load order of many filters.
2025-04-07 09:15:21 -06:00

628 lines
16 KiB
PHP

<?php
/**
* WP Multisite WaaS Dashboard Admin Page.
*
* @package WP_Ultimo
* @subpackage Admin_Pages
* @since 2.0.0
*/
namespace WP_Ultimo\Admin_Pages;
// Exit if accessed directly
defined('ABSPATH') || exit;
use WP_Ultimo\Dashboard_Statistics;
/**
* WP Multisite WaaS Dashboard Admin Page.
*/
class Dashboard_Admin_Page extends Base_Admin_Page {
/**
* Holds the ID for this page, this is also used as the page slug.
*
* @var string
*/
protected $id = 'wp-ultimo';
/**
* Menu position. This is only used for top-level menus
*
* @since 1.8.2
* @var integer
*/
protected $position = 10_101_010;
/**
* Dashicon to be used on the menu item. This is only used on top-level menus
*
* @since 1.8.2
* @var string
*/
protected $menu_icon = 'dashicons-wu-wp-ultimo';
/**
* If this number is greater than 0, a badge with the number will be displayed alongside the menu title
*
* @since 1.8.2
* @var integer
*/
protected $badge_count = 0;
/**
* Holds the admin panels where this page should be displayed, as well as which capability to require.
*
* To add a page to the regular admin (wp-admin/), use: 'admin_menu' => 'capability_here'
* To add a page to the network admin (wp-admin/network), use: 'network_admin_menu' => 'capability_here'
* To add a page to the user (wp-admin/user) admin, use: 'user_admin_menu' => 'capability_here'
*
* @since 2.0.0
* @var array
*/
protected $supported_panels = [
'network_admin_menu' => 'wu_read_dashboard',
];
/**
* The tab being displayed.
*
* @since 2.2.0
* @var string
*/
public $tab;
/**
* The start date for the statistics.
*
* @since 2.2.0
* @var string
*/
public $start_date;
/**
* The end date for the statistics.
*
* @since 2.2.0
* @var string
*/
public $end_date;
/**
* Sets up the global parameters.
*
* @since 2.0.0
* @return void
*/
public function init(): void {
parent::init();
/*
* Get the content of the tab.
*/
$this->tab = wu_request('tab', 'general');
$this->start_date = date_i18n('Y-m-d', strtotime((string) wu_request('start_date', '-1 month')));
$this->end_date = date_i18n('Y-m-d', strtotime((string) wu_request('end_date', 'tomorrow')));
}
/**
* Allow child classes to add hooks to be run once the page is loaded.
*
* @see https://codex.wordpress.org/Plugin_API/Action_Reference/load-(page)
* @since 1.8.2
* @return void
*/
public function hooks(): void {
add_action('wu_dash_after_full_metaboxes', [$this, 'render_filter']);
add_action('wu_dashboard_general_widgets', [$this, 'register_general_tab_widgets'], 10, 2);
}
/**
* Renders the filter.
*
* @since 2.0.0
*
* @param WP_Ultimo\Admin_Pages\Base_Admin_Page $page The page object.
* @return void
*/
public function render_filter($page): void {
if (apply_filters('wu_dashboard_display_filter', true) === false) {
return;
}
if ('wp-ultimo' === $page->id) {
$preset_options = [
'last_7_days' => [
'label' => __('Last 7 days', 'wp-multisite-waas'),
'start_date' => date_i18n('Y-m-d', strtotime('-7 days')),
'end_date' => date_i18n('Y-m-d'),
],
'last_30_days' => [
'label' => __('Last 30 days', 'wp-multisite-waas'),
'start_date' => date_i18n('Y-m-d', strtotime('-30 days')),
'end_date' => date_i18n('Y-m-d'),
],
'year_to_date' => [
'label' => __('Year to date', 'wp-multisite-waas'),
'start_date' => date_i18n('Y-m-d', strtotime('first day of january this year')),
'end_date' => date_i18n('Y-m-d'),
],
];
$args = [
'preset_options' => $preset_options,
'filters_el_id' => 'dashboard-filters',
'search_label' => '',
'has_search' => false,
'has_view_switch' => false,
'table' => $this,
'active_tab' => $this->tab,
'views' => $this->get_views(),
];
wu_get_template('dashboard-statistics/filter', $args);
}
}
/**
* Returns the views for the filter menu bar.
*
* @since 2.0.0
* @return array
*/
public function get_views() {
$dashboard_filters = [
'general' => [
'field' => 'type',
'url' => add_query_arg('tab', 'general'),
'label' => __('General', 'wp-multisite-waas'),
'count' => 0,
],
];
return apply_filters('wu_dashboard_filter_bar', $dashboard_filters);
}
/**
* Allow child classes to register widgets, if they need them.
*
* @since 1.8.2
* @return void
*/
public function register_widgets(): void {
$screen = get_current_screen();
if ( ! $screen) {
return;
}
/**
* Allow plugin developers to add widgets to Network Dashboard Panel.
*
* @since 2.0.0
*
* @param string $tab The current tab.
* @param \WP_Screen $screen The screen object.
* @param \WP_Ultimo\Admin_Pages\Dashboard_Admin_Page $page WP Multisite WaaS admin page instance.
*/
do_action("wu_dashboard_{$this->tab}_widgets", $this->tab, $screen, $this);
/**
* Allow plugin developers to add widgets to Network Dashboard Panel.
*
* @since 2.0.0
*
* @param string $tab The current tab.
* @param \WP_Screen $screen The screen object.
* @param \WP_Ultimo\Admin_Pages\Dashboard_Admin_Page $page WP Multisite WaaS admin page instance.
*/
do_action('wu_dashboard_widgets', $this->tab, $screen, $this);
if (wu_request('tab', 'general') === 'general') {
\WP_Ultimo\UI\Tours::get_instance()->create_tour(
'wp-ultimo-dashboard',
[
[
'id' => 'your-dashboard',
'title' => __('Our dashboard', 'wp-multisite-waas'),
'text' => [
__('This is the <strong>WP Multisite WaaS Dashboard</strong>, where you will find most of the important information you will need regarding your business\' performance.', 'wp-multisite-waas'),
],
],
[
'id' => 'documentation',
'title' => __('Learning more', 'wp-multisite-waas'),
'text' => [
__('Most of the WP Multisite WaaS admin pages will contain a link like this one at the top. These will link directly to the relevant knowledge base page on the WP Multisite WaaS site.', 'wp-multisite-waas'),
],
'attachTo' => [
'element' => '#wp-ultimo-wrap > h1 > a:last-child',
'on' => 'left',
],
],
[
'id' => 'mrr-growth',
'title' => __('It\'s all about growth!', 'wp-multisite-waas'),
'text' => [
__('This graph allows you to follow how your monthly recurring revenue is growing this year.', 'wp-multisite-waas'),
],
'attachTo' => [
'element' => '#wp-ultimo-mrr-growth',
'on' => 'bottom',
],
],
[
'id' => 'tailor-made',
'title' => __('Date-range support', 'wp-multisite-waas'),
'text' => [
__('Checking statistics and comparing data for different periods is key in maintaining a good grasp on your business.', 'wp-multisite-waas'),
__('You can use the date-range selectors to have access to just the data you need and nothing more.', 'wp-multisite-waas'),
],
'attachTo' => [
'element' => '#dashboard-filters',
'on' => 'bottom',
],
],
]
);
}
}
/**
* Register the widgets of the default general tab.
*
* @since 2.0.0
*
* @param string $tab Tab slug.
* @param \WP_Screen $screen The screen object.
* @return void
*/
public function register_general_tab_widgets($tab, $screen): void {
if (current_user_can('wu_read_financial')) {
add_meta_box('wp-ultimo-mrr-growth', __('Monthly Recurring Revenue Growth', 'wp-multisite-waas'), [$this, 'output_widget_mrr_growth'], $screen->id, 'full', 'high');
add_meta_box('wp-ultimo-revenue', __('Revenue', 'wp-multisite-waas'), [$this, 'output_widget_revenues'], $screen->id, 'normal', 'high');
}
add_meta_box('wp-ultimo-countries', __('Signups by Countries', 'wp-multisite-waas'), [$this, 'output_widget_countries'], $screen->id, 'side', 'high');
add_meta_box('wp-ultimo-signups', __('Signups by Form', 'wp-multisite-waas'), [$this, 'output_widget_forms'], $screen->id, 'side', 'high');
add_meta_box('wp-ultimo-most-visited-sites', __('Most Visited Sites', 'wp-multisite-waas'), [$this, 'output_widget_most_visited_sites'], $screen->id, 'side', 'low');
add_meta_box('wp-ultimo-new-accounts', __('New Memberships', 'wp-multisite-waas'), [$this, 'output_widget_new_accounts'], $screen->id, 'normal', 'low');
}
/**
* Output the statistics filter widget
*
* @return void
* @since 2.0.0
*/
public function output_widget_mrr_growth(): void {
wu_get_template('dashboard-statistics/widget-mrr-growth');
}
/**
* Output the statistics filter widget
*
* @return void
* @since 2.0.0
*/
public function output_widget_countries(): void {
wu_get_template(
'dashboard-statistics/widget-countries',
[
'countries' => wu_get_countries_of_customers(10, $this->start_date, $this->end_date),
'page' => $this,
]
);
}
/**
* Output the statistics filter widget
*
* @return void
* @since 2.0.0
*/
public function output_widget_forms(): void {
wu_get_template(
'dashboard-statistics/widget-forms',
[
'forms' => wu_calculate_signups_by_form($this->start_date, $this->end_date),
'page' => $this,
]
);
}
/**
* Output the statistics filter widget
*
* @return void
* @since 2.0.0
*/
public function output_widget_most_visited_sites(): void {
$sites = [];
$site_results = \WP_Ultimo\Objects\Visits::get_sites_by_visit_count($this->start_date, $this->end_date, 10);
foreach ($site_results as $site_result) {
$site = wu_get_site($site_result->site_id);
if ( ! $site) {
continue;
}
$sites[] = (object) [
'site' => $site,
'count' => $site_result->count,
];
}
wu_get_template(
'dashboard-statistics/widget-most-visited-sites',
[
'sites' => $sites,
'page' => $this,
]
);
}
/**
* Outputs the total refunds widget content.
*
* @since 2.0.0
*
* @param string $unknown Unknown.
* @param array $metabox With the metabox arguments passed when registered.
* @return void.
*/
public function output_widget_revenues($unknown = null, $metabox = null): void {
wu_get_template(
'dashboard-statistics/widget-revenue',
[
'mrr' => wu_calculate_mrr(),
'gross_revenue' => wu_calculate_revenue($this->start_date, $this->end_date),
'refunds' => wu_calculate_refunds($this->start_date, $this->end_date),
'product_stats' => wu_calculate_financial_data_by_product($this->start_date, $this->end_date),
]
);
}
/**
* Outputs the total refunds widget content.
*
* @since 2.0.0
*
* @param string $unknown Unknown.
* @param array $metabox With the metabox arguments passed when registered.
* @return void.
*/
public function output_widget_new_accounts($unknown = null, $metabox = []): void {
$new_accounts = wu_get_memberships(
[
'fields' => ['plan_id'],
'date_query' => [
'column' => 'date_created',
'after' => $this->start_date . ' 00:00:00',
'before' => $this->end_date . ' 23:59:59',
'inclusive' => true,
],
]
);
$products = wu_get_products(
[
'type' => 'plan',
'fields' => ['id', 'name', 'count'],
]
);
$products_ids = array_column($products, 'id');
$products = array_combine($products_ids, $products);
$products = array_map(
function ($item) {
$item->count = 0;
return $item;
},
$products
);
/**
* Add edge case for no plan.
*/
$products['none'] = (object) [
'name' => __('No Product', 'wp-multisite-waas'),
'count' => 0,
];
foreach ($new_accounts as $new_account) {
if (isset($products[ $new_account->plan_id ])) {
$products[ $new_account->plan_id ]->count += 1;
} else {
$products['none']->count += 1;
}
}
wu_get_template(
'dashboard-statistics/widget-new-accounts',
[
'new_accounts' => count($new_accounts),
'products' => $products,
]
);
}
/**
* Enqueue the necessary scripts.
*
* @since 2.0.0
* @return void
*/
public function register_scripts(): void {
$month_list = [];
$current_year = date_i18n('Y');
for ($i = 1; $i <= 12; $i++) {
$month_list[] = date_i18n('M y', mktime(0, 0, 0, $i, 1, $current_year));
}
$statistics = new Dashboard_Statistics(
[
'start_date' => $this->start_date,
'end_date' => $this->end_date,
'types' => [
'mrr_growth' => 'mrr_growth',
],
]
);
$data = $statistics->statistics_data();
wp_register_script('wu-apex-charts', wu_get_asset('apexcharts.js', 'js/lib'), [], wu_get_version(), true);
wp_register_script('wu-vue-apex-charts', wu_get_asset('vue-apexcharts.js', 'js/lib'), [], wu_get_version(), true);
wp_register_script('wu-dashboard-stats', wu_get_asset('dashboard-statistics.js', 'js'), ['jquery', 'wu-functions', 'wu-ajax-list-table', 'moment', 'wu-block-ui', 'dashboard', 'wu-apex-charts', 'wu-vue-apex-charts'], wu_get_version(), true);
wp_localize_script(
'wu-dashboard-stats',
'wu_dashboard_statistics_vars',
[
'mrr_array' => $data['mrr_growth'],
'start_date' => date_i18n('Y-m-d', strtotime((string) wu_request('start_date', '-1 month'))),
'end_date' => date_i18n('Y-m-d', strtotime((string) wu_request('end_date', 'tomorrow'))),
'today' => date_i18n('Y-m-d', strtotime('tomorrow')),
'month_list' => $month_list,
'i18n' => [
'new_mrr' => __('New MRR', 'wp-multisite-waas'),
'cancellations' => __('Cancellations', 'wp-multisite-waas'),
],
]
);
wp_enqueue_script('wu-dashboard-stats');
wp_enqueue_style('wu-apex-charts', wu_get_asset('apexcharts.css', 'css'), [], wu_get_version());
wp_enqueue_style('wu-flags');
wp_enqueue_script_module('wu-flags-polyfill');
}
/**
* Returns the title of the page.
*
* @since 2.0.0
* @return string Title of the page.
*/
public function get_title() {
return __('Dashboard', 'wp-multisite-waas');
}
/**
* Returns the title of menu for this page.
*
* @since 2.0.0
* @return string Menu label of the page.
*/
public function get_menu_title() {
return __('Multisite WaaS', 'wp-multisite-waas');
}
/**
* Allows admins to rename the sub-menu (first item) for a top-level page.
*
* @since 2.0.0
* @return string False to use the title menu or string with sub-menu title.
*/
public function get_submenu_title() {
return __('Dashboard', 'wp-multisite-waas');
}
/**
* Every child class should implement the output method to display the contents of the page.
*
* @since 1.8.2
* @return void
*/
public function output(): void {
/*
* Renders the base edit page layout, with the columns and everything else =)
*/
wu_get_template(
'base/dash',
[
'screen' => get_current_screen(),
'page' => $this,
'has_full_position' => true,
]
);
}
/**
* Render an export CSV button.
*
* @since 2.0.0
*
* @param array $args Data array to convert to CSV.
* @return void
*/
public function render_csv_button($args): void {
$args = wp_parse_args(
$args,
[
'slug' => 'csv',
'headers' => [],
'data' => [],
'action' => apply_filters('wu_export_data_table_action', 'wu_generate_csv'),
]
);
$slug = $args['slug'];
$header_strings = wp_json_encode($args['headers']);
$data_strings = wp_json_encode($args['data']);
$html = "<div class='wu-bg-gray-100 wu-p-2 wu-text-right wu-border-0 wu-border-b wu-border-solid wu-border-gray-400'>
<a href='#' attr-slug-csv='{$slug}' class='wu-export-button wu-no-underline wu-text-gray-800 wu-text-xs'>
<span class='dashicons-wu-download wu-mr-1'></span> %s
</a>
<input type='hidden' id='csv_headers_{$slug}' value='{$header_strings}' />
<input type='hidden' id='csv_data_{$slug}' value='{$data_strings}' />
<input type='hidden' id='csv_action_{$slug}' value='{$args['action']}' />
</div>";
$html = apply_filters('wu_export_html_render', $html, $html);
printf($html, apply_filters('wu_export_data_table_label', __('CSV', 'wp-multisite-waas')));
}
}