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
+2
View File
@@ -0,0 +1,2 @@
notes/
phpcs-results.txt
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0"?>
<ruleset name="Coding Style Checks">
<description>Coding Style Checks.</description>
<ini name="error_reporting" value="E_ALL &#38; ~E_DEPRECATED" />
<arg value="sp"/>
<arg name="colors"/>
<arg name="extensions" value="php,blade.php,html"/>
<arg name="parallel" value="2048"/>
<exclude-pattern>vendor/</exclude-pattern>
<exclude-pattern>node_modules/</exclude-pattern>
<rule ref="WordPress">
<exclude name="Generic.WhiteSpace.DisallowSpaceIndent.SpacesUsed"/>
<exclude name="Universal.WhiteSpace.PrecisionAlignment.Found"/>
<exclude name="WordPress.WP.I18n.NonSingularStringLiteralText"/>
<exclude name="WordPress.WP.I18n.NonSingularStringLiteralDomain"/>
<exclude name="WordPress.WP.I18n.NoEmptyStrings"/>
<exclude name="WordPress.WP.EnqueuedResourceParameters.MissingVersion"/>
<exclude name="WordPress.PHP.YodaConditions.NotYoda"/>
<exclude name="WordPress.PHP.NoSilencedErrors.Discouraged"/>
<exclude name="WordPress.Files.FileName.NotHyphenatedLowercase"/>
<exclude name="WordPress.Files.FileName.InvalidClassFileName"/>
<exclude name="WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase"/>
<exclude name="WordPress.NamingConventions.ValidVariableName.InterpolatedVariableNotSnakeCase"/>
<exclude name="WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase"/>
<exclude name="WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase"/>
<exclude name="WordPress.NamingConventions.ValidHookName.NotLowercase"/>
<exclude name="WordPress.NamingConventions.ValidHookName.UseUnderscores"/>
<exclude name="WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid"/>
<exclude name="WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid"/>
<exclude name="WordPress.DB.SlowDBQuery.slow_db_query_tax_query"/>
<exclude name="WordPress.DB.SlowDBQuery.slow_db_query_meta_key"/>
<exclude name="WordPress.DB.SlowDBQuery.slow_db_query_meta_query"/>
<exclude name="Squiz.Commenting.FileComment.MissingPackageTag"/>
<exclude name="Squiz.Commenting.FileComment.Missing"/>
<exclude name="Squiz.Commenting.FileComment.WrongStyle"/>
<exclude name="Squiz.Commenting.InlineComment.InvalidEndChar"/>
</rule>
<rule ref="Internal.NoCodeFound">
<severity>0</severity>
</rule>
</ruleset>
+8
View File
@@ -0,0 +1,8 @@
{
"workbench.colorCustomizations": {
"tree.indentGuidesStroke": "#3d92ec",
"activityBar.background": "#123509",
"titleBar.activeBackground": "#1A4A0D",
"titleBar.activeForeground": "#F5FDF3"
}
}
+200
View File
@@ -0,0 +1,200 @@
# AA Events
An approachable, themefriendly events plugin for WordPress. It adds an `Event` post type, useful taxonomies, ACFpowered fields, and accessible frontend templates (archive, single, and a calendar page template). Everything can be overridden from your theme when you need custom markup.
## What you get
- Event post type with archive at `/events`.
- Two taxonomies: `Event Types` (hierarchical) and `Event Tags` (nonhierarchical).
- Event details via ACF: date/time, cost, location (online URL vs inperson address).
- Frontend templates: archive list, single view, and a calendar page template.
- Theme overrides for templates and styles with simple folder conventions.
- Sensible defaults: admin column for date, sortable by event date; taxonomy views sort upcoming first; main events archive sorts newest first.
---
## Requirements
- WordPress 5.8+ (uses modern APIs like `wp_date()` and template filters).
- Advanced Custom Fields (free) is required for the event fields. The plugin will load without ACF, but the Event fields and related UI will not be available until ACF is active.
---
## Installation
1. Copy the `aa-events` folder into `wp-content/plugins/`.
2. (Required) Install and activate the “Advanced Custom Fields” plugin.
3. In WordPress Admin → Plugins, activate “AA Events”.
4. Visit Settings → Permalinks and click Save if your events archive doesnt appear (activation also flushes rewrites).
---
## Quick Start
1. Create events: Admin → Events → Add New.
2. Fill in the “Event Details” fields:
- Event Date & Time (required)
- Event Cost (optional)
- Online/InPerson toggle (required)
- Event URL (required when Online)
- Event Address (required when InPerson)
3. View your list of events at `/events` and single event pages at their permalinks.
4. For a calendar view, create a new Page and choose the “Events Calendar” template, then publish.
---
## Content Model
- Post type: `event`
- Public, has archive (`/events`), supports title, editor, author, thumbnail.
- Default sort:
- Admin list: ascending by Event Date & Time.
- Taxonomy views: ascending (upcoming first).
- Main events archive: descending (newest first).
- Taxonomies
- `event_type` (hierarchical, slug `event-type`)
- `event_tag` (nonhierarchical, slug `event-tag`)
- Admin UX
- Adds a “Date & Time” column on Events list, sortable by the event date.
---
## FrontEnd Views
- Archive: `/events` uses `templates/archive-event.php`.
- Single: each event uses `templates/single-event.php`.
- Calendar page: Page → Template → “Events Calendar” renders `templates/template-calendar.php` which includes `templates/calendar.php`.
- Calendar navigation: query args `?month=MM&year=YYYY` change the visible month; “Today” jumps back to the current month.
---
## Theme Overrides
The plugin is designed to be themeoverride friendly. Copy any template into your theme under `your-theme/aa-events/` and it will be used instead of the plugin version.
- Templates you can override:
- `archive-event.php`
- `single-event.php`
- `template-calendar.php` (page template)
- `calendar.php`
Example: to override the archive, create `your-theme/aa-events/archive-event.php`.
Page template in a theme: the “Events Calendar” page template also respects theme overrides at `your-theme/aa-events/template-calendar.php`.
---
## Styling
- Default CSS ships with the plugin and is enqueued automatically.
- To replace it, add `your-theme/aa-events/aa-events.css`. If present, the theme stylesheet is loaded instead of the plugins.
Note: The bundled CSS is intentionally minimal. Use the override to match your theme.
---
## Developer Notes
- Template loader helpers:
- `events_get_template( $template_name, $args = [], $template_path = 'aa-events/', $default_path = AA_EVENTS_PLUGIN_DIR . 'templates/' )`
- `events_locate_template( $template_name, $template_path = 'aa-events/', $default_path = AA_EVENTS_PLUGIN_DIR . 'templates/' )`
- Filters/actions:
- `events_get_template` (filter: final located path)
- `events_locate_template` (filter: path resolution)
- `events_before_template_part` / `events_after_template_part` (actions around includes)
- Page template registration (no theme file required):
- Registered as “Events Calendar” and loads from the plugin when selected.
---
## Screenshots
A quick gallery of the key screens. The images below are placeholders you can replace. Drop your own PNG/JPG/GIF assets into `assets/screenshots/` with the same filenames to update the gallery.
| Admin Events List | Event Edit | Events Archive | Calendar Page |
| --- | --- | --- | --- |
| ![Admin Events List](assets/screenshots/01-admin-events-list.png) | ![Event Edit](assets/screenshots/02-event-edit-fields.png) | ![Events Archive](assets/screenshots/03-events-archive.png) | ![Calendar Page](assets/screenshots/04-calendar-template.png) |
Tip: GIFs are great for showing calendar navigation (Today/Prev/Next). If you add GIFs, keep the same names but `.gif` extension and update the links here.
---
## Override Examples
Below are minimal examples showing how to override templates and styles from a theme. Copy into your theme under `your-theme/aa-events/`.
Example: override archive layout at `your-theme/aa-events/archive-event.php`:
```php
<?php
/* Template: Events Archive (Theme Override) */
get_header(); ?>
<div id="primary" class="content-area">
<main id="main" class="site-main">
<?php if ( have_posts() ) : ?>
<header class="page-header"><h1>Upcoming Events</h1></header>
<?php while ( have_posts() ) : the_post(); ?>
<article <?php post_class('aa-event-item'); ?>>
<h2 class="aa-event-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<p class="aa-event-when">
<strong>Date:</strong>
<time datetime="<?php echo esc_attr( get_field('event_datetime') ); ?>">
<?php echo esc_html( get_field('event_datetime') ); ?>
</time>
</p>
<div class="aa-event-excerpt"><?php the_excerpt(); ?></div>
</article>
<?php endwhile; the_posts_navigation(); ?>
<?php else : ?>
<p>No events found.</p>
<?php endif; ?>
</main>
</div>
<?php
get_sidebar();
get_footer();
```
Example: tweak the event card at `your-theme/aa-events/single-event.php`:
```php
<article <?php post_class('aa-event-item'); ?>>
<h2 class="aa-event-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<p class="aa-event-when">
<strong>Date:</strong>
<time datetime="<?php echo esc_attr( get_field('event_datetime') ); ?>">
<?php echo esc_html( get_field('event_datetime') ); ?>
</time>
</p>
<?php the_excerpt(); ?>
<p><a class="button" href="<?php the_permalink(); ?>">View details</a></p>
</article>
```
Example: replace styles by adding `your-theme/aa-events/aa-events.css`:
```css
/* Theme override for AA Events */
.aa-events-calendar th,
.aa-events-calendar td { border-color: #ddd; }
.aa-events-calendar .today-cell { background: #e6f7ff; border-color: #1890ff; }
.aa-event-title { font-size: 1.25rem; margin: .25rem 0 .5rem; }
```
---
## FAQ
- The “Events Calendar” template doesnt show up?
- Ensure the plugin is active. The template is registered by the plugin and appears in the Page Template dropdown. If you still dont see it, check the Page sidebar and expand the Template panel.
- Calendar shows no events?
- Make sure you created Event posts and set the “Event Date & Time”. The calendar shows events within the selected month using site timezone.
- Can I use only the archive without the calendar?
- Yes. The archive at `/events` works independently of the calendar page template.
---
## License
GPL-2.0-or-later. See `LICENSE` or the plugin header for details.
+68
View File
@@ -0,0 +1,68 @@
<?php
/**
* Plugin Name: AA Events
* Plugin URI: https://github.com/automattic/aa-events
* Description: An events plugin for WordPress.
* Version: 1.0.0
* Author: Keith Solomon
* Author URI: https://vincentdesign.ca
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* Text Domain: aa-events
* Domain Path: /languages
*/
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* The code that runs during plugin activation.
*/
function activate_aa_events() {
// Ensure the CPT and taxonomies are registered.
include_once plugin_dir_path( __FILE__ ) . 'includes/post-types.php';
aa_events_register_post_type();
aa_events_register_event_type_taxonomy();
aa_events_register_event_tag_taxonomy();
// Flush rewrite rules.
flush_rewrite_rules();
}
/**
* The code that runs during plugin deactivation.
*/
function deactivate_aa_events() {
// Flush rewrite rules.
flush_rewrite_rules();
}
register_activation_hook( __FILE__, 'activate_aa_events' );
register_deactivation_hook( __FILE__, 'deactivate_aa_events' );
/**
* Define Constants
*/
define( 'AA_EVENTS_PLUGIN_FILE', __FILE__ );
define( 'AA_EVENTS_VERSION', '1.0.0' );
define( 'AA_EVENTS_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
define( 'AA_EVENTS_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
/**
* Include the main class.
*/
require_once plugin_dir_path( __FILE__ ) . 'includes/class-aa-events.php';
/**
* Returns the main instance of AA_Events.
*
* @return AA_Events
*/
function AA_Events() {
return AA_Events::instance();
}
// Get AA_Events Running.
AA_Events();
+109
View File
@@ -0,0 +1,109 @@
/**
* AA Events Frontend CSS.
*/
.aa-events-calendar-wrap { margin-bottom: 1.25rem; }
.aa-events-wrap {
margin-left: auto;
margin-right: auto;
max-width: 96rem;
}
.aa-events-calendar-header {
align-items: center;
display: flex;
justify-content: space-between;
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 #333;
margin-top: 1.25rem;
padding-top: 1.25rem;
div { margin-bottom: 0.625rem; }
}
/* Events archive layout */
.aa-events-wrap .aa-events-grid {
display: grid;
gap: 1.5rem;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
/* Optional: responsive adjustments */
@media (max-width: 1024px) {
.aa-events-wrap .aa-events-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
.aa-events-wrap .aa-events-grid { grid-template-columns: 1fr; }
}
.aa-events-wrap .entry-header:has(.entry-thumbnail),
.aa-events-wrap .entry-wrap:has(.entry-thumbnail) {
display: flex;
justify-content: space-between;
.entry-thumbnail {
margin-left: 2rem;
width: 100%;
img {
height: auto;
width: 100%;
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

+152
View File
@@ -0,0 +1,152 @@
<?php
/**
* Register ACF Fields.
*
* @package AA_Events
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Register the ACF field group.
*/
function events_register_acf_fields() {
if ( function_exists( 'acf_add_local_field_group' ) ) :
acf_add_local_field_group(
array(
'key' => 'group_61b0c5f5a3e7e',
'title' => 'Event Details',
'fields' => array(
array(
'key' => 'field_61b0c609a3e7f',
'label' => 'Event Date & Time',
'name' => 'event_datetime',
'type' => 'date_time_picker',
'instructions' => '',
'required' => 1,
'conditional_logic' => 0,
'wrapper' => array(
'width' => '',
'class' => '',
'id' => '',
),
'display_format' => 'F j, Y g:i a',
'return_format' => 'Y-m-d H:i:s',
'first_day' => 1,
),
array(
'key' => 'field_61b0c6a5a3e82',
'label' => 'Event Cost',
'name' => 'event_cost',
'type' => 'text',
'instructions' => '',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => array(
'width' => '',
'class' => '',
'id' => '',
),
'default_value' => '',
'placeholder' => '',
'prepend' => '',
'append' => '',
'maxlength' => '',
),
array(
'key' => 'field_61b0c6cba3e83',
'label' => 'Online/In-Person',
'name' => 'event_location_type',
'type' => 'true_false',
'instructions' => '',
'required' => 0,
'conditional_logic' => 0,
'wrapper' => array(
'width' => '',
'class' => '',
'id' => '',
),
'message' => '',
'default_value' => 0,
'ui' => 1,
'ui_on_text' => 'Online',
'ui_off_text' => 'In-Person',
),
array(
'key' => 'field_61b0c702a3e84',
'label' => 'Event URL',
'name' => 'event_url',
'type' => 'url',
'instructions' => '',
'required' => 1,
'conditional_logic' => array(
array(
array(
'field' => 'field_61b0c6cba3e83',
'operator' => '==',
'value' => '1',
),
),
),
'wrapper' => array(
'width' => '',
'class' => '',
'id' => '',
),
'default_value' => '',
'placeholder' => '',
),
array(
'key' => 'field_61b0c72fa3e85',
'label' => 'Event Address',
'name' => 'event_address',
'type' => 'textarea',
'instructions' => '',
'required' => 1,
'conditional_logic' => array(
array(
array(
'field' => 'field_61b0c6cba3e83',
'operator' => '!=',
'value' => '1',
),
),
),
'wrapper' => array(
'width' => '',
'class' => '',
'id' => '',
),
'default_value' => '',
'placeholder' => '',
'maxlength' => '',
'rows' => '',
'new_lines' => '',
),
),
'location' => array(
array(
array(
'param' => 'post_type',
'operator' => '==',
'value' => 'event',
),
),
),
'menu_order' => 0,
'position' => 'normal',
'style' => 'default',
'label_placement' => 'top',
'instruction_placement' => 'label',
'hide_on_screen' => '',
'active' => true,
'description' => '',
)
);
endif;
}
add_action( 'acf/init', 'events_register_acf_fields' );
+128
View File
@@ -0,0 +1,128 @@
<?php
/**
* The core plugin class.
*
* @package AA_Events
*/
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
die;
}
/**
* The core plugin class.
*/
final class AA_Events {
/**
* The single instance of the class.
*
* @var AA_Events
*/
protected static $instance = null;
/**
* Main AA_Events Instance.
*
* Ensures only one instance of AA_Events is loaded or can be loaded.
*
* @static
* @return AA_Events - Main instance.
*/
public static function instance() {
if ( is_null( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* AA_Events Constructor.
*/
public function __construct() {
$this->includes();
$this->init_hooks();
}
/**
* Include required core files.
*/
public function includes() {
include_once AA_EVENTS_PLUGIN_DIR . 'includes/post-types.php';
include_once AA_EVENTS_PLUGIN_DIR . 'includes/acf-fields.php';
include_once AA_EVENTS_PLUGIN_DIR . 'includes/template-loader.php';
}
/**
* Hook into actions and filters.
*/
private function init_hooks() {
add_action( 'init', array( $this, 'load_plugin_textdomain' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) );
// Ensure the active theme supports thumbnails for the event post type.
add_action( 'after_setup_theme', array( $this, 'ensure_post_thumbnails_support' ) );
}
/**
* Enqueue styles.
*/
public function enqueue_styles() {
$theme_stylesheet = get_stylesheet_directory() . '/aa-events/aa-events.css';
if ( file_exists( $theme_stylesheet ) ) {
wp_enqueue_style(
'aa-events-theme',
get_stylesheet_directory_uri() . '/aa-events/aa-events.css',
array(),
filemtime( $theme_stylesheet )
);
} else {
wp_enqueue_style(
'aa-events',
AA_EVENTS_PLUGIN_URL . 'assets/css/aa-events.css',
array(),
AA_EVENTS_VERSION
);
}
}
/**
* Load Localisation files.
*/
public function load_plugin_textdomain() {
load_plugin_textdomain(
'aa-events',
false,
dirname( plugin_basename( __FILE__ ) ) . '/languages/'
);
}
/**
* Ensure the theme supports post thumbnails for events.
*
* Some themes do not declare `add_theme_support( 'post-thumbnails' )`,
* which hides the Featured Image panel. This enables thumbnails at least
* for the `event` post type without impacting other types.
*/
public function ensure_post_thumbnails_support() {
$support = get_theme_support( 'post-thumbnails' );
// If theme has no support at all, enable it for events only.
if ( false === $support ) {
add_theme_support( 'post-thumbnails', array( 'event' ) );
return;
}
// If theme supports specific post types, ensure 'event' is included.
if ( is_array( $support ) && isset( $support[0] ) && is_array( $support[0] ) ) {
$post_types = $support[0];
if ( ! in_array( 'event', $post_types, true ) ) {
$post_types[] = 'event';
add_theme_support( 'post-thumbnails', $post_types );
}
}
// If theme support is boolean true (all types), nothing to do.
}
}
+224
View File
@@ -0,0 +1,224 @@
<?php
/**
* Register Custom Post Types and Taxonomies.
*
* @package AA_Events
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Register a custom post type for events.
*/
function events_register_post_type() {
$labels = array(
'name' => _x( 'Events', 'post type general name', 'aa-events' ),
'singular_name' => _x( 'Event', 'post type singular name', 'aa-events' ),
'menu_name' => _x( 'Events', 'admin menu', 'aa-events' ),
'name_admin_bar' => _x( 'Event', 'add new on admin bar', 'aa-events' ),
'add_new' => _x( 'Add New', 'event', 'aa-events' ),
'add_new_item' => __( 'Add New Event', 'aa-events' ),
'new_item' => __( 'New Event', 'aa-events' ),
'edit_item' => __( 'Edit Event', 'aa-events' ),
'view_item' => __( 'View Event', 'aa-events' ),
'all_items' => __( 'All Events', 'aa-events' ),
'search_items' => __( 'Search Events', 'aa-events' ),
'parent_item_colon' => __( 'Parent Events:', 'aa-events' ),
'not_found' => __( 'No events found.', 'aa-events' ),
'not_found_in_trash' => __( 'No events found in Trash.', 'aa-events' ),
);
$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'events' ),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 5,
'supports' => array( 'title', 'editor', 'author', 'thumbnail' ),
'menu_icon' => 'dashicons-calendar-alt',
);
register_post_type( 'event', $args );
}
add_action( 'init', 'events_register_post_type' );
/**
* Register a custom taxonomy for event types.
*/
function events_register_event_type_taxonomy() {
$labels = array(
'name' => 'Event Types',
'singular_name' => 'Event Type',
'search_items' => 'Search Event Types',
'all_items' => 'All Event Types',
'parent_item' => 'Parent Event Type',
'parent_item_colon' => 'Parent Event Type:',
'edit_item' => 'Edit Event Type',
'update_item' => 'Update Event Type',
'add_new_item' => 'Add New Event Type',
'new_item_name' => 'New Event Type Name',
'menu_name' => 'Event Types',
);
$args = array(
'hierarchical' => true,
'labels' => $labels,
'show_ui' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'event-type' ),
);
register_taxonomy( 'event_type', array( 'event' ), $args );
}
add_action( 'init', 'events_register_event_type_taxonomy' );
/**
* Register a custom taxonomy for event tags.
*/
function events_register_event_tag_taxonomy() {
$labels = array(
'name' => _x( 'Event Tags', 'taxonomy general name', 'aa-events' ),
'singular_name' => _x( 'Event Tag', 'taxonomy singular name', 'aa-events' ),
'search_items' => __( 'Search Event Tags', 'aa-events' ),
'all_items' => __( 'All Event Tags', 'aa-events' ),
'parent_item' => null,
'parent_item_colon' => null,
'edit_item' => __( 'Edit Event Tag', 'aa-events' ),
'update_item' => __( 'Update Event Tag', 'aa-events' ),
'add_new_item' => __( 'Add New Event Tag', 'aa-events' ),
'new_item_name' => __( 'New Event Tag Name', 'aa-events' ),
'separate_items_with_commas' => __( 'Separate event tags with commas', 'aa-events' ),
'add_or_remove_items' => __( 'Add or remove event tags', 'aa-events' ),
'choose_from_most_used' => __( 'Choose from the most used event tags', 'aa-events' ),
'not_found' => __( 'No event tags found.', 'aa-events' ),
'menu_name' => __( 'Event Tags', 'aa-events' ),
);
$args = array(
'hierarchical' => false,
'labels' => $labels,
'show_ui' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'event-tag' ),
);
register_taxonomy( 'event_tag', array( 'event' ), $args );
}
add_action( 'init', 'events_register_event_tag_taxonomy' );
/**
* Add custom columns to the event list table.
*
* @param array $columns The existing columns.
* @return array The modified columns.
*/
function events_add_admin_columns( $columns ) {
$new_columns = array();
foreach ( $columns as $key => $value ) {
$new_columns[ $key ] = $value;
if ( 'title' === $key ) {
$new_columns['event_datetime'] = __( 'Event Date & Time', 'aa-events' );
}
}
return $new_columns;
}
add_filter( 'manage_edit-event_columns', 'events_add_admin_columns' );
/**
* Display the content for the custom columns.
*
* @param string $column The name of the column.
* @param int $post_id The ID of the post.
*/
function events_display_admin_column_content( $column, $post_id ) {
if ( 'event_datetime' === $column ) {
$datetime = get_field( 'event_datetime', $post_id );
if ( $datetime ) {
echo esc_html( gmdate( 'F j, Y g:i a', strtotime( $datetime ) ) );
}
}
}
add_action( 'manage_event_posts_custom_column', 'events_display_admin_column_content', 10, 2 );
/**
* Make the custom column sortable.
*
* @param array $columns The existing sortable columns.
* @return array The modified sortable columns.
*/
function events_make_admin_column_sortable( $columns ) {
$columns['event_datetime'] = 'event_datetime';
return $columns;
}
add_filter( 'manage_edit-event_sortable_columns', 'events_make_admin_column_sortable' );
/**
* Handle sorting by the custom column.
*
* @param WP_Query $query The WordPress query object.
*/
function events_sort_by_custom_column( $query ) {
if ( ! is_admin() || ! $query->is_main_query() ) {
return;
}
if ( 'event_datetime' === $query->get( 'orderby' ) ) {
$query->set( 'meta_key', 'event_datetime' );
$query->set( 'orderby', 'meta_value' );
}
}
add_action( 'pre_get_posts', 'events_sort_by_custom_column' );
/**
* Set default sort order for event post type by event_datetime ascending.
* Applies to both admin and frontend queries.
*
* @param WP_Query $query The WordPress query object.
*/
function events_set_default_sort_order( $query ) {
// Only modify main queries for event post type
if ( ! $query->is_main_query() ) {
return;
}
// Admin list table
if ( is_admin() && $query->get( 'post_type' ) === 'event' ) {
if ( ! $query->get( 'orderby' ) ) {
$query->set( 'meta_key', 'event_datetime' );
$query->set( 'orderby', 'meta_value' );
$query->set( 'order', 'ASC' );
}
}
// Frontend: event archives and taxonomies
if ( ! is_admin() && $query->get( 'post_type' ) === 'event' ) {
// Main event archive: newest first (descending by date/time)
if ( $query->is_post_type_archive( 'event' ) ) {
if ( ! $query->get( 'orderby' ) ) {
$query->set( 'meta_key', 'event_datetime' );
$query->set( 'meta_type', 'DATETIME' );
$query->set( 'orderby', 'meta_value' );
$query->set( 'order', 'DESC' );
}
}
// Taxonomy views: upcoming first (ascending)
if ( ( $query->is_tax( 'event_type' ) || $query->is_tax( 'event_tag' ) ) && ! $query->get( 'orderby' ) ) {
$query->set( 'meta_key', 'event_datetime' );
$query->set( 'meta_type', 'DATETIME' );
$query->set( 'orderby', 'meta_value' );
$query->set( 'order', 'ASC' );
}
}
}
add_action( 'pre_get_posts', 'events_set_default_sort_order' );
+163
View File
@@ -0,0 +1,163 @@
<?php
/**
* Template Loader for AA Events.
*
* @package AA_Events
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Load a template.
*
* Handles template usage so that users can override the plugin's templates by using the theme.
*
* @param string $template_name Template name.
* @param array $args Arguments. (default: array).
* @param string $template_path Template path. (default: '').
* @param string $default_path Default path. (default: '').
*/
function events_get_template( $template_name, $args = array(), $template_path = '', $default_path = '' ) {
if ( ! empty( $args ) && is_array( $args ) ) {
extract( $args ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
}
$located = events_locate_template( $template_name, $template_path, $default_path );
if ( ! file_exists( $located ) ) {
_doing_it_wrong( __FUNCTION__, sprintf( '<code>%s</code> does not exist.', esc_html( $located ) ), '1.0' );
return;
}
// Allow 3rd party plugin filter template file from theme.
$located = apply_filters( 'events_get_template', $located, $template_name, $args, $template_path, $default_path );
do_action( 'events_before_template_part', $template_name, $template_path, $located, $args );
include $located;
do_action( 'events_after_template_part', $template_name, $template_path, $located, $args );
}
/**
* Locate a template and return the path for inclusion.
*
* This is the load order:
*
* yourtheme/$template_path/$template_name
* yourtheme/$template_name
* $default_path/$template_name
*
* @param string $template_name Template name.
* @param string $template_path Template path. (default: '').
* @param string $default_path Default path. (default: '').
* @return string
*/
function events_locate_template( $template_name, $template_path = '', $default_path = '' ) {
if ( ! $template_path ) {
$template_path = 'aa-events/';
}
if ( ! $default_path ) {
$default_path = AA_EVENTS_PLUGIN_DIR . 'templates/';
}
// Look within passed path within the theme - this is priority.
$template = locate_template(
array(
trailingslashit( $template_path ) . $template_name,
$template_name,
)
);
// Get default template/
if ( ! $template ) {
$template = $default_path . $template_name;
}
// Return what we found.
return apply_filters( 'events_locate_template', $template, $template_name, $template_path );
}
/**
* Filter the single template.
*
* @param string $template Template.
* @return string
*/
function events_single_template( $template ) {
if ( get_post_type() === 'event' ) {
$template = events_locate_template( 'single-event.php' );
}
return $template;
}
add_filter( 'single_template', 'events_single_template' );
/**
* Filter the archive template.
*
* @param string $template Template.
* @return string
*/
function events_archive_template( $template ) {
if ( is_post_type_archive( 'event' ) ) {
$template = events_locate_template( 'archive-event.php' );
}
return $template;
}
add_filter( 'archive_template', 'events_archive_template' );
/**
* Register plugin-provided page templates so they appear in the editor.
*
* WordPress only scans themes for page templates by default. This filter
* injects our plugin template into the list shown in the Page Attributes
* (or Template) dropdown, without requiring the file to live in the theme.
*
* @param array $page_templates Array of page templates.
* @param WP_Theme $theme WP_Theme object.
* @param WP_Post|null $post The post being edited (or null).
* @param string $post_type Post type slug.
* @return array
*/
function events_register_page_templates( $page_templates, $theme, $post, $post_type ) {
if ( 'page' !== $post_type ) {
return $page_templates;
}
// Key can be any unique string. We'll look for this exact value when loading.
$page_templates['aa-events-template-calendar.php'] = __( 'Events Calendar', 'aa-events' );
return $page_templates;
}
add_filter( 'theme_page_templates', 'events_register_page_templates', 10, 4 );
/**
* Load the selected plugin template on the front end.
*
* When a page uses our registered template slug, point WordPress to the
* template file bundled inside the plugin.
*
* @param string $template Resolved template path.
* @return string
*/
function events_load_plugin_page_template( $template ) {
if ( is_singular( 'page' ) ) {
$post_id = get_queried_object_id();
if ( $post_id ) {
$selected = get_post_meta( $post_id, '_wp_page_template', true );
if ( 'aa-events-template-calendar.php' === $selected ) {
// Allow theme override via aa-events/template-calendar.php, then fall back to plugin file.
$resolved = events_locate_template( 'template-calendar.php' );
if ( $resolved && file_exists( $resolved ) ) {
return $resolved;
}
}
}
}
return $template;
}
add_filter( 'template_include', 'events_load_plugin_page_template', 20 );
+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();