|
|
@@ -14,7 +14,7 @@
|
|
|
## See the License for the specific language governing permissions and
|
|
|
## limitations under the License.
|
|
|
<%!
|
|
|
-from desktop.views import commonheader, commonfooter, antixss, _ko
|
|
|
+from desktop.views import commonheader, commonfooter, antixss
|
|
|
from django.template.defaultfilters import date, time
|
|
|
from django.utils.translation import ugettext as _
|
|
|
%>
|
|
|
@@ -32,40 +32,45 @@ ${layout.menubar(section='users')}
|
|
|
|
|
|
<%actionbar:render>
|
|
|
<%def name="search()">
|
|
|
- <input type="text" class="input-xlarge search-query" placeholder="${_('Search for name, group, etc...')}" data-bind="textInput: filter">
|
|
|
+ <input id="filterInput" type="text" class="input-xlarge search-query"
|
|
|
+ placeholder="${_('Search for name, group, etc...')}">
|
|
|
</%def>
|
|
|
<%def name="actions()">
|
|
|
- <!-- ko if: isSuperUser -->
|
|
|
- <button id="deleteUserBtn" class="btn" title="${_('Delete')}" data-bind="enable: chosenUsers().length > 0"><i class="fa fa-trash-o"></i> ${_('Delete')}</button>
|
|
|
- <!-- /ko -->
|
|
|
+ %if user.is_superuser:
|
|
|
+ <button id="deleteUserBtn" class="btn" title="${_('Delete')}" disabled="disabled"><i
|
|
|
+ class="fa fa-trash-o"></i> ${_('Delete')}</button>
|
|
|
+ %endif
|
|
|
</%def>
|
|
|
<%def name="creation()">
|
|
|
- <!-- ko if: isSuperUser -->
|
|
|
- % if not is_ldap_setup:
|
|
|
- <a href="${ url('useradmin.views.edit_user') }" class="btn"><i class="fa fa-user"></i> ${_('Add user')}</a>
|
|
|
+ %if user.is_superuser:
|
|
|
+ % if not is_ldap_setup:
|
|
|
+ <a href="${ url('useradmin.views.edit_user') }" class="btn"><i class="fa fa-user"></i> ${_('Add user')}</a>
|
|
|
+ %endif
|
|
|
+
|
|
|
+ % if is_ldap_setup:
|
|
|
+ <a href="${ url('useradmin.views.add_ldap_users') }" class="btn"><i
|
|
|
+ class="fa fa-briefcase"></i> ${_('Add/Sync LDAP user')}</a>
|
|
|
+ <a href="javascript:void(0)" class="btn confirmationModal"
|
|
|
+ data-confirmation-url="${ url('useradmin.views.sync_ldap_users_groups') }"><i
|
|
|
+ class="fa fa-refresh"></i> ${_('Sync LDAP users/groups')}</a>
|
|
|
+ % endif
|
|
|
+
|
|
|
+ <a href="http://gethue.com/making-hadoop-accessible-to-your-employees-with-ldap/" class="btn"
|
|
|
+ title="${ ('Learn how to integrate Hue with your company') }" target="_blank">
|
|
|
+ <i class="fa fa-question-circle"></i> LDAP
|
|
|
+ </a>
|
|
|
%endif
|
|
|
-
|
|
|
- % if is_ldap_setup:
|
|
|
- <a href="${ url('useradmin.views.add_ldap_users') }" class="btn"><i class="fa fa-briefcase"></i> ${_('Add/Sync LDAP user')}</a>
|
|
|
- <a href="javascript:void(0)" class="btn confirmationModal" data-confirmation-url="${ url('useradmin.views.sync_ldap_users_groups') }"><i class="fa fa-refresh"></i> ${_('Sync LDAP users/groups')}</a>
|
|
|
- % endif
|
|
|
-
|
|
|
- <a href="http://gethue.com/making-hadoop-accessible-to-your-employees-with-ldap/" class="btn" title="${ ('Learn how to integrate Hue with your company') }" target="_blank">
|
|
|
- <i class="fa fa-question-circle"></i> LDAP
|
|
|
- </a>
|
|
|
- <!-- /ko -->
|
|
|
</%def>
|
|
|
</%actionbar:render>
|
|
|
|
|
|
- <!-- ko ifnot: isLoadingUsers() -->
|
|
|
- <table class="table table-condensed">
|
|
|
+ <table class="table table-condensed datatables">
|
|
|
<thead>
|
|
|
<tr>
|
|
|
- <!-- ko if: isSuperUser -->
|
|
|
- <th width="1%">
|
|
|
- <div class="hueCheckbox fa fa-fw" data-bind="css: {'fa-check': allSelected, 'fa-minus': someSelected}, click: toggleAll"></div>
|
|
|
- </th>
|
|
|
- <!-- /ko -->
|
|
|
+ %if user.is_superuser:
|
|
|
+ <th width="1%">
|
|
|
+ <div id="selectAll" class="hueCheckbox fa"></div>
|
|
|
+ </th>
|
|
|
+ %endif
|
|
|
<th>${_('Username')}</th>
|
|
|
<th>${_('First Name')}</th>
|
|
|
<th>${_('Last Name')}</th>
|
|
|
@@ -74,37 +79,33 @@ ${layout.menubar(section='users')}
|
|
|
<th>${_('Last Login')}</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
- <tbody data-bind="foreach: filteredUsers">
|
|
|
- <tr>
|
|
|
- <!-- ko if: $parent.isSuperUser -->
|
|
|
- <!-- ko if: username() !== '${ user.username }' -->
|
|
|
- <td class="center" data-bind="click: function(){ selected(!selected()) }" style="cursor: default" data-row-selector-exclude="true">
|
|
|
- <div class="hueCheckbox fa" data-bind="css: { 'fa-check': selected }"></div>
|
|
|
- </td>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: username() === '${ user.username }' -->
|
|
|
- <td class="center" style="cursor: default" data-row-selector-exclude="true">
|
|
|
- <div class="hueCheckbox disabled"></div>
|
|
|
- </td>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- /ko -->
|
|
|
+ <tbody>
|
|
|
+ % for listed_user in users:
|
|
|
+ <tr class="tableRow"
|
|
|
+ data-search="${listed_user.username}${listed_user.first_name}${listed_user.last_name}${listed_user.email}${', '.join([group.name for group in listed_user.groups.all()])}">
|
|
|
+ %if user.is_superuser:
|
|
|
+ <td data-row-selector-exclude="true">
|
|
|
+ <div class="hueCheckbox userCheck fa" data-row-selector-exclude="true" data-id="${ listed_user.id }"></div>
|
|
|
+ </td>
|
|
|
+ %endif
|
|
|
<td>
|
|
|
- <!-- ko if: $parent.isSuperUser() || username() == '${user.username}' -->
|
|
|
- <strong><a data-bind="text: username, attr: {'title': '${ _ko('Edit user') } ' + username(), 'href': editURL}, jHueRowSelector"></a></strong>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko if: !$parent.isSuperUser() && username() != '${user.username}' -->
|
|
|
- <strong data-bind="text: username"></strong>
|
|
|
- <!-- /ko -->
|
|
|
+ %if user.is_superuser or user.username == listed_user.username:
|
|
|
+ <strong><a title="${_('Edit %(username)s') % dict(username=listed_user.username)}"
|
|
|
+ href="${ url('useradmin.views.edit_user', username=listed_user.username) }"
|
|
|
+ data-row-selector="true">${listed_user.username}</a></strong>
|
|
|
+ %else:
|
|
|
+ <strong>${listed_user.username}</strong>
|
|
|
+ %endif
|
|
|
</td>
|
|
|
- <td data-bind="text: first_name"></td>
|
|
|
- <td data-bind="text: last_name"></td>
|
|
|
- <td data-bind="text: email"></td>
|
|
|
- <td data-bind="text: readableGroups"></td>
|
|
|
- <td data-bind="text: localeFormat(last_login)"></td>
|
|
|
- </tr>
|
|
|
+ <td>${listed_user.first_name}</td>
|
|
|
+ <td>${listed_user.last_name}</td>
|
|
|
+ <td>${listed_user.email}</td>
|
|
|
+ <td>${', '.join([group.name for group in listed_user.groups.all()])}</td>
|
|
|
+ <td>${date(listed_user.last_login)} ${time(listed_user.last_login).replace("p.m.","PM").replace("a.m.","AM")}</td>
|
|
|
+ </tr>
|
|
|
+ % endfor
|
|
|
</tbody>
|
|
|
- <!-- ko if: filteredUsers().length === 0 -->
|
|
|
- <tfoot>
|
|
|
+ <tfoot class="hide">
|
|
|
<tr>
|
|
|
<td colspan="8">
|
|
|
<div class="alert">
|
|
|
@@ -113,10 +114,7 @@ ${layout.menubar(section='users')}
|
|
|
</td>
|
|
|
</tr>
|
|
|
</tfoot>
|
|
|
- <!-- /ko -->
|
|
|
</table>
|
|
|
- <!-- /ko -->
|
|
|
- <!-- ko hueSpinner: { spin: isLoadingUsers, center: true, size: 'xlarge' } --><!-- /ko -->
|
|
|
</div>
|
|
|
|
|
|
<div id="syncLdap" class="modal hide fade"></div>
|
|
|
@@ -134,7 +132,8 @@ ${layout.menubar(section='users')}
|
|
|
<input type="submit" class="btn btn-danger" value="${_('Yes')}"/>
|
|
|
</div>
|
|
|
<div class="hide">
|
|
|
- <select name="user_ids" data-bind="options: availableUsers().map(function(item){ return item.id() }), selectedOptions: chosenUsers().map(function(item){ return item.id() })" multiple="true"></select>
|
|
|
+ <select name="user_ids" data-bind="options: availableUsers, selectedOptions: chosenUsers"
|
|
|
+ multiple="true"></select>
|
|
|
</div>
|
|
|
</form>
|
|
|
</div>
|
|
|
@@ -144,91 +143,45 @@ ${layout.menubar(section='users')}
|
|
|
<script src="${ static('desktop/ext/js/datatables-paging-0.1.js') }" type="text/javascript" charset="utf-8"></script>
|
|
|
|
|
|
<script type="text/javascript" charset="utf-8">
|
|
|
- $(document).ready(function () {
|
|
|
-
|
|
|
- var ListUsersViewModel = function () {
|
|
|
- var self = this;
|
|
|
-
|
|
|
- self.filter = ko.observable('').extend({throttle: 500});
|
|
|
-
|
|
|
- self.isSuperUser = ko.observable(${ user.is_superuser and 'true' or 'false' })
|
|
|
- self.availableUsers = ko.observableArray([]);
|
|
|
-
|
|
|
- self.filteredUsers = ko.pureComputed(function () {
|
|
|
- return self.availableUsers().filter(function (item) {
|
|
|
- return item.username().toLowerCase().indexOf(self.filter()) > -1 ||
|
|
|
- item.first_name().toLowerCase().indexOf(self.filter()) > -1 ||
|
|
|
- item.last_name().toLowerCase().indexOf(self.filter()) > -1 ||
|
|
|
- item.email().toLowerCase().indexOf(self.filter()) > -1 ||
|
|
|
- item.readableGroups().toLowerCase().indexOf(self.filter()) > -1
|
|
|
- });
|
|
|
- });
|
|
|
-
|
|
|
- self.chosenUsers = ko.pureComputed(function () {
|
|
|
- return self.filteredUsers().filter(function (item) {
|
|
|
- return item.selected()
|
|
|
- });
|
|
|
- });
|
|
|
+ var $usersComponents = $('#usersComponents');
|
|
|
|
|
|
- self.toggleAll = function () {
|
|
|
- var selected = self.allSelected();
|
|
|
- ko.utils.arrayForEach(self.filteredUsers(), function (user) {
|
|
|
- if (user.username() !== '${ user.username }') {
|
|
|
- user.selected(!selected);
|
|
|
- }
|
|
|
- else {
|
|
|
- user.selected(false);
|
|
|
- }
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- self.allSelected = ko.pureComputed(function () {
|
|
|
- var filtered = self.filteredUsers().filter(function (item) {
|
|
|
- return item.selected()
|
|
|
- });
|
|
|
- return filtered.length > 0 && filtered.length === self.filteredUsers().length - 1
|
|
|
- });
|
|
|
-
|
|
|
- self.someSelected = ko.pureComputed(function () {
|
|
|
- var filtered = self.filteredUsers().filter(function (item) {
|
|
|
- return item.selected()
|
|
|
- });
|
|
|
- return filtered.length > 0 && filtered.length < self.filteredUsers().length - 1
|
|
|
- });
|
|
|
-
|
|
|
- self.isLoadingUsers = ko.observable(true);
|
|
|
+ var viewModel = {
|
|
|
+ availableUsers: ko.observableArray(${ users_json | n,antixss }),
|
|
|
+ chosenUsers: ko.observableArray([])
|
|
|
+ };
|
|
|
+ var mainDataTable;
|
|
|
+ $(document).ready(function () {
|
|
|
|
|
|
- self.init = function () {
|
|
|
- $.getJSON('/useradmin/users?format=json', function (data) {
|
|
|
- self.isLoadingUsers(false);
|
|
|
- if (data && data.users) {
|
|
|
- var users = [];
|
|
|
- data.users.forEach(function (u) {
|
|
|
- var user = ko.mapping.fromJS(u);
|
|
|
- user.selected = ko.observable(false);
|
|
|
- user.readableGroups = ko.pureComputed(function () {
|
|
|
- return u.groups ? user.groups().map(function (item) {
|
|
|
- return item.name()
|
|
|
- }).join(', ') : ''
|
|
|
- });
|
|
|
- users.push(user);
|
|
|
- })
|
|
|
- self.availableUsers(users);
|
|
|
- }
|
|
|
- else {
|
|
|
- $.jHueNotify.error('${ _('There was an error while loading users.') }')
|
|
|
- }
|
|
|
- });
|
|
|
+ ko.applyBindings(viewModel, $usersComponents[0]);
|
|
|
+
|
|
|
+ mainDataTable = $usersComponents.find('.datatables').dataTable({
|
|
|
+ "sPaginationType":"bootstrap",
|
|
|
+ "iDisplayLength":100,
|
|
|
+ "bLengthChange":false,
|
|
|
+ "sDom": "<'row'r>t<'row-fluid'<'dt-pages'p><'dt-records'i>>",
|
|
|
+ "bInfo": false,
|
|
|
+ "bFilter": true,
|
|
|
+ "aoColumns": [
|
|
|
+ %if user.is_superuser:
|
|
|
+ { "bSortable": false },
|
|
|
+ %endif
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ null,
|
|
|
+ { "sType": "date" },
|
|
|
+ ],
|
|
|
+ "oLanguage": {
|
|
|
+ "sEmptyTable": "${_('No data available')}",
|
|
|
+ "sZeroRecords": "${_('No matching records')}",
|
|
|
}
|
|
|
- };
|
|
|
-
|
|
|
- var viewModel = new ListUsersViewModel();
|
|
|
-
|
|
|
- viewModel.init();
|
|
|
+ });
|
|
|
|
|
|
- ko.applyBindings(viewModel, $('#usersComponents')[0]);
|
|
|
+ $usersComponents.find(".dataTables_wrapper").css("min-height", "0");
|
|
|
+ $usersComponents.find(".dataTables_filter").hide();
|
|
|
|
|
|
- $(".confirmationModal").click(function () {
|
|
|
+ $usersComponents.find(".confirmationModal").click(function () {
|
|
|
var _this = $(this);
|
|
|
$.ajax({
|
|
|
url: _this.data("confirmation-url"),
|
|
|
@@ -243,10 +196,48 @@ ${layout.menubar(section='users')}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
- $("#deleteUserBtn").click(function () {
|
|
|
- $("#deleteUser").modal("show");
|
|
|
+ $usersComponents.find("#selectAll").click(function () {
|
|
|
+ if ($(this).attr("checked")) {
|
|
|
+ $(this).removeAttr("checked").removeClass("fa-check");;
|
|
|
+ $usersComponents.find(".userCheck").removeClass("fa-check").removeAttr("checked");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ $(this).attr("checked", "checked").addClass("fa-check");
|
|
|
+ $usersComponents.find(".userCheck").addClass("fa-check").attr("checked", "checked");
|
|
|
+ }
|
|
|
+ toggleActions();
|
|
|
+ });
|
|
|
+
|
|
|
+ $(document).on('click', '#usersComponents .userCheck', function () {
|
|
|
+ if ($(this).attr("checked")) {
|
|
|
+ $(this).removeClass("fa-check").removeAttr("checked");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ $(this).addClass("fa-check").attr("checked", "checked");
|
|
|
+ }
|
|
|
+ toggleActions();
|
|
|
+ });
|
|
|
+
|
|
|
+ function toggleActions() {
|
|
|
+ if ($usersComponents.find(".userCheck[checked='checked']").length >= 1) {
|
|
|
+ $usersComponents.find("#deleteUserBtn").removeAttr("disabled");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ $usersComponents.find("#deleteUserBtn").attr("disabled", "disabled");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $usersComponents.find("#deleteUserBtn").click(function () {
|
|
|
+ viewModel.chosenUsers.removeAll();
|
|
|
+
|
|
|
+ $usersComponents.find(".hueCheckbox[checked='checked']").each(function (index) {
|
|
|
+ viewModel.chosenUsers.push($(this).data("id"));
|
|
|
+ });
|
|
|
+
|
|
|
+ $usersComponents.find("#deleteUser").modal("show");
|
|
|
});
|
|
|
|
|
|
+ $usersComponents.find("a[data-row-selector='true']").jHueRowSelector();
|
|
|
});
|
|
|
</script>
|
|
|
|