diff --git a/.gitignore b/.gitignore index 0981b45f552c..5bc74ae5bab7 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ /bootstrap/compiled.php /bootstrap/environment.php /vendor +/.env.development.php /composer.phar /composer.lock /.DS_Store diff --git a/README.md b/README.md index ad03017fff47..1be56f78a9f5 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ Most online invoicing sites are expensive. They shouldn't be. The aim of this project is to provide a free, open-source alternative. Additionally, the hope is the codebase will serve as a sample site for Laravel as well as other JavaScript technologies. -For updates follow [@invoiceninja](https://twitter.com/invoiceninja) and [@hillelcoren](https://twitter.com/hillelcoren) +The high level instructions for setting up the site are below but there's also a [setup guide](http://hillelcoren.com/invoice-ninja/laravel-ubuntu-virtualbox/). For discussion of the code please use the [Google Group](https://groups.google.com/d/forum/invoiceninja). + +For updates follow [@invoiceninja](https://twitter.com/invoiceninja) or join the [Facebook Group](https://www.facebook.com/invoiceninja). Site design by [kantorp-wegl.in](http://kantorp-wegl.in/) @@ -65,4 +67,5 @@ Configure config/database.php and then initialize the database * [mozilla/pdf.js](https://github.com/mozilla/pdf.js) - PDF Reader in JavaScript * [nnnick/Chart.js](https://github.com/nnnick/Chart.js) - Simple HTML5 Charts using the tag * [josscrowcroft/accounting.js](https://github.com/josscrowcroft/accounting.js) - A lightweight JavaScript library for number, money and currency formatting -* [jashkenas/underscore](https://github.com/jashkenas/underscore) - JavaScript's utility _ belt \ No newline at end of file +* [jashkenas/underscore](https://github.com/jashkenas/underscore) - JavaScript's utility _ belt +* [caouecs/Laravel4-long](https://github.com/caouecs/Laravel4-lang) - List of languages ​​for Laravel4 \ No newline at end of file diff --git a/app/config/app.php b/app/config/app.php index 5375b22c0b13..39506c499c95 100755 --- a/app/config/app.php +++ b/app/config/app.php @@ -121,8 +121,7 @@ return array( 'Barryvdh\Debugbar\ServiceProvider', 'Chumper\Datatable\DatatableServiceProvider', 'Intervention\Image\ImageServiceProvider', - 'Webpatser\Countries\CountriesServiceProvider', - 'Rocketeer\RocketeerServiceProvider', + 'Webpatser\Countries\CountriesServiceProvider' ), /* diff --git a/app/config/packages/anahkiasen/former/config.php b/app/config/packages/anahkiasen/former/config.php index a4625318375c..327019638f9d 100755 --- a/app/config/packages/anahkiasen/former/config.php +++ b/app/config/packages/anahkiasen/former/config.php @@ -48,7 +48,7 @@ //////////////////////////////////////////////////////////////////// // Where Former should look for translations - 'translate_from' => 'validation.attributes', + 'translate_from' => 'texts', // An array of attributes to automatically translate 'translatable' => array( diff --git a/app/controllers/AccountController.php b/app/controllers/AccountController.php index eae0d6923bfb..d9a0fb28299c 100755 --- a/app/controllers/AccountController.php +++ b/app/controllers/AccountController.php @@ -86,7 +86,8 @@ class AccountController extends \BaseController { 'timezones' => Timezone::remember(DEFAULT_QUERY_CACHE)->orderBy('location')->get(), 'dateFormats' => DateFormat::remember(DEFAULT_QUERY_CACHE)->get(), 'datetimeFormats' => DatetimeFormat::remember(DEFAULT_QUERY_CACHE)->get(), - 'currencies' => Currency::remember(DEFAULT_QUERY_CACHE)->orderBy('name')->get(), + 'currencies' => Currency::remember(DEFAULT_QUERY_CACHE)->orderBy('name')->get(), + 'languages' => Language::remember(DEFAULT_QUERY_CACHE)->orderBy('name')->get(), ]; return View::make('accounts.details', $data); @@ -442,7 +443,7 @@ class AccountController extends \BaseController { foreach ($fields as $field => $details) { - if (!in_array($field, ['testMode', 'developerMode', 'headerImageUrl', 'solutionType', 'landingPage'])) + if (!in_array($field, ['testMode', 'developerMode', 'headerImageUrl', 'solutionType', 'landingPage', 'brandName'])) { $rules[$gateway->id.'_'.$field] = 'required'; } @@ -460,7 +461,7 @@ class AccountController extends \BaseController { else { $account = Account::findOrFail(Auth::user()->account_id); - $account->account_gateways()->forceDelete(); + $account->account_gateways()->delete(); if ($gatewayId) { @@ -499,7 +500,7 @@ class AccountController extends \BaseController { } else { - $account = Account::findOrFail(Auth::user()->account_id); + $account = Auth::user()->account; $account->name = trim(Input::get('name')); $account->work_email = trim(Input::get('work_email')); $account->work_phone = trim(Input::get('work_phone')); @@ -514,7 +515,8 @@ class AccountController extends \BaseController { $account->timezone_id = Input::get('timezone_id') ? Input::get('timezone_id') : null; $account->date_format_id = Input::get('date_format_id') ? Input::get('date_format_id') : null; $account->datetime_format_id = Input::get('datetime_format_id') ? Input::get('datetime_format_id') : null; - $account->currency_id = Input::get('currency_id') ? Input::get('currency_id') : 1; + $account->currency_id = Input::get('currency_id') ? Input::get('currency_id') : 1; // US Dollar + $account->language_id = Input::get('language_id') ? Input::get('language_id') : 1; // English $account->save(); $user = Auth::user(); @@ -529,8 +531,8 @@ class AccountController extends \BaseController { if ($file = Input::file('logo')) { $path = Input::file('logo')->getRealPath(); - File::delete('logo/' . $account->account_key . '.jpg'); - Image::make($path)->resize(120, 80, true, false)->save('logo/' . $account->account_key . '.jpg'); + File::delete('logo/' . $account->account_key . '.jpg'); + Image::make($path)->resize(null, 120, true, false)->save('logo/' . $account->account_key . '.jpg'); } Event::fire('user.refresh'); @@ -540,6 +542,14 @@ class AccountController extends \BaseController { } } + public function removeLogo() { + + File::delete('logo/' . Auth::user()->account->account_key . '.jpg'); + + Session::flash('message', 'Successfully removed logo'); + return Redirect::to('company/details'); + } + public function checkEmail() { $email = User::withTrashed()->where('email', '=', Input::get('email'))->where('id', '<>', Auth::user()->id)->first(); diff --git a/app/controllers/ClientController.php b/app/controllers/ClientController.php index 339a98e3e66c..0cfa7f649cc0 100755 --- a/app/controllers/ClientController.php +++ b/app/controllers/ClientController.php @@ -23,7 +23,7 @@ class ClientController extends \BaseController { return View::make('list', array( 'entityType'=>ENTITY_CLIENT, 'title' => '- Clients', - 'columns'=>['checkbox', 'Client', 'Contact', 'Email', 'Date Created', 'Last Login', 'Balance', 'Action'] + 'columns'=>Utils::trans(['checkbox', 'client', 'contact', 'email', 'date_created', 'last_login', 'balance', 'action']) )); } @@ -43,17 +43,17 @@ class ClientController extends \BaseController { { return ''; }) diff --git a/app/controllers/CreditController.php b/app/controllers/CreditController.php index e33d613b1cac..1b9ca45c1fa5 100755 --- a/app/controllers/CreditController.php +++ b/app/controllers/CreditController.php @@ -23,7 +23,7 @@ class CreditController extends \BaseController { return View::make('list', array( 'entityType'=>ENTITY_CREDIT, 'title' => '- Credits', - 'columns'=>['checkbox', 'Client', 'Credit Amount', 'Credit Balance', 'Credit Date', 'Private Notes', 'Action'] + 'columns'=>Utils::trans(['checkbox', 'client', 'credit_amount', 'credit_balance', 'credit_date', 'private_notes', 'action']) )); } @@ -47,11 +47,11 @@ class CreditController extends \BaseController { { return ''; }) diff --git a/app/controllers/DashboardController.php b/app/controllers/DashboardController.php index 2c9d38196155..dd8cfd1ccaea 100644 --- a/app/controllers/DashboardController.php +++ b/app/controllers/DashboardController.php @@ -43,11 +43,11 @@ class DashboardController extends \BaseController { ->orderBy('due_date', 'asc')->take(6)->get(); $data = [ - 'totalIncome' => Utils::formatMoney($totalIncome->value, Session::get(SESSION_CURRENCY)), - 'billedClients' => $metrics->billed_clients, - 'invoicesSent' => $metrics->invoices_sent, - 'activeClients' => $metrics->active_clients, - 'invoiceAvg' => Utils::formatMoney($metrics->invoice_avg, Session::get(SESSION_CURRENCY)), + 'totalIncome' => Utils::formatMoney($totalIncome ? $totalIncome->value : 0, Session::get(SESSION_CURRENCY)), + 'billedClients' => $metrics ? $metrics->billed_clients : 0, + 'invoicesSent' => $metrics ? $metrics->invoices_sent : 0, + 'activeClients' => $metrics ? $metrics->active_clients : 0, + 'invoiceAvg' => Utils::formatMoney(($metrics ? $metrics->invoice_avg : 0), Session::get(SESSION_CURRENCY)), 'activities' => $activities, 'pastDue' => $pastDue, 'upcoming' => $upcoming diff --git a/app/controllers/HomeController.php b/app/controllers/HomeController.php index 459ad6bd2ef8..1a344c9245a2 100755 --- a/app/controllers/HomeController.php +++ b/app/controllers/HomeController.php @@ -16,22 +16,22 @@ class HomeController extends BaseController { public function showWelcome() { - return View::make('splash'); + return View::make('public.splash'); } public function showAboutUs() { - return View::make('about_us'); + return View::make('public.about_us'); } public function showContactUs() { - return View::make('contact_us'); + return View::make('public.contact_us'); } public function showTerms() { - return View::make('terms'); + return View::make('public.terms'); } public function doContactUs() @@ -46,7 +46,7 @@ class HomeController extends BaseController { 'text' => $message ]; - $this->mailer->sendTo('contact@invoiceninja.com', 'contact@invoiceninja.com', 'Invoice Ninja Feedback', 'contact', $data); + $this->mailer->sendTo(CONTACT_EMAIL, CONTACT_EMAIL, CONTACT_NAME, 'Invoice Ninja Feedback', 'contact', $data); Session::flash('message', 'Successfully sent message'); return Redirect::to('/contact'); diff --git a/app/controllers/InvoiceController.php b/app/controllers/InvoiceController.php index 7d16724aec52..37beae47b3b0 100755 --- a/app/controllers/InvoiceController.php +++ b/app/controllers/InvoiceController.php @@ -27,13 +27,13 @@ class InvoiceController extends \BaseController { $data = [ 'title' => '- Invoices', 'entityType'=>ENTITY_INVOICE, - 'columns'=>['checkbox', 'Invoice Number', 'Client', 'Invoice Date', 'Invoice Total', 'Balance Due', 'Due Date', 'Status', 'Action'] + 'columns'=>Utils::trans(['checkbox', 'invoice_number', 'client', 'invoice_date', 'invoice_total', 'balance_due', 'due_date', 'status', 'action']) ]; if (Invoice::scope()->where('is_recurring', '=', true)->count() > 0) { $data['secEntityType'] = ENTITY_RECURRING_INVOICE; - $data['secColumns'] = ['checkbox', 'Frequency', 'Client', 'Start Date', 'End Date', 'Invoice Total', 'Action']; + $data['secColumns'] = Utils::trans(['checkbox', 'frequency', 'client', 'start_date', 'end_date', 'invoice_total', 'action']); } return View::make('list', $data); @@ -63,14 +63,14 @@ class InvoiceController extends \BaseController { { return ''; }) @@ -89,7 +89,7 @@ class InvoiceController extends \BaseController { $table->addColumn('frequency', function($model) { return link_to('invoices/' . $model->public_id, $model->frequency); }); if (!$clientPublicId) { - $table->addColumn('client', function($model) { return link_to('clients/' . $model->client_public_id, Utils::getClientDisplayName($model)); }); + $table->addColumn('client_name', function($model) { return link_to('clients/' . $model->client_public_id, Utils::getClientDisplayName($model)); }); } return $table->addColumn('start_date', function($model) { return Utils::fromSqlDate($model->start_date); }) @@ -99,13 +99,13 @@ class InvoiceController extends \BaseController { { return ''; }) @@ -143,7 +143,8 @@ class InvoiceController extends \BaseController { $data = array( 'showBreadcrumbs' => false, 'invoice' => $invoice->hidePrivateFields(), - 'invitation' => $invitation + 'invitation' => $invitation, + 'invoiceLabels' => $client->account->getInvoiceLabels(), ); return View::make('invoices.view', $data); @@ -206,6 +207,12 @@ class InvoiceController extends \BaseController { public static function getViewModel() { + // Temporary fix to let users know to re-upload their logos for higher res + if (Auth::user()->account->getLogoHeight() == 80) + { + Session::flash('warning', "We've increased the logo resolution in the PDF. Please re-upload your logo to take advantage of it."); + } + return [ 'account' => Auth::user()->account, 'products' => Product::scope()->orderBy('id')->get(array('product_key','notes','cost','qty')), @@ -217,6 +224,7 @@ class InvoiceController extends \BaseController { 'paymentTerms' => PaymentTerm::remember(DEFAULT_QUERY_CACHE)->orderBy('num_days')->get(['name', 'num_days']), 'industries' => Industry::remember(DEFAULT_QUERY_CACHE)->orderBy('id')->get(), 'invoiceDesigns' => InvoiceDesign::remember(DEFAULT_QUERY_CACHE)->orderBy('id')->get(), + 'invoiceLabels' => Auth::user()->account->getInvoiceLabels(), 'frequencies' => array( 1 => 'Weekly', 2 => 'Two weeks', @@ -329,7 +337,15 @@ class InvoiceController extends \BaseController { else { Session::flash('message', 'Successfully saved invoice'.$message); - Session::flash('error', 'Please sign up to email an invoice'); + + if (Auth::user()->registered) + { + Session::flash('error', 'Please confirm your email address'); + } + else + { + Session::flash('error', 'Please sign up to email an invoice'); + } } } else diff --git a/app/controllers/PaymentController.php b/app/controllers/PaymentController.php index 7a9a70d4d7f6..bdf6f4bfd332 100755 --- a/app/controllers/PaymentController.php +++ b/app/controllers/PaymentController.php @@ -18,7 +18,7 @@ class PaymentController extends \BaseController return View::make('list', array( 'entityType'=>ENTITY_PAYMENT, 'title' => '- Payments', - 'columns'=>['checkbox', 'Invoice', 'Client', 'Transaction Reference', 'Method', 'Payment Amount', 'Payment Date', 'Action'] + 'columns'=>Utils::trans(['checkbox', 'invoice', 'client', 'transaction_reference', 'method', 'payment_amount', 'payment_date', 'action']) )); } @@ -46,11 +46,11 @@ class PaymentController extends \BaseController { return ''; }) @@ -115,12 +115,21 @@ class PaymentController extends \BaseController $function = "set" . ucfirst($key); $gateway->$function($val); } + + /* + if (!Utils::isProd()) + { + $gateway->setTestMode(true); + } + */ return $gateway; } private function getPaymentDetails($invoice, $input = null) { + $key = $invoice->invoice_number . '_details'; + if ($input) { $data = [ @@ -142,11 +151,15 @@ class PaymentController extends \BaseController 'shippingPostcode' => $input['postal_code'], ]; - Session::put($invoice->invoice_number . '_details', $data); + Session::put($key, $data); + } + else if (Session::get($key)) + { + $data = Session::get($key); } else { - $data = Session::get($invoice->invoice_number . '_details'); + $data = []; } $card = new CreditCard($data); @@ -162,6 +175,22 @@ class PaymentController extends \BaseController public function show_payment($invitationKey) { + // For PayPal Express we redirect straight to their site + $invitation = Invitation::with('invoice.client.account')->where('invitation_key', '=', $invitationKey)->firstOrFail(); + $account = $invitation->invoice->client->account; + if ($account->isGatewayConfigured(GATEWAY_PAYPAL_EXPRESS)) + { + if (Session::has('error')) + { + Session::reflash(); + return Redirect::to('view/' . $invitationKey); + } + else + { + return self::do_payment($invitationKey, false); + } + } + $invitation = Invitation::with('contact', 'invoice.client')->where('invitation_key', '=', $invitationKey)->firstOrFail(); $invoice = $invitation->invoice; $client = $invoice->client; @@ -177,7 +206,7 @@ class PaymentController extends \BaseController return View::make('payments.payment', $data); } - public function do_payment($invitationKey) + public function do_payment($invitationKey, $onSite = true) { $rules = array( 'first_name' => 'required', @@ -192,20 +221,24 @@ class PaymentController extends \BaseController 'postal_code' => 'required', ); - $validator = Validator::make(Input::all(), $rules); - - if ($validator->fails()) + if ($onSite) { - return Redirect::to('payment/' . $invitationKey) - ->withErrors($validator); - } - else - { - $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail(); - $invoice = $invitation->invoice; - $accountGateway = $invoice->client->account->account_gateways[0]; - $gateway = self::createGateway($accountGateway); + $validator = Validator::make(Input::all(), $rules); + if ($validator->fails()) + { + return Redirect::to('payment/' . $invitationKey) + ->withErrors($validator); + } + } + + $invitation = Invitation::with('invoice.invoice_items', 'invoice.client.currency', 'invoice.client.account.account_gateways.gateway')->where('invitation_key', '=', $invitationKey)->firstOrFail(); + $invoice = $invitation->invoice; + $accountGateway = $invoice->client->account->account_gateways[0]; + $gateway = self::createGateway($accountGateway); + + if ($onSite) + { $client = $invoice->client; $client->address1 = trim(Input::get('address1')); $client->address2 = trim(Input::get('address2')); @@ -213,51 +246,51 @@ class PaymentController extends \BaseController $client->state = trim(Input::get('state')); $client->postal_code = trim(Input::get('postal_code')); $client->save(); + } - try + try + { + $details = self::getPaymentDetails($invoice, Input::all()); + $response = $gateway->purchase($details)->send(); + $ref = $response->getTransactionReference(); + + if (!$ref) { - $details = self::getPaymentDetails($invoice, Input::all()); - $response = $gateway->purchase($details)->send(); - $ref = $response->getTransactionReference(); - - if (!$ref) - { - Session::flash('error', $response->getMessage()); - return Redirect::to('payment/' . $invitationKey) - ->withInput(); - } - - if ($response->isSuccessful()) - { - $payment = self::createPayment($invitation, $ref); - - $invoice->invoice_status_id = INVOICE_STATUS_PAID; - $invoice->save(); - - Event::fire('invoice.paid', $payment); - - Session::flash('message', 'Successfully applied payment'); - return Redirect::to('view/' . $payment->invitation->invitation_key); - } - else if ($response->isRedirect()) - { - $invitation->transaction_reference = $ref; - $invitation->save(); - - $response->redirect(); - } - else - { - Session::flash('error', $response->getMessage()); - return Utils::fatalError('Sorry, there was an error processing your payment. Please try again later.

', $response->getMessage()); - } - } - catch (\Exception $e) - { - Session::flash('error', $e->getMessage()); + Session::flash('error', $response->getMessage()); return Redirect::to('payment/' . $invitationKey) ->withInput(); } + + if ($response->isSuccessful()) + { + $payment = self::createPayment($invitation, $ref); + + $invoice->invoice_status_id = INVOICE_STATUS_PAID; + $invoice->save(); + + Event::fire('invoice.paid', $payment); + + Session::flash('message', 'Successfully applied payment'); + return Redirect::to('view/' . $payment->invitation->invitation_key); + } + else if ($response->isRedirect()) + { + $invitation->transaction_reference = $ref; + $invitation->save(); + + $response->redirect(); + } + else + { + Session::flash('error', $response->getMessage()); + return Utils::fatalError('Sorry, there was an error processing your payment. Please try again later.

', $response->getMessage()); + } + } + catch (\Exception $e) + { + Session::flash('error', $e->getMessage()); + return Redirect::to('payment/' . $invitationKey) + ->withInput(); } } @@ -266,7 +299,7 @@ class PaymentController extends \BaseController $invoice = $invitation->invoice; $accountGateway = $invoice->client->account->account_gateways[0]; - $payment = Payment::createNew(); + $payment = Payment::createNew($invitation); $payment->invitation_id = $invitation->id; $payment->account_gateway_id = $accountGateway->id; $payment->invoice_id = $invoice->id; @@ -309,7 +342,7 @@ class PaymentController extends \BaseController $invoice->invoice_status_id = INVOICE_STATUS_PAID; $invoice->save(); - + Event::fire('invoice.paid', $payment); Session::flash('message', 'Successfully applied payment'); diff --git a/app/controllers/UserController.php b/app/controllers/UserController.php index 91e93febe11f..e49828d85bbe 100755 --- a/app/controllers/UserController.php +++ b/app/controllers/UserController.php @@ -20,6 +20,17 @@ class UserController extends BaseController { return Redirect::to(Input::get('path')); } + public function forcePDFJS() + { + $user = Auth::user(); + $user->force_pdfjs = true; + $user->save(); + + Session::flash('message', 'Successfully updated PDF settings'); + + return Redirect::to('/invoices/create'); + } + /** * Displays the form for account creation * @@ -110,18 +121,18 @@ class UserController extends BaseController { // with the second parameter as true. // logAttempt will check if the 'email' perhaps is the username. // Get the value from the config file instead of changing the controller - if ( Confide::logAttempt( $input, false ) ) + if ( Input::get( 'login_email' ) && Confide::logAttempt( $input, false ) ) { Event::fire('user.login'); // Redirect the user to the URL they were trying to access before // caught by the authentication filter IE Redirect::guest('user/login'). // Otherwise fallback to '/' // Fix pull #145 - return Redirect::intended('/clients'); // change it to '/admin', '/dashboard' or something + return Redirect::intended('/dashboard'); // change it to '/admin', '/dashboard' or something } else { - $user = new User; + //$user = new User; // Check if there was too many login attempts if( Confide::isThrottled( $input ) ) @@ -249,10 +260,13 @@ class UserController extends BaseController { */ public function logout() { - if (!Auth::user()->registered) + if (Auth::check()) { - $account = Auth::user()->account; - $account->forceDelete(); + if (!Auth::user()->registered) + { + $account = Auth::user()->account; + $account->forceDelete(); + } } Confide::logout(); diff --git a/app/database/migrations/2014_03_19_201454_add_language_support.php b/app/database/migrations/2014_03_19_201454_add_language_support.php new file mode 100644 index 000000000000..b48683073e2f --- /dev/null +++ b/app/database/migrations/2014_03_19_201454_add_language_support.php @@ -0,0 +1,57 @@ +increments('id'); + $table->string('name'); + $table->string('locale'); + }); + + DB::table('languages')->insert(['name' => 'English', 'locale' => 'en']); + DB::table('languages')->insert(['name' => 'Italian', 'locale' => 'it']); + DB::table('languages')->insert(['name' => 'German', 'locale' => 'de']); + DB::table('languages')->insert(['name' => 'French', 'locale' => 'fr']); + DB::table('languages')->insert(['name' => 'Brazilian Portuguese', 'locale' => 'pt_BR']); + + Schema::table('accounts', function($table) + { + $table->unsignedInteger('language_id')->default(1); + }); + + DB::table('accounts')->update(['language_id' => 1]); + + Schema::table('accounts', function($table) + { + $table->foreign('language_id')->references('id')->on('languages'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('accounts', function($table) + { + $table->dropForeign('accounts_language_id_foreign'); + $table->dropColumn('language_id'); + }); + + Schema::drop('languages'); + } + +} diff --git a/app/database/migrations/2014_03_23_051736_enable_forcing_jspdf.php b/app/database/migrations/2014_03_23_051736_enable_forcing_jspdf.php new file mode 100644 index 000000000000..30d7c664e451 --- /dev/null +++ b/app/database/migrations/2014_03_23_051736_enable_forcing_jspdf.php @@ -0,0 +1,34 @@ +boolean('force_pdfjs')->default(false); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function($table) + { + $table->dropColumn('force_pdfjs'); + }); + } + +} diff --git a/app/database/seeds/UserTableSeeder.php b/app/database/seeds/UserTableSeeder.php index 3da62963161b..4250450755d8 100755 --- a/app/database/seeds/UserTableSeeder.php +++ b/app/database/seeds/UserTableSeeder.php @@ -5,21 +5,7 @@ class UserTableSeeder extends Seeder public function run() { - //DB::table('users')->delete(); - /* - $account = Account::create(array( - 'name' => 'Acme Inc', - )); - - $user = User::create(array( - 'account_id' => $account->id, - 'first_name' => 'Hillel', - 'last_name' => 'Coren', - 'email' => 'hillelcoren@gmail.com', - 'password' => Hash::make('1234'), - )); - */ } } \ No newline at end of file diff --git a/app/filters.php b/app/filters.php index 10f66bb40347..1cca8a301718 100755 --- a/app/filters.php +++ b/app/filters.php @@ -20,6 +20,11 @@ App::before(function($request) return Redirect::secure(Request::getRequestUri()); } } + + if (Auth::check()) + { + App::setLocale(Auth::user()->getLocale()); + } }); diff --git a/app/handlers/UserEventHandler.php b/app/handlers/UserEventHandler.php index 31753f6a0157..5e6a24955287 100755 --- a/app/handlers/UserEventHandler.php +++ b/app/handlers/UserEventHandler.php @@ -17,11 +17,11 @@ class UserEventHandler public function onLogin() { - $account = Auth::user()->account; - $account->last_login = Carbon::now()->toDateTimeString(); - $account->save(); + $account = Auth::user()->account; + $account->last_login = Carbon::now()->toDateTimeString(); + $account->save(); - Event::fire('user.refresh'); + Event::fire('user.refresh'); } public function onRefresh() diff --git a/app/lang/de/fields.php b/app/lang/de/fields.php new file mode 100644 index 000000000000..d1f9dd7f1c57 --- /dev/null +++ b/app/lang/de/fields.php @@ -0,0 +1,54 @@ + 'Organisation', + 'name' => 'Name', + 'website' => 'Webseite', + 'work_phone' => 'Telefon', + 'address' => 'Adresse', + 'address1' => 'Straße', + 'address2' => 'Adresszusatz', + 'city' => 'Stadt', + 'state' => 'Bundesland', + 'postal_code' => 'Postleitzahl', + 'country_id' => 'Land', + 'contacts' => 'Kontakte', + 'first_name' => 'Vorname', + 'last_name' => 'Nachname', + 'phone' => 'Telefon', + 'email' => 'Email', + 'additional_info' => 'Zusätzliche Info', + 'payment_terms' => 'Zahlungsbedingungen', + 'currency_id' => 'Währung', + 'size_id' => 'Größe', + 'industry_id' => 'Kategorie', + 'private_notes' => 'Notizen', + + // invoice + 'invoice' => 'Rechnung', + 'client' => 'Kunde', + 'invoice_date' => 'Rechnungsdatum', + 'due_date' => 'Fällig am', + 'invoice_number' => 'Rechungsnummer', + 'invoice_number_short' => 'Rechnung #', + 'po_number' => 'Bestell Nummer', + 'po_number_short' => 'BN #', + 'frequency_id' => 'Wie oft', + 'dicount' => 'Rabatt', + 'taxes' => 'Steuern', + 'tax' => 'Steuer', + 'item' => 'Artikel', + 'description' => 'Beschreibung', + 'unit_cost' => 'Kosten pro Einheit', + 'quantity' => 'Menge', + 'line_total' => 'Summe', + 'subtotal' => 'Zwischensumme', + 'paid_to_date' => 'Zahlungsdatum', + 'balance_due' => 'Rechnungsbetrag', + 'invoice_design_id' => 'Design', + 'terms' => 'Bedingungen', + 'your_invoice' => 'Ihre Rechnung', + +); diff --git a/app/lang/de/pagination.php b/app/lang/de/pagination.php new file mode 100644 index 000000000000..5f83d3e960ee --- /dev/null +++ b/app/lang/de/pagination.php @@ -0,0 +1,20 @@ + '« zurück', + + 'next' => 'weiter »', + +); \ No newline at end of file diff --git a/app/lang/de/reminders.php b/app/lang/de/reminders.php new file mode 100644 index 000000000000..6f0a413e0093 --- /dev/null +++ b/app/lang/de/reminders.php @@ -0,0 +1,24 @@ + "Passwörter müssen 6 Zeichen lang sein und korrekt bestätigt werden.", + + "user" => "Wir konnten leider keinen Nutzer mit dieser E-Mail Adresse finden.", + + "token" => "Der Passwort-Wiederherstellungs-Schlüssel ist ungültig.", + + "sent" => "Passworterinnerung wurde gesendet!", + +); diff --git a/app/lang/de/texts.php b/app/lang/de/texts.php new file mode 100644 index 000000000000..b2b291ddaa6a --- /dev/null +++ b/app/lang/de/texts.php @@ -0,0 +1,54 @@ + 'Organisation', + 'name' => 'Name', + 'website' => 'Webseite', + 'work_phone' => 'Telefon', + 'address' => 'Adresse', + 'address1' => 'Straße', + 'address2' => 'Adresszusatz', + 'city' => 'Stadt', + 'state' => 'Bundesland', + 'postal_code' => 'Postleitzahl', + 'country_id' => 'Land', + 'contacts' => 'Kontakte', + 'first_name' => 'Vorname', + 'last_name' => 'Nachname', + 'phone' => 'Telefon', + 'email' => 'Email', + 'additional_info' => 'Zusätzliche Info', + 'payment_terms' => 'Payment Terms', + 'currency_id' => 'Währung', + 'size_id' => 'Größe', + 'industry_id' => 'Kategorie', + 'private_notes' => 'Notizen', + + // invoice + 'invoice' => 'Rechnung', + 'client' => 'Kunde', + 'invoice_date' => 'Rechnungsdatum', + 'due_date' => 'Fällig am', + 'invoice_number' => 'Rechungsnummer', + 'invoice_number_short' => 'Rechnung #', + 'po_number' => 'Bestell Nummer', + 'po_number_short' => 'BN #', + 'frequency_id' => 'Wie oft', + 'dicount' => 'Rabatt', + 'taxes' => 'Steuern', + 'tax' => 'Steuer', + 'item' => 'Artikel', + 'description' => 'Beschreibung', + 'unit_cost' => 'Kosten pro Einheit', + 'quantity' => 'Menge', + 'line_total' => 'Summe', + 'subtotal' => 'Zwischensumme', + 'paid_to_date' => 'Zahlungsdatum', + 'balance_due' => 'Rechnungsbetrag', + 'invoice_design_id' => 'Design', + 'terms' => 'Bedingungen', + 'your_invoice' => 'Ihre Rechnung', + +); diff --git a/app/lang/de/validation.php b/app/lang/de/validation.php new file mode 100644 index 000000000000..4ceff2e3aada --- /dev/null +++ b/app/lang/de/validation.php @@ -0,0 +1,104 @@ + ":attribute muss akzeptiert werden.", + "active_url" => ":attribute ist keine gültige Internet-Adresse.", + "after" => ":attribute muss ein Datum nach dem :date sein.", + "alpha" => ":attribute darf nur aus Buchstaben bestehen.", + "alpha_dash" => ":attribute darf nur aus Buchstaben, Zahlen, Binde- und Unterstrichen bestehen. Umlaute (ä, ö, ü) und Eszett (ß) sind nicht erlaubt.", + "alpha_num" => ":attribute darf nur aus Buchstaben und Zahlen bestehen.", + "array" => ":attribute muss ein Array sein.", + "before" => ":attribute muss ein Datum vor dem :date sein.", + "between" => array( + "numeric" => ":attribute muss zwischen :min & :max liegen.", + "file" => ":attribute muss zwischen :min & :max Kilobytes groß sein.", + "string" => ":attribute muss zwischen :min & :max Zeichen lang sein.", + "array" => ":attribute muss zwischen :min & :max Elemente haben." + ), + "confirmed" => ":attribute stimmt nicht mit der Bestätigung überein.", + "date" => ":attribute muss ein gültiges Datum sein.", + "date_format" => ":attribute entspricht nicht dem gültigen Format für :format.", + "different" => ":attribute und :other müssen sich unterscheiden.", + "digits" => ":attribute muss :digits Stellen haben.", + "digits_between" => ":attribute muss zwischen :min und :max Stellen haben.", + "email" => ":attribute Format ist ungültig.", + "exists" => "Der gewählte Wert für :attribute ist ungültig.", + "image" => ":attribute muss ein Bild sein.", + "in" => "Der gewählte Wert für :attribute ist ungültig.", + "integer" => ":attribute muss eine ganze Zahl sein.", + "ip" => ":attribute muss eine gültige IP-Adresse sein.", + "max" => array( + "numeric" => ":attribute darf maximal :max sein.", + "file" => ":attribute darf maximal :max Kilobytes groß sein.", + "string" => ":attribute darf maximal :max Zeichen haben.", + "array" => ":attribute darf nicht mehr als :max Elemente haben." + ), + "mimes" => ":attribute muss den Dateityp :values haben.", + "min" => array( + "numeric" => ":attribute muss mindestens :min sein.", + "file" => ":attribute muss mindestens :min Kilobytes groß sein.", + "string" => ":attribute muss mindestens :min Zeichen lang sein.", + "array" => ":attribute muss mindestens :min Elemente haben." + ), + "not_in" => "Der gewählte Wert für :attribute ist ungültig.", + "numeric" => ":attribute muss eine Zahl sein.", + "regex" => ":attribute Format ist ungültig.", + "required" => ":attribute muss ausgefüllt sein.", + "required_if" => ":attribute muss ausgefüllt sein wenn :other :value ist.", + "required_with" => ":attribute muss angegeben werden wenn :values ausgefüllt wurde.", + "required_with_all" => "The :attribute field is required when :values is present.", + "required_without" => ":attribute muss angegeben werden wenn :values nicht ausgefüllt wurde.", + "required_without_all" => ":attribute muss angegeben werden wenn keines der Felder :values ausgefüllt wurde.", + "same" => ":attribute und :other müssen übereinstimmen.", + "size" => array( + "numeric" => ":attribute muss gleich :size sein.", + "file" => ":attribute muss :size Kilobyte groß sein.", + "string" => ":attribute muss :size Zeichen lang sein.", + "array" => ":attribute muss genau :size Elemente haben." + ), + "unique" => ":attribute ist schon vergeben.", + "url" => "Das Format von :attribute ist ungültig.", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => array( + 'attribute-name' => array( + 'rule-name' => 'custom-message', + ), + ), + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => array(), + +); diff --git a/app/lang/en/texts.php b/app/lang/en/texts.php new file mode 100644 index 000000000000..a4551f1b1dcf --- /dev/null +++ b/app/lang/en/texts.php @@ -0,0 +1,154 @@ + 'Organization', + 'name' => 'Name', + 'website' => 'Website', + 'work_phone' => 'Phone', + 'address' => 'Address', + 'address1' => 'Street', + 'address2' => 'Apt/Suite', + 'city' => 'City', + 'state' => 'State/Province', + 'postal_code' => 'Postal Code', + 'country_id' => 'Country', + 'contacts' => 'Contacts', + 'first_name' => 'First Name', + 'last_name' => 'Last Name', + 'phone' => 'Phone', + 'email' => 'Email', + 'additional_info' => 'Additional Info', + 'payment_terms' => 'Payment Terms', + 'currency_id' => 'Currency', + 'size_id' => 'Size', + 'industry_id' => 'Industry', + 'private_notes' => 'Private Notes', + + // invoice + 'invoice' => 'Invoice', + 'client' => 'Client', + 'invoice_date' => 'Invoice Date', + 'due_date' => 'Due Date', + 'invoice_number' => 'Invoice Number', + 'invoice_number_short' => 'Invoice #', + 'po_number' => 'PO Number', + 'po_number_short' => 'PO #', + 'frequency_id' => 'How often', + 'discount' => 'Discount', + 'taxes' => 'Taxes', + 'tax' => 'Tax', + 'item' => 'Item', + 'description' => 'Description', + 'unit_cost' => 'Unit Cost', + 'quantity' => 'Quantity', + 'line_total' => 'Line Total', + 'subtotal' => 'Subtotal', + 'paid_to_date' => 'Paid to Date', + 'balance_due' => 'Balance Due', + 'invoice_design_id' => 'Design', + 'terms' => 'Terms', + 'your_invoice' => 'Your Invoice', + + 'remove_contact' => 'Remove contact', + 'add_contact' => 'Add contact', + 'create_new_client' => 'Create new client', + 'edit_client_details' => 'Edit client details', + 'enable' => 'Enable', + 'learn_more' => 'Learn more', + 'manage_rates' => 'Manage rates', + 'note_to_client' => 'Note to client', + 'invoice_terms' => 'Invoice terms', + 'save_as_default_terms' => 'Save as default terms', + 'download_pdf' => 'Download PDF', + 'save_invoice' => 'Save Invoice', + 'clone_invoice' => 'Clone Invoice', + 'archive_invoice' => 'Archive Invoice', + 'delete_invoice' => 'Delete Invoice', + 'email_invoice' => 'Email Invoice', + 'enter_payment' => 'Enter Payment', + 'tax_rates' => 'Tax Rates', + 'rate' => 'Rate', + 'settings' => 'Settings', + 'enable_invoice_tax' => 'Enable specifying an invoice tax', + 'enable_line_item_tax' => 'Enable specifying line item taxes', + + // navigation + 'dashboard' => 'Dashboard', + 'clients' => 'Clients', + 'invoices' => 'Invoices', + 'payments' => 'Payments', + 'credits' => 'Credits', + 'history' => 'History', + 'search' => 'Search', + 'sign_up' => 'Sign Up', + 'guest' => 'Guest', + 'company_details' => 'Company Details', + 'online_payments' => 'Online Payments', + 'notifications' => 'Notifications', + 'import_export' => 'Import/Export', + 'done' => 'Done', + 'cancel' => 'Cancel', + 'provide_email' => 'Please provide a valid email address', + 'powered_by' => 'Powered by', + 'no_items' => 'No items', + + // recurring invoices + 'recurring_invoices' => 'Recurring Invoices', + 'recurring_help' => '

Automatically send clients the same invoices weekly, bi-monthly, monthly, quarterly or annually.

+

Use :MONTH, :QUARTER or :YEAR for dynamic dates. Basic math works as well, for example :MONTH-1.

+

Examples of dynamic invoice variables:

+ ', + + // dashboard + 'in_total_revenue' => 'in total revenue', + 'billed_client' => 'billed client', + 'billed_clients' => 'billed clients', + 'active_client' => 'active client', + 'active_clients' => 'active clients', + 'invoices_past_due' => 'Invoices Past Due', + 'upcoming_invoices' => 'Upcoming invoices', + 'average_invoice' => 'Average invoice', + + // list pages + 'archive' => 'Archive', + 'delete' => 'Delete', + 'archive_client' => 'Archive client', + 'delete_client' => 'Delete client', + 'archive_payment' => 'Archive payment', + 'delete_payment' => 'Delete payment', + 'archive_credit' => 'Archive credit', + 'delete_credit' => 'Delete credit', + 'show_archived_deleted' => 'Show archived/deleted', + 'filter' => 'Filter', + 'new_client' => 'New Client', + 'new_invoice' => 'New Invoice', + 'new_payment' => 'New Payment', + 'new_credit' => 'New Credit', + 'contact' => 'Contact', + 'date_created' => 'Date Created', + 'last_login' => 'Last Login', + 'balance' => 'Balance', + 'action' => 'Action', + 'status' => 'Status', + 'invoice_total' => 'Invoice Total', + 'frequency' => 'Frequency', + 'start_date' => 'Start Date', + 'end_date' => 'End Date', + 'transaction_reference' => 'Transaction Reference', + 'method' => 'Method', + 'payment_amount' => 'Payment Amount', + 'payment_date' => 'Payment Date', + 'credit_amount' => 'Credit Amount', + 'credit_balance' => 'Credit Balance', + 'credit_date' => 'Credit Date', + 'empty_table' => 'No data available in table', + 'select' => 'Select', + 'edit_client' => 'Edit Client', + 'edit_invoice' => 'Edit Invoice', +); \ No newline at end of file diff --git a/app/lang/es/fields.php b/app/lang/es/fields.php new file mode 100644 index 000000000000..c5761739d705 --- /dev/null +++ b/app/lang/es/fields.php @@ -0,0 +1,52 @@ + 'Organización', + 'name' => 'Nombre', + 'website' => 'Página Web', + 'work_phone' => 'Teléfono', + 'address' => 'Dirección', + 'address1' => 'Calle', + 'address2' => 'Bloq/Pta', + 'city' => 'Ciudad', + 'state' => 'Región/Provincia', + 'postal_code' => 'Código Postal', + 'country_id' => 'País', + 'contacts' => 'Contactos', + 'first_name' => 'Nombre', + 'last_name' => 'Apellidos', + 'phone' => 'Teléfono', + 'email' => 'Email', + 'additional_info' => 'Información extra', + 'payment_terms' => 'Términos de pago', + 'currency_id' => 'Divisa', + 'size_id' => 'Tamaño', + 'industry_id' => 'Industria', + 'private_notes' => 'Notas Privadas', + + // invoice + 'invoice' => 'Factura', + 'client' => 'Clienta', + 'invoice_date' => 'Fecha de factura', + 'due_date' => 'Fecha de pago', + 'invoice_number' => 'Número de Factura', + 'invoice_number_short' => 'Nº de Factura', + 'po_number' => 'Apartado de correos', + 'po_number_short' => 'Apdo.', + 'frequency_id' => 'Fracuencia', + 'discount' => 'Descuento', + 'taxes' => 'Impuestos', + 'tax' => 'Impuesto', + 'item' => 'Elemento', + 'description' => 'Descripción', + 'unit_cost' => 'Coste unitario', + 'quantity' => 'Cantidad', + 'line_total' => 'Total línea', + 'subtotal' => 'Subtotal', + 'paid_to_date' => 'Pagado', + 'balance_due' => 'Pendiente', + 'invoice_design_id' => 'Diseño', + 'terms' => 'Términos', + 'your_invoice' => 'Tu factura', +); diff --git a/app/lang/es/messages.php b/app/lang/es/messages.php new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/app/lang/es/pagination.php b/app/lang/es/pagination.php new file mode 100755 index 000000000000..9cbe91da3011 --- /dev/null +++ b/app/lang/es/pagination.php @@ -0,0 +1,20 @@ + '« Anterior', + + 'next' => 'Siguiente »', + +); diff --git a/app/lang/es/reminders.php b/app/lang/es/reminders.php new file mode 100755 index 000000000000..094e8788814b --- /dev/null +++ b/app/lang/es/reminders.php @@ -0,0 +1,24 @@ + "Las contraseñas deben contener al menos 6 caracteres y coincidir.", + + "user" => "No podemos encontrar a un usuario con ese correo electrónico.", + + "token" => "Este token de recuperación de contraseña es inválido.", + + "sent" => "¡Recordatorio de contraseña enviado!", + +); diff --git a/app/lang/es/validation.php b/app/lang/es/validation.php new file mode 100755 index 000000000000..b17accaa163f --- /dev/null +++ b/app/lang/es/validation.php @@ -0,0 +1,107 @@ + ":attribute debe ser aceptado.", + "active_url" => ":attribute no es una URL válida.", + "after" => ":attribute debe ser una fecha posterior a :date.", + "alpha" => ":attribute solo debe contener letras.", + "alpha_dash" => ":attribute solo debe contener letras, números y guiones.", + "alpha_num" => ":attribute solo debe contener letras y números.", + "array" => ":attribute debe ser un conjunto.", + "before" => ":attribute debe ser una fecha anterior a :date.", + "between" => array( + "numeric" => ":attribute tiene que estar entre :min - :max.", + "file" => ":attribute debe pesar entre :min - :max kilobytes.", + "string" => ":attribute tiene que tener entre :min - :max caracteres.", + "array" => ":attribute tiene que tener entre :min - :max ítems.", + ), + "confirmed" => "La confirmación de :attribute no coincide.", + "date" => ":attribute no es una fecha válida.", + "date_format" => ":attribute no corresponde al formato :format.", + "different" => ":attribute y :other deben ser diferentes.", + "digits" => ":attribute debe tener :digits dígitos.", + "digits_between" => ":attribute debe tener entre :min y :max dígitos.", + "email" => ":attribute no es un correo válido", + "exists" => ":attribute es inválido.", + "image" => ":attribute debe ser una imagen.", + "in" => ":attribute es inválido.", + "integer" => ":attribute debe ser un número entero.", + "ip" => ":attribute debe ser una dirección IP válida.", + "max" => array( + "numeric" => ":attribute no debe ser mayor a :max.", + "file" => ":attribute no debe ser mayor que :max kilobytes.", + "string" => ":attribute no debe ser mayor que :max caracteres.", + "array" => ":attribute no debe tener más de :max elementos.", + ), + "mimes" => ":attribute debe ser un archivo con formato: :values.", + "min" => array( + "numeric" => "El tamaño de :attribute debe ser de al menos :min.", + "file" => "El tamaño de :attribute debe ser de al menos :min kilobytes.", + "string" => ":attribute debe contener al menos :min caracteres.", + "array" => ":attribute debe tener al menos :min elementos.", + ), + "not_in" => ":attribute es inválido.", + "numeric" => ":attribute debe ser numérico.", + "regex" => "El formato de :attribute es inválido.", + "required" => "El campo :attribute es obligatorio.", + "required_if" => "El campo :attribute es obligatorio cuando :other es :value.", + "required_with" => "El campo :attribute es obligatorio cuando :values está presente.", + "required_with_all" => "The :attribute field is required when :values is present.", + "required_without" => "El campo :attribute es obligatorio cuando :values no está presente.", + "required_without_all" => "The :attribute field is required when none of :values are present.", + "same" => ":attribute y :other deben coincidir.", + "size" => array( + "numeric" => "El tamaño de :attribute debe ser :size.", + "file" => "El tamaño de :attribute debe ser :size kilobytes.", + "string" => ":attribute debe contener :size caracteres.", + "array" => ":attribute debe contener :size elementos.", + ), + "unique" => ":attribute ya ha sido registrado.", + "url" => "El formato :attribute es inválido.", + "positive" => ":attribute debe ser mayor que cero.", + "has_credit" => "el cliente no tiene crédito suficiente.", + + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => array( + 'attribute-name' => array( + 'rule-name' => 'custom-message', + ), + ), + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => array(), + +); diff --git a/app/lang/fr/pagination.php b/app/lang/fr/pagination.php new file mode 100644 index 000000000000..5f0b1d94f7b0 --- /dev/null +++ b/app/lang/fr/pagination.php @@ -0,0 +1,20 @@ + '« Précédent', + + 'next' => 'Suivant »', + +); \ No newline at end of file diff --git a/app/lang/fr/reminders.php b/app/lang/fr/reminders.php new file mode 100644 index 000000000000..e9f1f353e9d8 --- /dev/null +++ b/app/lang/fr/reminders.php @@ -0,0 +1,24 @@ + "Les mots de passe doivent avoir au moins six caractères et doivent être identiques.", + + "user" => "Nous ne pouvons trouver cet utilisateur avec cette adresse e-mail.", + + "token" => "Ce jeton de réinitialisation du mot de passe n'est pas valide.", + + "sent" => "Rappel du mot de passe envoyé !", + +); \ No newline at end of file diff --git a/app/lang/fr/texts.php b/app/lang/fr/texts.php new file mode 100644 index 000000000000..37dc8e1df81d --- /dev/null +++ b/app/lang/fr/texts.php @@ -0,0 +1,54 @@ + 'Entreprise', + 'name' => 'Nom', + 'website' => 'Site web', + 'work_phone' => 'Téléphone', + 'address' => 'Adresse', + 'address1' => 'Rue', + 'address2' => 'Appt/Batîment', + 'city' => 'Ville', + 'state' => 'Région/Département', + 'postal_code' => 'Code Postal', + 'country_id' => 'Pays', + 'contacts' => 'Informations de contact', //if you speak about contact details + 'first_name' => 'Prénom', + 'last_name' => 'Nom', + 'phone' => 'Téléphone', + 'email' => 'Email', + 'additional_info' => 'Informations complémentaires', + 'payment_terms' => 'Conditions de paiement', + 'currency_id' => 'Devise', + 'size_id' => 'Taille', + 'industry_id' => 'Secteur', // literal translation : Industrie + 'private_notes' => 'Note personnelle', + + // invoice + 'invoice' => 'Facture', + 'client' => 'Client', + 'invoice_date' => 'Date de la facture', + 'due_date' => 'Date d\'échéance', + 'invoice_number' => 'Numéro de facture', + 'invoice_number_short' => 'Facture #', + 'po_number' => 'Numéro du bon de commande', + 'po_number_short' => 'Bon de commande #', + 'frequency_id' => 'Fréquence', //litteral translation : Combien de fois + 'discount' => 'Remise', //can be "rabais" or "réduction" + 'taxes' => 'Taxes', + 'tax' => 'Taxe', + 'item' => 'Ligne', //I'm not sure, I need the context : screenshot ? + 'description' => 'Description', + 'unit_cost' => 'Coût à l\'unité', + 'quantity' => 'Quantité', + 'line_total' => 'Total', + 'subtotal' => 'Total', + 'paid_to_date' => 'Versé à ce jour',//this one is not very used in France + 'balance_due' => 'Montant total',//can be "Montant à verser" or "Somme totale" + 'invoice_design_id' => 'Design', //if you speak about invoice's design -> "Modèle" + 'terms' => 'Conditions', + 'your_invoice' => 'Votre Facture', + +); diff --git a/app/lang/fr/validation.php b/app/lang/fr/validation.php new file mode 100644 index 000000000000..967ffbca1ce1 --- /dev/null +++ b/app/lang/fr/validation.php @@ -0,0 +1,134 @@ + "Le champ :attribute doit être accepté.", + "active_url" => "Le champ :attribute n'est pas une URL valide.", + "after" => "Le champ :attribute doit être une date postérieure au :date.", + "alpha" => "Le champ :attribute doit seulement contenir des lettres.", + "alpha_dash" => "Le champ :attribute doit seulement contenir des lettres, des chiffres et des tirets.", + "alpha_num" => "Le champ :attribute doit seulement contenir des chiffres et des lettres.", + "array" => "Le champ :attribute doit être un tableau.", + "before" => "Le champ :attribute doit être une date antérieure au :date.", + "between" => array( + "numeric" => "La valeur de :attribute doit être comprise entre :min et :max.", + "file" => "Le fichier :attribute doit avoir une taille entre :min et :max kilobytes.", + "string" => "Le texte :attribute doit avoir entre :min et :max caractères.", + "array" => "Le champ :attribute doit avoir entre :min et :max éléments." + ), + "confirmed" => "Le champ de confirmation :attribute ne correspond pas.", + "date" => "Le champ :attribute n'est pas une date valide.", + "date_format" => "Le champ :attribute ne correspond pas au format :format.", + "different" => "Les champs :attribute et :other doivent être différents.", + "digits" => "Le champ :attribute doit avoir :digits chiffres.", + "digits_between" => "Le champ :attribute doit avoir entre :min and :max chiffres.", + "email" => "Le champ :attribute doit être une adresse email valide.", + "exists" => "Le champ :attribute sélectionné est invalide.", + "image" => "Le champ :attribute doit être une image.", + "in" => "Le champ :attribute est invalide.", + "integer" => "Le champ :attribute doit être un entier.", + "ip" => "Le champ :attribute doit être une adresse IP valide.", + "max" => array( + "numeric" => "La valeur de :attribute ne peut être supérieure à :max.", + "file" => "Le fichier :attribute ne peut être plus gros que :max kilobytes.", + "string" => "Le texte de :attribute ne peut contenir plus de :max caractères.", + "array" => "Le champ :attribute ne peut avoir plus de :max éléments.", + ), + "mimes" => "Le champ :attribute doit être un fichier de type : :values.", + "min" => array( + "numeric" => "La valeur de :attribute doit être supérieure à :min.", + "file" => "Le fichier :attribute doit être plus que gros que :min kilobytes.", + "string" => "Le texte :attribute doit contenir au moins :min caractères.", + "array" => "Le champ :attribute doit avoir au moins :min éléments." + ), + "not_in" => "Le champ :attribute sélectionné n'est pas valide.", + "numeric" => "Le champ :attribute doit contenir un nombre.", + "regex" => "Le format du champ :attribute est invalide.", + "required" => "Le champ :attribute est obligatoire.", + "required_if" => "Le champ :attribute est obligatoire quand la valeur de :other est :value.", + "required_with" => "Le champ :attribute est obligatoire quand :values est présent.", + "required_with_all" => "Le champ :attribute est obligatoire quand :values est présent.", + "required_without" => "Le champ :attribute est obligatoire quand :values n'est pas présent.", + "required_without_all" => "Le champ :attribute est requis quand aucun de :values n'est présent.", + "same" => "Les champs :attribute et :other doivent être identiques.", + "size" => array( + "numeric" => "La valeur de :attribute doit être :size.", + "file" => "La taille du fichier de :attribute doit être de :size kilobytes.", + "string" => "Le texte de :attribute doit contenir :size caractères.", + "array" => "Le champ :attribute doit contenir :size éléments." + ), + "unique" => "La valeur du champ :attribute est déjà utilisée.", + "url" => "Le format de l'URL de :attribute n'est pas valide.", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => array( + 'attribute-name' => array( + 'rule-name' => 'custom-message', + ), + ), + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => array( + "name" => "Nom", + "username" => "Pseudo", + "email" => "E-mail", + "first_name" => "Prénom", + "last_name" => "Nom", + "password" => "Mot de passe", + "password_confirmation" => "Confirmation du mot de passe", + "city" => "Ville", + "country" => "Pays", + "address" => "Adresse", + "phone" => "Téléphone", + "mobile" => "Portable", + "age" => "Age", + "sex" => "Sexe", + "gender" => "Genre", + "day" => "Jour", + "month" => "Mois", + "year" => "Année", + "hour" => "Heure", + "minute" => "Minute", + "second" => "Seconde", + "title" => "Titre", + "content" => "Contenu", + "description" => "Description", + "excerpt" => "Extrait", + "date" => "Date", + "time" => "Heure", + "available" => "Disponible", + "size" => "Taille" + ), + +); diff --git a/app/lang/it/pagination.php b/app/lang/it/pagination.php new file mode 100644 index 000000000000..f1e10a7b10e1 --- /dev/null +++ b/app/lang/it/pagination.php @@ -0,0 +1,20 @@ + '« Precedente', + + 'next' => 'Successivo »', + +); \ No newline at end of file diff --git a/app/lang/it/reminders.php b/app/lang/it/reminders.php new file mode 100644 index 000000000000..a7ea42a5a159 --- /dev/null +++ b/app/lang/it/reminders.php @@ -0,0 +1,24 @@ + "Le password devono essere di almeno 6 caratteri e devono coincidere.", + + "user" => "Non esiste un utente associato a questo indirizzo e-mail.", + + "token" => "Questo token per la reimpostazione della password non è valido.", + + "sent" => "Promemoria della password inviato!", + +); diff --git a/app/lang/it/texts.php b/app/lang/it/texts.php new file mode 100644 index 000000000000..10f5d738c39a --- /dev/null +++ b/app/lang/it/texts.php @@ -0,0 +1,54 @@ + 'Organization', + 'name' => 'Name', + 'website' => 'Website', + 'work_phone' => 'Phone', + 'address' => 'Address', + 'address1' => 'Street', + 'address2' => 'Apt/Suite', + 'city' => 'City', + 'state' => 'State/Province', + 'postal_code' => 'Postal Code', + 'country_id' => 'Country', + 'contacts' => 'Contacts', + 'first_name' => 'First Name', + 'last_name' => 'Last Name', + 'phone' => 'Phone', + 'email' => 'Email', + 'additional_info' => 'Additional Info', + 'payment_terms' => 'Payment Terms', + 'currency_id' => 'Currency', + 'size_id' => 'Size', + 'industry_id' => 'Industry', + 'private_notes' => 'Private Notes', + + // invoice + 'invoice' => 'Invoice', + 'client' => 'Client', + 'invoice_date' => 'Invoice Date', + 'due_date' => 'Due Date', + 'invoice_number' => 'Invoice Number', + 'invoice_number_short' => 'Invoice #', + 'po_number' => 'PO Number', + 'po_number_short' => 'PO #', + 'frequency_id' => 'How often', + 'dicount' => 'Discount', + 'taxes' => 'Taxes', + 'tax' => 'Tax', + 'item' => 'Item', + 'description' => 'Description', + 'unit_cost' => 'Unit Cost', + 'quantity' => 'Quantity', + 'line_total' => 'Line Total', + 'subtotal' => 'Subtotal', + 'paid_to_date' => 'Paid to Date', + 'balance_due' => 'Balance Due', + 'invoice_design_id' => 'Design', + 'terms' => 'Terms', + 'your_invoice' => 'Your Invoice', + +); \ No newline at end of file diff --git a/app/lang/it/validation.php b/app/lang/it/validation.php new file mode 100644 index 000000000000..3d2f8ce2987c --- /dev/null +++ b/app/lang/it/validation.php @@ -0,0 +1,103 @@ + ":attribute deve essere accettato.", + "active_url" => ":attribute non è un URL valido.", + "after" => ":attribute deve essere una data successiva al :date.", + "alpha" => ":attribute può contenere solo lettere.", + "alpha_dash" => ":attribute può contenere solo lettere, numeri e trattini.", + "alpha_num" => ":attribute può contenere solo lettere e numeri.", + "array" => ":attribute deve essere un array.", + "before" => ":attribute deve essere una data precedente al :date.", + "between" => array( + "numeric" => ":attribute deve trovarsi tra :min - :max.", + "file" => ":attribute deve trovarsi tra :min - :max kilobytes.", + "string" => ":attribute deve trovarsi tra :min - :max caratteri.", + "array" => ":attribute deve avere tra :min - :max elementi." + ), + "confirmed" => "Il campo di conferma per :attribute non coincide.", + "date" => ":attribute non è una data valida.", + "date_format" => ":attribute non coincide con il formato :format.", + "different" => ":attribute e :other devono essere differenti.", + "digits" => ":attribute deve essere di :digits cifre.", + "digits_between" => ":attribute deve essere tra :min e :max cifre.", + "email" => ":attribute non è valido.", + "exists" => ":attribute selezionato/a non è valido.", + "image" => ":attribute deve essere un'immagine.", + "in" => ":attribute selezionato non è valido.", + "integer" => ":attribute deve essere intero.", + "ip" => ":attribute deve essere un indirizzo IP valido.", + "max" => array( + "numeric" => ":attribute deve essere minore di :max.", + "file" => ":attribute non deve essere più grande di :max kilobytes.", + "string" => ":attribute non può contenere più di :max caratteri.", + "array" => ":attribute non può avere più di :max elementi." + ), + "mimes" => ":attribute deve essere del tipo: :values.", + "min" => array( + "numeric" => ":attribute deve valere almeno :min.", + "file" => ":attribute deve essere più grande di :min kilobytes.", + "string" => ":attribute deve contenere almeno :min caratteri.", + "array" => ":attribute deve avere almeno :min elementi." + ), + "not_in" => "Il valore selezionato per :attribute non è valido.", + "numeric" => ":attribute deve essere un numero.", + "regex" => "Il formato del campo :attribute non è valido.", + "required" => ":attribute è richiesto.", + "required_if" => "Il campo :attribute è richiesto quando :other è :value.", + "required_with" => "Il campo :attribute è richiesto quando :values è presente.", + "required_with_all" => "The :attribute field is required when :values is present.", + "required_without" => "Il campo :attribute è richiesto quando :values non è presente.", + "required_without_all" => "The :attribute field is required when none of :values are present.", + "same" => ":attribute e :other devono coincidere.", + "size" => array( + "numeric" => ":attribute deve valere :size.", + "file" => ":attribute deve essere grande :size kilobytes.", + "string" => ":attribute deve contenere :size caratteri.", + "array" => ":attribute deve contenere :size elementi." + ), + "unique" => ":attribute è stato già utilizzato.", + "url" => ":attribute deve essere un URL.", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => array( + 'attribute-name' => array( + 'rule-name' => 'custom-message', + ), + ), + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as E-Mail Address instead + | of "email". This simply helps us make messages a little cleaner. + | + */ + + 'attributes' => array(), + +); diff --git a/app/lang/pt_BR/pagination.php b/app/lang/pt_BR/pagination.php new file mode 100644 index 000000000000..39b1c02096a4 --- /dev/null +++ b/app/lang/pt_BR/pagination.php @@ -0,0 +1,20 @@ + '« Anterior', + +'next' => 'Próximo »', + +); diff --git a/app/lang/pt_BR/reminders.php b/app/lang/pt_BR/reminders.php new file mode 100644 index 000000000000..b1af622faf82 --- /dev/null +++ b/app/lang/pt_BR/reminders.php @@ -0,0 +1,24 @@ + "Senhas devem possuir no mínimo seis caracteres e devem ser iguais.", + +"user" => "Não achamos um usuário com o endereço de e-mail informado.", + +"token" => "Este token de redefinição de senha é inválido.", + +"sent" => "Lmebrete de senha enviado!", + +); diff --git a/app/lang/pt_BR/texts.php b/app/lang/pt_BR/texts.php new file mode 100644 index 000000000000..a405467cfeb4 --- /dev/null +++ b/app/lang/pt_BR/texts.php @@ -0,0 +1,53 @@ + 'Organização', + 'name' => 'Nome', + 'website' => 'Website', + 'work_phone' => 'Telefone', + 'address' => 'Endereço', + 'address1' => 'Rua', + 'address2' => 'Bloco/apto', + 'city' => 'Cidade', + 'state' => 'Estado', + 'postal_code' => 'CEP', + 'country_id' => 'País', + 'contacts' => 'Contatos', + 'first_name' => 'Primeiro Nome', + 'last_name' => 'Último Nome', + 'phone' => 'Telefone', + 'email' => 'Email', + 'additional_info' => 'Informações Adicionais', + 'payment_terms' => 'Termos de Pagamento', + 'currency_id' => 'Moeda', + 'size_id' => 'Tamanho', + 'industry_id' => 'Empresa', + 'private_notes' => 'Notas Privadas', + + // invoice + 'invoice' => 'Invoice', + 'client' => 'Client', + 'invoice_date' => 'Invoice Date', + 'due_date' => 'Due Date', + 'invoice_number' => 'Invoice Number', + 'invoice_number_short' => 'Invoice #', + 'po_number' => 'PO Number', + 'po_number_short' => 'PO #', + 'frequency_id' => 'How often', + 'discount' => 'Discount', + 'taxes' => 'Taxes', + 'tax' => 'Tax', + 'item' => 'Item', + 'description' => 'Description', + 'unit_cost' => 'Unit Cost', + 'quantity' => 'Quantity', + 'line_total' => 'Line Total', + 'subtotal' => 'Subtotal', + 'paid_to_date' => 'Paid to Date', + 'balance_due' => 'Balance Due', + 'invoice_design_id' => 'Design', + 'terms' => 'Terms', + 'your_invoice' => 'Your Invoice', + +); diff --git a/app/lang/pt_BR/validation.php b/app/lang/pt_BR/validation.php new file mode 100644 index 000000000000..8123efa69524 --- /dev/null +++ b/app/lang/pt_BR/validation.php @@ -0,0 +1,102 @@ + ":attribute deve ser aceito.", +"active_url" => ":attribute não é uma URL válida.", +"after" => ":attribute deve ser uma data maior que :date.", +"alpha" => ":attribute deve conter apenas letras.", +"alpha_dash" => ":attribute pode conter apenas letras, número e traços", +"alpha_num" => ":attribute pode conter apenas letras e números.", +"array" => ":attribute deve ser um array.", +"before" => ":attribute deve ser uma data anterior a :date.", +"between" => array( +"numeric" => ":attribute deve ser entre :min - :max.", +"file" => ":attribute deve ser entre :min - :max kilobytes.", +"string" => ":attribute deve ser entre :min - :max caracteres.", +"array" => ":attribute deve conter entre :min - :max itens.", +), +"confirmed" => ":attribute confirmação não correponde.", +"date" => ":attribute não é uma data válida.", +"date_format" => ":attribute não satisfaz o formato :format.", +"different" => ":attribute e :other devem ser diferentes.", +"digits" => ":attribute deve conter :digits dígitos.", +"digits_between" => ":attribute deve conter entre :min e :max dígitos.", +"email" => ":attribute está em um formato inválido.", +"exists" => "A opção selecionada :attribute é inválida.", +"image" => ":attribute deve ser uma imagem.", +"in" => "A opção selecionada :attribute é inválida.", +"integer" => ":attribute deve ser um número inteiro.", +"ip" => ":attribute deve ser um endereço IP válido.", +"max" => array( +"numeric" => ":attribute não pode ser maior que :max.", +"file" => ":attribute não pode ser maior que :max kilobytes.", +"string" => ":attribute não pode ser maior que :max caracteres.", +"array" => ":attribute não pode conter mais que :max itens.", +), +"mimes" => ":attribute deve ser um arquivo do tipo: :values.", +"min" => array( +"numeric" => ":attribute não deve ser menor que :min.", +"file" => ":attribute deve ter no mínimo :min kilobytes.", +"string" => ":attribute deve conter no mínimo :min caracteres.", +"array" => ":attribute deve conter ao menos :min itens.", +), +"not_in" => "A opção selecionada :attribute é inválida.", +"numeric" => ":attribute deve ser um número.", +"regex" => ":attribute está em um formato inválido.", +"required" => ":attribute é um campo obrigatório.", +"required_if" => ":attribute é necessário quando :other é :value.", +"required_with" => ":attribute é obrigatório quando :values está presente.", +"required_without" => ":attribute é obrigatório quando :values não está presente.", +"same" => ":attribute e :other devem corresponder.", +"size" => array( +"numeric" => ":attribute deve ter :size.", +"file" => ":attribute deve ter :size kilobytes.", +"string" => ":attribute deve conter :size caracteres.", +"array" => ":attribute deve conter :size itens.", +), +"unique" => ":attribute já está sendo utilizado.", +"url" => ":attribute está num formato inválido.", + +"positive" => ":attribute deve ser maior que zero.", +"has_credit" => "O cliente não possui crédito suficiente.", + + +/* +|-------------------------------------------------------------------------- +| Custom Validation Language Lines +|-------------------------------------------------------------------------- +| +| Here you may specify custom validation messages for attributes using the +| convention "attribute.rule" to name the lines. This makes it quick to +| specify a specific custom language line for a given attribute rule. +| +*/ + +'custom' => array(), + +/* +|-------------------------------------------------------------------------- +| Custom Validation Attributes +|-------------------------------------------------------------------------- +| +| The following language lines are used to swap attribute place-holders +| with something more reader friendly such as E-Mail Address instead +| of "email". This simply helps us make messages a little cleaner. +| +*/ + +'attributes' => array(), + +); diff --git a/app/libraries/utils.php b/app/libraries/utils.php index e636682a9843..fa4cf0319c19 100755 --- a/app/libraries/utils.php +++ b/app/libraries/utils.php @@ -2,6 +2,35 @@ class Utils { + public static function isProd() + { + return App::environment() == ENV_PRODUCTION; + } + + public static function basePath() + { + return substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/') + 1); + } + + public static function trans($input) + { + $data = []; + + foreach ($input as $field) + { + if ($field == "checkbox") + { + $data[] = $field; + } + else + { + $data[] = trans("texts.$field"); + } + } + + return $data; + } + public static function fatalError($message = false, $exception = false) { if (!$message) @@ -27,11 +56,10 @@ class Utils 'url' => Input::get('url', Request::url()), 'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '', 'ip' => Request::getClientIp(), - 'count' => Session::get('error_count', 0), - 'input' => Input::all() + 'count' => Session::get('error_count', 0) ]; - Log::error('\n'.$error, $data); + Log::error($error."\n", $data); /* Mail::queue('emails.error', ['message'=>$error.' '.json_encode($data)], function($message) @@ -95,7 +123,8 @@ class Utils public static function pluralize($string, $count) { $string = str_replace('?', $count, $string); - return $count == 1 ? $string : $string . 's'; + $field = $count == 1 ? $string : $string . 's'; + return trans("texts.$field"); } public static function toArray($data) diff --git a/app/models/Account.php b/app/models/Account.php index fb62df505c3d..c69e78137659 100755 --- a/app/models/Account.php +++ b/app/models/Account.php @@ -118,14 +118,22 @@ class Account extends Eloquent public function getLogoWidth() { - list($width, $height) = getimagesize($this->getLogoPath()); + $path = $this->getLogoPath(); + if (!file_exists($path)) { + return 0; + } + list($width, $height) = getimagesize($path); return $width; } public function getLogoHeight() { - list($width, $height) = getimagesize($this->getLogoPath()); - return $height; + $path = $this->getLogoPath(); + if (!file_exists($path)) { + return 0; + } + list($width, $height) = getimagesize($path); + return $height; } public function getNextInvoiceNumber() @@ -158,6 +166,39 @@ class Account extends Eloquent Session::put(SESSION_DATE_FORMAT, $this->date_format ? $this->date_format->format : DEFAULT_DATE_FORMAT); Session::put(SESSION_DATE_PICKER_FORMAT, $this->date_format ? $this->date_format->picker_format : DEFAULT_DATE_PICKER_FORMAT); Session::put(SESSION_DATETIME_FORMAT, $this->datetime_format ? $this->datetime_format->format : DEFAULT_DATETIME_FORMAT); - Session::put(SESSION_CURRENCY, $this->currency_id ? $this->currency_id : DEFAULT_CURRENCY); + Session::put(SESSION_CURRENCY, $this->currency_id ? $this->currency_id : DEFAULT_CURRENCY); } + + public function getInvoiceLabels() + { + $data = []; + $fields = [ + 'invoice', + 'invoice_date', + 'due_date', + 'invoice_number', + 'po_number', + 'discount', + 'taxes', + 'tax', + 'item', + 'description', + 'unit_cost', + 'quantity', + 'line_total', + 'subtotal', + 'paid_to_date', + 'balance_due', + 'terms', + 'your_invoice', + ]; + + foreach ($fields as $field) + { + $data[$field] = trans("texts.$field"); + } + + return $data; + } + } \ No newline at end of file diff --git a/app/models/Activity.php b/app/models/Activity.php index 9a58ab882fde..5cb9b861b3ca 100755 --- a/app/models/Activity.php +++ b/app/models/Activity.php @@ -96,13 +96,13 @@ class Activity extends Eloquent public static function createInvoice($invoice) { - if ($invoice->is_recurring) + if (Auth::check()) { - $message = Utils::encodeActivity(null, 'created recurring', $invoice); + $message = Utils::encodeActivity(Auth::user(), 'created', $invoice); } else { - $message = Utils::encodeActivity(Auth::user(), 'created', $invoice); + $message = Utils::encodeActivity(null, 'created', $invoice); } $client = $invoice->client; @@ -235,9 +235,7 @@ class Activity extends Eloquent $client->last_login = $now; $client->save(); - $activity = new Activity; - $activity->user_id = $invitation->user_id; - $activity->account_id = $invitation->user->account_id; + $activity = Activity::getBlank($invitation); $activity->client_id = $invitation->invoice->client_id; $activity->invitation_id = $invitation->id; $activity->contact_id = $invitation->contact_id; @@ -259,7 +257,7 @@ class Activity extends Eloquent if ($payment->contact_id) { - $activity = new Activity; + $activity = Activity::getBlank($client); $activity->contact_id = $payment->contact_id; $activity->message = Utils::encodeActivity($payment->invitation->contact, 'entered payment'); } diff --git a/app/models/Language.php b/app/models/Language.php new file mode 100755 index 000000000000..182cb510e320 --- /dev/null +++ b/app/models/Language.php @@ -0,0 +1,7 @@ +where('id', '=', $this->account->language_id)->first(); + return $language->locale; + } + public function showGreyBackground() { return !$this->theme_id || in_array($this->theme_id, [2, 3, 5, 6, 7, 8, 10, 11, 12]); } + public function showSignUpPopOver() + { + $count = Session::get(SESSION_COUNTER, 0); + Session::put(SESSION_COUNTER, ++$count); + + return $count == 1 || $count % 7 == 0; + } + public function afterSave($success=true, $forced = false) { if ($this->email) diff --git a/app/ninja/mailers/ContactMailer.php b/app/ninja/mailers/ContactMailer.php index d1c1fed440fb..d25dc657a46b 100755 --- a/app/ninja/mailers/ContactMailer.php +++ b/app/ninja/mailers/ContactMailer.php @@ -37,7 +37,10 @@ class ContactMailer extends Mailer { 'emailFooter' => $invoice->account->email_footer ]; - $this->sendTo($invitation->contact->email, $invitation->user->email, $subject, $view, $data); + $fromEmail = $invitation->user->email; + $fromName = $invitation->user->getDisplayName(); + + $this->sendTo($invitation->contact->email, $fromEmail, $fromName, $subject, $view, $data); Activity::emailInvoice($invitation); } @@ -63,6 +66,7 @@ class ContactMailer extends Mailer { 'paymentAmount' => Utils::formatMoney($payment->amount, $payment->client->currency_id) ]; - $this->sendTo($payment->contact->email, $payment->invitation->user->email, $subject, $view, $data); + $user = $payment->invitation->user; + $this->sendTo($payment->contact->email, $user->email, $user->getDisplayName(), $subject, $view, $data); } } \ No newline at end of file diff --git a/app/ninja/mailers/Mailer.php b/app/ninja/mailers/Mailer.php index f54e450b3e5f..cbc2e7e43998 100755 --- a/app/ninja/mailers/Mailer.php +++ b/app/ninja/mailers/Mailer.php @@ -4,7 +4,7 @@ use Mail; class Mailer { - public function sendTo($toEmail, $fromEmail, $subject, $view, $data = []) + public function sendTo($toEmail, $fromEmail, $fromName, $subject, $view, $data = []) { $views = [ 'emails.'.$view.'_html', @@ -13,9 +13,10 @@ class Mailer { //$view = 'emails.' . $view; - Mail::queue($views, $data, function($message) use ($toEmail, $fromEmail, $subject) + Mail::queue($views, $data, function($message) use ($toEmail, $fromEmail, $fromName, $subject) { - $message->to($toEmail)->replyTo($fromEmail)->subject($subject); + $message->to($toEmail)->from($fromEmail, $fromName)->sender($fromEmail, $fromName) + ->replyTo($fromEmail, $fromName)->returnPath($fromEmail)->subject($subject); }); } } \ No newline at end of file diff --git a/app/ninja/mailers/UserMailer.php b/app/ninja/mailers/UserMailer.php index 2ed34b9bb6d1..4cc7ac65f4bf 100755 --- a/app/ninja/mailers/UserMailer.php +++ b/app/ninja/mailers/UserMailer.php @@ -22,7 +22,7 @@ class UserMailer extends Mailer { 'user' => $user ]; - $this->sendTo($user->email, CONTACT_EMAIL, $subject, $view, $data); + $this->sendTo($user->email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); } public function sendNotification(User $user, Invoice $invoice, $type, Payment $payment = null) @@ -63,6 +63,6 @@ class UserMailer extends Mailer { $subject = "Invoice {$invoice->invoice_number} was $action {$invoice->client->getDisplayName()}"; - $this->sendTo($user->email, CONTACT_EMAIL, $subject, $view, $data); + $this->sendTo($user->email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data); } } \ No newline at end of file diff --git a/app/ninja/repositories/InvoiceRepository.php b/app/ninja/repositories/InvoiceRepository.php index c8327d0311a8..5f31ab7cc2c0 100755 --- a/app/ninja/repositories/InvoiceRepository.php +++ b/app/ninja/repositories/InvoiceRepository.php @@ -50,13 +50,13 @@ class InvoiceRepository { $query = \DB::table('invoices') ->join('clients', 'clients.id', '=','invoices.client_id') - ->join('frequencies', 'frequencies.id', '=', 'invoices.frequency_id') - ->join('contacts', 'contacts.client_id', '=', 'clients.id') - ->where('invoices.account_id', '=', $accountId) + ->join('frequencies', 'frequencies.id', '=', 'invoices.frequency_id') + ->join('contacts', 'contacts.client_id', '=', 'clients.id') + ->where('invoices.account_id', '=', $accountId) ->where('clients.deleted_at', '=', null) ->where('invoices.is_recurring', '=', true) ->where('contacts.is_primary', '=', true) - ->select('clients.public_id as client_public_id', 'clients.name as client_name', 'invoices.public_id', 'amount', 'frequencies.name as frequency', 'start_date', 'end_date', 'clients.currency_id', 'contacts.first_name', 'contacts.last_name', 'contacts.email'); + ->select('clients.public_id as client_public_id', 'clients.name as client_name', 'invoices.public_id', 'amount', 'frequencies.name as frequency', 'start_date', 'end_date', 'clients.currency_id', 'contacts.first_name', 'contacts.last_name', 'contacts.email'); if ($clientPublicId) { diff --git a/app/routes.php b/app/routes.php index 329213dec6da..0109ed9bf9fb 100755 --- a/app/routes.php +++ b/app/routes.php @@ -1,5 +1,6 @@ $binding) - { - if ($binding instanceof \DateTime) - { - $bindings[$i] = $binding->format('\'Y-m-d H:i:s\''); - } - else if (is_string($binding)) - { - $bindings[$i] = "'$binding'"; - } - } - - // Insert bindings into query - $query = str_replace(array('%', '?'), array('%%', '%s'), $query); - $query = vsprintf($query, $bindings); - - Log::info($query, $data); -}); -*/ - -/* -Route::get('/send_emails', function() { - Artisan::call('ninja:send-invoices'); -}); -*/ - Route::get('/', 'HomeController@showWelcome'); Route::get('/rocksteady', 'HomeController@showWelcome'); Route::get('/about', 'HomeController@showAboutUs'); @@ -87,11 +56,13 @@ Route::group(array('before' => 'auth'), function() { Route::get('dashboard', 'DashboardController@index'); Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible'); + Route::get('force_inline_pdf', 'UserController@forcePDFJS'); Route::get('account/getSearchData', array('as' => 'getSearchData', 'uses' => 'AccountController@getSearchData')); Route::get('company/{section?}', 'AccountController@showSection'); Route::post('company/{section?}', 'AccountController@doSection'); Route::post('user/setTheme', 'UserController@setTheme'); + Route::post('remove_logo', 'AccountController@removeLogo'); Route::resource('clients', 'ClientController'); Route::get('api/clients', array('as'=>'api.clients', 'uses'=>'ClientController@getDatatable')); @@ -127,7 +98,7 @@ Route::group(array('before' => 'auth'), function() HTML::macro('nav_link', function($url, $text, $url2 = '', $extra = '') { $class = ( Request::is($url) || Request::is($url.'/*') || Request::is($url2) ) ? ' class="active"' : ''; - return ''.$text.''; + return ''.trans("texts.$text").''; }); HTML::macro('tab_link', function($url, $text, $active = false) { @@ -163,7 +134,17 @@ HTML::macro('image_data', function($imagePath) { HTML::macro('breadcrumbs', function() { $str = '