set_slug( $slug ); $this->set_plugin_file( $plugin_file ); $this->set_options( $options ); $this->hooks(); } /** * Install the hooks required to run periodic update checks and inject update info * into WP data structures. * Also other hooks related to the automatic updates (such as checking agains API and what not (@from Darren) */ public function hooks() { // Override requests for plugin information add_filter( 'plugins_api', [ $this, 'inject_info' ], 10, 3 ); // Check for updates when the WP updates are checked and inject our update if needed. // Only add filter if the TRIBE_DISABLE_PUE constant is not set as true and where // the context is not 'service' if ( ( ! defined( 'TRIBE_DISABLE_PUE' ) || true !== TRIBE_DISABLE_PUE ) && 'service' !== $this->context ) { add_filter( 'pre_set_site_transient_update_plugins', [ $this, 'check_for_updates' ] ); } add_filter( 'tribe_licensable_addons', [ $this, 'build_addon_list' ] ); add_action( 'tribe_license_fields', [ $this, 'do_license_key_fields' ] ); add_action( 'tribe_settings_after_content_tab_licenses', [ $this, 'do_license_key_javascript' ] ); add_action( 'tribe_settings_success_message', [ $this, 'do_license_key_success_message' ], 10, 2 ); add_action( 'load-plugins.php', [ $this, 'remove_default_inline_update_msg' ], 50 ); // Key validation add_filter( 'tribe_settings_save_field_value', [ $this, 'check_for_api_key_error' ], 10, 3 ); add_action( 'wp_ajax_pue-validate-key_' . $this->get_slug(), [ $this, 'ajax_validate_key' ] ); add_filter( 'tribe-pue-install-keys', [ $this, 'return_install_key' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'maybe_display_json_error_on_plugins_page' ], 1 ); add_action( 'admin_init', [ $this, 'general_notifications' ] ); // Package name add_filter( 'upgrader_pre_download', [ Tribe__PUE__Package_Handler::instance(), 'filter_upgrader_pre_download' ], 5, 3 ); } /********************** Getter / Setter Functions **********************/ /** * Get the slug * * @return string */ public function get_slug() { return apply_filters( 'pue_get_slug', $this->slug ); } /** * Set the slug * * @param string $slug */ private function set_slug( $slug = '' ) { $this->slug = $slug; $clean_slug = str_replace( '-', '_', $this->slug ); $this->dismiss_upgrade = 'pu_dismissed_upgrade_' . $clean_slug; $this->pue_install_key = 'pue_install_key_' . $clean_slug; } /** * Get the PUE update API endpoint url * * @return string */ public function get_pue_update_url() { $pue_update_url = 'https://pue.theeventscalendar.com'; if ( defined( 'PUE_UPDATE_URL' ) ) { $pue_update_url = PUE_UPDATE_URL; } $pue_update_url = apply_filters( 'pue_get_update_url', $pue_update_url, $this->get_slug() ); $pue_update_url = untrailingslashit( $pue_update_url ); return $pue_update_url; } /** * Get the plugin file path * * @return string */ public function get_plugin_file() { return apply_filters( 'pue_get_plugin_file', $this->plugin_file, $this->get_slug() ); } /** * Set the plugin file path * * @param string $plugin_file */ private function set_plugin_file( $plugin_file = '' ) { if ( ! empty( $plugin_file ) ) { $this->plugin_file = $plugin_file; return; } $slug = $this->get_slug(); if ( ! empty( $slug ) ) { $this->plugin_file = $slug . '/' . $slug . '.php'; } } /** * Set the plugin name * * @param string $plugin_name */ private function set_plugin_name( $plugin_name = '' ) { if ( ! empty( $plugin_name ) ) { $this->plugin_name = $plugin_name; } else { //get name from plugin file itself if ( ! function_exists( 'get_plugins' ) ) { require_once( ABSPATH . 'wp-admin/includes/plugin.php' ); } // Prevents get_plugins from throwing a weird notice if ( ! file_exists( WP_PLUGIN_DIR . '/' . $this->get_plugin_file() ) ) { return; } $plugin_details = explode( '/', $this->get_plugin_file() ); $plugin_folder = get_plugins( '/' . $plugin_details[0] ); $this->plugin_name = isset( $plugin_details[1] ) && isset( $plugin_folder[ $plugin_details[1] ] ) ? $plugin_folder[ $plugin_details[1] ]['Name'] : null; } } /** * Get the plugin name * * @return string */ public function get_plugin_name() { if ( empty( $this->plugin_name ) ) { $this->set_plugin_name(); } return apply_filters( 'pue_get_plugin_name', $this->plugin_name, $this->get_slug() ); } /** * Set all the PUE instantiation options * * @param array $options */ private function set_options( $options = [] ) { $options = wp_parse_args( $options, [ 'pue_option_name' => 'external_updates-' . $this->get_slug(), 'apikey' => '', 'check_period' => 12, 'context' => 'component', 'plugin_name' => '', ] ); $this->pue_option_name = $options['pue_option_name']; $this->check_period = (int) $options['check_period']; $this->context = $options['context']; $this->plugin_name = $options['plugin_name']; } /** * Set all the download query array * * @param array $download_query */ private function set_download_query( $download_query = [] ) { if ( ! empty( $download_query ) ) { $this->download_query = $download_query; return; } // plugin slug $this->download_query['plugin'] = sanitize_text_field( $this->get_slug() ); // include current version $this->download_query['installed_version'] = sanitize_text_field( $this->get_installed_version() ); $this->download_query['domain'] = sanitize_text_field( $this->get_domain() ); // get general stats $stats = $this->get_stats(); $this->download_query['multisite'] = $stats['network']['multisite']; $this->download_query['network_activated'] = $stats['network']['network_activated']; $this->download_query['active_sites'] = $stats['network']['active_sites']; $this->download_query['wp_version'] = $stats['versions']['wp']; // the following is for install key inclusion (will apply later with PUE addons.) $this->download_query['key'] = sanitize_text_field( $this->get_key() ); $this->download_query['dk'] = sanitize_text_field( $this->get_key( 'default' ) ); $this->download_query['o'] = sanitize_text_field( $this->get_key( 'any', 'origin' ) ); } /** * Get the download_query args * * @return array */ public function get_download_query() { if ( empty( $this->download_query ) ) { $this->set_download_query(); } return apply_filters( 'pue_get_download_query', $this->download_query, $this->get_slug() ); } /** * Set all the validate query array * * @param array $validate_query */ private function set_validate_query( $validate_query = [] ) { if ( ! empty( $validate_query ) ) { $this->validate_query = $validate_query; return; } // the following is for install key inclusion (will apply later with PUE addons.) $this->validate_query['key'] = sanitize_text_field( $this->get_key() ); // include default key $this->validate_query['default_key'] = sanitize_text_field( $this->get_key( 'default' ) ); // include license origin $this->validate_query['license_origin'] = sanitize_text_field( $this->get_key( 'any', 'origin' ) ); // plugin slug $this->validate_query['plugin'] = sanitize_text_field( $this->get_slug() ); // include current version $this->validate_query['version'] = sanitize_text_field( $this->get_installed_version() ); // include current domain $this->validate_query['domain'] = sanitize_text_field( $this->get_domain() ); // include plugin stats $this->validate_query['stats'] = $this->get_stats(); } /** * Get the validate_query args * * @return array */ public function get_validate_query() { if ( empty( $this->validate_query ) ) { $this->set_validate_query(); } return apply_filters( 'pue_get_validate_query', $this->validate_query, $this->get_slug() ); } /** * Get current domain * * @return string */ public function get_domain() { $domain = self::$domain; if ( empty( $domain ) ) { if ( isset( $_SERVER['SERVER_NAME'] ) ) { $domain = $_SERVER['SERVER_NAME']; } if ( is_multisite() ) { // For multisite, return the network-level siteurl $domain = $this->get_network_domain(); } self::$domain = $domain; } return $domain; } /********************** General Functions **********************/ /** * Compile a list of addons * * @param array $addons list of addons * * @return array list of addons */ public function build_addon_list( $addons = [] ) { $addons[] = $this->get_plugin_name(); return $addons; } /** * Inserts license key fields on license key page * * @param array $fields List of fields * * @return array Modified list of fields. */ public function do_license_key_fields( $fields ) { // common fields whether licenses should be hidden or not $to_insert = [ $this->pue_install_key . '-heading' => [ 'type' => 'heading', 'label' => $this->get_plugin_name(), ], ]; $no_license_tooltip = esc_html__( 'A valid license key is required for support and updates', 'tribe-common' ); if ( 'event-aggregator' === $this->get_slug() ) { $no_license_tooltip = sprintf( esc_html__( '%1$sBuy a license%2$s for the Event Aggregator service to access additional import features.', 'tribe-common' ), '', '' ); } // we want to inject the following license settings at the end of the licenses tab if ( $this->should_show_network_editable_license() ) { $to_insert[ $this->pue_install_key ] = [ 'type' => 'license_key', 'size' => 'large', 'validation_type' => 'license_key', 'label' => sprintf( esc_attr__( 'License Key', 'tribe-common' ) ), 'default' => $this->get_key( 'default' ), 'tooltip' => $no_license_tooltip, 'parent_option' => false, 'network_option' => true, ]; } elseif ( $this->should_show_subsite_editable_license() ) { $to_insert[ $this->pue_install_key ] = [ 'type' => 'license_key', 'size' => 'large', 'validation_type' => 'license_key', 'label' => sprintf( esc_attr__( 'License Key', 'tribe-common' ) ), 'default' => $this->get_key( 'default' ), 'tooltip' => $no_license_tooltip, 'parent_option' => false, 'network_option' => false, ]; } elseif ( $this->should_show_overrideable_license() ) { $to_insert[ $this->pue_install_key . '-state' ] = [ 'type' => 'html', 'label' => sprintf( esc_attr__( 'License Key Status:', 'tribe-common' ) ), 'label_attributes' => [ 'style' => 'width:auto;' ], 'html' => sprintf( '
%s
', $this->get_network_license_state_string() ), ]; $override_id = $this->pue_install_key . '-override'; $to_insert[ $override_id ] = [ 'type' => 'checkbox_bool', 'label' => esc_html__( 'Override network license key', 'tribe-common' ), 'tooltip' => esc_html__( 'Check this box if you wish to override the network license key with your own', 'tribe-common' ), 'default' => false, 'validation_type' => 'boolean', 'parent_option' => false, 'attributes' => [ 'id' => $override_id . '-field' ], ]; $to_insert[ $this->pue_install_key ] = [ 'type' => 'license_key', 'size' => 'large', 'validation_type' => 'license_key', 'label' => sprintf( esc_attr__( 'Site License Key', 'tribe-common' ) ), 'tooltip' => $no_license_tooltip, 'parent_option' => false, 'network_option' => false, 'class' => 'tribe-dependent', 'fieldset_attributes' => [ 'data-depends' => '#' . $override_id . '-field', 'data-condition-checked' => true, ], ]; } else { $to_insert[ $this->pue_install_key . '-state' ] = [ 'type' => 'html', 'label' => sprintf( esc_attr__( 'License Key Status:', 'tribe-common' ) ), 'label_attributes' => [ 'style' => 'width:auto;' ], 'html' => sprintf( '%s
', $this->get_network_license_state_string() ), ]; } $fields = self::array_insert_after_key( 'tribe-form-content-start', $fields, $to_insert ); return $fields; } /** * Inserts the javascript that makes the ajax checking * work on the license key page * */ public function do_license_key_javascript() { ?>' . esc_html__( 'License key(s) updated.', 'tribe-common' ) . '
'; } /** * Build stats for endpoints * * @return array */ public function build_stats() { global $wpdb; $stats = [ 'versions' => [ 'wp' => sanitize_text_field( $GLOBALS['wp_version'] ), ], 'network' => [ 'multisite' => 0, 'network_activated' => 0, 'active_sites' => 1, ], ]; if ( is_multisite() ) { $sql_count = " SELECT COUNT( `blog_id` ) FROM `{$wpdb->blogs}` WHERE `public` = '1' AND `archived` = '0' AND `spam` = '0' AND `deleted` = '0' "; $stats['network']['multisite'] = 1; $stats['network']['network_activated'] = (int) $this->is_plugin_active_for_network(); $stats['network']['active_sites'] = (int) $wpdb->get_var( $sql_count ); } self::$stats = $stats; return $stats; } /** * Build full stats for endpoints * * @param array $stats Initial stats * * @return array */ public function build_full_stats( $stats ) { global $wpdb; $theme = wp_get_theme(); $current_offset = (int) get_option( 'gmt_offset', 0 ); $tzstring = get_option( 'timezone_string' ); // Remove old Etc mappings. Fallback to gmt_offset. if ( false !== strpos( $tzstring, 'Etc/GMT' ) ) { $timezone = ''; } // Create a UTC+- zone if no timezone string exists if ( empty( $tzstring ) ) { if ( 0 === $current_offset ) { $timezone = 'UTC+0'; } elseif ( $current_offset < 0 ) { $timezone = 'UTC' . $current_offset; } else { $timezone = 'UTC+' . $current_offset; } } $stats['versions'] = [ 'wp' => sanitize_text_field( $GLOBALS['wp_version'] ), 'php' => sanitize_text_field( phpversion() ), 'mysql' => sanitize_text_field( $wpdb->db_version() ), ]; $stats['theme'] = [ 'name' => sanitize_text_field( $theme->get( 'Name' ) ), 'version' => sanitize_text_field( $theme->get( 'Version' ) ), 'stylesheet' => sanitize_text_field( $theme->get_stylesheet() ), 'template' => sanitize_text_field( $theme->get_template() ), ]; $stats['site_language'] = sanitize_text_field( get_locale() ); $stats['user_language'] = sanitize_text_field( get_user_locale() ); $stats['is_public'] = (int) get_option( 'blog_public', 0 ); $stats['wp_debug'] = (int) ( defined( 'WP_DEBUG' ) && WP_DEBUG ); $stats['site_timezone'] = sanitize_text_field( $timezone ); $stats['totals'] = [ 'all_post_types' => (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$wpdb->posts}`" ), 'events' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE post_type = %s", 'tribe_events' ) ), 'venues' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE post_type = %s", 'tribe_venue' ) ), 'organizers' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->posts}` WHERE post_type = %s", 'tribe_organizer' ) ), 'event_categories' => (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$wpdb->term_taxonomy}` WHERE taxonomy = %s", 'tribe_events_cat' ) ), ]; self::$stats_full = $stats; return $stats; } /** * Build and get the stats * * @return array */ public function get_stats() { $stats = self::$stats; if ( empty( $stats ) ) { $stats = $this->build_stats(); } /** * Allow full stats data to be built and sent. * * @param boolean $use_full_stats Whether to send full stats * * @since 4.5.1 */ $use_full_stats = apply_filters( 'pue_use_full_stats', false ); if ( $use_full_stats ) { $stats_full = self::$stats_full; if ( empty( $stats_full ) ) { $stats = $this->build_full_stats( $stats ); } } /** * Filter stats and allow plugins to add their own stats * for tracking specific points of data. * * @param array $stats Stats gathered by PUE Checker class * @param boolean $use_full_stats Whether to send full stats * @param \Tribe__PUE__Checker $checker PUE Checker class object * * @since 4.5.1 */ $stats = apply_filters( 'pue_stats', $stats, $use_full_stats, $this ); return $stats; } /** * Get current license key, optionally of a specific type. * * @param string $type The type of key to get (any, network, local, default) * @param string $return_type The type of data to return (key, origin) * * @return string */ public function get_key( $type = 'any', $return_type = 'key' ) { $license_key = ''; $license_origin = 'm'; /* * Even if we have a network key if the plugin is not active on the network then it should * not be used. */ if ( ( 'network' === $type || 'any' === $type ) && is_multisite() && $this->is_plugin_active_for_network() ) { $license_key = get_network_option( null, $this->pue_install_key, '' ); } if ( ( 'local' === $type || 'any' === $type ) && empty( $license_key ) ) { $license_key = get_option( $this->pue_install_key, '' ); } if ( empty( $license_key ) && ( 'default' === $type || 'any' === $type ) ) { $autoloader = Tribe__Autoloader::instance(); $class_name = $autoloader->get_prefix_by_slug( $this->get_slug() ); if ( $class_name ) { $is_namespaced = false !== strpos( $class_name, '\\' ); if ( $is_namespaced ) { // Handle class prefixes like Tribe\Plugin\. $class_name .= 'PUE\Helper'; } else { // Handle class prefixes like Tribe__Plugin__. $class_name .= 'PUE__Helper'; } if ( constant( $class_name . '::DATA' ) ) { $license_key = constant( $class_name . '::DATA' ); $license_origin = 'e'; } } } if ( 'origin' === $return_type ) { if ( 'm' === $license_origin ) { $default_key = $this->get_key( 'default' ); if ( $license_key !== $default_key ) { $license_origin = 'o'; } } return $license_origin; } return $license_key; } /** * Update license key for specific type of license. * * @param string $license_key The new license key value * @param string $type The type of key to update (network or local) */ public function update_key( $license_key, $type = 'local' ) { if ( 'network' === $type && is_multisite() ) { update_network_option( null, $this->pue_install_key, sanitize_text_field( $license_key ) ); } elseif ( 'local' === $type ) { update_option( $this->pue_install_key, sanitize_text_field( $license_key ) ); } } /** * Checks for the license key status with MT servers. * * @param string $key * @param bool $network Whether the key to check for is a network one or not. * * @return array An associative array containing the license status response. */ public function validate_key( $key, $network = false ) { $response = []; $response['status'] = 0; if ( ! $key ) { $response['message'] = sprintf( esc_html__( 'Hmmm... something\'s wrong with this validator. Please contact %ssupport%s.', 'tribe-common' ), '', '' ); return $response; } $query_args = $this->get_validate_query(); $query_args['key'] = sanitize_text_field( $key ); // This method is primarily used during when validating keys by ajax, before they are // formally committed or saved by the user: for that reason we call request_info() // rather than license_key_status() as at this stage invalid or missing keys should // not result in admin notices being generated $plugin_info = $this->request_info( $query_args ); $expiration = isset( $plugin_info->expiration ) ? $plugin_info->expiration : __( 'unknown date', 'tribe-common' ); $pue_notices = Tribe__Main::instance()->pue_notices(); $plugin_name = $this->get_plugin_name(); if ( empty( $plugin_info ) ) { $response['message'] = __( 'Sorry, key validation server is not available.', 'tribe-common' ); } elseif ( isset( $plugin_info->api_expired ) && 1 === (int) $plugin_info->api_expired ) { $response['message'] = $this->get_license_expired_message(); $response['api_expired'] = true; } elseif ( isset( $plugin_info->api_upgrade ) && 1 === (int) $plugin_info->api_upgrade ) { $response['message'] = $this->get_api_message( $plugin_info ); $response['api_upgrade'] = true; } elseif ( isset( $plugin_info->api_invalid ) && 1 === (int) $plugin_info->api_invalid ) { $response['message'] = $this->get_api_message( $plugin_info ); $response['api_invalid'] = true; } else { $key_type = 'local'; if ( $network ) { $key_type = 'network'; } $current_install_key = $this->get_key( $key_type ); if ( $current_install_key && $current_install_key === $query_args['key'] ) { $default_success_msg = esc_html( sprintf( __( 'Valid Key! Expires on %s', 'tribe-common' ), $expiration ) ); } else { // Set the key $this->update_key( $query_args['key'], $key_type ); $default_success_msg = esc_html( sprintf( __( 'Thanks for setting up a valid key. It will expire on %s', 'tribe-common' ), $expiration ) ); //Set SysInfo Key on Tec.com After Successful Validation of License $optin_key = get_option( 'tribe_systeminfo_optin' ); if ( $optin_key ) { Tribe__Support::send_sysinfo_key( $optin_key, $query_args['domain'], false, true ); } } $pue_notices->clear_notices( $plugin_name ); $response['status'] = isset( $plugin_info->api_message ) ? 2 : 1; $response['message'] = isset( $plugin_info->api_message ) ? $plugin_info->api_message : $default_success_msg; $response['expiration'] = esc_html( $expiration ); if ( isset( $plugin_info->daily_limit ) ) { $response['daily_limit'] = intval( $plugin_info->daily_limit ); } } $response['message'] = wp_kses( $response['message'], 'data' ); return $response; } public function get_license_expired_message() { return '' . __( 'Renew Your License Now', 'tribe-common' ) . '' . __( ' (opens in a new window)', 'tribe-common' ) . ''; } /** * Echo JSON results for key validation */ public function ajax_validate_key() { $key = isset( $_POST['key'] ) ? wp_unslash( $_POST['key'] ) : null; $nonce = isset( $_POST['_wpnonce'] ) ? wp_unslash( $_POST['_wpnonce'] ) : null; if ( empty( $nonce ) || false === wp_verify_nonce( $nonce, 'pue-validate-key_' . $this->get_slug() ) ) { $response = [ 'status' => 0, 'message' => __( 'Please refresh the page and try your request again.', 'tribe-common' ), ]; } else { $response = $this->validate_key( $key ); } echo json_encode( $response ); exit; } /** * Processes variable substitutions for server-side API message. * * @param Tribe__PUE__Plugin_Info $info * * @return string */ private function get_api_message( $info ) { // this default message should never show, but is here as a fallback just in case. $message = sprintf( esc_html__( 'There is an update for %s. You\'ll need to %scheck your license%s to have access to updates, downloads, and support.', 'tribe-common' ), $this->get_plugin_name(), '', '' ); if ( ! empty( $info->api_inline_invalid_message ) ) { $message = wp_kses( $info->api_inline_invalid_message, 'post' ); } $message = str_replace( '%plugin_name%', $this->get_plugin_name(), $message ); $message = str_replace( '%plugin_slug%', $this->get_slug(), $message ); $message = str_replace( '%update_url%', $this->get_pue_update_url() . '/', $message ); $message = str_replace( '%version%', $info->version, $message ); $message = str_replace( '%changelog%', 'what\'s new', $message ); return $message; } /** * Whether the plugin is network activated and licensed or not. * * @return bool */ public function is_network_licensed() { $is_network_licensed = false; if ( ! is_network_admin() && $this->is_plugin_active_for_network() ) { $network_key = $this->get_key( 'network' ); $local_key = $this->get_key( 'local' ); // Check whether the network is licensed and NOT overridden by local license if ( $network_key && ( empty( $local_key ) || $local_key === $network_key ) ) { $is_network_licensed = true; } } return $is_network_licensed; } /** * Returns tet name of the option that stores the license key. * * @return string */ public function get_license_option_key() { return $this->pue_install_key; } private function get_api_update_message() { $plugin_info = $this->plugin_info; if ( ! isset( $plugin_info->api_invalid_message ) ) { return false; } $message = sprintf( esc_html__( 'There is an update for %s. %sRenew your license%s to get access to bug fixes, security updates, and new features.', 'tribe-common' ), $this->get_plugin_name(), '', '' ); return $message; } /** * Displays a PUE message on the page if it is relevant * * @param string $page */ public function maybe_display_json_error_on_plugins_page( $page ) { if ( 'plugins.php' !== $page ) { return; } $state = $this->get_state(); $messages = []; $plugin_updates = get_plugin_updates(); $update_available = isset( $plugin_updates[ $this->plugin_file ] ); // Check to see if there is an licensing error or update message we should show if ( ! empty( $state->update->license_error ) ) { $messages[] = $state->update->license_error; } elseif ( $update_available && current_user_can( 'update_plugins' ) ) { // A plugin update is available $update_now = sprintf( esc_html__( 'Update now to version %s.', 'tribe-common' ), $state->update->version ); $update_now_link = sprintf( ' %2$s', wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->plugin_file, 'upgrade-plugin_' . $this->plugin_file ), $update_now ); $update_message = sprintf( esc_html__( 'There is a new version of %1$s available. %2$s', 'tribe-common' ), $this->plugin_name, $update_now_link ); $messages[] = sprintf( '%s
', $update_message ); } if ( empty( $messages ) ) { return; } $message_row_html = ''; foreach ( $messages as $message ) { $message_row_html .= sprintf( ' ', $message ); } $message_row_html = sprintf( '