quiztech-assessment-platform/src/Admin/SettingsPage.php

487 lines
No EOL
20 KiB
PHP

<?php
namespace Quiztech\AssessmentPlatform\Admin;
/**
* Handles the Quiztech plugin settings page in WP Admin.
*/
class SettingsPage {
/**
* Option group name.
* @var string
*/
private $option_group = 'quiztech_settings';
/**
* Option name in wp_options table.
* @var string
*/
private $option_name = 'quiztech_settings';
/**
* Settings page slug.
* @var string
*/
private $page_slug = 'quiztech-settings-page';
/**
* Register hooks for the settings page.
*/
public function register_hooks() {
add_action( 'admin_menu', [ $this, 'add_admin_page' ] );
add_action( 'admin_init', [ $this, 'register_settings' ] );
add_action( 'wp_ajax_quiztech_send_test_email', [ $this, 'handle_send_test_email_ajax' ] );
}
/**
* Adds the submenu page under the main Settings menu.
*/
public function add_admin_page() {
add_options_page(
__( 'Quiztech Settings', 'quiztech' ), // Page Title
__( 'Quiztech', 'quiztech' ), // Menu Title
'manage_options', // Capability Required
$this->page_slug, // Menu Slug
[ $this, 'render_settings_page' ] // Callback function to render the page
);
}
/**
* Registers settings, sections, and fields using the Settings API.
*/
public function register_settings() {
register_setting(
$this->option_group, // Option group
$this->option_name, // Option name
[ $this, 'sanitize_settings' ] // Sanitization callback
);
// Stripe Section
add_settings_section(
'quiztech_stripe_section', // Section ID
__( 'Stripe API Keys', 'quiztech' ), // Section Title
'__return_false', // Section callback (optional description)
$this->page_slug // Page slug where section appears
);
// Stripe Public Key Field
add_settings_field(
'quiztech_stripe_public_key', // Field ID
__( 'Stripe Public Key', 'quiztech' ), // Field Title
[ $this, 'render_text_field' ], // Field render callback
$this->page_slug, // Page slug
'quiztech_stripe_section', // Section ID
[ // Arguments for callback
'id' => 'quiztech_stripe_public_key',
'option_name' => $this->option_name,
'key' => 'stripe_public_key',
'description' => __( 'Enter your Stripe publishable API key.', 'quiztech' )
]
);
// Stripe Secret Key Field
add_settings_field(
'quiztech_stripe_secret_key', // Field ID
__( 'Stripe Secret Key', 'quiztech' ), // Field Title
[ $this, 'render_text_field' ], // Field render callback
$this->page_slug, // Page slug
'quiztech_stripe_section', // Section ID
[ // Arguments for callback
'id' => 'quiztech_stripe_secret_key',
'option_name' => $this->option_name,
'key' => 'stripe_secret_key',
'type' => 'password', // Mask the input
'description' => __( 'Enter your Stripe secret API key. This is kept confidential.', 'quiztech' )
]
);
// --- SMTP Section ---
add_settings_section(
'quiztech_smtp_section', // Section ID
__( 'SMTP Settings', 'quiztech' ), // Section Title
[ $this, 'render_smtp_section_description' ], // Section callback for description
$this->page_slug // Page slug where section appears
);
// SMTP Enabled Field (Checkbox)
add_settings_field(
'quiztech_smtp_enabled',
__( 'Enable SMTP', 'quiztech' ),
[ $this, 'render_checkbox_field' ], // New callback needed
$this->page_slug,
'quiztech_smtp_section',
[
'id' => 'quiztech_smtp_enabled',
'option_name' => $this->option_name,
'key' => 'smtp_enabled',
'description' => __( 'Enable sending emails via a custom SMTP server instead of the default WordPress mail function.', 'quiztech' )
]
);
// SMTP Host Field
add_settings_field(
'quiztech_smtp_host',
__( 'SMTP Host', 'quiztech' ),
[ $this, 'render_text_field' ],
$this->page_slug,
'quiztech_smtp_section',
[
'id' => 'quiztech_smtp_host',
'option_name' => $this->option_name,
'key' => 'smtp_host',
'description' => __( 'e.g., smtp.example.com', 'quiztech' )
]
);
// SMTP Port Field
add_settings_field(
'quiztech_smtp_port',
__( 'SMTP Port', 'quiztech' ),
[ $this, 'render_text_field' ], // Use text field, sanitize as number later
$this->page_slug,
'quiztech_smtp_section',
[
'id' => 'quiztech_smtp_port',
'option_name' => $this->option_name,
'key' => 'smtp_port',
'type' => 'number',
'description' => __( 'e.g., 587 (TLS) or 465 (SSL)', 'quiztech' )
]
);
// SMTP Encryption Field (Select)
add_settings_field(
'quiztech_smtp_encryption',
__( 'Encryption', 'quiztech' ),
[ $this, 'render_select_field' ], // New callback needed
$this->page_slug,
'quiztech_smtp_section',
[
'id' => 'quiztech_smtp_encryption',
'option_name' => $this->option_name,
'key' => 'smtp_encryption',
'options' => [
'' => __( 'None', 'quiztech' ),
'ssl' => __( 'SSL', 'quiztech' ),
'tls' => __( 'TLS', 'quiztech' ),
],
'description' => __( 'Select the encryption method.', 'quiztech' )
]
);
// SMTP Auth Field (Checkbox)
add_settings_field(
'quiztech_smtp_auth',
__( 'Use Authentication', 'quiztech' ),
[ $this, 'render_checkbox_field' ], // Reuse callback
$this->page_slug,
'quiztech_smtp_section',
[
'id' => 'quiztech_smtp_auth',
'option_name' => $this->option_name,
'key' => 'smtp_auth',
'description' => __( 'Enable if your SMTP server requires a username and password.', 'quiztech' )
]
);
// SMTP Username Field
add_settings_field(
'quiztech_smtp_username',
__( 'SMTP Username', 'quiztech' ),
[ $this, 'render_text_field' ],
$this->page_slug,
'quiztech_smtp_section',
[
'id' => 'quiztech_smtp_username',
'option_name' => $this->option_name,
'key' => 'smtp_username',
'description' => __( 'The username for SMTP authentication.', 'quiztech' )
]
);
// SMTP Password Field
add_settings_field(
'quiztech_smtp_password',
__( 'SMTP Password', 'quiztech' ),
[ $this, 'render_text_field' ],
$this->page_slug,
'quiztech_smtp_section',
[
'id' => 'quiztech_smtp_password',
'option_name' => $this->option_name,
'key' => 'smtp_password',
'type' => 'password',
'description' => __( 'The password for SMTP authentication. Stored as plain text in the database.', 'quiztech' )
]
);
// SMTP From Address Field
add_settings_field(
'quiztech_smtp_from_address',
__( 'From Email Address', 'quiztech' ),
[ $this, 'render_text_field' ],
$this->page_slug,
'quiztech_smtp_section',
[
'id' => 'quiztech_smtp_from_address',
'option_name' => $this->option_name,
'key' => 'smtp_from_address',
'type' => 'email',
'description' => __( 'The email address emails will be sent from.', 'quiztech' )
]
);
// SMTP From Name Field
add_settings_field(
'quiztech_smtp_from_name',
__( 'From Name', 'quiztech' ),
[ $this, 'render_text_field' ],
$this->page_slug,
'quiztech_smtp_section',
[
'id' => 'quiztech_smtp_from_name',
'option_name' => $this->option_name,
'key' => 'smtp_from_name',
'description' => __( 'The name emails will be sent from.', 'quiztech' )
]
);
// Future settings sections/fields (e.g., email, defaults) should be registered here.
} // End register_settings()
/**
* Renders the main settings page container and form.
*/
public function render_settings_page() {
?>
<div class="wrap">
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
<form action="options.php" method="post">
<?php
// Output security fields for the registered setting group
settings_fields( $this->option_group );
// Output setting sections and their fields
do_settings_sections( $this->page_slug );
// Output save settings button
submit_button( __( 'Save Settings', 'quiztech' ) );
?>
<hr />
<h2><?php esc_html_e( 'Test SMTP Settings', 'quiztech' ); ?></h2>
<p><?php esc_html_e( 'Click the button below to send a test email to the site administrator', 'quiztech' ); ?> (<?php echo esc_html( get_option('admin_email') ); ?>) <?php esc_html_e( 'using the currently saved settings.', 'quiztech' ); ?></p>
<button type="button" id="quiztech-test-email-button" class="button">
<?php esc_html_e( 'Send Test Email', 'quiztech' ); ?>
</button>
<span id="quiztech-test-email-status" style="margin-left: 10px;"></span>
<script type="text/javascript">
jQuery(document).ready(function($) {
$('#quiztech-test-email-button').on('click', function() {
var button = $(this);
var statusSpan = $('#quiztech-test-email-status');
statusSpan.text('<?php echo esc_js( __( 'Sending...', 'quiztech' ) ); ?>').css('color', '');
button.prop('disabled', true);
$.post(ajaxurl, {
action: 'quiztech_send_test_email',
_ajax_nonce: '<?php echo wp_create_nonce( 'quiztech_test_email_nonce' ); ?>'
}, function(response) {
if (response.success) {
statusSpan.text('<?php echo esc_js( __( 'Test email sent successfully!', 'quiztech' ) ); ?>').css('color', 'green');
} else {
var errorMsg = response.data && response.data.message ? response.data.message : '<?php echo esc_js( __( 'An unknown error occurred.', 'quiztech' ) ); ?>';
statusSpan.text('<?php echo esc_js( __( 'Error:', 'quiztech' ) ); ?> ' + errorMsg).css('color', 'red');
}
button.prop('disabled', false);
}).fail(function() {
statusSpan.text('<?php echo esc_js( __( 'AJAX request failed.', 'quiztech' ) ); ?>').css('color', 'red');
button.prop('disabled', false);
});
});
});
</script>
</form>
</div>
<?php
}
/**
* Renders a standard text input field for a setting.
* Expects args: 'id', 'option_name', 'key', 'description' (optional), 'type' (optional, default 'text')
*
* @param array $args Arguments passed from add_settings_field.
*/
public function render_text_field( $args ) {
$options = get_option( $args['option_name'], [] ); // Get all settings or default to empty array
$value = isset( $options[ $args['key'] ] ) ? $options[ $args['key'] ] : '';
$type = isset( $args['type'] ) ? $args['type'] : 'text';
?>
<input
type="<?php echo esc_attr( $type ); ?>"
id="<?php echo esc_attr( $args['id'] ); ?>"
name="<?php echo esc_attr( $args['option_name'] . '[' . $args['key'] . ']' ); ?>"
value="<?php echo esc_attr( $value ); ?>"
class="regular-text"
/>
<?php if ( isset( $args['description'] ) ) : ?>
<p class="description"><?php echo esc_html( $args['description'] ); ?></p>
<?php endif; ?>
<?php
}
/**
* Renders the description for the SMTP settings section.
*/
public function render_smtp_section_description() {
echo '<p>' . esc_html__( 'Configure your SMTP server details for reliable email sending. If disabled, WordPress will use the default PHP mail function.', 'quiztech' ) . '</p>';
}
/**
* Renders a checkbox input field for a setting.
* Expects args: 'id', 'option_name', 'key', 'description' (optional)
*
* @param array $args Arguments passed from add_settings_field.
*/
public function render_checkbox_field( $args ) {
$options = get_option( $args['option_name'], [] );
$checked = isset( $options[ $args['key'] ] ) && $options[ $args['key'] ] ? 'checked' : '';
?>
<input
type="checkbox"
id="<?php echo esc_attr( $args['id'] ); ?>"
name="<?php echo esc_attr( $args['option_name'] . '[' . $args['key'] . ']' ); ?>"
value="1" <?php // Value is 1 when checked ?>
<?php echo esc_attr( $checked ); ?>
/>
<?php if ( isset( $args['description'] ) ) : ?>
<label for="<?php echo esc_attr( $args['id'] ); ?>"> <?php echo esc_html( $args['description'] ); ?></label>
<?php endif; ?>
<?php
}
/**
* Renders a select dropdown field for a setting.
* Expects args: 'id', 'option_name', 'key', 'options' (array of value => label), 'description' (optional)
*
* @param array $args Arguments passed from add_settings_field.
*/
public function render_select_field( $args ) {
$options = get_option( $args['option_name'], [] );
$value = isset( $options[ $args['key'] ] ) ? $options[ $args['key'] ] : '';
?>
<select
id="<?php echo esc_attr( $args['id'] ); ?>"
name="<?php echo esc_attr( $args['option_name'] . '[' . $args['key'] . ']' ); ?>"
>
<?php foreach ( $args['options'] as $option_value => $option_label ) : ?>
<option value="<?php echo esc_attr( $option_value ); ?>" <?php selected( $value, $option_value ); ?>>
<?php echo esc_html( $option_label ); ?>
</option>
<?php endforeach; ?>
</select>
<?php if ( isset( $args['description'] ) ) : ?>
<p class="description"><?php echo esc_html( $args['description'] ); ?></p>
<?php endif; ?>
<?php
}
/**
* Sanitizes the settings array before saving.
*
* @param array $input The raw input array from the form.
* @return array The sanitized array.
*/
public function sanitize_settings( $input ) {
$sanitized_input = [];
if ( isset( $input['stripe_public_key'] ) ) {
// Basic sanitization, might need stricter validation (e.g., regex for pk_live_/pk_test_)
$sanitized_input['stripe_public_key'] = sanitize_text_field( $input['stripe_public_key'] );
}
if ( isset( $input['stripe_secret_key'] ) ) {
// Basic sanitization, might need stricter validation (e.g., regex for sk_live_/sk_test_)
$sanitized_input['stripe_secret_key'] = sanitize_text_field( $input['stripe_secret_key'] );
// Sanitize SMTP settings
$smtp_fields = [
'smtp_host' => 'sanitize_text_field',
'smtp_port' => 'absint', // Sanitize as integer
'smtp_encryption' => 'sanitize_key', // 'ssl', 'tls', or ''
'smtp_username' => 'sanitize_text_field',
'smtp_password' => 'sanitize_text_field', // Keep as text, WP handles display
'smtp_from_address' => 'sanitize_email',
'smtp_from_name' => 'sanitize_text_field',
];
foreach ( $smtp_fields as $key => $sanitize_callback ) {
if ( isset( $input[ $key ] ) ) {
$sanitized_input[ $key ] = call_user_func( $sanitize_callback, $input[ $key ] );
}
}
// Checkboxes (only save if present and value is '1')
$checkbox_fields = [ 'smtp_enabled', 'smtp_auth' ];
foreach ( $checkbox_fields as $key ) {
$sanitized_input[ $key ] = ( isset( $input[ $key ] ) && $input[ $key ] === '1' ) ? 1 : 0;
}
// Validate encryption value
if ( ! in_array( $sanitized_input['smtp_encryption'], [ '', 'ssl', 'tls' ], true ) ) {
$sanitized_input['smtp_encryption'] = ''; // Default to none if invalid
}
// Future settings added should have their sanitization logic implemented here.
return $sanitized_input;
}
/**
* Handles the AJAX request to send a test email.
*/
public function handle_send_test_email_ajax() {
check_ajax_referer( 'quiztech_test_email_nonce', '_ajax_nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => __( 'Permission denied.', 'quiztech' ) ], 403 );
}
$admin_email = get_option( 'admin_email' );
if ( ! is_email( $admin_email ) ) {
wp_send_json_error( [ 'message' => __( 'Invalid site administrator email address.', 'quiztech' ) ] );
}
$subject = __( 'Quiztech SMTP Test Email', 'quiztech' );
$message = __( 'This is a test email sent from the Quiztech plugin settings page to verify your SMTP configuration.', 'quiztech' );
$headers = []; // Can add headers if needed, e.g., Content-Type
// Temporarily hook into phpmailer to capture errors
$error_message = '';
add_action( 'wp_mail_failed', function ( $wp_error ) use ( &$error_message ) {
if ( is_wp_error( $wp_error ) ) {
$error_message = $wp_error->get_error_message();
}
}, 10, 1 );
$sent = wp_mail( $admin_email, $subject, $message, $headers );
// Remove the temporary hook
// Note: Removing anonymous functions requires more complex handling (e.g., storing the closure in a property).
// For simplicity in this test function, we'll leave it, but be aware it persists for the request.
// A better approach for production code might involve a dedicated class or named function.
// remove_action( 'wp_mail_failed', $this->capture_wp_mail_error_closure ); // Example if stored
if ( $sent ) {
wp_send_json_success();
} else {
$error_to_send = ! empty( $error_message ) ? $error_message : __( 'The email could not be sent. Check your SMTP settings and server logs.', 'quiztech' );
wp_send_json_error( [ 'message' => $error_to_send ] );
}
}
}