product = $product; // set prefix $this->prefix = sanitize_title_with_dashes( $this->product->get_item_name() . '_', null, 'save' ); // maybe set license key from constant $this->maybe_set_license_key_from_constant(); } /** * Setup hooks * */ public function setup_hooks() { // show admin notice if license is not active add_action( 'admin_notices', array( $this, 'display_admin_notices' ) ); // catch POST requests from license form add_action( 'admin_init', array( $this, 'catch_post_request' ) ); // Adds the plugin to the active extensions. add_filter( 'yoast-active-extensions', array( $this, 'set_active_extension' ) ); // setup item type (plugin|theme) specific hooks $this->specific_hooks(); // setup the auto updater $this->setup_auto_updater(); } /** * Checks if the license is valid and put it into the list with extensions. * * @param array $extensions The extensions used in Yoast SEO. * * @return array */ public function set_active_extension( $extensions ) { if ( ! $this->license_is_valid() ) { $this->set_license_key( 'yoast-dummy-license' ); $this->activate_license(); } if ( $this->license_is_valid() ) { $extensions[] = $this->product->get_slug(); } return $extensions; } /** * Display license specific admin notices, namely: * * - License for the product isn't activated * - External requests are blocked through WP_HTTP_BLOCK_EXTERNAL */ public function display_admin_notices() { if ( ! current_user_can( 'manage_options' ) ) { return; } // show notice if license is invalid if ( $this->show_license_notice() && ! $this->license_is_valid() ) { if ( $this->get_license_key() == '' ) { $message = __( 'Warning! You didn\'t set your %s license key yet, which means you\'re missing out on updates and support! Enter your license key or get a license here.' ); } else { $message = __( 'Warning! Your %s license is inactive which means you\'re missing out on updates and support! Activate your license or get a license here.' ); } ?>

product->get_text_domain() ), $this->product->get_item_name(), $this->product->get_license_page_url(), $this->product->get_tracking_url( 'activate-license-notice' ) ); ?>

product->get_api_url(), PHP_URL_HOST ); if ( ! defined( "WP_ACCESSIBLE_HOSTS" ) || stristr( WP_ACCESSIBLE_HOSTS, $host ) === false ) { ?>

Warning! You\'re blocking external requests which means you won\'t be able to get %s updates. Please add %s to %s.', $this->product->get_text_domain() ), $this->product->get_item_name(), '' . $host . '', 'WP_ACCESSIBLE_HOSTS' ); ?>

prefix . 'license', 'license-notice', $message, $css_class ); } /** * Remotely activate License * @return boolean True if the license is now activated, false if not */ public function activate_license() { $result = $this->call_license_api( 'activate' ); if ( $result ) { // show success notice if license is valid if ( $result->license === 'valid' ) { $success = true; $message = $this->get_successful_activation_message( $result ); } else { $this->remote_license_activation_failed = true; $success = false; $message = $this->get_unsuccessful_activation_message( $result ); } // Append custom HTML message to default message. $message .= $this->get_custom_message( $result ); if ( $this->show_license_notice() ) { $this->set_notice( $message, $success ); } $this->set_license_status( $result->license ); } return $this->license_is_valid(); } /** * Remotely deactivate License * @return boolean True if the license is now deactivated, false if not */ public function deactivate_license() { $result = $this->call_license_api( 'deactivate' ); if ( $result ) { // show notice if license is deactivated if ( $result->license === 'deactivated' ) { $success = true; $message = sprintf( __( "Your %s license has been deactivated.", $this->product->get_text_domain() ), $this->product->get_item_name() ); } else { $success = false; $message = sprintf( __( "Failed to deactivate your %s license.", $this->product->get_text_domain() ), $this->product->get_item_name() ); } $message .= $this->get_custom_message( $result ); // Append custom HTML message to default message. if ( $this->show_license_notice() ) { $this->set_notice( $message, $success ); } $this->set_license_status( $result->license ); } return ( $this->get_license_status() === 'deactivated' ); } /** * Returns the home url with the following modifications: * * In case of a multisite setup we return the network_home_url. * In case of no multisite setup we return the home_url while overriding the WPML filter. */ public function get_url() { // Add a new filter to undo WPML's changing of home url. add_filter( 'wpml_get_home_url', array( $this, 'wpml_get_home_url' ), 10, 2 ); // If the plugin is network activated, use the network home URL. if ( $this->is_network_activated ) { $url = network_home_url(); } // Otherwise use the home URL for this specific site. if ( ! $this->is_network_activated ) { $url = home_url(); } remove_filter( 'wpml_get_home_url', array( $this, 'wpml_get_home_url' ), 10 ); return $url; } /** * Returns the original URL instead of the language-enriched URL. * This method gets automatically triggered by the wpml_get_home_url filter. * * @param string $home_url The url altered by WPML. Unused. * @param string $url The url that isn't altered by WPML. * * @return string The original url. */ public function wpml_get_home_url( $home_url, $url ) { return $url; } /** * @param string $action activate|deactivate * * @return mixed */ protected function call_license_api( $action ) { // don't make a request if license key is empty if ( $this->get_license_key() === '' ) { return false; } // data to send in our API request $api_params = array( 'edd_action' => $action . '_license', 'license' => $this->get_license_key(), 'item_name' => urlencode( trim( $this->product->get_item_name() ) ), 'url' => $this->get_url() // grab the URL straight from the option to prevent filters from breaking it. ); // create api request url $url = add_query_arg( $api_params, $this->product->get_api_url() ); require_once dirname( __FILE__ ) . '/class-api-request.php'; $request = new Yoast_API_Request( $url ); if ( $request->is_valid() !== true ) { $this->set_notice( sprintf( __( "Request error: \"%s\" (%scommon license notices%s)", $this->product->get_text_domain() ), $request->get_error_message(), '', '' ), false ); } // get response return $request->get_response(); } /** * Set the license status * * @param string $license_status */ public function set_license_status( $license_status ) { $this->set_option( 'status', $license_status ); } /** * Get the license status * * @return string $license_status; */ public function get_license_status() { $license_status = $this->get_option( 'status' ); return trim( $license_status ); } /** * Set the license key * * @param string $license_key */ public function set_license_key( $license_key ) { $this->set_option( 'key', $license_key ); } /** * Gets the license key from constant or option * * @return string $license_key */ public function get_license_key() { $license_key = $this->get_option( 'key' ); return trim( $license_key ); } /** * Gets the license expiry date * * @return string */ public function get_license_expiry_date() { return $this->get_option( 'expiry_date' ); } /** * Stores the license expiry date */ public function set_license_expiry_date( $expiry_date ) { $this->set_option( 'expiry_date', $expiry_date ); } /** * Checks whether the license status is active * * @return boolean True if license is active */ public function license_is_valid() { return ( $this->get_license_status() === 'valid' ); } /** * Get all license related options * * @return array Array of license options */ protected function get_options() { // create option name $option_name = $this->prefix . 'license'; // get array of options from db if ( $this->is_network_activated ) { $options = get_site_option( $option_name, array() ); } else { $options = get_option( $option_name, array() ); } // setup array of defaults $defaults = array( 'key' => '', 'status' => '', 'expiry_date' => '' ); // merge options with defaults $this->options = wp_parse_args( $options, $defaults ); return $this->options; } /** * Set license related options * * @param array $options Array of new license options */ protected function set_options( array $options ) { // create option name $option_name = $this->prefix . 'license'; // update db if ( $this->is_network_activated ) { update_site_option( $option_name, $options ); } else { update_option( $option_name, $options ); } } /** * Gets a license related option * * @param string $name The option name * * @return mixed The option value */ protected function get_option( $name ) { $options = $this->get_options(); return $options[ $name ]; } /** * Set a license related option * * @param string $name The option name * @param mixed $value The option value */ protected function set_option( $name, $value ) { // get options $options = $this->get_options(); // update option $options[ $name ] = $value; // save options $this->set_options( $options ); } public function show_license_form_heading() { ?>

product->get_text_domain() ), $this->product->get_item_name() ); ?>    

prefix . 'license_key'; $nonce_name = $this->prefix . 'license_nonce'; $action_name = $this->prefix . 'license_action'; $api_host_available = $this->get_api_availability(); $visible_license_key = $this->get_license_key(); // obfuscate license key $obfuscate = ( strlen( $this->get_license_key() ) > 5 && ( $this->license_is_valid() || ! $this->remote_license_activation_failed ) ); if ( $obfuscate ) { $visible_license_key = str_repeat( '*', strlen( $this->get_license_key() ) - 4 ) . substr( $this->get_license_key(), - 4 ); } // make license key readonly when license key is valid or license is defined with a constant $readonly = ( $this->license_is_valid() || $this->license_constant_is_defined ); require dirname( __FILE__ ) . '/views/form.php'; // enqueue script in the footer add_action( 'admin_footer', array( $this, 'output_script' ), 99 ); } /** * Check if the license form has been submitted */ public function catch_post_request() { $name = $this->prefix . 'license_key'; // check if license key was posted and not empty if ( ! isset( $_POST[ $name ] ) ) { return; } // run a quick security check $nonce_name = $this->prefix . 'license_nonce'; if ( ! check_admin_referer( $nonce_name, $nonce_name ) ) { return; } // @TODO: check for user cap? // get key from posted value $license_key = $_POST[ $name ]; // check if license key doesn't accidentally contain asterisks if ( strstr( $license_key, '*' ) === false ) { // sanitize key $license_key = trim( sanitize_key( $_POST[ $name ] ) ); // save license key $this->set_license_key( $license_key ); } // does user have an activated valid license if ( ! $this->license_is_valid() ) { // try to auto-activate license return $this->activate_license(); } $action_name = $this->prefix . 'license_action'; // was one of the action buttons clicked? if ( isset( $_POST[ $action_name ] ) ) { $action = trim( $_POST[ $action_name ] ); switch ( $action ) { case 'activate': return $this->activate_license(); case 'deactivate': return $this->deactivate_license(); } } } /** * Output the script containing the YoastLicenseManager JS Object * * This takes care of disabling the 'activate' and 'deactivate' buttons */ public function output_script() { require_once dirname( __FILE__ ) . '/views/script.php'; } /** * Set the constant used to define the license * * @param string $license_constant_name The license constant name */ public function set_license_constant_name( $license_constant_name ) { $this->license_constant_name = trim( $license_constant_name ); $this->maybe_set_license_key_from_constant(); } /** * Get the API availability information * * @return array */ protected function get_api_availability() { return array( 'url' => $this->product->get_api_url(), 'availability' => $this->check_api_host_availability(), 'curl_version' => $this->get_curl_version(), ); } /** * Check if the API host address is available from this server * * @return bool */ private function check_api_host_availability() { $wp_http = new WP_Http(); if ( $wp_http->block_request( $this->product->get_api_url() ) === false ) { return true; } return false; } /** * Get the current curl version, or false * * @return mixed */ protected function get_curl_version() { if ( function_exists( 'curl_version' ) ) { $curl_version = curl_version(); if ( isset( $curl_version['version'] ) ) { return $curl_version['version']; } } return false; } /** * Maybe set license key from a defined constant */ private function maybe_set_license_key_from_constant() { if ( empty( $this->license_constant_name ) ) { // generate license constant name $this->set_license_constant_name( strtoupper( str_replace( array( ' ', '-' ), '', sanitize_key( $this->product->get_item_name() ) ) ) . '_LICENSE' ); } // set license key from constant if ( defined( $this->license_constant_name ) ) { $license_constant_value = constant( $this->license_constant_name ); // update license key value with value of constant if ( $this->get_license_key() !== $license_constant_value ) { $this->set_license_key( $license_constant_value ); } $this->license_constant_is_defined = true; } } /** * Determine what message should be shown for a successful license activation * * @param Object $result Result of a request. * * @return string */ protected function get_successful_activation_message( $result ) { // Get expiry date. if ( isset( $result->expires ) ) { $this->set_license_expiry_date( $result->expires ); $expiry_date = strtotime( $result->expires ); } else { $expiry_date = false; } // Always show that it was successful. $message = sprintf( __( "Your %s license has been activated. ", $this->product->get_text_domain() ), $this->product->get_item_name() ); // Show a custom notice it is an unlimited license. if ( $result->license_limit == 0 ) { $message .= __( "You have an unlimited license. ", $this->product->get_text_domain() ); } else { $message .= sprintf( _n( "You have used %d/%d activation. ", "You have used %d/%d activations. ", $result->license_limit, $this->product->get_text_domain() ), $result->site_count, $result->license_limit ); } // add upgrade notice if user has less than 3 activations left if ( $result->license_limit > 0 && ( $result->license_limit - $result->site_count ) <= 3 ) { $message .= sprintf( __( 'Did you know you can upgrade your license? ', $this->product->get_text_domain() ), $this->product->get_extension_url( 'license-nearing-limit-notice' ) ); } if ( $expiry_date !== false && $expiry_date < strtotime( "+1 month" ) ) { // Add extend notice if license is expiring in less than 1 month. $days_left = round( ( $expiry_date - time() ) / 86400 ); $message .= sprintf( _n( 'Your license is expiring in %d day, would you like to extend it? ', 'Your license is expiring in %d days, would you like to extend it? ', $days_left, $this->product->get_text_domain() ), $this->product->get_extension_url( 'license-expiring-notice' ), $days_left ); } return $message; } /** * Determine what message should be shown for an unsuccessful activation * * @param Object $result Result of a request. * * @return string */ protected function get_unsuccessful_activation_message( $result ) { // Default message if we cannot detect anything more specific. $message = __( 'Failed to activate your license, your license key seems to be invalid.', $this->product->get_text_domain() ); if ( ! empty( $result->error ) ) { switch ( $result->error ) { // Show notice if user is at their activation limit. case 'no_activations_left': $message = sprintf( __( 'You\'ve reached your activation limit. You must upgrade your license to use it on this site.', $this->product->get_text_domain() ), $this->product->get_extension_url( 'license-at-limit-notice' ) ); break; // Show notice if the license is expired. case 'expired': $message = sprintf( __( 'Your license has expired. You must extend your license in order to use it again.', $this->product->get_text_domain() ), $this->product->get_extension_url( 'license-expired-notice' ) ); break; } } return $message; } /** * Get the locale for the current user * * @return string */ protected function get_user_locale() { if ( function_exists( 'get_user_locale' ) ) { return get_user_locale(); } return get_locale(); } /** * Parse custom HTML message from response * * @param Object $result Result of the request. * * @return string */ protected function get_custom_message( $result ) { $message = ''; // Allow for translated messages to be used. $localizedDescription = 'custom_message_' . $this->get_user_locale(); if ( ! empty( $result->{$localizedDescription} ) ) { $message = $result->{$localizedDescription}; } // Fall back to non-localized custom message if no locale has been provided. if ( empty( $message ) && ! empty( $result->custom_message ) ) { $message = $result->custom_message; } // Make sure we limit the type of HTML elements to be displayed. if ( ! empty( $message ) ) { $message = wp_kses( $message, array( 'a' => array( 'href' => array(), 'target' => array(), 'title' => array() ), 'br' => array(), ) ); // Make sure we are on a new line. $message = '
' . $message; } return $message; } /** * Returns true when a license notice should be shown. * * @return bool */ protected function show_license_notice() { /** * Filter: 'yoast-show-license-notice' - Show the license notice. * * @api bool $show True if notices should be shown. */ return ( bool ) apply_filters( 'yoast-show-license-notice', true ); } } }