コード例 #1
0
def edit_user(request, username=None):
  """
  edit_user(request, username = None) -> reply

  @type request:        HttpRequest
  @param request:       The request object
  @type username:       string
  @param username:      Default to None, when creating a new user
  """
  if request.user.username != username and not request.user.is_superuser:
    raise PopupException("You must be a superuser to add or edit another user.")
  if username is not None:
    instance = User.objects.get(username=username)
  else:
    instance = None

  if request.method == 'POST':
    form = UserChangeForm(request.POST, instance=instance)
    if form.is_valid(): # All validation rules pass
      if instance is None:
        form.save()
      else:
        #
        # Check for 3 more conditions:
        # (1) A user cannot inactivate oneself;
        # (2) Non-superuser cannot promote himself; and
        # (3) The last active superuser cannot demote/inactivate himself.
        #
        if request.user.username == username and not form.instance.is_active:
          raise PopupException("You cannot make yourself inactive.")

        global __users_lock
        __users_lock.acquire()
        try:
          # form.instance (and instance) now carry the new data
          orig = User.objects.get(username=username)
          if orig.is_superuser:
            if not form.instance.is_superuser or not form.instance.is_active:
              _check_remove_last_super(orig)
          else:
            if form.instance.is_superuser and not request.user.is_superuser:
              raise PopupException("You cannot make yourself a superuser.")

          # All ok
          form.save()
        finally:
          __users_lock.release()

      request.path = urlresolvers.reverse(list_users)
      return list_users(request)
  else:
    form = UserChangeForm(instance=instance)
  return render('edit_user.mako', request,
    dict(form=form, action=request.path, username=username))
コード例 #2
0
def get_help_fs(app_name):
    """
  Creates a local file system for a given app's help directory.
  """
    app = appmanager.get_desktop_module(app_name)
    if app is not None:
        if app.help_dir is None:
            raise PopupException("No help available for app '%s'." % app_name)
        return LocalSubFileSystem(app.help_dir)
    else:
        raise PopupException(
            "App '%s' is not loaded, so no help is available for it!" %
            app_name)
コード例 #3
0
def _upload(request):
    """
  Handles file uploads. The uploaded file is stored in HDFS. We just
  need to rename it to the right path.
  """
    if request.method == 'POST':
        form = UploadForm(request.POST, request.FILES)
        if not form.is_valid():
            logger.error("Error in upload form: %s" % (form.errors, ))
        else:
            uploaded_file = request.FILES['hdfs_file']
            dest = form.cleaned_data["dest"]
            if request.fs.isdir(dest):
                assert posixpath.sep not in uploaded_file.name
                dest = request.fs.join(dest, uploaded_file.name)

            # Temp file is created by superuser. Chown the file.
            tmp_file = uploaded_file.get_temp_path()
            username = request.user.username
            try:
                try:
                    request.fs.setuser(request.fs.superuser)
                    request.fs.chmod(tmp_file, 0644)
                    request.fs.chown(tmp_file, username, username)
                except IOError, ex:
                    msg = 'Failed to chown uploaded file ("%s") as superuser %s' % \
                          (tmp_file, request.fs.superuser)
                    logger.exception(msg)
                    raise PopupException(msg, detail=str(ex))
            finally:
                request.fs.setuser(username)

            # Move the file to where it belongs
            try:
                request.fs.rename(uploaded_file.get_temp_path(), dest)
            except IOError, ex:
                raise PopupException(
                    'Failed to rename uploaded temporary file ("%s") to "%s": %s'
                    % (tmp_file, dest, ex))

            dest_stats = request.fs.stats(dest)
            return render_with_toolbars(
                'upload_done.mako',
                request,
                {
                    # status is used by "fancy uploader"
                    'status': 1,
                    'path': dest,
                    'result': _massage_stats(request, dest_stats),
                    'next': request.GET.get("next")
                })
コード例 #4
0
def save_file(request):
    """
  The POST endpoint to save a file in the file editor.

  Does the save and then redirects back to the edit page.
  """
    form = EditorForm(request.POST)
    is_valid = form.is_valid()
    path = form.cleaned_data.get('path')

    if request.POST.get('save') == "saveAs":
        if not is_valid:
            return edit(request, path, form=form)
        else:
            data = dict(form=form)
            return render_with_toolbars("saveas.mako", request, data)

    if not path:
        raise PopupException("No path specified")
    if not is_valid:
        return edit(request, path, form=form)

    if request.fs.exists(path):
        _do_overwrite_save(request.fs, path, form.cleaned_data['contents'],
                           form.cleaned_data['encoding'])
    else:
        _do_newfile_save(request.fs, path, form.cleaned_data['contents'],
                         form.cleaned_data['encoding'])

    request.flash.put('Saved %s.' % os.path.basename(path))
    """ Changing path to reflect the request path of the JFrame that will actually be returned."""
    request.path = urlresolvers.reverse("filebrowser.views.edit",
                                        kwargs=dict(path=path))
    return edit(request, path, form)
コード例 #5
0
def _parse_fields(path, file_obj, encoding, filetypes, delimiters):
    """
  _parse_fields(path, file_obj, encoding, filetypes, delimiters)
                                  -> (delimiter, filetype, fields_list)

  Go through the list of ``filetypes`` (gzip, text) and stop at the first one
  that works for the data. Then apply the list of ``delimiters`` and pick the
  most appropriate one.
  ``path`` is used for debugging only.

  Return the best delimiter, filetype and the data broken down into rows of fields.
  """
    file_readers = [
        reader for reader in FILE_READERS if reader.TYPE in filetypes
    ]

    for reader in file_readers:
        LOG.debug("Trying %s for file: %s" % (reader.TYPE, path))
        file_obj.seek(0, hadoopfs.SEEK_SET)
        lines = reader.readlines(file_obj, encoding)
        if lines is not None:
            delim, fields_list = _readfields(lines, delimiters)
            return delim, reader.TYPE, fields_list
    else:
        # Even TextFileReader doesn't work
        msg = "Failed to decode file '%s' into printable characters under %s" % (
            path,
            encoding,
        )
        LOG.error(msg)
        raise PopupException(msg)
コード例 #6
0
 def drop_table(self, schema_name, table_name):
     c = self._cursor()
     try:
         c.execute("DROP TABLE %s.%s" % (schema_name, table_name))
     except Exception as e:
         err_msg = str(e)
         raise PopupException(err_msg, title="DB2 Error")
コード例 #7
0
def download(request, path):
    """
  Downloads a file.

  This is inspired by django.views.static.serve.
  """
    path = _unquote_path(path)
    if not request.fs.exists(path):
        raise Http404("File not found: %s" % escape(path))
    if not request.fs.isfile(path):
        raise PopupException("'%s' is not a file" % (path, ))

    mimetype = mimetypes.guess_type(path)[0] or 'application/octet-stream'
    stats = request.fs.stats(path)
    mtime = stats['mtime']
    size = stats['size']
    if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
                              mtime, size):
        return HttpResponseNotModified()
    # TODO(philip): Ideally a with statement would protect from leaks,
    # but tricky to do here.
    fh = request.fs.open(path)

    response = HttpResponse(_file_reader(fh), mimetype=mimetype)
    response["Last-Modified"] = http_date(stats['mtime'])
    response["Content-Length"] = stats['size']
    response["Content-Disposition"] = "attachment"
    return response
コード例 #8
0
    def process_exception(self, request, exception):
        import traceback
        logging.info("Processing exception: %s: %s" %
                     (exception, traceback.format_exc()))

        if hasattr(exception, "response"):
            return exception.response(request)

        if hasattr(exception, "response_data"):
            if request.ajax:
                response = render_json(exception.response_data)
                response[MIDDLEWARE_HEADER] = 'EXCEPTION'
                return response
            else:
                return render_to_response(
                    "error.html",
                    dict(error=exception.response_data.get("message")))

        # We didn't handle it as a special exception, but if we're ajax we still
        # need to do some kind of nicer handling than the built-in page
        # Note that exception may actually be an Http404 or similar.
        if request.ajax:
            err = "An error occurred: %s" % (exception, )
            logging.exception("Middleware caught an exception")
            return PopupException(err, detail=None).response(request)

        return None
コード例 #9
0
    def process_view(self, request, view_func, view_args, view_kwargs):
        """
    We also perform access logging in ``process_view()`` since we have the view function,
    which tells us the log level. The downside is that we don't have the status code,
    which isn't useful for status logging anyways.
    """
        access_log_level = getattr(view_func, 'access_log_level', None)
        # First, skip views not requiring login

        # If the view has "opted out" of login required, skip
        if hasattr(view_func, "login_notrequired"):
            log_page_hit(request,
                         view_func,
                         level=access_log_level or logging.DEBUG)
            return None

        # There are certain django views which are also opt-out, but
        # it would be evil to go add attributes to them
        if view_func in DJANGO_VIEW_AUTH_WHITELIST:
            log_page_hit(request,
                         view_func,
                         level=access_log_level or logging.DEBUG)
            return None

        # If user is logged in, check that he has permissions to access the
        # app.
        if request.user.is_active and request.user.is_authenticated():
            AppSpecificMiddleware.augment_request_with_app(request, view_func)
            if request._desktop_app and \
                request._desktop_app != "desktop" and \
                not request.user.has_desktop_permission(action="access", app=request._desktop_app):
                access_log(request,
                           'permission denied',
                           level=access_log_level)
                return PopupException(
                    "You do not have permission to access the %s application."
                    % (request._desktop_app.capitalize())).response(request)
            else:
                log_page_hit(request, view_func, level=access_log_level)
                return None

        logging.info("Redirecting to login page: %s", request.get_full_path())
        access_log(request, 'login redirection', level=access_log_level)
        if request.ajax:
            # Send back a magic header which causes Hue.Request to interpose itself
            # in the ajax request and make the user login before resubmitting the
            # request.
            response = HttpResponse("/* login required */",
                                    content_type="text/javascript")
            response[MIDDLEWARE_HEADER] = 'LOGIN_REQUIRED'
            return response
        else:
            return HttpResponseRedirect(
                "%s?%s=%s" % (settings.LOGIN_URL, REDIRECT_FIELD_NAME,
                              urlquote(request.get_full_path())))
コード例 #10
0
def _peek_file(fs, file_form):
    """_peek_file(fs, file_form) -> (path, initial data)"""
    try:
        path = file_form.cleaned_data['path']
        file_obj = fs.open(path)
        file_head = file_obj.read(IMPORT_PEEK_SIZE)
        file_obj.close()
        return (path, file_head)
    except IOError, ex:
        msg = "Failed to open file '%s': %s" % (path, ex)
        LOG.exception(msg)
        raise PopupException(msg)
コード例 #11
0
def delete_user(request, username):
  if not request.user.is_superuser:
    raise PopupException("You must be a superuser to delete users.")
  if request.method == 'POST':
    try:
      global __users_lock
      __users_lock.acquire()
      try:
        user = User.objects.get(username=username)
        _check_remove_last_super(user)
        user.delete()
      finally:
        __users_lock.release()

      # Send a flash message saying "deleted"?
      return list_users(request)
    except User.DoesNotExist:
      raise PopupException("User not found.")
  else:
    return render("confirm.mako",
      request,
      dict(path=request.path, title="Delete user?"))
コード例 #12
0
def _check_remove_last_super(user_obj):
  """Raise an error if we're removing the last superuser"""
  if not user_obj.is_superuser:
    return

  # Is there any other active superuser left?
  all_active_su = User.objects.filter(is_superuser__exact = True,
                                      is_active__exact = True)
  num_active_su = all_active_su.count()
  assert num_active_su >= 1, "No active superuser configured"
  if num_active_su == 1:
    raise PopupException("You cannot remove the last active "
                         "superuser from the configuration.")
コード例 #13
0
def view_source(request, path):
    """
  Handle view source requests.
  """
    gallery_name, extension = os.path.splitext(path)
    gallery_name = gallery_name.capitalize().replace('.',
                                                     ' ').replace('_', ' ')

    # Load the template source
    filename = os.path.join(_CWD, 'templates', _GALLERY_DIR, path)
    try:
        data = format_code(extension, file(filename).read())
    except OSError, ex:
        raise PopupException("Cannot read requested gallery template: %s" %
                             (path, ))
コード例 #14
0
def sleeper(req):

    seconds = False
    if req.REQUEST.get('delay'):
        seconds = int(req.REQUEST.get('delay'))
    if req.REQUEST.get('sleep'):
        seconds = int(req.REQUEST.get('sleep'))

    if seconds:
        # To prevent a DOS, only one thread can sleep at a time
        if not _sleep_lock.acquire(False):
            raise PopupException(
                "Only one sleep request at a time. Please try again")
        try:
            time.sleep(seconds)
        finally:
            _sleep_lock.release()
コード例 #15
0
def listdir(request, path):
    """
  Implements directory listing (or index).

  Intended to be called via view().
  """
    path = _unquote_path(path)
    if not request.fs.isdir(path):
        raise PopupException("Not a directory: %s" % (path, ))

    file_filter = request.REQUEST.get('file_filter', 'any')

    assert file_filter in ['any', 'file', 'dir']

    home_dir_path = request.user.get_home_directory()
    data = {
        'path':
        path,
        'file_filter':
        file_filter,
        # These could also be put in automatically via
        # http://docs.djangoproject.com/en/dev/ref/templates/api/#django-core-context-processors-request,
        # but manually seems cleaner, since we only need it here.
        'current_request_path':
        request.path,
        'home_directory':
        request.fs.isdir(home_dir_path) and home_dir_path or None,
        'cwd_set':
        True,
        'show_upload':
        (request.REQUEST.get('show_upload') == 'false' and (False, )
         or (True, ))[0]
    }

    stats = request.fs.listdir_stats(path)

    # Include parent dir, unless at filesystem root.
    if normpath(path) != posixpath.sep:
        parent_stat = request.fs.stats(posixpath.join(path, ".."))
        # The 'path' field would be absolute, but we want its basename to be
        # actually '..' for display purposes. Encode it since _massage_stats expects byte strings.
        parent_stat['path'] = posixpath.join(path, "..")
        stats.insert(0, parent_stat)

    data['files'] = [_massage_stats(request, stat) for stat in stats]
    return render_with_toolbars('listdir.mako', request, data)
コード例 #16
0
def view(request, app, path):
    """
  Views and renders a file at a given path.

  Markdown files are parsed through markdown; others
  are just pasted in <pre>'s.

  TODO: Expose a way to do images.
  """

    path = _unquote_path(path)
    fs = get_help_fs(app)

    if fs.isdir(path):
        for i in INDEX_FILENAMES:
            tmp_path = os.path.join(path, i)
            if fs.isfile(tmp_path):
                path = tmp_path

    if not fs.isfile(path):
        raise PopupException("Could not find or read the file: %s (app %s)" %
                             (path, app))

    content = fs.open(path, 'r').read()
    content = unicode(content, 'utf-8', errors='replace')
    if path.lower().endswith(".md"):
        content = ('<div class="print rendered-markdown">' +
                   markdown.markdown(content, ['extra']) + '</div>')
    elif path.lower().endswith(".html"):
        content = '<div class="print">%s</div>' % (content, )
    else:
        # TODO(todd) escape content?
        content = '<pre>' + content + '</pre>'

    data = {
        'content':
        content,
        'apps':
        sorted([x for x in appmanager.DESKTOP_MODULES if x.help_dir],
               key=lambda x: x.nice_name.lower()),
        'title':
        appmanager.get_desktop_module(app).nice_name
    }
    return render("display.mako", request, data)
コード例 #17
0
def _delim_preview(fs, file_form, encoding, file_types, delimiters):
    """
  _delim_preview(fs, file_form, encoding, file_types, delimiters)
                              -> (fields_list, n_cols, delim_form)

  Look at the beginning of the file and parse it according to the list of
  available file_types and delimiters.
  """
    assert file_form.is_valid()

    path = file_form.cleaned_data['path']
    try:
        file_obj = fs.open(path)
        delim, file_type, fields_list = _parse_fields(path, file_obj, encoding,
                                                      file_types, delimiters)
        file_obj.close()
    except IOError, ex:
        msg = "Failed to open file '%s': %s" % (path, ex)
        LOG.exception(msg)
        raise PopupException(msg)
コード例 #18
0
def load_after_create(request):
    """
  Automatically load data into a newly created table.

  We get here from the create's on_success_url, and expect to find
  ``table`` and ``path`` from the parameters.
  """
    tablename = request.REQUEST.get('table')
    path = request.REQUEST.get('path')
    if not tablename or not path:
        msg = 'Internal error: Missing needed parameter to load data into table'
        LOG.error(msg)
        raise PopupException(msg)

    LOG.debug("Auto loading data from %s into table %s" % (path, tablename))
    hql = "LOAD DATA INPATH '%s' INTO TABLE `%s`" % (path, tablename)
    query_msg = make_beeswax_query(request, hql)
    on_success_url = urlresolvers.reverse(describe_table,
                                          kwargs={'table': tablename})

    return execute_directly(request, query_msg, on_success_url=on_success_url)
コード例 #19
0
class SavedQuery(models.Model):
    """
  Stores the query/report that people have save or submitted.

  Note that this used to be called QueryDesign. Any references to 'design'
  probably mean a SavedQuery.
  """
    DEFAULT_NEW_DESIGN_NAME = 'My saved query'
    AUTO_DESIGN_SUFFIX = ' (new)'
    TYPES = (HQL, REPORT) = range(2)

    type = models.IntegerField(null=False)
    owner = models.ForeignKey(User, db_index=True)
    # Data is a json of dictionary. See the beeswax.design module.
    data = models.TextField(max_length=65536)
    name = models.CharField(max_length=64)
    desc = models.TextField(max_length=1024)
    mtime = models.DateTimeField(auto_now=True)
    # An auto design is a place-holder for things users submit but not saved.
    # We still want to store it as a design to allow users to save them later.
    is_auto = models.BooleanField(default=False, db_index=True)

    class Meta:
        ordering = ['-mtime']

    def clone(self):
        """clone() -> A new SavedQuery with a deep copy of the same data"""
        design = SavedQuery(type=self.type, owner=self.owner)
        design.data = copy.deepcopy(self.data)
        design.name = copy.deepcopy(self.name)
        design.desc = copy.deepcopy(self.desc)
        design.is_auto = copy.deepcopy(self.is_auto)
        return design

    @staticmethod
    def get(id, owner=None, type=None):
        """
    get(id, owner=None, type=None) -> SavedQuery object

    Checks that the owner and type match (when given).
    May raise PopupException (type/owner mismatch).
    May raise SavedQuery.DoesNotExist.
    """
        try:
            design = SavedQuery.objects.get(id=id)
        except SavedQuery.DoesNotExist, err:
            msg = 'Cannot retrieve Beeswax design id %s' % (id, )
            raise err

        if owner is not None and design.owner != owner:
            msg = 'Design id %s does not belong to user %s' % (id, owner)
            LOG.error(msg)
            raise PopupException(msg)

        if type is not None and design.type != type:
            msg = 'Type mismatch for design id %s (owner %s) - Expects %s got %s' % \
                  (id, owner, design.type, type)
            LOG.error(msg)
            raise PopupException(msg)

        return design
コード例 #20
0
def error_popup_exception(request):
    sleeper(request)
    raise PopupException("This is a popup exception.",
                         title="Hey there",
                         detail="Some detail")
コード例 #21
0
def edit(request, path, form=None):
    """Shows an edit form for the given path. Path does not necessarily have to exist."""

    try:
        stats = request.fs.stats(path)
    except IOError, ioe:
        # A file not found is OK, otherwise re-raise
        if ioe.errno == errno.ENOENT:
            stats = None
        else:
            raise

    # Can't edit a directory
    if stats and stats['mode'] & stat_module.S_IFDIR:
        raise PopupException("Cannot edit a directory: %s" % (path, ))

    # Maximum size of edit
    if stats and stats['size'] > MAX_FILEEDITOR_SIZE:
        raise PopupException("File too big to edit: %s" % (path, ))

    if not form:
        encoding = request.REQUEST.get('encoding') or i18n.get_site_encoding()
        if stats:
            f = request.fs.open(path)
            try:
                try:
                    current_contents = unicode(f.read(), encoding)
                except UnicodeDecodeError:
                    raise PopupException(
                        "File is not encoded in %s; cannot be edited: %s" %
コード例 #22
0
 def create_table(self, schema, table, columns, tablespace=None):
     c = self._cursor()
     try:
         c.execute(db2_create_table(schema, table, columns, tablespace))
     except Exception as e:
         raise PopupException(str(e), title="DB2 Client Error")
コード例 #23
0
        data = format_code(extension, file(filename).read())
    except OSError, ex:
        raise PopupException("Cannot read requested gallery template: %s" %
                             (path, ))

    # Load the js references
    js_data = {}  # map of { name: js content }
    yml_file = os.path.splitext(filename)[0] + '.yml'
    if os.path.exists(yml_file):
        yml = yaml.load(file(yml_file))
        try:
            for ref in yml['js-references']:
                try:
                    js_pkg, js_comp = ref.split('/')
                except ValueError:
                    raise PopupException('Invalid line "%s" in file %s' %
                                         (ref, yml_file))
                try:
                    file_data = dep.get((js_pkg, js_comp))
                    js_data[ref] = format_code('.js', file_data.content)
                except:
                    raise PopupException(
                        'Cannot locate "%s" package "%s" component' %
                        (js_pkg, js_comp))
        except KeyError, ex:
            LOG.warn('%s does not have a "js-references" section' %
                     (yml_file, ))

    return render('source.mako', request, {
        'data': data,
        'name': gallery_name,
        'js_data': js_data
コード例 #24
0
 def decorated(request, *args, **kwargs):
   if not request.user.has_desktop_permission(action, app):
     raise PopupException("Permission denied (%s/%s)" % (action, app))
   return view_func(request, *args, **kwargs)
コード例 #25
0
def display(request, path):
    """
  Implements displaying part of a file.

  GET arguments are length, offset, mode, compression and encoding
  with reasonable defaults chosen.

  Note that display by length and offset are on bytes, not on characters.

  TODO(philip): Could easily built-in file type detection
  (perhaps using something similar to file(1)), as well
  as more advanced binary-file viewing capability (de-serialize
  sequence files, decompress gzipped text files, etc.).
  There exists a python-magic package to interface with libmagic.
  """
    path = _unquote_path(path)
    if not request.fs.isfile(path):
        raise PopupException("Not a file: '%s'" % (path, ))

    stats = request.fs.stats(path)
    encoding = request.GET.get('encoding') or i18n.get_site_encoding()

    # I'm mixing URL-based parameters and traditional
    # HTTP GET parameters, since URL-based parameters
    # can't naturally be optional.

    # Need to deal with possibility that length is not present
    # because the offset came in via the toolbar manual byte entry.
    end = request.GET.get("end")
    if end:
        end = int(end)
    begin = request.GET.get("begin", 1)
    if begin:
        # Subtract one to zero index for file read
        begin = int(begin) - 1
    if end:
        offset = begin
        length = end - begin
        if begin >= end:
            raise PopupException(
                "First byte to display must be before last byte to display.")
    else:
        length = int(request.GET.get("length", DEFAULT_CHUNK_SIZE_BYTES))
        # Display first block by default.
        offset = int(request.GET.get("offset", 0))

    mode = request.GET.get("mode")
    compression = request.GET.get("compression")

    if mode and mode not in ["binary", "text"]:
        raise PopupException("Mode must be one of 'binary' or 'text'.")
    if offset < 0:
        raise PopupException("Offset may not be less than zero.")
    if length < 0:
        raise PopupException("Length may not be less than zero.")
    if length > MAX_CHUNK_SIZE_BYTES:
        raise PopupException("Cannot request chunks greater than %d bytes" %
                             MAX_CHUNK_SIZE_BYTES)

    # Auto gzip detection, unless we are explicitly told to view binary
    if not compression and mode != 'binary':
        if path.endswith('.gz') and detect_gzip(request.fs.open(path).read(2)):
            compression = 'gzip'
            offset = 0
        else:
            compression = 'none'

    f = request.fs.open(path)

    if compression == 'gzip':
        if offset and offset != 0:
            raise PopupException(
                "We don't support offset and gzip Compression")
        try:
            try:
                contents = GzipFile('', 'r', 0,
                                    StringIO(f.read())).read(length)
            except:
                logging.warn("Could not decompress file at %s" % path,
                             exc_info=True)
                contents = ''
                raise PopupException("Failed to decompress file")
        finally:
            f.close()
    else:
        try:
            f.seek(offset)
            contents = f.read(length)
        finally:
            f.close()

    # Get contents as string for text mode, or at least try
    uni_contents = None
    if not mode or mode == 'text':
        uni_contents = unicode(contents, encoding, errors='replace')
        is_binary = uni_contents.find(i18n.REPLACEMENT_CHAR) != -1
        # Auto-detect mode
        if not mode:
            mode = is_binary and 'binary' or 'text'

    # Get contents as bytes
    if mode == "binary":
        xxd_out = list(
            xxd.xxd(offset, contents, BYTES_PER_LINE, BYTES_PER_SENTENCE))

    dirname = posixpath.dirname(path)
    # Start with index-like data:
    data = _massage_stats(request, request.fs.stats(path))
    # And add a view structure:
    data["success"] = True
    data["view"] = {
        'offset': offset,
        'length': length,
        'end': offset + len(contents),
        'dirname': dirname,
        'mode': mode,
        'compression': compression,
        'size': stats['size']
    }
    data["filename"] = os.path.basename(path)
    data["editable"] = stats['size'] < MAX_FILEEDITOR_SIZE
    if mode == "binary":
        # This might be the wrong thing for ?format=json; doing the
        # xxd'ing in javascript might be more compact, or sending a less
        # intermediate representation...
        logger.debug("xxd: " + str(xxd_out))
        data['view']['xxd'] = xxd_out
        data['view']['masked_binary_data'] = False
    else:
        data['view']['contents'] = uni_contents
        data['view']['masked_binary_data'] = is_binary

    return render_with_toolbars("display.mako", request, data)