Skip to content

Commit 5ca25e1

Browse files
committed
added user delete and restore with email system
1 parent 8542d3e commit 5ca25e1

File tree

13 files changed

+208
-56
lines changed

13 files changed

+208
-56
lines changed

.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ NULL_IP_ADDRESS=0.0.0.0
4242

4343
DEBUG_BAR_ENVIRONMENT=local
4444

45+
USER_RESTORE_CUTOFF_DAYS=31
46+
USER_RESTORE_ENRYPTION_KEY=
47+
4548
DEFAULT_GRAVATAR_SIZE=80
4649
DEFAULT_GRAVATAR_FALLBACK=http://c1940652.r52.cf0.rackcdn.com/51ce28d0fb4f442061000000/Screen-Shot-2013-06-28-at-5.22.23-PM.png
4750
DEFAULT_GRAVATAR_SECURE=false

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ Laravel 5.4 with user authentication, registration with email confirmation, soci
5757
|User [Roles/ACL Implementation](https://github.com/jeremykenedy/laravel-roles)|
5858
|Makes of [Laravel's Soft Delete Structure](https://laravel.com/docs/5.4/eloquent#soft-deleting)|
5959
|Soft Deleted Users Management System|
60-
|Permanently Delete Sof Deleted Users|
60+
|Permanently Delete Soft Deleted Users|
61+
|User Delete Account with Goodby email|
62+
|User Restore Deleted Account Token|
6163
|Restore Soft Deleted Users|
6264
|View Soft Deleted Users|
6365
|Captures Soft Delete Date|
@@ -69,6 +71,9 @@ Laravel 5.4 with user authentication, registration with email confirmation, soci
6971
|404 Page|
7072
|403 Page|
7173

74+
|User Delete with Goodby email|
75+
|User Restore Deleted Account|
76+
7277
### Installation Instructions
7378
1. Run `sudo git clone https://github.com/jeremykenedy/laravel-auth.git laravel-auth`
7479
2. Create a MySQL database for the project
@@ -138,6 +143,7 @@ npm install
138143
* ```/activate```
139144
* ```/activate/{token}```
140145
* ```/activation-required```
146+
* ```/re-activate/{token}```
141147

142148
#### Profile Routes
143149
* ```/profile/{username}```
@@ -274,6 +280,9 @@ NULL_IP_ADDRESS=0.0.0.0
274280

275281
DEBUG_BAR_ENVIRONMENT=local
276282

283+
USER_RESTORE_CUTOFF_DAYS=31
284+
USER_RESTORE_ENRYPTION_KEY=
285+
277286
DEFAULT_GRAVATAR_SIZE=80
278287
DEFAULT_GRAVATAR_FALLBACK=http://c1940652.r52.cf0.rackcdn.com/51ce28d0fb4f442061000000/Screen-Shot-2013-06-28-at-5.22.23-PM.png
279288
DEFAULT_GRAVATAR_SECURE=false
@@ -349,7 +358,8 @@ INSTAGRAM_REDIRECT_URI=http://laravel-auth.local/social/handle/instagram
349358
* https://laravel.com/docs/5.4/errors
350359
351360
###### Updates:
352-
361+
* Added User Delete with Goodby email
362+
* Added User Restore Deleted Account from email with secure token
353363
* Added [Soft Deletes](https://laravel.com/docs/5.4/eloquent#soft-deleting) and Soft Deletes Management panel
354364
* Added User Account Settings to Profile Edit
355365
* Added User Change Password to Profile Edit

app/Http/Controllers/ProfilesController.php

Lines changed: 91 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use App\Models\Profile;
66
use App\Models\Theme;
77
use App\Models\User;
8+
use App\Notifications\SendGoodbyeEmail;
89
use App\Traits\CaptureIpTrait;
910
use File;
1011
use Helper;
@@ -14,13 +15,18 @@
1415
use Illuminate\Database\Eloquent\ModelNotFoundException;
1516
use Illuminate\Support\Facades\Input;
1617
use Illuminate\Support\Facades\Log;
18+
use Illuminate\Support\Facades\Session;
1719
use Image;
20+
use Webpatser\Uuid\Uuid;
1821
use Validator;
1922
use View;
2023

2124
class ProfilesController extends Controller
2225
{
2326

27+
protected $idMultiKey = '618423'; //int
28+
protected $seperationKey = '****';
29+
2430
/**
2531
* Create a new controller instance.
2632
*
@@ -281,6 +287,53 @@ public function updateUserPassword(Request $request, $id)
281287

282288
}
283289

290+
/**
291+
* Upload and Update user avatar
292+
*
293+
* @param $file
294+
* @return mixed
295+
*/
296+
public function upload() {
297+
if(Input::hasFile('file')) {
298+
299+
$currentUser = \Auth::user();
300+
$avatar = Input::file('file');
301+
$filename = 'avatar.' . $avatar->getClientOriginalExtension();
302+
$save_path = storage_path() . '/users/id/' . $currentUser->id . '/uploads/images/avatar/';
303+
$path = $save_path . $filename;
304+
$public_path = '/images/profile/' . $currentUser->id . '/avatar/' . $filename;
305+
306+
// Make the user a folder and set permissions
307+
File::makeDirectory($save_path, $mode = 0755, true, true);
308+
309+
// Save the file to the server
310+
Image::make($avatar)->resize(300, 300)->save($save_path . $filename);
311+
312+
// Save the public image path
313+
$currentUser->profile->avatar = $public_path;
314+
$currentUser->profile->save();
315+
316+
return response()->json(array('path'=> $path), 200);
317+
318+
} else {
319+
320+
return response()->json(false, 200);
321+
322+
}
323+
}
324+
325+
/**
326+
* Show user avatar
327+
*
328+
* @param $id
329+
* @param $image
330+
* @return string
331+
*/
332+
public function userProfileAvatar($id, $image)
333+
{
334+
return Image::make(storage_path() . '/users/id/' . $id . '/uploads/images/avatar/' . $image)->response();
335+
}
336+
284337
/**
285338
* Update the specified resource in storage.
286339
*
@@ -316,70 +369,63 @@ public function deleteUserAccount(Request $request, $id)
316369

317370
}
318371

372+
// Create and encrypt user account restore token
373+
$sepKey = $this->getSeperationKey();
374+
$userIdKey = $this->getIdMultiKey();
375+
$restoreKey = config('settings.restoreKey');
376+
$encrypter = config('settings.restoreUserEncType');
377+
$level1 = $user->id * $userIdKey;
378+
$level2 = urlencode(Uuid::generate(4) . $sepKey . $level1);
379+
$level3 = base64_encode($level2);
380+
$level4 = openssl_encrypt($level3, $encrypter, $restoreKey);
381+
$level5 = base64_encode($level4);
382+
383+
// Save Restore Token and Ip Address
384+
$user->token = $level5;
319385
$user->deleted_ip_address = $ipAddress->getClientIp();
320-
321386
$user->save();
322387

323-
$user->delete();
388+
// Send Goodbye email notification
389+
$this->sendGoodbyEmail($user, $user->token);
324390

325-
return redirect('/');
391+
// Soft Delete User
392+
$user->delete();
326393

327-
/*
328-
--- Finishing Todos ----
329-
1. Create HTML Goodbye View.
330-
2. Send goodbye email.
394+
// Clear out the session
395+
$request->session()->flush();
396+
$request->session()->regenerate();
331397

332-
---- Desired Other Todos ----
333-
1. Later add que job to delete the soft deletes.
334-
*/
398+
return redirect('/login/')->with('success', 'Your account has been deleted.');
335399

336400
}
337401

338402
/**
339-
* Upload and Update user avatar
403+
* Send GoodBye Email Function via Notify
340404
*
341-
* @param $file
342-
* @return mixed
405+
* @param array $user
406+
* @param string $token
407+
* @return void
343408
*/
344-
public function upload() {
345-
if(Input::hasFile('file')) {
346-
347-
$currentUser = \Auth::user();
348-
$avatar = Input::file('file');
349-
$filename = 'avatar.' . $avatar->getClientOriginalExtension();
350-
$save_path = storage_path() . '/users/id/' . $currentUser->id . '/uploads/images/avatar/';
351-
$path = $save_path . $filename;
352-
$public_path = '/images/profile/' . $currentUser->id . '/avatar/' . $filename;
353-
354-
// Make the user a folder and set permissions
355-
File::makeDirectory($save_path, $mode = 0755, true, true);
356-
357-
// Save the file to the server
358-
Image::make($avatar)->resize(300, 300)->save($save_path . $filename);
359-
360-
// Save the public image path
361-
$currentUser->profile->avatar = $public_path;
362-
$currentUser->profile->save();
363-
364-
return response()->json(array('path'=> $path), 200);
365-
366-
} else {
367-
368-
return response()->json(false, 200);
409+
public static function sendGoodbyEmail(User $user, $token) {
410+
$user->notify(new SendGoodbyeEmail($token));
411+
}
369412

370-
}
413+
/**
414+
* Get User Restore ID Multiplication Key
415+
*
416+
* @return string
417+
*/
418+
public function getIdMultiKey() {
419+
return $this->idMultiKey;
371420
}
372421

373422
/**
374-
* Show user avatar
423+
* Get User Restore Seperation Key
375424
*
376-
* @param $id
377-
* @param $image
378425
* @return string
379426
*/
380-
public function userProfileAvatar($id, $image)
381-
{
382-
return Image::make(storage_path() . '/users/id/' . $id . '/uploads/images/avatar/' . $image)->response();
427+
public function getSeperationKey() {
428+
return $this->seperationKey;
383429
}
384430

385431
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
namespace App\Http\Controllers;
4+
5+
use App\Models\User;
6+
use App\Http\Controllers\SoftDeletesController;
7+
use App\Http\Requests;
8+
use Carbon\Carbon;
9+
use Illuminate\Http\Request;
10+
use Illuminate\Support\Facades\Session;
11+
use Webpatser\Uuid\Uuid;
12+
13+
class RestoreUserController extends ProfilesController
14+
{
15+
16+
/**
17+
* Create a new controller instance.
18+
*
19+
* @return void
20+
*/
21+
public function __construct()
22+
{
23+
$this->middleware('web');
24+
}
25+
26+
public function userReActivate(Request $request, $token)
27+
{
28+
29+
$userKeys = new ProfilesController;
30+
$sepKey = $userKeys->getSeperationKey();
31+
$userIdKey = $userKeys->getIdMultiKey();
32+
$restoreKey = config('settings.restoreKey');
33+
$encrypter = config('settings.restoreUserEncType');
34+
$level5 = base64_decode($token);
35+
$level4 = openssl_decrypt($level5, $encrypter, $restoreKey);
36+
$level3 = base64_decode($level4);
37+
$level2 = urldecode($level3);
38+
$level1[] = explode($sepKey, $level2);
39+
$uuid = $level1[0][0];
40+
$userId = $level1[0][1] / $userIdKey;
41+
$user = SoftDeletesController::getDeletedUser($userId);
42+
43+
if (!is_object($user)) {
44+
abort(500);
45+
}
46+
47+
$deletedDate = $user->deleted_at;
48+
$currentDate = Carbon::now();
49+
$daysDeleted = $currentDate->diffInDays($deletedDate);
50+
$cutoffDays = config('settings.restoreUserCutoff');
51+
52+
if ($daysDeleted >= $cutoffDays) {
53+
54+
return redirect('/login')->with('error', 'Sorry, account cannot be restored');
55+
56+
}
57+
58+
$user->restore();
59+
60+
return redirect('/login')->with('success', 'Welcome back ' . $user->name . '! Account Successfully Restored');
61+
62+
}
63+
64+
}

app/Http/Controllers/SoftDeletesController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public function __construct()
2929
* @param int $id
3030
* @return \Illuminate\Http\Response
3131
*/
32-
private static function getDeletedUser($id)
32+
public static function getDeletedUser($id)
3333
{
3434
$user = User::onlyTrashed()->where('id', $id)->get();
3535
if (count($user) != 1) {

app/Models/User.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ class User extends Authenticatable
4545
'signup_confirmation_ip_address',
4646
'signup_sm_ip_address',
4747
'admin_ip_address',
48+
'updated_ip_address',
49+
'deleted_ip_address',
4850
];
4951

5052
/**

app/Notifications/SendGoodbyeEmail.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class SendGoodbyeEmail extends Notification implements ShouldQueue
1919
*/
2020
public function __construct($token)
2121
{
22-
//
22+
$this->token = $token;
2323
}
2424

2525
/**
@@ -43,9 +43,11 @@ public function toMail($notifiable)
4343
{
4444

4545
$message = new MailMessage;
46-
$message->subject(trans('emails.goodbyeSubject'));
47-
48-
// Finish contents here
46+
$message->subject(trans('emails.goodbyeSubject'))
47+
->greeting(trans('emails.goodbyeGreeting'))
48+
->line(trans('emails.goodbyeMessage'))
49+
->action(trans('emails.goodbyeButton'), route('user.reactivate', ['token' => $this->token]))
50+
->line(trans('emails.goodbyeThanks'));
4951

5052
return ($message);
5153

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"creativeorange/gravatar": "~1.0",
2222
"intervention/image": "^2.3",
2323
"barryvdh/laravel-debugbar": "^2.3",
24-
"rap2hpoutre/laravel-log-viewer": "^0.8.1"
24+
"rap2hpoutre/laravel-log-viewer": "^0.8.1",
25+
"webpatser/laravel-uuid": "^2.0"
2526
},
2627
"require-dev": {
2728
"fzaninotto/faker": "~1.4",

config/app.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@
243243
'Input' => Illuminate\Support\Facades\Input::class,
244244
'Gravatar' => Creativeorange\Gravatar\Facades\Gravatar::class,
245245
'Image' => Intervention\Image\Facades\Image::class,
246+
'Uuid' => Webpatser\Uuid\Uuid::class,
246247
],
247248

248249
];

config/settings.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,19 @@
2222
*/
2323
'nullIpAddress' => env('NULL_IP_ADDRESS', '0.0.0.0'),
2424

25+
/*
26+
* User restore encryption type
27+
*/
28+
'restoreUserEncType' => 'AES-256-ECB',
29+
30+
/*
31+
* User restore days past cutoff
32+
*/
33+
'restoreUserCutoff' => env('USER_RESTORE_CUTOFF_DAYS', 31),
34+
35+
/*
36+
* User restore encryption key
37+
*/
38+
'restoreKey' => env('USER_RESTORE_ENRYPTION_KEY', 'sup3rS3cr3tR35t0r3K3y21!'),
39+
2540
];

0 commit comments

Comments
 (0)