options = $this->get_options(); $this->sso_provider_enabled = ! empty( $this->options['enable-sso'] ); $this->sso_client_enabled = ! empty( $this->options['sso-client-enabled'] ); $this->sso_secret_set = ! empty( $this->options['sso-secret'] ); } /** * Validates the Discourse URL. * * @param string $input The input to be validated. * * @return string */ public function validate_url( $input ) { $regex = '/^(http:|https:)/'; // Make sure the url starts with a valid protocol. if ( ! preg_match( $regex, $input ) ) { add_settings_error( 'discourse', 'discourse_url', __( 'The Discourse URL needs to be set to a valid URL that begins with either \'http:\' or \'https:\'.', 'wp-discourse' ) ); $url = ''; } else { $url = untrailingslashit( esc_url_raw( $input ) ); if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { add_settings_error( 'discourse', 'discourse_url', __( 'The Discourse URL you provided is not a valid URL.', 'wp-discourse' ) ); } } return $url; } /** * Validates the api key. * * @param string $input The input to be validated. * * @return string */ public function validate_api_key( $input ) { $regex = '/^\s*([0-9]*[a-z]*|[a-z]*[0-9]*)*\s*$/'; if ( empty( $input ) ) { add_settings_error( 'discourse', 'api_key', __( 'You must provide an API key.', 'wp-discourse' ) ); $api_key = ''; } else { $api_key = trim( $input ); if ( ! preg_match( $regex, $input ) ) { add_settings_error( 'discourse', 'api_key', __( 'The API key you provided is not valid.', 'wp-discourse' ) ); } } return $api_key; } /** * Validates the publish_username. * * @param string $input The input to be validated. * * @return string */ public function validate_publish_username( $input ) { if ( ! empty( $input ) ) { $publish_username = $this->sanitize_text( $input ); } else { add_settings_error( 'discourse', 'publish_username', __( 'You need to provide a Discourse username.', 'wp-discourse' ) ); $publish_username = ''; } return $publish_username; } /** * Validates the 'publish_category' select input. * * Returns the category id. * * @param string $input The input to be validated. * * @return string */ public function validate_publish_category( $input ) { return $this->sanitize_int( $input ); } /** * Validates the 'allowed_post_types' multi-select. * * @param array $input The array of allowed post-types. * * @return array */ public function validate_allowed_post_types( $input ) { $output = array(); foreach ( $input as $post_type ) { $output[] = sanitize_text_field( $post_type ); } return $output; } /** * Validates the 'exclude_tags' input. * * @param mixed $input The excluded tags. * * @return array */ public function validate_exclude_tags( $input ) { $output = array(); if ( is_string( $input ) ) { $input = explode( ',', $input ); } foreach ( $input as $tag_slug ) { $tag_slug = trim( $tag_slug ); if ( '' == $tag_slug ) { continue; } if ( term_exists( $tag_slug, 'post_tag' ) ) { $output[] = sanitize_text_field( $tag_slug ); } else { add_settings_error( 'discourse_exclude_tags', 'tag-does-not-exist', "Exclude Posts By Tag: '$tag_slug' is not a valid tag.", 'warning' ); } } return array_unique( $output ); } /** * Validates the 'clear_cached_comment_html input. * * @param string $input The input to be validated. * * @return int */ public function validate_clear_comments_html( $input ) { if ( 1 === intval( $input ) ) { $this->clear_cached_html(); } return 0; } /** * Validates the 'existing_comments_heading' input. * * @param string $input The input to be validated. * * @return string */ public function validate_existing_comments_heading( $input ) { return $this->sanitize_html( $input ); } /** * Validates the 'max_comments' number input. * * @param int $input The input to be validated. * * @return mixed */ public function validate_max_comments( $input ) { return $this->validate_int( $input, 'max_comments', 0, null, __( 'The max visible comments must be set to at least 0.', 'wp-discourse' ), $this->use_discourse_comments ); } /** * Validates the 'min_replies' number input. * * @param int $input The input to be validated. * * @return mixed */ public function validate_min_replies( $input ) { return $this->validate_int( $input, 'min_replies', 0, null, __( 'The min number of replies setting requires a number greater than or equal to 0.', 'wp-discourse' ), $this->use_discourse_comments ); } /** * Validates the 'min_score' number input. * * @param int $input The input to be validated. * * @return mixed */ public function validate_min_score( $input ) { return $this->validate_int( $input, 'min_score', 0, null, __( 'The min score of posts setting requires a number greater than or equal to 0.', 'wp-discourse' ), $this->use_discourse_comments ); } /** * Validates the 'min_trust_level' number input. * * @param int $input The input to be validated. * * @return mixed */ public function validate_min_trust_level( $input ) { return $this->validate_int( $input, 'min_trust_level', 0, 5, __( 'The trust level setting requires a number between 0 and 5.', 'wp-discourse' ), $this->use_discourse_comments ); } /** * Validates the 'bypass_trust_level_score' number input. * * @param int $input The input to be validated. * * @return mixed */ public function validate_bypass_trust_level_score( $input ) { return $this->validate_int( $input, 'bypass_trust_level', 0, null, __( 'The bypass trust level score setting requires an integer greater than or equal to 0.', 'wp-discourse' ), $this->use_discourse_comments ); } /** * Validates the 'custom_excerpt_length' number input. * * @param int $input The input to be validated. * * @return mixed */ public function validate_custom_excerpt_length( $input ) { return $this->validate_int( $input, 'excerpt_length', 0, null, __( 'The custom excerpt length setting requires a positive integer.', 'wp-discourse' ), true ); } /** * Validates the 'max_tags' input. * * @param int $input The input to be validated. * * @return mixed */ public function validate_max_tags( $input ) { return $this->validate_int( $input ); } /** * Validates use_discourse_webhook. * * @param string $input The input to be validated. * * @return bool|int */ public function validate_use_discourse_webhook( $input ) { $this->use_discourse_webhook = $this->validate_checkbox( $input ); return $this->use_discourse_webhook; } /** * Validates user_discourse_user_webhook. * * @param string $input The input to be validated. * * @return bool|int */ public function validate_use_discourse_user_webhook( $input ) { $this->use_discourse_user_webhook = $this->validate_checkbox( $input ); return $this->use_discourse_user_webhook; } /** * Validates the webhook_secret input. * * @param string $input The input to be validated. * * @return string */ public function validate_webhook_secret( $input ) { if ( ( $this->use_discourse_webhook || $this->use_discourse_user_webhook ) && strlen( $input ) < 12 ) { add_settings_error( 'discourse', 'webhook_secret', __( 'To use a Discourse webhook, the secret must be set to a value at least 12 characters long.', 'wp-discourse' ) ); return ''; } return $input; } /** * Validates the webhook_match_old_topics input. * * @param string $input The input to be validated. * * @return int */ public function validate_webhook_match_old_topics( $input ) { $match_old_topics = $this->validate_checkbox( $input ); return $match_old_topics; } /** * Validates the 'enable_sso'checkbox. * * @param string $input The input to be validated. * * @return int */ public function validate_enable_sso( $input ) { $new_value = $this->sanitize_checkbox( $input ); if ( 1 === $new_value && $this->sso_client_enabled ) { add_settings_error( 'discourse', 'sso_client_enabled', __( "You have the 'DiscourseConnect Client' option enabled. Visit the 'DiscourseConnect Client' settings tab to disable it before enabling your site to function as the DiscourseConnect provider.", 'wp-discourse' ) ); return 0; } if ( 1 === $new_value && ! $this->sso_secret_set ) { add_settings_error( 'discourse', 'sso_provider_no_secret', __( 'Before enabling your site to function as the DiscourseConnect provider, you need to set the DiscourseConnect Secret Key.', 'wp-discourse' ) ); return 0; } // When the SSO Provider option is updated, clear the comment cache to update links to Discourse. $this->clear_cached_html(); return $new_value; } /** * Validates the 'sso_client_enabled' checkbox. * * @param string $input The input to be validated. * * @return int */ public function validate_sso_client_enabled( $input ) { $new_value = $this->sanitize_checkbox( $input ); if ( 1 === $new_value && $this->sso_provider_enabled ) { add_settings_error( 'discourse', 'sso_provider_enabled', __( "You have the 'DiscourseConnect Provider' option enabled. Click on the 'DiscourseConnect Provider' settings tab to disable it before enabling your site to function as a DiscourseConnect client.", 'wp-discourse' ) ); return 0; } if ( 1 === $new_value && ! $this->sso_secret_set ) { add_settings_error( 'discourse', 'sso_client_no_secret', __( 'Before enabling your site to function as a DiscourseConnect client, you need to set the DiscourseConnect Secret Key.', 'wp-discourse' ) ); return 0; } return $this->sanitize_checkbox( $input ); } /** * Validates the sso_client_login_form_redirect redirect text input. * * @param string $input The input to be validated. * * @return string */ public function validate_sso_client_login_form_redirect( $input ) { if ( empty( $input ) ) { return ''; } $regex = '/^(http:|https:)/'; // Make sure the url starts with a valid protocol. if ( ! preg_match( $regex, $input ) ) { add_settings_error( 'discourse', 'sso_client_login_redirect', __( 'The redirect URL needs to be set to a valid URL that begins with either \'http:\' or \'https:\'.', 'wp-discourse' ) ); $url = ''; } else { $url = untrailingslashit( esc_url_raw( $input ) ); if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) { add_settings_error( 'discourse', 'sso_client_login_redirect', __( 'The redirect URL you provided is not a valid URL.', 'wp-discourse' ) ); } } return $url; } /** * Validates the 'sso_secret' text input. * * @param string $input The input to be validated. * * @return string */ public function validate_sso_secret( $input ) { $secret = trim( $input ); if ( strlen( $secret ) < 10 ) { add_settings_error( 'discourse', 'sso_secret', __( 'The DiscourseConnect secret key must be at least 10 characters long.', 'wp-discourse' ) ); return ''; } return $secret; } /** * Validates the 'login_path' text input. * * @param string $input The input to be validated. * * @return string */ public function validate_login_path( $input ) { if ( $this->sso_provider_enabled && $input ) { $regex = '/^\//'; if ( ! preg_match( $regex, $input ) ) { add_settings_error( 'discourse', 'login_path', __( 'The path to login page setting needs to be a valid file path, starting with \'/\'.', 'wp-discourse' ) ); } // It's valid. return $this->sanitize_text( $input ); } // Sanitize, but don't validate. SSO is not enabled. return $this->sanitize_text( $input ); } /** * Validates the 'auto-create-login-redirect' field. * * @param string $input The input to be validated. * * @return string */ public function validate_auto_create_login_redirect( $input ) { if ( $this->sso_provider_enabled && $input ) { $regex = '/^\//'; if ( ! preg_match( $regex, $input ) ) { add_settings_error( 'discourse', 'auto_create_login_redirect', __( 'The path to the login redirect page setting needs to be a valid file path, starting with \'/\'.', 'wp-discourse' ) ); } // It's valid. return $this->sanitize_text( $input ); } // Sanitize, but don't validate. SSO is not enabled. return $this->sanitize_text( $input ); } /** * Validates the 'auto-create-welcome-redirect' field. * * @param string $input The input to be validated. * * @return string */ public function validate_auto_create_welcome_redirect( $input ) { if ( $this->sso_provider_enabled && $input ) { $regex = '/^\//'; if ( ! preg_match( $regex, $input ) ) { add_settings_error( 'discourse', 'auto_create_welcome_redirect', __( 'The path to the welcome page setting needs to be a valid file path, starting with \'/\'.', 'wp-discourse' ) ); } // It's valid. return $this->sanitize_text( $input ); } // Sanitize, but don't validate. SSO is not enabled. return $this->sanitize_text( $input ); } /** * Validates the `force-publish-max-age` input. * * @param string $input The input to be validated. * * @return int */ public function validate_force_publish_max_age( $input ) { return $this->sanitize_int( $input ); } /** * Validate a checkbox input. * * @param string $input The input to be validated. * * @return int */ public function validate_checkbox( $input ) { return $this->sanitize_checkbox( $input ); } /** * Validate a radio input that returns a string. * * @param string $input The input to be validated. * * @return string */ public function validate_radio_string_value( $input ) { return $this->validate_text_input( $input ); } /** * Validate a text input. * * @param string $input The input to be validated. * * @return string */ public function validate_text_input( $input ) { if ( ! empty( $input ) ) { return $this->sanitize_text( $input ); } else { return ''; } } /** * Validates an email input. * * @param string $input The input to be validated. * * @return string */ public function validate_email( $input ) { if ( ! empty( $input ) ) { return sanitize_email( $input ); } else { return ''; } } /** * Helper methods ******************************/ /** * A helper method to sanitize text inputs. * * @param string $input The input to be sanitized. * * @return string */ protected function sanitize_text( $input ) { return sanitize_text_field( $input ); } /** * A helper method to sanitize the value returned from checkbox inputs. * * @param string $input The value returned from the checkbox. * * @return int */ protected function sanitize_checkbox( $input ) { return 1 === intval( $input ) ? 1 : 0; } /** * A helper function to sanitize HTML. * * @param string $input HTML input to be sanitized. * * @return string */ protected function sanitize_html( $input ) { return wp_kses_post( $input ); } /** * A helper function to sanitize an int. * * @param mixed|int $input The input to be validated. * * @return int */ protected function sanitize_int( $input ) { return intval( $input ); } /** * A helper function to validate and sanitize integers. * * @param int $input The input to be validated. * @param string $option_id The option being validated. * @param null $min The minimum allowed value. * @param null $max The maximum allowed value. * @param string $error_message The error message to return. * @param bool $add_error Whether or not to add a setting error. * * @return mixed */ protected function validate_int( $input, $option_id = null, $min = null, $max = null, $error_message = '', $add_error = false ) { $options = array(); if ( isset( $min ) ) { $options['min_range'] = $min; } if ( isset( $max ) ) { $options['max_range'] = $max; } $input = filter_var( $input, FILTER_VALIDATE_INT, array( 'options' => $options, ) ); if ( false === $input ) { if ( $add_error ) { add_settings_error( 'discourse', $option_id, $error_message ); } // The input is not valid, but the setting's section is not being used, sanitize the input and return it. return null; } else { // Valid input. return $input; } } /** * Clears all cached comment HTML. */ protected function clear_cached_html() { $transient_keys = get_option( 'wpdc_cached_html_keys' ); if ( ! empty( $transient_keys ) ) { foreach ( $transient_keys as $transient_key ) { delete_transient( $transient_key ); } } } }