[gnome-software/1409-add-available-for-fedora-section-to-the-explore-page: 9/9] gs-overview-page: Add a deployment-featured section
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/1409-add-available-for-fedora-section-to-the-explore-page: 9/9] gs-overview-page: Add a deployment-featured section
- Date: Wed, 25 May 2022 06:20:54 +0000 (UTC)
commit b2b54cf4b87ee851a3d5a18817986a04bfbe7670
Author: Milan Crha <mcrha redhat com>
Date: Fri May 6 13:34:17 2022 +0200
gs-overview-page: Add a deployment-featured section
See the doc/vendor-customisation.md `Deployment Featured Apps` section
for an information how this works.
Closes https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1409
contrib/deployment-featured.ini | 4 +
contrib/org.gnome.Software.DeploymentFeatured.xml | 11 ++
doc/vendor-customisation.md | 32 ++++
src/gs-overview-page.c | 188 ++++++++++++++++++++++
src/gs-overview-page.ui | 27 ++++
5 files changed, 262 insertions(+)
---
diff --git a/contrib/deployment-featured.ini b/contrib/deployment-featured.ini
new file mode 100644
index 000000000..d6cf51f53
--- /dev/null
+++ b/contrib/deployment-featured.ini
@@ -0,0 +1,4 @@
+[Deployment Featured Apps]
+Selector=foocorp
+Label=Featured by Foo Corp.
+Label[it]=Presentato da Foo Corp.
diff --git a/contrib/org.gnome.Software.DeploymentFeatured.xml
b/contrib/org.gnome.Software.DeploymentFeatured.xml
new file mode 100644
index 000000000..520f19ae9
--- /dev/null
+++ b/contrib/org.gnome.Software.DeploymentFeatured.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- This is only an example file, what the deployment-featured file can look like;
+ if used, it should be installed beside the org.gnome.Software.Featured.xml file. -->
+<components>
+ <component merge="append">
+ <id>org.gnome.Software.desktop</id>
+ <custom>
+ <value key="GnomeSoftware::DeploymentFeatured">foocorp</value>
+ </custom>
+ </component>
+</components>
diff --git a/doc/vendor-customisation.md b/doc/vendor-customisation.md
index c15201ef7..88669105e 100644
--- a/doc/vendor-customisation.md
+++ b/doc/vendor-customisation.md
@@ -180,3 +180,35 @@ section of the overview page.
Pass `-Ddefault_featured_apps=false` when configuring GNOME Software to disable
the default list of featured applications. Pass `-Dhardcoded_curated=false` to
disable the default list of “Editor’s Choice” applications.
+
+Deployment Featured Apps
+------------------------
+
+Deployments can feature their own applications, which will be shown in the Explore
+page in its own section. To have the section shown, two files need to be provided.
+The number of deployment-featured apps is limited in the UI, and if not enough
+deployment-featured apps are found, then the section will not be shown at all.
+
+The first file is `org.gnome.Software.DeploymentFeatured.xml`, which works similarly
+to `org.gnome.Software.Featured.xml` and should be saved beside it in an appstream
+directory. It sets the `GnomeSoftware::DeploymentFeatured` key on apps which should
+be featured for this distribution or deployment. The value of this key is a string
+containing the deployment name as an identifier.
+
+The second file is `deployment-featured.ini`, which contains a human-readable title and
+the selector for the section. The title is a localized key, and is used to set the heading
+for the section on the Explore page. The selector defines which apps should be picked.
+It is a semicolon-separated list of `GnomeSoftware::DeploymentFeatured` key values, thus
+the deployment can feature apps from zero or more vendors.
+
+The `deployment-featured.ini` file should be saved in one of the `sysconfdir`, system
+config dirs or system data dirs. They are checked, in that order, for existence of
+the `gnome-software/deployment-featured.ini` file. The first directory containing
+it will be used. The relevant file names are `/etc/xdg/gnome-software/deployment-featured.ini`,
+`/usr/local/share/gnome-software/deployment-featured.ini` and
+`/usr/share/gnome-software/deployment-featured.ini`.
+
+Any changes to these files, including adding or removing them, will only be noticed
+when gnome-software is restarted.
+
+Example files can be found in the `contrib/` directory.
diff --git a/src/gs-overview-page.c b/src/gs-overview-page.c
index e2274f044..70968321a 100644
--- a/src/gs-overview-page.c
+++ b/src/gs-overview-page.c
@@ -36,6 +36,7 @@ struct _GsOverviewPage
gint action_cnt;
gboolean loading_featured;
gboolean loading_curated;
+ gboolean loading_deployment_featured;
gboolean loading_recent;
gboolean loading_categories;
gboolean empty;
@@ -43,6 +44,7 @@ struct _GsOverviewPage
GHashTable *category_hash; /* id : GsCategory */
GsFedoraThirdParty *third_party;
gboolean third_party_needs_question;
+ gchar **deployment_featured;
GtkWidget *infobar_third_party;
GtkWidget *label_third_party;
@@ -50,11 +52,13 @@ struct _GsOverviewPage
GtkWidget *box_overview;
GtkWidget *box_curated;
GtkWidget *box_recent;
+ GtkWidget *box_deployment_featured;
GtkWidget *flowbox_categories;
GtkWidget *flowbox_iconless_categories;
GtkWidget *iconless_categories_heading;
GtkWidget *curated_heading;
GtkWidget *recent_heading;
+ GtkWidget *deployment_featured_heading;
GtkWidget *scrolledwindow_overview;
GtkWidget *stack_overview;
};
@@ -114,6 +118,7 @@ gs_overview_page_decrement_action_cnt (GsOverviewPage *self)
self->cache_valid = TRUE;
g_signal_emit (self, signals[SIGNAL_REFRESHED], 0);
self->loading_categories = FALSE;
+ self->loading_deployment_featured = FALSE;
self->loading_featured = FALSE;
self->loading_curated = FALSE;
self->loading_recent = FALSE;
@@ -311,6 +316,55 @@ out:
gs_overview_page_decrement_action_cnt (self);
}
+static void
+gs_overview_page_get_deployment_featured_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GsOverviewPage *self = GS_OVERVIEW_PAGE (user_data);
+ GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (source_object);
+ guint i;
+ GsApp *app;
+ GtkWidget *tile;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GsAppList) list = NULL;
+
+ /* get deployment-featured apps */
+ list = gs_plugin_loader_job_process_finish (plugin_loader, res, &error);
+ if (list == NULL) {
+ if (!g_error_matches (error, GS_PLUGIN_ERROR, GS_PLUGIN_ERROR_CANCELLED))
+ g_warning ("failed to get deployment-featured apps: %s", error->message);
+ goto out;
+ }
+
+ /* not enough to show */
+ if (gs_app_list_length (list) < N_TILES) {
+ g_warning ("Only %u apps for deployment-featured list, hiding",
+ gs_app_list_length (list));
+ gtk_widget_set_visible (self->box_deployment_featured, FALSE);
+ gtk_widget_set_visible (self->deployment_featured_heading, FALSE);
+ goto out;
+ }
+
+ g_assert (gs_app_list_length (list) == N_TILES);
+ gs_widget_remove_all (self->box_deployment_featured, (GsRemoveFunc) gtk_flow_box_remove);
+
+ for (i = 0; i < gs_app_list_length (list); i++) {
+ app = gs_app_list_index (list, i);
+ tile = gs_summary_tile_new (app);
+ g_signal_connect (tile, "clicked",
+ G_CALLBACK (app_tile_clicked), self);
+ gtk_flow_box_insert (GTK_FLOW_BOX (self->box_deployment_featured), tile, -1);
+ }
+ gtk_widget_set_visible (self->box_deployment_featured, TRUE);
+ gtk_widget_set_visible (self->deployment_featured_heading, TRUE);
+
+ self->empty = FALSE;
+
+ out:
+ gs_overview_page_decrement_action_cnt (self);
+}
+
static void
category_tile_clicked (GsCategoryTile *tile, gpointer data)
{
@@ -487,6 +541,106 @@ fedora_third_party_disable (GsOverviewPage *self)
gs_fedora_third_party_opt_out (self->third_party, self->cancellable,
fedora_third_party_disable_done_cb, g_object_ref (self));
}
+static gchar *
+gs_overview_page_dup_deployment_featured_filename (void)
+{
+ g_autoptr(GPtrArray) dirs = NULL;
+ g_autofree gchar *filename = NULL;
+ const gchar * const *sys_dirs;
+
+ #define FILENAME "deployment-featured.ini"
+
+ filename = g_build_filename (SYSCONFDIR, "gnome-software", FILENAME, NULL);
+ if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
+ g_debug ("Found '%s'", filename);
+ return g_steal_pointer (&filename);
+ }
+ g_debug ("File '%s' does not exist, trying next", filename);
+ g_clear_pointer (&filename, g_free);
+
+ sys_dirs = g_get_system_config_dirs ();
+
+ for (guint i = 0; sys_dirs != NULL && sys_dirs[i]; i++) {
+ g_autofree gchar *tmp = g_build_filename (sys_dirs[i], "gnome-software", FILENAME, NULL);
+ if (g_file_test (tmp, G_FILE_TEST_IS_REGULAR)) {
+ g_debug ("Found '%s'", tmp);
+ return g_steal_pointer (&tmp);
+ }
+ g_debug ("File '%s' does not exist, trying next", tmp);
+ }
+
+ sys_dirs = g_get_system_data_dirs ();
+
+ for (guint i = 0; sys_dirs != NULL && sys_dirs[i]; i++) {
+ g_autofree gchar *tmp = g_build_filename (sys_dirs[i], "gnome-software", FILENAME, NULL);
+ if (g_file_test (tmp, G_FILE_TEST_IS_REGULAR)) {
+ g_debug ("Found '%s'", tmp);
+ return g_steal_pointer (&tmp);
+ }
+ g_debug ("File '%s' does not exist, %s", tmp, sys_dirs[i + 1] ? "trying next" : "no more
files to try");
+ }
+
+ #undef FILENAME
+
+ return NULL;
+}
+
+static gboolean
+gs_overview_page_read_deployment_featured_keys (gchar **out_label,
+ gchar ***out_deployment_featured)
+{
+ g_autoptr(GKeyFile) key_file = NULL;
+ g_autoptr(GPtrArray) array = NULL;
+ g_auto(GStrv) selector = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autofree gchar *filename = NULL;
+
+ filename = gs_overview_page_dup_deployment_featured_filename ();
+
+ if (filename == NULL)
+ return FALSE;
+
+ key_file = g_key_file_new ();
+ if (!g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &error)) {
+ g_debug ("Failed to read '%s': %s", filename, error->message);
+ return FALSE;
+ }
+
+ *out_label = g_key_file_get_locale_string (key_file, "Deployment Featured Apps", "Label", NULL, NULL);
+
+ if (*out_label == NULL || **out_label == '\0') {
+ g_clear_pointer (out_label, g_free);
+ return FALSE;
+ }
+
+ selector = g_key_file_get_string_list (key_file, "Deployment Featured Apps", "Selector", NULL, NULL);
+
+ /* Sanitize the content */
+ if (selector == NULL) {
+ g_clear_pointer (out_label, g_free);
+ return FALSE;
+ }
+
+ array = g_ptr_array_sized_new (g_strv_length (selector) + 1);
+
+ for (guint i = 0; selector[i] != NULL; i++) {
+ const gchar *value = g_strstrip (selector[i]);
+ if (*value != '\0')
+ g_ptr_array_add (array, g_strdup (value));
+ }
+
+ if (array->len == 0) {
+ g_clear_pointer (out_label, g_free);
+ return FALSE;
+ }
+
+ g_ptr_array_add (array, NULL);
+
+ *out_deployment_featured = (gchar **) g_ptr_array_free (g_steal_pointer (&array), FALSE);
+
+ return TRUE;
+}
+
static void
gs_overview_page_load (GsOverviewPage *self)
{
@@ -519,6 +673,32 @@ gs_overview_page_load (GsOverviewPage *self)
self->action_cnt++;
}
+ if (!self->loading_deployment_featured && self->deployment_featured != NULL) {
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ g_autoptr(GsAppQuery) query = NULL;
+ GsPluginListAppsFlags flags = GS_PLUGIN_LIST_APPS_FLAGS_INTERACTIVE;
+
+ self->loading_deployment_featured = TRUE;
+
+ query = gs_app_query_new ("deployment-featured", self->deployment_featured,
+ "max-results", N_TILES,
+ "refine-flags", GS_PLUGIN_REFINE_FLAGS_REQUIRE_RATING |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_CATEGORIES |
+ GS_PLUGIN_REFINE_FLAGS_REQUIRE_ICON,
+ "dedupe-flags", GS_APP_LIST_FILTER_FLAG_PREFER_INSTALLED |
+ GS_APP_LIST_FILTER_FLAG_KEY_ID_PROVIDES,
+ NULL);
+
+ plugin_job = gs_plugin_job_list_apps_new (query, flags);
+
+ gs_plugin_loader_job_process_async (self->plugin_loader,
+ plugin_job,
+ self->cancellable,
+ gs_overview_page_get_deployment_featured_cb,
+ self);
+ self->action_cnt++;
+ }
+
if (!self->loading_curated) {
g_autoptr(GsPluginJob) plugin_job = NULL;
g_autoptr(GsAppQuery) query = NULL;
@@ -727,11 +907,16 @@ refreshed_cb (GsOverviewPage *self, gpointer user_data)
static void
gs_overview_page_init (GsOverviewPage *self)
{
+ g_autofree gchar *tmp_label = NULL;
+
gtk_widget_init_template (GTK_WIDGET (self));
gs_featured_carousel_set_apps (GS_FEATURED_CAROUSEL (self->featured_carousel), NULL);
g_signal_connect (self, "refreshed", G_CALLBACK (refreshed_cb), self);
+
+ if (gs_overview_page_read_deployment_featured_keys (&tmp_label, &self->deployment_featured))
+ gtk_label_set_text (GTK_LABEL (self->deployment_featured_heading), tmp_label);
}
static void
@@ -783,6 +968,7 @@ gs_overview_page_dispose (GObject *object)
g_clear_object (&self->cancellable);
g_clear_object (&self->third_party);
g_clear_pointer (&self->category_hash, g_hash_table_unref);
+ g_clear_pointer (&self->deployment_featured, g_strfreev);
G_OBJECT_CLASS (gs_overview_page_parent_class)->dispose (object);
}
@@ -819,11 +1005,13 @@ gs_overview_page_class_init (GsOverviewPageClass *klass)
gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, box_overview);
gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, box_curated);
gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, box_recent);
+ gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, box_deployment_featured);
gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, flowbox_categories);
gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, flowbox_iconless_categories);
gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, iconless_categories_heading);
gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, curated_heading);
gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, recent_heading);
+ gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, deployment_featured_heading);
gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, scrolledwindow_overview);
gtk_widget_class_bind_template_child (widget_class, GsOverviewPage, stack_overview);
gtk_widget_class_bind_template_callback (widget_class, featured_carousel_app_clicked_cb);
diff --git a/src/gs-overview-page.ui b/src/gs-overview-page.ui
index a556aa04e..55c9dd795 100644
--- a/src/gs-overview-page.ui
+++ b/src/gs-overview-page.ui
@@ -151,6 +151,33 @@
</object>
</child>
+ <child>
+ <object class="GtkLabel" id="deployment_featured_heading">
+ <property name="visible">False</property>
+ <property name="xalign">0</property>
+ <property name="label">Available for Deployment</property> <!--
placeholder, set in the code -->
+ <property name="margin-top">21</property>
+ <property name="margin-bottom">6</property>
+ <style>
+ <class name="heading"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkFlowBox" id="box_deployment_featured">
+ <property name="visible">False</property>
+ <property name="row_spacing">14</property>
+ <property name="column_spacing">14</property>
+ <property name="homogeneous">True</property>
+ <property name="min_children_per_line">2</property>
+ <property name="max_children_per_line">3</property>
+ <property name="selection_mode">none</property>
+ <accessibility>
+ <relation name="labelled-by">deployment_featured_heading</relation>
+ </accessibility>
+ </object>
+ </child>
+
<child>
<object class="GtkLabel" id="iconless_categories_heading">
<property name="xalign">0</property>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]