Selaa lähdekoodia

HUE-803 The useradmin module needs some UI enhancements

Changed filter bar to look and behave like Filebrowser
Improved jHueSelector search results look, added height parameter, improved behavior for empty lists
Enrico Berti 13 vuotta sitten
vanhempi
commit
bb06548f4d

+ 10 - 4
apps/useradmin/src/useradmin/templates/edit_group.mako

@@ -53,6 +53,8 @@ ${layout.menubar(section='groups', _=_)}
 		% endif
 		% endif
 	% endif
 	% endif
 
 
+    <br/>
+
 	<form id="editForm" action="${urllib.quote(action)}" method="POST" class="form form-horizontal">
 	<form id="editForm" action="${urllib.quote(action)}" method="POST" class="form form-horizontal">
 		<fieldset>
 		<fieldset>
 		% for field in form:
 		% for field in form:
@@ -78,14 +80,18 @@ ${layout.menubar(section='groups', _=_)}
 <script type="text/javascript" charset="utf-8">
 <script type="text/javascript" charset="utf-8">
 	$(document).ready(function(){
 	$(document).ready(function(){
 		$("#id_members").jHueSelector({
 		$("#id_members").jHueSelector({
-            width:400,
             selectAllLabel: "${_('Select all')}",
             selectAllLabel: "${_('Select all')}",
-            searchPlaceholder: "${_('Search')}"
+            searchPlaceholder: "${_('Search')}",
+            noChoicesFound: "${_('No users found.')}",
+            width:600,
+            height:240
         });
         });
 		$("#id_permissions").jHueSelector({
 		$("#id_permissions").jHueSelector({
-            width:400,
             selectAllLabel: "${_('Select all')}",
             selectAllLabel: "${_('Select all')}",
-            searchPlaceholder: "${_('Search')}"
+            searchPlaceholder: "${_('Search')}",
+            noChoicesFound: "${_('No permissions found.')}",
+            width:600,
+            height:240
         });
         });
 	});
 	});
 </script>
 </script>

+ 4 - 2
apps/useradmin/src/useradmin/templates/edit_permissions.mako

@@ -58,9 +58,11 @@ ${layout.menubar(section='permissions', _=_)}
 <script type="text/javascript" charset="utf-8">
 <script type="text/javascript" charset="utf-8">
 	$(document).ready(function(){
 	$(document).ready(function(){
 		$("#id_groups").jHueSelector({
 		$("#id_groups").jHueSelector({
-            width:520,
             selectAllLabel: "${_('Select all')}",
             selectAllLabel: "${_('Select all')}",
-            searchPlaceholder: "${_('Search')}"
+            searchPlaceholder: "${_('Search')}",
+            noChoicesFound: "${_('No groups found.')}",
+            width:600,
+            height:240
         });
         });
 	});
 	});
 </script>
 </script>

+ 29 - 2
apps/useradmin/src/useradmin/templates/edit_user.mako

@@ -50,10 +50,34 @@ ${layout.menubar(section='users', _=_)}
 		% endif
 		% endif
 	% endif
 	% endif
 
 
+    <br/>
+
 	<form id="editForm" action="${urllib.quote(action)}" method="POST" class="form form-horizontal">
 	<form id="editForm" action="${urllib.quote(action)}" method="POST" class="form form-horizontal">
 		<fieldset>
 		<fieldset>
 			% for field in form:
 			% for field in form:
-				${render_field(field)}
+                %if field.name == "first_name":
+                    <div class="row">
+                        <div class="span5">
+                        ${render_field(form["first_name"])}
+                        </div>
+                        <div class="span4">
+                        ${render_field(form["last_name"])}
+                        </div>
+                    </div>
+                %elif field.name == "password1":
+                    <div class="row">
+                        <div class="span5">
+                        ${render_field(form["password1"])}
+                        </div>
+                        <div class="span4">
+                        ${render_field(form["password2"])}
+                        </div>
+                    </div>
+                %elif field.name == "last_name" or field.name == "password2":
+                    ## skip rendering
+                %else:
+				    ${render_field(field)}
+                %endif
 			% endfor
 			% endfor
 		</fieldset>
 		</fieldset>
 		<br/>
 		<br/>
@@ -75,7 +99,10 @@ ${layout.menubar(section='users', _=_)}
 	$(document).ready(function(){
 	$(document).ready(function(){
 		$("#id_groups").jHueSelector({
 		$("#id_groups").jHueSelector({
             selectAllLabel: "${_('Select all')}",
             selectAllLabel: "${_('Select all')}",
-            searchPlaceholder: "${_('Search')}"
+            searchPlaceholder: "${_('Search')}",
+            noChoicesFound: "${_('No groups found.')}",
+            width:618,
+            height:240
         });
         });
 	});
 	});
 </script>
 </script>

+ 42 - 0
apps/useradmin/src/useradmin/templates/layout.mako

@@ -37,3 +37,45 @@ def is_selected(section, matcher):
 	</div>
 	</div>
 </%def>
 </%def>
 
 
+<%def name="commons()">
+    <style type="text/css">
+        .fixed {
+            position: fixed;
+            top: 80px;
+            filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff2f2f2', GradientType=0);
+            -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+            -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+            box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+        }
+        .pull-right {
+            margin: 4px;
+        }
+        .sortable {
+            cursor: pointer;
+        }
+        .file-row {
+            height:37px;
+        }
+    </style>
+    <script type="text/javascript">
+        $(document).ready(function(){
+            $("#filterInput").keyup(function(){
+                var shown = 0;
+                $(".datatables tfoot").hide();
+                $.each($(".tableRow"), function(index, value) {
+                    if($(value).data("search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
+                        $(value).hide();
+                    }
+                    else{
+                        $(value).show();
+                        shown++;
+                    }
+                });
+                if (shown == 0){
+                    $(".datatables tfoot").show();
+                }
+            });
+        });
+    </script>
+</%def>
+

+ 63 - 34
apps/useradmin/src/useradmin/templates/list_groups.mako

@@ -26,43 +26,63 @@ ${layout.menubar(section='groups', _=_)}
 
 
 <div class="container-fluid">
 <div class="container-fluid">
     <h1>${_('Hue Groups')}</h1>
     <h1>${_('Hue Groups')}</h1>
-    <div class="well hueWell">
-        <div class="pull-right btn-group">
-            %if user.is_superuser == True:
-            <a id="addGroupBtn" href="${url('useradmin.views.edit_group')}" class="btn">${_('Add group')}</a>
-            <a id="addLdapGroupBtn" href="${url('useradmin.views.add_ldap_group')}" class="btn">${_('Add/Sync LDAP group')}</a>
-            %endif
+    <div class="subnavContainer">
+        <div class="subnav sticky">
+            <p class="pull-right">
+                <input id="filterInput" type="text" class="input-xlarge search-query" placeholder="${_('Search for group name, members, etc...')}">
+            </p>
+            <p style="padding: 4px">
+                %if user.is_superuser:
+                    <button id="deleteGroupBtn" class="btn confirmationModal" title="${_('Delete')}" disabled="disabled"><i class="icon-trash"></i> ${_('Delete')}</button>
+                    &nbsp;&nbsp;&nbsp;&nbsp;
+                    <a id="addGroupBtn" href="${url('useradmin.views.edit_group')}" class="btn"><i class="icon-plus-sign"></i> ${_('Add group')}</a>
+                    <a id="addLdapGroupBtn" href="${url('useradmin.views.add_ldap_group')}" class="btn"><i class="icon-refresh"></i> ${_('Add/Sync LDAP group')}</a>
+                    &nbsp;&nbsp;&nbsp;&nbsp;
+                %endif
+            </p>
         </div>
         </div>
-        <form class="form-search">
-            ${_('Filter: ')}<input type="text" id="filterInput" class="input-xxlarge search-query" placeholder="${_('Search for group name, members, etc...')}">
-        </form>
     </div>
     </div>
+    <br/>
     <table class="table table-striped table-condensed datatables">
     <table class="table table-striped table-condensed datatables">
     <thead>
     <thead>
       <tr>
       <tr>
+        %if user.is_superuser:
+          <th width="1%"><input id="selectAll" type="checkbox"/></th>
+        %endif
         <th>${_('Group Name')}</th>
         <th>${_('Group Name')}</th>
         <th>${_('Members')}</th>
         <th>${_('Members')}</th>
         <th>${_('Permissions')}</th>
         <th>${_('Permissions')}</th>
-        %if user.is_superuser == True:
-        <th>&nbsp;</th>
-        %endif
       </tr>
       </tr>
     </thead>
     </thead>
     <tbody>
     <tbody>
     % for group in groups:
     % for group in groups:
-      <tr class="groupRow" data-search="${group.name}${', '.join([group_user.username for group_user in group.user_set.all()])}">
-        <td>${group.name}</td>
+      <tr class="tableRow" data-search="${group.name}${', '.join([group_user.username for group_user in group.user_set.all()])}">
+        %if user.is_superuser:
+          <td class="center" data-row-selector-exclude="true">
+            <input type="checkbox" class="groupCheck" data-group="${group.name}" data-confirmation-url="${ url('useradmin.views.delete_group', name=urllib.quote_plus(group.name)) }" data-row-selector-exclude="true"/>
+          </td>
+        %endif
+        <td>
+            %if user.is_superuser:
+            <h5><a title="${_('Edit %(groupname)s') % dict(groupname=group.name)}" href="${ url('useradmin.views.edit_group', name=urllib.quote(group.name)) }" data-row-selector="true">${group.name}</a></h5>
+            %else:
+            <h5>${group.name}</h5>
+            %endif
+         </td>
         <td>${', '.join([group_user.username for group_user in group.user_set.all()])}</td>
         <td>${', '.join([group_user.username for group_user in group.user_set.all()])}</td>
         <td>${', '.join([perm.app + "." + perm.action for perm in group_permissions(group)])}</td>
         <td>${', '.join([perm.app + "." + perm.action for perm in group_permissions(group)])}</td>
-        %if user.is_superuser == True:
-        <td class="right">
-          <a title="${_('Edit %(groupname)s') % dict(groupname=group.name)}" class="btn small editGroupBtn" href="${ url('useradmin.views.edit_group', name=urllib.quote(group.name)) }" data-row-selector="true">${_('Edit')}</a>
-          <a title="${_('Delete %(groupname)s') % dict(groupname=group.name)}" class="btn small confirmationModal" alt="${ _('Are you sure you want to delete %(group_name)s?') % dict(group_name=group.name) }" href="javascript:void(0)" data-confirmation-url="${ url('useradmin.views.delete_group', name=urllib.quote_plus(group.name)) }">${_('Delete')}</a>
-        </td>
-        %endif
       </tr>
       </tr>
     % endfor
     % endfor
     </tbody>
     </tbody>
+    <tfoot class="hide">
+      <tr>
+        <td colspan="8">
+          <div class="alert">
+            ${_('There are no groups matching the search criteria.')}
+          </div>
+        </td>
+      </tr>
+    </tfoot>
     </table>
     </table>
 </div>
 </div>
 
 
@@ -80,12 +100,12 @@ ${layout.menubar(section='groups', _=_)}
                 "bFilter": false,
                 "bFilter": false,
                 "bAutoWidth": false,
                 "bAutoWidth": false,
                 "aoColumns": [
                 "aoColumns": [
+                    %if user.is_superuser:
+                    { "bSortable": false },
+                    %endif
                     { "sWidth": "20%" },
                     { "sWidth": "20%" },
                     { "sWidth": "20%" },
                     { "sWidth": "20%" },
-                    null,
-                    %if user.is_superuser == True:
-                    { "sWidth": "140px", "bSortable": false },
-                    %endif
+                    null
                  ]
                  ]
             });
             });
             $(".dataTables_wrapper").css("min-height","0");
             $(".dataTables_wrapper").css("min-height","0");
@@ -94,7 +114,7 @@ ${layout.menubar(section='groups', _=_)}
             $(".confirmationModal").click(function(){
             $(".confirmationModal").click(function(){
                 var _this = $(this);
                 var _this = $(this);
                 $.ajax({
                 $.ajax({
-                    url: _this.attr("data-confirmation-url"),
+                    url: _this.data("confirmation-url"),
                     beforeSend: function(xhr){
                     beforeSend: function(xhr){
                         xhr.setRequestHeader("X-Requested-With", "Hue");
                         xhr.setRequestHeader("X-Requested-With", "Hue");
                     },
                     },
@@ -106,20 +126,29 @@ ${layout.menubar(section='groups', _=_)}
                 });
                 });
             });
             });
 
 
-            $("#filterInput").keyup(function(){
-                $.each($(".groupRow"), function(index, value) {
-
-                  if($(value).data("search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
-                    $(value).hide(250);
-                  }else{
-                    $(value).show(250);
-                  }
-                });
+            $("#selectAll").change(function(){
+                if ($(this).is(":checked")){
+                    $(".groupCheck").attr("checked", "checked");
+                }
+                else {
+                    $(".groupCheck").removeAttr("checked");
+                }
+                $(".groupCheck").change();
+            });
 
 
+            $(".groupCheck").change(function(){
+                if ($(".groupCheck:checked").length == 1){
+                    $("#deleteGroupBtn").removeAttr("disabled").data("confirmation-url", $(".groupCheck:checked").data("confirmation-url"));
+                }
+                else {
+                    $("#deleteGroupBtn").attr("disabled", "disabled");
+                }
             });
             });
 
 
             $("a[data-row-selector='true']").jHueRowSelector();
             $("a[data-row-selector='true']").jHueRowSelector();
         });
         });
     </script>
     </script>
 
 
+${layout.commons()}
+
 ${commonfooter(messages)}
 ${commonfooter(messages)}

+ 27 - 30
apps/useradmin/src/useradmin/templates/list_permissions.mako

@@ -27,36 +27,46 @@ ${layout.menubar(section='permissions', _=_)}
 
 
 <div class="container-fluid">
 <div class="container-fluid">
     <h1>${_('Hue Permissions')}</h1>
     <h1>${_('Hue Permissions')}</h1>
-    <div class="well hueWell">
-        <form class="form-search">
-                ${_('Filter: ')}<input type="text" id="filterInput" class="input-xxlarge search-query" placeholder="${_('Search for application name, description, etc...')}">
-        </form>
+    <div class="subnavContainer">
+        <div class="subnav sticky">
+            <p class="pull-right">
+                <input id="filterInput" type="text" class="input-xlarge search-query" placeholder="${_('Search for application name, description, etc...')}">
+            </p>
+        </div>
     </div>
     </div>
+    <br/>
     <table class="table table-striped table-condensed datatables">
     <table class="table table-striped table-condensed datatables">
         <thead>
         <thead>
         <tr>
         <tr>
             <th>${_('Application')}</th>
             <th>${_('Application')}</th>
             <th>${_('Permission')}</th>
             <th>${_('Permission')}</th>
             <th>${_('Groups')}</th>
             <th>${_('Groups')}</th>
-            %if user.is_superuser:
-                <th>&nbsp;</th>
-            %endif
         </tr>
         </tr>
         </thead>
         </thead>
         <tbody>
         <tbody>
             % for perm in permissions:
             % for perm in permissions:
-            <tr class="permissionRow" data-search="${perm.app}${perm.description}${', '.join([group.name for group in Group.objects.filter(grouppermission__hue_permission=perm).order_by('name')])}">
-                <td>${perm.app}</td>
+            <tr class="tableRow" data-search="${perm.app}${perm.description}${', '.join([group.name for group in Group.objects.filter(grouppermission__hue_permission=perm).order_by('name')])}">
+                <td>
+                %if user.is_superuser:
+                    <h5><a title="${_('Edit permission')}" href="${ url('useradmin.views.edit_permission', app=urllib.quote(perm.app), priv=urllib.quote(perm.action)) }" data-name="${perm.app}" data-row-selector="true">${perm.app}</a></h5>
+                %else:
+                    <h5>${perm.app}</h5>
+                %endif
+                </td>
                 <td>${perm.description}</td>
                 <td>${perm.description}</td>
                 <td>${', '.join([group.name for group in Group.objects.filter(grouppermission__hue_permission=perm).order_by('name')])}</td>
                 <td>${', '.join([group.name for group in Group.objects.filter(grouppermission__hue_permission=perm).order_by('name')])}</td>
-            %if user.is_superuser:
-                <td class="right">
-                    <a title="${_('Edit permission')}" class="btn small editPermissionBtn" href="${ url('useradmin.views.edit_permission', app=urllib.quote(perm.app), priv=urllib.quote(perm.action)) }" data-name="${perm.app}" data-row-selector="true">${_('Edit')}</a>
-                </td>
-            %endif
             </tr>
             </tr>
             % endfor
             % endfor
         </tbody>
         </tbody>
+        <tfoot class="hide">
+        <tr>
+            <td colspan="3">
+                <div class="alert">
+                    ${_('There are no permissions matching the search criteria.')}
+                </div>
+            </td>
+        </tr>
+        </tfoot>
     </table>
     </table>
 </div>
 </div>
 
 
@@ -70,29 +80,16 @@ ${layout.menubar(section='permissions', _=_)}
             "aoColumns": [
             "aoColumns": [
                 null,
                 null,
                 null,
                 null,
-                %if user.is_superuser:
-                    null,
-                    { "bSortable": false }
-                %else:
-                    null
-                %endif
+                null
             ]
             ]
         });
         });
         $(".dataTables_wrapper").css("min-height","0");
         $(".dataTables_wrapper").css("min-height","0");
         $(".dataTables_filter").hide();
         $(".dataTables_filter").hide();
 
 
-        $("#filterInput").keyup(function(){
-            $.each($(".permissionRow"), function(index, value) {
-                if($(value).data("search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
-                    $(value).hide(250);
-                }else{
-                    $(value).show(250);
-                }
-            });
-        });
-
         $("a[data-row-selector='true']").jHueRowSelector();
         $("a[data-row-selector='true']").jHueRowSelector();
     });
     });
 </script>
 </script>
 
 
+${layout.commons()}
+
 ${commonfooter(messages)}
 ${commonfooter(messages)}

+ 72 - 46
apps/useradmin/src/useradmin/templates/list_users.mako

@@ -26,58 +26,71 @@ ${layout.menubar(section='users', _=_)}
 
 
 <div class="container-fluid">
 <div class="container-fluid">
     <h1>${_('Hue Users')}</h1>
     <h1>${_('Hue Users')}</h1>
-    <div class="well hueWell">
-        <div class="btn-group pull-right">
-            %if user.is_superuser == True:
-            <a href="${ url('useradmin.views.edit_user') }" class="btn">${_('Add user')}</a>
-            <a href="${ url('useradmin.views.add_ldap_user') }" class="btn">${_('Add/Sync LDAP user')}</a>
-            <a href="javascript:void(0)" class="btn confirmationModal" data-confirmation-url="${ url('useradmin.views.sync_ldap_users_groups') }">${_('Sync LDAP users/groups')}</a>
-            %endif
+    <div class="subnavContainer">
+        <div class="subnav sticky">
+            <p class="pull-right">
+                <input id="filterInput" type="text" class="input-xlarge search-query" placeholder="${_('Search for username, name, e-mail, etc...')}">
+            </p>
+            <p style="padding: 4px">
+                %if user.is_superuser:
+                    <button id="deleteUserBtn" class="btn confirmationModal" title="${_('Delete')}" disabled="disabled"><i class="icon-trash"></i> ${_('Delete')}</button>
+                    &nbsp;&nbsp;&nbsp;&nbsp;
+                    <a href="${ url('useradmin.views.edit_user') }" class="btn"><i class="icon-user"></i> ${_('Add user')}</a>
+                    <a href="${ url('useradmin.views.add_ldap_user') }" class="btn"><i class="icon-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="icon-refresh"></i> ${_('Sync LDAP users/groups')}</a>
+                    &nbsp;&nbsp;&nbsp;&nbsp;
+                %endif
+            </p>
         </div>
         </div>
-        <form class="form-search">
-            ${_('Filter: ')}<input type="text" id="filterInput" class="input-xxlarge search-query" placeholder="${_('Search for username, name, e-mail, etc...')}">
-        </form>
     </div>
     </div>
+    <br/>
+
     <table class="table table-striped table-condensed datatables">
     <table class="table table-striped table-condensed datatables">
         <thead>
         <thead>
             <tr>
             <tr>
+                %if user.is_superuser:
+                    <th width="1%"><input id="selectAll" type="checkbox"/></th>
+                %endif
                 <th>${_('Username')}</th>
                 <th>${_('Username')}</th>
                 <th>${_('First Name')}</th>
                 <th>${_('First Name')}</th>
                 <th>${_('Last Name')}</th>
                 <th>${_('Last Name')}</th>
                 <th>${_('E-mail')}</th>
                 <th>${_('E-mail')}</th>
                 <th>${_('Groups')}</th>
                 <th>${_('Groups')}</th>
                 <th>${_('Last Login')}</th>
                 <th>${_('Last Login')}</th>
-                <th>&nbsp;</th>
             </tr>
             </tr>
         </thead>
         </thead>
         <tbody>
         <tbody>
         % for listed_user in users:
         % for listed_user in users:
-            <tr class="userRow" 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()])}">
-                <td>${listed_user.username}</td>
+            <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 class="center" data-row-selector-exclude="true">
+                        <input type="checkbox" class="userCheck" data-username="${listed_user.username}" data-confirmation-url="${ url('useradmin.views.delete_user', username=urllib.quote(listed_user.username))}" data-row-selector-exclude="true"/>
+                    </td>
+                %endif
+                <td>
+                    %if user.is_superuser or user.username == listed_user.username:
+                        <h5><a title="${_('Edit %(username)s') % dict(username=listed_user.username)}" href="${ url('useradmin.views.edit_user', username=urllib.quote(listed_user.username)) }" data-row-selector="true">${listed_user.username}</a></h5>
+                    %else:
+                        <h5>${listed_user.username}</h5>
+                    %endif
+                </td>
                 <td>${listed_user.first_name}</td>
                 <td>${listed_user.first_name}</td>
                 <td>${listed_user.last_name}</td>
                 <td>${listed_user.last_name}</td>
                 <td>${listed_user.email}</td>
                 <td>${listed_user.email}</td>
                 <td>${', '.join([group.name for group in listed_user.groups.all()])}</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>
                 <td>${date(listed_user.last_login)} ${time(listed_user.last_login).replace("p.m.","PM").replace("a.m.","AM")}</td>
-                <td class="right">
-                <%
-                    i18n_editUsername = _('Edit %(username)s') % {'username': listed_user.username}
-                    i18n_deleteUsername = _('Delete %(username)s') % {'username': listed_user.username}
-                %>
-                %if user.is_superuser == True:
-                    <a title="${_('Edit %(username)s') % dict(username=listed_user.username)}" class="btn small" href="${ url('useradmin.views.edit_user', username=urllib.quote(listed_user.username)) }" data-row-selector="true">${_('Edit')}</a>
-                    <a title="${_('Delete %(username)s') % dict(username=listed_user.username)}" class="btn small confirmationModal" alt="Are you sure you want to delete ${listed_user.username}?" href="javascript:void(0)" data-confirmation-url="${ url('useradmin.views.delete_user', username=urllib.quote_plus(listed_user.username)) }">${_('Delete')}</a>
-                %else:
-                    %if user.username == listed_user.username:
-                        <a title="${_('Edit %(username)s') % dict(username=listed_user.username)}" class="btn small" href="${ url('useradmin.views.edit_user', username=urllib.quote(listed_user.username)) }">Edit</a>
-                    %else:
-                        &nbsp;
-                    %endif
-                %endif
-                </td>
             </tr>
             </tr>
         % endfor
         % endfor
         </tbody>
         </tbody>
+        <tfoot class="hide">
+            <tr>
+                <td colspan="8">
+                    <div class="alert">
+                        ${_('There are no users matching the search criteria.')}
+                    </div>
+                </td>
+            </tr>
+        </tfoot>
     </table>
     </table>
 
 
     <div id="syncLdap" class="modal hide fade"></div>
     <div id="syncLdap" class="modal hide fade"></div>
@@ -94,14 +107,16 @@ ${layout.menubar(section='users', _=_)}
                 "bInfo": false,
                 "bInfo": false,
                 "bFilter": false,
                 "bFilter": false,
                 "aoColumns": [
                 "aoColumns": [
-                        null,
-                        null,
-                        null,
-                        null,
-                        null,
-                        { "sType": "date" },
-                        { "bSortable": false }
-                    ]
+                    %if user.is_superuser:
+                    { "bSortable": false },
+                    %endif
+                    null,
+                    null,
+                    null,
+                    null,
+                    null,
+                    { "sType": "date" }
+                ]
             });
             });
             $(".dataTables_wrapper").css("min-height","0");
             $(".dataTables_wrapper").css("min-height","0");
             $(".dataTables_filter").hide();
             $(".dataTables_filter").hide();
@@ -109,7 +124,7 @@ ${layout.menubar(section='users', _=_)}
             $(".confirmationModal").click(function(){
             $(".confirmationModal").click(function(){
                 var _this = $(this);
                 var _this = $(this);
                 $.ajax({
                 $.ajax({
-                    url: _this.attr("data-confirmation-url"),
+                    url: _this.data("confirmation-url"),
                     beforeSend: function(xhr){
                     beforeSend: function(xhr){
                         xhr.setRequestHeader("X-Requested-With", "Hue");
                         xhr.setRequestHeader("X-Requested-With", "Hue");
                     },
                     },
@@ -121,18 +136,29 @@ ${layout.menubar(section='users', _=_)}
                 });
                 });
             });
             });
 
 
-            $("#filterInput").keyup(function(){
-                $.each($(".userRow"), function(index, value) {
-                  if($(value).data("search").toLowerCase().indexOf($("#filterInput").val().toLowerCase()) == -1 && $("#filterInput").val() != ""){
-                    $(value).hide(250);
-                  }else{
-                    $(value).show(250);
-                  }
-                });
+            $("#selectAll").change(function(){
+                if ($(this).is(":checked")){
+                    $(".userCheck").attr("checked", "checked");
+                }
+                else {
+                    $(".userCheck").removeAttr("checked");
+                }
+                $(".userCheck").change();
+            });
+
+            $(".userCheck").change(function(){
+                if ($(".userCheck:checked").length == 1){
+                    $("#deleteUserBtn").removeAttr("disabled").data("confirmation-url", $(".userCheck:checked").data("confirmation-url"));
+                }
+                else {
+                    $("#deleteUserBtn").attr("disabled", "disabled");
+                }
             });
             });
 
 
             $("a[data-row-selector='true']").jHueRowSelector();
             $("a[data-row-selector='true']").jHueRowSelector();
         });
         });
     </script>
     </script>
 
 
+${layout.commons()}
+
 ${commonfooter(messages)}
 ${commonfooter(messages)}

+ 4 - 3
desktop/core/static/css/hue2.css

@@ -884,11 +884,12 @@ div.box {
     width:100px;
     width:100px;
     height:11px;
     height:11px;
     border: 1px solid #CCCCCC;
     border: 1px solid #CCCCCC;
-    border-radius: 5.5px;
-    -webkit-border-radius: 5.5px;
-    -moz-border-radius: 5.5px;
+    border-radius: 10px;
+    -webkit-border-radius: 10px;
+    -moz-border-radius: 10px;
     color: #555555;
     color: #555555;
     font-size: 10px;
     font-size: 10px;
+    padding-left: 4px;
 }
 }
 
 
 .jHueSelectorBody {
 .jHueSelectorBody {

+ 132 - 117
desktop/core/static/js/Source/jHue/jquery.selector.js

@@ -1,135 +1,150 @@
 /*
 /*
-* jHue selector plugin
-* it tranforms a select multiple into a searchable/selectable alphabetical list
-*/
+ * jHue selector plugin
+ * it tranforms a select multiple into a searchable/selectable alphabetical list
+ */
 ;(function ($, window, document, undefined) {
 ;(function ($, window, document, undefined) {
 
 
-	var pluginName = "jHueSelector",
-	defaults = {
-		selectAllLabel: "Select all",
-		searchPlaceholder: "Search",
-        width: 300
-	};
+    var pluginName = "jHueSelector",
+        defaults = {
+            selectAllLabel: "Select all",
+            searchPlaceholder: "Search",
+            noChoicesFound: "No choices found for this element",
+            width: 300,
+            height: 200
+        };
 
 
-	function Plugin(element, options) {
-		this.element = element;
-		this.options = $.extend({}, defaults, options) ;
-		this._defaults = defaults;
-		this._name = pluginName;
-		this.init();
-	}
+    function Plugin(element, options) {
+        this.element = element;
+        this.options = $.extend({}, defaults, options) ;
+        this._defaults = defaults;
+        this._name = pluginName;
+        this.init();
+    }
 
 
-	Plugin.prototype.setOptions = function(options) {
-		this.options = $.extend({}, defaults, options) ;
-	};
+    Plugin.prototype.setOptions = function(options) {
+        this.options = $.extend({}, defaults, options) ;
+    };
 
 
-	Plugin.prototype.init = function () {
-		var _this = this;
-		var addressBook = [];
-		var selectorContainer = $("<div>").addClass("jHueSelector");
-        if (this.options.width != 300){
+    Plugin.prototype.init = function () {
+        var _this = this;
+        var addressBook = [];
+        var selectorContainer = $("<div>");
+        if (this.options.width != 300) {
             selectorContainer.width(this.options.width);
             selectorContainer.width(this.options.width);
         }
         }
-		$(_this.element).hide();
-		$(_this.element).find("option").each(function(cnt, opt){
-			var initial = $(opt).text().substr(0,1).toLowerCase();
-			if (addressBook[initial] == null){
-				addressBook[initial] = [];
-			}
-			addressBook[initial].push($(opt));
-		});
-		var sortedKeys = [];
-		for (var key in addressBook){
-			sortedKeys.push(key);
-		}
-		sortedKeys.sort();
+        $(_this.element).hide();
+        $(_this.element).find("option").each(function (cnt, opt) {
+            var initial = $(opt).text().substr(0, 1).toLowerCase();
+            if (addressBook[initial] == null) {
+                addressBook[initial] = [];
+            }
+            addressBook[initial].push($(opt));
+        });
+        var sortedKeys = [];
+        for (var key in addressBook) {
+            sortedKeys.push(key);
+        }
+        sortedKeys.sort();
+
+        if (sortedKeys.length == 0){
+            $(_this.element).after($("<input>").attr("type", "text").attr("disabled", "disabled").val(this.options.noChoicesFound));
+        }
+        else {
+            selectorContainer.addClass("jHueSelector");
+            var body = $("<div>").addClass("jHueSelectorBody");
+            body.appendTo(selectorContainer);
+
+            for (var i = 0; i < sortedKeys.length; i++) {
+                var key = sortedKeys[i];
+                var ul = $("<ul>");
+                var dividerLi = $("<li>").addClass("selectorDivider");
+                dividerLi.html("<strong>" + key.toUpperCase() + "</strong>");
+                dividerLi.appendTo(ul);
+                $.each(addressBook[key], function (cnt, opt) {
+                    var li = $("<li>");
+                    var lbl = $("<label>").text(opt.text());
+                    var chk = $("<input>").attr("type", "checkbox").addClass("selector").change(function () {
+                        if ($(this).is(":checked")) {
+                            $(this).data("opt").attr("selected", "selected");
+                        }
+                        else {
+                            $(this).data("opt").removeAttr("selected");
+                        }
+                    }).data("opt", opt).prependTo(lbl);
+                    if (opt.is(":selected")) {
+                        chk.attr("checked", "checked");
+                    }
+                    lbl.appendTo(li);
+                    li.appendTo(ul);
+                });
+                ul.appendTo(body);
+            }
 
 
-		var body = $("<div>").addClass("jHueSelectorBody");
-		body.appendTo(selectorContainer);
+            var header = $("<div>").addClass("jHueSelectorHeader");
+            header.prependTo(selectorContainer);
 
 
-		for (var i=0; i<sortedKeys.length; i++){
-			var key = sortedKeys[i];
-			var ul = $("<ul>");
-			var dividerLi = $("<li>").addClass("selectorDivider");
-			dividerLi.html("<strong>"+key.toUpperCase()+"</strong>");
-			dividerLi.appendTo(ul);
-			$.each(addressBook[key], function(cnt, opt){
-				var li = $("<li>");
-				var lbl = $("<label>").text(opt.text());
-				var chk = $("<input>").attr("type","checkbox").addClass("selector").change(function(){
-					if ($(this).is(":checked")){
-						$(this).data("opt").attr("selected", "selected");
-					}
-					else {
-						$(this).data("opt").removeAttr("selected");
-					}
-				}).data("opt",opt).prependTo(lbl);
-                if (opt.is(":selected")){
-                    chk.attr("checked","checked");
+            var selectAll = $("<label>").text(this.options.selectAllLabel);
+            $("<input>").attr("type", "checkbox").change(function () {
+                var isChecked = $(this).is(":checked");
+                selectorContainer.find("input.selector:visible").each(function () {
+                    if (isChecked) {
+                        $(this).attr("checked", "checked");
+                        $(this).data("opt").attr("selected", "selected");
+                    }
+                    else {
+                        $(this).removeAttr("checked");
+                        $(this).data("opt").removeAttr("selected");
+                    }
+                });
+                if (searchBox.val() != "") {
+                    $(this).removeAttr("checked");
                 }
                 }
-				lbl.appendTo(li);
-				li.appendTo(ul);
-			});
-			ul.appendTo(body);
-		}
+            }).prependTo(selectAll);
+            selectAll.appendTo(header);
 
 
-		var header = $("<div>").addClass("jHueSelectorHeader");
-		header.prependTo(selectorContainer);
+            var searchBox = $("<input>").attr("type", "text").attr("placeholder", this.options.searchPlaceholder).keyup(function () {
+                body.find("ul").attr("show", true).show();
+                var q = $.trim($(this).val());
+                if (q != "") {
+                    body.find("li.selectorDivider").hide();
+                    body.find("label").each(function () {
+                        if ($(this).text().toLowerCase().indexOf(q) > -1) {
+                            $(this).parent().show();
+                        }
+                        else {
+                            $(this).parent().hide();
+                        }
+                    });
+                    body.find("ul").attr("show", false);
+                    body.find("ul > *:visible").parent().attr("show", true).find("li.selectorDivider").show();
+                }
+                else {
+                    body.find("li.selectorDivider").show();
+                    body.find("label").parent().show();
+                }
+                body.find("ul[show=false]").hide();
+                body.find("ul[show=true]").show();
+            });
+            if (this.options.width != 300) {
+                searchBox.css("margin-left", this.options.width - 120 + "px");
+            }
+            searchBox.prependTo(header);
 
 
-		var selectAll = $("<label>").text(this.options.selectAllLabel);
-		$("<input>").attr("type","checkbox").change(function(){
-			var isChecked = $(this).is(":checked");
-			selectorContainer.find("input.selector:visible").each(function(){
-				if (isChecked){
-					$(this).attr("checked", "checked");
-					$(this).data("opt").attr("selected", "selected");
-				}
-				else {
-					$(this).removeAttr("checked");
-					$(this).data("opt").removeAttr("selected");
-				}
-			});
-			if (searchBox.val() != ""){
-				$(this).removeAttr("checked");
-			}
-		}).prependTo(selectAll);
-		selectAll.appendTo(header);
+            body.height(this.options.height - header.outerHeight());
 
 
-		var searchBox = $("<input>").attr("type","text").attr("placeholder", this.options.searchPlaceholder).keyup(function(){
-			var q = $.trim($(this).val());
-			if (q != ""){
-				body.find("li.selectorDivider").hide();
-				body.find("label").each(function(){
-					if ($(this).text().toLowerCase().indexOf(q) > -1){
-						$(this).show();
-					}
-					else {
-						$(this).hide();
-					}
-				});
-			}
-			else {
-				body.find("li.selectorDivider").show();
-				body.find("label").show();
-			}
-		});
-        if (this.options.width != 300){
-            searchBox.css("margin-left", this.options.width-120+"px");
+            $(_this.element).after(selectorContainer);
         }
         }
-		searchBox.prependTo(header);
-
-		$(_this.element).after(selectorContainer);
-	};
+    };
 
 
-	$.fn[pluginName] = function (options) {
-		return this.each(function () {
-			if (!$.data(this, 'plugin_' + pluginName)) {
-				$.data(this, 'plugin_' + pluginName, new Plugin( this, options));
-			}
-			else {
-				$.data(this, 'plugin_' + pluginName).setOptions(options);
-			}
-		});
-	}
+    $.fn[pluginName] = function (options) {
+        return this.each(function () {
+            if (!$.data(this, 'plugin_' + pluginName)) {
+                $.data(this, 'plugin_' + pluginName, new Plugin( this, options));
+            }
+            else {
+                $.data(this, 'plugin_' + pluginName).setOptions(options);
+            }
+        });
+    }
 
 
 })(jQuery, window, document);
 })(jQuery, window, document);