diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 000000000000..7694ad7822ba --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "./public/vendor" +} \ No newline at end of file diff --git a/.env.example b/.env.example index bd40885edb04..b354269ebf64 100644 --- a/.env.example +++ b/.env.example @@ -2,18 +2,18 @@ APP_ENV=development APP_DEBUG=true APP_URL=http://ninja.dev APP_CIPHER=rijndael-128 -APP_KEY= +APP_KEY DB_TYPE=mysql DB_HOST=localhost DB_DATABASE=ninja -DB_USERNAME= -DB_PASSWORD= +DB_USERNAME +DB_PASSWORD MAIL_DRIVER=smtp MAIL_PORT=587 MAIL_ENCRYPTION=tls -MAIL_HOST= -MAIL_USERNAME= -MAIL_FROM_NAME= -MAIL_PASSWORD= \ No newline at end of file +MAIL_HOST +MAIL_USERNAME +MAIL_FROM_NAME +MAIL_PASSWORD \ No newline at end of file diff --git a/.gitignore b/.gitignore index dd49935a1d7d..7055b9135f86 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ /public/build /public/packages /public/vendor +/storage /bootstrap/compiled.php /bootstrap/environment.php /vendor @@ -24,3 +25,6 @@ public/error_log /ninja.sublime-project /ninja.sublime-workspace auth.json + +.phpstorm.meta.php +_ide_helper.php \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index 8753d476c384..005c733a9d5e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -57,9 +57,14 @@ module.exports = function(grunt) { 'public/vendor/spectrum/spectrum.js', 'public/vendor/jspdf/dist/jspdf.min.js', //'public/vendor/handsontable/dist/jquery.handsontable.full.min.js', + //'public/vendor/pdfmake/build/pdfmake.min.js', + //'public/vendor/pdfmake/build/vfs_fonts.js', + //'public/js/vfs_fonts.js', 'public/js/lightbox.min.js', 'public/js/bootstrap-combobox.js', 'public/js/script.js', + 'public/js/pdf.pdfmake.js', + ], dest: 'public/js/built.js', nonull: true @@ -73,6 +78,7 @@ module.exports = function(grunt) { 'public/js/simpleexpand.js', */ 'public/vendor/bootstrap/dist/js/bootstrap.min.js', + 'public/js/bootstrap-combobox.js', ], dest: 'public/js/built.public.js', @@ -84,7 +90,7 @@ module.exports = function(grunt) { 'public/vendor/datatables/media/css/jquery.dataTables.css', 'public/vendor/datatables-bootstrap3/BS3/assets/css/datatables.css', 'public/vendor/font-awesome/css/font-awesome.min.css', - 'public/vendor/bootstrap-datepicker/css/datepicker.css', + 'public/vendor/bootstrap-datepicker/css/datepicker3.css', 'public/vendor/spectrum/spectrum.css', 'public/css/bootstrap-combobox.css', 'public/css/typeahead.js-bootstrap.css', @@ -105,6 +111,7 @@ module.exports = function(grunt) { 'public/css/bootstrap.splash.css', 'public/css/splash.css', */ + 'public/css/bootstrap-combobox.css', 'public/vendor/datatables/media/css/jquery.dataTables.css', 'public/vendor/datatables-bootstrap3/BS3/assets/css/datatables.css', ], diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000000..eaa9f1e3672c --- /dev/null +++ b/LICENSE @@ -0,0 +1,40 @@ +Attribution Assurance License +Copyright (c) 2014 by Hillel Coren +http://www.hillelcoren.com + +All Rights Reserved +ATTRIBUTION ASSURANCE LICENSE (adapted from the original BSD license) +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the conditions below are met. +These conditions require a modest attribution to InvoiceNinja.com. The hope +is that its promotional value may help justify the thousands of dollars in +otherwise billable time invested in writing this and other freely available, +open-source software. + +1. Redistributions of source code, in whole or part and with or without +modification requires the express permission of the author and must prominently +display "Powered by InvoiceNinja" or the Invoice Ninja logo in verifiable form +with hyperlink to said site. +2. Neither the name nor any trademark of the Author may be used to +endorse or promote products derived from this software without specific +prior written permission. +3. Users are entirely responsible, to the exclusion of the Author and +any other persons, for compliance with (1) regulations set by owners or +administrators of employed equipment, (2) licensing terms of any other +software, and (3) local regulations regarding use, including those +regarding import, export, and use of encryption software. + +THIS FREE SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL THE AUTHOR OR ANY CONTRIBUTOR BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +EFFECTS OF UNAUTHORIZED OR MALICIOUS NETWORK ACCESS; +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/app/Console/Commands/CheckData.php b/app/Console/Commands/CheckData.php index 501f95b43e4f..297431612473 100644 --- a/app/Console/Commands/CheckData.php +++ b/app/Console/Commands/CheckData.php @@ -1,5 +1,8 @@ groupBy('clients.id', 'clients.balance', 'clients.created_at') ->orderBy('clients.id', 'DESC') - ->get(['clients.id', 'clients.balance', 'clients.paid_to_date']); + ->get(['clients.account_id', 'clients.id', 'clients.balance', 'clients.paid_to_date', DB::raw('sum(invoices.balance) actual_balance')]); $this->info(count($clients) . ' clients with incorrect balance/activities'); foreach ($clients as $client) { - $this->info("=== Client:{$client->id} Balance:{$client->balance} ==="); + $this->info("=== Client:{$client->id} Balance:{$client->balance} Actual Balance:{$client->actual_balance} ==="); $foundProblem = false; $lastBalance = 0; + $lastAdjustment = 0; + $lastCreatedAt = null; $clientFix = false; $activities = DB::table('activities') ->where('client_id', '=', $client->id) @@ -195,6 +200,11 @@ class CheckData extends Command { $foundProblem = true; $clientFix -= $activity->adjustment; $activityFix = 0; + } else if ((strtotime($activity->created_at) - strtotime($lastCreatedAt) <= 1) && $activity->adjustment > 0 && $activity->adjustment == $lastAdjustment) { + $this->info("Duplicate adjustment for updated invoice adjustment:{$activity->adjustment}"); + $foundProblem = true; + $clientFix -= $activity->adjustment; + $activityFix = 0; } } elseif ($activity->activity_type_id == ACTIVITY_TYPE_UPDATE_QUOTE) { // **Fix for updating balance when updating a quote** @@ -231,18 +241,32 @@ class CheckData extends Command { } $lastBalance = $activity->balance; + $lastAdjustment = $activity->adjustment; + $lastCreatedAt = $activity->created_at; } - if ($clientFix !== false) { - $balance = $activity->balance + $clientFix; - $data = ['balance' => $balance]; - $this->info("Corrected balance:{$balance}"); + if ($activity->balance + $clientFix != $client->actual_balance) { + $this->info("** Creating 'recovered update' activity **"); if ($this->option('fix') == 'true') { - DB::table('clients') - ->where('id', $client->id) - ->update($data); + DB::table('activities')->insert([ + 'created_at' => new Carbon, + 'updated_at' => new Carbon, + 'account_id' => $client->account_id, + 'client_id' => $client->id, + 'message' => 'Recovered update to invoice [details]', + 'adjustment' => $client->actual_balance - $activity->balance, + 'balance' => $client->actual_balance, + ]); } } + + $data = ['balance' => $client->actual_balance]; + $this->info("Corrected balance:{$client->actual_balance}"); + if ($this->option('fix') == 'true') { + DB::table('clients') + ->where('id', $client->id) + ->update($data); + } } $this->info('Done'); diff --git a/app/Console/Commands/SendRecurringInvoices.php b/app/Console/Commands/SendRecurringInvoices.php index a5fe2cdaf9b9..aefc79bda752 100644 --- a/app/Console/Commands/SendRecurringInvoices.php +++ b/app/Console/Commands/SendRecurringInvoices.php @@ -69,8 +69,12 @@ class SendRecurringInvoices extends Command $invoice->custom_taxes2 = $recurInvoice->custom_taxes2; $invoice->is_amount_discount = $recurInvoice->is_amount_discount; - if ($invoice->client->payment_terms) { - $invoice->due_date = date_create()->modify($invoice->client->payment_terms.' day')->format('Y-m-d'); + if ($invoice->client->payment_terms != 0) { + $days = $invoice->client->payment_terms; + if ($days == -1) { + $days = 0; + } + $invoice->due_date = date_create()->modify($days.' day')->format('Y-m-d'); } $invoice->save(); diff --git a/app/Console/Commands/SendRenewalInvoices.php b/app/Console/Commands/SendRenewalInvoices.php new file mode 100644 index 000000000000..ceae80e611dc --- /dev/null +++ b/app/Console/Commands/SendRenewalInvoices.php @@ -0,0 +1,57 @@ +mailer = $mailer; + $this->accountRepo = $repo; + } + + public function fire() + { + $this->info(date('Y-m-d').' Running SendRenewalInvoices...'); + $today = new DateTime(); + + $accounts = Account::whereRaw('datediff(curdate(), pro_plan_paid) = 355')->get(); + $this->info(count($accounts).' accounts found'); + dd(0); + foreach ($accounts as $account) { + $client = $this->accountRepo->getNinjaClient($account); + $invitation = $this->accountRepo->createNinjaInvoice($client); + $this->mailer->sendInvoice($invitation->invoice); + } + + $this->info('Done'); + } + + protected function getArguments() + { + return array( + //array('example', InputArgument::REQUIRED, 'An example argument.'), + ); + } + + protected function getOptions() + { + return array( + //array('example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null), + ); + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index ddf38a4a3065..64d68d6f4642 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -16,6 +16,7 @@ class Kernel extends ConsoleKernel { 'App\Console\Commands\ResetData', 'App\Console\Commands\ImportTimesheetData', 'App\Console\Commands\CheckData', + 'App\Console\Commands\SendRenewalInvoices', ]; /** diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index 1475cbb5cb09..084f74fcc498 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -27,6 +27,7 @@ class Handler extends ExceptionHandler { { Utils::logError(Utils::getErrorString($e)); return false; + //return parent::report($e); } @@ -39,6 +40,15 @@ class Handler extends ExceptionHandler { */ public function render($request, Exception $e) { - return parent::render($request, $e); + if (Utils::isNinjaProd()) { + $data = [ + 'error' => get_class($e), + 'hideHeader' => true, + ]; + + return response()->view('error', $data); + } else { + return parent::render($request, $e); + } } } diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index b80a59b86450..e67102df2b7a 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -12,9 +12,20 @@ use Validator; use View; use stdClass; use Cache; +use Response; +use parseCSV; +use Request; +use App\Models\Affiliate; +use App\Models\License; use App\Models\User; +use App\Models\Client; +use App\Models\Contact; +use App\Models\Invoice; +use App\Models\InvoiceItem; use App\Models\Activity; +use App\Models\Payment; +use App\Models\Credit; use App\Models\Account; use App\Models\Country; use App\Models\Currency; @@ -90,7 +101,7 @@ class AccountController extends BaseController Auth::login($user, true); Event::fire(new UserLoggedIn()); - + return Redirect::to('invoices/create')->with('sign_up', Input::get('sign_up')); } @@ -153,7 +164,7 @@ class AccountController extends BaseController if ($count == 0) { return Redirect::to('gateways/create'); } else { - return View::make('accounts.payments', ['showAdd' => $count < 2]); + return View::make('accounts.payments', ['showAdd' => $count < 3]); } } elseif ($section == ACCOUNT_NOTIFICATIONS) { $data = [ @@ -197,7 +208,7 @@ class AccountController extends BaseController $invoice->invoice_items = [$invoiceItem]; $data['invoice'] = $invoice; - $data['invoiceDesigns'] = InvoiceDesign::where('id', '<=', Auth::user()->maxInvoiceDesignId())->orderBy('id')->get(); + $data['invoiceDesigns'] = InvoiceDesign::availableDesigns(); } else if ($subSection == ACCOUNT_EMAIL_TEMPLATES) { $data['invoiceEmail'] = $account->getEmailTemplate(ENTITY_INVOICE); $data['quoteEmail'] = $account->getEmailTemplate(ENTITY_QUOTE); @@ -291,6 +302,8 @@ class AccountController extends BaseController $account->share_counter = Input::get('share_counter') ? true : false; $account->pdf_email_attachment = Input::get('pdf_email_attachment') ? true : false; + $account->utf8_invoices = Input::get('utf8_invoices') ? true : false; + $account->auto_wrap = Input::get('auto_wrap') ? true : false; if (!$account->share_counter) { $account->quote_number_counter = Input::get('quote_number_counter'); @@ -333,40 +346,27 @@ class AccountController extends BaseController header('Content-Disposition:attachment;filename=export.csv'); $clients = Client::scope()->get(); - AccountController::exportData($output, $clients->toArray()); + Utils::exportData($output, $clients->toArray()); $contacts = Contact::scope()->get(); - AccountController::exportData($output, $contacts->toArray()); + Utils::exportData($output, $contacts->toArray()); $invoices = Invoice::scope()->get(); - AccountController::exportData($output, $invoices->toArray()); + Utils::exportData($output, $invoices->toArray()); $invoiceItems = InvoiceItem::scope()->get(); - AccountController::exportData($output, $invoiceItems->toArray()); + Utils::exportData($output, $invoiceItems->toArray()); $payments = Payment::scope()->get(); - AccountController::exportData($output, $payments->toArray()); + Utils::exportData($output, $payments->toArray()); $credits = Credit::scope()->get(); - AccountController::exportData($output, $credits->toArray()); + Utils::exportData($output, $credits->toArray()); fclose($output); exit; } - private function exportData($output, $data) - { - if (count($data) > 0) { - fputcsv($output, array_keys($data[0])); - } - - foreach ($data as $record) { - fputcsv($output, $record); - } - - fwrite($output, "\n"); - } - private function importFile() { $data = Session::get('data'); @@ -574,6 +574,14 @@ class AccountController extends BaseController $rules['email'] = 'email|required|unique:users,email,'.$user->id.',id'; } + $subdomain = preg_replace('/[^a-zA-Z0-9_\-]/', '', substr(strtolower(Input::get('subdomain')), 0, MAX_SUBDOMAIN_LENGTH)); + if (!$subdomain || in_array($subdomain, ['www', 'app', 'mail', 'admin', 'blog', 'user', 'contact', 'payment', 'payments', 'billing', 'invoice', 'business', 'owner'])) { + $subdomain = null; + } + if ($subdomain) { + $rules['subdomain'] = "unique:accounts,subdomain,{$user->account_id},id"; + } + $validator = Validator::make(Input::all(), $rules); if ($validator->fails()) { @@ -583,6 +591,7 @@ class AccountController extends BaseController } else { $account = Auth::user()->account; $account->name = trim(Input::get('name')); + $account->subdomain = $subdomain; $account->id_number = trim(Input::get('id_number')); $account->vat_number = trim(Input::get('vat_number')); $account->work_email = trim(Input::get('work_email')); diff --git a/app/Http/Controllers/AccountGatewayController.php b/app/Http/Controllers/AccountGatewayController.php index 489b2fb2d65c..29002f8727b1 100644 --- a/app/Http/Controllers/AccountGatewayController.php +++ b/app/Http/Controllers/AccountGatewayController.php @@ -25,10 +25,11 @@ class AccountGatewayController extends BaseController ->join('gateways', 'gateways.id', '=', 'account_gateways.gateway_id') ->where('account_gateways.deleted_at', '=', null) ->where('account_gateways.account_id', '=', Auth::user()->account_id) - ->select('account_gateways.public_id', 'gateways.name', 'account_gateways.deleted_at'); + ->select('account_gateways.public_id', 'gateways.name', 'account_gateways.deleted_at', 'account_gateways.gateway_id'); return Datatable::query($query) ->addColumn('name', function ($model) { return link_to('gateways/'.$model->public_id.'/edit', $model->name); }) + ->addColumn('payment_type', function ($model) { return Gateway::getPrettyPaymentType($model->gateway_id); }) ->addColumn('dropdown', function ($model) { $actions = ' -
-
-
+
+
+
- {!! Former::legend('organization') !!} - {!! Former::text('name')->data_bind("value: name, valueUpdate: 'afterkeydown', attr { placeholder: name.placeholder }") !!} - {!! Former::text('id_number')->data_bind("value: id_number, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('vat_number')->data_bind("value: vat_number, valueUpdate: 'afterkeydown'") !!} - - {!! Former::text('website')->data_bind("value: website, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('work_phone')->data_bind("value: work_phone, valueUpdate: 'afterkeydown'") !!} +
+
- @if (Auth::user()->isPro()) - @if ($account->custom_client_label1) - {!! Former::text('custom_value1')->label($account->custom_client_label1) - ->data_bind("value: custom_value1, valueUpdate: 'afterkeydown'") !!} - @endif - @if ($account->custom_client_label2) - {!! Former::text('custom_value2')->label($account->custom_client_label2) - ->data_bind("value: custom_value2, valueUpdate: 'afterkeydown'") !!} - @endif - @endif - - {!! Former::legend('address') !!} - {!! Former::text('address1')->data_bind("value: address1, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('address2')->data_bind("value: address2, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('city')->data_bind("value: city, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('state')->data_bind("value: state, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('postal_code')->data_bind("value: postal_code, valueUpdate: 'afterkeydown'") !!} - {!! Former::select('country_id')->addOption('','')->addGroupClass('country_select') - ->fromQuery($countries, 'name', 'id')->data_bind("dropdown: country_id") !!} - -
-
+ {!! Former::text('name')->data_bind("value: name, valueUpdate: 'afterkeydown', attr { placeholder: name.placeholder }")->label('client_name') !!} + + {!! Former::text('id_number')->data_bind("value: id_number, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('vat_number')->data_bind("value: vat_number, valueUpdate: 'afterkeydown'") !!} + + {!! Former::text('website')->data_bind("value: website, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('work_phone')->data_bind("value: work_phone, valueUpdate: 'afterkeydown'") !!} + @if (Auth::user()->isPro()) + @if ($account->custom_client_label1) + {!! Former::text('custom_value1')->label($account->custom_client_label1) + ->data_bind("value: custom_value1, valueUpdate: 'afterkeydown'") !!} + @endif + @if ($account->custom_client_label2) + {!! Former::text('custom_value2')->label($account->custom_client_label2) + ->data_bind("value: custom_value2, valueUpdate: 'afterkeydown'") !!} + @endif + @endif - {!! Former::legend('contacts') !!} -
- {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown'") !!} - {!! Former::text('email')->data_bind('value: email, valueUpdate: \'afterkeydown\', attr: {id:\'email\'+$index()}') !!} - {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown'") !!} +   -
-
- - {!! link_to('#', trans('texts.remove_contact').' -', array('data-bind'=>'click: $parent.removeContact')) !!} - - - {!! link_to('#', trans('texts.add_contact').' +', array('data-bind'=>'click: $parent.addContact')) !!} - -
-
-
+ {!! Former::text('address1')->data_bind("value: address1, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('address2')->data_bind("value: address2, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('city')->data_bind("value: city, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('state')->data_bind("value: state, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('postal_code')->data_bind("value: postal_code, valueUpdate: 'afterkeydown'") !!} + {!! Former::select('country_id')->addOption('','')->addGroupClass('country_select') + ->fromQuery($countries, 'name', 'id')->data_bind("dropdown: country_id") !!} +
- {!! Former::legend('additional_info') !!} - {!! Former::select('payment_terms')->addOption('','0')->data_bind('value: payment_terms') - ->fromQuery($paymentTerms, 'name', 'num_days') !!} - {!! Former::select('currency_id')->addOption('','')->data_bind('value: currency_id') - ->fromQuery($currencies, 'name', 'id') !!} - {!! Former::select('size_id')->addOption('','')->data_bind('value: size_id') - ->fromQuery($sizes, 'name', 'id') !!} - {!! Former::select('industry_id')->addOption('','')->data_bind('value: industry_id') - ->fromQuery($industries, 'name', 'id') !!} - {!! Former::textarea('private_notes')->data_bind('value: private_notes') !!} +
+
+
+ {!! Former::hidden('public_id')->data_bind("value: public_id, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('first_name')->data_bind("value: first_name, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown'") !!} + {!! Former::text('email')->data_bind('value: email, valueUpdate: \'afterkeydown\', attr: {id:\'email\'+$index()}') !!} + {!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown'") !!} +
+
+ + {!! link_to('#', trans('texts.remove_contact').' -', array('data-bind'=>'click: $parent.removeContact')) !!} + + + {!! link_to('#', trans('texts.add_contact').' +', array('data-bind'=>'click: $parent.addContact')) !!} + +
+
+
-
-
-
+ +   + - - -
-
-
+ {!! Former::select('currency_id')->addOption('','')->data_bind('value: currency_id') + ->fromQuery($currencies, 'name', 'id') !!} + + + {!! Former::select('payment_terms')->addOption('','')->data_bind('value: payment_terms') + ->fromQuery($paymentTerms, 'name', 'num_days') !!} + {!! Former::select('size_id')->addOption('','')->data_bind('value: size_id') + ->fromQuery($sizes, 'name', 'id') !!} + {!! Former::select('industry_id')->addOption('','')->data_bind('value: industry_id') + ->fromQuery($industries, 'name', 'id') !!} + {!! Former::textarea('private_notes')->data_bind('value: private_notes') !!} + +
+
+ + + + + + + + + + @if (Auth::user()->account->utf8_invoices) + + + @endif + + @if ($invoice->client->account->utf8_invoices) + + + @endif + + @@ -23,7 +30,7 @@ @if (!extension_loaded('fileinfo'))
Warning: The fileinfo extension needs to be installed and enabled.
@endif - @if (!@fopen(base_path()."/.env", 'w')) + @if (!@fopen(base_path()."/.env", 'a'))
Warning: Permission denied to write config file
sudo chown yourname:www-data /path/to/ninja
diff --git a/resources/views/users/edit.blade.php b/resources/views/users/edit.blade.php index 7e67dd508b64..164c9afe5ce2 100644 --- a/resources/views/users/edit.blade.php +++ b/resources/views/users/edit.blade.php @@ -10,16 +10,23 @@ 'email' => 'required|email', )); !!} - {!! Former::legend($title) !!} - @if ($user) {!! Former::populate($user) !!} @endif +
+
+

{!! $title !!}

+
+
+ {!! Former::text('first_name') !!} {!! Former::text('last_name') !!} {!! Former::text('email') !!} +
+
+ {!! Former::actions( Button::success(trans($user && $user->confirmed ? 'texts.save' : 'texts.send_invite'))->submit()->large()->appendIcon(Icon::create($user && $user->confirmed ? 'floppy-disk' : 'send')), Button::normal(trans('texts.cancel'))->asLinkTo('/company/advanced_settings/user_management')->appendIcon(Icon::create('remove-circle'))->large() @@ -27,4 +34,8 @@ {!! Former::close() !!} +@stop + +@section('onReady') + $('#first_name').focus(); @stop \ No newline at end of file diff --git a/storage/pdfcache/.gitignore b/storage/pdfcache/.gitignore new file mode 100755 index 000000000000..c96a04f008ee --- /dev/null +++ b/storage/pdfcache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file