course_id ] = true; if ( $e->session->unit_id > 0 ) { $units[ $e->session->unit_id ] = true; } if ( Placement::LESSON !== $e->session->placement ) { $own[ $e->session->id ] = $e->session; } } $posts = self::read( array_keys( $courses ), array_keys( $units ), $lang ); $titles = array() === $own ? array() : Services::live_session_repo()->titles( array_values( $own ), $lang ); $out = array(); foreach ( $events as $e ) { $course = $posts[ ObjectType::COURSE->value ][ $e->course_id ] ?? null; $unit = $posts[ ObjectType::UNIT->value ][ $e->session->unit_id ] ?? null; $course_url = null === $course ? '' : Services::posts()->permalink( (int) $course['ID'] ); $url = null === $course || null === $unit ? '' : Urls::build( (int) $course['ID'], (int) $unit['ID'] ); $course_t = null === $course ? '' : self::title( (string) $course['post_title'] ); $title = isset( $titles[ $e->session->id ] ) ? (string) $titles[ $e->session->id ] : ( null === $unit ? '' : self::title( (string) $unit['post_title'] ) ); $out[ $e->id() ] = array( 'title' => '' !== $title ? $title : ( '' !== $course_t ? $course_t : __( 'Live lesson', 'rooster-lms' ) ), 'course_title' => $course_t, 'url' => '' !== $url ? $url : $course_url, 'course_url' => $course_url, 'image' => self::image( $unit ) ?? self::image( $course ), ); } return $out; } private static function read( array $courses, array $units, string $lang ): array { global $wpdb; $or = array(); if ( array() !== $courses ) { $or[] = SqlPart::of( '( op.object_type = %d AND op.object_id IN (' . implode( ',', array_fill( 0, count( $courses ), '%d' ) ) . ') )', array_merge( array( ObjectType::COURSE->value ), $courses ) ); } if ( array() !== $units ) { $or[] = SqlPart::of( '( op.object_type = %d AND op.object_id IN (' . implode( ',', array_fill( 0, count( $units ), '%d' ) ) . ') )', array_merge( array( ObjectType::UNIT->value ), $units ) ); } $rows = Db::rows( SqlPart::of( "SELECT /* outside-tx */ op.object_type AS rl_type, op.object_id AS rl_object, op.lang AS rl_lang, ( op.post_id = COALESCE( cc.home_post_id, uu.home_post_id ) ) AS rl_home, p.*, th.meta_value AS rl_thumb, af.meta_value AS rl_file, am.meta_value AS rl_meta, al.meta_value AS rl_alt FROM %i op JOIN %i p ON p.ID = op.post_id AND p.post_status = 'publish' LEFT JOIN %i cc ON op.object_type = %d AND cc.id = op.object_id LEFT JOIN %i uu ON op.object_type = %d AND uu.id = op.object_id LEFT JOIN %i th ON th.post_id = p.ID AND th.meta_key = '_thumbnail_id' LEFT JOIN %i af ON af.post_id = CAST( th.meta_value AS UNSIGNED ) AND af.meta_key = '_wp_attached_file' LEFT JOIN %i am ON am.post_id = CAST( th.meta_value AS UNSIGNED ) AND am.meta_key = '_wp_attachment_metadata' LEFT JOIN %i al ON al.post_id = CAST( th.meta_value AS UNSIGNED ) AND al.meta_key = '_wp_attachment_image_alt' WHERE ", Db::table( 'object_posts' ), $wpdb->posts, Db::table( 'courses' ), ObjectType::COURSE->value, Db::table( 'units' ), ObjectType::UNIT->value, $wpdb->postmeta, $wpdb->postmeta, $wpdb->postmeta, $wpdb->postmeta )->add( '( ' )->add( SqlPart::join( ' OR ', $or ) )->add( ' )' ) ); $default = Providers::current()->default_language(); $by = array(); foreach ( $rows as $row ) { $type = (int) $row['rl_type']; $object = (int) $row['rl_object']; $key = (string) $row['rl_lang']; if ( ! isset( $by[ $type ][ $object ][ $key ] ) ) { $by[ $type ][ $object ][ $key ] = $row; } if ( 1 === (int) $row['rl_home'] ) { $by[ $type ][ $object ]['@home'] = $row; } } $out = array(); $cache = array(); foreach ( $by as $type => $objects ) { foreach ( $objects as $object => $langs ) { $row = $langs[ $lang ] ?? $langs[ $default ] ?? $langs['@home'] ?? reset( $langs ); if ( ! is_array( $row ) ) { continue; } $out[ $type ][ $object ] = $row; $post = $row; foreach ( array_keys( $post ) as $column ) { if ( str_starts_with( (string) $column, 'rl_' ) ) { unset( $post[ $column ] ); } } $cache[] = (object) $post; } } if ( array() !== $cache ) { update_post_cache( $cache ); } return $out; } private static function image( ?array $row ): ?array { $file = null === $row ? '' : (string) ( $row['rl_file'] ?? '' ); if ( '' === $file ) { return null; } $id = (int) $row['rl_thumb']; $uploads = wp_get_upload_dir(); $base = trailingslashit( (string) $uploads['baseurl'] ); $full = str_starts_with( $file, 'http://' ) || str_starts_with( $file, 'https://' ) ? $file : $base . ltrim( $file, '/' ); $full = (string) apply_filters( 'wp_get_attachment_url', $full, $id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WordPress's own filter, applied as wp_get_attachment_url() does. $meta = maybe_unserialize( (string) ( $row['rl_meta'] ?? '' ) ); $meta = is_array( $meta ) ? $meta : array(); $src = $full; $w = (int) ( $meta['width'] ?? 0 ); $h = (int) ( $meta['height'] ?? 0 ); foreach ( array( 'medium_large', 'large', 'medium' ) as $size ) { $one = $meta['sizes'][ $size ] ?? null; if ( is_array( $one ) && '' !== (string) ( $one['file'] ?? '' ) ) { $src = str_replace( wp_basename( $full ), (string) $one['file'], $full ); $w = (int) ( $one['width'] ?? 0 ); $h = (int) ( $one['height'] ?? 0 ); break; } } return array( 'src' => $src, 'width' => $w, 'height' => $h, 'alt' => trim( wp_strip_all_tags( (string) ( $row['rl_alt'] ?? '' ) ) ), ); } private static function title( string $title ): string { return trim( wp_strip_all_tags( html_entity_decode( $title, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) ) ); } }