diff --git a/app/Console/HostedMigrations.php b/app/Console/HostedMigrations.php new file mode 100644 index 0000000000..0c31313137 --- /dev/null +++ b/app/Console/HostedMigrations.php @@ -0,0 +1,131 @@ +buildCache(); + + if(!MultiDB::userFindAndSetDb($this->option('email'))){ + $this->info("Could not find a user with that email address"); + return; + } + + $user = User::where('email', $this->option('email'))->first(); + + if(!$user){ + $this->info("There was a problem getting the user, did you set the right DB?"); + return; + } + + $path = public_path('storage/migrations/import'); + + nlog(public_path('storage/migrations/import')); + + $directory = new DirectoryIterator($path); + + foreach ($directory as $file) { + if ($file->getExtension() === 'zip') { + + $company = $user->companies()->first(); + + $this->info('Started processing: '.$file->getBasename().' at '.now()); + + $zip = new ZipArchive(); + $archive = $zip->open($file->getRealPath()); + + try { + if (! $archive) { + throw new ProcessingMigrationArchiveFailed('Processing migration archive failed. Migration file is possibly corrupted.'); + } + + $filename = pathinfo($file->getRealPath(), PATHINFO_FILENAME); + + $zip->extractTo(public_path("storage/migrations/{$filename}")); + $zip->close(); + + $import_file = public_path("storage/migrations/$filename/migration.json"); + + Import::dispatch($import_file, $user->companies()->first(), $user); + + unlink(public_path("storage/migrations/$filename/migration.json")); + unlink($file->getRealPath()); + + } catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) { + \Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage())); + + if (app()->environment() !== 'production') { + info($e->getMessage()); + } + } + } + } + } + +} diff --git a/app/Jobs/Util/WebhookHandler.php b/app/Jobs/Util/WebhookHandler.php index 222ad861b8..fc656ff8eb 100644 --- a/app/Jobs/Util/WebhookHandler.php +++ b/app/Jobs/Util/WebhookHandler.php @@ -36,7 +36,7 @@ class WebhookHandler implements ShouldQueue private $company; - public $tries = 3; //number of retries + public $tries = 1; //number of retries public $backoff = 10; //seconds to wait until retry diff --git a/app/Models/Presenters/CompanyPresenter.php b/app/Models/Presenters/CompanyPresenter.php index 1e19ecc1ce..27194763fb 100644 --- a/app/Models/Presenters/CompanyPresenter.php +++ b/app/Models/Presenters/CompanyPresenter.php @@ -54,7 +54,7 @@ class CompanyPresenter extends EntityPresenter $settings = $this->entity->settings; } - if(config('ninja.is_docker')) + if(config('ninja.is_docker') || config('ninja.local_download')) return $this->logo($settings); $context_options =array( diff --git a/app/Transformers/ActivityTransformer.php b/app/Transformers/ActivityTransformer.php index a284f04898..bd761bcfd1 100644 --- a/app/Transformers/ActivityTransformer.php +++ b/app/Transformers/ActivityTransformer.php @@ -12,6 +12,7 @@ namespace App\Transformers; use App\Models\Activity; +use App\Models\Backup; use App\Utils\Traits\MakesHash; class ActivityTransformer extends EntityTransformer @@ -23,7 +24,9 @@ class ActivityTransformer extends EntityTransformer /** * @var array */ - protected $availableIncludes = []; + protected $availableIncludes = [ + 'history' + ]; /** * @param Activity $activity @@ -55,4 +58,11 @@ class ActivityTransformer extends EntityTransformer ]; } + + public function includeHistory(Activity $activity) + { + $transformer = new ActivityTransformer($this->serializer); + + return $this->includeItem($activity->backup, $transformer, Backup::class); + } } diff --git a/app/Transformers/InvoiceHistoryTransformer.php b/app/Transformers/InvoiceHistoryTransformer.php index b650c58c3a..2c83cc76b3 100644 --- a/app/Transformers/InvoiceHistoryTransformer.php +++ b/app/Transformers/InvoiceHistoryTransformer.php @@ -20,10 +20,12 @@ class InvoiceHistoryTransformer extends EntityTransformer use MakesHash; protected $defaultIncludes = [ - 'activity', + // 'activity', ]; - protected $availableIncludes = []; + protected $availableIncludes = [ + 'activity', + ]; public function transform(Backup $backup) { diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index 0f6b81bf3b..0914431dc4 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -132,6 +132,7 @@ class HtmlEngine $data['$view_link'] = ['value' => ''.ctrans('texts.view_invoice').'', 'label' => ctrans('texts.view_invoice')]; $data['$viewLink'] = &$data['$view_link']; $data['$viewButton'] = &$data['$view_link']; + $data['$paymentButton'] = &$data['$view_link']; $data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_invoice')]; $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->entity->client->date_format(), $this->entity->client->locale()) ?: ' ', 'label' => ctrans('texts.invoice_date')]; @@ -148,6 +149,8 @@ class HtmlEngine $data['$terms'] = &$data['$entity.terms']; $data['$view_link'] = ['value' => ''.ctrans('texts.view_quote').'', 'label' => ctrans('texts.view_quote')]; $data['$viewLink'] = &$data['$view_link']; + $data['$viewButton'] = &$data['$view_link']; + $data['$approveButton'] = ['value' => ''.ctrans('texts.view_quote').'', 'label' => ctrans('texts.approve')]; $data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_quote')]; $data['$date'] = ['value' => $this->translateDate($this->entity->date, $this->entity->client->date_format(), $this->entity->client->locale()) ?: ' ', 'label' => ctrans('texts.quote_date')]; } @@ -159,6 +162,7 @@ class HtmlEngine $data['$entity.terms'] = ['value' => $this->entity->terms ?: '', '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']; $data['$viewLink'] = &$data['$view_link']; $data['$view_url'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_credit')]; // $data['$view_link'] = ['value' => $this->invitation->getLink(), 'label' => ctrans('texts.view_credit')]; diff --git a/config/ninja.php b/config/ninja.php index 8db819f73e..c8d0356d3e 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -36,6 +36,7 @@ return [ 'phantomjs_pdf_generation' => env('PHANTOMJS_PDF_GENERATION', true), 'trusted_proxies' => env('TRUSTED_PROXIES', false), 'is_docker' => env('IS_DOCKER', false), + 'local_download' => env('LOCAL_DOWNLOAD', false), 'sentry_dsn' => env('SENTRY_LARAVEL_DSN', 'https://9b4e15e575214354a7d666489783904a@sentry.invoicing.co/6'), 'environment' => env('NINJA_ENVIRONMENT', 'selfhost'), // 'hosted', 'development', 'selfhost', 'reseller' 'preconfigured_install' => env('PRECONFIGURED_INSTALL',false), diff --git a/config/querydetector.php b/config/querydetector.php new file mode 100644 index 0000000000..5f8207ef22 --- /dev/null +++ b/config/querydetector.php @@ -0,0 +1,70 @@ + env('QUERY_DETECTOR_ENABLED', false), + + /* + * Threshold level for the N+1 query detection. If a relation query will be + * executed more then this amount, the detector will notify you about it. + */ + 'threshold' => (int) env('QUERY_DETECTOR_THRESHOLD', 1), + + /* + * Here you can whitelist model relations. + * + * Right now, you need to define the model relation both as the class name and the attribute name on the model. + * So if an "Author" model would have a "posts" relation that points to a "Post" class, you need to add both + * the "posts" attribute and the "Post::class", since the relation can get resolved in multiple ways. + */ + 'except' => [ + //Author::class => [ + // Post::class, + // 'posts', + //] + ], + + /* + * Here you can set a specific log channel to write to + * in case you are trying to isolate queries or have a lot + * going on in the laravel.log. Defaults to laravel.log though. + */ + 'log_channel' => env('QUERY_DETECTOR_LOG_CHANNEL', 'daily'), + + /* + * Define the output format that you want to use. Multiple classes are supported. + * Available options are: + * + * Alert: + * Displays an alert on the website + * \BeyondCode\QueryDetector\Outputs\Alert::class + * + * Console: + * Writes the N+1 queries into your browsers console log + * \BeyondCode\QueryDetector\Outputs\Console::class + * + * Clockwork: (make sure you have the itsgoingd/clockwork package installed) + * Writes the N+1 queries warnings to Clockwork log + * \BeyondCode\QueryDetector\Outputs\Clockwork::class + * + * Debugbar: (make sure you have the barryvdh/laravel-debugbar package installed) + * Writes the N+1 queries into a custom messages collector of Debugbar + * \BeyondCode\QueryDetector\Outputs\Debugbar::class + * + * JSON: + * Writes the N+1 queries into the response body of your JSON responses + * \BeyondCode\QueryDetector\Outputs\Json::class + * + * Log: + * Writes the N+1 queries into the Laravel.log file + * \BeyondCode\QueryDetector\Outputs\Log::class + */ + 'output' => [ + //\BeyondCode\QueryDetector\Outputs\Alert::class, + \BeyondCode\QueryDetector\Outputs\Log::class, + //\BeyondCode\QueryDetector\Outputs\Json::class, + ] +];