0 && ! in_array( $id, $out, true ) && count( $out ) < \RoosterLMS\UI\TableQuery::MULTI_MAX ) { $out[] = $id; } } return $out; } public static function keys( string $key ): array { $out = array(); foreach ( self::raw_list( $key ) as $raw ) { $value = sanitize_key( (string) $raw ); if ( '' !== $value && ! in_array( $value, $out, true ) && count( $out ) < \RoosterLMS\UI\TableQuery::MULTI_MAX ) { $out[] = $value; } } return $out; } public static function texts( string $key ): array { $out = array(); foreach ( self::raw_list( $key ) as $raw ) { $value = mb_substr( trim( sanitize_text_field( (string) $raw ) ), 0, 100 ); if ( '' !== $value && ! in_array( $value, $out, true ) && count( $out ) < \RoosterLMS\UI\TableQuery::MULTI_MAX ) { $out[] = $value; } } return $out; } public static function is_list( string $key ): bool { return is_array( self::get( $key ) ); } private static function raw_list( string $key ): array { $raw = self::get( $key ); $raw = is_array( $raw ) ? array_slice( $raw, 0, 4 * \RoosterLMS\UI\TableQuery::MULTI_MAX ) : array( $raw ); return array_values( array_filter( $raw, 'is_scalar' ) ); } public static function key( string $key ): string { $raw = self::get( $key ); return is_scalar( $raw ) ? sanitize_key( (string) $raw ) : ''; } public static function has( string $key ): bool { return null !== self::get( $key ); } public static function slug( string $key ): string { $raw = self::get( $key ); return is_scalar( $raw ) ? sanitize_title( (string) $raw ) : ''; } public static function cursor( string $key ): string { $raw = self::get( $key ); $raw = is_scalar( $raw ) ? sanitize_text_field( (string) $raw ) : ''; return 1 === preg_match( '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\|\d{1,20}$/', $raw ) ? $raw : ''; } public static function opaque( string $key ): string { $raw = self::get( $key ); $raw = is_scalar( $raw ) ? (string) $raw : ''; return 1 === preg_match( '/^[A-Za-z0-9_-]{1,512}$/', $raw ) ? $raw : ''; } public static function code(): string { $raw = self::get( 'rl_code' ); $raw = is_scalar( $raw ) ? sanitize_text_field( (string) $raw ) : ''; return function_exists( 'mb_substr' ) ? mb_substr( $raw, 0, 40 ) : substr( $raw, 0, 40 ); } public static function category(): int { $id = self::int( 'rl_cat' ); return $id > 0 ? $id : self::legacy_category(); } public static function legacy_category(): int { $raw = self::get( 'terms' ); if ( ! is_array( $raw ) ) { $raw = TableRequest::get( 'category' ); } if ( is_string( $raw ) && '' !== $raw ) { $value = sanitize_title( $raw ); if ( is_numeric( $value ) ) { $raw = array( (int) $value ); } else { $term = '' !== $value ? get_term_by( 'slug', $value, \RoosterLMS\Categories\CategoryTaxonomy::COURSE->taxonomy() ) : false; $raw = $term instanceof \WP_Term ? array( (int) $term->term_id ) : array(); } } if ( ! is_array( $raw ) ) { return 0; } foreach ( array_slice( $raw, 0, 10 ) as $term ) { $term_id = is_scalar( $term ) && is_numeric( $term ) ? (int) $term : 0; if ( $term_id <= 0 ) { continue; } $category = \RoosterLMS\Domain\Services::category_repo()->for_term( $term_id ); if ( null !== $category && $category->is_active() && \RoosterLMS\Categories\CategoryTaxonomy::COURSE === $category->taxonomy ) { return $category->id; } } return 0; } public static function categories(): array { $out = self::ints( 'rl_cat' ); foreach ( self::LEGACY_CATEGORY_LISTS as $name ) { $raw = TableRequest::get( $name ); if ( ! is_array( $raw ) ) { continue; } foreach ( array_slice( $raw, 0, \RoosterLMS\UI\TableQuery::MULTI_MAX ) as $term ) { $term_id = is_scalar( $term ) && is_numeric( $term ) ? (int) $term : 0; $category = $term_id > 0 ? \RoosterLMS\Domain\Services::category_repo()->for_term( $term_id ) : null; if ( null !== $category && ! in_array( $category->id, $out, true ) ) { $out[] = $category->id; } } } return $out; } public static function levels(): array { $out = self::ints( 'rl_level' ); foreach ( self::LEGACY_LEVEL_LISTS as $name ) { $raw = TableRequest::get( $name ); foreach ( is_array( $raw ) ? array_slice( $raw, 0, \RoosterLMS\UI\TableQuery::MULTI_MAX ) : array() as $item ) { if ( is_scalar( $item ) && '' !== (string) $item ) { $out[] = is_numeric( $item ) ? (int) $item : sanitize_key( (string) $item ); } } } return array_values( array_unique( $out, SORT_REGULAR ) ); } public static function availability(): string { $value = self::key( 'rl_avail' ); if ( '' === $value ) { $raw = TableRequest::get( 'availability' ); $value = match ( is_scalar( $raw ) ? sanitize_key( (string) $raw ) : '' ) { 'available_now' => 'now', 'coming_soon' => 'upcoming', default => '', }; } return in_array( $value, array( 'now', 'upcoming' ), true ) ? $value : ''; } public static function sort(): string { $sort = self::key( 'rl_sort' ); if ( '' === $sort ) { $sort = match ( self::key( 'sort' ) ) { 'date_high' => 'newest', 'date_low' => 'oldest', 'popular' => 'popularity', default => '', }; } return in_array( $sort, self::SORTS, true ) ? $sort : ''; } public static function search(): string { $raw = self::get( 'search' ); $raw = is_scalar( $raw ) ? sanitize_text_field( (string) $raw ) : ''; $term = trim( $raw ); return function_exists( 'mb_substr' ) ? mb_substr( $term, 0, 100 ) : substr( $term, 0, 100 ); } public static function page(): int { $page = self::int( 'rl_page' ); return max( 1, $page > 0 ? $page : self::int( 'current_page' ) ); } public static function text( string $key, int $max = 100 ): string { $raw = self::get( $key ); $raw = is_scalar( $raw ) ? trim( sanitize_text_field( (string) $raw ) ) : ''; return function_exists( 'mb_substr' ) ? mb_substr( $raw, 0, $max ) : substr( $raw, 0, $max ); } public static function arg_names(): array { $wp = $GLOBALS['wp'] ?? null; $names = array_merge( $wp instanceof \WP ? (array) $wp->public_query_vars : array(), self::PAGE_ARGS, self::VIEW_ARGS, array_keys( self::$read ) ); $names = apply_filters( 'rooster_lms_view_arg_names', $names ); $out = array(); foreach ( is_array( $names ) ? $names : array() as $name ) { $name = is_string( $name ) ? $name : ''; if ( '' !== $name && sanitize_key( $name ) === $name && 'current_page' !== $name && ! in_array( $name, self::PRIVATE_ARGS, true ) ) { $out[ $name ] = true; } } return array_keys( $out ); } public static function view_args(): array { $out = array(); foreach ( self::arg_names() as $key ) { $value = TableRequest::get( $key ); if ( is_scalar( $value ) ) { $out[ $key ] = sanitize_text_field( (string) $value ); } } return $out; } public static function list_args(): array { $out = array(); foreach ( self::arg_names() as $key ) { $value = TableRequest::get( $key ); if ( ! is_array( $value ) || ! str_starts_with( $key, 'rl_' ) ) { continue; } $list = array(); foreach ( $value as $item ) { $item = is_scalar( $item ) ? sanitize_text_field( (string) $item ) : ''; if ( '' !== $item && ! in_array( $item, $list, true ) && count( $list ) < \RoosterLMS\UI\TableQuery::MULTI_MAX ) { $list[] = $item; } } if ( array() !== $list ) { $out[ $key ] = $list; } } if ( ! isset( $out['rl_cat'] ) && self::has_any( self::LEGACY_CATEGORY_LISTS ) ) { $ids = array_map( 'strval', self::categories() ); if ( array() !== $ids ) { $out['rl_cat'] = $ids; } } if ( ! isset( $out['rl_level'] ) && self::has_any( self::LEGACY_LEVEL_LISTS ) ) { $ids = array_map( 'strval', \RoosterLMS\Display\Catalogue::picked_levels() ); if ( array() !== $ids ) { $out['rl_level'] = $ids; } } return $out; } private static function has_any( array $names ): bool { foreach ( $names as $name ) { if ( is_array( TableRequest::get( $name ) ) ) { return true; } } return false; } public static function url( array $args = array(), array $remove = array() ): string { $query = self::view_args() + self::list_args(); foreach ( $remove as $key ) { unset( $query[ $key ] ); } foreach ( $args as $key => $value ) { if ( is_array( $value ) ) { $value = array_values( array_map( 'strval', $value ) ); if ( array() === $value ) { unset( $query[ $key ] ); continue; } $query[ $key ] = $value; continue; } $query[ $key ] = (string) $value; } $url = self::origin() . self::path(); return array() === $query ? $url : add_query_arg( urlencode_deep( $query ), $url ); } public static function base(): string { return self::origin() . self::path(); } public static function search_term(): string { $term = trim( self::posted( 'rl_search' ) ); return function_exists( 'mb_substr' ) ? mb_substr( $term, 0, 100 ) : substr( $term, 0, 100 ); } public static function search_page(): int { return absint( self::posted( 'rl_page' ) ); } private static function posted( string $key ): string { $simulated = TableRequest::simulation(); if ( null !== $simulated ) { $value = $simulated['post'][ $key ] ?? ''; return is_string( $value ) ? sanitize_text_field( $value ) : ''; } if ( ! \RoosterLMS\Admin\Request::posted_nonce_valid( self::SEARCH_NONCE, self::SEARCH_ACTION ) ) { return ''; } $value = \RoosterLMS\Admin\Request::posted_value( $key ); return is_string( $value ) ? $value : ''; } private static function get( string $key ): mixed { self::$read[ $key ] = true; return TableRequest::get( $key ); } private static function path(): string { $simulated = TableRequest::simulation(); if ( null !== $simulated ) { return '' !== $simulated['path'] ? $simulated['path'] : '/'; } $uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; $path = '' !== $uri ? wp_parse_url( $uri, PHP_URL_PATH ) : ''; return is_string( $path ) && '' !== $path ? $path : '/'; } private static function origin(): string { $home = wp_parse_url( home_url() ); $scheme = is_array( $home ) && isset( $home['scheme'] ) ? $home['scheme'] : 'http'; $host = is_array( $home ) && isset( $home['host'] ) ? $home['host'] : 'localhost'; $port = is_array( $home ) && isset( $home['port'] ) ? ':' . $home['port'] : ''; return $scheme . '://' . $host . $port; } }