1
0
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:
sigitas 2015-06-17 15:29:38 +03:00
commit 1556951832
34 changed files with 647 additions and 134 deletions

View File

@ -19,3 +19,5 @@ MAIL_USERNAME
MAIL_FROM_ADDRESS
MAIL_FROM_NAME
MAIL_PASSWORD
ALLOW_NEW_ACCOUNTS

View File

@ -1,4 +1,5 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]
RewriteRule "^.env" - [F,L]
RewriteRule "^storage" - [F,L]
</IfModule>

View File

@ -1,8 +1,10 @@
<?php namespace App\Exceptions;
use Redirect;
use Utils;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Database\Eloquent\ModelNotFoundException;
class Handler extends ExceptionHandler {
@ -39,7 +41,12 @@ class Handler extends ExceptionHandler {
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
{
if ($e instanceof ModelNotFoundException) {
return Redirect::to('/');
}
if (Utils::isNinjaProd()) {
$data = [
'error' => get_class($e),

View File

@ -77,10 +77,12 @@ class AccountController extends BaseController
{
if (Auth::check()) {
return Redirect::to('invoices/create');
} elseif (!Utils::isNinja() && Account::count() > 0) {
return Redirect::to('/login');
}
if (!Utils::isNinja() && !Utils::allowNewAccounts() && Account::count() > 0) {
return Redirect::to('/login');
}
$user = false;
$guestKey = Input::get('guest_key');
@ -728,7 +730,7 @@ class AccountController extends BaseController
$email = trim(Input::get('email'));
if (!$email || $email == 'user@example.com') {
return RESULT_SUCCESS;
return '';
}
$license = new License();
@ -742,7 +744,7 @@ class AccountController extends BaseController
$license->is_claimed = 1;
$license->save();
return RESULT_SUCCESS;
return '';
}
public function cancelAccount()
@ -762,6 +764,7 @@ class AccountController extends BaseController
$account->forceDelete();
Auth::logout();
Session::flush();
return Redirect::to('/')->with('clearGuestKey', true);
}

View File

@ -3,10 +3,12 @@
use Auth;
use Event;
use Utils;
use Session;
use Illuminate\Http\Request;
use App\Models\User;
use App\Events\UserLoggedIn;
use App\Http\Controllers\Controller;
use App\Ninja\Repositories\AccountRepository;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\Registrar;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
@ -28,6 +30,7 @@ class AuthController extends Controller {
protected $loginPath = '/login';
protected $redirectTo = '/dashboard';
protected $accountRepo;
/**
* Create a new authentication controller instance.
@ -36,12 +39,13 @@ class AuthController extends Controller {
* @param \Illuminate\Contracts\Auth\Registrar $registrar
* @return void
*/
public function __construct(Guard $auth, Registrar $registrar)
public function __construct(Guard $auth, Registrar $registrar, AccountRepository $repo)
{
$this->auth = $auth;
$this->registrar = $registrar;
$this->accountRepo = $repo;
$this->middleware('guest', ['except' => 'getLogout']);
//$this->middleware('guest', ['except' => 'getLogout']);
}
public function getLoginWrapper()
@ -55,13 +59,36 @@ class AuthController extends Controller {
public function postLoginWrapper(Request $request)
{
$userId = Auth::check() ? Auth::user()->id : null;
$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);
}
}
return $response;
}
public function getLogoutWrapper()
{
$response = self::getLogout();
Session::flush();
return $response;
}
}

View File

@ -45,6 +45,11 @@ class HomeController extends BaseController
public function invoiceNow()
{
if (Auth::check() && Input::get('logout')) {
Auth::user()->clearSession();
Auth::logout();
}
if (Auth::check()) {
return Redirect::to('invoices/create')->with('sign_up', Input::get('sign_up'));
} else {

View File

@ -630,8 +630,9 @@ class PaymentController extends BaseController
$invoice = $invitation->invoice;
$accountGateway = $invoice->client->account->getGatewayByType(Session::get('payment_type'));
if ($invoice->account->account_key == NINJA_ACCOUNT_KEY) {
$account = Account::find($invoice->client->public_id);
if ($invoice->account->account_key == NINJA_ACCOUNT_KEY
&& $invoice->amount == PRO_PLAN_PRICE) {
$account = Account::with('users')->find($invoice->client->public_id);
if ($account->pro_plan_paid && $account->pro_plan_paid != '0000-00-00') {
$date = DateTime::createFromFormat('Y-m-d', $account->pro_plan_paid);
$account->pro_plan_paid = $date->modify('+1 year')->format('Y-m-d');
@ -639,6 +640,9 @@ class PaymentController extends BaseController
$account->pro_plan_paid = date_create()->format('Y-m-d');
}
$account->save();
$user = $account->users()->first();
$this->accountRepo->syncAccounts($user->id, $account->pro_plan_paid);
}
$payment = Payment::createNew($invitation);

View File

@ -7,6 +7,7 @@ use DB;
use Event;
use Input;
use View;
use Request;
use Redirect;
use Session;
use URL;
@ -309,10 +310,8 @@ class UserController extends BaseController
}
}
Session::forget('news_feed_id');
Session::forget('news_feed_message');
Auth::logout();
Session::flush();
return Redirect::to('/')->with('clearGuestKey', true);
}
@ -342,4 +341,32 @@ class UserController extends BaseController
return RESULT_SUCCESS;
}
public function switchAccount($newUserId)
{
$oldUserId = Auth::user()->id;
$referer = Request::header('referer');
$account = $this->accountRepo->findUserAccounts($newUserId, $oldUserId);
if ($account) {
if ($account->hasUserId($newUserId) && $account->hasUserId($oldUserId)) {
Auth::loginUsingId($newUserId);
Auth::user()->account->loadLocalizationSettings();
}
}
return Redirect::to($referer);
}
public function unlinkAccount($userAccountId, $userId)
{
$this->accountRepo->unlinkAccount($userAccountId, $userId);
$referer = Request::header('referer');
$users = $this->accountRepo->loadAccounts(Auth::user()->id);
Session::put(SESSION_USER_ACCOUNTS, $users);
Session::flash('message', trans('texts.unlinked_account'));
return Redirect::to($referer);
}
}

View File

@ -67,8 +67,7 @@ get('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@getRegiste
post('/signup', array('as' => 'signup', 'uses' => 'Auth\AuthController@postRegister'));
get('/login', array('as' => 'login', 'uses' => 'Auth\AuthController@getLoginWrapper'));
post('/login', array('as' => 'login', 'uses' => 'Auth\AuthController@postLoginWrapper'));
//get('/logout', array('as' => 'logout', 'uses' => 'Auth\AuthController@getLogoutWrapper'));
get('/logout', array('as' => 'logout', 'uses' => 'Auth\AuthController@getLogout'));
get('/logout', array('as' => 'logout', 'uses' => 'Auth\AuthController@getLogoutWrapper'));
get('/forgot', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@getEmail'));
post('/forgot', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@postEmail'));
get('/password/reset/{token}', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@getReset'));
@ -95,6 +94,7 @@ Route::group(['middleware' => 'auth'], function() {
Route::get('restore_user/{user_id}', 'UserController@restoreUser');
Route::post('users/change_password', 'UserController@changePassword');
Route::get('/switch_account/{user_id}', 'UserController@switchAccount');
Route::get('/unlink_account/{user_account_id}/{user_id}', 'UserController@unlinkAccount');
Route::get('api/tokens', array('as'=>'api.tokens', 'uses'=>'TokenController@getDatatable'));
Route::resource('tokens', 'TokenController');

View File

@ -51,12 +51,17 @@ class Utils
public static function isNinjaProd()
{
return isset($_ENV['NINJA_PROD']) && $_ENV['NINJA_PROD'];
return isset($_ENV['NINJA_PROD']) && $_ENV['NINJA_PROD'] == 'true';
}
public static function isNinjaDev()
{
return isset($_ENV['NINJA_DEV']) && $_ENV['NINJA_DEV'];
return isset($_ENV['NINJA_DEV']) && $_ENV['NINJA_DEV'] == 'true';
}
public static function allowNewAccounts()
{
return isset($_ENV['ALLOW_NEW_ACCOUNTS']) && $_ENV['ALLOW_NEW_ACCOUNTS'] == 'true';
}
public static function isPro()

View File

@ -1,9 +1,9 @@
<?php namespace App\Listeners;
use Auth;
use Session;
use App\Events\UserSettingsChanged;
use App\Ninja\Repositories\AccountRepository;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldBeQueued;
@ -14,9 +14,9 @@ class HandleUserSettingsChanged {
*
* @return void
*/
public function __construct()
public function __construct(AccountRepository $accountRepo)
{
//
$this->accountRepo = $accountRepo;
}
/**
@ -29,6 +29,9 @@ class HandleUserSettingsChanged {
{
$account = Auth::user()->account;
$account->loadLocalizationSettings();
$users = $this->accountRepo->loadAccounts(Auth::user()->id);
Session::put(SESSION_USER_ACCOUNTS, $users);
}
}

View File

@ -128,6 +128,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return Session::get(SESSION_COUNTER, 0);
}
/*
public function getPopOverText()
{
if (!Utils::isNinja() || !Auth::check() || Session::has('error')) {
@ -146,7 +147,8 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return false;
}
*/
public function afterSave($success = true, $forced = false)
{
if ($this->email) {
@ -176,4 +178,22 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
return 'remember_token';
}
public function clearSession()
{
$keys = [
RECENTLY_VIEWED,
SESSION_USER_ACCOUNTS,
SESSION_TIMEZONE,
SESSION_DATE_FORMAT,
SESSION_DATE_PICKER_FORMAT,
SESSION_DATETIME_FORMAT,
SESSION_CURRENCY,
SESSION_LOCALE,
];
foreach ($keys as $key) {
Session::forget($key);
}
}
}

View File

@ -0,0 +1,52 @@
<?php namespace App\Models;
use Eloquent;
class UserAccount extends Eloquent
{
public $timestamps = false;
public function hasUserId($userId)
{
if (!$userId) {
return false;
}
for ($i=1; $i<=5; $i++) {
$field = "user_id{$i}";
if ($this->$field && $this->$field == $userId) {
return true;
}
}
return false;
}
public function setUserId($userId)
{
if (self::hasUserId($userId)) {
return;
}
for ($i=1; $i<=5; $i++) {
$field = "user_id{$i}";
if (!$this->$field) {
$this->$field = $userId;
break;
}
}
}
public function removeUserId($userId)
{
if (!$userId || !self::hasUserId($userId)) {
return;
}
for ($i=1; $i<=5; $i++) {
$field = "user_id{$i}";
if ($this->$field && $this->$field == $userId) {
$this->$field = null;
}
}
}
}

View File

@ -4,6 +4,8 @@ use Auth;
use Request;
use Session;
use Utils;
use DB;
use stdClass;
use App\Models\AccountGateway;
use App\Models\Invitation;
@ -14,6 +16,7 @@ use App\Models\Language;
use App\Models\Contact;
use App\Models\Account;
use App\Models\User;
use App\Models\UserAccount;
class AccountRepository
{
@ -244,4 +247,129 @@ class AccountRepository
curl_exec($ch);
curl_close($ch);
}
public function findUserAccounts($userId1, $userId2 = false)
{
$query = UserAccount::where('user_id1', '=', $userId1)
->orWhere('user_id2', '=', $userId1)
->orWhere('user_id3', '=', $userId1)
->orWhere('user_id4', '=', $userId1)
->orWhere('user_id5', '=', $userId1);
if ($userId2) {
$query->orWhere('user_id1', '=', $userId2)
->orWhere('user_id2', '=', $userId2)
->orWhere('user_id3', '=', $userId2)
->orWhere('user_id4', '=', $userId2)
->orWhere('user_id5', '=', $userId2);
}
return $query->first(['id', 'user_id1', 'user_id2', 'user_id3', 'user_id4', 'user_id5']);
}
public function prepareUsersData($record) {
if (!$record) {
return false;
}
$userIds = [];
for ($i=1; $i<=5; $i++) {
$field = "user_id$i";
if ($record->$field) {
$userIds[] = $record->$field;
}
}
$users = User::with('account')
->whereIn('id', $userIds)
->get();
$data = [];
foreach ($users as $user) {
$item = new stdClass();
$item->id = $record->id;
$item->user_id = $user->id;
$item->user_name = $user->getDisplayName();
$item->account_id = $user->account->id;
$item->account_name = $user->account->getDisplayName();
$item->pro_plan_paid = $user->account->pro_plan_paid;
$data[] = $item;
}
return $data;
}
public function loadAccounts($userId) {
$record = self::findUserAccounts($userId);
return self::prepareUsersData($record);
}
public function syncAccounts($userId, $proPlanPaid) {
$users = self::loadAccounts($userId);
self::syncUserAccounts($users, $proPlanPaid);
}
public function syncUserAccounts($users, $proPlanPaid = false) {
if (!$proPlanPaid) {
foreach ($users as $user) {
if ($user->pro_plan_paid && $user->pro_plan_paid != '0000-00-00') {
$proPlanPaid = $user->pro_plan_paid;
break;
}
}
}
if (!$proPlanPaid) {
return;
}
$accountIds = [];
foreach ($users as $user) {
if ($user->pro_plan_paid != $proPlanPaid) {
$accountIds[] = $user->account_id;
}
}
if (count($accountIds)) {
DB::table('accounts')
->whereIn('id', $accountIds)
->update(['pro_plan_paid' => $proPlanPaid]);
}
}
public function associateAccounts($userId1, $userId2) {
$record = self::findUserAccounts($userId1, $userId2);
if ($record) {
foreach ([$userId1, $userId2] as $userId) {
if (!$record->hasUserId($userId)) {
$record->setUserId($userId);
}
}
} else {
$record = new UserAccount();
$record->user_id1 = $userId1;
$record->user_id2 = $userId2;
}
$record->save();
$users = self::prepareUsersData($record);
self::syncUserAccounts($users);
return $users;
}
public function unlinkAccount($userAccountId, $userId) {
$userAccount = UserAccount::whereId($userAccountId)->first();
if ($userAccount->hasUserId(Auth::user()->id)) {
$userAccount->removeUserId($userId);
$userAccount->save();
}
}
}

View File

@ -44,6 +44,10 @@ class AppServiceProvider extends ServiceProvider {
$str .= '<li class="divider"></li>
<li><a href="'.URL::to('quotes').'">'.trans("texts.quotes").'</a></li>
<li><a href="'.URL::to('quotes/create').'">'.trans("texts.new_quote").'</a></li>';
} else if ($type == ENTITY_CLIENT) {
$str .= '<li class="divider"></li>
<li><a href="'.URL::to('credits').'">'.trans("texts.credits").'</a></li>
<li><a href="'.URL::to('credits/create').'">'.trans("texts.new_credit").'</a></li>';
}
$str .= '</ul>

View File

@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class MultiCompanySupport extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_accounts', function($table)
{
$table->increments('id');
$table->unsignedInteger('user_id1')->nullable();
$table->unsignedInteger('user_id2')->nullable();
$table->unsignedInteger('user_id3')->nullable();
$table->unsignedInteger('user_id4')->nullable();
$table->unsignedInteger('user_id5')->nullable();
$table->foreign('user_id1')->references('id')->on('users');
$table->foreign('user_id2')->references('id')->on('users');
$table->foreign('user_id3')->references('id')->on('users');
$table->foreign('user_id4')->references('id')->on('users');
$table->foreign('user_id5')->references('id')->on('users');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_accounts');
}
}

15
public/css/built.css vendored
View File

@ -3345,3 +3345,18 @@ button .glyphicon {
width: 35px;
margin-top: 20px;
}
ul.user-accounts div.account {
font-size: large;
}
ul.user-accounts div.remove {
padding-top: 14px;
color: #BBB;
visibility: hidden;
}
ul.user-accounts a:hover div.remove {
visibility: visible;
}

15
public/css/style.css vendored
View File

@ -961,3 +961,18 @@ button .glyphicon {
width: 35px;
margin-top: 20px;
}
ul.user-accounts div.account {
font-size: large;
}
ul.user-accounts div.remove {
padding-top: 14px;
color: #BBB;
visibility: hidden;
}
ul.user-accounts a:hover div.remove {
visibility: visible;
}

View File

@ -696,6 +696,16 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',

View File

@ -687,6 +687,15 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',
);

View File

@ -696,5 +696,12 @@ return array(
'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',
'login' => 'Login',
'or' => 'or',
);

View File

@ -666,6 +666,15 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',
);

View File

@ -695,6 +695,16 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',
);

View File

@ -12,7 +12,7 @@ return array(
'address2' => 'Appt/Bâtiment',
'city' => 'Ville',
'state' => 'Région/Département',
'postal_code' => 'Code Postal',
'postal_code' => 'Code postal',
'country_id' => 'Pays',
'contacts' => 'Informations de contact', //if you speak about contact details
'first_name' => 'Prénom',
@ -133,17 +133,17 @@ return array(
'delete_credit' => 'Supprimer ce crédit',
'show_archived_deleted' => 'Afficher archivés/supprimés',
'filter' => 'Filtrer',
'new_client' => 'Nouveau Client',
'new_invoice' => 'Nouvelle Facture',
'new_payment' => 'Nouveau Paiement',
'new_credit' => 'Nouveau Crédit',
'new_client' => 'Nouveau client',
'new_invoice' => 'Nouvelle facture',
'new_payment' => 'Nouveau paiement',
'new_credit' => 'Nouveau crédit',
'contact' => 'Contact',
'date_created' => 'Date de création',
'last_login' => 'Dernière connexion',
'balance' => 'Solde',
'action' => 'Action',
'status' => 'Statut',
'invoice_total' => 'Montant Total',
'invoice_total' => 'Montant total',
'frequency' => 'Fréquence',
'start_date' => 'Date de début',
'end_date' => 'Date de fin',
@ -156,8 +156,8 @@ return array(
'credit_date' => 'Date de crédit',
'empty_table' => 'Aucune donnée disponible dans la table',
'select' => 'Sélectionner',
'edit_client' => 'Éditer le Client',
'edit_invoice' => 'Éditer la Facture',
'edit_client' => 'Éditer le client',
'edit_invoice' => 'Éditer la facture',
// client view page
'create_invoice' => 'Créer une facture',
@ -206,12 +206,12 @@ return array(
'import_to' => 'Importer en tant que',
'client_will_create' => 'client sera créé',
'clients_will_create' => 'clients seront créés',
'email_settings' => 'Email Settings',
'pdf_email_attachment' => 'Attach PDF to Emails',
'email_settings' => 'Paramètres mail',
'pdf_email_attachment' => 'Joindre PDF aux emails',
// application messages
'created_client' => 'Client créé avec succès',
'created_clients' => ':count clients créés ave csuccès',
'created_clients' => ':count clients créés avec csuccès',
'updated_settings' => 'paramètres mis à jour avec succès',
'removed_logo' => 'Logo supprimé avec succès',
'sent_message' => 'Message envoyé avec succès',
@ -252,7 +252,7 @@ return array(
'deleted_credits' => ':count crédits supprimés avec succès',
// Emails
'confirmation_subject' => 'Validation du compte invoice ninja',
'confirmation_subject' => 'Validation du compte Invoice Ninja',
'confirmation_header' => 'Validation du compte',
'confirmation_message' => 'Veuillez cliquer sur le lien ci-après pour valider votre compte.',
'invoice_subject' => 'Nouvelle facture :invoice en provenance de :account',
@ -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',
],
@ -330,7 +330,7 @@ return array(
'update_products' => 'Mise à jour auto des produits',
'update_products_help' => 'La mise à jour d\'une facture entraîne la <b>mise à jour des produits</b>',
'create_product' => 'Nouveau produit',
'edit_product' => 'Éditer Produit',
'edit_product' => 'Éditer produit',
'archive_product' => 'Archiver Produit',
'updated_product' => 'Produit mis à jour',
'created_product' => 'Produit créé',
@ -550,23 +550,23 @@ return array(
'created_gateway' => 'Successfully created gateway',
'deleted_gateway' => 'Successfully deleted gateway',
'pay_with_paypal' => 'PayPal',
'pay_with_card' => 'Credit card',
'pay_with_card' => 'Carte bancaire',
'change_password' => 'Change password',
'current_password' => 'Current password',
'new_password' => 'New password',
'confirm_password' => 'Confirm password',
'password_error_incorrect' => 'The current password is incorrect.',
'password_error_invalid' => 'The new password is invalid.',
'updated_password' => 'Successfully updated password',
'change_password' => 'Changer de pot de passe',
'current_password' => 'Mot de passe actuel',
'new_password' => 'Nouveau mot de passe',
'confirm_password' => 'Confirmer le mot de passe',
'password_error_incorrect' => 'Le mot de passe actuel est incorrect.',
'password_error_invalid' => 'Le nouveau mot de passe est invalide',
'updated_password' => 'Mot de passe mis à jour avec succès',
'api_tokens' => 'API Tokens',
'users_and_tokens' => 'Users & Tokens',
'account_login' => 'Account Login',
'recover_password' => 'Recover your password',
'forgot_password' => 'Forgot your password?',
'email_address' => 'Email address',
'lets_go' => 'Lets go',
'forgot_password' => 'Mot de passe oublié ?',
'email_address' => 'Adresse email',
'lets_go' => 'Allons-y !',
'password_recovery' => 'Password Recovery',
'send_email' => 'Send email',
'set_password' => 'Set Password',
@ -618,51 +618,51 @@ return array(
'recurring' => 'Recurring',
'last_invoice_sent' => 'Last invoice sent :date',
'processed_updates' => 'Successfully completed update',
'tasks' => 'Tasks',
'new_task' => 'New Task',
'start_time' => 'Start Time',
'created_task' => 'Successfully created task',
'updated_task' => 'Successfully updated task',
'edit_task' => 'Edit Task',
'archive_task' => 'Archive Task',
'restore_task' => 'Restore Task',
'delete_task' => 'Delete Task',
'stop_task' => 'Stop Task',
'time' => 'Time',
'start' => 'Start',
'stop' => 'Stop',
'now' => 'Now',
'timer' => 'Timer',
'manual' => 'Manual',
'date_and_time' => 'Date & Time',
'second' => 'second',
'seconds' => 'seconds',
'processed_updates' => 'Mise à jour effectuée avec succès',
'tasks' => 'Tâches',
'new_task' => 'Nouvelle tâche',
'start_time' => 'Début',
'created_task' => 'Tâche crée avec succès',
'updated_task' => 'Tâche mise à jour avec succès',
'edit_task' => 'Editer la tâche',
'archive_task' => 'Archiver tâche',
'restore_task' => 'Restaurer tâche',
'delete_task' => 'Supprimer tâche',
'stop_task' => 'Arrêter tâcher',
'time' => 'Temps',
'start' => 'Début',
'stop' => 'Fin',
'now' => 'Maintenant',
'timer' => 'Compteur',
'manual' => 'Manuel',
'date_and_time' => 'Date & heure',
'second' => 'seconde',
'seconds' => 'secondes',
'minute' => 'minute',
'minutes' => 'minutes',
'hour' => 'hour',
'hours' => 'hours',
'task_details' => 'Task Details',
'duration' => 'Duration',
'end_time' => 'End Time',
'end' => 'End',
'invoiced' => 'Invoiced',
'logged' => 'Logged',
'running' => 'Running',
'task_error_multiple_clients' => 'The tasks can\'t belong to different clients',
'task_error_running' => 'Please stop running tasks first',
'task_error_invoiced' => 'Tasks have already been invoiced',
'restored_task' => 'Successfully restored task',
'archived_task' => 'Successfully archived task',
'archived_tasks' => 'Successfully archived :count tasks',
'deleted_task' => 'Successfully deleted task',
'deleted_tasks' => 'Successfully deleted :count tasks',
'create_task' => 'Create Task',
'stopped_task' => 'Successfully stopped task',
'invoice_task' => 'Invoice Task',
'invoice_labels' => 'Invoice Labels',
'prefix' => 'Prefix',
'counter' => 'Counter',
'hour' => 'heure',
'hours' => 'houres',
'task_details' => 'Détails tâche',
'duration' => 'Durée',
'end_time' => 'Heure de fin',
'end' => 'Fin',
'invoiced' => 'Facturé',
'logged' => 'Connecté',
'running' => 'En cours',
'task_error_multiple_clients' => 'Cette tâche ne peut appartenir à plusieurs clients',
'task_error_running' => 'Merci d\'arrêter les tâches en cours',
'task_error_invoiced' => 'Tâches déjà facturées',
'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',
'create_task' => 'Créer tâche',
'stopped_task' => 'Tâche stoppée avec succès',
'invoice_task' => 'Tâche facturation',
'invoice_labels' => 'Champs facture',
'prefix' => 'Préfixe',
'counter' => 'Compteur',
'payment_type_dwolla' => 'Dwolla',
'gateway_help_43' => ':link to sign up for Dwolla.',
@ -687,6 +687,16 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',
);
);

View File

@ -688,5 +688,15 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',
);

View File

@ -690,6 +690,16 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',
);

View File

@ -697,6 +697,16 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',
);

View File

@ -695,6 +695,16 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',
);

View File

@ -690,6 +690,16 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',
);

View File

@ -690,6 +690,15 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',
);

View File

@ -693,6 +693,16 @@ return array(
'timezone_unset' => 'Please :link to set your timezone',
'click_here' => 'click here',
'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',
'login' => 'Login',
'or' => 'or',
);

View File

@ -79,7 +79,13 @@
{!! Former::hidden('remember')->raw() !!}
</p>
<p>{!! Button::success(trans('texts.lets_go'))->large()->submit()->block() !!}</p>
<p>{!! Button::success(trans(Utils::allowNewAccounts() ? 'texts.login' : 'texts.lets_go'))->large()->submit()->block() !!}</p>
@if (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>
@endif
<p class="link">
{!! link_to('/forgot', trans('texts.forgot_password')) !!}

View File

@ -186,6 +186,13 @@
});
}
function unlinkAccount(userAccountId, userId) {
if (confirm('{!! trans("texts.are_you_sure") !!}')) {
window.location = '{{ URL::to('/unlink_account') }}' + '/' + userAccountId + '/' + userId;
}
return false;
}
function wordWrapText(value, width)
{
@if (Auth::user()->account->auto_wrap)
@ -321,7 +328,6 @@
{!! HTML::menu_link('task') !!}
{!! HTML::menu_link('invoice') !!}
{!! HTML::menu_link('payment') !!}
{!! HTML::menu_link('credit') !!}
</ul>
<div class="navbar-form navbar-right">
@ -333,61 +339,67 @@
@endif
@endif
@if (Auth::user()->getPopOverText() && !Utils::isRegistered())
<button id="ninjaPopOver" type="button" class="btn btn-default" data-toggle="popover" data-placement="bottom" data-content="{{ Auth::user()->getPopOverText() }}" data-html="true" style="display:none">
{{ trans('texts.sign_up') }}
</button>
@endif
@if (Auth::user()->getPopOverText())
<script>
$(function() {
if (screen.width < 1170) return;
$('#ninjaPopOver').show().popover('show').hide();
$('body').click(function() {
$('#ninjaPopOver').popover('hide');
});
});
</script>
@endif
<div class="btn-group">
<div class="btn-group user-dropdown">
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown">
<div id="myAccountButton" class="ellipsis" style="max-width:100px">
{{ Auth::user()->getDisplayName() }}
@if (session(SESSION_USER_ACCOUNTS) && count(session(SESSION_USER_ACCOUNTS)))
{{ Auth::user()->account->getDisplayName() }}
@else
{{ Auth::user()->getDisplayName() }}
@endif
<span class="caret"></span>
</div>
</button>
<ul class="dropdown-menu" role="menu">
<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>
@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>
@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>
@endif
<li>{!! link_to('#', trans('texts.logout'), array('onclick'=>'logout()')) !!}</li>
</ul>
</div>
</div>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<span class="glyphicon glyphicon-cog" title="{{ trans('texts.settings') }}"/>
</a>
<ul class="dropdown-menu">
<li>{!! link_to('company/details', uctrans('texts.company_details')) !!}</li>
<li>{!! link_to('company/payments', uctrans('texts.online_payments')) !!}</li>
<li>{!! link_to('company/products', uctrans('texts.product_library')) !!}</li>
<li>{!! link_to('company/notifications', uctrans('texts.notifications')) !!}</li>
<li>{!! link_to('company/import_export', uctrans('texts.import_export')) !!}</li>
<li><a href="{{ url('company/advanced_settings/invoice_settings') }}">{!! uctrans('texts.advanced_settings') . Utils::getProLabel(ACCOUNT_ADVANCED_SETTINGS) !!}</a></li>
<li class="divider"></li>
<li>{!! link_to('#', trans('texts.logout'), array('onclick'=>'logout()')) !!}</li>
</ul>
</div>
</li>
</ul>
@if (Auth::user()->getPopOverText() && Utils::isRegistered())
<button id="ninjaPopOver" type="button" class="btn btn-default" data-toggle="popover" data-placement="bottom" data-content="{{ Auth::user()->getPopOverText() }}" data-html="true" style="display:none">
{{ Auth::user()->getDisplayName() }}
</button>
@endif
</div>
<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"
class="form-control" placeholder="{{ trans('texts.search') }}">
</div>
</form>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
@ -404,6 +416,15 @@
</ul>
</li>
</ul>
<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"
class="form-control" placeholder="{{ trans('texts.search') }}">
</div>
</form>
</div><!-- /.navbar-collapse -->

View File

@ -27,6 +27,8 @@
<input id="tableFilter" type="text" style="width:140px;margin-right:17px;background-color: white !important" class="form-control pull-left" placeholder="{{ trans('texts.filter') }}"/>
@if (Auth::user()->isPro() && $entityType == ENTITY_INVOICE)
{!! Button::normal(trans('texts.quotes'))->asLinkTo(URL::to('/quotes'))->appendIcon(Icon::create('list')) !!}
@elseif ($entityType == ENTITY_CLIENT)
{!! Button::normal(trans('texts.credits'))->asLinkTo(URL::to('/credits'))->appendIcon(Icon::create('list')) !!}
@endif
{!! Button::primary(trans("texts.new_$entityType"))->asLinkTo(URL::to("/{$entityType}s/create"))->appendIcon(Icon::create('plus-sign')) !!}
</div>