forked from miguel456/rbrecruiter
Compare commits
47 Commits
Author | SHA1 | Date | |
---|---|---|---|
bca6020ab0 | |||
1f50faaea7 | |||
e978a5417b | |||
4dc412e53c | |||
bd0664ce0d | |||
4b390ea536 | |||
035c9399a6 | |||
bbe3001157 | |||
d1f4be124c | |||
0f126886f5 | |||
4deb882d23 | |||
635f8593d3 | |||
c3a6b16a13 | |||
fb4d832489 | |||
4456d19c1f | |||
c7fd8989f9 | |||
fb422c1ef7 | |||
a42df354c7 | |||
173dc57aa7 | |||
cb2b45b55f | |||
a3e727f1f1 | |||
669c3c87e6 | |||
96298cd38c | |||
00f37d3f7e | |||
653641f4e8 | |||
d121119706 | |||
1c08cd9057 | |||
1bc63725c9 | |||
c7caf3a67c | |||
7c7d4a306c | |||
016ce1fbf6 | |||
f673ef8222 | |||
2bf8c8e90c | |||
d111864bea | |||
4dbf86a328 | |||
291c98cd0e | |||
119ea1d8ec | |||
0282a6a7c9 | |||
8ed2eb5027 | |||
|
61034ccae9 | ||
ed95f02e00 | |||
c58c46eda8 | |||
91c408fb4f | |||
599d742e96 | |||
50692a8abf | |||
664bdcf45e | |||
9ec2f9edce |
12
.env.example
12
.env.example
@@ -21,6 +21,18 @@ RECAPTCHA_VERIFY_URL="https://www.google.com/recaptcha/api/siteverify"
|
||||
IPGEO_API_KEY=""
|
||||
IPGEO_API_URL=""
|
||||
|
||||
MOJANG_STATUS_URL="https://status.mojang.com/check"
|
||||
MOJANG_API_URL="https://api.mojang.com"
|
||||
|
||||
IPGEO_API_KEY=""
|
||||
IPGEO_API_URL="https://api.ipgeolocation.io/ipgeo"
|
||||
|
||||
ARCANEDEV_LOGVIEWER_MIDDLEWARE=web,auth,can:admin.maintenance.logs.view
|
||||
|
||||
RELEASE=staffmanagement@0.2.0
|
||||
|
||||
SLACK_INTEGRATION_WEBHOOK=
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
QUEUE_CONNECTION=sync
|
||||
|
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
42
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG] - Short description of what went wrong"
|
||||
labels: bug, help wanted
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Steps to reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):**
|
||||
- OS: [e.g. iOS]
|
||||
- Browser [e.g. chrome, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**Smartphone (please complete the following information):**
|
||||
- Device: [e.g. iPhone6]
|
||||
- OS: [e.g. iOS8.1]
|
||||
- Browser [e.g. stock browser, safari]
|
||||
- Version [e.g. 22]
|
||||
|
||||
**PHP Version: **
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
**Log files: attach any log files from the storage/logs directory**.
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,6 +4,7 @@
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/vendor
|
||||
/tools
|
||||
.env
|
||||
.env.backup
|
||||
.phpunit.result.cache
|
||||
|
5
.phive/phars.xml
Normal file
5
.phive/phars.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phive xmlns="https://phar.io/phive">
|
||||
<phar name="phpunit" version="^9.2.5" installed="9.2.5" location="./tools/phpunit" copy="false"/>
|
||||
<phar name="php-cs-fixer" version="^2.16.4" installed="2.16.4" location="./tools/php-cs-fixer" copy="false"/>
|
||||
</phive>
|
116
README.md
116
README.md
@@ -1,79 +1,75 @@
|
||||
<p align="center"><img src="https://res.cloudinary.com/dtfbvvkyp/image/upload/v1566331377/laravel-logolockup-cmyk-red.svg" width="400"></p>
|
||||
# Raspberry Teams - The Simple Staff Application Manager v 0.1.0
|
||||
## The quick and pain-free staff application manager (for Minecraft)
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/laravel/framework"><img src="https://travis-ci.org/laravel/framework.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/d/total.svg" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/v/stable.svg" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/license.svg" alt="License"></a>
|
||||
</p>
|
||||
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?
|
||||
|
||||
## About Laravel
|
||||
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
Wish you had a better application managemet strategy? Well, then Raspberry Teams is for you! It was originally designed and developed for internal use, but sharing is caring! After noticing a worrying lack of "human resources" management systems on SpigotMC's resources section (There was only one outdated/unsupported project), I've decided to take it up into my own terms and start working on it.
|
||||
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
# Features (not exhaustive)
|
||||
- Beautiful (customizable in future releases) landing page for your application management center; It displays all available staff ranks
|
||||
- Contact form on landing page for those un-registerd users
|
||||
- User registration/authentication system; Users will be sent to the authentication flow to complete their application, if not logged in
|
||||
- Candidate tracking system - Applicants will be tracked from start to finish.
|
||||
- Peer approval system - Have all your staff members vote on applications and decide whether they should be accepted (this is overridable)
|
||||
- Interview scheduling (simple) - Schedule interviews with your candidates and automatically notify them!
|
||||
- Interview notes: Every staff member is able to add and edit interview notes (how the interview went, etc)
|
||||
- Application comments: Finally no more having to go to a private Discord channel just to comment on a single application. Comments are organised neatly for every application! This should help in the decision process of voting for an application.
|
||||
- User profiles - Fill out your profile for others to better find you
|
||||
- User directory - Public profile directory for everyone
|
||||
- Staff rank management - Add/remove ranks on demand, that users will be able to apply to
|
||||
- Simple form builder - Create your application forms easily!
|
||||
- Termination - Has a staff member met their untimely demise? Terminate them. This will strip their permissions and roles.
|
||||
- Controllable permissions - Every user has permissions! Control who has access to what (You can skip the application process and add staff members directly here).
|
||||
- Ban system - Having trouble with pesky spammers? Ban them! This will publicly shame their profile and keep them from signing up or logging in.
|
||||
- Notifications: Notifies slack and email primarily
|
||||
|
||||
## Learning Laravel
|
||||
And many more features!
|
||||
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||
# Roadmap
|
||||
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 1500 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
Many other features are currently planned for this app, such as:
|
||||
- Discord role management (approved applicants)
|
||||
- Luckperms/PEX integration - For now, you'll have to promote users manually in-game
|
||||
- Flexibility - This app is built on a flexible concept! It will be able to be used for other purposes other than MC staff members.
|
||||
- Customisable front page (**priority**)
|
||||
- Auto provisioning - Sign up on a website and get your instance of Raspberry Teams up and running in no time
|
||||
- Suggestions accepted!
|
||||
|
||||
## Laravel Sponsors
|
||||
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell).
|
||||
# Technical overview
|
||||
|
||||
- **[Vehikl](https://vehikl.com/)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Cubet Techno Labs](https://cubettech.com)**
|
||||
- **[Cyber-Duck](https://cyber-duck.co.uk)**
|
||||
- **[British Software Development](https://www.britishsoftware.co)**
|
||||
- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
|
||||
- **[DevSquad](https://devsquad.com)**
|
||||
- [UserInsights](https://userinsights.com)
|
||||
- [Fragrantica](https://www.fragrantica.com)
|
||||
- [SOFTonSOFA](https://softonsofa.com/)
|
||||
- [User10](https://user10.com)
|
||||
- [Soumettre.fr](https://soumettre.fr/)
|
||||
- [CodeBrisk](https://codebrisk.com)
|
||||
- [1Forge](https://1forge.com)
|
||||
- [TECPRESSO](https://tecpresso.co.jp/)
|
||||
- [Runtime Converter](http://runtimeconverter.com/)
|
||||
- [WebL'Agence](https://weblagence.com/)
|
||||
- [Invoice Ninja](https://www.invoiceninja.com)
|
||||
- [iMi digital](https://www.imi-digital.de/)
|
||||
- [Earthlink](https://www.earthlink.ro/)
|
||||
- [Steadfast Collective](https://steadfastcollective.com/)
|
||||
- [We Are The Robots Inc.](https://watr.mx/)
|
||||
- [Understand.io](https://www.understand.io/)
|
||||
- [Abdel Elrafa](https://abdelelrafa.com)
|
||||
- [Hyper Host](https://hyper.host)
|
||||
- [Appoly](https://www.appoly.co.uk)
|
||||
- [OP.GG](https://op.gg)
|
||||
- [云软科技](http://www.yunruan.ltd/)
|
||||
Tech stack:
|
||||
- [Laravel 7](https://laravel.com/)
|
||||
- Eloquent ORM
|
||||
- AdminLTE / Bootstrap 4
|
||||
- jQuery / Plain Javascript
|
||||
- vueJS (in the future)
|
||||
|
||||
## Contributing
|
||||
# Operating System Requirements
|
||||
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
Currently, this application is only supported on Linux environments (Ubuntu 20.04 or derivatives are recommended).
|
||||
|
||||
## Code of Conduct
|
||||
# Software Requirements
|
||||
- ``composer`` (min version: 1.8.4)
|
||||
- ``npm`` (tested w/ v 5.8.0)
|
||||
- ``php`` (required PHP 7 or newer - lower versions unsupported!)
|
||||
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
# PHP Extension Requirements
|
||||
|
||||
## Security Vulnerabilities
|
||||
- JSON
|
||||
- Curl (highly recommended)
|
||||
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
# Installation
|
||||
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
Make sure all prerequisites are installed. Afterwards, clone this repository, make ``install.sh``executable and run it.
|
||||
|
||||
# Configuration
|
||||
Configuration is currently done via the installer. Alternatively, you may also edit the ``.env`` file directly.
|
||||
This process will be moved to the browser later.
|
||||
|
||||
# Bug reports
|
||||
|
||||
Please report any bugs you find to the issues section here! It'd be immensely helpful. PRs are also accepted.
|
||||
|
137
app/Console/Commands/Install.php
Normal file
137
app/Console/Commands/Install.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class Install extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'application:install {-u|--unattended: Install non-interactively (currently unused: WIP)}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Installs the application and prepares for production use.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$basePath = base_path();
|
||||
if (Storage::disk('local')->missing('INSTALLED'))
|
||||
{
|
||||
|
||||
|
||||
$this->info('[!! Welcome to Rasberry Teams !!]');
|
||||
$this->info('>> Installing...');
|
||||
$this->call('down', [
|
||||
'--message' => 'Down for maintenance. We\'ll be right back!'
|
||||
]);
|
||||
|
||||
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.');
|
||||
|
||||
$npmOut = 0;
|
||||
$npmMessages = [];
|
||||
|
||||
$npmBuildOut = 0;
|
||||
$npmBuildMessages = [];
|
||||
|
||||
exec('cd ' . $basePath . ' && npm install --silent', $npmBuildOut, $npmOut);
|
||||
exec('cd ' . $basePath . '&& npm run dev --silent', $npmBuildMessages, $npmBuildOut);
|
||||
|
||||
|
||||
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.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$settings = [];
|
||||
|
||||
$this->info('>> Configuring application - We\'re going to ask a few questions here!');
|
||||
do
|
||||
{
|
||||
$this->info('== Database Settings (1/6) ==');
|
||||
|
||||
$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');
|
||||
|
||||
$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');
|
||||
|
||||
$this->info('== Notification Settings (5/6) (Slack) ==');
|
||||
$settings['SLACK_INTEGRATION_WEBHOOK'] = $this->ask('Integration webhook URL');
|
||||
|
||||
$this->info('== Web Settings (6/6) ==');
|
||||
$settings['APP_URL'] = $this->ask('Application\'s URL');
|
||||
|
||||
} 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!');
|
||||
}
|
||||
}
|
||||
}
|
56
app/Console/Commands/SetEnv.php
Normal file
56
app/Console/Commands/SetEnv.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use GeoSot\EnvEditor\Facades\EnvEditor;
|
||||
|
||||
class SetEnv extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'environment:modify {key : Key name} {value : New value}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Permanently modifies an environment variable on the .env file for later use.';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$path = base_path('/.env');
|
||||
$key = $this->argument('key');
|
||||
$value = $this->argument('value');
|
||||
|
||||
|
||||
|
||||
if (file_exists($path))
|
||||
{
|
||||
EnvEditor::editKey($key, $value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->error('Cannot update a file that doesn\'t exist! Please create .env first.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
14
app/Facades/ContextAwareValidation.php
Normal file
14
app/Facades/ContextAwareValidation.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace App\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
class ContextAwareValidation extends Facade
|
||||
{
|
||||
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return 'contextAwareValidator';
|
||||
}
|
||||
|
||||
}
|
@@ -16,7 +16,7 @@ class Form extends Model
|
||||
|
||||
public function vacancies()
|
||||
{
|
||||
return $this->hasMany('vacancies', 'vacancyFormID', 'id');
|
||||
return $this->hasMany('App\Vacancy', 'vacancyFormID', 'id');
|
||||
}
|
||||
|
||||
public function responses()
|
||||
|
72
app/Helpers/ContextAwareValidator.php
Normal file
72
app/Helpers/ContextAwareValidator.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace App\Helpers;
|
||||
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class ContextAwareValidator
|
||||
{
|
||||
/**
|
||||
* The getValidator() method will take an array of fields from the request body, iterates through them,
|
||||
* and dynamically adds validation rules for them. Depending on parameters, it may or may not generate
|
||||
* a form structure for rendering purposes.
|
||||
*
|
||||
* This method is mostly meant by internal use by means of static proxies (Facades), in order to reduce code repetition;
|
||||
* Using it outside it's directed scope may cause unexpected results; For instance, the method expects inputs to be in array format, e.g. myFieldNameID1[],
|
||||
* myFieldNameID2[], and so on and so forth.
|
||||
*
|
||||
* This isn't checked by the code yet, but if you're implementing it this way in the HTML markup, make sure it's consistent (e.g. use a loop).
|
||||
*
|
||||
* P.S This method automatically ignores the CSRF token for validation.
|
||||
*
|
||||
* @param array $fields The request form fields
|
||||
* @param bool $generateStructure Whether to incldue a JSON-ready form structure for rendering
|
||||
* @param bool $includeFormName Whether to include formName in the list of validation rules
|
||||
* @return Validator|Collection A validator instance you can use to check for validity, or a Collection with a validator and structure (validator, structure)
|
||||
*/
|
||||
public function getValidator(array $fields, bool $generateStructure = false, bool $includeFormName = false)
|
||||
{
|
||||
$formStructure = [];
|
||||
$validator = [];
|
||||
|
||||
$excludedNames = [
|
||||
'_token',
|
||||
'_method',
|
||||
'formName'
|
||||
];
|
||||
|
||||
if ($includeFormName)
|
||||
{
|
||||
$validator['formName'] = 'required|string|max:100';
|
||||
}
|
||||
|
||||
foreach ($fields as $fieldName => $field)
|
||||
{
|
||||
if(!in_array($fieldName, $excludedNames))
|
||||
{
|
||||
$validator[$fieldName . ".0"] = 'required|string';
|
||||
$validator[$fieldName . ".1"] = 'required|string';
|
||||
|
||||
if ($generateStructure)
|
||||
{
|
||||
$formStructure['fields'][$fieldName]['title'] = $field[0];
|
||||
$formStructure['fields'][$fieldName]['type'] = $field[1];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$validatorInstance = Validator::make($fields, $validator);
|
||||
|
||||
return ($generateStructure) ?
|
||||
collect([
|
||||
'validator' => $validatorInstance,
|
||||
'structure' => json_encode($formStructure)
|
||||
])
|
||||
: $validatorInstance;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -45,10 +45,9 @@ class ApplicationController extends Controller
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function showUserApp(Request $request, $applicationID)
|
||||
{
|
||||
// TODO: Inject it instead (do this where there is no injection, not just here)
|
||||
$application = Application::find($applicationID);
|
||||
|
||||
$this->authorize('view', $application);
|
||||
@@ -77,6 +76,12 @@ class ApplicationController extends Controller
|
||||
|
||||
|
||||
|
||||
public function showAllApps()
|
||||
{
|
||||
return view('dashboard.appmanagement.all')
|
||||
->with('applications', Application::paginate(6));
|
||||
}
|
||||
|
||||
|
||||
public function showAllPendingApps()
|
||||
{
|
||||
@@ -87,9 +92,6 @@ class ApplicationController extends Controller
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public function showPendingInterview()
|
||||
{
|
||||
$this->authorize('viewAny', Application::class);
|
||||
@@ -280,4 +282,16 @@ class ApplicationController extends Controller
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function delete(Request $request, Application $application)
|
||||
{
|
||||
|
||||
$this->authorize('delete', $application);
|
||||
$application->delete(); // observers will run, cleaning it up
|
||||
|
||||
$request->session()->flash('success', 'Application deleted. Comments, appointments and responses have also been deleted.');
|
||||
return redirect()->back();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@ use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
use ContextAwareValidator;
|
||||
|
||||
class FormController extends Controller
|
||||
{
|
||||
|
||||
@@ -29,39 +31,17 @@ class FormController extends Controller
|
||||
{
|
||||
|
||||
$this->authorize('create', Form::class);
|
||||
$fields = $request->all();
|
||||
|
||||
$formFields = $request->all();
|
||||
$contextValidation = ContextAwareValidator::getValidator($fields, true, true);
|
||||
|
||||
$formStructure = [];
|
||||
$excludedNames = [
|
||||
'_token',
|
||||
'formName' // It's added outside the loop. Not excluding causes unwanted duplication.
|
||||
];
|
||||
$validator = [
|
||||
'formName' => 'required|string|max:100'
|
||||
];
|
||||
|
||||
foreach ($formFields as $fieldName => $field)
|
||||
if (!$contextValidation->get('validator')->fails())
|
||||
{
|
||||
if(!in_array($fieldName, $excludedNames))
|
||||
{
|
||||
$validator[$fieldName . ".0"] = 'required|string';
|
||||
$validator[$fieldName . ".1"] = 'required|string';
|
||||
|
||||
$formStructure['fields'][$fieldName]['title'] = $field[0];
|
||||
$formStructure['fields'][$fieldName]['type'] = $field[1];
|
||||
}
|
||||
}
|
||||
|
||||
$validation = Validator::make($formFields, $validator);
|
||||
|
||||
if (!$validation->fails())
|
||||
{
|
||||
$storableFormStructure = json_encode($formStructure);
|
||||
$storableFormStructure = $contextValidation->get('structure');
|
||||
|
||||
Form::create(
|
||||
[
|
||||
'formName' => $formFields['formName'],
|
||||
'formName' => $fields['formName'],
|
||||
'formStructure' => $storableFormStructure,
|
||||
'formStatus' => 'ACTIVE'
|
||||
]
|
||||
@@ -71,7 +51,7 @@ class FormController extends Controller
|
||||
return redirect()->to(route('showForms'));
|
||||
}
|
||||
|
||||
$request->session()->flash('errors', $validation->errors()->getMessages());
|
||||
$request->session()->flash('errors', $contextValidation->get('validator')->errors()->getMessages());
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
@@ -80,19 +60,68 @@ class FormController extends Controller
|
||||
|
||||
$form = Form::find($id);
|
||||
$this->authorize('delete', $form);
|
||||
$deletable = true;
|
||||
|
||||
// TODO: Check if form is linked to vacancies before allowing deletion
|
||||
if (!is_null($form))
|
||||
|
||||
if (!is_null($form) && !is_null($form->vacancies) && $form->vacancies->count() !== 0 || !is_null($form->responses))
|
||||
{
|
||||
$form->delete();
|
||||
|
||||
$request->session()->flash('success', 'Form deleted successfully.');
|
||||
return redirect()->back();
|
||||
$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.');
|
||||
}
|
||||
|
||||
$request->session()->flash('error', 'The form you\'re trying to delete does not exist.');
|
||||
return redirect()->back();
|
||||
|
||||
}
|
||||
|
||||
public function preview(Request $request, Form $form)
|
||||
{
|
||||
return view('dashboard.administration.formpreview')
|
||||
->with('form', json_decode($form->formStructure, true))
|
||||
->with('title', $form->formName)
|
||||
->with('formID', $form->id);
|
||||
}
|
||||
|
||||
public function edit(Request $request, Form $form)
|
||||
{
|
||||
return view('dashboard.administration.editform')
|
||||
->with('formStructure', json_decode($form->formStructure, true))
|
||||
->with('title', $form->formName)
|
||||
->with('formID', $form->id);
|
||||
}
|
||||
|
||||
public function update(Request $request, Form $form)
|
||||
{
|
||||
$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());
|
||||
}
|
||||
|
||||
return redirect()->to(route('previewForm', ['form' => $form->id]));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -15,15 +15,12 @@ class HomeController extends Controller
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// TODO: Relationships for Applications, Users and Responses
|
||||
// Also prevent apps if user already has one in the space of 30d
|
||||
// Display apps in the relevant menus
|
||||
|
||||
$positions = DB::table('vacancies')
|
||||
->where('vacancyStatus', 'OPEN')
|
||||
->where('vacancyCount', '!=', 0)
|
||||
->get();
|
||||
|
||||
$positions = Vacancy::where('vacancyStatus', 'OPEN')
|
||||
->where('vacancyCount', '<>', 0)
|
||||
->get();
|
||||
|
||||
|
||||
return view('home')
|
||||
->with('positions', $positions);
|
||||
}
|
||||
|
@@ -3,15 +3,19 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\VacancyRequest;
|
||||
use App\Http\Requests\VacancyEditRequest;
|
||||
|
||||
use App\Vacancy;
|
||||
use App\User;
|
||||
use App\Form;
|
||||
|
||||
use App\Notifications\VacancyClosed;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
||||
class VacancyController extends Controller
|
||||
{
|
||||
public function index()
|
||||
@@ -31,10 +35,16 @@ class VacancyController extends Controller
|
||||
|
||||
if (!is_null($form))
|
||||
{
|
||||
/* note: since we can't convert HTML back to Markdown, we'll have to do the converting when the user requests a page,
|
||||
* and leave the database with Markdown only so it can be used and edited everywhere.
|
||||
* for several vacancies, this would require looping through all of them and replacing MD with HTML, which is obviously not the most clean solution;
|
||||
* however, the Model can be configured to return MD instead of HTML on that specific field saving us from looping.
|
||||
*/
|
||||
Vacancy::create([
|
||||
|
||||
'vacancyName' => $request->vacancyName,
|
||||
'vacancyDescription' => $request->vacancyDescription,
|
||||
'vacancyFullDescription' => $request->vacancyFullDescription,
|
||||
'vacancySlug' => Str::slug($request->vacancyName),
|
||||
'permissionGroupName' => $request->permissionGroup,
|
||||
'discordRoleID' => $request->discordRole,
|
||||
@@ -101,4 +111,29 @@ class VacancyController extends Controller
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
|
||||
public function edit(Request $request, Vacancy $position)
|
||||
{
|
||||
$this->authorize('update', $vacancy);
|
||||
return view('dashboard.administration.editposition')
|
||||
->with('vacancy', $position);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function update(VacancyEditRequest $request, Vacancy $position)
|
||||
{
|
||||
$this->authorize('update', $vacancy);
|
||||
|
||||
$position->vacancyFullDescription = $request->vacancyFullDescription;
|
||||
$position->vacancyDescription = $request->vacancyDescription;
|
||||
$position->vacancyCount = $request->vacancyCount;
|
||||
|
||||
$position->save();
|
||||
|
||||
$request->session()->flash('success', 'Vacancy successfully updated.');
|
||||
return redirect()->back();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use App\Facades\UUID;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class UsernameUUID
|
||||
@@ -19,13 +20,18 @@ class UsernameUUID
|
||||
$input = $request->all();
|
||||
if (isset($input['uuid']))
|
||||
{
|
||||
// TODO: Switch to custom Facade
|
||||
$username = $input['uuid'];
|
||||
try
|
||||
{
|
||||
$username = $input['uuid'];
|
||||
$input['uuid'] = UUID::toUUID($username);
|
||||
}
|
||||
catch(\InvalidArgumentException $iae)
|
||||
{
|
||||
report($iae);
|
||||
|
||||
$conversionRequest = Http::get(config('general.urls.mojang.api') . '/users/profiles/minecraft/' . $username)->body();
|
||||
$decodedConversionRequest = json_decode($conversionRequest, true);
|
||||
|
||||
$input['uuid'] = $decodedConversionRequest['id'];
|
||||
$request->session()->flash('error', $iae->getMessage());
|
||||
return redirect(route('register'));
|
||||
}
|
||||
|
||||
$request->replace($input);
|
||||
}
|
||||
|
34
app/Http/Requests/VacancyEditRequest.php
Normal file
34
app/Http/Requests/VacancyEditRequest.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
|
||||
class VacancyEditRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return Auth::user()->can('admin.hiring.vacancy.edit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'vacancyDescription' => 'required|string',
|
||||
'vacancyFullDescription' => 'nullable|string',
|
||||
'vacancyCount' => 'required|integer|min:1'
|
||||
];
|
||||
}
|
||||
}
|
@@ -26,6 +26,7 @@ class VacancyRequest extends FormRequest
|
||||
return [
|
||||
'vacancyName' => 'required|string',
|
||||
'vacancyDescription' => 'required|string',
|
||||
'vacancyFullDescription' => 'nullable|string',
|
||||
'permissionGroup' => 'required|string',
|
||||
'discordRole' => 'required|string',
|
||||
'vacancyCount' => 'required|integer',
|
||||
|
@@ -6,12 +6,13 @@ use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
use App\Vacancy;
|
||||
|
||||
class VacancyClosed extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
protected $vacancy;
|
||||
|
||||
|
93
app/Observers/ApplicationObserver.php
Normal file
93
app/Observers/ApplicationObserver.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Application;
|
||||
|
||||
class ApplicationObserver
|
||||
{
|
||||
/**
|
||||
* Handle the application "created" event.
|
||||
*
|
||||
* @param \App\Application $application
|
||||
* @return void
|
||||
*/
|
||||
public function created(Application $application)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the application "updated" event.
|
||||
*
|
||||
* @param \App\Application $application
|
||||
* @return void
|
||||
*/
|
||||
public function updated(Application $application)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function deleting(Application $application)
|
||||
{
|
||||
$application->response()->delete();
|
||||
$votes = $application->votes;
|
||||
|
||||
foreach ($votes as $vote)
|
||||
{
|
||||
Log::debug('Referential integrity cleanup: Deleting and detaching vote ' . $vote->id);
|
||||
$vote->application()->detach($application->id);
|
||||
$vote->delete();
|
||||
}
|
||||
|
||||
if (!is_null($application->appointment))
|
||||
{
|
||||
Log::debug('RIC: Deleting appointment!');
|
||||
$application->appointment()->delete();
|
||||
}
|
||||
|
||||
if (!$application->comments->isEmpty())
|
||||
{
|
||||
Log::debug('RIC: Deleting comments!');
|
||||
foreach($application->comments as $comment)
|
||||
{
|
||||
$comment->delete();
|
||||
}
|
||||
}
|
||||
|
||||
// application can now be deleted
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the application "deleted" event.
|
||||
*
|
||||
* @param \App\Application $application
|
||||
* @return void
|
||||
*/
|
||||
public function deleted(Application $application)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the application "restored" event.
|
||||
*
|
||||
* @param \App\Application $application
|
||||
* @return void
|
||||
*/
|
||||
public function restored(Application $application)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the application "force deleted" event.
|
||||
*
|
||||
* @param \App\Application $application
|
||||
* @return void
|
||||
*/
|
||||
public function forceDeleted(Application $application)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@@ -48,30 +48,7 @@ class UserObserver
|
||||
Log::debug('RIC: Now trying to delete applications and responses...');
|
||||
foreach($applications as $application)
|
||||
{
|
||||
$application->response()->delete();
|
||||
$votes = $application->votes;
|
||||
|
||||
foreach ($votes as $vote)
|
||||
{
|
||||
Log::debug('RIC: Deleting and detaching vote ' . $vote->id);
|
||||
$vote->application()->detach($application->id);
|
||||
$vote->delete();
|
||||
}
|
||||
|
||||
if (!is_null($application->appointment))
|
||||
{
|
||||
Log::debug('RIC: Deleting appointment!');
|
||||
$application->appointment()->delete();
|
||||
}
|
||||
|
||||
if (!$application->comments->isEmpty())
|
||||
{
|
||||
Log::debug('RIC: Deleting comments!');
|
||||
foreach($application->comments as $comment)
|
||||
{
|
||||
$comment->delete();
|
||||
}
|
||||
}
|
||||
// code moved to Application observer, where it gets rid of attached elements individually
|
||||
Log::debug('RIC: Deleting application ' . $application->id);
|
||||
$application->delete();
|
||||
|
||||
|
64
app/Observers/VacancyObserver.php
Normal file
64
app/Observers/VacancyObserver.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Observers;
|
||||
|
||||
use App\Vacancy;
|
||||
|
||||
|
||||
class VacancyObserver
|
||||
{
|
||||
/**
|
||||
* Handle the vacancy "created" event.
|
||||
*
|
||||
* @param \App\Vacancy $vacancy
|
||||
* @return void
|
||||
*/
|
||||
public function created(Vacancy $vacancy)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the vacancy "updated" event.
|
||||
*
|
||||
* @param \App\Vacancy $vacancy
|
||||
* @return void
|
||||
*/
|
||||
public function updated(Vacancy $vacancy)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the vacancy "deleted" event.
|
||||
*
|
||||
* @param \App\Vacancy $vacancy
|
||||
* @return void
|
||||
*/
|
||||
public function deleted(Vacancy $vacancy)
|
||||
{
|
||||
// TODO: Handle deletion of children's data
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the vacancy "restored" event.
|
||||
*
|
||||
* @param \App\Vacancy $vacancy
|
||||
* @return void
|
||||
*/
|
||||
public function restored(Vacancy $vacancy)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the vacancy "force deleted" event.
|
||||
*
|
||||
* @param \App\Vacancy $vacancy
|
||||
* @return void
|
||||
*/
|
||||
public function forceDeleted(Vacancy $vacancy)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
@@ -45,4 +45,11 @@ class ApplicationPolicy
|
||||
{
|
||||
return $user->hasAnyRole('admin', 'hiringManager');
|
||||
}
|
||||
|
||||
public function delete(User $user, Application $application)
|
||||
{
|
||||
|
||||
return $user->hasRole('admin');
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ class FormPolicy
|
||||
*/
|
||||
public function create(User $user)
|
||||
{
|
||||
return $this->user->can('admin.hiring.forms');
|
||||
return $user->can('admin.hiring.forms');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,7 +57,7 @@ class FormPolicy
|
||||
*/
|
||||
public function update(User $user, Form $form)
|
||||
{
|
||||
// unused
|
||||
return $user->can('admin.hiring.forms');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ class FormPolicy
|
||||
*/
|
||||
public function delete(User $user, Form $form)
|
||||
{
|
||||
return $this->user->can('admin.hiring.forms');
|
||||
return $user->can('admin.hiring.forms');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -53,7 +53,7 @@ class VacancyPolicy
|
||||
*/
|
||||
public function update(User $user, Vacancy $vacancy)
|
||||
{
|
||||
return $user->hasRole('admin', 'hiringManager');
|
||||
return $user->hasAnyRole('admin', 'hiringManager');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -6,6 +6,7 @@ use App\Observers\UserObserver;
|
||||
use App\User;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Sentry;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -26,6 +27,10 @@ class AppServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Sentry\init([
|
||||
'release' => env('RELEASE')
|
||||
]);
|
||||
|
||||
Schema::defaultStringLength(191);
|
||||
User::observe(UserObserver::class);
|
||||
}
|
||||
|
34
app/Providers/ContextAwareValidatorProvider.php
Normal file
34
app/Providers/ContextAwareValidatorProvider.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
use App;
|
||||
|
||||
class ContextAwareValidatorProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
App::bind('contextAwareValidator', function(){
|
||||
|
||||
return new App\Helpers\ContextAwareValidator();
|
||||
|
||||
});
|
||||
}
|
||||
}
|
@@ -19,11 +19,17 @@ class UUID
|
||||
throw new \LogicException('Argument username for ' . __METHOD__ . ' cannot be null!');
|
||||
}
|
||||
|
||||
$response = json_decode(Http::post(config('general.urls.mojang.api') . '/profiles/minecraft', [
|
||||
$response = json_decode(Http::post(trim(config('general.urls.mojang.api')) . '/profiles/minecraft', [
|
||||
$username
|
||||
])->body(), true);
|
||||
|
||||
return $response[0]['id'];
|
||||
if (isset($response[0]))
|
||||
{
|
||||
return $response[0]['id'];
|
||||
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException("You must supply a valid, premium Minecraft account to sign up.");
|
||||
}
|
||||
|
||||
// Note: Caching could simply be assigning the username to it's UUID, however, to make this work, we'd need to loop over all cache items, which would be slighly ineffective
|
||||
@@ -38,7 +44,7 @@ class UUID
|
||||
$shortUUID = substr($uuid, 0, 8);
|
||||
$username = Cache::remember('uuid_' . $shortUUID, now()->addDays(30), function() use ($shortUUID, $uuid) {
|
||||
|
||||
$response = json_decode(Http::get(config('general.urls.mojang.session') . '/session/minecraft/profile/' . $uuid)->body(), true);
|
||||
$response = json_decode(Http::get(trim(config('general.urls.mojang.session')) . '/session/minecraft/profile/' . $uuid)->body(), true);
|
||||
|
||||
Log::debug('Caching ' . $shortUUID . 'for thirty days');
|
||||
return $response['name'];
|
||||
|
@@ -6,6 +6,9 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
use GrahamCampbell\Markdown\Facades\Markdown;
|
||||
|
||||
|
||||
class Vacancy extends Model
|
||||
{
|
||||
public $fillable = [
|
||||
@@ -13,6 +16,7 @@ class Vacancy extends Model
|
||||
'permissionGroupName',
|
||||
'vacancyName',
|
||||
'vacancyDescription',
|
||||
'vacancyFullDescription',
|
||||
'discordRoleID',
|
||||
'vacancyFormID',
|
||||
'vacancyCount',
|
||||
@@ -21,6 +25,26 @@ class Vacancy extends Model
|
||||
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Get the HTML variant of the vacancyFullDescription attribute.
|
||||
*
|
||||
* @param string $value The original value
|
||||
* @return string
|
||||
*/
|
||||
public function getVacancyFullDescriptionAttribute($value)
|
||||
{
|
||||
if (!is_null($value))
|
||||
{
|
||||
return Markdown::convertToHTML($value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function forms()
|
||||
{
|
||||
return $this->belongsTo('App\Form', 'vacancyFormID', 'id');
|
||||
|
33
app/View/Components/Form.php
Normal file
33
app/View/Components/Form.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class Form extends Component
|
||||
{
|
||||
|
||||
public $formFields;
|
||||
|
||||
|
||||
public $disableFields = false;
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($disableFields = false)
|
||||
{
|
||||
$this->disableFields = $disableFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*
|
||||
* @return \Illuminate\View\View|string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return view('components.form');
|
||||
}
|
||||
}
|
33
app/View/Components/NoPermission.php
Normal file
33
app/View/Components/NoPermission.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use Illuminate\View\Component;
|
||||
|
||||
class NoPermission extends Component
|
||||
{
|
||||
public $type;
|
||||
|
||||
public $inDashboard;
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($type, $inDashboard = true)
|
||||
{
|
||||
$this->type = $type;
|
||||
|
||||
$this->inDashboard = $inDashboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*
|
||||
* @return \Illuminate\View\View|string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return view('components.no-permission');
|
||||
}
|
||||
}
|
@@ -14,6 +14,8 @@
|
||||
"doctrine/dbal": "^2.10",
|
||||
"fideloper/proxy": "^4.2",
|
||||
"fruitcake/laravel-cors": "^1.0",
|
||||
"geo-sot/laravel-env-editor": "^0.9.9",
|
||||
"graham-campbell/markdown": "^12.0",
|
||||
"guzzlehttp/guzzle": "^6.5",
|
||||
"jeroennoten/laravel-adminlte": "^3.2",
|
||||
"laravel/framework": "^7.0",
|
||||
|
128
composer.lock
generated
128
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "fd14fa762c6ac8136b171abd8437e7d7",
|
||||
"content-hash": "51429857899e8134bbe6b4fa61145cc3",
|
||||
"packages": [
|
||||
{
|
||||
"name": "almasaeed2010/adminlte",
|
||||
@@ -1032,6 +1032,132 @@
|
||||
],
|
||||
"time": "2020-04-28T08:47:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "geo-sot/laravel-env-editor",
|
||||
"version": "v0.9.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/GeoSot/Laravel-EnvEditor.git",
|
||||
"reference": "e828d3d3310890286d0b53045de9381187258605"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/GeoSot/Laravel-EnvEditor/zipball/e828d3d3310890286d0b53045de9381187258605",
|
||||
"reference": "e828d3d3310890286d0b53045de9381187258605",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"laravel/framework": "~5.5.0|~5.6.0|~5.7.0|~5.8.0|~6.0|~7.0",
|
||||
"php": "^7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.9.*-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"GeoSot\\EnvEditor\\ServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"EnvEditor": "GeoSot\\EnvEditor\\Facades\\EnvEditor"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GeoSot\\EnvEditor\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Geo Sot",
|
||||
"email": "geo.sotis@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A laravel Package that Supports .Env File editing and backup ",
|
||||
"keywords": [
|
||||
"EnvEditor",
|
||||
"geo-sot",
|
||||
"laravel",
|
||||
"laravel-env-editor"
|
||||
],
|
||||
"time": "2020-04-17T23:33:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "graham-campbell/markdown",
|
||||
"version": "v12.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/GrahamCampbell/Laravel-Markdown.git",
|
||||
"reference": "584eb9f24004238b80ee98b6e7be82f0933554dd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/GrahamCampbell/Laravel-Markdown/zipball/584eb9f24004238b80ee98b6e7be82f0933554dd",
|
||||
"reference": "584eb9f24004238b80ee98b6e7be82f0933554dd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/contracts": "^6.0|^7.0",
|
||||
"illuminate/support": "^6.0|^7.0",
|
||||
"illuminate/view": "^6.0|^7.0",
|
||||
"league/commonmark": "^1.3",
|
||||
"php": "^7.2.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"graham-campbell/analyzer": "^3.0",
|
||||
"graham-campbell/testbench": "^5.4",
|
||||
"mockery/mockery": "^1.3.1",
|
||||
"phpunit/phpunit": "^8.5|^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "12.0-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"GrahamCampbell\\Markdown\\MarkdownServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GrahamCampbell\\Markdown\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Graham Campbell",
|
||||
"email": "graham@alt-three.com"
|
||||
}
|
||||
],
|
||||
"description": "Markdown Is A CommonMark Wrapper For Laravel",
|
||||
"keywords": [
|
||||
"Graham Campbell",
|
||||
"GrahamCampbell",
|
||||
"Laravel Markdown",
|
||||
"Laravel-Markdown",
|
||||
"common mark",
|
||||
"commonmark",
|
||||
"framework",
|
||||
"laravel",
|
||||
"markdown"
|
||||
],
|
||||
"time": "2020-04-14T16:14:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "6.5.4",
|
||||
|
@@ -251,6 +251,12 @@ return [
|
||||
'header' => 'Application Management',
|
||||
'can' => ['applications.view.all', 'applications.vote']
|
||||
],
|
||||
[
|
||||
'text' => 'All applications',
|
||||
'url' => 'applications/staff/all',
|
||||
'icon' => 'fas fa-list-ol',
|
||||
'can' => 'applications.view.all'
|
||||
],
|
||||
[
|
||||
'text' => 'Outstanding Applications',
|
||||
'url' => '/applications/staff/outstanding',
|
||||
@@ -521,6 +527,7 @@ return [
|
||||
'location' => 'https://cdn.jsdelivr.net/npm/fullcalendar@5.0.1/main.min.css'
|
||||
]
|
||||
]
|
||||
]
|
||||
],
|
||||
|
||||
],
|
||||
];
|
||||
|
@@ -164,7 +164,9 @@ return [
|
||||
|
||||
/*
|
||||
* Package Service Providers...
|
||||
|
||||
*/
|
||||
GrahamCampbell\Markdown\MarkdownServiceProvider::class,
|
||||
|
||||
/*
|
||||
* Application Service Providers...
|
||||
@@ -173,6 +175,7 @@ return [
|
||||
App\Providers\AuthServiceProvider::class,
|
||||
App\Providers\UUIDConversionProvider::class,
|
||||
App\Providers\IPInfoProvider::class,
|
||||
App\Providers\ContextAwareValidatorProvider::class,
|
||||
// App\Providers\BroadcastServiceProvider::class,
|
||||
App\Providers\EventServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
@@ -230,7 +233,9 @@ return [
|
||||
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||
'View' => Illuminate\Support\Facades\View::class,
|
||||
'UUID' => App\Facades\UUID::class,
|
||||
'IP' => App\Facades\IP::class
|
||||
'IP' => App\Facades\IP::class,
|
||||
'Markdown' => GrahamCampbell\Markdown\Facades\Markdown::class,
|
||||
'ContextAwareValidator' => App\Facades\ContextAwareValidation::class
|
||||
|
||||
],
|
||||
|
||||
|
158
config/markdown.php
Normal file
158
config/markdown.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of Laravel Markdown.
|
||||
*
|
||||
* (c) Graham Campbell <graham@alt-three.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enable View Integration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option specifies if the view integration is enabled so you can write
|
||||
| markdown views and have them rendered as html. The following extensions
|
||||
| are currently supported: ".md", ".md.php", and ".md.blade.php". You may
|
||||
| disable this integration if it is conflicting with another package.
|
||||
|
|
||||
| Default: true
|
||||
|
|
||||
*/
|
||||
|
||||
'views' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| CommonMark Extensions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option specifies what extensions will be automatically enabled.
|
||||
| Simply provide your extension class names here.
|
||||
|
|
||||
| Default: []
|
||||
|
|
||||
*/
|
||||
|
||||
'extensions' => [],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Renderer Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option specifies an array of options for rendering HTML.
|
||||
|
|
||||
| Default: [
|
||||
| 'block_separator' => "\n",
|
||||
| 'inner_separator' => "\n",
|
||||
| 'soft_break' => "\n",
|
||||
| ]
|
||||
|
|
||||
*/
|
||||
|
||||
'renderer' => [
|
||||
'block_separator' => "\n",
|
||||
'inner_separator' => "\n",
|
||||
'soft_break' => "\n",
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enable Em Tag Parsing
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option specifies if `<em>` parsing is enabled.
|
||||
|
|
||||
| Default: true
|
||||
|
|
||||
*/
|
||||
|
||||
'enable_em' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enable Strong Tag Parsing
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option specifies if `<strong>` parsing is enabled.
|
||||
|
|
||||
| Default: true
|
||||
|
|
||||
*/
|
||||
|
||||
'enable_strong' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enable Asterisk Parsing
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option specifies if `*` should be parsed for emphasis.
|
||||
|
|
||||
| Default: true
|
||||
|
|
||||
*/
|
||||
|
||||
'use_asterisk' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Enable Underscore Parsing
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option specifies if `_` should be parsed for emphasis.
|
||||
|
|
||||
| Default: true
|
||||
|
|
||||
*/
|
||||
|
||||
'use_underscore' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTML Input
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option specifies how to handle untrusted HTML input.
|
||||
|
|
||||
| Default: 'strip'
|
||||
|
|
||||
*/
|
||||
|
||||
'html_input' => 'strip',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Allow Unsafe Links
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option specifies whether to allow risky image URLs and links.
|
||||
|
|
||||
| Default: true
|
||||
|
|
||||
*/
|
||||
|
||||
'allow_unsafe_links' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maximum Nesting Level
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option specifies the maximum permitted block nesting level.
|
||||
|
|
||||
| Default: INF
|
||||
|
|
||||
*/
|
||||
|
||||
'max_nesting_level' => INF,
|
||||
|
||||
];
|
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddDetailedDescriptionToVacancy extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('vacancies', function (Blueprint $table) {
|
||||
$table->longText('vacancyFullDescription')->nullable()->after('vacancyDescription');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('vacancies', function (Blueprint $table) {
|
||||
$table->dropColumn('vacancyFullDescription');
|
||||
});
|
||||
}
|
||||
}
|
9
install.sh
Executable file
9
install.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "Preliminary installation starting!"
|
||||
sleep 3
|
||||
composer install
|
||||
|
||||
echo "Full installation starting!"
|
||||
sleep 3
|
||||
php artisan application:install
|
10395
package-lock.json
generated
10395
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
1
public/css/home.css
vendored
Normal file
1
public/css/home.css
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.full-img-bg
|
1
public/img/403.svg
Normal file
1
public/img/403.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 17 KiB |
1
public/img/applications_all.svg
Normal file
1
public/img/applications_all.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 8.1 KiB |
1
public/img/editor.svg
Normal file
1
public/img/editor.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="a5860ba0-ef67-4914-ac96-07e2ebcccb4f" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="1095.74023" height="664.03433" viewBox="0 0 1095.74023 664.03433"><title>abstract</title><rect x="701" y="661" width="394.74023" height="2" fill="#3f3d56"/><polygon points="474 429 474 216 718 216 718 0 0 0 0 643 718 643 718 429 474 429" fill="#3f3d56"/><polygon points="718 216 718 0 469 0 469 213 249 213 249 0 0 0 0 216 225 216 225 429 0 429 0 643 476 643 476 429 474 429 474 216 718 216" opacity="0.1"/><path d="M224.12988,117.98284a172.00382,172.00382,0,0,1-172,172v-172Z" transform="translate(-52.12988 -117.98284)" fill="#6c63ff"/><path d="M132.12988,117.98284a80.00357,80.00357,0,0,1-80,80v44a124.00426,124.00426,0,0,0,124-124Z" transform="translate(-52.12988 -117.98284)" opacity="0.1"/><polygon points="718 60 718 29 553 29 553 0 517 0 517 29 517 60 517 84 517 115 553 115 718 115 718 84 553 84 553 60 718 60" fill="#d0cde1"/><path d="M481.12988,448.98284a81,81,0,1,0-92,80.23639V642.98284H256.36627a81,81,0,1,0,0,22H389.12988v48h22v-48h45v-22h-45V529.21923A81.00034,81.00034,0,0,0,481.12988,448.98284Z" transform="translate(-52.12988 -117.98284)" fill="#d0cde1"/><polygon points="626.5 445.263 662.508 507.631 698.517 570 626.5 570 554.483 570 590.492 507.631 626.5 445.263" fill="#d0cde1"/><polygon points="610.5 578 538.483 578 574.492 515.631 574.742 515.198 561.5 492.263 525.492 554.631 489.483 617 561.5 617 633.517 617 611 578 610.5 578" fill="#6c63ff"/><polygon points="159 345 159 291 186 291 186 259 52 259 52 393 186 393 186 345 159 345" fill="#d0cde1"/><rect x="89" y="234" width="50" height="50" fill="#6c63ff"/><path d="M989.162,357.83165,972.618,401.5551l-59.664,20.935s-36-4-36,16c0,14.18058,43,2,43,2l78.66169-23.57271,24.816-61.44917Z" transform="translate(-52.12988 -117.98284)" fill="#ffb8b8"/><rect x="635" y="228" width="234" height="202" fill="#6c63ff"/><polygon points="747.5 289.263 783.508 351.631 819.517 414 747.5 414 675.483 414 711.492 351.631 747.5 289.263" fill="#3f3d56"/><polygon points="747.331 334.415 763.665 362.708 780 391 747.331 391 714.661 391 730.996 362.708 747.331 334.415" fill="#d0cde1"/><polygon points="747.331 242.415 763.665 270.708 780 299 747.331 299 714.661 299 730.996 270.708 747.331 242.415" fill="#d0cde1"/><path d="M1024.15387,329.42035s-16.544-2.36343-24.816,28.36116-10.63544,33.088-10.63544,33.088,48.45031,7.09029,49.632,4.72686S1048.96988,336.51064,1024.15387,329.42035Z" transform="translate(-52.12988 -117.98284)" fill="#2f2e41"/><circle cx="962.57027" cy="167.71406" r="31.9063" fill="#ffb8b8"/><path d="M1000.51957,311.69463s10.63544,27.17944,7.09029,30.72459,43.72345,8.272,43.72345,8.272,3.54515-17.72572-10.63543-28.36116c0,0-5.90858-17.72572-4.72686-21.27087S1000.51957,311.69463,1000.51957,311.69463Z" transform="translate(-52.12988 -117.98284)" fill="#ffb8b8"/><path d="M1028.88073,323.51178a85.216,85.216,0,0,0-31.9063,21.27087c-14.18058,15.36229-21.27087,20.08915-21.27087,20.08915s-16.544,5.90857-10.63544,34.26973c2.95429,14.18058.88629,30.13373-1.92028,42.54174a265.8223,265.8223,0,0,0-6.35172,58.65559v12.2473s-108.71777,228.071-72.08461,243.43328,69.72118,20.08915,72.08461,16.544,44.90517-187.89267,44.90517-187.89267,11.81715,197.34639,41.36,197.34639,77.99318-8.272,77.99318-8.272l-60.26746-236.343s14.18058-40.17831-21.27087-68.53947l-4.72686-12.99886c19.60447.01,28.56614-124.06968,8.272-134.7155C1043.06131,321.14835,1038.33445,317.6032,1028.88073,323.51178Z" transform="translate(-52.12988 -117.98284)" fill="#2f2e41"/><path d="M999.33786,387.32438l-16.544,43.72345-59.664,20.935s-36-4-36,16c0,14.18058,43,2,43,2l78.6617-23.57271,24.816-61.44918Z" transform="translate(-52.12988 -117.98284)" fill="#ffb8b8"/><ellipse cx="868.62393" cy="652.21718" rx="20.68001" ry="11.81715" fill="#2f2e41"/><ellipse cx="1026.97373" cy="652.21718" rx="20.68001" ry="11.81715" fill="#2f2e41"/><path d="M1027.01734,240.95422h-.00012a25.34145,25.34145,0,0,0-4.89258.48651,21.44172,21.44172,0,0,0-9.34021-2.15271h-.84777c-16.34473,0-29.59461,14.7901-29.59461,33.03461v.00006h5.47669l.88452-6.73224L990,272.32269h4.56226a64.57589,64.57589,0,0,0-2.119,16.55036v33.62384h8.28027l4.79395-12.45795-1.19849,12.45795h53.22314l4.35816-11.32538-1.0896,11.32538h5.99243V296.09613C1066.80311,265.64215,1048.99037,240.95422,1027.01734,240.95422Z" transform="translate(-52.12988 -117.98284)" fill="#2f2e41"/></svg>
|
After Width: | Height: | Size: 4.3 KiB |
1
public/img/placeholders.svg
Normal file
1
public/img/placeholders.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 7.1 KiB |
1
public/img/preview.svg
Normal file
1
public/img/preview.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 7.3 KiB |
1
public/js/formbuilder.js
vendored
1
public/js/formbuilder.js
vendored
@@ -1,3 +1,4 @@
|
||||
// TODO: Add cleaner and less verbose solution found in formeditor.js
|
||||
$(document).ready(function() {
|
||||
$("#add").click(function() {
|
||||
var lastField = $("#buildyourform div:last");
|
||||
|
19
public/js/formeditor.js
vendored
Normal file
19
public/js/formeditor.js
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// reminder: use vuejs instead, this is still an ugly and cheap solution
|
||||
$(document).ready(function(){
|
||||
|
||||
var fieldID = 0;
|
||||
var wrapper = $('.field-container');
|
||||
var newBtn = $('#add');
|
||||
|
||||
$(newBtn).click(function(e){
|
||||
e.preventDefault()
|
||||
fieldID++;
|
||||
|
||||
$(wrapper).append('<div id=group' + fieldID + '><input type="text" name="newFieldID' + fieldID + '[]" class="form-control" />');
|
||||
$(wrapper).append('<select name="newFieldID' + fieldID + '[]" class="custom-select"> <option value="nil" disabled>Choose a type</option> <option value="textbox">Textbox</option> <option value="textarea">Multi line answer</option> <option value="checkbox">Checkbox</option> </select>');
|
||||
//$(wrapper).append('<button type="button" class="btn btn-danger btn-sm float-right delete"><i class="fas fa-minus"></i></button></div>');
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
@@ -1,9 +1,22 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
|
||||
<x-global-errors></x-global-errors>
|
||||
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
|
||||
@if (session()->has('error'))
|
||||
<div class="alert alert-danger">
|
||||
<i class="fas fa-exclamation"></i><b> Please verify your submission</b>
|
||||
<p>
|
||||
{{ session('error') }}
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">{{ __('Register') }}</div>
|
||||
|
||||
|
32
resources/views/components/form.blade.php
Normal file
32
resources/views/components/form.blade.php
Normal file
@@ -0,0 +1,32 @@
|
||||
@foreach($form['fields'] as $fieldName => $field)
|
||||
|
||||
@switch ($field['type'])
|
||||
|
||||
@case('textarea')
|
||||
|
||||
<div class="form-group mt-2 mb-2">
|
||||
|
||||
<label for="{{$fieldName}}">{{$field['title']}}</label>
|
||||
<textarea class="form-control" rows="7" name="{{$fieldName}}" id="{{$fieldName}}" {{ ($disableFields) ? 'disabled' : '' }}>
|
||||
|
||||
</textarea>
|
||||
|
||||
</div>
|
||||
|
||||
@break
|
||||
|
||||
@case('textbox')
|
||||
|
||||
<div class="form-group mt-2 mb-2">
|
||||
|
||||
<label for="{{$fieldName}}">{{$field['title']}}</label>
|
||||
<input type="text" name="{{$fieldName}}" id="{{$fieldName}}" {{ ($disableFields) ? 'disabled' : '' }} class="form-control">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@break
|
||||
|
||||
@endswitch
|
||||
|
||||
@endforeach
|
74
resources/views/components/no-permission.blade.php
Normal file
74
resources/views/components/no-permission.blade.php
Normal file
@@ -0,0 +1,74 @@
|
||||
@if ($inDashboard)
|
||||
<div class="row mb-4">
|
||||
|
||||
<div class="col-6 offset-5">
|
||||
|
||||
|
||||
<img src="/img/403.svg" width="150px" alt="Access denied" />
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- People find pleasure in different ways. I find it in keeping my mind clear. - Marcus Aurelius -->
|
||||
<div class="col">
|
||||
|
||||
<div class="alert alert-{{$type}}">
|
||||
<h4><i class="fas fa-user-lock"></i> Access Denied</h2>
|
||||
<p>
|
||||
We're sorry, but you do not have permission to access this web page.
|
||||
</p>
|
||||
<p>
|
||||
Please contact your administrator if you believe this was in error.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@else
|
||||
@extends('adminlte::page')
|
||||
|
||||
@section('title', 'Raspberry Network | Access Denied')
|
||||
|
||||
@section('content_header')
|
||||
<h4>Access Denied - HTTP 403</h4>
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
<div class="row mb-4">
|
||||
|
||||
<div class="col-6 offset-5">
|
||||
|
||||
|
||||
<img src="/img/403.svg" width="150px" alt="Access denied" />
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
|
||||
<div class="alert alert-{{$type}}">
|
||||
<h4><i class="fas fa-user-lock"></i> Access Denied</h2>
|
||||
<p class="text-muted">
|
||||
@if (isset($slot))
|
||||
{{ $slot }}
|
||||
@endif
|
||||
</p>
|
||||
<p>
|
||||
We're sorry, but you do not have permission to access this web page.
|
||||
</p>
|
||||
<p>
|
||||
Please contact your administrator if you believe this was in error.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@stop
|
||||
@endif
|
103
resources/views/dashboard/administration/editform.blade.php
Normal file
103
resources/views/dashboard/administration/editform.blade.php
Normal file
@@ -0,0 +1,103 @@
|
||||
@extends('adminlte::page')
|
||||
|
||||
@section('title', 'Raspberry Network | Edit From')
|
||||
|
||||
@section('content_header')
|
||||
|
||||
<h4>Administration / Forms / Editor</h4>
|
||||
|
||||
@stop
|
||||
|
||||
@section('js')
|
||||
|
||||
<script src="/js/formeditor.js"></script>
|
||||
<x-global-errors></x-global-errors>
|
||||
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="row mb-5">
|
||||
<div class="col">
|
||||
|
||||
</div>
|
||||
<div class="col text-center">
|
||||
|
||||
<img src="/img/editor.svg" width="250px" alt="Editor illustration" class="img-responsive" />
|
||||
|
||||
</div>
|
||||
<div class="col">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col">
|
||||
|
||||
<form id="editForm" method="POST" action="{{ route('updateForm', ['form' => $formID]) }}">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<div class="card">
|
||||
|
||||
<div class="card-header">
|
||||
|
||||
<h4>Editing {{ $title }}...</h4>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
@foreach($formStructure['fields'] as $fieldName => $field)
|
||||
|
||||
<div class="form-group mt-4 mb-4">
|
||||
|
||||
<input autocomplete="false" type="text" id="{{ $fieldName }}" class="form-control" name="{{ $fieldName }}[]" value="{{ $field['title'] }}" />
|
||||
|
||||
<select class="custom-select" id="{{ $fieldName }}-type" name="{{ $fieldName }}[]">
|
||||
|
||||
<option value="nil" disabled>Choose a type</option>
|
||||
<option value="textbox" {{ ($field['type'] == 'textbox' ? 'selected' : '') }}>Textbox</option>
|
||||
<option value="textarea" {{ ($field['type'] == 'textarea' ? 'selected' : '') }}>Multi line answer</option>
|
||||
<option value="checkbox" {{ ($field['type'] == 'checkbox' ? 'selected' : '') }}>Checkbox</option>
|
||||
|
||||
</select>
|
||||
|
||||
</div>
|
||||
|
||||
@endforeach
|
||||
|
||||
<div class="field-container mt-4 mb-4">
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-center">
|
||||
|
||||
<button type="button" class="btn btn-warning ml-2" onclick="$('#editForm').submit()"><i class="fas fa-save"></i> Save & Quit</button>
|
||||
<button type="button" class="btn btn-primary ml-2" id="add"><i class="fas fa-plus"></i> New field</button>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@stop
|
155
resources/views/dashboard/administration/editposition.blade.php
Normal file
155
resources/views/dashboard/administration/editposition.blade.php
Normal file
@@ -0,0 +1,155 @@
|
||||
@extends('adminlte::page')
|
||||
|
||||
@section('title', 'Raspberry Network | Edit Positions')
|
||||
|
||||
@section('content_header')
|
||||
|
||||
<h4>Administration / Positions / Edit</h4>
|
||||
|
||||
@stop
|
||||
|
||||
@section('js')
|
||||
|
||||
<x-global-errors>
|
||||
|
||||
</x-global-errors>
|
||||
|
||||
@stop
|
||||
|
||||
|
||||
@section('content')
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col center">
|
||||
|
||||
<h3>Vacancy Editor</h3>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="col">
|
||||
|
||||
|
||||
<div class="card">
|
||||
|
||||
|
||||
<div class="card-header">
|
||||
|
||||
<h3 class="card-title"><i class="fas fa-clipboard"></i> {{ $vacancy->vacancyName }}</h3>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<p class="text-muted"><i class="fas fa-question-circle"></i> For consistency purposes, grayed out fields can't be edited.</p>
|
||||
|
||||
<form method="POST" id="editPositionForm" action="{{ route('updatePosition', ['position' => $vacancy->id]) }}">
|
||||
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
|
||||
<label for="vacancyName">Vacancy Name</label>
|
||||
<input type="text" value="{{ $vacancy->vacancyName }}" class="form-control" disabled />
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
|
||||
<label for="vacancyDescription">Vacancy description</label>
|
||||
<input type="vacancyDescription" class="form-control" name="vacancyDescription" value="{{ $vacancy->vacancyDescription }}" />
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
<!-- skipping the accessor for obvious reasons -->
|
||||
<label for="vacanyDetails">Vacancy details</label>
|
||||
<textarea name="vacancyFullDescription" class="form-control" placeholder="{{ (is_null($vacancy->vacancyFullDescription)) ? 'No details yet. Add some!' : '' }}" rows="20">{{ $vacancy->getAttributes()['vacancyFullDescription'] }}</textarea>
|
||||
<span class="text-muted"><i class="fab fa-markdown"></i> Markdown supported</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
<label for "permissionGroupName">Permission Group</label>
|
||||
<input type="text" class="form-control" value="{{ $vacancy->permissionGroupName }}" id="permissionGroupName" disabled />
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
|
||||
<label for "discordRoleID">Discord Role ID</label>
|
||||
<input type="text" class="form-control" value="{{ $vacancy->discordRoleID }}" id="discordRoleID" disabled />
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
<label for "currentForm">Current Form (uneditable)</label>
|
||||
<input type="text" class="form-control" value="{{ $vacancy->forms->formName }}" id="currentForm" disabled />
|
||||
|
||||
<label for "remainingSlots">Remaining slots</label>
|
||||
<input type="text" class="form-control" value="{{ $vacancy->vacancyCount }}" id="remainingSlots" name="vacancyCount" />
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
|
||||
<button type="button" class="btn btn-warning" onclick="$('#editPositionForm').submit()"><i class="fas fa-edit"></i> Save Changes</button>
|
||||
<button type="button" class="btn btn-danger" onclick="window.location.href='{{ route('showPositions') }}'"><i class="fas fa-times"></i> Cancel</button>
|
||||
|
||||
@if($vacancy->vacancyStatus == 'OPEN')
|
||||
|
||||
<form method="POST" action="{{ route('updatePositionAvailability', ['id' => $vacancy->id, 'status' => 'close']) }}" style="display: inline">
|
||||
@method('PATCH')
|
||||
@csrf
|
||||
<button type="submit" class="ml-4 btn btn-danger"><i class="fas fa-ban"></i> Close Position</button>
|
||||
</form>
|
||||
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@stop
|
@@ -0,0 +1,79 @@
|
||||
@extends('adminlte::page')
|
||||
|
||||
@section('title', 'Raspberry Network | Application Form Preview')
|
||||
|
||||
@section('content_header')
|
||||
|
||||
<h4>Administration / Form Builder / Preview</h4>
|
||||
|
||||
@stop
|
||||
|
||||
|
||||
@section('js')
|
||||
|
||||
<x-global-errors></x-global-errors>
|
||||
|
||||
@stop
|
||||
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-6 offset-4">
|
||||
|
||||
<img src="/img/preview.svg" width="250px" alt="Form preview illustration" />
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
|
||||
<div class="col">
|
||||
<div class="alert alert-success">
|
||||
|
||||
<h5><i class="fas fa-eye"></i> This is how your form looks like to applicants</h3>
|
||||
|
||||
<p>
|
||||
You may edit it and add more fields later.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
<div class="card">
|
||||
|
||||
<div class="card-header">
|
||||
|
||||
<h3>{{ $title }}'s form</h2>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
@component('components.form', ['form' => $form, 'disableFields' => true])
|
||||
|
||||
@endcomponent
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-center">
|
||||
|
||||
<button type="button" class="btn btn-success ml-2" onclick="window.location.href='{{ route('showForms') }}'"><i class="fas fa-chevron-left"></i> Go back</button>
|
||||
<button type="button" class="btn btn-warning ml-2" onclick="window.location.href='{{ route('editForm', ['form' => $formID]) }}'"><i class="far fa-edit"></i> Edit</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@stop
|
@@ -38,6 +38,7 @@
|
||||
<th>#</th>
|
||||
<th>Form Title</th>
|
||||
<th>Created On</th>
|
||||
<th>Updated On</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
|
||||
@@ -51,6 +52,7 @@
|
||||
<td>{{$form->id}}</td>
|
||||
<td>{{$form->formName}}</td>
|
||||
<td>{{$form->created_at}}</td>
|
||||
<td>{{ $form->updated_at }}</td>
|
||||
<td>
|
||||
<form style="display: inline-block; white-space: nowrap" action="{{route('destroyForm', ['id' => $form->id])}}" method="POST">
|
||||
|
||||
@@ -59,7 +61,7 @@
|
||||
|
||||
<button type="submit" class="btn btn-sm btn-danger mr-2"><i class="fa fa-trash"></i> Delete</button>
|
||||
</form>
|
||||
<button type="button" class="btn btn-sm btn-success"><i class="fa fa-eye"></i> Preview</button>
|
||||
<button type="button" class="btn btn-sm btn-success" onclick="window.location.href='{{ route('previewForm', ['form' => $form->id]) }}'"><i class="fa fa-eye"></i> Preview</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
@@ -4,7 +4,11 @@
|
||||
|
||||
@section('content_header')
|
||||
|
||||
<h4>Administration / Open Positions</h4>
|
||||
@if (Auth::user()->hasAnyRole('admin', 'hiringManager'))
|
||||
<h4>Administration / Open Positions</h4>
|
||||
@else
|
||||
<h4>Application Access Denied</h4>
|
||||
@endif
|
||||
|
||||
@stop
|
||||
|
||||
@@ -33,7 +37,8 @@
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
|
||||
@if (Auth::user()->hasAnyRole('admin', 'hiringManager'))
|
||||
<!-- todo: switch to modal component -->
|
||||
<div class="modal fade" tabindex="-1" id="newVacancyForm" role="dialog" aria-labelledby="modalFormLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
@@ -55,6 +60,9 @@
|
||||
<label for="vacancyDescription">Vacancy Description</label>
|
||||
<input type="text" id="vacancyDescription" name="vacancyDescription" class="form-control">
|
||||
|
||||
<label for="vacancyFullDescription">Vacancy Details</label>
|
||||
<textarea name="vacancyFullDescription" class="form-control" rel="txtTooltip" title="Add things like admission requirements, rank resposibilities and roles, and anything else you feel is necessary" data-toggle="tooltip" data-placement="bottom"></textarea>
|
||||
<span class="right text-muted"><i class="fab fa-markdown"></i> Markdown supported</span>
|
||||
<div class="row mt-3">
|
||||
|
||||
<div class="col">
|
||||
@@ -153,9 +161,8 @@
|
||||
<thead>
|
||||
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Vacancy Name</th>
|
||||
<th>Vacancy Description</th>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Discord Role ID</th>
|
||||
<th>Perm. Group Name</th>
|
||||
<th>Open Slots</th>
|
||||
@@ -171,8 +178,6 @@
|
||||
@foreach($vacancies as $vacancy)
|
||||
|
||||
<tr>
|
||||
|
||||
<td>{{$vacancy->id}}</td>
|
||||
<td>{{$vacancy->vacancyName}}</td>
|
||||
<td>{{substr($vacancy->vacancyDescription, 0, 20)}}...</td>
|
||||
<td><span class="badge badge-success">{{$vacancy->discordRoleID}}</span></td>
|
||||
@@ -185,20 +190,23 @@
|
||||
@endif
|
||||
<td>{{$vacancy->created_at}}</td>
|
||||
<td>
|
||||
|
||||
<button type="button" class="btn btn-sm btn-warning" onclick="window.location.href='{{ route('editPosition', ['position' => $vacancy->id]) }}'"><i class="fas fa-edit"></i></button>
|
||||
|
||||
@if ($vacancy->vacancyStatus == 'OPEN')
|
||||
|
||||
<form action="{{route('updatePositionAvailability', ['status' => 'close', 'id' => $vacancy->id])}}" method="POST" id="closePosition">
|
||||
<form action="{{route('updatePositionAvailability', ['status' => 'close', 'id' => $vacancy->id])}}" method="POST" id="closePosition" style="display: inline">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<button type="submit" class="btn btn-sm btn-danger"><i class="fa fa-ban"></i> Close</button>
|
||||
<button type="submit" class="btn btn-sm btn-danger"><i class="fa fa-ban"></i></button>
|
||||
</form>
|
||||
|
||||
@else
|
||||
|
||||
<form action="{{route('updatePositionAvailability', ['status' => 'open', 'id' => $vacancy->id])}}" method="POST" id="openPosition">
|
||||
<form action="{{route('updatePositionAvailability', ['status' => 'open', 'id' => $vacancy->id])}}" method="POST" id="openPosition" style="display: inline">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<button type="submit" class="btn btn-sm btn-success"><i class="fa fa-check"></i> Open</button>
|
||||
<button type="submit" class="btn btn-sm btn-success"><i class="fa fa-check"></i></button>
|
||||
</form>
|
||||
|
||||
@endif
|
||||
@@ -232,5 +240,7 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@else
|
||||
<x-no-permission type="danger"></x-no-permission>
|
||||
@endif
|
||||
@stop
|
||||
|
@@ -93,38 +93,10 @@
|
||||
|
||||
<form action="{{route('saveApplicationForm', ['vacancySlug' => $vacancy->vacancySlug])}}" method="POST" id="submitApplicationForm">
|
||||
@csrf
|
||||
@foreach($preprocessedForm['fields'] as $fieldName => $field)
|
||||
|
||||
@switch ($field['type'])
|
||||
@component('components.form', ['form' => $preprocessedForm, 'disableFields' => false])
|
||||
|
||||
@case('textarea')
|
||||
|
||||
<div class="form-group mt-2 mb-2">
|
||||
|
||||
<label for="{{$fieldName}}">{{$field['title']}}</label>
|
||||
<textarea class="form-control" rows="7" name="{{$fieldName}}" id="{{$fieldName}}">
|
||||
|
||||
</textarea>
|
||||
|
||||
</div>
|
||||
|
||||
@break
|
||||
|
||||
@case('textbox')
|
||||
|
||||
<div class="form-group mt-2 mb-2">
|
||||
|
||||
<label for="{{$fieldName}}">{{$field['title']}}</label>
|
||||
<input type="text" name="{{$fieldName}}" id="{{$fieldName}}" class="form-control">
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
@break
|
||||
|
||||
@endswitch
|
||||
|
||||
@endforeach
|
||||
@endcomponent
|
||||
|
||||
</form>
|
||||
|
||||
|
243
resources/views/dashboard/appmanagement/all.blade.php
Normal file
243
resources/views/dashboard/appmanagement/all.blade.php
Normal file
@@ -0,0 +1,243 @@
|
||||
@extends('adminlte::page')
|
||||
|
||||
@section('title', 'Raspberry Network | Profile')
|
||||
|
||||
@section('content_header')
|
||||
|
||||
<h4>Application Management / All Applications</h4>
|
||||
|
||||
@stop
|
||||
|
||||
@section('js')
|
||||
|
||||
<script type="text/javascript" src="/js/app.js"></script>
|
||||
<x-global-errors></x-global-errors>
|
||||
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
|
||||
|
||||
@foreach($applications as $application)
|
||||
|
||||
<x-modal id="deletionConfirmationModal-{{ $application->id }}" modal-label="deletion-{{ $application->id }}" modal-title="Are you sure?" include-close-button="true">
|
||||
|
||||
<h4><i class="fas fa-exclamation-triangle"></i> Really delete this?</h3>
|
||||
<p>
|
||||
This action is <b>IRREVERSBILE.</b>
|
||||
</p>
|
||||
<p>Comments, appointments and any votes attached to this application WILL be deleted too. Please make sure this application really needs to be deleted.</p>
|
||||
|
||||
<x-slot name="modalFooter">
|
||||
|
||||
<form method="POST" action="{{ route('deleteApplication', ['application' => $application->id]) }}">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-danger"><i class="fas fa-check-double"></i> Confirm</button>
|
||||
|
||||
</form>
|
||||
|
||||
</x-slot>
|
||||
|
||||
</x-modal>
|
||||
|
||||
@endforeach
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
|
||||
<div class="callout callout-info">
|
||||
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="col-3">
|
||||
|
||||
<img src="/img/applications_all.svg" alt="Applications illustration" class="img-responsive" width="200px"/>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
|
||||
<h3><i class="fas fa-info-circle"></i> You're looking at all applications ever received</h3>
|
||||
<p>
|
||||
Here, you have quick and easy access to all applications ever received by the system.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="row mt-5">
|
||||
|
||||
<div class="col">
|
||||
|
||||
<div class="card">
|
||||
<!-- MAIN CONTENT - APPS AND PICS -->
|
||||
|
||||
<div class="card-header">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-3">
|
||||
<h3>All applications</h3>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
|
||||
<div class="navbtn right" style="whitespace: nowrap">
|
||||
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="window.location.href='{{ route('staffPendingApps') }}'"><i class="far fa-folder-open"></i> Outstanding Applications</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="window.location.href='{{ route('pendingInterview') }}'"><i class="fas fa-microphone-alt"></i> Interview Queue</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="window.location.href='{{ route('peerReview') }}'"><i class="fas fa-search"></i> Peer Review</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-3 center">
|
||||
|
||||
<img src="/img/placeholders.svg" alt="Placeholder illustration" class="img-responsive" width="200px"/>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col">
|
||||
|
||||
@if (!$applications->isEmpty())
|
||||
|
||||
<table class="table table-borderless" style="whitespace: nowrap">
|
||||
|
||||
<thead>
|
||||
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Applicant</th>
|
||||
<th>Status</th>
|
||||
<th>Date</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
|
||||
@foreach($applications as $application)
|
||||
|
||||
<tr>
|
||||
<td>{{ $application->id }}</td>
|
||||
<td><a href="{{ route('showSingleProfile', ['user' => $application->user->id]) }}">{{ $application->user->name }}</a></td>
|
||||
<td>
|
||||
@switch($application->applicationStatus)
|
||||
|
||||
@case('STAGE_SUBMITTED')
|
||||
|
||||
<span class="badge badge-primary"><i class="far fa-clock"></i> Outstanding (Submitted)</span>
|
||||
@break
|
||||
|
||||
@case('STAGE_PEERAPPROVAL')
|
||||
|
||||
<span class="badge badge-warning"><i class="fas fa-vote-yea"></i> Peer Approval</span>
|
||||
@break
|
||||
|
||||
@case('STAGE_INTERVIEW')
|
||||
|
||||
<span class="badge badge-warning"><i class="fas fa-microphone-alt"></i> Interview</span>
|
||||
|
||||
@break
|
||||
|
||||
@case('STAGE_INTERVIEW_SCHEDULED')
|
||||
|
||||
<span class="badge badge-warning"><i class="far fa-clock"></i>Interview Scheduled</span>
|
||||
|
||||
@break
|
||||
|
||||
@case('APPROVED')
|
||||
|
||||
<span class="badge badge-success"><i class="fas fa-check"></i> Approved</span>
|
||||
|
||||
@break
|
||||
|
||||
@case('DENIED')
|
||||
|
||||
<span class="badge badge-danger"><i class="fas fa-times"></i> Denied</span>
|
||||
|
||||
@break;
|
||||
|
||||
@default
|
||||
<span class="badge badge-secondary"><i class="fas fa-question-circle"></i> Unknown</span>
|
||||
|
||||
|
||||
@endswitch
|
||||
</td>
|
||||
<td>{{ $application->created_at }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-success btn-sm" onclick="window.location.href='{{ route('showUserApp', ['id' => $application->id]) }}'"><i class="fas fa-eye"></i> View</button>
|
||||
<button type="button" class="btn btn-danger btn-sm ml-2" onclick="$('#deletionConfirmationModal-{{ $application->id }}').modal('show')"><i class="fa fa-trash"></i> Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@endforeach
|
||||
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
@else
|
||||
|
||||
<div class="alert alert-warning">
|
||||
|
||||
<h3><i class="fas fa-question-circle"></i> There are no applications here</h3>
|
||||
<p>
|
||||
We couldn't find any applications. Maybe no one has applied yet?
|
||||
Please try again later.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!-- end main content card -->
|
||||
</div>
|
||||
|
||||
@if (!$applications->isEmpty() && isset($applications->links))
|
||||
|
||||
<div class="card-footer">
|
||||
|
||||
{{ $applications->links }}
|
||||
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@stop
|
@@ -14,6 +14,38 @@
|
||||
|
||||
@section('content')
|
||||
|
||||
@if (!$vacancies->isEmpty())
|
||||
|
||||
@foreach($vacancies as $vacancy)
|
||||
|
||||
<x-modal id="{{ $vacancy->vacancySlug . '-details' }}" modal-label="{{ $vacancy->vacancySlug . '-details-label' }}" modal-title="Vacancy details" include-close-button="true">
|
||||
|
||||
@if (is_null($vacancy->vacancyFullDescription))
|
||||
|
||||
<div class="alert alert-warning">
|
||||
|
||||
<h3><i class="fas fa-question-circle"></i> There don't seem to be any details</h3>
|
||||
<p>
|
||||
This vacancy does not have any details yet.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
@else
|
||||
|
||||
{!! $vacancy->vacancyFullDescription !!}
|
||||
<p class="text-sm text-muted">
|
||||
Last updated @ {{ $vacancy->updated_at }}
|
||||
</p>
|
||||
@endif
|
||||
|
||||
<x-slot name="modalFooter"></x-slot>
|
||||
|
||||
</x-modal>
|
||||
|
||||
@endforeach
|
||||
|
||||
@endif
|
||||
|
||||
<div class="row mt-5">
|
||||
|
||||
<div class="col">
|
||||
@@ -190,7 +222,7 @@
|
||||
<div class="card-footer text-center">
|
||||
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="window.location.href='{{ route('renderApplicationForm', ['vacancySlug' => $vacancy->vacancySlug]) }}'">Apply</button>
|
||||
<button type="button" class="btn btn-warning btn-sm">Learn More</button>
|
||||
<button type="button" class="btn btn-warning btn-sm" onclick="$('#{{ $vacancy->vacancySlug }}-details').modal('show')">Learn More</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -75,6 +75,18 @@
|
||||
|
||||
@endhasrole
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
|
||||
<div class="alert alert-warning alert-dismissible">
|
||||
<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>
|
||||
<strong>Reminder:</strong> If this form has been updated, new fields and updated questions will not show up here!
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
@@ -357,9 +369,9 @@
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="col">
|
||||
|
||||
|
||||
@if ($comments->isEmpty())
|
||||
|
||||
<div class="alert alert-warning">
|
||||
@@ -378,11 +390,11 @@
|
||||
|
||||
|
||||
@if (!$comments->isEmpty())
|
||||
|
||||
|
||||
@foreach($comments as $comment)
|
||||
<div class="row mt-3 mb-3">
|
||||
<div class="row mt-3 mb-3">
|
||||
<div class="col-md-2">
|
||||
|
||||
|
||||
<div class="text-center">
|
||||
@if($application->user->avatarPreference == 'gravatar')
|
||||
<img class="profile-user-img img-fluid img-circle" src="https://gravatar.com/avatar/{{md5($comment->user->email)}}" alt="User profile picture">
|
||||
@@ -394,24 +406,24 @@
|
||||
</div>
|
||||
|
||||
<div class="card comment">
|
||||
|
||||
|
||||
<div class="card-header comment-header">
|
||||
|
||||
<h1 class="commenter">{{$comment->user->name}} ● {{Carbon\Carbon::parse($comment->created_at)->diffForHumans()}}</h3>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
|
||||
{{$comment->text}}
|
||||
|
||||
</div>
|
||||
|
||||
@if(Auth::user()->is($comment->user) || Auth::user()->hasRole('admin'))
|
||||
|
||||
|
||||
<div class="card-footer comment-footer">
|
||||
|
||||
|
||||
<form method="POST" id="deleteComment" action="{{route('deleteApplicationComment', ['comment' => $comment->id])}}">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
@@ -434,7 +446,7 @@
|
||||
<div class="row mt-5">
|
||||
|
||||
<div class="col-md-2">
|
||||
|
||||
|
||||
<div class="text-center">
|
||||
@if($application->user->avatarPreference == 'gravatar')
|
||||
<img class="profile-user-img img-fluid img-circle" src="https://gravatar.com/avatar/{{md5(Auth::user()->email)}}" alt="User profile picture">
|
||||
@@ -443,15 +455,15 @@
|
||||
@endif
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<div class="card border-top border-bottom">
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
|
||||
<form id="newComment" method="POST" action="{{route('addApplicationComment', ['application' => $application->id])}}">
|
||||
|
||||
|
||||
@csrf
|
||||
|
||||
<textarea id="comment" name="comment" class="form-control" id="commentText"></textarea>
|
||||
@@ -459,14 +471,14 @@
|
||||
</form>
|
||||
|
||||
<div class="row">
|
||||
|
||||
|
||||
<div class="col text-left">
|
||||
<p class="text-sm text-muted">Commenting as {{Auth::user()->name}}</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col text-right">
|
||||
|
||||
|
||||
<p class="text-sm text-muted"><span id="charcount">0</span>/600 max characters</p>
|
||||
|
||||
</div>
|
||||
@@ -476,7 +488,7 @@
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-right">
|
||||
|
||||
|
||||
<button type="button" id="submitComment" class="btn btn-sm btn-secondary">Post</button>
|
||||
|
||||
</div>
|
||||
|
@@ -2,6 +2,40 @@
|
||||
|
||||
@section('content')
|
||||
|
||||
@if(!$positions->isEmpty())
|
||||
|
||||
<!-- todo: details component -->
|
||||
|
||||
@foreach($positions as $position)
|
||||
<x-modal id="{{ $position->vacancySlug . '-details' }}" modal-label="{{ $position->vacancySlug . '-details-label' }}" modal-title="Opening details" include-close-button="true">
|
||||
|
||||
@if (is_null($position->vacancyFullDescription))
|
||||
|
||||
<div class="alert alert-warning">
|
||||
|
||||
<h3><i class="fas fa-question-circle"></i> There don't seem to be any details</h3>
|
||||
<p>
|
||||
This opening does not have any details yet.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
@else
|
||||
|
||||
{!! $position->vacancyFullDescription !!}
|
||||
<p class="text-sm text-muted">
|
||||
Last updated @ {{ $position->updated_at }}
|
||||
</p>
|
||||
@endif
|
||||
|
||||
<x-slot name="modalFooter"></x-slot>
|
||||
|
||||
</x-modal>
|
||||
|
||||
@endforeach
|
||||
|
||||
|
||||
@endif
|
||||
|
||||
<!--Main Layout-->
|
||||
<main class="py-5">
|
||||
|
||||
@@ -57,6 +91,7 @@
|
||||
|
||||
@guest
|
||||
<button type="button" class="btn btn-success" onclick="window.location.href='{{route('renderApplicationForm', ['vacancySlug' => $position->vacancySlug])}}'">Apply</button>
|
||||
<button type="button" class="btn btn-info" onclick="$('#{{ $position->vacancySlug }}-details').modal('show')">Learn more</button>
|
||||
@endguest
|
||||
|
||||
</div>
|
||||
@@ -163,72 +198,11 @@
|
||||
<div class="row">
|
||||
|
||||
<div class="col text-center">
|
||||
<p>
|
||||
Join the team today and help out network grow and prosper!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
|
||||
<!-- Card content -->
|
||||
<div class="card-body text-center">
|
||||
|
||||
<!-- Title -->
|
||||
<img src="https://crafatar.com/avatars/6102256a-bd28-4dd7-b68e-4c96ef313734" class="img-fluid mb-3" alt="miguel456's avatar">
|
||||
|
||||
<h4 class="card-title text-center"><a>miguel456</a></h4>
|
||||
<!-- Text -->
|
||||
<p class="card-text">Network Owner / Web Developer</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
|
||||
<button type="button" class="btn btn-info">More Info</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col text-center">
|
||||
|
||||
<div class="card">
|
||||
|
||||
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Moderator</h4>
|
||||
<p class="card-text">Open Position!</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
|
||||
<button type="button" class="btn btn-success">Apply</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col text-center">
|
||||
|
||||
<div class="card">
|
||||
|
||||
<div class="card-body">
|
||||
<h4 class="card-title">Helper</h4>
|
||||
|
||||
<p class="card-text">Open Position!</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
|
||||
<button type="button" class="btn btn-success">Apply</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
@@ -12,6 +12,7 @@
|
||||
<!-- Scripts -->
|
||||
<script src="{{ asset('js/app.js') }}" defer></script>
|
||||
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="dns-prefetch" href="//fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">
|
||||
@@ -79,5 +80,7 @@
|
||||
@yield('content')
|
||||
</main>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -40,7 +40,6 @@ Route::group(['middleware' => ['auth', 'forcelogout']], function(){
|
||||
->name('showUserApps')
|
||||
->middleware('eligibility');
|
||||
|
||||
|
||||
Route::get('/view/{id}', 'ApplicationController@showUserApp')
|
||||
->name('showUserApp');
|
||||
|
||||
@@ -58,16 +57,27 @@ Route::group(['middleware' => ['auth', 'forcelogout']], function(){
|
||||
Route::patch('/update/{id}/{newStatus}', 'ApplicationController@updateApplicationStatus')
|
||||
->name('updateApplicationStatus');
|
||||
|
||||
Route::delete('{application}/delete', 'ApplicationController@delete')
|
||||
->name('deleteApplication');
|
||||
|
||||
|
||||
Route::get('/staff/all', 'ApplicationController@showAllApps')
|
||||
->name('allApplications');
|
||||
|
||||
|
||||
Route::get('/staff/outstanding', 'ApplicationController@showAllPendingApps')
|
||||
->name('staffPendingApps');
|
||||
|
||||
|
||||
Route::get('/staff/peer-review', 'ApplicationController@showPeerReview')
|
||||
->name('peerReview');
|
||||
|
||||
|
||||
Route::get('/staff/pending-interview', 'ApplicationController@showPendingInterview')
|
||||
->name('pendingInterview');
|
||||
|
||||
|
||||
|
||||
Route::post('{id}/staff/vote', 'VoteController@vote')
|
||||
->name('voteApplication');
|
||||
|
||||
@@ -161,6 +171,13 @@ Route::group(['middleware' => ['auth', 'forcelogout']], function(){
|
||||
->name('savePosition');
|
||||
|
||||
|
||||
Route::get('positions/edit/{position}', 'VacancyController@edit')
|
||||
->name('editPosition');
|
||||
|
||||
Route::patch('positions/update/{position}', 'VacancyController@update')
|
||||
->name('updatePosition');
|
||||
|
||||
|
||||
Route::patch('positions/availability/{status}/{id}', 'VacancyController@updatePositionAvailability')
|
||||
->name('updatePositionAvailability');
|
||||
|
||||
@@ -177,6 +194,15 @@ Route::group(['middleware' => ['auth', 'forcelogout']], function(){
|
||||
Route::get('forms', 'FormController@index')
|
||||
->name('showForms');
|
||||
|
||||
Route::get('forms/preview/{form}', 'FormController@preview')
|
||||
->name('previewForm');
|
||||
|
||||
Route::get('forms/edit/{form}', 'FormController@edit')
|
||||
->name('editForm');
|
||||
|
||||
Route::patch('forms/update/{form}', 'FormController@update')
|
||||
->name('updateForm');
|
||||
|
||||
|
||||
Route::get('devtools', 'DevToolsController@index')
|
||||
->name('devTools');
|
||||
|
Reference in New Issue
Block a user