Google Analytics - Version 2.0.0

Version Description

  • Completely redesigned with new features!
  • Updated with the latest Google Analytics code
  • No need to find your GA property ID and copy it over, just sign in with Google and choose your site
  • See analytics right inside the plugin, the past 7 days vs your previous 7 days
  • Shows pageviews, users, pages per session and bounce rate + top 5 traffic referrals
  • Wordpress Dashboard widget for 7, 30 or 90 days graph and top site usage stats
  • Disable tracking for logged in users like admins or editors for more reliable analytics
Download this release

Release Info

Developer ShareThis
Plugin Icon wp plugin Google Analytics
Version 2.0.0
Comparing to
See all releases

Code changes from version 1.0.7 to 2.0.0

class/Ga_Admin.php ADDED
@@ -0,0 +1,558 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ga_Admin {
4
+
5
+ const GA_WEB_PROPERTY_ID_OPTION_NAME = 'googleanalytics_web_property_id';
6
+
7
+ const GA_EXCLUDE_ROLES_OPTION_NAME = 'googleanalytics_exclude_roles';
8
+
9
+ const GA_SHARETHIS_TERMS_OPTION_NAME = 'googleanalytics_sharethis_terms';
10
+
11
+ const GA_HIDE_TERMS_OPTION_NAME = 'googleanalytics_hide_terms';
12
+
13
+ const GA_VERSION_OPTION_NAME = 'googleanalytics_version';
14
+
15
+ const GA_SELECTED_ACCOUNT = 'googleanalytics_selected_account';
16
+
17
+ const GA_OAUTH_AUTH_CODE_OPTION_NAME = 'googleanalytics_oauth_auth_code';
18
+
19
+ const GA_OAUTH_AUTH_TOKEN_OPTION_NAME = 'googleanalytics_oauth_auth_token';
20
+
21
+ const GA_ACCOUNT_DATA_OPTION_NAME = 'googleanalytics_account_data';
22
+
23
+ const GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME = 'googleanalytics_web_property_id_manually';
24
+
25
+ const GA_WEB_PROPERTY_ID_MANUALLY_VALUE_OPTION_NAME = 'googleanalytics_web_property_id_manually_value';
26
+
27
+ const MIN_WP_VERSION = '3.8';
28
+
29
+ /**
30
+ * Instantiate API client.
31
+ *
32
+ * @return Ga_Lib_Api_Client|null
33
+ */
34
+ public static function api_client() {
35
+ $instance = Ga_Lib_Api_Client::get_instance();
36
+ $token = Ga_Helper::get_option( self::GA_OAUTH_AUTH_TOKEN_OPTION_NAME );
37
+ try {
38
+ if ( ! empty( $token ) ) {
39
+ $token = json_decode( $token, true );
40
+ $instance->set_access_token( $token );
41
+ }
42
+ } catch ( Exception $e ) {
43
+ Ga_Helper::ga_oauth_notice( $e->getMessage() );
44
+ }
45
+
46
+ return $instance;
47
+ }
48
+
49
+ /*
50
+ * Initializes plugin's options during plugin activation process.
51
+ */
52
+ public static function activate_googleanalytics() {
53
+ add_option( self::GA_WEB_PROPERTY_ID_OPTION_NAME, Ga_Helper::GA_DEFAULT_WEB_ID );
54
+ add_option( self::GA_EXCLUDE_ROLES_OPTION_NAME, wp_json_encode( array() ) );
55
+ add_option( self::GA_SHARETHIS_TERMS_OPTION_NAME, false );
56
+ add_option( self::GA_HIDE_TERMS_OPTION_NAME, false );
57
+ add_option( self::GA_VERSION_OPTION_NAME );
58
+ add_option( self::GA_OAUTH_AUTH_CODE_OPTION_NAME );
59
+ add_option( self::GA_OAUTH_AUTH_TOKEN_OPTION_NAME );
60
+ add_option( self::GA_ACCOUNT_DATA_OPTION_NAME );
61
+ add_option( self::GA_SELECTED_ACCOUNT );
62
+ add_option( self::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME );
63
+ add_option( self::GA_WEB_PROPERTY_ID_MANUALLY_VALUE_OPTION_NAME );
64
+ }
65
+
66
+ /*
67
+ * Deletes plugin's options during plugin activation process.
68
+ */
69
+ public static function deactivate_googleanalytics() {
70
+ delete_option( self::GA_WEB_PROPERTY_ID_OPTION_NAME );
71
+ delete_option( self::GA_EXCLUDE_ROLES_OPTION_NAME );
72
+ delete_option( self::GA_OAUTH_AUTH_CODE_OPTION_NAME );
73
+ delete_option( self::GA_OAUTH_AUTH_TOKEN_OPTION_NAME );
74
+ delete_option( self::GA_ACCOUNT_DATA_OPTION_NAME );
75
+ delete_option( self::GA_SELECTED_ACCOUNT );
76
+ delete_option( self::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME );
77
+ delete_option( self::GA_WEB_PROPERTY_ID_MANUALLY_VALUE_OPTION_NAME );
78
+ }
79
+
80
+ /**
81
+ * Deletes plugin's options during plugin uninstallation process.
82
+ */
83
+ public static function uninstall_googleanalytics() {
84
+ delete_option( self::GA_SHARETHIS_TERMS_OPTION_NAME );
85
+ delete_option( self::GA_HIDE_TERMS_OPTION_NAME );
86
+ delete_option( self::GA_VERSION_OPTION_NAME );
87
+ }
88
+
89
+ /**
90
+ * Do actions during plugin load.
91
+ */
92
+ public static function loaded_googleanalytics() {
93
+ self::update_googleanalytics();
94
+ }
95
+
96
+ /**
97
+ * Update hook fires when plugin is being loaded.
98
+ */
99
+ public static function update_googleanalytics() {
100
+
101
+ $installed_version = get_option( self::GA_VERSION_OPTION_NAME, '1.0.7' );
102
+ $old_property_value = Ga_Helper::get_option( 'web_property_id' );
103
+ if ( version_compare( $installed_version, GOOGLEANALYTICS_VERSION, 'eq' ) ) {
104
+ return;
105
+ }
106
+ if ( empty( $old_property_value ) && empty( get_option( self::GA_VERSION_OPTION_NAME) ) ) {
107
+ update_option( self::GA_SHARETHIS_TERMS_OPTION_NAME, true );
108
+ }
109
+
110
+ if ( version_compare( $installed_version, GOOGLEANALYTICS_VERSION, 'lt' ) ) {
111
+
112
+ if ( ! empty( $old_property_value ) ) {
113
+ Ga_Helper::update_option( self::GA_WEB_PROPERTY_ID_MANUALLY_VALUE_OPTION_NAME, $old_property_value );
114
+ Ga_Helper::update_option( self::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME, 1 );
115
+ delete_option( 'web_property_id' );
116
+ }
117
+ }
118
+
119
+ update_option( self::GA_VERSION_OPTION_NAME, GOOGLEANALYTICS_VERSION );
120
+ }
121
+
122
+
123
+ public static function preupdate_exclude_roles( $new_value, $old_value ) {
124
+ if ( !Ga_Helper::are_terms_accepted()){
125
+ return '';
126
+ }
127
+ return wp_json_encode( $new_value );
128
+ }
129
+
130
+ /**
131
+ * Pre-update hook for preparing JSON structure.
132
+ *
133
+ * @param $new_value
134
+ * @param $old_value
135
+ *
136
+ * @return mixed
137
+ */
138
+ public static function preupdate_selected_account( $new_value, $old_value ) {
139
+ $data = explode( "_", $new_value );
140
+ if ( ! empty( $data[1] ) ) {
141
+ Ga_Helper::update_option( self::GA_WEB_PROPERTY_ID_OPTION_NAME, $data[1] );
142
+ }
143
+
144
+ return wp_json_encode( $data );
145
+ }
146
+
147
+ /**
148
+ * Registers plugin's settings.
149
+ */
150
+ public static function admin_init_googleanalytics() {
151
+ register_setting( GA_NAME, self::GA_WEB_PROPERTY_ID_OPTION_NAME );
152
+ register_setting( GA_NAME, self::GA_EXCLUDE_ROLES_OPTION_NAME );
153
+ register_setting( GA_NAME, self::GA_SELECTED_ACCOUNT );
154
+ register_setting( GA_NAME, self::GA_OAUTH_AUTH_CODE_OPTION_NAME );
155
+ register_setting( GA_NAME, self::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME );
156
+ register_setting( GA_NAME, self::GA_WEB_PROPERTY_ID_MANUALLY_VALUE_OPTION_NAME );
157
+ add_filter( 'pre_update_option_' . Ga_Admin::GA_EXCLUDE_ROLES_OPTION_NAME, 'Ga_Admin::preupdate_exclude_roles', 1, 2 );
158
+ add_filter( 'pre_update_option_' . Ga_Admin::GA_SELECTED_ACCOUNT, 'Ga_Admin::preupdate_selected_account', 1, 2 );
159
+ }
160
+
161
+ public static function admin_menu_googleanalytics() {
162
+ if ( current_user_can( 'manage_options' ) ) {
163
+ add_menu_page( 'Google Analytics', 'Google Analytics', 'manage_options', 'googleanalytics', 'Ga_Admin::statistics_page_googleanalytics', 'dashicons-chart-line', 1000 );
164
+ add_submenu_page( 'googleanalytics', 'Google Analytics', __( 'Dashboard' ), 'manage_options', 'googleanalytics', 'Ga_Admin::statistics_page_googleanalytics' );
165
+ add_submenu_page( 'googleanalytics', 'Google Analytics', __( 'Settings' ), 'manage_options', 'googleanalytics/settings', 'Ga_Admin::options_page_googleanalytics' );
166
+ }
167
+ }
168
+
169
+ public static function update_terms() {
170
+ if ( !empty( $_GET['accept-terms'] ) && ( 'Y' === $_GET['accept-terms'] ) ) {
171
+ update_option( self::GA_SHARETHIS_TERMS_OPTION_NAME, true );
172
+ }
173
+ }
174
+ /**
175
+ * Prepares and displays plugin's stats page.
176
+ */
177
+ public static function statistics_page_googleanalytics() {
178
+
179
+ if ( ! Ga_Helper::is_wp_version_valid() ) {
180
+ return false;
181
+ }
182
+ self::update_terms();
183
+ $data = self::get_stats_page();
184
+ Ga_View::load( 'statistics', array(
185
+ 'data' => $data
186
+ ) );
187
+ }
188
+
189
+ /**
190
+ * Prepares and displays plugin's settings page.
191
+ */
192
+ public static function options_page_googleanalytics() {
193
+
194
+ if ( ! Ga_Helper::is_wp_version_valid() ) {
195
+ return false;
196
+ }
197
+ self::update_terms();
198
+
199
+ /**
200
+ * Keeps data to be extracted as variables in the view.
201
+ *
202
+ * @var array $data
203
+ */
204
+ $data = array();
205
+
206
+ $data[ self::GA_WEB_PROPERTY_ID_OPTION_NAME ] = get_option( self::GA_WEB_PROPERTY_ID_OPTION_NAME );
207
+ $data[ self::GA_WEB_PROPERTY_ID_MANUALLY_VALUE_OPTION_NAME ] = get_option( self::GA_WEB_PROPERTY_ID_MANUALLY_VALUE_OPTION_NAME );
208
+ $data[ self::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME ] = get_option( self::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME );
209
+
210
+ $roles = Ga_Helper::get_user_roles();
211
+ $saved = json_decode( get_option( self::GA_EXCLUDE_ROLES_OPTION_NAME ), true );
212
+
213
+ $tmp = array();
214
+ if ( ! empty( $roles ) ) {
215
+ foreach ( $roles as $role ) {
216
+ $role_id = Ga_Helper::prepare_role_id( $role );
217
+ $tmp[] = array(
218
+ 'name' => $role,
219
+ 'id' => $role_id,
220
+ 'checked' => ( ! empty( $saved[ $role_id ] ) && $saved[ $role_id ] === 'on' )
221
+ );
222
+ }
223
+ }
224
+ $data['roles'] = $tmp;
225
+
226
+ if ( Ga_Helper::is_authorized() ) {
227
+ $data['ga_accounts_selector'] = self::get_accounts_selector();
228
+ } else {
229
+ $data['popup_url'] = self::get_auth_popup_url();
230
+ }
231
+
232
+ Ga_View::load( 'page', array(
233
+ 'data' => $data
234
+ ) );
235
+ }
236
+
237
+ /**
238
+ * Prepares and returns a plugin's URL to be opened in a popup window
239
+ * during Google authentication process.
240
+ *
241
+ * @return mixed
242
+ */
243
+ public static function get_auth_popup_url() {
244
+ return admin_url( Ga_Helper::GA_SETTINGS_PAGE_URL . '&ga_action=ga_auth' );
245
+ }
246
+
247
+ /**
248
+ * Prepares and returns Google Account's dropdown code.
249
+ *
250
+ * @return string
251
+ */
252
+ public static function get_accounts_selector() {
253
+ $selected = Ga_Helper::get_selected_account_data();
254
+
255
+ return Ga_View::load( 'ga_accounts_selector', array(
256
+ 'selector' => json_decode( get_option( self::GA_ACCOUNT_DATA_OPTION_NAME ), true ),
257
+ 'selected' => $selected ? implode( "_", $selected ) : null,
258
+ 'add_manually_enabled' => Ga_Helper::is_code_manually_enabled()
259
+ ), true );
260
+ }
261
+
262
+ /**
263
+ * Adds Bootstrap scripts.
264
+ */
265
+ public static function enqueue_bootstrap() {
266
+ wp_register_script( GA_NAME . '-bootstrap-js', GA_PLUGIN_URL . '/js/bootstrap.min.js', array(
267
+ 'jquery'
268
+ ) );
269
+ wp_register_style( GA_NAME . '-bootstrap-css', GA_PLUGIN_URL . '/css/bootstrap.min.css', false, null, 'all' );
270
+ wp_enqueue_script( GA_NAME . '-bootstrap-js' );
271
+ wp_enqueue_style( GA_NAME . '-bootstrap-css' );
272
+ }
273
+
274
+ /**
275
+ * Adds JS scripts for the settings page.
276
+ */
277
+ public static function enqueue_ga_scripts() {
278
+ wp_register_script( GA_NAME . '-page-js', GA_PLUGIN_URL . '/js/' . GA_NAME . '_page.js', array(
279
+ 'jquery'
280
+ ) );
281
+ wp_enqueue_script( GA_NAME . '-page-js' );
282
+ }
283
+
284
+ /**
285
+ * Adds CSS plugin's scripts.
286
+ */
287
+ public static function enqueue_ga_css() {
288
+ wp_register_style( GA_NAME . '-css', GA_PLUGIN_URL . '/css/' . GA_NAME . '.css', false, null, 'all' );
289
+ wp_enqueue_style( GA_NAME . '-css' );
290
+ }
291
+
292
+ /**
293
+ * Enqueues dashboard JS scripts.
294
+ */
295
+ private static function enqueue_dashboard_scripts() {
296
+ wp_register_script( GA_NAME . '-dashboard-js', GA_PLUGIN_URL . '/js/' . GA_NAME . '_dashboard.js', array(
297
+ 'jquery'
298
+ ) );
299
+ wp_enqueue_script( GA_NAME . '-dashboard-js' );
300
+ }
301
+
302
+ /**
303
+ * Enqueues plugin's JS and CSS scripts.
304
+ */
305
+ public static function enqueue_scripts() {
306
+ if ( Ga_Helper::is_dashboard_page() || Ga_Helper::is_plugin_page() ) {
307
+ wp_register_script( GA_NAME . '-js', GA_PLUGIN_URL . '/js/' . GA_NAME . '.js', array(
308
+ 'jquery'
309
+ ) );
310
+ wp_enqueue_script( GA_NAME . '-js' );
311
+
312
+ wp_register_script( 'googlecharts', 'https://www.gstatic.com/charts/loader.js', null, null, false );
313
+ wp_enqueue_script( 'googlecharts' );
314
+
315
+ self::enqueue_ga_css();
316
+ }
317
+
318
+ if ( Ga_Helper::is_dashboard_page() ) {
319
+ self::enqueue_dashboard_scripts();
320
+ }
321
+
322
+ if ( Ga_Helper::is_plugin_page() ) {
323
+ self::enqueue_bootstrap();
324
+ self::enqueue_ga_scripts();
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Prepares plugin's statistics page and return HTML code.
330
+ *
331
+ * @return string HTML code
332
+ */
333
+ public static function get_stats_page() {
334
+ $selected = Ga_Helper::get_selected_account_data( true );
335
+
336
+ $query_params = Ga_Stats::get_query( 'main_chart', $selected['view_id'] );
337
+ $stats_data = self::api_client()->call( 'ga_api_data', array(
338
+
339
+ $query_params
340
+ ) );
341
+
342
+ $boxes_data = self::api_client()->call( 'ga_api_data', array(
343
+
344
+ Ga_Stats::get_query( 'boxes', $selected['view_id'] )
345
+ ) );
346
+ $sources_data = self::api_client()->call( 'ga_api_data', array(
347
+
348
+ Ga_Stats::get_query( 'sources', $selected['view_id'] )
349
+ ) );
350
+ $chart = ! empty( $stats_data ) ? Ga_Stats::get_chart( $stats_data->getData() ) : array();
351
+ $boxes = ! empty( $boxes_data ) ? Ga_Stats::get_boxes( $boxes_data->getData() ) : array();
352
+ $last_chart_date = ! empty( $chart ) ? $chart['date'] : strtotime( 'now' );
353
+ unset( $chart['date'] );
354
+ $labels = array(
355
+ 'thisWeek' => date( 'M d, Y', strtotime( '-6 day', $last_chart_date ) ) . ' - ' . date( 'M d, Y', $last_chart_date ),
356
+ 'lastWeek' => date( 'M d, Y', strtotime( '-13 day', $last_chart_date ) ) . ' - ' . date( 'M d, Y', strtotime( '-7 day', $last_chart_date ) )
357
+ );
358
+ $sources = ! empty( $sources_data ) ? Ga_Stats::get_sources( $sources_data->getData() ) : array();
359
+
360
+ return Ga_Helper::get_chart_page( 'stats', array(
361
+
362
+ 'chart' => $chart,
363
+ 'boxes' => $boxes,
364
+ 'labels' => $labels,
365
+ 'sources' => $sources
366
+ ) );
367
+ }
368
+
369
+ /**
370
+ * Shows plugin's notice on the admin area.
371
+ */
372
+ public static function admin_notice_googleanalytics() {
373
+
374
+ if ( ( ! get_option( self::GA_SHARETHIS_TERMS_OPTION_NAME ) && Ga_Helper::is_plugin_page() ) || ( ! get_option( self::GA_SHARETHIS_TERMS_OPTION_NAME ) && ! get_option( self::GA_HIDE_TERMS_OPTION_NAME ) ) ) {
375
+ if ( !empty( $_GET['accept-terms'] ) && ( 'Y' === $_GET['accept-terms'] ) ) {
376
+ return;
377
+ }
378
+ $url = home_url( $_SERVER['REQUEST_URI'] . '&accept-terms=Y' );
379
+ Ga_View::load( 'ga_notice', array(
380
+
381
+ 'url' => $url
382
+ ) );
383
+ }
384
+
385
+ if ( ! Ga_Helper::is_wp_version_valid() ) {
386
+ Ga_View::load( 'ga_oauth_notice', array(
387
+ 'msg' => _( 'Google Analytics plugin requires at least WordPress version ' . self::MIN_WP_VERSION )
388
+ ) );
389
+ }
390
+ }
391
+
392
+ /**
393
+ * Hides plugin's notice
394
+ */
395
+ public static function admin_notice_hide_googleanalytics() {
396
+ update_option( self::GA_HIDE_TERMS_OPTION_NAME, true );
397
+ }
398
+
399
+ /**
400
+ * Adds GA dashboard widget only for administrators.
401
+ */
402
+ public static function add_dashboard_device_widget() {
403
+ if ( Ga_Helper::is_administrator() ) {
404
+ wp_add_dashboard_widget( 'ga_dashboard_widget', __( 'Google Analytics Dashboard' ), 'Ga_Helper::add_ga_dashboard_widget' );
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Adds plugin's actions
410
+ */
411
+ public static function add_actions() {
412
+ add_action( 'admin_init', 'Ga_Admin::admin_init_googleanalytics' );
413
+ add_action( 'admin_menu', 'Ga_Admin::admin_menu_googleanalytics' );
414
+ add_action( 'admin_enqueue_scripts', 'Ga_Admin::enqueue_scripts' );
415
+ add_action( 'wp_dashboard_setup', 'Ga_Admin::add_dashboard_device_widget' );
416
+ add_action( 'wp_ajax_ga_ajax_data_change', 'Ga_Admin::ga_ajax_data_change' );
417
+ add_action( 'admin_notices', 'Ga_Admin::admin_notice_googleanalytics' );
418
+
419
+ if ( ! get_option( self::GA_SHARETHIS_TERMS_OPTION_NAME ) && ! get_option( self::GA_HIDE_TERMS_OPTION_NAME ) ) {
420
+ add_action( 'wp_ajax_googleanalytics_hide_terms', 'Ga_Admin::admin_notice_hide_googleanalytics' );
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Adds plugin's filters
426
+ */
427
+ public static function add_filters() {
428
+ add_filter( 'plugin_action_links', 'Ga_Admin::ga_action_links', 10, 5 );
429
+ }
430
+
431
+ /**
432
+ * Adds new action links on the plugin list.
433
+ *
434
+ * @param $actions
435
+ * @param $plugin_file
436
+ *
437
+ * @return mixed
438
+ */
439
+ public static function ga_action_links( $actions, $plugin_file ) {
440
+
441
+ if ( basename( $plugin_file ) == GA_NAME . '.php' ) {
442
+ array_unshift( $actions, '<a href="' . esc_url( get_admin_url( null, Ga_Helper::GA_SETTINGS_PAGE_URL ) ) . '">' . _( 'Settings' ) . '</a>' );
443
+ }
444
+
445
+ return $actions;
446
+ }
447
+
448
+ public static function init_oauth() {
449
+ // $code = ! empty( $_GET['code'] ) ? $_GET['code'] : null;
450
+ $code = Ga_Helper::get_option( self::GA_OAUTH_AUTH_CODE_OPTION_NAME );
451
+
452
+ if ( ! Ga_Helper::is_authorized() && $code ) {
453
+ Ga_Helper::update_option( self::GA_OAUTH_AUTH_CODE_OPTION_NAME, "" );
454
+
455
+ // Get access token
456
+ $response = self::api_client()->call( 'ga_auth_get_access_token', $code );
457
+
458
+ self::save_access_token( $response );
459
+ self::api_client()->set_access_token( $response->getData() ); // sprawdzic
460
+
461
+ // Get accounts data
462
+ $account_summaries = self::api_client()->call( 'ga_api_account_summaries' );
463
+ self::save_ga_account_summaries( $account_summaries->getData() );
464
+
465
+ wp_redirect( admin_url( Ga_Helper::GA_SETTINGS_PAGE_URL ) );
466
+ }
467
+ }
468
+
469
+ public static function handle_actions() {
470
+ $action = ! empty( $_GET['ga_action'] ) ? $_GET['ga_action'] : null;
471
+
472
+ if ( $action ) {
473
+ $class = __CLASS__;
474
+ if ( is_callable( array(
475
+
476
+ $class,
477
+ $action
478
+ ) ) ) {
479
+ $class::$action();
480
+ }
481
+ }
482
+ }
483
+
484
+ public static function ga_auth() {
485
+ if ( Ga_Helper::are_terms_accepted() ) {
486
+ header( 'Location:' . self::api_client()->create_auth_url() );
487
+ } else {
488
+ wp_die( Ga_Helper::ga_oauth_notice( __( 'Please accept the terms to use this feature' ) ) );
489
+ }
490
+ }
491
+
492
+ /**
493
+ * Save access token.
494
+ *
495
+ * @param Ga_Lib_Api_Response $response
496
+ *
497
+ * @return boolean
498
+ */
499
+ public static function save_access_token( $response ) {
500
+ $access_token = $response->getData();
501
+ $access_token['created'] = time();
502
+
503
+ return update_option( self::GA_OAUTH_AUTH_TOKEN_OPTION_NAME, wp_json_encode( $access_token ) );
504
+ }
505
+
506
+ /**
507
+ * Saves Google Analytics account data.
508
+ *
509
+ * @param $data
510
+ *
511
+ * @return array
512
+ */
513
+ public static function save_ga_account_summaries( $data ) {
514
+ $return = array();
515
+ if ( ! empty( $data['items'] ) ) {
516
+ foreach ( $data['items'] as $item ) {
517
+ $tmp = array();
518
+ $tmp['id'] = $item['id'];
519
+ $tmp['name'] = $item['name'];
520
+ if ( is_array( $item['webProperties'] ) ) {
521
+ foreach ( $item['webProperties'] as $property ) {
522
+ $profiles = array();
523
+ if ( is_array( $property['profiles'] ) ) {
524
+ foreach ( $property['profiles'] as $profile ) {
525
+ $profiles[] = array(
526
+
527
+ 'id' => $profile['id'],
528
+ 'name' => $profile['name']
529
+ );
530
+ }
531
+ }
532
+
533
+ $tmp['webProperties'][] = array(
534
+
535
+ 'webPropertyId' => $property['id'],
536
+ 'name' => $property['name'],
537
+ 'profiles' => $profiles
538
+ );
539
+ }
540
+ }
541
+
542
+ $return[] = $tmp;
543
+ }
544
+
545
+ update_option( self::GA_ACCOUNT_DATA_OPTION_NAME, wp_json_encode( $return ) );
546
+ update_option( self::GA_WEB_PROPERTY_ID_OPTION_NAME, "" );
547
+ }
548
+
549
+ return $return;
550
+ }
551
+
552
+ public static function ga_ajax_data_change() {
553
+ $date_range = ! empty( $_POST['date_range'] ) ? $_POST['date_range'] : null;
554
+ $metric = ! empty( $_POST['metric'] ) ? $_POST['metric'] : null;
555
+ echo Ga_Helper::get_ga_dashboard_widget_data_json( $date_range, $metric, false, true );
556
+ wp_die();
557
+ }
558
+ }
class/Ga_Autoloader.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ga_Autoloader {
4
+
5
+ /**
6
+ * Registers clas loader.
7
+ */
8
+ public static function register() {
9
+ spl_autoload_register( "Ga_Autoloader::loader" );
10
+ }
11
+
12
+ /**
13
+ * Class loader.
14
+ *
15
+ * @param $class_name
16
+ */
17
+ private static function loader( $class_name ) {
18
+ $file_name = GA_PLUGIN_DIR . '/class/' . $class_name . '.php';
19
+ if ( file_exists( $file_name ) ) {
20
+ require $file_name;
21
+ }
22
+
23
+ if ( preg_match( '/Ga_Lib/', $class_name ) ) {
24
+ $file_name = GA_PLUGIN_DIR . '/lib/' . $class_name . '.php';
25
+ if ( file_exists( $file_name ) ) {
26
+ require $file_name;
27
+ }
28
+ }
29
+ }
30
+ }
class/Ga_Frontend.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ga_Frontend {
4
+
5
+ public static function insights_googleanalytics() {
6
+ if ( is_ssl() ) {
7
+ $url = 'https://ws.sharethis.com/button/st_insights.js';
8
+ } else {
9
+ $url = 'http://w.sharethis.com/button/st_insights.js';
10
+ }
11
+ $url = add_query_arg( array(
12
+
13
+ 'publisher' => '75560ae7-5c5f-483e-936f-e426496af114',
14
+ 'product' => 'GA'
15
+ ), $url );
16
+ wp_register_script( GA_NAME . '-sharethis', $url, null, null, false );
17
+ wp_enqueue_script( GA_NAME . '-sharethis' );
18
+ }
19
+
20
+ public static function loader_tag_googleanalytics( $tag, $handle ) {
21
+ if ( GA_NAME . '-sharethis' === $handle ) {
22
+ $tag = str_replace( '<script', '<script id=\'st_insights_js\'', $tag );
23
+ }
24
+
25
+ return $tag;
26
+ }
27
+
28
+ /**
29
+ * Adds frontend actions hooks.
30
+ */
31
+ public static function add_actions() {
32
+ if ( Ga_Helper::can_add_ga_code() ) {
33
+ add_action( 'wp_head', 'Ga_Frontend::googleanalytics' );
34
+ if ( get_option( Ga_Admin::GA_SHARETHIS_TERMS_OPTION_NAME ) && Ga_Helper::is_sharethis_included() ) {
35
+ add_action( 'wp_enqueue_scripts', 'Ga_Frontend::insights_googleanalytics' );
36
+ add_filter( 'script_loader_tag', 'Ga_Frontend::loader_tag_googleanalytics', 10, 2 );
37
+ }
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Displays Google Analytics Tracking code.
43
+ */
44
+ public static function googleanalytics() {
45
+ $web_property_id = self::get_web_property_id();
46
+ if ( Ga_Helper::is_configured( $web_property_id ) ) {
47
+ Ga_View::load( 'ga_code', array(
48
+ 'data' => array(
49
+ Ga_Admin::GA_WEB_PROPERTY_ID_OPTION_NAME => $web_property_id
50
+ )
51
+ ) );
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Gets and returns Web Property Id.
57
+ *
58
+ * @return string Web Property Id
59
+ */
60
+ private static function get_web_property_id() {
61
+ $web_property_id = get_option( Ga_Admin::GA_WEB_PROPERTY_ID_OPTION_NAME );
62
+ if ( Ga_Helper::is_code_manually_enabled() ) {
63
+ $web_property_id = get_option( Ga_Admin::GA_WEB_PROPERTY_ID_MANUALLY_VALUE_OPTION_NAME );
64
+ }
65
+
66
+ return $web_property_id;
67
+ }
68
+ }
class/Ga_Helper.php ADDED
@@ -0,0 +1,399 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ga_Helper {
4
+
5
+ const ROLE_ID_PREFIX = "role-id-";
6
+
7
+ const GA_DEFAULT_WEB_ID = "UA-0000000-0";
8
+
9
+ const GA_STATISTICS_PAGE_URL = "admin.php?page=googleanalytics";
10
+
11
+ const GA_SETTINGS_PAGE_URL = "admin.php?page=googleanalytics/settings";
12
+
13
+ const DASHBOARD_PAGE_NAME = "dashboard";
14
+
15
+ /**
16
+ * Init plugin actions.
17
+ *
18
+ */
19
+ public static function init() {
20
+ if ( ! is_admin() ) {
21
+ Ga_Frontend::add_actions();
22
+ }
23
+
24
+ if ( is_admin() ) {
25
+ Ga_Admin::add_filters();
26
+ Ga_Admin::add_actions();
27
+ Ga_Admin::init_oauth();
28
+ Ga_Admin::handle_actions();
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Checks if current page is a WordPress dashboard.
34
+ * @return int
35
+ */
36
+ public static function is_plugin_page() {
37
+ $site = get_current_screen();
38
+
39
+ return preg_match( '/' . GA_NAME . '/', $site->base );
40
+ }
41
+
42
+ /**
43
+ * Checks if current page is a WordPress dashboard.
44
+ * @return number
45
+ */
46
+ public static function is_dashboard_page() {
47
+ $site = get_current_screen();
48
+
49
+ return preg_match( '/' . self::DASHBOARD_PAGE_NAME . '/', $site->base );
50
+ }
51
+
52
+ /**
53
+ * Check whether the plugin is configured.
54
+ *
55
+ * @param String $web_id
56
+ *
57
+ * @return boolean
58
+ */
59
+ public static function is_configured( $web_id ) {
60
+ return $web_id !== self::GA_DEFAULT_WEB_ID;
61
+ }
62
+
63
+ /**
64
+ * Prepare an array of current site's user roles
65
+ *
66
+ * return array
67
+ */
68
+ public static function get_user_roles() {
69
+ global $wp_roles;
70
+ if ( ! isset( $wp_roles ) ) {
71
+ $wp_roles = new WP_Roles();
72
+ }
73
+
74
+ return $wp_roles->get_names();
75
+ }
76
+
77
+ /**
78
+ * Prepare a role ID.
79
+ *
80
+ * The role ID is derived from the role's name and will be used
81
+ * in its setting name in the additional settings.
82
+ *
83
+ * @param string $role_name Role name
84
+ *
85
+ * @return string
86
+ */
87
+ public static function prepare_role_id( $role_name ) {
88
+ return self::ROLE_ID_PREFIX . strtolower( preg_replace( '/[\W]/', '-', before_last_bar( $role_name ) ) );
89
+ }
90
+
91
+ /**
92
+ * Prepares role id.
93
+ *
94
+ * @param $v
95
+ * @param $k
96
+ */
97
+ public static function prepare_role( &$v, $k ) {
98
+ $v = self::prepare_role_id( $v );
99
+ }
100
+
101
+ /**
102
+ * Checks whether user role is excluded from adding UA code.
103
+ *
104
+ * @return boolean
105
+ */
106
+ public static function can_add_ga_code() {
107
+ $current_user = wp_get_current_user();
108
+ $user_roles = ! empty( $current_user->roles ) ? $current_user->roles : array();
109
+ $exclude_roles = json_decode( get_option( Ga_Admin::GA_EXCLUDE_ROLES_OPTION_NAME ), true );
110
+
111
+ array_walk( $user_roles, 'Ga_Helper::prepare_role' );
112
+
113
+ $return = true;
114
+ foreach ( $user_roles as $role ) {
115
+ if ( ! empty( $exclude_roles[ $role ] ) ) {
116
+ $return = false;
117
+ break;
118
+ }
119
+ }
120
+
121
+ return $return;
122
+ }
123
+
124
+ /**
125
+ * Adds ga dashboard widget HTML code for a WordPress
126
+ * Dashboard widget hook.
127
+ */
128
+ public static function add_ga_dashboard_widget() {
129
+ echo self::get_ga_dashboard_widget();
130
+ }
131
+
132
+ /**
133
+ * Generates dashboard widget HTML code.
134
+ *
135
+ * @param string $date_range Google Analytics specific date range string.
136
+ * @param boolean $text_mode
137
+ * @param boolean $ajax
138
+ *
139
+ * @return null | string HTML dashboard widget code.
140
+ */
141
+ public static function get_ga_dashboard_widget( $date_range = null, $text_mode = false, $ajax = false ) {
142
+ if ( empty( $date_range ) ) {
143
+ $date_range = '30daysAgo';
144
+ }
145
+
146
+ // Get chart and boxes data
147
+ $data = self::get_dashboard_widget_data( $date_range );
148
+
149
+ if ( $text_mode ) {
150
+ return self::get_chart_page( 'ga_dashboard_widget' . ( $ajax ? "_ajax" : "" ), array(
151
+ 'chart' => $data['chart'],
152
+ 'boxes' => $data['boxes']
153
+ ) );
154
+ } else {
155
+ echo self::get_chart_page( 'ga_dashboard_widget' . ( $ajax ? "_ajax" : "" ), array(
156
+ 'chart' => $data['chart'],
157
+ 'boxes' => $data['boxes'],
158
+ 'more_details_url' => admin_url( self::GA_STATISTICS_PAGE_URL )
159
+ ) );
160
+ }
161
+
162
+ return null;
163
+ }
164
+
165
+ /**
166
+ * Generates JSON data string for AJAX calls.
167
+ *
168
+ * @param string $date_range
169
+ * @param string $metric
170
+ * @param boolean $text_mode
171
+ * @param boolean $ajax
172
+ *
173
+ * @return string|false Returns JSON data string
174
+ */
175
+ public static function get_ga_dashboard_widget_data_json( $date_range = null, $metric = null, $text_mode = false, $ajax = false ) {
176
+ if ( empty( $date_range ) ) {
177
+ $date_range = '30daysAgo';
178
+ }
179
+
180
+ if ( empty( $metric ) ) {
181
+ $metric = 'pageviews';
182
+ }
183
+
184
+ $data = self::get_dashboard_widget_data( $date_range, $metric );
185
+
186
+ return wp_json_encode( $data );
187
+ }
188
+
189
+ /**
190
+ * Gets dashboard widget data.
191
+ *
192
+ * @param date_range
193
+ * @param metric
194
+ *
195
+ * @return array Return chart and boxes data
196
+ */
197
+ private static function get_dashboard_widget_data( $date_range, $metric = null ) {
198
+ $selected = self::get_selected_account_data( true );
199
+
200
+ $query_params = Ga_Stats::get_query( 'main_chart', $selected['view_id'], $date_range, $metric );
201
+ $stats_data = Ga_Admin::api_client()->call( 'ga_api_data', array(
202
+
203
+ $query_params
204
+ ) );
205
+
206
+ $boxes_query = Ga_Stats::get_query( 'dashboard_boxes', $selected['view_id'], $date_range );
207
+ $boxes_data = Ga_Admin::api_client()->call( 'ga_api_data', array(
208
+
209
+ $boxes_query
210
+ ) );
211
+
212
+ $chart = ! empty( $stats_data ) ? Ga_Stats::get_dashboard_chart( $stats_data->getData() ) : array();
213
+ $boxes = ! empty( $boxes_data ) ? Ga_Stats::get_dashboard_boxes_data( $boxes_data->getData() ) : array();
214
+
215
+ return array(
216
+
217
+ 'chart' => $chart,
218
+ 'boxes' => $boxes
219
+ );
220
+ }
221
+
222
+ public static function is_account_selected() {
223
+ return self::get_selected_account_data();
224
+ }
225
+
226
+ /**
227
+ * Returns HTML code of the chart page or a notice.
228
+ *
229
+ * @param chart
230
+ *
231
+ * @return string Returns HTML code
232
+ */
233
+ public static function get_chart_page( $view, $params ) {
234
+
235
+ $message = sprintf( __( 'Statistics can only be seen after you authenticate with your Google account on the <a href="%s">Settings page</a>.' ), admin_url( self::GA_SETTINGS_PAGE_URL ) );
236
+
237
+ if ( self::is_authorized() && ! self::is_code_manually_enabled() ) {
238
+ if ( self::is_account_selected() ) {
239
+ if ( $params ) {
240
+ return Ga_View::load( $view, $params, true );
241
+ } else {
242
+ return self::ga_oauth_notice( sprintf( 'Please configure your <a href="%s">Google Analytics settings</a>.', admin_url( self::GA_SETTINGS_PAGE_URL ) ) );
243
+ }
244
+ } else {
245
+ return self::ga_oauth_notice( $message );
246
+ }
247
+ } else {
248
+ return self::ga_oauth_notice( $message );
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Checks whether users is authorized with Google.
254
+ *
255
+ * @return boolean
256
+ */
257
+ public static function is_authorized() {
258
+ return Ga_Admin::api_client()->get_instance()->is_authorized();
259
+ }
260
+
261
+ /**
262
+ * Wrapper for WordPress method get_option
263
+ *
264
+ * @param string $name Option name
265
+ *
266
+ * @return NULL|mixed|boolean
267
+ */
268
+ public static function get_option( $name ) {
269
+ $opt = get_option( $name );
270
+
271
+ return ! empty( $opt ) ? $opt : null;
272
+ }
273
+
274
+ /**
275
+ * Wrapper for WordPress method update_option
276
+ *
277
+ * @param string $name
278
+ * @param mixed $value
279
+ *
280
+ * @return NULL|boolean
281
+ */
282
+ public static function update_option( $name, $value ) {
283
+ $opt = update_option( $name, $value );
284
+
285
+ return ! empty( $opt ) ? $opt : null;
286
+ }
287
+
288
+ /**
289
+ * Loads ga notice HTML code with gicen message included.
290
+ *
291
+ * @param string $message
292
+ *
293
+ * @return string
294
+ */
295
+ public static function ga_oauth_notice( $message ) {
296
+ return Ga_View::load( 'ga_oauth_notice', array(
297
+
298
+ 'msg' => $message
299
+ ), true );
300
+ }
301
+
302
+ /**
303
+ * Gets data according to selected GA account.
304
+ *
305
+ * @param boolean $assoc
306
+ *
307
+ * @return mixed
308
+ */
309
+ public static function get_selected_account_data( $assoc = false ) {
310
+ $data = json_decode( self::get_option( Ga_Admin::GA_SELECTED_ACCOUNT ) );
311
+ $data = ( ! empty( $data ) && count( $data ) == 3 ) ? $data : false;
312
+
313
+ if ( $data ) {
314
+ if ( $assoc ) {
315
+ return array(
316
+
317
+ 'account_id' => $data[0],
318
+ 'web_property_id' => $data[1],
319
+ 'view_id' => $data[2]
320
+ );
321
+ } else {
322
+ return $data;
323
+ }
324
+ }
325
+
326
+ return false;
327
+ }
328
+
329
+ /**
330
+ * Chekcs whether option for manually UA-code
331
+ * @return NULL|mixed|boolean
332
+ */
333
+ public static function is_code_manually_enabled() {
334
+ return Ga_Helper::get_option( Ga_Admin::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME );
335
+ }
336
+
337
+ /**
338
+ * Adds percent sign to the given text.
339
+ *
340
+ * @param $text
341
+ *
342
+ * @return string
343
+ */
344
+ public static function format_percent( $text ) {
345
+ $text = self::add_plus( $text );
346
+ return $text . '%';
347
+ }
348
+
349
+ /**
350
+ * Adds plus sign before number.
351
+ *
352
+ * @param $number
353
+ *
354
+ * @return string
355
+ */
356
+ public static function add_plus( $number ) {
357
+ if ( $number > 0 ){
358
+ return '+' . $number;
359
+ }
360
+ return $number;
361
+ }
362
+
363
+ /**
364
+ * Check whether current user has administrator privileges.
365
+ *
366
+ * @return bool
367
+ */
368
+ public static function is_administrator() {
369
+ if ( current_user_can( 'administrator' ) ) {
370
+ return true;
371
+ }
372
+
373
+ return false;
374
+ }
375
+
376
+ public static function is_wp_version_valid() {
377
+ $wp_version = get_bloginfo( 'version' );
378
+
379
+ return version_compare( $wp_version, Ga_Admin::MIN_WP_VERSION, 'ge' );
380
+ }
381
+
382
+ /**
383
+ * Check if terms are accepted
384
+ *
385
+ * @return bool
386
+ */
387
+ public static function are_terms_accepted() {
388
+ return self::get_option( Ga_Admin::GA_SHARETHIS_TERMS_OPTION_NAME );
389
+ }
390
+
391
+ /**
392
+ * Check if sharethis scripts enabled
393
+ *
394
+ * @return bool
395
+ */
396
+ public static function is_sharethis_included() {
397
+ return GA_SHARETHIS_SCRIPTS_INCLUDED;
398
+ }
399
+ }
class/Ga_Hook.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ga_Hook {
4
+
5
+ /**
6
+ * Adds WordPress hooks.
7
+ *
8
+ * @param string $plugin_file_path
9
+ */
10
+ public static function add_hooks( $plugin_file_path ) {
11
+ register_activation_hook( $plugin_file_path, 'Ga_Admin::activate_googleanalytics' );
12
+ register_deactivation_hook( $plugin_file_path, 'Ga_Admin::deactivate_googleanalytics' );
13
+ register_uninstall_hook( $plugin_file_path, 'Ga_Admin::uninstall_googleanalytics' );
14
+ }
15
+ }
class/Ga_Stats.php ADDED
@@ -0,0 +1,701 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Ga_Stats class
5
+ *
6
+ * Preparing request and parsing response from Google Analytics Reporting Api
7
+ *
8
+ * @author wle@adips.com
9
+ * @version 1.0
10
+ */
11
+ class Ga_Stats {
12
+
13
+ /**
14
+ * Preparing query to get Analytics data
15
+ *
16
+ * @param string $query query type
17
+ * @param int $id_view The Analytics view ID from which to retrieve data.
18
+ * @param string $date_range The start date for the query in the format YYYY-MM-DD or '7daysAgo'
19
+ * @param string $metric A metric expression
20
+ *
21
+ * @return array Request query
22
+ */
23
+ public static function get_query( $query, $id_view, $date_range = null, $metric = null ) {
24
+ if ( $query == 'main_chart' ) {
25
+ return self::main_chart_query( $id_view, $date_range, $metric );
26
+ } elseif ( $query == 'boxes' ) {
27
+ return self::boxes_query( $id_view );
28
+ } elseif ( $query == 'dashboard_boxes' ) {
29
+ return self::dashboard_boxes_query( $id_view, $date_range );
30
+ } elseif ( $query == 'sources' ) {
31
+ return self::sources_query( $id_view );
32
+ } else {
33
+ return array();
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Preparing query for top traffic sources table
39
+ *
40
+ * @param int $id_view The Analytics view ID from which to retrieve data.
41
+ *
42
+ * @return array Sources query
43
+ */
44
+ public static function sources_query( $id_view ) {
45
+ $reports_requests = array();
46
+ $reports_requests[] = array(
47
+ 'viewId' => $id_view,
48
+ 'dateRanges' => self::set_date_ranges( '7daysAgo', 'yesterday' ),
49
+ 'metrics' => self::set_metrics( array( 'ga:pageviews' ) ),
50
+ 'includeEmptyRows' => true,
51
+ 'pageSize' => 5,
52
+ 'dimensions' => self::set_dimensions( 'ga:sourceMedium' ),
53
+ 'orderBys' => self::set_order_bys( 'ga:pageviews', 'DESCENDING' ),
54
+ );
55
+ $query = array(
56
+ 'reportRequests' => $reports_requests
57
+ );
58
+
59
+ return $query;
60
+ }
61
+
62
+ /**
63
+ * Preparing query for dashbord boxes
64
+ *
65
+ * @param int $id_view The Analytics view ID from which to retrieve data.
66
+ * @param string $date_range The start date for the query in the format YYYY-MM-DD or '7daysAgo'
67
+ *
68
+ * @return array Dashboard boxes query
69
+ */
70
+ public static function dashboard_boxes_query( $id_view, $date_range ) {
71
+ $reports_requests = array();
72
+ $reports_requests[] = array(
73
+ 'viewId' => $id_view,
74
+ 'dateRanges' => self::set_date_ranges( $date_range, 'yesterday' ),
75
+ 'metrics' => self::set_metrics( array(
76
+ 'ga:sessions',
77
+ 'ga:pageviews',
78
+ 'ga:pageviewsPerSession',
79
+ 'ga:BounceRate',
80
+ 'ga:avgTimeOnPage',
81
+ 'ga:percentNewSessions',
82
+ ) ),
83
+ 'includeEmptyRows' => true,
84
+ 'dimensions' => self::set_dimensions( 'ga:date' )
85
+ );
86
+ $query = array(
87
+ 'reportRequests' => $reports_requests
88
+ );
89
+
90
+ return $query;
91
+ }
92
+
93
+ /**
94
+ * Preparing query for stats boxes
95
+ *
96
+ * @param int $id_view The Analytics view ID from which to retrieve data.
97
+ *
98
+ * @return array Boxes query
99
+ */
100
+ public static function boxes_query( $id_view ) {
101
+ $reports_requests = array();
102
+ $reports_requests[] = array(
103
+ 'viewId' => $id_view,
104
+ 'dateRanges' => self::set_date_ranges( '7daysAgo', 'yesterday', '14daysAgo', '8daysAgo' ),
105
+ 'metrics' => self::set_metrics( array(
106
+ 'ga:users',
107
+ 'ga:pageviews',
108
+ 'ga:pageviewsPerSession',
109
+ 'ga:BounceRate'
110
+ ) ),
111
+ 'includeEmptyRows' => true,
112
+ 'dimensions' => self::set_dimensions( 'ga:date' )
113
+ );
114
+ $query = array(
115
+ 'reportRequests' => $reports_requests
116
+ );
117
+
118
+ return $query;
119
+ }
120
+
121
+ /**
122
+ * Preparing query for chart
123
+ *
124
+ * @param int $id_view The Analytics view ID from which to retrieve data.
125
+ * @param string $date_range The start date for the query in the format YYYY-MM-DD or '7daysAgo'
126
+ * @param string $metric A metric expression
127
+ *
128
+ * @return array Chart query
129
+ */
130
+ public static function main_chart_query( $id_view, $date_range = null, $metric = null ) {
131
+ if ( empty( $date_range ) ) {
132
+ $date_ranges = self::set_date_ranges( '7daysAgo', 'yesterday', '14daysAgo', '8daysAgo' );
133
+ } else {
134
+ $date_ranges = self::set_date_ranges( $date_range, 'yesterday' );
135
+ }
136
+
137
+ if ( empty( $metric ) ) {
138
+ $metric = 'ga:pageviews';
139
+ } else {
140
+ $metric = 'ga:' . $metric;
141
+ }
142
+
143
+ $reports_requests = array();
144
+ $reports_requests[] = array(
145
+ 'viewId' => $id_view,
146
+ 'dateRanges' => $date_ranges,
147
+ 'metrics' => self::set_metrics( $metric ),
148
+ 'includeEmptyRows' => true,
149
+ 'dimensions' => self::set_dimensions( 'ga:date' )
150
+ );
151
+ $query = array(
152
+ 'reportRequests' => $reports_requests
153
+ );
154
+
155
+ return $query;
156
+ }
157
+
158
+ /**
159
+ * Setting order for requests
160
+ *
161
+ * @param string $name The field which to sort by. The default sort order is ascending. Example: ga:browser.
162
+ * @param string $sort The sorting order for the field. 'ASCENDING' or 'DESCENDING'
163
+ *
164
+ * @return array OrderBys
165
+ */
166
+ public static function set_order_bys( $name, $sort ) {
167
+ $order = array();
168
+ $order[] = array(
169
+ 'fieldName' => $name,
170
+ 'sortOrder' => $sort,
171
+ );
172
+
173
+ return $order;
174
+ }
175
+
176
+ /**
177
+ * Setting metrics for requests
178
+ *
179
+ * @param mixed $expression A metric expression or array of expressions
180
+ *
181
+ * @return array Metrics
182
+ */
183
+ public static function set_metrics( $expression ) {
184
+ $metrics = array();
185
+ if ( is_array( $expression ) ) {
186
+ foreach ( $expression as $exp ) {
187
+ $metrics[] = array(
188
+ 'expression' => $exp
189
+ );
190
+ }
191
+ } else {
192
+ $metrics[] = array(
193
+ 'expression' => $expression
194
+ );
195
+ }
196
+
197
+ return $metrics;
198
+ }
199
+
200
+ /**
201
+ * Setting dimensions for requests
202
+ *
203
+ * @param string $name Name of the dimension to fetch, for example ga:browser.
204
+ *
205
+ * @return array Dimensions
206
+ */
207
+ public static function set_dimensions( $name ) {
208
+ $dimensions = array();
209
+ $dimensions[] = array(
210
+ 'name' => $name
211
+ );
212
+
213
+ return $dimensions;
214
+ }
215
+
216
+ /**
217
+ * Setting date ranges for requests
218
+ *
219
+ * @param string $start_date The start date for the query in the format YYYY-MM-DD.
220
+ * @param string $end_date The end date for the query in the format YYYY-MM-DD.
221
+ * @param string $prev_start_date The start date (second range) for the query in the format YYYY-MM-DD.
222
+ * @param string $prev_end_date The start date (second range) for the query in the format YYYY-MM-DD.
223
+ *
224
+ * @return array Date ranges
225
+ */
226
+ public static function set_date_ranges( $start_date, $end_date, $prev_start_date = '', $prev_end_date = '' ) {
227
+ $date_danges = array();
228
+ $date_danges[] = array(
229
+ 'startDate' => $start_date,
230
+ 'endDate' => $end_date
231
+ );
232
+ if ( ! empty( $prev_start_date ) and ! empty( $prev_end_date ) ) {
233
+ $date_danges[] = array(
234
+ 'startDate' => $prev_start_date,
235
+ 'endDate' => $prev_end_date
236
+ );
237
+ }
238
+
239
+ return $date_danges;
240
+ }
241
+
242
+ /**
243
+ * Preparing response for data received from analytics
244
+ *
245
+ * @param array $data Analytics response
246
+ *
247
+ * @return array Response rows
248
+ */
249
+ public static function prepare_response( $data ) {
250
+ $data = self::get_reports_from_response( $data );
251
+ self::handle_more_reports( $data );
252
+ $report = self::get_single_report( $data );
253
+ self::get_report_column_header( $report );
254
+ $report_data = self::get_report_data( $report );
255
+ self::get_totals( $report_data );
256
+ self::get_row_count( $report_data );
257
+ $rows = self::get_rows( $report_data );
258
+
259
+ return $rows;
260
+ }
261
+
262
+ /**
263
+ * Get dimensions from response row
264
+ *
265
+ * @param array $row Analytics response row
266
+ *
267
+ * @return array Dimensions
268
+ */
269
+ public static function get_dimensions( $row ) {
270
+ if ( ! empty( $row['dimensions'] ) ) {
271
+ return $row['dimensions'];
272
+ }
273
+
274
+ return false;
275
+ }
276
+
277
+ /**
278
+ * Get metrics from response row
279
+ *
280
+ * @param array $row Analytics response row
281
+ *
282
+ * @return array Metrics
283
+ */
284
+ public static function get_metrics( $row ) {
285
+ if ( ! empty( $row['metrics'] ) ) {
286
+ return $row['metrics'];
287
+ }
288
+
289
+ return false;
290
+ }
291
+
292
+ /**
293
+ * Get row from response report data
294
+ *
295
+ * @param array $report_data Analytics response report data
296
+ *
297
+ * @return array Rows
298
+ */
299
+ public static function get_rows( $report_data ) {
300
+ if ( ! empty( $report_data['rows'] ) ) {
301
+ return $report_data['rows'];
302
+ }
303
+
304
+ return false;
305
+ }
306
+
307
+ /**
308
+ * Get row count from response report data
309
+ *
310
+ * @param array $report_data Analytics response report data
311
+ *
312
+ * @return array Row count
313
+ */
314
+ public static function get_row_count( $report_data ) {
315
+ if ( ! empty( $report_data['rowCount'] ) ) {
316
+ return $report_data['rowCount'];
317
+ }
318
+
319
+ return false;
320
+ }
321
+
322
+ /**
323
+ * Get totals from response report data
324
+ *
325
+ * @param array $report_data Analytics response report data
326
+ *
327
+ * @return array Totals
328
+ */
329
+ public static function get_totals( $report_data ) {
330
+ if ( ! empty( $report_data['totals'] ) ) {
331
+ return $report_data['totals'];
332
+ }
333
+
334
+ return false;
335
+ }
336
+
337
+ /**
338
+ * Get reports from response data
339
+ *
340
+ * @param array $data Analytics response data
341
+ *
342
+ * @return array Reports
343
+ */
344
+ public static function get_reports_from_response( $data ) {
345
+ if ( ! empty( $data['reports'] ) ) {
346
+ return $data['reports'];
347
+ }
348
+
349
+ return false;
350
+ }
351
+
352
+ /**
353
+ * Show info for multiple data
354
+ *
355
+ * @param array $data Analytics response data
356
+ *
357
+ */
358
+ public static function handle_more_reports( $data ) {
359
+ if ( count( $data ) > 1 ) {
360
+ echo 'more than one report';
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Show info for multiple rows
366
+ *
367
+ * @param array $rows Analytics response rows
368
+ *
369
+ */
370
+ public static function handle_more_rows( $rows ) {
371
+ if ( count( $rows ) > 1 ) {
372
+ echo 'more than one row';
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Get single report from response data
378
+ *
379
+ * @param array $data Analytics response data
380
+ *
381
+ * @return array Report
382
+ */
383
+ public static function get_single_report( $data ) {
384
+ if ( ! empty( $data ) ) {
385
+ foreach ( $data as $report ) {
386
+ if ( ! empty( $report ) ) {
387
+ return $report;
388
+ }
389
+ }
390
+ }
391
+
392
+ return false;
393
+ }
394
+
395
+ /**
396
+ * Get single row from response data rows
397
+ *
398
+ * @param array $rows Analytics response data rows
399
+ *
400
+ * @return array Row
401
+ */
402
+ public static function get_single_row( $rows ) {
403
+ if ( ! empty( $rows ) ) {
404
+ foreach ( $rows as $row ) {
405
+ if ( ! empty( $row ) ) {
406
+ return $row;
407
+ }
408
+ }
409
+ }
410
+
411
+ return false;
412
+ }
413
+
414
+ /**
415
+ * Get column header from response data
416
+ *
417
+ * @param array $data Analytics response data
418
+ *
419
+ * @return array Column header
420
+ */
421
+ public static function get_report_column_header( $data ) {
422
+ if ( ! empty( $data['columnHeader'] ) ) {
423
+ return $data['columnHeader'];
424
+ }
425
+
426
+ return false;
427
+ }
428
+
429
+ /**
430
+ * Get report data from response data
431
+ *
432
+ * @param array $data Analytics response data
433
+ *
434
+ * @return array data
435
+ */
436
+ public static function get_report_data( $data ) {
437
+ if ( ! empty( $data['data'] ) ) {
438
+ return $data['data'];
439
+ }
440
+
441
+ return false;
442
+ }
443
+
444
+ /**
445
+ * Get chart from response data
446
+ *
447
+ * @param array $data Analytics response data
448
+ *
449
+ * @return array chart data
450
+ */
451
+ public static function get_chart( $data ) {
452
+ $chart_data = array();
453
+ if ( ! empty( $data ) ) {
454
+ $data = $data['reports'][0]['data'];
455
+ $rows = $data['rows'];
456
+ if ( ! empty( $rows ) ) {
457
+ foreach ( $rows as $key => $row ) {
458
+ if ( $key < 7 ) {
459
+ $chart_data[ $key ]['previous'] = ! empty( $row['metrics'][1]['values'][0] ) ? $row['metrics'][1]['values'][0] : 0;
460
+ $chart_data[ $key ]['previous-day'] = date( 'M j', strtotime( $row['dimensions'][0] ) );
461
+ } else {
462
+ $chart_data[ $key - 7 ]['day'] = date( 'M j', strtotime( $row['dimensions'][0] ) );
463
+ $chart_data[ $key - 7 ]['current'] = ! empty( $row['metrics'][0]['values'][0] ) ? $row['metrics'][0]['values'][0] : 0;
464
+ $chart_data[ 'date'] = strtotime( $row['dimensions'][0] );
465
+ }
466
+ }
467
+ }
468
+ }
469
+
470
+ return $chart_data;
471
+ }
472
+
473
+ /**
474
+ * Get dasboard chart from response data
475
+ *
476
+ * @param array $data Analytics response data
477
+ *
478
+ * @return array dashboard chart data
479
+ */
480
+ public static function get_dashboard_chart( $data ) {
481
+ $chart_data = array();
482
+ if ( ! empty( $data ) ) {
483
+ $data = $data['reports'][0]['data'];
484
+ $rows = $data['rows'];
485
+
486
+ if ( $rows ) {
487
+ foreach ( $rows as $row ) {
488
+ $chart_data[] = array(
489
+ 'day' => date( 'M j', strtotime( $row['dimensions'][0] ) ),
490
+ 'current' => ! empty( $row['metrics'][0]['values'][0] ) ? $row['metrics'][0]['values'][0] : 0
491
+ );
492
+ }
493
+ }
494
+ }
495
+
496
+ return $chart_data;
497
+ }
498
+
499
+ /**
500
+ * Get boxes from response data
501
+ *
502
+ * @param array $data Analytics response data
503
+ *
504
+ * @return array boxes data
505
+ */
506
+ public static function get_boxes( $data ) {
507
+ if ( ! empty( $data ) ) {
508
+ $data = self::get_reports_from_response( $data );
509
+ self::handle_more_reports( $data );
510
+ $report = self::get_single_report( $data );
511
+ self::get_report_column_header( $report );
512
+ $report_data = self::get_report_data( $report );
513
+ $totals = self::get_totals( $report_data );
514
+
515
+ return self::get_boxes_from_totals( $totals );
516
+ }
517
+ }
518
+
519
+ /**
520
+ * Get boxes from totals
521
+ *
522
+ * @param array $totals Analytics response totals
523
+ *
524
+ * @return array boxes data
525
+ */
526
+ public static function get_boxes_from_totals( $totals ) {
527
+ if ( ! empty( $totals ) ) {
528
+ $boxes_data = array();
529
+ foreach ( $totals as $key => $total ) {
530
+ if ( $key == 0 ) {
531
+ $boxes_data['Users']['current'] = $total['values'][0];
532
+ $boxes_data['Pageviews']['current'] = $total['values'][1];
533
+ $boxes_data['PageviewsPerSession']['current'] = $total['values'][2];
534
+ $boxes_data['BounceRate']['current'] = round( $total['values'][3], 2 ) . '%';
535
+ } else {
536
+ $boxes_data['Users']['previous'] = $total['values'][0];
537
+ $boxes_data['Pageviews']['previous'] = $total['values'][1];
538
+ $boxes_data['PageviewsPerSession']['previous'] = $total['values'][2];
539
+ $boxes_data['BounceRate']['previous'] = round( $total['values'][3], 2 ) . '%';
540
+ }
541
+ }
542
+
543
+ return self::prepare_boxes( $boxes_data );
544
+ }
545
+
546
+ return false;
547
+ }
548
+
549
+ /**
550
+ * Prepare boxes data
551
+ *
552
+ * @param array $boxes_data Boxes data
553
+ *
554
+ * @return array boxes data
555
+ */
556
+ public static function prepare_boxes( $boxes_data ) {
557
+ $boxes_data['Users']['diff'] = ( $boxes_data['Users']['previous'] > 0 ) ? round( ( $boxes_data['Users']['current'] - $boxes_data['Users']['previous'] ) / $boxes_data['Users']['previous'] * 100, 2 ) : 100;
558
+ $boxes_data['Pageviews']['diff'] = ( $boxes_data['Pageviews']['previous'] > 0 ) ? round( ( $boxes_data['Pageviews']['current'] - $boxes_data['Pageviews']['previous'] ) / $boxes_data['Pageviews']['previous'] * 100, 2 ) : 100;
559
+ $boxes_data['PageviewsPerSession']['diff'] = ( $boxes_data['PageviewsPerSession']['previous'] > 0 ) ? round( ( $boxes_data['PageviewsPerSession']['current'] - $boxes_data['PageviewsPerSession']['previous'] ) / $boxes_data['PageviewsPerSession']['previous'] * 100, 2 ) : 100;
560
+ $boxes_data['BounceRate']['diff'] = ( $boxes_data['BounceRate']['previous'] > 0 ) ? round( ( $boxes_data['BounceRate']['current'] - $boxes_data['BounceRate']['previous'] ) / $boxes_data['BounceRate']['previous'] * 100, 2 ) : 100;
561
+ $boxes_data['BounceRate']['diff'] = ( $boxes_data['BounceRate']['previous'] == 0 && $boxes_data['BounceRate']['current'] == 0) ? 0 : $boxes_data['BounceRate']['diff'];
562
+ $boxes_data['Users']['label'] = 'Users';
563
+ $boxes_data['Pageviews']['label'] = 'Pageviews';
564
+ $boxes_data['PageviewsPerSession']['label'] = 'Pages / Session';
565
+ $boxes_data['BounceRate']['label'] = 'Bounce Rate';
566
+ $boxes_data['Users']['comparison'] = $boxes_data['Users']['current'] . ' vs ' . $boxes_data['Users']['previous'];
567
+ $boxes_data['Pageviews']['comparison'] = $boxes_data['Pageviews']['current'] . ' vs ' . $boxes_data['Pageviews']['previous'];
568
+ $boxes_data['PageviewsPerSession']['comparison'] = self::number_format_clean( $boxes_data['PageviewsPerSession']['current'], 2, '.', ',' ) . ' vs ' . self::number_format_clean( $boxes_data['PageviewsPerSession']['previous'], 2, '.', ',' );
569
+ $boxes_data['BounceRate']['comparison'] = self::number_format_clean( $boxes_data['BounceRate']['current'], 2, '.', ',' ) . '% vs ' . self::number_format_clean( $boxes_data['BounceRate']['previous'], 2, '.', ',' ) . '%';
570
+ $boxes_data['Users']['color'] = ( $boxes_data['Users']['diff'] > 0 ) ? 'green' : 'red';
571
+ $boxes_data['Pageviews']['color'] = ( $boxes_data['Pageviews']['diff'] > 0 ) ? 'green' : 'red';
572
+ $boxes_data['PageviewsPerSession']['color'] = ( $boxes_data['PageviewsPerSession']['diff'] > 0 ) ? 'green' : 'red';
573
+ $boxes_data['BounceRate']['color'] = ( $boxes_data['BounceRate']['diff'] > 0 ) ? 'red' : 'green';
574
+ $boxes_data['Users']['color'] = ( $boxes_data['Users']['diff'] != 0 ) ? $boxes_data['Users']['color'] : 'black';
575
+ $boxes_data['Pageviews']['color'] = ( $boxes_data['Pageviews']['diff'] != 0 ) ? $boxes_data['Pageviews']['color'] : 'black';
576
+ $boxes_data['PageviewsPerSession']['color'] = ( $boxes_data['PageviewsPerSession']['diff'] != 0 ) ? $boxes_data['PageviewsPerSession']['color'] : 'black';
577
+ $boxes_data['BounceRate']['color'] = ( $boxes_data['BounceRate']['diff'] != 0 ) ? $boxes_data['BounceRate']['color'] : 'black';
578
+
579
+ return $boxes_data;
580
+ }
581
+
582
+ /**
583
+ * Number format for boxes
584
+ *
585
+ * @param float $number Number to format
586
+ * @param int $precision Precision
587
+ * @param string $dec_point Decimal point
588
+ * @param string $thousands_sep Thousands Separator
589
+ *
590
+ * @return string clean number format
591
+ */
592
+ public static function number_format_clean( $number, $precision = 0, $dec_point = '.', $thousands_sep = ',' ) {
593
+ if ( $number == 0 ) {
594
+ return 0;
595
+ } else {
596
+ $format = number_format( $number, $precision, $dec_point, $thousands_sep );
597
+ if ( substr( $format, 2 ) == '.00' ) {
598
+ return substr( $format, 0, - 3 );
599
+ }
600
+
601
+ return $format;
602
+ }
603
+ }
604
+
605
+ /**
606
+ * Get sources from analytics response data
607
+ *
608
+ * @param array $data Analytics response data
609
+ *
610
+ * @return array sources data
611
+ */
612
+ public static function get_sources( $data ) {
613
+ if ( ! empty( $data ) ) {
614
+ $data = self::get_reports_from_response( $data );
615
+ self::handle_more_reports( $data );
616
+ $report = self::get_single_report( $data );
617
+ self::get_report_column_header( $report );
618
+ $report_data = self::get_report_data( $report );
619
+ $rows = self::get_rows( $report_data );
620
+ $totals = self::get_totals( $report_data );
621
+ if ( ! empty( $totals ) ) {
622
+ $totalCount = array();
623
+ foreach ( $totals as $key => $total ) {
624
+ $totalCount = $total['values'][0];
625
+ }
626
+ }
627
+ $sources = array(
628
+ 'total' => $totalCount,
629
+ 'sum' => 0,
630
+ 'rows' => array(),
631
+ );
632
+ if ( ! empty( $rows ) ) {
633
+ $i = 1;
634
+ foreach ( $rows as $row ) {
635
+ if ( ! empty( $row ) ) {
636
+ foreach ( $row as $key => $value ) {
637
+ if ( $key == 'dimensions' ) {
638
+ $sources['rows'][ $i ]['name'] = $value[0];
639
+ $sources['rows'][ $i ]['url'] = 'http://' . substr( $value[0], 0, strpos( $value[0], '/' ) - 1 );
640
+ } elseif ( $key == 'metrics' ) {
641
+ $sources['rows'][ $i ]['number'] = $value[0]['values'][0];
642
+ $sources['rows'][ $i ]['percent'] = ( ! empty( $totalCount ) ) ? round( $value[0]['values'][0] / $totalCount * 100, 2 ) : 0;
643
+ $sources['sum'] += $value[0]['values'][0];
644
+ }
645
+ }
646
+ $i ++;
647
+ }
648
+ }
649
+ }
650
+
651
+ return $sources;
652
+ }
653
+
654
+ return false;
655
+ }
656
+
657
+ /**
658
+ * Get dashboard boxes data from analytics response data
659
+ *
660
+ * @param array $data Analytics response data
661
+ *
662
+ * @return array dashboard boxes data
663
+ */
664
+ public static function get_dashboard_boxes_data( $data ) {
665
+ if ( ! empty( $data ) ) {
666
+ $data = self::get_reports_from_response( $data );
667
+ self::handle_more_reports( $data );
668
+ $report = self::get_single_report( $data );
669
+ self::get_report_column_header( $report );
670
+ $report_data = self::get_report_data( $report );
671
+ $totals = self::get_totals( $report_data );
672
+ $boxes_data = array();
673
+ $boxes_data['Sessions'] = array(
674
+ 'label' => 'Visits',
675
+ 'value' => $totals[0]['values'][0],
676
+ );
677
+ $boxes_data['Pageviews'] = array(
678
+ 'label' => 'Pageviews',
679
+ 'value' => $totals[0]['values'][1],
680
+ );
681
+ $boxes_data['pageviewsPerSession'] = array(
682
+ 'label' => 'Pages / Visit',
683
+ 'value' => self::number_format_clean( $totals[0]['values'][2], 2, '.', ',' ),
684
+ );
685
+ $boxes_data['BounceRate'] = array(
686
+ 'label' => 'Bounce Rate',
687
+ 'value' => self::number_format_clean( $totals[0]['values'][3], 2, '.', ',' ) . '%',
688
+ );
689
+ $boxes_data['avgTimeOnPage'] = array(
690
+ 'label' => 'Avg. Time on Site',
691
+ 'value' => gmdate("H:i:s", $totals[0]['values'][4]),
692
+ );
693
+ $boxes_data['percentNewSessions'] = array(
694
+ 'label' => '% of New Visits',
695
+ 'value' => self::number_format_clean( $totals[0]['values'][5], 2, '.', ',' ),
696
+ );
697
+
698
+ return $boxes_data;
699
+ }
700
+ }
701
+ }
class/Ga_View.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ga_View {
4
+
5
+ /**
6
+ * Name of the view folder.
7
+ */
8
+ const PATH = 'view';
9
+
10
+ /**
11
+ * Loads given view file and it's data.
12
+ * Displays view or returns HTML code.
13
+ *
14
+ * @param string $view Filename
15
+ * @param array $data Data array
16
+ * @param bool $html Whether to display or return HTML code.
17
+ *
18
+ * @return string
19
+ */
20
+ public static function load( $view, $data_array = array(), $html = false ) {
21
+ if ( ! empty( $view ) ) {
22
+ foreach ( $data_array as $k => $v ) {
23
+ $$k = $v;
24
+ }
25
+ ob_start();
26
+ include GA_PLUGIN_DIR . "/" . self::PATH . "/" . $view . ".php";
27
+ if ( $html ) {
28
+ return ob_get_clean();
29
+ } else {
30
+ echo ob_get_clean();
31
+ }
32
+ }
33
+ }
34
+
35
+ // private static function buffer($buffer) {
36
+ // $data = $data_array;
37
+ // return $buffer;
38
+ // }
39
+ }
css/bootstrap.min.css ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * Bootstrap v3.3.7 (http://getbootstrap.com)
3
+ * Copyright 2011-2016 Twitter, Inc.
4
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5
+ */
6
+
7
+ /*!
8
+ * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=ddbeb97adad43a942be618a7956c300d)
9
+ * Config saved to config.json and https://gist.github.com/ddbeb97adad43a942be618a7956c300d
10
+ *//*!
11
+ * Bootstrap v3.3.7 (http://getbootstrap.com)
12
+ * Copyright 2011-2016 Twitter, Inc.
13
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
14
+ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:hover,a:focus{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{overflow-x:auto;min-height:0.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:focus,.btn-default.focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#fff;background-color:#398439;border-color:#255625}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#337ab7;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-left:15px;padding-right:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);-webkit-background-clip:padding-box;background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.clearfix:before,.clearfix:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.nav:before,.nav:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.container:after,.container-fluid:after,.row:after,.nav:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}
css/googleanalytics.css ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @CHARSET "UTF-8";
2
+
3
+ .wrap.ga-wrap {
4
+ margin: 0px auto !important;
5
+ margin-top: 10px !important;
6
+ margin-right: 0px !important;
7
+ }
8
+
9
+ .ga_container {
10
+ margin-top: 25px;
11
+ }
12
+
13
+ #exTab1, #exTab2 {
14
+ margin-right: 15px !important;
15
+ }
16
+
17
+ #exTab1 .tab-content {
18
+ color: white;
19
+ background-color: #428bca;
20
+ padding: 5px 15px;
21
+ }
22
+
23
+ #exTab2 h3 {
24
+ color: white;
25
+ background-color: #428bca;
26
+ padding: 5px 15px;
27
+ }
28
+
29
+ /* remove border radius for the tab */
30
+ #exTab1 .nav-pills > li > a {
31
+ border-radius: 0;
32
+ }
33
+
34
+ /* change border radius for the tab , apply corners on top*/
35
+ #exTab3 .nav-pills > li > a {
36
+ border-radius: 4px 4px 0 0;
37
+ }
38
+
39
+ #exTab3 .tab-content {
40
+ color: white;
41
+ background-color: #428bca;
42
+ padding: 5px 15px;
43
+ }
44
+
45
+ label.ga_checkbox_label {
46
+ margin-top: 6px !important;
47
+ }
48
+
49
+ .form-table th {
50
+ width: 250px !important;
51
+ }
52
+
53
+ .wrap.ga-notice {
54
+ width: 100% !important;
55
+ margin-left: 0px !important;
56
+ margin-right: 10px !important;
57
+ }
58
+
59
+ .ga_warning {
60
+ font-size: 12px;
61
+ font-weight: normal;
62
+ margin-top: 10px;
63
+ }
64
+
65
+ .ga-boxes-container {
66
+
67
+ }
68
+
69
+ .ga-box-row {
70
+ display: table;
71
+ width: 100%;
72
+ table-layout: fixed;
73
+ border-spacing: 10px;
74
+ }
75
+
76
+ .ga-box-column {
77
+ display: table-cell;
78
+ -moz-box-shadow: 0 0 5px #e5e5e5;
79
+ -webkit-box-shadow: 0 0 5px #e5e5e5;
80
+ box-shadow: 0px 0px 5px #e5e5e5;
81
+ }
82
+
83
+ .ga-box-dashboard {
84
+ border: 1px solid #cccccc;
85
+ border-radius: 0px;
86
+ padding: 3px;
87
+ text-align: center;
88
+ }
89
+
90
+ .ga-box-centered {
91
+ text-align: center;
92
+ }
93
+
94
+ .ga-loader-wrapper {
95
+ float: right;
96
+ margin-top: 4px;
97
+ margin-right: 5px;
98
+ }
99
+
100
+ .ga-loader-wrapper.stats-page {
101
+ width: 45px;
102
+ text-align: center;
103
+ margin: 0 auto;
104
+ float: none;
105
+ }
106
+
107
+ .ga-loader {
108
+ border: 4px solid #f3f3f3; /* Light grey */
109
+ border-top: 4px solid #3498db; /* Blue */
110
+ border-radius: 50%;
111
+ width: 15px;
112
+ height: 15px;
113
+ animation: spin 2s linear infinite;
114
+ display: none;
115
+ }
116
+
117
+ .ga-loader.stats-page-loader {
118
+ width: 45px;
119
+ height: 45px;
120
+ border-width: 6px;
121
+ }
122
+
123
+ @keyframes spin {
124
+ 0% {
125
+ transform: rotate(0deg);
126
+ }
127
+ 100% {
128
+ transform: rotate(360deg);
129
+ }
130
+ }
131
+
132
+ .ga-chart {
133
+ }
134
+
135
+ .label-grey{
136
+ color:#ccc;
137
+ }
138
+
139
+ .ga-tooltip {
140
+ position: relative;
141
+ cursor: not-allowed;
142
+ }
143
+
144
+ .ga-tooltip input[disabled] {
145
+ cursor: not-allowed;
146
+ }
147
+
148
+ .ga-tooltiptext {
149
+ background-color: #ffe692;
150
+ border: 1px solid #ffb900;
151
+ border-radius: 4px;
152
+ color: #444;
153
+ font-size: 12px;
154
+ font-weight: 500;
155
+ margin-left: 10px;
156
+ margin-top: 30px;
157
+ padding: 3px 10px;
158
+ position: relative;
159
+ white-space: nowrap;
160
+ text-align: center;
161
+ visibility: hidden;
162
+ width: auto;
163
+ z-index: 1;
164
+ }
165
+ .ga-tt-abs{
166
+ display: inline-block;
167
+ position: absolute;
168
+ margin-top: 0px;
169
+ }
170
+ .ga-tooltip:hover .ga-tooltiptext {
171
+ visibility: visible;
172
+ }
googleanalytics.php CHANGED
@@ -1,67 +1,47 @@
1
- <?php
2
- /*
3
- Plugin Name: Google Analytics
4
- Plugin URI: http://wordpress.org/extend/plugins/googleanalytics/
5
- Description: Enables <a href="http://www.google.com/analytics/">Google Analytics</a> on all pages.
6
- Version: 1.0.7
7
- Author: Kevin Sylvestre
8
- Author URI: http://ksylvest.com/
9
- */
10
-
11
- if (!defined('WP_CONTENT_URL'))
12
- define('WP_CONTENT_URL', get_option('siteurl').'/wp-content');
13
- if (!defined('WP_CONTENT_DIR'))
14
- define('WP_CONTENT_DIR', ABSPATH.'wp-content');
15
- if (!defined('WP_PLUGIN_URL'))
16
- define('WP_PLUGIN_URL', WP_CONTENT_URL.'/plugins');
17
- if (!defined('WP_PLUGIN_DIR'))
18
- define('WP_PLUGIN_DIR', WP_CONTENT_DIR.'/plugins');
19
-
20
- function activate_googleanalytics() {
21
- add_option('web_property_id', 'UA-0000000-0');
22
- }
23
-
24
- function deactive_googleanalytics() {
25
- delete_option('web_property_id');
26
- }
27
-
28
- function admin_init_googleanalytics() {
29
- register_setting('googleanalytics', 'web_property_id');
30
- }
31
-
32
- function admin_menu_googleanalytics() {
33
- add_options_page('Google Analytics', 'Google Analytics', 'manage_options', 'googleanalytics', 'options_page_googleanalytics');
34
- }
35
-
36
- function options_page_googleanalytics() {
37
- include(WP_PLUGIN_DIR.'/googleanalytics/options.php');
38
- }
39
-
40
- function googleanalytics() {
41
- $web_property_id = get_option('web_property_id');
42
- ?>
43
- <script>
44
- (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
45
- (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
46
- m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
47
- })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
48
-
49
- ga('create', '<?php echo $web_property_id ?>', 'auto');
50
- ga('send', 'pageview');
51
- </script>
52
- <?php
53
- }
54
-
55
- register_activation_hook(__FILE__, 'activate_googleanalytics');
56
- register_deactivation_hook(__FILE__, 'deactive_googleanalytics');
57
-
58
- if (is_admin()) {
59
- add_action('admin_init', 'admin_init_googleanalytics');
60
- add_action('admin_menu', 'admin_menu_googleanalytics');
61
- }
62
-
63
- if (!is_admin()) {
64
- add_action('wp_head', 'googleanalytics');
65
- }
66
-
67
- ?>
1
+ <?php
2
+
3
+ /*
4
+ * Plugin Name: Google Analytics
5
+ * Plugin URI: http://wordpress.org/extend/plugins/googleanalytics/
6
+ * Description: Use Google Analytics on your Wordpress site without touching any code, and view visitor reports right in your Wordpress admin dashboard!
7
+ * Version: 2.0.0
8
+ * Author: ShareThis
9
+ * Author URI: http://sharethis.com
10
+ */
11
+ if ( ! defined( 'WP_CONTENT_URL' ) ) {
12
+ define( 'WP_CONTENT_URL', get_option( 'siteurl' ) . '/wp-content' );
13
+ }
14
+ if ( ! defined( 'WP_CONTENT_DIR' ) ) {
15
+ define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' );
16
+ }
17
+ if ( ! defined( 'WP_PLUGIN_URL' ) ) {
18
+ define( 'WP_PLUGIN_URL', WP_CONTENT_URL . '/plugins' );
19
+ }
20
+ if ( ! defined( 'WP_PLUGIN_DIR' ) ) {
21
+ define( 'WP_PLUGIN_DIR', WP_CONTENT_DIR . '/plugins' );
22
+ }
23
+ if ( ! defined( 'GA_NAME' ) ) {
24
+ define( 'GA_NAME', 'googleanalytics' );
25
+ }
26
+ if ( ! defined( 'GA_PLUGIN_DIR' ) ) {
27
+ define( 'GA_PLUGIN_DIR', WP_CONTENT_DIR . '/plugins/' . GA_NAME );
28
+ }
29
+ if ( ! defined( 'GA_PLUGIN_URL' ) ) {
30
+ define( 'GA_PLUGIN_URL', WP_CONTENT_URL . '/plugins/' . GA_NAME );
31
+ }
32
+ if ( ! defined( 'GA_MAIN_FILE_PATH' ) ) {
33
+ define( 'GA_MAIN_FILE_PATH', __FILE__ );
34
+ }
35
+ if ( ! defined( 'GA_SHARETHIS_SCRIPTS_INCLUDED' ) ) {
36
+ define( 'GA_SHARETHIS_SCRIPTS_INCLUDED', 0 );
37
+ }
38
+ define( 'GOOGLEANALYTICS_VERSION', '2.0.0' );
39
+ include_once GA_PLUGIN_DIR . '/overwrite/ga_overwrite.php';
40
+ include_once GA_PLUGIN_DIR . '/class/Ga_Autoloader.php';
41
+ Ga_Autoloader::register();
42
+ Ga_Hook::add_hooks( GA_MAIN_FILE_PATH );
43
+
44
+ add_action( 'plugins_loaded', 'Ga_Admin::loaded_googleanalytics' );
45
+ add_action( 'init', 'Ga_Helper::init' );
46
+
47
+ ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
js/bootstrap.min.js ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * Bootstrap v3.3.7 (http://getbootstrap.com)
3
+ * Copyright 2011-2016 Twitter, Inc.
4
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5
+ */
6
+
7
+ /*!
8
+ * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=ddbeb97adad43a942be618a7956c300d)
9
+ * Config saved to config.json and https://gist.github.com/ddbeb97adad43a942be618a7956c300d
10
+ */
11
+ if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(t){"use strict";var e=t.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1||e[0]>3)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4")}(jQuery),+function(t){"use strict";function e(e,o){return this.each(function(){var s=t(this),n=s.data("bs.modal"),a=t.extend({},i.DEFAULTS,s.data(),"object"==typeof e&&e);n||s.data("bs.modal",n=new i(this,a)),"string"==typeof e?n[e](o):a.show&&n.show(o)})}var i=function(e,i){this.options=i,this.$body=t(document.body),this.$element=t(e),this.$dialog=this.$element.find(".modal-dialog"),this.$backdrop=null,this.isShown=null,this.originalBodyPad=null,this.scrollbarWidth=0,this.ignoreBackdropClick=!1,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,t.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};i.VERSION="3.3.7",i.TRANSITION_DURATION=300,i.BACKDROP_TRANSITION_DURATION=150,i.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},i.prototype.toggle=function(t){return this.isShown?this.hide():this.show(t)},i.prototype.show=function(e){var o=this,s=t.Event("show.bs.modal",{relatedTarget:e});this.$element.trigger(s),this.isShown||s.isDefaultPrevented()||(this.isShown=!0,this.checkScrollbar(),this.setScrollbar(),this.$body.addClass("modal-open"),this.escape(),this.resize(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',t.proxy(this.hide,this)),this.$dialog.on("mousedown.dismiss.bs.modal",function(){o.$element.one("mouseup.dismiss.bs.modal",function(e){t(e.target).is(o.$element)&&(o.ignoreBackdropClick=!0)})}),this.backdrop(function(){var s=t.support.transition&&o.$element.hasClass("fade");o.$element.parent().length||o.$element.appendTo(o.$body),o.$element.show().scrollTop(0),o.adjustDialog(),s&&o.$element[0].offsetWidth,o.$element.addClass("in"),o.enforceFocus();var n=t.Event("shown.bs.modal",{relatedTarget:e});s?o.$dialog.one("bsTransitionEnd",function(){o.$element.trigger("focus").trigger(n)}).emulateTransitionEnd(i.TRANSITION_DURATION):o.$element.trigger("focus").trigger(n)}))},i.prototype.hide=function(e){e&&e.preventDefault(),e=t.Event("hide.bs.modal"),this.$element.trigger(e),this.isShown&&!e.isDefaultPrevented()&&(this.isShown=!1,this.escape(),this.resize(),t(document).off("focusin.bs.modal"),this.$element.removeClass("in").off("click.dismiss.bs.modal").off("mouseup.dismiss.bs.modal"),this.$dialog.off("mousedown.dismiss.bs.modal"),t.support.transition&&this.$element.hasClass("fade")?this.$element.one("bsTransitionEnd",t.proxy(this.hideModal,this)).emulateTransitionEnd(i.TRANSITION_DURATION):this.hideModal())},i.prototype.enforceFocus=function(){t(document).off("focusin.bs.modal").on("focusin.bs.modal",t.proxy(function(t){document===t.target||this.$element[0]===t.target||this.$element.has(t.target).length||this.$element.trigger("focus")},this))},i.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keydown.dismiss.bs.modal",t.proxy(function(t){27==t.which&&this.hide()},this)):this.isShown||this.$element.off("keydown.dismiss.bs.modal")},i.prototype.resize=function(){this.isShown?t(window).on("resize.bs.modal",t.proxy(this.handleUpdate,this)):t(window).off("resize.bs.modal")},i.prototype.hideModal=function(){var t=this;this.$element.hide(),this.backdrop(function(){t.$body.removeClass("modal-open"),t.resetAdjustments(),t.resetScrollbar(),t.$element.trigger("hidden.bs.modal")})},i.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},i.prototype.backdrop=function(e){var o=this,s=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var n=t.support.transition&&s;if(this.$backdrop=t(document.createElement("div")).addClass("modal-backdrop "+s).appendTo(this.$body),this.$element.on("click.dismiss.bs.modal",t.proxy(function(t){return this.ignoreBackdropClick?void(this.ignoreBackdropClick=!1):void(t.target===t.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus():this.hide()))},this)),n&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!e)return;n?this.$backdrop.one("bsTransitionEnd",e).emulateTransitionEnd(i.BACKDROP_TRANSITION_DURATION):e()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var a=function(){o.removeBackdrop(),e&&e()};t.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",a).emulateTransitionEnd(i.BACKDROP_TRANSITION_DURATION):a()}else e&&e()},i.prototype.handleUpdate=function(){this.adjustDialog()},i.prototype.adjustDialog=function(){var t=this.$element[0].scrollHeight>document.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&t?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!t?this.scrollbarWidth:""})},i.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},i.prototype.checkScrollbar=function(){var t=window.innerWidth;if(!t){var e=document.documentElement.getBoundingClientRect();t=e.right-Math.abs(e.left)}this.bodyIsOverflowing=document.body.clientWidth<t,this.scrollbarWidth=this.measureScrollbar()},i.prototype.setScrollbar=function(){var t=parseInt(this.$body.css("padding-right")||0,10);this.originalBodyPad=document.body.style.paddingRight||"",this.bodyIsOverflowing&&this.$body.css("padding-right",t+this.scrollbarWidth)},i.prototype.resetScrollbar=function(){this.$body.css("padding-right",this.originalBodyPad)},i.prototype.measureScrollbar=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",this.$body.append(t);var e=t.offsetWidth-t.clientWidth;return this.$body[0].removeChild(t),e};var o=t.fn.modal;t.fn.modal=e,t.fn.modal.Constructor=i,t.fn.modal.noConflict=function(){return t.fn.modal=o,this},t(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(i){var o=t(this),s=o.attr("href"),n=t(o.attr("data-target")||s&&s.replace(/.*(?=#[^\s]+$)/,"")),a=n.data("bs.modal")?"toggle":t.extend({remote:!/#/.test(s)&&s},n.data(),o.data());o.is("a")&&i.preventDefault(),n.one("show.bs.modal",function(t){t.isDefaultPrevented()||n.one("hidden.bs.modal",function(){o.is(":visible")&&o.trigger("focus")})}),e.call(n,a,this)})}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var o=t(this),s=o.data("bs.tab");s||o.data("bs.tab",s=new i(this)),"string"==typeof e&&s[e]()})}var i=function(e){this.element=t(e)};i.VERSION="3.3.7",i.TRANSITION_DURATION=150,i.prototype.show=function(){var e=this.element,i=e.closest("ul:not(.dropdown-menu)"),o=e.data("target");if(o||(o=e.attr("href"),o=o&&o.replace(/.*(?=#[^\s]*$)/,"")),!e.parent("li").hasClass("active")){var s=i.find(".active:last a"),n=t.Event("hide.bs.tab",{relatedTarget:e[0]}),a=t.Event("show.bs.tab",{relatedTarget:s[0]});if(s.trigger(n),e.trigger(a),!a.isDefaultPrevented()&&!n.isDefaultPrevented()){var r=t(o);this.activate(e.closest("li"),i),this.activate(r,r.parent(),function(){s.trigger({type:"hidden.bs.tab",relatedTarget:e[0]}),e.trigger({type:"shown.bs.tab",relatedTarget:s[0]})})}}},i.prototype.activate=function(e,o,s){function n(){a.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),e.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),r?(e[0].offsetWidth,e.addClass("in")):e.removeClass("fade"),e.parent(".dropdown-menu").length&&e.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),s&&s()}var a=o.find("> .active"),r=s&&t.support.transition&&(a.length&&a.hasClass("fade")||!!o.find("> .fade").length);a.length&&r?a.one("bsTransitionEnd",n).emulateTransitionEnd(i.TRANSITION_DURATION):n(),a.removeClass("in")};var o=t.fn.tab;t.fn.tab=e,t.fn.tab.Constructor=i,t.fn.tab.noConflict=function(){return t.fn.tab=o,this};var s=function(i){i.preventDefault(),e.call(t(this),"show")};t(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',s).on("click.bs.tab.data-api",'[data-toggle="pill"]',s)}(jQuery);
js/googleanalytics.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Created by mdn on 2016-12-02.
3
+ */
4
+
5
+ (function ($) {
6
+ ga_loader = {
7
+ show: function () {
8
+ $('.ga-loader').show();
9
+ },
10
+ hide: function () {
11
+ $('.ga-loader').hide();
12
+ }
13
+ };
14
+
15
+ ga_tools = {
16
+ getCurrentWidth: function (wrapperSelector) {
17
+ return $(wrapperSelector).width();
18
+ },
19
+ recomputeChartWidth: function (minWidth, offset, wrapperSelector) {
20
+ const currentWidth = ga_tools.getCurrentWidth(wrapperSelector);
21
+ if (currentWidth >= minWidth) {
22
+ return parseInt(currentWidth - offset);
23
+ } else {
24
+ return minWidth;
25
+ }
26
+ }
27
+ };
28
+ })(jQuery);
js/googleanalytics_dashboard.js ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ($) {
2
+
3
+ const wrapperSelector = '#ga_dashboard_widget';
4
+ const minWidth = 350;
5
+ const offset = 10;
6
+
7
+ ga_dashboard = {
8
+ chartData: [],
9
+ init: function (dataArr) {
10
+ ga_loader.show();
11
+ google.charts.load('current', {'packages': ['corechart']});
12
+ google.charts.setOnLoadCallback(function () {
13
+ ga_dashboard.drawChart(dataArr);
14
+ ga_dashboard.setChartData(dataArr);
15
+ });
16
+ },
17
+ events: function (data) {
18
+ $(document).ready(function () {
19
+ $('#range-selector').on('change', function () {
20
+ const selected = $(this).val();
21
+ const selected_name = $('#metrics-selector option:selected').html();
22
+ const selected_metric = $('#metrics-selector option:selected').val() || null;
23
+
24
+ ga_loader.show();
25
+
26
+ $.ajax({
27
+ type: "post",
28
+ dataType: "json",
29
+ url: ajaxurl,
30
+ data: {action: "ga_ajax_data_change", date_range: selected, metric: selected_metric},
31
+ success: function (response) {
32
+
33
+ ga_loader.hide();
34
+
35
+ var dataT = [['Day', selected_name]];
36
+ $.each(response.chart, function (k, v) {
37
+ dataT.push([v.day, parseInt(v.current)]);
38
+ });
39
+
40
+ $.each(response.boxes, function (k, v) {
41
+ $('#ga_box_dashboard_label_' + k).html(v.label)
42
+ $('#ga_box_dashboard_value_' + k).html(v.value);
43
+ });
44
+
45
+ ga_dashboard.drawChart(dataT, selected_name);
46
+
47
+ // Set new data
48
+ ga_dashboard.setChartData(dataT);
49
+ }
50
+ });
51
+ });
52
+
53
+ $('#metrics-selector').on('change', function () {
54
+ const selected = $(this).val();
55
+ const selected_name = $('#metrics-selector option:selected').html();
56
+ const selected_range = $('#range-selector option:selected').val() || null;
57
+
58
+ ga_loader.show();
59
+
60
+ $.ajax({
61
+ type: "post",
62
+ dataType: "json",
63
+ url: ajaxurl,
64
+ data: {action: "ga_ajax_data_change", metric: selected, date_range: selected_range},
65
+ success: function (response) {
66
+ ga_loader.hide();
67
+ var dataT = [['Day', selected_name]];
68
+ $.each(response.chart, function (k, v) {
69
+ dataT.push([v.day, parseInt(v.current)]);
70
+ });
71
+ ga_dashboard.drawChart(dataT, selected_name);
72
+
73
+ // Set new data
74
+ ga_dashboard.setChartData(dataT);
75
+ }
76
+ });
77
+ });
78
+
79
+ $(window).on('resize', function () {
80
+ ga_dashboard.drawChart(ga_dashboard.getChartData(), ga_tools.recomputeChartWidth(minWidth, offset, wrapperSelector));
81
+ });
82
+ });
83
+ },
84
+ /**
85
+ * Returns chart data array.
86
+ * @returns {Array}
87
+ */
88
+ getChartData: function () {
89
+ return ga_dashboard.chartData;
90
+ },
91
+ /**
92
+ * Overwrites initial data array.
93
+ * @param new_data
94
+ */
95
+ setChartData: function (new_data) {
96
+ ga_dashboard.chartData = new_data;
97
+ },
98
+ drawChart: function (dataArr, title) {
99
+ const chart_dom_element = document.getElementById('chart_div');
100
+
101
+ if (typeof title == 'undefined') {
102
+ title = 'Pageviews';
103
+ }
104
+
105
+ const data = google.visualization.arrayToDataTable(dataArr);
106
+
107
+ const options = {
108
+ /*title: title,*/
109
+ legend: 'top',
110
+ lineWidth: 2,
111
+ chartArea: {
112
+ left: 10,
113
+ top: 60,
114
+ bottom: 50,
115
+ right: 10
116
+
117
+ },
118
+ width: '95%',
119
+ height: 300,
120
+ hAxis: {title: 'Day', titleTextStyle: {color: '#333'}, direction: 1},
121
+ vAxis: {minValue: 0},
122
+ pointSize: 5
123
+ };
124
+
125
+ var chart = new google.visualization.AreaChart(chart_dom_element);
126
+ google.visualization.events.addListener(chart, 'ready', function () {
127
+ ga_loader.hide();
128
+ });
129
+ chart.draw(data, options);
130
+ }
131
+ };
132
+
133
+ })(jQuery);
js/googleanalytics_page.js ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const GA_ACCESS_CODE_MODAL_ID = "ga_access_code_modal";
2
+ const GA_ACCESS_CODE_TMP_ID = "ga_access_code_tmp";
3
+ const GA_ACCESS_CODE_ID = "ga_access_code";
4
+ const GA_FORM_ID = "ga_form";
5
+
6
+ (function ($) {
7
+
8
+ ga_popup = {
9
+ url: '',
10
+ authorize: function (e, url) {
11
+ e.preventDefault();
12
+ ga_popup.url = url;
13
+ $('#' + GA_ACCESS_CODE_MODAL_ID).appendTo("body").modal('show');
14
+ ga_popup.open();
15
+ },
16
+ open: function () {
17
+ const p_width = Math.round(screen.width / 2);
18
+ const p_height = Math.round(screen.height / 2);
19
+ const p_left = Math.round(p_width / 2);
20
+ const p_top = 300;
21
+ window.open(ga_popup.url, 'ga_auth_popup', 'width=' + p_width + ',height='
22
+ + p_height + ',top=' + p_top + ',left=' + p_left);
23
+ },
24
+ saveAccessCode: function (e) {
25
+ e.preventDefault();
26
+ e.target.disabled = 'disabled';
27
+ ga_loader.show();
28
+ const ac_tmp = $('#' + GA_ACCESS_CODE_TMP_ID).val();
29
+ if (ac_tmp) {
30
+ $('#' + GA_ACCESS_CODE_ID).val(ac_tmp);
31
+ $('#' + GA_FORM_ID).submit();
32
+ }
33
+ }
34
+ };
35
+ ga_events = {
36
+
37
+ click: function (selector, callback) {
38
+ $(selector).live('click', callback);
39
+ },
40
+ codeManuallyCallback: function ( terms_accepted ) {
41
+ const button_disabled = $('#ga_authorize_with_google_button').attr('disabled');
42
+ const selector_disabled = $('#ga_account_selector').attr('disabled');
43
+ if ( terms_accepted ) {
44
+ if (button_disabled) {
45
+ $('#ga_authorize_with_google_button').removeAttr('disabled').next().hide();
46
+ } else {
47
+ $('#ga_authorize_with_google_button').attr('disabled',
48
+ 'disabled').next().show();
49
+ }
50
+
51
+ if (selector_disabled) {
52
+ $('#ga_account_selector').removeAttr('disabled');
53
+ } else {
54
+ $('#ga_account_selector').attr('disabled', 'disabled');
55
+ }
56
+ }
57
+
58
+ $('#ga_manually_wrapper').toggle();
59
+ },
60
+ initModalEvents: function () {
61
+ $('#' + GA_ACCESS_CODE_MODAL_ID).on('shown.bs.modal', function () {
62
+ $('#' + GA_ACCESS_CODE_TMP_ID).focus();
63
+ });
64
+
65
+ $('#' + GA_ACCESS_CODE_MODAL_ID).on('hide.bs.modal', function () {
66
+ ga_loader.hide();
67
+ $('#ga_save_access_code').removeAttr('disabled');
68
+ });
69
+ }
70
+ };
71
+
72
+ $(document).ready(function () {
73
+ ga_events.initModalEvents();
74
+ });
75
+
76
+ const offset = 50;
77
+ const minWidth = 350;
78
+ const wrapperSelector = '#ga-stats-container';
79
+ const chartContainer = 'chart_div';
80
+
81
+ ga_charts = {
82
+
83
+ init: function (callback) {
84
+ $(document).ready(function () {
85
+ google.charts.load('current', {
86
+ 'packages': ['corechart']
87
+ });
88
+ ga_loader.show();
89
+ google.charts.setOnLoadCallback(callback);
90
+ });
91
+ },
92
+ createTooltip: function (day, pageviews) {
93
+ return '<div style="padding:10px;width:100px;">' + '<strong>' + day
94
+ + '</strong><br>' + 'Pageviews:<strong> ' + pageviews
95
+ + '</strong>' + '</div>';
96
+ },
97
+ events: function (data) {
98
+ $(window).on('resize', function () {
99
+ ga_charts.drawChart(data, ga_tools.recomputeChartWidth(minWidth, offset, wrapperSelector));
100
+ });
101
+ },
102
+ drawChart: function (data, chartWidth) {
103
+
104
+ if (typeof chartWidth == 'undefined') {
105
+ chartWidth = ga_tools.recomputeChartWidth(minWidth, offset, wrapperSelector);
106
+ }
107
+
108
+ const options = {
109
+ /*title : 'Page Views',*/
110
+ lineWidth: 5,
111
+ pointSize: 10,
112
+ tooltip: {
113
+ isHtml: true
114
+ },
115
+ legend: {
116
+ position: (ga_tools.getCurrentWidth(wrapperSelector) <= minWidth ? 'top'
117
+ : 'top'),
118
+ maxLines: 5,
119
+ alignment: 'start',
120
+ textStyle: {color: '#000', fontSize: 12}
121
+ },
122
+ colors: ['#4285f4', '#ff9800'],
123
+ hAxis: {
124
+ title: 'Day',
125
+ titleTextStyle: {
126
+ color: '#333'
127
+ }
128
+ },
129
+ vAxis: {
130
+ minValue: 0
131
+ },
132
+ width: chartWidth,
133
+ height: 500,
134
+ chartArea: {
135
+ top: 50,
136
+ left: 50,
137
+ right: 30,
138
+ bottom: 100
139
+ },
140
+ };
141
+ var chart = new google.visualization.AreaChart(document
142
+ .getElementById(chartContainer));
143
+ chart.draw(data, options);
144
+ }
145
+ };
146
+ })(jQuery);
lib/Ga_Lib_Api_Client.php ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ga_Lib_Api_Client {
4
+
5
+ const OAUTH2_REVOKE_ENDPOINT = 'https://accounts.google.com/o/oauth2/revoke';
6
+
7
+ const OAUTH2_TOKEN_ENDPOINT = 'https://accounts.google.com/o/oauth2/token';
8
+
9
+ const OAUTH2_AUTH_ENDPOINT = 'https://accounts.google.com/o/oauth2/auth';
10
+
11
+ const OAUTH2_FEDERATED_SIGNON_CERTS_ENDPOINT = 'https://www.googleapis.com/oauth2/v1/certs';
12
+
13
+ const GA_ACCOUNT_SUMMARIES_ENDPOINT = 'https://www.googleapis.com/analytics/v3/management/accountSummaries';
14
+
15
+ const GA_DATA_ENDPOINT = 'https://analyticsreporting.googleapis.com/v4/reports:batchGet';
16
+
17
+ const OAUTH2_CALLBACK_URI = 'urn:ietf:wg:oauth:2.0:oob';
18
+
19
+ /**
20
+ * Pre-defined API credentials.
21
+ *
22
+ * @var array
23
+ */
24
+ private $config = array(
25
+ 'access_type' => 'offline',
26
+ 'application_name' => 'Google Analytics',
27
+ 'client_id' => '207216681371-433ldmujuv4l0743c1j7g8sci57cb51r.apps.googleusercontent.com',
28
+ 'client_secret' => 'y0B-K-ODB1KZOam50aMEDhyc',
29
+ 'scopes' => array( 'https://www.googleapis.com/auth/analytics.readonly' ),
30
+ 'approval_prompt' => 'force'
31
+ );
32
+
33
+ /**
34
+ * Keeps Access Token information.
35
+ *
36
+ * @var array
37
+ */
38
+ private $token;
39
+
40
+ /**
41
+ * Returns API client instance.
42
+ *
43
+ * @return Ga_Lib_Api_Client|null
44
+ */
45
+ public static function get_instance() {
46
+ static $instance = null;
47
+ if ( $instance === null ) {
48
+ $instance = new Ga_Lib_Api_Client();
49
+ }
50
+
51
+ return $instance;
52
+ }
53
+
54
+ /**
55
+ * Calls api methods.
56
+ *
57
+ * @param string $callback
58
+ * @param mixed $args
59
+ *
60
+ * @return mixed
61
+ */
62
+ public function call( $callback, $args = null ) {
63
+ try {
64
+ $callback = array( get_class( $this ), $callback );
65
+ if ( is_callable( $callback ) ) {
66
+ if ( ! empty( $args ) ) {
67
+ if ( is_array( $args ) ) {
68
+ return call_user_func_array( $callback, $args );
69
+ } else {
70
+ return call_user_func_array( $callback, array( $args ) );
71
+ }
72
+ } else {
73
+ return call_user_func( $callback );
74
+ }
75
+ } else {
76
+ throw new Exception( 'Unknown method: ' . $callback );
77
+ }
78
+ } catch ( Exception $e ) {
79
+ // @todo: need to add exception
80
+ echo $e->getMessage();
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Sets access token.
86
+ *
87
+ * @param $token
88
+ */
89
+ public function set_access_token( $token ) {
90
+ $this->token = $token;
91
+ }
92
+
93
+ /**
94
+ * Returns Google Oauth2 redirect URL.
95
+ *
96
+ * @return string
97
+ */
98
+ private function get_redirect_uri() {
99
+ return self::OAUTH2_CALLBACK_URI;
100
+ }
101
+
102
+ /**
103
+ * Creates Google Oauth2 authorization URL.
104
+ *
105
+ * @return string
106
+ */
107
+ public function create_auth_url() {
108
+ $params = array(
109
+ 'response_type' => 'code',
110
+ 'redirect_uri' => $this->get_redirect_uri(),
111
+ 'client_id' => urlencode( $this->config['client_id'] ),
112
+ 'scope' => implode( " ", $this->config['scopes'] ),
113
+ 'access_type' => urlencode( $this->config['access_type'] ),
114
+ 'approval_prompt' => urlencode( $this->config['approval_prompt'] )
115
+ );
116
+
117
+ return self::OAUTH2_AUTH_ENDPOINT . "?" . http_build_query( $params );
118
+ }
119
+
120
+ /**
121
+ * Sends request for Access Token during Oauth2 process.
122
+ *
123
+ * @param $access_code
124
+ *
125
+ * @return Ga_Lib_Api_Response Returns response object
126
+ */
127
+ private function ga_auth_get_access_token( $access_code ) {
128
+ $request = array(
129
+ 'code' => $access_code,
130
+ 'grant_type' => 'authorization_code',
131
+ 'redirect_uri' => $this->get_redirect_uri(),
132
+ 'client_id' => $this->config['client_id'],
133
+ 'client_secret' => $this->config['client_secret']
134
+ );
135
+
136
+ $response = Ga_Lib_Api_Request::get_instance()->make_request( self::OAUTH2_TOKEN_ENDPOINT, $request );
137
+
138
+ return new Ga_Lib_Api_Response( $response );
139
+ }
140
+
141
+ /**
142
+ * Get list of the analytics accounts.
143
+ *
144
+ * @return Ga_Lib_Api_Response Returns response object
145
+ */
146
+ private function ga_api_account_summaries() {
147
+ $request = Ga_Lib_Api_Request::get_instance();
148
+ $request = $this->sign( $request );
149
+ $response = $request->make_request( self::GA_ACCOUNT_SUMMARIES_ENDPOINT );
150
+
151
+ return new Ga_Lib_Api_Response( $response );
152
+ }
153
+
154
+ /**
155
+ * Sends request for Google Analytics data using given query parameters.
156
+ *
157
+ * @param $query_params
158
+ *
159
+ * @return Ga_Lib_Api_Response Returns response object
160
+ */
161
+ private function ga_api_data( $query_params ) {
162
+ $request = Ga_Lib_Api_Request::get_instance();
163
+ $request = $this->sign( $request );
164
+ $response = $request->make_request( self::GA_DATA_ENDPOINT, wp_json_encode( $query_params ), true );
165
+
166
+ return new Ga_Lib_Api_Response( $response );
167
+ }
168
+
169
+ /**
170
+ * Sign request with Access Token.
171
+ * Adds Access Token to the request's headers.
172
+ *
173
+ * @param Ga_Lib_Api_Request $request
174
+ *
175
+ * @return Ga_Lib_Api_Request Returns response object
176
+ */
177
+ private function sign( Ga_Lib_Api_Request $request ) {
178
+ // Add the OAuth2 header to the request
179
+ $request->set_request_headers( array( 'Authorization: Bearer ' . $this->token['access_token'] ) );
180
+
181
+ return $request;
182
+ }
183
+
184
+ /**
185
+ * Checks if Access Token is valid.
186
+ *
187
+ * @return bool
188
+ */
189
+ public function is_authorized() {
190
+ $authorized = true;
191
+ if ( $this->is_access_token_expired() ) {
192
+ $authorized = false;
193
+ }
194
+
195
+ return $authorized;
196
+ }
197
+
198
+ /**
199
+ * Returns if the access_token is expired.
200
+ * @return bool Returns True if the access_token is expired.
201
+ */
202
+ public function is_access_token_expired() {
203
+ if ( null == $this->token ) {
204
+ return true;
205
+ }
206
+
207
+ // Check if the token is expired in the next 30 seconds.
208
+ $expired = ( $this->token['created'] + ( $this->token['expires_in'] - 30 ) ) < time();
209
+
210
+ return $expired;
211
+ }
212
+ }
lib/Ga_Lib_Api_Request.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ga_Lib_Api_Request {
4
+
5
+ const HEADER_CONTENT_TYPE = "application/x-www-form-urlencoded";
6
+
7
+ const HEADER_CONTENT_TYPE_JSON = "Content-type: application/json";
8
+
9
+ const HEADER_ACCEPT = "Accept: application/json, text/javascript, */*; q=0.01";
10
+
11
+ const TIMEOUT = 30;
12
+
13
+ const USER_AGENT = 'googleanalytics-wordpress-plugin';
14
+
15
+ private $headers = array();
16
+
17
+ function __construct() {
18
+ }
19
+
20
+ /**
21
+ * Returns API client instance.
22
+ *
23
+ * @return Ga_Lib_Api_Request|null
24
+ */
25
+ public static function get_instance() {
26
+ static $instance = null;
27
+ if ( $instance === null ) {
28
+ $instance = new Ga_Lib_Api_Request();
29
+ }
30
+
31
+ return $instance;
32
+ }
33
+
34
+ /**
35
+ * Sets request headers.
36
+ *
37
+ * @param $headers
38
+ */
39
+ public function set_request_headers( $headers ) {
40
+ if ( is_array( $headers ) ) {
41
+ $this->headers = array_merge( $this->headers, $headers );
42
+ } else {
43
+ $this->headers[] = $headers;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Perform HTTP request.
49
+ *
50
+ * @param string $url URL address
51
+ * @param string $rawPostBody
52
+ *
53
+ * @return string Response
54
+ * @throws Exception
55
+ */
56
+ public function make_request( $url, $rawPostBody = null, $json = false ) {
57
+ if ( ! function_exists( 'curl_init' ) ) {
58
+ throw new Exception( 'cURL functions are not available' );
59
+ }
60
+
61
+ // Set default headers
62
+ $this->set_request_headers( array(
63
+ ( $json ? self::HEADER_CONTENT_TYPE_JSON : self::HEADER_CONTENT_TYPE ),
64
+ self::HEADER_ACCEPT,
65
+ 'Expect: 200-OK'
66
+ ) );
67
+
68
+ $ch = curl_init( $url );
69
+ $headers = $this->headers;
70
+ curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
71
+
72
+ $curl_timeout = self::TIMEOUT;
73
+ $php_execution_time = ini_get( 'max_execution_time' );
74
+ if ( ! empty( $php_execution_time ) && is_numeric( $php_execution_time ) ) {
75
+ if ( $php_execution_time < 36 && $php_execution_time > 9 ) {
76
+ $curl_timeout = $php_execution_time - 5;
77
+ } elseif ( $php_execution_time < 10 ) {
78
+ $curl_timeout = 5;
79
+ }
80
+ }
81
+
82
+ curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $curl_timeout );
83
+ curl_setopt( $ch, CURLOPT_TIMEOUT, $curl_timeout );
84
+ curl_setopt( $ch, CURLOPT_HEADER, true );
85
+ curl_setopt( $ch, CURLINFO_HEADER_OUT, true );
86
+ curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
87
+ curl_setopt( $ch, CURLOPT_USERAGENT, self::USER_AGENT );
88
+
89
+ // POST body
90
+ if ( ! empty( $rawPostBody ) ) {
91
+ curl_setopt( $ch, CURLOPT_POST, true );
92
+ curl_setopt( $ch, CURLOPT_POSTFIELDS, $rawPostBody );
93
+ }
94
+
95
+ // Execute request
96
+ $response = curl_exec( $ch );
97
+
98
+ if ( $error = curl_error( $ch ) ) {
99
+ $errno = curl_errno( $ch );
100
+ curl_close( $ch );
101
+ throw new Exception( $error . ' (' . $errno . ')' );
102
+ } else {
103
+
104
+ $headerSize = curl_getinfo( $ch, CURLINFO_HEADER_SIZE );
105
+ $header = substr( $response, 0, $headerSize );
106
+ $body = substr( $response, $headerSize, strlen( $response ) );
107
+ curl_close( $ch );
108
+
109
+ return array( $header, $body );
110
+ }
111
+ }
112
+ }
lib/Ga_Lib_Api_Response.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ga_Lib_Api_Response {
4
+
5
+ private $header;
6
+ private $body;
7
+ private $data;
8
+
9
+ function __construct( $raw_response ) {
10
+ $this->setHeader( $raw_response[0] );
11
+ $this->setBody( $raw_response[1] );
12
+ $this->setData( json_decode( $raw_response[1], true ) );
13
+ }
14
+
15
+ public function setHeader( $header ) {
16
+ $this->header = $header;
17
+ }
18
+
19
+ public function getHeader() {
20
+ return $this->header;
21
+ }
22
+
23
+ public function setBody( $body ) {
24
+ $this->body = $body;
25
+ }
26
+
27
+ public function getBody() {
28
+ return $this->body;
29
+ }
30
+
31
+ public function setData( $data ) {
32
+ $this->data = $data;
33
+ }
34
+
35
+ public function getData() {
36
+ return $this->data;
37
+ }
38
+ }
overwrite/ga_overwrite.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Created by PhpStorm.
4
+ * User: mdn
5
+ * Date: 2016-12-08
6
+ * Time: 09:15
7
+ */
8
+
9
+ if ( ! function_exists( 'wp_json_encode' ) ) {
10
+ function wp_json_encode( $data ) {
11
+ return json_encode( $data );
12
+ }
13
+ }
readme.txt CHANGED
@@ -1,27 +1,60 @@
1
- === Google Analytics ===
2
- Contributors: kevinsylvestre
3
- Tags: javascript, google, analytics
4
- Requires at least: 2.7
5
- Tested up to: 4.6.1
6
- Stable tag: 1.0.7
7
-
8
- Enables google analytics on all pages.
9
-
10
- == Description ==
11
-
12
- This plugin adds the required javascript for google analytics.
13
-
14
- For more information visit:
15
-
16
- [Google Analytics](http://www.google.com/analytics)
17
-
18
- == Installation ==
19
-
20
- 1. Upload `googleanalytics` directory to the `/wp-content/plugins/` directory
21
- 2. Activate the plugin through the Plugins menu in your WordPress dashboard
22
- 3. Add the web property ID from Google Analytics (UA-0000000-0) to the settings (Admin > Settings > Google Analytics)
23
-
24
- == Screenshots ==
25
-
26
- 1. Modified settings panel with Google Analytics.
27
- 2. Google Analytics settings page.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Google Analytics ===
2
+ Contributors: ShareThis
3
+ Tags: analytics, dashboard, google, google analytics, google analytics plugin, javascript, marketing, pageviews, statistics, stats, tracking, visits, web stats, widget, analytics dashboard, google analytics dashboard, google analytics widget, google analytics dashboard
4
+ Requires at least: 3.8
5
+ Tested up to: 4.7.0
6
+ Stable tag: 2.0.0
7
+
8
+ Use Google Analytics on your Wordpress site without touching any code, and view visitor reports right in your Wordpress admin dashboard!
9
+
10
+ == Description ==
11
+
12
+ Google Analytics plugin from ShareThis is the best way to add GA tracking code to your website without modifying any files. Just log in with Google right from your WP admin dashboard and choose which website you want to link. Then you can disable GA tracking of specific users, so that when you are browsing your own site it won't affect your analytics.
13
+
14
+ Also, you will be able to see Google Analytics reports in the same interface you already us every day to write and manage your posts - in your Wordpress dashboard. Now you can stay informed on how your website is doing without having to log into a separate tool.
15
+
16
+ But wait, there's more! All feature of the Google Analytics plugin are free, with no subscriptions fees or add-ons!
17
+
18
+ Features:
19
+
20
+ * Simple setup - Adds latest version of Google Analytics javascript to every page
21
+ * Account linking - no need to copy and paste any code or know your GA ID, just log in with Google, select the required website and it will automatically include the appropriate code
22
+ * Visitor trends - Shows a summary dashboard with page views, users, pages per session and bounce rate for the past 7 days as compared to the previous 7 days
23
+ * Traffic sources - Shows top 5 traffic sources so you know where your visitors are coming from
24
+ * Only track real visitors - Allows you to disable tracking for Admins, Editors, Authors, Contributors, and/or
25
+ * Subscribers so your analytics represent real visitors
26
+ * Mobile - Fully optimized for mobile, so you can view your dashboards on any device
27
+ * More updates coming - Continually updated and supported by a team of top Wordpress developers
28
+
29
+ If you don't have a Google Analytics account, you can sign up for free here: https://www.google.com/analytics/
30
+
31
+ By downloading and installing this plugin you are agreeing to the <a href="http://www.sharethis.com/privacy/" target="_blank">Privacy Policy</a> and <a href="http://www.sharethis.com/publisher-terms-of-use/" target="_blank">Terms of Service</a>.
32
+
33
+ == Installation ==
34
+
35
+ 1. Install Google Analytics either via WordPress.org plugin repository or directly by uploading the files to your server
36
+ 2. Activate the plugin through the Plugins menu in your WordPress dashboard
37
+ 3. Navigate to the Plugin settings in the WordPress sidebar
38
+ 4. Authenticate via Google, copy and paste the access code and choose your property from the dropdown. You can also add the web property ID from Google Analytics manually but dashboards won't show up in this case.
39
+
40
+ == Screenshots ==
41
+
42
+ 1. Overall site performance - the past 7 days vs previous 7 days
43
+ 2. The top 5 traffic sources for the past 7 days
44
+ 3. Directly authenticate Google Analytics, and exclude sets of logged in users
45
+ 4. Just click to authenticate, then copy the API key and add it to the plugin
46
+ 5. View different time ranges and key metrics in the Wordpress Google Analytics widget
47
+
48
+ == Changelog ==
49
+
50
+ = 2.0.0 =
51
+ * Completely redesigned with new features!
52
+ * Updated with the latest Google Analytics code
53
+ * No need to find your GA property ID and copy it over, just sign in with Google and choose your site
54
+ * See analytics right inside the plugin, the past 7 days vs your previous 7 days
55
+ * Shows pageviews, users, pages per session and bounce rate + top 5 traffic referrals
56
+ * Wordpress Dashboard widget for 7, 30 or 90 days graph and top site usage stats
57
+ * Disable tracking for logged in users like admins or editors for more reliable analytics
58
+
59
+ = 1.0.7 =
60
+ * Added ability to include Google Analytics tracking code on every WordPress page
screenshot-1.png CHANGED
Binary file
screenshot-2.png CHANGED
Binary file
screenshot-3.png ADDED
Binary file
screenshot-4.png ADDED
Binary file
screenshot-5.png ADDED
Binary file
view/ga_accounts_selector.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+ <input type="hidden" name="<?php echo esc_attr( Ga_Admin::GA_SELECTED_ACCOUNT ); ?>"
3
+ value="<?php echo esc_attr( $selected ); ?>">
4
+ <select id="ga_account_selector"
5
+ name="<?php echo esc_attr( Ga_Admin::GA_SELECTED_ACCOUNT ); ?>" <?php echo esc_attr( $add_manually_enabled ? 'disabled="disabled"' : '' ); ?>>
6
+ <option><?php _e( 'Please select your Google Analytics account:' ); ?></option>
7
+ <?php
8
+ if ( ! empty( $selector ) ) {
9
+ foreach ( $selector as $account ) {
10
+ ?>
11
+ <optgroup label="<?php echo $account['name']; ?>">
12
+ <?php foreach ( $account['webProperties'] as $property ): ?>
13
+ <?php foreach ( $property['profiles'] as $profile ): ?>
14
+ <option
15
+ value="<?php echo esc_attr( $account['id'] . "_" . $property['webPropertyId'] . "_" . $profile['id'] ) ?>"
16
+ <?php echo( $selected === $account['id'] . "_" . $property['webPropertyId'] . "_" . $profile['id'] ? 'selected="selected"' : '' ); ?>><?php echo esc_html( $property['name'] . "&nbsp;[" . $property['webPropertyId'] . "][" . $profile['id'] . "]" ) ?></option>
17
+ <?php endforeach; ?>
18
+ <?php endforeach; ?>
19
+ </optgroup>
20
+ <?php
21
+ }
22
+ }
23
+ ?>
24
+ </select>
25
+ </div>
26
+
view/ga_code.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script>
2
+ (function (i, s, o, g, r, a, m) {
3
+ i['GoogleAnalyticsObject'] = r;
4
+ i[r] = i[r] || function () {
5
+ (i[r].q = i[r].q || []).push(arguments)
6
+ }, i[r].l = 1 * new Date();
7
+ a = s.createElement(o),
8
+ m = s.getElementsByTagName(o)[0];
9
+ a.async = 1;
10
+ a.src = g;
11
+ m.parentNode.insertBefore(a, m)
12
+ })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
13
+
14
+ ga('create', '<?php echo esc_attr( $data[ Ga_Admin::GA_WEB_PROPERTY_ID_OPTION_NAME ] ); ?>', 'auto');
15
+ ga('send', 'pageview');
16
+ </script>
view/ga_dashboard_widget.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap ga-wrap">
2
+
3
+ <div class="form-group">
4
+ <select id="range-selector" autocomplete="off">
5
+ <option value="7daysAgo">Last 7 Days</option>
6
+ <option value="30daysAgo" selected="selected">Last 30 Days</option>
7
+ <option value="90daysAgo">Last 90 Days</option>
8
+ </select>
9
+
10
+ <select id="metrics-selector" autocomplete="off">
11
+ <option value="pageviews">Pageviews</option>
12
+ <option value="sessions">Visits</option>
13
+ <option value="users">Users</option>
14
+ <option value="organicSearches">Organic Search</option>
15
+ <option value="visitBounceRate">Bounce Rate</option>
16
+ </select>
17
+
18
+ <div class="ga-loader-wrapper">
19
+ <div class="ga-loader"></div>
20
+ </div>
21
+ </div>
22
+
23
+ <div>
24
+ <div id="chart_div" style="width: 100%;"></div>
25
+ <div>
26
+ <div id="boxes-container">
27
+ <div class="ga-box-row">
28
+ <?php if ( ! empty( $boxes ) ) : ?>
29
+ <?php $iter = 1; ?>
30
+ <?php foreach ( $boxes as $k => $v ) : ?>
31
+ <div class="ga-box-column ga-box-dashboard">
32
+ <div style="color: grey; font-size: 13px;"
33
+ id="ga_box_dashboard_label_<?php echo $k; ?>"><?php echo $v['label'] ?></div>
34
+ <div style="font-size: 15px;"
35
+ id="ga_box_dashboard_value_<?php echo $k; ?>"><?php echo $v['value'] ?></div>
36
+ </div>
37
+ <?php if ( ( ( $iter ++ ) % 3 ) == 0 ) : ?>
38
+ </div>
39
+ <div class="ga-box-row">
40
+ <?php endif; ?>
41
+ <?php endforeach; ?>
42
+ <?php endif; ?>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </div>
47
+
48
+ <div style="margin-top: 5px;"><?php echo sprintf( '<a href="%s">' . __( 'Show more details' ) . '</a>', $more_details_url ); ?></div>
49
+ </div>
50
+
51
+ <script type="text/javascript">
52
+
53
+ dataArr = [['Day', 'Pageviews'],<?php
54
+ if ( $chart ) {
55
+ $arr = "";
56
+ foreach ( $chart as $row ) {
57
+ if ( $arr ) {
58
+ $arr .= ",";
59
+ }
60
+ $arr .= "['" . $row['day'] . "'," . $row['current'] . "]";
61
+ }
62
+ }
63
+ echo $arr;
64
+ ?>];
65
+
66
+ ga_dashboard.init(dataArr);
67
+ ga_dashboard.events(dataArr);
68
+
69
+ </script>
view/ga_notice.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="googleanalytics_terms_notice" class="notice notice-warning <?php echo ( Ga_Helper::is_plugin_page() ) ? : 'is-dismissible' ?>">
2
+ <p>
3
+ Google Analytics <?php echo esc_html( GOOGLEANALYTICS_VERSION ); ?> plugin <a href="http://www.sharethis.com/news/2016/12/sharethis-adds-analytics-plugin-to-suite-of-tools/" target="_blank">has joined the ShareThis family.</a> <strong>A host of new features</strong> has been added in this version, including Google Analytics dashboards and roles management, and the update requires agreeing to the <a href="http://www.sharethis.com/privacy/" target="_blank">privacy policy</a> and <a href="http://www.sharethis.com/publisher-terms-of-use/" target="_blank">terms of use</a> to enable them. <a href="<?php echo esc_url( $url ); ?>"><span class="button button-primary">I accept</span></a>
4
+ </p>
5
+ </div>
6
+ <script type="text/javascript">
7
+ jQuery( document ).ready( function () {
8
+ jQuery( '#googleanalytics_terms_notice .notice-dismiss' ).live( 'click', function ( event ) {
9
+ event.preventDefault();
10
+ jQuery.post( ajaxurl, { action: 'googleanalytics_hide_terms' } );
11
+ } );
12
+ } );
13
+ </script>
view/ga_oauth_notice.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <div class="wrap ga-notice">
2
+ <div class="alert alert-warning">
3
+ <?php echo $msg; ?>
4
+ </div>
5
+ </div>
view/page.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="ga_access_code_modal" class="modal fade" tabindex="-1" role="dialog" style="z-index: 1000000">
2
+ <div class="modal-dialog" role="document">
3
+ <div class="modal-content">
4
+ <div class="modal-header">
5
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
6
+ aria-hidden="true">&times;</span></button>
7
+ <h4 class="modal-title"><?php _e( 'Please paste the access code obtained from Google below:' ) ?></h4>
8
+ </div>
9
+ <div class="modal-body">
10
+ <label for="ga_access_code"><strong><?php _e( 'Access Code' ); ?></strong>:</label>
11
+ &nbsp;<input id="ga_access_code_tmp" type="text" style="width: 350px"
12
+ placeholder="<?php _e( 'Paste your access code here' ) ?>"/>
13
+ <div class="ga-loader-wrapper">
14
+ <div class="ga-loader"></div>
15
+ </div>
16
+ </div>
17
+ <div class="modal-footer">
18
+ <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
19
+ <button type="button" class="btn btn-primary"
20
+ id="ga_save_access_code"
21
+ onclick="ga_popup.saveAccessCode(event)"><?php _e( 'Save Changes' ); ?></button>
22
+ </div>
23
+ </div><!-- /.modal-content -->
24
+ </div><!-- /.modal-dialog -->
25
+ </div><!-- /.modal -->
26
+
27
+ <div class="wrap ga-wrap">
28
+ <h2>Google Analytics - <?php _e( 'Settings' ); ?></h2>
29
+ <div class="ga_container">
30
+ <form id="ga_form" method="post" action="options.php">
31
+ <?php settings_fields( 'googleanalytics' ); ?>
32
+ <input id="ga_access_code" type="hidden"
33
+ name="<?php echo esc_attr( Ga_Admin::GA_OAUTH_AUTH_CODE_OPTION_NAME ); ?>" value=""/>
34
+ <table class="form-table">
35
+ <tr valign="top">
36
+ <?php if ( ! empty( $data['popup_url'] ) ): ?>
37
+ <th scope="row">
38
+ <label <?php echo ( ! Ga_Helper::are_terms_accepted() ) ? 'class="label-grey ga-tooltip"' : '' ?>><?php echo _e( 'Google Profile' ) ?>
39
+ :
40
+ <span class="ga-tooltiptext ga-tt-abs"><?php _e( 'Please accept the terms to use this feature' ); ?></span>
41
+ </label>
42
+ </th>
43
+ <td <?php echo ( ! Ga_Helper::are_terms_accepted() ) ? 'class="ga-tooltip"' : ''; ?>>
44
+ <button id="ga_authorize_with_google_button" class="btn btn-primary"
45
+ <?php if ( Ga_Helper::are_terms_accepted() ) : ?>
46
+ onclick="ga_popup.authorize(event, '<?php echo esc_attr( $data['popup_url'] ); ?>')"
47
+ <?php endif; ?>
48
+ <?php echo( ( esc_attr( $data[ Ga_Admin::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME ] ) || ! Ga_Helper::are_terms_accepted() ) ? 'disabled="disabled"' : '' ); ?>
49
+ ><?php _e( 'Authenticate
50
+ with Google' ) ?>
51
+ </button>
52
+ <span class="ga-tooltiptext"><?php _e( 'Please accept the terms to use this feature' ); ?></span>
53
+ <?php if ( ! empty( $data[ Ga_Admin::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME ] ) ): ?>
54
+ <div class="ga_warning">
55
+ <strong><?php _e( 'Notice' ) ?></strong>:&nbsp;<?php _e( 'Please uncheck the "Manually enter Tracking ID" option to authenticate and view statistics.' ); ?>
56
+ </div>
57
+ <?php endif; ?>
58
+ </td>
59
+ <?php endif; ?>
60
+
61
+ <?php if ( ! empty( $data['ga_accounts_selector'] ) ): ?>
62
+ <th scope="row"><?php echo _e( 'Google Analytics Account' ) ?>:</th>
63
+ <td><?php echo $data['ga_accounts_selector']; ?></td>
64
+ <?php endif; ?>
65
+
66
+ </tr>
67
+
68
+ <tr valign="top">
69
+
70
+ <th scope="row">
71
+ <div class="checkbox">
72
+ <label class="ga_checkbox_label <?php echo ( ! Ga_Helper::are_terms_accepted() ) ? 'label-grey ga-tooltip' : '' ?>"
73
+ for="ga_enter_code_manually"> <input
74
+ <?php if ( Ga_Helper::are_terms_accepted() ) : ?>
75
+ onclick="ga_events.click( this, ga_events.codeManuallyCallback( <?php echo Ga_Helper::are_terms_accepted() ? 1 : 0; ?> ) )"
76
+ <?php endif; ?>
77
+ type="checkbox"
78
+ <?php echo ( ! Ga_Helper::are_terms_accepted() ) ? 'disabled="disabled"' : ''; ?>
79
+ name="<?php echo esc_attr( Ga_Admin::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME ); ?>"
80
+ id="ga_enter_code_manually"
81
+ value="1"
82
+ <?php echo( ( $data[ Ga_Admin::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME ] || ! Ga_Helper::are_terms_accepted() ) ? 'checked="checked"' : '' ); ?>/>&nbsp;
83
+ <?php _e( 'Manually enter Tracking ID' ) ?>
84
+ <span class="ga-tooltiptext ga-tt-abs"><?php _e( 'Please accept the terms to use this feature' ); ?></span>
85
+ </label>
86
+ </div>
87
+ </th>
88
+ <td></td>
89
+ </tr>
90
+ <tr valign="top"
91
+ id="ga_manually_wrapper" <?php echo( ( $data[ Ga_Admin::GA_WEB_PROPERTY_ID_MANUALLY_OPTION_NAME ] || ! Ga_Helper::are_terms_accepted() ) ? '' : 'style="display: none"' ); ?> >
92
+
93
+ <th scope="row"><?php _e( 'Tracking ID' ) ?>:</th>
94
+ <td>
95
+ <input type="text"
96
+ name="<?php echo esc_attr( Ga_Admin::GA_WEB_PROPERTY_ID_MANUALLY_VALUE_OPTION_NAME ); ?>"
97
+ value="<?php echo esc_attr( $data[ Ga_Admin::GA_WEB_PROPERTY_ID_MANUALLY_VALUE_OPTION_NAME ] ); ?>"
98
+ id="ga_manually_input"/>&nbsp;
99
+ <div class="ga_warning">
100
+ <strong><?php _e( 'Warning' ); ?></strong>:&nbsp;<?php _e( 'If you enter your Tracking ID manually, Analytics statistics will not be shown.' ); ?>
101
+ <br>
102
+ <?php _e( 'We strongly recommend to authenticate with Google using the button above.' ); ?>
103
+ </div>
104
+ </td>
105
+
106
+ </tr>
107
+
108
+ <tr valign="top">
109
+ <th scope="row">
110
+ <label <?php echo ( ! Ga_Helper::are_terms_accepted() ) ? 'class="label-grey ga-tooltip"' : '' ?>><?php _e( 'Exclude Tracking for Roles' ) ?>
111
+ :
112
+ <span class="ga-tooltiptext ga-tt-abs"><?php _e( 'Please accept the terms to use this feature' ); ?></span>
113
+ </label>
114
+ </th>
115
+ <td>
116
+
117
+
118
+ <?php
119
+ if ( ! empty( $data['roles'] ) ) {
120
+ $roles = $data['roles'];
121
+ foreach ( $roles as $role ) {
122
+
123
+ ?>
124
+ <div class="checkbox">
125
+ <label class="ga_checkbox_label <?php echo ( ! Ga_Helper::are_terms_accepted() ) ? 'label-grey ga-tooltip' : ''; ?>"
126
+ for="checkbox_<?php echo $role['id']; ?>">
127
+ <input id="checkbox_<?php echo $role['id']; ?>" type="checkbox"
128
+ <?php echo ( ! Ga_Helper::are_terms_accepted() ) ? 'disabled="disabled"' : ''; ?>
129
+ name="<?php echo esc_attr( Ga_Admin::GA_EXCLUDE_ROLES_OPTION_NAME . "[" . $role['id'] . "]" ); ?>"
130
+ id="<?php echo esc_attr( $role['id'] ); ?>"
131
+ <?php echo esc_attr( ( $role['checked'] ? 'checked="checked"' : '' ) ); ?> />&nbsp;
132
+ <?php echo esc_html( $role['name'] ); ?>
133
+ <span class="ga-tooltiptext"><?php _e( 'Please accept the terms to use this feature' ); ?></span>
134
+ </label>
135
+ </div>
136
+ <?php
137
+ }
138
+ }
139
+ ?>
140
+
141
+ </td>
142
+ </tr>
143
+
144
+ </table>
145
+
146
+ <p class="submit">
147
+ <input type="submit" class="button-primary"
148
+ value="<?php _e( 'Save Changes' ) ?>"/>
149
+ </p>
150
+ </form>
151
+ </div>
152
+ </div>
view/statistics.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <div class="wrap ga-wrap">
2
+ <h2>Google Analytics - <?php _e( 'Dashboard' ); ?></h2>
3
+ <div class="ga_container" id="exTab2">
4
+ <?php echo $data ?>
5
+ </div>
6
+ </div>
view/stats.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap ga-wrap" id="ga-stats-container">
2
+ <div class="panel panel-default">
3
+ <div class="panel-heading"><strong><?php _e( "Pageviews - Last 7 days vs previous 7 days" ) ?></strong></div>
4
+ <div class="panel-body ga-chart">
5
+ <div id="chart_div" style="width: 100%;"></div>
6
+ <div class="ga-loader-wrapper stats-page">
7
+ <div class="ga-loader stats-page-loader"></div>
8
+ </div>
9
+ </div>
10
+ </div>
11
+
12
+ <div class="panel panel-default">
13
+ <div class="panel-heading"><strong><?php _e( "Comparison - Last 7 days vs previous 7 days" ) ?></strong></div>
14
+ <div class="panel-body">
15
+ <div id="boxes-container">
16
+ <div class="row">
17
+ <?php if ( ! empty( $boxes ) ) : ?>
18
+ <?php foreach ( $boxes as $box ) : ?>
19
+ <div class="col-md-3">
20
+ <div class="panel panel-default">
21
+ <div class="panel-body ga-box-centered">
22
+ <div style="font-size: 16px;"><?php echo $box['label'] ?></div>
23
+ <div style="color: <?php echo $box['color'] ?>; font-size: 24px;"><?php echo Ga_Helper::format_percent( $box['diff'] ); ?></div>
24
+ <div style="color: grey; font-size: 16px;"><?php echo $box['comparison'] ?></div>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ <?php endforeach; ?>
29
+ <?php endif; ?>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ <?php if ( ! empty( $sources ) ) : ?>
35
+ <div class="panel panel-default">
36
+ <div class="panel-heading"><strong><?php _e( "Top 5 Traffic Sources for the past 7 days" ) ?></strong></div>
37
+ <div class="panel-body">
38
+
39
+ <div id="table-container">
40
+ <table class="table table-bordered">
41
+ <tr>
42
+ <td colspan="2">
43
+ </td>
44
+ <th style="text-align: right;">
45
+ <?php _e( 'Pageviews' ); ?>
46
+ </th>
47
+ <th style="text-align: right;">
48
+ <?php echo '%'; ?>
49
+ </th>
50
+ </tr>
51
+ <tr>
52
+ <td style="width: 20%; font-size: 20px;text-align:center" colspan="2"></td>
53
+ <td class="col-md-2" style="text-align: right">
54
+ <div style="font-size: 16px;"><?php echo $sources['total'] ?></div>
55
+ <div style="color: grey; font-size: 10px;">% of
56
+ Total: <?php echo Ga_Helper::format_percent( ( ! empty( $sources['total'] ) ) ? number_format( $sources['sum'] / $sources['total'] * 100, 2, '.', ' ' ) : 100 ); ?>
57
+ (<?php echo $sources['sum'] ?>)
58
+ </div>
59
+ </td>
60
+ <td class="col-md-5" style="text-align: right">
61
+ <div style="font-size: 16px;"><?php echo $sources['total'] ?></div>
62
+ <div style="color: grey; font-size: 10px;">% of
63
+ Total: <?php echo Ga_Helper::format_percent( ( ! empty( $sources['total'] ) ) ? number_format( $sources['sum'] / $sources['total'] * 100, 2, '.', ' ' ) : 100 ); ?>
64
+ (<?php echo $sources['sum'] ?>)
65
+ </div>
66
+ </td>
67
+ </tr>
68
+ <?php foreach ( $sources['rows'] as $key => $source ): ?>
69
+ <tr>
70
+ <td style="width: 5%;text-align: right"><?php echo $key ?>.</td>
71
+ <td style="width: 10%;">
72
+ <?php if ( $source['name'] != '(direct) / (none)' ) : ?>
73
+ <a href="<?php echo $source['url'] ?>"
74
+ target="_blank"><?php echo $source['name'] ?></a>
75
+ <?php else: ?>
76
+ <?php echo $source['name'] ?>
77
+ <?php endif; ?>
78
+ </td>
79
+ <td style="text-align: right"><?php echo $source['number'] ?></td>
80
+ <td>
81
+ <div class="progress">
82
+ <div class="progress-bar" role="progressbar"
83
+ aria-valuenow="<?php echo $source['percent'] ?>" aria-valuemin="0"
84
+ aria-valuemax="100"
85
+ style="width: <?php echo $source['percent'] ?>%;"></div>
86
+ <span style="margin-left: 10px;"><?php echo Ga_Helper::format_percent( $source['percent'] ); ?></span>
87
+ </div>
88
+ </td>
89
+ </tr>
90
+ <?php endforeach; ?>
91
+ </table>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ <?php endif; ?>
96
+
97
+ <?php if ( ! empty( $chart ) ) : ?>
98
+ <script type="text/javascript">
99
+
100
+ ga_charts.init(function () {
101
+
102
+ var data = new google.visualization.DataTable();
103
+ data.addColumn('string', 'Day');
104
+ data.addColumn('number', '<?php echo $labels['thisWeek'] ?>');
105
+ data.addColumn({type: 'string', role: 'tooltip', 'p': {'html': true}});
106
+ data.addColumn('number', '<?php echo $labels['lastWeek'] ?>');
107
+ data.addColumn({type: 'string', role: 'tooltip', 'p': {'html': true}});
108
+
109
+ <?php foreach ( $chart as $row ) : ?>
110
+ data.addRow(['<?php echo $row['day'] ?>', <?php echo $row['current'] ?>, ga_charts.createTooltip('<?php echo $row['day'] ?>', '<?php echo $row['current'] ?>'), <?php echo $row['previous'] ?>, ga_charts.createTooltip('<?php echo $row['previous-day'] ?>', '<?php echo $row['previous'] ?>')]);
111
+ <?php endforeach; ?>
112
+ ga_charts.events(data);
113
+ ga_charts.drawChart(data);
114
+ ga_loader.hide();
115
+ }
116
+ );
117
+ </script>
118
+ <?php endif; ?>
119
+ </div>