Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
privacy_consent.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel,
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#ifdef HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include "gui/privacy_consent.h"
24#include <glib/gi18n.h>
25#include <string.h>
26#include "gui/application.h"
27
28// The update check is always built, so the dialog always exists; crash reporting and
29// analytics add their lines to it when they are compiled in.
30
31#include "common/conf.h"
33
34// Non-confgen sentinel: its presence means the user has already been asked. The
35// per-feature toggles (sentry/enabled, telemetry/enabled) are confgen keys shown
36// in Preferences ▸ Storage ▸ Privacy, so they can be changed later.
37#define DT_PRIVACY_ASKED_KEY "privacy/consent_asked"
38
39// User-facing documentation describing exactly what is collected, where it goes
40// and why. Linked from the consent dialog so the decision is informed.
41#define DT_PRIVACY_DOC_URL "https://ansel.photos/en/doc/data-privacy/"
42
43void dt_privacy_ask_consent(const gboolean have_gui)
44{
45 // Already decided once: never ask again (toggles live in Preferences).
47
48 // Without a GUI we cannot prompt; leave the features at their defaults until the
49 // user is shown the dialog on a future GUI launch.
50 if(!have_gui) return;
51
52 // Nothing to ask a self-build or a distribution package: the two data flows are
53 // compiled out and the update check is a nightly-only thing.
54 const gboolean is_nightly = strcmp(DT_BUILD_CHANNEL, "nightly") == 0;
55#if !defined(HAVE_SENTRY) && !defined(HAVE_TELEMETRY)
56 if(!is_nightly)
57 {
59 return;
60 }
61#endif
62
63 GtkWidget *parent = (dt_gui_get_global() && dt_gui_get_ui()) ? dt_gui_main_window() : NULL;
64
65 GtkWidget *dialog = gtk_dialog_new_with_buttons(
66 _("Help us improve Ansel"), GTK_WINDOW(parent),
67 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
68 _("Confirm choices"), GTK_RESPONSE_ACCEPT, NULL);
69 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
70
71 GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
72 gtk_box_set_spacing(GTK_BOX(content), DT_PIXEL_APPLY_DPI(8));
73 gtk_container_set_border_width(GTK_CONTAINER(content), DT_PIXEL_APPLY_DPI(12));
74
75 GtkWidget *intro = gtk_label_new(
76 _("Ansel can share anonymous data with its developers to help fix bugs and "
77 "decide what to work on. This is entirely optional, separate for each purpose, "
78 "and can be changed any time in Preferences ▸ Storage ▸ Privacy.\n\n"
79 "We never send your images, file names or any personal data."));
80 gtk_label_set_line_wrap(GTK_LABEL(intro), TRUE);
81 gtk_label_set_xalign(GTK_LABEL(intro), 0.0);
82 gtk_label_set_max_width_chars(GTK_LABEL(intro), 64);
83 gtk_box_pack_start(GTK_BOX(content), intro, FALSE, FALSE, 0);
84
85#ifdef HAVE_SENTRY
86 GtkWidget *crash_check = gtk_check_button_new_with_label(
87 _("Send crash reports (backtrace, OS and hardware specs, app version)"));
88 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(crash_check), TRUE);
89 gtk_box_pack_start(GTK_BOX(content), crash_check, FALSE, FALSE, 0);
90#endif
91
92#ifdef HAVE_TELEMETRY
93 GtkWidget *usage_check = gtk_check_button_new_with_label(
94 _("Share anonymous usage statistics (features used, file types, OS and hardware)"));
95 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(usage_check), TRUE);
96 gtk_box_pack_start(GTK_BOX(content), usage_check, FALSE, FALSE, 0);
97#endif
98
99 // Not a data flow -- an HTTP GET of a static file, once a day -- but it reaches a
100 // server, so it is a choice the user makes here rather than one made for them.
101 GtkWidget *updates_check = NULL;
102 if(is_nightly)
103 {
104 updates_check = gtk_check_button_new_with_label(
105 _("Check for newer nightly builds at startup (one small download from ansel.photos, once a day)"));
106 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(updates_check), dt_conf_get_bool("updates/enabled"));
107 gtk_box_pack_start(GTK_BOX(content), updates_check, FALSE, FALSE, 0);
108 }
109
110 GtkWidget *link = gtk_link_button_new_with_label(
111 DT_PRIVACY_DOC_URL, _("Read what is collected, where it goes and why"));
112 gtk_widget_set_halign(link, GTK_ALIGN_START);
113 gtk_box_pack_start(GTK_BOX(content), link, FALSE, FALSE, 0);
114
115 gtk_widget_show_all(dialog);
116 gtk_dialog_run(GTK_DIALOG(dialog));
117
118 // Whatever the close action, honor the current checkbox state (both default to
119 // on). Features not built in stay disabled.
120#ifdef HAVE_SENTRY
121 dt_conf_set_bool("sentry/enabled", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(crash_check)));
122#endif
123#ifdef HAVE_TELEMETRY
124 dt_conf_set_bool("telemetry/enabled", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(usage_check)));
125#endif
126 if(updates_check)
127 dt_conf_set_bool("updates/enabled", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(updates_check)));
128
129 gtk_widget_destroy(dialog);
130
131 // Record that the user has decided so the dialog never shows again.
133}
134
135// clang-format off
136// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
137// vim: shiftwidth=2 expandtab tabstop=2 cindent
138// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
139// clang-format on
GtkWidget * dt_gui_main_window(void)
struct dt_ui_t * dt_gui_get_ui(void)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
int dt_conf_key_exists(const char *key)
Does key have a value?
#define DT_BUILD_CHANNEL
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:523
#define DT_PIXEL_APPLY_DPI(value)