Browse Source

HUE-1280 [fb] Inline download and preview of images and pdf mimetype

Preview files, video, audio, pdf and word document.
Another great UX improvement:
Also find a way to report uploading error happening in the middleware and
display the exception. This is very useful for uploads failing because of
no permission to the target directory, invalid filename...
Romain Rigaux 12 years ago
parent
commit
30d7669

+ 2 - 1
apps/filebrowser/src/filebrowser/templates/display.mako

@@ -45,7 +45,7 @@ ${ commonheader(_('%(filename)s - File Viewer') % dict(filename=truncate(filenam
         <ul class="nav nav-list">
         <ul class="nav nav-list">
           <li class="nav-header">${_('Actions')}</li>
           <li class="nav-header">${_('Actions')}</li>
           % if view['mode'] == "binary":
           % if view['mode'] == "binary":
-            <li><a href="${base_url}?offset=${view['offset']}&length=${view['length']}&mode=text&compression=${view['compression']}"><i class="icon-icon-font"></i> ${_('View as text')}</a></li>
+            <li><a href="${base_url}?offset=${view['offset']}&length=${view['length']}&mode=text&compression=${view['compression']}"><i class="icon-font"></i> ${_('View as text')}</a></li>
           % endif
           % endif
 
 
           % if view['mode'] == "text":
           % if view['mode'] == "text":
@@ -279,4 +279,5 @@ ${ commonheader(_('%(filename)s - File Viewer') % dict(filename=truncate(filenam
       viewModel.toggleDisables();
       viewModel.toggleDisables();
     });
     });
   </script>
   </script>
+
 ${ commonfooter(messages) | n,unicode }
 ${ commonfooter(messages) | n,unicode }

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

@@ -1247,7 +1247,7 @@ from django.utils.translation import ugettext as _
           onComplete:function (id, fileName, response) {
           onComplete:function (id, fileName, response) {
             num_of_pending_uploads--;
             num_of_pending_uploads--;
             if (response.status != 0) {
             if (response.status != 0) {
-              $.jHueNotify.error("${ _('Error: ') }" + (response['data'] ? response['data'] : "${ _('Check file permissions') }"));
+              $.jHueNotify.error("${ _('Error: ') }" + response['data']);
             } else if (num_of_pending_uploads == 0) {
             } else if (num_of_pending_uploads == 0) {
               window.location = "/filebrowser/view" + self.currentPath();
               window.location = "/filebrowser/view" + self.currentPath();
             }
             }

+ 20 - 4
apps/filebrowser/src/filebrowser/views.py

@@ -40,10 +40,13 @@ from datetime import datetime
 from django.contrib import messages
 from django.contrib import messages
 from django.contrib.auth.models import User, Group
 from django.contrib.auth.models import User, Group
 from django.core import urlresolvers
 from django.core import urlresolvers
+from django.core.urlresolvers import reverse
 from django.template.defaultfilters import stringformat, filesizeformat
 from django.template.defaultfilters import stringformat, filesizeformat
 from django.http import Http404, HttpResponse, HttpResponseNotModified
 from django.http import Http404, HttpResponse, HttpResponseNotModified
 from django.views.decorators.http import require_http_methods
 from django.views.decorators.http import require_http_methods
 from django.views.static import was_modified_since
 from django.views.static import was_modified_since
+from django.shortcuts import redirect
+from django.template.defaultfilters import urlencode
 from django.utils.functional import curry
 from django.utils.functional import curry
 from django.utils.http import http_date, urlquote
 from django.utils.http import http_date, urlquote
 from django.utils.html import escape
 from django.utils.html import escape
@@ -81,6 +84,10 @@ BYTES_PER_SENTENCE = 2
 # The maximum size the file editor will allow you to edit
 # The maximum size the file editor will allow you to edit
 MAX_FILEEDITOR_SIZE = 256 * 1024
 MAX_FILEEDITOR_SIZE = 256 * 1024
 
 
+INLINE_DISPLAY_MIMETYPE = re.compile('video/|image/|audio/|application/pdf|application/msword|application/excel|'
+                                     'application/vnd\.ms|'
+                                     'application/vnd\.openxmlformats')
+
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
 
 
@@ -138,12 +145,12 @@ def view(request, path):
     if 'default_to_home' in request.GET:
     if 'default_to_home' in request.GET:
         home_dir_path = request.user.get_home_directory()
         home_dir_path = request.user.get_home_directory()
         if request.fs.isdir(home_dir_path):
         if request.fs.isdir(home_dir_path):
-            return format_preserving_redirect(request, urlresolvers.reverse(view, kwargs=dict(path=home_dir_path)))
+            return format_preserving_redirect(request, reverse(view, kwargs=dict(path=home_dir_path)))
 
 
     # default_to_home is set in bootstrap.js
     # default_to_home is set in bootstrap.js
     if 'default_to_trash' in request.GET:
     if 'default_to_trash' in request.GET:
         if request.fs.isdir(request.fs.trash_path):
         if request.fs.isdir(request.fs.trash_path):
-            return format_preserving_redirect(request, urlresolvers.reverse(view, kwargs=dict(path=request.fs.trash_path)))
+            return format_preserving_redirect(request, reverse(view, kwargs=dict(path=request.fs.trash_path)))
 
 
     try:
     try:
         stats = request.fs.stats(path)
         stats = request.fs.stats(path)
@@ -249,7 +256,7 @@ def save_file(request):
                          form.cleaned_data['encoding'])
                          form.cleaned_data['encoding'])
 
 
     messages.info(request, _('Saved %(path)s.') % {'path': os.path.basename(path)})
     messages.info(request, _('Saved %(path)s.') % {'path': os.path.basename(path)})
-    request.path = urlresolvers.reverse("filebrowser.views.edit", kwargs=dict(path=path))
+    request.path = reverse("filebrowser.views.edit", kwargs=dict(path=path))
     return edit(request, path, form)
     return edit(request, path, form)
 
 
 
 
@@ -567,6 +574,12 @@ def display(request, path):
     if not request.fs.isfile(path):
     if not request.fs.isfile(path):
         raise PopupException(_("Not a file: '%(path)s'") % {'path': path})
         raise PopupException(_("Not a file: '%(path)s'") % {'path': path})
 
 
+    mimetype = mimetypes.guess_type(path)[0]
+
+    if mimetype is not None and INLINE_DISPLAY_MIMETYPE.search(mimetype):
+      path_enc = urlencode(path)
+      return redirect(reverse('filebrowser.views.download', args=[path_enc]) + '?disposition=inline')
+
     stats = request.fs.stats(path)
     stats = request.fs.stats(path)
     encoding = request.GET.get('encoding') or i18n.get_site_encoding()
     encoding = request.GET.get('encoding') or i18n.get_site_encoding()
 
 
@@ -1158,6 +1171,9 @@ def _upload_file(request):
     """
     """
     form = UploadFileForm(request.POST, request.FILES)
     form = UploadFileForm(request.POST, request.FILES)
 
 
+    if request.META.get('upload_failed'):
+      raise PopupException(request.META.get('upload_failed'))
+
     if form.is_valid():
     if form.is_valid():
         uploaded_file = request.FILES['hdfs_file']
         uploaded_file = request.FILES['hdfs_file']
         dest = form.cleaned_data['dest']
         dest = form.cleaned_data['dest']
@@ -1303,7 +1319,7 @@ def location_to_url(location, strict=True):
     if strict and not split_path[1]:
     if strict and not split_path[1]:
       # No netloc, not full url
       # No netloc, not full url
       return None
       return None
-    return urlresolvers.reverse("filebrowser.views.view", kwargs=dict(path=split_path[2]))
+    return reverse("filebrowser.views.view", kwargs=dict(path=split_path[2]))
 
 
 def truncate(toTruncate, charsToKeep=50):
 def truncate(toTruncate, charsToKeep=50):
     """
     """

+ 3 - 2
desktop/libs/hadoop/src/hadoop/fs/upload.py

@@ -126,6 +126,7 @@ class HDFSfileUploadHandler(FileUploadHandler):
     self._starttime = 0
     self._starttime = 0
     self._activated = False
     self._activated = False
     self._destination = request.GET.get('dest', None)
     self._destination = request.GET.get('dest', None)
+    self.request = request
     # Need to directly modify FileUploadHandler.chunk_size
     # Need to directly modify FileUploadHandler.chunk_size
     FileUploadHandler.chunk_size = UPLOAD_CHUNK_SIZE.get()
     FileUploadHandler.chunk_size = UPLOAD_CHUNK_SIZE.get()
 
 
@@ -139,13 +140,13 @@ class HDFSfileUploadHandler(FileUploadHandler):
         self._starttime = time.time()
         self._starttime = time.time()
       except Exception, ex:
       except Exception, ex:
         LOG.error("Not using HDFS upload handler: %s" % (ex,))
         LOG.error("Not using HDFS upload handler: %s" % (ex,))
-        raise ex
+        self.request.META['upload_failed'] = ex
 
 
       raise StopFutureHandlers()
       raise StopFutureHandlers()
 
 
   def receive_data_chunk(self, raw_data, start):
   def receive_data_chunk(self, raw_data, start):
     if not self._activated:
     if not self._activated:
-      return raw_data
+      raise StopUpload()
 
 
     try:
     try:
       self._file.write(raw_data)
       self._file.write(raw_data)