[gnome-software/1476-add-a-way-for-app-developers-to-test-their-metainfo-appdata-files: 169/169] application: Add '--show-metainfo' command line argument
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/1476-add-a-way-for-app-developers-to-test-their-metainfo-appdata-files: 169/169] application: Add '--show-metainfo' command line argument
- Date: Mon, 29 Nov 2021 10:16:01 +0000 (UTC)
commit 9909425a54b847cd2a9bf4ba3b33402718bfbfeb
Author: Milan Crha <mcrha redhat com>
Date: Thu Oct 14 12:27:05 2021 +0200
application: Add '--show-metainfo' command line argument
This can be used to view an appstream data file how it'll be shown
in the Software, without a need to install it and search for it.
Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1476
lib/gs-appstream.c | 3 +-
src/gs-application.c | 30 +++++++++++
src/gs-details-page.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++--
src/gs-details-page.h | 2 +
src/gs-shell.c | 33 +++++++++++-
src/gs-shell.h | 2 +
6 files changed, 208 insertions(+), 6 deletions(-)
---
diff --git a/lib/gs-appstream.c b/lib/gs-appstream.c
index 74be46837..2a311cf9d 100644
--- a/lib/gs-appstream.c
+++ b/lib/gs-appstream.c
@@ -28,7 +28,8 @@ gs_appstream_create_app (GsPlugin *plugin, XbSilo *silo, XbNode *component, GErr
error))
return NULL;
- /* never add wildcard apps to the plugin cache */
+ /* never add wildcard apps to the plugin cache, and only add to
+ * the cache if it’s available */
if (gs_app_has_quirk (app_new, GS_APP_QUIRK_IS_WILDCARD) || plugin == NULL)
return g_steal_pointer (&app_new);
diff --git a/src/gs-application.c b/src/gs-application.c
index 0f1728e3c..8fce53170 100644
--- a/src/gs-application.c
+++ b/src/gs-application.c
@@ -140,6 +140,8 @@ gs_application_init (GsApplication *application)
{ "interaction", '\0', 0, G_OPTION_ARG_STRING, NULL,
_("The kind of interaction expected for this action: either "
"‘none’, ‘notify’, or ‘full’"), NULL },
+ { "show-metainfo", '\0', 0, G_OPTION_ARG_FILENAME, NULL,
+ _("Show a local metainfo or appdata file"), _("FILENAME") },
{ "verbose", '\0', 0, G_OPTION_ARG_NONE, NULL,
_("Show verbose debugging information"), NULL },
{ "autoupdate", 0, 0, G_OPTION_ARG_NONE, NULL,
@@ -758,6 +760,23 @@ filename_activated (GSimpleAction *action,
gs_shell_show_local_file (app->shell, file);
}
+static void
+show_metainfo_activated (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer data)
+{
+ GsApplication *app = GS_APPLICATION (data);
+ const gchar *filename;
+ g_autoptr(GFile) file = NULL;
+
+ g_variant_get (parameter, "(&s)", &filename);
+
+ file = g_file_new_for_path (filename);
+
+ gs_shell_reset_state (app->shell);
+ gs_shell_show_metainfo (app->shell, file);
+}
+
static void
launch_activated (GSimpleAction *action,
GVariant *parameter,
@@ -875,6 +894,7 @@ static GActionEntry actions_after_loading[] = {
{ "install", install_activated, "(su)", NULL, NULL },
{ "filename", filename_activated, "(s)", NULL, NULL },
{ "install-resources", install_resources_activated, "(sassss)", NULL, NULL },
+ { "show-metainfo", show_metainfo_activated, "(s)", NULL, NULL },
{ "nop", NULL, NULL, NULL }
};
@@ -1190,6 +1210,16 @@ gs_application_handle_local_options (GApplication *app, GVariantDict *options)
"filename",
g_variant_new ("(s)", absolute_filename));
rc = 0;
+ } else if (g_variant_dict_lookup (options, "show-metainfo", "^&ay", &local_filename)) {
+ g_autoptr(GFile) file = NULL;
+ g_autofree gchar *absolute_filename = NULL;
+
+ file = g_file_new_for_path (local_filename);
+ absolute_filename = g_file_get_path (file);
+ g_action_group_activate_action (G_ACTION_GROUP (app),
+ "show-metainfo",
+ g_variant_new ("(s)", absolute_filename));
+ rc = 0;
}
return rc;
diff --git a/src/gs-details-page.c b/src/gs-details-page.c
index f7be1a700..1aa70d254 100644
--- a/src/gs-details-page.c
+++ b/src/gs-details-page.c
@@ -14,6 +14,8 @@
#include <string.h>
#include <glib/gi18n.h>
+#include "lib/gs-appstream.h"
+
#include "gs-common.h"
#include "gs-utils.h"
@@ -1501,7 +1503,8 @@ _set_app (GsDetailsPage *self, GsApp *app)
/* show the UI and do operations that should not block page load */
static void
-gs_details_page_load_stage2 (GsDetailsPage *self)
+gs_details_page_load_stage2 (GsDetailsPage *self,
+ gboolean continue_loading)
{
g_autofree gchar *tmp = NULL;
g_autoptr(GsPluginJob) plugin_job1 = NULL;
@@ -1522,6 +1525,9 @@ gs_details_page_load_stage2 (GsDetailsPage *self)
gs_details_page_refresh_all (self);
gs_details_page_update_origin_button (self, FALSE);
+ if (!continue_loading)
+ return;
+
/* if these tasks fail (e.g. because we have no networking) then it's
* of no huge importance if we don't get the required data */
plugin_job1 = gs_plugin_job_newv (GS_PLUGIN_ACTION_REFINE,
@@ -1588,7 +1594,7 @@ gs_details_page_load_stage1_cb (GObject *source,
}
/* do 2nd stage refine */
- gs_details_page_load_stage2 (self);
+ gs_details_page_load_stage2 (self, TRUE);
}
static void
@@ -1612,7 +1618,7 @@ gs_details_page_file_to_app_cb (GObject *source,
GsApp *app = gs_app_list_index (list, 0);
g_set_object (&self->app_local_file, app);
_set_app (self, app);
- gs_details_page_load_stage2 (self);
+ gs_details_page_load_stage2 (self, TRUE);
}
}
@@ -1636,7 +1642,7 @@ gs_details_page_url_to_app_cb (GObject *source,
} else {
GsApp *app = gs_app_list_index (list, 0);
_set_app (self, app);
- gs_details_page_load_stage2 (self);
+ gs_details_page_load_stage2 (self, TRUE);
}
}
@@ -2493,3 +2499,133 @@ gs_details_page_set_is_narrow (GsDetailsPage *self, gboolean is_narrow)
self->is_narrow = is_narrow;
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_IS_NARROW]);
}
+
+static void
+gs_details_page_metainfo_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GsDetailsPage *self = GS_DETAILS_PAGE (source_object);
+ g_autoptr(GsApp) app = NULL;
+ g_autoptr(GError) error = NULL;
+
+ app = g_task_propagate_pointer (G_TASK (result), &error);
+ if (error) {
+ gtk_label_set_text (GTK_LABEL (self->label_failed), error->message);
+ gs_details_page_set_state (self, GS_DETAILS_PAGE_STATE_FAILED);
+ return;
+ }
+
+ g_set_object (&self->app_local_file, app);
+ _set_app (self, app);
+ gs_details_page_load_stage2 (self, FALSE);
+}
+
+static void
+gs_details_page_metainfo_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ const gchar *const *locales;
+ g_autofree gchar *xml = NULL;
+ g_autofree gchar *path = NULL;
+ g_autoptr(XbBuilder) builder = NULL;
+ g_autoptr(XbBuilderSource) builder_source = NULL;
+ g_autoptr(XbSilo) silo = NULL;
+ g_autoptr(GPtrArray) nodes = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsApp) app = NULL;
+ GFile *file = task_data;
+ XbNode *component;
+
+ builder_source = xb_builder_source_new ();
+ if (!xb_builder_source_load_file (builder_source, file, XB_BUILDER_SOURCE_FLAG_NONE, cancellable,
&error)) {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ builder = xb_builder_new ();
+ locales = g_get_language_names ();
+
+ /* add current locales */
+ for (guint i = 0; locales[i] != NULL; i++) {
+ xb_builder_add_locale (builder, locales[i]);
+ }
+
+ xb_builder_import_source (builder, builder_source);
+
+ silo = xb_builder_compile (builder, XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID |
XB_BUILDER_COMPILE_FLAG_SINGLE_LANG, cancellable, &error);
+ if (silo == NULL) {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ nodes = xb_silo_query (silo, "component", 0, NULL);
+ if (nodes == NULL)
+ nodes = xb_silo_query (silo, "application", 0, NULL);
+ if (nodes == NULL) {
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
+ "Passed-in file doesn't have a 'component' (nor 'application') top-level element");
+ return;
+ }
+
+ if (nodes->len != 1) {
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Only one top-level element expected, received %u instead", nodes->len);
+ return;
+ }
+
+ component = g_ptr_array_index (nodes, 0);
+
+ app = gs_appstream_create_app (NULL, silo, component, &error);
+ if (app == NULL) {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ if (!gs_appstream_refine_app (NULL, app, silo, component, GS_DETAILS_PAGE_REFINE_FLAGS, &error)) {
+ g_task_return_error (task, g_steal_pointer (&error));
+ return;
+ }
+
+ path = g_file_get_path (file);
+ gs_app_set_origin (app, path);
+
+ gs_app_set_state (app, GS_APP_STATE_UNKNOWN);
+
+ g_task_return_pointer (task, g_steal_pointer (&app), g_object_unref);
+}
+
+/**
+ * gs_details_page_set_metainfo:
+ * @self: a #GsDetailsPage
+ * @file: path to a metainfo file to display
+ *
+ * Load and show the given metainfo @file on the details page.
+ *
+ * The file must be a single metainfo file, not an appstream file
+ * containing multiple components. It will be shown as if it came
+ * from a configured repository. This function is intended to be
+ * used by application developers wanting to test how their metainfo
+ * will appear to users.
+ *
+ * Since: 42
+ */
+void
+gs_details_page_set_metainfo (GsDetailsPage *self,
+ GFile *file)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (GS_IS_DETAILS_PAGE (self));
+ g_return_if_fail (G_IS_FILE (file));
+ gs_details_page_set_state (self, GS_DETAILS_PAGE_STATE_LOADING);
+ g_clear_object (&self->app_local_file);
+ g_clear_object (&self->app);
+ self->origin_by_packaging_format = FALSE;
+ task = g_task_new (self, self->cancellable, gs_details_page_metainfo_ready_cb, NULL);
+ g_task_set_source_tag (task, gs_details_page_set_metainfo);
+ g_task_set_task_data (task, g_object_ref (file), g_object_unref);
+ g_task_run_in_thread (task, gs_details_page_metainfo_thread);
+}
diff --git a/src/gs-details-page.h b/src/gs-details-page.h
index 1ec5059d1..566e79b95 100644
--- a/src/gs-details-page.h
+++ b/src/gs-details-page.h
@@ -33,5 +33,7 @@ void gs_details_page_set_odrs_provider (GsDetailsPage *self,
gboolean gs_details_page_get_is_narrow (GsDetailsPage *self);
void gs_details_page_set_is_narrow (GsDetailsPage *self,
gboolean is_narrow);
+void gs_details_page_set_metainfo (GsDetailsPage *self,
+ GFile *file);
G_END_DECLS
diff --git a/src/gs-shell.c b/src/gs-shell.c
index f3baf41c9..a79765eab 100644
--- a/src/gs-shell.c
+++ b/src/gs-shell.c
@@ -620,7 +620,10 @@ gs_shell_change_mode (GsShell *shell,
gtk_editable_set_position (GTK_EDITABLE (shell->entry_search), -1);
} else if (mode == GS_SHELL_MODE_DETAILS) {
app = GS_APP (data);
- if (gs_app_get_local_file (app) != NULL) {
+ if (gs_app_get_metadata_item (app, "GnomeSoftware::show-metainfo") != NULL) {
+ gs_details_page_set_metainfo (GS_DETAILS_PAGE (page),
+ gs_app_get_local_file (app));
+ } else if (gs_app_get_local_file (app) != NULL) {
gs_details_page_set_local_file (GS_DETAILS_PAGE (page),
gs_app_get_local_file (app));
} else if (gs_app_get_metadata_item (app, "GnomeSoftware::from-url") != NULL) {
@@ -2366,6 +2369,34 @@ gs_shell_show_local_file (GsShell *shell, GFile *file)
gs_shell_activate (shell);
}
+/**
+ * gs_shell_show_metainfo:
+ * @shell: a #GsShell
+ * @file: path to a metainfo file to display
+ *
+ * Open a metainfo file and display it on the details page as if it were
+ * published in a repository configured on the system.
+ *
+ * This is intended for app developers to be able to test their metainfo files
+ * locally.
+ *
+ * Since: 42
+ */
+void
+gs_shell_show_metainfo (GsShell *shell, GFile *file)
+{
+ g_autoptr(GsApp) app = gs_app_new (NULL);
+
+ g_return_if_fail (GS_IS_SHELL (shell));
+ g_return_if_fail (G_IS_FILE (file));
+ save_back_entry (shell);
+ gs_app_set_metadata (app, "GnomeSoftware::show-metainfo", "1");
+ gs_app_set_local_file (app, file);
+ gs_shell_change_mode (shell, GS_SHELL_MODE_DETAILS,
+ (gpointer) app, TRUE);
+ gs_shell_activate (shell);
+}
+
void
gs_shell_show_search_result (GsShell *shell, const gchar *id, const gchar *search)
{
diff --git a/src/gs-shell.h b/src/gs-shell.h
index 0ce741348..a7b1f73cc 100644
--- a/src/gs-shell.h
+++ b/src/gs-shell.h
@@ -84,5 +84,7 @@ void gs_shell_setup (GsShell *shell,
void gs_shell_show_notification (GsShell *shell,
const gchar *title);
gboolean gs_shell_get_is_narrow (GsShell *shell);
+void gs_shell_show_metainfo (GsShell *shell,
+ GFile *file);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]