[gnome-software/wip/rancell/ubuntu-3-20-5: 8/38] Backport URL handling improvements from master
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/rancell/ubuntu-3-20-5: 8/38] Backport URL handling improvements from master
- Date: Sat, 17 Jun 2017 12:31:16 +0000 (UTC)
commit bfd866e279b4fbe6f2b0a4e576637fd1b8c134ae
Author: Robert Ancell <robert ancell canonical com>
Date: Thu Feb 2 10:11:54 2017 +0000
Backport URL handling improvements from master
src/gs-application.c | 48 ++++++------
src/gs-cmd.c | 11 +++
src/gs-plugin-loader-sync.c | 50 ++++++++++++
src/gs-plugin-loader-sync.h | 5 +
src/gs-plugin-loader.c | 152 +++++++++++++++++++++++++++++++++++++
src/gs-plugin-loader.h | 9 ++
src/gs-plugin.h | 10 +++
src/gs-shell-details.c | 18 +++++
src/gs-shell-details.h | 2 +
src/gs-shell.c | 9 ++-
src/gs-utils.c | 61 +++++++++++++++
src/gs-utils.h | 3 +
src/plugins/gs-plugin-appstream.c | 35 +++++++++
13 files changed, 388 insertions(+), 25 deletions(-)
---
diff --git a/src/gs-application.c b/src/gs-application.c
index c10922d..b3bdfa0 100644
--- a/src/gs-application.c
+++ b/src/gs-application.c
@@ -603,6 +603,26 @@ details_activated (GSimpleAction *action,
}
static void
+details_url_activated (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ GsApplication *app = GS_APPLICATION (data);
+ const gchar *url;
+ g_autoptr (GsApp) a = NULL;
+
+ initialize_ui_and_present_window (app);
+
+ g_variant_get (parameter, "(&s)", &url);
+
+ /* this is only used as a wrapper to transport the URL to
+ * the gs_shell_change_mode() function -- not in the GsAppList */
+ a = gs_app_new (NULL);
+ gs_app_set_metadata (a, "GnomeSoftware::from-url", url);
+ gs_shell_show_app (app->shell, a);
+}
+
+static void
filename_activated (GSimpleAction *action,
GVariant *parameter,
gpointer data)
@@ -694,6 +714,8 @@ static GActionEntry actions[] = {
{ "set-mode", set_mode_activated, "s", NULL, NULL },
{ "search", search_activated, "s", NULL, NULL },
{ "details", details_activated, "(ss)", NULL, NULL },
+ { "details-pkg", details_pkg_activated, "s", NULL, NULL },
+ { "details-url", details_url_activated, "(s)", NULL, NULL },
{ "filename", filename_activated, "(s)", NULL, NULL },
{ "launch", launch_activated, "s", NULL, NULL },
{ "show-offline-update-error", show_offline_updates_error, NULL, NULL, NULL },
@@ -826,29 +848,9 @@ gs_application_open (GApplication *application,
for (i = 0; i < n_files; i++) {
g_autofree gchar *str = g_file_get_uri (files[i]);
- g_autoptr(SoupURI) uri = NULL;
-
- uri = soup_uri_new (str);
- if (!SOUP_URI_IS_VALID (uri))
- continue;
-
- if (g_strcmp0 (soup_uri_get_scheme (uri), "appstream") == 0) {
- const gchar *host = soup_uri_get_host (uri);
- const gchar *path = soup_uri_get_path (uri);
-
- /* appstream://foo -> scheme: appstream, host: foo, path: / */
- /* appstream:foo -> scheme: appstream, host: (empty string), path: /foo */
- if (host != NULL && (strlen (host) > 0))
- path = host;
-
- /* trim any leading slashes */
- while (*path == '/')
- path++;
-
- g_action_group_activate_action (G_ACTION_GROUP (app),
- "details",
- g_variant_new ("(ss)", path, ""));
- }
+ g_action_group_activate_action (G_ACTION_GROUP (app),
+ "details-url",
+ g_variant_new ("(s)", str));
}
}
diff --git a/src/gs-cmd.c b/src/gs-cmd.c
index acfd509..c1d9a0a 100644
--- a/src/gs-cmd.c
+++ b/src/gs-cmd.c
@@ -339,6 +339,17 @@ main (int argc, char **argv)
} else {
gs_plugin_add_app (&list, app);
}
+ } else if (argc == 3 && g_strcmp0 (argv[1], "url-to-app") == 0) {
+ app = gs_plugin_loader_url_to_app (plugin_loader,
+ argv[2],
+ refine_flags,
+ NULL,
+ &error);
+ if (app == NULL) {
+ ret = FALSE;
+ } else {
+ gs_plugin_add_app (&list, app);
+ }
} else if (argc == 2 && g_strcmp0 (argv[1], "updates") == 0) {
for (i = 0; i < repeat; i++) {
if (list != NULL)
diff --git a/src/gs-plugin-loader-sync.c b/src/gs-plugin-loader-sync.c
index ce0b9c2..efba07b 100644
--- a/src/gs-plugin-loader-sync.c
+++ b/src/gs-plugin-loader-sync.c
@@ -663,4 +663,54 @@ gs_plugin_loader_filename_to_app (GsPluginLoader *plugin_loader,
return helper.app;
}
+static void
+gs_plugin_loader_url_to_app_finish_sync (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
+ GsPluginLoaderHelper *helper = (GsPluginLoaderHelper *) user_data;
+ helper->app = gs_plugin_loader_url_to_app_finish (plugin_loader,
+ res,
+ helper->error);
+ g_main_loop_quit (helper->loop);
+}
+
+/**
+ * gs_plugin_loader_url_to_app:
+ **/
+GsApp *
+gs_plugin_loader_url_to_app (GsPluginLoader *plugin_loader,
+ const gchar *url,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GsPluginLoaderHelper helper;
+
+ /* create temp object */
+ helper.app = NULL;
+ helper.context = g_main_context_new ();
+ helper.loop = g_main_loop_new (helper.context, FALSE);
+ helper.error = error;
+
+ g_main_context_push_thread_default (helper.context);
+
+ /* run async method */
+ gs_plugin_loader_url_to_app_async (plugin_loader,
+ url,
+ flags,
+ cancellable,
+ gs_plugin_loader_url_to_app_finish_sync,
+ &helper);
+ g_main_loop_run (helper.loop);
+
+ g_main_context_pop_thread_default (helper.context);
+
+ g_main_loop_unref (helper.loop);
+ g_main_context_unref (helper.context);
+
+ return helper.app;
+}
+
/* vim: set noexpandtab: */
diff --git a/src/gs-plugin-loader-sync.h b/src/gs-plugin-loader-sync.h
index b1b88ec..158c81d 100644
--- a/src/gs-plugin-loader-sync.h
+++ b/src/gs-plugin-loader-sync.h
@@ -91,6 +91,11 @@ GsApp *gs_plugin_loader_filename_to_app (GsPluginLoader
*plugin_loader,
GsPluginRefineFlags flags,
GCancellable *cancellable,
GError **error);
+GsApp *gs_plugin_loader_url_to_app (GsPluginLoader *plugin_loader,
+ const gchar *url,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GError **error);
G_END_DECLS
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index e7467e5..04973b2 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -3960,6 +3960,158 @@ gs_plugin_loader_filename_to_app_finish (GsPluginLoader *plugin_loader,
/******************************************************************************/
/**
+ * gs_plugin_loader_url_to_app_thread_cb:
+ **/
+static void
+gs_plugin_loader_url_to_app_thread_cb (GTask *task,
+ gpointer object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
+ GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+ const gchar *function_name = "gs_plugin_url_to_app";
+ gboolean ret = TRUE;
+ GError *error = NULL;
+ GList *l;
+ GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) task_data;
+ GsPlugin *plugin;
+ GsPluginUrlToAppFunc plugin_func = NULL;
+ guint i;
+
+ /* run each plugin */
+ for (i = 0; i < priv->plugins->len; i++) {
+ g_autoptr(AsProfileTask) ptask = NULL;
+ g_autoptr(GError) error_local = NULL;
+ plugin = g_ptr_array_index (priv->plugins, i);
+ if (!plugin->enabled)
+ continue;
+ ret = g_task_return_error_if_cancelled (task);
+ if (ret)
+ return;
+ ret = g_module_symbol (plugin->module,
+ function_name,
+ (gpointer *) &plugin_func);
+ if (!ret)
+ continue;
+ ptask = as_profile_start (priv->profile,
+ "GsPlugin::%s(%s)",
+ plugin->name,
+ function_name);
+ ret = plugin_func (plugin, &state->list, state->value,
+ cancellable, &error_local);
+ if (!ret) {
+ g_warning ("failed to call %s on %s: %s",
+ function_name, plugin->name,
+ error_local->message);
+ continue;
+ }
+ gs_plugin_status_update (plugin, NULL, GS_PLUGIN_STATUS_FINISHED);
+ }
+
+ /* set the local file on any of the returned results */
+ for (l = state->list; l != NULL; l = l->next) {
+ GsApp *app = GS_APP (l->data);
+ if (gs_app_get_local_file (app) == NULL)
+ gs_app_set_local_file (app, state->file);
+ }
+
+ /* run refine() on each one */
+ ret = gs_plugin_loader_run_refine (plugin_loader,
+ function_name,
+ &state->list,
+ state->flags,
+ cancellable,
+ &error);
+ if (!ret) {
+ g_task_return_error (task, error);
+ return;
+ }
+
+ /* filter package list */
+ gs_plugin_list_filter_duplicates (&state->list);
+ if (state->list == NULL) {
+ g_task_return_new_error (task,
+ GS_PLUGIN_LOADER_ERROR,
+ GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
+ "no url_to_app results to show");
+ return;
+ }
+
+ /* success */
+ if (g_list_length (state->list) != 1) {
+ g_task_return_new_error (task,
+ GS_PLUGIN_LOADER_ERROR,
+ GS_PLUGIN_LOADER_ERROR_NO_RESULTS,
+ "no application was created for %s",
+ state->value);
+ return;
+ }
+ g_task_return_pointer (task, g_object_ref (state->list->data), (GDestroyNotify) g_object_unref);
+}
+
+/**
+ * gs_plugin_loader_url_to_app_async:
+ *
+ * This method calls all plugins that implement the gs_plugin_add_url_to_app()
+ * function. The plugins can either return #GsApp objects of kind
+ * %AS_APP_KIND_DESKTOP for bonafide applications, or #GsApp's of kind
+ * %AS_APP_KIND_GENERIC for packages that may or may not be applications.
+ *
+ * Once the list of updates is refined, some of the #GsApp's of kind
+ * %AS_APP_KIND_GENERIC will have been promoted to a kind of %AS_APP_KIND_DESKTOP,
+ * or if they are core applications.
+ *
+ * Files that are supported will have the GFile used to create them available
+ * from the gs_app_get_local_file() method.
+ **/
+void
+gs_plugin_loader_url_to_app_async (GsPluginLoader *plugin_loader,
+ const gchar *url,
+ GsPluginRefineFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GsPluginLoaderAsyncState *state;
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader));
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ /* save state */
+ state = g_slice_new0 (GsPluginLoaderAsyncState);
+ state->flags = flags;
+ state->value = g_strdup (url);
+
+ /* run in a thread */
+ task = g_task_new (plugin_loader, cancellable, callback, user_data);
+ g_task_set_task_data (task, state, (GDestroyNotify) gs_plugin_loader_free_async_state);
+ g_task_set_return_on_cancel (task, TRUE);
+ g_task_run_in_thread (task, gs_plugin_loader_url_to_app_thread_cb);
+}
+
+/**
+ * gs_plugin_loader_url_to_app_finish:
+ *
+ * Return value: (element-type GsApp) (transfer full): An application, or %NULL
+ **/
+GsApp *
+gs_plugin_loader_url_to_app_finish (GsPluginLoader *plugin_loader,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader), NULL);
+ g_return_val_if_fail (G_IS_TASK (res), NULL);
+ g_return_val_if_fail (g_task_is_valid (res, plugin_loader), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+/******************************************************************************/
+
+/**
* gs_plugin_loader_offline_update_thread_cb:
**/
static void
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index 5f79433..f62d8a3 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -181,6 +181,15 @@ void gs_plugin_loader_filename_to_app_async (GsPluginLoader
*plugin_loader,
GsApp *gs_plugin_loader_filename_to_app_finish(GsPluginLoader *plugin_loader,
GAsyncResult *res,
GError **error);
+void gs_plugin_loader_url_to_app_async (GsPluginLoader *plugin_loader,
+ const gchar *url,
+ GsPluginRefineFlags refine_flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GsApp *gs_plugin_loader_url_to_app_finish (GsPluginLoader *plugin_loader,
+ GAsyncResult *res,
+ GError **error);
void gs_plugin_loader_offline_update_async (GsPluginLoader *plugin_loader,
GList *apps,
GCancellable *cancellable,
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 17cea10..6b5834f 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -207,6 +207,11 @@ typedef gboolean (*GsPluginFilenameToAppFunc) (GsPlugin *plugin,
const gchar *filename,
GCancellable *cancellable,
GError **error);
+typedef gboolean (*GsPluginUrlToAppFunc) (GsPlugin *plugin,
+ GList **list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error);
typedef gboolean (*GsPluginOfflineUpdateFunc) (GsPlugin *plugin,
GList *apps,
GCancellable *cancellable,
@@ -399,6 +404,11 @@ gboolean gs_plugin_filename_to_app (GsPlugin *plugin,
const gchar *filename,
GCancellable *cancellable,
GError **error);
+gboolean gs_plugin_url_to_app (GsPlugin *plugin,
+ GList **list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error);
gboolean gs_plugin_offline_update (GsPlugin *plugin,
GList *apps,
GCancellable *cancellable,
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index 878ac96..9dbe4ef 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -1320,6 +1320,24 @@ gs_shell_details_set_filename (GsShellDetails *self, const gchar *filename)
}
/**
+ * gs_shell_details_set_url:
+ **/
+void
+gs_shell_details_set_url (GsShellDetails *self, const gchar *url)
+{
+ gs_shell_details_set_state (self, GS_SHELL_DETAILS_STATE_LOADING);
+ gs_plugin_loader_url_to_app_async (self->plugin_loader,
+ url,
+ GS_PLUGIN_REFINE_FLAGS_DEFAULT |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_RATING |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEW_RATINGS |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_REVIEWS,
+ self->cancellable,
+ gs_shell_details_file_to_app_cb,
+ self);
+}
+
+/**
* gs_shell_details_load:
**/
static void
diff --git a/src/gs-shell-details.h b/src/gs-shell-details.h
index 3ffd72a..cbe78a8 100644
--- a/src/gs-shell-details.h
+++ b/src/gs-shell-details.h
@@ -42,6 +42,8 @@ void gs_shell_details_set_app (GsShellDetails *self,
GsApp *app);
void gs_shell_details_set_filename (GsShellDetails *self,
const gchar *filename);
+void gs_shell_details_set_url (GsShellDetails *self,
+ const gchar *url);
GsApp *gs_shell_details_get_app (GsShellDetails *self);
void gs_shell_details_reload (GsShellDetails *self);
void gs_shell_details_setup (GsShellDetails *self,
diff --git a/src/gs-shell.c b/src/gs-shell.c
index 43dd240..8212f1e 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -292,8 +292,13 @@ gs_shell_change_mode (GsShell *shell,
new_page = GS_PAGE (priv->shell_updates);
break;
case GS_SHELL_MODE_DETAILS:
- if (app != NULL)
- gs_shell_details_set_app (priv->shell_details, app);
+ if (app != NULL) {
+ if (gs_app_get_metadata_item (app, "GnomeSoftware::from-url") != NULL)
+ gs_shell_details_set_url (priv->shell_details,
+ gs_app_get_metadata_item (app,
"GnomeSoftware::from-url"));
+ else
+ gs_shell_details_set_app (priv->shell_details, app);
+ }
if (data != NULL)
gs_shell_details_set_filename (priv->shell_details, data);
new_page = GS_PAGE (priv->shell_details);
diff --git a/src/gs-utils.c b/src/gs-utils.c
index 15702f6..c2b1b24 100644
--- a/src/gs-utils.c
+++ b/src/gs-utils.c
@@ -24,6 +24,7 @@
#include <glib/gi18n.h>
#include <gio/gdesktopappinfo.h>
#include <errno.h>
+#include <json-glib/json-glib.h>
#ifdef HAVE_POLKIT
#include <polkit/polkit.h>
@@ -755,4 +756,64 @@ gs_utils_get_desktop_app_info (const gchar *id)
return app_info;
}
+/**
+ * gs_utils_get_url_scheme:
+ * @url: A URL, e.g. "appstream://gimp.desktop"
+ *
+ * Gets the scheme from the URL string.
+ *
+ * Returns: the URL scheme, e.g. "appstream"
+ */
+gchar *
+gs_utils_get_url_scheme (const gchar *url)
+{
+ g_autoptr(SoupURI) uri = NULL;
+
+ /* no data */
+ if (url == NULL)
+ return NULL;
+
+ /* create URI from URL */
+ uri = soup_uri_new (url);
+ if (!SOUP_URI_IS_VALID (uri))
+ return NULL;
+
+ /* success */
+ return g_strdup (soup_uri_get_scheme (uri));
+}
+
+/**
+ * gs_utils_get_url_path:
+ * @url: A URL, e.g. "appstream://gimp.desktop"
+ *
+ * Gets the path from the URL string, removing any leading slashes.
+ *
+ * Returns: the URL path, e.g. "gimp.desktop"
+ */
+gchar *
+gs_utils_get_url_path (const gchar *url)
+{
+ g_autoptr(SoupURI) uri = NULL;
+ const gchar *host;
+ const gchar *path;
+
+ uri = soup_uri_new (url);
+ if (!SOUP_URI_IS_VALID (uri))
+ return NULL;
+
+ /* foo://bar -> scheme: foo, host: bar, path: / */
+ /* foo:bar -> scheme: foo, host: (empty string), path: /bar */
+ host = soup_uri_get_host (uri);
+ path = soup_uri_get_path (uri);
+ if (host != NULL && (strlen (host) > 0))
+ path = host;
+
+ /* trim any leading slashes */
+ while (*path == '/')
+ path++;
+
+ /* success */
+ return g_strdup (path);
+}
+
/* vim: set noexpandtab: */
diff --git a/src/gs-utils.h b/src/gs-utils.h
index 6990ca9..7757958 100644
--- a/src/gs-utils.h
+++ b/src/gs-utils.h
@@ -78,6 +78,9 @@ void gs_utils_show_error_dialog (GtkWindow *parent,
GDesktopAppInfo *gs_utils_get_desktop_app_info (const gchar *id);
+gchar *gs_utils_get_url_scheme (const gchar *url);
+gchar *gs_utils_get_url_path (const gchar *url);
+
G_END_DECLS
#endif /* __GS_UTILS_H */
diff --git a/src/plugins/gs-plugin-appstream.c b/src/plugins/gs-plugin-appstream.c
index 6dd5413..0ab2584 100644
--- a/src/plugins/gs-plugin-appstream.c
+++ b/src/plugins/gs-plugin-appstream.c
@@ -242,6 +242,41 @@ gs_plugin_setup (GsPlugin *plugin, GCancellable *cancellable, GError **error)
}
/**
+ * gs_plugin_url_to_app:
+ */
+gboolean
+gs_plugin_url_to_app (GsPlugin *plugin,
+ GList **list,
+ const gchar *url,
+ GCancellable *cancellable,
+ GError **error)
+{
+ AsApp *item;
+ g_autofree gchar *path = NULL;
+ g_autofree gchar *scheme = NULL;
+ g_autoptr(GsApp) app = NULL;
+
+ /* not us */
+ scheme = gs_utils_get_url_scheme (url);
+ if (g_strcmp0 (scheme, "appstream") != 0 && g_strcmp0 (scheme, "apt") != 0)
+ return TRUE;
+
+ /* create app */
+ path = gs_utils_get_url_path (url);
+ if (g_strcmp0 (scheme, "appstream") == 0)
+ item = as_store_get_app_by_id (plugin->priv->store, path);
+ else
+ item = as_store_get_app_by_pkgname (plugin->priv->store, path);
+ if (item == NULL)
+ return TRUE;
+ app = gs_app_new (as_app_get_id (item));
+ if (!gs_appstream_refine_app (plugin, app, item, error))
+ return FALSE;
+ gs_plugin_add_app (list, app);
+ return TRUE;
+}
+
+/**
* gs_plugin_refine_from_id:
*/
static gboolean
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]