<?php
if (!defined('ABSPATH')) {
die('No direct access.');
}
/**
* Class to handle analytics for MetaSlider. Will act more like a controller
*/
class MetaSlider_Analytics
{
/**
* @var Appsero\Insights $appsero
*/
public $appsero;
/**
* @var array $whereToShow
*/
public $whereToShow = array('plugins', 'dashboard');
/**
* Start various analytics systems
*
* @return void
*/
public function __construct()
{
// Since only one service for now, keep it simple and just load it directly
$this->boot(
apply_filters('metaslider_appsero_app_key', 'c3c10cf6-1a8f-4d7f-adf3-6bbbc5fe2885'),
apply_filters('metaslider_appsero_app_name', 'MetaSlider'),
apply_filters('metaslider_appsero_app_path', METASLIDER_PATH . 'ml-slider.php')
);
// Show notice only if they are not already opt in (pro will override this to show the opt-out notice once)
add_action('wp_ajax_handle_optin_action', array($this, 'handleOptinDismiss'));
add_action('admin_enqueue_scripts', array($this, 'addAdminNonce'));
}
/**
* Show the dang thing
*
* @return void
*/
public function addAdminNonce()
{
wp_register_script('metaslider-optin-extra-js', '');
wp_enqueue_script('metaslider-optin-extra-js');
$nonce = wp_create_nonce('metaslider_optin_notice_nonce');
$this->wp_add_inline_script(
'metaslider-optin-extra-js',
"window.metaslider_optin_notice_nonce = '{$nonce}'"
);
}
/**
* Handle the skip on the notice
*
* @return void
*/
public function handleOptinDismiss()
{
if (! isset($_REQUEST['_wpnonce']) || ! wp_verify_nonce(sanitize_key($_REQUEST['_wpnonce']), 'metaslider_optin_notice_nonce')) {
wp_send_json_error(array(
'message' => esc_html__('The security check failed. Please refresh the page and try again.', 'ml-slider')
), 401);
}
// They opted in, so we can instruct Appsero to communicate with the server
if (isset($_REQUEST['activate']) && filter_var($_REQUEST['activate'], FILTER_VALIDATE_BOOLEAN)) {
update_option('metaslider_optin_via', 'notice', true);
$this->optin();
}
update_user_option(get_current_user_id(), 'metaslider_optin_notice_dismissed', time());
wp_send_json_success();
}
/**
* Start Appsero's insights client
*
* @var string $key - The app key
* @var string $name - The app slug
* @var string $path - The app path to main file
*
* @return MetaSlider_Analytics
*/
public function boot($key, $name, $path)
{
if (is_multisite()) {
return $this;
}
if (!class_exists('Appsero\\Client')) {
require_once(METASLIDER_PATH . 'lib/appsero/src/Client.php');
}
add_filter('ml-slider_tracker_data', array($this, 'filterTrackingData'));
$client = new Appsero\Client($key, $name, $path);
$this->appsero = $client->insights();
return $this;
}
/**
* Start Appsero's insights client
*
* @return void
*/
public function load()
{
if (!$this->appsero) {
return;
}
if (self::siteIsOptin()) {
// If the user has opted in to sharing data with MetaSlider, we can skip the Appsero opt in
// $this->appsero->hide_notice()->init()->optinIfNotAlready(); <-- would be nice
$this->appsero->hide_notice()->add_extra(array($this, 'extraDataToCollect'))->init();
if (get_option('ml-slider_allow_tracking') === 'no') {
$this->appsero->optin();
}
} else {
// Here we are hiding the notice for users that aren't opted in, because we are serving our own notices
// We will make sure they are opted out from appsero too. Note, this initializes, but that's just for
// showing the notice and doesn't do any actual tracking unless the user approves.
$this->appsero->hide_notice()->add_extra(array($this, 'extraDataToCollect'))->init();
if (get_option('ml-slider_allow_tracking') === 'yes') {
$this->appsero->optout();
}
}
}
/**
* Filter Appsero's data
* - We want the user that opts in, not the first admin user
*
* @var array $data - The data from Appsero
*
* @return array
*/
public function filterTrackingData($data)
{
if (!$extras = get_option('metaslider_optin_user_extras')) {
return $data;
}
if ($admin_user = get_userdata($extras['id'])) {
$data['admin_email'] = $admin_user->user_email;
$data['first_name'] = $admin_user->first_name ? $admin_user->first_name : $admin_user->display_name;
$data['last_name'] = $admin_user->last_name;
}
return $data;
}
/**
* Add some extra fields - This is called async now so no need to cache it.
*
* @return array
*/
public function extraDataToCollect()
{
try {
$sliders_count = new WP_Query(array(
'post_type' => 'ml-slider',
'post_status' => array('inherit', 'publish'),
'suppress_filters' => 1,
'posts_per_page' => -1
));
$date_activated = new DateTime();
$date_activated->setTimestamp((int) get_option('ms_hide_all_ads_until'));
$date_activated->modify('-2 week');
$date_activated = $date_activated->getTimeStamp();
$metagallery = new WP_Query(array('post_type' => 'metagallery', 'posts_per_page' => -1));
$data = array(
'has_pro_installed' => metaslider_pro_is_installed() ? metaslider_pro_version() : 'false',
'cancelled_tour_on' => get_option('metaslider_tour_cancelled_on'),
'optin_user_info' => get_option('metaslider_optin_user_extras'),
'optin_via' => get_option('metaslider_optin_via'),
'slider_count' => $sliders_count ? $sliders_count->found_posts : 0,
'first_activated_on' => $date_activated > 0 ? $date_activated : 0,
'metagallery_opened' => (int) get_option('metagallery_opened'),
'metagallery_count' => (int) $metagallery->post_count
);
return $data;
} catch (\Exception $e) {
return array();
}
}
/**
* Helper method for checking whether the site has opted in
*
* @return boolean
*/
public static function siteIsOptin()
{
// Users of non-WP.org plugins can manage the opt in state manually here
if (apply_filters('metaslider_force_optout', false)) {
return self::updateOptinStatusTo(false);
}
if (apply_filters('metaslider_force_optin', false)) {
return self::updateOptinStatusTo(true);
}
$settings = get_option('metaslider_global_settings');
return isset($settings['optIn']) && (int) $settings['optIn'] > 0;
}
/**
* Helper method for setting the opt in status
*
* @var string $status - Whether the user is opt in ('true') or opt out ('false')
*
* @return boolean - The same as $status
*/
public static function updateOptinStatusTo($status)
{
$settings = get_option('metaslider_global_settings');
$settings['optIn'] = filter_var($status, FILTER_VALIDATE_BOOLEAN) ? 1 : 0;
update_option('metaslider_global_settings', $settings, true);
return filter_var($status, FILTER_VALIDATE_BOOLEAN);
}
/**
* Method to optin
* Note: Need to init() before optin/optout
*
* @return void
*/
public function optin()
{
$current_user = wp_get_current_user();
//check if there is a custom email added for optin
$optin_email = get_option('metaslider_optin_email');
if (!empty($optin_email)) {
$use_email = filter_var($optin_email, FILTER_SANITIZE_EMAIL);
} else {
$use_email = $current_user->user_email;
}
update_option('metaslider_optin_user_extras', array(
'id' => $current_user->ID,
'email' => $use_email,
'ip' => isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field($_SERVER['REMOTE_ADDR']) : '', // phpcs:ignore WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders
'time' => time()
));
if ($this->appsero) {
$this->appsero->hide_notice()->add_extra(array($this, 'extraDataToCollect'))->init();
$this->appsero->optin();
}
self::updateOptinStatusTo('true');
}
/**
* Method to optout
* Note: Need to init() before optin/optout
*
* @return void
*/
public function optout()
{
// We could possibly track who opted out, but for now just clear it
update_option('metaslider_optin_user_extras', array());
if ($this->appsero) {
$this->appsero->hide_notice()->init();
$this->appsero->optout();
}
self::updateOptinStatusTo('false');
}
/**
* Polyfill to handle the wp_add_inline_script() function.
*
* @param string $handle The script identifier
* @param string $data The script to add, without <script> tags
* @param string $position Whether to output before or after
*
* @return object|bool
*/
public function wp_add_inline_script($handle, $data, $position = 'after')
{
if (function_exists('wp_add_inline_script')) {
return wp_add_inline_script($handle, $data, $position);
}
global $wp_scripts;
if (!$data) {
return false;
}
// First fetch any existing scripts
$script = $wp_scripts->get_data($handle, 'data');
// Append to the end
$script .= $data;
return $wp_scripts->add_data($handle, 'data', $script);
}
}