Merge pull request #324 from nabeelio/265-editable-pilot-id

Backend changes separating id from pilot_id
This commit is contained in:
Nabeel S 2019-07-17 12:49:22 -04:00 committed by GitHub
commit 0225a84a81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 523 additions and 60 deletions

View File

@ -90,7 +90,7 @@ if [ "$TRAVIS" = "true" ]; then
# clear any app specific stuff that might have been loaded in # clear any app specific stuff that might have been loaded in
find storage/app -mindepth 1 -not -name '.gitignore' -not -name public -not -name import -print0 -exec rm -rf {} + find storage/app -mindepth 1 -not -name '.gitignore' -not -name public -not -name import -print0 -exec rm -rf {} +
find storage/app/public -mindepth 1 -not -name '.gitignore' -not -name avatar -not -name uploads -print0 -exec rm -rf {} + find storage/app/public -mindepth 1 -not -name '.gitignore' -not -name avatar -not -name uploads -print0 -exec rm -rf {} +
find storage/app/public/avatar -mindepth 1 -not -name '.gitignore' -print0 -exec rm -rf {} + find storage/app/public/avatars -mindepth 1 -not -name '.gitignore' -print0 -exec rm -rf {} +
find storage/app/public/uploads -mindepth 1 -not -name '.gitignore' -print0 -exec rm -rf {} + find storage/app/public/uploads -mindepth 1 -not -name '.gitignore' -print0 -exec rm -rf {} +
find storage/debugbar -mindepth 1 -not -name '.gitignore' -print0 -exec rm -rf {} + find storage/debugbar -mindepth 1 -not -name '.gitignore' -print0 -exec rm -rf {} +
find storage/docker -mindepth 1 -not -name '.gitignore' -print0 -exec rm -rf {} + find storage/docker -mindepth 1 -not -name '.gitignore' -print0 -exec rm -rf {} +

View File

@ -8,6 +8,7 @@ $factory->define(App\Models\User::class, function (Faker $faker) {
return [ return [
'id' => null, 'id' => null,
'pilot_id' => 0,
'name' => $faker->name, 'name' => $faker->name,
'email' => $faker->safeEmail, 'email' => $faker->safeEmail,
'password' => $password ?: $password = Hash::make('secret'), 'password' => $password ?: $password = Hash::make('secret'),

View File

@ -0,0 +1,66 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class UsersAddPilotId extends Migration
{
/**
* Kinda of gross operations to change the pilot ID column
* 1. Add an `pilot_id` column, which will get populated with the current ID
* 2. Drop the `id` column, and then recreate it as a string field
* 3. Iterate through all of the users and set their `id` to the `pilot_id`
* 4. Change the other tables column types that reference `user_id`
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->unsignedBigInteger('pilot_id')
->after('id')
->unique()
->nullable()
->index('users_pilot_id');
});
// Migrate the current pilot IDs
DB::update('UPDATE `users` SET `pilot_id`=`id`');
// Drop the old ID column and add a new one
/*Schema::table('users', function (Blueprint $table) {
$table->dropPrimary('users_id_primary');
$table->dropColumn('id');
$table->string('id', Model::ID_MAX_LENGTH)->primary();
});
// Update the users to use the `pilot_id` (so we don't need to migrate data from other tables)
$users = DB::table('users')->get(['id']);
foreach ($users as $user) {
$user->id = $user->pilot_id;
$user->save();
}*/
// role_user
// permission_user
// sessions
// pireps
// bids
// news
// user_awards
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('pilot_id');
});
}
}

View File

@ -15,6 +15,7 @@ roles:
users: users:
- id: 1 - id: 1
pilot_id: 1
name: Admin User name: Admin User
email: admin@phpvms.net email: admin@phpvms.net
password: admin password: admin
@ -34,6 +35,7 @@ users:
created_at: now created_at: now
updated_at: now updated_at: now
- id: 2 - id: 2
pilot_id: 2
name: Carla Walters name: Carla Walters
email: carla.walters68@example.com email: carla.walters68@example.com
password: admin password: admin
@ -50,6 +52,7 @@ users:
opt_in: 1 opt_in: 1
toc_accepted: 1 toc_accepted: 1
- id: 3 - id: 3
pilot_id: 3
name: Raymond Pearson name: Raymond Pearson
email: raymond.pearson56@example.com email: raymond.pearson56@example.com
password: admin password: admin

View File

@ -0,0 +1,9 @@
<?php
namespace App\Exceptions;
class UserPilotIdExists extends InternalError
{
public const FIELD = 'pilot_id';
public const MESSAGE = 'A user with this pilot ID already exists';
}

View File

@ -292,7 +292,7 @@ class UserController extends Controller
public function regen_apikey($id, Request $request) public function regen_apikey($id, Request $request)
{ {
$user = User::find($id); $user = User::find($id);
Log::info('Regenerating API key "'.$user->pilot_id.'"'); Log::info('Regenerating API key "'.$user->ident.'"');
$user->api_key = Utils::generateApiKey(); $user->api_key = Utils::generateApiKey();
$user->save(); $user->save();

View File

@ -131,7 +131,7 @@ class AcarsController extends Controller
$this->checkCancelled($pirep); $this->checkCancelled($pirep);
Log::debug( Log::debug(
'Posting ACARS update (user: '.Auth::user()->pilot_id.', pirep id :'.$id.'): ', 'Posting ACARS update (user: '.Auth::user()->ident.', pirep id :'.$id.'): ',
$request->post() $request->post()
); );

View File

@ -49,7 +49,7 @@ class LoginController extends Controller
// TODO: How to handle ON_LEAVE? // TODO: How to handle ON_LEAVE?
if ($user->state !== UserState::ACTIVE) { if ($user->state !== UserState::ACTIVE) {
Log::info('Trying to login '.$user->pilot_id.', state ' Log::info('Trying to login '.$user->ident.', state '
.UserState::label($user->state)); .UserState::label($user->state));
// Log them out // Log them out

View File

@ -133,7 +133,7 @@ class ProfileController extends Controller
]); ]);
if ($validator->fails()) { if ($validator->fails()) {
Log::info('validator failed for user '.$user->pilot_id); Log::info('validator failed for user '.$user->ident);
Log::info($validator->errors()->toArray()); Log::info($validator->errors()->toArray());
return redirect(route('frontend.profile.edit', $id)) return redirect(route('frontend.profile.edit', $id))
@ -153,7 +153,7 @@ class ProfileController extends Controller
} }
if ($request->hasFile('avatar')) { if ($request->hasFile('avatar')) {
$avatar = $request->file('avatar'); $avatar = $request->file('avatar');
$file_name = $user->pilot_id.'.'.$avatar->getClientOriginalExtension(); $file_name = $user->ident.'.'.$avatar->getClientOriginalExtension();
$path = "avatars/{$file_name}"; $path = "avatars/{$file_name}";
// Create the avatar, resizing it and keeping the aspect ratio. // Create the avatar, resizing it and keeping the aspect ratio.
@ -189,7 +189,7 @@ class ProfileController extends Controller
public function regen_apikey(Request $request) public function regen_apikey(Request $request)
{ {
$user = User::find(Auth::user()->id); $user = User::find(Auth::user()->id);
Log::info('Regenerating API key "'.$user->pilot_id.'"'); Log::info('Regenerating API key "'.$user->ident.'"');
$user->api_key = Utils::generateApiKey(); $user->api_key = Utils::generateApiKey();
$user->save(); $user->save();

View File

@ -5,7 +5,7 @@ namespace App\Http\Requests;
use App\Models\Pirep; use App\Models\Pirep;
use App\Repositories\PirepFieldRepository; use App\Repositories\PirepFieldRepository;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Log; use Illuminate\Support\Facades\Log;
class CreatePirepRequest extends FormRequest class CreatePirepRequest extends FormRequest
{ {

View File

@ -25,7 +25,10 @@ class CreateUserRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
$rules = User::$rules; $rules = User::$rules;
$rules['email'] .= '|unique:users,email'; $rules['email'] .= '|unique:users,email';
$rules['pilot_id'] .= '|unique:users,pilot_id';
return $rules; return $rules;
} }
} }

View File

@ -4,6 +4,7 @@ namespace App\Http\Requests;
use App\Models\User; use App\Models\User;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use function request;
class UpdateUserRequest extends FormRequest class UpdateUserRequest extends FormRequest
{ {
@ -24,6 +25,13 @@ class UpdateUserRequest extends FormRequest
*/ */
public function rules() public function rules()
{ {
return User::$rules; $rules = User::$rules;
$user_id = request('id', null);
$rules['email'] .= '|unique:users,email,'.$user_id.',id';
$rules['pilot_id'] .= '|unique:users,pilot_id,'.$user_id.',id';
return $rules;
} }
} }

View File

@ -27,6 +27,7 @@ class PirepComment extends Resource
'user' => [ 'user' => [
'id' => $user->id, 'id' => $user->id,
'pilot_id' => $user->pilot_id, 'pilot_id' => $user->pilot_id,
'ident' => $user->ident,
'name' => $user->name, 'name' => $user->name,
], ],
]; ];

View File

@ -11,6 +11,7 @@ class User extends Resource
return [ return [
'id' => $this->id, 'id' => $this->id,
'pilot_id' => $this->pilot_id, 'pilot_id' => $this->pilot_id,
'ident' => $this->ident,
'name' => $this->name, 'name' => $this->name,
'email' => $this->email, 'email' => $this->email,
'apikey' => $this->apikey, 'apikey' => $this->apikey,

View File

@ -68,7 +68,7 @@ class NotificationEvents extends Listener
public function onUserRegister(UserRegistered $event): void public function onUserRegister(UserRegistered $event): void
{ {
Log::info('onUserRegister: ' Log::info('onUserRegister: '
.$event->user->pilot_id.' is ' .$event->user->ident.' is '
.UserState::label($event->user->state) .UserState::label($event->user->state)
.', sending active email'); .', sending active email');

View File

@ -0,0 +1,26 @@
<?php
namespace App\Models\Observers;
use App\Models\User;
use App\Services\UserService;
class UserObserver
{
private $userSvc;
public function __construct(UserService $userSvc)
{
$this->userSvc = $userSvc;
}
/**
* After a user has been created, do some stuff
*
* @param User $user
*/
public function created(User $user): void
{
$this->userSvc->findAndSetPilotId($user);
}
}

View File

@ -10,14 +10,17 @@ use Illuminate\Notifications\Notifiable;
use Laratrust\Traits\LaratrustUserTrait; use Laratrust\Traits\LaratrustUserTrait;
/** /**
* @property int id * @property int id
* @property int pilot_id
* @property string name * @property string name
* @property string email * @property string email
* @property string password * @property string password
* @property string api_key * @property string api_key
* @property mixed ident * @property mixed timezone
* @property string ident
* @property string curr_airport_id * @property string curr_airport_id
* @property string home_airport_id * @property string home_airport_id
* @property Airline airline
* @property Flight[] flights * @property Flight[] flights
* @property string flight_time * @property string flight_time
* @property string remember_token * @property string remember_token
@ -45,9 +48,11 @@ class User extends Authenticatable
public $journal_type = JournalType::USER; public $journal_type = JournalType::USER;
protected $fillable = [ protected $fillable = [
'id',
'name', 'name',
'email', 'email',
'password', 'password',
'pilot_id',
'airline_id', 'airline_id',
'rank_id', 'rank_id',
'api_key', 'api_key',
@ -78,6 +83,8 @@ class User extends Authenticatable
]; ];
protected $casts = [ protected $casts = [
'id' => 'integer',
'pilot_id' => 'integer',
'flights' => 'integer', 'flights' => 'integer',
'flight_time' => 'integer', 'flight_time' => 'integer',
'transfer_time' => 'integer', 'transfer_time' => 'integer',
@ -89,26 +96,19 @@ class User extends Authenticatable
]; ];
public static $rules = [ public static $rules = [
'name' => 'required', 'name' => 'required',
'email' => 'required|email', 'email' => 'required|email',
'pilot_id' => 'required|integer',
]; ];
/**
* @return string
*/
public function getPilotIdAttribute()
{
$length = setting('pilots.id_length');
return $this->airline->icao.str_pad($this->id, $length, '0', STR_PAD_LEFT);
}
/** /**
* @return string * @return string
*/ */
public function getIdentAttribute() public function getIdentAttribute()
{ {
return $this->getPilotIdAttribute(); $length = setting('pilots.id_length');
return $this->airline->icao.str_pad($this->pilot_id, $length, '0', STR_PAD_LEFT);
} }
/** /**

View File

@ -15,10 +15,12 @@ use App\Models\Observers\JournalTransactionObserver;
use App\Models\Observers\SettingObserver; use App\Models\Observers\SettingObserver;
use App\Models\Observers\Sluggable; use App\Models\Observers\Sluggable;
use App\Models\Observers\SubfleetObserver; use App\Models\Observers\SubfleetObserver;
use App\Models\Observers\UserObserver;
use App\Models\PirepField; use App\Models\PirepField;
use App\Models\PirepFieldValue; use App\Models\PirepFieldValue;
use App\Models\Setting; use App\Models\Setting;
use App\Models\Subfleet; use App\Models\Subfleet;
use App\Models\User;
use App\Repositories\SettingRepository; use App\Repositories\SettingRepository;
use App\Services\ModuleService; use App\Services\ModuleService;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
@ -53,6 +55,7 @@ class AppServiceProvider extends ServiceProvider
Setting::observe(SettingObserver::class); Setting::observe(SettingObserver::class);
Subfleet::observe(SubfleetObserver::class); Subfleet::observe(SubfleetObserver::class);
User::observe(UserObserver::class);
} }
/** /**

View File

@ -425,7 +425,7 @@ class PirepService extends Service
]); ]);
if ($bid) { if ($bid) {
Log::info('Bid for user: '.$pirep->user->pilot_id.' on flight '.$flight->ident); Log::info('Bid for user: '.$pirep->user->ident.' on flight '.$flight->ident);
$bid->delete(); $bid->delete();
} }
} }

View File

@ -6,6 +6,7 @@ use App\Contracts\Service;
use App\Events\UserRegistered; use App\Events\UserRegistered;
use App\Events\UserStateChanged; use App\Events\UserStateChanged;
use App\Events\UserStatsChanged; use App\Events\UserStatsChanged;
use App\Exceptions\UserPilotIdExists;
use App\Models\Enums\PirepState; use App\Models\Enums\PirepState;
use App\Models\Enums\UserState; use App\Models\Enums\UserState;
use App\Models\Pirep; use App\Models\Pirep;
@ -16,6 +17,7 @@ use App\Repositories\AircraftRepository;
use App\Repositories\SubfleetRepository; use App\Repositories\SubfleetRepository;
use App\Support\Units\Time; use App\Support\Units\Time;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use function is_array;
use Log; use Log;
/** /**
@ -67,7 +69,7 @@ class UserService extends Service
// $user->attachRole($role); // $user->attachRole($role);
// Attach any additional roles // Attach any additional roles
if (!empty($groups) && \is_array($groups)) { if (!empty($groups) && is_array($groups)) {
foreach ($groups as $group) { foreach ($groups as $group) {
$role = Role::where('name', $group)->first(); $role = Role::where('name', $group)->first();
$user->attachRole($role); $user->attachRole($role);
@ -83,6 +85,65 @@ class UserService extends Service
return $user; return $user;
} }
/**
* Find the next available pilot ID and set the current user's pilot_id to that +1
* Called from UserObserver right now after a record is created
*
* @param User $user
*
* @return User
*/
public function findAndSetPilotId(User $user): User
{
if ($user->pilot_id !== null && $user->pilot_id > 0) {
return $user;
}
$max = (int) User::max('pilot_id');
$user->pilot_id = $max + 1;
$user->save();
Log::info('Set pilot ID for user '.$user->id.' to '.$user->pilot_id);
return $user;
}
public function isPilotIdAlreadyUsed(int $pilot_id): bool
{
return User::where('pilot_id', '=', $pilot_id)->exists();
}
/**
* Change a user's pilot ID
*
* @param User $user
* @param int $pilot_id
*
* @throws UserPilotIdExists
*
* @return User
*/
public function changePilotId(User $user, int $pilot_id): User
{
if ($user->pilot_id === $pilot_id) {
return $user;
}
if ($this->isPilotIdAlreadyUsed($pilot_id)) {
Log::error('User with id '.$pilot_id.' already exists');
throw new UserPilotIdExists();
}
$old_id = $user->pilot_id;
$user->pilot_id = $pilot_id;
$user->save();
Log::info('Changed pilot ID for user '.$user->id.' from '.$old_id.' to '.$user->pilot_id);
return $user;
}
/** /**
* Return the subfleets this user is allowed access to, * Return the subfleets this user is allowed access to,
* based on their current rank * based on their current rank
@ -133,7 +194,7 @@ class UserService extends Service
return $user; return $user;
} }
Log::info('User '.$user->pilot_id.' state changing from ' Log::info('User '.$user->ident.' state changing from '
.UserState::label($old_state).' to ' .UserState::label($old_state).' to '
.UserState::label($user->state)); .UserState::label($user->state));

View File

@ -8,6 +8,7 @@ use App\Support\Units\Pressure;
use App\Support\Units\Temperature; use App\Support\Units\Temperature;
use App\Support\Units\Velocity; use App\Support\Units\Velocity;
use function count; use function count;
use Illuminate\Support\Facades\Log;
use PhpUnitsOfMeasure\Exception\NonNumericValue; use PhpUnitsOfMeasure\Exception\NonNumericValue;
use PhpUnitsOfMeasure\Exception\NonStringUnitName; use PhpUnitsOfMeasure\Exception\NonStringUnitName;
@ -341,6 +342,7 @@ class Metar implements \ArrayAccess
public function __construct($raw, $taf = false, $debug = false, $icao = true) public function __construct($raw, $taf = false, $debug = false, $icao = true)
{ {
$this->debug_enabled = $debug; $this->debug_enabled = $debug;
// Log::info('Parsing metar="'.$raw.'"');
$raw_lines = explode("\n", $raw, 2); $raw_lines = explode("\n", $raw, 2);
if (isset($raw_lines[1])) { if (isset($raw_lines[1])) {
@ -408,7 +410,7 @@ class Metar implements \ArrayAccess
*/ */
protected function createAltitude($value, $unit) protected function createAltitude($value, $unit)
{ {
return new Altitude($value, $unit); return new Altitude((float) $value, $unit);
} }
/** /**
@ -424,7 +426,7 @@ class Metar implements \ArrayAccess
*/ */
protected function createDistance($value, $unit) protected function createDistance($value, $unit)
{ {
return new Distance($value, $unit); return new Distance((float) $value, $unit);
} }
/** /**
@ -440,7 +442,7 @@ class Metar implements \ArrayAccess
*/ */
protected function createPressure($value, $unit) protected function createPressure($value, $unit)
{ {
return new Pressure($value, $unit); return new Pressure((float) $value, $unit);
} }
/** /**
@ -456,7 +458,7 @@ class Metar implements \ArrayAccess
*/ */
protected function createTemperature($value, $unit) protected function createTemperature($value, $unit)
{ {
return new Temperature($value, $unit); return new Temperature((float) $value, $unit);
} }
/** /**
@ -472,7 +474,7 @@ class Metar implements \ArrayAccess
*/ */
protected function createVelocity($value, $unit) protected function createVelocity($value, $unit)
{ {
return new Velocity($value, $unit); return new Velocity((float) $value, $unit);
} }
/** /**
@ -1118,6 +1120,7 @@ class Metar implements \ArrayAccess
$report = implode(' ', $report); $report = implode(' ', $report);
$report_ft = implode(' ', $report_ft); $report_ft = implode(' ', $report_ft);
$observed['report'] = $this->createAltitude($report_ft, 'ft');
$observed['report'] = ucfirst($report); $observed['report'] = ucfirst($report);
$observed['report_ft'] = ucfirst($report_ft); $observed['report_ft'] = ucfirst($report_ft);

View File

@ -19,6 +19,7 @@
"anhskohbo/no-captcha": "3.0.*", "anhskohbo/no-captcha": "3.0.*",
"appstract/laravel-opcache": "^2.0", "appstract/laravel-opcache": "^2.0",
"arrilot/laravel-widgets": "3.13.*", "arrilot/laravel-widgets": "3.13.*",
"doctrine/dbal": "2.9.*",
"fzaninotto/faker": "^1.8", "fzaninotto/faker": "^1.8",
"guzzlehttp/guzzle": "6.3.*", "guzzlehttp/guzzle": "6.3.*",
"hashids/hashids": "2.0.*", "hashids/hashids": "2.0.*",

233
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "b4e5994cf5891923a2ed410bacad5fc4", "content-hash": "99c96d01e78e1d4400295a887ac36667",
"packages": [ "packages": [
{ {
"name": "akaunting/money", "name": "akaunting/money",
@ -857,6 +857,237 @@
], ],
"time": "2019-05-27T17:52:04+00:00" "time": "2019-05-27T17:52:04+00:00"
}, },
{
"name": "doctrine/cache",
"version": "v1.8.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
"reference": "d768d58baee9a4862ca783840eca1b9add7a7f57"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/d768d58baee9a4862ca783840eca1b9add7a7f57",
"reference": "d768d58baee9a4862ca783840eca1b9add7a7f57",
"shasum": ""
},
"require": {
"php": "~7.1"
},
"conflict": {
"doctrine/common": ">2.2,<2.4"
},
"require-dev": {
"alcaeus/mongo-php-adapter": "^1.1",
"doctrine/coding-standard": "^4.0",
"mongodb/mongodb": "^1.1",
"phpunit/phpunit": "^7.0",
"predis/predis": "~1.0"
},
"suggest": {
"alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.8.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "Caching library offering an object-oriented API for many cache backends",
"homepage": "https://www.doctrine-project.org",
"keywords": [
"cache",
"caching"
],
"time": "2018-08-21T18:01:43+00:00"
},
{
"name": "doctrine/dbal",
"version": "v2.9.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
"reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9",
"reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9",
"shasum": ""
},
"require": {
"doctrine/cache": "^1.0",
"doctrine/event-manager": "^1.0",
"ext-pdo": "*",
"php": "^7.1"
},
"require-dev": {
"doctrine/coding-standard": "^5.0",
"jetbrains/phpstorm-stubs": "^2018.1.2",
"phpstan/phpstan": "^0.10.1",
"phpunit/phpunit": "^7.4",
"symfony/console": "^2.0.5|^3.0|^4.0",
"symfony/phpunit-bridge": "^3.4.5|^4.0.5"
},
"suggest": {
"symfony/console": "For helpful console commands such as SQL execution and import of files."
},
"bin": [
"bin/doctrine-dbal"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.9.x-dev",
"dev-develop": "3.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\DBAL\\": "lib/Doctrine/DBAL"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
}
],
"description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.",
"homepage": "https://www.doctrine-project.org/projects/dbal.html",
"keywords": [
"abstraction",
"database",
"dbal",
"mysql",
"persistence",
"pgsql",
"php",
"queryobject"
],
"time": "2018-12-31T03:27:51+00:00"
},
{
"name": "doctrine/event-manager",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/event-manager.git",
"reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/event-manager/zipball/a520bc093a0170feeb6b14e9d83f3a14452e64b3",
"reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3",
"shasum": ""
},
"require": {
"php": "^7.1"
},
"conflict": {
"doctrine/common": "<2.9@dev"
},
"require-dev": {
"doctrine/coding-standard": "^4.0",
"phpunit/phpunit": "^7.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Common\\": "lib/Doctrine/Common"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
},
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com"
}
],
"description": "Doctrine Event Manager component",
"homepage": "https://www.doctrine-project.org/projects/event-manager.html",
"keywords": [
"event",
"eventdispatcher",
"eventmanager"
],
"time": "2018-06-11T11:59:03+00:00"
},
{ {
"name": "doctrine/inflector", "name": "doctrine/inflector",
"version": "v1.3.0", "version": "v1.3.0",

View File

@ -2,7 +2,7 @@
return [ return [
'dashboard' => 'Dashboard', 'dashboard' => 'Dashboard',
'administration' => 'Administration', 'administration' => 'Admin',
'flight' => 'Flight|Flights', 'flight' => 'Flight|Flights',
'livemap' => 'Live Map', 'livemap' => 'Live Map',
'pilot' => 'Pilot|Pilots', 'pilot' => 'Pilot|Pilots',

View File

@ -2,7 +2,7 @@
return [ return [
'dashboard' => 'Tablero', 'dashboard' => 'Tablero',
'administration' => 'Administración', 'administration' => 'Admin',
'flight' => 'Vuelo|Vuelos', 'flight' => 'Vuelo|Vuelos',
'livemap' => 'Mapa en vivo', 'livemap' => 'Mapa en vivo',
'pilot' => 'Piloto|Pilotos', 'pilot' => 'Piloto|Pilotos',

View File

@ -32,6 +32,10 @@
@ability('admin', 'finances') @ability('admin', 'finances')
<li><a href="{{ url('/admin/finances') }}"><i class="pe-7s-display1"></i>finances</a></li> <li><a href="{{ url('/admin/finances') }}"><i class="pe-7s-display1"></i>finances</a></li>
@endability @endability
@ability('admin', 'users')
<li><a href="{{ url('/admin/users') }}"><i class="pe-7s-users"></i>users</a></li>
@endability
</ul> </ul>
</div> </div>
</li> </li>
@ -55,10 +59,6 @@
<li><a href="{{ url('/admin/expenses') }}"><i class="pe-7s-cash"></i>expenses</a></li> <li><a href="{{ url('/admin/expenses') }}"><i class="pe-7s-cash"></i>expenses</a></li>
@endability @endability
@ability('admin', 'users')
<li><a href="{{ url('/admin/users') }}"><i class="pe-7s-users"></i>users</a></li>
@endability
@ability('admin', 'ranks') @ability('admin', 'ranks')
<li><a href="{{ url('/admin/ranks') }}"><i class="pe-7s-graph1"></i>ranks</a></li> <li><a href="{{ url('/admin/ranks') }}"><i class="pe-7s-graph1"></i>ranks</a></li>
@endability @endability

View File

@ -11,7 +11,7 @@
<div class="col-md-8"> <div class="col-md-8">
<h5 style="margin-top: 0px;"> <h5 style="margin-top: 0px;">
Filed By: <a href="{{ route('admin.users.edit', [$pirep->user_id]) }}" target="_blank"> Filed By: <a href="{{ route('admin.users.edit', [$pirep->user_id]) }}" target="_blank">
{{ $pirep->user->pilot_id }} {{ $pirep->user->name }} {{ $pirep->user->ident }} {{ $pirep->user->name }}
</a> </a>
</h5> </h5>
</div> </div>

View File

@ -1,17 +1,24 @@
<div class="row"> <div class="row">
<div class="form-group col-sm-4"> {{ Form::hidden('id') }}
<div class="form-group col-sm-3">
{{ Form::label('pilot_id', 'Pilot ID:') }}
{{ Form::number('pilot_id', null, ['class' => 'form-control']) }}
<p class="text-danger">{{ $errors->first('pilot_id') }}</p>
</div>
<div class="form-group col-sm-3">
{{ Form::label('name', 'Name:') }} {{ Form::label('name', 'Name:') }}
{{ Form::text('name', null, ['class' => 'form-control']) }} {{ Form::text('name', null, ['class' => 'form-control']) }}
<p class="text-danger">{{ $errors->first('name') }}</p> <p class="text-danger">{{ $errors->first('name') }}</p>
</div> </div>
<div class="form-group col-sm-4"> <div class="form-group col-sm-3">
{{ Form::label('email', 'Email:') }} {{ Form::label('email', 'Email:') }}
{{ Form::text('email', null, ['class' => 'form-control']) }} {{ Form::text('email', null, ['class' => 'form-control']) }}
<p class="text-danger">{{ $errors->first('email') }}</p> <p class="text-danger">{{ $errors->first('email') }}</p>
</div> </div>
<div class="form-group col-sm-4"> <div class="form-group col-sm-3">
{{ Form::label('password', 'Password:') }} {{ Form::label('password', 'Password:') }}
{{ Form::password('password', ['class' => 'form-control']) }} {{ Form::password('password', ['class' => 'form-control']) }}
<p class="text-danger">{{ $errors->first('password') }}</p> <p class="text-danger">{{ $errors->first('password') }}</p>

View File

@ -1,5 +1,6 @@
<table class="table table-hover table-responsive" id="users-table"> <table class="table table-hover table-responsive" id="users-table">
<thead> <thead>
<th>ID</th>
<th>Name</th> <th>Name</th>
<th>Email</th> <th>Email</th>
<th>Registered</th> <th>Registered</th>
@ -9,6 +10,7 @@
<tbody> <tbody>
@foreach($users as $user) @foreach($users as $user)
<tr> <tr>
<td>{{ $user->ident }}</td>
<td> <td>
@if(filled($user->country)) @if(filled($user->country))
<span class="flag-icon flag-icon-{{ $user->country }}" <span class="flag-icon flag-icon-{{ $user->country }}"

View File

@ -13,7 +13,7 @@
</div> </div>
<h3 class="title">{{ $user->name }}</h3> <h3 class="title">{{ $user->name }}</h3>
<h6><span class="flag-icon flag-icon-{{ $user->country }}"></span></h6> <h6><span class="flag-icon flag-icon-{{ $user->country }}"></span></h6>
<h6>{{ $user->pilot_id }}</h6> <h6>{{ $user->ident }}</h6>
<h6>{{ $user->rank->name }}</h6> <h6>{{ $user->rank->name }}</h6>
<p class="description" style="color: #9A9A9A;"> <p class="description" style="color: #9A9A9A;">
{{ $user->airline->name }} {{ $user->airline->name }}

View File

@ -2,7 +2,7 @@
@foreach($users as $u) @foreach($users as $u)
<tr> <tr>
<td style="padding-right: 10px;"> <td style="padding-right: 10px;">
<span class="title">{{ $u->pilot_id }}</span> <span class="title">{{ $u->ident }}</span>
</td> </td>
<td>{{ $u->name }}</td> <td>{{ $u->name }}</td>
</tr> </tr>

View File

@ -30,8 +30,8 @@ https://api.checkwx.com/#metar-decoded
<tr> <tr>
<td>@lang('widgets.weather.barometer')</td> <td>@lang('widgets.weather.barometer')</td>
<td> <td>
{{ number_format($metar['barometer'], 2) }} hPa {{ number_format($metar['barometer']['hPa'], 2) }} hPa
/ {{ number_format($metar['barometer_in'], 2) }} inHg / {{ number_format($metar['barometer']['inHg'], 2) }} inHg
</td> </td>
</tr> </tr>
@if($metar['clouds']) @if($metar['clouds'])

View File

@ -1,4 +1,4 @@
* *
!avatar !avatars
!uploads !uploads
!.gitignore !.gitignore

View File

@ -126,8 +126,6 @@ class MetarTest extends TestCase
'Scattered at 4500 feet, cumulonimbus; broken sky at 6000 feet; overcast sky at 8000 feet', 'Scattered at 4500 feet, cumulonimbus; broken sky at 6000 feet; overcast sky at 8000 feet',
$parsed['clouds_report_ft'] $parsed['clouds_report_ft']
); );
$this->assertNotNull($parsed);
} }
public function testMetarTrends3() public function testMetarTrends3()
@ -136,6 +134,15 @@ class MetarTest extends TestCase
$metar = Metar::parse($metar); $metar = Metar::parse($metar);
$this->assertEquals('VFR', $metar['category']); $this->assertEquals('VFR', $metar['category']);
$this->assertNotNull($metar); }
public function testMetar4Clouds()
{
$metar = 'KAUS 171153Z 18006KT 9SM FEW015 FEW250 26/24 A3003 RMK AO2 SLP156 T02560244 10267 20239 $';
$metar = Metar::parse($metar);
$this->assertEquals(2, count($metar['clouds']));
$this->assertEquals('A few at 457 meters; a few at 7620 meters', $metar['clouds_report']);
$this->assertEquals('A few at 1500 feet; a few at 25000 feet', $metar['clouds_report_ft']);
} }
} }

View File

@ -1,5 +1,6 @@
<?php <?php
use App\Models\User;
use App\Repositories\SettingRepository; use App\Repositories\SettingRepository;
use App\Services\UserService; use App\Services\UserService;
@ -149,14 +150,6 @@ class UserTest extends TestCase
$subfleetB['subfleet']->id, $subfleetB['subfleet']->id,
]); ]);
/*
* Now we can do some actual tests
*/
/*
* Do some sanity checks first
*/
// Make sure no flights are filtered out // Make sure no flights are filtered out
$this->settingsRepo->store('pilots.only_flights_from_current', false); $this->settingsRepo->store('pilots.only_flights_from_current', false);
@ -195,4 +188,41 @@ class UserTest extends TestCase
$body = $response->json()['data']; $body = $response->json()['data'];
$this->assertCount(1, $body[0]['subfleets']); $this->assertCount(1, $body[0]['subfleets']);
} }
/**
* Test the pilot ID being added when a new user is created
*
* @expectedException \App\Exceptions\UserPilotIdExists
*/
public function testUserPilotIdChangeAlreadyExists()
{
$user1 = factory(App\Models\User::class)->create(['id' => 1]);
$user2 = factory(App\Models\User::class)->create(['id' => 2]);
// Now try to change the original user's pilot_id to 2 (should conflict)
$this->userSvc->changePilotId($user1, 2);
}
/**
* Test the pilot ID being added when a new user is created
*/
public function testUserPilotIdAdded()
{
$new_user = factory(App\Models\User::class)->create(['id' => 1]);
$user = $this->userSvc->createUser($new_user);
$this->assertEquals($user->id, $user->pilot_id);
// Add a second user
$new_user = factory(App\Models\User::class)->create(['id' => 2]);
$user2 = $this->userSvc->createUser($new_user);
$this->assertEquals($user2->id, $user2->pilot_id);
// Now try to change the original user's pilot_id to 3
$user = $this->userSvc->changePilotId($user, 3);
$this->assertEquals(3, $user->pilot_id);
// Create a new user and the pilot_id should be 4
$user3 = factory(App\Models\User::class)->create(['id' => 3]);
$this->assertEquals(4, $user3->pilot_id);
}
} }