Passed
Push — master ( aa437c...805644 )
by Chris
08:50
created

save_widget_state()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 16
c 0
b 0
f 0
nc 8
nop 0
dl 0
loc 26
rs 9.4222
1
<?php
2
/**
3
 * Manage the MonsterInsights Dashboard Widget
4
 *
5
 * @since 7.1
6
 *
7
 * @package MonsterInsights
8
 */
9
10
// Exit if accessed directly.
11
if ( ! defined( 'ABSPATH' ) ) {
12
	exit;
13
}
14
15
/**
16
 * Class MonsterInsights_Dashboard_Widget
17
 */
18
class MonsterInsights_Dashboard_Widget {
19
20
	const WIDGET_KEY = 'monsterinsights_reports_widget';
21
	/**
22
	 * The default options for the widget.
23
	 *
24
	 * @var array $default_options
25
	 */
26
	public static $default_options = array(
27
		'width'    => 'regular',
28
		'interval' => '30',
29
		'reports'  => array(
30
			'overview'    => array(
31
				'toppages'    => true,
32
				'newvsreturn' => true,
33
				'devices'     => true,
34
			),
35
			'publisher'   => array(
36
				'landingpages'   => false,
37
				'exitpages'      => false,
38
				'outboundlinks'  => false,
39
				'affiliatelinks' => false,
40
				'downloadlinks'  => false,
41
			),
42
			'ecommerce'   => array(
43
				'infobox'     => false, // E-commerce Overview.
44
				'products'    => false, // Top Products.
45
				'conversions' => false, // Top Products.
46
				'addremove'   => false, // Total Add/Remove.
47
				'days'        => false, // Time to purchase.
48
				'sessions'    => false, // Sessions to purchase.
49
			),
50
			'notice30day' => false,
51
		),
52
	);
53
	/**
54
	 * The widget options.
55
	 *
56
	 * @var array $options
57
	 */
58
	public $options;
59
60
	/**
61
	 * MonsterInsights_Dashboard_Widget constructor.
62
	 */
63
	public function __construct() {
64
		// Allow dashboard widget to be hidden on multisite installs
65
		$show_widget         = is_multisite() ? apply_filters( 'monsterinsights_show_dashboard_widget', true ) : true;
66
		if ( ! $show_widget ) {
67
			return false;
68
		}
69
70
		// Check if reports should be visible.
71
		$dashboards_disabled = monsterinsights_get_option( 'dashboards_disabled', false );
72
		if ( ! current_user_can( 'monsterinsights_view_dashboard' ) || 'disabled' === $dashboards_disabled ) {
73
			return false;
74
		}
75
76
		add_action( 'wp_dashboard_setup', array( $this, 'register_dashboard_widget' ) );
77
78
		add_action( 'admin_enqueue_scripts', array( $this, 'widget_scripts' ) );
79
80
		add_action( 'wp_ajax_monsterinsights_save_widget_state', array( $this, 'save_widget_state' ) );
81
82
		// Reminder notice.
83
		add_action( 'admin_footer', array( $this, 'load_notice' ) );
84
85
		add_action( 'wp_ajax_monsterinsights_mark_notice_closed', array( $this, 'mark_notice_closed' ) );
86
	}
87
88
	/**
89
	 * Register the dashboard widget.
90
	 */
91
	public function register_dashboard_widget() {
92
		global $wp_meta_boxes;
93
94
		wp_add_dashboard_widget(
95
			self::WIDGET_KEY,
96
			esc_html__( 'MonsterInsights', 'google-analytics-for-wordpress' ),
97
			array( $this, 'dashboard_widget_content' )
98
		);
99
100
		// Attept to place the widget at the top.
101
		$normal_dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
102
		$widget_instance  = array( self::WIDGET_KEY => $normal_dashboard[ self::WIDGET_KEY ] );
103
		unset( $normal_dashboard[ self::WIDGET_KEY ] );
104
		$sorted_dashboard                             = array_merge( $widget_instance, $normal_dashboard );
105
		$wp_meta_boxes['dashboard']['normal']['core'] = $sorted_dashboard;
106
	}
107
108
	/**
109
	 * Load the widget content.
110
	 */
111
	public function dashboard_widget_content() {
112
		$is_authed = ( MonsterInsights()->auth->is_authed() || MonsterInsights()->auth->is_network_authed() );
0 ignored issues
show
Bug Best Practice introduced by
The property $auth is declared protected in MonsterInsights_Lite. Since you implement __get, consider adding a @property or @property-read.
Loading history...
113
114
		if ( ! $is_authed ) {
115
			$this->widget_content_no_auth();
116
		} else {
117
			monsterinsights_settings_error_page( 'monsterinsights-dashboard-widget', '', '0' );
118
			monsterinsights_settings_inline_js();
119
		}
120
121
	}
122
123
	/**
124
	 * Message to display when the plugin is not authenticated.
125
	 */
126
	public function widget_content_no_auth() {
127
128
		$url = is_network_admin() ? network_admin_url( 'admin.php?page=monsterinsights-onboarding' ) : admin_url( 'admin.php?page=monsterinsights-onboarding' );
129
		?>
130
		<div class="mi-dw-not-authed">
131
			<h2><?php esc_html_e( 'Website Analytics is not Setup', 'google-analytics-for-wordpress' ); ?></h2>
132
			<p><?php esc_html_e( 'To see your website stats, please connect MonsterInsights to Google Analytics.', 'google-analytics-for-wordpress' ); ?></p>
133
			<a href="<?php echo esc_url( $url ); ?>" class="mi-dw-btn-large"><?php esc_html_e( 'Setup Website Analytics', 'google-analytics-for-wordpress' ); ?></a>
134
		</div>
135
		<?php
136
	}
137
138
139
	/**
140
	 * Load widget-specific scripts.
141
	 */
142
	public function widget_scripts() {
143
		$version_path = 'lite';
144
		$rtl          = is_rtl() ? '.rtl' : '';
145
146
		$screen = get_current_screen();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $screen is correct as get_current_screen() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
147
		if ( isset( $screen->id ) && 'dashboard' === $screen->id ) {
148
			global $wp_version;
149
			if ( ! defined( 'MONSTERINSIGHTS_LOCAL_WIDGET_JS_URL' ) ) {
150
				wp_enqueue_style( 'monsterinsights-vue-style-vendors', plugins_url( $version_path . '/assets/vue/css/chunk-vendors' . $rtl . '.css', MONSTERINSIGHTS_PLUGIN_FILE ), array(), monsterinsights_get_asset_version() );
151
				wp_enqueue_style( 'monsterinsights-vue-widget-style', plugins_url( $version_path . '/assets/vue/css/widget' . $rtl . '.css', MONSTERINSIGHTS_PLUGIN_FILE ), array(), monsterinsights_get_asset_version() );
152
				wp_enqueue_script( 'monsterinsights-vue-vendors', plugins_url( $version_path . '/assets/vue/js/chunk-vendors.js', MONSTERINSIGHTS_PLUGIN_FILE ), array(), monsterinsights_get_asset_version(), true );
153
				wp_enqueue_script( 'monsterinsights-vue-common', plugins_url( $version_path . '/assets/vue/js/chunk-common.js', MONSTERINSIGHTS_PLUGIN_FILE ), array(), monsterinsights_get_asset_version(), true );
154
			}
155
			$widget_js_url = defined( 'MONSTERINSIGHTS_LOCAL_WIDGET_JS_URL' ) && MONSTERINSIGHTS_LOCAL_WIDGET_JS_URL ? MONSTERINSIGHTS_LOCAL_WIDGET_JS_URL : plugins_url( $version_path . '/assets/vue/js/widget.js', MONSTERINSIGHTS_PLUGIN_FILE );
0 ignored issues
show
Bug introduced by
The constant MONSTERINSIGHTS_LOCAL_WIDGET_JS_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
156
			wp_register_script( 'monsterinsights-vue-widget', $widget_js_url, array(), monsterinsights_get_asset_version(), true );
157
			wp_enqueue_script( 'monsterinsights-vue-widget' );
158
159
			$plugins           = get_plugins();
160
			$wp_forms_url      = false;
161
			$wpforms_installed = false;
162
			if ( current_user_can( 'install_plugins' ) ) {
163
				$wpforms_key = 'wpforms-lite/wpforms.php';
164
				if ( array_key_exists( $wpforms_key, $plugins ) ) {
165
					$wp_forms_url      = wp_nonce_url( self_admin_url( 'plugins.php?action=activate&plugin=' . $wpforms_key ), 'activate-plugin_' . $wpforms_key );
166
					$wpforms_installed = true;
167
				} else {
168
					$wp_forms_url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=wpforms-lite' ), 'install-plugin_wpforms-lite' );
169
				}
170
			}
171
			$is_authed = ( MonsterInsights()->auth->is_authed() || MonsterInsights()->auth->is_network_authed() );
0 ignored issues
show
Bug Best Practice introduced by
The property $auth is declared protected in MonsterInsights_Lite. Since you implement __get, consider adding a @property or @property-read.
Loading history...
172
			wp_localize_script(
173
				'monsterinsights-vue-widget',
174
				'monsterinsights',
175
				array(
176
					'ajax'                => admin_url( 'admin-ajax.php' ),
177
					'nonce'               => wp_create_nonce( 'mi-admin-nonce' ),
178
					'network'             => is_network_admin(),
179
					'translations'        => wp_get_jed_locale_data( monsterinsights_is_pro_version() ? 'ga-premium' : 'google-analytics-for-wordpress' ),
180
					'assets'              => plugins_url( $version_path . '/assets/vue', MONSTERINSIGHTS_PLUGIN_FILE ),
181
					'shareasale_id'       => monsterinsights_get_shareasale_id(),
182
					'shareasale_url'      => monsterinsights_get_shareasale_url( monsterinsights_get_shareasale_id(), '' ),
183
					'addons_url'          => is_multisite() ? network_admin_url( 'admin.php?page=monsterinsights_network#/addons' ) : admin_url( 'admin.php?page=monsterinsights_settings#/addons' ),
184
					'widget_state'        => $this->get_options(),
185
					'wpforms_enabled'     => function_exists( 'wpforms' ),
186
					'wpforms_installed'   => $wpforms_installed,
187
					'wpforms_url'         => $wp_forms_url,
188
					'authed'              => $is_authed,
189
					// Used to add notices for future deprecations.
190
					'versions'            => array(
191
						'php_version'          => phpversion(),
192
						'php_version_below_54' => apply_filters( 'monsterinsights_temporarily_hide_php_52_and_53_upgrade_warnings', version_compare( phpversion(), '5.4', '<' ) ),
193
						'php_version_below_56' => apply_filters( 'monsterinsights_temporarily_hide_php_54_and_55_upgrade_warnings', version_compare( phpversion(), '5.6', '<' ) ),
194
						'php_update_link'      => monsterinsights_get_url( 'settings-notice', 'settings-page', 'https://www.monsterinsights.com/docs/update-php/' ),
195
						'wp_version'           => $wp_version,
196
						'wp_version_below_46'  => version_compare( $wp_version, '4.6', '<' ),
197
						'wp_version_below_49'  => version_compare( $wp_version, '4.9', '<' ),
198
						'wp_update_link'       => monsterinsights_get_url( 'settings-notice', 'settings-page', 'https://www.monsterinsights.com/docs/update-wordpress/' ),
199
					),
200
					'plugin_version'      => MONSTERINSIGHTS_VERSION,
201
					'is_admin'            => true,
202
					'reports_url'         => add_query_arg( 'page', 'monsterinsights_reports', admin_url( 'admin.php' ) ),
203
					'getting_started_url' => is_multisite() ? network_admin_url( 'admin.php?page=monsterinsights_network#/about/getting-started' ) : admin_url( 'admin.php?page=monsterinsights_settings#/about/getting-started' ),
204
					'wizard_url'          => admin_url( 'index.php?page=monsterinsights-onboarding' ),
205
				)
206
			);
207
208
			$this->remove_conflicting_asset_files();
209
		}
210
	}
211
212
	/**
213
	 * Remove assets added by other plugins which conflict.
214
	 */
215
	public function remove_conflicting_asset_files() {
216
		$scripts = array(
217
			'jetpack-onboarding-vendor', // Jetpack Onboarding Bluehost.
218
		);
219
220
		if ( ! empty( $scripts ) ) {
221
			foreach ( $scripts as $script ) {
222
				wp_dequeue_script( $script ); // Remove JS file.
223
				wp_deregister_script( $script );
224
			}
225
		}
226
	}
227
228
	/**
229
	 * Store the widget state in the db using an Ajax call.
230
	 */
231
	public function save_widget_state() {
232
233
		check_ajax_referer( 'mi-admin-nonce', 'nonce' );
234
235
		$default         = self::$default_options;
236
		$current_options = $this->get_options();
237
238
		$reports = $default['reports'];
239
		if ( isset( $_POST['reports'] ) ) {
240
			$reports = json_decode( sanitize_text_field( wp_unslash( $_POST['reports'] ) ), true );
241
			foreach ( $reports as $report => $reports_sections ) {
242
				$reports[ $report ] = array_map( 'boolval', $reports_sections );
243
			}
244
		}
245
246
		$options = array(
247
			'width'    => ! empty( $_POST['width'] ) ? sanitize_text_field( wp_unslash( $_POST['width'] ) ) : $default['width'],
248
			'interval' => ! empty( $_POST['interval'] ) ? absint( wp_unslash( $_POST['interval'] ) ) : $default['interval'],
249
			'reports'  => $reports,
250
			'notice30day' => $current_options['notice30day'],
251
		);
252
253
		array_walk( $options, 'sanitize_text_field' );
254
		update_user_meta( get_current_user_id(), 'monsterinsights_user_preferences', $options );
255
256
		wp_send_json_success();
257
258
	}
259
260
	/**
261
	 * Load & store the dashboard widget settings.
262
	 *
263
	 * @return array
264
	 */
265
	public function get_options() {
266
		if ( ! isset( $this->options ) ) {
267
			$this->options = self::wp_parse_args_recursive( get_user_meta( get_current_user_id(), 'monsterinsights_user_preferences', true ), self::$default_options );
0 ignored issues
show
Bug introduced by
It seems like get_user_meta(get_curren...ser_preferences', true) can also be of type false; however, parameter $a of MonsterInsights_Dashboar..._parse_args_recursive() does only seem to accept array|object|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

267
			$this->options = self::wp_parse_args_recursive( /** @scrutinizer ignore-type */ get_user_meta( get_current_user_id(), 'monsterinsights_user_preferences', true ), self::$default_options );
Loading history...
268
		}
269
270
		return apply_filters( 'monsterinsights_dashboard_widget_options', $this->options );
271
272
	}
273
274
	/**
275
	 * Recursive wp_parse_args.
276
	 *
277
	 * @param string|array|object $a Value to merge with $b.
278
	 * @param array               $b The array with the default values.
279
	 *
280
	 * @return array
281
	 */
282
	public static function wp_parse_args_recursive( $a, $b ) {
283
		$a      = (array) $a;
284
		$b      = (array) $b;
285
		$result = $b;
286
		foreach ( $a as $k => &$v ) {
287
			if ( is_array( $v ) && isset( $result[ $k ] ) ) {
288
				$result[ $k ] = self::wp_parse_args_recursive( $v, $result[ $k ] );
289
			} else {
290
				$result[ $k ] = $v;
291
			}
292
		}
293
294
		return $result;
295
	}
296
297
	/**
298
	 * Reminder notice markup.
299
	 */
300
	public function load_notice() {
301
302
		$screen = get_current_screen();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $screen is correct as get_current_screen() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
303
		$ua     = monsterinsights_get_ua();
304
		if ( isset( $screen->id ) && 'dashboard' === $screen->id && ! empty( $ua ) ) {
305
			?>
306
			<div id="monsterinsights-reminder-notice"></div>
307
			<?php
308
		}
309
310
	}
311
312
	/**
313
	 * Mark notice as dismissed.
314
	 */
315
	public function mark_notice_closed() {
316
317
		check_ajax_referer( 'mi-admin-nonce', 'nonce' );
318
		$options                = $this->get_options();
319
		$options['notice30day'] = time();
320
		update_user_meta( get_current_user_id(), 'monsterinsights_user_preferences', $options );
321
322
		wp_send_json_success();
323
	}
324
}
325