[gnome-software/1409-add-available-for-fedora-section-to-the-explore-page: 115/115] 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: 115/115] gs-overview-page: Add a deployment-featured section
- Date: Mon, 23 May 2022 07:12:21 +0000 (UTC)
commit 722b54083d750f0c800f3f40f31d50ade41df048
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 | 29 ++++
src/gs-overview-page.c | 186 ++++++++++++++++++++++
src/gs-overview-page.ui | 27 ++++
5 files changed, 257 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..b3ba38578
--- /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;
+ 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 856a9e683..e24c806bd 100644
--- a/doc/vendor-customisation.md
+++ b/doc/vendor-customisation.md
@@ -180,3 +180,32 @@ 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 needs to be provided.
+
+The first file is `org.gnome.Software.DeploymentFeatured.xml`, which works similarly
+as the `org.gnome.Software.Featured.xml` and should be saved beside it, it only sets
+`GnomeSoftware::DeploymentFeatured` key for the apps. The value of this key is
+a string containing the deployment name. There is a limit of the found featured
+apps. The section is not shown when no enough apps are found.
+
+The second file is `deployment-featured.ini` file, which contains the label and
+the selector for the section. The label is a localized key, containing the label
+text for the section. The selector defines which apps should be picked. It is
+a comma-separated list of `GnomeSoftware::DeploymentFeatured` key values, thus
+the deployment can feature apps from more vendors. The `deployment-featured.ini`
+should be saved in one of the `sysconfdir`, system config dirs or system data
+dirs, which are checked, in this order, for existence of the `gnome-software/deployment-featured.ini`
+file and the first directory containing it will be used. The relevant file names
+contain `/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 of these files, including their adding/remove, require restart of
+the gnome-software process.
+
+Example files can be found in the `contrib/` directory.
diff --git a/src/gs-overview-page.c b/src/gs-overview-page.c
index 0b8115dc4..55bb0d54a 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_filename;
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;
@@ -313,6 +318,56 @@ 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;
+ }
+
+ gs_app_list_randomize (list);
+
+ gs_widget_remove_all (self->box_deployment_featured, (GsRemoveFunc) gtk_flow_box_remove);
+
+ for (i = 0; i < gs_app_list_length (list) && i < N_TILES; 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)
{
@@ -489,6 +544,96 @@ 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 void
+gs_overview_page_init_deployment_featured_filename (GsOverviewPage *self)
+{
+ g_autoptr(GPtrArray) dirs = NULL;
+ g_autofree gchar *filename = NULL;
+ const gchar * const *sys_dirs;
+
+ g_return_if_fail (self->deployment_featured_filename == NULL);
+
+ #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);
+ self->deployment_featured_filename = g_steal_pointer (&filename);
+ return;
+ }
+ 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);
+ self->deployment_featured_filename = g_steal_pointer (&tmp);
+ return;
+ }
+ 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);
+ self->deployment_featured_filename = g_steal_pointer (&tmp);
+ return;
+ }
+ g_debug ("File '%s' does not exist, %s", tmp, sys_dirs[i + 1] ? "trying next" : "no more
files to try");
+ }
+
+ #undef FILENAME
+}
+
+static gchar *
+gs_overview_page_read_deployment_featured_key (GsOverviewPage *self,
+ const gchar *key,
+ gboolean localized)
+{
+ g_autoptr(GKeyFile) key_file = NULL;
+ g_autoptr(GError) error = NULL;
+
+ if (!self->deployment_featured_filename)
+ return NULL;
+
+ key_file = g_key_file_new ();
+ if (!g_key_file_load_from_file (key_file, self->deployment_featured_filename, G_KEY_FILE_NONE,
&error)) {
+ g_debug ("Failed to read '%s': %s", self->deployment_featured_filename, error ?
error->message : "Unknown error");
+ g_clear_pointer (&self->deployment_featured_filename, g_free);
+ return NULL;
+ }
+
+ if (localized)
+ return g_key_file_get_locale_string (key_file, "Deployment Featured Apps", key, NULL, NULL);
+
+ return g_key_file_get_string (key_file, "Deployment Featured Apps", key, NULL);
+}
+
+static gchar *
+gs_overview_page_get_deployment_featured_label (GsOverviewPage *self)
+{
+ return gs_overview_page_read_deployment_featured_key (self, "Label", TRUE);
+}
+
+static gchar **
+gs_overview_page_get_featured_deployments (GsOverviewPage *self)
+{
+ g_autofree gchar *selector = gs_overview_page_read_deployment_featured_key (self, "Selector", FALSE);
+ g_auto(GStrv) deployments = NULL;
+ if (selector == NULL || *selector == '\0')
+ return NULL;
+ deployments = g_strsplit (selector, ",", -1);
+ if (deployments != NULL && deployments[0] != NULL && *deployments[0] != '\0')
+ return g_steal_pointer (&deployments);
+ return NULL;
+}
+
static void
gs_overview_page_load (GsOverviewPage *self)
{
@@ -513,6 +658,36 @@ gs_overview_page_load (GsOverviewPage *self)
self->action_cnt++;
}
+ if (!self->loading_deployment_featured) {
+ g_auto(GStrv) deployments = gs_overview_page_get_featured_deployments (self);
+
+ self->loading_deployment_featured = TRUE;
+
+ if (deployments != NULL) {
+ g_autoptr(GsPluginJob) plugin_job = NULL;
+ g_autoptr(GsAppQuery) query = NULL;
+ GsPluginListAppsFlags flags = GS_PLUGIN_LIST_APPS_FLAGS_INTERACTIVE;
+
+ query = gs_app_query_new ("deployment-featured", deployments,
+ "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;
@@ -721,11 +896,19 @@ 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);
+
+ gs_overview_page_init_deployment_featured_filename (self);
+
+ tmp_label = gs_overview_page_get_deployment_featured_label (self);
+ if (tmp_label != NULL)
+ gtk_label_set_text (GTK_LABEL (self->deployment_featured_heading), tmp_label);
}
static void
@@ -777,6 +960,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_filename, g_free);
G_OBJECT_CLASS (gs_overview_page_parent_class)->dispose (object);
}
@@ -813,11 +997,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]