This commit is contained in:
David Bomba 2015-11-05 07:18:03 +11:00
commit a7f2103a88
38 changed files with 386 additions and 7483 deletions

View File

@ -49,14 +49,8 @@ class AccountApiController extends BaseAPIController
// Create a new token only if one does not already exist // Create a new token only if one does not already exist
$this->accountRepo->createTokens(Auth::user(), $request->token_name); $this->accountRepo->createTokens(Auth::user(), $request->token_name);
return $this->index();
}
public function index()
{
$users = $this->accountRepo->findUsers(Auth::user(), 'account.account_tokens'); $users = $this->accountRepo->findUsers(Auth::user(), 'account.account_tokens');
$resource = new Collection($users, new UserAccountTransformer($request->token_name));
$resource = new Collection($users, new UserAccountTransformer);
return $this->returnData($resource); return $this->returnData($resource);
} }

View File

@ -38,6 +38,7 @@ use App\Models\Industry;
use App\Models\InvoiceDesign; use App\Models\InvoiceDesign;
use App\Models\TaxRate; use App\Models\TaxRate;
use App\Ninja\Repositories\AccountRepository; use App\Ninja\Repositories\AccountRepository;
use App\Ninja\Repositories\ClientRepository;
use App\Ninja\Repositories\ReferralRepository; use App\Ninja\Repositories\ReferralRepository;
use App\Ninja\Mailers\UserMailer; use App\Ninja\Mailers\UserMailer;
use App\Ninja\Mailers\ContactMailer; use App\Ninja\Mailers\ContactMailer;
@ -174,6 +175,8 @@ class AccountController extends BaseController
return self::showProducts(); return self::showProducts();
} elseif ($section === ACCOUNT_TAX_RATES) { } elseif ($section === ACCOUNT_TAX_RATES) {
return self::showTaxRates(); return self::showTaxRates();
} elseif ($section === ACCOUNT_SYSTEM_SETTINGS) {
return self::showSystemSettings();
} else { } else {
$data = [ $data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id), 'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
@ -184,6 +187,21 @@ class AccountController extends BaseController
} }
} }
private function showSystemSettings()
{
if (Utils::isNinjaProd()) {
return Redirect::to('/');
}
$data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'title' => trans("texts.system_settings"),
'section' => ACCOUNT_SYSTEM_SETTINGS,
];
return View::make("accounts.system_settings", $data);
}
private function showInvoiceSettings() private function showInvoiceSettings()
{ {
$account = Auth::user()->account; $account = Auth::user()->account;
@ -693,7 +711,8 @@ class AccountController extends BaseController
continue; continue;
} }
$this->dispatch(new CreateClient($data)); $clientRepository = new ClientRepository();
$clientRepository->save($data);
$count++; $count++;
} }

View File

@ -48,7 +48,7 @@ class AppController extends BaseController
public function doSetup() public function doSetup()
{ {
if (Utils::isNinjaProd() || (Utils::isDatabaseSetup() && Account::count() > 0)) { if (Utils::isNinjaProd()) {
return Redirect::to('/'); return Redirect::to('/');
} }
@ -57,9 +57,10 @@ class AppController extends BaseController
$app = Input::get('app'); $app = Input::get('app');
$app['key'] = env('APP_KEY') ?: str_random(RANDOM_KEY_LENGTH); $app['key'] = env('APP_KEY') ?: str_random(RANDOM_KEY_LENGTH);
$app['debug'] = Input::get('debug') ? 'true' : 'false';
$database = Input::get('database'); $database = Input::get('database');
$dbType = $database['default']; $dbType = 'mysql'; // $database['default'];
$database['connections'] = [$dbType => $database['type']]; $database['connections'] = [$dbType => $database['type']];
$mail = Input::get('mail'); $mail = Input::get('mail');
@ -78,8 +79,12 @@ class AppController extends BaseController
return Redirect::to('/setup')->withInput(); return Redirect::to('/setup')->withInput();
} }
if (Utils::isDatabaseSetup() && Account::count() > 0) {
return Redirect::to('/');
}
$config = "APP_ENV=production\n". $config = "APP_ENV=production\n".
"APP_DEBUG=false\n". "APP_DEBUG={$app['debug']}\n".
"APP_URL={$app['url']}\n". "APP_URL={$app['url']}\n".
"APP_KEY={$app['key']}\n\n". "APP_KEY={$app['key']}\n\n".
"DB_TYPE={$dbType}\n". "DB_TYPE={$dbType}\n".
@ -120,17 +125,68 @@ class AppController extends BaseController
return Redirect::to('/login'); return Redirect::to('/login');
} }
public function updateSetup()
{
if (Utils::isNinjaProd()) {
return Redirect::to('/');
}
if (!Auth::check() && Utils::isDatabaseSetup() && Account::count() > 0) {
return Redirect::to('/');
}
if ( ! $canUpdateEnv = @fopen(base_path()."/.env", 'w')) {
Session::flash('error', 'Warning: Permission denied to write to .env config file, try running <code>sudo chown www-data:www-data /path/to/ninja/.env</code>');
return Redirect::to('/settings/system_settings');
}
$app = Input::get('app');
$db = Input::get('database');
$mail = Input::get('mail');
$_ENV['APP_URL'] = $app['url'];
$_ENV['APP_DEBUG'] = Input::get('debug') ? 'true' : 'false';
$_ENV['DB_TYPE'] = 'mysql'; // $db['default'];
$_ENV['DB_HOST'] = $db['type']['host'];
$_ENV['DB_DATABASE'] = $db['type']['database'];
$_ENV['DB_USERNAME'] = $db['type']['username'];
$_ENV['DB_PASSWORD'] = $db['type']['password'];
if ($mail) {
$_ENV['MAIL_DRIVER'] = $mail['driver'];
$_ENV['MAIL_PORT'] = $mail['port'];
$_ENV['MAIL_ENCRYPTION'] = $mail['encryption'];
$_ENV['MAIL_HOST'] = $mail['host'];
$_ENV['MAIL_USERNAME'] = $mail['username'];
$_ENV['MAIL_FROM_NAME'] = $mail['from']['name'];
$_ENV['MAIL_PASSWORD'] = $mail['password'];
$_ENV['MAIL_FROM_ADDRESS'] = $mail['username'];
}
$config = '';
foreach ($_ENV as $key => $val) {
$config .= "{$key}={$val}\n";
}
$fp = fopen(base_path()."/.env", 'w');
fwrite($fp, $config);
fclose($fp);
Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('/settings/system_settings');
}
private function testDatabase($database) private function testDatabase($database)
{ {
$dbType = $database['default']; $dbType = 'mysql'; // $database['default'];
Config::set('database.default', $dbType); Config::set('database.default', $dbType);
foreach ($database['connections'][$dbType] as $key => $val) { foreach ($database['connections'][$dbType] as $key => $val) {
Config::set("database.connections.{$dbType}.{$key}", $val); Config::set("database.connections.{$dbType}.{$key}", $val);
} }
try { try {
DB::reconnect();
$valid = DB::connection()->getDatabaseName() ? true : false; $valid = DB::connection()->getDatabaseName() ? true : false;
} catch (Exception $e) { } catch (Exception $e) {
return $e->getMessage(); return $e->getMessage();

View File

@ -4,6 +4,7 @@ use Datatable;
use Input; use Input;
use Redirect; use Redirect;
use Session; use Session;
use URL;
use Utils; use Utils;
use View; use View;
use Validator; use Validator;
@ -67,7 +68,9 @@ class CreditController extends BaseController
<ul class="dropdown-menu" role="menu">'; <ul class="dropdown-menu" role="menu">';
if (!$model->deleted_at || $model->deleted_at == '0000-00-00') { if (!$model->deleted_at || $model->deleted_at == '0000-00-00') {
$str .= '<li><a href="javascript:archiveEntity('.$model->public_id.')">'.trans('texts.archive_credit').'</a></li>'; $str .= '<li><a href="'.URL::to('payments/create/'.$model->client_public_id).'?paymentTypeId=1">'.trans('texts.apply_credit').'</a></li>
<li class="divider"></li>
<li><a href="javascript:archiveEntity('.$model->public_id.')">'.trans('texts.archive_credit').'</a></li>';
} else { } else {
$str .= '<li><a href="javascript:restoreEntity('.$model->public_id.')">'.trans('texts.restore_credit').'</a></li>'; $str .= '<li><a href="javascript:restoreEntity('.$model->public_id.')">'.trans('texts.restore_credit').'</a></li>';
} }

View File

@ -118,6 +118,7 @@ class PaymentController extends BaseController
'url' => "payments", 'url' => "payments",
'title' => trans('texts.new_payment'), 'title' => trans('texts.new_payment'),
'paymentTypes' => Cache::get('paymentTypes'), 'paymentTypes' => Cache::get('paymentTypes'),
'paymentTypeId' => Input::get('paymentTypeId'),
'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), ); 'clients' => Client::scope()->with('contacts')->orderBy('name')->get(), );
return View::make('payments.edit', $data); return View::make('payments.edit', $data);

View File

@ -29,6 +29,7 @@ class SaveInvoiceRequest extends Request
$rules = [ $rules = [
'client.contacts' => 'valid_contacts', 'client.contacts' => 'valid_contacts',
'invoice_items' => 'valid_invoice_items',
'invoice_number' => 'required|unique:invoices,invoice_number,'.$invoiceId.',id,account_id,'.Auth::user()->account_id, 'invoice_number' => 'required|unique:invoices,invoice_number,'.$invoiceId.',id,account_id,'.Auth::user()->account_id,
'discount' => 'positive', 'discount' => 'positive',
]; ];

View File

@ -184,6 +184,7 @@ Route::group(['middleware' => 'auth'], function() {
Route::post('credits/bulk', 'CreditController@bulk'); Route::post('credits/bulk', 'CreditController@bulk');
get('/resend_confirmation', 'AccountController@resendConfirmation'); get('/resend_confirmation', 'AccountController@resendConfirmation');
post('/update_setup', 'AppController@updateSetup');
}); });
// Route groups for API // Route groups for API
@ -278,6 +279,7 @@ if (!defined('CONTACT_EMAIL')) {
define('ACCOUNT_TEMPLATES_AND_REMINDERS', 'templates_and_reminders'); define('ACCOUNT_TEMPLATES_AND_REMINDERS', 'templates_and_reminders');
define('ACCOUNT_API_TOKENS', 'api_tokens'); define('ACCOUNT_API_TOKENS', 'api_tokens');
define('ACCOUNT_CUSTOMIZE_DESIGN', 'customize_design'); define('ACCOUNT_CUSTOMIZE_DESIGN', 'customize_design');
define('ACCOUNT_SYSTEM_SETTINGS', 'system_settings');
define('ACTION_RESTORE', 'restore'); define('ACTION_RESTORE', 'restore');
define('ACTION_ARCHIVE', 'archive'); define('ACTION_ARCHIVE', 'archive');
@ -331,6 +333,7 @@ if (!defined('CONTACT_EMAIL')) {
define('MAX_NUM_CLIENTS', 100); define('MAX_NUM_CLIENTS', 100);
define('MAX_NUM_CLIENTS_PRO', 20000); define('MAX_NUM_CLIENTS_PRO', 20000);
define('MAX_NUM_CLIENTS_LEGACY', 500); define('MAX_NUM_CLIENTS_LEGACY', 500);
define('MAX_INVOICE_AMOUNT', 10000000);
define('LEGACY_CUTOFF', 57800); define('LEGACY_CUTOFF', 57800);
define('ERROR_DELAY', 3); define('ERROR_DELAY', 3);

View File

@ -225,6 +225,17 @@ class Account extends Eloquent
return file_exists($fileName.'.png') ? $fileName.'.png' : $fileName.'.jpg'; return file_exists($fileName.'.png') ? $fileName.'.png' : $fileName.'.jpg';
} }
public function getToken($name)
{
foreach ($this->account_tokens as $token) {
if ($token->name === $name) {
return $token->token;
}
}
return null;
}
public function getLogoWidth() public function getLogoWidth()
{ {
$path = $this->getLogoPath(); $path = $this->getLogoPath();

View File

@ -370,6 +370,10 @@ class AccountRepository
public function prepareUsersData($record) public function prepareUsersData($record)
{ {
if (!$record) {
return false;
}
$users = $this->getUserAccounts($record); $users = $this->getUserAccounts($record);
$data = []; $data = [];

View File

@ -6,17 +6,11 @@ use League\Fractal\TransformerAbstract;
class UserAccountTransformer extends TransformerAbstract class UserAccountTransformer extends TransformerAbstract
{ {
protected $defaultIncludes = [ protected $tokenName;
'account_tokens'
];
public function includeAccountTokens($user) public function __construct($tokenName)
{ {
$tokens = $user->account->account_tokens->filter(function($token) use ($user) { $this->tokenName = $tokenName;
return $token->user_id === $user->id;
});
return $this->collection($tokens, new AccountTokenTransformer);
} }
public function transform(User $user) public function transform(User $user)
@ -24,6 +18,7 @@ class UserAccountTransformer extends TransformerAbstract
return [ return [
'account_key' => $user->account->account_key, 'account_key' => $user->account->account_key,
'name' => $user->account->name, 'name' => $user->account->name,
'token' => $user->account->getToken($this->tokenName),
'user' => [ 'user' => [
'first_name' => $user->first_name, 'first_name' => $user->first_name,
'last_name' => $user->last_name, 'last_name' => $user->last_name,

View File

@ -163,6 +163,15 @@ class AppServiceProvider extends ServiceProvider {
} }
return true; return true;
}); });
Validator::extend('valid_invoice_items', function($attribute, $value, $parameters) {
$total = 0;
foreach ($value as $item) {
$total += $item['qty'] * $item['cost'];
}
return $total <= MAX_INVOICE_AMOUNT;
});
} }
/** /**

View File

@ -40,7 +40,25 @@
"Dwolla/omnipay-dwolla": "dev-master", "Dwolla/omnipay-dwolla": "dev-master",
"laravel/socialite": "~2.0", "laravel/socialite": "~2.0",
"simshaun/recurr": "dev-master", "simshaun/recurr": "dev-master",
"league/fractal": "0.13.*" "league/fractal": "0.13.*",
"agmscode/omnipay-agms": "~1.0",
"samvaughton/omnipay-barclays-epdq": "~2.0",
"cardgate/omnipay-cardgate": "~2.0",
"fotografde/omnipay-checkoutcom": "~2.0",
"meebio/omnipay-creditcall": "dev-master",
"dioscouri/omnipay-cybersource": "dev-master",
"dercoder/omnipay-ecopayz": "~1.0",
"andreas22/omnipay-fasapay": "1.*",
"delatbabel/omnipay-fatzebra": "dev-master",
"vink/omnipay-komoju": "~1.0",
"incube8/omnipay-multicards": "dev-master",
"descubraomundo/omnipay-pagarme": "dev-master",
"dercoder/omnipay-paysafecard": "dev-master",
"softcommerce/omnipay-paytrace": "~1.0",
"meebio/omnipay-secure-trading": "dev-master",
"justinbusschau/omnipay-secpay": "~2.0",
"labs7in0/omnipay-wechat": "dev-master",
"collizo4sky/omnipay-wepay": "~1.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "~4.0", "phpunit/phpunit": "~4.0",

7275
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,23 @@ class PaymentLibrariesSeeder extends Seeder
['name' => 'BitPay', 'provider' => 'BitPay', 'payment_library_id' => 1], ['name' => 'BitPay', 'provider' => 'BitPay', 'payment_library_id' => 1],
['name' => 'Dwolla', 'provider' => 'Dwolla', 'payment_library_id' => 1], ['name' => 'Dwolla', 'provider' => 'Dwolla', 'payment_library_id' => 1],
['name' => 'Eway Rapid', 'provider' => 'Eway_RapidShared', 'payment_library_id' => 1], ['name' => 'Eway Rapid', 'provider' => 'Eway_RapidShared', 'payment_library_id' => 1],
['name' => 'AGMS', 'provider' => 'Agms', 'payment_library_id' => 1],
['name' => 'Barclays', 'provider' => 'BarclaysEpdq\Essential', 'payment_library_id' => 1],
['name' => 'Cardgate', 'provider' => 'Cardgate', 'payment_library_id' => 1],
['name' => 'Checkout.com', 'provider' => 'CheckoutCom', 'payment_library_id' => 1],
['name' => 'Creditcall', 'provider' => 'Creditcall', 'payment_library_id' => 1],
['name' => 'Cybersource', 'provider' => 'Cybersource', 'payment_library_id' => 1],
['name' => 'ecoPayz', 'provider' => 'Ecopayz', 'payment_library_id' => 1],
['name' => 'Fasapay', 'provider' => 'Fasapay', 'payment_library_id' => 1],
['name' => 'Komoju', 'provider' => 'Komoju', 'payment_library_id' => 1],
['name' => 'Multicards', 'provider' => 'Multicards', 'payment_library_id' => 1],
['name' => 'Pagar.Me', 'provider' => 'Pagarme', 'payment_library_id' => 1],
['name' => 'Paysafecard', 'provider' => 'Paysafecard', 'payment_library_id' => 1],
['name' => 'Paytrace', 'provider' => 'Paytrace_CreditCard', 'payment_library_id' => 1],
['name' => 'Secure Trading', 'provider' => 'SecureTrading', 'payment_library_id' => 1],
['name' => 'SecPay', 'provider' => 'SecPay', 'payment_library_id' => 1],
['name' => 'WeChat Express', 'provider' => 'WeChat_Express', 'payment_library_id' => 1],
['name' => 'WePay', 'provider' => 'WePay', 'payment_library_id' => 1],
]; ];
foreach ($gateways as $gateway) { foreach ($gateways as $gateway) {

View File

@ -31627,6 +31627,7 @@ function GetPdfMake(invoice, javascript, callback) {
} }
} }
// only show the footer on the last page
if (key === 'footer') { if (key === 'footer') {
return function(page, pages) { return function(page, pages) {
return page === pages ? val : ''; return page === pages ? val : '';
@ -31676,6 +31677,7 @@ function GetPdfMake(invoice, javascript, callback) {
doc.save = function(fileName) { doc.save = function(fileName) {
this.download(fileName); this.download(fileName);
}; };
return doc; return doc;
} }
@ -32229,7 +32231,7 @@ NINJA.parseRegExpLine = function(line, regExp, formatter, groupText)
var parts = []; var parts = [];
var lastIndex = 0; var lastIndex = 0;
while (match = regExp.exec(line)) { while (match = regExp.exec(line + '\n')) {
if (match.index > lastIndex) { if (match.index > lastIndex) {
parts.push(line.substring(lastIndex, match.index)); parts.push(line.substring(lastIndex, match.index));
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -78,6 +78,8 @@ return array(
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}', "has_counter" => 'The value must contain {$counter}',
"valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -76,6 +76,8 @@ return array(
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}', "has_counter" => 'The value must contain {$counter}',
"valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -892,4 +892,8 @@ return array(
'free' => 'Free', 'free' => 'Free',
'quote_is_approved' => 'This quote is approved', 'quote_is_approved' => 'This quote is approved',
'apply_credit' => 'Apply Credit',
'system_settings' => 'System Settings',
); );

View File

@ -72,9 +72,10 @@ return array(
"positive" => "The :attribute must be greater than zero.", "positive" => "The :attribute must be greater than zero.",
"has_credit" => "The client does not have enough credit.", "has_credit" => "The client does not have enough credit.",
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => "The :attribute must be less than :value",
"has_counter" => 'The value must contain {$counter}', "has_counter" => "The value must contain {\$counter}",
"valid_contacts" => 'All of the contacts must have either an email or name', "valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -75,6 +75,8 @@ return array(
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}', "has_counter" => 'The value must contain {$counter}',
"valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -75,6 +75,8 @@ return array(
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}', "has_counter" => 'The value must contain {$counter}',
"valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -76,6 +76,8 @@ return array(
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}', "has_counter" => 'The value must contain {$counter}',
"valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -76,6 +76,8 @@ return array(
"notmasked" => "Les valeurs sont masquées", "notmasked" => "Les valeurs sont masquées",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}', "has_counter" => 'The value must contain {$counter}',
"valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -75,6 +75,8 @@ return array(
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}', "has_counter" => 'The value must contain {$counter}',
"valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -74,6 +74,8 @@ return array(
"notmasked" => "Verdiene er skjult", "notmasked" => "Verdiene er skjult",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}', "has_counter" => 'The value must contain {$counter}',
"valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -76,6 +76,8 @@ return array(
"notmasked" => "De waarden zijn verborgen", "notmasked" => "De waarden zijn verborgen",
"less_than" => 'Het :attribute moet minder zijn dan :value', "less_than" => 'Het :attribute moet minder zijn dan :value',
"has_counter" => 'The value must contain {$counter}', "has_counter" => 'The value must contain {$counter}',
"valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -74,6 +74,8 @@ return array(
"notmasked" => "Os valores são mascarados", "notmasked" => "Os valores são mascarados",
"less_than" => ':attribute deve ser menor que :value', "less_than" => ':attribute deve ser menor que :value',
"has_counter" => 'O valor deve conter {$counter}', "has_counter" => 'O valor deve conter {$counter}',
"valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -78,6 +78,8 @@ return [
"notmasked" => "The values are masked", "notmasked" => "The values are masked",
"less_than" => 'The :attribute must be less than :value', "less_than" => 'The :attribute must be less than :value',
"has_counter" => 'The value must contain {$counter}', "has_counter" => 'The value must contain {$counter}',
"valid_contacts" => "All of the contacts must have either an email or name",
"valid_invoice_items" => "The invoice exceeds the maximum amount",
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------

View File

@ -16,7 +16,7 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading" style="color:white"> <div class="panel-heading" style="color:white">
{{ trans("texts.{$type}") }} {{ trans("texts.{$type}") }}
@if ($type == ADVANCED_SETTINGS && !Utils::isPro()) @if ($type === ADVANCED_SETTINGS && !Utils::isPro())
<sup>{{ strtoupper(trans('texts.pro')) }}</sup> <sup>{{ strtoupper(trans('texts.pro')) }}</sup>
@endif @endif
</div> </div>
@ -25,6 +25,10 @@
<a href="{{ URL::to("settings/{$section}") }}" class="list-group-item {{ $selected === $section ? 'selected' : '' }}" <a href="{{ URL::to("settings/{$section}") }}" class="list-group-item {{ $selected === $section ? 'selected' : '' }}"
style="width:100%;text-align:left">{{ trans("texts.{$section}") }}</a> style="width:100%;text-align:left">{{ trans("texts.{$section}") }}</a>
@endforeach @endforeach
@if ($type === ADVANCED_SETTINGS && !Utils::isNinjaProd())
<a href="{{ URL::to("settings/system_settings") }}" class="list-group-item {{ $selected === 'system_settings' ? 'selected' : '' }}"
style="width:100%;text-align:left">{{ trans("texts.system_settings") }}</a>
@endif
</div> </div>
</div> </div>
@endforeach @endforeach

View File

@ -0,0 +1,36 @@
@extends('header')
@section('content')
@parent
{!! Former::open('/update_setup')->addClass('warn-on-exit') !!}
@include('accounts.nav', ['selected' => ACCOUNT_SYSTEM_SETTINGS])
<div class="row">
{!! Former::open()->rules([
'app[url]' => 'required',
//'database[default]' => 'required',
'database[type][host]' => 'required',
'database[type][database]' => 'required',
'database[type][username]' => 'required',
'database[type][password]' => 'required',
]) !!}
@include('partials.system_settings')
</div>
<center>
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
</center>
{!! Former::close() !!}
@stop
@section('onReady')
$('#app\\[url\\]').focus();
@stop

View File

@ -198,8 +198,8 @@
<thead> <thead>
<th>{{ trans('texts.quote_number_short') }}</th> <th>{{ trans('texts.quote_number_short') }}</th>
<th>{{ trans('texts.client') }}</th> <th>{{ trans('texts.client') }}</th>
<th>{{ trans('texts.due_date') }}</th> <th>{{ trans('texts.valid_until') }}</th>
<th>{{ trans('texts.balance_due') }}</th> <th>{{ trans('texts.amount') }}</th>
</thead> </thead>
<tbody> <tbody>
@foreach ($upcoming as $invoice) @foreach ($upcoming as $invoice)
@ -229,8 +229,8 @@
<thead> <thead>
<th>{{ trans('texts.quote_number_short') }}</th> <th>{{ trans('texts.quote_number_short') }}</th>
<th>{{ trans('texts.client') }}</th> <th>{{ trans('texts.client') }}</th>
<th>{{ trans('texts.due_date') }}</th> <th>{{ trans('texts.valid_until') }}</th>
<th>{{ trans('texts.balance_due') }}</th> <th>{{ trans('texts.amount') }}</th>
</thead> </thead>
<tbody> <tbody>
@foreach ($pastDue as $invoice) @foreach ($pastDue as $invoice)

View File

@ -15,6 +15,10 @@
@stop @stop
@section('content') @section('content')
@if ($errors->first('invoice_items'))
<div class="alert alert-danger">{{ trans($errors->first('invoice_items')) }}</div>
@endif
@if ($invoice->id) @if ($invoice->id)
<ol class="breadcrumb"> <ol class="breadcrumb">
@if ($invoice->is_recurring) @if ($invoice->is_recurring)
@ -204,7 +208,7 @@
<input type="text" data-bind="value: tax().name, attr: {name: 'invoice_items[' + $index() + '][tax_name]'}" style="display:none"> <input type="text" data-bind="value: tax().name, attr: {name: 'invoice_items[' + $index() + '][tax_name]'}" style="display:none">
<input type="text" data-bind="value: tax().rate, attr: {name: 'invoice_items[' + $index() + '][tax_rate]'}" style="display:none"> <input type="text" data-bind="value: tax().rate, attr: {name: 'invoice_items[' + $index() + '][tax_rate]'}" style="display:none">
</td> </td>
<td style="text-align:right;padding-top:9px !important"> <td style="text-align:right;padding-top:9px !important" nowrap>
<div class="line-total" data-bind="text: totals.total"></div> <div class="line-total" data-bind="text: totals.total"></div>
</td> </td>
<td style="cursor:pointer" class="hide-border td-icon"> <td style="cursor:pointer" class="hide-border td-icon">

View File

@ -0,0 +1,112 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Application Settings</h3>
</div>
<div class="panel-body form-padding-right">
{!! Former::text('app[url]')->label('URL')->value(isset($_ENV['APP_URL']) ? $_ENV['APP_URL'] : Request::root()) !!}
{!! Former::checkbox('debug')
->label('Debug')
->text(trans('texts.enable'))
->check(config('app.debug')) !!}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Database Connection</h3>
</div>
<div class="panel-body form-padding-right">
{{--- Former::select('database[default]')->label('Driver')->options(['mysql' => 'MySQL', 'pgsql' => 'PostgreSQL', 'sqlite' => 'SQLite'])
->value(isset($_ENV['DB_TYPE']) ? $_ENV['DB_TYPE'] : 'mysql') ---}}
{!! Former::plaintext('Driver')->value('MySQL') !!}
{!! Former::text('database[type][host]')->label('Host')->value('localhost')
->value(isset($_ENV['DB_HOST']) ? $_ENV['DB_HOST'] : '') !!}
{!! Former::text('database[type][database]')->label('Database')->value('ninja')
->value(isset($_ENV['DB_DATABASE']) ? $_ENV['DB_DATABASE'] : '') !!}
{!! Former::text('database[type][username]')->label('Username')->value('ninja')
->value(isset($_ENV['DB_USERNAME']) ? $_ENV['DB_USERNAME'] : '') !!}
{!! Former::password('database[type][password]')->label('Password')->value('ninja')
->value(isset($_ENV['DB_PASSWORD']) ? $_ENV['DB_PASSWORD'] : '') !!}
{!! Former::actions( Button::primary('Test connection')->small()->withAttributes(['onclick' => 'testDatabase()']), '&nbsp;&nbsp;<span id="dbTestResult"/>' ) !!}
</div>
</div>
@if (!isset($_ENV['POSTMARK_API_TOKEN']))
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Email Settings</h3>
</div>
<div class="panel-body form-padding-right">
{!! Former::select('mail[driver]')->label('Driver')->options(['smtp' => 'SMTP', 'mail' => 'Mail', 'sendmail' => 'Sendmail'])
->value(isset($_ENV['MAIL_DRIVER']) ? $_ENV['MAIL_DRIVER'] : 'smtp') !!}
{!! Former::text('mail[host]')->label('Host')
->value(isset($_ENV['MAIL_HOST']) ? $_ENV['MAIL_HOST'] : '') !!}
{!! Former::text('mail[port]')->label('Port')
->value(isset($_ENV['MAIL_PORT']) ? $_ENV['MAIL_PORT'] : '587') !!}
{!! Former::select('mail[encryption]')->label('Encryption')->options(['tls' => 'TLS', 'ssl' => 'SSL'])
->value(isset($_ENV['MAIL_ENCRYPTION']) ? $_ENV['MAIL_ENCRYPTION'] : 'tls') !!}
{!! Former::text('mail[from][name]')->label('From Name')
->value(isset($_ENV['MAIL_FROM_NAME']) ? $_ENV['MAIL_FROM_NAME'] : '') !!}
{!! Former::text('mail[username]')->label('Email')
->value(isset($_ENV['MAIL_USERNAME']) ? $_ENV['MAIL_USERNAME'] : '') !!}
{!! Former::password('mail[password]')->label('Password')
->value(isset($_ENV['MAIL_PASSWORD']) ? $_ENV['MAIL_PASSWORD'] : '') !!}
{!! Former::actions( Button::primary('Send test email')->small()->withAttributes(['onclick' => 'testMail()']), '&nbsp;&nbsp;<span id="mailTestResult"/>' ) !!}
</div>
</div>
@endif
<script type="text/javascript">
var db_valid = false
var mail_valid = false
function testDatabase()
{
var data = $("form").serialize() + "&test=db";
// Show Progress Text
$('#dbTestResult').html('Working...').css('color', 'black');
// Send / Test Information
$.post( "/setup", data, function( data ) {
var color = 'red';
if(data == 'Success'){
color = 'green';
db_valid = true;
}
$('#dbTestResult').html(data).css('color', color);
});
return db_valid;
}
function testMail()
{
var data = $("form").serialize() + "&test=mail";
// Show Progress Text
$('#mailTestResult').html('Working...').css('color', 'black');
// Send / Test Information
$.post( "/setup", data, function( data ) {
var color = 'red';
if(data == 'Sent'){
color = 'green';
mail_valid = true;
}
$('#mailTestResult').html(data).css('color', color);
});
return mail_valid;
}
// Prevent the Enter Button from working
$("form").bind("keypress", function (e) {
if (e.keyCode == 13) {
return false;
}
});
</script>

View File

@ -26,6 +26,10 @@
{!! Former::select('client')->addOption('', '')->addGroupClass('client-select') !!} {!! Former::select('client')->addOption('', '')->addGroupClass('client-select') !!}
{!! Former::select('invoice')->addOption('', '')->addGroupClass('invoice-select') !!} {!! Former::select('invoice')->addOption('', '')->addGroupClass('invoice-select') !!}
{!! Former::text('amount') !!} {!! Former::text('amount') !!}
@if (isset($paymentTypeId) && $paymentTypeId)
{!! Former::populateField('payment_type_id', $paymentTypeId) !!}
@endif
@endif @endif
@if (!$payment || !$payment->account_gateway_id) @if (!$payment || !$payment->account_gateway_id)

View File

@ -33,8 +33,8 @@
<div class="alert alert-warning">Warning: <a href="http://php.net/manual/en/function.proc-open.php" target="_blank">proc_open</a> must be enabled.</div> <div class="alert alert-warning">Warning: <a href="http://php.net/manual/en/function.proc-open.php" target="_blank">proc_open</a> must be enabled.</div>
@endif @endif
@if (!@fopen(base_path()."/.env", 'a')) @if (!@fopen(base_path()."/.env", 'a'))
<div class="alert alert-warning">Warning: Permission denied to write config file <div class="alert alert-warning">Warning: Permission denied to write .env config file
<pre>sudo chown yourname:www-data /path/to/ninja</pre> <pre>sudo chown www-data:www-data /path/to/ninja/.env</pre>
</div> </div>
@endif @endif
If you need help you can either post to our <a href="https://www.invoiceninja.com/forums/forum/support/" target="_blank">support forum</a> If you need help you can either post to our <a href="https://www.invoiceninja.com/forums/forum/support/" target="_blank">support forum</a>
@ -61,58 +61,7 @@ FLUSH PRIVILEGES;</pre>
'terms_checkbox' => 'required' 'terms_checkbox' => 'required'
]) !!} ]) !!}
<div class="panel panel-default"> @include('partials.system_settings')
<div class="panel-heading">
<h3 class="panel-title">Application Settings</h3>
</div>
<div class="panel-body">
{!! Former::text('app[url]')->label('URL')->value(isset($_ENV['APP_URL']) ? $_ENV['APP_URL'] : Request::root()) !!}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Database Connection</h3>
</div>
<div class="panel-body">
{!! Former::select('database[default]')->label('Driver')->options(['mysql' => 'MySQL', 'pgsql' => 'PostgreSQL', 'sqlite' => 'SQLite'])
->value(isset($_ENV['DB_TYPE']) ? $_ENV['DB_TYPE'] : 'mysql') !!}
{!! Former::text('database[type][host]')->label('Host')->value('localhost')
->value(isset($_ENV['DB_HOST']) ? $_ENV['DB_HOST'] : '') !!}
{!! Former::text('database[type][database]')->label('Database')->value('ninja')
->value(isset($_ENV['DB_DATABASE']) ? $_ENV['DB_DATABASE'] : '') !!}
{!! Former::text('database[type][username]')->label('Username')->value('ninja')
->value(isset($_ENV['DB_USERNAME']) ? $_ENV['DB_USERNAME'] : '') !!}
{!! Former::password('database[type][password]')->label('Password')->value('ninja')
->value(isset($_ENV['DB_PASSWORD']) ? $_ENV['DB_PASSWORD'] : '') !!}
{!! Former::actions( Button::primary('Test connection')->small()->withAttributes(['onclick' => 'testDatabase()']), '&nbsp;&nbsp;<span id="dbTestResult"/>' ) !!}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Email Settings</h3>
</div>
<div class="panel-body">
{!! Former::select('mail[driver]')->label('Driver')->options(['smtp' => 'SMTP', 'mail' => 'Mail', 'sendmail' => 'Sendmail'])
->value(isset($_ENV['MAIL_DRIVER']) ? $_ENV['MAIL_DRIVER'] : 'smtp') !!}
{!! Former::text('mail[host]')->label('Host')
->value(isset($_ENV['MAIL_HOST']) ? $_ENV['MAIL_HOST'] : '') !!}
{!! Former::text('mail[port]')->label('Port')
->value(isset($_ENV['MAIL_PORT']) ? $_ENV['MAIL_PORT'] : '587') !!}
{!! Former::select('mail[encryption]')->label('Encryption')->options(['tls' => 'TLS', 'ssl' => 'SSL'])
->value(isset($_ENV['MAIL_ENCRYPTION']) ? $_ENV['MAIL_ENCRYPTION'] : 'tls') !!}
{!! Former::text('mail[from][name]')->label('From Name')
->value(isset($_ENV['MAIL_FROM_NAME']) ? $_ENV['MAIL_FROM_NAME'] : '') !!}
{!! Former::text('mail[username]')->label('Email')
->value(isset($_ENV['MAIL_USERNAME']) ? $_ENV['MAIL_USERNAME'] : '') !!}
{!! Former::password('mail[password]')->label('Password')
->value(isset($_ENV['MAIL_PASSWORD']) ? $_ENV['MAIL_PASSWORD'] : '') !!}
{!! Former::actions( Button::primary('Send test email')->small()->withAttributes(['onclick' => 'testMail()']), '&nbsp;&nbsp;<span id="mailTestResult"/>' ) !!}
</div>
</div>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
@ -133,87 +82,5 @@ FLUSH PRIVILEGES;</pre>
</div> </div>
<script type="text/javascript">
/*
* TODO:
* - Add JS Validation to DB and Mail
* - Add Function to clear valid vars fields if they change a setting
* - Add Nicer Error Message
*
*/
var db_valid = false
var mail_valid = false
function testDatabase()
{
var data = $("form").serialize() + "&test=db";
// Show Progress Text
$('#dbTestResult').html('Working...').css('color', 'black');
// Send / Test Information
$.post( "/setup", data, function( data ) {
var color = 'red';
if(data == 'Success'){
color = 'green';
db_valid = true;
}
$('#dbTestResult').html(data).css('color', color);
});
return db_valid;
}
function testMail()
{
var data = $("form").serialize() + "&test=mail";
// Show Progress Text
$('#mailTestResult').html('Working...').css('color', 'black');
// Send / Test Information
$.post( "/setup", data, function( data ) {
var color = 'red';
if(data == 'Sent'){
color = 'green';
mail_valid = true;
}
$('#mailTestResult').html(data).css('color', color);
});
return mail_valid;
}
// Validate Settings
/*$('form button[type="submit"]').click( function(e)
{
// Check DB Settings
if( !db_valid && !testDatabase() ) {
alert('Please check your Database Settings.');
return false;
}
// If Mail Settings are incorrect, prompt for continue
if( !mail_valid && !testMail() ) {
var check = confirm("The mail settings are incomplete.\nAre you sure you want to continue?");
if (!check) {
return false;
}
}
return true;
});*/
// Prevent the Enter Button from working
$("form").bind("keypress", function (e) {
if (e.keyCode == 13) {
return false;
}
});
</script>
</body> </body>
</html> </html>