| @@ -1,0 +1,256 @@ | ||
| 1 | +<?php | |
| 2 | + | |
| 3 | +namespace NotificationX\Core; | |
| 4 | + | |
| 5 | +use NotificationX\Admin\Admin; | |
| 6 | +use NotificationX\Admin\Settings; | |
| 7 | +use NotificationX\GetInstance; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * @method static Analytics get_instance($args = null) | |
| 11 | + */ | |
| 12 | +class Analytics { | |
| 13 | + /** | |
| 14 | + * Instance of Analytics | |
| 15 | + * | |
| 16 | + * @var Analytics | |
| 17 | + */ | |
| 18 | + use GetInstance; | |
| 19 | + | |
| 20 | + /** | |
| 21 | + * | |
| 22 | + * | |
| 23 | + * @var string | |
| 24 | + */ | |
| 25 | + public static $date_format = 'Y-m-d'; | |
| 26 | + | |
| 27 | + /** | |
| 28 | + * Initially Invoked when initialized. | |
| 29 | + * | |
| 30 | + * @hook init | |
| 31 | + */ | |
| 32 | + public function __construct() { | |
| 33 | + // add_filter('get_notifications_ids', [$this, 'insert_views'], 999, 2); | |
| 34 | + add_filter( 'nx_filtered_data', [ $this, 'insert_views' ], 999, 2 ); | |
| 35 | + add_action( 'admin_menu', [ $this, 'menu' ], 30 ); | |
| 36 | + } | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * This method is responsible for Admin Menu of | |
| 40 | + * NotificationX | |
| 41 | + * | |
| 42 | + * @return void | |
| 43 | + */ | |
| 44 | + public function menu() { | |
| 45 | + if ( Settings::get_instance()->get( 'settings.enable_analytics', true ) ) { | |
| 46 | + add_submenu_page( 'nx-admin', __( 'Analytics', 'notificationx' ), __( 'Analytics', 'notificationx' ), 'read_notificationx_analytics', 'nx-analytics', [ Admin::get_instance(), 'views' ], 3 ); | |
| 47 | + } | |
| 48 | + } | |
| 49 | + | |
| 50 | + public function get_stats( $args = [] ) { | |
| 51 | + $where = []; | |
| 52 | + $args = wp_parse_args( $args, [] ); | |
| 53 | + if ( ! empty( $args['startDate'] ) && ! empty( $args['endDate'] ) ) { | |
| 54 | + $start_date = gmdate( self::$date_format, strtotime( $args['startDate'] ) ); | |
| 55 | + $end_date = gmdate( self::$date_format, strtotime( $args['endDate'] ) ); | |
| 56 | + $where['created_at'] = [ 'BETWEEN', $start_date, $end_date ]; | |
| 57 | + } | |
| 58 | + $stats = Database::get_instance()->get_posts( Database::$table_stats, '*', $where ); | |
| 59 | + $posts = PostType::get_instance()->get_posts( [], 'DISTINCT nx_id, title, source, theme' ); | |
| 60 | + return [ | |
| 61 | + 'stats' => $stats, | |
| 62 | + 'posts' => $posts, | |
| 63 | + ]; | |
| 64 | + } | |
| 65 | + | |
| 66 | + // Wrapper function for Database functions. | |
| 67 | + public function insert_analytics( $nx_id, $type = 'clicks' ) { | |
| 68 | + if ( ! $this->should_count() ) { | |
| 69 | + return false; | |
| 70 | + } | |
| 71 | + $format = 'Y-m-d'; | |
| 72 | + $stats = $this->stats_exists([ | |
| 73 | + 'nx_id' => $nx_id, | |
| 74 | + 'created_at' => gmdate( self::$date_format, time() ), | |
| 75 | + ] | |
| 76 | + ); | |
| 77 | + if ( empty( $stats ) ) { | |
| 78 | + $data = [ | |
| 79 | + 'nx_id' => $nx_id, | |
| 80 | + 'clicks' => $type == 'clicks' ? 1 : 0, | |
| 81 | + 'views' => 1, | |
| 82 | + ]; | |
| 83 | + $this->_insert_analytics( $data, time() ); | |
| 84 | + } else { | |
| 85 | + $this->increment_count( $type, $nx_id, gmdate( self::$date_format, time() ) ); | |
| 86 | + } | |
| 87 | + } | |
| 88 | + | |
| 89 | + public function _insert_analytics( $data, $time = null, $migration = false ) { | |
| 90 | + if ( ! $time ) { | |
| 91 | + $time = time(); | |
| 92 | + } | |
| 93 | + if ( $migration ) { | |
| 94 | + $_data = $data; | |
| 95 | + $nx_id = $_data['nx_id']; | |
| 96 | + $stats = $this->stats_exists([ | |
| 97 | + 'nx_id' => $nx_id, | |
| 98 | + 'created_at' => gmdate( self::$date_format, $time ), | |
| 99 | + ] | |
| 100 | + ); | |
| 101 | + if ( ! empty( $stats ) ) { | |
| 102 | + unset( $_data['nx_id'] ); | |
| 103 | + foreach ( $_data as $_type => $_s_data ) { | |
| 104 | + $this->increment_count( $_type, $nx_id, gmdate( self::$date_format, $time ), $_s_data ); | |
| 105 | + } | |
| 106 | + return; | |
| 107 | + } | |
| 108 | + } | |
| 109 | + $data['created_at'] = gmdate( self::$date_format, $time ); | |
| 110 | + Database::get_instance()->insert_post( Database::$table_stats, $data ); | |
| 111 | + } | |
| 112 | + | |
| 113 | + public function migrate_analytics( $rows ) { | |
| 114 | + if ( ! empty( $rows ) ) { | |
| 115 | + $stats = Database::get_instance()->insert_posts( Database::$table_stats, $rows ); | |
| 116 | + return $stats; | |
| 117 | + } | |
| 118 | + return false; | |
| 119 | + } | |
| 120 | + | |
| 121 | + public function delete_analytics($where__or_nx_id, $limit = 0) { | |
| 122 | + if (!is_array($where__or_nx_id)) { | |
| 123 | + $where__or_nx_id = ['nx_id' => $where__or_nx_id]; | |
| 124 | + } | |
| 125 | + Database::get_instance()->delete_posts(Database::$table_stats, $where__or_nx_id, $limit); | |
| 126 | + $analytics = $this->get_total_count(); | |
| 127 | + return $analytics; | |
| 128 | + } | |
| 129 | + | |
| 130 | + public function stats_exists( $where = [] ) { | |
| 131 | + $stats = Database::get_instance()->get_col( Database::$table_stats, 'stat_id', $where ); | |
| 132 | + return $stats; | |
| 133 | + } | |
| 134 | + | |
| 135 | + public function increment_count( $type, $nx_id, $date, $_data = null ) { | |
| 136 | + return Database::get_instance()->update_analytics( $type, $nx_id, $date, $_data ); | |
| 137 | + } | |
| 138 | + | |
| 139 | + public function get_count( $nx_id, $type ) { | |
| 140 | + $where = [ 'nx_id' => absint( $nx_id ) ]; | |
| 141 | + $stats = Database::get_instance()->get_col( Database::$table_stats, $type, $where ); | |
| 142 | + if ( ! empty( $stats[0] ) ) { | |
| 143 | + return $stats[0]; | |
| 144 | + } | |
| 145 | + return 0; | |
| 146 | + } | |
| 147 | + | |
| 148 | + public function get_total_count() { | |
| 149 | + $stats = Database::get_instance()->get_posts( Database::$table_stats, 'SUM(views) AS totalViews, SUM(clicks) AS totalClicks' ); | |
| 150 | + if ( ! empty( $stats[0] ) ) { | |
| 151 | + \extract( $stats[0] ); | |
| 152 | + return [ | |
| 153 | + 'totalViews' => Helper::nice_number( $totalViews ), | |
| 154 | + 'totalClicks' => Helper::nice_number( $totalClicks ), | |
| 155 | + 'totalCtr' => $totalViews > 0 ? round( ( $totalClicks / $totalViews ) * 100, 2 ) : 0, | |
| 156 | + ]; | |
| 157 | + } | |
| 158 | + return 0; | |
| 159 | + } | |
| 160 | + | |
| 161 | + /** | |
| 162 | + * @todo maybe optimize in future. so that db can be updated in one request. | |
| 163 | + * | |
| 164 | + * @param [type] $entries | |
| 165 | + * @param [type] $post | |
| 166 | + * @return void | |
| 167 | + */ | |
| 168 | + public function insert_views( $entries, $post ) { | |
| 169 | + if ( ! did_action( 'nx_ignore_analytics' ) ) { | |
| 170 | + $this->insert_analytics( $post['nx_id'], 'views' ); | |
| 171 | + } | |
| 172 | + return $entries; | |
| 173 | + } | |
| 174 | + | |
| 175 | + /* | |
| 176 | + public function insert_views($nx_ids_array){ | |
| 177 | + $nx_ids = array_merge($nx_ids_array['global'], $nx_ids_array['active']); | |
| 178 | + $this->insert_post($nx_ids, 'views'); | |
| 179 | + return $nx_ids_array; | |
| 180 | + } */ | |
| 181 | + | |
| 182 | + public function should_count() { | |
| 183 | + global $user_ID; | |
| 184 | + $should_count = false; | |
| 185 | + $exclude_bot_analytics = Settings::get_instance()->get( 'settings.exclude_bot_analytics', false ); | |
| 186 | + $analytics_from = Settings::get_instance()->get( 'settings.analytics_from', false ); | |
| 187 | + $analytics_from = empty( $analytics_from ) ? 'everyone' : $analytics_from; | |
| 188 | + | |
| 189 | + /** | |
| 190 | + * Inspired from WP-Postviews for | |
| 191 | + * this pece of code. | |
| 192 | + */ | |
| 193 | + switch ( $analytics_from ) { | |
| 194 | + case 'everyone': | |
| 195 | + $should_count = true; | |
| 196 | + break; | |
| 197 | + case 'guests': | |
| 198 | + if ( empty( $_COOKIE[ USER_COOKIE ] ) && (int) $user_ID === 0 ) { | |
| 199 | + $should_count = true; | |
| 200 | + } | |
| 201 | + break; | |
| 202 | + case 'registered_users': | |
| 203 | + if ( (int) $user_ID > 0 ) { | |
| 204 | + $should_count = true; | |
| 205 | + } | |
| 206 | + break; | |
| 207 | + } | |
| 208 | + | |
| 209 | + if ( $should_count && $exclude_bot_analytics ) { | |
| 210 | + /** | |
| 211 | + * Inspired from WP-Postviews for | |
| 212 | + * this piece of code. | |
| 213 | + */ | |
| 214 | + $bots = array( | |
| 215 | + 'Google Bot' => 'google', | |
| 216 | + 'MSN' => 'msnbot', | |
| 217 | + 'Alex' => 'ia_archiver', | |
| 218 | + 'Lycos' => 'lycos', | |
| 219 | + 'Ask Jeeves' => 'jeeves', | |
| 220 | + 'Altavista' => 'scooter', | |
| 221 | + 'AllTheWeb' => 'fast-webcrawler', | |
| 222 | + 'Inktomi' => 'slurp@inktomi', | |
| 223 | + 'Turnitin.com' => 'turnitinbot', | |
| 224 | + 'Technorati' => 'technorati', | |
| 225 | + 'Yahoo' => 'yahoo', | |
| 226 | + 'Findexa' => 'findexa', | |
| 227 | + 'NextLinks' => 'findlinks', | |
| 228 | + 'Gais' => 'gaisbo', | |
| 229 | + 'WiseNut' => 'zyborg', | |
| 230 | + 'WhoisSource' => 'surveybot', | |
| 231 | + 'Bloglines' => 'bloglines', | |
| 232 | + 'BlogSearch' => 'blogsearch', | |
| 233 | + 'PubSub' => 'pubsub', | |
| 234 | + 'Syndic8' => 'syndic8', | |
| 235 | + 'RadioUserland' => 'userland', | |
| 236 | + 'Gigabot' => 'gigabot', | |
| 237 | + 'Become.com' => 'become.com', | |
| 238 | + 'Baidu' => 'baiduspider', | |
| 239 | + 'so.com' => '360spider', | |
| 240 | + 'Sogou' => 'spider', | |
| 241 | + 'soso.com' => 'sosospider', | |
| 242 | + 'Yandex' => 'yandex', | |
| 243 | + ); | |
| 244 | + $useragent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : ''; | |
| 245 | + foreach ( $bots as $name => $lookfor ) { | |
| 246 | + if ( ! empty( $useragent ) && ( false !== stripos( $useragent, $lookfor ) ) ) { | |
| 247 | + $should_count = false; | |
| 248 | + break; | |
| 249 | + } | |
| 250 | + } | |
| 251 | + } | |
| 252 | + | |
| 253 | + return $should_count; | |
| 254 | + } | |
| 255 | + | |
| 256 | +} | |