+
|
+
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 612eaac..759926c 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -4,11 +4,15 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
+
{
"name": "Listen for XDebug",
"type": "php",
"request": "launch",
- "port": 9000
+ "port": 9000,
+ "ignore": [
+ "**/vendor/**/*.php"
+ ]
},
{
"name": "Launch currently open script",
diff --git a/app/Http/Controllers/TeamController.php b/app/Http/Controllers/TeamController.php
index 12211de..6743f20 100644
--- a/app/Http/Controllers/TeamController.php
+++ b/app/Http/Controllers/TeamController.php
@@ -8,9 +8,11 @@ use App\Http\Requests\SendInviteRequest;
use App\Mail\InviteToTeam;
use App\Team;
use App\User;
+use App\Vacancy;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
+use Mpociot\Teamwork\Exceptions\UserNotInTeamException;
use Mpociot\Teamwork\Facades\Teamwork;
use Mpociot\Teamwork\TeamInvite;
@@ -23,8 +25,10 @@ class TeamController extends Controller
*/
public function index()
{
+ $teams = Team::with('users.roles')->get();
+
return view('dashboard.teams.teams')
- ->with('teams', Team::all());
+ ->with('teams', $teams);
}
/**
@@ -45,11 +49,13 @@ class TeamController extends Controller
*/
public function store(NewTeamRequest $request)
{
- Team::create([
+ $team = Team::create([
'name' => $request->teamName,
'owner_id' => Auth::user()->id
]);
+ Auth::user()->teams()->attach($team->id);
+
$request->session()->flash('success', 'Team successfully created.');
return redirect()->back();
}
@@ -75,7 +81,8 @@ class TeamController extends Controller
{
return view('dashboard.teams.edit-team')
->with('team', $team)
- ->with('users', User::all());
+ ->with('users', User::all())
+ ->with('vacancies', Vacancy::all());
}
/**
@@ -186,4 +193,20 @@ class TeamController extends Controller
return redirect()->to(route('teams.index'));
}
+
+ public function switchTeam(Request $request, Team $team)
+ {
+ try
+ {
+ Auth::user()->switchTeam($team);
+
+ $request->session()->flash('success', 'Switched teams! Your team dashboard will now use this context.');
+ }
+ catch(UserNotInTeamException $ex)
+ {
+ $request->session()->flash('error', 'You can\'t switch to a team you don\'t belong to.');
+ }
+
+ return redirect()->back();
+ }
}
diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
index cb494b6..64ac48f 100644
--- a/app/Http/Controllers/UserController.php
+++ b/app/Http/Controllers/UserController.php
@@ -23,11 +23,13 @@ use App\Notifications\EmailChanged;
use App\Notifications\ChangedPassword;
use Spatie\Permission\Models\Role;
+use App\Traits\ReceivesAccountTokens;
use Google2FA;
class UserController extends Controller
{
+ use ReceivesAccountTokens;
public function showStaffMembers()
{
@@ -220,7 +222,7 @@ class UserController extends Controller
if ($request->confirmPrompt == 'DELETE ACCOUNT')
{
- $user->delete();
+ $user->forceDelete();
$request->session()->flash('success','User deleted successfully. PII has been erased.');
}
else
@@ -232,6 +234,7 @@ class UserController extends Controller
return redirect()->route('registeredPlayerList');
}
+
public function update(UpdateUserRequest $request, User $user)
{
@@ -356,4 +359,6 @@ class UserController extends Controller
//TODO: Dispatch event
return redirect()->back();
}
+
+
}
diff --git a/app/Http/Requests/UserDeleteRequest.php b/app/Http/Requests/UserDeleteRequest.php
new file mode 100644
index 0000000..5135685
--- /dev/null
+++ b/app/Http/Requests/UserDeleteRequest.php
@@ -0,0 +1,39 @@
+has2FA())
+ {
+ return [
+ 'currentPassword' => 'required|password:web',
+ 'otp' => 'required|integer|max:6'
+ ];
+ }
+
+ return [
+ 'currentPassword' => 'required|password:web'
+ ];
+ }
+}
diff --git a/app/Mail/UserAccountDeleteConfirmation.php b/app/Mail/UserAccountDeleteConfirmation.php
new file mode 100644
index 0000000..3ad83b2
--- /dev/null
+++ b/app/Mail/UserAccountDeleteConfirmation.php
@@ -0,0 +1,56 @@
+deleteToken = $tokens['delete'];
+ $this->cancelToken = $tokens['cancel'];
+
+ $this->originalIP = $originalIP;
+ $this->name = $user->name;
+ $this->userID = $user->id;
+ }
+
+ /**
+ * Build the message.
+ *
+ * @return $this
+ */
+ public function build()
+ {
+ return $this->view('mail.deleted-account');
+ }
+}
diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php
index 38f7660..eab928b 100644
--- a/app/Observers/UserObserver.php
+++ b/app/Observers/UserObserver.php
@@ -8,6 +8,12 @@ use Illuminate\Support\Facades\Log;
class UserObserver
{
+
+ public function __construct()
+ {
+ Log::debug('User observer has been initialised and ready for use!');
+ }
+
/**
* Handle the user "created" event.
*
@@ -39,20 +45,28 @@ class UserObserver
public function deleting(User $user)
{
- $user->profile()->delete();
- Log::debug('Referential integrity cleanup: Deleted profile!');
- $applications = $user->applications;
-
- if (!$applications->isEmpty())
+ if ($user->isForceDeleting())
{
- Log::debug('RIC: Now trying to delete applications and responses...');
- foreach($applications as $application)
+ $user->profile->delete();
+ Log::debug('Referential integrity cleanup: Deleted profile!');
+ $applications = $user->applications;
+
+ if (!$applications->isEmpty())
{
- // code moved to Application observer, where it gets rid of attached elements individually
- Log::debug('RIC: Deleting application ' . $application->id);
- $application->delete();
-
+ Log::debug('RIC: Now trying to delete applications and responses...');
+ foreach($applications as $application)
+ {
+ // code moved to Application observer, where it gets rid of attached elements individually
+ Log::debug('RIC: Deleting application ' . $application->id);
+ $application->delete();
+
+ }
}
+
+ }
+ else
+ {
+ Log::debug('RIC: Not cleaning up soft deleted models!');
}
Log::debug('RIC: Cleanup done!');
@@ -66,7 +80,6 @@ class UserObserver
*/
public function deleted(User $user)
{
- //
}
/**
@@ -88,6 +101,8 @@ class UserObserver
*/
public function forceDeleted(User $user)
{
- //
+ Log::info('Model has been force deleted', [
+ 'modelID' => $user->id
+ ]);
}
}
diff --git a/app/Services/VacancyApplicationService.php b/app/Services/VacancyApplicationService.php
new file mode 100644
index 0000000..94ca873
--- /dev/null
+++ b/app/Services/VacancyApplicationService.php
@@ -0,0 +1,35 @@
+response->vacancy->id == $model->id)
+ {
+ $applications->push($application);
+ }
+ }
+
+ return $applications;
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/Team.php b/app/Team.php
index d1fbb67..1492b2e 100644
--- a/app/Team.php
+++ b/app/Team.php
@@ -13,4 +13,10 @@ class Team extends TeamworkTeam
'description',
'openJoin'
];
+
+
+ public function vacancies()
+ {
+ return $this->belongsToMany('App\Vacancy', 'team_has_vacancy');
+ }
}
diff --git a/app/Traits/HandlesAccountTokens.php b/app/Traits/HandlesAccountTokens.php
new file mode 100644
index 0000000..ce6ee34
--- /dev/null
+++ b/app/Traits/HandlesAccountTokens.php
@@ -0,0 +1,51 @@
+ Hash::make($deleteToken),
+ 'cancel' => Hash::make($cancelToken)
+
+ ];
+
+ $this->account_tokens = json_encode($tokens);
+ $this->save();
+
+ return [
+
+ 'delete' => $deleteToken,
+ 'cancel' => $cancelToken
+ ];
+
+ }
+
+ public function verifyAccountToken(string $token, string $type): bool
+ {
+ $tokens = json_decode($this->account_tokens);
+
+ if ($type == 'deleteToken')
+ {
+ return Hash::check($token, $tokens->delete);
+ }
+ elseif ($type == 'cancelToken')
+ {
+ return Hash::check($token, $tokens->cancel);
+ }
+
+ return false;
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/Traits/ReceivesAccountTokens.php b/app/Traits/ReceivesAccountTokens.php
new file mode 100644
index 0000000..798c3ff
--- /dev/null
+++ b/app/Traits/ReceivesAccountTokens.php
@@ -0,0 +1,84 @@
+id);
+ $tokens = $user->generateAccountTokens();
+
+ Mail::to($user)->send(new UserAccountDeleteConfirmation($user, $tokens, $request->ip()));
+
+ $user->delete();
+ Auth::logout();
+
+ $request->session()->flash('success', 'Please check your email to finish deleting your account.');
+ return redirect()->to('/');
+ }
+
+
+ public function processDeleteConfirmation(Request $request, $ID, $action, $token)
+ {
+ // We can't rely on Laravel's route model injection, because it'll ignore soft-deleted models,
+ // so we have to use a special scope to find them ourselves.
+ $user = User::withTrashed()->findOrFail($ID);
+ $email = $user->email;
+
+ switch($action)
+ {
+ case 'confirm':
+
+ if ($user->verifyAccountToken($token, 'deleteToken'))
+ {
+ Log::info('SECURITY: User deleted account!', [
+
+ 'confirmDeleteToken' => $token,
+ 'ipAddress' => $request->ip(),
+ 'email' => $user->email
+
+ ]);
+
+
+
+ $user->forceDelete();
+
+ $request->session()->flash('success', 'Account permanently deleted. Thank you for using our service.');
+ return redirect()->to('/');
+ }
+
+ break;
+
+ case 'cancel':
+
+ if ($user->verifyAccountToken($token, 'cancelToken'))
+ {
+ $user->restore();
+ $request->session()->flash('success', 'Account deletion cancelled! You may now login.');
+
+ return redirect()->to(route('login'));
+ }
+
+ break;
+
+ default:
+
+ abort(404, 'The page you were trying to access may not exist or may be expired.');
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/User.php b/app/User.php
index e4a0ce8..cacb175 100644
--- a/app/User.php
+++ b/app/User.php
@@ -2,15 +2,18 @@
namespace App;
+use App\Traits\HandlesAccountTokens;
use Illuminate\Contracts\Auth\MustVerifyEmail;
+use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
+use Illuminate\Support\Facades\Hash;
use Mpociot\Teamwork\Traits\UserHasTeams;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable implements MustVerifyEmail
{
- use UserHasTeams, Notifiable, HasRoles;
+ use UserHasTeams, Notifiable, HasRoles, SoftDeletes, HandlesAccountTokens;
/**
* The attributes that are mass assignable.
@@ -74,7 +77,6 @@ class User extends Authenticatable implements MustVerifyEmail
-
public function isStaffMember()
{
return $this->hasAnyRole('reviewer', 'admin', 'hiringManager');
@@ -85,8 +87,7 @@ class User extends Authenticatable implements MustVerifyEmail
return !is_null($this->twofa_secret);
}
-
-
+
public function routeNotificationForSlack($notification)
{
return config('slack.webhook.integrationURL');
diff --git a/app/Vacancy.php b/app/Vacancy.php
index f740243..3308acf 100644
--- a/app/Vacancy.php
+++ b/app/Vacancy.php
@@ -13,7 +13,7 @@ use GrahamCampbell\Markdown\Facades\Markdown;
class Vacancy extends Model
{
- use UsedByTeams;
+ //use UsedByTeams;
public $fillable = [
@@ -25,7 +25,8 @@ class Vacancy extends Model
'vacancyFormID',
'vacancyCount',
'vacancyStatus',
- 'vacancySlug'
+ 'vacancySlug',
+ 'team_id'
];
@@ -49,6 +50,12 @@ class Vacancy extends Model
}
+ public function teams()
+ {
+ return $this->belongsToMany('App\Team', 'team_has_vacancy');
+ }
+
+
public function forms()
{
return $this->belongsTo('App\Form', 'vacancyFormID', 'id');
diff --git a/config/adminlte.php b/config/adminlte.php
index ef34abb..b0555f4 100644
--- a/config/adminlte.php
+++ b/config/adminlte.php
@@ -572,6 +572,22 @@ return [
'location' => 'https://gitcdn.github.io/bootstrap-toggle/2.2.2/js/bootstrap-toggle.min.js'
]
]
+ ],
+ [
+ 'name' => 'BootstrapMultiselectDropdown',
+ 'active' => true,
+ 'files' => [
+ [
+ 'type' => 'js',
+ 'asset' => 'false',
+ 'location' => 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.15/js/bootstrap-multiselect.min.js'
+ ],
+ [
+ 'type' => 'css',
+ 'asset' => false,
+ 'location' => 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap-multiselect/0.9.15/css/bootstrap-multiselect.css'
+ ]
+ ]
]
],
];
diff --git a/database/migrations/2020_10_04_124115_vacancy_nullable_team_id.php b/database/migrations/2020_10_04_124115_vacancy_nullable_team_id.php
new file mode 100644
index 0000000..569a9f5
--- /dev/null
+++ b/database/migrations/2020_10_04_124115_vacancy_nullable_team_id.php
@@ -0,0 +1,38 @@
+dropForeign('vacancies_ownerteamid_foreign');
+ $table->dropColumn('ownerTeamID');
+ $table->bigInteger('team_id')->nullable()->unsigned();
+
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('vacancies', function(Blueprint $table){
+
+ $table->dropColumn('team_id');
+
+ });
+ }
+}
diff --git a/database/migrations/2020_10_04_163715_team_has_vacancy.php b/database/migrations/2020_10_04_163715_team_has_vacancy.php
new file mode 100644
index 0000000..f71182b
--- /dev/null
+++ b/database/migrations/2020_10_04_163715_team_has_vacancy.php
@@ -0,0 +1,43 @@
+id();
+ $table->integer('team_id')->unsigned();
+ $table->bigInteger('vacancy_id')->unsigned();
+ $table->timestamps();
+
+ $table->foreign('team_id')
+ ->references('id')
+ ->on(config('teamwork.teams_table'));
+
+ $table->foreign('vacancy_id')
+ ->references('id')
+ ->on('vacancies');
+
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ //
+ }
+}
diff --git a/database/migrations/2020_09_10_181914_add_team_assoc_to_vacancies.php b/database/migrations/2020_10_07_095240_add_account_tokens_to_user.php
similarity index 50%
rename from database/migrations/2020_09_10_181914_add_team_assoc_to_vacancies.php
rename to database/migrations/2020_10_07_095240_add_account_tokens_to_user.php
index 607cd61..9cd3ffd 100644
--- a/database/migrations/2020_09_10_181914_add_team_assoc_to_vacancies.php
+++ b/database/migrations/2020_10_07_095240_add_account_tokens_to_user.php
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
-class AddTeamAssocToVacancies extends Migration
+class AddAccountTokensToUser extends Migration
{
/**
* Run the migrations.
@@ -13,13 +13,8 @@ class AddTeamAssocToVacancies extends Migration
*/
public function up()
{
- Schema::table('vacancies', function (Blueprint $table) {
-
- $table->integer('ownerTeamID')->unsigned()->after('vacancyFormID');
-
- $table->foreign('ownerTeamID')
- ->references('id')
- ->on('teams');
+ Schema::table('users', function (Blueprint $table) {
+ $table->string('account_tokens')->after('password')->nullable();
});
}
@@ -30,8 +25,8 @@ class AddTeamAssocToVacancies extends Migration
*/
public function down()
{
- Schema::table('vacancies', function (Blueprint $table) {
- //
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn('account_tokens');
});
}
}
diff --git a/database/migrations/2020_10_03_153118_add_team_i_d_to_vacancy.php b/database/migrations/2020_10_07_100540_add_soft_deletes_to_users.php
similarity index 58%
rename from database/migrations/2020_10_03_153118_add_team_i_d_to_vacancy.php
rename to database/migrations/2020_10_07_100540_add_soft_deletes_to_users.php
index 8ec91ef..cce9928 100644
--- a/database/migrations/2020_10_03_153118_add_team_i_d_to_vacancy.php
+++ b/database/migrations/2020_10_07_100540_add_soft_deletes_to_users.php
@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
-class AddTeamIDToVacancy extends Migration
+class AddSoftDeletesToUsers extends Migration
{
/**
* Run the migrations.
@@ -13,8 +13,8 @@ class AddTeamIDToVacancy extends Migration
*/
public function up()
{
- Schema::table('vacancy', function (Blueprint $table) {
- //
+ Schema::table('users', function (Blueprint $table) {
+ $table->softDeletes()->after('account_tokens');
});
}
@@ -25,8 +25,8 @@ class AddTeamIDToVacancy extends Migration
*/
public function down()
{
- Schema::table('vacancy', function (Blueprint $table) {
- //
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropSoftDeletes();
});
}
}
diff --git a/public/js/app.js b/public/js/app.js
index e95f7a4..129c3c6 100644
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -77279,6 +77279,7 @@ $("#submitComment").on('click', function () {
$("#newComment").submit();
});
$("#jointype").bootstrapToggle();
+$("#associatedVacancies").multiselect();
/***/ }),
@@ -77408,10 +77409,10 @@ window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
-__webpack_require__(/*! /home/miguel456/staffmanager/resources/js/app.js */"./resources/js/app.js");
-__webpack_require__(/*! /home/miguel456/staffmanager/resources/js/application_charts.js */"./resources/js/application_charts.js");
-__webpack_require__(/*! /home/miguel456/staffmanager/resources/js/calendar.js */"./resources/js/calendar.js");
-module.exports = __webpack_require__(/*! /home/miguel456/staffmanager/resources/sass/app.scss */"./resources/sass/app.scss");
+__webpack_require__(/*! /home/miguel456/Desktop/Projects/staffmanager/resources/js/app.js */"./resources/js/app.js");
+__webpack_require__(/*! /home/miguel456/Desktop/Projects/staffmanager/resources/js/application_charts.js */"./resources/js/application_charts.js");
+__webpack_require__(/*! /home/miguel456/Desktop/Projects/staffmanager/resources/js/calendar.js */"./resources/js/calendar.js");
+module.exports = __webpack_require__(/*! /home/miguel456/Desktop/Projects/staffmanager/resources/sass/app.scss */"./resources/sass/app.scss");
/***/ })
diff --git a/public/js/globaltooltip.js b/public/js/globaltooltip.js
index 3ffa14a..5f4b1ab 100644
--- a/public/js/globaltooltip.js
+++ b/public/js/globaltooltip.js
@@ -1,4 +1,5 @@
$(document).ready(function() {
$('input[rel="txtTooltip"]').tooltip();
$('span[rel="spanTxtTooltip"]').tooltip(); // Also allow span tooltips
+ $('button[rel="buttonTxtTooltip"]').tooltip(); // button tooltip
});
diff --git a/public/js/team-editor.js b/public/js/team-editor.js
new file mode 100644
index 0000000..c5c3e15
--- /dev/null
+++ b/public/js/team-editor.js
@@ -0,0 +1,6 @@
+$(function() {
+ $('#assocVacancies').multiselect({
+ disableIfEmpty: true,
+ nonSelectedText: 'Choose vacancies...'
+ });
+});
\ No newline at end of file
diff --git a/resources/js/app.js b/resources/js/app.js
index b4bbaa0..08bd18f 100644
--- a/resources/js/app.js
+++ b/resources/js/app.js
@@ -45,3 +45,4 @@ $("#submitComment").on('click', function(){
});
$("#jointype").bootstrapToggle();
+
diff --git a/resources/views/dashboard/teams/edit-team.blade.php b/resources/views/dashboard/teams/edit-team.blade.php
index 8d57e82..169f4a0 100644
--- a/resources/views/dashboard/teams/edit-team.blade.php
+++ b/resources/views/dashboard/teams/edit-team.blade.php
@@ -9,6 +9,7 @@
@section('js')
Team members and pending invites will appear here. Start inviting some people and grow your team.
+
+
+
+ @else
+
+
+
+
+
+
+
+ @foreach ($team->users as $teammate)
+
+ #
+ Name
+ Roles
+ Status
+ Actions
+
+
+
+ @endforeach
+
+
+
+ {{ $teammate->id }}
+ {{ $teammate->name }}
+
+ @foreach ($teammate->roles as $teammate_role)
+
+ {{ $teammate_role->name }}
+
+ @endforeach
+
+
+ @if ($teammate->isOwnerOfTeam($team))
+ Team Owner
+ @else
+ Team Member
+ @endif
+
+
+
+
+
Deleting your account is an irreversible process. The following data will be deleted (including personally identifiable data):
+What is not deleted:
+Careful! Actions in these tab might result in irreversible loss of data.
+ + + ++ |
+
+
+
+
+
|
+ + |