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();
?>
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 ) );
}
}