Skip to content

Commit

Permalink
handle donors phone encryption and dycription, manage proposal policy…
Browse files Browse the repository at this point in the history
… and showing the action buttons depending on the status of the proposal
  • Loading branch information
Ahmed-elhelou committed Jan 22, 2025
1 parent 697acda commit 9d1b211
Show file tree
Hide file tree
Showing 20 changed files with 206 additions and 49 deletions.
1 change: 1 addition & 0 deletions app/Http/Controllers/DonationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Http\Requests\UpdateDonationRequest;
use App\Models\Donation;
use App\Models\Currency;
use App\Models\Donor;
use App\Models\Proposal;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
Expand Down
4 changes: 3 additions & 1 deletion app/Http/Controllers/DonorController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Models\Donor;
use App\Models\Country;
use App\Models\Donation;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
Expand All @@ -23,12 +24,13 @@ public function __construct(Request $request)
parent::__construct($request);
}



/**
* Display a listing of the resource.
*/
public function index(Request $request)
{

return Inertia::render(Str::studly("Donor").'/Index', [
"headers" => Donor::headers(),
"items" => Donor::search($request)->sort($request)->paginate($this->pagination),
Expand Down
6 changes: 6 additions & 0 deletions app/Http/Controllers/ProposalController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use App\Models\Country;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Inertia\Inertia;

Expand Down Expand Up @@ -110,8 +112,12 @@ public function edit(Proposal $proposal)
*/
public function update(UpdateProposalRequest $request, Proposal $proposal)
{
// Log::debug($proposal);
$validated = $request->validated();
// update the propsal with the new values without storing it, so that we can check the new and the original values in the ProposalPolicy class
// $proposal->fill($validated);

Gate::authorize('update', [$proposal, $request['status']]);

// check if there is a donatingAmount and a new donation record is needed to be added
if(!empty($request->donatingAmount) && $request->status == 2 && $proposal->status != $request->status){
Expand Down
14 changes: 12 additions & 2 deletions app/Http/Requests/StoreDonationRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@

class StoreDonationRequest extends FormRequest
{

protected function prepareForValidation()
{
if ($this->has('phone')) {
$this->merge([
'phone' => deterministicEncrypt($this->phone),
]);
}
}

/**
* Determine if the user is authorized to make this request.
*
Expand All @@ -25,9 +35,9 @@ public function rules()
{
return [
'phone' => 'required|exists:donors,phone',
'project_id' => 'required|exists:proposals,id',
'proposal_id' => 'required|exists:proposals,id',
'currency_id' => 'required|exists:currencies,id',
'amount' => 'required|numberic|min:0',
'amount' => 'required|numeric|min:0',
'status' => 'required|integer|between:0,3',
];
}
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Requests/UpdateProposalRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function rules()
'cost' => 'sometimes|numeric',
'share_cost' => 'sometimes|numeric',
'expected_benificiaries_count' => 'sometimes|integer|min:0',
'min_documenting_amount' => 'required|integer|min:0',
'min_documenting_amount' => 'sometimes|integer|min:0',
'publishing_date' => 'sometimes|date',
'execution_date' => 'sometimes|date|after_or_equal:publishing_date',
'entity_id' => 'sometimes|integer|exists:entities,id',
Expand Down
14 changes: 14 additions & 0 deletions app/Http/helpers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
function deterministicEncrypt($value)
{
$key = config('app.key'); // Use a secure key
return base64_encode(openssl_encrypt($value, 'AES-256-ECB', $key, OPENSSL_RAW_DATA));
}

function deterministicDecrypt($encryptedValue)
{
$key = config('app.key'); // Use a secure key
return openssl_decrypt(base64_decode($encryptedValue), 'AES-256-ECB', $key, OPENSSL_RAW_DATA);
}


7 changes: 5 additions & 2 deletions app/Models/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Document extends BaseModel
{
use HasFactory;
protected $appends = ['proposal_name', 'donor_name', 'currency_name'];
protected $with = ['proposal', 'donor', 'currency'];
protected $with = ['proposal', 'donor', 'currency', 'attachments'];
public static $controllable = true;

public function proposal()
Expand Down Expand Up @@ -39,7 +39,10 @@ public function getCurrencyNameAttribute()
{
return $this->currency?->name;
}

public function attachments()
{
return $this->morphMany(Attachment::class, 'attachable');
}

public static function headers($user = null)
{
Expand Down
33 changes: 23 additions & 10 deletions app/Models/Donor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Models;

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

Expand All @@ -12,9 +13,6 @@ class Donor extends BaseModel
protected $guarded = ['donations'];
protected $with = ['country'];
public static $controllable = true;
protected $casts = [
// 'phone' => 'encrypted',
];

public const STATUSES = [
'pending' => 0,
Expand All @@ -24,9 +22,14 @@ class Donor extends BaseModel
2 => ('Approved'),
3 => ('Rejected'),

];
public const PHONE_ALLOWED_USER_TYPES = [
1

];

public static function genders() {

return [
['id' => 1, 'name' => __('Male')],
['id' => 2, 'name' => __('Female')],
Expand All @@ -40,15 +43,25 @@ public function country()
return $this->belongsTo(Country::class, 'country_id', 'id');
}




public function getPhoneAttribute($phone)
protected function phone(): Attribute
{
if(in_array(auth()->user()->type, [1,2]))
return ($phone);
return str_repeat("", 15);
return Attribute::make(
set: fn (string $value) => deterministicEncrypt($value),
get: function (string $value) {
if(in_array(auth()->user()->type, self::PHONE_ALLOWED_USER_TYPES))
return deterministicDecrypt($value);
return str_repeat("", 15);
},
);
}


// public function getPhoneAttribute($phone)
// {
// if(in_array(auth()->user()->type, self::PHONE_ALLOWED_USER_TYPES))
// return decrypt($phone, false);
// return str_repeat("●", 15);
// }
public function getCountryNameAttribute()
{
return $this->country->name ?? null;
Expand Down
29 changes: 24 additions & 5 deletions app/Models/Proposal.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Gate;
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel\Current;

class Proposal extends BaseModel
{
use HasFactory;
protected $guarded = ['donated_amount'];
protected $appends = ['status_str_ar', 'beneficiaries', 'currency_name', 'entity_name', 'proposal_type_type_ar', 'area_name'];
protected $appends = ['status_str_ar', 'beneficiaries', 'currency_name', 'entity_name', 'proposal_type_type_ar', 'area_name', 'can_complete_donating_status', 'can_complete_execution_status'];
protected $with = ['entity', 'area', 'proposalType', 'currency'];
public static $controllable = true;

Expand Down Expand Up @@ -58,15 +59,21 @@ public function getCurrencyNameAttribute(){
return $this->currency->name;
}

public function getCanCompleteDonatingStatusAttribute(){
return Gate::allows('completeDonatingStatus', $this);
}
public function getCanCompleteExecutionStatusAttribute(){
return Gate::allows('completeExecutionStatus', $this);
}
// public function getCanCompleteExecutionStatusAttribute(){
// return $this->currency->name;
// }

// public function getPropsalDetailsAttribute()
// {
// return $this->hasMany(ProposalDetail::class, 'proposal_id');
// }

// public function getTotalAttribute()
// {
// return $this->propsal_details->sum('total');
// }

// relations
public function entity(){
Expand All @@ -81,6 +88,18 @@ public function currency(){
public function proposalType(){
return $this->belongsTo(ProposalType::class, 'proposal_type_id');
}
public function donations(){
return $this->hasMany(Donation::class, 'proposal_id');
}
public function documents(){
return $this->hasMany(Document::class, 'proposal_id');
}
public function attachments()
{
return $this->morphMany(Attachment::class, 'attachable');
}

// Scopes

public function scopePublic($query){
$query->where('status', 1)->where('publishing_date', '<=', Carbon::now()->format('Y-m-d'));
Expand Down
53 changes: 51 additions & 2 deletions app/Policies/ProposalPolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use App\Models\Proposal;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Log\Logger;
use Illuminate\Support\Facades\Log;

class ProposalPolicy
{
Expand Down Expand Up @@ -51,9 +53,56 @@ public function create(User $user)
* @param \App\Models\Proposal $proposal
* @return mixed
*/
public function update(User $user, Proposal $proposal)
public function update(User $user, Proposal $proposal, $newStatus = null)
{
//

// if($newStatus != null && $newStatus != $proposal->status){
// return $this->completeDonatingStatus($user, $proposal) || $this->completeExecutionStatus($user, $proposal);
// }
if(!in_array($user->type, [1, 2, 3, 5])) return false;
if($newStatus === 2) return $this->completeDonatingStatus($user, $proposal);
if($newStatus === 3) return $this->completeExecutionStatus($user, $proposal);

return true;


}
public function completeDonatingStatus(User $user, Proposal $proposal)
{
if(!in_array($user->type, [1, 2, 3])) return false;
if($proposal->status !== 1) return false;

// make sure that all donations are approved
$pendingDonations = $proposal->donations()
->where('status', 0)
->exists();

return !$pendingDonations;

}
public function completeExecutionStatus(User $user, Proposal $proposal)
{
// return true;
if(!in_array($user->type, [1, 5])) return false;
if($proposal->status != 2 ) return false;

// Check if the proposal has at least one attachable of type 2
$proposalAttachableOfType2 = $proposal->attachments()
->where('attachment_type', 2)
->exists();
if(!$proposalAttachableOfType2) return false;

// Check if all related documents have at least one attachable record
$documentsWithoutAttachable = $proposal->documents()
->whereDoesntHave('attachments') // Check for documents without attachments
->exists();

if ($documentsWithoutAttachable) {
return false;
}

return true;

}

/**
Expand Down
4 changes: 4 additions & 0 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use App\Observers\UserObserver;
use App\Models\User;
use App\Observers\ProposalObserver;
use App\Policies\ProposalPolicy;
use Illuminate\Support\Facades\Gate;

class AppServiceProvider extends ServiceProvider
{
Expand All @@ -36,5 +38,7 @@ public function boot(): void
'document' => 'App\Models\Document',
'proposal' => 'App\Models\Proposal',
]);


}
}
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/"
}
},
"files": ["app/Http/helpers.php"]
},
"autoload-dev": {
"psr-4": {
Expand Down
2 changes: 1 addition & 1 deletion resources/js/Components/DropdownLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ defineProps({
<template>
<Link
:href="href"
class="block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 transition duration-150 ease-in-out hover:bg-gray-100 focus:bg-gray-100 focus:outline-none"
class="block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 dark:text-white transition duration-150 ease-in-out hover:bg-gray-100 focus:bg-gray-100 focus:outline-none"
>
<slot />
</Link>
Expand Down
17 changes: 14 additions & 3 deletions resources/js/Components/ItemList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@
</svg>
</button>
<ul :id="`dropdown-${item.title}`" class="hidden py-2 space-y-2">
<template v-for="(sub_item, index) in item.items">
<li :key="index" v-if="sub_item.allowedUserTypes.includes(user.type)" >
<template v-for="(sub_item, index) in item.items" :key="index" >
<li v-if="sub_item.allowedUserTypes.includes(user.type)" >
<Link
:href="route(sub_item.to)"
class="acrive flex items-center w-full p-2 text-white transition duration-75 rounded-lg ps-11 group hover:bg-gray-600 dark:hover:bg-gray-70 dark:text-white dark:hover:bg-gray-700"
>{{ $t(sub_item.title) }}</Link
>{{ $t(sub_item.title) }} </Link
>
</li>
</template>
Expand All @@ -54,8 +54,19 @@
</template>

<script>
import { usePage } from '@inertiajs/vue3';
export default {
props: ["item"],
computed: {
user(){
// console.log('usePage()');
// console.log(usePage().props.auth.user);
return usePage().props.auth.user;
}
},
methods: {
handleActive(event) {
let elements = document.querySelectorAll(".item-link");
Expand Down
Loading

0 comments on commit 9d1b211

Please sign in to comment.