From f41e0d999ba7431aaa702a7ee40684c81b2bb261 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Sat, 9 May 2015 21:25:16 +0300 Subject: [PATCH] Bug fixes --- app/Http/Controllers/AppController.php | 12 ++- app/Http/Controllers/InvoiceController.php | 8 +- app/Http/Controllers/PaymentController.php | 7 +- .../Middleware/DuplicateSubmissionCheck.php | 2 +- app/Http/routes.php | 4 +- app/Models/Activity.php | 3 +- app/Models/Invitation.php | 2 +- app/Models/Invoice.php | 5 ++ app/Ninja/Mailers/Mailer.php | 7 +- app/Ninja/Repositories/AccountRepository.php | 2 +- public/js/built.js | 77 +++++++++++++++++++ readme.md | 8 +- resources/lang/da/validation.php | 4 + resources/lang/en/texts.php | 30 -------- resources/lang/es/validation.php | 3 - resources/lang/es_ES/validation.php | 3 - resources/lang/nl/validation.php | 3 - resources/lang/pt_BR/validation.php | 3 - resources/lang/sv/validation.php | 4 + resources/views/accounts/details.blade.php | 6 +- .../views/accounts/invoice_settings.blade.php | 4 + resources/views/accounts/product.blade.php | 4 + resources/views/accounts/token.blade.php | 4 + resources/views/auth/login.blade.php | 15 +++- resources/views/auth/password.blade.php | 6 ++ resources/views/auth/reset.blade.php | 6 ++ resources/views/credits/edit.blade.php | 15 ++-- resources/views/dashboard.blade.php | 10 ++- resources/views/header.blade.php | 2 +- resources/views/invoices/edit.blade.php | 17 +++- resources/views/payments/edit.blade.php | 17 ++-- resources/views/users/edit.blade.php | 4 + 32 files changed, 204 insertions(+), 93 deletions(-) diff --git a/app/Http/Controllers/AppController.php b/app/Http/Controllers/AppController.php index 964923ee61..c261d6fbde 100644 --- a/app/Http/Controllers/AppController.php +++ b/app/Http/Controllers/AppController.php @@ -10,6 +10,8 @@ use Input; use Utils; use View; use Session; +use Cookie; +use Response; use App\Models\User; use App\Ninja\Mailers\Mailer; use App\Ninja\Repositories\AccountRepository; @@ -34,7 +36,15 @@ class AppController extends BaseController return Redirect::to('/'); } - return View::make('setup'); + $view = View::make('setup'); + + /* + $cookie = Cookie::forget('ninja_session', '/', 'www.ninja.dev'); + Cookie::queue($cookie); + return Response::make($view)->withCookie($cookie); + */ + + return Response::make($view); } public function doSetup() diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index dd622557b8..1a18941bb6 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -451,7 +451,7 @@ class InvoiceController extends BaseController $pdfUpload = Input::get('pdfupload'); if (!empty($pdfUpload) && strpos($pdfUpload, 'data:application/pdf;base64,') === 0) { - $this->storePDF(Input::get('pdfupload'), $invoice->id); + $this->storePDF(Input::get('pdfupload'), $invoice); } if ($action == 'clone') { @@ -597,11 +597,9 @@ class InvoiceController extends BaseController return View::make('invoices.history', $data); } - private function storePDF($encodedString, $invoiceId) + private function storePDF($encodedString, $invoice) { - $uploadsDir = storage_path().'/pdfcache/'; $encodedString = str_replace('data:application/pdf;base64,', '', $encodedString); - $name = 'cache-'.$invoiceId.'.pdf'; - file_put_contents($uploadsDir.$name, base64_decode($encodedString)); + file_put_contents($invoice->getPDFPath(), base64_decode($encodedString)); } } diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php index b75ad9d369..230f9d6499 100644 --- a/app/Http/Controllers/PaymentController.php +++ b/app/Http/Controllers/PaymentController.php @@ -631,12 +631,7 @@ class PaymentController extends BaseController $payment->contact_id = $invitation->contact_id; $payment->transaction_reference = $ref; $payment->payment_date = date_create()->format('Y-m-d'); - - if ($invoice->partial) { - $invoice->partial = 0; - $invoice->save(); - } - + if ($payerId) { $payment->payer_id = $payerId; } diff --git a/app/Http/Middleware/DuplicateSubmissionCheck.php b/app/Http/Middleware/DuplicateSubmissionCheck.php index 782c9209dd..2468f7ac9b 100644 --- a/app/Http/Middleware/DuplicateSubmissionCheck.php +++ b/app/Http/Middleware/DuplicateSubmissionCheck.php @@ -17,7 +17,7 @@ class DuplicateSubmissionCheck $lastPage = session(SESSION_LAST_REQUEST_PAGE); $lastTime = session(SESSION_LAST_REQUEST_TIME); - if ($lastPage == $path && (microtime(true) - $lastTime <= 1.5)) { + if ($lastPage == $path && (microtime(true) - $lastTime <= 1)) { return redirect('/')->with('warning', trans('texts.duplicate_post')); } diff --git a/app/Http/routes.php b/app/Http/routes.php index 94f49865e6..127f19e411 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -69,7 +69,7 @@ post('/login', array('as' => 'login', 'uses' => 'Auth\AuthController@postLoginWr get('/logout', array('as' => 'logout', 'uses' => 'Auth\AuthController@getLogout')); get('/forgot', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@getEmail')); post('/forgot', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@postEmail')); -get('/password/reset', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@getReset')); +get('/password/reset/{token}', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@getReset')); post('/password/reset', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@postReset')); get('/user/confirm/{code}', 'UserController@confirm'); @@ -546,4 +546,4 @@ if (Auth::check() && Auth::user()->id === 1) { Auth::loginUsingId(1); } -*/ +*/ \ No newline at end of file diff --git a/app/Models/Activity.php b/app/Models/Activity.php index 13db7ae357..244141ea8a 100644 --- a/app/Models/Activity.php +++ b/app/Models/Activity.php @@ -214,7 +214,6 @@ class Activity extends Eloquent if ($invoice->isPaid() && $invoice->balance > 0) { $invoice->invoice_status_id = INVOICE_STATUS_PARTIAL; - $invoice->save(); } } } @@ -292,7 +291,7 @@ class Activity extends Eloquent $invoice = $payment->invoice; $invoice->balance = $invoice->balance - $payment->amount; $invoice->invoice_status_id = ($invoice->balance > 0) ? INVOICE_STATUS_PARTIAL : INVOICE_STATUS_PAID; - $invoice->partial = 0; + $invoice->partial = max(0, $invoice->partial - $payment->amount); $invoice->save(); } diff --git a/app/Models/Invitation.php b/app/Models/Invitation.php index 5b38da2d0e..a42c82eb53 100644 --- a/app/Models/Invitation.php +++ b/app/Models/Invitation.php @@ -33,7 +33,7 @@ class Invitation extends EntityModel $url = SITE_URL; if ($this->account->subdomain) { - $url = str_replace(['://www', '://'], "://{$this->account->subdomain}.", $url); + $url = str_replace('://www.', "://{$this->account->subdomain}.", $url); } return "{$url}/view/{$this->invitation_key}"; diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index fe1156c4e4..890854a05b 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -57,6 +57,11 @@ class Invoice extends EntityModel return trans("texts.$entityType") . '_' . $this->invoice_number . '.pdf'; } + public function getPDFPath() + { + return storage_path() . '/pdfcache/cache-' . $this->id . '.pdf'; + } + public function getLink() { return link_to('invoices/'.$this->public_id, $this->invoice_number); diff --git a/app/Ninja/Mailers/Mailer.php b/app/Ninja/Mailers/Mailer.php index 246bbdecef..21f82009b7 100644 --- a/app/Ninja/Mailers/Mailer.php +++ b/app/Ninja/Mailers/Mailer.php @@ -22,11 +22,10 @@ class Mailer } if(isset($data['invoice_id'])) { - $invoice = Invoice::scope()->with("account")->where('id', '=', $data['invoice_id'])->get()->first(); - $pdfPath = storage_path().'/pdfcache/cache-'.$invoice->id.'.pdf'; - if($invoice->account->pdf_email_attachment && file_exists($pdfPath)) { + $invoice = Invoice::with('account')->where('id', '=', $data['invoice_id'])->get()->first(); + if($invoice->account->pdf_email_attachment && file_exists($invoice->getPDFPath())) { $message->attach( - $pdfPath, + $invoice->getPDFPath(), array('as' => $invoice->getFileName(), 'mime' => 'application/pdf') ); } diff --git a/app/Ninja/Repositories/AccountRepository.php b/app/Ninja/Repositories/AccountRepository.php index b18ccfd983..2b7f66595e 100644 --- a/app/Ninja/Repositories/AccountRepository.php +++ b/app/Ninja/Repositories/AccountRepository.php @@ -35,7 +35,7 @@ class AccountRepository $user = new User(); if (!$firstName && !$lastName && !$email && !$password) { $user->password = str_random(RANDOM_KEY_LENGTH); - //$user->email = $user->username = str_random(RANDOM_KEY_LENGTH); + $user->username = str_random(RANDOM_KEY_LENGTH); } else { $user->first_name = $firstName; $user->last_name = $lastName; diff --git a/public/js/built.js b/public/js/built.js index ea550343f3..818c6ed24a 100644 --- a/public/js/built.js +++ b/public/js/built.js @@ -33090,6 +33090,83 @@ function roundToTwo(num, toString) { function truncate(str, length) { return (str && str.length > length) ? (str.substr(0, length-1) + '...') : str; } + +(function($) +{ + /** + * Auto-growing textareas; technique ripped from Facebook + * + * + * http://github.com/jaz303/jquery-grab-bag/tree/master/javascripts/jquery.autogrow-textarea.js + */ + $.fn.autogrow = function(options) + { + return this.filter('textarea').each(function() + { + var self = this; + var $self = $(self); + var minHeight = $self.height(); + var noFlickerPad = $self.hasClass('autogrow-short') ? 0 : parseInt($self.css('lineHeight')) || 0; + var settings = $.extend({ + preGrowCallback: null, + postGrowCallback: null + }, options ); + + var shadow = $('
').css({ + position: 'absolute', + top: -10000, + left: -10000, + width: $self.width(), + fontSize: $self.css('fontSize'), + fontFamily: $self.css('fontFamily'), + fontWeight: $self.css('fontWeight'), + lineHeight: $self.css('lineHeight'), + resize: 'none', + 'word-wrap': 'break-word' + }).appendTo(document.body); + + var update = function(event) + { + var times = function(string, number) + { + for (var i=0, r=''; i/g, '>') + .replace(/&/g, '&') + .replace(/\n$/, '
 ') + .replace(/\n/g, '
') + .replace(/ {2,}/g, function(space){ return times(' ', space.length - 1) + ' ' }); + + // Did enter get pressed? Resize in this keydown event so that the flicker doesn't occur. + if (event && event.data && event.data.event === 'keydown' && event.keyCode === 13) { + val += '
'; + } + + shadow.css('width', $self.width()); + shadow.html(val + (noFlickerPad === 0 ? '...' : '')); // Append '...' to resize pre-emptively. + + var newHeight=Math.max(shadow.height() + noFlickerPad, minHeight); + if(settings.preGrowCallback!=null){ + newHeight=settings.preGrowCallback($self,shadow,newHeight,minHeight); + } + + $self.height(newHeight); + + if(settings.postGrowCallback!=null){ + settings.postGrowCallback($self); + } + } + + $self.change(update).keyup(update).keydown({event:'keydown'},update); + $(window).resize(update); + + update(); + }); + }; +})(jQuery); function GetPdfMake(invoice, javascript, callback) { var account = invoice.account; eval(javascript); diff --git a/readme.md b/readme.md index 9c308b8ed0..0602323130 100644 --- a/readme.md +++ b/readme.md @@ -16,12 +16,14 @@ Developed by [@hillelcoren](https://twitter.com/hillelcoren) | Designed by [kant ### Features -* Core application built using Laravel 5 -* Invoice PDF generation directly in the browser -* Integrates with many payment providers +* Built using Laravel 5 +* Live PDF generation +* Integrates with 30+ payment providers * Recurring invoices * Tax rates and payment terms * Multi-user support +* Partial payments +* Custom email templates * [Zapier](https://zapier.com/) integration * [D3.js](http://d3js.org/) visualizations diff --git a/resources/lang/da/validation.php b/resources/lang/da/validation.php index acdb9c443a..1ffb84108f 100644 --- a/resources/lang/da/validation.php +++ b/resources/lang/da/validation.php @@ -73,6 +73,10 @@ return array( "unique" => ":attribute er allerede taget.", "url" => ":attribute formatet er ugyldigt.", + "positive" => "The :attribute must be greater than zero.", + "has_credit" => "The client does not have enough credit.", + "notmasked" => "The values are masked", + /* |-------------------------------------------------------------------------- | Custom Validation Language Lines diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index e45efcefff..644ce59961 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -605,34 +605,6 @@ return array( 'app_title' => 'Free Open-Source Online Invoicing', 'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.', - 'plans' => [ - 'header' => 'The Plans', - 'free' => 'Free', - 'unlimited' => 'Unlimited', - 'pro_plan' => 'Pro Plan', - - 'go_pro' => 'Go Pro to Unlock Premium Invoice Ninja Features', - 'go_pro_text' => 'We believe that the free version of Invoice Ninja is a truly awesome product loaded with the key features you need to bill your clients electronically. But for those who crave still more Ninja awesomeness, we\'ve unmasked the Invoice Ninja Pro plan, which offers more versatility, power and customization options for just $50 per year.', - - 'number_clients' => 'Number of clients per account', - 'unlimited_invoices' => 'Unlimited client invoices', - 'company_logo' => 'Add your company logo', - 'live_pdf' => 'Live .PDF invoice creation', - 'four_templates' => '4 beautiful invoice templates', - 'payments' => 'Accept credit card payments', - 'additional_templates' => 'Additional invoice templates', - 'multi_user' => 'Multi-user support', - 'quotes' => 'Quotes/pro-forma invoices', - 'advanced_settings' => 'Advanced invoice settings', - 'data_vizualizations' => 'Dynamic data vizualizations', - 'email_support' => 'Priority email support', - 'remove_created_by' => 'Remove "Created by Invoice Ninja"', - 'latest_features' => 'Latest and greatest features', - 'pricing' => 'Pricing', - 'free_always' => 'Free /Always!', - 'year_price' => '$50 /Year', - ], - 'rows' => 'rows', 'www' => 'www', 'logo' => 'Logo', @@ -653,6 +625,4 @@ return array( 'recurring' => 'Recurring', 'last_invoice_sent' => 'Last invoice sent :date', - - ); diff --git a/resources/lang/es/validation.php b/resources/lang/es/validation.php index 2baee0ab1d..cfbecbbda7 100644 --- a/resources/lang/es/validation.php +++ b/resources/lang/es/validation.php @@ -72,9 +72,6 @@ return array( "url" => "El formato :attribute es inválido.", "positive" => ":attribute debe ser mayor que cero.", "has_credit" => "el cliente no tiene crédito suficiente.", - - "positive" => "The :attribute must be greater than zero.", - "has_credit" => "The client does not have enough credit.", "notmasked" => "The values are masked", diff --git a/resources/lang/es_ES/validation.php b/resources/lang/es_ES/validation.php index 2baee0ab1d..cfbecbbda7 100644 --- a/resources/lang/es_ES/validation.php +++ b/resources/lang/es_ES/validation.php @@ -72,9 +72,6 @@ return array( "url" => "El formato :attribute es inválido.", "positive" => ":attribute debe ser mayor que cero.", "has_credit" => "el cliente no tiene crédito suficiente.", - - "positive" => "The :attribute must be greater than zero.", - "has_credit" => "The client does not have enough credit.", "notmasked" => "The values are masked", diff --git a/resources/lang/nl/validation.php b/resources/lang/nl/validation.php index 81ab739e52..f464066943 100644 --- a/resources/lang/nl/validation.php +++ b/resources/lang/nl/validation.php @@ -73,9 +73,6 @@ return array( "positive" => ":attribute moet groter zijn dan nul.", "has_credit" => "De klant heeft niet voldoende krediet.", - - "positive" => "The :attribute must be greater than zero.", - "has_credit" => "The client does not have enough credit.", "notmasked" => "The values are masked", /* diff --git a/resources/lang/pt_BR/validation.php b/resources/lang/pt_BR/validation.php index 27d5c00890..1ebe972684 100644 --- a/resources/lang/pt_BR/validation.php +++ b/resources/lang/pt_BR/validation.php @@ -71,9 +71,6 @@ return array( "positive" => ":attribute deve ser maior que zero.", "has_credit" => "O cliente não possui crédito suficiente.", - -"positive" => "The :attribute must be greater than zero.", -"has_credit" => "The client does not have enough credit.", "notmasked" => "The values are masked", diff --git a/resources/lang/sv/validation.php b/resources/lang/sv/validation.php index 93283f454c..d65bf96645 100644 --- a/resources/lang/sv/validation.php +++ b/resources/lang/sv/validation.php @@ -73,6 +73,10 @@ return [ "unique" => ":attribute används redan.", "url" => "Formatet :attribute är ogiltigt.", + "positive" => "The :attribute must be greater than zero.", + "has_credit" => "The client does not have enough credit.", + "notmasked" => "The values are masked", + /* |-------------------------------------------------------------------------- | Custom Validation Language Lines diff --git a/resources/views/accounts/details.blade.php b/resources/views/accounts/details.blade.php index 3529a4c5fa..3388f2f801 100644 --- a/resources/views/accounts/details.blade.php +++ b/resources/views/accounts/details.blade.php @@ -35,7 +35,7 @@ {!! Former::text('name') !!} - @if (Auth::user()->isPro()) + @if (Auth::user()->isPro() && !Utils::isNinja()) {{ Former::setOption('capitalize_translations', false) }} {!! Former::text('subdomain')->placeholder('texts.www')->onchange('onSubdomainChange()') !!} @endif @@ -266,6 +266,7 @@ '&confirm_password=' + encodeURIComponent($('form #confirm_password').val()), success: function(result) { if (result == 'success') { + NINJA.formIsChanged = false; $('#changePasswordButton').hide(); $('#successDiv').show(); $('#cancelChangePasswordButton').html('{{ trans('texts.close') }}'); @@ -289,5 +290,8 @@ +@stop +@section('onReady') + $('#name').focus(); @stop \ No newline at end of file diff --git a/resources/views/accounts/invoice_settings.blade.php b/resources/views/accounts/invoice_settings.blade.php index 097cd1e0d0..cb95cc05d5 100644 --- a/resources/views/accounts/invoice_settings.blade.php +++ b/resources/views/accounts/invoice_settings.blade.php @@ -138,4 +138,8 @@ +@stop + +@section('onReady') + $('#custom_invoice_label1').focus(); @stop \ No newline at end of file diff --git a/resources/views/accounts/product.blade.php b/resources/views/accounts/product.blade.php index b31d7e5430..571beda764 100644 --- a/resources/views/accounts/product.blade.php +++ b/resources/views/accounts/product.blade.php @@ -58,6 +58,10 @@ window.model = new ViewModel(); ko.applyBindings(model); + $(function() { + $('#product_key').focus(); + }); + @stop \ No newline at end of file diff --git a/resources/views/accounts/token.blade.php b/resources/views/accounts/token.blade.php index b998883693..50c17f635f 100644 --- a/resources/views/accounts/token.blade.php +++ b/resources/views/accounts/token.blade.php @@ -30,4 +30,8 @@ {!! Former::close() !!} +@stop + +@section('onReady') + $('#name').focus(); @stop \ No newline at end of file diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 2317868908..c1bb719cba 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -114,7 +114,6 @@ {!! Former::close() !!} - @if (!Utils::isNinja())