Skip to content

Commit

Permalink
Merge pull request #1413 from younginnovations/1384-add-ability-for-i…
Browse files Browse the repository at this point in the history
…ati-admin-users-to-delete-an-organisation-from-iati-publisher

Reviewed: 1384-add-ability-for-iati-admin-users-to-delete-an-organisation-from-iati-publisher
  • Loading branch information
PG-Momik authored Apr 5, 2024
2 parents f939357 + a728403 commit 8b7c665
Show file tree
Hide file tree
Showing 24 changed files with 682 additions and 108 deletions.
136 changes: 117 additions & 19 deletions app/Http/Controllers/Admin/Organization/OrganizationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@

use App\Constants\Enums;
use App\Http\Controllers\Controller;
use App\Http\Requests\DeleteOrganizationRequest;
use App\IATI\Models\Organization\Organization;
use App\IATI\Services\Organization\OrganizationService;
use App\IATI\Services\Publisher\PublisherService;
use App\IATI\Services\Workflow\OrganizationWorkflowService;
use App\SpamEmail;
use Exception;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Contracts\View\View;
Expand All @@ -18,6 +24,7 @@
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Session;
use JsonException;

/**
* Class OrganizationController.
Expand All @@ -28,14 +35,21 @@ class OrganizationController extends Controller
* @var OrganizationService
*/
protected OrganizationService $organizationService;
/**
* @var OrganizationWorkflowService
*/
protected OrganizationWorkflowService $organizationWorkflowService;

/**
* OrganizationController Constructor.
*
* @param OrganizationService $organizationService
* @param OrganizationWorkflowService $organizationWorkflowService
*/
public function __construct(OrganizationService $organizationService)
public function __construct(OrganizationService $organizationService, OrganizationWorkflowService $organizationWorkflowService)
{
$this->organizationService = $organizationService;
$this->organizationWorkflowService = $organizationWorkflowService;
}

/**
Expand All @@ -46,16 +60,6 @@ public function index(): void
//
}

/**
* Show the form for creating a new resource.
*
* @return void
*/
public function create(): void
{
//
}

/**
* Store a newly created resource in storage.
*
Expand Down Expand Up @@ -91,7 +95,7 @@ public function show(): View|RedirectResponse
$userRole = Auth::user()->role->role;

return view('admin.organisation.index', compact('elements', 'elementGroups', 'progress', 'organization', 'toast', 'types', 'mandatoryCompleted', 'status', 'userRole'));
} catch (\Exception $e) {
} catch (Exception $e) {
logger()->error($e->getMessage());

return redirect()->route('admin.activities.index')->with('error', 'Error has occurred while opening organization detail page.');
Expand All @@ -113,7 +117,7 @@ public function edit(Organization $organization): void
/**
* Update the specified resource in storage.
*
* @param Request $request
* @param Request $request
* @param Organization $organization
*
* @return void
Expand All @@ -126,11 +130,105 @@ public function update(Request $request, Organization $organization): void
/**
* Remove the specified resource from storage.
*
* @param Organization $organization
* @param DeleteOrganizationRequest $request
* @param string $orgId
* @return array
*/
public function destroy(DeleteOrganizationRequest $request, string $orgId): array
{
DB::beginTransaction();

try {
$markAsSpam = $request->query->get('markAsSpam', false);
$markAsSpam = $markAsSpam === 'true';
$organization = $this->organizationService->getOrganizationData($orgId);

$publisherId = $organization->publisher_id;
$apiToken = $organization->settings?->publishing_info['api_token'] ?? false;
$organizationPublished = $organization->organizationPublished;

if ($organizationPublished) {
$this->unlinkOldFilesFromRegistry($publisherId, $apiToken, 'organisation');
$organizationPublished->delete();
}

$activityPublished = $organization->activityPublished;

if ($activityPublished) {
$this->unlinkOldFilesFromRegistry($publisherId, $apiToken, 'activities');
$activityPublished->delete();
}

$organization->settings()->delete();

$activities = $organization->activities;

foreach ($activities as $activity) {
$activity->delete();
}

$users = $organization->users;

foreach ($users as $user) {
if ($markAsSpam) {
$this->markEmailAsSpam($user->email);
}

$user->forceDelete();
}

$organization->delete();

DB::commit();

return ['success' => true, 'message' => 'Organisation deleted successfully.'];
} catch (Exception $e) {
DB::rollBack();

logger()->error($e->getMessage());

return ['success' => false, 'message' => 'Error occurred while deleting organisation.'];
}
}

/**
* @param string $publisherId
* @param string $orgApiToken
* @param string $filetype
*
* @return bool
*
* @throws BindingResolutionException
*/
private function unlinkOldFilesFromRegistry(string $publisherId, string $orgApiToken, string $filetype): bool
{
/** @var PublisherService $publisherService */
$publisherService = app()->make(PublisherService::class);
$files = ["$publisherId-$filetype"];

return $publisherService->unlink($orgApiToken, $files);
}

/**
* @param string $email
*
* @return void
*/
private function markEmailAsSpam(string $email): void
{
$existingEmail = SpamEmail::where('email', $email)->first();

if (!$existingEmail) {
SpamEmail::create(['email' => $email]);
}
}

/**
* Show the form for creating a new resource.
*
* @return void
*/
public function destroy(Organization $organization): void
public function create(): void
{
//
}
Expand All @@ -142,7 +240,7 @@ public function destroy(Organization $organization): void
*
* @return array
*
* @throws \JsonException
* @throws JsonException
*/
public function getRegistrationAgency(bool|string $country_code = false): array
{
Expand Down Expand Up @@ -180,13 +278,13 @@ public function getPublisherStatus(): JsonResponse
'message' => 'Publisher status successfully retrieved.',
'data' => ['publisher_active' => $status],
]);
} catch (\Exception $e) {
} catch (Exception $e) {
logger()->error($e);

return response()->json([
'success' => false,
'message' => 'Encountered issue when checking publisher state on registry.',
'data' => ['publisher_active' => false],
'data' => ['publisher_active' => false],
]);
}
}
Expand Down Expand Up @@ -217,7 +315,7 @@ public function deleteElement($element): Response|Application|ResponseFactory
Session::put('success', $message);

return response(['status' => true, 'message' => $message]);
} catch (\Exception $e) {
} catch (Exception $e) {
DB::rollBack();
logger()->error($e->getMessage());

Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/Auth/RegisterController.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ public function register(Request $request): JsonResponse|RedirectResponse
$validator = Validator::make($request->all(), [
'username' => ['required', 'max:255', 'string', 'unique:users,username'],
'full_name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255', 'unique:users,email'],
'email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255', 'unique:users,email', 'not_in_spam_emails'],
'publisher_id' => ['required', 'string', 'max:255', 'unique:organizations,publisher_id'],
'password' => ['required', 'string', 'min:8', 'max:255', 'confirmed'],
'password_confirmation' => ['required', 'string', 'min:8', 'max:255'],
Expand Down
30 changes: 30 additions & 0 deletions app/Http/Requests/DeleteOrganizationRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class DeleteOrganizationRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return isSuperAdmin();
}

/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
//
];
}
}
2 changes: 1 addition & 1 deletion app/Http/Requests/User/UserProfileRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function rules(): array
$rules = [
'username' => ['required', 'max:255', sprintf('unique:users,username,%d', $id)],
'full_name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255', sprintf('unique:users,email,%d', $id)],
'email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255', sprintf('unique:users,email,%d', $id), 'not_in_spam_emails'],
'language_preference' => 'required',
];

Expand Down
2 changes: 1 addition & 1 deletion app/Http/Requests/User/UserRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function rules(): array
$rules = [
'username' => ['required', 'max:255', 'string', 'unique:users,username'],
'full_name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255', 'unique:users,email'],
'email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255', 'unique:users,email', 'not_in_spam_emails'],
'status' => ['required'],
'password' => ['required', 'string', 'min:6', 'max:255', 'confirmed'],
'password_confirmation' => ['required', 'string', 'min:6', 'max:255'],
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Requests/User/UserUpdateRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function rules(): array
$rules = [
'username' => ['required', 'max:255', 'string', sprintf('unique:users,username,%d', $id)],
'full_name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255', sprintf('unique:users,email,%d', $id)],
'email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255', sprintf('unique:users,email,%d', $id), 'not_in_spam_emails'],
];

if (!empty(Arr::get($request, 'password', null))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function rules(): array
break;
case '2':
$rules = [
'contact_email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255'],
'contact_email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255', 'not_in_spam_emails'],
'website' => ['nullable', 'url'],
];
break;
Expand All @@ -60,7 +60,7 @@ public function rules(): array
$rules = [
'username' => ['required', 'max:255', 'string', 'regex:/^[a-z]([0-9a-z-_])*$/', 'unique:users,username'],
'full_name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255', 'unique:users,email'],
'email' => ['required', 'string', 'email', 'regex:/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,}$/ix', 'max:255', 'unique:users,email', 'not_in_spam_emails'],
'password' => ['required', 'string', 'min:6', 'max:255', 'confirmed'],
'password_confirmation' => ['required', 'string', 'min:6', 'max:255'],
];
Expand Down
16 changes: 16 additions & 0 deletions app/IATI/Models/Organization/Organization.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,20 @@ public function usersIncludingDeleted(): HasMany
{
return $this->hasMany(User::class, 'organization_id', 'id')->withTrashed();
}

/**
* @return HasOne
*/
public function organizationPublished(): HasOne
{
return $this->hasOne(OrganizationPublished::class, 'organization_id', 'id');
}

/**
* @return HasOne
*/
public function activityPublished(): HasOne
{
return $this->hasOne(ActivityPublished::class, 'organization_id', 'id');
}
}
9 changes: 8 additions & 1 deletion app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
namespace App\Providers;

use App\Database\PostgresConnection;
use App\SpamEmail;
use Illuminate\Database\Connection;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;
use Laravel\Horizon\Horizon;
use URL;

class AppServiceProvider extends ServiceProvider
{
Expand All @@ -28,8 +31,12 @@ public function register(): void
*/
public function boot(): void
{
Validator::extend('not_in_spam_emails', function ($attribute, $value, $parameters, $validator) {
return !SpamEmail::where('email', $value)->exists();
});

if (config('app.env') === 'production' || config('app.env') === 'staging') {
\URL::forceScheme('https');
URL::forceScheme('https');
// return true;
}

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

declare(strict_types=1);

namespace App;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class SpamEmail extends Model
{
use HasFactory;

protected $fillable = ['email'];
}
Loading

0 comments on commit 8b7c665

Please sign in to comment.