diff --git a/app/Http/Controllers/Support/Messages/SendingController.php b/app/Http/Controllers/Support/Messages/SendingController.php new file mode 100644 index 000000000000..a321470df53a --- /dev/null +++ b/app/Http/Controllers/Support/Messages/SendingController.php @@ -0,0 +1,26 @@ +validate([ + 'message' => ['required'], + ]); + + Mail::to(config('ninja.contact.ninja_official_contact')) + ->send(new SupportMessageSent($request->message)); + + return response()->json([ + 'success' => true + ]); + } + +} diff --git a/app/Mail/SupportMessageSent.php b/app/Mail/SupportMessageSent.php new file mode 100644 index 000000000000..b4b4b89d3da7 --- /dev/null +++ b/app/Mail/SupportMessageSent.php @@ -0,0 +1,59 @@ +message = $message; + + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + $system_info = null; + $log_lines = []; + + /** + * With self-hosted version of Ninja, + * we are going to bundle system-level info + * and last 10 lines of laravel.log file. + */ + if(Ninja::isSelfHost()) { + $system_info = Ninja::getDebugInfo(); + + $log_file = new \SplFileObject(sprintf('%s/laravel.log', base_path('storage/logs'))); + + $log_file->seek(PHP_INT_MAX); + $last_line = $log_file->key(); + $lines = new \LimitIterator($log_file, $last_line - 10, $last_line); + + $log_lines = iterator_to_array($lines); + } + + return $this->from(config('mail.from.address')) + ->subject(ctrans('texts.new_support_message')) + ->markdown('email.support.message', [ + 'message' => $this->message, + 'system_info' => $system_info, + 'laravel_log' => $log_lines + ]); + } +} + diff --git a/app/Utils/Ninja.php b/app/Utils/Ninja.php index 57ddc3e3829e..83c2bac7971e 100644 --- a/app/Utils/Ninja.php +++ b/app/Utils/Ninja.php @@ -11,18 +11,33 @@ namespace App\Utils; +use Illuminate\Support\Facades\DB; + /** * Class Ninja. */ class Ninja { - public static function isSelfHost() - { - return config('ninja.environment') === 'selfhost'; - } + public static function isSelfHost() + { + return config('ninja.environment') === 'selfhost'; + } - public static function isHosted() - { - return config('ninja.environment') === 'hosted'; - } + public static function isHosted() + { + return config('ninja.environment') === 'hosted'; + } + + public static function getDebugInfo() + { + $mysql_version = DB::select(DB::raw("select version() as version"))[0]->version; + + $info = "App Version: v" . config('ninja.app_version') . "\\n" . + "White Label: " . "\\n" . // TODO: Implement white label with hasFeature. + "Server OS: " . php_uname('s') . ' ' . php_uname('r') . "\\n" . + "PHP Version: " . phpversion() . "\\n" . + "MySQL Version: " . $mysql_version; + + return $info; + } } diff --git a/config/ninja.php b/config/ninja.php index 811bbf174cee..b4c51725b9c3 100644 --- a/config/ninja.php +++ b/config/ninja.php @@ -64,6 +64,7 @@ return [ 'contact' => [ 'email' => env('MAIL_FROM_ADDRESS'), 'from_name' => env('MAIL_FROM_NAME'), + 'ninja_official_contact' => env('NINJA_OFFICIAL_CONTACT', 'contact@invoiceninja.com'), ], 'cached_tables' => [ 'banks' => 'App\Models\Bank', diff --git a/resources/views/email/support/message.blade.php b/resources/views/email/support/message.blade.php new file mode 100644 index 000000000000..e4bf02b2a6c1 --- /dev/null +++ b/resources/views/email/support/message.blade.php @@ -0,0 +1,39 @@ +@component('mail::layout') + +{{-- Header --}} +@slot('header') +@component('mail::header', ['url' => config('app.url')]) +Header Title +@endcomponent +@endslot + +{{-- Body --}} +{{ $message }} + +{!! str_replace('\n', '
', $system_info) !!} + +
+ {{ ctrans('texts.display_log') }} + @foreach($laravel_log as $log_line) + {{ $log_line }}
+ @endforeach +
+ +{{-- Subcopy --}} +@isset($subcopy) +@slot('subcopy') +@component('mail::subcopy') +{{ $subcopy }} +@endcomponent +@endslot +@endisset + + +{{-- Footer --}} +@slot('footer') +@component('mail::footer') +© {{ date('Y') }} {{ config('ninja.app_name') }}. +@endcomponent +@endslot + +@endcomponent diff --git a/routes/api.php b/routes/api.php index 32af9eb49574..cc642c25feed 100644 --- a/routes/api.php +++ b/routes/api.php @@ -22,20 +22,20 @@ Route::group(['middleware' => ['api_secret_check']], function () { Route::post('api/v1/signup', 'AccountController@store')->name('signup.submit'); Route::post('api/v1/oauth_login', 'Auth\LoginController@oauthApiLogin'); - + }); Route::group(['api_secret_check','domain_db'], function () { Route::post('api/v1/login', 'Auth\LoginController@apiLogin')->name('login.submit'); Route::post('api/v1/reset_password', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.reset'); - + }); Route::group(['middleware' => ['api_db','api_secret_check','token_auth'], 'prefix' =>'api/v1', 'as' => 'api.'], function () { Route::resource('activities', 'ActivityController'); // name = (clients. index / create / show / update / destroy / edit - + Route::resource('clients', 'ClientController'); // name = (clients. index / create / show / update / destroy / edit Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk'); @@ -65,7 +65,7 @@ Route::group(['middleware' => ['api_db','api_secret_check','token_auth'], 'prefi Route::resource('client_statement', 'ClientStatementController@statement'); // name = (client_statement. index / create / show / update / destroy / edit Route::resource('payments', 'PaymentController'); // name = (payments. index / create / show / update / destroy / edit - + Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk'); Route::resource('users', 'UserController'); // name = (users. index / create / show / update / destroy / edit @@ -81,22 +81,23 @@ Route::group(['middleware' => ['api_db','api_secret_check','token_auth'], 'prefi Route::post('refresh', 'Auth\LoginController@refresh'); /* Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit - + Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk'); - - + + Route::resource('credits', 'CreditController'); // name = (credits. index / create / show / update / destroy / edit - + Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk'); - + Route::resource('expenses', 'ExpenseController'); // name = (expenses. index / create / show / update / destroy / edit - + Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk'); - - + + Route::get('settings', 'SettingsController@index')->name('user.settings'); */ + Route::post('support/messages/send', 'Support\Messages\SendingController'); }); -Route::fallback('BaseController@notFound'); \ No newline at end of file +Route::fallback('BaseController@notFound'); diff --git a/tests/Browser/ClientPortalTest.php b/tests/Browser/ClientPortalTest.php index 1dfc5989a141..ecf107131690 100644 --- a/tests/Browser/ClientPortalTest.php +++ b/tests/Browser/ClientPortalTest.php @@ -180,6 +180,7 @@ class ClientPortalTest extends DuskTestCase $this->browse(function ($browser) { $browser->visit('/client/login') + ->assertPathIs('/client/login') ->type('email', 'user@example.com') ->type('password', config('ninja.testvars.password')) ->press('Login') @@ -319,4 +320,4 @@ class ClientPortalTest extends DuskTestCase }); } -} \ No newline at end of file +}