diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:57:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:57:27 +0000 |
commit | 6f0f7d1b40a8fa8d46a2d6f4317600001cdbbb18 (patch) | |
tree | d423850ae901365e582137bdf2b5cbdffd7ca266 /doc | |
parent | Initial commit. (diff) | |
download | gnome-software-upstream/43.5.tar.xz gnome-software-upstream/43.5.zip |
Adding upstream version 43.5.upstream/43.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | doc/api/gnome-software-docs.xml | 778 | ||||
-rw-r--r-- | doc/api/gnome-software.types | 6 | ||||
-rw-r--r-- | doc/api/gs-example-details.png | bin | 0 -> 53714 bytes | |||
-rw-r--r-- | doc/api/gs-example-installed.png | bin | 0 -> 106482 bytes | |||
-rw-r--r-- | doc/api/gs-example-search.png | bin | 0 -> 24934 bytes | |||
-rw-r--r-- | doc/api/meson.build | 11 | ||||
-rw-r--r-- | doc/app-developers.md | 69 | ||||
-rw-r--r-- | doc/design.svg | 448 | ||||
-rw-r--r-- | doc/kudos.md | 70 | ||||
-rw-r--r-- | doc/meson.build | 3 | ||||
-rw-r--r-- | doc/privacy-policy.html | 31 | ||||
-rwxr-xr-x | doc/update.sh | 1 | ||||
-rw-r--r-- | doc/use-cases.md | 38 | ||||
-rw-r--r-- | doc/vendor-customisation.md | 214 |
14 files changed, 1669 insertions, 0 deletions
diff --git a/doc/api/gnome-software-docs.xml b/doc/api/gnome-software-docs.xml new file mode 100644 index 0000000..aa16279 --- /dev/null +++ b/doc/api/gnome-software-docs.xml @@ -0,0 +1,778 @@ +<?xml version="1.0"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN" + "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd" +[ + <!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'"> +]> +<book id="index"> + <bookinfo> + <title>GNOME Software Reference Manual</title> + </bookinfo> + + <reference id="tutorial"> + <title>GNOME Software Plugin Tutorial</title> + <partintro> + <para> + GNOME Software is a software installer designed to be easy to use. + </para> + + <section> + <title>Introduction</title> + <para> + At the heart of gnome software the application is just a plugin loader + that has some GTK UI that gets created for various result types. + The idea is we have lots of small plugins that each do one thing and + then pass the result onto the other plugins. + These are ordered by dependencies against each other at runtime and + each one can do things like editing an existing application or adding a + new application to the result set. + This is how we can add support for things like firmware updating, + GNOME Shell web-apps and flatpak bundles without making big + changes all over the source tree. + </para> + <para> + There are broadly 3 types of plugin methods: + </para> + <itemizedlist> + <listitem> + <para><emphasis role="strong">Actions</emphasis>: Do something on a specific GsApp</para> + </listitem> + <listitem> + <para><emphasis role="strong">Refine</emphasis>: Get details about a specific GsApp</para> + </listitem> + <listitem> + <para><emphasis role="strong">Adopt</emphasis>: Can this plugin handle this GsApp</para> + </listitem> + </itemizedlist> + <para> + In general, building things out-of-tree isn't something that I think is + a very good idea; the API and ABI inside gnome-software is still + changing and there's a huge benefit to getting plugins upstream where + they can undergo review and be ported as the API adapts. + I'm also super keen to provide configurability in GSettings for doing + obviously-useful things, the sort of thing Fleet Commander can set for + groups of users. + </para> + <para> + However, now we're shipping gnome-software in enterprise-class distros + we might want to allow customers to ship their own plugins to make + various business-specific changes that don't make sense upstream. + This might involve querying a custom LDAP server and changing the + suggested apps to reflect what groups the user is in, or might involve + showing a whole new class of applications that does not conform to the + Linux-specific <emphasis>application is a desktop-file</emphasis> paradigm. + This is where a plugin makes sense. + </para> + + <para> + The plugin needs to create a class derived from <type>GsPlugin</type>, + and define the vfuncs that it needs. The + plugin name is taken automatically from the suffix of the + <filename>.so</filename> file. The type of the plugin is exposed to + gnome-software using <function>gs_plugin_query_type()</function>, which + must be exported from the module. + </para> + <example> + <title>A sample plugin</title> + <programlisting> +/* + * Copyright (C) 2016 Richard Hughes + */ + +#include <glib.h> +#include <gnome-software.h> + +struct _GsPluginSample { + GsPlugin parent; + + /* private data here */ +}; + +G_DEFINE_TYPE (GsPluginSample, gs_plugin_sample, GS_TYPE_PLUGIN) + +static void +gs_plugin_sample_init (GsPluginSample *self) +{ + GsPlugin *plugin = GS_PLUGIN (self); + + gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_BEFORE, "appstream"); +} + +static void +gs_plugin_sample_list_apps_async (GsPlugin *plugin, + GsAppQuery *query, + GsPluginListAppsFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GsPluginSample *self = GS_PLUGIN_SAMPLE (plugin); + g_autoptr(GTask) task = NULL; + const gchar * const *keywords; + g_autoptr(GsAppList) list = gs_app_list_new (); + + task = gs_plugin_list_apps_data_new_task (plugin, query, flags, + cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_sample_list_apps_async); + + if (query == NULL || + gs_app_query_get_keywords (query) == NULL || + gs_app_query_get_n_properties_set (query) != 1) { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Unsupported query"); + return; + } + + keywords = gs_app_query_get_keywords (query); + + for (gsize i = 0; keywords[i] != NULL; i++) { + if (g_str_equal (keywords[i], "fotoshop")) { + g_autoptr(GsApp) app = gs_app_new ("gimp.desktop"); + gs_app_add_quirk (app, GS_APP_QUIRK_IS_WILDCARD); + gs_app_list_add (list, app); + } + } + + g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); +} + +static GsAppList * +gs_plugin_sample_list_apps_finish (GsPlugin *plugin, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (result), error); +} + +static void +gs_plugin_sample_class_init (GsPluginSampleClass *klass) +{ + GsPluginClass *plugin_class = GS_PLUGIN_CLASS (klass); + + plugin_class->list_apps_async = gs_plugin_sample_list_apps_async; + plugin_class->list_apps_finish = gs_plugin_sample_list_apps_finish; +} + +GType +gs_plugin_query_type (void) +{ + return GS_TYPE_PLUGIN_SAMPLE; +} + </programlisting> + </example> + + <para> + We have to define when our plugin is run in reference to other plugins, + in this case, making sure we run before <code>appstream</code>. + As we're such a simple plugin we're relying on another plugin to run + after us to actually make the GsApp <emphasis>complete</emphasis>, + i.e. loading icons and setting a localised long description. + </para> + <para> + In this example we want to show GIMP as a result (from any provider, + e.g. flatpak or a distro package) when the user searches exactly for + <code>fotoshop</code>. + </para> + <para> + We can then build and install the plugin using: + </para> + <informalexample> + <programlisting> +gcc -shared -o libgs_plugin_example.so gs-plugin-example.c -fPIC \ + $(pkg-config --libs --cflags gnome-software) \ + -DI_KNOW_THE_GNOME_SOFTWARE_API_IS_SUBJECT_TO_CHANGE && \ +cp libgs_plugin_example.so $(pkg-config gnome-software --variable=plugindir) + </programlisting> + </informalexample> + + <mediaobject id="gs-example-search"> + <imageobject> + <imagedata format="PNG" fileref="gs-example-search.png" align="center"/> + </imageobject> + </mediaobject> + + </section> + + <section> + <title>Distribution Specific Functionality</title> + <para> + Some plugins should only run on specific distributions, for instance + the <code>fedora-pkgdb-collections</code> plugin should only be used on + Fedora systems. + This can be achieved with a simple runtime check using the helper + <code>gs_plugin_check_distro_id()</code> method or the <code>GsOsRelease</code> + object where more complicated rules are required. + </para> + <example> + <title>Self disabling on other distributions</title> + <programlisting> +static void +gs_plugin_sample_init (GsPluginSample *self) +{ + GsPlugin *plugin = GS_PLUGIN (self); + + if (!gs_plugin_check_distro_id (plugin, "ubuntu")) { + gs_plugin_set_enabled (plugin, FALSE); + return; + } + /* set up private data etc. */ +} + </programlisting> + </example> + + </section> + + <section> + <title>Custom Applications in the Installed List</title> + <para> + Next is returning custom applications in the installed list. + The use case here is a proprietary software distribution method that + installs custom files into your home directory, but you can use your + imagination for how this could be useful. + The example here is all hardcoded, and a true plugin would have to + derive the details about the GsApp, for example reading in an XML + file or YAML config file somewhere. + </para> + <example> + <title>Example showing a custom installed application</title> + <programlisting> +static void +gs_plugin_sample_init (GsPluginSample *self) +{ + GsPlugin *plugin = GS_PLUGIN (self); + + gs_plugin_add_rule (plugin, GS_PLUGIN_RULE_RUN_BEFORE, "icons"); +} + +static void +gs_plugin_custom_list_apps_async (GsPlugin *plugin, + GsAppQuery *query, + GsPluginListAppsFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autofree gchar *fn = NULL; + g_autoptr(GsApp) app = NULL; + g_autoptr(GIcon) icon = NULL; + g_autoptr(GsAppList) list = gs_app_list_new (); + g_autoptr(GTask) task = NULL; + + task = g_task_new (plugin, cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_custom_list_apps_async); + + /* We’re only listing installed apps in this example. */ + if (query == NULL || + gs_app_query_get_is_installed (query) != GS_APP_QUERY_TRISTATE_TRUE || + gs_app_query_get_n_properties_set (query) != 1) { + g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Unsupported query"); + return; + } + + /* check if the app exists */ + fn = g_build_filename (g_get_home_dir (), "chiron", NULL); + if (!g_file_test (fn, G_FILE_TEST_EXISTS)) { + g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); + return; + } + + /* the trigger exists, so create a fake app */ + app = gs_app_new ("chiron.desktop"); + gs_app_set_management_plugin (app, plugin); + gs_app_set_kind (app, AS_COMPONENT_KIND_DESKTOP_APP); + gs_app_set_state (app, GS_APP_STATE_INSTALLED); + gs_app_set_name (app, GS_APP_QUALITY_NORMAL, "Chiron"); + gs_app_set_summary (app, GS_APP_QUALITY_NORMAL, "A teaching application"); + gs_app_set_description (app, GS_APP_QUALITY_NORMAL, + "Chiron is the name of an application.\n\n" + "It can be used to demo some of our features"); + + /* these are all optional, but make details page looks better */ + gs_app_set_version (app, "1.2.3"); + gs_app_set_size_installed (app, GS_SIZE_TYPE_VALID, 2 * 1024 * 1024); + gs_app_set_size_download (app, GS_SIZE_TYPE_VALID, 3 * 1024 * 1024); + gs_app_set_origin_hostname (app, "http://www.teaching-example.org/"); + gs_app_add_category (app, "Game"); + gs_app_add_category (app, "ActionGame"); + gs_app_add_kudo (app, GS_APP_KUDO_INSTALLS_USER_DOCS); + gs_app_set_license (app, GS_APP_QUALITY_NORMAL, "GPL-2.0+ and LGPL-2.1+"); + + /* use a stock icon */ + icon = g_themed_icon_new ("input-gaming"); + gs_app_add_icon (app, icon); + + /* return new app */ + gs_app_list_add (list, app); + + g_task_return_pointer (task, g_steal_pointer (&list), g_object_unref); +} + +static GsAppList * +gs_plugin_custom_list_apps_finish (GsPlugin *plugin, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (result), error); +} + </programlisting> + </example> + <para> + This shows a lot of the plugin architecture in action. Some notable points: + </para> + <itemizedlist> + <listitem> + <para> + Setting the management plugin means we can check for this string + when working out if we can handle the install or remove action. + </para> + </listitem> + <listitem> + <para> + Most applications want a kind of <code>AS_COMPONENT_KIND_DESKTOP_APP</code> + to be visible as an application. + </para> + </listitem> + <listitem> + <para> + The origin is where the application originated from — usually + this will be something like <emphasis>Fedora Updates</emphasis>. + </para> + </listitem> + <listitem> + <para> + The <code>GS_APP_KUDO_INSTALLS_USER_DOCS</code> means we get the + blue "Documentation" award in the details page; there are many + kudos to award to deserving apps. + </para> + </listitem> + <listitem> + <para> + Setting the license means we don't get the non-free warning — + removing the 3rd party warning can be done using + <code>GS_APP_QUIRK_PROVENANCE</code> + </para> + </listitem> + <listitem> + <para> + The icon will be loaded into a pixbuf of the correct size when + needed by the UI. You must ensure that icons are available at + common sizes. For icons of type <code>GsRemoteIcon</code>, the + <code>icons</code> plugin will download and cache the icon + locally. + </para> + </listitem> + </itemizedlist> + <para> + To show this fake application just compile and install the plugin, + <code>touch ~/chiron</code> and then restart gnome-software. + To avoid restarting <filename>gnome-software</filename> each time a + proper plugin would create a <code>GFileMonitor</code> object to + monitor files. + </para> + + <mediaobject id="gs-example-installed"> + <imageobject> + <imagedata format="PNG" fileref="gs-example-installed.png" align="center"/> + </imageobject> + </mediaobject> + + <para> + By filling in the optional details (which can also be filled in using + <code>refine_async()</code> you can also make the details + page a much more exciting place. + Adding a set of screenshots is left as an exercise to the reader. + </para> + + <mediaobject id="gs-example-details"> + <imageobject> + <imagedata format="PNG" fileref="gs-example-details.png" align="center"/> + </imageobject> + </mediaobject> + + </section> + + <section> + <title>Downloading Metadata and Updates</title> + + <para> + The plugin loader supports a <code>refresh_metadata_async()</code> vfunc that + is called in various situations. + To ensure plugins have the minimum required metadata on disk it is + called at startup, but with a cache age of <emphasis>infinite</emphasis>. + This basically means the plugin must just ensure that + <emphasis role="strong">any</emphasis> data exists no matter what the age. + </para> + <para> + Usually once per hour, we'll call <code>refresh_metadata_async()</code> but + with the correct cache age set (typically a little over 24 hours) which + allows the plugin to download new metadata or payload files from remote + servers. + The <code>gs_utils_get_file_age()</code> utility helper can help you + work out the cache age of a file, or the plugin can handle it some other + way. + </para> + <para> + For the Flatpak plugin we just make sure the AppStream metadata exists + at startup, which allows us to show search results in the UI. + If the metadata did not exist (e.g. if the user had added a remote + using the command-line without gnome-software running) then we would + show a loading screen with a progress bar before showing the main UI. + On fast connections we should only show that for a couple of seconds, + but it's a good idea to try any avoid that if at all possible in the + plugin. + Once per day the <code>gs_plugin_get_updates()</code> method is called, + and then <code>gs_plugin_download_app()</code> may be called if the + user has configured automatic updates. + This is where the Flatpak plugin would download any ostree trees (but + not doing the deploy step) so that the applications can be updated live + in the details panel without having to wait for the download to complete. + In a similar way, the fwupd plugin downloads the tiny LVFS metadata with + <code>refresh_metadata_async()</code> and then downloads the large firmware + files themselves when <code>gs_plugin_download()</code> or + <code>gs_plugin_download_app()</code> is called. + </para> + <para> + Note, if the downloading fails it's okay to return <code>FALSE</code>; + the plugin loader continues to run all plugins and just logs an error + to the console. We'll be calling into <code>refresh_metadata_async()</code> + again in only another hour, so there's no need to bother the user. + For actions like <code>gs_plugin_app_install</code> we also do the same + thing, but we also save the error on the GsApp itself so that the UI is + free to handle that how it wants, for instance showing a GtkDialog + window for example. + </para> + <example> + <title>Refresh example</title> + <programlisting> +static void progress_cb (gsize bytes_downloaded, + gsize total_download_size, + gpointer user_data); +static void download_file_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data); + +static void +gs_plugin_example_refresh_metadata_async (GsPlugin *plugin, + guint64 cache_age_secs, + GsPluginRefreshMetadataFlags flags, + GCancellable *cancellable, + GError **error) +{ + const gchar *metadata_filename = "/var/cache/example/metadata.xml"; + const gchar *metadata_url = "https://www.example.com/new.xml"; + g_autoptr(GFile) file = g_file_new_for_path (metadata_filename); + g_autoptr(GTask) task = NULL; + g_autoptr(SoupSession) soup_session = NULL; + + task = g_task_new (plugin, cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_example_refresh_metadata_async); + + soup_session = gs_build_soup_session (); + + /* is the metadata missing or too old? */ + if (gs_utils_get_file_age (file) > cache_age_secs) { + gs_download_file_async (soup_session, + metadata_url, + file, + G_PRIORITY_LOW, + progress_cb, + plugin, + cancellable, + download_file_cb, + g_steal_pointer (&task)); + return; + } + + g_task_return_boolean (task, TRUE); +} + +static void +progress_cb (gsize bytes_downloaded, + gsize total_download_size, + gpointer user_data) +{ + g_debug ("Downloaded %zu of %zu bytes", bytes_downloaded, total_download_size); +} + +static void +download_file_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GsPlugin *plugin = GS_PLUGIN (source_object); + g_autoptr(GTask) task = g_steal_pointer (&user_data); + + if (!gs_download_file_finish (result, &local_error)) { + g_task_return_error (task, g_steal_pointer (&local_error)); + } else { + g_debug ("successfully downloaded new metadata"); + g_task_return_boolean (task, TRUE); + } +} + +static gboolean +gs_plugin_example_refresh_metadata_finish (GsPlugin *plugin, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + </programlisting> + </example> + </section> + + <section> + <title>Adding Application Information Using Refine</title> + + <para> + As previous examples have shown it's very easy to add a new + application to the search results, updates list or installed list. + Some plugins don't want to add more applications, but want to modify + existing applications to add more information depending on what is + required by the UI code. + The reason we don't just add everything at once is that for + search-as-you-type to work effectively we need to return results in + less than about 50ms and querying some data can take a long time. + For example, it might take a few hundred ms to work out the download + size for an application when a plugin has to also look at what + dependencies are already installed. + We only need this information once the user has clicked the search + results and when the user is in the details panel, so we can save a + ton of time not working out properties that are not useful. + </para> + <example> + <title>Refine example</title> + <programlisting> +static void +gs_plugin_example_refine_async (GsPlugin *plugin, + GsAppList *list, + GsPluginRefineFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + task = g_task_new (plugin, cancellable, callback, user_data); + g_task_set_source_tag (task, gs_plugin_example_refine_async); + + /* not required */ + if ((flags & GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENSE) == 0) { + g_task_return_boolean (task, TRUE); + return; + } + + for (guint i = 0; i < gs_app_list_length (list); i++) { + GsApp *app = gs_app_list_index (list, i); + + /* already set */ + if (gs_app_get_license (app) != NULL) { + g_task_return_boolean (task, TRUE); + return; + } + + /* FIXME, not just hardcoded! */ + if (g_strcmp0 (gs_app_get_id (app, "chiron.desktop") == 0)) + gs_app_set_license (app, "GPL-2.0 and LGPL-2.0+"); + } + + g_task_return_boolean (task, TRUE); +} + +static gboolean +gs_plugin_example_refine_finish (GsPlugin *plugin, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + </programlisting> + </example> + <para> + This is a simple example, but shows what a plugin needs to do. + It first checks if the action is required, in this case + <code>GS_PLUGIN_REFINE_FLAGS_REQUIRE_LICENSE</code>. + This request is more common than you might expect as even the search + results shows a non-free label if the license is unspecified or + non-free. + It then checks if the license is already set, returning with success + if so. + If not, it checks the application ID and hardcodes a license; in the + real world this would be querying a database or parsing an additional + config file. + As mentioned before, if the license value is freely available without + any extra work then it's best just to set this at the same time as + when adding the app with <code>gs_app_list_add()</code>. + Think of refine as <emphasis>adding things that cost time to + calculate only when really required</emphasis>. + </para> + <para> + The UI in gnome-software is quite forgiving for missing data, hiding + sections or labels as required. + Some things are required however, and forgetting to assign an icon or + short description will get the application vetoed so that it's not + displayed at all. + Helpfully, running <code>gnome-software --verbose</code> on the + command line will tell you why an application isn't shown along with + any extra data. + </para> + + </section> + + <section> + <title>Adopting AppStream Applications</title> + + <para> + There's a lot of flexibility in the gnome-software plugin structure; + a plugin can add custom applications and handle things like search and + icon loading in a totally custom way. + Most of the time you don't care about how search is implemented or how + icons are going to be loaded, and you can re-use a lot of the existing + code in the <code>appstream</code> plugin. + To do this you just save an AppStream-format XML file in either + <filename>/usr/share/swcatalog/xml/</filename>, + <filename>/var/cache/swcatalog/xml/</filename> or + <filename>~/.local/share/swcatalog/xml/</filename>. + GNOME Software will immediately notice any new files, or changes to + existing files as it has set up the various inotify watches. + </para> + <para> + This allows plugins to care a lot less about how applications are + going to be shown. + For example, the <code>flatpak</code> plugin downloads AppStream data + for configured remotes during <code>refresh_metadata_async()</code>. + </para> + <para> + The only extra step a plugin providing its own apps needs to do + is to implement the <code>gs_plugin_adopt_app()</code> function. + This is called when an application does not have a management plugin + set, and allows the plugin to <emphasis>claim</emphasis> the + application for itself so it can handle installation, removal and + updating. + </para> + <para> + Another good example is the <code>fwupd</code> that wants to handle + any firmware we've discovered in the AppStream XML. + This might be shipped by the vendor in a package using Satellite, + or downloaded from the LVFS. It wouldn't be kind to set a management + plugin explicitly in case XFCE or KDE want to handle this in a + different way. This adoption function in this case is trivial: + </para> + + <informalexample> + <programlisting> +void +gs_plugin_adopt_app (GsPlugin *plugin, GsApp *app) +{ + if (gs_app_get_kind (app) == AS_COMPONENT_KIND_FIRMWARE) + gs_app_set_management_plugin (app, plugin); +} + </programlisting> + </informalexample> + </section> + + <section> + <title>Using The Plugin Cache</title> + + <para> + GNOME Software used to provide a per-process plugin cache, + automatically de-duplicating applications and trying to be smarter + than the plugins themselves. + This involved merging applications created by different plugins and + really didn't work very well. + For versions 3.20 and later we moved to a per-plugin cache which + allows the plugin to control getting and adding applications to the + cache and invalidating it when it made sense. + This seems to work a lot better and is an order of magnitude less + complicated. + Plugins can trivially be ported to using the cache using something + like this: + </para> + <informalexample> + <programlisting> + /* create new object */ + id = gs_plugin_flatpak_build_id (inst, xref); +- app = gs_app_new (id); ++ app = gs_plugin_cache_lookup (plugin, id); ++ if (app == NULL) { ++ app = gs_app_new (id); ++ gs_plugin_cache_add (plugin, id, app); ++ } + </programlisting> + </informalexample> + <para> + Using the cache has two main benefits for plugins. + The first is that we avoid creating duplicate GsApp objects for the + same logical thing. + This means we can query the installed list, start installing an + application, then query it again before the install has finished. + The GsApp returned from the second <code>list_apps()</code> + request will be the same GObject, and thus all the signals connecting + up to the UI will still be correct. + This means we don't have to care about <emphasis>migrating</emphasis> + the UI widgets as the object changes and things like progress bars just + magically work. + </para> + <para> + The other benefit is more obvious. + If we know the application state from a previous request we don't have + to query a daemon or do another blocking library call to get it. + This does of course imply that the plugin is properly invalidating + the cache using <code>gs_plugin_cache_invalidate()</code> which it + should do whenever a change is detected. + Whether a plugin uses the cache for this reason is up to the plugin, + but if it does it is up to the plugin to make sure the cache doesn't + get out of sync. + </para> + </section> + + </partintro> + </reference> + + <reference id="api"> + <partintro> + <para> + This documentation is auto-generated. + If you see any issues, please file bugs. + </para> + </partintro> + <title>GNOME Software Plugin API</title> + <xi:include href="xml/gs-app.xml"/> + <xi:include href="xml/gs-app-collation.xml"/> + <xi:include href="xml/gs-app-list.xml"/> + <xi:include href="xml/gs-app-query.xml"/> + <xi:include href="xml/gs-appstream.xml"/> + <xi:include href="xml/gs-category.xml"/> + <xi:include href="xml/gs-category-manager.xml"/> + <xi:include href="xml/gs-debug.xml"/> + <xi:include href="xml/gs-desktop-data.xml"/> + <xi:include href="xml/gs-download-utils.xml"/> + <xi:include href="xml/gs-external-appstream-utils.xml"/> + <xi:include href="xml/gs-fedora-third-party.xml"/> + <xi:include href="xml/gs-icon.xml"/> + <xi:include href="xml/gs-ioprio.xml"/> + <xi:include href="xml/gs-key-colors.xml"/> + <xi:include href="xml/gs-metered.xml"/> + <xi:include href="xml/gs-odrs-provider.xml"/> + <xi:include href="xml/gs-os-release.xml"/> + <xi:include href="xml/gs-plugin.xml"/> + <xi:include href="xml/gs-plugin-event.xml"/> + <xi:include href="xml/gs-plugin-helpers.xml"/> + <xi:include href="xml/gs-plugin-job-list-apps.xml"/> + <xi:include href="xml/gs-plugin-job-list-categories.xml"/> + <xi:include href="xml/gs-plugin-job-list-distro-upgrades.xml"/> + <xi:include href="xml/gs-plugin-job-refine.xml"/> + <xi:include href="xml/gs-plugin-job-refresh-metadata.xml"/> + <xi:include href="xml/gs-plugin-job.xml"/> + <xi:include href="xml/gs-plugin-loader-sync.xml"/> + <xi:include href="xml/gs-plugin-loader.xml"/> + <xi:include href="xml/gs-plugin-types.xml"/> + <xi:include href="xml/gs-plugin-vfuncs.xml"/> + <xi:include href="xml/gs-remote-icon.xml"/> + <xi:include href="xml/gs-test.xml"/> + <xi:include href="xml/gs-worker-thread.xml"/> + <xi:include href="xml/gs-utils.xml"/> + </reference> + +</book> diff --git a/doc/api/gnome-software.types b/doc/api/gnome-software.types new file mode 100644 index 0000000..5f0a7b0 --- /dev/null +++ b/doc/api/gnome-software.types @@ -0,0 +1,6 @@ +gs_app_get_type +gs_app_list_get_type +gs_category_get_type +gs_os_release_get_type +gs_plugin_event_get_type +gs_plugin_get_type diff --git a/doc/api/gs-example-details.png b/doc/api/gs-example-details.png Binary files differnew file mode 100644 index 0000000..88c29e4 --- /dev/null +++ b/doc/api/gs-example-details.png diff --git a/doc/api/gs-example-installed.png b/doc/api/gs-example-installed.png Binary files differnew file mode 100644 index 0000000..0cd424a --- /dev/null +++ b/doc/api/gs-example-installed.png diff --git a/doc/api/gs-example-search.png b/doc/api/gs-example-search.png Binary files differnew file mode 100644 index 0000000..77611bd --- /dev/null +++ b/doc/api/gs-example-search.png diff --git a/doc/api/meson.build b/doc/api/meson.build new file mode 100644 index 0000000..2ccab7b --- /dev/null +++ b/doc/api/meson.build @@ -0,0 +1,11 @@ +gnome.gtkdoc( + 'gnome-software', + src_dir : join_paths(meson.project_source_root(), 'lib'), + main_xml : 'gnome-software-docs.xml', + html_assets : [ + 'gs-example-details.png', + 'gs-example-installed.png', + 'gs-example-search.png' + ], + install : true +) diff --git a/doc/app-developers.md b/doc/app-developers.md new file mode 100644 index 0000000..dad5da7 --- /dev/null +++ b/doc/app-developers.md @@ -0,0 +1,69 @@ +Tools in GNOME Software for application developers +================================================== + +GNOME Software is often where users will first come into contact with an +application which they might later install, so the impression the user gets of +that application is important. Application developers want to see how their +application will appear in GNOME Software, and to have some control over it. + +GNOME Software provides some tools to help with this. + +If there is a supported tool which is not in this document, please +[submit a merge request](https://gitlab.gnome.org/GNOME/gnome-software/-/merge_requests/new) +to document it. + +Metainfo/Appstream +------------------ + +The information about an application comes from its metainfo (or, as it was +previously known: appdata) file. Metainfo files for multiple applications are +concatenated into ‘appstream’ files. Almost all of the customisation which GNOME +Software provides for applications comes from information in metainfo or +appstream files. + +So the first thing to check for your application is that its metainfo file is +complete and valid. See the +[metainfo file specification](https://www.freedesktop.org/software/appstream/docs/). + +To validate your metainfo file, run +``` +appstreamcli validate /path/to/app.metainfo.xml +``` + +You can add this as a unit test to your application by adding the following to +the appropriate `meson.build` in your application: +``` +metainfo_file = files('com.example.MyApp.metainfo.xml') +appstreamcli = find_program('appstreamcli', required: false) +if appstreamcli.found() + test ( + 'Validate metainfo file', + appstreamcli, + args: ['validate', '--no-net', metainfo_file], + ) +endif +``` + +Context tiles +------------- + +The context tiles which are shown on an application’s details page in GNOME +Software are derived from the application’s metainfo. + +There’s more detailed information about them, and the information they are built +from, [on the GNOME Software wiki](https://gitlab.gnome.org/GNOME/gnome-software/-/wikis/Software-metadata). + +Previewing the details page for an application +---------------------------------------------- + +GNOME Software allows previewing how an application will appear, by loading its +metainfo file directly. This allows previewing in-progress changes to an +application without publishing it to a repository. + +Do this with the `--show-metainfo` argument: +``` +gnome-software --show-metainfo=/path/to/app.metainfo.xml,icon=/path/to/icon.png +``` + +This will show the application in the details page of GNOME Software, and will +also display it in the featured carousel on the overview page. diff --git a/doc/design.svg b/doc/design.svg new file mode 100644 index 0000000..4f9a181 --- /dev/null +++ b/doc/design.svg @@ -0,0 +1,448 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="744.09448819" + height="1052.3622047" + id="svg2" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="design.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.9899495" + inkscape:cx="290.74866" + inkscape:cy="1216.6706" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:window-width="1200" + inkscape:window-height="1881" + inkscape:window-x="1600" + inkscape:window-y="0" + inkscape:window-maximized="1" + showguides="true" + inkscape:guide-bbox="true"> + <inkscape:grid + type="xygrid" + id="grid2985" + empspacing="5" + visible="true" + enabled="true" + snapvisiblegridlinesonly="true" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <rect + style="opacity:0.97154475;fill:#c3cdee;fill-opacity:1;stroke:#000000;stroke-width:0.51179862;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect2987-5" + width="115.2915" + height="34.988201" + x="179.70259" + y="127.36808" + ry="1.8272164" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957" /> + <rect + style="opacity:0.97154475;fill:#cef1d7;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect2987-5-5-5-3" + width="110" + height="210" + x="75" + y="127.36218" + ry="4.6491089" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957" /> + <path + style="fill:#000000;fill-opacity:0.97695851;stroke:none" + d="m 295,145.69551 0,3.33334 65,0 0,3.33333 20,-5 -20,-5 0,3.33333 z" + id="path3757-0" + inkscape:connector-curvature="0" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="240" + y="152.36218" + id="text3801-0" + sodipodi:linespacing="125%" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957"><tspan + sodipodi:role="line" + id="tspan3803-8" + x="240" + y="152.36218" + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans">Plugins</tspan></text> + <rect + style="opacity:0.97154474999999996;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect3900" + width="75" + height="75" + x="380" + y="92.362183" + ry="11.155499" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="130" + y="152.36218" + id="text3801-0-7" + sodipodi:linespacing="125%" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957"><tspan + sodipodi:role="line" + id="tspan3803-8-6" + x="130" + y="152.36218" + style="font-size:18px;text-align:center;text-anchor:middle;-inkscape-font-specification:Liberation Sans;font-family:Liberation Sans;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">PackageKit</tspan></text> + <rect + style="opacity:0.97154475;fill:#cef1d7;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect2987-5-5-5" + width="110" + height="35" + x="75" + y="162.36218" + ry="0" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="130" + y="187.36218" + id="text3801-0-7-9" + sodipodi:linespacing="125%" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957"><tspan + sodipodi:role="line" + id="tspan3803-8-6-7" + x="130" + y="187.36218" + style="font-size:18px;text-align:center;text-anchor:middle;-inkscape-font-specification:Liberation Sans;font-family:Liberation Sans;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">AppStream</tspan></text> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="130" + y="222.36218" + id="text3801-0-7-4" + sodipodi:linespacing="125%" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957"><tspan + sodipodi:role="line" + id="tspan3803-8-6-3" + x="130" + y="222.36218" + style="font-size:18px;text-align:center;text-anchor:middle;-inkscape-font-specification:Liberation Sans;font-family:Liberation Sans;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">glick2</tspan></text> + <rect + style="opacity:0.97154475;fill:#cef1d7;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect2987-5-5-5-4" + width="110" + height="35" + x="75" + y="232.36218" + ry="0" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="130" + y="257.36218" + id="text3801-0-7-9-9" + sodipodi:linespacing="125%" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957"><tspan + sodipodi:role="line" + id="tspan3803-8-6-7-3" + x="130" + y="257.36218" + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans">epiphany</tspan></text> + <rect + style="opacity:0.97154475;fill:#cef1d7;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect2987-5-5-2" + width="110" + height="35" + x="75" + y="267.36218" + ry="0" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="130" + y="292.36218" + id="text3801-0-7-1" + sodipodi:linespacing="125%" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957"><tspan + sodipodi:role="line" + id="tspan3803-8-6-8" + x="130" + y="292.36218" + style="font-size:18px;text-align:center;text-anchor:middle;-inkscape-font-specification:Liberation Sans;font-family:Liberation Sans;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">listaller</tspan></text> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="130" + y="327.36218" + id="text3801-0-7-9-7" + sodipodi:linespacing="125%" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957"><tspan + sodipodi:role="line" + id="tspan3803-8-6-7-2" + x="130" + y="327.36218" + style="font-size:18px;text-align:center;text-anchor:middle;font-weight:normal;-inkscape-font-specification:Liberation Sans;font-family:Liberation Sans;font-style:normal;font-stretch:normal;font-variant:normal">ostree</tspan></text> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="190" + y="172.36218" + id="text3801-0-7-9-5" + sodipodi:linespacing="125%" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957"><tspan + sodipodi:role="line" + x="190" + y="172.36218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3913">list = Search(text)</tspan><tspan + sodipodi:role="line" + x="190" + y="186.11218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3951">list = AddInstalled()</tspan><tspan + sodipodi:role="line" + x="190" + y="199.86218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3917">list = AddUpdates()</tspan><tspan + sodipodi:role="line" + x="190" + y="213.61218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan4121">list = AddPopular()</tspan><tspan + sodipodi:role="line" + x="190" + y="227.36218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3947">bool = AppInstall(app)</tspan><tspan + sodipodi:role="line" + x="190" + y="241.11218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3928">bool = AppUpdate(app)</tspan><tspan + sodipodi:role="line" + x="190" + y="254.86218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3930">bool = AppRemove(app)</tspan><tspan + sodipodi:role="line" + x="190" + y="268.61218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3934">bool = Refine(app)</tspan><tspan + sodipodi:role="line" + x="190" + y="282.36218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3941">bool = SetRating(app)</tspan><tspan + sodipodi:role="line" + x="190" + y="296.11218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3943">list = AddCategories()</tspan><tspan + sodipodi:role="line" + x="190" + y="309.86218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan4050">:str name</tspan><tspan + sodipodi:role="line" + x="190" + y="323.61218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3044">:list deps</tspan><tspan + sodipodi:role="line" + x="190" + y="337.36218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3914">:bool enabled</tspan></text> + <rect + style="opacity:0.97154475;fill:#c3cdee;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect2987-5-6-0" + width="110" + height="35" + x="365" + y="197.36218" + ry="1.8278326" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957" /> + <rect + style="opacity:0.97154475;fill:#c3cdee;fill-opacity:1;stroke:#000000;stroke-width:0.5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" + id="rect2987-5-6" + width="110" + height="35" + x="360" + y="202.36218" + ry="1.8278326" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957" /> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="415" + y="225.36218" + id="text3801-0-3" + sodipodi:linespacing="125%" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957"><tspan + sodipodi:role="line" + id="tspan3803-8-7" + x="415" + y="225.36218" + style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:PakType Naqsh;-inkscape-font-specification:PakType Naqsh">Applications</tspan></text> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="360" + y="247.36218" + id="text3801-0-7-9-5-7" + sodipodi:linespacing="125%" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957"><tspan + sodipodi:role="line" + x="360" + y="247.36218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan4073">:str app-id (e.g. foo.desktop)</tspan><tspan + sodipodi:role="line" + x="360" + y="261.11218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan4046">:ptr source (e.g. pkgname)</tspan><tspan + sodipodi:role="line" + x="360" + y="274.86218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan4048">:str management_plugin</tspan><tspan + sodipodi:role="line" + x="360" + y="288.61218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan4052">:str title (localized)</tspan><tspan + sodipodi:role="line" + x="360" + y="302.36218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan4054">:str description (localized)</tspan><tspan + sodipodi:role="line" + x="360" + y="316.11218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan4056">:str screenshots</tspan><tspan + sodipodi:role="line" + x="360" + y="329.86218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan4058">:str icon_uri</tspan><tspan + sodipodi:role="line" + x="360" + y="343.61218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan4093">:int rating</tspan><tspan + sodipodi:role="line" + x="360" + y="357.36218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan4064">:int state (e.g. installed)</tspan><tspan + sodipodi:role="line" + x="360" + y="371.11218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3037">:int app-kind (e.g. codec)</tspan><tspan + sodipodi:role="line" + x="360" + y="384.86218" + style="font-size:11px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;text-anchor:start;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L" + id="tspan3959">:int install_date</tspan></text> + <path + style="fill:#000000;fill-opacity:0.97695851;stroke:none" + d="m 405,167.36218 -5,0 0.37902,15.83248 -3.33333,0 5,20 5,-20 -3.33333,0 z" + id="path3757-0-1" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccccc" + inkscape:export-filename="/home/hughsie/Code/gnome3/gnome-software/design.png" + inkscape:export-xdpi="120.03957" + inkscape:export-ydpi="120.03957" /> + <image + y="96.362183" + x="385" + id="image3134" + xlink:href="file:///home/hughsie/Code/gnome3/gnome-software/data/icons/256x256/gnome-software.png" + height="66" + width="66" /> + <flowRoot + xml:space="preserve" + id="flowRoot3137" + style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Abyssinica SIL;font-style:normal;font-weight:normal;font-size:8px;line-height:125%;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:Abyssinica SIL;font-stretch:normal;font-variant:normal"><flowRegion + id="flowRegion3139"><rect + id="rect3141" + width="118.69292" + height="96.469566" + x="349.00769" + y="375.0549" /></flowRegion><flowPara + id="flowPara3143"></flowPara></flowRoot> </g> +</svg> diff --git a/doc/kudos.md b/doc/kudos.md new file mode 100644 index 0000000..f43aa51 --- /dev/null +++ b/doc/kudos.md @@ -0,0 +1,70 @@ +Kudos used in Software +====================== + +As part of the AppStream generation process we explode various parts of the +distro binary package and try to build metadata by merging various sources +together, for example AppData, desktop files and icons. + +As part of this we also have access to the finished binary and libraries, and +so can also run tools on them to get a metric of awesomeness. So far, the +metrics of awesomeness (here-on known as "kudos") are: + + * `HiDpiIcon` — installs a 128x128 or larger application icon + * `HighContrast` — uses hicontrast or scalable icons for visually impaired users + * `ModernToolkit` — uses a modern toolkit like Gtk-3 or QT-5 + * `Notifications` — registers desktop notifications + * `SearchProvider` — provides a search provider for GNOME Shell or KDE Plasma + * `UserDocs` — provides user documentation + +These attempt to define how tightly the application is integrated with the +platform, which is usually a pretty good metric of awesomeness. Of course, +some applications like Blender are an island in terms of integration, but of +course are awesome. + +There are some other "run-time" kudos used as well. These are not encoded by +the builder as they require some user information or are too specific to +GNOME Software. These include: + + * `FeaturedRecommended` — One of the design team chose to feature this + * `HasKeywords` — there are keywords in the desktop file used for searching + * `HasScreenshots` — more than one screenshot is supplied + * `MyLanguage` — has a populated translation in my locale, or a locale fallback + * `PerfectScreenshots` — screenshots are perfectly sized, in 16:9 aspect + * `Popular` — lots of people have downloaded this (only available on Fedora) + * `RecentRelease` — there been an upstream release in the last year + +You can verify the kudos your application is getting by doing something like: + + killall gnome-software + gnome-software --verbose + +and then navigating to the details for an application you'll see on the console: + + id-kind: desktop + state: available + id: blender.desktop + kudo: recent-release + kudo: featured-recommended + kudo: has-screenshots + kudo: popular + +Manually Adding Kudos +--------------------- + +If the AppStream generator fails to detect a specific kudo then you can add them +manually in the AppData file and they will be used in the AppStream metadata. +To do this, just add something like: + + <kudos> + <kudo>ModernToolkit</kudo> + <kudo>UserDocs</kudo> + </kudos> + +Although, please bear in mind any application that is found cheating, i.e. +adding kudos artificially will have **all** the kudos manually removed +with a blocklist rule in the AppStream builder. + +If you are a vendor, or a system distributor and just want to increase the +number of kudos for your pet proprietary application that's essential to +business function, a good kudo to manually add would be `FeaturedRecommended`, +or `GnomeSoftware::popular`. diff --git a/doc/meson.build b/doc/meson.build new file mode 100644 index 0000000..1a1d9bf --- /dev/null +++ b/doc/meson.build @@ -0,0 +1,3 @@ +if get_option('gtk_doc') + subdir('api') +endif diff --git a/doc/privacy-policy.html b/doc/privacy-policy.html new file mode 100644 index 0000000..ad97cac --- /dev/null +++ b/doc/privacy-policy.html @@ -0,0 +1,31 @@ +<html> +<head> + <title>GNOME Software Privacy Policy</title> +</head> +<body> + <h1>Policy</h1> + <p> + GNOME Software can optionally use a ratings and reviews service offered by + your distribution. + If enabled, GNOME Software will notify a web service that a specific + application has been installed or removed. + This information about which software you use helps us provide other users + with more accurate recommendations and helps us to choose applications to + feature on the overview page. + </p> + <p> + When submitting ratings your IP address is sent along with the usage information. + This is exactly the same information submitted viewing this web page. + A one-way hash of this address is also stored in a database to prevent + duplicate entries being added by the same user, but this can not be used to + identify specific users or network addresses. + </p> + <p> + We will never share the list of applications or your IP address with third + parties other than the original distribution web-service used by GNOME Software. + </p> + <h1>History of Changes</h1> + <p> + Modified 2014-02-04 by Richard Hughes + </p> +</html> diff --git a/doc/update.sh b/doc/update.sh new file mode 100755 index 0000000..5abf679 --- /dev/null +++ b/doc/update.sh @@ -0,0 +1 @@ +scp api/html/* hughsient@people.freedesktop.org:~/public_html/gnome-software diff --git a/doc/use-cases.md b/doc/use-cases.md new file mode 100644 index 0000000..c60eb4c --- /dev/null +++ b/doc/use-cases.md @@ -0,0 +1,38 @@ +This document will evolve over time to indicate what goals and use cases +gnome-software targets at the moment. + +Primary goals +============= + + * Allow people to find apps by browsing or search: + - a specific app that they're looking for, or + - apps in a particular category, or with particular functionality that they require + * Allow people to effectively inspect and appraise apps before they install them (screenshots, descriptions, ratings, comments, metadata) + * Allow people to view which apps are installed and remove them + * Present a positive view of the app ecosystem + - Reinforce the sense that there are lots of high quality apps + - Encourage people to engage with that ecosystem, both as users and as contributors + - When browsing, present and promote the best apps that are available + - Facilitate accidental discovery of great apps + * Handle software updates. Make software updates as little work for users as possible. To include: apps, OS updates (PackageKit, eos, rpm-ostree), firmware + * Support multiple software repositories, defined by both the distributor and users. + - Show which repos are configured. Allow them to be added/removed. + - Handle cases where the same app can be installed from multiple sources. + +Secondary goals +=============== + + * OS upgrades + * Hardware driver installation + * Input method installation + * Respond to application queries for software (apps, codecs, languages) + * Offline and metered connections + * OS updates end of life + * App end of life + +Non-goals +========= + + * Not a package manager front-end + * Not all repos are equal + * Not all apps are equal diff --git a/doc/vendor-customisation.md b/doc/vendor-customisation.md new file mode 100644 index 0000000..8866910 --- /dev/null +++ b/doc/vendor-customisation.md @@ -0,0 +1,214 @@ +Vendor customisation of GNOME Software +====================================== + +GNOME Software is in an unusual position in a distribution, as it lies at the +interface of the GNOME project and the distribution’s packaging and release +infrastructure. GNOME Software is the user interface which a lot of users will +use to see updates and new releases from their distribution. Distributions +understandably want to be able to put some of their own branding on this +interface, both to publicise their distribution and to confer some level of +official authority on the updates being provided. + +For this reason, GNOME Software has a few ways which vendors can use to +customise its appearance. + +A variety of different customisations have been implemented in the past, some of +which have been removed and others are still present. This document aims to +document the ones which are still present and supported. This document is *not +necessarily complete*. It will be added to over time as different customisations +are refreshed and updated. + +If there is a supported customisation method which is not in this document, +please [submit a merge request](https://gitlab.gnome.org/GNOME/gnome-software/-/merge_requests/new) +to document it. + +Likewise, if your distribution would like to customise gnome-software in a way +which isn’t currently supported, please +[open a new issue](https://gitlab.gnome.org/GNOME/gnome-software/-/issues/new?issue%5Bmilestone_id%5D=) +to discuss it. We don’t guarantee to implement anything, and customisations are +limited to adding branding in specific areas. + +Principles +---------- + +The principles which guide vendor customisation features in GNOME Software are: + * Avoid requiring vendor specific code. + - Otherwise vendors have to maintain and test GNOME Software plugins, which + is a lot of work. + * Don’t use GSettings unless customisations really should be per-user. + - While GSettings overrides are convenient, they are designed for user + preferences, not packaging customisation. + * Don’t require downstream patching of GNOME Software, although configure-time + arguments are OK. + - Many distributions are derived from other ones and would not like to have + to maintain a packaging fork in order to make small customisations. + * Be mindful of release cadences. + - If customisations related to a new OS version were tied to the release + cycle of GNOME Software, a new GNOME Software packaging release would have + to be done by a distribution in advance of making their new OS release, + which is a burden. + - It’s easier to allow distributions to put customisations specific to a new + OS version into a separate package. + +Upgrade background image +------------------------ + +The background image which is shown when a new OS upgrade is available is +customisable in several ways. It’s displayed by the `GsUpgradeBanner` widget, +and shown on the updates page. + +If your distribution has a specific GNOME Software plugin providing its upgrade +information, that plugin can provide CSS for rendering the background. See the +`fedora-pkgdb-collections` plugin for an example of this. + +Otherwise, the background image is looked up from several well-known locations, +in order: + * `${DATADIR}/gnome-software/backgrounds/${os_id}-${version}.png` + * `${DATADIR}/gnome-software/backgrounds/${os_id}.png` + +`${DATADIR}` is the configured data directory (typically `/usr/share`). +`${os_id}` is the `ID=` value from `/etc/os-release`, and `${version}` is the +version string being upgraded to. + +Featured apps and Editor’s Choice +--------------------------------- + +There are several ways to promote and highlight specific applications in GNOME +Software. On the overview page, there’s a carousel of featured applications +(`featured_carousel`), and an “Editor’s Choice” section (`box_curated`). Both of +them highlight curated sets of applications. The same is true on each category +page: a carousel (`top_carousel`) and an “Editor’s Choice” section +(`featured_flow_box`) are present. + +Both pages also have a “New & Updated” section (`box_recent` or +`recently_updated_flow_box`) presented below “Editor’s Choice”. The applications +listed in the new and updated section are not curated: they are chosen as the +applications which have had a recent release, according to the +`component/releases/release[@timestamp]` attribute in their metainfo. +Technically these are the results of a `GsPlugin.list_apps_async()` query with +`GsAppQuery:released-since` set. + +Applications are included in any of the curated sets through having special +metadata in their metainfo. The required metadata is different for the different +sections: + * Carousel on the overview page: Applications are included if they have + `component/custom/value[@key='GnomeSoftware::FeatureTile]` or + `component/custom/value[@key='GnomeSoftware::FeatureTile-css]` set in their + metainfo. They are also required to have a high-resolution icon, and the set + of applications shown in the carousel is randomised and limited to (for + example) 5. Technically these are the results of a + `GsPlugin.list_apps_async()` query with `GsAppQuery:is-featured` set. + * “Editor’s Choice” on the overview page: Applications are included if they + have `component/kudos/kudo[text()='GnomeSoftware::popular']` set in their + metainfo. Technically these are the results of a `GsPlugin.list_apps_async()` + query with `GsAppQuery:is-curated` set. + * Carousel on the category page: Applications are included if they are in the + `Featured` subcategory of the displayed category. They are also required to + have a high-resolution icon, and the set of applications shown in the carousel + is randomised and limited to (for example) 5. + * “Editor’s Choice” on the category page: Applications are included if they + meet the requirements for being in the carousel, but weren’t chosen as part + of the randomisation process. + +Example: +```xml +<?xml version="1.0" encoding="UTF-8"?> +<components> + <component merge="append"> + <!-- The ID must always be present to allow merging --> + <id>org.gnome.Podcasts</id> + + <!-- Make the app a candidate for inclusion in the carousel on the + overview page (if it has a hi-res icon). --> + <custom> + <value key="GnomeSoftware::FeatureTile">True</value> + </custom> + + <!-- Include the app in the “Editor’s Choice” section on the overview page. --> + <kudos> + <kudo>GnomeSoftware::popular</kudo> + </kudos> + + <!-- Make the app a candidate for inclusion in the carousel or + “Editor’s Choice” section on category pages (if it has a hi-res icon). --> + <categories> + <!-- Note that, due to a bug (https://gitlab.gnome.org/GNOME/gnome-software/-/issues/1649), + currently all the other categories for the app must also be listed + here, as well as the additional ‘Featured’ category. --> + <category>AudioVideo</category> + <category>Player</category> + <!-- This category has been added: --> + <category>Featured</category> + </categories> + </component> + <!-- more components --> +</components> +``` + +There are several ways to modify the metainfo for applications so that they are +highlighted as required, all of which involve providing an additional appstream +file which sets the additional metainfo for those applications. + +The main approach is to ship an additional distro-specific appstream file in +`${DATADIR}/swcatalog/xml`, providing and updating it via normal distribution +channels. For example, by packaging it in its own package which is updated +regularly. + +For distributions which can’t do regular updates of individual files – such as +image-based distributions – GNOME Software can download distro-specific +appstream files from the internet. List them in the `external-appstream-urls` +GSetting in `/org/gnome/software`, typically via a distribution-provided +GSettings override. Each URL must be HTTPS, and must point to a valid appstream +file. GNOME Software must be configured with `-Dexternal_appstream=true` for +this to work. + +GNOME Software will periodically check and download any updates to these +files, and will cache them locally. Ensure the `If-Modified-Since` HTTP header +functions correctly on your server, or GNOME Software’s caching will be +ineffective. + +The `external-appstream-urls` mechanism may change in future. + +GNOME Software ships a default list of featured applications, chosen to match +the [GNOME Circle](https://circle.gnome.org/). See +`data/assets/org.gnome.Software.Featured.xml` for this list, and for an example +of the metainfo XML needed to feature or highlight applications. See +`data/assets/org.gnome.Software.Curated.xml` for a default hard-coded list of +curated high quality applications, which is displayed in the “Editor’s Choice” +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. |