add_filter( 'the_content', $this, 'maybe_inject', 11 ); $loader->add_action( 'wp_enqueue_scripts', $this, 'enqueue_assets' ); $loader->add_filter( 'body_class', $this, 'add_body_class' ); } public function add_body_class( array $classes ): array { if ( is_singular( 'page' ) && (int) get_post_meta( get_queried_object_id(), '_academiccrew_member_id', true ) ) { $classes[] = 'acc-member-profile-page'; } return $classes; } public function enqueue_assets(): void { if ( ! is_singular( 'page' ) ) { return; } $member_id = (int) get_post_meta( get_queried_object_id(), '_academiccrew_member_id', true ); if ( ! $member_id ) { return; } wp_enqueue_style( 'academiccrew-member-profile', ACADEMICCREW_URL . 'assets/css/member-profile.css', [ 'fusion-stylesheet', 'tabler-icons' ], ACADEMICCREW_VERSION ); wp_enqueue_script( 'academiccrew-member-profile', ACADEMICCREW_URL . 'assets/js/member-profile.js', [], ACADEMICCREW_VERSION, true ); } public function maybe_inject( string $content ): string { if ( ! is_singular( 'page' ) || ! in_the_loop() ) { return $content; } $page_id = get_the_ID(); $member_id = (int) get_post_meta( $page_id, '_academiccrew_member_id', true ); if ( ! $member_id ) { return $content; } static $injected = []; if ( isset( $injected[ $page_id ] ) ) { return $content; } $injected[ $page_id ] = true; return $this->render_hero( $member_id ) . $this->render_publications( $member_id ) . $this->render_collaborations( $member_id ) . $this->render_service_and_awards( $member_id ) . $content; } // --------------------------------------------------------------- // Zone 1: Hero // --------------------------------------------------------------- private function render_hero( int $member_id ): string { $get = fn( string $key ) => get_post_meta( $member_id, $key, true ); $name = get_the_title( $member_id ); $role = $get( '_acc_member_role' ) ?: 'collaborator'; // The group's own default department (Settings) is implied — not worth a line. $department = (string) ( $get( '_acc_department' ) ?: '' ); $org_name = (string) ( get_option( 'academiccrew_settings', [] )['org_name'] ?? '' ); if ( $org_name !== '' && 0 === strcasecmp( trim( $department ), $org_name ) ) { $department = ''; } $bio = $get( '_acc_bio' ) ?: ''; $email = $get( '_acc_email' ) ?: ''; $avatar_url = $get( '_acc_avatar_url' ) ?: ( get_the_post_thumbnail_url( $member_id, [ 96, 96 ] ) ?: '' ); $orcid_id = $get( '_acc_orcid_id' ) ?: ''; $scopus_id = $get( '_acc_scopus_id' ) ?: ''; $scholar_id = $get( '_acc_scholar_id' ) ?: ''; $iris_url = $get( '_acc_iris_profile_url' ) ?: ''; if ( ! $iris_url ) { $iris_id = $get( '_acc_iris_person_id' ); $iris_base = untrailingslashit( (string) ( get_option( 'academiccrew_settings', [] )['iris_base_url'] ?? '' ) ); $iris_url = ( $iris_id && $iris_base ) ? "{$iris_base}/cris/rp/{$iris_id}" : ''; } $personal_web = $get( '_acc_personal_website' ) ?: ''; $linkedin = $get( '_acc_linkedin_url' ) ?: ''; $github = $get( '_acc_github_url' ) ?: ''; $twitter = $get( '_acc_twitter_url' ) ?: ''; $phone = $get( '_acc_phone' ) ?: ''; $office = $get( '_acc_office' ) ?: ''; $areas_raw = $get( '_acc_research_areas' ); $areas = is_array( $areas_raw ) ? $areas_raw : array_filter( array_map( 'trim', explode( ',', $areas_raw ?: '' ) ) ); $pub_count = (int) ( $get( '_acc_pub_count' ) ?: 0 ); $citations = (int) ( $get( '_acc_citation_count' ) ?: 0 ); $h_index = (int) ( $get( '_acc_h_index' ) ?: 0 ); $cit_url = MemberStats::citation_url( $member_id ); $stat_tag = $cit_url ? 'a' : 'span'; $stat_attrs = $cit_url ? sprintf( ' href="%s" target="_blank" rel="noopener noreferrer" title="%s"', esc_url( $cit_url ), esc_attr__( 'Source: Google Scholar', 'academiccrew' ) ) : ''; $course_count = $this->count_member_courses( $member_id ); $colors = RoleRegistry::colors()[ $role ] ?? [ 'bg' => '#f0f0f0', 'text' => '#333' ]; $portrait_class = 'acc-portrait' . ( $avatar_url ? '' : ' is-initials' ); $archive_id = (int) get_option( 'academiccrew_members_archive_page_id', 0 ); $archive_url = $archive_id ? get_permalink( $archive_id ) : null; ob_start(); ?>
<?php echo esc_attr( $name ); ?> initials( $name ) ); ?>

">
$linkedin, 'github' => $github, 'twitter' => $twitter ] ); if ( $socials ) : ?>
$url ) : // Tabler's "brand-twitter" is the retired bird logo; "brand-x" is the current one. $icon = 'twitter' === $net ? 'x' : $net; $label = 'twitter' === $net ? 'X' : ucfirst( $net ); ?>
< class="stat"> > < class="stat"> h-index >
$member_id, 'restUrl' => rest_url( "academiccrew/v1/members/{$member_id}/publications" ), 'filtersUrl' => rest_url( "academiccrew/v1/members/{$member_id}/publications/filters" ), 'nonce' => wp_create_nonce( 'wp_rest' ), 'perPage' => 10, 'i18n' => [ 'publications' => __( 'Publications', 'academiccrew' ), 'inPress' => __( 'In press', 'academiccrew' ), 'searchPlaceholder' => __( 'Search title or venue...', 'academiccrew' ), 'all' => __( 'All', 'academiccrew' ), 'noResults' => __( 'No publications match these filters.', 'academiccrew' ), 'loading' => __( 'Loading...', 'academiccrew' ), 'sortYearDesc' => __( 'Year ↓', 'academiccrew' ), 'sortYearAsc' => __( 'Year ↑', 'academiccrew' ), 'sortCitations' => __( 'Citations ↓', 'academiccrew' ), 'sortCitationsAsc' => __( 'Citations ↑', 'academiccrew' ), 'sortTitle' => __( 'Title A–Z', 'academiccrew' ), 'sortTitleDesc' => __( 'Title Z–A', 'academiccrew' ), 'showing' => __( 'Showing', 'academiccrew' ), 'of' => __( 'of', 'academiccrew' ), 'doi' => 'DOI', 'pdf' => 'PDF', 'etAl' => 'et al.', 'copyBibtex' => __( 'Copy BibTeX', 'academiccrew' ), 'bibtexCopied' => __( 'BibTeX copied!', 'academiccrew' ), 'corresponding' => __( 'Corresponding author', 'academiccrew' ), 'featuredOnly' => __( 'Featured only', 'academiccrew' ), 'allTags' => __( 'All tags', 'academiccrew' ), 'typeLabels' => [ 'journal-article' => __( 'Journal', 'academiccrew' ), 'conference-paper' => __( 'Conference', 'academiccrew' ), 'book-chapter' => __( 'Book chapter', 'academiccrew' ), 'preprint' => __( 'Preprint', 'academiccrew' ), 'book' => __( 'Book', 'academiccrew' ), 'thesis' => __( 'Thesis', 'academiccrew' ), 'report' => __( 'Report', 'academiccrew' ), 'other' => __( 'Other', 'academiccrew' ), ], ], ] ); // The member's own picks, above the full list. $selected_html = ( new \AcademicCrew\Blocks\FeaturedPublicationsBlock() )->render( [ 'count' => 10, 'member' => $member_id ] ); ob_start(); ?>

'acc_collaboration', 'post_status' => 'publish', 'posts_per_page' => -1, 'orderby' => 'title', 'order' => 'ASC', 'meta_query' => [ 'relation' => 'OR', [ // new format: [{member_id: X, role: Y}, ...] 'key' => '_acc_linked_members', 'value' => '"member_id";i:' . $member_id . ';', 'compare' => 'LIKE', ], [ // old format: [X, Y, Z] (flat int array, backward compat) 'key' => '_acc_linked_members', 'value' => ':' . $member_id . ';', 'compare' => 'LIKE', ], ], ] ); if ( empty( $projects ) ) { return ''; } ob_start(); ?>

get_post_meta( $project->ID, $k, true ); $start = (string) ( $get( '_acc_start_date' ) ?: '' ); $end = (string) ( $get( '_acc_end_date' ) ?: '' ); $ptype = (string) ( $get( '_acc_project_type' ) ?: '' ); $thumbnail = get_the_post_thumbnail_url( $project->ID, 'medium' ) ?: ''; $kw_raw = $get( '_acc_keywords' ); $keywords = is_array( $kw_raw ) ? $kw_raw : []; $short_desc = (string) ( $get( '_acc_short_desc' ) ?: '' ); $linked_page = (int) get_post_meta( $project->ID, '_academiccrew_linked_page_id', true ); $url = $linked_page ? get_permalink( $linked_page ) : get_permalink( $project->ID ); $dates = ''; if ( $start && $end ) { $dates = date_i18n( 'Y', strtotime( $start ) ) . '–' . date_i18n( 'Y', strtotime( $end ) ); } elseif ( $start ) { $dates = date_i18n( 'Y', strtotime( $start ) ) . '–' . esc_html__( 'ongoing', 'academiccrew' ); } $status = ( $end && strtotime( $end ) < time() ) ? 'completed' : 'ongoing'; ?>
<?php echo esc_attr( $project->post_title ); ?>

post_title ); ?>

__( 'Chair', 'academiccrew' ), 'area-chair' => __( 'Area Chair', 'academiccrew' ), 'senior-pc' => __( 'Senior PC', 'academiccrew' ), 'tpc' => __( 'TPC', 'academiccrew' ), 'organiser' => __( 'Organiser', 'academiccrew' ), 'editor' => __( 'Editor', 'academiccrew' ), // Backwards compat with the old schema keys: 'tpc-member' => __( 'TPC', 'academiccrew' ), 'editorial-board' => __( 'Editor', 'academiccrew' ), 'reviewer' => __( 'Reviewer', 'academiccrew' ), ]; $has_roles = ! empty( $roles ); $has_awards = ! empty( $awards ); $grid_class = ( $has_roles && $has_awards ) ? 'acc-member-extra-grid' : 'acc-member-extra-single'; ob_start(); ?>

    = 2 ) { $year_str = $year_raw[0] !== $year_raw[1] ? $year_raw[0] . '–' . $year_raw[1] : (string) $year_raw[0]; } elseif ( is_numeric( $year_raw ) ) { $year_str = (string) $year_raw; } else { $yf = $entry['year_from'] ?? ''; $yt = $entry['year_to'] ?? ''; $year_str = ( $yf && $yt && $yt !== $yf ) ? $yf . '–' . $yt : (string) $yf; } ?>
  • ·

    __( 'Best Paper Award', 'academiccrew' ), 'best-demo' => __( 'Best Demo Paper', 'academiccrew' ), 'best-student-paper' => __( 'Best Student Paper', 'academiccrew' ), 'best-poster' => __( 'Best Poster Award', 'academiccrew' ), 'outstanding-paper' => __( 'Outstanding Paper Award', 'academiccrew' ), 'distinguished-paper' => __( 'Distinguished Paper Award', 'academiccrew' ), 'test-of-time' => __( 'Test of Time Award', 'academiccrew' ), 'best-dissertation' => __( 'Best PhD Dissertation', 'academiccrew' ), 'fellowship' => __( 'Fellowship', 'academiccrew' ), 'honorary-degree' => __( 'Honorary Degree', 'academiccrew' ), 'research-prize' => __( 'Research Prize', 'academiccrew' ), 'teaching-award' => __( 'Teaching Award', 'academiccrew' ), 'service-award' => __( 'Service Award', 'academiccrew' ), ]; foreach ( $awards as $entry ) : // title: new schema stores directly; old schema derives from type key. $title = $entry['title'] ?? ( $aw_type_labels[ $entry['type'] ?? '' ] ?? ( $entry['custom_title'] ?? '' ) ); $paper_title = $entry['paperTitle'] ?? $entry['paper_title'] ?? ''; $venue = $entry['venue'] ?? $entry['organization'] ?? ''; $year = $entry['year'] ?? ''; $url = $entry['url'] ?? ''; $pub_id = (int) ( $entry['publication_id'] ?? 0 ); // coAuthors: new schema is string[]; old was a plain string. $co_raw = $entry['coAuthors'] ?? $entry['coauthors'] ?? []; $co_str = is_array( $co_raw ) ? implode( ', ', $co_raw ) : (string) $co_raw; if ( $pub_id && ! $url ) { $doi = $wpdb->get_var( $wpdb->prepare( "SELECT doi FROM `{$wpdb->prefix}academiccrew_publications` WHERE id = %d", $pub_id ) ); if ( $doi ) { $url = 'https://doi.org/' . $doi; } } ?>
post_content ) ) { return ''; } return wpautop( do_shortcode( do_blocks( $post->post_content ) ) ); } private function count_member_courses( int $member_id ): int { $courses = get_posts( [ 'post_type' => 'academiccrew_course', 'post_status' => 'publish', 'posts_per_page' => -1, 'fields' => 'ids', 'meta_query' => [ [ 'key' => '_acc_instructors', 'value' => '"member_id";i:' . $member_id, 'compare' => 'LIKE', ] ], ] ); return count( $courses ); } private function format_phone( string $raw ): string { $digits = preg_replace( '/\D/', '', $raw ); if ( strlen( $digits ) === 10 && str_starts_with( $digits, '011' ) ) { return '+39 ' . substr( $digits, 0, 3 ) . ' ' . substr( $digits, 3, 3 ) . ' ' . substr( $digits, 6 ); } return $raw; } private function initials( string $name ): string { $parts = array_values( array_filter( preg_split( '/\s+/', trim( $name ) ) ) ); return strtoupper( implode( '', array_map( fn( $w ) => mb_substr( $w, 0, 1 ), array_slice( $parts, 0, 2 ) ) ) ); } }