summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Nocera <hadess@hadess.net>2022-07-20 17:47:19 +0200
committerRay Strode <rstrode@redhat.com>2022-07-26 12:52:24 -0400
commit881e0ea74414d0b5442400e339bd23abf3313e4e (patch)
treef84cd92b3cfee8bb14bf45b9812f99400207ae08
parent7ba53f819e01e018eefdedef1a5ba4cc9d263e21 (diff)
downloadaccountsservice-881e0ea74414d0b5442400e339bd23abf3313e4e.tar.gz
user: Add "Languages" user property
Languages is a property that can be used by desktops to declare what languages other than the main UI language they would want to use, such as fallback languages for missing translations, preferred languages in subtitles, installed dictionaries, etc. See https://gitlab.gnome.org/GNOME/gnome-control-center/-/issues/1969
-rw-r--r--data/org.freedesktop.Accounts.User.xml51
-rw-r--r--doc/libaccountsservice/libaccountsservice-sections.txt1
-rw-r--r--src/libaccountsservice/act-user.c56
-rw-r--r--src/libaccountsservice/act-user.h8
-rw-r--r--src/user.c90
5 files changed, 200 insertions, 6 deletions
diff --git a/data/org.freedesktop.Accounts.User.xml b/data/org.freedesktop.Accounts.User.xml
index 9f2a37a..d54ba44 100644
--- a/data/org.freedesktop.Accounts.User.xml
+++ b/data/org.freedesktop.Accounts.User.xml
@@ -150,6 +150,47 @@
</doc:doc>
</method>
+ <method name="SetLanguages">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+ <arg name="languages" direction="in" type="as">
+ <doc:doc>
+ <doc:summary>
+ The user's preferred languages, as an array of locale specification like "de_DE.UTF-8".
+ </doc:summary>
+ </doc:doc>
+ </arg>
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ Sets the userʼs preferred languages. The first item in the list will
+ be used to set the Language property.
+ </doc:para>
+ <doc:para>
+ The expectation is that package installers will use
+ this to know which languages the user is interested in, so as
+ to install extra data, like translations, dictionaries, etc.
+ </doc:para>
+ </doc:description>
+ <doc:permission>
+ The caller needs one of the following PolicyKit authorizations:
+ <doc:list>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term>
+ <doc:definition>To change their own preferred languages</doc:definition>
+ </doc:item>
+ <doc:item>
+ <doc:term>org.freedesktop.accounts.user-administration</doc:term>
+ <doc:definition>To change the preferred languages of another user</doc:definition>
+ </doc:item>
+ </doc:list>
+ </doc:permission>
+ <doc:errors>
+ <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+ <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error>
+ </doc:errors>
+ </doc:doc>
+ </method>
+
<method name="SetXSession">
<annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
<annotation name="org.freedesktop.DBus.GLib.CSymbol" value="user_set_x_session"/>
@@ -764,6 +805,16 @@
</doc:doc>
</property>
+ <property name="Languages" type="as" access="read">
+ <doc:doc>
+ <doc:description>
+ <doc:para>
+ The user's other preferred languages, as a locale specification like "de_DE.UTF-8".
+ </doc:para>
+ </doc:description>
+ </doc:doc>
+ </property>
+
<property name="Session" type="s" access="read">
<doc:doc>
<doc:description>
diff --git a/doc/libaccountsservice/libaccountsservice-sections.txt b/doc/libaccountsservice/libaccountsservice-sections.txt
index f55d9d2..da07fd2 100644
--- a/doc/libaccountsservice/libaccountsservice-sections.txt
+++ b/doc/libaccountsservice/libaccountsservice-sections.txt
@@ -41,6 +41,7 @@ act_user_set_automatic_login
act_user_set_email
act_user_set_icon_file
act_user_set_language
+act_user_set_languages
act_user_set_location
act_user_set_locked
act_user_set_password
diff --git a/src/libaccountsservice/act-user.c b/src/libaccountsservice/act-user.c
index 01dd210..484b15e 100644
--- a/src/libaccountsservice/act-user.c
+++ b/src/libaccountsservice/act-user.c
@@ -1018,6 +1018,27 @@ act_user_get_icon_file (ActUser *user)
}
/**
+ * act_user_get_languages:
+ * @user: a #ActUser
+ *
+ * Returns the value of #ActUser:languages.
+ *
+ * Returns: (transfer none) (nullable): the user’s preferred languages, or the
+ * empty string if they are using the system default language, or %NULL
+ * if there is no connection to the daemon
+ */
+const char * const *
+act_user_get_languages (ActUser *user)
+{
+ g_return_val_if_fail (ACT_IS_USER (user), NULL);
+
+ if (user->accounts_proxy == NULL)
+ return NULL;
+
+ return (const char **) accounts_user_get_languages (user->accounts_proxy);
+}
+
+/**
* act_user_get_language:
* @user: a #ActUser
*
@@ -1453,6 +1474,41 @@ act_user_set_language (ActUser *user,
}
/**
+ * act_user_set_languages:
+ * @user: the user object to alter.
+ * @languages: (array zero-terminated=1) (not nullable): an array of locale (for example, `en_US.utf8`), or
+ * the empty string to use the system default locale
+ *
+ * Assigns preferred languages for @user, setting #ActUser:languages, and
+ * overriding #ActUser:language with the first item in the list if there is one.
+ *
+ * Note this function is synchronous and ignores errors.
+ **/
+void
+act_user_set_languages (ActUser *user,
+ const char * const *languages)
+{
+ g_autoptr(GError) error = NULL;
+
+ g_return_if_fail (ACT_IS_USER (user));
+ g_return_if_fail (languages != NULL);
+ g_return_if_fail (ACCOUNTS_IS_USER (user->accounts_proxy));
+
+ if (!accounts_user_call_set_languages_sync (user->accounts_proxy,
+ languages,
+ G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION,
+ -1,
+ NULL,
+ &error)) {
+ g_autofree char *languages_s = NULL;
+
+ languages_s = g_strjoinv (":", (gchar **) languages);
+ g_warning ("SetLanguages for languages %s failed: %s", languages_s, error->message);
+ return;
+ }
+}
+
+/**
* act_user_set_x_session:
* @user: the user object to alter.
* @x_session: an x session (e.g. gnome)
diff --git a/src/libaccountsservice/act-user.h b/src/libaccountsservice/act-user.h
index 022db11..7ce41b1 100644
--- a/src/libaccountsservice/act-user.h
+++ b/src/libaccountsservice/act-user.h
@@ -69,6 +69,7 @@ gboolean act_user_is_local_account (ActUser *user);
gboolean act_user_is_nonexistent (ActUser *user);
const char *act_user_get_icon_file (ActUser *user);
const char *act_user_get_language (ActUser *user);
+const char * const *act_user_get_languages (ActUser *user);
const char *act_user_get_x_session (ActUser *user);
const char *act_user_get_session (ActUser *user);
const char *act_user_get_session_type (ActUser *user);
@@ -95,10 +96,13 @@ void act_user_set_password_expiration_policy (ActUser *user,
void act_user_set_user_expiration_policy (ActUser *user,
gint64 expiration_time);
+void act_user_set_language (ActUser *user,
+ const char *language);
+void act_user_set_languages (ActUser *user,
+ const char * const *languages);
+
void act_user_set_email (ActUser *user,
const char *email);
-void act_user_set_language (ActUser *user,
- const char *language);
void act_user_set_x_session (ActUser *user,
const char *x_session);
void act_user_set_session (ActUser *user,
diff --git a/src/user.c b/src/user.c
index 111dd3e..d480d1e 100644
--- a/src/user.c
+++ b/src/user.c
@@ -515,13 +515,22 @@ static void
user_update_from_keyfile (User *user,
GKeyFile *keyfile)
{
- gchar *s;
+ gchar *s, **sl;
s = g_key_file_get_string (keyfile, "User", "Language", NULL);
- if (s != NULL) {
+ sl = g_key_file_get_string_list (keyfile, "User", "Languages", NULL, NULL);
+ if (sl != NULL) {
+ accounts_user_set_languages (ACCOUNTS_USER (user), (const gchar *const *) sl);
+ accounts_user_set_language (ACCOUNTS_USER (user), sl[0]);
+ g_clear_pointer (&sl, g_strfreev);
+ } else if (s != NULL) {
+ const char *languages[] = { s, NULL };
if (!verify_locale (s)) {
g_warning ("Language '%s' set for user %s is invalid",
s, accounts_user_get_user_name (ACCOUNTS_USER (user)));
+ } else {
+ /* Don't migrate broken locales */
+ accounts_user_set_languages (ACCOUNTS_USER (user), languages);
}
accounts_user_set_language (ACCOUNTS_USER (user), s);
g_clear_pointer (&s, g_free);
@@ -626,8 +635,16 @@ user_save_to_keyfile (User *user,
if (accounts_user_get_email (ACCOUNTS_USER (user)))
g_key_file_set_string (keyfile, "User", "Email", accounts_user_get_email (ACCOUNTS_USER (user)));
- if (accounts_user_get_language (ACCOUNTS_USER (user)))
- g_key_file_set_string (keyfile, "User", "Language", accounts_user_get_language (ACCOUNTS_USER (user)));
+ if (accounts_user_get_languages (ACCOUNTS_USER (user))) {
+ const gchar * const* languages;
+ languages = accounts_user_get_languages (ACCOUNTS_USER (user));
+ g_key_file_set_string_list (keyfile, "User", "Languages", languages, g_strv_length ((char **) languages));
+ } else if (accounts_user_get_language (ACCOUNTS_USER (user))) {
+ const gchar *languages[] = { NULL, NULL };
+
+ languages[0] = accounts_user_get_language (ACCOUNTS_USER (user));
+ g_key_file_set_string_list (keyfile, "User", "Languages", languages, g_strv_length ((char **) languages));
+ }
if (accounts_user_get_session (ACCOUNTS_USER (user)))
g_key_file_set_string (keyfile, "User", "Session", accounts_user_get_session (ACCOUNTS_USER (user)));
@@ -1310,6 +1327,67 @@ user_set_email (AccountsUser *auser,
}
static void
+user_change_languages_authorized_cb (Daemon *daemon,
+ User *user,
+ GDBusMethodInvocation *context,
+ gpointer data)
+
+{
+ const gchar * const* languages = data;
+ guint i;
+
+ for (i = 0; languages[i] != NULL; i++) {
+ if (!verify_locale (languages[i])) {
+ g_dbus_method_invocation_return_error (context,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "Locale '%s' is not a valid XPG-formatted locale",
+ languages[i]);
+ return;
+ }
+ }
+
+ if (!g_strv_equal (accounts_user_get_languages (ACCOUNTS_USER (user)), languages)) {
+ accounts_user_set_language (ACCOUNTS_USER (user), languages[0]);
+ accounts_user_set_languages (ACCOUNTS_USER (user), languages);
+
+ save_extra_data (user);
+ }
+
+ accounts_user_complete_set_languages (ACCOUNTS_USER (user), context);
+}
+
+static gboolean
+user_set_languages (AccountsUser *auser,
+ GDBusMethodInvocation *context,
+ const gchar * const *languages)
+{
+ User *user = (User*)auser;
+ int uid;
+ const gchar *action_id;
+
+ if (!get_caller_uid (context, &uid)) {
+ throw_error (context, ERROR_FAILED, "identifying caller failed");
+ return TRUE;
+ }
+
+ if (accounts_user_get_uid (ACCOUNTS_USER (user)) == (uid_t) uid)
+ action_id = "org.freedesktop.accounts.change-own-user-data";
+ else
+ action_id = "org.freedesktop.accounts.user-administration";
+
+ daemon_local_check_auth (user->daemon,
+ user,
+ action_id,
+ user_change_languages_authorized_cb,
+ context,
+ g_strdupv ((gchar **) languages),
+ (GDestroyNotify)g_strfreev);
+
+ return TRUE;
+}
+
+static void
user_change_language_authorized_cb (Daemon *daemon,
User *user,
GDBusMethodInvocation *context,
@@ -1328,7 +1406,10 @@ user_change_language_authorized_cb (Daemon *daemon,
}
if (g_strcmp0 (accounts_user_get_language (ACCOUNTS_USER (user)), language) != 0) {
+ const gchar *languages[] = { language, NULL };
+
accounts_user_set_language (ACCOUNTS_USER (user), language);
+ accounts_user_set_languages (ACCOUNTS_USER (user), languages);
save_extra_data (user);
}
@@ -2592,6 +2673,7 @@ user_accounts_user_iface_init (AccountsUserIface *iface)
iface->handle_set_home_directory = user_set_home_directory;
iface->handle_set_icon_file = user_set_icon_file;
iface->handle_set_language = user_set_language;
+ iface->handle_set_languages = user_set_languages;
iface->handle_set_location = user_set_location;
iface->handle_set_locked = user_set_locked;
iface->handle_set_password = user_set_password;