diff --git a/app/Console/Commands/MobileLocalization.php b/app/Console/Commands/MobileLocalization.php new file mode 100644 index 000000000000..7ae72f6cd1bc --- /dev/null +++ b/app/Console/Commands/MobileLocalization.php @@ -0,0 +1,120 @@ +option('type')); + + switch ($type) { + case 'laravel': + $this->laravelResources(); + break; + default: + $this->flutterResources(); + break; + } + } + + private function laravelResources() + { + $resources = $this->getResources(); + + foreach ($resources as $key => $val) { + $transKey = "texts.{$key}"; + if (trans($transKey) == $transKey) { + echo "'$key' => '$val',\n"; + } + } + } + + private function flutterResources() + { + $languages = cache('languages'); + $resources = $this->getResources(); + + foreach ($languages as $language) { + if ($language->locale == 'en') { + continue; + } + + echo "'{$language->locale}': {\n"; + + foreach ($resources as $key => $val) { + $text = trim(addslashes(trans("texts.{$key}", [], $language->locale))); + if (substr($text, 0, 6) == 'texts.') { + $text = $resources->$key; + } + + $text = str_replace(array('', ''), '', $text); + $text = str_replace(array('', ''), '', $text); + $text = str_replace(array('', ''), '', $text); + + echo "'$key': '$text',\n"; + } + + echo "},\n"; + } + } + + private function getResources() + { + $url = 'https://raw.githubusercontent.com/invoiceninja/flutter-client/develop/lib/utils/i18n.dart'; + $data = CurlUtils::get($url); + + $start = strpos($data, 'do not remove comment') + 25; + $end = strpos($data, '},', $start); + $data = substr($data, $start, $end - $start - 5); + + $data = str_replace("\n", "", $data); + $data = str_replace("\"", "\'", $data); + $data = str_replace("'", "\"", $data); + + return json_decode('{' . rtrim($data, ',') . '}'); + } + + protected function getOptions() + { + return [ + ['type', null, InputOption::VALUE_OPTIONAL, 'Type', null], + ]; + } + +} diff --git a/app/DataMapper/Analytics/Mail/EmailBounce.php b/app/DataMapper/Analytics/Mail/EmailBounce.php new file mode 100644 index 000000000000..bf86139a9dad --- /dev/null +++ b/app/DataMapper/Analytics/Mail/EmailBounce.php @@ -0,0 +1,77 @@ +string_metric5 = $string_metric5; + $this->string_metric6 = $string_metric6; + $this->string_metric7 = $string_metric7; + } +} diff --git a/app/DataMapper/Analytics/Mail/EmailSpam.php b/app/DataMapper/Analytics/Mail/EmailSpam.php new file mode 100644 index 000000000000..75b9dc26b53b --- /dev/null +++ b/app/DataMapper/Analytics/Mail/EmailSpam.php @@ -0,0 +1,77 @@ +string_metric5 = $string_metric5; + $this->string_metric6 = $string_metric6; + $this->string_metric7 = $string_metric7; + } +} diff --git a/app/Http/Controllers/MigrationController.php b/app/Http/Controllers/MigrationController.php index afe76b9c1c80..c554a657e821 100644 --- a/app/Http/Controllers/MigrationController.php +++ b/app/Http/Controllers/MigrationController.php @@ -18,6 +18,7 @@ use App\Jobs\Mail\NinjaMailerJob; use App\Jobs\Mail\NinjaMailerObject; use App\Jobs\Util\StartMigration; use App\Mail\ExistingMigration; +use App\Mail\Migration\MaxCompanies; use App\Models\Company; use App\Models\CompanyToken; use Illuminate\Foundation\Bus\DispatchesJobs; @@ -231,19 +232,51 @@ class MigrationController extends BaseController nlog($request->all()); } + try { + return response()->json([ + '_id' => Str::uuid(), + 'method' => config('queue.default'), + 'started_at' => now(), + ], 200); + + } finally { + // Controller logic here + foreach ($companies as $company) { $is_valid = $request->file($company->company_index)->isValid(); if (!$is_valid) { - // We might want to send user something's wrong with migration or nope? continue; } $user = auth()->user(); + $company_count = $user->account->companies()->count(); + // Look for possible existing company (based on company keys). $existing_company = Company::whereRaw('BINARY `company_key` = ?', [$company->company_key])->first(); + if(!$existing_company && $company_count >=10) { + + $nmo = new NinjaMailerObject; + $nmo->mailable = new MaxCompanies($user->account->companies()->first()); + $nmo->company = $user->account->companies()->first(); + $nmo->settings = $user->account->companies()->first()->settings; + $nmo->to_user = $user; + NinjaMailerJob::dispatch($nmo); + return; + } + elseif($existing_company && $company_count > 10 ){ + + $nmo = new NinjaMailerObject; + $nmo->mailable = new MaxCompanies($user->account->companies()->first()); + $nmo->company = $user->account->companies()->first(); + $nmo->settings = $user->account->companies()->first()->settings; + $nmo->to_user = $user; + NinjaMailerJob::dispatch($nmo); + return; + } + $checks = [ 'existing_company' => $existing_company ? (bool)1 : false, 'force' => property_exists($company, 'force') ? (bool) $company->force : false, @@ -254,11 +287,11 @@ class MigrationController extends BaseController nlog('Migrating: Existing company without force. (CASE_01)'); $nmo = new NinjaMailerObject; - $nmo->mailable = new ExistingMigration(); - $nmo->company = $existing_company; - $nmo->settings = $existing_company->settings; + $nmo->mailable = new ExistingMigration($existing_company); + $nmo->company = $user->account->companies()->first(); + $nmo->settings = $user->account->companies()->first(); $nmo->to_user = $user; - + NinjaMailerJob::dispatch($nmo); return response()->json([ @@ -355,10 +388,7 @@ class MigrationController extends BaseController // } } - return response()->json([ - '_id' => Str::uuid(), - 'method' => config('queue.default'), - 'started_at' => now(), - ], 200); + } + } } diff --git a/app/Http/Controllers/PostMarkController.php b/app/Http/Controllers/PostMarkController.php index e4f6a855d0af..7a98a59f1adc 100644 --- a/app/Http/Controllers/PostMarkController.php +++ b/app/Http/Controllers/PostMarkController.php @@ -11,6 +11,8 @@ namespace App\Http\Controllers; +use App\DataMapper\Analytics\EmailBounce; +use App\DataMapper\Analytics\EmailSpam; use App\Jobs\Util\SystemLogger; use App\Libraries\MultiDB; use App\Models\CreditInvitation; @@ -19,6 +21,7 @@ use App\Models\QuoteInvitation; use App\Models\RecurringInvoiceInvitation; use App\Models\SystemLog; use Illuminate\Http\Request; +use Turbo124\Beacon\Facades\LightLogs; /** * Class PostMarkController. @@ -71,8 +74,7 @@ class PostMarkController extends BaseController if($request->header('X-API-SECURITY') && $request->header('X-API-SECURITY') == config('postmark.secret')) { - - nlog($request->all()); + // nlog($request->all()); MultiDB::findAndSetDbByCompanyKey($request->input('Tag')); @@ -157,6 +159,14 @@ class PostMarkController extends BaseController $this->invitation->email_status = 'bounced'; $this->invitation->save(); + $bounce = new EmailBounce( + $request->input('Tag'), + $request->input('From'), + $request->input('MessageID') + ); + + LightLogs::create($bounce)->batch(); + SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_BOUNCED, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client); } @@ -191,6 +201,14 @@ class PostMarkController extends BaseController $this->invitation->email_status = 'spam'; $this->invitation->save(); + $spam = new EmailSpam( + $request->input('Tag'), + $request->input('From'), + $request->input('MessageID') + ); + + LightLogs::create($bounce)->batch(); + SystemLogger::dispatch($request->all(), SystemLog::CATEGORY_MAIL, SystemLog::EVENT_MAIL_SPAM_COMPLAINT, SystemLog::TYPE_WEBHOOK_RESPONSE, $this->invitation->contact->client); } diff --git a/app/Mail/ExistingMigration.php b/app/Mail/ExistingMigration.php index 60f7abdf95d4..366a4c146af5 100644 --- a/app/Mail/ExistingMigration.php +++ b/app/Mail/ExistingMigration.php @@ -10,14 +10,22 @@ class ExistingMigration extends Mailable { // use Queueable, SerializesModels; + public $company; + + public $settings; + + public $logo; + + public $company_name; + /** * Create a new message instance. * * @return void */ - public function __construct() + public function __construct($company) { - // + $this->company = $company; } /** @@ -27,8 +35,11 @@ class ExistingMigration extends Mailable */ public function build() { - return $this->from(config('mail.from.address'), config('mail.from.name')) + $this->settings = $this->company->settings; + $this->logo = $this->company->present()->logo(); + $this->company_name = $this->company->present()->name(); + return $this->from(config('mail.from.address'), config('mail.from.name')) ->view('email.migration.existing'); } } diff --git a/app/Mail/Migration/MaxCompanies.php b/app/Mail/Migration/MaxCompanies.php new file mode 100644 index 000000000000..af65f766b95e --- /dev/null +++ b/app/Mail/Migration/MaxCompanies.php @@ -0,0 +1,51 @@ +company = $company; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + $this->settings = $this->company->settings; + $this->logo = $this->company->present()->logo(); + $this->title = ctrans('texts.max_companies'); + $this->message = ctrans('texts.max_companies_desc'); + $this->whitelabel = $this->company->account->isPaid(); + + return $this->from(config('mail.from.address'), config('mail.from.name')) + ->view('email.migration.max_companies'); + } +} diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 6495fae14aaf..063153d36b63 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -225,7 +225,7 @@ class CompanyGateway extends BaseModel { $config = $this->getConfig(); - if ($this->gateway->provider == 'Stripe' && strpos($config->publishableKey, 'test')) { + if ($this->gateway->provider == 'Stripe' && property_exists($config, 'publishableKey') && strpos($config->publishableKey, 'test')) { return true; } diff --git a/app/Repositories/Migration/InvoiceMigrationRepository.php b/app/Repositories/Migration/InvoiceMigrationRepository.php index 14d56965d29f..ee142650f2ba 100644 --- a/app/Repositories/Migration/InvoiceMigrationRepository.php +++ b/app/Repositories/Migration/InvoiceMigrationRepository.php @@ -107,35 +107,7 @@ class InvoiceMigrationRepository extends BaseRepository InvoiceInvitation::reguard(); RecurringInvoiceInvitation::reguard(); - /* - if (isset($data['invitations'])) { - $invitations = collect($data['invitations']); - $model->invitations->pluck('key')->diff($invitations->pluck('key'))->each(function ($invitation) use ($resource) { - $this->getInvitation($invitation, $resource)->delete(); - }); - - foreach ($data['invitations'] as $invitation) { - - //if no invitations are present - create one. - if (! $this->getInvitation($invitation, $resource)) { - if (isset($invitation['id'])) { - unset($invitation['id']); - } - - //make sure we are creating an invite for a contact who belongs to the client only! - $contact = ClientContact::find($invitation['client_contact_id']); - - if ($contact && $model->client_id == $contact->client_id) { - $new_invitation = $invitation_factory_class::create($model->company_id, $model->user_id); - $new_invitation->{$lcfirst_resource_id} = $model->id; - $new_invitation->client_contact_id = $contact->id; - $new_invitation->save(); - } - } - } - } - */ $model->load('invitations'); /* If no invitations have been created, this is our fail safe to maintain state*/ @@ -152,8 +124,6 @@ class InvoiceMigrationRepository extends BaseRepository if ($class->name == Invoice::class || $class->name == RecurringInvoice::class) { if (($state['finished_amount'] != $state['starting_amount']) && ($model->status_id != Invoice::STATUS_DRAFT)) { - // $model->ledger()->updateInvoiceBalance(($state['finished_amount'] - $state['starting_amount'])); - // $model->client->service()->updateBalance(($state['finished_amount'] - $state['starting_amount']))->save(); } if (! $model->design_id) { @@ -162,7 +132,7 @@ class InvoiceMigrationRepository extends BaseRepository if ($model->company->update_products) { - UpdateOrCreateProduct::dispatchNow($model->line_items, $model, $model->company); + //UpdateOrCreateProduct::dispatchNow($model->line_items, $model, $model->company); } } diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index 2619e20330fc..1bebb2cc44cf 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -4235,6 +4235,11 @@ $LANG = array( 'notification_quote_created_subject' => 'Quote :invoice was created for :client', 'notification_credit_created_subject' => 'Credit :invoice was created to :client', 'notification_credit_created_subject' => 'Credit :invoice was created for :client', + 'max_companies' => 'Maximum companies migrated', + 'max_companies_desc' => 'You have reached your maximum number of companies. Delete existing companies to migrate new ones.', + 'migration_already_completed' => 'Company already migrated', + 'migration_already_completed_desc' => 'Looks like you already migrated :company_name to the V5 version of the Invoice Ninja. In case you want to start over, you can force migrate to wipe existing data.', + ); return $LANG; diff --git a/resources/views/email/migration/existing.blade.php b/resources/views/email/migration/existing.blade.php index 1491488b17d6..3a4c1dbd6ca1 100644 --- a/resources/views/email/migration/existing.blade.php +++ b/resources/views/email/migration/existing.blade.php @@ -1,31 +1,18 @@ @component('email.template.master', ['design' => 'light', 'settings' => $settings]) -@slot('header') - @component('email.components.header') - Migration already completed - @endcomponent -@endslot + @slot('header') + @include('email.components.header', ['logo' => $logo]) + @endslot -@slot('greeting') - Hello, -@endslot +
{{ctrans('texts.migration_already_completed_desc', ['company_name' => $company_name])}}
+ @if(isset($whitelabel) && !$whitelabel) + @slot('footer') + @component('email.components.footer', ['url' => 'https://invoiceninja.com', 'url_text' => '© InvoiceNinja']) + For any info, please visit InvoiceNinja. + @endcomponent + @endslot + @endif @endcomponent diff --git a/resources/views/email/migration/max_companies.blade.php b/resources/views/email/migration/max_companies.blade.php new file mode 100644 index 000000000000..4d76f055b962 --- /dev/null +++ b/resources/views/email/migration/max_companies.blade.php @@ -0,0 +1,18 @@ +@component('email.template.master', ['design' => 'light', 'settings' => $settings]) + + @slot('header') + @include('email.components.header', ['logo' => $logo]) + @endslot + +{{ctrans('texts.max_companies_desc')}}
+ + @if(isset($whitelabel) && !$whitelabel) + @slot('footer') + @component('email.components.footer', ['url' => 'https://invoiceninja.com', 'url_text' => '© InvoiceNinja']) + For any info, please visit InvoiceNinja. + @endcomponent + @endslot + @endif +@endcomponent