Skip to content

Commit

Permalink
#169 - ability to edit and delete opinions for administrator (#201)
Browse files Browse the repository at this point in the history
* Create event

* Add button url

* Fix url

* Secure backend from unauthorised changes

* logic for admin to delete comments

* lint

* pencil is not displayed for admin

* cleaned update opinion method on backend

* Fix unnecessary commits and cs

* Fix DataImporter.php

* Added policies

* Added validation and fixed policy

* change policy to bool

* Change policy logic

* fix updating opinion

* Fix policy

---------

Co-authored-by: JakubKermes <[email protected]>
Co-authored-by: zmigroo <[email protected]>
  • Loading branch information
3 people authored Apr 8, 2024
1 parent 911b999 commit 39e2e60
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 14 deletions.
13 changes: 4 additions & 9 deletions app/Http/Controllers/CityOpinionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,19 @@

use App\Http\Requests\CityOpinionRequest;
use App\Models\CityOpinion;
use Illuminate\Support\Facades\Auth;

class CityOpinionController extends Controller
{
public function store(CityOpinionRequest $request): void
{
$opinion = $request->only(["rating", "content", "city_id"]);
$opinion["user_id"] = Auth::id();

CityOpinion::query()->create($opinion);
$request->user()
->cityOpinions()
->create($request->validated());
}

public function update(CityOpinionRequest $request, CityOpinion $cityOpinion): void
{
$opinion = $request->only(["rating", "content", "city_id"]);
$opinion["user_id"] = Auth::id();

$cityOpinion->update($opinion);
$cityOpinion->update($request->validated());
}

public function destroy(CityOpinion $cityOpinion): void
Expand Down
1 change: 1 addition & 0 deletions app/Importers/DataImporter.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ protected function deleteMissingProviders(string $providerName, array $existingC
->whereNotIn("city_id", $existingCityProviders)
->whereNot("created_by", "admin")
->get();

$cityProvidersToDelete->each(fn($cityProvider) => $cityProvider->delete());
}

Expand Down
21 changes: 21 additions & 0 deletions app/Policies/CityOpinionPolicy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace App\Policies;

use App\Models\CityOpinion;
use App\Models\User;

class CityOpinionPolicy
{
public function update(User $user, CityOpinion $cityOpinion): bool
{
return $cityOpinion->user_id === $user->id;
}

public function delete(User $user, CityOpinion $cityOpinion): bool
{
return $cityOpinion->user_id === $user->id || $user->hasRole("admin");
}
}
4 changes: 4 additions & 0 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

namespace App\Providers;

use App\Models\CityOpinion;
use App\Policies\CityOpinionPolicy;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
use Laravel\Sanctum\PersonalAccessToken;
use Laravel\Sanctum\Sanctum;
Expand All @@ -28,6 +31,7 @@ public function register(): void
public function boot(): void
{
JsonResource::withoutWrapping();
Gate::policy(CityOpinion::class, CityOpinionPolicy::class);
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
}
}
1 change: 0 additions & 1 deletion resources/js/Pages/City/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import InfoPopup from '@/Shared/Components/InfoPopup.vue'
import Opinion from '@/Shared/Components/Opinion.vue'
const toast = useToast()
const page = usePage()
const isAuth = computed(() => page.props.auth.isAuth)
Expand Down
5 changes: 3 additions & 2 deletions resources/js/Shared/Components/Opinion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import DeleteModal from './DeleteModal.vue'
import { useToast } from 'vue-toastification'
import ErrorMessage from './ErrorMessage.vue'
const isAdmin = computed(() => page.props.auth.isAdmin)
const toast = useToast()
const page = usePage()
const user = computed(() => page.props.auth.user)
Expand Down Expand Up @@ -103,8 +104,8 @@ const emptyRatingError = ref('')
{{ opinion.content }}
</div>

<div v-if="user.id === props.opinion.user.id" class="mt-1 flex justify-end">
<button class="flex px-1 hover:drop-shadow" @click="toggleUpdateOpinionDialog">
<div v-if="user.id === props.opinion.user.id || isAdmin" class="mt-1 flex justify-end">
<button v-if="user.id === props.opinion.user.id" class="flex px-1 hover:drop-shadow" @click="toggleUpdateOpinionDialog">
<PencilIcon class="h-5 w-5 text-blumilk-500 hover:drop-shadow sm:h-4 sm:w-4" />
</button>
<button class="flex px-1 hover:drop-shadow" @click="toggleDeleteOpinionDialog">
Expand Down
6 changes: 4 additions & 2 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
Route::get("/favorite-cities", [FavoritesController::class, "index"]);

Route::post("/opinions", [CityOpinionController::class, "store"]);
Route::patch("/opinions/{cityOpinion}", [CityOpinionController::class, "update"]);
Route::delete("/opinions/{cityOpinion}", [CityOpinionController::class, "destroy"]);
Route::patch("/opinions/{cityOpinion}", [CityOpinionController::class, "update"])
->middleware("can:update,cityOpinion");
Route::delete("/opinions/{cityOpinion}", [CityOpinionController::class, "destroy"])
->middleware("can:delete,cityOpinion");

Route::middleware(["role:admin"])->group(function (): void {
Route::get("/admin/importers", [ImportInfoController::class, "index"]);
Expand Down

0 comments on commit 39e2e60

Please sign in to comment.