From a7f877b88996b0d3b6b0b9ff9b4cc10cb1255e0e Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Jun 09 2020 19:34:04 +0000 Subject: import gnome-software-3.36.1-3.el8 --- diff --git a/.gitignore b/.gitignore index 8699332..de98123 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/gnome-software-3.30.6.tar.xz +SOURCES/gnome-software-3.36.1.tar.xz diff --git a/.gnome-software.metadata b/.gnome-software.metadata index ce66794..33f7de1 100644 --- a/.gnome-software.metadata +++ b/.gnome-software.metadata @@ -1 +1 @@ -a7006d754afdc4f9d59690b5805f8c93d02a0c98 SOURCES/gnome-software-3.30.6.tar.xz +9c6342f47f5ad90deac7cc8250f559146f506fff SOURCES/gnome-software-3.36.1.tar.xz diff --git a/SOURCES/0001-Add-basic-auth-support-to-flatpak-plugin.patch b/SOURCES/0001-Add-basic-auth-support-to-flatpak-plugin.patch new file mode 100644 index 0000000..40b256a --- /dev/null +++ b/SOURCES/0001-Add-basic-auth-support-to-flatpak-plugin.patch @@ -0,0 +1,813 @@ +From b6a41a1b9e9020a23dbc418183ebe4746b6ec027 Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Mon, 18 May 2020 14:45:35 +0200 +Subject: [PATCH 1/2] Add basic auth support to flatpak plugin + +This is useful for e.g. OCI remotes that can use basic auth. + +All user visible strings in the basic auth dialog are taken from the +flatpak CLI client. +--- + lib/gs-plugin-loader.c | 29 +++- + lib/gs-plugin-loader.h | 7 +- + lib/gs-plugin.c | 68 +++++++++- + lib/gs-plugin.h | 13 +- + plugins/flatpak/gs-plugin-flatpak.c | 55 +++++++- + po/POTFILES.in | 2 + + src/gnome-software.gresource.xml | 1 + + src/gs-basic-auth-dialog.c | 130 ++++++++++++++++++ + src/gs-basic-auth-dialog.h | 28 ++++ + src/gs-basic-auth-dialog.ui | 203 ++++++++++++++++++++++++++++ + src/gs-shell.c | 25 +++- + src/meson.build | 1 + + 12 files changed, 556 insertions(+), 6 deletions(-) + create mode 100644 src/gs-basic-auth-dialog.c + create mode 100644 src/gs-basic-auth-dialog.h + create mode 100644 src/gs-basic-auth-dialog.ui + +diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c +index 979f3d5d..35382e3f 100644 +--- a/lib/gs-plugin-loader.c ++++ b/lib/gs-plugin-loader.c +@@ -1,7 +1,7 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2007-2018 Richard Hughes +- * Copyright (C) 2014-2018 Kalev Lember ++ * Copyright (C) 2014-2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -74,6 +74,7 @@ enum { + SIGNAL_PENDING_APPS_CHANGED, + SIGNAL_UPDATES_CHANGED, + SIGNAL_RELOAD, ++ SIGNAL_BASIC_AUTH_START, + SIGNAL_LAST + }; + +@@ -2016,6 +2017,23 @@ gs_plugin_loader_status_changed_cb (GsPlugin *plugin, + 0, app, status); + } + ++static void ++gs_plugin_loader_basic_auth_start_cb (GsPlugin *plugin, ++ const gchar *remote, ++ const gchar *realm, ++ GCallback callback, ++ gpointer user_data, ++ GsPluginLoader *plugin_loader) ++{ ++ g_debug ("emitting basic-auth-start %s", realm); ++ g_signal_emit (plugin_loader, ++ signals[SIGNAL_BASIC_AUTH_START], 0, ++ remote, ++ realm, ++ callback, ++ user_data); ++} ++ + static gboolean + gs_plugin_loader_job_actions_changed_delay_cb (gpointer user_data) + { +@@ -2102,6 +2120,9 @@ gs_plugin_loader_open_plugin (GsPluginLoader *plugin_loader, + g_signal_connect (plugin, "status-changed", + G_CALLBACK (gs_plugin_loader_status_changed_cb), + plugin_loader); ++ g_signal_connect (plugin, "basic-auth-start", ++ G_CALLBACK (gs_plugin_loader_basic_auth_start_cb), ++ plugin_loader); + g_signal_connect (plugin, "report-event", + G_CALLBACK (gs_plugin_loader_report_event_cb), + plugin_loader); +@@ -2712,6 +2733,12 @@ gs_plugin_loader_class_init (GsPluginLoaderClass *klass) + G_STRUCT_OFFSET (GsPluginLoaderClass, reload), + NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); ++ signals [SIGNAL_BASIC_AUTH_START] = ++ g_signal_new ("basic-auth-start", ++ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GsPluginLoaderClass, basic_auth_start), ++ NULL, NULL, g_cclosure_marshal_generic, ++ G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER); + } + + static void +diff --git a/lib/gs-plugin-loader.h b/lib/gs-plugin-loader.h +index 74cbfa53..e88ea2d1 100644 +--- a/lib/gs-plugin-loader.h ++++ b/lib/gs-plugin-loader.h +@@ -1,7 +1,7 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2007-2017 Richard Hughes +- * Copyright (C) 2015 Kalev Lember ++ * Copyright (C) 2015-2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -31,6 +31,11 @@ struct _GsPluginLoaderClass + void (*pending_apps_changed) (GsPluginLoader *plugin_loader); + void (*updates_changed) (GsPluginLoader *plugin_loader); + void (*reload) (GsPluginLoader *plugin_loader); ++ void (*basic_auth_start) (GsPluginLoader *plugin_loader, ++ const gchar *remote, ++ const gchar *realm, ++ GCallback callback, ++ gpointer user_data); + }; + + GsPluginLoader *gs_plugin_loader_new (void); +diff --git a/lib/gs-plugin.c b/lib/gs-plugin.c +index 5aed1058..3f63fa97 100644 +--- a/lib/gs-plugin.c ++++ b/lib/gs-plugin.c +@@ -1,7 +1,7 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2013-2016 Richard Hughes +- * Copyright (C) 2014-2018 Kalev Lember ++ * Copyright (C) 2014-2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -87,6 +87,7 @@ enum { + SIGNAL_RELOAD, + SIGNAL_REPORT_EVENT, + SIGNAL_ALLOW_UPDATES, ++ SIGNAL_BASIC_AUTH_START, + SIGNAL_LAST + }; + +@@ -851,6 +852,64 @@ gs_plugin_status_update (GsPlugin *plugin, GsApp *app, GsPluginStatus status) + g_source_attach (idle_source, NULL); + } + ++typedef struct { ++ GsPlugin *plugin; ++ gchar *remote; ++ gchar *realm; ++ GCallback callback; ++ gpointer user_data; ++} GsPluginBasicAuthHelper; ++ ++static gboolean ++gs_plugin_basic_auth_start_cb (gpointer user_data) ++{ ++ GsPluginBasicAuthHelper *helper = user_data; ++ g_signal_emit (helper->plugin, ++ signals[SIGNAL_BASIC_AUTH_START], 0, ++ helper->remote, ++ helper->realm, ++ helper->callback, ++ helper->user_data); ++ g_free (helper->remote); ++ g_free (helper->realm); ++ g_slice_free (GsPluginBasicAuthHelper, helper); ++ return FALSE; ++} ++ ++/** ++ * gs_plugin_basic_auth_start: ++ * @plugin: a #GsPlugin ++ * @remote: a string ++ * @realm: a string ++ * @callback: callback to invoke to submit the user/password ++ * @user_data: callback data to pass to the callback ++ * ++ * Emit the basic-auth-start signal in the main thread. ++ * ++ * Since: 3.38 ++ **/ ++void ++gs_plugin_basic_auth_start (GsPlugin *plugin, ++ const gchar *remote, ++ const gchar *realm, ++ GCallback callback, ++ gpointer user_data) ++{ ++ GsPluginBasicAuthHelper *helper; ++ g_autoptr(GSource) idle_source = NULL; ++ ++ helper = g_slice_new0 (GsPluginBasicAuthHelper); ++ helper->plugin = plugin; ++ helper->remote = g_strdup (remote); ++ helper->realm = g_strdup (realm); ++ helper->callback = callback; ++ helper->user_data = user_data; ++ ++ idle_source = g_idle_source_new (); ++ g_source_set_callback (idle_source, gs_plugin_basic_auth_start_cb, helper, NULL); ++ g_source_attach (idle_source, NULL); ++} ++ + static gboolean + gs_plugin_app_launch_cb (gpointer user_data) + { +@@ -1959,6 +2018,13 @@ gs_plugin_class_init (GsPluginClass *klass) + G_STRUCT_OFFSET (GsPluginClass, allow_updates), + NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); ++ ++ signals [SIGNAL_BASIC_AUTH_START] = ++ g_signal_new ("basic-auth-start", ++ G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (GsPluginClass, basic_auth_start), ++ NULL, NULL, g_cclosure_marshal_generic, ++ G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER); + } + + static void +diff --git a/lib/gs-plugin.h b/lib/gs-plugin.h +index 7dd2d864..d07afd3b 100644 +--- a/lib/gs-plugin.h ++++ b/lib/gs-plugin.h +@@ -1,6 +1,7 @@ + /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2012-2016 Richard Hughes ++ * Copyright (C) 2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -37,7 +38,12 @@ struct _GsPluginClass + GsPluginEvent *event); + void (*allow_updates) (GsPlugin *plugin, + gboolean allow_updates); +- gpointer padding[26]; ++ void (*basic_auth_start) (GsPlugin *plugin, ++ const gchar *remote, ++ const gchar *realm, ++ GCallback callback, ++ gpointer user_data); ++ gpointer padding[25]; + }; + + typedef struct GsPluginData GsPluginData; +@@ -116,5 +122,10 @@ void gs_plugin_report_event (GsPlugin *plugin, + void gs_plugin_set_allow_updates (GsPlugin *plugin, + gboolean allow_updates); + gboolean gs_plugin_get_network_available (GsPlugin *plugin); ++void gs_plugin_basic_auth_start (GsPlugin *plugin, ++ const gchar *remote, ++ const gchar *realm, ++ GCallback callback, ++ gpointer user_data); + + G_END_DECLS +diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c +index 4d6a81ba..2518025d 100644 +--- a/plugins/flatpak/gs-plugin-flatpak.c ++++ b/plugins/flatpak/gs-plugin-flatpak.c +@@ -2,7 +2,7 @@ + * + * Copyright (C) 2016 Joaquim Rocha + * Copyright (C) 2016-2018 Richard Hughes +- * Copyright (C) 2017-2018 Kalev Lember ++ * Copyright (C) 2017-2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -456,6 +456,55 @@ _group_apps_by_installation (GsPlugin *plugin, + return g_steal_pointer (&applist_by_flatpaks); + } + ++#if FLATPAK_CHECK_VERSION(1,6,0) ++typedef struct { ++ FlatpakTransaction *transaction; ++ guint id; ++} BasicAuthData; ++ ++static void ++basic_auth_data_free (BasicAuthData *data) ++{ ++ g_object_unref (data->transaction); ++ g_slice_free (BasicAuthData, data); ++} ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC(BasicAuthData, basic_auth_data_free) ++ ++static void ++_basic_auth_cb (const gchar *user, const gchar *password, gpointer user_data) ++{ ++ g_autoptr(BasicAuthData) data = user_data; ++ ++ g_debug ("Submitting basic auth data"); ++ ++ /* NULL user aborts the basic auth request */ ++ flatpak_transaction_complete_basic_auth (data->transaction, data->id, user, password, NULL /* options */); ++} ++ ++static gboolean ++_basic_auth_start (FlatpakTransaction *transaction, ++ const char *remote, ++ const char *realm, ++ GVariant *options, ++ guint id, ++ GsPlugin *plugin) ++{ ++ BasicAuthData *data; ++ ++ if (!gs_plugin_has_flags (plugin, GS_PLUGIN_FLAGS_INTERACTIVE)) ++ return FALSE; ++ ++ data = g_slice_new0 (BasicAuthData); ++ data->transaction = g_object_ref (transaction); ++ data->id = id; ++ ++ g_debug ("Login required remote %s (realm %s)\n", remote, realm); ++ gs_plugin_basic_auth_start (plugin, remote, realm, G_CALLBACK (_basic_auth_cb), data); ++ return TRUE; ++} ++#endif ++ + static FlatpakTransaction * + _build_transaction (GsPlugin *plugin, GsFlatpak *flatpak, + GCancellable *cancellable, GError **error) +@@ -491,6 +540,10 @@ _build_transaction (GsPlugin *plugin, GsFlatpak *flatpak, + /* connect up signals */ + g_signal_connect (transaction, "ref-to-app", + G_CALLBACK (_ref_to_app), plugin); ++#if FLATPAK_CHECK_VERSION(1,6,0) ++ g_signal_connect (transaction, "basic-auth-start", ++ G_CALLBACK (_basic_auth_start), plugin); ++#endif + + /* use system installations as dependency sources for user installations */ + flatpak_transaction_add_default_dependency_sources (transaction); +diff --git a/po/POTFILES.in b/po/POTFILES.in +index 20721c4a..a44a6ad3 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -10,6 +10,8 @@ src/gs-app-row.c + src/gs-app-row.ui + src/gs-app-tile.c + src/gs-app-tile.ui ++src/gs-basic-auth-dialog.c ++src/gs-basic-auth-dialog.ui + lib/gs-category.c + src/gs-category-page.c + src/gs-category-page.ui +diff --git a/src/gnome-software.gresource.xml b/src/gnome-software.gresource.xml +index 3eaabca2..459ecf82 100644 +--- a/src/gnome-software.gresource.xml ++++ b/src/gnome-software.gresource.xml +@@ -4,6 +4,7 @@ + gnome-software.ui + gs-app-addon-row.ui + gs-app-row.ui ++ gs-basic-auth-dialog.ui + gs-category-page.ui + gs-category-tile.ui + gs-details-page.ui +diff --git a/src/gs-basic-auth-dialog.c b/src/gs-basic-auth-dialog.c +new file mode 100644 +index 00000000..c690a327 +--- /dev/null ++++ b/src/gs-basic-auth-dialog.c +@@ -0,0 +1,130 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2020 Kalev Lember ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include "config.h" ++ ++#include "gs-basic-auth-dialog.h" ++ ++#include ++#include ++#include ++ ++struct _GsBasicAuthDialog ++{ ++ GtkDialog parent_instance; ++ ++ GsBasicAuthCallback callback; ++ gpointer callback_data; ++ ++ /* template widgets */ ++ GtkButton *login_button; ++ GtkLabel *description_label; ++ GtkEntry *user_entry; ++ GtkEntry *password_entry; ++}; ++ ++G_DEFINE_TYPE (GsBasicAuthDialog, gs_basic_auth_dialog, GTK_TYPE_DIALOG) ++ ++static void ++cancel_button_clicked_cb (GsBasicAuthDialog *dialog) ++{ ++ /* abort the basic auth request */ ++ dialog->callback (NULL, NULL, dialog->callback_data); ++ ++ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL); ++} ++ ++static void ++login_button_clicked_cb (GsBasicAuthDialog *dialog) ++{ ++ const gchar *user; ++ const gchar *password; ++ ++ user = gtk_entry_get_text (dialog->user_entry); ++ password = gtk_entry_get_text (dialog->password_entry); ++ ++ /* submit the user/password to basic auth */ ++ dialog->callback (user, password, dialog->callback_data); ++ ++ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); ++} ++ ++static void ++dialog_validate (GsBasicAuthDialog *dialog) ++{ ++ const gchar *user; ++ const gchar *password; ++ gboolean valid_user; ++ gboolean valid_password; ++ ++ /* require user */ ++ user = gtk_entry_get_text (dialog->user_entry); ++ valid_user = user != NULL && strlen (user) != 0; ++ ++ /* require password */ ++ password = gtk_entry_get_text (dialog->password_entry); ++ valid_password = password != NULL && strlen (password) != 0; ++ ++ gtk_widget_set_sensitive (GTK_WIDGET (dialog->login_button), valid_user && valid_password); ++} ++ ++static void ++update_description (GsBasicAuthDialog *dialog, const gchar *remote, const gchar *realm) ++{ ++ g_autofree gchar *description = NULL; ++ ++ /* TRANSLATORS: This is a description for entering user/password */ ++ description = g_strdup_printf (_("Login required remote %s (realm %s)"), ++ remote, realm); ++ gtk_label_set_text (dialog->description_label, description); ++} ++ ++static void ++gs_basic_auth_dialog_init (GsBasicAuthDialog *dialog) ++{ ++ gtk_widget_init_template (GTK_WIDGET (dialog)); ++} ++ ++static void ++gs_basic_auth_dialog_class_init (GsBasicAuthDialogClass *klass) ++{ ++ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); ++ ++ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-basic-auth-dialog.ui"); ++ ++ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, login_button); ++ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, description_label); ++ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, user_entry); ++ gtk_widget_class_bind_template_child (widget_class, GsBasicAuthDialog, password_entry); ++ ++ gtk_widget_class_bind_template_callback (widget_class, dialog_validate); ++ gtk_widget_class_bind_template_callback (widget_class, cancel_button_clicked_cb); ++ gtk_widget_class_bind_template_callback (widget_class, login_button_clicked_cb); ++} ++ ++GtkWidget * ++gs_basic_auth_dialog_new (GtkWindow *parent, ++ const gchar *remote, ++ const gchar *realm, ++ GsBasicAuthCallback callback, ++ gpointer callback_data) ++{ ++ GsBasicAuthDialog *dialog; ++ ++ dialog = g_object_new (GS_TYPE_BASIC_AUTH_DIALOG, ++ "use-header-bar", TRUE, ++ "transient-for", parent, ++ "modal", TRUE, ++ NULL); ++ dialog->callback = callback; ++ dialog->callback_data = callback_data; ++ ++ update_description (dialog, remote, realm); ++ dialog_validate (dialog); ++ ++ return GTK_WIDGET (dialog); ++} +diff --git a/src/gs-basic-auth-dialog.h b/src/gs-basic-auth-dialog.h +new file mode 100644 +index 00000000..ec5f1d03 +--- /dev/null ++++ b/src/gs-basic-auth-dialog.h +@@ -0,0 +1,28 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2020 Kalev Lember ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#pragma once ++ ++#include ++ ++#include "gnome-software-private.h" ++ ++G_BEGIN_DECLS ++ ++typedef void (*GsBasicAuthCallback) (const gchar *user, const gchar *password, gpointer callback_data); ++ ++#define GS_TYPE_BASIC_AUTH_DIALOG (gs_basic_auth_dialog_get_type ()) ++ ++G_DECLARE_FINAL_TYPE (GsBasicAuthDialog, gs_basic_auth_dialog, GS, BASIC_AUTH_DIALOG, GtkDialog) ++ ++GtkWidget *gs_basic_auth_dialog_new (GtkWindow *parent, ++ const gchar *remote, ++ const gchar *realm, ++ GsBasicAuthCallback callback, ++ gpointer callback_data); ++ ++G_END_DECLS +diff --git a/src/gs-basic-auth-dialog.ui b/src/gs-basic-auth-dialog.ui +new file mode 100644 +index 00000000..339e831d +--- /dev/null ++++ b/src/gs-basic-auth-dialog.ui +@@ -0,0 +1,203 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ horizontal ++ ++ ++ ++ ++ ++ +diff --git a/src/gs-shell.c b/src/gs-shell.c +index 009776ad..41503cf8 100644 +--- a/src/gs-shell.c ++++ b/src/gs-shell.c +@@ -2,7 +2,7 @@ + * + * Copyright (C) 2013-2017 Richard Hughes + * Copyright (C) 2013 Matthias Clasen +- * Copyright (C) 2014-2018 Kalev Lember ++ * Copyright (C) 2014-2020 Kalev Lember + * + * SPDX-License-Identifier: GPL-2.0+ + */ +@@ -18,6 +18,7 @@ + + #include "gs-common.h" + #include "gs-shell.h" ++#include "gs-basic-auth-dialog.h" + #include "gs-details-page.h" + #include "gs-installed-page.h" + #include "gs-metered-data-dialog.h" +@@ -362,6 +363,25 @@ scheduler_ready_cb (GObject *source_object, + } + #endif /* HAVE_MOGWAI */ + ++static void ++gs_shell_basic_auth_start_cb (GsPluginLoader *plugin_loader, ++ const gchar *remote, ++ const gchar *realm, ++ GsBasicAuthCallback callback, ++ gpointer callback_data, ++ GsShell *shell) ++{ ++ GsShellPrivate *priv = gs_shell_get_instance_private (shell); ++ GtkWidget *dialog; ++ ++ dialog = gs_basic_auth_dialog_new (priv->main_window, remote, realm, callback, callback_data); ++ gs_shell_modal_dialog_present (shell, GTK_DIALOG (dialog)); ++ ++ /* just destroy */ ++ g_signal_connect_swapped (dialog, "response", ++ G_CALLBACK (gtk_widget_destroy), dialog); ++} ++ + static void + free_back_entry (BackEntry *entry) + { +@@ -2126,6 +2146,9 @@ gs_shell_setup (GsShell *shell, GsPluginLoader *plugin_loader, GCancellable *can + g_signal_connect_object (priv->plugin_loader, "notify::network-metered", + G_CALLBACK (gs_shell_network_metered_notify_cb), + shell, 0); ++ g_signal_connect_object (priv->plugin_loader, "basic-auth-start", ++ G_CALLBACK (gs_shell_basic_auth_start_cb), ++ shell, 0); + priv->cancellable = g_object_ref (cancellable); + + priv->settings = g_settings_new ("org.gnome.software"); +diff --git a/src/meson.build b/src/meson.build +index cbd0a511..6581e77c 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -20,6 +20,7 @@ gnome_software_sources = [ + 'gs-application.c', + 'gs-app-row.c', + 'gs-app-tile.c', ++ 'gs-basic-auth-dialog.c', + 'gs-category-page.c', + 'gs-category-tile.c', + 'gs-common.c', +-- +2.26.2 + diff --git a/SOURCES/0001-Fix-hardcoded-desktop-and-appdata-names-to-match-wha.patch b/SOURCES/0001-Fix-hardcoded-desktop-and-appdata-names-to-match-wha.patch new file mode 100644 index 0000000..45ecdf3 --- /dev/null +++ b/SOURCES/0001-Fix-hardcoded-desktop-and-appdata-names-to-match-wha.patch @@ -0,0 +1,61 @@ +From 9dce785c2a71e81c410c6e314dd4d4db6cc02808 Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Wed, 3 Jun 2020 16:35:03 +0200 +Subject: [PATCH] Fix hardcoded desktop and appdata names to match what's in + RHEL 8.3 + +--- + data/assets/org.gnome.Software.Featured.xml | 2 +- + plugins/core/gs-plugin-hardcoded-popular.c | 2 +- + src/gs-folders.c | 6 +++--- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/data/assets/org.gnome.Software.Featured.xml b/data/assets/org.gnome.Software.Featured.xml +index d1d920be..822fc6a9 100644 +--- a/data/assets/org.gnome.Software.Featured.xml ++++ b/data/assets/org.gnome.Software.Featured.xml +@@ -145,7 +145,7 @@ text-shadow: none; + + + +- org.gimp.GIMP ++ gimp.desktop + + border-color: #4a8c30; + text-shadow: none; +diff --git a/plugins/core/gs-plugin-hardcoded-popular.c b/plugins/core/gs-plugin-hardcoded-popular.c +index 3998a813..03b44475 100644 +--- a/plugins/core/gs-plugin-hardcoded-popular.c ++++ b/plugins/core/gs-plugin-hardcoded-popular.c +@@ -29,7 +29,7 @@ gs_plugin_add_popular (GsPlugin *plugin, + "org.gnome.clocks.desktop", + "org.gnome.Dictionary.desktop", + "org.gnome.Documents.desktop", +- "org.gnome.Evince", ++ "evince.desktop", + "org.gnome.gedit.desktop", + "org.gnome.Maps.desktop", + "org.gnome.Weather", +diff --git a/src/gs-folders.c b/src/gs-folders.c +index fa068f0e..589cc1e2 100644 +--- a/src/gs-folders.c ++++ b/src/gs-folders.c +@@ -560,12 +560,12 @@ gs_folders_convert (void) + "org.gnome.DejaDup.desktop", + "org.gnome.Dictionary.desktop", + "org.gnome.DiskUtility.desktop", +- "org.gnome.eog.desktop", +- "org.gnome.Evince.desktop", ++ "eog.desktop", ++ "evince.desktop", + "org.gnome.FileRoller.desktop", + "org.gnome.fonts.desktop", + "org.gnome.Screenshot.desktop", +- "org.gnome.seahorse.Application.desktop", ++ "seahorse.desktop", + "org.gnome.Terminal.desktop", + "org.gnome.tweaks.desktop", + "org.gnome.Usage.desktop", +-- +2.18.2 + diff --git a/SOURCES/0001-Lower-AsStore-new-API-version-checks-for-Fedora.patch b/SOURCES/0001-Lower-AsStore-new-API-version-checks-for-Fedora.patch deleted file mode 100644 index 3b61293..0000000 --- a/SOURCES/0001-Lower-AsStore-new-API-version-checks-for-Fedora.patch +++ /dev/null @@ -1,145 +0,0 @@ -From f91c54a70860ed12270bbafa121acae36f29badb Mon Sep 17 00:00:00 2001 -From: Kalev Lember -Date: Tue, 18 Dec 2018 02:44:31 +0100 -Subject: [PATCH] Lower AsStore new API version checks for Fedora - -We have new the AsStore thread safety patches along with new thread safe -API backported to libappstream-glib-0.7.14-3.fc29. ---- - plugins/core/gs-appstream.c | 12 ++++++------ - plugins/core/gs-plugin-appstream.c | 12 ++++++------ - plugins/flatpak/gs-flatpak.c | 2 +- - 3 files changed, 13 insertions(+), 13 deletions(-) - -diff --git a/plugins/core/gs-appstream.c b/plugins/core/gs-appstream.c -index b7bccd19a..7b606fe0d 100644 ---- a/plugins/core/gs-appstream.c -+++ b/plugins/core/gs-appstream.c -@@ -860,7 +860,7 @@ gs_appstream_store_search (GsPlugin *plugin, - gboolean ret = TRUE; - g_autoptr(GPtrArray) array = NULL; - --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - array = as_store_dup_apps (store); - #else - array = g_ptr_array_ref (as_store_get_apps (store)); -@@ -937,7 +937,7 @@ gs_appstream_store_add_category_apps (GsPlugin *plugin, - g_autoptr(GPtrArray) array = NULL; - - /* just look at each app in turn */ --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - array = as_store_dup_apps (store); - #else - array = g_ptr_array_ref (as_store_get_apps (store)); -@@ -985,7 +985,7 @@ gs_appstream_store_add_categories (GsPlugin *plugin, - g_autoptr(GPtrArray) array = NULL; - - /* find out how many packages are in each category */ --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - array = as_store_dup_apps (store); - #else - array = g_ptr_array_ref (as_store_get_apps (store)); -@@ -1013,7 +1013,7 @@ gs_appstream_add_popular (GsPlugin *plugin, - { - g_autoptr(GPtrArray) array = NULL; - --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - array = as_store_dup_apps (store); - #else - array = g_ptr_array_ref (as_store_get_apps (store)); -@@ -1059,7 +1059,7 @@ gs_appstream_add_recent (GsPlugin *plugin, - { - g_autoptr(GPtrArray) array = NULL; - --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - array = as_store_dup_apps (store); - #else - array = g_ptr_array_ref (as_store_get_apps (store)); -@@ -1088,7 +1088,7 @@ gs_appstream_add_featured (GsPlugin *plugin, - { - g_autoptr(GPtrArray) array = NULL; - --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - array = as_store_dup_apps (store); - #else - array = g_ptr_array_ref (as_store_get_apps (store)); -diff --git a/plugins/core/gs-plugin-appstream.c b/plugins/core/gs-plugin-appstream.c -index 77d004162..ce0ca9bda 100644 ---- a/plugins/core/gs-plugin-appstream.c -+++ b/plugins/core/gs-plugin-appstream.c -@@ -56,7 +56,7 @@ gs_plugin_appstream_create_app_hash (AsStore *store) - - hash = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) g_object_unref); --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - apps = as_store_dup_apps (store); - #else - apps = g_ptr_array_ref (as_store_get_apps (store)); -@@ -290,7 +290,7 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error) - return FALSE; - } - } --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - items = as_store_dup_apps (priv->store); - #else - items = g_ptr_array_ref (as_store_get_apps (priv->store)); -@@ -391,7 +391,7 @@ gs_plugin_refine_from_id (GsPlugin *plugin, - g_autoptr(GPtrArray) apps_merge = NULL; - - g_debug ("no app with ID %s found in system appstream", unique_id); --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - apps = as_store_dup_apps (priv->store); - #else - apps = g_ptr_array_ref (as_store_get_apps (priv->store)); -@@ -405,7 +405,7 @@ gs_plugin_refine_from_id (GsPlugin *plugin, - } - - /* fall back to trying to get a merge app */ --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - apps_merge = as_store_dup_apps_by_id_merge (priv->store, gs_app_get_id (app)); - for (guint i = 0; i < apps_merge->len; i++) { - item = g_ptr_array_index (apps_merge, i); -@@ -472,7 +472,7 @@ gs_plugin_add_distro_upgrades (GsPlugin *plugin, - g_autoptr(GPtrArray) array = NULL; - - /* find any upgrades */ --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - array = as_store_dup_apps (priv->store); - #else - array = g_ptr_array_ref (as_store_get_apps (priv->store)); -@@ -620,7 +620,7 @@ gs_plugin_add_installed (GsPlugin *plugin, - g_autoptr(GPtrArray) array = NULL; - - /* search categories for the search term */ --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - array = as_store_dup_apps (priv->store); - #else - array = g_ptr_array_ref (as_store_get_apps (priv->store)); -diff --git a/plugins/flatpak/gs-flatpak.c b/plugins/flatpak/gs-flatpak.c -index b835d843c..921fd12e9 100644 ---- a/plugins/flatpak/gs-flatpak.c -+++ b/plugins/flatpak/gs-flatpak.c -@@ -328,7 +328,7 @@ gs_flatpak_add_apps_from_xremote (GsFlatpak *self, - } - - /* override the *AppStream* origin */ --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - apps = as_store_dup_apps (store); - #else - apps = g_ptr_array_ref (as_store_get_apps (store)); --- -2.19.1 - diff --git a/SOURCES/0001-Lower-as_utils_vercmp_full-version-check-for-Fedora.patch b/SOURCES/0001-Lower-as_utils_vercmp_full-version-check-for-Fedora.patch deleted file mode 100644 index fdb2ccd..0000000 --- a/SOURCES/0001-Lower-as_utils_vercmp_full-version-check-for-Fedora.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 96f516a4e01b00094919c6c365d804a109b5875f Mon Sep 17 00:00:00 2001 -From: Kalev Lember -Date: Wed, 24 Oct 2018 15:00:59 +0200 -Subject: [PATCH] Lower as_utils_vercmp_full version check for Fedora - -We have new as_utils_vercmp_full API backported to -libappstream-glib-0.7.14-2.fc29. ---- - lib/gs-plugin-loader.c | 2 +- - src/gs-update-dialog.c | 4 ++-- - 2 files changed, 3 insertions(+), 3 deletions(-) - -diff --git a/lib/gs-plugin-loader.c b/lib/gs-plugin-loader.c -index 93e394906..b84a0ba65 100644 ---- a/lib/gs-plugin-loader.c -+++ b/lib/gs-plugin-loader.c -@@ -1423,7 +1423,7 @@ gs_plugin_loader_app_sort_match_value_cb (GsApp *app1, GsApp *app2, gpointer use - static gint - gs_plugin_loader_app_sort_version_cb (GsApp *app1, GsApp *app2, gpointer user_data) - { --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - return as_utils_vercmp_full (gs_app_get_version (app1), - gs_app_get_version (app2), - AS_VERSION_COMPARE_FLAG_NONE); -diff --git a/src/gs-update-dialog.c b/src/gs-update-dialog.c -index b4c1b1187..e92cd5b87 100644 ---- a/src/gs-update-dialog.c -+++ b/src/gs-update-dialog.c -@@ -374,7 +374,7 @@ is_downgrade (const gchar *evr1, - * part of the semantic version */ - - /* check version */ --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - rc = as_utils_vercmp_full (version1, version2, - AS_VERSION_COMPARE_FLAG_NONE); - #else -@@ -384,7 +384,7 @@ is_downgrade (const gchar *evr1, - return rc > 0; - - /* check release */ --#if AS_CHECK_VERSION(0,7,15) -+#if AS_CHECK_VERSION(0,7,14) - rc = as_utils_vercmp_full (version1, version2, - AS_VERSION_COMPARE_FLAG_NONE); - #else --- -2.19.1 - diff --git a/SOURCES/0001-details-page-Hide-addons-that-are-not-available-in-r.patch b/SOURCES/0001-details-page-Hide-addons-that-are-not-available-in-r.patch deleted file mode 100644 index 6a629ee..0000000 --- a/SOURCES/0001-details-page-Hide-addons-that-are-not-available-in-r.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 4107a4aafc2a524454ca0590ab4d36764e513ddf Mon Sep 17 00:00:00 2001 -From: Kalev Lember -Date: Fri, 12 Jul 2019 11:08:54 +0300 -Subject: [PATCH] details page: Hide addons that are not available in repos - -https://bugzilla.redhat.com/show_bug.cgi?id=1719779 ---- - src/gs-details-page.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/src/gs-details-page.c b/src/gs-details-page.c -index 2a59f8f08..5784250aa 100644 ---- a/src/gs-details-page.c -+++ b/src/gs-details-page.c -@@ -1383,7 +1383,8 @@ gs_details_page_refresh_addons (GsDetailsPage *self) - GtkWidget *row; - - addon = gs_app_list_index (addons, i); -- if (gs_app_get_state (addon) == AS_APP_STATE_UNAVAILABLE) -+ if (gs_app_get_state (addon) == AS_APP_STATE_UNKNOWN || -+ gs_app_get_state (addon) == AS_APP_STATE_UNAVAILABLE) - continue; - - row = gs_app_addon_row_new (addon); --- -2.21.0 - diff --git a/SOURCES/0001-epiphany-Don-t-adopt-apps-that-have-package-name-set.patch b/SOURCES/0001-epiphany-Don-t-adopt-apps-that-have-package-name-set.patch deleted file mode 100644 index cc12a51..0000000 --- a/SOURCES/0001-epiphany-Don-t-adopt-apps-that-have-package-name-set.patch +++ /dev/null @@ -1,35 +0,0 @@ -From d51c480fc9f1bcb1b878be156f88741a6bbb364d Mon Sep 17 00:00:00 2001 -From: Kalev Lember -Date: Tue, 24 Sep 2019 11:16:14 +0200 -Subject: [PATCH 1/2] epiphany: Don't adopt apps that have package name set - -In Fedora appstream metadata we have apps that -are supposed to be installed as packages and have the pkgname set. - -Instead of adopting them in the epiphany plugin, leave them for the -packagekit plugin to handle. - -https://gitlab.gnome.org/GNOME/gnome-software/issues/802 ---- - plugins/epiphany/gs-plugin-epiphany.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/plugins/epiphany/gs-plugin-epiphany.c b/plugins/epiphany/gs-plugin-epiphany.c -index 44e2e5bd..3db666e8 100644 ---- a/plugins/epiphany/gs-plugin-epiphany.c -+++ b/plugins/epiphany/gs-plugin-epiphany.c -@@ -56,8 +56,10 @@ gs_plugin_initialize (GsPlugin *plugin) - void - gs_plugin_adopt_app (GsPlugin *plugin, GsApp *app) - { -- if (gs_app_get_kind (app) == AS_APP_KIND_WEB_APP) -+ if (gs_app_get_kind (app) == AS_APP_KIND_WEB_APP && -+ gs_app_get_bundle_kind (app) != AS_BUNDLE_KIND_PACKAGE) { - gs_app_set_management_plugin (app, gs_plugin_get_name (plugin)); -+ } - } - - static gchar * --- -2.24.1 - diff --git a/SOURCES/0002-Add-webflow-auth-support-to-flatpak-plugin.patch b/SOURCES/0002-Add-webflow-auth-support-to-flatpak-plugin.patch new file mode 100644 index 0000000..2d0d0c8 --- /dev/null +++ b/SOURCES/0002-Add-webflow-auth-support-to-flatpak-plugin.patch @@ -0,0 +1,104 @@ +From de3afc6463aeb0e2d637a0360d1b96acffdf4e6d Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Tue, 19 May 2020 14:28:10 +0200 +Subject: [PATCH 2/2] Add webflow auth support to flatpak plugin + +This is just the minimal support, launching the auth page in the default +web browser when libflatpak signals that we need to do the webflow auth. + +Possible improvements could include doing either a webkitgtk dialog, or +maybe asking for confirmation before launching the web browser. +--- + plugins/flatpak/gs-plugin-flatpak.c | 69 +++++++++++++++++++++++++++++ + 1 file changed, 69 insertions(+) + +diff --git a/plugins/flatpak/gs-plugin-flatpak.c b/plugins/flatpak/gs-plugin-flatpak.c +index 2518025d..a453cec8 100644 +--- a/plugins/flatpak/gs-plugin-flatpak.c ++++ b/plugins/flatpak/gs-plugin-flatpak.c +@@ -503,6 +503,71 @@ _basic_auth_start (FlatpakTransaction *transaction, + gs_plugin_basic_auth_start (plugin, remote, realm, G_CALLBACK (_basic_auth_cb), data); + return TRUE; + } ++ ++static gboolean ++_webflow_start (FlatpakTransaction *transaction, ++ const char *remote, ++ const char *url, ++ GVariant *options, ++ guint id, ++ GsPlugin *plugin) ++{ ++ const char *browser; ++ g_autoptr(GError) error_local = NULL; ++ ++ if (!gs_plugin_has_flags (plugin, GS_PLUGIN_FLAGS_INTERACTIVE)) ++ return FALSE; ++ ++ g_debug ("Authentication required for remote '%s'", remote); ++ ++ /* Allow hard overrides with $BROWSER */ ++ browser = g_getenv ("BROWSER"); ++ if (browser != NULL) { ++ const char *args[3] = { NULL, url, NULL }; ++ args[0] = browser; ++ if (!g_spawn_async (NULL, (char **)args, NULL, G_SPAWN_SEARCH_PATH, ++ NULL, NULL, NULL, &error_local)) { ++ g_autoptr(GsPluginEvent) event = NULL; ++ ++ g_warning ("Failed to start browser %s: %s", browser, error_local->message); ++ ++ event = gs_plugin_event_new (); ++ gs_flatpak_error_convert (&error_local); ++ gs_plugin_event_set_error (event, error_local); ++ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING); ++ gs_plugin_report_event (plugin, event); ++ ++ return FALSE; ++ } ++ } else { ++ if (!g_app_info_launch_default_for_uri (url, NULL, &error_local)) { ++ g_autoptr(GsPluginEvent) event = NULL; ++ ++ g_warning ("Failed to show url: %s", error_local->message); ++ ++ event = gs_plugin_event_new (); ++ gs_flatpak_error_convert (&error_local); ++ gs_plugin_event_set_error (event, error_local); ++ gs_plugin_event_add_flag (event, GS_PLUGIN_EVENT_FLAG_WARNING); ++ gs_plugin_report_event (plugin, event); ++ ++ return FALSE; ++ } ++ } ++ ++ g_debug ("Waiting for browser..."); ++ ++ return TRUE; ++} ++ ++static void ++_webflow_done (FlatpakTransaction *transaction, ++ GVariant *options, ++ guint id, ++ GsPlugin *plugin) ++{ ++ g_debug ("Browser done"); ++} + #endif + + static FlatpakTransaction * +@@ -543,6 +608,10 @@ _build_transaction (GsPlugin *plugin, GsFlatpak *flatpak, + #if FLATPAK_CHECK_VERSION(1,6,0) + g_signal_connect (transaction, "basic-auth-start", + G_CALLBACK (_basic_auth_start), plugin); ++ g_signal_connect (transaction, "webflow-start", ++ G_CALLBACK (_webflow_start), plugin); ++ g_signal_connect (transaction, "webflow-done", ++ G_CALLBACK (_webflow_done), plugin); + #endif + + /* use system installations as dependency sources for user installations */ +-- +2.26.2 + diff --git a/SOURCES/0002-packagekit-Don-t-skip-over-web-apps.patch b/SOURCES/0002-packagekit-Don-t-skip-over-web-apps.patch deleted file mode 100644 index c3090b9..0000000 --- a/SOURCES/0002-packagekit-Don-t-skip-over-web-apps.patch +++ /dev/null @@ -1,39 +0,0 @@ -From d3b4d3bd00b3d664ed5873babf06baf42f624de3 Mon Sep 17 00:00:00 2001 -From: Kalev Lember -Date: Tue, 24 Sep 2019 11:19:31 +0200 -Subject: [PATCH 2/2] packagekit: Don't skip over web apps - -We now have web apps that are supposed to be installed as packages. -Don't just skip over all web apps, but rely on the management plugin -being correctly set. - -Fixes: https://gitlab.gnome.org/GNOME/gnome-software/issues/802 ---- - plugins/packagekit/gs-plugin-packagekit-refine.c | 4 ---- - 1 file changed, 4 deletions(-) - -diff --git a/plugins/packagekit/gs-plugin-packagekit-refine.c b/plugins/packagekit/gs-plugin-packagekit-refine.c -index 924b74a4..84849dc4 100644 ---- a/plugins/packagekit/gs-plugin-packagekit-refine.c -+++ b/plugins/packagekit/gs-plugin-packagekit-refine.c -@@ -487,8 +487,6 @@ gs_plugin_packagekit_refine_details (GsPlugin *plugin, - GsApp *app = gs_app_list_index (list, i); - if (gs_app_has_quirk (app, AS_APP_QUIRK_MATCH_ANY_PREFIX)) - continue; -- if (gs_app_get_kind (app) == AS_APP_KIND_WEB_APP) -- continue; - if (g_strcmp0 (gs_app_get_management_plugin (app), "packagekit") != 0) - continue; - if (gs_app_get_source_id_default (app) == NULL) -@@ -635,8 +633,6 @@ gs_plugin_packagekit_refine_name_to_id (GsPlugin *plugin, - const gchar *tmp; - if (gs_app_has_quirk (app, AS_APP_QUIRK_MATCH_ANY_PREFIX)) - continue; -- if (gs_app_get_kind (app) == AS_APP_KIND_WEB_APP) -- continue; - tmp = gs_app_get_management_plugin (app); - if (tmp != NULL && g_strcmp0 (tmp, "packagekit") != 0) - continue; --- -2.24.1 - diff --git a/SOURCES/add-back-shell-extensions-support.patch b/SOURCES/add-back-shell-extensions-support.patch new file mode 100644 index 0000000..d0eb41d --- /dev/null +++ b/SOURCES/add-back-shell-extensions-support.patch @@ -0,0 +1,1758 @@ +From 82a85d6bbacb148e355680446c4bbd6a091ad2eb Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Wed, 3 Jun 2020 15:50:41 +0200 +Subject: [PATCH 1/2] Revert "Remove support for Shell extensions" + +This reverts commit 77bdc3855c26de0edc2ce5d73bb6be861721c37b. +--- + contrib/gnome-software.spec.in | 1 + + meson_options.txt | 1 + + plugins/core/gs-appstream.c | 14 +- + plugins/core/gs-desktop-common.c | 3 + + plugins/meson.build | 3 + + plugins/packagekit/gs-plugin-packagekit.c | 3 + + plugins/shell-extensions/gs-appstream.c | 1 + + plugins/shell-extensions/gs-appstream.h | 1 + + .../gs-plugin-shell-extensions.c | 1159 +++++++++++++++++ + plugins/shell-extensions/gs-self-test.c | 156 +++ + plugins/shell-extensions/meson.build | 47 + + src/gs-category-page.c | 25 + + src/gs-category-page.ui | 45 + + src/gs-details-page.c | 14 +- + src/gs-repo-row.c | 3 +- + src/gs-repos-dialog.c | 3 +- + src/gs-summary-tile.c | 15 +- + 17 files changed, 1478 insertions(+), 16 deletions(-) + create mode 120000 plugins/shell-extensions/gs-appstream.c + create mode 120000 plugins/shell-extensions/gs-appstream.h + create mode 100644 plugins/shell-extensions/gs-plugin-shell-extensions.c + create mode 100644 plugins/shell-extensions/gs-self-test.c + create mode 100644 plugins/shell-extensions/meson.build + +diff --git a/contrib/gnome-software.spec.in b/contrib/gnome-software.spec.in +index 587a72e6..7f415f51 100644 +--- a/contrib/gnome-software.spec.in ++++ b/contrib/gnome-software.spec.in +@@ -183,6 +183,7 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop + %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_provenance.so + %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_repos.so + %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_rewrite-resource.so ++%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_shell-extensions.so + %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_systemd-updates.so + %{_sysconfdir}/xdg/autostart/gnome-software-service.desktop + %{_datadir}/app-info/xmls/org.gnome.Software.Featured.xml +diff --git a/meson_options.txt b/meson_options.txt +index f6068ba5..eba69ea2 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -10,6 +10,7 @@ option('fwupd', type : 'boolean', value : true, description : 'enable fwupd supp + option('flatpak', type : 'boolean', value : true, description : 'enable Flatpak support') + option('malcontent', type : 'boolean', value : true, description : 'enable parental controls support using libmalcontent') + option('rpm_ostree', type : 'boolean', value : false, description : 'enable rpm-ostree support') ++option('shell_extensions', type : 'boolean', value : true, description : 'enable shell extensions support') + option('odrs', type : 'boolean', value : true, description : 'enable ODRS support') + option('gudev', type : 'boolean', value : true, description : 'enable GUdev support') + option('snap', type : 'boolean', value : false, description : 'enable Snap support') +diff --git a/plugins/core/gs-appstream.c b/plugins/core/gs-appstream.c +index a387f2e0..e507ac0f 100644 +--- a/plugins/core/gs-appstream.c ++++ b/plugins/core/gs-appstream.c +@@ -30,15 +30,6 @@ gs_appstream_create_app (GsPlugin *plugin, XbSilo *silo, XbNode *component, GErr + if (gs_app_has_quirk (app_new, GS_APP_QUIRK_IS_WILDCARD)) + return g_steal_pointer (&app_new); + +- /* no longer supported */ +- if (gs_app_get_kind (app_new) == AS_APP_KIND_SHELL_EXTENSION) { +- g_set_error (error, +- GS_PLUGIN_ERROR, +- GS_PLUGIN_ERROR_NOT_SUPPORTED, +- "shell extensions no longer supported"); +- return NULL; +- } +- + /* look for existing object */ + app = gs_plugin_cache_lookup (plugin, gs_app_get_unique_id (app_new)); + if (app != NULL) +@@ -1548,6 +1539,11 @@ gs_appstream_component_add_extra_info (GsPlugin *plugin, XbBuilderNode *componen + gs_appstream_component_add_category (component, "Addon"); + gs_appstream_component_add_category (component, "Font"); + break; ++ case AS_APP_KIND_SHELL_EXTENSION: ++ gs_appstream_component_add_category (component, "Addon"); ++ gs_appstream_component_add_category (component, "ShellExtension"); ++ gs_appstream_component_add_icon (component, "application-x-addon-symbolic"); ++ break; + case AS_APP_KIND_DRIVER: + gs_appstream_component_add_category (component, "Addon"); + gs_appstream_component_add_category (component, "Driver"); +diff --git a/plugins/core/gs-desktop-common.c b/plugins/core/gs-desktop-common.c +index 33ae3fa2..17ed029d 100644 +--- a/plugins/core/gs-desktop-common.c ++++ b/plugins/core/gs-desktop-common.c +@@ -203,6 +203,9 @@ static const GsDesktopMap map_addons[] = { + { "language-packs", NC_("Menu of Add-ons", "Language Packs"), + { "Addon::LanguagePack", + NULL} }, ++ { "shell-extensions", NC_("Menu of Add-ons", "Shell Extensions"), ++ { "Addon::ShellExtension", ++ NULL} }, + { "localization", NC_("Menu of Add-ons", "Localization"), + { "Addon::Localization", + NULL} }, +diff --git a/plugins/meson.build b/plugins/meson.build +index d749b3df..d30f14d4 100644 +--- a/plugins/meson.build ++++ b/plugins/meson.build +@@ -39,6 +39,9 @@ subdir('repos') + if get_option('rpm_ostree') + subdir('rpm-ostree') + endif ++if get_option('shell_extensions') ++ subdir('shell-extensions') ++endif + if get_option('snap') + subdir('snap') + endif +diff --git a/plugins/packagekit/gs-plugin-packagekit.c b/plugins/packagekit/gs-plugin-packagekit.c +index d0bdabae..2c4e1644 100644 +--- a/plugins/packagekit/gs-plugin-packagekit.c ++++ b/plugins/packagekit/gs-plugin-packagekit.c +@@ -688,5 +688,8 @@ gs_plugin_launch (GsPlugin *plugin, + if (g_strcmp0 (gs_app_get_management_plugin (app), + gs_plugin_get_name (plugin)) != 0) + return TRUE; ++ /* these are handled by the shell extensions plugin */ ++ if (gs_app_get_kind (app) == AS_APP_KIND_SHELL_EXTENSION) ++ return TRUE; + return gs_plugin_app_launch (plugin, app, error); + } +diff --git a/plugins/shell-extensions/gs-appstream.c b/plugins/shell-extensions/gs-appstream.c +new file mode 120000 +index 00000000..96326ab0 +--- /dev/null ++++ b/plugins/shell-extensions/gs-appstream.c +@@ -0,0 +1 @@ ++../core/gs-appstream.c +\ No newline at end of file +diff --git a/plugins/shell-extensions/gs-appstream.h b/plugins/shell-extensions/gs-appstream.h +new file mode 120000 +index 00000000..4eabcb3c +--- /dev/null ++++ b/plugins/shell-extensions/gs-appstream.h +@@ -0,0 +1 @@ ++../core/gs-appstream.h +\ No newline at end of file +diff --git a/plugins/shell-extensions/gs-plugin-shell-extensions.c b/plugins/shell-extensions/gs-plugin-shell-extensions.c +new file mode 100644 +index 00000000..80a5d0eb +--- /dev/null ++++ b/plugins/shell-extensions/gs-plugin-shell-extensions.c +@@ -0,0 +1,1159 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2016-2018 Richard Hughes ++ * Copyright (C) 2018 Kalev Lember ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "gs-appstream.h" ++ ++#define SHELL_EXTENSIONS_API_URI "https://extensions.gnome.org/" ++ ++/* ++ * Things we want from the API: ++ * ++ * - Size on disk/download ++ * - Existing review data for each extension? ++ * - A local icon for an installed shell extension ++ * ++ * See https://git.gnome.org/browse/extensions-web/tree/sweettooth/extensions/views.py ++ * for the source to the web application. ++ */ ++ ++struct GsPluginData { ++ GDBusProxy *proxy; ++ gchar *shell_version; ++ GsApp *cached_origin; ++ GSettings *settings; ++ XbSilo *silo; ++ GRWLock silo_lock; ++}; ++ ++typedef enum { ++ GS_PLUGIN_SHELL_EXTENSION_STATE_ENABLED = 1, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_DISABLED = 2, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_ERROR = 3, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_OUT_OF_DATE = 4, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_DOWNLOADING = 5, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_INITIALIZED = 6, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_UNINSTALLED = 99, ++ GS_PLUGIN_SHELL_EXTENSION_STATE_LAST ++} GsPluginShellExtensionState; ++ ++typedef enum { ++ GS_PLUGIN_SHELL_EXTENSION_KIND_SYSTEM = 1, ++ GS_PLUGIN_SHELL_EXTENSION_KIND_PER_USER = 2, ++ GS_PLUGIN_SHELL_EXTENSION_KIND_LAST ++} GsPluginShellExtensionKind; ++ ++static gboolean _check_silo (GsPlugin *plugin, GCancellable *cancellable, GError **error); ++ ++void ++gs_plugin_initialize (GsPlugin *plugin) ++{ ++ GsPluginData *priv = gs_plugin_alloc_data (plugin, sizeof(GsPluginData)); ++ ++ /* XbSilo needs external locking as we destroy the silo and build a new ++ * one when something changes */ ++ g_rw_lock_init (&priv->silo_lock); ++ ++ /* add source */ ++ priv->cached_origin = gs_app_new (gs_plugin_get_name (plugin)); ++ gs_app_set_kind (priv->cached_origin, AS_APP_KIND_SOURCE); ++ gs_app_set_origin_hostname (priv->cached_origin, SHELL_EXTENSIONS_API_URI); ++ gs_app_set_origin (priv->cached_origin, _("GNOME")); ++ ++ priv->settings = g_settings_new ("org.gnome.software"); ++ ++ /* add the source to the plugin cache which allows us to match the ++ * unique ID to a GsApp when creating an event */ ++ gs_plugin_cache_add (plugin, ++ gs_app_get_unique_id (priv->cached_origin), ++ priv->cached_origin); ++} ++ ++void ++gs_plugin_destroy (GsPlugin *plugin) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_free (priv->shell_version); ++ if (priv->proxy != NULL) ++ g_object_unref (priv->proxy); ++ if (priv->silo != NULL) ++ g_object_unref (priv->silo); ++ g_object_unref (priv->cached_origin); ++ g_object_unref (priv->settings); ++ g_rw_lock_clear (&priv->silo_lock); ++} ++ ++void ++gs_plugin_adopt_app (GsPlugin *plugin, GsApp *app) ++{ ++ if (gs_app_get_kind (app) == AS_APP_KIND_SHELL_EXTENSION && ++ gs_app_get_scope (app) == AS_APP_SCOPE_USER) { ++ gs_app_set_management_plugin (app, gs_plugin_get_name (plugin)); ++ } ++} ++ ++static AsAppState ++gs_plugin_shell_extensions_convert_state (guint value) ++{ ++ switch (value) { ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_DISABLED: ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_DOWNLOADING: ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_ENABLED: ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_ERROR: ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_INITIALIZED: ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_OUT_OF_DATE: ++ return AS_APP_STATE_INSTALLED; ++ case GS_PLUGIN_SHELL_EXTENSION_STATE_UNINSTALLED: ++ return AS_APP_STATE_AVAILABLE; ++ default: ++ g_warning ("unknown state %u", value); ++ } ++ return AS_APP_STATE_UNKNOWN; ++} ++ ++static GsApp * ++gs_plugin_shell_extensions_parse_installed (GsPlugin *plugin, ++ const gchar *uuid, ++ GVariantIter *iter, ++ GError **error) ++{ ++ const gchar *tmp; ++ gchar *str; ++ GVariant *val; ++ g_autofree gchar *id = NULL; ++ g_autoptr(AsIcon) ic = NULL; ++ g_autoptr(GsApp) app = NULL; ++ ++ id = as_utils_appstream_id_build (uuid); ++ app = gs_app_new (id); ++ gs_app_set_metadata (app, "GnomeSoftware::Creator", ++ gs_plugin_get_name (plugin)); ++ gs_app_set_management_plugin (app, gs_plugin_get_name (plugin)); ++ gs_app_set_metadata (app, "shell-extensions::uuid", uuid); ++ gs_app_set_kind (app, AS_APP_KIND_SHELL_EXTENSION); ++ gs_app_set_license (app, GS_APP_QUALITY_NORMAL, "GPL-2.0+"); ++ gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "GNOME Shell Extension"); ++ gs_app_set_origin_hostname (app, SHELL_EXTENSIONS_API_URI); ++ gs_app_set_origin (app, _("GNOME")); ++ while (g_variant_iter_loop (iter, "{sv}", &str, &val)) { ++ if (g_strcmp0 (str, "description") == 0) { ++ g_autofree gchar *tmp1 = NULL; ++ g_autofree gchar *tmp2 = NULL; ++ tmp1 = as_markup_import (g_variant_get_string (val, NULL), ++ AS_MARKUP_CONVERT_FORMAT_SIMPLE, ++ NULL); ++ tmp2 = as_markup_convert_simple (tmp1, error); ++ if (tmp2 == NULL) { ++ gs_utils_error_convert_appstream (error); ++ return NULL; ++ } ++ gs_app_set_description (app, GS_APP_QUALITY_NORMAL, tmp2); ++ continue; ++ } ++ if (g_strcmp0 (str, "name") == 0) { ++ gs_app_set_name (app, GS_APP_QUALITY_NORMAL, ++ g_variant_get_string (val, NULL)); ++ continue; ++ } ++ if (g_strcmp0 (str, "version") == 0) { ++ guint val_int = (guint) g_variant_get_double (val); ++ g_autofree gchar *version = g_strdup_printf ("%u", val_int); ++ gs_app_set_version (app, version); ++ continue; ++ } ++ if (g_strcmp0 (str, "url") == 0) { ++ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, ++ g_variant_get_string (val, NULL)); ++ continue; ++ } ++ if (g_strcmp0 (str, "type") == 0) { ++ guint val_int = (guint) g_variant_get_double (val); ++ switch (val_int) { ++ case GS_PLUGIN_SHELL_EXTENSION_KIND_SYSTEM: ++ gs_app_set_scope (app, AS_APP_SCOPE_SYSTEM); ++ break; ++ case GS_PLUGIN_SHELL_EXTENSION_KIND_PER_USER: ++ gs_app_set_scope (app, AS_APP_SCOPE_USER); ++ break; ++ default: ++ g_warning ("%s unknown type %u", uuid, val_int); ++ break; ++ } ++ continue; ++ } ++ if (g_strcmp0 (str, "state") == 0) { ++ AsAppState st; ++ guint val_int = (guint) g_variant_get_double (val); ++ st = gs_plugin_shell_extensions_convert_state (val_int); ++ gs_app_set_state (app, st); ++ continue; ++ } ++ if (g_strcmp0 (str, "error") == 0) { ++ tmp = g_variant_get_string (val, NULL); ++ if (tmp != NULL && tmp[0] != '\0') { ++ g_warning ("unhandled shell error: %s", tmp); ++ } ++ continue; ++ } ++ if (g_strcmp0 (str, "hasPrefs") == 0) { ++ if (g_variant_get_boolean (val)) ++ gs_app_set_metadata (app, "shell-extensions::has-prefs", ""); ++ continue; ++ } ++ if (g_strcmp0 (str, "extension-id") == 0) { ++ tmp = g_variant_get_string (val, NULL); ++ gs_app_set_metadata (app, "shell-extensions::extension-id", tmp); ++ continue; ++ } ++ if (g_strcmp0 (str, "path") == 0) { ++ tmp = g_variant_get_string (val, NULL); ++ gs_app_set_metadata (app, "shell-extensions::path", tmp); ++ continue; ++ } ++ } ++ ++ /* hardcode icon */ ++ ic = as_icon_new (); ++ as_icon_set_kind (ic, AS_ICON_KIND_STOCK); ++ as_icon_set_name (ic, "application-x-addon-symbolic"); ++ gs_app_add_icon (app, ic); ++ ++ /* add categories */ ++ gs_app_add_category (app, "Addon"); ++ gs_app_add_category (app, "ShellExtension"); ++ ++ return g_steal_pointer (&app); ++} ++ ++static void ++gs_plugin_shell_extensions_changed_cb (GDBusProxy *proxy, ++ const gchar *sender_name, ++ const gchar *signal_name, ++ GVariant *parameters, ++ GsPlugin *plugin) ++{ ++ if (g_strcmp0 (signal_name, "ExtensionStatusChanged") == 0) { ++ AsAppState st; ++ GsApp *app; ++ const gchar *error_str; ++ const gchar *uuid; ++ guint state; ++ ++ /* get what changed */ ++ g_variant_get (parameters, "(&si&s)", ++ &uuid, &state, &error_str); ++ ++ /* find it in the cache; do we care? */ ++ app = gs_plugin_cache_lookup (plugin, uuid); ++ if (app == NULL) { ++ g_debug ("no app for changed %s", uuid); ++ return; ++ } ++ ++ /* set the new state in the UI */ ++ st = gs_plugin_shell_extensions_convert_state (state); ++ gs_app_set_state (app, st); ++ ++ /* not sure what to do here */ ++ if (error_str != NULL && error_str[0] != '\0') { ++ g_warning ("%s has error: %s", ++ gs_app_get_id (app), ++ error_str); ++ } ++ } ++} ++ ++gboolean ++gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autofree gchar *name_owner = NULL; ++ g_autoptr(GVariant) version = NULL; ++ ++ if (priv->proxy != NULL) ++ return TRUE; ++ priv->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, ++ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START_AT_CONSTRUCTION, ++ NULL, ++ "org.gnome.Shell", ++ "/org/gnome/Shell", ++ "org.gnome.Shell.Extensions", ++ cancellable, ++ error); ++ if (priv->proxy == NULL) { ++ gs_utils_error_convert_gio (error); ++ return FALSE; ++ } ++ ++ /* not running under Shell */ ++ name_owner = g_dbus_proxy_get_name_owner (priv->proxy); ++ if (name_owner == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_NOT_SUPPORTED, ++ "gnome-shell is not running"); ++ return FALSE; ++ } ++ ++ g_signal_connect (priv->proxy, "g-signal", ++ G_CALLBACK (gs_plugin_shell_extensions_changed_cb), plugin); ++ ++ /* get the GNOME Shell version */ ++ version = g_dbus_proxy_get_cached_property (priv->proxy, ++ "ShellVersion"); ++ if (version == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_NOT_SUPPORTED, ++ "unable to get shell version"); ++ return FALSE; ++ } ++ priv->shell_version = g_variant_dup_string (version, NULL); ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_add_installed (GsPlugin *plugin, ++ GsAppList *list, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ GVariantIter *ext_iter; ++ gchar *ext_uuid; ++ g_autoptr(GVariantIter) iter = NULL; ++ g_autoptr(GVariant) retval = NULL; ++ ++ /* installed */ ++ retval = g_dbus_proxy_call_sync (priv->proxy, ++ "ListExtensions", ++ NULL, ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ error); ++ if (retval == NULL) { ++ gs_utils_error_convert_gdbus (error); ++ gs_utils_error_convert_gio (error); ++ return FALSE; ++ } ++ ++ /* parse each installed extension */ ++ g_variant_get (retval, "(a{sa{sv}})", &iter); ++ while (g_variant_iter_loop (iter, "{sa{sv}}", &ext_uuid, &ext_iter)) { ++ g_autoptr(GsApp) app = NULL; ++ ++ /* search in the cache */ ++ app = gs_plugin_cache_lookup (plugin, ext_uuid); ++ if (app != NULL) { ++ gs_app_list_add (list, app); ++ continue; ++ } ++ ++ /* parse the data into an GsApp */ ++ app = gs_plugin_shell_extensions_parse_installed (plugin, ++ ext_uuid, ++ ext_iter, ++ error); ++ if (app == NULL) ++ return FALSE; ++ ++ /* ignore system installed */ ++ if (gs_app_get_scope (app) == AS_APP_SCOPE_SYSTEM) ++ continue; ++ ++ /* save in the cache */ ++ gs_plugin_cache_add (plugin, ext_uuid, app); ++ ++ /* add to results */ ++ gs_app_list_add (list, app); ++ } ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_add_sources (GsPlugin *plugin, ++ GsAppList *list, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autoptr(GsApp) app = NULL; ++ ++ /* create something that we can use to enable/disable */ ++ app = gs_app_new ("org.gnome.extensions"); ++ gs_app_set_kind (app, AS_APP_KIND_SOURCE); ++ gs_app_set_scope (app, AS_APP_SCOPE_USER); ++ if (g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ gs_app_set_state (app, AS_APP_STATE_INSTALLED); ++ else ++ gs_app_set_state (app, AS_APP_STATE_AVAILABLE); ++ gs_app_add_quirk (app, GS_APP_QUIRK_NOT_LAUNCHABLE); ++ gs_app_set_name (app, GS_APP_QUALITY_LOWEST, ++ _("GNOME Shell Extensions Repository")); ++ gs_app_set_url (app, AS_URL_KIND_HOMEPAGE, ++ SHELL_EXTENSIONS_API_URI); ++ gs_app_set_management_plugin (app, gs_plugin_get_name (plugin)); ++ gs_app_list_add (list, app); ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_refine_app (GsPlugin *plugin, ++ GsApp *app, ++ GsPluginRefineFlags flags, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ const gchar *uuid; ++ g_autofree gchar *xpath = NULL; ++ g_autoptr(GError) error_local = NULL; ++ g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(XbNode) component = NULL; ++ ++ /* repo not enabled */ ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ ++ /* only process this app if was created by this plugin */ ++ if (g_strcmp0 (gs_app_get_management_plugin (app), ++ gs_plugin_get_name (plugin)) != 0) ++ return TRUE; ++ ++ /* can we get the AppStream-created app state using the cache */ ++ uuid = gs_app_get_metadata_item (app, "shell-extensions::uuid"); ++ if (uuid != NULL && gs_app_get_state (app) == AS_APP_STATE_UNKNOWN) { ++ GsApp *app_cache = gs_plugin_cache_lookup (plugin, uuid); ++ if (app_cache != NULL) { ++ g_debug ("copy cached state for %s", ++ gs_app_get_id (app)); ++ gs_app_set_state (app, gs_app_get_state (app_cache)); ++ } ++ } ++ ++ /* assume apps are available if they exist in AppStream metadata */ ++ if (gs_app_get_state (app) == AS_APP_STATE_UNKNOWN) ++ gs_app_set_state (app, AS_APP_STATE_AVAILABLE); ++ ++ /* FIXME: assume these are small */ ++ if (gs_app_get_size_installed (app) == 0) ++ gs_app_set_size_installed (app, 1024 * 50); ++ if (gs_app_get_size_download (app) == 0) ++ gs_app_set_size_download (app, GS_APP_SIZE_UNKNOWABLE); ++ ++ ++ /* check silo is valid */ ++ if (!_check_silo (plugin, cancellable, error)) ++ return FALSE; ++ ++ locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ ++ /* find the component using the UUID */ ++ if (uuid == NULL) ++ return TRUE; ++ ++ xpath = g_strdup_printf ("components/component/custom/" ++ "value[@key='shell-extensions::uuid'][text()='%s']/../..", ++ uuid); ++ component = xb_silo_query_first (priv->silo, xpath, &error_local); ++ if (component == NULL) { ++ if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) ++ return TRUE; ++ if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) ++ return TRUE; ++ g_propagate_error (error, g_steal_pointer (&error_local)); ++ return FALSE; ++ } ++ return gs_appstream_refine_app (plugin, app, priv->silo, component, flags, error); ++} ++ ++gboolean ++gs_plugin_refine_wildcard (GsPlugin *plugin, ++ GsApp *app, ++ GsAppList *list, ++ GsPluginRefineFlags refine_flags, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ const gchar *id; ++ g_autofree gchar *xpath = NULL; ++ g_autoptr(GError) error_local = NULL; ++ g_autoptr(GPtrArray) components = NULL; ++ g_autoptr(GRWLockReaderLocker) locker = NULL; ++ ++ /* repo not enabled */ ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ ++ /* check silo is valid */ ++ if (!_check_silo (plugin, cancellable, error)) ++ return FALSE; ++ ++ /* not enough info to find */ ++ id = gs_app_get_id (app); ++ if (id == NULL) ++ return TRUE; ++ ++ locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ ++ /* find all apps */ ++ xpath = g_strdup_printf ("components/component/id[text()='%s']/..", id); ++ components = xb_silo_query (priv->silo, xpath, 0, &error_local); ++ if (components == NULL) { ++ if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) ++ return TRUE; ++ if (g_error_matches (error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) ++ return TRUE; ++ g_propagate_error (error, g_steal_pointer (&error_local)); ++ return FALSE; ++ } ++ for (guint i = 0; i < components->len; i++) { ++ XbNode *component = g_ptr_array_index (components, i); ++ g_autoptr(GsApp) new = NULL; ++ new = gs_appstream_create_app (plugin, priv->silo, component, error); ++ if (new == NULL) ++ return FALSE; ++ gs_app_subsume_metadata (new, app); ++ if (!gs_appstream_refine_app (plugin, new, priv->silo, component, ++ refine_flags, error)) ++ return FALSE; ++ gs_app_list_add (list, new); ++ } ++ ++ /* success */ ++ return TRUE; ++} ++ ++static gboolean ++gs_plugin_shell_extensions_parse_version (GsPlugin *plugin, ++ const gchar *component_id, ++ XbBuilderNode *app, ++ JsonObject *ver_map, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ JsonObject *json_ver = NULL; ++ gint64 version; ++ g_autofree gchar *shell_version = NULL; ++ ++ /* look for version, major.minor.micro */ ++ if (json_object_has_member (ver_map, priv->shell_version)) { ++ json_ver = json_object_get_object_member (ver_map, ++ priv->shell_version); ++ } ++ ++ /* look for version, major.minor */ ++ if (json_ver == NULL) { ++ g_auto(GStrv) ver_majmin = NULL; ++ ver_majmin = g_strsplit (priv->shell_version, ".", -1); ++ if (g_strv_length (ver_majmin) >= 2) { ++ g_autofree gchar *tmp = NULL; ++ tmp = g_strdup_printf ("%s.%s", ver_majmin[0], ver_majmin[1]); ++ if (json_object_has_member (ver_map, tmp)) ++ json_ver = json_object_get_object_member (ver_map, tmp); ++ } ++ } ++ ++ /* FIXME: mark as incompatible? */ ++ if (json_ver == NULL) ++ return TRUE; ++ ++ /* parse the version */ ++ version = json_object_get_int_member (json_ver, "version"); ++ if (version == 0) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no version in map!"); ++ return FALSE; ++ } ++ shell_version = g_strdup_printf ("%" G_GINT64_FORMAT, version); ++ ++ /* add a dummy release */ ++ xb_builder_node_insert_text (app, "release", NULL, ++ "version", shell_version, ++ NULL); ++ return TRUE; ++} ++ ++ ++ ++static XbBuilderNode * ++gs_plugin_shell_extensions_parse_app (GsPlugin *plugin, ++ JsonObject *json_app, ++ GError **error) ++{ ++ JsonObject *json_ver_map; ++ const gchar *tmp; ++ g_autofree gchar *component_id = NULL; ++ g_autoptr(XbBuilderNode) app = NULL; ++ g_autoptr(XbBuilderNode) categories = NULL; ++ g_autoptr(XbBuilderNode) metadata = NULL; ++ ++ app = xb_builder_node_new ("component"); ++ xb_builder_node_set_attr (app, "kind", "shell-extension"); ++ xb_builder_node_insert_text (app, "project_license", "GPL-2.0+", NULL); ++ categories = xb_builder_node_insert (app, "categories", NULL); ++ xb_builder_node_insert_text (categories, "category", "Addon", NULL); ++ xb_builder_node_insert_text (categories, "category", "ShellExtension", NULL); ++ metadata = xb_builder_node_insert (app, "custom", NULL); ++ ++ tmp = json_object_get_string_member (json_app, "description"); ++ if (tmp != NULL) { ++ g_auto(GStrv) paras = g_strsplit (tmp, "\n", -1); ++ g_autoptr(XbBuilderNode) desc = xb_builder_node_insert (app, "description", NULL); ++ for (guint i = 0; paras[i] != NULL; i++) ++ xb_builder_node_insert_text (desc, "p", paras[i], NULL); ++ } ++ tmp = json_object_get_string_member (json_app, "screenshot"); ++ if (tmp != NULL) { ++ g_autoptr(XbBuilderNode) screenshots = NULL; ++ g_autoptr(XbBuilderNode) screenshot = NULL; ++ g_autofree gchar *uri = NULL; ++ screenshots = xb_builder_node_insert (app, "screenshots", NULL); ++ screenshot = xb_builder_node_insert (screenshots, "screenshot", ++ "kind", "default", ++ NULL); ++ uri = g_build_path ("/", SHELL_EXTENSIONS_API_URI, tmp, NULL); ++ xb_builder_node_insert_text (screenshot, "image", uri, ++ "kind", "source", ++ NULL); ++ } ++ tmp = json_object_get_string_member (json_app, "name"); ++ if (tmp != NULL) ++ xb_builder_node_insert_text (app, "name", tmp, NULL); ++ tmp = json_object_get_string_member (json_app, "uuid"); ++ if (tmp != NULL) { ++ component_id = as_utils_appstream_id_build (tmp); ++ xb_builder_node_insert_text (app, "id", component_id, NULL); ++ xb_builder_node_insert_text (metadata, "value", tmp, ++ "key", "shell-extensions::uuid", ++ NULL); ++ } ++ tmp = json_object_get_string_member (json_app, "link"); ++ if (tmp != NULL) { ++ g_autofree gchar *uri = NULL; ++ uri = g_build_filename (SHELL_EXTENSIONS_API_URI, tmp, NULL); ++ xb_builder_node_insert_text (app, "url", uri, ++ "type", "homepage", ++ NULL); ++ } ++ tmp = json_object_get_string_member (json_app, "icon"); ++ if (tmp != NULL) { ++ /* just use a stock icon as the remote icons are ++ * sometimes missing, poor quality and low resolution */ ++ xb_builder_node_insert_text (app, "icon", ++ "application-x-addon-symbolic", ++ "type", "stock", ++ NULL); ++ } ++ ++ /* try to get version */ ++ json_ver_map = json_object_get_object_member (json_app, "shell_version_map"); ++ if (json_ver_map != NULL) { ++ if (!gs_plugin_shell_extensions_parse_version (plugin, ++ component_id, ++ app, ++ json_ver_map, ++ error)) ++ return NULL; ++ } ++ ++ return g_steal_pointer (&app); ++} ++ ++static GInputStream * ++gs_plugin_appstream_load_json_cb (XbBuilderSource *self, ++ XbBuilderSourceCtx *ctx, ++ gpointer user_data, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPlugin *plugin = GS_PLUGIN (user_data); ++ JsonArray *json_extensions_array; ++ JsonNode *json_extensions; ++ JsonNode *json_root; ++ JsonObject *json_item; ++ gchar *xml; ++ g_autoptr(JsonParser) json_parser = NULL; ++ g_autoptr(XbBuilderNode) apps = NULL; ++ ++ /* parse the data and find the success */ ++ json_parser = json_parser_new (); ++ if (!json_parser_load_from_stream (json_parser, ++ xb_builder_source_ctx_get_stream (ctx), ++ cancellable, error)) { ++ gs_utils_error_convert_json_glib (error); ++ return NULL; ++ } ++ json_root = json_parser_get_root (json_parser); ++ if (json_root == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no data root"); ++ return NULL; ++ } ++ if (json_node_get_node_type (json_root) != JSON_NODE_OBJECT) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no data object"); ++ return NULL; ++ } ++ json_item = json_node_get_object (json_root); ++ if (json_item == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no data object"); ++ return NULL; ++ } ++ ++ /* load extensions */ ++ apps = xb_builder_node_new ("components"); ++ json_extensions = json_object_get_member (json_item, "extensions"); ++ if (json_extensions == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no extensions object"); ++ return NULL; ++ } ++ json_extensions_array = json_node_get_array (json_extensions); ++ if (json_extensions_array == NULL) { ++ g_set_error_literal (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_INVALID_FORMAT, ++ "no extensions array"); ++ return NULL; ++ } ++ ++ /* parse each app */ ++ for (guint i = 0; i < json_array_get_length (json_extensions_array); i++) { ++ JsonNode *json_extension; ++ JsonObject *json_extension_obj; ++ g_autoptr(XbBuilderNode) component = NULL; ++ ++ json_extension = json_array_get_element (json_extensions_array, i); ++ json_extension_obj = json_node_get_object (json_extension); ++ component = gs_plugin_shell_extensions_parse_app (plugin, ++ json_extension_obj, ++ error); ++ if (component == NULL) ++ return NULL; ++ xb_builder_node_add_child (apps, component); ++ } ++ ++ /* convert back to XML */ ++ xml = xb_builder_node_export (apps, XB_NODE_EXPORT_FLAG_ADD_HEADER, error); ++ if (xml == NULL) ++ return NULL; ++ return g_memory_input_stream_new_from_data (xml, -1, g_free); ++} ++ ++static gboolean ++gs_plugin_shell_extensions_refresh (GsPlugin *plugin, ++ guint cache_age, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autofree gchar *fn = NULL; ++ g_autofree gchar *uri = NULL; ++ g_autoptr(GFile) file = NULL; ++ g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(GsApp) app_dl = gs_app_new (gs_plugin_get_name (plugin)); ++ ++ /* get cache filename */ ++ fn = gs_utils_get_cache_filename ("shell-extensions", ++ "gnome.json", ++ GS_UTILS_CACHE_FLAG_WRITEABLE, ++ error); ++ if (fn == NULL) ++ return FALSE; ++ ++ /* check age */ ++ file = g_file_new_for_path (fn); ++ if (g_file_query_exists (file, NULL)) { ++ guint age = gs_utils_get_file_age (file); ++ if (age < cache_age) { ++ g_debug ("%s is only %u seconds old, ignoring", fn, age); ++ return TRUE; ++ } ++ } ++ ++ /* download the file */ ++ uri = g_strdup_printf ("%s/static/extensions.json", ++ SHELL_EXTENSIONS_API_URI); ++ gs_app_set_summary_missing (app_dl, ++ /* TRANSLATORS: status text when downloading */ ++ _("Downloading shell extension metadata…")); ++ if (!gs_plugin_download_file (plugin, app_dl, uri, fn, cancellable, error)) { ++ gs_utils_error_add_origin_id (error, priv->cached_origin); ++ return FALSE; ++ } ++ ++ /* be explicit */ ++ locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ if (priv->silo != NULL) ++ xb_silo_invalidate (priv->silo); ++ ++ return TRUE; ++} ++ ++static gboolean ++_check_silo (GsPlugin *plugin, GCancellable *cancellable, GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autofree gchar *blobfn = NULL; ++ g_autofree gchar *fn = NULL; ++ g_autoptr(GError) error_local = NULL; ++ g_autoptr(GFile) blobfile = NULL; ++ g_autoptr(GFile) file = NULL; ++ g_autoptr(GRWLockReaderLocker) reader_locker = NULL; ++ g_autoptr(GRWLockWriterLocker) writer_locker = NULL; ++ g_autoptr(XbBuilder) builder = xb_builder_new (); ++ g_autoptr(XbBuilderNode) info = NULL; ++ g_autoptr(XbBuilderSource) source = xb_builder_source_new (); ++ ++ reader_locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ /* everything is okay */ ++ if (priv->silo != NULL && xb_silo_is_valid (priv->silo)) { ++ g_debug ("silo already valid"); ++ return TRUE; ++ } ++ g_clear_pointer (&reader_locker, g_rw_lock_reader_locker_free); ++ ++ /* drat! silo needs regenerating */ ++ writer_locker = g_rw_lock_writer_locker_new (&priv->silo_lock); ++ g_clear_object (&priv->silo); ++ ++ /* verbose profiling */ ++ if (g_getenv ("GS_XMLB_VERBOSE") != NULL) { ++ xb_builder_set_profile_flags (builder, ++ XB_SILO_PROFILE_FLAG_XPATH | ++ XB_SILO_PROFILE_FLAG_DEBUG); ++ } ++ ++ /* add metadata */ ++ info = xb_builder_node_insert (NULL, "info", NULL); ++ xb_builder_node_insert_text (info, "scope", "user", NULL); ++ xb_builder_source_set_info (source, info); ++ ++ /* add support for JSON files */ ++ fn = gs_utils_get_cache_filename ("shell-extensions", ++ "gnome.json", ++ GS_UTILS_CACHE_FLAG_WRITEABLE, ++ error); ++ if (fn == NULL) ++ return FALSE; ++ xb_builder_source_add_adapter (source, "application/json", ++ gs_plugin_appstream_load_json_cb, ++ plugin, NULL); ++ file = g_file_new_for_path (fn); ++ if (!xb_builder_source_load_file (source, file, ++ XB_BUILDER_SOURCE_FLAG_WATCH_FILE, ++ cancellable, ++ error)) { ++ return FALSE; ++ } ++ xb_builder_import_source (builder, source); ++ ++ /* create binary cache */ ++ blobfn = gs_utils_get_cache_filename ("shell-extensions", ++ "extensions-web.xmlb", ++ GS_UTILS_CACHE_FLAG_WRITEABLE, ++ error); ++ if (blobfn == NULL) ++ return FALSE; ++ blobfile = g_file_new_for_path (blobfn); ++ g_debug ("ensuring %s", blobfn); ++ priv->silo = xb_builder_ensure (builder, blobfile, ++ XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID, ++ NULL, &error_local); ++ if (priv->silo == NULL) { ++ g_set_error (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_FAILED, ++ "failed to compile %s", ++ error_local->message); ++ return FALSE; ++ } ++ ++ /* success */ ++ return TRUE; ++} ++ ++static void ++_claim_components (GsPlugin *plugin, GsAppList *list) ++{ ++ for (guint i = 0; i < gs_app_list_length (list); i++) { ++ GsApp *app = gs_app_list_index (list, i); ++ gs_app_set_kind (app, AS_APP_KIND_SHELL_EXTENSION); ++ gs_app_set_origin_hostname (app, SHELL_EXTENSIONS_API_URI); ++ gs_app_set_origin (app, _("GNOME")); ++ gs_app_set_management_plugin (app, gs_plugin_get_name (plugin)); ++ gs_app_set_summary (app, GS_APP_QUALITY_LOWEST, ++ /* TRANSLATORS: the one-line summary */ ++ _("GNOME Shell Extension")); ++ } ++} ++ ++gboolean ++gs_plugin_add_search (GsPlugin *plugin, gchar **values, GsAppList *list, ++ GCancellable *cancellable, GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autoptr(GRWLockReaderLocker) locker = NULL; ++ g_autoptr(GsAppList) list_tmp = gs_app_list_new (); ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ if (!_check_silo (plugin, cancellable, error)) ++ return FALSE; ++ locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ if (!gs_appstream_search (plugin, priv->silo, (const gchar * const *) values, list_tmp, ++ cancellable, error)) ++ return FALSE; ++ _claim_components (plugin, list_tmp); ++ gs_app_list_add_list (list, list_tmp); ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_add_category_apps (GsPlugin *plugin, GsCategory *category, GsAppList *list, ++ GCancellable *cancellable, GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autoptr(GRWLockReaderLocker) locker = NULL; ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ if (!_check_silo (plugin, cancellable, error)) ++ return FALSE; ++ locker = g_rw_lock_reader_locker_new (&priv->silo_lock); ++ return gs_appstream_add_category_apps (plugin, priv->silo, category, ++ list, cancellable, error); ++} ++ ++gboolean ++gs_plugin_refresh (GsPlugin *plugin, ++ guint cache_age, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ if (!gs_plugin_shell_extensions_refresh (plugin, ++ cache_age, ++ cancellable, ++ error)) ++ return FALSE; ++ return _check_silo (plugin, cancellable, error); ++} ++ ++gboolean ++gs_plugin_app_remove (GsPlugin *plugin, ++ GsApp *app, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ const gchar *uuid; ++ gboolean ret; ++ g_autoptr(GVariant) retval = NULL; ++ ++ /* only process this app if was created by this plugin */ ++ if (g_strcmp0 (gs_app_get_management_plugin (app), ++ gs_plugin_get_name (plugin)) != 0) ++ return TRUE; ++ ++ /* disable repository */ ++ if (gs_app_get_kind (app) == AS_APP_KIND_SOURCE) { ++ gs_app_set_state (app, AS_APP_STATE_REMOVING); ++ g_settings_set_boolean (priv->settings, "enable-shell-extensions-repo", FALSE); ++ /* remove appstream data */ ++ ret = gs_plugin_shell_extensions_refresh (plugin, ++ G_MAXUINT, ++ cancellable, ++ error); ++ gs_app_set_state (app, AS_APP_STATE_AVAILABLE); ++ return ret; ++ } ++ ++ /* remove */ ++ gs_app_set_state (app, AS_APP_STATE_REMOVING); ++ uuid = gs_app_get_metadata_item (app, "shell-extensions::uuid"); ++ retval = g_dbus_proxy_call_sync (priv->proxy, ++ "UninstallExtension", ++ g_variant_new ("(s)", uuid), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ error); ++ if (retval == NULL) { ++ gs_utils_error_convert_gio (error); ++ gs_app_set_state_recover (app); ++ return FALSE; ++ } ++ ++ /* not sure why this would fail -- perhaps installed in /usr? */ ++ g_variant_get (retval, "(b)", &ret); ++ if (!ret) { ++ gs_app_set_state_recover (app); ++ g_set_error (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_NOT_SUPPORTED, ++ "failed to uninstall %s", ++ gs_app_get_id (app)); ++ return FALSE; ++ } ++ ++ /* state is not known: we don't know if we can re-install this app */ ++ gs_app_set_state (app, AS_APP_STATE_UNKNOWN); ++ ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_app_install (GsPlugin *plugin, ++ GsApp *app, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ const gchar *uuid; ++ const gchar *retstr; ++ g_autoptr(GVariant) retval = NULL; ++ ++ /* only process this app if was created by this plugin */ ++ if (g_strcmp0 (gs_app_get_management_plugin (app), ++ gs_plugin_get_name (plugin)) != 0) ++ return TRUE; ++ ++ /* enable repository */ ++ if (gs_app_get_kind (app) == AS_APP_KIND_SOURCE) { ++ gboolean ret; ++ ++ gs_app_set_state (app, AS_APP_STATE_INSTALLING); ++ g_settings_set_boolean (priv->settings, "enable-shell-extensions-repo", TRUE); ++ /* refresh metadata */ ++ ret = gs_plugin_shell_extensions_refresh (plugin, ++ G_MAXUINT, ++ cancellable, ++ error); ++ gs_app_set_state (app, AS_APP_STATE_INSTALLED); ++ return ret; ++ } ++ ++ /* install */ ++ uuid = gs_app_get_metadata_item (app, "shell-extensions::uuid"); ++ gs_app_set_state (app, AS_APP_STATE_INSTALLING); ++ retval = g_dbus_proxy_call_sync (priv->proxy, ++ "InstallRemoteExtension", ++ g_variant_new ("(s)", uuid), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ error); ++ if (retval == NULL) { ++ gs_app_set_state_recover (app); ++ return FALSE; ++ } ++ g_variant_get (retval, "(&s)", &retstr); ++ ++ /* user declined download */ ++ if (g_strcmp0 (retstr, "cancelled") == 0) { ++ gs_app_set_state_recover (app); ++ g_set_error (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_CANCELLED, ++ "extension %s download was cancelled", ++ gs_app_get_id (app)); ++ return FALSE; ++ } ++ g_debug ("shell returned: %s", retstr); ++ ++ /* state is known */ ++ gs_app_set_state (app, AS_APP_STATE_INSTALLED); ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_launch (GsPlugin *plugin, ++ GsApp *app, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ g_autofree gchar *uuid = NULL; ++ g_autoptr(GVariant) retval = NULL; ++ ++ /* launch both PackageKit-installed and user-installed */ ++ if (gs_app_get_kind (app) != AS_APP_KIND_SHELL_EXTENSION) ++ return TRUE; ++ ++ uuid = g_strdup (gs_app_get_metadata_item (app, "shell-extensions::uuid")); ++ if (uuid == NULL) { ++ const gchar *suffix = ".shell-extension"; ++ const gchar *id = gs_app_get_id (app); ++ /* PackageKit-installed extension ID generated by appstream-builder */ ++ if (g_str_has_suffix (id, suffix)) ++ uuid = g_strndup (id, strlen (id) - strlen (suffix)); ++ } ++ if (uuid == NULL) { ++ g_set_error (error, ++ GS_PLUGIN_ERROR, ++ GS_PLUGIN_ERROR_FAILED, ++ "no uuid set for %s", ++ gs_app_get_id (app)); ++ return FALSE; ++ } ++ /* launch */ ++ retval = g_dbus_proxy_call_sync (priv->proxy, ++ "LaunchExtensionPrefs", ++ g_variant_new ("(s)", uuid), ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ cancellable, ++ error); ++ if (retval == NULL) { ++ gs_utils_error_convert_gio (error); ++ return FALSE; ++ } ++ return TRUE; ++} ++ ++gboolean ++gs_plugin_add_categories (GsPlugin *plugin, ++ GPtrArray *list, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ GsPluginData *priv = gs_plugin_get_data (plugin); ++ ++ /* repo not enabled */ ++ if (!g_settings_get_boolean (priv->settings, "enable-shell-extensions-repo")) ++ return TRUE; ++ ++ /* just ensure there is any data, no matter how old */ ++ return gs_plugin_shell_extensions_refresh (plugin, ++ G_MAXUINT, ++ cancellable, ++ error); ++} +diff --git a/plugins/shell-extensions/gs-self-test.c b/plugins/shell-extensions/gs-self-test.c +new file mode 100644 +index 00000000..f96ee60d +--- /dev/null ++++ b/plugins/shell-extensions/gs-self-test.c +@@ -0,0 +1,156 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- ++ * ++ * Copyright (C) 2017 Richard Hughes ++ * ++ * SPDX-License-Identifier: GPL-2.0+ ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++ ++#include "gnome-software-private.h" ++ ++#include "gs-test.h" ++ ++static void ++gs_plugins_shell_extensions_installed_func (GsPluginLoader *plugin_loader) ++{ ++ GsApp *app; ++ g_autoptr(GError) error = NULL; ++ g_autoptr(GsAppList) list = NULL; ++ g_autoptr(GsPluginJob) plugin_job = NULL; ++ ++ /* no shell-extensions, abort */ ++ if (!gs_plugin_loader_get_enabled (plugin_loader, "shell-extensions")) { ++ g_test_skip ("not enabled"); ++ return; ++ } ++ ++ /* get installed packages */ ++ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_GET_INSTALLED, ++ "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_CATEGORIES, ++ NULL); ++ list = gs_plugin_loader_job_process (plugin_loader, plugin_job, NULL, &error); ++ gs_test_flush_main_context (); ++ g_assert_no_error (error); ++ g_assert (list != NULL); ++ ++ /* no shell-extensions installed, abort */ ++ if (gs_app_list_length (list) < 1) { ++ g_test_skip ("no shell extensions installed"); ++ return; ++ } ++ ++ /* test properties */ ++ app = gs_app_list_lookup (list, "*/*/*/*/background-logo_fedorahosted.org/*"); ++ if (app == NULL) { ++ g_test_skip ("not found"); ++ return; ++ } ++ ++ g_assert_cmpint (gs_app_get_state (app), ==, AS_APP_STATE_INSTALLED); ++ g_assert_cmpint (gs_app_get_scope (app), ==, AS_APP_SCOPE_USER); ++ g_assert_cmpstr (gs_app_get_name (app), ==, "Background Logo"); ++ g_assert_cmpstr (gs_app_get_summary (app), ==, "GNOME Shell Extension"); ++ g_assert_cmpstr (gs_app_get_description (app), ==, ++ "Overlay a tasteful logo on the background to " ++ "enhance the user experience"); ++ g_assert_cmpstr (gs_app_get_license (app), ==, "GPL-2.0+"); ++ g_assert_cmpstr (gs_app_get_management_plugin (app), ==, "shell-extensions"); ++ g_assert (gs_app_has_category (app, "Addon")); ++ g_assert (gs_app_has_category (app, "ShellExtension")); ++ g_assert_cmpstr (gs_app_get_metadata_item (app, "shell-extensions::has-prefs"), ==, ""); ++ g_assert_cmpstr (gs_app_get_metadata_item (app, "shell-extensions::uuid"), ==, ++ "background-logo@fedorahosted.org"); ++} ++ ++static void ++gs_plugins_shell_extensions_remote_func (GsPluginLoader *plugin_loader) ++{ ++ const gchar *cachedir = "/var/tmp/gs-self-test"; ++ gboolean ret; ++ g_autofree gchar *fn = NULL; ++ g_autoptr(GError) error = NULL; ++ g_autoptr(GFile) file = NULL; ++ g_autoptr(GPtrArray) components = NULL; ++ g_autoptr(GsPluginJob) plugin_job = NULL; ++ g_autoptr(XbSilo) silo = NULL; ++ ++ /* no shell-extensions, abort */ ++ if (!gs_plugin_loader_get_enabled (plugin_loader, "shell-extensions")) { ++ g_test_skip ("not enabled"); ++ return; ++ } ++ ++ /* ensure files are removed */ ++ g_setenv ("GS_SELF_TEST_CACHEDIR", cachedir, TRUE); ++ gs_utils_rmtree (cachedir, NULL); ++ ++ /* refresh the metadata */ ++ plugin_job = gs_plugin_job_newv (GS_PLUGIN_ACTION_REFRESH, ++ "age", (guint64) G_MAXUINT, ++ NULL); ++ ret = gs_plugin_loader_job_action (plugin_loader, plugin_job, NULL, &error); ++ g_assert_no_error (error); ++ g_assert (ret); ++ ++ /* ensure file was populated */ ++ silo = xb_silo_new (); ++ fn = gs_utils_get_cache_filename ("shell-extensions", ++ "extensions-web.xmlb", ++ GS_UTILS_CACHE_FLAG_WRITEABLE, ++ &error); ++ g_assert_no_error (error); ++ g_assert_nonnull (fn); ++ file = g_file_new_for_path (fn); ++ ret = xb_silo_load_from_file (silo, file, ++ XB_SILO_LOAD_FLAG_NONE, ++ NULL, &error); ++ g_assert_no_error (error); ++ g_assert (ret); ++ components = xb_silo_query (silo, "components/component", 0, &error); ++ g_assert_no_error (error); ++ g_assert_nonnull (components); ++ g_assert_cmpint (components->len, >, 20); ++} ++ ++int ++main (int argc, char **argv) ++{ ++ gboolean ret; ++ g_autoptr(GError) error = NULL; ++ g_autoptr(GsPluginLoader) plugin_loader = NULL; ++ const gchar *whitelist[] = { ++ "shell-extensions", ++ NULL ++ }; ++ ++ g_test_init (&argc, &argv, NULL); ++ g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); ++ ++ /* only critical and error are fatal */ ++ g_log_set_fatal_mask (NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); ++ ++ /* we can only load this once per process */ ++ plugin_loader = gs_plugin_loader_new (); ++ gs_plugin_loader_add_location (plugin_loader, LOCALPLUGINDIR); ++ ret = gs_plugin_loader_setup (plugin_loader, ++ (gchar**) whitelist, ++ NULL, ++ NULL, ++ &error); ++ g_assert_no_error (error); ++ g_assert (ret); ++ ++ /* plugin tests go here */ ++ g_test_add_data_func ("/gnome-software/plugins/shell-extensions/installed", ++ plugin_loader, ++ (GTestDataFunc) gs_plugins_shell_extensions_installed_func); ++ g_test_add_data_func ("/gnome-software/plugins/shell-extensions/remote", ++ plugin_loader, ++ (GTestDataFunc) gs_plugins_shell_extensions_remote_func); ++ ++ return g_test_run (); ++} +diff --git a/plugins/shell-extensions/meson.build b/plugins/shell-extensions/meson.build +new file mode 100644 +index 00000000..3a008f7b +--- /dev/null ++++ b/plugins/shell-extensions/meson.build +@@ -0,0 +1,47 @@ ++cargs = ['-DG_LOG_DOMAIN="GsPluginShellExtensions"'] ++cargs += ['-DLOCALPLUGINDIR="' + meson.current_build_dir() + '"'] ++ ++shared_module( ++ 'gs_plugin_shell-extensions', ++ sources : [ ++ 'gs-appstream.c', ++ 'gs-plugin-shell-extensions.c' ++ ], ++ include_directories : [ ++ include_directories('../..'), ++ include_directories('../../lib'), ++ ], ++ install : true, ++ install_dir: plugin_dir, ++ c_args : cargs, ++ dependencies : [ ++ plugin_libs, ++ libxmlb, ++ ], ++ link_with : [ ++ libgnomesoftware ++ ] ++) ++ ++if get_option('tests') ++ e = executable( ++ 'gs-self-test-shell-extensions', ++ compiled_schemas, ++ sources : [ ++ 'gs-self-test.c' ++ ], ++ include_directories : [ ++ include_directories('../..'), ++ include_directories('../../lib'), ++ ], ++ dependencies : [ ++ plugin_libs, ++ libxmlb, ++ ], ++ link_with : [ ++ libgnomesoftware ++ ], ++ c_args : cargs, ++ ) ++ test('gs-self-test-shell-extensions', e, suite: ['plugins', 'shell-extensions'], env: test_env) ++endif +diff --git a/src/gs-category-page.c b/src/gs-category-page.c +index 10467436..a786d48d 100644 +--- a/src/gs-category-page.c ++++ b/src/gs-category-page.c +@@ -38,6 +38,8 @@ struct _GsCategoryPage + guint sort_name_handler_id; + SubcategorySortType sort_type; + ++ GtkWidget *infobar_category_shell_extensions; ++ GtkWidget *button_category_shell_extensions; + GtkWidget *category_detail_box; + GtkWidget *scrolledwindow_category; + GtkWidget *subcats_filter_label; +@@ -321,6 +323,12 @@ gs_category_page_reload (GsPage *page) + gtk_widget_set_visible (self->subcats_sort_button, TRUE); + } + ++ /* show the shell extensions header */ ++ if (g_strcmp0 (gs_category_get_id (self->subcategory), "shell-extensions") == 0) ++ gtk_widget_set_visible (self->infobar_category_shell_extensions, TRUE); ++ else ++ gtk_widget_set_visible (self->infobar_category_shell_extensions, FALSE); ++ + if (self->sort_rating_handler_id > 0) { + g_signal_handler_disconnect (self->sort_rating_button, + self->sort_rating_handler_id); +@@ -524,6 +532,18 @@ gs_category_page_dispose (GObject *object) + G_OBJECT_CLASS (gs_category_page_parent_class)->dispose (object); + } + ++static void ++button_shell_extensions_cb (GtkButton *button, GsCategoryPage *self) ++{ ++ gboolean ret; ++ g_autoptr(GError) error = NULL; ++ const gchar *argv[] = { "gnome-shell-extension-prefs", NULL }; ++ ret = g_spawn_async (NULL, (gchar **) argv, NULL, G_SPAWN_SEARCH_PATH, ++ NULL, NULL, NULL, &error); ++ if (!ret) ++ g_warning ("failed to exec %s: %s", argv[0], error->message); ++} ++ + static gboolean + gs_category_page_setup (GsPage *page, + GsShell *shell, +@@ -545,6 +565,9 @@ gs_category_page_setup (GsPage *page, + + adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (self->scrolledwindow_category)); + gtk_container_set_focus_vadjustment (GTK_CONTAINER (self->category_detail_box), adj); ++ ++ g_signal_connect (self->button_category_shell_extensions, "clicked", ++ G_CALLBACK (button_shell_extensions_cb), self); + return TRUE; + } + +@@ -563,6 +586,8 @@ gs_category_page_class_init (GsCategoryPageClass *klass) + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Software/gs-category-page.ui"); + + gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, category_detail_box); ++ gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, infobar_category_shell_extensions); ++ gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, button_category_shell_extensions); + gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, scrolledwindow_category); + gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, subcats_filter_label); + gtk_widget_class_bind_template_child (widget_class, GsCategoryPage, subcats_filter_button_label); +diff --git a/src/gs-category-page.ui b/src/gs-category-page.ui +index cbb82840..2a3c789f 100644 +--- a/src/gs-category-page.ui ++++ b/src/gs-category-page.ui +@@ -97,6 +97,51 @@ + 24 + + ++ ++ ++ False ++ True ++ True ++ other ++ 24 ++ 24 ++ 24 ++ ++ ++ ++ 6 ++ end ++ ++ ++ Extension Settings ++ True ++ True ++ True ++ True ++ ++ ++ ++ ++ ++ ++ start ++ True ++ 16 ++ ++ ++ True ++ True ++ Extensions are used at your own risk. If you have any system problems, it is recommended to disable them. ++ True ++ ++ ++ ++ ++ ++ ++ + + + True +diff --git a/src/gs-details-page.c b/src/gs-details-page.c +index 8eb07bd3..37a717bf 100644 +--- a/src/gs-details-page.c ++++ b/src/gs-details-page.c +@@ -923,9 +923,17 @@ gs_details_page_refresh_buttons (GsDetailsPage *self) + break; + } + +- gtk_button_set_label (GTK_BUTTON (self->button_details_launch), +- /* TRANSLATORS: A label for a button to execute the selected application. */ +- _("_Launch")); ++ if (gs_app_get_kind (self->app) == AS_APP_KIND_SHELL_EXTENSION) { ++ gtk_button_set_label (GTK_BUTTON (self->button_details_launch), ++ /* TRANSLATORS: A label for a button to show the settings for ++ the selected shell extension. */ ++ _("Extension Settings")); ++ } else { ++ gtk_button_set_label (GTK_BUTTON (self->button_details_launch), ++ /* TRANSLATORS: A label for a button to execute the selected ++ application. */ ++ _("_Launch")); ++ } + + /* don't show the launch and shortcut buttons if the app doesn't have a desktop ID */ + if (gs_app_get_id (self->app) == NULL) { +diff --git a/src/gs-repo-row.c b/src/gs-repo-row.c +index 35b35045..7ce7e618 100644 +--- a/src/gs-repo-row.c ++++ b/src/gs-repo-row.c +@@ -68,7 +68,8 @@ repo_supports_removal (GsApp *repo) + /* can't remove a repo, only enable/disable existing ones */ + if (g_strcmp0 (management_plugin, "fwupd") == 0 || + g_strcmp0 (management_plugin, "packagekit") == 0 || +- g_strcmp0 (management_plugin, "rpm-ostree") == 0) ++ g_strcmp0 (management_plugin, "rpm-ostree") == 0 || ++ g_strcmp0 (management_plugin, "shell-extensions") == 0) + return FALSE; + + return TRUE; +diff --git a/src/gs-repos-dialog.c b/src/gs-repos-dialog.c +index 93830308..7c11dc78 100644 +--- a/src/gs-repos-dialog.c ++++ b/src/gs-repos-dialog.c +@@ -136,7 +136,8 @@ repo_supports_removal (GsApp *repo) + /* can't remove a repo, only enable/disable existing ones */ + if (g_strcmp0 (management_plugin, "fwupd") == 0 || + g_strcmp0 (management_plugin, "packagekit") == 0 || +- g_strcmp0 (management_plugin, "rpm-ostree") == 0) ++ g_strcmp0 (management_plugin, "rpm-ostree") == 0 || ++ g_strcmp0 (management_plugin, "shell-extensions") == 0) + return FALSE; + + return TRUE; +diff --git a/src/gs-summary-tile.c b/src/gs-summary-tile.c +index 446200de..90e810c9 100644 +--- a/src/gs-summary-tile.c ++++ b/src/gs-summary-tile.c +@@ -41,7 +41,7 @@ gs_summary_tile_refresh (GsAppTile *self) + const GdkPixbuf *pixbuf; + gboolean installed; + g_autofree gchar *name = NULL; +- const gchar *summary; ++ g_autofree gchar *summary = NULL; + const gchar *css; + + if (app == NULL) +@@ -53,7 +53,18 @@ gs_summary_tile_refresh (GsAppTile *self) + /* set name */ + gtk_label_set_label (GTK_LABEL (tile->name), gs_app_get_name (app)); + +- summary = gs_app_get_summary (app); ++ /* some kinds have boring summaries */ ++ switch (gs_app_get_kind (app)) { ++ case AS_APP_KIND_SHELL_EXTENSION: ++ summary = g_strdup (gs_app_get_description (app)); ++ if (summary != NULL) ++ g_strdelimit (summary, "\n\t", ' '); ++ break; ++ default: ++ summary = g_strdup (gs_app_get_summary (app)); ++ break; ++ } ++ + gtk_label_set_label (GTK_LABEL (tile->summary), summary); + gtk_widget_set_visible (tile->summary, summary && summary[0]); + +-- +2.18.2 + + +From 420396f45380ce4da2a609f139e5f7f47a6d5d5d Mon Sep 17 00:00:00 2001 +From: Kalev Lember +Date: Wed, 3 Jun 2020 15:50:44 +0200 +Subject: [PATCH 2/2] Revert "Update POTFILES.in" + +This reverts commit 9afc91a6de17117a32e4d36210230ba218e8ea7d. +--- + po/POTFILES.in | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/po/POTFILES.in b/po/POTFILES.in +index a44a6ad3..c38c56e2 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -88,5 +88,6 @@ plugins/fwupd/gs-plugin-fwupd.c + plugins/fwupd/org.gnome.Software.Plugin.Fwupd.metainfo.xml.in + plugins/odrs/gs-plugin-odrs.c + plugins/odrs/org.gnome.Software.Plugin.Odrs.metainfo.xml.in ++plugins/shell-extensions/gs-plugin-shell-extensions.c + plugins/snap/gs-plugin-snap.c + plugins/snap/org.gnome.Software.Plugin.Snap.metainfo.xml.in +-- +2.18.2 + diff --git a/SPECS/gnome-software.spec b/SPECS/gnome-software.spec index f67ed02..f465426 100644 --- a/SPECS/gnome-software.spec +++ b/SPECS/gnome-software.spec @@ -8,26 +8,28 @@ %global gnome_desktop_version 3.18.0 %global fwupd_version 1.0.7 %global flatpak_version 0.9.4 +%global libxmlb_version 0.1.7 %global fwupd_arches aarch64 ppc64le s390x x86_64 Name: gnome-software -Version: 3.30.6 +Version: 3.36.1 Release: 3%{?dist} Summary: A software center for GNOME License: GPLv2+ URL: https://wiki.gnome.org/Apps/Software -Source0: https://download.gnome.org/sources/gnome-software/3.30/%{name}-%{version}.tar.xz -# Lower appstream-glib version check as we have the required new API backported to -# libappstream-glib-0.7.14-3.fc29. -Patch0: 0001-Lower-as_utils_vercmp_full-version-check-for-Fedora.patch -Patch1: 0001-Lower-AsStore-new-API-version-checks-for-Fedora.patch - -# Backported from upstream -Patch2: 0001-details-page-Hide-addons-that-are-not-available-in-r.patch -Patch3: 0001-epiphany-Don-t-adopt-apps-that-have-package-name-set.patch -Patch4: 0002-packagekit-Don-t-skip-over-web-apps.patch +Source0: https://download.gnome.org/sources/gnome-software/3.36/%{name}-%{version}.tar.xz + +# Add support for basic auth and webflow auth in flatpak plugin +# https://gitlab.gnome.org/GNOME/gnome-software/-/merge_requests/467 +Patch0: 0001-Add-basic-auth-support-to-flatpak-plugin.patch +Patch1: 0002-Add-webflow-auth-support-to-flatpak-plugin.patch +# Add back shell extensions support as we don't have the extensions app in RHEL 8.3 +# https://bugzilla.redhat.com/show_bug.cgi?id=1839774 +Patch2: add-back-shell-extensions-support.patch +# Fix hardcoded desktop and appdata names to match what's in RHEL 8.3 +Patch3: 0001-Fix-hardcoded-desktop-and-appdata-names-to-match-wha.patch BuildRequires: gcc BuildRequires: gettext @@ -39,33 +41,29 @@ BuildRequires: fwupd-devel >= %{fwupd_version} %endif BuildRequires: glib2-devel >= %{glib2_version} BuildRequires: gnome-desktop3-devel +BuildRequires: gnome-online-accounts-devel BuildRequires: gsettings-desktop-schemas-devel >= %{gsettings_desktop_schemas_version} BuildRequires: gspell-devel BuildRequires: gtk3-devel >= %{gtk3_version} BuildRequires: gtk-doc BuildRequires: json-glib-devel >= %{json_glib_version} BuildRequires: libappstream-glib-devel >= %{appstream_glib_version} +BuildRequires: libdnf-devel BuildRequires: libsoup-devel +BuildRequires: libxmlb-devel >= %{libxmlb_version} BuildRequires: meson BuildRequires: PackageKit-glib-devel >= %{packagekit_version} BuildRequires: polkit-devel -BuildRequires: libsecret-devel BuildRequires: flatpak-devel >= %{flatpak_version} BuildRequires: ostree-devel +BuildRequires: rpm-devel BuildRequires: rpm-ostree-devel BuildRequires: libgudev1-devel %ifarch %{valgrind_arches} BuildRequires: valgrind-devel %endif -BuildRequires: liboauth-devel -%if 0%{?fedora} -BuildRequires: snapd-glib-devel >= 1.42 -%endif Requires: appstream-data -%if 0%{?fedora} -Requires: epiphany-runtime -%endif Requires: flatpak%{?_isa} >= %{flatpak_version} Requires: flatpak-libs%{?_isa} >= %{flatpak_version} %ifarch %{fwupd_arches} @@ -84,9 +82,12 @@ Requires: libappstream-glib%{?_isa} >= %{appstream_glib_version} Requires: librsvg2%{?_isa} Requires: libsoup%{?_isa} >= %{libsoup_version} Requires: PackageKit%{?_isa} >= %{packagekit_version} +Requires: libxmlb%{?_isa} >= %{libxmlb_version} + +Obsoletes: gnome-software-editor < 3.35.1 # this is not a library version -%define gs_plugin_version 12 +%define gs_plugin_version 13 %description gnome-software is an application that makes it easy to add, remove @@ -100,34 +101,12 @@ Requires: %{name}%{?_isa} = %{version}-%{release} These development files are for building gnome-software plugins outside the source tree. Most users do not need this subpackage installed. -%package editor -Summary: Banner designer for GNOME Software -Requires: %{name}%{?_isa} = %{version}-%{release} - -%description editor -Editor for designing banners for GNOME Software. - -%if 0%{?fedora} -%package snap -Summary: Support for Ubuntu Snap packages -Requires: %{name}%{?_isa} = %{version}-%{release} -Requires: snapd-login-service -Supplements: (gnome-software%{?_isa} and snapd%{?_isa}) - -%description snap -Adds support for Snap packages from the Snap store. -%endif - %prep %autosetup -p1 %build %meson \ -%if 0%{?fedora} - -Dsnap=true \ -%else -Dsnap=false \ -%endif %ifnarch %{valgrind_arches} -Dvalgrind=false \ %endif @@ -137,6 +116,7 @@ Adds support for Snap packages from the Snap store. -Dgudev=true \ -Dpackagekit=true \ -Dexternal_appstream=false \ + -Dmalcontent=false \ -Drpm_ostree=true \ -Dtests=false \ -Dubuntuone=false \ @@ -146,6 +126,9 @@ Adds support for Snap packages from the Snap store. %install %meson_install +# remove unneeded dpkg plugin +rm %{buildroot}%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_dpkg.so + # make the software center load faster desktop-file-edit %{buildroot}%{_datadir}/applications/org.gnome.Software.desktop \ --set-key=X-AppInstall-Package --set-value=%{name} @@ -170,24 +153,23 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %dir %{_datadir}/gnome-software %{_datadir}/gnome-software/*.png %{_mandir}/man1/gnome-software.1.gz -%{_datadir}/icons/hicolor/*/apps/* +%{_datadir}/icons/hicolor/*/apps/org.gnome.Software.svg +%{_datadir}/icons/hicolor/symbolic/apps/org.gnome.Software-symbolic.svg +%{_datadir}/icons/hicolor/scalable/status/software-installed-symbolic.svg %{_datadir}/gnome-software/featured-*.svg %{_datadir}/gnome-software/featured-*.jpg %{_datadir}/metainfo/org.gnome.Software.appdata.xml -%{_datadir}/metainfo/org.gnome.Software.Plugin.Epiphany.metainfo.xml %{_datadir}/metainfo/org.gnome.Software.Plugin.Flatpak.metainfo.xml %ifarch %{fwupd_arches} %{_datadir}/metainfo/org.gnome.Software.Plugin.Fwupd.metainfo.xml %endif %{_datadir}/metainfo/org.gnome.Software.Plugin.Odrs.metainfo.xml -%{_datadir}/metainfo/org.gnome.Software.Plugin.Steam.metainfo.xml %dir %{_libdir}/gs-plugins-%{gs_plugin_version} %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_appstream.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_desktop-categories.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_desktop-menu-path.so -%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_dpkg.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_dummy.so -%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_epiphany.so +%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_fedora-langpacks.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_fedora-pkgdb-collections.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_flatpak.so %ifarch %{fwupd_arches} @@ -195,7 +177,6 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %endif %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_generic-updates.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_hardcoded-blacklist.so -%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_hardcoded-featured.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_hardcoded-popular.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_icons.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_key-colors-metadata.so @@ -219,7 +200,6 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_rewrite-resource.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_rpm-ostree.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_shell-extensions.so -%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_steam.so %{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_systemd-updates.so %{_sysconfdir}/xdg/autostart/gnome-software-service.desktop %{_datadir}/app-info/xmls/org.gnome.Software.Featured.xml @@ -231,24 +211,27 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %{_libexecdir}/gnome-software-cmd %{_libexecdir}/gnome-software-restarter -%if 0%{?fedora} -%files snap -%{_libdir}/gs-plugins-%{gs_plugin_version}/libgs_plugin_snap.so -%{_datadir}/metainfo/org.gnome.Software.Plugin.Snap.metainfo.xml -%endif - %files devel %{_libdir}/pkgconfig/gnome-software.pc %dir %{_includedir}/gnome-software %{_includedir}/gnome-software/*.h %{_datadir}/gtk-doc/html/gnome-software -%files editor -%{_bindir}/gnome-software-editor -%{_datadir}/applications/org.gnome.Software.Editor.desktop -%{_mandir}/man1/gnome-software-editor.1* - %changelog +* Wed Jun 03 2020 Kalev Lember - 3.36.1-3 +- Upload correct 3.36.1 tarball +- Fix hardcoded desktop and appdata names to match what's in RHEL 8.3 +- Add back shell extensions support +- Resolves: #1839774 + +* Tue Jun 02 2020 Kalev Lember - 3.36.1-2 +- Add support for basic auth and webflow auth in flatpak plugin +- Resolves: #1815502 + +* Fri May 22 2020 Richard Hughes - 3.36.1-1 +- Update to 3.36.1 +- Resolves: #1797932 + * Wed Jan 29 2020 Kalev Lember - 3.30.6-3 - Fix issues with installing Cockpit - Resolves: #1759913