mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-05 18:52:44 +01:00
Flutter Client ! (#3325)
* Working on emailing invoices * Working on emailing and displaying email * Working on emailing and displaying email * Email invoices * Fixes for html emails * Restart queue after self-update * Email Invoices * Push Flutter Web Clientgit statusgit status!
This commit is contained in:
parent
bf25b7db95
commit
4a3d37a42b
@ -59,5 +59,13 @@ class ArtisanUpgrade extends Command
|
||||
\Log::error("I wasn't able to optimize.");
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Artisan::call('queue:restart');
|
||||
|
||||
}catch(Exception $e) {
|
||||
|
||||
\Log::error("I wasn't able to restart the queue");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -338,4 +338,8 @@ class BaseController extends Controller
|
||||
|
||||
}
|
||||
|
||||
public function flutterRoute()
|
||||
{
|
||||
return redirect('/index.html');
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,8 @@ class CreateInvoicePdf implements ShouldQueue {
|
||||
|
||||
$path = $this->invoice->client->client_hash . '/invoices/';
|
||||
|
||||
$file_path = $path . $this->invoice->number . '-' . $this->contact->contact_key .'.pdf';
|
||||
//$file_path = $path . $this->invoice->number . '-' . $this->contact->contact_key .'.pdf';
|
||||
$file_path = $path . $this->invoice->number . '.pdf';
|
||||
|
||||
$modern = new Modern();
|
||||
$designer = new Designer($modern, $this->invoice->client->getSetting('invoice_variables'));
|
||||
|
@ -64,15 +64,22 @@ class EmailInvoice implements ShouldQueue
|
||||
|
||||
$this->invoice->invitations->each(function ($invitation) use ($template_style) {
|
||||
if ($invitation->contact->send_invoice && $invitation->contact->email) {
|
||||
|
||||
$message_array = $this->invoice->getEmailData('', $invitation->contact);
|
||||
|
||||
$message_array['title'] = &$message_array['subject'];
|
||||
$message_array['footer'] = "Sent to ".$invitation->contact->present()->name();
|
||||
|
||||
//$message_array['footer'] = "Sent to ".$invitation->contact->present()->name();
|
||||
$message_array['footer'] = "<a href='{$invitation->getLink()}'>Invoice Link</a>";
|
||||
|
||||
//change the runtime config of the mail provider here:
|
||||
|
||||
//send message
|
||||
Mail::to($invitation->contact->email, $invitation->contact->present()->name())
|
||||
->send(new TemplateEmail($message_array, $template_style, $invitation->contact->user, $invitation->contact->client));
|
||||
->send(new TemplateEmail($message_array,
|
||||
$template_style,
|
||||
$invitation->contact->user,
|
||||
$invitation->contact->client));
|
||||
|
||||
if (count(Mail::failures()) > 0) {
|
||||
event(new InvoiceWasEmailedAndFailed($this->invoice, Mail::failures()));
|
||||
|
@ -346,17 +346,18 @@ class Invoice extends BaseModel
|
||||
|
||||
// $storage_path = 'public/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
|
||||
|
||||
$public_path = $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
|
||||
$public_path = $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
|
||||
|
||||
$storage_path = $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
|
||||
$storage_path = 'storage/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
|
||||
|
||||
$disk = config('filesystems.default');
|
||||
|
||||
if (!Storage::exists($storage_path)) {
|
||||
if (!Storage::disk($disk)->exists($public_path)) {
|
||||
event(new InvoiceWasUpdated($this, $this->company));
|
||||
CreateInvoicePdf::dispatch($this, $this->company, $this->client->primary_contact()->first());
|
||||
}
|
||||
|
||||
return $public_path;
|
||||
return $storage_path;
|
||||
}
|
||||
|
||||
public function pdf_file_path()
|
||||
|
@ -25,7 +25,7 @@ class GetInvoicePdf
|
||||
|
||||
$path = $invoice->client->client_hash . '/invoices/';
|
||||
|
||||
$file_path = $path . $invoice->number . '-' . $contact->contact_key . '.pdf';
|
||||
$file_path = $path . $invoice->number . '.pdf';
|
||||
|
||||
$disk = config('filesystems.default');
|
||||
|
||||
|
@ -24,7 +24,6 @@ use Parsedown;
|
||||
trait InvoiceEmailBuilder
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Builds the correct template to send
|
||||
* @param string $reminder_template The template name ie reminder1
|
||||
@ -66,8 +65,10 @@ trait InvoiceEmailBuilder
|
||||
}
|
||||
}
|
||||
|
||||
$data['body'] = $this->parseTemplate($body_template, false, $contact);
|
||||
$data['subject'] = $this->parseTemplate($subject_template, true, $contact);
|
||||
|
||||
$data['body'] = $this->parseTemplate($body_template, true, $contact);
|
||||
|
||||
$data['subject'] = $this->parseTemplate($subject_template, false, $contact);
|
||||
|
||||
if ($client->getSetting('pdf_email_attachment') !== false) {
|
||||
$data['files'][] = $this->pdf_file_path();
|
||||
@ -76,7 +77,7 @@ trait InvoiceEmailBuilder
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function parseTemplate(string $template_data, bool $is_markdown = true, $contact) :string
|
||||
private function parseTemplate(string $template_data, bool $is_markdown = true, $contact = null) :string
|
||||
{
|
||||
$invoice_variables = $this->makeValues($contact);
|
||||
|
||||
@ -85,11 +86,10 @@ trait InvoiceEmailBuilder
|
||||
|
||||
//process markdown
|
||||
if ($is_markdown) {
|
||||
//$data = Parsedown::instance()->line($data);
|
||||
|
||||
$converter = new CommonMarkConverter([
|
||||
'html_input' => 'strip',
|
||||
'allow_unsafe_links' => false,
|
||||
'html_input' => 'allow',
|
||||
'allow_unsafe_links' => true,
|
||||
]);
|
||||
|
||||
$data = $converter->convertToHtml($data);
|
||||
|
@ -59,6 +59,9 @@ trait MakesDates
|
||||
*/
|
||||
public function formatDate($date, string $format) :string
|
||||
{
|
||||
if(!$date || strlen($date) < 1)
|
||||
return '';
|
||||
|
||||
if (is_string($date)) {
|
||||
$date = $this->convertToDateObject($date);
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ return [
|
||||
'web_url' => 'https://www.invoiceninja.com',
|
||||
'app_name' => env('APP_NAME'),
|
||||
'site_url' => env('APP_URL', ''),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoiceninja.com'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '0.0.1',
|
||||
'api_version' => '0.0.1',
|
||||
'terms_version' => '1.0.1',
|
||||
|
1
public/assets/AssetManifest.json
Normal file
1
public/assets/AssetManifest.json
Normal file
@ -0,0 +1 @@
|
||||
{"web/assets/fonts/Roboto-Regular.ttf":["web/assets/fonts/Roboto-Regular.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf":["packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf":["packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf":["packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf"],"assets/images/logo.png":["assets/images/logo.png"]}
|
45
public/assets/FontManifest.json
Normal file
45
public/assets/FontManifest.json
Normal file
@ -0,0 +1,45 @@
|
||||
[
|
||||
{
|
||||
"family": "Roboto",
|
||||
"fonts": [
|
||||
{
|
||||
"asset": "fonts/Roboto-Regular.ttf"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"family": "MaterialIcons",
|
||||
"fonts": [
|
||||
{
|
||||
"asset": "https://fonts.gstatic.com/s/materialicons/v42/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"family": "packages/font_awesome_flutter/FontAwesomeBrands",
|
||||
"fonts": [
|
||||
{
|
||||
"weight": 400,
|
||||
"asset": "packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"family": "packages/font_awesome_flutter/FontAwesomeRegular",
|
||||
"fonts": [
|
||||
{
|
||||
"weight": 400,
|
||||
"asset": "packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"family": "packages/font_awesome_flutter/FontAwesomeSolid",
|
||||
"fonts": [
|
||||
{
|
||||
"weight": 900,
|
||||
"asset": "packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
16269
public/assets/LICENSE
Normal file
16269
public/assets/LICENSE
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/assets/assets/images/logo.png
Normal file
BIN
public/assets/assets/images/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
BIN
public/assets/fonts/MaterialIcons-Regular.ttf
Normal file
BIN
public/assets/fonts/MaterialIcons-Regular.ttf
Normal file
Binary file not shown.
BIN
public/assets/fonts/Roboto-Regular.ttf
Normal file
BIN
public/assets/fonts/Roboto-Regular.ttf
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
public/assets/web/assets/fonts/Roboto-Regular.ttf
Normal file
BIN
public/assets/web/assets/fonts/Roboto-Regular.ttf
Normal file
Binary file not shown.
42
public/flutter_service_worker.js
vendored
Normal file
42
public/flutter_service_worker.js
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
'use strict';
|
||||
const CACHE_NAME = 'flutter-app-cache';
|
||||
const RESOURCES = {
|
||||
"/main.dart.js": "fa2c639a9577f242475de037e8c3c909",
|
||||
"/index.html": "2b291048759a26809bc606c981d175ad",
|
||||
"/assets/FontManifest.json": "280b2f61f6810d59bd1bcd4cf01d3bf4",
|
||||
"/assets/packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf": "bdd8d75eb9e6832ccd3117e06c51f0d3",
|
||||
"/assets/packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf": "d21f791b837673851dd14f7c132ef32e",
|
||||
"/assets/packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf": "3ca122272cfac33efb09d0717efde2fa",
|
||||
"/assets/LICENSE": "0134b30d1e80505d7a68fb6fa8d3e2cd",
|
||||
"/assets/AssetManifest.json": "90d7b27343fcf361dbf6385b3055ce4f",
|
||||
"/assets/fonts/Roboto-Regular.ttf": "3e1af3ef546b9e6ecef9f3ba197bf7d2",
|
||||
"/assets/fonts/MaterialIcons-Regular.ttf": "56d3ffdef7a25659eab6a68a3fbfaf16",
|
||||
"/assets/assets/images/logo.png": "090f69e23311a4b6d851b3880ae52541",
|
||||
"/assets/web/assets/fonts/Roboto-Regular.ttf": "3e1af3ef546b9e6ecef9f3ba197bf7d2"
|
||||
};
|
||||
|
||||
self.addEventListener('activate', function (event) {
|
||||
event.waitUntil(
|
||||
caches.keys().then(function (cacheName) {
|
||||
return caches.delete(cacheName);
|
||||
}).then(function (_) {
|
||||
return caches.open(CACHE_NAME);
|
||||
}).then(function (cache) {
|
||||
return cache.addAll(Object.keys(RESOURCES));
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', function (event) {
|
||||
event.respondWith(
|
||||
caches.match(event.request)
|
||||
.then(function (response) {
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
return fetch(event.request, {
|
||||
credentials: 'include'
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
26
public/index.html
Normal file
26
public/index.html
Normal file
@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Invoice Ninja</title>
|
||||
</head>
|
||||
<body style="background-color:#888888;">
|
||||
|
||||
<!--
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register('/flutter_service_worker.js');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
-->
|
||||
|
||||
<script src="main.dart.js" type="application/javascript"></script>
|
||||
|
||||
<center style="font-family:Tahoma,Geneva,sans-serif;font-size:28px;color:white;padding-top:100px">
|
||||
Loading...
|
||||
</center>
|
||||
|
||||
</body>
|
||||
</html>
|
156208
public/main.dart.js
vendored
Normal file
156208
public/main.dart.js
vendored
Normal file
File diff suppressed because one or more lines are too long
16
public/main.dart.js.map
Normal file
16
public/main.dart.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -1,4 +1,13 @@
|
||||
{{ $body }}
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ App::getLocale() }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
{!! $body !!}
|
||||
|
||||
|
||||
{{ $footer }}
|
||||
{!! $footer !!}
|
||||
</body>
|
||||
</html>
|
@ -16,8 +16,7 @@ return $request->user();
|
||||
});
|
||||
*/
|
||||
|
||||
Route::group(['middleware' => ['api_secret_check']],
|
||||
function () {
|
||||
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');
|
||||
@ -85,7 +84,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
|
||||
|
||||
Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk');
|
||||
|
||||
Route::post('migrate', 'Migration\MigrateController@index')->name('migrate.start');
|
||||
Route::post('migrate', 'MigrationController@index')->name('migrate.start');
|
||||
|
||||
// Route::resource('users', 'UserController')->middleware('password_protected'); // name = (users. index / create / show / update / destroy / edit
|
||||
Route::get('users', 'UserController@index');
|
||||
|
@ -3,38 +3,39 @@
|
||||
* Authentication Routes Laravel Defaults... replaces //Auth::routes();
|
||||
*/
|
||||
|
||||
Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');
|
||||
Route::post('login', 'Auth\LoginController@login');
|
||||
Route::post('logout', 'Auth\LoginController@logout')->name('logout');
|
||||
// Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');
|
||||
// Route::post('login', 'Auth\LoginController@login');
|
||||
// Route::post('logout', 'Auth\LoginController@logout')->name('logout');
|
||||
|
||||
/*
|
||||
* Signup Routes
|
||||
*/
|
||||
|
||||
Route::redirect('/', '/login', 301);
|
||||
Route::get('signup', 'AccountController@index')->name('signup');
|
||||
Route::post('signup', 'AccountController@store')->name('signup.submit');
|
||||
Route::get('/', 'BaseController@flutterRoute')->middleware('guest');
|
||||
|
||||
// Route::get('signup', 'AccountController@index')->name('signup');
|
||||
// Route::post('signup', 'AccountController@store')->name('signup.submit');
|
||||
|
||||
/*
|
||||
* Password Reset Routes...
|
||||
*/
|
||||
|
||||
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
|
||||
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
|
||||
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
|
||||
Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update');
|
||||
// Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
|
||||
// Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
|
||||
// Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
|
||||
// Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update');
|
||||
|
||||
/*
|
||||
* Social authentication
|
||||
*/
|
||||
|
||||
Route::get('auth/{provider}', 'Auth\LoginController@redirectToProvider');
|
||||
Route::get('auth/{provider}/create', 'Auth\LoginController@redirectToProviderAndCreate');
|
||||
// Route::get('auth/{provider}', 'Auth\LoginController@redirectToProvider');
|
||||
// Route::get('auth/{provider}/create', 'Auth\LoginController@redirectToProviderAndCreate');
|
||||
|
||||
/*
|
||||
* Authenticated User Routes
|
||||
*/
|
||||
|
||||
/*
|
||||
Route::group(['middleware' => ['auth:user', 'web_db']], function () {
|
||||
|
||||
Route::resource('dashboard', 'DashboardController'); // name = (dashboard. index / create / show / update / destroy / edit
|
||||
@ -58,7 +59,7 @@ Route::group(['middleware' => ['auth:user', 'web_db']], function () {
|
||||
Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk');
|
||||
|
||||
Route::resource('client_statement', 'ClientStatementController@statement'); // name = (client_statement. index / create / show / update / destroy / edit
|
||||
/*
|
||||
|
||||
Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk');
|
||||
@ -78,33 +79,22 @@ Route::group(['middleware' => ['auth:user', 'web_db']], function () {
|
||||
Route::resource('user', 'UserProfileController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
|
||||
Route::get('settings', 'SettingsController@index')->name('user.settings');
|
||||
*/
|
||||
|
||||
|
||||
});
|
||||
|
||||
*/
|
||||
/*
|
||||
* Inbound routes requiring DB Lookup
|
||||
*/
|
||||
Route::group(['middleware' => ['url_db']], function () {
|
||||
// Route::group(['middleware' => ['url_db']], function () {
|
||||
|
||||
Route::get('/user/confirm/{confirmation_code}', 'UserController@confirm');
|
||||
// Route::get('/user/confirm/{confirmation_code}', 'UserController@confirm');
|
||||
|
||||
});
|
||||
// });
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Injects users translation strings in json format for frontend consumption.
|
||||
*/
|
||||
Route::get('js/lang.js', 'TranslationController@index')->name('assets.lang');
|
||||
|
||||
|
||||
|
||||
/* Dev Playground
|
||||
Route::get('/mailable', function () {
|
||||
$user = App\Models\User::find(1);
|
||||
|
||||
return new App\Mail\VerifyUser($user);
|
||||
});
|
||||
|
||||
*/
|
||||
//Route::get('js/lang.js', 'TranslationController@index')->name('assets.lang');
|
Loading…
Reference in New Issue
Block a user