瀏覽代碼

[security] Restyled ACLs panel for HDFS

Enrico Berti 11 年之前
父節點
當前提交
f0220b7
共有 2 個文件被更改,包括 156 次插入97 次删除
  1. 149 97
      apps/security/src/security/templates/hdfs.mako
  2. 7 0
      apps/security/static/js/hdfs.ko.js

+ 149 - 97
apps/security/src/security/templates/hdfs.mako

@@ -28,17 +28,17 @@ ${ layout.menubar(section='hdfs') }
 
 
 <style type="text/css">
+  #hdfsTree {
+    width: 100%;
+    overflow-y: scroll;
+    min-height: 100px;
+  }
+
   #hdfsTree ul {
     list-style-type: none;
     padding: 0;
   }
 
-  .tree {
-    margin-left: 0 !important;
-    padding-left: 0 !important;
-    min-height: 300px;
-  }
-
   .node-row {
     margin: 4px;
     padding: 2px;
@@ -59,7 +59,7 @@ ${ layout.menubar(section='hdfs') }
   }
 
   .node-row.selected {
-    background-color: #F6F6F6;
+    background-color: #F6F6F6!important;
   }
 
   .loading-popover {
@@ -78,7 +78,40 @@ ${ layout.menubar(section='hdfs') }
     border-left: 1px solid #e5e5e5;
     padding-top: 6px;
     padding-left: 12px;
-    min-height: 300px;
+  }
+
+  .acl-panel .nav-tabs {
+    margin-bottom: 0;
+  }
+
+  .acl-panel h4:not(:first-child) {
+    margin-top: 20px;
+  }
+
+  .acl-panel-content {
+    padding: 6px;
+    overflow-y: scroll;
+  }
+
+  .acl-block {
+    background-color: #f6f6f6;
+    padding: 3px;
+    margin-bottom: 4px;
+  }
+
+  .acl-block .checkbox, .acl-block .radio {
+    margin-left: 6px;
+  }
+
+  .add-acl {
+    padding: 5px;
+    text-align: center;
+    color: #CCC;
+    font-size: 20px;
+  }
+
+  .add-acl:hover {
+    color: #999;
   }
 
   #path {
@@ -131,13 +164,21 @@ ${ layout.menubar(section='hdfs') }
   }
 
   .tree-toolbar .pull-right {
-    margin-top: -3px;
+    margin-top: -5px;
   }
 
   .inline-block {
     display: inline-block;
   }
 
+  .force-word-break {
+    word-break: break-all;
+  }
+
+  .pointer {
+    cursor: pointer;
+  }
+
 </style>
 
 <script type="text/html" id="acl-display">
@@ -148,18 +189,47 @@ ${ layout.menubar(section='hdfs') }
 
 
 <script type="text/html" id="acl-edition">
-  <div data-bind="visible: status() != 'deleted'">
-    <input type="radio" value="group" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('group') }
-    <input type="radio" value="user" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('user') }
-    <input type="radio" value="mask" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('mask') }
-    <input type="radio" value="other" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('other') }
-    <input type="text" data-bind="value: name, valueUpdate: 'afterkeydown', css: { 'user-list': type() == 'user', 'group-list': type() == 'group' }" class="input-small" placeholder="${ _('name...') }"/>
-    <input type="checkbox" title="r, ${ _('Read') }" data-bind="checked: r"/>
-    <input type="checkbox" title="w, ${ _('Write') }" data-bind="checked: w"/>
-    <input type="checkbox" title="x, ${ _('Execute') }" data-bind="checked: x"/>
-    <a href="javascript: void(0)">
-      <i class="fa fa-minus" data-bind="click: $root.assist.removeAcl"></i>
+  <div data-bind="visible: status() != 'deleted'" class="acl-block">
+    <a href="javascript: void(0)" class="pull-right" style="margin-right: 4px">
+      <i class="fa fa-times" data-bind="click: $root.assist.removeAcl"></i>
     </a>
+    <label class="radio inline-block">
+      <input type="radio" value="group" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('group') }
+    </label>
+    <label class="radio inline-block">
+      <input type="radio" value="user" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('user') }
+    </label>
+    <label class="radio inline-block">
+      <input type="radio" value="mask" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('mask') }
+    </label>
+    <label class="radio inline-block">
+      <input type="radio" value="other" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('other') }
+    </label>
+    <div style="margin-left: 6px">
+      <div data-bind="visible: type() == 'user'">
+        <select class="user-list-acl" data-bind="options: $root.selectableHadoopUsers, select2: { placeholder: '${ _("Select a user") }', update: name}" style="width: 200px"></select>
+      </div>
+
+      <div data-bind="visible: type() == 'group'">
+        <select class="group-list-acl" data-bind="options: $root.selectableHadoopGroups, select2: { placeholder: '${ _("Select a group") }', update: name}" style="width: 200px"></select>
+      </div>
+
+      <input type="text" data-bind="value: name, valueUpdate: 'afterkeydown', visible: type() == 'mask' || type() == 'other'" placeholder="${ _('name ad...') }" style="width: 180px; margin-bottom: 0px; height: 26px; min-height: 26px"/>
+    </div>
+
+    <br/>
+    <label class="checkbox inline-block">
+      <input type="checkbox" data-bind="checked: r"/>
+      ${ _('Read') } <span class="muted">(r)</span>
+    </label>
+    <label class="checkbox inline-block">
+      <input type="checkbox" data-bind="checked: w"/>
+      ${ _('Write') } <span class="muted">(w)</span>
+    </label>
+    <label class="checkbox inline-block">
+      <input type="checkbox" data-bind="checked: x"/>
+      ${ _('Execute') } <span class="muted">(x)</span>
+    </label>
   </div>
 </script>
 
@@ -187,8 +257,8 @@ ${ layout.menubar(section='hdfs') }
                     <div class="dropdown inline-block" style="margin-right: 6px">
                       <a class="dropdown-toggle" data-toggle="dropdown" href="#"><i class="fa fa-eye-slash" data-bind="visible: $root.assist.isDiffMode"></i><i class="fa fa-eye" data-bind="visible: ! $root.assist.isDiffMode()"></i> <span data-bind="visible: $root.assist.isDiffMode">${ _('Show non accessible files for') }</span><span data-bind="visible: ! $root.assist.isDiffMode()">${ _('Impersonate the user') }</span></a>
                       <ul class="dropdown-menu">
-                        <li data-bind="click: function() { $root.assist.isDiffMode(true); }"><a tabindex="-1" href="#">${ _('Show non accessible files for') } <strong data-bind="text: $root.doAs"></strong></a></li>
-                        <li data-bind="click: function() { $root.assist.isDiffMode(false); }"><a tabindex="-1" href="#">${ _('Impersonate the user') } <strong data-bind="text: $root.doAs"></strong></a></li>
+                        <li data-bind="visible: ! $root.assist.isDiffMode(), click: function() { $root.assist.isDiffMode(true); }"><a tabindex="-1" href="#">${ _('Show non accessible files for') } <strong data-bind="text: $root.doAs"></strong></a></li>
+                        <li data-bind="visible: $root.assist.isDiffMode(), click: function() { $root.assist.isDiffMode(false); }"><a tabindex="-1" href="#">${ _('Impersonate the user') } <strong data-bind="text: $root.doAs"></strong></a></li>
                       </ul>
                     </div>
                     <select class="user-list" data-bind="options: $root.selectableHadoopUsers, select2: { placeholder: '${ _("Select a user") }', update: $root.doAs}" style="width: 120px"></select>
@@ -213,53 +283,57 @@ ${ layout.menubar(section='hdfs') }
             <div class="span4">
               <div class="acl-panel" data-bind="visible: ! $root.assist.isLoadingAcls()">
 
+                  <div data-bind="visible: $root.assist.changedAcls().length" class="pull-right" style="margin-right: 6px; margin-top: 4px">
+                    <button type="button" data-bind="click: $root.assist.updateAcls" class="btn disable-feedback">
+                      <i class="fa fa-save"></i> ${ _('Save') }
+                    </button>
+                  </div>
+
                   <ul class="nav nav-tabs">
                     <li data-bind="css: {'active': ! $root.assist.showAclsAsText()}"><a href="javascript: void(0)" data-bind="click: function() { $root.assist.showAclsAsText(false); }"><i class="fa fa-pencil"></i> ${ _('Edit') }</a></li>
                     <li data-bind="css: {'active': $root.assist.showAclsAsText()}"><a href="javascript: void(0)" data-bind="click: function() { $root.assist.showAclsAsText(true); }"><i class="fa fa-header"></i> ${ _('View as text') }</a></li>
                   </ul>
 
-                  <span class="fake-pre" data-bind="visible: $root.assist.showAclsAsText">
-                    # file: <span data-bind="text: $root.assist.path"></span><br/>
-                    # owner: <span data-bind="text: $root.assist.owner"></span><br/>
-                    # group: <span data-bind="text: $root.assist.group"></span><br/>
-                    <div data-bind="foreach: $root.assist.regularAcls">
-                      <div data-bind="template: {name: 'acl-display'}"></div>
-                    </div>
-                    <div data-bind="foreach: $root.assist.defaultAcls">
-                      <div data-bind="template: {name: 'acl-display'}"></div>
-                    </div>
-                  </span>
-
-                  <span data-bind="visible: ! $root.assist.showAclsAsText()">
-                    <dl>
-                      <dt><a data-bind="attr: { href: '/filebrowser/view' + $root.assist.path() }, text: $root.assist.path()" target="_blank" title="${ _('Open in File Browser') }"></a></dt>
-                      <dt>
-                        <i class="fa fa-user" style="color: #999999" title="${_('User')}"></i> <span title="${_('User')}" data-bind="text: $root.assist.owner"></span>
-                        <i class="fa fa-users" style="color: #999999" title="${_('Group')}"></i> <span title="${_('Group')}" data-bind="text: $root.assist.group"></span>
-                      </dt>
-                    </dl>
-
-                    <h4>${ _('ACLs') }</h4>
-                    <div data-bind="foreach: $root.assist.regularAcls">
-                      <div data-bind="template: {name: 'acl-edition'}"></div>
-                    </div>
-
-                    <a href="javascript: void(0)" data-bind="click: $root.assist.addAcl"><i class="fa fa-plus"></i></a>
-
-                    <br/>
-
-                    <h4>${ _('Default ACLs') }</h4>
-                    <div data-bind="foreach: $root.assist.defaultAcls">
-                      <div data-bind="template: {name: 'acl-edition'}"></div>
-                    </div>
-                    <a href="javascript: void(0)" data-bind="click: $root.assist.addDefaultAcl"><i class="fa fa-plus"></i></a>
-
-                    <div data-bind="visible: $root.assist.changedAcls().length">
-                      <button type="button" data-bind="click: $root.assist.updateAcls" rel="tooltip" data-placement="bottom" data-loading-text="${ _('Saving...') }" data-original-title="${ _('Save') }" class="btn">
-                        <i class="fa fa-save"></i>
-                      </button>
-                    </div>
-                  </span>
+                  <div class="acl-panel-content">
+                    <span class="fake-pre" data-bind="visible: $root.assist.showAclsAsText">
+                      # file: <span data-bind="text: $root.assist.path"></span><br/>
+                      # owner: <span data-bind="text: $root.assist.owner"></span><br/>
+                      # group: <span data-bind="text: $root.assist.group"></span><br/>
+                      <div data-bind="foreach: $root.assist.regularAcls">
+                        <div data-bind="template: {name: 'acl-display'}"></div>
+                      </div>
+                      <div data-bind="foreach: $root.assist.defaultAcls">
+                        <div data-bind="template: {name: 'acl-display'}"></div>
+                      </div>
+                    </span>
+
+                    <span data-bind="visible: ! $root.assist.showAclsAsText()">
+                      <h4>${ _('Path') }</h4>
+                      <a class="force-word-break" data-bind="attr: { href: '/filebrowser/view' + $root.assist.path() }, text: $root.assist.path()" target="_blank" title="${ _('Open in File Browser') }" rel="tooltip"></a>
+
+                      <h4>${ _('User/Group') }</h4>
+                      <i class="fa fa-user" style="color: #999999" title="${_('User')}"></i> <span title="${_('User')}" data-bind="text: $root.assist.owner"></span>&nbsp;
+                      <i class="fa fa-users" style="color: #999999" title="${_('Group')}"></i> <span title="${_('Group')}" data-bind="text: $root.assist.group"></span>
+
+                      <h4>${ _('ACLs') }</h4>
+                      <div data-bind="foreach: $root.assist.regularAcls">
+                        <div data-bind="template: {name: 'acl-edition'}"></div>
+                      </div>
+
+                      <div class="acl-block pointer add-acl" data-bind="click: $root.assist.addAcl">
+                        <i class="fa fa-plus"></i>
+                      </div>
+
+                      <h4>${ _('Default ACLs') }</h4>
+                      <div data-bind="foreach: $root.assist.defaultAcls">
+                        <div data-bind="template: {name: 'acl-edition'}"></div>
+                      </div>
+
+                      <div class="acl-block pointer add-acl" data-bind="click: $root.assist.addDefaultAcl">
+                        <i class="fa fa-plus"></i>
+                      </div>
+                    </span>
+                  </div>
                 </div>
                 <div class="loading-popover center" data-bind="visible: $root.assist.isLoadingAcls()"><i class="fa fa-spinner fa-spin fa-5x"></i></div>
             </div>
@@ -287,7 +361,7 @@ ${ layout.menubar(section='hdfs') }
 </%def>
 
 
-${ tree.import_templates(itemClick='$root.assist.setPath', itemDblClick='$root.assist.openPath', itemSelected='$root.assist.path() == path()', iconModifier=treeIcons, styleModifier='aclBit', styleModifierPullRight=aclBitPullRight, anchorProperty='path', showMore='$root.assist.loadMore') }
+${ tree.import_templates(itemClick='$root.assist.setPath', itemSelected='$root.assist.path() == path()', iconModifier=treeIcons, styleModifier='aclBit', styleModifierPullRight=aclBitPullRight, anchorProperty='path', showMore='$root.assist.loadMore') }
 
 
 <script src="/static/ext/js/knockout-min.js" type="text/javascript" charset="utf-8"></script>
@@ -306,13 +380,15 @@ ${ tree.import_templates(itemClick='$root.assist.setPath', itemDblClick='$root.a
     init: function(element, valueAccessor, allBindingsAccessor, vm) {
       var options = ko.toJS(valueAccessor()) || {};
       $(element).select2(options).on("change", function(e){
-        if (typeof e.val != "undefined"){
-          vm.doAs(e.val);
+        if (typeof e.val != "undefined" && typeof valueAccessor().update != "undefined"){
+          valueAccessor().update(e.val);
         }
       });
     },
     update: function(element, valueAccessor, allBindingsAccessor, vm) {
-      $(element).select2("val", vm.doAs());
+      if (typeof valueAccessor().update != "undefined"){
+        $(element).select2("val", valueAccessor().update());
+      }
     }
 };
 
@@ -326,7 +402,6 @@ ${ tree.import_templates(itemClick='$root.assist.setPath', itemDblClick='$root.a
       $(".user-list").select2("val", viewModel.doAs());
     });
 
-
     var _initialPath = "/";
     if (window.location.hash != "") {
       _initialPath = window.location.hash.substr(1);
@@ -346,11 +421,13 @@ ${ tree.import_templates(itemClick='$root.assist.setPath', itemDblClick='$root.a
       smartTooltip: "${_('Did you know? You can use the tab key or CTRL + Space to autocomplete file and folder names')}"
     });
 
-    function resizePathInput () {
+    function resizeComponents () {
       $("#path").width($(".tree-toolbar").width() - 64);
+      $("#hdfsTree").height($(window).height() - 260);
+      $(".acl-panel-content").height($(window).height() - 260);
     }
 
-    resizePathInput();
+    resizeComponents();
 
     $(document).on("rendered.tree", function() {
       var _path = viewModel.assist.path();
@@ -364,41 +441,16 @@ ${ tree.import_templates(itemClick='$root.assist.setPath', itemDblClick='$root.a
       }
     });
 
-    $(".path-container").data("originalWidth", $(".path-container").width());
-    $(".acl-panel").data("originalWidth", $(".acl-panel").width());
-    $(".path-container-ghost").height($(".path-container").outerHeight());
-    resetPathContainer();
-
-    $(window).scroll(function () {
-      if ($(window).scrollTop() > 85) {
-        $(".path-container").width($(".path-container").data("originalWidth"));
-        $(".acl-panel").width($(".acl-panel").data("originalWidth"));
-        $(".path-container").css("position", "fixed").css("top", "70px");
-        $(".acl-panel").css("position", "fixed").css("top", "70px");
-        $(".path-container-ghost").removeClass("hide");
-      }
-      else {
-        resetPathContainer();
-      }
-    });
-
     var _resizeTimeout = -1;
     $(window).resize(function(){
       window.clearTimeout(_resizeTimeout);
-      _resizeTimeout = window.setTimeout(resizePathInput, 100);
+      _resizeTimeout = window.setTimeout(resizeComponents, 100);
     });
 
     window.onpopstate = function() {
       viewModel.assist.path(window.location.hash.substr(1));
     };
 
-    function resetPathContainer() {
-      $(".path-container").attr("style", "min-width: 190px");
-      $(".acl-panel").attr("style", "min-width: 190px");
-      $(".path-container").data("originalWidth", $(".path-container").width());
-      $(".acl-panel").data("originalWidth", $(".acl-panel").width());
-      $(".path-container-ghost").addClass("hide");
-    }
   });
 </script>
 

+ 7 - 0
apps/security/static/js/hdfs.ko.js

@@ -401,6 +401,13 @@ var HdfsViewModel = function (initial) {
     return _users.sort();
   }, self);
 
+  self.selectableHadoopGroups = ko.computed(function() {
+    var _users = ko.utils.arrayMap(self.availableHadoopGroups(), function(group) {
+        return group.name;
+    });
+    return _users.sort();
+  }, self);
+
 
   self.init = function (path) {
     self.fetchUsers();