add_filter( 'the_content', $this, 'maybe_inject', 20 );
$loader->add_action( 'wp', $this, 'suppress_page_title' );
}
public function maybe_inject( string $content ): string {
// in_the_loop(): Avada's layout-builder header also runs the_content
// filters; without this guard the layout was injected there, above the
// site header, and consumed the once-only flag below.
if ( ! is_singular() || ! in_the_loop() ) {
return $content;
}
$page_id = get_queried_object_id();
if ( ! $page_id ) {
return $content;
}
$collab_id = (int) get_post_meta( $page_id, '_academiccrew_collaboration_id', true );
// Fallback: CPT URL accessed directly — the queried object IS the collaboration.
if ( ! $collab_id && is_singular( 'acc_collaboration' ) ) {
$collab_id = $page_id;
}
if ( ! $collab_id ) {
return $content;
}
static $injected = [];
if ( isset( $injected[ $page_id ] ) ) {
return $content;
}
$injected[ $page_id ] = true;
return $this->render_crumb( $collab_id )
. $this->render_hero( $collab_id )
. '
'
. '
'
. $this->render_lede( $collab_id )
. $content
. $this->render_partners( $collab_id )
. '
'
. '
'
. '
'
. $this->render_members( $collab_id );
}
// ---------------------------------------------------------------
// Title suppression (Avada + any theme that reads the_title in loop)
// ---------------------------------------------------------------
/**
* Fired on every frontend request. Sets pyre_page_title (Avada) and
* registers the_title filter (Gutenberg / any theme).
*
* Handles two cases:
* • Auto-generated WP page linked to a acc_collaboration post
* (identified by _academiccrew_collaboration_id meta on the page)
* • CPT direct URL /projects/{slug} (is_singular('acc_collaboration'))
*/
public function suppress_page_title(): void {
if ( ! is_singular() ) {
return;
}
$page_id = get_queried_object_id();
if ( ! $page_id ) {
return;
}
// Case 1: auto-generated WP page.
$collab_id = (int) get_post_meta( $page_id, '_academiccrew_collaboration_id', true );
if ( $collab_id ) {
update_post_meta( $page_id, 'pyre_page_title', 'no' );
add_filter( 'the_title', [ $this, 'blank_project_title' ], 10, 2 );
return;
}
// Case 2: CPT direct URL.
if ( is_singular( 'acc_collaboration' ) ) {
update_post_meta( $page_id, 'pyre_page_title', 'no' );
add_filter( 'the_title', [ $this, 'blank_project_title' ], 10, 2 );
}
}
/** Returns empty string for the queried object's title when rendered inside the loop. */
public function blank_project_title( string $title, int $id = 0 ): string {
return ( (int) $id === get_queried_object_id() && in_the_loop() ) ? '' : esc_html( $title );
}
// ---------------------------------------------------------------
// Shared utilities
// ---------------------------------------------------------------
private function meta( int $id, string $key ): string {
return (string) ( get_post_meta( $id, $key, true ) ?: '' );
}
private function year_range( string $start, string $end ): string {
$sy = $start ? (int) date_i18n( 'Y', strtotime( $start ) ) : 0;
$ey = $end ? (int) date_i18n( 'Y', strtotime( $end ) ) : 0;
if ( $sy && $ey ) {
return $sy === $ey ? (string) $sy : $sy . "\u{2013}" . $ey;
}
if ( $sy ) {
return $sy . "\u{2013}" . __( 'ongoing', 'academiccrew' );
}
return $ey ? (string) $ey : '';
}
private function project_status( string $end ): string {
return ( $end && strtotime( $end ) < time() ) ? 'completed' : 'ongoing';
}
// ---------------------------------------------------------------
// 1. Breadcrumb
// ---------------------------------------------------------------
private function render_crumb( int $collab_id ): string {
$archive_id = (int) get_option( 'academiccrew_collaborations_archive_page_id', 0 );
$archive_url = $archive_id ? get_permalink( $archive_id ) : null;
if ( ! $archive_url ) {
return '';
}
$title = (string) get_the_title( $collab_id );
ob_start(); ?>
post_title;
$ptype = $this->meta( $collab_id, '_acc_project_type' );
$start = $this->meta( $collab_id, '_acc_start_date' );
$end = $this->meta( $collab_id, '_acc_end_date' );
$status = $this->project_status( $end );
$yr = $this->year_range( $start, $end );
$thumb = get_the_post_thumbnail_url( $collab_id, 'medium' ) ?: '';
$kw_raw = get_post_meta( $collab_id, '_acc_keywords', true );
$keywords = is_array( $kw_raw ) ? $kw_raw : [];
$linked = (array) ( get_post_meta( $collab_id, '_acc_linked_members', true ) ?: [] );
$pi = CollaborationMeta::extract_pi_name( $linked );
$kicker_parts = array_filter( [
$ptype ? ucfirst( $ptype ) : '',
$start ? date_i18n( 'Y', strtotime( $start ) ) : '',
$pi ? 'PI ' . $pi : '',
] );
$status_label = $status === 'ongoing'
? __( 'Ongoing', 'academiccrew' )
: __( 'Completed', 'academiccrew' );
ob_start(); ?>
meta( $collab_id, '_acc_short_desc' );
$h3 = '' . esc_html__( 'Description', 'academiccrew' ) . '
';
if ( ! $lede ) {
return $h3;
}
return '' . esc_html( $lede ) . '
' . $h3;
}
// ---------------------------------------------------------------
// 4. Partners box
// ---------------------------------------------------------------
private function render_partners( int $collab_id ): string {
$raw = get_post_meta( $collab_id, '_acc_partners', true );
$partners = is_array( $raw ) ? array_filter( $raw ) : [];
if ( empty( $partners ) ) {
return '';
}
ob_start(); ?>