Merge pull request #4623 from beganovich/v5-setup-add-db-port

(v5) Add support for database port on setup page
This commit is contained in:
David Bomba 2021-01-05 09:15:53 +11:00 committed by GitHub
commit 98975f7319
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 139 additions and 169 deletions

View File

@ -12,6 +12,7 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use \Illuminate\Support\Facades\DB;
use App\Http\Requests\Setup\CheckDatabaseRequest; use App\Http\Requests\Setup\CheckDatabaseRequest;
use App\Http\Requests\Setup\CheckMailRequest; use App\Http\Requests\Setup\CheckMailRequest;
use App\Http\Requests\Setup\StoreSetupRequest; use App\Http\Requests\Setup\StoreSetupRequest;
@ -22,7 +23,6 @@ use App\Utils\CurlUtils;
use App\Utils\SystemHealth; use App\Utils\SystemHealth;
use App\Utils\Traits\AppSetup; use App\Utils\Traits\AppSetup;
use Beganovich\Snappdf\Snappdf; use Beganovich\Snappdf\Snappdf;
use DB;
use Exception; use Exception;
use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory; use Illuminate\Contracts\Routing\ResponseFactory;
@ -55,7 +55,7 @@ class SetupController extends Controller
{ {
try { try {
$check = SystemHealth::check(false); $check = SystemHealth::check(false);
} catch (\Exception $e) { } catch (Exception $e) {
nlog(['message' => $e->getMessage(), 'action' => 'SetupController::doSetup()']); nlog(['message' => $e->getMessage(), 'action' => 'SetupController::doSetup()']);
return response()->json(['message' => $e->getMessage()], 400); return response()->json(['message' => $e->getMessage()], 400);
@ -71,9 +71,9 @@ class SetupController extends Controller
$db = SystemHealth::dbCheck($request); $db = SystemHealth::dbCheck($request);
if ($db['success'] == false) { if ($db['success'] == false) {
throw new \Exception($db['message']); throw new Exception($db['message']);
} }
} catch (\Exception $e) { } catch (Exception $e) {
return response([ return response([
'message' => 'Oops, connection to database was not successful.', 'message' => 'Oops, connection to database was not successful.',
'error' => $e->getMessage(), 'error' => $e->getMessage(),
@ -85,10 +85,10 @@ class SetupController extends Controller
$smtp = SystemHealth::testMailServer($request); $smtp = SystemHealth::testMailServer($request);
if ($smtp['success'] == false) { if ($smtp['success'] == false) {
throw new \Exception($smtp['message']); throw new Exception($smtp['message']);
} }
} }
} catch (\Exception $e) { } catch (Exception $e) {
return response([ return response([
'message' => 'Oops, connection to mail server was not successful.', 'message' => 'Oops, connection to mail server was not successful.',
'error' => $e->getMessage(), 'error' => $e->getMessage(),
@ -100,9 +100,10 @@ class SetupController extends Controller
$env_values = [ $env_values = [
'APP_URL' => $request->input('url'), 'APP_URL' => $request->input('url'),
'REQUIRE_HTTPS' => $request->input('https') ? 'true' : 'false', 'REQUIRE_HTTPS' => $request->input('https') ? 'true' : 'false',
'APP_DEBUG' => $request->input('debug') ? 'true' : 'false', 'APP_DEBUG' => 'false',
'DB_HOST1' => $request->input('db_host'), 'DB_HOST1' => $request->input('db_host'),
'DB_PORT1' => $request->input('db_port'),
'DB_DATABASE1' => $request->input('db_database'), 'DB_DATABASE1' => $request->input('db_database'),
'DB_USERNAME1' => $request->input('db_username'), 'DB_USERNAME1' => $request->input('db_username'),
'DB_PASSWORD1' => $request->input('db_password'), 'DB_PASSWORD1' => $request->input('db_password'),
@ -173,7 +174,7 @@ class SetupController extends Controller
} }
return response($status, 400); return response($status, 400);
} catch (\Exception $e) { } catch (Exception $e) {
nlog(['message' => $e->getMessage(), 'action' => 'SetupController::checkDB()']); nlog(['message' => $e->getMessage(), 'action' => 'SetupController::checkDB()']);
return response()->json(['message' => $e->getMessage()], 400); return response()->json(['message' => $e->getMessage()], 400);
@ -203,17 +204,6 @@ class SetupController extends Controller
} }
} }
private function failsafeMailCheck($request)
{
$response = SystemHealth::testMailServer($request);
if ($response['success']) {
true;
}
return false;
}
public function checkPdf(Request $request) public function checkPdf(Request $request)
{ {
try { try {
@ -231,9 +221,10 @@ class SetupController extends Controller
->setHtml('GENERATING PDFs WORKS! Thank you for using Invoice Ninja!') ->setHtml('GENERATING PDFs WORKS! Thank you for using Invoice Ninja!')
->generate(); ->generate();
Storage::put('public/test.pdf', $pdf); Storage::disk(config('filesystems.default'))->put('test.pdf', $pdf);
Storage::disk('local')->put('test.pdf', $pdf);
return response(['url' => asset('test.pdf')], 200); return response(['url' => Storage::disk('local')->url('test.pdf')], 200);
} catch (Exception $e) { } catch (Exception $e) {
nlog($e->getMessage()); nlog($e->getMessage());

View File

@ -18,8 +18,8 @@ use App\Utils\Traits\MakesHash;
class ActionInvoiceRequest extends Request class ActionInvoiceRequest extends Request
{ {
use MakesHash; use MakesHash;
use ActionsInvoice; use ActionsInvoice;
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
@ -36,31 +36,28 @@ class ActionInvoiceRequest extends Request
public function rules() public function rules()
{ {
return [ return [
'action' => 'required' 'action' => 'required'
]; ];
} }
protected function prepareForValidation() protected function prepareForValidation()
{ {
$input = $this->all(); $input = $this->all();
$this->invoice = Invoice::find($this->decodePrimary($invoice_id)); $this->invoice = Invoice::find($this->decodePrimary($invoice_id));
if(!array_key_exists('action', $input)) { if (!array_key_exists('action', $input)) {
$this->error_msg = 'Action is a required field'; $this->error_msg = 'Action is a required field';
} } elseif (!$this->invoiceDeletable($this->invoice)) {
elseif(!$this->invoiceDeletable($this->invoice)){ unset($input['action']);
unset($input['action']); $this->error_msg = 'This invoice cannot be deleted';
$this->error_msg = 'This invoice cannot be deleted'; } elseif (!$this->invoiceCancellable($this->invoice)) {
} unset($input['action']);
elseif(!$this->invoiceCancellable($this->invoice)) { $this->error_msg = 'This invoice cannot be cancelled';
unset($input['action']); } elseif (!$this->invoiceReversable($this->invoice)) {
$this->error_msg = 'This invoice cannot be cancelled'; unset($input['action']);
} $this->error_msg = 'This invoice cannot be reversed';
else if(!$this->invoiceReversable($this->invoice)) {
unset($input['action']);
$this->error_msg = 'This invoice cannot be reversed';
} }
$this->replace($input); $this->replace($input);
@ -68,13 +65,8 @@ class ActionInvoiceRequest extends Request
public function messages() public function messages()
{ {
return [ return [
'action' => $this->error_msg, 'action' => $this->error_msg,
]; ];
} }
} }

View File

@ -35,6 +35,7 @@ class CheckDatabaseRequest extends Request
{ {
return [ return [
'db_host' => ['required'], 'db_host' => ['required'],
'db_port' => ['required'],
'db_database' => ['required'], 'db_database' => ['required'],
'db_username' => ['required'], 'db_username' => ['required'],
]; ];

View File

@ -16,7 +16,6 @@ use App\Libraries\MultiDB;
use App\Models\RecurringInvoice; use App\Models\RecurringInvoice;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
class RecurringInvoicesCron class RecurringInvoicesCron
{ {

View File

@ -464,19 +464,16 @@ class Import implements ShouldQueue
$client->fresh(); $client->fresh();
$new_contacts = $client->contacts; $new_contacts = $client->contacts;
foreach($resource['contacts'] as $key => $old_contact) foreach ($resource['contacts'] as $key => $old_contact) {
{
$contact_match = $new_contacts->where('contact_key', $old_contact['contact_key'])->first(); $contact_match = $new_contacts->where('contact_key', $old_contact['contact_key'])->first();
if($contact_match) if ($contact_match) {
{
$this->ids['client_contacts']['client_contacts_'.$old_contact['id']] = [ $this->ids['client_contacts']['client_contacts_'.$old_contact['id']] = [
'old' => $old_contact['id'], 'old' => $old_contact['id'],
'new' => $contact_match->id, 'new' => $contact_match->id,
]; ];
} }
} }
} }
$key = "clients_{$resource['id']}"; $key = "clients_{$resource['id']}";
@ -629,16 +626,12 @@ class Import implements ShouldQueue
unset($modified['id']); unset($modified['id']);
if(array_key_exists('invitations', $resource)) if (array_key_exists('invitations', $resource)) {
{ foreach ($resource['invitations'] as $key => $invite) {
foreach($resource['invitations'] as $key => $invite)
{
$resource['invitations'][$key]['client_contact_id'] = $this->transformId('client_contacts', $invite['client_contact_id']); $resource['invitations'][$key]['client_contact_id'] = $this->transformId('client_contacts', $invite['client_contact_id']);
$resource['invitations'][$key]['user_id'] = $modified['user_id']; $resource['invitations'][$key]['user_id'] = $modified['user_id'];
$resource['invitations'][$key]['company_id'] = $this->company->id; $resource['invitations'][$key]['company_id'] = $this->company->id;
unset($resource['invitations'][$key]['recurring_invoice_id']); unset($resource['invitations'][$key]['recurring_invoice_id']);
} }
$modified['invitations'] = $resource['invitations']; $modified['invitations'] = $resource['invitations'];
@ -694,19 +687,15 @@ class Import implements ShouldQueue
unset($modified['id']); unset($modified['id']);
if(array_key_exists('invitations', $resource)) if (array_key_exists('invitations', $resource)) {
{ foreach ($resource['invitations'] as $key => $invite) {
foreach($resource['invitations'] as $key => $invite)
{
$resource['invitations'][$key]['client_contact_id'] = $this->transformId('client_contacts', $invite['client_contact_id']); $resource['invitations'][$key]['client_contact_id'] = $this->transformId('client_contacts', $invite['client_contact_id']);
$resource['invitations'][$key]['user_id'] = $modified['user_id']; $resource['invitations'][$key]['user_id'] = $modified['user_id'];
$resource['invitations'][$key]['company_id'] = $this->company->id; $resource['invitations'][$key]['company_id'] = $this->company->id;
unset($resource['invitations'][$key]['invoice_id']); unset($resource['invitations'][$key]['invoice_id']);
} }
$modified['invitations'] = $resource['invitations']; $modified['invitations'] = $resource['invitations'];
} }
$invoice = $invoice_repository->save( $invoice = $invoice_repository->save(
$modified, $modified,
@ -877,7 +866,7 @@ class Import implements ShouldQueue
PaymentFactory::create($this->company->id, $modified['user_id']) PaymentFactory::create($this->company->id, $modified['user_id'])
); );
if($resource['company_gateway_id'] != 'NULL' && $resource['company_gateway_id'] != NULL){ if ($resource['company_gateway_id'] != 'NULL' && $resource['company_gateway_id'] != null) {
$payment->company_gateway_id = $this->transformId('company_gateways', $resource['company_gateway_id']); $payment->company_gateway_id = $this->transformId('company_gateways', $resource['company_gateway_id']);
$payment->save(); $payment->save();
} }

View File

@ -23,8 +23,9 @@ class ClientContactPresenter extends EntityPresenter
{ {
$contact_name = $this->entity->first_name.' '.$this->entity->last_name; $contact_name = $this->entity->first_name.' '.$this->entity->last_name;
if(strlen($contact_name) > 1) if (strlen($contact_name) > 1) {
return $contact_name; return $contact_name;
}
return $this->entity->client->present()->name(); return $this->entity->client->present()->name();
} }

View File

@ -376,8 +376,9 @@ class RecurringInvoice extends BaseModel
$data = []; $data = [];
if(!Carbon::parse($this->next_send_date)) if (!Carbon::parse($this->next_send_date)) {
return $data; return $data;
}
$next_send_date = Carbon::parse($this->next_send_date)->copy(); $next_send_date = Carbon::parse($this->next_send_date)->copy();

View File

@ -123,8 +123,8 @@ use App\Listeners\Invoice\InvoiceArchivedActivity;
use App\Listeners\Invoice\InvoiceCancelledActivity; use App\Listeners\Invoice\InvoiceCancelledActivity;
use App\Listeners\Invoice\InvoiceDeletedActivity; use App\Listeners\Invoice\InvoiceDeletedActivity;
use App\Listeners\Invoice\InvoiceEmailActivity; use App\Listeners\Invoice\InvoiceEmailActivity;
use App\Listeners\Invoice\InvoiceEmailFailedActivity;
use App\Listeners\Invoice\InvoiceEmailedNotification; use App\Listeners\Invoice\InvoiceEmailedNotification;
use App\Listeners\Invoice\InvoiceEmailFailedActivity;
use App\Listeners\Invoice\InvoicePaidActivity; use App\Listeners\Invoice\InvoicePaidActivity;
use App\Listeners\Invoice\InvoiceReminderEmailActivity; use App\Listeners\Invoice\InvoiceReminderEmailActivity;
use App\Listeners\Invoice\InvoiceRestoredActivity; use App\Listeners\Invoice\InvoiceRestoredActivity;
@ -132,8 +132,8 @@ use App\Listeners\Invoice\InvoiceReversedActivity;
use App\Listeners\Invoice\InvoiceViewedActivity; use App\Listeners\Invoice\InvoiceViewedActivity;
use App\Listeners\Invoice\UpdateInvoiceActivity; use App\Listeners\Invoice\UpdateInvoiceActivity;
use App\Listeners\Misc\InvitationViewedListener; use App\Listeners\Misc\InvitationViewedListener;
use App\Listeners\Payment\PaymentEmailFailureActivity;
use App\Listeners\Payment\PaymentEmailedActivity; use App\Listeners\Payment\PaymentEmailedActivity;
use App\Listeners\Payment\PaymentEmailFailureActivity;
use App\Listeners\Payment\PaymentNotification; use App\Listeners\Payment\PaymentNotification;
use App\Listeners\Payment\PaymentRestoredActivity; use App\Listeners\Payment\PaymentRestoredActivity;
use App\Listeners\Quote\QuoteApprovedActivity; use App\Listeners\Quote\QuoteApprovedActivity;

View File

@ -11,7 +11,6 @@
namespace App\Providers; namespace App\Providers;
use App\Models\RecurringInvoice;
use App\Utils\Traits\MakesHash; use App\Utils\Traits\MakesHash;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
@ -37,7 +36,6 @@ class RouteServiceProvider extends ServiceProvider
{ {
// //
parent::boot(); parent::boot();
} }
/** /**

View File

@ -92,11 +92,11 @@ class InvoiceMigrationRepository extends BaseRepository
InvoiceInvitation::unguard(); InvoiceInvitation::unguard();
RecurringInvoiceInvitation::unguard(); RecurringInvoiceInvitation::unguard();
if($model instanceof RecurringInvoice) if ($model instanceof RecurringInvoice) {
$lcfirst_resource_id = 'recurring_invoice_id'; $lcfirst_resource_id = 'recurring_invoice_id';
}
foreach($data['invitations'] as $invitation) foreach ($data['invitations'] as $invitation) {
{
nlog($invitation); nlog($invitation);
$new_invitation = $invitation_factory_class::create($model->company_id, $model->user_id); $new_invitation = $invitation_factory_class::create($model->company_id, $model->user_id);
@ -107,35 +107,35 @@ class InvoiceMigrationRepository extends BaseRepository
InvoiceInvitation::reguard(); InvoiceInvitation::reguard();
RecurringInvoiceInvitation::reguard(); RecurringInvoiceInvitation::reguard();
/* /*
if (isset($data['invitations'])) { if (isset($data['invitations'])) {
$invitations = collect($data['invitations']); $invitations = collect($data['invitations']);
$model->invitations->pluck('key')->diff($invitations->pluck('key'))->each(function ($invitation) use ($resource) { $model->invitations->pluck('key')->diff($invitations->pluck('key'))->each(function ($invitation) use ($resource) {
$this->getInvitation($invitation, $resource)->delete(); $this->getInvitation($invitation, $resource)->delete();
}); });
foreach ($data['invitations'] as $invitation) { foreach ($data['invitations'] as $invitation) {
//if no invitations are present - create one. //if no invitations are present - create one.
if (! $this->getInvitation($invitation, $resource)) { if (! $this->getInvitation($invitation, $resource)) {
if (isset($invitation['id'])) { if (isset($invitation['id'])) {
unset($invitation['id']); unset($invitation['id']);
} }
//make sure we are creating an invite for a contact who belongs to the client only! //make sure we are creating an invite for a contact who belongs to the client only!
$contact = ClientContact::find($invitation['client_contact_id']); $contact = ClientContact::find($invitation['client_contact_id']);
if ($contact && $model->client_id == $contact->client_id) { if ($contact && $model->client_id == $contact->client_id) {
$new_invitation = $invitation_factory_class::create($model->company_id, $model->user_id); $new_invitation = $invitation_factory_class::create($model->company_id, $model->user_id);
$new_invitation->{$lcfirst_resource_id} = $model->id; $new_invitation->{$lcfirst_resource_id} = $model->id;
$new_invitation->client_contact_id = $contact->id; $new_invitation->client_contact_id = $contact->id;
$new_invitation->save(); $new_invitation->save();
}
}
} }
} }
} */
}
*/
$model->load('invitations'); $model->load('invitations');
/* If no invitations have been created, this is our fail safe to maintain state*/ /* If no invitations have been created, this is our fail safe to maintain state*/

View File

@ -63,8 +63,7 @@ class UserRepository extends BaseRepository
$user->fill($details); $user->fill($details);
//allow users to change only their passwords - not others! //allow users to change only their passwords - not others!
if(auth()->user()->id == $user->id && array_key_exists('password', $data) && isset($data['password'])) if (auth()->user()->id == $user->id && array_key_exists('password', $data) && isset($data['password'])) {
{
$user->password = Hash::make($data['password']); $user->password = Hash::make($data['password']);
} }

View File

@ -211,10 +211,10 @@ class CompanyTransformer extends EntityTransformer
{ {
$transformer = new UserTransformer($this->serializer); $transformer = new UserTransformer($this->serializer);
$users = $company->users->map(function ($user) use ($company){ $users = $company->users->map(function ($user) use ($company) {
$user->company_id = $company->id; $user->company_id = $company->id;
return $user; return $user;
}); });
return $this->includeCollection($users, $transformer, User::class); return $this->includeCollection($users, $transformer, User::class);
} }

View File

@ -45,7 +45,7 @@ class SystemHealth
* @param bool $check_database * @param bool $check_database
* @return array Result set of checks * @return array Result set of checks
*/ */
public static function check($check_database = true) : array public static function check($check_database = true): array
{ {
$system_health = true; $system_health = true;
@ -57,7 +57,7 @@ class SystemHealth
$system_health = false; $system_health = false;
} }
if (! self::simpleDbCheck() && $check_database) { if (!self::simpleDbCheck() && $check_database) {
info('db fails'); info('db fails');
$system_health = false; $system_health = false;
} }
@ -66,18 +66,18 @@ class SystemHealth
'system_health' => $system_health, 'system_health' => $system_health,
'extensions' => self::extensions(), 'extensions' => self::extensions(),
'php_version' => [ 'php_version' => [
'minimum_php_version' => (string) self::$php_version, 'minimum_php_version' => (string)self::$php_version,
'current_php_version' => phpversion(), 'current_php_version' => phpversion(),
'current_php_cli_version' => (string) self::checkPhpCli(), 'current_php_cli_version' => (string)self::checkPhpCli(),
'is_okay' => version_compare(phpversion(), self::$php_version, '>='), 'is_okay' => version_compare(phpversion(), self::$php_version, '>='),
], ],
'env_writable' => self::checkEnvWritable(), 'env_writable' => self::checkEnvWritable(),
//'mail' => self::testMailServer(), //'mail' => self::testMailServer(),
'simple_db_check' => (bool) self::simpleDbCheck(), 'simple_db_check' => (bool)self::simpleDbCheck(),
'cache_enabled' => self::checkConfigCache(), 'cache_enabled' => self::checkConfigCache(),
'phantom_enabled' => (bool) config('ninja.phantomjs_pdf_generation'), 'phantom_enabled' => (bool)config('ninja.phantomjs_pdf_generation'),
'exec' => (bool) self::checkExecWorks(), 'exec' => (bool)self::checkExecWorks(),
'open_basedir' => (bool) self::checkOpenBaseDir(), 'open_basedir' => (bool)self::checkOpenBaseDir(),
]; ];
} }
@ -108,7 +108,7 @@ class SystemHealth
return true; return true;
} }
private static function simpleDbCheck() :bool private static function simpleDbCheck(): bool
{ {
$result = true; $result = true;
@ -135,7 +135,7 @@ class SystemHealth
} }
} }
private static function extensions() :array private static function extensions(): array
{ {
$loaded_extensions = []; $loaded_extensions = [];
@ -151,22 +151,23 @@ class SystemHealth
$result = ['success' => false]; $result = ['success' => false];
if ($request) { if ($request) {
config(['database.connections.db-ninja-01.host'=> $request->input('db_host')]); config(['database.connections.db-ninja-01.host' => $request->input('db_host')]);
config(['database.connections.db-ninja-01.database'=> $request->input('db_database')]); config(['database.connections.db-ninja-01.port' => $request->input('db_port')]);
config(['database.connections.db-ninja-01.username'=> $request->input('db_username')]); config(['database.connections.db-ninja-01.database' => $request->input('db_database')]);
config(['database.connections.db-ninja-01.password'=> $request->input('db_password')]); config(['database.connections.db-ninja-01.username' => $request->input('db_username')]);
config(['database.connections.db-ninja-01.password' => $request->input('db_password')]);
config(['database.default' => 'db-ninja-01']); config(['database.default' => 'db-ninja-01']);
DB::purge('db-ninja-01'); DB::purge('db-ninja-01');
} }
if (! config('ninja.db.multi_db_enabled')) { if (!config('ninja.db.multi_db_enabled')) {
try { try {
$pdo = DB::connection()->getPdo(); $pdo = DB::connection()->getPdo();
$result[] = [DB::connection()->getDatabaseName() => true]; $result[] = [DB::connection()->getDatabaseName() => true];
$result['success'] = true; $result['success'] = true;
} catch (Exception $e) { } catch (Exception $e) {
$result[] = [config('database.connections.'.config('database.default').'.database') => false]; $result[] = [config('database.connections.' . config('database.default') . '.database') => false];
$result['success'] = false; $result['success'] = false;
$result['message'] = $e->getMessage(); $result['message'] = $e->getMessage();
} }
@ -179,7 +180,7 @@ class SystemHealth
$result[] = [DB::connection()->getDatabaseName() => true]; $result[] = [DB::connection()->getDatabaseName() => true];
$result['success'] = true; $result['success'] = true;
} catch (Exception $e) { } catch (Exception $e) {
$result[] = [config('database.connections.'.config('database.default').'.database') => false]; $result[] = [config('database.connections.' . config('database.default') . '.database') => false];
$result['success'] = false; $result['success'] = false;
$result['message'] = $e->getMessage(); $result['message'] = $e->getMessage();
} }
@ -222,6 +223,6 @@ class SystemHealth
private static function checkEnvWritable() private static function checkEnvWritable()
{ {
return is_writable(base_path().'/.env'); return is_writable(base_path() . '/.env');
} }
} }

View File

@ -629,7 +629,6 @@ trait GeneratesCounter
} }
if ($entity->client || ($entity instanceof Client)) { if ($entity->client || ($entity instanceof Client)) {
$client = $entity->client ?: $entity; $client = $entity->client ?: $entity;
$search[] = '{$client_custom1}'; $search[] = '{$client_custom1}';

View File

@ -3,8 +3,6 @@
use App\Models\Currency; use App\Models\Currency;
use App\Utils\Traits\AppSetup; use App\Utils\Traits\AppSetup;
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class UpdateCanadianDollarSymbol extends Migration class UpdateCanadianDollarSymbol extends Migration
{ {
@ -18,8 +16,9 @@ class UpdateCanadianDollarSymbol extends Migration
{ {
$currency = Currency::find(9); $currency = Currency::find(9);
if($currency) if ($currency) {
$currency->update(['symbol' => '$']); $currency->update(['symbol' => '$']);
}
$this->buildCache(true); $this->buildCache(true);
} }

File diff suppressed because one or more lines are too long

View File

@ -15,6 +15,6 @@
"/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=85bcae0a646882e56b12", "/js/clients/quotes/approve.js": "/js/clients/quotes/approve.js?id=85bcae0a646882e56b12",
"/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=5c35d28cf0a3286e7c45", "/js/clients/shared/multiple-downloads.js": "/js/clients/shared/multiple-downloads.js?id=5c35d28cf0a3286e7c45",
"/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=fa54bb4229aba6b0817c", "/js/clients/shared/pdf.js": "/js/clients/shared/pdf.js?id=fa54bb4229aba6b0817c",
"/js/setup/setup.js": "/js/setup/setup.js?id=29e88ab480038cba57df", "/js/setup/setup.js": "/js/setup/setup.js?id=8cb5e2bb0d404725c20a",
"/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad" "/css/card-js.min.css": "/css/card-js.min.css?id=62afeb675235451543ad"
} }

View File

@ -25,6 +25,7 @@ class Setup {
handleDatabaseCheck() { handleDatabaseCheck() {
let data = { let data = {
db_host: document.querySelector('input[name="db_host"]').value, db_host: document.querySelector('input[name="db_host"]').value,
db_port: document.querySelector('input[name="db_port"]').value,
db_database: document.querySelector('input[name="db_database"]') db_database: document.querySelector('input[name="db_database"]')
.value, .value,
db_username: document.querySelector('input[name="db_username"]') db_username: document.querySelector('input[name="db_username"]')
@ -33,13 +34,15 @@ class Setup {
.value, .value,
}; };
this.checkDbButton.disabled = true;
Axios.post('/setup/check_db', data) Axios.post('/setup/check_db', data)
.then((response) => .then((response) =>
this.handleSuccess(this.checkDbAlert, 'mail-wrapper') this.handleSuccess(this.checkDbAlert, 'mail-wrapper')
) )
.catch((e) => .catch((e) =>
this.handleFailure(this.checkDbAlert, e.response.data.message) this.handleFailure(this.checkDbAlert, e.response.data.message)
); ).finally(() => this.checkDbButton.disabled = false);
} }
handleSmtpCheck() { handleSmtpCheck() {
@ -113,7 +116,7 @@ class Setup {
document.getElementById(nextStep).classList.remove('hidden'); document.getElementById(nextStep).classList.remove('hidden');
document document
.getElementById(nextStep) .getElementById(nextStep)
.scrollIntoView({ behavior: 'smooth', block: 'center' }); .scrollIntoView({behavior: 'smooth', block: 'center'});
} }
} }

View File

@ -32,16 +32,6 @@
</dd> </dd>
</div> </div>
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center"> <div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center">
<dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.debug') }}
</dt>
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
<input type="checkbox" class="form-checkbox mr-1" name="debug" {{ old('debug') ? 'checked': '' }}>
<span>{{ ctrans('texts.enable') }}</span>
<span class="text-gray-600 text-xs ml-2">({{ ctrans('texts.enable_only_for_development') }})</span>
</dd>
</div>
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center">
<dt class="text-sm leading-5 font-medium text-gray-500"> <dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.reports') }} {{ ctrans('texts.reports') }}
</dt> </dt>
@ -53,14 +43,14 @@
about how we use this.</a> about how we use this.</a>
</dd> </dd>
</div> </div>
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center"> <div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center">
<dt class="text-sm leading-5 font-medium text-gray-500"> <dt class="text-sm leading-5 font-medium text-gray-500">
<button type="button" class="button button-primary bg-blue-600 py-2 px-3 text-xs" id="test-pdf"> <button type="button" class="button button-primary bg-blue-600 py-2 px-3 text-xs" id="test-pdf">
{{ ctrans('texts.test_pdf') }} {{ ctrans('texts.test_pdf') }}
</button> </button>
</dt> </dt>
<dd class="text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2"> <dd class="text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
<div class="alert py-2 bg-gray-50" id="test-pdf-response"></div> <div class="alert py-2 bg-white" id="test-pdf-response"></div>
</dd> </dd>
<a target="_blank" class="block text-sm text-gray-900 leading-5 underline" <a target="_blank" class="block text-sm text-gray-900 leading-5 underline"
href="https://invoiceninja.github.io/selfhost.html#phantom-js"> href="https://invoiceninja.github.io/selfhost.html#phantom-js">

View File

@ -42,6 +42,14 @@ FLUSH PRIVILEGES;
</dd> </dd>
</div> </div>
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center"> <div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center">
<dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.port') }}*
</dt>
<dd class="text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
<input type="text" class="input w-full" name="db_port" required value="{{ old('db_port') ?: '3306'}}">
</dd>
</div>
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center">
<dt class="text-sm leading-5 font-medium text-gray-500"> <dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.database') }}* {{ ctrans('texts.database') }}*
</dt> </dt>
@ -49,15 +57,15 @@ FLUSH PRIVILEGES;
<input type="text" class="input w-full" name="db_database" required value="{{ old('database') ?: 'db-ninja-01'}}"> <input type="text" class="input w-full" name="db_database" required value="{{ old('database') ?: 'db-ninja-01'}}">
</dd> </dd>
</div> </div>
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center"> <div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center">
<dt class="text-sm leading-5 font-medium text-gray-500" value="{{ old('username') }}"> <dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.username') }}* {{ ctrans('texts.username') }}*
</dt> </dt>
<dd class="text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2"> <dd class="text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
<input type="text" class="input w-full" name="db_username" required value="{{ old('db_username') ?: 'ninja' }}"> <input type="text" class="input w-full" name="db_username" required value="{{ old('db_username') ?: 'ninja' }}">
</dd> </dd>
</div> </div>
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center"> <div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center">
<dt class="text-sm leading-5 font-medium text-gray-500"> <dt class="text-sm leading-5 font-medium text-gray-500">
{{ ctrans('texts.password') }} {{ ctrans('texts.password') }}
</dt> </dt>
@ -65,14 +73,14 @@ FLUSH PRIVILEGES;
<input type="password" class="input w-full" name="db_password" value="{{ old('db_password') ?: 'ninja' }}"> <input type="password" class="input w-full" name="db_password" value="{{ old('db_password') ?: 'ninja' }}">
</dd> </dd>
</div> </div>
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center"> <div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 sm:flex sm:items-center">
<dt class="text-sm leading-5 font-medium text-gray-500"> <dt class="text-sm leading-5 font-medium text-gray-500">
<button type="button" class="button button-primary bg-blue-600 py-2 px-3 text-xs" id="test-db-connection"> <button type="button" class="button button-primary bg-blue-600 py-2 px-3 text-xs" id="test-db-connection">
{{ ctrans('texts.test_connection') }} {{ ctrans('texts.test_connection') }}
</button> </button>
</dt> </dt>
<dd class="text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2"> <dd class="text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
<div class="alert py-2 bg-gray-50" id="database-response"></div> <div class="alert py-2 bg-white" id="database-response"></div>
</dd> </dd>
</div> </div>
</dl> </dl>

View File

@ -37,47 +37,46 @@ class PreviewTest extends TestCase
public function testPreviewRoute() public function testPreviewRoute()
{ {
$data = $this->getData(); $data = $this->getData();
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->post('/api/v1/preview/', $data); ])->post('/api/v1/preview/', $data);
$response->assertStatus(200); $response->assertStatus(200);
} }
public function testPreviewHtmlResponse() public function testPreviewHtmlResponse()
{ {
$data = $this->getData(); $data = $this->getData();
$response = $this->withHeaders([ $response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'), 'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token, 'X-API-TOKEN' => $this->token,
])->post('/api/v1/preview?html=true', $data); ])->post('/api/v1/preview?html=true', $data);
$response->assertStatus(200); $response->assertStatus(200);
} }
private function getData() private function getData()
{ {
$data = $data =
[ [
'entity_type' => 'invoice', 'entity_type' => 'invoice',
'entity_id' => '', 'entity_id' => '',
'design' => [ 'design' => [
'name' => '', 'name' => '',
'design' => [ 'design' => [
'includes' => '</style>', 'includes' => '</style>',
'header' => '<div id="header"></div>', 'header' => '<div id="header"></div>',
'body' => '<div id="body">', 'body' => '<div id="body">',
'product' => '', 'product' => '',
'task' => '', 'task' => '',
'footer' => '<div id="footer">$entity_footer</div>' 'footer' => '<div id="footer">$entity_footer</div>'
], ],
], ],
'is_custom' => 1, 'is_custom' => 1,
]; ];