AbstractReport.php
3 years ago
Orders.php
3 years ago
PaymentMethods.php
3 years ago
Refunds.php
4 years ago
ReportFilterData.php
4 years ago
ReportInterval.php
4 years ago
Revenue.php
4 years ago
SettingsPage.php
1 year ago
Taxes.php
4 years ago
TopPlans.php
4 years ago
index.php
4 years ago
SettingsPage.php
450 lines
| 1 | <?php |
| 2 | |
| 3 | namespace ProfilePress\Core\Admin\SettingsPages\Membership\DashboardPage; |
| 4 | |
| 5 | use ProfilePress\Core\Admin\SettingsPages\AbstractSettingsPage; |
| 6 | use ProfilePress\Core\Base; |
| 7 | use ProfilePress\Core\Membership\Models\Customer\CustomerStatus; |
| 8 | use ProfilePress\Core\Membership\Models\Order\OrderStatus; |
| 9 | use ProfilePress\Core\Membership\Models\Subscription\SubscriptionStatus; |
| 10 | use ProfilePress\Core\Membership\Repositories\CustomerRepository; |
| 11 | use ProfilePress\Core\Membership\Repositories\PlanRepository; |
| 12 | use ProfilePress\Core\Membership\Repositories\SubscriptionRepository; |
| 13 | use ProfilePress\Core\Membership\Services\Calculator; |
| 14 | use ProfilePress\Core\Membership\Services\TaxService; |
| 15 | use ProfilePress\Custom_Settings_Page_Api; |
| 16 | use ProfilePressVendor\Carbon\CarbonImmutable; |
| 17 | |
| 18 | class SettingsPage extends AbstractSettingsPage |
| 19 | { |
| 20 | function __construct() |
| 21 | { |
| 22 | add_action('ppress_register_menu_page', [$this, 'register_cpf_settings_page']); |
| 23 | add_action('ppress_admin_settings_page_reports', [$this, 'reports_admin_page']); |
| 24 | } |
| 25 | |
| 26 | public function admin_page_title() |
| 27 | { |
| 28 | return apply_filters( |
| 29 | 'ppress_membership_dashboard_settings_page_title', |
| 30 | esc_html__('Dashboard', 'wp-user-avatar') |
| 31 | ); |
| 32 | } |
| 33 | |
| 34 | public function register_cpf_settings_page() |
| 35 | { |
| 36 | $hook = add_submenu_page( |
| 37 | PPRESS_DASHBOARD_SETTINGS_SLUG, |
| 38 | $this->admin_page_title() . ' - ProfilePress', |
| 39 | esc_html__('Dashboard', 'wp-user-avatar'), |
| 40 | 'manage_options', |
| 41 | PPRESS_DASHBOARD_SETTINGS_SLUG, |
| 42 | array($this, 'admin_page_callback')); |
| 43 | |
| 44 | do_action('ppress_membership_reports_settings_page_register', $hook); |
| 45 | } |
| 46 | |
| 47 | public function header_menu_tabs() |
| 48 | { |
| 49 | $tabs = apply_filters('ppress_membership_dashboard_settings_page_tabs', [ |
| 50 | 5 => ['id' => $this->default_header_menu(), 'url' => PPRESS_DASHBOARD_SETTINGS_PAGE, 'label' => esc_html__('Reports', 'wp-user-avatar')], |
| 51 | 7 => ['id' => 'export', 'url' => PPRESS_MEMBERSHIP_EXPORT_SETTINGS_PAGE, 'label' => esc_html__('Export', 'wp-user-avatar')], |
| 52 | 10 => ['id' => 'download-logs', 'url' => PPRESS_MEMBERSHIP_DOWNLOAD_LOGS_SETTINGS_PAGE, 'label' => esc_html__('Download Logs', 'wp-user-avatar')] |
| 53 | ]); |
| 54 | |
| 55 | ksort($tabs); |
| 56 | |
| 57 | return $tabs; |
| 58 | } |
| 59 | |
| 60 | public function default_header_menu() |
| 61 | { |
| 62 | return 'reports'; |
| 63 | } |
| 64 | |
| 65 | public function reports_admin_page() |
| 66 | { |
| 67 | add_action('admin_footer', [$this, 'js_script']); |
| 68 | add_filter('wp_cspa_main_content_area', [$this, 'admin_settings_page_callback'], 10, 2); |
| 69 | |
| 70 | $instance = Custom_Settings_Page_Api::instance(); |
| 71 | $instance->option_name('ppview'); // adds ppview css class to #poststuff |
| 72 | $instance->form_method('get'); |
| 73 | $instance->page_header($this->admin_page_title()); |
| 74 | $instance->build(true); |
| 75 | } |
| 76 | |
| 77 | public function get_filter_data() |
| 78 | { |
| 79 | $preset = ! empty($_GET['preset']) ? sanitize_text_field($_GET['preset']) : 'today'; |
| 80 | $plan_id = ! empty($_GET['plan']) ? absint($_GET['plan']) : ''; |
| 81 | |
| 82 | $start_of_week = absint(get_option('start_of_week', 1)); |
| 83 | $end_of_week = $start_of_week == 0 ? 6 : $start_of_week - 1; |
| 84 | |
| 85 | $carbonNow = CarbonImmutable::now(wp_timezone()); |
| 86 | |
| 87 | switch ($preset) { |
| 88 | case 'today': |
| 89 | $start_date = $carbonNow->startOfDay()->toDateString(); |
| 90 | $end_date = $carbonNow->endOfDay()->toDateString(); |
| 91 | break; |
| 92 | case 'yesterday': |
| 93 | $start_date = $carbonNow->subDay()->startOfDay()->toDateString(); |
| 94 | $end_date = $carbonNow->subDay()->endOfDay()->toDateString(); |
| 95 | break; |
| 96 | case 'this_week': |
| 97 | $start_date = $carbonNow->startOfWeek($start_of_week)->toDateString(); |
| 98 | $end_date = $carbonNow->endOfWeek($end_of_week)->toDateString(); |
| 99 | break; |
| 100 | case 'last_week': |
| 101 | $start_date = $carbonNow->startOfWeek($start_of_week)->subWeek()->toDateString(); |
| 102 | $end_date = $carbonNow->endOfWeek($end_of_week)->subWeek()->toDateString(); |
| 103 | break; |
| 104 | case 'last_30_days': |
| 105 | $start_date = $carbonNow->subDays(29)->startOfDay()->toDateString(); |
| 106 | $end_date = $carbonNow->endOfDay()->toDateString(); |
| 107 | break; |
| 108 | case 'this_month': |
| 109 | $start_date = $carbonNow->startOfMonth()->toDateString(); |
| 110 | $end_date = $carbonNow->endOfMonth()->toDateString(); |
| 111 | break; |
| 112 | case 'last_month': |
| 113 | $start_date = $carbonNow->subMonthNoOverflow()->startOfMonth()->toDateString(); |
| 114 | $end_date = $carbonNow->subMonthNoOverflow()->endOfMonth()->toDateString(); |
| 115 | break; |
| 116 | case 'this_quarter': |
| 117 | $start_date = $carbonNow->startOfQuarter()->toDateString(); |
| 118 | $end_date = $carbonNow->endOfQuarter()->toDateString(); |
| 119 | break; |
| 120 | case 'last_quarter': |
| 121 | $start_date = $carbonNow->subQuarter()->startOfQuarter()->toDateString(); |
| 122 | $end_date = $carbonNow->subQuarter()->endOfQuarter()->toDateString(); |
| 123 | break; |
| 124 | case 'this_year': |
| 125 | $start_date = $carbonNow->startOfYear()->toDateString(); |
| 126 | $end_date = $carbonNow->endOfYear()->toDateString(); |
| 127 | break; |
| 128 | case 'last_year': |
| 129 | $start_date = $carbonNow->subYear()->startOfYear()->toDateString(); |
| 130 | $end_date = $carbonNow->subYear()->endOfYear()->toDateString(); |
| 131 | break; |
| 132 | default: |
| 133 | $start_date = sanitize_text_field(ppressGET_var('start_date', '', true)); |
| 134 | $end_date = sanitize_text_field(ppressGET_var('end_date', '', true)); |
| 135 | break; |
| 136 | } |
| 137 | |
| 138 | $filterData = new ReportFilterData(); |
| 139 | $filterData->start_date = $start_date; |
| 140 | $filterData->end_date = $end_date; |
| 141 | $filterData->plan_id = $plan_id; |
| 142 | |
| 143 | return $filterData; |
| 144 | } |
| 145 | |
| 146 | public function js_script() |
| 147 | { |
| 148 | ?> |
| 149 | <script type="text/javascript"> |
| 150 | var currencySymbol = '<?= ppress_display_amount('00')?>', // 00 is placeholder replaced on the clientside |
| 151 | revenueStat = <?= wp_json_encode($this->get_revenue()) ?>, |
| 152 | taxesStat = <?= wp_json_encode($this->get_taxes()) ?>, |
| 153 | orderStat = <?= wp_json_encode($this->get_orders()) ?>, |
| 154 | topPlansStat = <?= wp_json_encode($this->get_top_plans()) ?>, |
| 155 | paymentMethodsStat = <?= wp_json_encode($this->get_payment_methods()) ?>, |
| 156 | refundStat = <?= wp_json_encode($this->get_refunds()) ?>; |
| 157 | </script> |
| 158 | <?php |
| 159 | } |
| 160 | |
| 161 | public function cache_transformer($key, $callback) |
| 162 | { |
| 163 | static $cache = []; |
| 164 | |
| 165 | if ( ! isset($cache[$key])) { |
| 166 | $cache[$key] = $callback; |
| 167 | } |
| 168 | |
| 169 | return $cache[$key]; |
| 170 | } |
| 171 | |
| 172 | public function get_revenue() |
| 173 | { |
| 174 | return $this->cache_transformer( |
| 175 | __FUNCTION__, |
| 176 | (new Revenue($this->get_filter_data()))->val() |
| 177 | ); |
| 178 | } |
| 179 | |
| 180 | public function get_taxes() |
| 181 | { |
| 182 | return $this->cache_transformer( |
| 183 | __FUNCTION__, |
| 184 | (new Taxes($this->get_filter_data()))->val() |
| 185 | ); |
| 186 | } |
| 187 | |
| 188 | public function get_orders() |
| 189 | { |
| 190 | return $this->cache_transformer( |
| 191 | __FUNCTION__, |
| 192 | (new Orders($this->get_filter_data()))->val() |
| 193 | ); |
| 194 | } |
| 195 | |
| 196 | public function get_refunds() |
| 197 | { |
| 198 | return $this->cache_transformer( |
| 199 | __FUNCTION__, |
| 200 | (new Refunds($this->get_filter_data()))->val() |
| 201 | ); |
| 202 | } |
| 203 | |
| 204 | public function get_top_plans() |
| 205 | { |
| 206 | return $this->cache_transformer( |
| 207 | __FUNCTION__, |
| 208 | (new TopPlans($this->get_filter_data()))->val() |
| 209 | ); |
| 210 | } |
| 211 | |
| 212 | public function get_payment_methods() |
| 213 | { |
| 214 | return $this->cache_transformer( |
| 215 | __FUNCTION__, |
| 216 | (new PaymentMethods($this->get_filter_data()))->val() |
| 217 | ); |
| 218 | } |
| 219 | |
| 220 | public static function get_lifetime_revenue() |
| 221 | { |
| 222 | global $wpdb; |
| 223 | |
| 224 | $order_table = Base::orders_db_table(); |
| 225 | |
| 226 | return $wpdb->get_var( |
| 227 | $wpdb->prepare("SELECT SUM(total) from $order_table WHERE status = %s", OrderStatus::COMPLETED) |
| 228 | ); |
| 229 | } |
| 230 | |
| 231 | public function admin_settings_page_callback() |
| 232 | { |
| 233 | echo '<style>.ppress-admin .ppview h2{display:none;}</style>'; |
| 234 | echo '<input type="hidden" name="page" value="' . PPRESS_DASHBOARD_SETTINGS_SLUG . '" />'; |
| 235 | echo '<div class="ppress-report-charts-container">'; |
| 236 | $this->top_cards(); |
| 237 | $this->range_picker(); |
| 238 | $this->report_charts(); |
| 239 | echo '</div>'; |
| 240 | } |
| 241 | |
| 242 | public function range_picker() |
| 243 | { |
| 244 | $start_date = isset($_GET['start_date']) ? sanitize_text_field($_GET['start_date']) : null; |
| 245 | $end_date = isset($_GET['end_date']) ? sanitize_text_field($_GET['end_date']) : null; |
| 246 | $plan_id = isset($_GET['plan']) ? sanitize_text_field($_GET['plan']) : 'all'; |
| 247 | $preset = isset($_GET['preset']) ? sanitize_text_field($_GET['preset']) : 'today'; |
| 248 | |
| 249 | $clear_url = PPRESS_DASHBOARD_SETTINGS_PAGE; |
| 250 | |
| 251 | $plans = PlanRepository::init()->retrieveAll(); |
| 252 | |
| 253 | $preset_args = [ |
| 254 | 'today' => esc_html__('Today', 'wp-user-avatar'), |
| 255 | 'yesterday' => esc_html__('Yesterday', 'wp-user-avatar'), |
| 256 | 'this_week' => esc_html__('This Week', 'wp-user-avatar'), |
| 257 | 'last_week' => esc_html__('Last Week', 'wp-user-avatar'), |
| 258 | 'last_30_days' => esc_html__('Last 30 Days', 'wp-user-avatar'), |
| 259 | 'this_month' => esc_html__('This Month', 'wp-user-avatar'), |
| 260 | 'last_month' => esc_html__('Last Month', 'wp-user-avatar'), |
| 261 | 'this_quarter' => esc_html__('This Quarter', 'wp-user-avatar'), |
| 262 | 'last_quarter' => esc_html__('Last Quarter', 'wp-user-avatar'), |
| 263 | 'this_year' => esc_html__('This Year', 'wp-user-avatar'), |
| 264 | 'last_year' => esc_html__('Last Year', 'wp-user-avatar'), |
| 265 | 'custom' => esc_html__('Custom', 'wp-user-avatar'), |
| 266 | ]; |
| 267 | |
| 268 | echo '<div class="wp-filter" id="ppress-filters">'; |
| 269 | echo '<div class="filter-items">'; |
| 270 | echo '<span id="ppress-mode-filter">'; |
| 271 | echo '<select name="preset">'; |
| 272 | foreach ($preset_args as $index => $arg) { |
| 273 | printf('<option value="%1$s" %3$s>%2$s</option>', $index, $arg, selected($preset, $index, false)); |
| 274 | } |
| 275 | echo '</select>'; |
| 276 | echo '</span>'; |
| 277 | |
| 278 | echo '<span id="ppress-date-filters" class="ppress-from-to-wrapper" style="display:none">'; |
| 279 | echo '<span class="ppress-start-date-wrap">'; |
| 280 | echo '<input type="text" name="start_date" id="start-date" placeholder="' . _x('From', 'date filter', 'wp-user-avatar') . '" value="' . esc_attr($start_date) . '" class="ppress_datepicker">'; |
| 281 | echo '</span>'; |
| 282 | echo '<span id="ppress-end-date-wrap">'; |
| 283 | echo '<input type="text" name="end_date" id="end-date" value="' . esc_attr($end_date) . '" placeholder="' . _x('To', 'date filter', 'wp-user-avatar') . '" class="ppress_datepicker">'; |
| 284 | echo '</span>'; |
| 285 | echo '</span>'; |
| 286 | |
| 287 | if ( ! empty($plans)) { |
| 288 | echo '<span id="ppress-gateway-filter">'; |
| 289 | echo '<select name="plan">'; |
| 290 | echo '<option value="all">' . esc_html__('All Plans', 'wp-user-avatar') . '</option>'; |
| 291 | foreach ($plans as $plan) { |
| 292 | printf('<option value="%1$s" %3$s>%2$s</option>', $plan->id, $plan->name, selected($plan->id, $plan_id, false)); |
| 293 | } |
| 294 | echo '</select>'; |
| 295 | echo '</span>'; |
| 296 | } |
| 297 | |
| 298 | echo '<span id="ppress-after-core-filters">'; |
| 299 | echo '<input type="submit" class="button button-secondary" value="' . esc_html__('Filter', 'wp-user-avatar') . '"/> '; |
| 300 | |
| 301 | if ( ! empty($start_date) || ! empty($end_date) || 'all' !== $plan_id || 'today' !== $preset) { |
| 302 | echo '<a href="' . esc_url($clear_url) . '" class="button-secondary">'; |
| 303 | esc_html_e('Clear', 'wp-user-avatar'); |
| 304 | echo '</a>'; |
| 305 | } |
| 306 | echo '</span>'; |
| 307 | |
| 308 | echo '</div>'; |
| 309 | echo '</div>'; |
| 310 | } |
| 311 | |
| 312 | public function single_report_card($id, $title, $total, $trend = '', $sign = '%') |
| 313 | { |
| 314 | ?> |
| 315 | <div class="ppress-report-chart-bottom-card-item"> |
| 316 | <dt class="ppress-report-chart-bottom-card-item-header"> |
| 317 | <p><?= $title ?></p> |
| 318 | </dt> |
| 319 | <dd class="ppress-report-chart-bottom-card-item-content-wrap"> |
| 320 | <?php if ( ! is_null($total)) : ?> |
| 321 | <p><?php echo $total ?></p> |
| 322 | <?php endif; ?> |
| 323 | <?php if ( ! Calculator::init($trend)->isZero()) : ?> |
| 324 | <?php if (Calculator::init($trend)->isNegative()) : ?> |
| 325 | <p class="ppress-report-chart-bottom-card-item-trend negative"><?= $trend . $sign ?></p> |
| 326 | <?php else : ?> |
| 327 | <p class="ppress-report-chart-bottom-card-item-trend">+<?= $trend . $sign ?></p> |
| 328 | <?php endif; ?> |
| 329 | <?php endif; ?> |
| 330 | </dd> |
| 331 | <div class="ppress-report-chart-bottom-card-item-chart"> |
| 332 | <canvas id="<?= $id ?>"></canvas> |
| 333 | </div> |
| 334 | </div> |
| 335 | <?php |
| 336 | } |
| 337 | |
| 338 | /** |
| 339 | * @return void |
| 340 | */ |
| 341 | public function top_cards() |
| 342 | { |
| 343 | ?> |
| 344 | <div class="ppress-report-chart-top-cards-wrap"> |
| 345 | <dl class="ppress-report-chart-top-card-item-wrap"> |
| 346 | <div class="ppress-report-chart-top-card-item"> |
| 347 | <dt class="ppress-report-chart-top-card-item-header"><?php esc_html_e('Total Customers', 'wp-user-avatar') ?></dt> |
| 348 | <dd class="ppress-report-chart-top-card-item-content-wrap"> |
| 349 | <div class="ppress-report-chart-top-card-item-content"><?php echo CustomerRepository::init()->record_count() ?></div> |
| 350 | </dd> |
| 351 | </div> |
| 352 | |
| 353 | <div class="ppress-report-chart-top-card-item"> |
| 354 | <dt class="ppress-report-chart-top-card-item-header"><?php esc_html_e('Active Customers', 'wp-user-avatar') ?></dt> |
| 355 | <dd class="ppress-report-chart-top-card-item-content-wrap"> |
| 356 | <div class="ppress-report-chart-top-card-item-content"><?php echo CustomerRepository::init()->get_count_by_status(CustomerStatus::ACTIVE) ?></div> |
| 357 | </dd> |
| 358 | </div> |
| 359 | |
| 360 | <div class="ppress-report-chart-top-card-item"> |
| 361 | <dt class="ppress-report-chart-top-card-item-header"><?php esc_html_e('Active Subscriptions', 'wp-user-avatar') ?></dt> |
| 362 | <dd class="ppress-report-chart-top-card-item-content-wrap"> |
| 363 | <div class="ppress-report-chart-top-card-item-content"><?php echo SubscriptionRepository::init()->get_count_by_status(SubscriptionStatus::ACTIVE) ?></div> |
| 364 | </dd> |
| 365 | </div> |
| 366 | |
| 367 | <div class="ppress-report-chart-top-card-item"> |
| 368 | <dt class="ppress-report-chart-top-card-item-header"><?php esc_html_e('Lifetime Revenue', 'wp-user-avatar') ?></dt> |
| 369 | <dd class="ppress-report-chart-top-card-item-content-wrap"> |
| 370 | <div class="ppress-report-chart-top-card-item-content"><?php echo ppress_display_amount(self::get_lifetime_revenue()) ?></div> |
| 371 | </dd> |
| 372 | </div> |
| 373 | |
| 374 | </dl> |
| 375 | </div> |
| 376 | <?php |
| 377 | } |
| 378 | |
| 379 | public function report_charts() |
| 380 | { |
| 381 | $revenue = $this->get_revenue(); |
| 382 | $orders = $this->get_orders(); |
| 383 | $refunds = $this->get_refunds(); |
| 384 | $taxes = $this->get_taxes(); |
| 385 | $top_plans = $this->get_top_plans(); |
| 386 | $payment_methods = $this->get_payment_methods(); |
| 387 | ?> |
| 388 | <div class="ppress-report-chart-bottom-cards-wrap"> |
| 389 | <dl class="ppress-report-chart-bottom-card-item-wrap"> |
| 390 | |
| 391 | <?php $this->single_report_card( |
| 392 | 'ppress-report-revenue', |
| 393 | esc_html__('Revenue', 'wp-user-avatar'), |
| 394 | ppress_display_amount($revenue['total']), |
| 395 | $revenue['trend'] |
| 396 | ) ?> |
| 397 | |
| 398 | <?php $this->single_report_card( |
| 399 | 'ppress-report-orders', |
| 400 | esc_html__('Orders', 'wp-user-avatar'), |
| 401 | $orders['total'], |
| 402 | $orders['trend'] |
| 403 | ) ?> |
| 404 | |
| 405 | <?php if (TaxService::init()->is_tax_enabled()) : |
| 406 | $this->single_report_card( |
| 407 | 'ppress-report-tax', |
| 408 | esc_html__('Taxes', 'wp-user-avatar'), |
| 409 | ppress_display_amount($taxes['total']), |
| 410 | $taxes['trend'] |
| 411 | ); |
| 412 | endif; ?> |
| 413 | |
| 414 | <?php $this->single_report_card( |
| 415 | 'ppress-report-refunds', |
| 416 | esc_html__('Refunds', 'wp-user-avatar'), |
| 417 | $refunds['total'], |
| 418 | $refunds['trend'] |
| 419 | ) ?> |
| 420 | |
| 421 | <?php $this->single_report_card( |
| 422 | 'ppress-report-top-plans', |
| 423 | esc_html__('Top Plans', 'wp-user-avatar'), |
| 424 | $top_plans['total'], |
| 425 | $top_plans['trend'] |
| 426 | ) ?> |
| 427 | |
| 428 | <?php $this->single_report_card( |
| 429 | 'ppress-report-payment-methods', |
| 430 | esc_html__('Payment Methods', 'wp-user-avatar'), |
| 431 | $payment_methods['total'], |
| 432 | $payment_methods['trend'] |
| 433 | ) ?> |
| 434 | |
| 435 | </dl> |
| 436 | </div> |
| 437 | <?php |
| 438 | } |
| 439 | |
| 440 | public static function get_instance() |
| 441 | { |
| 442 | static $instance = null; |
| 443 | |
| 444 | if (is_null($instance)) { |
| 445 | $instance = new self(); |
| 446 | } |
| 447 | |
| 448 | return $instance; |
| 449 | } |
| 450 | } |