From 16cc214db683890bc838c3d601723a0a21844ec9 Mon Sep 17 00:00:00 2001 From: salam Date: Fri, 26 Dec 2025 10:42:51 +0100 Subject: [PATCH] basic comparatore completed --- app/Actions/MultilineSlots.php | 33 - .../Commands/Grapper/Betsapi/Fixtures.php | 33 +- .../Resources/Bettypes/BettypeResource.php | 5 - .../Bookmakers/BookmakerResource.php | 5 - .../Resources/Clients/ClientResource.php | 5 - .../CompetitionGroupFilterResource.php | 5 - .../Resources/Countries/CountryResource.php | 5 - .../ElementFilters/ElementFilterResource.php | 5 - .../Resources/Events/Tables/EventsTable.php | 166 +- .../GroupFilters/GroupFilterResource.php | 5 - .../Pages/ListMultilineClients.php | 4 +- .../Resources/Providers/ProviderResource.php | 5 - .../Resources/Sports/SportResource.php | 5 - .../Supervisors/SupervisorResource.php | 5 - .../Resources/Users/Schemas/UserForm.php | 19 +- .../Resources/Users/Tables/UsersTable.php | 2 + app/Filament/Resources/Users/UserResource.php | 5 - app/Filament/Tables/Columns/Column3way.php | 46 +- app/Filament/Tables/Columns/ColumnDc.php | 46 +- app/Filament/Tables/Columns/ColumnDnb.php | 46 +- app/Filament/Tables/Columns/ColumnGgng.php | 46 +- app/Filament/Tables/Columns/ColumnTotal.php | 46 +- app/Filament/Tables/Columns/ColumnTq.php | 46 +- app/Livewire/MapperClients.php | 55 - app/Livewire/MapperEvents.php | 62 - app/Livewire/MapperLeft.php | 44 + app/Livewire/MapperRight.php | 44 + app/Livewire/TopMenuItems.php | 6 +- app/Models/Event.php | 5 - app/Models/User.php | 10 +- app/Policies/BettypePolicy.php | 70 + app/Policies/BookmakerPolicy.php | 70 + app/Policies/ClientPolicy.php | 70 + app/Policies/CompetitionGroupFilterPolicy.php | 70 + app/Policies/CountryPolicy.php | 70 + app/Policies/ElementFilterPolicy.php | 70 + app/Policies/EventPolicy.php | 70 + app/Policies/GroupFilterPolicy.php | 70 + app/Policies/MultilineClientPolicy.php | 70 + app/Policies/MultilineSettingPolicy.php | 70 + app/Policies/ProviderPolicy.php | 70 + app/Policies/RolePolicy.php | 70 + app/Policies/SportPolicy.php | 70 + app/Policies/SupervisorPolicy.php | 70 + app/Policies/UserPolicy.php | 67 + app/Providers/Filament/AdminPanelProvider.php | 3 + composer.json | 1 + composer.lock | 257 +- config/filament-shield.php | 263 +++ .../0001_01_01_000000_create_users_table.php | 3 +- ..._12_26_090903_create_permission_tables.php | 134 ++ database/seeders/DatabaseSeeder.php | 2 +- .../seeders/RolesAndPermissionsSeeder.php | 48 + database/seeders/UserSeeder.php | 36 - public/js/app/dojo.js | 2076 ----------------- public/js/app/tabulator-js.js | 3 - resources/css/filament/admin/theme.css | 9 +- .../tables/columns/column-dc.blade.php | 49 +- .../tables/columns/column-dnb.blade.php | 44 +- .../tables/columns/column-ggng.blade.php | 44 +- .../tables/columns/column-total.blade.php | 49 +- .../tables/columns/column-tq.blade.php | 44 +- .../tables/columns/column_3way.blade.php | 55 +- .../fumetto_1x2.blade.php | 4 +- .../fumetto_dc.blade.php | 4 +- .../fumetto_dnb.blade.php | 4 +- .../fumetto_ggng.blade.php | 4 +- .../fumetto_total.blade.php | 4 +- .../fumetto_tq.blade.php | 4 +- .../tables/columns/multiline/label.blade.php | 6 + .../tables/columns/multiline/labels.blade.php | 65 - .../tables/columns/multiline/main.blade.php | 27 - .../tables/columns/multiline/odds.blade.php | 2 +- .../responsive-bettype-columns.blade.php | 5 - .../tables/two-columns-split.blade.php | 8 - .../views/livewire/bettype-toggle.blade.php | 7 +- ...lients.blade.php => mapper-left.blade.php} | 0 ...vents.blade.php => mapper-right.blade.php} | 0 .../views/livewire/top-menu-items.blade.php | 44 +- setup.sh | 38 +- 80 files changed, 2413 insertions(+), 2794 deletions(-) delete mode 100644 app/Actions/MultilineSlots.php delete mode 100644 app/Livewire/MapperClients.php delete mode 100644 app/Livewire/MapperEvents.php create mode 100644 app/Livewire/MapperLeft.php create mode 100644 app/Livewire/MapperRight.php create mode 100644 app/Policies/BettypePolicy.php create mode 100644 app/Policies/BookmakerPolicy.php create mode 100644 app/Policies/ClientPolicy.php create mode 100644 app/Policies/CompetitionGroupFilterPolicy.php create mode 100644 app/Policies/CountryPolicy.php create mode 100644 app/Policies/ElementFilterPolicy.php create mode 100644 app/Policies/EventPolicy.php create mode 100644 app/Policies/GroupFilterPolicy.php create mode 100644 app/Policies/MultilineClientPolicy.php create mode 100644 app/Policies/MultilineSettingPolicy.php create mode 100644 app/Policies/ProviderPolicy.php create mode 100644 app/Policies/RolePolicy.php create mode 100644 app/Policies/SportPolicy.php create mode 100644 app/Policies/SupervisorPolicy.php create mode 100644 app/Policies/UserPolicy.php create mode 100644 config/filament-shield.php create mode 100644 database/migrations/2025_12_26_090903_create_permission_tables.php create mode 100644 database/seeders/RolesAndPermissionsSeeder.php delete mode 100644 database/seeders/UserSeeder.php delete mode 100644 public/js/app/dojo.js delete mode 100644 public/js/app/tabulator-js.js rename resources/views/filament/tables/columns/{multiline => fumetto}/fumetto_1x2.blade.php (95%) rename resources/views/filament/tables/columns/{multiline => fumetto}/fumetto_dc.blade.php (95%) rename resources/views/filament/tables/columns/{multiline => fumetto}/fumetto_dnb.blade.php (95%) rename resources/views/filament/tables/columns/{multiline => fumetto}/fumetto_ggng.blade.php (95%) rename resources/views/filament/tables/columns/{multiline => fumetto}/fumetto_total.blade.php (95%) rename resources/views/filament/tables/columns/{multiline => fumetto}/fumetto_tq.blade.php (95%) create mode 100644 resources/views/filament/tables/columns/multiline/label.blade.php delete mode 100644 resources/views/filament/tables/columns/multiline/labels.blade.php delete mode 100644 resources/views/filament/tables/columns/multiline/main.blade.php delete mode 100644 resources/views/filament/tables/responsive-bettype-columns.blade.php delete mode 100644 resources/views/filament/tables/two-columns-split.blade.php rename resources/views/livewire/{mapper-clients.blade.php => mapper-left.blade.php} (100%) rename resources/views/livewire/{mapper-events.blade.php => mapper-right.blade.php} (100%) diff --git a/app/Actions/MultilineSlots.php b/app/Actions/MultilineSlots.php deleted file mode 100644 index 2afdcc47..00000000 --- a/app/Actions/MultilineSlots.php +++ /dev/null @@ -1,33 +0,0 @@ -where('multiline_setting_id', auth()->user()->multiline_setting_id) - ->orderBy('sort') - ->get(); - - $multilines = []; - - foreach ($slots as $id => $slot) { - $open = true; - foreach ($data as $item) { - if ($slot->bookmaker_id == $item->bookmaker_id) { - $multilines[$id] = $item; - $open = false; - } - } - if ($open) { - $multilines[$id] = $slot; - } - } - - return $multilines; - } -} diff --git a/app/Console/Commands/Grapper/Betsapi/Fixtures.php b/app/Console/Commands/Grapper/Betsapi/Fixtures.php index 8227a947..056fe277 100644 --- a/app/Console/Commands/Grapper/Betsapi/Fixtures.php +++ b/app/Console/Commands/Grapper/Betsapi/Fixtures.php @@ -53,22 +53,25 @@ class Fixtures foreach ($data['results'] as $row) { - $event = [ - 'sport_id' => (int) $sport_id, - 'provider_id' => (int) Nuh::$options['provider_id'], - 'starttime' => Carbon::createFromTimestamp((string) $row['time'])->toDateTimeString(), - 'home' => $row['home']['name'] ?? '', - 'home_id' => (int) $row['home']['id'], - 'home_cc' => $row['home']['cc'] ?? '', - 'away' => $row['away']['name'] ?? '', - 'away_id' => (int) $row['away']['id'], - 'away_cc' => $row['away']['cc'] ?? '', - 'group' => $row['league']['name'] ?? '', - 'group_id' => (int) $row['league']['id'] ?? 0, - 'group_cc' => $row['league']['cc'] ?? '', - ]; + if (isset($row['home']['id']) && isset($row['away']['id']) && strpos($row['league']['name'], 'Esoccer') === false) { - Mapper::updateOrCreate(['event_id' => (int) $row['id']], $event); + $event = [ + 'sport_id' => (int) $sport_id, + 'provider_id' => (int) Nuh::$options['provider_id'], + 'starttime' => Carbon::createFromTimestamp((string) $row['time'])->toDateTimeString(), + 'home' => $row['home']['name'] ?? '', + 'home_id' => (int) $row['home']['id'], + 'home_cc' => $row['home']['cc'] ?? '', + 'away' => $row['away']['name'] ?? '', + 'away_id' => (int) $row['away']['id'], + 'away_cc' => $row['away']['cc'] ?? '', + 'group' => $row['league']['name'] ?? '', + 'group_id' => (int) $row['league']['id'] ?? 0, + 'group_cc' => $row['league']['cc'] ?? '', + ]; + + Mapper::updateOrCreate(['event_id' => (int) $row['id']], $event); + } } } } diff --git a/app/Filament/Resources/Bettypes/BettypeResource.php b/app/Filament/Resources/Bettypes/BettypeResource.php index 84480060..a078a624 100644 --- a/app/Filament/Resources/Bettypes/BettypeResource.php +++ b/app/Filament/Resources/Bettypes/BettypeResource.php @@ -23,11 +23,6 @@ class BettypeResource extends Resource protected static string|\UnitEnum|null $navigationGroup = 'Static Data'; - public static function canViewAny(): bool - { - return auth()->user()->super_admin == 1; - } - public static function form(Schema $schema): Schema { return BettypeForm::configure($schema); diff --git a/app/Filament/Resources/Bookmakers/BookmakerResource.php b/app/Filament/Resources/Bookmakers/BookmakerResource.php index 44774f32..e39a058c 100644 --- a/app/Filament/Resources/Bookmakers/BookmakerResource.php +++ b/app/Filament/Resources/Bookmakers/BookmakerResource.php @@ -24,11 +24,6 @@ class BookmakerResource extends Resource protected static string|\UnitEnum|null $navigationGroup = 'Static Data'; - public static function canViewAny(): bool - { - return auth()->user()->super_admin == 1; - } - public static function form(Schema $schema): Schema { return BookmakerForm::configure($schema); diff --git a/app/Filament/Resources/Clients/ClientResource.php b/app/Filament/Resources/Clients/ClientResource.php index d6346ea1..12ea7813 100644 --- a/app/Filament/Resources/Clients/ClientResource.php +++ b/app/Filament/Resources/Clients/ClientResource.php @@ -23,11 +23,6 @@ class ClientResource extends Resource protected static string|\UnitEnum|null $navigationGroup = 'User'; - public static function canViewAny(): bool - { - return auth()->user()->super_admin == 1; - } - public static function form(Schema $schema): Schema { return ClientForm::configure($schema); diff --git a/app/Filament/Resources/CompetitionGroupFilters/CompetitionGroupFilterResource.php b/app/Filament/Resources/CompetitionGroupFilters/CompetitionGroupFilterResource.php index d0530395..09629a3d 100644 --- a/app/Filament/Resources/CompetitionGroupFilters/CompetitionGroupFilterResource.php +++ b/app/Filament/Resources/CompetitionGroupFilters/CompetitionGroupFilterResource.php @@ -36,11 +36,6 @@ class CompetitionGroupFilterResource extends Resource protected static ?string $pluralModelLabel = 'Comp Groups'; - public static function canViewAny(): bool - { - return auth()->user()->super_admin == 1; - } - public static function form(Schema $schema): Schema { return CompetitionGroupFilterForm::configure($schema); diff --git a/app/Filament/Resources/Countries/CountryResource.php b/app/Filament/Resources/Countries/CountryResource.php index da040a4a..69bd8e79 100644 --- a/app/Filament/Resources/Countries/CountryResource.php +++ b/app/Filament/Resources/Countries/CountryResource.php @@ -30,11 +30,6 @@ class CountryResource extends Resource protected static string|\UnitEnum|null $navigationGroup = 'Static Data'; - public static function canViewAny(): bool - { - return auth()->user()->super_admin == 1; - } - public static function form(Schema $schema): Schema { return $schema diff --git a/app/Filament/Resources/ElementFilters/ElementFilterResource.php b/app/Filament/Resources/ElementFilters/ElementFilterResource.php index 4d2bc8d3..8b8228df 100644 --- a/app/Filament/Resources/ElementFilters/ElementFilterResource.php +++ b/app/Filament/Resources/ElementFilters/ElementFilterResource.php @@ -36,11 +36,6 @@ class ElementFilterResource extends Resource protected static ?string $pluralModelLabel = 'Elements'; - public static function canViewAny(): bool - { - return auth()->user()->super_admin == 1; - } - public static function form(Schema $schema): Schema { return ElementFilterForm::configure($schema); diff --git a/app/Filament/Resources/Events/Tables/EventsTable.php b/app/Filament/Resources/Events/Tables/EventsTable.php index ed8d215f..29c7cb02 100644 --- a/app/Filament/Resources/Events/Tables/EventsTable.php +++ b/app/Filament/Resources/Events/Tables/EventsTable.php @@ -7,10 +7,12 @@ use App\Filament\Tables\Columns\ColumnDc; use App\Filament\Tables\Columns\ColumnDnb; use App\Filament\Tables\Columns\ColumnGgng; use App\Filament\Tables\Columns\ColumnTotal; +use App\Filament\Tables\Columns\ColumnTq; use App\Filament\Tables\Columns\MatchIds; use App\Models\BettypeUser; use App\Models\Country; use App\Models\Event; +use App\Models\MultilineClient; use Carbon\Carbon; use Filament\Actions\Action; use Filament\Actions\ActionGroup; @@ -19,8 +21,8 @@ use Filament\Actions\EditAction; use Filament\Forms\Components\Select; use Filament\Schemas\Components\Utilities\Get; use Filament\Support\Enums\FontWeight; +use Filament\Support\Enums\VerticalAlignment; use Filament\Tables\Columns\IconColumn; -use Filament\Tables\Columns\Layout\View; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Enums\FiltersLayout; use Filament\Tables\Filters\Filter; @@ -31,6 +33,81 @@ class EventsTable { public static $bettype_settings = []; + public static $multiline_bookmaker = []; + + public static $odds_3ways = []; + + public static $odds_totals = []; + + public static $odds_totals_q1 = []; + + public static $odds_dc = []; + + public static $odds_dnb = []; + + public static $odds_ggng = []; + + public static $odds_handicaps = []; + + public static $odds_handicaps_q1 = []; + + public static $odds_2ways = []; + + public static $odds_2ways_s1 = []; + + public static $odds_tq = []; + + public static function load_multiline_bookmaker() + { + $res = MultilineClient::where('client_id', auth()->user()->client_id)->where('sport_id', auth()->user()->sport_id)->select('bookmaker_id', 'multiline_setting_id')->orderBy('multiline_setting_id')->orderBy('sort')->get(); + + foreach ($res as $value) { + switch ($value->multiline_setting_id) { + case 1: + case 13: + self::$odds_3ways[] = $value->bookmaker_id; + break; + case 2: + self::$odds_dc[] = $value->bookmaker_id; + break; + case 3: + self::$odds_dnb[] = $value->bookmaker_id; + break; + case 4: + self::$odds_ggng[] = $value->bookmaker_id; + break; + case 5: + self::$odds_tq[] = $value->bookmaker_id; + break; + case 8: + case 14: + self::$odds_handicaps[] = $value->bookmaker_id; + break; + case 9: + self::$odds_handicaps_q1[] = $value->bookmaker_id; + break; + case 7: + case 12: + case 16: + self::$odds_2ways[] = $value->bookmaker_id; + break; + case 17: + self::$odds_2ways_s1[] = $value->bookmaker_id; + break; + case 6: + case 10: + case 15: + case 18: + self::$odds_totals[] = $value->bookmaker_id; + break; + case 11: + self::$odds_totals_q1[] = $value->bookmaker_id; + break; + } + + } + } + public static function load_bettype_settings() { self::$bettype_settings = BettypeUser::where('user_id', auth()->user()->id)->where('sport_id', auth()->user()->sport_id)->where('is_active', 1)->pluck('multiline_setting_id')->toArray(); @@ -40,6 +117,8 @@ class EventsTable { self::load_bettype_settings(); + self::load_multiline_bookmaker(); + return $table ->striped() ->columns([ @@ -49,55 +128,86 @@ class EventsTable ->color('primary') ->width(30) ->icon(fn ($record) => $record->sport->icon) - ->tooltip(fn ($record) => $record->id), - // MatchIds::make('match_ids') - // ->grow(false), - TextColumn::make('id')->hidden()->searchable(), - TextColumn::make('betsapi_id')->hidden()->searchable(), - TextColumn::make('betradar_id')->hidden()->searchable(), - TextColumn::make('bet365_id')->hidden()->searchable(), - TextColumn::make('bwin_id')->hidden()->searchable(), + ->tooltip(fn ($record) => $record->id) + ->visibleFrom('lg'), + + // MatchIds::make('match_ids')->grow(false)->visibleFrom('xl'), + TextColumn::make('starttime') ->label('Start') ->alignCenter() ->dateTime('H:i') ->weight(FontWeight::Bold) ->description(fn ($state): string => Carbon::createFromDate($state)->format('d. M')), + TextColumn::make('country.name') - ->width('20rem') ->searchable() + ->wrap() ->color('gray') ->label('Event') ->icon(fn ($record) => $record->country->icon) ->description(fn ($record) => $record->competition_group->name), + TextColumn::make('home.name') ->wrap() ->searchable() ->alignRight(), + TextColumn::make('away.name') ->wrap() ->searchable(), - Column3way::make('odds3way')->visible(fn () => in_array(1, self::$bettype_settings))->width(225)->label('1 X 2'), - ColumnDc::make('oddsdc')->visible(fn () => in_array(2, self::$bettype_settings))->width(225)->label('Double Chance'), - ColumnDnb::make('oddsdnb')->visible(fn () => in_array(3, self::$bettype_settings))->width(190)->label('Draw No Bet'), - ColumnGgng::make('oddsggng')->visible(fn () => in_array(4, self::$bettype_settings))->width(190)->label('Goal No Goal'), - ColumnGgng::make('oddstq')->visible(fn () => in_array(5, self::$bettype_settings))->width(190)->label('To Qualify'), - ColumnTotal::make('oddstotal')->visible(fn () => in_array(6, self::$bettype_settings))->width(225)->label('Totals'), - // Column3way::make('odds3way')->visible(fn () => in_array(1, self::$bettype_settings))->width(225)->label(fn () => view('filament.tables.columns.multiline.labels', ['menu' => '1x2'])), - // ColumnDc::make('oddsdc')->visible(fn () => in_array(2, self::$bettype_settings))->width(225)->label(fn () => view('filament.tables.columns.multiline.labels', ['menu' => 'dc'])), - // ColumnDnb::make('oddsdnb')->visible(fn () => in_array(3, self::$bettype_settings))->width(190)->label(fn () => view('filament.tables.columns.multiline.labels', ['menu' => 'dnb'])), - // ColumnGgng::make('oddsggng')->visible(fn () => in_array(4, self::$bettype_settings))->width(190)->label(fn () => view('filament.tables.columns.multiline.labels', ['menu' => 'gg'])), - // ColumnGgng::make('oddstq')->visible(fn () => in_array(5, self::$bettype_settings))->width(190)->label(fn () => view('filament.tables.columns.multiline.labels', ['menu' => 'tq'])), - // ColumnTotal::make('oddstotal')->visible(fn () => in_array(6, self::$bettype_settings))->width(225)->label(fn () => view('filament.tables.columns.multiline.labels', ['menu' => 'ou'])), - ]) + Column3way::make('odds3way')->bookmakers(self::$odds_3ways) + ->visible(fn () => in_array(1, self::$bettype_settings)) + ->verticalAlignment(VerticalAlignment::Start) + ->width(225) + ->label('1 X 2') + ->extraCellAttributes(['class' => 'border-l border-gray-200 dark:border-gray-800']), + + ColumnDc::make('oddsdc')->bookmakers(self::$odds_dc) + ->visible(fn () => in_array(2, self::$bettype_settings)) + ->verticalAlignment(VerticalAlignment::Start) + ->width(225) + ->label('Double Chance') + ->extraCellAttributes(['class' => 'border-l border-gray-200 dark:border-gray-800']), + + ColumnDnb::make('oddsdnb')->bookmakers(self::$odds_dnb) + ->visible(fn () => in_array(3, self::$bettype_settings)) + ->verticalAlignment(VerticalAlignment::Start) + ->width(190) + ->label('Draw No Bet') + ->extraCellAttributes(['class' => 'border-l border-gray-200 dark:border-gray-800']), + + ColumnGgng::make('oddsggng')->bookmakers(self::$odds_ggng) + ->visible(fn () => in_array(4, self::$bettype_settings)) + ->verticalAlignment(VerticalAlignment::Start) + ->width(190) + ->label('Goal No Goal') + ->extraCellAttributes(['class' => 'border-l border-gray-200 dark:border-gray-800']), + + ColumnTq::make('oddstq')->bookmakers(self::$odds_tq) + ->visible(fn () => in_array(5, self::$bettype_settings)) + ->verticalAlignment(VerticalAlignment::Start) + ->width(190) + ->label('To Qualify') + ->extraCellAttributes(['class' => 'border-l border-gray-200 dark:border-gray-800']), + + ColumnTotal::make('oddstotal')->bookmakers(self::$odds_totals) + ->visible(fn () => in_array(6, self::$bettype_settings)) + ->verticalAlignment(VerticalAlignment::Start) + ->width(225) + ->label('Totals') + ->extraCellAttributes(['class' => 'border-l border-gray-200 dark:border-gray-800']), + ])->searchable(['id', 'betsapi_id', 'betradar_id', 'bet365_id', 'bwin_id']) ->poll('10s') ->defaultSort('starttime', 'ASC') ->filters([ Filter::make('events') ->schema([ Select::make('country_id') - ->label('Country') + ->placeholder('Country') + ->hiddenLabel() + ->label('') ->searchable() ->preload() ->options(function () { @@ -111,7 +221,9 @@ class EventsTable $set('competition_group_id', null); }), Select::make('competition_group_id') - ->label('League') + ->placeholder('League') + ->hiddenLabel() + ->label('') ->multiple() ->columnSpan(2) ->searchable() @@ -149,10 +261,10 @@ class EventsTable ->recordActions([ ActionGroup::make([ // EditAction::make(), - // DeleteAction::make(), + DeleteAction::make()->visible(fn () => auth()->user()->hasRole('super_admin')), Action::make('txodds_odds') ->label('TxOdds') - ->disabled(fn () => ! auth()->user()->super_admin) + ->disabled(fn () => ! auth()->user()->hasRole('super_admin')) ->icon('heroicon-o-arrow-top-right-on-square') ->url(fn ($record): string => route('txodds_odds', ['sportid' => $record->sport_id, 'txoddsid' => $record->id])) ->openUrlInNewTab(), diff --git a/app/Filament/Resources/GroupFilters/GroupFilterResource.php b/app/Filament/Resources/GroupFilters/GroupFilterResource.php index bf98cd79..d87096a6 100644 --- a/app/Filament/Resources/GroupFilters/GroupFilterResource.php +++ b/app/Filament/Resources/GroupFilters/GroupFilterResource.php @@ -33,11 +33,6 @@ class GroupFilterResource extends Resource protected static ?string $pluralModelLabel = 'Groups'; - public static function canViewAny(): bool - { - return auth()->user()->super_admin == 1; - } - public static function form(Schema $schema): Schema { return GroupFilterForm::configure($schema); diff --git a/app/Filament/Resources/MultilineClients/Pages/ListMultilineClients.php b/app/Filament/Resources/MultilineClients/Pages/ListMultilineClients.php index 7a3c9d04..bc5b1f0a 100644 --- a/app/Filament/Resources/MultilineClients/Pages/ListMultilineClients.php +++ b/app/Filament/Resources/MultilineClients/Pages/ListMultilineClients.php @@ -13,9 +13,7 @@ class ListMultilineClients extends ListRecords protected function getHeaderActions(): array { return [ - // CreateAction::make() - // ->createAnother(false) - // ->hidden(! auth()->user()->super_admin), + // CreateAction::make()->createAnother(false), ]; } } diff --git a/app/Filament/Resources/Providers/ProviderResource.php b/app/Filament/Resources/Providers/ProviderResource.php index 66d4c39f..d822d8fe 100644 --- a/app/Filament/Resources/Providers/ProviderResource.php +++ b/app/Filament/Resources/Providers/ProviderResource.php @@ -27,11 +27,6 @@ class ProviderResource extends Resource protected static string|UnitEnum|null $navigationGroup = 'Grapper'; - public static function canViewAny(): bool - { - return auth()->user()->super_admin == 1; - } - public static function form(Schema $schema): Schema { return ProviderForm::configure($schema); diff --git a/app/Filament/Resources/Sports/SportResource.php b/app/Filament/Resources/Sports/SportResource.php index 55608c9e..fe9f5439 100644 --- a/app/Filament/Resources/Sports/SportResource.php +++ b/app/Filament/Resources/Sports/SportResource.php @@ -28,11 +28,6 @@ class SportResource extends Resource protected static string|\UnitEnum|null $navigationGroup = 'Static Data'; - public static function canViewAny(): bool - { - return auth()->user()->super_admin == 1; - } - public static function form(Schema $schema): Schema { return $schema diff --git a/app/Filament/Resources/Supervisors/SupervisorResource.php b/app/Filament/Resources/Supervisors/SupervisorResource.php index 536f9214..3f691190 100644 --- a/app/Filament/Resources/Supervisors/SupervisorResource.php +++ b/app/Filament/Resources/Supervisors/SupervisorResource.php @@ -23,11 +23,6 @@ class SupervisorResource extends Resource protected static string|\UnitEnum|null $navigationGroup = 'Grapper'; - public static function canViewAny(): bool - { - return auth()->user()->super_admin == 1; - } - public static function form(Schema $schema): Schema { return SupervisorForm::configure($schema); diff --git a/app/Filament/Resources/Users/Schemas/UserForm.php b/app/Filament/Resources/Users/Schemas/UserForm.php index 76b82fee..ec172ae4 100644 --- a/app/Filament/Resources/Users/Schemas/UserForm.php +++ b/app/Filament/Resources/Users/Schemas/UserForm.php @@ -2,7 +2,8 @@ namespace App\Filament\Resources\Users\Schemas; -use Filament\Forms\Components\DateTimePicker; +use App\Models\Bookmaker; +use App\Models\Client; use Filament\Forms\Components\Select; use Filament\Forms\Components\TextInput; use Filament\Schemas\Schema; @@ -19,14 +20,22 @@ class UserForm ->label('Email address') ->email() ->required(), - // DateTimePicker::make('email_verified_at'), - // TextInput::make('password') - // ->password() - // ->required(), Select::make('client_id') ->relationship('client', 'name') ->preload(), + Select::make('bookmaker_id') + ->label('Active Bookmaker') + ->options(function () { + $bookmaker = Client::where('id', auth()->user()->client_id)->value('bookmaker'); + + return Bookmaker::query()->whereIn('id', $bookmaker)->pluck('name', 'id'); + }) + ->preload(), Select::make('roles') + ->label('Rollen') + ->prefixIcon('phosphor-gear') + ->aboveContent('Only visible for super_admin') + ->visible(fn () => auth()->user()->hasRole('super_admin')) ->relationship('roles', 'name') ->multiple() ->preload() diff --git a/app/Filament/Resources/Users/Tables/UsersTable.php b/app/Filament/Resources/Users/Tables/UsersTable.php index 53151027..59a1ab23 100644 --- a/app/Filament/Resources/Users/Tables/UsersTable.php +++ b/app/Filament/Resources/Users/Tables/UsersTable.php @@ -20,6 +20,8 @@ class UsersTable ->label('Email address') ->searchable(), TextColumn::make('client.name')->badge(), + TextColumn::make('roles.name')->badge()->color('success')->wrap(), + TextColumn::make('bookmaker.name')->badge()->color('warning'), TextColumn::make('created_at') ->dateTime() ->sortable() diff --git a/app/Filament/Resources/Users/UserResource.php b/app/Filament/Resources/Users/UserResource.php index 1f58d634..29ad9bd2 100644 --- a/app/Filament/Resources/Users/UserResource.php +++ b/app/Filament/Resources/Users/UserResource.php @@ -25,11 +25,6 @@ class UserResource extends Resource protected static string|\UnitEnum|null $navigationGroup = 'User'; - public static function canViewAny(): bool - { - return auth()->user()->super_admin == 1; - } - public static function form(Schema $schema): Schema { return UserForm::configure($schema); diff --git a/app/Filament/Tables/Columns/Column3way.php b/app/Filament/Tables/Columns/Column3way.php index cc6b8e3b..a205ff68 100644 --- a/app/Filament/Tables/Columns/Column3way.php +++ b/app/Filament/Tables/Columns/Column3way.php @@ -9,46 +9,38 @@ class Column3way extends Column { protected string $view = 'filament.tables.columns.column_3way'; - protected ?array $multiline; + protected $data; - protected ?array $fumetto; + protected ?array $bookmakers; - protected ?bool $has_odds; + protected ?bool $has_data; - public function loadData($record, $multiline_client) + public function bookmakers(?array $bookmakers): static { + $this->bookmakers = $bookmakers; - $res = Odds3way::with('bookmaker')->where('event_id', $record->id)->get(); - - $this->fumetto = []; - - $this->multiline = []; - - foreach ($res as $item) { - - $this->fumetto[] = $item; - foreach ($multiline_client as $slot => $bookmaker) { - if ($item->bookmaker_id == $bookmaker->bookmaker_id) { - $this->multiline[$slot] = $item; - } - } - } - - $this->has_odds = count($this->multiline) ? true : false; + return $this; } - public function getData(): ?array + public function getBookmaker(): ?array { - return $this->multiline; + return $this->bookmakers; } - public function getFumetto() + public function loadData($record) { - return $this->fumetto; + $this->data = Odds3way::with('bookmaker')->where('event_id', $record->id)->get(); + + $this->has_data = count($this->data) ? true : false; } - public function hasodds() + public function getData() { - return $this->has_odds; + return $this->data; + } + + public function hasData() + { + return $this->has_data; } } diff --git a/app/Filament/Tables/Columns/ColumnDc.php b/app/Filament/Tables/Columns/ColumnDc.php index cbc21e10..000817ec 100644 --- a/app/Filament/Tables/Columns/ColumnDc.php +++ b/app/Filament/Tables/Columns/ColumnDc.php @@ -9,46 +9,38 @@ class ColumnDc extends Column { protected string $view = 'filament.tables.columns.column-dc'; - protected ?array $multiline; + protected $data; - protected ?array $fumetto; + protected ?array $bookmakers; - protected ?bool $has_odds; + protected ?bool $has_data; - public function loadData($record, $multiline_client) + public function bookmakers(?array $bookmakers): static { + $this->bookmakers = $bookmakers; - $res = OddsDc::with('bookmaker')->where('event_id', $record->id)->get(); - - $this->fumetto = []; - - $this->multiline = []; - - foreach ($res as $item) { - - $this->fumetto[] = $item; - foreach ($multiline_client as $slot => $bookmaker) { - if ($item->bookmaker_id == $bookmaker->bookmaker_id) { - $this->multiline[$slot] = $item; - } - } - } - - $this->has_odds = count($this->multiline) ? true : false; + return $this; } - public function getData(): ?array + public function getBookmaker(): ?array { - return $this->multiline; + return $this->bookmakers; } - public function getFumetto() + public function loadData($record) { - return $this->fumetto; + $this->data = OddsDc::with('bookmaker')->where('event_id', $record->id)->get(); + + $this->has_data = count($this->data) ? true : false; } - public function hasodds() + public function getData() { - return $this->has_odds; + return $this->data; + } + + public function hasData() + { + return $this->has_data; } } diff --git a/app/Filament/Tables/Columns/ColumnDnb.php b/app/Filament/Tables/Columns/ColumnDnb.php index 89c68fa4..4b149694 100644 --- a/app/Filament/Tables/Columns/ColumnDnb.php +++ b/app/Filament/Tables/Columns/ColumnDnb.php @@ -9,46 +9,38 @@ class ColumnDnb extends Column { protected string $view = 'filament.tables.columns.column-dnb'; - protected ?array $multiline; + protected $data; - protected ?array $fumetto; + protected ?array $bookmakers; - protected ?bool $has_odds; + protected ?bool $has_data; - public function loadData($record, $multiline_client) + public function bookmakers(?array $bookmakers): static { + $this->bookmakers = $bookmakers; - $res = OddsDnb::with('bookmaker')->where('event_id', $record->id)->get(); - - $this->fumetto = []; - - $this->multiline = []; - - foreach ($res as $item) { - - $this->fumetto[] = $item; - foreach ($multiline_client as $slot => $bookmaker) { - if ($item->bookmaker_id == $bookmaker->bookmaker_id) { - $this->multiline[$slot] = $item; - } - } - } - - $this->has_odds = count($this->multiline) ? true : false; + return $this; } - public function getData(): ?array + public function getBookmaker(): ?array { - return $this->multiline; + return $this->bookmakers; } - public function getFumetto() + public function loadData($record) { - return $this->fumetto; + $this->data = OddsDnb::with('bookmaker')->where('event_id', $record->id)->get(); + + $this->has_data = count($this->data) ? true : false; } - public function hasodds() + public function getData() { - return $this->has_odds; + return $this->data; + } + + public function hasData() + { + return $this->has_data; } } diff --git a/app/Filament/Tables/Columns/ColumnGgng.php b/app/Filament/Tables/Columns/ColumnGgng.php index 932df8a4..9f6edf4a 100644 --- a/app/Filament/Tables/Columns/ColumnGgng.php +++ b/app/Filament/Tables/Columns/ColumnGgng.php @@ -9,46 +9,38 @@ class ColumnGgng extends Column { protected string $view = 'filament.tables.columns.column-ggng'; - protected ?array $multiline; + protected $data; - protected ?array $fumetto; + protected ?array $bookmakers; - protected ?bool $has_odds; + protected ?bool $has_data; - public function loadData($record, $multiline_client) + public function bookmakers(?array $bookmakers): static { + $this->bookmakers = $bookmakers; - $res = OddsGgng::with('bookmaker')->where('event_id', $record->id)->get(); - - $this->fumetto = []; - - $this->multiline = []; - - foreach ($res as $item) { - - $this->fumetto[] = $item; - foreach ($multiline_client as $slot => $bookmaker) { - if ($item->bookmaker_id == $bookmaker->bookmaker_id) { - $this->multiline[$slot] = $item; - } - } - } - - $this->has_odds = count($this->multiline) ? true : false; + return $this; } - public function getData(): ?array + public function getBookmaker(): ?array { - return $this->multiline; + return $this->bookmakers; } - public function getFumetto() + public function loadData($record) { - return $this->fumetto; + $this->data = OddsGgng::with('bookmaker')->where('event_id', $record->id)->get(); + + $this->has_data = count($this->data) ? true : false; } - public function hasodds() + public function getData() { - return $this->has_odds; + return $this->data; + } + + public function hasData() + { + return $this->has_data; } } diff --git a/app/Filament/Tables/Columns/ColumnTotal.php b/app/Filament/Tables/Columns/ColumnTotal.php index c1f0e0fb..fe96d389 100644 --- a/app/Filament/Tables/Columns/ColumnTotal.php +++ b/app/Filament/Tables/Columns/ColumnTotal.php @@ -9,46 +9,38 @@ class ColumnTotal extends Column { protected string $view = 'filament.tables.columns.column-total'; - protected ?array $multiline; + protected $data; - protected ?array $fumetto; + protected ?array $bookmakers; - protected ?bool $has_odds; + protected ?bool $has_data; - public function loadData($record, $multiline_client) + public function bookmakers(?array $bookmakers): static { + $this->bookmakers = $bookmakers; - $res = OddsTotal::with('bookmaker')->where('event_id', $record->id)->get(); - - $this->fumetto = []; - - $this->multiline = []; - - foreach ($res as $item) { - - $this->fumetto[] = $item; - foreach ($multiline_client as $slot => $bookmaker) { - if ($item->bookmaker_id == $bookmaker->bookmaker_id) { - $this->multiline[$slot] = $item; - } - } - } - - $this->has_odds = count($this->multiline) ? true : false; + return $this; } - public function getData(): ?array + public function getBookmaker(): ?array { - return $this->multiline; + return $this->bookmakers; } - public function getFumetto() + public function loadData($record) { - return $this->fumetto; + $this->data = OddsTotal::with('bookmaker')->where('event_id', $record->id)->get(); + + $this->has_data = count($this->data) ? true : false; } - public function hasodds() + public function getData() { - return $this->has_odds; + return $this->data; + } + + public function hasData() + { + return $this->has_data; } } diff --git a/app/Filament/Tables/Columns/ColumnTq.php b/app/Filament/Tables/Columns/ColumnTq.php index c1217e9f..d3ad1869 100644 --- a/app/Filament/Tables/Columns/ColumnTq.php +++ b/app/Filament/Tables/Columns/ColumnTq.php @@ -9,46 +9,38 @@ class ColumnTq extends Column { protected string $view = 'filament.tables.columns.column-tq'; - protected ?array $multiline; + protected $data; - protected ?array $fumetto; + protected ?array $bookmakers; - protected ?bool $has_odds; + protected ?bool $has_data; - public function loadData($record, $multiline_client) + public function bookmakers(?array $bookmakers): static { + $this->bookmakers = $bookmakers; - $res = OddsQualify::with('bookmaker')->where('event_id', $record->id)->get(); - - $this->fumetto = []; - - $this->multiline = []; - - foreach ($res as $item) { - - $this->fumetto[] = $item; - foreach ($multiline_client as $slot => $bookmaker) { - if ($item->bookmaker_id == $bookmaker->bookmaker_id) { - $this->multiline[$slot] = $item; - } - } - } - - $this->has_odds = count($this->multiline) ? true : false; + return $this; } - public function getData(): ?array + public function getBookmaker(): ?array { - return $this->multiline; + return $this->bookmakers; } - public function getFumetto() + public function loadData($record) { - return $this->fumetto; + $this->data = OddsQualify::with('bookmaker')->where('event_id', $record->id)->get(); + + $this->has_data = count($this->data) ? true : false; } - public function hasodds() + public function getData() { - return $this->has_odds; + return $this->data; + } + + public function hasData() + { + return $this->has_data; } } diff --git a/app/Livewire/MapperClients.php b/app/Livewire/MapperClients.php deleted file mode 100644 index d114e927..00000000 --- a/app/Livewire/MapperClients.php +++ /dev/null @@ -1,55 +0,0 @@ -query(fn (): Builder => Mapper::query()) - ->columns([ - TextColumn::make('home')->searchable()->alignRight()->size(TextSize::ExtraSmall), - TextColumn::make('away')->searchable()->size(TextSize::ExtraSmall), - TextColumn::make('group')->searchable()->wrap()->size(TextSize::ExtraSmall)->label('League'), - ]) - ->filters([ - // - ]) - ->headerActions([ - // - ]) - ->recordActions([ - // - ]) - ->toolbarActions([ - BulkActionGroup::make([ - // - ]), - ]); - } - - public function render(): View - { - return view('livewire.mapper-clients'); - } -} diff --git a/app/Livewire/MapperEvents.php b/app/Livewire/MapperEvents.php deleted file mode 100644 index 5e50bf2b..00000000 --- a/app/Livewire/MapperEvents.php +++ /dev/null @@ -1,62 +0,0 @@ -query(fn (): Builder => Event::query()) - ->columns([ - // TextColumn::make('country.name') - // ->color('gray') - // ->label('Event') - // ->icon(fn ($record) => $record->country->icon) - // ->description(fn ($record) => $record->competition_group->name) - // ->wrap(), - TextColumn::make('competition_group.name')->searchable()->wrap()->size(TextSize::ExtraSmall)->label('League'), - TextColumn::make('home.name')->searchable()->alignRight()->size(TextSize::ExtraSmall), - TextColumn::make('away.name')->searchable()->size(TextSize::ExtraSmall), - - ]) - ->filters([ - // - ]) - ->headerActions([ - // - ]) - ->recordActions([ - // - ]) - ->toolbarActions([ - BulkActionGroup::make([ - // - ]), - ]); - } - - public function render(): View - { - return view('livewire.mapper-events'); - } -} diff --git a/app/Livewire/MapperLeft.php b/app/Livewire/MapperLeft.php new file mode 100644 index 00000000..08ff880b --- /dev/null +++ b/app/Livewire/MapperLeft.php @@ -0,0 +1,44 @@ +query(Event::query()) + ->columns([ + TextColumn::make('name'), + ]) + ->filters([ + // ... + ]) + ->recordActions([ + // ... + ]) + ->toolbarActions([ + // ... + ]); + } + + public function render() + { + return view('livewire.mapper-left'); + } +} diff --git a/app/Livewire/MapperRight.php b/app/Livewire/MapperRight.php new file mode 100644 index 00000000..eff712d1 --- /dev/null +++ b/app/Livewire/MapperRight.php @@ -0,0 +1,44 @@ +query(Mapper::query()) + ->columns([ + TextColumn::make('name'), + ]) + ->filters([ + // ... + ]) + ->recordActions([ + // ... + ]) + ->toolbarActions([ + // ... + ]); + } + + public function render() + { + return view('livewire.mapper-left'); + } +} diff --git a/app/Livewire/TopMenuItems.php b/app/Livewire/TopMenuItems.php index 640c1526..548626c0 100644 --- a/app/Livewire/TopMenuItems.php +++ b/app/Livewire/TopMenuItems.php @@ -50,7 +50,11 @@ class TopMenuItems extends Component public function swop($client_id) { - User::where('id', auth()->user()->id)->update(['client_id' => $client_id]); + $bookmakers = Client::where('id', $client_id)->pluck('bookmaker')->first(); + + $bookmaker_id = array_shift($bookmakers); + + User::where('id', auth()->user()->id)->update(['bookmaker_id' => $bookmaker_id, 'client_id' => $client_id]); $this->js('window.location.reload()'); } diff --git a/app/Models/Event.php b/app/Models/Event.php index e096b02f..79065f17 100644 --- a/app/Models/Event.php +++ b/app/Models/Event.php @@ -52,9 +52,4 @@ class Event extends Model { return $this->hasOne(Element::class, 'id', 'away_id'); } - - public function multiline_client() - { - return $this->hasMany(MultilineClient::class, 'sport_id', 'sport_id')->with('bookmaker')->where('multiline_setting_id', auth()->user()->multiline_setting_id)->orderBy('sort'); - } } diff --git a/app/Models/User.php b/app/Models/User.php index 9aeff8ea..fe61e116 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -13,11 +13,12 @@ use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Spatie\Permission\Traits\HasRoles; class User extends Authenticatable implements FilamentUser, HasAppAuthentication, HasAppAuthenticationRecovery, HasEmailAuthentication, MustVerifyEmail { /** @use HasFactory<\Database\Factories\UserFactory> */ - use HasFactory, Notifiable; + use HasFactory, HasRoles, Notifiable; protected $casts = [ 'bettype_status' => 'json', @@ -36,7 +37,9 @@ class User extends Authenticatable implements FilamentUser, HasAppAuthentication 'agb_akzeptiert', 'app_verified_at', 'bettype_status', + 'client_id', 'sport_id', + 'bookmaker_id', ]; protected static function newFactory(): Factory @@ -152,6 +155,11 @@ class User extends Authenticatable implements FilamentUser, HasAppAuthentication return $this->belongsTo(Client::class); } + public function bookmaker() + { + return $this->belongsTo(Bookmaker::class); + } + public function bettype_user() { return $this->hasMany(BettypeUser::class)->where('sport_id', auth()->user()->sport_id); diff --git a/app/Policies/BettypePolicy.php b/app/Policies/BettypePolicy.php new file mode 100644 index 00000000..b3dfa74f --- /dev/null +++ b/app/Policies/BettypePolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:Bettype'); + } + + public function view(AuthUser $authUser, Bettype $bettype): bool + { + return $authUser->can('View:Bettype'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:Bettype'); + } + + public function update(AuthUser $authUser, Bettype $bettype): bool + { + return $authUser->can('Update:Bettype'); + } + + public function delete(AuthUser $authUser, Bettype $bettype): bool + { + return $authUser->can('Delete:Bettype'); + } + + public function restore(AuthUser $authUser, Bettype $bettype): bool + { + return $authUser->can('Restore:Bettype'); + } + + public function forceDelete(AuthUser $authUser, Bettype $bettype): bool + { + return $authUser->can('ForceDelete:Bettype'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:Bettype'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:Bettype'); + } + + public function replicate(AuthUser $authUser, Bettype $bettype): bool + { + return $authUser->can('Replicate:Bettype'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:Bettype'); + } + +} \ No newline at end of file diff --git a/app/Policies/BookmakerPolicy.php b/app/Policies/BookmakerPolicy.php new file mode 100644 index 00000000..affb3d31 --- /dev/null +++ b/app/Policies/BookmakerPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:Bookmaker'); + } + + public function view(AuthUser $authUser, Bookmaker $bookmaker): bool + { + return $authUser->can('View:Bookmaker'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:Bookmaker'); + } + + public function update(AuthUser $authUser, Bookmaker $bookmaker): bool + { + return $authUser->can('Update:Bookmaker'); + } + + public function delete(AuthUser $authUser, Bookmaker $bookmaker): bool + { + return $authUser->can('Delete:Bookmaker'); + } + + public function restore(AuthUser $authUser, Bookmaker $bookmaker): bool + { + return $authUser->can('Restore:Bookmaker'); + } + + public function forceDelete(AuthUser $authUser, Bookmaker $bookmaker): bool + { + return $authUser->can('ForceDelete:Bookmaker'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:Bookmaker'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:Bookmaker'); + } + + public function replicate(AuthUser $authUser, Bookmaker $bookmaker): bool + { + return $authUser->can('Replicate:Bookmaker'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:Bookmaker'); + } + +} \ No newline at end of file diff --git a/app/Policies/ClientPolicy.php b/app/Policies/ClientPolicy.php new file mode 100644 index 00000000..2c4879c9 --- /dev/null +++ b/app/Policies/ClientPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:Client'); + } + + public function view(AuthUser $authUser, Client $client): bool + { + return $authUser->can('View:Client'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:Client'); + } + + public function update(AuthUser $authUser, Client $client): bool + { + return $authUser->can('Update:Client'); + } + + public function delete(AuthUser $authUser, Client $client): bool + { + return $authUser->can('Delete:Client'); + } + + public function restore(AuthUser $authUser, Client $client): bool + { + return $authUser->can('Restore:Client'); + } + + public function forceDelete(AuthUser $authUser, Client $client): bool + { + return $authUser->can('ForceDelete:Client'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:Client'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:Client'); + } + + public function replicate(AuthUser $authUser, Client $client): bool + { + return $authUser->can('Replicate:Client'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:Client'); + } + +} \ No newline at end of file diff --git a/app/Policies/CompetitionGroupFilterPolicy.php b/app/Policies/CompetitionGroupFilterPolicy.php new file mode 100644 index 00000000..60e56bfc --- /dev/null +++ b/app/Policies/CompetitionGroupFilterPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:CompetitionGroupFilter'); + } + + public function view(AuthUser $authUser, CompetitionGroupFilter $competitionGroupFilter): bool + { + return $authUser->can('View:CompetitionGroupFilter'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:CompetitionGroupFilter'); + } + + public function update(AuthUser $authUser, CompetitionGroupFilter $competitionGroupFilter): bool + { + return $authUser->can('Update:CompetitionGroupFilter'); + } + + public function delete(AuthUser $authUser, CompetitionGroupFilter $competitionGroupFilter): bool + { + return $authUser->can('Delete:CompetitionGroupFilter'); + } + + public function restore(AuthUser $authUser, CompetitionGroupFilter $competitionGroupFilter): bool + { + return $authUser->can('Restore:CompetitionGroupFilter'); + } + + public function forceDelete(AuthUser $authUser, CompetitionGroupFilter $competitionGroupFilter): bool + { + return $authUser->can('ForceDelete:CompetitionGroupFilter'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:CompetitionGroupFilter'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:CompetitionGroupFilter'); + } + + public function replicate(AuthUser $authUser, CompetitionGroupFilter $competitionGroupFilter): bool + { + return $authUser->can('Replicate:CompetitionGroupFilter'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:CompetitionGroupFilter'); + } + +} \ No newline at end of file diff --git a/app/Policies/CountryPolicy.php b/app/Policies/CountryPolicy.php new file mode 100644 index 00000000..d48a280f --- /dev/null +++ b/app/Policies/CountryPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:Country'); + } + + public function view(AuthUser $authUser, Country $country): bool + { + return $authUser->can('View:Country'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:Country'); + } + + public function update(AuthUser $authUser, Country $country): bool + { + return $authUser->can('Update:Country'); + } + + public function delete(AuthUser $authUser, Country $country): bool + { + return $authUser->can('Delete:Country'); + } + + public function restore(AuthUser $authUser, Country $country): bool + { + return $authUser->can('Restore:Country'); + } + + public function forceDelete(AuthUser $authUser, Country $country): bool + { + return $authUser->can('ForceDelete:Country'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:Country'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:Country'); + } + + public function replicate(AuthUser $authUser, Country $country): bool + { + return $authUser->can('Replicate:Country'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:Country'); + } + +} \ No newline at end of file diff --git a/app/Policies/ElementFilterPolicy.php b/app/Policies/ElementFilterPolicy.php new file mode 100644 index 00000000..ee556bce --- /dev/null +++ b/app/Policies/ElementFilterPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:ElementFilter'); + } + + public function view(AuthUser $authUser, ElementFilter $elementFilter): bool + { + return $authUser->can('View:ElementFilter'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:ElementFilter'); + } + + public function update(AuthUser $authUser, ElementFilter $elementFilter): bool + { + return $authUser->can('Update:ElementFilter'); + } + + public function delete(AuthUser $authUser, ElementFilter $elementFilter): bool + { + return $authUser->can('Delete:ElementFilter'); + } + + public function restore(AuthUser $authUser, ElementFilter $elementFilter): bool + { + return $authUser->can('Restore:ElementFilter'); + } + + public function forceDelete(AuthUser $authUser, ElementFilter $elementFilter): bool + { + return $authUser->can('ForceDelete:ElementFilter'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:ElementFilter'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:ElementFilter'); + } + + public function replicate(AuthUser $authUser, ElementFilter $elementFilter): bool + { + return $authUser->can('Replicate:ElementFilter'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:ElementFilter'); + } + +} \ No newline at end of file diff --git a/app/Policies/EventPolicy.php b/app/Policies/EventPolicy.php new file mode 100644 index 00000000..1d7e5b2e --- /dev/null +++ b/app/Policies/EventPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:Event'); + } + + public function view(AuthUser $authUser, Event $event): bool + { + return $authUser->can('View:Event'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:Event'); + } + + public function update(AuthUser $authUser, Event $event): bool + { + return $authUser->can('Update:Event'); + } + + public function delete(AuthUser $authUser, Event $event): bool + { + return $authUser->can('Delete:Event'); + } + + public function restore(AuthUser $authUser, Event $event): bool + { + return $authUser->can('Restore:Event'); + } + + public function forceDelete(AuthUser $authUser, Event $event): bool + { + return $authUser->can('ForceDelete:Event'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:Event'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:Event'); + } + + public function replicate(AuthUser $authUser, Event $event): bool + { + return $authUser->can('Replicate:Event'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:Event'); + } + +} \ No newline at end of file diff --git a/app/Policies/GroupFilterPolicy.php b/app/Policies/GroupFilterPolicy.php new file mode 100644 index 00000000..a30f4cb0 --- /dev/null +++ b/app/Policies/GroupFilterPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:GroupFilter'); + } + + public function view(AuthUser $authUser, GroupFilter $groupFilter): bool + { + return $authUser->can('View:GroupFilter'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:GroupFilter'); + } + + public function update(AuthUser $authUser, GroupFilter $groupFilter): bool + { + return $authUser->can('Update:GroupFilter'); + } + + public function delete(AuthUser $authUser, GroupFilter $groupFilter): bool + { + return $authUser->can('Delete:GroupFilter'); + } + + public function restore(AuthUser $authUser, GroupFilter $groupFilter): bool + { + return $authUser->can('Restore:GroupFilter'); + } + + public function forceDelete(AuthUser $authUser, GroupFilter $groupFilter): bool + { + return $authUser->can('ForceDelete:GroupFilter'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:GroupFilter'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:GroupFilter'); + } + + public function replicate(AuthUser $authUser, GroupFilter $groupFilter): bool + { + return $authUser->can('Replicate:GroupFilter'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:GroupFilter'); + } + +} \ No newline at end of file diff --git a/app/Policies/MultilineClientPolicy.php b/app/Policies/MultilineClientPolicy.php new file mode 100644 index 00000000..a1e0ed9a --- /dev/null +++ b/app/Policies/MultilineClientPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:MultilineClient'); + } + + public function view(AuthUser $authUser, MultilineClient $multilineClient): bool + { + return $authUser->can('View:MultilineClient'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:MultilineClient'); + } + + public function update(AuthUser $authUser, MultilineClient $multilineClient): bool + { + return $authUser->can('Update:MultilineClient'); + } + + public function delete(AuthUser $authUser, MultilineClient $multilineClient): bool + { + return $authUser->can('Delete:MultilineClient'); + } + + public function restore(AuthUser $authUser, MultilineClient $multilineClient): bool + { + return $authUser->can('Restore:MultilineClient'); + } + + public function forceDelete(AuthUser $authUser, MultilineClient $multilineClient): bool + { + return $authUser->can('ForceDelete:MultilineClient'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:MultilineClient'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:MultilineClient'); + } + + public function replicate(AuthUser $authUser, MultilineClient $multilineClient): bool + { + return $authUser->can('Replicate:MultilineClient'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:MultilineClient'); + } + +} \ No newline at end of file diff --git a/app/Policies/MultilineSettingPolicy.php b/app/Policies/MultilineSettingPolicy.php new file mode 100644 index 00000000..5b3b076c --- /dev/null +++ b/app/Policies/MultilineSettingPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:MultilineSetting'); + } + + public function view(AuthUser $authUser, MultilineSetting $multilineSetting): bool + { + return $authUser->can('View:MultilineSetting'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:MultilineSetting'); + } + + public function update(AuthUser $authUser, MultilineSetting $multilineSetting): bool + { + return $authUser->can('Update:MultilineSetting'); + } + + public function delete(AuthUser $authUser, MultilineSetting $multilineSetting): bool + { + return $authUser->can('Delete:MultilineSetting'); + } + + public function restore(AuthUser $authUser, MultilineSetting $multilineSetting): bool + { + return $authUser->can('Restore:MultilineSetting'); + } + + public function forceDelete(AuthUser $authUser, MultilineSetting $multilineSetting): bool + { + return $authUser->can('ForceDelete:MultilineSetting'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:MultilineSetting'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:MultilineSetting'); + } + + public function replicate(AuthUser $authUser, MultilineSetting $multilineSetting): bool + { + return $authUser->can('Replicate:MultilineSetting'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:MultilineSetting'); + } + +} \ No newline at end of file diff --git a/app/Policies/ProviderPolicy.php b/app/Policies/ProviderPolicy.php new file mode 100644 index 00000000..e4be9cf5 --- /dev/null +++ b/app/Policies/ProviderPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:Provider'); + } + + public function view(AuthUser $authUser, Provider $provider): bool + { + return $authUser->can('View:Provider'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:Provider'); + } + + public function update(AuthUser $authUser, Provider $provider): bool + { + return $authUser->can('Update:Provider'); + } + + public function delete(AuthUser $authUser, Provider $provider): bool + { + return $authUser->can('Delete:Provider'); + } + + public function restore(AuthUser $authUser, Provider $provider): bool + { + return $authUser->can('Restore:Provider'); + } + + public function forceDelete(AuthUser $authUser, Provider $provider): bool + { + return $authUser->can('ForceDelete:Provider'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:Provider'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:Provider'); + } + + public function replicate(AuthUser $authUser, Provider $provider): bool + { + return $authUser->can('Replicate:Provider'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:Provider'); + } + +} \ No newline at end of file diff --git a/app/Policies/RolePolicy.php b/app/Policies/RolePolicy.php new file mode 100644 index 00000000..a2a3263e --- /dev/null +++ b/app/Policies/RolePolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:Role'); + } + + public function view(AuthUser $authUser, Role $role): bool + { + return $authUser->can('View:Role'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:Role'); + } + + public function update(AuthUser $authUser, Role $role): bool + { + return $authUser->can('Update:Role'); + } + + public function delete(AuthUser $authUser, Role $role): bool + { + return $authUser->can('Delete:Role'); + } + + public function restore(AuthUser $authUser, Role $role): bool + { + return $authUser->can('Restore:Role'); + } + + public function forceDelete(AuthUser $authUser, Role $role): bool + { + return $authUser->can('ForceDelete:Role'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:Role'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:Role'); + } + + public function replicate(AuthUser $authUser, Role $role): bool + { + return $authUser->can('Replicate:Role'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:Role'); + } + +} \ No newline at end of file diff --git a/app/Policies/SportPolicy.php b/app/Policies/SportPolicy.php new file mode 100644 index 00000000..ee934ef6 --- /dev/null +++ b/app/Policies/SportPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:Sport'); + } + + public function view(AuthUser $authUser, Sport $sport): bool + { + return $authUser->can('View:Sport'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:Sport'); + } + + public function update(AuthUser $authUser, Sport $sport): bool + { + return $authUser->can('Update:Sport'); + } + + public function delete(AuthUser $authUser, Sport $sport): bool + { + return $authUser->can('Delete:Sport'); + } + + public function restore(AuthUser $authUser, Sport $sport): bool + { + return $authUser->can('Restore:Sport'); + } + + public function forceDelete(AuthUser $authUser, Sport $sport): bool + { + return $authUser->can('ForceDelete:Sport'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:Sport'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:Sport'); + } + + public function replicate(AuthUser $authUser, Sport $sport): bool + { + return $authUser->can('Replicate:Sport'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:Sport'); + } + +} \ No newline at end of file diff --git a/app/Policies/SupervisorPolicy.php b/app/Policies/SupervisorPolicy.php new file mode 100644 index 00000000..3098563b --- /dev/null +++ b/app/Policies/SupervisorPolicy.php @@ -0,0 +1,70 @@ +can('ViewAny:Supervisor'); + } + + public function view(AuthUser $authUser, Supervisor $supervisor): bool + { + return $authUser->can('View:Supervisor'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:Supervisor'); + } + + public function update(AuthUser $authUser, Supervisor $supervisor): bool + { + return $authUser->can('Update:Supervisor'); + } + + public function delete(AuthUser $authUser, Supervisor $supervisor): bool + { + return $authUser->can('Delete:Supervisor'); + } + + public function restore(AuthUser $authUser, Supervisor $supervisor): bool + { + return $authUser->can('Restore:Supervisor'); + } + + public function forceDelete(AuthUser $authUser, Supervisor $supervisor): bool + { + return $authUser->can('ForceDelete:Supervisor'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:Supervisor'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:Supervisor'); + } + + public function replicate(AuthUser $authUser, Supervisor $supervisor): bool + { + return $authUser->can('Replicate:Supervisor'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:Supervisor'); + } + +} \ No newline at end of file diff --git a/app/Policies/UserPolicy.php b/app/Policies/UserPolicy.php new file mode 100644 index 00000000..10cdce3e --- /dev/null +++ b/app/Policies/UserPolicy.php @@ -0,0 +1,67 @@ +can('ViewAny:User'); + } + + public function view(AuthUser $authUser): bool + { + return $authUser->can('View:User'); + } + + public function create(AuthUser $authUser): bool + { + return $authUser->can('Create:User'); + } + + public function update(AuthUser $authUser): bool + { + return $authUser->can('Update:User'); + } + + public function delete(AuthUser $authUser): bool + { + return $authUser->can('Delete:User'); + } + + public function restore(AuthUser $authUser): bool + { + return $authUser->can('Restore:User'); + } + + public function forceDelete(AuthUser $authUser): bool + { + return $authUser->can('ForceDelete:User'); + } + + public function forceDeleteAny(AuthUser $authUser): bool + { + return $authUser->can('ForceDeleteAny:User'); + } + + public function restoreAny(AuthUser $authUser): bool + { + return $authUser->can('RestoreAny:User'); + } + + public function replicate(AuthUser $authUser): bool + { + return $authUser->can('Replicate:User'); + } + + public function reorder(AuthUser $authUser): bool + { + return $authUser->can('Reorder:User'); + } + +} \ No newline at end of file diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index c0a421db..1d1e9b04 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -3,6 +3,7 @@ namespace App\Providers\Filament; use Agencetwogether\HooksHelper\HooksHelperPlugin; +use BezhanSalleh\FilamentShield\FilamentShieldPlugin; use Awcodes\LightSwitch\Enums\Alignment; use Awcodes\LightSwitch\LightSwitchPlugin; use Filament\Actions\Action; @@ -67,6 +68,7 @@ class AdminPanelProvider extends PanelProvider // Dashboard::class, ]) ->maxContentWidth(Width::ScreenTwoExtraLarge) + // ->maxContentWidth(Width::Full) ->breadcrumbs(false) ->topNavigation() ->userMenuItems([ @@ -94,6 +96,7 @@ class AdminPanelProvider extends PanelProvider ->authMiddleware([ Authenticate::class, ])->plugins([ + FilamentShieldPlugin::make(), HooksHelperPlugin::make(), FilamentEditProfilePlugin::make()->shouldRegisterNavigation(false), LightSwitchPlugin::make()->position(Alignment::BottomCenter), diff --git a/composer.json b/composer.json index 09e7d3ba..be6a34fd 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "php": "^8.3", "agencetwogether/hookshelper": "^0.0.3", "awcodes/light-switch": "^2.0", + "bezhansalleh/filament-shield": "^4.0", "codeat3/blade-phosphor-icons": "^2.0", "codewithdennis/filament-lucide-icons": "*", "filament/filament": "^4.0", diff --git a/composer.lock b/composer.lock index 788f13df..d1c232c6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c75c2edc61fdc14a9f9af425bf74c8a0", + "content-hash": "f7c5bb72f0348ddbd51bf015f82c3feb", "packages": [ { "name": "agencetwogether/hookshelper", @@ -218,6 +218,178 @@ ], "time": "2025-09-18T15:22:43+00:00" }, + { + "name": "bezhansalleh/filament-plugin-essentials", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/bezhanSalleh/filament-plugin-essentials.git", + "reference": "c6a9df98b798edbb1857b899169dc8125a59b4e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bezhanSalleh/filament-plugin-essentials/zipball/c6a9df98b798edbb1857b899169dc8125a59b4e1", + "reference": "c6a9df98b798edbb1857b899169dc8125a59b4e1", + "shasum": "" + }, + "require": { + "filament/filament": "^4.0", + "illuminate/contracts": "^11.28|^12.0", + "php": "^8.2", + "spatie/laravel-package-tools": "^1.9" + }, + "require-dev": { + "larastan/larastan": "^2.9||^3.0", + "laravel/pint": "^1.14", + "nunomaduro/collision": "^8.1.1||^7.10.0", + "orchestra/testbench": "^10.0.0||^9.0.0", + "pestphp/pest": "^3.0", + "pestphp/pest-plugin-arch": "^3.0", + "pestphp/pest-plugin-laravel": "^3.0", + "pestphp/pest-plugin-type-coverage": "^3.5", + "phpstan/extension-installer": "^1.3||^2.0", + "phpstan/phpstan-deprecation-rules": "^1.1||^2.0", + "phpstan/phpstan-phpunit": "^1.3||^2.0", + "rector/rector": "^2.1", + "spatie/laravel-ray": "^1.40" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "BezhanSalleh\\PluginEssentials\\PluginEssentialsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "BezhanSalleh\\PluginEssentials\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bezhan Salleh", + "email": "bezhan_salleh@yahoo.com", + "role": "Developer" + } + ], + "description": "A collection of essential traits that streamline Filament plugin development by taking care of the boilerplate, so you can focus on shipping real features faster", + "homepage": "https://github.com/bezhansalleh/filament-plugin-essentials", + "keywords": [ + "Bezhan Salleh", + "filament-plugin-essentials", + "laravel" + ], + "support": { + "issues": "https://github.com/bezhanSalleh/filament-plugin-essentials/issues", + "source": "https://github.com/bezhanSalleh/filament-plugin-essentials/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/bezhanSalleh", + "type": "github" + } + ], + "time": "2025-11-30T00:09:48+00:00" + }, + { + "name": "bezhansalleh/filament-shield", + "version": "4.0.3", + "source": { + "type": "git", + "url": "https://github.com/bezhanSalleh/filament-shield.git", + "reference": "e2d33d8e3c5179d6824d26902056c03ee1c8e7d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bezhanSalleh/filament-shield/zipball/e2d33d8e3c5179d6824d26902056c03ee1c8e7d2", + "reference": "e2d33d8e3c5179d6824d26902056c03ee1c8e7d2", + "shasum": "" + }, + "require": { + "bezhansalleh/filament-plugin-essentials": "^1.0", + "filament/filament": "^4.0", + "illuminate/contracts": "^11.28|^12.0", + "illuminate/support": "*", + "php": "^8.2", + "spatie/laravel-package-tools": "^1.9", + "spatie/laravel-permission": "^6.0" + }, + "require-dev": { + "larastan/larastan": "^2.2|^3.0", + "laravel/pint": "^1.0", + "nunomaduro/collision": "^7.0|^8.0", + "orchestra/testbench": "^8.0|^9.0|^10.0", + "pestphp/pest": "^2.0|^3.0", + "pestphp/pest-plugin-laravel": "^2.0|^3.0", + "pestphp/pest-plugin-livewire": "^3.0", + "pestphp/pest-plugin-type-coverage": "^3.5", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.1|^11.0", + "rector/rector": "^2.1", + "spatie/laravel-ray": "^1.40" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "FilamentShield": "BezhanSalleh\\FilamentShield\\Facades\\FilamentShield" + }, + "providers": [ + "BezhanSalleh\\FilamentShield\\FilamentShieldServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "BezhanSalleh\\FilamentShield\\": "src", + "BezhanSalleh\\FilamentShield\\Database\\Factories\\": "database/factories" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bezhan Salleh", + "email": "bezhan_salleh@yahoo.com", + "role": "Developer" + } + ], + "description": "Filament support for `spatie/laravel-permission`.", + "homepage": "https://github.com/bezhansalleh/filament-shield", + "keywords": [ + "acl", + "bezhanSalleh", + "filament", + "filament-shield", + "laravel", + "permission", + "permissions", + "rbac", + "roles", + "security" + ], + "support": { + "issues": "https://github.com/bezhanSalleh/filament-shield/issues", + "source": "https://github.com/bezhanSalleh/filament-shield/tree/4.0.3" + }, + "funding": [ + { + "url": "https://github.com/bezhanSalleh", + "type": "github" + } + ], + "time": "2025-11-29T20:33:45+00:00" + }, { "name": "blade-ui-kit/blade-heroicons", "version": "2.6.0", @@ -5892,6 +6064,89 @@ ], "time": "2025-07-17T15:46:43+00:00" }, + { + "name": "spatie/laravel-permission", + "version": "6.24.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/laravel-permission.git", + "reference": "76adb1fc8d07c16a0721c35c4cc330b7a12598d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/76adb1fc8d07c16a0721c35c4cc330b7a12598d7", + "reference": "76adb1fc8d07c16a0721c35c4cc330b7a12598d7", + "shasum": "" + }, + "require": { + "illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0", + "illuminate/database": "^8.12|^9.0|^10.0|^11.0|^12.0", + "php": "^8.0" + }, + "require-dev": { + "laravel/passport": "^11.0|^12.0", + "laravel/pint": "^1.0", + "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0", + "phpunit/phpunit": "^9.4|^10.1|^11.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Spatie\\Permission\\PermissionServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "6.x-dev", + "dev-master": "6.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Spatie\\Permission\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Freek Van der Herten", + "email": "freek@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + } + ], + "description": "Permission handling for Laravel 8.0 and up", + "homepage": "https://github.com/spatie/laravel-permission", + "keywords": [ + "acl", + "laravel", + "permission", + "permissions", + "rbac", + "roles", + "security", + "spatie" + ], + "support": { + "issues": "https://github.com/spatie/laravel-permission/issues", + "source": "https://github.com/spatie/laravel-permission/tree/6.24.0" + }, + "funding": [ + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2025-12-13T21:45:21+00:00" + }, { "name": "spatie/shiki-php", "version": "2.3.2", diff --git a/config/filament-shield.php b/config/filament-shield.php new file mode 100644 index 00000000..189c29a0 --- /dev/null +++ b/config/filament-shield.php @@ -0,0 +1,263 @@ + [ + 'slug' => 'shield/roles', + 'show_model_path' => true, + 'cluster' => null, + 'tabs' => [ + 'pages' => true, + 'widgets' => true, + 'resources' => true, + 'custom_permissions' => false, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Multi-Tenancy + |-------------------------------------------------------------------------- + | + | When your application supports teams, Shield will automatically detect + | and configure the tenant model during setup. This enables tenant-scoped + | roles and permissions throughout your application. + | + */ + + 'tenant_model' => null, + + /* + |-------------------------------------------------------------------------- + | User Model + |-------------------------------------------------------------------------- + | + | This value contains the class name of your user model. This model will + | be used for role assignments and must implement the HasRoles trait + | provided by the Spatie\Permission package. + | + */ + + 'auth_provider_model' => 'App\\Models\\User', + + /* + |-------------------------------------------------------------------------- + | Super Admin + |-------------------------------------------------------------------------- + | + | Here you may define a super admin that has unrestricted access to your + | application. You can choose to implement this via Laravel's gate system + | or as a traditional role with all permissions explicitly assigned. + | + */ + + 'super_admin' => [ + 'enabled' => true, + 'name' => 'super_admin', + 'define_via_gate' => false, + 'intercept_gate' => 'before', + ], + + /* + |-------------------------------------------------------------------------- + | Panel User + |-------------------------------------------------------------------------- + | + | When enabled, Shield will create a basic panel user role that can be + | assigned to users who should have access to your Filament panels but + | don't need any specific permissions beyond basic authentication. + | + */ + + 'panel_user' => [ + 'enabled' => true, + 'name' => 'panel_user', + ], + + /* + |-------------------------------------------------------------------------- + | Permission Builder + |-------------------------------------------------------------------------- + | + | You can customize how permission keys are generated to match your + | preferred naming convention and organizational standards. Shield uses + | these settings when creating permission names from your resources. + | + | Supported formats: snake, kebab, pascal, camel, upper_snake, lower_snake + | + */ + + 'permissions' => [ + 'separator' => ':', + 'case' => 'pascal', + 'generate' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Policies + |-------------------------------------------------------------------------- + | + | Shield can automatically generate Laravel policies for your resources. + | When merge is enabled, the methods below will be combined with any + | resource-specific methods you define in the resources section. + | + */ + + 'policies' => [ + 'path' => app_path('Policies'), + 'merge' => true, + 'generate' => true, + 'methods' => [ + 'viewAny', 'view', 'create', 'update', 'delete', 'restore', + 'forceDelete', 'forceDeleteAny', 'restoreAny', 'replicate', 'reorder', + ], + 'single_parameter_methods' => [ + 'viewAny', + 'create', + 'deleteAny', + 'forceDeleteAny', + 'restoreAny', + 'reorder', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Localization + |-------------------------------------------------------------------------- + | + | Shield supports multiple languages out of the box. When enabled, you + | can provide translated labels for permissions to create a more + | localized experience for your international users. + | + */ + + 'localization' => [ + 'enabled' => false, + 'key' => 'filament-shield::filament-shield.resource_permission_prefixes_labels', + ], + + /* + |-------------------------------------------------------------------------- + | Resources + |-------------------------------------------------------------------------- + | + | Here you can fine-tune permissions for specific Filament resources. + | Use the 'manage' array to override the default policy methods for + | individual resources, giving you granular control over permissions. + | + */ + + 'resources' => [ + 'subject' => 'model', + 'manage' => [ + \BezhanSalleh\FilamentShield\Resources\Roles\RoleResource::class => [ + 'viewAny', + 'view', + 'create', + 'update', + 'delete', + ], + ], + 'exclude' => [ + // + ], + ], + + /* + |-------------------------------------------------------------------------- + | Pages + |-------------------------------------------------------------------------- + | + | Most Filament pages only require view permissions. Pages listed in the + | exclude array will be skipped during permission generation and won't + | appear in your role management interface. + | + */ + + 'pages' => [ + 'subject' => 'class', + 'prefix' => 'view', + 'exclude' => [ + \Filament\Pages\Dashboard::class, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Widgets + |-------------------------------------------------------------------------- + | + | Like pages, widgets typically only need view permissions. Add widgets + | to the exclude array if you don't want them to appear in your role + | management interface. + | + */ + + 'widgets' => [ + 'subject' => 'class', + 'prefix' => 'view', + 'exclude' => [ + \Filament\Widgets\AccountWidget::class, + \Filament\Widgets\FilamentInfoWidget::class, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Permissions + |-------------------------------------------------------------------------- + | + | Sometimes you need permissions that don't map to resources, pages, or + | widgets. Define any custom permissions here and they'll be available + | when editing roles in your application. + | + */ + + 'custom_permissions' => [], + + /* + |-------------------------------------------------------------------------- + | Entity Discovery + |-------------------------------------------------------------------------- + | + | By default, Shield only looks for entities in your default Filament + | panel. Enable these options if you're using multiple panels and want + | Shield to discover entities across all of them. + | + */ + + 'discovery' => [ + 'discover_all_resources' => false, + 'discover_all_widgets' => false, + 'discover_all_pages' => false, + ], + + /* + |-------------------------------------------------------------------------- + | Role Policy + |-------------------------------------------------------------------------- + | + | Shield can automatically register a policy for role management itself. + | This lets you control who can manage roles using Laravel's built-in + | authorization system. Requires a RolePolicy class in your app. + | + */ + + 'register_role_policy' => true, + +]; diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 7ae88989..c95d0952 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -19,9 +19,8 @@ return new class extends Migration $table->string('password'); $table->integer('client_id'); $table->integer('sport_id')->default(1); - $table->integer('multiline_setting_id')->nullable(); + $table->integer('bookmaker_id')->nullable(); $table->json('bettype_status')->nullable(); - $table->boolean('super_admin')->default(false); $table->text('app_authentication_secret')->nullable(); $table->text('app_authentication_recovery_codes')->nullable(); diff --git a/database/migrations/2025_12_26_090903_create_permission_tables.php b/database/migrations/2025_12_26_090903_create_permission_tables.php new file mode 100644 index 00000000..66ce1f97 --- /dev/null +++ b/database/migrations/2025_12_26_090903_create_permission_tables.php @@ -0,0 +1,134 @@ +engine('InnoDB'); + $table->bigIncrements('id'); // permission id + $table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) + $table->string('guard_name'); // For MyISAM use string('guard_name', 25); + $table->timestamps(); + + $table->unique(['name', 'guard_name']); + }); + + Schema::create($tableNames['roles'], static function (Blueprint $table) use ($teams, $columnNames) { + // $table->engine('InnoDB'); + $table->bigIncrements('id'); // role id + if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing + $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable(); + $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index'); + } + $table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) + $table->string('guard_name'); // For MyISAM use string('guard_name', 25); + $table->timestamps(); + if ($teams || config('permission.testing')) { + $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']); + } else { + $table->unique(['name', 'guard_name']); + } + }); + + Schema::create($tableNames['model_has_permissions'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) { + $table->unsignedBigInteger($pivotPermission); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index'); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } else { + $table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } + + }); + + Schema::create($tableNames['model_has_roles'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) { + $table->unsignedBigInteger($pivotRole); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } else { + $table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } + }); + + Schema::create($tableNames['role_has_permissions'], static function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) { + $table->unsignedBigInteger($pivotPermission); + $table->unsignedBigInteger($pivotRole); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + + $table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary'); + }); + + app('cache') + ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null) + ->forget(config('permission.cache.key')); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + $tableNames = config('permission.table_names'); + + throw_if(empty($tableNames), Exception::class, 'Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.'); + + Schema::drop($tableNames['role_has_permissions']); + Schema::drop($tableNames['model_has_roles']); + Schema::drop($tableNames['model_has_permissions']); + Schema::drop($tableNames['roles']); + Schema::drop($tableNames['permissions']); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 140965b1..34940d6f 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -12,7 +12,7 @@ class DatabaseSeeder extends Seeder public function run(): void { $this->call([ - UserSeeder::class, + RolesAndPermissionsSeeder::class, ProvoiderSeeder::class, ClientsSeeder::class, BookmakerSeeder::class, diff --git a/database/seeders/RolesAndPermissionsSeeder.php b/database/seeders/RolesAndPermissionsSeeder.php new file mode 100644 index 00000000..eac89c41 --- /dev/null +++ b/database/seeders/RolesAndPermissionsSeeder.php @@ -0,0 +1,48 @@ +upsert( + [ + ['name' => 'super_admin', 'guard_name' => 'web'], + ['name' => 'konto_switch', 'guard_name' => 'web'], + ['name' => 'betsapi_mapper', 'guard_name' => 'web'], + ], + ['name'] + ); + $user = User::factory()->create([ + 'name' => 'Hermann Telser', + 'email' => 'ht@webwerkstatt.tirol', + 'password' => Hash::make('salam'), + 'email_verified_at' => now(), + 'client_id' => 1, + 'sport_id' => 1, + 'bookmaker_id' => 26, + ]); + $user->assignRole('super_admin'); + $user->assignRole('konto_switch'); + $user->assignRole('betsapi_mapper'); + + $user = User::factory()->create([ + 'name' => 'Ubaldo Perrucci', + 'email' => 'ubaldo.perrucci@gmail.com', + 'password' => Hash::make('oscar'), + 'email_verified_at' => now(), + 'client_id' => 1, + 'sport_id' => 1, + 'bookmaker_id' => 26, + ]); + $user->assignRole('super_admin'); + $user->assignRole('konto_switch'); + $user->assignRole('betsapi_mapper'); + } +} diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php deleted file mode 100644 index 6b3a851a..00000000 --- a/database/seeders/UserSeeder.php +++ /dev/null @@ -1,36 +0,0 @@ -create([ - 'name' => 'Hermann Telser', - 'email' => 'ht@webwerkstatt.tirol', - 'password' => Hash::make('salam'), - 'email_verified_at' => now(), - 'client_id' => 1, - 'sport_id' => 1, - 'multiline_setting_id' => 1, - 'super_admin' => 1, - ]); - - User::factory()->create([ - 'name' => 'Ubaldo Perrucci', - 'email' => 'ubaldo.perrucci@gmail.com', - 'password' => Hash::make('oscar'), - 'email_verified_at' => now(), - 'client_id' => 1, - 'sport_id' => 1, - 'multiline_setting_id' => 1, - 'super_admin' => 1, - ]); - } -} diff --git a/public/js/app/dojo.js b/public/js/app/dojo.js deleted file mode 100644 index 1ffbee8b..00000000 --- a/public/js/app/dojo.js +++ /dev/null @@ -1,2076 +0,0 @@ -(function( - userConfig, - defaultConfig -){ - // summary: - // This is the "source loader" and is the entry point for Dojo during development. You may also load Dojo with - // any AMD-compliant loader via the package main module dojo/main. - // description: - // This is the "source loader" for Dojo. It provides an AMD-compliant loader that can be configured - // to operate in either synchronous or asynchronous modes. After the loader is defined, dojo is loaded - // IAW the package main module dojo/main. In the event you wish to use a foreign loader, you may load dojo as a package - // via the package main module dojo/main and this loader is not required; see dojo/package.json for details. - // - // In order to keep compatibility with the v1.x line, this loader includes additional machinery that enables - // the dojo.provide, dojo.require et al API. This machinery is loaded by default, but may be dynamically removed - // via the has.js API and statically removed via the build system. - // - // This loader includes sniffing machinery to determine the environment; the following environments are supported: - // - // - browser - // - node.js - // - rhino - // - // This is the so-called "source loader". As such, it includes many optional features that may be discarded by - // building a customized version with the build system. - - // Design and Implementation Notes - // - // This is a dojo-specific adaption of bdLoad, donated to the dojo foundation by Altoviso LLC. - // - // This function defines an AMD-compliant (http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition) - // loader that can be configured to operate in either synchronous or asynchronous modes. - // - // Since this machinery implements a loader, it does not have the luxury of using a load system and/or - // leveraging a utility library. This results in an unpleasantly long file; here is a road map of the contents: - // - // 1. Small library for use implementing the loader. - // 2. Define the has.js API; this is used throughout the loader to bracket features. - // 3. Define the node.js and rhino sniffs and sniff. - // 4. Define the loader's data. - // 5. Define the configuration machinery. - // 6. Define the script element sniffing machinery and sniff for configuration data. - // 7. Configure the loader IAW the provided user, default, and sniffing data. - // 8. Define the global require function. - // 9. Define the module resolution machinery. - // 10. Define the module and plugin module definition machinery - // 11. Define the script injection machinery. - // 12. Define the window load detection. - // 13. Define the logging API. - // 14. Define the tracing API. - // 16. Define the AMD define function. - // 17. Define the dojo v1.x provide/require machinery--so called "legacy" modes. - // 18. Publish global variables. - // - // Language and Acronyms and Idioms - // - // moduleId: a CJS module identifier, (used for public APIs) - // mid: moduleId (used internally) - // packageId: a package identifier (used for public APIs) - // pid: packageId (used internally); the implied system or default package has pid==="" - // pack: package is used internally to reference a package object (since javascript has reserved words including "package") - // prid: plugin resource identifier - // The integer constant 1 is used in place of true and 0 in place of false. - // - // The "foreign-loader" has condition is defined if another loader is being used (e.g. webpack) and this code is only - // needed for resolving module identifiers based on the config. In this case, only the functions require.toUrl and - // require.toAbsMid are supported. The require and define functions are not supported. - - // define global - var globalObject = (function(){ - if (typeof global !== 'undefined' && typeof global !== 'function') { - // global spec defines a reference to the global object called 'global' - // https://github.com/tc39/proposal-global - // `global` is also defined in NodeJS - return global; - } - else if (typeof window !== 'undefined') { - // window is defined in browsers - return window; - } - else if (typeof self !== 'undefined') { - // self is defined in WebWorkers - return self; - } - return this; - })(); - - // define a minimal library to help build the loader - var noop = function(){ - }, - - isEmpty = function(it){ - for(var p in it){ - return 0; - } - return 1; - }, - - toString = {}.toString, - - isFunction = function(it){ - return toString.call(it) == "[object Function]"; - }, - - isString = function(it){ - return toString.call(it) == "[object String]"; - }, - - isArray = function(it){ - return toString.call(it) == "[object Array]"; - }, - - forEach = function(vector, callback){ - if(vector){ - for(var i = 0; i < vector.length;){ - callback(vector[i++]); - } - } - }, - - mix = function(dest, src){ - for(var p in src){ - dest[p] = src[p]; - } - return dest; - }, - - makeError = function(error, info){ - return mix(new Error(error), {src:"dojoLoader", info:info}); - }, - - uidSeed = 1, - - uid = function(){ - // Returns a unique identifier (within the lifetime of the document) of the form /_d+/. - return "_" + uidSeed++; - }, - - // FIXME: how to doc window.require() api - - // this will be the global require function; define it immediately so we can start hanging things off of it - req = function( - config, //(object, optional) hash of configuration properties - dependencies, //(array of commonjs.moduleId, optional) list of modules to be loaded before applying callback - callback //(function, optional) lambda expression to apply to module values implied by dependencies - ){ - return contextRequire(config, dependencies, callback, 0, req); - }, - - // the loader uses the has.js API to control feature inclusion/exclusion; define then use throughout - global = globalObject, - - doc = global.document, - - element = doc && doc.createElement("DiV"), - - has = req.has = function(name){ - return isFunction(hasCache[name]) ? (hasCache[name] = hasCache[name](global, doc, element)) : hasCache[name]; - }, - - hasCache = has.cache = defaultConfig.hasCache; - - if (isFunction(userConfig)) { - userConfig = userConfig(globalObject); - } - - has.add = function(name, test, now, force){ - (hasCache[name]===undefined || force) && (hasCache[name] = test); - return now && has(name); - }; - - has.add("host-node", userConfig.has && "host-node" in userConfig.has ? - userConfig.has["host-node"] : - (typeof process == "object" && process.versions && process.versions.node && process.versions.v8)); - if(has("host-node")){ - // fixup the default config for node.js environment - require("./_base/configNode.js").config(defaultConfig); - // remember node's require (with respect to baseUrl==dojo's root) - defaultConfig.loaderPatch.nodeRequire = require; - } - - has.add("host-rhino", userConfig.has && "host-rhino" in userConfig.has ? - userConfig.has["host-rhino"] : - (typeof load == "function" && (typeof Packages == "function" || typeof Packages == "object"))); - if(has("host-rhino")){ - // owing to rhino's lame feature that hides the source of the script, give the user a way to specify the baseUrl... - for(var baseUrl = userConfig.baseUrl || ".", arg, rhinoArgs = this.arguments, i = 0; i < rhinoArgs.length;){ - arg = (rhinoArgs[i++] + "").split("="); - if(arg[0] == "baseUrl"){ - baseUrl = arg[1]; - break; - } - } - load(baseUrl + "/_base/configRhino.js"); - rhinoDojoConfig(defaultConfig, baseUrl, rhinoArgs); - } - - has.add("host-webworker", ((typeof WorkerGlobalScope !== 'undefined') && (self instanceof WorkerGlobalScope))); - if(has("host-webworker")){ - mix(defaultConfig.hasCache, { - "host-browser": 0, - "dom": 0, - "dojo-dom-ready-api": 0, - "dojo-sniff": 0, - "dojo-inject-api": 1, - "host-webworker": 1, - "dojo-guarantee-console": 0 // console is immutable in FF30+, see https://bugs.dojotoolkit.org/ticket/18100 - }); - - defaultConfig.loaderPatch = { - injectUrl: function(url, callback){ - // TODO: - // This is not async, nor can it be in Webworkers. It could be made better by passing - // the entire require array into importScripts at. This way the scripts are loaded in - // async mode; even if the callbacks are ran in sync. It is not a major issue as webworkers - // tend to be long running where initial startup is not a major factor. - - try{ - importScripts(url); - callback(); - }catch(e){ - console.info("failed to load resource (" + url + ")"); - console.error(e); - } - } - }; - } - - // userConfig has tests override defaultConfig has tests; do this after the environment detection because - // the environment detection usually sets some has feature values in the hasCache. - for(var p in userConfig.has){ - has.add(p, userConfig.has[p], 0, 1); - } - - // - // define the loader data - // - - // the loader will use these like symbols if the loader has the traceApi; otherwise - // define magic numbers so that modules can be provided as part of defaultConfig - var requested = 1, - arrived = 2, - nonmodule = 3, - executing = 4, - executed = 5; - - if(has("dojo-trace-api")){ - // these make debugging nice; but using strings for symbols is a gross rookie error; don't do it for production code - requested = "requested"; - arrived = "arrived"; - nonmodule = "not-a-module"; - executing = "executing"; - executed = "executed"; - } - - var legacyMode = 0, - sync = "sync", - xd = "xd", - syncExecStack = [], - dojoRequirePlugin = 0, - checkDojoRequirePlugin = noop, - transformToAmd = noop, - getXhr; - if(has("dojo-sync-loader")){ - req.isXdUrl = noop; - - req.initSyncLoader = function(dojoRequirePlugin_, checkDojoRequirePlugin_, transformToAmd_){ - // the first dojo/_base/loader loaded gets to define these variables; they are designed to work - // in the presence of zero to many mapped dojo/_base/loaders - if(!dojoRequirePlugin){ - dojoRequirePlugin = dojoRequirePlugin_; - checkDojoRequirePlugin = checkDojoRequirePlugin_; - transformToAmd = transformToAmd_; - } - - return { - sync:sync, - requested:requested, - arrived:arrived, - nonmodule:nonmodule, - executing:executing, - executed:executed, - syncExecStack:syncExecStack, - modules:modules, - execQ:execQ, - getModule:getModule, - injectModule:injectModule, - setArrived:setArrived, - signal:signal, - finishExec:finishExec, - execModule:execModule, - dojoRequirePlugin:dojoRequirePlugin, - getLegacyMode:function(){return legacyMode;}, - guardCheckComplete:guardCheckComplete - }; - }; - - if(has("dom") || has("host-webworker")){ - // in legacy sync mode, the loader needs a minimal XHR library - - var locationProtocol = location.protocol, - locationHost = location.host; - req.isXdUrl = function(url){ - if(/^\./.test(url)){ - // begins with a dot is always relative to page URL; therefore not xdomain - return false; - } - if(/^\/\//.test(url)){ - // for v1.6- backcompat, url starting with // indicates xdomain - return true; - } - // get protocol and host - // \/+ takes care of the typical file protocol that looks like file:///drive/path/to/file - // locationHost is falsy if file protocol => if locationProtocol matches and is "file:", || will return false - var match = url.match(/^([^\/\:]+\:)\/+([^\/]+)/); - return match && (match[1] != locationProtocol || (locationHost && match[2] != locationHost)); - }; - - - // note: to get the file:// protocol to work in FF, you must set security.fileuri.strict_origin_policy to false in about:config - has.add("dojo-xhr-factory", 1); - has.add("dojo-force-activex-xhr", has("host-browser") && !doc.addEventListener && window.location.protocol == "file:"); - has.add("native-xhr", typeof XMLHttpRequest != "undefined"); - if(has("native-xhr") && !has("dojo-force-activex-xhr")){ - getXhr = function(){ - return new XMLHttpRequest(); - }; - }else{ - // if in the browser an old IE; find an xhr - for(var XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], progid, i = 0; i < 3;){ - try{ - progid = XMLHTTP_PROGIDS[i++]; - if(new ActiveXObject(progid)){ - // this progid works; therefore, use it from now on - break; - } - }catch(e){ - // squelch; we're just trying to find a good ActiveX progid - // if they all fail, then progid ends up as the last attempt and that will signal the error - // the first time the client actually tries to exec an xhr - } - } - getXhr = function(){ - return new ActiveXObject(progid); - }; - } - req.getXhr = getXhr; - - has.add("dojo-gettext-api", 1); - req.getText = function(url, async, onLoad){ - var xhr = getXhr(); - xhr.open('GET', fixupUrl(url), false); - xhr.send(null); - if(xhr.status == 200 || (!location.host && !xhr.status)){ - if(onLoad){ - onLoad(xhr.responseText, async); - } - }else{ - throw makeError("xhrFailed", xhr.status); - } - return xhr.responseText; - }; - } - }else{ - req.async = 1; - } - - // - // loader eval - // - var eval_ = has("csp-restrictions") ? - // noop eval if there are csp restrictions - function(){} : - // use the function constructor so our eval is scoped close to (but not in) in the global space with minimal pollution - new Function('return eval(arguments[0]);'); - - req.eval = - function(text, hint){ - return eval_(text + "\r\n//# sourceURL=" + hint); - }; - - // - // loader micro events API - // - var listenerQueues = {}, - error = "error", - signal = req.signal = function(type, args){ - var queue = listenerQueues[type]; - // notice we run a copy of the queue; this allows listeners to add/remove - // other listeners without affecting this particular signal - forEach(queue && queue.slice(0), function(listener){ - listener.apply(null, isArray(args) ? args : [args]); - }); - }, - on = req.on = function(type, listener){ - // notice a queue is not created until a client actually connects - var queue = listenerQueues[type] || (listenerQueues[type] = []); - queue.push(listener); - return { - remove:function(){ - for(var i = 0; i (alias, actual) - = [], - - paths - // CommonJS paths - = {}, - - pathsMapProg - // list of (from-path, to-path, regex, length) derived from paths; - // a "program" to apply paths; see computeMapProg - = [], - - packs - // a map from packageId to package configuration object; see fixupPackageInfo - = {}, - - map = req.map - // AMD map config variable; dojo/_base/kernel needs req.map to figure out the scope map - = {}, - - mapProgs - // vector of quads as described by computeMapProg; map-key is AMD map key, map-value is AMD map value - = [], - - modules - // A hash:(mid) --> (module-object) the module namespace - // - // pid: the package identifier to which the module belongs (e.g., "dojo"); "" indicates the system or default package - // mid: the fully-resolved (i.e., mappings have been applied) module identifier without the package identifier (e.g., "dojo/io/script") - // url: the URL from which the module was retrieved - // pack: the package object of the package to which the module belongs - // executed: 0 => not executed; executing => in the process of traversing deps and running factory; executed => factory has been executed - // deps: the dependency vector for this module (vector of modules objects) - // def: the factory for this module - // result: the result of the running the factory for this module - // injected: (0 | requested | arrived) the status of the module; nonmodule means the resource did not call define - // load: plugin load function; applicable only for plugins - // - // Modules go through several phases in creation: - // - // 1. Requested: some other module's definition or a require application contained the requested module in - // its dependency vector or executing code explicitly demands a module via req.require. - // - // 2. Injected: a script element has been appended to the insert-point element demanding the resource implied by the URL - // - // 3. Loaded: the resource injected in [2] has been evaluated. - // - // 4. Defined: the resource contained a define statement that advised the loader about the module. Notice that some - // resources may just contain a bundle of code and never formally define a module via define - // - // 5. Evaluated: the module was defined via define and the loader has evaluated the factory and computed a result. - = {}, - - cacheBust - // query string to append to module URLs to bust browser cache - = "", - - cache - // hash:(mid | url)-->(function | string) - // - // A cache of resources. The resources arrive via a config.cache object, which is a hash from either mid --> function or - // url --> string. The url key is distinguished from the mid key by always containing the prefix "url:". url keys as provided - // by config.cache always have a string value that represents the contents of the resource at the given url. mid keys as provided - // by configl.cache always have a function value that causes the same code to execute as if the module was script injected. - // - // Both kinds of key-value pairs are entered into cache via the function consumePendingCache, which may relocate keys as given - // by any mappings *iff* the config.cache was received as part of a module resource request. - // - // Further, for mid keys, the implied url is computed and the value is entered into that key as well. This allows mapped modules - // to retrieve cached items that may have arrived consequent to another namespace. - // - = {}, - - urlKeyPrefix - // the prefix to prepend to a URL key in the cache. - = "url:", - - pendingCacheInsert - // hash:(mid)-->(function) - // - // Gives a set of cache modules pending entry into cache. When cached modules are published to the loader, they are - // entered into pendingCacheInsert; modules are then pressed into cache upon (1) AMD define or (2) upon receiving another - // independent set of cached modules. (1) is the usual case, and this case allows normalizing mids given in the pending - // cache for the local configuration, possibly relocating modules. - = {}, - - dojoSniffConfig - // map of configuration variables - // give the data-dojo-config as sniffed from the document (if any) - = {}, - - insertPointSibling - // the nodes used to locate where scripts are injected into the document - = 0; - - if(has("dojo-config-api")){ - if (!has("foreign-loader")) { - var consumePendingCacheInsert = function(referenceModule, clear){ - clear = clear !== false; - var p, item, match, now, m; - for(p in pendingCacheInsert){ - item = pendingCacheInsert[p]; - match = p.match(/^url\:(.+)/); - if(match){ - cache[urlKeyPrefix + toUrl(match[1], referenceModule)] = item; - }else if(p=="*now"){ - now = item; - }else if(p!="*noref"){ - m = getModuleInfo(p, referenceModule, true); - cache[m.mid] = cache[urlKeyPrefix + m.url] = item; - } - } - if(now){ - now(createRequire(referenceModule)); - } - if(clear){ - pendingCacheInsert = {}; - } - }; - } - var escapeString = function(s){ - return s.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(c){ return "\\" + c; }); - }, - - computeMapProg = function(map, dest){ - // This routine takes a map as represented by a JavaScript object and initializes dest, a vector of - // quads of (map-key, map-value, refex-for-map-key, length-of-map-key), sorted decreasing by length- - // of-map-key. The regex looks for the map-key followed by either "/" or end-of-string at the beginning - // of a the search source. Notice the map-value is irrelevant to the algorithm - dest.splice(0, dest.length); - for(var p in map){ - dest.push([ - p, - map[p], - new RegExp("^" + escapeString(p) + "(\/|$)"), - p.length]); - } - dest.sort(function(lhs, rhs){ return rhs[3] - lhs[3]; }); - return dest; - }, - - computeAliases = function(config, dest){ - forEach(config, function(pair){ - // take a fixed-up copy... - dest.push([isString(pair[0]) ? new RegExp("^" + escapeString(pair[0]) + "$") : pair[0], pair[1]]); - }); - }, - - - fixupPackageInfo = function(packageInfo){ - // calculate the precise (name, location, main, mappings) for a package - var name = packageInfo.name; - if(!name){ - // packageInfo must be a string that gives the name - name = packageInfo; - packageInfo = {name:name}; - } - packageInfo = mix({main:"main"}, packageInfo); - packageInfo.location = packageInfo.location ? packageInfo.location : name; - - // packageMap is deprecated in favor of AMD map - if(packageInfo.packageMap){ - map[name] = packageInfo.packageMap; - } - - if(!packageInfo.main.indexOf("./")){ - packageInfo.main = packageInfo.main.substring(2); - } - - // now that we've got a fully-resolved package object, push it into the configuration - packs[name] = packageInfo; - }, - - delayedModuleConfig - // module config cannot be consumed until the loader is completely initialized; therefore, all - // module config detected during booting is memorized and applied at the end of loader initialization - // TODO: this is a bit of a kludge; all config should be moved to end of loader initialization, but - // we'll delay this chore and do it with a final loader 1.x cleanup after the 2.x loader prototyping is complete - = [], - - - config = function(config, booting, referenceModule){ - for(var p in config){ - if(p=="waitSeconds"){ - req.waitms = (config[p] || 0) * 1000; - } - if(p=="cacheBust"){ - cacheBust = config[p] ? (isString(config[p]) ? config[p] : (new Date()).getTime() + "") : ""; - } - if(p=="baseUrl" || p=="combo"){ - req[p] = config[p]; - } - if(has("dojo-sync-loader") && p=="async"){ - // falsy or "sync" => legacy sync loader - // "xd" => sync but loading xdomain tree and therefore loading asynchronously (not configurable, set automatically by the loader) - // "legacyAsync" => permanently in "xd" by choice - // "debugAtAllCosts" => trying to load everything via script injection (not implemented) - // otherwise, must be truthy => AMD - // legacyMode: sync | legacyAsync | xd | false - var mode = config[p]; - req.legacyMode = legacyMode = (isString(mode) && /sync|legacyAsync/.test(mode) ? mode : (!mode ? sync : false)); - req.async = !legacyMode; - } - if(config[p]!==hasCache){ - // accumulate raw config info for client apps which can use this to pass their own config - req.rawConfig[p] = config[p]; - p!="has" && has.add("config-"+p, config[p], 0, booting); - } - } - - // make sure baseUrl exists - if(!req.baseUrl){ - req.baseUrl = "./"; - } - // make sure baseUrl ends with a slash - if(!/\/$/.test(req.baseUrl)){ - req.baseUrl += "/"; - } - - // now do the special work for has, packages, packagePaths, paths, aliases, and cache - - for(p in config.has){ - has.add(p, config.has[p], 0, booting); - } - - // for each package found in any packages config item, augment the packs map owned by the loader - forEach(config.packages, fixupPackageInfo); - - // for each packagePath found in any packagePaths config item, augment the packageConfig - // packagePaths is deprecated; remove in 2.0 - for(var baseUrl in config.packagePaths){ - forEach(config.packagePaths[baseUrl], function(packageInfo){ - var location = baseUrl + "/" + packageInfo; - if(isString(packageInfo)){ - packageInfo = {name:packageInfo}; - } - packageInfo.location = location; - fixupPackageInfo(packageInfo); - }); - } - - // notice that computeMapProg treats the dest as a reference; therefore, if/when that variable - // is published (see dojo-publish-privates), the published variable will always hold a valid value. - - // this must come after all package processing since package processing may mutate map - computeMapProg(mix(map, config.map), mapProgs); - forEach(mapProgs, function(item){ - item[1] = computeMapProg(item[1], []); - if(item[0]=="*"){ - mapProgs.star = item; - } - }); - - // push in any paths and recompute the internal pathmap - computeMapProg(mix(paths, config.paths), pathsMapProg); - - // aliases - computeAliases(config.aliases, aliases); - - if (!has("foreign-loader")) { - if(booting){ - delayedModuleConfig.push({config:config.config}); - }else{ - for(p in config.config){ - var module = getModule(p, referenceModule); - module.config = mix(module.config || {}, config.config[p]); - } - } - - // push in any new cache values - if(config.cache){ - consumePendingCacheInsert(); - pendingCacheInsert = config.cache; - //inject now all depencies so cache is available for mapped module - consumePendingCacheInsert(0, !!config.cache["*noref"]); - } - } - signal("config", [config, req.rawConfig]); - }; - - // - // execute the various sniffs; userConfig can override and value - // - - if(has("dojo-cdn") || has("dojo-sniff")){ - // the sniff regex looks for a src attribute ending in dojo.js, optionally preceded with a path. - // match[3] returns the path to dojo.js (if any) without the trailing slash. This is used for the - // dojo location on CDN deployments and baseUrl when either/both of these are not provided - // explicitly in the config data; this is the 1.6- behavior. - - var scripts = doc.getElementsByTagName("script"), - i = 0, - script, dojoDir, src, match; - while(i < scripts.length){ - script = scripts[i++]; - if((src = script.getAttribute("src")) && (match = src.match(/(((.*)\/)|^)dojo\.js(\W|$)/i))){ - // sniff dojoDir and baseUrl - dojoDir = match[3] || ""; - defaultConfig.baseUrl = defaultConfig.baseUrl || dojoDir; - - // remember an insertPointSibling - insertPointSibling = script; - } - - // sniff configuration on attribute in script element - if((src = (script.getAttribute("data-dojo-config") || script.getAttribute("djConfig")))){ - dojoSniffConfig = req.eval("({ " + src + " })", "data-dojo-config"); - - // remember an insertPointSibling - insertPointSibling = script; - } - - // sniff requirejs attribute - if(has("dojo-requirejs-api")){ - if((src = script.getAttribute("data-main"))){ - dojoSniffConfig.deps = dojoSniffConfig.deps || [src]; - } - } - } - } - - if(has("dojo-test-sniff")){ - // pass down doh.testConfig from parent as if it were a data-dojo-config - try{ - if(window.parent != window && window.parent.require){ - var doh = window.parent.require("doh"); - doh && mix(dojoSniffConfig, doh.testConfig); - } - }catch(e){} - } - - // configure the loader; let the user override defaults - req.rawConfig = {}; - config(defaultConfig, 1); - - // do this before setting userConfig/sniffConfig to allow userConfig/sniff overrides - if(has("dojo-cdn")){ - packs.dojo.location = dojoDir; - if(dojoDir){ - dojoDir += "/"; - } - packs.dijit.location = dojoDir + "../dijit/"; - packs.dojox.location = dojoDir + "../dojox/"; - } - - config(userConfig, 1); - config(dojoSniffConfig, 1); - - }else{ - // no config API, assume defaultConfig has everything the loader needs...for the entire lifetime of the application - paths = defaultConfig.paths; - pathsMapProg = defaultConfig.pathsMapProg; - packs = defaultConfig.packs; - aliases = defaultConfig.aliases; - mapProgs = defaultConfig.mapProgs; - modules = defaultConfig.modules; - cache = defaultConfig.cache; - cacheBust = defaultConfig.cacheBust; - - // remember the default config for other processes (e.g., dojo/config) - req.rawConfig = defaultConfig; - } - - - if (!has("foreign-loader")) { - if(has("dojo-combo-api")){ - req.combo = req.combo || {add:noop}; - var comboPending = 0, - combosPending = [], - comboPendingTimer = null; - } - - - // build the loader machinery iaw configuration, including has feature tests - var injectDependencies = function(module){ - // checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies - guardCheckComplete(function(){ - forEach(module.deps, injectModule); - if(has("dojo-combo-api") && comboPending && !comboPendingTimer){ - comboPendingTimer = setTimeout(function() { - comboPending = 0; - comboPendingTimer = null; - req.combo.done(function(mids, url) { - var onLoadCallback= function(){ - // defQ is a vector of module definitions 1-to-1, onto mids - runDefQ(0, mids); - checkComplete(); - }; - combosPending.push(mids); - injectingModule = mids; - req.injectUrl(url, onLoadCallback, mids); - injectingModule = 0; - }, req); - }, 0); - } - }); - }, - - contextRequire = function(a1, a2, a3, referenceModule, contextRequire){ - var module, syntheticMid; - if(isString(a1)){ - // signature is (moduleId) - module = getModule(a1, referenceModule, true); - if(module && module.executed){ - return module.result; - } - throw makeError("undefinedModule", a1); - } - if(!isArray(a1)){ - // a1 is a configuration - config(a1, 0, referenceModule); - - // juggle args; (a2, a3) may be (dependencies, callback) - a1 = a2; - a2 = a3; - } - if(isArray(a1)){ - // signature is (requestList [,callback]) - if(!a1.length){ - a2 && a2(); - }else{ - syntheticMid = "require*" + uid(); - - // resolve the request list with respect to the reference module - for(var mid, deps = [], i = 0; i < a1.length;){ - mid = a1[i++]; - deps.push(getModule(mid, referenceModule)); - } - - // construct a synthetic module to control execution of the requestList, and, optionally, callback - module = mix(makeModuleInfo("", syntheticMid, 0, ""), { - injected: arrived, - deps: deps, - def: a2 || noop, - require: referenceModule ? referenceModule.require : req, - gc: 1 //garbage collect - }); - modules[module.mid] = module; - - // checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies - injectDependencies(module); - - // try to immediately execute - // if already traversing a factory tree, then strict causes circular dependency to abort the execution; maybe - // it's possible to execute this require later after the current traversal completes and avoid the circular dependency. - // ...but *always* insist on immediate in synch mode - var strict = checkCompleteGuard && legacyMode!=sync; - guardCheckComplete(function(){ - execModule(module, strict); - }); - if(!module.executed){ - // some deps weren't on board or circular dependency detected and strict; therefore, push into the execQ - execQ.push(module); - } - checkComplete(); - } - } - return contextRequire; - }, - - createRequire = function(module){ - if(!module){ - return req; - } - var result = module.require; - if(!result){ - result = function(a1, a2, a3){ - return contextRequire(a1, a2, a3, module, result); - }; - module.require = mix(result, req); - result.module = module; - result.toUrl = function(name){ - return toUrl(name, module); - }; - result.toAbsMid = function(mid){ - return toAbsMid(mid, module); - }; - if(has("dojo-undef-api")){ - result.undef = function(mid){ - req.undef(mid, module); - }; - } - if(has("dojo-sync-loader")){ - result.syncLoadNls = function(mid){ - var nlsModuleInfo = getModuleInfo(mid, module), - nlsModule = modules[nlsModuleInfo.mid]; - if(!nlsModule || !nlsModule.executed){ - cached = cache[nlsModuleInfo.mid] || cache[urlKeyPrefix + nlsModuleInfo.url]; - if(cached){ - evalModuleText(cached); - nlsModule = modules[nlsModuleInfo.mid]; - } - } - return nlsModule && nlsModule.executed && nlsModule.result; - }; - } - - } - return result; - }, - - execQ = - // The list of modules that need to be evaluated. - [], - - defQ = - // The queue of define arguments sent to loader. - [], - - waiting = - // The set of modules upon which the loader is waiting for definition to arrive - {}, - - setRequested = function(module){ - module.injected = requested; - waiting[module.mid] = 1; - if(module.url){ - waiting[module.url] = module.pack || 1; - } - startTimer(); - }, - - setArrived = function(module){ - module.injected = arrived; - delete waiting[module.mid]; - if(module.url){ - delete waiting[module.url]; - } - if(isEmpty(waiting)){ - clearTimer(); - has("dojo-sync-loader") && legacyMode==xd && (legacyMode = sync); - } - }, - - execComplete = req.idle = - // says the loader has completed (or not) its work - function(){ - return !defQ.length && isEmpty(waiting) && !execQ.length && !checkCompleteGuard; - }; - } - - var runMapProg = function(targetMid, map){ - // search for targetMid in map; return the map item if found; falsy otherwise - if(map){ - for(var i = 0; i < map.length; i++){ - if(map[i][2].test(targetMid)){ - return map[i]; - } - } - } - return 0; - }, - - compactPath = function(path){ - var result = [], - segment, lastSegment; - path = path.replace(/\\/g, '/').split('/'); - while(path.length){ - segment = path.shift(); - if(segment==".." && result.length && lastSegment!=".."){ - result.pop(); - lastSegment = result[result.length - 1]; - }else if(segment!="."){ - result.push(lastSegment= segment); - } // else ignore "." - } - return result.join("/"); - }, - - makeModuleInfo = function(pid, mid, pack, url){ - if(has("dojo-sync-loader")){ - var xd= req.isXdUrl(url); - return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0, isXd:xd, isAmd:!!(xd || (packs[pid] && packs[pid].isAmd))}; - }else{ - return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0}; - } - }, - - getModuleInfo_ = function(mid, referenceModule, packs, modules, baseUrl, mapProgs, pathsMapProg, aliases, alwaysCreate, fromPendingCache){ - // arguments are passed instead of using lexical variables so that this function my be used independent of the loader (e.g., the builder) - // alwaysCreate is useful in this case so that getModuleInfo never returns references to real modules owned by the loader - var pid, pack, midInPackage, mapItem, url, result, isRelative, requestedMid; - requestedMid = mid; - isRelative = /^\./.test(mid); - if(/(^\/)|(\:)|(\.js$)/.test(mid) || (isRelative && !referenceModule)){ - // absolute path or protocol of .js filetype, or relative path but no reference module and therefore relative to page - // whatever it is, it's not a module but just a URL of some sort - // note: pid===0 indicates the routine is returning an unmodified mid - - return makeModuleInfo(0, mid, 0, mid); - }else{ - // relative module ids are relative to the referenceModule; get rid of any dots - mid = compactPath(isRelative ? (referenceModule.mid + "/../" + mid) : mid); - if(/^\./.test(mid)){ - throw makeError("irrationalPath", mid); - } - // at this point, mid is an absolute mid - - // map the mid - if(!fromPendingCache && !isRelative && mapProgs.star){ - mapItem = runMapProg(mid, mapProgs.star[1]); - } - if(!mapItem && referenceModule){ - mapItem = runMapProg(referenceModule.mid, mapProgs); - mapItem = mapItem && runMapProg(mid, mapItem[1]); - } - - if(mapItem){ - mid = mapItem[1] + mid.substring(mapItem[3]); - } - - match = mid.match(/^([^\/]+)(\/(.+))?$/); - pid = match ? match[1] : ""; - if((pack = packs[pid])){ - mid = pid + "/" + (midInPackage = (match[3] || pack.main)); - }else{ - pid = ""; - } - - // search aliases - var candidateLength = 0, - candidate = 0; - forEach(aliases, function(pair){ - var match = mid.match(pair[0]); - if(match && match.length>candidateLength){ - candidate = isFunction(pair[1]) ? mid.replace(pair[0], pair[1]) : pair[1]; - } - }); - if(candidate){ - return getModuleInfo_(candidate, 0, packs, modules, baseUrl, mapProgs, pathsMapProg, aliases, alwaysCreate); - } - - result = modules[mid]; - if(result){ - return alwaysCreate ? makeModuleInfo(result.pid, result.mid, result.pack, result.url) : modules[mid]; - } - } - // get here iff the sought-after module does not yet exist; therefore, we need to compute the URL given the - // fully resolved (i.e., all relative indicators and package mapping resolved) module id - - // note: pid!==0 indicates the routine is returning a url that has .js appended unmodified mid - mapItem = runMapProg(mid, pathsMapProg); - if(mapItem){ - url = mapItem[1] + mid.substring(mapItem[3]); - }else if(pid){ - url = (pack.location.slice(-1) === '/' ? pack.location.slice(0, -1) : pack.location) + "/" + midInPackage; - }else if(has("config-tlmSiblingOfDojo")){ - url = "../" + mid; - }else{ - url = mid; - } - // if result is not absolute, add baseUrl - if(!(/(^\/)|(\:)/.test(url))){ - url = baseUrl + url; - } - url += ".js"; - return makeModuleInfo(pid, mid, pack, compactPath(url)); - }, - - getModuleInfo = function(mid, referenceModule, fromPendingCache){ - return getModuleInfo_(mid, referenceModule, packs, modules, req.baseUrl, mapProgs, pathsMapProg, aliases, undefined, fromPendingCache); - }; - - if (!has("foreign-loader")) { - var resolvePluginResourceId = function(plugin, prid, referenceModule){ - return plugin.normalize ? plugin.normalize(prid, function(mid){return toAbsMid(mid, referenceModule);}) : toAbsMid(prid, referenceModule); - }, - - dynamicPluginUidGenerator = 0, - - getModule = function(mid, referenceModule, immediate){ - // compute and optionally construct (if necessary) the module implied by the mid with respect to referenceModule - var match, plugin, prid, result; - match = mid.match(/^(.+?)\!(.*)$/); - if(match){ - // name was ! - plugin = getModule(match[1], referenceModule, immediate); - - if(has("dojo-sync-loader") && legacyMode == sync && !plugin.executed){ - injectModule(plugin); - if(plugin.injected===arrived && !plugin.executed){ - guardCheckComplete(function(){ - execModule(plugin); - }); - } - if(plugin.executed){ - promoteModuleToPlugin(plugin); - }else{ - // we are in xdomain mode for some reason - execQ.unshift(plugin); - } - } - - - - if(plugin.executed === executed && !plugin.load){ - // executed the module not knowing it was a plugin - promoteModuleToPlugin(plugin); - } - - // if the plugin has not been loaded, then can't resolve the prid and must assume this plugin is dynamic until we find out otherwise - if(plugin.load){ - prid = resolvePluginResourceId(plugin, match[2], referenceModule); - mid = (plugin.mid + "!" + (plugin.dynamic ? ++dynamicPluginUidGenerator + "!" : "") + prid); - }else{ - prid = match[2]; - mid = plugin.mid + "!" + (++dynamicPluginUidGenerator) + "!waitingForPlugin"; - } - result = {plugin:plugin, mid:mid, req:createRequire(referenceModule), prid:prid}; - }else{ - result = getModuleInfo(mid, referenceModule); - } - return modules[result.mid] || (!immediate && (modules[result.mid] = result)); - }; - } - - var toAbsMid = req.toAbsMid = function(mid, referenceModule){ - return getModuleInfo(mid, referenceModule).mid; - }, - - toUrl = req.toUrl = function(name, referenceModule){ - var moduleInfo = getModuleInfo(name+"/x", referenceModule), - url= moduleInfo.url; - return fixupUrl(moduleInfo.pid===0 ? - // if pid===0, then name had a protocol or absolute path; either way, toUrl is the identify function in such cases - name : - // "/x.js" since getModuleInfo automatically appends ".js" and we appended "/x" to make name look like a module id - url.substring(0, url.length-5) - ); - }; - - if (!has("foreign-loader")) { - var nonModuleProps = { - injected: arrived, - executed: executed, - def: nonmodule, - result: nonmodule - }, - - makeCjs = function(mid){ - return modules[mid] = mix({mid:mid}, nonModuleProps); - }, - - cjsRequireModule = makeCjs("require"), - cjsExportsModule = makeCjs("exports"), - cjsModuleModule = makeCjs("module"), - - runFactory = function(module, args){ - req.trace("loader-run-factory", [module.mid]); - var factory = module.def, - result; - has("dojo-sync-loader") && syncExecStack.unshift(module); - if(has("config-dojo-loader-catches")){ - try{ - result= isFunction(factory) ? factory.apply(null, args) : factory; - }catch(e){ - signal(error, module.result = makeError("factoryThrew", [module, e])); - } - }else{ - result= isFunction(factory) ? factory.apply(null, args) : factory; - } - module.result = result===undefined && module.cjs ? module.cjs.exports : result; - has("dojo-sync-loader") && syncExecStack.shift(module); - }, - - abortExec = {}, - - defOrder = 0, - - promoteModuleToPlugin = function(pluginModule){ - var plugin = pluginModule.result; - pluginModule.dynamic = plugin.dynamic; - pluginModule.normalize = plugin.normalize; - pluginModule.load = plugin.load; - return pluginModule; - }, - - resolvePluginLoadQ = function(plugin){ - // plugins is a newly executed module that has a loadQ waiting to run - - // step 1: traverse the loadQ and fixup the mid and prid; remember the map from original mid to new mid - // recall the original mid was created before the plugin was on board and therefore it was impossible to - // compute the final mid; accordingly, prid may or may not change, but the mid will definitely change - var map = {}; - forEach(plugin.loadQ, function(pseudoPluginResource){ - // manufacture and insert the real module in modules - var prid = resolvePluginResourceId(plugin, pseudoPluginResource.prid, pseudoPluginResource.req.module), - mid = plugin.dynamic ? pseudoPluginResource.mid.replace(/waitingForPlugin$/, prid) : (plugin.mid + "!" + prid), - pluginResource = mix(mix({}, pseudoPluginResource), {mid:mid, prid:prid, injected:0}); - if(!modules[mid] || !modules[mid].injected /*for require.undef*/){ - // create a new (the real) plugin resource and inject it normally now that the plugin is on board - injectPlugin(modules[mid] = pluginResource); - } // else this was a duplicate request for the same (plugin, rid) for a nondynamic plugin - - // pluginResource is really just a placeholder with the wrong mid (because we couldn't calculate it until the plugin was on board) - // mark is as arrived and delete it from modules; the real module was requested above - map[pseudoPluginResource.mid] = modules[mid]; - setArrived(pseudoPluginResource); - delete modules[pseudoPluginResource.mid]; - }); - plugin.loadQ = 0; - - // step2: replace all references to any placeholder modules with real modules - var substituteModules = function(module){ - for(var replacement, deps = module.deps || [], i = 0; i")]); - return (!module.def || strict) ? abortExec : (module.cjs && module.cjs.exports); - } - // at this point the module is either not executed or fully executed - - - if(!module.executed){ - if(!module.def){ - return abortExec; - } - var mid = module.mid, - deps = module.deps || [], - arg, argResult, - args = [], - i = 0; - - if(has("dojo-trace-api")){ - circleTrace.push(mid); - req.trace("loader-exec-module", ["exec", circleTrace.length, mid]); - } - - // for circular dependencies, assume the first module encountered was executed OK - // modules that circularly depend on a module that has not run its factory will get - // the pre-made cjs.exports===module.result. They can take a reference to this object and/or - // add properties to it. When the module finally runs its factory, the factory can - // read/write/replace this object. Notice that so long as the object isn't replaced, any - // reference taken earlier while walking the deps list is still valid. - module.executed = executing; - while((arg = deps[i++])){ - argResult = ((arg === cjsRequireModule) ? createRequire(module) : - ((arg === cjsExportsModule) ? module.cjs.exports : - ((arg === cjsModuleModule) ? module.cjs : - execModule(arg, strict)))); - if(argResult === abortExec){ - module.executed = 0; - req.trace("loader-exec-module", ["abort", mid]); - has("dojo-trace-api") && circleTrace.pop(); - return abortExec; - } - args.push(argResult); - } - runFactory(module, args); - finishExec(module); - has("dojo-trace-api") && circleTrace.pop(); - } - // at this point the module is guaranteed fully executed - - return module.result; - }, - - - checkCompleteGuard = 0, - - guardCheckComplete = function(proc){ - try{ - checkCompleteGuard++; - proc(); - }catch(e){ - // https://bugs.dojotoolkit.org/ticket/16617 - throw e; - }finally{ - checkCompleteGuard--; - } - if(execComplete()){ - signal("idle", []); - } - }, - - checkComplete = function(){ - // keep going through the execQ as long as at least one factory is executed - // plugins, recursion, cached modules all make for many execution path possibilities - if(checkCompleteGuard){ - return; - } - guardCheckComplete(function(){ - checkDojoRequirePlugin(); - for(var currentDefOrder, module, i = 0; i < execQ.length;){ - currentDefOrder = defOrder; - module = execQ[i]; - execModule(module); - if(currentDefOrder!=defOrder){ - // defOrder was bumped one or more times indicating something was executed (note, this indicates - // the execQ was modified, maybe a lot (for example a later module causes an earlier module to execute) - checkDojoRequirePlugin(); - i = 0; - }else{ - // nothing happened; check the next module in the exec queue - i++; - } - } - }); - }; - } - - var fixupUrl= typeof userConfig.fixupUrl == "function" ? userConfig.fixupUrl : function(url){ - url += ""; // make sure url is a Javascript string (some paths may be a Java string) - return url + (cacheBust ? ((/\?/.test(url) ? "&" : "?") + cacheBust) : ""); - }; - - - - if(has("dojo-undef-api")){ - req.undef = function(moduleId, referenceModule){ - // In order to reload a module, it must be undefined (this routine) and then re-requested. - // This is useful for testing frameworks (at least). - var module = getModule(moduleId, referenceModule); - setArrived(module); - mix(module, {def:0, executed:0, injected:0, node:0, load:0}); - }; - } - - if(has("dojo-inject-api")){ - if(has("dojo-loader-eval-hint-url")===undefined){ - has.add("dojo-loader-eval-hint-url", 1); - } - - var injectPlugin = function( - module - ){ - // injects the plugin module given by module; may have to inject the plugin itself - var plugin = module.plugin; - - if(plugin.executed === executed && !plugin.load){ - // executed the module not knowing it was a plugin - promoteModuleToPlugin(plugin); - } - - var onLoad = function(def){ - module.result = def; - setArrived(module); - finishExec(module); - checkComplete(); - }; - - if(plugin.load){ - plugin.load(module.prid, module.req, onLoad); - }else if(plugin.loadQ){ - plugin.loadQ.push(module); - }else{ - // the unshift instead of push is important: we don't want plugins to execute as - // dependencies of some other module because this may cause circles when the plugin - // loadQ is run; also, generally, we want plugins to run early since they may load - // several other modules and therefore can potentially unblock many modules - plugin.loadQ = [module]; - execQ.unshift(plugin); - injectModule(plugin); - } - }, - - // for IE, injecting a module may result in a recursive execution if the module is in the cache - - cached = 0, - - injectingModule = 0, - - injectingCachedModule = 0, - - evalModuleText = function(text, module){ - // see def() for the injectingCachedModule bracket; it simply causes a short, safe circuit - if(has("config-stripStrict")){ - text = text.replace(/(["'])use strict\1/g, ''); - } - injectingCachedModule = 1; - if(has("config-dojo-loader-catches")){ - try{ - if(text===cached){ - cached.call(null); - }else{ - req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid); - } - }catch(e){ - signal(error, makeError("evalModuleThrew", module)); - } - }else{ - if(text===cached){ - cached.call(null); - }else{ - req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid); - } - } - injectingCachedModule = 0; - }, - - injectModule = function(module){ - // Inject the module. In the browser environment, this means appending a script element into - // the document; in other environments, it means loading a file. - // - // If in synchronous mode, then get the module synchronously if it's not xdomainLoading. - - var mid = module.mid, - url = module.url; - if(module.executed || module.injected || waiting[mid] || (module.url && ((module.pack && waiting[module.url]===module.pack) || waiting[module.url]==1))){ - return; - } - setRequested(module); - - if(has("dojo-combo-api")){ - var viaCombo = 0; - if(module.plugin && module.plugin.isCombo){ - // a combo plugin; therefore, must be handled by combo service - // the prid should have already been converted to a URL (if required by the plugin) during - // the normalize process; in any event, there is no way for the loader to know how to - // to the conversion; therefore the third argument is zero - req.combo.add(module.plugin.mid, module.prid, 0, req); - viaCombo = 1; - }else if(!module.plugin){ - viaCombo = req.combo.add(0, module.mid, module.url, req); - } - if(viaCombo){ - comboPending= 1; - return; - } - } - - if(module.plugin){ - injectPlugin(module); - return; - } // else a normal module (not a plugin) - - - var onLoadCallback = function(){ - runDefQ(module); - if(module.injected !== arrived){ - // the script that contained the module arrived and has been executed yet - // nothing was added to the defQ (so it wasn't an AMD module) and the module - // wasn't marked as arrived by dojo.provide (so it wasn't a v1.6- module); - // therefore, it must not have been a module; adjust state accordingly - if(has("dojo-enforceDefine")){ - signal(error, makeError("noDefine", module)); - return; - } - setArrived(module); - mix(module, nonModuleProps); - req.trace("loader-define-nonmodule", [module.url]); - } - - if(has("dojo-sync-loader") && legacyMode){ - // must call checkComplete even in for sync loader because we may be in xdomainLoading mode; - // but, if xd loading, then don't call checkComplete until out of the current sync traversal - // in order to preserve order of execution of the dojo.required modules - !syncExecStack.length && checkComplete(); - }else{ - checkComplete(); - } - }; - cached = cache[mid] || cache[urlKeyPrefix + module.url]; - if(cached){ - req.trace("loader-inject", ["cache", module.mid, url]); - evalModuleText(cached, module); - onLoadCallback(); - return; - } - if(has("dojo-sync-loader") && legacyMode){ - if(module.isXd){ - // switch to async mode temporarily; if current legacyMode!=sync, then is must be one of {legacyAsync, xd, false} - legacyMode==sync && (legacyMode = xd); - // fall through and load via script injection - }else if(module.isAmd && legacyMode!=sync){ - // fall through and load via script injection - }else{ - // mode may be sync, xd/legacyAsync, or async; module may be AMD or legacy; but module is always located on the same domain - var xhrCallback = function(text){ - if(legacyMode==sync){ - // the top of syncExecStack gives the current synchronously executing module; the loader needs - // to know this if it has to switch to async loading in the middle of evaluating a legacy module - // this happens when a modules dojo.require's a module that must be loaded async because it's xdomain - // (using unshift/shift because there is no back() methods for Javascript arrays) - syncExecStack.unshift(module); - evalModuleText(text, module); - syncExecStack.shift(); - - // maybe the module was an AMD module - runDefQ(module); - - // legacy modules never get to defineModule() => cjs and injected never set; also evaluation implies executing - if(!module.cjs){ - setArrived(module); - finishExec(module); - } - - if(module.finish){ - // while synchronously evaluating this module, dojo.require was applied referencing a module - // that had to be loaded async; therefore, the loader stopped answering all dojo.require - // requests so they could be answered completely in the correct sequence; module.finish gives - // the list of dojo.requires that must be re-applied once all target modules are available; - // make a synthetic module to execute the dojo.require's in the correct order - - // compute a guaranteed-unique mid for the synthetic finish module; remember the finish vector; remove it from the reference module - // TODO: can we just leave the module.finish...what's it hurting? - var finishMid = mid + "*finish", - finish = module.finish; - delete module.finish; - - def(finishMid, ["dojo", ("dojo/require!" + finish.join(",")).replace(/\./g, "/")], function(dojo){ - forEach(finish, function(mid){ dojo.require(mid); }); - }); - // unshift, not push, which causes the current traversal to be reattempted from the top - execQ.unshift(getModule(finishMid)); - } - onLoadCallback(); - }else{ - text = transformToAmd(module, text); - if(text){ - evalModuleText(text, module); - onLoadCallback(); - }else{ - // if transformToAmd returned falsy, then the module was already AMD and it can be script-injected - // do so to improve debugability(even though it means another download...which probably won't happen with a good browser cache) - injectingModule = module; - req.injectUrl(fixupUrl(url), onLoadCallback, module); - injectingModule = 0; - } - } - }; - - req.trace("loader-inject", ["xhr", module.mid, url, legacyMode!=sync]); - if(has("config-dojo-loader-catches")){ - try{ - req.getText(url, legacyMode!=sync, xhrCallback); - }catch(e){ - signal(error, makeError("xhrInjectFailed", [module, e])); - } - }else{ - req.getText(url, legacyMode!=sync, xhrCallback); - } - return; - } - } // else async mode or fell through in xdomain loading mode; either way, load by script injection - req.trace("loader-inject", ["script", module.mid, url]); - injectingModule = module; - req.injectUrl(fixupUrl(url), onLoadCallback, module); - injectingModule = 0; - }, - - defineModule = function(module, deps, def){ - req.trace("loader-define-module", [module.mid, deps]); - - if(has("dojo-combo-api") && module.plugin && module.plugin.isCombo){ - // the module is a plugin resource loaded by the combo service - // note: check for module.plugin should be enough since normal plugin resources should - // not follow this path; module.plugin.isCombo is future-proofing belt and suspenders - module.result = isFunction(def) ? def() : def; - setArrived(module); - finishExec(module); - return module; - } - - var mid = module.mid; - if(module.injected === arrived){ - signal(error, makeError("multipleDefine", module)); - return module; - } - mix(module, { - deps: deps, - def: def, - cjs: { - id: module.mid, - uri: module.url, - exports: (module.result = {}), - setExports: function(exports){ - module.cjs.exports = exports; - }, - config:function(){ - return module.config; - } - } - }); - - // resolve deps with respect to this module - for(var i = 0; deps[i]; i++){ - deps[i] = getModule(deps[i], module); - } - - if(has("dojo-sync-loader") && legacyMode && !waiting[mid]){ - // the module showed up without being asked for; it was probably in a