1 /*
2 * Copyright (C) 2014 Red Hat, Inc. (www.redhat.com)
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors: Milan Crha <mcrha@redhat.com>
17 */
18
19 #include "evolution-config.h"
20
21 #include <glib.h>
22 #include <glib/gi18n-lib.h>
23
24 #include <e-util/e-util.h>
25 #include <shell/e-shell-view.h>
26
27 #include "e-comp-editor.h"
28 #include "e-comp-editor-event.h"
29 #include "e-comp-editor-memo.h"
30 #include "e-comp-editor-task.h"
31 #include "e-cal-dialogs.h"
32 #include "calendar-config.h"
33 #include "comp-util.h"
34 #include "itip-utils.h"
35
36 #include "e-cal-data-model.h"
37
38 #include "e-cal-ops.h"
39
40 static void
cal_ops_manage_send_component(ECalModel * model,ECalClient * client,ICalComponent * icomp,ECalObjModType mod,ECalOpsSendFlags send_flags)41 cal_ops_manage_send_component (ECalModel *model,
42 ECalClient *client,
43 ICalComponent *icomp,
44 ECalObjModType mod,
45 ECalOpsSendFlags send_flags)
46 {
47 ECalComponent *comp;
48 ESourceRegistry *registry;
49
50 g_return_if_fail (E_IS_CAL_MODEL (model));
51 g_return_if_fail (E_IS_CAL_CLIENT (client));
52 g_return_if_fail (I_CAL_IS_COMPONENT (icomp));
53
54 if ((send_flags & E_CAL_OPS_SEND_FLAG_DONT_SEND) != 0)
55 return;
56
57 comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icomp));
58 if (!comp)
59 return;
60
61 registry = e_cal_model_get_registry (model);
62
63 if (itip_organizer_is_user (registry, comp, client)) {
64 gboolean strip_alarms = (send_flags & E_CAL_OPS_SEND_FLAG_STRIP_ALARMS) != 0;
65 gboolean only_new_attendees = (send_flags & E_CAL_OPS_SEND_FLAG_ONLY_NEW_ATTENDEES) != 0;
66 gboolean can_send = (send_flags & E_CAL_OPS_SEND_FLAG_SEND) != 0;
67
68 if (!can_send) /* E_CAL_OPS_SEND_FLAG_ASK */
69 can_send = e_cal_dialogs_send_component (NULL, client, comp,
70 (send_flags & E_CAL_OPS_SEND_FLAG_IS_NEW_COMPONENT) != 0,
71 &strip_alarms, &only_new_attendees);
72
73 if (can_send)
74 itip_send_component_with_model (model, I_CAL_METHOD_REQUEST, comp, client,
75 NULL, NULL, NULL,
76 (strip_alarms ? E_ITIP_SEND_COMPONENT_FLAG_STRIP_ALARMS : 0) |
77 (only_new_attendees ? E_ITIP_SEND_COMPONENT_FLAG_ONLY_NEW_ATTENDEES : 0) |
78 (mod == E_CAL_OBJ_MOD_ALL ? E_ITIP_SEND_COMPONENT_FLAG_ENSURE_MASTER_OBJECT : 0));
79 }
80
81 g_clear_object (&comp);
82 }
83
84 typedef struct {
85 ECalModel *model;
86 ECalClient *client;
87 ICalComponent *icomp;
88 ECalObjModType mod;
89 gchar *uid;
90 gchar *rid;
91 gboolean check_detached_instance;
92 ECalOpsCreateComponentFunc create_cb;
93 ECalOpsGetDefaultComponentFunc get_default_comp_cb;
94 gboolean all_day_default_comp;
95 gchar *for_client_uid;
96 gboolean is_modify;
97 ECalOpsSendFlags send_flags;
98 gpointer user_data;
99 GDestroyNotify user_data_free;
100 gboolean success;
101 } BasicOperationData;
102
103 static BasicOperationData *
basic_operation_data_new(void)104 basic_operation_data_new (void)
105 {
106 return g_slice_new0 (BasicOperationData);
107 }
108
109 static void
basic_operation_data_free(gpointer ptr)110 basic_operation_data_free (gpointer ptr)
111 {
112 BasicOperationData *bod = ptr;
113
114 if (bod) {
115 if (bod->success) {
116 if (bod->create_cb && bod->uid && bod->icomp) {
117 bod->create_cb (bod->model, bod->client, bod->icomp, bod->uid, bod->user_data);
118 if (bod->user_data_free)
119 bod->user_data_free (bod->user_data);
120 }
121
122 if (bod->is_modify && bod->icomp && (bod->send_flags & E_CAL_OPS_SEND_FLAG_DONT_SEND) == 0) {
123 cal_ops_manage_send_component (bod->model, bod->client, bod->icomp, bod->mod, bod->send_flags);
124 }
125
126 if (bod->get_default_comp_cb && bod->icomp) {
127 bod->get_default_comp_cb (bod->model, bod->client, bod->icomp, bod->user_data);
128 if (bod->user_data_free)
129 bod->user_data_free (bod->user_data);
130 }
131 }
132
133 g_clear_object (&bod->model);
134 g_clear_object (&bod->client);
135 g_clear_object (&bod->icomp);
136 g_free (bod->for_client_uid);
137 g_free (bod->uid);
138 g_free (bod->rid);
139 g_slice_free (BasicOperationData, bod);
140 }
141 }
142
143 static void
cal_ops_create_component_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)144 cal_ops_create_component_thread (EAlertSinkThreadJobData *job_data,
145 gpointer user_data,
146 GCancellable *cancellable,
147 GError **error)
148 {
149 BasicOperationData *bod = user_data;
150
151 g_return_if_fail (bod != NULL);
152
153 bod->success = e_cal_client_create_object_sync (bod->client, bod->icomp, E_CAL_OPERATION_FLAG_NONE, &bod->uid, cancellable, error);
154 }
155
156 /**
157 * e_cal_ops_create_component:
158 * @model: an #ECalModel
159 * @client: an #ECalClient
160 * @icomp: an #ICalComponent
161 * @callback: (allow none): a callback to be called on success
162 * @user_data: user data to be passed to @callback; ignored when @callback is #NULL
163 * @user_data_free: a function to free @user_data; ignored when @callback is #NULL
164 *
165 * Creates a new @icomp in the @client. The @callback, if not #NULL,
166 * is called with a new uid of the @icomp on sucessful component save.
167 * The @callback is called in the main thread.
168 *
169 * Since: 3.16
170 **/
171 void
e_cal_ops_create_component(ECalModel * model,ECalClient * client,ICalComponent * icomp,ECalOpsCreateComponentFunc callback,gpointer user_data,GDestroyNotify user_data_free)172 e_cal_ops_create_component (ECalModel *model,
173 ECalClient *client,
174 ICalComponent *icomp,
175 ECalOpsCreateComponentFunc callback,
176 gpointer user_data,
177 GDestroyNotify user_data_free)
178 {
179 ECalDataModel *data_model;
180 ESource *source;
181 ICalProperty *prop;
182 const gchar *description;
183 const gchar *alert_ident;
184 gchar *display_name;
185 BasicOperationData *bod;
186 GCancellable *cancellable;
187
188 g_return_if_fail (E_IS_CAL_MODEL (model));
189 g_return_if_fail (E_IS_CAL_CLIENT (client));
190 g_return_if_fail (I_CAL_IS_COMPONENT (icomp));
191
192 switch (e_cal_client_get_source_type (client)) {
193 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
194 description = _("Creating an event");
195 alert_ident = "calendar:failed-create-event";
196 break;
197 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
198 description = _("Creating a memo");
199 alert_ident = "calendar:failed-create-memo";
200 break;
201 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
202 description = _("Creating a task");
203 alert_ident = "calendar:failed-create-task";
204 break;
205 default:
206 g_warn_if_reached ();
207 return;
208 }
209
210 data_model = e_cal_model_get_data_model (model);
211 source = e_client_get_source (E_CLIENT (client));
212
213 bod = basic_operation_data_new ();
214 bod->model = g_object_ref (model);
215 bod->client = g_object_ref (client);
216 bod->icomp = i_cal_component_clone (icomp);
217 bod->create_cb = callback;
218 bod->user_data = user_data;
219 bod->user_data_free = user_data_free;
220
221 cal_comp_util_maybe_ensure_allday_timezone_properties (client, bod->icomp,
222 e_cal_model_get_timezone (model));
223
224 prop = i_cal_component_get_first_property (bod->icomp, I_CAL_CLASS_PROPERTY);
225 if (!prop || i_cal_property_get_class (prop) == I_CAL_CLASS_NONE) {
226 ICalProperty_Class ical_class = I_CAL_CLASS_PUBLIC;
227 GSettings *settings;
228
229 settings = e_util_ref_settings ("org.gnome.evolution.calendar");
230 if (g_settings_get_boolean (settings, "classify-private"))
231 ical_class = I_CAL_CLASS_PRIVATE;
232 g_object_unref (settings);
233
234 if (!prop) {
235 prop = i_cal_property_new_class (ical_class);
236 i_cal_component_add_property (bod->icomp, prop);
237 } else
238 i_cal_property_set_class (prop, ical_class);
239 }
240 g_clear_object (&prop);
241
242 display_name = e_util_get_source_full_name (e_cal_model_get_registry (model), source);
243 cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident,
244 display_name, cal_ops_create_component_thread,
245 bod, basic_operation_data_free);
246
247 g_clear_object (&cancellable);
248 g_free (display_name);
249 }
250
251 static void
cal_ops_modify_component_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)252 cal_ops_modify_component_thread (EAlertSinkThreadJobData *job_data,
253 gpointer user_data,
254 GCancellable *cancellable,
255 GError **error)
256 {
257 BasicOperationData *bod = user_data;
258
259 g_return_if_fail (bod != NULL);
260
261 if (bod->mod == E_CAL_OBJ_MOD_ALL) {
262 ECalComponent *comp;
263
264 comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (bod->icomp));
265 if (comp && e_cal_component_has_recurrences (comp)) {
266 if (!comp_util_sanitize_recurrence_master_sync (comp, bod->client, cancellable, error)) {
267 g_object_unref (comp);
268 return;
269 }
270
271 g_clear_object (&bod->icomp);
272 bod->icomp = i_cal_component_clone (e_cal_component_get_icalcomponent (comp));
273 }
274
275 g_clear_object (&comp);
276 }
277
278 bod->success = e_cal_client_modify_object_sync (bod->client, bod->icomp, bod->mod, E_CAL_OPERATION_FLAG_NONE, cancellable, error);
279 }
280
281 /**
282 * e_cal_ops_modify_component:
283 * @model: an #ECalModel
284 * @client: an #ECalClient
285 * @icomp: an #ICalComponent
286 * @mod: a mode to use for modification of the component
287 * @send_flags: what to do when the modify succeeded and the component has attendees
288 *
289 * Saves changes of the @icomp into the @client using the @mod. The @send_flags influences
290 * what to do when the @icomp has attendees and the organizer is user. Only one of
291 * #E_CAL_OPS_SEND_FLAG_ASK, #E_CAL_OPS_SEND_FLAG_SEND, #E_CAL_OPS_SEND_FLAG_DONT_SEND
292 * can be used, while the ASK flag is the default.
293 *
294 * Since: 3.16
295 **/
296 void
e_cal_ops_modify_component(ECalModel * model,ECalClient * client,ICalComponent * icomp,ECalObjModType mod,ECalOpsSendFlags send_flags)297 e_cal_ops_modify_component (ECalModel *model,
298 ECalClient *client,
299 ICalComponent *icomp,
300 ECalObjModType mod,
301 ECalOpsSendFlags send_flags)
302 {
303 ECalDataModel *data_model;
304 ESource *source;
305 const gchar *description;
306 const gchar *alert_ident;
307 gchar *display_name;
308 BasicOperationData *bod;
309 GCancellable *cancellable;
310
311 g_return_if_fail (E_IS_CAL_MODEL (model));
312 g_return_if_fail (E_IS_CAL_CLIENT (client));
313 g_return_if_fail (I_CAL_IS_COMPONENT (icomp));
314
315 switch (e_cal_client_get_source_type (client)) {
316 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
317 description = _("Modifying an event");
318 alert_ident = "calendar:failed-modify-event";
319 break;
320 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
321 description = _("Modifying a memo");
322 alert_ident = "calendar:failed-modify-memo";
323 break;
324 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
325 description = _("Modifying a task");
326 alert_ident = "calendar:failed-modify-task";
327 break;
328 default:
329 g_warn_if_reached ();
330 return;
331 }
332
333 data_model = e_cal_model_get_data_model (model);
334 source = e_client_get_source (E_CLIENT (client));
335
336 bod = basic_operation_data_new ();
337 bod->model = g_object_ref (model);
338 bod->client = g_object_ref (client);
339 bod->icomp = i_cal_component_clone (icomp);
340 bod->mod = mod;
341 bod->send_flags = send_flags;
342 bod->is_modify = TRUE;
343
344 cal_comp_util_maybe_ensure_allday_timezone_properties (client, bod->icomp,
345 e_cal_model_get_timezone (model));
346
347 display_name = e_util_get_source_full_name (e_cal_model_get_registry (model), source);
348 cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident,
349 display_name, cal_ops_modify_component_thread,
350 bod, basic_operation_data_free);
351
352 g_clear_object (&cancellable);
353 g_free (display_name);
354 }
355
356 static void
cal_ops_remove_component_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)357 cal_ops_remove_component_thread (EAlertSinkThreadJobData *job_data,
358 gpointer user_data,
359 GCancellable *cancellable,
360 GError **error)
361 {
362 BasicOperationData *bod = user_data;
363
364 g_return_if_fail (bod != NULL);
365
366 /* The check_detached_instance means to test whether the event is a detached instance,
367 then only that one is deleted, otherwise the master object is deleted */
368 if (bod->check_detached_instance && bod->mod == E_CAL_OBJ_MOD_THIS && bod->rid && *bod->rid) {
369 ICalComponent *icomp = NULL;
370 GError *local_error = NULL;
371
372 if (!e_cal_client_get_object_sync (bod->client, bod->uid, bod->rid, &icomp, cancellable, &local_error) &&
373 g_error_matches (local_error, E_CAL_CLIENT_ERROR, E_CAL_CLIENT_ERROR_OBJECT_NOT_FOUND)) {
374 g_free (bod->rid);
375 bod->rid = NULL;
376 bod->mod = E_CAL_OBJ_MOD_ALL;
377 }
378
379 g_clear_error (&local_error);
380 g_clear_object (&icomp);
381 }
382
383 bod->success = e_cal_client_remove_object_sync (bod->client, bod->uid, bod->rid, bod->mod, E_CAL_OPERATION_FLAG_NONE, cancellable, error);
384 }
385
386 /**
387 * e_cal_ops_remove_component:
388 * @model: an #ECalModel
389 * @client: an #ECalClient
390 * @uid: a UID of the component to remove
391 * @rid: (allow none): a recurrence ID of the component; can be #NULL
392 * @mod: a mode to use for the component removal
393 * @check_detached_instance: whether to test whether a detached instance is to be removed
394 *
395 * Removes component identified by @uid and @rid from the @client using mode @mod.
396 * The @check_detached_instance influences behaviour when removing only one instance.
397 * If set to #TRUE, then it is checked first whether the component to be removed is
398 * a detached instance. If it is, then only that one is removed (as requested), otherwise
399 * the master objects is removed. If the @check_detached_instance is set to #FALSE, then
400 * the removal is done exactly with the given values.
401 *
402 * Since: 3.16
403 **/
404 void
e_cal_ops_remove_component(ECalModel * model,ECalClient * client,const gchar * uid,const gchar * rid,ECalObjModType mod,gboolean check_detached_instance)405 e_cal_ops_remove_component (ECalModel *model,
406 ECalClient *client,
407 const gchar *uid,
408 const gchar *rid,
409 ECalObjModType mod,
410 gboolean check_detached_instance)
411 {
412 ECalDataModel *data_model;
413 ESource *source;
414 const gchar *description;
415 const gchar *alert_ident;
416 gchar *display_name;
417 BasicOperationData *bod;
418 GCancellable *cancellable;
419
420 g_return_if_fail (E_IS_CAL_MODEL (model));
421 g_return_if_fail (E_IS_CAL_CLIENT (client));
422 g_return_if_fail (uid != NULL);
423
424 switch (e_cal_client_get_source_type (client)) {
425 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
426 description = _("Removing an event");
427 alert_ident = "calendar:failed-remove-event";
428 break;
429 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
430 description = _("Removing a memo");
431 alert_ident = "calendar:failed-remove-memo";
432 break;
433 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
434 description = _("Removing a task");
435 alert_ident = "calendar:failed-remove-task";
436 break;
437 default:
438 g_warn_if_reached ();
439 return;
440 }
441
442 data_model = e_cal_model_get_data_model (model);
443 source = e_client_get_source (E_CLIENT (client));
444
445 bod = basic_operation_data_new ();
446 bod->model = g_object_ref (model);
447 bod->client = g_object_ref (client);
448 bod->uid = g_strdup (uid);
449 bod->rid = g_strdup (rid);
450 bod->mod = mod;
451 bod->check_detached_instance = check_detached_instance;
452
453 display_name = e_util_get_source_full_name (e_cal_model_get_registry (model), source);
454 cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident,
455 display_name, cal_ops_remove_component_thread,
456 bod, basic_operation_data_free);
457
458 g_clear_object (&cancellable);
459 g_free (display_name);
460 }
461
462 static void
cal_ops_delete_components_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)463 cal_ops_delete_components_thread (EAlertSinkThreadJobData *job_data,
464 gpointer user_data,
465 GCancellable *cancellable,
466 GError **error)
467 {
468 GSList *objects = user_data, *link;
469
470 for (link = objects; link && !g_cancellable_is_cancelled (cancellable); link = g_slist_next (link)) {
471 ECalModelComponent *comp_data = (ECalModelComponent *) link->data;
472 gchar *rid;
473
474 rid = e_cal_util_component_get_recurid_as_string (comp_data->icalcomp);
475
476 if (!e_cal_client_remove_object_sync (
477 comp_data->client, i_cal_component_get_uid (comp_data->icalcomp),
478 rid, E_CAL_OBJ_MOD_THIS, E_CAL_OPERATION_FLAG_NONE, cancellable, error)) {
479 ESource *source = e_client_get_source (E_CLIENT (comp_data->client));
480 e_alert_sink_thread_job_set_alert_arg_0 (job_data, e_source_get_display_name (source));
481 /* Stop on the first error */
482 g_free (rid);
483 break;
484 }
485
486 g_free (rid);
487 }
488 }
489
490 /**
491 * e_cal_ops_delete_ecalmodel_components:
492 * @model: an #ECalModel
493 * @objects: a #GSList of an #ECalModelComponent objects to delete
494 *
495 * Deletes all components from their sources. The @objects should
496 * be part of @model.
497 *
498 * Since: 3.16
499 **/
500 void
e_cal_ops_delete_ecalmodel_components(ECalModel * model,const GSList * objects)501 e_cal_ops_delete_ecalmodel_components (ECalModel *model,
502 const GSList *objects)
503 {
504 ECalDataModel *data_model;
505 GCancellable *cancellable;
506 const gchar *alert_ident;
507 gchar *description;
508 GSList *objects_copy;
509 gint nobjects;
510
511 g_return_if_fail (E_IS_CAL_MODEL (model));
512
513 if (!objects)
514 return;
515
516 objects_copy = g_slist_copy ((GSList *) objects);
517 g_slist_foreach (objects_copy, (GFunc) g_object_ref, NULL);
518 nobjects = g_slist_length (objects_copy);
519
520 switch (e_cal_model_get_component_kind (model)) {
521 case I_CAL_VEVENT_COMPONENT:
522 description = g_strdup_printf (ngettext ("Deleting an event", "Deleting %d events", nobjects), nobjects);
523 alert_ident = "calendar:failed-remove-event";
524 break;
525 case I_CAL_VJOURNAL_COMPONENT:
526 description = g_strdup_printf (ngettext ("Deleting a memo", "Deleting %d memos", nobjects), nobjects);
527 alert_ident = "calendar:failed-remove-memo";
528 break;
529 case I_CAL_VTODO_COMPONENT:
530 description = g_strdup_printf (ngettext ("Deleting a task", "Deleting %d tasks", nobjects), nobjects);
531 alert_ident = "calendar:failed-remove-task";
532 break;
533 default:
534 g_warn_if_reached ();
535 return;
536 }
537
538 data_model = e_cal_model_get_data_model (model);
539
540 cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident,
541 NULL, cal_ops_delete_components_thread, objects_copy, (GDestroyNotify) e_util_free_nullable_object_slist);
542
543 g_clear_object (&cancellable);
544 g_free (description);
545 }
546
547 static gboolean
cal_ops_create_comp_with_new_uid_sync(ECalClient * cal_client,ICalComponent * icomp,ICalTimezone * zone,GCancellable * cancellable,GError ** error)548 cal_ops_create_comp_with_new_uid_sync (ECalClient *cal_client,
549 ICalComponent *icomp,
550 ICalTimezone *zone,
551 GCancellable *cancellable,
552 GError **error)
553 {
554 ICalComponent *clone;
555 gchar *uid;
556 gboolean success;
557
558 g_return_val_if_fail (E_IS_CAL_CLIENT (cal_client), FALSE);
559 g_return_val_if_fail (I_CAL_IS_COMPONENT (icomp), FALSE);
560
561 clone = i_cal_component_clone (icomp);
562
563 uid = e_util_generate_uid ();
564 i_cal_component_set_uid (clone, uid);
565 g_free (uid);
566
567 cal_comp_util_maybe_ensure_allday_timezone_properties (cal_client, clone, zone);
568
569 success = e_cal_client_create_object_sync (cal_client, clone, E_CAL_OPERATION_FLAG_NONE, NULL, cancellable, error);
570
571 g_clear_object (&clone);
572
573 return success;
574 }
575
576 typedef struct {
577 ECalModel *model;
578 ICalComponent *icomp;
579 ICalComponentKind kind;
580 ICalTimezone *zone;
581 const gchar *extension_name;
582 gboolean success;
583 } PasteComponentsData;
584
585 static void
paste_components_data_free(gpointer ptr)586 paste_components_data_free (gpointer ptr)
587 {
588 PasteComponentsData *pcd = ptr;
589
590 if (pcd) {
591 if (pcd->model && pcd->success)
592 g_signal_emit_by_name (pcd->model, "row-appended", 0);
593
594 g_clear_object (&pcd->model);
595 g_clear_object (&pcd->icomp);
596 g_clear_object (&pcd->zone);
597 g_slice_free (PasteComponentsData, pcd);
598 }
599 }
600
601 static void
cal_ops_update_components_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)602 cal_ops_update_components_thread (EAlertSinkThreadJobData *job_data,
603 gpointer user_data,
604 GCancellable *cancellable,
605 GError **error)
606 {
607 PasteComponentsData *pcd = user_data;
608 EClient *client;
609 EClientCache *client_cache;
610 ECalClient *cal_client;
611 ESourceRegistry *registry;
612 ESource *source;
613 const gchar *uid;
614 gchar *display_name;
615 gboolean success = TRUE, any_copied = FALSE;
616 GError *local_error = NULL;
617
618 g_return_if_fail (pcd != NULL);
619
620 uid = e_cal_model_get_default_source_uid (pcd->model);
621 g_return_if_fail (uid != NULL);
622
623 client_cache = e_cal_model_get_client_cache (pcd->model);
624 registry = e_cal_model_get_registry (pcd->model);
625
626 source = e_source_registry_ref_source (registry, uid);
627 if (!source) {
628 g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
629 _("Source with UID “%s” not found"), uid);
630 e_alert_sink_thread_job_set_alert_arg_0 (job_data, uid);
631 return;
632 }
633
634 display_name = e_util_get_source_full_name (registry, source);
635 e_alert_sink_thread_job_set_alert_arg_0 (job_data, display_name);
636 g_free (display_name);
637
638 client = e_client_cache_get_client_sync (client_cache, source, pcd->extension_name, 30, cancellable, &local_error);
639 g_clear_object (&source);
640
641 if (!client) {
642 e_util_propagate_open_source_job_error (job_data, pcd->extension_name, local_error, error);
643 return;
644 }
645
646 cal_client = E_CAL_CLIENT (client);
647
648 if (i_cal_component_isa (pcd->icomp) == I_CAL_VCALENDAR_COMPONENT &&
649 i_cal_component_count_components (pcd->icomp, pcd->kind) > 0) {
650 ICalComponent *subcomp;
651
652 for (subcomp = i_cal_component_get_first_component (pcd->icomp, I_CAL_VTIMEZONE_COMPONENT);
653 subcomp && !g_cancellable_is_cancelled (cancellable);
654 g_object_unref (subcomp), subcomp = i_cal_component_get_next_component (pcd->icomp, I_CAL_VTIMEZONE_COMPONENT)) {
655 ICalTimezone *zone;
656
657 zone = i_cal_timezone_new ();
658 i_cal_timezone_set_component (zone, subcomp);
659 if (!e_cal_client_add_timezone_sync (cal_client, zone, cancellable, error)) {
660 g_clear_object (&zone);
661 success = FALSE;
662 break;
663 }
664
665 g_clear_object (&zone);
666 }
667
668 g_clear_object (&subcomp);
669
670 for (subcomp = i_cal_component_get_first_component (pcd->icomp, pcd->kind);
671 subcomp && !g_cancellable_is_cancelled (cancellable) && success;
672 g_object_unref (subcomp), subcomp = i_cal_component_get_next_component (pcd->icomp, pcd->kind)) {
673 if (!cal_ops_create_comp_with_new_uid_sync (cal_client, subcomp, pcd->zone, cancellable, error)) {
674 success = FALSE;
675 break;
676 }
677
678 any_copied = TRUE;
679 }
680
681 g_clear_object (&subcomp);
682 } else if (i_cal_component_isa (pcd->icomp) == pcd->kind) {
683 success = cal_ops_create_comp_with_new_uid_sync (cal_client, pcd->icomp, pcd->zone, cancellable, error);
684 any_copied = success;
685 }
686
687 pcd->success = success && any_copied;
688
689 g_object_unref (client);
690 }
691
692 /**
693 * e_cal_ops_paste_components:
694 * @model: an #ECalModel
695 * @icompstr: a string representation of an iCalendar component
696 *
697 * Pastes components into the default source of the @model.
698 *
699 * Since: 3.16
700 **/
701 void
e_cal_ops_paste_components(ECalModel * model,const gchar * icompstr)702 e_cal_ops_paste_components (ECalModel *model,
703 const gchar *icompstr)
704 {
705 ECalDataModel *data_model;
706 ICalComponent *icomp;
707 ICalComponentKind kind;
708 gint ncomponents = 0;
709 GCancellable *cancellable;
710 const gchar *alert_ident;
711 const gchar *extension_name;
712 gchar *description;
713 PasteComponentsData *pcd;
714
715 g_return_if_fail (E_IS_CAL_MODEL (model));
716 g_return_if_fail (icompstr != NULL);
717
718 icomp = i_cal_parser_parse_string (icompstr);
719 if (!icomp)
720 return;
721
722 kind = i_cal_component_isa (icomp);
723 if (kind != I_CAL_VCALENDAR_COMPONENT &&
724 kind != e_cal_model_get_component_kind (model)) {
725 g_clear_object (&icomp);
726 return;
727 }
728
729 switch (e_cal_model_get_component_kind (model)) {
730 case I_CAL_VEVENT_COMPONENT:
731 if (kind == I_CAL_VCALENDAR_COMPONENT) {
732 kind = I_CAL_VEVENT_COMPONENT;
733 ncomponents = i_cal_component_count_components (icomp, kind);
734 } else if (kind == I_CAL_VEVENT_COMPONENT) {
735 ncomponents = 1;
736 }
737
738 if (ncomponents == 0)
739 break;
740
741 description = g_strdup_printf (ngettext ("Pasting an event", "Pasting %d events", ncomponents), ncomponents);
742 alert_ident = "calendar:failed-create-event";
743 extension_name = E_SOURCE_EXTENSION_CALENDAR;
744 break;
745 case I_CAL_VJOURNAL_COMPONENT:
746 if (kind == I_CAL_VCALENDAR_COMPONENT) {
747 kind = I_CAL_VJOURNAL_COMPONENT;
748 ncomponents = i_cal_component_count_components (icomp, kind);
749 } else if (kind == I_CAL_VJOURNAL_COMPONENT) {
750 ncomponents = 1;
751 }
752
753 if (ncomponents == 0)
754 break;
755
756 description = g_strdup_printf (ngettext ("Pasting a memo", "Pasting %d memos", ncomponents), ncomponents);
757 alert_ident = "calendar:failed-create-memo";
758 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
759 break;
760 case I_CAL_VTODO_COMPONENT:
761 if (kind == I_CAL_VCALENDAR_COMPONENT) {
762 kind = I_CAL_VTODO_COMPONENT;
763 ncomponents = i_cal_component_count_components (icomp, kind);
764 } else if (kind == I_CAL_VTODO_COMPONENT) {
765 ncomponents = 1;
766 }
767
768 if (ncomponents == 0)
769 break;
770
771 description = g_strdup_printf (ngettext ("Pasting a task", "Pasting %d tasks", ncomponents), ncomponents);
772 alert_ident = "calendar:failed-create-task";
773 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
774 break;
775 default:
776 g_warn_if_reached ();
777 break;
778 }
779
780 if (ncomponents == 0) {
781 g_object_unref (icomp);
782 return;
783 }
784
785 pcd = g_slice_new0 (PasteComponentsData);
786 pcd->model = g_object_ref (model);
787 pcd->icomp = icomp;
788 pcd->kind = kind;
789 pcd->zone = e_cal_model_get_timezone (model);
790 pcd->extension_name = extension_name;
791 pcd->success = FALSE;
792
793 if (pcd->zone)
794 g_object_ref (pcd->zone);
795
796 data_model = e_cal_model_get_data_model (model);
797
798 cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident,
799 NULL, cal_ops_update_components_thread, pcd, paste_components_data_free);
800
801 g_clear_object (&cancellable);
802 g_free (description);
803 }
804
805 typedef struct {
806 ECalClient *client;
807 ICalComponent *icomp;
808 } SendComponentData;
809
810 static void
send_component_data_free(gpointer ptr)811 send_component_data_free (gpointer ptr)
812 {
813 SendComponentData *scd = ptr;
814
815 if (scd) {
816 g_clear_object (&scd->client);
817 g_clear_object (&scd->icomp);
818 g_slice_free (SendComponentData, scd);
819 }
820 }
821
822 static void
cal_ops_send_component_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)823 cal_ops_send_component_thread (EAlertSinkThreadJobData *job_data,
824 gpointer user_data,
825 GCancellable *cancellable,
826 GError **error)
827 {
828 SendComponentData *scd = user_data;
829 ICalComponent *mod_comp = NULL;
830 GSList *users = NULL;
831
832 g_return_if_fail (scd != NULL);
833
834 e_cal_client_send_objects_sync (scd->client, scd->icomp, E_CAL_OPERATION_FLAG_NONE,
835 &users, &mod_comp, cancellable, error);
836
837 g_clear_object (&mod_comp);
838 g_slist_free_full (users, g_free);
839 }
840
841 /**
842 * e_cal_ops_send_component:
843 * @model: an #ECalModel
844 * @client: an #ECalClient
845 * @icomp: an #ICalComponent
846 *
847 * Sends (calls e_cal_client_send_objects_sync()) on the given @client
848 * with the given @icomp in a dedicated thread.
849 *
850 * Since: 3.16
851 **/
852 void
e_cal_ops_send_component(ECalModel * model,ECalClient * client,ICalComponent * icomp)853 e_cal_ops_send_component (ECalModel *model,
854 ECalClient *client,
855 ICalComponent *icomp)
856 {
857 ECalDataModel *data_model;
858 ESource *source;
859 GCancellable *cancellable;
860 const gchar *alert_ident;
861 const gchar *description;
862 gchar *display_name;
863 SendComponentData *scd;
864
865 g_return_if_fail (E_IS_CAL_MODEL (model));
866 g_return_if_fail (E_IS_CAL_CLIENT (client));
867 g_return_if_fail (I_CAL_IS_COMPONENT (icomp));
868
869 switch (e_cal_client_get_source_type (client)) {
870 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
871 description = _("Updating an event");
872 alert_ident = "calendar:failed-update-event";
873 break;
874 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
875 description = _("Updating a memo");
876 alert_ident = "calendar:failed-update-memo";
877 break;
878 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
879 description = _("Updating a task");
880 alert_ident = "calendar:failed-update-task";
881 break;
882 default:
883 g_warn_if_reached ();
884 return;
885 }
886
887 scd = g_slice_new0 (SendComponentData);
888 scd->client = g_object_ref (client);
889 scd->icomp = i_cal_component_clone (icomp);
890
891 source = e_client_get_source (E_CLIENT (client));
892 data_model = e_cal_model_get_data_model (model);
893 display_name = e_util_get_source_full_name (e_cal_model_get_registry (model), source);
894
895 cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident,
896 display_name, cal_ops_send_component_thread,
897 scd, send_component_data_free);
898
899 g_clear_object (&cancellable);
900 g_free (display_name);
901 }
902
903 typedef struct {
904 ECalModel *model;
905 GList *clients;
906 ICalComponentKind kind;
907 time_t older_than;
908 } PurgeComponentsData;
909
910 static void
purge_components_data_free(gpointer ptr)911 purge_components_data_free (gpointer ptr)
912 {
913 PurgeComponentsData *pcd = ptr;
914
915 if (pcd) {
916 g_clear_object (&pcd->model);
917 g_list_free_full (pcd->clients, g_object_unref);
918 g_slice_free (PurgeComponentsData, pcd);
919 }
920 }
921
922 struct purge_data {
923 GList *clients;
924 gboolean remove;
925 time_t older_than;
926 };
927
928 static gboolean
ca_ops_purge_check_instance_cb(ICalComponent * comp,ICalTime * instance_start,ICalTime * instance_end,gpointer user_data,GCancellable * cancellable,GError ** error)929 ca_ops_purge_check_instance_cb (ICalComponent *comp,
930 ICalTime *instance_start,
931 ICalTime *instance_end,
932 gpointer user_data,
933 GCancellable *cancellable,
934 GError **error)
935 {
936 struct purge_data *pd = user_data;
937
938 if (i_cal_time_as_timet (instance_end) >= pd->older_than)
939 pd->remove = FALSE;
940
941 return pd->remove;
942 }
943
944 static void
cal_ops_purge_components_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)945 cal_ops_purge_components_thread (EAlertSinkThreadJobData *job_data,
946 gpointer user_data,
947 GCancellable *cancellable,
948 GError **error)
949 {
950 PurgeComponentsData *pcd = user_data;
951 GList *clink;
952 gchar *sexp, *start, *end;
953 gboolean pushed_message = FALSE;
954 const gchar *tzloc = NULL;
955 ICalTimezone *zone;
956 ICalComponentKind model_kind;
957
958 g_return_if_fail (pcd != NULL);
959
960 model_kind = e_cal_model_get_component_kind (pcd->model);
961 zone = e_cal_model_get_timezone (pcd->model);
962 if (zone && zone != i_cal_timezone_get_utc_timezone ()) {
963 tzloc = i_cal_timezone_get_location (zone);
964 if (tzloc && g_ascii_strcasecmp (tzloc, "UTC") == 0)
965 tzloc = NULL;
966 }
967
968 start = isodate_from_time_t (0);
969 end = isodate_from_time_t (pcd->older_than);
970 sexp = g_strdup_printf (
971 "(occur-in-time-range? (make-time \"%s\") (make-time \"%s\") \"%s\")",
972 start, end, tzloc ? tzloc : "");
973 g_free (start);
974 g_free (end);
975
976 for (clink = pcd->clients; clink && !g_cancellable_is_cancelled (cancellable); clink = g_list_next (clink)) {
977 ECalClient *client = clink->data;
978 GSList *objects, *olink;
979 gint nobjects, ii, last_percent = 0;
980 gchar *display_name;
981 gboolean success = TRUE;
982
983 if (!client || e_client_is_readonly (E_CLIENT (client)))
984 continue;
985
986 display_name = e_util_get_source_full_name (e_cal_model_get_registry (pcd->model), e_client_get_source (E_CLIENT (client)));
987 e_alert_sink_thread_job_set_alert_arg_0 (job_data, display_name);
988
989 switch (model_kind) {
990 case I_CAL_VEVENT_COMPONENT:
991 camel_operation_push_message (cancellable,
992 _("Getting events to purge in the calendar “%s”"), display_name);
993 break;
994 case I_CAL_VJOURNAL_COMPONENT:
995 camel_operation_push_message (cancellable,
996 _("Getting memos to purge in the memo list “%s”"), display_name);
997 break;
998 case I_CAL_VTODO_COMPONENT:
999 camel_operation_push_message (cancellable,
1000 _("Getting tasks to purge in the task list “%s”"), display_name);
1001 break;
1002 default:
1003 g_warn_if_reached ();
1004 g_free (display_name);
1005 return;
1006 }
1007
1008 pushed_message = TRUE;
1009
1010 if (!e_cal_client_get_object_list_sync (client, sexp, &objects, cancellable, error)) {
1011 g_free (display_name);
1012 break;
1013 }
1014
1015 camel_operation_pop_message (cancellable);
1016 pushed_message = FALSE;
1017
1018 if (!objects) {
1019 g_free (display_name);
1020 continue;
1021 }
1022
1023 switch (model_kind) {
1024 case I_CAL_VEVENT_COMPONENT:
1025 camel_operation_push_message (cancellable,
1026 _("Purging events in the calendar “%s”"), display_name);
1027 break;
1028 case I_CAL_VJOURNAL_COMPONENT:
1029 camel_operation_push_message (cancellable,
1030 _("Purging memos in the memo list “%s”"), display_name);
1031 break;
1032 case I_CAL_VTODO_COMPONENT:
1033 camel_operation_push_message (cancellable,
1034 _("Purging tasks in the task list “%s”"), display_name);
1035 break;
1036 default:
1037 g_warn_if_reached ();
1038 g_free (display_name);
1039 return;
1040 }
1041
1042 g_free (display_name);
1043 pushed_message = TRUE;
1044 nobjects = g_slist_length (objects);
1045
1046 for (olink = objects, ii = 0; olink; olink = g_slist_next (olink), ii++) {
1047 ICalComponent *icomp = olink->data;
1048 gboolean remove = TRUE;
1049 gint percent = 100 * (ii + 1) / nobjects;
1050
1051 if (!e_cal_client_check_recurrences_no_master (client)) {
1052 struct purge_data pd;
1053
1054 pd.remove = TRUE;
1055 pd.older_than = pcd->older_than;
1056
1057 e_cal_client_generate_instances_for_object_sync (client, icomp,
1058 pcd->older_than, G_MAXINT32, cancellable, ca_ops_purge_check_instance_cb, &pd);
1059
1060 remove = pd.remove;
1061 }
1062
1063 if (remove) {
1064 const gchar *uid = i_cal_component_get_uid (icomp);
1065
1066 if (e_cal_util_component_is_instance (icomp) ||
1067 e_cal_util_component_has_recurrences (icomp)) {
1068 gchar *rid;
1069
1070 rid = e_cal_util_component_get_recurid_as_string (icomp);
1071
1072 success = e_cal_client_remove_object_sync (client, uid, rid, E_CAL_OBJ_MOD_ALL, E_CAL_OPERATION_FLAG_NONE, cancellable, error);
1073
1074 g_free (rid);
1075 } else {
1076 success = e_cal_client_remove_object_sync (client, uid, NULL, E_CAL_OBJ_MOD_THIS, E_CAL_OPERATION_FLAG_NONE, cancellable, error);
1077 }
1078
1079 if (!success)
1080 break;
1081 }
1082
1083 if (percent != last_percent) {
1084 camel_operation_progress (cancellable, percent);
1085 last_percent = percent;
1086 }
1087 }
1088
1089 g_slist_free_full (objects, g_object_unref);
1090
1091 camel_operation_progress (cancellable, 0);
1092 camel_operation_pop_message (cancellable);
1093 pushed_message = FALSE;
1094
1095 if (!success)
1096 break;
1097 }
1098
1099 if (pushed_message)
1100 camel_operation_pop_message (cancellable);
1101
1102 g_free (sexp);
1103 }
1104
1105 /**
1106 * e_cal_ops_purge_components:
1107 * @model: an #ECalModel instance
1108 * @older_than: threshold for the purge operation
1109 *
1110 * Purges (removed) all components older than @older_than from all
1111 * currently active clients in @model.
1112 *
1113 * Since: 3.16
1114 **/
1115 void
e_cal_ops_purge_components(ECalModel * model,time_t older_than)1116 e_cal_ops_purge_components (ECalModel *model,
1117 time_t older_than)
1118 {
1119 ECalDataModel *data_model;
1120 GCancellable *cancellable;
1121 const gchar *alert_ident;
1122 const gchar *description;
1123 PurgeComponentsData *pcd;
1124
1125 g_return_if_fail (E_IS_CAL_MODEL (model));
1126
1127 switch (e_cal_model_get_component_kind (model)) {
1128 case I_CAL_VEVENT_COMPONENT:
1129 description = _("Purging events");
1130 alert_ident = "calendar:failed-remove-event";
1131 break;
1132 case I_CAL_VJOURNAL_COMPONENT:
1133 description = _("Purging memos");
1134 alert_ident = "calendar:failed-remove-memo";
1135 break;
1136 case I_CAL_VTODO_COMPONENT:
1137 description = _("Purging tasks");
1138 alert_ident = "calendar:failed-remove-task";
1139 break;
1140 default:
1141 g_warn_if_reached ();
1142 return;
1143 }
1144
1145 data_model = e_cal_model_get_data_model (model);
1146
1147 pcd = g_slice_new0 (PurgeComponentsData);
1148 pcd->model = g_object_ref (model);
1149 pcd->clients = e_cal_data_model_get_clients (data_model);
1150 pcd->kind = e_cal_model_get_component_kind (model);
1151 pcd->older_than = older_than;
1152
1153 cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident,
1154 NULL, cal_ops_purge_components_thread,
1155 pcd, purge_components_data_free);
1156
1157 g_clear_object (&cancellable);
1158 }
1159
1160 static void
clients_list_free(gpointer ptr)1161 clients_list_free (gpointer ptr)
1162 {
1163 g_list_free_full (ptr, g_object_unref);
1164 }
1165
1166 static void
cal_ops_delete_completed_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)1167 cal_ops_delete_completed_thread (EAlertSinkThreadJobData *job_data,
1168 gpointer user_data,
1169 GCancellable *cancellable,
1170 GError **error)
1171 {
1172 GList *clients = user_data, *link;
1173
1174 for (link = clients; link; link = g_list_next (link)) {
1175 ECalClient *client = link->data;
1176 GSList *objects = NULL, *olink;
1177
1178 if (!client ||
1179 e_client_is_readonly (E_CLIENT (client)))
1180 continue;
1181
1182 if (!e_cal_client_get_object_list_sync (client, "(is-completed?)", &objects, cancellable, error)) {
1183 ESource *source = e_client_get_source (E_CLIENT (client));
1184 e_alert_sink_thread_job_set_alert_arg_0 (job_data, e_source_get_display_name (source));
1185 break;
1186 }
1187
1188 for (olink = objects; olink != NULL; olink = g_slist_next (olink)) {
1189 ICalComponent *icomp = olink->data;
1190 const gchar *uid;
1191
1192 uid = i_cal_component_get_uid (icomp);
1193
1194 if (!e_cal_client_remove_object_sync (client, uid, NULL, E_CAL_OBJ_MOD_THIS, E_CAL_OPERATION_FLAG_NONE, cancellable, error)) {
1195 ESource *source = e_client_get_source (E_CLIENT (client));
1196 e_alert_sink_thread_job_set_alert_arg_0 (job_data, e_source_get_display_name (source));
1197 break;
1198 }
1199 }
1200
1201 e_util_free_nullable_object_slist (objects);
1202
1203 /* did not process all objects => an error occurred */
1204 if (olink != NULL)
1205 break;
1206 }
1207 }
1208
1209 /**
1210 * e_cal_ops_delete_completed_tasks:
1211 * @model: an #ECalModel
1212 *
1213 * Deletes all completed tasks from all currently opened
1214 * clients in @model.
1215 *
1216 * Since: 3.16
1217 **/
1218 void
e_cal_ops_delete_completed_tasks(ECalModel * model)1219 e_cal_ops_delete_completed_tasks (ECalModel *model)
1220 {
1221 ECalDataModel *data_model;
1222 GCancellable *cancellable;
1223 GList *clients;
1224
1225 g_return_if_fail (E_IS_CAL_MODEL (model));
1226
1227 data_model = e_cal_model_get_data_model (model);
1228 clients = e_cal_data_model_get_clients (data_model);
1229
1230 if (!clients)
1231 return;
1232
1233 if (e_cal_client_get_source_type (clients->data) != E_CAL_CLIENT_SOURCE_TYPE_TASKS) {
1234 g_list_free_full (clients, g_object_unref);
1235 g_warn_if_reached ();
1236 return;
1237 }
1238
1239 cancellable = e_cal_data_model_submit_thread_job (data_model, _("Expunging completed tasks"),
1240 "calendar:failed-remove-task", NULL, cal_ops_delete_completed_thread,
1241 clients, clients_list_free);
1242
1243 g_clear_object (&cancellable);
1244 }
1245
1246 static ECalClient *
cal_ops_open_client_sync(EAlertSinkThreadJobData * job_data,EShell * shell,const gchar * client_uid,const gchar * extension_name,GCancellable * cancellable,GError ** error)1247 cal_ops_open_client_sync (EAlertSinkThreadJobData *job_data,
1248 EShell *shell,
1249 const gchar *client_uid,
1250 const gchar *extension_name,
1251 GCancellable *cancellable,
1252 GError **error)
1253 {
1254 ECalClient *cal_client = NULL;
1255 ESourceRegistry *registry;
1256 EClientCache *client_cache;
1257 ESource *source;
1258 EClient *client;
1259
1260 g_return_val_if_fail (E_IS_SHELL (shell), NULL);
1261 g_return_val_if_fail (client_uid != NULL, NULL);
1262 g_return_val_if_fail (extension_name != NULL, NULL);
1263
1264 registry = e_shell_get_registry (shell);
1265 client_cache = e_shell_get_client_cache (shell);
1266
1267 source = e_source_registry_ref_source (registry, client_uid);
1268 if (!source) {
1269 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1270 _("Source with UID “%s” not found"), client_uid);
1271 e_alert_sink_thread_job_set_alert_arg_0 (job_data, client_uid);
1272 } else {
1273 client = e_client_cache_get_client_sync (client_cache, source, extension_name, 30, cancellable, error);
1274 if (client)
1275 cal_client = E_CAL_CLIENT (client);
1276 }
1277
1278 g_clear_object (&source);
1279
1280 return cal_client;
1281 }
1282
1283 static void
cal_ops_get_default_component_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)1284 cal_ops_get_default_component_thread (EAlertSinkThreadJobData *job_data,
1285 gpointer user_data,
1286 GCancellable *cancellable,
1287 GError **error)
1288 {
1289 BasicOperationData *bod = user_data;
1290
1291 g_return_if_fail (bod != NULL);
1292
1293 if (!bod->for_client_uid) {
1294 ESourceRegistry *registry;
1295 ESource *default_source = NULL;
1296
1297 registry = e_cal_model_get_registry (bod->model);
1298
1299 switch (e_cal_model_get_component_kind (bod->model)) {
1300 case I_CAL_VEVENT_COMPONENT:
1301 default_source = e_source_registry_ref_default_calendar (registry);
1302 break;
1303 case I_CAL_VJOURNAL_COMPONENT:
1304 default_source = e_source_registry_ref_default_memo_list (registry);
1305 break;
1306 case I_CAL_VTODO_COMPONENT:
1307 default_source = e_source_registry_ref_default_task_list (registry);
1308 break;
1309 default:
1310 g_warn_if_reached ();
1311 return;
1312 }
1313
1314 if (default_source)
1315 bod->for_client_uid = g_strdup (e_source_get_uid (default_source));
1316
1317 g_clear_object (&default_source);
1318 }
1319
1320 if (bod->for_client_uid) {
1321 const gchar *extension_name = NULL;
1322
1323 switch (e_cal_model_get_component_kind (bod->model)) {
1324 case I_CAL_VEVENT_COMPONENT:
1325 extension_name = E_SOURCE_EXTENSION_CALENDAR;
1326 break;
1327 case I_CAL_VJOURNAL_COMPONENT:
1328 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
1329 break;
1330 case I_CAL_VTODO_COMPONENT:
1331 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
1332 break;
1333 default:
1334 g_warn_if_reached ();
1335 return;
1336 }
1337
1338 bod->client = cal_ops_open_client_sync (job_data,
1339 e_cal_model_get_shell (bod->model),
1340 bod->for_client_uid,
1341 extension_name,
1342 cancellable,
1343 error);
1344 }
1345
1346 bod->icomp = e_cal_model_create_component_with_defaults_sync (bod->model, bod->client, bod->all_day_default_comp, cancellable, error);
1347 bod->success = bod->icomp != NULL && !g_cancellable_is_cancelled (cancellable);
1348 }
1349
1350 /**
1351 * e_cal_ops_get_default_component:
1352 * @model: an #ECalModel
1353 * @for_client_uid: (allow none): a client UID to use for the new component; can be #NULL
1354 * @all_day: whether the default event should be an all day event; this argument
1355 * is ignored for other than event @model-s
1356 * @callback: a callback to be called when the operation succeeded
1357 * @user_data: user data passed to @callback
1358 * @user_data_free: (allow none): a function to free @user_data, or #NULL
1359 *
1360 * Creates a new component with default values as defined by the @client,
1361 * or by the @model, if @client is #NULL. The @callback is called on success.
1362 * The @callback is called in the main thread.
1363 *
1364 * Since: 3.16
1365 **/
1366 void
e_cal_ops_get_default_component(ECalModel * model,const gchar * for_client_uid,gboolean all_day,ECalOpsGetDefaultComponentFunc callback,gpointer user_data,GDestroyNotify user_data_free)1367 e_cal_ops_get_default_component (ECalModel *model,
1368 const gchar *for_client_uid,
1369 gboolean all_day,
1370 ECalOpsGetDefaultComponentFunc callback,
1371 gpointer user_data,
1372 GDestroyNotify user_data_free)
1373 {
1374 ECalDataModel *data_model;
1375 ESource *source = NULL;
1376 const gchar *description;
1377 const gchar *alert_ident;
1378 gchar *display_name = NULL;
1379 BasicOperationData *bod;
1380 GCancellable *cancellable;
1381
1382 g_return_if_fail (E_IS_CAL_MODEL (model));
1383 g_return_if_fail (callback != NULL);
1384
1385 switch (e_cal_model_get_component_kind (model)) {
1386 case I_CAL_VEVENT_COMPONENT:
1387 description = _("Creating an event");
1388 alert_ident = "calendar:failed-create-event";
1389 break;
1390 case I_CAL_VJOURNAL_COMPONENT:
1391 description = _("Creating a memo");
1392 alert_ident = "calendar:failed-create-memo";
1393 break;
1394 case I_CAL_VTODO_COMPONENT:
1395 description = _("Creating a task");
1396 alert_ident = "calendar:failed-create-task";
1397 break;
1398 default:
1399 g_warn_if_reached ();
1400 return;
1401 }
1402
1403 data_model = e_cal_model_get_data_model (model);
1404 if (for_client_uid) {
1405 ESourceRegistry *registry;
1406
1407 registry = e_cal_model_get_registry (model);
1408 source = e_source_registry_ref_source (registry, for_client_uid);
1409 if (source)
1410 display_name = e_util_get_source_full_name (registry, source);
1411 }
1412
1413 bod = basic_operation_data_new ();
1414 bod->model = g_object_ref (model);
1415 bod->client = NULL;
1416 bod->icomp = NULL;
1417 bod->for_client_uid = g_strdup (for_client_uid);
1418 bod->all_day_default_comp = all_day;
1419 bod->get_default_comp_cb = callback;
1420 bod->user_data = user_data;
1421 bod->user_data_free = user_data_free;
1422
1423 cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident,
1424 display_name ? display_name : "", cal_ops_get_default_component_thread,
1425 bod, basic_operation_data_free);
1426
1427 g_clear_object (&cancellable);
1428 g_clear_object (&source);
1429 g_free (display_name);
1430 }
1431
1432 static void
cal_ops_emit_model_object_created(ECompEditor * comp_editor,ECalModel * model)1433 cal_ops_emit_model_object_created (ECompEditor *comp_editor,
1434 ECalModel *model)
1435 {
1436 g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
1437 g_return_if_fail (E_IS_CAL_MODEL (model));
1438
1439 e_cal_model_emit_object_created (model, e_comp_editor_get_target_client (comp_editor));
1440 }
1441
1442 typedef struct
1443 {
1444 gboolean is_new_component;
1445 EShell *shell;
1446 ECalModel *model;
1447 ECalClientSourceType source_type;
1448 gboolean is_assigned;
1449 gchar *extension_name;
1450 gchar *for_client_uid;
1451 ESource *default_source;
1452 ECalClient *client;
1453 ECalComponent *comp;
1454
1455 /* for events only */
1456 time_t dtstart;
1457 time_t dtend;
1458 gboolean all_day;
1459 gboolean use_default_reminder;
1460 gint default_reminder_interval;
1461 EDurationType default_reminder_units;
1462 } NewComponentData;
1463
1464 static NewComponentData *
new_component_data_new(void)1465 new_component_data_new (void)
1466 {
1467 return g_slice_new0 (NewComponentData);
1468 }
1469
1470 static void
new_component_data_free(gpointer ptr)1471 new_component_data_free (gpointer ptr)
1472 {
1473 NewComponentData *ncd = ptr;
1474
1475 if (ncd) {
1476 /* successfully opened the default client */
1477 if (ncd->client && ncd->comp) {
1478 ECompEditor *comp_editor;
1479 ECompEditorFlags flags = 0;
1480
1481 if (ncd->is_new_component) {
1482 flags |= E_COMP_EDITOR_FLAG_IS_NEW;
1483 } else {
1484 if (e_cal_component_has_attendees (ncd->comp))
1485 ncd->is_assigned = TRUE;
1486 }
1487
1488 if (ncd->is_assigned) {
1489 if (ncd->is_new_component)
1490 flags |= E_COMP_EDITOR_FLAG_ORGANIZER_IS_USER;
1491
1492 flags |= E_COMP_EDITOR_FLAG_WITH_ATTENDEES;
1493 }
1494
1495 if (ncd->source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS) {
1496 if (ncd->is_new_component && ncd->dtstart > 0 && ncd->dtend > 0) {
1497 ECalComponentDateTime *dt;
1498 ICalTime *itt;
1499 ICalTimezone *zone;
1500
1501 if (ncd->model)
1502 zone = e_cal_model_get_timezone (ncd->model);
1503 else
1504 zone = calendar_config_get_icaltimezone ();
1505
1506 itt = i_cal_time_new_from_timet_with_zone (ncd->dtstart, FALSE, zone);
1507 if (ncd->all_day) {
1508 i_cal_time_set_time (itt, 0, 0, 0);
1509 i_cal_time_set_is_date (itt, TRUE);
1510 }
1511
1512 dt = e_cal_component_datetime_new_take (itt,
1513 (ncd->all_day || !zone) ? NULL : g_strdup (i_cal_timezone_get_tzid (zone)));
1514 e_cal_component_set_dtstart (ncd->comp, dt);
1515 e_cal_component_datetime_free (dt);
1516
1517 itt = i_cal_time_new_from_timet_with_zone (ncd->dtend, FALSE, zone);
1518 if (ncd->all_day) {
1519 /* We round it up to the end of the day, unless it is
1520 * already set to midnight */
1521 if (i_cal_time_get_hour (itt) != 0 ||
1522 i_cal_time_get_minute (itt) != 0 ||
1523 i_cal_time_get_second (itt) != 0) {
1524 i_cal_time_adjust (itt, 1, 0, 0, 0);
1525 }
1526 i_cal_time_set_time (itt, 0, 0, 0);
1527 i_cal_time_set_is_date (itt, TRUE);
1528 }
1529 dt = e_cal_component_datetime_new_take (itt,
1530 (ncd->all_day || !zone) ? NULL : g_strdup (i_cal_timezone_get_tzid (zone)));
1531 e_cal_component_set_dtend (ncd->comp, dt);
1532 e_cal_component_datetime_free (dt);
1533 }
1534 e_cal_component_commit_sequence (ncd->comp);
1535 }
1536
1537 comp_editor = e_comp_editor_open_for_component (NULL, ncd->shell,
1538 ncd->client ? e_client_get_source (E_CLIENT (ncd->client)) : NULL,
1539 e_cal_component_get_icalcomponent (ncd->comp), flags);
1540
1541 if (comp_editor) {
1542 if (ncd->model) {
1543 g_signal_connect (comp_editor, "object-created",
1544 G_CALLBACK (cal_ops_emit_model_object_created), ncd->model);
1545
1546 g_object_set_data_full (G_OBJECT (comp_editor), "e-cal-ops-model", g_object_ref (ncd->model), g_object_unref);
1547 }
1548
1549 gtk_window_present (GTK_WINDOW (comp_editor));
1550 }
1551 }
1552
1553 g_clear_object (&ncd->shell);
1554 g_clear_object (&ncd->model);
1555 g_clear_object (&ncd->default_source);
1556 g_clear_object (&ncd->client);
1557 g_clear_object (&ncd->comp);
1558 g_free (ncd->extension_name);
1559 g_free (ncd->for_client_uid);
1560 g_slice_free (NewComponentData, ncd);
1561 }
1562 }
1563
1564 static void
cal_ops_new_component_editor_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)1565 cal_ops_new_component_editor_thread (EAlertSinkThreadJobData *job_data,
1566 gpointer user_data,
1567 GCancellable *cancellable,
1568 GError **error)
1569 {
1570 NewComponentData *ncd = user_data;
1571 GError *local_error = NULL;
1572
1573 g_return_if_fail (ncd != NULL);
1574
1575 if (ncd->for_client_uid) {
1576 ncd->client = cal_ops_open_client_sync (job_data, ncd->shell, ncd->for_client_uid,
1577 ncd->extension_name, cancellable, &local_error);
1578 }
1579
1580 if (!ncd->default_source && !ncd->client && !ncd->for_client_uid) {
1581 const gchar *message;
1582
1583 switch (ncd->source_type) {
1584 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1585 message = _("Default calendar not found");
1586 break;
1587 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1588 message = _("Default memo list not found");
1589 break;
1590 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1591 message = _("Default task list not found");
1592 break;
1593 default:
1594 g_warn_if_reached ();
1595 return;
1596 }
1597
1598 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, message);
1599 return;
1600 }
1601
1602 if (!ncd->client && !ncd->for_client_uid) {
1603 EClient *client;
1604 EClientCache *client_cache;
1605
1606 client_cache = e_shell_get_client_cache (ncd->shell);
1607
1608 client = e_client_cache_get_client_sync (client_cache, ncd->default_source, ncd->extension_name, 30, cancellable, &local_error);
1609 if (client)
1610 ncd->client = E_CAL_CLIENT (client);
1611 }
1612
1613 if (ncd->client) {
1614 switch (ncd->source_type) {
1615 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1616 ncd->comp = cal_comp_event_new_with_current_time_sync (ncd->client,
1617 ncd->all_day, ncd->use_default_reminder, ncd->default_reminder_interval,
1618 ncd->default_reminder_units, cancellable, &local_error);
1619 break;
1620 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1621 ncd->comp = cal_comp_memo_new_with_defaults_sync (ncd->client, cancellable, &local_error);
1622 break;
1623 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1624 ncd->comp = cal_comp_task_new_with_defaults_sync (ncd->client, cancellable, &local_error);
1625 break;
1626 default:
1627 g_warn_if_reached ();
1628 return;
1629 }
1630 }
1631
1632 e_util_propagate_open_source_job_error (job_data, ncd->extension_name, local_error, error);
1633 }
1634
1635 static void
e_cal_ops_new_component_ex(EShellWindow * shell_window,ECalModel * model,ECalClientSourceType source_type,const gchar * for_client_uid,gboolean is_assigned,gboolean all_day,time_t dtstart,time_t dtend,gboolean use_default_reminder,gint default_reminder_interval,EDurationType default_reminder_units)1636 e_cal_ops_new_component_ex (EShellWindow *shell_window,
1637 ECalModel *model,
1638 ECalClientSourceType source_type,
1639 const gchar *for_client_uid,
1640 gboolean is_assigned,
1641 gboolean all_day,
1642 time_t dtstart,
1643 time_t dtend,
1644 gboolean use_default_reminder,
1645 gint default_reminder_interval,
1646 EDurationType default_reminder_units)
1647 {
1648 ESourceRegistry *registry;
1649 ESource *default_source, *for_client_source = NULL;
1650 EShell *shell;
1651 gchar *description = NULL, *alert_ident = NULL, *alert_arg_0 = NULL;
1652 gchar *source_display_name = NULL;
1653 const gchar *extension_name;
1654 NewComponentData *ncd;
1655
1656 if (shell_window) {
1657 g_return_if_fail (E_IS_SHELL_WINDOW (shell_window));
1658
1659 shell = e_shell_window_get_shell (shell_window);
1660 } else {
1661 g_return_if_fail (E_IS_CAL_MODEL (model));
1662
1663 shell = e_cal_model_get_shell (model);
1664 }
1665
1666 registry = e_shell_get_registry (shell);
1667
1668 switch (source_type) {
1669 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1670 extension_name = E_SOURCE_EXTENSION_CALENDAR;
1671 default_source = e_source_registry_ref_default_calendar (registry);
1672 break;
1673 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1674 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
1675 default_source = e_source_registry_ref_default_memo_list (registry);
1676 break;
1677 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1678 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
1679 default_source = e_source_registry_ref_default_task_list (registry);
1680 break;
1681 default:
1682 g_warn_if_reached ();
1683 return;
1684 }
1685
1686 if (for_client_uid)
1687 for_client_source = e_source_registry_ref_source (registry, for_client_uid);
1688
1689 ncd = new_component_data_new ();
1690 ncd->is_new_component = TRUE;
1691 ncd->shell = g_object_ref (shell);
1692 ncd->model = model ? g_object_ref (model) : NULL;
1693 ncd->source_type = source_type;
1694 ncd->for_client_uid = g_strdup (for_client_uid);
1695 ncd->is_assigned = is_assigned;
1696 ncd->extension_name = g_strdup (extension_name);
1697 ncd->default_source = default_source ? g_object_ref (default_source) : NULL;
1698 ncd->client = NULL;
1699 ncd->comp = NULL;
1700 ncd->dtstart = dtstart;
1701 ncd->dtend = dtend;
1702 ncd->all_day = all_day;
1703 ncd->use_default_reminder = use_default_reminder;
1704 ncd->default_reminder_interval = default_reminder_interval;
1705 ncd->default_reminder_units = default_reminder_units;
1706
1707 if (for_client_source)
1708 source_display_name = e_util_get_source_full_name (registry, for_client_source);
1709 else if (default_source)
1710 source_display_name = e_util_get_source_full_name (registry, default_source);
1711
1712 g_warn_if_fail (e_util_get_open_source_job_info (extension_name,
1713 source_display_name ? source_display_name : "", &description, &alert_ident, &alert_arg_0));
1714
1715 if (shell_window) {
1716 EShellView *shell_view;
1717 EActivity *activity;
1718
1719 shell_view = e_shell_window_get_shell_view (shell_window,
1720 e_shell_window_get_active_view (shell_window));
1721
1722 activity = e_shell_view_submit_thread_job (
1723 shell_view, description, alert_ident, alert_arg_0,
1724 cal_ops_new_component_editor_thread, ncd, new_component_data_free);
1725
1726 g_clear_object (&activity);
1727 } else {
1728 GCancellable *cancellable;
1729 ECalDataModel *data_model;
1730
1731 data_model = e_cal_model_get_data_model (model);
1732
1733 cancellable = e_cal_data_model_submit_thread_job (data_model, description, alert_ident, alert_arg_0,
1734 cal_ops_new_component_editor_thread, ncd, new_component_data_free);
1735
1736 g_clear_object (&cancellable);
1737 }
1738
1739 g_clear_object (&default_source);
1740 g_clear_object (&for_client_source);
1741 g_free (source_display_name);
1742 g_free (description);
1743 g_free (alert_ident);
1744 g_free (alert_arg_0);
1745 }
1746
1747 /**
1748 * e_cal_ops_new_component_editor:
1749 * @shell_window: an #EShellWindow
1750 * @source_type: a source type of the new component
1751 * @for_client_uid: (allow none): a client UID to use for the new component; can be #NULL
1752 * @is_assigned: whether the new component should be assigned
1753 *
1754 * Creates a new component either for an #ECalClient with UID @for_client_uid, or
1755 * for a default source of the @source_type, with prefilled values as provided
1756 * by the #ECalClient. Use e_cal_ops_new_event_editor() for events with
1757 * predefined alarms.
1758 *
1759 * Since: 3.16
1760 **/
1761 void
e_cal_ops_new_component_editor(EShellWindow * shell_window,ECalClientSourceType source_type,const gchar * for_client_uid,gboolean is_assigned)1762 e_cal_ops_new_component_editor (EShellWindow *shell_window,
1763 ECalClientSourceType source_type,
1764 const gchar *for_client_uid,
1765 gboolean is_assigned)
1766 {
1767 e_cal_ops_new_component_ex (shell_window, NULL, source_type, for_client_uid, is_assigned, FALSE, 0, 0, FALSE, 0, E_DURATION_MINUTES);
1768 }
1769
1770 /**
1771 * e_cal_ops_new_event_editor:
1772 * @shell_window: an #EShellWindow
1773 * @source_type: a source type of the new component
1774 * @for_client_uid: (allow none): a client UID to use for the new component; can be #NULL
1775 * @is_meeting: whether the new event should be a meeting
1776 * @all_day: whether the new event should be an all day event
1777 * @use_default_reminder: whether a default reminder should be added,
1778 * if #FALSE, then the next two reminded arguments are ignored
1779 * @default_reminder_interval: reminder interval for the default reminder
1780 * @default_reminder_units: reminder uints for the default reminder
1781 * @dtstart: a time_t of DTSTART to use, or 0 to use the default value
1782 * @dtend: a time_t of DTEND to use, or 0 to use the default value
1783 *
1784 * This is a fine-grained version of e_cal_ops_new_component_editor(), suitable
1785 * for events with predefined alarms. The e_cal_ops_new_component_editor()
1786 * accepts events as well.
1787 *
1788 * The @dtend is ignored, when @dtstart is zero or a negative value.
1789 *
1790 * Since: 3.16
1791 **/
1792 void
e_cal_ops_new_event_editor(EShellWindow * shell_window,const gchar * for_client_uid,gboolean is_meeting,gboolean all_day,gboolean use_default_reminder,gint default_reminder_interval,EDurationType default_reminder_units,time_t dtstart,time_t dtend)1793 e_cal_ops_new_event_editor (EShellWindow *shell_window,
1794 const gchar *for_client_uid,
1795 gboolean is_meeting,
1796 gboolean all_day,
1797 gboolean use_default_reminder,
1798 gint default_reminder_interval,
1799 EDurationType default_reminder_units,
1800 time_t dtstart,
1801 time_t dtend)
1802 {
1803 e_cal_ops_new_component_ex (shell_window, NULL, E_CAL_CLIENT_SOURCE_TYPE_EVENTS, for_client_uid, is_meeting,
1804 all_day, dtstart, dtstart <= 0 ? 0 : dtend, use_default_reminder, default_reminder_interval, default_reminder_units);
1805 }
1806
1807 /**
1808 * e_cal_ops_new_component_editor_from_model:
1809 * @model: an #ECalModel
1810 * @for_client_uid: (allow none): a client UID to use for the new component; can be #NULL
1811 * @dtstart: a DTSTART to use, for events; less than or equal to 0 to ignore
1812 * @dtend: a DTEND to use, for events; less than or equal to 0 to ignore
1813 * @is_assigned: whether the new component should be assigned
1814 * @all_day: whether the new component should be an all day event
1815 *
1816 * Creates a new component either for an #ECalClient with UID @for_client_uid, or
1817 * for a default source of the source type as defined by @model, with prefilled
1818 * values as provided by the #ECalClient. The @all_day is used only for events
1819 * source type.
1820 *
1821 * Since: 3.16
1822 **/
1823 void
e_cal_ops_new_component_editor_from_model(ECalModel * model,const gchar * for_client_uid,time_t dtstart,time_t dtend,gboolean is_assigned,gboolean all_day)1824 e_cal_ops_new_component_editor_from_model (ECalModel *model,
1825 const gchar *for_client_uid,
1826 time_t dtstart,
1827 time_t dtend,
1828 gboolean is_assigned,
1829 gboolean all_day)
1830 {
1831 ECalClientSourceType source_type;
1832
1833 g_return_if_fail (E_IS_CAL_MODEL (model));
1834
1835 switch (e_cal_model_get_component_kind (model)) {
1836 case I_CAL_VEVENT_COMPONENT:
1837 source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
1838 break;
1839 case I_CAL_VJOURNAL_COMPONENT:
1840 source_type = E_CAL_CLIENT_SOURCE_TYPE_MEMOS;
1841 break;
1842 case I_CAL_VTODO_COMPONENT:
1843 source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
1844 break;
1845 default:
1846 g_warn_if_reached ();
1847 return;
1848 }
1849
1850 if (!for_client_uid)
1851 for_client_uid = e_cal_model_get_default_source_uid (model);
1852
1853 if (for_client_uid && !*for_client_uid)
1854 for_client_uid = NULL;
1855
1856 e_cal_ops_new_component_ex (NULL, model, source_type, for_client_uid, is_assigned, all_day, dtstart, dtend,
1857 e_cal_model_get_use_default_reminder (model),
1858 e_cal_model_get_default_reminder_interval (model),
1859 e_cal_model_get_default_reminder_units (model));
1860 }
1861
1862 /**
1863 * e_cal_ops_open_component_in_editor_sync:
1864 * @model: (nullable): an #ECalModel instance
1865 * @client: an #ECalClient, to which the component belongs
1866 * @icomp: an #ICalComponent to open in an editor
1867 * @force_attendees: set to TRUE to force to show attendees, FALSE to auto-detect
1868 *
1869 * Opens a component @icomp, which belongs to a @client, in
1870 * a component editor. This is done synchronously.
1871 *
1872 * Since: 3.16
1873 **/
1874 void
e_cal_ops_open_component_in_editor_sync(ECalModel * model,ECalClient * client,ICalComponent * icomp,gboolean force_attendees)1875 e_cal_ops_open_component_in_editor_sync (ECalModel *model,
1876 ECalClient *client,
1877 ICalComponent *icomp,
1878 gboolean force_attendees)
1879 {
1880 NewComponentData *ncd;
1881 ECalComponent *comp;
1882 ECompEditor *comp_editor;
1883
1884 if (model)
1885 g_return_if_fail (E_IS_CAL_MODEL (model));
1886 g_return_if_fail (E_IS_CAL_CLIENT (client));
1887 g_return_if_fail (I_CAL_IS_COMPONENT (icomp));
1888
1889 comp_editor = e_comp_editor_find_existing_for (e_client_get_source (E_CLIENT (client)), icomp);
1890 if (comp_editor) {
1891 gtk_window_present (GTK_WINDOW (comp_editor));
1892 return;
1893 }
1894
1895 comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (icomp));
1896 g_return_if_fail (comp != NULL);
1897
1898 ncd = new_component_data_new ();
1899 ncd->is_new_component = FALSE;
1900 ncd->shell = g_object_ref (model ? e_cal_model_get_shell (model) : e_shell_get_default ());
1901 ncd->model = model ? g_object_ref (model) : NULL;
1902 ncd->source_type = e_cal_client_get_source_type (client);
1903 ncd->is_assigned = force_attendees;
1904 ncd->extension_name = NULL;
1905 ncd->for_client_uid = NULL;
1906 ncd->default_source = NULL;
1907 ncd->client = g_object_ref (client);
1908 ncd->comp = comp;
1909
1910 /* This opens the editor */
1911 new_component_data_free (ncd);
1912 }
1913
1914 typedef struct {
1915 EShell *shell;
1916 ECalModel *model;
1917 ESource *destination;
1918 ECalClient *destination_client;
1919 ECalClientSourceType source_type;
1920 GHashTable *icomps_by_source;
1921 gboolean is_move;
1922 gint nobjects;
1923 } TransferComponentsData;
1924
1925 static void
transfer_components_free_icomps_slist(gpointer ptr)1926 transfer_components_free_icomps_slist (gpointer ptr)
1927 {
1928 GSList *icomps = ptr;
1929
1930 g_slist_free_full (icomps, g_object_unref);
1931 }
1932
1933 static void
transfer_components_data_free(gpointer ptr)1934 transfer_components_data_free (gpointer ptr)
1935 {
1936 TransferComponentsData *tcd = ptr;
1937
1938 if (tcd) {
1939 if (tcd->destination_client)
1940 e_cal_model_emit_object_created (tcd->model, tcd->destination_client);
1941
1942 g_clear_object (&tcd->shell);
1943 g_clear_object (&tcd->model);
1944 g_clear_object (&tcd->destination);
1945 g_clear_object (&tcd->destination_client);
1946 g_hash_table_destroy (tcd->icomps_by_source);
1947 g_slice_free (TransferComponentsData, tcd);
1948 }
1949 }
1950
1951 static void
transfer_components_thread(EAlertSinkThreadJobData * job_data,gpointer user_data,GCancellable * cancellable,GError ** error)1952 transfer_components_thread (EAlertSinkThreadJobData *job_data,
1953 gpointer user_data,
1954 GCancellable *cancellable,
1955 GError **error)
1956 {
1957 TransferComponentsData *tcd = user_data;
1958 const gchar *extension_name;
1959 EClient *from_client = NULL, *to_client = NULL;
1960 ECalClient *from_cal_client = NULL, *to_cal_client = NULL;
1961 EClientCache *client_cache;
1962 GHashTableIter iter;
1963 gpointer key, value;
1964 gint nobjects, ii = 0, last_percent = 0;
1965 GSList *link;
1966 gboolean success = TRUE;
1967
1968 g_return_if_fail (tcd != NULL);
1969
1970 switch (tcd->source_type) {
1971 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
1972 extension_name = E_SOURCE_EXTENSION_CALENDAR;
1973 break;
1974 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
1975 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
1976 break;
1977 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
1978 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
1979 break;
1980 default:
1981 g_warn_if_reached ();
1982 return;
1983 }
1984
1985 client_cache = e_shell_get_client_cache (tcd->shell);
1986
1987 to_client = e_util_open_client_sync (job_data, client_cache, extension_name, tcd->destination, 30, cancellable, error);
1988 if (!to_client)
1989 goto out;
1990
1991 to_cal_client = E_CAL_CLIENT (to_client);
1992
1993 if (e_client_is_readonly (E_CLIENT (to_client))) {
1994 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_READ_ONLY, _("Destination is read only"));
1995 goto out;
1996 }
1997
1998 nobjects = tcd->nobjects;
1999
2000 g_hash_table_iter_init (&iter, tcd->icomps_by_source);
2001 while (g_hash_table_iter_next (&iter, &key, &value)) {
2002 ESource *source = key;
2003 GSList *icomps = value;
2004
2005 from_client = e_util_open_client_sync (job_data, client_cache, extension_name, source, 30, cancellable, error);
2006 if (!from_client) {
2007 success = FALSE;
2008 goto out;
2009 }
2010
2011 from_cal_client = E_CAL_CLIENT (from_client);
2012
2013 for (link = icomps; link && !g_cancellable_is_cancelled (cancellable); link = g_slist_next (link), ii++) {
2014 gint percent = 100 * (ii + 1) / nobjects;
2015 ICalComponent *icomp = link->data;
2016
2017 if (!cal_comp_transfer_item_to_sync (from_cal_client, to_cal_client, icomp, !tcd->is_move, cancellable, error)) {
2018 success = FALSE;
2019 break;
2020 }
2021
2022 if (percent != last_percent) {
2023 camel_operation_progress (cancellable, percent);
2024 last_percent = percent;
2025 }
2026 }
2027
2028 g_clear_object (&from_client);
2029 }
2030
2031 if (success && ii > 0)
2032 tcd->destination_client = E_CAL_CLIENT (g_object_ref (to_client));
2033
2034 out:
2035 g_clear_object (&from_client);
2036 g_clear_object (&to_client);
2037 }
2038
2039 /**
2040 * e_cal_ops_transfer_components:
2041 * @shell_view: an #EShellView
2042 * @model: an #ECalModel, where to notify about created objects
2043 * @source_type: a source type of the @destination and the sources
2044 * @icomps_by_source: a hash table of #ESource to #GSList of ICalComponent to transfer
2045 * @destination: a destination #ESource
2046 * @is_move: whether the transfer is move (%TRUE) or copy (%FALSE)
2047 *
2048 * Transfers (copies or moves, as set by @is_move) all @icomps_by_source from their source
2049 * to the @destination of type source type (calendar/memo list/task list).
2050 *
2051 * Since: 3.16
2052 **/
2053 void
e_cal_ops_transfer_components(EShellView * shell_view,ECalModel * model,ECalClientSourceType source_type,GHashTable * icomps_by_source,ESource * destination,gboolean is_move)2054 e_cal_ops_transfer_components (EShellView *shell_view,
2055 ECalModel *model,
2056 ECalClientSourceType source_type,
2057 GHashTable *icomps_by_source,
2058 ESource *destination,
2059 gboolean is_move)
2060 {
2061 gint nobjects;
2062 gchar *description, *display_name;
2063 const gchar *alert_ident;
2064 TransferComponentsData *tcd;
2065 GHashTableIter iter;
2066 gpointer key, value;
2067 EActivity *activity;
2068
2069 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
2070 g_return_if_fail (E_IS_CAL_MODEL (model));
2071 g_return_if_fail (icomps_by_source != NULL);
2072 g_return_if_fail (E_IS_SOURCE (destination));
2073
2074 nobjects = 0;
2075 g_hash_table_iter_init (&iter, icomps_by_source);
2076 while (g_hash_table_iter_next (&iter, &key, &value)) {
2077 ESource *source = key;
2078 GSList *icomps = value;
2079
2080 if (!is_move || !e_source_equal (source, destination))
2081 nobjects += g_slist_length (icomps);
2082 }
2083
2084 switch (source_type) {
2085 case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
2086 description = g_strdup_printf (is_move ?
2087 ngettext ("Moving an event", "Moving %d events", nobjects) :
2088 ngettext ("Copying an event", "Copying %d events", nobjects),
2089 nobjects);
2090 alert_ident = is_move ? "calendar:failed-move-event" : "calendar:failed-copy-event";
2091 break;
2092 case E_CAL_CLIENT_SOURCE_TYPE_MEMOS:
2093 description = g_strdup_printf (is_move ?
2094 ngettext ("Moving a memo", "Moving %d memos", nobjects) :
2095 ngettext ("Copying a memo", "Copying %d memos", nobjects),
2096 nobjects);
2097 alert_ident = is_move ? "calendar:failed-move-memo" : "calendar:failed-copy-memo";
2098 break;
2099 case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
2100 description = g_strdup_printf (is_move ?
2101 ngettext ("Moving a task", "Moving %d tasks", nobjects) :
2102 ngettext ("Copying a task", "Copying %d tasks", nobjects),
2103 nobjects);
2104 alert_ident = is_move ? "calendar:failed-move-task" : "calendar:failed-copy-task";
2105 break;
2106 default:
2107 g_warn_if_reached ();
2108 return;
2109 }
2110
2111 tcd = g_slice_new0 (TransferComponentsData);
2112 tcd->shell = g_object_ref (e_shell_window_get_shell (e_shell_view_get_shell_window (shell_view)));
2113 tcd->model = g_object_ref (model);
2114 tcd->icomps_by_source = g_hash_table_new_full ((GHashFunc) e_source_hash, (GEqualFunc) e_source_equal,
2115 g_object_unref, transfer_components_free_icomps_slist);
2116 tcd->destination = g_object_ref (destination);
2117 tcd->source_type = source_type;
2118 tcd->is_move = is_move;
2119 tcd->nobjects = nobjects;
2120 tcd->destination_client = NULL;
2121
2122 g_hash_table_iter_init (&iter, icomps_by_source);
2123 while (g_hash_table_iter_next (&iter, &key, &value)) {
2124 ESource *source = key;
2125 GSList *icomps = value;
2126
2127 if (!is_move || !e_source_equal (source, destination)) {
2128 GSList *link;
2129
2130 icomps = g_slist_copy (icomps);
2131 for (link = icomps; link; link = g_slist_next (link)) {
2132 link->data = i_cal_component_clone (link->data);
2133 }
2134
2135 g_hash_table_insert (tcd->icomps_by_source, g_object_ref (source), icomps);
2136 }
2137 }
2138
2139 display_name = e_util_get_source_full_name (e_cal_model_get_registry (model), destination);
2140 activity = e_shell_view_submit_thread_job (shell_view, description, alert_ident,
2141 display_name, transfer_components_thread, tcd,
2142 transfer_components_data_free);
2143
2144 g_clear_object (&activity);
2145 g_free (display_name);
2146 g_free (description);
2147 }
2148