浏览代码

HUE-780 [fb] Small improvements for a better usability

'..' directory all the time on top (event when sorting).
'..' directory row same height as the other rows
renamed 'Options' button to 'Operations'
added "row-selector-exclude" attribute to the operations column
added new sortable column (type) with icons
removed pagination links when not valuable
fixed modal popups that now can be closed when clicking on the overlay
swapped 'submit' and 'cancel' buttons in the modals
added the current name of the file/directory instead of an empty name on the 'Rename' modal
moved the two 'Delete' operations in the bottom of the operations btn-group
styled type column
updated style for sortable column titles

Conflicts:

	apps/filebrowser/src/filebrowser/templates/listdir_components.mako
Enrico Berti 13 年之前
父节点
当前提交
c8c9bdd

+ 1 - 1
apps/filebrowser/src/filebrowser/templates/chmod.mako

@@ -69,7 +69,7 @@ from django.utils.translation import ugettext as _
         </table>
         </table>
     </div>
     </div>
     <div class="modal-footer" style="padding-top: 10px;">
     <div class="modal-footer" style="padding-top: 10px;">
-        <input class="btn primary" type="submit" value="${_('Submit')}"/>
         <a class="btn" onclick="$('#changePermissionModal').modal('hide');">${_('Cancel')}</a>
         <a class="btn" onclick="$('#changePermissionModal').modal('hide');">${_('Cancel')}</a>
+        <input class="btn primary" type="submit" value="${_('Submit')}"/>
     </div>
     </div>
 </form>
 </form>

+ 1 - 1
apps/filebrowser/src/filebrowser/templates/chown.mako

@@ -91,8 +91,8 @@ from django.utils.translation import ugettext as _
         <div id="chownRequired" class="hide" style="position: absolute; left: 10;">
         <div id="chownRequired" class="hide" style="position: absolute; left: 10;">
             <span class="label label-important">${_('Sorry, name is required.')}</span>
             <span class="label label-important">${_('Sorry, name is required.')}</span>
         </div>
         </div>
-        <input class="btn primary" type="submit" value="${_('Submit')}" />
         <a class="btn" onclick="$('#changeOwnerModal').modal('hide');">${_('Cancel')}</a>
         <a class="btn" onclick="$('#changeOwnerModal').modal('hide');">${_('Cancel')}</a>
+        <input class="btn primary" type="submit" value="${_('Submit')}" />
     </div>
     </div>
 </form>
 </form>
 
 

+ 51 - 36
apps/filebrowser/src/filebrowser/templates/listdir_components.mako

@@ -51,13 +51,17 @@ from django.utils.translation import ugettext as _
 <%def name="bottompage(page)">
 <%def name="bottompage(page)">
   ${pageref(page.num_pages())}
   ${pageref(page.num_pages())}
 </%def>
 </%def>
-<%def name="pagination(page)">
+<%def name="pagination(localpage)">
     <div class="pagination">
     <div class="pagination">
         <ul class="pull-right">
         <ul class="pull-right">
-            <li class="prev"><a title="${_('Beginning of List')}" ${toppage(page)}>&larr; ${_('Beginning of List')}</a></li>
-            <li><a title="${_('Previous Page')}" ${prevpage(page)}>${_('Previous Page')}</a></li>
-            <li><a title="${_('Next Page')}" ${nextpage(page)}>${_('Next Page')}</a></li>
-            <li class="next"><a title="${_('End of List')}" ${bottompage(page)}>${_('End of List')} &rarr;</a></li>
+            % if page.number > 1:
+                <li class="prev"><a title="${_('Beginning of List')}" ${toppage(page)}>&larr; ${_('Beginning of List')}</a></li>
+                <li><a title="${_('Previous Page')}" ${prevpage(page)}>${_('Previous Page')}</a></li>
+            % endif
+            % if page.number < page.num_pages():
+                <li><a title="${_('Next page')}" ${nextpage(page)}>${_('Next Page')}</a></li>
+                <li class="next"><a title="${_('End of List')}" ${bottompage(page)}>${_('End of List')} &rarr;</a></li>
+            % endif
         </ul>
         </ul>
         <p>${_('Show')} <select id="pagesize" class="input-mini"><option>15</option><option selected>30</option><option>45</option><option>60</option><option>100</option><option>200</option></select> ${_('items per page')}. ${_('Showing %(start_index)s to %(end_index)s of %(total_count)s items, page %(page)s of %(num_pages)s.') % dict(start_index=page.start_index(), end_index=page.end_index(), total_count=page.total_count(), page=page.number, num_pages=page.num_pages())}</p>
         <p>${_('Show')} <select id="pagesize" class="input-mini"><option>15</option><option selected>30</option><option>45</option><option>60</option><option>100</option><option>200</option></select> ${_('items per page')}. ${_('Showing %(start_index)s to %(end_index)s of %(total_count)s items, page %(page)s of %(num_pages)s.') % dict(start_index=page.start_index(), end_index=page.end_index(), total_count=page.total_count(), page=page.number, num_pages=page.num_pages())}</p>
     </div>
     </div>
@@ -80,47 +84,60 @@ from django.utils.translation import ugettext as _
         .sortable {
         .sortable {
             cursor: pointer;
             cursor: pointer;
         }
         }
+        .file-row {
+            height:37px;
+        }
+        .parent {
+            display: none;
+        }
     </style>
     </style>
     %if len(files)>0 :
     %if len(files)>0 :
     <table class="table table-condensed table-striped datatables">
     <table class="table table-condensed table-striped datatables">
         <thead>
         <thead>
             <tr>
             <tr>
+                <th class="sortable sorting" data-sort="type" width="5%">Type</th>
             % if cwd_set:
             % if cwd_set:
                 <th class="sortable sorting" data-sort="name">${_('Name')}</th>
                 <th class="sortable sorting" data-sort="name">${_('Name')}</th>
             % else:
             % else:
                 <th class="sortable sorting" data-sort="name">${_('Path')}</th>
                 <th class="sortable sorting" data-sort="name">${_('Path')}</th>
             % endif
             % endif
-                <th class="sortable sorting" data-sort="size">${_('Size')}</th>
-                <th class="sortable sorting" data-sort="user">${_('User')}</th>
-                <th class="sortable sorting" data-sort="group">${_('Group')}</th>
-                <th>${_('Permissions')}</th>
-                <th class="sortable sorting" data-sort="mtime">${_('Date')}</th>
-                <th>&nbsp;</th>
+                <th class="sortable sorting" data-sort="size" width="10%">${_('Size')}</th>
+                <th class="sortable sorting" data-sort="user" width="10%">${_('User')}</th>
+                <th class="sortable sorting" data-sort="group" width="10%">${_('Group')}</th>
+                <th width="10%">${_('Permissions')}</th>
+                <th class="sortable sorting" data-sort="mtime" width="15%">${_('Date')}</th>
+                <th width="100">&nbsp;</th>
             </tr>
             </tr>
         </thead>
         </thead>
-        <tbody>
+        <tbody id="files" class="hide">
             % for file in files:
             % for file in files:
             <%
             <%
-              cls = ''
-              if (file_filter == 'dir' and file['type'] != 'dir') or (file_filter != 'dir' and file['type'] == 'dir'):
-                if (file_filter != 'any'):
-                  cls = ' not-selectable'
+              icon = 'icon-file'
+              if file['type'] == 'dir':
+                icon = 'icon-folder-close'
+              endif
 
 
               if cwd_set:
               if cwd_set:
                 display_name = file['name']
                 display_name = file['name']
               else:
               else:
                 display_name = file['path']
                 display_name = file['path']
               endif
               endif
+
+              row_class = 'file-row'
+              if '..' == display_name:
+                row_class += ' parent'
+              endif
             %>
             %>
 
 
             <% path = file['path'] %>
             <% path = file['path'] %>
-            <tr class="file-row" data-search="${display_name}">
+            <tr class="${row_class}" data-search="${display_name}">
+                <td class="left"><i class="${icon}"></i></td>
                 <td>
                 <td>
                     <h5><a href="${url('filebrowser.views.'+view, path=urlencode(path))}?file_filter=${file_filter}" data-row-selector="true">${display_name}</a></h5>
                     <h5><a href="${url('filebrowser.views.'+view, path=urlencode(path))}?file_filter=${file_filter}" data-row-selector="true">${display_name}</a></h5>
                 </td>
                 </td>
                 <td>
                 <td>
                     % if "dir" == file['type']:
                     % if "dir" == file['type']:
-                    <span>~</span>
+                    <span></span>
                     % else:
                     % else:
                     <span>${file['stats']['size']|filesizeformat}</span>
                     <span>${file['stats']['size']|filesizeformat}</span>
                     % endif
                     % endif
@@ -129,22 +146,18 @@ from django.utils.translation import ugettext as _
                 <td>${file['stats']['group']}</td>
                 <td>${file['stats']['group']}</td>
                 <td>${file['rwx']}</td>
                 <td>${file['rwx']}</td>
                 <td>${date(datetime.datetime.fromtimestamp(file['stats']['mtime']))} ${time(datetime.datetime.fromtimestamp(file['stats']['mtime'])).replace("p.m.","PM").replace("a.m.","AM")}</td>
                 <td>${date(datetime.datetime.fromtimestamp(file['stats']['mtime']))} ${time(datetime.datetime.fromtimestamp(file['stats']['mtime'])).replace("p.m.","PM").replace("a.m.","AM")}</td>
-                <td>
+                <td data-row-selector-exclude="true">
                     % if ".." != file['name']:
                     % if ".." != file['name']:
                     <%
                     <%
                     path_digest = urlencode(md5.md5(smart_str(path)).hexdigest())
                     path_digest = urlencode(md5.md5(smart_str(path)).hexdigest())
                     %>
                     %>
-                    <div class="btn-group">
+                    <div class="btn-group" data-row-selector-exclude="true">
                         <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
                         <a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
-                            ${_('Options')}
+                            ${_('Operations')}
                             <span class="caret"></span>
                             <span class="caret"></span>
                         </a>
                         </a>
-                        <ul class="dropdown-menu">
-                            % if "dir" == file['type']:
-                            <li><a class="delete" delete-type="rmdir" file-to-delete="${path}" data-backdrop="static" data-keyboard="true">${_('Delete')}</a></li>
-                            <li><a class="delete" delete-type="rmtree" file-to-delete="${path}" data-backdrop="static" data-keyboard="true">${_('Delete Recursively')}</a></li>
-                            % else:
-                            <li><a class="delete" delete-type="remove" file-to-delete="${path}" data-backdrop="static" data-keyboard="true">${_('Delete')}</a></li>
+                        <ul class="dropdown-menu pull-right">
+                            % if "file" == file['type']:
                             <li><a href="${url('filebrowser.views.view', path=urlencode(path))}">${_('View File')}</a></li>
                             <li><a href="${url('filebrowser.views.view', path=urlencode(path))}">${_('View File')}</a></li>
                             <li><a href="${url('filebrowser.views.edit', path=urlencode(path))}">${_('Edit File')}</a></li>
                             <li><a href="${url('filebrowser.views.edit', path=urlencode(path))}">${_('Edit File')}</a></li>
                             <li><a href="${url('filebrowser.views.download', path=urlencode(path))}" target="_blank">${_('Download File')}</a></li>
                             <li><a href="${url('filebrowser.views.download', path=urlencode(path))}" target="_blank">${_('Download File')}</a></li>
@@ -153,6 +166,12 @@ from django.utils.translation import ugettext as _
                             <li><a onclick="openChownWindow('${path}','${file['stats']['user']}','${file['stats']['group']}','${current_request_path}')">${_('Change Owner / Group')}</a></li>
                             <li><a onclick="openChownWindow('${path}','${file['stats']['user']}','${file['stats']['group']}','${current_request_path}')">${_('Change Owner / Group')}</a></li>
                             <li><a onclick="openChmodWindow('${path}','${stringformat(file['stats']['mode'], "o")}','${current_request_path}')">${_('Change Permissions')}</a></li>
                             <li><a onclick="openChmodWindow('${path}','${stringformat(file['stats']['mode'], "o")}','${current_request_path}')">${_('Change Permissions')}</a></li>
                             <li><a onclick="openMoveModal('${path}','${stringformat(file['stats']['mode'], "o")}', '${current_request_path}')">${_('Move')}</a></li>
                             <li><a onclick="openMoveModal('${path}','${stringformat(file['stats']['mode'], "o")}', '${current_request_path}')">${_('Move')}</a></li>
+                            % if "dir" == file['type']:
+                            <li><a class="delete" delete-type="rmdir" file-to-delete="${path}" data-keyboard="true">${_('Delete')}</a></li>
+                            <li><a class="delete" delete-type="rmtree" file-to-delete="${path}" data-keyboard="true">${_('Delete Recursively')}</a></li>
+                            % else:
+                            <li><a class="delete" delete-type="remove" file-to-delete="${path}" data-keyboard="true">${_('Delete')}</a></li>
+                            % endif
                         </ul>
                         </ul>
                     </div>
                     </div>
                     % endif
                     % endif
@@ -204,8 +223,8 @@ from django.utils.translation import ugettext as _
             </div>
             </div>
 
 
             <input id="renameSrcPath" type="hidden" name="src_path" type="text">
             <input id="renameSrcPath" type="hidden" name="src_path" type="text">
-            <input type="submit" value="${_('Submit')}" class="btn primary" />
             <a id="cancelRenameBtn" class="btn">${_('Cancel')}</a>
             <a id="cancelRenameBtn" class="btn">${_('Cancel')}</a>
+            <input type="submit" value="${_('Submit')}" class="btn primary" />
         </div>
         </div>
         </form>
         </form>
     </div>
     </div>
@@ -255,8 +274,8 @@ from django.utils.translation import ugettext as _
          <div id="directoryNameRequiredAlert" class="alert-message error hide" style="position: absolute; left: 10;">
          <div id="directoryNameRequiredAlert" class="alert-message error hide" style="position: absolute; left: 10;">
             <p><strong>${_('Sorry, directory name is required.')}</strong>
             <p><strong>${_('Sorry, directory name is required.')}</strong>
         </div>
         </div>
-        <input class="btn primary" type="submit" value="${_('Submit')}" />
         <a id="cancelCreateDirectoryBtn" class="btn" href="#">${_('Cancel')}</a>
         <a id="cancelCreateDirectoryBtn" class="btn" href="#">${_('Cancel')}</a>
+        <input class="btn primary" type="submit" value="${_('Submit')}" />
     </div>
     </div>
     </form>
     </form>
 </div>
 </div>
@@ -276,7 +295,6 @@ from django.utils.translation import ugettext as _
             success: function(data){
             success: function(data){
                 $("#changeOwnerModal").html(data);
                 $("#changeOwnerModal").html(data);
                 $("#changeOwnerModal").modal({
                 $("#changeOwnerModal").modal({
-                    backdrop: "static",
                     keyboard: true,
                     keyboard: true,
                     show: true
                     show: true
                 });
                 });
@@ -295,7 +313,6 @@ from django.utils.translation import ugettext as _
             success: function(data){
             success: function(data){
                 $("#changePermissionModal").html(data);
                 $("#changePermissionModal").html(data);
                 $("#changePermissionModal").modal({
                 $("#changePermissionModal").modal({
-                    backdrop: "static",
                     keyboard: true,
                     keyboard: true,
                     show: true
                     show: true
                 });
                 });
@@ -314,7 +331,6 @@ from django.utils.translation import ugettext as _
             success: function(data){
             success: function(data){
                 $("#moveModal").html(data);
                 $("#moveModal").html(data);
                 $("#moveModal").modal({
                 $("#moveModal").modal({
-                    backdrop: "static",
                     keyboard: true,
                     keyboard: true,
                     show: true
                     show: true
                 });
                 });
@@ -423,7 +439,6 @@ from django.utils.translation import ugettext as _
             $("#fileToDeleteInput").attr("value", $(e.target).attr("file-to-delete"));
             $("#fileToDeleteInput").attr("value", $(e.target).attr("file-to-delete"));
             $("#deleteForm").attr("action", "/filebrowser/" + $(e.target).attr("delete-type") + "?next=" + encodeURI("${current_request_path}") + "&path=" + encodeURI("${path}"));
             $("#deleteForm").attr("action", "/filebrowser/" + $(e.target).attr("delete-type") + "?next=" + encodeURI("${current_request_path}") + "&path=" + encodeURI("${path}"));
             $("#deleteModal").modal({
             $("#deleteModal").modal({
-                backdrop: "static",
                 keyboard: true,
                 keyboard: true,
                 show: true
                 show: true
             });
             });
@@ -437,8 +452,8 @@ from django.utils.translation import ugettext as _
         $(".rename").live("click",function(eventObject){
         $(".rename").live("click",function(eventObject){
             $("#renameSrcPath").attr("value", $(eventObject.target).attr("file-to-rename"));
             $("#renameSrcPath").attr("value", $(eventObject.target).attr("file-to-rename"));
             $("#renameFileName").text($(eventObject.target).attr("file-to-rename"));
             $("#renameFileName").text($(eventObject.target).attr("file-to-rename"));
+            $("#newNameInput").val($(eventObject.target).attr("file-to-rename"));
             $("#renameModal").modal({
             $("#renameModal").modal({
-                backdrop: "static",
                 keyboard: true,
                 keyboard: true,
                 show: true
                 show: true
             });
             });
@@ -475,13 +490,14 @@ from django.utils.translation import ugettext as _
             $("#moveForm").find("input[name='dest_path']").removeClass("fieldError");
             $("#moveForm").find("input[name='dest_path']").removeClass("fieldError");
         });
         });
 
 
+        $(".parent").prependTo("#files").removeClass("parent");
+        $("#files").removeClass("hide");
         $("a[data-row-selector='true']").jHueRowSelector();
         $("a[data-row-selector='true']").jHueRowSelector();
     %endif
     %endif
 
 
         //upload handlers
         //upload handlers
         $(".upload-link").click(function(){
         $(".upload-link").click(function(){
             $("#uploadModal").modal({
             $("#uploadModal").modal({
-                backdrop: "static",
                 keyboard: true,
                 keyboard: true,
                 show: true
                 show: true
             });
             });
@@ -490,7 +506,6 @@ from django.utils.translation import ugettext as _
         //create directory handlers
         //create directory handlers
         $(".create-directory-link").click(function(){
         $(".create-directory-link").click(function(){
             $("#createDirectoryModal").modal({
             $("#createDirectoryModal").modal({
-                backdrop: "static",
                 keyboard: true,
                 keyboard: true,
                 show: true
                 show: true
             });
             });

+ 1 - 1
apps/filebrowser/src/filebrowser/templates/move.mako

@@ -37,8 +37,8 @@ from django.utils.translation import ugettext as _
         <div id="moveNameRequiredAlert" class="hide" style="position: absolute; left: 10;">
         <div id="moveNameRequiredAlert" class="hide" style="position: absolute; left: 10;">
             <span class="label label-important">${_('Sorry, name is required.')}</span>
             <span class="label label-important">${_('Sorry, name is required.')}</span>
         </div>
         </div>
-        <input class="btn primary" type="submit" value="${_('Submit')}"/>
         <a class="btn" onclick="$('#moveModal').modal('hide');">${_('Cancel')}</a>
         <a class="btn" onclick="$('#moveModal').modal('hide');">${_('Cancel')}</a>
+        <input class="btn primary" type="submit" value="${_('Submit')}"/>
     </div>
     </div>
 </form>
 </form>
 
 

+ 2 - 2
apps/filebrowser/src/filebrowser/views.py

@@ -356,7 +356,7 @@ def listdir_paged(request, path):
       pagenum           - The page number to show. Defaults to 1.
       pagenum           - The page number to show. Defaults to 1.
       pagesize          - How many to show on a page. Defaults to 15.
       pagesize          - How many to show on a page. Defaults to 15.
       sortby=?          - Specify attribute to sort by. Accepts:
       sortby=?          - Specify attribute to sort by. Accepts:
-                            (name, atime, mtime, size, user, group)
+                            (type, name, atime, mtime, size, user, group)
                           Defaults to name.
                           Defaults to name.
       descending        - Specify a descending sort order.
       descending        - Specify a descending sort order.
                           Default to false.
                           Default to false.
@@ -394,7 +394,7 @@ def listdir_paged(request, path):
     sortby = request.GET.get('sortby', None)
     sortby = request.GET.get('sortby', None)
     descending_param = request.GET.get('descending', None)
     descending_param = request.GET.get('descending', None)
     if sortby is not None:
     if sortby is not None:
-        if sortby not in ('name', 'atime', 'mtime', 'user', 'group', 'size'):
+        if sortby not in ('type', 'name', 'atime', 'mtime', 'user', 'group', 'size'):
             logger.info("Invalid sort attribute '%s' for listdir." %
             logger.info("Invalid sort attribute '%s' for listdir." %
                         (sortby,))
                         (sortby,))
         else:
         else:

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

@@ -504,12 +504,14 @@ table.display td.center {
 .sorting_asc {
 .sorting_asc {
     cursor: pointer;
     cursor: pointer;
     background: url('../art/datatables/sort_asc.png') no-repeat center left;
     background: url('../art/datatables/sort_asc.png') no-repeat center left;
+    border-top: 3px solid #FFFFFF!important;
     padding-left: 20px!important;
     padding-left: 20px!important;
 }
 }
 
 
 .sorting_desc {
 .sorting_desc {
     cursor: pointer;
     cursor: pointer;
     background: url('../art/datatables/sort_desc.png') no-repeat center left;
     background: url('../art/datatables/sort_desc.png') no-repeat center left;
+    border-top: 3px solid #FFFFFF!important;
     padding-left: 20px!important;
     padding-left: 20px!important;
 }
 }
 
 
@@ -520,7 +522,7 @@ table.display td.center {
     padding-left: 20px!important;
     padding-left: 20px!important;
 }
 }
 
 
-.sorting:hover {
+.sorting:hover, .sorting_asc:hover, .sorting_desc:hover {
     border-top: 3px solid #EEEEEE!important;
     border-top: 3px solid #EEEEEE!important;
 }
 }
 
 

+ 1 - 0
desktop/libs/hadoop/src/hadoop/fs/webhdfs_types.py

@@ -35,6 +35,7 @@ class WebHdfsStat(object):
     self.name = decode_fs_path(file_status['pathSuffix'])
     self.name = decode_fs_path(file_status['pathSuffix'])
     self.path = Hdfs.join(parent_path, self.name)
     self.path = Hdfs.join(parent_path, self.name)
     self.isDir = file_status['type'] == 'DIRECTORY'
     self.isDir = file_status['type'] == 'DIRECTORY'
+    self.type = file_status['type']
     self.atime = file_status['accessTime'] / 1000
     self.atime = file_status['accessTime'] / 1000
     self.mtime = file_status['modificationTime'] / 1000
     self.mtime = file_status['modificationTime'] / 1000
     self.user = file_status['owner']
     self.user = file_status['owner']