[evolution] Add a WebDAV browser into the Account Editor
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution] Add a WebDAV browser into the Account Editor
- Date: Tue, 1 Aug 2017 15:14:31 +0000 (UTC)
commit ef833811ce7cc9f8839bfe22f105f20eaa14d6d7
Author: Milan Crha <mcrha redhat com>
Date: Tue Aug 1 17:14:08 2017 +0200
Add a WebDAV browser into the Account Editor
Users can create, edit or delete books, calendars or collections
on servers which support it.
po/POTFILES.in | 2 +
src/e-util/CMakeLists.txt | 2 +
src/e-util/e-system.error.xml | 12 +
src/e-util/e-util.h | 1 +
src/e-util/e-webdav-browser.c | 2915 ++++++++++++++++++++
src/e-util/e-webdav-browser.h | 83 +
src/modules/accounts-window/CMakeLists.txt | 2 +
src/modules/accounts-window/accounts-window.c | 2 +
.../accounts-window/e-webdav-browser-page.c | 236 ++
.../accounts-window/e-webdav-browser-page.h | 29 +
10 files changed, 3284 insertions(+), 0 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index da15e6d..b8d6340 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -285,6 +285,7 @@ src/e-util/e-tree.c
src/e-util/e-tree-selection-model.c
src/e-util/e-url-entry.c
src/e-util/evolution-source-viewer.c
+src/e-util/e-webdav-browser.c
src/e-util/e-web-view.c
src/e-util/e-widget-undo.c
src/e-util/filter.error.xml
@@ -383,6 +384,7 @@ src/mail/message-list.etspec
src/mail/searchtypes.xml.in
src/mail/vfoldertypes.xml.in
src/modules/accounts-window/e-accounts-window-editors.c
+src/modules/accounts-window/e-webdav-browser-page.c
src/modules/addressbook/autocompletion-config.c
src/modules/addressbook/eab-composer-util.c
src/modules/addressbook/e-book-shell-backend.c
diff --git a/src/e-util/CMakeLists.txt b/src/e-util/CMakeLists.txt
index 3b3740b..4b2b939 100644
--- a/src/e-util/CMakeLists.txt
+++ b/src/e-util/CMakeLists.txt
@@ -263,6 +263,7 @@ set(SOURCES
e-unicode.c
e-url-entry.c
e-util-private.h
+ e-webdav-browser.c
e-web-view-preview.c
e-web-view.c
e-widget-undo.c
@@ -529,6 +530,7 @@ set(HEADERS
e-unicode.h
e-url-entry.h
e-util-enums.h
+ e-webdav-browser.h
e-web-view-preview.h
e-web-view.h
e-widget-undo.h
diff --git a/src/e-util/e-system.error.xml b/src/e-util/e-system.error.xml
index bb88067..5e10494 100644
--- a/src/e-util/e-system.error.xml
+++ b/src/e-util/e-system.error.xml
@@ -1,5 +1,9 @@
<?xml version="1.0"?>
<error-list domain="system">
+ <error type="error" id="generic-error">
+ <primary>{0}</primary>
+ <secondary>{1}</secondary>
+ </error>
<error id="simple-info" type="info">
<secondary>{0}</secondary>
@@ -89,4 +93,12 @@
<_primary>Something has gone wrong</_primary>
<_secondary>A WebKitWebProcess crashed when displaying the content. You can try again by reopening the
window. If the issue persists, please file a bug report in the GNOME bugzilla.</_secondary>
</error>
+
+ <error id="prompt-delete-remote-collection" type="question" default="GTK_RESPONSE_CANCEL">
+ <_primary>Are you sure you want to delete remote collection “{0}”?</_primary>
+ <_secondary>This will permanently remove the collection “{0}” from the server. Are you sure you want to
proceed?</_secondary>
+ <button _label="Do _Not Delete" response="GTK_RESPONSE_CANCEL"/>
+ <button _label="_Delete From Server" response="GTK_RESPONSE_YES"/>
+ </error>
+
</error-list>
diff --git a/src/e-util/e-util.h b/src/e-util/e-util.h
index b448f5f..6d95cf3 100644
--- a/src/e-util/e-util.h
+++ b/src/e-util/e-util.h
@@ -258,6 +258,7 @@
#include <e-util/e-url-entry.h>
#include <e-util/e-util-enums.h>
#include <e-util/e-util-enumtypes.h>
+#include <e-util/e-webdav-browser.h>
#ifndef E_UTIL_INCLUDE_WITHOUT_WEBKIT
#include <e-util/e-web-view-preview.h>
#include <e-util/e-web-view.h>
diff --git a/src/e-util/e-webdav-browser.c b/src/e-util/e-webdav-browser.c
new file mode 100644
index 0000000..b990bdc
--- /dev/null
+++ b/src/e-util/e-webdav-browser.c
@@ -0,0 +1,2915 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION: e-webdav-browser
+ * @include: e-util/e-util.h
+ * @short_description: WebDAV server browser
+ *
+ * #EWebDAVBrowser allows to browse WebDAV servers and manage (create/edit/remove)
+ * collections there, like calendars and address books, if the server supports it.
+ **/
+
+#include "evolution-config.h"
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+#include <libedataserverui/libedataserverui.h>
+
+#include "e-activity.h"
+#include "e-activity-bar.h"
+#include "e-alert.h"
+#include "e-alert-bar.h"
+#include "e-alert-dialog.h"
+#include "e-alert-sink.h"
+#include "e-spell-text-view.h"
+
+#include "e-webdav-browser.h"
+
+struct _EWebDAVBrowserPrivate {
+ ECredentialsPrompter *credentials_prompter;
+
+ GMutex property_lock;
+ EWebDAVSession *session;
+ GCancellable *cancellable;
+
+ guint update_ui_id;
+ GSList *resources; /* ResourceData * */
+
+ GHashTable *href_to_reference; /* gchar *href ~> GtkTreeRowReference * */
+
+ GtkLabel *url_label;
+ GtkWidget *tree_view;
+ GtkWidget *create_book_button;
+ GtkWidget *create_calendar_button;
+ GtkWidget *create_collection_button;
+ GtkWidget *edit_button;
+ GtkWidget *delete_button;
+ /* GtkWidget *permissions_button; */
+ GtkWidget *refresh_button;
+
+ EAlertBar *alert_bar;
+ EActivityBar *activity_bar;
+
+ GtkWidget *create_edit_popover;
+ GtkWidget *create_edit_name_entry;
+ GtkWidget *create_edit_color_label;
+ GtkWidget *create_edit_color_chooser;
+ GtkWidget *create_edit_support_label;
+ GtkWidget *create_edit_event_check;
+ GtkWidget *create_edit_memo_check;
+ GtkWidget *create_edit_task_check;
+ GtkWidget *create_edit_description_label;
+ GtkWidget *create_edit_description_scrolled_window;
+ GtkWidget *create_edit_description_textview;
+ GtkWidget *create_edit_save_button;
+ GtkWidget *create_edit_hint_popover;
+ GtkWidget *create_edit_hint_label;
+};
+
+enum {
+ PROP_0,
+ PROP_CREDENTIALS_PROMPTER,
+ PROP_SOURCE
+};
+
+static void webdav_browser_alert_sink_init (EAlertSinkInterface *iface);
+static void webdav_browser_change_busy_state (EWebDAVBrowser *webdav_browser, gboolean is_busy);
+
+G_DEFINE_TYPE_WITH_CODE (EWebDAVBrowser, e_webdav_browser, GTK_TYPE_GRID,
+ G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK, webdav_browser_alert_sink_init))
+
+typedef enum {
+ E_EDITING_FLAG_NONE = 0,
+ E_EDITING_FLAG_IS_LOADING_ROW = 1 << 0,
+ E_EDITING_FLAG_HAS_OPTIONS = 1 << 1,
+ E_EDITING_FLAG_MKCOL = 1 << 2,
+ E_EDITING_FLAG_EXMKCOL = 1 << 3,
+ E_EDITING_FLAG_MKCALENDAR = 1 << 4,
+ E_EDITING_FLAG_CAN_BOOK = 1 << 5,
+ E_EDITING_FLAG_CAN_CALENDAR = 1 << 6,
+ E_EDITING_FLAG_CAN_ACL = 1 << 7,
+ E_EDITING_FLAG_CAN_DELETE = 1 << 8,
+ E_EDITING_FLAG_IS_BOOK = 1 << 9,
+ E_EDITING_FLAG_IS_CALENDAR = 1 << 10
+} EEditingFlags;
+
+enum {
+ COLUMN_STRING_DISPLAY_NAME = 0,
+ COLUMN_STRING_TYPE,
+ COLUMN_STRING_HREF,
+ COLUMN_STRING_DESCRIPTION,
+ COLUMN_STRING_ICON_NAME,
+ COLUMN_BOOL_ICON_VISIBLE,
+ COLUMN_RGBA_COLOR,
+ COLUMN_BOOL_COLOR_VISIBLE,
+ COLUMN_BOOL_CHILDREN_LOADED,
+ COLUMN_UINT_EDITING_FLAGS,
+ COLUMN_UINT_SUPPORTS,
+ N_COLUMNS
+};
+
+typedef struct _ResourceData {
+ guint32 editing_flags; /* bit-or of EEditingFlags */
+ EWebDAVResource *resource;
+} ResourceData;
+
+static void
+resource_data_free (gpointer ptr)
+{
+ ResourceData *rd = ptr;
+
+ if (rd) {
+ e_webdav_resource_free (rd->resource);
+ g_free (rd);
+ }
+}
+
+static gint
+resource_data_compare (gconstpointer aa,
+ gconstpointer bb)
+{
+ const ResourceData *rda = aa, *rdb = bb;
+
+ if (!rda || !rdb) {
+ if (rda == rdb)
+ return 0;
+ if (rda)
+ return -1;
+
+ return 1;
+ }
+
+ g_return_val_if_fail (rda->resource != NULL, 0);
+ g_return_val_if_fail (rdb->resource != NULL, 0);
+
+ return g_strcmp0 (rda->resource->href, rdb->resource->href);
+}
+
+static void
+webdav_browser_add_alert (EWebDAVBrowser *webdav_browser,
+ const gchar *primary_text,
+ const gchar *secondary_text)
+{
+ EAlert *alert;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (primary_text != NULL);
+
+ alert = e_alert_new ("system:general-error", primary_text, secondary_text ? secondary_text : "",
NULL);
+
+ e_alert_bar_add_alert (webdav_browser->priv->alert_bar, alert);
+
+ g_object_unref (alert);
+}
+
+typedef struct _LoginErrorsData {
+ EWebDAVBrowser *webdav_browser;
+ EWebDAVSession *session;
+ GCancellable *cancellable;
+ const GError *error;
+
+ gboolean run_trust_prompt;
+ gchar *certificate_pem;
+ GTlsCertificateFlags certificate_errors;
+
+ EFlag *flag;
+ gboolean repeat;
+} LoginErrorsData;
+
+static void
+webdav_browser_trust_prompt_done_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ETrustPromptResponse response = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+ ESource *source;
+ LoginErrorsData *led = user_data;
+
+ g_return_if_fail (E_IS_SOURCE (source_object));
+ g_return_if_fail (led != NULL);
+
+ source = E_SOURCE (source_object);
+ if (e_trust_prompt_run_for_source_finish (source, result, &response, NULL) && (
+ response == E_TRUST_PROMPT_RESPONSE_ACCEPT ||
+ response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY)) {
+ led->repeat = TRUE;
+ }
+
+ e_flag_set (led->flag);
+}
+
+static void
+webdav_browser_credentials_prompt_done_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ LoginErrorsData *led = user_data;
+ ENamedParameters *credentials = NULL;
+ ESource *source = NULL;
+
+ g_return_if_fail (led != NULL);
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (source_object));
+
+ if (e_credentials_prompter_prompt_finish (E_CREDENTIALS_PROMPTER (source_object), result, &source,
&credentials, NULL)) {
+ e_soup_session_set_credentials (E_SOUP_SESSION (led->session), credentials);
+ led->repeat = credentials != NULL;
+ }
+
+ e_named_parameters_free (credentials);
+
+ e_flag_set (led->flag);
+}
+
+static gboolean
+webdav_browser_manage_login_error_cb (gpointer user_data)
+{
+ LoginErrorsData *led = user_data;
+ ESource *source;
+
+ g_return_val_if_fail (led != NULL, FALSE);
+ g_return_val_if_fail (led->flag != NULL, FALSE);
+
+ source = e_soup_session_get_source (E_SOUP_SESSION (led->session));
+ if (!E_IS_SOURCE (source)) {
+ e_flag_set (led->flag);
+ return FALSE;
+ }
+
+ if (led->run_trust_prompt) {
+ GtkWindow *parent;
+ GtkWidget *widget;
+
+ widget = gtk_widget_get_toplevel (GTK_WIDGET (led->webdav_browser));
+ parent = widget ? GTK_WINDOW (widget) : NULL;
+
+ e_trust_prompt_run_for_source (parent, source, led->certificate_pem, led->certificate_errors,
+ NULL, FALSE, led->cancellable, webdav_browser_trust_prompt_done_cb, led);
+ } else {
+ ENamedParameters *credentials;
+
+ credentials = e_soup_session_dup_credentials (E_SOUP_SESSION (led->session));
+
+ e_credentials_prompter_prompt (led->webdav_browser->priv->credentials_prompter, source,
+ led->error ? led->error->message : NULL,
+ credentials ? E_CREDENTIALS_PROMPTER_PROMPT_FLAG_NONE:
+ E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_STORED_CREDENTIALS,
+ webdav_browser_credentials_prompt_done_cb, led);
+
+ e_named_parameters_free (credentials);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+webdav_browser_manage_login_errors (EWebDAVBrowser *webdav_browser,
+ EWebDAVSession *session,
+ GCancellable *cancellable,
+ const GError *error)
+{
+ LoginErrorsData led;
+
+ g_return_val_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser), FALSE);
+ g_return_val_if_fail (E_IS_WEBDAV_SESSION (session), FALSE);
+
+ led.webdav_browser = webdav_browser;
+ led.session = session;
+ led.cancellable = cancellable;
+ led.error = error;
+ led.run_trust_prompt = FALSE;
+ led.certificate_pem = NULL;
+ led.certificate_errors = 0;
+ led.flag = NULL;
+ led.repeat = FALSE;
+
+ if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) &&
+ e_soup_session_get_ssl_error_details (E_SOUP_SESSION (session), &led.certificate_pem,
&led.certificate_errors)) {
+ led.run_trust_prompt = TRUE;
+ led.flag = e_flag_new ();
+ } else if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+ led.flag = e_flag_new ();
+ }
+
+ if (led.flag) {
+ g_timeout_add (100, webdav_browser_manage_login_error_cb, &led);
+
+ e_flag_wait (led.flag);
+ e_flag_free (led.flag);
+ }
+
+ return led.repeat;
+}
+
+/* This has the property_lock already locked */
+static void
+webdav_browser_update_ui (EWebDAVBrowser *webdav_browser)
+{
+ GtkTreeModel *model;
+ GtkTreeStore *tree_store;
+ GSList *added_iters = NULL, *link;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (webdav_browser->priv->tree_view));
+ model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model));
+ tree_store = GTK_TREE_STORE (model);
+
+ webdav_browser->priv->resources = g_slist_sort (webdav_browser->priv->resources,
resource_data_compare);
+
+ for (link = webdav_browser->priv->resources; link; link = g_slist_next (link)) {
+ ResourceData *rd = link->data;
+ GtkTreeRowReference *reference;
+ GtkTreeIter parent_iter, iter, *piter;
+ GtkTreePath *path;
+ GdkRGBA rgba;
+ GString *type_info;
+ const gchar *icon_name = NULL;
+ gchar *parent_href, *ptr;
+ gboolean has_parent_iter = FALSE, has_color, is_loaded_row = FALSE, is_existing_row = FALSE;
+ gint len;
+
+ if (!rd || !rd->resource || !rd->resource->href)
+ continue;
+
+ parent_href = g_strdup (rd->resource->href);
+ len = strlen (parent_href);
+
+ if (len <= 0) {
+ g_free (parent_href);
+ continue;
+ }
+
+ parent_href[len - 1] = '\0';
+
+ ptr = strrchr (parent_href, '/');
+ if (!ptr) {
+ g_free (parent_href);
+ continue;
+ }
+
+ ptr[1] = '\0';
+
+ reference = g_hash_table_lookup (webdav_browser->priv->href_to_reference, parent_href);
+ if (reference) {
+ path = gtk_tree_row_reference_get_path (reference);
+ has_parent_iter = gtk_tree_model_get_iter (model, &parent_iter, path);
+ g_warn_if_fail (has_parent_iter);
+ gtk_tree_path_free (path);
+ }
+
+ reference = g_hash_table_lookup (webdav_browser->priv->href_to_reference, rd->resource->href);
+ if (reference) {
+ path = gtk_tree_row_reference_get_path (reference);
+ if (gtk_tree_model_get_iter (model, &iter, path)) {
+ is_existing_row = TRUE;
+ gtk_tree_model_get (model, &iter, COLUMN_BOOL_CHILDREN_LOADED,
&is_loaded_row, -1);
+ } else
+ gtk_tree_store_append (tree_store, &iter, has_parent_iter ? &parent_iter :
NULL);
+ gtk_tree_path_free (path);
+ } else {
+ gtk_tree_store_append (tree_store, &iter, has_parent_iter ? &parent_iter : NULL);
+ }
+
+ if (has_parent_iter) {
+ gboolean is_loaded = FALSE;
+
+ gtk_tree_model_get (model, &parent_iter, COLUMN_BOOL_CHILDREN_LOADED, &is_loaded, -1);
+
+ if (!is_loaded) {
+ GtkTreeIter child;
+
+ gtk_tree_store_set (tree_store, &parent_iter, COLUMN_BOOL_CHILDREN_LOADED,
TRUE, -1);
+
+ path = gtk_tree_model_get_path (model, &parent_iter);
+ if (path) {
+ gtk_tree_view_expand_row (GTK_TREE_VIEW
(webdav_browser->priv->tree_view), path, FALSE);
+ gtk_tree_path_free (path);
+ }
+
+ /* And remove "Loading…" row */
+ if (gtk_tree_model_iter_nth_child (model, &child, &parent_iter, 0)) {
+ do {
+ guint flags = E_EDITING_FLAG_NONE;
+
+ gtk_tree_model_get (model, &child, COLUMN_UINT_EDITING_FLAGS,
&flags, -1);
+
+ if (flags == E_EDITING_FLAG_IS_LOADING_ROW) {
+ gtk_tree_store_remove (tree_store, &child);
+ break;
+ }
+ } while (gtk_tree_model_iter_next (model, &child));
+ }
+ }
+
+ if (!(rd->editing_flags & E_EDITING_FLAG_HAS_OPTIONS)) {
+ guint parent_editing_flags = E_EDITING_FLAG_NONE;
+
+ gtk_tree_model_get (model, &parent_iter, COLUMN_UINT_EDITING_FLAGS,
&parent_editing_flags, -1);
+
+ rd->editing_flags = (parent_editing_flags & ~(E_EDITING_FLAG_IS_BOOK |
E_EDITING_FLAG_IS_CALENDAR)) |
+ (rd->editing_flags & (E_EDITING_FLAG_IS_BOOK |
E_EDITING_FLAG_IS_CALENDAR));
+ }
+ }
+
+ if (!is_existing_row) {
+ piter = g_new0 (GtkTreeIter, 1);
+ *piter = iter;
+ added_iters = g_slist_prepend (added_iters, piter);
+ }
+
+ path = gtk_tree_model_get_path (model, &iter);
+ reference = gtk_tree_row_reference_new (model, path);
+ gtk_tree_path_free (path);
+
+ g_hash_table_insert (webdav_browser->priv->href_to_reference, g_strdup (rd->resource->href),
reference);
+
+ type_info = g_string_new ("");
+
+ if (rd->resource->kind == E_WEBDAV_RESOURCE_KIND_ADDRESSBOOK) {
+ icon_name = "x-office-address-book";
+ g_string_append (type_info, _("Address book"));
+ } else if (rd->resource->kind == E_WEBDAV_RESOURCE_KIND_CALENDAR) {
+ icon_name = "x-office-calendar";
+
+ #define append_if_set(_flag, _str) \
+ if ((rd->resource->supports & (_flag)) != 0) { \
+ if (type_info->len) \
+ g_string_append (type_info, " "); \
+ g_string_append (type_info, _str); \
+ }
+
+ append_if_set (E_WEBDAV_RESOURCE_SUPPORTS_EVENTS, _("Events"));
+ append_if_set (E_WEBDAV_RESOURCE_SUPPORTS_MEMOS, _("Memos"));
+ append_if_set (E_WEBDAV_RESOURCE_SUPPORTS_TASKS, _("Tasks"));
+
+ #undef append_if_set
+
+ if (type_info->len) {
+ g_string_prepend (type_info, " (");
+ g_string_append (type_info, ")");
+ }
+
+ g_string_prepend (type_info, _("Calendar"));
+
+ } else if (rd->resource->kind == E_WEBDAV_RESOURCE_KIND_COLLECTION) {
+ icon_name = "folder";
+ g_string_append (type_info, _("Collection"));
+ }
+
+ has_color = rd->resource->color && gdk_rgba_parse (&rgba, rd->resource->color);
+ if (!has_color && rd->resource->color && strlen (rd->resource->color) == 9 &&
rd->resource->color[0] == '#') {
+ rd->resource->color[7] = '\0';
+ has_color = gdk_rgba_parse (&rgba, rd->resource->color);
+ }
+
+ gtk_tree_store_set (tree_store, &iter,
+ COLUMN_STRING_DISPLAY_NAME, rd->resource->display_name,
+ COLUMN_STRING_TYPE, type_info->str,
+ COLUMN_STRING_HREF, rd->resource->href,
+ COLUMN_STRING_DESCRIPTION, rd->resource->description,
+ COLUMN_STRING_ICON_NAME, icon_name,
+ COLUMN_BOOL_ICON_VISIBLE, icon_name != NULL,
+ COLUMN_RGBA_COLOR, has_color ? &rgba : NULL,
+ COLUMN_BOOL_COLOR_VISIBLE, has_color,
+ COLUMN_BOOL_CHILDREN_LOADED, is_loaded_row,
+ COLUMN_UINT_EDITING_FLAGS, rd->editing_flags,
+ COLUMN_UINT_SUPPORTS, rd->resource->supports,
+ -1);
+
+ g_string_free (type_info, TRUE);
+ g_free (parent_href);
+ }
+
+ g_slist_free_full (webdav_browser->priv->resources, resource_data_free);
+ webdav_browser->priv->resources = NULL;
+
+ for (link = added_iters; link; link = g_slist_next (link)) {
+ GtkTreeIter *piter = link->data;
+ gboolean is_loaded = TRUE;
+
+ gtk_tree_model_get (model, piter, COLUMN_BOOL_CHILDREN_LOADED, &is_loaded, -1);
+
+ if (!is_loaded) {
+ GtkTreeIter iter;
+
+ gtk_tree_store_append (tree_store, &iter, piter);
+ gtk_tree_store_set (tree_store, &iter,
+ COLUMN_STRING_DISPLAY_NAME, _("Loading…"),
+ COLUMN_UINT_EDITING_FLAGS, E_EDITING_FLAG_IS_LOADING_ROW,
+ -1);
+ }
+ }
+
+ g_slist_free_full (added_iters, g_free);
+}
+
+typedef void (* UpdateUICallback) (EWebDAVBrowser *webdav_browser, gpointer user_data);
+
+typedef struct _UpdateUIData {
+ GWeakRef *webdav_browser_weakref;
+ UpdateUICallback callback;
+ gpointer user_data;
+ GDestroyNotify free_user_data;
+} UpdateUIData;
+
+static void
+update_ui_data_free (gpointer ptr)
+{
+ UpdateUIData *uud = ptr;
+
+ if (uud) {
+ e_weak_ref_free (uud->webdav_browser_weakref);
+ if (uud->free_user_data)
+ uud->free_user_data (uud->user_data);
+ g_free (uud);
+ }
+}
+
+static gboolean
+webdav_browser_update_ui_timeout_cb (gpointer user_data)
+{
+ UpdateUIData *uud = user_data;
+ EWebDAVBrowser *webdav_browser;
+
+ g_return_val_if_fail (uud != NULL, FALSE);
+
+ if (g_source_is_destroyed (g_main_current_source ()))
+ return FALSE;
+
+ webdav_browser = g_weak_ref_get (uud->webdav_browser_weakref);
+ if (!webdav_browser)
+ return FALSE;
+
+ g_mutex_lock (&webdav_browser->priv->property_lock);
+
+ webdav_browser->priv->update_ui_id = 0;
+
+ webdav_browser_update_ui (webdav_browser);
+
+ if (uud->callback)
+ uud->callback (webdav_browser, uud->user_data);
+
+ g_mutex_unlock (&webdav_browser->priv->property_lock);
+
+ webdav_browser_change_busy_state (webdav_browser, FALSE);
+
+ g_object_unref (webdav_browser);
+
+ return FALSE;
+}
+
+static EWebDAVSession *
+webdav_browser_ref_session (EWebDAVBrowser *webdav_browser)
+{
+ EWebDAVSession *session;
+
+ g_return_val_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser), NULL);
+
+ g_mutex_lock (&webdav_browser->priv->property_lock);
+
+ session = webdav_browser->priv->session;
+ if (session)
+ g_object_ref (session);
+
+ g_mutex_unlock (&webdav_browser->priv->property_lock);
+
+ return session;
+}
+
+static void
+webdav_browser_schedule_ui_update (EWebDAVBrowser *webdav_browser,
+ UpdateUICallback callback,
+ gpointer user_data,
+ GDestroyNotify free_user_data)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+
+ g_mutex_lock (&webdav_browser->priv->property_lock);
+
+ /* This should not be called recursively/multiple times when one is waiting */
+ g_warn_if_fail (!webdav_browser->priv->update_ui_id);
+
+ if (!webdav_browser->priv->update_ui_id) {
+ UpdateUIData *uud;
+
+ uud = g_new0 (UpdateUIData, 1);
+ uud->webdav_browser_weakref = e_weak_ref_new (webdav_browser);
+ uud->callback = callback;
+ uud->user_data = user_data;
+ uud->free_user_data = free_user_data;
+
+ webdav_browser->priv->update_ui_id = g_timeout_add_full (G_PRIORITY_DEFAULT, 100,
+ webdav_browser_update_ui_timeout_cb, uud, update_ui_data_free);
+ }
+
+ g_mutex_unlock (&webdav_browser->priv->property_lock);
+}
+
+typedef struct _SearchHomeData {
+ GHashTable *covered_todo_hrefs;
+ GHashTable *covered_home_hrefs;
+ GSList *todo_hrefs;
+ GSList *home_hrefs;
+} SearchHomeData;
+
+static gboolean
+webdav_browser_search_home_hrefs_cb (EWebDAVSession *webdav,
+ xmlXPathContextPtr xpath_ctx,
+ const gchar *xpath_prop_prefix,
+ const SoupURI *request_uri,
+ const gchar *href,
+ guint status_code,
+ gpointer user_data)
+{
+ SearchHomeData *shd = user_data;
+
+ g_return_val_if_fail (shd != NULL, FALSE);
+
+ if (!xpath_prop_prefix) {
+ e_xml_xpath_context_register_namespaces (xpath_ctx,
+ "C", E_WEBDAV_NS_CALDAV,
+ "A", E_WEBDAV_NS_CARDDAV,
+ NULL);
+ } else if (status_code == SOUP_STATUS_OK) {
+ xmlXPathObjectPtr xpath_obj;
+ gchar *principal_href, *full_href;
+
+ xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/A:addressbook-home-set/D:href",
xpath_prop_prefix);
+ if (xpath_obj) {
+ gint ii, length;
+
+ length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
+
+ for (ii = 0; ii < length; ii++) {
+ gchar *home_set_href;
+
+ full_href = NULL;
+
+ home_set_href = e_xml_xpath_eval_as_string (xpath_ctx,
"%s/A:addressbook-home-set/D:href[%d]", xpath_prop_prefix, ii + 1);
+ if (home_set_href && *home_set_href) {
+ full_href = e_webdav_session_ensure_full_uri (webdav, request_uri,
home_set_href);
+ if (full_href && *full_href && !g_hash_table_contains
(shd->covered_home_hrefs, full_href)) {
+ shd->home_hrefs = g_slist_prepend (shd->home_hrefs,
full_href);
+ g_hash_table_insert (shd->covered_home_hrefs, g_strdup
(full_href), NULL);
+ full_href = NULL;
+ }
+ }
+
+ g_free (home_set_href);
+ g_free (full_href);
+ }
+
+ xmlXPathFreeObject (xpath_obj);
+ }
+
+ xpath_obj = e_xml_xpath_eval (xpath_ctx, "%s/C:calendar-home-set/D:href", xpath_prop_prefix);
+ if (xpath_obj) {
+ gint ii, length;
+
+ length = xmlXPathNodeSetGetLength (xpath_obj->nodesetval);
+
+ for (ii = 0; ii < length; ii++) {
+ gchar *home_set_href, *full_href = NULL;
+
+ home_set_href = e_xml_xpath_eval_as_string (xpath_ctx,
"%s/C:calendar-home-set/D:href[%d]", xpath_prop_prefix, ii + 1);
+ if (home_set_href && *home_set_href) {
+ full_href = e_webdav_session_ensure_full_uri (webdav, request_uri,
home_set_href);
+ if (full_href && *full_href && !g_hash_table_contains
(shd->covered_home_hrefs, full_href)) {
+ shd->home_hrefs = g_slist_prepend (shd->home_hrefs,
full_href);
+ g_hash_table_insert (shd->covered_home_hrefs, g_strdup
(full_href), NULL);
+ full_href = NULL;
+ }
+ }
+
+ g_free (home_set_href);
+ g_free (full_href);
+ }
+
+ xmlXPathFreeObject (xpath_obj);
+ }
+
+ principal_href = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:current-user-principal/D:href",
xpath_prop_prefix);
+ if (principal_href && *principal_href) {
+ full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, principal_href);
+
+ if (full_href && *full_href &&
+ !g_hash_table_contains (shd->covered_todo_hrefs, full_href)) {
+ g_hash_table_insert (shd->covered_todo_hrefs, full_href, NULL);
+ shd->todo_hrefs = g_slist_prepend (shd->todo_hrefs, g_strdup (full_href));
+ full_href = NULL;
+ }
+
+ g_free (full_href);
+ g_free (principal_href);
+
+ return TRUE;
+ }
+
+ g_free (principal_href);
+
+ principal_href = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:principal-URL/D:href",
xpath_prop_prefix);
+ if (principal_href && *principal_href) {
+ full_href = e_webdav_session_ensure_full_uri (webdav, request_uri, principal_href);
+
+ if (full_href && *full_href &&
+ !g_hash_table_contains (shd->covered_todo_hrefs, full_href)) {
+ g_hash_table_insert (shd->covered_todo_hrefs, full_href, NULL);
+ shd->todo_hrefs = g_slist_prepend (shd->todo_hrefs, g_strdup (full_href));
+ full_href = NULL;
+ }
+
+ g_free (full_href);
+ g_free (principal_href);
+
+ return TRUE;
+ }
+
+ g_free (principal_href);
+ }
+
+ return TRUE;
+}
+
+static guint32
+webdav_browser_options_to_editing_flags (GHashTable *capabilities,
+ GHashTable *allows)
+{
+ guint32 editing_flags = 0;
+
+ if (!capabilities || !allows)
+ return 0;
+
+ editing_flags |= E_EDITING_FLAG_HAS_OPTIONS;
+
+ if (g_hash_table_contains (allows, SOUP_METHOD_MKCOL)) {
+ editing_flags |= E_EDITING_FLAG_MKCOL;
+
+ if (g_hash_table_contains (capabilities, E_WEBDAV_CAPABILITY_EXTENDED_MKCOL))
+ editing_flags |= E_EDITING_FLAG_EXMKCOL;
+ }
+
+ if (g_hash_table_contains (allows, "MKCALENDAR"))
+ editing_flags |= E_EDITING_FLAG_MKCALENDAR;
+
+ if (g_hash_table_contains (capabilities, E_WEBDAV_CAPABILITY_ADDRESSBOOK))
+ editing_flags |= E_EDITING_FLAG_CAN_BOOK;
+
+ if (g_hash_table_contains (capabilities, E_WEBDAV_CAPABILITY_CALENDAR_ACCESS))
+ editing_flags |= E_EDITING_FLAG_CAN_CALENDAR;
+
+ if (g_hash_table_contains (allows, "ACL"))
+ editing_flags |= E_EDITING_FLAG_CAN_ACL;
+
+ if (g_hash_table_contains (allows, SOUP_METHOD_DELETE))
+ editing_flags |= E_EDITING_FLAG_CAN_DELETE;
+
+ return editing_flags;
+}
+
+static gboolean
+webdav_browser_gather_href_resources_sync (EWebDAVBrowser *webdav_browser,
+ EWebDAVSession *session,
+ const gchar *href,
+ gboolean options_first,
+ gboolean with_children,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean done = FALSE, success = TRUE;
+
+ g_return_val_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser), FALSE);
+ g_return_val_if_fail (E_IS_WEBDAV_SESSION (session), FALSE);
+ g_return_val_if_fail (href != NULL, FALSE);
+
+ while (!done && success) {
+ GSList *resources = NULL;
+ guint32 top_editing_flags = 0;
+ GError *local_error = NULL;
+
+ done = TRUE;
+
+ if (options_first) {
+ GHashTable *capabilities = NULL;
+ GHashTable *allows = NULL;
+
+ /* Some servers do not allow OPTIONS on each collection, thus ignore all but login
errors */
+ if (!e_webdav_session_options_sync (session, href, &capabilities, &allows,
cancellable, &local_error)) {
+ if (webdav_browser_manage_login_errors (webdav_browser, session, cancellable,
local_error)) {
+ done = FALSE;
+ g_clear_error (&local_error);
+ continue;
+ }
+
+ g_clear_error (&local_error);
+ }
+
+ top_editing_flags = webdav_browser_options_to_editing_flags (capabilities, allows);
+
+ if (capabilities)
+ g_hash_table_destroy (capabilities);
+ if (allows)
+ g_hash_table_destroy (allows);
+ }
+
+ if (e_webdav_session_list_sync (session, href, with_children ?
E_WEBDAV_DEPTH_THIS_AND_CHILDREN : E_WEBDAV_DEPTH_THIS, E_WEBDAV_LIST_ALL,
+ &resources, cancellable, &local_error)) {
+ GSList *link;
+
+ for (link = resources; link && !g_cancellable_is_cancelled (cancellable); link =
g_slist_next (link)) {
+ EWebDAVResource *resource = link->data;
+ GHashTable *capabilities = NULL;
+ GHashTable *allows = NULL;
+ guint32 editing_flags = E_EDITING_FLAG_NONE;
+ ResourceData *rd;
+ gchar *tmp;
+
+ if (!resource ||
+ !resource->href || (
+ resource->kind != E_WEBDAV_RESOURCE_KIND_ADDRESSBOOK &&
+ resource->kind != E_WEBDAV_RESOURCE_KIND_CALENDAR &&
+ resource->kind != E_WEBDAV_RESOURCE_KIND_COLLECTION &&
+ resource->kind != E_WEBDAV_RESOURCE_KIND_PRINCIPAL)) {
+ continue;
+ }
+
+ /* Some servers do not allow OPTIONS on each collection, thus ignore all
errors;
+ there might not be any login errors here, but even if, then bad luck. */
+ if (e_webdav_session_options_sync (session, resource->href, &capabilities,
&allows, cancellable, NULL)) {
+ editing_flags = webdav_browser_options_to_editing_flags
(capabilities, allows);
+ }
+
+ if (capabilities)
+ g_hash_table_destroy (capabilities);
+ if (allows)
+ g_hash_table_destroy (allows);
+
+ if (!(editing_flags & E_EDITING_FLAG_HAS_OPTIONS))
+ editing_flags = top_editing_flags;
+
+ if (resource->kind == E_WEBDAV_RESOURCE_KIND_ADDRESSBOOK)
+ editing_flags |= E_EDITING_FLAG_IS_BOOK;
+
+ if (resource->kind == E_WEBDAV_RESOURCE_KIND_CALENDAR)
+ editing_flags |= E_EDITING_FLAG_IS_CALENDAR;
+
+ if (!g_str_has_suffix (resource->href, "/")) {
+ tmp = g_strconcat (resource->href, "/", NULL);
+
+ g_free (resource->href);
+ resource->href = tmp;
+ }
+
+ /* Because for example Google server returns the '@' sometimes encoded and
sometimes not,
+ which breaks lookup based on href in the code. */
+ tmp = soup_uri_normalize (resource->href, "@");
+ g_free (resource->href);
+ resource->href = tmp;
+
+ rd = g_new0 (ResourceData, 1);
+ rd->editing_flags = editing_flags;
+ rd->resource = resource;
+
+ g_mutex_lock (&webdav_browser->priv->property_lock);
+ webdav_browser->priv->resources = g_slist_prepend
(webdav_browser->priv->resources, rd);
+ g_mutex_unlock (&webdav_browser->priv->property_lock);
+
+ link->data = NULL;
+ }
+ } else if (webdav_browser_manage_login_errors (webdav_browser, session, cancellable,
local_error)) {
+ done = FALSE;
+ g_clear_error (&local_error);
+ } else if (local_error) {
+ g_propagate_error (error, local_error);
+ success = FALSE;
+ }
+
+ g_slist_free_full (resources, e_webdav_resource_free);
+ }
+
+ return success;
+}
+
+typedef struct _SearchChildrenData {
+ GWeakRef *webdav_browser_weakref;
+ GtkTreeRowReference *loading_row;
+ gchar *href;
+} SearchChildrenData;
+
+static void
+search_children_data_free (gpointer ptr)
+{
+ SearchChildrenData *scd = ptr;
+
+ if (scd) {
+ if (scd->webdav_browser_weakref)
+ e_weak_ref_free (scd->webdav_browser_weakref);
+ if (scd->loading_row)
+ gtk_tree_row_reference_free (scd->loading_row);
+ g_free (scd->href);
+ g_free (scd);
+ }
+}
+
+static void
+webdav_browser_finish_search_children (EWebDAVBrowser *webdav_browser,
+ gpointer user_data)
+{
+ SearchChildrenData *scd = user_data;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (scd);
+
+ if (gtk_tree_row_reference_valid (scd->loading_row)) {
+ GtkTreeIter sort_iter, iter;
+ GtkTreePath *path;
+ GtkTreeModel *model;
+
+ model = gtk_tree_row_reference_get_model (scd->loading_row);
+ path = gtk_tree_row_reference_get_path (scd->loading_row);
+ if (path && gtk_tree_model_get_iter (model, &sort_iter, path)) {
+ gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model), &iter,
&sort_iter);
+
+ model = gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (model));
+
+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
+ }
+
+ if (path)
+ gtk_tree_path_free (path);
+
+ if (scd->href) {
+ GtkTreeRowReference *reference;
+
+ reference = g_hash_table_lookup (webdav_browser->priv->href_to_reference, scd->href);
+ if (reference) {
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+ if (path && gtk_tree_model_get_iter (model, &iter, path)) {
+ gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
COLUMN_BOOL_CHILDREN_LOADED, TRUE, -1);
+ }
+
+ if (path)
+ gtk_tree_path_free (path);
+ }
+ }
+ }
+}
+
+static void
+webdav_browser_search_children_thread (EAlertSinkThreadJobData *job_data,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SearchChildrenData *scd = user_data, *scd2;
+ EWebDAVBrowser *webdav_browser;
+ EWebDAVSession *session;
+
+ g_return_if_fail (scd != NULL);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return;
+
+ webdav_browser = g_weak_ref_get (scd->webdav_browser_weakref);
+ if (!webdav_browser)
+ return;
+
+ session = webdav_browser_ref_session (webdav_browser);
+ if (!session) {
+ g_object_unref (webdav_browser);
+ return;
+ }
+
+ webdav_browser_gather_href_resources_sync (webdav_browser, session, scd->href, FALSE, TRUE,
cancellable, error);
+
+ scd2 = g_new0 (SearchChildrenData, 1);
+ scd2->loading_row = scd->loading_row;
+ scd2->href = scd->href;
+
+ scd->loading_row = NULL;
+ scd->href = NULL;
+
+ webdav_browser_schedule_ui_update (webdav_browser,
+ webdav_browser_finish_search_children, scd2, search_children_data_free);
+
+ g_object_unref (webdav_browser);
+ g_object_unref (session);
+}
+
+static gboolean
+webdav_browser_is_any_parent_covered (GHashTable *covered_hrefs,
+ const gchar *href)
+{
+ gchar *path;
+
+ g_return_val_if_fail (covered_hrefs != NULL, FALSE);
+ g_return_val_if_fail (href != NULL, FALSE);
+
+ if (!g_hash_table_size (covered_hrefs))
+ return FALSE;
+
+ path = g_strdup (href);
+ if (path) {
+ gint pos;
+
+ for (pos = strlen (path) - 1; pos > 0; pos--) {
+ if (path[pos] == '/' && path[pos + 1] != '\0') {
+ path[pos + 1] = '\0';
+
+ if (g_hash_table_contains (covered_hrefs, path)) {
+ g_free (path);
+ return TRUE;
+ }
+ }
+ }
+
+ g_free (path);
+ }
+
+ return FALSE;
+}
+
+static void
+webdav_browser_search_user_home_thread (EAlertSinkThreadJobData *job_data,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EWebDAVBrowser *webdav_browser;
+ EWebDAVSession *session;
+ EXmlDocument *xml;
+ SearchHomeData shd;
+ GHashTable *checked_tops;
+ ESource *source;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return;
+
+ webdav_browser = g_weak_ref_get (user_data);
+ if (!webdav_browser)
+ return;
+
+ session = webdav_browser_ref_session (webdav_browser);
+ if (!session) {
+ g_object_unref (webdav_browser);
+ return;
+ }
+
+ xml = e_xml_document_new (E_WEBDAV_NS_DAV, "propfind");
+ g_return_if_fail (xml != NULL);
+
+ e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "prop");
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "current-user-principal");
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "principal-URL");
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALDAV, "calendar-home-set");
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CARDDAV, "addressbook-home-set");
+ e_xml_document_end_element (xml); /* prop */
+
+ shd.covered_todo_hrefs = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free,
NULL);
+ shd.covered_home_hrefs = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free,
NULL);
+ shd.todo_hrefs = NULL;
+ shd.home_hrefs = NULL;
+
+ source = e_soup_session_get_source (E_SOUP_SESSION (session));
+ if (source && e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND)) {
+ ESourceWebdav *webdav_extension;
+ SoupURI *suri;
+
+ webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+ suri = e_source_webdav_dup_soup_uri (webdav_extension);
+
+ if (suri) {
+ gchar *path;
+
+ soup_uri_set_user (suri, NULL);
+ path = soup_uri_to_string (suri, FALSE);
+ if (path) {
+ shd.home_hrefs = g_slist_prepend (shd.home_hrefs, g_strdup (path));
+ g_hash_table_insert (shd.covered_home_hrefs, path, NULL);
+ }
+
+ path = g_strdup (soup_uri_get_path (suri));
+ if (path) {
+ gint len, pos;
+ gint levels_back = 0;
+
+ /* There is no guarantee that the parent folder is a WebDAV collection,
+ but let's try it, just in case. */
+ len = strlen (path);
+ for (pos = len - 1; pos > 0; pos--) {
+ if (path[pos] == '/' && path[pos + 1] != '\0') {
+ levels_back++;
+
+ /* Do not go back too far, most servers has URIs like
"/dav/user/collection/" */
+ if (levels_back > 2)
+ break;
+
+ path[pos + 1] = '\0';
+
+ soup_uri_set_path (suri, path);
+ shd.todo_hrefs = g_slist_prepend (shd.todo_hrefs,
soup_uri_to_string (suri, FALSE));
+ }
+ }
+
+ g_free (path);
+ }
+ }
+
+ if (suri && (!soup_uri_get_path (suri) || !strstr (soup_uri_get_path (suri),
"/.well-known/"))) {
+ soup_uri_set_path (suri, "/.well-known/caldav");
+ shd.todo_hrefs = g_slist_prepend (shd.todo_hrefs, soup_uri_to_string (suri, FALSE));
+
+ soup_uri_set_path (suri, "/.well-known/carddav");
+ shd.todo_hrefs = g_slist_prepend (shd.todo_hrefs, soup_uri_to_string (suri, FALSE));
+ }
+
+ if (suri) {
+ soup_uri_set_path (suri, "");
+ shd.todo_hrefs = g_slist_prepend (shd.todo_hrefs, soup_uri_to_string (suri, FALSE));
+
+ soup_uri_free (suri);
+ }
+ }
+
+ /* Try the URL provided in the ESource first */
+ shd.todo_hrefs = g_slist_prepend (shd.todo_hrefs, NULL);
+
+ checked_tops = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL);
+
+ while (shd.todo_hrefs &&
+ !g_cancellable_set_error_if_cancelled (cancellable, error)) {
+ gchar *top_href;
+ gboolean done;
+ GError *local_error = NULL;
+
+ top_href = shd.todo_hrefs->data;
+ shd.todo_hrefs = g_slist_remove (shd.todo_hrefs, top_href);
+
+ done = top_href && g_hash_table_contains (checked_tops, top_href);
+
+ if (top_href)
+ g_hash_table_insert (checked_tops, g_strdup (top_href), NULL);
+
+ while (!done) {
+ done = TRUE;
+
+ if (e_webdav_session_propfind_sync (session, top_href, E_WEBDAV_DEPTH_THIS, xml,
+ webdav_browser_search_home_hrefs_cb, &shd, cancellable, &local_error)) {
+ } else if (webdav_browser_manage_login_errors (webdav_browser, session, cancellable,
local_error)) {
+ done = FALSE;
+ }
+
+ /* Ignore all but login errors here, because some of the URIs are just guesses */
+ g_clear_error (&local_error);
+ }
+
+ g_free (top_href);
+ }
+
+ g_hash_table_destroy (checked_tops);
+
+ if (!shd.home_hrefs) {
+ ESourceWebdav *webdav_extension;
+ SoupURI *suri;
+
+ webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+ suri = e_source_webdav_dup_soup_uri (webdav_extension);
+
+ if (suri) {
+ gchar *path;
+
+ soup_uri_set_user (suri, NULL);
+
+ path = g_strdup (soup_uri_get_path (suri));
+ if (path) {
+ gint len, pos;
+ gint levels_back = 0;
+
+ shd.home_hrefs = g_slist_prepend (shd.home_hrefs, soup_uri_to_string (suri,
FALSE));
+
+ /* There is no guarantee that the parent folder is a WebDAV collection,
+ but let's try it, just in case. */
+ len = strlen (path);
+ for (pos = len - 1; pos > 0; pos--) {
+ if (path[pos] == '/' && path[pos + 1] != '\0') {
+ gchar *href;
+
+ levels_back++;
+
+ /* Do not go back too far, most servers has URIs like
"/dav/user/collection/" */
+ if (levels_back > 2)
+ break;
+
+ path[pos + 1] = '\0';
+
+ soup_uri_set_path (suri, path);
+ href = soup_uri_to_string (suri, FALSE);
+
+ if (!g_hash_table_contains (shd.covered_home_hrefs, href))
+ shd.home_hrefs = g_slist_prepend (shd.home_hrefs,
href);
+ else
+ g_free (href);
+ }
+ }
+
+ g_free (path);
+ }
+
+ soup_uri_free (suri);
+ }
+ }
+
+ g_hash_table_remove_all (shd.covered_home_hrefs);
+ shd.home_hrefs = g_slist_sort (shd.home_hrefs, (GCompareFunc) g_strcmp0);
+
+ while (!g_cancellable_is_cancelled (cancellable) && shd.home_hrefs) {
+ gchar *home_href;
+
+ home_href = shd.home_hrefs->data;
+ shd.home_hrefs = g_slist_remove (shd.home_hrefs, home_href);
+
+ if (webdav_browser_is_any_parent_covered (shd.covered_home_hrefs, home_href)) {
+ g_free (home_href);
+ } else {
+ /* Ignore errors here as well */
+ webdav_browser_gather_href_resources_sync (webdav_browser, session, home_href, TRUE,
TRUE, cancellable, NULL);
+ g_hash_table_insert (shd.covered_home_hrefs, home_href, NULL);
+ }
+ }
+
+ webdav_browser_schedule_ui_update (webdav_browser, NULL, NULL, NULL);
+
+ g_hash_table_destroy (shd.covered_todo_hrefs);
+ g_hash_table_destroy (shd.covered_home_hrefs);
+ g_slist_free_full (shd.todo_hrefs, g_free);
+ g_slist_free_full (shd.home_hrefs, g_free);
+ g_object_unref (webdav_browser);
+ g_object_unref (session);
+ g_clear_object (&xml);
+}
+
+static void
+webdav_browser_selection_changed_cb (GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ EWebDAVBrowser *webdav_browser = user_data;
+ GtkTreeModel *model = NULL;
+ GtkTreeIter iter;
+ guint editing_flags = E_EDITING_FLAG_NONE;
+ gboolean has_parent = FALSE;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ GtkTreeIter parent;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_UINT_EDITING_FLAGS, &editing_flags,
+ -1);
+
+ has_parent = gtk_tree_model_iter_parent (model, &parent, &iter);
+ }
+
+ #define has_set(x) ((editing_flags & (x)) == (x))
+
+ gtk_widget_set_sensitive (webdav_browser->priv->create_book_button,
+ has_set (E_EDITING_FLAG_EXMKCOL | E_EDITING_FLAG_CAN_BOOK));
+
+ gtk_widget_set_sensitive (webdav_browser->priv->create_calendar_button,
+ has_set (E_EDITING_FLAG_MKCALENDAR | E_EDITING_FLAG_CAN_CALENDAR));
+
+ gtk_widget_set_sensitive (webdav_browser->priv->create_collection_button,
+ has_set (E_EDITING_FLAG_MKCOL));
+
+ gtk_widget_set_sensitive (webdav_browser->priv->edit_button,
+ (editing_flags & (E_EDITING_FLAG_IS_BOOK | E_EDITING_FLAG_IS_CALENDAR)) != 0);
+
+ gtk_widget_set_sensitive (webdav_browser->priv->delete_button,
+ has_set (E_EDITING_FLAG_CAN_DELETE) && has_parent);
+
+ /* gtk_widget_set_sensitive (webdav_browser->priv->permissions_button,
+ has_set (E_EDITING_FLAG_CAN_ACL)); */
+
+ #undef has_set
+}
+
+static void
+webdav_browser_change_busy_state (EWebDAVBrowser *webdav_browser,
+ gboolean is_busy)
+{
+ gtk_widget_set_sensitive (webdav_browser->priv->tree_view, !is_busy);
+
+ if (is_busy) {
+ gtk_widget_set_sensitive (webdav_browser->priv->create_book_button, FALSE);
+ gtk_widget_set_sensitive (webdav_browser->priv->create_calendar_button, FALSE);
+ gtk_widget_set_sensitive (webdav_browser->priv->create_collection_button, FALSE);
+ gtk_widget_set_sensitive (webdav_browser->priv->edit_button, FALSE);
+ gtk_widget_set_sensitive (webdav_browser->priv->delete_button, FALSE);
+ /* gtk_widget_set_sensitive (webdav_browser->priv->permissions_button, FALSE); */
+ gtk_widget_set_sensitive (webdav_browser->priv->refresh_button, FALSE);
+
+ e_alert_bar_clear (webdav_browser->priv->alert_bar);
+ } else {
+ GtkTreeView *tree_view;
+
+ tree_view = GTK_TREE_VIEW (webdav_browser->priv->tree_view);
+
+ webdav_browser_selection_changed_cb (gtk_tree_view_get_selection (tree_view), webdav_browser);
+
+ gtk_widget_set_sensitive (webdav_browser->priv->refresh_button, webdav_browser->priv->session
!= NULL);
+ }
+}
+
+static void
+webdav_browser_row_expanded_cb (GtkTreeView *tree_view,
+ GtkTreeIter *iter,
+ GtkTreePath *path,
+ gpointer user_data)
+{
+ EWebDAVBrowser *webdav_browser = user_data;
+ EActivity *activity;
+ GtkTreeModel *model;
+ GtkTreeIter loading_child;
+ GtkTreePath *loading_path;
+ SearchChildrenData *scd;
+ gboolean is_loaded = TRUE;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+ g_return_if_fail (iter);
+
+ model = gtk_tree_view_get_model (tree_view);
+ gtk_tree_model_get (model, iter, COLUMN_BOOL_CHILDREN_LOADED, &is_loaded, -1);
+
+ if (is_loaded)
+ return;
+
+ g_return_if_fail (gtk_tree_model_iter_nth_child (model, &loading_child, iter, 0));
+ g_return_if_fail (webdav_browser->priv->session);
+
+ scd = g_new0 (SearchChildrenData, 1);
+ scd->webdav_browser_weakref = e_weak_ref_new (webdav_browser);
+
+ loading_path = gtk_tree_model_get_path (model, &loading_child);
+ scd->loading_row = gtk_tree_row_reference_new (model, loading_path);
+ gtk_tree_path_free (loading_path);
+
+ gtk_tree_model_get (model, iter, COLUMN_STRING_HREF, &scd->href, -1);
+
+ e_webdav_browser_abort (webdav_browser);
+
+ g_clear_object (&webdav_browser->priv->cancellable);
+
+ webdav_browser_change_busy_state (webdav_browser, TRUE);
+
+ activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (webdav_browser),
+ _("Searching collection children…"),
+ "system:generic-error",
+ _("Failed to search for collection children"),
+ webdav_browser_search_children_thread, scd,
+ search_children_data_free);
+
+ if (activity) {
+ webdav_browser->priv->cancellable = e_activity_get_cancellable (activity);
+
+ if (webdav_browser->priv->cancellable)
+ g_object_ref (webdav_browser->priv->cancellable);
+
+ e_activity_bar_set_activity (webdav_browser->priv->activity_bar, activity);
+
+ g_object_unref (activity);
+ } else {
+ webdav_browser_change_busy_state (webdav_browser, FALSE);
+ webdav_browser_schedule_ui_update (webdav_browser, NULL, NULL, NULL);
+ }
+}
+
+static void
+webdav_browser_search_user_home (EWebDAVBrowser *webdav_browser)
+{
+ EActivity *activity;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (webdav_browser->priv->session);
+
+ e_webdav_browser_abort (webdav_browser);
+
+ g_clear_object (&webdav_browser->priv->cancellable);
+
+ webdav_browser_change_busy_state (webdav_browser, TRUE);
+
+ activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (webdav_browser),
+ _("Searching for user home, please wait…"),
+ "system:generic-error",
+ _("Failed to search for user home"),
+ webdav_browser_search_user_home_thread,
+ e_weak_ref_new (webdav_browser),
+ (GDestroyNotify) e_weak_ref_free);
+
+ if (activity) {
+ webdav_browser->priv->cancellable = e_activity_get_cancellable (activity);
+
+ if (webdav_browser->priv->cancellable)
+ g_object_ref (webdav_browser->priv->cancellable);
+
+ e_activity_bar_set_activity (webdav_browser->priv->activity_bar, activity);
+
+ g_object_unref (activity);
+ } else {
+ webdav_browser_change_busy_state (webdav_browser, FALSE);
+ webdav_browser_schedule_ui_update (webdav_browser, NULL, NULL, NULL);
+ }
+}
+
+static gchar *
+webdav_browser_dup_selected_href (EWebDAVBrowser *webdav_browser)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gchar *href = NULL;
+
+ g_return_val_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser), NULL);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (webdav_browser->priv->tree_view));
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ return NULL;
+
+ gtk_tree_model_get (model, &iter, COLUMN_STRING_HREF, &href, -1);
+
+ return href;
+}
+
+static gboolean
+webdav_browser_get_selected_loaded (EWebDAVBrowser *webdav_browser)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gboolean is_loaded = FALSE;
+
+ g_return_val_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser), FALSE);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (webdav_browser->priv->tree_view));
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ return FALSE;
+
+ gtk_tree_model_get (model, &iter, COLUMN_BOOL_CHILDREN_LOADED, &is_loaded, -1);
+
+ return is_loaded;
+}
+
+static gboolean
+webdav_browser_any_parent_is_book_or_calendar (EWebDAVBrowser *webdav_browser)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter, parent;
+ GtkTreeSelection *selection;
+ gboolean done;
+
+ g_return_val_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser), FALSE);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (webdav_browser->priv->tree_view));
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ return FALSE;
+
+ do {
+ guint editing_flags = E_EDITING_FLAG_NONE;
+
+ gtk_tree_model_get (model, &iter, COLUMN_UINT_EDITING_FLAGS, &editing_flags, -1);
+
+ if ((editing_flags & (E_EDITING_FLAG_IS_BOOK | E_EDITING_FLAG_IS_CALENDAR)) != 0)
+ return TRUE;
+
+ done = !gtk_tree_model_iter_parent (model, &parent, &iter);
+ if (!done)
+ iter = parent;
+ } while (!done);
+
+ return FALSE;
+}
+
+static void
+webdav_browser_drop_loading_node_for_href (EWebDAVBrowser *webdav_browser,
+ gpointer user_data)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter, child;
+ const gchar *href = user_data;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (href != NULL);
+
+ reference = g_hash_table_lookup (webdav_browser->priv->href_to_reference, href);
+ if (!reference && !g_str_has_suffix (href, "/")) {
+ gchar *tmp;
+
+ tmp = g_strconcat (href, "/", NULL);
+ reference = g_hash_table_lookup (webdav_browser->priv->href_to_reference, tmp);
+ g_free (tmp);
+ }
+
+ if (!reference)
+ return;
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+
+ if (!path)
+ return;
+
+ if (gtk_tree_model_get_iter (model, &iter, path)) {
+ gtk_tree_store_set (GTK_TREE_STORE (model), &iter, COLUMN_BOOL_CHILDREN_LOADED, TRUE, -1);
+
+ /* Remove "Loading…" row */
+ if (gtk_tree_model_iter_nth_child (model, &child, &iter, 0)) {
+ do {
+ guint flags = E_EDITING_FLAG_NONE;
+
+ gtk_tree_model_get (model, &child, COLUMN_UINT_EDITING_FLAGS, &flags, -1);
+
+ if (flags == E_EDITING_FLAG_IS_LOADING_ROW) {
+ gtk_tree_store_remove (GTK_TREE_STORE (model), &child);
+ break;
+ }
+ } while (gtk_tree_model_iter_next (model, &child));
+ }
+ }
+
+ gtk_tree_path_free (path);
+}
+
+typedef struct _SaveChangesData {
+ GWeakRef *webdav_browser_weakref;
+ gchar *href;
+ gboolean is_edit; /* TRUE for save changes, FALSE to create new under href */
+ gboolean load_first;
+ gchar *name;
+ GdkRGBA rgba;
+ guint32 supports; /* bit-or of EWebDAVResourceSupports */
+ gchar *description;
+} SaveChangesData;
+
+static void
+save_changes_data_free (gpointer ptr)
+{
+ SaveChangesData *scd = ptr;
+
+ if (scd) {
+ e_weak_ref_free (scd->webdav_browser_weakref);
+ g_free (scd->href);
+ g_free (scd->name);
+ g_free (scd->description);
+ g_free (scd);
+ }
+}
+
+static void
+webdav_browser_save_changes_thread (EAlertSinkThreadJobData *job_data,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EWebDAVBrowser *webdav_browser;
+ EWebDAVSession *session;
+ SaveChangesData *scd = user_data;
+ gchar *new_href = NULL;
+ gboolean success = FALSE;
+
+ g_return_if_fail (scd != NULL);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return;
+
+ webdav_browser = g_weak_ref_get (scd->webdav_browser_weakref);
+ if (!webdav_browser)
+ return;
+
+ session = webdav_browser_ref_session (webdav_browser);
+ if (!session) {
+ g_object_unref (webdav_browser);
+ return;
+ }
+
+ if (scd->load_first)
+ webdav_browser_gather_href_resources_sync (webdav_browser, session, scd->href, FALSE, TRUE,
cancellable, NULL);
+
+ if (scd->is_edit) {
+ GSList *changes = NULL;
+
+ changes = g_slist_append (changes, e_webdav_property_change_new_set (E_WEBDAV_NS_DAV,
"displayname", scd->name));
+
+ if ((scd->supports & E_WEBDAV_RESOURCE_SUPPORTS_CONTACTS) != 0) {
+ if (scd->description && *scd->description)
+ changes = g_slist_append (changes, e_webdav_property_change_new_set
(E_WEBDAV_NS_CARDDAV, "addressbook-description", scd->description));
+ else
+ changes = g_slist_append (changes, e_webdav_property_change_new_remove
(E_WEBDAV_NS_CARDDAV, "addressbook-description"));
+ } else if ((scd->supports & (E_WEBDAV_RESOURCE_SUPPORTS_EVENTS |
E_WEBDAV_RESOURCE_SUPPORTS_MEMOS | E_WEBDAV_RESOURCE_SUPPORTS_TASKS)) != 0) {
+ gchar *color;
+
+ color = g_strdup_printf ("#%02x%02x%02x",
+ (gint) CLAMP (scd->rgba.red * 0xFF, 0, 0xFF),
+ (gint) CLAMP (scd->rgba.green * 0xFF, 0, 0xFF),
+ (gint) CLAMP (scd->rgba.blue * 0xFF, 0, 0xFF));
+
+ changes = g_slist_append (changes, e_webdav_property_change_new_set
(E_WEBDAV_NS_ICAL, "calendar-color", color));
+
+ if (scd->description && *scd->description)
+ changes = g_slist_append (changes, e_webdav_property_change_new_set
(E_WEBDAV_NS_CALDAV, "calendar-description", scd->description));
+ else
+ changes = g_slist_append (changes, e_webdav_property_change_new_remove
(E_WEBDAV_NS_CALDAV, "calendar-description"));
+
+ g_free (color);
+ }
+
+ success = e_webdav_session_update_properties_sync (session, scd->href, changes, cancellable,
error);
+
+ g_slist_free_full (changes, e_webdav_property_change_free);
+ } else {
+ SoupURI *suri;
+ GString *path;
+ gchar *encoded;
+
+ suri = soup_uri_new (scd->href);
+ path = g_string_new (soup_uri_get_path (suri));
+
+ if (path->len && path->str[path->len - 1] != '/')
+ g_string_append_c (path, '/');
+
+ encoded = soup_uri_encode (scd->name, NULL);
+ g_string_append (path, encoded);
+ g_free (encoded);
+
+ soup_uri_set_path (suri, path->str);
+
+ new_href = soup_uri_to_string (suri, FALSE);
+
+ if ((scd->supports & E_WEBDAV_RESOURCE_SUPPORTS_CONTACTS) != 0) {
+ success = e_webdav_session_mkcol_addressbook_sync (session, new_href,
+ scd->name, scd->description, cancellable, error);
+ } else if ((scd->supports & (E_WEBDAV_RESOURCE_SUPPORTS_EVENTS |
E_WEBDAV_RESOURCE_SUPPORTS_MEMOS | E_WEBDAV_RESOURCE_SUPPORTS_TASKS)) != 0) {
+ gchar *color;
+
+ color = g_strdup_printf ("#%02x%02x%02x",
+ (gint) CLAMP (scd->rgba.red * 0xFF, 0, 0xFF),
+ (gint) CLAMP (scd->rgba.green * 0xFF, 0, 0xFF),
+ (gint) CLAMP (scd->rgba.blue * 0xFF, 0, 0xFF));
+
+ success = e_webdav_session_mkcalendar_sync (session, new_href,
+ scd->name, scd->description, color,
+ scd->supports, cancellable, error);
+
+ g_free (color);
+ } else {
+ success = e_webdav_session_mkcol_sync (session, new_href, cancellable, error);
+ }
+
+ g_string_free (path, TRUE);
+ soup_uri_free (suri);
+ }
+
+ if (success) {
+ const gchar *href = new_href ? new_href : scd->href;
+
+ if (scd->load_first) {
+ GSList *link;
+
+ for (link = webdav_browser->priv->resources; link; link = g_slist_next (link)) {
+ ResourceData *rd = link->data;
+
+ if (rd && rd->resource && rd->resource->href &&
+ g_strcmp0 (rd->resource->href, href) == 0) {
+ webdav_browser->priv->resources = g_slist_remove
(webdav_browser->priv->resources, rd);
+ resource_data_free (rd);
+ break;
+ }
+ }
+ }
+
+ webdav_browser_gather_href_resources_sync (webdav_browser, session, href, FALSE, FALSE,
cancellable, error);
+
+ if (!scd->is_edit)
+ webdav_browser_schedule_ui_update (webdav_browser,
+ webdav_browser_drop_loading_node_for_href, g_strdup (href), g_free);
+ else
+ webdav_browser_schedule_ui_update (webdav_browser, NULL, NULL, NULL);
+ } else {
+ webdav_browser_schedule_ui_update (webdav_browser, NULL, NULL, NULL);
+ }
+
+ g_object_unref (webdav_browser);
+ g_object_unref (session);
+ g_free (new_href);
+}
+
+static void
+webdav_browser_save_clicked (EWebDAVBrowser *webdav_browser,
+ gboolean is_book,
+ gboolean is_calendar,
+ gboolean is_edit)
+{
+ EActivity *activity;
+ SaveChangesData *scd;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+ guint32 supports = 0;
+ gchar *text, *href;
+ const gchar *description;
+ const gchar *error_message;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+
+ text = g_strdup (gtk_entry_get_text (GTK_ENTRY (webdav_browser->priv->create_edit_name_entry)));
+ if (text)
+ text = g_strstrip (text);
+
+ if (!text || !*text) {
+ gtk_widget_hide (webdav_browser->priv->create_edit_hint_popover);
+
+ gtk_label_set_text (GTK_LABEL (webdav_browser->priv->create_edit_hint_label), _("Name cannot
be empty"));
+ gtk_popover_set_relative_to (GTK_POPOVER (webdav_browser->priv->create_edit_hint_popover),
+ webdav_browser->priv->create_edit_name_entry);
+
+ gtk_widget_show (webdav_browser->priv->create_edit_hint_popover);
+
+ g_free (text);
+
+ return;
+ } else if (is_calendar &&
+ !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(webdav_browser->priv->create_edit_event_check)) &&
+ !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(webdav_browser->priv->create_edit_memo_check)) &&
+ !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(webdav_browser->priv->create_edit_task_check))) {
+ gtk_widget_hide (webdav_browser->priv->create_edit_hint_popover);
+
+ gtk_label_set_text (GTK_LABEL (webdav_browser->priv->create_edit_hint_label), _("At least one
component type should be set"));
+ gtk_popover_set_relative_to (GTK_POPOVER (webdav_browser->priv->create_edit_hint_popover),
+ webdav_browser->priv->create_edit_task_check);
+
+ gtk_widget_show (webdav_browser->priv->create_edit_hint_popover);
+
+ g_free (text);
+
+ return;
+ }
+
+ gtk_widget_hide (webdav_browser->priv->create_edit_popover);
+
+ href = webdav_browser_dup_selected_href (webdav_browser);
+ if (!href || !*href) {
+ g_free (href);
+ g_free (text);
+
+ webdav_browser_add_alert (webdav_browser, _("Failed to get selected collection HREF"), NULL);
+
+ return;
+ }
+
+ if (is_book) {
+ supports = E_WEBDAV_RESOURCE_SUPPORTS_CONTACTS;
+ } else if (is_calendar) {
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(webdav_browser->priv->create_edit_event_check)))
+ supports |= E_WEBDAV_RESOURCE_SUPPORTS_EVENTS;
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(webdav_browser->priv->create_edit_memo_check)))
+ supports |= E_WEBDAV_RESOURCE_SUPPORTS_MEMOS;
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
(webdav_browser->priv->create_edit_task_check)))
+ supports |= E_WEBDAV_RESOURCE_SUPPORTS_TASKS;
+ }
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW
(webdav_browser->priv->create_edit_description_textview));
+ gtk_text_buffer_get_start_iter (buffer, &start);
+ gtk_text_buffer_get_end_iter (buffer, &end);
+
+ scd = g_new0 (SaveChangesData, 1);
+ scd->webdav_browser_weakref = e_weak_ref_new (webdav_browser);
+ scd->href = href;
+ scd->is_edit = is_edit;
+ scd->load_first = !webdav_browser_get_selected_loaded (webdav_browser);
+ scd->name = text;
+ gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (webdav_browser->priv->create_edit_color_chooser),
&scd->rgba);
+ scd->supports = supports;
+ scd->description = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+
+ if (is_edit) {
+ description = _("Saving changes…");
+ error_message = _("Failed to save changes");
+ } else if (is_book) {
+ description = _("Creating new book…");
+ error_message = _("Failed to create new book");
+ } else if (is_calendar) {
+ description = _("Creating new calendar…");
+ error_message = _("Failed to create new calendar");
+ } else {
+ description = _("Creating new collection…");
+ error_message = _("Failed to create new collection");
+ }
+
+ e_webdav_browser_abort (webdav_browser);
+
+ g_clear_object (&webdav_browser->priv->cancellable);
+
+ webdav_browser_change_busy_state (webdav_browser, TRUE);
+
+ activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (webdav_browser),
+ description, "system:generic-error", error_message,
+ webdav_browser_save_changes_thread, scd,
+ save_changes_data_free);
+
+ if (activity) {
+ webdav_browser->priv->cancellable = e_activity_get_cancellable (activity);
+
+ if (webdav_browser->priv->cancellable)
+ g_object_ref (webdav_browser->priv->cancellable);
+
+ e_activity_bar_set_activity (webdav_browser->priv->activity_bar, activity);
+
+ g_object_unref (activity);
+ } else {
+ webdav_browser_change_busy_state (webdav_browser, FALSE);
+ webdav_browser_schedule_ui_update (webdav_browser, NULL, NULL, NULL);
+ }
+}
+
+static void
+webdav_browser_create_book_save_clicked_cb (GtkWidget *button,
+ EWebDAVBrowser *webdav_browser)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (GTK_IS_POPOVER (webdav_browser->priv->create_edit_popover));
+
+ webdav_browser_save_clicked (webdav_browser, TRUE, FALSE, FALSE);
+}
+
+static void
+webdav_browser_create_calendar_save_clicked_cb (GtkWidget *button,
+ EWebDAVBrowser *webdav_browser)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (GTK_IS_POPOVER (webdav_browser->priv->create_edit_popover));
+
+ webdav_browser_save_clicked (webdav_browser, FALSE, TRUE, FALSE);
+}
+
+static void
+webdav_browser_create_collection_save_clicked_cb (GtkWidget *button,
+ EWebDAVBrowser *webdav_browser)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (GTK_IS_POPOVER (webdav_browser->priv->create_edit_popover));
+
+ gtk_widget_hide (webdav_browser->priv->create_edit_popover);
+
+ webdav_browser_save_clicked (webdav_browser, FALSE, FALSE, FALSE);
+}
+
+static void
+webdav_browser_edit_book_save_clicked_cb (GtkWidget *button,
+ EWebDAVBrowser *webdav_browser)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (GTK_IS_POPOVER (webdav_browser->priv->create_edit_popover));
+
+ webdav_browser_save_clicked (webdav_browser, TRUE, FALSE, TRUE);
+}
+
+static void
+webdav_browser_edit_calendar_save_clicked_cb (GtkWidget *button,
+ EWebDAVBrowser *webdav_browser)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (GTK_IS_POPOVER (webdav_browser->priv->create_edit_popover));
+
+ webdav_browser_save_clicked (webdav_browser, FALSE, TRUE, TRUE);
+}
+
+static void
+webdav_browser_edit_collection_save_clicked_cb (GtkWidget *button,
+ EWebDAVBrowser *webdav_browser)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (GTK_IS_POPOVER (webdav_browser->priv->create_edit_popover));
+
+ gtk_widget_hide (webdav_browser->priv->create_edit_popover);
+
+ webdav_browser_save_clicked (webdav_browser, FALSE, FALSE, TRUE);
+}
+
+static void
+webdav_browser_prepare_popover (EWebDAVBrowser *webdav_browser,
+ gboolean for_book,
+ gboolean for_calendar)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+
+ gtk_widget_hide (webdav_browser->priv->create_edit_popover);
+
+ gtk_widget_set_visible (webdav_browser->priv->create_edit_color_label, for_calendar);
+ gtk_widget_set_visible (webdav_browser->priv->create_edit_color_chooser, for_calendar);
+ gtk_widget_set_visible (webdav_browser->priv->create_edit_support_label, for_calendar);
+ gtk_widget_set_visible (webdav_browser->priv->create_edit_event_check, for_calendar);
+ gtk_widget_set_visible (webdav_browser->priv->create_edit_memo_check, for_calendar);
+ gtk_widget_set_visible (webdav_browser->priv->create_edit_task_check, for_calendar);
+ gtk_widget_set_visible (webdav_browser->priv->create_edit_description_label, for_book ||
for_calendar);
+ gtk_widget_set_visible (webdav_browser->priv->create_edit_description_scrolled_window, for_book ||
for_calendar);
+
+ gtk_widget_set_sensitive (webdav_browser->priv->create_edit_support_label, TRUE);
+ gtk_widget_set_sensitive (webdav_browser->priv->create_edit_event_check, TRUE);
+ gtk_widget_set_sensitive (webdav_browser->priv->create_edit_memo_check, TRUE);
+ gtk_widget_set_sensitive (webdav_browser->priv->create_edit_task_check, TRUE);
+
+ gtk_widget_hide (webdav_browser->priv->create_edit_hint_popover);
+
+ gtk_entry_set_text (GTK_ENTRY (webdav_browser->priv->create_edit_name_entry), "");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (webdav_browser->priv->create_edit_event_check),
FALSE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (webdav_browser->priv->create_edit_memo_check),
FALSE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (webdav_browser->priv->create_edit_task_check),
FALSE);
+ gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW
(webdav_browser->priv->create_edit_description_textview)), "", -1);
+}
+
+static void
+webdav_browser_create_clicked_cb (GtkWidget *button,
+ EWebDAVBrowser *webdav_browser)
+{
+ GCallback callback;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (GTK_IS_POPOVER (webdav_browser->priv->create_edit_popover));
+
+ if (button != webdav_browser->priv->create_collection_button &&
+ webdav_browser_any_parent_is_book_or_calendar (webdav_browser)) {
+ const gchar *msg;
+
+ if (button == webdav_browser->priv->create_book_button)
+ msg = _("It is not allowed to create book under another book or calendar");
+ else if (button == webdav_browser->priv->create_calendar_button)
+ msg = _("It is not allowed to create calendar under another book or calendar");
+
+ gtk_widget_hide (webdav_browser->priv->create_edit_hint_popover);
+
+ gtk_label_set_text (GTK_LABEL (webdav_browser->priv->create_edit_hint_label), msg);
+ gtk_popover_set_relative_to (GTK_POPOVER (webdav_browser->priv->create_edit_hint_popover),
button);
+
+ gtk_widget_show (webdav_browser->priv->create_edit_hint_popover);
+
+ return;
+ }
+
+ webdav_browser_prepare_popover (webdav_browser,
+ button == webdav_browser->priv->create_book_button,
+ button == webdav_browser->priv->create_calendar_button);
+
+ gtk_popover_set_relative_to (GTK_POPOVER (webdav_browser->priv->create_edit_popover), button);
+
+ g_signal_handlers_disconnect_by_data (webdav_browser->priv->create_edit_save_button, webdav_browser);
+
+ if (button == webdav_browser->priv->create_book_button)
+ callback = G_CALLBACK (webdav_browser_create_book_save_clicked_cb);
+ else if (button == webdav_browser->priv->create_calendar_button)
+ callback = G_CALLBACK (webdav_browser_create_calendar_save_clicked_cb);
+ else
+ callback = G_CALLBACK (webdav_browser_create_collection_save_clicked_cb);
+
+ g_signal_connect (webdav_browser->priv->create_edit_save_button, "clicked", callback, webdav_browser);
+
+ gtk_widget_show (webdav_browser->priv->create_edit_popover);
+
+ gtk_widget_grab_focus (webdav_browser->priv->create_edit_name_entry);
+}
+
+static void
+webdav_browser_edit_clicked_cb (GtkWidget *button,
+ EWebDAVBrowser *webdav_browser)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gchar *href;
+ gchar *display_name = NULL, *description = NULL;
+ guint editing_flags = E_EDITING_FLAG_NONE, supports = E_WEBDAV_RESOURCE_SUPPORTS_NONE;
+ GdkRGBA *rgba = NULL;
+ gboolean color_is_set = FALSE;
+ GCallback callback;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (GTK_IS_POPOVER (webdav_browser->priv->create_edit_popover));
+
+ href = webdav_browser_dup_selected_href (webdav_browser);
+ g_return_if_fail (href != NULL);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (webdav_browser->priv->tree_view));
+ g_return_if_fail (gtk_tree_selection_get_selected (selection, &model, &iter));
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_STRING_DISPLAY_NAME, &display_name,
+ COLUMN_STRING_DESCRIPTION, &description,
+ COLUMN_RGBA_COLOR, &rgba,
+ COLUMN_BOOL_COLOR_VISIBLE, &color_is_set,
+ COLUMN_UINT_EDITING_FLAGS, &editing_flags,
+ COLUMN_UINT_SUPPORTS, &supports,
+ -1);
+
+ webdav_browser_prepare_popover (webdav_browser,
+ (editing_flags & E_EDITING_FLAG_IS_BOOK) != 0,
+ (editing_flags & E_EDITING_FLAG_IS_CALENDAR) != 0);
+
+ if ((editing_flags & E_EDITING_FLAG_IS_CALENDAR) != 0) {
+ if (color_is_set && rgba)
+ gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER
(webdav_browser->priv->create_edit_color_chooser), rgba);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
(webdav_browser->priv->create_edit_event_check),
+ (supports & E_WEBDAV_RESOURCE_SUPPORTS_EVENTS) != 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
(webdav_browser->priv->create_edit_memo_check),
+ (supports & E_WEBDAV_RESOURCE_SUPPORTS_MEMOS) != 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
(webdav_browser->priv->create_edit_task_check),
+ (supports & E_WEBDAV_RESOURCE_SUPPORTS_TASKS) != 0);
+
+ gtk_widget_set_sensitive (webdav_browser->priv->create_edit_support_label, FALSE);
+ gtk_widget_set_sensitive (webdav_browser->priv->create_edit_event_check, FALSE);
+ gtk_widget_set_sensitive (webdav_browser->priv->create_edit_memo_check, FALSE);
+ gtk_widget_set_sensitive (webdav_browser->priv->create_edit_task_check, FALSE);
+ }
+
+ gtk_entry_set_text (GTK_ENTRY (webdav_browser->priv->create_edit_name_entry), display_name);
+
+ if (description) {
+ gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW
(webdav_browser->priv->create_edit_description_textview)),
+ description, -1);
+ }
+
+ gtk_popover_set_relative_to (GTK_POPOVER (webdav_browser->priv->create_edit_popover), button);
+
+ g_signal_handlers_disconnect_by_data (webdav_browser->priv->create_edit_save_button, webdav_browser);
+
+ if ((editing_flags & E_EDITING_FLAG_IS_BOOK) != 0)
+ callback = G_CALLBACK (webdav_browser_edit_book_save_clicked_cb);
+ else if ((editing_flags & E_EDITING_FLAG_IS_CALENDAR) != 0)
+ callback = G_CALLBACK (webdav_browser_edit_calendar_save_clicked_cb);
+ else
+ callback = G_CALLBACK (webdav_browser_edit_collection_save_clicked_cb);
+
+ g_signal_connect (webdav_browser->priv->create_edit_save_button, "clicked", callback, webdav_browser);
+
+ gtk_widget_show (webdav_browser->priv->create_edit_popover);
+
+ gtk_widget_grab_focus (webdav_browser->priv->create_edit_name_entry);
+
+ g_free (href);
+ g_free (description);
+ g_free (display_name);
+
+ if (rgba)
+ gdk_rgba_free (rgba);
+}
+
+typedef struct _DeleteData {
+ GWeakRef *webdav_browser_weakref;
+ gchar *href;
+} DeleteData;
+
+static void
+delete_data_free (gpointer ptr)
+{
+ DeleteData *dd = ptr;
+
+ if (dd) {
+ e_weak_ref_free (dd->webdav_browser_weakref);
+ g_free (dd->href);
+ g_free (dd);
+ }
+}
+
+static void
+webdav_browser_delete_done (EWebDAVBrowser *webdav_browser,
+ gpointer user_data)
+{
+ GtkTreeRowReference *reference;
+ const gchar *href = user_data;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (href != NULL);
+
+ reference = g_hash_table_lookup (webdav_browser->priv->href_to_reference, href);
+ if (reference) {
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ model = gtk_tree_row_reference_get_model (reference);
+ path = gtk_tree_row_reference_get_path (reference);
+
+ if (gtk_tree_model_get_iter (model, &iter, path))
+ gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
+
+ gtk_tree_path_free (path);
+ }
+}
+
+static void
+webdav_browser_delete_thread (EAlertSinkThreadJobData *job_data,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EWebDAVBrowser *webdav_browser;
+ EWebDAVSession *session;
+ DeleteData *dd = user_data;
+
+ g_return_if_fail (dd != NULL);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return;
+
+ webdav_browser = g_weak_ref_get (dd->webdav_browser_weakref);
+ if (!webdav_browser)
+ return;
+
+ session = webdav_browser_ref_session (webdav_browser);
+ if (!session) {
+ g_object_unref (webdav_browser);
+ return;
+ }
+
+ if (e_webdav_session_delete_sync (session, dd->href, E_WEBDAV_DEPTH_THIS_AND_CHILDREN, NULL,
cancellable, error)) {
+ webdav_browser_schedule_ui_update (webdav_browser,
+ webdav_browser_delete_done, g_strdup (dd->href), g_free);
+ } else {
+ webdav_browser_schedule_ui_update (webdav_browser, NULL, NULL, NULL);
+ }
+
+ g_object_unref (webdav_browser);
+ g_object_unref (session);
+}
+
+static void
+webdav_browser_delete_clicked_cb (GtkWidget *button,
+ EWebDAVBrowser *webdav_browser)
+{
+ GtkWidget *parent;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ gchar *href;
+ gchar *display_name = NULL;
+ const gchar *question_tag, *description, *error_message;
+ guint editing_flags = E_EDITING_FLAG_NONE;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+
+ href = webdav_browser_dup_selected_href (webdav_browser);
+ g_return_if_fail (href != NULL);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (webdav_browser->priv->tree_view));
+ g_return_if_fail (gtk_tree_selection_get_selected (selection, &model, &iter));
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_STRING_DISPLAY_NAME, &display_name,
+ COLUMN_UINT_EDITING_FLAGS, &editing_flags,
+ -1);
+
+ if ((editing_flags & E_EDITING_FLAG_IS_BOOK) != 0) {
+ question_tag = "addressbook:ask-delete-remote-addressbook";
+ description = _("Deleting book…");
+ error_message = _("Failed to delete book");
+ } else if ((editing_flags & E_EDITING_FLAG_IS_CALENDAR) != 0) {
+ question_tag = "calendar:prompt-delete-remote-calendar";
+ description = _("Deleting calendar…");
+ error_message = _("Failed to delete calendar");
+ } else {
+ question_tag = "system:prompt-delete-remote-collection";
+ description = _("Deleting collection…");
+ error_message = _("Failed to delete collection");
+ }
+
+ parent = gtk_widget_get_toplevel (button);
+ if (!GTK_IS_WINDOW (parent))
+ parent = NULL;
+
+ if (e_alert_run_dialog_for_args (parent ? GTK_WINDOW (parent) : NULL, question_tag, display_name,
NULL) == GTK_RESPONSE_YES) {
+ EActivity *activity;
+ DeleteData *dd;
+
+ dd = g_new0 (DeleteData, 1);
+ dd->webdav_browser_weakref = e_weak_ref_new (webdav_browser);
+ dd->href = g_strdup (href);
+
+ e_webdav_browser_abort (webdav_browser);
+
+ g_clear_object (&webdav_browser->priv->cancellable);
+
+ webdav_browser_change_busy_state (webdav_browser, TRUE);
+
+ activity = e_alert_sink_submit_thread_job (E_ALERT_SINK (webdav_browser),
+ description, "system:generic-error", error_message,
+ webdav_browser_delete_thread, dd,
+ delete_data_free);
+
+ if (activity) {
+ webdav_browser->priv->cancellable = e_activity_get_cancellable (activity);
+
+ if (webdav_browser->priv->cancellable)
+ g_object_ref (webdav_browser->priv->cancellable);
+
+ e_activity_bar_set_activity (webdav_browser->priv->activity_bar, activity);
+
+ g_object_unref (activity);
+ } else {
+ webdav_browser_change_busy_state (webdav_browser, FALSE);
+ webdav_browser_schedule_ui_update (webdav_browser, NULL, NULL, NULL);
+ }
+ }
+
+ g_free (href);
+ g_free (display_name);
+}
+
+static void
+webdav_browser_refresh (EWebDAVBrowser *webdav_browser)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+
+ gtk_widget_set_sensitive (webdav_browser->priv->refresh_button, webdav_browser->priv->session !=
NULL);
+
+ gtk_tree_store_clear (GTK_TREE_STORE (gtk_tree_model_sort_get_model (GTK_TREE_MODEL_SORT (
+ gtk_tree_view_get_model (GTK_TREE_VIEW (webdav_browser->priv->tree_view))))));
+
+ g_hash_table_remove_all (webdav_browser->priv->href_to_reference);
+
+ g_mutex_lock (&webdav_browser->priv->property_lock);
+ g_slist_free_full (webdav_browser->priv->resources, resource_data_free);
+ webdav_browser->priv->resources = NULL;
+ g_mutex_unlock (&webdav_browser->priv->property_lock);
+
+ if (webdav_browser->priv->session) {
+ ESource *source;
+ ESourceWebdav *webdav_extension;
+ SoupURI *suri;
+
+ source = e_soup_session_get_source (E_SOUP_SESSION (webdav_browser->priv->session));
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND));
+
+ webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+ suri = e_source_webdav_dup_soup_uri (webdav_extension);
+
+ g_return_if_fail (suri != NULL);
+
+ gtk_label_set_text (webdav_browser->priv->url_label, soup_uri_get_host (suri));
+
+ soup_uri_free (suri);
+
+ webdav_browser_search_user_home (webdav_browser);
+ } else {
+ gtk_label_set_text (webdav_browser->priv->url_label, "");
+ }
+}
+
+static gint
+webdav_browser_compare_iters_cb (GtkTreeModel *model,
+ GtkTreeIter *aa,
+ GtkTreeIter *bb,
+ gpointer user_data)
+{
+ gchar *aa_display_name = NULL, *bb_display_name = NULL;
+ gint res;
+
+ if (!aa || !bb)
+ return aa == bb ? 0 : bb ? -1 : 1;
+
+ gtk_tree_model_get (model, aa, COLUMN_STRING_DISPLAY_NAME, &aa_display_name, -1);
+ gtk_tree_model_get (model, bb, COLUMN_STRING_DISPLAY_NAME, &bb_display_name, -1);
+
+ if (!aa_display_name || !bb_display_name)
+ res = g_strcmp0 (aa_display_name, bb_display_name);
+ else
+ res = g_utf8_collate (aa_display_name, bb_display_name);
+
+ g_free (aa_display_name);
+ g_free (bb_display_name);
+
+ return res;
+}
+
+static GtkWidget *
+webdav_browser_tree_view_new (EWebDAVBrowser *webdav_browser)
+{
+ GtkTreeStore *tree_store;
+ GtkTreeView *tree_view;
+ GtkTreeViewColumn *column;
+ GtkTreeModel *sort_model;
+ GtkCellRenderer *cell_renderer;
+
+ g_return_val_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser), NULL);
+
+ tree_store = gtk_tree_store_new (N_COLUMNS,
+ G_TYPE_STRING, /* COLUMN_STRING_DISPLAY_NAME */
+ G_TYPE_STRING, /* COLUMN_STRING_TYPE */
+ G_TYPE_STRING, /* COLUMN_STRING_HREF */
+ G_TYPE_STRING, /* COLUMN_STRING_DESCRIPTION */
+ G_TYPE_STRING, /* COLUMN_STRING_ICON_NAME */
+ G_TYPE_BOOLEAN, /* COLUMN_BOOL_ICON_VISIBLE */
+ GDK_TYPE_RGBA, /* COLUMN_RGBA_COLOR */
+ G_TYPE_BOOLEAN, /* COLUMN_BOOL_COLOR_VISIBLE */
+ G_TYPE_BOOLEAN, /* COLUMN_BOOL_CHILDREN_LOADED */
+ G_TYPE_UINT, /* COLUMN_UINT_EDITING_FLAGS */
+ G_TYPE_UINT /* COLUMN_UINT_SUPPORTS */
+ );
+
+ sort_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree_store));
+ gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort_model),
+ webdav_browser_compare_iters_cb, NULL, NULL);
+
+ tree_view = GTK_TREE_VIEW (gtk_tree_view_new_with_model (sort_model));
+
+ g_object_unref (sort_model);
+ g_object_unref (tree_store);
+
+ gtk_tree_view_set_reorderable (tree_view, FALSE);
+ gtk_tree_view_set_tooltip_column (tree_view, COLUMN_STRING_DESCRIPTION);
+
+ /* Column: Name */
+
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_column_set_resizable (column, TRUE);
+ gtk_tree_view_column_set_title (column, _("Name"));
+
+ cell_renderer = gtk_cell_renderer_pixbuf_new ();
+ g_object_set (cell_renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL);
+ gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
+
+ gtk_tree_view_column_add_attribute (column, cell_renderer, "icon-name", COLUMN_STRING_ICON_NAME);
+ gtk_tree_view_column_add_attribute (column, cell_renderer, "visible", COLUMN_BOOL_ICON_VISIBLE);
+
+ cell_renderer = e_cell_renderer_color_new ();
+ gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
+
+ gtk_tree_view_column_add_attribute (column, cell_renderer, "rgba", COLUMN_RGBA_COLOR);
+ gtk_tree_view_column_add_attribute (column, cell_renderer, "visible", COLUMN_BOOL_COLOR_VISIBLE);
+
+ cell_renderer = gtk_cell_renderer_text_new ();
+ g_object_set (cell_renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+ gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
+
+ gtk_tree_view_column_add_attribute (column, cell_renderer, "text", COLUMN_STRING_DISPLAY_NAME);
+
+ gtk_tree_view_append_column (tree_view, column);
+ gtk_tree_view_set_expander_column (tree_view, column);
+
+ /* Column: Type */
+
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_expand (column, FALSE);
+ gtk_tree_view_column_set_resizable (column, TRUE);
+ gtk_tree_view_column_set_title (column, _("Type"));
+
+ cell_renderer = gtk_cell_renderer_text_new ();
+ g_object_set (cell_renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+ gtk_tree_view_column_pack_start (column, cell_renderer, TRUE);
+
+ gtk_tree_view_column_add_attribute (column, cell_renderer, "text", COLUMN_STRING_TYPE);
+
+ gtk_tree_view_append_column (tree_view, column);
+
+ return GTK_WIDGET (tree_view);
+}
+
+static void
+webdav_browser_create_popover (EWebDAVBrowser *webdav_browser)
+{
+ GtkWidget *widget, *label;
+ GtkGrid *grid;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (webdav_browser->priv->create_edit_popover == NULL);
+
+ widget = gtk_grid_new ();
+ grid = GTK_GRID (widget);
+ gtk_grid_set_column_spacing (grid, 6);
+ gtk_grid_set_row_spacing (grid, 6);
+
+ widget = gtk_label_new_with_mnemonic (_("_Name:"));
+ gtk_widget_set_halign (widget, GTK_ALIGN_END);
+ gtk_grid_attach (grid, widget, 0, 0, 1, 1);
+ label = widget;
+
+ widget = gtk_entry_new ();
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+ gtk_grid_attach (grid, widget, 1, 0, 1, 1);
+ webdav_browser->priv->create_edit_name_entry = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Color:"));
+ gtk_widget_set_halign (widget, GTK_ALIGN_END);
+ gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+ webdav_browser->priv->create_edit_color_label = widget;
+ label = widget;
+
+ widget = gtk_color_button_new ();
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+ gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+ webdav_browser->priv->create_edit_color_chooser = widget;
+
+ widget = gtk_label_new (_("For Components:"));
+ gtk_widget_set_halign (widget, GTK_ALIGN_END);
+ gtk_widget_set_valign (widget, GTK_ALIGN_START);
+ gtk_grid_attach (grid, widget, 0, 2, 1, 1);
+ webdav_browser->priv->create_edit_support_label = widget;
+
+ widget = gtk_check_button_new_with_mnemonic (_("_Events"));
+ gtk_grid_attach (grid, widget, 1, 2, 1, 1);
+ webdav_browser->priv->create_edit_event_check = widget;
+
+ widget = gtk_check_button_new_with_mnemonic (_("_Memos"));
+ gtk_grid_attach (grid, widget, 1, 3, 1, 1);
+ webdav_browser->priv->create_edit_memo_check = widget;
+
+ widget = gtk_check_button_new_with_mnemonic (_("_Tasks"));
+ gtk_grid_attach (grid, widget, 1, 4, 1, 1);
+ webdav_browser->priv->create_edit_task_check = widget;
+
+ widget = gtk_label_new_with_mnemonic (_("_Description:"));
+ gtk_widget_set_halign (widget, GTK_ALIGN_END);
+ gtk_widget_set_valign (widget, GTK_ALIGN_START);
+ gtk_grid_attach (grid, widget, 0, 5, 1, 1);
+ webdav_browser->priv->create_edit_description_label = widget;
+ label = widget;
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_grid_attach (grid, widget, 1, 5, 1, 1);
+ webdav_browser->priv->create_edit_description_scrolled_window = widget;
+
+ widget = gtk_text_view_new ();
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (widget), GTK_WRAP_WORD);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget);
+ e_spell_text_view_attach (GTK_TEXT_VIEW (widget));
+ gtk_container_add (GTK_CONTAINER (webdav_browser->priv->create_edit_description_scrolled_window),
widget);
+ webdav_browser->priv->create_edit_description_textview = widget;
+
+ widget = gtk_button_new_with_mnemonic (_("_Save"));
+ gtk_widget_set_halign (widget, GTK_ALIGN_END);
+ gtk_grid_attach (grid, widget, 0, 6, 2, 1);
+ webdav_browser->priv->create_edit_save_button = widget;
+
+ gtk_widget_show_all (GTK_WIDGET (grid));
+
+ widget = gtk_popover_new (GTK_WIDGET (webdav_browser));
+ gtk_popover_set_position (GTK_POPOVER (widget), GTK_POS_BOTTOM);
+ gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (grid));
+ gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
+ webdav_browser->priv->create_edit_popover = widget;
+
+ label = gtk_label_new ("");
+ gtk_widget_show (label);
+ webdav_browser->priv->create_edit_hint_label = label;
+
+ widget = gtk_popover_new (webdav_browser->priv->create_edit_popover);
+ gtk_popover_set_position (GTK_POPOVER (widget), GTK_POS_BOTTOM);
+ gtk_popover_set_modal (GTK_POPOVER (widget), FALSE);
+ gtk_container_add (GTK_CONTAINER (widget), label);
+ gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
+ webdav_browser->priv->create_edit_hint_popover = widget;
+}
+
+static void
+webdav_browser_submit_alert (EAlertSink *alert_sink,
+ EAlert *alert)
+{
+ EWebDAVBrowser *webdav_browser;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (alert_sink));
+
+ webdav_browser = E_WEBDAV_BROWSER (alert_sink);
+
+ e_alert_bar_submit_alert (webdav_browser->priv->alert_bar, alert);
+}
+
+static void
+webdav_browser_set_credentials_prompter (EWebDAVBrowser *webdav_browser,
+ ECredentialsPrompter *credentials_prompter)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (credentials_prompter));
+ g_return_if_fail (webdav_browser->priv->credentials_prompter == NULL);
+
+ webdav_browser->priv->credentials_prompter = g_object_ref (credentials_prompter);
+}
+
+static void
+webdav_browser_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CREDENTIALS_PROMPTER:
+ webdav_browser_set_credentials_prompter (
+ E_WEBDAV_BROWSER (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_SOURCE:
+ e_webdav_browser_set_source (
+ E_WEBDAV_BROWSER (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+webdav_browser_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_CREDENTIALS_PROMPTER:
+ g_value_set_object (
+ value,
+ e_webdav_browser_get_credentials_prompter (
+ E_WEBDAV_BROWSER (object)));
+ return;
+
+ case PROP_SOURCE:
+ g_value_take_object (
+ value,
+ e_webdav_browser_ref_source (
+ E_WEBDAV_BROWSER (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+webdav_browser_dispose (GObject *object)
+{
+ EWebDAVBrowser *webdav_browser = E_WEBDAV_BROWSER (object);
+
+ g_mutex_lock (&webdav_browser->priv->property_lock);
+
+ if (webdav_browser->priv->update_ui_id) {
+ g_source_remove (webdav_browser->priv->update_ui_id);
+ webdav_browser->priv->update_ui_id = 0;
+ }
+
+ if (webdav_browser->priv->cancellable) {
+ g_cancellable_cancel (webdav_browser->priv->cancellable);
+ g_clear_object (&webdav_browser->priv->cancellable);
+ }
+
+ g_clear_object (&webdav_browser->priv->session);
+ g_clear_object (&webdav_browser->priv->credentials_prompter);
+
+ g_mutex_unlock (&webdav_browser->priv->property_lock);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_webdav_browser_parent_class)->dispose (object);
+}
+
+static void
+webdav_browser_finalize (GObject *object)
+{
+ EWebDAVBrowser *webdav_browser = E_WEBDAV_BROWSER (object);
+
+ g_slist_free_full (webdav_browser->priv->resources, resource_data_free);
+ g_hash_table_destroy (webdav_browser->priv->href_to_reference);
+
+ g_mutex_clear (&webdav_browser->priv->property_lock);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_webdav_browser_parent_class)->finalize (object);
+}
+
+static void
+webdav_browser_constructed (GObject *object)
+{
+ EWebDAVBrowser *webdav_browser = E_WEBDAV_BROWSER (object);
+ GtkTreeSelection *selection;
+ GtkWidget *container;
+ GtkWidget *widget;
+ GtkGrid *grid;
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_webdav_browser_parent_class)->constructed (object);
+
+ grid = GTK_GRID (webdav_browser);
+ gtk_grid_set_column_spacing (grid, 6);
+ gtk_grid_set_row_spacing (grid, 6);
+
+ widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+ gtk_grid_attach (grid, widget, 0, 0, 2, 1);
+
+ container = widget;
+
+ widget = gtk_label_new (_("WebDAV server:"));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+
+ widget = gtk_label_new ("");
+ webdav_browser->priv->url_label = GTK_LABEL (widget);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+
+ widget = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (widget), GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
+ gtk_widget_set_hexpand (widget, TRUE);
+ gtk_widget_set_vexpand (widget, TRUE);
+ gtk_grid_attach (grid, widget, 0, 1, 1, 1);
+
+ container = widget;
+
+ widget = webdav_browser_tree_view_new (webdav_browser);
+ gtk_container_add (GTK_CONTAINER (container), widget);
+ webdav_browser->priv->tree_view = widget;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (webdav_browser_selection_changed_cb), webdav_browser);
+
+ g_signal_connect (widget, "row-expanded",
+ G_CALLBACK (webdav_browser_row_expanded_cb), webdav_browser);
+
+ widget = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_START);
+ gtk_box_set_spacing (GTK_BOX (widget), 6);
+ gtk_grid_attach (grid, widget, 1, 1, 1, 1);
+
+ container = widget;
+
+ widget = gtk_button_new_with_mnemonic (_("Create _Book"));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ webdav_browser->priv->create_book_button = widget;
+
+ g_signal_connect (webdav_browser->priv->create_book_button, "clicked",
+ G_CALLBACK (webdav_browser_create_clicked_cb), webdav_browser);
+
+ widget = gtk_button_new_with_mnemonic (_("Create _Calendar"));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ webdav_browser->priv->create_calendar_button = widget;
+
+ g_signal_connect (webdav_browser->priv->create_calendar_button, "clicked",
+ G_CALLBACK (webdav_browser_create_clicked_cb), webdav_browser);
+
+ widget = gtk_button_new_with_mnemonic (_("Create Collectio_n"));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ webdav_browser->priv->create_collection_button = widget;
+
+ g_signal_connect (webdav_browser->priv->create_collection_button, "clicked",
+ G_CALLBACK (webdav_browser_create_clicked_cb), webdav_browser);
+
+ widget = gtk_button_new_with_mnemonic (_("_Edit"));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ webdav_browser->priv->edit_button = widget;
+
+ g_signal_connect (webdav_browser->priv->edit_button, "clicked",
+ G_CALLBACK (webdav_browser_edit_clicked_cb), webdav_browser);
+
+ widget = gtk_button_new_with_mnemonic (_("_Delete"));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ webdav_browser->priv->delete_button = widget;
+
+ g_signal_connect (webdav_browser->priv->delete_button, "clicked",
+ G_CALLBACK (webdav_browser_delete_clicked_cb), webdav_browser);
+
+ /* widget = gtk_button_new_with_mnemonic (_("_Permissions"));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ webdav_browser->priv->permissions_button = widget; */
+
+ widget = gtk_button_new_with_mnemonic (_("_Refresh"));
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ webdav_browser->priv->refresh_button = widget;
+
+ g_signal_connect_swapped (webdav_browser->priv->refresh_button, "clicked",
+ G_CALLBACK (webdav_browser_refresh), webdav_browser);
+
+ gtk_widget_show_all (GTK_WIDGET (grid));
+
+ /* Add the bars after show_all call, because they manage
+ their visibility on their own */
+ widget = e_alert_bar_new ();
+ gtk_widget_set_margin_bottom (widget, 6);
+ gtk_grid_attach (grid, widget, 0, 2, 2, 1);
+ webdav_browser->priv->alert_bar = E_ALERT_BAR (widget);
+
+ widget = e_activity_bar_new ();
+ gtk_widget_set_margin_bottom (widget, 6);
+ gtk_grid_attach (grid, widget, 0, 3, 2, 1);
+ webdav_browser->priv->activity_bar = E_ACTIVITY_BAR (widget);
+
+ webdav_browser_create_popover (webdav_browser);
+}
+
+static void
+e_webdav_browser_class_init (EWebDAVBrowserClass *klass)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (klass, sizeof (EWebDAVBrowserPrivate));
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->set_property = webdav_browser_set_property;
+ object_class->get_property = webdav_browser_get_property;
+ object_class->dispose = webdav_browser_dispose;
+ object_class->finalize = webdav_browser_finalize;
+ object_class->constructed = webdav_browser_constructed;
+
+ /**
+ * EWebDAVBrowser:credentials-prompter:
+ *
+ * The #ECredentialsPrompter used to ask for credentials when needed.
+ *
+ * Since: 3.26
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_CREDENTIALS_PROMPTER,
+ g_param_spec_object (
+ "credentials-prompter",
+ "Credentials Prompter",
+ "an ECredentialsPrompter",
+ E_TYPE_CREDENTIALS_PROMPTER,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * EWebDAVBrowser:source:
+ *
+ * The #ESource currently used for the GUI. It can be %NULL.
+ *
+ * Since: 3.26
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_SOURCE,
+ g_param_spec_object (
+ "source",
+ "Source",
+ "an ESource",
+ E_TYPE_SOURCE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
+
+static void
+webdav_browser_alert_sink_init (EAlertSinkInterface *iface)
+{
+ iface->submit_alert = webdav_browser_submit_alert;
+}
+
+static void
+e_webdav_browser_init (EWebDAVBrowser *webdav_browser)
+{
+ webdav_browser->priv = G_TYPE_INSTANCE_GET_PRIVATE (webdav_browser, E_TYPE_WEBDAV_BROWSER,
EWebDAVBrowserPrivate);
+
+ g_mutex_init (&webdav_browser->priv->property_lock);
+
+ webdav_browser->priv->href_to_reference = g_hash_table_new_full (camel_strcase_hash,
camel_strcase_equal,
+ g_free, (GDestroyNotify) gtk_tree_row_reference_free);
+}
+
+/**
+ * e_webdav_browser_new:
+ * @credentials_prompter: an #ECredentialsPrompter
+ *
+ * Creates a new #EWebDAVBrowser instance.
+ *
+ * Returns: (transfer full): an #EWebDAVBrowser as a #GtkWidget
+ *
+ * Since: 3.26
+ **/
+GtkWidget *
+e_webdav_browser_new (ECredentialsPrompter *credentials_prompter)
+{
+ return g_object_new (E_TYPE_WEBDAV_BROWSER,
+ "credentials-prompter", credentials_prompter,
+ NULL);
+}
+
+/**
+ * e_webdav_browser_get_credentials_prompter:
+ * @webdav_browser: an #EWebDAVBrowser
+ *
+ * Returns: (transfer none): an #ECredentialsPrompter used to call
+ * of e_webdav_browser_new()
+ *
+ * Since: 3.26
+ **/
+ECredentialsPrompter *
+e_webdav_browser_get_credentials_prompter (EWebDAVBrowser *webdav_browser)
+{
+ g_return_val_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser), NULL);
+
+ return webdav_browser->priv->credentials_prompter;
+}
+
+/**
+ * e_webdav_browser_set_source:
+ * @webdav_browser: an #EWebDAVBrowser
+ * @source: (nullable): an #ESource
+ *
+ * Sets the @source to be the one used for the @webdav_browser.
+ * It can be %NULL, to have none set.
+ *
+ * Since: 3.26
+ **/
+void
+e_webdav_browser_set_source (EWebDAVBrowser *webdav_browser,
+ ESource *source)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+ if (source)
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ e_alert_bar_clear (webdav_browser->priv->alert_bar);
+
+ g_mutex_lock (&webdav_browser->priv->property_lock);
+
+ if (!source && !webdav_browser->priv->session) {
+ g_mutex_unlock (&webdav_browser->priv->property_lock);
+ return;
+ }
+
+ g_clear_object (&webdav_browser->priv->session);
+
+ if (source) {
+ webdav_browser->priv->session = e_webdav_session_new (source);
+
+ if (webdav_browser->priv->session)
+ e_soup_session_setup_logging (E_SOUP_SESSION (webdav_browser->priv->session),
g_getenv ("WEBDAV_DEBUG"));
+ }
+
+ g_mutex_unlock (&webdav_browser->priv->property_lock);
+
+ webdav_browser_refresh (webdav_browser);
+
+ g_object_notify (G_OBJECT (webdav_browser), "source");
+}
+
+/**
+ * e_webdav_browser_ref_source:
+ * @webdav_browser: an #EWebDAVBrowser
+ *
+ * Returns: (transfer full) (nullable): an #ESource, currently used by @webdav_browser;
+ * if not %NULL, then free with g_object_unref(), when no longer needed.
+ *
+ * Since: 3.26
+ **/
+ESource *
+e_webdav_browser_ref_source (EWebDAVBrowser *webdav_browser)
+{
+ ESource *source = NULL;
+
+ g_return_val_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser), NULL);
+
+ g_mutex_lock (&webdav_browser->priv->property_lock);
+
+ if (webdav_browser->priv->session) {
+ source = e_soup_session_get_source (E_SOUP_SESSION (webdav_browser->priv->session));
+ if (source)
+ g_object_ref (source);
+ }
+
+ g_mutex_unlock (&webdav_browser->priv->property_lock);
+
+ return source;
+}
+
+/**
+ * e_webdav_browser_abort:
+ * @webdav_browser: an #EWebDAVBrowser
+ *
+ * Aborts any ongoing operation. It does nothing, if no
+ * operation is running.
+ *
+ * Since: 3.26
+ **/
+void
+e_webdav_browser_abort (EWebDAVBrowser *webdav_browser)
+{
+ g_return_if_fail (E_IS_WEBDAV_BROWSER (webdav_browser));
+
+ if (webdav_browser->priv->cancellable)
+ g_cancellable_cancel (webdav_browser->priv->cancellable);
+}
diff --git a/src/e-util/e-webdav-browser.h b/src/e-util/e-webdav-browser.h
new file mode 100644
index 0000000..b043e5f
--- /dev/null
+++ b/src/e-util/e-webdav-browser.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__E_UTIL_H_INSIDE__) && !defined (LIBEUTIL_COMPILATION)
+#error "Only <e-util/e-util.h> should be included directly."
+#endif
+
+#ifndef E_WEBDAV_BROWSER_H
+#define E_WEBDAV_BROWSER_H
+
+#include <gtk/gtk.h>
+#include <libedataserverui/libedataserverui.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEBDAV_BROWSER \
+ (e_webdav_browser_get_type ())
+#define E_WEBDAV_BROWSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_WEBDAV_BROWSER, EWebDAVBrowser))
+#define E_WEBDAV_BROWSER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_WEBDAV_BROWSER, EWebDAVBrowserClass))
+#define E_IS_WEBDAV_BROWSER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_WEBDAV_BROWSER))
+#define E_IS_WEBDAV_BROWSER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_WEBDAV_BROWSER))
+#define E_WEBDAV_BROWSER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_WEBDAV_BROWSER, EWebDAVBrowserClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EWebDAVBrowser EWebDAVBrowser;
+typedef struct _EWebDAVBrowserClass EWebDAVBrowserClass;
+typedef struct _EWebDAVBrowserPrivate EWebDAVBrowserPrivate;
+
+/**
+ * EWebDAVBrowser:
+ *
+ * Contains only private data that should be read and manipulated using
+ * the functions below.
+ *
+ * Since: 3.26
+ **/
+struct _EWebDAVBrowser {
+ GtkGrid parent;
+ EWebDAVBrowserPrivate *priv;
+};
+
+struct _EWebDAVBrowserClass {
+ GtkGridClass parent_class;
+};
+
+GType e_webdav_browser_get_type (void) G_GNUC_CONST;
+GtkWidget * e_webdav_browser_new (ECredentialsPrompter *credentials_prompter);
+ECredentialsPrompter *
+ e_webdav_browser_get_credentials_prompter
+ (EWebDAVBrowser *webdav_browser);
+void e_webdav_browser_set_source (EWebDAVBrowser *webdav_browser,
+ ESource *source);
+ESource * e_webdav_browser_ref_source (EWebDAVBrowser *webdav_browser);
+void e_webdav_browser_abort (EWebDAVBrowser *webdav_browser);
+void e_webdav_browser_refresh (EWebDAVBrowser *webdav_browser);
+
+G_END_DECLS
+
+#endif /* E_WEBDAV_BROWSER_H */
diff --git a/src/modules/accounts-window/CMakeLists.txt b/src/modules/accounts-window/CMakeLists.txt
index 864076b..1c51b47 100644
--- a/src/modules/accounts-window/CMakeLists.txt
+++ b/src/modules/accounts-window/CMakeLists.txt
@@ -6,6 +6,8 @@ set(sources
accounts-window.c
e-accounts-window-editors.c
e-accounts-window-editors.h
+ e-webdav-browser-page.c
+ e-webdav-browser-page.h
)
set(extra_defines)
set(extra_cflags)
diff --git a/src/modules/accounts-window/accounts-window.c b/src/modules/accounts-window/accounts-window.c
index 6a8e07a..400fbc2 100644
--- a/src/modules/accounts-window/accounts-window.c
+++ b/src/modules/accounts-window/accounts-window.c
@@ -21,6 +21,7 @@
#include <glib-object.h>
#include "e-accounts-window-editors.h"
+#include "e-webdav-browser-page.h"
/* Module Entry Points */
void e_module_load (GTypeModule *type_module);
@@ -30,6 +31,7 @@ G_MODULE_EXPORT void
e_module_load (GTypeModule *type_module)
{
e_accounts_window_editors_type_register (type_module);
+ e_webdav_browser_page_type_register (type_module);
}
G_MODULE_EXPORT void
diff --git a/src/modules/accounts-window/e-webdav-browser-page.c
b/src/modules/accounts-window/e-webdav-browser-page.c
new file mode 100644
index 0000000..ade9008
--- /dev/null
+++ b/src/modules/accounts-window/e-webdav-browser-page.c
@@ -0,0 +1,236 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "evolution-config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "e-util/e-util.h"
+#include "shell/e-shell.h"
+
+#include "e-webdav-browser-page.h"
+
+/* Standard GObject macros */
+#define E_TYPE_WEBDAV_BROWSER_PAGE \
+ (e_webdav_browser_page_get_type ())
+#define E_WEBDAV_BROWSER_PAGE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_WEBDAV_BROWSER_PAGE, EWebDAVBrowserPage))
+#define E_WEBDAV_BROWSER_PAGE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_WEBDAV_BROWSER_PAGE, EWebDAVBrowserPageClass))
+#define E_IS_WEBDAV_BROWSER_PAGE(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_WEBDAV_BROWSER_PAGE))
+#define E_IS_WEBDAV_BROWSER_PAGE_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_WEBDAV_BROWSER_PAGE))
+#define E_WEBDAV_BROWSER_PAGE_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_WEBDAV_BROWSER_PAGE, EWebDAVBrowserPageClass))
+
+typedef struct _EWebDAVBrowserPage EWebDAVBrowserPage;
+typedef struct _EWebDAVBrowserPageClass EWebDAVBrowserPageClass;
+
+struct _EWebDAVBrowserPage {
+ EExtension parent;
+
+ GtkWidget *browse_button;
+ EWebDAVBrowser *webdav_browser;
+ gint page_index;
+};
+
+struct _EWebDAVBrowserPageClass {
+ EExtensionClass parent_class;
+};
+
+GType e_webdav_browser_page_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_DYNAMIC_TYPE (EWebDAVBrowserPage, e_webdav_browser_page, E_TYPE_EXTENSION)
+
+static void
+webdav_browser_page_selection_changed_cb (EAccountsWindow *accounts_window,
+ ESource *source,
+ gpointer user_data)
+{
+ EWebDAVBrowserPage *page = user_data;
+ gboolean has_path = FALSE;
+
+ g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+ g_return_if_fail (E_IS_WEBDAV_BROWSER_PAGE (page));
+
+ if (source && e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND)) {
+ gchar *path;
+
+ path = e_source_webdav_dup_resource_path (e_source_get_extension (source,
E_SOURCE_EXTENSION_WEBDAV_BACKEND));
+
+ has_path = path && *path;
+
+ g_free (path);
+ }
+
+ gtk_widget_set_sensitive (page->browse_button, has_path);
+}
+
+static void
+webdav_browser_page_browse_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ EWebDAVBrowserPage *page = user_data;
+ EAccountsWindow *accounts_window;
+ ESource *source;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER_PAGE (page));
+
+ accounts_window = E_ACCOUNTS_WINDOW (e_extension_get_extensible (E_EXTENSION (page)));
+ g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+ source = e_accounts_window_ref_selected_source (accounts_window);
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ e_accounts_window_activate_page (accounts_window, page->page_index);
+ e_webdav_browser_set_source (page->webdav_browser, source);
+
+ g_object_unref (source);
+}
+
+static void
+webdav_browser_back_button_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ EWebDAVBrowserPage *page = user_data;
+ EAccountsWindow *accounts_window;
+
+ g_return_if_fail (E_IS_WEBDAV_BROWSER_PAGE (page));
+
+ accounts_window = E_ACCOUNTS_WINDOW (e_extension_get_extensible (E_EXTENSION (page)));
+ g_return_if_fail (E_IS_ACCOUNTS_WINDOW (accounts_window));
+
+ e_webdav_browser_abort (page->webdav_browser);
+ e_webdav_browser_set_source (page->webdav_browser, NULL);
+
+ e_accounts_window_activate_page (accounts_window, -1);
+}
+
+static void
+webdav_browser_page_constructed (GObject *object)
+{
+ EAccountsWindow *accounts_window;
+ ECredentialsPrompter *credentials_prompter;
+ EWebDAVBrowserPage *page;
+ EShell *shell;
+ GtkButtonBox *button_box;
+ GtkWidget *widget;
+ GtkWidget *vbox;
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_webdav_browser_page_parent_class)->constructed (object);
+
+ page = E_WEBDAV_BROWSER_PAGE (object);
+ accounts_window = E_ACCOUNTS_WINDOW (e_extension_get_extensible (E_EXTENSION (page)));
+
+ g_signal_connect (accounts_window, "selection-changed",
+ G_CALLBACK (webdav_browser_page_selection_changed_cb), object);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ gtk_widget_show (vbox);
+
+ shell = e_shell_get_default ();
+ /* Can be NULL in test-accounts-window */
+ if (shell) {
+ credentials_prompter = e_shell_get_credentials_prompter (shell);
+ g_object_ref (credentials_prompter);
+ } else {
+ credentials_prompter = e_credentials_prompter_new (e_accounts_window_get_registry
(accounts_window));
+ }
+
+ widget = e_webdav_browser_new (credentials_prompter);
+ g_object_set (G_OBJECT (widget),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "vexpand", TRUE,
+ "valign", GTK_ALIGN_FILL,
+ NULL);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, TRUE, TRUE, 0);
+ page->webdav_browser = E_WEBDAV_BROWSER (widget);
+
+ g_object_unref (credentials_prompter);
+
+ widget = e_dialog_button_new_with_icon ("go-previous", _("_Back"));
+ g_object_set (G_OBJECT (widget),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_END,
+ "vexpand", FALSE,
+ "valign", GTK_ALIGN_START,
+ NULL);
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+
+ g_signal_connect (widget, "clicked",
+ G_CALLBACK (webdav_browser_back_button_clicked_cb), page);
+
+ page->page_index = e_accounts_window_add_page (accounts_window, vbox);
+
+ button_box = e_accounts_window_get_button_box (accounts_window);
+
+ widget = gtk_label_new ("");
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (button_box), widget, FALSE, FALSE, 0);
+
+ widget = gtk_button_new_with_mnemonic (_("_Browse"));
+ gtk_widget_set_sensitive (widget, FALSE);
+ gtk_widget_set_tooltip_text (widget, _("Browse a WebDAV (CalDAV or CardDAV) server and create, edit
or delete address books, calendars, memo lists or task lists there"));
+ gtk_widget_show (widget);
+ gtk_box_pack_start (GTK_BOX (button_box), widget, FALSE, FALSE, 0);
+ page->browse_button = widget;
+
+ g_signal_connect (widget, "clicked",
+ G_CALLBACK (webdav_browser_page_browse_button_clicked_cb), page);
+}
+
+static void
+e_webdav_browser_page_class_init (EWebDAVBrowserPageClass *class)
+{
+ GObjectClass *object_class;
+ EExtensionClass *extension_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = webdav_browser_page_constructed;
+
+ extension_class = E_EXTENSION_CLASS (class);
+ extension_class->extensible_type = E_TYPE_ACCOUNTS_WINDOW;
+}
+
+static void
+e_webdav_browser_page_class_finalize (EWebDAVBrowserPageClass *class)
+{
+}
+
+static void
+e_webdav_browser_page_init (EWebDAVBrowserPage *extension)
+{
+}
+
+void
+e_webdav_browser_page_type_register (GTypeModule *type_module)
+{
+ /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
+ * function, so we have to wrap it with a public function in
+ * order to register types from a separate compilation unit. */
+ e_webdav_browser_page_register_type (type_module);
+}
diff --git a/src/modules/accounts-window/e-webdav-browser-page.h
b/src/modules/accounts-window/e-webdav-browser-page.h
new file mode 100644
index 0000000..75239e0
--- /dev/null
+++ b/src/modules/accounts-window/e-webdav-browser-page.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library 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 Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef E_WEBDAV_BROWSER_PAGE_H
+#define E_WEBDAV_BROWSER_PAGE_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void e_webdav_browser_page_type_register (GTypeModule *type_module);
+
+G_END_DECLS
+
+#endif /* E_WEBDAV_BROWSER_PAGE_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]