WIP: Road to 1.0.0 #1
|
@ -1,154 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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/berkayk/onesignal-laravel" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/brick/math" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/clue/stream-filter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/dnoegel/php-xdg-base-dir" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/dbal" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/event-manager" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/inflector" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/instantiator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/lexer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/dragonmantank/cron-expression" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/egulias/email-validator" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/facade/flare-client-php" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/facade/ignition" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/facade/ignition-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/fideloper/proxy" />
|
||||
<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" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/hamcrest/hamcrest-php" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/http-interop/http-factory-guzzle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/jean85/pretty-package-versions" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/jeroennoten/laravel-adminlte" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/framework" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/tinker" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/ui" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/league/commonmark" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/league/flysystem" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/league/mime-type-detection" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/maximebf/debugbar" />
|
||||
<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" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/nunomaduro/collision" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/opis/closure" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/paragonie/random_compat" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/manifest" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/version" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/client-common" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/discovery" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/guzzle6-adapter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/httplug" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/message" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/message-factory" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/php-http/promise" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-common" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-docblock" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/type-resolver" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpoption/phpoption" />
|
||||
<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" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/event-dispatcher" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-client" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-factory" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/simple-cache" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/psy/psysh" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
|
||||
<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" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/resource-operations" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/type" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/version" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sentry/sentry" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/sentry/sentry-laravel" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/spatie/laravel-permission" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/swiftmailer/swiftmailer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/console" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/css-selector" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/debug" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/deprecation-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/error-handler" />
|
||||
<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" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/options-resolver" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-iconv" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-grapheme" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-idn" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-normalizer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php72" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php73" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php80" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-uuid" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/process" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/psr-http-message-bridge" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/routing" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/service-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/string" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/translation-contracts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/var-dumper" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/theseer/tokenizer" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/tijsverkoyen/css-to-inline-styles" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/vlucas/phpdotenv" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/voku/portable-ascii" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/webmozart/assert" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/vendor/laravel/sanctum" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -2,7 +2,7 @@
|
|||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/hrm-mcserver.iml" filepath="$PROJECT_DIR$/.idea/hrm-mcserver.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/../rbrecruiter/.idea/rbrecruiter.iml" filepath="$PROJECT_DIR$/../rbrecruiter/.idea/rbrecruiter.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class ApplicationNotFoundException extends ModelNotFoundException
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class EmptyFormException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class EmptyOptionsException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class FailedCaptchaException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class FileUploadException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class FormHasConstraintsException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class IncompleteApplicationException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidAppointmentException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidAppointmentStatusException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidGamePreferenceException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidInviteException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class OptionCategoryNotFoundException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class OptionNotFoundException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ProfileNotFoundException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class PublicTeamInviteException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class UnavailableApplicationException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class UserAlreadyInvitedException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class VacancyNotFoundException extends ModelNotFoundException
|
||||
{
|
||||
//
|
||||
}
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
namespace App\Helpers;
|
||||
|
||||
use App\Exceptions\EmptyOptionsException;
|
||||
use App\Exceptions\OptionNotFoundException;
|
||||
use App\Options as Option;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
@ -43,7 +45,7 @@ class Options
|
|||
$options = Option::where('option_category', $category)->get();
|
||||
if ($options->isEmpty())
|
||||
{
|
||||
throw new \Exception('There are no options in category ' . $category);
|
||||
throw new EmptyOptionsException('There are no options in category ' . $category);
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
@ -58,7 +60,7 @@ class Options
|
|||
Log::debug('Option '.$option.'not found in cache, refreshing from database');
|
||||
$value = Option::where('option_name', $option)->first();
|
||||
if (is_null($value)) {
|
||||
throw new \Exception('This option does not exist.');
|
||||
throw new OptionNotFoundException('This option does not exist.');
|
||||
}
|
||||
Cache::put($option, $value->option_value);
|
||||
Cache::put($option.'_desc', 'Undefined description');
|
||||
|
@ -118,7 +120,7 @@ class Options
|
|||
|
||||
Cache::put('option_name', $newValue, now()->addDay());
|
||||
} else {
|
||||
throw new \Exception('This option does not exist.');
|
||||
throw new OptionNotFoundException('This option does not exist.');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,34 +22,23 @@
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Application;
|
||||
use App\Events\ApplicationDeniedEvent;
|
||||
use App\Facades\JSON;
|
||||
use App\Notifications\ApplicationMoved;
|
||||
use App\Notifications\NewApplicant;
|
||||
use App\Response;
|
||||
use App\User;
|
||||
use App\Vacancy;
|
||||
use ContextAwareValidator;
|
||||
use App\Exceptions\IncompleteApplicationException;
|
||||
use App\Exceptions\UnavailableApplicationException;
|
||||
use App\Exceptions\VacancyNotFoundException;
|
||||
use App\Services\ApplicationService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ApplicationController extends Controller
|
||||
{
|
||||
|
||||
private $applicationService;
|
||||
|
||||
private function canVote($votes): bool
|
||||
{
|
||||
$allvotes = collect([]);
|
||||
public function __construct(ApplicationService $applicationService) {
|
||||
|
||||
foreach ($votes as $vote) {
|
||||
if ($vote->userID == Auth::user()->id) {
|
||||
$allvotes->push($vote);
|
||||
}
|
||||
$this->applicationService = $applicationService;
|
||||
}
|
||||
|
||||
return !(($allvotes->count() == 1));
|
||||
}
|
||||
|
||||
public function showUserApps()
|
||||
{
|
||||
|
@ -70,7 +59,7 @@ class ApplicationController extends Controller
|
|||
'structuredResponses' => json_decode($application->response->responseData, true),
|
||||
'formStructure' => $application->response->form,
|
||||
'vacancy' => $application->response->vacancy,
|
||||
'canVote' => $this->canVote($application->votes),
|
||||
'canVote' => $this->applicationService->canVote($application->votes),
|
||||
]
|
||||
);
|
||||
} else {
|
||||
|
@ -90,91 +79,27 @@ class ApplicationController extends Controller
|
|||
}
|
||||
|
||||
|
||||
public function renderApplicationForm(Request $request, $vacancySlug)
|
||||
public function renderApplicationForm($vacancySlug)
|
||||
{
|
||||
$vacancyWithForm = Vacancy::with('forms')->where('vacancySlug', $vacancySlug)->get();
|
||||
|
||||
$firstVacancy = $vacancyWithForm->first();
|
||||
|
||||
if (!$vacancyWithForm->isEmpty() && $firstVacancy->vacancyCount !== 0 && $firstVacancy->vacancyStatus == 'OPEN') {
|
||||
return view('dashboard.application-rendering.apply')
|
||||
->with([
|
||||
'vacancy' => $vacancyWithForm->first(),
|
||||
'preprocessedForm' => json_decode($vacancyWithForm->first()->forms->formStructure, true),
|
||||
]);
|
||||
} else {
|
||||
abort(404, __('The application you\'re looking for could not be found or it is currently unavailable.'));
|
||||
}
|
||||
return $this->applicationService->renderForm($vacancySlug);
|
||||
}
|
||||
|
||||
public function saveApplicationAnswers(Request $request, $vacancySlug)
|
||||
{
|
||||
$vacancy = Vacancy::with('forms')->where('vacancySlug', $vacancySlug)->get();
|
||||
try {
|
||||
|
||||
if ($vacancy->isEmpty()) {
|
||||
$this->applicationService->fillForm(Auth::user(), $request->all(), $vacancySlug);
|
||||
|
||||
} catch (VacancyNotFoundException | IncompleteApplicationException | UnavailableApplicationException $e) {
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', __('This vacancy doesn\'t exist; Please use the proper buttons to apply to one.'));
|
||||
->with('error', $e->getMessage());
|
||||
}
|
||||
|
||||
if ($vacancy->first()->vacancyCount == 0 || $vacancy->first()->vacancyStatus !== 'OPEN') {
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', __('This application is unavailable'));
|
||||
}
|
||||
|
||||
Log::info('Processing new application!');
|
||||
|
||||
$formStructure = json_decode($vacancy->first()->forms->formStructure, true);
|
||||
$responseValidation = ContextAwareValidator::getResponseValidator($request->all(), $formStructure);
|
||||
$applicant = Auth::user();
|
||||
|
||||
// API users may specify ID 1 for an anonymous application, but they'll have to submit contact details for it to become active.
|
||||
// User ID 1 is exempt from application rate limits
|
||||
|
||||
Log::info('Built response & validator structure!');
|
||||
|
||||
if (!$responseValidation->get('validator')->fails()) {
|
||||
$response = Response::create([
|
||||
'responseFormID' => $vacancy->first()->forms->id,
|
||||
'associatedVacancyID' => $vacancy->first()->id, // Since a form can be used by multiple vacancies, we can only know which specific vacancy this response ties to by using a vacancy ID
|
||||
'responseData' => $responseValidation->get('responseStructure'),
|
||||
]);
|
||||
|
||||
Log::info('Registered form response!', [
|
||||
'applicant' => $applicant->name,
|
||||
'vacancy' => $vacancy->first()->vacancyName
|
||||
]);
|
||||
|
||||
$application = Application::create([
|
||||
'applicantUserID' => $applicant->id,
|
||||
'applicantFormResponseID' => $response->id,
|
||||
'applicationStatus' => 'STAGE_SUBMITTED',
|
||||
]);
|
||||
|
||||
Log::info('Submitted an application!', [
|
||||
'responseID' => $response->id,
|
||||
'applicant' => $applicant->name
|
||||
]);
|
||||
|
||||
foreach (User::all() as $user) {
|
||||
if ($user->hasRole('admin')) {
|
||||
$user->notify((new NewApplicant($application, $vacancy->first()))->delay(now()->addSeconds(10)));
|
||||
}
|
||||
}
|
||||
|
||||
$request->session()->flash('success', __('Thank you for your application! It will be reviewed as soon as possible.'));
|
||||
return redirect(route('showUserApps'));
|
||||
|
||||
}
|
||||
|
||||
Log::warning('Application form for ' . $applicant->name . ' contained errors, resetting!');
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', __('There are one or more errors in your application. Please make sure none of your fields are empty, since they are all required.'));
|
||||
->with('success', __('Thank you! Your application has been processed and our team will get to it shortly.'));
|
||||
}
|
||||
|
||||
public function updateApplicationStatus(Request $request, Application $application, $newStatus)
|
||||
|
@ -182,36 +107,28 @@ class ApplicationController extends Controller
|
|||
$messageIsError = false;
|
||||
$this->authorize('update', Application::class);
|
||||
|
||||
|
||||
switch ($newStatus) {
|
||||
case 'deny':
|
||||
|
||||
event(new ApplicationDeniedEvent($application));
|
||||
$message = __("Application denied successfully.");
|
||||
|
||||
break;
|
||||
|
||||
case 'interview':
|
||||
Log::info(' Moved application ID ' . $application->id . 'to interview stage!');
|
||||
$message = __('Application moved to interview stage!');
|
||||
|
||||
$application->setStatus('STAGE_INTERVIEW');
|
||||
$application->user->notify(new ApplicationMoved());
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
$message = __("There are no suitable statuses to update to.");
|
||||
$messageIsError = true;
|
||||
try {
|
||||
$status = $this->applicationService->updateStatus($application, $newStatus);
|
||||
} catch (\LogicException $ex)
|
||||
{
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete(Request $request, Application $application)
|
||||
{
|
||||
$this->authorize('delete', $application);
|
||||
$application->delete(); // observers will run, cleaning it up
|
||||
$this->applicationService->delete($application);
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
|
|
|
@ -23,85 +23,79 @@ namespace App\Http\Controllers;
|
|||
|
||||
use App\Application;
|
||||
use App\Appointment;
|
||||
use App\Exceptions\InvalidAppointmentException;
|
||||
use App\Exceptions\InvalidAppointmentStatusException;
|
||||
use App\Http\Requests\SaveNotesRequest;
|
||||
use App\Notifications\ApplicationMoved;
|
||||
use App\Notifications\AppointmentScheduled;
|
||||
use App\Services\AppointmentService;
|
||||
use App\Services\MeetingNoteService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AppointmentController extends Controller
|
||||
{
|
||||
private $allowedPlatforms = [
|
||||
|
||||
'ZOOM',
|
||||
'DISCORD',
|
||||
'SKYPE',
|
||||
'MEET',
|
||||
'TEAMSPEAK',
|
||||
private $appointmentService;
|
||||
private $meetingNoteService;
|
||||
|
||||
];
|
||||
|
||||
public function saveAppointment(Request $request, Application $application)
|
||||
{
|
||||
$this->authorize('create', Appointment::class);
|
||||
$appointmentDate = Carbon::parse($request->appointmentDateTime);
|
||||
public function __construct(AppointmentService $appointmentService, MeetingNoteService $meetingNoteService) {
|
||||
|
||||
$appointment = Appointment::create([
|
||||
'appointmentDescription' => $request->appointmentDescription,
|
||||
'appointmentDate' => $appointmentDate->toDateTimeString(),
|
||||
'applicationID' => $application->id,
|
||||
'appointmentLocation' => (in_array($request->appointmentLocation, $this->allowedPlatforms)) ? $request->appointmentLocation : 'DISCORD',
|
||||
]);
|
||||
$application->setStatus('STAGE_INTERVIEW_SCHEDULED');
|
||||
|
||||
Log::info('User '.Auth::user()->name.' has scheduled an appointment with '.$application->user->name.' for application ID'.$application->id, [
|
||||
'datetime' => $appointmentDate->toDateTimeString(),
|
||||
'scheduled' => now(),
|
||||
]);
|
||||
|
||||
$application->user->notify(new AppointmentScheduled($appointment));
|
||||
$request->session()->flash('success', __('Appointment successfully scheduled @ :appointmentTime', ['appointmentTime', $appointmentDate->toDateTimeString()]));
|
||||
|
||||
return redirect()->back();
|
||||
$this->appointmentService = $appointmentService;
|
||||
$this->meetingNoteService = $meetingNoteService;
|
||||
}
|
||||
|
||||
public function updateAppointment(Request $request, Application $application, $status)
|
||||
public function saveAppointment(Request $request, Application $application): RedirectResponse
|
||||
{
|
||||
$this->authorize('create', Appointment::class);
|
||||
|
||||
$appointmentDate = Carbon::parse($request->appointmentDateTime);
|
||||
$this->appointmentService->createAppointment($application, $appointmentDate, $request->appointmentDescription, $request->appointmentLocation);
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success',__('Appointment successfully scheduled @ :appointmentTime', ['appointmentTime', $appointmentDate->toDateTimeString()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function updateAppointment(Application $application, $status): RedirectResponse
|
||||
{
|
||||
$this->authorize('update', $application->appointment);
|
||||
|
||||
$validStatuses = [
|
||||
'SCHEDULED',
|
||||
'CONCLUDED',
|
||||
];
|
||||
try {
|
||||
$this->appointmentService->updateAppointment($application, $status);
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __("Interview finished! Staff members can now vote on it."));
|
||||
|
||||
}
|
||||
catch (InvalidAppointmentStatusException $ex) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
}
|
||||
|
||||
// NOTE: This is a little confusing, refactor
|
||||
$application->appointment->appointmentStatus = (in_array($status, $validStatuses)) ? strtoupper($status) : 'SCHEDULED';
|
||||
$application->appointment->save();
|
||||
|
||||
$application->setStatus('STAGE_PEERAPPROVAL');
|
||||
$application->user->notify(new ApplicationMoved());
|
||||
|
||||
$request->session()->flash('success', __('Interview finished! Staff members can now vote on it.'));
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
// also updates
|
||||
public function saveNotes(SaveNotesRequest $request, Application $application)
|
||||
{
|
||||
if (! is_null($application)) {
|
||||
$application->load('appointment');
|
||||
try {
|
||||
|
||||
$application->appointment->meetingNotes = $request->noteText;
|
||||
$application->appointment->save();
|
||||
$this->meetingNoteService->addToApplication($application, $request->noteText);
|
||||
|
||||
$request->session()->flash('success', __('Meeting notes have been saved.'));
|
||||
} else {
|
||||
$request->session()->flash('error', __('There\'s no appointment to save notes to!'));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', 'Saved notes.');
|
||||
|
||||
return redirect()->back();
|
||||
} catch (InvalidAppointmentException $ex) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,21 +24,22 @@ namespace App\Http\Controllers;
|
|||
use App\Application;
|
||||
use App\Comment;
|
||||
use App\Http\Requests\NewCommentRequest;
|
||||
use App\Services\CommentService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class CommentController extends Controller
|
||||
{
|
||||
private $commentService;
|
||||
|
||||
public function __construct(CommentService $commentService) {
|
||||
$this->commentService = $commentService;
|
||||
}
|
||||
|
||||
public function insert(NewCommentRequest $request, Application $application)
|
||||
{
|
||||
$this->authorize('create', Comment::class);
|
||||
|
||||
$comment = Comment::create([
|
||||
'authorID' => Auth::user()->id,
|
||||
'applicationID' => $application->id,
|
||||
'text' => $request->comment,
|
||||
]);
|
||||
$comment = $this->commentService->addComment($application, $request->comment);
|
||||
|
||||
if ($comment) {
|
||||
$request->session()->flash('success', __('Comment posted!'));
|
||||
|
@ -52,10 +53,10 @@ class CommentController extends Controller
|
|||
public function delete(Request $request, Comment $comment)
|
||||
{
|
||||
$this->authorize('delete', $comment);
|
||||
$this->commentService->deleteComment($comment);
|
||||
|
||||
$comment->delete();
|
||||
$request->session()->flash('success', __('Comment deleted!'));
|
||||
|
||||
return redirect()->back();
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Comment deleted!'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\FailedCaptchaException;
|
||||
use App\Notifications\NewContact;
|
||||
use App\Services\ContactService;
|
||||
use App\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
@ -30,47 +32,32 @@ class ContactController extends Controller
|
|||
{
|
||||
protected $users;
|
||||
|
||||
public function __construct(User $users)
|
||||
private $contactService;
|
||||
|
||||
public function __construct(User $users, ContactService $contactService)
|
||||
{
|
||||
$this->contactService = $contactService;
|
||||
$this->users = $users;
|
||||
}
|
||||
|
||||
public function create(Request $request)
|
||||
{
|
||||
$name = $request->name;
|
||||
$email = $request->email;
|
||||
$subject = $request->subject;
|
||||
$msg = $request->msg;
|
||||
try {
|
||||
|
||||
$email = $request->email;
|
||||
$msg = $request->msg;
|
||||
$challenge = $request->input('captcha');
|
||||
|
||||
// TODO: now: add middleware for this verification, move to invisible captcha
|
||||
$verifyrequest = Http::asForm()->post(config('recaptcha.verify.apiurl'), [
|
||||
'secret' => config('recaptcha.keys.secret'),
|
||||
'response' => $challenge,
|
||||
'remoteip' => $request->ip(),
|
||||
]);
|
||||
$this->contactService->sendMessage($request->ip(), $msg, $email, $challenge);
|
||||
|
||||
$response = json_decode($verifyrequest->getBody(), true);
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success',__('Message sent successfully! We usually respond within 48 hours.'));
|
||||
|
||||
if (! $response['success']) {
|
||||
$request->session()->flash('error', __('Beep beep boop... Robot? Submission failed.'));
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
foreach (User::all() as $user) {
|
||||
if ($user->hasRole('admin')) {
|
||||
$user->notify(new NewContact(collect([
|
||||
'message' => $msg,
|
||||
'ip' => $request->ip(),
|
||||
'email' => $email,
|
||||
])));
|
||||
} catch (FailedCaptchaException $ex) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$request->session()->flash('success', __('Message sent successfully! We usually respond within 48 hours.'));
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ use App\Vacancy;
|
|||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
// Note: The dashboard doesn't need a service because it doesn't contain any significant business logic
|
||||
|
||||
public function index()
|
||||
{
|
||||
$totalPeerReview = Application::where('applicationStatus', 'STAGE_PEERAPPROVAL')->get()->count();
|
||||
|
|
|
@ -21,12 +21,20 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\FormHasConstraintsException;
|
||||
use App\Form;
|
||||
use App\Services\FormManagementService;
|
||||
use ContextAwareValidator;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class FormController extends Controller
|
||||
{
|
||||
private $formService;
|
||||
|
||||
public function __construct(FormManagementService $formService) {
|
||||
$this->formService = $formService;
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$forms = Form::all();
|
||||
|
@ -45,60 +53,38 @@ class FormController extends Controller
|
|||
|
||||
public function saveForm(Request $request)
|
||||
{
|
||||
$this->authorize('create', Form::class);
|
||||
$fields = $request->all();
|
||||
$form = $this->formService->addForm($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'));
|
||||
// Form is boolean or array
|
||||
if ($form)
|
||||
{
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Form created!'));
|
||||
}
|
||||
|
||||
$contextValidation = ContextAwareValidator::getValidator($fields, true, true);
|
||||
|
||||
if (! $contextValidation->get('validator')->fails()) {
|
||||
$storableFormStructure = $contextValidation->get('structure');
|
||||
|
||||
Form::create(
|
||||
[
|
||||
'formName' => $fields['formName'],
|
||||
'formStructure' => $storableFormStructure,
|
||||
'formStatus' => 'ACTIVE',
|
||||
]
|
||||
);
|
||||
|
||||
$request->session()->flash('success', __('Form created! You can now link this form to a vacancy.'));
|
||||
|
||||
return redirect()->to(route('showForms'));
|
||||
}
|
||||
|
||||
$request->session()->flash('errors', $contextValidation->get('validator')->errors()->getMessages());
|
||||
|
||||
return redirect()->back();
|
||||
return redirect()
|
||||
->back()
|
||||
->with('errors', $form);
|
||||
}
|
||||
|
||||
public function destroy(Request $request, Form $form)
|
||||
{
|
||||
$this->authorize('delete', $form);
|
||||
try {
|
||||
|
||||
$deletable = true;
|
||||
$this->formService->deleteForm($form);
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Form deleted successfuly'));
|
||||
|
||||
} catch (FormHasConstraintsException $ex) {
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
|
||||
if (! is_null($form) && ! is_null($form->vacancies) && $form->vacancies->count() !== 0 || ! is_null($form->responses)) {
|
||||
$deletable = false;
|
||||
}
|
||||
|
||||
if ($deletable) {
|
||||
$form->delete();
|
||||
|
||||
$request->session()->flash('success', __('Form deleted successfully.'));
|
||||
} else {
|
||||
$request->session()->flash('error', __('You cannot delete this form because it\'s tied to one or more applications and ranks, or because it doesn\'t exist.'));
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
|
||||
}
|
||||
|
||||
public function preview(Request $request, Form $form)
|
||||
|
@ -124,22 +110,15 @@ class FormController extends Controller
|
|||
public function update(Request $request, Form $form)
|
||||
{
|
||||
$this->authorize('update', $form);
|
||||
$updatedForm = $this->formService->updateForm($form, $request->all());
|
||||
|
||||
$contextValidation = ContextAwareValidator::getValidator($request->all(), true);
|
||||
$this->authorize('update', $form);
|
||||
|
||||
if (! $contextValidation->get('validator')->fails()) {
|
||||
// Add the new structure into the form. New, subsquent fields will be identified by the "new" prefix
|
||||
// This prefix doesn't actually change the app's behavior when it receives applications.
|
||||
// Additionally, old applications won't of course display new and updated fields, because we can't travel into the past and get data for them
|
||||
$form->formStructure = $contextValidation->get('structure');
|
||||
$form->save();
|
||||
|
||||
$request->session()->flash('success', __('Hooray! Your form was updated. New applications for it\'s vacancy will use it.'));
|
||||
} else {
|
||||
$request->session()->flash('errors', $contextValidation->get('validator')->errors()->getMessages());
|
||||
if ($updatedForm instanceof Form) {
|
||||
return redirect()->to(route('previewForm', ['form' => $updatedForm->id]));
|
||||
}
|
||||
|
||||
return redirect()->to(route('previewForm', ['form' => $form->id]));
|
||||
// array of errors
|
||||
return redirect()
|
||||
->back()
|
||||
->with('errors', $updatedForm);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ use App\Vacancy;
|
|||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
// doesn't need a service, because it doesn't contain major logic.
|
||||
|
||||
/**
|
||||
* Show the application dashboard.
|
||||
*
|
||||
|
|
|
@ -21,14 +21,25 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\InvalidGamePreferenceException;
|
||||
use App\Exceptions\OptionNotFoundException;
|
||||
use App\Facades\Options;
|
||||
use App\Options as Option;
|
||||
use App\Services\ConfigurationService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class OptionsController extends Controller
|
||||
{
|
||||
private $configurationService;
|
||||
|
||||
public function __construct(ConfigurationService $configurationService) {
|
||||
|
||||
$this->configurationService = $configurationService;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
|
@ -36,7 +47,7 @@ class OptionsController extends Controller
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
|
||||
// TODO: Replace with settings package
|
||||
return view('dashboard.administration.settings')
|
||||
->with([
|
||||
'options' => Options::getCategory('notifications'),
|
||||
|
@ -51,65 +62,44 @@ class OptionsController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
public function saveSettings(Request $request)
|
||||
public function saveSettings(Request $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
if (Auth::user()->can('admin.settings.edit')) {
|
||||
Log::debug('Updating application options', [
|
||||
'ip' => $request->ip(),
|
||||
'ua' => $request->userAgent(),
|
||||
'username' => Auth::user()->username,
|
||||
]);
|
||||
foreach ($request->all() as $optionName => $option) {
|
||||
try {
|
||||
Log::debug('Going through option '.$optionName);
|
||||
if (Options::optionExists($optionName)) {
|
||||
Log::debug('Option exists, updating to new values', [
|
||||
'opt' => $optionName,
|
||||
'new_value' => $option,
|
||||
]);
|
||||
Options::changeOption($optionName, $option);
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
Log::error('Unable to update options!', [
|
||||
'msg' => $ex->getMessage(),
|
||||
'trace' => $ex->getTraceAsString(),
|
||||
]);
|
||||
report($ex);
|
||||
|
||||
$errorCond = true;
|
||||
$request->session()->flash('error', __('An error occurred while trying to save settings: :message ', ['message' => $ex->getMessage()]));
|
||||
}
|
||||
if (Auth::user()->can('admin.settings.edit')) {
|
||||
$this->configurationService->saveConfiguration($request->all());
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Options updated successfully!'));
|
||||
}
|
||||
|
||||
if (! isset($errorCond)) {
|
||||
$request->session()->flash('success', __('Settings saved successfully!'));
|
||||
}
|
||||
} else {
|
||||
$request->session()->flash('error', __('You do not have permission to update this resource.'));
|
||||
} catch (OptionNotFoundException | \Exception $ex) {
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', __('You do not have permission to update this resource.'));
|
||||
}
|
||||
|
||||
public function saveGameIntegration(Request $request)
|
||||
{
|
||||
$supportedGames = [
|
||||
'RUST',
|
||||
'MINECRAFT',
|
||||
'SE',
|
||||
'GMOD'
|
||||
];
|
||||
try {
|
||||
|
||||
if (!is_null($request->gamePref) && in_array($request->gamePref, $supportedGames))
|
||||
{
|
||||
Options::changeOption('currentGame', $request->gamePref);
|
||||
$request->session()->flash('success', __('Updated current game.'));
|
||||
$this->configurationService->saveGameIntegration($request->gamePref);
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Game preference updated.'));
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
$request->session()->flash('error', __('Unsupported game :game.', ['game' => $request->gamePref ]));
|
||||
|
||||
return redirect()->back();
|
||||
} catch (InvalidGamePreferenceException $ex) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ namespace App\Http\Controllers;
|
|||
|
||||
use App\Facades\IP;
|
||||
use App\Http\Requests\ProfileSave;
|
||||
use App\Services\ProfileService;
|
||||
use App\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
|
@ -31,6 +32,12 @@ use Spatie\Permission\Models\Role;
|
|||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
private $profileService;
|
||||
|
||||
public function __construct(ProfileService $profileService) {
|
||||
$this->profileService = $profileService;
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
return view('dashboard.user.directory')
|
||||
|
@ -39,6 +46,7 @@ class ProfileController extends Controller
|
|||
|
||||
public function showProfile()
|
||||
{
|
||||
// TODO: Come up with cleaner social media solution, e.g. social media object
|
||||
$socialLinks = Auth::user()->profile->socialLinks ?? '[]';
|
||||
$socialMediaProfiles = json_decode($socialLinks, true);
|
||||
|
||||
|
@ -52,8 +60,7 @@ class ProfileController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
// Route model binding
|
||||
public function showSingleProfile(Request $request, User $user)
|
||||
public function showSingleProfile(User $user)
|
||||
{
|
||||
$socialMediaProfiles = json_decode($user->profile->socialLinks, true);
|
||||
$createdDate = Carbon::parse($user->created_at);
|
||||
|
@ -102,36 +109,9 @@ class ProfileController extends Controller
|
|||
|
||||
public function saveProfile(ProfileSave $request)
|
||||
{
|
||||
$profile = User::find(Auth::user()->id)->profile;
|
||||
$social = [];
|
||||
|
||||
if (! is_null($profile)) {
|
||||
switch ($request->avatarPref) {
|
||||
case 'MOJANG':
|
||||
$avatarPref = 'crafatar';
|
||||
|
||||
break;
|
||||
case 'GRAVATAR':
|
||||
$avatarPref = strtolower($request->avatarPref);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$social['links']['github'] = $request->socialGithub;
|
||||
$social['links']['twitter'] = $request->socialTwitter;
|
||||
$social['links']['insta'] = $request->socialInsta;
|
||||
$social['links']['discord'] = $request->socialDiscord;
|
||||
|
||||
$profile->profileShortBio = $request->shortBio;
|
||||
$profile->profileAboutMe = $request->aboutMe;
|
||||
$profile->avatarPreference = $avatarPref;
|
||||
$profile->socialLinks = json_encode($social);
|
||||
|
||||
$newProfile = $profile->save();
|
||||
|
||||
$request->session()->flash('success', __('Profile settings saved successfully.'));
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
$this->profileService->updateProfile(Auth::user()->id, $request);
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Profile updated.'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace App\Http\Controllers;
|
|||
|
||||
use App\Facades\Options;
|
||||
use App\Http\Requests\SaveSecuritySettings;
|
||||
use App\Services\SecuritySettingsService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
|
@ -11,38 +12,24 @@ use function PHPSTORM_META\map;
|
|||
|
||||
class SecuritySettingsController extends Controller
|
||||
{
|
||||
private $securityService;
|
||||
|
||||
public function __construct(SecuritySettingsService $securityService) {
|
||||
$this->securityService = $securityService;
|
||||
}
|
||||
|
||||
public function save(SaveSecuritySettings $request)
|
||||
{
|
||||
$validPolicies = [
|
||||
'off',
|
||||
'low',
|
||||
'medium',
|
||||
'high'
|
||||
];
|
||||
|
||||
if (in_array($request->secPolicy, $validPolicies))
|
||||
{
|
||||
Options::changeOption('pw_security_policy', $request->secPolicy);
|
||||
|
||||
Log::debug('[Options] Changing option pw_security_policy', [
|
||||
'new_value' => $request->secPolicy
|
||||
$this->securityService->save($request->secPolicy, [
|
||||
'graceperiod' => $request->graceperiod,
|
||||
'pwExpiry' => $request->pwExpiry,
|
||||
'enforce2fa' => $request->enforce2fa,
|
||||
'requirePMC' => $request->requirePMC
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::debug('[WARN] Ignoring bogus policy', [
|
||||
'avaliable' => $validPolicies,
|
||||
'given' >= $request->secPolicy
|
||||
]);
|
||||
}
|
||||
|
||||
Options::changeOption('graceperiod', $request->graceperiod);
|
||||
Options::changeOption('password_expiry', $request->pwExpiry);
|
||||
Options::changeOption('force2fa', $request->enforce2fa);
|
||||
Options::changeOption('requireGameLicense', $request->requirePMC);
|
||||
|
||||
$request->session()->flash('success', __('Settings saved successfully.'));
|
||||
return redirect()->back();
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Settings saved.'));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,13 +21,18 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\InvalidInviteException;
|
||||
use App\Exceptions\PublicTeamInviteException;
|
||||
use App\Exceptions\UserAlreadyInvitedException;
|
||||
use App\Http\Requests\EditTeamRequest;
|
||||
use App\Http\Requests\NewTeamRequest;
|
||||
use App\Http\Requests\SendInviteRequest;
|
||||
use App\Mail\InviteToTeam;
|
||||
use App\Services\TeamService;
|
||||
use App\Team;
|
||||
use App\User;
|
||||
use App\Vacancy;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
@ -37,10 +42,15 @@ use Mpociot\Teamwork\TeamInvite;
|
|||
|
||||
class TeamController extends Controller
|
||||
{
|
||||
private $teamService;
|
||||
|
||||
public function __construct(TeamService $teamService) {
|
||||
$this->teamService = $teamService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
|
@ -56,23 +66,17 @@ class TeamController extends Controller
|
|||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param NewTeamRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @return RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function store(NewTeamRequest $request)
|
||||
{
|
||||
$this->authorize('create', Team::class);
|
||||
$this->teamService->createTeam($request->teamName, Auth::user()->id);
|
||||
|
||||
$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();
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Team successfully created.'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,21 +102,24 @@ class TeamController extends Controller
|
|||
*
|
||||
* @param EditTeamRequest $request
|
||||
* @param Team $team
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function update(EditTeamRequest $request, Team $team): \Illuminate\Http\Response
|
||||
public function update(EditTeamRequest $request, Team $team): RedirectResponse
|
||||
{
|
||||
$this->authorize('update', $team);
|
||||
$team = $this->teamService->updateTeam($team, $request->teamDescription, $team->joinType);
|
||||
|
||||
|
||||
$team->description = $request->teamDescription;
|
||||
$team->openJoin = $request->joinType;
|
||||
if ($team) {
|
||||
return redirect()
|
||||
->to(route('teams.index'))
|
||||
->with('success', __('Team updated.'));
|
||||
}
|
||||
|
||||
$team->save();
|
||||
$request->session()->flash('success', __('Team edited successfully.'));
|
||||
|
||||
return redirect()->to(route('teams.index'));
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', __('An error ocurred while trying to update this team.'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,68 +133,45 @@ class TeamController extends Controller
|
|||
// wip
|
||||
}
|
||||
|
||||
public function invite(SendInviteRequest $request, Team $team): \Illuminate\Http\RedirectResponse
|
||||
public function invite(SendInviteRequest $request, Team $team): RedirectResponse
|
||||
{
|
||||
$this->authorize('invite', $team);
|
||||
|
||||
$user = User::findOrFail($request->user);
|
||||
try {
|
||||
|
||||
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));
|
||||
});
|
||||
$this->teamService->inviteUser($team, $request->user);
|
||||
|
||||
$request->session()->flash('success', __('Invite sent! They can now accept or deny it.'));
|
||||
} else {
|
||||
$request->session()->flash('error', __('This user has already been invited.'));
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('User invited successfully!'));
|
||||
|
||||
} catch (UserAlreadyInvitedException | PublicTeamInviteException $ex) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
}
|
||||
} else {
|
||||
$request->session()->flash('error', __('You can\'t invite users to public teams.'));
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function processInviteAction(Request $request, $action, $token): \Illuminate\Http\RedirectResponse
|
||||
public function processInviteAction(Request $request, $action, $token): RedirectResponse
|
||||
{
|
||||
switch ($action) {
|
||||
case 'accept':
|
||||
try {
|
||||
|
||||
$invite = Teamwork::getInviteFromAcceptToken($token);
|
||||
$this->teamService->processInvite(Auth::user(), $action, $token);
|
||||
|
||||
if ($invite && $invite->user->is(Auth::user())) {
|
||||
Teamwork::acceptInvite($invite);
|
||||
$request->session()->flash('success', __('Invite accepted! You have now joined :teamName.', ['teamName' => $invite->team->name]));
|
||||
} else {
|
||||
$request->session()->flash('error', __('Invalid or expired invite URL.'));
|
||||
}
|
||||
return redirect()
|
||||
->to(route('teams.index'))
|
||||
->with('success', __('Invite processed successfully!'));
|
||||
|
||||
break;
|
||||
} catch (InvalidInviteException $e) {
|
||||
|
||||
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.');
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $e->getMessage());
|
||||
|
||||
}
|
||||
|
||||
// This page will show the user's current teams
|
||||
return redirect()->to(route('teams.index'));
|
||||
}
|
||||
|
||||
public function switchTeam(Request $request, Team $team): \Illuminate\Http\RedirectResponse
|
||||
public function switchTeam(Request $request, Team $team): RedirectResponse
|
||||
{
|
||||
$this->authorize('switchTeam', $team);
|
||||
|
||||
|
@ -203,44 +187,13 @@ class TeamController extends Controller
|
|||
}
|
||||
|
||||
// Since it's a separate form, we shouldn't use the same update method
|
||||
public function assignVacancies(Request $request, Team $team): \Illuminate\Http\RedirectResponse
|
||||
public function assignVacancies(Request $request, Team $team): RedirectResponse
|
||||
{
|
||||
$this->authorize('update', $team);
|
||||
$message = $this->teamService->updateVacancies($team, $request->assocVacancies);
|
||||
|
||||
// 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();
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', $message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ namespace App\Http\Controllers;
|
|||
// Most of these namespaces have no effect on the code, however, they're used by IDEs so they can resolve return types and for PHPDocumentor as well
|
||||
|
||||
|
||||
use App\Exceptions\FileUploadException;
|
||||
use App\Services\TeamFileService;
|
||||
use App\TeamFile;
|
||||
use App\Http\Requests\UploadFileRequest;
|
||||
|
||||
|
@ -24,11 +26,16 @@ use Illuminate\Http\Response;
|
|||
|
||||
class TeamFileController extends Controller
|
||||
{
|
||||
private $fileService;
|
||||
|
||||
public function __construct(TeamFileService $fileService) {
|
||||
$this->fileService = $fileService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param Request $request
|
||||
* @return Application|Factory|View|Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
|
@ -55,33 +62,24 @@ class TeamFileController extends Controller
|
|||
{
|
||||
$this->authorize('store', TeamFile::class);
|
||||
|
||||
$upload = $request->file('file');
|
||||
try {
|
||||
$caption = $request->caption;
|
||||
$description = $request->description;
|
||||
|
||||
$file = $upload->store('uploads');
|
||||
$originalFileName = $upload->getClientOriginalName();
|
||||
$originalFileExtension = $upload->extension();
|
||||
$originalFileSize = $upload->getSize();
|
||||
$this->fileService->addFile($request->file('file'), Auth::user()->id, Auth::user()->currentTeam->id, $caption, $description);
|
||||
|
||||
$fileEntry = TeamFile::create([
|
||||
'uploaded_by' => Auth::user()->id,
|
||||
'team_id' => Auth::user()->currentTeam->id,
|
||||
'name' => $originalFileName,
|
||||
'caption' => $request->caption,
|
||||
'description' => $request->description,
|
||||
'fs_location' => $file,
|
||||
'extension' => $originalFileExtension,
|
||||
'size' => $originalFileSize
|
||||
]);
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('File uploaded successfully.'));
|
||||
|
||||
} catch (FileUploadException $uploadException) {
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $uploadException->getMessage());
|
||||
|
||||
if ($fileEntry && !is_bool($file))
|
||||
{
|
||||
$request->session()->flash('success', 'File uploaded successfully!');
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
$request->session()->flash('error', 'There was an unknown error whilst trying to upload your file.');
|
||||
return redirect()->back();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -101,29 +99,6 @@ class TeamFileController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\TeamFile $teamFile
|
||||
* @return Response
|
||||
*/
|
||||
public function edit(TeamFile $teamFile)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\TeamFile $teamFile
|
||||
* @return Response
|
||||
*/
|
||||
public function update(Request $request, TeamFile $teamFile)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use ContextAwareValidator;
|
||||
use App\Application;
|
||||
use App\Events\ApplicationDeniedEvent;
|
||||
use App\Exceptions\ApplicationNotFoundException;
|
||||
use App\Exceptions\IncompleteApplicationException;
|
||||
use App\Exceptions\UnavailableApplicationException;
|
||||
use App\Exceptions\VacancyNotFoundException;
|
||||
use App\Notifications\ApplicationMoved;
|
||||
use App\Notifications\NewApplicant;
|
||||
use App\Response;
|
||||
use App\User;
|
||||
use App\Vacancy;
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ApplicationService
|
||||
{
|
||||
public function renderForm($vacancySlug)
|
||||
{
|
||||
$vacancyWithForm = Vacancy::with('forms')->where('vacancySlug', $vacancySlug)->get();
|
||||
|
||||
$firstVacancy = $vacancyWithForm->first();
|
||||
|
||||
if (!$vacancyWithForm->isEmpty() && $firstVacancy->vacancyCount !== 0 && $firstVacancy->vacancyStatus == 'OPEN') {
|
||||
return view('dashboard.application-rendering.apply')
|
||||
->with([
|
||||
'vacancy' => $vacancyWithForm->first(),
|
||||
'preprocessedForm' => json_decode($vacancyWithForm->first()->forms->formStructure, true),
|
||||
]);
|
||||
} else {
|
||||
|
||||
throw new ApplicationNotFoundException('The application you\'re looking for could not be found or it is currently unavailable.', 404);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills a vacancy's form with submitted data.
|
||||
*
|
||||
* @throws UnavailableApplicationException Thrown when the application has no vacancies or is closed
|
||||
* @throws VacancyNotFoundException Thrown when the associated vacancy is not found
|
||||
* @throws IncompleteApplicationException Thrown when there are missing fields
|
||||
*/
|
||||
public function fillForm(Authenticatable $applicant, array $formData, $vacancySlug): bool
|
||||
{
|
||||
$vacancy = Vacancy::with('forms')->where('vacancySlug', $vacancySlug)->get();
|
||||
|
||||
if ($vacancy->isEmpty()) {
|
||||
|
||||
throw new VacancyNotFoundException('This vacancy doesn\'t exist; Please use the proper buttons to apply to one.', 404);
|
||||
|
||||
}
|
||||
|
||||
if ($vacancy->first()->vacancyCount == 0 || $vacancy->first()->vacancyStatus !== 'OPEN') {
|
||||
|
||||
throw new UnavailableApplicationException("This application is unavailable.");
|
||||
}
|
||||
|
||||
Log::info('Processing new application!');
|
||||
|
||||
$formStructure = json_decode($vacancy->first()->forms->formStructure, true);
|
||||
$responseValidation = ContextAwareValidator::getResponseValidator($formData, $formStructure);
|
||||
|
||||
|
||||
Log::info('Built response & validator structure!');
|
||||
|
||||
if (!$responseValidation->get('validator')->fails()) {
|
||||
$response = Response::create([
|
||||
'responseFormID' => $vacancy->first()->forms->id,
|
||||
'associatedVacancyID' => $vacancy->first()->id, // Since a form can be used by multiple vacancies, we can only know which specific vacancy this response ties to by using a vacancy ID
|
||||
'responseData' => $responseValidation->get('responseStructure'),
|
||||
]);
|
||||
|
||||
Log::info('Registered form response!', [
|
||||
'applicant' => $applicant->name,
|
||||
'vacancy' => $vacancy->first()->vacancyName
|
||||
]);
|
||||
|
||||
$application = Application::create([
|
||||
'applicantUserID' => $applicant->id,
|
||||
'applicantFormResponseID' => $response->id,
|
||||
'applicationStatus' => 'STAGE_SUBMITTED',
|
||||
]);
|
||||
|
||||
Log::info('Submitted an application!', [
|
||||
'responseID' => $response->id,
|
||||
'applicant' => $applicant->name
|
||||
]);
|
||||
|
||||
foreach (User::all() as $user) {
|
||||
if ($user->hasRole('admin')) {
|
||||
$user->notify((new NewApplicant($application, $vacancy->first()))->delay(now()->addSeconds(10)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
Log::warning('Application form for ' . $applicant->name . ' contained errors, resetting!');
|
||||
|
||||
throw new IncompleteApplicationException('There are one or more errors in your application. Please make sure none of your fields are empty, since they are all required.');
|
||||
}
|
||||
|
||||
public function updateStatus(Application $application, $newStatus)
|
||||
{
|
||||
switch ($newStatus) {
|
||||
case 'deny':
|
||||
|
||||
event(new ApplicationDeniedEvent($application));
|
||||
$message = __("Application denied successfully.");
|
||||
|
||||
break;
|
||||
|
||||
case 'interview':
|
||||
Log::info(' Moved application ID ' . $application->id . 'to interview stage!');
|
||||
$message = __('Application moved to interview stage!');
|
||||
|
||||
$application->setStatus('STAGE_INTERVIEW');
|
||||
$application->user->notify(new ApplicationMoved());
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \LogicException("Wrong status parameter. Please notify a developer.");
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete(Application $application): ?bool
|
||||
{
|
||||
return $application->delete();
|
||||
}
|
||||
|
||||
|
||||
public function canVote($votes): bool
|
||||
{
|
||||
$allvotes = collect([]);
|
||||
|
||||
foreach ($votes as $vote) {
|
||||
if ($vote->userID == Auth::user()->id) {
|
||||
$allvotes->push($vote);
|
||||
}
|
||||
}
|
||||
|
||||
return !(($allvotes->count() == 1));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Application;
|
||||
use App\Appointment;
|
||||
use App\Exceptions\InvalidAppointmentStatusException;
|
||||
use App\Notifications\ApplicationMoved;
|
||||
use App\Notifications\AppointmentScheduled;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AppointmentService
|
||||
{
|
||||
private $allowedPlatforms = [
|
||||
|
||||
'ZOOM',
|
||||
'DISCORD',
|
||||
'SKYPE',
|
||||
'MEET',
|
||||
'TEAMSPEAK',
|
||||
|
||||
];
|
||||
|
||||
public function createAppointment(Application $application, Carbon $appointmentDate, $appointmentDescription, $appointmentLocation)
|
||||
{
|
||||
$appointment = Appointment::create([
|
||||
'appointmentDescription' => $appointmentDescription,
|
||||
'appointmentDate' => $appointmentDate->toDateTimeString(),
|
||||
'applicationID' => $application->id,
|
||||
'appointmentLocation' => (in_array($appointmentLocation, $this->allowedPlatforms)) ? $appointmentLocation : 'DISCORD',
|
||||
]);
|
||||
$application->setStatus('STAGE_INTERVIEW_SCHEDULED');
|
||||
|
||||
Log::info('User '.Auth::user()->name.' has scheduled an appointment with '.$application->user->name.' for application ID'.$application->id, [
|
||||
'datetime' => $appointmentDate->toDateTimeString(),
|
||||
'scheduled' => now(),
|
||||
]);
|
||||
|
||||
$application->user->notify(new AppointmentScheduled($appointment));
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the appointment with the new $status.
|
||||
* It also sets the application's status to peer approval.
|
||||
*
|
||||
* Set $updateApplication to false to only update its status
|
||||
*
|
||||
* @throws InvalidAppointmentStatusException
|
||||
*/
|
||||
public function updateAppointment(Application $application, $status, $updateApplication = true)
|
||||
{
|
||||
$validStatuses = [
|
||||
'SCHEDULED',
|
||||
'CONCLUDED',
|
||||
];
|
||||
|
||||
if ($status == 'SCHEDULED' || $status == 'CONCLUDED')
|
||||
{
|
||||
$application->appointment->appointmentStatus = strtoupper($status);
|
||||
$application->appointment->save();
|
||||
|
||||
if ($updateApplication)
|
||||
{
|
||||
$application->setStatus('STAGE_PEERAPPROVAL');
|
||||
$application->user->notify(new ApplicationMoved());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidAppointmentStatusException("Invalid appointment status!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAllowedPlatforms(): array
|
||||
{
|
||||
return $this->allowedPlatforms;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Application;
|
||||
use App\Comment;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class CommentService
|
||||
{
|
||||
|
||||
public function addComment(Application $application, $comment): Comment {
|
||||
return Comment::create([
|
||||
'authorID' => Auth::user()->id,
|
||||
'applicationID' => $application->id,
|
||||
'text' => $comment,
|
||||
]);
|
||||
}
|
||||
|
||||
public function deleteComment(Comment $comment): ?bool
|
||||
{
|
||||
return $comment->delete();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Exceptions\InvalidGamePreferenceException;
|
||||
use App\Exceptions\OptionNotFoundException;
|
||||
use App\Facades\Options;
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ConfigurationService
|
||||
{
|
||||
|
||||
/**
|
||||
* @throws OptionNotFoundException|\Exception
|
||||
*
|
||||
*/
|
||||
public function saveConfiguration($configuration) {
|
||||
|
||||
foreach ($configuration as $optionName => $option) {
|
||||
try {
|
||||
|
||||
Log::debug('Going through option '.$optionName);
|
||||
if (Options::optionExists($optionName)) {
|
||||
Log::debug('Option exists, updating to new values', [
|
||||
'opt' => $optionName,
|
||||
'new_value' => $option,
|
||||
]);
|
||||
Options::changeOption($optionName, $option);
|
||||
}
|
||||
|
||||
} catch (\Exception $ex) {
|
||||
|
||||
Log::error('Unable to update options!', [
|
||||
'msg' => $ex->getMessage(),
|
||||
'trace' => $ex->getTraceAsString(),
|
||||
]);
|
||||
|
||||
// Let service caller handle this without failing here
|
||||
throw $ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the chosen game integration
|
||||
*
|
||||
* @throws InvalidGamePreferenceException
|
||||
* @returns bool
|
||||
*/
|
||||
public function saveGameIntegration($gamePreference): bool
|
||||
{
|
||||
|
||||
// TODO: Find solution to dynamically support games
|
||||
|
||||
$supportedGames = [
|
||||
'RUST',
|
||||
'MINECRAFT',
|
||||
'SE',
|
||||
'GMOD'
|
||||
];
|
||||
|
||||
if (!is_null($gamePreference) && in_array($gamePreference, $supportedGames))
|
||||
{
|
||||
Options::changeOption('currentGame', $gamePreference);
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new InvalidGamePreferenceException("Unsupported game " . $gamePreference);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Exceptions\FailedCaptchaException;
|
||||
use App\Notifications\NewContact;
|
||||
use App\User;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class ContactService
|
||||
{
|
||||
|
||||
/**
|
||||
* Sends a message to all admins.
|
||||
*
|
||||
* @throws FailedCaptchaException
|
||||
*/
|
||||
public function sendMessage($ipAddress, $message, $email, $challenge)
|
||||
{
|
||||
// TODO: now: add middleware for this verification, move to invisible captcha
|
||||
$verifyrequest = Http::asForm()->post(config('recaptcha.verify.apiurl'), [
|
||||
'secret' => config('recaptcha.keys.secret'),
|
||||
'response' => $challenge,
|
||||
'remoteip' => $ipAddress,
|
||||
]);
|
||||
|
||||
$response = json_decode($verifyrequest->getBody(), true);
|
||||
|
||||
if (! $response['success']) {
|
||||
throw new FailedCaptchaException('Beep beep boop... Robot? Submission failed.');
|
||||
}
|
||||
|
||||
foreach (User::all() as $user) {
|
||||
if ($user->hasRole('admin')) {
|
||||
$user->notify(new NewContact(collect([
|
||||
'message' => $message,
|
||||
'ip' => $ipAddress,
|
||||
'email' => $email,
|
||||
])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Exceptions\EmptyFormException;
|
||||
use App\Exceptions\FormHasConstraintsException;
|
||||
use App\Form;
|
||||
use ContextAwareValidator;
|
||||
|
||||
class FormManagementService
|
||||
{
|
||||
|
||||
public function addForm($fields) {
|
||||
|
||||
if (count($fields) == 2) {
|
||||
// form is probably empty, since forms with fields will always have more than 2 items
|
||||
throw new EmptyFormException('Sorry, but you may not create empty forms.');
|
||||
}
|
||||
|
||||
$contextValidation = ContextAwareValidator::getValidator($fields, true, true);
|
||||
|
||||
if (! $contextValidation->get('validator')->fails()) {
|
||||
$storableFormStructure = $contextValidation->get('structure');
|
||||
|
||||
Form::create(
|
||||
[
|
||||
'formName' => $fields['formName'],
|
||||
'formStructure' => $storableFormStructure,
|
||||
'formStatus' => 'ACTIVE',
|
||||
]
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return $contextValidation->get('validator')->errors()->getMessages();
|
||||
}
|
||||
|
||||
public function deleteForm(Form $form) {
|
||||
|
||||
$deletable = true;
|
||||
|
||||
if (! is_null($form) && ! is_null($form->vacancies) && $form->vacancies->count() !== 0 || ! is_null($form->responses)) {
|
||||
$deletable = false;
|
||||
}
|
||||
|
||||
if ($deletable) {
|
||||
|
||||
$form->delete();
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
throw new FormHasConstraintsException(__('You cannot delete this form because it\'s tied to one or more applications and ranks, or because it doesn\'t exist.'));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function updateForm(Form $form, $fields) {
|
||||
|
||||
$contextValidation = ContextAwareValidator::getValidator($fields, true);
|
||||
|
||||
if (! $contextValidation->get('validator')->fails()) {
|
||||
// Add the new structure into the form. New, subsquent fields will be identified by the "new" prefix
|
||||
// This prefix doesn't actually change the app's behavior when it receives applications.
|
||||
// Additionally, old applications won't of course display new and updated fields, because we can't travel into the past and get data for them
|
||||
$form->formStructure = $contextValidation->get('structure');
|
||||
$form->save();
|
||||
|
||||
return $form;
|
||||
|
||||
} else {
|
||||
return $contextValidation->get('validator')->errors()->getMessages();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Application;
|
||||
use App\Exceptions\InvalidAppointmentException;
|
||||
|
||||
class MeetingNoteService
|
||||
{
|
||||
/**
|
||||
* Adds meeting notes to an application.
|
||||
*
|
||||
* @param Application $application
|
||||
* @param $noteText
|
||||
* @return bool
|
||||
* @throws InvalidAppointmentException Thrown when an application doesn't have an appointment to save notes to
|
||||
*/
|
||||
public function addToApplication(Application $application, $noteText): bool {
|
||||
|
||||
if (! is_null($application)) {
|
||||
$application->load('appointment');
|
||||
|
||||
$application->appointment->meetingNotes = $noteText;
|
||||
$application->appointment->save();
|
||||
|
||||
return true;
|
||||
|
||||
} else {
|
||||
throw new InvalidAppointmentException('There\'s no appointment to save notes to!');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Exceptions\ProfileNotFoundException;
|
||||
use App\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class ProfileService
|
||||
{
|
||||
|
||||
/**
|
||||
* @throws ProfileNotFoundException
|
||||
*/
|
||||
public function updateProfile($userID, Request $request) {
|
||||
$profile = User::find($userID)->profile;
|
||||
$social = [];
|
||||
|
||||
if (! is_null($profile)) {
|
||||
switch ($request->avatarPref) {
|
||||
case 'MOJANG':
|
||||
$avatarPref = 'crafatar';
|
||||
|
||||
break;
|
||||
case 'GRAVATAR':
|
||||
$avatarPref = strtolower($request->avatarPref);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$social['links']['github'] = $request->socialGithub;
|
||||
$social['links']['twitter'] = $request->socialTwitter;
|
||||
$social['links']['insta'] = $request->socialInsta;
|
||||
$social['links']['discord'] = $request->socialDiscord;
|
||||
|
||||
$profile->profileShortBio = $request->shortBio;
|
||||
$profile->profileAboutMe = $request->aboutMe;
|
||||
$profile->avatarPreference = $avatarPref;
|
||||
$profile->socialLinks = json_encode($social);
|
||||
|
||||
return $profile->save();
|
||||
}
|
||||
|
||||
throw new ProfileNotFoundException("This profile does not exist.");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Facades\Options;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class SecuritySettingsService
|
||||
{
|
||||
|
||||
/**
|
||||
* Saves the app security settings.
|
||||
*
|
||||
* @param $policy
|
||||
* @param array $options
|
||||
* @return bool
|
||||
*/
|
||||
public function save($policy, $options = []) {
|
||||
|
||||
$validPolicies = [
|
||||
'off',
|
||||
'low',
|
||||
'medium',
|
||||
'high'
|
||||
];
|
||||
|
||||
if (in_array($policy, $validPolicies))
|
||||
{
|
||||
Options::changeOption('pw_security_policy', $policy);
|
||||
|
||||
Log::debug('[Options] Changing option pw_security_policy', [
|
||||
'new_value' => $policy
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log::debug('[WARN] Ignoring bogus policy', [
|
||||
'avaliable' => $validPolicies,
|
||||
'given' => $policy
|
||||
]);
|
||||
}
|
||||
|
||||
Options::changeOption('graceperiod', $options['graceperiod']);
|
||||
Options::changeOption('password_expiry', $options['pwexpiry']);
|
||||
Options::changeOption('force2fa', $options['enforce2fa']);
|
||||
Options::changeOption('requireGameLicense', $options['requirePMC']);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Exceptions\FileUploadException;
|
||||
use App\TeamFile;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class TeamFileService
|
||||
{
|
||||
|
||||
public function addFile(UploadedFile $upload, $uploader, $team, $caption, $description) {
|
||||
|
||||
$file = $upload->store('uploads');
|
||||
$originalFileName = $upload->getClientOriginalName();
|
||||
$originalFileExtension = $upload->extension();
|
||||
$originalFileSize = $upload->getSize();
|
||||
|
||||
$fileEntry = TeamFile::create([
|
||||
'uploaded_by' => $uploader,
|
||||
'team_id' => $team,
|
||||
'name' => $originalFileName,
|
||||
'caption' => $caption,
|
||||
'description' => $description,
|
||||
'fs_location' => $file,
|
||||
'extension' => $originalFileExtension,
|
||||
'size' => $originalFileSize
|
||||
]);
|
||||
|
||||
if ($fileEntry && !is_bool($file))
|
||||
{
|
||||
return $fileEntry;
|
||||
}
|
||||
|
||||
throw new FileUploadException("There was an unknown error whilst trying to upload your file.");
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
|
||||
use App\Exceptions\InvalidInviteException;
|
||||
use App\Exceptions\PublicTeamInviteException;
|
||||
use App\Exceptions\UserAlreadyInvitedException;
|
||||
use App\Mail\InviteToTeam;
|
||||
use App\Team;
|
||||
use App\User;
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Mpociot\Teamwork\Facades\Teamwork;
|
||||
use Mpociot\Teamwork\TeamInvite;
|
||||
|
||||
class TeamService
|
||||
{
|
||||
|
||||
/**
|
||||
* Create a team
|
||||
*
|
||||
* @param $teamName
|
||||
* @param $ownerID
|
||||
* @return Team
|
||||
*/
|
||||
public function createTeam($teamName, $ownerID): Team {
|
||||
|
||||
$team = Team::create([
|
||||
'name' => $teamName,
|
||||
'owner_id' => $ownerID,
|
||||
]);
|
||||
|
||||
Auth::user()->teams()->attach($team->id);
|
||||
|
||||
return $team;
|
||||
}
|
||||
|
||||
public function updateTeam(Team $team, $teamDescription, $joinType): bool
|
||||
{
|
||||
|
||||
$team->description = $teamDescription;
|
||||
$team->openJoin = $joinType;
|
||||
|
||||
return $team->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invites a user to a $team.
|
||||
*
|
||||
* @throws PublicTeamInviteException Thrown when trying to invite a user to a public team
|
||||
* @throws UserAlreadyInvitedException Thrown when a user is already invited
|
||||
*/
|
||||
public function inviteUser(Team $team, $userID): bool
|
||||
{
|
||||
|
||||
$user = User::findOrFail($userID);
|
||||
|
||||
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));
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
throw new UserAlreadyInvitedException('This user has already been invited.');
|
||||
}
|
||||
} else {
|
||||
throw new PublicTeamInviteException('You can\'t invite users to public teams.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts or denies a user invite
|
||||
*
|
||||
* @param Authenticatable $user
|
||||
* @param $action
|
||||
* @param $token
|
||||
* @return bool True on success or exception on failure
|
||||
* @throws InvalidInviteException Thrown when the invite code / url is invalid
|
||||
*/
|
||||
public function processInvite(Authenticatable $user, $action, $token): bool {
|
||||
|
||||
switch ($action) {
|
||||
case 'accept':
|
||||
|
||||
$invite = Teamwork::getInviteFromAcceptToken($token);
|
||||
|
||||
if ($invite && $invite->user->is($user)) {
|
||||
Teamwork::acceptInvite($invite);
|
||||
|
||||
} else {
|
||||
|
||||
throw new InvalidInviteException('Invalid or expired invite URL.');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'deny':
|
||||
|
||||
$invite = Teamwork::getInviteFromDenyToken($token);
|
||||
|
||||
if ($invite && $invite->user->is($user)) {
|
||||
|
||||
Teamwork::denyInvite($invite);
|
||||
|
||||
} else {
|
||||
|
||||
throw new InvalidInviteException('Invalid or expired invite URL.');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidInviteException('Sorry, but the invite URL you followed was malformed.');
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Team $team
|
||||
* @param $associatedVacancies
|
||||
* @return string The success message, exception/bool if error
|
||||
*/
|
||||
public function updateVacancies(Team $team, $associatedVacancies): string
|
||||
{
|
||||
|
||||
// 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 = $associatedVacancies;
|
||||
$currentVacancies = $team->vacancies->pluck('id')->all();
|
||||
|
||||
if (is_null($requestVacancies)) {
|
||||
foreach ($team->vacancies as $vacancy) {
|
||||
$team->vacancies()->detach($vacancy->id);
|
||||
}
|
||||
|
||||
return 'Removed all vacancy associations.';
|
||||
}
|
||||
|
||||
$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);
|
||||
}
|
||||
return 'Assignments changed successfully.';
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue