feature: Initial commit

This commit is contained in:
Keith Solomon
2025-08-30 14:22:44 -05:00
commit 63fc9f034d
19 changed files with 1633 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
/**
* AA Events Frontend CSS.
*/
.aa-events-calendar-wrap { margin-bottom: 1.25rem; }
.aa-events-calendar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.625rem;
}
.aa-events-calendar-today-btn {
background: #aaa;
border-radius: 0.25rem;
color: #222;
display: inline-block;
font-weight: bold;
padding: 0.5rem 1rem;
text-decoration: none;
&:hover { background: #eee; }
}
.aa-events-calendar {
border-collapse: collapse;
table-layout: fixed;
width: 100%;
th, td {
border: 1px solid #aaa;
padding: 0.625rem;
text-align: left;
vertical-align: top;
}
th {
background-color: #f5f5f5;
text-align: center;
}
td { height: 9.375rem; }
.day-number {
font-weight: bold;
margin-bottom: 0.3125rem;
}
.today-cell {
background: #ffe9b3;
border: 2px solid #f7b500;
font-weight: bold;
}
.events-list {
list-style: none;
margin: 0;
padding: 0;
li { margin-bottom: 0.3125rem; }
}
}
.aa-event-item .aa-event-details {
border-top: 1px solid #ddd;
margin-top: 1.25rem;
padding-top: 1.25rem;
div { margin-bottom: 0.625rem; }
}
+104
View File
@@ -0,0 +1,104 @@
<?php
/**
* The template for displaying archive pages.
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/
*
* @package AA_Events
*/
get_header(); ?>
<div id="primary" class="aa-events-wrap">
<main id="main" class="aa-events-grid">
<?php
if ( have_posts() ) :
/* Start the Loop */
while ( have_posts() ) :
the_post();
?>
<article id="post-<?php echo esc_attr( get_the_ID() ); ?>" <?php post_class( 'aa-event-item' ); ?>>
<header class="entry-header">
<?php the_title( '<h2 class="entry-title aa-event-title"><a href="' . esc_url( get_permalink() ) . '">', '</a></h2>' ); ?>
</header><!-- .entry-header -->
<div class="entry-excerpt">
<?php the_excerpt(); ?>
</div><!-- .entry-excerpt -->
<footer class="entry-footer">
<div class="aa-event-details">
<div class="aa-event-date">
<strong><?php esc_html_e( 'Date:', 'aa-events' ); ?></strong>
<?php
$event_raw = get_field( 'event_datetime' );
if ( $event_raw ) {
$tz = wp_timezone();
$ts = ( new DateTimeImmutable( $event_raw, $tz ) )->getTimestamp();
$human = wp_date( 'F j, Y, g:i A', $ts );
$iso_attr = wp_date( 'c', $ts );
?>
<time datetime="<?php echo esc_attr( $iso_attr ); ?>"><?php echo esc_html( $human ); ?></time>
<?php
}
?>
</div>
<?php if ( get_field( 'event_cost' ) ) { ?>
<div class="aa-event-cost">
<strong><?php esc_html_e( 'Cost:', 'aa-events' ); ?></strong>
<?php echo esc_html( get_field( 'event_cost' ) ); ?>
</div>
<?php } ?>
<div class="aa-event-location">
<strong><?php esc_html_e( 'Location:', 'aa-events' ); ?></strong>
<?php
if ( get_field( 'event_location_type' ) ) {
// Online event
$location = get_field( 'event_url' );
echo '<a href="' . esc_url( $location ) . '" aria-label="' . esc_attr__( 'View event website', 'aa-events' ) . '">' . esc_html( $location ) . '</a>';
} else {
// In-person event
$location = get_field( 'event_address' );
echo '<p>' . esc_html( $location ) . '</p>';
}
?>
</div>
<?php if ( get_the_terms( get_the_ID(), 'event_type' ) ) { ?>
<div class="aa-event-type">
<strong><?php esc_html_e( 'Type:', 'aa-events' ); ?></strong>
<?php the_terms( get_the_ID(), 'event_type', '', ', ', '' ); ?>
</div>
<?php } ?>
<?php if ( get_the_terms( get_the_ID(), 'event_tag' ) ) { ?>
<div class="aa-event-tags">
<strong><?php esc_html_e( 'Tags:', 'aa-events' ); ?></strong>
<?php the_terms( get_the_ID(), 'event_tag', '', ', ', '' ); ?>
</div>
<?php } ?>
</div>
</footer><!-- .entry-footer -->
</article><!-- #post-<?php the_ID(); ?> -->
<?php
endwhile;
the_posts_navigation();
else :
?>
<p><?php esc_html_e( 'No events found.', 'aa-events' ); ?></p>
<?php
endif;
?>
</main><!-- #main -->
</div><!-- #primary -->
<?php
get_sidebar();
get_footer();
+231
View File
@@ -0,0 +1,231 @@
<?php
/**
* The template for displaying the events calendar.
*
* @package AA_Events
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
// Get current month and year (read-only query vars; validate instead of nonce)
$month_input = filter_input(
INPUT_GET,
'month',
FILTER_VALIDATE_INT,
array(
'options' => array(
'min_range' => 1,
'max_range' => 12,
),
)
);
$year_input = filter_input(
INPUT_GET,
'year',
FILTER_VALIDATE_INT,
array(
'options' => array(
// Choose a sane range for years.
'min_range' => 1970,
'max_range' => 2200,
),
)
);
$curMonth = ( false !== $month_input && null !== $month_input ) ? (int) $month_input : (int) wp_date( 'n' );
$curYear = ( false !== $year_input && null !== $year_input ) ? (int) $year_input : (int) wp_date( 'Y' );
// Get previous and next month and year.
$prev_month = $curMonth - 1;
$prev_year = $curYear;
if ( $prev_month < 1 ) {
$prev_month = 12;
--$prev_year;
}
$next_month = $curMonth + 1;
$next_year = $curYear;
if ( $next_month > 12 ) {
$next_month = 1;
++$next_year;
}
// Get the number of days in the month.
$days_in_month = cal_days_in_month( CAL_GREGORIAN, $curMonth, $curYear );
// Get the first day of the month in site timezone.
$tz = wp_timezone();
$first_ts = ( new DateTimeImmutable( sprintf( '%04d-%02d-01 00:00:00', $curYear, $curMonth ), $tz ) )->getTimestamp();
$first_day = (int) wp_date( 'N', $first_ts );
// Get events for the current month.
$events = new WP_Query(
array(
'post_type' => 'event',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'event_datetime',
'value' => array( "$curYear-$curMonth-01 00:00:00", "$curYear-$curMonth-$days_in_month 23:59:59" ),
'compare' => 'BETWEEN',
'type' => 'DATETIME',
),
),
)
);
$events_by_day = array();
if ( $events->have_posts() ) {
while ( $events->have_posts() ) {
$events->the_post();
$event_date = get_field( 'event_datetime' );
$event_ts = ( new DateTimeImmutable( $event_date, $tz ) )->getTimestamp();
$day = (int) wp_date( 'j', $event_ts );
$events_by_day[ $day ][] = '<a href="' . get_permalink() . '">' . get_the_title() . '</a>';
}
wp_reset_postdata();
}
// Get today's date.
$todayDay = intval( wp_date( 'j' ) );
$todayMonth = intval( wp_date( 'n' ) );
$todayYear = intval( wp_date( 'Y' ) );
// Helper to check if today is in current view.
$isTodayInCurrentMonth = ( $curMonth === $todayMonth && $curYear === $todayYear );
?>
<?php
// Modify the day cell output to add highlight for today.
$originalEchoDayCell = function ( $day, $firstDay, $eventsByDay ) use ( $isTodayInCurrentMonth, $todayDay ) {
$cellClass = '';
if ( $day === $todayDay && $curMonth === $todayMonth && $curYear === $todayYear ) {
$cellClass = 'today-cell';
}
echo '<td' . ( $cellClass ? ' class="' . esc_attr( $cellClass ) . '"' : '' ) . '>';
echo '<div class="day-number">' . esc_html( $day ) . '</div>';
if ( isset( $eventsByDay[ $day ] ) ) {
echo '<ul class="events-list">';
foreach ( $eventsByDay[ $day ] as $event ) {
echo '<li>' . wp_kses_post( $event ) . '</li>';
}
echo '</ul>';
}
echo '</td>';
};
// Overwrite the default day cell output in the table loop.
ob_start();
for ( $day = 1; $day <= $days_in_month; $day++ ) {
if ( ( $day + $first_day - 2 ) % 7 === 0 && $day > 1 ) {
echo '</tr><tr>';
}
$originalEchoDayCell( $day, $first_day, $events_by_day );
}
$calendarRows = ob_get_clean();
// Replace the original loop output with the new one.
$calendarOutput = preg_replace(
'/for\s*\(\s*\$day\s*=\s*1;\s*\$day\s*<=\s*\$days_in_month;\s*\$day\+\+\s*\)\s*\{.*?\}/s',
$calendarRows,
ob_get_contents()
);
?>
<div class="aa-events-calendar-wrap container">
<div class="aa-events-calendar-today-link" style="margin-bottom: 1em;">
<a href="?month=<?php echo esc_attr( $todayMonth ); ?>&year=<?php echo esc_attr( $todayYear ); ?>" class="aa-events-calendar-today-btn">
<?php esc_html_e( 'Today', 'aa-events' ); ?>
</a>
</div>
<div class="aa-events-calendar-header">
<?php
// Calculate previous and next month names and years for aria-labels (site timezone)
$prev_ts = ( new DateTimeImmutable( sprintf( '%04d-%02d-01 00:00:00', $prev_year, $prev_month ), $tz ) )->getTimestamp();
$next_ts = ( new DateTimeImmutable( sprintf( '%04d-%02d-01 00:00:00', $next_year, $next_month ), $tz ) )->getTimestamp();
$prevMonthName = wp_date( 'F', $prev_ts );
$nextMonthName = wp_date( 'F', $next_ts );
?>
<a
href="?month=<?php echo esc_attr( $prev_month ); ?>&year=<?php echo esc_attr( $prev_year ); ?>"
aria-label="<?php echo esc_attr( "Go to $prevMonthName $prev_year" ); ?>"
>&laquo; <?php esc_html_e( 'Previous Month', 'aa-events' ); ?></a>
<h2><?php echo esc_html( wp_date( 'F Y', $first_ts ) ); ?></h2>
<a
href="?month=<?php echo esc_attr( $next_month ); ?>&year=<?php echo esc_attr( $next_year ); ?>"
aria-label="<?php echo esc_attr( "Go to $nextMonthName $next_year" ); ?>"
><?php esc_html_e( 'Next Month', 'aa-events' ); ?> &raquo;</a>
</div>
<table class="aa-events-calendar" role="grid" aria-labelledby="calendar-heading">
<caption id="calendar-heading" class="screen-reader-text"><?php echo esc_html( gmdate( 'F Y', strtotime( "$curYear-$curMonth-01" ) ) ); ?> <?php esc_html_e( 'Events Calendar', 'aa-events' ); ?></caption>
<thead>
<tr>
<th scope="col" abbr="<?php esc_attr_e( 'Mon', 'aa-events' ); ?>"><?php esc_html_e( 'Monday', 'aa-events' ); ?></th>
<th scope="col" abbr="<?php esc_attr_e( 'Tue', 'aa-events' ); ?>"><?php esc_html_e( 'Tuesday', 'aa-events' ); ?></th>
<th scope="col" abbr="<?php esc_attr_e( 'Wed', 'aa-events' ); ?>"><?php esc_html_e( 'Wednesday', 'aa-events' ); ?></th>
<th scope="col" abbr="<?php esc_attr_e( 'Thu', 'aa-events' ); ?>"><?php esc_html_e( 'Thursday', 'aa-events' ); ?></th>
<th scope="col" abbr="<?php esc_attr_e( 'Fri', 'aa-events' ); ?>"><?php esc_html_e( 'Friday', 'aa-events' ); ?></th>
<th scope="col" abbr="<?php esc_attr_e( 'Sat', 'aa-events' ); ?>"><?php esc_html_e( 'Saturday', 'aa-events' ); ?></th>
<th scope="col" abbr="<?php esc_attr_e( 'Sun', 'aa-events' ); ?>"><?php esc_html_e( 'Sunday', 'aa-events' ); ?></th>
</tr>
</thead>
<tbody>
<tr>
<?php
// Add empty cells for the first week.
for ( $i = 1; $i < $first_day; $i++ ) {
echo '<td role="gridcell" class="empty-cell"></td>';
}
// Loop through the days of the month.
for ( $day = 1; $day <= $days_in_month; $day++ ) {
if ( ( $day + $first_day - 2 ) % 7 === 0 && $day > 1 ) {
echo '</tr><tr>';
}
$cellClass = '';
if ( $isTodayInCurrentMonth && $day === $todayDay ) {
$cellClass = 'today-cell';
}
$cell_ts = ( new DateTimeImmutable( sprintf( '%04d-%02d-%02d 00:00:00', $curYear, $curMonth, $day ), $tz ) )->getTimestamp();
echo '<td role="gridcell" aria-label="' . esc_attr( wp_date( 'F j, Y', $cell_ts ) ) . '" ' . ( $cellClass ? ' class="' . esc_attr( $cellClass ) . '"' : '' ) . '>';
echo '<div class="day-number">' . esc_html( $day ) . '</div>';
if ( isset( $events_by_day[ $day ] ) ) {
echo '<ul class="events-list">';
foreach ( $events_by_day[ $day ] as $event ) {
echo '<li>' . wp_kses_post( $event ) . '</li>';
}
echo '</ul>';
}
echo '</td>';
}
// Add empty cells for the last week.
$remaining_days = 7 - ( ( $days_in_month + $first_day - 1 ) % 7 );
if ( $remaining_days < 7 ) {
for ( $i = 0; $i < $remaining_days; $i++ ) {
echo '<td class="empty-cell"></td>';
}
}
?>
</tr>
</tbody>
</table>
</div>
+102
View File
@@ -0,0 +1,102 @@
<?php
/**
* The template for displaying all single posts.
*
* @link https://developer.wordpress.org/themes/basics/template-hierarchy/#single-post
*
* @package AA_Events
*/
get_header(); ?>
<div id="primary" class="aa-events-wrap">
<main id="main" class="aa-event-single">
<?php
while ( have_posts() ) :
the_post();
?>
<article id="post-<?php echo esc_attr( get_the_ID() ); ?>" <?php post_class( 'aa-event-item' ); ?>>
<header class="entry-header">
<?php the_title( '<h2 class="entry-title aa-event-title">', '</h2>' ); ?>
</header><!-- .entry-header -->
<div class="entry-wrap">
<div class="entry-content">
<?php the_content(); ?>
</div><!-- .entry-content -->
<?php if ( has_post_thumbnail() ) : ?>
<div class="entry-thumbnail">
<?php the_post_thumbnail( 'medium', array( 'alt' => the_title_attribute( 'echo=0' ) ) ); ?>
</div>
<?php endif; ?>
</div><!-- .entry-wrap -->
<footer class="entry-footer">
<div class="aa-event-details">
<div class="aa-event-date">
<strong><?php esc_html_e( 'Date:', 'aa-events' ); ?></strong>
<?php
$event_raw = get_field( 'event_datetime' );
if ( $event_raw ) {
$tz = wp_timezone();
$ts = ( new DateTimeImmutable( $event_raw, $tz ) )->getTimestamp();
$human = wp_date( 'F j, Y, g:i A', $ts );
$iso_attr = wp_date( 'c', $ts );
?>
<time datetime="<?php echo esc_attr( $iso_attr ); ?>"><?php echo esc_html( $human ); ?></time>
<?php
}
?>
</div>
<?php if ( get_field( 'event_cost' ) ) { ?>
<div class="aa-event-cost">
<strong><?php esc_html_e( 'Cost:', 'aa-events' ); ?></strong>
<?php echo esc_html( get_field( 'event_cost' ) ); ?>
</div>
<?php } ?>
<div class="aa-event-location">
<strong><?php esc_html_e( 'Location:', 'aa-events' ); ?></strong>
<?php
if ( get_field( 'event_location_type' ) ) {
// Online event
$location = get_field( 'event_url' );
echo '<a href="' . esc_url( $location ) . '" aria-label="' . esc_attr__( 'View event website for ' . get_the_title(), 'aa-events' ) . '">' . esc_html( $location ) . '</a>';
} else {
// In-person event
$location = get_field( 'event_address' );
echo '<p>' . esc_html( $location ) . '</p>';
}
?>
</div>
<?php if ( get_the_terms( get_the_ID(), 'event_type' ) ) { ?>
<div class="aa-event-type">
<strong><?php esc_html_e( 'Type:', 'aa-events' ); ?></strong>
<?php the_terms( get_the_ID(), 'event_type', '', ', ', '' ); ?>
</div>
<?php } ?>
<?php if ( get_the_terms( get_the_ID(), 'event_tag' ) ) { ?>
<div class="aa-event-tags">
<strong><?php esc_html_e( 'Tags:', 'aa-events' ); ?></strong>
<?php the_terms( get_the_ID(), 'event_tag', '', ', ', '' ); ?>
</div>
<?php } ?>
</div>
</footer><!-- .entry-footer -->
</article><!-- #post-<?php the_ID(); ?> -->
<?php
the_post_navigation();
endwhile; // End of the loop.
?>
</main><!-- #main -->
</div><!-- #primary -->
<?php
get_sidebar();
get_footer();
+26
View File
@@ -0,0 +1,26 @@
<?php
/**
* Template Name: Events Calendar
*
* @package AA_Events
*/
get_header(); ?>
<div id="primary" class="content-area">
<main id="main" class="site-main">
<?php
while ( have_posts() ) :
the_post();
// Include the calendar content.
events_get_template( 'calendar.php' );
endwhile; // End of the loop.
?>
</main><!-- #main -->
</div><!-- #primary -->
<?php
get_footer();