diff --git a/app/Exceptions/NonExistingBackupFile.php b/app/Exceptions/NonExistingBackupFile.php new file mode 100644 index 000000000000..77c29936d6e6 --- /dev/null +++ b/app/Exceptions/NonExistingBackupFile.php @@ -0,0 +1,10 @@ +response($this->manager->createData($resource)->toArray()); } + protected function miniLoadResponse($query) + { + $user = auth()->user(); + + + $this->serializer = request()->input('serializer') ?: EntityTransformer::API_SERIALIZER_ARRAY; + + if ($this->serializer === EntityTransformer::API_SERIALIZER_JSON) { + $this->manager->setSerializer(new JsonApiSerializer()); + } else { + $this->manager->setSerializer(new ArraySerializer()); + } + + $transformer = new $this->entity_transformer($this->serializer); + $created_at = request()->has('created_at') ? request()->input('created_at') : 0; + + $created_at = date('Y-m-d H:i:s', $created_at); + + $query->with( + [ + 'company' => function ($query) use ($created_at, $user) { + $query->whereNotNull('created_at')->with('documents'); + }, + 'company.designs'=> function ($query) use ($created_at, $user) { + $query->where('created_at', '>=', $created_at)->with('company'); + + if(!$user->isAdmin()) + $query->where('designs.user_id', $user->id); + }, + 'company.documents'=> function ($query) use ($created_at, $user) { + $query->where('created_at', '>=', $created_at); + }, + 'company.groups' => function ($query) use ($created_at, $user) { + $query->where('created_at', '>=', $created_at); + + if(!$user->isAdmin()) + $query->where('group_settings.user_id', $user->id); + }, + 'company.payment_terms'=> function ($query) use ($created_at, $user) { + $query->where('created_at', '>=', $created_at); + + if(!$user->isAdmin()) + $query->where('payment_terms.user_id', $user->id); + + }, + 'company.tax_rates' => function ($query) use ($created_at, $user) { + $query->where('created_at', '>=', $created_at); + + if(!$user->isAdmin()) + $query->where('tax_rates.user_id', $user->id); + + }, + 'company.activities'=> function ($query) use($user) { + + if(!$user->isAdmin()) + $query->where('activities.user_id', $user->id); + + } + ] + ); + + if ($query instanceof Builder) { + $limit = request()->input('per_page', 20); + + $paginator = $query->paginate($limit); + $query = $paginator->getCollection(); + $resource = new Collection($query, $transformer, $this->entity_type); + $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); + } else { + $resource = new Collection($query, $transformer, $this->entity_type); + } + + return $this->response($this->manager->createData($resource)->toArray()); + + + } + protected function timeConstrainedResponse($query) { $user = auth()->user(); - if ($user->getCompany()->is_large) + if ($user->getCompany()->is_large){ $this->manager->parseIncludes($this->mini_load); + return $this->miniLoadResponse($query); + } else $this->manager->parseIncludes($this->first_load); diff --git a/app/Http/Controllers/ClientPortal/ContactHashLoginController.php b/app/Http/Controllers/ClientPortal/ContactHashLoginController.php index bffbf0ff6450..4a6a7470cf71 100644 --- a/app/Http/Controllers/ClientPortal/ContactHashLoginController.php +++ b/app/Http/Controllers/ClientPortal/ContactHashLoginController.php @@ -24,12 +24,12 @@ class ContactHashLoginController extends Controller */ public function login(string $contact_key) { - return redirect('/client/login'); + return redirect('/client/invoices'); } public function magicLink(string $magic_link) { - return redirect('/client/login'); + return redirect('/client/invoices'); } public function errorPage() diff --git a/app/Http/Middleware/ContactKeyLogin.php b/app/Http/Middleware/ContactKeyLogin.php index 9de9034bda2f..4ababf873a0b 100644 --- a/app/Http/Middleware/ContactKeyLogin.php +++ b/app/Http/Middleware/ContactKeyLogin.php @@ -42,7 +42,7 @@ class ContactKeyLogin if (MultiDB::findAndSetDbByContactKey($request->segment(3))) { if($client_contact = ClientContact::where('contact_key', $request->segment(3))->first()){ - auth()->guard('contact')->login($client_contact, true); + Auth::guard('contact')->login($client_contact, true); return redirect()->to('client/dashboard'); } diff --git a/app/Jobs/Company/CompanyExport.php b/app/Jobs/Company/CompanyExport.php index 935b088c401b..5a410a070c34 100644 --- a/app/Jobs/Company/CompanyExport.php +++ b/app/Jobs/Company/CompanyExport.php @@ -66,7 +66,7 @@ class CompanyExport implements ShouldQueue * * @return CompanyToken|null */ - public function handle() : void + public function handle() { MultiDB::setDb($this->company->db); @@ -149,7 +149,8 @@ class CompanyExport implements ShouldQueue $this->export_data['company_gateways'] = $this->company->company_gateways->map(function ($company_gateway){ $company_gateway = $this->transformArrayOfKeys($company_gateway, ['company_id', 'user_id']); - + $company_gateway->config = decrypt($company_gateway->config); + return $company_gateway; })->toArray(); @@ -405,7 +406,9 @@ class CompanyExport implements ShouldQueue //write to tmp and email to owner(); - $this->zipAndSend(); + $this->zipAndSend(); + + return true; } private function transformBasicEntities($model) diff --git a/app/Jobs/Company/CompanyImport.php b/app/Jobs/Company/CompanyImport.php new file mode 100644 index 000000000000..d40d7be0efec --- /dev/null +++ b/app/Jobs/Company/CompanyImport.php @@ -0,0 +1,127 @@ +company = $company; + $this->file_path = $file_path; + $this->options = $options; + } + + public function handle() + { + MultiDB::setDb($this->company->db); + + $this->company =Company::where('company_key', $this->company->company_key)->firstOrFail(); + + $this->unzipFile() + ->preFlightChecks(); + + } + + + //check if this is a complete company import OR if it is selective + /* + Company and settings only + Data + */ + + private function preFlightChecks() + { + //check the file version and perform any necessary adjustments to the file in order to proceed - needed when we change schema + // + $app_version = $this->backup_file->app_version; + + nlog($app_version); + + return $this; + } + + private function unzipFile() + { + $zip = new ZipArchive(); + $archive = $zip->open(public_path("storage/backups/{$this->file_path}")); + $filename = pathinfo($this->filepath, PATHINFO_FILENAME); + $zip->extractTo(public_path("storage/backups/{$filename}")); + $zip->close(); + $file_location = public_path("storage/backups/$filename/backup.json"); + + if (! file_exists($file_location)) { + throw new NonExistingMigrationFile('Backup file does not exist, or it is corrupted.'); + } + + $this->backup_file = json_decode(file_get_contents($file_location)); + + return $this; + } + + private function importCompany() + { + + //$this->import_company = .. + return $this; + } + + private function importData() + { + // $this->import_company = Company::where('company_key', $this->company->company_key)->firstOrFail(); + + return $this; + } +} \ No newline at end of file diff --git a/resources/views/email/admin/download_files.blade.php b/resources/views/email/admin/download_files.blade.php index d1f58b786d25..f1ca9d417c62 100644 --- a/resources/views/email/admin/download_files.blade.php +++ b/resources/views/email/admin/download_files.blade.php @@ -1,7 +1,7 @@ @component('email.template.master', ['design' => 'light', 'settings' =>$settings]) @slot('header') - @component('email.components.header', ['p' => '', 'logo' => $url]) + @component('email.components.header', ['p' => '', 'logo' => $logo]) @lang('texts.download') @endcomponent @@ -12,6 +12,11 @@ @lang('texts.download_timeframe') + + @component('email.components.button', ['url' => $url]) + @lang('texts.download') + @endcomponent + @slot('signature') InvoiceNinja (contact@invoiceninja.com) @endslot diff --git a/tests/Feature/Export/ExportCompanyTest.php b/tests/Feature/Export/ExportCompanyTest.php index c30e85417a0e..82bb57e9f318 100644 --- a/tests/Feature/Export/ExportCompanyTest.php +++ b/tests/Feature/Export/ExportCompanyTest.php @@ -44,8 +44,8 @@ class ExportCompanyTest extends TestCase public function testCompanyExport() { - CompanyExport::dispatchNow($this->company, $this->company->users->first()); + $res = CompanyExport::dispatchNow($this->company, $this->company->users->first()); - $this->assertTrue(true); + $this->assertTrue($res); } } diff --git a/tests/Feature/Import/ImportCompanyTest.php b/tests/Feature/Import/ImportCompanyTest.php new file mode 100644 index 000000000000..01b4e9f916c5 --- /dev/null +++ b/tests/Feature/Import/ImportCompanyTest.php @@ -0,0 +1,56 @@ +withoutMiddleware( + ThrottleRequests::class + ); + + + $this->withoutExceptionHandling(); + } + + public function testBackupJsonRead() + { + $backup_json_file = base_path().'/tests/Feature/Import/backup.json'; + + $this->assertTrue(is_array(json_decode(file_get_contents($backup_json_file),1))); + } + +} diff --git a/tests/Feature/Import/backup.json b/tests/Feature/Import/backup.json new file mode 100644 index 000000000000..a49c25849b4b --- /dev/null +++ b/tests/Feature/Import/backup.json @@ -0,0 +1 @@ +{"app_version":"5.1.62","activities":[{"user_id":"WJxbojagwO","company_id":"gl9avmeG1v","client_id":"VWPe9xdLyw","client_contact_id":"","account_id":"","project_id":"","vendor_id":"","payment_id":"","invoice_id":"VWPe9OYbLy","credit_id":"","invitation_id":"","task_id":"","expense_id":"","activity_type_id":5,"ip":"","is_system":false,"notes":"","created_at":1620971761,"updated_at":1620971761,"token_id":"","quote_id":"","subscription_id":"","recurring_invoice_id":""},{"user_id":"WJxbojagwO","company_id":"gl9avmeG1v","client_id":"VWPe9xdLyw","client_contact_id":"","account_id":"","project_id":"","vendor_id":"","payment_id":"","invoice_id":"VWPe9OYbLy","credit_id":"","invitation_id":"","task_id":"","expense_id":"","activity_type_id":4,"ip":"","is_system":false,"notes":"","created_at":1620971762,"updated_at":1620971762,"token_id":"","quote_id":"","subscription_id":"","recurring_invoice_id":""},{"user_id":"WJxbojagwO","company_id":"gl9avmeG1v","client_id":"VWPe9xdLyw","client_contact_id":"","account_id":"","project_id":"","vendor_id":"","payment_id":"","invoice_id":"QnXe0O3dxr","credit_id":"","invitation_id":"","task_id":"","expense_id":"","activity_type_id":5,"ip":"","is_system":false,"notes":"","created_at":1620971762,"updated_at":1620971762,"token_id":"","quote_id":"","subscription_id":"","recurring_invoice_id":""},{"user_id":"WJxbojagwO","company_id":"gl9avmeG1v","client_id":"VWPe9xdLyw","client_contact_id":"","account_id":"","project_id":"","vendor_id":"","payment_id":"","invoice_id":"QnXe0O3dxr","credit_id":"","invitation_id":"","task_id":"","expense_id":"","activity_type_id":4,"ip":"","is_system":false,"notes":"","created_at":1620971763,"updated_at":1620971763,"token_id":"","quote_id":"","subscription_id":"","recurring_invoice_id":""},{"user_id":"WJxbojagwO","company_id":"gl9avmeG1v","client_id":"VWPe9xdLyw","client_contact_id":"","account_id":"","project_id":"","vendor_id":"","payment_id":"","invoice_id":"Wjneg6DdwZ","credit_id":"","invitation_id":"","task_id":"","expense_id":"","activity_type_id":5,"ip":"","is_system":false,"notes":"","created_at":1620971763,"updated_at":1620971763,"token_id":"","quote_id":"","subscription_id":"","recurring_invoice_id":""},{"user_id":"WJxbojagwO","company_id":"gl9avmeG1v","client_id":"VWPe9xdLyw","client_contact_id":"","account_id":"","project_id":"","vendor_id":"","payment_id":"","invoice_id":"Wjneg6DdwZ","credit_id":"","invitation_id":"","task_id":"","expense_id":"","activity_type_id":4,"ip":"","is_system":false,"notes":"","created_at":1620971764,"updated_at":1620971764,"token_id":"","quote_id":"","subscription_id":"","recurring_invoice_id":""},{"user_id":"WJxbojagwO","company_id":"gl9avmeG1v","client_id":"VWPe9xdLyw","client_contact_id":"","account_id":"","project_id":"","vendor_id":"","payment_id":"","invoice_id":"VolejLBbjN","credit_id":"","invitation_id":"","task_id":"","expense_id":"","activity_type_id":5,"ip":"","is_system":false,"notes":"","created_at":1620971764,"updated_at":1620971764,"token_id":"","quote_id":"","subscription_id":"","recurring_invoice_id":""},{"user_id":"WJxbojagwO","company_id":"gl9avmeG1v","client_id":"VWPe9xdLyw","client_contact_id":"","account_id":"","project_id":"","vendor_id":"","payment_id":"","invoice_id":"VolejLBbjN","credit_id":"","invitation_id":"","task_id":"","expense_id":"","activity_type_id":4,"ip":"","is_system":false,"notes":"","created_at":1620971765,"updated_at":1620971765,"token_id":"","quote_id":"","subscription_id":"","recurring_invoice_id":""},{"user_id":"WJxbojagwO","company_id":"gl9avmeG1v","client_id":"VWPe9xdLyw","client_contact_id":"","account_id":"","project_id":"","vendor_id":"","payment_id":"","invoice_id":"WpmbkDKdzJ","credit_id":"","invitation_id":"","task_id":"","expense_id":"","activity_type_id":5,"ip":"","is_system":false,"notes":"","created_at":1620971765,"updated_at":1620971765,"token_id":"","quote_id":"","subscription_id":"","recurring_invoice_id":""},{"user_id":"WJxbojagwO","company_id":"gl9avmeG1v","client_id":"VWPe9xdLyw","client_contact_id":"","account_id":"","project_id":"","vendor_id":"","payment_id":"","invoice_id":"WpmbkDKdzJ","credit_id":"","invitation_id":"","task_id":"","expense_id":"","activity_type_id":4,"ip":"","is_system":false,"notes":"","created_at":1620971766,"updated_at":1620971766,"token_id":"","quote_id":"","subscription_id":"","recurring_invoice_id":""},{"user_id":"WJxbojagwO","company_id":"gl9avmeG1v","client_id":"","client_contact_id":"","account_id":"","project_id":"","vendor_id":"","payment_id":"","invoice_id":"","credit_id":"","invitation_id":"","task_id":"","expense_id":"","activity_type_id":49,"ip":"","is_system":false,"notes":"Noe Reynolds Beatrice Reichel Updated user Noe Reynolds Beatrice Reichel","created_at":1620971766,"updated_at":1620971766,"token_id":"","quote_id":"","subscription_id":"","recurring_invoice_id":""}],"backups":[{"id":68,"activity_id":"kQBeXqoayK","json_backup":"","html_backup":"\n