Event Details Meta Box
Adds an "Event details" box (start, end, venue, address, price, attendance mode and ticket link) to events, saved securely, plus an [event_details] shortcode to display them.
Opens WPIDE on
Code
// Post type, and the fields: meta key (saved with a leading underscore) => label and input type.
$wpide_box = array(
'post_type' => 'event',
'title' => 'Event details',
'fields' => array(
'event_start' => array( 'label' => 'Starts', 'type' => 'datetime-local' ),
'event_end' => array( 'label' => 'Ends', 'type' => 'datetime-local' ),
'event_venue' => array( 'label' => 'Venue name', 'type' => 'text' ),
'event_address' => array( 'label' => 'Address', 'type' => 'text' ),
'event_price' => array( 'label' => 'Price (0 = free)', 'type' => 'number' ),
'event_mode' => array( 'label' => 'Attendance', 'type' => 'select', 'options' => array( 'offline' => 'In person', 'online' => 'Online', 'mixed' => 'In person and online' ) ),
'event_tickets' => array( 'label' => 'Tickets URL', 'type' => 'url' ),
),
);
add_action( 'add_meta_boxes_' . $wpide_box['post_type'], function () use ( $wpide_box ) {
add_meta_box( 'wpide-event-details', $wpide_box['title'], function ( $post ) use ( $wpide_box ) {
wp_nonce_field( 'wpide_event_details', 'wpide_event_details_nonce' );
echo '<div style="display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(220px,1fr))">';
foreach ( $wpide_box['fields'] as $key => $field ) {
$value = get_post_meta( $post->ID, '_' . $key, true );
$id = 'wpide-' . str_replace( '_', '-', $key );
echo '<p style="margin:0">';
if ( 'checkbox' === $field['type'] ) {
printf( '<label><input type="checkbox" name="%1$s" value="1"%2$s> %3$s</label>', esc_attr( $key ), checked( $value, '1', false ), esc_html( $field['label'] ) );
} else {
printf( '<label for="%1$s"><strong>%2$s</strong></label><br>', esc_attr( $id ), esc_html( $field['label'] ) );
if ( 'textarea' === $field['type'] ) {
printf( '<textarea class="widefat" rows="5" id="%1$s" name="%2$s">%3$s</textarea>', esc_attr( $id ), esc_attr( $key ), esc_textarea( $value ) );
} elseif ( 'select' === $field['type'] ) {
printf( '<select class="widefat" id="%1$s" name="%2$s">', esc_attr( $id ), esc_attr( $key ) );
foreach ( $field['options'] as $option => $label ) {
printf( '<option value="%1$s"%2$s>%3$s</option>', esc_attr( $option ), selected( (string) $value, (string) $option, false ), esc_html( $label ) );
}
echo '</select>';
} else {
printf( '<input class="widefat" type="%1$s" id="%2$s" name="%3$s" value="%4$s"%5$s>', esc_attr( $field['type'] ), esc_attr( $id ), esc_attr( $key ), esc_attr( $value ), 'number' === $field['type'] ? ' step="any" min="0"' : '' );
}
}
echo '</p>';
}
echo '</div>';
}, $wpide_box['post_type'], 'normal', 'high' );
} );
add_action( 'save_post_' . $wpide_box['post_type'], function ( $post_id ) use ( $wpide_box ) {
if ( ! isset( $_POST['wpide_event_details_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['wpide_event_details_nonce'] ), 'wpide_event_details' ) ) {
return;
}
if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
foreach ( $wpide_box['fields'] as $key => $field ) {
$raw = isset( $_POST[ $key ] ) ? wp_unslash( $_POST[ $key ] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- sanitized below per type.
$raw = is_string( $raw ) ? $raw : '';
switch ( $field['type'] ) {
case 'url':
$value = esc_url_raw( $raw );
break;
case 'email':
$value = sanitize_email( $raw );
break;
case 'number':
$value = is_numeric( $raw ) ? (string) ( 0 + $raw ) : '';
break;
case 'textarea':
$value = sanitize_textarea_field( $raw );
break;
case 'checkbox':
$value = $raw ? '1' : '';
break;
case 'select':
$value = array_key_exists( $raw, $field['options'] ) ? $raw : '';
break;
case 'date':
$value = preg_match( '/^\d{4}-\d{2}-\d{2}$/', $raw ) ? $raw : '';
break;
case 'datetime-local':
$value = preg_match( '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/', $raw ) ? $raw : '';
break;
default:
$value = sanitize_text_field( $raw );
}
if ( '' === $value ) {
delete_post_meta( $post_id, '_' . $key );
} else {
update_post_meta( $post_id, '_' . $key, $value );
}
}
} );
add_shortcode( 'event_details', function () {
$id = get_the_ID();
if ( ! $id || 'event' !== get_post_type( $id ) ) {
return '';
}
$get = function ( $key ) use ( $id ) { return (string) get_post_meta( $id, '_' . $key, true ); };
// Stored as local date-time (site timezone).
$to_time = function ( $value ) {
$date = $value ? date_create_immutable( $value, wp_timezone() ) : false;
return $date ? $date->getTimestamp() : 0;
};
$start = $to_time( $get( 'event_start' ) );
$rows = array();
if ( $start ) {
$fmt = get_option( 'date_format' ) . ' ' . get_option( 'time_format' );
$when = wp_date( $fmt, $start );
$end = $to_time( $get( 'event_end' ) );
if ( $end > $start ) {
$when .= ' – ' . wp_date( wp_date( 'Y-m-d', $end ) === wp_date( 'Y-m-d', $start ) ? get_option( 'time_format' ) : $fmt, $end );
}
$rows['When'] = esc_html( $when );
}
if ( $get( 'event_venue' ) || $get( 'event_address' ) ) {
$rows['Where'] = esc_html( trim( $get( 'event_venue' ) . ', ' . $get( 'event_address' ), ', ' ) );
}
if ( '' !== $get( 'event_price' ) ) {
$rows['Price'] = 0.0 === (float) $get( 'event_price' ) ? 'Free' : esc_html( $get( 'event_price' ) );
}
if ( $get( 'event_tickets' ) ) {
$rows['Tickets'] = '<a href="' . esc_url( $get( 'event_tickets' ) ) . '">Get tickets</a>';
}
$html = '';
foreach ( $rows as $label => $value ) {
$html .= '<dt>' . esc_html( $label ) . '</dt><dd>' . $value . '</dd>';
}
return $html ? '<dl class="event-details">' . $html . '</dl>' : '';
} );
Notes
Made for the Events post type snippet; change post_type to use it with another. The Event schema snippet reads these fields.
How to add this snippet
- Install WPIDE on your WordPress site (it is free) and open Code Snippets.
- Use snippet above adds it from the library in one click. Or copy the code into a new snippet with the same code type.
- It is added inactive: review the code and settings, then switch it on.