From 3ebf4f29247ab2eeb61fe18ee2d1861c6533aa45 Mon Sep 17 00:00:00 2001 From: Nabeel S Date: Tue, 11 Aug 2020 17:48:51 -0400 Subject: [PATCH] Custom user fields #711 (#772) Custom user fields during registration and profile edit #711 --- app/Contracts/Listener.php | 2 +- app/Database/factories/AirlineFactory.php | 2 +- .../2020_07_21_141153_create_user_fields.php | 49 ++++++ app/Database/seeds/dev/users.yml | 23 +++ app/Http/Controllers/Admin/UserController.php | 5 +- .../Controllers/Admin/UserFieldController.php | 154 ++++++++++++++++++ .../Controllers/Auth/RegisterController.php | 34 +++- .../Frontend/ProfileController.php | 61 +++++-- app/Models/File.php | 16 +- app/Models/Journal.php | 10 +- app/Models/Ledger.php | 10 +- app/Models/SimBrief.php | 8 +- app/Models/User.php | 80 +++++---- app/Models/UserField.php | 49 ++++++ app/Models/UserFieldValue.php | 37 +++++ app/Notifications/BaseNotification.php | 2 +- app/Providers/RouteServiceProvider.php | 6 +- app/Repositories/UserFieldRepository.php | 32 ++++ app/Repositories/UserRepository.php | 25 ++- docker-compose.yml | 1 + modules/Importer/Services/BaseImporter.php | 2 +- .../Services/Importers/ClearDatabase.php | 2 +- .../Services/Importers/FinalizeImporter.php | 2 +- .../views/admin/flightfields/table.blade.php | 7 +- .../views/admin/userfields/create.blade.php | 11 ++ .../views/admin/userfields/edit.blade.php | 11 ++ .../views/admin/userfields/fields.blade.php | 64 ++++++++ .../views/admin/userfields/index.blade.php | 13 ++ .../views/admin/userfields/table.blade.php | 33 ++++ resources/views/admin/users/fields.blade.php | 25 ++- resources/views/admin/users/index.blade.php | 4 + .../layouts/default/auth/register.blade.php | 12 ++ .../default/pireps/custom_fields.blade.php | 2 +- .../default/profile/custom_fields.blade.php | 16 ++ .../layouts/default/profile/fields.blade.php | 21 +++ .../layouts/default/profile/index.blade.php | 16 ++ 36 files changed, 740 insertions(+), 107 deletions(-) create mode 100644 app/Database/migrations/2020_07_21_141153_create_user_fields.php create mode 100644 app/Http/Controllers/Admin/UserFieldController.php create mode 100644 app/Models/UserField.php create mode 100644 app/Models/UserFieldValue.php create mode 100644 app/Repositories/UserFieldRepository.php create mode 100644 resources/views/admin/userfields/create.blade.php create mode 100644 resources/views/admin/userfields/edit.blade.php create mode 100644 resources/views/admin/userfields/fields.blade.php create mode 100644 resources/views/admin/userfields/index.blade.php create mode 100644 resources/views/admin/userfields/table.blade.php create mode 100644 resources/views/layouts/default/profile/custom_fields.blade.php diff --git a/app/Contracts/Listener.php b/app/Contracts/Listener.php index 9245d044..59a10131 100644 --- a/app/Contracts/Listener.php +++ b/app/Contracts/Listener.php @@ -16,7 +16,7 @@ abstract class Listener public function subscribe(Dispatcher $events): void { foreach (static::$callbacks as $klass => $cb) { - $events->listen($klass, get_class($this).'@'.$cb); + $events->listen($klass, static::class.'@'.$cb); } } } diff --git a/app/Database/factories/AirlineFactory.php b/app/Database/factories/AirlineFactory.php index 25224b74..62525ee7 100644 --- a/app/Database/factories/AirlineFactory.php +++ b/app/Database/factories/AirlineFactory.php @@ -9,7 +9,7 @@ use Hashids\Hashids; $factory->define(App\Models\Airline::class, function (Faker $faker) { return [ 'id' => null, - 'icao' => function (array $apt) use ($faker) { + 'icao' => function (array $apt) { $hashids = new Hashids(microtime(), 5); $mt = str_replace('.', '', microtime(true)); diff --git a/app/Database/migrations/2020_07_21_141153_create_user_fields.php b/app/Database/migrations/2020_07_21_141153_create_user_fields.php new file mode 100644 index 00000000..c98514e2 --- /dev/null +++ b/app/Database/migrations/2020_07_21_141153_create_user_fields.php @@ -0,0 +1,49 @@ +increments('id'); + $table->string('name', 200); + $table->text('description')->nullable(); + $table->boolean('show_on_registration')->default(false)->nullable(); + $table->boolean('required')->default(false)->nullable(); + $table->boolean('private')->default(false)->nullable(); + $table->boolean('active')->default(true)->nullable(); + $table->timestamps(); + }); + + /* + * The values for the actual fields + */ + Schema::create('user_field_values', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('user_field_id'); + $table->string('user_id', Model::ID_MAX_LENGTH); + $table->text('value')->nullable(); + $table->timestamps(); + + $table->index(['user_field_id', 'user_id']); + }); + } + + /** + * @return void + */ + public function down() + { + } +} diff --git a/app/Database/seeds/dev/users.yml b/app/Database/seeds/dev/users.yml index 1500528b..106b7030 100644 --- a/app/Database/seeds/dev/users.yml +++ b/app/Database/seeds/dev/users.yml @@ -69,3 +69,26 @@ role_user: - user_id: 3 role_id: 2 user_type: App\Models\User + +user_fields: + - id: 1 + name: 'VATSIM ID' + show_on_registration: true + required: false + private: false + - id: 2 + name: 'Referral' + description: 'Who referred you' + show_on_registration: true + required: false + private: true + +user_field_values: + - id: 1 + user_field_id: 1 + user_id: 1 + value: 'my vatsim id' + - id: 2 + user_field_id: 2 + user_id: 1 + value: 'Nobody did' diff --git a/app/Http/Controllers/Admin/UserController.php b/app/Http/Controllers/Admin/UserController.php index 563c252d..9d836370 100644 --- a/app/Http/Controllers/Admin/UserController.php +++ b/app/Http/Controllers/Admin/UserController.php @@ -158,11 +158,12 @@ class UserController extends Controller */ public function edit($id) { - $user = $this->userRepo->findWithoutFail($id); + $user = $this->userRepo + ->with(['fields', 'rank']) + ->findWithoutFail($id); if (empty($user)) { Flash::error('User not found'); - return redirect(route('admin.users.index')); } diff --git a/app/Http/Controllers/Admin/UserFieldController.php b/app/Http/Controllers/Admin/UserFieldController.php new file mode 100644 index 00000000..0dd05e07 --- /dev/null +++ b/app/Http/Controllers/Admin/UserFieldController.php @@ -0,0 +1,154 @@ +userFieldRepo = $userFieldRepo; + } + + /** + * Display a listing of the UserField. + * + * @param Request $request + * + * @throws \Prettus\Repository\Exceptions\RepositoryException + * + * @return mixed + */ + public function index(Request $request) + { + $this->userFieldRepo->pushCriteria(new RequestCriteria($request)); + $fields = $this->userFieldRepo->all(); + + return view('admin.userfields.index', ['fields' => $fields]); + } + + /** + * Show the form for creating a new UserField. + */ + public function create() + { + return view('admin.userfields.create'); + } + + /** + * Store a newly created UserField in storage. + * + * @param Request $request + * + * @throws \Prettus\Validator\Exceptions\ValidatorException + * + * @return mixed + */ + public function store(Request $request) + { + $this->userFieldRepo->create($request->all()); + + Flash::success('Field added successfully.'); + return redirect(route('admin.userfields.index')); + } + + /** + * Display the specified UserField. + * + * @param int $id + * + * @return mixed + */ + public function show($id) + { + $field = $this->userFieldRepo->findWithoutFail($id); + + if (empty($field)) { + Flash::error('Flight field not found'); + return redirect(route('admin.userfields.index')); + } + + return view('admin.userfields.show', ['field' => $field]); + } + + /** + * Show the form for editing the specified UserField. + * + * @param int $id + * + * @return mixed + */ + public function edit($id) + { + $field = $this->userFieldRepo->findWithoutFail($id); + + if (empty($field)) { + Flash::error('Field not found'); + return redirect(route('admin.userfields.index')); + } + + return view('admin.userfields.edit', ['field' => $field]); + } + + /** + * Update the specified UserField in storage. + * + * @param $id + * @param Request $request + * + * @throws \Prettus\Validator\Exceptions\ValidatorException + * + * @return mixed + */ + public function update($id, Request $request) + { + $field = $this->userFieldRepo->findWithoutFail($id); + + if (empty($field)) { + Flash::error('UserField not found'); + return redirect(route('admin.userfields.index')); + } + + $this->userFieldRepo->update($request->all(), $id); + + Flash::success('Field updated successfully.'); + return redirect(route('admin.userfields.index')); + } + + /** + * Remove the specified UserField from storage. + * + * @param int $id + * + * @return mixed + */ + public function destroy($id) + { + $field = $this->userFieldRepo->findWithoutFail($id); + if (empty($field)) { + Flash::error('Field not found'); + return redirect(route('admin.userfields.index')); + } + + if ($this->userFieldRepo->isInUse($id)) { + Flash::error('This field cannot be deleted, it is in use. Deactivate it instead'); + return redirect(route('admin.userfields.index')); + } + + $this->userFieldRepo->delete($id); + + Flash::success('Field deleted successfully.'); + return redirect(route('admin.userfields.index')); + } +} diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 7775b21e..b3f3bd5a 100755 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -3,19 +3,20 @@ namespace App\Http\Controllers\Auth; use App\Contracts\Controller; -use App\Http\Requests\CreateUserRequest; use App\Models\Enums\UserState; use App\Models\User; +use App\Models\UserField; +use App\Models\UserFieldValue; use App\Repositories\AirlineRepository; use App\Repositories\AirportRepository; use App\Services\UserService; use App\Support\Countries; use App\Support\Timezonelist; -use Illuminate\Contracts\Validation\Validator; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Validator; class RegisterController extends Controller { @@ -61,12 +62,14 @@ class RegisterController extends Controller { $airports = $this->airportRepo->selectBoxList(false, setting('pilots.home_hubs_only')); $airlines = $this->airlineRepo->selectBoxList(); + $userFields = UserField::where(['show_on_registration' => true])->get(); return view('auth.register', [ - 'airports' => $airports, - 'airlines' => $airlines, - 'countries' => Countries::getSelectList(), - 'timezones' => Timezonelist::toArray(), + 'airports' => $airports, + 'airlines' => $airlines, + 'countries' => Countries::getSelectList(), + 'timezones' => Timezonelist::toArray(), + 'userFields' => $userFields, ]); } @@ -88,6 +91,12 @@ class RegisterController extends Controller 'toc_accepted' => 'accepted', ]; + // Dynamically add the required fields + $userFields = UserField::where(['show_on_registration' => true, 'required' => true])->get(); + foreach ($userFields as $field) { + $rules['field_'.$field->slug] = 'required'; + } + if (config('captcha.enabled')) { $rules['g-recaptcha-response'] = 'required|captcha'; } @@ -119,6 +128,15 @@ class RegisterController extends Controller Log::info('User registered: ', $user->toArray()); + $userFields = UserField::all(); + foreach ($userFields as $field) { + $field_name = 'field_'.$field->slug; + UserFieldValue::updateOrCreate([ + 'user_field_id' => $field->id, + 'user_id' => $user->id, + ], ['value' => $opts[$field_name]]); + } + return $user; } @@ -131,8 +149,10 @@ class RegisterController extends Controller * * @return mixed */ - public function register(CreateUserRequest $request) + public function register(Request $request) { + $this->validator($request->all())->validate(); + $user = $this->create($request->all()); if ($user->state === UserState::PENDING) { return view('auth.pending'); diff --git a/app/Http/Controllers/Frontend/ProfileController.php b/app/Http/Controllers/Frontend/ProfileController.php index a0d0bc05..26e1d4df 100644 --- a/app/Http/Controllers/Frontend/ProfileController.php +++ b/app/Http/Controllers/Frontend/ProfileController.php @@ -4,6 +4,8 @@ namespace App\Http\Controllers\Frontend; use App\Contracts\Controller; use App\Models\User; +use App\Models\UserField; +use App\Models\UserFieldValue; use App\Repositories\AirlineRepository; use App\Repositories\AirportRepository; use App\Repositories\UserRepository; @@ -63,16 +65,22 @@ class ProfileController extends Controller */ public function index() { + /** @var User $user */ + $user = Auth::user(); + if (setting('pilots.home_hubs_only')) { $airports = $this->airportRepo->findWhere(['hub' => true]); } else { $airports = $this->airportRepo->all(); } + $userFields = $this->userRepo->getUserFields($user); + return view('profile.index', [ - 'acars' => $this->acarsEnabled(), - 'user' => Auth::user(), - 'airports' => $airports, + 'acars' => $this->acarsEnabled(), + 'user' => $user, + 'airports' => $airports, + 'userFields' => $userFields, ]); } @@ -83,10 +91,12 @@ class ProfileController extends Controller */ public function show($id) { - $user = User::where('id', $id)->first(); + $user = User::with(['fields', 'fields.field']) + ->where('id', $id) + ->first(); + if (empty($user)) { Flash::error('User not found!'); - return redirect(route('frontend.dashboard.index')); } @@ -109,22 +119,27 @@ class ProfileController extends Controller */ public function edit(Request $request) { - $user = User::where('id', Auth::user()->id)->first(); + /** @var \App\Models\User $user */ + $user = User::with(['fields', 'fields.field']) + ->where('id', Auth::user()->id) + ->first(); + if (empty($user)) { Flash::error('User not found!'); - return redirect(route('frontend.dashboard.index')); } $airlines = $this->airlineRepo->selectBoxList(); $airports = $this->airportRepo->selectBoxList(false, setting('pilots.home_hubs_only')); + $userFields = $this->userRepo->getUserFields($user); return view('profile.edit', [ - 'user' => $user, - 'airlines' => $airlines, - 'airports' => $airports, - 'countries' => Countries::getSelectList(), - 'timezones' => Timezonelist::toArray(), + 'user' => $user, + 'airlines' => $airlines, + 'airports' => $airports, + 'countries' => Countries::getSelectList(), + 'timezones' => Timezonelist::toArray(), + 'userFields' => $userFields, ]); } @@ -140,13 +155,20 @@ class ProfileController extends Controller $id = Auth::user()->id; $user = $this->userRepo->findWithoutFail($id); - $validator = Validator::make($request->toArray(), [ + $rules = [ 'name' => 'required', 'email' => 'required|unique:users,email,'.$id, 'airline_id' => 'required', 'password' => 'confirmed', 'avatar' => 'nullable|mimes:jpeg,png,jpg', - ]); + ]; + + $userFields = UserField::where(['show_on_registration' => true, 'required' => true])->get(); + foreach ($userFields as $field) { + $rules['field_'.$field->slug] = 'required'; + } + + $validator = Validator::make($request->toArray(), $rules); if ($validator->fails()) { Log::info('validator failed for user '.$user->ident); @@ -167,6 +189,7 @@ class ProfileController extends Controller if (isset($req_data['avatar']) !== null) { Storage::delete($user->avatar); } + if ($request->hasFile('avatar')) { $avatar = $request->file('avatar'); $file_name = $user->ident.'.'.$avatar->getClientOriginalExtension(); @@ -190,6 +213,16 @@ class ProfileController extends Controller $this->userRepo->update($req_data, $id); + // Save all of the user fields + $userFields = UserField::all(); + foreach ($userFields as $field) { + $field_name = 'field_'.$field->slug; + UserFieldValue::updateOrCreate([ + 'user_field_id' => $field->id, + 'user_id' => $id, + ], ['value' => $request->get($field_name)]); + } + Flash::success('Profile updated successfully!'); return redirect(route('frontend.profile.index')); diff --git a/app/Models/File.php b/app/Models/File.php index 5887d4cb..4dbbc244 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -9,14 +9,14 @@ use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; /** - * @property string $name - * @property string $description - * @property string $disk - * @property string $path - * @property bool $public - * @property int $download_count - * @property string $url - * @property string $filename + * @property string $name + * @property string $description + * @property string $disk + * @property string $path + * @property bool $public + * @property int $download_count + * @property string $url + * @property string $filename */ class File extends Model { diff --git a/app/Models/Journal.php b/app/Models/Journal.php index bc4d8182..bbfce96c 100644 --- a/app/Models/Journal.php +++ b/app/Models/Journal.php @@ -14,11 +14,11 @@ use Carbon\Carbon; * Holds various journals, depending on the morphed_type and morphed_id columns * * @property mixed id - * @property Money $balance - * @property string $currency - * @property Carbon $updated_at - * @property Carbon $post_date - * @property Carbon $created_at + * @property Money $balance + * @property string $currency + * @property Carbon $updated_at + * @property Carbon $post_date + * @property Carbon $created_at * @property \App\Models\Enums\JournalType type * @property mixed morphed_type * @property mixed morphed_id diff --git a/app/Models/Ledger.php b/app/Models/Ledger.php index 51c78763..22334662 100644 --- a/app/Models/Ledger.php +++ b/app/Models/Ledger.php @@ -13,11 +13,11 @@ use Carbon\Carbon; /** * Class Ledger * - * @property Money $balance - * @property string $currency - * @property Carbon $updated_at - * @property Carbon $post_date - * @property Carbon $created_at + * @property Money $balance + * @property string $currency + * @property Carbon $updated_at + * @property Carbon $post_date + * @property Carbon $created_at */ class Ledger extends Model { diff --git a/app/Models/SimBrief.php b/app/Models/SimBrief.php index cfc4b88d..1dcb1b63 100644 --- a/app/Models/SimBrief.php +++ b/app/Models/SimBrief.php @@ -6,10 +6,10 @@ use App\Contracts\Model; use Illuminate\Support\Collection; /** - * @property string $id The Simbrief OFP ID - * @property int $user_id The user that generated this - * @property string $flight_id Optional, if attached to a flight, removed if attached to PIREP - * @property string $pirep_id Optional, if attached to a PIREP, removed if attached to flight + * @property string $id The Simbrief OFP ID + * @property int $user_id The user that generated this + * @property string $flight_id Optional, if attached to a flight, removed if attached to PIREP + * @property string $pirep_id Optional, if attached to a PIREP, removed if attached to flight * @property string $acars_xml * @property string $ofp_xml * @property string $ofp_html diff --git a/app/Models/User.php b/app/Models/User.php index e36850bf..fbccafae 100755 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -10,32 +10,33 @@ use Illuminate\Notifications\Notifiable; use Laratrust\Traits\LaratrustUserTrait; /** - * @property int id - * @property int pilot_id - * @property int airline_id - * @property string name - * @property string name_private Only first name, rest are initials - * @property string email - * @property string password - * @property string api_key - * @property mixed timezone - * @property string ident - * @property string curr_airport_id - * @property string home_airport_id - * @property string avatar - * @property Airline airline - * @property Flight[] flights - * @property int flight_time - * @property int transfer_time - * @property string remember_token - * @property \Carbon\Carbon created_at - * @property \Carbon\Carbon updated_at - * @property Rank rank - * @property Journal journal - * @property int rank_id - * @property int state - * @property bool opt_in - * @property string last_pirep_id + * @property int id + * @property int pilot_id + * @property int airline_id + * @property string name + * @property string name_private Only first name, rest are initials + * @property string email + * @property string password + * @property string api_key + * @property mixed timezone + * @property string ident + * @property string curr_airport_id + * @property string home_airport_id + * @property string avatar + * @property Airline airline + * @property Flight[] flights + * @property int flight_time + * @property int transfer_time + * @property string remember_token + * @property \Carbon\Carbon created_at + * @property \Carbon\Carbon updated_at + * @property Rank rank + * @property Journal journal + * @property int rank_id + * @property int state + * @property bool opt_in + * @property string last_pirep_id + * @property UserFieldValue[] fields * * @mixin \Illuminate\Database\Eloquent\Builder * @mixin \Illuminate\Notifications\Notifiable @@ -212,6 +213,16 @@ class User extends Authenticatable return $this->hasMany(UserAward::class, 'user_id'); } + /** + * The bid rows + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function bids() + { + return $this->hasMany(Bid::class, 'user_id'); + } + public function home_airport() { return $this->belongsTo(Airport::class, 'home_airport_id'); @@ -227,22 +238,9 @@ class User extends Authenticatable return $this->belongsTo(Pirep::class, 'last_pirep_id'); } - /** - * These are the flights they've bid on - */ - // public function flights() - // { - // return $this->belongsToMany(Flight::class, 'bids'); - // } - - /** - * The bid rows - * - * @return \Illuminate\Database\Eloquent\Relations\HasMany - */ - public function bids() + public function fields() { - return $this->hasMany(Bid::class, 'user_id'); + return $this->hasMany(UserFieldValue::class, 'user_id'); } public function pireps() diff --git a/app/Models/UserField.php b/app/Models/UserField.php new file mode 100644 index 00000000..a723437b --- /dev/null +++ b/app/Models/UserField.php @@ -0,0 +1,49 @@ + 'boolean', + 'required' => 'boolean', + 'private' => 'boolean', + 'active' => 'boolean', + ]; + + public static $rules = [ + 'name' => 'required', + 'description' => 'nullable', + ]; + + /** + * Get the slug so we can use it in forms + * + * @return string + */ + public function getSlugAttribute(): string + { + return str_slug($this->name, '_'); + } +} diff --git a/app/Models/UserFieldValue.php b/app/Models/UserFieldValue.php new file mode 100644 index 00000000..608cdfe7 --- /dev/null +++ b/app/Models/UserFieldValue.php @@ -0,0 +1,37 @@ +belongsTo(UserField::class, 'user_field_id'); + } + + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } +} diff --git a/app/Notifications/BaseNotification.php b/app/Notifications/BaseNotification.php index 234ef5c8..ab1357ce 100644 --- a/app/Notifications/BaseNotification.php +++ b/app/Notifications/BaseNotification.php @@ -17,7 +17,7 @@ class BaseNotification extends Notification implements ShouldQueue { // Look in the notifications.channels config and see where this particular // notification can go. Map it to $channels - $klass = get_class($this); + $klass = static::class; $notif_config = config('notifications.channels', []); if (!array_key_exists($klass, $notif_config)) { Log::error('Notification type '.$klass.' missing from notifications config, defaulting to mail'); diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index bf5b2ee8..40bdbaa1 100755 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -262,10 +262,10 @@ class RouteServiceProvider extends ServiceProvider Route::resource('flightfields', 'FlightFieldController') ->middleware('ability:admin,flights'); - // pirep related routes - Route::get('pireps/fares', 'PirepController@fares') - ->middleware('ability:admin,pireps'); + Route::resource('userfields', 'UserFieldController')->middleware('ability:admin,users'); + // pirep related routes + Route::get('pireps/fares', 'PirepController@fares')->middleware('ability:admin,pireps'); Route::get('pireps/pending', 'PirepController@pending') ->middleware('ability:admin,pireps'); diff --git a/app/Repositories/UserFieldRepository.php b/app/Repositories/UserFieldRepository.php new file mode 100644 index 00000000..317b3622 --- /dev/null +++ b/app/Repositories/UserFieldRepository.php @@ -0,0 +1,32 @@ + 'like', + ]; + + /** + * @return string + */ + public function model(): string + { + return UserField::class; + } + + /** + * Return whether or not this field is in use by a value + * + * @param $id + */ + public function isInUse($id): bool + { + return UserFieldValue::where(['user_field_id' => $id])->exists(); + } +} diff --git a/app/Repositories/UserRepository.php b/app/Repositories/UserRepository.php index 416d7ed3..20cf7a56 100644 --- a/app/Repositories/UserRepository.php +++ b/app/Repositories/UserRepository.php @@ -5,12 +5,11 @@ namespace App\Repositories; use App\Contracts\Repository; use App\Models\Enums\UserState; use App\Models\User; +use App\Models\UserField; use App\Repositories\Criteria\WhereCriteria; use Illuminate\Http\Request; +use Illuminate\Support\Collection; -/** - * Class UserRepository - */ class UserRepository extends Repository { protected $fieldSearchable = [ @@ -29,6 +28,26 @@ class UserRepository extends Repository return User::class; } + /** + * Get all of the fields which has the mapped values + * + * @param \App\Models\User $user + * + * @return \App\Models\UserField[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection + */ + public function getUserFields(User $user): Collection + { + return (UserField::all())->map(function ($field, $_) use ($user) { + foreach ($user->fields as $userFieldValue) { + if ($userFieldValue->field->slug === $field->slug) { + $field->value = $userFieldValue->value; + } + } + + return $field; + }); + } + /** * Number of PIREPs that are pending * diff --git a/docker-compose.yml b/docker-compose.yml index 220d37b7..452a3ccb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,6 +28,7 @@ services: mysql: image: mysql:5.7.26 + command: "--innodb_use_native_aio=0" environment: MYSQL_DATABASE: phpvms MYSQL_USER: phpvms diff --git a/modules/Importer/Services/BaseImporter.php b/modules/Importer/Services/BaseImporter.php index 67938181..24fad2b7 100644 --- a/modules/Importer/Services/BaseImporter.php +++ b/modules/Importer/Services/BaseImporter.php @@ -90,7 +90,7 @@ abstract class BaseImporter $idx = $start + 1; $manifest[] = [ - 'importer' => get_class($this), + 'importer' => static::class, 'start' => $start, 'end' => $end, 'message' => 'Importing '.$this->table.' ('.$idx.' - '.$end.' of '.$total_rows.')', diff --git a/modules/Importer/Services/Importers/ClearDatabase.php b/modules/Importer/Services/Importers/ClearDatabase.php index dc4a86cf..e883af1f 100644 --- a/modules/Importer/Services/Importers/ClearDatabase.php +++ b/modules/Importer/Services/Importers/ClearDatabase.php @@ -33,7 +33,7 @@ class ClearDatabase extends BaseImporter { return [ [ - 'importer' => get_class($this), + 'importer' => static::class, 'start' => 0, 'end' => 1, 'message' => 'Clearing database', diff --git a/modules/Importer/Services/Importers/FinalizeImporter.php b/modules/Importer/Services/Importers/FinalizeImporter.php index 7d340de0..837f9b29 100644 --- a/modules/Importer/Services/Importers/FinalizeImporter.php +++ b/modules/Importer/Services/Importers/FinalizeImporter.php @@ -16,7 +16,7 @@ class FinalizeImporter extends BaseImporter { return [ [ - 'importer' => get_class($this), + 'importer' => static::class, 'start' => 0, 'end' => 1, 'message' => 'Finalizing import', diff --git a/resources/views/admin/flightfields/table.blade.php b/resources/views/admin/flightfields/table.blade.php index 15182ff1..46549d84 100644 --- a/resources/views/admin/flightfields/table.blade.php +++ b/resources/views/admin/flightfields/table.blade.php @@ -2,8 +2,7 @@
@component('admin.components.info') - Flights fields that can be filled out. You can still add other custom fields - directly in the flight, but this provides a template for all flights. + These are fields that can be filled out by users @endcomponent
@@ -17,8 +16,8 @@ {{ $field->name }} - {{ Form::open(['route' => ['admin.flightfields.destroy', $field->id], 'method' => 'delete']) }} - ['admin.userfields.destroy', $field->id], 'method' => 'delete']) }} + diff --git a/resources/views/admin/userfields/create.blade.php b/resources/views/admin/userfields/create.blade.php new file mode 100644 index 00000000..cd9d108b --- /dev/null +++ b/resources/views/admin/userfields/create.blade.php @@ -0,0 +1,11 @@ +@extends('admin.app') +@section('title', 'Adding Field') +@section('content') +
+
+ {{ Form::open(['route' => 'admin.userfields.store']) }} + @include('admin.userfields.fields') + {{ Form::close() }} +
+
+@endsection diff --git a/resources/views/admin/userfields/edit.blade.php b/resources/views/admin/userfields/edit.blade.php new file mode 100644 index 00000000..3d93eb72 --- /dev/null +++ b/resources/views/admin/userfields/edit.blade.php @@ -0,0 +1,11 @@ +@extends('admin.app') +@section('title', 'Editing ' . $field->name) +@section('content') +
+
+ {{ Form::model($field, ['route' => ['admin.userfields.update', $field->id], 'method' => 'patch']) }} + @include('admin.userfields.fields') + {{ Form::close() }} +
+
+@endsection diff --git a/resources/views/admin/userfields/fields.blade.php b/resources/views/admin/userfields/fields.blade.php new file mode 100644 index 00000000..39ef3486 --- /dev/null +++ b/resources/views/admin/userfields/fields.blade.php @@ -0,0 +1,64 @@ +
+
+ {{ Form::label('name', 'Name:') }}  * + {{ Form::text('name', null, ['class' => 'form-control']) }} +

{{ $errors->first('name') }}

+
+ +
+ {{ Form::label('description', 'Description:') }} + {{ Form::text('description', null, ['class' => 'form-control']) }} +

{{ $errors->first('description') }}

+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+ +
+ {{ Form::button('Save', ['type' => 'submit', 'class' => 'btn btn-success']) }} + Cancel +
+
diff --git a/resources/views/admin/userfields/index.blade.php b/resources/views/admin/userfields/index.blade.php new file mode 100644 index 00000000..3c7f9a4b --- /dev/null +++ b/resources/views/admin/userfields/index.blade.php @@ -0,0 +1,13 @@ +@extends('admin.app') +@section('title', 'User Fields') +@section('actions') +
  • Add Field
  • +@endsection +@section('content') +
    +
    + @include('admin.userfields.table') +
    +
    +@endsection + diff --git a/resources/views/admin/userfields/table.blade.php b/resources/views/admin/userfields/table.blade.php new file mode 100644 index 00000000..471a5345 --- /dev/null +++ b/resources/views/admin/userfields/table.blade.php @@ -0,0 +1,33 @@ +
    + +
    + @component('admin.components.info') + These are custom fields that can be filled out by a user + @endcomponent +
    + + + + + + + + @foreach($fields as $field) + + + + + @endforeach + +
    Name
    {{ $field->name }} + {{ Form::open(['route' => ['admin.userfields.destroy', $field->id], 'method' => 'delete']) }} + + + + {{ Form::button('', + ['type' => 'submit', 'class' => 'btn btn-sm btn-danger btn-icon', + 'onclick' => "return confirm('Are you sure?')"]) }} + {{ Form::close() }} +
    +
    diff --git a/resources/views/admin/users/fields.blade.php b/resources/views/admin/users/fields.blade.php index 241d9330..f024c6ba 100644 --- a/resources/views/admin/users/fields.blade.php +++ b/resources/views/admin/users/fields.blade.php @@ -94,10 +94,11 @@
    - {{-- - - - --}} + + + @@ -126,6 +127,22 @@ + + @if($user->fields) + + + + + {{-- Custom Fields --}} + @foreach($user->fields as $field) + + + + + @endforeach + @endif
    API Key{{ $user->api_key }}
    +
    User Details
    +
    Total Flights {{ $user->flights }}@lang('profile.opt-in') {{ $user->opt_in ? __('common.yes') : __('common.no') }}
    +
    Custom Fields
    +
    {{ $field->field->name }}{{ $field->value }}
    diff --git a/resources/views/admin/users/index.blade.php b/resources/views/admin/users/index.blade.php index fb6793ab..6edef019 100644 --- a/resources/views/admin/users/index.blade.php +++ b/resources/views/admin/users/index.blade.php @@ -2,6 +2,10 @@ @section('title', 'Users') @section('actions') +
  • + Profile Fields +
  • +
  • @lang(UserState::label(UserState::PENDING))
  • diff --git a/resources/views/layouts/default/auth/register.blade.php b/resources/views/layouts/default/auth/register.blade.php index 6e50b105..a393a337 100644 --- a/resources/views/layouts/default/auth/register.blade.php +++ b/resources/views/layouts/default/auth/register.blade.php @@ -85,6 +85,18 @@

    {{ $errors->first('password_confirmation') }}

    @endif + @if($userFields) + @foreach($userFields as $field) + +
    + {{ Form::text('field_'.$field->slug, null, ['class' => 'form-control']) }} +
    + @if ($errors->has('field_'.$field->slug)) +

    {{ $errors->first('field_'.$field->slug) }}

    + @endif + @endforeach + @endif + @if(config('captcha.enabled'))
    value }} @endif
    -

    {{ $errors->first($field->slug) }}

    +

    {{ $errors->first('field_'.$field->slug) }}

    diff --git a/resources/views/layouts/default/profile/custom_fields.blade.php b/resources/views/layouts/default/profile/custom_fields.blade.php new file mode 100644 index 00000000..1427ec68 --- /dev/null +++ b/resources/views/layouts/default/profile/custom_fields.blade.php @@ -0,0 +1,16 @@ + + + {{ $field->field->name }} + @if($field->field->required === true) + * + @endif + + +
    + {{ Form::text($field->field->slug, $field->value, [ + 'class' => 'form-control', + ]) }} +
    +

    {{ $errors->first('field_'.$field->slug) }}

    + + diff --git a/resources/views/layouts/default/profile/fields.blade.php b/resources/views/layouts/default/profile/fields.blade.php index ae6ad93e..83a72fb5 100644 --- a/resources/views/layouts/default/profile/fields.blade.php +++ b/resources/views/layouts/default/profile/fields.blade.php @@ -108,6 +108,27 @@ @endif + + {{-- Custom fields --}} + @foreach($userFields as $field) + + + {{ $field->name }} + @if($field->required === true) + * + @endif + + +
    + {{ Form::text('field_'.$field->slug, $field->value, [ + 'class' => 'form-control', + ]) }} +
    +

    {{ $errors->first('field_'.$field->slug) }}

    + + + @endforeach + @lang('profile.opt-in') diff --git a/resources/views/layouts/default/profile/index.blade.php b/resources/views/layouts/default/profile/index.blade.php index 05b978b8..6e0c3d96 100644 --- a/resources/views/layouts/default/profile/index.blade.php +++ b/resources/views/layouts/default/profile/index.blade.php @@ -128,4 +128,20 @@ @endif + +
    +
    +
    + + @foreach($userFields as $field) + @if($field->public === true) + + + + + @endif + @endforeach +
    {{ $field->name }}{{ $field->value }}
    +
    +
    @endsection