1 /*
2 * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com)
3 *
4 * This library 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 library 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 library. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include "evolution-config.h"
18
19 #include <glib/gi18n-lib.h>
20
21 #include <libebackend/libebackend.h>
22 #include <libedataserver/libedataserver.h>
23
24 #include "e-util/e-util.h"
25 #include "composer/e-msg-composer.h"
26 #include "composer/e-composer-from-header.h"
27 #include "calendar/gui/e-comp-editor.h"
28 #include "calendar/gui/e-comp-editor-page-attachments.h"
29 #include "calendar/gui/itip-utils.h"
30
31 #include "e-meeting-to-composer.h"
32
33 /* Standard GObject macros */
34 #define E_TYPE_MEETING_TO_COMPOSER \
35 (e_meeting_to_composer_get_type ())
36 #define E_MEETING_TO_COMPOSER(obj) \
37 (G_TYPE_CHECK_INSTANCE_CAST \
38 ((obj), E_TYPE_MEETING_TO_COMPOSER, EMeetingToComposer))
39 #define E_MEETING_TO_COMPOSER_CLASS(cls) \
40 (G_TYPE_CHECK_CLASS_CAST \
41 ((cls), E_TYPE_MEETING_TO_COMPOSER, EMeetingToComposerClass))
42 #define E_IS_MEETING_TO_COMPOSER(obj) \
43 (G_TYPE_CHECK_INSTANCE_TYPE \
44 ((obj), E_TYPE_MEETING_TO_COMPOSER))
45 #define E_IS_MEETING_TO_COMPOSER_CLASS(cls) \
46 (G_TYPE_CHECK_CLASS_TYPE \
47 ((cls), E_TYPE_MEETING_TO_COMPOSER))
48 #define E_MEETING_TO_COMPOSER_GET_CLASS(obj) \
49 (G_TYPE_INSTANCE_GET_CLASS \
50 ((obj), E_TYPE_MEETING_TO_COMPOSER, EMeetingToComposerClass))
51
52 typedef struct _EMeetingToComposer EMeetingToComposer;
53 typedef struct _EMeetingToComposerClass EMeetingToComposerClass;
54
55 struct _EMeetingToComposer {
56 EExtension parent;
57 };
58
59 struct _EMeetingToComposerClass {
60 EExtensionClass parent_class;
61 };
62
63 GType e_meeting_to_composer_get_type (void) G_GNUC_CONST;
64
G_DEFINE_DYNAMIC_TYPE(EMeetingToComposer,e_meeting_to_composer,E_TYPE_EXTENSION)65 G_DEFINE_DYNAMIC_TYPE (EMeetingToComposer, e_meeting_to_composer, E_TYPE_EXTENSION)
66
67 static void
68 meeting_to_composer_unref_nonull_object (gpointer ptr)
69 {
70 if (ptr)
71 g_object_unref (ptr);
72 }
73
74 static gboolean
meeting_to_composer_check_identity_source(ESource * source,const gchar * address,gchar ** alias_name,gchar ** alias_address)75 meeting_to_composer_check_identity_source (ESource *source,
76 const gchar *address,
77 gchar **alias_name,
78 gchar **alias_address)
79 {
80 ESourceMailIdentity *identity_extension;
81 GHashTable *aliases = NULL;
82 const gchar *text;
83 gboolean found = FALSE;
84
85 if (!E_IS_SOURCE (source) || !address ||
86 !e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_IDENTITY))
87 return FALSE;
88
89 identity_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_IDENTITY);
90
91 text = e_source_mail_identity_get_address (identity_extension);
92 found = text && g_ascii_strcasecmp (text, address) == 0;
93
94 if (!found) {
95 aliases = e_source_mail_identity_get_aliases_as_hash_table (identity_extension);
96 if (aliases) {
97 found = g_hash_table_contains (aliases, address);
98 if (found) {
99 if (alias_name)
100 *alias_name = g_strdup (g_hash_table_lookup (aliases, address));
101 if (alias_address)
102 *alias_address = g_strdup (address);
103 }
104 }
105 }
106
107 if (aliases)
108 g_hash_table_destroy (aliases);
109
110 return found;
111 }
112
113 static void
meeting_to_composer_copy_attachments(ECompEditor * comp_editor,EMsgComposer * composer)114 meeting_to_composer_copy_attachments (ECompEditor *comp_editor,
115 EMsgComposer *composer)
116 {
117 ECompEditorPage *page_attachments;
118 EAttachmentView *attachment_view;
119 EAttachmentStore *store;
120 GList *attachments, *link;
121
122 g_return_if_fail (E_IS_MSG_COMPOSER (composer));
123 g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
124
125 page_attachments = e_comp_editor_get_page (comp_editor, E_TYPE_COMP_EDITOR_PAGE_ATTACHMENTS);
126 if (!page_attachments)
127 return;
128
129 store = e_comp_editor_page_attachments_get_store (E_COMP_EDITOR_PAGE_ATTACHMENTS (page_attachments));
130 attachments = e_attachment_store_get_attachments (store);
131
132 if (!attachments)
133 return;
134
135 attachment_view = e_msg_composer_get_attachment_view (composer);
136 store = e_attachment_view_get_store (attachment_view);
137
138 for (link = attachments; link; link = g_list_next (link)) {
139 EAttachment *attachment = link->data;
140
141 e_attachment_store_add_attachment (store, attachment);
142 }
143
144 g_list_free_full (attachments, g_object_unref);
145 }
146
147 static void
meeting_to_composer_composer_created_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)148 meeting_to_composer_composer_created_cb (GObject *source_object,
149 GAsyncResult *result,
150 gpointer user_data)
151 {
152 ECompEditor *comp_editor = user_data;
153 EMsgComposer *composer;
154 EComposerHeaderTable *header_table;
155 gboolean did_updating;
156 ICalComponent *icomp;
157 ICalProperty *prop;
158 const gchar *text;
159 GPtrArray *to_recips, *cc_recips;
160 GError *error = NULL;
161
162 g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
163
164 composer = e_msg_composer_new_finish (result, &error);
165 if (!composer) {
166 g_warning ("%s: Faild to create message composer: %s", G_STRFUNC, error ? error->message : "Unknown error");
167 return;
168 }
169
170 header_table = e_msg_composer_get_header_table (composer);
171
172 did_updating = e_comp_editor_get_updating (comp_editor);
173 /* Just a trick to not show validation errors when getting the component */
174 e_comp_editor_set_updating (comp_editor, TRUE);
175
176 icomp = i_cal_component_clone (e_comp_editor_get_component (comp_editor));
177 e_comp_editor_fill_component (comp_editor, icomp);
178
179 e_comp_editor_set_updating (comp_editor, did_updating);
180
181 /* Subject */
182 text = i_cal_component_get_summary (icomp);
183 if (text && *text)
184 e_composer_header_table_set_subject (header_table, text);
185
186 /* From */
187 prop = i_cal_component_get_first_property (icomp, I_CAL_ORGANIZER_PROPERTY);
188 if (prop) {
189 EComposerHeader *from_header;
190 const gchar *organizer;
191
192 from_header = e_composer_header_table_get_header (header_table, E_COMPOSER_HEADER_FROM);
193 organizer = itip_strip_mailto (i_cal_property_get_organizer (prop));
194
195 if (organizer && *organizer && from_header) {
196 GtkComboBox *identities_combo;
197 GtkTreeModel *model;
198 GtkTreeIter iter;
199 gint id_column;
200
201 identities_combo = GTK_COMBO_BOX (from_header->input_widget);
202 id_column = gtk_combo_box_get_id_column (identities_combo);
203 model = gtk_combo_box_get_model (identities_combo);
204
205 if (gtk_tree_model_get_iter_first (model, &iter)) {
206 do {
207 ESource *source;
208 gchar *uid;
209 gboolean use_source;
210 gchar *alias_name = NULL;
211 gchar *alias_address = NULL;
212
213 gtk_tree_model_get (model, &iter, id_column, &uid, -1);
214 source = e_composer_header_table_ref_source (header_table, uid);
215
216 use_source = meeting_to_composer_check_identity_source (source, organizer, &alias_name, &alias_address);
217 if (use_source)
218 e_composer_header_table_set_identity_uid (header_table, uid, alias_name, alias_address);
219
220 g_clear_object (&source);
221 g_free (alias_name);
222 g_free (alias_address);
223 g_free (uid);
224
225 if (use_source)
226 break;
227 } while (gtk_tree_model_iter_next (model, &iter));
228 }
229 }
230
231 g_clear_object (&prop);
232 }
233
234 /* Recipients */
235 to_recips = g_ptr_array_new_with_free_func (meeting_to_composer_unref_nonull_object);
236 cc_recips = g_ptr_array_new_with_free_func (meeting_to_composer_unref_nonull_object);
237
238 for (prop = i_cal_component_get_first_property (icomp, I_CAL_ATTENDEE_PROPERTY);
239 prop;
240 g_object_unref (prop), prop = i_cal_component_get_next_property (icomp, I_CAL_ATTENDEE_PROPERTY)) {
241 ICalParameter *param;
242 ICalParameterRole role = I_CAL_ROLE_REQPARTICIPANT;
243 const gchar *name = NULL, *address;
244 EDestination *dest;
245
246 address = itip_strip_mailto (i_cal_property_get_attendee (prop));
247 if (!address || !*address)
248 continue;
249
250 param = i_cal_property_get_first_parameter (prop, I_CAL_ROLE_PARAMETER);
251 if (param) {
252 role = i_cal_parameter_get_role (param);
253 g_object_unref (param);
254 }
255
256 if (role == I_CAL_ROLE_NONPARTICIPANT || role == I_CAL_ROLE_NONE)
257 continue;
258
259 param = i_cal_property_get_first_parameter (prop, I_CAL_CN_PARAMETER);
260 if (param)
261 name = i_cal_parameter_get_cn (param);
262
263 if (name && !*name)
264 name = NULL;
265
266 dest = e_destination_new ();
267 e_destination_set_name (dest, name);
268 e_destination_set_email (dest, address);
269
270 if (role == I_CAL_ROLE_REQPARTICIPANT)
271 g_ptr_array_add (to_recips, dest);
272 else
273 g_ptr_array_add (cc_recips, dest);
274
275 g_clear_object (¶m);
276 }
277
278 if (to_recips->len > 0) {
279 g_ptr_array_add (to_recips, NULL);
280
281 e_composer_header_table_set_destinations_to (header_table, (EDestination **) to_recips->pdata);
282 }
283
284 if (cc_recips->len > 0) {
285 g_ptr_array_add (cc_recips, NULL);
286
287 e_composer_header_table_set_destinations_cc (header_table, (EDestination **) cc_recips->pdata);
288 }
289
290 g_ptr_array_free (to_recips, TRUE);
291 g_ptr_array_free (cc_recips, TRUE);
292
293 /* Body */
294 prop = i_cal_component_get_first_property (icomp, I_CAL_DESCRIPTION_PROPERTY);
295 if (prop) {
296 text = i_cal_property_get_description (prop);
297
298 if (text && *text) {
299 EHTMLEditor *html_editor;
300 EContentEditor *cnt_editor;
301
302 html_editor = e_msg_composer_get_editor (composer);
303 cnt_editor = e_html_editor_get_content_editor (html_editor);
304
305 e_content_editor_set_html_mode (cnt_editor, FALSE);
306 e_content_editor_insert_content (cnt_editor, text, E_CONTENT_EDITOR_INSERT_REPLACE_ALL | E_CONTENT_EDITOR_INSERT_TEXT_PLAIN);
307 }
308
309 g_object_unref (prop);
310 }
311
312 /* Attachments */
313 meeting_to_composer_copy_attachments (comp_editor, composer);
314
315 gtk_window_present (GTK_WINDOW (composer));
316
317 gtk_widget_destroy (GTK_WIDGET (comp_editor));
318 g_object_unref (icomp);
319 }
320
321 static void
action_meeting_to_composer_cb(GtkAction * action,ECompEditor * comp_editor)322 action_meeting_to_composer_cb (GtkAction *action,
323 ECompEditor *comp_editor)
324 {
325 ICalComponent *icomp;
326 ICalComponentKind kind;
327 const gchar *prompt_key;
328
329 g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
330
331 icomp = e_comp_editor_get_component (comp_editor);
332 kind = icomp ? i_cal_component_isa (icomp) : I_CAL_VEVENT_COMPONENT;
333
334 if (kind == I_CAL_VTODO_COMPONENT)
335 prompt_key = "mail-composer:prompt-task-to-composer";
336 else if (kind == I_CAL_VJOURNAL_COMPONENT)
337 prompt_key = "mail-composer:prompt-memo-to-composer";
338 else
339 prompt_key = "mail-composer:prompt-event-to-composer";
340
341 if (!e_util_prompt_user (GTK_WINDOW (comp_editor), NULL, NULL, prompt_key, NULL))
342 return;
343
344 e_msg_composer_new (e_comp_editor_get_shell (comp_editor),
345 meeting_to_composer_composer_created_cb, comp_editor);
346 }
347
348 static void
e_meeting_to_composer_setup_ui(ECompEditor * comp_editor)349 e_meeting_to_composer_setup_ui (ECompEditor *comp_editor)
350 {
351 const gchar *ui =
352 "<ui>"
353 " <menubar action='main-menu'>"
354 " <menu action='file-menu'>"
355 " <placeholder name='custom-actions-placeholder'>"
356 " <menuitem action='meeting-to-composer-action'/>"
357 " </placeholder>"
358 " </menu>"
359 " </menubar>"
360 "</ui>";
361
362 GtkActionEntry entries[] = {
363 { "meeting-to-composer-action",
364 "mail-message-new",
365 N_("Convert to M_essage"),
366 NULL,
367 N_("Convert to the mail message"),
368 G_CALLBACK (action_meeting_to_composer_cb) }
369 };
370
371 GtkUIManager *ui_manager;
372 GtkActionGroup *action_group;
373 GError *error = NULL;
374
375 g_return_if_fail (E_IS_COMP_EDITOR (comp_editor));
376
377 ui_manager = e_comp_editor_get_ui_manager (comp_editor);
378 action_group = e_comp_editor_get_action_group (comp_editor, "individual");
379
380 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), comp_editor);
381
382 gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
383
384 if (error) {
385 g_critical ("%s: %s", G_STRFUNC, error->message);
386 g_error_free (error);
387 }
388 }
389
390 static void
meeting_to_composer_constructed(GObject * object)391 meeting_to_composer_constructed (GObject *object)
392 {
393 ECompEditor *comp_editor;
394
395 /* Chain up to parent's constructed() method. */
396 G_OBJECT_CLASS (e_meeting_to_composer_parent_class)->constructed (object);
397
398 comp_editor = E_COMP_EDITOR (e_extension_get_extensible (E_EXTENSION (object)));
399
400 e_meeting_to_composer_setup_ui (comp_editor);
401 }
402
403 static void
e_meeting_to_composer_class_init(EMeetingToComposerClass * class)404 e_meeting_to_composer_class_init (EMeetingToComposerClass *class)
405 {
406 GObjectClass *object_class;
407 EExtensionClass *extension_class;
408
409 object_class = G_OBJECT_CLASS (class);
410 object_class->constructed = meeting_to_composer_constructed;
411
412 extension_class = E_EXTENSION_CLASS (class);
413 extension_class->extensible_type = E_TYPE_COMP_EDITOR;
414 }
415
416 static void
e_meeting_to_composer_class_finalize(EMeetingToComposerClass * class)417 e_meeting_to_composer_class_finalize (EMeetingToComposerClass *class)
418 {
419 }
420
421 static void
e_meeting_to_composer_init(EMeetingToComposer * extension)422 e_meeting_to_composer_init (EMeetingToComposer *extension)
423 {
424 }
425
426 void
e_meeting_to_composer_type_register(GTypeModule * type_module)427 e_meeting_to_composer_type_register (GTypeModule *type_module)
428 {
429 /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
430 * function, so we have to wrap it with a public function in
431 * order to register types from a separate compilation unit. */
432 e_meeting_to_composer_register_type (type_module);
433 }
434