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

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