Kaynağa Gözat

HUE-7873 [core] Update the AutoComplete for Document share in /home page

Roohi 7 yıl önce
ebeveyn
işleme
3e295ca

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue-embedded.css


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue.css


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue3-extra.css


+ 103 - 92
desktop/core/src/desktop/static/desktop/js/document/hueDocument.js

@@ -32,93 +32,79 @@ var HueDocument = (function() {
     self.loaded = ko.observable(false);
     self.loading = ko.observable(false);
     self.hasErrors = ko.observable(false);
-
+    self.searchInput = ko.observable();
     self.selectedUserOrGroup = ko.observable();
     self.selectedPerm = ko.observable('read');
 
-    self.availableUsersAndGroups = {};
+
+    self.userMap = {};
+    self.idToUserMap = {};
+    self.groupMap = {};
+    self.items = [];
   };
 
-  HueDocument.prototype.initUserTypeahead = function (callback) {
+  HueDocument.prototype.onShareAutocompleteUserEnter = function () {
     var self = this;
-    $.getJSON('/desktop/api/users/autocomplete', function (data) {
-      self.availableUsersAndGroups = data;
-      self.prettifyUserNames(self.availableUsersAndGroups.users);
-      var dropdown = [];
-      var usermap = {};
-      var groupmap = {};
-
-      $.each(self.availableUsersAndGroups.users, function (i, user) {
-        usermap[user.prettyName] = user;
-        dropdown.push(user.prettyName);
-      });
 
-      $.each(self.availableUsersAndGroups.groups, function (i, group) {
-        groupmap[group.name] = group;
-        dropdown.push(group.name);
-      });
+    searchAutoCompInput = $("#userSearchAutocomp").val();
+    selectedUserOrGroup = self.userMap[searchAutoCompInput] ? self.userMap[searchAutoCompInput] : self.groupMap[searchAutoCompInput];
+    if (selectedUserOrGroup != null) {
+      if (typeof selectedUserOrGroup.username !== 'undefined') {
+        self.definition().perms[self.selectedPerm()].users.push(selectedUserOrGroup);
+      } else {
+        self.definition().perms[self.selectedPerm()].groups.push(selectedUserOrGroup);
+      }
+      self.persistPerms();
+    }
+    $("#userSearchAutocomp").val("");
 
-      $("#documentShareTypeahead").typeahead({
-        source: function (query, process) {
-          process(dropdown);
-        },
-        matcher: function (item) {
-          if (item.toLowerCase() === this.query.trim().toLowerCase()) {
-            self.selectedUserOrGroup(usermap[item] ? usermap[item] : groupmap[item]);
-            return true;
-          } else if (item.toLowerCase().indexOf(this.query.trim().toLowerCase()) != -1) {
-            self.selectedUserOrGroup(undefined);
-            return true;
-          }
-        },
-        sorter: function (items) {
-          return items.sort();
-        },
-        highlighter: function (item) {
-          var _icon = "fa";
-          if (usermap[item]) {
-            _icon += " fa-user";
-          }
-          else {
-            _icon += " fa-users";
-          }
-          var regex = new RegExp('(' + this.query + ')', 'gi');
-          return "<i class='" + _icon + "'></i> " + item.replace(regex, "<strong>$1</strong>");
-        },
-        updater: function (item) {
-          self.selectedUserOrGroup(usermap[item] ? usermap[item] : groupmap[item]);
-          return item;
-        }
-      });
+  };
 
-      $("#documentShareTypeahead").on("keyup", function (e) {
-        var _code = (e.keyCode ? e.keyCode : e.which);
-        if (_code == 13) {
-          self.handleTypeAheadSelection();
-        }
+  HueDocument.prototype.shareAutocompleteUserSource = function(request, callback) {
+    var self = this;
+    var successCallback = function (data) {
+      var JSON_USERS_GROUPS = data;
+      self.items = [];
+      $.each(JSON_USERS_GROUPS.users, function (i, user) {
+        var label = prettifyUsername(user);
+        var highLightedLabel = hueUtils.highlight(label, request.term);
+        self.userMap[label] = user;
+        self.items.push({
+          data: {
+            "icon": "fa fa-user",
+            "label": highLightedLabel
+          },
+          value: label
+        });
+        self.idToUserMap[user.id] = user;
+      });
+      $.each(JSON_USERS_GROUPS.groups, function (i, group) {
+        self.groupMap[group.name] = group;
+        var highLightedLabel = hueUtils.highlight(group.name, request.term);
+        self.items.push({
+          data: {
+            "icon": "fa fa-users",
+            "label": highLightedLabel
+          },
+          value: group.name
+        });
       });
 
-      if (typeof id == "function"){
-        id();
+      if(self.items.length == 0){
+        self.items.push({
+          'noMatch': true
+        });
       }
-      callback();
-    }).fail(function (response) {
-      $(document).trigger("error", "There was an error processing your action: " + response.responseText);
-    });
-  };
 
-  HueDocument.prototype.handleTypeAheadSelection = function () {
-    var self = this;
-    if (self.selectedUserOrGroup()) {
-      if (typeof self.selectedUserOrGroup().username !== 'undefined') {
-        self.definition().perms[self.selectedPerm()].users.push(self.selectedUserOrGroup());
-      } else {
-        self.definition().perms[self.selectedPerm()].groups.push(self.selectedUserOrGroup());
+      callback(self.items);
+
       }
-      self.persistPerms();
-    }
-    self.selectedUserOrGroup(null);
-    $("#documentShareTypeahead").val("");
+
+    self.apiHelper.fetchUsersAndGroups({
+      data: {filter: request.term},
+      successCallback: successCallback,
+      errorCallback: errorCallback
+    });
   };
 
   HueDocument.prototype.persistPerms = function () {
@@ -159,13 +145,33 @@ var HueDocument = (function() {
     self.loading(true);
     self.hasErrors(false);
 
+    var fetchDocumentsSuccessCallback = function (data) {
+      readusers = data.document.perms.read.users.map(function(user){return user.id});
+      writeusers = data.document.perms.write.users.map(function(user){return user.id});
+      allusers = readusers.concat(writeusers);
+      if (allusers.length > 0) {
+        var successCallback = function (response) {
+          $.each(response.users, function (i, user) {
+            // Needed for getting prettyusername of already shared users
+            self.idToUserMap[user.id] = user;
+          });
+          self.definition(data.document);
+        };
+        self.apiHelper.fetchUsersByIds({
+          userids: JSON.stringify(allusers),
+          successCallback: successCallback,
+          errorCallback: errorCallback
+        });
+      } else {
+        self.definition(data.document);
+      }
+    }
+
     var fetchFunction = function () {
       self.apiHelper.fetchDocument({
         uuid: self.fileEntry.definition().uuid
       }).done(function (data) {
-        self.prettifyUserNames(data.document.perms.write.users);
-        self.prettifyUserNames(data.document.perms.read.users);
-        self.definition(data.document);
+        fetchDocumentsSuccessCallback(data);
         self.loading(false);
         self.loaded(true);
       }).fail(function () {
@@ -175,24 +181,29 @@ var HueDocument = (function() {
       })
     };
 
-    self.initUserTypeahead(fetchFunction);
+    fetchFunction();
   };
 
-  HueDocument.prototype.prettifyUserNames = function (users) {
-    $.each(users, function (idx, user) {
-      user.prettyName = '';
-      if (user.first_name) {
-        user.prettyName += user.first_name + ' ';
-      }
-      if (user.last_name) {
-        user.prettyName += user.last_name + ' ';
-      }
-      if (user.prettyName) {
-        user.prettyName += '(' + user.username + ')';
-      } else {
-        user.prettyName += user.username;
-      }
-    });
+
+  HueDocument.prototype.prettifyUsernameById = function (id) {
+    var self = this;
+    return self.prettifyUserName(self.idToUserMap[id]);
+  }
+
+  HueDocument.prototype.prettifyUserName = function (user) {
+    user.prettyName = '';
+    if (user.first_name) {
+      user.prettyName += user.first_name + ' ';
+    }
+    if (user.last_name) {
+      user.prettyName += user.last_name + ' ';
+    }
+    if (user.prettyName) {
+      user.prettyName += '(' + user.username + ')';
+    } else {
+      user.prettyName += user.username;
+    }
+    return user.prettyName;
   };
 
   HueDocument.prototype.removeFromPerms = function (perms, id) {

+ 22 - 0
desktop/core/src/desktop/static/desktop/js/hue.utils.js

@@ -442,6 +442,28 @@ window.hueUtils = window.hueUtils || (function () {
     }
   };
 
+  hueUtils.highlight = function (text, searchTerm) {
+    if(searchTerm === '' || text === '') return text;
+
+    var remText = text;
+    var highLightedText = '';
+    searchTerm = searchTerm.toLowerCase();
+
+    do {
+      var remLowerText = remText.toLowerCase();
+      var startIndex = remLowerText.indexOf(searchTerm);
+      if(startIndex >= 0) {
+        highLightedText += remText.substring(0, startIndex) + '<strong>' + remText.substring(startIndex, startIndex + searchTerm.length) + '</strong>';
+        remText = remText.substring(startIndex + searchTerm.length);
+      } else {
+         highLightedText += remText;
+      }
+
+    } while (startIndex >= 0);
+
+    return highLightedText;
+  };
+
   return hueUtils;
 
 })();

+ 14 - 1
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -208,9 +208,19 @@
         },
         open: function(event, ui) {
           hideSpinner();
+          if($element.data('custom-hueAutocomplete')) {
+            if($element.data('custom-hueAutocomplete').menu.element.getNiceScroll()) {
+              $element.data('custom-hueAutocomplete').menu.element.getNiceScroll().resize();
+            }
+          }
         },
         close: function(event, ui) {
           hideSpinner();
+          if($element.data('custom-hueAutocomplete')) {
+            if($element.data('custom-hueAutocomplete').menu.element.getNiceScroll()) {
+              $element.data('custom-hueAutocomplete').menu.element.getNiceScroll().hide();
+            }
+          }
         }
       }, options);
 
@@ -255,7 +265,10 @@
             }
           },
           _resizeMenu: function () {
-            this.menu.element.outerWidth(options.minWidth);
+            // This overrides the default behaviour of using dropdown width of the same size as input autocomplete box
+            if (options.limitWidthToInput) {
+              this.menu.element.outerWidth(options.minWidth);
+            }
           },
           _renderMenu: function (ul, items) {
             var self = this;

+ 9 - 7
desktop/core/src/desktop/static/desktop/js/share2.vm.js

@@ -137,15 +137,16 @@ function setupSharing(id, updateFunc) {
 function source(request, callback) {
 
   var successCallback = function (data) {
-    JSON_USERS_GROUPS = data;
+    var JSON_USERS_GROUPS = data;
     shareViewModel.items = [];
     $.each(JSON_USERS_GROUPS.users, function (i, user) {
       var label = prettifyUsername(user);
+      var highLightedLabel = hueUtils.highlight(label, request.term);
       shareViewModel.userMap[label] = user;
       shareViewModel.items.push({
         data: {
           "icon": "fa fa-user",
-          "label": label
+          "label": highLightedLabel
         },
         value: label
       });
@@ -153,19 +154,20 @@ function source(request, callback) {
     });
     $.each(JSON_USERS_GROUPS.groups, function (i, group) {
       shareViewModel.groupMap[group.name] = group;
+      var highLightedLabel = hueUtils.highlight(group.name, request.term);
       shareViewModel.items.push({
         data: {
           "icon": "fa fa-users",
-          "label": group.name
+          "label": highLightedLabel
         },
         value: group.name
       });
     });
 
     if(shareViewModel.items.length == 0){
-     shareViewModel.items.push({
-       'noMatch': true
-     });
+      shareViewModel.items.push({
+        'noMatch': true
+      });
     }
 
     callback(shareViewModel.items);
@@ -263,7 +265,7 @@ function shareDocFinal() {
         $(document).trigger("error", "There was an error processing your action: " + response.message);
       }
       else {
-        shareViewModel.updateDoc(response.doc);
+        shareViewModel.setDocUuid(shareViewModel.docUuid);
       }
     }
   }).fail(function (response) {

+ 15 - 4
desktop/core/src/desktop/static/desktop/less/hue-cross-version.less

@@ -631,16 +631,27 @@ ul.errorlist {
 
 .hue-autocomplete {
   border-radius: @hue-panel-border-radius;
+  max-height: 162px;
 }
 
 .hue-autocomplete-item {
   a {
-    height: 40px;
-    padding-left: 20px !important;
-    padding-top: 10px !important;
+    display: block;
+    clear: both;
+    font-weight: normal;
+    font-size: 14px;
+    line-height: 20px;
+    color: @cui-gray-800;
+    white-space: nowrap;
+    padding: 6px 20px !important;
     span{
       font-size: 15px;
-      font-weight: bold;
     }
   }
+  div.no-match {
+    width: 200px;
+    height: 25px;
+    font-size : 15px
+  }
 }
+

+ 7 - 6
desktop/core/src/desktop/templates/common_share.mako

@@ -27,8 +27,8 @@ from django.utils.translation import ugettext as _
 </script>
 
 <script type="text/html" id="user-search-autocomp-no-match">
-   <div>
-     <div>${ _('No match found') }</div>
+   <div class="no-match">
+     <span>${ _('No match found') }</span>
    </div>
  </script>
 
@@ -39,8 +39,8 @@ from django.utils.translation import ugettext as _
     <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
     <h2 class="modal-title">${_('Sharing settings')}</h2>
   </div>
-  <div class="modal-body" style="overflow-y: visible; height: 240px">
-    <div class="row-fluid">
+  <div class="modal-body" style="overflow: visible; height: 240px">
+    <div class="row-fluid" data-bind="niceScroll" style="max-height: 114px">
       <div class="span6">
         <h4 class="muted" style="margin-top:0px">${_('Read')}</h4>
         <div data-bind="visible: (selectedDoc().perms.read.users.length == 0 && selectedDoc().perms.read.groups.length == 0)">${_('The document is not shared for read.')}</div>
@@ -65,9 +65,10 @@ from django.utils.translation import ugettext as _
     <div class="clearfix"></div>
     <div style="margin-top: 20px">
       <div class="input-append">
-      <input id="userSearchAutocomp" placeholder="${_('Type a username or a group name')}" type="text" data-bind="autocomplete: { source: source, itemTemplate: 'user-search-autocomp-item', noMatchTemplate: 'user-search-autocomp-no-match',valueObservable: searchInput, showSpinner: true, classPrefix: 'hue-', limitWidthToInput: true, minWidth: 160, onEnter: handleTypeaheadSelection }, clearable: { value: searchInput }, textInput: searchInput" class="ui-autocomplete-input" autocomplete="off" style="width: 460px">
+      <div id="menu"></div>
+      <input id="userSearchAutocomp" placeholder="${_('Type a username or a group name')}" type="text" data-bind="autocomplete: { source: source, itemTemplate: 'user-search-autocomp-item', noMatchTemplate: 'user-search-autocomp-no-match',valueObservable: searchInput, showSpinner: true, classPrefix: 'hue-', onEnter: handleTypeaheadSelection, appendTo: $('#menu') }, clearable: { value: searchInput }, textInput: searchInput" class="ui-autocomplete-input" autocomplete="off" style="width: 460px">
         <div class="btn-group">
-          <a id="documentShareAddBtn" class="btn"><i class="fa fa-plus-circle"></i> <span data-bind="text: selectedPermLabel"></span></a>
+          <a id="documentShareAddBtn" class="btn"><span data-bind="text: selectedPermLabel"></span></a>
           <a id="documentShareCaret" class="btn dropdown-toggle" data-toggle="dropdown">
             <span class="caret"></span>
           </a>

+ 13 - 10
desktop/core/src/desktop/templates/document_browser.mako

@@ -34,7 +34,7 @@ from desktop.views import _ko
       <i class="fa fa-fw"></i><span class="drag-text">4 entries</span>
     </div>
 
-    <div id="shareDocumentModal" data-keyboard="true" class="modal hide fade" tabindex="-1">
+    <div id="shareDocumentModal"  class="modal hide fade">
       <!-- ko with: activeEntry -->
       <!-- ko with: selectedEntry -->
       <!-- ko with: document -->
@@ -42,15 +42,15 @@ from desktop.views import _ko
         <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
         <h2 class="modal-title">${_('Sharing')} - <span data-bind="text: $parent.definition().name"></span></h2>
       </div>
-      <div class="modal-body" style="overflow-y: visible; height: 240px">
+      <div class="modal-body" style="overflow: visible; height: 240px">
         <!-- ko with: definition -->
-        <div class="row-fluid" data-bind="visible: !$parent.hasErrors()">
+        <div class="row-fluid" data-bind="niceScroll , visible: !$parent.hasErrors()" style="max-height: 114px;" id="scrolldiv">
           <div class="span6">
             <h4 class="muted" style="margin-top:0px">${_('Read')}</h4>
             <div data-bind="visible: (perms.read.users.length == 0 && perms.read.groups.length == 0)">${_('The document is not shared for read.')}</div>
             <ul class="unstyled airy" data-bind="foreach: perms.read.users">
               <li>
-                <span class="badge badge-info" data-bind="css: { 'badge-left' : $parents[1].fileEntry.canModify() }"><i class="fa fa-user"></i> <span data-bind="text: prettyName, css:{ 'notpretty': prettyName === '' }, attr:{ 'data-id': id }"></span></span><span class="badge badge-right trash-share" data-bind="visible: $parents[1].fileEntry.canModify(), click: function() { $parents[1].removeUserReadShare($data) }"> <i class="fa fa-times"></i></span>
+                <span class="badge badge-info" data-bind="css: { 'badge-left' : $parents[1].fileEntry.canModify() }"><i class="fa fa-user"></i> <span data-bind="text: $parents[1].prettifyUsernameById(id), attr:{'data-id': id}"></span></span><span class="badge badge-right trash-share" data-bind="visible: $parents[1].fileEntry.canModify(), click: function() { $parents[1].removeUserReadShare($data) }"> <i class="fa fa-times"></i></span>
               </li>
             </ul>
             <ul class="unstyled airy" data-bind="foreach: perms.read.groups">
@@ -65,7 +65,7 @@ from desktop.views import _ko
             <div data-bind="visible: (perms.write.users.length == 0 && perms.write.groups.length == 0)">${_('The document is not shared for modify.')}</div>
             <ul class="unstyled airy" data-bind="foreach: perms.write.users">
               <li>
-                <span class="badge badge-info badge-left" data-bind="css: { 'badge-left' : $parents[1].fileEntry.canModify() }"><i class="fa fa-user"></i> <span data-bind="text: prettyName, css:{'notpretty': prettyName == ''}, attr:{'data-id': id}"></span></span><span class="badge badge-right trash-share" data-bind="visible: $parents[1].fileEntry.canModify(), click: function() { $parents[1].removeUserWriteShare($data) }"> <i class="fa fa-times"></i></span>
+                <span class="badge badge-info badge-left" data-bind="css: { 'badge-left' : $parents[1].fileEntry.canModify() }"><i class="fa fa-user"></i> <span data-bind="text: $parents[1].prettifyUsernameById(id), attr:{'data-id': id}"></span></span><span class="badge badge-right trash-share" data-bind="visible: $parents[1].fileEntry.canModify(), click: function() { $parents[1].removeUserWriteShare($data) }"> <i class="fa fa-times"></i></span>
               </li>
             </ul>
             <ul class="unstyled airy" data-bind="foreach: perms.write.groups">
@@ -84,13 +84,16 @@ from desktop.views import _ko
         </div>
         <div style="margin-top: 20px" data-bind="visible: fileEntry.canModify() && ! hasErrors() && ! loading()">
           <div class="input-append">
-            <input id="documentShareTypeahead" type="text" style="width: 420px" placeholder="${_('Type a username or a group name')}">
+             <div id="menu"></div>
+              <input id="userSearchAutocomp" placeholder="${_('Type a username or a group name')}" type="text" data-bind="autocomplete: { source: shareAutocompleteUserSource.bind($data), itemTemplate: 'user-search-autocomp-item', noMatchTemplate: 'user-search-autocomp-no-match', valueObservable: searchInput, showSpinner: true, classPrefix: 'hue-', onEnter: onShareAutocompleteUserEnter.bind($data), appendTo: $('#menu') }, clearable: { value: searchInput, textInput: searchInput }" class="ui-autocomplete-input" autocomplete="off" style="width: 420px">
             <div class="btn-group" style="overflow:visible">
-              <a class="btn btn-primary" data-bind="click: function () { if (selectedUserOrGroup()) { handleTypeAheadSelection() }}, css: { 'disabled': !selectedUserOrGroup() }"><i class="fa fa-plus-circle"></i> <span data-bind="text: selectedPerm() == 'read' ? '${ _('Read') }' : '${ _('Modify') }'"></span></a>
-              <a class="btn btn-primary dropdown-toggle" data-bind="css: { 'disabled': !selectedUserOrGroup() }" data-toggle="dropdown"><span class="caret"></span></a>
+              <a id="documentShareAddBtn" class="btn" data-bind="click: function () {  onShareAutocompleteUserEnter() }"><span data-bind="text: selectedPerm() == 'read' ? '${ _('Read') }' : '${ _('Modify') }'"></span></a>
+              <a id="documentShareCaret" class="btn dropdown-toggle" data-toggle="dropdown">
+               <span class="caret"></span>
+              </a>
               <ul class="dropdown-menu">
-                <li><a data-bind="click: function () { selectedPerm('read'); handleTypeAheadSelection() }" href="javascript:void(0)"><i class="fa fa-plus"></i> ${ _('Read') }</a></li>
-                <li><a data-bind="click: function () { selectedPerm('write'); handleTypeAheadSelection() }" href="javascript:void(0)"><i class="fa fa-plus"></i> ${ _('Modify') }</a></li>
+                <li><a data-bind="click: function () { selectedPerm('read'); onShareAutocompleteUserEnter() }" href="javascript:void(0)"><i class="fa fa-plus"></i> ${ _('Read') }</a></li>
+                <li><a data-bind="click: function () { selectedPerm('write'); onShareAutocompleteUserEnter() }" href="javascript:void(0)"><i class="fa fa-plus"></i> ${ _('Modify') }</a></li>
               </ul>
             </div>
           </div>

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor