diff --git a/app/controllers/AccountController.php b/app/controllers/AccountController.php index 52caaae720..2fb4e0e39d 100755 --- a/app/controllers/AccountController.php +++ b/app/controllers/AccountController.php @@ -87,6 +87,8 @@ class AccountController extends \BaseController if ($entityType == 'user') { return Redirect::to('company/'.ACCOUNT_ADVANCED_SETTINGS.'/'.ACCOUNT_USER_MANAGEMENT); + } elseif ($entityType == 'token') { + return Redirect::to('company/'.ACCOUNT_ADVANCED_SETTINGS.'/'.ACCOUNT_TOKEN_MANAGEMENT); } else { return Redirect::to("{$entityType}s"); } diff --git a/app/controllers/TokenController.php b/app/controllers/TokenController.php new file mode 100755 index 0000000000..c472cc6d2b --- /dev/null +++ b/app/controllers/TokenController.php @@ -0,0 +1,159 @@ +where('account_tokens.account_id', '=', Auth::user()->account_id); + + if (!Session::get('show_trash:token')) { + $query->where('account_tokens.deleted_at', '=', null); + } + + $query->select('account_tokens.public_id', 'account_tokens.name', 'account_tokens.token', 'account_tokens.public_id', 'account_tokens.deleted_at'); + + return Datatable::query($query) + ->addColumn('name', function ($model) { return link_to('tokens/'.$model->public_id.'/edit', $model->name); }) + ->addColumn('token', function ($model) { return $model->token; }) + ->addColumn('dropdown', function ($model) { + $actions = '
'; + + return $actions; + }) + ->orderColumns(['name', 'token']) + ->make(); + } + + public function edit($publicId) + { + $token = AccountToken::where('account_id', '=', Auth::user()->account_id) + ->where('public_id', '=', $publicId)->firstOrFail(); + + $data = [ + 'showBreadcrumbs' => false, + 'token' => $token, + 'method' => 'PUT', + 'url' => 'tokens/'.$publicId, + 'title' => trans('texts.edit_token'), + ]; + + return View::make('accounts.token', $data); + } + + public function update($publicId) + { + return $this->save($publicId); + } + + public function store() + { + return $this->save(); + } + + /** + * Displays the form for account creation + * + */ + public function create() + { + if (!Auth::user()->confirmed) { + Session::flash('error', trans('texts.register_to_add_user')); + return Redirect::to('company/advanced_settings/user_management'); + } + + $data = [ + 'showBreadcrumbs' => false, + 'token' => null, + 'method' => 'POST', + 'url' => 'tokens', + 'title' => trans('texts.add_token'), + ]; + + return View::make('accounts.token', $data); + } + + public function delete() + { + $tokenPublicId = Input::get('tokenPublicId'); + $token = AccountToken::where('account_id', '=', Auth::user()->account_id) + ->where('public_id', '=', $tokenPublicId)->firstOrFail(); + + $token->delete(); + + Session::flash('message', trans('texts.deleted_token')); + + return Redirect::to('company/advanced_settings/token_management'); + } + + /** + * Stores new account + * + */ + public function save($tokenPublicId = false) + { + $rules = [ + 'name' => 'required', + ]; + + if ($tokenPublicId) { + $token = AccountToken::where('account_id', '=', Auth::user()->account_id) + ->where('public_id', '=', $tokenPublicId)->firstOrFail(); + } + + $validator = Validator::make(Input::all(), $rules); + + if ($validator->fails()) { + return Redirect::to($tokenPublicId ? 'tokens/edit' : 'tokens/create')->withInput()->withErrors($validator); + } + + if ($tokenPublicId) { + $token->name = trim(Input::get('name')); + } else { + $lastToken = AccountToken::withTrashed()->where('account_id', '=', Auth::user()->account_id) + ->orderBy('public_id', 'DESC')->first(); + + $token = AccountToken::createNew(); + $token->name = trim(Input::get('name')); + $token->token = str_random(RANDOM_KEY_LENGTH); + $token->public_id = $lastToken ? $lastToken->public_id + 1 : 1; + } + + $token->save(); + + if ($tokenPublicId) { + $message = trans('texts.updated_token'); + } else { + $message = trans('texts.created_token'); + } + + Session::flash('message', $message); + + return Redirect::to('company/advanced_settings/token_management'); + } + +} diff --git a/app/database/migrations/2015_03_03_140259_add_tokens.php b/app/database/migrations/2015_03_03_140259_add_tokens.php new file mode 100644 index 0000000000..45b54e3ca4 --- /dev/null +++ b/app/database/migrations/2015_03_03_140259_add_tokens.php @@ -0,0 +1,54 @@ +increments('id'); + $table->unsignedInteger('account_id')->index(); + $table->unsignedInteger('user_id'); + $table->timestamps(); + $table->softDeletes(); + + $table->string('name')->nullable(); + $table->string('token')->unique(); + + $table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade'); + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + + $table->unsignedInteger('public_id')->nullable(); + $table->unique(['account_id', 'public_id']); + }); + + Schema::table('activities', function($table) + { + $table->unsignedInteger('token_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::drop('account_tokens'); + + Schema::table('activities', function($table) + { + $table->dropColumn('token_id'); + }); + } + +} diff --git a/app/filters.php b/app/filters.php index 3792f38a9d..db20fa951e 100755 --- a/app/filters.php +++ b/app/filters.php @@ -176,6 +176,16 @@ Route::filter('auth.basic', function() Route::filter('api.access', function() { $headers = Utils::getApiHeaders(); + + // check for a valid token + $token = AccountToken::where('token', '=', Request::header('X-Ninja-Token'))->first(['id', 'user_id']); + + if ($token) { + Auth::loginUsingId($token->user_id); + Session::set('token_id', $token->id); + } else { + return Response::make('Invalid token', 403, $headers); + } if (!Utils::isPro()) { return Response::make('API requires pro plan', 403, $headers); diff --git a/app/lang/da/texts.php b/app/lang/da/texts.php index 9996f0cb96..b9fe647bc5 100644 --- a/app/lang/da/texts.php +++ b/app/lang/da/texts.php @@ -539,4 +539,15 @@ return array( 'invoice_footer' => 'Invoice footer', 'save_as_default_footer' => 'Save as default footer', + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', + + ); diff --git a/app/lang/de/texts.php b/app/lang/de/texts.php index ffbbf1ec2b..baae707b26 100644 --- a/app/lang/de/texts.php +++ b/app/lang/de/texts.php @@ -529,4 +529,15 @@ return array( 'invoice_footer' => 'Invoice footer', 'save_as_default_footer' => 'Save as default footer', + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', + + ); diff --git a/app/lang/en/texts.php b/app/lang/en/texts.php index 70d7096605..78077cb34e 100644 --- a/app/lang/en/texts.php +++ b/app/lang/en/texts.php @@ -537,4 +537,15 @@ return array( 'invoice_footer' => 'Invoice footer', 'save_as_default_footer' => 'Save as default footer', + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', + + ); diff --git a/app/lang/es/texts.php b/app/lang/es/texts.php index e264b28887..c1656cdd62 100644 --- a/app/lang/es/texts.php +++ b/app/lang/es/texts.php @@ -509,4 +509,15 @@ return array( 'invoice_footer' => 'Invoice footer', 'save_as_default_footer' => 'Save as default footer', + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', + + ); \ No newline at end of file diff --git a/app/lang/fr/texts.php b/app/lang/fr/texts.php index eda3e7de46..feb611dffa 100644 --- a/app/lang/fr/texts.php +++ b/app/lang/fr/texts.php @@ -530,4 +530,14 @@ return array( 'invoice_footer' => 'Invoice footer', 'save_as_default_footer' => 'Save as default footer', + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', + ); \ No newline at end of file diff --git a/app/lang/it/texts.php b/app/lang/it/texts.php index 359bbfd5a1..418acd0aed 100644 --- a/app/lang/it/texts.php +++ b/app/lang/it/texts.php @@ -531,5 +531,15 @@ return array( 'default_invoice_footer' => 'Set default invoice footer', 'invoice_footer' => 'Invoice footer', 'save_as_default_footer' => 'Save as default footer', + + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', ); diff --git a/app/lang/lt/texts.php b/app/lang/lt/texts.php index 914c185b22..194a88af58 100644 --- a/app/lang/lt/texts.php +++ b/app/lang/lt/texts.php @@ -539,6 +539,16 @@ return array( 'default_invoice_footer' => 'Set default invoice footer', 'invoice_footer' => 'Invoice footer', 'save_as_default_footer' => 'Save as default footer', + + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', ); diff --git a/app/lang/nb_NO/texts.php b/app/lang/nb_NO/texts.php index 6f66e10b6d..c90260274b 100644 --- a/app/lang/nb_NO/texts.php +++ b/app/lang/nb_NO/texts.php @@ -538,5 +538,15 @@ return array( 'invoice_footer' => 'Invoice footer', 'save_as_default_footer' => 'Save as default footer', + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', + ); \ No newline at end of file diff --git a/app/lang/nl/texts.php b/app/lang/nl/texts.php index c92925e847..961228fbe1 100644 --- a/app/lang/nl/texts.php +++ b/app/lang/nl/texts.php @@ -532,6 +532,16 @@ return array( 'default_invoice_footer' => 'Set default invoice footer', 'invoice_footer' => 'Invoice footer', 'save_as_default_footer' => 'Save as default footer', + + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', ); \ No newline at end of file diff --git a/app/lang/pt_BR/texts.php b/app/lang/pt_BR/texts.php index ae4c7d976f..b194ed7538 100644 --- a/app/lang/pt_BR/texts.php +++ b/app/lang/pt_BR/texts.php @@ -519,5 +519,16 @@ return array( 'default_invoice_footer' => 'Set default invoice footer', 'invoice_footer' => 'Invoice footer', 'save_as_default_footer' => 'Save as default footer', + + 'token_management' => 'Token Management', + 'tokens' => 'Tokens', + 'add_token' => 'Add Token', + 'show_deleted_tokens' => 'Show deleted tokens', + 'deleted_token' => 'Successfully deleted token', + 'created_token' => 'Successfully created token', + 'edit_token' => 'Edit Token', + 'delete_token' => 'Delete Token', + 'token' => 'Token', + ); diff --git a/app/libraries/Utils.php b/app/libraries/Utils.php index 98c209ac63..99d5a7052e 100755 --- a/app/libraries/Utils.php +++ b/app/libraries/Utils.php @@ -466,8 +466,9 @@ class Utils $person = $person ? $person->getDisplayName() : 'System'; $entity = $entity ? '['.$entity->getActivityKey().']' : ''; $otherPerson = $otherPerson ? 'to '.$otherPerson->getDisplayName() : ''; + $token = Session::get('token_id') ? ' ('.trans('texts.token').')' : ''; - return trim("$person $action $entity $otherPerson"); + return trim("$person $token $action $entity $otherPerson"); } public static function decodeActivity($message) diff --git a/app/models/AccountToken.php b/app/models/AccountToken.php new file mode 100755 index 0000000000..dea1f1d19a --- /dev/null +++ b/app/models/AccountToken.php @@ -0,0 +1,9 @@ +belongsTo('Account'); + } +} diff --git a/app/models/Activity.php b/app/models/Activity.php index 9e9cb16710..912aa554be 100755 --- a/app/models/Activity.php +++ b/app/models/Activity.php @@ -34,6 +34,8 @@ class Activity extends Eloquent Utils::fatalError(); } + $activity->token_id = Session::get('token_id', null); + return $activity; } diff --git a/app/routes.php b/app/routes.php index c039dfda9b..377407cc6e 100755 --- a/app/routes.php +++ b/app/routes.php @@ -82,6 +82,10 @@ Route::group(array('before' => 'auth'), function() { Route::get('send_confirmation/{user_id}', 'UserController@sendConfirmation'); Route::get('restore_user/{user_id}', 'UserController@restoreUser'); + Route::get('api/tokens', array('as'=>'api.tokens', 'uses'=>'TokenController@getDatatable')); + Route::resource('tokens', 'TokenController'); + Route::post('tokens/delete', 'TokenController@delete'); + Route::get('api/products', array('as'=>'api.products', 'uses'=>'ProductController@getDatatable')); Route::resource('products', 'ProductController'); Route::get('products/{product_id}/archive', 'ProductController@archive'); @@ -142,7 +146,7 @@ Route::group(array('before' => 'auth'), function() { }); // Route group for API -Route::group(array('prefix' => 'api/v1', 'before' => ['auth.basic', 'api.access']), function() +Route::group(array('prefix' => 'api/v1', 'before' => ['api.access']), function() { Route::resource('ping', 'ClientApiController@ping'); Route::resource('clients', 'ClientApiController'); @@ -186,6 +190,7 @@ define('ACCOUNT_CHART_BUILDER', 'chart_builder'); define('ACCOUNT_USER_MANAGEMENT', 'user_management'); define('ACCOUNT_DATA_VISUALIZATIONS', 'data_visualizations'); define('ACCOUNT_EMAIL_TEMPLATES', 'email_templates'); +define('ACCOUNT_TOKEN_MANAGEMENT', 'token_management'); define('ACTIVITY_TYPE_CREATE_CLIENT', 1); define('ACTIVITY_TYPE_ARCHIVE_CLIENT', 2); diff --git a/app/views/accounts/nav_advanced.blade.php b/app/views/accounts/nav_advanced.blade.php index 513c05dffd..99ea2abf8e 100644 --- a/app/views/accounts/nav_advanced.blade.php +++ b/app/views/accounts/nav_advanced.blade.php @@ -4,7 +4,8 @@ {{ HTML::nav_link('company/advanced_settings/email_templates', 'email_templates') }} {{ HTML::nav_link('company/advanced_settings/data_visualizations', 'data_visualizations') }} {{ HTML::nav_link('company/advanced_settings/chart_builder', 'chart_builder') }} - {{ HTML::nav_link('company/advanced_settings/user_management', 'user_management') }} + {{ HTML::nav_link('company/advanced_settings/user_management', 'users') }} + {{ HTML::nav_link('company/advanced_settings/token_management', 'tokens') }} +diff --git a/app/views/accounts/token.blade.php b/app/views/accounts/token.blade.php new file mode 100644 index 0000000000..ac8649079f --- /dev/null +++ b/app/views/accounts/token.blade.php @@ -0,0 +1,29 @@ +@extends('accounts.nav') + +@section('content') + @parent + + {{ Former::open($url)->method($method)->addClass('col-md-8 col-md-offset-2 warn-on-exit')->rules(array( + 'name' => 'required', + )); }} + + {{ Former::legend($title) }} + +
+ + @if ($token) + {{ Former::populate($token) }} + @endif + + {{ Former::text('name') }} + +
+ + {{ Former::actions( + Button::lg_success_submit(trans('texts.save'))->append_with_icon('floppy-disk'), + Button::lg_default_link('company/advanced_settings/token_management', 'Cancel')->append_with_icon('remove-circle') + ) }} + + {{ Former::close() }} + +@stop \ No newline at end of file diff --git a/app/views/accounts/token_management.blade.php b/app/views/accounts/token_management.blade.php new file mode 100644 index 0000000000..9f1c0ed1c7 --- /dev/null +++ b/app/views/accounts/token_management.blade.php @@ -0,0 +1,67 @@ +@extends('accounts.nav') + +@section('content') + @parent + @include('accounts.nav_advanced') + + {{ Former::open('tokens/delete')->addClass('user-form') }} + {{ Former::legend('token_management') }} + + + {{ Former::close() }} + + + @if (Utils::isPro()) + {{ Button::success_link(URL::to('tokens/create'), trans("texts.add_token"), array('class' => 'pull-right'))->append_with_icon('plus-sign') }} + @endif + + + + {{ Datatable::table() + ->addColumn( + trans('texts.name'), + trans('texts.token'), + trans('texts.action')) + ->setUrl(url('api/tokens/')) + ->setOptions('sPaginationType', 'bootstrap') + ->setOptions('bFilter', false) + ->setOptions('bAutoWidth', false) + ->setOptions('aoColumns', [[ "sWidth"=> "40%" ], [ "sWidth"=> "40%" ], ["sWidth"=> "20%"]]) + ->setOptions('aoColumnDefs', [['bSortable'=>false, 'aTargets'=>[2]]]) + ->render('datatable') }} + + + +@stop diff --git a/app/views/accounts/user_management.blade.php b/app/views/accounts/user_management.blade.php index 6d46a19bf0..8a1a66df9b 100644 --- a/app/views/accounts/user_management.blade.php +++ b/app/views/accounts/user_management.blade.php @@ -7,8 +7,6 @@ {{ Former::open('users/delete')->addClass('user-form') }} {{ Former::legend('user_management') }} - -