diff --git a/VERSION.txt b/VERSION.txt index e774ed209d..0e6854f58e 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -5.8.55 \ No newline at end of file +5.8.56 \ No newline at end of file diff --git a/app/Http/Controllers/CompanyController.php b/app/Http/Controllers/CompanyController.php index 5c5f15a060..032ade89cf 100644 --- a/app/Http/Controllers/CompanyController.php +++ b/app/Http/Controllers/CompanyController.php @@ -705,8 +705,25 @@ class CompanyController extends BaseController $logo = strlen($company->settings->company_logo) > 5 ? $company->settings->company_logo : 'https://pdf.invoicing.co/favicon-v2.png'; $headers = ['Content-Disposition' => 'inline']; + try{ + $response = \Illuminate\Support\Facades\Http::get($logo); + + if ($response->successful()) { + $logo = $response->body(); + } + else { + $logo = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='); + } + + } + catch(\Exception $e){ + + $logo = base64_decode('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='); + + } + return response()->streamDownload(function () use ($logo) { - echo @file_get_contents($logo); + echo $logo; }, 'logo.png', $headers); } diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 20479e552d..6b76ce25c3 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -121,7 +121,8 @@ class DocumentController extends BaseController } return response()->streamDownload(function () use ($document) { - echo file_get_contents($document->generateUrl()); + // echo file_get_contents($document->generateUrl()); + echo $document->getFile(); }, basename($document->generateUrl()), $headers); } diff --git a/app/Http/Requests/Company/UpdateCompanyRequest.php b/app/Http/Requests/Company/UpdateCompanyRequest.php index da5395e941..e53dade24d 100644 --- a/app/Http/Requests/Company/UpdateCompanyRequest.php +++ b/app/Http/Requests/Company/UpdateCompanyRequest.php @@ -137,9 +137,12 @@ class UpdateCompanyRequest extends Request } if (isset($settings['email_style_custom'])) { - $settings['email_style_custom'] = str_replace(['{!!','!!}','{{','}}','@if(','@endif','@isset','@unless','@auth','@empty','@guest','@env','@section','@switch', '@foreach', '@while', '@include', '@each', '@once', '@push', '@use', '@forelse', '@verbatim', ' 2) + $settings['company_logo'] = $this->forceScheme($settings['company_logo']); + if (! $account->isFreeHostedClient()) { return $settings; } @@ -164,4 +167,9 @@ class UpdateCompanyRequest extends Request return rtrim($url, '/'); } + + private function forceScheme($url){ + return stripos($url, 'http') !== false ? $url : "https://{$url}"; + } + } diff --git a/app/Jobs/Company/CompanyImport.php b/app/Jobs/Company/CompanyImport.php index e1249a6727..eb288bcf31 100644 --- a/app/Jobs/Company/CompanyImport.php +++ b/app/Jobs/Company/CompanyImport.php @@ -215,6 +215,14 @@ class CompanyImport implements ShouldQueue "convert_rate_to_client", ]; + private array $protected_input = [ + 'client_portal_privacy_policy', + 'client_portal_terms', + 'portal_custom_footer', + 'portal_custom_css', + 'portal_custom_head' + ]; + private array $version_keys = [ 'baseline' => [], '5.7.35' => [ @@ -475,9 +483,16 @@ class CompanyImport implements ShouldQueue $settings->payment_number_counter = 1; $settings->project_number_counter = 1; $settings->purchase_order_number_counter = 1; - $this->company->settings = $co->settings; - $this->company->saveSettings($co->settings, $this->company); + $settings->email_style_custom = str_replace(['{!!','!!}','{{','}}','@dd', '@dump', '@if', '@if(','@endif','@isset','@unless','@auth','@empty','@guest','@env','@section','@switch', '@foreach', '@while', '@include', '@each', '@once', '@push', '@use', '@forelse', '@verbatim', 'email_style_custom); + $settings->company_logo = (strlen($settings->company_logo) > 2 && stripos($settings->company_logo, 'http') !== false) ? $settings->company_logo : "https://{$settings->company_logo}"; + + foreach($this->protected_input as $protected_var) + { + $settings->{$protected_var} = str_replace("script", "", $settings->{$protected_var}); + } + + $this->company->saveSettings($settings, $this->company); $this->company->save(); diff --git a/app/Jobs/Invoice/CreateUbl.php b/app/Jobs/Invoice/CreateUbl.php index 5787fda50c..fcf4509929 100644 --- a/app/Jobs/Invoice/CreateUbl.php +++ b/app/Jobs/Invoice/CreateUbl.php @@ -103,7 +103,7 @@ class CreateUbl implements ShouldQueue $ubl_invoice->setLegalMonetaryTotal((new LegalMonetaryTotal()) //->setLineExtensionAmount() - ->setTaxInclusiveAmount($taxtotal->getTaxAmount()) + ->setTaxInclusiveAmount($invoice->balance) ->setTaxExclusiveAmount($taxable) ->setPayableAmount($invoice->balance)); diff --git a/app/Models/Presenters/CompanyPresenter.php b/app/Models/Presenters/CompanyPresenter.php index 1a86bc81d5..085fc2806d 100644 --- a/app/Models/Presenters/CompanyPresenter.php +++ b/app/Models/Presenters/CompanyPresenter.php @@ -88,7 +88,6 @@ class CompanyPresenter extends EntityPresenter return "data:image/png;base64, ". base64_encode(@file_get_contents(url('') . $settings->company_logo, false, stream_context_create($context_options))); } else { return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="; - //return "data:image/png;base64, ". base64_encode(@file_get_contents(asset('images/new_logo.png'), false, stream_context_create($context_options))); } } diff --git a/app/Repositories/ClientRepository.php b/app/Repositories/ClientRepository.php index 80b12deaa5..f9c25a2e3a 100644 --- a/app/Repositories/ClientRepository.php +++ b/app/Repositories/ClientRepository.php @@ -144,6 +144,9 @@ class ClientRepository extends BaseRepository public function purge($client) { + + nlog("Purging client id => {$client->id}"); + $client->contacts()->forceDelete(); $client->tasks()->forceDelete(); $client->invoices()->forceDelete(); diff --git a/app/Services/Template/TemplateMock.php b/app/Services/Template/TemplateMock.php index 572b64e7e7..7bf3b9fd7f 100644 --- a/app/Services/Template/TemplateMock.php +++ b/app/Services/Template/TemplateMock.php @@ -56,9 +56,6 @@ class TemplateMock $this->engines['payments'] = json_decode($this->payment_data, true); $this->engines['purchase_orders'] = json_decode($this->purchase_order_data, true); - // nlog("engines"); - // nlog($this->engines); - return $this; } diff --git a/app/Services/Template/TemplateService.php b/app/Services/Template/TemplateService.php index 2d55d8ad96..429a565cab 100644 --- a/app/Services/Template/TemplateService.php +++ b/app/Services/Template/TemplateService.php @@ -94,6 +94,7 @@ class TemplateService $this->twig = new \Twig\Environment($loader, [ 'debug' => true, ]); + $string_extension = new \Twig\Extension\StringLoaderExtension(); $this->twig->addExtension($string_extension); $this->twig->addExtension(new IntlExtension()); @@ -121,6 +122,26 @@ class TemplateService $this->twig->addFilter($filter); + $filter = new \Twig\TwigFilter('filter', function ($array, $arrow){ + + if(is_string($arrow) && in_array($arrow, ['popen','exec','shell_exec','system','passthru','proc_open','pcntl_exec','sleep','escapeshellcmd','escapeshellarg'])) + throw new RuntimeError("Attempt to access command line"); + + if (!is_iterable($array)) { + throw new RuntimeError(sprintf('The "filter" filter expects an array or "Traversable", got "%s".', \is_object($array) ? \get_class($array) : \gettype($array))); + } + + if (\is_array($array)) { + return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH); + } + + // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator + return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow); + + }); + + $this->twig->addFilter($filter); + return $this; } @@ -240,7 +261,7 @@ class TemplateService */ public function getPdf(): string { - + if (config('ninja.invoiceninja_hosted_pdf_generation') || config('ninja.pdf_generator') == 'hosted_ninja') { $pdf = (new NinjaPdf())->build($this->compiled_html); } else { @@ -271,7 +292,7 @@ class TemplateService { $this->data = $this->preProcessDataBlocks($data); - // nlog(json_encode($this->data)); + return $this; } @@ -1211,12 +1232,8 @@ class TemplateService }); })->toArray(); - // nlog($company_details); - $company_details = $include_labels ? $this->labelledFieldStack($company_details, 'company_details-') : $company_details; - // nlog($company_details); - $this->updateElementProperties('company-details', $company_details); return $this; diff --git a/app/Utils/Traits/MakesInvoiceHtml.php b/app/Utils/Traits/MakesInvoiceHtml.php index 63fc06e734..33868b0a3d 100644 --- a/app/Utils/Traits/MakesInvoiceHtml.php +++ b/app/Utils/Traits/MakesInvoiceHtml.php @@ -44,29 +44,6 @@ trait MakesInvoiceHtml return Blade::render($string, $data); //potential fix for removing eval() - // $php = Blade::compileString($string); - - // $obLevel = ob_get_level(); - // ob_start(); - // extract($data, EXTR_SKIP); - - // try { - // eval('?'.'>'.$php); - // } catch (Exception $e) { - // while (ob_get_level() > $obLevel) { - // ob_end_clean(); - // } - - // throw $e; - // } catch (Throwable $e) { - // while (ob_get_level() > $obLevel) { - // ob_end_clean(); - // } - - // throw new \Exception($e->getMessage()); - // } - - // return ob_get_clean(); } /* diff --git a/config/ninja.php b/config/ninja.php index 5e2a012c57..94445f5394 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -17,8 +17,8 @@ return [ 'require_https' => env('REQUIRE_HTTPS', true), 'app_url' => rtrim(env('APP_URL', ''), '/'), 'app_domain' => env('APP_DOMAIN', 'invoicing.co'), - 'app_version' => env('APP_VERSION', '5.8.55'), - 'app_tag' => env('APP_TAG', '5.8.55'), + 'app_version' => env('APP_VERSION', '5.8.56'), + 'app_tag' => env('APP_TAG', '5.8.56'), 'minimum_client_version' => '5.0.16', 'terms_version' => '1.0.1', 'api_secret' => env('API_SECRET', false), diff --git a/lang/en/texts.php b/lang/en/texts.php index ffc9c157cc..63654d4849 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5304,6 +5304,22 @@ $lang = array( 'always_show_required_fields_help' => 'Displays the required fields form always at checkout', 'advanced_cards' => 'Advanced Cards', 'activity_140' => 'Statement sent to :client', + 'invoice_net_amount' => 'Invoice Net Amount', + 'round_to_minutes' => 'Round To Minutes', + '1_minute' => '1 Minute', + '5_minutes' => '5 Minutes', + '15_minutes' => '15 Minutes', + '30_minutes' => '30 Minutes', + '1_hour' => '1 Hour', + '1_day' => '1 Day', + 'round_tasks' => 'Round Tasks', + 'round_tasks_help' => 'Round time intervals when saving tasks', + 'direction' => 'Direction', + 'round_up' => 'Round Up', + 'round_down' => 'Round Down', + 'task_round_to_nearest' => 'Round To Nearest', + 'bulk_updated' => 'Successfully updated data', + 'bulk_update' => 'Bulk Update', ); return $lang;