array( 'label' => __( 'Rooster One', 'rooster-lms' ), 'description' => __( 'Light and clear: the standard look.', 'rooster-lms' ), 'tokens' => array(), 'dark' => false, 'style' => '', 'swatches' => array( '#ffffff', '#3b82f6', '#09090b' ), 'layout' => 'default', ), 'rooster-one-dark' => array( 'label' => __( 'Rooster One Dark', 'rooster-lms' ), 'description' => __( 'Light text on dark backgrounds.', 'rooster-lms' ), 'tokens' => array(), 'dark' => true, 'style' => '', 'swatches' => array( '#09090b', '#2563eb', '#fafafa' ), 'layout' => 'default', ), 'rooster-one-compact' => array( 'label' => __( 'Rooster One Compact', 'rooster-lms' ), 'description' => __( 'Denser lists and smaller controls, for people who work with many rows.', 'rooster-lms' ), 'tokens' => array( '--rl-control-h' => '32px', '--rl-control-h-sm' => '24px', '--rl-space-3' => '8px', '--rl-space-4' => '12px', '--rl-table-pad-y' => '6px', ), 'dark' => false, 'style' => '', 'swatches' => array( '#ffffff', '#3b82f6', '#71717a' ), 'layout' => 'default', ), ); $looks = apply_filters( 'rooster_lms_ui_templates', $free ); $out = array(); foreach ( is_array( $looks ) ? $looks : $free as $key => $look ) { $clean = is_string( $key ) && 1 === preg_match( self::KEY, $key ) && is_array( $look ) ? self::clean( $look ) : null; if ( null !== $clean ) { $out[ $key ] = $clean; } } if ( ! isset( $out[ self::DEFAULT ] ) ) { $out = array( self::DEFAULT => $free[ self::DEFAULT ] ) + $out; } self::$all[ $locale ] = $out; return $out; } public static function get( string $key ): ?array { return self::all()[ $key ] ?? null; } public static function exists( string $key ): bool { return isset( self::all()[ $key ] ); } public static function for_builder(): array { $out = array(); foreach ( self::all() as $key => $look ) { $out[] = array( 'key' => $key, 'label' => $look['label'], 'description' => $look['description'], 'swatches' => $look['swatches'], ); } return $out; } public static function for_viewer( Org $org, int $user_id, ?array $tree = null ): string { $settings = $org->settings; $unit = $user_id > 0 ? \RoosterLMS\Orgs\OrgScope::for_user( $user_id )->member_unit( $org->id ) : 0; $ar = \RoosterLMS\Orgs\ApparentRoot::unit_in( $org->id ); if ( $ar > 0 && ( $unit <= 0 || ! \RoosterLMS\Orgs\ApparentRoot::in_branch( $org->id, $unit ) ) ) { $unit = $ar; } $candidates = array(); if ( $unit > 0 && $settings->unit_looks ) { $repo = \RoosterLMS\Domain\Services::unit_settings(); if ( null !== $tree ) { $parents = array(); foreach ( $tree as $id => $row ) { if ( $row instanceof \RoosterLMS\Orgs\OrgUnit ) { $parents[ $row->id ] = $row->parent_id; } elseif ( is_int( $row ) || ( is_string( $row ) && ctype_digit( $row ) ) ) { $parents[ (int) $id ] = (int) $row; } } $looks = $repo->looks( $org->id ); $seen = array(); for ( $id = $unit; $id > 0 && ! isset( $seen[ $id ] ); $id = (int) ( $parents[ $id ] ?? 0 ) ) { $seen[ $id ] = true; if ( isset( $looks[ $id ] ) ) { $candidates[] = $looks[ $id ]; } } } else { $near = $repo->look_of( $unit ); if ( '' !== $near ) { $candidates[] = $near; } if ( '' !== $near && ! self::exists( $near ) ) { $looks = $repo->looks( $org->id ); foreach ( array_reverse( \RoosterLMS\Domain\Services::org_unit_repo()->ancestor_ids( $unit ) ) as $id ) { if ( isset( $looks[ (int) $id ] ) ) { $candidates[] = $looks[ (int) $id ]; } } } } } $candidates[] = $settings->ui_template; $candidates[] = self::site(); $key = self::DEFAULT; foreach ( $candidates as $candidate ) { if ( '' !== $candidate && self::exists( $candidate ) ) { $key = $candidate; break; } } $filtered = apply_filters( 'rooster_lms_ui_look', $key, array( 'org' => $org, 'unit' => $unit, 'user' => $user_id, ) ); return is_string( $filtered ) && self::exists( $filtered ) ? $filtered : $key; } public static function current(): string { $applied = Tokens::applied_look(); return '' !== $applied && self::exists( $applied ) ? $applied : self::site(); } public static function site(): string { $key = (string) Settings::get( 'ui_look' ); return self::exists( $key ) ? $key : self::DEFAULT; } public static function forget(): void { self::$all = array(); } private static function clean( array $look ): ?array { $label = is_string( $look['label'] ?? null ) ? sanitize_text_field( $look['label'] ) : ''; if ( '' === $label ) { return null; } $tokens = array(); foreach ( is_array( $look['tokens'] ?? null ) ? $look['tokens'] : array() as $name => $value ) { $value = is_scalar( $value ) ? trim( (string) $value ) : ''; if ( is_string( $name ) && 1 === preg_match( '/^--rl-[a-z0-9-]+$/', $name ) && '' !== $value && 1 !== preg_match( '/[;{}<>\\\\]|\/\*/', $value ) ) { $tokens[ $name ] = $value; } } $swatches = array(); foreach ( is_array( $look['swatches'] ?? null ) ? $look['swatches'] : array() as $colour ) { $hex = is_string( $colour ) ? (string) sanitize_hex_color( $colour ) : ''; if ( '' !== $hex ) { $swatches[] = $hex; } } $layout = is_string( $look['layout'] ?? null ) ? sanitize_key( $look['layout'] ) : ''; return array( 'label' => $label, 'description' => is_string( $look['description'] ?? null ) ? sanitize_text_field( $look['description'] ) : '', 'tokens' => $tokens, 'dark' => ! empty( $look['dark'] ), 'style' => is_string( $look['style'] ?? null ) ? sanitize_key( $look['style'] ) : '', 'swatches' => array_slice( $swatches, 0, 3 ), 'layout' => '' !== $layout ? $layout : 'default', ); } }