From b79d5210fa9a1df1faee1716d2784672d95e8186 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 28 Nov 2013 18:40:13 +0200 Subject: [PATCH] Prevent duplicate form submissions --- app/config/mail.php | 2 +- app/controllers/ClientController.php | 21 ++++++++++-- app/controllers/InvoiceController.php | 34 ++++++++++++------- ...11_05_180133_confide_setup_users_table.php | 19 +++++++++-- app/filters.php | 14 +++++++- app/models/Activity.php | 16 ++++----- app/models/Invitation.php | 16 +++++++++ app/models/Product.php | 4 +-- app/routes.php | 32 +++++++++++++++-- app/views/clients/edit.blade.php | 4 +-- app/views/invoices/edit.blade.php | 19 ++++++++--- 11 files changed, 143 insertions(+), 38 deletions(-) create mode 100644 app/models/Invitation.php diff --git a/app/config/mail.php b/app/config/mail.php index a93a48b29d85..82ce8f4c0b45 100755 --- a/app/config/mail.php +++ b/app/config/mail.php @@ -119,6 +119,6 @@ return array( | */ - 'pretend' => false, + 'pretend' => true, ); \ No newline at end of file diff --git a/app/controllers/ClientController.php b/app/controllers/ClientController.php index 29020e45896e..57dd8a359026 100755 --- a/app/controllers/ClientController.php +++ b/app/controllers/ClientController.php @@ -92,8 +92,11 @@ class ClientController extends \BaseController { $contact->phone = Input::get('phone'); $client->contacts()->save($contact); + $url = 'clients/' . $client->id; + processedRequest($url); + Session::flash('message', 'Successfully created client'); - return Redirect::to('clients/' . $client->id); + return Redirect::to($url); } } @@ -141,9 +144,23 @@ class ClientController extends \BaseController { ->withInput(Input::except('password')); } else { $client = Client::find($id); - $client->name = Input::get('name'); + $client->name = Input::get('name'); + $client->work_phone = Input::get('work_phone'); + $client->address1 = Input::get('address1'); + $client->address2 = Input::get('address2'); + $client->city = Input::get('city'); + $client->state = Input::get('state'); + $client->notes = Input::get('notes'); + $client->postal_code = Input::get('postal_code'); $client->save(); + $contact = $client->contacts[0]; + $contact->email = Input::get('email'); + $contact->first_name = Input::get('first_name'); + $contact->last_name = Input::get('last_name'); + $contact->phone = Input::get('phone'); + $contact->save(); + Session::flash('message', 'Successfully updated client'); return Redirect::to('clients'); } diff --git a/app/controllers/InvoiceController.php b/app/controllers/InvoiceController.php index 915e426e6b39..e8cfd505a567 100755 --- a/app/controllers/InvoiceController.php +++ b/app/controllers/InvoiceController.php @@ -37,14 +37,14 @@ class InvoiceController extends \BaseController { } - public function view($invoiceKey) + public function view($key) { - $invoice = Invoice::with('invoice_items', 'client.account.account_gateways')->where('invoice_key', '=', $invoiceKey)->firstOrFail(); + $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.account.account_gateways')->where('key', '=', $key)->firstOrFail(); $contact = null; - Activity::viewInvoice($invoice, $contact); + Activity::viewInvoice($invitation); - return View::make('invoices.view')->with('invoice', $invoice); + return View::make('invoices.view')->with('invoice', $invitation->invoice); } private function createGateway($accountGateway) @@ -91,7 +91,7 @@ class InvoiceController extends \BaseController { public function show_payment($invoiceKey) { - $invoice = Invoice::with('invoice_items', 'client.account.account_gateways.gateway')->where('invoice_key', '=', $invoiceKey)->firstOrFail(); + $invoice = Invoice::with('invoice_items', 'client.account.account_gateways.gateway')->where('key', '=', $invoiceKey)->firstOrFail(); $accountGateway = $invoice->client->account->account_gateways[0]; $gateway = InvoiceController::createGateway($accountGateway); @@ -159,7 +159,7 @@ class InvoiceController extends \BaseController { $payment->save(); Session::flash('message', 'Successfully applied payment'); - return Redirect::to('view/' . $payment->invoice->invoice_key); + return Redirect::to('view/' . $payment->invoice->key); } else { @@ -247,16 +247,14 @@ class InvoiceController extends \BaseController { $invoice->invoice_items()->forceDelete(); } else { $invoice = new Invoice; - $invoice->invoice_key = str_random(20); $invoice->account_id = Auth::user()->account_id; } - $date = DateTime::createFromFormat('m/d/Y', Input::get('invoice_date')); - $invoice->client_id = $clientId; $invoice->invoice_number = Input::get('invoice_number'); $invoice->discount = 0; - $invoice->invoice_date = $date->format('Y-m-d'); + $invoice->invoice_date = toSqlDate(Input::get('invoice_date')); + $invoice->due_date = toSqlDate(Input::get('due_date')); $invoice->save(); $items = json_decode(Input::get('items')); @@ -277,7 +275,7 @@ class InvoiceController extends \BaseController { { $product = new Product; $product->account_id = Auth::user()->account_id; - $product->product_key = $item->product_key; + $product->key = $item->product_key; } $product->notes = $item->notes; @@ -298,20 +296,30 @@ class InvoiceController extends \BaseController { if (Input::get('send_email_checkBox')) { $data = array('link' => URL::to('view') . '/' . $invoice->invoice_key); + /* Mail::send(array('html'=>'emails.invoice_html','text'=>'emails.invoice_text'), $data, function($message) use ($contact) { $message->from('hillelcoren@gmail.com', 'Hillel Coren'); $message->to($contact->email); }); + */ + + $invitation = new Invitation; + $invitation->invoice_id = $invoice->id; + $invitation->user_id = Auth::user()->id; + $invitation->contact_id = $contact->id; + $invitation->key = str_random(20); + $invitation->save(); - Activity::emailInvoice($invoice, $contact); Session::flash('message', 'Successfully emailed invoice'); } else { Session::flash('message', 'Successfully saved invoice'); } - return Redirect::to('invoices/' . $invoice->id . '/edit'); + $url = 'invoices/' . $invoice->id . '/edit'; + processedRequest($url); + return Redirect::to($url); } } diff --git a/app/database/migrations/2013_11_05_180133_confide_setup_users_table.php b/app/database/migrations/2013_11_05_180133_confide_setup_users_table.php index 8f5459958037..2151a838eaec 100755 --- a/app/database/migrations/2013_11_05_180133_confide_setup_users_table.php +++ b/app/database/migrations/2013_11_05_180133_confide_setup_users_table.php @@ -10,6 +10,7 @@ class ConfideSetupUsersTable extends Migration { */ public function up() { + Schema::dropIfExists('invitations'); Schema::dropIfExists('activities'); Schema::dropIfExists('account_gateways'); Schema::dropIfExists('gateways'); @@ -138,14 +139,26 @@ class ConfideSetupUsersTable extends Migration { $t->timestamps(); $t->softDeletes(); - $t->string('invoice_key')->unique(); $t->string('invoice_number'); $t->float('discount'); $t->date('invoice_date'); + $t->date('due_date'); //$t->foreign('account_id')->references('id')->on('accounts'); }); + + Schema::create('invitations', function($t) + { + $t->increments('id'); + $t->integer('user_id'); + $t->integer('contact_id'); + $t->integer('invoice_id'); + $t->string('key')->unique(); + $t->timestamps(); + $t->softDeletes(); + }); + Schema::create('invoice_items', function($t) { $t->increments('id'); @@ -169,7 +182,7 @@ class ConfideSetupUsersTable extends Migration { $t->timestamps(); $t->softDeletes(); - $t->string('product_key'); + $t->string('key'); $t->string('notes'); $t->decimal('cost', 8, 2); $t->integer('qty'); @@ -203,6 +216,7 @@ class ConfideSetupUsersTable extends Migration { $t->integer('contact_id'); $t->integer('invoice_id'); $t->integer('payment_id'); + $t->integer('invitation_id'); $t->timestamps(); $t->integer('activity_type_id'); @@ -217,6 +231,7 @@ class ConfideSetupUsersTable extends Migration { */ public function down() { + Schema::dropIfExists('invitations'); Schema::dropIfExists('activities'); Schema::dropIfExists('account_gateways'); Schema::dropIfExists('gateways'); diff --git a/app/filters.php b/app/filters.php index 85f82c418dda..efb311bd434a 100755 --- a/app/filters.php +++ b/app/filters.php @@ -73,7 +73,19 @@ Route::filter('guest', function() Route::filter('csrf', function() { - if (Session::token() != Input::get('_token')) + if (!Input::get('_token')) + { + return; + } + + $tokenInput = Input::get('_token'); + $tokenSession = Session::token(); + + if ($url = Session::get($tokenInput)) + { + return Redirect::to($url); + } + else if ($tokenSession != $tokenInput) { throw new Illuminate\Session\TokenMismatchException; } diff --git a/app/models/Activity.php b/app/models/Activity.php index ac197024bdca..adc9310de8c6 100755 --- a/app/models/Activity.php +++ b/app/models/Activity.php @@ -59,13 +59,13 @@ class Activity extends Eloquent $activity->save(); } - public static function emailInvoice($invoice, $contact) + public static function emailInvoice($invitation) { $activity = Activity::getBlank(); - $activity->invoice_id = $invoice->id; - $activity->client_id = $invoice->client_id; + $activity->invoice_id = $invitation->invoice_id; + $activity->contact_id = $invitation->contact_id; $activity->activity_type_id = ACTIVITY_TYPE_EMAIL_INVOICE; - $activity->message = Auth::user()->getFullName() . ' emailed invoice ' . $invoice->number . ' to ' . $contact->getFullName(); + //$activity->message = Auth::user()->getFullName() . ' emailed invoice ' . $invitation->invoice->number . ' to ' . $contact->getFullName(); $activity->save(); } @@ -100,12 +100,12 @@ class Activity extends Eloquent $activity->save(); } - public static function viewInvoice($invoice, $contact) + public static function viewInvoice($invitation) { $activity = new Activity; - //$activity->contact_id = $contact->id; - $activity->invoice_id = $invoice->id; - $activity->client_id = $invoice->client_id; + $activity->invitation_id = $invitation->invitation_id; + $activity->contact_id = $invitation->contact_id; + $activity->invoice_id = $invitation->invoice_id; $activity->activity_type_id = ACTIVITY_TYPE_VIEW_INVOICE; //$activity->message = $contact->getFullName() . ' viewed invoice ' . $invoice->number; $activity->save(); diff --git a/app/models/Invitation.php b/app/models/Invitation.php new file mode 100644 index 000000000000..17200e483ccf --- /dev/null +++ b/app/models/Invitation.php @@ -0,0 +1,16 @@ +belongsTo('Invoice'); + } +} + +Invitation::created(function($invitation) +{ + Activity::emailInvoice($invitation); +}); \ No newline at end of file diff --git a/app/models/Product.php b/app/models/Product.php index 9f00e236afe0..85c1d9b92580 100755 --- a/app/models/Product.php +++ b/app/models/Product.php @@ -11,12 +11,12 @@ class Product extends Eloquent public static function findProduct($key) { - return Product::getProducts()->where('product_key','=',$key)->first(); + return Product::getProducts()->where('key','=',$key)->first(); } public static function getProductKeys($products) { - $products = array_pluck($products, 'product_key'); + $products = array_pluck($products, 'key'); $products = array_combine($products, $products); return $products; diff --git a/app/routes.php b/app/routes.php index bfa50886db2d..54f9ef9efbf3 100755 --- a/app/routes.php +++ b/app/routes.php @@ -24,13 +24,13 @@ Route::get('complete', 'InvoiceController@do_payment'); Route::filter('auth', function() { - if (!Auth::check()) + if (!Auth::check()) { return Redirect::to('/'); } }); -Route::group(array('before' => 'auth'), function() +Route::group(array('before' => array('auth', 'csrf')), function() { Route::get('account/{section?}', 'AccountController@showSection'); Route::post('account/{section?}', 'AccountController@doSection'); @@ -39,7 +39,7 @@ Route::group(array('before' => 'auth'), function() Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable')); Route::resource('invoices', 'InvoiceController'); - Route::get('api/invoices', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable')); + Route::get('api/invoices', array('as'=>'api.invoices', 'uses'=>'InvoiceController@getDatatable')); Route::get('invoices/create/{client_id}', 'InvoiceController@create'); Route::get('payments', 'PaymentController@index'); @@ -105,6 +105,32 @@ function toSpaceCase($camelStr) return preg_replace('/([a-z])([A-Z])/s','$1 $2', $camelStr); } +function toSqlDate($date) +{ + if (!$date) + { + return ''; + } + + return DateTime::createFromFormat('m/d/Y', $date); +} + +function fromSqlDate($date) +{ + if (!$date || $date == '0000-00-00') + { + return ''; + } + + return DateTime::createFromFormat('Y-m-d', $date)->format('m/d/Y'); +} + +function processedRequest($url) +{ + Session::put(Input::get('_token'), $url); + Session::put('_token', md5(microtime())); +} + define("ENV_DEVELOPMENT", "local"); define("ENV_STAGING", "staging"); define("ENV_PRODUCTION", "production"); diff --git a/app/views/clients/edit.blade.php b/app/views/clients/edit.blade.php index 630352883b60..a02daaeab031 100755 --- a/app/views/clients/edit.blade.php +++ b/app/views/clients/edit.blade.php @@ -14,7 +14,7 @@ {{ Former::populate($client); }} @endif - {{ Former::open($url)->addClass('col-md-9 col-md-offset-1')->method($method)->rules(array( + {{ Former::open($url)->addClass('col-md-9 col-md-offset-1 main_form')->method($method)->rules(array( 'name' => 'required', 'email' => 'email' )); }} @@ -38,7 +38,7 @@ {{ Former::text('state') }} {{ Former::text('postal_code') }} - {{ Former::actions()->lg_primary_submit('Save') }} + {{ Former::actions( Button::lg_primary_submit('Save') ) }} {{ Former::close() }} @stop \ No newline at end of file diff --git a/app/views/invoices/edit.blade.php b/app/views/invoices/edit.blade.php index b4003a0b2a4c..1e7d3a3da0d3 100755 --- a/app/views/invoices/edit.blade.php +++ b/app/views/invoices/edit.blade.php @@ -13,7 +13,8 @@ @if ($invoice) {{ Former::populate($invoice); }} - {{ Former::populateField('invoice_date', DateTime::createFromFormat('Y-m-d', $invoice->invoice_date)->format('m/d/Y')); }} + {{ Former::populateField('invoice_date', fromSqlDate($invoice->invoice_date)); }} + {{ Former::populateField('due_date', fromSqlDate($invoice->due_date)); }} @else {{ Former::populateField('invoice_date', date('m/d/Y')) }} @endif @@ -26,6 +27,7 @@
{{ Former::text('invoice_number')->label('Invoice #') }} {{ Former::text('invoice_date') }} + {{ Former::text('due_date') }} {{-- Former::text('discount')->data_bind("value: discount, valueUpdate: 'afterkeydown'") --}}
@@ -53,7 +55,7 @@ - {{ Former::text('product_key')->useDatalist(Product::getProductKeys($products), 'product_key') + {{ Former::text('product_key')->useDatalist(Product::getProductKeys($products), 'key') ->raw()->data_bind("value: product_key, valueUpdate: 'afterkeydown'")->addClass('datalist') }} @@ -106,7 +108,7 @@

 

{{ Button::primary('Download PDF', array('onclick' => 'onDownloadClick()')) }} - {{ Button::primary_submit('Save Invoice') }} + {{ Button::primary_submit('Save Invoice', array('onclick' => 'onSaveClick()')) }} {{ Button::primary('Send Email', array('onclick' => 'onEmailClick()')) }}

 

@@ -174,6 +176,11 @@ refreshPDF(); }); + $('#due_date').datepicker({ + autoclose: true, + todayHighlight: true + }); + var $input = $('select#client'); $input.combobox(); $('.combobox-container input.form-control').attr('name', 'client_combobox').on('change', function(e) { @@ -212,7 +219,7 @@ var key = $(this).val(); for (var i=0; i