[libsoup] soup-directory-input-stream: Redesign directory listing layout



commit 5bdb5caed2f729a6a1844cc3020c4d4ed278db62
Author: Jan-Michael Brummer <jan brummer tabos org>
Date:   Fri May 29 23:04:17 2020 +0200

    soup-directory-input-stream: Redesign directory listing layout
    
    - Fixes encoding issues on file names
    - Adds sorting support
    - Adds translations
    - Add CSS for nicer design
    
    Closes !123

 libsoup/directory.css                 | 70 ++++++++++++++++++++++++++++++++
 libsoup/directory.js                  | 76 +++++++++++++++++++++++++++++++++++
 libsoup/meson.build                   |  7 ++++
 libsoup/soup-directory-input-stream.c | 72 ++++++++++++++++++++++++++++-----
 libsoup/soup.gresource.xml            |  7 ++++
 po/POTFILES.in                        |  1 +
 6 files changed, 223 insertions(+), 10 deletions(-)
---
diff --git a/libsoup/directory.css b/libsoup/directory.css
new file mode 100644
index 00000000..82e86516
--- /dev/null
+++ b/libsoup/directory.css
@@ -0,0 +1,70 @@
+:root {
+  background-color: #f6f5f4;
+}
+
+a {
+  text-decoration: none;
+}
+
+a:hover {
+  text-decoration: underline;
+}
+
+table {
+  width: 100%;
+  table-layout: fixed;
+  margin: 0 auto;
+}
+
+th > a {
+  color: inherit;
+}
+
+table[order] > thead > tr > th::after {
+  display: none;
+  width: .8em;
+  margin-inline-end: -.8em;
+  text-align: end;
+}
+
+table[order="asc"] > thead > tr > th::after {
+  content: "\2193";
+}
+table[order="desc"] > thead > tr > th::after {
+  content: "\2191";
+}
+
+table[order][order-by="0"] > thead > tr > th:first-child > a ,
+table[order][order-by="1"] > thead > tr > th:first-child + th > a ,
+table[order][order-by="2"] > thead > tr > th:first-child + th + th > a {
+  text-decoration: underline;
+}
+
+table[order][order-by="0"] > thead > tr > th:first-child::after ,
+table[order][order-by="1"] > thead > tr > th:first-child + th::after ,
+table[order][order-by="2"] > thead > tr > th:first-child + th + th::after {
+  display: inline-block;
+}
+
+td:first-child {
+  max-width: 100%;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+tr:nth-child(even) {
+  background-color: #f2f2f2;
+}
+
+@media (min-width: 550px) {
+  body {
+    padding: 3em;
+    background-color: white;
+    border-radius: 1em;
+    border: 2px solid rgba(211, 215, 207);
+    max-width: 65em;
+    margin: 2em auto;
+  }
+}
+
diff --git a/libsoup/directory.js b/libsoup/directory.js
new file mode 100644
index 00000000..b1380bd8
--- /dev/null
+++ b/libsoup/directory.js
@@ -0,0 +1,76 @@
+
+/* This code is based on Firefox directory listing, distributed under
+ * Mozilla Public License.
+ */
+
+'use strict';
+
+var gTable, gOrderBy, gTBody, gRows;
+
+document.addEventListener("DOMContentLoaded", function() {
+  gTable = document.getElementsByTagName("table")[0];
+  gTBody = gTable.tBodies[0];
+  if (gTBody.rows.length < 2)
+    return;
+  var headCells = gTable.tHead.rows[0].cells;
+
+  function rowAction(i) {
+    return function(event) {
+      event.preventDefault();
+      orderBy(i);
+    }
+  }
+
+  for (var i = headCells.length - 1; i >= 0; i--) {
+    var anchor = document.createElement("a");
+    anchor.href = "";
+    anchor.appendChild(headCells[i].firstChild);
+    headCells[i].appendChild(anchor);
+    headCells[i].addEventListener("click", rowAction(i), true);
+  }
+  gTable.setAttribute("order", "");
+  orderBy(0);
+}, "false");
+
+function compareRows(rowA, rowB) {
+  var a = rowA.cells[gOrderBy].getAttribute("sortable-data") || rowA.cells[gOrderBy].innerHTML;
+  var b = rowB.cells[gOrderBy].getAttribute("sortable-data") || rowB.cells[gOrderBy].innerHTML;
+  var intA = +a;
+  var intB = +b;
+  if (a == intA && b == intB) {
+    a = intA;
+    b = intB;
+  } else {
+    a = a.toLowerCase();
+    b = b.toLowerCase();
+  }
+  if (a < b)
+    return -1;
+  if (a > b)
+    return 1;
+  return 0;
+}
+
+function orderBy(column) {
+  if (!gRows)
+    gRows = Array.from(gTBody.rows);
+  var order;
+  if (gOrderBy == column) {
+    order = gTable.getAttribute("order") == "asc" ? "desc" : "asc";
+  } else {
+    order = "asc";
+    gOrderBy = column;
+    gTable.setAttribute("order-by", column);
+    gRows.sort(compareRows);
+  }
+  gTable.removeChild(gTBody);
+  gTable.setAttribute("order", order);
+  if (order == "asc")
+    for (var i = 0; i < gRows.length; i++)
+      gTBody.appendChild(gRows[i]);
+  else
+    for (var i = gRows.length - 1; i >= 0; i--)
+      gTBody.appendChild(gRows[i]);
+  gTable.appendChild(gTBody);
+}
+
diff --git a/libsoup/meson.build b/libsoup/meson.build
index 73bb1188..613d68a4 100644
--- a/libsoup/meson.build
+++ b/libsoup/meson.build
@@ -1,6 +1,13 @@
 pkg = import('pkgconfig')
 
+resources = gnome.compile_resources('soup-resources',
+    'soup.gresource.xml',
+    c_name: 'soup',
+    source_dir: '.'
+)
+
 soup_sources = [
+  resources,
   'soup-address.c',
   'soup-auth.c',
   'soup-auth-basic.c',
diff --git a/libsoup/soup-directory-input-stream.c b/libsoup/soup-directory-input-stream.c
index e0ccfd90..43bc1d9f 100644
--- a/libsoup/soup-directory-input-stream.c
+++ b/libsoup/soup-directory-input-stream.c
@@ -25,11 +25,12 @@
 
 #include <string.h>
 
+#include <glib/gi18n-lib.h>
+
 #include "soup-directory-input-stream.h"
 #include "soup.h"
 
-#define INIT_STRING "<html>\n<body>\n<table><th align=\"left\">Name</th><th>Size</th><th>Date 
Modified</th>\n"
-#define ROW_FORMAT  "<td><a href=\"%s\">%s</a></td><td align=\"right\">%s</td><td align=\"right\" 
margin=8>%s</td>\n"
+#define ROW_FORMAT  "<td sortable-data=\"%s\"><a href=\"%s\">%s</a></td><td align=\"right\" 
sortable-data=\"%ld\">%s</td><td align=\"right\" sortable-data=\"%ld\">%s&ensp;%s</td>\n"
 #define EXIT_STRING "</table>\n</html>\n"
 
 G_DEFINE_TYPE (SoupDirectoryInputStream, soup_directory_input_stream, G_TYPE_INPUT_STREAM)
@@ -41,7 +42,9 @@ soup_directory_input_stream_parse_info (SoupDirectoryInputStream *stream,
        SoupBuffer *buffer;
        GString *string;
        const char *file_name;
-       char *escaped, *path, *xml_string, *size, *time;
+       char *escaped, *path, *xml_string, *size, *date, *time, *name;
+       goffset raw_size;
+       gint64 timestamp;
 #if !GLIB_CHECK_VERSION (2, 61, 2)
        GTimeVal modified;
 #endif
@@ -62,21 +65,35 @@ soup_directory_input_stream_parse_info (SoupDirectoryInputStream *stream,
        xml_string = g_markup_escape_text (file_name, -1);
        escaped = g_uri_escape_string (file_name, NULL, FALSE);
        path = g_strconcat (stream->uri, G_DIR_SEPARATOR_S, escaped, NULL);
-       size = g_format_size (g_file_info_get_size (info));
+       raw_size = g_file_info_get_size (info);
+
+       if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR)
+               size = g_format_size (raw_size);
+       else
+               size = g_strdup("");
+
+       if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
+               name = g_strdup_printf("1.%s", path);
+       else
+               name = g_strdup_printf("%s", path);
+
 #if GLIB_CHECK_VERSION (2, 61, 2)
        modification_time = g_file_info_get_modification_date_time (info);
 #else
        g_file_info_get_modification_time (info, &modified);
        modification_time = g_date_time_new_from_timeval_local (&modified);
 #endif
-       time = g_date_time_format (modification_time, "%X %x");
+       time = g_date_time_format (modification_time, "%X");
+       date = g_date_time_format (modification_time, "%x");
+       timestamp = g_date_time_to_unix (modification_time);
        g_date_time_unref (modification_time);
 
-       g_string_append_printf (string, ROW_FORMAT, path, xml_string, size, time);
+       g_string_append_printf (string, ROW_FORMAT, name, path, xml_string, raw_size, size, timestamp, time, 
date);
        g_string_append (string, "</tr>\n");
        buffer = soup_buffer_new (SOUP_MEMORY_TAKE, string->str, string->len);
 
        g_free (time);
+       g_free (date);
        g_free (escaped);
        g_free (size);
        g_free (path);
@@ -190,12 +207,46 @@ soup_directory_input_stream_class_init (SoupDirectoryInputStreamClass *stream_cl
        inputstream_class->close_fn = soup_directory_input_stream_close;
 }
 
+static
+char *soup_directory_input_stream_create_header (SoupDirectoryInputStream *stream)
+{
+       char *header;
+       GBytes *css = g_resources_lookup_data ("/org/gnome/libsoup/directory.css", 
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
+       GBytes *js = g_resources_lookup_data ("/org/gnome/libsoup/directory.js", 
G_RESOURCE_LOOKUP_FLAGS_NONE, NULL);
+
+       header = g_strdup_printf ("<html><head>" \
+                            "<title>%s</title>" \
+                            "<meta http-equiv=\"Content-Type\" content=\"text/html;\" charset=\"UTF-8\">" \
+                            "<style>%s</style>" \
+                            "<script>%s</script>" \
+                            "</head>" \
+                            "<body>" \
+                            "<table>" \
+                            "<thead>" \
+                            "<th align=\"left\">%s</th><th align=\"right\">%s</th><th 
align=\"right\">%s</th>" \
+                            "</thead>",
+                            stream->uri,
+                            css ? (gchar *)g_bytes_get_data (css, NULL) : "",
+                            js ? (gchar *)g_bytes_get_data (js, NULL) : "",
+                            _("Name"),
+                            _("Size"),
+                            _("Date Modified"));
+       return header;
+}
+
 static void
 soup_directory_input_stream_init (SoupDirectoryInputStream *stream)
 {
-       stream->buffer = soup_buffer_new (SOUP_MEMORY_STATIC,
-                                         INIT_STRING,
-                                         sizeof (INIT_STRING));
+}
+
+static void
+soup_directory_input_stream_setup_buffer (SoupDirectoryInputStream *stream)
+{
+       char *init = soup_directory_input_stream_create_header (stream);
+
+       stream->buffer = soup_buffer_new (SOUP_MEMORY_TAKE,
+                                         init,
+                                         strlen (init));
 }
 
 GInputStream *
@@ -212,6 +263,7 @@ soup_directory_input_stream_new (GFileEnumerator *enumerator,
        SOUP_DIRECTORY_INPUT_STREAM (stream)->enumerator = g_object_ref (enumerator);
        SOUP_DIRECTORY_INPUT_STREAM (stream)->uri = soup_uri_to_string (uri, FALSE);
 
+       soup_directory_input_stream_setup_buffer (SOUP_DIRECTORY_INPUT_STREAM (stream));
+
        return stream;
 }
-
diff --git a/libsoup/soup.gresource.xml b/libsoup/soup.gresource.xml
new file mode 100644
index 00000000..da3ad10b
--- /dev/null
+++ b/libsoup/soup.gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/libsoup">
+    <file>directory.css</file>
+    <file>directory.js</file>
+  </gresource>
+</gresources>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3cd20bf4..61d52750 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,5 +1,6 @@
 libsoup/soup-body-input-stream.c
 libsoup/soup-cache-input-stream.c
+libsoup/soup-directory-input-stream.c
 libsoup/soup-converter-wrapper.c
 libsoup/soup-message-client-io.c
 libsoup/soup-message-io.c


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]