From 776a73f7521f10ee7b65983caff3030c895cb4b4 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Thu, 7 Aug 2008 09:19:27 +0000 Subject: ** Fix for bug #535745 2008-08-07 Milan Crha ** Fix for bug #535745 * configure.in: Require and link calendar libs with libgdata and libgdata-google. * plugins/google-account-setup/google-source.c: (sanitize_user_mail), (construct_default_uri), (is_default_uri), (init_combo_values), (user_changed), (cal_combo_changed), (claim_error), (retrieve_list_clicked), (plugin_google): New widgets and functionality to retrieve list of subscribed calendars from the Google account and let user choose which one would be shown. Note: Requires newer EDS, at least revision 9294. svn path=/trunk/; revision=35921 --- ChangeLog | 7 + configure.in | 6 +- plugins/google-account-setup/ChangeLog | 11 + plugins/google-account-setup/google-source.c | 389 +++++++++++++++++++++++++-- 4 files changed, 394 insertions(+), 19 deletions(-) diff --git a/ChangeLog b/ChangeLog index b372f98a09..54402ed777 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2008-08-07 Milan Crha + + ** Part of fix for bug #535745 + + * configure.in: Require and link calendar libs with libgdata + and libgdata-google. + 2008-08-06 Michael Monreal ** Fix for bug #467115 diff --git a/configure.in b/configure.in index 72de14a960..a01bbc59e8 100644 --- a/configure.in +++ b/configure.in @@ -184,7 +184,9 @@ PKG_CHECK_MODULES(EVOLUTION_DATA_SERVER, libedataserver-$EDS_PACKAGE >= eds_minimum_version libedataserverui-$EDS_PACKAGE >= eds_minimum_version libegroupwise-$EDS_PACKAGE >= eds_minimum_version - libebackend-$EDS_PACKAGE >= eds_minimum_version]) + libebackend-$EDS_PACKAGE >= eds_minimum_version + libgdata-$EDS_PACKAGE >= eds_minimum_version + libgdata-google-$EDS_PACKAGE >= eds_minimum_version]) dnl ****************** @@ -1570,7 +1572,7 @@ EVO_SET_COMPILE_FLAGS(LIBSOUP, libsoup-2.4 >= 2.3.0) AC_SUBST(LIBSOUP_CFLAGS) AC_SUBST(LIBSOUP_LIBS) -EVO_SET_COMPILE_FLAGS(EVOLUTION_CALENDAR, libgnomeui-2.0 libbonoboui-2.0 libglade-2.0 gio-2.0 gconf-2.0 gobject-2.0 libgtkhtml-$GTKHTML_PACKAGE libebook-$EDS_PACKAGE libecal-$EDS_PACKAGE libedataserverui-$EDS_PACKAGE libebackend-$EDS_PACKAGE $HAL_REQUIREMENT $libnotify gtkhtml-editor) +EVO_SET_COMPILE_FLAGS(EVOLUTION_CALENDAR, libgnomeui-2.0 libbonoboui-2.0 libglade-2.0 gio-2.0 gconf-2.0 gobject-2.0 libgtkhtml-$GTKHTML_PACKAGE libebook-$EDS_PACKAGE libecal-$EDS_PACKAGE libedataserverui-$EDS_PACKAGE libebackend-$EDS_PACKAGE $HAL_REQUIREMENT $libnotify gtkhtml-editor libgdata-$EDS_PACKAGE libgdata-google-$EDS_PACKAGE) AC_SUBST(EVOLUTION_CALENDAR_CFLAGS) AC_SUBST(EVOLUTION_CALENDAR_LIBS) diff --git a/plugins/google-account-setup/ChangeLog b/plugins/google-account-setup/ChangeLog index 403bdd0333..037aa01a3c 100644 --- a/plugins/google-account-setup/ChangeLog +++ b/plugins/google-account-setup/ChangeLog @@ -1,3 +1,14 @@ +2008-08-07 Milan Crha + + ** Fix for bug #535745 + + * google-source.c: (sanitize_user_mail), (construct_default_uri), + (is_default_uri), (init_combo_values), (user_changed), + (cal_combo_changed), (claim_error), (retrieve_list_clicked), + (plugin_google): New widgets and functionality to retrieve list + of subscribed calendars from the Google account and let user choose + which one would be shown. + 2008-08-01 Matthew Barnes ** Fixes bug #544860 diff --git a/plugins/google-account-setup/google-source.c b/plugins/google-account-setup/google-source.c index 7e73b5b8ba..57727c6122 100644 --- a/plugins/google-account-setup/google-source.c +++ b/plugins/google-account-setup/google-source.c @@ -40,10 +40,18 @@ #include #include #include +#include +#include + +#include +#include +#include #include "google-contacts-source.h" #define CALENDAR_LOCATION "http://www.google.com/calendar/feeds/" +#define CALENDAR_DEFAULT_PATH "/private/full" +#define URL_GET_SUBSCRIBED_CALENDARS "http://www.google.com/calendar/feeds/default/allcalendars/full" #define d(x) @@ -58,6 +66,8 @@ GtkWidget * plugin_google (EPlugin *epl, /*****************************************************************************/ /* plugin intialization */ + + static void ensure_google_source_group () { @@ -174,6 +184,83 @@ is_email (const char *address) return TRUE; } +static char * +sanitize_user_mail (const char *user) +{ + if (!user) + return NULL; + + if (!is_email (user)) { + return g_strconcat (user, "%40gmail.com", NULL); + } else { + char *tmp = g_malloc0 (sizeof (char) * (1 + strlen (user) + 2)); + char *at = strchr (user, '@'); + + strncpy (tmp, user, at - user); + strcat (tmp, "%40"); + strcat (tmp, at + 1); + + return tmp; + } +} + +static char * +construct_default_uri (const char *username) +{ + char *user, *uri; + + user = sanitize_user_mail (username); + uri = g_strconcat (CALENDAR_LOCATION, user, CALENDAR_DEFAULT_PATH, NULL); + g_free (user); + + return uri; +} + +/* checks whether the given_uri is pointing to the default user's calendar or not */ +static gboolean +is_default_uri (const char *given_uri, const char *username) +{ + char *uri, *at; + int ats; + gboolean res; + + if (!given_uri) + return TRUE; + + uri = construct_default_uri (username); + + /* count number of '@' in given_uri to know how much memory will be required */ + ats = 0; + for (at = strchr (given_uri, '@'); at; at = strchr (at + 1, '@')) { + ats++; + } + + if (!ats) + res = g_ascii_strcasecmp (given_uri, uri) == 0; + else { + const char *last; + char *tmp = g_malloc0 (sizeof (char) * (1 + strlen (given_uri) + (2 * ats))); + + last = given_uri; + for (at = strchr (last, '@'); at; at = strchr (at + 1, '@')) { + strncat (tmp, last, at - last); + strcat (tmp, "%40"); + last = at + 1; + } + strcat (tmp, last); + + res = g_ascii_strcasecmp (tmp, uri) == 0; + + g_free (tmp); + } + + g_free (uri); + + return res; +} + +static void init_combo_values (GtkComboBox *combo, const char *deftitle, const char *defuri); + static void user_changed (GtkEntry *editable, ESource *source) { @@ -181,10 +268,8 @@ user_changed (GtkEntry *editable, ESource *source) char *uri; char *ruri; const char *user; - char *projection; uri = e_source_get_uri (source); - user = gtk_entry_get_text (GTK_ENTRY (editable)); if (uri == NULL) { g_free (uri); @@ -193,24 +278,27 @@ user_changed (GtkEntry *editable, ESource *source) euri = e_uri_new (uri); g_free (euri->user); + euri->user = NULL; - if (user != NULL) { - euri->user = g_strdup (user); - e_source_set_property (source, "auth", "1"); - } else { - e_source_set_property (source, "auth", NULL); - } - - projection = g_strdup ("/private/full"); + /* two reasons why set readonly to FALSE: + a) the e_source_set_relative_uri does nothing for readonly sources + b) we are going to set default uri, which should be always writeable */ + e_source_set_readonly (source, FALSE); - if (!is_email (user)) { - user = g_strconcat (user, "@gmail.com", NULL); - } + user = gtk_entry_get_text (GTK_ENTRY (editable)); + uri = construct_default_uri (user); + e_source_set_relative_uri (source, uri); + g_free (uri); - e_source_set_relative_uri (source, g_strconcat (CALENDAR_LOCATION, g_strdup(user), g_strdup (projection), NULL)); - e_source_set_property (source, "username", euri->user); + e_source_set_property (source, "username", gtk_entry_get_text (GTK_ENTRY (editable))); e_source_set_property (source, "protocol", "google"); e_source_set_property (source, "auth-domain", "google"); + e_source_set_property (source, "auth", (user && *user) ? "1" : NULL); + e_source_set_property (source, "googlename", NULL); + + /* we changed user, thus reset the chosen calendar combo too, because + other user means other calendars subscribed */ + init_combo_values (GTK_COMBO_BOX (g_object_get_data (G_OBJECT (editable), "CalendarCombo")), _("Default"), NULL); ruri = print_uri_noproto (euri); g_free (ruri); @@ -296,6 +384,229 @@ set_refresh_time (ESource *source, GtkWidget *spin, GtkWidget *option) gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), time); } +enum { + COL_COLOR = 0, /* GDK_TYPE_COLOR */ + COL_TITLE, /* G_TYPE_STRING */ + COL_URL_PATH, /* G_TYPE_STRING */ + COL_READ_ONLY, /* G_TYPE_BOOLEAN */ + NUM_COLUMNS +}; + +static void +init_combo_values (GtkComboBox *combo, const char *deftitle, const char *defuri) +{ + GtkTreeIter iter; + GtkListStore *store; + + if (!combo) + return; + + store = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + + gtk_list_store_clear (store); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + COL_COLOR, NULL, + COL_TITLE, deftitle, + COL_URL_PATH, defuri, + COL_READ_ONLY, FALSE, + -1); + + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); +} + +static void +cal_combo_changed (GtkComboBox *combo, ESource *source) +{ + GtkListStore *store; + GtkTreeIter iter; + + g_return_if_fail (combo != NULL); + g_return_if_fail (source != NULL); + + store = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + + if (gtk_combo_box_get_active_iter (combo, &iter)) { + char *uri = NULL, *title = NULL; + gboolean readonly = FALSE; + + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_TITLE, &title, COL_URL_PATH, &uri, COL_READ_ONLY, &readonly, -1); + + if (!uri) + uri = construct_default_uri (e_source_get_property (source, "username")); + + if (is_default_uri (uri, e_source_get_property (source, "username"))) { + /* do not store title when we use default uri */ + g_free (title); + title = NULL; + } + + /* first set readonly to FALSE, otherwise if TRUE, then e_source_set_readonly does nothing */ + e_source_set_readonly (source, FALSE); + e_source_set_relative_uri (source, uri); + e_source_set_readonly (source, readonly); + e_source_set_property (source, "googlename", title); + e_source_set_property (source, "protocol", "google"); + e_source_set_property (source, "auth-domain", "google"); + + g_free (title); + g_free (uri); + } +} + +static void +claim_error (GtkWindow *parent, const char *error) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + error); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +static void +retrieve_list_clicked (GtkButton *button, GtkComboBox *combo) +{ + ESource *source; + GDataGoogleService *service; + GDataFeed *feed; + char *password, *tmp; + const char *username; + GError *error = NULL; + GtkWindow *parent; + + g_return_if_fail (button != NULL); + g_return_if_fail (combo != NULL); + + parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))); + + source = g_object_get_data (G_OBJECT (button), "ESource"); + g_return_if_fail (source != NULL); + + username = e_source_get_property (source, "username"); + if (!username || !*username) { + claim_error (parent, _("Please enter user name first.")); + return; + } + + tmp = g_strdup_printf (_("Enter password for user %s to access list of subscribed calendars."), username); + password = e_passwords_ask_password (_("Enter password"), "Calendar", "", tmp, + E_PASSWORDS_REMEMBER_NEVER | E_PASSWORDS_REPROMPT | E_PASSWORDS_SECRET | E_PASSWORDS_DISABLE_REMEMBER, + NULL, parent); + g_free (tmp); + + if (!password) + return; + + service = gdata_google_service_new ("cl", "evolution-client-0.0.1"); + gdata_service_set_credentials (GDATA_SERVICE (service), username, password); + /* privacy... maybe... */ + memset (password, 0, strlen (password)); + g_free (password); + + feed = gdata_service_get_feed (GDATA_SERVICE (service), URL_GET_SUBSCRIBED_CALENDARS, &error); + + if (feed) { + GSList *l; + char *old_selected = NULL; + int idx, active = -1, default_idx = -1; + GtkListStore *store = GTK_LIST_STORE (gtk_combo_box_get_model (combo)); + GtkTreeIter iter; + + if (gtk_combo_box_get_active_iter (combo, &iter)) + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, COL_URL_PATH, &old_selected, -1); + + gtk_list_store_clear (store); + + for (l = gdata_feed_get_entries (feed), idx = 1; l != NULL; l = l->next) { + const char *uri, *title, *color, *access; + GSList *links; + GDataEntry *entry = (GDataEntry *) l->data; + + if (!entry || !GDATA_IS_ENTRY (entry)) + continue; + + /* skip hidden entries */ + if (gdata_entry_get_custom (entry, "hidden") && g_ascii_strcasecmp (gdata_entry_get_custom (entry, "hidden"), "true") == 0) + continue; + + uri = NULL; + for (links = gdata_entry_get_links (entry); links && !uri; links = links->next) { + GDataEntryLink *link = (GDataEntryLink *)links->data; + + if (!link || !link->href || !link->rel) + continue; + + if (g_ascii_strcasecmp (link->rel, "alternate") == 0) + uri = link->href; + } + + title = gdata_entry_get_title (entry); + color = gdata_entry_get_custom (entry, "color"); + access = gdata_entry_get_custom (entry, "accesslevel"); + + if (uri && title) { + GdkColor gdkcolor; + + if (old_selected && g_str_equal (old_selected, uri)) + active = idx; + + if (color) + gdk_color_parse (color, &gdkcolor); + + if (default_idx == -1 && is_default_uri (uri, username)) { + /* have the default uri always NULL and first in the combo */ + uri = NULL; + gtk_list_store_insert (store, &iter, 0); + default_idx = idx; + } else { + gtk_list_store_append (store, &iter); + } + + gtk_list_store_set (store, &iter, + COL_COLOR, color ? &gdkcolor : NULL, + COL_TITLE, title, + COL_URL_PATH, uri, + COL_READ_ONLY, access && !g_str_equal (access, "owner") && !g_str_equal (access, "contributor"), + -1); + idx++; + } + } + + if (default_idx == -1) { + /* Hey, why we didn't find the default uri? Did something go so wrong or what? */ + gtk_list_store_insert (store, &iter, 0); + gtk_list_store_set (store, &iter, + COL_COLOR, NULL, + COL_TITLE, _("Default"), + COL_URL_PATH, NULL, + COL_READ_ONLY, FALSE, + -1); + } + + gtk_combo_box_set_active (combo, active == -1 ? 0 : active); + + g_free (old_selected); + g_object_unref (feed); + } else { + tmp = g_strdup_printf (_("Cannot read data from Google server.\n%s"), (error && error->message) ? error->message : _("Unknown error.")); + claim_error (parent, tmp); + g_free (tmp); + + if (error) { + g_error_free (error); + error = NULL; + } + } + + g_object_unref (service); +} + GtkWidget * plugin_google (EPlugin *epl, EConfigHookItemFactoryData *data) @@ -310,11 +621,14 @@ plugin_google (EPlugin *epl, GtkWidget *luser; GtkWidget *user; GtkWidget *label; + GtkWidget *combo; char *uri; char *username; const char *ssl_prop; gboolean ssl_enabled; int row; + GtkCellRenderer *renderer; + GtkListStore *store; GtkWidget *option, *spin, *menu, *hbox; GtkWidget *times [4]; @@ -430,7 +744,48 @@ plugin_google (EPlugin *epl, g_free (uri); g_free (username); - return widget; -} + label = gtk_label_new_with_mnemonic (_("Cal_endar:")); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_widget_show (label); + gtk_table_attach (GTK_TABLE (parent), label, 0, 1, row + 4, row + 5, GTK_FILL | GTK_EXPAND, 0, 0, 0); + + store = gtk_list_store_new ( + NUM_COLUMNS, + GDK_TYPE_COLOR, /* COL_COLOR */ + G_TYPE_STRING, /* COL_TITLE */ + G_TYPE_STRING, /* COL_URL_PATH */ + G_TYPE_BOOLEAN); /* COL_READ_ONLY */ + + combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + + renderer = e_cell_renderer_color_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "color", COL_COLOR, NULL); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "text", COL_TITLE, NULL); + + init_combo_values (GTK_COMBO_BOX (combo), + e_source_get_property (source, "googlename") ? e_source_get_property (source, "googlename") : _("Default"), + e_source_get_property (source, "googlename") ? e_source_peek_relative_uri (source) : NULL); + g_signal_connect (combo, "changed", G_CALLBACK (cal_combo_changed), source); + g_object_set_data (G_OBJECT (user), "CalendarCombo", combo); + + hbox = gtk_hbox_new (FALSE, 6); + + gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0); + label = gtk_button_new_with_mnemonic (_("Retrieve _list")); + g_signal_connect (label, "clicked", G_CALLBACK (retrieve_list_clicked), combo); + g_object_set_data (G_OBJECT (label), "ESource", source); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + gtk_table_attach (GTK_TABLE (parent), hbox, 1, 2, row + 4, row + 5, GTK_FILL | GTK_EXPAND, 0, 0, 0); + + return widget; +} -- cgit v1.2.3