add_filter( 'the_content', $this, 'maybe_inject', 11 ); $loader->add_action( 'wp_enqueue_scripts', $this, 'enqueue_assets' ); } public function enqueue_assets(): void { if ( ! is_singular( 'page' ) ) { return; } $course_id = (int) get_post_meta( get_queried_object_id(), '_academiccrew_course_id', true ); if ( ! $course_id ) { return; } wp_enqueue_style( 'academiccrew-member-profile', ACADEMICCREW_URL . 'assets/css/member-profile.css', [ 'fusion-stylesheet', 'tabler-icons' ], ACADEMICCREW_VERSION ); wp_enqueue_style( 'academiccrew-course-profile', ACADEMICCREW_URL . 'assets/css/course-profile.css', [ 'academiccrew-member-profile', 'fusion-stylesheet', 'tabler-icons' ], ACADEMICCREW_VERSION ); } public function maybe_inject( string $content ): string { if ( ! is_singular( 'page' ) || ! in_the_loop() ) { return $content; } $page_id = get_the_ID(); $course_id = (int) get_post_meta( $page_id, '_academiccrew_course_id', true ); if ( ! $course_id ) { return $content; } static $injected = []; if ( isset( $injected[ $page_id ] ) ) { return $content; } $injected[ $page_id ] = true; // A course is shown in a single language — its own. No IT/EN toggle. $this->lang = strtolower( (string) get_post_meta( $course_id, '_acc_language', true ) ) === 'en' ? 'en' : 'it'; return '
' . $this->render_header( $course_id ) . $this->render_extended_sections( $course_id ) . '
' . $content; } // --------------------------------------------------------------- // Bilingual helpers // --------------------------------------------------------------- /** * Returns the (translatable) English string. The $it parameter is kept * for call-site compatibility but unused — course header copy is English * only, translated at display time via standard WP i18n like the rest * of the plugin. */ private function bil( string $it, string $en ): string { return esc_html( $en ); } /** * Returns HTML for the period window (e.g. "Sep – Jan"). * Returns empty string if no window can be determined. */ private function bilingual_period_window( int $period ): string { $settings = get_option( 'academiccrew_settings', [] ); $windows = $settings['period_windows'] ?? []; $months_en = [ 1 => __( 'Jan', 'academiccrew' ), 2 => __( 'Feb', 'academiccrew' ), 3 => __( 'Mar', 'academiccrew' ), 4 => __( 'Apr', 'academiccrew' ), 5 => __( 'May', 'academiccrew' ), 6 => __( 'Jun', 'academiccrew' ), 7 => __( 'Jul', 'academiccrew' ), 8 => __( 'Aug', 'academiccrew' ), 9 => __( 'Sep', 'academiccrew' ), 10 => __( 'Oct', 'academiccrew' ), 11 => __( 'Nov', 'academiccrew' ), 12 => __( 'Dec', 'academiccrew' ), ]; if ( empty( $windows[ $period ] ) ) { $en = match ( $period ) { 1 => __( 'Sep – Jan', 'academiccrew' ), 2 => __( 'Feb – Jun', 'academiccrew' ), default => '', }; } else { $s = $windows[ $period ]['start']; $e = $windows[ $period ]['end']; $en = ( $months_en[ $s['month'] ] ?? '' ) . ' – ' . ( $months_en[ $e['month'] ] ?? '' ); } return $en ? $this->bil( '', $en ) : ''; } // --------------------------------------------------------------- // Zone 1: Course header // --------------------------------------------------------------- private function render_header( int $course_id ): string { $get = fn( string $key ) => get_post_meta( $course_id, $key, true ); $calc = new AcademicYearCalculator(); $all_codes = (array) ( $get( '_acc_course_codes' ) ?: [] ); if ( empty( $all_codes ) ) { $single = $get( '_acc_course_code' ); if ( $single ) { $all_codes = [ $single ]; } } $credits = $get( '_acc_credits' ) ?: ''; $degree = $get( '_acc_degree_program' ) ?: ''; $language = $get( '_acc_language' ) ?: 'IT'; $period = (int) ( $get( '_acc_period' ) ?: 1 ); $year_override = (bool) $get( '_acc_year_override' ); $is_archived = (bool) $get( '_acc_archived' ); $acad_year = ( $year_override || $is_archived ) ? (int) ( $get( '_acc_academic_year' ) ?: $calc->calculate( $period ) ) : $calc->calculate( $period ); $syllabus = $get( '_acc_syllabus_url' ) ?: ''; $moodle = $get( '_acc_moodle_url' ) ?: ''; $level = $get( '_acc_level' ) ?: null; $abstract = $get( '_acc_abstract' ) ?: null; // Older imports have no _acc_abstract but do carry a Presentation/Programme // body — derive the tagline from it rather than show the "not available" note. if ( ! $abstract ) { $body = wp_strip_all_tags( (string) get_post_field( 'post_content', $course_id ) ); if ( trim( $body ) !== '' ) { $abstract = wp_trim_words( $body, 45 ); } } $title = get_the_title( $course_id ); $archive_id = (int) get_option( 'academiccrew_courses_archive_page_id', 0 ); $archive_url = $archive_id ? get_permalink( $archive_id ) : null; $raw = get_post_meta( $course_id, '_acc_instructors', true ); $instructors = is_array( $raw ) ? $raw : ( $raw ? maybe_unserialize( $raw ) : [] ); if ( ! is_array( $instructors ) ) { $instructors = []; } $role_order = [ 'coordinator', 'teacher', 'teaching-assistant' ]; $role_labels_en = [ 'coordinator' => __( 'Course Coordinator', 'academiccrew' ), 'teacher' => __( 'Lecturer', 'academiccrew' ), 'teaching-assistant' => __( 'Teaching Assistant', 'academiccrew' ), ]; $role_labels_it = [ 'coordinator' => 'Responsabile del corso', 'teacher' => 'Docente', 'teaching-assistant' => 'Assistente', ]; $by_role = array_fill_keys( $role_order, [] ); foreach ( $instructors as $entry ) { $role = $entry['role'] ?? 'teacher'; if ( ! isset( $by_role[ $role ] ) ) { continue; } $mid = (int) ( $entry['member_id'] ?? 0 ); if ( $mid ) { $lp = (int) get_post_meta( $mid, '_academiccrew_linked_page_id', true ); $by_role[ $role ][] = [ 'name' => get_the_title( $mid ), 'url' => $lp ? get_permalink( $lp ) : null, 'avatar' => get_post_meta( $mid, '_acc_avatar_url', true ) ?: ( get_the_post_thumbnail_url( $mid, [ 96, 120 ] ) ?: '' ), ]; } elseif ( ! empty( $entry['name'] ) ) { // External instructor (not a group member): name + optional link. $by_role[ $role ][] = [ 'name' => (string) $entry['name'], 'url' => ! empty( $entry['url'] ) ? (string) $entry['url'] : null, 'avatar' => '', ]; } } $window_html = $this->bilingual_period_window( $period ); wp_enqueue_style( 'academiccrew-member-profile', ACADEMICCREW_URL . 'assets/css/member-profile.css', [ 'fusion-stylesheet', 'tabler-icons' ], ACADEMICCREW_VERSION ); wp_enqueue_style( 'academiccrew-course-profile', ACADEMICCREW_URL . 'assets/css/course-profile.css', [ 'academiccrew-member-profile', 'fusion-stylesheet', 'tabler-icons' ], ACADEMICCREW_VERSION ); ob_start(); ?>

bil( '', __( 'The extended description for this course is not yet available.', 'academiccrew' ) ); } ?>

P
0 ) : ?>
bil( 'Crediti', __( 'Credits', 'academiccrew' ) ); ?> CFU
bil( 'Anno accademico', __( 'Academic year', 'academiccrew' ) ); ?> format( $acad_year ) ); ?>
bil( 'Corso di laurea', __( 'Degree programme', 'academiccrew' ) ); ?>
bil( 'Periodo', __( 'Period window', 'academiccrew' ) ); ?>
'BSc', 'msc' => 'MSc', 'phd' => 'PhD', 'professional' => __( 'Professional', 'academiccrew' ), 'other' => __( 'Other', 'academiccrew' ), ]; $level_labels_it = [ 'bsc' => 'BSc', 'msc' => 'MSc', 'phd' => 'PhD', 'professional' => 'Professionale', 'other' => 'Altro', ]; ?>
bil( 'Livello', __( 'Level', 'academiccrew' ) ); ?> bil( $level_labels_it[ $level ] ?? ucfirst( $level ), $level_labels_en[ $level ] ?? ucfirst( $level ) ); ?>

bil( 'Docenti degli anni precedenti', __( 'Past teaching staff', 'academiccrew' ) ); ?>

get_post_meta( $course_id, $key, true ); $outcomes_raw = $get( '_acc_learning_outcomes' ); $outcomes = is_array( $outcomes_raw ) && ! empty( $outcomes_raw ) ? $outcomes_raw : null; $topics_raw = $get( '_acc_topics' ); $topics = is_array( $topics_raw ) && ! empty( $topics_raw ) ? $topics_raw : null; $prereq_raw = $get( '_acc_prerequisites' ); $prereq_text = is_array( $prereq_raw ) ? ( $prereq_raw['text'] ?? null ) : null; $prereq_text = $prereq_text ?: null; $prereq_tags = is_array( $prereq_raw ) && ! empty( $prereq_raw['tags'] ) ? $prereq_raw['tags'] : null; $assessment = $get( '_acc_assessment' ) ?: null; $schedule_raw = $get( '_acc_schedule' ); $schedule = is_array( $schedule_raw ) && ! empty( $schedule_raw ) ? $schedule_raw : null; $reading_raw = $get( '_acc_reading_list' ); $reading = is_array( $reading_raw ) && ! empty( $reading_raw ) ? $reading_raw : null; $has_prereq = $prereq_text || $prereq_tags; if ( ! $outcomes && ! $topics && ! $has_prereq && ! $assessment && ! $schedule && ! $reading ) { return ''; } $day_labels_it = [ 'Mon' => 'Lun', 'Tue' => 'Mar', 'Wed' => 'Mer', 'Thu' => 'Gio', 'Fri' => 'Ven', ]; $day_labels_en = [ 'Mon' => __( 'Mon', 'academiccrew' ), 'Tue' => __( 'Tue', 'academiccrew' ), 'Wed' => __( 'Wed', 'academiccrew' ), 'Thu' => __( 'Thu', 'academiccrew' ), 'Fri' => __( 'Fri', 'academiccrew' ), ]; ob_start(); ?>

bil( 'Risultati di apprendimento', __( 'Learning Outcomes', 'academiccrew' ) ); ?>

bil( 'Argomenti del corso', __( 'Topics', 'academiccrew' ) ); ?>

bil( 'Prerequisiti', __( 'Prerequisites', 'academiccrew' ) ); ?>

bil( "Modalità d'esame", __( 'Assessment', 'academiccrew' ) ); ?>

bil( 'Orario delle lezioni', __( 'Schedule', 'academiccrew' ) ); ?>

bil( $day_labels_it[ $s['day'] ] ?? $s['day'], $day_labels_en[ $s['day'] ] ?? $s['day'] ); ?>

bil( 'Riferimenti bibliografici', __( 'Reading List', 'academiccrew' ) ); ?>

post_content ) ) { return ''; } return wpautop( do_shortcode( do_blocks( $post->post_content ) ) ); } private function period_window_label( int $period ): string { $settings = get_option( 'academiccrew_settings', [] ); $windows = $settings['period_windows'] ?? []; if ( empty( $windows[ $period ] ) ) { return match ( $period ) { 1 => __( 'Sep – Jan', 'academiccrew' ), 2 => __( 'Feb – Jun', 'academiccrew' ), default => '', }; } $s = $windows[ $period ]['start']; $e = $windows[ $period ]['end']; $months = [ 1 => __( 'Jan', 'academiccrew' ), 2 => __( 'Feb', 'academiccrew' ), 3 => __( 'Mar', 'academiccrew' ), 4 => __( 'Apr', 'academiccrew' ), 5 => __( 'May', 'academiccrew' ), 6 => __( 'Jun', 'academiccrew' ), 7 => __( 'Jul', 'academiccrew' ), 8 => __( 'Aug', 'academiccrew' ), 9 => __( 'Sep', 'academiccrew' ), 10 => __( 'Oct', 'academiccrew' ), 11 => __( 'Nov', 'academiccrew' ), 12 => __( 'Dec', 'academiccrew' ), ]; return sprintf( '%s – %s', $months[ $s['month'] ] ?? $s['month'], $months[ $e['month'] ] ?? $e['month'] ); } private function initials( string $name ): string { $parts = array_filter( explode( ' ', trim( $name ) ) ); if ( count( $parts ) >= 2 ) { return strtoupper( mb_substr( $parts[0], 0, 1 ) . mb_substr( end( $parts ), 0, 1 ) ); } return strtoupper( mb_substr( $name, 0, 2 ) ); } }