Compare commits
15 Commits
master
...
feature/RS
Author | SHA1 | Date |
---|---|---|
Miguel Nogueira | 82123b1c26 | |
Miguel Nogueira | 2a43e213f9 | |
Miguel Nogueira | b2adcee51e | |
Miguel Nogueira | 077ead9612 | |
Miguel Nogueira | 6cc99d2ebe | |
Miguel Nogueira | 0930c29b9a | |
Miguel Nogueira | 596a469e15 | |
Miguel Nogueira | 75f4404259 | |
Miguel Nogueira | 7c0c244e21 | |
Miguel Nogueira | 982854d5c6 | |
Miguel Nogueira | 551741c687 | |
Miguel Nogueira | dbeddd6fdb | |
Miguel Nogueira | b68449d3bf | |
Miguel Nogueira | 67d1df7571 | |
Miguel Nogueira | d4f1b433dc |
|
@ -2,11 +2,14 @@
|
|||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/database/factories" isTestSource="false" packagePrefix="Database\Factories\" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" packagePrefix="Tests\" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/database/seeders" isTestSource="false" packagePrefix="Database\Seeders\" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/app" isTestSource="false" packagePrefix="App\" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/almasaeed2010/adminlte" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/asm89/stack-cors" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/awssat/discord-notification-channel" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/barryvdh/laravel-debugbar" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/brick/math" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/clue/stream-filter" />
|
||||
|
@ -27,6 +30,7 @@
|
|||
<excludeFolder url="file://$MODULE_DIR$/vendor/filp/whoops" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/fruitcake/laravel-cors" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/fzaninotto/faker" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/graham-campbell/result-type" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
|
||||
|
@ -44,6 +48,7 @@
|
|||
<excludeFolder url="file://$MODULE_DIR$/vendor/mcamara/laravel-localization" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/mockery/mockery" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/monolog/monolog" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/mpociot/teamwork" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/myclabs/deep-copy" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/nesbot/carbon" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/nikic/php-parser" />
|
||||
|
@ -66,6 +71,7 @@
|
|||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/prophecy" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-code-coverage" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-file-iterator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-invoker" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-text-template" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-timer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-token-stream" />
|
||||
|
@ -82,12 +88,16 @@
|
|||
<excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/collection" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/uuid" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/scrivo/highlight.php" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/cli-parser" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/comparator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/complexity" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/diff" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/environment" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/exporter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/global-state" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/lines-of-code" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-enumerator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-reflector" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/recursion-context" />
|
||||
|
@ -106,6 +116,8 @@
|
|||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/event-dispatcher-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/finder" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-client" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-client-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-kernel" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/mime" />
|
||||
|
|
|
@ -141,9 +141,19 @@
|
|||
<path value="$PROJECT_DIR$/vendor/graham-campbell/markdown" />
|
||||
<path value="$PROJECT_DIR$/vendor/league/mime-type-detection" />
|
||||
<path value="$PROJECT_DIR$/vendor/mcamara/laravel-localization" />
|
||||
<path value="$PROJECT_DIR$/vendor/mpociot/teamwork" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/code-unit" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/cli-parser" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/complexity" />
|
||||
<path value="$PROJECT_DIR$/vendor/sebastian/lines-of-code" />
|
||||
<path value="$PROJECT_DIR$/vendor/graham-campbell/result-type" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/http-client" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/http-client-contracts" />
|
||||
<path value="$PROJECT_DIR$/vendor/phpunit/php-invoker" />
|
||||
<path value="$PROJECT_DIR$/vendor/awssat/discord-notification-channel" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="7.2" />
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="7.3" />
|
||||
<component name="PhpUnit">
|
||||
<phpunit_settings>
|
||||
<PhpUnitSettings configuration_file_path="$PROJECT_DIR$/phpunit.xml" custom_loader_path="$PROJECT_DIR$/vendor/autoload.php" use_configuration_file="true" />
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -3,7 +3,17 @@
|
|||
|
||||
namespace App\Facades;
|
||||
use \Illuminate\Support\Facades\Facade;
|
||||
use phpDocumentor\Reflection\Types\Boolean;
|
||||
|
||||
/**
|
||||
* Class Options
|
||||
* @package App\Facades
|
||||
*
|
||||
* @method static void setOption(string $option, string $value, string $description)
|
||||
* @method static string getOption(string $option)
|
||||
* @method static void changeOption(string $option, string $newValue)
|
||||
* @method static Boolean optionExists(string $option)
|
||||
*/
|
||||
class Options extends Facade
|
||||
{
|
||||
public static function getFacadeAccessor()
|
||||
|
|
|
@ -13,9 +13,12 @@ class Options
|
|||
public function getOption(string $option): string
|
||||
{
|
||||
$value = Cache::get($option);
|
||||
$fromCache = true;
|
||||
|
||||
if (is_null($value))
|
||||
{
|
||||
$fromCache = false;
|
||||
|
||||
Log::debug('Option ' . $option . 'not found in cache, refreshing from database');
|
||||
$value = Option::where('option_name', $option)->first();
|
||||
if (is_null($value))
|
||||
|
@ -25,7 +28,9 @@ class Options
|
|||
Cache::put($option . '_desc', 'Undefined description');
|
||||
}
|
||||
|
||||
return $value->option_value;
|
||||
return (!$fromCache)
|
||||
? $value->option_value
|
||||
: $value;
|
||||
}
|
||||
|
||||
public function setOption(string $option, string $value, string $description)
|
||||
|
|
|
@ -33,6 +33,14 @@ class FormController extends Controller
|
|||
$this->authorize('create', Form::class);
|
||||
$fields = $request->all();
|
||||
|
||||
if (count($fields) == 2)
|
||||
{
|
||||
// form is probably empty, since forms with fields will alawys have more than 2 items
|
||||
|
||||
$request->session()->flash('error', 'Sorry, but you may not create empty forms.');
|
||||
return redirect()->to(route('showForms'));
|
||||
}
|
||||
|
||||
$contextValidation = ContextAwareValidator::getValidator($fields, true, true);
|
||||
|
||||
if (!$contextValidation->get('validator')->fails())
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\EditTeamRequest;
|
||||
use App\Http\Requests\NewTeamRequest;
|
||||
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;
|
||||
|
||||
|
||||
class TeamController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$teams = Team::with('users.roles')->get();
|
||||
|
||||
return view('dashboard.teams.teams')
|
||||
->with('teams', $teams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(NewTeamRequest $request)
|
||||
{
|
||||
$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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit(Team $team)
|
||||
{
|
||||
return view('dashboard.teams.edit-team')
|
||||
->with('team', $team)
|
||||
->with('users', User::all())
|
||||
->with('vacancies', Vacancy::with('teams')->get()->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(EditTeamRequest $request, Team $team)
|
||||
{
|
||||
$team->description = $request->teamDescription;
|
||||
$team->openJoin = $request->joinType;
|
||||
|
||||
$team->save();
|
||||
|
||||
$request->session()->flash('success', 'Team edited successfully.');
|
||||
return redirect()->to(route('teams.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function invite(SendInviteRequest $request, Team $team)
|
||||
{
|
||||
$user = User::findOrFail($request->user);
|
||||
|
||||
if (!$team->openJoin)
|
||||
{
|
||||
|
||||
if (!Teamwork::hasPendingInvite($user->email, $team))
|
||||
{
|
||||
Teamwork::inviteToTeam($user, $team, function(TeamInvite $invite) use ($user) {
|
||||
Mail::to($user)->send(new InviteToTeam($invite));
|
||||
});
|
||||
|
||||
$request->session()->flash('success', 'Invite sent! They can now accept or deny it.');
|
||||
}
|
||||
else
|
||||
{
|
||||
$request->session()->flash('error', 'This user has already been invited.');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$request->session()->flash('error', 'You can\'t invite users to public teams.');
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function processInviteAction(Request $request, $action, $token)
|
||||
{
|
||||
|
||||
switch($action)
|
||||
{
|
||||
case 'accept':
|
||||
|
||||
$invite = Teamwork::getInviteFromAcceptToken($token);
|
||||
|
||||
if ($invite && $invite->user->is(Auth::user()))
|
||||
{
|
||||
Teamwork::acceptInvite($invite);
|
||||
$request->session()->flash('success', 'Invite accepted! You have now joined ' . $invite->team->name . '.');
|
||||
}
|
||||
else
|
||||
{
|
||||
$request->session()->flash('error', 'Invalid or expired invite URL.');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'deny':
|
||||
|
||||
$invite = Teamwork::getInviteFromDenyToken($token);
|
||||
|
||||
if ($invite && $invite->user->is(Auth::user()))
|
||||
{
|
||||
Teamwork::denyInvite($invite);
|
||||
$request->session()->flash('success', 'Invite denied! Ask for another invite if this isn\'t what you meant.');
|
||||
}
|
||||
else
|
||||
{
|
||||
$request->session()->flash('error', 'Invalid or expired invite URL.');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$request->session()->flash('error', 'Sorry, but the invite URL you followed was malformed. Try asking for another invite, or submit a bug report.');
|
||||
|
||||
|
||||
}
|
||||
|
||||
// This page will show the user's current teams
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
// Since it's a separate form, we shouldn't use the same update method
|
||||
public function assignVacancies(Request $request, Team $team)
|
||||
{
|
||||
// P.S. To future developers
|
||||
// This method gave me a lot of trouble lol. It's hard to write code when you're half asleep.
|
||||
// There may be an n+1 query in the view and I don't think there's a way to avoid that without writing a lot of extra code.
|
||||
|
||||
$requestVacancies = $request->assocVacancies;
|
||||
$currentVacancies = $team->vacancies->pluck('id')->all();
|
||||
|
||||
if (is_null($requestVacancies))
|
||||
{
|
||||
|
||||
foreach ($team->vacancies as $vacancy)
|
||||
{
|
||||
$team->vacancies()->detach($vacancy->id);
|
||||
}
|
||||
|
||||
$request->session()->flash('success', 'Removed all vacancy associations.');
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
$vacancyDiff = array_diff($requestVacancies, $currentVacancies);
|
||||
$deselectedDiff = array_diff($currentVacancies, $requestVacancies);
|
||||
|
||||
|
||||
if (!empty($vacancyDiff) || !empty($deselectedDiff))
|
||||
{
|
||||
foreach ($vacancyDiff as $selectedVacancy)
|
||||
{
|
||||
$team->vacancies()->attach($selectedVacancy);
|
||||
}
|
||||
|
||||
foreach ($deselectedDiff as $deselectedVacancy)
|
||||
{
|
||||
$team->vacancies()->detach($deselectedVacancy);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$team->vacancies()->attach($requestVacancies);
|
||||
}
|
||||
|
||||
$request->session()->flash('success', 'Assignments changed successfully.');
|
||||
return redirect()->back();
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class EditTeamRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'teamDescription' => 'required|string|max:200',
|
||||
'joinType' => 'required|boolean'
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class NewTeamRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'teamName' => 'required|max:200|string'
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class SendInviteRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'user' => 'required|integer'
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class UserDeleteRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
if (Auth::user()->has2FA())
|
||||
{
|
||||
return [
|
||||
'currentPassword' => 'required|password:web',
|
||||
'otp' => 'required|integer|max:6'
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'currentPassword' => 'required|password:web'
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Mpociot\Teamwork\TeamInvite;
|
||||
|
||||
class InviteToTeam extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
|
||||
public $teamName;
|
||||
|
||||
|
||||
public $name;
|
||||
|
||||
|
||||
public $inviterName;
|
||||
|
||||
|
||||
public $denyToken;
|
||||
|
||||
|
||||
public $acceptToken;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(TeamInvite $invite)
|
||||
{
|
||||
$this->teamName = $invite->team->name;
|
||||
$this->name = $invite->user->name;
|
||||
$this->inviterName = $invite->inviter->name;
|
||||
$this->acceptToken = $invite->accept_token;
|
||||
$this->denyToken = $invite->deny_token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this
|
||||
->subject('You have just been invited to ' . $this->teamName)
|
||||
->view('mail.invited-to-team');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
use App\User;
|
||||
|
||||
class UserAccountDeleteConfirmation extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
|
||||
|
||||
public $deleteToken;
|
||||
|
||||
|
||||
public $cancelToken;
|
||||
|
||||
|
||||
public $originalIP;
|
||||
|
||||
|
||||
public $name;
|
||||
|
||||
|
||||
public $userID;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(User $user, array $tokens, string $originalIP)
|
||||
{
|
||||
$this->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');
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ namespace App\Notifications;
|
|||
|
||||
use App\Facades\Options;
|
||||
use App\Traits\Cancellable;
|
||||
use App\Traits\DiscordRoutable;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
@ -13,7 +14,7 @@ use App\Application;
|
|||
|
||||
class ApplicationApproved extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable, Cancellable;
|
||||
use Queueable, Cancellable, DiscordRoutable;
|
||||
|
||||
public $application;
|
||||
|
||||
|
@ -80,6 +81,11 @@ class ApplicationApproved extends Notification implements ShouldQueue
|
|||
});
|
||||
}
|
||||
|
||||
public function toDiscord($notifiable)
|
||||
{
|
||||
return $this->toSlack($notifiable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Facades\Options;
|
||||
use App\Traits\DiscordRoutable;
|
||||
use Awssat\Notifications\Messages\DiscordMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
@ -11,7 +14,7 @@ use App\Application;
|
|||
|
||||
class ApplicationDenied extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
use Queueable, DiscordRoutable;
|
||||
|
||||
|
||||
public $application;
|
||||
|
@ -34,14 +37,22 @@ class ApplicationDenied extends Notification implements ShouldQueue
|
|||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['mail', 'slack'];
|
||||
$options = ['mail'];
|
||||
|
||||
if (Options::getOption('enable_discord_notifications'))
|
||||
array_push($options, 'discord');
|
||||
|
||||
if (Options::getOption('enable_slack_notifications'))
|
||||
array_push($options, 'slack');
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||
* @return MailMessage
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
|
@ -70,6 +81,13 @@ class ApplicationDenied extends Notification implements ShouldQueue
|
|||
});
|
||||
|
||||
}
|
||||
|
||||
public function toDiscord($notifiable)
|
||||
{
|
||||
// SlackMessage is similar to DiscordMessage, so they're compatible
|
||||
return $this->toSlack($notifiable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Traits\DiscordRoutable;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
@ -15,7 +16,7 @@ use App\Facades\Options;
|
|||
|
||||
class NewApplicant extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable, Cancellable;
|
||||
use Queueable, Cancellable, DiscordRoutable;
|
||||
|
||||
|
||||
protected $application;
|
||||
|
@ -26,7 +27,8 @@ class NewApplicant extends Notification implements ShouldQueue
|
|||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @return void
|
||||
* @param Application $application
|
||||
* @param Vacancy $vacancy
|
||||
*/
|
||||
public function __construct(Application $application, Vacancy $vacancy)
|
||||
{
|
||||
|
@ -36,12 +38,7 @@ class NewApplicant extends Notification implements ShouldQueue
|
|||
|
||||
public function channels()
|
||||
{
|
||||
if (Options::getOption('enable_slack_notifications') == 1)
|
||||
{
|
||||
return ['slack'];
|
||||
}
|
||||
|
||||
return [];
|
||||
$this->chooseChannelsViaOptions();
|
||||
}
|
||||
|
||||
public function optOut($notifiable)
|
||||
|
@ -90,6 +87,12 @@ class NewApplicant extends Notification implements ShouldQueue
|
|||
->action('Review application', $url);
|
||||
});
|
||||
}
|
||||
|
||||
public function toDiscord($notifiable)
|
||||
{
|
||||
return $this->toSlack($notifiable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
|
|
|
@ -21,7 +21,8 @@ class NewComment extends Notification implements ShouldQueue
|
|||
/**
|
||||
* Create a new notification instance.
|
||||
*
|
||||
* @return void
|
||||
* @param Comment $comment
|
||||
* @param Application $application
|
||||
*/
|
||||
public function __construct(Comment $comment, Application $application)
|
||||
{
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Traits\DiscordRoutable;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
@ -15,7 +16,7 @@ use App\Facades\Options;
|
|||
|
||||
class NewUser extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable, Cancellable;
|
||||
use Queueable, Cancellable, DiscordRoutable;
|
||||
|
||||
public $user;
|
||||
|
||||
|
@ -83,6 +84,11 @@ class NewUser extends Notification implements ShouldQueue
|
|||
});
|
||||
}
|
||||
|
||||
public function toDiscord($notifiable)
|
||||
{
|
||||
return $this->toSlack($notifiable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ class Options extends Model
|
|||
{
|
||||
public $fillable = [
|
||||
'option_name',
|
||||
'option_value'
|
||||
'option_value',
|
||||
'friendly_name'
|
||||
];
|
||||
}
|
||||
|
|
|
@ -6,8 +6,11 @@ use App\Application;
|
|||
use App\Observers\ApplicationObserver;
|
||||
use App\Observers\UserObserver;
|
||||
use App\User;
|
||||
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
use Sentry;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
|
@ -34,6 +37,9 @@ class AppServiceProvider extends ServiceProvider
|
|||
]);
|
||||
|
||||
Schema::defaultStringLength(191);
|
||||
|
||||
// Keep using Bootstrap; Laravel 8 has the paginator use Tailwind. Quite opinionated tbh
|
||||
Paginator::useBootstrap();
|
||||
|
||||
User::observe(UserObserver::class);
|
||||
Application::observe(ApplicationObserver::class);
|
||||
|
|
|
@ -7,14 +7,6 @@ use Illuminate\Support\Facades\Route;
|
|||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* This namespace is applied to your controller routes.
|
||||
*
|
||||
* In addition, it is set as the URL generator's root namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'App\Http\Controllers';
|
||||
|
||||
/**
|
||||
* The path to the "home" route for your application.
|
||||
|
@ -59,7 +51,6 @@ class RouteServiceProvider extends ServiceProvider
|
|||
protected function mapWebRoutes()
|
||||
{
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web.php'));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Vacancy;
|
||||
use App\Application;
|
||||
|
||||
class VacancyApplicationService
|
||||
{
|
||||
|
||||
/**
|
||||
* Finds all applications associated with $model.
|
||||
*
|
||||
* @param Vacancy $model The model you want to search through.
|
||||
* @return Illuminate\Support\Collection A collection of applications
|
||||
*/
|
||||
public function findApplications(Vacancy $model)
|
||||
{
|
||||
|
||||
$applications = collect([]);
|
||||
|
||||
foreach(Application::all() as $application)
|
||||
{
|
||||
if ($application->response->vacancy->id == $model->id)
|
||||
{
|
||||
$applications->push($application);
|
||||
}
|
||||
}
|
||||
|
||||
return $applications;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Mpociot\Teamwork\TeamworkTeam;
|
||||
|
||||
class Team extends TeamworkTeam
|
||||
{
|
||||
public $fillable = [
|
||||
'owner_id',
|
||||
'name',
|
||||
'description',
|
||||
'openJoin'
|
||||
];
|
||||
|
||||
|
||||
public function vacancies()
|
||||
{
|
||||
return $this->belongsToMany('App\Vacancy', 'team_has_vacancy');
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@ use App\Facades\Options;
|
|||
trait Cancellable
|
||||
{
|
||||
|
||||
// This method is only used if you want this default set of channels;
|
||||
// Other channels can always be configured by overloading the channels method here.
|
||||
public function chooseChannelsViaOptions()
|
||||
{
|
||||
$channels = [];
|
||||
|
@ -17,11 +19,17 @@ trait Cancellable
|
|||
{
|
||||
array_push($channels, 'slack');
|
||||
}
|
||||
elseif(Options::getOption('enable_email_notifications') == 1)
|
||||
|
||||
if (Options::getOption('enable_email_notifications') == 1)
|
||||
{
|
||||
array_push($channels, 'email');
|
||||
}
|
||||
|
||||
if (Options::getOption('enable_discord_notifications'))
|
||||
{
|
||||
array_push($channels, 'discord');
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
|
||||
trait DiscordRoutable
|
||||
{
|
||||
|
||||
public function routeNotificationForDiscord()
|
||||
{
|
||||
return config('channels.notifications.discord.webhook_url');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
trait HandlesAccountTokens
|
||||
{
|
||||
|
||||
|
||||
public function generateAccountTokens()
|
||||
{
|
||||
$deleteToken = bin2hex(openssl_random_pseudo_bytes(32));
|
||||
$cancelToken = bin2hex(openssl_random_pseudo_bytes(32));
|
||||
|
||||
$tokens = [
|
||||
|
||||
'delete' => 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
|
||||
use App\Http\Requests\UserDeleteRequest;
|
||||
use App\Mail\UserAccountDeleteConfirmation;
|
||||
use App\User;
|
||||
|
||||
|
||||
trait ReceivesAccountTokens
|
||||
{
|
||||
public function userDelete(UserDeleteRequest $request)
|
||||
{
|
||||
// a little verbose
|
||||
$user = User::find(Auth::user()->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.');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
11
app/User.php
11
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 Notifiable;
|
||||
use 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');
|
||||
|
|
|
@ -5,12 +5,16 @@ namespace App;
|
|||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Mpociot\Teamwork\Traits\UsedByTeams;
|
||||
|
||||
|
||||
use GrahamCampbell\Markdown\Facades\Markdown;
|
||||
|
||||
|
||||
class Vacancy extends Model
|
||||
{
|
||||
//use UsedByTeams;
|
||||
|
||||
public $fillable = [
|
||||
|
||||
'permissionGroupName',
|
||||
|
@ -21,7 +25,8 @@ class Vacancy extends Model
|
|||
'vacancyFormID',
|
||||
'vacancyCount',
|
||||
'vacancyStatus',
|
||||
'vacancySlug'
|
||||
'vacancySlug',
|
||||
'team_id'
|
||||
|
||||
];
|
||||
|
||||
|
@ -45,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');
|
||||
|
@ -69,4 +80,32 @@ class Vacancy extends Model
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the Modal is attached to the $checkingTeam Model
|
||||
*
|
||||
* @param Team $checkingTeam The mdoel you want to check against
|
||||
* @return boolean Whether the models are attached
|
||||
*/
|
||||
public function hasTeam(Team $checkingTeam): bool
|
||||
{
|
||||
$myTeams = $this->teams;
|
||||
|
||||
if (empty($myTeams))
|
||||
{
|
||||
// no associated teams
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($myTeams as $team)
|
||||
{
|
||||
if ($team->id === $checkingTeam->id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,33 +8,35 @@
|
|||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.2.5",
|
||||
"php": "^7.3.4",
|
||||
"ext-imagick": "*",
|
||||
"ext-json": "*",
|
||||
"arcanedev/log-viewer": "^7.0",
|
||||
"arcanedev/log-viewer": "^8.0",
|
||||
"awssat/discord-notification-channel": "^1.4",
|
||||
"doctrine/dbal": "^2.10",
|
||||
"fideloper/proxy": "^4.2",
|
||||
"fruitcake/laravel-cors": "^1.0",
|
||||
"geo-sot/laravel-env-editor": "^0.9.9",
|
||||
"graham-campbell/markdown": "^12.0",
|
||||
"guzzlehttp/guzzle": "^6.5",
|
||||
"graham-campbell/markdown": "^13.1",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"jeroennoten/laravel-adminlte": "^3.2",
|
||||
"laravel/framework": "^7.0",
|
||||
"laravel/framework": "^8.0",
|
||||
"laravel/slack-notification-channel": "^2.0",
|
||||
"laravel/tinker": "^2.0",
|
||||
"laravel/ui": "^2.0",
|
||||
"laravel/ui": "^3.0",
|
||||
"mcamara/laravel-localization": "^1.5",
|
||||
"mpociot/teamwork": "^6.0",
|
||||
"pragmarx/google2fa-laravel": "^1.3",
|
||||
"sentry/sentry-laravel": "1.7.1",
|
||||
"sentry/sentry-laravel": "2.1.1",
|
||||
"spatie/laravel-permission": "^3.13"
|
||||
},
|
||||
"require-dev": {
|
||||
"barryvdh/laravel-debugbar": "^3.3",
|
||||
"facade/ignition": "^2.0",
|
||||
"facade/ignition": "^2.3.6",
|
||||
"fzaninotto/faker": "^1.9.1",
|
||||
"mockery/mockery": "^1.3.1",
|
||||
"nunomaduro/collision": "^4.1",
|
||||
"phpunit/phpunit": "^8.5"
|
||||
"nunomaduro/collision": "^5.0",
|
||||
"phpunit/phpunit": "^9.0"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
|
@ -48,12 +50,10 @@
|
|||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/"
|
||||
},
|
||||
"classmap": [
|
||||
"database/seeds",
|
||||
"database/factories"
|
||||
]
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -299,6 +299,12 @@ return [
|
|||
'url' => '/hr/players',
|
||||
'can' => 'admin.userlist'
|
||||
],
|
||||
[
|
||||
'text' => 'm_teams',
|
||||
'icon' => 'fas fa-user-friends',
|
||||
'url' => 'teams',
|
||||
'can' => 'teams.view'
|
||||
],
|
||||
[
|
||||
'text' => 'sm_hiring_man',
|
||||
'icon' => 'far fa-calendar-plus',
|
||||
|
@ -529,6 +535,17 @@ return [
|
|||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => 'CheckboxValues',
|
||||
'active' => true,
|
||||
'files' => [
|
||||
[
|
||||
'type' => 'js',
|
||||
'asset' => false,
|
||||
'location' => '/js/switches.js'
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => 'AuthCustomisations',
|
||||
'active' => true,
|
||||
|
@ -539,6 +556,38 @@ return [
|
|||
'location' => '/css/authpages.css'
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'name' => 'BootstrapToggleButton',
|
||||
'active' => true,
|
||||
'files' => [
|
||||
[
|
||||
'type' => 'css',
|
||||
'asset' => false,
|
||||
'location' => 'https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css'
|
||||
],
|
||||
[
|
||||
'type' => 'js',
|
||||
'asset' => false,
|
||||
'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'
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
];
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'notifications' => [
|
||||
|
||||
'discord' => [
|
||||
'webhook_url' => env('DISCORD_INTEGRATION_WEBHOOK')
|
||||
],
|
||||
|
||||
'slack' => [
|
||||
'webhook_url' => env('SLACK_INTEGRATION_WEBHOOK')
|
||||
]
|
||||
|
||||
]
|
||||
|
||||
];
|
|
@ -81,7 +81,7 @@ return [
|
|||
*/
|
||||
|
||||
'failed' => [
|
||||
'driver' => env('QUEUE_FAILED_DRIVER', 'database'),
|
||||
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
|
||||
'database' => env('DB_CONNECTION', 'mysql'),
|
||||
'table' => 'failed_jobs',
|
||||
],
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Auth Model
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the Auth model used by Teamwork.
|
||||
|
|
||||
*/
|
||||
'user_model' => config('auth.providers.users.model', App\User::class),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Teamwork users Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the users table name used by Teamwork.
|
||||
|
|
||||
*/
|
||||
'users_table' => 'users',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Teamwork Team Model
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the Team model used by Teamwork to create correct relations. Update
|
||||
| the team if it is in a different namespace.
|
||||
|
|
||||
*/
|
||||
'team_model' => Mpociot\Teamwork\TeamworkTeam::class,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Teamwork teams Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the teams table name used by Teamwork to save teams to the database.
|
||||
|
|
||||
*/
|
||||
'teams_table' => 'teams',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Teamwork team_user Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the team_user table used by Teamwork to save assigned teams to the
|
||||
| database.
|
||||
|
|
||||
*/
|
||||
'team_user_table' => 'team_user',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| User Foreign key on Teamwork's team_user Table (Pivot)
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
'user_foreign_key' => 'id',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Teamwork Team Invite Model
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the Team Invite model used by Teamwork to create correct relations.
|
||||
| Update the team if it is in a different namespace.
|
||||
|
|
||||
*/
|
||||
'invite_model' => Mpociot\Teamwork\TeamInvite::class,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Teamwork team invites Table
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the team invites table name used by Teamwork to save sent/pending
|
||||
| invitation into teams to the database.
|
||||
|
|
||||
*/
|
||||
'team_invites_table' => 'team_invites',
|
||||
];
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
||||
class TeamworkSetupTables extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table(\Config::get('teamwork.users_table'), function (Blueprint $table) {
|
||||
$table->integer('current_team_id')->unsigned()->nullable();
|
||||
});
|
||||
|
||||
Schema::create(\Config::get('teamwork.teams_table'), function (Blueprint $table) {
|
||||
$table->increments('id')->unsigned();
|
||||
$table->integer('owner_id')->unsigned()->nullable();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create(\Config::get('teamwork.team_user_table'), function (Blueprint $table) {
|
||||
$table->bigInteger('user_id')->unsigned();
|
||||
$table->integer('team_id')->unsigned();
|
||||
$table->timestamps();
|
||||
|
||||
$table->foreign('user_id')
|
||||
->references(\Config::get('teamwork.user_foreign_key'))
|
||||
->on(\Config::get('teamwork.users_table'))
|
||||
->onUpdate('cascade')
|
||||
->onDelete('cascade');
|
||||
|
||||
$table->foreign('team_id')
|
||||
->references('id')
|
||||
->on(\Config::get('teamwork.teams_table'))
|
||||
->onDelete('cascade');
|
||||
});
|
||||
|
||||
Schema::create(\Config::get('teamwork.team_invites_table'), function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->bigInteger('user_id')->unsigned();
|
||||
$table->integer('team_id')->unsigned();
|
||||
$table->enum('type', ['invite', 'request']);
|
||||
$table->string('email');
|
||||
$table->string('accept_token');
|
||||
$table->string('deny_token');
|
||||
$table->timestamps();
|
||||
$table->foreign('team_id')
|
||||
->references('id')
|
||||
->on(\Config::get('teamwork.teams_table'))
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table(\Config::get('teamwork.users_table'), function (Blueprint $table) {
|
||||
$table->dropColumn('current_team_id');
|
||||
});
|
||||
|
||||
Schema::table(\Config::get('teamwork.team_user_table'), function (Blueprint $table) {
|
||||
if (DB::getDriverName() !== 'sqlite') {
|
||||
$table->dropForeign(\Config::get('teamwork.team_user_table').'_user_id_foreign');
|
||||
}
|
||||
if (DB::getDriverName() !== 'sqlite') {
|
||||
$table->dropForeign(\Config::get('teamwork.team_user_table').'_team_id_foreign');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::drop(\Config::get('teamwork.team_user_table'));
|
||||
Schema::drop(\Config::get('teamwork.team_invites_table'));
|
||||
Schema::drop(\Config::get('teamwork.teams_table'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddTeamDetails extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table(config('teamwork.teams_table'), function(Blueprint $table){
|
||||
|
||||
$table->text('description')->after('name')->nullable();
|
||||
$table->enum('status', ['ACTIVE','SUSPENDED'])->after('description');
|
||||
$table->boolean('openJoin')->default(false)->after('status');
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table(config('teamwork.teams_table'), function(Blueprint $table){
|
||||
|
||||
$table->dropColumn('description');
|
||||
$table->dropColumn('status');
|
||||
$table->dropColumn('openJoin');
|
||||
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class VacancyNullableTeamId extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('vacancies', function(Blueprint $table){
|
||||
|
||||
$table->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');
|
||||
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class TeamHasVacancy extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('team_has_vacancy', function(Blueprint $table){
|
||||
|
||||
$table->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()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddAccountTokensToUser extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('account_tokens')->after('password')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('account_tokens');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddSoftDeletesToUsers extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->softDeletes()->after('account_tokens');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddUuidToFailedJobs extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('failed_jobs', function (Blueprint $table) {
|
||||
$table->string('uuid')->after('id')->nullable()->unique();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('failed_jobs', function (Blueprint $table) {
|
||||
$table->dropColumn('uuid');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Facades\Options;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
@ -22,6 +23,7 @@ class DefaultOptionsSeeder extends Seeder
|
|||
|
||||
Options::setOption('enable_slack_notifications', true, 'Enable slack notifications');
|
||||
Options::setOption('enable_email_notifications', true, 'Enable e-mail notifications');
|
||||
Options::setOption('enable_discord_notifications', true, 'Enable discord notifications');
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Spatie\Permission\Models\Permission;
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use Spatie\Permission\Models\Role;
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
|
||||
class TeamSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
|
||||
Permission::create([
|
||||
'name' => 'teams.user.view.own'
|
||||
]);
|
||||
|
||||
Permission::create([
|
||||
'name' => 'teams.admin.view.all'
|
||||
]);
|
||||
|
||||
// Has access to the teams feature
|
||||
Permission::create([
|
||||
'name' => 'teams.view'
|
||||
]);
|
||||
|
||||
Permission::create([
|
||||
'name' => 'teams.admin.create',
|
||||
]);
|
||||
Permission::create([
|
||||
'name' => 'teams.admin.delete',
|
||||
]);
|
||||
Permission::create([
|
||||
'name' => 'teams.user.join',
|
||||
]);
|
||||
Permission::create([
|
||||
'name' => 'teams.user.leave',
|
||||
]);
|
||||
Permission::create([
|
||||
'name' => 'teams.admin.vacancies.assign',
|
||||
]);
|
||||
Permission::create([
|
||||
'name' => 'teams.admin.vacancies.unassign',
|
||||
]);
|
||||
Permission::create([
|
||||
'name' => 'teams.admin.applications.changeteam',
|
||||
]);
|
||||
Permission::create([
|
||||
'name' => 'teams.members.appointment.schedule',
|
||||
]);
|
||||
Permission::create([
|
||||
'name' => 'teams.members.appointment.deleteappointment',
|
||||
]);
|
||||
Permission::create([
|
||||
'name' => 'teams.members.groupchat',
|
||||
]);
|
||||
|
||||
Permission::create([
|
||||
'name' => 'chat.use',
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Profile;
|
||||
use App\User;
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"postinstall": "npm run prod",
|
||||
"postinstall": "npm run prod",
|
||||
"dev": "npm run development",
|
||||
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"watch": "npm run development -- --watch",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
@import url(https://fonts.googleapis.com/css?family=Nunito);@charset "UTF-8";
|
||||
|
||||
/*!
|
||||
* Bootstrap v4.4.1 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2019 The Bootstrap Authors
|
||||
* Copyright 2011-2019 Twitter, Inc.
|
||||
* Bootstrap v4.5.0 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2020 The Bootstrap Authors
|
||||
* Copyright 2011-2020 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
|
@ -202,6 +202,7 @@ pre {
|
|||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
|
@ -269,6 +270,10 @@ select {
|
|||
text-transform: none;
|
||||
}
|
||||
|
||||
[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
select {
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
@ -301,13 +306,6 @@ input[type=checkbox] {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
input[type=date],
|
||||
input[type=time],
|
||||
input[type=datetime-local],
|
||||
input[type=month] {
|
||||
-webkit-appearance: listbox;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
|
@ -749,6 +747,7 @@ pre code {
|
|||
.col {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
@ -956,6 +955,7 @@ pre code {
|
|||
.col-sm {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
@ -1168,6 +1168,7 @@ pre code {
|
|||
.col-md {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
@ -1380,6 +1381,7 @@ pre code {
|
|||
.col-lg {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
@ -1592,6 +1594,7 @@ pre code {
|
|||
.col-xl {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
@ -2187,11 +2190,6 @@ pre code {
|
|||
box-shadow: 0 0 0 0.2rem rgba(52, 144, 220, 0.25);
|
||||
}
|
||||
|
||||
.form-control::-webkit-input-placeholder {
|
||||
color: #6c757d;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.form-control::-moz-placeholder {
|
||||
color: #6c757d;
|
||||
opacity: 1;
|
||||
|
@ -2218,6 +2216,15 @@ pre code {
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
input[type=date].form-control,
|
||||
input[type=time].form-control,
|
||||
input[type=datetime-local].form-control,
|
||||
input[type=month].form-control {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
select.form-control:focus::-ms-value {
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
|
@ -2653,7 +2660,6 @@ textarea.form-control.is-invalid {
|
|||
color: #212529;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
|
@ -2689,6 +2695,10 @@ textarea.form-control.is-invalid {
|
|||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.btn:not(:disabled):not(.disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a.btn.disabled,
|
||||
fieldset:disabled a.btn {
|
||||
pointer-events: none;
|
||||
|
@ -3324,7 +3334,6 @@ fieldset:disabled a.btn {
|
|||
.btn-link:focus,
|
||||
.btn-link.focus {
|
||||
text-decoration: underline;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.btn-link:disabled,
|
||||
|
@ -3789,7 +3798,8 @@ input[type=button].btn-block {
|
|||
.input-group > .custom-select,
|
||||
.input-group > .custom-file {
|
||||
position: relative;
|
||||
flex: 1 1 0%;
|
||||
flex: 1 1 auto;
|
||||
width: 1%;
|
||||
min-width: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
@ -4889,7 +4899,7 @@ input[type=button].btn-block {
|
|||
}
|
||||
|
||||
.navbar-light .navbar-toggler-icon {
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
.navbar-light .navbar-text {
|
||||
|
@ -4940,7 +4950,7 @@ input[type=button].btn-block {
|
|||
}
|
||||
|
||||
.navbar-dark .navbar-toggler-icon {
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
|
||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-text {
|
||||
|
@ -4973,14 +4983,21 @@ input[type=button].btn-block {
|
|||
margin-left: 0;
|
||||
}
|
||||
|
||||
.card > .list-group:first-child .list-group-item:first-child {
|
||||
border-top-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0.25rem;
|
||||
.card > .list-group {
|
||||
border-top: inherit;
|
||||
border-bottom: inherit;
|
||||
}
|
||||
|
||||
.card > .list-group:last-child .list-group-item:last-child {
|
||||
border-bottom-right-radius: 0.25rem;
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
.card > .list-group:first-child {
|
||||
border-top-width: 0;
|
||||
border-top-left-radius: calc(0.25rem - 1px);
|
||||
border-top-right-radius: calc(0.25rem - 1px);
|
||||
}
|
||||
|
||||
.card > .list-group:last-child {
|
||||
border-bottom-width: 0;
|
||||
border-bottom-right-radius: calc(0.25rem - 1px);
|
||||
border-bottom-left-radius: calc(0.25rem - 1px);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
|
@ -5196,6 +5213,10 @@ input[type=button].btn-block {
|
|||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.breadcrumb-item {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.breadcrumb-item + .breadcrumb-item {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
@ -5667,6 +5688,7 @@ a.badge-dark.focus {
|
|||
display: flex;
|
||||
height: 1rem;
|
||||
overflow: hidden;
|
||||
line-height: 0;
|
||||
font-size: 0.675rem;
|
||||
background-color: #e9ecef;
|
||||
border-radius: 0.25rem;
|
||||
|
@ -5721,6 +5743,7 @@ a.badge-dark.focus {
|
|||
flex-direction: column;
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.list-group-item-action {
|
||||
|
@ -5751,13 +5774,13 @@ a.badge-dark.focus {
|
|||
}
|
||||
|
||||
.list-group-item:first-child {
|
||||
border-top-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0.25rem;
|
||||
border-top-left-radius: inherit;
|
||||
border-top-right-radius: inherit;
|
||||
}
|
||||
|
||||
.list-group-item:last-child {
|
||||
border-bottom-right-radius: 0.25rem;
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
border-bottom-right-radius: inherit;
|
||||
border-bottom-left-radius: inherit;
|
||||
}
|
||||
|
||||
.list-group-item.disabled,
|
||||
|
@ -5787,26 +5810,26 @@ a.badge-dark.focus {
|
|||
flex-direction: row;
|
||||
}
|
||||
|
||||
.list-group-horizontal .list-group-item:first-child {
|
||||
.list-group-horizontal > .list-group-item:first-child {
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal .list-group-item:last-child {
|
||||
.list-group-horizontal > .list-group-item:last-child {
|
||||
border-top-right-radius: 0.25rem;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal .list-group-item.active {
|
||||
.list-group-horizontal > .list-group-item.active {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal .list-group-item + .list-group-item {
|
||||
.list-group-horizontal > .list-group-item + .list-group-item {
|
||||
border-top-width: 1px;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal .list-group-item + .list-group-item.active {
|
||||
.list-group-horizontal > .list-group-item + .list-group-item.active {
|
||||
margin-left: -1px;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
@ -5816,26 +5839,26 @@ a.badge-dark.focus {
|
|||
flex-direction: row;
|
||||
}
|
||||
|
||||
.list-group-horizontal-sm .list-group-item:first-child {
|
||||
.list-group-horizontal-sm > .list-group-item:first-child {
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-sm .list-group-item:last-child {
|
||||
.list-group-horizontal-sm > .list-group-item:last-child {
|
||||
border-top-right-radius: 0.25rem;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-sm .list-group-item.active {
|
||||
.list-group-horizontal-sm > .list-group-item.active {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-sm .list-group-item + .list-group-item {
|
||||
.list-group-horizontal-sm > .list-group-item + .list-group-item {
|
||||
border-top-width: 1px;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-sm .list-group-item + .list-group-item.active {
|
||||
.list-group-horizontal-sm > .list-group-item + .list-group-item.active {
|
||||
margin-left: -1px;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
@ -5846,26 +5869,26 @@ a.badge-dark.focus {
|
|||
flex-direction: row;
|
||||
}
|
||||
|
||||
.list-group-horizontal-md .list-group-item:first-child {
|
||||
.list-group-horizontal-md > .list-group-item:first-child {
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-md .list-group-item:last-child {
|
||||
.list-group-horizontal-md > .list-group-item:last-child {
|
||||
border-top-right-radius: 0.25rem;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-md .list-group-item.active {
|
||||
.list-group-horizontal-md > .list-group-item.active {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-md .list-group-item + .list-group-item {
|
||||
.list-group-horizontal-md > .list-group-item + .list-group-item {
|
||||
border-top-width: 1px;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-md .list-group-item + .list-group-item.active {
|
||||
.list-group-horizontal-md > .list-group-item + .list-group-item.active {
|
||||
margin-left: -1px;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
@ -5876,26 +5899,26 @@ a.badge-dark.focus {
|
|||
flex-direction: row;
|
||||
}
|
||||
|
||||
.list-group-horizontal-lg .list-group-item:first-child {
|
||||
.list-group-horizontal-lg > .list-group-item:first-child {
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-lg .list-group-item:last-child {
|
||||
.list-group-horizontal-lg > .list-group-item:last-child {
|
||||
border-top-right-radius: 0.25rem;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-lg .list-group-item.active {
|
||||
.list-group-horizontal-lg > .list-group-item.active {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-lg .list-group-item + .list-group-item {
|
||||
.list-group-horizontal-lg > .list-group-item + .list-group-item {
|
||||
border-top-width: 1px;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-lg .list-group-item + .list-group-item.active {
|
||||
.list-group-horizontal-lg > .list-group-item + .list-group-item.active {
|
||||
margin-left: -1px;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
@ -5906,42 +5929,40 @@ a.badge-dark.focus {
|
|||
flex-direction: row;
|
||||
}
|
||||
|
||||
.list-group-horizontal-xl .list-group-item:first-child {
|
||||
.list-group-horizontal-xl > .list-group-item:first-child {
|
||||
border-bottom-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-xl .list-group-item:last-child {
|
||||
.list-group-horizontal-xl > .list-group-item:last-child {
|
||||
border-top-right-radius: 0.25rem;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-xl .list-group-item.active {
|
||||
.list-group-horizontal-xl > .list-group-item.active {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-xl .list-group-item + .list-group-item {
|
||||
.list-group-horizontal-xl > .list-group-item + .list-group-item {
|
||||
border-top-width: 1px;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.list-group-horizontal-xl .list-group-item + .list-group-item.active {
|
||||
.list-group-horizontal-xl > .list-group-item + .list-group-item.active {
|
||||
margin-left: -1px;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-flush .list-group-item {
|
||||
border-right-width: 0;
|
||||
border-left-width: 0;
|
||||
.list-group-flush {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-flush .list-group-item:first-child {
|
||||
border-top-width: 0;
|
||||
.list-group-flush > .list-group-item {
|
||||
border-width: 0 0 1px;
|
||||
}
|
||||
|
||||
.list-group-flush:last-child .list-group-item:last-child {
|
||||
.list-group-flush > .list-group-item:last-child {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
|
@ -6105,9 +6126,6 @@ button.close {
|
|||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
a.close.disabled {
|
||||
|
@ -6234,6 +6252,9 @@ a.close.disabled {
|
|||
.modal-dialog-centered::before {
|
||||
display: block;
|
||||
height: calc(100vh - 1rem);
|
||||
height: -webkit-min-content;
|
||||
height: -moz-min-content;
|
||||
height: min-content;
|
||||
content: "";
|
||||
}
|
||||
|
||||
|
@ -6351,6 +6372,9 @@ a.close.disabled {
|
|||
|
||||
.modal-dialog-centered::before {
|
||||
height: calc(100vh - 3.5rem);
|
||||
height: -webkit-min-content;
|
||||
height: -moz-min-content;
|
||||
height: min-content;
|
||||
}
|
||||
|
||||
.modal-sm {
|
||||
|
@ -6900,6 +6924,7 @@ a.close.disabled {
|
|||
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6910,6 +6935,7 @@ a.close.disabled {
|
|||
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8201,6 +8227,27 @@ button.bg-dark:focus {
|
|||
}
|
||||
}
|
||||
|
||||
.user-select-all {
|
||||
-webkit-user-select: all !important;
|
||||
-moz-user-select: all !important;
|
||||
-ms-user-select: all !important;
|
||||
user-select: all !important;
|
||||
}
|
||||
|
||||
.user-select-auto {
|
||||
-webkit-user-select: auto !important;
|
||||
-moz-user-select: auto !important;
|
||||
-ms-user-select: auto !important;
|
||||
user-select: auto !important;
|
||||
}
|
||||
|
||||
.user-select-none {
|
||||
-webkit-user-select: none !important;
|
||||
-moz-user-select: none !important;
|
||||
-ms-user-select: none !important;
|
||||
user-select: none !important;
|
||||
}
|
||||
|
||||
.overflow-auto {
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
@ -8357,18 +8404,6 @@ button.bg-dark:focus {
|
|||
height: 100vh !important;
|
||||
}
|
||||
|
||||
.stretched-link::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
pointer-events: auto;
|
||||
content: "";
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.m-0 {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
@ -10537,6 +10572,18 @@ button.bg-dark:focus {
|
|||
}
|
||||
}
|
||||
|
||||
.stretched-link::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
pointer-events: auto;
|
||||
content: "";
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.text-monospace {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important;
|
||||
}
|
||||
|
@ -10768,8 +10815,7 @@ a.text-dark:focus {
|
|||
}
|
||||
|
||||
.text-break {
|
||||
word-break: break-word !important;
|
||||
overflow-wrap: break-word !important;
|
||||
word-wrap: break-word !important;
|
||||
}
|
||||
|
||||
.text-reset {
|
||||
|
|
|
@ -1050,6 +1050,7 @@ Lots taken from Flatly (MIT): https://bootswatch.com/4/flatly/bootstrap.css
|
|||
/* don't display any button-related controls */
|
||||
}
|
||||
}
|
||||
|
||||
/* DayGridView
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
/* day row structure */
|
||||
|
@ -1128,6 +1129,7 @@ Lots taken from Flatly (MIT): https://bootswatch.com/4/flatly/bootstrap.css
|
|||
display: inline-block;
|
||||
min-width: 1.25em;
|
||||
}
|
||||
|
||||
/* Scroller
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
.fc-scroller-clip {
|
||||
|
@ -1479,6 +1481,7 @@ TODO: figure out better styling
|
|||
.fc-rtl .fc-timeline-event.fc-not-start:before {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
@charset "UTF-8";
|
||||
/* TimeGridView all-day area
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
@ -1788,6 +1791,7 @@ be a descendant of the grid when it is being dragged.
|
|||
border-top-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
/* List View
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
/* possibly reusable */
|
||||
|
@ -1906,6 +1910,7 @@ be a descendant of the grid when it is being dragged.
|
|||
/* theme will provide own background */
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.flatpickr-calendar{background:transparent;opacity:0;display:none;text-align:center;visibility:hidden;padding:0;-webkit-animation:none;animation:none;direction:ltr;border:0;font-size:14px;line-height:24px;border-radius:5px;position:absolute;width:307.875px;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-touch-action:manipulation;touch-action:manipulation;background:#fff;-webkit-box-shadow:1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,0.08);box-shadow:1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,0.08);}.flatpickr-calendar.open,.flatpickr-calendar.inline{opacity:1;max-height:640px;visibility:visible}.flatpickr-calendar.open{display:inline-block;z-index:99999}.flatpickr-calendar.animate.open{-webkit-animation:fpFadeInDown 300ms cubic-bezier(.23,1,.32,1);animation:fpFadeInDown 300ms cubic-bezier(.23,1,.32,1)}.flatpickr-calendar.inline{display:block;position:relative;top:2px}.flatpickr-calendar.static{position:absolute;top:calc(100% + 2px);}.flatpickr-calendar.static.open{z-index:999;display:block}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+1) .flatpickr-day.inRange:nth-child(7n+7){-webkit-box-shadow:none !important;box-shadow:none !important}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1){-webkit-box-shadow:-2px 0 0 #e6e6e6,5px 0 0 #e6e6e6;box-shadow:-2px 0 0 #e6e6e6,5px 0 0 #e6e6e6}.flatpickr-calendar .hasWeeks .dayContainer,.flatpickr-calendar .hasTime .dayContainer{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.flatpickr-calendar .hasWeeks .dayContainer{border-left:0}.flatpickr-calendar.showTimeInput.hasTime .flatpickr-time{height:40px;border-top:1px solid #e6e6e6}.flatpickr-calendar.noCalendar.hasTime .flatpickr-time{height:auto}.flatpickr-calendar:before,.flatpickr-calendar:after{position:absolute;display:block;pointer-events:none;border:solid transparent;content:'';height:0;width:0;left:22px}.flatpickr-calendar.rightMost:before,.flatpickr-calendar.rightMost:after{left:auto;right:22px}.flatpickr-calendar:before{border-width:5px;margin:0 -5px}.flatpickr-calendar:after{border-width:4px;margin:0 -4px}.flatpickr-calendar.arrowTop:before,.flatpickr-calendar.arrowTop:after{bottom:100%}.flatpickr-calendar.arrowTop:before{border-bottom-color:#e6e6e6}.flatpickr-calendar.arrowTop:after{border-bottom-color:#fff}.flatpickr-calendar.arrowBottom:before,.flatpickr-calendar.arrowBottom:after{top:100%}.flatpickr-calendar.arrowBottom:before{border-top-color:#e6e6e6}.flatpickr-calendar.arrowBottom:after{border-top-color:#fff}.flatpickr-calendar:focus{outline:0}.flatpickr-wrapper{position:relative;display:inline-block}.flatpickr-months{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}.flatpickr-months .flatpickr-month{background:transparent;color:rgba(0,0,0,0.9);fill:rgba(0,0,0,0.9);height:34px;line-height:1;text-align:center;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:hidden;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.flatpickr-months .flatpickr-prev-month,.flatpickr-months .flatpickr-next-month{text-decoration:none;cursor:pointer;position:absolute;top:0;height:34px;padding:10px;z-index:3;color:rgba(0,0,0,0.9);fill:rgba(0,0,0,0.9);}.flatpickr-months .flatpickr-prev-month.flatpickr-disabled,.flatpickr-months .flatpickr-next-month.flatpickr-disabled{display:none}.flatpickr-months .flatpickr-prev-month i,.flatpickr-months .flatpickr-next-month i{position:relative}.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month,.flatpickr-months .flatpickr-next-month.flatpickr-prev-month{/*
|
||||
/*rtl:begin:ignore*/left:0;/*
|
||||
/*rtl:end:ignore*/}/*
|
||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.4 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 11 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 11 KiB |
|
@ -1,14 +1,23 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Laravel - A PHP Framework For Web Artisans
|
||||
*
|
||||
* @package Laravel
|
||||
* @author Taylor Otwell <taylor@laravel.com>
|
||||
*/
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Check If Application Is Under Maintenance
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| If the application is maintenance / demo mode via the "down" command we
|
||||
| will require this file so that any prerendered template can be shown
|
||||
| instead of starting the framework, which could cause an exception.
|
||||
|
|
||||
*/
|
||||
|
||||
if (file_exists(__DIR__.'/../storage/framework/maintenance.php')) {
|
||||
require __DIR__.'/../storage/framework/maintenance.php';
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,94 @@
|
|||
/* flatpickr v4.6.3, @license MIT */
|
||||
|
||||
/*!
|
||||
* Bootstrap v4.5.0 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Chart.js v2.9.3
|
||||
* https://www.chartjs.org
|
||||
* (c) 2019 Chart.js Contributors
|
||||
* Released under the MIT License
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Sizzle CSS Selector Engine v2.3.5
|
||||
* https://sizzlejs.com/
|
||||
*
|
||||
* Copyright JS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://js.foundation/
|
||||
*
|
||||
* Date: 2020-03-14
|
||||
*/
|
||||
|
||||
/*!
|
||||
* jQuery JavaScript Library v3.5.1
|
||||
* https://jquery.com/
|
||||
*
|
||||
* Includes Sizzle.js
|
||||
* https://sizzlejs.com/
|
||||
*
|
||||
* Copyright JS Foundation and other contributors
|
||||
* Released under the MIT license
|
||||
* https://jquery.org/license
|
||||
*
|
||||
* Date: 2020-05-04T22:49Z
|
||||
*/
|
||||
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Lodash <https://lodash.com/>
|
||||
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
|
||||
* Released under MIT license <https://lodash.com/license>
|
||||
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
|
||||
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||
*/
|
||||
|
||||
/**!
|
||||
* @fileOverview Kickass library to create and place poppers near their reference elements.
|
||||
* @version 1.16.1
|
||||
* @license
|
||||
* Copyright (c) 2016 Federico Zivolo and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
//! moment.js
|
||||
|
||||
//! moment.js language configuration
|
||||
|
||||
//! moment.js locale configuration
|
|
@ -1,3 +1,5 @@
|
|||
$(document).ready(function() {
|
||||
$('input[rel="txtTooltip"]').tooltip();
|
||||
$('span[rel="spanTxtTooltip"]').tooltip(); // Also allow span tooltips
|
||||
$('button[rel="buttonTxtTooltip"]').tooltip(); // button tooltip
|
||||
});
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
$("#jointype").on('change', function() {
|
||||
if ($(this).is(':checked')) {
|
||||
$(this).attr('value', '1');
|
||||
} else {
|
||||
$(this).attr('value', '0');
|
||||
}
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
$(function() {
|
||||
$('#assocVacancies').multiselect({
|
||||
disableIfEmpty: true,
|
||||
nonSelectedText: 'Choose vacancies...'
|
||||
});
|
||||
});
|
|
@ -43,3 +43,6 @@ $("#comment").keyup(function(){
|
|||
$("#submitComment").on('click', function(){
|
||||
$("#newComment").submit();
|
||||
});
|
||||
|
||||
$("#jointype").bootstrapToggle();
|
||||
|
||||
|
|
|
@ -605,6 +605,13 @@ return [
|
|||
'max_chars' => 'max characters', // Context: A number is added before max characters
|
||||
'post' => 'Post', // Context: Post as in post comment
|
||||
|
||||
],
|
||||
|
||||
|
||||
'teams' => [
|
||||
|
||||
'm_teams_page' => 'Teams'
|
||||
|
||||
]
|
||||
|
||||
// ==================== END OF MAIN I18N FILE ======================
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
@extends('adminlte::page')
|
||||
|
||||
@section('title', config('app.name') . ' | ' . __('messages.teams.m_teams_page'))
|
||||
|
||||
@section('content_header')
|
||||
<h1>{{config('app.name')}} / {{__('messages.teams.m_teams_page')}}</h1>
|
||||
@stop
|
||||
|
||||
@section('js')
|
||||
|
||||
<x-global-errors></x-global-errors>
|
||||
<script src="/js/team-editor.js"></script>
|
||||
|
||||
@stop
|
||||
|
||||
|
||||
@section('content')
|
||||
|
||||
@if($team->openJoin == false)
|
||||
<x-modal id="addUserModal" modal-label="addUserModalLabel" modal-title="Invite User" include-close-button="true">
|
||||
|
||||
<form id="inviteToTeam" method="POST" action="{{ route('sendInvite', ['team' => $team->id]) }}">
|
||||
@csrf
|
||||
<div class="form-group">
|
||||
<select class="custom-select" name="user">
|
||||
<option disabled selected>Choose a user to invite</option>
|
||||
@foreach ($users as $user)
|
||||
<option value="{{ $user->id }}" {{ ($user->id == Auth::user()->id) ? 'disabled' : '' }}>{{ $user->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<span class="text-sm text-muted"><i class="fas fa-info-circle"></i> This user will receive an email notification asking them to join your team.</span>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<x-slot name="modalFooter">
|
||||
<button type="button" class="btn btn-success" onclick="$('#inviteToTeam').submit()"><i class="fas fa-paper-plane"></i> Send invite</button>
|
||||
</x-slot>
|
||||
|
||||
</x-modal>
|
||||
@endif
|
||||
|
||||
<x-modal id="userlist_modal" modal-label="UserListModalLabel" modal-title="Team Members and Invites" include-close-button="true">
|
||||
|
||||
<p><i class="fas fa-info-circle"></i> Team members and pending invites will appear here.</p>
|
||||
|
||||
@if (!$team->users->isEmpty())
|
||||
<table class="table table-borderless">
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Name</th>
|
||||
<th>Roles</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
@foreach ($team->users as $teammate)
|
||||
|
||||
<tr>
|
||||
<td>{{ $teammate->id }}</td>
|
||||
<td><a target="_blank" href="{{ route('showSingleProfile', ['user' => $teammate->id]) }}">{{ $teammate->name }}</a></td>
|
||||
<td>
|
||||
@foreach ($teammate->roles as $teammate_role)
|
||||
|
||||
<span class="badge badge-secondary">{{ $teammate_role->name }}</span>
|
||||
|
||||
@endforeach
|
||||
</td>
|
||||
<td>
|
||||
@if ($teammate->isOwnerOfTeam($team))
|
||||
<span class="badge badge-success">Team Owner</span>
|
||||
@else
|
||||
<span class="badge badge-primary">Team Member</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<button rel="buttonTxtTooltip" data-toggle="tooltip" data-placement="top" title="Kick User" type="button" class="btn btn-danger btn-sm"><i class="fas fa-bolt"></i></button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@endforeach
|
||||
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
@else
|
||||
|
||||
<div class="alert alert-warning">
|
||||
|
||||
<span><i class="fas fa-exclamation-triangle"></i> <b>There don't seem to be any teammates here!</b></span>
|
||||
<p>Start inviting some people and grow your team.</p>
|
||||
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
<x-slot name="modalFooter"></x-slot>
|
||||
|
||||
</x-modal>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col text-center">
|
||||
<img src="/img/editable.svg" alt="Edit illustration" height="220px" width="220px">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
<div class="card">
|
||||
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">Edit Team</h4>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<form id="editTeam" method="POST" action="{{ route('teams.update', ['team' => $team->id]) }}">
|
||||
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
|
||||
<div class="form-group">
|
||||
|
||||
<label for="teamName">Team name</label>
|
||||
<input type="text" class="form-control" id="teamName" value="{{ $team->name }}" disabled>
|
||||
|
||||
<label for="teamDescription">Team description</label>
|
||||
<textarea class="form-control" rows="4" name="teamDescription" id="teamDescription">{{ $team->description }}</textarea>
|
||||
<span class="text-left text-muted text-sm"><a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet"><i class="fab fa-markdown"></i></a> Markdown supported</span>
|
||||
|
||||
<div class="controlbuttons mt-3">
|
||||
|
||||
<span rel="spanTxtTooltip" data-toggle="tooltip" title="This setting controls whether people can join the team freely." data-placement="top">
|
||||
<input type="hidden" name="joinType" value="0"> <!-- unchecked checkbox hack (no js) -->
|
||||
<input value="0" type="checkbox" {{ ($team->openJoin == 1) ? 'checked' : '' }} name="joinType" id="jointype" data-toggle="toggle" data-on="Public Team" data-off="Private" data-onstyle="success" data-offstyle="danger" data-width="100">
|
||||
</span>
|
||||
|
||||
<button onclick="$('#addUserModal').modal('show')" type="button" id="inviteUser" name="inviteUser" class="btn btn-success" {{ ($team->openJoin) ? 'disabled' : '' }}><i class="fas fa-user-plus"></i> Invite user</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<button type="button" class="btn btn-success ml-2" onclick="$('#editTeam').submit()"><i class="fas fa-save"></i> Save and Close</button>
|
||||
<button type="button" class="btn btn-warning ml-2" onclick="$('#userlist_modal').modal('show')"><i class="fas fa-users"></i> Team Members</button>
|
||||
<button type="button" class="btn btn-danger ml-2" onclick="location.href='{{ route('teams.index') }}'"><i class="far fa-trash-alt"></i> Cancel</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col">
|
||||
|
||||
<div class="card">
|
||||
|
||||
<div class="card-header">
|
||||
<div class="card-title"><h4>Team Vacancies</h4></div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<span class="text-muted"><i class="fas fa-info-circle"></i> The vacancies you select determine what applications your team members see. All applications under the vacancies you choose will be displayed.</span>
|
||||
|
||||
<form method="POST" id="vacancyChangeForm" action="{{ route('assignVacancies', ['team' => $team->id]) }}">
|
||||
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
|
||||
<select id="assocVacancies" name="assocVacancies[]" multiple="multiple">
|
||||
|
||||
@foreach($vacancies as $vacancy)
|
||||
|
||||
<!-- fixme: n+1 query here -->
|
||||
<option {{ ($vacancy->hasTeam($team)) ? 'selected' : '' }} value="{{ $vacancy->id }}">{{ $vacancy->vacancyName }}</option>
|
||||
|
||||
@endforeach
|
||||
|
||||
</select>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
|
||||
<button onclick="$('#vacancyChangeForm').submit()" type="button" class="btn btn-success"><i class="far fa-save"></i> Update Assignments</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@stop
|
|
@ -0,0 +1,147 @@
|
|||
@extends('adminlte::page')
|
||||
|
||||
@section('title', config('app.name') . ' | ' . __('messages.teams.m_teams_page'))
|
||||
|
||||
@section('content_header')
|
||||
<h1>{{config('app.name')}} / {{__('messages.teams.m_teams_page')}}</h1>
|
||||
@stop
|
||||
|
||||
@section('js')
|
||||
|
||||
<x-global-errors></x-global-errors>
|
||||
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
|
||||
<x-modal id="newTeamModal" modal-label="newTeamModalLabel" modal-title="New team" include-close-button="true">
|
||||
|
||||
<div class="row">
|
||||
<div class="col offset-3">
|
||||
<img src="/img/new_team.svg" height="220px" width="220px" alt="New Team illustration">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('teams.store') }}" method="POST" id="newTeamForm">
|
||||
|
||||
@csrf
|
||||
|
||||
<div class="text-center">
|
||||
<input type="text" id="teamName" class="form-control" required name="teamName">
|
||||
</div>
|
||||
|
||||
<p class="text-muted text-sm">This is the name team members will see.</p>
|
||||
|
||||
</form>
|
||||
|
||||
<x-slot name="modalFooter">
|
||||
|
||||
<button type="button" class="btn btn-success" onclick="$('#newTeamForm').submit()"><i class="fas fa-check"></i> Create</button>
|
||||
|
||||
</x-slot>
|
||||
|
||||
</x-modal>
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-4 offset-4 text-center">
|
||||
|
||||
<img src="/img/team.svg" height="230px" width="230px" alt="Team illustration">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="col">
|
||||
|
||||
|
||||
<div class="card bg-gray-dark">
|
||||
|
||||
<div class="card-header bg-indigo">
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
<div class="card-title"><h4>{{ __('messages.teams.m_teams_page') }} <span class="badge badge-warning"><i class="fas fa-check-circle"></i> {{ (Auth::user()->currentTeam) ? Auth::user()->currentTeam->name : 'Select a team' }}</span></h4></div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
@if (!$teams->isEmpty())
|
||||
|
||||
<table class="table-borderless table-active table">
|
||||
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Team Owner</th>
|
||||
<th>Team Name</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
@foreach ($teams as $team)
|
||||
|
||||
<tr>
|
||||
<td>{{ $team->id }}</td>
|
||||
<td>{{ $team->owner_id }}</td>
|
||||
<td>{{ $team->name }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-success btn-sm ml-2" onclick="location.href='{{ route('teams.edit', ['team' => $team->id]) }}'"><i class="fas fa-cogs"></i> Settings</button>
|
||||
<button onclick="location.href='{{ route('switchTeam', ['team' => $team]) }}'" rel="buttonTxtTooltip" data-placement="top" data-toggle="tooltip" title="Select your active team (for dasboard context, etc)" type="button" class="btn btn-warning btn-sm ml-2"><i class="fas fa-random"></i> Switch To</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@endforeach
|
||||
|
||||
</tbody>
|
||||
|
||||
|
||||
</table>
|
||||
|
||||
@else
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i> <b> There don't seem to be any teams here</b>
|
||||
<p>Have you tried creating or joining a team? You may also click an invite link if you've been invited.</p>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
|
||||
<button type="button" class="btn btn-success btn-sm ml-3" onclick="$('#newTeamModal').modal('show')"><i class="fas fa-plus-circle"></i> New team</button>
|
||||
<button type="button" class="btn btn-warning btn-sm ml-3"><i class="fas fas fa-long-arrow-alt-right"></i> Team Dashboard</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@stop
|
|
@ -20,8 +20,61 @@
|
|||
|
||||
@section('content')
|
||||
|
||||
<x-modal id="deleteAccountModal" modal-label="deleteAccountModalLabel" modal-title="Close account" include-close-button="true">
|
||||
|
||||
<p>Deleting your account is an irreversible process. The following data will be deleted (including personally identifiable data):</p>
|
||||
<ul>
|
||||
<li>Last IP address</li>
|
||||
<li>Name, Email and MC Username</li>
|
||||
<li>Your previous applications</li>
|
||||
<li>Your profile data and preferences</li>
|
||||
<li>If you were a staff member:</li>
|
||||
<ul>
|
||||
<li>Your comments</li>
|
||||
<li>Any votes</li>
|
||||
<li>Your roles</li>
|
||||
</ul>
|
||||
</ul>
|
||||
<p>What is not deleted:</p>
|
||||
<ul>
|
||||
<li>Server logs of your visits, including IP addresses</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<form id="deleteAccountForm" method="POST" action="{{ route('userDelete') }}">
|
||||
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
|
||||
<div class="form-group">
|
||||
<label for="currentPassword">Re-enter your password</label>
|
||||
<input class="form-control" autocomplete="current-password" type="password" name="currentPassword" id="currentPassword" required>
|
||||
<p class="text-muted text-sm"><i class="fas fa-info-circle"></i> For your security, your password is always required for sensitive operations. <a href="{{ route('password.request') }}">Forgot your password?</a></p>
|
||||
</div>
|
||||
|
||||
@if (Auth::user()->has2FA())
|
||||
<div class="form-group mt-5">
|
||||
|
||||
<label for="otp">Two-factor authentication code</label>
|
||||
<input type="text" id="otp" name="otp" class="form-control">
|
||||
<p class="text-muted text-sm"><i class="fas fa-info-circle"></i> You cannot recover lost 2FA secrets.</p>
|
||||
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</form>
|
||||
|
||||
<x-slot name="modalFooter">
|
||||
|
||||
<button onclick="$('#deleteAccountForm').submit()" type="button" class="btn btn-warning"><i class="fas fa-exclamation-triangle"></i> Continue</button>
|
||||
|
||||
</x-slot>
|
||||
|
||||
</x-modal>
|
||||
|
||||
@if (!Auth::user()->has2FA())
|
||||
|
||||
|
||||
|
||||
<x-modal id="twoFactorAuthModal" modal-label="2faLabel" modal-title="{{__('messages.2fa_txt')}}" include-close-button="true">
|
||||
|
||||
|
@ -170,6 +223,9 @@
|
|||
<li class="nav-item">
|
||||
<a class="nav-link" id="contactSettingsTab" data-toggle="tab" href="#contactSettings" role="tab" aria-controls="ContactSettings" aria-selected="false">{{__('messages.profile.contact_settings')}}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="dangerZoneTab" data-toggle="tab" href="#dangerZone" role="tab" aria-controls="DangerZone" aria-selected="false">Danger Zone</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
@ -250,6 +306,15 @@
|
|||
<button class="btn btn-success" type="button" onclick="document.getElementById('changeEmail').submit()">{{__('messages.profile.change_email')}}</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane fade p-3" id="dangerZone" role="tabpanel" aria-labelledby="dangerZoneTab">
|
||||
<h5 class="card-title">Danger Zone</h5>
|
||||
<p class="card-text text-bold"><i class="fas fa-radiation"></i> Careful! Actions in these tab might result in irreversible loss of data.</p>
|
||||
|
||||
<button onclick="$('#deleteAccountModal').modal('show')" rel="buttonTxtTooltip" data-toggle="tooltip" data-placement="top" title="This action will delete your account permanently." class="btn btn-danger" type="button"><i class="fas fa-user-slash"></i> Close Account</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>Your account has been deleted</title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
INLINED WITH htmlemail.io/inline
|
||||
------------------------------------- */
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important;
|
||||
}
|
||||
table[class=body] .content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important;
|
||||
}
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important;
|
||||
}
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important;
|
||||
}
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
}
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%;
|
||||
}
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
#MessageViewBody a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
font-weight: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
.btn-primary table td:hover {
|
||||
background-color: #34495e !important;
|
||||
}
|
||||
.btn-primary a:hover {
|
||||
background-color: #34495e !important;
|
||||
border-color: #34495e !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="" style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
|
||||
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">Your account has just been deleted!</span>
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #f6f6f6;">
|
||||
<tr>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;"> </td>
|
||||
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; Margin: 0 auto; max-width: 580px; padding: 10px; width: 580px;">
|
||||
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; padding: 10px;">
|
||||
|
||||
<!-- START CENTERED WHITE CONTAINER -->
|
||||
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 3px;">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
||||
<tr>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;">
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Hi {{ $name }},</p>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Someone (hopefully you) has requested that your account at {{ config('app.name') }} be deleted.</p>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">As a security measure, an email is always sent out to make sure you really want to delete your account.</p>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">This is the IP address the request was made from: {{ $originalIP }}.</p>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">If you don't do anything, your account will automatically be permanently deleted in 30 days, and will remain unaccessible for that time period.</p>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Click one of the buttons below to make a decision.</p>
|
||||
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-family: sans-serif; font-size: 14px; vertical-align: top; padding-bottom: 15px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; background-color: #3498db; border-radius: 5px; text-align: center; margin-left: 5px;"> <a href="{{ route('processDeleteConfirmation', ['ID' => $userID, 'action' => 'confirm', 'token' => $deleteToken]) }}" target="_blank" style="display: inline-block; color: #ffffff; background-color: #3498db; border: solid 1px #3498db; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-transform: capitalize; border-color: #3498db;">Delete Account</a> </td>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; background-color: #3498db; border-radius: 5px; text-align: center; margin-left: 5px;"> <a href="{{ route('processDeleteConfirmation', ['ID' => $userID, 'action' => 'cancel', 'token' => $cancelToken]) }}" target="_blank" style="display: inline-block; color: #ffffff; background-color: #3498db; border: solid 1px #3498db; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-transform: capitalize; border-color: #3498db;">Cancel Deletion</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Thank you!</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- END MAIN CONTENT AREA -->
|
||||
</table>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
<div class="footer" style="clear: both; Margin-top: 10px; text-align: center; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
||||
<tr>
|
||||
<td class="content-block" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; font-size: 12px; color: #999999; text-align: center;">
|
||||
<span class="apple-link" style="color: #999999; font-size: 12px; text-align: center;">Staff Manager</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- END FOOTER -->
|
||||
|
||||
<!-- END CENTERED WHITE CONTAINER -->
|
||||
</div>
|
||||
</td>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,167 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title>You have just been invited to a team </title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
INLINED WITH htmlemail.io/inline
|
||||
------------------------------------- */
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important;
|
||||
}
|
||||
table[class=body] .content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important;
|
||||
}
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important;
|
||||
}
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important;
|
||||
}
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
}
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%;
|
||||
}
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
#MessageViewBody a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-size: inherit;
|
||||
font-family: inherit;
|
||||
font-weight: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
.btn-primary table td:hover {
|
||||
background-color: #34495e !important;
|
||||
}
|
||||
.btn-primary a:hover {
|
||||
background-color: #34495e !important;
|
||||
border-color: #34495e !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="" style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
|
||||
<span class="preheader" style="color: transparent; display: none; height: 0; max-height: 0; max-width: 0; opacity: 0; overflow: hidden; mso-hide: all; visibility: hidden; width: 0;">You have just been invited to {{ $teamName }}</span>
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #f6f6f6;">
|
||||
<tr>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;"> </td>
|
||||
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; Margin: 0 auto; max-width: 580px; padding: 10px; width: 580px;">
|
||||
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; padding: 10px;">
|
||||
|
||||
<!-- START CENTERED WHITE CONTAINER -->
|
||||
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 3px;">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
||||
<tr>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;">
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Hi {{ $name }},</p>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">{{ $inviterName }} has just invited you to join {{ $teamName }} (team).</p>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Joining a team confers many benefits, such as access to the team calendar, shared files, assigned applications and much more.</p>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">If you accept this invite, you'll be added to the team immediately. Conversely, if you refuse it, {{ $inviterName }} won't be notified and you'll need to be invited again to join it.</p>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Click one of the buttons below to make a decision.</p>
|
||||
|
||||
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; box-sizing: border-box;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="left" style="font-family: sans-serif; font-size: 14px; vertical-align: top; padding-bottom: 15px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; background-color: #3498db; border-radius: 5px; text-align: center; margin-left: 5px;"> <a href="{{ route('processInvite', ['action' => 'accept', 'token' => $acceptToken]) }}" target="_blank" style="display: inline-block; color: #ffffff; background-color: #3498db; border: solid 1px #3498db; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-transform: capitalize; border-color: #3498db;">Accept Invite</a> </td>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top; background-color: #3498db; border-radius: 5px; text-align: center; margin-left: 5px;"> <a href="{{ route('processInvite', ['action' => 'deny', 'token' => $denyToken]) }}" target="_blank" style="display: inline-block; color: #ffffff; background-color: #3498db; border: solid 1px #3498db; border-radius: 5px; box-sizing: border-box; cursor: pointer; text-decoration: none; font-size: 14px; font-weight: bold; margin: 0; padding: 12px 25px; text-transform: capitalize; border-color: #3498db;">Deny Invite</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">The accept and deny buttons do not expire (unless your invite is cancelled), so you can take your time to make a decision.</p>
|
||||
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">Thank you!</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- END MAIN CONTENT AREA -->
|
||||
</table>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
<div class="footer" style="clear: both; Margin-top: 10px; text-align: center; width: 100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
|
||||
<tr>
|
||||
<td class="content-block" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; font-size: 12px; color: #999999; text-align: center;">
|
||||
<span class="apple-link" style="color: #999999; font-size: 12px; text-align: center;">Staff Manager</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- END FOOTER -->
|
||||
|
||||
<!-- END CENTERED WHITE CONTAINER -->
|
||||
</div>
|
||||
</td>
|
||||
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
151
routes/web.php
151
routes/web.php
|
@ -1,6 +1,23 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\ApplicationController;
|
||||
use App\Http\Controllers\AppointmentController;
|
||||
use App\Http\Controllers\Auth\TwofaController;
|
||||
use App\Http\Controllers\BanController;
|
||||
use App\Http\Controllers\CommentController;
|
||||
use App\Http\Controllers\ContactController;
|
||||
use App\Http\Controllers\DashboardController;
|
||||
use App\Http\Controllers\DevToolsController;
|
||||
use App\Http\Controllers\FormController;
|
||||
use App\Http\Controllers\HomeController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\TeamController;
|
||||
use App\Http\Controllers\UserController;
|
||||
use App\Http\Controllers\VacancyController;
|
||||
use App\Http\Controllers\VoteController;
|
||||
use App\Http\Controllers\OptionsController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
||||
|
||||
/*
|
||||
|
@ -19,72 +36,95 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => [ 'l
|
|||
|
||||
Auth::routes(['verify' => true]);
|
||||
|
||||
Route::post('/twofa/authenticate', 'Auth\TwofaController@verify2FA')
|
||||
Route::post('/twofa/authenticate', [TwofaController::class, 'verify2FA'])
|
||||
->name('verify2FA');
|
||||
|
||||
});
|
||||
|
||||
Route::get('/','HomeController@index')
|
||||
Route::get('/', [HomeController::class, 'index'])
|
||||
->middleware('eligibility');
|
||||
|
||||
Route::post('/form/contact', 'ContactController@create')
|
||||
Route::post('/form/contact', [ContactController::class, 'create'])
|
||||
->name('sendSubmission');
|
||||
|
||||
Route::get('/accounts/danger-zone/{ID}/{action}/{token}', [UserController::class, 'processDeleteConfirmation'])
|
||||
->name('processDeleteConfirmation');
|
||||
|
||||
|
||||
Route::group(['middleware' => ['auth', 'forcelogout', '2fa', 'verified']], function(){
|
||||
|
||||
Route::get('/dashboard', 'DashboardController@index')
|
||||
Route::get('/dashboard', [DashboardController::class, 'index'])
|
||||
->name('dashboard')
|
||||
->middleware('eligibility');
|
||||
|
||||
Route::get('users/directory', 'ProfileController@index')
|
||||
Route::get('users/directory', [ProfileController::class, 'index'])
|
||||
->name('directory');
|
||||
|
||||
|
||||
|
||||
Route::resource('teams', TeamController::class);
|
||||
|
||||
|
||||
Route::post('teams/{team}/invites/send', [TeamController::class, 'invite'])
|
||||
->name('sendInvite');
|
||||
|
||||
Route::get('teams/{team}/switch', [TeamController::class, 'switchTeam'])
|
||||
->name('switchTeam');
|
||||
|
||||
Route::patch('teams/{team}/vacancies/update', [TeamController::class, 'assignVacancies'])
|
||||
->name('assignVacancies');
|
||||
|
||||
|
||||
Route::get('teams/invites/{action}/{token}', [TeamController::class, 'processInviteAction'])
|
||||
->name('processInvite');
|
||||
|
||||
|
||||
|
||||
Route::group(['prefix' => '/applications'], function (){
|
||||
|
||||
Route::get('/my-applications', 'ApplicationController@showUserApps')
|
||||
Route::get('/my-applications', [ApplicationController::class, 'showUserApps'])
|
||||
->name('showUserApps')
|
||||
->middleware('eligibility');
|
||||
|
||||
Route::get('/view/{application}', 'ApplicationController@showUserApp')
|
||||
Route::get('/view/{application}', [ApplicationController::class, 'showUserApp'])
|
||||
->name('showUserApp');
|
||||
|
||||
Route::post('/{application}/comments', 'CommentController@insert')
|
||||
Route::post('/{application}/comments', [CommentController::class, 'insert'])
|
||||
->name('addApplicationComment');
|
||||
|
||||
Route::delete('/comments/{comment}/delete', 'CommentController@delete')
|
||||
Route::delete('/comments/{comment}/delete', [CommentController::class, 'delete'])
|
||||
->name('deleteApplicationComment');
|
||||
|
||||
|
||||
Route::patch('/notes/save/{application}', 'AppointmentController@saveNotes')
|
||||
Route::patch('/notes/save/{application}', [AppointmentController::class, 'saveNotes'])
|
||||
->name('saveNotes');
|
||||
|
||||
|
||||
Route::patch('/update/{application}/{newStatus}', 'ApplicationController@updateApplicationStatus')
|
||||
Route::patch('/update/{application}/{newStatus}', [ApplicationController::class, 'updateApplicationStatus'])
|
||||
->name('updateApplicationStatus');
|
||||
|
||||
Route::delete('{application}/delete', 'ApplicationController@delete')
|
||||
Route::delete('{application}/delete', [ApplicationController::class, 'delete'])
|
||||
->name('deleteApplication');
|
||||
|
||||
|
||||
Route::get('/staff/all', 'ApplicationController@showAllApps')
|
||||
Route::get('/staff/all', [ApplicationController::class, 'showAllApps'])
|
||||
->name('allApplications');
|
||||
|
||||
|
||||
Route::get('/staff/outstanding', 'ApplicationController@showAllPendingApps')
|
||||
Route::get('/staff/outstanding', [ApplicationController::class, 'showAllPendingApps'])
|
||||
->name('staffPendingApps');
|
||||
|
||||
|
||||
Route::get('/staff/peer-review', 'ApplicationController@showPeerReview')
|
||||
Route::get('/staff/peer-review', [ApplicationController::class, 'showPeerReview'])
|
||||
->name('peerReview');
|
||||
|
||||
|
||||
Route::get('/staff/pending-interview', 'ApplicationController@showPendingInterview')
|
||||
Route::get('/staff/pending-interview', [ApplicationController::class, 'showPendingInterview'])
|
||||
->name('pendingInterview');
|
||||
|
||||
|
||||
|
||||
Route::post('{application}/staff/vote', 'VoteController@vote')
|
||||
Route::post('{application}/staff/vote', [VoteController::class, 'vote'])
|
||||
->name('voteApplication');
|
||||
|
||||
|
||||
|
@ -92,143 +132,146 @@ Route::group(['prefix' => LaravelLocalization::setLocale(), 'middleware' => [ 'l
|
|||
|
||||
Route::group(['prefix' => 'appointments'], function (){
|
||||
|
||||
Route::post('schedule/appointments/{application}', 'AppointmentController@saveAppointment')
|
||||
Route::post('schedule/appointments/{application}', [AppointmentController::class, 'saveAppointment'])
|
||||
->name('scheduleAppointment');
|
||||
|
||||
Route::patch('update/appointments/{application}/{status}', 'AppointmentController@updateAppointment')
|
||||
Route::patch('update/appointments/{application}/{status}', [AppointmentController::class, 'updateAppointment'])
|
||||
->name('updateAppointment');
|
||||
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'apply', 'middleware' => ['eligibility']], function (){
|
||||
|
||||
Route::get('positions/{vacancySlug}', 'ApplicationController@renderApplicationForm')
|
||||
Route::get('positions/{vacancySlug}', [ApplicationController::class, 'renderApplicationForm'])
|
||||
->name('renderApplicationForm');
|
||||
|
||||
Route::post('positions/{vacancySlug}/submit', 'ApplicationController@saveApplicationAnswers')
|
||||
Route::post('positions/{vacancySlug}/submit', [ApplicationController::class, 'saveApplicationAnswers'])
|
||||
->name('saveApplicationForm');
|
||||
|
||||
});
|
||||
|
||||
Route::group(['prefix' => '/profile'], function (){
|
||||
|
||||
Route::get('/settings', 'ProfileController@showProfile')
|
||||
Route::get('/settings', [ProfileController::class, 'showProfile'])
|
||||
->name('showProfileSettings');
|
||||
|
||||
Route::patch('/settings/save', 'ProfileController@saveProfile')
|
||||
Route::patch('/settings/save', [ProfileController::class, 'saveProfile'])
|
||||
->name('saveProfileSettings');
|
||||
|
||||
Route::get('user/{user}', 'ProfileController@showSingleProfile')
|
||||
Route::get('user/{user}', [ProfileController::class, 'showSingleProfile'])
|
||||
->name('showSingleProfile');
|
||||
|
||||
|
||||
Route::get('/settings/account', 'UserController@showAccount')
|
||||
Route::get('/settings/account', [UserController::class, 'showAccount'])
|
||||
->name('showAccountSettings');
|
||||
|
||||
|
||||
Route::patch('/settings/account/change-password', 'UserController@changePassword')
|
||||
Route::patch('/settings/account/change-password', [UserController::class, 'changePassword'])
|
||||
->name('changePassword');
|
||||
|
||||
Route::patch('/settings/account/change-email', 'UserController@changeEmail')
|
||||
Route::patch('/settings/account/change-email', [UserController::class, 'changeEmail'])
|
||||
->name('changeEmail');
|
||||
|
||||
Route::post('/settings/account/flush-sessions', 'UserController@flushSessions')
|
||||
Route::post('/settings/account/flush-sessions', [UserController::class, 'flushSessions'])
|
||||
->name('flushSessions');
|
||||
|
||||
Route::patch('/settings/account/twofa/enable', 'UserController@add2FASecret')
|
||||
Route::patch('/settings/account/twofa/enable', [UserController::class, 'add2FASecret'])
|
||||
->name('enable2FA');
|
||||
|
||||
Route::patch('/settings/account/twofa/disable', 'UserController@remove2FASecret')
|
||||
Route::patch('/settings/account/twofa/disable', [UserController::class, 'remove2FASecret'])
|
||||
->name('disable2FA');
|
||||
|
||||
Route::patch('/settings/account/dg/delete', [UserController::class, 'userDelete'])
|
||||
->name('userDelete');
|
||||
|
||||
});
|
||||
|
||||
|
||||
Route::group(['prefix' => '/hr'], function (){
|
||||
|
||||
Route::get('staff-members', 'UserController@showStaffMembers')
|
||||
Route::get('staff-members', [UserController::class, 'showStaffMembers'])
|
||||
->name('staffMemberList');
|
||||
|
||||
Route::get('players', 'UserController@showPlayers')
|
||||
Route::get('players', [UserController::class, 'showPlayers'])
|
||||
->name('registeredPlayerList');
|
||||
|
||||
Route::post('players/search', 'UserController@showPlayersLike')
|
||||
Route::post('players/search', [UserController::class, 'showPlayersLike'])
|
||||
->name('searchRegisteredPLayerList');
|
||||
|
||||
Route::patch('staff-members/terminate/{user}', 'UserController@terminate')
|
||||
Route::patch('staff-members/terminate/{user}', [UserController::class, 'terminate'])
|
||||
->name('terminateStaffMember');
|
||||
|
||||
});
|
||||
|
||||
Route::group(['prefix' => 'admin'], function (){
|
||||
|
||||
Route::get('settings', 'OptionsController@index')
|
||||
Route::get('settings', [OptionsController::class, 'index'])
|
||||
->name('showSettings');
|
||||
|
||||
Route::post('settings/save', 'OptionsController@saveSettings')
|
||||
Route::post('settings/save', [OptionsController::class, 'saveSettings'])
|
||||
->name('saveSettings');
|
||||
|
||||
Route::post('players/ban/{user}', 'BanController@insert')
|
||||
Route::post('players/ban/{user}', [BanController::class, 'insert'])
|
||||
->name('banUser');
|
||||
|
||||
Route::delete('players/unban/{user}', 'BanController@delete')
|
||||
Route::delete('players/unban/{user}', [BanController::class, 'delete'])
|
||||
->name('unbanUser');
|
||||
|
||||
|
||||
|
||||
Route::delete('players/delete/{user}', 'UserController@delete')
|
||||
Route::delete('players/delete/{user}', [UserController::class, 'delete'])
|
||||
->name('deleteUser');
|
||||
|
||||
Route::patch('players/update/{user}', 'UserController@update')
|
||||
Route::patch('players/update/{user}', [UserController::class, 'update'])
|
||||
->name('updateUser');
|
||||
|
||||
|
||||
|
||||
Route::get('positions', 'VacancyController@index')
|
||||
Route::get('positions', [VacancyController::class, 'index'])
|
||||
->name('showPositions');
|
||||
|
||||
Route::post('positions/save', 'VacancyController@store')
|
||||
Route::post('positions/save', [VacancyController::class, 'store'])
|
||||
->name('savePosition');
|
||||
|
||||
|
||||
Route::get('positions/edit/{vacancy}', 'VacancyController@edit')
|
||||
Route::get('positions/edit/{vacancy}', [VacancyController::class, 'edit'])
|
||||
->name('editPosition');
|
||||
|
||||
Route::patch('positions/update/{vacancy}', 'VacancyController@update')
|
||||
Route::patch('positions/update/{vacancy}', [VacancyController::class, 'update'])
|
||||
->name('updatePosition');
|
||||
|
||||
|
||||
Route::patch('positions/availability/{status}/{vacancy}', 'VacancyController@updatePositionAvailability')
|
||||
Route::patch('positions/availability/{status}/{vacancy}', [VacancyController::class, 'updatePositionAvailability'])
|
||||
->name('updatePositionAvailability');
|
||||
|
||||
|
||||
Route::get('forms/builder', 'FormController@showFormBuilder')
|
||||
Route::get('forms/builder', [FormController::class, 'showFormBuilder'])
|
||||
->name('showFormBuilder');
|
||||
|
||||
Route::post('forms/save', 'FormController@saveForm')
|
||||
Route::post('forms/save', [FormController::class, 'saveForm'])
|
||||
->name('saveForm');
|
||||
|
||||
Route::delete('forms/destroy/{form}', 'FormController@destroy')
|
||||
Route::delete('forms/destroy/{form}', [FormController::class, 'destroy'])
|
||||
->name('destroyForm');
|
||||
|
||||
Route::get('forms', 'FormController@index')
|
||||
Route::get('forms', [FormController::class, 'index'])
|
||||
->name('showForms');
|
||||
|
||||
Route::get('forms/preview/{form}', 'FormController@preview')
|
||||
Route::get('forms/preview/{form}', [FormController::class, 'preview'])
|
||||
->name('previewForm');
|
||||
|
||||
Route::get('forms/edit/{form}', 'FormController@edit')
|
||||
Route::get('forms/edit/{form}', [FormController::class, 'edit'])
|
||||
->name('editForm');
|
||||
|
||||
Route::patch('forms/update/{form}', 'FormController@update')
|
||||
Route::patch('forms/update/{form}', [FormController::class, 'update'])
|
||||
->name('updateForm');
|
||||
|
||||
|
||||
Route::get('devtools', 'DevToolsController@index')
|
||||
Route::get('devtools', [DevToolsController::class, 'index'])
|
||||
->name('devTools');
|
||||
|
||||
// we could use route model binding
|
||||
Route::post('devtools/vote-evaluation/force', 'DevToolsController@forceVoteCount')
|
||||
Route::post('devtools/vote-evaluation/force', [DevToolsController::class, 'forceVoteCount'])
|
||||
->name('devToolsForceVoteCount');
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue