actions(); $this->set_addons(); // Fire up the plugin updates $this->updates = new Delicious_Brains_API_Updates( $this ); } /** * Fire up the actions for the licenses */ public function actions() { add_action( $this->plugin->load_hook, array( $this, 'remove_licence_when_constant_set' ) ); add_action( $this->plugin->load_hook, array( $this, 'http_disable_ssl' ) ); add_action( $this->plugin->load_hook, array( $this, 'http_refresh_licence' ) ); add_action( 'wp_ajax_' . $this->plugin->prefix . '_check_licence', array( $this, 'ajax_check_licence' ) ); add_action( 'wp_ajax_' . $this->plugin->prefix . '_reactivate_licence', array( $this, 'ajax_reactivate_licence' ) ); } /** * Define the addons available for the plugin * * @return array */ function set_addons() { if ( is_null( $this->addons ) ) { $meta_key = $this->plugin->get_global_meta_key(); $addons = array(); if ( isset( $GLOBALS[ $meta_key ][ $this->plugin->slug ]['supported_addon_versions'] ) ) { $available_addons = get_site_transient( $this->plugin->prefix . '_addons_available' ); foreach ( $GLOBALS[ $meta_key ][ $this->plugin->slug ]['supported_addon_versions'] as $addon => $version ) { $basename = $this->plugin->get_plugin_basename( $addon ); $name = $this->plugin->get_plugin_name( $addon ); $installed = file_exists( WP_PLUGIN_DIR . '/' . $basename ); $addons[ $basename ] = array( 'name' => $name, 'slug' => $addon, 'basename' => $basename, 'required_version' => $version, 'available' => ( false === $available_addons || isset( $available_addons[ $addon ] ) ), 'installed' => $installed, ); } } $this->addons = $addons; } return $this->addons; } /** * Get method for returning the license key from the database * that must be defined by the extending class * * @return mixed */ abstract protected function get_plugin_licence_key(); /** * Set method for saving the license key to the database * that must be defined by the extending class * * @param string $key * * @return mixed */ abstract protected function set_plugin_licence_key( $key ); /** * Get the name of the license constant * * @return string */ protected function get_licence_constant_name() { return strtoupper( $this->plugin->prefix ) . '_LICENCE'; } /** * Is the license key defined as a constant? * * @return bool */ public function is_licence_constant() { return defined( $this->get_licence_constant_name() ); } /** * Get the license key either from a constant or saved by the plugin * * @return string */ public function get_licence_key() { if ( $this->is_licence_constant() ) { $licence = constant( $this->get_licence_constant_name() ); } else { $licence = $this->get_plugin_licence_key(); } return $licence; } /** * Sets the licence key * * @param string $key */ protected function set_licence_key( $key ) { $this->set_plugin_licence_key( $key ); } /** * Checks whether the saved licence has expired or not. * * @param bool $skip_transient_check * @param bool $skip_expired_check * @param array $licence_response Optional pre-fetched licence response data. * * @return bool */ public function is_valid_licence( $skip_transient_check = false, $skip_expired_check = true, $licence_response = array() ) { if ( ! empty( $licence_response ) && is_array( $licence_response ) ) { $response = $licence_response; } else { $response = $this->is_licence_expired( $skip_transient_check ); } if ( isset( $response['dbrains_api_down'] ) ) { return true; } if ( $this->plugin->expired_licence_is_valid && isset( $response['errors']['subscription_expired'] ) && 1 === count( $response['errors'] ) ) { // Maybe don't cripple the plugin's functionality if the user's licence is expired. return $skip_expired_check; } return ! isset( $response['errors'] ); } /** * Check to see if the license has expired * * @param bool $skip_transient_check * * @return array|mixed */ public function is_licence_expired( $skip_transient_check = false ) { $licence = $this->get_licence_key(); if ( empty( $licence ) ) { $settings_link = sprintf( '%s', $this->admin_url( $this->plugin->settings_url_path ) . $this->plugin->settings_url_hash, __( 'enter your license key' ) ); $message = sprintf( __( 'To finish activating %1$s, %2$s. If you don\'t have a license key, you may purchase one.' ), $this->plugin->name, $settings_link, $this->plugin->purchase_url ); return array( 'errors' => array( 'no_licence' => $message ) ); } return json_decode( $this->check_licence( $licence ), true ); } /** * Check the license validity and fetch a response from the Delicious Brains API * * @param string $licence_key * * @return bool|mixed */ public function check_licence( $licence_key ) { if ( empty( $licence_key ) ) { return false; } $response = []; $response['addon_list'] = ''; $response['addons_available_list'] = ''; $response = json_encode($response); $response = $this->store_licence_addon_data( $response, true ); $response = apply_filters( $this->plugin->prefix . '_check_licence_response', $response ); set_site_transient( $this->plugin->prefix . '_licence_response', $response, $this->transient_timeout ); return $response; } /** * Process and store the Addon data after a license request * * @param string|array $response * @param bool $encoded * * @return array|mixed|string|void */ protected function store_licence_addon_data( $response, $encoded = false ) { $decoded_response = $encoded ? json_decode( $response, ARRAY_A ) : $response; if ( isset( $decoded_response['addon_list'] ) ) { // Save the addons list for use when installing // Don't really need to expire it ever, but let's clean it up after 60 days set_site_transient( $this->plugin->prefix . '_addons', $decoded_response['addon_list'], DAY_IN_SECONDS * 60 ); } if ( isset( $decoded_response['addons_available_list'] ) ) { // Save the available addons list for use set_site_transient( $this->plugin->prefix . '_addons_available', $decoded_response['addons_available_list'], DAY_IN_SECONDS * 60 ); } $response = $encoded ? json_encode( $decoded_response ) : $decoded_response; return $response; } /** * Remove the license from the settings if defined as a constant */ public function remove_licence_when_constant_set() { $licence_key = $this->get_plugin_licence_key(); // Remove licence from the database if constant is set if ( $this->is_licence_constant() && ! empty( $licence_key ) ) { $this->set_licence_key( '' ); } } /** * Returns a formatted message dependent on the status of the licence. * * @param bool|array $trans * * @return string */ public function get_licence_status_message( $trans = false ) { $licence = $this->get_licence_key(); $api_response_provided = true; if ( empty( $licence ) && ! $trans ) { $message = sprintf( __( 'Activate Your License — Please enter your license key.' ), 'js-action-link enter-licence' ); return $message; } } /** * Return a license key formatted with HTML to mask all but the last part * * @param string $licence * * @return string */ public function mask_licence( $licence ) { $licence_parts = explode( '-', $licence ); $i = count( $licence_parts ) - 1; $masked_licence = ''; foreach ( $licence_parts as $licence_part ) { if ( $i == 0 ) { $masked_licence .= $licence_part; continue; } $masked_licence .= ''; $masked_licence .= str_repeat( '•', strlen( $licence_part ) ) . '–'; --$i; } return $masked_licence; } /** * Helper method to display markup of the masked license * * @return string */ public function get_formatted_masked_licence() { return sprintf( '
%s %s
', $this->mask_licence( $this->get_plugin_licence_key() ), $this->admin_url( $this->plugin->settings_url_path . '&nonce=' . wp_create_nonce( $this->plugin->prefix . '-remove-licence' ) . '&' . $this->plugin->prefix . '-remove-licence=1' . $this->plugin->settings_url_hash ), _x( 'Remove', 'Delete license' ) ); } /** * Check for {prefix}-disable-ssl and related nonce * if found temporarily disable ssl via transient * * @return void */ function http_disable_ssl() { if ( isset( $_GET[ $this->plugin->prefix . '-disable-ssl' ] ) && wp_verify_nonce( $_GET['nonce'], $this->plugin->prefix . '-disable-ssl' ) ) { // input var okay set_site_transient( $this->plugin->prefix . '_temporarily_disable_ssl', '1', DAY_IN_SECONDS * 30 ); // 30 days $hash = ( isset( $_GET['hash'] ) ) ? '#' . sanitize_title( $_GET['hash'] ) : ''; // input var okay // delete the licence transient as we want to attempt to fetch the licence details again delete_site_transient( $this->plugin->prefix . '_licence_response' ); // redirecting here because we don't want to keep the query string in the web browsers address bar wp_redirect( $this->admin_url( $this->plugin->settings_url_path . $hash ) ); exit; } } /** * Remove the license. */ public function remove() { $this->set_licence_key( '' ); // Delete these transients as they contain information only valid for authenticated licence holders. delete_site_transient( 'update_plugins' ); delete_site_transient( $this->plugin->prefix . '_upgrade_data' ); delete_site_transient( $this->plugin->prefix . '_licence_response' ); delete_site_transient( $this->plugin->prefix . '_addons_available' ); delete_site_transient( $this->plugin->prefix . '_licence_media_check' ); do_action( $this->plugin->prefix . '_http_remove_licence' ); } /** * Check for {prefix}-check-licence and related nonce * if found refresh licence details * * @return void */ function http_refresh_licence() { if ( isset( $_GET[ $this->plugin->prefix . '-check-licence' ] ) && wp_verify_nonce( $_GET['nonce'], $this->plugin->prefix . '-check-licence' ) ) { // input var okay $hash = ( isset( $_GET['hash'] ) ) ? '#' . sanitize_title( $_GET['hash'] ) : ''; // input var okay // delete the licence transient as we want to attempt to fetch the licence details again delete_site_transient( $this->plugin->prefix . '_licence_response' ); do_action( $this->plugin->prefix . '_http_refresh_licence' ); $sendback = $this->admin_url( $this->plugin->settings_url_path . $hash ); if ( isset( $_GET['sendback'] ) ) { $sendback = $_GET['sendback']; } // redirecting because we don't want to keep the query string in the web browsers address bar wp_redirect( esc_url_raw( $sendback ) ); exit; } } /** * Activate a licence. * * @param string $licence_key * * @return bool|WP_Error */ public function activate( $licence_key ) { if ( empty( $licence_key ) ) { return new WP_Error( 'missing-licence-key', __( 'Licence Key not supplied.' ) ); } // Regardless of whether the license is valid or not, save it and let status/errors deal with fallout. // This way license status is handled the same whether saved in db or set via define. $this->set_licence_key( $licence_key ); $api_response_json = $this->activate_licence( $licence_key, $this->home_url ); set_site_transient( $this->plugin->prefix . '_licence_response', $api_response_json, $this->transient_timeout ); $api_response = json_decode( $api_response_json, true ); $api_down = ! empty( $api_response['dbrains_api_down'] ); $errors = isset( $api_response['errors'] ) ? $api_response['errors'] : array(); // Remove insignificant errors. unset( $errors['activation_deactivated'], $errors['subscription_expired'] ); if ( empty( $errors ) && ! $api_down ) { return true; } // For some reason we couldn't activate the licence, reason saved in transient. return false; } /** * AJAX handler for checking a licence. */ public function ajax_check_licence() { $this->check_ajax_referer( 'check-licence' ); $decoded_response = $this->check( true ); $decoded_response = apply_filters( $this->plugin->prefix . '_ajax_check_licence_response', $decoded_response ); wp_send_json( $decoded_response ); } /** * Check a licence. * * @param bool $skip_transient_check * * @return array */ public function check( $skip_transient_check = false ) { $decoded_response = $this->is_licence_expired( $skip_transient_check ); if ( ! empty( $decoded_response['dbrains_api_down'] ) ) { $decoded_response['message'] = $this->get_default_help_message(); } elseif ( ! empty( $decoded_response['errors'] ) ) { $decoded_response['htmlErrors'] = array( sprintf( '', $this->get_licence_status_message() ) ); } elseif ( ! empty( $decoded_response['message'] ) && ! get_site_transient( $this->plugin->prefix . '_help_message' ) ) { set_site_transient( $this->plugin->prefix . '_help_message', $decoded_response['message'], $this->transient_timeout ); } return $decoded_response; } /** * Get previously saved or fallback help message/form. * * @return string */ public function get_default_help_message() { $help_message = get_site_transient( $this->plugin->prefix . '_help_message' ); if ( ! $help_message ) { ob_start(); ?>active license, you may send an email to the following address.' ); ?>
check_ajax_referer( 'reactivate-licence' ); $return = array(); $response = $this->reactivate_licence( $this->get_licence_key(), $this->home_url ); $decoded_response = json_decode( $response, true ); if ( isset( $decoded_response['dbrains_api_down'] ) ) { $return['dbrains_api_down'] = 1; $return['body'] = $decoded_response['dbrains_api_down']; $this->end_ajax( json_encode( $return ) ); } if ( isset( $decoded_response['errors'] ) ) { $return[ $this->plugin->prefix . '_error' ] = 1; $return['body'] = reset( $decoded_response['errors'] ); $this->log_error( $return['body'], $decoded_response ); $this->end_ajax( json_encode( $return ) ); } delete_site_transient( $this->plugin->prefix . '_upgrade_data' ); delete_site_transient( $this->plugin->prefix . '_licence_response' ); $this->end_ajax( json_encode( array() ) ); } /** * Helper to end an AJAX request * * @param bool|string $return */ protected function end_ajax( $return = false ) { echo ( false === $return ) ? '' : $return; wp_die(); } /** * Custom verification of the AJAX request to prevent processing requests * * @param string $action Action nonce name */ protected function check_ajax_referer( $action ) { $result = check_ajax_referer( $action, 'nonce', false ); if ( false === $result ) { $return = array( $this->plugin->prefix . '_error' => 1, 'body' => sprintf( __( 'Invalid nonce for: %s' ), $action ), ); $this->end_ajax( json_encode( $return ) ); } } /** * Get the raw licence masked with bullets except for the last segment. * * @return bool|string false if no licence, masked string otherwise. */ public function get_masked_licence() { $licence_key = $this->get_licence_key(); if ( ! $licence_key ) { return false; } $licence_segments = explode( '-', $licence_key ); $visible_segment = array_pop( $licence_segments ); $masked_segments = array_map( function ( $segment ) { return str_repeat( '•', strlen( $segment ) ); }, $licence_segments ); $masked_segments[] = $visible_segment; return join( '-', $masked_segments ); } }