HappenBoard stores one post per event and expands recurrence into occurrences at query time. The hooks on this page sit on that pipeline: what an event description may render, what happens when a visitor submits an event, what is pushed to Google Calendar and Microsoft Graph, and what the Event JSON-LD contains.
The last three filters control the series URL, the secondary /events/{slug}/all-dates/ permalink that lists every date of a recurring event. Two of them run inside an init callback at priority 25, so register them early and flush the rewrite rules after any change.
happenboard_event_description_allowed_html
Changes the KSES allow-list applied to the Event Description block. The block runs the_content first, then passes the result through wp_kses() with this list, so any tag or attribute missing from it is stripped before output.
Signature
apply_filters( 'happenboard_event_description_allowed_html', array<string,array<string,bool>> $allowed )
Parameters
-
$allowedarray<string,array<string,bool>> - KSES definition, tag name to allowed attributes. Built from
wp_kses_allowed_html( 'post' )plus theiframe,video,audio,sourceandtrackentries the block adds back.
Return value
array<string,array<string,bool>>
The allow-list handed to wp_kses(). The return value is cast with (array).
Example
add_filter(
'happenboard_event_description_allowed_html',
function ( $allowed ) {
$allowed['details'] = array(
'open' => true,
'class' => true,
);
$allowed['summary'] = array( 'class' => true );
return $allowed;
}
);
Notes
- One argument only. A plain
add_filter()with the default accepted-args count is enough. - The block re-adds
iframe,video,audio,sourceandtrackon top ofwp_kses_allowed_html( 'post' )so oEmbed iframes and self-hosted media survive. Unsetting those keys breaks embeds inside event descriptions. - The filter runs after
the_content, so it cannot bring back markup another plugin already removed. It can only remove more. - The return value is cast with
(array). Returningnullorfalseproduces an empty allow-list:wp_kses()then strips every tag and only the plain text is rendered. - Scope is the Event Description block alone. Content rendered by core blocks or by the theme’s own
the_contentcall is untouched.
happenboard_event_submitted
Fires at the end of POST /happenboard/v1/pro/events/submit, once the visitor’s event has been inserted as a pending hboard_event and every meta field written. Use it to notify a channel, tag the submission, or push it to an external moderation queue.
Signature
do_action( 'happenboard_event_submitted', int $post_id, array<string,string> $data )
Parameters
-
$post_idint - ID of the newly created event. Its
post_statusis alwayspending. -
$dataarray<string,string> - Exactly three keys:
submitter_name,submitter_email,title.
Example
add_action(
'happenboard_event_submitted',
function ( $post_id, $data ) {
update_post_meta(
$post_id,
'_my_submitter_ref',
sanitize_email( $data['submitter_email'] )
);
wp_set_object_terms( $post_id, 'needs-review', 'happenboard_tag', true );
},
10,
2
);
Notes
- Two arguments. Register with
add_action( 'happenboard_event_submitted', $cb, 10, 2 )or$datanever reaches your callback. - Pro already listens:
Service\Submissions\SubmissionNotifier::on_submittedis hooked at priority 10 and emails the moderator. Your callback runs alongside it, not instead of it. $datacarries only the submitter name, the submitter e-mail and the title. Dates, venue, format and category are already saved as_happenboard_*post meta when the action fires, so read them from the post.- The endpoint self-gates on the
submissions_enabledsetting and on the full anti-abuse stack (REST nonce, per-IP and per-e-mail throttle, honeypot, signed form timestamp, optional Turnstile and Akismet). The action never fires for a rejected submission. - This is the only call site. Events created in wp-admin, imported through a migration adapter, or inserted via the REST CRUD routes do not trigger it.
happenboard_pro_gcal_event_payload
Last chance to change the Google Calendar Event resource before GooglePusher sends it to events.insert or events.patch. Add attendees, reminder overrides or extra fields, or rewrite anything HappenBoard mapped.
Signature
apply_filters( 'happenboard_pro_gcal_event_payload', array<string,mixed> $payload, WP_Post $event )
Parameters
-
$payloadarray<string,mixed> - Google Event resource. Always contains
summary,description,location,status,extendedProperties,startandend.recurrenceappears only when the event has an RRULE. -
$eventWP_Post - The source
hboard_eventpost. Meta is not attached to the object, read it withget_post_meta().
Return value
array<string,mixed>
The payload sent to Google. Cast with (array), so a non-array return becomes an empty payload and the API call fails.
Example
add_filter(
'happenboard_pro_gcal_event_payload',
function ( $payload, $event ) {
$payload['reminders'] = array(
'useDefault' => false,
'overrides' => array(
array(
'method' => 'popup',
'minutes' => 60,
),
),
);
$organizer_email = (string) get_post_meta( $event->ID, '_happenboard_organizer_email', true );
if ( is_email( $organizer_email ) ) {
$payload['attendees'] = array(
array(
'email' => $organizer_email,
'responseStatus' => 'accepted',
),
);
}
return $payload;
},
10,
2
);
Notes
- Two arguments. Use
add_filter( ..., 10, 2 )to receive$event. - Do not drop
extendedProperties.private.happenboard_origin.GooglePulleruses that marker to recognise events HappenBoard itself pushed; without it the next delta sync treats the event as foreign and can re-import it as a duplicate. - The same payload feeds both the insert and the patch path, and the patch path re-sends the whole resource. A key you remove is removed upstream too.
- Everything you add is sent verbatim. An invalid field or date block comes back as an API error, which the pusher stores in
last_errorand the event stays unsynced. - The source docblock suggests
conferenceDatafor Meet auto-creation, butGoogleClient::insert_event()andpatch_event()send noconferenceDataVersionquery parameter, which Google requires before it acts on a conference create request. Adding that block here has no effect today.
happenboard_pro_outlook_event_payload
Filters the Microsoft Graph Event resource before OutlookPusher sends it. Same role as happenboard_pro_gcal_event_payload on the Google side: inject attendees, wrap the body HTML, add an online meeting block.
Signature
apply_filters( 'happenboard_pro_outlook_event_payload', array<string,mixed> $payload, WP_Post $event )
Parameters
-
$payloadarray<string,mixed> - Graph Event resource. Always contains
subject,body,location,isAllDay,isCancelled,showAs,singleValueExtendedProperties,startandend.recurrenceonly when an RRULE maps cleanly. -
$eventWP_Post - The source
hboard_eventpost.
Return value
array<string,mixed>
The payload sent to Graph. Cast with (array).
Example
add_filter(
'happenboard_pro_outlook_event_payload',
function ( $payload, $event ) {
$payload['categories'] = array( 'HappenBoard' );
$payload['sensitivity'] = 'normal';
$payload['body']['content'] = '<p>' . esc_html( get_the_title( $event ) ) . '</p>'
. $payload['body']['content'];
return $payload;
},
10,
2
);
Notes
- Two arguments. Use
add_filter( ..., 10, 2 ). - Keep the
singleValueExtendedPropertiesentry whoseidends inhappenboard_origin.OutlookPullerscans that list for it to recognise its own pushes; drop it and the event can be re-imported on the next sync. - All-day events follow strict Graph rules the mapper already satisfies:
start.dateTimeat local midnight,end.dateTimeat local midnight of the following day, identicaltimeZoneon both. Rewritingstartorendby hand usually breaks that contract. body.contentis HTML and already ends with a “Synced from HappenBoard” footer plus the permalink, truncated to 16384 characters. Append after the footer or rebuild the string.isCancelledis mapped from the_happenboard_event_statusmeta and performs a soft archive. Hard deletions go throughOutlookClient::delete_event()and never reach this filter.
happenboard_schema_event
Last filter in the Event JSON-LD pipeline. It receives the fully built payload just before EventSchemaPrinter encodes it and prints the <script type="application/ld+json"> tag. Use it to add properties HappenBoard does not emit, or to overwrite what the builder produced.
Signature
apply_filters( 'happenboard_schema_event', array<string,mixed> $schema, int $event_id, \WP_Post $post )
Parameters
-
$schemaarray<string,mixed> - Complete payload. Always has
@context,@type,name,description,startDate,endDate,eventStatus,inLanguage,url,eventAttendanceMode,offersandisAccessibleForFree.image,location,organizer,performerandmaximumAttendeeCapacityappear only when the data exists. -
$event_idint - The event post ID.
-
$post\WP_Post - The same post, already fetched by the builder.
Return value
array<string,mixed>
The array that gets JSON-encoded. Returning null suppresses the whole JSON-LD block.
Example
add_filter(
'happenboard_schema_event',
function ( $schema, $event_id, $post ) {
$schema['typicalAgeRange'] = (string) get_post_meta( $event_id, '_my_age_range', true );
$schema['keywords'] = wp_get_post_terms(
$post->ID,
'happenboard_tag',
array( 'fields' => 'names' )
);
return $schema;
},
10,
3
);
Notes
- Three arguments. Use
add_filter( ..., 10, 3 )to receive$post. - It fires last.
happenboard_schema_event_type,happenboard_schema_offersand, when there are no offers,happenboard_schema_fallback_currencyhave already run, so$schemareflects their results. EventSchemaBuilder::build()is typed: ?array. Returning a string, an object or a number raises aTypeErrorand fatals the request.- It does not fire at all when the output is skipped upstream:
happenboard_schema_disablereturning true, or SEOPress already holding a manually configured Event schema for the post in the_seopress_pro_schemasmeta. In both cases the printer returns before the builder ever runs. - The builder also runs in wp-admin. Pro’s SEOPress integration calls it from
seopress_schemas_default_blocks_eventto pre-fill SEOPress’s schema editor, so your filter shapes what an editor sees there too. Note that this is a direct builder call, not a listener on this filter: nothing shipped hooks it, priority 10 is free.
happenboard_schema_event_type
Sets the @type of the Event JSON-LD. Return a schema.org Event subtype such as MusicEvent, BusinessEvent, Festival or SportsEvent so search engines classify the event more precisely than the generic Event.
Signature
apply_filters( 'happenboard_schema_event_type', string $type, int $event_id, \WP_Post $post )
Parameters
-
$typestring - Default
'Event'. -
$event_idint - The event post ID.
-
$post\WP_Post - The event post object.
Return value
string
The value written straight into $schema['@type']. No cast and no validation are applied.
Example
add_filter(
'happenboard_schema_event_type',
function ( $type, $event_id, $post ) {
if ( has_term( 'concerts', 'happenboard_category', $post ) ) {
return 'MusicEvent';
}
return $type;
},
10,
3
);
Notes
- Three arguments, none of them documented. The call passes
'Event',$event_idand$postbut carries no docblock, so tooling that reads docblocks reports zero parameters. Useadd_filter( ..., 10, 3 ). - First hook in the schema chain. It runs at the top of
EventSchemaBuilder::build(), before offers, andhappenboard_schema_eventcan still overwrite@typeafterwards. - The return value is not cast. Returning an array is emitted as a JSON array, which schema.org accepts for multi-typing, but an object or a non-serialisable value corrupts the JSON-LD.
- Only Event subtypes make sense here. Returning an unrelated type keeps the Event properties (
startDate,offers,eventAttendanceMode) in the payload and Google reports invalid structured data. - Like every builder filter, it is skipped when
happenboard_schema_disablereturns true or when SEOPress owns the Event schema for the post.
happenboard_series_rewrite_pattern
Replaces the regex behind the series rewrite rule, the /events/{slug}/all-dates/ URL that renders every date of a recurring event. Use it when the event permastruct is deeper than a single slug segment.
Signature
apply_filters( 'happenboard_series_rewrite_pattern', string $pattern )
Parameters
-
$patternstring - Default
'^' . preg_quote( $prefix, '/' ) . '/([^/]+)/all-dates/?$', where$prefixcomes fromhappenboard_series_rewrite_prefix.
Return value
string
The regex passed as the first argument of add_rewrite_rule(). It must capture the event slug in group 1, because the rewrite target is index.php?post_type=hboard_event&name=$matches[1]&happenboard_view=series.
Example
add_filter(
'happenboard_series_rewrite_pattern',
function ( $pattern ) {
// Event permalinks live under /agenda/{category}/{slug}/.
return '^agenda/[^/]+/([^/]+)/all-dates/?$';
}
);
Notes
- One argument, and the docblock contradicts the code: it describes the pattern as anchor-free, while the default it hands you starts with
^. Keep the anchor unless you know exactly what you are replacing it with. - Group 1 must be the event slug. The rewrite target hard-codes
name=$matches[1], so an extra capture group placed before it shifts the slug and the rule resolves to a 404. - The filter runs inside an
initcallback at priority 25. Register it earlier: from a plugin main file, or from aninithook at a priority below 25. - Rewrite rules are cached in the
rewrite_rulesoption. A new pattern has no effect until the rules are flushed, for example by re-saving Settings, Permalinks. - Changing the pattern does not change the URL
happenboard_series_urlhands out. Adjust both, or the advertised link stops resolving.
happenboard_series_rewrite_prefix
Sets the first path segment of the series rewrite rule. The default 'events' matches the stock hboard_event permastruct; change it when the post type rewrite slug has been customised.
Signature
apply_filters( 'happenboard_series_rewrite_prefix', string $prefix )
Parameters
-
$prefixstring - Default
'events'. It goes throughpreg_quote(), so it is treated as a literal path segment.
Return value
string
The literal slug prefix. It is only used to build the default pattern.
Example
add_filter(
'happenboard_series_rewrite_prefix',
function ( $prefix ) {
return 'agenda';
}
);
Notes
- One argument, and no docblock in the source, so the extracted signature listing zero parameters is wrong.
- It runs immediately before
happenboard_series_rewrite_patternand feeds it. A pattern callback that ignores the value it is given makes this filter irrelevant. - Same constraints as the pattern filter: register it before
initpriority 25, and flush the rewrite rules afterwards or nothing changes. - It moves the rewrite rule only. It does not change the post type’s own permalink structure, so set it to the
hboard_eventrewrite slug that is already in use, not to a new value.
happenboard_series_url
Resolves the series URL of an event, the /all-dates/ page listing every date of a recurring event. HappenBoard never applies this filter itself: it registers a callback on it and expects themes and plugins to call apply_filters( 'happenboard_series_url', '', $post ) when they need the link. Hook it to override what that built-in callback returns.
Signature
apply_filters( 'happenboard_series_url', string $url, WP_Post|int $post )
Parameters
-
$urlstring - Incoming value, returned unchanged on a mismatch. Callers normally pass an empty string.
-
$postWP_Post|int - The event to resolve. A post ID is accepted: the built-in callback runs
get_post()on it.
Return value
string
The series URL, built as trailingslashit( $permalink ) . 'all-dates/'. The built-in callback returns $url untouched when the post it was registered for is not the one being asked about.
Example
add_filter(
'happenboard_series_url',
function ( $url, $post ) {
$post = $post instanceof WP_Post ? $post : get_post( (int) $post );
if ( null === $post || 'hboard_event' !== $post->post_type ) {
return $url;
}
return trailingslashit( get_permalink( $post ) ) . 'toutes-les-dates/';
},
20,
2
);
Notes
- Two arguments. Register with
add_filter( ..., 10, 2 )at minimum, and use a priority above 10 to run after HappenBoard’s own callback. - The docblock in
SeriesRewrite::expose_series_link()showsapply_filters( 'happenboard_series_url', $post ), which contradicts the code: the registered callback takes( $url, $for_post )and is registered with 2 accepted args. Passing the post as the filtered value simply gives it back to you unchanged. - The built-in callback exists only as a side effect of
post_type_link. It is registered while WordPress builds a permalink, soget_permalink( $event ), or anything that calls it such as a rendered event list, must have run for that post earlier in the request. Otherwise the filter has no callback at all and returns whatever you passed in. expose_series_link()does not check the post type, so a closure is registered for every permalink WordPress builds, of any post type. Asking for the series URL of a page returns that page’s URL withall-dates/appended, even though no rewrite rule matches it. Checkget_post_type()yourself before trusting the result.- The URL it returns assumes the default rewrite pattern. If you changed
happenboard_series_rewrite_prefixorhappenboard_series_rewrite_pattern, this filter is where you correct the advertised link.