7.0.0-beta3 Release (#541)

* 391 Notification refactorings (#441)

* Refactor notifications to allow easier plugins

* Notification refactoring

* Formatting

* Move news to NewsService; cleanup of events

* More refactoring; added send email out for news item and the template

* Formatting

* Formatting

* Fix missing newsRepo (#445)

* Refactor and add importer to Installer module #443 (#444)

* Refactor and add importer to Installer module #443

* Refactor for finances to use in import

* Import groups into roles

* Formatting

* Formatting

* Add interface in installer for import

* Notes about importing

* Check for installer folder

* Formatting

* Fix pirep->user mapping

* Unused import

* Formatting

* Replace importer with AJAX powered; better error handling #443 (#447)

* Replace importer with AJAX powered; better error handling #443

* Formatting

* Fix command line importer

* Remove bootstrap cache (#448)

* Cleanup the bootstrap/cache directory when packaging

* Fix removal of bootstrap cache

* Formatting

* Stricter checks on ACARS API data (#451)

* Stricter checks on ACARS API data

* More checks

* Fix for flight_number check forcing to exist

* Allow nullable on flight_id

* Avoid proc_open use #455 (#456)

* Use PhpExecutableFinder() closes #457 #458 (#460)

* Use DateTimeZone instead of int for creating datetime

closes #461

* Fix CSV imports giving Storage class not found #454 (#462)

* Fix CSV imports giving Storage class not found #454

* Update yarn files for security alert

* Add PHP 7.4 support (#464)

* Add PHP 7.4 to build matrix

* DB fix

* YAML parser fix in test data

* Show versions

* Package updates

* Track used ICAOs

* 7.4 METAR parsing fix

* METAR parser fix

* Formatting

* Add meters to response units

* Call instance for unit conversion

* Return value

* Catch exception for unknown quantity

* Comment fix

* Formatting

* METAR parsing fixes on PHP 7.4

* Package updates

* More random airport ID

* More random airport ID

* Properly disable toolbar

* Semver written out to version file

* Use dev as default identifier

* Fix BindingResolutionError when debug toolbar isn't present (#465)

* Fix BindingResolutionError when debug toolbar isn't present

* Formatting

* Split the importer module out from the installer module (#468)

* Split the importer module out from the installer module

* Cleanup of unused imports

* Move updater into separate module #453

* Remove unused imports/formatting

* Disable the install and importer modules at the end of the setup

* Unused imports; update IJ style

* test explicit stage for php+mysql

* add more to matrix

* Add different MariaDB versions

* undo

* Cleanup Model doc

* Pilots cannot use the dashboard or flights without admin rights (#481)

* Use auth middleware instead of specific groups for logged in state

* Auth check for admin access

* Check user admin access for updates

* Formatting

* Allow nullable field and calculate distance if nulled for flight import #478 (#482)

* Check for no roles being attached #480 (#483)

* Return the flight fares if there are no subfleet fares #488 (#489)

* Return the flight fares if there are no subfleet fares #488

* Formatting

* Formatting

* Account for units when entering fuel amounts #493

* Search for ICAO not working properly (#496)

* /flights and /flights/search direct to the same endpoint

* Properly set the distance/planned_distance on save (#497)

* 491 Installation Error (#495)

* Disable CSRF token

* Add error handling around looking up the theme and set a default

* Note about logs in issue template

* Formatting

* Fix GeoService errors when viewing PIREP #498 (#499)

* Add new command to export a specific PIREP for debugging (#501)

* Set a default model value for airports on PIREP (#500)

* Set a default model value for airports on PIREP

* Fix airport icao reference

* Default airport models

* Catch broader exception writing out config files #491

* style

* Add reference to docs on doc site (#502)

* Properly create/update rows importing #486 (#503)

* Add base Dockerfile for Dockerhub builds (#504)

* New subfleet not being attached to an airline on import #479 (#505)

* Fix subfleet not being attached to an airline on creation in import #479

* Call airline name with optional() around subfleet

* Minor cleanup

* Search flights by subfleet #484 (#506)

* API level search of flights #484

* Add Subfleet to flights page for search

* Make the fuel used optional (#512)

* Add make to Docker container

* Add getRootDomain() to Utils (#514)

* Show admin dropdown for admin-access ability (#515)

* Show admin dropdown for admin-access ability closes #509

* Formatting

* Check user permissions on the routes #508 (#516)

* Check user permissions on the routes #508

* Formatting

* Return default value on exception for setting()

* Correct text for no subfleets #507 (#518)

* Add a public_url() helper #513 (#519)

* Reduce number of queries for update check (#520)

* Try to clear caches before updating (#522)

* Try to clear caches before updating

* Add clear-compiled to maintenance cache list

* Formatting

* Set PIREPs page to public (#526)

Set PIREPs page to public

* Fix live and route map errors #527 (#528)

* Add menu bar for mobile (#529)

* Format all blade templates to 2 spaces #530 (#531)

* Fix PIREP edit endpoint closes #533 (#534)

* Fix import during flight cron #532 (#535)

* PIREPS resource except for show (#536)

* Use optional() around the airport fields (#537)

* Use optional() around the airport fields

* Add null-coalesce around full_name

* Add link to download ACARS config from profile (#539)

* Add link to download ACARS config from profile

* Formatting

* Update xml config file template (#540)
This commit is contained in:
Nabeel S
2020-02-08 13:29:34 -05:00
committed by GitHub
parent a335ed8e9b
commit 073e48c396
515 changed files with 13939 additions and 10717 deletions

3
modules/.gitignore vendored
View File

@@ -3,6 +3,9 @@
/*
/*/
!.gitignore
!/Awards
!/Importer
!/Installer
!/Sample
!/Updater
!/Vacentral

View File

@@ -0,0 +1,52 @@
<?php
namespace Modules\Awards\Awards;
use App\Contracts\Award;
/**
* Simple example of an awards class, where you can apply an award when a user
* has 100 flights. All award classes need to extend Award and implement the check() method
*
* See: http://docs.phpvms.net/customizing/awards
*/
class PilotFlightAwards extends Award
{
/**
* Set the name of this award class to make it easier to see when
* assigning to a specific award
*
* @var string
*/
public $name = 'Pilot Flights';
/**
* The description to show under the parameters field, so the admin knows
* what the parameter actually controls. You can leave this blank if there
* isn't a parameter.
*
* @var string
*/
public $param_description = 'The number of flights at which to give this award';
/**
* If the user has over N flights, then we can give them this award. This method
* only needs to return a true or false of whether it should be awarded or not.
*
* If no parameter is passed in, just default it to 100. You should check if there
* is a parameter or not. You can call it whatever you want, since that would make
* sense with the $param_description.
*
* @param int|null $number_of_flights The parameters passed in from the UI
*
* @return bool
*/
public function check($number_of_flights = null): bool
{
if (!$number_of_flights) {
$number_of_flights = 100;
}
return $this->user->flights >= $number_of_flights;
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Modules\Awards\Providers;
use Illuminate\Support\ServiceProvider;
class AwardServiceProvider extends ServiceProvider
{
}

View File

@@ -0,0 +1,14 @@
{
"name": "Awards",
"alias": "awards",
"description": "",
"keywords": [],
"active": 1,
"order": 0,
"providers": [
"Modules\\Awards\\Providers\\AwardServiceProvider"
],
"aliases": {},
"files": [],
"requires": []
}

View File

@@ -0,0 +1,7 @@
<?php
return [
'importer' => [
'batch_size' => 20,
],
];

View File

@@ -0,0 +1,40 @@
<?php
namespace Modules\Importer\Console\Commands;
use App\Contracts\Command;
use Illuminate\Support\Facades\Log;
use Modules\Importer\Services\ImporterService;
class ImportFromClassicCommand extends Command
{
protected $signature = 'phpvms:importer {db_host} {db_name} {db_user} {db_pass?} {table_prefix=phpvms_}';
protected $description = 'Import from an older version of phpVMS';
/**
* Run dev related commands
*/
public function handle()
{
$creds = [
'host' => $this->argument('db_host'),
'name' => $this->argument('db_name'),
'user' => $this->argument('db_user'),
'pass' => $this->argument('db_pass'),
'table_prefix' => $this->argument('table_prefix'),
];
$importerSvc = new ImporterService();
$importerSvc->saveCredentials($creds);
$manifest = $importerSvc->generateImportManifest();
foreach ($manifest as $record) {
try {
$importerSvc->run($record['importer'], $record['start']);
} catch (\Exception $e) {
Log::error($e->getMessage());
}
}
}
}

View File

@@ -0,0 +1,137 @@
<?php
namespace Modules\Importer\Http\Controllers;
use App\Contracts\Controller;
use App\Services\Installer\DatabaseService;
use App\Services\Installer\InstallerService;
use App\Support\Utils;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Modules\Importer\Services\ImporterService;
class ImporterController extends Controller
{
private $dbSvc;
private $importerSvc;
public function __construct(DatabaseService $dbSvc, ImporterService $importerSvc)
{
$this->dbSvc = $dbSvc;
$this->importerSvc = $importerSvc;
Utils::disableDebugToolbar();
}
/**
* Show the main page for the importer; show form for the admin email
* and the credentials for the other database
*
* @param \Illuminate\Http\Request $request
*
* @return mixed
*/
public function index(Request $request)
{
return view('importer::step1-configure');
}
/**
* Check the database connection
*
* @param \Illuminate\Http\Request $request
*
* @return mixed
*/
public function dbtest(Request $request)
{
$status = 'success'; // success|warn|danger
$message = 'Database connection looks good!';
try {
$this->dbSvc->checkDbConnection(
$request->post('db_conn'),
$request->post('db_host'),
$request->post('db_port'),
$request->post('db_name'),
$request->post('db_user'),
$request->post('db_pass')
);
} catch (\Exception $e) {
$status = 'danger';
$message = 'Failed! '.$e->getMessage();
}
return view('importer::dbtest', [
'status' => $status,
'message' => $message,
]);
}
/**
* The post from the above
*
* @param \Illuminate\Http\Request $request
*
* @return mixed
*/
public function config(Request $request)
{
try {
// Save the credentials to use later
$this->importerSvc->saveCredentialsFromRequest($request);
// Generate the import manifest
$manifest = $this->importerSvc->generateImportManifest();
} catch (\Exception $e) {
Log::error($e->getMessage());
// Send it to run, step1
return view('importer::error', [
'error' => $e->getMessage(),
]);
}
// Send it to run, step1
return view('importer::step2-processing', [
'manifest' => $manifest,
]);
}
/**
* Run the importer. Pass in query string with a few different parameters:
*
* stage=STAGE NAME
* start=record_start
*
* @param \Illuminate\Http\Request $request
*
* @throws \Exception
*
* @return mixed
*/
public function run(Request $request)
{
$importer = $request->input('importer');
$start = $request->input('start');
Log::info('Starting stage '.$importer.' from offset '.$start);
$this->importerSvc->run($importer, $start);
return response()->json([
'message' => 'completed',
]);
}
/**
* Complete the import
*/
public function complete()
{
$installerSvc = app(InstallerService::class);
$installerSvc->disableInstallerModules();
return redirect('/');
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace Modules\Importer\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Modules\Importer\Console\Commands\ImportFromClassicCommand;
class ImporterServiceProvider extends ServiceProvider
{
/**
* Boot the application events.
*/
public function boot()
{
$this->registerCommands();
$this->registerRoutes();
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
}
protected function registerCommands()
{
$this->commands([
ImportFromClassicCommand::class,
]);
}
/**
* Register the routes
*/
protected function registerRoutes()
{
Route::group([
'as' => 'importer.',
'prefix' => 'importer',
'middleware' => ['web'],
'namespace' => 'Modules\Importer\Http\Controllers',
], function () {
Route::get('/', 'ImporterController@index')->name('index');
Route::post('/config', 'ImporterController@config')->name('config');
Route::post('/dbtest', 'ImporterController@dbtest')->name('dbtest');
// Run the actual importer process. Additional middleware
Route::post('/run', 'ImporterController@run')->middleware('api')->name('run');
Route::post('/complete', 'ImporterController@complete')->name('complete');
});
}
/**
* Register config.
*/
protected function registerConfig()
{
$this->mergeConfigFrom(__DIR__.'/../Config/config.php', 'importer');
}
/**
* Register views.
*/
public function registerViews()
{
$viewPath = resource_path('views/modules/importer');
$sourcePath = __DIR__.'/../Resources/views';
$this->publishes([$sourcePath => $viewPath], 'views');
$paths = array_map(
function ($path) {
return $path.'/modules/importer';
},
\Config::get('view.paths')
);
$paths[] = $sourcePath;
$this->loadViewsFrom($paths, 'importer');
}
/**
* Register translations.
*/
public function registerTranslations()
{
$langPath = resource_path('lang/modules/importer');
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, 'importer');
} else {
$this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'importer');
}
}
/**
* Get the services provided by the provider.
*/
public function provides(): array
{
return [];
}
}

View File

@@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title') - importer</title>
<link rel="shortcut icon" type="image/png" href="{{ public_asset('/assets/img/favicon.png') }}"/>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no'
name='viewport'/>
<meta name="base-url" content="{!! url('') !!}">
<meta name="api-key" content="{!! Auth::check() ? Auth::user()->api_key: '' !!}">
<meta name="csrf-token" content="{!! csrf_token() !!}">
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700,200" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/bootstrap.min.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/now-ui-kit.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/installer/css/vendor.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/styles.css') }}" rel="stylesheet"/>
<link rel="stylesheet"
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
<style>
.table tr:first-child td {
border-top: 0px;
}
@yield('css')
</style>
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-toggleable-md" style="background: #067ec1;">
<div class="container" style="width: 85%!important;">
<div class="navbar-translate">
<p class="navbar-brand text-white" data-placement="bottom" target="_blank">
<a href="{{ url('/') }}">
<img src="{{ public_asset('/assets/img/logo_blue_bg.svg') }}" width="135px" style=""/>
</a>
</p>
</div>
<div class="justify-content-center" id="navigation" style="margin-left: 50px; color: white; font-size: 20px;">
@yield('title')
</div>
</div>
</nav>
<!-- End Navbar -->
{{--<div class="clearfix" style="height: 25px;"></div>--}}
<div class="wrapper">
<div class="clear"></div>
<div class="container" style="width: 50%">
<div class="row">
<div class="col-12">
@include('importer::flash.message')
@yield('content')
</div>
</div>
</div>
<div class="clearfix" style="height: 200px;"></div>
</div>
{{--<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>--}}
<script src="{{ public_mix('/assets/global/js/vendor.js') }}"></script>
<script src="{{ public_mix('/assets/frontend/js/vendor.js') }}"></script>
<script src="{{ public_mix('/assets/frontend/js/app.js') }}"></script>
<script src="{{ public_asset('/assets/installer/js/vendor.js') }}" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script>
hljs.configure({languages: ['sh']});
$(document).ready(function () {
$(".select2").select2();
$('pre code').each(function (i, block) {
hljs.fixMarkup(block);
hljs.highlightBlock(block);
});
});
</script>
@yield('scripts')
</body>
</html>

View File

@@ -0,0 +1,20 @@
@extends('importer::app')
@section('title', 'Import Completed!')
@section('content')
<div style="align-content: center;">
{{ Form::open(['route' => 'importer.complete', 'method' => 'GET']) }}
<h4>Installer Completed!</h4>
<p>Edit the <span class="code">config.php</span> to fill in some additional settings. </p>
<p>Click the button to proceed to the login screen!</p>
<p style="text-align: right">
{{ Form::submit('Import Complete! Continue to Log-In >>',
['class' => 'btn btn-success'])
}}
</p>
{{ Form::close() }}
</div>
@endsection

View File

@@ -0,0 +1,8 @@
<div class="alert alert-{{ $status }}" role="alert">
<div class="container">
<div class="alert-icon">
<i class="now-ui-icons ui-1_bell-53"></i>
</div>
{{ $message }}
</div>
</div>

View File

@@ -0,0 +1,9 @@
@extends('importer::app')
@section('title', 'Import Error!')
@section('content')
<div style="align-content: center;">
<h4>Error!</h4>
<p class="text-danger">{{ $error }}</p>
</div>
@endsection

View File

@@ -0,0 +1,6 @@
@if($errors->has($field))
<p class="text-danger" style="margin-top: 10px;">{{ $errors->first($field) }}</p>
{{--<div class="alert alert-danger" role="alert" style="margin-top: 10px;">
{{ $errors->first($field) }}
</div>--}}
@endif

View File

@@ -0,0 +1,11 @@
@foreach (session('flash_notification', collect())->toArray() as $message)
<div class="alert alert-danger" role="alert">
<div class="container">
<div class="alert-icon">
<i class="now-ui-icons ui-2_like"></i>
</div>
{{ $message['message'] }}
</div>
</div>
@endforeach
{{ session()->forget('flash_notification') }}

View File

@@ -0,0 +1,136 @@
@extends('importer::app')
@section('title', 'Import Configuration')
@section('content')
<div style="align-content: center;">
{{ Form::open(['route' => 'importer.config', 'method' => 'POST']) }}
<table class="table" width="25%">
<tr>
<td colspan="2">
<h4>IMPORTANT NOTES</h4>
<ul>
<li>If you have more than 1000 PIREPs or flights, it's best to use the command-line importer!
<a href="http://docs.phpvms.net/setup/importing-from-v2-v5" target="_blank">Click here</a> to
see the documentation of how to use it.
</li>
<li><strong>THIS WILL WIPE OUT YOUR EXISTING DATA</strong> - this is required to make sure that things like
pilot IDs match up
</li>
</ul>
</td>
</tr>
<tr>
<td colspan="2"><h4>Site Config</h4></td>
</tr>
<tr>
<td>Admin Email</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'email', '', ['class' => 'form-control']) }}
<p>The admin's email address, the password for this will be reset</p>
</div>
</td>
</tr>
<tr>
<td colspan="2"><h4>Database Config</h4></td>
</tr>
<tbody id="mysql_settings">
<tr>
<td>Database Host</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_host', '127.0.0.1', ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database Port</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_port', '3306', ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database Name</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_name', 'phpvms', ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database User</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_user', null, ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database Password</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_pass', null, ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td colspan="2" style="text-align: right;">
{{ Form::submit('Test Database Credentials', ['class' => 'btn btn-info', 'id' => 'dbtest_button']) }}
</td>
</tr>
</tbody>
<tr>
<td>Database Prefix</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_prefix', 'phpvms_', ['class' => 'form-control']) }}
<p>Prefix of the tables, if you're using one</p>
</div>
</td>
</tr>
</table>
<div id="dbtest"></div>
<p style="text-align: right">
{{ Form::submit('Start Importer >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
</div>
@endsection
@section('scripts')
<script>
$(document).ready(() => {
$("#dbtest_button").click((e) => {
e.preventDefault();
const opts = {
_token: "{{ csrf_token() }}",
db_conn: 'mysql',
db_host: $("input[name=db_host]").val(),
db_port: $("input[name=db_port]").val(),
db_name: $("input[name=db_name]").val(),
db_user: $("input[name=db_user]").val(),
db_pass: $("input[name=db_pass]").val(),
};
$.post("{{ route('importer.dbtest') }}", opts, (data) => {
$("#dbtest").html(data);
})
})
});
</script>
@endsection

View File

@@ -0,0 +1,156 @@
@extends('importer::app')
@section('title', 'Import Configuration')
@section('content')
<div style="align-content: center;">
{{ Form::open(['route' => 'importer.complete', 'method' => 'POST']) }}
<table class="table" width="25%">
<tr>
<td colspan="2"><h4>Running Importer</h4></td>
</tr>
<tr>
<td colspan="2">
<div class="progress">
<div id="progress" class="progress-bar" style="width: 0%"></div>
</div>
<div>
<p id="message" style="margin-top: 7px;"></p>
<p id="error" class="text-danger" style="margin-top: 7px;"></p>
</div>
</td>
</tr>
</table>
<p style="text-align: right">
{{ Form::submit('Complete Import', [
'id' => 'completebutton',
'class' => 'btn btn-success'
]) }}
</p>
{{ Form::close() }}
</div>
@endsection
@section('scripts')
<script>
const manifest = {!!json_encode($manifest) !!};
/**
* Run each step of the importer
*/
async function startImporter() {
let current = 1;
const total_steps = manifest.length;
/**
* Update the progress bar
*/
const setProgress = (current, message) => {
const percent = Math.round(current / total_steps * 100);
$("#progress").css("width", `${percent}%`);
$("#message").text(message);
};
/**
* Sleep for a given interval
*/
const sleep = (timeout) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, timeout);
});
};
const setError = (error) => {
let message = '';
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
message = error.response.data.message;
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
message = error.request;
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
message = error.message;
}
$("#error").text(`Error processing, check the logs: ${message}`);
console.log(error.config);
};
/**
* Call the endpoint as a POST
*/
const runStep = async function (stage) {
setProgress(current, stage.message);
try {
return await phpvms.request({
method: 'post',
url: '/importer/run',
data: {
importer: stage.importer,
start: stage.start,
}
});
} catch (e) {
if (e.response.status === 504) {
const err = $("#error");
console.log('got timeout, retrying');
err.text(`Timed out, attempting to retry`);
// await sleep(5000);
const val = await runStep(stage);
err.text('');
return val;
}
setError(e);
throw e;
}
};
let errors = false;
const complete_button = $("#completebutton");
complete_button.hide();
for (let stage of manifest) {
console.log(`Running ${stage.importer} step ${stage.start}`);
try {
await runStep(stage);
} catch (e) {
errors = true;
break;
}
current++;
}
if (!errors) {
$("#message").text('Done!');
complete_button.show();
}
}
$(document).ready(() => {
startImporter();
});
</script>
@endsection

View File

@@ -0,0 +1,135 @@
<?php
namespace Modules\Importer\Services;
use App\Services\Installer\LoggerTrait;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Modules\Importer\Utils\IdMapper;
use Modules\Importer\Utils\ImporterDB;
abstract class BaseImporter implements ShouldQueue
{
use LoggerTrait;
use Dispatchable;
use InteractsWithQueue;
use Queueable;
/**
* Holds the connection to the legacy database
*
* @var \Modules\Importer\Utils\ImporterDB
*/
protected $db;
/**
* The mapper class used for old IDs to new IDs
*
* @var \Illuminate\Contracts\Foundation\Application|mixed
*/
protected $idMapper;
/**
* The legacy table this importer targets
*
* @var string
*/
protected $table;
public function __construct()
{
$importerService = app(ImporterService::class);
$this->db = new ImporterDB($importerService->getCredentials());
$this->idMapper = app(IdMapper::class);
}
/**
* The start method. Takes the offset to start from
*
* @param int $start
*
* @return mixed
*/
abstract public function run($start = 0);
/**
* Return a manifest of the import tasks to run. Returns an array of objects,
* which contain a start and end row
*
* @return array
*/
public function getManifest(): array
{
$manifest = [];
$start = 0;
$total_rows = $this->db->getTotalRows($this->table);
do {
$end = $start + $this->db->batchSize;
if ($end > $total_rows) {
$end = $total_rows;
}
$idx = $start + 1;
$manifest[] = [
'importer' => get_class($this),
'start' => $start,
'end' => $end,
'message' => 'Importing '.$this->table.' ('.$idx.' - '.$end.' of '.$total_rows.')',
];
$start += $this->db->batchSize;
} while ($start < $total_rows);
return $manifest;
}
/**
* Determine what columns exist, can be used for feature testing between v2/v5
*
* @return array
*/
public function getColumns(): array
{
}
/**
* @param $date
*
* @return Carbon
*/
protected function parseDate($date)
{
$carbon = Carbon::parse($date);
return $carbon;
}
/**
* Take a decimal duration and convert it to minutes
*
* @param $duration
*
* @return float|int
*/
protected function convertDuration($duration)
{
if (strpos($duration, '.') !== false) {
$delim = '.';
} elseif (strpos($duration, ':')) {
$delim = ':';
} else {
// no delimiter, assume it's just a straight hour
return (int) $duration * 60;
}
$hm = explode($delim, $duration);
$hours = (int) $hm[0] * 60;
$mins = (int) $hm[1];
return $hours + $mins;
}
}

View File

@@ -0,0 +1,135 @@
<?php
namespace Modules\Importer\Services;
use App\Contracts\Service;
use App\Repositories\KvpRepository;
use Exception;
use Illuminate\Http\Request;
use Modules\Importer\Services\Importers\AircraftImporter;
use Modules\Importer\Services\Importers\AirlineImporter;
use Modules\Importer\Services\Importers\AirportImporter;
use Modules\Importer\Services\Importers\ClearDatabase;
use Modules\Importer\Services\Importers\FinalizeImporter;
use Modules\Importer\Services\Importers\FlightImporter;
use Modules\Importer\Services\Importers\GroupImporter;
use Modules\Importer\Services\Importers\PirepImporter;
use Modules\Importer\Services\Importers\RankImport;
use Modules\Importer\Services\Importers\UserImport;
class ImporterService extends Service
{
private $CREDENTIALS_KEY = 'legacy.importer.db';
/**
* @var KvpRepository
*/
private $kvpRepo;
/**
* The list of importers, in proper order
*/
private $importList = [
ClearDatabase::class,
RankImport::class,
GroupImporter::class,
AirlineImporter::class,
AircraftImporter::class,
AirportImporter::class,
FlightImporter::class,
UserImport::class,
PirepImporter::class,
FinalizeImporter::class,
];
public function __construct()
{
$this->kvpRepo = app(KvpRepository::class);
}
/**
* Save the credentials from a request
*
* @param \Illuminate\Http\Request $request
*/
public function saveCredentialsFromRequest(Request $request)
{
$creds = [
'admin_email' => $request->post('email'),
'host' => $request->post('db_host'),
'port' => $request->post('db_port'),
'name' => $request->post('db_name'),
'user' => $request->post('db_user'),
'pass' => $request->post('db_pass'),
'table_prefix' => $request->post('db_prefix'),
];
$this->saveCredentials($creds);
}
/**
* Save the given credentials
*
* @param array $creds
*/
public function saveCredentials(array $creds)
{
$creds = array_merge([
'admin_email' => '',
'host' => '',
'port' => '',
'name' => '',
'user' => '',
'pass' => 3306,
'table_prefix' => 'phpvms_',
], $creds);
$this->kvpRepo->save($this->CREDENTIALS_KEY, $creds);
}
/**
* Get the saved credentials
*/
public function getCredentials()
{
return $this->kvpRepo->get($this->CREDENTIALS_KEY);
}
/**
* Create a manifest of the import. Creates an array with the importer name,
* which then has a subarray of all of the different steps/stages it needs to run
*/
public function generateImportManifest()
{
$manifest = [];
foreach ($this->importList as $importerKlass) {
/** @var \Modules\Importer\Services\BaseImporter $importer */
$importer = new $importerKlass();
$manifest = array_merge($manifest, $importer->getManifest());
}
return $manifest;
}
/**
* Run a given stage
*
* @param $importer
* @param int $start
*
* @throws \Exception
*
* @return int|void
*/
public function run($importer, $start = 0)
{
if (!in_array($importer, $this->importList)) {
throw new Exception('Unknown importer "'.$importer.'"');
}
/** @var $importerInst \Modules\Importer\Services\BaseImporter */
$importerInst = new $importer();
$importerInst->run($start);
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Modules\Importer\Services\Importers;
use App\Models\Aircraft;
use App\Models\Airline;
use App\Models\Subfleet;
use Modules\Importer\Services\BaseImporter;
class AircraftImporter extends BaseImporter
{
public $table = 'aircraft';
/**
* CONSTANTS
*/
public const SUBFLEET_NAME = 'Imported Aircraft';
public function run($start = 0)
{
$this->comment('--- AIRCRAFT IMPORT ---');
$subfleet = $this->getSubfleet();
$this->info('Subfleet ID is '.$subfleet->id);
$count = 0;
foreach ($this->db->readRows($this->table, $start) as $row) {
$where = [
'name' => $row->fullname,
'registration' => $row->registration,
];
$aircraft = Aircraft::firstOrCreate($where, [
'icao' => $row->icao,
'subfleet_id' => $subfleet->id,
'active' => $row->enabled,
]);
$this->idMapper->addMapping('aircraft', $row->id, $aircraft->id);
if ($aircraft->wasRecentlyCreated) {
$count++;
}
}
$this->info('Imported '.$count.' aircraft');
}
/**
* Return the subfleet
*
* @return mixed
*/
protected function getSubfleet()
{
$airline = Airline::first();
$subfleet = Subfleet::firstOrCreate([
'airline_id' => $airline->id,
'name' => self::SUBFLEET_NAME,
], ['type' => 'PHPVMS']);
return $subfleet;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Modules\Importer\Services\Importers;
use App\Models\Airline;
use Illuminate\Support\Facades\Log;
use Modules\Importer\Services\BaseImporter;
class AirlineImporter extends BaseImporter
{
public $table = 'airlines';
/**
* @param int $start
*/
public function run($start = 0)
{
$this->comment('--- AIRLINE IMPORT ---');
$count = 0;
foreach ($this->db->readRows($this->table, $start) as $row) {
$attrs = [
'iata' => $row->code,
'name' => $row->name,
'active' => $row->enabled,
];
$w = ['icao' => $row->code];
//$airline = Airline::firstOrCreate($w, $attrs);
$airline = Airline::create(array_merge($w, $attrs));
$this->idMapper->addMapping('airlines', $row->id, $airline->id);
$this->idMapper->addMapping('airlines', $row->code, $airline->id);
Log::debug('Mapping '.$row->id.'/'.$row->code.' to ID '.$airline->id);
if ($airline->wasRecentlyCreated) {
$count++;
}
}
$this->info('Imported '.$count.' airlines');
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Modules\Importer\Services\Importers;
use App\Models\Airport;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Log;
use Modules\Importer\Services\BaseImporter;
class AirportImporter extends BaseImporter
{
protected $table = 'airports';
public function run($start = 0)
{
$this->comment('--- AIRPORT IMPORT ---');
$fields = [
'icao',
'name',
'country',
'lat',
'lng',
'hub',
];
$count = 0;
foreach ($this->db->readRows($this->table, $start, $fields) as $row) {
$attrs = [
'id' => trim($row->icao),
'icao' => trim($row->icao),
'name' => $row->name,
'country' => $row->country,
'lat' => $row->lat,
'lon' => $row->lng,
'hub' => $row->hub,
];
$w = ['id' => $attrs['id']];
//$airport = Airport::updateOrCreate($w, $attrs);
try {
$airport = Airport::create(array_merge($w, $attrs));
} catch (QueryException $e) {
$sqlState = $e->errorInfo[0];
$errorCode = $e->errorInfo[1];
if ($sqlState === '23000' && $errorCode === 1062) {
Log::info('Found duplicate for '.$row->icao.', ignoring');
return true;
}
return false;
}
if ($airport->wasRecentlyCreated) {
$count++;
}
}
$this->info('Imported '.$count.' airports');
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace Modules\Importer\Services\Importers;
use App\Models\Acars;
use App\Models\Airline;
use App\Models\Airport;
use App\Models\Bid;
use App\Models\Expense;
use App\Models\File;
use App\Models\Flight;
use App\Models\FlightField;
use App\Models\FlightFieldValue;
use App\Models\Journal;
use App\Models\JournalTransaction;
use App\Models\News;
use App\Models\Pirep;
use App\Models\Subfleet;
use App\Models\User;
use App\Models\UserAward;
use Illuminate\Support\Facades\DB;
use Modules\Importer\Services\BaseImporter;
class ClearDatabase extends BaseImporter
{
/**
* Returns a default manifest just so this step gets run
*/
public function getManifest(): array
{
return [
[
'importer' => get_class($this),
'start' => 0,
'end' => 1,
'message' => 'Clearing database',
],
];
}
public function run($start = 0)
{
$this->cleanupDb();
}
/**
* Cleanup the local database of any users and other data that might conflict
* before running the importer
*/
protected function cleanupDb()
{
$this->info('Running database cleanup/empty before starting');
DB::statement('SET FOREIGN_KEY_CHECKS=0');
Bid::truncate();
File::truncate();
News::truncate();
Expense::truncate();
JournalTransaction::truncate();
Journal::truncate();
// Clear flights
DB::table('flight_fare')->truncate();
DB::table('flight_subfleet')->truncate();
FlightField::truncate();
FlightFieldValue::truncate();
Flight::truncate();
Subfleet::truncate();
// Clear permissions
// DB::table('permission_role')->truncate();
// DB::table('permission_user')->truncate();
// DB::table('role_user')->truncate();
// Role::truncate();
Airline::truncate();
Airport::truncate();
Acars::truncate();
Pirep::truncate();
UserAward::truncate();
User::truncate();
DB::statement('SET FOREIGN_KEY_CHECKS=1');
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Modules\Importer\Services\Importers;
use App\Models\User;
use App\Services\UserService;
use Modules\Importer\Services\BaseImporter;
class FinalizeImporter extends BaseImporter
{
/**
* Returns a default manifest just so this step gets run
*/
public function getManifest(): array
{
return [
[
'importer' => get_class($this),
'start' => 0,
'end' => 1,
'message' => 'Finalizing import',
],
];
}
/**
* The start method. Takes the offset to start from
*
* @param int $start
*
* @return mixed
*/
public function run($start = 0)
{
$this->findLastPireps();
$this->recalculateUserStats();
}
/**
* Go through and set the last PIREP ID for the users
*/
protected function findLastPireps()
{
}
/**
* Recalculate all of the user stats
*/
protected function recalculateUserStats()
{
$this->comment('--- RECALCULATING USER STATS ---');
$userSvc = app(UserService::class);
User::all()->each(function ($user) use ($userSvc) {
$userSvc->recalculateStats($user);
});
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Modules\Importer\Services\Importers;
use App\Models\Flight;
use Modules\Importer\Services\BaseImporter;
class FlightImporter extends BaseImporter
{
protected $table = 'schedules';
public function run($start = 0)
{
$this->comment('--- FLIGHT SCHEDULE IMPORT ---');
$fields = [
'id',
'code',
'flightnum',
'depicao',
'arricao',
'route',
'distance',
'flightlevel',
'deptime',
'arrtime',
'flightttime',
'notes',
'enabled',
];
$count = 0;
foreach ($this->db->readRows($this->table, $start, $fields) as $row) {
$airline_id = $this->idMapper->getMapping('airlines', $row->code);
$flight_num = trim($row->flightnum);
$attrs = [
'dpt_airport_id' => $row->depicao,
'arr_airport_id' => $row->arricao,
'route' => $row->route ?: '',
'distance' => round($row->distance ?: 0, 2),
'level' => $row->flightlevel ?: 0,
'dpt_time' => $row->deptime ?: '',
'arr_time' => $row->arrtime ?: '',
'flight_time' => $this->convertDuration($row->flighttime) ?: '',
'notes' => $row->notes ?: '',
'active' => $row->enabled ?: true,
];
try {
$w = ['airline_id' => $airline_id, 'flight_number' => $flight_num];
// $flight = Flight::updateOrCreate($w, $attrs);
$flight = Flight::create(array_merge($w, $attrs));
} catch (\Exception $e) {
//$this->error($e);
}
$this->idMapper->addMapping('flights', $row->id, $flight->id);
// TODO: deserialize route_details into ACARS table
if ($flight->wasRecentlyCreated) {
$count++;
}
}
$this->info('Imported '.$count.' flights');
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace Modules\Importer\Services\Importers;
use App\Models\Role;
use App\Services\RoleService;
use Modules\Importer\Services\BaseImporter;
/**
* Imports the groups into the permissions feature(s)
*/
class GroupImporter extends BaseImporter
{
protected $table = 'groups';
/**
* Permissions in the legacy system, mapping them to the current system
*/
protected $legacy_permission_set = [
'EDIT_NEWS' => 0x1,
'EDIT_PAGES' => 0x2,
'EDIT_DOWNLOADS' => 0x4,
'EMAIL_PILOTS' => 0x8,
'EDIT_AIRLINES' => 0x10, //
'EDIT_FLEET' => 0x20, //
'EDIT_SCHEDULES' => 0x80, //
'IMPORT_SCHEDULES' => 0x100, //
'MODERATE_REGISTRATIONS' => 0x200,
'EDIT_PILOTS' => 0x400, //
'EDIT_GROUPS' => 0x800,
'EDIT_RANKS' => 0x1000, //
'EDIT_AWARDS' => 0x2000, //
'MODERATE_PIREPS' => 0x4000, //
'VIEW_FINANCES' => 0x8000, //
'EDIT_EXPENSES' => 0x10000, //
'EDIT_SETTINGS' => 0x20000, //
'EDIT_PIREPS_FIELDS' => 0x40000, //
'EDIT_PROFILE_FIELDS' => 0x80000, //
'EDIT_VACENTRAL' => 0x100000,
'ACCESS_ADMIN' => 0x2000000,
'FULL_ADMIN' => 35651519, //
];
/**
* Map a legacy value over to one of the current permission values
*/
protected $legacy_to_permission = [
'FULL_ADMIN' => 'admin',
'EDIT_AIRLINES' => 'airlines',
'EDIT_AWARDS' => 'awards',
'EDIT_FLEET' => 'fleet',
'EDIT_EXPENCES' => 'finances',
'VIEW_FINANCES' => 'finances',
'EDIT_SCHEDULES' => 'flights',
'EDIT_PILOTS' => 'users',
'EDIT_PROFILE_FIELDS' => 'users',
'EDIT_SETTINGS' => 'settings',
'MODERATE_PIREPS' => 'pireps',
'EDIT_PIREPS_FIELDS' => 'pireps',
'EDIT_RANKS' => 'ranks',
'MODERATE_REGISTRATIONS' => 'users',
];
public function run($start = 0)
{
$this->comment('--- ROLES/GROUPS IMPORT ---');
$roleSvc = app(RoleService::class);
$count = 0;
foreach ($this->db->readRows($this->table, $start) as $row) {
$name = str_slug($row->name);
$role = Role::firstOrCreate(
['name' => $name],
['display_name' => $row->name]
);
$this->idMapper->addMapping('group', $row->groupid, $role->id);
// See if the permission set mask contains one of the mappings above
// Add all of the ones which apply, and then set them on the new role
$permissions = [];
foreach ($this->legacy_permission_set as $legacy_name => $mask) {
if (($row->permissions & $mask) === true) {
if (!array_key_exists($legacy_name, $this->legacy_to_permission)) {
continue;
}
$permissions[] = $this->legacy_to_permission[$legacy_name];
}
}
$roleSvc->setPermissionsForRole($role, $permissions);
if ($role->wasRecentlyCreated) {
$count++;
}
}
$this->info('Imported '.$count.' ranks');
}
}

View File

@@ -0,0 +1,146 @@
<?php
namespace Modules\Importer\Services\Importers;
use App\Models\Enums\FlightType;
use App\Models\Enums\PirepSource;
use App\Models\Enums\PirepState;
use App\Models\Pirep;
use Modules\Importer\Services\BaseImporter;
class PirepImporter extends BaseImporter
{
protected $table = 'pireps';
public function run($start = 0)
{
$this->comment('--- PIREP IMPORT ---');
$fields = [
'pirepid',
'pilotid',
'code',
'aircraft',
'flightnum',
'depicao',
'arricao',
'fuelused',
'route',
'source',
'accepted',
'submitdate',
'distance',
'flighttime_stamp',
'flighttype',
'flightlevel',
];
$count = 0;
foreach ($this->db->readRows($this->table, $start, $fields) as $row) {
$pirep_id = $row->pirepid;
$user_id = $this->idMapper->getMapping('users', $row->pilotid);
$airline_id = $this->idMapper->getMapping('airlines', $row->code);
$aircraft_id = $this->idMapper->getMapping('aircraft', $row->aircraft);
$attrs = [
'user_id' => $user_id,
'airline_id' => $airline_id,
'aircraft_id' => $aircraft_id,
'flight_number' => $row->flightnum ?: '',
'dpt_airport_id' => $row->depicao,
'arr_airport_id' => $row->arricao,
'block_fuel' => $row->fuelused,
'route' => $row->route ?: '',
'source_name' => $row->source,
'state' => $this->mapState($row->accepted),
'created_at' => $this->parseDate($row->submitdate),
'updated_at' => $this->parseDate($row->submitdate),
];
// Set the distance
$distance = round($row->distance ?: 0, 2);
$attrs['distance'] = $distance;
$attrs['planned_distance'] = $distance;
// Set the flight time properly
$duration = $this->convertDuration($row->flighttime_stamp);
$attrs['flight_time'] = $duration;
$attrs['planned_flight_time'] = $duration;
// Set how it was filed
if (strtoupper($row->source) === 'MANUAL') {
$attrs['source'] = PirepSource::MANUAL;
} else {
$attrs['source'] = PirepSource::ACARS;
}
// Set the flight type
$row->flighttype = strtoupper($row->flighttype);
if ($row->flighttype === 'P') {
$attrs['flight_type'] = FlightType::SCHED_PAX;
} elseif ($row->flighttype === 'C') {
$attrs['flight_type'] = FlightType::SCHED_CARGO;
} else {
$attrs['flight_type'] = FlightType::CHARTER_PAX_ONLY;
}
// Set the flight level of the PIREP is set
if (property_exists($row, 'flightlevel')) {
$attrs['level'] = $row->flightlevel;
} else {
$attrs['level'] = 0;
}
$w = ['id' => $pirep_id];
$pirep = Pirep::updateOrCreate($w, $attrs);
//$pirep = Pirep::create(array_merge($w, $attrs));
//Log::debug('pirep oldid='.$pirep_id.', olduserid='.$row->pilotid
// .'; new id='.$pirep->id.', user id='.$user_id);
$source = strtoupper($row->source);
if ($source === 'SMARTCARS') {
// TODO: Parse smartcars log into the acars table
} elseif ($source === 'KACARS') {
// TODO: Parse kACARS log into acars table
} elseif ($source === 'XACARS') {
// TODO: Parse XACARS log into acars table
}
// TODO: Add extra fields in as PIREP fields
$this->idMapper->addMapping('pireps', $row->pirepid, $pirep->id);
if ($pirep->wasRecentlyCreated) {
$count++;
}
}
$this->info('Imported '.$count.' pireps');
}
/**
* Map the old status to the current
* https://github.com/nabeelio/phpvms_v2/blob/master/core/app.config.php#L450
*
* @param int $old_state
*
* @return int
*/
private function mapState($old_state)
{
$map = [
0 => PirepState::PENDING,
1 => PirepState::ACCEPTED,
2 => PirepState::REJECTED,
3 => PirepState::IN_PROGRESS,
];
$old_state = (int) $old_state;
if (!in_array($old_state, $map)) {
return PirepState::PENDING;
}
return $map[$old_state];
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Modules\Importer\Services\Importers;
use App\Models\Rank;
use Modules\Importer\Services\BaseImporter;
class RankImport extends BaseImporter
{
protected $table = 'ranks';
public function run($start = 0)
{
$this->comment('--- RANK IMPORT ---');
$count = 0;
foreach ($this->db->readRows($this->table, $start) as $row) {
$rank = Rank::firstOrCreate(['name' => $row->rank], [
'image_url' => $row->rankimage,
'hours' => $row->minhours,
]);
$this->idMapper->addMapping('ranks', $row->rankid, $rank->id);
$this->idMapper->addMapping('ranks', $row->rank, $rank->id);
if ($rank->wasRecentlyCreated) {
$count++;
}
}
$this->info('Imported '.$count.' ranks');
}
}

View File

@@ -0,0 +1,141 @@
<?php
namespace Modules\Importer\Services\Importers;
use App\Facades\Utils;
use App\Models\Enums\UserState;
use App\Models\User;
use App\Services\UserService;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Modules\Importer\Services\BaseImporter;
class UserImport extends BaseImporter
{
protected $table = 'pilots';
/**
* @var UserService
*/
private $userSvc;
public function run($start = 0)
{
$this->comment('--- USER IMPORT ---');
$this->userSvc = app(UserService::class);
$count = 0;
$first_row = true;
foreach ($this->db->readRows($this->table, $start) as $row) {
$pilot_id = $row->pilotid; // This isn't their actual ID
$name = $row->firstname.' '.$row->lastname;
// Figure out which airline, etc, they belong to
$airline_id = $this->idMapper->getMapping('airlines', $row->code);
// Log::info('User airline from '.$row->code.' to ID '.$airline_id);
$rank_id = $this->idMapper->getMapping('ranks', $row->rank);
$state = $this->getUserState($row->retired);
if ($first_row) {
$new_password = 'admin';
$first_row = false;
} else {
$new_password = Str::random(60);
}
// Look for a user with that pilot ID already. If someone has it
if ($this->userSvc->isPilotIdAlreadyUsed($pilot_id)) {
Log::info('User with pilot id '.$pilot_id.' exists, reassigning');
$pilot_id = $this->userSvc->getNextAvailablePilotId();
}
$attrs = [
'pilot_id' => $pilot_id,
'name' => $name,
'password' => Hash::make($new_password),
'api_key' => Utils::generateApiKey(),
'airline_id' => $airline_id,
'rank_id' => $rank_id,
'home_airport_id' => $row->hub,
'curr_airport_id' => $row->hub,
'flights' => (int) $row->totalflights,
'flight_time' => Utils::hoursToMinutes($row->totalhours),
'state' => $state,
'created_at' => $this->parseDate($row->joindate),
];
$user = User::updateOrCreate(['email' => $row->email], $attrs);
$this->idMapper->addMapping('users', $row->pilotid, $user->id);
$this->updateUserRoles($user, $row->pilotid);
if ($user->wasRecentlyCreated) {
$count++;
}
}
$this->info('Imported '.$count.' users');
}
/**
* Update a user's roles and add them to the proper ones
*
* @param User $user
* @param string $old_pilot_id
*/
protected function updateUserRoles(User $user, $old_pilot_id)
{
// Be default add them to the user role, and then determine if they
// belong to any other groups, and add them to that
$this->userSvc->addUserToRole($user, 'user');
// Figure out what other groups they belong to
}
/**
* Get the user's new state from their original state
*
* @param $state
*
* @return int
*/
protected function getUserState($state)
{
// Return active for now, let the stats/cron determine the status later
return UserState::ACTIVE;
/*$state = (int) $state;
// Declare array of classic states
$phpvms_classic_states = [
'ACTIVE' => 0,
'INACTIVE' => 1,
'BANNED' => 2,
'ON_LEAVE' => 3,
];
// Decide which state they will be in accordance with v7
if ($state === $phpvms_classic_states['ACTIVE']) {
return UserState::ACTIVE;
}
if ($state === $phpvms_classic_states['INACTIVE']) {
// TODO: Make an inactive state?
return UserState::REJECTED;
}
if ($state === $phpvms_classic_states['BANNED']) {
return UserState::SUSPENDED;
}
if ($state === $phpvms_classic_states['ON_LEAVE']) {
return UserState::ON_LEAVE;
}
$this->error('Unknown status: '.$state);
return UserState::ACTIVE;*/
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Modules\Importer\Utils;
use App\Contracts\Service;
use Spatie\Valuestore\Valuestore;
class IdMapper extends Service
{
private $valueStore;
public function __construct()
{
$this->valueStore = Valuestore::make(storage_path('app/legacy_migration.json'));
}
/**
* Create a new mapping between an old ID and the new one
*
* @param string $entity Name of the entity (e,g table)
* @param string $old_id
* @param string $new_id
*/
public function addMapping($entity, $old_id, $new_id)
{
$key_name = $entity.'_'.$old_id;
if (!$this->valueStore->has($key_name)) {
$this->valueStore->put($key_name, $new_id);
}
}
/**
* Return the ID for a mapping
*
* @param $entity
* @param $old_id
*
* @return bool
*/
public function getMapping($entity, $old_id)
{
$key_name = $entity.'_'.$old_id;
if (!$this->valueStore->has($key_name)) {
return 0;
}
return $this->valueStore->get($key_name);
}
}

View File

@@ -0,0 +1,168 @@
<?php
namespace Modules\Importer\Utils;
use Illuminate\Support\Facades\Log;
use PDO;
use PDOException;
/**
* Real basic to interface with an importer
*/
class ImporterDB
{
/**
* @var int
*/
public $batchSize;
/**
* @var PDO
*/
private $conn;
/**
* @var string
*/
private $dsn;
/**
* @var array
*/
private $creds;
public function __construct($creds)
{
$this->creds = $creds;
$this->dsn = 'mysql:'.implode(';', [
'host='.$this->creds['host'],
'port='.$this->creds['port'],
'dbname='.$this->creds['name'],
]);
Log::info('Using DSN: '.$this->dsn);
$this->batchSize = config('installer.importer.batch_size', 20);
}
public function __destruct()
{
$this->close();
}
public function connect()
{
try {
$this->conn = new PDO($this->dsn, $this->creds['user'], $this->creds['pass']);
$this->conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
} catch (PDOException $e) {
Log::error($e);
throw $e;
}
}
public function close()
{
if ($this->conn) {
$this->conn = null;
}
}
/**
* Return the table name with the prefix
*
* @param $table
*
* @return string
*/
public function tableName($table)
{
if ($this->creds['table_prefix'] !== false) {
return $this->creds['table_prefix'].$table;
}
return $table;
}
/**
* @param $table
*
* @return mixed
*/
public function getTotalRows($table)
{
$this->connect();
$sql = 'SELECT COUNT(*) FROM '.$this->tableName($table);
$rows = $this->conn->query($sql)->fetchColumn();
Log::info('Found '.$rows.' rows in '.$table);
return (int) $rows;
}
/**
* Read all the rows in a table, but read them in a batched manner
*
* @param string $table The name of the table
* @param int [$start_offset]
* @param string [$fields]
*
* @return \Generator
*/
public function readRows($table, $start_offset = 0, $fields = '*')
{
$this->connect();
$offset = $start_offset;
$total_rows = $this->getTotalRows($table);
while ($offset < $total_rows) {
$rows_to_read = $offset + $this->batchSize;
if ($rows_to_read > $total_rows) {
$rows_to_read = $total_rows;
}
// Log::info('Reading '.$offset.' to '.$rows_to_read.' of '.$total_rows);
yield from $this->readRowsOffset($table, $this->batchSize, $offset, $fields);
$offset += $this->batchSize;
}
}
/**
* @param string $table
* @param int $limit Number of rows to read
* @param int $offset Where to start from
* @param string [$fields]
*
* @return \Generator
*/
public function readRowsOffset($table, $limit, $offset, $fields = '*')
{
if (is_array($fields)) {
$fields = implode(',', $fields);
}
$sql = 'SELECT '.$fields.' FROM '.$this->tableName($table).' LIMIT '.$limit.' OFFSET '.$offset;
try {
$result = $this->conn->query($sql);
if (!$result || $result->rowCount() === 0) {
return;
}
foreach ($result as $row) {
yield $row;
}
} catch (PDOException $e) {
// Without incrementing the offset, it should re-run the same query
Log::error('Error readRowsOffset: '.$e->getMessage());
if (strpos($e->getMessage(), 'server has gone away') !== false) {
$this->connect();
}
}
}
}

View File

@@ -0,0 +1,29 @@
{
"name": "phpvms/importer",
"type": "laravel-library",
"description": "The importer module for phpVMS",
"authors": [
{
"name": "Nabeel Shahzad",
"email": ""
}
],
"require": {
"composer/installers": "~1.0"
},
"extra": {
"laravel": {
"providers": [
"Modules\\Importer\\Providers\\ImporterServiceProvider"
],
"aliases": {
}
}
},
"autoload": {
"psr-4": {
"Modules\\Importer\\": ""
}
}
}

View File

@@ -0,0 +1,14 @@
{
"name": "Importer",
"alias": "importer",
"description": "",
"keywords": [],
"active": 1,
"order": 0,
"providers": [
"Modules\\Importer\\Providers\\ImporterServiceProvider"
],
"aliases": {},
"files": [],
"requires": []
}

View File

@@ -7,6 +7,8 @@ use App\Facades\Utils;
use App\Models\User;
use App\Repositories\AirlineRepository;
use App\Services\AnalyticsService;
use App\Services\Installer\DatabaseService;
use App\Services\Installer\InstallerService;
use App\Services\Installer\MigrationService;
use App\Services\Installer\SeederService;
use App\Services\UserService;
@@ -17,13 +19,8 @@ use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Modules\Installer\Services\ConfigService;
use Modules\Installer\Services\DatabaseService;
use Modules\Installer\Services\RequirementsService;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
/**
* Class InstallerController
*/
class InstallerController extends Controller
{
private $airlineRepo;
@@ -65,6 +62,8 @@ class InstallerController extends Controller
$this->reqSvc = $reqSvc;
$this->seederSvc = $seederSvc;
$this->userService = $userService;
\App\Support\Utils::disableDebugToolbar();
}
/**
@@ -106,7 +105,7 @@ class InstallerController extends Controller
$message = 'Failed! '.$e->getMessage();
}
return view('installer::flash/dbtest', [
return view('installer::install/dbtest', [
'status' => $status,
'message' => $message,
]);
@@ -221,7 +220,7 @@ class InstallerController extends Controller
*/
try {
$this->envSvc->createConfigFiles($attrs);
} catch (FileException $e) {
} catch (\Exception $e) {
Log::error('Config files failed to write');
Log::error($e->getMessage());
@@ -351,6 +350,9 @@ class InstallerController extends Controller
*/
public function complete(Request $request)
{
$installerSvc = app(InstallerService::class);
$installerSvc->disableInstallerModules();
return redirect('/login');
}
}

View File

@@ -1,16 +0,0 @@
<?php
Route::get('/', 'InstallerController@index')->name('index');
Route::post('/dbtest', 'InstallerController@dbtest')->name('dbtest');
Route::get('/step1', 'InstallerController@step1')->name('step1');
Route::post('/step1', 'InstallerController@step1')->name('step1');
Route::get('/step2', 'InstallerController@step2')->name('step2');
Route::post('/envsetup', 'InstallerController@envsetup')->name('envsetup');
Route::get('/dbsetup', 'InstallerController@dbsetup')->name('dbsetup');
Route::get('/step3', 'InstallerController@step3')->name('step3');
Route::post('/usersetup', 'InstallerController@usersetup')->name('usersetup');
Route::get('/complete', 'InstallerController@complete')->name('complete');

View File

@@ -1,9 +0,0 @@
<?php
Route::get('/', 'UpdaterController@index')->name('index');
Route::get('/step1', 'UpdaterController@step1')->name('step1');
Route::post('/step1', 'UpdaterController@step1')->name('step1');
Route::post('/run-migrations', 'UpdaterController@run_migrations')->name('run_migrations');
Route::get('/complete', 'UpdaterController@complete')->name('complete');

View File

@@ -2,27 +2,20 @@
namespace Modules\Installer\Providers;
use App\Services\ModuleService;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Route;
class InstallerServiceProvider extends ServiceProvider
{
protected $moduleSvc;
/**
* Boot the application events.
*/
public function boot()
{
$this->moduleSvc = app(ModuleService::class);
$this->registerRoutes();
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->loadMigrationsFrom(__DIR__.'/../Database/migrations');
}
/**
@@ -36,17 +29,21 @@ class InstallerServiceProvider extends ServiceProvider
'middleware' => ['web'],
'namespace' => 'Modules\Installer\Http\Controllers',
], function () {
$this->loadRoutesFrom(__DIR__.'/../Http/Routes/install.php');
});
Route::get('/', 'InstallerController@index')->name('index');
Route::post('/dbtest', 'InstallerController@dbtest')->name('dbtest');
Route::group([
'as' => 'update.',
'prefix' => 'update',
'middleware' => ['web'],
'namespace' => 'Modules\Installer\Http\Controllers',
], function () {
$this->loadRoutesFrom(__DIR__.'/../Http/Routes/update.php');
});
Route::get('/step1', 'InstallerController@step1')->name('step1');
Route::post('/step1', 'InstallerController@step1')->name('step1');
Route::get('/step2', 'InstallerController@step2')->name('step2');
Route::post('/envsetup', 'InstallerController@envsetup')->name('envsetup');
Route::get('/dbsetup', 'InstallerController@dbsetup')->name('dbsetup');
Route::get('/step3', 'InstallerController@step3')->name('step3');
Route::post('/usersetup', 'InstallerController@usersetup')->name('usersetup');
Route::get('/complete', 'InstallerController@complete')->name('complete');
});
}
/**
@@ -54,13 +51,7 @@ class InstallerServiceProvider extends ServiceProvider
*/
protected function registerConfig()
{
$this->publishes([
__DIR__.'/../Config/config.php' => config_path('installer.php'),
], 'installer');
$this->mergeConfigFrom(
__DIR__.'/../Config/config.php', 'installer'
);
$this->mergeConfigFrom(__DIR__.'/../Config/config.php', 'installer');
}
/**

View File

@@ -2,82 +2,90 @@
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title') - installer</title>
<title>@yield('title') - installer</title>
<link rel="shortcut icon" type="image/png" href="{{ public_asset('/assets/img/favicon.png') }}"/>
<link rel="shortcut icon" type="image/png" href="{{ public_asset('/assets/img/favicon.png') }}"/>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no'
name='viewport'/>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no'
name='viewport'/>
<meta name="base-url" content="{!! url('') !!}">
<meta name="api-key" content="{!! Auth::check() ? Auth::user()->api_key: '' !!}">
<meta name="csrf-token" content="{!! csrf_token() !!}">
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700,200" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700,200" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/bootstrap.min.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/now-ui-kit.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/installer/css/vendor.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/styles.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/bootstrap.min.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/now-ui-kit.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/installer/css/vendor.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/styles.css') }}" rel="stylesheet"/>
<link rel="stylesheet"
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
<link rel="stylesheet"
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
<style>
.table tr:first-child td { border-top: 0px; }
@yield('css')
</style>
<style>
.table tr:first-child td {
border-top: 0px;
}
@yield('css')
</style>
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-toggleable-md" style="background: #067ec1;">
<div class="container" style="width: 85%!important;">
<div class="navbar-translate">
<p class="navbar-brand text-white" data-placement="bottom" target="_blank">
<a href="{{ url('/') }}">
<img src="{{ public_asset('/assets/img/logo_blue_bg.svg') }}" width="135px" style=""/>
</a>
</p>
</div>
<div class="justify-content-center" id="navigation" style="margin-left: 50px; color: white; font-size: 20px;">
@yield('title')
</div>
<div class="container" style="width: 85%!important;">
<div class="navbar-translate">
<p class="navbar-brand text-white" data-placement="bottom" target="_blank">
<a href="{{ url('/') }}">
<img src="{{ public_asset('/assets/img/logo_blue_bg.svg') }}" width="135px" style=""/>
</a>
</p>
</div>
<div class="justify-content-center" id="navigation" style="margin-left: 50px; color: white; font-size: 20px;">
@yield('title')
</div>
</div>
</nav>
<!-- End Navbar -->
{{--<div class="clearfix" style="height: 25px;"></div>--}}
<div class="wrapper">
<div class="clear"></div>
<div class="container" style="width: 50%">
<div class="row">
<div class="col-12">
@include('installer::flash.message')
@yield('content')
</div>
</div>
<div class="clear"></div>
<div class="container" style="width: 50%">
<div class="row">
<div class="col-12">
@include('installer::flash.message')
@yield('content')
</div>
</div>
<div class="clearfix" style="height: 200px;"></div>
</div>
<div class="clearfix" style="height: 200px;"></div>
</div>
{{--<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>--}}
<script src="{{ public_mix('/assets/global/js/vendor.js') }}"></script>
<script src="{{ public_mix('/assets/frontend/js/vendor.js') }}"></script>
<script src="{{ public_mix('/assets/frontend/js/app.js') }}"></script>
<script src="{{ public_asset('/assets/installer/js/vendor.js') }}" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script>
hljs.configure({languages: ['sh']});
hljs.configure({languages: ['sh']});
$(document).ready(function () {
$(document).ready(function () {
$(".select2").select2();
$(".select2").select2();
$('pre code').each(function (i, block) {
hljs.fixMarkup(block);
hljs.highlightBlock(block);
});
$('pre code').each(function (i, block) {
hljs.fixMarkup(block);
hljs.highlightBlock(block);
});
});
</script>
@yield('scripts')

View File

@@ -1,11 +1,11 @@
@extends('installer::app')
@section('content')
<h2>phpVMS already installed!</h2>
<p>phpVMS has already been installed! Please remove the modules/Installer folder.</p>
{{ Form::open(['url' => '/', 'method' => 'get']) }}
<p style="text-align: right">
{{ Form::submit('Go to your site >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
<h2>phpVMS already installed!</h2>
<p>phpVMS has already been installed! Please remove the modules/Installer folder.</p>
{{ Form::open(['url' => '/', 'method' => 'get']) }}
<p style="text-align: right">
{{ Form::submit('Go to your site >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
@endsection

View File

@@ -1,6 +1,6 @@
@if($errors->has($field))
<p class="text-danger" style="margin-top: 10px;">{{ $errors->first($field) }}</p>
{{--<div class="alert alert-danger" role="alert" style="margin-top: 10px;">
{{ $errors->first($field) }}
</div>--}}
<p class="text-danger" style="margin-top: 10px;">{{ $errors->first($field) }}</p>
{{--<div class="alert alert-danger" role="alert" style="margin-top: 10px;">
{{ $errors->first($field) }}
</div>--}}
@endif

View File

@@ -1,8 +0,0 @@
<div class="alert alert-{{ $status }}" role="alert">
<div class="container">
<div class="alert-icon">
<i class="now-ui-icons ui-1_bell-53"></i>
</div>
{{ $message }}
</div>
</div>

View File

@@ -1,11 +1,11 @@
@foreach (session('flash_notification', collect())->toArray() as $message)
<div class="alert alert-danger" role="alert">
@foreach (session('flash_notification', []) as $message)
<div class="alert alert-danger" role="alert">
<div class="container">
<div class="alert-icon">
<i class="now-ui-icons ui-2_like"></i>
</div>
{{ $message['message'] }}
<div class="alert-icon">
<i class="now-ui-icons ui-2_like"></i>
</div>
{{ $message['message'] }}
</div>
</div>
</div>
@endforeach
{{ session()->forget('flash_notification') }}

View File

@@ -0,0 +1,8 @@
<div class="alert alert-{{ $status }}" role="alert">
<div class="container">
<div class="alert-icon">
<i class="now-ui-icons ui-1_bell-53"></i>
</div>
{{ $message }}
</div>
</div>

View File

@@ -2,11 +2,11 @@
@section('title', 'Install phpVMS')
@section('content')
<h2>phpvms installer</h2>
<p>Press continue to start</p>
{{ Form::open(['route' => 'installer.step1', 'method' => 'post']) }}
<p style="text-align: right">
{{ Form::submit('Start >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
<h2>phpvms installer</h2>
<p>Press continue to start</p>
{{ Form::open(['route' => 'installer.step1', 'method' => 'post']) }}
<p style="text-align: right">
{{ Form::submit('Start >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
@endsection

View File

@@ -2,62 +2,66 @@
@section('title', 'Requirements Check')
@section('content')
<div style="align-content: center;">
<div style="align-content: center;">
{{ Form::open(['route' => 'installer.step2', 'method' => 'GET']) }}
<table class="table" width="25%">
<tr><td colspan="2"><h4>php version</h4></td></tr>
<tr>
<td>PHP Version: {{ $php['version'] }}</td>
<td style="text-align:center;">
@if($php['passed'] === true)
<span class="badge badge-success">OK!</span>
@else
<span class="badge badge-danger">Failed!</span>
@endif
</td>
</tr>
<tr><td colspan="2"><h4>php extensions</h4></td></tr>
@foreach($extensions as $ext)
<tr>
<td>{{ $ext['ext'] }}</td>
<td style="text-align:center;">
@if($ext['passed'] === true)
<span class="badge badge-success">OK!</span>
@else
<span class="badge badge-danger">Failed!</span>
@endif
</td>
</tr>
@endforeach
<tr>
<td colspan="2"><h4>php version</h4></td>
</tr>
<tr>
<td>PHP Version: {{ $php['version'] }}</td>
<td style="text-align:center;">
@if($php['passed'] === true)
<span class="badge badge-success">OK!</span>
@else
<span class="badge badge-danger">Failed!</span>
@endif
</td>
</tr>
<tr>
<td colspan="2"><h4>php extensions</h4></td>
</tr>
@foreach($extensions as $ext)
<tr>
<td colspan="2">
<h4>directory permissions</h4>
<p>Make sure these directories have read and write permissions</p>
</td>
<td>{{ $ext['ext'] }}</td>
<td style="text-align:center;">
@if($ext['passed'] === true)
<span class="badge badge-success">OK!</span>
@else
<span class="badge badge-danger">Failed!</span>
@endif
</td>
</tr>
@foreach($directories as $dir)
<tr>
<td>{{ $dir['dir'] }}</td>
<td style="text-align:center;">
@if($dir['passed'] === true)
<span class="badge badge-success">OK!</span>
@else
<span class="badge badge-danger">Failed!</span>
@endif
</td>
</tr>
@endforeach
@endforeach
<tr>
<td colspan="2">
<h4>directory permissions</h4>
<p>Make sure these directories have read and write permissions</p>
</td>
</tr>
@foreach($directories as $dir)
<tr>
<td>{{ $dir['dir'] }}</td>
<td style="text-align:center;">
@if($dir['passed'] === true)
<span class="badge badge-success">OK!</span>
@else
<span class="badge badge-danger">Failed!</span>
@endif
</td>
</tr>
@endforeach
</table>
@if($passed === true)
<p style="text-align: right">
{{ Form::submit('Database Setup >>', ['class' => 'btn btn-success']) }}
</p>
<p style="text-align: right">
{{ Form::submit('Database Setup >>', ['class' => 'btn btn-success']) }}
</p>
@endif
{{--{{ $php_version }}
{{ $extensions }}
{{ $passed }}--}}
{{ Form::close() }}
</div>
</div>
@endsection

View File

@@ -1,154 +1,154 @@
@extends('installer::app')
@section('title', 'Database Setup')
@section('content')
<div style="align-content: center;">
<div style="align-content: center;">
{{ Form::open(['route' => 'installer.envsetup', 'method' => 'POST']) }}
<table class="table" width="25%">
<tr>
<td colspan="2"><h4>Site Config</h4></td>
</tr>
<tr>
<td colspan="2"><h4>Site Config</h4></td>
</tr>
<tr>
<td>Site Name</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'site_name', 'phpvms', ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Site Name</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'site_name', 'phpvms', ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Site URL</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'site_url', Request::root(), ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Site URL</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'site_url', Request::root(), ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td colspan="2"><h4>Database Config</h4></td>
</tr>
<tr>
<td colspan="2"><h4>Database Config</h4></td>
</tr>
<tr>
<td><p>Select Database Type</p></td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::select('db_conn', $db_types, null, ['class' => 'form-control', 'id' => 'db_conn']) }}
</div>
</td>
</tr>
<tr>
<td><p>Select Database Type</p></td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::select('db_conn', $db_types, null, ['class' => 'form-control', 'id' => 'db_conn']) }}
</div>
</td>
</tr>
<tbody id="mysql_settings" class="settings_panel">
<tr>
<td>Database Host</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_host', '127.0.0.1', ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tbody id="mysql_settings" class="settings_panel">
<tr>
<td>Database Host</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_host', '127.0.0.1', ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database Port</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_port', '3306', ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database Port</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_port', '3306', ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database Name</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_name', 'phpvms', ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database Name</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_name', 'phpvms', ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database User</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_user', null, ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database User</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_user', null, ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database Password</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_pass', null, ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td>Database Password</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_pass', null, ['class' => 'form-control']) }}
</div>
</td>
</tr>
<tr>
<td colspan="2" style="text-align: right;">
{{ Form::submit('Test Database Credentials', ['class' => 'btn btn-info', 'id' => 'dbtest_button']) }}
</td>
</tr>
</tbody>
<tr>
<td colspan="2" style="text-align: right;">
{{ Form::submit('Test Database Credentials', ['class' => 'btn btn-info', 'id' => 'dbtest_button']) }}
</td>
</tr>
</tbody>
<tbody id="sqlite_settings" class="settings_panel">
<tbody id="sqlite_settings" class="settings_panel">
</tbody>
</tbody>
<tr>
<td>Database Prefix</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_prefix', '', ['class' => 'form-control']) }}
<p>Set this if you're sharing the database with another application.</p>
</div>
</td>
</tr>
<tr>
<td>Database Prefix</td>
<td style="text-align:center;">
<div class="form-group">
{{ Form::input('text', 'db_prefix', '', ['class' => 'form-control']) }}
<p>Set this if you're sharing the database with another application.</p>
</div>
</td>
</tr>
</table>
<div id="dbtest"></div>
<p style="text-align: right">
{{ Form::submit('Setup Database >>', ['class' => 'btn btn-success']) }}
{{ Form::submit('Setup Database >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
</div>
</div>
@endsection
@section('scripts')
<script>
function changeForm(selected) {
$("tbody.settings_panel").hide();
$("tbody#" + selected + "_settings").show();
}
<script>
function changeForm(selected) {
$("tbody.settings_panel").hide();
$("tbody#" + selected + "_settings").show();
}
$(document).ready(() => {
$(document).ready(() => {
const selValue = $("#db_conn option:selected").text();
changeForm(selValue);
const selValue = $("#db_conn option:selected").text();
changeForm(selValue);
$("#db_conn").change((e) => {
$("#db_conn").change((e) => {
const selValue = $("#db_conn option:selected").text();
changeForm(selValue);
});
});
$("#dbtest_button").click((e) => {
$("#dbtest_button").click((e) => {
e.preventDefault();
const opts = {
_token: "{{ csrf_token() }}",
db_conn: $("#db_conn option:selected").text(),
db_host: $("input[name=db_host]").val(),
db_port: $("input[name=db_port]").val(),
db_name: $("input[name=db_name]").val(),
db_user: $("input[name=db_user]").val(),
db_pass: $("input[name=db_pass]").val(),
_token: "{{ csrf_token() }}",
db_conn: $("#db_conn option:selected").text(),
db_host: $("input[name=db_host]").val(),
db_port: $("input[name=db_port]").val(),
db_name: $("input[name=db_name]").val(),
db_user: $("input[name=db_user]").val(),
db_pass: $("input[name=db_pass]").val(),
};
$.post("{{ route('installer.dbtest') }}", opts, (data) => {
$("#dbtest").html(data);
$("#dbtest").html(data);
})
})
});
</script>
})
});
</script>
@endsection

View File

@@ -1,19 +1,19 @@
@extends('installer::app')
@section('title', 'Database Setup Completed')
@section('content')
<div style="align-content: center;">
<div style="align-content: center;">
{{ Form::open(['route' => 'installer.step3', 'method' => 'GET']) }}
<pre class="lang-sh">
<code class="lang-sh">
{{--<code class="language-bash">--}}
{{ $console_output }}
{{ $console_output }}
</code>
</pre>
<p style="text-align: right">
{{ Form::submit('Continue >>', ['class' => 'btn btn-success']) }}
{{ Form::submit('Continue >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
</div>
</div>
@endsection

View File

@@ -2,111 +2,118 @@
@section('title', 'User Setup')
@section('content')
<div class="row"><div class="col-md-12">
<div style="align-content: center;">
{{ Form::open(['route' => 'installer.usersetup', 'method' => 'POST']) }}
<table class="table" width="25%">
<div class="row">
<div class="col-md-12">
<div style="align-content: center;">
{{ Form::open(['route' => 'installer.usersetup', 'method' => 'POST']) }}
<table class="table" width="25%">
<tr>
<tr>
<td colspan="2" style="text-align: right">
<a href="{{ route('importer.index') }}">Importing from a legacy install?</a>
</td>
</tr>
<tr>
<td colspan="2"><h4>Airline Information</h4></td>
</tr>
</tr>
<tr>
<tr>
<td><p>Airline ICAO</p></td>
<td>
<div class="form-group">
{{ Form::input('text', 'airline_icao', null, ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'airline_icao'])
</div>
<div class="form-group">
{{ Form::input('text', 'airline_icao', null, ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'airline_icao'])
</div>
</td>
</tr>
</tr>
<tr>
<tr>
<td><p>Airline Name</p></td>
<td>
<div class="form-group">
{{ Form::input('text', 'airline_name', null, ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'airline_name'])
</div>
<div class="form-group">
{{ Form::input('text', 'airline_name', null, ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'airline_name'])
</div>
</td>
</tr>
</tr>
<tr>
<tr>
<td><p>Airline Country</p></td>
<td>
<div class="form-group">
{{ Form::select('airline_country', $countries, null, ['class' => 'form-control select2' ]) }}
@include('installer::flash/check_error', ['field' => 'airline_country'])
</div>
<div class="form-group">
{{ Form::select('airline_country', $countries, null, ['class' => 'form-control select2' ]) }}
@include('installer::flash/check_error', ['field' => 'airline_country'])
</div>
</td>
</tr>
</tr>
<tr>
<tr>
<td colspan="2"><h4>First User</h4></td>
</tr>
</tr>
<tr>
<tr>
<td><p>Name</p></td>
<td>
<div class="form-group">
{{ Form::input('text', 'name', null, ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'name'])
</div>
<div class="form-group">
{{ Form::input('text', 'name', null, ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'name'])
</div>
</td>
</tr>
</tr>
<tr>
<tr>
<td><p>Email</p></td>
<td>
<div class="form-group">
{{ Form::input('text', 'email', null, ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'email'])
</div>
<div class="form-group">
{{ Form::input('text', 'email', null, ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'email'])
</div>
</td>
</tr>
</tr>
<tr>
<tr>
<td><p>Password</p></td>
<td>
{{ Form::password('password', ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'password'])
{{ Form::password('password', ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'password'])
</td>
</tr>
</tr>
<tr>
<tr>
<td width="40%"><p>Password Confirm</p></td>
<td>
{{ Form::password('password_confirmation', ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'password_confirmation'])
{{ Form::password('password_confirmation', ['class' => 'form-control']) }}
@include('installer::flash/check_error', ['field' => 'password_confirmation'])
</td>
</tr>
</tr>
<tr>
<tr>
<td colspan="2"><h4>Options</h4></td>
</tr>
</tr>
<tr>
<tr>
<td><p>Analytics</p></td>
<td>
<div class="form-group">
{{ Form::hidden('telemetry', 0) }}
{{ Form::checkbox('telemetry', 1, true, ['class' => 'form-control']) }}
<br />
<p>
Allows collection of analytics. They won't identify you, and helps us to track
the PHP and database versions that are used, and help to figure out problems
and slowdowns when vaCentral integration is enabled.
</p>
</div>
<div class="form-group">
{{ Form::hidden('telemetry', 0) }}
{{ Form::checkbox('telemetry', 1, true, ['class' => 'form-control']) }}
<br/>
<p>
Allows collection of analytics. They won't identify you, and helps us to track
the PHP and database versions that are used, and help to figure out problems
and slowdowns when vaCentral integration is enabled.
</p>
</div>
</td>
</tr>
</table>
<div id="dbtest"></div>
<p style="text-align: right">
{{ Form::submit('Complete Setup >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
</div>
</div>
</tr>
</table>
<div id="dbtest"></div>
<p style="text-align: right">
{{ Form::submit('Complete Setup >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
</div>
</div>
</div>
@endsection

View File

@@ -2,19 +2,19 @@
@section('title', 'Installation Completed!')
@section('content')
<div style="align-content: center;">
{{ Form::open(['route' => 'installer.complete', 'method' => 'GET']) }}
<div style="align-content: center;">
{{ Form::open(['route' => 'installer.complete', 'method' => 'GET']) }}
<h4>Installer Completed!</h4>
<h4>Installer Completed!</h4>
<p>Edit the <span class="code">config.php</span> to fill in some additional settings. </p>
<p>Click the button to proceed to the login screen!</p>
<p>Edit the <span class="code">config.php</span> to fill in some additional settings. </p>
<p>Click the button to proceed to the login screen!</p>
<p style="text-align: right">
{{ Form::submit('Install Complete! Continue to Log-In >>',
['class' => 'btn btn-success'])
}}
</p>
{{ Form::close() }}
</div>
<p style="text-align: right">
{{ Form::submit('Install Complete! Continue to Log-In >>',
['class' => 'btn btn-success'])
}}
</p>
{{ Form::close() }}
</div>
@endsection

View File

@@ -2,11 +2,11 @@
@section('title', 'Update phpVMS')
@section('content')
<h2>phpvms updater</h2>
<p>Press continue to check if there are any updates available.</p>
{{ Form::open(['route' => 'update.step1', 'method' => 'post']) }}
<p style="text-align: right">
{{ Form::submit('Start >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
<h2>phpvms updater</h2>
<p>Press continue to check if there are any updates available.</p>
{{ Form::open(['route' => 'update.step1', 'method' => 'post']) }}
<p style="text-align: right">
{{ Form::submit('Start >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
@endsection

View File

@@ -2,12 +2,12 @@
@section('title', 'Update phpVMS')
@section('content')
<h2>phpvms updater</h2>
<p>It seems like you're up to date!</p>
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
<h2>phpvms updater</h2>
<p>It seems like you're up to date!</p>
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
<p style="text-align: right">
{{ Form::submit('Complete >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
<p style="text-align: right">
{{ Form::submit('Complete >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
@endsection

View File

@@ -2,11 +2,11 @@
@section('title', 'Update phpVMS')
@section('content')
<h2>phpvms updater</h2>
<p>Click run to complete the update!.</p>
{{ Form::open(['route' => 'update.run_migrations', 'method' => 'post']) }}
<p style="text-align: right">
{{ Form::submit('Run >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
<h2>phpvms updater</h2>
<p>Click run to complete the update!.</p>
{{ Form::open(['route' => 'update.run_migrations', 'method' => 'post']) }}
<p style="text-align: right">
{{ Form::submit('Run >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
@endsection

View File

@@ -1,18 +1,18 @@
@extends('installer::app')
@section('title', 'Update Completed')
@section('content')
<div style="align-content: center;">
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
<div style="align-content: center;">
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
<pre class="lang-sh">
<pre class="lang-sh">
<code class="lang-sh">
{{ $console_output }}
</code>
</pre>
<p style="text-align: right">
{{ Form::submit('Complete >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
</div>
<p style="text-align: right">
{{ Form::submit('Complete >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
</div>
@endsection

View File

@@ -2,12 +2,12 @@
@section('title', 'Update Completed')
@section('content')
<h2>phpvms updater</h2>
<p>Update completed!.</p>
<h2>phpvms updater</h2>
<p>Update completed!.</p>
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
<p style="text-align: right">
{{ Form::submit('Finish >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
<p style="text-align: right">
{{ Form::submit('Finish >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
@endsection

View File

@@ -13,9 +13,6 @@ use Nwidart\Modules\Support\Stub;
use PDO;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
/**
* Class ConfigService
*/
class ConfigService extends Service
{
/**

View File

@@ -1,68 +0,0 @@
<?php
namespace Modules\Installer\Services;
use App\Contracts\Service;
use Log;
use PDO;
class DatabaseService extends Service
{
/**
* Check the PHP version that it meets the minimum requirement
*
* @param $driver
* @param $host
* @param $port
* @param $name
* @param $user
* @param $pass
*
* @return bool
*/
public function checkDbConnection($driver, $host, $port, $name, $user, $pass)
{
Log::info('Testing Connection: '.$driver.'::'.$user.':<hidden>@'.$host.':'.$port.';'.$name);
if ($driver === 'mysql') {
$dsn = "mysql:host=$host;port=$port;dbname=$name";
Log::info('Connection string: '.$dsn);
try {
$conn = new PDO($dsn, $user, $pass);
} catch (\PDOException $e) {
throw $e;
}
}
// TODO: Needs testing
elseif ($driver === 'postgres') {
$dsn = "pgsql:host=$host;port=$port;dbname=$name";
try {
$conn = new PDO($dsn, $user, $pass);
} catch (\PDOException $e) {
throw $e;
}
}
return true;
}
/**
* Setup the database by running the migration commands
* Only run the setup for sqlite, otherwise, we're assuming
* that the MySQL database has already been created
*/
public function setupDB()
{
$output = '';
if (config('database.default') === 'sqlite') {
\Artisan::call('database:create');
$output .= \Artisan::output();
}
return trim($output);
}
}

View File

@@ -1,11 +1,12 @@
{
"name": "phpvms/installer",
"license": "",
"type": "laravel-library",
"description": "The installer module for phpVMS",
"authors": [
{
"name": "Nabeel Shahzad",
"email": ""
"email": "nabeel@phpvms.net"
}
],
"require": {

View File

@@ -5,10 +5,10 @@
@endsection
@section('content')
<div class="card border-blue-bottom">
<div class="content">
<div class="header"><h4 class="title">Create something!</h4></div>
<p>Add a form!</p>
</div>
<div class="card border-blue-bottom">
<div class="content">
<div class="header"><h4 class="title">Create something!</h4></div>
<p>Add a form!</p>
</div>
</div>
@endsection

View File

@@ -2,17 +2,17 @@
@section('title', 'Sample')
@section('actions')
<li>
<a href="{{ url('/admin/sample/create') }}">
<i class="ti-plus"></i>
Add New</a>
</li>
<li>
<a href="{{ url('/admin/sample/create') }}">
<i class="ti-plus"></i>
Add New</a>
</li>
@endsection
@section('content')
<div class="card border-blue-bottom">
<div class="content">
<div class="header"><h4 class="title">Admin Scaffold!</h4></div>
<p>This view is loaded from module: {{ config('sample.name') }}</p>
</div>
<div class="card border-blue-bottom">
<div class="content">
<div class="header"><h4 class="title">Admin Scaffold!</h4></div>
<p>This view is loaded from module: {{ config('sample.name') }}</p>
</div>
</div>
@endsection

View File

@@ -1,9 +1,9 @@
@extends('sample::layouts.frontend')
@section('content')
<h1>Hello World</h1>
<h1>Hello World</h1>
<p>
This view is loaded from module: {{ config('sample.name') }}
</p>
<p>
This view is loaded from module: {{ config('sample.name') }}
</p>
@endsection

View File

@@ -0,0 +1,4 @@
<?php
return [
];

View File

@@ -1,34 +1,34 @@
<?php
namespace Modules\Installer\Http\Controllers;
namespace Modules\Updater\Http\Controllers;
use App\Contracts\Controller;
use App\Services\Installer\InstallerService;
use App\Services\Installer\MigrationService;
use App\Services\Installer\SeederService;
use function count;
use Illuminate\Http\Request;
use Log;
use Illuminate\Support\Facades\Log;
/**
* Class UpdaterController
*/
class UpdaterController extends Controller
class UpdateController extends Controller
{
private $installerSvc;
private $migrationSvc;
private $seederSvc;
/**
* UpdaterController constructor.
*
* @param InstallerService $installerSvc
* @param MigrationService $migrationSvc
* @param SeederService $seederSvc
*/
public function __construct(
InstallerService $installerSvc,
MigrationService $migrationSvc,
SeederService $seederSvc
) {
$this->migrationSvc = $migrationSvc;
$this->seederSvc = $seederSvc;
$this->installerSvc = $installerSvc;
}
/**
@@ -36,7 +36,7 @@ class UpdaterController extends Controller
*/
public function index()
{
return view('installer::update/index-start');
return view('updater::index-start');
}
/**
@@ -45,16 +45,17 @@ class UpdaterController extends Controller
*
* @param Request $request
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @return mixed
*/
public function step1(Request $request)
{
$migrations = $this->migrationSvc->migrationsAvailable();
if (count($migrations) > 0) {
Log::info('No migrations found');
$this->installerSvc->clearCaches();
if ($this->installerSvc->isUpgradePending()) {
Log::info('Upgrade is pending');
}
return view('installer::update/steps/step1-update-available');
return view('updater::steps/step1-update-available');
}
/**
@@ -62,7 +63,7 @@ class UpdaterController extends Controller
*
* @param Request $request
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @return mixed
*/
public function run_migrations(Request $request)
{
@@ -71,13 +72,13 @@ class UpdaterController extends Controller
$migrations = $this->migrationSvc->migrationsAvailable();
if (count($migrations) === 0) {
$this->seederSvc->syncAllSeeds();
return view('installer::update/steps/step3-update-complete');
return view('updater::steps/step3-update-complete');
}
$output = $this->migrationSvc->runAllMigrations();
$this->seederSvc->syncAllSeeds();
return view('installer::update/steps/step2-migrations-done', [
return view('updater::steps/step2-migrations-done', [
'console_output' => $output,
]);
}
@@ -87,7 +88,7 @@ class UpdaterController extends Controller
*
* @param Request $request
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @return mixed
*/
public function complete(Request $request)
{

View File

@@ -0,0 +1,91 @@
<?php
namespace Modules\Updater\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
class UpdateServiceProvider extends ServiceProvider
{
public function boot()
{
$this->registerRoutes();
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
}
/**
* Register the routes
*/
protected function registerRoutes()
{
Route::group([
'as' => 'update.',
'prefix' => 'update',
'middleware' => ['web', 'auth', 'ability:admin,admin-access'],
'namespace' => 'Modules\Updater\Http\Controllers',
], function () {
Route::get('/', 'UpdateController@index')->name('index');
Route::get('/step1', 'UpdateController@step1')->name('step1');
Route::post('/step1', 'UpdateController@step1')->name('step1');
Route::post('/run-migrations', 'UpdateController@run_migrations')->name('run_migrations');
Route::get('/complete', 'UpdateController@complete')->name('complete');
});
}
/**
* Register config.
*/
protected function registerConfig()
{
$this->mergeConfigFrom(__DIR__.'/../Config/config.php', 'updater');
}
/**
* Register views.
*/
public function registerViews()
{
$viewPath = resource_path('views/modules/updater');
$sourcePath = __DIR__.'/../Resources/views';
$this->publishes([
$sourcePath => $viewPath,
], 'views');
$paths = array_map(
function ($path) {
return $path.'/modules/updater';
},
\Config::get('view.paths')
);
$paths[] = $sourcePath;
$this->loadViewsFrom($paths, 'updater');
}
/**
* Register translations.
*/
public function registerTranslations()
{
$langPath = resource_path('lang/modules/updater');
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, 'updater');
} else {
$this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'updater');
}
}
/**
* Get the services provided by the provider.
*/
public function provides(): array
{
return [];
}
}

View File

@@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title') - updater</title>
<link rel="shortcut icon" type="image/png" href="{{ public_asset('/assets/img/favicon.png') }}"/>
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no'
name='viewport'/>
<meta name="base-url" content="{!! url('') !!}">
<meta name="api-key" content="{!! Auth::check() ? Auth::user()->api_key: '' !!}">
<meta name="csrf-token" content="{!! csrf_token() !!}">
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700,200" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/latest/css/font-awesome.min.css" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/bootstrap.min.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/now-ui-kit.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/installer/css/vendor.css') }}" rel="stylesheet"/>
<link href="{{ public_asset('/assets/frontend/css/styles.css') }}" rel="stylesheet"/>
<link rel="stylesheet"
href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css">
<style>
.table tr:first-child td {
border-top: 0px;
}
@yield('css')
</style>
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-toggleable-md" style="background: #067ec1;">
<div class="container" style="width: 85%!important;">
<div class="navbar-translate">
<p class="navbar-brand text-white" data-placement="bottom" target="_blank">
<a href="{{ url('/') }}">
<img src="{{ public_asset('/assets/img/logo_blue_bg.svg') }}" width="135px" style=""/>
</a>
</p>
</div>
<div class="justify-content-center" id="navigation" style="margin-left: 50px; color: white; font-size: 20px;">
@yield('title')
</div>
</div>
</nav>
<!-- End Navbar -->
{{--<div class="clearfix" style="height: 25px;"></div>--}}
<div class="wrapper">
<div class="clear"></div>
<div class="container" style="width: 50%">
<div class="row">
<div class="col-12">
@include('installer::flash.message')
@yield('content')
</div>
</div>
</div>
<div class="clearfix" style="height: 200px;"></div>
</div>
{{--<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>--}}
<script src="{{ public_mix('/assets/global/js/vendor.js') }}"></script>
<script src="{{ public_mix('/assets/frontend/js/vendor.js') }}"></script>
<script src="{{ public_mix('/assets/frontend/js/app.js') }}"></script>
<script src="{{ public_asset('/assets/installer/js/vendor.js') }}" type="text/javascript"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
<script>
hljs.configure({languages: ['sh']});
$(document).ready(function () {
$(".select2").select2();
$('pre code').each(function (i, block) {
hljs.fixMarkup(block);
hljs.highlightBlock(block);
});
});
</script>
@yield('scripts')
</body>
</html>

View File

@@ -0,0 +1,6 @@
@if($errors->has($field))
<p class="text-danger" style="margin-top: 10px;">{{ $errors->first($field) }}</p>
{{--<div class="alert alert-danger" role="alert" style="margin-top: 10px;">
{{ $errors->first($field) }}
</div>--}}
@endif

View File

@@ -0,0 +1,11 @@
@foreach (session('flash_notification', collect())->toArray() as $message)
<div class="alert alert-danger" role="alert">
<div class="container">
<div class="alert-icon">
<i class="now-ui-icons ui-2_like"></i>
</div>
{{ $message['message'] }}
</div>
</div>
@endforeach
{{ session()->forget('flash_notification') }}

View File

@@ -0,0 +1,12 @@
@extends('installer::app')
@section('title', 'Update phpVMS')
@section('content')
<h2>phpvms updater</h2>
<p>Press continue to check if there are any updates available.</p>
{{ Form::open(['route' => 'update.step1', 'method' => 'post']) }}
<p style="text-align: right">
{{ Form::submit('Start >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
@endsection

View File

@@ -0,0 +1,13 @@
@extends('installer::app')
@section('title', 'Update phpVMS')
@section('content')
<h2>phpvms updater</h2>
<p>It seems like you're up to date!</p>
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
<p style="text-align: right">
{{ Form::submit('Complete >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
@endsection

View File

@@ -0,0 +1,12 @@
@extends('installer::app')
@section('title', 'Update phpVMS')
@section('content')
<h2>phpvms updater</h2>
<p>Click run to complete the update!.</p>
{{ Form::open(['route' => 'update.run_migrations', 'method' => 'post']) }}
<p style="text-align: right">
{{ Form::submit('Run >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
@endsection

View File

@@ -0,0 +1,18 @@
@extends('installer::app')
@section('title', 'Update Completed')
@section('content')
<div style="align-content: center;">
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
<pre class="lang-sh">
<code class="lang-sh">
{{ $console_output }}
</code>
</pre>
<p style="text-align: right">
{{ Form::submit('Complete >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
</div>
@endsection

View File

@@ -0,0 +1,13 @@
@extends('installer::app')
@section('title', 'Update Completed')
@section('content')
<h2>phpvms updater</h2>
<p>Update completed!.</p>
{{ Form::open(['route' => 'update.complete', 'method' => 'GET']) }}
<p style="text-align: right">
{{ Form::submit('Finish >>', ['class' => 'btn btn-success']) }}
</p>
{{ Form::close() }}
@endsection

View File

@@ -0,0 +1,30 @@
{
"name": "phpvms/updater",
"license": "",
"type": "laravel-library",
"description": "The installer module for phpVMS",
"authors": [
{
"name": "Nabeel Shahzad",
"email": "nabeel@phpvms.net"
}
],
"require": {
"composer/installers": "~1.0"
},
"extra": {
"laravel": {
"providers": [
"Modules\\Updater\\Providers\\UpdateServiceProvider"
],
"aliases": {
}
}
},
"autoload": {
"psr-4": {
"Modules\\Installer\\": ""
}
}
}

View File

@@ -0,0 +1,14 @@
{
"name": "Updater",
"alias": "updater",
"description": "",
"keywords": [],
"active": 1,
"order": 0,
"providers": [
"Modules\\Updater\\Providers\\UpdateServiceProvider"
],
"aliases": {},
"files": [],
"requires": []
}