No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * David Trowbridge <trowbrds@cs.colorado.edu>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 #include <gio/gio.h>
31
32 #include <calendar/gui/e-cal-config.h>
33 #include <shell/es-event.h>
34 #include <e-util/e-util.h>
35 #include <e-util/e-util-private.h>
36 #include <e-util/e-dialog-utils.h>
37
38 #include <shell/e-shell.h>
39 #include <shell/e-shell-view.h>
40
41 #include "url-editor-dialog.h"
42 #include "publish-format-fb.h"
43 #include "publish-format-ical.h"
44
45 #ifdef HAVE_LIBNOTIFY
46 #include <libnotify/notify.h>
47 #endif
48
49 static GtkListStore *store = NULL;
50 static GHashTable *uri_timeouts = NULL;
51 static GSList *publish_uris = NULL;
52 static GSList *queued_publishes = NULL;
53 static gint online = 0;
54
55 static GSList *error_queue = NULL;
56 static GStaticMutex error_queue_lock = G_STATIC_MUTEX_INIT;
57 static guint error_queue_show_idle_id = 0;
58 static void error_queue_add (gchar *descriptions, GError *error);
59
60 gint e_plugin_lib_enable (EPlugin *ep, gint enable);
61 GtkWidget *publish_calendar_locations (EPlugin *epl, EConfigHookItemFactoryData *data);
62 static void update_timestamp (EPublishUri *uri);
63 static void publish (EPublishUri *uri, gboolean can_report_success);
64
65 static GtkStatusIcon *status_icon = NULL;
66 static guint status_icon_timeout_id = 0;
67 #ifdef HAVE_LIBNOTIFY
68 static NotifyNotification *notify = NULL;
69
70 static gboolean
71 show_notify_cb (gpointer data)
72 {
73 return notify && !notify_notification_show (notify, NULL);
74 }
75 #endif
76
77 static gboolean
78 remove_notification (gpointer data)
79 {
80 if (status_icon_timeout_id)
81 g_source_remove (status_icon_timeout_id);
82 status_icon_timeout_id = 0;
83
84 #ifdef HAVE_LIBNOTIFY
85 if (notify)
86 notify_notification_close (notify, NULL);
87 notify = NULL;
88 #endif
89
90 gtk_status_icon_set_visible (status_icon, FALSE);
91 g_object_unref (status_icon);
92 status_icon = NULL;
93
94 return FALSE;
95 }
96
97 static void
98 update_publish_notification (GtkMessageType msg_type,
99 const gchar *msg_text)
100 {
101 static GString *actual_msg = NULL;
102 #ifdef HAVE_LIBNOTIFY
103 static gboolean can_notify = TRUE;
104 #endif
105 gboolean new_icon = !status_icon;
106 const gchar *stock_name;
107
108 g_return_if_fail (msg_text != NULL);
109
110 if (new_icon) {
111 status_icon = gtk_status_icon_new ();
112 if (actual_msg) {
113 g_string_free (actual_msg, TRUE);
114 actual_msg = NULL;
115 }
116 } else if (status_icon_timeout_id) {
117 g_source_remove (status_icon_timeout_id);
118 }
119
120 switch (msg_type) {
121 case GTK_MESSAGE_WARNING:
122 stock_name = GTK_STOCK_DIALOG_WARNING;
123 break;
124 case GTK_MESSAGE_ERROR:
125 stock_name = GTK_STOCK_DIALOG_ERROR;
126 break;
127 default:
128 stock_name = GTK_STOCK_DIALOG_INFO;
129 break;
130 }
131
132 if (!actual_msg) {
133 actual_msg = g_string_new (msg_text);
134 } else {
135 g_string_append (actual_msg, "\n");
136 g_string_append (actual_msg, msg_text);
137 }
138
139 gtk_status_icon_set_from_stock (status_icon, stock_name);
140 gtk_status_icon_set_tooltip_text (status_icon, actual_msg->str);
141
142 #ifdef HAVE_LIBNOTIFY
143 if (can_notify) {
144 if (notify) {
145 notify_notification_update (notify, _("Calendar Publishing"), actual_msg->str, stock_name);
146 } else {
147 if (!notify_init ("evolution-publish-calendar")) {
148 can_notify = FALSE;
149 return;
150 }
151
152 notify = notify_notification_new (_("Calendar Publishing"), actual_msg->str, stock_name);
153 notify_notification_set_urgency (notify, NOTIFY_URGENCY_NORMAL);
154 notify_notification_set_timeout (notify, NOTIFY_EXPIRES_DEFAULT);
155 g_timeout_add (500, show_notify_cb, NULL);
156
157 g_signal_connect (
158 notify, "closed",
159 G_CALLBACK (remove_notification), NULL);
160 }
161 }
162 #endif
163
164 status_icon_timeout_id = g_timeout_add_seconds (15, remove_notification, NULL);
165
166 if (new_icon) {
167 g_signal_connect (
168 status_icon, "activate",
169 G_CALLBACK (remove_notification), NULL);
170 }
171 }
172
173 static void
174 publish_no_succ_info (EPublishUri *uri)
175 {
176 publish (uri, FALSE);
177 }
178
179 static void
180 publish_uri_async (EPublishUri *uri)
181 {
182 GError *error = NULL;
183
184 g_thread_create (
185 (GThreadFunc) publish_no_succ_info, uri, FALSE, &error);
186 if (error != NULL) {
187 g_warning (G_STRLOC ": %s", error->message);
188 g_error_free (error);
189 }
190 }
191
192 static void
193 publish_online (EPublishUri *uri,
194 GFile *file,
195 GError **perror,
196 gboolean can_report_success)
197 {
198 GOutputStream *stream;
199 GError *error = NULL;
200
201 stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error));
202
203 if (!stream || error) {
204 if (stream)
205 g_object_unref (stream);
206
207 if (perror) {
208 *perror = error;
209 } else if (error) {
210
211 error_queue_add (g_strdup_printf (_("Could not open %s:"), uri->location), error);
212 } else {
213 error_queue_add (g_strdup_printf (_("Could not open %s: Unknown error"), uri->location), NULL);
214 }
215 return;
216 }
217
218 switch (uri->publish_format) {
219 case URI_PUBLISH_AS_ICAL:
220 publish_calendar_as_ical (stream, uri, &error);
221 break;
222 case URI_PUBLISH_AS_FB:
223 publish_calendar_as_fb (stream, uri, &error);
224 break;
225 /*
226 case URI_PUBLISH_AS_HTML:
227 publish_calendar_as_html (handle, uri);
228 break;
229 */
230 }
231
232 if (error)
233 error_queue_add (g_strdup_printf (_("There was an error while publishing to %s:"), uri->location), error);
234 else if (can_report_success)
235 error_queue_add (g_strdup_printf (_("Publishing to %s finished successfully"), uri->location), NULL);
236
237 update_timestamp (uri);
238
239 g_output_stream_close (stream, NULL, NULL);
240 g_object_unref (stream);
241 }
242
243 static void
244 unmount_done_cb (GObject *source_object,
245 GAsyncResult *result,
246 gpointer user_data)
247 {
248 GError *error = NULL;
249
250 g_mount_unmount_with_operation_finish (G_MOUNT (source_object), result, &error);
251
252 if (error) {
253 g_warning ("Unmount failed: %s", error->message);
254 g_error_free (error);
255 }
256
257 g_object_unref (source_object);
258 }
259
260 struct mnt_struct {
261 EPublishUri *uri;
262 GFile *file;
263 GMountOperation *mount_op;
264 gboolean can_report_success;
265 };
266
267 static void
268 mount_ready_cb (GObject *source_object,
269 GAsyncResult *result,
270 gpointer user_data)
271 {
272 struct mnt_struct *ms = (struct mnt_struct *) user_data;
273 GError *error = NULL;
274 GMount *mount;
275
276 g_file_mount_enclosing_volume_finish (G_FILE (source_object), result, &error);
277
278 if (error) {
279 error_queue_add (g_strdup_printf (_("Mount of %s failed:"), ms->uri->location), error);
280
281 if (ms)
282 g_object_unref (ms->mount_op);
283 g_free (ms);
284
285 g_object_unref (source_object);
286
287 return;
288 }
289
290 g_return_if_fail (ms != NULL);
291
292 publish_online (ms->uri, ms->file, NULL, ms->can_report_success);
293
294 g_object_unref (ms->mount_op);
295 g_free (ms);
296
297 mount = g_file_find_enclosing_mount (G_FILE (source_object), NULL, NULL);
298 if (mount)
299 g_mount_unmount_with_operation (mount, G_MOUNT_UNMOUNT_NONE, NULL, NULL, unmount_done_cb, NULL);
300
301 g_object_unref (source_object);
302 }
303
304 static void
305 ask_password (GMountOperation *op,
306 const gchar *message,
307 const gchar *default_user,
308 const gchar *default_domain,
309 GAskPasswordFlags flags,
310 gpointer user_data)
311 {
312 struct mnt_struct *ms = (struct mnt_struct *) user_data;
313 const gchar *username;
314 gchar *password;
315 gboolean req_pass = FALSE;
316 SoupURI *soup_uri;
317
318 g_return_if_fail (ms != NULL);
319
320 /* we can ask only for a password */
321 if ((flags & G_ASK_PASSWORD_NEED_PASSWORD) == 0)
322 return;
323
324 soup_uri = soup_uri_new (ms->uri->location);
325 g_return_if_fail (soup_uri != NULL);
326
327 username = soup_uri_get_user (soup_uri);
328 password = e_passwords_get_password (NULL, ms->uri->location);
329 req_pass =
330 ((username && *username) &&
331 !(ms->uri->service_type == TYPE_ANON_FTP &&
332 !strcmp (username, "anonymous")));
333
334 if (!password && req_pass) {
335 gboolean remember = FALSE;
336
337 password = e_passwords_ask_password (
338 _("Enter password"), NULL,
339 ms->uri->location, message,
340 E_PASSWORDS_REMEMBER_FOREVER |
341 E_PASSWORDS_SECRET |
342 E_PASSWORDS_ONLINE,
343 &remember, NULL);
344
345 if (!password) {
346 /* user canceled password dialog */
347 g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED);
348 soup_uri_free (soup_uri);
349
350 return;
351 }
352 }
353
354 if (!req_pass)
355 g_mount_operation_set_anonymous (op, TRUE);
356 else {
357 g_mount_operation_set_anonymous (op, FALSE);
358 g_mount_operation_set_username (op, username);
359 g_mount_operation_set_password (op, password);
360 }
361
362 g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
363
364 soup_uri_free (soup_uri);
365 }
366
367 static void
368 ask_question (GMountOperation *op,
369 const gchar *message,
370 const gchar *choices[])
371 {
372 /* this has been stolen from file-chooser */
373 GtkWidget *dialog;
374 gint cnt, len;
375 gchar *primary;
376 const gchar *secondary = NULL;
377 gint res;
378
379 primary = strstr (message, "\n");
380 if (primary) {
381 secondary = primary + 1;
382 primary = g_strndup (message, strlen (message) - strlen (primary));
383 }
384
385 dialog = gtk_message_dialog_new (
386 NULL,
387 0, GTK_MESSAGE_QUESTION,
388 GTK_BUTTONS_NONE, "%s", primary);
389 g_free (primary);
390
391 if (secondary) {
392 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
393 "%s", secondary);
394 }
395
396 if (choices) {
397 /* First count the items in the list then
398 * add the buttons in reverse order */
399 for (len = 0; choices[len] != NULL; len++) {
400 ;
401 }
402
403 for (cnt = len - 1; cnt >= 0; cnt--) {
404 gtk_dialog_add_button (GTK_DIALOG (dialog), choices[cnt], cnt);
405 }
406 }
407
408 res = gtk_dialog_run (GTK_DIALOG (dialog));
409 if (res >= 0) {
410 g_mount_operation_set_choice (op, res);
411 g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
412 } else {
413 g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED);
414 }
415
416 gtk_widget_destroy (GTK_WIDGET (dialog));
417 }
418
419 static void
420 mount_first (EPublishUri *uri,
421 GFile *file,
422 gboolean can_report_success)
423 {
424 struct mnt_struct *ms = g_malloc (sizeof (struct mnt_struct));
425
426 ms->uri = uri;
427 ms->file = g_object_ref (file);
428 ms->mount_op = g_mount_operation_new ();
429 ms->can_report_success = can_report_success;
430
431 g_signal_connect (
432 ms->mount_op, "ask-password",
433 G_CALLBACK (ask_password), ms);
434 g_signal_connect (
435 ms->mount_op, "ask-question",
436 G_CALLBACK (ask_question), ms);
437
438 g_file_mount_enclosing_volume (file, G_MOUNT_MOUNT_NONE, ms->mount_op, NULL, mount_ready_cb, ms);
439 }
440
441 static void
442 publish (EPublishUri *uri,
443 gboolean can_report_success)
444 {
445 if (online) {
446 GError *error = NULL;
447 GFile *file;
448
449 if (g_slist_find (queued_publishes, uri))
450 queued_publishes = g_slist_remove (queued_publishes, uri);
451
452 if (!uri->enabled)
453 return;
454
455 file = g_file_new_for_uri (uri->location);
456
457 g_return_if_fail (file != NULL);
458
459 publish_online (uri, file, &error, can_report_success);
460
461 if (error && error->domain == G_IO_ERROR && error->code == G_IO_ERROR_NOT_MOUNTED) {
462 g_error_free (error);
463 error = NULL;
464
465 mount_first (uri, file, can_report_success);
466 }
467
468 if (error)
469 error_queue_add (g_strdup_printf (_("Could not open %s:"), uri->location), error);
470
471 g_object_unref (file);
472 } else {
473 if (g_slist_find (queued_publishes, uri) == NULL)
474 queued_publishes = g_slist_prepend (queued_publishes, uri);
475 }
476 }
477
478 typedef struct {
479 GSettings *settings;
480 GtkWidget *treeview;
481 GtkWidget *url_add;
482 GtkWidget *url_edit;
483 GtkWidget *url_remove;
484 GtkWidget *url_enable;
485 } PublishUIData;
486
487 static void
488 add_timeout (EPublishUri *uri)
489 {
490 guint id;
491
492 /* Set the timeout for now+frequency */
493 switch (uri->publish_frequency) {
494 case URI_PUBLISH_DAILY:
495 id = g_timeout_add_seconds (24 * 60 * 60, (GSourceFunc) publish, uri);
496 g_hash_table_insert (uri_timeouts, uri, GUINT_TO_POINTER (id));
497 break;
498 case URI_PUBLISH_WEEKLY:
499 id = g_timeout_add_seconds (7 * 24 * 60 * 60, (GSourceFunc) publish, uri);
500 g_hash_table_insert (uri_timeouts, uri, GUINT_TO_POINTER (id));
501 break;
502 }
503 }
504
505 static void
506 update_timestamp (EPublishUri *uri)
507 {
508 GSettings *settings;
509 gchar **set_uris;
510 GPtrArray *uris_array;
511 gboolean found = FALSE;
512 gchar *xml;
513 gint ii;
514 guint id;
515
516 /* Remove timeout if we have one */
517 id = GPOINTER_TO_UINT (g_hash_table_lookup (uri_timeouts, uri));
518 if (id) {
519 g_source_remove (id);
520 add_timeout (uri);
521 }
522
523 /* Update timestamp in settings */
524 xml = e_publish_uri_to_xml (uri);
525
526 if (uri->last_pub_time)
527 g_free (uri->last_pub_time);
528 uri->last_pub_time = g_strdup_printf ("%d", (gint) time (NULL));
529
530 uris_array = g_ptr_array_new_full (3, g_free);
531 settings = g_settings_new (PC_SETTINGS_ID);
532 set_uris = g_settings_get_strv (settings, PC_SETTINGS_URIS);
533
534 for (ii = 0; set_uris && set_uris[ii]; ii++) {
535 const gchar *d = set_uris[ii];
536
537 if (!found && g_str_equal (d, xml)) {
538 found = TRUE;
539 g_ptr_array_add (uris_array, e_publish_uri_to_xml (uri));
540 } else {
541 g_ptr_array_add (uris_array, g_strdup (d));
542 }
543 }
544
545 g_strfreev (set_uris);
546 g_free (xml);
547
548 /* this should not happen, right? */
549 if (!found)
550 g_ptr_array_add (uris_array, e_publish_uri_to_xml (uri));
551 g_ptr_array_add (uris_array, NULL);
552
553 g_settings_set_strv (settings, PC_SETTINGS_URIS, (const gchar * const *) uris_array->pdata);
554
555 g_object_unref (settings);
556 g_ptr_array_free (uris_array, TRUE);
557 }
558
559 static void
560 add_offset_timeout (EPublishUri *uri)
561 {
562 guint id;
563 time_t offset = atoi (uri->last_pub_time);
564 time_t current = time (NULL);
565 gint elapsed = current - offset;
566
567 switch (uri->publish_frequency) {
568 case URI_PUBLISH_DAILY:
569 if (elapsed > 24 * 60 * 60) {
570 publish (uri, FALSE);
571 add_timeout (uri);
572 } else {
573 id = g_timeout_add_seconds (24 * 60 * 60 - elapsed, (GSourceFunc) publish, uri);
574 g_hash_table_insert (uri_timeouts, uri, GUINT_TO_POINTER (id));
575 break;
576 }
577 break;
578 case URI_PUBLISH_WEEKLY:
579 if (elapsed > 7 * 24 * 60 * 60) {
580 publish (uri, FALSE);
581 add_timeout (uri);
582 } else {
583 id = g_timeout_add_seconds (7 * 24 * 60 * 60 - elapsed, (GSourceFunc) publish, uri);
584 g_hash_table_insert (uri_timeouts, uri, GUINT_TO_POINTER (id));
585 break;
586 }
587 break;
588 }
589 }
590
591 static void
592 url_list_changed (PublishUIData *ui)
593 {
594 GtkTreeModel *model = NULL;
595 GPtrArray *uris;
596 GtkTreeIter iter;
597 gboolean valid;
598 GSettings *settings;
599
600 uris = g_ptr_array_new_full (3, g_free);
601
602 model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
603 valid = gtk_tree_model_get_iter_first (model, &iter);
604 while (valid) {
605 EPublishUri *url;
606 gchar *xml;
607
608 gtk_tree_model_get (model, &iter, URL_LIST_URL_COLUMN, &url, -1);
609
610 if ((xml = e_publish_uri_to_xml (url)) != NULL)
611 g_ptr_array_add (uris, xml);
612
613 valid = gtk_tree_model_iter_next (model, &iter);
614 }
615
616 g_ptr_array_add (uris, NULL);
617
618 settings = g_settings_new (PC_SETTINGS_ID);
619 g_settings_set_strv (settings, PC_SETTINGS_URIS, (const gchar * const *) uris->pdata);
620 g_object_unref (settings);
621
622 g_ptr_array_free (uris, TRUE);
623 }
624
625 static void
626 update_url_enable_button (EPublishUri *url,
627 GtkWidget *url_enable)
628 {
629 g_return_if_fail (url_enable != NULL);
630 g_return_if_fail (GTK_IS_BUTTON (url_enable));
631
632 gtk_button_set_label (GTK_BUTTON (url_enable), url && url->enabled ? _("_Disable") : _("E_nable"));
633 }
634
635 static void
636 url_list_enable_toggled (GtkCellRendererToggle *renderer,
637 const gchar *path_string,
638 PublishUIData *ui)
639 {
640 EPublishUri *url = NULL;
641 GtkTreeModel *model;
642 GtkTreePath *path;
643 GtkTreeIter iter;
644
645 path = gtk_tree_path_new_from_string (path_string);
646 model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
647
648 if (gtk_tree_model_get_iter (model, &iter, path)) {
649 gtk_tree_model_get (model, &iter, URL_LIST_URL_COLUMN, &url, -1);
650
651 url->enabled = !url->enabled;
652
653 update_url_enable_button (url, ui->url_enable);
654
655 gtk_list_store_set (GTK_LIST_STORE (model), &iter, URL_LIST_ENABLED_COLUMN, url->enabled, -1);
656
657 url_list_changed (ui);
658 }
659
660 gtk_tree_path_free (path);
661 }
662
663 static void
664 selection_changed (GtkTreeSelection *selection,
665 PublishUIData *ui)
666 {
667 GtkTreeModel *model;
668 GtkTreeIter iter;
669 EPublishUri *url = NULL;
670
671 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
672 gtk_tree_model_get (model, &iter, URL_LIST_URL_COLUMN, &url, -1);
673 gtk_widget_set_sensitive (ui->url_edit, TRUE);
674 gtk_widget_set_sensitive (ui->url_remove, TRUE);
675 gtk_widget_set_sensitive (ui->url_enable, TRUE);
676 } else {
677 gtk_widget_set_sensitive (ui->url_edit, FALSE);
678 gtk_widget_set_sensitive (ui->url_remove, FALSE);
679 gtk_widget_set_sensitive (ui->url_enable, FALSE);
680 }
681
682 update_url_enable_button (url, ui->url_enable);
683 }
684
685 static void
686 url_add_clicked (GtkButton *button,
687 PublishUIData *ui)
688 {
689 GtkTreeModel *model;
690 GtkTreeIter iter;
691 GtkWidget *url_editor;
692 EPublishUri *uri;
693
694 model = gtk_tree_view_get_model (GTK_TREE_VIEW (ui->treeview));
695 url_editor = url_editor_dialog_new (model, NULL);
696
697 if (url_editor_dialog_run ((UrlEditorDialog *) url_editor)) {
698 uri = URL_EDITOR_DIALOG (url_editor)->uri;
699 if (uri->location) {
700 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
701 gtk_list_store_set (
702 GTK_LIST_STORE (model), &iter,
703 URL_LIST_ENABLED_COLUMN, uri->enabled,
704 URL_LIST_LOCATION_COLUMN, uri->location,
705 URL_LIST_URL_COLUMN, uri, -1);
706 url_list_changed (ui);
707 publish_uris = g_slist_prepend (publish_uris, uri);
708 add_timeout (uri);
709 publish_uri_async (uri);
710 } else {
711 g_free (uri);
712 }
713 }
714 gtk_widget_destroy (url_editor);
715 }
716
717 static void
718 url_edit_clicked (GtkButton *button,
719 PublishUIData *ui)
720 {
721 GtkTreeSelection *selection;
722 GtkTreeModel *model;
723 GtkTreeIter iter;
724
725 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
726 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
727 EPublishUri *uri;
728 GtkWidget *url_editor;
729 guint id;
730
731 gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 2, &uri, -1);
732 url_editor = url_editor_dialog_new (model, uri);
733
734 if (url_editor_dialog_run ((UrlEditorDialog *) url_editor)) {
735 gtk_list_store_set (
736 GTK_LIST_STORE (model), &iter,
737 URL_LIST_ENABLED_COLUMN, uri->enabled,
738 URL_LIST_LOCATION_COLUMN, uri->location,
739 URL_LIST_URL_COLUMN, uri, -1);
740
741 id = GPOINTER_TO_UINT (g_hash_table_lookup (uri_timeouts, uri));
742 if (id)
743 g_source_remove (id);
744 add_timeout (uri);
745 url_list_changed (ui);
746 publish_uri_async (uri);
747 }
748
749 gtk_widget_destroy (url_editor);
750 }
751 }
752
753 static void
754 url_list_double_click (GtkTreeView *treeview,
755 GtkTreePath *path,
756 GtkTreeViewColumn *column,
757 PublishUIData *ui)
758 {
759 url_edit_clicked (NULL, ui);
760 }
761
762 static void
763 url_remove_clicked (GtkButton *button,
764 PublishUIData *ui)
765 {
766 EPublishUri *url = NULL;
767 GtkTreeSelection *selection;
768 GtkTreeModel *model;
769 GtkTreeIter iter;
770 GtkWidget *confirm;
771 gint response;
772
773 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
774 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
775 return;
776
777 gtk_tree_model_get (model, &iter, URL_LIST_URL_COLUMN, &url, -1);
778
779 confirm = gtk_message_dialog_new (
780 NULL, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
781 GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
782 _("Are you sure you want to remove this location?"));
783 gtk_dialog_add_button (GTK_DIALOG (confirm), GTK_STOCK_CANCEL, GTK_RESPONSE_NO);
784 gtk_dialog_add_button (GTK_DIALOG (confirm), GTK_STOCK_REMOVE, GTK_RESPONSE_YES);
785 gtk_dialog_set_default_response (GTK_DIALOG (confirm), GTK_RESPONSE_CANCEL);
786
787 response = gtk_dialog_run (GTK_DIALOG (confirm));
788 gtk_widget_destroy (confirm);
789
790 if (response == GTK_RESPONSE_YES) {
791 gint len;
792 guint id;
793 gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
794
795 len = gtk_tree_model_iter_n_children (model, NULL);
796 if (len > 0) {
797 gtk_tree_selection_select_iter (selection, &iter);
798 } else {
799 gtk_widget_set_sensitive (ui->url_edit, FALSE);
800 gtk_widget_set_sensitive (ui->url_remove, FALSE);
801 gtk_widget_set_sensitive (ui->url_enable, FALSE);
802
803 update_url_enable_button (NULL, ui->url_enable);
804 }
805
806 publish_uris = g_slist_remove (publish_uris, url);
807 id = GPOINTER_TO_UINT (g_hash_table_lookup (uri_timeouts, url));
808 if (id)
809 g_source_remove (id);
810
811 g_free (url);
812 url_list_changed (ui);
813 }
814 }
815
816 static void
817 url_enable_clicked (GtkButton *button,
818 PublishUIData *ui)
819 {
820 EPublishUri *url = NULL;
821 GtkTreeSelection *selection;
822 GtkTreeModel *model;
823 GtkTreeIter iter;
824
825 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
826 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
827 gtk_tree_model_get (model, &iter, URL_LIST_URL_COLUMN, &url, -1);
828 url->enabled = !url->enabled;
829
830 update_url_enable_button (url, ui->url_enable);
831
832 gtk_list_store_set (GTK_LIST_STORE (model), &iter, URL_LIST_ENABLED_COLUMN, url->enabled, -1);
833 gtk_tree_selection_select_iter (selection, &iter);
834 url_list_changed (ui);
835 }
836 }
837
838 static void
839 online_state_changed (EShell *shell)
840 {
841 online = e_shell_get_online (shell);
842 if (online)
843 while (queued_publishes)
844 publish (queued_publishes->data, FALSE);
845 }
846
847 GtkWidget *
848 publish_calendar_locations (EPlugin *epl,
849 EConfigHookItemFactoryData *data)
850 {
851 GtkBuilder *builder;
852 GtkCellRenderer *renderer;
853 GtkTreeSelection *selection;
854 GtkWidget *toplevel;
855 PublishUIData *ui = g_new0 (PublishUIData, 1);
856 GSList *l;
857 GtkTreeIter iter;
858
859 builder = gtk_builder_new ();
860 e_load_ui_builder_definition (builder, "publish-calendar.ui");
861
862 ui->treeview = e_builder_get_widget (builder, "url list");
863 if (store == NULL)
864 store = gtk_list_store_new (URL_LIST_N_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER);
865 else
866 gtk_list_store_clear (store);
867
868 gtk_tree_view_set_model (GTK_TREE_VIEW (ui->treeview), GTK_TREE_MODEL (store));
869
870 renderer = gtk_cell_renderer_toggle_new ();
871 g_object_set (renderer, "activatable", TRUE, NULL);
872 gtk_tree_view_insert_column_with_attributes (
873 GTK_TREE_VIEW (ui->treeview), -1, _("Enabled"),
874 renderer, "active", URL_LIST_ENABLED_COLUMN, NULL);
875 g_signal_connect (
876 renderer, "toggled",
877 G_CALLBACK (url_list_enable_toggled), ui);
878 renderer = gtk_cell_renderer_text_new ();
879 gtk_tree_view_insert_column_with_attributes (
880 GTK_TREE_VIEW (ui->treeview), -1, _("Location"),
881 renderer, "text", URL_LIST_LOCATION_COLUMN, NULL);
882 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ui->treeview));
883 gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
884 g_signal_connect (
885 selection, "changed",
886 G_CALLBACK (selection_changed), ui);
887 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ui->treeview), TRUE);
888 g_signal_connect (
889 ui->treeview, "row-activated",
890 G_CALLBACK (url_list_double_click), ui);
891
892 ui->url_add = e_builder_get_widget (builder, "url add");
893 ui->url_edit = e_builder_get_widget (builder, "url edit");
894 ui->url_remove = e_builder_get_widget (builder, "url remove");
895 ui->url_enable = e_builder_get_widget (builder, "url enable");
896 update_url_enable_button (NULL, ui->url_enable);
897 g_signal_connect (
898 ui->url_add, "clicked",
899 G_CALLBACK (url_add_clicked), ui);
900 g_signal_connect (
901 ui->url_edit, "clicked",
902 G_CALLBACK (url_edit_clicked), ui);
903 g_signal_connect (
904 ui->url_remove, "clicked",
905 G_CALLBACK (url_remove_clicked), ui);
906 g_signal_connect (
907 ui->url_enable, "clicked",
908 G_CALLBACK (url_enable_clicked), ui);
909 gtk_widget_set_sensitive (GTK_WIDGET (ui->url_edit), FALSE);
910 gtk_widget_set_sensitive (GTK_WIDGET (ui->url_remove), FALSE);
911 gtk_widget_set_sensitive (GTK_WIDGET (ui->url_enable), FALSE);
912
913 gtk_button_set_image (GTK_BUTTON (ui->url_enable), gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_BUTTON));
914 gtk_button_set_use_underline (GTK_BUTTON (ui->url_enable), TRUE);
915
916 l = publish_uris;
917 while (l) {
918 EPublishUri *url = (EPublishUri *) l->data;
919
920 gtk_list_store_append (store, &iter);
921 gtk_list_store_set (
922 store, &iter,
923 URL_LIST_ENABLED_COLUMN, url->enabled,
924 URL_LIST_LOCATION_COLUMN, url->location,
925 URL_LIST_URL_COLUMN, url, -1);
926
927 l = g_slist_next (l);
928 }
929 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
930 gtk_tree_selection_select_iter (selection, &iter);
931
932 toplevel = e_builder_get_widget (builder, "toplevel");
933 gtk_widget_show_all (toplevel);
934 gtk_box_pack_start (GTK_BOX (data->parent), toplevel, FALSE, TRUE, 0);
935
936 g_object_unref (builder);
937
938 return toplevel;
939 }
940
941 static gpointer
942 publish_urls (gpointer data)
943 {
944 GSList *l;
945
946 for (l = publish_uris; l; l = g_slist_next (l)) {
947 EPublishUri *uri = l->data;
948 publish (uri, TRUE);
949 }
950
951 return GINT_TO_POINTER (0);
952 }
953
954 static gpointer
955 publish_uris_set_timeout (gchar **uris)
956 {
957 gint ii;
958
959 uri_timeouts = g_hash_table_new (g_direct_hash, g_direct_equal);
960
961 for (ii = 0; uris && uris[ii]; ii++) {
962 const gchar *xml = uris[ii];
963 EPublishUri *uri = e_publish_uri_from_xml (xml);
964
965 if (!uri->location) {
966 g_free (uri);
967 continue;
968 }
969
970 publish_uris = g_slist_prepend (publish_uris, uri);
971
972 /* Add a timeout based on the last publish time */
973 add_offset_timeout (uri);
974 }
975
976 g_strfreev (uris);
977
978 return NULL;
979 }
980
981 gint
982 e_plugin_lib_enable (EPlugin *ep,
983 gint enable)
984 {
985 EShell *shell = e_shell_get_default ();
986
987 if (shell) {
988 g_signal_handlers_disconnect_by_func (shell, G_CALLBACK (online_state_changed), NULL);
989 if (enable) {
990 online = e_shell_get_online (shell);
991 g_signal_connect (
992 shell, "notify::online",
993 G_CALLBACK (online_state_changed), NULL);
994 }
995 }
996
997 if (enable) {
998 GSettings *settings;
999 gchar **uris;
1000 GError *error = NULL;
1001
1002 settings = g_settings_new (PC_SETTINGS_ID);
1003 uris = g_settings_get_strv (settings, PC_SETTINGS_URIS);
1004 g_object_unref (settings);
1005
1006 g_thread_create (
1007 (GThreadFunc) publish_uris_set_timeout,
1008 uris, FALSE, &error);
1009 if (error != NULL) {
1010 g_warning ("Could create thread to set timeout for publishing uris : %s", error->message);
1011 g_error_free (error);
1012 }
1013 }
1014
1015 return 0;
1016 }
1017
1018 struct eq_data {
1019 gchar *description;
1020 GError *error;
1021 };
1022
1023 static gboolean
1024 error_queue_show_idle (gpointer user_data)
1025 {
1026 GString *info = NULL;
1027 GSList *l;
1028 gboolean has_error = FALSE, has_info = FALSE;
1029
1030 g_static_mutex_lock (&error_queue_lock);
1031
1032 for (l = error_queue; l; l = l->next) {
1033 struct eq_data *data = l->data;
1034
1035 if (data) {
1036 if (data->description) {
1037 if (!info) {
1038 info = g_string_new (data->description);
1039 } else {
1040 g_string_append (info, "\n\n");
1041 g_string_append (info, data->description);
1042 }
1043
1044 g_free (data->description);
1045 }
1046
1047 if (data->error) {
1048 has_error = TRUE;
1049 if (!info) {
1050 info = g_string_new (data->error->message);
1051 } else if (data->description) {
1052 g_string_append (info, " ");
1053 g_string_append (info, data->error->message);
1054 } else {
1055 g_string_append (info, "\n\n");
1056 g_string_append (info, data->error->message);
1057 }
1058
1059 g_error_free (data->error);
1060 } else if (data->description) {
1061 has_info = TRUE;
1062 }
1063
1064 g_free (data);
1065 }
1066 }
1067
1068 g_slist_free (error_queue);
1069
1070 error_queue = NULL;
1071 error_queue_show_idle_id = 0;
1072
1073 g_static_mutex_unlock (&error_queue_lock);
1074
1075 if (info) {
1076 update_publish_notification (has_error && has_info ? GTK_MESSAGE_WARNING : has_error ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO, info->str);
1077
1078 g_string_free (info, TRUE);
1079 }
1080
1081 return FALSE;
1082 }
1083
1084 void
1085 error_queue_add (gchar *description,
1086 GError *error)
1087 {
1088 struct eq_data *data;
1089
1090 if (!error && !description)
1091 return;
1092
1093 data = g_new0 (struct eq_data, 1);
1094 data->description = description;
1095 data->error = error;
1096
1097 g_static_mutex_lock (&error_queue_lock);
1098 error_queue = g_slist_append (error_queue, data);
1099 if (error_queue_show_idle_id == 0)
1100 error_queue_show_idle_id = g_idle_add (error_queue_show_idle, NULL);
1101 g_static_mutex_unlock (&error_queue_lock);
1102 }
1103
1104 static void
1105 action_calendar_publish_cb (GtkAction *action,
1106 EShellView *shell_view)
1107 {
1108 GError *error = NULL;
1109
1110 g_thread_create ((GThreadFunc) publish_urls, NULL, FALSE, &error);
1111 if (error != NULL) {
1112 /* To Translators: This is shown to a user when creation of a new thread,
1113 * where the publishing should be done, fails. Basically, this shouldn't
1114 * ever happen, and if so, then something is really wrong. */
1115 error_queue_add (g_strdup (_("Could not create publish thread.")), error);
1116 }
1117 }
1118
1119 static GtkActionEntry entries[] = {
1120
1121 { "calendar-publish",
1122 NULL,
1123 N_("_Publish Calendar Information"),
1124 NULL,
1125 NULL, /* XXX Add a tooltip! */
1126 G_CALLBACK (action_calendar_publish_cb) }
1127 };
1128
1129 gboolean e_plugin_ui_init (GtkUIManager *ui_manager, EShellView *shell_view);
1130
1131 gboolean
1132 e_plugin_ui_init (GtkUIManager *ui_manager,
1133 EShellView *shell_view)
1134 {
1135 EShellWindow *shell_window;
1136 GtkActionGroup *action_group;
1137
1138 shell_window = e_shell_view_get_shell_window (shell_view);
1139 action_group = e_shell_window_get_action_group (shell_window, "calendar");
1140
1141 gtk_action_group_add_actions (
1142 action_group, entries,
1143 G_N_ELEMENTS (entries), shell_view);
1144
1145 return TRUE;
1146 }