From 6ec6ae8756eb03e46ac1a844f288f1cd00f0d05a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 17 Feb 2022 23:07:16 +1100 Subject: [PATCH 01/12] Minor fixes for quote permissions --- app/Http/Requests/ClientPortal/Credits/ShowCreditRequest.php | 3 ++- .../Requests/ClientPortal/Documents/ShowDocumentRequest.php | 3 ++- app/Http/Requests/ClientPortal/Invoices/ShowInvoiceRequest.php | 2 +- app/Http/Requests/ClientPortal/Quotes/ShowQuoteRequest.php | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/Http/Requests/ClientPortal/Credits/ShowCreditRequest.php b/app/Http/Requests/ClientPortal/Credits/ShowCreditRequest.php index 432e22fed..c01afe8cf 100644 --- a/app/Http/Requests/ClientPortal/Credits/ShowCreditRequest.php +++ b/app/Http/Requests/ClientPortal/Credits/ShowCreditRequest.php @@ -15,7 +15,8 @@ class ShowCreditRequest extends FormRequest public function authorize() { return !$this->credit->is_deleted - && auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_CREDITS; + && auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_CREDITS + && auth()->guard('contact')->user()->client_id === $this->credit->client_id; } /** diff --git a/app/Http/Requests/ClientPortal/Documents/ShowDocumentRequest.php b/app/Http/Requests/ClientPortal/Documents/ShowDocumentRequest.php index 97ce78a40..3094b78b9 100644 --- a/app/Http/Requests/ClientPortal/Documents/ShowDocumentRequest.php +++ b/app/Http/Requests/ClientPortal/Documents/ShowDocumentRequest.php @@ -27,9 +27,10 @@ class ShowDocumentRequest extends FormRequest */ public function authorize() { + return auth()->guard('contact')->user()->client_id == $this->document->documentable_id - || $this->document->documentable->client_id == auth()->guard('contact')->user()->client_id || $this->document->company_id == auth()->guard('contact')->user()->company_id; + } /** diff --git a/app/Http/Requests/ClientPortal/Invoices/ShowInvoiceRequest.php b/app/Http/Requests/ClientPortal/Invoices/ShowInvoiceRequest.php index 1795dab2f..71a9aa568 100644 --- a/app/Http/Requests/ClientPortal/Invoices/ShowInvoiceRequest.php +++ b/app/Http/Requests/ClientPortal/Invoices/ShowInvoiceRequest.php @@ -23,7 +23,7 @@ class ShowInvoiceRequest extends Request */ public function authorize() : bool { - return auth()->guard('contact')->user()->client_id == $this->invoice->client_id + return auth()->guard('contact')->user()->client_id === $this->invoice->client_id && auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_INVOICES; } } diff --git a/app/Http/Requests/ClientPortal/Quotes/ShowQuoteRequest.php b/app/Http/Requests/ClientPortal/Quotes/ShowQuoteRequest.php index 4c6b819cd..124bb97a4 100644 --- a/app/Http/Requests/ClientPortal/Quotes/ShowQuoteRequest.php +++ b/app/Http/Requests/ClientPortal/Quotes/ShowQuoteRequest.php @@ -19,7 +19,7 @@ class ShowQuoteRequest extends FormRequest { public function authorize() { - return auth()->user()->client->id === $this->quote->client_id + return auth()->guard('contact')->user()->client->id === $this->quote->client_id && auth()->guard('contact')->user()->company->enabled_modules & PortalComposer::MODULE_QUOTES; } From ded4b2af28036ad71e93fe716278e70e20552291 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 18 Feb 2022 10:52:17 +1100 Subject: [PATCH 02/12] Hide archived payment methods where the gateway has been archived / deleted --- app/Http/Livewire/PaymentMethodsTable.php | 4 ++++ app/Services/Invoice/AutoBillInvoice.php | 10 ++++++++-- .../gateways/includes/save_card.blade.php | 5 +---- .../gateways/stripe/credit_card/pay.blade.php | 14 +++++++++++++- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/app/Http/Livewire/PaymentMethodsTable.php b/app/Http/Livewire/PaymentMethodsTable.php index b4906176f..e5dd04905 100644 --- a/app/Http/Livewire/PaymentMethodsTable.php +++ b/app/Http/Livewire/PaymentMethodsTable.php @@ -36,6 +36,10 @@ class PaymentMethodsTable extends Component ->with('gateway_type') ->where('company_id', $this->company->id) ->where('client_id', $this->client->id) + ->whereHas('gateway', function ($query) { + $query->where('is_deleted',0) + ->where('deleted_at', NULL); + }) ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc') ->paginate($this->per_page); diff --git a/app/Services/Invoice/AutoBillInvoice.php b/app/Services/Invoice/AutoBillInvoice.php index 17a511909..ec5ddc18f 100644 --- a/app/Services/Invoice/AutoBillInvoice.php +++ b/app/Services/Invoice/AutoBillInvoice.php @@ -307,8 +307,14 @@ class AutoBillInvoice extends AbstractService { //get all client gateway tokens and set the is_default one to the first record - $gateway_tokens = $this->client->gateway_tokens()->orderBy('is_default', 'DESC')->get(); - // $gateway_tokens = $this->client->gateway_tokens; + $gateway_tokens = $this->client + ->gateway_tokens() + ->whereHas('gateway', function ($query) { + $query->where('is_deleted',0) + ->where('deleted_at', NULL); + })->orderBy('is_default', 'DESC') + ->get(); + $filtered_gateways = $gateway_tokens->filter(function ($gateway_token) use($amount) { diff --git a/resources/views/portal/ninja2020/gateways/includes/save_card.blade.php b/resources/views/portal/ninja2020/gateways/includes/save_card.blade.php index 42e50ca1d..e319effdc 100644 --- a/resources/views/portal/ninja2020/gateways/includes/save_card.blade.php +++ b/resources/views/portal/ninja2020/gateways/includes/save_card.blade.php @@ -1,7 +1,4 @@ @php - // $token_billing = $gateway instanceof \App\Models\CompanyGateway - // ? $gateway->token_billing !== 'always' - // : $gateway->company_gateway->token_billing !== 'always'; $gateway_instance = $gateway instanceof \App\Models\CompanyGateway ? $gateway : $gateway->company_gateway; $token_billing = true; @@ -9,7 +6,7 @@ $checked_on = ''; $checked_off = 'checked'; - if($gateway_instance->token_billing == 'off' || $gateway_instance->token_billing == 'always'){ + if($gateway_instance->token_billing == 'off'){ $token_billing = false; $token_billing_string = 'false'; } diff --git a/resources/views/portal/ninja2020/gateways/stripe/credit_card/pay.blade.php b/resources/views/portal/ninja2020/gateways/stripe/credit_card/pay.blade.php index 53216f13d..8a91de072 100644 --- a/resources/views/portal/ninja2020/gateways/stripe/credit_card/pay.blade.php +++ b/resources/views/portal/ninja2020/gateways/stripe/credit_card/pay.blade.php @@ -1,5 +1,17 @@ @extends('portal.ninja2020.layout.payments', ['gateway_title' => 'Credit card', 'card_title' => 'Credit card']) +@php + $gateway_instance = $gateway instanceof \App\Models\CompanyGateway ? $gateway : $gateway->company_gateway; + $token_billing_string = 'true'; + + if($gateway_instance->token_billing == 'off' || $gateway_instance->token_billing == 'optin'){ + $token_billing_string = 'false'; + } + + +@endphp + + @section('gateway_head') @if($gateway->company_gateway->getConfigField('account_id')) @@ -18,7 +30,7 @@
@csrf - + From 84c03e3db67987c11ac5d4f42dd072d59db8e84c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 18 Feb 2022 11:56:43 +1100 Subject: [PATCH 03/12] Fixes for line spacing --- app/Utils/Helpers.php | 6 +++++- app/Utils/HtmlEngine.php | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/Utils/Helpers.php b/app/Utils/Helpers.php index a2507496e..f1b139efa 100644 --- a/app/Utils/Helpers.php +++ b/app/Utils/Helpers.php @@ -262,7 +262,11 @@ class Helpers } } - return $value; + $x = str_replace(["\n", "
"], ["\r", "
"], $value); + + nlog($x); + + return $x; } /** diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index dba31cfb1..beb8c79f4 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -165,7 +165,7 @@ class HtmlEngine $data['$entity'] = ['value' => '', 'label' => ctrans('texts.quote')]; $data['$number'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.quote_number')]; $data['$number_short'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.quote_number_short')]; - $data['$entity.terms'] = ['value' => $this->entity->terms ?: '', 'label' => ctrans('texts.quote_terms')]; + $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->client) ?: '', 'label' => ctrans('texts.quote_terms')]; $data['$terms'] = &$data['$entity.terms']; $data['$view_link'] = ['value' => ''.ctrans('texts.view_quote').'', 'label' => ctrans('texts.view_quote')]; $data['$viewLink'] = &$data['$view_link']; @@ -180,7 +180,7 @@ class HtmlEngine $data['$entity'] = ['value' => '', 'label' => ctrans('texts.credit')]; $data['$number'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.credit_number')]; $data['$number_short'] = ['value' => $this->entity->number ?: '', 'label' => ctrans('texts.credit_number_short')]; - $data['$entity.terms'] = ['value' => $this->entity->terms ?: '', 'label' => ctrans('texts.credit_terms')]; + $data['$entity.terms'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->terms), $this->client) ?: '', 'label' => ctrans('texts.credit_terms')]; $data['$terms'] = &$data['$entity.terms']; $data['$view_link'] = ['value' => ''.ctrans('texts.view_credit').'', 'label' => ctrans('texts.view_credit')]; $data['$viewButton'] = &$data['$view_link']; From 9c106e8d0ae8b9450b8465a20f12c57f995bdbb9 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 18 Feb 2022 16:31:17 +1100 Subject: [PATCH 04/12] Performance improvements for client portal --- app/Http/Livewire/InvoicesTable.php | 2 +- app/Http/Middleware/Locale.php | 2 +- app/PaymentDrivers/BaseDriver.php | 2 +- app/Services/Client/PaymentMethod.php | 29 +++++++++---------- app/Services/Payment/UpdateInvoicePayment.php | 17 ----------- .../portal/ninja2020/invoices/show.blade.php | 13 --------- 6 files changed, 16 insertions(+), 49 deletions(-) diff --git a/app/Http/Livewire/InvoicesTable.php b/app/Http/Livewire/InvoicesTable.php index 36bf35237..d5b75448e 100644 --- a/app/Http/Livewire/InvoicesTable.php +++ b/app/Http/Livewire/InvoicesTable.php @@ -43,7 +43,7 @@ class InvoicesTable extends Component $local_status = []; $query = Invoice::query() - ->with('client.gateway_tokens','company','client.contacts') + ->with('client.gateway_tokens','client.contacts') ->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc') ->where('company_id', $this->company->id) ->where('is_deleted', false); diff --git a/app/Http/Middleware/Locale.php b/app/Http/Middleware/Locale.php index e43d6d7ff..771d926f8 100644 --- a/app/Http/Middleware/Locale.php +++ b/app/Http/Middleware/Locale.php @@ -33,7 +33,7 @@ class Locale $locale = $request->input('lang'); App::setLocale($locale); } elseif (auth()->guard('contact')->user()) { - App::setLocale(auth()->guard('contact')->user()->client->locale()); + App::setLocale(auth()->guard('contact')->user()->client()->setEagerLoads([])->first()->locale()); } elseif (auth()->user()) { try{ diff --git a/app/PaymentDrivers/BaseDriver.php b/app/PaymentDrivers/BaseDriver.php index 0c52bb908..4cf6aa938 100644 --- a/app/PaymentDrivers/BaseDriver.php +++ b/app/PaymentDrivers/BaseDriver.php @@ -258,7 +258,7 @@ class BaseDriver extends AbstractPaymentDriver $payment->client_contact_id = $client_contact_id; $payment->saveQuietly(); - /* Return early if the payment is no completed or pending*/ + /* Return early if the payment is not completed or pending*/ if(!in_array($status, [Payment::STATUS_COMPLETED, Payment::STATUS_PENDING]) ) return $payment; diff --git a/app/Services/Client/PaymentMethod.php b/app/Services/Client/PaymentMethod.php index 4eee1a235..4a18387d2 100644 --- a/app/Services/Client/PaymentMethod.php +++ b/app/Services/Client/PaymentMethod.php @@ -70,25 +70,24 @@ class PaymentMethod $transformed_ids = $this->transformKeys(explode(',', $company_gateways)); - $this->gateways = $this->client - ->company - ->company_gateways + $this->gateways = + CompanyGateway::with('gateway') + ->where('company_id', $this->client->company_id) ->whereIn('id', $transformed_ids) ->where('is_deleted', false) ->whereNull('deleted_at') ->where('gateway_key', '!=', '54faab2ab6e3223dbe848b1686490baa') ->sortby(function ($model) use ($transformed_ids) { //company gateways are sorted in order of priority return array_search($model->id, $transformed_ids);// this closure sorts for us - }); + })->get(); } else { - $this->gateways = $this->client - ->company - ->company_gateways + $this->gateways = CompanyGateway::with('gateway') + ->where('company_id', $this->client->company_id) ->where('gateway_key', '!=', '54faab2ab6e3223dbe848b1686490baa') ->whereNull('deleted_at') - ->where('is_deleted', false); + ->where('is_deleted', false)->get(); } @@ -106,25 +105,23 @@ class PaymentMethod $transformed_ids = $this->transformKeys(explode(',', $company_gateways)); - $this->gateways = $this->client - ->company - ->company_gateways + $this->gateways = CompanyGateway::with('gateway') + ->where('company_id', $this->client->company_id) ->whereIn('id', $transformed_ids) ->where('is_deleted', false) ->whereNull('deleted_at') ->where('gateway_key', '54faab2ab6e3223dbe848b1686490baa') ->sortby(function ($model) use ($transformed_ids) { //company gateways are sorted in order of priority return array_search($model->id, $transformed_ids);// this closure sorts for us - }); + })->get(); } else { - $this->gateways = $this->client - ->company - ->company_gateways + $this->gateways = CompanyGateway::with('gateway') + ->where('company_id', $this->client->company_id) ->where('gateway_key', '54faab2ab6e3223dbe848b1686490baa') ->whereNull('deleted_at') - ->where('is_deleted', false); + ->where('is_deleted', false)->get(); } diff --git a/app/Services/Payment/UpdateInvoicePayment.php b/app/Services/Payment/UpdateInvoicePayment.php index 5a6a2a239..7065705c2 100644 --- a/app/Services/Payment/UpdateInvoicePayment.php +++ b/app/Services/Payment/UpdateInvoicePayment.php @@ -87,23 +87,6 @@ class UpdateInvoicePayment $this->payment->applied += $paid_amount; - // $invoice->service() //caution what if we amount paid was less than partial - we wipe it! - // ->clearPartial() - // ->updateBalance($paid_amount * -1) - // ->updatePaidToDate($paid_amount) - // ->updateStatus() - // ->save(); - - // $invoice->refresh(); - - // $invoice->service() - // ->touchPdf(true) - // ->workFlow() - // ->save(); - - - - }); /* Remove the event updater from within the loop to prevent race conditions */ diff --git a/resources/views/portal/ninja2020/invoices/show.blade.php b/resources/views/portal/ninja2020/invoices/show.blade.php index a9401ca1e..f374b1459 100644 --- a/resources/views/portal/ninja2020/invoices/show.blade.php +++ b/resources/views/portal/ninja2020/invoices/show.blade.php @@ -105,18 +105,5 @@ var clipboard = new ClipboardJS('.btn'); - // clipboard.on('success', function(e) { - // console.info('Action:', e.action); - // console.info('Text:', e.text); - // console.info('Trigger:', e.trigger); - - // e.clearSelection(); - // }); - - // clipboard.on('error', function(e) { - // console.error('Action:', e.action); - // console.error('Trigger:', e.trigger); - // }); - @endsection From 670ad4359b51189b9b927b41d983e49e0a6a0275 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 18 Feb 2022 16:38:15 +1100 Subject: [PATCH 05/12] Performance improvements for client portal --- app/Http/Livewire/InvoicesTable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Livewire/InvoicesTable.php b/app/Http/Livewire/InvoicesTable.php index d5b75448e..e389c4523 100644 --- a/app/Http/Livewire/InvoicesTable.php +++ b/app/Http/Livewire/InvoicesTable.php @@ -75,7 +75,7 @@ class InvoicesTable extends Component } $query = $query - ->where('client_id', auth()->guard('contact')->user()->client->id) + ->where('client_id', auth()->guard('contact')->user()->client_id) ->where('status_id', '<>', Invoice::STATUS_DRAFT) ->where('status_id', '<>', Invoice::STATUS_CANCELLED) ->withTrashed() From e42ac1197d35e1332cf6834ac01d6306616d202b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 18 Feb 2022 19:06:09 +1100 Subject: [PATCH 06/12] Performance improvements for Swoole --- app/Providers/MailCssInlinerServiceProvider.php | 13 ++++++++++++- .../ninja2020/components/pdf-viewer.blade.php | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/Providers/MailCssInlinerServiceProvider.php b/app/Providers/MailCssInlinerServiceProvider.php index a3e223a4a..3bcf96816 100644 --- a/app/Providers/MailCssInlinerServiceProvider.php +++ b/app/Providers/MailCssInlinerServiceProvider.php @@ -12,6 +12,7 @@ namespace App\Providers; use App\Utils\CssInlinerPlugin; +use Illuminate\Container\Container; use Illuminate\Mail\MailManager; use Illuminate\Support\ServiceProvider; @@ -38,8 +39,18 @@ class MailCssInlinerServiceProvider extends ServiceProvider */ public function register() { - $this->app->singleton(CssInlinerPlugin::class, function ($app) { + // $this->app->singleton(CssInlinerPlugin::class, function ($app) { + // return new CssInlinerPlugin([]); + // }); + + // $this->app->singleton(CssInlinerPlugin::class, function ($app) { + // return new CssInlinerPlugin([]); + // }); + + $this->app->bind(CssInlinerPlugin::class, function($app) { return new CssInlinerPlugin([]); }); } } + + diff --git a/resources/views/portal/ninja2020/components/pdf-viewer.blade.php b/resources/views/portal/ninja2020/components/pdf-viewer.blade.php index 52f3aaa8b..6812bfcbb 100644 --- a/resources/views/portal/ninja2020/components/pdf-viewer.blade.php +++ b/resources/views/portal/ninja2020/components/pdf-viewer.blade.php @@ -1,5 +1,5 @@ @php - $mobile = stripos($_SERVER['HTTP_USER_AGENT'], 'Android') || stripos($_SERVER['HTTP_USER_AGENT'], 'iPhone') || stripos($_SERVER['HTTP_USER_AGENT'], 'iPod') || stripos($_SERVER['HTTP_USER_AGENT'], 'iPad'); + $mobile = stripos(request()->server('HTTP_USER_AGENT'), 'Android') || stripos(request()->server('HTTP_USER_AGENT'), 'iPhone') || stripos(request()->server('HTTP_USER_AGENT'), 'iPod') || stripos(request()->server('HTTP_USER_AGENT'), 'iPad'); @endphp @push('head') From 6d10160f33d1f698423769288ca4ac6801bb2377 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 18 Feb 2022 19:07:40 +1100 Subject: [PATCH 07/12] Clean up --- app/Http/Middleware/SessionDomains.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/Http/Middleware/SessionDomains.php b/app/Http/Middleware/SessionDomains.php index 86fb61a32..849665fc0 100644 --- a/app/Http/Middleware/SessionDomains.php +++ b/app/Http/Middleware/SessionDomains.php @@ -39,8 +39,6 @@ class SessionDomains } else{ - // Cookie::queue(Cookie::forget('invoice_ninja_session', '/')); - config(['session.domain' => $domain_name]); } From 5259728443ae7803a025bf34af6977a70517c70d Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 18 Feb 2022 20:55:44 +1100 Subject: [PATCH 08/12] Improve .zip functionality --- .../ClientPortal/InvoiceController.php | 55 +++++++++++--- composer.json | 3 +- composer.lock | 75 ++++++++++++++++++- 3 files changed, 120 insertions(+), 13 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/InvoiceController.php b/app/Http/Controllers/ClientPortal/InvoiceController.php index b6789e8df..9a38d07f0 100644 --- a/app/Http/Controllers/ClientPortal/InvoiceController.php +++ b/app/Http/Controllers/ClientPortal/InvoiceController.php @@ -228,21 +228,54 @@ class InvoiceController extends Controller }, basename($file), ['Content-Type' => 'application/pdf']); } - // enable output of HTTP headers - $options = new Archive(); - $options->setSendHttpHeaders(true); + return $this->buildZip($invoices); - // create a new zipstream object - $zip = new ZipStream(date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip', $options); + // // enable output of HTTP headers + // $options = new Archive(); + // $options->setSendHttpHeaders(true); - foreach ($invoices as $invoice) { + // // create a new zipstream object + // $zip = new ZipStream(date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip', $options); - #add it to the zip - $zip->addFile(basename($invoice->pdf_file_path()), file_get_contents($invoice->pdf_file_path(null, 'url', true))); + // foreach ($invoices as $invoice) { + + // #add it to the zip + // $zip->addFile(basename($invoice->pdf_file_path()), file_get_contents($invoice->pdf_file_path(null, 'url', true))); + + // } + + // // finish the zip stream + // $zip->finish(); + } + + + private function buildZip($invoices) + { + // create new archive + $zipFile = new \PhpZip\ZipFile(); + try{ + + foreach ($invoices as $invoice) { + + #add it to the zip + $zipFile->addFromString(basename($invoice->pdf_file_path()), file_get_contents($invoice->pdf_file_path(null, 'url', true))); + + } + + $filename = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip'; + $filepath = sys_get_temp_dir() . '/' . $filename; + + $zipFile->saveAsFile($filepath) // save the archive to a file + ->close(); // close archive + + return response()->download($filepath, $filename)->deleteFileAfterSend(true); } - - // finish the zip stream - $zip->finish(); + catch(\PhpZip\Exception\ZipException $e){ + // handle exception + } + finally{ + $zipFile->close(); + } } } diff --git a/composer.json b/composer.json index 877931817..fb87b944e 100644 --- a/composer.json +++ b/composer.json @@ -67,6 +67,7 @@ "livewire/livewire": "^2.6", "maennchen/zipstream-php": "^1.2", "mollie/mollie-api-php": "^2.36", + "nelexa/zip": "^4.0", "nwidart/laravel-modules": "^8.0", "omnipay/paypal": "^3.0", "payfast/payfast-php-sdk": "^1.1", @@ -149,4 +150,4 @@ }, "minimum-stability": "dev", "prefer-stable": true -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index e8ef2564f..ecfd6023b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f430e1dc06c72464f547ba0bb71226cf", + "content-hash": "ebb191c91f4011a448605a0fd725faff", "packages": [ { "name": "apimatic/jsonmapper", @@ -5582,6 +5582,79 @@ ], "time": "2021-07-05T08:18:36+00:00" }, + { + "name": "nelexa/zip", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/Ne-Lexa/php-zip.git", + "reference": "a18f80db509b1b6e9798e2745dc100759107f50c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Ne-Lexa/php-zip/zipball/a18f80db509b1b6e9798e2745dc100759107f50c", + "reference": "a18f80db509b1b6e9798e2745dc100759107f50c", + "shasum": "" + }, + "require": { + "ext-zlib": "*", + "php": "^7.4 || ^8.0", + "psr/http-message": "*", + "symfony/finder": "*" + }, + "require-dev": { + "ext-bz2": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-iconv": "*", + "ext-openssl": "*", + "ext-xml": "*", + "friendsofphp/php-cs-fixer": "^3.4.0", + "guzzlehttp/psr7": "^1.6", + "phpunit/phpunit": "^9", + "symfony/http-foundation": "*", + "symfony/var-dumper": "*", + "vimeo/psalm": "^4.6" + }, + "suggest": { + "ext-bz2": "Needed to support BZIP2 compression", + "ext-fileinfo": "Needed to get mime-type file", + "ext-iconv": "Needed to support convert zip entry name to requested character encoding", + "ext-openssl": "Needed to support encrypt zip entries or use ext-mcrypt" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpZip\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ne-Lexa", + "email": "alexey@nelexa.ru", + "role": "Developer" + } + ], + "description": "PhpZip is a php-library for extended work with ZIP-archives. Open, create, update, delete, extract and get info tool. Supports appending to existing ZIP files, WinZip AES encryption, Traditional PKWARE Encryption, BZIP2 compression, external file attributes and ZIP64 extensions. Alternative ZipArchive. It does not require php-zip extension.", + "homepage": "https://github.com/Ne-Lexa/php-zip", + "keywords": [ + "archive", + "extract", + "unzip", + "winzip", + "zip", + "ziparchive" + ], + "support": { + "issues": "https://github.com/Ne-Lexa/php-zip/issues", + "source": "https://github.com/Ne-Lexa/php-zip/tree/4.0.1" + }, + "time": "2021-12-12T09:50:45+00:00" + }, { "name": "nesbot/carbon", "version": "2.56.0", From 118d2bc214d524a907b39f78d8cf067a01392855 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 18 Feb 2022 21:45:01 +1100 Subject: [PATCH 09/12] Refactor for zip files --- .../ClientPortal/DocumentController.php | 35 +++++++++------- .../ClientPortal/InvoiceController.php | 21 ---------- .../ClientPortal/QuoteController.php | 41 +++++++++++++------ app/Jobs/Company/CompanyExport.php | 2 - app/Jobs/Company/CompanyImport.php | 2 - app/Jobs/Invoice/ZipInvoices.php | 2 - composer.json | 1 - .../js/clients/shared/multiple-downloads.js | 1 + 8 files changed, 50 insertions(+), 55 deletions(-) diff --git a/app/Http/Controllers/ClientPortal/DocumentController.php b/app/Http/Controllers/ClientPortal/DocumentController.php index f24615f13..de2fa66c7 100644 --- a/app/Http/Controllers/ClientPortal/DocumentController.php +++ b/app/Http/Controllers/ClientPortal/DocumentController.php @@ -22,8 +22,6 @@ use App\Utils\Traits\MakesHash; use Illuminate\Contracts\View\Factory; use Illuminate\Support\Facades\Storage; use Illuminate\View\View; -use ZipStream\Option\Archive; -use ZipStream\ZipStream; class DocumentController extends Controller { @@ -71,25 +69,34 @@ class DocumentController extends Controller public function downloadMultiple(DownloadMultipleDocumentsRequest $request) { $documents = Document::whereIn('id', $this->transformKeys($request->file_hash)) - ->where('company_id', auth()->guard('contact')->user()->company->id) + ->where('company_id', auth()->guard('contact')->user()->company_id) ->get(); - $documents->map(function ($document) { - if (auth()->guard('contact')->user()->client->id != $document->documentable->id) { - abort(401, 'Permission denied'); + $zipFile = new \PhpZip\ZipFile(); + + try{ + + foreach ($documents as $document) { + $zipFile->addFile(TempFile::path($document->filePath()), $document->name); } - }); - $options = new Archive(); + $filename = now() . '-documents.zip'; + $filepath = sys_get_temp_dir() . '/' . $filename; - $options->setSendHttpHeaders(true); + $zipFile->saveAsFile($filepath) // save the archive to a file + ->close(); // close archive + + return response()->download($filepath, $filename)->deleteFileAfterSend(true); - $zip = new ZipStream(now() . '-documents.zip', $options); - - foreach ($documents as $document) { - $zip->addFileFromPath(basename($document->diskPath()), TempFile::path($document->filePath())); + } + catch(\PhpZip\Exception\ZipException $e){ + // handle exception + } + finally{ + $zipFile->close(); } - $zip->finish(); } + + } diff --git a/app/Http/Controllers/ClientPortal/InvoiceController.php b/app/Http/Controllers/ClientPortal/InvoiceController.php index 9a38d07f0..6dad4a19a 100644 --- a/app/Http/Controllers/ClientPortal/InvoiceController.php +++ b/app/Http/Controllers/ClientPortal/InvoiceController.php @@ -28,8 +28,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\Storage; use Illuminate\View\View; -use ZipStream\Option\Archive; -use ZipStream\ZipStream; use Illuminate\Http\Request; class InvoiceController extends Controller @@ -198,9 +196,6 @@ class InvoiceController extends Controller * @param array $ids * * @return void - * @throws \ZipStream\Exception\FileNotFoundException - * @throws \ZipStream\Exception\FileNotReadableException - * @throws \ZipStream\Exception\OverflowException */ private function downloadInvoicePDF(array $ids) { @@ -230,22 +225,6 @@ class InvoiceController extends Controller return $this->buildZip($invoices); - // // enable output of HTTP headers - // $options = new Archive(); - // $options->setSendHttpHeaders(true); - - // // create a new zipstream object - // $zip = new ZipStream(date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip', $options); - - // foreach ($invoices as $invoice) { - - // #add it to the zip - // $zip->addFile(basename($invoice->pdf_file_path()), file_get_contents($invoice->pdf_file_path(null, 'url', true))); - - // } - - // // finish the zip stream - // $zip->finish(); } diff --git a/app/Http/Controllers/ClientPortal/QuoteController.php b/app/Http/Controllers/ClientPortal/QuoteController.php index fec64c9b4..378fd8aa0 100644 --- a/app/Http/Controllers/ClientPortal/QuoteController.php +++ b/app/Http/Controllers/ClientPortal/QuoteController.php @@ -28,8 +28,6 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Support\Facades\Storage; use Illuminate\View\View; use Symfony\Component\HttpFoundation\BinaryFileResponse; -use ZipStream\Option\Archive; -use ZipStream\ZipStream; use Illuminate\Http\Request; use Illuminate\Support\Carbon; @@ -140,21 +138,38 @@ class QuoteController extends Controller }, basename($file), ['Content-Type' => 'application/pdf']); } - // enable output of HTTP headers - $options = new Archive(); - $options->setSendHttpHeaders(true); + return $this->buildZip($quotes); - // create a new zipstream object - $zip = new ZipStream(date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.invoices')).'.zip', $options); + } - foreach ($quotes as $quote) { - $zip->addFile(basename($quote->pdf_file_path()), file_get_contents($quote->pdf_file_path(null, 'url', true))); + private function buildZip($quotes) + { + // create new archive + $zipFile = new \PhpZip\ZipFile(); + try{ + + foreach ($quotes as $quote) { + + #add it to the zip + $zipFile->addFromString(basename($quote->pdf_file_path()), file_get_contents($quote->pdf_file_path(null, 'url', true))); + + } + + $filename = date('Y-m-d').'_'.str_replace(' ', '_', trans('texts.quotes')).'.zip'; + $filepath = sys_get_temp_dir() . '/' . $filename; + + $zipFile->saveAsFile($filepath) // save the archive to a file + ->close(); // close archive + + return response()->download($filepath, $filename)->deleteFileAfterSend(true); - // $zip->addFileFromPath(basename($quote->pdf_file_path()), TempFile::path($quote->pdf_file_path())); } - - // finish the zip stream - $zip->finish(); + catch(\PhpZip\Exception\ZipException $e){ + // handle exception + } + finally{ + $zipFile->close(); + } } protected function approve(array $ids, $process = false) diff --git a/app/Jobs/Company/CompanyExport.php b/app/Jobs/Company/CompanyExport.php index d8917c494..4e6fcc528 100644 --- a/app/Jobs/Company/CompanyExport.php +++ b/app/Jobs/Company/CompanyExport.php @@ -33,8 +33,6 @@ use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Storage; -use ZipStream\Option\Archive; -use ZipStream\ZipStream; use Illuminate\Support\Facades\App; class CompanyExport implements ShouldQueue diff --git a/app/Jobs/Company/CompanyImport.php b/app/Jobs/Company/CompanyImport.php index 5150740c8..ffc88ff83 100644 --- a/app/Jobs/Company/CompanyImport.php +++ b/app/Jobs/Company/CompanyImport.php @@ -74,8 +74,6 @@ use Illuminate\Support\Str; use JsonMachine\JsonDecoder\ExtJsonDecoder; use JsonMachine\JsonMachine; use ZipArchive; -use ZipStream\Option\Archive; -use ZipStream\ZipStream; use function GuzzleHttp\json_encode; diff --git a/app/Jobs/Invoice/ZipInvoices.php b/app/Jobs/Invoice/ZipInvoices.php index 0dff22aa3..18fa2f0b9 100644 --- a/app/Jobs/Invoice/ZipInvoices.php +++ b/app/Jobs/Invoice/ZipInvoices.php @@ -25,8 +25,6 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Storage; -use ZipStream\Option\Archive; -use ZipStream\ZipStream; use ZipArchive; class ZipInvoices implements ShouldQueue diff --git a/composer.json b/composer.json index fb87b944e..90cbe1790 100644 --- a/composer.json +++ b/composer.json @@ -65,7 +65,6 @@ "league/fractal": "^0.17.0", "league/omnipay": "^3.1", "livewire/livewire": "^2.6", - "maennchen/zipstream-php": "^1.2", "mollie/mollie-api-php": "^2.36", "nelexa/zip": "^4.0", "nwidart/laravel-modules": "^8.0", diff --git a/resources/js/clients/shared/multiple-downloads.js b/resources/js/clients/shared/multiple-downloads.js index d3662fff8..7dd69e860 100644 --- a/resources/js/clients/shared/multiple-downloads.js +++ b/resources/js/clients/shared/multiple-downloads.js @@ -24,6 +24,7 @@ const appendToElement = (parent, value) => { _temp.hidden = true; _parent.append(_temp); + }; window.appendToElement = appendToElement; From 25f8cd249d45f979d992a4673f1795bd2addab01 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 19 Feb 2022 07:12:00 +1100 Subject: [PATCH 10/12] Add in checks for account payments --- app/Console/Commands/CheckData.php | 58 ++++++++++++++---------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index 68ca30c41..0656c39f1 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -115,6 +115,8 @@ class CheckData extends Command $this->checkEntityInvitations(); $this->checkCompanyData(); + if(Ninja::isHosted()) + $this->checkAccountStatuses(); if (! $this->option('client_id')) { $this->checkOAuth(); @@ -244,38 +246,6 @@ class CheckData extends Command } } - // // check for more than one primary contact - // $clients = DB::table('clients') - // ->leftJoin('client_contacts', function ($join) { - // $join->on('client_contacts.client_id', '=', 'clients.id') - // ->where('client_contacts.is_primary', '=', true) - // ->whereNull('client_contacts.deleted_at'); - // }) - // ->groupBy('clients.id') - // ->havingRaw('count(client_contacts.id) != 1'); - - // if ($this->option('client_id')) { - // $clients->where('clients.id', '=', $this->option('client_id')); - // } - - // $clients = $clients->get(['clients.id', 'clients.user_id', 'clients.company_id']); - // // $this->logMessage($clients->count().' clients without a single primary contact'); - - // // if ($this->option('fix') == 'true') { - // // foreach ($clients as $client) { - // // $this->logMessage("Fixing missing primary contacts #{$client->id}"); - - // // $new_contact = ClientContactFactory::create($client->company_id, $client->user_id); - // // $new_contact->client_id = $client->id; - // // $new_contact->contact_key = Str::random(40); - // // $new_contact->is_primary = true; - // // $new_contact->save(); - // // } - // // } - - // if ($clients->count() > 0) { - // $this->isValid = false; - // } } private function checkFailedJobs() @@ -948,6 +918,30 @@ ORDER BY clients.id; return $type.'s'; } + + public function checkAccountStatuses() + { + Account::where('plan_expires', '<=', now()->subDays(2))->cursor()->each(function ($account){ + + $client = Client::on('db-ninja-01')->where('company_id', config('ninja.ninja_default_company_id'))->where('custom_value2', $account->key)->first(); + + if($client){ + $payment = Payment::on('db-ninja-01') + ->where('company_id', config('ninja.ninja_default_company_id')) + ->where('client_id', $client->id) + ->where('date', '>=', now()->subDays(2)) + ->exists(); + + if($payment) + $this->logMessage("I found a payment for {$account->key}"); + + + } + + + }); + } + } From 92e2c7c614be8a1454c7894943eed228a657ee13 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 19 Feb 2022 16:11:30 +1100 Subject: [PATCH 11/12] Clean up --- app/Console/Commands/BackupUpdate.php | 15 +++++++++------ app/Console/Commands/CheckData.php | 1 + app/Http/Controllers/MigrationController.php | 7 ++++--- app/Mail/TemplateEmail.php | 2 -- app/Models/Backup.php | 4 ++-- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/app/Console/Commands/BackupUpdate.php b/app/Console/Commands/BackupUpdate.php index 08afc8324..d1418a7a7 100644 --- a/app/Console/Commands/BackupUpdate.php +++ b/app/Console/Commands/BackupUpdate.php @@ -75,16 +75,19 @@ class BackupUpdate extends Command private function handleOnDb() { + set_time_limit(0); + + Backup::chunk(100, function ($backups) { + foreach ($backups as $backup) { + + if($backup->activity->client()->exists()){ - Backup::whereHas('activity')->whereNotNull('html_backup')->cursor()->each(function($backup){ + $client = $backup->activity->client; + $backup->storeRemotely($backup->html_backup, $client); - if($backup->activity->client()->exists()){ - - $client = $backup->activity->client; - $backup->storeRemotely($backup->html_backup, $client); + } } - }); } diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index 0656c39f1..22fcba5c5 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -417,6 +417,7 @@ class CheckData extends Command payments.id = paymentables.payment_id WHERE paymentable_type = 'App\\Models\\Credit' AND paymentables.deleted_at is NULL + AND payments.is_deleted = 0 AND payments.client_id = ?; "), [$client->id] ); diff --git a/app/Http/Controllers/MigrationController.php b/app/Http/Controllers/MigrationController.php index 0b04cc504..bbf997f26 100644 --- a/app/Http/Controllers/MigrationController.php +++ b/app/Http/Controllers/MigrationController.php @@ -265,15 +265,16 @@ class MigrationController extends BaseController foreach($request->all() as $input){ - if($input instanceof UploadedFile) - nlog('is file'); + if($input instanceof UploadedFile){ + + } else $companies[] = json_decode($input,1); } } if (app()->environment() === 'local') { - nlog($request->all()); + } try { diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php index b5aee3923..20296b2d7 100644 --- a/app/Mail/TemplateEmail.php +++ b/app/Mail/TemplateEmail.php @@ -132,8 +132,6 @@ class TemplateEmail extends Mailable if($this->invitation && $this->invitation->invoice && $settings->ubl_email_attachment && $this->company->account->hasFeature(Account::FEATURE_PDF_ATTACHMENT)){ $ubl_string = CreateUbl::dispatchNow($this->invitation->invoice); - - nlog($ubl_string); if($ubl_string) $this->attachData($ubl_string, $this->invitation->invoice->getFileName('xml')); diff --git a/app/Models/Backup.php b/app/Models/Backup.php index 72fa770ca..75540bc43 100644 --- a/app/Models/Backup.php +++ b/app/Models/Backup.php @@ -26,10 +26,10 @@ class Backup extends BaseModel return $this->belongsTo(Activity::class); } - public function storeRemotely(string $html, Client $client) + public function storeRemotely(?string $html, Client $client) { - if(strlen($html) == 0) + if(!$html || strlen($html) == 0) return; $path = $client->backup_path() . "/"; From 65565f01fc56186ef120b06d72fc30d955b09bdc Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 19 Feb 2022 21:41:34 +1100 Subject: [PATCH 12/12] Improve Stripe customer importationg --- app/PaymentDrivers/Stripe/Connect/Verify.php | 16 ++++++++++++-- app/PaymentDrivers/Stripe/ImportCustomers.php | 21 ++++++++++++------- app/Utils/Helpers.php | 2 -- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/app/PaymentDrivers/Stripe/Connect/Verify.php b/app/PaymentDrivers/Stripe/Connect/Verify.php index e957e6f5b..fb4919529 100644 --- a/app/PaymentDrivers/Stripe/Connect/Verify.php +++ b/app/PaymentDrivers/Stripe/Connect/Verify.php @@ -52,7 +52,19 @@ class Verify if($this->stripe->stripe_connect && strlen($this->stripe->company_gateway->getConfigField('account_id')) < 1) throw new StripeConnectFailure('Stripe Connect has not been configured'); - $customers = Customer::all([], $this->stripe->stripe_connect_auth); + $total_stripe_customers = 0; + + $starting_after = null; + + do { + + $customers = Customer::all(['limit' => 100, 'starting_after' => $starting_after], $this->stripe->stripe_connect_auth); + + $total_stripe_customers += count($customers->data); + $starting_after = end($customers->data)['id']; + + } while($customers->has_more); + $stripe_customers = $this->stripe->company_gateway->client_gateway_tokens->map(function ($cgt){ @@ -66,7 +78,7 @@ class Verify }); $data = [ - 'stripe_customer_count' => count($customers), + 'stripe_customer_count' => $total_stripe_customers, 'stripe_customers' => $stripe_customers, ]; diff --git a/app/PaymentDrivers/Stripe/ImportCustomers.php b/app/PaymentDrivers/Stripe/ImportCustomers.php index bd06616da..38690c8aa 100644 --- a/app/PaymentDrivers/Stripe/ImportCustomers.php +++ b/app/PaymentDrivers/Stripe/ImportCustomers.php @@ -55,13 +55,20 @@ class ImportCustomers if(Ninja::isHosted() && strlen($this->stripe->company_gateway->getConfigField('account_id')) < 1) throw new StripeConnectFailure('Stripe Connect has not been configured'); - $customers = Customer::all([], $this->stripe->stripe_connect_auth); + $starting_after = null; - foreach($customers as $customer) - { - $this->addCustomer($customer); - } + do { + + $customers = Customer::all(['limit' => 100, 'starting_after' => $starting_after], $this->stripe->stripe_connect_auth); + foreach($customers as $customer) + { + $this->addCustomer($customer); + } + + $starting_after = end($customers->data)['id']; + + } while($customers->has_more); } private function addCustomer(Customer $customer) @@ -72,7 +79,7 @@ class ImportCustomers if(!$account->isPaidHostedClient() && Client::where('company_id', $this->stripe->company_gateway->company_id)->count() > config('ninja.quotas.free.clients')) return; - nlog("search Stripe for {$customer->id}"); + // nlog("search Stripe for {$customer->id}"); $existing_customer_token = $this->stripe ->company_gateway @@ -97,7 +104,7 @@ class ImportCustomers return; } - nlog("inserting a customer"); + // nlog("inserting a customer"); //nlog($customer); $client = ClientFactory::create($this->stripe->company_gateway->company_id, $this->stripe->company_gateway->user_id); diff --git a/app/Utils/Helpers.php b/app/Utils/Helpers.php index f1b139efa..6f633612e 100644 --- a/app/Utils/Helpers.php +++ b/app/Utils/Helpers.php @@ -264,8 +264,6 @@ class Helpers $x = str_replace(["\n", "
"], ["\r", "
"], $value); - nlog($x); - return $x; }