forked from miguel456/rbrecruiter
Compare commits
84 Commits
Author | SHA1 | Date | |
---|---|---|---|
1aee4c053f
|
|||
a880eb65b0
|
|||
2ddef6058d
|
|||
41d86de5b0
|
|||
427c9d1c57
|
|||
64d8ffa9d9 | |||
4594973a21
|
|||
fc6d7d2b18
|
|||
42178e26de | |||
b2b29382bf
|
|||
92679e94d5
|
|||
d8e836980a
|
|||
8d1e39c43c
|
|||
a3d0730808 | |||
d876dd6055 | |||
521810c23c
|
|||
e6f84cd09a
|
|||
3f4bc28fd4
|
|||
8942623bde
|
|||
c739933668
|
|||
cbcc1f025a
|
|||
6cda1fe183
|
|||
620453c1e4
|
|||
9baa249ba7
|
|||
f62ea9669b
|
|||
|
f267da3760 | ||
6940b1816e
|
|||
ddd79d38fe
|
|||
2bc07d8ca0
|
|||
99779c9053
|
|||
6d94263ede
|
|||
|
242ba7b31e | ||
|
5b39c573b3 | ||
|
638b2719a8 | ||
|
aad04d6d14 | ||
|
5f6f6f693d | ||
|
f83b3a6860 | ||
|
f17bb0e3cc | ||
|
f941980602 | ||
|
0f5e812e03 | ||
|
3a56d2bfb2 | ||
|
26e5a53efb | ||
|
685ec75d0c | ||
|
8769f279ac | ||
|
d234415d38 | ||
|
9c597eeb65 | ||
|
7319d091e2 | ||
14a8e9e9d5
|
|||
aa2bfac3e5
|
|||
42868be96e
|
|||
d1142d3e0c
|
|||
3b28bf1cfe
|
|||
5cf6b2b241
|
|||
3e1a75dfea
|
|||
3156b0d17d
|
|||
abace4e85b
|
|||
baddf3fc76
|
|||
2ec2a92645
|
|||
33960270f3
|
|||
8be29f9739
|
|||
8b9cb7fbc4
|
|||
|
e0fc9b2d94 | ||
29f697d7b2
|
|||
1c0eeb4bb0
|
|||
2f0fc14825
|
|||
bc8570019c
|
|||
aaaf56d415 | |||
79571d8b4c | |||
035e94421e | |||
a72abb9147 | |||
|
96ebdc554e | ||
|
ece01fc71f | ||
|
6f08b852f4 | ||
|
889c771454 | ||
|
2e78d8c321 | ||
|
d7331b2dc1 | ||
|
a8107a5421 | ||
|
7c663dcc02 | ||
85c719c24d | |||
5df3f965ef | |||
4eb115d165 | |||
0433ce7693 | |||
773ec570d9 | |||
53c23f3698 |
10
.env.example
10
.env.example
@@ -11,10 +11,14 @@ APP_SITEHOMEPAGE=""
|
||||
# Void if env is production.
|
||||
NONPROD_FORCE_SECURE=false
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
# Disables certain features for security purposes while running an open authentication system
|
||||
# Enable only for demonostration purposes
|
||||
DEMO_MODE=false
|
||||
|
||||
LOG_CHANNEL=daily
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_HOST=z
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=laravel
|
||||
DB_USERNAME=root
|
||||
@@ -33,7 +37,7 @@ IPGEO_API_URL="https://api.ipgeolocation.io/ipgeo"
|
||||
|
||||
ARCANEDEV_LOGVIEWER_MIDDLEWARE=web,auth,can:admin.maintenance.logs.view
|
||||
|
||||
RELEASE=staffmanagement@0.6.1
|
||||
RELEASE=0.6.2
|
||||
|
||||
SLACK_INTEGRATION_WEBHOOK=
|
||||
|
||||
|
153
.idea/hrm-mcserver.iml
generated
153
.idea/hrm-mcserver.iml
generated
@@ -1,153 +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" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
2
.idea/modules.xml
generated
2
.idea/modules.xml
generated
@@ -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>
|
1
.idea/php.xml
generated
1
.idea/php.xml
generated
@@ -153,6 +153,7 @@
|
||||
<path value="$PROJECT_DIR$/vendor/awssat/discord-notification-channel" />
|
||||
<path value="$PROJECT_DIR$/vendor/berkayk/onesignal-laravel" />
|
||||
<path value="$PROJECT_DIR$/vendor/symfony/psr-http-message-bridge" />
|
||||
<path value="$PROJECT_DIR$/vendor/laravel/sanctum" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="7.3" />
|
||||
|
16
README.md
Executable file → Normal file
16
README.md
Executable file → Normal file
@@ -1,6 +1,6 @@
|
||||
|
||||
# Raspberry Teams - The Simple Staff Application Manager v 0.6.2 [](https://crowdin.com/project/raspberry-staff-manager) [](https://bitbucket.styleci.io/repos/2513833655827911319?branch=develop)
|
||||
## The quick and pain-free staff application manager
|
||||
# RB Recruiter v 0.6.2 [](https://crowdin.com/project/raspberry-staff-manager)
|
||||
## The quick and pain-free form management solution for communities
|
||||
|
||||
Have you ever gotten tired of managing your Minecraft server/network's applications through Discord (or anything else) and having to scroll through hundreds of new messages just to find that one applicant's username?
|
||||
|
||||
@@ -42,11 +42,13 @@ Many other features are currently planned for this app, such as:
|
||||
# Technical overview
|
||||
|
||||
Tech stack:
|
||||
- [Laravel 7](https://laravel.com/)
|
||||
- [Laravel 8](https://laravel.com/)
|
||||
- [Eloquent ORM](https://laravel.com/docs/5.0/eloquent)
|
||||
- [AdminLTE](https://adminlte.io/) / [Bootstrap 4](https://getbootstrap.com/docs/4.0/getting-started/introduction/)
|
||||
- [jQuery](https://jquery.com/) / [Plain Javascript](https://www.javascript.com/)
|
||||
- [vueJS](https://vuejs.org/) (in the future)
|
||||
- [AdminLTE](https://adminlte.io/) /
|
||||
- [Bootstrap 4](https://getbootstrap.com/docs/4.0/getting-started/introduction/)
|
||||
- [jQuery](https://jquery.com/)
|
||||
- [Bootstrap 4](https://getbootstrap.com/)
|
||||
- [Icons by FontAwesome](https://fontawesome.com/)
|
||||
|
||||
# Stability
|
||||
|
||||
@@ -63,7 +65,7 @@ Tech stack:
|
||||
# Software Requirements
|
||||
- ``composer`` (min version: 1.8.4)
|
||||
- ``npm`` (tested w/ v 5.8.0)
|
||||
- ``php`` (required PHP 7 or newer - lower versions unsupported!)
|
||||
- ``php`` (required PHP 8 or newer - lower versions unsupported!)
|
||||
|
||||
# PHP Extension Requirements
|
||||
|
||||
|
25
app/ApiKey.php
Normal file
25
app/ApiKey.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ApiKey extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'status',
|
||||
'discriminator',
|
||||
'last_used',
|
||||
'secret',
|
||||
'owner_user_id'
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('App\User', 'owner_user_id', 'id');
|
||||
}
|
||||
}
|
@@ -33,6 +33,11 @@ class Application extends Model
|
||||
|
||||
];
|
||||
|
||||
public function oneoffApplicant()
|
||||
{
|
||||
return $this->hasOne('App\OneoffApplicant', 'application_id', 'id');
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('App\User', 'applicantUserID', 'id');
|
||||
@@ -64,4 +69,12 @@ class Application extends Model
|
||||
'applicationStatus' => $status,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
public function isOneoff()
|
||||
{
|
||||
return $this->user->id == 1; // ID 1 is always the ghost
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -30,13 +30,13 @@ class Ban extends Model
|
||||
'userID',
|
||||
'reason',
|
||||
'bannedUntil',
|
||||
'userAgent',
|
||||
'isPermanent',
|
||||
'authorUserID',
|
||||
|
||||
];
|
||||
|
||||
public $dates = [
|
||||
'bannedUntil',
|
||||
'suspendedUntil',
|
||||
];
|
||||
|
||||
public function user()
|
||||
|
157
app/Console/Commands/Install.php
Executable file → Normal file
157
app/Console/Commands/Install.php
Executable file → Normal file
@@ -1,24 +1,5 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright © 2020 Miguel Nogueira
|
||||
*
|
||||
* This file is part of Raspberry Staff Manager.
|
||||
*
|
||||
* Raspberry Staff Manager is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Raspberry Staff Manager is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Raspberry Staff Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
@@ -58,87 +39,103 @@ class Install extends Command
|
||||
public function handle()
|
||||
{
|
||||
$basePath = base_path();
|
||||
if (Storage::disk('local')->missing('INSTALLED')) {
|
||||
$this->info('[!! Welcome to Rasberry Teams !!]');
|
||||
$this->info('>> Installing...');
|
||||
$this->call('down');
|
||||
if (Storage::disk('local')->missing('INSTALLED'))
|
||||
{
|
||||
|
||||
copy($basePath.'/.env.example', $basePath.'/.env');
|
||||
$this->call('key:generate');
|
||||
|
||||
$this->info('>> Installing and preparing dependencies. This may take a while, depending on your computer.');
|
||||
$this->info('[!! Welcome to Rasberry Teams !!]');
|
||||
$this->info('>> Installing...');
|
||||
$this->call('down', [
|
||||
'--message' => 'Down for maintenance. We\'ll be right back!'
|
||||
]);
|
||||
|
||||
$npmOut = 0;
|
||||
$npmMessages = [];
|
||||
copy($basePath . '/.env.example', $basePath . '/.env');
|
||||
$this->call('key:generate');
|
||||
|
||||
$npmBuildOut = 0;
|
||||
$npmBuildMessages = [];
|
||||
$this->info('>> Installing and preparing dependencies. This may take a while, depending on your computer.');
|
||||
|
||||
exec('cd '.$basePath.' && npm install --silent', $npmBuildOut, $npmOut);
|
||||
exec('cd '.$basePath.'&& npm run dev --silent', $npmBuildMessages, $npmBuildOut);
|
||||
$npmOut = 0;
|
||||
$npmMessages = [];
|
||||
|
||||
if ($npmOut !== 0 && $npmBuildOut !== 0) {
|
||||
$this->error('[!] One or more errors have ocurred whilst attempting to install dependencies.');
|
||||
$this->error('[!] It is recommended to run this command again, and report a bug if it keeps happening.');
|
||||
$npmBuildOut = 0;
|
||||
$npmBuildMessages = [];
|
||||
|
||||
return false;
|
||||
}
|
||||
exec('cd ' . $basePath . ' && npm install --silent', $npmBuildOut, $npmOut);
|
||||
exec('cd ' . $basePath . '&& npm run dev --silent', $npmBuildMessages, $npmBuildOut);
|
||||
|
||||
$settings = [];
|
||||
|
||||
$this->info('>> Configuring application - We\'re going to ask a few questions here!');
|
||||
do {
|
||||
$this->info('== Database Settings (1/6) ==');
|
||||
if($npmOut !== 0 && $npmBuildOut !== 0)
|
||||
{
|
||||
$this->error('[!] One or more errors have ocurred whilst attempting to install dependencies.');
|
||||
$this->error('[!] It is recommended to run this command again, and report a bug if it keeps happening.');
|
||||
|
||||
$settings['DB_USERNAME'] = $this->ask('Database username');
|
||||
$settings['DB_PASSWORD'] = $this->secret('Database password (Input won\'t be seen)');
|
||||
$settings['DB_DATABASE'] = $this->ask('Database name');
|
||||
$settings['DB_PORT'] = $this->ask('Database port');
|
||||
$settings['DB_HOST'] = $this->ask('Database hostname');
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->info('== Antispam Settings (2/6) (Recaptcha v2) ==');
|
||||
$settings['RECAPTCHA_SITE_KEY'] = $this->ask('Site key');
|
||||
$settings['RECAPTCHA_PRIVATE_KEY'] = $this->ask('Private site key');
|
||||
|
||||
$this->info('== IP Geolocation Settings (3/6) (refer to README.md) ==');
|
||||
$settings['IPGEO_API_KEY'] = $this->ask('API Key');
|
||||
|
||||
$this->info('== Notification Settings (4/6) (Email) ==');
|
||||
$settings['MAIL_USERNAME'] = $this->ask('SMTP Username');
|
||||
$settings['MAIL_PASSWORD'] = $this->secret('SMTP Password (Input won\'t be seen)');
|
||||
$settings['MAIL_PORT'] = $this->ask('SMTP Server Port');
|
||||
$settings['MAIL_HOST'] = $this->ask('SMTP Server Hostname');
|
||||
$settings['MAIL_FROM'] = $this->ask('E-mail address to send from: ');
|
||||
$settings = [];
|
||||
|
||||
$this->info('== Notification Settings (5/6) (Slack) ==');
|
||||
$settings['SLACK_INTEGRATION_WEBHOOK'] = $this->ask('Integration webhook URL');
|
||||
$this->info('>> Configuring application - We\'re going to ask a few questions here!');
|
||||
do
|
||||
{
|
||||
$this->info('== Database Settings (1/6) ==');
|
||||
|
||||
$this->info('== Web Settings (6/6) ==');
|
||||
$settings['APP_URL'] = $this->ask('Application\'s URL (ex. https://where.you.installed.theapp.com): ');
|
||||
$settings['APP_LOGO'] = $this->ask('App logo (Link to an image): ');
|
||||
$settings['APP_SITEHOMEPAGE'] = $this->ask('Site homepage (appears in the main header): ');
|
||||
} while (! $this->confirm('Are you sure you want to save these settings? You can always go back and try again.'));
|
||||
$settings['DB_USERNAME'] = $this->ask('Database username');
|
||||
$settings['DB_PASSWORD'] = $this->secret('Database password (Input won\'t be seen)');
|
||||
$settings['DB_DATABASE'] = $this->ask('Database name');
|
||||
$settings['DB_PORT'] = $this->ask('Database port');
|
||||
$settings['DB_HOST'] = $this->ask('Database hostname');
|
||||
|
||||
foreach ($settings as $keyname => $value) {
|
||||
$this->call('environment:modify', [
|
||||
'key' => $keyname,
|
||||
'value' => $value,
|
||||
]);
|
||||
}
|
||||
$this->info('== Antispam Settings (2/6) (Recaptcha v2) ==');
|
||||
$settings['RECAPTCHA_SITE_KEY'] = $this->ask('Site key');
|
||||
$settings['RECAPTCHA_PRIVATE_KEY'] = $this->ask('Private site key');
|
||||
|
||||
$this->info('>> Saved configuration settings!');
|
||||
$this->info('>> Preparing database...');
|
||||
$this->info('== IP Geolocation Settings (3/6) (refer to README.md) ==');
|
||||
$settings['IPGEO_API_KEY'] = $this->ask('API Key');
|
||||
|
||||
$this->callSilent('config:cache');
|
||||
$this->call('migrate');
|
||||
$this->call('db:seed');
|
||||
$this->info('== Notification Settings (4/6) (Email) ==');
|
||||
$settings['MAIL_USERNAME'] = $this->ask('SMTP Username');
|
||||
$settings['MAIL_PASSWORD'] = $this->secret('SMTP Password (Input won\'t be seen)');
|
||||
$settings['MAIL_PORT'] = $this->ask('SMTP Server Port');
|
||||
$settings['MAIL_HOST'] = $this->ask('SMTP Server Hostname');
|
||||
$settings['MAIL_FROM_ADDRESS'] = $this->ask('E-mail address to send from');
|
||||
|
||||
touch($basePath.'/INSTALLED');
|
||||
$this->info('== Notification Settings (5/6) (Slack) ==');
|
||||
$settings['SLACK_INTEGRATION_WEBHOOK'] = $this->ask('Integration webhook URL');
|
||||
|
||||
$this->call('up');
|
||||
$this->info('>> All done! Visit '.$basePath.' to start using your brand new installation of Raspberry Teams!');
|
||||
} else {
|
||||
$this->error('[!] The application is already installed!');
|
||||
$this->info('== Web Settings (6/6) ==');
|
||||
$settings['APP_URL'] = $this->ask('Application\'s URL (ex. https://where.you.installed.theapp.com): ');
|
||||
$settings['APP_LOGO'] = $this->ask('App logo (Link to an image): ');
|
||||
$settings['APP_SITEHOMEPAGE'] = $this->ask('Site homepage (appears in the main header): ');
|
||||
|
||||
|
||||
} while(!$this->confirm('Are you sure you want to save these settings? You can always go back and try again.'));
|
||||
|
||||
foreach($settings as $keyname => $value)
|
||||
{
|
||||
$this->call('environment:modify', [
|
||||
'key' => $keyname,
|
||||
'value' => $value
|
||||
]);
|
||||
}
|
||||
|
||||
$this->info('>> Saved configuration settings!');
|
||||
$this->info('>> Preparing database...');
|
||||
|
||||
$this->callSilent('config:cache');
|
||||
$this->call('migrate');
|
||||
$this->call('db:seed');
|
||||
|
||||
touch($basePath . '/INSTALLED');
|
||||
|
||||
$this->call('up');
|
||||
$this->info('>> All done! Visit ' . $basePath . ' to start using your brand new installation of Raspberry Teams!');
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->error('[!] The application is already installed!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Jobs\CleanBans;
|
||||
use App\Jobs\ProcessDueSuspensions;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
@@ -50,7 +50,7 @@ class Kernel extends ConsoleKernel
|
||||
->daily();
|
||||
// Production value: Every day
|
||||
|
||||
$schedule->job(new CleanBans)
|
||||
$schedule->job(new ProcessDueSuspensions)
|
||||
->daily();
|
||||
// Production value: Every day
|
||||
}
|
||||
|
@@ -38,13 +38,18 @@ class IP
|
||||
'ip' => $IP,
|
||||
];
|
||||
|
||||
// TODO: Maybe unwrap this? Methods are chained here
|
||||
|
||||
return json_decode(Cache::remember($IP, 3600, function () use ($IP) {
|
||||
return Http::get(config('general.urls.ipapi.ipcheck'), [
|
||||
'apiKey' => config('general.keys.ipapi.apikey'),
|
||||
'ip' => $IP,
|
||||
])->body();
|
||||
}));
|
||||
if (!config('demo.is_enabled')) {
|
||||
return json_decode(Cache::remember($IP, 3600, function () use ($IP) {
|
||||
return Http::get(config('general.urls.ipapi.ipcheck'), [
|
||||
'apiKey' => config('general.keys.ipapi.apikey'),
|
||||
'ip' => $IP,
|
||||
])->body();
|
||||
}));
|
||||
}
|
||||
|
||||
return new class {
|
||||
public $message = "This feature is disabled.";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
11
app/Exceptions/ApplicationNotFoundException.php
Normal file
11
app/Exceptions/ApplicationNotFoundException.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class ApplicationNotFoundException extends ModelNotFoundException
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/EmptyFormException.php
Normal file
10
app/Exceptions/EmptyFormException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class EmptyFormException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/EmptyOptionsException.php
Normal file
10
app/Exceptions/EmptyOptionsException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class EmptyOptionsException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/FailedCaptchaException.php
Normal file
10
app/Exceptions/FailedCaptchaException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class FailedCaptchaException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/FileUploadException.php
Normal file
10
app/Exceptions/FileUploadException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class FileUploadException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/FormHasConstraintsException.php
Normal file
10
app/Exceptions/FormHasConstraintsException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class FormHasConstraintsException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/IncompleteApplicationException.php
Normal file
10
app/Exceptions/IncompleteApplicationException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class IncompleteApplicationException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/InvalidAppointmentException.php
Normal file
10
app/Exceptions/InvalidAppointmentException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidAppointmentException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/InvalidAppointmentStatusException.php
Normal file
10
app/Exceptions/InvalidAppointmentStatusException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidAppointmentStatusException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/InvalidGamePreferenceException.php
Normal file
10
app/Exceptions/InvalidGamePreferenceException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidGamePreferenceException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/InvalidInviteException.php
Normal file
10
app/Exceptions/InvalidInviteException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidInviteException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/OptionCategoryNotFoundException.php
Normal file
10
app/Exceptions/OptionCategoryNotFoundException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class OptionCategoryNotFoundException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/OptionNotFoundException.php
Normal file
10
app/Exceptions/OptionNotFoundException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class OptionNotFoundException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/ProfileNotFoundException.php
Normal file
10
app/Exceptions/ProfileNotFoundException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ProfileNotFoundException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/PublicTeamInviteException.php
Normal file
10
app/Exceptions/PublicTeamInviteException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class PublicTeamInviteException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/UnavailableApplicationException.php
Normal file
10
app/Exceptions/UnavailableApplicationException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class UnavailableApplicationException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
10
app/Exceptions/UserAlreadyInvitedException.php
Normal file
10
app/Exceptions/UserAlreadyInvitedException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class UserAlreadyInvitedException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
11
app/Exceptions/VacancyNotFoundException.php
Normal file
11
app/Exceptions/VacancyNotFoundException.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
|
||||
class VacancyNotFoundException extends ModelNotFoundException
|
||||
{
|
||||
//
|
||||
}
|
17
app/Facades/JSON.php
Normal file
17
app/Facades/JSON.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Facades;
|
||||
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
class JSON extends Facade
|
||||
{
|
||||
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return 'json';
|
||||
}
|
||||
|
||||
}
|
@@ -71,7 +71,7 @@ class ContextAwareValidator
|
||||
$validator = [];
|
||||
|
||||
if ($includeFormName) {
|
||||
$validator['formName'] = 'required|string|max:100';
|
||||
$validator['formName'] = 'required|string';
|
||||
}
|
||||
|
||||
foreach ($fields as $fieldName => $field) {
|
||||
|
142
app/Helpers/JSON.php
Normal file
142
app/Helpers/JSON.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
/**
|
||||
* Class JSON - Used for JSON responses.
|
||||
* @package App\Helpers
|
||||
*/
|
||||
class JSON
|
||||
{
|
||||
|
||||
protected $type, $status, $message, $code, $data, $additional;
|
||||
|
||||
/**
|
||||
* @param mixed $type
|
||||
*/
|
||||
public function setResponseType($type): JSON
|
||||
{
|
||||
$this->type = $type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $additional
|
||||
*/
|
||||
public function setAdditional($additional)
|
||||
{
|
||||
$this->additional = $additional;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAdditional()
|
||||
{
|
||||
return $this->additional;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getStatus()
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $status
|
||||
* @return JSON
|
||||
*/
|
||||
public function setStatus($status)
|
||||
{
|
||||
$this->status = $status;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMessage()
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $message
|
||||
* @return JSON
|
||||
*/
|
||||
public function setMessage($message)
|
||||
{
|
||||
$this->message = $message;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $code
|
||||
* @return JSON
|
||||
*/
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $data
|
||||
* @return JSON
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function build($headers = [])
|
||||
{
|
||||
// Uses the same structure as model resources, for consistency when they aren't used.
|
||||
$response = [
|
||||
'data' => $this->getData(),
|
||||
'meta' => [
|
||||
'status' => $this->getStatus(),
|
||||
'message' => $this->getMessage(),
|
||||
]
|
||||
];
|
||||
|
||||
if (!empty($this->additional))
|
||||
{
|
||||
foreach($this->additional as $additionalKeyName => $key)
|
||||
{
|
||||
$response[$additionalKeyName] = $key;
|
||||
}
|
||||
}
|
||||
return response($response, $this->getCode(), $headers);
|
||||
}
|
||||
|
||||
}
|
@@ -21,23 +21,48 @@
|
||||
|
||||
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;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* The options class. A simple wrapper around the model. Could be a repository, but we're not using that design pattern just yet
|
||||
*/
|
||||
class Options
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns an assortment of settings found in the mentioned category
|
||||
*
|
||||
* @param $category The category
|
||||
* @return Collection The settings in this category
|
||||
*/
|
||||
public function getCategory(string $category): Collection
|
||||
{
|
||||
$options = Option::where('option_category', $category)->get();
|
||||
if ($options->isEmpty())
|
||||
{
|
||||
throw new EmptyOptionsException('There are no options in category ' . $category);
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
|
||||
public function getOption(string $option): string
|
||||
{
|
||||
$value = Cache::get($option);
|
||||
|
||||
|
||||
if (is_null($value)) {
|
||||
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);
|
||||
Cache::put($option, $value->option_value);
|
||||
Cache::put($option.'_desc', 'Undefined description');
|
||||
|
||||
return $value->option_value;
|
||||
@@ -46,12 +71,14 @@ class Options
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function setOption(string $option, string $value, string $description)
|
||||
// Null categories are settings without categories and will appear ungrouped
|
||||
public function setOption(string $option, string $value, string $description, string $category = null)
|
||||
{
|
||||
Option::create([
|
||||
'option_name' => $option,
|
||||
'option_value' => $value,
|
||||
'friendly_name' => $description,
|
||||
'option_category' => $category
|
||||
]);
|
||||
|
||||
Cache::put($option, $value, now()->addDay());
|
||||
@@ -93,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.');
|
||||
}
|
||||
}
|
||||
|
||||
|
95
app/Http/Controllers/ApiKeyController.php
Normal file
95
app/Http/Controllers/ApiKeyController.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\ApiKey;
|
||||
use App\Http\Requests\CreateApiKeyRequest;
|
||||
use App\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class ApiKeyController extends Controller
|
||||
{
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('viewAny', ApiKey::class);
|
||||
|
||||
return view('dashboard.administration.keys')
|
||||
->with('keys', ApiKey::all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*/
|
||||
public function store(CreateApiKeyRequest $request)
|
||||
{
|
||||
$this->authorize('create', ApiKey::class);
|
||||
|
||||
$discriminator = "#" . bin2hex(random_bytes(7));
|
||||
$secret = bin2hex(random_bytes(32));
|
||||
|
||||
$key = ApiKey::create([
|
||||
'name' => $request->keyName,
|
||||
'discriminator' => $discriminator,
|
||||
'secret' => Hash::make($secret),
|
||||
'status' => 'active',
|
||||
'owner_user_id' => Auth::user()->id
|
||||
]);
|
||||
|
||||
if ($key)
|
||||
{
|
||||
$request->session()->flash('success', __('Key successfully registered!'));
|
||||
$request->session()->flash('finalKey', $discriminator . '.' . $secret);
|
||||
|
||||
return redirect()
|
||||
->back();
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', __('An error occurred whilst trying to create an API key.'));
|
||||
}
|
||||
|
||||
|
||||
public function revokeKey(Request $request, ApiKey $key)
|
||||
{
|
||||
$this->authorize('update', $key);
|
||||
|
||||
if ($key->status == 'active')
|
||||
{
|
||||
$key->status = 'disabled';
|
||||
$key->save();
|
||||
}
|
||||
else
|
||||
{
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', __('Key already revoked.'));
|
||||
}
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Key revoked. Apps using this key will stop working.'));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
$key = ApiKey::findOrFail($id);
|
||||
$this->authorize('delete', $key);
|
||||
|
||||
$key->delete();
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Key deleted successfully. Apps using this key will stop working.'));
|
||||
|
||||
}
|
||||
}
|
@@ -22,32 +22,25 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Application;
|
||||
use App\Events\ApplicationDeniedEvent;
|
||||
use App\Notifications\ApplicationMoved;
|
||||
use App\Notifications\NewApplicant;
|
||||
use App\Response;
|
||||
use App\User;
|
||||
use App\Vacancy;
|
||||
use ContextAwareValidator;
|
||||
use App\Exceptions\ApplicationNotFoundException;
|
||||
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 function canVote($votes): bool
|
||||
{
|
||||
$allvotes = collect([]);
|
||||
|
||||
foreach ($votes as $vote) {
|
||||
if ($vote->userID == Auth::user()->id) {
|
||||
$allvotes->push($vote);
|
||||
}
|
||||
}
|
||||
private $applicationService;
|
||||
|
||||
return ($allvotes->count() == 1) ? false : true;
|
||||
public function __construct(ApplicationService $applicationService) {
|
||||
|
||||
$this->applicationService = $applicationService;
|
||||
}
|
||||
|
||||
|
||||
public function showUserApps()
|
||||
{
|
||||
return view('dashboard.user.applications')
|
||||
@@ -58,7 +51,7 @@ class ApplicationController extends Controller
|
||||
{
|
||||
$this->authorize('view', $application);
|
||||
|
||||
if (! is_null($application)) {
|
||||
if (!is_null($application)) {
|
||||
return view('dashboard.user.viewapp')
|
||||
->with(
|
||||
[
|
||||
@@ -67,128 +60,88 @@ 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 {
|
||||
$request->session()->flash('error', 'The application you requested could not be found.');
|
||||
$request->session()->flash('error', __('The application you requested could not be found.'));
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
|
||||
}
|
||||
|
||||
public function showAllApps()
|
||||
public function showAllApps(Request $request)
|
||||
{
|
||||
$this->authorize('viewAny', Application::class);
|
||||
|
||||
return view('dashboard.appmanagement.all')
|
||||
->with('applications', Application::paginate(6));
|
||||
->with('applications', Application::all());
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function renderApplicationForm(Request $request, $vacancySlug)
|
||||
public function renderApplicationForm($vacancySlug)
|
||||
{
|
||||
// FIXME: Get rid of references to first(), this is a wonky query
|
||||
$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.');
|
||||
try {
|
||||
return $this->applicationService->renderForm($vacancySlug);
|
||||
}
|
||||
catch (ApplicationNotFoundException $ex) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function saveApplicationAnswers(Request $request, $vacancySlug)
|
||||
{
|
||||
$vacancy = Vacancy::with('forms')->where('vacancySlug', $vacancySlug)->get();
|
||||
try {
|
||||
|
||||
if ($vacancy->first()->vacancyCount == 0 || $vacancy->first()->vacancyStatus !== 'OPEN') {
|
||||
$request->session()->flash('error', 'This application is unavailable.');
|
||||
$this->applicationService->fillForm(Auth::user(), $request->all(), $vacancySlug);
|
||||
|
||||
return redirect()->back();
|
||||
} catch (VacancyNotFoundException | IncompleteApplicationException | UnavailableApplicationException $e) {
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $e->getMessage());
|
||||
}
|
||||
|
||||
Log::info('Processing new application!');
|
||||
|
||||
$formStructure = json_decode($vacancy->first()->forms->formStructure, true);
|
||||
$responseValidation = ContextAwareValidator::getResponseValidator($request->all(), $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 for user '.Auth::user()->name.' for vacancy '.$vacancy->first()->vacancyName);
|
||||
|
||||
$application = Application::create([
|
||||
'applicantUserID' => Auth::user()->id,
|
||||
'applicantFormResponseID' => $response->id,
|
||||
'applicationStatus' => 'STAGE_SUBMITTED',
|
||||
]);
|
||||
|
||||
Log::info('Submitted application for user '.Auth::user()->name.' with response ID'.$response->id);
|
||||
|
||||
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()->to(route('showUserApps'));
|
||||
} else {
|
||||
Log::warning('Application form for '.Auth::user()->name.' contained errors, resetting!');
|
||||
$request->session()->flash('error', 'There are one or more errors in your application. Please make sure none of your fields are empty, since they are all required.');
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
return redirect()
|
||||
->to(route('showUserApps'))
|
||||
->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)
|
||||
{
|
||||
$messageIsError = false;
|
||||
$this->authorize('update', Application::class);
|
||||
|
||||
switch ($newStatus) {
|
||||
case 'deny':
|
||||
|
||||
event(new ApplicationDeniedEvent($application));
|
||||
break;
|
||||
|
||||
case 'interview':
|
||||
Log::info('User '.Auth::user()->name.' has moved application ID '.$application->id.'to interview stage');
|
||||
$request->session()->flash('success', 'Application moved to interview stage! (:');
|
||||
$application->setStatus('STAGE_INTERVIEW');
|
||||
|
||||
$application->user->notify(new ApplicationMoved());
|
||||
break;
|
||||
|
||||
default:
|
||||
$request->session()->flash('error', 'There are no suitable statuses to update to. Do not mess with the URL.');
|
||||
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);
|
||||
|
||||
$request->session()->flash('success', 'Application deleted. Comments, appointments and responses have also been deleted.');
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Application deleted. Comments, appointments and responses have also been deleted.'));
|
||||
|
||||
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 @ '.$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);
|
||||
|
||||
// NOTE: This is a little confusing, refactor
|
||||
$application->appointment->appointmentStatus = (in_array($status, $validStatuses)) ? strtoupper($status) : 'SCHEDULED';
|
||||
$application->appointment->save();
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __("Interview finished! Staff members can now vote on it."));
|
||||
|
||||
$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');
|
||||
|
||||
$application->appointment->meetingNotes = $request->noteText;
|
||||
$application->appointment->save();
|
||||
|
||||
$request->session()->flash('success', 'Meeting notes have been saved.');
|
||||
} else {
|
||||
$request->session()->flash('error', 'There\'s no appointment to save notes to!');
|
||||
}
|
||||
catch (InvalidAppointmentStatusException $ex) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
|
||||
}
|
||||
|
||||
public function saveNotes(SaveNotesRequest $request, Application $application)
|
||||
{
|
||||
try {
|
||||
|
||||
$this->meetingNoteService->addToApplication($application, $request->noteText);
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', 'Saved notes.');
|
||||
|
||||
} catch (InvalidAppointmentException $ex) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ use App\Http\Controllers\Controller;
|
||||
use App\User;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
@@ -77,4 +78,19 @@ class LoginController extends Controller
|
||||
|
||||
return $this->originalAttemptLogin($request);
|
||||
}
|
||||
|
||||
public function authenticated(Request $request, User $user)
|
||||
{
|
||||
if (!config('demo.is_enabled')) {
|
||||
if ($user->originalIP !== $request->ip())
|
||||
{
|
||||
Log::alert('User IP address changed from last login. Updating.', [
|
||||
'prev' => $user->originalIP,
|
||||
'new' => $request->ip()
|
||||
]);
|
||||
$user->originalIP = $request->ip();
|
||||
$user->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ namespace App\Http\Controllers\Auth;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Profile;
|
||||
use App\User;
|
||||
use App\Facades\Options;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
@@ -81,11 +82,30 @@ class RegisterController extends Controller
|
||||
*/
|
||||
protected function validator(array $data)
|
||||
{
|
||||
$password = ['required', 'string', 'confirmed'];
|
||||
|
||||
switch (Options::getOption('pw_security_policy'))
|
||||
{ // this could be better structured, switch doesn't feel right
|
||||
case 'off':
|
||||
$password = ['required', 'string', 'confirmed'];
|
||||
break;
|
||||
case 'low':
|
||||
$password = ['required', 'string', 'min:10', 'confirmed'];
|
||||
break;
|
||||
|
||||
case 'medium':
|
||||
$password = ['required', 'string', 'confirmed', 'regex:/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[#?!@$%^&*-]).{12,}$/'];
|
||||
break;
|
||||
|
||||
case 'high':
|
||||
$password = ['required', 'string', 'confirmed', 'regex:/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{20,}$/'];
|
||||
}
|
||||
|
||||
return Validator::make($data, [
|
||||
'uuid' => ['required', 'string', 'unique:users', 'min:32', 'max:32'],
|
||||
'uuid' => (Options::getOption('requireGameLicense') && Options::getOption('currentGame') == 'MINECRAFT') ? ['required', 'string', 'unique:users', 'min:32', 'max:32'] : ['nullable', 'string'],
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
|
||||
'password' => ['required', 'string', 'min:10', 'confirmed', 'regex:/^.*(?=.{3,})(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[\d\x])(?=.*[!$#%]).*$/'],
|
||||
'password' => $password,
|
||||
], [
|
||||
'uuid.required' => 'Please enter a valid (and Premium) Minecraft username! We do not support cracked users.',
|
||||
]);
|
||||
@@ -104,11 +124,11 @@ class RegisterController extends Controller
|
||||
'name' => $data['name'],
|
||||
'email' => $data['email'],
|
||||
'password' => Hash::make($data['password']),
|
||||
'originalIP' => request()->ip(),
|
||||
'originalIP' => config('demo.is_enabled') ? '0.0.0.0' : request()->ip(),
|
||||
]);
|
||||
|
||||
// It's not the registration controller's concern to create a profile for the user,
|
||||
// so this code has been moved to it's respective observer, following the separation of concerns pattern.
|
||||
// so this code has been moved to its respective observer, following the separation of concerns pattern.
|
||||
|
||||
$user->assignRole('user');
|
||||
|
||||
|
@@ -24,58 +24,41 @@ namespace App\Http\Controllers;
|
||||
use App\Ban;
|
||||
use App\Events\UserBannedEvent;
|
||||
use App\Http\Requests\BanUserRequest;
|
||||
use App\Services\AccountSuspensionService;
|
||||
use App\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class BanController extends Controller
|
||||
{
|
||||
|
||||
protected $suspensionService;
|
||||
|
||||
public function __construct(AccountSuspensionService $suspensionService)
|
||||
{
|
||||
// Inject the service via DI
|
||||
$this->suspensionService = $suspensionService;
|
||||
}
|
||||
|
||||
public function insert(BanUserRequest $request, User $user)
|
||||
{
|
||||
if (config('demo.is_enabled')) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
|
||||
$this->authorize('create', [Ban::class, $user]);
|
||||
|
||||
if (is_null($user->bans)) {
|
||||
$reason = $request->reason;
|
||||
$duration = strtolower($request->durationOperator);
|
||||
$durationOperand = $request->durationOperand;
|
||||
|
||||
$expiryDate = now();
|
||||
if (!$this->suspensionService->isSuspended($user)) {
|
||||
|
||||
if (! empty($duration)) {
|
||||
switch ($duration) {
|
||||
case 'days':
|
||||
$expiryDate->addDays($durationOperand);
|
||||
break;
|
||||
$this->suspensionService->suspend($request->reason, $request->duration, $user, $request->suspensionType);
|
||||
$request->session()->flash('success', __('Account suspended.'));
|
||||
|
||||
case 'weeks':
|
||||
$expiryDate->addWeeks($durationOperand);
|
||||
break;
|
||||
|
||||
case 'months':
|
||||
$expiryDate->addMonths($durationOperand);
|
||||
break;
|
||||
|
||||
case 'years':
|
||||
$expiryDate->addYears($durationOperand);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Essentially permanent
|
||||
$expiryDate->addYears(5);
|
||||
}
|
||||
|
||||
$ban = Ban::create([
|
||||
'userID' => $user->id,
|
||||
'reason' => $reason,
|
||||
'bannedUntil' => $expiryDate->format('Y-m-d H:i:s'),
|
||||
'userAgent' => 'Unknown',
|
||||
'authorUserID' => Auth::user()->id,
|
||||
]);
|
||||
|
||||
event(new UserBannedEvent($user, $ban));
|
||||
$request->session()->flash('success', 'User banned successfully! Ban ID: #'.$ban->id);
|
||||
} else {
|
||||
$request->session()->flash('error', 'User already banned!');
|
||||
|
||||
$request->session()->flash('error', __('Account already suspended!'));
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
@@ -83,13 +66,21 @@ class BanController extends Controller
|
||||
|
||||
public function delete(Request $request, User $user)
|
||||
{
|
||||
if (config('demo.is_enabled')) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
|
||||
$this->authorize('delete', $user->bans);
|
||||
|
||||
if (! is_null($user->bans)) {
|
||||
$user->bans->delete();
|
||||
$request->session()->flash('success', 'User unbanned successfully!');
|
||||
if ($this->suspensionService->isSuspended($user)) {
|
||||
|
||||
$this->suspensionService->unsuspend($user);
|
||||
$request->session()->flash('success', __('Account unsuspended successfully!'));
|
||||
|
||||
} else {
|
||||
$request->session()->flash('error', 'This user isn\'t banned!');
|
||||
$request->session()->flash('error', __('This account isn\'t suspended!'));
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
|
@@ -24,30 +24,27 @@ 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
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
//
|
||||
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! (:');
|
||||
$request->session()->flash('success', __('Comment posted!'));
|
||||
} else {
|
||||
$request->session()->flash('error', 'Something went wrong while posting your comment!');
|
||||
$request->session()->flash('error', __('Something went wrong while posting your comment!'));
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
@@ -56,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 {
|
||||
|
||||
$challenge = $request->input('captcha');
|
||||
$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();
|
||||
} catch (FailedCaptchaException $ex) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
}
|
||||
|
||||
foreach (User::all() as $user) {
|
||||
if ($user->hasRole('admin')) {
|
||||
$user->notify(new NewContact(collect([
|
||||
'message' => $msg,
|
||||
'ip' => $request->ip(),
|
||||
'email' => $email,
|
||||
])));
|
||||
}
|
||||
}
|
||||
|
||||
$request->session()->flash('success', 'Message sent successfully! We usually respond within 48 hours.');
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
|
@@ -24,22 +24,38 @@ namespace App\Http\Controllers;
|
||||
use App\Application;
|
||||
use App\User;
|
||||
use App\Vacancy;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
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();
|
||||
$totalNewApplications = Application::where('applicationStatus', 'STAGE_SUBMITTED')->get()->count();
|
||||
$totalDenied = Application::where('applicationStatus', 'DENIED')->get()->count();
|
||||
$vacancies = Vacancy::where('vacancyStatus', '<>', 'CLOSED')->get();
|
||||
|
||||
$totalDeniedSingle = Application::where([
|
||||
['applicationStatus', '=', 'DENIED'],
|
||||
['applicantUserID', '=', Auth::user()->id]
|
||||
])->get();
|
||||
|
||||
$totalNewSingle = Application::where([
|
||||
['applicationStatus', '=', 'STAGE_SUBMITTED'],
|
||||
['applicantUserID', '=', Auth::user()->id]
|
||||
])->get();
|
||||
|
||||
return view('dashboard.dashboard')
|
||||
->with([
|
||||
'vacancies' => Vacancy::all(),
|
||||
'vacancies' => $vacancies,
|
||||
'totalUserCount' => User::all()->count(),
|
||||
'totalDenied' => $totalDenied,
|
||||
'totalPeerReview' => $totalPeerReview,
|
||||
'totalNewApplications' => $totalNewApplications,
|
||||
'totalNewSingle' => $totalNewSingle->count(),
|
||||
'totalDeniedSingle' => $totalDeniedSingle->count()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ class DevToolsController extends Controller
|
||||
protected function isolatedAuthorise()
|
||||
{
|
||||
if (! Auth::user()->can('admin.developertools.use')) {
|
||||
abort(403, 'You\'re not authorized to access this page.');
|
||||
abort(403, __('You\'re not authorized to access this page.'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,9 +52,9 @@ class DevToolsController extends Controller
|
||||
if (! is_null($application)) {
|
||||
event(new ApplicationApprovedEvent($application));
|
||||
|
||||
$request->session()->flash('success', 'Event dispatched! Please check the debug logs for more info');
|
||||
$request->session()->flash('success', __('Event dispatched! Please check the debug logs for more info'));
|
||||
} else {
|
||||
$request->session()->flash('error', 'Application doesn\'t exist!');
|
||||
$request->session()->flash('error', __('Application doesn\'t exist!'));
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
|
@@ -21,12 +21,21 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Exceptions\EmptyFormException;
|
||||
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,58 +54,46 @@ class FormController extends Controller
|
||||
|
||||
public function saveForm(Request $request)
|
||||
{
|
||||
$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'));
|
||||
try {
|
||||
$form = $this->formService->addForm($request->all());
|
||||
}
|
||||
catch (EmptyFormException $ex)
|
||||
{
|
||||
return redirect()
|
||||
->back()
|
||||
->with('exception', $ex->getMessage());
|
||||
}
|
||||
|
||||
$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'));
|
||||
// Form is boolean or array
|
||||
if ($form)
|
||||
{
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Form created!'));
|
||||
}
|
||||
|
||||
$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);
|
||||
$deletable = true;
|
||||
try {
|
||||
|
||||
$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)
|
||||
@@ -122,22 +119,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,50 +47,59 @@ class OptionsController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// TODO: Obtain this from the facade
|
||||
$options = Option::all();
|
||||
|
||||
// TODO: Replace with settings package
|
||||
return view('dashboard.administration.settings')
|
||||
->with('options', $options);
|
||||
->with([
|
||||
'options' => Options::getCategory('notifications'),
|
||||
'security' => [ // We could use the method above, but we need to set these names here for greater control in the template. This would nto be feasible for many options, we'd need to use a loop and the category method.
|
||||
'secPolicy' => Options::getOption('pw_security_policy'),
|
||||
'graceperiod' => Options::getOption('graceperiod'),
|
||||
'pwExpiry' => Options::getOption('password_expiry'),
|
||||
'requiresPMC' => Options::getOption('requireGameLicense'),
|
||||
'enforce2fa' => Options::getOption('force2fa')
|
||||
],
|
||||
'currentGame' => Options::getOption('currentGame')
|
||||
]);
|
||||
}
|
||||
|
||||
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);
|
||||
try {
|
||||
|
||||
$errorCond = true;
|
||||
$request->session()->flash('error', 'An error occurred while trying to save settings: '.$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)
|
||||
{
|
||||
try {
|
||||
|
||||
$this->configurationService->saveGameIntegration($request->gamePref);
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Game preference updated.'));
|
||||
|
||||
} 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);
|
||||
@@ -71,6 +78,17 @@ class ProfileController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$suspensionInfo = null;
|
||||
if ($user->isBanned())
|
||||
{
|
||||
$suspensionInfo = [
|
||||
|
||||
'isPermanent' => $user->bans->isPermanent,
|
||||
'reason' => $user->bans->reason,
|
||||
'bannedUntil' => $user->bans->bannedUntil
|
||||
];
|
||||
}
|
||||
|
||||
if (Auth::user()->is($user) || Auth::user()->can('profiles.view.others')) {
|
||||
return view('dashboard.user.profile.displayprofile')
|
||||
->with([
|
||||
@@ -82,44 +100,18 @@ class ProfileController extends Controller
|
||||
'since' => $createdDate->englishMonth.' '.$createdDate->year,
|
||||
'ipInfo' => IP::lookup($user->originalIP),
|
||||
'roles' => $roleList,
|
||||
'suspensionInfo' => $suspensionInfo
|
||||
]);
|
||||
} else {
|
||||
abort(403, 'You cannot view someone else\'s profile.');
|
||||
abort(403, __('You cannot view someone else\'s profile.'));
|
||||
}
|
||||
}
|
||||
|
||||
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.'));
|
||||
}
|
||||
}
|
||||
|
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright © 2020 Miguel Nogueira
|
||||
*
|
||||
* This file is part of Raspberry Staff Manager.
|
||||
*
|
||||
* Raspberry Staff Manager is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Raspberry Staff Manager is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Raspberry Staff Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
class ResponseController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
35
app/Http/Controllers/SecuritySettingsController.php
Normal file
35
app/Http/Controllers/SecuritySettingsController.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
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;
|
||||
|
||||
use function PHPSTORM_META\map;
|
||||
|
||||
class SecuritySettingsController extends Controller
|
||||
{
|
||||
private $securityService;
|
||||
|
||||
public function __construct(SecuritySettingsService $securityService) {
|
||||
$this->securityService = $securityService;
|
||||
}
|
||||
|
||||
public function save(SaveSecuritySettings $request)
|
||||
{
|
||||
$this->securityService->save($request->secPolicy, [
|
||||
'graceperiod' => $request->graceperiod,
|
||||
'pwExpiry' => $request->pwExpiry,
|
||||
'enforce2fa' => $request->enforce2fa,
|
||||
'requirePMC' => $request->requirePMC
|
||||
]);
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Settings saved.'));
|
||||
|
||||
}
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright © 2020 Miguel Nogueira
|
||||
*
|
||||
* This file is part of Raspberry Staff Manager.
|
||||
*
|
||||
* Raspberry Staff Manager is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Raspberry Staff Manager is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Raspberry Staff Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
class StaffProfileController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
@@ -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,14 +42,19 @@ 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\Http\Response
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->authorize('index');
|
||||
$this->authorize('index', Team::class);
|
||||
|
||||
$teams = Team::with('users.roles')->get();
|
||||
|
||||
@@ -55,61 +65,61 @@ class TeamController extends Controller
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
* @param NewTeamRequest $request
|
||||
* @return RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function store(NewTeamRequest $request)
|
||||
{
|
||||
$this->authorize('create');
|
||||
$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.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
* @param Team $team
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\Response
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function edit(Team $team)
|
||||
{
|
||||
$this->authorize('update', $team);
|
||||
|
||||
return view('dashboard.teams.edit-team')
|
||||
->with('team', $team)
|
||||
->with('users', User::all())
|
||||
->with('vacancies', Vacancy::with('teams')->get()->all());
|
||||
->with([
|
||||
'team' => $team,
|
||||
'users' => User::all(),
|
||||
'vacancies' => Vacancy::with('teams')->get()->all()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
* @param EditTeamRequest $request
|
||||
* @param Team $team
|
||||
* @return RedirectResponse
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function update(EditTeamRequest $request, Team $team)
|
||||
public function update(EditTeamRequest $request, Team $team): RedirectResponse
|
||||
{
|
||||
$this->authorize('update', $team);
|
||||
$team = $this->teamService->updateTeam($team, $request->teamDescription, $request->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.'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,124 +130,70 @@ class TeamController extends Controller
|
||||
*/
|
||||
public function destroy($id)
|
||||
{
|
||||
//
|
||||
// wip
|
||||
}
|
||||
|
||||
public function invite(SendInviteRequest $request, Team $team)
|
||||
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.');
|
||||
}
|
||||
} else {
|
||||
$request->session()->flash('error', 'You can\'t invite users to public teams.');
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('User invited successfully!'));
|
||||
|
||||
} catch (UserAlreadyInvitedException | PublicTeamInviteException $ex) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $ex->getMessage());
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function processInviteAction(Request $request, $action, $token)
|
||||
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 '.$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)
|
||||
public function switchTeam(Request $request, Team $team): RedirectResponse
|
||||
{
|
||||
$this->authorize('switchTeam', $team);
|
||||
|
||||
try {
|
||||
Auth::user()->switchTeam($team);
|
||||
|
||||
$request->session()->flash('success', 'Switched teams! Your team dashboard will now use this context.');
|
||||
$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.');
|
||||
$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)
|
||||
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,15 +26,20 @@ 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)
|
||||
{
|
||||
$this->authorize('index');
|
||||
$this->authorize('index', TeamFile::class);
|
||||
|
||||
if (is_null(Auth::user()->currentTeam))
|
||||
{
|
||||
@@ -53,41 +60,39 @@ class TeamFileController extends Controller
|
||||
*/
|
||||
public function store(UploadFileRequest $request)
|
||||
{
|
||||
$this->authorize('store');
|
||||
$this->authorize('store', TeamFile::class);
|
||||
|
||||
$upload = $request->file('file');
|
||||
|
||||
$file = $upload->store('uploads');
|
||||
$originalFileName = $upload->getClientOriginalName();
|
||||
$originalFileExtension = $upload->extension();
|
||||
$originalFileSize = $upload->getSize();
|
||||
|
||||
$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
|
||||
]);
|
||||
|
||||
if ($fileEntry && !is_bool($file))
|
||||
if (config('demo.is_enabled'))
|
||||
{
|
||||
$request->session()->flash('success', 'File uploaded successfully!');
|
||||
return redirect()->back();
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
|
||||
$request->session()->flash('error', 'There was an unknown error whilst trying to upload your file.');
|
||||
return redirect()->back();
|
||||
try {
|
||||
$caption = $request->caption;
|
||||
$description = $request->description;
|
||||
|
||||
$this->fileService->addFile($request->file('file'), Auth::user()->id, Auth::user()->currentTeam->id, $caption, $description);
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('File uploaded successfully.'));
|
||||
|
||||
} catch (FileUploadException $uploadException) {
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', $uploadException->getMessage());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function download(Request $request, TeamFile $teamFile)
|
||||
{
|
||||
$this->authorize('download');
|
||||
$this->authorize('download', TeamFile::class);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -101,29 +106,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.
|
||||
*
|
||||
@@ -133,18 +115,25 @@ class TeamFileController extends Controller
|
||||
*/
|
||||
public function destroy(Request $request, TeamFile $teamFile)
|
||||
{
|
||||
$this->authorize('delete');
|
||||
$this->authorize('delete', $teamFile);
|
||||
|
||||
if (config('demo.is_enabled'))
|
||||
{
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Storage::delete($teamFile->fs_location);
|
||||
$teamFile->delete();
|
||||
|
||||
$request->session()->flash('success', 'File deleted successfully.');
|
||||
$request->session()->flash('success', __('File deleted successfully.'));
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
$request->session()->flash('error', 'There was an error deleting the file: ' . $ex->getMessage());
|
||||
$request->session()->flash('error', __('There was an error deleting the file: :msg', ['msg' => $ex->getMessage()]));
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
|
@@ -32,6 +32,7 @@ use App\Http\Requests\SearchPlayerRequest;
|
||||
use App\Http\Requests\UpdateUserRequest;
|
||||
use App\Notifications\ChangedPassword;
|
||||
use App\Notifications\EmailChanged;
|
||||
use App\Traits\DisablesFeatures;
|
||||
use App\Traits\ReceivesAccountTokens;
|
||||
use App\User;
|
||||
use Google2FA;
|
||||
@@ -110,7 +111,7 @@ class UserController extends Controller
|
||||
->get();
|
||||
|
||||
if (! $matchingUsers->isEmpty()) {
|
||||
$request->session()->flash('success', 'There were '.$matchingUsers->count().' user(s) matching your search.');
|
||||
$request->session()->flash('success', __('There were :usersCount user(s) matching your search.', ['usersCount' => $matchingUsers->count()]));
|
||||
|
||||
return view('dashboard.administration.players')
|
||||
->with([
|
||||
@@ -118,7 +119,7 @@ class UserController extends Controller
|
||||
'bannedUserCount' => Ban::all()->count(),
|
||||
]);
|
||||
} else {
|
||||
$request->session()->flash('error', 'Your search term did not return any results.');
|
||||
$request->session()->flash('error', __('Your search term did not return any results.'));
|
||||
|
||||
return redirect(route('registeredPlayerList'));
|
||||
}
|
||||
@@ -161,17 +162,24 @@ class UserController extends Controller
|
||||
'timestamp' => now(),
|
||||
]);
|
||||
|
||||
$request->session()->flash('success', 'Successfully logged out other devices. Remember to change your password if you think you\'ve been compromised.');
|
||||
$request->session()->flash('success', __('Successfully logged out other devices. Remember to change your password if you think you\'ve been compromised.'));
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function changePassword(ChangePasswordRequest $request)
|
||||
{
|
||||
if (config('demo.is_enabled')) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
$user = User::find(Auth::user()->id);
|
||||
|
||||
if (! is_null($user)) {
|
||||
$user->password = Hash::make($request->newPassword);
|
||||
$user->password_last_updated = now();
|
||||
|
||||
$user->save();
|
||||
|
||||
Log::info('User '.$user->name.' has changed their password', [
|
||||
@@ -189,6 +197,12 @@ class UserController extends Controller
|
||||
|
||||
public function changeEmail(ChangeEmailRequest $request)
|
||||
{
|
||||
if (config('demo.is_enabled')) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
|
||||
$user = User::find(Auth::user()->id);
|
||||
|
||||
if (! is_null($user)) {
|
||||
@@ -202,9 +216,9 @@ class UserController extends Controller
|
||||
]);
|
||||
$user->notify(new EmailChanged());
|
||||
|
||||
$request->session()->flash('success', 'Your email address has been changed!');
|
||||
$request->session()->flash('success', __('Your email address has been changed!'));
|
||||
} else {
|
||||
$request->session()->flash('error', 'There has been an error whilst trying to update your account. Please contact administrators.');
|
||||
$request->session()->flash('error', __('There has been an error whilst trying to update your account. Please contact administrators.'));
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
@@ -212,13 +226,19 @@ class UserController extends Controller
|
||||
|
||||
public function delete(DeleteUserRequest $request, User $user)
|
||||
{
|
||||
if (config('demo.is_enabled')) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
|
||||
$this->authorize('delete', $user);
|
||||
|
||||
if ($request->confirmPrompt == 'DELETE ACCOUNT') {
|
||||
$user->forceDelete();
|
||||
$request->session()->flash('success', 'User deleted successfully. PII has been erased.');
|
||||
$request->session()->flash('success', __('User deleted successfully.'));
|
||||
} else {
|
||||
$request->session()->flash('error', 'Wrong confirmation text! Try again.');
|
||||
$request->session()->flash('error', __('Wrong confirmation text! Try again.'));
|
||||
}
|
||||
|
||||
return redirect()->route('registeredPlayerList');
|
||||
@@ -226,6 +246,11 @@ class UserController extends Controller
|
||||
|
||||
public function update(UpdateUserRequest $request, User $user)
|
||||
{
|
||||
if (config('demo.is_enabled')) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
$this->authorize('adminEdit', $user);
|
||||
|
||||
// Mass update would not be possible here without extra code, making route model binding useless
|
||||
@@ -234,8 +259,8 @@ class UserController extends Controller
|
||||
$user->uuid = $request->uuid;
|
||||
|
||||
$existingRoles = Role::all()
|
||||
->pluck('name')
|
||||
->all();
|
||||
->pluck('name')
|
||||
->all();
|
||||
|
||||
$roleDiff = array_diff($existingRoles, $request->roles);
|
||||
|
||||
@@ -253,13 +278,19 @@ class UserController extends Controller
|
||||
}
|
||||
|
||||
$user->save();
|
||||
$request->session()->flash('success', 'User updated successfully!');
|
||||
$request->session()->flash('success', __('User updated successfully!'));
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function add2FASecret(Add2FASecretRequest $request)
|
||||
{
|
||||
if (config('demo.is_enabled')) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
|
||||
$currentSecret = $request->session()->get('current2FA');
|
||||
$isValid = Google2FA::verifyKey($currentSecret, $request->otp);
|
||||
|
||||
@@ -285,9 +316,9 @@ class UserController extends Controller
|
||||
$request->session()->forget('twofaAttemptFailed');
|
||||
}
|
||||
|
||||
$request->session()->flash('success', '2FA succesfully enabled! You\'ll now be prompted for an OTP each time you log in.');
|
||||
$request->session()->flash('success', __('2FA succesfully enabled! You\'ll now be prompted for an OTP each time you log in.'));
|
||||
} else {
|
||||
$request->session()->flash('error', 'Incorrect code. Please reopen the 2FA settings panel and try again.');
|
||||
$request->session()->flash('error', __('Incorrect code. Please reopen the 2FA settings panel and try again.'));
|
||||
$request->session()->put('twofaAttemptFailed', true);
|
||||
}
|
||||
|
||||
@@ -304,7 +335,7 @@ class UserController extends Controller
|
||||
$request->user()->twofa_secret = null;
|
||||
$request->user()->save();
|
||||
|
||||
$request->session()->flash('success', 'Two-factor authentication disabled.');
|
||||
$request->session()->flash('success', __('Two-factor authentication disabled.'));
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
@@ -312,10 +343,15 @@ class UserController extends Controller
|
||||
public function terminate(Request $request, User $user)
|
||||
{
|
||||
$this->authorize('terminate', User::class);
|
||||
if (config('demo.is_enabled')) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
|
||||
// TODO: move logic to policy
|
||||
if (! $user->isStaffMember() || $user->is(Auth::user())) {
|
||||
$request->session()->flash('error', 'You cannot terminate this user.');
|
||||
$request->session()->flash('error', __('You cannot terminate this user.'));
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
@@ -329,7 +365,7 @@ class UserController extends Controller
|
||||
}
|
||||
|
||||
Log::info('User '.$user->name.' has just been demoted.');
|
||||
$request->session()->flash('success', 'User terminated successfully.');
|
||||
$request->session()->flash('success', __('User terminated successfully.'));
|
||||
|
||||
//TODO: Dispatch event
|
||||
return redirect()->back();
|
||||
|
@@ -21,6 +21,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Facades\JSON;
|
||||
use App\Form;
|
||||
use App\Http\Requests\VacancyEditRequest;
|
||||
use App\Http\Requests\VacancyRequest;
|
||||
@@ -45,7 +46,11 @@ class VacancyController extends Controller
|
||||
|
||||
public function store(VacancyRequest $request)
|
||||
{
|
||||
$messageIsError = false;
|
||||
$this->authorize('create', Vacancy::class);
|
||||
|
||||
|
||||
|
||||
$form = Form::find($request->vacancyFormID);
|
||||
|
||||
if (! is_null($form)) {
|
||||
@@ -67,12 +72,16 @@ class VacancyController extends Controller
|
||||
|
||||
]);
|
||||
|
||||
$request->session()->flash('success', 'Vacancy successfully opened. It will now show in the home page.');
|
||||
$message = __('Vacancy successfully opened. It will now show in the home page.');
|
||||
|
||||
} else {
|
||||
$request->session()->flash('error', 'You cannot create a vacancy without a valid form.');
|
||||
$message = __('You cannot create a vacancy without a valid form.');
|
||||
$messageIsError = true;
|
||||
}
|
||||
|
||||
return redirect()->back();
|
||||
return redirect()
|
||||
->back()
|
||||
->with(($messageIsError) ? 'error' : 'success', $message);
|
||||
}
|
||||
|
||||
public function updatePositionAvailability(Request $request, $status, Vacancy $vacancy)
|
||||
@@ -85,13 +94,13 @@ class VacancyController extends Controller
|
||||
switch ($status) {
|
||||
case 'open':
|
||||
$vacancy->open();
|
||||
$message = 'Position successfully opened!';
|
||||
$message = __('Position successfully opened!');
|
||||
|
||||
break;
|
||||
|
||||
case 'close':
|
||||
$vacancy->close();
|
||||
$message = 'Position successfully closed!';
|
||||
$message = __('Position successfully closed!');
|
||||
|
||||
foreach (User::all() as $user) {
|
||||
if ($user->isStaffMember()) {
|
||||
@@ -101,18 +110,19 @@ class VacancyController extends Controller
|
||||
break;
|
||||
|
||||
default:
|
||||
$message = "Please do not tamper with the button's URLs. To report a bug, please contact an administrator.";
|
||||
$message = __("Please do not tamper with the URLs. To report a bug, please contact an administrator.");
|
||||
$type = 'error';
|
||||
|
||||
}
|
||||
} else {
|
||||
$message = "The position you're trying to update doesn't exist!";
|
||||
$message = __("The position you're trying to update doesn't exist!");
|
||||
$type = 'error';
|
||||
}
|
||||
|
||||
$request->session()->flash($type, $message);
|
||||
return redirect()
|
||||
->back()
|
||||
->with($type, $message);
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function edit(Request $request, Vacancy $vacancy)
|
||||
@@ -127,14 +137,14 @@ class VacancyController extends Controller
|
||||
{
|
||||
$this->authorize('update', $vacancy);
|
||||
|
||||
$vacancy->vacancyFullDescription = $request->vacancyFullDescription;
|
||||
$vacancy->vacancyFullDescription = $request->vacancyFullDescription;
|
||||
$vacancy->vacancyDescription = $request->vacancyDescription;
|
||||
$vacancy->vacancyCount = $request->vacancyCount;
|
||||
|
||||
$vacancy->save();
|
||||
|
||||
$request->session()->flash('success', 'Vacancy successfully updated.');
|
||||
|
||||
return redirect()->back();
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', __('Vacancy successfully updated.'));
|
||||
}
|
||||
}
|
||||
|
@@ -42,7 +42,7 @@ class VoteController extends Controller
|
||||
Log::info('User '.Auth::user()->name.' has voted in applicant '.$application->user->name.'\'s application', [
|
||||
'voteType' => $voteRequest->voteType,
|
||||
]);
|
||||
$voteRequest->session()->flash('success', 'Your vote has been registered!');
|
||||
$voteRequest->session()->flash('success', __('Your vote has been counted!'));
|
||||
|
||||
// Cron job will run command that processes votes
|
||||
return redirect()->back();
|
||||
|
@@ -21,6 +21,7 @@
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use App\Http\Middleware\APIAuthenticationMiddleware;
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
@@ -60,6 +61,7 @@ class Kernel extends HttpKernel
|
||||
'api' => [
|
||||
'throttle:60,1',
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
APIAuthenticationMiddleware::class
|
||||
],
|
||||
];
|
||||
|
||||
@@ -85,6 +87,8 @@ class Kernel extends HttpKernel
|
||||
'usernameUUID' => \App\Http\Middleware\UsernameUUID::class,
|
||||
'forcelogout' => \App\Http\Middleware\ForceLogoutMiddleware::class,
|
||||
'2fa' => \PragmaRX\Google2FALaravel\Middleware::class,
|
||||
'passwordexpiration' => \App\Http\Middleware\PasswordExpirationMiddleware::class,
|
||||
'passwordredirect' => \App\Http\Middleware\PasswordExpirationRedirectMiddleware::class,
|
||||
'localize' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRoutes::class,
|
||||
'localizationRedirect' => \Mcamara\LaravelLocalization\Middleware\LaravelLocalizationRedirectFilter::class,
|
||||
'localeSessionRedirect' => \Mcamara\LaravelLocalization\Middleware\LocaleSessionRedirect::class,
|
||||
|
65
app/Http/Middleware/APIAuthenticationMiddleware.php
Normal file
65
app/Http/Middleware/APIAuthenticationMiddleware.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\ApiKey;
|
||||
use App\Facades\JSON;
|
||||
use Carbon\Carbon;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class APIAuthenticationMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$key = $request->bearerToken();
|
||||
|
||||
if (!is_null($key))
|
||||
{
|
||||
// we have a valid discriminator
|
||||
$discriminator = Str::before($key, '.');
|
||||
$loneKey = Str::after($key, '.');
|
||||
|
||||
$keyRecord = ApiKey::where('discriminator', $discriminator)->first();
|
||||
|
||||
if ($keyRecord && Hash::check($loneKey, $keyRecord->secret) && $keyRecord->status == 'active')
|
||||
{
|
||||
$keyRecord->last_used = Carbon::now();
|
||||
$keyRecord->save();
|
||||
|
||||
Log::info('Recording API call, see context', [
|
||||
'uri' => $request->url(),
|
||||
'name' => Route::currentRouteName(),
|
||||
'discriminator' => $discriminator,
|
||||
'ip' => $request->ip()
|
||||
]);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
return JSON::setResponseType('error')
|
||||
->setStatus('authfail')
|
||||
->setMessage('Invalid / Revoked API key.')
|
||||
->setCode(401)
|
||||
->build();
|
||||
}
|
||||
|
||||
return JSON::setResponseType('error')
|
||||
->setStatus('malformed_key')
|
||||
->setMessage('Missing or malformed API key.')
|
||||
->setCode(400)
|
||||
->build();
|
||||
|
||||
}
|
||||
}
|
21
app/Http/Middleware/IPHistoryMiddleware.php
Normal file
21
app/Http/Middleware/IPHistoryMiddleware.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class IPHistoryMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
return $next($request);
|
||||
}
|
||||
}
|
40
app/Http/Middleware/PasswordExpirationMiddleware.php
Normal file
40
app/Http/Middleware/PasswordExpirationMiddleware.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Facades\Options;
|
||||
use Carbon\Carbon;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class PasswordExpirationMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if(Auth::check())
|
||||
{
|
||||
$sinceUpdate = Carbon::parse(Auth::user()->password_last_updated)->diffInDays(now());
|
||||
$updateThreshold = Options::getOption('password_expiry');
|
||||
|
||||
if ($updateThreshold !== 0 && $sinceUpdate > $updateThreshold)
|
||||
{
|
||||
session()->put('passwordExpired', true);
|
||||
}
|
||||
else
|
||||
{
|
||||
session()->put('passwordExpired', false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
28
app/Http/Middleware/PasswordExpirationRedirectMiddleware.php
Normal file
28
app/Http/Middleware/PasswordExpirationRedirectMiddleware.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class PasswordExpirationRedirectMiddleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (Auth::check() && session('passwordExpired'))
|
||||
{
|
||||
// WARNING!! Routes under the profile group must not have this middleware, because it'll result in an infinite redirect loop.
|
||||
return redirect(route('showAccountSettings'));
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
@@ -45,8 +45,15 @@ class BanUserRequest extends FormRequest
|
||||
{
|
||||
return [
|
||||
'reason' => 'required|string',
|
||||
'durationOperand' => 'nullable|string',
|
||||
'durationOperator' => 'nullable|string',
|
||||
'suspensionType' => 'required|string',
|
||||
'duration' => 'required_if:suspensionType,on|nullable|integer',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'duration.required_if' => __('You must provide a duration if the suspension is temporary.')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
30
app/Http/Requests/CreateApiKeyRequest.php
Normal file
30
app/Http/Requests/CreateApiKeyRequest.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CreateApiKeyRequest 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 [
|
||||
'keyName' => 'required|string'
|
||||
];
|
||||
}
|
||||
}
|
34
app/Http/Requests/SaveSecuritySettings.php
Normal file
34
app/Http/Requests/SaveSecuritySettings.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class SaveSecuritySettings 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 [
|
||||
'secPolicy' => 'required|string',
|
||||
'graceperiod' => 'required|integer',
|
||||
'pwExpiry' => 'required|integer',
|
||||
'enforce2fa' => 'required|boolean',
|
||||
'requirePMC' => 'required|boolean'
|
||||
];
|
||||
}
|
||||
}
|
28
app/Http/Resources/ApplicationResource.php
Normal file
28
app/Http/Resources/ApplicationResource.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Response;
|
||||
use App\User;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ApplicationResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'applicationStatus' => $this->applicationStatus,
|
||||
'applicant' => new UserResource(User::findOrFail($this->applicantUserID)),
|
||||
'response' => new ResponseResource(Response::findOrFail($this->applicantFormResponseID)),
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at
|
||||
];
|
||||
}
|
||||
}
|
19
app/Http/Resources/AppointmentResource.php
Normal file
19
app/Http/Resources/AppointmentResource.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class AppointmentResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
19
app/Http/Resources/BanResource.php
Normal file
19
app/Http/Resources/BanResource.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class BanResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
26
app/Http/Resources/FormResource.php
Normal file
26
app/Http/Resources/FormResource.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class FormResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'formName' => $this->formName,
|
||||
'formStructure' => json_decode($this->formStructure),
|
||||
'formStatus' => $this->formStatus,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at
|
||||
];
|
||||
}
|
||||
}
|
19
app/Http/Resources/OptionResource.php
Normal file
19
app/Http/Resources/OptionResource.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class OptionResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
19
app/Http/Resources/ProfileResource.php
Normal file
19
app/Http/Resources/ProfileResource.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProfileResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
28
app/Http/Resources/ResponseResource.php
Normal file
28
app/Http/Resources/ResponseResource.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Form;
|
||||
use App\Vacancy;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ResponseResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'form' => new FormResource(Form::findOrFail($this->responseFormID)),
|
||||
'responseData' => json_decode($this->responseData),
|
||||
'vacancy' => new VacancyResource(Vacancy::findOrFail($this->associatedVacancyID)),
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at
|
||||
];
|
||||
}
|
||||
}
|
19
app/Http/Resources/TeamFileResource.php
Normal file
19
app/Http/Resources/TeamFileResource.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class TeamFileResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
19
app/Http/Resources/TeamResource.php
Normal file
19
app/Http/Resources/TeamResource.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class TeamResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
28
app/Http/Resources/UserResource.php
Normal file
28
app/Http/Resources/UserResource.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class UserResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'uuid' => $this->uuid,
|
||||
'name' => $this->name,
|
||||
'email' => $this->email,
|
||||
'username' => $this->username,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
'current_team_id' => $this->current_team_id
|
||||
];
|
||||
}
|
||||
}
|
19
app/Http/Resources/VacancyResource.php
Normal file
19
app/Http/Resources/VacancyResource.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class VacancyResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return parent::toArray($request);
|
||||
}
|
||||
}
|
@@ -30,7 +30,7 @@ use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class CleanBans implements ShouldQueue
|
||||
class ProcessDueSuspensions implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
@@ -52,15 +52,15 @@ class CleanBans implements ShouldQueue
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Log::debug('Running automatic ban cleaner...');
|
||||
Log::debug('Running automatic suspension cleaner...');
|
||||
$bans = Ban::all();
|
||||
|
||||
if (! is_null($bans)) {
|
||||
foreach ($this->bans as $ban) {
|
||||
$bannedUntil = Carbon::parse($ban->bannedUntil);
|
||||
|
||||
if ($bannedUntil->equalTo(now())) {
|
||||
Log::debug('Deleted ban '.$ban->id.' belonging to '.$ban->user->name);
|
||||
if ($bannedUntil->isToday()) {
|
||||
Log::debug('Lifted expired suspension ID '.$ban->id.' for '.$ban->user->name);
|
||||
$ban->delete();
|
||||
}
|
||||
}
|
17
app/OneoffApplicant.php
Normal file
17
app/OneoffApplicant.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class OneoffApplicant extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public function application()
|
||||
{
|
||||
return $this->belongsTo('App\Application', 'id', 'application_id');
|
||||
}
|
||||
}
|
||||
|
72
app/Policies/ApiKeyPolicy.php
Normal file
72
app/Policies/ApiKeyPolicy.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\ApiKey;
|
||||
use App\User;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
|
||||
class ApiKeyPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function viewAny(User $user)
|
||||
{
|
||||
if ($user->hasRole('admin'))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function create(User $user)
|
||||
{
|
||||
if ($user->hasRole('admin'))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\ApiKey $apiKey
|
||||
* @return mixed
|
||||
*/
|
||||
public function update(User $user, ApiKey $apiKey)
|
||||
{
|
||||
if ($user->hasRole('admin'))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*
|
||||
* @param \App\User $user
|
||||
* @param \App\ApiKey $apiKey
|
||||
* @return mixed
|
||||
*/
|
||||
public function delete(User $user, ApiKey $apiKey)
|
||||
{
|
||||
if ($user->hasRole('admin'))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@@ -27,6 +27,7 @@ use App\Observers\UserObserver;
|
||||
use App\User;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Sentry;
|
||||
|
||||
@@ -67,5 +68,7 @@ class AppServiceProvider extends ServiceProvider
|
||||
$https = true;
|
||||
|
||||
$this->app['request']->server->set('HTTPS', $https);
|
||||
|
||||
View::share('demoActive', config('demo.is_enabled'));
|
||||
}
|
||||
}
|
||||
|
@@ -21,10 +21,12 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\ApiKey;
|
||||
use App\Application;
|
||||
use App\Appointment;
|
||||
use App\Ban;
|
||||
use App\Form;
|
||||
use App\Policies\ApiKeyPolicy;
|
||||
use App\Policies\ApplicationPolicy;
|
||||
use App\Policies\AppointmentPolicy;
|
||||
use App\Policies\BanPolicy;
|
||||
@@ -61,7 +63,8 @@ class AuthServiceProvider extends ServiceProvider
|
||||
Ban::class => BanPolicy::class,
|
||||
Appointment::class => AppointmentPolicy::class,
|
||||
Team::class => TeamPolicy::class,
|
||||
TeamFile::class, TeamFilePolicy::class
|
||||
TeamFile::class => TeamFilePolicy::class,
|
||||
ApiKey::class => ApiKeyPolicy::class
|
||||
];
|
||||
|
||||
/**
|
||||
|
32
app/Providers/JSONProvider.php
Normal file
32
app/Providers/JSONProvider.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App;
|
||||
use App\Helpers\JSON;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class JSONProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
App::bind('json', function () {
|
||||
return new JSON();
|
||||
});
|
||||
}
|
||||
}
|
45
app/Providers/MojangStatusProvider.php
Executable file → Normal file
45
app/Providers/MojangStatusProvider.php
Executable file → Normal file
@@ -1,32 +1,13 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Copyright © 2020 Miguel Nogueira
|
||||
*
|
||||
* This file is part of Raspberry Staff Manager.
|
||||
*
|
||||
* Raspberry Staff Manager is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Raspberry Staff Manager is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Raspberry Staff Manager. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
|
||||
class MojangStatusProvider extends ServiceProvider
|
||||
{
|
||||
@@ -47,16 +28,26 @@ class MojangStatusProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
// TODO: (IMPORTANT) Switch this to Middleware
|
||||
if (! Cache::has('mojang_status')) {
|
||||
Log::info('Mojang Status Provider: Mojang Status not found in the cache; Sending new request.');
|
||||
$unknown_status = '[{"minecraft.net":"red"},{"session.minecraft.net":"red"},{"account.mojang.com":"red"},{"authserver.mojang.com":"red"},{"sessionserver.mojang.com":"red"},{"api.mojang.com":"red"},{"textures.minecraft.net":"red"},{"mojang.com":"red"}]';
|
||||
|
||||
try {
|
||||
// TODO: (IMPORTANT) Switch this to Middleware
|
||||
if (!Cache::has('mojang_status'))
|
||||
{
|
||||
Log::info("Mojang Status Provider: Mojang Status not found in the cache; Sending new request.");
|
||||
|
||||
try
|
||||
{
|
||||
$mcstatus = Http::get(config('general.urls.mojang.statuscheck'));
|
||||
Cache::put('mojang_status', base64_encode($mcstatus->body()), now()->addDays(3));
|
||||
} catch (ConnectException $connectException) {
|
||||
}
|
||||
catch(ConnectionException $connectException)
|
||||
{
|
||||
// Shorter TTL because mojang status server might have recovered
|
||||
Cache::put('mojang_status', base64_encode($unknown_status), now()->addMinutes(60));
|
||||
|
||||
Log::alert('Writing unknown Mojang status placeholder to cache');
|
||||
Log::critical('Could not connect to Mojang servers: Cannot check/refresh status', [
|
||||
'message' => $connectException->getMessage(),
|
||||
'message' => $connectException->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
53
app/Services/AccountSuspensionService.php
Normal file
53
app/Services/AccountSuspensionService.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Ban;
|
||||
use App\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class AccountSuspensionService
|
||||
{
|
||||
|
||||
public function suspend($reason, $duration, User $target, $type = "on"): Ban {
|
||||
|
||||
Log::debug("AccountSuspensionService: Suspending user account", [
|
||||
'userID' => $target->id
|
||||
]);
|
||||
|
||||
if ($type == "on") {
|
||||
$expiryDate = now()->addDays($duration);
|
||||
}
|
||||
|
||||
$ban = Ban::create([
|
||||
'userID' => $target->id,
|
||||
'reason' => $reason,
|
||||
'bannedUntil' => ($type == "on") ? $expiryDate->format('Y-m-d H:i:s') : null,
|
||||
'authorUserID' => Auth::user()->id,
|
||||
'isPermanent' => ($type == "off") ? true : false
|
||||
]);
|
||||
|
||||
return $ban;
|
||||
}
|
||||
|
||||
public function unsuspend(User $user): void {
|
||||
$user->bans->delete();
|
||||
}
|
||||
|
||||
public function isSuspended(User $user): bool {
|
||||
return !is_null($user->bans);
|
||||
}
|
||||
|
||||
public function makePermanent(Ban $ban): void {
|
||||
|
||||
$ban->bannedUntil = null;
|
||||
$ban->isPermanent = true;
|
||||
|
||||
$ban->save();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
158
app/Services/ApplicationService.php
Normal file
158
app/Services/ApplicationService.php
Normal file
@@ -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(User $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));
|
||||
}
|
||||
}
|
85
app/Services/AppointmentService.php
Normal file
85
app/Services/AppointmentService.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
27
app/Services/CommentService.php
Normal file
27
app/Services/CommentService.php
Normal file
@@ -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();
|
||||
}
|
||||
|
||||
}
|
75
app/Services/ConfigurationService.php
Normal file
75
app/Services/ConfigurationService.php
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
}
|
47
app/Services/ContactService.php
Normal file
47
app/Services/ContactService.php
Normal file
@@ -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,
|
||||
])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
11
app/Services/DemoService.php
Normal file
11
app/Services/DemoService.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
namespace App\Services;
|
||||
|
||||
class DemoService {
|
||||
|
||||
public function isDemoEnabled(): bool {
|
||||
|
||||
return config('demo.is_enabled');
|
||||
|
||||
}
|
||||
}
|
76
app/Services/FormManagementService.php
Normal file
76
app/Services/FormManagementService.php
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
36
app/Services/MeetingNoteService.php
Normal file
36
app/Services/MeetingNoteService.php
Normal file
@@ -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!');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
50
app/Services/ProfileService.php
Normal file
50
app/Services/ProfileService.php
Normal file
@@ -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.");
|
||||
}
|
||||
|
||||
}
|
54
app/Services/SecuritySettingsService.php
Normal file
54
app/Services/SecuritySettingsService.php
Normal file
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
42
app/Services/TeamFileService.php
Normal file
42
app/Services/TeamFileService.php
Normal file
@@ -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.");
|
||||
|
||||
}
|
||||
|
||||
}
|
165
app/Services/TeamService.php
Normal file
165
app/Services/TeamService.php
Normal 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.';
|
||||
}
|
||||
}
|
@@ -33,6 +33,13 @@ trait ReceivesAccountTokens
|
||||
{
|
||||
public function userDelete(UserDeleteRequest $request)
|
||||
{
|
||||
if (config('demo.is_enabled'))
|
||||
{
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
|
||||
// a little verbose
|
||||
$user = User::find(Auth::user()->id);
|
||||
$tokens = $user->generateAccountTokens();
|
||||
@@ -42,13 +49,20 @@ trait ReceivesAccountTokens
|
||||
$user->delete();
|
||||
Auth::logout();
|
||||
|
||||
$request->session()->flash('success', 'Please check your email to finish deleting your account.');
|
||||
$request->session()->flash('success', __('Please check your email to finish deleting your account.'));
|
||||
|
||||
return redirect()->to('/');
|
||||
}
|
||||
|
||||
public function processDeleteConfirmation(Request $request, $ID, $action, $token)
|
||||
{
|
||||
if (config('demo.is_enabled'))
|
||||
{
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', 'This feature is disabled');
|
||||
}
|
||||
|
||||
// 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);
|
||||
@@ -68,7 +82,7 @@ trait ReceivesAccountTokens
|
||||
|
||||
$user->forceDelete();
|
||||
|
||||
$request->session()->flash('success', 'Account permanently deleted. Thank you for using our service.');
|
||||
$request->session()->flash('success', __('Account permanently deleted. Thank you for using our service.'));
|
||||
|
||||
return redirect()->to('/');
|
||||
}
|
||||
@@ -79,7 +93,7 @@ trait ReceivesAccountTokens
|
||||
|
||||
if ($user->verifyAccountToken($token, 'cancelToken')) {
|
||||
$user->restore();
|
||||
$request->session()->flash('success', 'Account deletion cancelled! You may now login.');
|
||||
$request->session()->flash('success', __('Account deletion cancelled! You may now login.'));
|
||||
|
||||
return redirect()->to(route('login'));
|
||||
}
|
||||
@@ -88,7 +102,7 @@ trait ReceivesAccountTokens
|
||||
|
||||
default:
|
||||
|
||||
abort(404, 'The page you were trying to access may not exist or may be expired.');
|
||||
abort(404, __('The page you were trying to access may not exist or may be expired.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -92,6 +92,11 @@ class User extends Authenticatable implements MustVerifyEmail
|
||||
return $this->hasMany('App\TeamFile', 'uploaded_by');
|
||||
}
|
||||
|
||||
public function keys()
|
||||
{
|
||||
return $this->hasMany('App\ApiKey', 'owner_user_id');
|
||||
}
|
||||
|
||||
// UTILITY LOGIC
|
||||
|
||||
public function isBanned()
|
||||
|
@@ -8,12 +8,10 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.3.4",
|
||||
"php": "^8.0",
|
||||
"ext-imagick": "*",
|
||||
"ext-json": "*",
|
||||
"arcanedev/log-viewer": "^8.0",
|
||||
"awssat/discord-notification-channel": "^1.4",
|
||||
"berkayk/onesignal-laravel": "^1.0",
|
||||
"arcanedev/log-viewer": "^8.1.0",
|
||||
"doctrine/dbal": "^2.10",
|
||||
"fideloper/proxy": "^4.2",
|
||||
"fruitcake/laravel-cors": "^1.0",
|
||||
@@ -26,9 +24,9 @@
|
||||
"laravel/tinker": "^2.0",
|
||||
"laravel/ui": "^3.0",
|
||||
"mcamara/laravel-localization": "^1.5",
|
||||
"mpociot/teamwork": "^6.0",
|
||||
"mpociot/teamwork": "^6.1",
|
||||
"pragmarx/google2fa-laravel": "^1.3",
|
||||
"sentry/sentry-laravel": "2.1.1",
|
||||
"sentry/sentry-laravel": "2.9.0",
|
||||
"spatie/laravel-permission": "^3.13"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -37,7 +35,7 @@
|
||||
"fzaninotto/faker": "^1.9.1",
|
||||
"mockery/mockery": "^1.3.1",
|
||||
"nunomaduro/collision": "^5.0",
|
||||
"phpunit/phpunit": "^9.0"
|
||||
"phpunit/phpunit": "^9.3"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
|
3495
composer.lock
generated
Executable file → Normal file
3495
composer.lock
generated
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
@@ -365,6 +365,12 @@ return [
|
||||
'url' => '/admin/devtools',
|
||||
'can' => 'admin.developertools.use',
|
||||
],
|
||||
[
|
||||
'text' => 'API Keys',
|
||||
'icon' => 'fas fa-user-shield',
|
||||
'can' => 'admin.settings.view',
|
||||
'route' => 'keys.index'
|
||||
]
|
||||
],
|
||||
],
|
||||
[
|
||||
@@ -572,22 +578,6 @@ return [
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'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,
|
||||
@@ -605,5 +595,37 @@ return [
|
||||
],
|
||||
|
||||
],
|
||||
[
|
||||
'name' => 'BootstrapSwitch',
|
||||
'active' => true,
|
||||
'files' => [
|
||||
[
|
||||
'type' => 'js',
|
||||
'asset' => false,
|
||||
'location' => 'https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/js/bootstrap4-toggle.min.js'
|
||||
],
|
||||
[
|
||||
'type' => 'css',
|
||||
'asset' => false,
|
||||
'location' => 'https://cdn.jsdelivr.net/gh/gitbrent/bootstrap4-toggle@3.6.1/css/bootstrap4-toggle.min.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',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
@@ -48,6 +48,20 @@ return [
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Version
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value is the application's version.
|
||||
| It's used for informational purposes, and it'll be used for an auto-update system
|
||||
| in the near future.
|
||||
| Should always be the latest minor release.
|
||||
|
|
||||
*/
|
||||
'release' => env('RELEASE', '(unknown)'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| SSL for non-production environments
|
||||
@@ -230,6 +244,7 @@ return [
|
||||
\App\Providers\MojangStatusProvider::class,
|
||||
\App\Providers\OptionsProvider::class,
|
||||
App\Providers\DigitalStorageProvider::class,
|
||||
App\Providers\JSONProvider::class,
|
||||
|
||||
],
|
||||
|
||||
@@ -287,6 +302,7 @@ return [
|
||||
'Markdown' => GrahamCampbell\Markdown\Facades\Markdown::class,
|
||||
'ContextAwareValidator' => App\Facades\ContextAwareValidation::class,
|
||||
'Settings' => App\Facades\Options::class,
|
||||
'JSON' => App\Facades\JSON::class
|
||||
|
||||
],
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user