v1.5.0: extend theme with navigation dropdowns, 9 page templates, auth UI, newsletter, contact panel

Major theme extension adding full-featured navigation with dropdown submenus (Tools, My Data, Resources),
9 new page templates (Calculator, Protocol Builder, Tracker, Subject Log, Documentation, Directory,
Science Feed, Profile, Sign In/Register), documentation page with sidebar ToC and scroll spy,
branded AJAX authentication forms, newsletter signup with admin export, slide-out contact/support panel,
Research Modules 6-card grid on front page, and comprehensive footer update.

Split functions.php (554→206 lines) into 6 focused inc/ modules: helpers, customizer, auth-handlers,
contact-handler, page-setup, newsletter-admin. All new components include dark mode support, CSRF
protection, input sanitization, output escaping, and graceful plugin degradation via shortcode_exists().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
peptiderepo
2026-04-14 08:38:30 +07:00
parent 6c7faa6eed
commit 9da702e746
31 changed files with 4311 additions and 1085 deletions

94
inc/contact-handler.php Normal file
View File

@@ -0,0 +1,94 @@
<?php
/**
* Contact Form AJAX Handler
*
* Processes the contact/support form from the settings panel.
* Sends email via wp_mail() to the site admin.
*
* @see template-parts/settings-panel.php — renders the contact form
* @see assets/js/settings-panel.js — submits form via XMLHttpRequest
* @see functions.php — includes this file
*
* What: AJAX endpoint for the support contact form.
* Who calls it: WordPress admin-ajax.php via settings-panel.js.
* Dependencies: wp_mail() for email delivery.
*
* @package peptide-starter
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Handle AJAX contact form submission.
*
* Validates nonce, sanitizes all inputs, sends email to admin.
* Works for both authenticated and unauthenticated users.
*
* @return void Sends JSON response and exits.
*/
function peptide_starter_ajax_contact() {
// Verify CSRF nonce.
if ( ! isset( $_POST['ps_contact_nonce'] ) ||
! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['ps_contact_nonce'] ) ), 'ps_contact_form' ) ) {
wp_send_json_error( array( 'message' => __( 'Security check failed. Please refresh and try again.', 'peptide-starter' ) ) );
}
$name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : '';
$email = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
$topic = isset( $_POST['topic'] ) ? sanitize_text_field( wp_unslash( $_POST['topic'] ) ) : '';
$message = isset( $_POST['message'] ) ? sanitize_textarea_field( wp_unslash( $_POST['message'] ) ) : '';
// Validate required fields.
if ( empty( $name ) || empty( $email ) || empty( $topic ) || empty( $message ) ) {
wp_send_json_error( array( 'message' => __( 'All fields are required.', 'peptide-starter' ) ) );
}
if ( ! is_email( $email ) ) {
wp_send_json_error( array( 'message' => __( 'Please enter a valid email address.', 'peptide-starter' ) ) );
}
// Allowed topics — reject anything unexpected.
$allowed_topics = array( 'bug', 'feature', 'data', 'other' );
if ( ! in_array( $topic, $allowed_topics, true ) ) {
wp_send_json_error( array( 'message' => __( 'Invalid topic selected.', 'peptide-starter' ) ) );
}
// Build the email.
$topic_labels = array(
'bug' => __( 'Bug Report', 'peptide-starter' ),
'feature' => __( 'Feature Request', 'peptide-starter' ),
'data' => __( 'Data Correction', 'peptide-starter' ),
'other' => __( 'Other', 'peptide-starter' ),
);
$subject = sprintf(
/* translators: 1: topic label, 2: sender name */
__( '[Peptide Repo] %1$s from %2$s', 'peptide-starter' ),
$topic_labels[ $topic ],
$name
);
$body = sprintf( __( 'Name: %s', 'peptide-starter' ), $name ) . "\n";
$body .= sprintf( __( 'Email: %s', 'peptide-starter' ), $email ) . "\n";
$body .= sprintf( __( 'Topic: %s', 'peptide-starter' ), $topic_labels[ $topic ] ) . "\n\n";
$body .= __( 'Message:', 'peptide-starter' ) . "\n";
$body .= $message;
$headers = array(
'Reply-To: ' . $name . ' <' . $email . '>',
'Content-Type: text/plain; charset=UTF-8',
);
$sent = wp_mail( get_option( 'admin_email' ), $subject, $body, $headers );
if ( $sent ) {
wp_send_json_success( array( 'message' => __( 'Message sent successfully. We will respond within 48 hours.', 'peptide-starter' ) ) );
} else {
wp_send_json_error( array( 'message' => __( 'Failed to send message. Please try again later.', 'peptide-starter' ) ) );
}
}
add_action( 'wp_ajax_ps_contact_submit', 'peptide_starter_ajax_contact' );
add_action( 'wp_ajax_nopriv_ps_contact_submit', 'peptide_starter_ajax_contact' );