args = $args; $this->product_id = sanitize_key( $args['name'] ); if ( isset( $args['bundled'] ) ) { $this->add_bundled_product( $args['bundled'] ); } $this->set_registration_data(); // Instantiate the updater. if ( null === $this->updater ) { $this->updater = new Fusion_Updater( $this ); } add_action( 'wp_ajax_avada_product_registration', [ $this, 'ajax_check_registration' ] ); } /** * Adds a product to the array of bundled products. * * @access private * @since 1.0.0 * @param array $bundled An array o bundled products. */ private function add_bundled_product( $bundled ) { $bundled = (array) $bundled; foreach ( $bundled as $product_slug => $product_name ) { $product = sanitize_key( $product_name ); if ( ! isset( self::$bundled[ $product ] ) ) { self::$bundled[ $product ] = $this->args['name']; } } } /** * Gets bundled products array. * * @access public * @since 1.0.0 * @return array */ public function get_bundled() { return self::$bundled; } /** * Gets the arguments. * * @access public * @since 1.0.0 * @return array */ public function get_args() { return $this->args; } /** * Checks if the product is part of the themes or plugins * purchased by the user belonging to the token. * * @access public * @since 1.0.0 */ public function ajax_check_registration() { if ( ! isset( $_POST['avada_product_reg'] ) || ! wp_verify_nonce( $_POST['avada_product_reg'], 'avada_product_reg_nonce' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput exit( 'Invalid request.' ); } $this->check_registration(); ob_start(); $this->the_form(); $response = ob_get_clean(); exit( $response ); // phpcs:ignore WordPress.Security.EscapeOutput } /** * Checks if the product is part of the themes or plugins * purchased by the user belonging to the token. * * @access public * @since 1.0.0 */ public function check_registration() { // Sanity check. No need to do anything if we're not saving the form. if ( ( isset( $_POST[ $this->option_name ] ) && isset( $_POST[ $this->option_name ][ $this->product_id ] ) || isset( $_POST['avada_unregister_product'] ) ) && isset( $_POST['_wpnonce'] ) ) { // Reset saved errors. $this->errors = null; // Revoking or registering. $revoke = isset( $_POST['avada_unregister_product'] ) && '1' === $_POST['avada_unregister_product']; // Security check. check_admin_referer( $this->option_name . '_' . $this->product_id ); // No purchase code passed and we are not revoking. if ( ! $revoke && ! isset( $_POST[ $this->option_name ][ $this->product_id ]['purchase_code'] ) ) { return; } if ( $revoke ) { $purchase_code = $this->get_purchase_code(); $revoked = $this->revoke_purchase_code( $purchase_code ); // Always revoke, regardless of response. $valid = false; $purchase_code = ''; $this->registration_data[ $this->product_id ]['token'] = ''; } else { $purchase_code = sanitize_text_field( wp_unslash( $_POST[ $this->option_name ][ $this->product_id ]['purchase_code'] ) ); $purchase_code = wp_strip_all_tags( trim( $purchase_code ) ); $valid = $this->check_purchase( $purchase_code ); } // Update saved product data. $this->registration_data[ $this->product_id ]['purchase_code'] = $purchase_code; $this->registration_data[ $this->product_id ]['is_valid'] = $valid; $this->registration_data[ $this->product_id ]['errors'] = null !== $this->errors ? $this->errors : ''; $this->update_data(); // Refresh data for grace period. delete_transient( 'avada_dashboard_data' ); } } /** * Update data to database. * * @access public * @since 3.3 * @return void */ public function update_data() { $save_data = $this->registration_data; // Filter out non-persistent error messages. if ( isset( $save_data[ $this->product_id ]['errors'] ) && is_wp_error( $save_data[ $this->product_id ]['errors'] ) ) { $error_code = $save_data[ $this->product_id ]['errors']->get_error_code(); if ( 400 === $error_code ) { $save_data[ $this->product_id ]['errors'] = ''; } } else { $save_data[ $this->product_id ]['errors'] = ''; } update_option( $this->option_name, $save_data ); } /** * Update data to database, CLI version. * * @access public * @since 3.4 * @param array $registration_data Registration data. * @return void */ public function cli_update_data( $registration_data ) { // Early exit. if ( empty( $registration_data ) || ! defined( 'WP_CLI' ) || ! WP_CLI ) { return; } $save_data = $registration_data; // Filter out non-persistent error messages. if ( isset( $save_data['avada']['errors'] ) && is_wp_error( $save_data['avada']['errors'] ) ) { $error_code = $save_data['avada']['errors']->get_error_code(); if ( 400 === $error_code ) { $save_data['avada']['errors'] = ''; } } else { $save_data['avada']['errors'] = ''; } update_option( $this->option_name, $save_data ); } /** * Get errors property. * * @access public * @since 3.4 * @return null|object */ public function get_errors() { return $this->errors; } /** * Check if purchase code is valid. * * @access public * @since 3.3 * @param string $purchase purchase code. * @return bool */ public function check_purchase( $purchase = '' ) { if ( '' === $purchase ) { $this->errors = $this->get_error( 400, 'auth' ); return false; } if ( false === strpos( $purchase, '-' ) && 32 === strlen( $purchase ) ) { $this->errors = $this->get_error( 401, 'token' ); return; } if ( 36 !== strlen( $purchase ) || 4 !== substr_count( $purchase, '-' ) ) { $this->errors = $this->get_error( 401, 'auth' ); return false; } $args = [ 'timeout' => 60, 'user-agent' => 'fusion-purchase-code', ]; $response = wp_remote_get( FUSION_UPDATES_URL . '/wp-json/avada-api/validate-code/' . $purchase, $args ); if ( is_wp_error( $response ) ) { $this->errors = $response; return false; } $code = wp_remote_retrieve_response_code( $response ); if ( 399 < $code && 501 > $code ) { $this->errors = $this->get_error( $code, 'auth' ); return false; } $response = isset( $response['body'] ) ? json_decode( $response['body'], true ) : false; if ( true === $response ) { return true; } $this->get_error( $code, 'auth' ); return false; } /** * Registration doesn't appear to be valid. * * @access public * @param object $error WordPress error object. * @return object * @since 3.3 */ public function invalidate( $error = '' ) { $this->registration_data[ $this->product_id ]['is_valid'] = false; $this->registration_data[ $this->product_id ]['errors'] = $error; $this->update_data(); return $error; } /** * Get error for code. * * @access public * @param int $code HTTP code. * @param string $request Request type. * @return object WP error * @since 3.3 */ public function get_error( $code = 403, $request = 'auth' ) { switch ( (int) $code ) { // No code. case 400: if ( 'revoke' === $request ) { return new WP_Error( $code, __( 'No purchase code was passed on to be revoked.', 'Avada' ) ); } if ( 'prebuilt' === $request ) { return new WP_Error( $code, __( 'In order to import a prebuilt website Avada must be registered. No purchase code was found.', 'Avada' ) ); } return new WP_Error( $code, __( 'No purchase code was provided.', 'Avada' ) ); // No domain. case 417: if ( 'revoke' === $request ) { return new WP_Error( $code, __( 'No domain was passed on and therefore revoke could not be confirmed.', 'Avada' ) ); } if ( 'prebuilt' === $request ) { return new WP_Error( $code, __( 'In order to import a prebuilt website Avada must be registered. No domain was passed on, therefore validation could not be confirmed.', 'Avada' ) ); } return new WP_Error( $code, __( 'No domain was passed on in the request, this is needed to confirm registration.', 'Avada' ) ); // Invalid purchase code. case 401: if ( 'token' === $request ) { return new WP_Error( $code, __( 'Invalid purchase code. Code provided appears to be a token instead.', 'Avada' ) ); } if ( 'download' === $request ) { return $this->invalidate( new WP_Error( $code, __( 'Invalid purchase code.', 'Avada' ) ) ); } if ( 'prebuilt' === $request ) { return $this->invalidate( new WP_Error( $code, __( 'In order to import a prebuilt website Avada must be registered. The purchase code being used does not seem to be valid.', 'Avada' ) ) ); } return new WP_Error( $code, __( 'Invalid purchase code.', 'Avada' ) ); // Envato forbidden. case 403: return new WP_Error( $code, __( 'Envato API did not respond. Either the purchase code is incorrect or the API is temporarily unavailable.', 'Avada' ) ); // Domain mismatch. case 409: if ( 'download' === $request ) { return $this->invalidate( new WP_Error( $code, __( 'The purchase code is already being used on another domain.', 'Avada' ) ) ); } if ( 'revoke' === $request ) { return new WP_Error( $code, __( 'The current domain does not match our records and therefore was not revoked.', 'Avada' ) ); } if ( 'prebuilt' === $request ) { return $this->invalidate( new WP_Error( $code, __( 'In order to import a prebuilt website Avada must be registered. The current domain does not match our records.', 'Avada' ) ) ); } return new WP_Error( $code, __( 'The purchase code is already being used on another domain.', 'Avada' ) ); // Staging mismatch. case 412: if ( 'download' === $request ) { return $this->invalidate( new WP_Error( $code, __( 'The purchase code is already being used on another staging domain.', 'Avada' ) ) ); } if ( 'prebuilt' === $request ) { return $this->invalidate( new WP_Error( $code, __( 'In order to import a prebuilt website Avada must be registered. The current staging domain does not match our records.', 'Avada' ) ) ); } return new WP_Error( $code, __( 'The purchase code is already being used on another staging domain.', 'Avada' ) ); // Purchase code locked. case 423: /* translators: "ThemeFusion" contact link. */ return $this->invalidate( new WP_Error( $code, sprintf( __( 'This purchase code has been locked, as it was used in a manner that violates Envato license terms. Please contact us via the %s page to resolve.', 'Avada' ), 'license unlock' ) ) ); // Envato API limited. case 429: return new WP_Error( $code, __( 'Sorry, the API is currently overloaded. Please try again later.', 'Avada' ) ); // Too many registrations. case 406: /* translators: "ThemeFusion" contact link. */ return new WP_Error( $code, sprintf( __( 'The purchase code has been registered too many times. Please contact %s to resolve.', 'Avada' ), 'Avada' ) ); } return new WP_Error( $code, __( 'Unknown error encountered. Please try again later.', 'Avada' ) ); } /** * Bypass active or not. * * @access public * @since 3.3 */ public function bypass_active() { $data = Avada::get_data(); if ( isset( $data['bypass_active'] ) && $data['bypass_active'] ) { return true; } return false; } /** * Whether user should see restricted UI or not. * * @access public * @param string $type section type. * @since 3.3 */ public function should_show( $type = 'plugins' ) { return $this->is_registered() || $this->legacy_support() || $this->bypass_active(); } /** * Reset token because its invalid. * * @access public * @since 3.3 */ public function reset_token() { $this->registration_data[ $this->product_id ]['token'] = ''; $this->update_data(); } /** * Revoke purchase code. * * @access public * @param string $purchase Purchase code to revoke. * @since 3.3 */ public function revoke_purchase_code( $purchase = '' ) { $args = [ 'timeout' => 60, 'user-agent' => 'fusion-purchase-code', ]; $response = wp_remote_get( FUSION_UPDATES_URL . '/wp-json/avada-api/revoke-code/' . $purchase, $args ); if ( is_wp_error( $response ) ) { $this->errors = $response; return false; } $code = wp_remote_retrieve_response_code( $response ); if ( 399 < $code && 501 > $code ) { $this->errors = $this->get_error( $code, 'revoke' ); return false; } $response = isset( $response['body'] ) ? json_decode( $response['body'], true ) : false; if ( true === $response ) { return true; } $this->get_error( $code, 'revoke' ); return false; } /** * Check if updates can be served in grace period. * * @access public * @since 1.9.2 * @param string $product_id The plugin/theme ID. * @return bool */ public function legacy_support( $product_id = '' ) { if ( ! $product_id ) { $product_id = $this->product_id; } if ( ! isset( $this->registration_data[ $product_id ] ) ) { return false; } // No token, need to register. if ( '' === $this->get_token() ) { return false; } $data = Avada::get_data(); // Token and no grace set, must be first plugin update. if ( ! isset( $data['legacy_end'] ) || '' === $data['legacy_end'] ) { return true; } // Current time is before end date, grace is valid. $current = new DateTime( gmdate( 'D, d M Y H:i' ) ); $legacy_end = new DateTime( gmdate( 'D, d M Y H:i', $data['legacy_end'] ) ); if ( $current < $legacy_end ) { return true; } // Grace has ended. return false; } /** * Set available registration data. * * @access public * @since 1.9.2 * @return void */ public function set_registration_data() { $registration_data = []; $registration_data_stored = get_option( $this->option_name, [] ); $registration_data_dummy = [ 'token' => '', 'purchase_code' => '', 'is_valid' => 'false', 'scopes' => [], 'errors' => '', ]; foreach ( $registration_data_stored as $product => $data ) { $registration_data[ $product ] = wp_parse_args( $data, $registration_data_dummy ); } // No data at all, set it to dummy data. if ( ! isset( $registration_data[ $this->product_id ] ) ) { $registration_data[ $this->product_id ] = $registration_data_dummy; } // if we have stored errors, set them to display. if ( isset( $registration_data[ $this->product_id ]['errors'] ) && '' !== $registration_data[ $this->product_id ]['errors'] ) { $this->errors = $registration_data[ $this->product_id ]['errors']; } $this->registration_data = $registration_data; } /** * Check if product is part of registration data and is also valid. * * @access public * @since 1.9.2 * @param string $product_id The plugin/theme ID. * @return bool */ public function is_registered( $product_id = '' ) { if ( ! $product_id ) { $product_id = $this->product_id; } if ( ! isset( $this->registration_data[ $product_id ] ) ) { return false; } if ( '' === $this->registration_data[ $product_id ]['purchase_code'] ) { return false; } // Is the product registered? if ( true === $this->registration_data[ $product_id ]['is_valid'] ) { return true; } return false; } /** * Legacy update and builder has not been updated yet. * * @access public * @since 1.0.0 * @return boolean */ public function is_legacy_update() { return '' !== $this->get_token() && defined( 'FUSION_BUILDER_VERSION' ) && version_compare( FUSION_BUILDER_VERSION, '3.3', '<' ); } /** * If it should appear as if registered. * * @access public * @since 1.0.0 * @return boolean */ public function appear_registered() { return $this->is_registered() || $this->is_legacy_update(); } /** * Returns the stored token for the product. * * @access public * @since 1.0.0 * @param string $product_id The product-ID. * @return string The current token. */ public function get_token( $product_id = '' ) { if ( '' === $product_id ) { $product_id = $this->product_id; } if ( isset( $this->registration_data[ $product_id ] ) ) { return $this->registration_data[ $product_id ]['token']; } return ''; } /** * Returns the purchase code * * @access public * @since 3.3 * @param string $product_id The product-ID. * @return string The current token. */ public function get_purchase_code( $product_id = '' ) { if ( '' === $product_id ) { $product_id = $this->product_id; } if ( isset( $this->registration_data[ $product_id ] ) ) { return $this->registration_data[ $product_id ]['purchase_code']; } return ''; } /** * Prints the registration form. * * @access public * @since 1.0.0 * @return void */ public function the_form() { /** * Check registration. Now done in the admin class. * $this->check_registration(); */ // Get the stored token. $token = $this->get_token(); $purchase_code = $this->get_purchase_code(); // Is the product registered? $is_registered = $this->appear_registered(); ?>
my.avada.com' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
errors ) : ?>