diff --git a/src/Config/web.characterfilter.php b/src/Config/web.characterfilter.php new file mode 100644 index 000000000..f1e3dbf9b --- /dev/null +++ b/src/Config/web.characterfilter.php @@ -0,0 +1,33 @@ + 'scopes', 'src' => 'seatcore::fastlookup.scopes', 'path' => 'refresh_token', 'field' => 'scopes', 'label' => 'Scopes'], + ['name' => 'character', 'src' => 'seatcore::fastlookup.characters', 'path' => '', 'field' => 'character_id', 'label' => 'Character'], + ['name' => 'title', 'src' => 'seatcore::fastlookup.titles', 'path' => 'titles', 'field' => 'id', 'label' => 'Title'], + ['name' => 'corporation', 'src' => 'seatcore::fastlookup.corporations', 'path' => 'affiliation', 'field' => 'corporation_id', 'label' => 'Corporation'], + ['name' => 'alliance', 'src' => 'seatcore::fastlookup.alliances', 'path' => 'affiliation', 'field' => 'alliance_id', 'label' => 'Alliance'], + ['name' => 'skill', 'src' => 'seatcore::fastlookup.skills', 'path' => 'skills', 'field' => 'skill_id', 'label' => 'Skill'], + ['name' => 'skill_level', 'src' => [['id' => 1, 'text' => 'Level 1'], ['id' => 2, 'text' => 'Level 2'], ['id' => 3, 'text' => 'Level 3'], ['id' => 4, 'text' => 'Level 4'], ['id' => 5, 'text' => 'Level 5']], 'path' => 'skills', 'field' => 'trained_skill_level', 'label' => 'Skill Level'], + ['name' => 'type', 'src' => 'seatcore::fastlookup.items', 'path' => 'assets', 'field' => 'type_id', 'label' => 'Item'], + ['name' => 'role', 'src' => 'seatcore::fastlookup.roles', 'path' => 'corporation_roles', 'field' => 'role', 'label' => 'Role'], +]; diff --git a/src/Events/CharacterFilterDataUpdate.php b/src/Events/CharacterFilterDataUpdate.php new file mode 100644 index 000000000..415b4ec23 --- /dev/null +++ b/src/Events/CharacterFilterDataUpdate.php @@ -0,0 +1,45 @@ +character = $character; + } +} diff --git a/src/Http/Composers/CharacterFilter.php b/src/Http/Composers/CharacterFilter.php new file mode 100644 index 000000000..01f16642f --- /dev/null +++ b/src/Http/Composers/CharacterFilter.php @@ -0,0 +1,45 @@ +with('characterFilterRules', $newrules); + } +} diff --git a/src/Http/Controllers/Configuration/ScheduleController.php b/src/Http/Controllers/Configuration/ScheduleController.php index 3e4333d5d..64a9eb6b1 100644 --- a/src/Http/Controllers/Configuration/ScheduleController.php +++ b/src/Http/Controllers/Configuration/ScheduleController.php @@ -23,9 +23,12 @@ namespace Seat\Web\Http\Controllers\Configuration; use Artisan; +use Illuminate\Http\Request; +use Seat\Eveapi\Models\RefreshToken; use Seat\Services\Models\Schedule; use Seat\Web\Http\Controllers\Controller; use Seat\Web\Http\Validation\NewSchedule; +use Seat\Web\Models\CharacterSchedulingRule; /** * Class ScheduleController. @@ -52,11 +55,12 @@ public function listSchedule() 'every five minutes' => '*/5 * * * *', 'every ten minutes' => '*/10 * * * *', 'every thirty minutes' => '*/30 * * * *', - ]; + $scheduling_rules = CharacterSchedulingRule::all(); + return view('web::configuration.schedule.view', - compact('schedule', 'commands', 'expressions')); + compact('schedule', 'commands', 'expressions', 'scheduling_rules')); } /** @@ -85,4 +89,55 @@ public function deleteSchedule(int $schedule_id) return redirect()->back() ->with('success', 'Schedule entry deleted!'); } + + public function createSchedulingRule(Request $request) + { + $request->validate([ + 'filters' => 'required|json', + 'name' => 'required|string', + 'time' => 'required|numeric', + 'timeunit' => 'required|in:hour,day,week', + ]); + + // $time_modifier: conversion factor from timeunit to seconds + if($request->timeunit === 'hour') { + $time_modifier = 60 * 60; + } elseif ($request->timeunit === 'day') { + $time_modifier = 60 * 60 * 24; + } elseif ($request->timeunit === 'week') { + $time_modifier = 60 * 60 * 24 * 7; + } + $time = $request->time * $time_modifier; + + $rule = CharacterSchedulingRule::where('name', $request->name)->first(); + if($rule === null) { + $rule = new CharacterSchedulingRule(); + $rule->name = $request->name; + } + $rule->interval = $time; + $rule->filter = $request->filters; + $rule->save(); + + RefreshToken::all()->each(function ($token) { + CharacterSchedulingRule::updateRefreshTokenSchedule($token); + }); + + return redirect()->back() + ->with('success', 'Character Scheduling Rule added!'); + } + + public function deleteSchedulingRule(Request $request) + { + $request->validate([ + 'rule_id' => 'required|numeric', + ]); + + CharacterSchedulingRule::destroy($request->rule_id); + + RefreshToken::all()->each(function ($token) { + CharacterSchedulingRule::updateRefreshTokenSchedule($token); + }); + + return redirect()->back()->with('success', 'Successfully removed character scheduling rule!'); + } } diff --git a/src/Http/Routes/Configuration/Schedule.php b/src/Http/Routes/Configuration/Schedule.php index 11bf0ee67..9f4a2e1d6 100644 --- a/src/Http/Routes/Configuration/Schedule.php +++ b/src/Http/Routes/Configuration/Schedule.php @@ -31,3 +31,11 @@ Route::get('/delete/{schedule_id}') ->name('seatcore::configuration.schedule.delete') ->uses('ScheduleController@deleteSchedule'); + +Route::post('/rules/create') + ->name('seatcore::configuration.schedule.rule.create') + ->uses('ScheduleController@createSchedulingRule'); + +Route::post('/rules/delete') + ->name('seatcore::configuration.schedule.rule.delete') + ->uses('ScheduleController@deleteSchedulingRule'); diff --git a/src/Observers/AbstractSquadObserver.php b/src/Listeners/CharacterFilterDataUpdatedSquads.php similarity index 62% rename from src/Observers/AbstractSquadObserver.php rename to src/Listeners/CharacterFilterDataUpdatedSquads.php index b0189f9aa..82d810e62 100644 --- a/src/Observers/AbstractSquadObserver.php +++ b/src/Listeners/CharacterFilterDataUpdatedSquads.php @@ -20,41 +20,16 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -namespace Seat\Web\Observers; +namespace Seat\Web\Listeners; -use Illuminate\Database\Eloquent\Model; -use Seat\Web\Exceptions\InvalidFilterException; +use Seat\Web\Events\CharacterFilterDataUpdate; use Seat\Web\Models\Squads\Squad; -use Seat\Web\Models\User; -/** - * Class AbstractSquadObserver. - * - * @package Seat\Web\Observers - */ -abstract class AbstractSquadObserver +class CharacterFilterDataUpdatedSquads { - /** - * Return the User owning the model which fired the catch event. - * - * @param \Illuminate\Database\Eloquent\Model $fired_model The model which fired the catch event - * @return \Seat\Web\Models\User|null The user owning this model - */ - abstract protected function findRelatedUser(Model $fired_model): ?User; - - /** - * Update squads to which the user owning model firing the event is member. - * - * @param \Illuminate\Database\Eloquent\Model $fired_model The model which fired the catch event - * - * @throws InvalidFilterException - */ - protected function updateUserSquads(Model $fired_model) + public static function handle(CharacterFilterDataUpdate $event) { - $user = $this->findRelatedUser($fired_model); - - if (! $user) - return; + $user = $event->character->user; $member_squads = $user->squads; diff --git a/src/Listeners/CharacterFilterDataUpdatedTokens.php b/src/Listeners/CharacterFilterDataUpdatedTokens.php new file mode 100644 index 000000000..0dfee262f --- /dev/null +++ b/src/Listeners/CharacterFilterDataUpdatedTokens.php @@ -0,0 +1,34 @@ +character->refresh_token); + } +} diff --git a/src/Models/CharacterSchedulingRule.php b/src/Models/CharacterSchedulingRule.php new file mode 100644 index 000000000..471a5f6f7 --- /dev/null +++ b/src/Models/CharacterSchedulingRule.php @@ -0,0 +1,93 @@ +filter); + } + + /** + * Recomputes the update interval of a character and saves it in the refresh_token_schedules table. + * + * @param RefreshToken $token + * @return void + */ + public static function updateRefreshTokenSchedule(RefreshToken $token): void + { + $schedule = $token->token_schedule; + + if($schedule === null) { + $schedule = new RefreshTokenSchedule(); + $schedule->character_id = $token->character_id; + } + + $schedule->update_interval = self::getCharacterSchedulingInterval($token->character); + $schedule->save(); + } + + /** + * Computes the scheduling interval from the character scheduling rules for a character. + * + * @param CharacterInfo $character + * @return int + */ + private static function getCharacterSchedulingInterval(CharacterInfo $character): int + { + $scheduling_rules = CharacterSchedulingRule::orderBy('interval', 'asc')->get(); + + foreach ($scheduling_rules as $rule) { + if($rule->isEligible($character)) { + return $rule->interval; + } + } + + return 60 * 60; // 1 hour + } +} diff --git a/src/Observers/AbstractCharacterFilterObserver.php b/src/Observers/AbstractCharacterFilterObserver.php new file mode 100644 index 000000000..c4b16cfad --- /dev/null +++ b/src/Observers/AbstractCharacterFilterObserver.php @@ -0,0 +1,62 @@ +findRelatedCharacter($fired_model); + + if (! $character) + return; + + event(new CharacterFilterDataUpdate($character)); + } +} diff --git a/src/Observers/CharacterAffiliationObserver.php b/src/Observers/CharacterAffiliationObserver.php index 37a1fd4da..116179fa5 100644 --- a/src/Observers/CharacterAffiliationObserver.php +++ b/src/Observers/CharacterAffiliationObserver.php @@ -24,6 +24,7 @@ use Illuminate\Database\Eloquent\Model; use Seat\Eveapi\Models\Character\CharacterAffiliation; +use Seat\Eveapi\Models\Character\CharacterInfo; use Seat\Web\Models\User; /** @@ -31,14 +32,14 @@ * * @package Seat\Web\Observers */ -class CharacterAffiliationObserver extends AbstractSquadObserver +class CharacterAffiliationObserver extends AbstractCharacterFilterObserver { /** * @param \Seat\Eveapi\Models\Character\CharacterAffiliation $affiliation */ public function created(CharacterAffiliation $affiliation) { - $this->updateUserSquads($affiliation); + $this->fireCharacterFilterEvent($affiliation); } /** @@ -46,19 +47,18 @@ public function created(CharacterAffiliation $affiliation) */ public function updated(CharacterAffiliation $affiliation) { - $this->updateUserSquads($affiliation); + $this->fireCharacterFilterEvent($affiliation); } /** - * {@inheritdoc} + * Return the User owning the model which fired the catch event. + * + * @param \Illuminate\Database\Eloquent\Model $fired_model The model which fired the catch event + * @return ?CharacterInfo The character that is affected by this update */ - protected function findRelatedUser(Model $fired_model): ?User + protected function findRelatedCharacter(Model $fired_model): ?CharacterInfo { - // retrieve user related to the character affiliation - return User::with('squads') - ->standard() - ->whereHas('characters', function ($query) use ($fired_model) { - $query->where('character_infos.character_id', $fired_model->character_id); - })->first(); + // CharacterAffiliation links to UniverseName instead of CharacterInfo + return CharacterInfo::find($fired_model->character_id); } } diff --git a/src/Observers/CharacterAssetObserver.php b/src/Observers/CharacterAssetObserver.php index 2f70f5d70..67f2b9f15 100644 --- a/src/Observers/CharacterAssetObserver.php +++ b/src/Observers/CharacterAssetObserver.php @@ -24,6 +24,7 @@ use Illuminate\Database\Eloquent\Model; use Seat\Eveapi\Models\Assets\CharacterAsset; +use Seat\Eveapi\Models\Character\CharacterInfo; use Seat\Web\Models\User; /** @@ -31,14 +32,14 @@ * * @package Seat\Web\Observers */ -class CharacterAssetObserver extends AbstractSquadObserver +class CharacterAssetObserver extends AbstractCharacterFilterObserver { /** * @param \Seat\Eveapi\Models\Assets\CharacterAsset $asset */ public function created(CharacterAsset $asset) { - $this->updateUserSquads($asset); + $this->fireCharacterFilterEvent($asset); } /** @@ -46,7 +47,7 @@ public function created(CharacterAsset $asset) */ public function updated(CharacterAsset $asset) { - $this->updateUserSquads($asset); + $this->fireCharacterFilterEvent($asset); } /** @@ -54,19 +55,17 @@ public function updated(CharacterAsset $asset) */ public function deleted(CharacterAsset $asset) { - $this->updateUserSquads($asset); + $this->fireCharacterFilterEvent($asset); } /** - * {@inheritdoc} + * Return the User owning the model which fired the catch event. + * + * @param \Illuminate\Database\Eloquent\Model $fired_model The model which fired the catch event + * @return ?CharacterInfo The character that is affected by this update */ - protected function findRelatedUser(Model $fired_model): ?User + protected function findRelatedCharacter(Model $fired_model): ?CharacterInfo { - // retrieve user related to the character affiliation - return User::with('squads') - ->standard() - ->whereHas('characters', function ($query) use ($fired_model) { - $query->where('character_infos.character_id', $fired_model->character_id); - })->first(); + return $fired_model->character; } } diff --git a/src/Observers/CharacterRoleObserver.php b/src/Observers/CharacterRoleObserver.php index 82cc1394f..688f6f9b1 100644 --- a/src/Observers/CharacterRoleObserver.php +++ b/src/Observers/CharacterRoleObserver.php @@ -25,6 +25,7 @@ use Exception; use Illuminate\Database\Eloquent\Model; use Seat\Eveapi\Bus\Corporation; +use Seat\Eveapi\Models\Character\CharacterInfo; use Seat\Eveapi\Models\Character\CharacterRole; use Seat\Eveapi\Models\RefreshToken; use Seat\Web\Models\User; @@ -34,14 +35,14 @@ * * @package Seat\Web\Observers */ -class CharacterRoleObserver extends AbstractSquadObserver +class CharacterRoleObserver extends AbstractCharacterFilterObserver { /** * @param \Seat\Eveapi\Models\Character\CharacterRole $role */ public function created(CharacterRole $role) { - $this->updateUserSquads($role); + $this->fireCharacterFilterEvent($role); // in case the created role is not a Director role, ignore if ($role->role != 'Director') @@ -67,7 +68,7 @@ public function created(CharacterRole $role) */ public function updated(CharacterRole $role) { - $this->updateUserSquads($role); + $this->fireCharacterFilterEvent($role); } /** @@ -75,19 +76,17 @@ public function updated(CharacterRole $role) */ public function deleted(CharacterRole $role) { - $this->updateUserSquads($role); + $this->fireCharacterFilterEvent($role); } /** - * {@inheritdoc} + * Return the User owning the model which fired the catch event. + * + * @param \Illuminate\Database\Eloquent\Model $fired_model The model which fired the catch event + * @return ?CharacterInfo The character that is affected by this update */ - protected function findRelatedUser(Model $fired_model): ?User + protected function findRelatedCharacter(Model $fired_model): ?CharacterInfo { - // retrieve user related to the character affiliation - return User::with('squads') - ->standard() - ->whereHas('characters', function ($query) use ($fired_model) { - $query->where('character_infos.character_id', $fired_model->character_id); - })->first(); + return $fired_model->character; } } diff --git a/src/Observers/CharacterSkillObserver.php b/src/Observers/CharacterSkillObserver.php index eaa579773..f3c7e1820 100644 --- a/src/Observers/CharacterSkillObserver.php +++ b/src/Observers/CharacterSkillObserver.php @@ -23,6 +23,7 @@ namespace Seat\Web\Observers; use Illuminate\Database\Eloquent\Model; +use Seat\Eveapi\Models\Character\CharacterInfo; use Seat\Eveapi\Models\Character\CharacterSkill; use Seat\Web\Models\User; @@ -31,14 +32,14 @@ * * @package Seat\Web\Observers */ -class CharacterSkillObserver extends AbstractSquadObserver +class CharacterSkillObserver extends AbstractCharacterFilterObserver { /** * @param \Seat\Eveapi\Models\Character\CharacterSkill $skill */ public function created(CharacterSkill $skill) { - $this->updateUserSquads($skill); + $this->fireCharacterFilterEvent($skill); } /** @@ -46,7 +47,7 @@ public function created(CharacterSkill $skill) */ public function updated(CharacterSkill $skill) { - $this->updateUserSquads($skill); + $this->fireCharacterFilterEvent($skill); } /** @@ -54,7 +55,7 @@ public function updated(CharacterSkill $skill) */ public function deleted(CharacterSkill $skill) { - $this->updateUserSquads($skill); + $this->fireCharacterFilterEvent($skill); } /** @@ -69,4 +70,15 @@ protected function findRelatedUser(Model $fired_model): ?User $query->where('character_infos.character_id', $fired_model->character_id); })->first(); } + + /** + * Return the User owning the model which fired the catch event. + * + * @param \Illuminate\Database\Eloquent\Model $fired_model The model which fired the catch event + * @return ?CharacterInfo The character that is affected by this update + */ + protected function findRelatedCharacter(Model $fired_model): ?CharacterInfo + { + return $fired_model->character; + } } diff --git a/src/Observers/CharacterTitleObserver.php b/src/Observers/CharacterTitleObserver.php index c45385d9b..d01b033fd 100644 --- a/src/Observers/CharacterTitleObserver.php +++ b/src/Observers/CharacterTitleObserver.php @@ -23,6 +23,7 @@ namespace Seat\Web\Observers; use Illuminate\Database\Eloquent\Model; +use Seat\Eveapi\Models\Character\CharacterInfo; use Seat\Eveapi\Pivot\Character\CharacterTitle; use Seat\Web\Models\User; @@ -31,14 +32,14 @@ * * @package Seat\Web\Observers */ -class CharacterTitleObserver extends AbstractSquadObserver +class CharacterTitleObserver extends AbstractCharacterFilterObserver { /** * @param \Seat\Eveapi\Models\Assets\CharacterAsset $asset */ public function created(CharacterTitle $title) { - $this->updateUserSquads($title); + $this->fireCharacterFilterEvent($title); } /** @@ -46,7 +47,7 @@ public function created(CharacterTitle $title) */ public function updated(CharacterTitle $title) { - $this->updateUserSquads($title); + $this->fireCharacterFilterEvent($title); } /** @@ -54,19 +55,17 @@ public function updated(CharacterTitle $title) */ public function deleted(CharacterTitle $title) { - $this->updateUserSquads($title); + $this->fireCharacterFilterEvent($title); } /** - * {@inheritdoc} + * Return the User owning the model which fired the catch event. + * + * @param \Illuminate\Database\Eloquent\Model $fired_model The model which fired the catch event + * @return ?CharacterInfo The character that is affected by this update */ - protected function findRelatedUser(Model $fired_model): ?User + protected function findRelatedCharacter(Model $fired_model): ?CharacterInfo { - // retrieve user related to the character affiliation - return User::with('squads') - ->standard() - ->whereHas('characters', function ($query) use ($fired_model) { - $query->where('character_infos.character_id', $fired_model->character_id); - })->first(); + return CharacterInfo::find($fired_model->character_id); } } diff --git a/src/Observers/RefreshTokenObserver.php b/src/Observers/RefreshTokenObserver.php index 2466db757..901e91bef 100644 --- a/src/Observers/RefreshTokenObserver.php +++ b/src/Observers/RefreshTokenObserver.php @@ -25,6 +25,7 @@ use Exception; use Illuminate\Database\Eloquent\Model; use Seat\Eveapi\Bus\Character; +use Seat\Eveapi\Models\Character\CharacterInfo; use Seat\Eveapi\Models\RefreshToken; use Seat\Web\Models\User; @@ -33,7 +34,7 @@ * * @package Seat\Web\Observers */ -class RefreshTokenObserver extends AbstractSquadObserver +class RefreshTokenObserver extends AbstractCharacterFilterObserver { /** * @param \Seat\Eveapi\Models\RefreshToken $token @@ -45,7 +46,7 @@ public function created(RefreshToken $token) $job->fire(); // enqueue squads update - $this->updateUserSquads($token); + $this->fireCharacterFilterEvent($token); } catch (Exception $e) { logger()->error($e->getMessage()); } @@ -57,7 +58,7 @@ public function created(RefreshToken $token) public function updated(RefreshToken $token) { try { - $this->updateUserSquads($token); + $this->fireCharacterFilterEvent($token); } catch (Exception $e) { logger()->error($e->getMessage()); } @@ -77,7 +78,7 @@ public function softDeleted(RefreshToken $token) public function deleted(RefreshToken $token) { try { - $this->updateUserSquads($token); + $this->fireCharacterFilterEvent($token); } catch (Exception $e) { logger()->error($e->getMessage()); } @@ -93,18 +94,20 @@ public function restored(RefreshToken $token) $job->fire(); // enqueue squads update - $this->updateUserSquads($token); + $this->fireCharacterFilterEvent($token); } catch (Exception $e) { logger()->error($e->getMessage()); } } /** - * {@inheritdoc} + * Return the User owning the model which fired the catch event. + * + * @param \Illuminate\Database\Eloquent\Model $fired_model The model which fired the catch event + * @return ?CharacterInfo The character that is affected by this update */ - protected function findRelatedUser(Model $fired_model): ?User + protected function findRelatedCharacter(Model $fired_model): ?CharacterInfo { - return User::with('squads') - ->find($fired_model->user_id); + return $fired_model->character; } } diff --git a/src/WebServiceProvider.php b/src/WebServiceProvider.php index 8c2a918ed..e503a832e 100644 --- a/src/WebServiceProvider.php +++ b/src/WebServiceProvider.php @@ -28,6 +28,7 @@ use Illuminate\Foundation\AliasLoader; use Illuminate\Pagination\Paginator; use Illuminate\Routing\Router; +use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Validator; use Laravel\Horizon\Horizon; @@ -43,11 +44,13 @@ use Seat\Web\Commands\Seat\Admin\Login as AdminLogin; use Seat\Web\Database\Seeders\ScheduleSeeder; use Seat\Web\Events\Attempt; +use Seat\Web\Events\CharacterFilterDataUpdate; use Seat\Web\Events\Login; use Seat\Web\Events\Logout; use Seat\Web\Events\SecLog; use Seat\Web\Http\Composers\AllianceLayout; use Seat\Web\Http\Composers\AllianceMenu; +use Seat\Web\Http\Composers\CharacterFilter; use Seat\Web\Http\Composers\CharacterLayout; use Seat\Web\Http\Composers\CharacterMenu; use Seat\Web\Http\Composers\CorporationLayout; @@ -59,6 +62,8 @@ use Seat\Web\Http\Middleware\Locale; use Seat\Web\Http\Middleware\RegistrationAllowed; use Seat\Web\Http\Middleware\Requirements; +use Seat\Web\Listeners\CharacterFilterDataUpdatedSquads; +use Seat\Web\Listeners\CharacterFilterDataUpdatedTokens; use Seat\Web\Models\Squads\SquadMember; use Seat\Web\Models\Squads\SquadRole; use Seat\Web\Observers\CharacterAffiliationObserver; @@ -243,6 +248,12 @@ private function add_view_composers() 'web::alliance.layouts.view', ], AllianceLayout::class); + // Squad filter rules + $this->app['view']->composer([ + 'web::squads.edit', + 'web::squads.create', + 'web::configuration.schedule.view', + ], CharacterFilter::class); } /** @@ -285,12 +296,14 @@ private function add_events() { // Internal Authentication Events - $this->app->events->listen(LoginEvent::class, Login::class); - $this->app->events->listen(LogoutEvent::class, Logout::class); - $this->app->events->listen(Attempting::class, Attempt::class); + Event::listen(LoginEvent::class, Login::class); + Event::listen(LogoutEvent::class, Logout::class); + Event::listen(Attempting::class, Attempt::class); // Custom Events - $this->app->events->listen('security.log', SecLog::class); + Event::listen('security.log', SecLog::class); + Event::listen(CharacterFilterDataUpdate::class, CharacterFilterDataUpdatedSquads::class); + Event::listen(CharacterFilterDataUpdate::class, CharacterFilterDataUpdatedTokens::class); // Characters / Corporations first auth - Jobs Events CharacterRole::observe(CharacterRoleObserver::class); @@ -346,6 +359,8 @@ public function register() __DIR__ . '/Config/web.locale.php', 'web.locale'); $this->mergeConfigFrom( __DIR__ . '/Config/web.skins.php', 'web.skins'); + $this->mergeConfigFrom( + __DIR__ . '/Config/web.characterfilter.php', 'web.characterfilter'); // Menu Configurations $this->mergeConfigFrom( diff --git a/src/database/migrations/2024_08_15_create_character_scheduling_rules_table.php b/src/database/migrations/2024_08_15_create_character_scheduling_rules_table.php new file mode 100644 index 000000000..37d3b6403 --- /dev/null +++ b/src/database/migrations/2024_08_15_create_character_scheduling_rules_table.php @@ -0,0 +1,52 @@ +bigIncrements('id'); + $table->string('name'); + $table->integer('interval')->unsigned(); + $table->json('filter'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('character_scheduling_rules'); + } +}; diff --git a/src/resources/lang/en/seat.php b/src/resources/lang/en/seat.php index a2ebccd92..3987b986e 100644 --- a/src/resources/lang/en/seat.php +++ b/src/resources/lang/en/seat.php @@ -99,6 +99,11 @@ 'token_status_valid' => 'This character has a valid registered token.', 'token_status_invalid' => 'You don\'t own any valid token for this character.', 'set_default' => 'Set Default', + 'rule' => 'Rule|Rules', + 'hour' => 'hour', + 'week' => 'week', + 'day' => 'day', + 'save' => 'Save', // Requirements 'requirements' => 'Requirements', @@ -375,6 +380,13 @@ 'scheduled_commands' => 'Scheduled Commands', 'choose_prepop' => 'Choose a pre-populated cron expression, or write your own.', 'add_scheduled' => 'Add Scheduled Command', + 'character_scheduling_rule' => 'character scheduling rule', + 'character_scheduling_rules' => 'Character Scheduling Rules', + 'new_character_scheduling_rule' => 'New Character Scheduling Rule', + 'update_interval' => 'Update Interval', + 'name_input_placeholder' => 'Enter a name...', + 'character_scheduling_rules_empty' => 'There are no character scheduling rules defined, using a default of one hour for everyone.', + 'character_scheduling_rules_default' => 'When no rules apply to a character, an update interval of one hour is used.', // Security 'category' => 'Category', diff --git a/src/resources/views/components/filters/buttons/filters.blade.php b/src/resources/views/components/filters/buttons/filters.blade.php index f0722d426..84ab36256 100644 --- a/src/resources/views/components/filters/buttons/filters.blade.php +++ b/src/resources/views/components/filters/buttons/filters.blade.php @@ -1,3 +1,3 @@ - \ No newline at end of file diff --git a/src/resources/views/configuration/schedule/view.blade.php b/src/resources/views/configuration/schedule/view.blade.php index 69f90a3f1..01724c804 100644 --- a/src/resources/views/configuration/schedule/view.blade.php +++ b/src/resources/views/configuration/schedule/view.blade.php @@ -1,141 +1,266 @@ -@extends('web::layouts.grids.3-9') +@extends('web::layouts.app') @section('title', trans('web::seat.schedule')) @section('page_header', trans('web::seat.schedule')) -@section('left') +@section('content') -
-
-

{{ trans('web::seat.new_schedule') }}

-
-
+
+
-
- {{ csrf_field() }} +
+
+

{{ trans('web::seat.new_schedule') }}

+
+
-
+ + {{ csrf_field() }} -
- - - + @foreach($commands as $name => $data) - @endforeach + - -
+ @endforeach -
- - +
- @foreach($expressions as $name => $expression) +
+ + -

-
- {{ trans('web::seat.choose_prepop') }} -

-
+ @endforeach + + +

+
+ {{ trans('web::seat.choose_prepop') }} +

+
+ +
+ + + +
- +
+
+ + {{-- Job Schedule Table --}} - -@stop -@section('right') + {{-- Character scheduling UI --}} +
-
-
-

{{ trans('web::seat.current_schedule') }}

+
+
+
+

{{ trans('web::seat.new_character_scheduling_rule') }}

+
+
+
+ @csrf + +
+ + +
+ +
+ +
+ + +
+
+ +
+ @include('web::components.filters.buttons.filters', ['rules' => [], 'buttonText'=>'Configure Character Filter']) +
+ + + + +
+
+
-
- - - - - - - - - - - - - - @foreach($schedule as $job) +
+
+
+

{{ trans('web::seat.character_scheduling_rules') }}

+
+
+
{{ trans('web::seat.command') }}{{ trans('web::seat.cron') }}{{ trans('web::seat.allow_overlap') }}{{ trans('web::seat.allow_maintenance') }}
+ - - - - - + + + + + + @foreach($scheduling_rules as $scheduling_rule) + + + + + + @endforeach + + @if($scheduling_rules->isEmpty()) + + + + @endif + +
{{ $job->command }}{{ $job->expression }}{{ $job->allow_overlap }}{{ $job->allow_maintenance }} - - - {{ trans('web::seat.delete') }} - - {{trans_choice('web::seat.rule', 1)}}{{trans('web::seat.update_interval')}}{{trans('web::seat.action')}}
{{ $scheduling_rule->name }}{{ \Carbon\CarbonInterval::seconds($scheduling_rule->interval)->cascade() }} + + +
+ @csrf + + +
+
+ {{trans('web::seat.character_scheduling_rules_empty')}} +
+ @if(!$scheduling_rules->isEmpty()) +

+ {{trans('web::seat.character_scheduling_rules_default')}} +

+ @endif +
+
+
+
- @endforeach - - -
- -
+ @include('web::components.filters.modals.filters.filters', [ + 'filters' => $characterFilterRules, + ]) @stop @push('javascript') - - - + + + + + @endpush diff --git a/src/resources/views/squads/create.blade.php b/src/resources/views/squads/create.blade.php index 04ab36dc7..85d5491fc 100644 --- a/src/resources/views/squads/create.blade.php +++ b/src/resources/views/squads/create.blade.php @@ -66,17 +66,7 @@ @include('web::components.filters.modals.filters.filters', [ - 'filters' => [ - (object) ['name' => 'scopes', 'src' => route('seatcore::fastlookup.scopes'), 'path' => 'refresh_token', 'field' => 'scopes', 'label' => 'Scopes'], - (object) ['name' => 'character', 'src' => route('seatcore::fastlookup.characters'), 'path' => '', 'field' => 'character_id', 'label' => 'Character'], - (object) ['name' => 'title', 'src' => route('seatcore::fastlookup.titles'), 'path' => 'titles', 'field' => 'id', 'label' => 'Title'], - (object) ['name' => 'corporation', 'src' => route('seatcore::fastlookup.corporations'), 'path' => 'affiliation', 'field' => 'corporation_id', 'label' => 'Corporation'], - (object) ['name' => 'alliance', 'src' => route('seatcore::fastlookup.alliances'), 'path' => 'affiliation', 'field' => 'alliance_id', 'label' => 'Alliance'], - (object) ['name' => 'skill', 'src' => route('seatcore::fastlookup.skills'), 'path' => 'skills', 'field' => 'skill_id', 'label' => 'Skill'], - (object) ['name' => 'skill_level', 'src' => [['id' => 1, 'text' => 'Level 1'], ['id' => 2, 'text' => 'Level 2'], ['id' => 3, 'text' => 'Level 3'], ['id' => 4, 'text' => 'Level 4'], ['id' => 5, 'text' => 'Level 5']], 'path' => 'skills', 'field' => 'trained_skill_level', 'label' => 'Skill Level'], - (object) ['name' => 'type', 'src' => route('seatcore::fastlookup.items'), 'path' => 'assets', 'field' => 'type_id', 'label' => 'Item'], - (object) ['name' => 'role', 'src' => route('seatcore::fastlookup.roles'), 'path' => 'corporation_roles', 'field' => 'role', 'label' => 'Role'], - ], + 'filters' => $characterFilterRules, ]) @endsection diff --git a/src/resources/views/squads/edit.blade.php b/src/resources/views/squads/edit.blade.php index 475f74ea4..6553313d2 100644 --- a/src/resources/views/squads/edit.blade.php +++ b/src/resources/views/squads/edit.blade.php @@ -83,17 +83,7 @@ @include('web::components.filters.modals.filters.filters', [ - 'filters' => [ - (object) ['name' => 'scopes', 'src' => route('seatcore::fastlookup.scopes'), 'path' => 'refresh_token', 'field' => 'scopes', 'label' => 'Scopes'], - (object) ['name' => 'character', 'src' => route('seatcore::fastlookup.characters'), 'path' => '', 'field' => 'character_id', 'label' => 'Character'], - (object) ['name' => 'title', 'src' => route('seatcore::fastlookup.titles'), 'path' => 'titles', 'field' => 'id', 'label' => 'Title'], - (object) ['name' => 'corporation', 'src' => route('seatcore::fastlookup.corporations'), 'path' => 'affiliation', 'field' => 'corporation_id', 'label' => 'Corporation'], - (object) ['name' => 'alliance', 'src' => route('seatcore::fastlookup.alliances'), 'path' => 'affiliation', 'field' => 'alliance_id', 'label' => 'Alliance'], - (object) ['name' => 'skill', 'src' => route('seatcore::fastlookup.skills'), 'path' => 'skills', 'field' => 'skill_id', 'label' => 'Skill'], - (object) ['name' => 'skill_level', 'src' => [['id' => 1, 'text' => 'Level 1'], ['id' => 2, 'text' => 'Level 2'], ['id' => 3, 'text' => 'Level 3'], ['id' => 4, 'text' => 'Level 4'], ['id' => 5, 'text' => 'Level 5']], 'path' => 'skills', 'field' => 'trained_skill_level', 'label' => 'Skill Level'], - (object) ['name' => 'type', 'src' => route('seatcore::fastlookup.items'), 'path' => 'assets', 'field' => 'type_id', 'label' => 'Item'], - (object) ['name' => 'role', 'src' => route('seatcore::fastlookup.roles'), 'path' => 'corporation_roles', 'field' => 'role', 'label' => 'Role'], - ], + 'filters' => $characterFilterRules, ]) @endsection diff --git a/tests/Acl/AlliancePolicyTest.php b/tests/Acl/AlliancePolicyTest.php index c4fbfabf9..8f0162461 100644 --- a/tests/Acl/AlliancePolicyTest.php +++ b/tests/Acl/AlliancePolicyTest.php @@ -149,6 +149,9 @@ public function testAlliancePermissionsAsWildcard() */ public function testAlliancePermissionsAsDelegatedCharacter() { + // make sure this doesn't generate observer events that trigger the squads logic + CharacterAffiliation::unsetEventDispatcher(); + $permissions = array_keys(require __DIR__ . '/../../src/Config/Permissions/alliance.php'); $user = User::factory()->create(); @@ -199,6 +202,9 @@ public function testAlliancePermissionsAsDelegatedCharacter() */ public function testAlliancePermissionsAsDelegatedCorporation() { + // make sure this doesn't generate observer events that trigger the squads logic + CharacterAffiliation::unsetEventDispatcher(); + $permissions = array_keys(require __DIR__ . '/../../src/Config/Permissions/alliance.php'); $user = User::factory()->create(); diff --git a/tests/Acl/CharacterPolicyTest.php b/tests/Acl/CharacterPolicyTest.php index b909b1282..906286dec 100644 --- a/tests/Acl/CharacterPolicyTest.php +++ b/tests/Acl/CharacterPolicyTest.php @@ -258,6 +258,9 @@ public function testCharacterPermissionsAsDelegatedCharacter() */ public function testCharacterPermissionsAsDelegatedCorporation() { + // make sure this doesn't generate observer events that trigger the squads logic + CharacterAffiliation::unsetEventDispatcher(); + $permissions = array_keys(require __DIR__ . '/../../src/Config/Permissions/character.php'); $user = User::factory()->create(); @@ -313,6 +316,9 @@ public function testCharacterPermissionsAsDelegatedCorporation() */ public function testCharacterPermissionsAsDelegatedAlliance() { + // make sure this doesn't generate observer events that trigger the squads logic + CharacterAffiliation::unsetEventDispatcher(); + $permissions = array_keys(require __DIR__ . '/../../src/Config/Permissions/character.php'); $user = User::factory()->create(); diff --git a/tests/Acl/CorporationPolicyTest.php b/tests/Acl/CorporationPolicyTest.php index 86465b068..81b68b2c2 100644 --- a/tests/Acl/CorporationPolicyTest.php +++ b/tests/Acl/CorporationPolicyTest.php @@ -186,6 +186,9 @@ public function testCorporationPermissionsAsWildcard() */ public function testCorporationPermissionsAsDelegatedCharacter() { + // make sure this doesn't generate observer events that trigger the squads logic + CharacterAffiliation::unsetEventDispatcher(); + $permissions = array_keys(require __DIR__ . '/../../src/Config/Permissions/corporation.php'); $user = User::factory()->create();