diff --git a/composer.json b/composer.json index 9712a78..3968eaa 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,8 @@ } ], "require": { - "php": ">=7.4" + "php": ">=7.4", + "phpmailer/phpmailer": "^6.8" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index 07a77ca..3e655f3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,90 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ae3db58fab4d027fbd3814761fec769f", - "packages": [], + "content-hash": "aa1767c4b73d4c64f1f4b5531b361ba5", + "packages": [ + { + "name": "phpmailer/phpmailer", + "version": "v6.9.3", + "source": { + "type": "git", + "url": "https://github.com/PHPMailer/PHPMailer.git", + "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2f5c94fe7493efc213f643c23b1b1c249d40f47e", + "reference": "2f5c94fe7493efc213f643c23b1b1c249d40f47e", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "php": ">=5.5.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "doctrine/annotations": "^1.2.6 || ^1.13.3", + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.3.5", + "roave/security-advisories": "dev-latest", + "squizlabs/php_codesniffer": "^3.7.2", + "yoast/phpunit-polyfills": "^1.0.4" + }, + "suggest": { + "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication", + "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", + "ext-openssl": "Needed for secure SMTP sending and DKIM signing", + "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", + "hayageek/oauth2-yahoo": "Needed for Yahoo XOAUTH2 authentication", + "league/oauth2-google": "Needed for Google XOAUTH2 authentication", + "psr/log": "For optional PSR-3 debug logging", + "symfony/polyfill-mbstring": "To support UTF-8 if the Mbstring PHP extension is not enabled (^1.2)", + "thenetworg/oauth2-azure": "Needed for Microsoft XOAUTH2 authentication" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPMailer\\PHPMailer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-only" + ], + "authors": [ + { + "name": "Marcus Bointon", + "email": "phpmailer@synchromedia.co.uk" + }, + { + "name": "Jim Jagielski", + "email": "jimjag@gmail.com" + }, + { + "name": "Andy Prevost", + "email": "codeworxtech@users.sourceforge.net" + }, + { + "name": "Brent R. Matzelle" + } + ], + "description": "PHPMailer is a full-featured email creation and transfer class for PHP", + "support": { + "issues": "https://github.com/PHPMailer/PHPMailer/issues", + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.3" + }, + "funding": [ + { + "url": "https://github.com/Synchro", + "type": "github" + } + ], + "time": "2024-11-24T18:04:13+00:00" + } + ], "packages-dev": [ { "name": "php-stubs/wordpress-stubs", diff --git a/quiztech-assessment-platform.php b/quiztech-assessment-platform.php index e083a69..11d0a70 100644 --- a/quiztech-assessment-platform.php +++ b/quiztech-assessment-platform.php @@ -139,3 +139,71 @@ function quiztech_init() { // Future core classes (e.g., Shortcode handlers) should be instantiated here. } add_action( 'plugins_loaded', 'quiztech_init' ); + + +/** + * Configure PHPMailer to use SMTP settings if enabled. + * + * @param PHPMailer\PHPMailer\PHPMailer $phpmailer The PHPMailer instance. + */ +function quiztech_configure_smtp( $phpmailer ) { + $options = get_option( 'quiztech_settings', [] ); + + // Check if SMTP is enabled in settings + if ( empty( $options['smtp_enabled'] ) || ! $options['smtp_enabled'] ) { + return; // SMTP not enabled, do nothing + } + + // Basic validation + if ( empty( $options['smtp_host'] ) || empty( $options['smtp_port'] ) ) { + // Optionally log an error or add an admin notice here + error_log('Quiztech SMTP Error: Host or Port not configured.'); + return; // Cannot configure SMTP without host and port + } + + // Set mailer to use SMTP + $phpmailer->isSMTP(); + + // Set SMTP server details + $phpmailer->Host = $options['smtp_host']; + $phpmailer->Port = (int) $options['smtp_port']; + + // Set encryption type (tls, ssl, or none) + $encryption = isset( $options['smtp_encryption'] ) ? strtolower( $options['smtp_encryption'] ) : ''; + if ( in_array( $encryption, [ 'ssl', 'tls' ] ) ) { + $phpmailer->SMTPSecure = $encryption; + } else { + $phpmailer->SMTPSecure = ''; // Explicitly set to none if not ssl/tls + } + + // Set authentication details if enabled + $phpmailer->SMTPAuth = ! empty( $options['smtp_auth'] ) && $options['smtp_auth']; + if ( $phpmailer->SMTPAuth ) { + if ( empty( $options['smtp_username'] ) || empty( $options['smtp_password'] ) ) { + error_log('Quiztech SMTP Error: Auth enabled but Username or Password missing.'); + // Decide if we should prevent sending or let PHPMailer handle the error + // For now, we proceed and let PHPMailer fail if credentials are bad + } + $phpmailer->Username = isset( $options['smtp_username'] ) ? $options['smtp_username'] : ''; + $phpmailer->Password = isset( $options['smtp_password'] ) ? $options['smtp_password'] : ''; + } + + // Set From address (optional, but recommended) + $from_address = isset( $options['smtp_from_address'] ) ? $options['smtp_from_address'] : ''; + $from_name = isset( $options['smtp_from_name'] ) ? $options['smtp_from_name'] : get_bloginfo( 'name' ); + + if ( ! empty( $from_address ) && is_email( $from_address ) ) { + try { + $phpmailer->setFrom( $from_address, $from_name ); + } catch ( \PHPMailer\PHPMailer\Exception $e ) { + error_log( 'Quiztech SMTP Error setting From address: ' . $e->getMessage() ); + // Continue without setting From if it fails + } + } + + // Optional: Add debugging + // $phpmailer->SMTPDebug = 2; // Enable verbose debug output (use 0 in production) + // $phpmailer->Debugoutput = 'error_log'; // Log debug output to PHP error log +} +add_action( 'phpmailer_init', 'quiztech_configure_smtp' ); + diff --git a/src/Admin/SettingsPage.php b/src/Admin/SettingsPage.php index dee13d7..84fa30e 100644 --- a/src/Admin/SettingsPage.php +++ b/src/Admin/SettingsPage.php @@ -31,6 +31,7 @@ class SettingsPage { 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' ] ); } /** @@ -95,8 +96,160 @@ class SettingsPage { ] ); + + // --- 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. @@ -116,6 +269,42 @@ class SettingsPage { // Output save settings button submit_button( __( 'Save Settings', 'quiztech' ) ); ?> + +
()
+ + + + ' . esc_html__( 'Configure your SMTP server details for reliable email sending. If disabled, WordPress will use the default PHP mail function.', 'quiztech' ) . ''; + } + + /** + * 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' : ''; + ?> + + + /> + + + + 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'] ] : ''; + ?> + + + + + '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 ] ); + } + } } \ No newline at end of file diff --git a/src/Includes/Invitations.php b/src/Includes/Invitations.php index 8891f36..f833506 100644 --- a/src/Includes/Invitations.php +++ b/src/Includes/Invitations.php @@ -83,10 +83,8 @@ class Invitations { $message = "Please click the following link to take your assessment:\n\n" . $invite_url; // $headers = ['Content-Type: text/html; charset=UTF-8']; // If sending HTML email - // $sent = \wp_mail($applicant_email, $subject, $message); // Corrected line 76 - // return $sent; - - return true; // Placeholder + $sent = \wp_mail($applicant_email, $subject, $message); // Corrected line 76 + return $sent; } /**