diff --git a/app/Actions/NotificationChannels/AddChannel.php b/app/Actions/NotificationChannels/AddChannel.php index 2c3f7b8c..11e98aef 100644 --- a/app/Actions/NotificationChannels/AddChannel.php +++ b/app/Actions/NotificationChannels/AddChannel.php @@ -19,6 +19,7 @@ public function add(User $user, array $input): void 'user_id' => $user->id, 'provider' => $input['provider'], 'label' => $input['label'], + 'project_id' => isset($input['global']) && $input['global'] ? null : $user->current_project_id, ]); $this->validateType($channel, $input); $channel->data = $channel->provider()->createData($input); diff --git a/app/Actions/NotificationChannels/EditChannel.php b/app/Actions/NotificationChannels/EditChannel.php new file mode 100644 index 00000000..fbba3de7 --- /dev/null +++ b/app/Actions/NotificationChannels/EditChannel.php @@ -0,0 +1,34 @@ +validate($input); + + $notificationChannel->label = $input['label']; + $notificationChannel->project_id = isset($input['global']) && $input['global'] ? null : $user->current_project_id; + + $notificationChannel->save(); + } + + /** + * @throws ValidationException + */ + private function validate(array $input): void + { + $rules = [ + 'label' => [ + 'required', + ], + ]; + Validator::make($input, $rules)->validate(); + } +} diff --git a/app/Actions/SourceControl/EditSourceControl.php b/app/Actions/SourceControl/EditSourceControl.php index a7665e5a..54ae1d1f 100644 --- a/app/Actions/SourceControl/EditSourceControl.php +++ b/app/Actions/SourceControl/EditSourceControl.php @@ -14,7 +14,7 @@ public function edit(SourceControl $sourceControl, User $user, array $input): vo $this->validate($input); $sourceControl->profile = $input['name']; - $sourceControl->url = isset($input['url']) ? $input['url'] : null; + $sourceControl->url = $input['url'] ?? null; $sourceControl->project_id = isset($input['global']) && $input['global'] ? null : $user->current_project_id; $this->validateProvider($sourceControl, $input); diff --git a/app/Actions/StorageProvider/CreateStorageProvider.php b/app/Actions/StorageProvider/CreateStorageProvider.php index 0f8ba733..7fcbad7e 100644 --- a/app/Actions/StorageProvider/CreateStorageProvider.php +++ b/app/Actions/StorageProvider/CreateStorageProvider.php @@ -21,6 +21,7 @@ public function create(User $user, array $input): void 'user_id' => $user->id, 'provider' => $input['provider'], 'profile' => $input['name'], + 'project_id' => isset($input['global']) && $input['global'] ? null : $user->current_project_id, ]); $this->validateProvider($input, $storageProvider->provider()->validationRules()); diff --git a/app/Actions/StorageProvider/EditStorageProvider.php b/app/Actions/StorageProvider/EditStorageProvider.php new file mode 100644 index 00000000..0d33c808 --- /dev/null +++ b/app/Actions/StorageProvider/EditStorageProvider.php @@ -0,0 +1,34 @@ +validate($input); + + $storageProvider->profile = $input['name']; + $storageProvider->project_id = isset($input['global']) && $input['global'] ? null : $user->current_project_id; + + $storageProvider->save(); + } + + /** + * @throws ValidationException + */ + private function validate(array $input): void + { + $rules = [ + 'name' => [ + 'required', + ], + ]; + Validator::make($input, $rules)->validate(); + } +} diff --git a/app/Http/Controllers/SSHKeyController.php b/app/Http/Controllers/SSHKeyController.php index c576ebde..28d0679b 100644 --- a/app/Http/Controllers/SSHKeyController.php +++ b/app/Http/Controllers/SSHKeyController.php @@ -29,7 +29,7 @@ public function store(Server $server, Request $request): HtmxResponse { $this->authorize('manage', $server); - /** @var \App\Models\SshKey $key */ + /** @var SshKey $key */ $key = app(CreateSshKey::class)->create( $request->user(), $request->input() diff --git a/app/Http/Controllers/Settings/NotificationChannelController.php b/app/Http/Controllers/Settings/NotificationChannelController.php index c007c4f4..9facf8cb 100644 --- a/app/Http/Controllers/Settings/NotificationChannelController.php +++ b/app/Http/Controllers/Settings/NotificationChannelController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Settings; use App\Actions\NotificationChannels\AddChannel; +use App\Actions\NotificationChannels\EditChannel; use App\Facades\Toast; use App\Helpers\HtmxResponse; use App\Http\Controllers\Controller; @@ -13,11 +14,17 @@ class NotificationChannelController extends Controller { - public function index(): View + public function index(Request $request): View { - return view('settings.notification-channels.index', [ - 'channels' => NotificationChannel::query()->latest()->get(), - ]); + $data = [ + 'channels' => NotificationChannel::getByProjectId(auth()->user()->current_project_id), + ]; + + if ($request->has('edit')) { + $data['editChannel'] = NotificationChannel::find($request->input('edit')); + } + + return view('settings.notification-channels.index', $data); } public function add(Request $request): HtmxResponse @@ -32,6 +39,19 @@ public function add(Request $request): HtmxResponse return htmx()->redirect(route('settings.notification-channels')); } + public function update(NotificationChannel $notificationChannel, Request $request): HtmxResponse + { + app(EditChannel::class)->edit( + $notificationChannel, + $request->user(), + $request->input(), + ); + + Toast::success('Channel updated.'); + + return htmx()->redirect(route('settings.notification-channels')); + } + public function delete(int $id): RedirectResponse { $channel = NotificationChannel::query()->findOrFail($id); diff --git a/app/Http/Controllers/Settings/SourceControlController.php b/app/Http/Controllers/Settings/SourceControlController.php index 55a9ea9e..1c6f89f7 100644 --- a/app/Http/Controllers/Settings/SourceControlController.php +++ b/app/Http/Controllers/Settings/SourceControlController.php @@ -18,7 +18,7 @@ class SourceControlController extends Controller public function index(Request $request): View { $data = [ - 'sourceControls' => SourceControl::getByCurrentProject(), + 'sourceControls' => SourceControl::getByProjectId(auth()->user()->current_project_id), ]; if ($request->has('edit')) { diff --git a/app/Http/Controllers/Settings/StorageProviderController.php b/app/Http/Controllers/Settings/StorageProviderController.php index fc5b12b9..c65990db 100644 --- a/app/Http/Controllers/Settings/StorageProviderController.php +++ b/app/Http/Controllers/Settings/StorageProviderController.php @@ -4,6 +4,7 @@ use App\Actions\StorageProvider\CreateStorageProvider; use App\Actions\StorageProvider\DeleteStorageProvider; +use App\Actions\StorageProvider\EditStorageProvider; use App\Facades\Toast; use App\Helpers\HtmxResponse; use App\Http\Controllers\Controller; @@ -14,11 +15,17 @@ class StorageProviderController extends Controller { - public function index(): View + public function index(Request $request): View { - return view('settings.storage-providers.index', [ - 'providers' => auth()->user()->storageProviders, - ]); + $data = [ + 'providers' => StorageProvider::getByProjectId(auth()->user()->current_project_id), + ]; + + if ($request->has('edit')) { + $data['editProvider'] = StorageProvider::find($request->input('edit')); + } + + return view('settings.storage-providers.index', $data); } public function connect(Request $request): HtmxResponse @@ -33,6 +40,19 @@ public function connect(Request $request): HtmxResponse return htmx()->redirect(route('settings.storage-providers')); } + public function update(StorageProvider $storageProvider, Request $request): HtmxResponse + { + app(EditStorageProvider::class)->edit( + $storageProvider, + $request->user(), + $request->input(), + ); + + Toast::success('Provider updated.'); + + return htmx()->redirect(route('settings.storage-providers')); + } + public function delete(StorageProvider $storageProvider): RedirectResponse { try { diff --git a/app/Models/NotificationChannel.php b/app/Models/NotificationChannel.php index 803cc07c..5f55bafe 100644 --- a/app/Models/NotificationChannel.php +++ b/app/Models/NotificationChannel.php @@ -3,7 +3,9 @@ namespace App\Models; use App\Notifications\NotificationInterface; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Notifications\Notifiable; /** @@ -12,6 +14,7 @@ * @property array data * @property string label * @property bool connected + * @property int $project_id */ class NotificationChannel extends AbstractModel { @@ -24,6 +27,7 @@ class NotificationChannel extends AbstractModel 'data', 'connected', 'is_default', + 'project_id', ]; protected $casts = [ @@ -47,4 +51,16 @@ public static function notifyAll(NotificationInterface $notification): void $channel->notify($notification); } } + + public function project(): BelongsTo + { + return $this->belongsTo(Project::class); + } + + public static function getByProjectId(int $projectId): Collection + { + return self::query() + ->where('project_id', $projectId) + ->orWhereNull('project_id')->get(); + } } diff --git a/app/Models/SourceControl.php b/app/Models/SourceControl.php index 8f4c4c7d..8d872545 100755 --- a/app/Models/SourceControl.php +++ b/app/Models/SourceControl.php @@ -57,10 +57,10 @@ public function project(): BelongsTo return $this->belongsTo(Project::class); } - public static function getByCurrentProject(): Collection + public static function getByProjectId(int $projectId): Collection { return self::query() - ->where('project_id', auth()->user()->current_project_id) + ->where('project_id', $projectId) ->orWhereNull('project_id')->get(); } } diff --git a/app/Models/StorageProvider.php b/app/Models/StorageProvider.php index 7fe9b4c1..a7f5d2a1 100644 --- a/app/Models/StorageProvider.php +++ b/app/Models/StorageProvider.php @@ -2,6 +2,7 @@ namespace App\Models; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -12,6 +13,7 @@ * @property string $provider * @property array $credentials * @property User $user + * @property int $project_id */ class StorageProvider extends AbstractModel { @@ -22,11 +24,13 @@ class StorageProvider extends AbstractModel 'profile', 'provider', 'credentials', + 'project_id', ]; protected $casts = [ 'user_id' => 'integer', 'credentials' => 'encrypted:array', + 'project_id' => 'integer', ]; public function user(): BelongsTo @@ -45,4 +49,16 @@ public function backups(): HasMany { return $this->hasMany(Backup::class, 'storage_id'); } + + public function project(): BelongsTo + { + return $this->belongsTo(Project::class); + } + + public static function getByProjectId(int $projectId): Collection + { + return self::query() + ->where('project_id', $projectId) + ->orWhereNull('project_id')->get(); + } } diff --git a/app/Models/User.php b/app/Models/User.php index fe16eadb..f2684310 100755 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -106,24 +106,6 @@ public function storageProvider(string $provider): HasOne return $this->hasOne(StorageProvider::class)->where('provider', $provider); } - public function connectedStorageProviders(): HasMany - { - return $this->storageProviders()->where('connected', true); - } - - public function connectedSourceControls(): array - { - $connectedSourceControls = []; - $sourceControls = $this->sourceControls() - ->where('connected', 1) - ->get(['provider']); - foreach ($sourceControls as $sourceControl) { - $connectedSourceControls[] = $sourceControl->provider; - } - - return $connectedSourceControls; - } - public function projects(): BelongsToMany { return $this->belongsToMany(Project::class, 'user_project')->withTimestamps(); @@ -134,11 +116,6 @@ public function currentProject(): HasOne return $this->HasOne(Project::class, 'id', 'current_project_id'); } - public function isMemberOfProject(Project $project): bool - { - return $project->user_id === $this->id; - } - public function createDefaultProject(): Project { $project = $this->projects()->first(); diff --git a/database/migrations/2024_06_24_211155_add_project_id_to_notification_channels_table.php b/database/migrations/2024_06_24_211155_add_project_id_to_notification_channels_table.php new file mode 100644 index 00000000..fedba8c2 --- /dev/null +++ b/database/migrations/2024_06_24_211155_add_project_id_to_notification_channels_table.php @@ -0,0 +1,22 @@ +unsignedBigInteger('project_id')->nullable(); + }); + } + + public function down(): void + { + Schema::table('notification_channels', function (Blueprint $table) { + $table->dropColumn('project_id'); + }); + } +}; diff --git a/database/migrations/2024_06_25_202655_add_project_id_to_storage_providers_table.php b/database/migrations/2024_06_25_202655_add_project_id_to_storage_providers_table.php new file mode 100644 index 00000000..1eaeb0a4 --- /dev/null +++ b/database/migrations/2024_06_25_202655_add_project_id_to_storage_providers_table.php @@ -0,0 +1,22 @@ +unsignedBigInteger('project_id')->nullable(); + }); + } + + public function down(): void + { + Schema::table('storage_providers', function (Blueprint $table) { + $table->dropColumn('project_id'); + }); + } +}; diff --git a/resources/views/databases/partials/create-backup-modal.blade.php b/resources/views/databases/partials/create-backup-modal.blade.php index 6fc1782a..30ec3266 100644 --- a/resources/views/databases/partials/create-backup-modal.blade.php +++ b/resources/views/databases/partials/create-backup-modal.blade.php @@ -35,7 +35,7 @@ class="p-6"
- @foreach (auth()->user()->storageProviders as $st) + @foreach (\App\Models\StorageProvider::getByProjectId(auth()->user()->current_project_id) as $st) diff --git a/resources/views/settings/notification-channels/partials/add-channel.blade.php b/resources/views/settings/notification-channels/partials/add-channel.blade.php index ab413c3c..e3a7b0b1 100644 --- a/resources/views/settings/notification-channels/partials/add-channel.blade.php +++ b/resources/views/settings/notification-channels/partials/add-channel.blade.php @@ -100,6 +100,15 @@ class="mt-1 w-full" @enderror
+
+ + Is Global (Accessible in all projects) + + @error("global") + + @enderror +
+
{{ __("Cancel") }} diff --git a/resources/views/settings/notification-channels/partials/channels-list.blade.php b/resources/views/settings/notification-channels/partials/channels-list.blade.php index db1b363a..5ca02785 100644 --- a/resources/views/settings/notification-channels/partials/channels-list.blade.php +++ b/resources/views/settings/notification-channels/partials/channels-list.blade.php @@ -16,13 +16,29 @@ @include("settings.notification-channels.partials.icons." . $channel->provider)
- {{ $channel->label }} +
+ {{ $channel->label }} + @if (! $channel->project_id) + GLOBAL + @endif +
+ + + @@ -34,6 +50,12 @@ @endforeach @include("settings.notification-channels.partials.delete-channel") + +
+ @if (isset($editChannel)) + @include("settings.notification-channels.partials.edit-notification-channel", ["notificationChannel" => $editChannel]) + @endif +
@else
diff --git a/resources/views/settings/notification-channels/partials/edit-notification-channel.blade.php b/resources/views/settings/notification-channels/partials/edit-notification-channel.blade.php new file mode 100644 index 00000000..9376e4ed --- /dev/null +++ b/resources/views/settings/notification-channels/partials/edit-notification-channel.blade.php @@ -0,0 +1,59 @@ + +
$notificationChannel->id]) }}" + hx-swap="outerHTML" + hx-select="#edit-notification-channel-form" + hx-ext="disable-element" + hx-disable-element="#btn-edit-notification-channel" + class="p-6" + > + @csrf + +

+ {{ __("Edit Channel") }} +

+ +
+ + + @error("label") + + @enderror +
+ +
+ + Is Global (Accessible in all projects) + + @error("global") + + @enderror +
+ +
+ + {{ __("Cancel") }} + + + + {{ __("Save") }} + +
+
+
diff --git a/resources/views/settings/source-controls/partials/edit-source-control.blade.php b/resources/views/settings/source-controls/partials/edit-source-control.blade.php index e927255a..959b6ee8 100644 --- a/resources/views/settings/source-controls/partials/edit-source-control.blade.php +++ b/resources/views/settings/source-controls/partials/edit-source-control.blade.php @@ -109,7 +109,7 @@ class="text-primary-500"
@endif +
+ + Is Global (Accessible in all projects) + + @error("global") + + @enderror +
+
{{ __("Cancel") }} diff --git a/resources/views/settings/storage-providers/partials/edit-provider.blade.php b/resources/views/settings/storage-providers/partials/edit-provider.blade.php new file mode 100644 index 00000000..6ae8b56a --- /dev/null +++ b/resources/views/settings/storage-providers/partials/edit-provider.blade.php @@ -0,0 +1,59 @@ + +
$storageProvider->id]) }}" + hx-swap="outerHTML" + hx-select="#edit-storage-provider-form" + hx-ext="disable-element" + hx-disable-element="#btn-edit-storage-provider" + class="p-6" + > + @csrf + +

+ {{ __("Edit Provider") }} +

+ +
+ + + @error("name") + + @enderror +
+ +
+ + Is Global (Accessible in all projects) + + @error("global") + + @enderror +
+ +
+ + {{ __("Cancel") }} + + + + {{ __("Save") }} + +
+
+
diff --git a/resources/views/settings/storage-providers/partials/providers-list.blade.php b/resources/views/settings/storage-providers/partials/providers-list.blade.php index 4ce2facf..0b817268 100644 --- a/resources/views/settings/storage-providers/partials/providers-list.blade.php +++ b/resources/views/settings/storage-providers/partials/providers-list.blade.php @@ -18,13 +18,29 @@ class="h-10 w-10" />
- {{ $provider->profile }} +
+ {{ $provider->profile }} + @if (! $provider->project_id) + GLOBAL + @endif +
+ + + @@ -36,6 +52,12 @@ class="h-10 w-10" @endforeach @include("settings.storage-providers.partials.delete-storage-provider") + +
+ @if (isset($editProvider)) + @include("settings.storage-providers.partials.edit-provider", ["storageProvider" => $editProvider]) + @endif +
@else
diff --git a/resources/views/sites/partials/create/fields/source-control.blade.php b/resources/views/sites/partials/create/fields/source-control.blade.php index 313f4327..f8a8b66d 100644 --- a/resources/views/sites/partials/create/fields/source-control.blade.php +++ b/resources/views/sites/partials/create/fields/source-control.blade.php @@ -3,7 +3,7 @@
- @foreach (\App\Models\SourceControl::getByCurrentProject() as $sourceControl) + @foreach (\App\Models\SourceControl::getByProjectId(auth()->user()->current_project_id) as $sourceControl)