Developer Guide
Developer Guide#
Extend and customize the plugin using hooks, filters, templates, and the API. This guide covers common customization scenarios.
Architecture overview#
File structure#
evenzo-events-manager/
├── assets/
│ ├── css/ # Stylesheets
│ └── js/ # JavaScript files
├── includes/
│ ├── admin/ # Admin screens and settings
│ ├── api/ # REST API controllers and webhooks
│ ├── blocks/ # Gutenberg blocks
│ ├── core/ # Event, attendee, post types, QR code, recurrence
│ ├── emails/ # WooCommerce email classes
│ ├── export/ # CSV and iCal export
│ ├── frontend/ # Templates, shortcodes, calendar, widgets
│ ├── import/ # CSV and iCal import
│ ├── pdf/ # Printable tickets
│ ├── seating/ # Seating charts
│ ├── speakers/ # Speaker post type
│ ├── waitlist/ # Waitlist and pricing rules
│ └── woocommerce/ # Product and order integration
├── languages/ # Translation files
└── templates/ # Template files
Class naming#
All classes use the EMWC_ prefix. The main plugin class is EMWC (use class_exists( 'EMWC' ) for dependency checks).
EMWC_Event- Event model (get_start_date(),get_venue_id(),is_upcoming(),save())EMWC_Attendee- Attendee model (check_in(),check_out(),get_by_ticket_code(),get_for_event())EMWC_Logger- Logging through the WooCommerce logger
Database tables#
Custom tables created on activation:
| Table | Purpose |
|---|---|
wp_emwc_attendees | Attendee and ticket records |
wp_emwc_checkins | Check-in and check-out history |
wp_emwc_waitlist | Waitlist entries |
wp_emwc_pricing_rules | Created but not used by the current pricing code (rules are stored in product meta) |
Hooks reference#
Action hooks#
Tickets and attendees
// Attendee record created while generating tickets for an order
do_action( 'emwc_attendee_created', $attendee_id, $attendee_data, $event_id, $order_id );
// All tickets for an order generated
do_action( 'emwc_tickets_generated', $order_id, $order );
// All tickets for an order cancelled (order cancelled or refunded)
do_action( 'emwc_tickets_cancelled', $order_id );
// Single ticket cancelled or reactivated from the Attendees screen
do_action( 'emwc_ticket_cancelled', $attendee_id, $attendee );
do_action( 'emwc_ticket_reactivated', $attendee_id, $attendee );
// Ticket email resent from the Attendees screen
do_action( 'emwc_ticket_email_resent', $attendee_id, $order_id );
// Attendee deleted
do_action( 'emwc_attendee_deleted', $attendee_id );
$attendee is an EMWC_Attendee object. $attendee_data is an array with first_name, last_name, email, and ticket_code.
Check-in
// Fired from the admin check-in screen (second argument is an EMWC_Attendee)
// and from the REST check-in endpoint (second argument is the attendee row as an array)
do_action( 'emwc_attendee_checked_in', $attendee_id, $attendee );
do_action( 'emwc_attendee_checked_out', $attendee_id, $attendee );
Waitlist
// Someone joined the waitlist
do_action( 'emwc_waitlist_joined', $entry_id, $product_id, $email );
// Notification email sent to a waitlist entry
do_action( 'emwc_waitlist_notification_sent', $entry, $product );
REST API
do_action( 'emwc_rest_event_created', $post_id, $request );
do_action( 'emwc_rest_event_updated', $post_id, $request );
do_action( 'emwc_rest_event_deleted', $post_id, $force );
do_action( 'emwc_rest_venue_created', $post_id, $request );
do_action( 'emwc_rest_venue_updated', $post_id, $request );
do_action( 'emwc_rest_venue_deleted', $post_id, $force );
do_action( 'emwc_rest_organizer_created', $post_id, $request );
do_action( 'emwc_rest_organizer_updated', $post_id, $request );
do_action( 'emwc_rest_organizer_deleted', $post_id, $force );
do_action( 'emwc_rest_attendee_updated', $attendee_id, $request );
Templates
// Inside the single event template, after the event details block
do_action( 'emwc_single_event_after_details', $event_id );
Filter hooks#
REST responses
add_filter( 'emwc_rest_prepare_event', function( $data, $post, $request ) {
$data['custom_field'] = get_post_meta( $post->ID, 'custom_field', true );
return $data;
}, 10, 3 );
Also available: emwc_rest_prepare_venue and emwc_rest_prepare_organizer ($data, $post, $request), and emwc_rest_prepare_attendee ($data, $attendee array, $request).
Structured data
// Schema.org Event data printed on single event pages
add_filter( 'emwc_event_schema', function( $schema, $event_id ) {
$schema['eventAttendanceMode'] = 'https://schema.org/MixedEventAttendanceMode';
return $schema;
}, 10, 2 );
Tickets
// Accent color on the printable ticket (default #2563eb)
add_filter( 'emwc_ticket_primary_color', function( $color ) {
return '#111111';
} );
Template overrides#
Page templates#
Copy a template from:
plugins/evenzo-events-manager/templates/
To either of these locations in your theme:
your-theme/single-emwc_event.php
your-theme/evenzo-events-manager/single-emwc_event.php
| Template | Purpose |
|---|---|
single-emwc_event.php | Single event page |
archive-emwc_event.php | Event archive and category pages |
single-emwc_speaker.php | Single speaker page |
archive-emwc_speaker.php | Speaker archive |
Email templates#
Emails are WooCommerce emails and follow the WooCommerce override path:
your-theme/woocommerce/emails/registration-confirmation.php
your-theme/woocommerce/emails/plain/registration-confirmation.php
Available files: admin-new-registration.php, event-cancellation.php, event-reminder.php, event-update.php, registration-confirmation.php, ticket-delivery.php, each with a plain/ version.
Custom post type integration#
Post types: emwc_event, emwc_venue, emwc_organizer, emwc_speaker. Taxonomy: emwc_event_category.
Event meta keys#
| Key | Description |
|---|---|
_emwc_start_date | Start date (Y-m-d) |
_emwc_end_date | End date |
_emwc_start_time | Start time (H:i) |
_emwc_end_time | End time |
_emwc_all_day | All day event (yes/no) |
_emwc_timezone | Event timezone |
_emwc_event_status | scheduled, postponed, cancelled, sold-out |
_emwc_event_type | in-person, virtual, hybrid |
_emwc_venue_id | Venue post ID |
_emwc_organizer_id | Organizer post ID |
_emwc_capacity | Maximum attendees |
_emwc_registration_deadline | Registration deadline |
_emwc_featured | Featured event (yes/no) |
_emwc_virtual_url | Virtual event URL |
_emwc_external_url | External URL (meta only, no admin field) |
The REST API and calendar endpoints read and write venue and organizer under _emwc_venue and _emwc_organizer instead. Events created in the admin report venue_id: 0 through the API until the two key names match.
Querying events#
$events = new WP_Query( array(
'post_type' => 'emwc_event',
'meta_key' => '_emwc_start_date',
'orderby' => 'meta_value',
'order' => 'ASC',
'meta_query' => array(
array(
'key' => '_emwc_start_date',
'value' => current_time( 'Y-m-d' ),
'compare' => '>=',
'type' => 'DATE',
),
),
) );
Database operations#
Attendee table#
Prefer the model over raw SQL:
$attendees = EMWC_Attendee::get_for_event( $event_id, array( 'status' => 'active' ) );
$attendee = EMWC_Attendee::get_by_ticket_code( 'EMWC-ABC123DEF' );
$attendee->check_in( get_current_user_id(), 'front desk', 'VIP' );
Attendee statuses: active, checked_in, checked_out, cancelled.
Check-in table#
Columns: checkin_id, attendee_id, event_id, check_type (in or out), checked_by, checked_at, device_info, notes. Rows are written by EMWC_Attendee::check_in() and check_out().
Creating extensions#
Plugin header#
<?php
/**
* Plugin Name: EMWC Custom Extension
* Description: Custom features for Evenzo Events Manager
* Version: 1.0.0
* Requires Plugins: woocommerce
*/
Dependency check#
add_action( 'plugins_loaded', function() {
if ( ! class_exists( 'EMWC' ) ) {
add_action( 'admin_notices', function() {
echo '<div class="error"><p>This extension requires Evenzo Events Manager.</p></div>';
} );
return;
}
// Initialize extension
}, 20 );
Adding admin pages#
The top-level menu slug is emwc-dashboard and uses the manage_woocommerce capability.
add_action( 'admin_menu', function() {
add_submenu_page(
'emwc-dashboard',
'Custom Page',
'Custom Page',
'manage_woocommerce',
'emwc-custom',
'render_custom_page'
);
}, 20 );
Debugging#
Enable Debug Mode under Events > Settings > Advanced (option emwc_enable_debug_mode). Debug, info, notice, and warning messages are then written to the WooCommerce log with source emwc. Error level messages and above are always written.
EMWC_Logger::debug( 'Custom message', 'my-context', array( 'key' => 'value' ) );
EMWC_Logger::error( 'Something failed', 'my-context' );
View logs under WooCommerce > Status > Logs.
Recommended approach#
Performance#
- Cache expensive queries
- Use transients for repeated data
- Load assets only where needed
Security#
- Sanitize all input
- Escape all output
- Verify nonces
- Check capabilities
Compatibility#
- Test with the latest WordPress and WooCommerce
- Use hooks instead of modifying plugin files