← Back to team overview

zeitgeist team mailing list archive

[Merge] lp:~ev/activity-log-manager/whoopsie into lp:activity-log-manager

 

Evan Dandrea has proposed merging lp:~ev/activity-log-manager/whoopsie into lp:activity-log-manager.

Requested reviews:
  Activity Log Manager (activity-log-manager)

For more details, see:
https://code.launchpad.net/~ev/activity-log-manager/whoopsie/+merge/93899

As discussed with Seif and the Canonical design team, we should merge the two existing privacy pages in GNOME Control Center into a single entity, under activity-log-manager.  Here is a branch to do just that.

Christian has provided the following mockup as guidance:

http://dl.dropbox.com/u/2935618/canonical/ss/privacy-7.png

The privacy page for the Ubuntu crash report settings, hereafter referred to as "diagnostics," provides a small DBus daemon for writing the root-owned /etc/default/whoopsie preferences file.

This also adds a dependency on PolicyKit.

I've only made one change to activity-log-manager itself, which exposes a new append_page method to allow adding the diagnostics page.

As stated previously in #ubuntu-release and #ubuntu-devel, this code already exists in another form in the archive (whoopsie-daisy), and it is the release team's belief that it therefore falls under UI Freeze and not Feature Freeze.  However, care will have to be taken to coordinate a release of whoopsie-daisy that does not have the aforementioned files.
-- 
https://code.launchpad.net/~ev/activity-log-manager/whoopsie/+merge/93899
Your team Activity Log Manager is requested to review the proposed merge of lp:~ev/activity-log-manager/whoopsie into lp:activity-log-manager.
=== modified file '.bzrignore'
--- .bzrignore	2012-02-15 19:55:47 +0000
+++ .bzrignore	2012-02-20 20:39:19 +0000
@@ -31,6 +31,7 @@
   src/applications-widget.c
   src/files-widget.c
   src/history-widget.c
+src/diagnostics/Makefile
 po/*.gmo
 src/*.lo
 po/stamp-it

=== modified file 'configure.ac'
--- configure.ac	2012-02-14 20:20:46 +0000
+++ configure.ac	2012-02-20 20:39:19 +0000
@@ -35,6 +35,7 @@
 PKG_CHECK_MODULES(GTK, gtk+-3.0, [HAVE_GTK=yes], [HAVE_GTK=no])
 PKG_CHECK_MODULES(GEE, gee-1.0, [HAVE_GEE=yes], [HAVE_GEE=no])
 PKG_CHECK_MODULES(GIO_UNIX, gio-unix-2.0, [HAVE_GIO_UNIX=yes], [HAVE_GIO_UNIX=no])
+PKG_CHECK_MODULES(POLKIT, polkit-gobject-1, [HAVE_POLKIT=yes], [HAVE_POLKIT=no])
 
 AC_ARG_WITH([extensiondir],
   [AS_HELP_STRING([--with-extensiondir],
@@ -71,5 +72,6 @@
 Makefile
 data/Makefile
 src/Makefile
+src/diagnostics/Makefile
 po/Makefile.in
 ])

=== modified file 'po/POTFILES.in'
--- po/POTFILES.in	2012-02-14 20:20:46 +0000
+++ po/POTFILES.in	2012-02-20 20:39:19 +0000
@@ -6,3 +6,4 @@
 src/applications-widget.vala
 src/files-widget.vala
 src/history-widget.vala
+src/diagnostics/whoopsie.ui

=== modified file 'src/Makefile.am'
--- src/Makefile.am	2012-02-08 14:19:18 +0000
+++ src/Makefile.am	2012-02-20 20:39:19 +0000
@@ -3,6 +3,8 @@
 ccpanel_LTLIBRARIES = libactivity-log-manager.la
 endif
 
+SUBDIRS = diagnostics
+
 VALAFLAGS = \
 	--pkg zeitgeist-1.0 \
 	--pkg gtk+-3.0 \
@@ -13,6 +15,8 @@
 
 bin_PROGRAMS = activity-log-manager
 
+gnomeccuidir = $(datadir)/gnome-control-center/ui/
+
 SHARED_CFLAGS = \
 	-Wall \
 	-g \
@@ -21,14 +25,15 @@
 	-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
 	-DPROGRAMNAME_LOCALEDIR=\"${PROGRAMNAME_LOCALEDIR}\" \
 	-DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
+    -DGNOMECC_UI_DIR=\""$(gnomeccuidir)"\" \
 	$(GTK_CFLAGS) $(ZEITGEIST_CFLAGS) $(GEE_CFLAGS) $(GIO_UNIX_CFLAGS)
 activity_log_manager_CFLAGS = \
 	$(SHARED_CFLAGS)
 libactivity_log_manager_la_CFLAGS = \
 	$(SHARED_CFLAGS) \
+    $(POLKIT_CFLAGS) \
 	$(CCPANEL_CFLAGS)
 
-
 SOURCES = \
 	activity-log-manager.vala \
 	history-widget.vala \
@@ -38,7 +43,9 @@
 activity_log_manager_SOURCES = \
 	$(SOURCES) alm.vala 
 libactivity_log_manager_la_SOURCES = \
-	$(SOURCES) alm-cc.c
+	$(SOURCES) alm-cc.c \
+    diagnostics-widget.c \
+    diagnostics/whoopsie-generated.c
 
 activity_log_manager_LDFLAGS = \
 	-Wl,--export-dynamic
@@ -53,6 +60,7 @@
 	$(SHARED_LIBS)
 libactivity_log_manager_la_LIBADD = \
 	$(SHARED_LIBS) \
+    $(POLKIT_LIBS) \
 	$(CCPANEL_LIBS)
 
 GENERATED_C_FILES = $(alm_SOURCES:.vala=.c)
@@ -62,7 +70,3 @@
 CLEANFILES = \
 	$(GENERATED_C_FILES) \
 	$(GENERATED_O_FILES)
-
-
-
-

=== modified file 'src/activity-log-manager.vala'
--- src/activity-log-manager.vala	2012-02-18 21:38:27 +0000
+++ src/activity-log-manager.vala	2012-02-20 20:39:19 +0000
@@ -71,6 +71,10 @@
 			this.show_all();
 		}
 
+        public void append_page (Gtk.Widget widget, string label) {
+			var app_label = new Gtk.Label(_(label));
+			notebook.append_page(widget, app_label);
+        }
 		public void on_incognito_toggled(bool status) {
 			this.logging_switch.set_active(!status);
 		}

=== modified file 'src/alm-cc.c'
--- src/alm-cc.c	2012-02-11 16:53:55 +0000
+++ src/alm-cc.c	2012-02-20 20:39:19 +0000
@@ -21,6 +21,8 @@
 #include "config.h"
 
 extern void* alm_activity_log_manager_new (void);
+extern void alm_activity_log_manager_append_page (void* alm, GtkWidget* widget, const gchar* label);
+extern void* whoopsie_daisy_preferences_new (void);
 
 #define ALM_TYPE_MAIN_WINDOW_PANEL alm_main_window_panel_get_type()
 
@@ -53,6 +55,8 @@
 alm_main_window_panel_init (AlmMainWindowPanel *self)
 {
   GtkWidget *widget = GTK_WIDGET (alm_activity_log_manager_new ());
+  GtkWidget *whoopsie = GTK_WIDGET (whoopsie_daisy_preferences_new ());
+  alm_activity_log_manager_append_page (widget, whoopsie, "Diagnostics");
   gtk_widget_show_all (widget);
   gtk_container_add (GTK_CONTAINER (self), widget);
 }

=== added directory 'src/diagnostics'
=== added file 'src/diagnostics-widget.c'
--- src/diagnostics-widget.c	1970-01-01 00:00:00 +0000
+++ src/diagnostics-widget.c	2012-02-20 20:39:19 +0000
@@ -0,0 +1,195 @@
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include <libgnome-control-center/cc-panel.h>
+#include <polkit/polkit.h>
+
+#include "diagnostics/whoopsie-generated.h"
+
+static WhoopsiePreferences* proxy = NULL;
+
+#define POL_PATH "com.ubuntu.whoopsiepreferences.change"
+#define PRIVACY_URL "http://www.ubuntu.com/aboutus/privacypolicy";
+
+#define WHOOPSIE_DAISY_TYPE_PREFERENCES whoopsie_daisy_preferences_get_type()
+#define WHOOPSIE_DAISY_PREFERENCES(obj) \
+    (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+    WHOOPSIE_DAISY_TYPE_PREFERENCES, WhoopsieDaisyPreferences))
+#define WHOOPSIE_DAISY_PREFERENCES_PRIVATE(o) \
+    (G_TYPE_INSTANCE_GET_PRIVATE ((o), WHOOPSIE_DAISY_TYPE_PREFERENCES, WhoopsieDaisyPreferencesPrivate))
+
+GType whoopsie_daisy_preferences_get_type (void) G_GNUC_CONST;
+
+typedef struct _WhoopsieDaisyPreferences WhoopsieDaisyPreferences;
+typedef struct _WhoopsieDaisyPreferencesClass WhoopsieDaisyPreferencesClass;
+typedef struct _WhoopsieDaisyPreferencesPrivate WhoopsieDaisyPreferencesPrivate;
+
+struct _WhoopsieDaisyPreferencesPrivate
+{
+    GtkBuilder* builder;
+    GPermission* permission;
+};
+
+struct _WhoopsieDaisyPreferences
+{
+    GtkBox parent;
+    WhoopsieDaisyPreferencesPrivate* priv;
+};
+
+struct _WhoopsieDaisyPreferencesClass
+{
+    GtkBoxClass parent_class;
+};
+
+G_DEFINE_TYPE (WhoopsieDaisyPreferences, whoopsie_daisy_preferences, GTK_TYPE_BOX)
+
+static void
+whoopsie_daisy_preferences_dispose (GObject* object)
+{
+    WhoopsieDaisyPreferencesPrivate* priv = WHOOPSIE_DAISY_PREFERENCES (object)->priv;
+
+    if (priv->builder) {
+        g_object_unref (priv->builder);
+        priv->builder = NULL;
+    }
+    if (priv->permission) {
+        g_object_unref (priv->permission);
+        priv->permission = NULL;
+    }
+}
+
+static void
+whoopsie_daisy_preferences_class_init (WhoopsieDaisyPreferencesClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    g_type_class_add_private (klass, sizeof (WhoopsieDaisyPreferencesPrivate));
+    object_class->dispose = whoopsie_daisy_preferences_dispose;
+}
+
+static void
+on_privacy_policy_clicked (GtkWidget* button, gpointer user_data)
+{
+    system ("xdg-open " PRIVACY_URL);
+}
+
+static void
+on_permission_changed (GPermission* permission, GParamSpec* pspec, gpointer data)
+{
+    gboolean allowed;
+    GtkWidget* error_reports_box = NULL;
+    WhoopsieDaisyPreferencesPrivate* priv = WHOOPSIE_DAISY_PREFERENCES (data)->priv;
+
+    error_reports_box = GTK_WIDGET (gtk_builder_get_object (
+                                    priv->builder, "error_reports_box"));
+
+    allowed = g_permission_get_allowed (permission);
+    gtk_widget_set_sensitive (error_reports_box, allowed);
+}
+
+static void
+on_submit_error_reports_checked (GtkToggleButton* button, gpointer user_data)
+{
+    GError* error = NULL;
+
+    whoopsie_preferences_call_set_report_crashes_sync (proxy,
+        gtk_toggle_button_get_active (button), NULL, &error);
+    if (error != NULL) {
+        g_printerr ("Error setting crash reporting: %s\n", error->message);
+        g_error_free (error);
+    }
+}
+
+static void
+on_properties_changed (WhoopsiePreferences* interface,
+                       GVariant* changed_properties,
+                       const gchar* const* invalidated_properties,
+                       gpointer user_data)
+{
+    WhoopsieDaisyPreferencesPrivate* priv = WHOOPSIE_DAISY_PREFERENCES (user_data)->priv;
+    gboolean report_errors;
+    GtkWidget* submit_error_reports = NULL;
+
+    submit_error_reports = GTK_WIDGET (
+        gtk_builder_get_object (priv->builder, "submit_error_reports"));
+    report_errors = whoopsie_preferences_get_report_crashes (interface);
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (submit_error_reports), report_errors);
+}
+
+static void
+whoopsie_daisy_preferences_setup_dbus (WhoopsieDaisyPreferences *self, GError *error)
+{
+    WhoopsieDaisyPreferencesPrivate* priv = WHOOPSIE_DAISY_PREFERENCES (self)->priv;
+    proxy = whoopsie_preferences_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                         G_DBUS_PROXY_FLAGS_NONE,
+                                         "com.ubuntu.WhoopsiePreferences",
+                                         "/com/ubuntu/WhoopsiePreferences",
+                                         NULL, &error);
+    if (!proxy)
+        return
+
+    g_signal_connect (proxy, "g-properties-changed",
+                                   G_CALLBACK (on_properties_changed), self);
+    on_properties_changed (proxy, NULL, NULL, self);
+}
+static void
+whoopsie_daisy_preferences_init (WhoopsieDaisyPreferences *self)
+{
+    GError *error = NULL;
+    GtkWidget* privacy_page = NULL;
+    GtkWidget* unlock_align = NULL;
+    GtkWidget* unlock_button = NULL;
+    GtkWidget* submit_error_reports = NULL;
+    GtkWidget* privacy_policy = NULL;
+    GtkBuilder* builder = NULL;
+    WhoopsieDaisyPreferencesPrivate* priv;
+    priv = self->priv = WHOOPSIE_DAISY_PREFERENCES_PRIVATE (self);
+    
+    priv->builder = gtk_builder_new ();
+    gtk_builder_add_from_file(priv->builder, GNOMECC_UI_DIR "/whoopsie.ui", &error);
+    if (error != NULL) {
+        g_warning ("Could not load interface file: %s", error->message);
+        g_error_free (error);
+        return;
+    }
+    submit_error_reports = GTK_WIDGET (
+        gtk_builder_get_object (priv->builder, "submit_error_reports"));
+    privacy_page = GTK_WIDGET (
+        gtk_builder_get_object (priv->builder, "privacy_page_box"));
+    unlock_align = GTK_WIDGET (
+        gtk_builder_get_object (priv->builder, "unlock_alignment"));
+    privacy_policy = GTK_WIDGET (
+        gtk_builder_get_object (priv->builder, "privacy_policy"));
+
+    gtk_widget_reparent (privacy_page, (GtkWidget *) self);
+    g_object_set (self, "valign", GTK_ALIGN_START, NULL);
+
+    priv->permission = polkit_permission_new_sync (POL_PATH, NULL, NULL, &error);
+    if (!priv->permission) {
+        g_warning ("Could not acquire permission: %s", error->message);
+        g_error_free (error);
+    }
+
+    unlock_button = gtk_lock_button_new (priv->permission);
+    gtk_container_add (GTK_CONTAINER (unlock_align), GTK_WIDGET (unlock_button));
+    gtk_widget_show (unlock_button);
+
+    g_signal_connect (priv->permission, "notify", G_CALLBACK (on_permission_changed), self);
+    on_permission_changed (priv->permission, NULL, self);
+
+    whoopsie_daisy_preferences_setup_dbus (self, error);
+    if (error) {
+        g_warning ("Could not set up DBus connection: %s", error->message);
+        g_error_free (error);
+    }
+
+    g_signal_connect (submit_error_reports, "toggled",
+                      G_CALLBACK (on_submit_error_reports_checked), NULL);
+    g_signal_connect (privacy_policy, "clicked",
+                      G_CALLBACK (on_privacy_policy_clicked), NULL);
+}
+
+GtkWidget*
+whoopsie_daisy_preferences_new (void)
+{
+    return g_object_new (WHOOPSIE_DAISY_TYPE_PREFERENCES, NULL);
+}
+

=== added file 'src/diagnostics/Makefile.am'
--- src/diagnostics/Makefile.am	1970-01-01 00:00:00 +0000
+++ src/diagnostics/Makefile.am	2012-02-20 20:39:19 +0000
@@ -0,0 +1,44 @@
+if HAVE_CCPANEL
+ccpaneldir = $(CCPANEL_DIR)
+bin_PROGRAMS = whoopsie-preferences
+endif
+
+policykitdir = $(datadir)/polkit-1/actions/
+dbussystemdir = $(sysconfdir)/dbus-1/system.d/
+dbusservicedir = $(datadir)/dbus-1/system-services/
+gnomeccuidir = $(datadir)/gnome-control-center/ui/
+
+whoopsie_preferences_CFLAGS = \
+	-Wall \
+	-g \
+	$(POLKIT_CFLAGS) $(GIO_UNIX_CFLAGS)
+
+whoopsie_preferences_SOURCES = \
+	whoopsie-generated.c whoopsie-generated.h whoopsie-preferences.c
+
+whoopsie_preferences_LDADD = \
+	$(POLKIT_LIBS) \
+	$(GIO_UNIX_LIBS)
+
+EXTRA_DIST = \
+	whoopsie-preferences.xml \
+	com.ubuntu.WhoopsiePreferences.service \
+	com.ubuntu.WhoopsiePreferences.conf \
+	com.ubuntu.whoopsiepreferences.policy \
+	whoopsie.ui
+
+gnomeccui_DATA = \
+	whoopsie.ui
+policykit_DATA = \
+	com.ubuntu.whoopsiepreferences.policy
+dbussystem_DATA = \
+	com.ubuntu.WhoopsiePreferences.conf
+dbusservice_DATA = \
+	com.ubuntu.WhoopsiePreferences.service
+
+whoopsie-generated.c: whoopsie-preferences.xml
+	gdbus-codegen --interface-prefix com.ubuntu. \
+		--generate-c-code whoopsie-generated \
+		$<
+
+CLEANFILES = whoopsie-generated.[ch]

=== added file 'src/diagnostics/com.ubuntu.WhoopsiePreferences.conf'
--- src/diagnostics/com.ubuntu.WhoopsiePreferences.conf	1970-01-01 00:00:00 +0000
+++ src/diagnostics/com.ubuntu.WhoopsiePreferences.conf	2012-02-20 20:39:19 +0000
@@ -0,0 +1,26 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd";>
+<busconfig>
+
+  <!-- This configuration file specifies the required security policies
+       for configuring the crash daemon. -->
+
+  <!-- Only root can own the service -->
+  <policy user="root">
+    <allow own="com.ubuntu.WhoopsiePreferences"/>
+  </policy>
+
+  <!-- Allow anyone to invoke methods (further constrained by
+       PolicyKit privileges -->
+  <policy context="default">
+    <allow send_destination="com.ubuntu.WhoopsiePreferences" 
+           send_interface="com.ubuntu.WhoopsiePreferences"/>
+    <allow send_destination="com.ubuntu.WhoopsiePreferences" 
+           send_interface="org.freedesktop.DBus.Introspectable"/>
+    <allow send_destination="com.ubuntu.WhoopsiePreferences" 
+           send_interface="org.freedesktop.DBus.Properties"/>
+  </policy>
+
+</busconfig>
+

=== added file 'src/diagnostics/com.ubuntu.WhoopsiePreferences.service'
--- src/diagnostics/com.ubuntu.WhoopsiePreferences.service	1970-01-01 00:00:00 +0000
+++ src/diagnostics/com.ubuntu.WhoopsiePreferences.service	2012-02-20 20:39:19 +0000
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=com.ubuntu.WhoopsiePreferences
+Exec=/usr/lib/whoopsie/whoopsie-preferences
+User=root

=== added file 'src/diagnostics/com.ubuntu.whoopsiepreferences.policy'
--- src/diagnostics/com.ubuntu.whoopsiepreferences.policy	1970-01-01 00:00:00 +0000
+++ src/diagnostics/com.ubuntu.whoopsiepreferences.policy	2012-02-20 20:39:19 +0000
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd";>
+<policyconfig>
+  <vendor>Ubuntu crash reporting</vendor>
+  <vendor_url>https://launchpad.net/whoopsie-daisy</vendor_url>
+  <icon_name>stock_lock</icon_name>
+  <action id="com.ubuntu.whoopsiepreferences.change">
+    <description gettext-domain="whoopsie">Privacy settings</description>
+    <message gettext-domain="whoopsie">To change your privacy settings you need to authenticate.</message>
+    <defaults>
+      <allow_any>auth_admin</allow_any>
+      <allow_inactive>auth_admin</allow_inactive>
+      <allow_active>auth_admin_keep</allow_active>
+    </defaults>
+ </action>
+</policyconfig>

=== added file 'src/diagnostics/whoopsie-preferences.c'
--- src/diagnostics/whoopsie-preferences.c	1970-01-01 00:00:00 +0000
+++ src/diagnostics/whoopsie-preferences.c	2012-02-20 20:39:19 +0000
@@ -0,0 +1,297 @@
+/* whoopsie
+ * 
+ * Copyright © 2011 Canonical Ltd.
+ * Author: Evan Dandrea <evan.dandrea@xxxxxxxxxxxxx>
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gio/gio.h>
+#include <stdlib.h>
+#include <polkit/polkit.h>
+
+#include "whoopsie-generated.h"
+
+#define CONFIG_PATH "/etc/default/whoopsie"
+
+static GMainLoop* loop = NULL;
+static guint loop_shutdown = 0;
+static GKeyFile* key_file = NULL;
+static PolkitAuthority* authority = NULL;
+
+/* Eventually, it might make sense to move to gsettings with the dconf
+ * backend, once it gains PolicyKit support, rather than have a program just
+ * to expose an interface to a small configuration file over DBus/PolicyKit.
+ * */
+
+/* Once upstart has an interface for disabiling jobs via initctl, we wont need
+ * a configuration file anymore */
+
+gboolean
+whoopsie_preferences_load_configuration (void)
+{
+    char* data;
+    gsize data_size;
+    gboolean ret = TRUE;
+    GError* error = NULL;
+
+    if (g_key_file_load_from_file (key_file, CONFIG_PATH,
+                                   G_KEY_FILE_KEEP_COMMENTS, NULL)) {
+        return TRUE;
+    }
+
+    g_key_file_set_boolean (key_file, "General", "report_crashes", TRUE);
+    data = g_key_file_to_data (key_file, &data_size, &error);
+    if (error) {
+        g_print ("Could not process configuration: %s\n", error->message);
+        ret = FALSE;
+        goto out;
+    }
+    if (!g_file_set_contents (CONFIG_PATH, data, data_size, &error)) {
+        g_print ("Could not write configuration: %s\n", error->message);
+        ret = FALSE;
+        goto out;
+    }
+
+    out:
+        if (data)
+            g_free (data);
+        if (error)
+            g_error_free (error);
+        return ret;
+}
+
+static void
+whoopsie_preferences_dbus_notify (WhoopsiePreferences* interface)
+{
+    GError* error = NULL;
+    gboolean report_crashes = FALSE;
+
+    report_crashes = g_key_file_get_boolean (key_file, "General",
+                                             "report_crashes", &error);
+    if (error) {
+        g_warning ("Could not get crashes: %s", error->message);
+        g_error_free (error);
+    }
+    whoopsie_preferences_set_report_crashes (interface, report_crashes);
+}
+
+static void
+whoopsie_preferences_file_changed (GFileMonitor *monitor, GFile *file,
+                                   GFile *other_file,
+                                   GFileMonitorEvent event_type,
+                                   gpointer user_data)
+{
+    if (event_type == G_FILE_MONITOR_EVENT_CHANGED) {
+        whoopsie_preferences_load_configuration ();
+        whoopsie_preferences_dbus_notify (user_data);
+    }
+}
+
+gboolean
+whoopsie_preferences_load (WhoopsiePreferences* interface)
+{
+    GError* error = NULL;
+    GFile* fp = NULL;
+    GFileMonitor* file_monitor = NULL;
+
+    fp = g_file_new_for_path (CONFIG_PATH);
+    file_monitor = g_file_monitor_file (fp, G_FILE_MONITOR_NONE,
+                                        NULL, &error);
+    g_signal_connect (file_monitor, "changed",
+                      G_CALLBACK (whoopsie_preferences_file_changed),
+                      interface);
+    if (error) {
+        g_print ("Could not set up file monitor: %s\n", error->message);
+        g_error_free (error);
+    }
+    g_object_unref (fp);
+    
+    key_file = g_key_file_new ();
+    whoopsie_preferences_load_configuration ();
+}
+
+gboolean
+whoopsie_preferences_changed (WhoopsiePreferences* object, GParamSpec* pspec,
+                              gpointer user_data)
+{
+    WhoopsiePreferencesIface* iface;
+    gboolean saved_value, new_value;
+    GError* error = NULL;
+    char* data;
+    gsize data_size;
+
+    if (loop_shutdown) {
+        g_source_remove (loop_shutdown);
+        loop_shutdown = 0;
+    }
+    loop_shutdown = g_timeout_add_seconds (60, (GSourceFunc) g_main_loop_quit,
+                                           loop);
+
+    iface = WHOOPSIE_PREFERENCES_GET_IFACE (object);
+    saved_value = g_key_file_get_boolean (key_file, "General", user_data,
+                                            &error);
+    if (error) {
+        g_print ("Could not process configuration: %s\n", error->message);
+        return FALSE;
+    }
+    new_value = iface->get_report_crashes (object);
+
+    if (saved_value != new_value) {
+        g_key_file_set_boolean (key_file, "General", user_data, new_value);
+        data = g_key_file_to_data (key_file, &data_size, &error);
+        if (error) {
+            g_print ("Could not process configuration: %s\n", error->message);
+            return FALSE;
+        }
+        if (!g_file_set_contents (CONFIG_PATH, data, data_size, &error)) {
+            g_print ("Could not write configuration: %s\n", error->message);
+            return FALSE;
+        }
+    }
+}
+
+static gboolean
+whoopsie_preferences_on_set_report_crashes (WhoopsiePreferences* object,
+                                            GDBusMethodInvocation* invocation,
+                                            gboolean report,
+                                            gpointer user_data)
+{
+    whoopsie_preferences_set_report_crashes (object, report);
+    whoopsie_preferences_complete_set_report_crashes (object, invocation);
+    return TRUE;
+}
+
+static gboolean
+whoopsie_preferences_authorize_method (GDBusInterfaceSkeleton* interface,
+                                       GDBusMethodInvocation* invocation,
+                                       gpointer user_data)
+{
+    PolkitSubject* subject = NULL;
+    PolkitAuthorizationResult* result = NULL;
+    GError* error = NULL;
+    const char* sender = NULL;
+    gboolean ret = FALSE;
+
+    sender = g_dbus_method_invocation_get_sender (invocation);
+    subject = polkit_system_bus_name_new (sender);
+    result = polkit_authority_check_authorization_sync (authority, subject,
+                                    "com.ubuntu.whoopsiepreferences.change",
+                                    NULL,
+                                    POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE,
+                                    NULL, &error);
+    if (result == NULL) {
+        g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                           G_DBUS_ERROR_AUTH_FAILED, "Could not authorize: %s",
+                           error->message);
+        g_error_free (error);
+        goto out;
+    }
+    if (!polkit_authorization_result_get_is_authorized (result)) {
+        g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+                           G_DBUS_ERROR_AUTH_FAILED, "Not authorized.");
+        goto out;
+    }
+    ret = TRUE;
+    out:
+        if (result != NULL)
+            g_object_unref (result);
+        g_object_unref (subject);
+        return ret;
+}
+
+static void
+on_bus_acquired (GDBusConnection* connection, const gchar* name,
+                 gpointer user_data)
+{
+    WhoopsiePreferences* interface;
+    GError* error = NULL;
+
+    interface = whoopsie_preferences_skeleton_new ();
+    if (!g_dbus_interface_skeleton_export (
+                G_DBUS_INTERFACE_SKELETON (interface),
+                connection,
+                "/com/ubuntu/WhoopsiePreferences", &error)) {
+
+        g_print ("Could not export path: %s\n", error->message);
+        g_error_free (error);
+        g_main_loop_quit (loop);
+        return;
+    }
+
+    authority = polkit_authority_get_sync (NULL, &error);
+    if (authority == NULL) {
+        g_print ("Could not get authority: %s\n", error->message);
+        g_error_free (error);
+        g_main_loop_quit (loop);
+        return;
+    }
+    loop_shutdown = g_timeout_add_seconds (60, (GSourceFunc) g_main_loop_quit,
+                                           loop);
+
+    g_signal_connect (interface, "notify::report-crashes",
+                      G_CALLBACK (whoopsie_preferences_changed),
+                      "report_crashes");
+    g_signal_connect (interface, "handle-set-report-crashes",
+                      G_CALLBACK (whoopsie_preferences_on_set_report_crashes),
+                      NULL);
+    g_signal_connect (interface, "g-authorize-method", G_CALLBACK
+                      (whoopsie_preferences_authorize_method), authority);
+
+    whoopsie_preferences_load (interface);
+    whoopsie_preferences_dbus_notify (interface);
+}
+
+static void
+on_name_acquired (GDBusConnection* connection, const gchar* name,
+                  gpointer user_data)
+{
+    g_print ("Acquired the name: %s\n", name);
+}
+
+static void
+on_name_lost (GDBusConnection* connection, const gchar* name,
+              gpointer user_data)
+{
+    g_print ("Lost the name: %s\n", name);
+}
+
+int
+main (int argc, char** argv)
+{
+    guint owner_id;
+
+    if (getuid () != 0) {
+        g_print ("This program must be run as root.\n");
+        return 1;
+    }
+
+    g_type_init();
+
+    owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
+                               "com.ubuntu.WhoopsiePreferences",
+                               G_BUS_NAME_OWNER_FLAGS_NONE,
+                               on_bus_acquired,
+                               on_name_acquired,
+                               on_name_lost,
+                               NULL, NULL);
+
+    loop = g_main_loop_new (NULL, FALSE);
+    g_main_loop_run (loop);
+    g_bus_unown_name (owner_id);
+    g_main_loop_unref (loop);
+    if (key_file)
+        g_key_file_free (key_file);
+    return 0;
+}

=== added file 'src/diagnostics/whoopsie-preferences.xml'
--- src/diagnostics/whoopsie-preferences.xml	1970-01-01 00:00:00 +0000
+++ src/diagnostics/whoopsie-preferences.xml	2012-02-20 20:39:19 +0000
@@ -0,0 +1,8 @@
+<node>
+ <interface name="com.ubuntu.WhoopsiePreferences">
+  <property name="ReportCrashes" type="b" access="read" />
+  <method name="SetReportCrashes">
+   <arg direction="in" type="b" name="report" />
+  </method>
+ </interface>
+</node>

=== added file 'src/diagnostics/whoopsie.ui'
--- src/diagnostics/whoopsie.ui	1970-01-01 00:00:00 +0000
+++ src/diagnostics/whoopsie.ui	2012-02-20 20:39:19 +0000
@@ -0,0 +1,165 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkWindow" id="window1">
+    <property name="can_focus">False</property>
+    <property name="has_resize_grip">False</property>
+    <child>
+      <object class="GtkBox" id="privacy_page_box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="border_width">12</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkBox" id="privacy_box">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkLabel" id="privacy_heading">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Ubuntu can collect anonymous information that helps developers improve it. All information collected is covered by our privacy policy.</property>
+                <property name="wrap">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkAlignment" id="privacy_policy_alignment">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="xscale">0</property>
+                <child>
+                  <object class="GtkButton" id="privacy_policy">
+                    <property name="label" translatable="yes">Privacy Policy</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="use_action_appearance">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="reports_box">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">12</property>
+            <child>
+              <object class="GtkBox" id="reports_heading_box">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkLabel" id="reports_heading">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes">People using this computer can:</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkAlignment" id="unlock_alignment">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">1</property>
+                    <property name="xscale">0</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="error_reports_box">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">3</property>
+                <child>
+                  <object class="GtkCheckButton" id="submit_error_reports">
+                    <property name="label" translatable="yes">Send error reports to Canonical</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="xalign">0</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="error_reports_description">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="xalign">0</property>
+                    <property name="xpad">24</property>
+                    <property name="label" translatable="yes">&lt;small&gt;Error reports include information about what a program was doing when it failed. You always have the choice to send or cancel an error report.&lt;/small&gt;</property>
+                    <property name="use_markup">True</property>
+                    <property name="wrap">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>


Follow ups