Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/cydrobolt/polr.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChaoyi Zha <summermontreal@gmail.com>2017-04-30 06:32:22 +0300
committerChaoyi Zha <summermontreal@gmail.com>2017-04-30 06:32:22 +0300
commit6a53d0644bb0abc1bcbc821dbd6f29e82d918cfa (patch)
tree478b52790421159bb6c6da70cc8290689805a524
parent21e465e149282004840d0e9fe9c1ad1b0b41974b (diff)
Implement long link editing WIP; code refactoring
-rw-r--r--app/Http/Controllers/AdminPaginationController.php230
-rw-r--r--app/Http/Controllers/AjaxController.php28
-rw-r--r--app/Http/routes.php1
-rw-r--r--public/css/admin.css4
-rw-r--r--public/directives/editLongLinkModal.html18
-rw-r--r--public/js/AdminCtrl.js84
-rw-r--r--public/js/SetupCtrl.js2
-rw-r--r--resources/views/admin.blade.php5
-rw-r--r--resources/views/layouts/base.blade.php10
9 files changed, 257 insertions, 125 deletions
diff --git a/app/Http/Controllers/AdminPaginationController.php b/app/Http/Controllers/AdminPaginationController.php
index 156bea4..78e290d 100644
--- a/app/Http/Controllers/AdminPaginationController.php
+++ b/app/Http/Controllers/AdminPaginationController.php
@@ -14,75 +14,126 @@ class AdminPaginationController extends Controller {
* @return Response
*/
+ /* Cell rendering functions */
+
+ 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>';
+ }
+
+ public function renderToggleUserActiveCell($user) {
+ // Add user account active state toggle buttons
+ $btn_class = '';
+ if (session('username') === $user->username) {
+ $btn_class = ' disabled';
+ }
+
+ if ($user->active) {
+ $active_text = 'Active';
+ $btn_color_class = ' btn-success';
+ }
+ else {
+ $active_text = 'Inactive';
+ $btn_color_class = ' btn-danger';
+ }
+
+ return '<a class="btn btn-sm status-display' . $btn_color_class . $btn_class . '" ng-click="toggleUserActiveStatus($event, ' . $user->id . ')">' . $active_text . '</a>';
+ }
+
+ public function renderChangeUserRoleCell($user) {
+ // Add "change role" select box
+ // <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) {
+ // Do not allow user to change own role
+ $select_role .= ' disabled';
+ }
+ $select_role .= '>';
+
+ foreach (UserHelper::$USER_ROLES as $role_text => $role_val) {
+ // Iterate over each available role and output option
+ $select_role .= '<option value="' . e($role_val) . '"';
+
+ if ($user->role === $role_val) {
+ $select_role .= ' selected';
+ }
+
+ $select_role .= '>' . e($role_text) . '</option>';
+ }
+
+ $select_role .= '</select>';
+ return $select_role;
+ }
+
+ public function renderToggleLinkActiveCell($link) {
+ // Add "Disable/Enable" action buttons
+ $btn_class = 'btn-danger';
+ $btn_text = 'Disable';
+
+ if ($link->is_disabled) {
+ $btn_class = 'btn-success';
+ $btn_text = 'Enable';
+ }
+
+ return '<a ng-click="toggleLink($event, \'' . e($link->short_url) . '\')" class="btn btn-sm ' . $btn_class . '">
+ ' . $btn_text . '
+ </a>';
+ }
+
+ /* 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', function ($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) {
- // Add user account active state toggle buttons
- $btn_class = '';
- if (session('username') == $user->username) {
- $btn_class = ' disabled';
- }
-
- if ($user->active) {
- $active_text = 'Active';
- $btn_color_class = ' btn-success';
- }
- else {
- $active_text = 'Inactive';
- $btn_color_class = ' btn-danger';
- }
-
- 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) {
- // Add "change role" select box
- // FIXME <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) {
- // Do not allow user to change own role
- $select_role .= ' disabled';
- }
- $select_role .= '>';
-
- foreach (UserHelper::$USER_ROLES as $role_text => $role_val) {
- // Iterate over each available role and output option
- $select_role .= '<option value="' . e($role_val) . '"';
-
- if ($user->role == $role_val) {
- $select_role .= ' selected';
- }
-
- $select_role .= '>' . e($role_text) . '</option>';
- }
-
- $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>';
- })
+ ->addColumn('api_action', [$this, 'renderAdminApiActionCell'])
+ ->addColumn('toggle_active', [$this, 'renderToggleUserActiveCell'])
+ ->addColumn('change_role', [$this, 'renderChangeUserRoleCell'])
+ ->addColumn('delete', [$this, 'renderDeleteUserCell'])
->escapeColumns(['username', 'email'])
->make(true);
}
@@ -92,38 +143,10 @@ class AdminPaginationController extends Controller {
$admin_links = Link::select(['short_url', 'long_url', 'clicks', 'created_at', 'creator', 'is_disabled']);
return Datatables::of($admin_links)
- ->addColumn('disable', function ($link) {
- // Add "Disable/Enable" action buttons
- $btn_class = 'btn-danger';
- $btn_text = 'Disable';
-
- if ($link->is_disabled) {
- $btn_class = 'btn-success';
- $btn_text = 'Enable';
- }
-
- 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;
- }
- })
- ->editColumn('long_url', '<a target="_blank" title="{{ $long_url }}" href="{{ $long_url }}">{{ str_limit($long_url, 50) }}</a>')
+ ->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']) // TODO make sure users can't edit other people's links!
->escapeColumns(['short_url'])
->make(true);
}
diff --git a/app/Http/Controllers/AjaxController.php b/app/Http/Controllers/AjaxController.php
index 0373118..b8f5a4e 100644
--- a/app/Http/Controllers/AjaxController.php
+++ b/app/Http/Controllers/AjaxController.php
@@ -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'); // TODO check if valid
+
+ $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";
+ }
}
diff --git a/app/Http/routes.php b/app/Http/routes.php
index 15c4bfd..7982af5 100644
--- a/app/Http/routes.php
+++ b/app/Http/routes.php
@@ -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']);
diff --git a/public/css/admin.css b/public/css/admin.css
index b718f20..2c9b51f 100644
--- a/public/css/admin.css
+++ b/public/css/admin.css
@@ -54,3 +54,7 @@ input.api-quota {
a.new-user-add {
margin-left: 0.5em
}
+
+.edit-long-link-btn {
+ opacity: 0.45;
+}
diff --git a/public/directives/editLongLinkModal.html b/public/directives/editLongLinkModal.html
new file mode 100644
index 0000000..92c7da4
--- /dev/null
+++ b/public/directives/editLongLinkModal.html
@@ -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" ng-click="cleanModals()">
+ <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 -->
diff --git a/public/js/AdminCtrl.js b/public/js/AdminCtrl.js
index cd0c606..7c96a74 100644
--- a/public/js/AdminCtrl.js
+++ b/public/js/AdminCtrl.js
@@ -1,8 +1,37 @@
-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) {
+ // TODO set a listener on close then delete!
+
+ $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')
+ $scope.cleanModals();
+ }, function(err) {
+ toastr.error('The new URL format is not valid.', 'Error');
+ });
+ };
+ },
+ };
+});
+
+polr.controller('AdminCtrl', function($scope, $compile, $timeout) {
$scope.state = {
showNewUserWell: false
};
$scope.datatables = {};
+ $scope.editLongLinkModals = [];
$scope.syncHash = function() {
var url = document.location.toString();
@@ -11,8 +40,18 @@ polr.controller('AdminCtrl', function($scope, $compile) {
}
};
+ $scope.cleanModals = function() {
+ $timeout(function () {
+ $scope.editLongLinkModals.shift();
+ console.log('cleaning modals!!');
+ console.log($scope.editLongLinkModals);
+ }, 5000);
+
+ $scope.reloadLinkTables();
+ };
+
// Initialise Datatables elements
- $scope.initTables = function () {
+ $scope.initTables = function() {
var datatables_config = {
'autoWidth': false,
'processing': true,
@@ -70,6 +109,20 @@ polr.controller('AdminCtrl', function($scope, $compile) {
}, datatables_config));
};
+ $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);
+ }
+
+ $scope.datatables['user_links_table'].ajax.reload(null, false);
+ };
+
+ $scope.reloadUserTables = function () {
+ $scope.datatables['admin_users_table'].ajax.reload(null, false);
+ };
+
// Append modals to Angular root
$scope.appendModal = function(html, id) {
id = esc_selector(id);
@@ -86,13 +139,6 @@ polr.controller('AdminCtrl', function($scope, $compile) {
});
};
- // Hide table rows
- $scope.hideRow = function(el, msg) {
- var row = el.parent().parent();
- toastr.success(msg, "Success");
- row.fadeOut('slow');
- };
-
/*
User Management
*/
@@ -161,7 +207,8 @@ polr.controller('AdminCtrl', function($scope, $compile) {
apiCall('admin/delete_user', {
'user_id': user_id,
}, function(new_status) {
- $scope.hideRow(el, 'User successfully deleted.');
+ toastr.success('User successfully deleted.', 'Success');
+ $scope.reloadUserTables();
});
};
@@ -253,7 +300,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 +326,20 @@ polr.controller('AdminCtrl', function($scope, $compile) {
});
};
+ // Edit links' long_url
+ $scope.editLongLink = function(link_ending, old_long_link) {
+ $scope.editLongLinkModals.push({
+ linkEnding: link_ending,
+ oldLongLink: old_long_link,
+ });
+
+ $timeout(function () {
+ console.log(link_ending);
+ $('#edit-long-link-' + link_ending).modal('show');
+ // XXX refresh table when done
+ });
+ }
+
/*
Initialisation
*/
diff --git a/public/js/SetupCtrl.js b/public/js/SetupCtrl.js
index c5fced4..61caa50 100644
--- a/public/js/SetupCtrl.js
+++ b/public/js/SetupCtrl.js
@@ -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 () {
diff --git a/resources/views/admin.blade.php b/resources/views/admin.blade.php
index 1940187..3850724 100644
--- a/resources/views/admin.blade.php
+++ b/resources/views/admin.blade.php
@@ -123,6 +123,11 @@
@endif
</div>
</div>
+
+ <div class="angular-modals">
+ <edit-long-link-modal ng-repeat="modal in editLongLinkModals" link-ending="modal.linkEnding"
+ old-long-link="modal.oldLongLink" clean-modals="cleanModals"></edit-long-link-modal>
+ </div>
</div>
diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php
index 85d0ea2..19f910b 100644
--- a/resources/views/layouts/base.blade.php
+++ b/resources/views/layouts/base.blade.php
@@ -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'))