mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-09 20:52:56 +01:00
Merge remote-tracking branch 'remotes/ninja/master'
This commit is contained in:
commit
ae557645fc
12
Gruntfile.js
12
Gruntfile.js
@ -51,7 +51,7 @@ module.exports = function(grunt) {
|
||||
'public/vendor/knockout-mapping/build/output/knockout.mapping-latest.js',
|
||||
'public/vendor/knockout-sortable/build/knockout-sortable.min.js',
|
||||
'public/vendor/underscore/underscore.js',
|
||||
'public/vendor/bootstrap-datepicker/js/bootstrap-datepicker.js',
|
||||
'public/vendor/bootstrap-datepicker/dist/js/bootstrap-datepicker.js',
|
||||
'public/vendor/typeahead.js/dist/typeahead.min.js',
|
||||
'public/vendor/accounting/accounting.min.js',
|
||||
'public/vendor/spectrum/spectrum.js',
|
||||
@ -65,7 +65,7 @@ module.exports = function(grunt) {
|
||||
'public/js/lightbox.min.js',
|
||||
'public/js/bootstrap-combobox.js',
|
||||
'public/js/script.js',
|
||||
'public/js/pdf.pdfmake.js'
|
||||
'public/js/pdf.pdfmake.js',
|
||||
],
|
||||
dest: 'public/js/built.js',
|
||||
nonull: true
|
||||
@ -79,8 +79,8 @@ module.exports = function(grunt) {
|
||||
'public/js/simpleexpand.js',
|
||||
*/
|
||||
'public/vendor/bootstrap/dist/js/bootstrap.min.js',
|
||||
'public/js/bootstrap-combobox.js',
|
||||
|
||||
'public/js/bootstrap-combobox.js',
|
||||
|
||||
],
|
||||
dest: 'public/js/built.public.js',
|
||||
nonull: true
|
||||
@ -91,7 +91,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/datepicker3.css',
|
||||
'public/vendor/bootstrap-datepicker/dist/css/bootstrap-datepicker3.css',
|
||||
'public/vendor/spectrum/spectrum.css',
|
||||
'public/css/bootstrap-combobox.css',
|
||||
'public/css/typeahead.js-bootstrap.css',
|
||||
@ -115,7 +115,7 @@ module.exports = function(grunt) {
|
||||
*/
|
||||
'public/css/bootstrap-combobox.css',
|
||||
'public/vendor/datatables/media/css/jquery.dataTables.css',
|
||||
'public/vendor/datatables-bootstrap3/BS3/assets/css/datatables.css',
|
||||
'public/vendor/datatables-bootstrap3/BS3/assets/css/datatables.css',
|
||||
],
|
||||
dest: 'public/css/built.public.css',
|
||||
nonull: true,
|
||||
|
@ -84,9 +84,10 @@ class AccountController extends BaseController
|
||||
}
|
||||
|
||||
$user = false;
|
||||
$guestKey = Input::get('guest_key');
|
||||
$guestKey = Input::get('guest_key'); // local storage key to login until registered
|
||||
$prevUserId = Session::pull(PREV_USER_ID); // last user id used to link to new account
|
||||
|
||||
if ($guestKey) {
|
||||
if ($guestKey && !$prevUserId) {
|
||||
$user = User::where('password', '=', $guestKey)->first();
|
||||
|
||||
if ($user && $user->registered) {
|
||||
@ -99,6 +100,11 @@ class AccountController extends BaseController
|
||||
$user = $account->users()->first();
|
||||
|
||||
Session::forget(RECENTLY_VIEWED);
|
||||
|
||||
if ($prevUserId) {
|
||||
$users = $this->accountRepo->associateAccounts($user->id, $prevUserId);
|
||||
Session::put(SESSION_USER_ACCOUNTS, $users);
|
||||
}
|
||||
}
|
||||
|
||||
Auth::login($user, true);
|
||||
@ -154,6 +160,7 @@ class AccountController extends BaseController
|
||||
'currencies' => Cache::get('currencies'),
|
||||
'languages' => Cache::get('languages'),
|
||||
'showUser' => Auth::user()->id === Auth::user()->account->users()->first()->id,
|
||||
'title' => trans('texts.company_details'),
|
||||
];
|
||||
|
||||
return View::make('accounts.details', $data);
|
||||
@ -166,21 +173,26 @@ class AccountController extends BaseController
|
||||
if ($count == 0) {
|
||||
return Redirect::to('gateways/create');
|
||||
} else {
|
||||
return View::make('accounts.payments', ['showAdd' => $count < 3]);
|
||||
return View::make('accounts.payments', [
|
||||
'showAdd' => $count < 3,
|
||||
'title' => trans('texts.online_payments')
|
||||
]);
|
||||
}
|
||||
} elseif ($section == ACCOUNT_NOTIFICATIONS) {
|
||||
$data = [
|
||||
'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
|
||||
'title' => trans('texts.notifications'),
|
||||
];
|
||||
|
||||
return View::make('accounts.notifications', $data);
|
||||
} elseif ($section == ACCOUNT_IMPORT_EXPORT) {
|
||||
return View::make('accounts.import_export');
|
||||
return View::make('accounts.import_export', ['title' => trans('texts.import_export')]);
|
||||
} elseif ($section == ACCOUNT_ADVANCED_SETTINGS) {
|
||||
$account = Auth::user()->account;
|
||||
$data = [
|
||||
'account' => $account,
|
||||
'feature' => $subSection,
|
||||
'title' => trans('texts.invoice_settings'),
|
||||
];
|
||||
|
||||
if ($subSection == ACCOUNT_INVOICE_DESIGN) {
|
||||
@ -212,17 +224,22 @@ class AccountController extends BaseController
|
||||
$data['invoice'] = $invoice;
|
||||
$data['invoiceDesigns'] = InvoiceDesign::availableDesigns();
|
||||
$data['invoiceLabels'] = json_decode($account->invoice_labels) ?: [];
|
||||
$data['title'] = trans('texts.invoice_design');
|
||||
} else if ($subSection == ACCOUNT_EMAIL_TEMPLATES) {
|
||||
$data['invoiceEmail'] = $account->getEmailTemplate(ENTITY_INVOICE);
|
||||
$data['quoteEmail'] = $account->getEmailTemplate(ENTITY_QUOTE);
|
||||
$data['paymentEmail'] = $account->getEmailTemplate(ENTITY_PAYMENT);
|
||||
$data['emailFooter'] = $account->getEmailFooter();
|
||||
$data['title'] = trans('texts.email_templates');
|
||||
} else if ($subSection == ACCOUNT_USER_MANAGEMENT) {
|
||||
$data['title'] = trans('texts.users_and_tokens');
|
||||
}
|
||||
|
||||
return View::make("accounts.{$subSection}", $data);
|
||||
} elseif ($section == ACCOUNT_PRODUCTS) {
|
||||
$data = [
|
||||
'account' => Auth::user()->account,
|
||||
'title' => trans('texts.product_library'),
|
||||
];
|
||||
|
||||
return View::make('accounts.products', $data);
|
||||
@ -704,8 +721,6 @@ class AccountController extends BaseController
|
||||
|
||||
if (Utils::isNinja()) {
|
||||
$this->userMailer->sendConfirmation($user);
|
||||
} else {
|
||||
$this->accountRepo->registerUser($user);
|
||||
}
|
||||
|
||||
$activities = Activity::scope()->get();
|
||||
@ -761,6 +776,7 @@ class AccountController extends BaseController
|
||||
}
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$this->accountRepo->unlinkAccount($account);
|
||||
$account->forceDelete();
|
||||
|
||||
Auth::logout();
|
||||
|
@ -200,7 +200,7 @@ class AccountGatewayController extends BaseController
|
||||
$fields = $gateway->getFields();
|
||||
$optional = array_merge(Gateway::$hiddenFields, Gateway::$optionalFields);
|
||||
|
||||
if (Utils::isNinja() && $gatewayId == GATEWAY_DWOLLA) {
|
||||
if ($gatewayId == GATEWAY_DWOLLA) {
|
||||
$optional = array_merge($optional, ['key', 'secret']);
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,8 @@ use Session;
|
||||
use Cookie;
|
||||
use Response;
|
||||
use App\Models\User;
|
||||
use App\Models\Account;
|
||||
use App\Models\Industry;
|
||||
use App\Ninja\Mailers\Mailer;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use Redirect;
|
||||
@ -32,24 +34,16 @@ class AppController extends BaseController
|
||||
|
||||
public function showSetup()
|
||||
{
|
||||
if (Utils::isNinja() || Utils::isDatabaseSetup()) {
|
||||
if (Utils::isNinja() || (Utils::isDatabaseSetup() && Account::count() > 0)) {
|
||||
return Redirect::to('/');
|
||||
}
|
||||
|
||||
$view = View::make('setup');
|
||||
|
||||
/*
|
||||
$cookie = Cookie::forget('ninja_session', '/', 'www.ninja.dev');
|
||||
Cookie::queue($cookie);
|
||||
return Response::make($view)->withCookie($cookie);
|
||||
*/
|
||||
|
||||
return Response::make($view);
|
||||
return View::make('setup');
|
||||
}
|
||||
|
||||
public function doSetup()
|
||||
{
|
||||
if (Utils::isNinja() || Utils::isDatabaseSetup()) {
|
||||
if (Utils::isNinja() || (Utils::isDatabaseSetup() && Account::count() > 0)) {
|
||||
return Redirect::to('/');
|
||||
}
|
||||
|
||||
@ -104,7 +98,9 @@ class AppController extends BaseController
|
||||
// == DB Migrate & Seed == //
|
||||
// Artisan::call('migrate:rollback', array('--force' => true)); // Debug Purposes
|
||||
Artisan::call('migrate', array('--force' => true));
|
||||
Artisan::call('db:seed', array('--force' => true));
|
||||
if (Industry::count() == 0) {
|
||||
Artisan::call('db:seed', array('--force' => true));
|
||||
}
|
||||
Artisan::call('optimize', array('--force' => true));
|
||||
|
||||
$firstName = trim(Input::get('first_name'));
|
||||
@ -114,8 +110,6 @@ class AppController extends BaseController
|
||||
$account = $this->accountRepo->create($firstName, $lastName, $email, $password);
|
||||
$user = $account->users()->first();
|
||||
|
||||
//Auth::login($user, true);
|
||||
|
||||
return Redirect::to('/login');
|
||||
}
|
||||
|
||||
@ -168,7 +162,9 @@ class AppController extends BaseController
|
||||
if (!Utils::isNinja() && !Utils::isDatabaseSetup()) {
|
||||
try {
|
||||
Artisan::call('migrate', array('--force' => true));
|
||||
Artisan::call('db:seed', array('--force' => true));
|
||||
if (Industry::count() == 0) {
|
||||
Artisan::call('db:seed', array('--force' => true));
|
||||
}
|
||||
Artisan::call('optimize', array('--force' => true));
|
||||
} catch (Exception $e) {
|
||||
Response::make($e->getMessage(), 500);
|
||||
|
@ -60,24 +60,32 @@ class AuthController extends Controller {
|
||||
public function postLoginWrapper(Request $request)
|
||||
{
|
||||
$userId = Auth::check() ? Auth::user()->id : null;
|
||||
$user = User::where('email', '=', $request->input('email'))->first();
|
||||
|
||||
if ($user->failed_logins >= 3) {
|
||||
Session::flash('error', 'These credentials do not match our records.');
|
||||
return redirect()->to('login');
|
||||
}
|
||||
|
||||
$response = self::postLogin($request);
|
||||
|
||||
if (Auth::check()) {
|
||||
Event::fire(new UserLoggedIn());
|
||||
|
||||
if (Utils::isPro()) {
|
||||
$users = false;
|
||||
// we're linking a new account
|
||||
if ($userId && Auth::user()->id != $userId) {
|
||||
$users = $this->accountRepo->associateAccounts($userId, Auth::user()->id);
|
||||
Session::flash('message', trans('texts.associated_accounts'));
|
||||
// check if other accounts are linked
|
||||
} else {
|
||||
$users = $this->accountRepo->loadAccounts(Auth::user()->id);
|
||||
}
|
||||
|
||||
Session::put(SESSION_USER_ACCOUNTS, $users);
|
||||
$users = false;
|
||||
// we're linking a new account
|
||||
if ($userId && Auth::user()->id != $userId) {
|
||||
$users = $this->accountRepo->associateAccounts($userId, Auth::user()->id);
|
||||
Session::flash('message', trans('texts.associated_accounts'));
|
||||
// check if other accounts are linked
|
||||
} else {
|
||||
$users = $this->accountRepo->loadAccounts(Auth::user()->id);
|
||||
}
|
||||
|
||||
Session::put(SESSION_USER_ACCOUNTS, $users);
|
||||
} elseif ($user) {
|
||||
$user->failed_logins = $user->failed_logins + 1;
|
||||
$user->save();
|
||||
}
|
||||
|
||||
return $response;
|
||||
@ -85,6 +93,12 @@ class AuthController extends Controller {
|
||||
|
||||
public function getLogoutWrapper()
|
||||
{
|
||||
if (Auth::check() && !Auth::user()->registered) {
|
||||
$account = Auth::user()->account;
|
||||
$this->accountRepo->unlinkAccount($account);
|
||||
$account->forceDelete();
|
||||
}
|
||||
|
||||
$response = self::getLogout();
|
||||
|
||||
Session::flush();
|
||||
|
@ -24,8 +24,11 @@ class ClientApiController extends Controller
|
||||
|
||||
public function index()
|
||||
{
|
||||
$clients = Client::scope()->with('contacts')->orderBy('created_at', 'desc')->get();
|
||||
$clients = Utils::remapPublicIds($clients->toArray());
|
||||
$clients = Client::scope()
|
||||
->with('country', 'contacts', 'industry', 'size', 'currency')
|
||||
->orderBy('created_at', 'desc')
|
||||
->get();
|
||||
$clients = Utils::remapPublicIds($clients);
|
||||
|
||||
$response = json_encode($clients, JSON_PRETTY_PRINT);
|
||||
$headers = Utils::getApiHeaders(count($clients));
|
||||
@ -43,9 +46,9 @@ class ClientApiController extends Controller
|
||||
|
||||
return Response::make($error, 500, $headers);
|
||||
} else {
|
||||
$client = $this->clientRepo->save(false, $data, false);
|
||||
$client->load('contacts');
|
||||
$client = Utils::remapPublicIds($client->toArray());
|
||||
$client = $this->clientRepo->save(isset($data['id']) ? $data['id'] : false, $data, false);
|
||||
$client = Client::scope($client->public_id)->with('country', 'contacts', 'industry', 'size', 'currency')->first();
|
||||
$client = Utils::remapPublicIds([$client]);
|
||||
$response = json_encode($client, JSON_PRETTY_PRINT);
|
||||
$headers = Utils::getApiHeaders();
|
||||
|
||||
|
@ -83,6 +83,7 @@ class DashboardController extends BaseController
|
||||
'activities' => $activities,
|
||||
'pastDue' => $pastDue,
|
||||
'upcoming' => $upcoming,
|
||||
'title' => trans('texts.dashboard'),
|
||||
];
|
||||
|
||||
return View::make('dashboard', $data);
|
||||
|
@ -27,10 +27,8 @@ class HomeController extends BaseController
|
||||
{
|
||||
Session::reflash();
|
||||
|
||||
if (!Utils::isDatabaseSetup()) {
|
||||
if (!Utils::isNinja() && (!Utils::isDatabaseSetup() || Account::count() == 0)) {
|
||||
return Redirect::to('/setup');
|
||||
} elseif (Account::count() == 0) {
|
||||
return Redirect::to('/invoice_now');
|
||||
} elseif (Auth::check()) {
|
||||
return Redirect::to('/dashboard');
|
||||
} else {
|
||||
@ -45,7 +43,8 @@ class HomeController extends BaseController
|
||||
|
||||
public function invoiceNow()
|
||||
{
|
||||
if (Auth::check() && Input::get('logout')) {
|
||||
if (Auth::check() && Input::get('new_account')) {
|
||||
Session::put(PREV_USER_ID, Auth::user()->id);
|
||||
Auth::user()->clearSession();
|
||||
Auth::logout();
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ class InvoiceApiController extends Controller
|
||||
|
||||
public function index()
|
||||
{
|
||||
$invoices = Invoice::scope()->with('invitations')->where('invoices.is_quote', '=', false)->orderBy('created_at', 'desc')->get();
|
||||
$invoices = Invoice::scope()->with('client', 'invitations.account')->where('invoices.is_quote', '=', false)->orderBy('created_at', 'desc')->get();
|
||||
|
||||
// Add the first invitation link to the data
|
||||
foreach ($invoices as $key => $invoice) {
|
||||
@ -36,7 +36,7 @@ class InvoiceApiController extends Controller
|
||||
unset($invoice['invitations']);
|
||||
}
|
||||
|
||||
$invoices = Utils::remapPublicIds($invoices->toArray());
|
||||
$invoices = Utils::remapPublicIds($invoices);
|
||||
|
||||
$response = json_encode($invoices, JSON_PRETTY_PRINT);
|
||||
$headers = Utils::getApiHeaders(count($invoices));
|
||||
@ -99,7 +99,6 @@ class InvoiceApiController extends Controller
|
||||
$data = self::prepareData($data);
|
||||
$data['client_id'] = $client->id;
|
||||
$invoice = $this->invoiceRepo->save(false, $data, false);
|
||||
$invoice->load('invoice_items');
|
||||
|
||||
$invitation = Invitation::createNew();
|
||||
$invitation->invoice_id = $invoice->id;
|
||||
@ -112,13 +111,9 @@ class InvoiceApiController extends Controller
|
||||
}
|
||||
|
||||
// prepare the return data
|
||||
$invoice = $invoice->toArray();
|
||||
$invoice['link'] = $invitation->getLink();
|
||||
unset($invoice['account']);
|
||||
unset($invoice['client']);
|
||||
$invoice = Utils::remapPublicIds($invoice);
|
||||
$invoice['client_id'] = $client->public_id;
|
||||
|
||||
$invoice = Invoice::scope($invoice->public_id)->with('client', 'invoice_items', 'invitations')->first();
|
||||
$invoice = Utils::remapPublicIds([$invoice]);
|
||||
|
||||
$response = json_encode($invoice, JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
|
@ -236,6 +236,11 @@ class InvoiceController extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
$paymentURL = '';
|
||||
if (count($paymentTypes)) {
|
||||
$paymentURL = $paymentTypes[0]['url'];
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'isConverted' => $invoice->quote_invoice_id ? true : false,
|
||||
'showBreadcrumbs' => false,
|
||||
@ -244,7 +249,8 @@ class InvoiceController extends BaseController
|
||||
'invitation' => $invitation,
|
||||
'invoiceLabels' => $account->getInvoiceLabels(),
|
||||
'contact' => $contact,
|
||||
'paymentTypes' => $paymentTypes
|
||||
'paymentTypes' => $paymentTypes,
|
||||
'paymentURL' => $paymentURL
|
||||
);
|
||||
|
||||
return View::make('invoices.view', $data);
|
||||
@ -505,9 +511,13 @@ class InvoiceController extends BaseController
|
||||
return $this->convertQuote($publicId);
|
||||
} elseif ($action == 'email') {
|
||||
if (Auth::user()->confirmed && !Auth::user()->isDemo()) {
|
||||
$message = trans("texts.emailed_{$entityType}");
|
||||
$this->mailer->sendInvoice($invoice);
|
||||
Session::flash('message', $message);
|
||||
$response = $this->mailer->sendInvoice($invoice);
|
||||
if ($response === true) {
|
||||
$message = trans("texts.emailed_{$entityType}");
|
||||
Session::flash('message', $message);
|
||||
} else {
|
||||
Session::flash('error', $response);
|
||||
}
|
||||
} else {
|
||||
$errorMessage = trans(Auth::user()->registered ? 'texts.confirmation_required' : 'texts.registration_required');
|
||||
Session::flash('error', $errorMessage);
|
||||
|
@ -1,8 +1,10 @@
|
||||
<?php namespace App\Http\Controllers;
|
||||
|
||||
use Input;
|
||||
use Utils;
|
||||
use Response;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Invoice;
|
||||
use App\Ninja\Repositories\PaymentRepository;
|
||||
|
||||
class PaymentApiController extends Controller
|
||||
@ -16,24 +18,50 @@ class PaymentApiController extends Controller
|
||||
|
||||
public function index()
|
||||
{
|
||||
$payments = Payment::scope()->orderBy('created_at', 'desc')->get();
|
||||
$payments = Utils::remapPublicIds($payments->toArray());
|
||||
|
||||
$payments = Payment::scope()
|
||||
->with('client', 'contact', 'invitation', 'user', 'invoice')
|
||||
->orderBy('created_at', 'desc')
|
||||
->get();
|
||||
$payments = Utils::remapPublicIds($payments);
|
||||
|
||||
$response = json_encode($payments, JSON_PRETTY_PRINT);
|
||||
$headers = Utils::getApiHeaders(count($payments));
|
||||
|
||||
return Response::make($response, 200, $headers);
|
||||
}
|
||||
|
||||
/*
|
||||
public function store()
|
||||
{
|
||||
$data = Input::all();
|
||||
$invoice = $this->invoiceRepo->save(false, $data, false);
|
||||
|
||||
$response = json_encode($invoice, JSON_PRETTY_PRINT);
|
||||
$headers = Utils::getApiHeaders();
|
||||
return Response::make($response, 200, $headers);
|
||||
}
|
||||
*/
|
||||
public function store()
|
||||
{
|
||||
$data = Input::all();
|
||||
$error = false;
|
||||
|
||||
if (isset($data['invoice_id'])) {
|
||||
$invoice = Invoice::scope($data['invoice_id'])->with('client')->first();
|
||||
|
||||
if ($invoice) {
|
||||
$data['invoice'] = $invoice->public_id;
|
||||
$data['client'] = $invoice->client->public_id;
|
||||
} else {
|
||||
$error = trans('validation.not_in', ['attribute' => 'invoice_id']);
|
||||
}
|
||||
} else {
|
||||
$error = trans('validation.not_in', ['attribute' => 'invoice_id']);
|
||||
}
|
||||
|
||||
if (!isset($data['transaction_reference'])) {
|
||||
$data['transaction_reference'] = '';
|
||||
}
|
||||
|
||||
if (!$error) {
|
||||
$payment = $this->paymentRepo->save(false, $data);
|
||||
$payment = Payment::scope($payment->public_id)->with('client', 'contact', 'user', 'invoice')->first();
|
||||
|
||||
$payment = Utils::remapPublicIds([$payment]);
|
||||
}
|
||||
|
||||
$response = json_encode($error ?: $payment, JSON_PRETTY_PRINT);
|
||||
$headers = Utils::getApiHeaders();
|
||||
return Response::make($response, 200, $headers);
|
||||
}
|
||||
}
|
||||
|
@ -460,10 +460,11 @@ class PaymentController extends BaseController
|
||||
$this->contactMailer->sendLicensePaymentConfirmation($name, $license->email, $affiliate->price, $license->license_key, $license->product_id);
|
||||
|
||||
if (Session::has('return_url')) {
|
||||
return Redirect::away(Session::get('return_url')."?license_key={$license->license_key}&product_id=".Session::get('product_id'));
|
||||
} else {
|
||||
return View::make('public.license', $data);
|
||||
$data['redirectTo'] = Session::get('return_url')."?license_key={$license->license_key}&product_id=".Session::get('product_id');
|
||||
$data['message'] = "Redirecting to " . Session::get('return_url');
|
||||
}
|
||||
|
||||
return View::make('public.license', $data);
|
||||
} catch (\Exception $e) {
|
||||
$errorMessage = trans('texts.payment_error');
|
||||
Session::flash('error', $errorMessage);
|
||||
@ -685,6 +686,14 @@ class PaymentController extends BaseController
|
||||
$accountGateway = $invoice->client->account->getGatewayByType(Session::get('payment_type'));
|
||||
$gateway = self::createGateway($accountGateway);
|
||||
|
||||
// Check for Dwolla payment error
|
||||
if ($accountGateway->isGateway(GATEWAY_DWOLLA) && Input::get('error')) {
|
||||
$errorMessage = trans('texts.payment_error')."\n\n".Input::get('error_description');
|
||||
Session::flash('error', $errorMessage);
|
||||
Utils::logError($errorMessage);
|
||||
return Redirect::to('view/'.$invitation->invitation_key);
|
||||
}
|
||||
|
||||
try {
|
||||
if (method_exists($gateway, 'completePurchase')) {
|
||||
$details = self::getPaymentDetails($invitation);
|
||||
|
@ -16,8 +16,8 @@ class QuoteApiController extends Controller
|
||||
|
||||
public function index()
|
||||
{
|
||||
$invoices = Invoice::scope()->where('invoices.is_quote', '=', true)->orderBy('created_at', 'desc')->get();
|
||||
$invoices = Utils::remapPublicIds($invoices->toArray());
|
||||
$invoices = Invoice::scope()->with('client', 'user')->where('invoices.is_quote', '=', true)->orderBy('created_at', 'desc')->get();
|
||||
$invoices = Utils::remapPublicIds($invoices);
|
||||
|
||||
$response = json_encode($invoices, JSON_PRETTY_PRINT);
|
||||
$headers = Utils::getApiHeaders(count($invoices));
|
||||
|
@ -243,6 +243,7 @@ class ReportController extends BaseController
|
||||
'reportType' => $reportType,
|
||||
'enableChart' => $enableChart,
|
||||
'enableReport' => $enableReport,
|
||||
'title' => trans('texts.charts_and_reports'),
|
||||
];
|
||||
|
||||
return View::make('reports.chart_builder', $params);
|
||||
|
@ -301,11 +301,13 @@ class UserController extends BaseController
|
||||
* Log the user out of the application.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
public function logout()
|
||||
{
|
||||
if (Auth::check()) {
|
||||
if (!Auth::user()->registered) {
|
||||
$account = Auth::user()->account;
|
||||
$this->accountRepo->unlinkAccount($account);
|
||||
$account->forceDelete();
|
||||
}
|
||||
}
|
||||
@ -315,7 +317,8 @@ class UserController extends BaseController
|
||||
|
||||
return Redirect::to('/')->with('clearGuestKey', true);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
public function changePassword()
|
||||
{
|
||||
// check the current password is correct
|
||||
@ -352,6 +355,10 @@ class UserController extends BaseController
|
||||
if ($account->hasUserId($newUserId) && $account->hasUserId($oldUserId)) {
|
||||
Auth::loginUsingId($newUserId);
|
||||
Auth::user()->account->loadLocalizationSettings();
|
||||
|
||||
// regenerate token to prevent open pages
|
||||
// from saving under the wrong account
|
||||
Session::put('_token', str_random(40));
|
||||
}
|
||||
}
|
||||
|
||||
@ -360,7 +367,7 @@ class UserController extends BaseController
|
||||
|
||||
public function unlinkAccount($userAccountId, $userId)
|
||||
{
|
||||
$this->accountRepo->unlinkAccount($userAccountId, $userId);
|
||||
$this->accountRepo->unlinkUser($userAccountId, $userId);
|
||||
$referer = Request::header('referer');
|
||||
|
||||
$users = $this->accountRepo->loadAccounts(Auth::user()->id);
|
||||
|
@ -157,6 +157,14 @@ class StartupCheck
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
if (preg_match('/(?i)msie [2-8]/', $_SERVER['HTTP_USER_AGENT'])) {
|
||||
Session::flash('error', trans('texts.old_browser'));
|
||||
}
|
||||
|
||||
// for security prevent displaying within an iframe
|
||||
$response = $next($request);
|
||||
$response->headers->set('X-Frame-Options', 'DENY');
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
@ -354,6 +354,7 @@ define('EVENT_CREATE_PAYMENT', 4);
|
||||
|
||||
define('REQUESTED_PRO_PLAN', 'REQUESTED_PRO_PLAN');
|
||||
define('DEMO_ACCOUNT_ID', 'DEMO_ACCOUNT_ID');
|
||||
define('PREV_USER_ID', 'PREV_USER_ID');
|
||||
define('NINJA_ACCOUNT_KEY', 'zg4ylmzDkdkPOT8yoKQw9LTWaoZJx79h');
|
||||
define('NINJA_GATEWAY_ID', GATEWAY_STRIPE);
|
||||
define('NINJA_GATEWAY_CONFIG', '');
|
||||
@ -364,6 +365,7 @@ define('NINJA_DATE', '2000-01-01');
|
||||
define('NINJA_FROM_EMAIL', 'maildelivery@invoiceninja.com');
|
||||
define('RELEASES_URL', 'https://github.com/hillelcoren/invoice-ninja/releases/');
|
||||
define('ZAPIER_URL', 'https://zapier.com/developer/invite/11276/85cf0ee4beae8e802c6c579eb4e351f1/');
|
||||
define('OUTDATE_BROWSER_URL', 'http://browsehappy.com/');
|
||||
|
||||
define('COUNT_FREE_DESIGNS', 4);
|
||||
define('PRODUCT_ONE_CLICK_INSTALL', 1);
|
||||
|
@ -61,7 +61,7 @@ class Utils
|
||||
|
||||
public static function allowNewAccounts()
|
||||
{
|
||||
return isset($_ENV['ALLOW_NEW_ACCOUNTS']) && $_ENV['ALLOW_NEW_ACCOUNTS'] == 'true';
|
||||
return Utils::isNinja() || (isset($_ENV['ALLOW_NEW_ACCOUNTS']) && $_ENV['ALLOW_NEW_ACCOUNTS'] == 'true');
|
||||
}
|
||||
|
||||
public static function isPro()
|
||||
@ -568,14 +568,15 @@ class Utils
|
||||
{
|
||||
$curl = curl_init();
|
||||
|
||||
$jsonEncodedData = json_encode($data->toJson());
|
||||
$jsonEncodedData = json_encode($data->toPublicArray());
|
||||
|
||||
$opts = [
|
||||
CURLOPT_URL => $subscription->target_url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $jsonEncodedData,
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Content-Length: '.strlen($jsonEncodedData)],
|
||||
CURLOPT_URL => $subscription->target_url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CUSTOMREQUEST => 'POST',
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $jsonEncodedData,
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Content-Length: '.strlen($jsonEncodedData)],
|
||||
];
|
||||
|
||||
curl_setopt_array($curl, $opts);
|
||||
@ -591,27 +592,39 @@ class Utils
|
||||
}
|
||||
|
||||
|
||||
public static function remapPublicIds(array $data)
|
||||
public static function remapPublicIds($items)
|
||||
{
|
||||
$return = [];
|
||||
|
||||
foreach ($data as $key => $val) {
|
||||
if ($key === 'public_id') {
|
||||
$key = 'id';
|
||||
} elseif (strpos($key, '_id')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_array($val)) {
|
||||
$val = Utils::remapPublicIds($val);
|
||||
}
|
||||
|
||||
$return[$key] = $val;
|
||||
|
||||
foreach ($items as $item) {
|
||||
$return[] = $item->toPublicArray();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public static function hideIds($data)
|
||||
{
|
||||
$publicId = null;
|
||||
|
||||
foreach ($data as $key => $val) {
|
||||
if (is_array($val)) {
|
||||
$data[$key] = Utils::hideIds($val);
|
||||
} else if ($key == 'id' || strpos($key, '_id')) {
|
||||
if ($key == 'public_id') {
|
||||
$publicId = $val;
|
||||
}
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($publicId) {
|
||||
$data['id'] = $publicId;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public static function getApiHeaders($count = 0)
|
||||
{
|
||||
return [
|
||||
|
@ -3,6 +3,7 @@
|
||||
use Utils;
|
||||
use Auth;
|
||||
use Carbon;
|
||||
use Session;
|
||||
use App\Events\UserLoggedIn;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
@ -32,13 +33,16 @@ class HandleUserLoggedIn {
|
||||
{
|
||||
$account = Auth::user()->account;
|
||||
|
||||
if (!Utils::isNinja() && empty($account->last_login)) {
|
||||
if (!Utils::isNinja() && Auth::user()->id == 1 && empty($account->last_login)) {
|
||||
$this->accountRepo->registerUser(Auth::user());
|
||||
}
|
||||
|
||||
$account->last_login = Carbon::now()->toDateTimeString();
|
||||
$account->save();
|
||||
|
||||
$users = $this->accountRepo->loadAccounts(Auth::user()->id);
|
||||
Session::put(SESSION_USER_ACCOUNTS, $users);
|
||||
|
||||
$account->loadLocalizationSettings();
|
||||
}
|
||||
|
||||
|
@ -133,6 +133,13 @@ class Account extends Eloquent
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
public function hasLogo()
|
||||
{
|
||||
file_exists($this->getLogoPath());
|
||||
}
|
||||
*/
|
||||
|
||||
public function getLogoPath()
|
||||
{
|
||||
return 'logo/'.$this->account_key.'.jpg';
|
||||
@ -250,6 +257,7 @@ class Account extends Eloquent
|
||||
'date',
|
||||
'rate',
|
||||
'hours',
|
||||
'balance',
|
||||
];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
|
@ -34,5 +34,9 @@ class AccountGateway extends EntityModel
|
||||
public function isPaymentType($type) {
|
||||
return $this->getPaymentType() == $type;
|
||||
}
|
||||
|
||||
public function isGateway($gatewayId) {
|
||||
return $this->gateway_id == $gatewayId;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ class Client extends EntityModel
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->getDisplayName();
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getDisplayName()
|
||||
@ -82,8 +82,9 @@ class Client extends EntityModel
|
||||
if ($this->name) {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
|
||||
$this->load('contacts');
|
||||
|
||||
$contact = $this->contacts()->first();
|
||||
|
||||
return $contact->getDisplayName();
|
||||
|
@ -38,6 +38,11 @@ class Contact extends EntityModel
|
||||
}
|
||||
*/
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->getDisplayName();
|
||||
}
|
||||
|
||||
public function getDisplayName()
|
||||
{
|
||||
if ($this->getFullName()) {
|
||||
|
@ -4,7 +4,12 @@ use Eloquent;
|
||||
|
||||
class Country extends Eloquent
|
||||
{
|
||||
public $timestamps = false;
|
||||
public $timestamps = false;
|
||||
|
||||
protected $visible = ['id', 'name'];
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
@ -5,4 +5,9 @@ use Eloquent;
|
||||
class Currency extends Eloquent
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
@ -77,4 +77,34 @@ class EntityModel extends Eloquent
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->public_id;
|
||||
}
|
||||
|
||||
// Remap ids to public_ids and show name
|
||||
public function toPublicArray()
|
||||
{
|
||||
$data = $this->toArray();
|
||||
|
||||
foreach ($this->attributes as $key => $val) {
|
||||
if (strpos($key, '_id')) {
|
||||
list($field, $id) = explode('_', $key);
|
||||
if ($field == 'account') {
|
||||
// do nothing
|
||||
} else {
|
||||
$entity = @$this->$field;
|
||||
if ($entity) {
|
||||
$data["{$field}_name"] = $entity->getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data = Utils::hideIds($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,4 +5,9 @@ use Eloquent;
|
||||
class Industry extends Eloquent
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,10 @@ class Invitation extends EntityModel
|
||||
|
||||
public function getLink()
|
||||
{
|
||||
$this->load('account');
|
||||
if (!$this->account) {
|
||||
$this->load('account');
|
||||
}
|
||||
|
||||
$url = SITE_URL;
|
||||
|
||||
if ($this->account->subdomain) {
|
||||
@ -41,4 +44,9 @@ class Invitation extends EntityModel
|
||||
|
||||
return "{$url}/view/{$this->invitation_key}";
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->invitation_key;
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,11 @@ class Invoice extends EntityModel
|
||||
return $this->belongsTo('App\Models\InvoiceDesign');
|
||||
}
|
||||
|
||||
public function recurring_invoice()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Invoice');
|
||||
}
|
||||
|
||||
public function invitations()
|
||||
{
|
||||
return $this->hasMany('App\Models\Invitation')->orderBy('invitations.contact_id');
|
||||
|
@ -5,4 +5,9 @@ use Eloquent;
|
||||
class Size extends Eloquent
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,11 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
return $this->belongsTo('App\Models\Theme');
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->getDisplayName();
|
||||
}
|
||||
|
||||
public function getPersonType()
|
||||
{
|
||||
return PERSON_USER;
|
||||
@ -196,4 +201,15 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
}
|
||||
}
|
||||
|
||||
public static function updateUser($user)
|
||||
{
|
||||
if ($user->password != !$user->getOriginal('password')) {
|
||||
$user->failed_logins = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
User::updating(function ($user) {
|
||||
User::updateUser($user);
|
||||
});
|
||||
|
@ -54,7 +54,11 @@ class ContactMailer extends Mailer
|
||||
$data['invoice_id'] = $invoice->id;
|
||||
|
||||
$fromEmail = $invitation->user->email;
|
||||
$this->sendTo($invitation->contact->email, $fromEmail, $accountName, $subject, $view, $data);
|
||||
$response = $this->sendTo($invitation->contact->email, $fromEmail, $accountName, $subject, $view, $data);
|
||||
|
||||
if ($response !== true) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
Activity::emailInvoice($invitation);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php namespace App\Ninja\Mailers;
|
||||
|
||||
use Exception;
|
||||
use Mail;
|
||||
use Utils;
|
||||
use App\Models\Invoice;
|
||||
@ -13,26 +14,30 @@ class Mailer
|
||||
'emails.'.$view.'_text',
|
||||
];
|
||||
|
||||
Mail::send($views, $data, function ($message) use ($toEmail, $fromEmail, $fromName, $subject, $data) {
|
||||
$replyEmail = $fromEmail;
|
||||
try {
|
||||
Mail::send($views, $data, function ($message) use ($toEmail, $fromEmail, $fromName, $subject, $data) {
|
||||
|
||||
// http://stackoverflow.com/questions/2421234/gmail-appearing-to-ignore-reply-to
|
||||
if (Utils::isNinja() && $toEmail != CONTACT_EMAIL) {
|
||||
$fromEmail = NINJA_FROM_EMAIL;
|
||||
}
|
||||
|
||||
if(isset($data['invoice_id'])) {
|
||||
$invoice = Invoice::with('account')->where('id', '=', $data['invoice_id'])->get()->first();
|
||||
if($invoice->account->pdf_email_attachment && file_exists($invoice->getPDFPath())) {
|
||||
$message->attach(
|
||||
$invoice->getPDFPath(),
|
||||
array('as' => $invoice->getFileName(), 'mime' => 'application/pdf')
|
||||
);
|
||||
$replyEmail = $fromEmail;
|
||||
$fromEmail = CONTACT_EMAIL;
|
||||
|
||||
if(isset($data['invoice_id'])) {
|
||||
$invoice = Invoice::with('account')->where('id', '=', $data['invoice_id'])->get()->first();
|
||||
if($invoice->account->pdf_email_attachment && file_exists($invoice->getPDFPath())) {
|
||||
$message->attach(
|
||||
$invoice->getPDFPath(),
|
||||
array('as' => $invoice->getFileName(), 'mime' => 'application/pdf')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//$message->setEncoder(\Swift_Encoding::get8BitEncoding());
|
||||
$message->to($toEmail)->from($fromEmail, $fromName)->replyTo($replyEmail, $fromName)->subject($subject);
|
||||
});
|
||||
|
||||
$message->to($toEmail)->from($fromEmail, $fromName)->replyTo($replyEmail, $fromName)->subject($subject);
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
$response = $e->getResponse()->getBody()->getContents();
|
||||
$response = json_decode($response);
|
||||
return nl2br($response->Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -294,6 +294,7 @@ class AccountRepository
|
||||
$item->account_id = $user->account->id;
|
||||
$item->account_name = $user->account->getDisplayName();
|
||||
$item->pro_plan_paid = $user->account->pro_plan_paid;
|
||||
$item->account_key = file_exists($user->account->getLogoPath()) ? $user->account->account_key : null;
|
||||
$data[] = $item;
|
||||
}
|
||||
|
||||
@ -363,11 +364,19 @@ class AccountRepository
|
||||
return $users;
|
||||
}
|
||||
|
||||
public function unlinkAccount($userAccountId, $userId) {
|
||||
public function unlinkAccount($account) {
|
||||
foreach ($account->users as $user) {
|
||||
if ($userAccount = self::findUserAccounts($user->id)) {
|
||||
$userAccount->removeUserId($user->id);
|
||||
$userAccount->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function unlinkUser($userAccountId, $userId) {
|
||||
|
||||
$userAccount = UserAccount::whereId($userAccountId)->first();
|
||||
|
||||
if ($userAccount->hasUserId(Auth::user()->id)) {
|
||||
if ($userAccount->hasUserId($userId)) {
|
||||
$userAccount->removeUserId($userId);
|
||||
$userAccount->save();
|
||||
}
|
||||
|
@ -108,7 +108,14 @@ class PaymentRepository
|
||||
$payment->payment_type_id = $paymentTypeId;
|
||||
}
|
||||
|
||||
$payment->payment_date = Utils::toSqlDate($input['payment_date']);
|
||||
if (isset($input['payment_date_sql'])) {
|
||||
$payment->payment_date = $input['payment_date_sql'];
|
||||
} elseif (isset($input['payment_date'])) {
|
||||
$payment->payment_date = Utils::toSqlDate($input['payment_date']);
|
||||
} else {
|
||||
$payment->payment_date = date('Y-m-d');
|
||||
}
|
||||
|
||||
$payment->transaction_reference = trim($input['transaction_reference']);
|
||||
|
||||
if (!$publicId) {
|
||||
|
26
bower.json
26
bower.json
@ -2,22 +2,22 @@
|
||||
"name": "hillelcoren/invoice-ninja",
|
||||
"version": "0.9.0",
|
||||
"dependencies": {
|
||||
"jquery": "~1.11",
|
||||
"bootstrap": "~3.*",
|
||||
"jquery-ui": "~1.*",
|
||||
"jquery": "1.11.3",
|
||||
"bootstrap": "3.3.1",
|
||||
"jquery-ui": "1.11.2",
|
||||
"datatables": "1.10.4",
|
||||
"datatables-bootstrap3": "*",
|
||||
"knockout.js": "~3.*",
|
||||
"knockout-mapping": "*",
|
||||
"knockout-sortable": "*",
|
||||
"knockout.js": "3.1.0",
|
||||
"knockout-mapping": "2.4.1",
|
||||
"knockout-sortable": "0.9.3",
|
||||
"font-awesome": "~4.*",
|
||||
"underscore": "~1.*",
|
||||
"jspdf": "*",
|
||||
"bootstrap-datepicker": "~1.*",
|
||||
"typeahead.js": "~0.9.3",
|
||||
"accounting": "~0.*",
|
||||
"spectrum": "~1.3.4",
|
||||
"d3": "~3.4.11",
|
||||
"underscore": "1.7.0",
|
||||
"jspdf": "1.0.272",
|
||||
"bootstrap-datepicker": "1.4.0",
|
||||
"typeahead.js": "0.9.3",
|
||||
"accounting": "0.3.2",
|
||||
"spectrum": "1.3.4",
|
||||
"d3": "3.4.11",
|
||||
"handsontable": "*",
|
||||
"pdfmake": "*",
|
||||
"moment": "*"
|
||||
|
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class SupportLockingAccount extends Migration {
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function($table)
|
||||
{
|
||||
$table->smallInteger('failed_logins')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('account_gateways', function($table)
|
||||
{
|
||||
$table->boolean('show_address')->default(true)->nullable();
|
||||
$table->boolean('update_address')->default(true)->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function($table)
|
||||
{
|
||||
$table->dropColumn('failed_logins');
|
||||
});
|
||||
|
||||
Schema::table('account_gateways', function($table)
|
||||
{
|
||||
$table->dropColumn('show_address');
|
||||
$table->dropColumn('update_address');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
60
public/css/built.css
vendored
60
public/css/built.css
vendored
File diff suppressed because one or more lines are too long
4
public/css/built.public.css
vendored
4
public/css/built.public.css
vendored
File diff suppressed because one or more lines are too long
BIN
public/fonts/fontawesome-webfont.woff2
Normal file
BIN
public/fonts/fontawesome-webfont.woff2
Normal file
Binary file not shown.
@ -27152,31 +27152,13 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
}
|
||||
}.call(this));
|
||||
|
||||
/* =========================================================
|
||||
* bootstrap-datepicker.js
|
||||
* Repo: https://github.com/eternicode/bootstrap-datepicker/
|
||||
* Demo: http://eternicode.github.io/bootstrap-datepicker/
|
||||
* Docs: http://bootstrap-datepicker.readthedocs.org/
|
||||
* Forked from http://www.eyecon.ro/bootstrap-datepicker
|
||||
* =========================================================
|
||||
* Started by Stefan Petre; improvements by Andrew Rowls + contributors
|
||||
/*!
|
||||
* Datepicker for Bootstrap v1.4.0 (https://github.com/eternicode/bootstrap-datepicker)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ========================================================= */
|
||||
|
||||
(function($, undefined){
|
||||
|
||||
var $window = $(window);
|
||||
* Copyright 2012 Stefan Petre
|
||||
* Improvements by Andrew Rowls
|
||||
* Licensed under the Apache License v2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
*/(function($, undefined){
|
||||
|
||||
function UTCDate(){
|
||||
return new Date(Date.UTC.apply(Date, arguments));
|
||||
@ -27185,6 +27167,13 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
var today = new Date();
|
||||
return UTCDate(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
}
|
||||
function isUTCEquals(date1, date2) {
|
||||
return (
|
||||
date1.getUTCFullYear() === date2.getUTCFullYear() &&
|
||||
date1.getUTCMonth() === date2.getUTCMonth() &&
|
||||
date1.getUTCDate() === date2.getUTCDate()
|
||||
);
|
||||
}
|
||||
function alias(method){
|
||||
return function(){
|
||||
return this[method].apply(this, arguments);
|
||||
@ -27238,16 +27227,16 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
// Picker object
|
||||
|
||||
var Datepicker = function(element, options){
|
||||
this.dates = new DateArray();
|
||||
this.viewDate = UTCToday();
|
||||
this.focusDate = null;
|
||||
|
||||
this._process_options(options);
|
||||
|
||||
this.dates = new DateArray();
|
||||
this.viewDate = this.o.defaultViewDate;
|
||||
this.focusDate = null;
|
||||
|
||||
this.element = $(element);
|
||||
this.isInline = false;
|
||||
this.isInput = this.element.is('input');
|
||||
this.component = this.element.is('.date') ? this.element.find('.add-on, .input-group-addon, .btn') : false;
|
||||
this.component = this.element.hasClass('date') ? this.element.find('.add-on, .input-group-addon, .btn') : false;
|
||||
this.hasInput = this.component && this.element.find('input').length;
|
||||
if (this.component && this.component.length === 0)
|
||||
this.component = false;
|
||||
@ -27270,7 +27259,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
this.viewMode = this.o.startView;
|
||||
|
||||
if (this.o.calendarWeeks)
|
||||
this.picker.find('tfoot th.today, tfoot th.clear')
|
||||
this.picker.find('tfoot .today, tfoot .clear')
|
||||
.attr('colspan', function(i, val){
|
||||
return parseInt(val) + 1;
|
||||
});
|
||||
@ -27280,6 +27269,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
this.setStartDate(this._o.startDate);
|
||||
this.setEndDate(this._o.endDate);
|
||||
this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled);
|
||||
this.setDatesDisabled(this.o.datesDisabled);
|
||||
|
||||
this.fillDow();
|
||||
this.fillMonths();
|
||||
@ -27346,8 +27336,6 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
o.multidate = Number(o.multidate) || false;
|
||||
if (o.multidate !== false)
|
||||
o.multidate = Math.max(0, o.multidate);
|
||||
else
|
||||
o.multidate = 1;
|
||||
}
|
||||
o.multidateSeparator = String(o.multidateSeparator);
|
||||
|
||||
@ -27385,10 +27373,20 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
return parseInt(d, 10);
|
||||
});
|
||||
|
||||
o.datesDisabled = o.datesDisabled||[];
|
||||
if (!$.isArray(o.datesDisabled)) {
|
||||
var datesDisabled = [];
|
||||
datesDisabled.push(DPGlobal.parseDate(o.datesDisabled, format, o.language));
|
||||
o.datesDisabled = datesDisabled;
|
||||
}
|
||||
o.datesDisabled = $.map(o.datesDisabled,function(d){
|
||||
return DPGlobal.parseDate(d, format, o.language);
|
||||
});
|
||||
|
||||
var plc = String(o.orientation).toLowerCase().split(/\s+/g),
|
||||
_plc = o.orientation.toLowerCase();
|
||||
plc = $.grep(plc, function(word){
|
||||
return (/^auto|left|right|top|bottom$/).test(word);
|
||||
return /^auto|left|right|top|bottom$/.test(word);
|
||||
});
|
||||
o.orientation = {x: 'auto', y: 'auto'};
|
||||
if (!_plc || _plc === 'auto')
|
||||
@ -27407,15 +27405,24 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
}
|
||||
else {
|
||||
_plc = $.grep(plc, function(word){
|
||||
return (/^left|right$/).test(word);
|
||||
return /^left|right$/.test(word);
|
||||
});
|
||||
o.orientation.x = _plc[0] || 'auto';
|
||||
|
||||
_plc = $.grep(plc, function(word){
|
||||
return (/^top|bottom$/).test(word);
|
||||
return /^top|bottom$/.test(word);
|
||||
});
|
||||
o.orientation.y = _plc[0] || 'auto';
|
||||
}
|
||||
if (o.defaultViewDate) {
|
||||
var year = o.defaultViewDate.year || new Date().getFullYear();
|
||||
var month = o.defaultViewDate.month || 0;
|
||||
var day = o.defaultViewDate.day || 1;
|
||||
o.defaultViewDate = UTCDate(year, month, day);
|
||||
} else {
|
||||
o.defaultViewDate = UTCToday();
|
||||
}
|
||||
o.showOnFocus = o.showOnFocus !== undefined ? o.showOnFocus : true;
|
||||
},
|
||||
_events: [],
|
||||
_secondaryEvents: [],
|
||||
@ -27448,34 +27455,32 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
}
|
||||
},
|
||||
_buildEvents: function(){
|
||||
if (this.isInput){ // single input
|
||||
this._events = [
|
||||
[this.element, {
|
||||
focus: $.proxy(this.show, this),
|
||||
keyup: $.proxy(function(e){
|
||||
if ($.inArray(e.keyCode, [27,37,39,38,40,32,13,9]) === -1)
|
||||
this.update();
|
||||
}, this),
|
||||
keydown: $.proxy(this.keydown, this)
|
||||
}]
|
||||
];
|
||||
}
|
||||
else if (this.component && this.hasInput){ // component: input + button
|
||||
this._events = [
|
||||
// For components that are not readonly, allow keyboard nav
|
||||
[this.element.find('input'), {
|
||||
focus: $.proxy(this.show, this),
|
||||
keyup: $.proxy(function(e){
|
||||
if ($.inArray(e.keyCode, [27,37,39,38,40,32,13,9]) === -1)
|
||||
this.update();
|
||||
}, this),
|
||||
keydown: $.proxy(this.keydown, this)
|
||||
}],
|
||||
[this.component, {
|
||||
click: $.proxy(this.show, this)
|
||||
}]
|
||||
];
|
||||
}
|
||||
var events = {
|
||||
keyup: $.proxy(function(e){
|
||||
if ($.inArray(e.keyCode, [27, 37, 39, 38, 40, 32, 13, 9]) === -1)
|
||||
this.update();
|
||||
}, this),
|
||||
keydown: $.proxy(this.keydown, this)
|
||||
};
|
||||
|
||||
if (this.o.showOnFocus === true) {
|
||||
events.focus = $.proxy(this.show, this);
|
||||
}
|
||||
|
||||
if (this.isInput) { // single input
|
||||
this._events = [
|
||||
[this.element, events]
|
||||
];
|
||||
}
|
||||
else if (this.component && this.hasInput) { // component: input + button
|
||||
this._events = [
|
||||
// For components that are not readonly, allow keyboard nav
|
||||
[this.element.find('input'), events],
|
||||
[this.component, {
|
||||
click: $.proxy(this.show, this)
|
||||
}]
|
||||
];
|
||||
}
|
||||
else if (this.element.is('div')){ // inline datepicker
|
||||
this.isInline = true;
|
||||
}
|
||||
@ -27562,19 +27567,25 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
},
|
||||
|
||||
show: function(){
|
||||
if (this.element.attr('readonly') && this.o.enableOnReadonly === false)
|
||||
return;
|
||||
if (!this.isInline)
|
||||
this.picker.appendTo('body');
|
||||
this.picker.show();
|
||||
this.picker.appendTo(this.o.container);
|
||||
this.place();
|
||||
this.picker.show();
|
||||
this._attachSecondaryEvents();
|
||||
this._trigger('show');
|
||||
if ((window.navigator.msMaxTouchPoints || 'ontouchstart' in document) && this.o.disableTouchKeyboard) {
|
||||
$(this.element).blur();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
hide: function(){
|
||||
if (this.isInline)
|
||||
return;
|
||||
return this;
|
||||
if (!this.picker.is(':visible'))
|
||||
return;
|
||||
return this;
|
||||
this.focusDate = null;
|
||||
this.picker.hide().detach();
|
||||
this._detachSecondaryEvents();
|
||||
@ -27590,6 +27601,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
)
|
||||
this.setValue();
|
||||
this._trigger('hide');
|
||||
return this;
|
||||
},
|
||||
|
||||
remove: function(){
|
||||
@ -27601,6 +27613,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
if (!this.isInput){
|
||||
delete this.element.data().date;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
_utc_to_local: function(utc){
|
||||
@ -27631,14 +27644,39 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
},
|
||||
|
||||
getUTCDate: function(){
|
||||
return new Date(this.dates.get(-1));
|
||||
var selected_date = this.dates.get(-1);
|
||||
if (typeof selected_date !== 'undefined') {
|
||||
return new Date(selected_date);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
clearDates: function(){
|
||||
var element;
|
||||
if (this.isInput) {
|
||||
element = this.element;
|
||||
} else if (this.component) {
|
||||
element = this.element.find('input');
|
||||
}
|
||||
|
||||
if (element) {
|
||||
element.val('').change();
|
||||
}
|
||||
|
||||
this.update();
|
||||
this._trigger('changeDate');
|
||||
|
||||
if (this.o.autoclose) {
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
setDates: function(){
|
||||
var args = $.isArray(arguments[0]) ? arguments[0] : arguments;
|
||||
this.update.apply(this, args);
|
||||
this._trigger('changeDate');
|
||||
this.setValue();
|
||||
return this;
|
||||
},
|
||||
|
||||
setUTCDates: function(){
|
||||
@ -27646,6 +27684,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
this.update.apply(this, $.map(args, this._utc_to_local));
|
||||
this._trigger('changeDate');
|
||||
this.setValue();
|
||||
return this;
|
||||
},
|
||||
|
||||
setDate: alias('setDates'),
|
||||
@ -27661,6 +27700,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
else {
|
||||
this.element.val(formatted).change();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
getFormattedDate: function(format){
|
||||
@ -27677,41 +27717,51 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
this._process_options({startDate: startDate});
|
||||
this.update();
|
||||
this.updateNavArrows();
|
||||
return this;
|
||||
},
|
||||
|
||||
setEndDate: function(endDate){
|
||||
this._process_options({endDate: endDate});
|
||||
this.update();
|
||||
this.updateNavArrows();
|
||||
return this;
|
||||
},
|
||||
|
||||
setDaysOfWeekDisabled: function(daysOfWeekDisabled){
|
||||
this._process_options({daysOfWeekDisabled: daysOfWeekDisabled});
|
||||
this.update();
|
||||
this.updateNavArrows();
|
||||
return this;
|
||||
},
|
||||
|
||||
setDatesDisabled: function(datesDisabled){
|
||||
this._process_options({datesDisabled: datesDisabled});
|
||||
this.update();
|
||||
this.updateNavArrows();
|
||||
},
|
||||
|
||||
place: function(){
|
||||
if (this.isInline)
|
||||
return;
|
||||
return this;
|
||||
var calendarWidth = this.picker.outerWidth(),
|
||||
calendarHeight = this.picker.outerHeight(),
|
||||
visualPadding = 10,
|
||||
windowWidth = $window.width(),
|
||||
windowHeight = $window.height(),
|
||||
scrollTop = $window.scrollTop();
|
||||
windowWidth = $(this.o.container).width(),
|
||||
windowHeight = $(this.o.container).height(),
|
||||
scrollTop = $(this.o.container).scrollTop(),
|
||||
appendOffset = $(this.o.container).offset();
|
||||
|
||||
var parentsZindex = [];
|
||||
this.element.parents().each(function() {
|
||||
this.element.parents().each(function(){
|
||||
var itemZIndex = $(this).css('z-index');
|
||||
if ( itemZIndex !== 'auto' && itemZIndex !== 0 ) parentsZindex.push( parseInt( itemZIndex ) );
|
||||
if (itemZIndex !== 'auto' && itemZIndex !== 0) parentsZindex.push(parseInt(itemZIndex));
|
||||
});
|
||||
var zIndex = Math.max.apply( Math, parentsZindex ) + 10;
|
||||
var zIndex = Math.max.apply(Math, parentsZindex) + 10;
|
||||
var offset = this.component ? this.component.parent().offset() : this.element.offset();
|
||||
var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false);
|
||||
var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false);
|
||||
var left = offset.left,
|
||||
top = offset.top;
|
||||
var left = offset.left - appendOffset.left,
|
||||
top = offset.top - appendOffset.top;
|
||||
|
||||
this.picker.removeClass(
|
||||
'datepicker-orient-top datepicker-orient-bottom '+
|
||||
@ -27726,12 +27776,18 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
// auto x orientation is best-placement: if it crosses a window
|
||||
// edge, fudge it sideways
|
||||
else {
|
||||
// Default to left
|
||||
this.picker.addClass('datepicker-orient-left');
|
||||
if (offset.left < 0)
|
||||
if (offset.left < 0) {
|
||||
// component is outside the window on the left side. Move it into visible range
|
||||
this.picker.addClass('datepicker-orient-left');
|
||||
left -= offset.left - visualPadding;
|
||||
else if (offset.left + calendarWidth > windowWidth)
|
||||
left = windowWidth - calendarWidth - visualPadding;
|
||||
} else if (left + calendarWidth > windowWidth) {
|
||||
// the calendar passes the widow right edge. Align it to component right side
|
||||
this.picker.addClass('datepicker-orient-right');
|
||||
left = offset.left + width - calendarWidth;
|
||||
} else {
|
||||
// Default to left
|
||||
this.picker.addClass('datepicker-orient-left');
|
||||
}
|
||||
}
|
||||
|
||||
// auto y orientation is best-situation: top or bottom, no fudging,
|
||||
@ -27739,8 +27795,8 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
var yorient = this.o.orientation.y,
|
||||
top_overflow, bottom_overflow;
|
||||
if (yorient === 'auto'){
|
||||
top_overflow = -scrollTop + offset.top - calendarHeight;
|
||||
bottom_overflow = scrollTop + windowHeight - (offset.top + height + calendarHeight);
|
||||
top_overflow = -scrollTop + top - calendarHeight;
|
||||
bottom_overflow = scrollTop + windowHeight - (top + height + calendarHeight);
|
||||
if (Math.max(top_overflow, bottom_overflow) === bottom_overflow)
|
||||
yorient = 'top';
|
||||
else
|
||||
@ -27752,17 +27808,27 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
else
|
||||
top -= calendarHeight + parseInt(this.picker.css('padding-top'));
|
||||
|
||||
this.picker.css({
|
||||
top: top,
|
||||
left: left,
|
||||
zIndex: zIndex
|
||||
});
|
||||
if (this.o.rtl) {
|
||||
var right = windowWidth - (left + width);
|
||||
this.picker.css({
|
||||
top: top,
|
||||
right: right,
|
||||
zIndex: zIndex
|
||||
});
|
||||
} else {
|
||||
this.picker.css({
|
||||
top: top,
|
||||
left: left,
|
||||
zIndex: zIndex
|
||||
});
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
_allow_update: true,
|
||||
update: function(){
|
||||
if (!this._allow_update)
|
||||
return;
|
||||
return this;
|
||||
|
||||
var oldDates = this.dates.copy(),
|
||||
dates = [],
|
||||
@ -27818,15 +27884,19 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
this._trigger('clearDate');
|
||||
|
||||
this.fill();
|
||||
return this;
|
||||
},
|
||||
|
||||
fillDow: function(){
|
||||
var dowCnt = this.o.weekStart,
|
||||
html = '<tr>';
|
||||
if (this.o.calendarWeeks){
|
||||
var cell = '<th class="cw"> </th>';
|
||||
this.picker.find('.datepicker-days thead tr:first-child .datepicker-switch')
|
||||
.attr('colspan', function(i, val){
|
||||
return parseInt(val) + 1;
|
||||
});
|
||||
var cell = '<th class="cw"> </th>';
|
||||
html += cell;
|
||||
this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
|
||||
}
|
||||
while (dowCnt < this.o.weekStart + 7){
|
||||
html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
|
||||
@ -27880,6 +27950,12 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
$.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1){
|
||||
cls.push('disabled');
|
||||
}
|
||||
if (this.o.datesDisabled.length > 0 &&
|
||||
$.grep(this.o.datesDisabled, function(d){
|
||||
return isUTCEquals(date, d); }).length > 0) {
|
||||
cls.push('disabled', 'disabled-date');
|
||||
}
|
||||
|
||||
if (this.range){
|
||||
if (date > this.range[0] && date < this.range[this.range.length-1]){
|
||||
cls.push('range');
|
||||
@ -27902,13 +27978,14 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
todaytxt = dates[this.o.language].today || dates['en'].today || '',
|
||||
cleartxt = dates[this.o.language].clear || dates['en'].clear || '',
|
||||
tooltip;
|
||||
if (isNaN(year) || isNaN(month)) return;
|
||||
this.picker.find('.datepicker-days thead th.datepicker-switch')
|
||||
if (isNaN(year) || isNaN(month))
|
||||
return;
|
||||
this.picker.find('.datepicker-days thead .datepicker-switch')
|
||||
.text(dates[this.o.language].months[month]+' '+year);
|
||||
this.picker.find('tfoot th.today')
|
||||
this.picker.find('tfoot .today')
|
||||
.text(todaytxt)
|
||||
.toggle(this.o.todayBtn !== false);
|
||||
this.picker.find('tfoot th.clear')
|
||||
this.picker.find('tfoot .clear')
|
||||
.text(cleartxt)
|
||||
.toggle(this.o.clearBtn !== false);
|
||||
this.updateNavArrows();
|
||||
@ -27991,6 +28068,18 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
months.slice(endMonth+1).addClass('disabled');
|
||||
}
|
||||
|
||||
if (this.o.beforeShowMonth !== $.noop){
|
||||
var that = this;
|
||||
$.each(months, function(i, month){
|
||||
if (!$(month).hasClass('disabled')) {
|
||||
var moDate = new Date(year, i, 1);
|
||||
var before = that.o.beforeShowMonth(moDate);
|
||||
if (before === false)
|
||||
$(month).addClass('disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
html = '';
|
||||
year = parseInt(year/10, 10) * 10;
|
||||
var yearCont = this.picker.find('.datepicker-years')
|
||||
@ -28013,7 +28102,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
classes.push('active');
|
||||
if (year < startYear || year > endYear)
|
||||
classes.push('disabled');
|
||||
html += '<span class="' + classes.join(' ') + '">'+year+'</span>';
|
||||
html += '<span class="' + classes.join(' ') + '">' + year + '</span>';
|
||||
year += 1;
|
||||
}
|
||||
yearCont.html(html);
|
||||
@ -28096,24 +28185,14 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
this._setDate(date, which);
|
||||
break;
|
||||
case 'clear':
|
||||
var element;
|
||||
if (this.isInput)
|
||||
element = this.element;
|
||||
else if (this.component)
|
||||
element = this.element.find('input');
|
||||
if (element)
|
||||
element.val("").change();
|
||||
this.update();
|
||||
this._trigger('changeDate');
|
||||
if (this.o.autoclose)
|
||||
this.hide();
|
||||
this.clearDates();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'span':
|
||||
if (!target.is('.disabled')){
|
||||
if (!target.hasClass('disabled')){
|
||||
this.viewDate.setUTCDate(1);
|
||||
if (target.is('.month')){
|
||||
if (target.hasClass('month')){
|
||||
day = 1;
|
||||
month = target.parent().find('span').index(target);
|
||||
year = this.viewDate.getUTCFullYear();
|
||||
@ -28138,11 +28217,11 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
}
|
||||
break;
|
||||
case 'td':
|
||||
if (target.is('.day') && !target.is('.disabled')){
|
||||
if (target.hasClass('day') && !target.hasClass('disabled')){
|
||||
day = parseInt(target.text(), 10)||1;
|
||||
year = this.viewDate.getUTCFullYear();
|
||||
month = this.viewDate.getUTCMonth();
|
||||
if (target.is('.old')){
|
||||
if (target.hasClass('old')){
|
||||
if (month === 0){
|
||||
month = 11;
|
||||
year -= 1;
|
||||
@ -28151,7 +28230,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
month -= 1;
|
||||
}
|
||||
}
|
||||
else if (target.is('.new')){
|
||||
else if (target.hasClass('new')){
|
||||
if (month === 11){
|
||||
month = 0;
|
||||
year += 1;
|
||||
@ -28176,15 +28255,19 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
if (!date){
|
||||
this.dates.clear();
|
||||
}
|
||||
if (this.o.multidate === 1 && ix === 0){
|
||||
// single datepicker, don't remove selected date
|
||||
}
|
||||
else if (ix !== -1){
|
||||
this.dates.remove(ix);
|
||||
|
||||
if (ix !== -1){
|
||||
if (this.o.multidate === true || this.o.multidate > 1 || this.o.toggleActive){
|
||||
this.dates.remove(ix);
|
||||
}
|
||||
} else if (this.o.multidate === false) {
|
||||
this.dates.clear();
|
||||
this.dates.push(date);
|
||||
}
|
||||
else {
|
||||
this.dates.push(date);
|
||||
}
|
||||
|
||||
if (typeof this.o.multidate === 'number')
|
||||
while (this.dates.length > this.o.multidate)
|
||||
this.dates.remove(0);
|
||||
@ -28198,7 +28281,9 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
|
||||
this.fill();
|
||||
this.setValue();
|
||||
this._trigger('changeDate');
|
||||
if (!which || which !== 'view') {
|
||||
this._trigger('changeDate');
|
||||
}
|
||||
var element;
|
||||
if (this.isInput){
|
||||
element = this.element;
|
||||
@ -28273,7 +28358,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
},
|
||||
|
||||
keydown: function(e){
|
||||
if (this.picker.is(':not(:visible)')){
|
||||
if (!this.picker.is(':visible')){
|
||||
if (e.keyCode === 27) // allow escape to hide and re-show picker
|
||||
this.show();
|
||||
return;
|
||||
@ -28313,7 +28398,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
newViewDate = new Date(focusDate);
|
||||
newViewDate.setUTCDate(focusDate.getUTCDate() + dir);
|
||||
}
|
||||
if (this.dateWithinRange(newDate)){
|
||||
if (this.dateWithinRange(newViewDate)){
|
||||
this.focusDate = this.viewDate = newViewDate;
|
||||
this.setValue();
|
||||
this.fill();
|
||||
@ -28341,7 +28426,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
newViewDate = new Date(focusDate);
|
||||
newViewDate.setUTCDate(focusDate.getUTCDate() + dir * 7);
|
||||
}
|
||||
if (this.dateWithinRange(newDate)){
|
||||
if (this.dateWithinRange(newViewDate)){
|
||||
this.focusDate = this.viewDate = newViewDate;
|
||||
this.setValue();
|
||||
this.fill();
|
||||
@ -28364,6 +28449,11 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
this.fill();
|
||||
if (this.picker.is(':visible')){
|
||||
e.preventDefault();
|
||||
if (typeof e.stopPropagation === 'function') {
|
||||
e.stopPropagation(); // All modern browsers, IE9+
|
||||
} else {
|
||||
e.cancelBubble = true; // IE6,7,8 ignore "stopPropagation"
|
||||
}
|
||||
if (this.o.autoclose)
|
||||
this.hide();
|
||||
}
|
||||
@ -28398,9 +28488,9 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
|
||||
}
|
||||
this.picker
|
||||
.find('>div')
|
||||
.children('div')
|
||||
.hide()
|
||||
.filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName)
|
||||
.filter('.datepicker-' + DPGlobal.modes[this.viewMode].clsName)
|
||||
.css('display', 'block');
|
||||
this.updateNavArrows();
|
||||
}
|
||||
@ -28413,8 +28503,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
});
|
||||
delete options.inputs;
|
||||
|
||||
$(this.inputs)
|
||||
.datepicker(options)
|
||||
datepickerPlugin.call($(this.inputs), options)
|
||||
.bind('changeDate', $.proxy(this.dateUpdated, this));
|
||||
|
||||
this.pickers = $.map(this.inputs, function(i){
|
||||
@ -28448,6 +28537,8 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
var dp = $(e.target).data('datepicker'),
|
||||
new_date = dp.getUTCDate(),
|
||||
i = $.inArray(e.target, this.inputs),
|
||||
j = i - 1,
|
||||
k = i + 1,
|
||||
l = this.inputs.length;
|
||||
if (i === -1)
|
||||
return;
|
||||
@ -28457,16 +28548,16 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
p.setUTCDate(new_date);
|
||||
});
|
||||
|
||||
if (new_date < this.dates[i]){
|
||||
if (new_date < this.dates[j]){
|
||||
// Date being moved earlier/left
|
||||
while (i >= 0 && new_date < this.dates[i]){
|
||||
this.pickers[i--].setUTCDate(new_date);
|
||||
while (j >= 0 && new_date < this.dates[j]){
|
||||
this.pickers[j--].setUTCDate(new_date);
|
||||
}
|
||||
}
|
||||
else if (new_date > this.dates[i]){
|
||||
else if (new_date > this.dates[k]){
|
||||
// Date being moved later/right
|
||||
while (i < l && new_date > this.dates[i]){
|
||||
this.pickers[i++].setUTCDate(new_date);
|
||||
while (k < l && new_date > this.dates[k]){
|
||||
this.pickers[k++].setUTCDate(new_date);
|
||||
}
|
||||
}
|
||||
this.updateDates();
|
||||
@ -28515,7 +28606,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
}
|
||||
|
||||
var old = $.fn.datepicker;
|
||||
$.fn.datepicker = function(option){
|
||||
var datepickerPlugin = function(option){
|
||||
var args = Array.apply(null, arguments);
|
||||
args.shift();
|
||||
var internal_return;
|
||||
@ -28530,7 +28621,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
locopts = opts_from_locale(xopts.language),
|
||||
// Options priority: js args, data-attrs, locales, defaults
|
||||
opts = $.extend({}, defaults, locopts, elopts, options);
|
||||
if ($this.is('.input-daterange') || opts.inputs){
|
||||
if ($this.hasClass('input-daterange') || opts.inputs){
|
||||
var ropts = {
|
||||
inputs: opts.inputs || $this.find('input').toArray()
|
||||
};
|
||||
@ -28551,13 +28642,17 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
else
|
||||
return this;
|
||||
};
|
||||
$.fn.datepicker = datepickerPlugin;
|
||||
|
||||
var defaults = $.fn.datepicker.defaults = {
|
||||
autoclose: false,
|
||||
beforeShowDay: $.noop,
|
||||
beforeShowMonth: $.noop,
|
||||
calendarWeeks: false,
|
||||
clearBtn: false,
|
||||
toggleActive: false,
|
||||
daysOfWeekDisabled: [],
|
||||
datesDisabled: [],
|
||||
endDate: Infinity,
|
||||
forceParse: true,
|
||||
format: 'mm/dd/yyyy',
|
||||
@ -28572,7 +28667,10 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
startView: 0,
|
||||
todayBtn: false,
|
||||
todayHighlight: false,
|
||||
weekStart: 0
|
||||
weekStart: 0,
|
||||
disableTouchKeyboard: false,
|
||||
enableOnReadonly: true,
|
||||
container: 'body'
|
||||
};
|
||||
var locale_opts = $.fn.datepicker.locale_opts = [
|
||||
'format',
|
||||
@ -28700,7 +28798,7 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
function match_part(){
|
||||
var m = this.slice(0, parts[i].length),
|
||||
p = parts[i].slice(0, m.length);
|
||||
return m === p;
|
||||
return m.toLowerCase() === p.toLowerCase();
|
||||
}
|
||||
if (parts.length === fparts.length){
|
||||
var cnt;
|
||||
@ -28762,9 +28860,9 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
},
|
||||
headTemplate: '<thead>'+
|
||||
'<tr>'+
|
||||
'<th class="prev">«</th>'+
|
||||
'<th class="prev">«</th>'+
|
||||
'<th colspan="5" class="datepicker-switch"></th>'+
|
||||
'<th class="next">»</th>'+
|
||||
'<th class="next">»</th>'+
|
||||
'</tr>'+
|
||||
'</thead>',
|
||||
contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
|
||||
@ -28812,6 +28910,9 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
return this;
|
||||
};
|
||||
|
||||
/* DATEPICKER VERSION
|
||||
* =================== */
|
||||
$.fn.datepicker.version = "1.4.0";
|
||||
|
||||
/* DATEPICKER DATA-API
|
||||
* ================== */
|
||||
@ -28825,11 +28926,11 @@ d[b]="undefined"!==f.getType(g)?g:f.visitModel(j,c,a);break;default:d[b]=c(j,a.p
|
||||
return;
|
||||
e.preventDefault();
|
||||
// component click requires us to explicitly show it
|
||||
$this.datepicker('show');
|
||||
datepickerPlugin.call($this, 'show');
|
||||
}
|
||||
);
|
||||
$(function(){
|
||||
$('[data-provide="datepicker-inline"]').datepicker();
|
||||
datepickerPlugin.call($('[data-provide="datepicker-inline"]'));
|
||||
});
|
||||
|
||||
}(window.jQuery));
|
||||
@ -32240,8 +32341,12 @@ function getInvoiceDetails(invoice) {
|
||||
{'due_date': invoice.due_date},
|
||||
];
|
||||
|
||||
if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) {
|
||||
fields.push({'total': formatMoney(invoice.amount, invoice.client.currency_id)});
|
||||
}
|
||||
|
||||
if (NINJA.parseFloat(invoice.partial)) {
|
||||
fields.push({'total': formatMoney(invoice.total_amount, invoice.client.currency_id)});
|
||||
fields.push({'balance': formatMoney(invoice.total_amount, invoice.client.currency_id)});
|
||||
}
|
||||
|
||||
fields.push({'balance_due': formatMoney(invoice.balance_amount, invoice.client.currency_id)})
|
||||
@ -32297,12 +32402,16 @@ function displaySubtotals(doc, layout, invoice, y, rightAlignTitleX)
|
||||
}
|
||||
|
||||
var paid = invoice.amount - invoice.balance;
|
||||
if (paid) {
|
||||
data.push({'total': formatMoney(invoice.amount, invoice.client.currency_id)});
|
||||
}
|
||||
|
||||
if (invoice.account.hide_paid_to_date != '1' || paid) {
|
||||
data.push({'paid_to_date': formatMoney(paid, invoice.client.currency_id)});
|
||||
}
|
||||
|
||||
if (NINJA.parseFloat(invoice.partial) && invoice.total_amount != invoice.subtotal_amount) {
|
||||
data.push({'total': formatMoney(invoice.total_amount, invoice.client.currency_id)});
|
||||
data.push({'balance': formatMoney(invoice.total_amount, invoice.client.currency_id)});
|
||||
}
|
||||
|
||||
var options = {
|
||||
|
32
public/js/pdfmake.min.js
vendored
32
public/js/pdfmake.min.js
vendored
File diff suppressed because one or more lines are too long
@ -705,8 +705,12 @@ function getInvoiceDetails(invoice) {
|
||||
{'due_date': invoice.due_date},
|
||||
];
|
||||
|
||||
if (NINJA.parseFloat(invoice.balance) < NINJA.parseFloat(invoice.amount)) {
|
||||
fields.push({'total': formatMoney(invoice.amount, invoice.client.currency_id)});
|
||||
}
|
||||
|
||||
if (NINJA.parseFloat(invoice.partial)) {
|
||||
fields.push({'total': formatMoney(invoice.total_amount, invoice.client.currency_id)});
|
||||
fields.push({'balance': formatMoney(invoice.total_amount, invoice.client.currency_id)});
|
||||
}
|
||||
|
||||
fields.push({'balance_due': formatMoney(invoice.balance_amount, invoice.client.currency_id)})
|
||||
@ -762,12 +766,16 @@ function displaySubtotals(doc, layout, invoice, y, rightAlignTitleX)
|
||||
}
|
||||
|
||||
var paid = invoice.amount - invoice.balance;
|
||||
if (paid) {
|
||||
data.push({'total': formatMoney(invoice.amount, invoice.client.currency_id)});
|
||||
}
|
||||
|
||||
if (invoice.account.hide_paid_to_date != '1' || paid) {
|
||||
data.push({'paid_to_date': formatMoney(paid, invoice.client.currency_id)});
|
||||
}
|
||||
|
||||
if (NINJA.parseFloat(invoice.partial) && invoice.total_amount != invoice.subtotal_amount) {
|
||||
data.push({'total': formatMoney(invoice.total_amount, invoice.client.currency_id)});
|
||||
data.push({'balance': formatMoney(invoice.total_amount, invoice.client.currency_id)});
|
||||
}
|
||||
|
||||
var options = {
|
||||
|
@ -419,7 +419,7 @@ return array(
|
||||
'confirm_email_quote' => 'Bist du sicher, dass du dieses Angebot per E-Mail versenden möchtest',
|
||||
'confirm_recurring_email_invoice' => 'Wiederkehrende Rechnung ist aktiv. Bis du sicher, dass du diese Rechnung weiterhin als E-Mail verschicken möchtest?',
|
||||
|
||||
'cancel_account' => 'Account Kündigen',
|
||||
'cancel_account' => 'Konto Kündigen',
|
||||
'cancel_account_message' => 'Warnung: Alle Daten werden unwiderruflich und vollständig gelöscht, es gibt kein zurück.',
|
||||
'go_back' => 'Zurück',
|
||||
|
||||
@ -481,7 +481,7 @@ return array(
|
||||
'restored_client' => 'Kunde erfolgreich wiederhergestellt',
|
||||
'restored_payment' => 'Zahlung erfolgreich wiederhergestellt',
|
||||
'restored_credit' => 'Guthaben erfolgreich wiederhergestellt',
|
||||
|
||||
|
||||
'reason_for_canceling' => 'Hilf uns, unser Angebot zu verbessern, indem du uns mitteilst, weswegen du dich dazu entschieden hast, unseren Service nicht länger zu nutzen.',
|
||||
'discount_percent' => 'Prozent',
|
||||
'discount_amount' => 'Wert',
|
||||
@ -562,7 +562,7 @@ return array(
|
||||
|
||||
'api_tokens' => 'API Token',
|
||||
'users_and_tokens' => 'Benutzer & Token',
|
||||
'account_login' => 'Account Login',
|
||||
'account_login' => 'Konto Login',
|
||||
'recover_password' => 'Passwort wiederherstellen',
|
||||
'forgot_password' => 'Passwort vergessen?',
|
||||
'email_address' => 'E-Mail-Adresse',
|
||||
@ -597,7 +597,7 @@ return array(
|
||||
'view_documentation' => 'Dokumentation anzeigen',
|
||||
'app_title' => 'Kostenlose Online Open-Source Rechnungsausstellung',
|
||||
'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.',
|
||||
|
||||
|
||||
'rows' => 'Zeilen',
|
||||
'www' => 'www',
|
||||
'logo' => 'Logo',
|
||||
@ -660,7 +660,7 @@ return array(
|
||||
'create_task' => 'Aufgabe erstellen',
|
||||
'stopped_task' => 'Aufgabe erfolgreich angehalten',
|
||||
'invoice_task' => 'Aufgabe in Rechnung stellen',
|
||||
'invoice_labels' => 'Rechnung Etiketten',
|
||||
'invoice_labels' => 'Rechnung Spaltenüberschriften',
|
||||
'prefix' => 'Präfix',
|
||||
'counter' => 'Zähler',
|
||||
|
||||
@ -670,32 +670,32 @@ return array(
|
||||
'more_actions' => 'More Actions',
|
||||
|
||||
'pro_plan_title' => 'NINJA PRO',
|
||||
'pro_plan_call_to_action' => 'Upgrade Now!',
|
||||
'pro_plan_feature1' => 'Create Unlimited Clients',
|
||||
'pro_plan_feature2' => 'Access to 10 Beautiful Invoice Designs',
|
||||
'pro_plan_feature3' => 'Custom URLs - "YourBrand.InvoiceNinja.com"',
|
||||
'pro_plan_feature4' => 'Remove "Created by Invoice Ninja"',
|
||||
'pro_plan_feature5' => 'Multi-user Access & Activity Tracking',
|
||||
'pro_plan_feature6' => 'Create Quotes & Pro-forma Invoices',
|
||||
'pro_plan_feature7' => 'Customize Invoice Field Titles & Numbering',
|
||||
'pro_plan_feature8' => 'Option to Attach PDFs to Client Emails',
|
||||
'pro_plan_call_to_action' => 'Jetzt Upgraden!',
|
||||
'pro_plan_feature1' => 'Unlimitierte Anzahl Kunden erstellen',
|
||||
'pro_plan_feature2' => 'Zugriff ui 10 schönen Rechnungsdesigns',
|
||||
'pro_plan_feature3' => 'Benutzerdefinierte URLs - "DeineFirma.InvoiceNinja.com"',
|
||||
'pro_plan_feature4' => '"Created by Invoice Ninja" entfernen',
|
||||
'pro_plan_feature5' => 'Multi-Benutzer Zugriff & Aktivitätstracking',
|
||||
'pro_plan_feature6' => 'Angebote & pro-forma Rechnungen erstellen',
|
||||
'pro_plan_feature7' => 'Rechungstitelfelder und Nummerierung anpassen',
|
||||
'pro_plan_feature8' => 'PDFs an E-Mails zu Kunden anhängen',
|
||||
|
||||
'resume' => 'Resume',
|
||||
'break_duration' => 'Break',
|
||||
'edit_details' => 'Edit Details',
|
||||
'work' => 'Work',
|
||||
'timezone_unset' => 'Please :link to set your timezone',
|
||||
'click_here' => 'click here',
|
||||
'resume' => 'Fortfahren',
|
||||
'break_duration' => 'Pause',
|
||||
'edit_details' => 'Details bearbeiten',
|
||||
'work' => 'Arbeiten',
|
||||
'timezone_unset' => 'Bitte :link um deine Zeitzone zu setzen',
|
||||
'click_here' => 'hier klicken',
|
||||
|
||||
'email_receipt' => 'Email payment receipt to the client',
|
||||
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
|
||||
'add_account' => 'Add Account',
|
||||
'untitled' => 'Untitled',
|
||||
'new_account' => 'New Account',
|
||||
'associated_accounts' => 'Successfully linked accounts',
|
||||
'unlinked_account' => 'Successfully unlinked accounts',
|
||||
'email_receipt' => 'Zahlungsbestätigung an Kunden per E-Mail senden',
|
||||
'created_payment_emailed_client' => 'Zahlung erfolgreich erstellt und Kunde per E-Mail benachrichtigt',
|
||||
'add_account' => 'Konto hinzufügen',
|
||||
'untitled' => 'Unbenannt',
|
||||
'new_account' => 'Neues Konto',
|
||||
'associated_accounts' => 'Konten erfolgreich verlinkt',
|
||||
'unlinked_account' => 'Konten erfolgreich getrennt',
|
||||
'login' => 'Login',
|
||||
'or' => 'or',
|
||||
'or' => 'oder',
|
||||
|
||||
|
||||
);
|
@ -704,4 +704,12 @@ return array(
|
||||
'login' => 'Login',
|
||||
'or' => 'or',
|
||||
|
||||
'email_error' => 'There was a problem sending the email',
|
||||
'created_by_recurring' => 'Created by recurring invoice :invoice',
|
||||
'confirm_recurring_timing' => 'Note: emails are sent at the start of the hour.',
|
||||
'old_browser' => 'Please use a <a href="'.OUTDATE_BROWSER_URL.'" target="_blank">newer browser</a>',
|
||||
'payment_terms_help' => 'Sets the default invoice due date',
|
||||
'unlink_account' => 'Unlink Account',
|
||||
'unlink' => 'Unlink',
|
||||
|
||||
);
|
||||
|
@ -14,7 +14,7 @@ return array(
|
||||
'state' => 'Région/Département',
|
||||
'postal_code' => 'Code postal',
|
||||
'country_id' => 'Pays',
|
||||
'contacts' => 'Informations de contact', //if you speak about contact details
|
||||
'contacts' => 'Informations de contact',
|
||||
'first_name' => 'Prénom',
|
||||
'last_name' => 'Nom',
|
||||
'phone' => 'Téléphone',
|
||||
@ -23,7 +23,7 @@ return array(
|
||||
'payment_terms' => 'Conditions de paiement',
|
||||
'currency_id' => 'Devise',
|
||||
'size_id' => 'Taille',
|
||||
'industry_id' => 'Secteur', // literal translation : Industrie
|
||||
'industry_id' => 'Secteur',
|
||||
'private_notes' => 'Note personnelle',
|
||||
|
||||
// invoice
|
||||
@ -45,11 +45,11 @@ return array(
|
||||
'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"
|
||||
'paid_to_date' => 'Versé à ce jour',
|
||||
'balance_due' => 'Montant total',
|
||||
'invoice_design_id' => 'Design',
|
||||
'terms' => 'Conditions',
|
||||
'your_invoice' => 'Votre Facture',
|
||||
'your_invoice' => 'Votre facture',
|
||||
|
||||
'remove_contact' => 'Supprimer un contact',
|
||||
'add_contact' => 'Ajouter un contact',
|
||||
@ -211,8 +211,8 @@ return array(
|
||||
|
||||
// application messages
|
||||
'created_client' => 'Client créé avec succès',
|
||||
'created_clients' => ':count clients créés avec csuccès',
|
||||
'updated_settings' => 'paramètres mis à jour avec succès',
|
||||
'created_clients' => ':count clients créés avec succès',
|
||||
'updated_settings' => 'Paramètres mis à jour avec succès',
|
||||
'removed_logo' => 'Logo supprimé avec succès',
|
||||
'sent_message' => 'Message envoyé avec succès',
|
||||
'invoice_error' => 'Veuillez vous assurer de sélectionner un client et de corriger les erreurs',
|
||||
@ -261,7 +261,7 @@ return array(
|
||||
'payment_message' => 'Merci pour votre paiement d\'un montant de :amount',
|
||||
'email_salutation' => 'Cher :name,',
|
||||
'email_signature' => 'Cordialement,',
|
||||
'email_from' => 'L\'équipe InvoiceNinja',
|
||||
'email_from' => 'L\'équipe Invoice Ninja',
|
||||
'user_email_footer' => 'Pour modifier vos paramètres de notification par courriel, veuillez visiter '.SITE_URL.'/company/notifications',
|
||||
'invoice_link_message' => 'Pour voir la facture de votre client cliquez sur le lien ci-après :',
|
||||
'notification_invoice_paid_subject' => 'La facture :invoice a été payée par le client :client',
|
||||
@ -293,7 +293,7 @@ return array(
|
||||
|
||||
// Pro Plan
|
||||
'pro_plan' => [
|
||||
'remove_logo' => ':link pour supprimer le logo Invoice Ninja en souscrivant au plan Pro',
|
||||
'remove_logo' => ':link pour supprimer le logo Invoice Ninja en souscrivant au Plan Pro',
|
||||
'remove_logo_link' => 'Cliquez ici',
|
||||
],
|
||||
|
||||
@ -456,10 +456,10 @@ return array(
|
||||
'sent' => 'envoyé',
|
||||
'timesheets' => 'Feuilles de temps',
|
||||
|
||||
'payment_title' => 'Enter Your Billing Address and Credit Card information',
|
||||
'payment_cvv' => '*This is the 3-4 digit number onthe back of your card',
|
||||
'payment_footer1' => '*Billing address must match address associated with credit card.',
|
||||
'payment_footer2' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.',
|
||||
'payment_title' => 'Entrez votre adresse de facturation et vos informations bancaires',
|
||||
'payment_cvv' => '*Numéro à 3 ou 4 chiffres au dos de votre carte',
|
||||
'payment_footer1' => '*L\'adresse de facturation doit correspondre à celle enregistrée avec votre carte bancaire',
|
||||
'payment_footer2' => '*Merci de cliquer sur "Payer maintenant" une seule fois. Le processus peut prendre jusqu\'à 1 minute.',
|
||||
'vat_number' => 'Numéro de TVA',
|
||||
|
||||
'id_number' => 'Numéro ID',
|
||||
@ -492,18 +492,18 @@ return array(
|
||||
'select_versiony' => 'Choix de la verison',
|
||||
'view_history' => 'Consulter l\'historique',
|
||||
|
||||
'edit_payment' => 'Edit Payment',
|
||||
'updated_payment' => 'Successfully updated payment',
|
||||
'deleted' => 'Deleted',
|
||||
'restore_user' => 'Restore User',
|
||||
'restored_user' => 'Successfully restored user',
|
||||
'show_deleted_users' => 'Show deleted users',
|
||||
'email_templates' => 'Email Templates',
|
||||
'invoice_email' => 'Invoice Email',
|
||||
'payment_email' => 'Payment Email',
|
||||
'quote_email' => 'Quote Email',
|
||||
'reset_all' => 'Reset All',
|
||||
'approve' => 'Approve',
|
||||
'edit_payment' => 'Editer le paiement',
|
||||
'updated_payment' => 'Paiement édité avec succès',
|
||||
'deleted' => 'Supprimé',
|
||||
'restore_user' => 'Restaurer l\'utilisateur',
|
||||
'restored_user' => 'Restaurer la commande',
|
||||
'show_deleted_users' => 'Voir les utilisateurs supprimés',
|
||||
'email_templates' => 'Templates de mail',
|
||||
'invoice_email' => 'Templates de facture',
|
||||
'payment_email' => 'Email de paiement',
|
||||
'quote_email' => 'Email de déclaration',
|
||||
'reset_all' => 'Réinitialiser',
|
||||
'approve' => 'Accepter',
|
||||
|
||||
'token_billing_type_id' => 'Token Billing',
|
||||
'token_billing_help' => 'Enables you to store credit cards with your gateway, and charge them at a later date.',
|
||||
@ -519,18 +519,18 @@ return array(
|
||||
'token_billing_secure' => 'The data is stored securely by :stripe_link',
|
||||
|
||||
'support' => 'Support',
|
||||
'contact_information' => 'Contact information',
|
||||
'contact_information' => 'Information de contact',
|
||||
'256_encryption' => '256-Bit Encryption',
|
||||
'amount_due' => 'Amount due',
|
||||
'amount_due' => 'Montant dû',
|
||||
'billing_address' => 'Billing address',
|
||||
'billing_method' => 'Billing method',
|
||||
'order_overview' => 'Order overview',
|
||||
'match_address' => '*Address must match address associated with credit card.',
|
||||
'click_once' => '*Please click "PAY NOW" only once - transaction may take up to 1 minute to process.',
|
||||
|
||||
'default_invoice_footer' => 'Set default invoice footer',
|
||||
'invoice_footer' => 'Invoice footer',
|
||||
'save_as_default_footer' => 'Save as default footer',
|
||||
'default_invoice_footer' => 'Définir par défaut',
|
||||
'invoice_footer' => 'Pied de facture',
|
||||
'save_as_default_footer' => 'Définir comme pied de facture par défatu',
|
||||
|
||||
'token_management' => 'Token Management',
|
||||
'tokens' => 'Tokens',
|
||||
@ -543,12 +543,12 @@ return array(
|
||||
'delete_token' => 'Delete Token',
|
||||
'token' => 'Token',
|
||||
|
||||
'add_gateway' => 'Add Gateway',
|
||||
'delete_gateway' => 'Delete Gateway',
|
||||
'edit_gateway' => 'Edit Gateway',
|
||||
'updated_gateway' => 'Successfully updated gateway',
|
||||
'created_gateway' => 'Successfully created gateway',
|
||||
'deleted_gateway' => 'Successfully deleted gateway',
|
||||
'add_gateway' => 'Ajouter passerelle',
|
||||
'delete_gateway' => 'Supprimer passerelle',
|
||||
'edit_gateway' => 'Editer passerelle',
|
||||
'updated_gateway' => 'Passerelle mise à jour avec succès',
|
||||
'created_gateway' => 'Passerelle crée avec succès',
|
||||
'deleted_gateway' => 'Passerelle supprimée avec succès',
|
||||
'pay_with_paypal' => 'PayPal',
|
||||
'pay_with_card' => 'Carte bancaire',
|
||||
|
||||
@ -579,44 +579,44 @@ return array(
|
||||
'confirmation_resent' => 'The confirmation email was resent',
|
||||
|
||||
'gateway_help_42' => ':link to sign up for BitPay.<br/>Note: use a Legacy API Key, not an API token.',
|
||||
'payment_type_credit_card' => 'Credit card',
|
||||
'payment_type_credit_card' => 'Carte de crédit',
|
||||
'payment_type_paypal' => 'PayPal',
|
||||
'payment_type_bitcoin' => 'Bitcoin',
|
||||
'knowledge_base' => 'Knowledge Base',
|
||||
'partial' => 'Partial',
|
||||
'partial_remaining' => ':partial of :balance',
|
||||
'knowledge_base' => 'Base de connaissances',
|
||||
'partial' => 'Partiel',
|
||||
'partial_remaining' => ':partial de :balance',
|
||||
|
||||
'more_fields' => 'More Fields',
|
||||
'less_fields' => 'Less Fields',
|
||||
'client_name' => 'Client Name',
|
||||
'pdf_settings' => 'PDF Settings',
|
||||
'more_fields' => 'Plus de champs',
|
||||
'less_fields' => 'Moins de champs',
|
||||
'client_name' => 'Nom du client',
|
||||
'pdf_settings' => 'Réglages PDF',
|
||||
'utf8_invoices' => 'Cyrillic Support <sup>Beta</sup>',
|
||||
'product_settings' => 'Product Settings',
|
||||
'product_settings' => 'Réglages du produit',
|
||||
'auto_wrap' => 'Auto Line Wrap',
|
||||
'duplicate_post' => 'Warning: the previous page was submitted twice. The second submission had been ignored.',
|
||||
'view_documentation' => 'View Documentation',
|
||||
'view_documentation' => 'Voir documentation',
|
||||
'app_title' => 'Free Open-Source Online Invoicing',
|
||||
'app_description' => 'Invoice Ninja is a free, open-source solution for invoicing and billing customers. With Invoice Ninja, you can easily build and send beautiful invoices from any device that has access to the web. Your clients can print your invoices, download them as pdf files, and even pay you online from within the system.',
|
||||
|
||||
'rows' => 'rows',
|
||||
'rows' => 'lignes',
|
||||
'www' => 'www',
|
||||
'logo' => 'Logo',
|
||||
'subdomain' => 'Subdomain',
|
||||
'provide_name_or_email' => 'Please provide a contact name or email',
|
||||
'subdomain' => 'Sous domaine',
|
||||
'provide_name_or_email' => 'Merci d\'indiquer un nom ou une adresse email',
|
||||
'charts_and_reports' => 'Charts & Reports',
|
||||
'chart' => 'Chart',
|
||||
'report' => 'Report',
|
||||
'group_by' => 'Group by',
|
||||
'paid' => 'Paid',
|
||||
'group_by' => 'Grouper par',
|
||||
'paid' => 'Payé',
|
||||
'enable_report' => 'Report',
|
||||
'enable_chart' => 'Chart',
|
||||
'totals' => 'Totals',
|
||||
'run' => 'Run',
|
||||
'export' => 'Export',
|
||||
'export' => 'Exporter',
|
||||
'documentation' => 'Documentation',
|
||||
'zapier' => 'Zapier <sup>Beta</sup>',
|
||||
'recurring' => 'Recurring',
|
||||
'last_invoice_sent' => 'Last invoice sent :date',
|
||||
'recurring' => 'Récurrent',
|
||||
'last_invoice_sent' => 'Dernière facture envoyée le :date',
|
||||
|
||||
'processed_updates' => 'Mise à jour effectuée avec succès',
|
||||
'tasks' => 'Tâches',
|
||||
@ -641,7 +641,7 @@ return array(
|
||||
'minute' => 'minute',
|
||||
'minutes' => 'minutes',
|
||||
'hour' => 'heure',
|
||||
'hours' => 'houres',
|
||||
'hours' => 'heures',
|
||||
'task_details' => 'Détails tâche',
|
||||
'duration' => 'Durée',
|
||||
'end_time' => 'Heure de fin',
|
||||
@ -655,8 +655,8 @@ return array(
|
||||
'restored_task' => 'Tâche restaurée avec succès',
|
||||
'archived_task' => 'Tâche archivée avec succès',
|
||||
'archived_tasks' => ':count tâches archivées avec succès',
|
||||
'deleted_task' => 'Tâche supprimée avec succès,'
|
||||
'deleted_tasks' => ':count tâches supprimées avec syccès',
|
||||
'deleted_task' => 'Tâche supprimée avec succès',
|
||||
'deleted_tasks' => ':count tâches supprimées avec succès',
|
||||
'create_task' => 'Créer tâche',
|
||||
'stopped_task' => 'Tâche stoppée avec succès',
|
||||
'invoice_task' => 'Tâche facturation',
|
||||
@ -682,20 +682,20 @@ return array(
|
||||
|
||||
'resume' => 'Resume',
|
||||
'break_duration' => 'Break',
|
||||
'edit_details' => 'Edit Details',
|
||||
'work' => 'Work',
|
||||
'edit_details' => 'Editer détails',
|
||||
'work' => 'Travail',
|
||||
'timezone_unset' => 'Please :link to set your timezone',
|
||||
'click_here' => 'click here',
|
||||
'click_here' => 'cliquer ici',
|
||||
|
||||
'email_receipt' => 'Email payment receipt to the client',
|
||||
'created_payment_emailed_client' => 'Successfully created payment and emailed client',
|
||||
'add_account' => 'Add Account',
|
||||
'untitled' => 'Untitled',
|
||||
'new_account' => 'New Account',
|
||||
'created_payment_emailed_client' => 'Paiement crée avec succès et envoyé au client',
|
||||
'add_account' => 'Ajouter compte',
|
||||
'untitled' => 'Sans titre',
|
||||
'new_account' => 'Nouveau compte',
|
||||
'associated_accounts' => 'Successfully linked accounts',
|
||||
'unlinked_account' => 'Successfully unlinked accounts',
|
||||
'login' => 'Login',
|
||||
'or' => 'or',
|
||||
'login' => 'Connexion',
|
||||
'or' => 'ou',
|
||||
|
||||
|
||||
|
||||
|
@ -79,11 +79,11 @@
|
||||
{!! Former::hidden('remember')->raw() !!}
|
||||
</p>
|
||||
|
||||
<p>{!! Button::success(trans(Utils::allowNewAccounts() ? 'texts.login' : 'texts.lets_go'))->large()->submit()->block() !!}</p>
|
||||
<p>{!! Button::success(trans(Input::get('new_account') && Utils::allowNewAccounts() ? 'texts.login' : 'texts.lets_go'))->large()->submit()->block() !!}</p>
|
||||
|
||||
@if (Utils::allowNewAccounts())
|
||||
@if (Input::get('new_account') && Utils::allowNewAccounts())
|
||||
<center><p>- {{ trans('texts.or') }} -</p></center>
|
||||
<p>{!! Button::primary(trans('texts.new_account'))->asLinkTo(URL::to('/invoice_now?logout=true'))->large()->submit()->block() !!}</p>
|
||||
<p>{!! Button::primary(trans('texts.new_account'))->asLinkTo(URL::to('/invoice_now?new_account=true'))->large()->submit()->block() !!}</p>
|
||||
@endif
|
||||
|
||||
|
||||
@ -110,12 +110,9 @@
|
||||
@endif
|
||||
|
||||
@if (Session::has('error'))
|
||||
<div class="alert alert-danger">{{ Session::get('error') }}</div>
|
||||
<div class="alert alert-danger"><li>{{ Session::get('error') }}</li></div>
|
||||
@endif
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
@ -8,7 +8,11 @@
|
||||
@section('content')
|
||||
<div class="row">
|
||||
|
||||
{!! Former::open($url)->addClass('col-md-12 warn-on-exit')->method($method) !!}
|
||||
{!! Former::open($url)
|
||||
->rules(
|
||||
['email' => 'email']
|
||||
)->addClass('col-md-12 warn-on-exit')
|
||||
->method($method) !!}
|
||||
|
||||
@if ($client)
|
||||
{!! Former::populate($client) !!}
|
||||
@ -100,7 +104,8 @@
|
||||
{!! Former::select('currency_id')->addOption('','')
|
||||
->fromQuery($currencies, 'name', 'id') !!}
|
||||
{!! Former::select('payment_terms')->addOption('','')
|
||||
->fromQuery($paymentTerms, 'name', 'num_days') !!}
|
||||
->fromQuery($paymentTerms, 'name', 'num_days')
|
||||
->help(trans('texts.payment_terms_help')) !!}
|
||||
{!! Former::select('size_id')->addOption('','')
|
||||
->fromQuery($sizes, 'name', 'id') !!}
|
||||
{!! Former::select('industry_id')->addOption('','')
|
||||
|
@ -112,7 +112,7 @@
|
||||
<i class="fa fa-envelope" style="width: 20px"></i>{!! HTML::mailto($contact->email, $contact->email) !!}<br/>
|
||||
@endif
|
||||
@if ($contact->phone)
|
||||
<i class="fa fa-phone" style="width: 20px"></i>{!! Utils::formatPhoneNumber($contact->phone) !!}
|
||||
<i class="fa fa-phone" style="width: 20px"></i>{!! Utils::formatPhoneNumber($contact->phone) !!}<br/>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@ -305,4 +305,4 @@
|
||||
|
||||
</script>
|
||||
|
||||
@stop
|
||||
@stop
|
||||
|
@ -186,13 +186,19 @@
|
||||
});
|
||||
}
|
||||
|
||||
function unlinkAccount(userAccountId, userId) {
|
||||
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
|
||||
window.location = '{{ URL::to('/unlink_account') }}' + '/' + userAccountId + '/' + userId;
|
||||
}
|
||||
function showUnlink(userAccountId, userId) {
|
||||
NINJA.unlink = {
|
||||
'userAccountId': userAccountId,
|
||||
'userId': userId
|
||||
};
|
||||
$('#unlinkModal').modal('show');
|
||||
return false;
|
||||
}
|
||||
|
||||
function unlinkAccount() {
|
||||
window.location = '{{ URL::to('/unlink_account') }}' + '/' + NINJA.unlink.userAccountId + '/' + NINJA.unlink.userId;
|
||||
}
|
||||
|
||||
function wordWrapText(value, width)
|
||||
{
|
||||
@if (Auth::user()->account->auto_wrap)
|
||||
@ -242,7 +248,14 @@
|
||||
$(".alert-hide").fadeOut(500);
|
||||
}, 2000);
|
||||
|
||||
$('#search').blur(function(){
|
||||
$('#search').css('width', '150px');
|
||||
$('ul.navbar-right').show();
|
||||
});
|
||||
|
||||
$('#search').focus(function(){
|
||||
$('#search').css('width', '256px');
|
||||
$('ul.navbar-right').hide();
|
||||
if (!window.hasOwnProperty('searchData')) {
|
||||
$.get('{{ URL::route('getSearchData') }}', function(data) {
|
||||
window.searchData = true;
|
||||
@ -316,7 +329,7 @@
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a href="{{ URL::to(NINJA_WEB_URL) }}" class='navbar-brand'>
|
||||
<a href="{{ URL::to(NINJA_WEB_URL) }}" class='navbar-brand' target="_blank">
|
||||
<img src="{{ asset('images/invoiceninja-logo.png') }}" style="height:18px;width:auto"/>
|
||||
</a>
|
||||
</div>
|
||||
@ -353,29 +366,42 @@
|
||||
<ul class="dropdown-menu user-accounts" role="menu">
|
||||
@if (session(SESSION_USER_ACCOUNTS))
|
||||
@foreach (session(SESSION_USER_ACCOUNTS) as $item)
|
||||
<li><a href='{{ URL::to("/switch_account/{$item->user_id}") }}'>
|
||||
@if ($item->user_id == Auth::user()->id)
|
||||
<b>
|
||||
@endif
|
||||
@if (count(session(SESSION_USER_ACCOUNTS)) > 1)
|
||||
<div class="pull-right glyphicon glyphicon-remove remove" onclick="return unlinkAccount({{ $item->id }}, {{ $item->user_id }})"></div>
|
||||
@endif
|
||||
<div class="account" style="padding-right:28px">{{ $item->account_name }}</div>
|
||||
<div class="user">{{ $item->user_name }}</div>
|
||||
@if ($item->user_id == Auth::user()->id)
|
||||
</b>
|
||||
@endif
|
||||
</a></li>
|
||||
@if ($item->user_id == Auth::user()->id)
|
||||
@include('user_account', [
|
||||
'user_account_id' => $item->id,
|
||||
'user_id' => $item->user_id,
|
||||
'account_name' => $item->account_name,
|
||||
'user_name' => $item->user_name,
|
||||
'account_key' => $item->account_key,
|
||||
'selected' => true,
|
||||
'show_remove' => count(session(SESSION_USER_ACCOUNTS)) > 1,
|
||||
])
|
||||
@endif
|
||||
@endforeach
|
||||
@foreach (session(SESSION_USER_ACCOUNTS) as $item)
|
||||
@if ($item->user_id != Auth::user()->id)
|
||||
@include('user_account', [
|
||||
'user_account_id' => $item->id,
|
||||
'user_id' => $item->user_id,
|
||||
'account_name' => $item->account_name,
|
||||
'user_name' => $item->user_name,
|
||||
'account_key' => $item->account_key,
|
||||
'selected' => false,
|
||||
'show_remove' => count(session(SESSION_USER_ACCOUNTS)) > 1,
|
||||
])
|
||||
@endif
|
||||
@endforeach
|
||||
@else
|
||||
<li><a href='#'><b>
|
||||
<div class="account">{{ Auth::user()->account->name ?: trans('texts.untitled') }}</div>
|
||||
<div class="user">{{ Auth::user()->getDisplayName() }}</div>
|
||||
</b></a></li>
|
||||
@include('user_account', [
|
||||
'account_name' => Auth::user()->account->name ?: trans('texts.untitled'),
|
||||
'user_name' => Auth::user()->getDisplayName(),
|
||||
'account_key' => Auth::user()->account->account_key,
|
||||
'selected' => true,
|
||||
])
|
||||
@endif
|
||||
<li class="divider"></li>
|
||||
@if (Auth::user()->isPro() && (!session(SESSION_USER_ACCOUNTS) || count(session(SESSION_USER_ACCOUNTS)) < 5))
|
||||
<li>{!! link_to('/login', trans('texts.add_account')) !!}</li>
|
||||
@if (!session(SESSION_USER_ACCOUNTS) || count(session(SESSION_USER_ACCOUNTS)) < 5)
|
||||
<li>{!! link_to('/login?new_account=true', trans('texts.add_account')) !!}</li>
|
||||
@endif
|
||||
<li>{!! link_to('#', trans('texts.logout'), array('onclick'=>'logout()')) !!}</li>
|
||||
</ul>
|
||||
@ -419,7 +445,7 @@
|
||||
|
||||
<form class="navbar-form navbar-right" role="search">
|
||||
<div class="form-group">
|
||||
<input type="text" id="search" style="width: {{ Session::get(SESSION_LOCALE) == 'en' ? 180 : 140 }}px"
|
||||
<input type="text" id="search" style="width: 150px"
|
||||
class="form-control" placeholder="{{ trans('texts.search') }}">
|
||||
</div>
|
||||
</form>
|
||||
@ -454,7 +480,7 @@
|
||||
@endif
|
||||
|
||||
@if (Session::has('error'))
|
||||
<div class="alert alert-danger">{{ Session::get('error') }}</div>
|
||||
<div class="alert alert-danger">{!! Session::get('error') !!}</div>
|
||||
@endif
|
||||
|
||||
@if (!isset($showBreadcrumbs) || $showBreadcrumbs)
|
||||
@ -549,6 +575,28 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (Auth::check() && session(SESSION_USER_ACCOUNTS) && count(session(SESSION_USER_ACCOUNTS)))
|
||||
<div class="modal fade" id="unlinkModal" tabindex="-1" role="dialog" aria-labelledby="unlinkModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="myModalLabel">{{ trans('texts.unlink_account') }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<h3>{{ trans('texts.are_you_sure') }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer" id="signUpFooter">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{ trans('texts.cancel') }}</button>
|
||||
<button type="button" class="btn btn-primary" onclick="unlinkAccount()">{{ trans('texts.unlink') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (Auth::check() && !Auth::user()->isPro())
|
||||
<div class="modal fade" id="proPlanModal" tabindex="-1" role="dialog" aria-labelledby="proPlanModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog large-dialog">
|
||||
@ -588,7 +636,7 @@
|
||||
|
||||
@endif
|
||||
|
||||
{{-- Per our license, please do not remove or modify this link. --}}
|
||||
{{-- Per our license, please do not remove or modify this section. --}}
|
||||
@if (!Utils::isNinja())
|
||||
<div class="container">
|
||||
{{ trans('texts.powered_by') }} <a href="https://www.invoiceninja.com/?utm_source=powered_by" target="_blank">InvoiceNinja.com</a> |
|
||||
|
@ -96,7 +96,7 @@
|
||||
</div>
|
||||
@if ($invoice && $invoice->recurring_invoice_id)
|
||||
<div class="pull-right" style="padding-top: 6px">
|
||||
Created by a {!! link_to('/invoices/'.$invoice->recurring_invoice_id, 'recurring invoice') !!}
|
||||
{!! trans('texts.created_by_recurring', ['invoice' => link_to('/invoices/'.$invoice->recurring_invoice->public_id, $invoice->recurring_invoice->invoice_number)]) !!}
|
||||
</div>
|
||||
@else
|
||||
<div data-bind="visible: invoice_status_id() === 0">
|
||||
@ -438,7 +438,8 @@
|
||||
|
||||
<span data-bind="visible: $root.showMore">
|
||||
{!! Former::select('payment_terms')->addOption('','')->data_bind('value: payment_terms')
|
||||
->fromQuery($paymentTerms, 'name', 'num_days') !!}
|
||||
->fromQuery($paymentTerms, 'name', 'num_days')
|
||||
->help(trans('texts.payment_terms_help')) !!}
|
||||
{!! Former::select('size_id')->addOption('','')->data_bind('value: size_id')
|
||||
->fromQuery($sizes, 'name', 'id') !!}
|
||||
{!! Former::select('industry_id')->addOption('','')->data_bind('value: industry_id')
|
||||
@ -738,14 +739,24 @@
|
||||
}
|
||||
|
||||
function onEmailClick() {
|
||||
if (confirm('{!! trans("texts.confirm_email_$entityType") !!}')) {
|
||||
if (!NINJA.isRegistered) {
|
||||
alert("{{ trans('texts.registration_required') }}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm('{!! trans("texts.confirm_email_$entityType") !!}' + '\n\n' + getSendToEmails())) {
|
||||
preparePdfData('email');
|
||||
}
|
||||
}
|
||||
|
||||
function onSaveClick() {
|
||||
if (model.invoice().is_recurring()) {
|
||||
if (confirm('{!! trans("texts.confirm_recurring_email_$entityType") !!}')) {
|
||||
if (!NINJA.isRegistered) {
|
||||
alert("{{ trans('texts.registration_required') }}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (confirm('{!! trans("texts.confirm_recurring_email_$entityType") !!}' + '\n\n' + getSendToEmails() + '\n' + '{!! trans("texts.confirm_recurring_timing") !!}')) {
|
||||
submitAction('');
|
||||
}
|
||||
} else {
|
||||
@ -753,6 +764,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getSendToEmails() {
|
||||
var client = model.invoice().client();
|
||||
var parts = [];
|
||||
|
||||
for (var i=0; i<client.contacts().length; i++) {
|
||||
var contact = client.contacts()[i];
|
||||
if (contact.send_invoice()) {
|
||||
parts.push(contact.displayName());
|
||||
}
|
||||
}
|
||||
|
||||
return parts.join('\n');
|
||||
}
|
||||
|
||||
function preparePdfData(action) {
|
||||
var invoice = createInvoiceModel();
|
||||
var design = getDesignJavascript();
|
||||
@ -1430,6 +1455,22 @@
|
||||
self.send_invoice = ko.observable(false);
|
||||
self.invitation_link = ko.observable('');
|
||||
|
||||
if (data) {
|
||||
ko.mapping.fromJS(data, {}, this);
|
||||
}
|
||||
|
||||
self.displayName = ko.computed(function() {
|
||||
var str = '';
|
||||
if (self.first_name() || self.last_name()) {
|
||||
str += self.first_name() + ' ' + self.last_name() + '\n';
|
||||
}
|
||||
if (self.email()) {
|
||||
str += self.email() + '\n';
|
||||
}
|
||||
|
||||
return str;
|
||||
});
|
||||
|
||||
self.email.display = ko.computed(function() {
|
||||
var str = '';
|
||||
if (self.first_name() || self.last_name()) {
|
||||
@ -1447,10 +1488,6 @@
|
||||
|
||||
return str;
|
||||
});
|
||||
|
||||
if (data) {
|
||||
ko.mapping.fromJS(data, {}, this);
|
||||
}
|
||||
}
|
||||
|
||||
function TaxRateModel(data) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
<iframe id="theFrame" style="display:none" frameborder="1" width="100%" height="{{ isset($pdfHeight) ? $pdfHeight : 1180 }}px"></iframe>
|
||||
<canvas id="theCanvas" style="display:none;width:100%;border:solid 1px #CCCCCC;"></canvas>
|
||||
|
||||
@if (!Utils::isNinja() || !Utils::isPro())
|
||||
<div class="modal fade" id="moreDesignsModal" tabindex="-1" role="dialog" aria-labelledby="moreDesignsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
@ -43,7 +44,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
@ -36,7 +36,7 @@
|
||||
@if (count($paymentTypes) > 1)
|
||||
{!! DropdownButton::success(trans('texts.pay_now'))->withContents($paymentTypes)->large() !!}
|
||||
@else
|
||||
{!! Button::success(trans('texts.pay_now'))->asLinkTo(URL::to('/payment/' . $invitation->invitation_key))->large() !!}
|
||||
{!! Button::success(trans('texts.pay_now'))->asLinkTo(URL::to($paymentURL))->large() !!}
|
||||
@endif
|
||||
@else
|
||||
{!! Button::normal('Download PDF')->withAttributes(['onclick' => 'onDownloadClick()'])->large() !!}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{App::getLocale()}}">
|
||||
<head>
|
||||
<title>Invoice Ninja | {{ isset($title) ? $title : ' ' . trans('texts.app_title') }}</title>
|
||||
<title>{{ isset($title) ? ($title . ' | Invoice Ninja') : ('Invoice Ninja | ' . trans('texts.app_title')) }} | </title>
|
||||
<meta name="description" content="{{ isset($description) ? $description : trans('texts.app_description') }}" />
|
||||
|
||||
<!-- Source: https://github.com/hillelcoren/invoice-ninja -->
|
||||
|
@ -181,7 +181,7 @@ table.table thead .sorting_desc_disabled:after { content: '' !important }
|
||||
</button>
|
||||
@if (!isset($hideLogo) || !$hideLogo)
|
||||
{{-- Per our license, please do not remove or modify this link. --}}
|
||||
<a class="navbar-brand" href="https://www.invoiceninja.com/"><img src="{{ asset('images/invoiceninja-logo.png') }}"></a>
|
||||
<a class="navbar-brand" href="{{ URL::to(NINJA_WEB_URL) }}" target="_blank"><img src="{{ asset('images/invoiceninja-logo.png') }}"></a>
|
||||
@endif
|
||||
</div>
|
||||
<div id="navbar" class="collapse navbar-collapse">
|
||||
|
@ -77,7 +77,11 @@ header h3 em {
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<header>
|
||||
<h2>License Key<br/><small>{{ $message }}</small></h2>
|
||||
@if (isset($redirectTo))
|
||||
<h2>Payment Complete</h2>
|
||||
@else
|
||||
<h2>License Key<br/><small>{{ $message }}</small></h2>
|
||||
@endif
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
@ -87,9 +91,16 @@ header h3 em {
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2 style="text-align:center">{{ $license }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<h2 style="text-align:center">
|
||||
@if (isset($redirectTo))
|
||||
{{ $message }}
|
||||
@else
|
||||
{{ $license }}
|
||||
@endif
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -102,6 +113,13 @@ header h3 em {
|
||||
|
||||
$(function() {
|
||||
trackEvent('/license', '/product_{{ $productId }}');
|
||||
|
||||
@if (isset($redirectTo))
|
||||
setTimeout(function() {
|
||||
location.href = "{!! $redirectTo !!}";
|
||||
}, 3000);
|
||||
@endif
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
@ -6,10 +6,11 @@
|
||||
<meta name="csrf-token" content="<?= csrf_token() ?>">
|
||||
<script src="{{ asset('js/built.js') }}?no_cache={{ NINJA_VERSION }}" type="text/javascript"></script>
|
||||
<link href="{{ asset('css/built.public.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
||||
<link href="{{ asset('css/built.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
background-color: #f8f8f8;
|
||||
background-color: #FEFEFE;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -35,7 +36,7 @@
|
||||
<pre>sudo chown yourname:www-data /path/to/ninja</pre>
|
||||
</div>
|
||||
@endif
|
||||
If you need help you can either post to our <a href="https://groups.google.com/forum/#!forum/invoiceninja" target="_blank">Google Group</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>
|
||||
or email us at <a href="mailto:contact@invoiceninja.com" target="_blank">contact@invoiceninja.com</a>.
|
||||
<p>
|
||||
<pre>-- Commands to create a MySQL database and user
|
||||
@ -64,7 +65,7 @@ FLUSH PRIVILEGES;</pre>
|
||||
<h3 class="panel-title">Application Settings</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{!! Former::text('app[url]')->label('URL')->value(Request::root()) !!}
|
||||
{!! Former::text('app[url]')->label('URL')->value(isset($_ENV['APP_URL']) ? $_ENV['APP_URL'] : Request::root()) !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -73,12 +74,17 @@ FLUSH PRIVILEGES;</pre>
|
||||
<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']) !!}
|
||||
{!! Former::text('database[type][host]')->label('Host')->value('localhost') !!}
|
||||
{!! Former::text('database[type][database]')->label('Database')->value('ninja') !!}
|
||||
{!! Former::text('database[type][username]')->label('Username')->value('ninja') !!}
|
||||
{!! Former::password('database[type][password]')->label('Password')->value('ninja') !!}
|
||||
{!! Former::actions( Button::normal('Test connection')->withAttributes(['onclick' => 'testDatabase()']), ' <span id="dbTestResult"/>' ) !!}
|
||||
{!! 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()']), ' <span id="dbTestResult"/>' ) !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -88,14 +94,21 @@ FLUSH PRIVILEGES;</pre>
|
||||
<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']) !!}
|
||||
{!! Former::text('mail[host]')->label('Host')->value('localhost') !!}
|
||||
{!! Former::text('mail[port]')->label('Port')->value('587') !!}
|
||||
{!! Former::select('mail[encryption]')->label('Encryption')->options(['tls' => 'TLS', 'ssl' => 'SSL']) !!}
|
||||
{!! Former::text('mail[from][name]')->label('From Name') !!}
|
||||
{!! Former::text('mail[username]')->label('Email') !!}
|
||||
{!! Former::password('mail[password]')->label('Password') !!}
|
||||
{!! Former::actions( Button::normal('Send test email')->withAttributes(['onclick' => 'testMail()']), ' <span id="mailTestResult"/>' ) !!}
|
||||
{!! 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()']), ' <span id="mailTestResult"/>' ) !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -112,8 +125,9 @@ FLUSH PRIVILEGES;</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{!! Former::checkbox('terms_checkbox')->label(' ')->text(trans('texts.agree_to_terms', ['terms' => '<a href="'.NINJA_APP_URL.'/terms" target="_blank">'.trans('texts.terms_of_service').'</a>'])) !!}
|
||||
{!! Former::actions( Button::primary('Submit')->submit() ) !!}
|
||||
{!! Former::actions( Button::primary('Submit')->large()->submit() ) !!}
|
||||
{!! Former::close() !!}
|
||||
|
||||
</div>
|
||||
|
31
resources/views/user_account.blade.php
Normal file
31
resources/views/user_account.blade.php
Normal file
@ -0,0 +1,31 @@
|
||||
<li style="margin-top: 4px; margin-bottom: 4px; min-width: 220px; cursor: pointer">
|
||||
@if (isset($user_id) && $show_remove)
|
||||
<a href='{{ URL::to("/switch_account/{$user_id}") }}'>
|
||||
@else
|
||||
<a href='#' onclick="return false;">
|
||||
@endif
|
||||
|
||||
@if (isset($show_remove) && $show_remove)
|
||||
<div class="pull-right glyphicon glyphicon-remove remove" onclick="return showUnlink({{ $user_account_id }}, {{ $user_id }})"></div>
|
||||
@endif
|
||||
|
||||
@if (file_exists('logo/'.$account_key.'.jpg'))
|
||||
<img class="pull-left" style="width: 40px; min-height: 40px; margin-right: 16px" src="{{ asset('logo/'.$account_key.'.jpg') }}"/>
|
||||
@else
|
||||
<div class="pull-left" style="width: 40px; min-height: 40px; margin-right: 16px"> </div>
|
||||
@endif
|
||||
|
||||
@if (isset($selected) && $selected)
|
||||
<b>
|
||||
@endif
|
||||
|
||||
<div class="account" style="padding-right:90px">{{ $account_name }}</div>
|
||||
<div class="user" style="padding-right:90px">{{ $user_name }}</div>
|
||||
|
||||
@if (isset($selected) && $selected)
|
||||
</b>
|
||||
@endif
|
||||
|
||||
</a>
|
||||
|
||||
</li>
|
Loading…
Reference in New Issue
Block a user