1
0
mirror of https://github.com/cydrobolt/polr.git synced 2024-09-19 15:11:40 +02:00

Merge pull request #335 from cydrobolt/feature/edit_long_link

Allow editing of long links and refactor modals
This commit is contained in:
Chaoyi Zha 2017-05-06 12:50:41 -04:00 committed by GitHub
commit def7e36bc1
12 changed files with 418 additions and 286 deletions

View File

@ -14,22 +14,55 @@ class AdminPaginationController extends Controller {
* @return Response
*/
public function paginateAdminUsers(Request $request) {
self::ensureAdmin();
/* Cell rendering functions */
$admin_users = User::select(['username', 'email', 'created_at', 'active', 'api_key', 'api_active', 'api_quota', 'role', 'id']);
return Datatables::of($admin_users)
->addColumn('api_action', function ($user) {
public function renderLongUrlCell($link) {
return '<a target="_blank" title="' . e($link->long_url) . '" href="'. $link->long_url .'">' . str_limit($link->long_url, 50) . '</a>
<a class="btn btn-primary btn-xs edit-long-link-btn" ng-click="editLongLink(\'' . $link->short_url . '\', \'' . $link->long_url . '\')"><i class="fa fa-edit edit-link-icon"></i></a>';
}
public function renderClicksCell($link) {
if (env('SETTING_ADV_ANALYTICS')) {
return $link->clicks . ' <a target="_blank" class="stats-icon" href="/admin/stats/' . e($link->short_url) . '">
<i class="fa fa-area-chart" aria-hidden="true"></i>
</a>';
}
else {
return $link->clicks;
}
}
public function renderDeleteUserCell($user) {
// Add "Delete" action button
$btn_class = '';
if (session('username') === $user->username) {
$btn_class = 'disabled';
}
return '<a ng-click="deleteUser($event, \''. $user->id .'\')" class="btn btn-sm btn-danger ' . $btn_class . '">
Delete
</a>';
}
public function renderDeleteLinkCell($link) {
// Add "Delete" action button
return '<a ng-click="deleteLink($event, \'' . e($link->short_url) . '\')"
class="btn btn-sm btn-warning delete-link">
Delete
</a>';
}
public function renderAdminApiActionCell($user) {
// Add "API Info" action button
return '<a class="activate-api-modal btn btn-sm btn-info"
ng-click="openAPIModal($event, \'' . e($user->username) . '\', \'' . $user->api_key . '\', \'' . $user->api_active . '\', \'' . e($user->api_quota) . '\', \'' . $user->id . '\')">
API info
</a>';
})
->addColumn('toggle_active', function ($user) {
}
public function renderToggleUserActiveCell($user) {
// Add user account active state toggle buttons
$btn_class = '';
if (session('username') == $user->username) {
if (session('username') === $user->username) {
$btn_class = ' disabled';
}
@ -43,17 +76,18 @@ class AdminPaginationController extends Controller {
}
return '<a class="btn btn-sm status-display' . $btn_color_class . $btn_class . '" ng-click="toggleUserActiveStatus($event, ' . $user->id . ')">' . $active_text . '</a>';
})
->addColumn('change_role', function ($user) {
}
public function renderChangeUserRoleCell($user) {
// Add "change role" select box
// FIXME <select> field does not use Angular bindings
// <select> field does not use Angular bindings
// because of an issue affecting fields with duplicate names.
$select_role = '<select ng-init="changeUserRole.u' . $user->id . ' = \'' . e($user->role) . '\'"
ng-model="changeUserRole.u' . $user->id . '" ng-change="changeUserRole(changeUserRole.u' . $user->id . ', '.$user->id.')"
class="form-control"';
if (session('username') == $user->username) {
if (session('username') === $user->username) {
// Do not allow user to change own role
$select_role .= ' disabled';
}
@ -63,7 +97,7 @@ class AdminPaginationController extends Controller {
// Iterate over each available role and output option
$select_role .= '<option value="' . e($role_val) . '"';
if ($user->role == $role_val) {
if ($user->role === $role_val) {
$select_role .= ' selected';
}
@ -72,27 +106,9 @@ class AdminPaginationController extends Controller {
$select_role .= '</select>';
return $select_role;
})
->addColumn('delete', function ($user) {
// Add "Delete" action button
$btn_class = '';
if (session('username') == $user->username) {
$btn_class = 'disabled';
}
return '<a ng-click="deleteUser($event, \''. $user->id .'\')" class="btn btn-sm btn-danger ' . $btn_class . '">
Delete
</a>';
})
->escapeColumns(['username', 'email'])
->make(true);
}
public function paginateAdminLinks(Request $request) {
self::ensureAdmin();
$admin_links = Link::select(['short_url', 'long_url', 'clicks', 'created_at', 'creator', 'is_disabled']);
return Datatables::of($admin_links)
->addColumn('disable', function ($link) {
public function renderToggleLinkActiveCell($link) {
// Add "Disable/Enable" action buttons
$btn_class = 'btn-danger';
$btn_text = 'Disable';
@ -105,25 +121,32 @@ class AdminPaginationController extends Controller {
return '<a ng-click="toggleLink($event, \'' . e($link->short_url) . '\')" class="btn btn-sm ' . $btn_class . '">
' . $btn_text . '
</a>';
})
->addColumn('delete', function ($link) {
// Add "Delete" action button
return '<a ng-click="deleteLink($event, \'' . e($link->short_url) . '\')"
class="btn btn-sm btn-warning delete-link">
Delete
</a>';
})
->editColumn('clicks', function ($link) {
if (env('SETTING_ADV_ANALYTICS')) {
return $link->clicks . ' <a target="_blank" class="stats-icon" href="/admin/stats/' . e($link->short_url) . '">
<i class="fa fa-area-chart" aria-hidden="true"></i>
</a>';
}
else {
return $link->clicks;
/* DataTables bindings */
public function paginateAdminUsers(Request $request) {
self::ensureAdmin();
$admin_users = User::select(['username', 'email', 'created_at', 'active', 'api_key', 'api_active', 'api_quota', 'role', 'id']);
return Datatables::of($admin_users)
->addColumn('api_action', [$this, 'renderAdminApiActionCell'])
->addColumn('toggle_active', [$this, 'renderToggleUserActiveCell'])
->addColumn('change_role', [$this, 'renderChangeUserRoleCell'])
->addColumn('delete', [$this, 'renderDeleteUserCell'])
->escapeColumns(['username', 'email'])
->make(true);
}
})
->editColumn('long_url', '<a target="_blank" title="{{ $long_url }}" href="{{ $long_url }}">{{ str_limit($long_url, 50) }}</a>')
public function paginateAdminLinks(Request $request) {
self::ensureAdmin();
$admin_links = Link::select(['short_url', 'long_url', 'clicks', 'created_at', 'creator', 'is_disabled']);
return Datatables::of($admin_links)
->addColumn('disable', [$this, 'renderToggleLinkActiveCell'])
->addColumn('delete', [$this, 'renderDeleteLinkCell'])
->editColumn('clicks', [$this, 'renderClicksCell'])
->editColumn('long_url', [$this, 'renderLongUrlCell'])
->escapeColumns(['short_url', 'creator'])
->make(true);
}
@ -133,20 +156,11 @@ class AdminPaginationController extends Controller {
$username = session('username');
$user_links = Link::where('creator', $username)
->select(['short_url', 'long_url', 'clicks', 'created_at']);
->select(['id', 'short_url', 'long_url', 'clicks', 'created_at']);
return Datatables::of($user_links)
->editColumn('clicks', function ($link) {
if (env('SETTING_ADV_ANALYTICS')) {
return $link->clicks . ' <a target="_blank" class="stats-icon" href="/admin/stats/' . e($link->short_url) . '">
<i class="fa fa-area-chart" aria-hidden="true"></i>
</a>';
}
else {
return $link->clicks;
}
})
->editColumn('long_url', '<a target="_blank" title="{{ $long_url }}" href="{{ $long_url }}">{{ str_limit($long_url, 50) }}</a>')
->editColumn('clicks', [$this, 'renderClicksCell'])
->editColumn('long_url', [$this, 'renderLongUrlCell'])
->escapeColumns(['short_url'])
->make(true);
}

View File

@ -225,4 +225,32 @@ class AjaxController extends Controller {
return ($new_status ? "Enable" : "Disable");
}
public function editLinkLongUrl(Request $request) {
/**
* If user is an admin, allow the user to edit the value of any link's long URL.
* Otherwise, only allow the user to edit their own links.
*/
$link_ending = $request->input('link_ending');
$link = LinkHelper::linkExists($link_ending);
$new_long_url = $request->input('new_long_url');
$this->validate($request, [
'new_long_url' => 'required|url',
]);
if (!$link) {
abort(404, 'Link not found.');
}
if ($link->creator !== session('username')) {
self::ensureAdmin();
}
$link->long_url = $new_long_url;
$link->save();
return "OK";
}
}

View File

@ -55,6 +55,7 @@ $app->group(['prefix' => '/api/v2', 'namespace' => 'App\Http\Controllers'], func
$app->post('admin/delete_user', ['as' => 'api_delete_user', 'uses' => 'AjaxController@deleteUser']);
$app->post('admin/toggle_link', ['as' => 'api_toggle_link', 'uses' => 'AjaxController@toggleLink']);
$app->post('admin/delete_link', ['as' => 'api_delete_link', 'uses' => 'AjaxController@deleteLink']);
$app->post('admin/edit_link_long_url', ['as' => 'api_edit_link_long_url', 'uses' => 'AjaxController@editLinkLongUrl']);
$app->get('admin/get_admin_users', ['as' => 'api_get_admin_users', 'uses' => 'AdminPaginationController@paginateAdminUsers']);
$app->get('admin/get_admin_links', ['as' => 'api_get_admin_links', 'uses' => 'AdminPaginationController@paginateAdminLinks']);

View File

@ -21,6 +21,7 @@ you may be interested in looking at a [legacy 1.x release](https://github.com/cy
- Mbstring PHP Extension
- Tokenizer PHP Extension
- JSON PHP Extension
- PHP curl extension
## Downloading the source code
@ -57,6 +58,14 @@ curl -sS https://getcomposer.org/installer | php
php composer.phar install --no-dev -o
```
If composer fails to install the proper dependencies due to your PHP version, delete `composer.lock`
and try installing the dependencies again.
```bash
rm composer.lock
php composer.phar install --no-dev -o
```
## Running Polr on...
### Apache

View File

@ -54,3 +54,7 @@ input.api-quota {
a.new-user-add {
margin-left: 0.5em
}
.edit-long-link-btn {
opacity: 0.45;
}

View File

@ -0,0 +1,18 @@
<div class="modal fade" id="edit-long-link-{{ linkEnding }}" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Long URL</h4>
</div>
<div class="modal-body">
<input type="url" value="{{ oldLongLink }}" placeholder="Long URL..." class="form-control" />
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" ng-click="saveChanges()">Save Changes</button>
</div>
</div> <!-- /.modal-content -->
</div> <!-- /.modal-dialog -->
</div> <!-- /.modal -->

View File

@ -0,0 +1,31 @@
<div class="modal fade" id="edit-user-api-info-{{ userId }}" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<h4 class="modal-title">Edit User API Settings</h4>
</div>
<div class="modal-body">
<p>
<span>API Active</span>:
<code class='status-display' ng-bind="apiActive"></code>
<a ng-click="toggleAPIStatus()" class='btn btn-xs btn-success'>toggle</a>
</p>
<p>
<span>API Key: </span><code class='status-display' ng-bind="apiKey">{{api_key}}</code>
<a ng-click="parentGenerateNewAPIKey($event)" class='btn btn-xs btn-danger'>reset</a>
</p>
<p>
<span>API Quota (req/min, -1 for unlimited):</span> <input type='number' class='form-control api-quota' ng-model="apiQuota">
<a ng-click="updateAPIQuota()" class='btn btn-xs btn-warning'>change</a>
</p>
</div>
</div> <!-- /.modal-content -->
</div> <!-- /.modal-dialog -->
</div> <!-- /.modal -->

View File

@ -1,8 +1,107 @@
polr.controller('AdminCtrl', function($scope, $compile) {
polr.directive('editLongLinkModal', function () {
return {
scope: {
oldLongLink: '=',
linkEnding: '=',
cleanModals: '='
},
templateUrl: '/directives/editLongLinkModal.html',
transclude: true,
controller: function ($scope, $element, $timeout) {
$scope.init = function () {
// Destroy directive and clean modal on close
$element.find('.modal').on("hidden.bs.modal", function () {
$scope.$destroy();
$scope.cleanModals('editLongLink');
});
}
$scope.saveChanges = function () {
// Save long URL changes
apiCall('admin/edit_link_long_url', {
'link_ending': $scope.linkEnding,
'new_long_url': $element.find('input').val()
}, function(data) {
toastr.success('The link was updated.', 'Success')
}, function(err) {
toastr.error('The new URL format is not valid.', 'Error');
});
};
$scope.init();
}
};
});
polr.directive('editUserApiInfoModal', function () {
return {
scope: {
userId: '=',
apiActive: '=',
apiKey: '=',
apiQuota: '=',
generateNewApiKey: '=',
cleanModals: '='
},
templateUrl: '/directives/editUserApiInfoModal.html',
transclude: true,
controller: function ($scope, $element, $timeout) {
$scope.init = function () {
// Destroy directive and clean modal on close
$element.find('.modal').on("hidden.bs.modal", function () {
$scope.$destroy();
$scope.cleanModals('editUserApiInfo');
});
$scope.apiActive = res_value_to_text($scope.apiActive);
}
// Toggle API access status
$scope.toggleAPIStatus = function() {
apiCall('admin/toggle_api_active', {
'user_id': $scope.userId,
}, function(new_status) {
$scope.apiActive = res_value_to_text(new_status);
$scope.$digest();
});
};
// Generate new API key for user_id
$scope.parentGenerateNewAPIKey = function($event) {
$scope.generateNewApiKey($event, $scope.userId, false);
};
// Update user API quotas
$scope.updateAPIQuota = function() {
apiCall('admin/edit_api_quota', {
'user_id': $scope.userId,
'new_quota': parseInt($scope.apiQuota)
}, function(next_action) {
toastr.success("Quota successfully changed.", "Success");
});
};
$scope.init();
}
};
});
polr.controller('AdminCtrl', function($scope, $compile, $timeout) {
/* Initialize $scope variables */
$scope.state = {
showNewUserWell: false
};
$scope.datatables = {};
$scope.modals = {
editLongLink: [],
editUserApiInfo: []
};
$scope.newUserParams = {
username: '',
userPassword: '',
userEmail: '',
userRole: ''
};
$scope.syncHash = function() {
var url = document.location.toString();
@ -11,6 +110,14 @@ polr.controller('AdminCtrl', function($scope, $compile) {
}
};
$scope.cleanModals = function(modalType) {
$timeout(function () {
$scope.modals[modalType].shift();
});
$scope.reloadLinkTables();
};
// Initialise Datatables elements
$scope.initTables = function() {
var datatables_config = {
@ -70,27 +177,18 @@ polr.controller('AdminCtrl', function($scope, $compile) {
}, datatables_config));
};
// Append modals to Angular root
$scope.appendModal = function(html, id) {
id = esc_selector(id);
$scope.reloadLinkTables = function () {
// Reload DataTables for affected tables
// without resetting page
if ('admin_links_table' in $scope.datatables) {
$scope.datatables['admin_links_table'].ajax.reload(null, false);
}
$(".ng-root").append(html);
var modal_ele = $("#" + id);
modal_ele.append(html);
modal_ele.modal();
$compile(modal_ele)($scope);
$("body").delegate("#" + id, "hidden.bs.modal", function() {
modal_ele.remove();
});
$scope.datatables['user_links_table'].ajax.reload(null, false);
};
// Hide table rows
$scope.hideRow = function(el, msg) {
var row = el.parent().parent();
toastr.success(msg, "Success");
row.fadeOut('slow');
$scope.reloadUserTables = function () {
$scope.datatables['admin_users_table'].ajax.reload(null, false);
};
/*
@ -113,68 +211,6 @@ polr.controller('AdminCtrl', function($scope, $compile) {
});
}
$scope.checkNewUserFields = function() {
var response = true;
$('.new-user-fields input').each(function () {
if ($(this).val().trim() == '' || response == false) {
response = false;
}
});
return response;
}
$scope.addNewUser = function($event) {
// Create a new user
// FIXME could use Angular models in the future
// instead of relying on .val()
var username = $('#new-username').val();
var user_password = $('#new-user-password').val();
var user_email = $('#new-user-email').val();
var user_role = $('#new-user-role').val();
if (!$scope.checkNewUserFields()) {
toastr.error("Fields cannot be empty.", "Error");
return false;
}
apiCall('admin/add_new_user', {
'username': username,
'user_password': user_password,
'user_email': user_email,
'user_role': user_role,
}, function(result) {
toastr.success("User " + username + " successfully created.", "Success");
$('#new-user-form').clearForm();
$scope.datatables['admin_users_table'].ajax.reload();
}, function () {
toastr.error("An error occured while creating the user.", "Error");
});
}
// Delete user
$scope.deleteUser = function($event, user_id) {
var el = $($event.target);
apiCall('admin/delete_user', {
'user_id': user_id,
}, function(new_status) {
$scope.hideRow(el, 'User successfully deleted.');
});
};
$scope.changeUserRole = function(role, user_id) {
apiCall('admin/change_user_role', {
'user_id': user_id,
'role': role,
}, function(result) {
toastr.success("User role successfully changed.", "Success");
});
};
// Generate new API key for user_id
$scope.generateNewAPIKey = function($event, user_id, is_dev_tab) {
var el = $($event.target);
@ -195,51 +231,75 @@ polr.controller('AdminCtrl', function($scope, $compile) {
});
};
// Toggle API access status
$scope.toggleAPIStatus = function($event, user_id) {
var el = $($event.target);
var status_display_elem = el.prevAll('.status-display');
$scope.checkNewUserFields = function() {
var response = true;
apiCall('admin/toggle_api_active', {
$('.new-user-fields input').each(function () {
if ($(this).val().trim() == '' || response == false) {
response = false;
}
});
return response;
}
$scope.addNewUser = function($event) {
// Allow admins to add new users
if (!$scope.checkNewUserFields()) {
toastr.error("Fields cannot be empty.", "Error");
return false;
}
apiCall('admin/add_new_user', {
'username': $scope.newUserParams.username,
'user_password': $scope.newUserParams.userPassword,
'user_email': $scope.newUserParams.userEmail,
'user_role': $scope.newUserParams.userRole,
}, function(result) {
toastr.success("User " + $scope.newUserParams.username + " successfully created.", "Success");
$('#new-user-form').clearForm();
$scope.datatables['admin_users_table'].ajax.reload();
}, function () {
toastr.error("An error occured while creating the user.", "Error");
});
}
// Delete user
$scope.deleteUser = function($event, user_id) {
var el = $($event.target);
apiCall('admin/delete_user', {
'user_id': user_id,
}, function(new_status) {
new_status = res_value_to_text(new_status);
status_display_elem.text(new_status);
toastr.success('User successfully deleted.', 'Success');
$scope.reloadUserTables();
});
};
// Update user API quotas
$scope.updateAPIQuota = function($event, user_id) {
var el = $($event.target);
var new_quota = el.prevAll('.api-quota').val();
apiCall('admin/edit_api_quota', {
$scope.changeUserRole = function(role, user_id) {
apiCall('admin/change_user_role', {
'user_id': user_id,
'new_quota': parseInt(new_quota)
}, function(next_action) {
toastr.success("Quota successfully changed.", "Success");
'role': role,
}, function(result) {
toastr.success("User role successfully changed.", "Success");
});
};
// Open user API settings menu
$scope.openAPIModal = function($event, username, api_key, api_active, api_quota, user_id) {
var el = $($event.target);
var markup = $('#api-modal-template').html();
var modal_id = "api-modal-" + username;
var modal_context = {
id: modal_id,
api_key: api_key,
api_active: parseInt(api_active),
api_quota: api_quota,
user_id: user_id,
title: "API Information for " + username,
body: markup
};
var mt_html = $scope.modal_template(modal_context);
var compiled_mt = Handlebars.compile(mt_html);
mt_html = compiled_mt(modal_context);
$scope.appendModal(mt_html, modal_id);
$scope.modals.editUserApiInfo.push({
apiKey: api_key,
apiQuota: parseInt(api_quota),
userId: user_id,
apiActive: api_active
});
$timeout(function () {
$('#edit-user-api-info-' + user_id).modal('show');
});
};
/*
@ -253,7 +313,8 @@ polr.controller('AdminCtrl', function($scope, $compile) {
apiCall('admin/delete_link', {
'link_ending': link_ending,
}, function(new_status) {
$scope.hideRow(el, 'Link successfully deleted.');
toastr.success('Link successfully deleted.', 'Success');
$scope.reloadLinkTables();
});
};
@ -278,6 +339,18 @@ polr.controller('AdminCtrl', function($scope, $compile) {
});
};
// Edit links' long_url
$scope.editLongLink = function(link_ending, old_long_link) {
$scope.modals.editLongLink.push({
linkEnding: link_ending,
oldLongLink: old_long_link,
});
$timeout(function () {
$('#edit-long-link-' + link_ending).modal('show');
});
}
/*
Initialisation
*/
@ -285,7 +358,6 @@ polr.controller('AdminCtrl', function($scope, $compile) {
// Initialise AdminCtrl
$scope.init = function() {
var modal_source = $("#modal-template").html();
$scope.modal_template = Handlebars.compile(modal_source);
$('.admin-nav a').click(function(e) {
e.preventDefault();

View File

@ -6,7 +6,7 @@ polr.directive('setupTooltip', function() {
replace: true,
template: '<button data-content="{{ content }}" type="button" class="btn btn-xs btn-default setup-qmark" data-toggle="popover">?</button>'
}
})
});
polr.controller('SetupCtrl', function($scope) {
$scope.init = function () {

File diff suppressed because one or more lines are too long

View File

@ -65,11 +65,11 @@
<th></th>
</tr>
<tr id="new-user-form">
<td><input type="text" class="form-control" id="new-username"></td>
<td><input type="password" class="form-control" id="new-user-password"></td>
<td><input type="email" class="form-control" id="new-user-email"></td>
<td><input type="text" class="form-control" ng-model="newUserParams.username"></td>
<td><input type="password" class="form-control" ng-model="newUserParams.userPassword"></td>
<td><input type="email" class="form-control" ng-model="newUserParams.userEmail"></td>
<td>
<select class="form-control new-user-role" id="new-user-role">
<select class="form-control new-user-role" ng-model="newUserParams.userRole">
@foreach ($user_roles as $role_text => $role_val)
<option value="{{$role_val}}">{{$role_text}}</option>
@endforeach
@ -123,6 +123,14 @@
@endif
</div>
</div>
<div class="angular-modals">
<edit-long-link-modal ng-repeat="modal in modals.editLongLink" link-ending="modal.linkEnding"
old-long-link="modal.oldLongLink" clean-modals="cleanModals"></edit-long-link-modal>
<edit-user-api-info-modal ng-repeat="modal in modals.editUserApiInfo" user-id="modal.userId"
api-quota="modal.apiQuota" api-active="modal.apiActive" api-key="modal.apiKey"
generate-new-api-key="generateNewAPIKey" clean-modals="cleanModals"></edit-user-api-info>
</div>
</div>
@ -133,31 +141,7 @@
@include('snippets.modals')
{{-- Include extra JS --}}
<script src='/js/handlebars-v4.0.5.min.js'></script>
<script src='/js/datatables.min.js'></script>
<script src='/js/api.js'></script>
<script src='/js/AdminCtrl.js'></script>
{{-- Extra templating --}}
<script id="api-modal-template" type="text/x-handlebars-template">
<div>
<p>
<span>API Active</span>:
<code class='status-display'>
@{{#if api_active}}True@{{else}}False@{{/if}}</code>
<a ng-click="toggleAPIStatus($event, '@{{user_id}}')" class='btn btn-xs btn-success'>toggle</a>
</p>
<p>
<span>API Key: </span><code class='status-display'>@{{api_key}}</code>
<a ng-click="generateNewAPIKey($event, '@{{user_id}}', false)" class='btn btn-xs btn-danger'>reset</a>
</p>
<p>
<span>API Quota (req/min, -1 for unlimited):</span> <input type='number' class='form-control api-quota' value='@{{api_quota}}'>
<a ng-click="updateAPIQuota($event, '@{{user_id}}')" class='btn btn-xs btn-warning'>change</a>
</p>
</div>
</script>
@endsection

View File

@ -50,13 +50,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
</div>
</div>
{{-- Load header JavaScript --}}
<script src='/js/constants.js'></script>
{{-- Load JavaScript dependencies --}}
<script src="/js/constants.js"></script>
<script src="/js/jquery-1.11.3.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
<script src='/js/angular.min.js'></script>
<script src='/js/toastr.min.js'></script>
<script src='/js/base.js'></script>
<script src="/js/angular.min.js"></script>
<script src="/js/toastr.min.js"></script>
<script src="/js/base.js"></script>
<script>
@if (Session::has('info'))