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
+}