| @@ -53,8 +53,9 @@ | ||
| 53 | 53 | self::$date_range = 7; |
| 54 | 54 | |
| 55 | 55 | add_action( 'wp_ajax_sellkit_get_chart_data', [ $this, 'get_chart_data' ] ); |
| 56 | 56 | add_action( 'wp_ajax_sellkit_get_summary_chart_data', [ $this, 'get_summary' ] ); |
| 57 | + add_action( 'wp_ajax_sellkit_get_dashboard_stats', [ $this, 'get_dashboard_stats' ] ); | |
| 57 | 58 | } |
| 58 | 59 | |
| 59 | 60 | /** |
| 60 | 61 | * Gets analytics data. |
| @@ -63,16 +64,14 @@ | ||
| 63 | 64 | */ |
| 64 | 65 | public function get_chart_data() { |
| 65 | 66 | check_ajax_referer( 'sellkit', 'nonce' ); |
| 66 | 67 | |
| 67 | - $feature = filter_input( INPUT_GET, 'feature', FILTER_SANITIZE_STRING ); | |
| 68 | - $class = filter_input( INPUT_GET, 'type', FILTER_SANITIZE_STRING ); | |
| 69 | - $date_range = filter_input( INPUT_GET, 'date_range', FILTER_SANITIZE_STRING ); | |
| 70 | - $target_id = filter_input( INPUT_GET, 'target_id', FILTER_SANITIZE_STRING ); | |
| 68 | + $feature = sellkit_htmlspecialchars( INPUT_GET, 'feature' ); | |
| 69 | + $class = sellkit_htmlspecialchars( INPUT_GET, 'type' ); | |
| 70 | + $date_range = sellkit_htmlspecialchars( INPUT_GET, 'date_range' ); | |
| 71 | + $target_id = sellkit_htmlspecialchars( INPUT_GET, 'target_id' ); | |
| 71 | 72 | |
| 72 | - if ( 'thirty-days' === $date_range ) { | |
| 73 | - self::$date_range = 30; | |
| 74 | - } | |
| 73 | + self::$date_range = $this->resolve_date_range( $date_range ); | |
| 75 | 74 | |
| 76 | 75 | $file_name = str_replace( '_', '-', $class ); |
| 77 | 76 | |
| 78 | 77 | sellkit()->load_files( [ |
| @@ -96,14 +95,12 @@ | ||
| 96 | 95 | */ |
| 97 | 96 | public function get_summary() { |
| 98 | 97 | check_ajax_referer( 'sellkit', 'nonce' ); |
| 99 | 98 | |
| 100 | - $feature = filter_input( INPUT_GET, 'feature', FILTER_SANITIZE_STRING ); | |
| 101 | - $date_range = filter_input( INPUT_GET, 'date_range', FILTER_SANITIZE_STRING ); | |
| 99 | + $feature = sellkit_htmlspecialchars( INPUT_GET, 'feature' ); | |
| 100 | + $date_range = sellkit_htmlspecialchars( INPUT_GET, 'date_range' ); | |
| 102 | 101 | |
| 103 | - if ( 'thirty-days' === $date_range ) { | |
| 104 | - self::$date_range = 30; | |
| 105 | - } | |
| 102 | + self::$date_range = $this->resolve_date_range( $date_range ); | |
| 106 | 103 | |
| 107 | 104 | sellkit()->load_files( [ "admin/components/analytics/{$feature}/summary" ] ); |
| 108 | 105 | |
| 109 | 106 | $class = __NAMESPACE__ . "\Analytics\\$feature\\summary"; |
| @@ -108,8 +105,113 @@ | ||
| 108 | 105 | |
| 109 | 106 | $class = __NAMESPACE__ . "\Analytics\\$feature\\summary"; |
| 110 | 107 | |
| 111 | 108 | wp_send_json_success( $class::get_instance()->data ); |
| 109 | + } | |
| 110 | + | |
| 111 | + /** | |
| 112 | + * Post counts for the dashboard overview (funnels, steps, Pro post types). | |
| 113 | + * | |
| 114 | + * @since 2.5.0 | |
| 115 | + * @return void | |
| 116 | + */ | |
| 117 | + public function get_dashboard_stats() { | |
| 118 | + check_ajax_referer( 'sellkit', 'nonce' ); | |
| 119 | + | |
| 120 | + if ( ! current_user_can( 'edit_theme_options' ) ) { | |
| 121 | + wp_send_json_error( [ 'message' => esc_html__( 'Forbidden.', 'sellkit' ) ], 403 ); | |
| 122 | + } | |
| 123 | + | |
| 124 | + $data = [ | |
| 125 | + 'funnels' => $this->get_post_type_counts( 'sellkit-funnels' ), | |
| 126 | + 'steps' => $this->get_post_type_counts( 'sellkit_step' ), | |
| 127 | + 'coupons' => $this->get_post_type_counts( 'sellkit-coupon' ), | |
| 128 | + 'discounts' => $this->get_post_type_counts( 'sellkit-discount' ), | |
| 129 | + 'notices' => $this->get_post_type_counts( 'sellkit-alert' ), | |
| 130 | + ]; | |
| 131 | + | |
| 132 | + wp_send_json_success( $data ); | |
| 133 | + } | |
| 134 | + | |
| 135 | + /** | |
| 136 | + * Total and published counts for a post type. | |
| 137 | + * | |
| 138 | + * @since 2.5.0 | |
| 139 | + * @param string $post_type Post type name. | |
| 140 | + * @return array{total:int,active:int,draft:int} | |
| 141 | + */ | |
| 142 | + private function get_post_type_counts( $post_type ) { | |
| 143 | + if ( ! post_type_exists( $post_type ) ) { | |
| 144 | + return [ | |
| 145 | + 'total' => 0, | |
| 146 | + 'active' => 0, | |
| 147 | + 'draft' => 0, | |
| 148 | + ]; | |
| 149 | + } | |
| 150 | + | |
| 151 | + $counts = wp_count_posts( $post_type ); | |
| 152 | + | |
| 153 | + if ( ! $counts ) { | |
| 154 | + return [ | |
| 155 | + 'total' => 0, | |
| 156 | + 'active' => 0, | |
| 157 | + 'draft' => 0, | |
| 158 | + ]; | |
| 159 | + } | |
| 160 | + | |
| 161 | + $total = 0; | |
| 162 | + | |
| 163 | + foreach ( (array) $counts as $num ) { | |
| 164 | + $total += (int) $num; | |
| 165 | + } | |
| 166 | + | |
| 167 | + return [ | |
| 168 | + 'total' => $total, | |
| 169 | + 'active' => isset( $counts->publish ) ? (int) $counts->publish : 0, | |
| 170 | + 'draft' => isset( $counts->draft ) ? (int) $counts->draft : 0, | |
| 171 | + ]; | |
| 172 | + } | |
| 173 | + | |
| 174 | + /** | |
| 175 | + * Resolve a dashboard date range key to a day count. | |
| 176 | + * | |
| 177 | + * @since 2.5.0 | |
| 178 | + * @param string $date_range Preset key. | |
| 179 | + * @return int | |
| 180 | + */ | |
| 181 | + private function resolve_date_range( $date_range ) { | |
| 182 | + $today = new \DateTimeImmutable( 'now', wp_timezone() ); | |
| 183 | + | |
| 184 | + switch ( $date_range ) { | |
| 185 | + case 'today': | |
| 186 | + return 0; | |
| 187 | + | |
| 188 | + case 'this-week': | |
| 189 | + return (int) $today->format( 'w' ); | |
| 190 | + | |
| 191 | + case 'this-month': | |
| 192 | + return (int) $today->format( 'j' ) - 1; | |
| 193 | + | |
| 194 | + case 'this-quarter': | |
| 195 | + $month = (int) $today->format( 'n' ); | |
| 196 | + $quarter_start_month = (int) ( floor( ( $month - 1 ) / 3 ) * 3 ) + 1; | |
| 197 | + $quarter_start = $today->setDate( (int) $today->format( 'Y' ), $quarter_start_month, 1 ); | |
| 198 | + | |
| 199 | + return (int) $quarter_start->diff( $today )->days; | |
| 200 | + | |
| 201 | + case 'ninety-days': | |
| 202 | + return 90; | |
| 203 | + | |
| 204 | + case 'this-year': | |
| 205 | + return (int) $today->format( 'z' ); | |
| 206 | + | |
| 207 | + case 'thirty-days': | |
| 208 | + return 30; | |
| 209 | + | |
| 210 | + case 'seven-days': | |
| 211 | + default: | |
| 212 | + return 7; | |
| 213 | + } | |
| 112 | 214 | } |
| 113 | 215 | |
| 114 | 216 | /** |
| 115 | 217 | * The condition which is related to the target id. |