[gnome-software] Move the PackageKit historical updates logic into the plugin



commit 0c4797934d407d74884f48f1aeb8e5ab61ca0de4
Author: Richard Hughes <richard hughsie com>
Date:   Wed Jan 27 14:24:57 2016 +0000

    Move the PackageKit historical updates logic into the plugin
    
    This does mean adding a few error codes, but means we can make showing the
    offline updates failure dialog a lot easier. Since we already got all the
    information we need in the update monitor it makes no sense to re-get this
    data when we could just save one GError.
    
    Plus, it's two less source files to worry about...

 po/POTFILES.in                             |    1 -
 src/Makefile.am                            |    2 -
 src/gs-application.c                       |    3 +-
 src/gs-offline-updates.c                   |  303 ----------------------------
 src/gs-offline-updates.h                   |   37 ----
 src/gs-plugin.h                            |    4 +
 src/gs-update-dialog.c                     |    1 -
 src/gs-update-monitor.c                    |  172 ++++++++++++++++-
 src/gs-update-monitor.h                    |    8 +-
 src/plugins/gs-plugin-packagekit-offline.c |   89 +++++++-
 10 files changed, 259 insertions(+), 361 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index f4cb828..643e044 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -19,7 +19,6 @@ src/gs-hiding-box.c
 src/gs-history-dialog.c
 [type: gettext/glade]src/gs-history-dialog.ui
 src/gs-main.c
-src/gs-offline-updates.c
 src/gs-page.c
 src/gs-plugin-loader.c
 src/gs-popular-tile.c
diff --git a/src/Makefile.am b/src/Makefile.am
index a0c4c1f..bb27a78 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -174,8 +174,6 @@ gnome_software_SOURCES =                            \
        gs-update-monitor.h                             \
        gs-vendor.c                                     \
        gs-vendor.h                                     \
-       gs-offline-updates.c                            \
-       gs-offline-updates.h                            \
        gs-plugin-loader.c                              \
        gs-plugin-loader.h                              \
        gs-plugin-loader-sync.c                         \
diff --git a/src/gs-application.c b/src/gs-application.c
index 98beeb5..0af7b33 100644
--- a/src/gs-application.c
+++ b/src/gs-application.c
@@ -40,7 +40,6 @@
 #include "gs-shell.h"
 #include "gs-update-monitor.h"
 #include "gs-shell-search-provider.h"
-#include "gs-offline-updates.h"
 #include "gs-folders.h"
 #include "gs-utils.h"
 
@@ -524,7 +523,7 @@ show_offline_updates_error (GSimpleAction *action,
        initialize_ui_and_present_window (app);
 
        gs_shell_set_mode (app->shell, GS_SHELL_MODE_UPDATES);
-       gs_offline_updates_show_error (app->shell);
+       gs_update_monitor_show_error (app->update_monitor, app->shell);
 }
 
 static void
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index c737dc8..3cfdf7b 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -80,6 +80,10 @@ struct GsPlugin {
 typedef enum {
        GS_PLUGIN_ERROR_FAILED,
        GS_PLUGIN_ERROR_NOT_SUPPORTED,
+       GS_PLUGIN_ERROR_CANCELLED,
+       GS_PLUGIN_ERROR_NO_NETWORK,
+       GS_PLUGIN_ERROR_NO_SECURITY,
+       GS_PLUGIN_ERROR_NO_SPACE,
        GS_PLUGIN_ERROR_LAST
 } GsPluginError;
 
diff --git a/src/gs-update-dialog.c b/src/gs-update-dialog.c
index 7acb8a7..a44b5a3 100644
--- a/src/gs-update-dialog.c
+++ b/src/gs-update-dialog.c
@@ -27,7 +27,6 @@
 #include "gs-update-dialog.h"
 #include "gs-app-row.h"
 #include "gs-markdown.h"
-#include "gs-offline-updates.h"
 #include "gs-update-list.h"
 #include "gs-utils.h"
 
diff --git a/src/gs-update-monitor.c b/src/gs-update-monitor.c
index 8f8b311..330aa6a 100644
--- a/src/gs-update-monitor.c
+++ b/src/gs-update-monitor.c
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2013-2015 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013-2016 Richard Hughes <richard hughsie com>
  * Copyright (C) 2013 Matthias Clasen <mclasen redhat com>
  *
  * Licensed under the GNU General Public License Version 2
@@ -30,7 +30,6 @@
 #include "gs-update-monitor.h"
 #include "gs-plugin-loader.h"
 #include "gs-utils.h"
-#include "gs-offline-updates.h"
 
 struct _GsUpdateMonitor {
        GObject          parent;
@@ -39,6 +38,7 @@ struct _GsUpdateMonitor {
        GCancellable    *cancellable;
        GSettings       *settings;
        GsPluginLoader  *plugin_loader;
+       GError          *last_offline_error;
 
        guint            cleanup_notifications_id;      /* at startup */
        guint            check_startup_id;              /* 60s after startup */
@@ -411,6 +411,11 @@ get_updates_historical_cb (GObject *object, GAsyncResult *res, gpointer data)
                        g_application_withdraw_notification (monitor->application,
                                                             "updates-available");
                } else {
+                       /* save this in case the user clicks the
+                        * 'Show Details' button from the notification below */
+                       g_clear_error (&monitor->last_offline_error);
+                       monitor->last_offline_error = g_error_copy (error);
+
                        /* TRANSLATORS: title when we offline updates have failed */
                        notification = g_notification_new (_("Software Updates Failed"));
                        /* TRANSLATORS: message when we offline updates have failed */
@@ -474,6 +479,168 @@ cleanup_notifications_cb (gpointer user_data)
 }
 
 static void
+do_not_expand (GtkWidget *child, gpointer data)
+{
+       gtk_container_child_set (GTK_CONTAINER (gtk_widget_get_parent (child)),
+                                child, "expand", FALSE, "fill", FALSE, NULL);
+}
+
+static gboolean
+unset_focus (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+       if (GTK_IS_WINDOW (widget))
+               gtk_window_set_focus(GTK_WINDOW (widget), NULL);
+       return FALSE;
+}
+
+/**
+ * insert_details_widget:
+ * @dialog: the message dialog where the widget will be inserted
+ * @details: (allow-none): the detailed message text to display
+ *
+ * Inserts a widget displaying the detailed message into the message dialog.
+ * Does nothing if @details is %NULL so it is safe to call this function
+ * without checking if there is anything to display.
+ */
+static void
+insert_details_widget (GtkMessageDialog *dialog, const gchar *details)
+{
+       GtkWidget *message_area, *sw, *label;
+       GtkWidget *box, *tv;
+       GtkTextBuffer *buffer;
+       GList *children;
+       g_autoptr(GString) msg = NULL;
+
+       if (!details)
+               return;
+       g_return_if_fail (dialog != NULL);
+       gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+
+       msg = g_string_new ("");
+       g_string_append_printf (msg, "%s\n\n%s",
+                               /* TRANSLATORS: these are show_detailed_error messages from the
+                                * package manager no mortal is supposed to understand,
+                                * but google might know what they mean */
+                               _("Detailed errors from the package manager follow:"),
+                               details);
+
+       message_area = gtk_message_dialog_get_message_area (dialog);
+       g_assert (GTK_IS_BOX (message_area));
+       /* make the hbox expand */
+       box = gtk_widget_get_parent (message_area);
+       gtk_container_child_set (GTK_CONTAINER (gtk_widget_get_parent (box)), box,
+                                "expand", TRUE, "fill", TRUE, NULL);
+       /* make the labels not expand */
+       gtk_container_foreach (GTK_CONTAINER (message_area), do_not_expand, NULL);
+
+       /* Find the secondary label and set its width_chars.   */
+       /* Otherwise the label will tend to expand vertically. */
+       children = gtk_container_get_children (GTK_CONTAINER (message_area));
+       if (children && children->next && GTK_IS_LABEL (children->next->data)) {
+               gtk_label_set_width_chars (GTK_LABEL (children->next->data), 40);
+       }
+
+       label = gtk_label_new (_("Details"));
+       gtk_widget_set_halign (label, GTK_ALIGN_START);
+       gtk_widget_set_visible (label, TRUE);
+       gtk_box_pack_start (GTK_BOX (message_area), label, FALSE, FALSE, 0);
+
+       sw = gtk_scrolled_window_new (NULL, NULL);
+       gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
+                                            GTK_SHADOW_IN);
+       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+                                       GTK_POLICY_NEVER,
+                                       GTK_POLICY_AUTOMATIC);
+       gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (sw), 150);
+       gtk_widget_set_visible (sw, TRUE);
+
+       tv = gtk_text_view_new ();
+       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
+       gtk_text_view_set_editable (GTK_TEXT_VIEW (tv), FALSE);
+       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (tv), GTK_WRAP_WORD);
+       gtk_style_context_add_class (gtk_widget_get_style_context (tv),
+                                    "update-failed-details");
+       gtk_text_buffer_set_text (buffer, msg->str, -1);
+       gtk_widget_set_visible (tv, TRUE);
+
+       gtk_container_add (GTK_CONTAINER (sw), tv);
+       gtk_box_pack_end (GTK_BOX (message_area), sw, TRUE, TRUE, 0);
+
+       g_signal_connect (dialog, "map-event", G_CALLBACK (unset_focus), NULL);
+}
+
+void
+gs_update_monitor_show_error (GsUpdateMonitor *monitor, GsShell *shell)
+{
+       const gchar *title;
+       const gchar *msg;
+       gboolean show_detailed_error;
+       GtkWidget *dialog;
+
+       /* can this happen in reality? */
+       if (monitor->last_offline_error == NULL)
+               return;
+
+       /* TRANSLATORS: this is when the offline update failed */
+       title = _("Failed To Update");
+
+       switch (monitor->last_offline_error->code) {
+       case GS_PLUGIN_ERROR_NOT_SUPPORTED:
+               /* TRANSLATORS: the user must have updated manually after
+                * the updates were prepared */
+               msg = _("The system was already up to date.");
+               show_detailed_error = TRUE;
+               break;
+       case GS_PLUGIN_ERROR_CANCELLED:
+               /* TRANSLATORS: the user aborted the update manually */
+               msg = _("The update was cancelled.");
+               show_detailed_error = FALSE;
+               break;
+       case GS_PLUGIN_ERROR_NO_NETWORK:
+               /* TRANSLATORS: the package manager needed to download
+                * something with no network available */
+               msg = _("Internet access was required but wasn’t available. "
+                       "Please make sure that you have internet access and try again.");
+               show_detailed_error = FALSE;
+               break;
+       case GS_PLUGIN_ERROR_NO_SECURITY:
+               /* TRANSLATORS: if the package is not signed correctly */
+               msg = _("There were security issues with the update. "
+                       "Please consult your software provider for more details.");
+               show_detailed_error = TRUE;
+               break;
+       case GS_PLUGIN_ERROR_NO_SPACE:
+               /* TRANSLATORS: we ran out of disk space */
+               msg = _("There wasn’t enough disk space. Please free up some space and try again.");
+               show_detailed_error = FALSE;
+               break;
+       default:
+               /* TRANSLATORS: We didn't handle the error type */
+               msg = _("We’re sorry: the update failed to install. "
+                       "Please wait for another update and try again. "
+                       "If the problem persists, contact your software provider.");
+               show_detailed_error = TRUE;
+               break;
+       }
+
+       dialog = gtk_message_dialog_new_with_markup (gs_shell_get_window (shell),
+                                        0,
+                                        GTK_MESSAGE_INFO,
+                                        GTK_BUTTONS_CLOSE,
+                                        "<big><b>%s</b></big>", title);
+       gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+                                                 "%s", msg);
+       if (show_detailed_error) {
+               insert_details_widget (GTK_MESSAGE_DIALOG (dialog),
+                                      monitor->last_offline_error->message);
+       }
+       g_signal_connect_swapped (dialog, "response",
+                                 G_CALLBACK (gtk_widget_destroy),
+                                 dialog);
+       gtk_widget_show (dialog);
+}
+
+static void
 gs_update_monitor_init (GsUpdateMonitor *monitor)
 {
        monitor->settings = g_settings_new ("org.gnome.software");
@@ -538,6 +705,7 @@ gs_update_monitor_finalize (GObject *object)
        GsUpdateMonitor *monitor = GS_UPDATE_MONITOR (object);
 
        g_application_release (monitor->application);
+       g_clear_error (&monitor->last_offline_error);
 
        G_OBJECT_CLASS (gs_update_monitor_parent_class)->finalize (object);
 }
diff --git a/src/gs-update-monitor.h b/src/gs-update-monitor.h
index bc9ce8a..d30552d 100644
--- a/src/gs-update-monitor.h
+++ b/src/gs-update-monitor.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  *
- * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+ * Copyright (C) 2013-2016 Richard Hughes <richard hughsie com>
  *
  * Licensed under the GNU General Public License Version 2
  *
@@ -23,7 +23,9 @@
 #define __GS_UPDATE_MONITOR_H
 
 #include <glib-object.h>
+
 #include "gs-application.h"
+#include "gs-shell.h"
 
 G_BEGIN_DECLS
 
@@ -31,7 +33,9 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GsUpdateMonitor, gs_update_monitor, GS, UPDATE_MONITOR, GObject)
 
-GsUpdateMonitor        *gs_update_monitor_new          (GsApplication *app);
+GsUpdateMonitor        *gs_update_monitor_new                  (GsApplication  *app);
+void            gs_update_monitor_show_error           (GsUpdateMonitor *monitor,
+                                                        GsShell        *shell);
 GPermission    *gs_update_monitor_permission_get       (void);
 gboolean        gs_update_monitor_is_managed           (void);
 
diff --git a/src/plugins/gs-plugin-packagekit-offline.c b/src/plugins/gs-plugin-packagekit-offline.c
index 831c3dc..a386858 100644
--- a/src/plugins/gs-plugin-packagekit-offline.c
+++ b/src/plugins/gs-plugin-packagekit-offline.c
@@ -21,6 +21,9 @@
 
 #include <config.h>
 
+#define I_KNOW_THE_PACKAGEKIT_GLIB2_API_IS_SUBJECT_TO_CHANGE
+#include <packagekit-glib2/packagekit.h>
+
 #include <gs-plugin.h>
 
 /*
@@ -44,6 +47,65 @@ gs_plugin_get_name (void)
 #define PK_OFFLINE_UPDATE_RESULTS_FILENAME     "/var/lib/PackageKit/offline-update-competed"
 
 /**
+ * gs_plugin_packagekit_convert_error:
+ */
+static gboolean
+gs_plugin_packagekit_convert_error (GError **error,
+                                   PkErrorEnum error_enum,
+                                   const gchar *details)
+{
+       switch (error_enum) {
+       case PK_ERROR_ENUM_PACKAGE_DOWNLOAD_FAILED:
+       case PK_ERROR_ENUM_NO_CACHE:
+       case PK_ERROR_ENUM_NO_NETWORK:
+       case PK_ERROR_ENUM_NO_MORE_MIRRORS_TO_TRY:
+       case PK_ERROR_ENUM_CANNOT_FETCH_SOURCES:
+       case PK_ERROR_ENUM_UNFINISHED_TRANSACTION:
+               g_set_error_literal (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_NO_NETWORK,
+                                    details);
+               break;
+       case PK_ERROR_ENUM_BAD_GPG_SIGNATURE:
+       case PK_ERROR_ENUM_CANNOT_UPDATE_REPO_UNSIGNED:
+       case PK_ERROR_ENUM_GPG_FAILURE:
+       case PK_ERROR_ENUM_MISSING_GPG_SIGNATURE:
+       case PK_ERROR_ENUM_PACKAGE_CORRUPT:
+               g_set_error_literal (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_NO_SECURITY,
+                                    details);
+               break;
+       case PK_ERROR_ENUM_TRANSACTION_CANCELLED:
+               g_set_error_literal (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_CANCELLED,
+                                    details);
+               break;
+       case PK_ERROR_ENUM_NO_PACKAGES_TO_UPDATE:
+       case PK_ERROR_ENUM_UPDATE_NOT_FOUND:
+               g_set_error_literal (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_NOT_SUPPORTED,
+                                    details);
+               break;
+       case PK_ERROR_ENUM_NO_SPACE_ON_DEVICE:
+               g_set_error_literal (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_NO_SPACE,
+                                    details);
+               break;
+       default:
+               g_set_error_literal (error,
+                                    GS_PLUGIN_ERROR,
+                                    GS_PLUGIN_ERROR_FAILED,
+                                    details);
+               break;
+       }
+       return FALSE;
+}
+
+/**
  * gs_plugin_add_updates_historical:
  */
 gboolean
@@ -56,7 +118,6 @@ gs_plugin_add_updates_historical (GsPlugin *plugin,
        guint64 mtime;
        guint i;
        g_auto(GStrv) package_ids = NULL;
-       g_autofree gchar *error_details = NULL;
        g_autofree gchar *packages = NULL;
        g_autoptr(GFile) file = NULL;
        g_autoptr(GFileInfo) info = NULL;
@@ -92,17 +153,23 @@ gs_plugin_add_updates_historical (GsPlugin *plugin,
                                      "Success",
                                      NULL);
        if (!ret) {
-               error_details = g_key_file_get_string (key_file,
-                                                      PK_OFFLINE_UPDATE_RESULTS_GROUP,
-                                                      "ErrorDetails",
-                                                      error);
-               if (error_details == NULL)
+               g_autofree gchar *code = NULL;
+               g_autofree gchar *details = NULL;
+               code = g_key_file_get_string (key_file,
+                                             PK_OFFLINE_UPDATE_RESULTS_GROUP,
+                                             "ErrorCode",
+                                             error);
+               if (code == NULL)
                        return FALSE;
-               g_set_error_literal (error,
-                                    GS_PLUGIN_ERROR,
-                                    GS_PLUGIN_ERROR_FAILED,
-                                    error_details);
-               return FALSE;
+               details = g_key_file_get_string (key_file,
+                                                PK_OFFLINE_UPDATE_RESULTS_GROUP,
+                                                "ErrorDetails",
+                                                error);
+               if (details == NULL)
+                       return FALSE;
+               return gs_plugin_packagekit_convert_error (error,
+                                                          pk_error_enum_from_string (code),
+                                                          details);
        }
 
        /* get list of package-ids */


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