Blame SOURCES/gnome-boxes-hardcode-recommended-oses.patch

b0d0d0
From b874d9355644eb686a8af9df03884a0a19513059 Mon Sep 17 00:00:00 2001
b0d0d0
From: Felipe Borges <felipeborges@gnome.org>
b0d0d0
Date: Tue, 13 Nov 2018 14:26:12 +0100
b0d0d0
Subject: [PATCH] wizard-downloads: Load recommended downloads from an XML file
b0d0d0
b0d0d0
This way downstreams (vendors, distros) could easily tweak the
b0d0d0
list and offer the Osinfo downloads that they prefer, with the
b0d0d0
sorting they want.
b0d0d0
b0d0d0
Cherry-picked from eb3af034b5cda6ce1fa6812624b531ea53f90f72
b0d0d0
---
b0d0d0
 data/gnome-boxes.gresource.xml |  1 +
b0d0d0
 data/recommended-downloads.xml | 18 +++++++
b0d0d0
 data/ui/wizard-source.ui       |  1 -
b0d0d0
 src/util-app.vala              | 80 ++++++++++++++++++++++++++++
b0d0d0
 src/wizard-downloads-page.vala | 95 ++++++++++++++++++++++++++++++++++
b0d0d0
 src/wizard-source.vala         | 33 ++++--------
b0d0d0
 6 files changed, 204 insertions(+), 24 deletions(-)
b0d0d0
 create mode 100644 data/recommended-downloads.xml
b0d0d0
 create mode 100644 src/wizard-downloads-page.vala
b0d0d0
b0d0d0
diff --git a/data/gnome-boxes.gresource.xml b/data/gnome-boxes.gresource.xml
b0d0d0
index 01c72d59..8a9b8b95 100644
b0d0d0
--- a/data/gnome-boxes.gresource.xml
b0d0d0
+++ b/data/gnome-boxes.gresource.xml
b0d0d0
@@ -3,6 +3,7 @@
b0d0d0
   <gresource prefix="/org/gnome/Boxes">
b0d0d0
     <file>gtk-style.css</file>
b0d0d0
     <file preprocess="xml-stripblanks" alias="gtk/menus.ui">ui/menus.ui</file>
b0d0d0
+    <file preprocess="xml-stripblanks">recommended-downloads.xml</file>
b0d0d0
     <file>icons/boxes-arrow.svg</file>
b0d0d0
     <file>icons/boxes-create.png</file>
b0d0d0
     <file>icons/empty-boxes.png</file>
b0d0d0
diff --git a/data/recommended-downloads.xml b/data/recommended-downloads.xml
b0d0d0
new file mode 100644
b0d0d0
index 00000000..b389e945
b0d0d0
--- /dev/null
b0d0d0
+++ b/data/recommended-downloads.xml
b0d0d0
@@ -0,0 +1,18 @@
b0d0d0
+
b0d0d0
+
b0d0d0
+  These are OSes listed in the recommended section of the "Download an OS" page.
b0d0d0
+
b0d0d0
+  This list is powered by libosinfo, therefore the URLs are unique identifiers
b0d0d0
+  for each OS in osinfo-db.
b0d0d0
+
b0d0d0
+  Downstreams are encouraged to tweak the list as they wish. Sorting is also
b0d0d0
+  available.
b0d0d0
+ -->
b0d0d0
+<list>
b0d0d0
+  <os_id>http://redhat.com/rhel/7.6</os_id>
b0d0d0
+  <os_id>http://fedoraproject.org/fedora/29</os_id>
b0d0d0
+  <os_id>http://fedoraproject.org/silverblue/29</os_id>
b0d0d0
+  <os_id>http://ubuntu.com/ubuntu/18.10</os_id>
b0d0d0
+  <os_id>http://opensuse.org/opensuse/15.0</os_id>
b0d0d0
+  <os_id>http://debian.org/debian/9</os_id>
b0d0d0
+</list>
b0d0d0
diff --git a/data/ui/wizard-source.ui b/data/ui/wizard-source.ui
b0d0d0
index b59fccfc..6762d2d6 100644
b0d0d0
--- a/data/ui/wizard-source.ui
b0d0d0
+++ b/data/ui/wizard-source.ui
b0d0d0
@@ -49,7 +49,6 @@
b0d0d0
         <child>
b0d0d0
           <object class="BoxesWizardScrolled" id="downloads_scrolled">
b0d0d0
             <property name="visible">False</property>
b0d0d0
-            <signal name="show" handler="on_downloads_scrolled_shown"/>
b0d0d0
           </object>
b0d0d0
         </child>
b0d0d0
 
b0d0d0
diff --git a/src/util-app.vala b/src/util-app.vala
b0d0d0
index aba87cfd..253d1b74 100644
b0d0d0
--- a/src/util-app.vala
b0d0d0
+++ b/src/util-app.vala
b0d0d0
@@ -102,6 +102,86 @@ public void fetch_os_logo (Gtk.Image image, Osinfo.Os os, int size) {
b0d0d0
         }
b0d0d0
     }
b0d0d0
 
b0d0d0
+    public string serialize_os_title (Osinfo.Media media) {
b0d0d0
+        var title = "unknown";
b0d0d0
+
b0d0d0
+        /* Libosinfo lacks some OS variant names, so we do some
b0d0d0
+           parsing here to compose a unique human-readable media
b0d0d0
+           identifier. */
b0d0d0
+        var variant = "";
b0d0d0
+        var variants = media.get_os_variants ();
b0d0d0
+        if (variants.get_length () > 0)
b0d0d0
+            variant = (variants.get_nth (0) as Osinfo.OsVariant).get_name ();
b0d0d0
+        else if ((media.os as Osinfo.Product).name != null) {
b0d0d0
+            variant = (media.os as Osinfo.Product).name;
b0d0d0
+            if (media.url != null && media.url.contains ("server"))
b0d0d0
+                variant += " Server";
b0d0d0
+        } else {
b0d0d0
+            var file = File.new_for_uri (media.url);
b0d0d0
+
b0d0d0
+            title = file.get_basename ().replace ("_", "");
b0d0d0
+        }
b0d0d0
+
b0d0d0
+        var subvariant = "";
b0d0d0
+
b0d0d0
+        if (media.url != null) {
b0d0d0
+            if (media.url.contains ("netinst"))
b0d0d0
+                subvariant = "(netinst)";
b0d0d0
+            else if (media.url.contains ("minimal"))
b0d0d0
+                subvariant = "(minimal)";
b0d0d0
+            else if (media.url.contains ("dvd"))
b0d0d0
+                subvariant = "(DVD)";
b0d0d0
+        }
b0d0d0
+
b0d0d0
+        var is_live = media.live ? " (" + _("Live") + ")" : "";
b0d0d0
+
b0d0d0
+        title = @"$variant $(media.architecture) $subvariant $is_live";
b0d0d0
+
b0d0d0
+        /* Strip consequent whitespaces */
b0d0d0
+        return title.replace ("  ", "");
b0d0d0
+    }
b0d0d0
+
b0d0d0
+    public async GLib.List<Osinfo.Media>? get_recommended_downloads () {
b0d0d0
+        uint8[] contents;
b0d0d0
+
b0d0d0
+        try {
b0d0d0
+            File file = File.new_for_uri ("resource:///org/gnome/Boxes/recommended-downloads.xml");
b0d0d0
+
b0d0d0
+            file.load_contents (null, out contents, null);
b0d0d0
+        } catch (GLib.Error e) {
b0d0d0
+            warning ("Failed to load recommended downloads file: %s", e.message);
b0d0d0
+
b0d0d0
+            return null;
b0d0d0
+        }
b0d0d0
+
b0d0d0
+        Xml.Doc* doc = Xml.Parser.parse_doc ((string)contents);
b0d0d0
+        if (doc == null)
b0d0d0
+            return null;
b0d0d0
+
b0d0d0
+        Xml.Node* root = doc->get_root_element ();
b0d0d0
+        if (root == null || root->name != "list") {
b0d0d0
+            warning ("Failed to parse recommended downloads");
b0d0d0
+
b0d0d0
+            return null;
b0d0d0
+        }
b0d0d0
+
b0d0d0
+        GLib.List<Osinfo.Media> list = new GLib.List<Osinfo.Media> ();
b0d0d0
+        var os_db = MediaManager.get_instance ().os_db;
b0d0d0
+        for (Xml.Node* iter = root->children; iter != null; iter = iter->next) {
b0d0d0
+            var os_id = iter->get_content ();
b0d0d0
+            try {
b0d0d0
+                var os = yield os_db.get_os_by_id (os_id);
b0d0d0
+                var media = os.get_media_list ().get_nth (0) as Osinfo.Media;
b0d0d0
+
b0d0d0
+                list.append (media);
b0d0d0
+            } catch (OSDatabaseError error) {
b0d0d0
+                warning ("Failed to find OS with id: '%s': %s", os_id, error.message);
b0d0d0
+            }
b0d0d0
+        }
b0d0d0
+
b0d0d0
+        return list;
b0d0d0
+    }
b0d0d0
+
b0d0d0
     public async GVir.StoragePool ensure_storage_pool (GVir.Connection connection) throws GLib.Error {
b0d0d0
         var pool = get_storage_pool (connection);
b0d0d0
         if (pool == null) {
b0d0d0
diff --git a/src/wizard-downloads-page.vala b/src/wizard-downloads-page.vala
b0d0d0
new file mode 100644
b0d0d0
index 00000000..0b77a9cb
b0d0d0
--- /dev/null
b0d0d0
+++ b/src/wizard-downloads-page.vala
b0d0d0
@@ -0,0 +1,95 @@
b0d0d0
+// This file is part of GNOME Boxes. License: LGPLv2+
b0d0d0
+
b0d0d0
+public enum WizardDownloadsPageView {
b0d0d0
+    RECOMMENDED,
b0d0d0
+    SEARCH_RESULTS,
b0d0d0
+    NO_RESULTS,
b0d0d0
+}
b0d0d0
+
b0d0d0
+public delegate void Boxes.DownloadChosenFunc (Boxes.WizardDownloadableEntry entry);
b0d0d0
+
b0d0d0
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/wizard-downloads-page.ui")]
b0d0d0
+public class Boxes.WizardDownloadsPage : Gtk.Stack {
b0d0d0
+    private OSDatabase os_db = new OSDatabase ();
b0d0d0
+    public DownloadsSearch search { private set; get; }
b0d0d0
+
b0d0d0
+    public DownloadChosenFunc download_chosen_func;
b0d0d0
+
b0d0d0
+    [GtkChild]
b0d0d0
+    private Gtk.ListBox listbox;
b0d0d0
+    [GtkChild]
b0d0d0
+    private Gtk.ListBox recommended_listbox;
b0d0d0
+
b0d0d0
+    private GLib.ListStore recommended_model;
b0d0d0
+
b0d0d0
+    private WizardDownloadsPageView _page;
b0d0d0
+    public WizardDownloadsPageView page {
b0d0d0
+        get { return _page; }
b0d0d0
+        set {
b0d0d0
+            _page = value;
b0d0d0
+
b0d0d0
+            switch (_page) {
b0d0d0
+                case WizardDownloadsPageView.SEARCH_RESULTS:
b0d0d0
+                    visible_child_name = "search-results";
b0d0d0
+                    break;
b0d0d0
+                case WizardDownloadsPageView.NO_RESULTS:
b0d0d0
+                    visible_child_name = "no-results";
b0d0d0
+                    break;
b0d0d0
+                case WizardDownloadsPageView.RECOMMENDED:
b0d0d0
+                default:
b0d0d0
+                    visible_child_name = "recommended";
b0d0d0
+                    break;
b0d0d0
+            }
b0d0d0
+        }
b0d0d0
+    }
b0d0d0
+
b0d0d0
+    construct {
b0d0d0
+        os_db.load.begin ();
b0d0d0
+
b0d0d0
+        search = new DownloadsSearch ();
b0d0d0
+
b0d0d0
+        recommended_model = new GLib.ListStore (typeof (Osinfo.Media));
b0d0d0
+        recommended_listbox.bind_model (recommended_model, create_downloads_entry);
b0d0d0
+        populate_recommended_list.begin ();
b0d0d0
+
b0d0d0
+        listbox.bind_model (search.model, create_downloads_entry);
b0d0d0
+
b0d0d0
+        search.search_changed.connect (set_visible_view);
b0d0d0
+    }
b0d0d0
+
b0d0d0
+    private void set_visible_view () {
b0d0d0
+        if (search.text.length == 0) {
b0d0d0
+            page = WizardDownloadsPageView.RECOMMENDED;
b0d0d0
+        } else if (search.model.get_n_items () == 0) {
b0d0d0
+            page = WizardDownloadsPageView.NO_RESULTS;
b0d0d0
+        } else {
b0d0d0
+            page = WizardDownloadsPageView.SEARCH_RESULTS;
b0d0d0
+        }
b0d0d0
+    }
b0d0d0
+
b0d0d0
+    private async void populate_recommended_list () {
b0d0d0
+        foreach (var media in yield get_recommended_downloads ()) {
b0d0d0
+            recommended_model.append (media);
b0d0d0
+        }
b0d0d0
+    }
b0d0d0
+
b0d0d0
+    private Gtk.Widget create_downloads_entry (Object item) {
b0d0d0
+        var media = item as Osinfo.Media;
b0d0d0
+
b0d0d0
+        return new WizardDownloadableEntry (media);
b0d0d0
+    }
b0d0d0
+
b0d0d0
+    [GtkCallback]
b0d0d0
+    private void on_listbox_row_activated (Gtk.ListBoxRow row) {
b0d0d0
+        var entry = row as WizardDownloadableEntry;
b0d0d0
+
b0d0d0
+        download_chosen_func (entry);
b0d0d0
+    }
b0d0d0
+
b0d0d0
+    [GtkCallback]
b0d0d0
+    private void on_show_more_button_clicked () {
b0d0d0
+        search.show_all ();
b0d0d0
+
b0d0d0
+        page = WizardDownloadsPageView.SEARCH_RESULTS;
b0d0d0
+    }
b0d0d0
+}
b0d0d0
diff --git a/src/wizard-source.vala b/src/wizard-source.vala
b0d0d0
index 9ea0a9b1..494c5561 100644
b0d0d0
--- a/src/wizard-source.vala
b0d0d0
+++ b/src/wizard-source.vala
b0d0d0
@@ -310,6 +310,7 @@ private void on_notify_estimated_load_progress () {
b0d0d0
 
b0d0d0
     private Gtk.ListBox media_vbox;
b0d0d0
     private Gtk.ListBox downloads_vbox;
b0d0d0
+    private GLib.ListStore downloads_model;
b0d0d0
     private Osinfo.Os rhel_os;
b0d0d0
 
b0d0d0
     private Cancellable? rhel_cancellable;
b0d0d0
@@ -318,12 +319,6 @@ private void on_notify_estimated_load_progress () {
b0d0d0
 
b0d0d0
     public string filename { get; set; }
b0d0d0
 
b0d0d0
-    private string[] recommended_downloads = {
b0d0d0
-        "http://ubuntu.com/ubuntu/16.04",
b0d0d0
-        "http://opensuse.org/opensuse/42.2",
b0d0d0
-        "http://fedoraproject.org/fedora/27",
b0d0d0
-    };
b0d0d0
-
b0d0d0
     public bool download_required {
b0d0d0
         get {
b0d0d0
             string scheme = Uri.parse_scheme (uri);
b0d0d0
@@ -409,6 +404,8 @@ private void on_notify_estimated_load_progress () {
b0d0d0
             }
b0d0d0
         });
b0d0d0
 
b0d0d0
+        downloads_model = new GLib.ListStore (typeof (Osinfo.Media));
b0d0d0
+
b0d0d0
         rhel_web_view.view.decide_policy.connect (on_rhel_web_view_decide_policy);
b0d0d0
     }
b0d0d0
 
b0d0d0
@@ -425,26 +422,16 @@ public void setup_ui (AppWindow window) {
b0d0d0
         assert (window != null);
b0d0d0
 
b0d0d0
         this.window = window;
b0d0d0
+
b0d0d0
+        downloads_vbox.bind_model (downloads_model, create_downloadable_entry);
b0d0d0
+
b0d0d0
+        populate_recommended_downloads.begin ();
b0d0d0
     }
b0d0d0
 
b0d0d0
-    [GtkCallback]
b0d0d0
-    private void on_downloads_scrolled_shown () {
b0d0d0
+    private async void populate_recommended_downloads () {
b0d0d0
         var os_db = media_manager.os_db;
b0d0d0
-        foreach (var os_id in recommended_downloads) {
b0d0d0
-            os_db.get_os_by_id.begin (os_id, (obj, res) => {
b0d0d0
-                try {
b0d0d0
-                    var os = os_db.get_os_by_id.end (res);
b0d0d0
-
b0d0d0
-                    // TODO: Select the desktop/workstation variant.
b0d0d0
-                    var media = os.get_media_list ().get_nth (0) as Osinfo.Media;
b0d0d0
-                    var entry = create_downloadable_entry (media);
b0d0d0
-
b0d0d0
-                    downloads_vbox.insert (entry, -1);
b0d0d0
-                } catch (OSDatabaseError error) {
b0d0d0
-                    warning ("Failed to find OS with ID '%s': %s", os_id, error.message);
b0d0d0
-                    return;
b0d0d0
-                }
b0d0d0
-            });
b0d0d0
+        foreach (var media in yield get_recommended_downloads ()) {
b0d0d0
+            downloads_model.append (media);
b0d0d0
         }
b0d0d0
     }
b0d0d0
 
b0d0d0
-- 
b0d0d0
2.19.2
b0d0d0