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:
98
template-parts/module-cards.php
Normal file
98
template-parts/module-cards.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
/**
|
||||
* Template Part: Research Module Cards
|
||||
*
|
||||
* Renders a 6-card grid of research modules (tools/sections) for the front page.
|
||||
* Each card checks if the target plugin is active; inactive cards are dimmed.
|
||||
*
|
||||
* @see front-page.php — includes this template part
|
||||
* @see style.css — .ps-module-* classes
|
||||
*
|
||||
* What: 3-column grid of research tool cards with SVG icons.
|
||||
* Who calls it: get_template_part( 'template-parts/module', 'cards' ).
|
||||
* Dependencies: None — all output is self-contained.
|
||||
*
|
||||
* @package peptide-starter
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Module card definitions with shortcode checks for graceful degradation.
|
||||
$modules = array(
|
||||
array(
|
||||
'title' => __( 'Peptides', 'peptide-starter' ),
|
||||
'description' => __( 'Browse the complete reagent directory with protocols and research parameters.', 'peptide-starter' ),
|
||||
'url' => '/peptides',
|
||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L12 22"/><path d="M5 12C5 8.13 8.13 5 12 5s7 3.13 7 7-3.13 7-7 7-7-3.13-7-7z"/><circle cx="12" cy="8" r="1.5" fill="currentColor" stroke="none"/><circle cx="12" cy="16" r="1.5" fill="currentColor" stroke="none"/></svg>',
|
||||
'available' => shortcode_exists( 'peptide_directory' ) || post_type_exists( 'peptide' ),
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Calculator', 'peptide-starter' ),
|
||||
'description' => __( 'Precision reconstitution calculator for accurate laboratory preparations.', 'peptide-starter' ),
|
||||
'url' => '/calculator',
|
||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="2" width="16" height="20" rx="2"/><line x1="8" y1="6" x2="16" y2="6"/><line x1="8" y1="10" x2="10" y2="10"/><line x1="14" y1="10" x2="16" y2="10"/><line x1="8" y1="14" x2="10" y2="14"/><line x1="14" y1="14" x2="16" y2="14"/><line x1="8" y1="18" x2="16" y2="18"/></svg>',
|
||||
'available' => shortcode_exists( 'peptide_tools_calculator' ),
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Protocol Builder', 'peptide-starter' ),
|
||||
'description' => __( 'Interactive multi-step wizard for designing research protocols.', 'peptide-starter' ),
|
||||
'url' => '/protocol-builder',
|
||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2"/><rect x="9" y="3" width="6" height="4" rx="1"/><path d="M9 14l2 2 4-4"/></svg>',
|
||||
'available' => shortcode_exists( 'peptide_tools_protocol_builder' ),
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Tracker', 'peptide-starter' ),
|
||||
'description' => __( 'Log doses, manage active compounds, and forecast vial depletion timelines.', 'peptide-starter' ),
|
||||
'url' => '/tracker',
|
||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/><line x1="3" y1="20" x2="21" y2="20"/></svg>',
|
||||
'available' => shortcode_exists( 'peptide_tracker' ),
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Science Feed', 'peptide-starter' ),
|
||||
'description' => __( 'Latest research publications, clinical studies, and laboratory insights.', 'peptide-starter' ),
|
||||
'url' => '/news',
|
||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 22h16a2 2 0 002-2V4a2 2 0 00-2-2H8a2 2 0 00-2 2v16a2 2 0 01-2 2zm0 0a2 2 0 01-2-2v-9c0-1.1.9-2 2-2h2"/><line x1="10" y1="6" x2="18" y2="6"/><line x1="10" y1="10" x2="18" y2="10"/><line x1="10" y1="14" x2="14" y2="14"/></svg>',
|
||||
'available' => shortcode_exists( 'peptide_news' ),
|
||||
),
|
||||
array(
|
||||
'title' => __( 'Subject Log', 'peptide-starter' ),
|
||||
'description' => __( 'Longitudinal tracking for biomarker data and protocol history.', 'peptide-starter' ),
|
||||
'url' => '/subject-log',
|
||||
'icon' => '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>',
|
||||
'available' => shortcode_exists( 'peptide_tracker_subject_log' ),
|
||||
),
|
||||
);
|
||||
?>
|
||||
|
||||
<section class="ps-modules-section">
|
||||
<div class="ps-container-wide">
|
||||
<div class="ps-modules-header">
|
||||
<h2 class="ps-modules-title"><?php esc_html_e( 'Research Modules', 'peptide-starter' ); ?></h2>
|
||||
<p class="ps-modules-subtitle">
|
||||
<?php esc_html_e( 'Tools and resources for laboratory research and data management.', 'peptide-starter' ); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="ps-modules-grid">
|
||||
<?php foreach ( $modules as $module ) : ?>
|
||||
<a href="<?php echo esc_url( home_url( $module['url'] ) ); ?>"
|
||||
class="ps-module-card <?php echo $module['available'] ? '' : 'ps-module-card--inactive'; ?>">
|
||||
<div class="ps-module-card__icon">
|
||||
<?php
|
||||
// SVG icons are defined in this template — safe to output directly.
|
||||
echo $module['icon']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- static SVG
|
||||
?>
|
||||
</div>
|
||||
<h3 class="ps-module-card__title"><?php echo esc_html( $module['title'] ); ?></h3>
|
||||
<p class="ps-module-card__description"><?php echo esc_html( $module['description'] ); ?></p>
|
||||
<span class="ps-module-card__link">
|
||||
<?php esc_html_e( 'Open', 'peptide-starter' ); ?> →
|
||||
</span>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
82
template-parts/newsletter-signup.php
Normal file
82
template-parts/newsletter-signup.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* Template Part: Newsletter Signup
|
||||
*
|
||||
* Reusable newsletter signup section displayed on front page and science feed.
|
||||
* Stores emails in wp_options as a placeholder until Mailchimp/similar is integrated.
|
||||
*
|
||||
* @see functions.php — peptide_starter_handle_newsletter_signup() for form processing
|
||||
* @see front-page.php — includes this template part
|
||||
* @see page-science-feed.php — includes this template part
|
||||
*
|
||||
* What: Renders the "Monthly Research Dispatch" email signup form.
|
||||
* Who calls it: get_template_part( 'template-parts/newsletter', 'signup' ).
|
||||
* Dependencies: None — self-contained HTML form.
|
||||
*
|
||||
* @package peptide-starter
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Check for newsletter submission status messages.
|
||||
$newsletter_status = isset( $_GET['ps_newsletter'] ) ? sanitize_text_field( wp_unslash( $_GET['ps_newsletter'] ) ) : '';
|
||||
$status_message = '';
|
||||
$status_class = '';
|
||||
|
||||
if ( 'success' === $newsletter_status ) {
|
||||
$status_message = __( 'Thanks for subscribing! You will receive our next dispatch.', 'peptide-starter' );
|
||||
$status_class = 'ps-alert-success';
|
||||
} elseif ( 'invalid' === $newsletter_status ) {
|
||||
$status_message = __( 'Please enter a valid email address.', 'peptide-starter' );
|
||||
$status_class = 'ps-alert-error';
|
||||
} elseif ( 'duplicate' === $newsletter_status ) {
|
||||
$status_message = __( 'This email is already subscribed.', 'peptide-starter' );
|
||||
$status_class = 'ps-alert-info';
|
||||
}
|
||||
?>
|
||||
|
||||
<section class="ps-newsletter-section">
|
||||
<div class="ps-container">
|
||||
<div class="ps-newsletter-inner">
|
||||
<h2 class="ps-newsletter-title">
|
||||
<?php esc_html_e( 'Monthly Research Dispatch', 'peptide-starter' ); ?>
|
||||
</h2>
|
||||
<p class="ps-newsletter-subtitle">
|
||||
<?php esc_html_e( "First edition drops soon. Don't miss the data.", 'peptide-starter' ); ?>
|
||||
</p>
|
||||
|
||||
<?php if ( $status_message ) : ?>
|
||||
<div class="ps-alert <?php echo esc_attr( $status_class ); ?> ps-newsletter-status" role="alert">
|
||||
<p><?php echo esc_html( $status_message ); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form class="ps-newsletter-form" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post">
|
||||
<?php wp_nonce_field( 'peptide_starter_newsletter', 'ps_newsletter_nonce' ); ?>
|
||||
<input type="hidden" name="action" value="peptide_starter_newsletter_signup">
|
||||
<div class="ps-newsletter-form-row">
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
class="ps-newsletter-input"
|
||||
placeholder="<?php esc_attr_e( 'your@email.com', 'peptide-starter' ); ?>"
|
||||
required
|
||||
aria-label="<?php esc_attr_e( 'Email address', 'peptide-starter' ); ?>"
|
||||
>
|
||||
<button type="submit" class="ps-btn ps-btn-primary">
|
||||
<?php esc_html_e( 'Subscribe', 'peptide-starter' ); ?>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="ps-newsletter-learn-more">
|
||||
<a href="<?php echo esc_url( home_url( '/documentation' ) ); ?>">
|
||||
<?php esc_html_e( 'Learn more about the dispatch', 'peptide-starter' ); ?> →
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
102
template-parts/settings-panel.php
Normal file
102
template-parts/settings-panel.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
/**
|
||||
* Template Part: Settings Panel
|
||||
*
|
||||
* Slide-out panel triggered by the settings icon in the header.
|
||||
* Contains contact form, compound data submission link, and support info.
|
||||
*
|
||||
* @see assets/js/settings-panel.js — open/close toggle
|
||||
* @see functions.php — AJAX handler for contact form
|
||||
* @see header.php — settings icon trigger
|
||||
*
|
||||
* What: Slide-out support panel with contact form and links.
|
||||
* Who calls it: get_template_part( 'template-parts/settings', 'panel' ) in footer.php.
|
||||
* Dependencies: None — self-contained HTML.
|
||||
*
|
||||
* @package peptide-starter
|
||||
*/
|
||||
|
||||
// Exit if accessed directly.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$current_user = wp_get_current_user();
|
||||
$user_name = is_user_logged_in() ? $current_user->display_name : '';
|
||||
$user_email = is_user_logged_in() ? $current_user->user_email : '';
|
||||
?>
|
||||
|
||||
<!-- Settings Panel Overlay -->
|
||||
<div class="ps-settings-overlay" id="ps-settings-overlay" aria-hidden="true"></div>
|
||||
|
||||
<!-- Settings Panel -->
|
||||
<aside class="ps-settings-panel" id="ps-settings-panel" aria-hidden="true" aria-label="<?php esc_attr_e( 'Support panel', 'peptide-starter' ); ?>">
|
||||
<div class="ps-settings-panel__header">
|
||||
<h2><?php esc_html_e( 'Support', 'peptide-starter' ); ?></h2>
|
||||
<button class="ps-settings-panel__close" id="ps-settings-close" aria-label="<?php esc_attr_e( 'Close panel', 'peptide-starter' ); ?>">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
||||
<path d="M15 5L5 15M5 5L15 15"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="ps-settings-panel__body">
|
||||
<!-- Contact Engineering Form -->
|
||||
<div class="ps-settings-section">
|
||||
<h3><?php esc_html_e( 'Contact Engineering', 'peptide-starter' ); ?></h3>
|
||||
<form id="ps-contact-form" class="ps-contact-form" novalidate>
|
||||
<?php wp_nonce_field( 'ps_contact_form', 'ps_contact_nonce' ); ?>
|
||||
<input type="hidden" name="action" value="ps_contact_submit">
|
||||
|
||||
<div class="ps-form-group">
|
||||
<label for="ps-contact-name"><?php esc_html_e( 'Name', 'peptide-starter' ); ?></label>
|
||||
<input type="text" id="ps-contact-name" name="name" value="<?php echo esc_attr( $user_name ); ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="ps-form-group">
|
||||
<label for="ps-contact-email"><?php esc_html_e( 'Email', 'peptide-starter' ); ?></label>
|
||||
<input type="email" id="ps-contact-email" name="email" value="<?php echo esc_attr( $user_email ); ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="ps-form-group">
|
||||
<label for="ps-contact-topic"><?php esc_html_e( 'Topic', 'peptide-starter' ); ?></label>
|
||||
<select id="ps-contact-topic" name="topic" required>
|
||||
<option value=""><?php esc_html_e( 'Select a topic', 'peptide-starter' ); ?></option>
|
||||
<option value="bug"><?php esc_html_e( 'Bug Report', 'peptide-starter' ); ?></option>
|
||||
<option value="feature"><?php esc_html_e( 'Feature Request', 'peptide-starter' ); ?></option>
|
||||
<option value="data"><?php esc_html_e( 'Data Correction', 'peptide-starter' ); ?></option>
|
||||
<option value="other"><?php esc_html_e( 'Other', 'peptide-starter' ); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="ps-form-group">
|
||||
<label for="ps-contact-message"><?php esc_html_e( 'Description', 'peptide-starter' ); ?></label>
|
||||
<textarea id="ps-contact-message" name="message" rows="4" required></textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="ps-btn ps-btn-primary ps-btn-full">
|
||||
<?php esc_html_e( 'Send Message', 'peptide-starter' ); ?>
|
||||
</button>
|
||||
|
||||
<div id="ps-contact-status" class="ps-contact-status" aria-live="polite"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Submit Compound Data -->
|
||||
<div class="ps-settings-section">
|
||||
<h3><?php esc_html_e( 'Submit Compound Data', 'peptide-starter' ); ?></h3>
|
||||
<p><?php esc_html_e( 'Have data on a peptide not in our directory? Submit it for review.', 'peptide-starter' ); ?></p>
|
||||
<a href="<?php echo esc_url( home_url( '/compound-request' ) ); ?>" class="ps-btn ps-btn-secondary ps-btn-full">
|
||||
<?php esc_html_e( 'Submit Compound', 'peptide-starter' ); ?>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Support Policy -->
|
||||
<div class="ps-settings-section ps-settings-disclaimer">
|
||||
<h3><?php esc_html_e( 'Support Policy', 'peptide-starter' ); ?></h3>
|
||||
<p>
|
||||
<?php esc_html_e( 'Peptide Repo is a research reference database only. All data is provided for informational and educational purposes. This platform does not provide medical advice, diagnoses, or treatment recommendations. Always consult qualified professionals for clinical decisions.', 'peptide-starter' ); ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
Reference in New Issue
Block a user