|
|
@@ -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
|
|
|
+from desktop.views import commonheader, commonfooter, antixss, _ko
|
|
|
from django.template.defaultfilters import date, time
|
|
|
from django.utils.translation import ugettext as _
|
|
|
%>
|
|
|
@@ -30,45 +30,39 @@ ${layout.menubar(section='users')}
|
|
|
|
|
|
<%actionbar:render>
|
|
|
<%def name="search()">
|
|
|
- <input id="filterInput" type="text" class="input-xlarge search-query"
|
|
|
- placeholder="${_('Search for name, group, etc...')}">
|
|
|
+ <input type="text" class="input-xlarge search-query" placeholder="${_('Search for name, group, etc...')}" data-bind="textInput: filter">
|
|
|
</%def>
|
|
|
<%def name="actions()">
|
|
|
- %if user.is_superuser:
|
|
|
- <button id="deleteUserBtn" class="btn" title="${_('Delete')}" disabled="disabled"><i
|
|
|
- class="fa fa-trash-o"></i> ${_('Delete')}</button>
|
|
|
- %endif
|
|
|
+ <!-- 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 -->
|
|
|
</%def>
|
|
|
<%def name="creation()">
|
|
|
- %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>
|
|
|
+ <!-- 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>
|
|
|
%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>
|
|
|
|
|
|
- <table class="table table-condensed datatables">
|
|
|
+ <table class="table table-condensed">
|
|
|
<thead>
|
|
|
<tr>
|
|
|
- %if user.is_superuser:
|
|
|
- <th width="1%">
|
|
|
- <div id="selectAll" class="hueCheckbox fa"></div>
|
|
|
- </th>
|
|
|
- %endif
|
|
|
+ <!-- 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 -->
|
|
|
<th>${_('Username')}</th>
|
|
|
<th>${_('First Name')}</th>
|
|
|
<th>${_('Last Name')}</th>
|
|
|
@@ -77,33 +71,37 @@ ${layout.menubar(section='users')}
|
|
|
<th>${_('Last Login')}</th>
|
|
|
</tr>
|
|
|
</thead>
|
|
|
- <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
|
|
|
+ <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 -->
|
|
|
<td>
|
|
|
- %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
|
|
|
+ <!-- ko if: $parent.isSuperUser() || username() == '${user.username}' -->
|
|
|
+ <strong><a data-bind="text: username, attr: {'title': '${ _ko('Edit user') } ' + username(), 'href': editURL}" data-row-selector="true"></a></strong>
|
|
|
+ <!-- /ko -->
|
|
|
+ <!-- ko if: !$parent.isSuperUser() && username() != '${user.username}' -->
|
|
|
+ <strong data-bind="text: username"></strong>
|
|
|
+ <!-- /ko -->
|
|
|
</td>
|
|
|
- <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
|
|
|
+ <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>
|
|
|
</tbody>
|
|
|
- <tfoot class="hide">
|
|
|
+ <!-- ko if: filteredUsers().length === 0 -->
|
|
|
+ <tfoot>
|
|
|
<tr>
|
|
|
<td colspan="8">
|
|
|
<div class="alert">
|
|
|
@@ -112,6 +110,7 @@ ${layout.menubar(section='users')}
|
|
|
</td>
|
|
|
</tr>
|
|
|
</tfoot>
|
|
|
+ <!-- /ko -->
|
|
|
</table>
|
|
|
</div>
|
|
|
|
|
|
@@ -130,8 +129,7 @@ ${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, selectedOptions: chosenUsers"
|
|
|
- multiple="true"></select>
|
|
|
+ <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>
|
|
|
</div>
|
|
|
</form>
|
|
|
</div>
|
|
|
@@ -141,41 +139,83 @@ ${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">
|
|
|
- var viewModel = {
|
|
|
- availableUsers: ko.observableArray(${ users_json | n,antixss }),
|
|
|
- chosenUsers: ko.observableArray([])
|
|
|
- };
|
|
|
- var mainDataTable;
|
|
|
$(document).ready(function () {
|
|
|
|
|
|
- ko.applyBindings(viewModel);
|
|
|
+ var ListUsersViewModel = function () {
|
|
|
+ var self = this;
|
|
|
|
|
|
- mainDataTable = $(".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')}",
|
|
|
+ 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()
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ 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.init = function () {
|
|
|
+ $.getJSON('/useradmin/users', function (data) {
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
- });
|
|
|
+ };
|
|
|
|
|
|
- $(".dataTables_wrapper").css("min-height", "0");
|
|
|
- $(".dataTables_filter").hide();
|
|
|
+ var viewModel = new ListUsersViewModel();
|
|
|
+
|
|
|
+ viewModel.init();
|
|
|
+
|
|
|
+ ko.applyBindings(viewModel);
|
|
|
|
|
|
$(".confirmationModal").click(function () {
|
|
|
var _this = $(this);
|
|
|
@@ -192,44 +232,7 @@ ${layout.menubar(section='users')}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
- $("#selectAll").click(function () {
|
|
|
- if ($(this).attr("checked")) {
|
|
|
- $(this).removeAttr("checked").removeClass("fa-check");;
|
|
|
- $(".userCheck").removeClass("fa-check").removeAttr("checked");
|
|
|
- }
|
|
|
- else {
|
|
|
- $(this).attr("checked", "checked").addClass("fa-check");
|
|
|
- $(".userCheck").addClass("fa-check").attr("checked", "checked");
|
|
|
- }
|
|
|
- toggleActions();
|
|
|
- });
|
|
|
-
|
|
|
- $(document).on('click', '.userCheck', function () {
|
|
|
- if ($(this).attr("checked")) {
|
|
|
- $(this).removeClass("fa-check").removeAttr("checked");
|
|
|
- }
|
|
|
- else {
|
|
|
- $(this).addClass("fa-check").attr("checked", "checked");
|
|
|
- }
|
|
|
- toggleActions();
|
|
|
- });
|
|
|
-
|
|
|
- function toggleActions() {
|
|
|
- if ($(".userCheck[checked='checked']").length >= 1) {
|
|
|
- $("#deleteUserBtn").removeAttr("disabled");
|
|
|
- }
|
|
|
- else {
|
|
|
- $("#deleteUserBtn").attr("disabled", "disabled");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
$("#deleteUserBtn").click(function () {
|
|
|
- viewModel.chosenUsers.removeAll();
|
|
|
-
|
|
|
- $(".hueCheckbox[checked='checked']").each(function (index) {
|
|
|
- viewModel.chosenUsers.push($(this).data("id"));
|
|
|
- });
|
|
|
-
|
|
|
$("#deleteUser").modal("show");
|
|
|
});
|
|
|
|